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

com.jtransc.gen.d.DTarget.kt Maven / Gradle / Ivy

Go to download

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

The newest version!
package com.jtransc.gen.d

import com.jtransc.ConfigOutputFile
import com.jtransc.ConfigTargetDirectory
import com.jtransc.JTranscSystem
import com.jtransc.ast.*
import com.jtransc.ast.feature.method.GotosFeature
import com.jtransc.ast.feature.method.SwitchFeature
import com.jtransc.ast.feature.method.UndeterministicParameterEvaluationFeature
import com.jtransc.error.invalidOp
import com.jtransc.gen.GenTargetDescriptor
import com.jtransc.gen.TargetBuildTarget
import com.jtransc.gen.common.*
import com.jtransc.injector.Injector
import com.jtransc.injector.Singleton
import com.jtransc.io.ProcessResult2
import com.jtransc.text.*
import com.jtransc.vfs.*
import java.io.File

// Supports GOTO keyword
// Supports static fields and methods on interfaces
class DTarget : GenTargetDescriptor() {
	override val name = "d"
	override val outputExtension = "bin"
	override val extraLibraries = listOf()
	override val extraClasses = listOf()
	override val runningAvailable = true
	override val programFeatures: Set> = setOf()

	override val buildTargets: List = listOf(
		TargetBuildTarget("d", "d", "program.d", minimizeNames = false)
	)

	@Suppress("ConvertLambdaToReference")
	override fun getGenerator(injector: Injector): CommonGenerator {
		val settings = injector.get()
		val configTargetDirectory = injector.get()
		val configOutputFile = injector.get()
		val targetFolder = LocalVfsEnsureDirs(File("${configTargetDirectory.targetDirectory}/jtransc-d"))
		injector.mapInstance(CommonGenFolders(settings.assets.map { LocalVfs(it) }))
		injector.mapInstance(ConfigTargetFolder(targetFolder))
		injector.mapInstance(ConfigSrcFolder(targetFolder))
		injector.mapInstance(ConfigOutputFile2(targetFolder[configOutputFile.outputFileBaseName].realfile))
		return injector.get()
	}

	override fun getTargetByExtension(ext: String): String? = when (ext) {
		"exe" -> "cpp"
		"bin" -> "cpp"
		else -> null
	}
}

@Singleton
class DGenerator(injector: Injector) : CommonGenerator(injector) {
	override val TARGET_NAME: String = "DLANG"
	override val SINGLE_FILE: Boolean = true

	//class DGenerator(injector: Injector) : FilePerClassCommonGenerator(injector) {
	override val methodFeaturesWithTraps = setOf(SwitchFeature::class.java, UndeterministicParameterEvaluationFeature::class.java) // Undeterministic is required on windows!?
	override val methodFeatures = (methodFeaturesWithTraps + listOf(GotosFeature::class.java)).toSet()
	override val stringPoolType: StringPool.Type = StringPool.Type.GLOBAL
	override val floatHasFSuffix: Boolean = true

	override val keywords = setOf(
		"abstract", "alias", "align", "asm", "assert", "auto",
		"body", "bool", "break", "byte",
		"case", "cast", "catch", "cdouble", "cent", "cfloat", "char", "class", "const", "continue", "creal",
		"dchar", "debug", "default", "delegate", "delete", "deprecated", "do", "double",
		"else", "enum", "export", "extern",
		"false", "final", "finally", "float", "for", "foreach", "foreach_reverse", "function",
		"goto",
		"idouble", "if", "ifloat", "immutable", "import", "in", "inout", "int", "interface", "invariant", "ireal", "is",
		"lazy", "long",
		"macro", "mixin", "module",
		"new", "nothrow", "null",
		"out", "override",
		"package", "pragma", "private", "protected", "public", "pure",
		"real", "ref", "return",
		"scope", "shared", "short", "static", "struct", "super", "switch", "synchronized",
		"template", "this", "throw", "true", "try", "typedef", "typeid", "typeof",
		"ubyte", "ucent", "uint", "ulong", "union", "unittest", "ushort",
		"version", "void", "volatile",
		"wchar", "while", "with",
		"__FILE__", "__FILE_FULL_PATH__", "__MODULE__", "__LINE__", "__FUNCTION__", "__PRETTY_FUNCTION__", "__gshared", "__traits", "__vector", "__parameters",

		// Known Object symbols
		"clone", "toString",
		"std", "core"
	)

