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

com.jtransc.gen.haxe.haxe_gen.kt Maven / Gradle / Ivy

Go to download

JVM AOT compiler currently generating JavaScript, Haxe, with initial focus on Kotlin and games.

There is a newer version: 0.6.8
Show newest version
package com.jtransc.gen.haxe

import com.jtransc.JTranscFunction
import com.jtransc.annotation.JTranscInvisible
import com.jtransc.annotation.JTranscInvisibleExternal
import com.jtransc.annotation.JTranscKeep
import com.jtransc.annotation.haxe.*
import com.jtransc.ast.*
import com.jtransc.ast.feature.GotosFeature
import com.jtransc.ds.concatNotNull
import com.jtransc.error.InvalidOperationException
import com.jtransc.error.invalidOp
import com.jtransc.error.noImplWarn
import com.jtransc.ffi.StdCall
import com.jtransc.gen.GenTargetInfo
import com.jtransc.internal.JTranscAnnotationBase
import com.jtransc.lang.nullMap
import com.jtransc.lang.toBetterString
import com.jtransc.log.log
import com.jtransc.sourcemaps.Sourcemaps
import com.jtransc.template.Minitemplate
import com.jtransc.text.Indenter
import com.jtransc.text.quote
import com.jtransc.util.sortDependenciesSimple
import com.jtransc.vfs.SyncVfsFile
import java.util.*

//const val ENABLE_HXCPP_GOTO_HACK = true
const val ENABLE_HXCPP_GOTO_HACK = false

class GenHaxeGen(
	val program: AstProgram,
	val features: AstFeatures,
	val srcFolder: SyncVfsFile,
	val featureSet: Set,
	val settings: AstBuildSettings,
	val tinfo: GenTargetInfo,
	val names: HaxeNames,
	val haxeTemplateString: HaxeTemplateString
) {
	val refs = References()
	val context = AstGenContext()
	lateinit var mutableBody: MutableBody
	lateinit var stm: AstStm

	fun AstStm.genStm(): Indenter = genStm2(this)
	fun AstStm.Box.genStm(): Indenter = genStm2(this.value)
	fun AstExpr.genExpr(): String = genExpr2(this)
	fun AstExpr.Box.genExpr(): String = genExpr2(this.value)
	fun AstExpr.Box.genNotNull(): String = this.value.genNotNull()

	val JAVA_LANG_OBJECT = names.haxeName()
	val JAVA_LANG_CLASS = names.haxeName>()
	val JAVA_LANG_CLASS_name = names.getHaxeFieldName(java.lang.Class::class.java, "name")
	val JAVA_LANG_STRING = names.haxeName()
	val invocationHandlerHaxeName = names.haxeName()
	val methodHaxeName = names.haxeName()
	val invokeHaxeName = AstMethodRef(java.lang.reflect.InvocationHandler::class.java.name.fqname, "invoke", AstType.build { METHOD(OBJECT, OBJECT, METHOD, ARRAY(OBJECT)) }).haxeName
	val toStringHaxeName = AstMethodRef(java.lang.Object::class.java.name.fqname, "toString", AstType.build { METHOD(STRING) }).haxeName
	val hashCodeHaxeName = AstMethodRef(java.lang.Object::class.java.name.fqname, "hashCode", AstType.build { METHOD(INT) }).haxeName
	val getClassHaxeName = AstMethodRef(java.lang.Object::class.java.name.fqname, "getClass", AstType.build { METHOD(CLASS) }).haxeName

	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.annotationsList.contains()) return false
		return true
	}

	fun AstField.isVisible(): Boolean = !this.annotationsList.contains()
	fun AstMethod.isVisible(): Boolean = !this.annotationsList.contains()

	fun AstExpr.genNotNull(): String {
		return if (this is AstExpr.THIS) {
			genExpr2(this)
		} else {
			genExpr2(this)
			//"HaxeNatives.checkNotNull(${gen2(this)})"
		}
	}

	fun AstBody.genBody(): Indenter = genBody2(this)
	fun AstBody.genBodyWithFeatures(): Indenter {
		return if (ENABLE_HXCPP_GOTO_HACK && (tinfo.subtarget in setOf("cpp", "windows", "linux", "mac", "android"))) {
			features.apply(this, (featureSet + setOf(GotosFeature)), settings).genBody()
		} else {
			features.apply(this, featureSet, settings).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 {
		return program[field].ref
	}

	fun fixMethod(method: AstMethodRef): AstMethodRef {
		return program[method]?.ref ?: invalidOp("Can't find method $method while generating $context")
	}

	val allAnnotationTypes = program.allAnnotations.flatMap {
		it.getAllDescendantAnnotations()
	}.map { it.type }.distinct().map { program[it.name] }.toSet()

	internal fun _write(): GenHaxe.ProgramInfo {
		val vfs = srcFolder
		for (clazz in program.classes.filter { !it.isNative }) {
			if (clazz.implCode != null) {
				vfs[clazz.name.haxeFilePath] = clazz.implCode!!
			} else {
				writeClass(clazz, vfs)
			}
		}

		val copyFilesRaw = program.classes.flatMap { it.annotationsList.getTyped()?.value?.toList() ?: listOf() }
		val copyFilesTemplate = program.classes.flatMap { it.annotationsList.getTyped()?.value?.toList() ?: listOf() }

		for (file in copyFilesRaw) vfs[file] = program.resourcesVfs[file]
		for (file in copyFilesTemplate) vfs[file] = haxeTemplateString.gen(program.resourcesVfs[file].readString())

		val mainClassFq = program.entrypoint
		val mainClass = mainClassFq.haxeClassFqName
		val mainMethod = program[mainClassFq].getMethod("main", AstType.build { METHOD(VOID, ARRAY(STRING)) }.desc)!!.haxeName
		val entryPointClass = FqName(mainClassFq.fqname + "_EntryPoint")
		val entryPointFilePath = entryPointClass.haxeFilePath
		val entryPointFqName = entryPointClass.haxeGeneratedFqName
		val entryPointSimpleName = entryPointClass.haxeGeneratedSimpleClassName
		val entryPointPackage = entryPointFqName.packagePath

		fun calcClasses(program: AstProgram, mainClass: AstClass): List {
			return sortDependenciesSimple(mainClass) {
				it.classDependencies.map { program.get3(it) }
			}
		}

		fun inits() = Indenter.gen {
			line("haxe.CallStack.callStack();")
			line(names.getHaxeClassStaticInit(program[mainClassFq].ref, "program main"))
		}

		val customMain = program.allAnnotationsList.getTyped()?.value

		val plainMain = Indenter.genString {
			line("package {{ entryPointPackage }};")
			line("class {{ entryPointSimpleName }}") {
				line("static public function main()") {
					line("{{ inits }}")
					line("{{ mainClass }}.{{ mainMethod }}(HaxeNatives.strArray(HaxeNatives.args()));")
				}
			}
		}

		log("Using ... " + if (customMain != null) "customMain" else "plainMain")

		haxeTemplateString.setExtraData(mapOf(
			"entryPointPackage" to entryPointPackage,
			"entryPointSimpleName" to entryPointSimpleName,
			"mainClass" to mainClass,
			"mainClass2" to mainClassFq.fqname,
			"mainMethod" to mainMethod,
			"inits" to inits().toString()
		))
		vfs[entryPointFilePath] = haxeTemplateString.gen(customMain ?: plainMain)

		vfs["HaxeReflectionInfo.hx"] = Indenter.genString {
			line("class HaxeReflectionInfo") {
				line("static public function __registerClasses()") {
					for (clazz in program.classes) {
						if (clazz.nativeName == null) {
							line("R.register(${clazz.ref.fqname.quote()}, ${clazz.ref.name.haxeClassFqName.quote()}, ${names.getHaxeClassStaticClassInit(clazz.ref)});")
						}
					}
				}
			}
			//line(annotationProxyTypes)
		}

		return GenHaxe.ProgramInfo(entryPointClass, entryPointFilePath, vfs)
	}


	fun annotation(a: AstAnnotation): String {
		fun escapeValue(it: Any?): String {
			return when (it) {
				null -> "null"
				is AstAnnotation -> annotation(it)
				is Pair<*, *> -> escapeValue(it.second)
				is AstFieldRef -> it.containingTypeRef.name.haxeClassFqName + "." + it.haxeName
				is AstFieldWithoutTypeRef -> program[it.containingClass].ref.name.haxeClassFqName + "." + program.get(it).haxeName
				is String -> "HaxeNatives.boxString(${it.quote()})"
				is Int -> "HaxeNatives.boxInt($it)"
				is Long -> "HaxeNatives.boxLong($it)"
				is Float -> "HaxeNatives.boxFloat($it)"
				is Double -> "HaxeNatives.boxDouble($it)"
				is List<*> -> "[" + it.map { escapeValue(it) }.joinToString(", ") + "]"
				is com.jtransc.org.objectweb.asm.Type -> "HaxeNatives.resolveClass(" + it.descriptor.quote() + ")"
				else -> invalidOp("Can't handle value ${it.javaClass.name} : ${it.toBetterString()} while generating $context")
			}
		}
		//val itStr = a.elements.map { it.key.quote() + ": " + escapeValue(it.value) }.joinToString(", ")
		val annotation = program.get3(a.type)
		val itStr = annotation.methods.map { escapeValue(if (it.name in a.elements) a.elements[it.name]!! else it.defaultTag) }.joinToString(", ")
		return "new ${names.getFullAnnotationProxyName(a.type)}([$itStr])"
	}

	fun annotationInit(a: AstAnnotation): List {
		fun escapeValue(it: Any?): List {
			return when (it) {
				null -> listOf()
				is AstAnnotation -> annotationInit(it)
				is Pair<*, *> -> escapeValue(it.second)
				is AstFieldRef -> listOf(it.containingTypeRef)
				is AstFieldWithoutTypeRef -> listOf(it.containingClass.ref())
				is List<*> -> it.flatMap { escapeValue(it) }
				else -> listOf()
			}
		}
		//val itStr = a.elements.map { it.key.quote() + ": " + escapeValue(it.value) }.joinToString(", ")
		val annotation = program.get3(a.type)
		return annotation.methods.flatMap {
			escapeValue(if (it.name in a.elements) a.elements[it.name]!! else it.defaultTag)
		}
	}

	fun visibleAnnotations(annotations: List): String = "[" + annotations.filter { it.runtimeVisible }.map { annotation(it) }.joinToString(", ") + "]"
	fun visibleAnnotationsList(annotations: List>): String = "[" + annotations.map { visibleAnnotations(it) }.joinToString(", ") + "]"

	fun annotationsInit(annotations: List): Indenter {
		return Indenter.gen {
			for (i in annotations.filter { it.runtimeVisible }.flatMap { annotationInit(it) }.toHashSet()) {
				line(names.getHaxeClassStaticInit(i, "annotationsInit"))
			}
		}
	}

	fun dumpClassInfo(clazz: AstClass) = Indenter.genString {
		line("static public var HAXE_CLASS_NAME = ${clazz.name.fqname.quote()};")
		line("static public function HAXE_CLASS_INIT(c:$JAVA_LANG_CLASS = null):$JAVA_LANG_CLASS") {
			line("if (c == null) c = new $JAVA_LANG_CLASS();")
			line("c.$JAVA_LANG_CLASS_name = N.strLit(HAXE_CLASS_NAME);")
			//line("info(c, \"${clazz.name.haxeGeneratedFqName}\", " + (clazz.extending?.fqname?.quote() ?: "null") + ", [" + clazz.implementing.map { "\"${it.fqname}\"" }.joinToString(", ") + "], ${clazz.modifiers}, " + annotations(clazz.runtimeAnnotations) + ");")
			line(annotationsInit(clazz.runtimeAnnotations))
			val proxyClassName = if (clazz.isInterface) clazz.name.haxeGeneratedFqName.fqname + "." + clazz.name.haxeGeneratedSimpleClassName + "_Proxy" else "null"
			val ffiClassName = if (clazz.hasFFI) clazz.name.haxeGeneratedFqName.fqname + "." + clazz.name.haxeGeneratedSimpleClassName + "_FFI" else "null"
			line("R.i(c, ${clazz.name.haxeGeneratedFqName}, $proxyClassName, $ffiClassName, " + (clazz.extending?.fqname?.quote() ?: "null") + ", [" + clazz.implementing.map { "\"${it.fqname}\"" }.joinToString(", ") + "], ${clazz.modifiers}, " + visibleAnnotations(clazz.runtimeAnnotations) + ");")
			if (clazz.isVisible()) {
				for ((slot, field) in clazz.fields.withIndex()) {
					val internalName = field.haxeName
					if (field.isVisible()) {
						line("R.f(c, ${internalName.quote()}, $slot, \"${field.name}\", \"${field.descriptor}\", ${field.modifiers}, ${field.genericSignature.quote()}, ${visibleAnnotations(field.annotations)});");
					}
				}
				for ((slot, method) in clazz.methods.withIndex()) {
					val internalName = method.haxeName
					if (method.isVisible()) {
						if (method.name == "") {
							line("R.c(c, ${internalName.quote()}, $slot, ${method.modifiers}, ${method.signature.quote()}, ${method.genericSignature.quote()}, ${visibleAnnotations(method.annotations)}, ${visibleAnnotationsList(method.parameterAnnotations)});");
						} else if (method.name == "") {
						} else {
							line("R.m(c, ${method.id}, ${internalName.quote()}, $slot, \"${method.name}\", ${method.modifiers}, ${method.desc.quote()}, ${method.genericSignature.quote()}, ${visibleAnnotations(method.annotations)}, ${visibleAnnotationsList(method.parameterAnnotations)});");
						}
					}
				}
			}
			line("return c;")
		}
	}

	fun genStm2(stm: AstStm): Indenter {
		this.stm = stm
		val program = program
		//val clazz = context.clazz
		val mutableBody = mutableBody
		return Indenter.gen {
			when (stm) {
				// c++ goto hack
				is AstStm.STM_LABEL -> line("untyped __cpp__('${stm.label.name}:');")
				is AstStm.GOTO -> line("untyped __cpp__('goto ${stm.label.name};');")
				is AstStm.IF_GOTO -> line("if (${stm.cond.genExpr()}) { untyped __cpp__('goto ${stm.label.name};'); }")
				is AstStm.SWITCH_GOTO -> {
					line("switch (${stm.subject.genExpr()})") {
						for ((value, label) in stm.cases) {
							line("case $value: untyped __cpp__('goto ${label.name};');");
						}
						line("default: untyped __cpp__('goto ${stm.default.name};');");
					}
				}
				// plain
				is AstStm.NOP -> Unit
				is AstStm.IF -> {
					line("if (${stm.cond.genExpr()})") { line(stm.strue.genStm()) }
				}
				is AstStm.IF_ELSE -> {
					line("if (${stm.cond.genExpr()})") { line(stm.strue.genStm()) }
					line("else") { line(stm.sfalse.genStm()) }
				}
				is AstStm.RETURN_VOID -> {
					if (context.method.methodVoidReturnThis) line("return this;") else line("return;")
				}
				is AstStm.RETURN -> {
					line("return ${stm.retval.genExpr()};")
				}
				is AstStm.SET_LOCAL -> {
					val localName = stm.local.haxeName
					val expr = stm.expr.genExpr()
					if (localName != expr) {
						// Avoid: Assigning a value to itself
						line("$localName = $expr;")
					}
				}
				is AstStm.SET_NEW_WITH_CONSTRUCTOR -> {
					val newClazz = program[stm.target.name]
					//val mapping = mappings.getClassMapping(newClazz)
					refs.add(stm.target)
					val commaArgs = stm.args.map { it.genExpr() }.joinToString(", ")
					val className = stm.target.haxeTypeNew
					val localHaxeName = stm.local.haxeName

					if (newClazz.nativeName != null) {
						line("$localHaxeName = new $className($commaArgs);")
					} else {
						line("$localHaxeName = new $className();")
						line("$localHaxeName.${stm.method.haxeName}($commaArgs);")
					}
				}
				is AstStm.SET_ARRAY -> {
					val set = when (stm.array.type.elementType) {
						AstType.BOOL -> "setBool"
						else -> "set"
					}
					line("${stm.array.genNotNull()}.$set(${stm.index.genExpr()}, ${stm.expr.genExpr()});")
				}
				is AstStm.SET_FIELD_STATIC -> {
					refs.add(stm.clazz)
					mutableBody.initClassRef(fixField(stm.field).classRef, "SET_FIELD_STATIC")
					val left = fixField(stm.field).haxeStaticText
					val right = stm.expr.genExpr()
					if (left != right) {
						// Avoid: Assigning a value to itself
						line("$left /*${stm.field.name}*/ = $right;")
					}
				}
				is AstStm.SET_FIELD_INSTANCE -> {
					val left = "${stm.left.genExpr()}.${fixField(stm.field).haxeName}"
					val right = stm.expr.genExpr()
					if (left != right) {
						// Avoid: Assigning a value to itself
						line("$left = $right;")
					}
				}
				is AstStm.STM_EXPR -> line("${stm.expr.genExpr()};")
				is AstStm.STMS -> for (s in stm.stms) line(s.genStm())
				is AstStm.STM_LABEL -> line("${stm.label.name}:;")
				is AstStm.BREAK -> line("break;")
				is AstStm.BREAK -> line("break;")
				is AstStm.CONTINUE -> line("continue;")
				is AstStm.WHILE -> {
					line("while (${stm.cond.genExpr()})") {
						line(stm.iter.genStm())
					}
				}
				is AstStm.SWITCH -> {
					line("switch (${stm.subject.genExpr()})") {
						for (case in stm.cases) {
							val value = case.first
							val caseStm = case.second
							line("case $value:")
							indent {
								line(caseStm.genStm())
							}
						}
						line("default:")
						indent {
							line(stm.default.genStm())
						}
					}
				}
				is AstStm.TRY_CATCH -> {
					line("try") {
						line(stm.trystm.genStm())
					}
					line("catch (J__i__exception__: Dynamic)") {
						line("J__exception__ = J__i__exception__;")
						line(stm.catch.genStm())
					}
				}
				is AstStm.THROW -> line("throw ${stm.value.genExpr()};")
				is AstStm.RETHROW -> line("""HaxeNatives.rethrow(J__i__exception__);""")
				is AstStm.MONITOR_ENTER -> line("// MONITOR_ENTER")
				is AstStm.MONITOR_EXIT -> line("// MONITOR_EXIT")
				is AstStm.LINE -> {
					mark(stm)
					line("// ${stm.line}")
				}
				else -> throw RuntimeException("Unhandled statement $stm")
			}
		}
	}

	fun genBody2(body: AstBody): Indenter {
		val method = context.method
		this.mutableBody = MutableBody(method)

		return Indenter.gen {
			for (local in body.locals) {
				refs.add(local.type)
				line("var ${local.haxeName}: ${local.type.haxeTypeTag} = ${local.type.haxeDefaultString};")
			}
			if (body.traps.isNotEmpty()) {
				line("var J__exception__:Dynamic = null;")
			}
			for (field in method.dependencies.fields2.filter { it.isStatic }) {
				val clazz = field.containingClass
				if (clazz.isInterface) {

				} else {
				}
			}

			val bodyContent = body.stm.genStm()

			for ((clazzRef, reasons) in mutableBody.referencedClasses) {
				line(names.getHaxeClassStaticInit(clazzRef, reasons.joinToString(", ")))
			}
			line(bodyContent)
		}
	}

	class Strings {
		private var id = 0
		private val strings = hashMapOf()
		private val idsToString = hashMapOf()

		fun getIndices(): Set {
			return idsToString.keys
		}

		fun getStringWithId(id: Int): String {
			return idsToString[id]!!
		}

		fun getIndex(str: String): Int {
			if (str !in strings) {
				strings[str] = id
				idsToString[id] = str
				id++
			}
			return strings[str]!!
		}

		fun getId(str: String): String {
			return "__str" + getIndex(str)
		}
	}

	private var strings = Strings()

	fun genExpr2(e: AstExpr): String {
		return when (e) {
			is AstExpr.THIS -> "this"
			is AstExpr.LITERAL -> {
				val value = e.value
				if (value is String) {
					strings.getId(value)
				} else {
					names.escapeConstant(value)
				}
			}
			is AstExpr.TERNARY -> "((" + e.cond.genExpr() + ") ? (" + e.etrue.genExpr() + ") : (" + e.efalse.genExpr() + "))"
			is AstExpr.PARAM -> "${e.argument.name}"
			is AstExpr.LOCAL -> "${e.local.haxeName}"
			is AstExpr.UNOP -> "(${e.op.symbol}(" + e.right.genExpr() + "))"
			is AstExpr.BINOP -> {
				val resultType = e.type
				var l = e.left.genExpr()
				var r = e.right.genExpr()
				val opSymbol = e.op.symbol
				val opName = e.op.str

				val binexpr = if (resultType == AstType.LONG) {
					"N.l$opName($l, $r)"
				} else if (resultType == AstType.INT && opSymbol in setOf("/", "<<", ">>", ">>>")) {
					"N.i$opName($l, $r)"
				} else {
					when (opSymbol) {
						"lcmp", "cmp", "cmpl", "cmpg", "==", "!=" -> "N.$opName($l, $r)"
						else -> "($l $opSymbol $r)"
					}
				}
				when (resultType) {
					AstType.INT -> "N.i($binexpr)"
					AstType.CHAR -> "N.i2c($binexpr)"
					AstType.SHORT -> "N.i2s($binexpr)"
					AstType.BYTE -> "N.i2b($binexpr)"
					else -> binexpr
				}
			}
			is AstExpr.CALL_BASE -> {
				// 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

				if (e2 is AstExpr.CALL_STATIC) {
					refs.add(clazz)
					mutableBody.initClassRef(clazz, "CALL_STATIC")
				}

				val isNativeCall = refMethodClass.isNative

				val commaArgs = args.map {
					if (isNativeCall) convertToHaxe(it) else it.genExpr()
				}.joinToString(", ")

				val base = when (e2) {
					is AstExpr.CALL_STATIC -> "${clazz.haxeTypeNew}"
					is AstExpr.CALL_SUPER -> "super"
					is AstExpr.CALL_INSTANCE -> "${e2.obj.genNotNull()}"
					else -> throw InvalidOperationException("Unexpected")
				}

				val result = "$base.${refMethod.haxeName}($commaArgs)"
				if (isNativeCall) convertToJava(refMethod.methodType.ret, result) else result
			}
			is AstExpr.FIELD_INSTANCE_ACCESS -> {
				"${e.expr.genNotNull()}.${fixField(e.field).haxeName}"
			}
			is AstExpr.FIELD_STATIC_ACCESS -> {
				refs.add(e.clazzName)
				mutableBody.initClassRef(fixField(e.field).classRef, "FIELD_STATIC_ACCESS")

				"${fixField(e.field).haxeStaticText}"
			}
			is AstExpr.ARRAY_LENGTH -> {
				val type = e.array.type
				if (type is AstType.ARRAY) {
					"(${e.array.genNotNull()}).length"
				} else {
					"cast(${e.array.genNotNull()}, ${names.HaxeArrayBase}).length"
				}
			}
			is AstExpr.ARRAY_ACCESS -> {
				val get = when (e.array.type.elementType) {
					AstType.BOOL -> "getBool"
					else -> "get"
				}
				"${e.array.genNotNull()}.$get(${e.index.genExpr()})"
			}
			is AstExpr.CAST -> {
				refs.add(e.from)
				refs.add(e.to)
				genCast(e.expr.genExpr(), e.from, e.to)
			}
			is AstExpr.NEW -> {
				refs.add(e.target)
				val className = e.target.haxeTypeNew
				"new $className()"
			}
			is AstExpr.INSTANCE_OF -> {
				refs.add(e.checkType)
				"Std.is(${e.expr.genExpr()}, ${e.checkType.haxeTypeCast})"
			}
			is AstExpr.NEW_ARRAY -> {
				refs.add(e.type.elementType)
				val desc = e.type.mangle().replace('/', '.') // Internal to normal name!?
				when (e.counts.size) {
					1 -> {
						if (e.type.elementType !is AstType.Primitive) {
							"new ${names.HaxeArrayAny}(${e.counts[0].genExpr()}, \"$desc\")"
						} else {
							"new ${e.type.haxeTypeNew}(${e.counts[0].genExpr()})"
						}
					}
					else -> {
						"${names.HaxeArrayAny}.createMultiSure([${e.counts.map { it.genExpr() }.joinToString(", ")}], \"$desc\")"
					}
				}
			}
			is AstExpr.CAUGHT_EXCEPTION -> "J__exception__"
			is AstExpr.METHOD_CLASS -> {
				val methodInInterfaceRef = e.methodInInterfaceRef
				val methodToConvertRef = e.methodToConvertRef
				val interfaceName = methodInInterfaceRef.classRef.name

				val interfaceLambdaFqname = interfaceName.haxeLambdaName
				"new $interfaceLambdaFqname(" + Indenter.genString {
					//methodInInterfaceRef.type.args

					val argNameTypes = methodInInterfaceRef.type.args.map { it.haxeNameAndType }.joinToString(", ")

					line("function($argNameTypes)") {
						// @TODO: Static + non-static
						//val methodToCallClassName = methodToConvertRef.classRef.name.haxeClassFqName
						//val methodToCallName = methodToConvertRef.haxeName

						val args = methodInInterfaceRef.type.args.map { AstLocal(-1, it.name, it.type) }

						line("return " + genExpr2(AstExpr.CAST(AstExpr.CALL_STATIC(
							methodToConvertRef.containingClassType,
							methodToConvertRef,
							args.zip(methodToConvertRef.type.args).map { AstExpr.CAST(AstExpr.LOCAL(it.first), it.second.type) }
						), methodInInterfaceRef.type.ret)) + ";"
						)
					}
				} + ")"

			}
		//is AstExpr.REF -> genExpr2(e.expr)
			else -> throw NotImplementedError("Unhandled expression $this")
		}
	}

	fun convertToHaxe(expr: AstExpr.Box): String {
		return convertToHaxe(expr.type, expr.genExpr())
	}

	fun convertToJava(expr: AstExpr.Box): String {
		return convertToJava(expr.type, expr.genExpr())
	}

	fun convertToHaxe(type: AstType, text: String): String {
		return return convertToFromHaxe(type, text, toHaxe = true)
	}

	fun convertToJava(type: AstType, text: String): String {
		return return convertToFromHaxe(type, text, toHaxe = false)
	}

	fun convertToFromHaxe(type: AstType, text: String, toHaxe:Boolean): String {
		if (type is AstType.ARRAY) {
			return (if (toHaxe) "HaxeNatives.unbox($text)" else "HaxeNatives.box($text)")
		}

		if (type is AstType.REF) {
			val conversion = program[type.name].annotationsList.getTyped()
			if (conversion != null) {
				return (if (toHaxe) conversion.toHaxe else conversion.toJava).replace("@self", text)
			}
		}
		return text
	}

	fun genCast(e: String, from: AstType, to: AstType): String {
		if (from == to) return e

		if (from !is AstType.Primitive && to is AstType.Primitive) {
			return when (from) {
			// @TODO: Check!
				AstType.BOOL.CLASSTYPE -> genCast("HaxeNatives.unboxBool($e)", AstType.BOOL, to)
				AstType.BYTE.CLASSTYPE -> genCast("HaxeNatives.unboxByte($e)", AstType.BYTE, to)
				AstType.SHORT.CLASSTYPE -> genCast("HaxeNatives.unboxShort($e)", AstType.SHORT, to)
				AstType.CHAR.CLASSTYPE -> genCast("HaxeNatives.unboxChar($e)", AstType.CHAR, to)
				AstType.INT.CLASSTYPE -> genCast("HaxeNatives.unboxInt($e)", AstType.INT, to)
				AstType.LONG.CLASSTYPE -> genCast("HaxeNatives.unboxLong($e)", AstType.LONG, to)
				AstType.FLOAT.CLASSTYPE -> genCast("HaxeNatives.unboxFloat($e)", AstType.FLOAT, to)
				AstType.DOUBLE.CLASSTYPE -> genCast("HaxeNatives.unboxDouble($e)", AstType.DOUBLE, to)
			//AstType.OBJECT -> genCast(genCast(e, from, to.CLASSTYPE), to.CLASSTYPE, to)
			//else -> noImpl("Unhandled conversion $e : $from -> $to")
				else -> genCast(genCast(e, from, to.CLASSTYPE), to.CLASSTYPE, to)
			}
		}

		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 -> "($e2)"
					is AstType.LONG -> "HaxeNatives.intToLong($e2)"
					is AstType.FLOAT, is AstType.DOUBLE -> "($e2)"
					else -> unhandled()
				}
			}
			is AstType.DOUBLE, is AstType.FLOAT -> {
				when (to) {
					is AstType.BOOL -> "N.i2z(Std.int($e))"
					is AstType.BYTE -> "N.i2b(Std.int($e))"
					is AstType.CHAR -> "N.i2c(Std.int($e))"
					is AstType.SHORT -> "N.i2s(Std.int($e))"
					is AstType.INT -> "Std.int($e)"
					is AstType.LONG -> "HaxeNatives.floatToLong($e)"
					is AstType.FLOAT, is AstType.DOUBLE -> "($e)"
					else -> unhandled()
				}
			}
			is AstType.LONG -> {
				when (to) {
					is AstType.BOOL -> "N.i2z(($e).low)"
					is AstType.BYTE -> "N.i2b(($e).low)"
					is AstType.CHAR -> "N.i2c(($e).low)"
					is AstType.SHORT -> "N.i2s(($e).low)"
					is AstType.INT -> "($e).low"
					is AstType.LONG -> "($e)"
					is AstType.FLOAT, is AstType.DOUBLE -> "HaxeNatives.longToFloat($e)"
					else -> unhandled()
				}
			}
			is AstType.REF, is AstType.ARRAY, is AstType.GENERIC -> {
				when (to) {
					FUNCTION_REF -> "(HaxeNatives.getFunction($e))"
					else -> "N.c($e, ${to.haxeTypeCast})"
				}
			}
			is AstType.NULL -> "$e"
			else -> unhandled()
		}
	}

	val FUNCTION_REF = AstType.REF(JTranscFunction::class.java.name)

	private fun AstMethod.getHaxeNativeBodyList(): List {
		val bodyList = this.annotationsList.getTyped()
		val bodyEntry = this.annotationsList.getTyped()
		val bodies = listOf(bodyList?.value?.toList(), listOf(bodyEntry)).concatNotNull()
		return bodies
	}

	private fun AstMethod.hasHaxeNativeBody(): Boolean = this.annotationsList.contains() || this.annotationsList.contains()

	private fun AstMethod.getHaxeNativeBody(defaultContent: Indenter): Indenter {
		val method = this

		val bodies = this.getHaxeNativeBodyList()

		return if (bodies.size > 0) {
			val pre = method.annotationsList.getTyped()?.value ?: ""
			val post = method.annotationsList.getTyped()?.value ?: ""

			val bodiesmap = bodies.map { it.target to it.value }.toMap()
			val defaultbody: Indenter = if ("" in bodiesmap) Indenter.gen { line(bodiesmap[""]!!) } else defaultContent
			val extrabodies = bodiesmap.filterKeys { it != "" }
			Indenter.gen {
				line(pre)
				if (extrabodies.size == 0) {
					line(defaultbody)
				} else {
					var first = true
					for ((target, extrabody) in extrabodies) {
						line((if (first) "#if" else "#elseif") + " ($target) $extrabody")
						first = false
					}
					line("#else")
					line(defaultbody)
					line("#end")
				}
				line(post)
			}
		} else {
			defaultContent
		}
	}

	fun writeClass(clazz: AstClass, vfs: SyncVfsFile) {
		context.clazz = clazz
		strings = Strings()

		val isRootObject = clazz.name.fqname == "java.lang.Object"
		val isInterface = clazz.isInterface
		val isAbstract = (clazz.classType == AstClassType.ABSTRACT)
		val isNormalClass = (clazz.classType == AstClassType.CLASS)
		val classType = if (isInterface) "interface" else "class"
		val simpleClassName = clazz.name.haxeGeneratedSimpleClassName
		fun getInterfaceList(keyword: String) = (if (clazz.implementing.isNotEmpty()) " $keyword " else "") + clazz.implementing.map { it.haxeClassFqName }.joinToString(" $keyword ")
		//val implementingString = getInterfaceList("implements")
		val isInterfaceWithStaticMembers = isInterface && clazz.fields.any { it.isStatic }
		//val isInterfaceWithStaticFields = clazz.name.withSimpleName(clazz.name.simpleName + "\$StaticMembers")
		refs._usedDependencies.clear()

		if (!clazz.extending?.fqname.isNullOrEmpty()) refs.add(AstType.REF(clazz.extending!!))
		for (impl in clazz.implementing) refs.add(AstType.REF(impl))
		//val interfaceClassName = clazz.name.append("_Fields");

		var output = arrayListOf>()

		fun writeField(field: AstField, isInterface: Boolean): Indenter = Indenter.gen {
			val static = if (field.isStatic) "static " else ""
			val visibility = if (isInterface) " " else field.visibility.haxe
			val fieldType = field.type
			refs.add(fieldType)
			val defaultValue: Any? = if (field.hasConstantValue) field.constantValue else fieldType.haxeDefault
			val fieldName = field.haxeName
			//if (field.name == "this\$0") println("field: $field : fieldRef: ${field.ref} : $fieldName")
			if (!field.annotationsList.contains()) {
				val keep = if (field.annotationsList.contains()) "@:keep " else ""
				line("$keep$static$visibility var $fieldName:${fieldType.haxeTypeTag} = ${names.escapeConstant(defaultValue, fieldType)}; // /*${field.name}*/")
			}
		}

		fun writeMethod(method: AstMethod, isInterface: Boolean): Indenter {
			context.method = method
			return Indenter.gen {
				val static = if (method.isStatic) "static " else ""
				val visibility = if (isInterface) " " else method.visibility.haxe
				refs.add(method.methodType)
				val margs = method.methodType.args.map { it.name + ":" + it.type.haxeTypeTag }
				var override = if (method.haxeIsOverriding) "override " else ""
				val inline = if (method.isInline) "inline " else ""
				val rettype = if (method.methodVoidReturnThis) method.containingClass.astType else method.methodType.ret
				val decl = try {
					"$static $visibility $inline $override function ${method.haxeName}/*${method.name}*/(${margs.joinToString(", ")}):${rettype.haxeTypeTag}".trim()
				} catch (e: RuntimeException) {
					println("@TODO abstract interface not referenced: ${method.containingClass.fqname} :: ${method.name} : $e")
					//null
					throw e
				}

				if (isInterface) {
					if (!method.isImplementing) line("$decl;")
				} else {
					val meta = method.annotationsList.getTyped()?.value
					if (meta != null) line(meta)
					val rbody = if (method.body != null) {
						method.body
					} else if (method.bodyRef != null) {
						program[method.bodyRef!!]?.body
					} else {
						null
					}
					line(decl) {
						try {
							// @TODO: Do not hardcode this!
							if (method.name == "throwParameterIsNullException") line("HaxeNatives.debugger();")
							val javaBody = if (rbody != null) {
								rbody.genBodyWithFeatures()
							} else Indenter.gen {
								line("throw R.n(HAXE_CLASS_NAME, ${method.id});")
							}
							line(method.getHaxeNativeBody(javaBody).toString().template())
							if (method.methodVoidReturnThis) line("return this;")
						} catch (e: Throwable) {
							//e.printStackTrace()
							log.warn("WARNING haxe_gen.writeMethod:" + e.message)

							line("HaxeNatives.debugger(); throw " + "Errored method: ${clazz.name}.${method.name} :: ${method.desc} :: ${e.message}".quote() + ";")
						}
					}
				}
			}
		}

		fun addClassInit(clazz: AstClass) = Indenter.gen {
			line("static public var SII = false;");
			for (index in strings.getIndices()) {
				val str = strings.getStringWithId(index)
				val id = strings.getId(str)
				line("static private var $id:$JAVA_LANG_STRING;")
			}

			line("static public function SI()") {
				line("if (SII) return;")
				line("SII = true;")

				for (index in strings.getIndices()) {
					val str = strings.getStringWithId(index)
					val id = strings.getId(str)
					line("$id = ${names.escapeConstant(str)};")
				}

				if (clazz.hasStaticInit) {
					val methodName = clazz.staticInitMethod!!.haxeName
					line("$methodName();")
				}
			}
		}


		//val annotationTypeHaxeName = AstMethodRef(java.lang.annotation.Annotation::class.java.name.fqname, "annotationType", AstType.build { METHOD(java.lang.annotation.Annotation::class.java.ast()) }).haxeName
		val annotationTypeHaxeName = AstMethodRef(java.lang.annotation.Annotation::class.java.name.fqname, "annotationType", AstType.build { METHOD(CLASS) }).haxeName
		// java.lang.annotation.Annotation
		//abstract fun annotationType():Class

		val classCodeIndenter = Indenter.gen {
			line("package ${clazz.name.haxeGeneratedFqPackage};")

			if (isAbstract) line("// ABSTRACT")
			var declaration = "$classType $simpleClassName"
			if (isInterface) {
				if (clazz.implementing.isNotEmpty()) declaration += getInterfaceList("extends")
			} else {
				if (clazz.extending != null && clazz.name.fqname != "java.lang.Object") declaration += " extends ${clazz.extending!!.haxeClassFqName}"
				if (clazz.implementing.isNotEmpty()) declaration += getInterfaceList("implements")
			}

			// Additional imports!
			val imports = clazz.annotationsList.getTyped()?.value
			if (imports != null) for (i in imports) line(i)

			val meta = clazz.annotationsList.getTyped()?.value
			if (meta != null) line(meta)
			line(declaration) {
				if (!isInterface) {
					line("public function new()") {
						line(if (isRootObject) "" else "super();")
						line("SI();")
					}
				}

				val nativeMembers = clazz.annotationsList.getTyped()?.value?.toList() ?: listOf()

				for (member in nativeMembers) line(member.template())

				if (!isInterface) {
					for (field in clazz.fields) {
						line(writeField(field, isInterface))
					}
				}

				for (method in clazz.methods) {
					if (isInterface && method.isStatic) continue
					line(writeMethod(method, isInterface))
				}

				if (!isInterface) {
					//println(clazz.fqname + " -> " + program.getAllInterfaces(clazz))
					val isFunctionType = program.isImplementing(clazz, "all.core.AllFunction")

					if (isFunctionType) {
						val executeFirst = clazz.methodsByName["execute"]!!.first()
						line("public const _execute:Function = ${executeFirst.ref.haxeName};")
					}
				}

				/*
				if (isNormalClass) {
					val override = if (isRootObject) " " else "override "
					line("$override public function toString():String { return HaxeNatives.toNativeString(this.toString__Ljava_lang_String_()); }")
				}
				*/
				if (isRootObject) {
					line("public function toString():String { return HaxeNatives.toNativeString(this.$toStringHaxeName()); }")
					line("public function hashCode():Int { return this.$hashCodeHaxeName(); }")
				}

				if (!isInterface) {
					line(addClassInit(clazz))
					line(dumpClassInfo(clazz))
				}
			}

			//if (isInterfaceWithStaticMembers) {
			if (isInterface) {
				val javaLangObjectClass = program[FqName("java.lang.Object")]

				line("class ${simpleClassName}_IFields") {
					line("public function new() {}")
					for (field in clazz.fields) line(writeField(field, isInterface = false))
					for (method in clazz.methods.filter { it.isStatic }) line(writeMethod(method, isInterface = false))
					line(addClassInit(clazz))
					line(dumpClassInfo(clazz))
				}


				if (clazz in allAnnotationTypes) {
					line("// annotation type: ${clazz.name}")

					line("class ${names.getAnnotationProxyName(clazz.astType)} extends ${names.haxeName()} implements ${clazz.name.haxeClassFqName}") {
						line("private var _data:Array;")
						line("public function new(_data:Dynamic = null) { super(); this._data = _data; }")

						line("public function $annotationTypeHaxeName():$JAVA_LANG_CLASS { return HaxeNatives.resolveClass(${clazz.fqname.quote()}); }")
						line("override public function $getClassHaxeName():$JAVA_LANG_CLASS { return HaxeNatives.resolveClass(${clazz.fqname.quote()}); }")
						for ((index, m) in clazz.methods.withIndex()) {
							line("public function ${m.haxeName}():${m.methodType.ret.haxeTypeTag} { return this._data[$index]; }")
						}
					}
				}

				if (clazz.hasFFI) {
					line("class ${simpleClassName}_FFI extends $JAVA_LANG_OBJECT implements $simpleClassName implements HaxeFfiLibrary") {
						val methods = clazz.allMethodsToImplement.map { clazz.getMethodInAncestorsAndInterfaces(it)!! }
						line("private var __ffi_lib:haxe.Int64 = 0;")
						for (method in methods) {
							line("private var __ffi_${method.name}:haxe.Int64 = 0;")
						}
						line("@:noStack public function _ffi__load(library:String)") {
							//line("trace('Loading... \$library');")
							line("#if cpp")
							line("__ffi_lib = HaxeDynamicLoad.dlopen(library);")
							line("if (__ffi_lib == 0) trace('Cannot open library: \$library');")
							for (method in methods) {
								line("__ffi_${method.name} = HaxeDynamicLoad.dlsym(__ffi_lib, '${method.name}');")
								line("if (__ffi_${method.name} == 0) trace('Cannot load method ${method.name}');")
							}
							line("#end")
						}
						line("@:noStack public function _ffi__close()") {
							line("#if cpp")
							line("HaxeDynamicLoad.dlclose(__ffi_lib);")
							line("#end")
						}

						fun AstType.castToHaxe(): String {
							return when (this) {
								AstType.VOID -> ""
								AstType.BOOL -> "(bool)"
								AstType.INT -> "(int)"
								AstType.LONG -> "(int)" // @TODO!
							//AstType.STRING -> "char*"
								else -> "(void*)"
							}
						}

						fun AstType.nativeType(): String {
							return when (this) {
								AstType.VOID -> "void"
								AstType.BOOL -> "bool"
								AstType.INT -> "int"
								AstType.LONG -> "int" // @TODO!
								AstType.STRING -> "char*"
								else -> "void*"
							}
						}

						fun AstType.castToNative(): String {
							return "(${this.nativeType()})"
						}

						fun AstType.castToNativeHx(str: String): String {
							return when (this) {
								AstType.STRING -> "cpp.NativeString.c_str(($str)._str)"
								else -> return str
							}
						}

						fun AstType.METHOD.toCast(stdCall: Boolean): String {
							val argTypes = this.args.map { it.type.nativeType() }
							val typeInfix = if (stdCall) "__stdcall " else " "
							return "(${this.ret.nativeType()} (${typeInfix}*)(${argTypes.joinToString(", ")}))(void *)(size_t)"
						}

						for (method in methods) {
							val methodName = method.ref.haxeName
							val methodType = method.methodType
							val margs = methodType.args.map { it.name + ":" + it.type.haxeTypeTag }.joinToString(", ")
							val rettype = methodType.ret.haxeTypeTag

							val stdCall = method.annotationsList.contains()

							line("@:noStack public function $methodName($margs):$rettype") {
								val argIds = methodType.args.withIndex().map { "${it.value.type.castToNative()}{${(it.index + 1)}}" }.joinToString(", ")
								val cppArgs = (listOf("__ffi_${method.name}") + methodType.args.map { it.type.castToNativeHx(it.name) }).joinToString(", ")
								val mustReturn = methodType.ret != AstType.VOID
								val retstr = if (mustReturn) "return " else ""
								line("#if cpp untyped __cpp__('$retstr ${methodType.ret.castToHaxe()}((${methodType.toCast(stdCall)}{0})($argIds));', $cppArgs); #end")
								if (mustReturn) line("return cast 0;")
							}
						}
					}
				}

				line("class ${simpleClassName}_Proxy extends $JAVA_LANG_OBJECT implements $simpleClassName") {

					line("private var __clazz:$JAVA_LANG_CLASS;")
					line("private var __invocationHandler:$invocationHandlerHaxeName;")
					line("private var __methods:Map;")
					line("public function new(handler:$invocationHandlerHaxeName)") {
						line("super();")
						line("this.__clazz = HaxeNatives.resolveClass(\"${clazz.name.fqname}\");")
						line("this.__invocationHandler = handler;")
					}
					// public Object invoke(Object proxy, Method method, Object[] args)
					line("private function _invoke(methodId:Int, args:Array<$JAVA_LANG_OBJECT>):$JAVA_LANG_OBJECT") {
						line("var method = this.__clazz.locateMethodById(methodId);");
						line("return this.__invocationHandler.$invokeHaxeName(this, method, ${names.HaxeArrayAny}.fromArray(args, '[Ljava.lang.Object;'));")
					}

					for (methodRef in clazz.allMethodsToImplement) {
						val mainMethod = clazz.getMethodInAncestorsAndInterfaces(methodRef)
						if (mainMethod == null) {
							println("NULL methodRef: $methodRef")
							continue
						}
						val mainMethodName = mainMethod.ref.haxeName
						val methodType = mainMethod.methodType
						val margs = methodType.args.map { it.name + ":" + it.type.haxeTypeTag }.joinToString(", ")
						val rettype = methodType.ret.haxeTypeTag
						val returnOrEmpty = if (methodType.retVoid) "" else "return "
						val margBoxedNames = methodType.args.map { it.type.box(it.name) }.joinToString(", ")
						val typeStr = methodType.functionalType
						val methodInObject = javaLangObjectClass[mainMethod.ref.withoutClass]
						val methodId = mainMethod.id

						line("${methodInObject.nullMap("override", "")} public function $mainMethodName($margs):$rettype { return " + methodType.ret.unbox("this._invoke($methodId, [$margBoxedNames]") + ");  }")
					}
				}

				val methodsWithoutBody = clazz.methods.filter { it.body == null }
				if (methodsWithoutBody.size == 1 && clazz.implementing.size == 0) {
					// @TODO: Probably it should allow interfaces extending!
					val mainMethod = methodsWithoutBody.first()
					val mainMethodName = mainMethod.ref.haxeName
					val methodType = mainMethod.methodType
					val margs = methodType.args.map { it.name + ":" + it.type.haxeTypeTag }.joinToString(", ")
					val rettype = methodType.ret.haxeTypeTag
					val returnOrEmpty = if (methodType.retVoid) "" else "return "
					val margNames = methodType.args.map { it.name }.joinToString(", ")
					val typeStr = methodType.functionalType
					line("class ${simpleClassName}_Lambda extends $JAVA_LANG_OBJECT implements $simpleClassName") {
						line("private var ___func__:$typeStr;")
						line("public function new(func: $typeStr) { super(); this.___func__ = func; }")
						val methodInObject = javaLangObjectClass[mainMethod.ref.withoutClass]
						line("${methodInObject.nullMap("override", "")} public function $mainMethodName($margs):$rettype { $returnOrEmpty ___func__($margNames); }")
						for (dmethod in clazz.methods.filter { it.body != null }) {
							val dmethodName = dmethod.ref.haxeName
							val dmethodArgs = dmethod.methodType.args.map { it.name + ":" + it.type.haxeTypeTag }.joinToString(", ")
							val dmethodRettype = dmethod.methodType.ret.haxeTypeTag
							line("${methodInObject.nullMap("override", "")} public function $dmethodName($dmethodArgs):$dmethodRettype") {
								line(dmethod.body!!.genBodyWithFeatures())
							}
						}
					}
				}
			}
		}

		val lineMappings = hashMapOf()

		val fileStr = classCodeIndenter.toString { sb, line, data ->
			if (data is AstStm.LINE) {
				//println("MARKER: ${sb.length}, $line, $data, ${clazz.source}")
				lineMappings[line] = data.line
				//clazzName.internalFqname + ".java"
			}
		}

		val haxeFilePath = clazz.name.haxeFilePath
		vfs["$haxeFilePath"] = fileStr
		vfs["$haxeFilePath.map"] = Sourcemaps.encodeFile(vfs["$haxeFilePath"].realpathOS, fileStr, clazz.source, lineMappings)
	}

	//val FqName.as3Fqname: String get() = this.fqname
	//fun AstMethod.getHaxeMethodName(program: AstProgram): String = this.ref.getHaxeMethodName(program)

	enum class TypeKind { TYPETAG, NEW, CAST }

	val AstVisibility.haxe: String get() = "public"

	val AstType.haxeTypeTag: FqName get() = names.getHaxeType(this, TypeKind.TYPETAG)
	val AstType.haxeTypeNew: FqName get() = names.getHaxeType(this, TypeKind.NEW)
	val AstType.haxeTypeCast: FqName get() = names.getHaxeType(this, TypeKind.CAST)
	val AstType.haxeDefault: Any? get() = names.getHaxeDefault(this)
	val AstType.haxeDefaultString: String get() = names.escapeConstant(names.getHaxeDefault(this), this)
	val AstType.METHOD.functionalType: String get() = names.getHaxeFunctionalType(this)

	fun AstType.box(arg: String): String {
		return when (this) {
			is AstType.Primitive -> "HaxeNatives.box${this.shortName.capitalize()}($arg)"
			else -> "cast($arg)";
		}
	}

	fun AstType.unbox(arg: String): String {
		return when (this) {
			is AstType.Primitive -> "HaxeNatives.unbox${this.shortName.capitalize()}($arg)"
			else -> "cast($arg)";
		}
	}

	val AstLocal.haxeName: String get() = this.name.replace('$', '_')
	val AstExpr.LocalExpr.haxeName: String get() = this.name.replace('$', '_')

	val AstField.haxeName: String get() = names.getHaxeFieldName(this)
	val AstFieldRef.haxeName: String get() = names.getHaxeFieldName(this)
	val AstFieldRef.haxeStaticText: String get() = names.getStaticFieldText(this)

	val AstMethod.haxeName: String get() = names.getHaxeMethodName(this)
	val AstMethodRef.haxeName: String get() = names.getHaxeMethodName(this)

	val AstMethod.haxeIsOverriding: Boolean get() = this.isOverriding && !this.isInstanceInit

	val FqName.haxeLambdaName: String get() = names.getHaxeClassFqNameLambda(this)
	val FqName.haxeClassFqName: String get() = names.getHaxeClassFqName(this)
	val FqName.haxeClassFqNameInt: String get() = names.getHaxeClassFqNameInt(this)
	val FqName.haxeFilePath: String get() = names.getHaxeFilePath(this)
	val FqName.haxeGeneratedFqPackage: String get() = names.getHaxeGeneratedFqPackage(this)
	val FqName.haxeGeneratedFqName: FqName get() = names.getHaxeGeneratedFqName(this)
	val FqName.haxeGeneratedSimpleClassName: String get() = names.getHaxeGeneratedSimpleClassName(this)

	fun String.template(): String = haxeTemplateString.gen(this)

	val AstArgument.haxeNameAndType: String get() = this.name + ":" + this.type.haxeTypeTag

	class MutableBody(val method: AstMethod) {
		val referencedClasses = hashMapOf>()
		fun initClassRef(classRef: AstType.REF, reason:String) {
			referencedClasses.putIfAbsent(classRef, arrayListOf())
			referencedClasses[classRef]!! += reason
		}
	}

	class References {
		var _usedDependencies = hashSetOf()
		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
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy