diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index 7d90276a3..348b7c330 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -41,6 +41,7 @@ import org.teavm.classlib.java.lang.reflect.TModifier; import org.teavm.dependency.PluggableDependency; import org.teavm.interop.Address; import org.teavm.interop.DelegateTo; +import org.teavm.interop.Unmanaged; import org.teavm.jso.core.JSArray; import org.teavm.platform.Platform; import org.teavm.platform.PlatformClass; @@ -82,14 +83,26 @@ public class TClass extends TObject implements TAnnotatedElement { return platformClass; } + @DelegateTo("isInstanceLowLevel") public boolean isInstance(TObject obj) { return Platform.isInstance(Platform.getPlatformObject(obj), platformClass); } + @Unmanaged + private boolean isInstanceLowLevel(RuntimeObject obj) { + return obj != null && isAssignableFromLowLevel(RuntimeClass.getClass(obj)); + } + + @DelegateTo("isAssignableFromLowLevel") public boolean isAssignableFrom(TClass obj) { return Platform.isAssignable(obj.getPlatformClass(), platformClass); } + @Unmanaged + private boolean isAssignableFromLowLevel(RuntimeClass other) { + return Address.ofObject(this).toStructure().isSupertypeOf.apply(other); + } + @DelegateTo("getNameLowLevel") public TString getName() { if (name == null) { @@ -98,6 +111,7 @@ public class TClass extends TObject implements TAnnotatedElement { return name; } + @Unmanaged private RuntimeObject getNameLowLevel() { RuntimeClass runtimeClass = Address.ofObject(this).toStructure(); return runtimeClass.name; diff --git a/core/src/main/java/org/teavm/ast/optimization/Optimizer.java b/core/src/main/java/org/teavm/ast/optimization/Optimizer.java index b66c5d19e..c93cbe7fa 100644 --- a/core/src/main/java/org/teavm/ast/optimization/Optimizer.java +++ b/core/src/main/java/org/teavm/ast/optimization/Optimizer.java @@ -22,6 +22,7 @@ import org.teavm.ast.RegularMethodNode; import org.teavm.common.Graph; import org.teavm.model.BasicBlock; import org.teavm.model.Instruction; +import org.teavm.model.MethodReference; import org.teavm.model.Program; import org.teavm.model.Variable; import org.teavm.model.util.AsyncProgramSplitter; @@ -40,6 +41,8 @@ public class Optimizer { public void optimize(RegularMethodNode method, Program program, boolean friendlyToDebugger) { ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariables().size()); stats.analyze(program); + applyParametersToWriteStats(stats, method.getReference()); + boolean[] preservedVars = new boolean[stats.writes.length]; BreakEliminator breakEliminator = new BreakEliminator(); breakEliminator.eliminate(method.getBody()); @@ -72,6 +75,7 @@ public class Optimizer { boolean[] preservedVars = new boolean[method.getVariables().size()]; ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariables().size()); stats.analyze(splitter.getProgram(i)); + applyParametersToWriteStats(stats, method.getReference()); AsyncMethodPart part = method.getBody().get(i); BreakEliminator breakEliminator = new BreakEliminator(); @@ -101,6 +105,12 @@ public class Optimizer { } } + private void applyParametersToWriteStats(ReadWriteStatsBuilder stats, MethodReference method) { + for (int i = 0; i <= method.parameterCount(); ++i) { + stats.writes[i]++; + } + } + private void findEscapingLiveVars(LivenessAnalyzer liveness, Graph cfg, AsyncProgramSplitter splitter, int partIndex, boolean[] output) { Program originalProgram = splitter.getOriginalProgram(); diff --git a/core/src/main/java/org/teavm/ast/optimization/OptimizingVisitor.java b/core/src/main/java/org/teavm/ast/optimization/OptimizingVisitor.java index af37a28f9..f12aaf75a 100644 --- a/core/src/main/java/org/teavm/ast/optimization/OptimizingVisitor.java +++ b/core/src/main/java/org/teavm/ast/optimization/OptimizingVisitor.java @@ -68,6 +68,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { Statement resultStmt; private final boolean[] preservedVars; private final int[] writeFrequencies; + private final int[] initialWriteFrequences; private final int[] readFrequencies; private final Object[] constants; private List resultSequence; @@ -80,6 +81,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { boolean friendlyToDebugger) { this.preservedVars = preservedVars; this.writeFrequencies = writeFrequencies; + this.initialWriteFrequences = writeFrequencies.clone(); this.readFrequencies = readFrequencies; this.constants = constants; this.friendlyToDebugger = friendlyToDebugger; @@ -276,7 +278,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { return; } - if (constants[index] != null) { + if (!preservedVars[index] && initialWriteFrequences[index] == 1 && constants[index] != null) { ConstantExpr constantExpr = new ConstantExpr(); constantExpr.setValue(constants[index]); constantExpr.setLocation(expr.getLocation()); @@ -555,7 +557,8 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor { left = resultExpr; } else { int varIndex = ((VariableExpr) statement.getLeftValue()).getIndex(); - if (writeFrequencies[varIndex] == 1 && constants[varIndex] != null) { + if (!preservedVars[varIndex] && initialWriteFrequences[varIndex] == 1 + && constants[varIndex] != null) { resultStmt = new SequentialStatement(); return; } diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 0f34a8132..1c8f4d342 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -422,4 +422,9 @@ public class CTarget implements TeaVMTarget { public String[] getPlatformTags() { return new String[] { PlatformMarkers.C, PlatformMarkers.LOW_LEVEL }; } + + @Override + public boolean isAsyncSupported() { + return false; + } } diff --git a/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java index 0d8136500..e141a6ba6 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/StringPoolGenerator.java @@ -71,6 +71,12 @@ public class StringPoolGenerator { for (int j = 0; j < string.length(); ++j) { char c = string.charAt(j); switch (c) { + case '\\': + writer.print("\\\\"); + break; + case '"': + writer.print("\\\""); + break; case '\r': writer.print("\\r"); break; @@ -81,7 +87,9 @@ public class StringPoolGenerator { writer.print("\\t"); break; default: - if (c < 32 || c > 127) { + if (c < 32) { + writer.print("\\x" + Character.forDigit(c >> 4, 16) + Character.forDigit(c & 0xF, 16)); + } else if (c > 127) { writer.print("\\u" + Character.forDigit(c >> 12, 16) + Character.forDigit((c >> 8) & 15, 16) diff --git a/core/src/main/java/org/teavm/backend/c/intrinsic/PlatformIntrinsic.java b/core/src/main/java/org/teavm/backend/c/intrinsic/PlatformIntrinsic.java index 460d35676..5853683d9 100644 --- a/core/src/main/java/org/teavm/backend/c/intrinsic/PlatformIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/c/intrinsic/PlatformIntrinsic.java @@ -30,6 +30,7 @@ public class PlatformIntrinsic implements Intrinsic { switch (method.getName()) { case "getPlatformObject": case "asJavaClass": + case "createQueue": return true; } return false; @@ -42,6 +43,9 @@ public class PlatformIntrinsic implements Intrinsic { case "asJavaClass": context.emit(invocation.getArguments().get(0)); break; + case "createQueue": + context.writer().print("NULL"); + break; } } } 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 6e69610b0..877f4beba 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -458,4 +458,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { public String[] getPlatformTags() { return new String[] { PlatformMarkers.JAVASCRIPT }; } + + @Override + public boolean isAsyncSupported() { + return true; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 33f80d59f..fc2374ffb 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -749,4 +749,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { public String[] getPlatformTags() { return new String[] { PlatformMarkers.WEBASSEMBLY, PlatformMarkers.LOW_LEVEL }; } + + @Override + public boolean isAsyncSupported() { + return false; + } } \ No newline at end of file diff --git a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java index 687d42c92..d7887ce49 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java @@ -87,6 +87,7 @@ public class DependencyAnalyzer implements DependencyInfo { Map dependencyPlugins = new HashMap<>(); private boolean completing; private Map superClassFilters = new HashMap<>(); + boolean asyncSupported; public DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services, Diagnostics diagnostics) { @@ -116,6 +117,10 @@ public class DependencyAnalyzer implements DependencyInfo { agent = new DependencyAgent(this); } + public void setAsyncSupported(boolean asyncSupported) { + this.asyncSupported = asyncSupported; + } + public DependencyAgent getAgent() { return agent; } diff --git a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 47c149f4b..bb1616369 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -148,20 +148,25 @@ class DependencyGraphBuilder { if (method.hasModifier(ElementModifier.SYNCHRONIZED)) { List syncNodes = new ArrayList<>(); - MethodDependency methodDep = dependencyAnalyzer.linkMethod( + MethodDependency methodDep; + if (dependencyAnalyzer.asyncSupported) { + methodDep = dependencyAnalyzer.linkMethod( new MethodReference(Object.class, "monitorEnter", Object.class, void.class), null); - syncNodes.add(methodDep.getVariable(1)); - methodDep.use(); + syncNodes.add(methodDep.getVariable(1)); + methodDep.use(); + } methodDep = dependencyAnalyzer.linkMethod( new MethodReference(Object.class, "monitorEnterSync", Object.class, void.class), null); syncNodes.add(methodDep.getVariable(1)); methodDep.use(); - methodDep = dependencyAnalyzer.linkMethod( - new MethodReference(Object.class, "monitorExit", Object.class, void.class), null); - syncNodes.add(methodDep.getVariable(1)); - methodDep.use(); + if (dependencyAnalyzer.asyncSupported) { + methodDep = dependencyAnalyzer.linkMethod( + new MethodReference(Object.class, "monitorExit", Object.class, void.class), null); + syncNodes.add(methodDep.getVariable(1)); + methodDep.use(); + } methodDep = dependencyAnalyzer.linkMethod( new MethodReference(Object.class, "monitorExitSync", Object.class, void.class), null); @@ -696,25 +701,29 @@ class DependencyGraphBuilder { @Override public void monitorEnter(VariableReader objectRef) { - MethodDependency methodDep = dependencyAnalyzer.linkMethod( + if (dependencyAnalyzer.asyncSupported) { + MethodDependency methodDep = dependencyAnalyzer.linkMethod( new MethodReference(Object.class, "monitorEnter", Object.class, void.class), null); - nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); - methodDep.use(); + nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); + methodDep.use(); + } - methodDep = dependencyAnalyzer.linkMethod( + MethodDependency methodDep = dependencyAnalyzer.linkMethod( new MethodReference(Object.class, "monitorEnterSync", Object.class, void.class), null); - nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); - methodDep.use(); + nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); + methodDep.use(); } @Override public void monitorExit(VariableReader objectRef) { - MethodDependency methodDep = dependencyAnalyzer.linkMethod( - new MethodReference(Object.class, "monitorExit", Object.class, void.class), null); - nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); - methodDep.use(); + if (dependencyAnalyzer.asyncSupported) { + MethodDependency methodDep = dependencyAnalyzer.linkMethod( + new MethodReference(Object.class, "monitorExit", Object.class, void.class), null); + nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); + methodDep.use(); + } - methodDep = dependencyAnalyzer.linkMethod( + MethodDependency methodDep = dependencyAnalyzer.linkMethod( new MethodReference(Object.class, "monitorExitSync", Object.class, void.class), null); nodes[objectRef.getIndex()].connect(methodDep.getVariable(1)); methodDep.use(); diff --git a/core/src/main/java/org/teavm/runtime/RuntimeClass.java b/core/src/main/java/org/teavm/runtime/RuntimeClass.java index ec139ad9f..2cdf30ecb 100644 --- a/core/src/main/java/org/teavm/runtime/RuntimeClass.java +++ b/core/src/main/java/org/teavm/runtime/RuntimeClass.java @@ -47,6 +47,7 @@ public class RuntimeClass extends RuntimeObject { public RuntimeClass parent; public Address enumValues; public Address layout; + public RuntimeObject simpleName; @Unmanaged public static int computeCanary(int size, int tag) { diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index aa9ae0365..c8a9dbb03 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -358,6 +358,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { return; } + dependencyAnalyzer.setAsyncSupported(target.isAsyncSupported()); dependencyAnalyzer.setInterruptor(() -> progressListener.progressReached(0) == TeaVMProgressFeedback.CONTINUE); target.contributeDependencies(dependencyAnalyzer); dependencyAnalyzer.processDependencies(); diff --git a/core/src/main/java/org/teavm/vm/TeaVMTarget.java b/core/src/main/java/org/teavm/vm/TeaVMTarget.java index b1fc1f726..2d2be8522 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTarget.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTarget.java @@ -46,4 +46,6 @@ public interface TeaVMTarget { void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException; String[] getPlatformTags(); + + boolean isAsyncSupported(); } diff --git a/platform/src/main/java/org/teavm/platform/Platform.java b/platform/src/main/java/org/teavm/platform/Platform.java index 0eea8a94a..3b9944e6e 100644 --- a/platform/src/main/java/org/teavm/platform/Platform.java +++ b/platform/src/main/java/org/teavm/platform/Platform.java @@ -234,10 +234,16 @@ public final class Platform { return cls.itemType; } + @DelegateTo("getNameLowLevel") public static String getName(PlatformClass cls) { return cls.getMetadata().getName(); } + @Unmanaged + private static RuntimeObject getNameLowLevel(RuntimeClass cls) { + return cls.name; + } + @JSBody(script = "return $rt_global;") private static native JSObject getGlobal(); }