	override val languageRequiresDefaultInSwitch = true
	override val defaultGenStmSwitchHasBreaks = true

	override fun genCompilerCommand(programFile: File, debug: Boolean, libs: List): List {
		return DCompiler.genCommand(programFile, debug, libs, extraVars)
	}

	override fun run(redirect: Boolean): ProcessResult2 {
		val names = if (JTranscSystem.isWindows()) {
			listOf("program.exe", "a.exe")
		} else {
			listOf("program", "program.out", "a", "a.out")
		}
		val outFile = names.map { configTargetFolder.targetFolder[it] }.firstOrNull { it.exists } ?: invalidOp("Not generated output file $names")
		return ProcessResult2(RootLocalVfs().exec(outFile.realpathOS, listOf(), ExecOptions(passthru = redirect, sysexec = true)))
	}

	override fun writeClasses(output: SyncVfsFile) {
		//println(program.resourcesVfs)
		super.writeClasses(output)
		println(output)
	}

	override fun genField(field: AstField): Indenter = Indenter {
		var targetType = field.type.targetName
		//if (field.modifiers.isVolatile) targetType = "shared($targetType)"
		if (field.isStatic) targetType = "__gshared $targetType"

		if (field.targetName == "__parameters") {
			println("ERROR")
		}

		line("$targetType ${field.targetName} = ${field.type.getNull().escapedConstant};")
	}

	override fun genSingleFileClasses(output: SyncVfsFile): Indenter = Indenter {
		val StringFqName = buildTemplateClass("java.lang.String".fqname)
		val classesStr = super.genSingleFileClasses(output)
		line(classesStr)

		for (lit in getGlobalStrings()) {
			line("__gshared $StringFqName ${lit.name} = N.strLitEscape(${lit.str.dquote()});")
		}
		//line("static void __initStrings()") {
		//	for (lit in getGlobalStrings()) {
		//		// STRINGLIT_
		//		line("${lit.name} = N.strLitEscape(${lit.str.dquote()});")
		//	}
		//}

		val entryPointFqName = program.entrypoint
		val entryPointClass = program[entryPointFqName]
		line("int main(string[] args)") {
			line("N.init();")
			//line("__initStrings();")
			line(genStaticConstructorsSorted())
			//line(buildStaticInit(entryPointFqName))
			val mainMethod = entryPointClass[AstMethodRef(entryPointFqName, "main", AstType.METHOD(AstType.VOID, ARRAY(AstType.STRING)))]
			line(buildMethod(mainMethod, static = true) + "(N.strArray(args[1..$]));")
			line("return 0;")
		}
	}

	fun String?.dquote(): String {
		if (this == null) return "null"
		return "[" + this.map { it.toInt() }.joinToString(",") + "]"
		/*
		val out = StringBuilder()
		for (n in 0 until this.length) {
			val c = this[n]
			when (c) {
				'\\' -> out.append("\\\\")
				'"' -> out.append("\\\"")
				'\n' -> out.append("\\n")
				'\r' -> out.append("\\r")
				'\t' -> out.append("\\t")
				in '\u0000'..'\u001f', in '\u007f'..'\uffff' -> out.append("\\u" + "%04x".format(c.toInt()))
				else -> out.append(c)
			}
		}
		return "\"" + out.toString() + "\""
		*/
	}

	override fun genClassBodyMethods(clazz: AstClass, kind: MemberTypes): Indenter = Indenter {
		val directMethods = clazz.methods
		val interfaceMethods = clazz.allDirectInterfaces.flatMap { it.methods }
		val actualMethods = (if (clazz.isInterface) directMethods else directMethods + interfaceMethods).filter { !it.isStatic }
		for (rm in directMethods.filter { it.isStatic }) {
			line(genMethod(clazz, rm, true))
		}
		for (rm in actualMethods.map { clazz.getMethodInAncestors(it.ref.nameDesc) ?: invalidOp("Can't find method $it in $clazz ancestors") }.distinct()) {
			// @TODO: HACK!
			if (rm.containingClass != clazz) {
				if (!rm.isOverriding) line("override")
			}
			line(genMethod(clazz, rm, !clazz.isInterface))
		}
	}

	override fun genMethodDeclModifiers(method: AstMethod): String {
		//if (method.isInstanceInit) {
		//	//return "pragma(inline, true)" + super.genMethodDeclModifiers(method)
		//	return "pragma(inline) final " + super.genMethodDeclModifiers(method)
		//} else {
		return super.genMethodDeclModifiers(method)
		//}
	}

	override fun genClassDecl(clazz: AstClass, kind: MemberTypes): String {
		val CLASS = if (clazz.isInterface) "interface" else "class"
		val iabstract = if (clazz.isAbstract) "abstract " else ""
		val base = "$iabstract$CLASS ${clazz.name.targetSimpleName}"
		val parts = arrayListOf()
		if (clazz.extending != null) parts += clazz.extending!!.targetClassFqName
		if (clazz.implementing.isNotEmpty()) parts += clazz.implementing.map { it.targetClassFqName }
		if (parts.isEmpty()) {
			return base
		} else {
			return "$base : ${parts.distinct().joinToString(", ")}"
		}
	}

	override fun N_is(a: String, b: String): String = "((cast($b)$a) !is null)"

	override val NullType by lazy { AstType.OBJECT.targetName }
	override val VoidType = "void"
	override val BoolType = "bool"
	override val IntType = "int"
	override val ShortType = "short"
	override val CharType = "wchar"
	override val ByteType = "byte"
	override val FloatType = "float"
	override val DoubleType = "double"
	override val LongType = "long"

	override val FqName.targetSimpleName: String get() = this.targetName

	override fun N_c(str: String, from: AstType, to: AstType): String {
		//if (str == "this") return "this"
		//if (to is AstType.REF && to.fqname == "java.lang.Object" && from is AstType.Reference) return str

		if (from is AstType.REF && to is AstType.REF) {
			val fromClass = program[from]!!
			val toClass = program[to]!!
			if (toClass in fromClass.ancestors) return str
			if (toClass in fromClass.allInterfacesInAncestors) return str
		}

		return "(cast(${to.targetName})($str))"
	}

	override fun genExprArrayLength(e: AstExpr.ARRAY_LENGTH): String = "(cast($BaseArrayType)${e.array.genNotNull()}).length"
	override fun genStmThrow(stm: AstStm.THROW, last: Boolean) = Indenter("throw new WrappedThrowable(${stm.exception.genExpr()});")

	override fun genSIMethod(clazz: AstClass): Indenter = Indenter {
		if (clazz.isJavaLangObject) {
			line("override public string toString()") {
				line("return to!string(N.istr(" + buildMethod(clazz.getMethodWithoutOverrides("toString")!!, static = false) + "()));")
			}
		}

		if (!clazz.isInterface) {
			if (clazz.isJavaLangObject) {
				line("public int __JT__CLASS_ID;")
				line("this(int CLASS_ID = ${clazz.classId}) { this.__JT__CLASS_ID = CLASS_ID; }")
			} else {
				line("this(int CLASS_ID = ${clazz.classId}) { super(CLASS_ID); }")
			}
		}
		if (clazz.staticConstructor != null) {
			line("static public void SI()") {
				for (field in clazz.fields.filter { it.isStatic }) {
					line("${clazz.name.targetName}.${field.targetName} = ${field.escapedConstantValue};")
				}
				line(genSIMethodBody(clazz))
			}
		} else {
			line("static public void SI() { }")
		}
	}

	//override fun N_i(str: String) = "(cast(int)($str))"
	override fun N_i(str: String) = "($str)"

	override fun N_f2i(str: String) = "N.f2i($str)"
	override fun N_d2i(str: String) = "N.d2i($str)"
	override fun N_c_eq(l: String, r: String) = "($l is $r)"
	override fun N_c_ne(l: String, r: String) = "($l !is $r)"

	override fun N_i2f(str: String) = "(cast(float)($str))"
	override fun N_i2d(str: String) = "(cast(double)($str))"

	override fun N_j2f(str: String) = "(cast(float)($str))"
	override fun N_j2d(str: String) = "(cast(double)($str))"

	override fun N_idiv(l: String, r: String): String = "N.idiv($l, $r)"
	override fun N_irem(l: String, r: String): String = "N.irem($l, $r)"

