
com.jtransc.gen.common.CommonGenerator.kt Maven / Gradle / Ivy
package com.jtransc.gen.common
import com.jtransc.*
import com.jtransc.annotation.*
import com.jtransc.ast.*
import com.jtransc.ast.template.CommonTagHandler
import com.jtransc.ast.treeshaking.getTargetAddFiles
import com.jtransc.ds.getOrPut2
import com.jtransc.ds.toHashMap
import com.jtransc.error.invalidOp
import com.jtransc.error.noImpl
import com.jtransc.error.noImplWarn
import com.jtransc.error.unexpected
import com.jtransc.gen.MinimizedNames
import com.jtransc.gen.TargetName
import com.jtransc.injector.Injector
import com.jtransc.io.ProcessResult2
import com.jtransc.json.Json
import com.jtransc.lang.high
import com.jtransc.lang.low
import com.jtransc.lang.putIfAbsentJre7
import com.jtransc.log.log
import com.jtransc.plugin.JTranscPluginGroup
import com.jtransc.template.Minitemplate
import com.jtransc.text.Indenter
import com.jtransc.text.isLetterDigitOrUnderscore
import com.jtransc.text.quote
import com.jtransc.text.substr
import com.jtransc.util.toIntOrNull2
import com.jtransc.vfs.ExecOptions
import com.jtransc.vfs.LocalVfs
import com.jtransc.vfs.MergeVfs
import com.jtransc.vfs.SyncVfsFile
import java.io.File
import java.util.*
import kotlin.reflect.KMutableProperty1
class ConfigSrcFolder(val srcFolder: SyncVfsFile)
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "RemoveSingleExpressionStringTemplate")
abstract class CommonGenerator(val injector: Injector) : IProgramTemplate {
abstract val SINGLE_FILE: Boolean
open val ADD_UTF8_BOM = false
// CONFIG
open val staticAccessOperator: String = "."
open val instanceAccessOperator: String = "."
open val stringPoolType: StringPool.Type = StringPool.Type.GLOBAL
open val languageRequiresDefaultInSwitch = false
open val defaultGenStmSwitchHasBreaks = true
open val interfacesSupportStaticMembers = true
open val usePackages = true
open val classFileExtension = ""
open val allowRepeatMethodsInInterfaceChain = true
open val localVarPrefix = ""
open val floatHasFSuffix = true
open val casesWithCommas = false
open val optionalDoubleDummyDecimals = false
open val GENERATE_LINE_NUMBERS = true
val configTargetFolder: ConfigTargetFolder = injector.get()
val plugins: JTranscPluginGroup = injector.get()
val program: AstProgram = injector.get()
val sortedClasses by lazy { program.classes.filter { it.mustGenerate }.sortedByExtending() }
val targetName = injector.get()
open val methodFeatures: Set> = setOf()
open val methodFeaturesWithTraps: Set> get() = methodFeatures
open val keywords: Set = program.getExtraKeywords(targetName.name).toSet()
val configSrcFolder: ConfigSrcFolder = injector.get()
open val srcFolder: SyncVfsFile = configSrcFolder.srcFolder
val configMinimizeNames: ConfigMinimizeNames? = injector.getOrNull()
val minimize: Boolean = configMinimizeNames?.minimizeNames ?: false
val settings: AstBuildSettings = injector.get()
val extraParams = settings.extra
val extraVars by lazy { program.getTemplateVariables(targetName, settings.extraVars) }
val debugVersion: Boolean = settings.debug
val folders: CommonGenFolders = injector.get()
val configOutputFile: ConfigOutputFile = injector.get()
val configOutputFile2: ConfigOutputFile2 = injector.get()
val outputFileBaseName: String = configOutputFile.outputFileBaseName
val outputFile = configOutputFile.output
open val outputFile2 = configOutputFile2.file
val configTargetDirectory: ConfigTargetDirectory = injector.get()
val tempdir = configTargetDirectory.targetDirectory
val features = injector.get()
val types: AstTypes = program.types
val context = AstGenContext()
val refs = References()
val targetLibraries by lazy { program.getLibsFor(targetName) }
val targetIncludes by lazy { program.getIncludesFor(targetName) }
val targetImports by lazy { program.getImportsFor(targetName) }
val targetDefines by lazy { program.getDefinesFor(targetName) }
open val allTargetLibraries by lazy { targetLibraries + (injector.getOrNull()?.libs ?: listOf()) }
open val allTargetDefines by lazy { targetDefines }
val AstClass.nativeMembers get() = this.getMembersFor(targetName)
open fun writeProgramAndFiles(): Unit {
if (SINGLE_FILE) {
writeClasses(configTargetFolder.targetFolder)
setTemplateParamsAfterBuildingSource()
} else {
val output = configTargetFolder.targetFolder
writeClasses(output)
setTemplateParamsAfterBuildingSource()
for (file in getFilesToCopy(targetName.name)) {
val str = program.resourcesVfs[file.src].readString()
val strr = if (file.process) str.template("includeFile") else str
output[file.dst] = strr
}
}
}
open val fixencoding = true
open fun compile(): ProcessResult2 {
val cmdAndArgs = genCompilerCommand(
programFile = configTargetFolder.targetFolder[configOutputFile.output].realfile,
debug = settings.debug,
libs = injector.getOrNull()?.libs ?: listOf()
)
val cmdAndArgsStr = cmdAndArgs.joinToString(" ")
println(cmdAndArgsStr)
return if (cmdAndArgs.isEmpty()) {
ProcessResult2(0)
} else {
val result = LocalVfs(File(configTargetFolder.targetFolder.realpathOS)).exec(cmdAndArgs, ExecOptions(sysexec = true, fixencoding = fixencoding, passthru = true))
if (!result.success) {
throw RuntimeException("success=${result.success} exitCode=${result.exitCode} output='${result.outputString}' error='${result.errorString}' folder=${configTargetFolder.targetFolder.realpathOS} command='$cmdAndArgsStr'")
}
ProcessResult2(result)
}
}
open protected fun genCompilerCommand(programFile: File, debug: Boolean, libs: List): List {
return listOf()
}
open fun run(redirect: Boolean = true): ProcessResult2 {
return ProcessResult2(0)
}
open fun compileAndRun(redirect: Boolean = true): ProcessResult2 {
val compileResult = compile()
return if (!compileResult.success) {
ProcessResult2(compileResult.exitValue)
} else {
this.run(redirect)
}
}
open fun writeClasses(output: SyncVfsFile) {
if (SINGLE_FILE) {
if (ADD_UTF8_BOM) {
output[outputFileBaseName] = byteArrayOf(0xEF.toByte(), 0xBB.toByte(), 0xBF.toByte()) + genSingleFileClasses(output).toString().toByteArray()
} else {
output[outputFileBaseName] = genSingleFileClasses(output).toString()
}
} else {
for (clazz in sortedClasses) {
val results = genClass(clazz)
for (result in results) {
output[getClassFilename(result.subclass.clazz, result.subclass.type)] = result.indenter.toString()
}
}
}
}
open fun getClassBaseFilename(clazz: AstClass, type: MemberTypes): String {
val basename = if (type == MemberTypes.STATIC) {
clazz.name.targetNameForStatic
} else {
clazz.name.targetName
}
return basename.replace('.', if (usePackages) '/' else '_')
}
open fun getClassFilename(clazz: AstClass, type: MemberTypes) = getClassBaseFilename(clazz, type) + classFileExtension
val indenterPerClass = hashMapOf()
open fun genSingleFileClasses(output: SyncVfsFile): Indenter = Indenter {
imports.clear()
val concatFilesTrans = copyFiles(output)
line(concatFilesTrans.prepend)
line(genSingleFileClassesWithoutAppends(output))
line(concatFilesTrans.append)
}
open fun genSingleFileClassesWithoutAppends(output: SyncVfsFile): Indenter = Indenter {
for (clazz in sortedClasses) {
val indenters = if (clazz.implCode != null) listOf(Indenter(clazz.implCode!!)) else genClass(clazz).map { it.indenter }
for (indenter in indenters) {
indenterPerClass[clazz] = indenter
line(indenter)
}
}
}
data class SubClass(val clazz: AstClass, val type: MemberTypes)
data class ClassResult(val subclass: SubClass, val indenter: Indenter)
open fun genClass(clazz: AstClass): List {
setCurrentClass(clazz)
val out = arrayListOf()
if (interfacesSupportStaticMembers || !clazz.isInterface) {
out += genClassPart(clazz, MemberTypes.ALL)
} else {
out += genClassPart(clazz, MemberTypes.INSTANCE)
out += genClassPart(clazz, MemberTypes.STATIC)
}
return out
}
open fun genClassPart(clazz: AstClass, type: MemberTypes): ClassResult {
imports.clear()
return ClassResult(
SubClass(clazz, type),
//when (type) {
// MemberTypes.ALL, MemberTypes.INSTANCE -> FqName(clazz.name.targetName)
// MemberTypes.STATIC -> FqName(clazz.name.targetNameForStatic)
//},
Indenter {
val nativeName = clazz.nativeName
if (nativeName != null) line(singleLineComment("Native $nativeName"))
line(genClassDecl(clazz, type)) {
line(genClassBody(clazz, type))
}
}
)
}
open fun singleLineComment(text: String) = Indenter("// $text")
enum class MemberTypes(val isStatic: Boolean) {
ALL(false), INSTANCE(false), STATIC(true);
fun check(member: AstMember) = when (this) {
ALL -> true
INSTANCE -> !member.isStatic
STATIC -> member.isStatic
}
}
open fun genClassDecl(clazz: AstClass, kind: MemberTypes): String {
val CLASS = if (clazz.isInterface) "interface" else "class"
val iabstract = if (clazz.isAbstract) "abstract " else ""
var decl = "$iabstract$CLASS ${clazz.name.targetSimpleName}"
decl += genClassDeclExtendsImplements(clazz, kind)
return decl
}
protected open fun genClassDeclExtendsImplements(clazz: AstClass, kind: MemberTypes): String {
var decl = ""
decl += genClassDeclExtends(clazz, kind)
decl += genClassDeclImplements(clazz, kind)
return decl
}
protected open fun genClassDeclExtends(clazz: AstClass, kind: MemberTypes): String {
return if (clazz.extending != null) " extends ${clazz.extending.targetClassFqName}" else ""
}
protected open fun genClassDeclImplements(clazz: AstClass, kind: MemberTypes): String {
val implementing = getClassInterfaces(clazz).distinct()
val IMPLEMENTS = if (clazz.isInterface) "extends" else "implements"
return if (implementing.isNotEmpty()) " $IMPLEMENTS ${implementing.map { it.targetClassFqName }.joinToString(", ")}" else ""
}
protected open fun getClassInterfaces(clazz: AstClass): List = clazz.implementing
open fun genClassBody(clazz: AstClass, kind: MemberTypes): Indenter = Indenter {
val members = clazz.annotationsList.getTypedList(JTranscAddMembersList::value).filter { it.target == targetName.name }.flatMap { it.value.toList() }.joinToString("\n")
line(gen(members, process = true))
line(genClassBodyFields(clazz, kind))
line(genClassBodyMethods(clazz, kind))
if (kind != MemberTypes.INSTANCE) {
line(genSIMethod(clazz))
}
}
open fun genClassBodyFields(clazz: AstClass, kind: MemberTypes): Indenter = Indenter {
for (f in clazz.fields) if (kind.check(f)) line(genField(f))
}
open fun genClassBodyMethods(clazz: AstClass, kind: MemberTypes): Indenter = Indenter {
val methodsWithoutClassToIgnore = if (clazz.isInterface && !allowRepeatMethodsInInterfaceChain) {
clazz.allInterfacesInAncestors.flatMap { it.methodsWithoutConstructors }.distinct().map { it.ref.withoutClass }.toSet()
} else {
setOf()
}
val nativeClass = clazz.isNative
for (m in clazz.methods) {
if (!kind.check(m)) continue
if (m.ref.withoutClass in methodsWithoutClassToIgnore) continue
//if (nativeClass && !m.annotationsList.nonNativeCall) continue
val mustPutBody = !clazz.isInterface || m.isStatic
line(genMethod(clazz, m, mustPutBody))
}
}
open fun genSIMethod(clazz: AstClass): Indenter = Indenter {
line(genSIMethodBody(clazz))
}
open fun genSIMethodBody(clazz: AstClass): Indenter = Indenter {
//for (field in clazz.fields.filter { it.isStatic }) {
// line("${field.buildField(static = true)};")
//}
if (clazz.staticConstructor != null) {
line("${buildMethod(clazz.staticConstructor!!, static = true)}();")
}
}
open fun genField(field: AstField): Indenter = Indenter {
val istatic = if (field.isStatic) "static " else ""
line("$istatic${field.type.targetName} ${field.targetName} = ${field.escapedConstantValueField};")
}
open fun genMetodDecl(method: AstMethod): String {
val args = method.methodType.args.map { it.decl }
//if (method.isInstanceInit) mods += "final "
val mods = genMethodDeclModifiers(method)
return "$mods ${method.actualRetType.targetName} ${method.targetName}(${args.joinToString(", ")})"
}
open fun genMethodDeclModifiers(method: AstMethod): String {
var mods = ""
if (!method.isStatic && method.targetIsOverriding) mods += "override "
if (method.isStatic) mods += "static "
return mods
}
open val AstMethod.actualRetType: AstType get() = if (this.isInstanceInit) this.containingClass.astType else this.methodType.ret
open fun genMethod(clazz: AstClass, method: AstMethod, mustPutBody: Boolean): Indenter = Indenter {
currentMethod = method.ref
context.method = method
val decl = genMetodDecl(method)
if (mustPutBody) {
line(decl) {
val actualMethod: AstMethod = if (method.bodyRef != null) {
// Default methods
method.bodyRef.resolve(program)
} else {
method
}
val native = actualMethod.nativeBodies
val defaultNativeBody = native[""]
if (defaultNativeBody != null) {
line(defaultNativeBody)
} else if (actualMethod.body != null) {
line(genBody2WithFeatures(actualMethod, actualMethod.body!!))
if (actualMethod.isInstanceInit) line("return " + genExprThis(AstExpr.THIS(clazz.name)) + ";")
} else {
line(genMissingBody(actualMethod))
}
}
} else {
line("$decl;")
}
}
open fun genMissingBody(method: AstMethod): Indenter = Indenter {
val message = "Missing body ${method.containingClass.name}.${method.name}${method.desc}"
line("throw ${quoteString(message)};")
}
open val AstMethod.targetIsOverriding: Boolean get() = this.isOverriding && !this.isInstanceInit && !this.isStatic
var entryPointClass = FqName("EntryPointClass")
var entryPointFilePath = "EntryPointFile"
val JAVA_LANG_OBJECT_CLASS by lazy { program["java.lang.Object".fqname]!! }
val JAVA_LANG_OBJECT_REF by lazy { AstType.REF("java.lang.Object") }
val JAVA_LANG_OBJECT by lazy { nativeName() }
val JAVA_LANG_CLASS by lazy { nativeName>() }
val JAVA_LANG_STRING by lazy { nativeName() }
val invocationHandlerTargetName by lazy { nativeName() }
val methodTargetName by lazy { nativeName() }
val invokeTargetName by lazy { AstMethodRef(java.lang.reflect.InvocationHandler::class.java.name.fqname, "invoke", types.build { METHOD(OBJECT, OBJECT, METHOD, ARRAY(OBJECT)) }).targetName }
val toStringTargetName by lazy { AstMethodRef(java.lang.Object::class.java.name.fqname, "toString", types.build { METHOD(STRING) }).targetName }
val hashCodeTargetName by lazy { AstMethodRef(java.lang.Object::class.java.name.fqname, "hashCode", types.build { METHOD(INT) }).targetName }
val getClassTargetName by lazy { AstMethodRef(java.lang.Object::class.java.name.fqname, "getClass", types.build { METHOD(CLASS) }).targetName }
protected fun setCurrentClass(clazz: AstClass) {
context.clazz = clazz
currentClass = clazz.name
params["CLASS"] = clazz.fqname
}
protected fun setCurrentMethod(method: AstMethod) {
context.method = method
currentMethod = method.ref
}
open fun genStm2(stm: AstStm, last: Boolean = false): Indenter {
this.stm = stm
return when (stm) {
is AstStm.STM_LABEL -> genStmLabel(stm)
is AstStm.GOTO -> genStmGoto(stm, last)
is AstStm.IF_GOTO -> genStmIfGoto(stm)
is AstStm.SWITCH_GOTO -> genStmSwitchGoto(stm)
is AstStm.NOP -> genStmNop(stm)
is AstStm.WHILE -> genStmWhile(stm)
is AstStm.IF -> genStmIf(stm)
is AstStm.IF_ELSE -> genStmIfElse(stm)
is AstStm.RETURN_VOID -> genStmReturnVoid(stm, last)
is AstStm.RETURN -> genStmReturnValue(stm, last)
is AstStm.STM_EXPR -> genStmExpr(stm)
is AstStm.STMS -> genStmStms(stm)
is AstStm.THROW -> genStmThrow(stm, last)
is AstStm.RETHROW -> genStmRethrow(stm, last)
is AstStm.MONITOR_ENTER -> genStmMonitorEnter(stm)
is AstStm.MONITOR_EXIT -> genStmMonitorExit(stm)
is AstStm.TRY_CATCH -> genStmTryCatch(stm)
is AstStm.SET_LOCAL -> genStmSetLocal(stm)
is AstStm.LINE -> genStmLine(stm)
is AstStm.SET_ARRAY -> genStmSetArray(stm)
is AstStm.SET_ARRAY_LITERALS -> genStmSetArrayLiterals(stm)
is AstStm.BREAK -> genStmBreak(stm)
is AstStm.CONTINUE -> genStmContinue(stm)
is AstStm.SET_FIELD_INSTANCE -> genStmSetFieldInstance(stm)
is AstStm.SET_FIELD_STATIC -> genStmSetFieldStatic(stm)
is AstStm.SWITCH -> genStmSwitch(stm)
is AstStm.SET_NEW_WITH_CONSTRUCTOR -> genStmSetNewWithConstructor(stm)
else -> noImpl("Statement $stm")
}
}
open fun genExpr2(e: AstExpr): String = when (e) {
is AstExpr.THIS -> genExprThis(e)
is AstExpr.TERNARY -> genExprTernary(e)
is AstExpr.LITERAL -> genExprLiteral(e)
is AstExpr.LITERAL_REFNAME -> genExprLiteralRefName(e)
is AstExpr.CAST -> genExprCast(e)
is AstExpr.CHECK_CAST -> genExprCheckCast(e)
is AstExpr.PARAM -> genExprParam(e)
is AstExpr.LOCAL -> genExprLocal(e)
is AstExpr.TYPED_LOCAL -> genExprTypedLocal(e)
is AstExpr.UNOP -> genExprUnop(e)
is AstExpr.BINOP -> genExprBinop(e)
is AstExpr.FIELD_STATIC_ACCESS -> genExprFieldStaticAccess(e)
is AstExpr.FIELD_INSTANCE_ACCESS -> genExprFieldInstanceAccess(e)
is AstExpr.ARRAY_ACCESS -> genExprArrayAccess(e)
is AstExpr.CAUGHT_EXCEPTION -> genExprCaughtException(e)
is AstExpr.ARRAY_LENGTH -> genExprArrayLength(e)
is AstExpr.INSTANCE_OF -> genExprInstanceOf(e)
is AstExpr.NEW -> genExprNew(e)
is AstExpr.NEW_WITH_CONSTRUCTOR -> genExprNewWithConstructor(e)
is AstExpr.NEW_ARRAY -> genExprNewArray(e)
is AstExpr.INTARRAY_LITERAL -> genExprIntArrayLit(e)
is AstExpr.STRINGARRAY_LITERAL -> genExprStringArrayLit(e)
is AstExpr.CALL_BASE -> genExprCallBase(e)
is AstExpr.INVOKE_DYNAMIC_METHOD -> genExprMethodClass(e)
else -> noImpl("Expression $e")
}
open fun genExprMethodClass(e: AstExpr.INVOKE_DYNAMIC_METHOD): String {
System.err.println("GenCommonGen.genExprMethodClass. Lambdas should be replaced by not including LambdaProgramFeature")
return "N::dummyMethodClass()"
}
private fun genExprCallBase(e: AstExpr.CALL_BASE): String {
// Determine method to call!
val e2 = if (e.isSpecial && e is AstExpr.CALL_INSTANCE) AstExprUtils.RESOLVE_SPECIAL(program, e, context) else e
val method = fixMethod(e2.method)
val refMethod = program[method] ?: invalidOp("Can't find method: $method while generating $context")
val refMethodClass = refMethod.containingClass
val clazz = method.containingClassType
val args = e2.args
val isStaticCall = e2 is AstExpr.CALL_STATIC
if (isStaticCall) {
refs.add(clazz)
mutableBody.initClassRef(clazz, "CALL_STATIC")
}
val isNativeCall = refMethodClass.isNative
//val nonNativeCall = if (isNativeCall) refMethod.annotationsList.nonNativeCall else false
val nonNativeCall = false
fun processArg(arg: AstExpr.Box, targetType: AstType) = processCallArg(arg.value, if (isNativeCall) convertToTarget(arg) else arg.genExpr(), targetType)
fun processArg(arg: AstExpr.Box) = processArg(arg, arg.type)
val callsiteBody = refMethod.annotationsList.getCallSiteBodyForTarget(targetName)?.template("JTranscCallSiteBody")
fun unbox(arg: AstExpr.Box): String {
val processed = processArg(arg)
val invoke = PeepholeMatcher.matchOptionalCastAndStaticInvoke(arg.value)
if (invoke != null) {
val ret = when (invoke.method.fid) {
"java.lang.Integer:valueOf:(I)Ljava/lang/Integer;",
"java.lang.Long:valueOf:(J)Ljava/lang/Long;",
"java.lang.Double:valueOf:(D)Ljava/lang/Double;",
"java.lang.Float:valueOf:(F)Ljava/lang/Float;"
-> {
processArg(invoke.args[0])
}
else -> null
}
//println(":::" + invoke.method.fid + " -> $ret")
if (ret != null) return ret
}
//println("Unbox: ${arg.value}")
//println("Unbox: ${invoke}")
//println("Unbox: $processed")
return N_unboxRaw(processed)
}
fun processArg2(param: AstArgumentCallWithAnnotations): String {
val arg = param.exprBox
if (param.annotationList.contains()) {
val lit = (arg.value as? AstExpr.LITERAL)
return if (lit != null) {
when (lit.value) {
is String -> quoteString(lit.value)
else -> unbox(arg)
}
} else {
unbox(arg)
}
}
return processArg(param.exprBox, param.arg.type)
}
val pparams = refMethod.getParamsWithAnnotationsBox(args)
if (callsiteBody != null) {
val args2Unquoted = arrayOfNulls(pparams.size)
val args2 = pparams.map { arginfo ->
val index = arginfo.arg.index
val arg = arginfo.exprBox
val paramAnnotations = refMethod.parameterAnnotationsList[index]
if (paramAnnotations.contains()) {
val lit = (arg.value as? AstExpr.LITERAL) ?: invalidOp("Used @JTranscLiteralParam without a literal: ${processArg(arg)} in $context")
lit.value.toString().template("JTranscLiteralParam")
} else if (paramAnnotations.contains()) {
val lit = (arg.value as? AstExpr.LITERAL)
if (lit != null) {
when (lit.value) {
is String -> {
args2Unquoted[index] = lit.value
quoteString(lit.value)
}
//is String -> lit.value
else -> unbox(arg)
}
} else {
unbox(arg)
}
} else {
processArg(arg)
}
}
val objStr = when (e2) {
is AstExpr.CALL_INSTANCE -> e2.obj.genNotNull()
else -> ""
}
val out = Regex("#([',.:])?((@|\\d)+)").replace(callsiteBody) { mr ->
val mustQuote = mr.groupValues[1]
val rid = mr.groupValues[2]
val id = rid.toIntOrNull2() ?: 0
val res = if (rid == "@") objStr else args2[id]
val res2 = if (rid != "@") args2Unquoted[id] ?: args2[id] else objStr
when (mustQuote) {
"'", "," -> quoteString(res2)
".", ":" -> {
access(res2, static = false, field = (mustQuote == "."))
}
else -> res
}
}
return out
} else {
val processedArgs = pparams.map { processArg2(it) }
val methodAccess = getTargetMethodAccess(refMethod, static = isStaticCall)
val result = when (e2) {
is AstExpr.CALL_STATIC -> genExprCallBaseStatic(e2, clazz, refMethodClass, method, methodAccess, processedArgs, nonNativeCall)
is AstExpr.CALL_SUPER -> {
genExprCallBaseSuper(e2, clazz, refMethodClass, method, methodAccess, processedArgs)
}
is AstExpr.CALL_INSTANCE -> genExprCallBaseInstance(e2, clazz, refMethodClass, method, methodAccess, processedArgs)
else -> invalidOp("Unexpected")
}
return if (isNativeCall) convertToJava(refMethod.methodType.ret, result) else result
}
}
open fun quoteString(str: String) = str.quote()
open fun processCallArg(e: AstExpr, str: String, targetType: AstType): String = str
open fun genExprCallBaseSuper(e2: AstExpr.CALL_SUPER, clazz: AstType.REF, refMethodClass: AstClass, method: AstMethodRef, methodAccess: String, args: List): String {
return "super$methodAccess(${args.joinToString(", ")})"
}
fun genExprCallBaseStatic(e2: AstExpr.CALL_STATIC, clazz: AstType.REF, refMethodClass: AstClass, method: AstMethodRef, methodAccess: String, args: List, nonNativeCall: Boolean): String {
if (nonNativeCall) {
}
return "${clazz.targetName}$methodAccess(${args.joinToString(", ")})"
}
open fun genExprCallBaseInstance(e2: AstExpr.CALL_INSTANCE, clazz: AstType.REF, refMethodClass: AstClass, method: AstMethodRef, methodAccess: String, args: List): String {
//if (method.isInstanceInit) {
// return "${e2.obj.value.withoutCasts().genNotNull()}$methodAccess(${args.joinToString(", ")})"
//} else {
return "${e2.obj.genNotNull()}$methodAccess(${args.joinToString(", ")})"
//}
}
fun convertToTarget(expr: AstExpr.Box): String = convertToTarget(expr.type, expr.genExpr())
fun convertToJava(expr: AstExpr.Box): String = convertToJava(expr.type, expr.genExpr())
fun convertToTarget(type: AstType, text: String): String = convertToFromTarget(type, text, toTarget = true)
fun convertToJava(type: AstType, text: String): String = convertToFromTarget(type, text, toTarget = false)
open fun convertToFromTarget(type: AstType, text: String, toTarget: Boolean): String {
return if (type is AstType.ARRAY) (if (toTarget) "N.unbox($text)" else "N.box($text)") else text
}
fun String.template(type: String = "template"): String = gen(this, context, type)
lateinit var mutableBody: MutableBody
lateinit var stm: AstStm
fun AstExpr.genExpr(): String = genExpr2(this)
fun AstExpr.Box.genExpr(): String = genExpr2(this.value)
fun AstStm.genStm(last: Boolean = false): Indenter = genStm2(this, last)
fun AstStm.Box.genStm(last: Boolean = false): Indenter = genStm2(this.value, last)
fun AstExpr.Box.genNotNull(): String = this.value.genNotNull()
fun AstField.isVisible(): Boolean = !this.invisible
fun AstMethod.isVisible(): Boolean = !this.invisible
val invisibleExternalList = program.allAnnotations
.map { it.toObject() }.filterNotNull()
.flatMap { it.classes.toList() }
fun AstClass.isVisible(): Boolean {
if (this.fqname in invisibleExternalList) return false
if (this.invisible) return false
return true
}
fun getFilesToCopy(target: String) = program.classes.flatMap { it.annotationsList.getTargetAddFiles(target) }.sortedBy { it.priority }
data class ConcatFile(val prepend: String?, val append: String?)
class CopyFile(val content: ByteArray, val dst: String, val isAsset: Boolean)
class PrependAppend(val prepend: String, val append: String)
open fun copyFilesExtra(output: SyncVfsFile) {
}
fun copyFiles(output: SyncVfsFile): PrependAppend {
val resourcesVfs = program.resourcesVfs
val copyFiles = getFilesToCopy(targetName.name)
copyFilesExtra(output)
//println(copyFiles)
val copyFilesTrans = copyFiles.filter { it.src.isNotEmpty() && it.dst.isNotEmpty() }.map {
val file = resourcesVfs[it.src]
if (it.process) {
CopyFile(file.readString().template("copyfile").toByteArray(), it.dst, it.isAsset)
} else {
CopyFile(file.read(), it.dst, it.isAsset)
}
}
// Copy assets
folders.copyAssetsTo(output)
for (file in copyFilesTrans) {
output[file.dst].ensureParentDir().write(file.content)
}
params["assetFiles"] = (params["assetFiles"] as List) + copyFilesTrans.map { output[it.dst] }
val concatFilesTrans = copyFiles.filter { it.append.isNotEmpty() || it.prepend.isNotEmpty() || it.prependAppend.isNotEmpty() }.map {
val prependAppend = if (it.prependAppend.isNotEmpty()) (resourcesVfs[it.prependAppend].readString() + "\n") else null
val prependAppendParts = prependAppend?.split("/* ## BODY ## */")
val prepend = if (prependAppendParts != null && prependAppendParts.size >= 2) prependAppendParts[0] else if (it.prepend.isNotEmpty()) (resourcesVfs[it.prepend].readString() + "\n") else null
val append = if (prependAppendParts != null && prependAppendParts.size >= 2) prependAppendParts[1] else if (it.append.isNotEmpty()) (resourcesVfs[it.append].readString() + "\n") else null
fun process(str: String?): String? = if (it.process) str?.template("includeFile") else str
ConcatFile(process(prepend), process(append))
}
return PrependAppend(
concatFilesTrans.map { it.prepend }.filterNotNull().joinToString("\n"),
concatFilesTrans.map { it.append }.filterNotNull().reversed().joinToString("\n")
)
}
//fun locateDefaultMethod(method: List) { sad ad ad as
//
//}
open fun AstExpr.genNotNull(): String = genExpr2(this)
fun AstBody.genBody(): Indenter = genBody2(this)
fun AstBody.genBodyWithFeatures(method: AstMethod): Indenter = genBody2WithFeatures(method, this)
open fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter {
val actualFeatures = if (body.traps.isNotEmpty()) methodFeaturesWithTraps else methodFeatures
val transformedBody = features.apply(method, body, actualFeatures, settings, types)
plugins.onAfterAppliedMethodBodyFeature(method, transformedBody)
return transformedBody.genBody()
}
// @TODO: Remove this from here, so new targets don't have to do this too!
// @TODO: AstFieldRef should be fine already, so fix it in asm_ast!
fun fixField(field: AstFieldRef): AstFieldRef = program[field].ref
fun fixMethod(method: AstMethodRef): AstMethodRef = program[method]?.ref
?: invalidOp("Can't find method $method while generating $context")
val allAnnotationTypes = program.allAnnotations.flatMap { it.getAllDescendantAnnotations() }.filter { it.runtimeVisible }.map { it.type }.distinct().map { program[it.name] }.toSet()
val trapsByStart = hashMapOf>()
val trapsByEnd = hashMapOf>()
open fun genBody2(body: AstBody): Indenter {
val method = context.method
this.mutableBody = MutableBody(method)
trapsByStart.clear()
trapsByEnd.clear()
for (trap in body.traps) {
trapsByStart.getOrPut(trap.start) { arrayListOf() } += trap
trapsByEnd.getOrPut(trap.end) { arrayListOf() } += trap
}
return Indenter {
@Suppress("LoopToCallChain", "Destructure")
for (local in body.locals) refs.add(local.type)
resetLocalsPrefix()
var info: Indenter? = null
linedeferred { line(info!!) }
line(genBodyLocals(body.locals))
if (body.traps.isNotEmpty()) line(genBodyTrapsPrefix())
val bodyContent = body.stm.genStm(last = true)
info = genLocalsPrefix()
if (method.isClassOrInstanceInit) mutableBody.initClassRef(context.clazz.ref, "self")
for ((clazzRef, reasons) in mutableBody.referencedClasses) {
if (!program[clazzRef.name].mustGenerate) continue
if (!method.isClassOrInstanceInit && (context.clazz.ref == clazzRef)) continue // Calling internal methods (which should be initialized already!)
line(genBodyStaticInitPrefix(clazzRef, reasons))
}
line(bodyContent)
}
}
val AstClass.nativeNameInfo: JTranscNativeName? get() = this.nativeNameForTarget([email protected])
val AstClass.nativeName: String? get() = this.nativeNameInfo?.value
open fun genStmSetNewWithConstructor(stm: AstStm.SET_NEW_WITH_CONSTRUCTOR): Indenter = indent {
val newClazz = program[stm.target.name]
refs.add(stm.target)
val commaArgs = stm.args.map { it.genExpr() }.joinToString(", ")
val className = stm.target.targetName
val targetLocalName = stm.local.targetName
if (newClazz.nativeName != null) {
imports += FqName(newClazz.nativeName!!)
line("$targetLocalName = new $className($commaArgs);")
} else {
line("$targetLocalName = new $className();")
line("$targetLocalName.${stm.method.targetName}($commaArgs);")
}
}
open fun resetLocalsPrefix() = Unit
open fun genLocalsPrefix(): Indenter = indent { }
open fun genBodyLocals(locals: List): Indenter = indent { for (local in locals) line(local.decl) }
open fun genBodyTrapsPrefix() = Indenter(AstLocal(0, "J__exception__", AstType.THROWABLE).decl)
open fun genExprCaughtException(e: AstExpr.CAUGHT_EXCEPTION): String = "J__exception__"
open val AstType.localDeclType: String get() = this.targetName
open val AstLocal.decl: String get() = "${this.type.localDeclType} ${this.targetName} = ${this.type.nativeDefaultString};"
open val AstArgument.decl: String get() = "${this.type.localDeclType} ${this.targetName}"
open fun genStmSetFieldStatic(stm: AstStm.SET_FIELD_STATIC): Indenter = indent {
refs.add(stm.clazz)
mutableBody.initClassRef(fixField(stm.field).classRef, "SET_FIELD_STATIC")
val left = fixField(stm.field).nativeStaticText
val right = stm.expr.genExpr()
if (allowAssignItself || left != right) {
// Avoid: Assigning a value to itself
line(genStmSetFieldStaticActual(stm, left, stm.field, right))
}
}
open fun genStmSetFieldStaticActual(stm: AstStm.SET_FIELD_STATIC, left: String, field: AstFieldRef, right: String): Indenter = indent {
line("$left = $right;")
}
fun genStmSwitchGoto(stm: AstStm.SWITCH_GOTO): Indenter = indent {
flowBlock(FlowKind.SWITCH) {
line("switch (${stm.subject.genExpr()})") {
for ((values, label) in stm.cases) {
line("${buildMultipleCase(values)} ${genGoto(label, false)}")
}
line("default: ${genGoto(stm.default, false)}")
}
}
}
open fun buildMultipleCase(cases: List): String = if (casesWithCommas) {
"case " + cases.joinToString(",") + ":"
} else {
cases.map { "case $it:" }.joinToString(" ")
}
open fun genStmIfGoto(stm: AstStm.IF_GOTO): Indenter = Indenter("if (${stm.cond.genExpr()}) " + genGoto(stm.label, false))
open fun genStmGoto(stm: AstStm.GOTO, last: Boolean): Indenter = Indenter(genGoto(stm.label, last))
open fun genGoto(label: AstLabel, last: Boolean) = "goto ${label.name};"
open fun genStmSetFieldInstance(stm: AstStm.SET_FIELD_INSTANCE): Indenter = indent {
val left = buildInstanceField(stm.left.genExpr(), fixField(stm.field))
val right = stm.expr.genExpr()
if (allowAssignItself || left != right) {
// Avoid: Assigning a value to itself
line(actualSetField(stm, left, right))
}
}
open fun actualSetField(stm: AstStm.SET_FIELD_INSTANCE, left: String, right: String): String = "$left = $right;"
open fun genStmContinue(stm: AstStm.CONTINUE) = Indenter("continue;")
open fun genStmBreak(stm: AstStm.BREAK) = Indenter("break;")
open fun genStmLabel(stm: AstStm.STM_LABEL): Indenter = Indenter {
if (stm.label in trapsByEnd) {
for (trap in trapsByEnd[stm.label]!!) line(genStmRawCatch(trap))
}
line(genStmLabelCore(stm))
if (stm.label in trapsByStart) {
for (trap in trapsByStart[stm.label]!!) line(genStmRawTry(trap))
}
}
open fun genLabel(label: AstLabel) = "${label.name}:;"
fun genStmLabelCore(stm: AstStm.STM_LABEL) = genLabel(stm.label)
open fun genStmRawTry(trap: AstTrap): Indenter = Indenter {
}
open fun genStmRawCatch(trap: AstTrap): Indenter = Indenter {
}
enum class FlowKind { SWITCH, WHILE }
val flowBlocks = ArrayList()
inline fun flowBlock(kind: FlowKind, callback: () -> T): T {
try {
flowBlocks += kind
return callback()
} finally {
flowBlocks.removeAt(flowBlocks.size - 1)
}
}
open fun genStmSwitch(stm: AstStm.SWITCH): Indenter = indent {
flowBlock(FlowKind.SWITCH) {
if (stm.cases.isNotEmpty() || !stm.default.value.isEmpty()) {
line("switch (${stm.subject.genExpr()})") {
for (case in stm.cases) {
val values = case.first
val caseStm = case.second
if (caseStm.value.isSingleStm()) {
val append = if (!defaultGenStmSwitchHasBreaks || caseStm.value.lastStm().isBreakingFlow()) "" else "break;"
line(buildMultipleCase(values) + caseStm.genStm().toString().trim() + " " + append)
} else {
line(buildMultipleCase(values))
indent {
line(caseStm.genStm())
if (defaultGenStmSwitchHasBreaks && !caseStm.value.lastStm().isBreakingFlow()) line("break;")
}
}
}
if (languageRequiresDefaultInSwitch || !stm.default.value.isEmpty()) {
line("default:")
indent {
line(stm.default.genStm())
if (defaultGenStmSwitchHasBreaks && !stm.default.value.lastStm().isBreakingFlow()) line("break;")
}
}
}
}
}
}
open fun genExprNewArray(e: AstExpr.NEW_ARRAY): String {
refs.add(e.type.elementType)
val desc = e.type.mangle().replace('/', '.') // Internal to normal name!?
return when (e.counts.size) {
1 -> createArraySingle(e, desc)
else -> createArrayMultisure(e, desc)
}
}
open fun genExprIntArrayLit(e: AstExpr.INTARRAY_LITERAL): String {
return "JA_I${staticAccessOperator}T([" + e.values.joinToString(",") + "])"
}
open fun genExprStringArrayLit(e: AstExpr.STRINGARRAY_LITERAL): String {
return "JA_J${staticAccessOperator}fromArray([" + e.values.joinToString(",") + "], \"Ljava/lang/String;\")"
}
open fun createArraySingle(e: AstExpr.NEW_ARRAY, desc: String): String {
return if (e.type.elementType !is AstType.Primitive) {
"new $ObjectArrayType(${e.counts[0].genExpr()}, " + quoteString(desc) + ")"
} else {
"new ${e.type.targetName}(${e.counts[0].genExpr()})"
}
}
open fun createArrayMultisure(e: AstExpr.NEW_ARRAY, desc: String): String {
return "$ObjectArrayType${staticAccessOperator}createMultiSure([${e.counts.map { it.genExpr() }.joinToString(", ")}], \"$desc\")"
}
open fun genExprNew(e: AstExpr.NEW): String {
refs.add(e.target)
val className = e.target.targetName
return "(new $className())"
}
open fun genExprNewWithConstructor(e: AstExpr.NEW_WITH_CONSTRUCTOR): String {
return genExprCallBase(AstExpr.CALL_INSTANCE(
AstExpr.NEW(e.target),
e.constructor,
e.args.map { it.value },
isSpecial = true
))
}
open fun genExprInstanceOf(e: AstExpr.INSTANCE_OF): String {
refs.add(e.checkType)
return N_is(e.expr.genExpr(), e.checkType)
}
open fun genExprArrayLength(e: AstExpr.ARRAY_LENGTH): String {
return e.array.genNotNull().instanceAccessField("length")
}
fun AstType.resolve(): AstType = when (this) {
is AstType.COMMON -> this.resolve(program)
is AstType.MUTABLE -> this.ref.resolve()
else -> this
}
private fun SHIFT_FIX_32(r: Int): Int {
if (r < 0) {
return (32 - ((-r) and 0x1F)) and 0x1F;
} else {
return r and 0x1F;
}
}
private fun SHIFT_FIX_64(r: Int): Int {
if (r < 0) {
return (64 - ((-r) and 0x3F)) and 0x3F;
} else {
return r and 0x3F;
}
}
open fun genExprBinop(e: AstExpr.BINOP): String {
val resultType = e.type.resolve()
val leftType = e.left.type.resolve()
val rightType = e.right.type.resolve()
val lv = e.left.value
val rv = e.right.value
val l = lv.genExpr()
val r = rv.genExpr()
val op = e.op
val commonType = leftType
fun invalid(): Nothing = invalidOp("leftType=$leftType, rightType=$rightType, op=$op, resultType=$resultType")
val result = when (commonType) {
AstType.BOOL -> when (op) {
AstBinop.BAND -> N_zand(l, r)
AstBinop.BOR -> N_zor(l, r)
AstBinop.EQ -> N_zeq(l, r)
AstBinop.NE -> N_zne(l, r)
else -> invalid()
}
AstType.LONG -> when (op) {
AstBinop.ADD -> N_ladd(l, r)
AstBinop.SUB -> N_lsub(l, r)
AstBinop.MUL -> N_lmul(l, r)
AstBinop.DIV -> N_ldiv(l, r)
AstBinop.REM -> N_lrem(l, r)
AstBinop.EQ -> N_leq(l, r)
AstBinop.NE -> N_lne(l, r)
AstBinop.GE -> N_lge(l, r)
AstBinop.LE -> N_lle(l, r)
AstBinop.LT -> N_llt(l, r)
AstBinop.GT -> N_lgt(l, r)
AstBinop.AND -> N_land(l, r)
AstBinop.OR -> N_lor(l, r)
AstBinop.XOR -> N_lxor(l, r)
AstBinop.SHL -> {
val rv2 = rv.withoutCasts()
if (rv2 is AstExpr.LITERAL) {
N_lshl_cst(l, SHIFT_FIX_64(rv2.valueAsInt))
} else {
N_lshl(l, r)
}
}
AstBinop.SHR -> {
val rv2 = rv.withoutCasts()
if (rv2 is AstExpr.LITERAL) {
N_lshr_cst(l, SHIFT_FIX_64(rv2.valueAsInt))
} else {
N_lshr(l, r)
}
}
AstBinop.USHR -> {
val rv2 = rv.withoutCasts()
if (rv2 is AstExpr.LITERAL) {
N_lushr_cst(l, SHIFT_FIX_64(rv2.valueAsInt))
} else {
N_lushr(l, r)
}
}
AstBinop.LCMP -> N_lcmp(l, r) // long,long -> int
else -> invalid()
}
AstType.BYTE, AstType.CHAR, AstType.SHORT, AstType.INT -> when (op) {
AstBinop.ADD -> N_iadd(l, r)
AstBinop.SUB -> N_isub(l, r)
AstBinop.MUL -> N_imul(l, r)
AstBinop.DIV -> N_idiv(l, r)
AstBinop.REM -> N_irem(l, r)
AstBinop.EQ -> N_ieq(l, r)
AstBinop.NE -> N_ine(l, r)
AstBinop.GE -> N_ige(l, r)
AstBinop.LE -> N_ile(l, r)
AstBinop.LT -> N_ilt(l, r)
AstBinop.GT -> N_igt(l, r)
AstBinop.AND -> N_iand(l, r)
AstBinop.OR -> N_ior(l, r)
AstBinop.XOR -> N_ixor(l, r)
AstBinop.SHL -> {
val rv2 = rv.withoutCasts()
if (rv2 is AstExpr.LITERAL) {
N_ishl_cst(l, SHIFT_FIX_32(rv2.valueAsInt))
} else {
N_ishl(l, r)
}
}
AstBinop.SHR -> {
val rv2 = rv.withoutCasts()
if (rv2 is AstExpr.LITERAL) {
N_ishr_cst(l, SHIFT_FIX_32(rv2.valueAsInt))
} else {
N_ishr(l, r)
}
}
AstBinop.USHR -> {
val rv2 = rv.withoutCasts()
if (rv2 is AstExpr.LITERAL) {
N_iushr_cst(l, SHIFT_FIX_32(rv2.valueAsInt))
} else {
N_iushr(l, r)
}
}
else -> invalid()
}
AstType.FLOAT -> when (op) {
AstBinop.ADD -> N_fadd(l, r)
AstBinop.SUB -> N_fsub(l, r)
AstBinop.MUL -> N_fmul(l, r)
AstBinop.DIV -> N_fdiv(l, r)
AstBinop.REM -> N_frem(l, r)
AstBinop.EQ -> N_feq(l, r)
AstBinop.NE -> N_fne(l, r)
AstBinop.GE -> N_fge(l, r)
AstBinop.LE -> N_fle(l, r)
AstBinop.LT -> N_flt(l, r)
AstBinop.GT -> N_fgt(l, r)
AstBinop.CMPL -> N_fcmpl(l, r)
AstBinop.CMPG -> N_fcmpg(l, r)
else -> invalid()
}
AstType.DOUBLE -> when (op) {
AstBinop.ADD -> N_dadd(l, r)
AstBinop.SUB -> N_dsub(l, r)
AstBinop.MUL -> N_dmul(l, r)
AstBinop.DIV -> N_ddiv(l, r)
AstBinop.REM -> N_drem(l, r)
AstBinop.EQ -> N_deq(l, r)
AstBinop.NE -> N_dne(l, r)
AstBinop.GE -> N_dge(l, r)
AstBinop.LE -> N_dle(l, r)
AstBinop.LT -> N_dlt(l, r)
AstBinop.GT -> N_dgt(l, r)
AstBinop.CMPL -> N_dcmpl(l, r)
AstBinop.CMPG -> N_dcmpg(l, r)
else -> invalid()
}
is AstType.Reference -> when (op) {
AstBinop.EQ -> N_obj_eq(l, r)
AstBinop.NE -> N_obj_ne(l, r)
else -> invalid()
}
else -> invalid()
}
return when (resultType) {
AstType.BOOL -> N_z2z(result)
AstType.BYTE -> N_i2b(result)
AstType.CHAR -> N_i2c(result)
AstType.SHORT -> N_i2s(result)
AstType.INT -> N_i(result)
AstType.LONG -> N_j2j(result)
AstType.FLOAT -> N_f2f(result)
AstType.DOUBLE -> N_d2d(result)
else -> invalid()
}
}
open fun genExprArrayAccess(e: AstExpr.ARRAY_ACCESS): String = N_AGET_T(e.array.type.resolve(program).asArray(), e.array.type.elementType, e.array.genNotNull(), e.index.genExpr())
open fun genExprFieldStaticAccess(e: AstExpr.FIELD_STATIC_ACCESS): String {
refs.add(e.clazzName)
mutableBody.initClassRef(fixField(e.field).classRef, "FIELD_STATIC_ACCESS")
return "${fixField(e.field).nativeStaticText}"
}
open fun genExprFieldInstanceAccess(e: AstExpr.FIELD_INSTANCE_ACCESS): String {
return buildInstanceField(e.expr.genNotNull(), fixField(e.field))
}
private fun genExprUnop(e: AstExpr.UNOP): String {
val resultType = e.type
val es = e.right.genExpr()
return when (resultType) {
AstType.BOOL -> {
when (e.op) {
AstUnop.NOT -> N_znot(es)
else -> invalidOp("${e.op} for bool")
}
}
AstType.LONG -> {
when (e.op) {
AstUnop.NEG -> N_lneg(es)
AstUnop.INV -> N_linv(es)
else -> invalidOp("${e.op} for longs")
}
}
AstType.BYTE, AstType.SHORT, AstType.CHAR, AstType.INT -> {
val expr = when (e.op) {
AstUnop.NEG -> N_ineg(es)
AstUnop.INV -> N_iinv(es)
else -> invalidOp("${e.op} for $resultType")
}
when (resultType) {
AstType.INT -> N_i(expr)
AstType.CHAR -> N_i2c(expr)
AstType.SHORT -> N_i2s(expr)
AstType.BYTE -> N_i2b(expr)
else -> expr
}
}
AstType.FLOAT -> {
when (e.op) {
AstUnop.NEG -> N_fneg(es)
else -> invalidOp("${e.op} for float")
}
}
AstType.DOUBLE -> {
when (e.op) {
AstUnop.NEG -> N_dneg(es)
else -> invalidOp("${e.op} for double")
}
}
else -> invalidOp("Invalid type")
}
}
inline protected fun indent(init: Indenter.() -> Unit): Indenter = Indenter(init)
open fun genStmSetArray(stm: AstStm.SET_ARRAY): Indenter {
val array = stm.array.genNotNull()
//if (array == "((JA_B)(((java_lang_Object)(p1))))") println(array)
val res = N_ASET_T(
stm.array.type.resolve(program).asArray(),
stm.array.type.elementType,
array,
stm.index.genExpr(),
stm.expr.genExpr()
)
return Indenter(res)
}
open fun genStmSetArrayLiterals(stm: AstStm.SET_ARRAY_LITERALS) = Indenter {
var n = 0
for (v in stm.values) {
line(genStmSetArray(AstStm.SET_ARRAY(stm.array.value, (stm.startIndex + n).lit, v.value)))
n++
}
}
open fun genExprParam(e: AstExpr.PARAM) = e.argument.targetName
fun genExprLocal(e: AstLocal) = if (localVarPrefix.isEmpty()) e.targetName else localVarPrefix + e.targetName
fun genExprLocal(e: AstExpr.LOCAL) = if (localVarPrefix.isEmpty()) e.local.targetName else localVarPrefix + e.local.targetName
fun genExprTypedLocal(e: AstExpr.TYPED_LOCAL) = genExprCast(genExprLocal(e.local), e.local.type, e.type)
fun genStmLine(stm: AstStm.LINE) = indent {
mark(stm)
if (GENERATE_LINE_NUMBERS) line("// ${stm.line}")
}
open fun genStmTryCatch(stm: AstStm.TRY_CATCH) = indent {
line("try") {
line(stm.trystm.genStm())
}
line("catch (J__i__exception__)") {
line("J__exception__ = J__i__exception__;")
line(stm.catch.genStm())
}
}
open fun genStmMonitorEnter(stm: AstStm.MONITOR_ENTER) = indent { line("// MONITOR_ENTER") }
open fun genStmMonitorExit(stm: AstStm.MONITOR_EXIT) = indent { line("// MONITOR_EXIT") }
open fun genStmThrow(stm: AstStm.THROW, last: Boolean) = Indenter("throw ${stm.exception.genExpr()};")
open fun genStmRethrow(stm: AstStm.RETHROW, last: Boolean) = Indenter("""throw J__i__exception__;""")
open fun genStmStms(stm: AstStm.STMS) = indent {
val stms = stm.stms
for (i in stms.indices) {
val last = i == stms.lastIndex
line(stms[i].genStm(last))
}
}
open fun genStmExpr(stm: AstStm.STM_EXPR) = Indenter("${stm.expr.genExpr()};")
open fun genStmReturnVoid(stm: AstStm.RETURN_VOID, last: Boolean) = Indenter(if (context.method.methodVoidReturnThis) "return " + genExprThis(AstExpr.THIS("Dummy".fqname)) + ";" else "return;")
open fun genStmReturnValue(stm: AstStm.RETURN, last: Boolean) = Indenter("return ${stm.retval.genExpr()};")
fun genStmWhile(stm: AstStm.WHILE) = indent {
flowBlock(FlowKind.WHILE) {
line("while (${stm.cond.genExpr()})") {
line(stm.iter.genStm())
}
}
}
open fun genStmIf(stm: AstStm.IF) = indent {
line("if (${stm.cond.genExpr()})") { line(stm.strue.genStm()) }
}
open fun genStmIfElse(stm: AstStm.IF_ELSE) = indent {
line("if (${stm.cond.genExpr()})") { line(stm.strue.genStm()) }
line("else") { line(stm.sfalse.genStm()) }
}
open val allowAssignItself = false
open fun genStmSetLocal(stm: AstStm.SET_LOCAL) = indent {
val localName = stm.local.targetName
val expr = stm.expr.genExpr()
if (allowAssignItself || localName != expr) {
// Avoid: Assigning a value to itself
line(actualSetLocal(stm, localName, expr))
}
}
open fun actualSetLocal(stm: AstStm.SET_LOCAL, localName: String, exprStr: String): String {
return "$localName = $exprStr;"
}
open fun genStmNop(stm: AstStm.NOP) = Indenter.EMPTY
open fun genExprTernary(e: AstExpr.TERNARY): String = "((${e.cond.genExpr()}) ? (${e.etrue.genExpr()}) : (${e.efalse.genExpr()}))"
open fun genExprThis(e: AstExpr.THIS): String = "this"
open fun genExprLiteral(e: AstExpr.LITERAL): String {
val value = e.value
return when (value) {
null -> null.escapedConstant
is AstType -> value.escapedConstant
is String -> value.escapedConstant
is Boolean -> value.escapedConstant
is Byte -> value.escapedConstant
is Char -> value.escapedConstant
is Short -> value.escapedConstant
is Int -> value.escapedConstant
is Long -> value.escapedConstant
is Float -> value.escapedConstant
is Double -> value.escapedConstant
else -> invalidOp("Unsupported value $value")
}
}
open fun genExprLiteralRefName(e: AstExpr.LITERAL_REFNAME): String {
val value = e.value
return when (value) {
is AstType.REF -> value.targetName
is AstMethodRef -> value.targetName
is AstFieldRef -> value.targetName
else -> invalidOp("Unknown AstExpr.LITERAL_REFNAME value type : ${value?.javaClass} : $value")
}.escapedConstant
}
class MutableBody(val method: AstMethod) {
val referencedClasses = hashMapOf>()
fun initClassRef(classRef: AstType.REF, reason: String) {
referencedClasses.putIfAbsentJre7(classRef, arrayListOf())
referencedClasses[classRef]!! += reason
}
}
open fun genExprCast(e: AstExpr.CAST): String = genExprCast(e.subject.genExpr(), e.from, e.to)
open fun genExprCheckCast(e: AstExpr.CHECK_CAST): String = genExprCastChecked(e.subject.genExpr(), e.from.asReference(), e.to.asReference())
open fun genExprCastChecked(e: String, from: AstType.Reference, to: AstType.Reference): String = N_c(e, from, to)
open fun genExprCast(e: String, from: AstType, to: AstType): String {
refs.add(from)
refs.add(to)
if (from == to) return e
if ((from !is AstType.Primitive) && (to is AstType.Primitive)) {
return when (from) {
// @TODO: Check!
AstType.BOOL.CLASSTYPE -> genExprCast(N_unboxBool(e), AstType.BOOL, to)
AstType.BYTE.CLASSTYPE -> genExprCast(N_unboxByte(e), AstType.BYTE, to)
AstType.SHORT.CLASSTYPE -> genExprCast(N_unboxShort(e), AstType.SHORT, to)
AstType.CHAR.CLASSTYPE -> genExprCast(N_unboxChar(e), AstType.CHAR, to)
AstType.INT.CLASSTYPE -> genExprCast(N_unboxInt(e), AstType.INT, to)
AstType.LONG.CLASSTYPE -> genExprCast(N_unboxLong(e), AstType.LONG, to)
AstType.FLOAT.CLASSTYPE -> genExprCast(N_unboxFloat(e), AstType.FLOAT, to)
AstType.DOUBLE.CLASSTYPE -> genExprCast(N_unboxDouble(e), AstType.DOUBLE, to)
//AstType.OBJECT -> genCast(genCast(e, from, to.CLASSTYPE), to.CLASSTYPE, to)
//else -> noImpl("Unhandled conversion $e : $from -> $to")
else -> genExprCast(genExprCast(e, from, to.CLASSTYPE), to.CLASSTYPE, to)
}
}
if ((from is AstType.Primitive) && (to !is AstType.Primitive)) {
return when (from) {
AstType.BOOL -> N_boxBool(e)
AstType.BYTE -> N_boxByte(e)
AstType.SHORT -> N_boxShort(e)
AstType.CHAR -> N_boxChar(e)
AstType.INT -> N_boxInt(e)
AstType.LONG -> N_boxLong(e)
AstType.FLOAT -> N_boxFloat(e)
AstType.DOUBLE -> N_boxDouble(e)
else -> invalidOp
}
}
fun unhandled(): String {
noImplWarn("Unhandled conversion ($from -> $to) at $context")
return "($e)"
}
return when (from) {
is AstType.BOOL, is AstType.INT, is AstType.CHAR, is AstType.SHORT, is AstType.BYTE -> {
val e2 = if (from == AstType.BOOL) N_z2i(e) else "$e"
when (to) {
is AstType.BOOL -> N_i2z(e2)
is AstType.BYTE -> N_i2b(e2)
is AstType.CHAR -> N_i2c(e2)
is AstType.SHORT -> N_i2s(e2)
is AstType.INT -> N_i2i(e2)
is AstType.LONG -> N_i2j(e2)
is AstType.FLOAT -> N_i2f(e2)
is AstType.DOUBLE -> N_i2d(e2)
else -> unhandled()
}
}
is AstType.FLOAT -> {
when (to) {
is AstType.BOOL -> N_i2z(N_f2i(e))
is AstType.BYTE -> N_i2b(N_f2i(e))
is AstType.CHAR -> N_i2c(N_f2i(e))
is AstType.SHORT -> N_i2s(N_f2i(e))
is AstType.INT -> N_i2i(N_f2i(e))
is AstType.LONG -> N_f2j(e)
is AstType.FLOAT -> N_f2f(e)
is AstType.DOUBLE -> N_f2d(e)
else -> unhandled()
}
}
is AstType.DOUBLE -> {
when (to) {
is AstType.BOOL -> N_i2z(N_d2i(e))
is AstType.BYTE -> N_i2b(N_d2i(e))
is AstType.CHAR -> N_i2c(N_d2i(e))
is AstType.SHORT -> N_i2s(N_d2i(e))
is AstType.INT -> N_i2i(N_d2i(e))
is AstType.LONG -> N_d2j(e)
is AstType.FLOAT -> N_d2f(e)
is AstType.DOUBLE -> N_d2d(e)
else -> unhandled()
}
}
is AstType.LONG -> {
when (to) {
is AstType.BOOL -> N_i2z(N_j2i(e))
is AstType.BYTE -> N_i2b(N_j2i(e))
is AstType.CHAR -> N_i2c(N_j2i(e))
is AstType.SHORT -> N_i2s(N_j2i(e))
is AstType.INT -> N_j2i(e)
is AstType.LONG -> N_j2j(e)
is AstType.FLOAT -> N_j2f(e)
is AstType.DOUBLE -> N_j2d(e)
else -> unhandled()
}
}
is AstType.REF, is AstType.ARRAY, is AstType.GENERIC -> {
when (to) {
FUNCTION_REF -> N_getFunction(e)
else -> {
N_c(e, from, to)
}
}
}
is AstType.NULL -> "$e"
else -> unhandled()
}
}
val FUNCTION_REF = AstType.REF(com.jtransc.JTranscFunction::class.java.name)
open protected fun N_AGET_T(arrayType: AstType.ARRAY, elementType: AstType, array: String, index: String) = "($array[$index])"
open protected fun N_ASET_T(arrayType: AstType.ARRAY, elementType: AstType, array: String, index: String, value: String) = "$array[$index] = $value;"
fun N_box(type: AstType, e: String) = when (type) {
is AstType.Primitive -> N_box(type, e)
else -> e
}
fun N_box(type: AstType.Primitive, e: String) = when (type) {
AstType.VOID -> N_boxVoid(e)
AstType.BOOL -> N_boxBool(e)
AstType.BYTE -> N_boxByte(e)
AstType.CHAR -> N_boxChar(e)
AstType.SHORT -> N_boxShort(e)
AstType.INT -> N_boxInt(e)
AstType.LONG -> N_boxLong(e)
AstType.FLOAT -> N_boxFloat(e)
AstType.DOUBLE -> N_boxDouble(e)
else -> invalidOp("Don't know how to box $type")
}
fun N_unbox(type: AstType, e: String) = when (type) {
is AstType.Primitive -> N_unbox(type, e)
//else -> N_unboxRaw(e)
else -> e
}
fun N_unbox(type: AstType.Primitive, e: String) = when (type) {
AstType.VOID -> N_unboxVoid(e)
AstType.BOOL -> N_unboxBool(e)
AstType.BYTE -> N_unboxByte(e)
AstType.CHAR -> N_unboxChar(e)
AstType.SHORT -> N_unboxShort(e)
AstType.INT -> N_unboxInt(e)
AstType.LONG -> N_unboxLong(e)
AstType.FLOAT -> N_unboxFloat(e)
AstType.DOUBLE -> N_unboxDouble(e)
else -> invalidOp("Don't know how to unbox $type")
}
open fun N_func(name: String, args: String) = "N$staticAccessOperator$name($args)"
open protected fun N_boxVoid(e: String) = N_func("boxVoid", "$e")
open protected fun N_boxBool(e: String) = N_func("boxBool", "$e")
open protected fun N_boxByte(e: String) = N_func("boxByte", "$e")
open protected fun N_boxShort(e: String) = N_func("boxShort", "$e")
open protected fun N_boxChar(e: String) = N_func("boxChar", "$e")
open protected fun N_boxInt(e: String) = N_func("boxInt", "$e")
open protected fun N_boxLong(e: String) = N_func("boxLong", "$e")
open protected fun N_boxFloat(e: String) = N_func("boxFloat", "$e")
open protected fun N_boxDouble(e: String) = N_func("boxDouble", "$e")
open protected fun N_unboxVoid(e: String) = N_func("unboxVoid", "$e")
open protected fun N_unboxBool(e: String) = N_func("unboxBool", "$e")
open protected fun N_unboxByte(e: String) = N_func("unboxByte", "$e")
open protected fun N_unboxShort(e: String) = N_func("unboxShort", "$e")
open protected fun N_unboxChar(e: String) = N_func("unboxChar", "$e")
open protected fun N_unboxInt(e: String) = N_func("unboxInt", "$e")
open protected fun N_unboxLong(e: String) = N_func("unboxLong", "$e")
open protected fun N_unboxFloat(e: String) = N_func("unboxFloat", "$e")
open protected fun N_unboxDouble(e: String) = N_func("unboxDouble", "$e")
open protected fun N_unboxRaw(e: String) = N_func("unbox", "$e")
open protected fun N_is(a: String, b: AstType.Reference) = N_is(a, b.targetName)
open protected fun N_is(a: String, b: String) = N_func("is", "$a, $b")
open protected fun N_z2z(str: String) = "($str)"
open protected fun N_z2i(str: String) = N_func("z2i", "$str")
open protected fun N_i(str: String) = "(($str)|0)"
open protected fun N_i2z(str: String) = "(($str)!=0)"
open protected fun N_i2b(str: String) = "(($str)<<24>>24)"
open protected fun N_i2c(str: String) = "(($str)&0xFFFF)"
open protected fun N_i2s(str: String) = "(($str)<<16>>16)"
open protected fun N_f2i(str: String) = "(($str)|0)"
open protected fun N_i2i(str: String) = N_i(str)
open protected fun N_i2j(str: String) = N_func("i2j", "$str")
open protected fun N_i2f(str: String) = "(($str))"
open protected fun N_i2d(str: String) = "($str)"
open protected fun N_f2f(str: String) = "($str)"
open protected fun N_f2d(str: String) = "($str)"
open protected fun N_d2f(str: String) = "(($str))"
open protected fun N_d2d(str: String) = "($str)"
open protected fun N_d2i(str: String) = "(($str)|0)"
open protected fun N_f2j(str: String) = N_func("f2j", str)
open protected fun N_d2j(str: String) = N_func("d2j", str)
open protected fun N_j2i(str: String) = N_func("j2i", "$str")
open protected fun N_j2j(str: String) = "($str)"
open protected fun N_j2f(str: String) = N_func("j2f", "$str")
open protected fun N_j2d(str: String) = N_func("j2d", "$str")
open protected fun N_getFunction(str: String) = N_func("getFunction", "$str")
open protected fun N_c(str: String, from: AstType, to: AstType) = "($str)"
open protected fun N_ineg(str: String) = "-($str)"
open protected fun N_fneg(str: String) = "-($str)"
open protected fun N_dneg(str: String) = "-($str)"
open protected fun N_iinv(str: String) = "~($str)"
open protected fun N_znot(str: String) = "!($str)"
open protected fun N_zand(l: String, r: String) = "($l && $r)"
open protected fun N_zor(l: String, r: String) = "($l || $r)"
open protected fun N_zeq(l: String, r: String) = N_c_eq(l, r)
open protected fun N_zne(l: String, r: String) = N_c_ne(l, r)
open protected fun N_c_add(l: String, r: String) = "($l + $r)"
open protected fun N_c_sub(l: String, r: String) = "($l - $r)"
open protected fun N_c_mul(l: String, r: String) = "($l * $r)"
open protected fun N_c_div(l: String, r: String) = "($l / $r)"
open protected fun N_c_rem(l: String, r: String) = "($l % $r)"
open protected fun N_c_eq(l: String, r: String) = "($l == $r)"
open protected fun N_c_ne(l: String, r: String) = "($l != $r)"
open protected fun N_c_ge(l: String, r: String) = "($l >= $r)"
open protected fun N_c_le(l: String, r: String) = "($l <= $r)"
open protected fun N_c_lt(l: String, r: String) = "($l < $r)"
open protected fun N_c_gt(l: String, r: String) = "($l > $r)"
open protected fun N_c_and(l: String, r: String) = "($l & $r)"
open protected fun N_c_or(l: String, r: String) = "($l | $r)"
open protected fun N_c_xor(l: String, r: String) = "($l ^ $r)"
open protected fun N_c_shl(l: String, r: String) = "($l << $r)"
open protected fun N_c_shr(l: String, r: String) = "($l >> $r)"
open protected fun N_c_ushr(l: String, r: String) = "($l >>> $r)"
open protected fun N_inew(value: Int) = "$value"
open protected fun N_iadd(l: String, r: String) = N_c_add(l, r)
open protected fun N_isub(l: String, r: String) = N_c_sub(l, r)
open protected fun N_imul(l: String, r: String) = N_c_mul(l, r)
open protected fun N_idiv(l: String, r: String) = N_c_div(l, r)
open protected fun N_irem(l: String, r: String) = N_c_rem(l, r)
open protected fun N_ieq(l: String, r: String) = N_c_eq(l, r)
open protected fun N_ine(l: String, r: String) = N_c_ne(l, r)
open protected fun N_ige(l: String, r: String) = N_c_ge(l, r)
open protected fun N_ile(l: String, r: String) = N_c_le(l, r)
open protected fun N_ilt(l: String, r: String) = N_c_lt(l, r)
open protected fun N_igt(l: String, r: String) = N_c_gt(l, r)
open protected fun N_iand(l: String, r: String) = N_c_and(l, r)
open protected fun N_ior(l: String, r: String) = N_c_or(l, r)
open protected fun N_ixor(l: String, r: String) = N_c_xor(l, r)
open protected fun N_ishl(l: String, r: String) = N_c_shl(l, r)
open protected fun N_ishr(l: String, r: String) = N_c_shr(l, r)
open protected fun N_iushr(l: String, r: String) = N_c_ushr(l, r)
open protected fun N_ishl_cst(l: String, r: Int) = N_ishl(l, "$r")
open protected fun N_ishr_cst(l: String, r: Int) = N_ishr(l, "$r")
open protected fun N_iushr_cst(l: String, r: Int) = N_iushr(l, "$r")
open protected fun N_lnew(value: Long) = N_func("lnew", "${value.high}, ${value.low}")
open protected fun N_lneg(str: String) = N_func("lneg", "$str")
open protected fun N_linv(str: String) = N_func("linv", "$str")
open protected fun N_ladd(l: String, r: String) = N_func("ladd", "$l, $r")
open protected fun N_lsub(l: String, r: String) = N_func("lsub", "$l, $r")
open protected fun N_lmul(l: String, r: String) = N_func("lmul", "$l, $r")
open protected fun N_ldiv(l: String, r: String) = N_func("ldiv", "$l, $r")
open protected fun N_lrem(l: String, r: String) = N_func("lrem", "$l, $r")
open protected fun N_leq(l: String, r: String) = N_func("leq", "$l, $r")
open protected fun N_lne(l: String, r: String) = N_func("lne", "$l, $r")
open protected fun N_lge(l: String, r: String) = N_func("lge", "$l, $r")
open protected fun N_lle(l: String, r: String) = N_func("lle", "$l, $r")
open protected fun N_llt(l: String, r: String) = N_func("llt", "$l, $r")
open protected fun N_lgt(l: String, r: String) = N_func("lgt", "$l, $r")
open protected fun N_land(l: String, r: String) = N_func("land", "$l, $r")
open protected fun N_lor(l: String, r: String) = N_func("lor", "$l, $r")
open protected fun N_lxor(l: String, r: String) = N_func("lxor", "$l, $r")
open protected fun N_lshl(l: String, r: String) = N_func("lshl", "$l, $r")
open protected fun N_lshr(l: String, r: String) = N_func("lshr", "$l, $r")
open protected fun N_lushr(l: String, r: String) = N_func("lushr", "$l, $r")
open protected fun N_lshl_cst(l: String, r: Int) = N_lshl(l, "$r")
open protected fun N_lshr_cst(l: String, r: Int) = N_lshr(l, "$r")
open protected fun N_lushr_cst(l: String, r: Int) = N_lushr(l, "$r")
open protected fun N_lcmp(l: String, r: String) = N_func("lcmp", "$l, $r")
open protected fun N_fadd(l: String, r: String) = N_fd_add(l, r)
open protected fun N_fsub(l: String, r: String) = N_fd_sub(l, r)
open protected fun N_fmul(l: String, r: String) = N_fd_mul(l, r)
open protected fun N_fdiv(l: String, r: String) = N_fd_div(l, r)
open protected fun N_frem(l: String, r: String) = N_fd_rem(l, r)
open protected fun N_feq(l: String, r: String) = N_fd_eq(l, r)
open protected fun N_fne(l: String, r: String) = N_fd_ne(l, r)
open protected fun N_fge(l: String, r: String) = N_fd_ge(l, r)
open protected fun N_fle(l: String, r: String) = N_fd_le(l, r)
open protected fun N_flt(l: String, r: String) = N_fd_lt(l, r)
open protected fun N_fgt(l: String, r: String) = N_fd_gt(l, r)
open protected fun N_dadd(l: String, r: String) = N_fd_add(l, r)
open protected fun N_dsub(l: String, r: String) = N_fd_sub(l, r)
open protected fun N_dmul(l: String, r: String) = N_fd_mul(l, r)
open protected fun N_ddiv(l: String, r: String) = N_fd_div(l, r)
open protected fun N_drem(l: String, r: String) = N_fd_rem(l, r)
open protected fun N_deq(l: String, r: String) = N_fd_eq(l, r)
open protected fun N_dne(l: String, r: String) = N_fd_ne(l, r)
open protected fun N_dge(l: String, r: String) = N_fd_ge(l, r)
open protected fun N_dle(l: String, r: String) = N_fd_le(l, r)
open protected fun N_dlt(l: String, r: String) = N_fd_lt(l, r)
open protected fun N_dgt(l: String, r: String) = N_fd_gt(l, r)
open protected fun N_fcmpl(l: String, r: String) = N_fd_cmpl(l, r)
open protected fun N_fcmpg(l: String, r: String) = N_fd_cmpg(l, r)
open protected fun N_dcmpl(l: String, r: String) = N_fd_cmpl(l, r)
open protected fun N_dcmpg(l: String, r: String) = N_fd_cmpg(l, r)
open protected fun N_fd_add(l: String, r: String) = N_c_add(l, r)
open protected fun N_fd_sub(l: String, r: String) = N_c_sub(l, r)
open protected fun N_fd_mul(l: String, r: String) = N_c_mul(l, r)
open protected fun N_fd_div(l: String, r: String) = N_c_div(l, r)
open protected fun N_fd_rem(l: String, r: String) = N_c_rem(l, r)
open protected fun N_fd_eq(l: String, r: String) = N_c_eq(l, r)
open protected fun N_fd_ne(l: String, r: String) = N_c_ne(l, r)
open protected fun N_fd_ge(l: String, r: String) = N_c_ge(l, r)
open protected fun N_fd_le(l: String, r: String) = N_c_le(l, r)
open protected fun N_fd_lt(l: String, r: String) = N_c_lt(l, r)
open protected fun N_fd_gt(l: String, r: String) = N_c_gt(l, r)
open protected fun N_fd_cmpl(l: String, r: String) = N_func("cmpl", "$l, $r")
open protected fun N_fd_cmpg(l: String, r: String) = N_func("cmpg", "$l, $r")
open protected fun N_obj_eq(l: String, r: String) = N_c_eq(l, r)
open protected fun N_obj_ne(l: String, r: String) = N_c_ne(l, r)
class References {
var _usedDependencies = hashSetOf()
fun clear() {
_usedDependencies.clear()
}
fun add(type: AstType?) {
when (type) {
null -> Unit
is AstType.METHOD -> {
for (arg in type.argTypes) add(arg)
add(type.ret)
}
is AstType.REF -> _usedDependencies.add(type)
is AstType.ARRAY -> add(type.elementType)
else -> Unit
}
}
}
val AstMethod.nativeBodies: Map get() = getNativeBodies(target = [email protected])
fun AstMethod.getNativeBodies(target: String): Map {
val bodies = this.annotationsList.getBodiesForTarget(TargetName(target))
return bodies.associate { body ->
body.cond to Indenter {
for (line in body.lines) line(line.template("nativeBody"))
}
}
}
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
@Suppress("ConvertLambdaToReference")
val params by lazy {
val result = (
mapOf(
"CLASS" to "",
"outputFolder" to outputFile2.parent,
"outputFile" to outputFile2.absolutePath,
"outputFileBase" to outputFile2.name,
"release" to settings.release,
"debug" to !settings.release,
"releasetype" to if (settings.release) "release" else "debug",
"settings" to settings,
"title" to settings.title,
"name" to settings.name,
"package" to settings.package_,
"version" to settings.version,
"company" to settings.company,
"initialWidth" to settings.initialWidth,
"initialHeight" to settings.initialHeight,
"fullscreen" to settings.fullscreen,
"resizable" to settings.resizable,
"borderless" to settings.borderless,
"vsync" to settings.vsync,
"orientation" to settings.orientation.lowName,
"assetFiles" to MergeVfs(settings.assets.map { LocalVfs(it) }).listdirRecursive().filter { it.isFile }.map { it.file },
"embedResources" to settings.embedResources,
"assets" to settings.assets,
"hasIcon" to !settings.icon.isNullOrEmpty(),
"icon" to settings.icon,
"libraries" to settings.libraries,
"extra" to settings.extra,
"folders" to folders,
"TARGET_IMPORTS" to targetImports,
"TARGET_INCLUDES" to targetIncludes,
"TARGET_LIBRARIES" to targetLibraries,
"TARGET_DEFINES" to targetDefines,
"JTRANSC_VERSION" to JTranscVersion.getVersion(),
"JTRANSC_OS" to JTranscSystem.getOS()
) + extraVars
)
.toHashMap()
log.info("TEMPLATE VARS:")
log.info(Json.encode(result))
log.info("extraVars:")
log.info(Json.encode(extraVars))
result
}
open fun setTemplateParamsAfterBuildingSource() {
params["entryPointFile"] = entryPointFilePath
params["entryPointClass"] = entryPointClass.targetName
}
fun setExtraData(map: Map) {
for ((key, value) in map) this.params[key] = value
}
private fun getOrReplaceVar(name: String): String = if (name.startsWith("#")) params[name.substring(1)].toString() else name
private fun evalReference(type: String, desc: String): String {
val ref = CommonTagHandler.getRef(program, type, desc, params)
return when (ref) {
is CommonTagHandler.SINIT -> buildStaticInit(ref.method.containingClass.name) ?: ""
is CommonTagHandler.CONSTRUCTOR -> buildConstructor(ref.method)
is CommonTagHandler.METHOD -> buildMethod(ref.method, static = ref.isStatic, includeDot = ref.includeDot)
is CommonTagHandler.FIELD -> ref.field.buildField(static = ref.isStatic, includeDot = ref.includeDot)
is CommonTagHandler.CLASS -> buildTemplateClass(ref.clazz)
else -> invalidOp("Unsupported result")
}
}
class ProgramRefNode(val ts: CommonGenerator, val type: String, val desc: String) : Minitemplate.BlockNode {
override fun eval(context: Minitemplate.Context) {
context.write(ts.evalReference(type, desc))
}
}
val miniConfig = Minitemplate.Config(
extraTags = listOf(
Minitemplate.Tag(
":programref:", setOf(), null,
aliases = listOf("SINIT", "CONSTRUCTOR", "SMETHOD", "METHOD", "IMETHOD", "SFIELD", "FIELD", "IFIELD", "CLASS")
) { ProgramRefNode(this, it.first().token.name, it.first().token.content) }
),
extraFilters = listOf(
)
)
override fun gen(template: String): String = gen(template, extra = hashMapOf())
fun gen(template: String, extra: HashMap = hashMapOf()): String = Minitemplate(template, miniConfig).invoke(HashMap(params + extra))
fun gen(template: String, process: Boolean): String = if (process) Minitemplate(template, miniConfig).invoke(params) else template
@Suppress("UNUSED_PARAMETER")
fun gen(template: String, context: AstGenContext, type: String): String = context.rethrowWithContext { Minitemplate(template, miniConfig).invoke(params) }
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
private var minClassLastId: Int = 0
private var minMemberLastId: Int = 0
fun allocClassName(): String = keywords.runUntilNotInSet { MinimizedNames.getTypeNameById(minClassLastId++) }
fun allocMemberName(): String = keywords.runUntilNotInSet { MinimizedNames.getIdNameById(minMemberLastId++) }
private fun Set.runUntilNotInSet(callback: () -> T): T {
while (true) {
val result = callback()
if (result !in this) return result
}
}
lateinit var currentClass: FqName
lateinit var currentMethod: AstMethodRef
val perClassNameAllocator = hashMapOf()
private val stringPoolGlobal = StringPool()
private val stringPoolPerClass = hashMapOf()
data class StringInPool(val id: Int, val str: String) {
val name = "STRINGLIT_$id"
}
fun getClassNameAllocator(clazz: FqName) = perClassNameAllocator.getOrPut(clazz) { PerClassNameAllocator() }
private fun getPerClassStrings(clazz: FqName) = stringPoolPerClass.getOrPut(clazz) { StringPool() }
fun getGlobalStrings(): List = when (stringPoolType) {
StringPool.Type.GLOBAL -> stringPoolGlobal.getAllSorted()
else -> invalidOp("This target doesn't support global string pool")
}
fun getClassStrings(clazz: FqName): List = when (stringPoolType) {
StringPool.Type.PER_CLASS -> getPerClassStrings(clazz).getAllSorted()
else -> invalidOp("This target doesn't support per class string pool")
}
fun allocString(clazz: FqName, str: String): Int = when (stringPoolType) {
StringPool.Type.GLOBAL -> stringPoolGlobal.alloc(str)
StringPool.Type.PER_CLASS -> getPerClassStrings(clazz).alloc(str)
}
open fun buildTemplateClass(clazz: FqName): String = clazz.targetName
fun buildTemplateClass(clazz: AstClass): String = buildTemplateClass(clazz.name)
//open fun getClassStaticInit(classRef: AstType.REF, reason: String): String = buildStaticInit(classRef.name)
val normalizeNameCache = hashMapOf()
protected open var baseElementPrefix = ""
enum class NameKind { PARAM, LOCAL, METHOD, FIELD }
open fun normalizeName(name: String, kind: NameKind): String {
if (name.isNullOrEmpty()) return ""
val rname = when (kind) {
NameKind.METHOD, NameKind.FIELD -> "$baseElementPrefix$name"
NameKind.PARAM, NameKind.LOCAL -> name
}
if (rname !in normalizeNameCache) {
if (rname in keywords) return normalizeName("_$rname", kind)
val chars = rname.toCharArray()
for (i in chars.indices) {
var c = chars[i]
if (!c.isLetterDigitOrUnderscore() || c == '$') c = '_'
chars[i] = c
}
if (chars[0].isDigit()) chars[0] = '_'
normalizeNameCache[rname] = String(chars)
}
return normalizeNameCache[rname]!!
}
//////////////////////////////////////////////////
// Primitive types
//////////////////////////////////////////////////
open val NullType by lazy { AstType.OBJECT.targetName }
open val VoidType = "void"
open val BoolType = "boolean"
open val IntType = "int"
open val ShortType: String get() = IntType
open val CharType: String get() = IntType
open val ByteType: String get() = IntType
open val FloatType = "float"
open val DoubleType = "double"
open val LongType = "long"
open val BaseArrayType = "JA_0"
open val BoolArrayType = "JA_Z"
open val ByteArrayType = "JA_B"
open val CharArrayType = "JA_C"
open val ShortArrayType = "JA_S"
open val IntArrayType = "JA_I"
open val LongArrayType = "JA_J"
open val FloatArrayType = "JA_F"
open val DoubleArrayType = "JA_D"
open val ObjectArrayType = "JA_L"
open val BaseArrayTypeRef get() = BaseArrayType
open val BoolArrayTypeRef get() = BoolArrayType
open val ByteArrayTypeRef get() = ByteArrayType
open val CharArrayTypeRef get() = CharArrayType
open val ShortArrayTypeRef get() = ShortArrayType
open val IntArrayTypeRef get() = IntArrayType
open val LongArrayTypeRef get() = LongArrayType
open val FloatArrayTypeRef get() = FloatArrayType
open val DoubleArrayTypeRef get() = DoubleArrayType
open val ObjectArrayTypeRef get() = ObjectArrayType
open val DoubleNegativeInfinityString = "-Infinity"
open val DoublePositiveInfinityString = "Infinity"
open val DoubleNanString = "NaN"
open val FloatNegativeInfinityString get() = DoubleNegativeInfinityString
open val FloatPositiveInfinityString get() = DoublePositiveInfinityString
open val FloatNanString get() = DoubleNanString
//////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////
open val AstType.nativeDefault: Any? get() = this.getNull()
open val AstType.nativeDefaultString: String get() {
return this.nativeDefault.escapedConstant
}
fun Any?.escapedConstantOfType(type: AstType): String {
if (type == AstType.BOOL) {
return when ("$this") {
"", "0", "false" -> "false"
else -> "true"
}
} else {
return this.escapedConstant
}
}
enum class ConstantPlace { ANY, LOCAL, PARAM, FIELD }
val Any?.escapedConstant: String get() = escapedConstant(this, ConstantPlace.ANY)
val Any?.escapedConstantLocal: String get() = escapedConstant(this, ConstantPlace.LOCAL)
val Any?.escapedConstantParam: String get() = escapedConstant(this, ConstantPlace.PARAM)
val Any?.escapedConstantField: String get() = escapedConstant(this, ConstantPlace.FIELD)
open fun escapedConstant(v: Any?, place: ConstantPlace): String = when (v) {
null -> "null"
is Boolean -> if (v) "true" else "false"
is String -> v.escapeString
is Long -> N_lnew(v)
is Float -> if (v.isInfinite()) if (v < 0) FloatNegativeInfinityString else FloatPositiveInfinityString else if (v.isNaN()) FloatNanString else if (floatHasFSuffix) "${v}f" else "$v"
is Double -> {
val out = if (v.isInfinite()) if (v < 0) DoubleNegativeInfinityString else DoublePositiveInfinityString else if (v.isNaN()) if (v < 0) "-$DoubleNanString" else DoubleNanString else "$v"
if (optionalDoubleDummyDecimals && out.endsWith(".0")) {
out.substr(0, -2)
} else {
out
}
}
is Int -> when (v) {
Int.MIN_VALUE -> "N${staticAccessOperator}MIN_INT32"
else -> N_inew(v.toInt())
}
is Number -> N_inew(v.toInt())
is Char -> N_inew(v.toInt())
is AstType -> {
for (fqName in v.getRefClasses()) mutableBody.initClassRef(fqName, "class literal")
v.escapeType
}
else -> throw NotImplementedError("Literal of type $v")
}
open protected val String.escapeString: String get() = N_func("strLitEscape", quoteString(this))
open protected val AstType.escapeType: String get() = N_func("resolveClass", quoteString(this.mangle()))
//////////////////////////////////////////////////
// Type names
//////////////////////////////////////////////////
enum class TypeType {
NORMAL, REF, STATIC_CALL
}
val AstType.targetName: String get() = getTypeTargetName(this, ref = false)
open val AstType.targetNameRef: String get() = getTypeTargetName(this, ref = true)
//open val AstType.targetNameRefBounds: String get() = getTypeTargetName(this, ref = true)
fun getTypeTargetName(type: AstType, ref: Boolean): String {
val type = type.resolve()
return when (type) {
is AstType.NULL -> NullType
is AstType.UNKNOWN -> {
println("Referenced UNKNOWN: $type")
NullType
}
is AstType.VOID -> VoidType
is AstType.BOOL -> BoolType
is AstType.GENERIC -> type.type.targetName
is AstType.INT -> IntType
is AstType.SHORT -> ShortType
is AstType.CHAR -> CharType
is AstType.BYTE -> ByteType
is AstType.FLOAT -> FloatType
is AstType.DOUBLE -> DoubleType
is AstType.LONG -> LongType
is AstType.REF -> {
val clazz = program[type.name]
val nativeName = clazz.nativeName
if (nativeName != null) imports += FqName(clazz.nativeName!!)
nativeName ?: if (ref) type.name.targetNameRef else type.name.targetName
}
is AstType.ARRAY -> when (type.element) {
is AstType.BOOL -> if (ref) BoolArrayTypeRef else BoolArrayType
is AstType.BYTE -> if (ref) ByteArrayTypeRef else ByteArrayType
is AstType.CHAR -> if (ref) CharArrayTypeRef else CharArrayType
is AstType.SHORT -> if (ref) ShortArrayTypeRef else ShortArrayType
is AstType.INT -> if (ref) IntArrayTypeRef else IntArrayType
is AstType.LONG -> if (ref) LongArrayTypeRef else LongArrayType
is AstType.FLOAT -> if (ref) FloatArrayTypeRef else FloatArrayType
is AstType.DOUBLE -> if (ref) DoubleArrayTypeRef else DoubleArrayType
else -> if (ref) ObjectArrayTypeRef else ObjectArrayType
}
else -> throw RuntimeException("Not supported native type $this")
}
}
//////////////////////////////////////////////////
// Local names
//////////////////////////////////////////////////
open val LocalParamRef.targetName: String get() = normalizeName(this.name, NameKind.LOCAL)
//////////////////////////////////////////////////
// Class names
//////////////////////////////////////////////////
inline fun nativeName(): String = T::class.java.name.fqname.targetName
val imports = hashSetOf()
open val FqName.targetNameRef: String get() = this.targetName
open val FqName.targetName: String get() = this.fqname.replace('.', '_').replace('$', '_')
open val FqName.targetClassFqName: String get() = this.targetName
open val FqName.targetSimpleName: String get() = this.simpleName
open val FqName.targetNameForStatic: String get() {
val clazz = program[this]
return when {
(clazz.nativeName != null) -> {
imports += FqName(clazz.nativeName!!)
clazz.nativeName!!
}
else -> this.targetNameForStaticNonNative
}
}
open val FqName.targetNameForStaticNonNative: String get() {
val clazz = program[this]
return when {
(!clazz.isInterface || interfacesSupportStaticMembers) -> this.targetName
else -> this.targetName + "_IFields"
}
}
open val FqName.targetFilePath: String get() = this.simpleName
open val FqName.targetGeneratedFqName: FqName get() = this
open val FqName.targetGeneratedFqPackage: String get() = this.packagePath
//////////////////////////////////////////////////
// Method names
//////////////////////////////////////////////////
//open val MethodRef.targetName: String get() = normalizeName(this.ref.name)
open val AstMethodRef.objectToCache: Any get() = if (this.isClassOrInstanceInit) this else this.withoutClass
open val MethodRef.targetName: String get() {
val method = this.ref
val realmethod = program[method] ?: invalidOp("Can't find method $method")
val realclass = realmethod.containingClass
return if (realclass.isNative) {
// No cache
realmethod.nativeNameForTarget([email protected]) ?: method.name
} else {
methodNames.getOrPut2(method.objectToCache) {
if (minimize && !realmethod.keepName) {
allocMemberName()
} else {
if (realmethod.nativeMethod != null) {
realmethod.nativeMethod!!
} else {
val name2 = method.targetNameBase
val name = when (method.name) {
"", "" -> "${method.containingClass}$name2"
else -> name2
}
cleanMethodName(name)
}
}
}
}
}
open val MethodRef.targetNameBase: String get() = "${this.ref.name}${this.ref.desc}"
//open val MethodRef.targetNameBase: String get() = "${this.ref.name}"
open protected fun cleanMethodName(name: String): String {
if (name in keywords) return cleanMethodName("_$name")
val out = CharArray(name.length)
for (n in 0 until name.length) out[n] = if (name[n].isLetterOrDigit()) name[n] else '_'
return String(out)
}
open protected fun cleanFieldName(name: String): String {
if (name in keywords) return cleanFieldName("_$name")
val out = CharArray(name.length)
for (n in 0 until name.length) out[n] = if (name[n].isLetterOrDigit()) name[n] else '_'
return String(out)
}
//override val MethodRef.targetName: String get() {
// val methodRef: AstMethodRef = this.ref
// val keyToUse: Any = if (methodRef.isInstanceInit) methodRef else methodRef.withoutClass
// return methodNames.getOrPut2(keyToUse) {
// if (minimize) {
// allocMemberName()
// } else {
// if (program is AstProgram) {
// val method = methodRef.resolve(program)
// if (method.nativeName != null) {
// return method.nativeName!!
// }
// }
// return if (methodRef.isInstanceInit) {
// "${methodRef.classRef.fqname}${methodRef.name}${methodRef.desc}"
// } else {
// "${methodRef.name}${methodRef.desc}"
// }
// }
// }
//}
fun getTargetMethodAccess(refMethod: AstMethod, static: Boolean): String = access(refMethod.targetName, static, field = false)
fun buildMethod(method: AstMethod, static: Boolean, includeDot: Boolean = false): String {
val clazzFqname = method.containingClass.name
//val nonNativeCall = method.annotationsList.nonNativeCall
val nonNativeCall = false
val clazz = when {
static && nonNativeCall -> clazzFqname.targetNameForStaticNonNative
static -> clazzFqname.targetNameForStatic
else -> clazzFqname.targetName
}
val name = method.targetName
return if (static) (clazz + access(name, static = true, field = false)) else if (includeDot) instanceAccess(name, field = false) else name
}
fun buildConstructor(method: AstMethod): String {
val clazz = method.containingClass.name.targetName
val methodName = method.targetName
return "(new $clazz())" + access(methodName, static = false, field = false)
}
//////////////////////////////////////////////////
// Field names
//////////////////////////////////////////////////
protected val fieldNames = hashMapOf()
protected val methodNames = hashMapOf()
protected val classNames = hashMapOf()
protected val cachedFieldNames = hashMapOf()
open val FieldRef.targetName: String get() {
val fieldRef = this
val field = fieldRef.ref
val realfield = program[field]
val realclass = program[field.containingClass]
val keyToUse = field
val normalizedFieldName = cleanFieldName(field.name)
//if (normalizedFieldName == "_parameters" || normalizedFieldName == "__parameters") {
// println("_parameters")
//}
return if (realclass.isNative) {
realfield.nativeNameForTarget([email protected]) ?: normalizedFieldName
} else {
fieldNames.getOrPut2(keyToUse) {
if (minimize && !realfield.keepName) {
allocMemberName()
} else {
val rnormalizedFieldName = normalizeName(cleanFieldName(field.name), NameKind.FIELD)
// @TODO: Move to CommonNames
if (field !in cachedFieldNames) {
//val fieldName = normalizedFieldName
val fieldName = rnormalizedFieldName
//var name = if (fieldName in keywords) "${fieldName}_" else fieldName
val clazz = program[field].containingClass
var name = "_$fieldName"
//var name = "_${fieldName}_${clazz.name.fqname}_${fieldRef.ref.type.mangle()}"
val clazzAncestors = clazz.ancestors.reversed()
val names = clazzAncestors.flatMap { it.fields }
.filter { normalizeName(it.name, NameKind.FIELD) == rnormalizedFieldName }
//.filter { it.name == field.name }
.map { it.targetName }.toHashSet()
val fieldsColliding = clazz.fields.filter {
(it.ref == field) || (normalizeName(it.name, NameKind.FIELD) == rnormalizedFieldName)
}.map { it.ref }
// JTranscBugInnerMethodsWithSameName.kt
for (f2 in fieldsColliding) {
while (name in names) name += "_"
cachedFieldNames[f2] = name
names += name
}
}
cachedFieldNames[field] ?: unexpected("Unexpected. Not cached: $field")
}
}
}
}
//override val FieldRef.targetName: String get() {
// val fieldRef = this
// //"_" + field.uniqueName
// val keyToUse = fieldRef.ref
//
// return fieldNames.getOrPut2(keyToUse) {
// val field = program[fieldRef.ref]
// if (minimize) {
// allocMemberName()
// } else {
// if (fieldRef !in cachedFieldNames) {
// val fieldName = field.name.replace('$', '_')
// //var name = if (fieldName in JsKeywordsWithToStringAndHashCode) "${fieldName}_" else fieldName
// var name = "_$fieldName"
//
// val clazz = program[fieldRef.ref].containingClass
// val clazzAncestors = clazz.ancestors.reversed()
// val names = clazzAncestors.flatMap { it.fields }.filter { it.name == field.name }.map { it.targetName }.toHashSet()
// val fieldsColliding = clazz.fields.filter { it.name == field.name }.map { it.ref }
//
// // JTranscBugInnerMethodsWithSameName.kt
// for (f2 in fieldsColliding) {
// while (name in names) name += "_"
// cachedFieldNames[f2] = name
// names += name
// }
// cachedFieldNames[field.ref] ?: unexpected("Unexpected. Not cached: $field")
// }
// cachedFieldNames[field.ref] ?: unexpected("Unexpected. Not cached: $field")
// }
// }
//}
val AstField.constantValueOrNativeDefault: Any? get() = if (this.hasConstantValue) this.constantValue else this.type.nativeDefault
val AstField.escapedConstantValue: String get() = this.constantValueOrNativeDefault.escapedConstant
val AstField.escapedConstantValueField: String get() = this.constantValueOrNativeDefault.escapedConstantField
val AstField.escapedConstantValueLocal: String get() = this.constantValueOrNativeDefault.escapedConstantLocal
val FieldRef.nativeStaticText: String get() = this.ref.containingTypeRef.name.targetNameForStatic + buildAccessName(program[this.ref], static = true)
inline fun KMutableProperty1.getTargetName(): String = this.locate(program).targetName
fun FieldRef.buildField(static: Boolean, includeDot: Boolean = false): String = if (static) this.nativeStaticText else if (includeDot) instanceAccess(this.targetName, field = true) else this.targetName
fun buildInstanceField(expr: String, field: FieldRef): String = expr + buildAccessName(program[field], static = false)
fun buildAccessName(field: AstField, static: Boolean): String = access(field.targetName, static, field = true)
//////////////////////////////////////////////////
// Access to members
//////////////////////////////////////////////////
open fun access(name: String, static: Boolean, field: Boolean): String = if (static) staticAccess(name, field) else instanceAccess(name, field)
open fun staticAccess(name: String, field: Boolean): String = "$staticAccessOperator$name"
open fun instanceAccess(name: String, field: Boolean): String = "$instanceAccessOperator$name"
open fun String.instanceAccessField(name: String): String = this + instanceAccess(name, field = true)
open fun String.instanceAccessMethod(name: String): String = this + instanceAccess(name, field = false)
/////////////////
// STATIC INIT //
/////////////////
// @TODO: This should simplify StaticInit
open fun genBodyStaticInitPrefix(clazzRef: AstType.REF, reasons: ArrayList) = indent {
line(buildStaticInit(clazzRef.name))
}
open fun buildStaticInit(clazzName: FqName): String? = clazzName.targetName + access("SI", static = true, field = false) + "();"
val AstClass?.isNative get() = this.isNativeForTarget(targetName)
//val AstClass.mustGenerate get() = !this.isNative || this.annotationsList.nonNativeCall
val AstClass.mustGenerate get() = !this.isNative
open fun getClassesForStaticConstruction(): List = program.staticInitsSorted.map { program[it]!! }.filter {
!it.isNative
//it.mustGenerate && !it.isNative
}
open fun genStaticConstructorsSortedLines(): List {
return getClassesForStaticConstruction().map { "${it.name.targetNameForStatic}" + access("SI", static = true, field = false) + "();" }
}
open fun genStaticConstructorsSorted() = indent {
for (line in genStaticConstructorsSortedLines()) line(line)
}
open val FqName.actualFqName: FqName get() = this
val AstClass.actualFqName: FqName get() = this.name.actualFqName
val prepareThrow by lazy {
program["java.lang.Throwable".fqname].getMethods("prepareThrow").first()
}
//open fun getActualFqName(name: FqName): FqName {
// /*
// val realclass = if (name in program) program[name] else null
// return FqName(classNames.getOrPut2(name) {
// if (realclass?.nativeName != null) {
// realclass!!.nativeName!!
// } else {
// FqName(name.packageParts.map { if (it in keywords) "${it}_" else it }.map(String::decapitalize), "${name.simpleName.replace('$', '_')}_".capitalize()).fqname
// }
// })
// */
//}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy