diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderJSSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderJSSupport.java index f4f9a5c97..4108b9e96 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderJSSupport.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderJSSupport.java @@ -16,10 +16,10 @@ package org.teavm.classlib.impl; import java.io.IOException; -import java.util.Collection; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.GeneratorContext; +import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory; import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; @@ -28,39 +28,31 @@ public class ServiceLoaderJSSupport implements Generator { @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { - ServiceLoaderInformation information = context.getService(ServiceLoaderInformation.class); - writer.append("if (!").appendClass("java.util.ServiceLoader").append(".$$services$$) {").indent() - .softNewLine(); - writer.appendClass("java.util.ServiceLoader").append(".$$services$$ = true;").softNewLine(); - for (String serviceType : information.serviceTypes()) { - writer.appendClass(serviceType).append(".$$serviceList$$ = ["); - Collection implementations = information.serviceImplementations(serviceType); - boolean first = true; - for (String implName : implementations) { - if (context.getClassSource().getClassNames().contains(implName)) { - if (!first) { - writer.append(", "); + var templateFactory = new JavaScriptTemplateFactory(context.getClassLoader(), context.getClassSource()); + var template = templateFactory.createFromResource("org/teavm/classlib/java/util/ServiceLoader.js"); + var information = context.getService(ServiceLoaderInformation.class); + var fragment = template.builder("loadServices") + .withContext(context) + .withFragment("fillServices", (w, precedence) -> { + for (var serviceType : information.serviceTypes()) { + writer.appendClass(serviceType).append(".$$serviceList$$ = ["); + var implementations = information.serviceImplementations(serviceType); + boolean first = true; + for (var implName : implementations) { + if (context.getClassSource().getClassNames().contains(implName)) { + if (!first) { + writer.append(",").ws(); + } + first = false; + writer.append("[").appendClass(implName).append(",").ws() + .appendMethodBody(new MethodReference(implName, INIT_METHOD)) + .append("]"); + } + } + writer.append("];").softNewLine(); } - first = false; - writer.append("[").appendClass(implName).append(", ").appendMethodBody( - new MethodReference(implName, INIT_METHOD)) - .append("]"); - } - } - writer.append("];").softNewLine(); - } - writer.outdent().append("}").softNewLine(); - String param = context.getParameterName(1); - writer.append("var cls = " + param + ";").softNewLine(); - writer.append("if (!cls.$$serviceList$$) {").indent().softNewLine(); - writer.append("return $rt_createArray($rt_objcls(), 0);").softNewLine(); - writer.outdent().append("}").softNewLine(); - writer.append("var result = $rt_createArray($rt_objcls(), cls.$$serviceList$$.length);").softNewLine(); - writer.append("for (var i = 0; i < result.data.length; ++i) {").indent().softNewLine(); - writer.append("var serviceDesc = cls.$$serviceList$$[i];").softNewLine(); - writer.append("result.data[i] = new serviceDesc[0]();").softNewLine(); - writer.append("serviceDesc[1](result.data[i]);").softNewLine(); - writer.outdent().append("}").softNewLine(); - writer.append("return result;").softNewLine(); + }) + .build(); + fragment.write(writer, 0); } } diff --git a/classlib/src/main/java/org/teavm/classlib/impl/string/JSStringInjector.java b/classlib/src/main/java/org/teavm/classlib/impl/string/JSStringInjector.java index 74f7b9a43..ac6d3a223 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/string/JSStringInjector.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/string/JSStringInjector.java @@ -122,7 +122,7 @@ public class JSStringInjector implements Injector, Function 0) { writer.append(",").ws(); diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayDependencyPlugin.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayDependencyPlugin.java new file mode 100644 index 000000000..a90d3580e --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayDependencyPlugin.java @@ -0,0 +1,111 @@ +/* + * Copyright 2023 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.lang.reflect; + +import java.util.HashSet; +import java.util.Set; +import org.teavm.dependency.DependencyAgent; +import org.teavm.dependency.DependencyPlugin; +import org.teavm.dependency.MethodDependency; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; + +public class ArrayDependencyPlugin implements DependencyPlugin { + private static final String[] primitives = { "Byte", "Short", "Char", "Int", "Long", "Float", "Double", + "Boolean" }; + private static final String[] primitiveWrappers = { "Byte", "Short", "Character", "Integer", "Long", + "Float", "Double", "Boolean" }; + private static final ValueType[] primitiveTypes = { ValueType.BYTE, ValueType.SHORT, ValueType.CHARACTER, + ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN }; + + private Set reachedMethods = new HashSet<>(); + + @Override + public void methodReached(DependencyAgent agent, MethodDependency method) { + if (!reachedMethods.add(method.getReference())) { + return; + } + switch (method.getReference().getName()) { + case "getLength": + reachGetLength(agent, method); + break; + case "newInstance": + method.getVariable(1).getClassValueNode().addConsumer(t -> { + String arrayTypeName; + if (t.getName().startsWith("[")) { + arrayTypeName = t.getName(); + } else if (t.getName().startsWith("~")) { + arrayTypeName = t.getName().substring(1); + } else { + arrayTypeName = ValueType.object(t.getName()).toString(); + } + if (!arrayTypeName.startsWith("[[[")) { + method.getResult().propagate(agent.getType("[" + arrayTypeName)); + } + }); + break; + case "getImpl": + reachGet(agent, method); + break; + case "setImpl": + reachSet(agent, method); + break; + } + } + + private void reachGetLength(DependencyAgent agent, MethodDependency method) { + method.getVariable(1).addConsumer(type -> { + if (!type.getName().startsWith("[")) { + MethodReference cons = new MethodReference(IllegalArgumentException.class, "", void.class); + agent.linkMethod(cons).use(); + } + }); + } + private void reachGet(DependencyAgent agent, MethodDependency method) { + method.getVariable(1).getArrayItem().connect(method.getResult()); + method.getVariable(1).addConsumer(type -> { + if (type.getName().startsWith("[")) { + String typeName = type.getName().substring(1); + for (int i = 0; i < primitiveTypes.length; ++i) { + if (primitiveTypes[i].toString().equals(typeName)) { + String wrapper = "java.lang." + primitiveWrappers[i]; + MethodReference methodRef = new MethodReference(wrapper, "valueOf", + primitiveTypes[i], ValueType.object(wrapper)); + agent.linkMethod(methodRef).use(); + method.getResult().propagate(agent.getType("java.lang." + primitiveWrappers[i])); + } + } + } + }); + } + + private void reachSet(DependencyAgent agent, MethodDependency method) { + method.getVariable(3).connect(method.getVariable(1).getArrayItem()); + method.getVariable(1).addConsumer(type -> { + if (type.getName().startsWith("[")) { + String typeName = type.getName().substring(1); + for (int i = 0; i < primitiveTypes.length; ++i) { + if (primitiveTypes[i].toString().equals(typeName)) { + String wrapper = "java.lang." + primitiveWrappers[i]; + MethodReference methodRef = new MethodReference(wrapper, + primitives[i].toLowerCase() + "Value", primitiveTypes[i]); + agent.linkMethod(methodRef).use(); + } + } + } + }); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java index 206f9e29b..1712cef98 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java @@ -16,190 +16,22 @@ package org.teavm.classlib.java.lang.reflect; import java.io.IOException; -import java.util.HashSet; -import java.util.Set; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.GeneratorContext; -import org.teavm.dependency.DependencyAgent; -import org.teavm.dependency.DependencyPlugin; -import org.teavm.dependency.MethodDependency; -import org.teavm.model.ClassReader; +import org.teavm.backend.javascript.templating.JavaScriptTemplate; +import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory; import org.teavm.model.MethodReference; -import org.teavm.model.ValueType; -public class ArrayNativeGenerator implements Generator, DependencyPlugin { - private static final String[] primitives = { "Byte", "Short", "Char", "Int", "Long", "Float", "Double", - "Boolean" }; - private static final String[] primitiveWrappers = { "Byte", "Short", "Character", "Integer", "Long", - "Float", "Double", "Boolean" }; - private static final ValueType[] primitiveTypes = { ValueType.BYTE, ValueType.SHORT, ValueType.CHARACTER, - ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN }; +public class ArrayNativeGenerator implements Generator { + private JavaScriptTemplate template; - private Set reachedMethods = new HashSet<>(); - - @Override - public void methodReached(DependencyAgent agent, MethodDependency method) { - if (!reachedMethods.add(method.getReference())) { - return; - } - switch (method.getReference().getName()) { - case "getLength": - reachGetLength(agent, method); - break; - case "newInstance": - method.getVariable(1).getClassValueNode().addConsumer(t -> { - String arrayTypeName; - if (t.getName().startsWith("[")) { - arrayTypeName = t.getName(); - } else if (t.getName().startsWith("~")) { - arrayTypeName = t.getName().substring(1); - } else { - arrayTypeName = ValueType.object(t.getName()).toString(); - } - if (!arrayTypeName.startsWith("[[[")) { - method.getResult().propagate(agent.getType("[" + arrayTypeName)); - } - }); - break; - case "getImpl": - reachGet(agent, method); - break; - case "setImpl": - reachSet(agent, method); - break; - } + public ArrayNativeGenerator(JavaScriptTemplateFactory templateFactory) throws IOException { + template = templateFactory.createFromResource("org/teavm/classlib/java/lang/reflect/Array.js"); } @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { - switch (methodRef.getName()) { - case "getLength": - generateGetLength(context, writer); - break; - case "newInstanceImpl": - generateNewInstance(context, writer); - break; - case "getImpl": - generateGet(context, writer); - break; - case "setImpl": - generateSet(context, writer); - break; - } - } - - private void generateGetLength(GeneratorContext context, SourceWriter writer) throws IOException { - String array = context.getParameterName(1); - writer.append("if (" + array + " === null || " + array + ".constructor.$meta.item === undefined) {") - .softNewLine().indent(); - MethodReference cons = new MethodReference("java.lang.IllegalArgumentException", "", ValueType.VOID); - writer.append("$rt_throw(").appendInit(cons).append("());").softNewLine(); - writer.outdent().append("}").softNewLine(); - writer.append("return " + array + ".data.length;").softNewLine(); - } - - private void reachGetLength(DependencyAgent agent, MethodDependency method) { - method.getVariable(1).addConsumer(type -> { - if (!type.getName().startsWith("[")) { - MethodReference cons = new MethodReference(IllegalArgumentException.class, "", void.class); - agent.linkMethod(cons).use(); - } - }); - } - - private void generateNewInstance(GeneratorContext context, SourceWriter writer) throws IOException { - String type = context.getParameterName(1); - String length = context.getParameterName(2); - writer.append("if (").append(type).append(".$meta.primitive) {").softNewLine().indent(); - for (String primitive : primitives) { - writer.append("if (" + type + " == $rt_" + primitive.toLowerCase() + "cls) {").indent().softNewLine(); - writer.append("return $rt_create" + primitive + "Array(" + length + ");").softNewLine(); - writer.outdent().append("}").softNewLine(); - } - writer.outdent().append("} else {").indent().softNewLine(); - writer.append("return $rt_createArray(" + type + ", " + length + ")").softNewLine(); - writer.outdent().append("}").softNewLine(); - } - - private void generateGet(GeneratorContext context, SourceWriter writer) throws IOException { - String array = context.getParameterName(1); - writer.append("var item = " + array + ".data[" + context.getParameterName(2) + "];").softNewLine(); - writer.append("var type = " + array + ".constructor.$meta.item;").softNewLine(); - for (int i = 0; i < primitives.length; ++i) { - String wrapper = "java.lang." + primitiveWrappers[i]; - MethodReference methodRef = new MethodReference(wrapper, "valueOf", - primitiveTypes[i], ValueType.object(wrapper)); - ClassReader cls = context.getClassSource().get(methodRef.getClassName()); - if (cls == null || cls.getMethod(methodRef.getDescriptor()) == null) { - continue; - } - writer.append("if (type === $rt_" + primitives[i].toLowerCase() + "cls) {").indent().softNewLine(); - writer.append("return ").appendMethodBody(methodRef).append("(item);").softNewLine(); - writer.outdent().append("} else "); - } - writer.append("{").indent().softNewLine(); - writer.append("return item;").softNewLine(); - writer.outdent().append("}").softNewLine(); - } - - private void generateSet(GeneratorContext context, SourceWriter writer) throws IOException { - String array = context.getParameterName(1); - String item = context.getParameterName(3); - writer.append("var type = " + array + ".constructor.$meta.item;").softNewLine(); - boolean first = true; - for (int i = 0; i < primitives.length; ++i) { - String wrapper = "java.lang." + primitiveWrappers[i]; - MethodReference methodRef = new MethodReference(wrapper, primitives[i].toLowerCase() + "Value", - primitiveTypes[i]); - ClassReader cls = context.getClassSource().get(methodRef.getClassName()); - if (cls == null || cls.getMethod(methodRef.getDescriptor()) == null) { - continue; - } - if (!first) { - writer.append(" else "); - } - first = false; - writer.append("if (type === $rt_" + primitives[i].toLowerCase() + "cls) {").indent().softNewLine(); - writer.append(item + " = ").appendMethodBody(methodRef).append("(" + item + ");").softNewLine(); - writer.outdent().append("}"); - } - writer.softNewLine(); - writer.append(array + ".data[" + context.getParameterName(2) + "] = " + item + ";").softNewLine(); - } - - private void reachGet(DependencyAgent agent, MethodDependency method) { - method.getVariable(1).getArrayItem().connect(method.getResult()); - method.getVariable(1).addConsumer(type -> { - if (type.getName().startsWith("[")) { - String typeName = type.getName().substring(1); - for (int i = 0; i < primitiveTypes.length; ++i) { - if (primitiveTypes[i].toString().equals(typeName)) { - String wrapper = "java.lang." + primitiveWrappers[i]; - MethodReference methodRef = new MethodReference(wrapper, "valueOf", - primitiveTypes[i], ValueType.object(wrapper)); - agent.linkMethod(methodRef).use(); - method.getResult().propagate(agent.getType("java.lang." + primitiveWrappers[i])); - } - } - } - }); - } - - private void reachSet(DependencyAgent agent, MethodDependency method) { - method.getVariable(3).connect(method.getVariable(1).getArrayItem()); - method.getVariable(1).addConsumer(type -> { - if (type.getName().startsWith("[")) { - String typeName = type.getName().substring(1); - for (int i = 0; i < primitiveTypes.length; ++i) { - if (primitiveTypes[i].toString().equals(typeName)) { - String wrapper = "java.lang." + primitiveWrappers[i]; - MethodReference methodRef = new MethodReference(wrapper, - primitives[i].toLowerCase() + "Value", primitiveTypes[i]); - agent.linkMethod(methodRef).use(); - } - } - } - }); + template.builder(methodRef.getName()).withContext(context).build().write(writer, 0); } } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java index 911b66df9..99197e583 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java @@ -33,7 +33,7 @@ import org.teavm.runtime.RuntimeObject; public final class TArray extends TObject { @GeneratedBy(ArrayNativeGenerator.class) - @PluggableDependency(ArrayNativeGenerator.class) + @PluggableDependency(ArrayDependencyPlugin.class) @DelegateTo("getLengthLowLevel") @NoSideEffects public static native int getLength(TObject array) throws TIllegalArgumentException; @@ -48,7 +48,7 @@ public final class TArray extends TObject { return array.size; } - @PluggableDependency(ArrayNativeGenerator.class) + @PluggableDependency(ArrayDependencyPlugin.class) public static TObject newInstance(Class componentType, int length) throws TNegativeArraySizeException { if (componentType == null) { throw new TNullPointerException(); @@ -89,12 +89,12 @@ public final class TArray extends TObject { } @GeneratedBy(ArrayNativeGenerator.class) - @PluggableDependency(ArrayNativeGenerator.class) + @PluggableDependency(ArrayDependencyPlugin.class) @NoSideEffects private static native TObject getImpl(TObject array, int index); @GeneratedBy(ArrayNativeGenerator.class) - @PluggableDependency(ArrayNativeGenerator.class) + @PluggableDependency(ArrayDependencyPlugin.class) @NoSideEffects private static native void setImpl(TObject array, int index, TObject value); } diff --git a/classlib/src/main/resources/org/teavm/classlib/java/lang/ref/ReferenceQueue.js b/classlib/src/main/resources/org/teavm/classlib/java/lang/ref/ReferenceQueue.js new file mode 100644 index 000000000..11aeeabc4 --- /dev/null +++ b/classlib/src/main/resources/org/teavm/classlib/java/lang/ref/ReferenceQueue.js @@ -0,0 +1,33 @@ +/* + * Copyright 2023 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function init() { + this[teavm_javaField("java.lang.ref.ReferenceQueue", "inner")] = []; + if (teavm_javaMethodExists("java.lang.ref.ReferenceQueue", "reportNext(Ljava/lang/ref/Reference;)Z")) { + this[teavm_javaField("java.lang.ref.ReferenceQueue", "registry")] = + new (teavm_globals.FinalizationRegistry)(ref => { + if (!teavm_javaMethod("java.lang.ref.ReferenceQueue", + "reportNext(Ljava/lang/ref/Reference;)Z")(this, ref)) { + this[teavm_javaField("java.lang.ref.ReferenceQueue", "inner")].push(ref); + } + }); + } +} + +function poll() { + var value = this[teavm_javaField("java.lang.ref.ReferenceQueue", "inner")]; + return typeof value !== 'undefined' ? value : null; +} \ No newline at end of file diff --git a/classlib/src/main/resources/org/teavm/classlib/java/lang/ref/WeakReference.js b/classlib/src/main/resources/org/teavm/classlib/java/lang/ref/WeakReference.js new file mode 100644 index 000000000..c6a23f526 --- /dev/null +++ b/classlib/src/main/resources/org/teavm/classlib/java/lang/ref/WeakReference.js @@ -0,0 +1,41 @@ +/* + * Copyright 2023 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function init(target, queue) { + let supported = typeof teavm_globals.WeakRef !== 'undefined'; + let value = supported ? new teavm_globals.WeakRef(target) : target; + this[teavm_javaField("java.lang.ref.WeakReference", "value")] = value; + if (queue !== null && supported) { + let registry = queue[teavm_javaField("java.lang.ref.ReferenceQueue", "registry")]; + if (registry !== null) { + registry.register(target, this); + } + } +} +function get() { + let value = this[teavm_javaField("java.lang.ref.WeakReference", "value")]; + if (typeof teavm_globals.WeakRef !== 'undefined') { + if (value === null) { + return null; + } + let result = value.deref(); + return typeof result !== 'undefined' ? result : null; + } + return value; +} +function clear() { + this[teavm_javaField("java.lang.ref.WeakReference", "value")] = null; +} \ No newline at end of file diff --git a/classlib/src/main/resources/org/teavm/classlib/java/lang/reflect/Array.js b/classlib/src/main/resources/org/teavm/classlib/java/lang/reflect/Array.js new file mode 100644 index 000000000..a20f53807 --- /dev/null +++ b/classlib/src/main/resources/org/teavm/classlib/java/lang/reflect/Array.js @@ -0,0 +1,86 @@ +/* + * Copyright 2023 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function getLength(array) { + if (array === null || array.constructor.$meta.item === 'undefined') { + $rt_throw(teavm_javaConstructor("java.lang.IllegalArgumentException", "()V")()); + } + return array.data.length; +} + +function newInstanceImpl(type, length) { + if (type.$meta.primitive) { + switch (type) { + case $rt_booleanArrayCls: return $rt_createBooleanArray(length); + case $rt_byteArrayCls: return $rt_createByteArray(length); + case $rt_shortArrayCls: return $rt_createShortArray(length); + case $rt_charArrayCls: return $rt_createCharArray(length); + case $rt_intArrayCls: return $rt_createIntArray(length); + case $rt_longArrayCls: return $rt_createLongArray(length); + case $rt_floatArrayCls: return $rt_createFloatArray(length); + case $rt_doubleArrayCls: return $rt_createDoubleArray(length); + } + } + return $rt_createArray(type, length); +} + +function getImpl(array, index) { + var item = array.data[index]; + var type = array.constructor.$meta.item; + if (teavm_javaMethodExists("java.lang.Boolean", "valueOf(Z)Ljava/lang/Boolean;") && type === $rt_booleancls) { + return teavm_javaMethod("java.lang.Boolean", "valueOf(Z)Ljava/lang/Boolean;")(item); + } else if (teavm_javaMethodExists("java.lang.Byte", "valueOf(B)Ljava/lang/Byte;") && type === $rt_bytecls) { + return teavm_javaMethod("java.lang.Byte", "valueOf(B)Ljava/lang/Byte;")(item); + } else if (teavm_javaMethodExists("java.lang.Short", "valueOf(S)Ljava/lang/Short;") && type === $rt_shortcls) { + return teavm_javaMethod("java.lang.Short", "valueOf(S)Ljava/lang/Short;")(item); + } else if (teavm_javaMethodExists("java.lang.Character", "valueOf(C)Ljava/lang/Character;") + && type === $rt_charcls) { + return teavm_javaMethod("java.lang.Character", "valueOf(C)Ljava/lang/Character;")(item); + } else if (teavm_javaMethodExists("java.lang.Integer", "valueOf(I)Ljava/lang/Integer;") && type === $rt_intcls) { + return teavm_javaMethod("java.lang.Integer", "valueOf(I)Ljava/lang/Integer;")(item); + } else if (teavm_javaMethodExists("java.lang.Long", "valueOf(J)Ljava/lang/Long;") && type === $rt_longcls) { + return teavm_javaMethod("java.lang.Long", "valueOf(J)Ljava/lang/Long;")(item); + } else if (teavm_javaMethodExists("java.lang.Float", "valueOf(F)Ljava/lang/Float;") && type === $rt_floatcls) { + return teavm_javaMethod("java.lang.Float", "valueOf(F)Ljava/lang/Float;")(item); + } else if (teavm_javaMethodExists("java.lang.Double", "valueOf(D)Ljava/lang/Double;") && type === $rt_doublecls) { + return teavm_javaMethod("java.lang.Double", "valueOf(D)Ljava/lang/Double;")(item); + } else { + return item; + } +} + +function setImpl(array, index, value) { + var type = array.constructor.$meta.item; + if (teavm_javaMethodExists("java.lang.Boolean", "booleanValue()Z") && type === $rt_booleancls) { + array.data[index] = teavm_javaMethod("java.lang.Boolean", "booleanValue()Z")(value); + } else if (teavm_javaMethodExists("java.lang.Byte", "byteValue()B") && type === $rt_booleancls) { + array.data[index] = teavm_javaMethod("java.lang.Byte", "byteValue()B")(value); + } else if (teavm_javaMethodExists("java.lang.Short", "shortValue()S") && type === $rt_shortcls) { + array.data[index] = teavm_javaMethod("java.lang.Short", "shortValue()S")(value); + } else if (teavm_javaMethodExists("java.lang.Character", "charValue()C") && type === $rt_charcls) { + array.data[index] = teavm_javaMethod("java.lang.Character", "charValue()C")(value); + } else if (teavm_javaMethodExists("java.lang.Integer", "intValue()I") && type === $rt_intcls) { + array.data[index] = teavm_javaMethod("java.lang.Integer", "intValue()I")(value); + } else if (teavm_javaMethodExists("java.lang.Long", "longValue()J") && type === $rt_longcls) { + array.data[index] = teavm_javaMethod("java.lang.Long", "longValue()J")(value); + } else if (teavm_javaMethodExists("java.lang.Float", "floatValue()F") && type === $rt_floatcls) { + array.data[index] = teavm_javaMethod("java.lang.Float", "floatValue()F")(value); + } else if (teavm_javaMethodExists("java.lang.Double", "doubleValue()D") && type === $rt_floatcls) { + array.data[index] = teavm_javaMethod("java.lang.Double", "doubleValue()D")(value); + } else { + array.data[index] = value; + } +} \ No newline at end of file diff --git a/classlib/src/main/resources/org/teavm/classlib/java/util/ServiceLoader.js b/classlib/src/main/resources/org/teavm/classlib/java/util/ServiceLoader.js new file mode 100644 index 000000000..c1ba4aa35 --- /dev/null +++ b/classlib/src/main/resources/org/teavm/classlib/java/util/ServiceLoader.js @@ -0,0 +1,38 @@ +/* + * Copyright 2023 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function loadServices(cls) { + let serviceLoader = teavm_javaClass("java.util.ServiceLoader"); + if (!serviceLoader.$$services$$) { + serviceLoader.$$services$$ = true; + teavm_fragment("fillServices"); + } + + let serviceList = cls.$$serviceList$$; + if (!serviceList) { + return $rt_createArray($rt_objcls(), 0); + } + + let result = $rt_createArray($rt_objcls(), serviceList.length); + for (let i = 0; i < serviceList.length; ++i) { + let serviceDesc = serviceList[i]; + let instance = new serviceDesc[0](); + serviceDesc[1](instance); + result.data[i] = instance; + } + + return result; +} diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index 9c7eee7df..33fccaac5 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -171,7 +171,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { public void setController(TeaVMTargetController controller) { this.controller = controller; - var weakRefGenerator = new WeakReferenceGenerator(); + templateFactory = new JavaScriptTemplateFactory(controller.getClassLoader(), + controller.getDependencyInfo().getClassSource()); + + var weakRefGenerator = new WeakReferenceGenerator(templateFactory); methodGenerators.put(new MethodReference(WeakReference.class, "", Object.class, ReferenceQueue.class, void.class), weakRefGenerator); methodGenerators.put(new MethodReference(WeakReference.class, "get", Object.class), weakRefGenerator); @@ -180,9 +183,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { var refQueueGenerator = new ReferenceQueueGenerator(); methodGenerators.put(new MethodReference(ReferenceQueue.class, "", void.class), refQueueGenerator); methodGenerators.put(new MethodReference(ReferenceQueue.class, "poll", Reference.class), refQueueGenerator); - - templateFactory = new JavaScriptTemplateFactory(controller.getClassLoader(), - controller.getDependencyInfo().getClassSource()); } @Override @@ -480,7 +480,6 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { if (renderer.isLongLibraryUsed()) { runtimeRenderer.renderHandWrittenRuntime("long.js"); - renderer.renderLongRuntimeAliases(); } if (renderer.isThreadLibraryUsed()) { runtimeRenderer.renderHandWrittenRuntime("thread.js"); @@ -491,10 +490,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { for (var entry : controller.getEntryPoints().entrySet()) { sourceWriter.append("$rt_exports.").append(entry.getKey()).ws().append("=").ws(); var ref = entry.getValue().getMethod(); - sourceWriter.append("$rt_mainStarter(").appendMethodBody(ref); + sourceWriter.appendFunction("$rt_mainStarter").append("(").appendMethodBody(ref); sourceWriter.append(");").newLine(); sourceWriter.append("$rt_exports.").append(entry.getKey()).append(".").append("javaException") - .ws().append("=").ws().append("$rt_javaException;").newLine(); + .ws().append("=").ws().appendFunction("$rt_javaException").append(";").newLine(); } for (var listener : rendererListeners) { diff --git a/core/src/main/java/org/teavm/backend/javascript/intrinsics/ref/ReferenceQueueGenerator.java b/core/src/main/java/org/teavm/backend/javascript/intrinsics/ref/ReferenceQueueGenerator.java index ed5d3222b..6866f55c7 100644 --- a/core/src/main/java/org/teavm/backend/javascript/intrinsics/ref/ReferenceQueueGenerator.java +++ b/core/src/main/java/org/teavm/backend/javascript/intrinsics/ref/ReferenceQueueGenerator.java @@ -16,56 +16,33 @@ package org.teavm.backend.javascript.intrinsics.ref; import java.io.IOException; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.GeneratorContext; -import org.teavm.model.FieldReference; +import org.teavm.backend.javascript.templating.JavaScriptTemplate; +import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory; import org.teavm.model.MethodReference; public class ReferenceQueueGenerator implements Generator { - private static final FieldReference INNER_FIELD = new FieldReference(ReferenceQueue.class.getName(), "inner"); - private static final FieldReference REGISTRY_FIELD = new FieldReference(ReferenceQueue.class.getName(), - "registry"); - private static final MethodReference REPORT_METHOD = new MethodReference(ReferenceQueue.class, - "reportNext", Reference.class, boolean.class); + private JavaScriptTemplate template; @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { + ensureTemplate(context); switch (methodRef.getName()) { case "": - generateInitMethod(context, writer); + template.builder("init").withContext(context).build().write(writer, 0); break; case "poll": - generatePollMethod(context, writer); + template.builder("poll").withContext(context).build().write(writer, 0); break; } } - private void generateInitMethod(GeneratorContext context, SourceWriter writer) throws IOException { - writer.append(context.getParameterName(0)).append(".").appendField(INNER_FIELD).ws().append("=") - .ws().append("[];").softNewLine(); - - if (context.getDependency().getMethod(REPORT_METHOD) != null) { - generateFinalizationRegistry(context, writer); + private void ensureTemplate(GeneratorContext context) throws IOException { + if (template == null) { + template = new JavaScriptTemplateFactory(context.getClassLoader(), context.getClassSource()) + .createFromResource("org/teavm/classlib/java/lang/ref/ReferenceQueue.js"); } } - - private void generateFinalizationRegistry(GeneratorContext context, SourceWriter writer) throws IOException { - writer.append(context.getParameterName(0)).append(".").appendField(REGISTRY_FIELD).ws().append("=") - .ws().append("new $rt_globals.FinalizationRegistry(ref").ws().append("=>").appendBlockStart(); - writer.appendIf().append("!").appendMethodBody(REPORT_METHOD).append("(") - .append(context.getParameterName(0)).append(",").ws().append("ref))").ws(); - writer.append(context.getParameterName(0)).append(".").appendField(INNER_FIELD) - .append(".push(ref)").softNewLine(); - writer.appendBlockEnd().append(");").softNewLine(); - } - - private void generatePollMethod(GeneratorContext context, SourceWriter writer) throws IOException { - writer.append("var value").ws().append("=").ws().append(context.getParameterName(0)) - .append(".").appendField(INNER_FIELD).append(".shift();").softNewLine(); - writer.append("return typeof value").ws().append("!==").ws().append("'undefined'").ws() - .append("?").ws().append("value").ws().append(":").ws().append("null;").softNewLine(); - } } diff --git a/core/src/main/java/org/teavm/backend/javascript/intrinsics/ref/WeakReferenceGenerator.java b/core/src/main/java/org/teavm/backend/javascript/intrinsics/ref/WeakReferenceGenerator.java index 10474c4b1..f4eece43d 100644 --- a/core/src/main/java/org/teavm/backend/javascript/intrinsics/ref/WeakReferenceGenerator.java +++ b/core/src/main/java/org/teavm/backend/javascript/intrinsics/ref/WeakReferenceGenerator.java @@ -16,82 +16,34 @@ package org.teavm.backend.javascript.intrinsics.ref; import java.io.IOException; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; import org.teavm.backend.javascript.codegen.SourceWriter; import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.GeneratorContext; -import org.teavm.model.FieldReference; +import org.teavm.backend.javascript.templating.JavaScriptTemplate; +import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory; import org.teavm.model.MethodReference; public class WeakReferenceGenerator implements Generator { + private JavaScriptTemplate template; + + public WeakReferenceGenerator(JavaScriptTemplateFactory templateFactory) { + try { + template = templateFactory.createFromResource("org/teavm/classlib/java/lang/ref/WeakReference.js"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { case "": - generateConstructor(context, writer); + template.builder("init").withContext(context).build().write(writer, 0); break; case "get": - generateGet(context, writer); - break; case "clear": - generateClear(context, writer); + template.builder(methodRef.getName()).withContext(context).build().write(writer, 0); break; } } - - private void generateConstructor(GeneratorContext context, SourceWriter writer) throws IOException { - writer.append("var supported").ws().append("=").ws(); - isSupported(writer).append(";").softNewLine(); - writer.append("var value").ws().append("=").ws().append("supported").ws() - .append("?").ws().append("new $rt_globals.WeakRef(") - .append(context.getParameterName(1)).append(")").ws(); - writer.append(":").ws().append(context.getParameterName(0)).append(";").softNewLine(); - - writer.append(context.getParameterName(0)).append(".") - .appendField(new FieldReference(WeakReference.class.getName(), "value")) - .ws().append("=").ws().append("value;").softNewLine(); - - writer.appendIf().append(context.getParameterName(2)).ws().append("!==").ws().append("null") - .ws().append("&&").ws().append("supported)") - .appendBlockStart(); - - writer.append("var registry").ws().append("=").ws() - .append(context.getParameterName(2)).append(".") - .appendField(new FieldReference(ReferenceQueue.class.getName(), "registry")).append(";") - .softNewLine(); - writer.appendIf().append("registry").ws().append("!==").ws().append("null)").ws(); - writer.append("registry.register(").append(context.getParameterName(1)) - .append(",").ws().append(context.getParameterName(0)).append(");").softNewLine(); - - writer.appendBlockEnd(); - } - - private void generateGet(GeneratorContext context, SourceWriter writer) throws IOException { - writer.append("var value").ws().append("=").ws().append(context.getParameterName(0)).append(".") - .appendField(new FieldReference(WeakReference.class.getName(), "value")) - .append(";").softNewLine(); - - writer.appendIf(); - isSupported(writer).append(")").appendBlockStart(); - writer.appendIf().append("value").ws().append("===").ws().append("null)") - .ws().append("return null;").softNewLine(); - writer.append("var result").ws().append("=").ws().append("value.deref();").softNewLine(); - writer.append("return typeof result").ws().append("!==").ws().append("'undefined'") - .ws().append("?").ws().append("result").ws().append(":").ws().append("null;").softNewLine(); - writer.appendElse(); - writer.append("return value;").softNewLine(); - writer.appendBlockEnd(); - } - - private void generateClear(GeneratorContext context, SourceWriter writer) throws IOException { - writer.append(context.getParameterName(0)).append(".") - .appendField(new FieldReference(WeakReference.class.getName(), "value")).ws(); - writer.append("=").ws().append("null;").softNewLine(); - } - - private SourceWriter isSupported(SourceWriter writer) throws IOException { - return writer.append("typeof ").append("$rt_globals.WeakRef").ws().append("!==").ws() - .append("'undefined'"); - } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java b/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java index 50af72307..9f2522891 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/DefaultGlobalNameWriter.java @@ -28,7 +28,7 @@ public class DefaultGlobalNameWriter implements Function { @Override public NameEmitter apply(String s) { if (s.startsWith("$rt_") || s.startsWith("Long_") || s.equals("Long")) { - return prec -> writer.append(s); + return prec -> writer.appendFunction(s); } return prec -> writer.append("$rt_globals").append('.').append(s); } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index c7da72beb..3c3a68d1f 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -183,7 +183,7 @@ public class Renderer implements RenderingManager { } try { int start = writer.getOffset(); - writer.append("$rt_stringPool(["); + writer.appendFunction("$rt_stringPool").append("(["); for (int i = 0; i < context.getStringPool().size(); ++i) { if (i > 0) { writer.append(',').ws(); @@ -223,9 +223,9 @@ public class Renderer implements RenderingManager { } private void renderJavaStringToString() throws IOException { - writer.appendClass("java.lang.String").append(".prototype.toString").ws().append("=").ws() - .append("function()").ws().append("{").indent().softNewLine(); - writer.append("return $rt_ustr(this);").softNewLine(); + writer.appendClass("java.lang.String").append(".prototype.toString").ws().append("=").ws().append("()") + .ws().append("=>").ws().append("{").indent().softNewLine(); + writer.append("return ").appendFunction("$rt_ustr").append("(this);").softNewLine(); writer.outdent().append("};").newLine(); writer.appendClass("java.lang.String").append(".prototype.valueOf").ws().append("=").ws() .appendClass("java.lang.String").append(".prototype.toString;").softNewLine(); @@ -233,8 +233,9 @@ public class Renderer implements RenderingManager { private void renderJavaObjectToString() throws IOException { writer.appendClass("java.lang.Object").append(".prototype.toString").ws().append("=").ws() - .append("function()").ws().append("{").indent().softNewLine(); - writer.append("return $rt_ustr(").appendMethodBody(Object.class, "toString", String.class).append("(this));") + .append("()").ws().append("=>").ws().append("{").indent().softNewLine(); + writer.append("return ").appendFunction("$rt_ustr").append("(") + .appendMethodBody(Object.class, "toString", String.class).append("(this));") .softNewLine(); writer.outdent().append("};").newLine(); } @@ -242,7 +243,7 @@ public class Renderer implements RenderingManager { private void renderTeaVMClass() throws IOException { writer.appendClass("java.lang.Object").append(".prototype.__teavm_class__").ws().append("=").ws() .append("function()").ws().append("{").indent().softNewLine(); - writer.append("return $dbg_class(this);").softNewLine(); + writer.append("return ").appendFunction("$dbg_class").append("(this);").softNewLine(); writer.outdent().append("};").newLine(); } @@ -250,42 +251,6 @@ public class Renderer implements RenderingManager { sizeByClass.put(className, sizeByClass.getOrDefault(className, 0) + sz); } - private void renderCommonRuntimeAliases() throws IOException { - renderRuntimeAliases("$rt_throw", "$rt_compare", "$rt_nullCheck", "$rt_cls", "$rt_createArray", - "$rt_isInstance", "$rt_nativeThread", "$rt_suspending", "$rt_resuming", "$rt_invalidPointer", - "$rt_s", "$rt_eraseClinit", "$rt_imul", "$rt_wrapException", "$rt_checkBounds", - "$rt_checkUpperBound", "$rt_checkLowerBound", "$rt_wrapFunction0", "$rt_wrapFunction1", - "$rt_wrapFunction2", "$rt_wrapFunction3", "$rt_wrapFunction4", - "$rt_classWithoutFields", "$rt_createArrayFromData", "$rt_createCharArrayFromData", - "$rt_createByteArrayFromData", "$rt_createShortArrayFromData", "$rt_createIntArrayFromData", - "$rt_createBooleanArrayFromData", "$rt_createFloatArrayFromData", "$rt_createDoubleArrayFromData", - "$rt_createLongArrayFromData", "$rt_createBooleanArray", "$rt_createByteArray", - "$rt_createShortArray", "$rt_createCharArray", "$rt_createIntArray", "$rt_createLongArray", - "$rt_createFloatArray", "$rt_createDoubleArray", "$rt_compare", - "$rt_castToClass", "$rt_castToInterface", "$rt_equalDoubles", - "$rt_str", "Long_toNumber", "Long_fromInt", "Long_fromNumber", "Long_create", "Long_ZERO", - "$rt_intern", "$rt_substring", "$rt_ustr", - "Long_hi", "Long_lo"); - } - - public void renderLongRuntimeAliases() throws IOException { - renderRuntimeAliases("Long_add", "Long_sub", "Long_mul", "Long_div", "Long_rem", "Long_or", "Long_and", - "Long_xor", "Long_shl", "Long_shr", "Long_shru", "Long_compare", "Long_eq", "Long_ne", - "Long_lt", "Long_le", "Long_gt", "Long_ge", "Long_not", "Long_neg"); - } - - private void renderRuntimeAliases(String... names) throws IOException { - boolean first = true; - for (String name : names) { - if (!first) { - writer.softNewLine(); - } - first = false; - writer.append("var ").appendFunction(name).ws().append('=').ws().append(name).append(";").softNewLine(); - } - writer.newLine(); - } - public void prepare(List classes) { if (minifying) { NamingOrderer orderer = new NamingOrderer(); @@ -300,13 +265,6 @@ public class Renderer implements RenderingManager { } public boolean render(List classes) throws RenderingException { - if (minifying) { - try { - renderCommonRuntimeAliases(); - } catch (IOException e) { - throw new RenderingException(e); - } - } int index = 0; for (PreparedClass cls : classes) { int start = writer.getOffset(); @@ -527,7 +485,7 @@ public class Renderer implements RenderingManager { int start = writer.getOffset(); try { - writer.append("$rt_packages(["); + writer.appendFunction("$rt_packages").append("(["); ObjectIntMap packageIndexes = generatePackageMetadata(classes, metadataRequirements); writer.append("]);").newLine(); @@ -545,7 +503,7 @@ public class Renderer implements RenderingManager { private void renderClassMetadataPortion(List classes, ObjectIntMap packageIndexes, ClassMetadataRequirements metadataRequirements) throws IOException { - writer.append("$rt_metadata(["); + writer.appendFunction("$rt_metadata").append("(["); boolean first = true; for (PreparedClass cls : classes) { if (!first) { diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java index dc800b3f1..f32af0897 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java @@ -305,40 +305,40 @@ public abstract class RenderingContext { } for (int i = 0; i < arrayCount; ++i) { - writer.append("$rt_arraycls("); + writer.appendFunction("$rt_arraycls").append("("); } if (type instanceof ValueType.Object) { ValueType.Object objType = (ValueType.Object) type; writer.appendClass(objType.getClassName()); } else if (type instanceof ValueType.Void) { - writer.append("$rt_voidcls"); + writer.appendFunction("$rt_voidcls"); } else if (type instanceof ValueType.Primitive) { ValueType.Primitive primitiveType = (ValueType.Primitive) type; switch (primitiveType.getKind()) { case BOOLEAN: - writer.append("$rt_booleancls"); + writer.appendFunction("$rt_booleancls"); break; case CHARACTER: - writer.append("$rt_charcls"); + writer.appendFunction("$rt_charcls"); break; case BYTE: - writer.append("$rt_bytecls"); + writer.appendFunction("$rt_bytecls"); break; case SHORT: - writer.append("$rt_shortcls"); + writer.appendFunction("$rt_shortcls"); break; case INTEGER: - writer.append("$rt_intcls"); + writer.appendFunction("$rt_intcls"); break; case LONG: - writer.append("$rt_longcls"); + writer.appendFunction("$rt_longcls"); break; case FLOAT: - writer.append("$rt_floatcls"); + writer.appendFunction("$rt_floatcls"); break; case DOUBLE: - writer.append("$rt_doublecls"); + writer.appendFunction("$rt_doublecls"); break; default: throw new IllegalArgumentException("The type is not renderable"); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index 0f65b6c2e..41739ad6b 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -1414,32 +1414,32 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { if (type instanceof ValueType.Primitive) { switch (((ValueType.Primitive) type).getKind()) { case BOOLEAN: - writer.append("$rt_createBooleanMultiArray("); + writer.appendFunction("$rt_createBooleanMultiArray").append("("); break; case BYTE: - writer.append("$rt_createByteMultiArray("); + writer.appendFunction("$rt_createByteMultiArray").append("("); break; case SHORT: - writer.append("$rt_createShortMultiArray("); + writer.appendFunction("$rt_createShortMultiArray").append("("); break; case INTEGER: - writer.append("$rt_createIntMultiArray("); + writer.appendFunction("$rt_createIntMultiArray").append("("); break; case LONG: - writer.append("$rt_createLongMultiArray("); + writer.appendFunction("$rt_createLongMultiArray").append("("); break; case FLOAT: - writer.append("$rt_createFloatMultiArray("); + writer.appendFunction("$rt_createFloatMultiArray").append("("); break; case DOUBLE: - writer.append("$rt_createDoubleMultiArray("); + writer.appendFunction("$rt_createDoubleMultiArray").append("("); break; case CHARACTER: - writer.append("$rt_createCharMultiArray("); + writer.appendFunction("$rt_createCharMultiArray").append("("); break; } } else { - writer.append("$rt_createMultiArray("); + writer.appendFunction("$rt_createMultiArray").append("("); context.typeToClsString(writer, type); writer.append(",").ws(); } diff --git a/core/src/main/java/org/teavm/backend/javascript/templating/JavaScriptTemplate.java b/core/src/main/java/org/teavm/backend/javascript/templating/JavaScriptTemplate.java index d106be0e0..c111a599d 100644 --- a/core/src/main/java/org/teavm/backend/javascript/templating/JavaScriptTemplate.java +++ b/core/src/main/java/org/teavm/backend/javascript/templating/JavaScriptTemplate.java @@ -16,6 +16,7 @@ package org.teavm.backend.javascript.templating; import java.util.HashMap; +import java.util.Map; import java.util.function.IntFunction; import org.mozilla.javascript.ast.AstNode; import org.mozilla.javascript.ast.FunctionNode; @@ -42,6 +43,7 @@ public class JavaScriptTemplate { public static class FragmentBuilder { private FunctionNode node; private IntFunction parameters; + private Map fragments = new HashMap<>(); private FragmentBuilder(FunctionNode node) { this.node = node; @@ -53,7 +55,12 @@ public class JavaScriptTemplate { } public FragmentBuilder withContext(GeneratorContext context) { - return withParameters(param -> (writer, precedence) -> writer.append(context.getParameterName(param + 1))); + return withParameters(param -> (writer, precedence) -> writer.append(context.getParameterName(param))); + } + + public FragmentBuilder withFragment(String name, SourceFragment fragment) { + fragments.put(name, fragment); + return this; } public SourceFragment build() { @@ -62,13 +69,16 @@ public class JavaScriptTemplate { for (var i = 0; i < node.getParams().size(); ++i) { var param = node.getParams().get(i); if (param instanceof Name) { - nameParameters.put(((Name) param).getIdentifier(), intParameters.apply(i)); + nameParameters.put(((Name) param).getIdentifier(), intParameters.apply(i + 1)); } } var thisFragment = parameters.apply(0); var body = node.getBody(); return (writer, precedence) -> { var astWriter = new TemplatingAstWriter(writer, nameParameters, node); + for (var entry : fragments.entrySet()) { + astWriter.setFragment(entry.getKey(), entry.getValue()); + } if (node.getSymbolTable() != null) { for (var name : node.getSymbolTable().keySet()) { astWriter.currentScopes.put(name, node); diff --git a/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java b/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java index f5fe26ab3..b109e3cf8 100644 --- a/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java +++ b/core/src/main/java/org/teavm/backend/javascript/templating/TemplatingAstWriter.java @@ -16,6 +16,7 @@ package org.teavm.backend.javascript.templating; import java.io.IOException; +import java.util.HashMap; import java.util.Map; import org.mozilla.javascript.ast.ElementGet; import org.mozilla.javascript.ast.FunctionCall; @@ -34,6 +35,7 @@ import org.teavm.model.MethodReference; public class TemplatingAstWriter extends AstWriter { private Map names; private Scope scope; + private Map fragments = new HashMap<>(); public TemplatingAstWriter(SourceWriter writer, Map names, Scope scope) { super(writer, new DefaultGlobalNameWriter(writer)); @@ -49,6 +51,10 @@ public class TemplatingAstWriter extends AstWriter { } } + public void setFragment(String name, SourceFragment fragment) { + fragments.put(name, fragment); + } + @Override protected boolean intrinsic(FunctionCall node, int precedence) throws IOException { if (node.getTarget() instanceof Name) { @@ -70,6 +76,8 @@ public class TemplatingAstWriter extends AstWriter { return writeJavaConstructor(node); case "teavm_javaClassInit": return writeJavaClassInit(node); + case "teavm_fragment": + return writeFragment(node); default: return false; } @@ -129,6 +137,19 @@ public class TemplatingAstWriter extends AstWriter { return true; } + private boolean writeFragment(FunctionCall node) throws IOException { + if (node.getArguments().size() != 1) { + return false; + } + var fragmentArg = node.getArguments().get(0); + if (!(fragmentArg instanceof StringLiteral)) { + return false; + } + var fragment = fragments.get(((StringLiteral) fragmentArg).getValue()); + fragment.write(writer, AstWriter.PRECEDENCE_COMMA + 1); + return true; + } + @Override protected void print(ElementGet node) throws IOException { if (node.getElement() instanceof FunctionCall) { @@ -209,7 +230,7 @@ public class TemplatingAstWriter extends AstWriter { return; } } - if (definingScope == null && scope != null) { + if (definingScope == null) { writer.appendFunction(node.getIdentifier()); return; } diff --git a/core/src/main/resources/org/teavm/backend/javascript/intern.js b/core/src/main/resources/org/teavm/backend/javascript/intern.js index 6df4f1211..1e4bccd47 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/intern.js +++ b/core/src/main/resources/org/teavm/backend/javascript/intern.js @@ -18,11 +18,11 @@ var $rt_intern if (teavm_javaMethodExists("java.lang.String", "intern()Ljava/lang/String;")) { $rt_intern = function() { - var map = Object.create(null); + var map = teavm_globals.Object.create(null); var get; - if (typeof WeakRef !== 'undefined') { - var registry = new FinalizationRegistry(value => { + if (typeof teavm_globals.WeakRef !== 'undefined') { + var registry = new teavm_globals.FinalizationRegistry(value => { delete map[value]; }); @@ -32,7 +32,7 @@ if (teavm_javaMethodExists("java.lang.String", "intern()Ljava/lang/String;")) { var result = typeof ref !== 'undefined' ? ref.deref() : void 0; if (typeof result !== 'object') { result = str; - map[key] = new WeakRef(result); + map[key] = new teavm_globals.WeakRef(result); registry.register(result, key); } return result; diff --git a/core/src/main/resources/org/teavm/backend/javascript/long.js b/core/src/main/resources/org/teavm/backend/javascript/long.js index abc00c957..b63261ad5 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/long.js +++ b/core/src/main/resources/org/teavm/backend/javascript/long.js @@ -41,7 +41,7 @@ var Long_shr; var Long_shru; var Long_not; -if (typeof BigInt !== 'function') { +if (typeof teavm_globals.BigInt !== 'function') { Long_eq = function(a, b) { return a.hi === b.hi && a.lo === b.lo; } @@ -267,7 +267,7 @@ if (typeof BigInt !== 'function') { function Long_divRem(a, b) { if (b.lo === 0 && b.hi === 0) { - throw new Error("Division by zero"); + throw new teavm_globals.Error("Division by zero"); } var positive = Long_isNegative(a) === Long_isNegative(b); if (Long_isNegative(a)) { @@ -286,7 +286,7 @@ if (typeof BigInt !== 'function') { function Long_udivRem(a, b) { if (b.lo === 0 && b.hi === 0) { - throw new Error("Division by zero"); + throw new teavm_globals.Error("Division by zero"); } a = new LongInt(a.lo, a.hi, 0); b = new LongInt(b.lo, b.hi, 0); @@ -599,79 +599,81 @@ if (typeof BigInt !== 'function') { } Long_add = function(a, b) { - return BigInt.asIntN(64, a + b); + return teavm_globals.BigInt.asIntN(64, a + b); } Long_inc = function(a) { - return BigInt.asIntN(64, a + 1); + return teavm_globals.BigInt.asIntN(64, a + 1); } Long_dec = function(a) { - return BigInt.asIntN(64, a - 1); + return teavm_globals.BigInt.asIntN(64, a - 1); } Long_neg = function(a) { - return BigInt.asIntN(64, -a); + return teavm_globals.BigInt.asIntN(64, -a); } Long_sub = function(a, b) { - return BigInt.asIntN(64, a - b); + return teavm_globals.BigInt.asIntN(64, a - b); } Long_compare = function(a, b) { return a < b ? -1 : a > b ? 1 : 0; } Long_ucompare = function(a, b) { - a = BigInt.asUintN(64, a); - b = BigInt.asUintN(64, b); + a = teavm_globals.BigInt.asUintN(64, a); + b = teavm_globals.BigInt.asUintN(64, b); return a < b ? -1 : a > b ? 1 : 0; } Long_mul = function(a, b) { - return BigInt.asIntN(64, a * b); + return teavm_globals.BigInt.asIntN(64, a * b); } Long_div = function(a, b) { - return BigInt.asIntN(64, a / b); + return teavm_globals.BigInt.asIntN(64, a / b); } Long_udiv = function(a, b) { - return BigInt.asIntN(64, BigInt.asUintN(64, a) / BigInt.asUintN(64, b)); + return teavm_globals.BigInt.asIntN(64, teavm_globals.BigInt.asUintN(64, a) / + teavm_globals.BigInt.asUintN(64, b)); } Long_rem = function(a, b) { - return BigInt.asIntN(64, a % b); + return teavm_globals.BigInt.asIntN(64, a % b); } Long_urem = function(a, b) { - return BigInt.asIntN(64, BigInt.asUintN(64, a) % BigInt.asUintN(64, b)); + return teavm_globals.BigInt.asIntN(64, teavm_globals.BigInt.asUintN(64, a) % + teavm_globals.BigInt.asUintN(64, b)); } Long_and = function(a, b) { - return BigInt.asIntN(64, a & b); + return teavm_globals.BigInt.asIntN(64, a & b); } Long_or = function(a, b) { - return BigInt.asIntN(64, a | b); + return teavm_globals.BigInt.asIntN(64, a | b); } Long_xor = function(a, b) { - return BigInt.asIntN(64, a ^ b); + return teavm_globals.BigInt.asIntN(64, a ^ b); } Long_shl = function(a, b) { - return BigInt.asIntN(64, a << BigInt(b & 63)); + return teavm_globals.BigInt.asIntN(64, a << teavm_globals.BigInt(b & 63)); } Long_shr = function(a, b) { - return BigInt.asIntN(64, a >> BigInt(b & 63)); + return teavm_globals.BigInt.asIntN(64, a >> teavm_globals.BigInt(b & 63)); } Long_shru = function(a, b) { - return BigInt.asIntN(64, BigInt.asUintN(64, a) >> BigInt(b & 63)); + return teavm_globals.BigInt.asIntN(64, teavm_globals.BigInt.asUintN(64, a) >> teavm_globals.BigInt(b & 63)); } Long_not = function(a) { - return BigInt.asIntN(64, ~a); + return teavm_globals.BigInt.asIntN(64, ~a); } } diff --git a/core/src/main/resources/org/teavm/backend/javascript/runtime.js b/core/src/main/resources/org/teavm/backend/javascript/runtime.js index 21ab7bc39..cdb6fc501 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/runtime.js +++ b/core/src/main/resources/org/teavm/backend/javascript/runtime.js @@ -57,7 +57,7 @@ function $rt_castToClass(obj, cls) { return obj; } function $rt_createArray(cls, sz) { - var data = new Array(sz); + var data = new teavm_globals.Array(sz); data.fill(null); return new ($rt_arraycls(cls))(data); } @@ -68,13 +68,13 @@ function $rt_wrapArray(cls, data) { return new ($rt_arraycls(cls))(data); } function $rt_createUnfilledArray(cls, sz) { - return new ($rt_arraycls(cls))(new Array(sz)); + return new ($rt_arraycls(cls))(new teavm_globals.Array(sz)); } var $rt_createLongArray; var $rt_createLongArrayFromData; -if (typeof BigInt64Array !== 'function') { +if (typeof teavm_globals.BigInt64Array !== 'function') { $rt_createLongArray = function(sz) { - var data = new Array(sz); + var data = new teavm_globals.Array(sz); var arr = new $rt_longArrayCls(data); data.fill(Long_ZERO); return arr; @@ -84,69 +84,69 @@ if (typeof BigInt64Array !== 'function') { } } else { $rt_createLongArray = function (sz) { - return new $rt_longArrayCls(new BigInt64Array(sz)); + return new $rt_longArrayCls(new teavm_globals.BigInt64Array(sz)); } $rt_createLongArrayFromData = function(data) { - var buffer = new BigInt64Array(data.length); + var buffer = new teavm_globals.BigInt64Array(data.length); buffer.set(data); return new $rt_longArrayCls(buffer); } } function $rt_createCharArray(sz) { - return new $rt_charArrayCls(new Uint16Array(sz)); + return new $rt_charArrayCls(new teavm_globals.Uint16Array(sz)); } function $rt_createCharArrayFromData(data) { - var buffer = new Uint16Array(data.length); + var buffer = new teavm_globals.Uint16Array(data.length); buffer.set(data); return new $rt_charArrayCls(buffer); } function $rt_createByteArray(sz) { - return new $rt_byteArrayCls(new Int8Array(sz)); + return new $rt_byteArrayCls(new teavm_globals.Int8Array(sz)); } function $rt_createByteArrayFromData(data) { - var buffer = new Int8Array(data.length); + var buffer = new teavm_globals.Int8Array(data.length); buffer.set(data); return new $rt_byteArrayCls(buffer); } function $rt_createShortArray(sz) { - return new $rt_shortArrayCls(new Int16Array(sz)); + return new $rt_shortArrayCls(new teavm_globals.Int16Array(sz)); } function $rt_createShortArrayFromData(data) { - var buffer = new Int16Array(data.length); + var buffer = new teavm_globals.Int16Array(data.length); buffer.set(data); return new $rt_shortArrayCls(buffer); } function $rt_createIntArray(sz) { - return new $rt_intArrayCls(new Int32Array(sz)); + return new $rt_intArrayCls(new teavm_globals.Int32Array(sz)); } function $rt_createIntArrayFromData(data) { - var buffer = new Int32Array(data.length); + var buffer = new teavm_globals.Int32Array(data.length); buffer.set(data); return new $rt_intArrayCls(buffer); } function $rt_createBooleanArray(sz) { - return new $rt_booleanArrayCls(new Int8Array(sz)); + return new $rt_booleanArrayCls(new teavm_globals.Int8Array(sz)); } function $rt_createBooleanArrayFromData(data) { - var buffer = new Int8Array(data.length); + var buffer = new teavm_globals.Int8Array(data.length); buffer.set(data); return new $rt_booleanArrayCls(buffer); } function $rt_createFloatArray(sz) { - return new $rt_floatArrayCls(new Float32Array(sz)); + return new $rt_floatArrayCls(new teavm_globals.Float32Array(sz)); } function $rt_createFloatArrayFromData(data) { - var buffer = new Float32Array(data.length); + var buffer = new teavm_globals.Float32Array(data.length); buffer.set(data); return new $rt_floatArrayCls(buffer); } function $rt_createDoubleArray(sz) { - return new $rt_doubleArrayCls(new Float64Array(sz)); + return new $rt_doubleArrayCls(new teavm_globals.Float64Array(sz)); } function $rt_createDoubleArrayFromData(data) { - var buffer = new Float64Array(data.length); + var buffer = new teavm_globals.Float64Array(data.length); buffer.set(data); return new $rt_doubleArrayCls(buffer); } @@ -158,7 +158,7 @@ function $rt_arraycls(cls) { $rt_objcls().call(this); this.data = data; } - JavaArray.prototype = Object.create($rt_objcls().prototype); + JavaArray.prototype = teavm_globals.Object.create($rt_objcls().prototype); JavaArray.prototype.type = cls; JavaArray.prototype.constructor = JavaArray; JavaArray.prototype.toString = function() { @@ -239,16 +239,16 @@ var $rt_voidcls = $rt_createPrimitiveCls("void", "V"); function $rt_throw(ex) { throw $rt_exception(ex); } -var $rt_javaExceptionProp = Symbol("javaException") +var $rt_javaExceptionProp = teavm_globals.Symbol("javaException") function $rt_exception(ex) { var err = ex.$jsException; if (!err) { var javaCause = $rt_throwableCause(ex); - var jsCause = javaCause !== null ? javaCause.$jsException : undefined; - var cause = typeof jsCause === "object" ? { cause : jsCause } : undefined; + var jsCause = javaCause !== null ? javaCause.$jsException : void 0; + var cause = typeof jsCause === "object" ? { cause : jsCause } : void 0; err = new JavaError("Java exception thrown", cause); - if (typeof Error.captureStackTrace === "function") { - Error.captureStackTrace(err); + if (typeof teavm_globals.Error.captureStackTrace === "function") { + teavm_globals.Error.captureStackTrace(err); } err[$rt_javaExceptionProp] = ex; ex.$jsException = err; @@ -293,7 +293,7 @@ function $rt_createMultiArray(cls, dimensions) { return $rt_createArray(cls, dimensions[first]); } } - var arrays = new Array($rt_primitiveArrayCount(dimensions, first)); + var arrays = new teavm_globals.Array($rt_primitiveArrayCount(dimensions, first)); var firstDim = dimensions[first] | 0; for (i = 0; i < arrays.length; i = (i + 1) | 0) { arrays[i] = $rt_createArray(cls, firstDim); @@ -301,7 +301,7 @@ function $rt_createMultiArray(cls, dimensions) { return $rt_createMultiArrayImpl(cls, arrays, dimensions, first); } function $rt_createByteMultiArray(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions, 0)); + var arrays = new teavm_globals.Array($rt_primitiveArrayCount(dimensions, 0)); if (arrays.length === 0) { return $rt_createMultiArray($rt_bytecls, dimensions); } @@ -312,7 +312,7 @@ function $rt_createByteMultiArray(dimensions) { return $rt_createMultiArrayImpl($rt_bytecls, arrays, dimensions); } function $rt_createCharMultiArray(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions, 0)); + var arrays = new teavm_globals.Array($rt_primitiveArrayCount(dimensions, 0)); if (arrays.length === 0) { return $rt_createMultiArray($rt_charcls, dimensions); } @@ -323,7 +323,7 @@ function $rt_createCharMultiArray(dimensions) { return $rt_createMultiArrayImpl($rt_charcls, arrays, dimensions, 0); } function $rt_createBooleanMultiArray(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions, 0)); + var arrays = new teavm_globals.Array($rt_primitiveArrayCount(dimensions, 0)); if (arrays.length === 0) { return $rt_createMultiArray($rt_booleancls, dimensions); } @@ -334,7 +334,7 @@ function $rt_createBooleanMultiArray(dimensions) { return $rt_createMultiArrayImpl($rt_booleancls, arrays, dimensions, 0); } function $rt_createShortMultiArray(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions, 0)); + var arrays = new teavm_globals.Array($rt_primitiveArrayCount(dimensions, 0)); if (arrays.length === 0) { return $rt_createMultiArray($rt_shortcls, dimensions); } @@ -345,7 +345,7 @@ function $rt_createShortMultiArray(dimensions) { return $rt_createMultiArrayImpl($rt_shortcls, arrays, dimensions, 0); } function $rt_createIntMultiArray(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions, 0)); + var arrays = new teavm_globals.Array($rt_primitiveArrayCount(dimensions, 0)); if (arrays.length === 0) { return $rt_createMultiArray($rt_intcls, dimensions); } @@ -356,7 +356,7 @@ function $rt_createIntMultiArray(dimensions) { return $rt_createMultiArrayImpl($rt_intcls, arrays, dimensions, 0); } function $rt_createLongMultiArray(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions, 0)); + var arrays = new teavm_globals.Array($rt_primitiveArrayCount(dimensions, 0)); if (arrays.length === 0) { return $rt_createMultiArray($rt_longcls, dimensions); } @@ -367,7 +367,7 @@ function $rt_createLongMultiArray(dimensions) { return $rt_createMultiArrayImpl($rt_longcls, arrays, dimensions, 0); } function $rt_createFloatMultiArray(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions, 0)); + var arrays = new teavm_globals.Array($rt_primitiveArrayCount(dimensions, 0)); if (arrays.length === 0) { return $rt_createMultiArray($rt_floatcls, dimensions); } @@ -378,7 +378,7 @@ function $rt_createFloatMultiArray(dimensions) { return $rt_createMultiArrayImpl($rt_floatcls, arrays, dimensions, 0); } function $rt_createDoubleMultiArray(dimensions) { - var arrays = new Array($rt_primitiveArrayCount(dimensions, 0)); + var arrays = new teavm_globals.Array($rt_primitiveArrayCount(dimensions, 0)); if (arrays.length === 0) { return $rt_createMultiArray($rt_doublecls, dimensions); } @@ -477,7 +477,7 @@ var $rt_putStderr = typeof $rt_putStderrCustom === "function" var $rt_packageData = null; function $rt_packages(data) { var i = 0; - var packages = new Array(data.length); + var packages = new teavm_globals.Array(data.length); for (var j = 0; j < data.length; ++j) { var prefixIndex = data[i++]; var prefix = prefixIndex >= 0 ? packages[prefixIndex] : ""; @@ -508,7 +508,7 @@ function $rt_metadata(data) { m.supertypes = data[i++]; if (m.superclass) { m.supertypes.push(m.superclass); - cls.prototype = Object.create(m.superclass.prototype); + cls.prototype = teavm_globals.Object.create(m.superclass.prototype); } else { cls.prototype = {}; } @@ -583,7 +583,7 @@ function $rt_wrapFunction4(f) { } function $rt_threadStarter(f) { return function() { - var args = Array.prototype.slice.apply(arguments); + var args = teavm_globals.Array.prototype.slice.apply(arguments); $rt_startThread(function() { f.apply(this, args); }); @@ -604,7 +604,7 @@ function $rt_mainStarter(f) { var $rt_stringPool_instance; function $rt_stringPool(strings) { $rt_stringClassInit(); - $rt_stringPool_instance = new Array(strings.length); + $rt_stringPool_instance = new teavm_globals.Array(strings.length); for (var i = 0; i < strings.length; ++i) { $rt_stringPool_instance[i] = $rt_intern($rt_str(strings[i])); } @@ -616,15 +616,15 @@ function $rt_eraseClinit(target) { return target.$clinit = function() {}; } -var $rt_numberConversionBuffer = new ArrayBuffer(16); -var $rt_numberConversionView = new DataView($rt_numberConversionBuffer); -var $rt_numberConversionFloatArray = new Float32Array($rt_numberConversionBuffer); -var $rt_numberConversionDoubleArray = new Float64Array($rt_numberConversionBuffer); -var $rt_numberConversionIntArray = new Int32Array($rt_numberConversionBuffer); +var $rt_numberConversionBuffer = new teavm_globals.ArrayBuffer(16); +var $rt_numberConversionView = new teavm_globals.DataView($rt_numberConversionBuffer); +var $rt_numberConversionFloatArray = new teavm_globals.Float32Array($rt_numberConversionBuffer); +var $rt_numberConversionDoubleArray = new teavm_globals.Float64Array($rt_numberConversionBuffer); +var $rt_numberConversionIntArray = new teavm_globals.Int32Array($rt_numberConversionBuffer); var $rt_doubleToRawLongBits; var $rt_longBitsToDouble; -if (typeof BigInt !== 'function') { +if (typeof teavm_globals.BigInt !== 'function') { $rt_doubleToRawLongBits = function(n) { $rt_numberConversionView.setFloat64(0, n, true); return new Long($rt_numberConversionView.getInt32(0, true), $rt_numberConversionView.getInt32(4, true)); @@ -634,21 +634,23 @@ if (typeof BigInt !== 'function') { $rt_numberConversionView.setInt32(4, n.hi, true); return $rt_numberConversionView.getFloat64(0, true); } -} else if (typeof BigInt64Array !== 'function') { +} else if (typeof teavm_globals.BigInt64Array !== 'function') { $rt_doubleToRawLongBits = function(n) { $rt_numberConversionView.setFloat64(0, n, true); var lo = $rt_numberConversionView.getInt32(0, true); var hi = $rt_numberConversionView.getInt32(4, true); - return BigInt.asIntN(64, BigInt.asUintN(32, BigInt(lo)) | (BigInt(hi) << BigInt(32))); + return teavm_globals.BigInt.asIntN(64, teavm_globals.BigInt.asUintN(32, teavm_globals.BigInt(lo)) + | (teavm_globals.BigInt(hi) << teavm_globals.BigInt(32))); } $rt_longBitsToDouble = function(n) { $rt_numberConversionView.setFloat64(0, n, true); var lo = $rt_numberConversionView.getInt32(0, true); var hi = $rt_numberConversionView.getInt32(4, true); - return BigInt.asIntN(64, BigInt.asUintN(32, BigInt(lo)) | (BigInt(hi) << BigInt(32))); + return teavm_globals.BigInt.asIntN(64, teavm_globals.BigInt.asUintN(32, teavm_globals.BigInt(lo)) + | (teavm_globals.BigInt(hi) << teavm_globals.BigInt(32))); } } else { - var $rt_numberConversionLongArray = new BigInt64Array($rt_numberConversionBuffer); + var $rt_numberConversionLongArray = new teavm_globals.BigInt64Array($rt_numberConversionBuffer); $rt_doubleToRawLongBits = function(n) { $rt_numberConversionDoubleArray[0] = n; return $rt_numberConversionLongArray[0]; @@ -679,14 +681,14 @@ function $rt_equalDoubles(a, b) { var JavaError; if (typeof Reflect === 'object') { - var defaultMessage = Symbol("defaultMessage"); + var defaultMessage = teavm_globals.Symbol("defaultMessage"); JavaError = function JavaError(message, cause) { - var self = Reflect.construct(Error, [undefined, cause], JavaError); - Object.setPrototypeOf(self, JavaError.prototype); + var self = teavm_globals.Reflect.construct(teavm_globals.Error, [void 0, cause], JavaError); + teavm_globals.Object.setPrototypeOf(self, JavaError.prototype); self[defaultMessage] = message; return self; } - JavaError.prototype = Object.create(Error.prototype, { + JavaError.prototype = teavm_globals.Object.create(teavm_globals.Error.prototype, { constructor: { configurable: true, writable: true, @@ -710,11 +712,13 @@ if (typeof Reflect === 'object') { } }); } else { - JavaError = Error; + JavaError = teavm_globals.Error; } function $rt_javaException(e) { - return e instanceof Error && typeof e[$rt_javaExceptionProp] === 'object' ? e[$rt_javaExceptionProp] : null; + return e instanceof teavm_globals.Error && typeof e[$rt_javaExceptionProp] === 'object' + ? e[$rt_javaExceptionProp] + : null; } function $rt_jsException(e) { return typeof e.$jsException === 'object' ? e.$jsException : null; @@ -785,7 +789,7 @@ var Long_fromNumber; var Long_toNumber; var Long_hi; var Long_lo; -if (typeof BigInt !== "function") { +if (typeof teavm_globals.BigInt !== "function") { Long.prototype.toString = function() { var result = []; var n = this; @@ -796,7 +800,7 @@ if (typeof BigInt !== "function") { var radix = new Long(10, 0); do { var divRem = Long_divRem(n, radix); - result.push(String.fromCharCode(48 + divRem[1].lo)); + result.push(teavm_globals.String.fromCharCode(48 + divRem[1].lo)); n = divRem[0]; } while (n.lo !== 0 || n.hi !== 0); result = result.reverse().join(''); @@ -830,28 +834,29 @@ if (typeof BigInt !== "function") { return val.lo; } } else { - Long_ZERO = BigInt(0); + Long_ZERO = teavm_globals.BigInt(0); Long_create = function(lo, hi) { - return BigInt.asIntN(64, BigInt.asUintN(64, BigInt(lo)) - | BigInt.asUintN(64, (BigInt(hi) << BigInt(32)))); + return teavm_globals.BigInt.asIntN(64, teavm_globals.BigInt.asUintN(64, teavm_globals.BigInt(lo)) + | teavm_globals.BigInt.asUintN(64, (teavm_globals.BigInt(hi) << teavm_globals.BigInt(32)))); } Long_fromInt = function(val) { - return BigInt.asIntN(64, BigInt(val | 0)); + return teavm_globals.BigInt.asIntN(64, teavm_globals.BigInt(val | 0)); } Long_fromNumber = function(val) { - return BigInt.asIntN(64, BigInt(val >= 0 ? Math.floor(val) : Math.ceil(val))); + return teavm_globals.BigInt.asIntN(64, teavm_globals.BigInt( + val >= 0 ? teavm_globals.Math.floor(val) : teavm_globals.Math.ceil(val))); } Long_toNumber = function(val) { - return Number(val); + return teavm_globals.Number(val); } Long_hi = function(val) { - return Number(BigInt.asIntN(64, val >> BigInt(32))) | 0; + return teavm_globals.Number(teavm_globals.BigInt.asIntN(64, val >> teavm_globals.BigInt(32))) | 0; } Long_lo = function(val) { - return Number(BigInt.asIntN(32, val)) | 0; + return teavm_globals.Number(teavm_globals.BigInt.asIntN(32, val)) | 0; } } -var $rt_imul = Math.imul || function(a, b) { +var $rt_imul = teavm_globals.Math.imul || function(a, b) { var ah = (a >>> 16) & 0xFFFF; var al = a & 0xFFFF; var bh = (b >>> 16) & 0xFFFF; @@ -902,8 +907,8 @@ function $rt_charArrayToString(array, offset, count) { var result = ""; var limit = offset + count; for (var i = offset; i < limit; i = (i + 1024) | 0) { - var next = Math.min(limit, (i + 1024) | 0); - result += String.fromCharCode.apply(null, array.subarray(i, next)); + var next = teavm_globals.Math.min(limit, (i + 1024) | 0); + result += teavm_globals.String.fromCharCode.apply(null, array.subarray(i, next)); } return result; } @@ -916,7 +921,7 @@ function $rt_stringToCharArray(string, begin, dst, dstBegin, count) { } } function $rt_fastStringToCharArray(string) { - var array = new Uint16Array(string.length); + var array = new teavm_globals.Uint16Array(string.length); for (var i = 0; i < array.length; ++i) { array[i] = string.charCodeAt(i); } diff --git a/core/src/main/resources/org/teavm/backend/javascript/simpleThread.js b/core/src/main/resources/org/teavm/backend/javascript/simpleThread.js index 13059653b..4c1fa99e9 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/simpleThread.js +++ b/core/src/main/resources/org/teavm/backend/javascript/simpleThread.js @@ -24,7 +24,7 @@ function $rt_startThread(runner, callback) { } if (typeof callback !== 'undefined') { callback(result); - } else if (result instanceof Error) { + } else if (result instanceof teavm_globals.Error) { throw result; } } diff --git a/core/src/main/resources/org/teavm/backend/javascript/thread.js b/core/src/main/resources/org/teavm/backend/javascript/thread.js index d30e9b27a..e5ea89f9c 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/thread.js +++ b/core/src/main/resources/org/teavm/backend/javascript/thread.js @@ -46,14 +46,14 @@ TeaVMThread.prototype.suspend = function(callback) { }; TeaVMThread.prototype.start = function(callback) { if (this.status !== 3) { - throw new Error("Thread already started"); + throw new teavm_globals.Error("Thread already started"); } if ($rt_currentNativeThread !== null) { - throw new Error("Another thread is running"); + throw new teavm_globals.Error("Another thread is running"); } this.status = 0; - this.completeCallback = callback ? callback : function(result) { - if (result instanceof Error) { + this.completeCallback = callback ? callback : (result) => { + if (result instanceof teavm_globals.Error) { throw result; } }; @@ -61,7 +61,7 @@ TeaVMThread.prototype.start = function(callback) { }; TeaVMThread.prototype.resume = function() { if ($rt_currentNativeThread !== null) { - throw new Error("Another thread is running"); + throw new teavm_globals.Error("Another thread is running"); } this.status = 2; this.run(); @@ -80,9 +80,7 @@ TeaVMThread.prototype.run = function() { var self = this; var callback = this.suspendCallback; this.suspendCallback = null; - callback(function() { - self.resume(); - }); + callback(() => self.resume()); } else if (this.status === 0) { this.completeCallback(result); } @@ -99,7 +97,7 @@ function $rt_resuming() { function $rt_suspend(callback) { var nativeThread = $rt_nativeThread(); if (nativeThread === null) { - throw new Error("Suspension point reached from non-threading context (perhaps, from native JS method)."); + throw new teavm_globals.Error("Suspension point reached from non-threading context (perhaps, from native JS method)."); } return nativeThread.suspend(callback); } @@ -111,5 +109,5 @@ function $rt_nativeThread() { return $rt_currentNativeThread; } function $rt_invalidPointer() { - throw new Error("Invalid recorded state"); + throw new teavm_globals.Error("Invalid recorded state"); } \ No newline at end of file diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSExceptionsGenerator.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSExceptionsGenerator.java index 8a4936828..b4ee6f805 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSExceptionsGenerator.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSExceptionsGenerator.java @@ -25,12 +25,12 @@ public class JSExceptionsGenerator implements Injector { public void generate(InjectorContext context, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { case "getJavaException": - context.getWriter().append("$rt_javaException("); + context.getWriter().appendFunction("$rt_javaException").append("("); context.writeExpr(context.getArgument(0)); context.getWriter().append(")"); break; case "getJSException": - context.getWriter().append("$rt_jsException("); + context.getWriter().appendFunction("$rt_jsException").append("("); context.writeExpr(context.getArgument(0)); context.getWriter().append(")"); break; diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapperGenerator.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapperGenerator.java index 85a79e499..09f277faa 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapperGenerator.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSWrapperGenerator.java @@ -44,7 +44,7 @@ public class JSWrapperGenerator implements Injector, DependencyPlugin { context.getWriter().append("("); } context.writeExpr(context.getArgument(0)); - context.getWriter().append(" instanceof ").append("$rt_objcls").append("()"); + context.getWriter().append(" instanceof ").appendFunction("$rt_objcls").append("()"); if (context.getPrecedence().ordinal() >= Precedence.COMPARISON.ordinal()) { context.getWriter().append(")"); } diff --git a/platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java b/platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java index 58f608d50..f49414346 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java +++ b/platform/src/main/java/org/teavm/platform/plugin/AsyncMethodGenerator.java @@ -21,6 +21,8 @@ import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.backend.javascript.spi.VirtualMethodContributor; import org.teavm.backend.javascript.spi.VirtualMethodContributorContext; +import org.teavm.backend.javascript.templating.JavaScriptTemplate; +import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory; import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; @@ -36,53 +38,30 @@ import org.teavm.model.ValueType; public class AsyncMethodGenerator implements Generator, DependencyPlugin, VirtualMethodContributor { private static final MethodDescriptor completeMethod = new MethodDescriptor("complete", Object.class, void.class); private static final MethodDescriptor errorMethod = new MethodDescriptor("error", Throwable.class, void.class); + private JavaScriptTemplate template; @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { - MethodReference asyncRef = getAsyncReference(context.getClassSource(), methodRef); - writer.append("var thread").ws().append('=').ws().append("$rt_nativeThread();").softNewLine(); - writer.append("var javaThread").ws().append('=').ws().append("$rt_getThread();").softNewLine(); - writer.append("if").ws().append("(thread.isResuming())").ws().append("{").indent().softNewLine(); - writer.append("thread.status").ws().append("=").ws().append("0;").softNewLine(); - writer.append("var result").ws().append("=").ws().append("thread.attribute;").softNewLine(); - writer.append("if").ws().append("(result instanceof Error)").ws().append("{").indent().softNewLine(); - writer.append("throw result;").softNewLine(); - writer.outdent().append("}").softNewLine(); - writer.append("return result;").softNewLine(); - writer.outdent().append("}").softNewLine(); - - writer.append("var callback").ws().append("=").ws().append("function()").ws().append("{};").softNewLine(); - writer.append("callback.").appendMethod(completeMethod).ws().append("=").ws() - .append("function(val)").ws().append("{").indent().softNewLine(); - writer.append("thread.attribute").ws().append('=').ws().append("val;").softNewLine(); - writer.append("$rt_setThread(javaThread);").softNewLine(); - writer.append("thread.resume();").softNewLine(); - writer.outdent().append("};").softNewLine(); - writer.append("callback.").appendMethod(errorMethod).ws().append("=").ws() - .append("function(e)").ws().append("{").indent().softNewLine(); - writer.append("thread.attribute").ws().append('=').ws().append("$rt_exception(e);").softNewLine(); - writer.append("$rt_setThread(javaThread);").softNewLine(); - writer.append("thread.resume();").softNewLine(); - writer.outdent().append("};").softNewLine(); - writer.append("callback").ws().append("=").ws().appendMethodBody(AsyncCallbackWrapper.class, "create", - AsyncCallback.class, AsyncCallbackWrapper.class).append("(callback);").softNewLine(); - writer.append("thread.suspend(function()").ws().append("{").indent().softNewLine(); - writer.append("try").ws().append("{").indent().softNewLine(); - writer.appendMethodBody(asyncRef).append('('); - ClassReader cls = context.getClassSource().get(methodRef.getClassName()); - MethodReader method = cls.getMethod(methodRef.getDescriptor()); - int start = method.hasModifier(ElementModifier.STATIC) ? 1 : 0; - for (int i = start; i <= methodRef.parameterCount(); ++i) { - writer.append(context.getParameterName(i)); - writer.append(',').ws(); + if (template == null) { + var templateFactory = new JavaScriptTemplateFactory(context.getClassLoader(), context.getClassSource()); + template = templateFactory.createFromResource("org/teavm/platform/plugin/Async.js"); } - writer.append("callback);").softNewLine(); - writer.outdent().append("}").ws().append("catch($e)").ws().append("{").indent().softNewLine(); - writer.append("callback.").appendMethod(errorMethod).append("($rt_exception($e));") - .softNewLine(); - writer.outdent().append("}").softNewLine(); - writer.outdent().append("});").softNewLine(); - writer.append("return null;").softNewLine(); + MethodReference asyncRef = getAsyncReference(context.getClassSource(), methodRef); + template.builder("asyncMethod") + .withContext(context) + .withFragment("callMethod", (w, p) -> { + w.appendMethodBody(asyncRef).append('('); + ClassReader cls = context.getClassSource().get(methodRef.getClassName()); + MethodReader method = cls.getMethod(methodRef.getDescriptor()); + int start = method.hasModifier(ElementModifier.STATIC) ? 1 : 0; + for (int i = start; i <= methodRef.parameterCount(); ++i) { + w.append(context.getParameterName(i)); + w.append(',').ws(); + } + w.append("callback);").softNewLine(); + }) + .build() + .write(writer, 0); } private MethodReference getAsyncReference(ClassReaderSource classSource, MethodReference methodRef) { diff --git a/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java b/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java index 0f75cb495..8b37339fe 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java @@ -22,6 +22,8 @@ import org.teavm.backend.javascript.spi.Generator; import org.teavm.backend.javascript.spi.GeneratorContext; import org.teavm.backend.javascript.spi.Injector; import org.teavm.backend.javascript.spi.InjectorContext; +import org.teavm.backend.javascript.templating.JavaScriptTemplate; +import org.teavm.backend.javascript.templating.JavaScriptTemplateFactory; import org.teavm.dependency.DependencyAgent; import org.teavm.dependency.DependencyPlugin; import org.teavm.dependency.MethodDependency; @@ -36,6 +38,8 @@ import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformRunnable; public class PlatformGenerator implements Generator, Injector, DependencyPlugin { + private JavaScriptTemplate template; + @Override public void methodReached(DependencyAgent agent, MethodDependency method) { switch (method.getReference().getName()) { @@ -90,37 +94,37 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { switch (methodRef.getName()) { - case "newInstanceImpl": - generateNewInstance(context, writer); - break; case "prepareNewInstance": generatePrepareNewInstance(context, writer); break; case "lookupClass": generateLookup(context, writer); break; - case "clone": - generateClone(context, writer); - break; - case "startThread": - generateSchedule(context, writer, false); - break; - case "schedule": - generateSchedule(context, writer, true); - break; case "getEnumConstants": generateEnumConstants(context, writer); break; case "getAnnotations": generateAnnotations(context, writer); break; + default: + generateWithTemplate(context, writer, methodRef); + break; } } + private void generateWithTemplate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) + throws IOException { + if (template == null) { + template = new JavaScriptTemplateFactory(context.getClassLoader(), context.getClassSource()) + .createFromResource("org/teavm/platform/plugin/Platform.js"); + } + template.builder(methodRef.getName()).withContext(context).build().write(writer, 0); + } + private void generatePrepareNewInstance(GeneratorContext context, SourceWriter writer) throws IOException { MethodDependencyInfo newInstanceMethod = context.getDependency().getMethod( new MethodReference(Platform.class, "newInstanceImpl", PlatformClass.class, Object.class)); - writer.append("var c").ws().append("=").ws().append("'$$constructor$$';").softNewLine(); + writer.append("let c").ws().append("=").ws().append("'$$constructor$$';").softNewLine(); if (newInstanceMethod != null) { for (String clsName : newInstanceMethod.getResult().getTypes()) { ClassReader cls = context.getClassSource().get(clsName); @@ -136,71 +140,21 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin } } - private void generateNewInstance(GeneratorContext context, SourceWriter writer) throws IOException { - String cls = context.getParameterName(1); - - writer.append("if").ws().append("($rt_resuming())").ws().append("{").indent().softNewLine(); - writer.append("var $r = $rt_nativeThread().pop();").softNewLine(); - writer.append(cls + ".$$constructor$$($r);").softNewLine(); - writer.append("if").ws().append("($rt_suspending())").ws().append("{").indent().softNewLine(); - writer.append("return $rt_nativeThread().push($r);").softNewLine(); - writer.outdent().append("}").softNewLine(); - writer.append("return $r;").softNewLine(); - writer.outdent().append("}").softNewLine(); - - writer.append("if").ws().append("(!").append(cls).append(".hasOwnProperty('$$constructor$$'))") - .ws().append("{").indent().softNewLine(); - writer.append("return null;").softNewLine(); - writer.outdent().append("}").softNewLine(); - - writer.append("var $r").ws().append('=').ws().append("new ").append(cls).append("();").softNewLine(); - writer.append(cls).append(".$$constructor$$($r);").softNewLine(); - writer.append("if").ws().append("($rt_suspending())").ws().append("{").indent().softNewLine(); - writer.append("return $rt_nativeThread().push($r);").softNewLine(); - writer.outdent().append("}").softNewLine(); - writer.append("return $r;").softNewLine(); - } - private void generateLookup(GeneratorContext context, SourceWriter writer) throws IOException { String param = context.getParameterName(1); - writer.append("switch ($rt_ustr(" + param + ")) {").softNewLine().indent(); + writer.append("switch").ws().append("(").appendFunction("$rt_ustr").append("(" + param + "))") + .ws().append("{").softNewLine().indent(); for (String name : context.getClassSource().getClassNames()) { - writer.append("case \"" + name + "\": ").appendClass(name).append(".$clinit(); ") + writer.append("case \"" + name + "\":").ws().appendClass(name).append(".$clinit();").ws() .append("return ").appendClass(name).append(";").softNewLine(); } - writer.append("default: return null;").softNewLine(); + writer.append("default:").ws().append("return null;").softNewLine(); writer.outdent().append("}").softNewLine(); } - private void generateClone(GeneratorContext context, SourceWriter writer) throws IOException { - String obj = context.getParameterName(1); - writer.append("var copy").ws().append("=").ws().append("new ").append(obj).append(".constructor();") - .softNewLine(); - writer.append("for").ws().append("(var field in " + obj + ")").ws().append("{").softNewLine().indent(); - writer.append("if").ws().append("(!" + obj + ".hasOwnProperty(field))").ws().append("{").softNewLine().indent(); - writer.append("continue;").softNewLine().outdent().append("}").softNewLine(); - writer.append("copy[field]").ws().append("=").ws().append(obj).append("[field];") - .softNewLine().outdent().append("}").softNewLine(); - writer.append("return copy;").softNewLine(); - } - - private void generateSchedule(GeneratorContext context, SourceWriter writer, boolean timeout) throws IOException { - MethodReference launchRef = new MethodReference(Platform.class, "launchThread", - PlatformRunnable.class, void.class); - String runnable = context.getParameterName(1); - writer.append("return setTimeout(function()").ws().append("{").indent().softNewLine(); - if (timeout) { - writer.appendMethodBody(launchRef); - } else { - writer.append("$rt_threadStarter(").appendMethodBody(launchRef).append(")"); - } - writer.append("(").append(runnable).append(");").softNewLine(); - writer.outdent().append("},").ws().append(timeout ? context.getParameterName(2) : "0") - .append(");").softNewLine(); - } private void generateEnumConstants(GeneratorContext context, SourceWriter writer) throws IOException { - writer.append("var c").ws().append("=").ws().append("'$$enumConstants$$';").softNewLine(); + writer.append("let c").ws().append("=").ws().append("'$$enumConstants$$';").softNewLine(); for (String clsName : context.getClassSource().getClassNames()) { ClassReader cls = context.getClassSource().get(clsName); MethodReader method = cls.getMethod(new MethodDescriptor("values", @@ -214,8 +168,8 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin MethodReference selfRef = new MethodReference(Platform.class, "getEnumConstants", PlatformClass.class, Enum[].class); - writer.appendMethodBody(selfRef).ws().append("=").ws().append("function(cls)").ws().append("{").softNewLine() - .indent(); + writer.appendMethodBody(selfRef).ws().append("=").ws().append("cls").ws().append("=>").ws() + .append("{").softNewLine().indent(); writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine(); writer.append("return null;").softNewLine(); writer.outdent().append("}").softNewLine(); @@ -231,7 +185,7 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin } private void generateAnnotations(GeneratorContext context, SourceWriter writer) throws IOException { - writer.append("var c").ws().append("=").ws().append("'$$annotations$$';").softNewLine(); + writer.append("let c").ws().append("=").ws().append("'$$annotations$$';").softNewLine(); for (String clsName : context.getClassSource().getClassNames()) { ClassReader annotCls = context.getClassSource().get(clsName + "$$__annotations__$$"); if (annotCls != null) { @@ -244,8 +198,8 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin MethodReference selfRef = new MethodReference(Platform.class, "getAnnotations", PlatformClass.class, Annotation[].class); - writer.appendMethodBody(selfRef).ws().append("=").ws().append("function(cls)").ws().append("{").softNewLine() - .indent(); + writer.appendMethodBody(selfRef).ws().append("=").ws().append("cls").ws().append("=>").ws() + .append("{").softNewLine().indent(); writer.append("if").ws().append("(!cls.hasOwnProperty(c))").ws().append("{").indent().softNewLine(); writer.append("return null;").softNewLine(); writer.outdent().append("}").softNewLine(); diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java index 23ceebd11..4d0a1802e 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorGenerator.java @@ -24,8 +24,9 @@ import org.teavm.model.MethodReference; class ResourceAccessorGenerator implements Generator { @Override public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException { - writer.append("var result = [];").softNewLine(); - writer.append("for (var key in ").append(context.getParameterName(1)).append(") {").indent().softNewLine(); + writer.append("let result").ws().append("=").ws().append("[];").softNewLine(); + writer.append("for").ws().append("(let key in ").append(context.getParameterName(1)).append(")").ws() + .append("{").indent().softNewLine(); writer.append("result.push(key);").softNewLine(); writer.outdent().append("}").softNewLine(); writer.append("return result;").softNewLine(); diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorInjector.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorInjector.java index 55849881f..955748751 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorInjector.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorInjector.java @@ -95,7 +95,7 @@ class ResourceAccessorInjector implements Injector { context.getWriter().append('('); context.writeExpr(context.getArgument(0)); context.getWriter().ws().append("!==").ws().append("null").ws().append("?").ws(); - context.getWriter().append("$rt_ustr("); + context.getWriter().appendFunction("$rt_ustr").append("("); context.writeExpr(context.getArgument(0)); context.getWriter().append(")").ws().append(':').ws().append("null)"); break; @@ -114,7 +114,7 @@ class ResourceAccessorInjector implements Injector { return; } } - context.getWriter().append("[$rt_ustr("); + context.getWriter().append("[").appendFunction("$rt_ustr").append("("); context.writeExpr(property); context.getWriter().append(")]"); } @@ -127,7 +127,7 @@ class ResourceAccessorInjector implements Injector { context.getWriter().append('"'); return; } - context.getWriter().append("$rt_ustr("); + context.getWriter().appendFunction("$rt_ustr").append("("); context.writeExpr(expr); context.getWriter().append(")"); } diff --git a/platform/src/main/resources/org/teavm/platform/plugin/Async.js b/platform/src/main/resources/org/teavm/platform/plugin/Async.js new file mode 100644 index 000000000..d1ae02b8a --- /dev/null +++ b/platform/src/main/resources/org/teavm/platform/plugin/Async.js @@ -0,0 +1,50 @@ +/* + * Copyright 2023 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function asyncMethod() { + let thread = $rt_nativeThread(); + let javaThread = $rt_getThread(); + if (thread.isResuming()) { + thread.status = 0; + let result = thread.attribute; + if (result instanceof teavm_globals.Error) { + throw result; + } + return result; + } + + let callback = function() {}; + callback[teavm_javaVirtualMethod("complete(Ljava/lang/Object;)V")] = val => { + thread.attribute = val; + $rt_setThread(javaThread); + thread.resume(); + } + callback[teavm_javaVirtualMethod("error(Ljava/lang/Throwable;)V")] = e => { + thread.attribute = $rt_exception(e); + $rt_setThread(javaThread); + thread.resume(); + } + callback = teavm_javaMethod("org.teavm.platform.plugin.AsyncCallbackWrapper", + "create(Lorg/teavm/interop/AsyncCallback;)Lorg/teavm/platform/plugin/AsyncCallbackWrapper;")(callback); + thread.suspend(() => { + try { + teavm_fragment("callMethod"); + } catch ($e) { + callback[teavm_javaVirtualMethod("error(Ljava/lang/Throwable;)V")]($e); + } + }); + return null; +} \ No newline at end of file diff --git a/platform/src/main/resources/org/teavm/platform/plugin/Platform.js b/platform/src/main/resources/org/teavm/platform/plugin/Platform.js new file mode 100644 index 000000000..9c45957cf --- /dev/null +++ b/platform/src/main/resources/org/teavm/platform/plugin/Platform.js @@ -0,0 +1,62 @@ +/* + * Copyright 2023 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function newInstanceImpl(cls) { + let thread = $rt_nativeThread(); + if ($rt_resuming()) { + let r = thread.pop(); + cls.$$constructor$$(r); + if ($rt_suspending()) { + return thread.push(r); + } + return r; + } + + if (!cls.hasOwnProperty("$$constructor$$")) { + return null; + } + + let r = new cls(); + cls.$$constructor$$(r); + if ($rt_suspending()) { + thread.push(r); + } + return r; +} + +function clone(obj) { + let copy = new obj.constructor(); + for (let field in obj) { + if (obj.hasOwnProperty(field)) { + copy[field] = obj[field]; + } + } + return copy; +} + +function startThread(runnable) { + teavm_globals.setTimeout(() => { + $rt_threadStarter(teavm_javaMethod("org.teavm.platform.Platform", + "launchThread(Lorg/teavm/platform/PlatformRunnable;)V"))(runnable); + }, 0); +} + +function schedule(runnable, timeout) { + teavm_globals.setTimeout(() => { + teavm_javaMethod("org.teavm.platform.Platform", + "launchThread(Lorg/teavm/platform/PlatformRunnable;)V")(runnable); + }, timeout); +} \ No newline at end of file diff --git a/tools/junit/src/main/java/org/teavm/junit/TestExceptionPlugin.java b/tools/junit/src/main/java/org/teavm/junit/TestExceptionPlugin.java index 75c0bcc15..c3807249b 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TestExceptionPlugin.java +++ b/tools/junit/src/main/java/org/teavm/junit/TestExceptionPlugin.java @@ -65,8 +65,8 @@ class TestExceptionPlugin implements TeaVMPlugin { private void renderExceptionMessage(SourceWriter writer) throws IOException { writer.appendClass("java.lang.Throwable").append(".prototype.getMessage").ws().append("=").ws() .append("function()").ws().append("{").indent().softNewLine(); - writer.append("return $rt_ustr(this.").appendMethod("getMessage", String.class).append("());") - .softNewLine(); + writer.append("return ").appendFunction("$rt_ustr").append("(this.") + .appendMethod("getMessage", String.class).append("());").softNewLine(); writer.outdent().append("};").newLine(); } }