	override fun N_lnew(value: Long): String = when (value) {
		Long.MIN_VALUE -> "(cast(long)(0x8000000000000000LU))"
		else -> "(cast(long)(${value}L))"
	}

	override fun genMissingBody(method: AstMethod): Indenter = Indenter {
		val message = "Missing body ${method.containingClass.name}.${method.name}${method.desc}"
		line("throw new Throwable(${message.quote()});")
	}

	//override val MethodRef.targetNameBase: String get() = "${this.ref.name}${this.ref.desc}"
	//override val MethodRef.targetNameBase: String get() = "${this.ref.name}"

	override fun genStmRawTry(trap: AstTrap): Indenter = Indenter {
		//line("try {")
		//_indent()
	}

	override fun genStmRawCatch(trap: AstTrap): Indenter = Indenter {
		//_unindent()
		//line("} catch (Throwable e) {")
		//indent {
		//	line("goto ${trap.handler.name};")
		//}
		//line("}")
	}

	override fun genStmTryCatch(stm: AstStm.TRY_CATCH) = indent {
		line("try") {
			line(stm.trystm.genStm())
		}
		line("catch (WrappedThrowable J__i__exception__)") {
			line("J__exception__ = J__i__exception__.t;")
			line(stm.catch.genStm())
		}
	}

	override val DoubleNegativeInfinityString = "-double.infinity"
	override val DoublePositiveInfinityString = "double.infinity"
	override val DoubleNanString = "double.nan"

	override val String.escapeString: String get() = "STRINGLIT_${allocString(currentClass, this)}${this.toCommentString()}"

	override fun AstExpr.genNotNull(): String {
		if (debugVersion) {
			return "ensureNotNull(" + genExpr2(this) + ")"
		} else {
			return genExpr2(this)
		}
	}

	override fun escapedConstant(v: Any?, place: ConstantPlace): String = when (v) {
		is Double -> {
			//val isVerySmall = (v in 0.0..4.940656e-324)
			@Suppress("ConvertTwoComparisonsToRangeCheck")
			val isVerySmall = (v >= 0.0 && v <= 4.940656e-324)
			val representable = !isVerySmall
			if (representable) {
				super.escapedConstant(v, place)
			} else {
				"N.longBitsToDouble(cast(long)0x" + java.lang.Long.toHexString(java.lang.Double.doubleToRawLongBits(v)) + "UL)"
			}
			//"N.longBitsToDouble(" + java.lang.Double.doubleToRawLongBits(v) + "L)"
		}
		else -> super.escapedConstant(v, place)
	}

	override fun genStmMonitorEnter(stm: AstStm.MONITOR_ENTER) = indent {
		line("N.monitorEnter(" + stm.expr.genExpr() + ");")
	}

	override fun genStmMonitorExit(stm: AstStm.MONITOR_EXIT) = indent {
		line("N.monitorExit(" + stm.expr.genExpr() + ");")
	}

	override fun buildStaticInit(clazzName: FqName): String? = null

	//override fun N_AGET_T(arrayType: AstType.ARRAY, elementType: AstType, array: String, index: String) = "$array.data[$index]"
	//override fun N_ASET_T(arrayType: AstType.ARRAY, elementType: AstType, array: String, index: String, value: String): String = "$array.data[$index] = $value;"

	//override fun N_AGET_T(arrayType: AstType.ARRAY, elementType: AstType, array: String, index: String) = "$array.get($index)"
	//override fun N_ASET_T(arrayType: AstType.ARRAY, elementType: AstType, array: String, index: String, value: String): String = "$array.set($index, $value);"

	override fun N_AGET_T(arrayType: AstType.ARRAY, elementType: AstType, array: String, index: String) = "ARRAY_GET($array, $index)"
	override fun N_ASET_T(arrayType: AstType.ARRAY, elementType: AstType, array: String, index: String, value: String): String = "ARRAY_SET($array, $index, $value);"

	override fun genExprCaughtException(e: AstExpr.CAUGHT_EXCEPTION): String = "cast(${e.type.targetName})J__exception__"

	override fun genExprCastChecked(e: String, from: AstType.Reference, to: AstType.Reference): String {
		return "checkCast!(${to.targetNameRef})($e)"
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy