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

com.jtransc.gen.haxe.HaxeNames.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.ast.*
import com.jtransc.error.invalidOp
import com.jtransc.error.unexpected
import com.jtransc.gen.MinimizedNames
import com.jtransc.text.escape
import com.jtransc.text.quote

val HaxeKeywords = setOf(
	"haxe",
	"Dynamic",
	"Void",
	"java",
	"package",
	"import",
	"class", "interface", "extends", "implements",
	"internal", "private", "protected", "final",
	"function", "var", "const",
	"if", "else",
	"switch", "case", "default",
	"do", "while", "for", "each", "in",
	"try", "catch", "finally",
	"break", "continue",
	"int", "uint", "void",
	"goto"
)

val HaxeSpecial = setOf(
	"hx",
	"z", // used for package
	"N", // used for HaxeNatives
	"NN", // used for HaxeNatives without references to other classes
	"R", // used for reflect
	"SI", // STATIC INIT
	"SII", // STATIC INIT INITIALIZED
	"HAXE_CLASS_INIT", // Information about the class
	"HAXE_CLASS_NAME", // Information about the class
	"HaxeNatives", // used for HaxeNatives
	"unix",
	"OVERFLOW", // iphone sdk
	"UNDERFLOW" // iphone sdk
)

val HaxeKeywordsWithToStringAndHashCode: Set = HaxeKeywords + HaxeSpecial + setOf("toString", "hashCode")

inline fun  MutableMap.getOrPut2(key: T1, generator: () -> T2): T2 {
	if (key !in this) this[key] = generator()
	return this[key]!!
}

class HaxeNames(
	val program: AstResolver,
	val minimize: Boolean = false
) {
	private val cachedFieldNames = hashMapOf()

	//val ENABLED_MINIFY = false
	val ENABLED_MINIFY = true
	private val ENABLED_MINIFY_MEMBERS = ENABLED_MINIFY && minimize
	private val ENABLED_MINIFY_CLASSES = ENABLED_MINIFY && minimize

	//private val ENABLED_MINIFY_CLASSES = true
	//private val ENABLED_MINIFY_MEMBERS = false

	private var minClassLastId: Int = 0
	private var minMemberLastId: Int = 0
	private val classNames = hashMapOf()
	private val methodNmaes = hashMapOf()
	private val fieldNames = hashMapOf()

	val minClassPrefix = "z."
	//val minClassPrefix = ""

	private fun  Set.runUntilNotInSet(callback: () -> T): T {
		while (true) {
			val result = callback()
			if (result !in this) return result
		}
	}

	fun allocClassName(): String = HaxeKeywordsWithToStringAndHashCode.runUntilNotInSet { MinimizedNames.getTypeNameById(minClassLastId++) }
	fun allocMemberName(): String = HaxeKeywordsWithToStringAndHashCode.runUntilNotInSet { MinimizedNames.getIdNameById(minMemberLastId++) }

	fun getHaxeMethodName(method: AstMethod): String = getHaxeMethodName(method.ref)
	fun getHaxeMethodName(method: AstMethodRef): String {
		val realmethod = program[method] ?: invalidOp("Can't find method $method")
		val realclass = realmethod.containingClass
		val methodWithoutClass = method.withoutClass

		val objectToCache: Any = if (method.isClassOrInstanceInit) method else methodWithoutClass

		return if (realclass.isNative) {
			// No cache
			realmethod.nativeName ?: method.name
		} else {
			methodNmaes.getOrPut2(objectToCache) {
				if (ENABLED_MINIFY_MEMBERS && !realmethod.keepName) {
					allocMemberName()
				} else {
					if (realmethod.nativeMethod != null) {
						realmethod.nativeMethod!!
					} else {
						val name2 = "${method.name}${method.desc}"
						val name = when (method.name) {
							"", "" -> "${method.containingClass}$name2"
							else -> name2
						}
						cleanName(name)
					}
				}
			}
		}
	}

	private fun cleanName(name: String): String {
		val out = CharArray(name.length)
		for (n in 0 until name.length) out[n] = if (name[n].isLetterOrDigit()) name[n] else '_'
		return String(out)
	}

	fun getHaxeFunctionalType(type: AstType.METHOD): String {
		return type.argsPlusReturnVoidIsEmpty.map { getHaxeType(it, GenHaxeGen.TypeKind.TYPETAG) }.joinToString(" -> ")
	}

	fun getHaxeDefault(type: AstType): Any? = type.getNull()

	private fun _getHaxeFqName(name: FqName): FqName {
		val realclass = if (name in program) program[name]!! else null
		return classNames.getOrPut2(name) {
			if (realclass?.nativeName != null) {
				FqName(realclass!!.nativeName!!)
			} else if (ENABLED_MINIFY_CLASSES && !realclass.keepName) {
				FqName(minClassPrefix + allocClassName())
			} else {
				FqName(name.packageParts.map { if (it in HaxeKeywords) "${it}_" else it }.map { it.decapitalize() }, "${name.simpleName.replace('$', '_')}_".capitalize())
			}
		}
	}

	fun getHaxeFilePath(name: FqName): String = getHaxeGeneratedFqName(name).internalFqname + ".hx"

	fun getHaxeGeneratedFqPackage(name: FqName): String = _getHaxeFqName(name).packagePath
	fun getHaxeGeneratedFqName(name: FqName): FqName = _getHaxeFqName(name)
	fun getHaxeGeneratedSimpleClassName(name: FqName): String = _getHaxeFqName(name).simpleName
	inline fun  haxeName(): String = getHaxeClassFqName(T::class.java.name.fqname)

	fun getHaxeClassFqName(name: FqName): String {
		val clazz = if (name in program) program[name] else null
		return clazz?.nativeName ?: getHaxeGeneratedFqName(name).fqname
	}

	fun getHaxeFieldName(clazz: Class<*>, name:String): String {
		return getHaxeFieldName(program[clazz.name.fqname]!!.fieldsByName[name]!!)
	}

	fun getHaxeFieldName(field: AstFieldRef): String {
		val realfield = program[field]
		val realclass = program[field.containingClass]
		//val keyToUse = if (realfield.keepName) field else field.name
		//val keyToUse = if (ENABLED_MINIFY_FIELDS) field else field.name
		val keyToUse = field

		return if (realclass.isNative) {
			// No cache
			realfield?.nativeName ?: field.name
		} else {
			fieldNames.getOrPut2(keyToUse) {
				if (ENABLED_MINIFY_MEMBERS && !realfield.keepName) {
					allocMemberName()
				} else {
					if (field !in cachedFieldNames) {
						val fieldName = field.name.replace('$', '_')
						var name = if (fieldName in HaxeKeywordsWithToStringAndHashCode) "${fieldName}_" else fieldName

						val clazz = program[field]?.containingClass
						val clazzAncestors = clazz?.ancestors?.reversed() ?: listOf()
						val names = clazzAncestors.flatMap { it.fields }.filter { it.name == field.name }.map { getHaxeFieldName(it.ref) }.toHashSet()
						val fieldsColliding = clazz?.fields?.filter { it.name == field.name }?.map { it.ref } ?: listOf(field)

						// JTranscBugInnerMethodsWithSameName.kt
						for (f2 in fieldsColliding) {
							while (name in names) name += "_"
							cachedFieldNames[f2] = name
							names += name
						}
						cachedFieldNames[field] ?: unexpected("Unexpected. Not cached: $field")
					}
					cachedFieldNames[field] ?: unexpected("Unexpected. Not cached: $field")
				}
			}
		}
	}

	fun getHaxeFieldName(field: AstField): String {
		//field.annotations.contains()
		return getHaxeFieldName(field.ref)
	}

	fun getStaticFieldText(field: AstFieldRef): String {
		val prefix = getHaxeClassFqNameInt(field.classRef.name)
		return "$prefix.${getHaxeFieldName(field)}"
	}

	fun getHaxeClassFqNameInt(name: FqName): String {
		val clazz = program[name]
		val simpleName = getHaxeGeneratedSimpleClassName(name)
		val suffix = if (clazz?.isInterface ?: false) ".${simpleName}_IFields" else ""
		return getHaxeClassFqName(clazz?.name ?: name) + "$suffix"
	}

	fun getHaxeClassFqNameLambda(name: FqName): String {
		val clazz = program[name]
		val simpleName = getHaxeGeneratedSimpleClassName(name)
		return getHaxeClassFqName(clazz?.name ?: name) + ".${simpleName}_Lambda"
	}

	fun getHaxeClassStaticInit(classRef: AstType.REF, reason: String): String {
		val clazz = program[classRef.name]
		if (clazz?.nativeName != null) {
			return ""
		} else {
			return "${getHaxeClassFqNameInt(classRef.name)}.SI() /* $reason */;"
		}
	}

	fun getHaxeClassStaticClassInit(classRef: AstType.REF): String {
		return "${getHaxeClassFqNameInt(classRef.name)}.HAXE_CLASS_INIT"
	}

	fun getAnnotationProxyName(classRef: AstType.REF): String {
		return "AnnotationProxy_${getHaxeGeneratedFqName(classRef.name).fqname.replace('.', '_')}"
	}

	fun getFullAnnotationProxyName(classRef: AstType.REF): String {
		return getHaxeClassFqName(classRef.name) + ".AnnotationProxy_${getHaxeGeneratedFqName(classRef.name).fqname.replace('.', '_')}"
	}

	fun getHaxeType(type: AstType, typeKind: GenHaxeGen.TypeKind): FqName {
		return FqName(when (type) {
			is AstType.NULL -> "Dynamic"
			is AstType.VOID -> "Void"
			is AstType.BOOL -> "Bool"
			is AstType.GENERIC -> getHaxeType(type.type, typeKind).fqname
			is AstType.INT, is AstType.SHORT, is AstType.CHAR, is AstType.BYTE -> "Int"
			is AstType.FLOAT -> "Float32"
			is AstType.DOUBLE -> "Float64"
			is AstType.LONG -> "haxe.Int64"
			is AstType.REF -> program[type.name]?.nativeName ?: getHaxeClassFqName(type.name)
			is AstType.ARRAY -> when (type.element) {
				is AstType.BOOL -> "JA_Z"
				is AstType.BYTE -> "JA_B"
				is AstType.CHAR -> "JA_C"
				is AstType.SHORT -> "JA_S"
				is AstType.INT -> "JA_I"
				is AstType.LONG -> "JA_J"
				is AstType.FLOAT -> "JA_F"
				is AstType.DOUBLE -> "JA_D"
				else -> HaxeArrayAny
			}
			else -> throw RuntimeException("Not supported haxe type $type, $typeKind")
		})
	}

	val HaxeArrayAny = "JA_L"
	val HaxeArrayBase = "JA_0"

	fun escapeConstant(value: Any?, type: AstType): String {
		val result = escapeConstant(value)
		return if (type != AstType.BOOL) result else if (result != "false" && result != "0") "true" else "false"
	}

	fun escapeConstant(value: Any?): String = when (value) {
		null -> "null"
		is Boolean -> if (value) "true" else "false"
		is String -> "N.strLit(\"" + value.escape() + "\")"
		is Long -> "N.lnew(${((value ushr 32) and 0xFFFFFFFF).toInt()}, ${((value ushr 0) and 0xFFFFFFFF).toInt()})"
		is Float -> escapeConstant(value.toDouble())
		is Double -> if (value.isInfinite()) if (value < 0) "Math.NEGATIVE_INFINITY" else "Math.POSITIVE_INFINITY" else if (value.isNaN()) "Math.NaN" else "$value"
		is Number -> "${value.toInt()}"
		is Char -> "${value.toInt()}"
		is AstType.REF -> "HaxeNatives.resolveClass(${value.mangle().quote()})"
		is AstType.ARRAY -> "HaxeNatives.resolveClass(${value.mangle().quote()})"
		else -> throw NotImplementedError("Literal of type $value")
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy