All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jtransc.ast.ast.kt Maven / Gradle / Ivy

There is a newer version: 0.6.8
Show newest version
/*
 * Copyright 2016 Carlos Ballesteros Velasco
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jtransc.ast

import com.jtransc.ast.dependency.AstDependencyAnalyzer
import com.jtransc.error.InvalidOperationException
import com.jtransc.error.invalidOp
import com.jtransc.input.SootToAst
import com.jtransc.util.dependencySorter
import com.jtransc.vfs.IUserData
import com.jtransc.vfs.SyncVfsFile
import com.jtransc.vfs.UserData
import jtransc.annotation.*
import java.io.File
import java.util.*

data class AstBuildSettings(
	var jtranscVersion: String,
	var title: String = "App Title",
	var name: String = "AppName",
	var version: String = "0.0.0",
	var company: String = "My Company",
	var package_: String = "",
	var libraries: List = listOf(),
	var assets: List = listOf(),
	var debug: Boolean = true,
	var initialWidth: Int = 1280,
	var initialHeight: Int = 720,
	var vsync: Boolean = true,
	var resizable: Boolean = true,
	var borderless: Boolean = false,
	var fullscreen: Boolean = false,
	var icon: String? = null,
	var orientation: AstBuildSettings.Orientation = AstBuildSettings.Orientation.AUTO
) {
	val release: Boolean get() = !debug

	data class Library(val name: String, val version: String?) {
		companion object {
			fun fromInfo(info: String): Library {
				val parts = info.split(':')
				val name = parts.getOrNull(0)
				val version = parts.getOrNull(1)
				return Library(name!!, version)
			}
		}
	}

	enum class Orientation {
		PORTRAIT, LANDSCAPE, AUTO;

		val lowName: String = name.toLowerCase()

		companion object {
			fun fromString(str: String): Orientation = when (str.toLowerCase()) {
				"portrait" -> PORTRAIT
				"landscape" -> LANDSCAPE
				"auto" -> AUTO
				else -> AUTO
			}
		}
	}
}

interface AstClassGenerator {
	fun generateClass(program: AstProgram, fqname: FqName): AstClass
}

class AstProgram(
	val entrypoint: FqName,
	val resourcesVfs: SyncVfsFile,
	val generator: AstClassGenerator
) : IUserData by UserData() {
	private val _classes = arrayListOf()
	private val _classesByFqname = hashMapOf()

	val classes: List get() = _classes

	private val classesToGenerate = LinkedList()
	private val referencedClasses = hashSetOf()

	fun hasClassToGenerate() = classesToGenerate.isNotEmpty()

	fun readClassToGenerate():AstClassRef = classesToGenerate.remove()

	fun addReference(clazz: AstClassRef) {
		if (clazz !in referencedClasses) {
			classesToGenerate += clazz
			referencedClasses += clazz
		}
	}

	operator fun contains(name: FqName) = name.fqname in _classesByFqname
	//operator fun get(name: FqName) = classesByFqname[name.fqname] ?: throw RuntimeException("AstProgram. Can't find class '$name'")
	operator fun get(name: FqName): AstClass {
		val result = _classesByFqname[name.fqname]
		if (result == null) {
			val classFile = name.internalFqname + ".class"
			println("AstProgram. Can't find class '$name'")
			println("AstProgram. ClassFile: $classFile")
			println("AstProgram. File exists: " + resourcesVfs[classFile].exists)
			//println("AstProgram. Soot exists: " + SootToAst.checkIfClassExists(name))

			throw RuntimeException("AstProgram. Can't find class '$name'")
		} else {
			return result
		}
	}

	private var finished = false

	fun add(clazz: AstClass) {
		if (finished) invalidOp("Can't add more classes to a finished program")
		_classes.add(clazz)
		_classesByFqname[clazz.fqname] = clazz
	}

	fun finish() {
		finished = true
	}

	val allAnnotations by lazy {
		classes.flatMap {
			it.annotations + it.methods.flatMap { it.annotations } + it.fields.flatMap { it.annotations }
		}
	}

	operator fun get(ref: AstType.REF): AstClass = this[ref.name]
	operator fun get(ref: AstClassRef): AstClass = this[ref.name]
	operator fun get(ref: AstMethodRef): AstMethod? = this[ref.containingClass].getMethodInAncestorsAndInterfaces(ref.nameDesc)
	operator fun get(ref: AstFieldRef): AstField = this[ref.containingClass][ref]

	// @TODO: Cache all this stuff!
	/*
	fun getAncestors(clazz: AstClass): List {
		var out = arrayListOf()
		var current = clazz
		while (current != null) {
			out.add(current)
			val extending = current.extending ?: break
			if (extending.fqname.isNullOrEmpty()) break
			current = this[extending]
		}
		return out
	}
	*/

	fun getInterfaces(clazz: AstClass) = clazz.implementing.map { this[it] }

	fun getAllInterfaces(clazz: AstClass): Set {
		val thisInterfaces = getInterfaces(clazz).flatMap { listOf(it) + getAllInterfaces(it) }.toSet()
		if (clazz.extending == null) {
			return thisInterfaces
		} else {
			return getAllInterfaces(this[clazz.extending]) + thisInterfaces
		}
	}

	fun isImplementing(clazz: AstClass, implementingClazz: String): Boolean {
		return (implementingClazz.fqname in this) && (this[implementingClazz.fqname] in getAllInterfaces(clazz))
	}
}

enum class AstVisibility { PUBLIC, PROTECTED, PRIVATE }
enum class AstClassType { CLASS, ABSTRACT, INTERFACE }

class AstClass(
	val program: AstProgram,
	val name: FqName,
	val modifiers: Int,
	val classType: AstClassType = AstClassType.CLASS,
	val visibility: AstVisibility = AstVisibility.PUBLIC,
	val extending: FqName? = null,
	val implementing: List = listOf(),
	val annotations: List = listOf()
) : IUserData by UserData() {
	val fields = arrayListOf()
	val methods = arrayListOf()
	val methodsByName = hashMapOf>()
	val methodsByNameDescInterfaces = hashMapOf()
	val methodsByNameDesc = hashMapOf()
	val fieldsByName = hashMapOf()
	val runtimeAnnotations = annotations.filter { it.runtimeVisible }

	//fun getDirectInterfaces(): List = implementing.map { program[it] }
	val directInterfaces: List by lazy { implementing.map { program[it] } }

	val parentClass: AstClass? by lazy { if (extending != null) program[extending] else null }
	//fun getParentClass(): AstClass? = if (extending != null) program[extending] else null

	val allInterfaces: List by lazy {
		val out = arrayListOf()
		val sets = hashSetOf()
		val queue: Queue = LinkedList()
		queue += this
		while (queue.isNotEmpty()) {
			val item = queue.remove()
			if (item.isInterface && item != this) out += item
			for (i in item.directInterfaces) {
				if (i !in sets) {
					sets += i
					queue += i
				}
			}
		}
		out
	}

	val allMethodsToImplement: List by lazy {
		this.allInterfaces.flatMap { it.methods }.filter { !it.isStatic }.map { it.ref.withoutClass }
	}

	fun add(field: AstField) {
		if (finished) invalidOp("Finished class")
		fields.add(field)
		fieldsByName[field.name] = field
	}

	fun add(method: AstMethod) {
		if (finished) invalidOp("Finished class")
		methods.add(method)
		if (method.name !in methodsByName) methodsByName[method.name] = arrayListOf()
		val methodDesc = AstMethodWithoutClassRef(method.name, method.methodType)
		methodsByName[method.name]?.add(method)
		methodsByNameDescInterfaces[methodDesc] = method
		methodsByNameDesc[methodDesc] = method
	}

	private var finished = false

	fun finish() {
		finished = true
	}

	//val dependencies: AstReferences = AstReferences()
	val implCode by lazy { annotations.get(JTranscNativeClassImpl::value) }
	val nativeName by lazy { annotations.get(JTranscNativeClass::value) }

	val isInterface: Boolean get() = classType == AstClassType.INTERFACE
	val isAbstract: Boolean get() = classType == AstClassType.ABSTRACT
	val fqname = name.fqname
	val isNative by lazy { (nativeName != null) }

	val classAndFieldAndMethodAnnotations by lazy {
		annotations + methods.flatMap { it.annotations } + fields.flatMap { it.annotations }
	}

	fun getMethods(name: String): List = methodsByName[name]!!
	fun getMethod(name: String, desc: String): AstMethod? = methodsByName[name]?.firstOrNull { it.desc == desc }

	fun getMethodInAncestors(nameDesc: AstMethodWithoutClassRef): AstMethod? {
		var result = methodsByNameDesc[nameDesc]
		if (result == null) {
			result = parentClass?.getMethodInAncestorsAndInterfaces(nameDesc)
		}
		methodsByNameDesc[nameDesc] = result
		return result
	}

	fun getMethodInAncestorsAndInterfaces(nameDesc: AstMethodWithoutClassRef): AstMethod? {
		var result = methodsByNameDescInterfaces[nameDesc]
		if (result == null) {
			result = parentClass?.getMethodInAncestorsAndInterfaces(nameDesc)
		}
		if (result == null) {
			for (it in directInterfaces) {
				result = it.getMethodInAncestorsAndInterfaces(nameDesc)
				if (result != null) break
			}
		}
		methodsByNameDescInterfaces[nameDesc] = result
		return result
		//return methodsByNameDesc[nameDesc] ?: parentClass?.getMethodInAncestors(nameDesc)
	}

	fun getMethodSure(name: String, desc: String): AstMethod {
		return getMethod(name, desc) ?: throw InvalidOperationException("Can't find method ${this.name}:$name:$desc")
	}

	operator fun get(ref: AstMethodRef) = getMethodSure(ref.name, ref.desc)
	operator fun get(ref: AstMethodWithoutClassRef) = getMethod(ref.name, ref.desc)
	operator fun get(ref: AstFieldRef) = fieldsByName[ref.name] ?: invalidOp("Can't find field $ref")

	val hasStaticInit: Boolean get() = staticInitMethod != null
	val staticInitMethod: AstMethod? by lazy { methodsByName[""]?.firstOrNull() }

	val allDependencies: Set by lazy {
		var out = hashSetOf()
		if (extending != null) out.add(AstClassRef(extending))
		for (i in implementing) out.add(AstClassRef(i))
		for (f in fields) for (ref in f.type.getRefClasses()) out.add(ref)
		for (m in methods) {
			for (dep in m.dependencies.methods) {
				out.add(dep.classRef)
				out.add(dep)
			}
			for (dep in m.dependencies.fields) {
				out.add(dep.classRef)
				out.add(dep)
			}
			for (dep in m.dependencies.classes) {
				out.add(dep)
			}
		}
		out.toSet()
	}

	val classDependencies: Set by lazy {
		allDependencies.filterIsInstance().toSet()
	}

	override fun toString() = "AstClass($name)"

	val thisAndAncestors: List by lazy {
		if (extending == null) {
			listOf(this)
		} else {
			listOf(this) + program[extending].thisAndAncestors
		}
	}

	val ancestors: List by lazy { thisAndAncestors.drop(1) }
}

fun List.sortedByDependencies(): List {
	val classes = this.associateBy { it.name.fqname }
	fun resolveClassRef(ref: AstClassRef) = classes[ref.fqname]!!
	fun resolveMethodRef(ref: AstMethodRef) = resolveClassRef(ref.classRef)[ref]

	return this.dependencySorter(allowCycles = true) {
		val extending = it.extending?.fqname
		val implementing = it.implementing.map { classes[it.fqname]!! }
		val ext = if (extending != null) classes[extending] else null
		val ext2 = if (ext != null) listOf(ext) else listOf()
		val ext3 = it.staticInitMethod?.dependencies?.allClasses?.map { classes[it.fqname]!! } ?: listOf()

		fun checkMethodsRecursive(method: AstMethod, explored: MutableSet = hashSetOf()): MutableSet {
			if (method !in explored) {
				explored.add(method)
				for (m in method.dependencies.methods) {
					checkMethodsRecursive(resolveMethodRef(m), explored)
				}
			}
			return explored
		}

		val ext4 = if (it.staticInitMethod != null) {
			checkMethodsRecursive(it.staticInitMethod!!).toList().flatMap { it.dependencies.allClasses }.map { classes[it.fqname]!! }
		} else {
			listOf()
		}
		//val ext3 = it.methods.flatMap { it.dependencies.allClasses.map { classes[it.fqname]!! } }
		// @TODO: Check fields too!
		ext2 + implementing + ext3 + ext4
	}
}

val AstClass.ref: AstClassRef get() = AstClassRef(this.name)
val AstClass.astType: AstType.REF get() = AstType.REF(this.name)

fun AstType.getRefClasses(): List = this.getRefTypesFqName().map { AstClassRef(it) }

data class AstReferences(
	val program: AstProgram?,
	val classes: Set = setOf(),
	val methods: Set = setOf(),
	val fields: Set = setOf()
) {
	val allClasses: Set by lazy {
		classes + methods.flatMap { it.allClassRefs } + fields.flatMap { listOf(it.classRef) }
	}

	val fields2 by lazy { fields.map { program!![it] } }
}

open class AstMember(
	val containingClass: AstClass,
	val name: String,
	val type: AstType,
	val genericType: AstType,
	val isStatic: Boolean = false,
	val visibility: AstVisibility = AstVisibility.PUBLIC,
	val annotations: List = listOf()
) : IUserData by UserData() {
	val program = containingClass.program
}

class AstField(
	containingClass: AstClass,
	name: String,
	type: AstType,
	val modifiers: Int,
	val descriptor: String,
	annotations: List,
	val genericSignature: String?,
	isStatic: Boolean = false,
	val isFinal: Boolean = false,
	visibility: AstVisibility = AstVisibility.PUBLIC,
	val constantValue: Any? = null
) : AstMember(containingClass, name, type, if (genericSignature != null) AstType.demangle(genericSignature) else type, isStatic, visibility, annotations) {
	val ref: AstFieldRef by lazy { AstFieldRef(this.containingClass.name, this.name, this.type) }
	val hasConstantValue = constantValue != null
}

class AstMethod(
	containingClass: AstClass,
	name: String,
	type: AstType.METHOD_TYPE,
	annotations: List,
	val signature: String,
	val genericSignature: String?,
	val defaultTag: Any?,
	val modifiers: Int,
	val body: AstBody? = null,
	isStatic: Boolean = false,
	visibility: AstVisibility = AstVisibility.PUBLIC,
	val isNative: Boolean = false
	//val isOverriding: Boolean = overridingMethod != null,
) : AstMember(containingClass, name, type, if (genericSignature != null) AstType.demangleMethod(genericSignature) else type, isStatic, visibility, annotations) {

	val methodType: AstType.METHOD_TYPE = type
	val genericMethodType: AstType.METHOD_TYPE = genericType as AstType.METHOD_TYPE
	val desc = methodType.desc
	val ref: AstMethodRef by lazy { AstMethodRef(containingClass.name, name, methodType) }
	val dependencies by lazy { AstDependencyAnalyzer.analyze(containingClass.program, body) }

	val getterField: String? by lazy { annotations.get(JTranscGetter::value) }
	val setterField: String? by lazy { annotations.get(JTranscSetter::value) }
	val nativeMethod: String? by lazy { annotations.get(JTranscMethod::value) }
	val isInline: Boolean by lazy { annotations.contains() }

	val isOverriding: Boolean by lazy {
		containingClass.ancestors.any { it[ref.withoutClass] != null }
	}

	val isImplementing: Boolean by lazy {
		containingClass.allInterfaces.any { it.getMethod(this.name, this.desc) != null }
	}

	override fun toString(): String = "AstMethod(${containingClass.fqname}:$name:$desc)"
}

fun AstMethodRef.toEmptyMethod(program: AstProgram, isStatic: Boolean = false, visibility: AstVisibility = AstVisibility.PUBLIC): AstMethod {
	return AstMethod(
		program[this.containingClass],
		this.name,
		this.type,
		annotations = listOf(),
		defaultTag = null,
		signature = "()V",
		genericSignature = null,
		modifiers = 0,
		body = null,
		isStatic = isStatic,
		visibility = AstVisibility.PUBLIC
	)
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy