diff --git a/gradle.properties b/gradle.properties index 8f5ea202..f1e45273 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ jtranscVersion=0.6.7 -kotlinVersion=1.1.4 +kotlinVersion=1.1.4-2 file.encoding=UTF-8 kotlin.incremental=true org.gradle.daemon=true diff --git a/jtransc-core/src/com/jtransc/ast/ast.kt b/jtransc-core/src/com/jtransc/ast/ast.kt index abee7c0f..3ba934bf 100644 --- a/jtransc-core/src/com/jtransc/ast/ast.kt +++ b/jtransc-core/src/com/jtransc/ast/ast.kt @@ -340,12 +340,12 @@ class AstClass( val source: String, program: AstProgram, val name: FqName, - val modifiers: AstModifiers, + override val modifiers: AstModifiers, val extending: FqName? = null, val implementing: List = listOf(), annotations: List = listOf(), val classId: Int = program.lastClassId++ -) : AstAnnotatedElement(program, name.ref, annotations), IUserData by UserData() { +) : AstAnnotatedElement(program, name.ref, annotations), IUserData by UserData(), WithAstModifiersClass { val types get() = program.types val implementingUnique by lazy { implementing.distinct() } @@ -680,14 +680,14 @@ class AstField( val id: Int = containingClass.program.lastFieldId++, name: String, type: AstType, - val modifiers: AstModifiers, + override val modifiers: AstModifiers, val desc: String, annotations: List, val genericSignature: String?, val constantValue: Any? = null, val types: AstTypes, override val ref: AstFieldRef = AstFieldRef(containingClass.name, name, type) -) : AstMember(containingClass, name, type, if (genericSignature != null) types.demangle(genericSignature) else type, modifiers.isStatic, modifiers.visibility, ref, annotations), FieldRef { +) : AstMember(containingClass, name, type, if (genericSignature != null) types.demangle(genericSignature) else type, modifiers.isStatic, modifiers.visibility, ref, annotations), FieldRef, WithAstModifiersField { val uniqueName = containingClass.uniqueNames.alloc(name) val isFinal: Boolean = modifiers.isFinal val refWithoutClass: AstFieldWithoutClassRef by lazy { AstFieldWithoutClassRef(this.name, this.type) } @@ -711,7 +711,7 @@ class AstMethod constructor( val signature: String, val genericSignature: String?, val defaultTag: Any?, - val modifiers: AstModifiers, + override val modifiers: AstModifiers, var generateBody: () -> AstBody?, val bodyRef: AstMethodRef? = null, val parameterAnnotations: List> = listOf(), @@ -721,7 +721,7 @@ class AstMethod constructor( containingClass, name, methodType, if (genericSignature != null) containingClass.types.demangleMethod(genericSignature) else methodType, modifiers.isStatic, modifiers.visibility, ref, annotations -), MethodRef { +), MethodRef, WithAstModifiersMethod { val types: AstTypes get() = program.types val parameterAnnotationsList: List = parameterAnnotations.map { AstAnnotationList(ref, it) } @@ -736,8 +736,6 @@ class AstMethod constructor( } } - val isNative: Boolean = modifiers.isNative - private var generatedBody: Boolean = false private var generatedBodyBody: AstBody? = null @@ -845,8 +843,19 @@ val AstMethodRef.isInstanceInit: Boolean get() = name == "" val AstMethodRef.isClassInit: Boolean get() = name == "" val AstMethodRef.isClassOrInstanceInit: Boolean get() = isInstanceInit || isClassInit +interface WithAstModifiers { + val modifiers: AstModifiers +} + +interface WithAstModifiersMember : WithAstModifiers +interface WithAstModifiersMethod : WithAstModifiersMember +interface WithAstModifiersField : WithAstModifiersMember +interface WithAstModifiersParameter : WithAstModifiers +interface WithAstModifiersClass : WithAstModifiers + @Suppress("unused") -data class AstModifiers(val acc: Int) { +data class AstModifiers(val acc: Int) : WithAstModifiersMethod, WithAstModifiersField, WithAstModifiersClass, WithAstModifiersParameter { + override val modifiers = this companion object { fun withFlags(vararg flags: Int): AstModifiers { var out = 0 @@ -875,43 +884,6 @@ data class AstModifiers(val acc: Int) { const val ACC_MANDATED = 0x8000 // parameter } - val isPublic: Boolean get() = acc hasFlag ACC_PUBLIC - val isPrivate: Boolean get() = acc hasFlag ACC_PRIVATE - val isProtected: Boolean get() = acc hasFlag ACC_PROTECTED - val isStatic: Boolean get() = acc hasFlag ACC_STATIC - val isFinal: Boolean get() = acc hasFlag ACC_FINAL - val isSuper: Boolean get() = acc hasFlag ACC_SUPER - val isSynchronized: Boolean get() = acc hasFlag ACC_SYNCHRONIZED - val isVolatile: Boolean get() = acc hasFlag ACC_VOLATILE - val isBridge: Boolean get() = acc hasFlag ACC_BRIDGE - val isVarargs: Boolean get() = acc hasFlag ACC_VARARGS - val isTransient: Boolean get() = acc hasFlag ACC_TRANSIENT - val isNative: Boolean get() = acc hasFlag ACC_NATIVE - val isInterface: Boolean get() = acc hasFlag ACC_INTERFACE - val isAbstract: Boolean get() = acc hasFlag ACC_ABSTRACT - val isStrict: Boolean get() = acc hasFlag ACC_STRICT - val isSynthetic: Boolean get() = acc hasFlag ACC_SYNTHETIC - val isAnnotation: Boolean get() = acc hasFlag ACC_ANNOTATION - val isEnum: Boolean get() = acc hasFlag ACC_ENUM - val isMandated: Boolean get() = acc hasFlag ACC_MANDATED - val isConcrete: Boolean get() = !isNative && !isAbstract - - val visibility: AstVisibility get() = if (isPublic) { - AstVisibility.PUBLIC - } else if (isProtected) { - AstVisibility.PROTECTED - } else { - AstVisibility.PRIVATE - } - - val classType: AstClassType get() = if (isInterface) { - AstClassType.INTERFACE - } else if (isAbstract) { - AstClassType.ABSTRACT - } else { - AstClassType.CLASS - } - fun with(flags: Int) = AstModifiers(this.acc or flags) fun without(flags: Int) = AstModifiers(this.acc and flags.inv()) @@ -927,6 +899,38 @@ data class AstModifiers(val acc: Int) { override fun toString(): String = "$acc" } +val WithAstModifiers.isPublic: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_PUBLIC +val WithAstModifiers.isPrivate: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_PRIVATE +val WithAstModifiers.isProtected: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_PROTECTED +val WithAstModifiersMember.isStatic: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_STATIC +val WithAstModifiers.isFinal: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_FINAL +val WithAstModifiersMethod.isSynchronized: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_SYNCHRONIZED +val WithAstModifiersField.isVolatile: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_VOLATILE +val WithAstModifiersMethod.isBridge: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_BRIDGE +val WithAstModifiersMethod.isVarargs: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_VARARGS +val WithAstModifiersField.isTransient: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_TRANSIENT +val WithAstModifiersMethod.isNative: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_NATIVE +val WithAstModifiersMethod.isAbstract: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_ABSTRACT +val WithAstModifiersMethod.isStrict: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_STRICT +val WithAstModifiers.isSynthetic: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_SYNTHETIC +val WithAstModifiersParameter.isMandated: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_MANDATED +val WithAstModifiersMethod.isConcrete: Boolean get() = !isNative && !isAbstract +val WithAstModifiers.visibility: AstVisibility get() = when { + isPublic -> AstVisibility.PUBLIC + isProtected -> AstVisibility.PROTECTED + else -> AstVisibility.PRIVATE +} +val WithAstModifiersClass.isSuper: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_SUPER +val WithAstModifiersClass.isInterface: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_INTERFACE +val WithAstModifiersClass.isAbstract: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_ABSTRACT +val WithAstModifiersClass.isAnnotation: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_ANNOTATION +val WithAstModifiersClass.isEnum: Boolean get() = modifiers.acc hasFlag AstModifiers.ACC_ENUM +val WithAstModifiersClass.classType: AstClassType get() = when { + isInterface -> AstClassType.INTERFACE + isAbstract -> AstClassType.ABSTRACT + else -> AstClassType.CLASS +} + fun ARRAY(type: AstClass) = AstType.ARRAY(type.astType) fun getCommonTypePrim(a: AstType.Primitive, b: AstType.Primitive): AstType.Primitive { diff --git a/jtransc-core/src/com/jtransc/ast/ast_body.kt b/jtransc-core/src/com/jtransc/ast/ast_body.kt index b6e1d988..9425f02e 100644 --- a/jtransc-core/src/com/jtransc/ast/ast_body.kt +++ b/jtransc-core/src/com/jtransc/ast/ast_body.kt @@ -721,7 +721,7 @@ object AstExprUtils { val clazz = program.get3(e.method.classRef) val refMethod = program.get(e.method) ?: invalidOp("Can't find method: ${e.method} while generating $context") // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokespecial - return if (refMethod.modifiers.isPrivate || refMethod.isInstanceInit) { + return if (refMethod.isPrivate || refMethod.isInstanceInit) { // Call this! AstExpr.CALL_INSTANCE(e.obj.value, e.method, e.args.map { it.value }, e.isSpecial) } else { diff --git a/jtransc-core/src/com/jtransc/ast/template/CommonTagHandler.kt b/jtransc-core/src/com/jtransc/ast/template/CommonTagHandler.kt index 80ccfe9b..e9006fb1 100644 --- a/jtransc-core/src/com/jtransc/ast/template/CommonTagHandler.kt +++ b/jtransc-core/src/com/jtransc/ast/template/CommonTagHandler.kt @@ -42,7 +42,7 @@ object CommonTagHandler { val desc2 = dataParts.joinToString(":") val classFqname = resolveClassName(dataParts[0], params) if (!program.contains(classFqname)) { - invalidOp("evalReference: Can't find class '$classFqname' (I)") + invalidOp("evalReference: Can't find class '$classFqname' (I) : parts : $dataParts") } val clazz = program[classFqname] val types = program.types diff --git a/jtransc-core/src/com/jtransc/ast/treeshaking/TemplateReferences.kt b/jtransc-core/src/com/jtransc/ast/treeshaking/TemplateReferences.kt index 26fe046b..841a1992 100644 --- a/jtransc-core/src/com/jtransc/ast/treeshaking/TemplateReferences.kt +++ b/jtransc-core/src/com/jtransc/ast/treeshaking/TemplateReferences.kt @@ -27,7 +27,6 @@ fun GetClassTemplateReferences(program: AstProgram, templateStr: String, current return _GetTemplateReferences(program, templateStr, currentClass, classes = true, config = config).map { (it as CommonTagHandler.CLASS_REF).clazz } } - fun _GetTemplateReferences(program: AstProgram, templateStr: String, currentClass: FqName, classes: Boolean, config: TRefConfig): List { val refs = arrayListOf() val params: HashMap = hashMapOf("CLASS" to currentClass.fqname) diff --git a/jtransc-core/src/com/jtransc/gen/common/CommonGenerator.kt b/jtransc-core/src/com/jtransc/gen/common/CommonGenerator.kt index eafe7c80..b0785ea5 100644 --- a/jtransc-core/src/com/jtransc/gen/common/CommonGenerator.kt +++ b/jtransc-core/src/com/jtransc/gen/common/CommonGenerator.kt @@ -739,7 +739,24 @@ abstract class CommonGenerator(val injector: Injector) : IProgramTemplate { fun AstBody.genBody(): Indenter = genBody2(this) fun AstBody.genBodyWithFeatures(method: AstMethod): Indenter = genBody2WithFeatures(method, this) - open fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter { + open fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter = Indenter { + if (method.isSynchronized) { + lineMonitorEnter() + line("try {") + indent { + line(genBody2WithFeatures2(method, body)) + } + line("} finally{") + indent { + lineMonitorExit() + } + line("}") + } else { + line(genBody2WithFeatures2(method, body)) + } + } + + open fun genBody2WithFeatures2(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) @@ -1285,11 +1302,9 @@ abstract class CommonGenerator(val injector: Injector) : IProgramTemplate { } } - open fun getMonitorLockedObjectExpr(method: AstMethod): AstExpr { - if (method.isStatic) - return (AstExpr.LITERAL(method.containingClass.astType, dummy = true)) - else - return (AstExpr.THIS(method.containingClass.name)) + open fun getMonitorLockedObjectExpr(method: AstMethod): AstExpr = when { + method.isStatic -> AstExpr.LITERAL(method.containingClass.astType, dummy = true) + else -> AstExpr.THIS(method.containingClass.name) } open fun genStmMonitorEnter(stm: AstStm.MONITOR_ENTER) = indent { line("// MONITOR_ENTER") } @@ -1980,9 +1995,10 @@ abstract class CommonGenerator(val injector: Injector) : IProgramTemplate { ////////////////////////////////////////////////// open val AstType.nativeDefault: Any? get() = this.getNull() - open val AstType.nativeDefaultString: String get() { - return this.nativeDefault.escapedConstant - } + open val AstType.nativeDefaultString: String + get() { + return this.nativeDefault.escapedConstant + } fun Any?.escapedConstantOfType(type: AstType): String { if (type == AstType.BOOL) { @@ -2101,23 +2117,25 @@ abstract class CommonGenerator(val injector: Injector) : IProgramTemplate { 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!! + open val FqName.targetNameForStatic: String + get() { + val clazz = program[this] + return when { + (clazz.nativeName != null) -> { + imports += FqName(clazz.nativeName!!) + clazz.nativeName!! + } + else -> this.targetNameForStaticNonNative } - 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.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 @@ -2130,33 +2148,34 @@ abstract class CommonGenerator(val injector: Injector) : IProgramTemplate { 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 + 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(this@CommonGenerator.targetName) ?: method.name - } else { - methodNames.getOrPut2(method.objectToCache) { - if (minimize && !realmethod.keepName) { - allocMemberName() - } else { - if (realmethod.nativeMethod != null) { - realmethod.nativeMethod!! + return if (realclass.isNative) { + // No cache + realmethod.nativeNameForTarget(this@CommonGenerator.targetName) ?: method.name + } else { + methodNames.getOrPut2(method.objectToCache) { + if (minimize && !realmethod.keepName) { + allocMemberName() } else { - val name2 = method.targetNameBase - val name = when (method.name) { - "", "" -> "${method.containingClass}$name2" - else -> name2 + if (realmethod.nativeMethod != null) { + realmethod.nativeMethod!! + } else { + val name2 = method.targetNameBase + val name = when (method.name) { + "", "" -> "${method.containingClass}$name2" + else -> name2 + } + cleanMethodName(name) } - cleanMethodName(name) } } } } - } open val MethodRef.targetNameBase: String get() = "${this.ref.name}${this.ref.desc}" //open val MethodRef.targetNameBase: String get() = "${this.ref.name}" @@ -2226,59 +2245,60 @@ abstract class CommonGenerator(val injector: Injector) : IProgramTemplate { 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 + 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) + val normalizedFieldName = cleanFieldName(field.name) - //if (normalizedFieldName == "_parameters" || normalizedFieldName == "__parameters") { - // println("_parameters") - //} + //if (normalizedFieldName == "_parameters" || normalizedFieldName == "__parameters") { + // println("_parameters") + //} - return if (realclass.isNative) { - realfield.nativeNameForTarget(this@CommonGenerator.targetName) ?: 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 + return if (realclass.isNative) { + realfield.nativeNameForTarget(this@CommonGenerator.targetName) ?: 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") } - cachedFieldNames[field] ?: unexpected("Unexpected. Not cached: $field") } } } - } //override val FieldRef.targetName: String get() { // val fieldRef = this @@ -2371,6 +2391,16 @@ abstract class CommonGenerator(val injector: Injector) : IProgramTemplate { program["java.lang.Throwable".fqname].getMethods("prepareThrow").first() } + fun Indenter.lineMonitorEnter() = line(genStmMonitorEnter(AstStm.MONITOR_ENTER(getMonitorLockedObjectExpr(context.method)))) + fun Indenter.lineMonitorExit() = line(genStmMonitorExit(AstStm.MONITOR_EXIT(getMonitorLockedObjectExpr(context.method)))) + + fun Indenter.lineMonitorEnterIfRequired() { + if (context.method.isSynchronized) lineMonitorEnter() + } + + fun Indenter.lineMonitorExitIfRequired() { + if (context.method.isSynchronized) lineMonitorExit() + } //open fun getActualFqName(name: FqName): FqName { // /* diff --git a/jtransc-core/src/com/jtransc/plugin/enum/EnumJTranscPlugin.kt b/jtransc-core/src/com/jtransc/plugin/enum/EnumJTranscPlugin.kt index b39aa522..a7218e8f 100644 --- a/jtransc-core/src/com/jtransc/plugin/enum/EnumJTranscPlugin.kt +++ b/jtransc-core/src/com/jtransc/plugin/enum/EnumJTranscPlugin.kt @@ -9,7 +9,7 @@ import com.jtransc.plugin.JTranscPlugin */ class EnumJTranscPlugin : JTranscPlugin() { override fun onTreeShakingAddBasicClass(treeShaking: TreeShakingApi, fqname: FqName, oldclass: AstClass, newclass: AstClass) { - if (oldclass.modifiers.isEnum && oldclass.extending == java.lang.Enum::class.java.fqname) { + if (oldclass.isEnum && oldclass.extending == java.lang.Enum::class.java.fqname) { treeShaking.addMethod(AstMethodRef(fqname, "values", AstType.METHOD(ARRAY(oldclass.astType), listOf())), "EnumJTranscPlugin") } } diff --git a/jtransc-gen-as3/src/com/jtransc/gen/as3/As3Target.kt b/jtransc-gen-as3/src/com/jtransc/gen/as3/As3Target.kt index f397370f..8fc3a8cd 100644 --- a/jtransc-gen-as3/src/com/jtransc/gen/as3/As3Target.kt +++ b/jtransc-gen-as3/src/com/jtransc/gen/as3/As3Target.kt @@ -14,7 +14,6 @@ import com.jtransc.injector.Singleton import com.jtransc.io.ProcessResult2 import com.jtransc.lang.map import com.jtransc.text.Indenter -import com.jtransc.text.Indenter.Companion import com.jtransc.text.quote import com.jtransc.vfs.* import java.io.File @@ -267,19 +266,6 @@ class As3Generator(injector: Injector) : CommonGenerator(injector) { } } - override fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter = Indenter { - if(method.modifiers.isSynchronized) { - line("try{") - line(genStmMonitorEnter(AstStm.MONITOR_ENTER(getMonitorLockedObjectExpr(method)))) - } - line(super.genBody2WithFeatures(method, body)) - if(method.modifiers.isSynchronized) { - line("}finally{") - line(genStmMonitorExit(AstStm.MONITOR_EXIT(getMonitorLockedObjectExpr(method)))) - line("}") - } - } - override fun N_i2b(str: String) = "avm2.intrinsics.memory.sxi8($str)" override fun N_i2c(str: String) = "(($str)&0xFFFF)" override fun N_i2s(str: String) = "avm2.intrinsics.memory.sxi16($str)" @@ -288,9 +274,9 @@ class As3Generator(injector: Injector) : CommonGenerator(injector) { // avm2.intrinsics.memory.sxi8 // avm2.intrinsics.memory.sxi16 return when (elementType) { - //AstType.BYTE -> "((($array.data[$index])<<24)>>24)" - //AstType.CHAR -> "((($array.data[$index]))&0xFFFF)" - //AstType.SHORT -> "((($array.data[$index])<<16)>>16)" + //AstType.BYTE -> "((($array.data[$index])<<24)>>24)" + //AstType.CHAR -> "((($array.data[$index]))&0xFFFF)" + //AstType.SHORT -> "((($array.data[$index])<<16)>>16)" AstType.BYTE -> N_i2b("$array.data[$index]") AstType.CHAR -> N_i2c("$array.data[$index]") AstType.SHORT -> N_i2s("$array.data[$index]") @@ -298,6 +284,7 @@ class As3Generator(injector: Injector) : CommonGenerator(injector) { } //return "$array.get($index)" } + override fun N_ASET_T(arrayType: AstType.ARRAY, elementType: AstType, array: String, index: String, value: String): String { return "$array.data[$index] = $value;" } diff --git a/jtransc-gen-common-tests/src/big/ThreadTest.java b/jtransc-gen-common-tests/src/big/ThreadTest.java new file mode 100644 index 00000000..53e8980e --- /dev/null +++ b/jtransc-gen-common-tests/src/big/ThreadTest.java @@ -0,0 +1,106 @@ +package big; + +import com.jtransc.io.JTranscConsole; + +@SuppressWarnings("ConstantConditions") +public class ThreadTest { + static public void main(String[] args) { + System.out.println("ThreadTest.main:"); + interlock(); + synchronizedBlock(); + synchronizedMethod(); + } + + synchronized static private void a() { + b(); + } + + synchronized static private void b() { + } + + synchronized private void c() { + synchronized (this) { + synchronized (this) { + d(); + } + } + } + + synchronized private void d() { + synchronized (this) { + System.out.println("not interlocked!"); + System.out.flush(); + } + } + + static private void interlock() { + a(); + new ThreadTest().c(); + } + + static private void synchronizedBlock() { + JTranscConsole.log("ThreadTest.synchronizedBlock:"); + JTranscConsole.log(Thread.currentThread() != null); + try { + final SynchronizedTraits sync = new SynchronizedTraits(); + JTranscConsole.log("START1"); + Thread t1 = new Thread(() -> sync.synchronizedBlock()); + Thread t2 = new Thread(() -> sync.synchronizedBlock()); + System.out.println("START2"); + long start = System.currentTimeMillis(); + t1.start(); + t2.start(); + long end = System.currentTimeMillis(); + t1.join(); + t2.join(); + System.out.println("Not waited: " + ((end - start) < 500)); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + static private void synchronizedMethod() { + JTranscConsole.log("ThreadTest.synchronizedMethod:"); + JTranscConsole.log(Thread.currentThread() != null); + try { + final SynchronizedTraits sync = new SynchronizedTraits(); + JTranscConsole.log("START1"); + Thread t1 = new Thread(() -> sync.synchronizedMethod()); + Thread t2 = new Thread(() -> sync.synchronizedMethod()); + System.out.println("START2"); + long start = System.currentTimeMillis(); + t1.start(); + t2.start(); + long end = System.currentTimeMillis(); + t1.join(); + t2.join(); + System.out.println("Not waited: " + ((end - start) < 500)); + } catch (Throwable e) { + e.printStackTrace(); + } + } +} + +class SynchronizedTraits { + void synchronizedBlock() { + synchronized (this) { + System.out.println("BEGIN"); + try { + Thread.sleep(500L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("END"); + } + } + + synchronized void synchronizedMethod() { + System.out.println("BEGIN"); + try { + Thread.sleep(500L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("END"); + } +} diff --git a/jtransc-gen-cpp/src/com/jtransc/gen/cpp/CppTarget.kt b/jtransc-gen-cpp/src/com/jtransc/gen/cpp/CppTarget.kt index ff5bdf02..4b6ab659 100644 --- a/jtransc-gen-cpp/src/com/jtransc/gen/cpp/CppTarget.kt +++ b/jtransc-gen-cpp/src/com/jtransc/gen/cpp/CppTarget.kt @@ -477,7 +477,9 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { indent { for (memberCond in clazz.nativeMembers) { condWrapper(memberCond.cond) { - lines(memberCond.members) + for (member in memberCond.members) { + line(member.replace("###", "").template("native members")) + } } } @@ -583,6 +585,16 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { } } + for (memberCond in clazz.nativeMembers) { + condWrapper(memberCond.cond) { + for (member in memberCond.members) { + if (member.startsWith("static ")) { + line(member.replace("###", "${clazz.cppName}::").replace("static ", "").template("native members 2")) + } + } + } + } + line("void ${clazz.cppName}::SI() {") indent { line("""TRACE_REGISTER("${clazz.cppName}::SI");""") @@ -866,24 +878,25 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { fun genExprThis(): String = "this" //->sptr()" override fun genExprMethodClass(e: AstExpr.INVOKE_DYNAMIC_METHOD): String = "N::dummyMethodClass()" - override val AstType.targetNameRef: String get() { - if (ENABLE_TYPING) { - return getTypeTargetName(this, ref = true) - } else { - if (this is AstType.Reference) { - if (this is AstType.REF) { - val clazz = program[this]!! - val nativeName = clazz.nativeName - if (nativeName != null) { - return nativeName + override val AstType.targetNameRef: String + get() { + if (ENABLE_TYPING) { + return getTypeTargetName(this, ref = true) + } else { + if (this is AstType.Reference) { + if (this is AstType.REF) { + val clazz = program[this]!! + val nativeName = clazz.nativeName + if (nativeName != null) { + return nativeName + } } + return "p_java_lang_Object" + } else { + return getTypeTargetName(this, ref = true) } - return "p_java_lang_Object" - } else { - return getTypeTargetName(this, ref = true) } } - } val AstType.targetNameRefCast: String get() = getTypeTargetName(this, ref = true) @@ -897,10 +910,10 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { //} override fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter = Indenter { - if(method.modifiers.isSynchronized) { + if (method.isSynchronized) { line("SynchronizedMethodLocker __locker(" + getMonitorLockedObjectExpr(method).genExpr() + ");") } - line(super.genBody2WithFeatures(method, body)) + line(genBody2WithFeatures2(method, body)) } override fun N_i2b(str: String) = "((int8_t)($str))" @@ -1116,24 +1129,25 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { override val FieldRef.targetName: String get() = getNativeName(this) - override val MethodRef.targetNameBase: String get() { - val method = this - return getClassNameAllocator(method.ref.containingClass).allocate(method.ref) { - val astMethod = program[method.ref]!! - val containingClass = astMethod.containingClass - val prefix = if (containingClass.isInterface) "I_" else if (astMethod.isStatic) "S_" else "M_" - val prefix2 = if (containingClass.isInterface || method.ref.isClassOrInstanceInit) { - //getClassFqNameForCalling(containingClass.name) + "_" - containingClass.name.fqname + "_" - } else { - "" - } - val suffix = "_${astMethod.name}${astMethod.desc}" - //"$prefix$prefix2" + super.getNativeName(method) + "$suffix" + override val MethodRef.targetNameBase: String + get() { + val method = this + return getClassNameAllocator(method.ref.containingClass).allocate(method.ref) { + val astMethod = program[method.ref]!! + val containingClass = astMethod.containingClass + val prefix = if (containingClass.isInterface) "I_" else if (astMethod.isStatic) "S_" else "M_" + val prefix2 = if (containingClass.isInterface || method.ref.isClassOrInstanceInit) { + //getClassFqNameForCalling(containingClass.name) + "_" + containingClass.name.fqname + "_" + } else { + "" + } + val suffix = "_${astMethod.name}${astMethod.desc}" + //"$prefix$prefix2" + super.getNativeName(method) + "$suffix" - "$prefix$prefix2$suffix" + "$prefix$prefix2$suffix" + } } - } fun getNativeName(field: FieldRef): String { val clazz = field.ref.getClass(program) @@ -1168,13 +1182,6 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { //return e } - 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 genStmMonitorEnter(stm: AstStm.MONITOR_ENTER) = Indenter("N::monitorEnter(" + stm.expr.genExpr() + ");") + override fun genStmMonitorExit(stm: AstStm.MONITOR_EXIT) = Indenter("N::monitorExit(" + stm.expr.genExpr() + ");") } diff --git a/jtransc-gen-cpp/test/CppTest.kt b/jtransc-gen-cpp/test/CppTest.kt index e31e4aca..d5c1291e 100644 --- a/jtransc-gen-cpp/test/CppTest.kt +++ b/jtransc-gen-cpp/test/CppTest.kt @@ -16,6 +16,7 @@ import big.BigTest import big.HelloWorldTest +import big.ThreadTest import com.jtransc.gen.common._Base import com.jtransc.gen.cpp.CppTarget import issues.issue130.Issue130 @@ -38,6 +39,9 @@ class CppTest : _Base() { @Test fun testBigTest() = testClass(Params(clazz = BigTest::class.java, minimize = false, log = false, treeShaking = true, debug = true)) // debug=true makes builds much faster + @Ignore("Failing for now") + @Test fun testThread() = testClass(Params(clazz = ThreadTest::class.java, minimize = false, log = false, treeShaking = true, debug = true)) // debug=true makes builds much faster + @Ignore @Test fun testBigTestRelease() = testClass(Params(clazz = BigTest::class.java, minimize = false, log = false, treeShaking = true, debug = false)) diff --git a/jtransc-gen-cs/src/com/jtransc/gen/cs/CSharpTarget.kt b/jtransc-gen-cs/src/com/jtransc/gen/cs/CSharpTarget.kt index 8862fd7e..33ce8957 100644 --- a/jtransc-gen-cs/src/com/jtransc/gen/cs/CSharpTarget.kt +++ b/jtransc-gen-cs/src/com/jtransc/gen/cs/CSharpTarget.kt @@ -203,7 +203,7 @@ class CSharpGenerator(injector: Injector) : CommonGenerator(injector) { '\t' -> out.append("\\t") //in '\u0000'..'\u001f' -> out.append("\\x" + "%02x".format(c.toInt())) //in '\u0020'..'\u00ff' -> out.append(c) - in 'a' .. 'z', in 'A' .. 'Z', in '0' .. '9', '_', '.', ',', ';', ':', '<', '>', '{', '}', '[', ']', '/', ' ', '=', '!', '%', '$', '&' -> out.append(c) + in 'a'..'z', in 'A'..'Z', in '0'..'9', '_', '.', ',', ';', ':', '<', '>', '{', '}', '[', ']', '/', ' ', '=', '!', '%', '$', '&' -> out.append(c) else -> out.append("\\u" + "%04x".format(c.toInt())) } } @@ -297,9 +297,9 @@ class CSharpGenerator(injector: Injector) : CommonGenerator(injector) { line("static public void SI()") { val clazzName = if (clazz.isInterface) clazz.name.targetNameForStatic else clazz.name.targetName for (field in clazz.fields.filter { it.isStatic }) { - if(field.type==AstType.BOOL){ + if (field.type == AstType.BOOL) { line("$clazzName.${field.targetName} = ${field.escapedConstantValue == "1"};") - }else { + } else { line("$clazzName.${field.targetName} = ${field.escapedConstantValue};") } } @@ -310,18 +310,9 @@ class CSharpGenerator(injector: Injector) : CommonGenerator(injector) { } } - override fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter = Indenter { - if(method.modifiers.isSynchronized) { - line("try{") - line(genStmMonitorEnter(AstStm.MONITOR_ENTER(getMonitorLockedObjectExpr(method)))) - } + override fun genBody2WithFeatures2(method: AstMethod, body: AstBody): Indenter = Indenter { line("unchecked") { - line(super.genBody2WithFeatures(method, body)) - } - if(method.modifiers.isSynchronized) { - line("}finally{") - line(genStmMonitorExit(AstStm.MONITOR_EXIT(getMonitorLockedObjectExpr(method)))) - line("}") + line(super.genBody2WithFeatures2(method, body)) } } diff --git a/jtransc-gen-cs/test/CSharpTest.kt b/jtransc-gen-cs/test/CSharpTest.kt index 62a0279f..db5df810 100644 --- a/jtransc-gen-cs/test/CSharpTest.kt +++ b/jtransc-gen-cs/test/CSharpTest.kt @@ -1,7 +1,4 @@ -import big.AsyncIOTest -import big.BigIOTest -import big.BigTest -import big.HelloWorldTest +import big.* import com.jtransc.BuildBackend import com.jtransc.gen.common._Base import com.jtransc.gen.cs.CSharpTarget @@ -50,6 +47,9 @@ class CSharpTest : _Base() { @Ignore("Disabled until it works on travis") @Test fun testBig() = testClass(Params(clazz = BigTest::class.java, minimize = false, debug = false, log = false)) + @Ignore("Disabled until it works on travis") + @Test fun testThread() = testClass(Params(clazz = ThreadTest::class.java, minimize = false, debug = false, log = false)) + @Ignore("Already included in BigTest") @Test fun testDescentIssue130() = testClass(Params(clazz = Issue130::class.java, minimize = false, log = false, treeShaking = true)) diff --git a/jtransc-gen-d/src/com/jtransc/gen/d/DTarget.kt b/jtransc-gen-d/src/com/jtransc/gen/d/DTarget.kt index a1df771d..5e5b8f8b 100644 --- a/jtransc-gen-d/src/com/jtransc/gen/d/DTarget.kt +++ b/jtransc-gen-d/src/com/jtransc/gen/d/DTarget.kt @@ -14,7 +14,6 @@ import com.jtransc.injector.Injector import com.jtransc.injector.Singleton import com.jtransc.io.ProcessResult2 import com.jtransc.text.Indenter -import com.jtransc.text.Indenter.Companion import com.jtransc.text.escape import com.jtransc.text.quote import com.jtransc.vfs.* @@ -178,7 +177,7 @@ class DGenerator(injector: Injector) : CommonGenerator(injector) { // //return "pragma(inline, true)" + super.genMethodDeclModifiers(method) // return "pragma(inline) final " + super.genMethodDeclModifiers(method) //} else { - return super.genMethodDeclModifiers(method) + return super.genMethodDeclModifiers(method) //} } @@ -211,7 +210,7 @@ class DGenerator(injector: Injector) : CommonGenerator(injector) { override val FqName.targetSimpleName: String get() = this.targetName - override fun N_c(str: String, from: AstType, to: AstType):String { + 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 @@ -342,6 +341,7 @@ class DGenerator(injector: Injector) : CommonGenerator(injector) { 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() + ");") } @@ -356,17 +356,4 @@ class DGenerator(injector: Injector) : CommonGenerator(injector) { override fun genExprCastChecked(e: String, from: AstType.Reference, to: AstType.Reference): String { return "checkCast!(${to.targetNameRef})($e)" } - - override fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter = Indenter { - if(method.modifiers.isSynchronized) { - line("try{") - line(genStmMonitorEnter(AstStm.MONITOR_ENTER(getMonitorLockedObjectExpr(method)))) - } - line(super.genBody2WithFeatures(method, body)) - if(method.modifiers.isSynchronized) { - line("}finally{") - line(genStmMonitorExit(AstStm.MONITOR_EXIT(getMonitorLockedObjectExpr(method)))) - line("}") - } - } } \ No newline at end of file diff --git a/jtransc-gen-d/test/DTest.kt b/jtransc-gen-d/test/DTest.kt index 9fa342dd..bcca2c62 100644 --- a/jtransc-gen-d/test/DTest.kt +++ b/jtransc-gen-d/test/DTest.kt @@ -1,5 +1,6 @@ import big.BigTest import big.HelloWorldTest +import big.ThreadTest import com.jtransc.BuildBackend import com.jtransc.gen.common._Base import com.jtransc.gen.d.DTarget @@ -47,6 +48,8 @@ class DTest : _Base() { @Test fun testBig() = testClass(Params(clazz = BigTest::class.java, minimize = false, log = false, debug = true)) + @Test fun testThread() = testClass(Params(clazz = ThreadTest::class.java, minimize = false, log = false, debug = true)) + @Ignore @Test fun testJTranscBug127() = testClass(Params(clazz = JTranscBug127::class.java, minimize = false, log = false, debug = true)) diff --git a/jtransc-gen-dart/src/com.jtransc.gen.dart/DartTarget.kt b/jtransc-gen-dart/src/com.jtransc.gen.dart/DartTarget.kt index 910b3441..399b8ad8 100644 --- a/jtransc-gen-dart/src/com.jtransc.gen.dart/DartTarget.kt +++ b/jtransc-gen-dart/src/com.jtransc.gen.dart/DartTarget.kt @@ -11,7 +11,6 @@ import com.jtransc.injector.Injector import com.jtransc.injector.Singleton import com.jtransc.io.ProcessResult2 import com.jtransc.text.Indenter -import com.jtransc.text.Indenter.Companion import com.jtransc.vfs.* import java.io.File @@ -181,7 +180,7 @@ class DartGenerator(injector: Injector) : CommonGenerator(injector) { '$' -> out.append("\\$") //in '\u0000'..'\u001f' -> out.append("\\x" + "%02x".format(c.toInt())) //in '\u0020'..'\u00ff' -> out.append(c) - in 'a' .. 'z', in 'A' .. 'Z', in '0' .. '9', '_', '.', ',', ';', ':', '<', '>', '{', '}', '[', ']', '/', ' ', '=', '!', '%', '&' -> out.append(c) + in 'a'..'z', in 'A'..'Z', in '0'..'9', '_', '.', ',', ';', ':', '<', '>', '{', '}', '[', ']', '/', ' ', '=', '!', '%', '&' -> out.append(c) else -> out.append("\\u" + "%04x".format(c.toInt())) } } @@ -241,6 +240,7 @@ class DartGenerator(injector: Injector) : CommonGenerator(injector) { //override fun genExprArrayLength(e: AstExpr.ARRAY_LENGTH): String = "(($BaseArrayType)${e.array.genNotNull()}).length" override fun genExprArrayLength(e: AstExpr.ARRAY_LENGTH): String = "(${e.array.genNotNull()} as JA_0).length" + //override fun genStmThrow(stm: AstStm.THROW, last: Boolean) = Indenter("throw new WrappedThrowable(${stm.value.genExpr()});") override fun genStmThrow(stm: AstStm.THROW, last: Boolean) = Indenter("throw (${stm.exception.genExpr()}).${prepareThrow.targetName}().dartError;") @@ -276,6 +276,7 @@ class DartGenerator(injector: Injector) : CommonGenerator(injector) { //override fun N_i(str: String) = "N.i($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)" @@ -456,17 +457,4 @@ class DartGenerator(injector: Injector) : CommonGenerator(injector) { //return "N.CHECK_CAST($e, ${to.targetNameRef})" return "(($e) as ${to.targetNameRef})" } - - override fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter = Indenter { - if(method.modifiers.isSynchronized) { - line("try{") - line(genStmMonitorEnter(AstStm.MONITOR_ENTER(getMonitorLockedObjectExpr(method)))) - } - line(super.genBody2WithFeatures(method, body)) - if(method.modifiers.isSynchronized) { - line("}finally{") - line(genStmMonitorExit(AstStm.MONITOR_EXIT(getMonitorLockedObjectExpr(method)))) - line("}") - } - } } \ No newline at end of file diff --git a/jtransc-gen-haxe/src/com/jtransc/gen/haxe/HaxeTarget.kt b/jtransc-gen-haxe/src/com/jtransc/gen/haxe/HaxeTarget.kt index 8362b0fe..c5e9ddc9 100644 --- a/jtransc-gen-haxe/src/com/jtransc/gen/haxe/HaxeTarget.kt +++ b/jtransc-gen-haxe/src/com/jtransc/gen/haxe/HaxeTarget.kt @@ -152,19 +152,37 @@ class HaxeGenerator(injector: Injector) : CommonGenerator(injector) { override fun genLabel(label: AstLabel): String = "untyped __cpp__('${label.name}:;');" + val __out_var = "__out_var" + override fun genStmReturnVoid(stm: AstStm.RETURN_VOID, last: Boolean): Indenter { - val res = super.genStmReturnVoid(stm, last) + val res = Indenter { + lineMonitorExitIfRequired() + if (context.method.methodVoidReturnThis) { + line("return this;") + } else { + line("return;") + } + } if (usingGotoHack && !last) { - return Indenter("if (untyped __cpp__('true')) " + res.toString()) + return Indenter("if (untyped __cpp__('true')) { " + res.toString() + " }") } else { return res } } override fun genStmReturnValue(stm: AstStm.RETURN, last: Boolean): Indenter { - val res = super.genStmReturnValue(stm, last) + val res = Indenter { + if (context.method.isSynchronized) { + line("$__out_var = ${stm.retval.genExpr()};") + lineMonitorExit() + line("return $__out_var;") + } else { + line("return ${stm.retval.genExpr()};") + } + } + if (usingGotoHack && !last) { - return Indenter("if (untyped __cpp__('true')) " + res.toString()) + return Indenter("if (untyped __cpp__('true')) { " + res.toString() + " }") } else { return res } @@ -221,7 +239,7 @@ class HaxeGenerator(injector: Injector) : CommonGenerator(injector) { //override val tempdir = configTargetDirectory.targetDirectory val mergedAssetsFolder = haxeConfigMergedAssetsFolder?.mergedAssetsFolder val mergedAssetsVfs by lazy { LocalVfs(mergedAssetsFolder!!) } - //override val outputFile2 = configOutputFile2.file +//override val outputFile2 = configOutputFile2.file override fun writeProgramAndFiles() { _write() @@ -242,13 +260,13 @@ class HaxeGenerator(injector: Injector) : CommonGenerator(injector) { } } - //override fun genStmSwitch(stm: AstStm.SWITCH): Indenter = if (stm.cases.size > MAX_SWITCH_SIZE) { - // this.genStm2(stm.reduceSwitch(maxChunkSize = 10)) - //} else { - // super.genStmSwitch(stm) - //} +//override fun genStmSwitch(stm: AstStm.SWITCH): Indenter = if (stm.cases.size > MAX_SWITCH_SIZE) { +// this.genStm2(stm.reduceSwitch(maxChunkSize = 10)) +//} else { +// super.genStmSwitch(stm) +//} - //val BUILD_COMMAND = listOf("haxelib", "run", "lime", "@@SWITCHES", "build", "@@SUBTARGET") +//val BUILD_COMMAND = listOf("haxelib", "run", "lime", "@@SWITCHES", "build", "@@SUBTARGET") override fun compileAndRun(redirect: Boolean): ProcessResult2 { return _compileRun(run = true, redirect = redirect) @@ -497,7 +515,7 @@ class HaxeGenerator(injector: Injector) : CommonGenerator(injector) { } //"Std.is(${e.expr.genExpr()}, ${e.checkType.targetTypeCast})" - //override fun N_is(a: String, b: String) = "N.is($a, $b)" +//override fun N_is(a: String, b: String) = "N.is($a, $b)" override fun N_is(a: String, b: String) = "Std.is($a, $b)" override fun N_z2i(str: String) = "N.z2i($str)" @@ -660,7 +678,10 @@ class HaxeGenerator(injector: Injector) : CommonGenerator(injector) { line(method.getHaxeNativeBody { rbody?.genBodyWithFeatures(method) ?: Indenter("throw '${str}';") }) - if (method.methodVoidReturnThis) line("return this;") + if (method.methodVoidReturnThis) { + lineMonitorExitIfRequired() + line("return this;") + } } catch (e: Throwable) { //e.printStackTrace() log.warn("WARNING haxe_gen.writeMethod:" + e.message) @@ -778,12 +799,13 @@ class HaxeGenerator(injector: Injector) : CommonGenerator(injector) { override fun buildStaticInit(clazzName: FqName) = null - override val FqName.targetNameForStatic: String get() { - val clazz = program[this] - val simpleName = this.targetSimpleName - val suffix = if (clazz.isInterface) ".${simpleName}_IFields" else "" - return clazz.name.targetClassFqName + suffix - } + override val FqName.targetNameForStatic: String + get() { + val clazz = program[this] + val simpleName = this.targetSimpleName + val suffix = if (clazz.isInterface) ".${simpleName}_IFields" else "" + return clazz.name.targetClassFqName + suffix + } override fun buildTemplateClass(clazz: FqName): String = clazz.targetClassFqName @@ -837,7 +859,7 @@ class HaxeGenerator(injector: Injector) : CommonGenerator(injector) { override val DoublePositiveInfinityString = "Math.POSITIVE_INFINITY" override val DoubleNanString = "Math.NaN" - /////////////////////////// +/////////////////////////// //val actualSubtarget = configActualSubtarget.subtarget override val srcFolder = HaxeGenTools.getSrcFolder(tempdir) @@ -900,7 +922,7 @@ class HaxeGenerator(injector: Injector) : CommonGenerator(injector) { params["HAXE_METHOD_ANNOTATIONS"] = METHOD_ANNOTATIONS } - //override val AstMethod.targetIsOverriding: Boolean get() = this.isOverriding && !this.isInstanceInit +//override val AstMethod.targetIsOverriding: Boolean get() = this.isOverriding && !this.isInstanceInit override val AstType.localDeclType: String get() = "var" @@ -926,25 +948,33 @@ class HaxeGenerator(injector: Injector) : CommonGenerator(injector) { override fun genStmSetArrayLiterals(stm: AstStm.SET_ARRAY_LITERALS) = Indenter { line("${stm.array.genExpr()}.setArraySlice(${stm.startIndex}, [${stm.values.map { it.genExpr() }.joinToString(", ")}]);") } -/* - // Not working: Compile Error: Cannot access super inside a local function - // Since MONITOR_ENTER/EXIT is not implemented on haxe, this is disabled - // @TODO check this when implementing MONITOR_ENTER/EXIT + override fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter = Indenter { - if(method.modifiers.isSynchronized) { - if(method.methodType.retVoid) - line("N.tryFinallyVoid(function(){") - else line("return N.tryFinallyDynamic(function(){") - line(genStmMonitorEnter(AstStm.MONITOR_ENTER(getMonitorLockedObjectExpr(method)))) - } - line(super.genBody2WithFeatures(method, body)) - if(method.modifiers.isSynchronized) { - line("}, function() {") - line(genStmMonitorExit(AstStm.MONITOR_EXIT(getMonitorLockedObjectExpr(method)))) - line("});") + if (method.isSynchronized) { + line("var $__out_var;") + lineMonitorEnter() + line("try {") + indent { + line(genBody2WithFeatures2(method, body)) + } + line("} catch (e: Dynamic) {") + indent { + lineMonitorExit() + line("throw e;") + } + line("}") + } else { + line(genBody2WithFeatures2(method, body)) } } -*/ + + 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() + ");") + } } data class ConfigHaxeAddSubtarget(val subtarget: HaxeAddSubtarget) diff --git a/jtransc-gen-haxe/test/HaxeTest.kt b/jtransc-gen-haxe/test/HaxeTest.kt index 66c4efe8..1e6ec4a8 100644 --- a/jtransc-gen-haxe/test/HaxeTest.kt +++ b/jtransc-gen-haxe/test/HaxeTest.kt @@ -16,6 +16,7 @@ import big.BigTest import big.HelloWorldTest +import big.ThreadTest import com.jtransc.gen.common._Base import com.jtransc.gen.haxe.HaxeTarget import issues.Issue103 @@ -56,6 +57,8 @@ class HaxeTest : _Base() { @Test fun testBigCpp() = testClass(Params(clazz = BigTest::class.java, minimize = false, lang = "cpp", log = null, debug = true)) + @Test fun testThreadCpp() = testClass(Params(clazz = ThreadTest::class.java, minimize = false, lang = "cpp", log = null, debug = true)) + @Test fun haxeNativeCallTest() = testNativeClass(""" true true diff --git a/jtransc-gen-js/src/com/jtransc/gen/js/JsTarget.kt b/jtransc-gen-js/src/com/jtransc/gen/js/JsTarget.kt index 7a177ee5..61bb0e27 100644 --- a/jtransc-gen-js/src/com/jtransc/gen/js/JsTarget.kt +++ b/jtransc-gen-js/src/com/jtransc/gen/js/JsTarget.kt @@ -17,7 +17,6 @@ import com.jtransc.io.ProcessResult2 import com.jtransc.log.log import com.jtransc.sourcemaps.Sourcemaps import com.jtransc.text.Indenter -import com.jtransc.text.Indenter.Companion import com.jtransc.text.isLetterDigitOrUnderscore import com.jtransc.text.quote import com.jtransc.vfs.ExecOptions @@ -238,6 +237,7 @@ class JsGenerator(injector: Injector) : CommonGenerator(injector) { "($array.data[$index])" } } + override fun N_ASET_T(arrayType: AstType.ARRAY, elementType: AstType, array: String, index: String, value: String): String { return if (debugVersion) { "$array.set($index, $value);" @@ -483,18 +483,4 @@ class JsGenerator(injector: Injector) : CommonGenerator(injector) { override fun genExprCastChecked(e: String, from: AstType.Reference, to: AstType.Reference): String { return "N.checkCast($e, ${to.targetNameRef})" } - - override fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter = Indenter { - if(method.modifiers.isSynchronized) { - line("try{") - line(genStmMonitorEnter(AstStm.MONITOR_ENTER(getMonitorLockedObjectExpr(method)))) - } - line(super.genBody2WithFeatures(method, body)) - if(method.modifiers.isSynchronized) { - line("}finally{") - line(genStmMonitorExit(AstStm.MONITOR_EXIT(getMonitorLockedObjectExpr(method)))) - line("}") - } - } - } \ No newline at end of file diff --git a/jtransc-gen-php/src/com/jtransc/gen/php/PhpTarget.kt b/jtransc-gen-php/src/com/jtransc/gen/php/PhpTarget.kt index 9ad16006..d293147f 100644 --- a/jtransc-gen-php/src/com/jtransc/gen/php/PhpTarget.kt +++ b/jtransc-gen-php/src/com/jtransc/gen/php/PhpTarget.kt @@ -460,17 +460,4 @@ class PhpGenerator(injector: Injector) : CommonGenerator(injector) { override fun genExprCastChecked(e: String, from: AstType.Reference, to: AstType.Reference): String { return "N::checkcast($e, ${to.targetName.quote()})" } - - override fun genBody2WithFeatures(method: AstMethod, body: AstBody): Indenter = Indenter { - if(method.modifiers.isSynchronized) { - line("try{") - line(genStmMonitorEnter(AstStm.MONITOR_ENTER(getMonitorLockedObjectExpr(method)))) - } - line(super.genBody2WithFeatures(method, body)) - if(method.modifiers.isSynchronized) { - line("}finally{") - line(genStmMonitorExit(AstStm.MONITOR_EXIT(getMonitorLockedObjectExpr(method)))) - line("}") - } - } } \ No newline at end of file diff --git a/jtransc-maven-plugin/example/pom.xml b/jtransc-maven-plugin/example/pom.xml index 3da006f1..6501539e 100644 --- a/jtransc-maven-plugin/example/pom.xml +++ b/jtransc-maven-plugin/example/pom.xml @@ -29,7 +29,7 @@ org.jetbrains.kotlin kotlin-stdlib - 1.1.4 + 1.1.4-2 1.1.4 + 1.1.4-2 org.jetbrains.kotlin kotlin-runtime jar - 1.1.4 + 1.1.4-2 com.jtransc @@ -536,13 +536,13 @@ org.eclipse.aether aether-api jar - 1.1.4 + 1.1.4-2 org.eclipse.aether aether-util jar - 1.1.4 + 1.1.4-2 org.apache.maven diff --git a/jtransc-rt-core/src/com/jtransc/io/JTranscConsolePrintStream.java b/jtransc-rt-core/src/com/jtransc/io/JTranscConsolePrintStream.java index cabf23f0..5aa02db3 100644 --- a/jtransc-rt-core/src/com/jtransc/io/JTranscConsolePrintStream.java +++ b/jtransc-rt-core/src/com/jtransc/io/JTranscConsolePrintStream.java @@ -8,13 +8,13 @@ public class JTranscConsolePrintStream extends PrintStream { final boolean error; - final ConsoleBaseStream stream; + final ConsoleStream stream; public JTranscConsolePrintStream(final boolean error) { - this(error ? new ConsoleErrorStream() : new ConsoleOutputStream(), error); + this(new ConsoleStream(error), error); } - private JTranscConsolePrintStream(ConsoleBaseStream stream, final boolean error) { + private JTranscConsolePrintStream(ConsoleStream stream, final boolean error) { super(stream); this.stream = stream; this.error = error; @@ -22,20 +22,21 @@ private JTranscConsolePrintStream(ConsoleBaseStream stream, final boolean error) @Override public void println(String x) { - JTranscConsole.logOrError(stream.sb.toString() + x, error); - stream.sb.setLength(0); + synchronized (this) { + JTranscConsole.logOrError(stream.sb.toString() + x, error); + stream.sb.setLength(0); + } } - static private abstract class ConsoleBaseStream extends OutputStream { - public StringBuilder sb = new StringBuilder(); + static private class ConsoleStream extends OutputStream { + public final StringBuilder sb = new StringBuilder(); private final boolean error; - public ConsoleBaseStream(boolean error) { + public ConsoleStream(boolean error) { this.error = error; } - protected void _write(int b) throws IOException { - char c = (char)b; + protected void _write(char c) throws IOException { if (c == '\n') { JTranscConsole.logOrError(sb.toString(), error); sb.setLength(0); @@ -43,29 +44,20 @@ protected void _write(int b) throws IOException { sb.append(c); } } - } - - static private class ConsoleOutputStream extends ConsoleBaseStream { - public ConsoleOutputStream() { - super(false); - } @Override - @JTranscMethodBody(target = "dart", value = "stdout.writeCharCode(p0);") + @JTranscMethodBody(target = "dart", value = "if (this{% IFIELD #CLASS:error %}) stderr.writeCharCode(p0); else stdout.writeCharCode(p0);") public void write(int b) throws IOException { - _write(b); - } - } - - static private class ConsoleErrorStream extends ConsoleBaseStream { - public ConsoleErrorStream() { - super(true); + synchronized (this) { + _write((char) b); + } } @Override - @JTranscMethodBody(target = "dart", value = "stderr.writeCharCode(p0);") - public void write(int b) throws IOException { - _write(b); + public void write(byte[] b, int off, int len) throws IOException { + synchronized (this) { + super.write(b, off, len); + } } } } diff --git a/jtransc-rt-core/src/com/jtransc/thread/JTranscThreading.java b/jtransc-rt-core/src/com/jtransc/thread/JTranscThreading.java deleted file mode 100644 index 1ecc7937..00000000 --- a/jtransc-rt-core/src/com/jtransc/thread/JTranscThreading.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.jtransc.thread; - -import com.jtransc.JTranscSystem; -import com.jtransc.annotation.JTranscMethodBody; -import com.jtransc.annotation.JTranscMethodBodyList; -import com.jtransc.annotation.haxe.HaxeAddMembers; -import com.jtransc.annotation.haxe.HaxeMethodBody; - -public class JTranscThreading { - static public Impl impl = new Impl(null); - - @HaxeAddMembers("private static var threadsMap = new haxe.ds.ObjectMap();") - static public class Impl { - Impl parent; - - public Impl(Impl parent) { - this.parent = parent; - } - - public boolean isSupported() { - if (parent != null) return parent.isSupported(); - return _isSupported(); - } - @HaxeMethodBody(target = "cpp", value = "return threadsMap.get(cpp.vm.Thread.current().handle);") - public Thread getCurrent() { - return null; - } - - @HaxeMethodBody(target = "cpp", value = - "cpp.vm.Thread.create(function():Void{" + - "var h = cpp.vm.Thread.current().handle;" + - "threadsMap.set(h, p0);" + - "p0{% IMETHOD java.lang.Runnable:run %}();" + - "threadsMap.remove(h);" + - "});") - public void start(Thread thread) { - if (parent != null) { - parent.start(thread); - return; - } - System.err.println("WARNING: Threads not supported! Executing thread code in the parent's thread!"); - thread.run(); - } - - @JTranscMethodBodyList({ - @JTranscMethodBody(target = "js", value = "return false;"), - @JTranscMethodBody(target = "d", value = "return true;"), - }) - private boolean _isSupported() { - return !JTranscSystem.isJTransc(); - } - - public boolean isAlive(Thread thread) { - if (parent != null) return parent.isAlive(thread); - //return thread._isAlive; - return false; - } - } -} diff --git a/jtransc-rt/resources/cpp/Base.cpp b/jtransc-rt/resources/cpp/Base.cpp index ef3e4ab1..1540132a 100644 --- a/jtransc-rt/resources/cpp/Base.cpp +++ b/jtransc-rt/resources/cpp/Base.cpp @@ -63,10 +63,19 @@ # include "GC_boehm.cpp" #endif +//#define DO_NOT_USE_GC // For debugging purposes (to detect crashes related to GC) +#ifdef DO_NOT_USE_GC + void* JT_MALLOC(std::size_t sz) { void* out = malloc(sz); memset(out, 0, sz); return out; } + void* JT_MALLOC_ATOMIC(std::size_t sz) { return malloc(sz); } +#else + void* JT_MALLOC(std::size_t sz) { return GC_MALLOC(sz); } + void* JT_MALLOC_ATOMIC(std::size_t sz) { return GC_MALLOC_ATOMIC(sz); } +#endif + struct gc { void* operator new(std::size_t sz) { //std::printf("global op new called, size = %zu\n",sz); - return GC_MALLOC(sz); + return JT_MALLOC(sz); } }; @@ -437,11 +446,12 @@ struct JA_0 : public java_lang_Object { public: void * result = nullptr; int64_t bytesSize = esize * (len + 1); if (pointers) { - result = (void*)GC_MALLOC(bytesSize); + result = (void*)JT_MALLOC(bytesSize); + // this malloc already clears memory, so it is pointless doing this again } else { - result = (void*)GC_MALLOC_ATOMIC(bytesSize); + result = (void*)JT_MALLOC_ATOMIC(bytesSize); + ::memset(result, 0, bytesSize); } - ::memset(result, 0, bytesSize); return result; } @@ -1247,12 +1257,18 @@ int64_t N::nanoTime() { return (int64_t)time.count(); }; -void N::monitorEnter(JAVA_OBJECT obj){ +void N::monitorEnter(JAVA_OBJECT obj) { + if (obj == nullptr) return; + //std::wcout << L"N::monitorEnter[1]\n"; obj->mtx.lock(); + //std::wcout << L"N::monitorEnter[2]\n"; } void N::monitorExit(JAVA_OBJECT obj){ + if (obj == nullptr) return; + //std::wcout << L"N::monitorExit[1]\n"; obj->mtx.unlock(); + //std::wcout << L"N::monitorExit[2]\n"; } void SIGSEGV_handler(int signal) { @@ -3065,7 +3081,7 @@ void N::startup() { */ //GC_set_all_interior_pointers(0); - GC_INIT(); + GC_init_main_thread(); /* GC_clear_roots(); diff --git a/jtransc-rt/resources/cpp/GC_boehm.cpp b/jtransc-rt/resources/cpp/GC_boehm.cpp index 2ad0d32a..5a306a5b 100644 --- a/jtransc-rt/resources/cpp/GC_boehm.cpp +++ b/jtransc-rt/resources/cpp/GC_boehm.cpp @@ -6,3 +6,20 @@ void GC_ADD_ROOT_SINGLE(void* ptr) { GC_add_roots(ptr, (void *)((uintptr_t)ptr + sizeof(uintptr_t))); } + +void GC_init_main_thread() { + GC_init(); + //GC_allow_register_threads(); +} + +void GC_init_pre_thread() { + //GC_init(); +} + +void GC_init_thread() { + //int a = 0; GC_register_my_thread(&a); + GC_init(); +} + +void GC_finish_thread() { +} \ No newline at end of file diff --git a/jtransc-rt/resources/cpp/GC_portable.cpp b/jtransc-rt/resources/cpp/GC_portable.cpp index 3576d612..82999478 100644 --- a/jtransc-rt/resources/cpp/GC_portable.cpp +++ b/jtransc-rt/resources/cpp/GC_portable.cpp @@ -348,3 +348,15 @@ uintptr_t GC_get_free_bytes() { uintptr_t GC_get_total_bytes() { return 0; } + +void GC_init_main_thread() { +} + +void GC_init_pre_thread() { +} + +void GC_init_thread() { +} + +void GC_finish_thread() { +} \ No newline at end of file diff --git a/jtransc-rt/resources/cs/Base.cs b/jtransc-rt/resources/cs/Base.cs index b2b72743..12b159b9 100644 --- a/jtransc-rt/resources/cs/Base.cs +++ b/jtransc-rt/resources/cs/Base.cs @@ -9,11 +9,16 @@ class N { public static readonly float FloatNaN = intBitsToFloat(0x7FC00000); public static readonly double DoubleNaN = longBitsToDouble(0x7FF8000000000000); - public static readonly long MAX_INT64 = 9223372036854775807; - public static readonly long MIN_INT64 = -9223372036854775808; + //public static readonly long MAX_INT64 = 9223372036854775807; + //public static readonly long MIN_INT64 = -9223372036854775808; + //public static readonly int MAX_INT32 = 2147483647; + //public static readonly int MIN_INT32 = -2147483648; + + static readonly public int MIN_INT32 = unchecked((int)0x80000000); + static readonly public int MAX_INT32 = unchecked((int)0x7FFFFFFF); + static readonly public long MIN_INT64 = unchecked((long)0x8000000000000000L); + static readonly public long MAX_INT64 = unchecked((long)0x7FFFFFFFFFFFFFFFL); - public static readonly int MAX_INT32 = 2147483647; - public static readonly int MIN_INT32 = -2147483648; //static public TOut CHECK_CAST(TIn i) where TIn : class where TOut : class { // if (i == null) return null; @@ -44,12 +49,6 @@ static public int iushr(int l, int r) { return (int)(((uint)l) >> r); } - //static public int MIN_INT32 = Int32.MinValue; - static readonly public int MIN_INT32 = unchecked((int)0x80000000); - static readonly public int MAX_INT32 = unchecked((int)0x7FFFFFFF); - static readonly public long MIN_INT64 = unchecked((long)0x8000000000000000L); - static readonly public long MAX_INT64 = unchecked((long)0x7FFFFFFFFFFFFFFFL); - static public void init() { //Console.WriteLine(Console.OutputEncoding.CodePage); } @@ -98,9 +97,9 @@ static public JA_L getStackTrace(System.Diagnostics.StackTrace st, int skip) { static public int z2i(bool v) { return v ? 1 : 0; } static public int j2i(long v) { return (int)v; } static public long d2j(double v) { - if (Single.IsNaN(v)) { + if (Double.IsNaN(v)) { return 0; - } else if (!Single.IsInfinity(v)) { + } else if (!Double.IsInfinity(v)) { return (long)v; } else if (v >= 0) { return MAX_INT64; diff --git a/jtransc-rt/resources/hx/N.hx b/jtransc-rt/resources/hx/N.hx index 8f7ed071..0c13c8a0 100644 --- a/jtransc-rt/resources/hx/N.hx +++ b/jtransc-rt/resources/hx/N.hx @@ -821,4 +821,30 @@ class N { finallyBlock(); return ret; } + + {{ HAXE_METHOD_ANNOTATIONS }} static public function monitorCommon(obj: JavaObject) { + #if cpp + if (obj._hx_mutex == null) obj._hx_mutex = new cpp.vm.Mutex(); + #end + } + + {{ HAXE_METHOD_ANNOTATIONS }} static public function monitorEnter(obj: JavaObject) { + if (obj == null) return; + monitorCommon(obj); + #if cpp + //trace('acquire1'); + obj._hx_mutex.acquire(); + //trace('acquire2'); + #end + } + + {{ HAXE_METHOD_ANNOTATIONS }} static public function monitorExit(obj: JavaObject) { + if (obj == null) return; + monitorCommon(obj); + #if cpp + //trace('release1'); + obj._hx_mutex.release(); + //trace('release2'); + #end + } } \ No newline at end of file diff --git a/jtransc-rt/src/java/lang/Object.java b/jtransc-rt/src/java/lang/Object.java index 9fbe0d95..88566e80 100644 --- a/jtransc-rt/src/java/lang/Object.java +++ b/jtransc-rt/src/java/lang/Object.java @@ -18,6 +18,7 @@ import com.jtransc.annotation.*; import com.jtransc.annotation.haxe.HaxeAddFilesTemplate; +import com.jtransc.annotation.haxe.HaxeAddMembers; import com.jtransc.annotation.haxe.HaxeAddSubtarget; import java.lang.jtransc.JTranscCoreReflection; @@ -69,6 +70,9 @@ @JTranscAddFile(target = "as3", priority = -1, process = true, src = "as3/Main.xml", dst = "Main.xml") @JTranscAddMembers(target = "d", value = "core.sync.mutex.Mutex __d_mutex = null;") @JTranscAddMembers(target = "cpp", value = "std::recursive_mutex mtx;") +@HaxeAddMembers({ + "#if cpp public var _hx_mutex: cpp.vm.Mutex = null; #end", +}) public class Object { @JTranscInvisible public int $$id; @@ -124,6 +128,7 @@ public String toString() { private static final long SAMPLING_STEP = 50; private long waitTimeout; + public final void notify() { waitTimeout = 0; } @@ -136,7 +141,7 @@ public final void wait(long timeout) throws InterruptedException { if (timeout < 0) throw new IllegalArgumentException("timeout is negative"); waitTimeout = timeout == 0 ? Long.MAX_VALUE : timeout; - while (waitTimeout > 0){ + while (waitTimeout > 0) { waitTimeout -= SAMPLING_STEP; Thread.sleep(SAMPLING_STEP); } diff --git a/jtransc-rt/src/java/lang/Thread.java b/jtransc-rt/src/java/lang/Thread.java index 174a1680..17329e84 100644 --- a/jtransc-rt/src/java/lang/Thread.java +++ b/jtransc-rt/src/java/lang/Thread.java @@ -17,31 +17,39 @@ package java.lang; import com.jtransc.JTranscSystem; -import com.jtransc.annotation.JTranscAddHeader; import com.jtransc.annotation.JTranscAddIncludes; import com.jtransc.annotation.JTranscAddMembers; import com.jtransc.annotation.JTranscMethodBody; +import com.jtransc.annotation.haxe.HaxeAddMembers; import com.jtransc.annotation.haxe.HaxeMethodBody; -import com.jtransc.thread.JTranscThreading; +import java.util.Collection; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; @JTranscAddMembers(target = "d", value = "static {% CLASS java.lang.Thread %} _dCurrentThread; Thread thread;") -@JTranscAddHeader(target = "cpp", cond = "USE_BOOST", value = { - "#include ", - "#include ", -}) -@JTranscAddIncludes(target = "cpp", value = "thread") +@JTranscAddMembers(target = "cs", value = "System.Threading.Thread _cs_thread;") +@JTranscAddIncludes(target = "cpp", cond = "USE_BOOST", value = {"thread", "map", "boost/thread.hpp", "boost/chrono.hpp"}) +@JTranscAddIncludes(target = "cpp", value = {"thread", "map"}) @JTranscAddMembers(target = "cpp", cond = "USE_BOOST", value = "boost::thread t_;") @JTranscAddMembers(target = "cpp", cond = "!USE_BOOST", value = "std::thread t_;") +@JTranscAddMembers(target = "cpp", cond = "USE_BOOST", value = "static std::map ###_cpp_threads;") +@JTranscAddMembers(target = "cpp", cond = "!USE_BOOST", value = "static std::map ###_cpp_threads;") +@HaxeAddMembers({ + "private static var threadsMap = new haxe.ds.ObjectMap();", + "#if cpp var _cpp_thread: cpp.vm.Thread; #end", +}) public class Thread implements Runnable { public final static int MIN_PRIORITY = 1; public final static int NORM_PRIORITY = 5; public final static int MAX_PRIORITY = 10; - static private Thread _currentThread; - private static int _threadNum = 0; + public static Thread currentThread() { + lazyPrepareThread(); + Thread out = _getCurrentThreadOrNull(); + return (out != null) ? out : _mainThread; + } @JTranscMethodBody(target = "d", value = { "if (_dCurrentThread is null) {", @@ -49,30 +57,28 @@ public class Thread implements Runnable { "}", "return _dCurrentThread;", }) - //@JTranscMethodBody(target = "cpp", value = { - // "return boost::this_thread;", - //}) - - public static Thread currentThread() { - if (JTranscThreading.impl != null) { // due to static init issue - Thread thread = JTranscThreading.impl.getCurrent(); - if (thread != null) return thread; - } - - if (_currentThread == null) { - _currentThread = new Thread(); - _currentThread.setName("main"); // Most possible, it will represent the main stream - } - return _currentThread; + @JTranscMethodBody(target = "cpp", cond = "USE_BOOST", value = "return _cpp_threads[boost::this_thread::get_id()];") + @JTranscMethodBody(target = "cpp", value = "return _cpp_threads[std::this_thread::get_id()];") + @HaxeMethodBody(target = "cpp", value = "return threadsMap.get(cpp.vm.Thread.current().handle);") + private static Thread _getCurrentThreadOrNull() { + for (Thread t : getThreadsCopy()) return t; // Just valid for programs with just once thread + return null; } + public StackTraceElement[] getStackTrace() { return new Throwable().getStackTrace(); } + @SuppressWarnings("unused") @JTranscMethodBody(target = "d", value = "Thread.yield();") - @JTranscMethodBody(target = "cpp", value = "std::this_thread::yield();") + //@JTranscMethodBody(target = "cpp", value = "std::this_thread::yield();") public static void yield() { + try { + Thread.sleep(1L); + } catch (InterruptedException e) { + e.printStackTrace(); + } } @JTranscMethodBody(target = "d", value = "Thread.sleep(dur!(\"msecs\")(p0));") @@ -94,20 +100,20 @@ public Thread() { this(null, null, null, 1024); } + static private LinkedHashMap _threadsById; private ThreadGroup group; public String name; private long stackSize; - public long _data; - public boolean _isAlive; private Runnable target; + private int priority = MIN_PRIORITY; + private int id; + static private int lastId = 0; + private UncaughtExceptionHandler uncaughtExceptionHandler = defaultUncaughtExceptionHandler; public Thread(Runnable target) { this(null, target, null, 1024); } - //Thread(Runnable target, AccessControlContext acc) { - //} - public Thread(ThreadGroup group, Runnable target) { this(group, target, null, 1024); } @@ -129,9 +135,10 @@ public Thread(ThreadGroup group, Runnable target, String name) { } public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { - this.group = group; + this.group = (group != null) ? group : currentThread().getThreadGroup(); this.target = target; - this.name = name == null ? "thread-" + _threadNum++ : name; + this.id = lastId++; + this.name = (name != null) ? name : ("thread-" + id++); this.stackSize = stackSize; _init(); } @@ -144,26 +151,118 @@ public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { private void _init() { } - @JTranscMethodBody(target = "d", value = "this.thread.start();") - @JTranscMethodBody(target = "cpp", cond = "USE_BOOST", value = "t_ = std::thread(&{% CLASS java.lang.Thread:runInternal %}::{% METHOD java.lang.Thread:runInternal:()V %}, this);") - @JTranscMethodBody(target = "cpp", value = "t_ = std::thread(&{% CLASS java.lang.Thread:runInternal %}::{% METHOD java.lang.Thread:runInternal:()V %}, this);") + private boolean _isAlive; + + static private final Object staticLock = new Object(); + static private ThreadGroup _mainThreadGroup = null; + static private Thread _mainThread = null; + + synchronized static private Thread[] getThreadsCopy() { + Collection threads = getThreadSetInternal().values(); + synchronized (staticLock) { + return threads.toArray(new Thread[0]); + } + } + + static private void lazyPrepareThread() { + synchronized (staticLock) { + if (_mainThreadGroup == null) { + _mainThreadGroup = new ThreadGroup("main"); + } + if (_mainThread == null) { + _mainThread = new Thread(_mainThreadGroup, "main"); + } + if (_threadsById == null) { + _threadsById = new LinkedHashMap<>(); + _threadsById.put(_mainThread.getId(), _mainThread); + } + } + } + + static private LinkedHashMap getThreadSetInternal() { + lazyPrepareThread(); + return _threadsById; + } + public synchronized void start() { - JTranscThreading.impl.start(this); + runInternalPreInit(); + _start(); } + @JTranscMethodBody(target = "d", value = "this.thread.start();") + @JTranscMethodBody(target = "cs", value = { + "_cs_thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate() { this{% IMETHOD java.lang.Thread:runInternal:()V %}(); }));", + "_cs_thread.Start();", + }) + @JTranscMethodBody(target = "cpp", cond = "USE_BOOST", value = { + "t_ = std::thread(&{% SMETHOD java.lang.Thread:runInternalStatic:(Ljava/lang/Thread;)V %}, this);", + }) + @JTranscMethodBody(target = "cpp", value = { + "t_ = std::thread(&{% SMETHOD java.lang.Thread:runInternalStatic:(Ljava/lang/Thread;)V %}, this);", + }) + @HaxeMethodBody(target = "cpp", value = "" + + "var that = this;" + + "cpp.vm.Thread.create(function():Void {" + + " that._cpp_thread = cpp.vm.Thread.current();" + + " that{% IMETHOD java.lang.Thread:runInternal:()V %}();" + + "});" + ) + private void _start() { + System.err.println("WARNING: Threads not supported! Executing thread code in the parent's thread!"); + runInternal(); + } @SuppressWarnings("unused") private void runInternal() { - runInternalInit(); - run(); + try { + runInternalInit(); + run(); + } catch (Throwable t) { + uncaughtExceptionHandler.uncaughtException(this, t); + } finally { + runExit(); + } } - @JTranscMethodBody(target = "d", value = { - "_dCurrentThread = this;", - }) + @SuppressWarnings("unused") + static private void runInternalStatic(Thread thread) { + thread.runInternal(); + } + + + @JTranscMethodBody(target = "cpp", value = "GC_init_pre_thread();") + private void runInternalPreInitNative() { + } + + private void runInternalPreInit() { + runInternalPreInitNative(); + final LinkedHashMap set = getThreadSetInternal(); + synchronized (staticLock) { + set.put(getId(), this); + _isAlive = true; + } + } + + @JTranscMethodBody(target = "d", value = "_dCurrentThread = this;") + @JTranscMethodBody(target = "cpp", value = "GC_init_thread(); _cpp_threads[t_.get_id()] = this;") + @HaxeMethodBody(target = "cpp", value = "threadsMap.set(_cpp_thread.handle, this);") private void runInternalInit() { } + @JTranscMethodBody(target = "cpp", value = "_cpp_threads.erase(t_.get_id()); GC_finish_thread();") + @HaxeMethodBody(target = "cpp", value = "threadsMap.remove(_cpp_thread.handle);") + private void runInternalExit() { + } + + private void runExit() { + final LinkedHashMap set = getThreadSetInternal(); + synchronized (this) { + runInternalExit(); + set.remove(getId()); + _isAlive = false; + } + } + @Override public void run() { if (this.target != null) { @@ -180,11 +279,10 @@ public final synchronized void stop(Throwable obj) { } public void interrupt() { - } public static boolean interrupted() { - return false; + return Thread.currentThread().isInterrupted(); } public boolean isInterrupted() { @@ -196,7 +294,8 @@ public void destroy() { } public final boolean isAlive() { - return JTranscThreading.impl.isAlive(this); + //System.out.println("isAlive: " + _isAlive); + return _isAlive; } @Deprecated @@ -205,10 +304,12 @@ public final boolean isAlive() { @Deprecated native public final void resume(); - native public final void setPriority(int newPriority); + public final void setPriority(int newPriority) { + this.priority = newPriority; + } public final int getPriority() { - return NORM_PRIORITY; + return priority; } public final synchronized void setName(String name) { @@ -224,29 +325,56 @@ public final ThreadGroup getThreadGroup() { } public static int activeCount() { - return 1; + return getThreadsCopy().length; } - native public static int enumerate(Thread tarray[]); + public static int enumerate(Thread tarray[]) { + int n = 0; + for (Thread thread : getThreadsCopy()) { + if (n >= tarray.length) break; + tarray[n++] = thread; + } + return n; + } @Deprecated - native public int countStackFrames(); + public int countStackFrames() { + return 0; + } - native public final synchronized void join(long millis) throws InterruptedException; + public final synchronized void join(long millis) throws InterruptedException { + join(millis, 0); + } - native public final synchronized void join(long millis, int nanos) throws InterruptedException; + public final synchronized void join(long millis, int nanos) throws InterruptedException { + final long start = System.currentTimeMillis(); + while (isAlive()) { + final long current = System.currentTimeMillis(); + final long elapsed = current - start; + if (elapsed >= millis) break; + Thread.sleep(1L); + } + } - native public final void join() throws InterruptedException; + public final void join() throws InterruptedException { + while (isAlive()) { + Thread.sleep(1L); + } + } native public static void dumpStack(); + private boolean _isDaemon = false; + @JTranscMethodBody(target = "d", value = "this.thread.isDaemon = p0;") public final void setDaemon(boolean on) { + _isDaemon = on; } - @HaxeMethodBody("return false;") @JTranscMethodBody(target = "d", value = "return this.thread.isDaemon;") - native public final boolean isDaemon(); + public final boolean isDaemon() { + return _isDaemon; + } native public final void checkAccess(); @@ -282,10 +410,12 @@ public static Map getAllStackTraces() { @JTranscMethodBody(target = "d", value = "return this.thread.id;") public long getId() { - return 0L; + return id; } - public enum State {NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED} + public enum State { + NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED + } public State getState() { return State.RUNNABLE; @@ -295,11 +425,29 @@ public interface UncaughtExceptionHandler { void uncaughtException(Thread t, Throwable e); } - native public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh); + static public UncaughtExceptionHandler defaultUncaughtExceptionHandler = (t, e) -> { + System.out.println(t); + System.out.println(e); + }; - native public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(); + public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) { + defaultUncaughtExceptionHandler = eh; + } + + public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { + return defaultUncaughtExceptionHandler; + } + + public UncaughtExceptionHandler getUncaughtExceptionHandler() { + return uncaughtExceptionHandler; + } - native public UncaughtExceptionHandler getUncaughtExceptionHandler(); + public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) { + this.uncaughtExceptionHandler = eh; + } - native public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh); + @Override + protected Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } } diff --git a/jtransc-rt/src/java/lang/ThreadGroup.java b/jtransc-rt/src/java/lang/ThreadGroup.java index b660833f..6eca694a 100644 --- a/jtransc-rt/src/java/lang/ThreadGroup.java +++ b/jtransc-rt/src/java/lang/ThreadGroup.java @@ -16,73 +16,174 @@ package java.lang; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; + public class ThreadGroup implements Thread.UncaughtExceptionHandler { - private ThreadGroup() { + private ThreadGroup parent; + private String name; + private LinkedHashSet threads; + private LinkedHashSet children; + private int maxPriority = Thread.MAX_PRIORITY; + private boolean isDaemon = false; + private boolean isDestroyed = false; + + synchronized private Thread[] getThreadsCopy() { + return (threads != null) ? threads.toArray(new Thread[0]) : new Thread[0]; } - public ThreadGroup(String name) { + synchronized private ArrayList getAllThreads(ArrayList out) { + for (Thread thread : getThreadsCopy()) out.add(thread); + for (ThreadGroup group : getChildrenCopy()) group.getAllThreads(out); + return out; + } + synchronized private ArrayList getAllThreads() { + return getAllThreads(new ArrayList<>()); } - public ThreadGroup(ThreadGroup parent, String name) { + synchronized private ThreadGroup[] getChildrenCopy() { + return (children != null) ? children.toArray(new ThreadGroup[0]) : new ThreadGroup[0]; + } + synchronized private ArrayList getAllChildren(ArrayList out) { + out.add(this); + for (ThreadGroup group : getChildrenCopy()) group.getAllChildren(out); + return out; } - private ThreadGroup(Void unused, ThreadGroup parent, String name) { + synchronized private ArrayList getAllChildren() { + return getAllChildren(new ArrayList<>()); } - native public final String getName(); + private ThreadGroup() { + this("ThreadGroup"); + } - native public final ThreadGroup getParent(); + public ThreadGroup(String name) { + this(null, name); + } - native public final int getMaxPriority(); + public ThreadGroup(ThreadGroup parent, String name) { + this.parent = parent; + this.name = (name != null) ? name : "ThreadGroup"; + } - native public final boolean isDaemon(); + public final String getName() { + return name; + } + + public final ThreadGroup getParent() { + return parent; + } + + public final int getMaxPriority() { + return maxPriority; + } + + public final boolean isDaemon() { + return this.isDaemon; + } - native public synchronized boolean isDestroyed(); + public synchronized boolean isDestroyed() { + return isDestroyed; + } - native public final void setDaemon(boolean daemon); + public final void setDaemon(boolean daemon) { + this.isDaemon = daemon; + } - native public final void setMaxPriority(int pri); + public final void setMaxPriority(int priority) { + this.maxPriority = priority; + } - native public final boolean parentOf(ThreadGroup g); + public final boolean parentOf(ThreadGroup g) { + return (g == this.parent) || ((this.parent != null) && this.parent.parentOf(g)); + } - native public final void checkAccess(); + public final void checkAccess() { + } - native public int activeCount(); + public int activeGroupCount() { + int count = 1; + for (ThreadGroup child : getChildrenCopy()) { + count += child.activeGroupCount(); + } + return count; + } - native public int enumerate(Thread list[]); + public int activeCount() { + return getAllThreads().size(); + } - native public int enumerate(Thread list[], boolean recurse); + public int enumerate(Thread list[]) { + return enumerate(list, true); + } - native public int activeGroupCount(); + public int enumerate(ThreadGroup list[]) { + return enumerate(list, true); + } - native public int enumerate(ThreadGroup list[]); + public int enumerate(Thread list[], boolean recurse) { + int n = 0; + for (Thread item : recurse ? getAllThreads() : Arrays.asList(getThreadsCopy())) { + if (n >= list.length) break; + list[n++] = item; + } + return n; + } - native public int enumerate(ThreadGroup list[], boolean recurse); + public int enumerate(ThreadGroup list[], boolean recurse) { + int n = 0; + for (ThreadGroup item : recurse ? getAllChildren() : Arrays.asList(getChildrenCopy())) { + if (n >= list.length) break; + list[n++] = item; + } + return n; + } @Deprecated - native public final void stop(); + public final void stop() { + for (Thread thread : getThreadsCopy()) thread.stop(); + for (ThreadGroup group : getChildrenCopy()) group.stop(); + } - native public final void interrupt(); + public final void interrupt() { + for (Thread thread : getThreadsCopy()) thread.interrupt(); + for (ThreadGroup group : getChildrenCopy()) group.interrupt(); + } @Deprecated @SuppressWarnings("deprecation") - native public final void suspend(); - + public final void suspend() { + for (Thread thread : getThreadsCopy()) thread.suspend(); + for (ThreadGroup group : getChildrenCopy()) group.suspend(); + } @Deprecated @SuppressWarnings("deprecation") - native public final void resume(); + public final void resume() { + for (Thread thread : getThreadsCopy()) thread.resume(); + for (ThreadGroup group : getChildrenCopy()) group.resume(); + } - native public final void destroy(); + synchronized public final void destroy() { + isDestroyed = true; + for (Thread thread : getThreadsCopy()) thread.destroy(); + for (ThreadGroup group : getChildrenCopy()) group.destroy(); + } - native public void list(); + public void list() { + System.out.println("Unimplemented ThreadGroup.list()"); + } native public void uncaughtException(Thread t, Throwable e); @Deprecated - native public boolean allowThreadSuspension(boolean b); + public boolean allowThreadSuspension(boolean b) { + return false; + } public String toString() { return getClass().getName() + "[name=" + getName() + ",maxpri=" + getMaxPriority() + "]"; diff --git a/jtransc-utils/src/com/jtransc/KotlinVersion.kt b/jtransc-utils/src/com/jtransc/KotlinVersion.kt index cf554b63..829c277b 100644 --- a/jtransc-utils/src/com/jtransc/KotlinVersion.kt +++ b/jtransc-utils/src/com/jtransc/KotlinVersion.kt @@ -16,4 +16,4 @@ package com.jtransc -val KotlinVersion = "1.1.4" \ No newline at end of file +val KotlinVersion = "1.1.4-2" \ No newline at end of file