From 861d5210efe39af12189528127cae550ed771bf4 Mon Sep 17 00:00:00 2001 From: soywiz Date: Sun, 13 Jun 2021 20:03:45 +0200 Subject: [PATCH] New treeshaking experiment --- gradle.properties | 2 +- jtransc-core/src/com/jtransc/ast/ast.kt | 105 +++++++++++++++--- jtransc-core/src/com/jtransc/ast/ast_ref.kt | 4 + .../src/com/jtransc/ast/ast_references.kt | 13 +++ jtransc-core/src/com/jtransc/ast/ast_type.kt | 26 +++-- .../ast/dependency/genStaticInitOrder.kt | 2 +- .../src/com/jtransc/backend/AsmToAst.kt | 12 +- jtransc-core/src/com/jtransc/build.kt | 101 ++++++++++++++--- .../reflection/MetaReflectionJTranscPlugin.kt | 2 + .../service/ServiceLoaderJTranscPlugin.kt | 6 +- .../src/com/jtransc/gen/cpp/CppTarget.kt | 4 +- jtransc-main/src/com/jtransc/main.kt | 13 +++ .../src/com/jtransc/io/ra/RAStream.java | 18 ++- jtransc-rt/src/java/util/Collection.java | 15 +++ .../com/jtransc/imaging/ImagePropsDecoder.kt | 2 +- jtransc-utils/src/com/jtransc/lang/Dynamic.kt | 2 +- .../src/com/jtransc/sourcemaps/sourcemaps.kt | 4 +- jtransc-utils/src/com/jtransc/vfs/FileExt.kt | 4 +- jtransc-utils/src/com/jtransc/vfs/syncvfs.kt | 2 +- jtransc-utils/src/com/jtransc/vfs/zipvfs.kt | 10 +- 20 files changed, 291 insertions(+), 56 deletions(-) diff --git a/gradle.properties b/gradle.properties index 233052e6..11a0f530 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ jtranscVersion=0.8.0-SNAPSHOT group=com.jtransc file.encoding=UTF-8 -kotlinVersion=1.4.31 +kotlinVersion=1.5.10 bintrayVersion=1.7.3 mavenPluginAnnotationsVersion=3.4 diff --git a/jtransc-core/src/com/jtransc/ast/ast.kt b/jtransc-core/src/com/jtransc/ast/ast.kt index 0e2abfc8..42c8cd92 100644 --- a/jtransc-core/src/com/jtransc/ast/ast.kt +++ b/jtransc-core/src/com/jtransc/ast/ast.kt @@ -20,6 +20,7 @@ import com.jtransc.* import com.jtransc.annotation.* import com.jtransc.ast.dependency.AstDependencyAnalyzer import com.jtransc.ast.optimize.AstOptimizer +import com.jtransc.backend.* import com.jtransc.ds.cast import com.jtransc.ds.clearFlags import com.jtransc.ds.combinedWith @@ -34,6 +35,7 @@ import com.jtransc.lang.Extra import com.jtransc.lang.putIfAbsentJre7 import com.jtransc.maven.MavenLocalRepository import com.jtransc.org.objectweb.asm.Type +import com.jtransc.org.objectweb.asm.tree.* import com.jtransc.text.quote import com.jtransc.text.substr import com.jtransc.util.dependencySorter @@ -42,6 +44,8 @@ import com.jtransc.vfs.UserData import java.io.File import java.io.IOException import java.util.* +import kotlin.collections.ArrayDeque +import kotlin.collections.ArrayList import kotlin.reflect.KMutableProperty1 data class ConfigCompile(val compile: Boolean = true) @@ -110,17 +114,40 @@ data class AstBuildSettings( } interface AstClassGenerator { + fun getOrGenerateClass(program: AstProgram, fqname: FqName): AstClass { + return program.getOrNull(fqname) ?: generateClass(program, fqname) + } + fun generateClass(program: AstProgram, fqname: FqName): AstClass + fun generateAndAddMethod(program: AstProgram, ref: AstMethodRef): AstMethod { + val clazz = getOrGenerateClass(program, ref.classRef.name) + tryGenerateAndAddMethod(clazz, ref)?.let { return it } + TODO("Can't find $ref in any ancestor : ${clazz.thisAncestorsAndInterfaces.map { it.name }}") + } + fun tryGenerateAndAddMethod(clazz: AstClass, ref: AstMethodRef): AstMethod? { + val refWithoutClass = ref.withoutClass + + val allClasses = clazz.thisAncestorsAndInterfaces + clazz.allInterfacesInAncestors.toSet() + + for (subClass in allClasses) { + if (subClass.hasBasicMethod(refWithoutClass)) { + return generateAndAddMethod(subClass, refWithoutClass) + } + } + return null + } + + fun generateAndAddMethod(clazz: AstClass, ref: AstMethodWithoutClassRef): AstMethod } interface AstResolver { - operator fun get(ref: AstMethodRef): AstMethod? - operator fun get(ref: AstFieldRef): AstField? - operator fun get(name: FqName): AstClass? + operator fun get(ref: AstMethodRef, reason: Any? = null): AstMethod? + operator fun get(ref: AstFieldRef, reason: Any? = null): AstField? + operator fun get(name: FqName, reason: Any? = null): AstClass? operator fun contains(name: FqName): Boolean } -operator fun AstResolver.get(ref: AstType.REF): AstClass? = this[ref.name] +operator fun AstResolver.get(ref: AstType.REF, reason: Any? = null): AstClass? = this.get(ref.name, reason) fun AstResolver.get3(ref: AstType.REF): AstClass = this[ref.name]!! @@ -192,11 +219,15 @@ class AstProgram( val classes: List get() = _classes - private val classesToGenerate = LinkedList() + private val classesToGenerate = ArrayDeque() private val referencedClasses = hashSetOf() private val referencedClassBy = hashMapOf() + private val methodsToGenerate = ArrayDeque() + private val referencedMethods = hashSetOf() + fun hasClassToGenerate() = classesToGenerate.isNotEmpty() + fun hasMethodToGenerate() = methodsToGenerate.isNotEmpty() fun getClassBytes(clazz: FqName): ByteArray { try { @@ -206,7 +237,8 @@ class AstProgram( } } - fun readClassToGenerate(): AstType.REF = classesToGenerate.remove() + fun readClassToGenerate(): AstType.REF = classesToGenerate.removeFirst() + fun readMethodToGenerate(): AstMethodRef = methodsToGenerate.removeFirst() fun addReference(clazz: AstType.REF, referencedBy: AstType.REF) { if (clazz !in referencedClasses) { @@ -216,6 +248,14 @@ class AstProgram( } } + fun addReference(methodRef: AstMethodRef, referencedBy: AstType.REF) { + if (methodRef !in referencedMethods) { + println("REFERENCED: $methodRef") + methodsToGenerate += methodRef + referencedMethods += methodRef + } + } + override operator fun contains(name: FqName) = name.fqname in _classesByFqname //operator fun get(name: FqName) = classesByFqname[name.fqname] ?: throw RuntimeException("AstProgram. Can't find class '$name'") @@ -223,13 +263,14 @@ class AstProgram( return _classesByFqname[name.fqname] } - override operator fun get(name: FqName): AstClass { + override fun get(name: FqName, reason: Any?): AstClass { val result = getOrNull(name) if (result == null) { val classFile = name.internalFqname + ".class" println("AstProgram. Can't find class '$name'") println("AstProgram. ClassFile: $classFile") println("AstProgram. File exists: " + resourcesVfs[classFile].exists + " : " + resourcesVfs[classFile].realpathOS) + println("AstProgram. Reason: $reason") println("ConfigClassPaths:") for (classPath in injector.get().classPaths) { println(" - $classPath") @@ -262,9 +303,9 @@ class AstProgram( val allAnnotationsList by lazy { AstAnnotationList(AstProgramRef, allAnnotations) } - override operator fun get(ref: AstMethodRef): AstMethod? = this[ref.containingClass].getMethodInAncestorsAndInterfaces(ref.nameDesc) + override operator fun get(ref: AstMethodRef, reason: Any?): AstMethod? = this[ref.containingClass].getMethodInAncestorsAndInterfaces(ref.nameDesc) //override operator fun get(ref: AstFieldRef): AstField = this[ref.containingClass][ref] - override operator fun get(ref: AstFieldRef): AstField = this[ref.containingClass].get(ref.withoutClass) + override operator fun get(ref: AstFieldRef, reason: Any?): AstField = this[ref.containingClass].get(ref.withoutClass) operator fun get(ref: FieldRef): AstField = this[ref.ref.containingClass].get(ref.ref.withoutClass) @@ -344,7 +385,7 @@ val AstAnnotated?.keepName: Boolean get() = this?.annotationsList?.contains = listOf(), val classId: Int = program.lastClassId++ ) : AstAnnotatedElement(program, name.ref, annotations), IUserData by UserData(), WithAstModifiersClass { + var classNode: ClassNode? = null + var classNodeMethods: Map? = null + //var classNodeFields: Map? = null + val types get() = program.types + fun hasBasicMethod(methodRef: AstMethodWithoutClassRef): Boolean { + return classNodeMethods?.contains(methodRef) == true + } + + fun setAsmClassNode(classNode: ClassNode) { + this.classNode = classNode + classNodeMethods = classNode.methods.associateBy { it.astRef(this.ref, types).withoutClass } + //classNodeFields = classNode.fields.associateBy { AstFieldRef(name, it.name, AstType.) } + } + val implementingUnique by lazy { implementing.distinct() } val THIS: AstExpr get() = AstExpr.THIS(name) //var lastMethodId = 0 @@ -419,9 +474,28 @@ class AstClass( } //fun getDirectInterfaces(): List = implementing.map { program[it] } - val directInterfaces: List by lazy { implementing.map { program[it] } } + val directInterfaces: List by lazy { + implementing.mapNotNull { interfaceName -> + program.getOrNull(interfaceName).also { + if (it == null) { + println(interfaceName) + } + } + } + //implementing.map { program.get(it, this) } + } - val parentClass: AstClass? by lazy { if (extending != null) program[extending] else null } + val parentClass: AstClass? by lazy { + if (extending != null) { + program.getOrNull(extending).also { + if (it == null) { + System.err.println("AstClass.parentClass.extending can't find $extending") + } + } ?: program["java.lang.Object".fqname] + } else { + null + } + } val parentClassList by lazy { listOf(parentClass).filterNotNull() } //fun getParentClass(): AstClass? = if (extending != null) program[extending] else null @@ -590,7 +664,12 @@ class AstClass( if (extending == null) { listOf(this) } else { - listOf(this) + program[extending].thisAndAncestors + listOf(this) + program.getOrNull(extending)?.thisAndAncestors.let { + if (it == null) { + System.err.println("ERROR thisAndAncestors: extending=$extending") + } + it ?: listOf() + } } } diff --git a/jtransc-core/src/com/jtransc/ast/ast_ref.kt b/jtransc-core/src/com/jtransc/ast/ast_ref.kt index bb8d9084..04b588c4 100644 --- a/jtransc-core/src/com/jtransc/ast/ast_ref.kt +++ b/jtransc-core/src/com/jtransc/ast/ast_ref.kt @@ -74,6 +74,10 @@ data class AstFieldWithoutClassRef(val name: String, val type: AstType) data class AstFieldWithoutTypeRef(val containingClass: FqName, val name: String) data class AstMethodWithoutClassRef(val name: String, val type: AstType.METHOD) { + companion object { + val CLINIT = AstMethodWithoutClassRef("", AstType.METHOD(AstType.VOID, listOf())) + } + val fid2: String get() = "$name:${type.mangle()}" val fid2Wildcard: String get() = "$name:*" val desc = type.desc diff --git a/jtransc-core/src/com/jtransc/ast/ast_references.kt b/jtransc-core/src/com/jtransc/ast/ast_references.kt index 8a656653..e931ba48 100644 --- a/jtransc-core/src/com/jtransc/ast/ast_references.kt +++ b/jtransc-core/src/com/jtransc/ast/ast_references.kt @@ -3,6 +3,8 @@ package com.jtransc.ast import com.jtransc.annotation.JTranscAddFileList import com.jtransc.annotation.JTranscAddMembersList import com.jtransc.annotation.JTranscMethodBodyList +import com.jtransc.ast.References.getClassReferences +import com.jtransc.ast.References.getMethodReferences import com.jtransc.ast.treeshaking.GetClassTemplateReferences import com.jtransc.ast.treeshaking.TRefConfig import com.jtransc.ast.treeshaking.TRefReason @@ -48,6 +50,11 @@ object References { return this.genericType.getRefClasses() + this.annotations.getClassReferences(targetName) } + fun AstMethod.getMethodReferences(targetName: TargetName): List { + val methodBodyList = this.annotationsList.getBodiesForTarget(targetName) + return if (methodBodyList.isEmpty()) this.body?.getMethodReferences() ?: listOf() else listOf() + } + fun AstMethod.getClassReferences(targetName: TargetName): List { val clazzFqname = this.containingClass.name val signatureRefs = this.genericMethodType.getRefClasses() @@ -108,6 +115,12 @@ object References { return locals + traps + stms } + fun AstBody.getMethodReferences(): List { + val refs = ReferencesVisitor() + refs.visit(this) + return refs.methodReferences.toList() + } + fun AstStm.getClassReferences(): List { val refs = ReferencesVisitor() refs.visit(this) diff --git a/jtransc-core/src/com/jtransc/ast/ast_type.kt b/jtransc-core/src/com/jtransc/ast/ast_type.kt index 0f57fe69..b8c42138 100644 --- a/jtransc-core/src/com/jtransc/ast/ast_type.kt +++ b/jtransc-core/src/com/jtransc/ast/ast_type.kt @@ -58,7 +58,10 @@ open class AstType { object DOUBLE : Primitive("java.lang.Double", 'D', "double", 8, priority = 0, subsets = setOf(BOOL, BYTE, CHAR, SHORT, INT)) data class REF(val name: FqName) : Reference(), AstRef { - constructor(name: String) : this(FqName(name)) + //constructor(name: String) : this(FqName(name)) + companion object { + operator fun invoke(name: String) = REF(FqName(name)) + } init { if (fqname.contains(';') || fqname.contains(']')) { @@ -345,7 +348,9 @@ fun _castLiteral(value: Int, to: AstType): Any = when (to) { AstType.FLOAT -> value.toFloat() AstType.DOUBLE -> value.toDouble() //is AstType.Reference -> null - else -> invalidOp("Can't cast $value to $to") + else -> { + invalidOp("Can't cast $value to $to") + } } fun Class.ref() = AstType.REF(this.name) @@ -370,9 +375,9 @@ fun _castLiteral(value: Long, to: AstType): Any = when (to) { fun _castLiteral(value: Float, to: AstType): Any = when (to) { AstType.BOOL -> value.toBool() - AstType.BYTE -> value.toByte() + AstType.BYTE -> value.toInt().toByte() AstType.CHAR -> value.toChar() - AstType.SHORT -> value.toShort() + AstType.SHORT -> value.toInt().toShort() AstType.INT -> value.toInt() AstType.LONG -> value.toLong() AstType.FLOAT -> value.toFloat() @@ -382,9 +387,9 @@ fun _castLiteral(value: Float, to: AstType): Any = when (to) { fun _castLiteral(value: Double, to: AstType): Any = when (to) { AstType.BOOL -> value.toBool() - AstType.BYTE -> value.toByte() + AstType.BYTE -> value.toInt().toByte() AstType.CHAR -> value.toChar() - AstType.SHORT -> value.toShort() + AstType.SHORT -> value.toInt().toShort() AstType.INT -> value.toInt() AstType.LONG -> value.toLong() AstType.FLOAT -> value.toFloat() @@ -436,6 +441,7 @@ data class AstArgument(val index: Int, val type: AstType, override val name: Str } data class FqName(val fqname: String) : Serializable { +//inline class FqName(val fqname: String) : Serializable { constructor(packagePath: String, simpleName: String) : this("$packagePath.$simpleName".trim('.')) constructor(packageParts: List, simpleName: String) : this("${packageParts.joinToString(".")}.$simpleName".trim('.')) @@ -449,7 +455,7 @@ data class FqName(val fqname: String) : Serializable { } init { - //if (fqname.isNullOrBlank()) invalidOp("Fqname is empty!") + if (fqname.isNullOrBlank()) invalidOp("Fqname is empty!") if (!fqname.isEmpty()) { val f = fqname.first() @@ -468,6 +474,11 @@ data class FqName(val fqname: String) : Serializable { val internalFqname by lazy { fqname.replace('.', '/') } val pathToClass by lazy { "$internalFqname.class" } + //val packagePath get() = fqname.substringBeforeLast('.', "") + //val simpleName get() = fqname.substringAfterLast('.') + //val internalFqname get() = fqname.replace('.', '/') + //val pathToClass get() = "$internalFqname.class" + fun withPackagePath(packagePath: String) = FqName(packagePath, simpleName) fun withPackageParts(packageParts: List) = FqName(packageParts, simpleName) fun withSimpleName(simpleName: String) = FqName(packagePath, simpleName) @@ -475,7 +486,6 @@ data class FqName(val fqname: String) : Serializable { override fun toString() = fqname override fun hashCode(): Int = fqname.hashCode() - override fun equals(other: Any?): Boolean = this.fqname == (other as? FqName)?.fqname fun append(s: String): FqName = FqName(this.fqname + s) diff --git a/jtransc-core/src/com/jtransc/ast/dependency/genStaticInitOrder.kt b/jtransc-core/src/com/jtransc/ast/dependency/genStaticInitOrder.kt index 43415741..2009ea56 100644 --- a/jtransc-core/src/com/jtransc/ast/dependency/genStaticInitOrder.kt +++ b/jtransc-core/src/com/jtransc/ast/dependency/genStaticInitOrder.kt @@ -78,7 +78,7 @@ fun genStaticInitOrder(program: AstProgram, plugins: JTranscPluginGroup) { } if (ref is AstType.REF) { - lockClass(program[ref]!!) + lockClass(program.get(ref, method)!!) } // First visit static constructor diff --git a/jtransc-core/src/com/jtransc/backend/AsmToAst.kt b/jtransc-core/src/com/jtransc/backend/AsmToAst.kt index d856e101..d0d8444d 100644 --- a/jtransc-core/src/com/jtransc/backend/AsmToAst.kt +++ b/jtransc-core/src/com/jtransc/backend/AsmToAst.kt @@ -42,14 +42,24 @@ abstract class BaseAsmToAst(val types: AstTypes, val settings: AstBuildSettings) extending = if (classNode.hasSuperclass() && !classNode.isInterface()) FqName.fromInternal(classNode.superName) else null, implementing = classNode.getInterfaces().map { FqName.fromInternal(it) } ) + + astClass.setAsmClassNode(classNode) program.add(astClass) - classNode.getMethods().withIndex().forEach { astClass.add(generateMethod(astClass, it.value)) } + //classNode.getMethods().withIndex().forEach { astClass.add(generateMethod(astClass, it.value)) } classNode.getFields().withIndex().forEach { astClass.add(generateField(astClass, it.value)) } return astClass } + override fun generateAndAddMethod(clazz: AstClass, ref: AstMethodWithoutClassRef): AstMethod { + val methodNode = clazz.classNodeMethods?.get(ref) ?: error("Can't find ref: $ref") + return clazz.methodsByNameDesc[ref] ?: generateMethod(clazz, methodNode).also { + clazz.add(it) + } + } + + fun generateMethod(containingClass: AstClass, method: MethodNode): AstMethod { val mods = AstModifiers(method.access) val methodRef = method.astRef(containingClass.ref, types) diff --git a/jtransc-core/src/com/jtransc/build.kt b/jtransc-core/src/com/jtransc/build.kt index 3f9b320d..6cb4c75a 100644 --- a/jtransc-core/src/com/jtransc/build.kt +++ b/jtransc-core/src/com/jtransc/build.kt @@ -17,6 +17,8 @@ package com.jtransc import com.jtransc.ast.* +import com.jtransc.ast.References.getClassReferences +import com.jtransc.ast.References.getMethodReferences import com.jtransc.ast.dependency.genStaticInitOrder import com.jtransc.ast.treeshaking.TreeShaking import com.jtransc.backend.asm1.AsmToAst1 @@ -33,6 +35,7 @@ import com.jtransc.log.log import com.jtransc.maven.MavenLocalRepository import com.jtransc.plugin.JTranscPlugin import com.jtransc.plugin.JTranscPluginGroup +import com.jtransc.plugin.reflection.* import com.jtransc.plugin.toGroup import com.jtransc.time.measureProcess import com.jtransc.time.measureTime @@ -190,43 +193,105 @@ class JTranscBuild( val program = injector.get() // Preprocesses classes - classNames.forEach { program.addReference(AstType.REF(it), AstType.REF(it)) } + classNames.forEach { + val clazzRef = AstType.REF(it) + program.addReference(AstMethodRef(clazzRef.name, "main", AstType.METHOD(AstType.VOID, listOf(AstType.ARRAY(AstType.STRING)))), clazzRef) + //program.addReference(AstType.REF(it), AstType.REF(it)) + } + log("Processing classes...") val targetName = TargetName(target.name) + val jruntime = Runtime.getRuntime() val (elapsed) = measureTime { plugins.onStartBuilding(program) - while (true) { - if (!program.hasClassToGenerate()) { - plugins.onAfterAllClassDiscovered(program) + var numProcessedClasses = 0 - if (!program.hasClassToGenerate()) { + //data class LongClassInfo(val className: AstType.REF, val timeMs: Long) + //val longClasses = arrayListOf() + + + if (true) { + while (true) { + if (!program.hasMethodToGenerate()) { break } - } - val className = program.readClassToGenerate() - log("Processing class... $className") - plugins.onAfterClassDiscovered(className, program) + val methodRef = program.readMethodToGenerate() + val methodRefWithoutClass = methodRef.withoutClass + + program.addReference(methodRef.classRef, methodRef.classRef) - val time = measureTime { - try { - val generatedClass = generator.generateClass(program, className.name) - for (ref in References.get(generatedClass, targetName)) { - //println("$ref : $className") - program.addReference(ref, className) + while (program.hasClassToGenerate()) { + val clazzRef = program.readClassToGenerate() + val classFqname = clazzRef.name + println("Exploring class... $clazzRef") + val clazz = program.getOrNull(classFqname) ?: generator.generateClass(program, classFqname) + for (impl in listOfNotNull(clazz.extending) + clazz.implementing) { + program.addReference(impl.ref, clazzRef) + } + val clinit = AstMethodWithoutClassRef.CLINIT + val clinitMethod = clazz.classNodeMethods?.get(clinit) + if (clinitMethod != null) { + program.addReference(clinit.withClass(clazzRef.name), clazzRef) } - } catch (e: InvalidOperationException) { - System.err.println("ERROR! : " + e.message) + //println(clazz.classNodeMethods) + } + + val classFqname = methodRef.classRef.name + println("Exploring method... $methodRef") + + val clazz = program.getOrNull(classFqname) ?: generator.generateClass(program, classFqname) + if (!clazz.hasBasicMethod(methodRefWithoutClass)) { + println(clazz.parentClass) + } + val method = generator.generateAndAddMethod(program, methodRef) + for (methodRef in method.getMethodReferences(targetName)) { + program.addReference(methodRef, clazz.ref) + //println("methodRef: $methodRef") } } + } else { + while (true) { + if (!program.hasClassToGenerate()) { + plugins.onAfterAllClassDiscovered(program) + + if (!program.hasClassToGenerate()) { + break + } + } + val className = program.readClassToGenerate() + log( + "Processing class [$numProcessedClasses]... $className : ${ + jruntime.freeMemory().toDouble() / jruntime.maxMemory() + }" + ) + + numProcessedClasses++ + + plugins.onAfterClassDiscovered(className, program) + + val time = measureTime { + try { + val generatedClass = generator.generateClass(program, className.name) + for (ref in References.get(generatedClass, targetName)) { + //println("$ref : $className") + program.addReference(ref, className) + } + } catch (e: InvalidOperationException) { + System.err.println("ERROR! : " + e.message) + } + } - //println("Ok(${time.time})"); + //if (time.time >= 100L) longClasses += LongClassInfo(className, time.time) + } } + //for (longClass in longClasses) println("Long Class: ${longClass.className.fqname}, timeMs=${longClass.timeMs}") + // Reference default methods for (clazz in program.classes) { for (method in clazz.allDirectInterfaces.flatMap { it.methods }) { diff --git a/jtransc-core/src/com/jtransc/plugin/reflection/MetaReflectionJTranscPlugin.kt b/jtransc-core/src/com/jtransc/plugin/reflection/MetaReflectionJTranscPlugin.kt index 614c09a6..fe8626a2 100644 --- a/jtransc-core/src/com/jtransc/plugin/reflection/MetaReflectionJTranscPlugin.kt +++ b/jtransc-core/src/com/jtransc/plugin/reflection/MetaReflectionJTranscPlugin.kt @@ -26,6 +26,8 @@ class MetaReflectionJTranscPlugin : JTranscPlugin() { fun AstField.mustReflect(): Boolean = this.visible && this.annotationsList.getRemoveFieldForTarget(targetName) == null override fun processAfterTreeShaking(program: AstProgram) { + return + // Do not generate if ProgramReflection class is not referenced! // Also methods are not updated in the case they do not exist! if (ProgramReflection::class.java.fqname !in program) return diff --git a/jtransc-core/src/com/jtransc/plugin/service/ServiceLoaderJTranscPlugin.kt b/jtransc-core/src/com/jtransc/plugin/service/ServiceLoaderJTranscPlugin.kt index d146755d..7ab70628 100644 --- a/jtransc-core/src/com/jtransc/plugin/service/ServiceLoaderJTranscPlugin.kt +++ b/jtransc-core/src/com/jtransc/plugin/service/ServiceLoaderJTranscPlugin.kt @@ -20,6 +20,7 @@ class ServiceLoaderJTranscPlugin : JTranscPlugin() { val SERVICE_LOADER_FQ = "java.util.ServiceLoader".fqname override fun onStartBuilding(program: AstProgram) { + return val targetName = program.injector.get() servicesToImpls.clear() referencedServices.clear() @@ -33,7 +34,8 @@ class ServiceLoaderJTranscPlugin : JTranscPlugin() { val servicesFolders = program.resourcesVfs["META-INF/services"].getUnmergedFiles().filter { it.exists && it.isDirectory } val targetRegex = Regex("]*)>") for (serviceListFile in servicesFolders.flatMap { it.listdir() }) { - val serviceName = serviceListFile.name + val serviceName = serviceListFile.name.trim() + if (serviceName.isEmpty()) continue if (serviceName !in servicesToImpls) servicesToImpls[serviceName] = listOf() for (line in serviceListFile.file.readString().trim().lines()) { val parts = line.split('#') @@ -66,6 +68,7 @@ class ServiceLoaderJTranscPlugin : JTranscPlugin() { log.info("Discovered used service: $clazz with impls $impls") //println(":: Discovered used service: $clazz with impls $impls") for (impl in impls) { + if (impl.isEmpty()) continue program.addReference(AstType.REF(impl.fqname), clazz) } } else { @@ -91,6 +94,7 @@ class ServiceLoaderJTranscPlugin : JTranscPlugin() { IF(Objects_equals(nameArg.expr, serviceName.lit)) { SET(out, ARRAY(OBJECT).newArray(impls.size)) for ((index, impl) in impls.withIndex()) { + if (impl.isEmpty()) continue //val ref = AstType.REF(impl.fqname) val clazz = programBase[impl.fqname] val emptyConstructor = clazz[AstMethodWithoutClassRef("", AstType.METHOD(AstType.VOID, listOf()))] ?: invalidOp("Can't find default constructor for service implementation $impl") 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 1e166841..24255f51 100644 --- a/jtransc-gen-cpp/src/com/jtransc/gen/cpp/CppTarget.kt +++ b/jtransc-gen-cpp/src/com/jtransc/gen/cpp/CppTarget.kt @@ -149,7 +149,7 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { } override val allowAssignItself = true - val lastClassId = program.classes.map { it.classId }.max() ?: 0 + val lastClassId = program.classes.map { it.classId }.maxOrNull() ?: 0 fun generateTypeTableHeader() = Indenter { line("struct TYPE_INFO", after2 = ";") { @@ -348,7 +348,7 @@ class CppGenerator(injector: Injector) : CommonGenerator(injector) { val STRINGS = Indenter { val globalStrings = getGlobalStrings() - val maxGlobalStrings = globalStrings.map { it.id }.max()?.plus(1) ?: 0 + val maxGlobalStrings = globalStrings.map { it.id }.maxOrNull()?.plus(1) ?: 0 line("__GC_thread_local static void* STRINGS_START = nullptr;") line("__GC_thread_local static ${JAVA_LANG_STRING_FQ.targetNameRef} STRINGLIT[$maxGlobalStrings] = {0};") line("__GC_thread_local static void* STRINGS_END = nullptr;") diff --git a/jtransc-main/src/com/jtransc/main.kt b/jtransc-main/src/com/jtransc/main.kt index 612f8fb4..83a95f85 100644 --- a/jtransc-main/src/com/jtransc/main.kt +++ b/jtransc-main/src/com/jtransc/main.kt @@ -27,6 +27,19 @@ import java.util.* object JTranscMain { @Suppress("CanBeVal") @JvmStatic fun main(pargs: Array) { + val pargs = arrayOf( + //"C:\\dev\\kotlinc\\lib\\t\\tools.jar", + "C:\\dev\\kotlinc\\lib\\t\\annotations-13.0.jar", + "C:\\dev\\kotlinc\\lib\\t\\kotlin-annotations-jvm.jar", + "C:\\dev\\kotlinc\\lib\\t\\kotlin-compiler-embeddable-1.5.10.jar", + "C:\\dev\\kotlinc\\lib\\t\\kotlin-stdlib-1.5.10.jar", + "C:\\dev\\kotlinc\\lib\\t\\kotlin-reflect-1.5.10.jar", + "C:\\dev\\kotlinc\\lib\\trove4j.jar", + "-main", "org.jetbrains.kotlin.cli.js.K2JSCompiler", + //"-main", "javaslang.λ", + "-out", "program.js" + ) + // @TODO: allow a plugin system val targets = AllBuildTargets val jtranscVersion = JTranscVersion.getVersion() diff --git a/jtransc-rt-core/src/com/jtransc/io/ra/RAStream.java b/jtransc-rt-core/src/com/jtransc/io/ra/RAStream.java index 8162e5e8..a154f7c7 100644 --- a/jtransc-rt-core/src/com/jtransc/io/ra/RAStream.java +++ b/jtransc-rt-core/src/com/jtransc/io/ra/RAStream.java @@ -133,12 +133,12 @@ public byte[] readBytes(long count) { } public String readStringz(int count, Charset charset) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream(count * 4); while (count > 0) { - int c = this.readS8_LE(); + int c = this.readU8_LE(); count--; - if (c <= 0) break; + if (c == 0) break; out.write(c); } @@ -147,6 +147,18 @@ public String readStringz(int count, Charset charset) { return new String(out.toByteArray(), charset); } + public String readString(int count, Charset charset) { + ByteArrayOutputStream out = new ByteArrayOutputStream(count); + + while (count > 0) { + int c = this.readU8_LE(); + count--; + out.write(c); + } + + return new String(out.toByteArray(), charset); + } + public RASlice readSlice(long count) { long actualCount = Math.min(count, getAvailable()); RASlice out = new RASlice(this, getPosition(), getPosition() + actualCount); diff --git a/jtransc-rt/src/java/util/Collection.java b/jtransc-rt/src/java/util/Collection.java index 1b5cff50..f304e57c 100644 --- a/jtransc-rt/src/java/util/Collection.java +++ b/jtransc-rt/src/java/util/Collection.java @@ -18,6 +18,8 @@ package java.util; +import java.util.function.Predicate; + /** * {@code Collection} is the root of the collection hierarchy. It defines operations on * data collections and the behavior that they will have in all implementations @@ -313,4 +315,17 @@ public interface Collection extends Iterable { * stored in the type of the specified array. */ public T[] toArray(T[] array); + + default boolean removeIf(Predicate filter) { + Objects.requireNonNull(filter); + boolean removed = false; + final Iterator each = iterator(); + while (each.hasNext()) { + if (filter.test(each.next())) { + each.remove(); + removed = true; + } + } + return removed; + } } diff --git a/jtransc-utils/src/com/jtransc/imaging/ImagePropsDecoder.kt b/jtransc-utils/src/com/jtransc/imaging/ImagePropsDecoder.kt index 8b91872a..f19911d1 100644 --- a/jtransc-utils/src/com/jtransc/imaging/ImagePropsDecoder.kt +++ b/jtransc-utils/src/com/jtransc/imaging/ImagePropsDecoder.kt @@ -105,7 +105,7 @@ object PNGImagePropsDecoder : ImagePropsDecoder { object JPEGImagePropsDecoder : ImagePropsDecoder { override fun tryDecodeHeader(ss: InputStream): ImageInfo? { try { - val s = ss.readBytes(ss.available()).open() + val s = ss.readBytes().open() val magic = s.readU16_BE() if (magic != 0xFFD8) return null while (!s.eof()) { diff --git a/jtransc-utils/src/com/jtransc/lang/Dynamic.kt b/jtransc-utils/src/com/jtransc/lang/Dynamic.kt index bcd8b645..9e300562 100644 --- a/jtransc-utils/src/com/jtransc/lang/Dynamic.kt +++ b/jtransc-utils/src/com/jtransc/lang/Dynamic.kt @@ -208,7 +208,7 @@ object Dynamic { } fun String?.parseInt(): Int = this?.parseDouble()?.toInt() ?: 0 - fun String?.parseShort(): Short = this?.parseDouble()?.toShort() ?: 0 + fun String?.parseShort(): Short = this?.parseDouble()?.toInt()?.toShort() ?: 0 fun String?.parseLong(): Long = try { this?.toLong() } catch (e: Throwable) { diff --git a/jtransc-utils/src/com/jtransc/sourcemaps/sourcemaps.kt b/jtransc-utils/src/com/jtransc/sourcemaps/sourcemaps.kt index 572722a2..e3b5bea4 100644 --- a/jtransc-utils/src/com/jtransc/sourcemaps/sourcemaps.kt +++ b/jtransc-utils/src/com/jtransc/sourcemaps/sourcemaps.kt @@ -91,7 +91,7 @@ object Sourcemaps { fun encodeFile(targetPath: String, targetContent: String, source: String, mappings: HashMap): String { //(0 until targetContent.count { it == '\n' }).map {} - val mapping = MappingFile((0 until (mappings.keys.max() ?: 1)).map { + val mapping = MappingFile((0 until (mappings.keys.maxOrNull() ?: 1)).map { val targetLine = mappings[it] MappingRow(if (targetLine == null) listOf() else listOf(MappingItem(0, targetLine, 0, 0))) }) @@ -105,7 +105,7 @@ object Sourcemaps { } fun encodeFile(files: List, mappings: Map): String { - val mapping = MappingFile((0 until (mappings.keys.max() ?: 1)).map { + val mapping = MappingFile((0 until (mappings.keys.maxOrNull() ?: 1)).map { val item = mappings[it] MappingRow(if (item == null) listOf() else listOf(item)) }) diff --git a/jtransc-utils/src/com/jtransc/vfs/FileExt.kt b/jtransc-utils/src/com/jtransc/vfs/FileExt.kt index 736afcd3..2a10f452 100644 --- a/jtransc-utils/src/com/jtransc/vfs/FileExt.kt +++ b/jtransc-utils/src/com/jtransc/vfs/FileExt.kt @@ -10,6 +10,6 @@ fun File.ensureExists() { fun ClassLoader.getResourceBytes(name: String): ByteArray { return this.getResourceAsStream(name).use { stream -> - stream.readBytes(stream.available()) + stream.readBytes() } -} \ No newline at end of file +} diff --git a/jtransc-utils/src/com/jtransc/vfs/syncvfs.kt b/jtransc-utils/src/com/jtransc/vfs/syncvfs.kt index 173e2183..40c58e88 100644 --- a/jtransc-utils/src/com/jtransc/vfs/syncvfs.kt +++ b/jtransc-utils/src/com/jtransc/vfs/syncvfs.kt @@ -166,7 +166,7 @@ class SyncVfsFile(internal val vfs: SyncVfs, val path: String) { operator fun set(path: String, content: SyncVfsFile) = set(path, content.readBytes()) operator fun set(path: String, content: ByteArray) { val file = access(path).ensureParentDir() - if (!file.exists || file.read() != content) { + if (!file.exists || !file.read().contentEquals(content)) { file.write(content) } } diff --git a/jtransc-utils/src/com/jtransc/vfs/zipvfs.kt b/jtransc-utils/src/com/jtransc/vfs/zipvfs.kt index c4d25a1f..6152528b 100644 --- a/jtransc-utils/src/com/jtransc/vfs/zipvfs.kt +++ b/jtransc-utils/src/com/jtransc/vfs/zipvfs.kt @@ -97,9 +97,13 @@ fun ZipVfs(s: RAStream, zipFile: SyncVfsFile? = null): SyncVfsFile { val internalAttributes = readU16_LE() val externalAttributes = readS32_LE() val headerOffset = readS32_LE() - val name = readStringz(fileNameLength, Charsets.UTF_8) + val name = readString(fileNameLength, Charsets.UTF_8) val extra = readBytes(extraLength) + //if (name.startsWith("javaslang")) { + // println("name=$name : ${fileNameLength}") + //} + val isDirectory = name.endsWith("/") val normalizedName = name.normalizeName() @@ -208,6 +212,10 @@ fun ZipVfs(s: RAStream, zipFile: SyncVfsFile? = null): SyncVfsFile { */ override fun stat(path: String): SyncVfsStat { + if (path.startsWith("javaslang")) { + val keys = files.keys.filter { it.startsWith("javaslang") } + println(keys) + } return files[path.normalizeName()].toStat(this@Impl[path]) }