From 3d65d38375460de8b2a726365d8c8dfcf11fab1b Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 3 Oct 2023 21:25:50 +0200 Subject: [PATCH] Massive refactoring around Wasm support 1. Get rid of old exception handling IR transformer in favor of generation of EH code inside BE 2. Get rid of no-setjmp/longjmp support in C BE 3. Fix various bugs in WebAssembly BE 4. Suppress remaining failing tests for Wasm 5. Enable running Wasm tests by default --- .../classlib/impl/ServiceLoaderCSupport.java | 9 - .../teavm/classlib/java/io/TPrintStream.java | 2 +- .../org/teavm/classlib/java/lang/TClass.java | 1 - .../org/teavm/classlib/java/lang/TObject.java | 2 + .../org/teavm/classlib/java/lang/TSystem.java | 5 +- .../org/teavm/classlib/java/util/TBase64.java | 9 +- .../java/util/TTemplateCollections.java | 2 +- .../ast/decompilation/StatementGenerator.java | 4 +- .../java/org/teavm/backend/c/CTarget.java | 28 +- .../backend/c/generate/ClassGenerator.java | 15 +- .../c/generate/CodeGenerationVisitor.java | 8 +- .../backend/c/generate/GenerationContext.java | 8 +- .../c/generate/GeneratorContextImpl.java | 9 +- .../c/generators/GeneratorContext.java | 2 - .../org/teavm/backend/wasm/WasmRuntime.java | 64 +- .../org/teavm/backend/wasm/WasmTarget.java | 55 +- .../wasm/generate/CachedExpression.java} | 14 +- .../generate/CallSiteBinaryGenerator.java | 2 +- .../wasm/generate/ExpressionCache.java | 94 ++ .../wasm/generate/TemporaryVariablePool.java | 52 ++ .../wasm/generate/WasmClassGenerator.java | 1 + .../wasm/generate/WasmGenerationContext.java | 7 +- .../wasm/generate/WasmGenerationVisitor.java | 857 ++++++++++++++---- .../backend/wasm/generate/WasmGenerator.java | 9 +- .../wasm/generators/ArrayGenerator.java | 8 +- .../wasm/intrinsics/DoubleIntrinsic.java | 42 +- .../ExceptionHandlingIntrinsic.java | 12 +- .../wasm/intrinsics/FloatIntrinsic.java | 42 +- .../wasm/intrinsics/ShadowStackIntrinsic.java | 2 + .../wasm/intrinsics/WasmHeapIntrinsic.java | 2 +- .../wasm/intrinsics/WasmIntrinsicManager.java | 10 + .../wasm/model/expression/WasmBlock.java | 8 + .../wasm/model/expression/WasmBreak.java | 5 + .../model/expression/WasmConditional.java | 5 + .../wasm/model/expression/WasmExpression.java | 23 + .../expression/WasmIntUnaryOperation.java | 1 + .../wasm/model/expression/WasmReturn.java | 5 + .../wasm/model/expression/WasmSwitch.java | 5 + .../model/expression/WasmUnreachable.java | 5 + .../wasm/parser/CodeSectionParser.java | 6 + .../render/WasmBinaryRenderingVisitor.java | 6 + .../wasm/render/WasmRenderingVisitor.java | 6 +- .../model/lowlevel/CallSiteDescriptor.java | 97 -- .../lowlevel/CallSiteDescriptorAnnot.java | 24 - .../lowlevel/CallSiteDescriptorsAnnot.java | 20 - .../model/lowlevel/CallSiteLocation.java | 38 - .../lowlevel/CallSiteLocationsAnnot.java | 20 - .../lowlevel/ExceptionHandlerDescriptor.java | 17 - .../ExceptionHandlerDescriptorAnnot.java | 22 - ...ceptionHandlingShadowStackContributor.java | 510 ----------- .../model/lowlevel/ExceptionHandlingUtil.java | 62 ++ .../lowlevel/GCShadowStackContributor.java | 2 +- .../lowlevel/ShadowStackTransformer.java | 34 +- .../org/teavm/runtime/ExceptionHandling.java | 18 +- .../java/org/teavm/runtime/ShadowStack.java | 4 + .../org/teavm/backend/wasm/wasm-runtime.c | 11 +- .../org/teavm/backend/wasm/wasm-runtime.js | 4 +- gradle.properties | 3 +- .../teavm/platform/plugin/PlatformPlugin.java | 3 +- ....java => ResourceLowLevelTransformer.java} | 2 +- .../java/io/PipedInputStreamTest.java | 10 +- .../java/io/PipedOutputStreamTest.java | 6 +- .../classlib/java/lang/ClassLoaderTest.java | 2 +- .../teavm/classlib/java/lang/ClassTest.java | 2 +- .../lang/invoke/SerializedLambdaTest.java | 2 +- .../classlib/java/lang/reflect/ArrayTest.java | 4 +- .../java/lang/reflect/ConstructorTest.java | 2 +- .../classlib/java/lang/reflect/FieldTest.java | 2 +- .../java/lang/reflect/MethodTest.java | 2 +- .../org/teavm/classlib/java/net/URLTest.java | 3 +- .../time/TestDateTimesImplementation.java | 2 +- .../classlib/java/time/TestLocalDate.java | 4 +- .../classlib/java/time/TestLocalDateTime.java | 4 +- .../classlib/java/time/TestOffsetTime.java | 2 +- .../classlib/java/time/TestZonedDateTime.java | 2 +- .../classlib/java/time/temporal/TestYear.java | 2 +- .../teavm/classlib/java/util/DateTest.java | 4 + .../classlib/java/util/FormatterTest.java | 3 + .../test/MetaprogrammingTest.java | 3 + tests/src/test/java/org/teavm/vm/VMTest.java | 23 +- .../c/incremental/IncrementalCBuilder.java | 6 - .../org/teavm/cli/TeaVMCBuilderRunner.java | 7 - .../main/java/org/teavm/cli/TeaVMRunner.java | 7 - .../java/org/teavm/tooling/TeaVMTool.java | 6 - .../teavm/tooling/builder/BuildStrategy.java | 2 - .../builder/InProcessBuildStrategy.java | 7 - .../tooling/builder/RemoteBuildStrategy.java | 6 - .../org/teavm/tooling/daemon/BuildDaemon.java | 1 - .../tooling/daemon/RemoteBuildRequest.java | 1 - .../org/teavm/maven/TeaVMCompileMojo.java | 4 - 90 files changed, 1265 insertions(+), 1209 deletions(-) rename core/src/main/java/org/teavm/{model/lowlevel/CallSiteLocationAnnot.java => backend/wasm/generate/CachedExpression.java} (71%) create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/ExpressionCache.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java delete mode 100644 core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorAnnot.java delete mode 100644 core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorsAnnot.java delete mode 100644 core/src/main/java/org/teavm/model/lowlevel/CallSiteLocationsAnnot.java delete mode 100644 core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptorAnnot.java delete mode 100644 core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java create mode 100644 core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingUtil.java rename platform/src/main/java/org/teavm/platform/plugin/{ResourceCTransformer.java => ResourceLowLevelTransformer.java} (94%) diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderCSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderCSupport.java index eba531420..e8865793f 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderCSupport.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderCSupport.java @@ -124,22 +124,13 @@ public class ServiceLoaderCSupport implements GeneratorFactory { .print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&") .print(names.forClassInstance(ValueType.parse(Object[].class))).print(", ") .println("services->size);"); - if (!context.usesLongjmp()) { - writer.println("if (TEAVM_EXCEPTION_HANDLER != " + callSite.getId() + ") goto exit;"); - } writer.println("TEAVM_GC_ROOT(0, result);"); writer.println("void** arrayData = (void**) TEAVM_ARRAY_DATA(result, void*);"); writer.println("for (int32_t i = 0; i < services->size; ++i) {").indent(); writer.print("void* obj = ").print(names.forMethod(ALLOC_METHOD)).println("(services->entries[i].cls);"); - if (!context.usesLongjmp()) { - writer.println("if (TEAVM_EXCEPTION_HANDLER != " + callSite.getId() + ") goto exit;"); - } writer.println("TEAVM_GC_ROOT(1, obj);"); writer.println("services->entries[i].constructor(obj);"); - if (!context.usesLongjmp()) { - writer.println("if (TEAVM_EXCEPTION_HANDLER != " + callSite.getId() + ") goto exit;"); - } writer.println("arrayData[i] = obj;"); writer.outdent().println("}"); diff --git a/classlib/src/main/java/org/teavm/classlib/java/io/TPrintStream.java b/classlib/src/main/java/org/teavm/classlib/java/io/TPrintStream.java index bfeed1305..664d55287 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/io/TPrintStream.java +++ b/classlib/src/main/java/org/teavm/classlib/java/io/TPrintStream.java @@ -134,7 +134,7 @@ public class TPrintStream extends TFilterOutputStream { private void print(char[] s, int begin, int end) { TCharBuffer src = TCharBuffer.wrap(s, begin, end - begin); - byte[] destBytes = new byte[TMath.max(16, TMath.min(s.length, 1024))]; + byte[] destBytes = new byte[TMath.max(16, TMath.min(end - begin, 1024))]; TByteBuffer dest = TByteBuffer.wrap(destBytes); TCharsetEncoder encoder = charset.newEncoder() .onMalformedInput(TCodingErrorAction.REPLACE) 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 0accce26a..d3d638e38 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 @@ -114,7 +114,6 @@ public class TClass extends TObject implements TAnnotatedElement, TType { return Address.ofObject(this).toStructure().isSupertypeOf.apply(other); } - @Unmanaged public String getName() { if (PlatformDetector.isLowLevel()) { String result = getNameCache(this); diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index fe656132e..6f8345417 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -273,6 +273,7 @@ public class TObject { @DelegateTo("hashCodeLowLevelImpl") @NoSideEffects + @Unmanaged private static native int hashCodeLowLevel(TObject obj); @Unmanaged @@ -282,6 +283,7 @@ public class TObject { @DelegateTo("setHashCodeLowLevelImpl") @NoSideEffects + @Unmanaged private static native void setHashCodeLowLevel(TObject obj, int value); @Unmanaged diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java index 45fc19cac..c74c6078d 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java @@ -138,10 +138,11 @@ public final class TSystem extends TObject { GC.writeBarrier(dest); } - Address srcAddress = Address.align(src.toAddress().add(RuntimeArray.class, 1), itemSize); + var offset = Address.align(Address.fromInt(0).add(RuntimeArray.class, 1), itemSize).toInt(); + Address srcAddress = src.toAddress().add(offset); srcAddress = srcAddress.add(itemSize * srcPos); - Address destAddress = Address.align(dest.toAddress().add(RuntimeArray.class, 1), itemSize); + Address destAddress = dest.toAddress().add(offset); destAddress = destAddress.add(itemSize * destPos); Allocator.moveMemoryBlock(srcAddress, destAddress, length * itemSize); diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TBase64.java b/classlib/src/main/java/org/teavm/classlib/java/util/TBase64.java index 84804f4cc..397e374aa 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TBase64.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TBase64.java @@ -17,7 +17,10 @@ package org.teavm.classlib.java.util; import org.teavm.classlib.impl.Base64Impl; -public class TBase64 { +public final class TBase64 { + private TBase64() { + } + public static Encoder getEncoder() { return new Encoder(Base64Impl.alphabet, true); } @@ -53,11 +56,11 @@ public class TBase64 { } } - public Decoder getDecoder() { + public static Decoder getDecoder() { return new Decoder(Base64Impl.reverse); } - public Decoder getUrlDecoder() { + public static Decoder getUrlDecoder() { return new Decoder(Base64Impl.urlReverse); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TTemplateCollections.java b/classlib/src/main/java/org/teavm/classlib/java/util/TTemplateCollections.java index 0ca1b9339..f04a8d32f 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TTemplateCollections.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TTemplateCollections.java @@ -549,7 +549,7 @@ public final class TTemplateCollections { @Override public boolean containsKey(Object key) { - if (key == null) { + if (key == null || data.length == 0) { return false; } int suggestedIndex = Math.abs(key.hashCode()) % data.length; diff --git a/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java b/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java index ec8c955a4..39014f4c2 100644 --- a/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java +++ b/core/src/main/java/org/teavm/ast/decompilation/StatementGenerator.java @@ -617,7 +617,9 @@ class StatementGenerator implements InstructionVisitor { @Override public void visit(NullCheckInstruction insn) { - assign(Expr.unary(UnaryOperation.NULL_CHECK, null, Expr.var(insn.getValue().getIndex())), insn.getReceiver()); + var expr = Expr.unary(UnaryOperation.NULL_CHECK, null, Expr.var(insn.getValue().getIndex())); + expr.setLocation(insn.getLocation()); + assign(expr, insn.getReceiver()); } @Override 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 c3e72ae11..58e6fc9bc 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -116,7 +116,6 @@ import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.lowlevel.CallSiteDescriptor; import org.teavm.model.lowlevel.Characteristics; -import org.teavm.model.lowlevel.CheckInstructionTransformation; import org.teavm.model.lowlevel.ClassInitializerEliminator; import org.teavm.model.lowlevel.ClassInitializerTransformer; import org.teavm.model.lowlevel.ExportDependencyListener; @@ -164,7 +163,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private WriteBarrierInsertion writeBarrierInsertion; private NullCheckInsertion nullCheckInsertion; private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion(); - private CheckInstructionTransformation checkTransformation; private ExportDependencyListener exportDependencyListener = new ExportDependencyListener(); private int minHeapSize = 4 * 1024 * 1024; private int maxHeapSize = 128 * 1024 * 1024; @@ -177,7 +175,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private boolean incremental; private boolean lineNumbersGenerated; private SimpleStringPool stringPool; - private boolean longjmpUsed = true; private boolean heapDump; private boolean obfuscated; private List callSites = new ArrayList<>(); @@ -202,10 +199,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { this.lineNumbersGenerated = lineNumbersGenerated; } - public void setLongjmpUsed(boolean longjmpUsed) { - this.longjmpUsed = longjmpUsed; - } - public void setHeapDump(boolean heapDump) { this.heapDump = heapDump; } @@ -244,9 +237,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { characteristics = new Characteristics(controller.getUnprocessedClassSource()); classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerTransformer = new ClassInitializerTransformer(); - shadowStackTransformer = new ShadowStackTransformer(characteristics, !longjmpUsed); + shadowStackTransformer = new ShadowStackTransformer(characteristics); nullCheckInsertion = new NullCheckInsertion(new LowLevelNullCheckFilter(characteristics)); - checkTransformation = new CheckInstructionTransformation(); writeBarrierInsertion = new WriteBarrierInsertion(characteristics); controller.addVirtualMethods(VIRTUAL_METHODS::contains); @@ -357,14 +349,11 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { public void afterOptimizations(Program program, MethodReader method) { classInitializerEliminator.apply(program); classInitializerTransformer.transform(program); - if (!longjmpUsed) { - checkTransformation.apply(program, method.getResultType()); - } new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads) .apply(program, method.getReference()); - ShadowStackTransformer shadowStackTransformer = !incremental + var shadowStackTransformer = !incremental ? this.shadowStackTransformer - : new ShadowStackTransformer(characteristics, !longjmpUsed); + : new ShadowStackTransformer(characteristics); shadowStackTransformer.apply(program, method); writeBarrierInsertion.apply(program); } @@ -412,7 +401,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { GenerationContext context = new GenerationContext(vtableProvider, characteristics, controller.getDependencyInfo(), stringPool, nameProvider, fileNames, controller.getDiagnostics(), classes, intrinsics, generators, asyncMethods::contains, buildTarget, - controller.getClassInitializerInfo(), incremental, longjmpUsed, + controller.getClassInitializerInfo(), incremental, vmAssertions, vmAssertions || heapDump, obfuscated); BufferedCodeWriter specialWriter = new BufferedCodeWriter(false); @@ -422,9 +411,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { if (incremental) { configHeaderWriter.println("#define TEAVM_INCREMENTAL 1"); } - if (!longjmpUsed) { - configHeaderWriter.println("#define TEAVM_USE_SETJMP 0"); - } if (vmAssertions) { configHeaderWriter.println("#define TEAVM_MEMORY_TRACE 1"); } @@ -441,7 +427,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler, controller.getCacheStatus()); classGenerator.setAstCache(astCache); - if (context.isLongjmp() && !context.isIncremental()) { + if (!context.isIncremental()) { classGenerator.setCallSites(callSites); } IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl( @@ -545,9 +531,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { private void generateFastCallSites(GenerationContext context, CodeWriter writer, IncludeManager includes, Collection classNames) { - List callSites = context.isLongjmp() - ? this.callSites - : CallSiteDescriptor.extract(context.getClassSource(), classNames); + var callSites = this.callSites; new CallSiteGenerator(context, writer, includes, "teavm_callSites").generate(callSites); if (obfuscated) { generateCallSitesJson(context.getBuildTarget(), callSites); diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 93910b293..6b7d0ec9e 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -258,7 +258,7 @@ public class ClassGenerator { ClassGenerationContext classContext = new ClassGenerationContext(context, includes, prologueWriter, initWriter, currentClassName); codeGenerator = new CodeGenerator(classContext, codeWriter, includes); - if (context.isLongjmp() && !context.isIncremental()) { + if (!context.isIncremental()) { codeGenerator.setCallSites(callSites); } } @@ -334,18 +334,15 @@ public class ClassGenerator { } List callSites = null; - if (context.isLongjmp()) { - if (context.isIncremental()) { - callSites = new ArrayList<>(); - codeGenerator.setCallSites(callSites); - } + if (context.isIncremental()) { + callSites = new ArrayList<>(); + codeGenerator.setCallSites(callSites); } codeGenerator.generateMethod(methodNode); if (context.isIncremental()) { - generateCallSites(method.getReference(), - context.isLongjmp() ? callSites : CallSiteDescriptor.extract(method.getProgram())); + generateCallSites(method.getReference(), callSites); codeWriter.println("#undef TEAVM_ALLOC_STACK"); } } @@ -1273,7 +1270,7 @@ public class ClassGenerator { codeWriter.outdent().println("}"); GeneratorContextImpl generatorContext = new GeneratorContextImpl(codeGenerator.getClassContext(), - bodyWriter, writerBefore, codeWriter, includes, callSites, context.isLongjmp()); + bodyWriter, writerBefore, codeWriter, includes, callSites); generator.generate(generatorContext, methodRef); try { generatorContext.flush(); diff --git a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java index 408562852..aacb58a46 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CodeGenerationVisitor.java @@ -15,7 +15,7 @@ */ package org.teavm.backend.c.generate; -import static org.teavm.model.lowlevel.ExceptionHandlingShadowStackContributor.isManagedMethodCall; +import static org.teavm.model.lowlevel.ExceptionHandlingUtil.isManagedMethodCall; import com.carrotsearch.hppc.IntContainer; import com.carrotsearch.hppc.IntHashSet; import com.carrotsearch.hppc.IntSet; @@ -469,7 +469,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { } private boolean needsCallSiteId() { - return context.isLongjmp() && managed; + return managed; } @Override @@ -1357,9 +1357,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { } writer.println(";"); - if (context.isLongjmp()) { - writer.println("TEAVM_UNREACHABLE"); - } + writer.println("TEAVM_UNREACHABLE"); popLocation(statement.getLocation()); } diff --git a/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java b/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java index 64b5bdf17..09bca803a 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java +++ b/core/src/main/java/org/teavm/backend/c/generate/GenerationContext.java @@ -48,7 +48,6 @@ public class GenerationContext { private BuildTarget buildTarget; private ClassInitializerInfo classInitializerInfo; private boolean incremental; - private boolean longjmp; private boolean vmAssertions; private boolean heapDump; private boolean obfuscated; @@ -57,7 +56,7 @@ public class GenerationContext { DependencyInfo dependencies, StringPool stringPool, NameProvider names, FileNameProvider fileNames, Diagnostics diagnostics, ClassReaderSource classSource, List intrinsics, List generators, Predicate asyncMethods, BuildTarget buildTarget, - ClassInitializerInfo classInitializerInfo, boolean incremental, boolean longjmp, boolean vmAssertions, + ClassInitializerInfo classInitializerInfo, boolean incremental, boolean vmAssertions, boolean heapDump, boolean obfuscated) { this.virtualTableProvider = virtualTableProvider; this.characteristics = characteristics; @@ -73,7 +72,6 @@ public class GenerationContext { this.buildTarget = buildTarget; this.classInitializerInfo = classInitializerInfo; this.incremental = incremental; - this.longjmp = longjmp; this.vmAssertions = vmAssertions; this.heapDump = heapDump; this.obfuscated = obfuscated; @@ -149,10 +147,6 @@ public class GenerationContext { return incremental; } - public boolean isLongjmp() { - return longjmp; - } - public boolean isHeapDump() { return heapDump; } diff --git a/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java b/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java index 29eda11bb..65233b35a 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java +++ b/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java @@ -38,11 +38,10 @@ class GeneratorContextImpl implements GeneratorContext { private IncludeManager includes; private List fileGenerators = new ArrayList<>(); private List callSites; - private boolean longjmp; public GeneratorContextImpl(ClassGenerationContext classContext, CodeWriter bodyWriter, CodeWriter writerBefore, CodeWriter writerAfter, IncludeManager includes, - List callSites, boolean longjmp) { + List callSites) { this.context = classContext.getContext(); this.classContext = classContext; this.bodyWriter = bodyWriter; @@ -50,7 +49,6 @@ class GeneratorContextImpl implements GeneratorContext { this.writerAfter = writerAfter; this.includes = includes; this.callSites = callSites; - this.longjmp = longjmp; } @Override @@ -142,11 +140,6 @@ class GeneratorContextImpl implements GeneratorContext { return callSite; } - @Override - public boolean usesLongjmp() { - return longjmp; - } - void flush() throws IOException { for (FileGeneratorImpl generator : fileGenerators) { OutputFileUtil.write(generator.writer, generator.path, context.getBuildTarget()); diff --git a/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java b/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java index 303d9505f..98742515f 100644 --- a/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java +++ b/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java @@ -57,7 +57,5 @@ public interface GeneratorContext { void importMethod(MethodReference method, boolean isStatic); - boolean usesLongjmp(); - CallSiteDescriptor createCallSite(CallSiteLocation[] locations, ExceptionHandlerDescriptor[] exceptionHandlers); } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java index fb0f4278a..0ae1aa61f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java @@ -22,75 +22,99 @@ import org.teavm.interop.Unmanaged; import org.teavm.runtime.RuntimeObject; @StaticInit -@Unmanaged public final class WasmRuntime { private WasmRuntime() { } + @Unmanaged public static int compare(int a, int b) { return gt(a, b) ? 1 : lt(a, b) ? -1 : 0; } + @Unmanaged public static int compareUnsigned(int a, int b) { return gtu(a, b) ? 1 : ltu(a, b) ? -1 : 0; } + @Unmanaged public static int compareUnsigned(long a, long b) { return gtu(a, b) ? 1 : ltu(a, b) ? -1 : 0; } + @Unmanaged public static int compare(long a, long b) { return gt(a, b) ? 1 : lt(a, b) ? -1 : 0; } + @Unmanaged public static int compare(float a, float b) { return gt(a, b) ? 1 : lt(a, b) ? -1 : 0; } + @Unmanaged public static int compare(double a, double b) { return gt(a, b) ? 1 : lt(a, b) ? -1 : 0; } + @Unmanaged public static native float min(float a, float b); + @Unmanaged public static native double min(double a, double b); + @Unmanaged public static native float max(float a, float b); + @Unmanaged public static native double max(double a, double b); + @Unmanaged public static float remainder(float a, float b) { return a - (float) (int) (a / b) * b; } + @Unmanaged public static double remainder(double a, double b) { return a - (double) (long) (a / b) * b; } + @Unmanaged private static native boolean lt(int a, int b); + @Unmanaged private static native boolean gt(int a, int b); + @Unmanaged private static native boolean ltu(int a, int b); + @Unmanaged private static native boolean gtu(int a, int b); + @Unmanaged private static native boolean lt(long a, long b); + @Unmanaged private static native boolean gt(long a, long b); + @Unmanaged private static native boolean ltu(long a, long b); + @Unmanaged private static native boolean gtu(long a, long b); + @Unmanaged private static native boolean lt(float a, float b); + @Unmanaged private static native boolean gt(float a, float b); + @Unmanaged private static native boolean lt(double a, double b); + @Unmanaged private static native boolean gt(double a, double b); + @Unmanaged public static Address align(Address address, int alignment) { int value = address.toInt(); if (value == 0) { @@ -100,6 +124,7 @@ public final class WasmRuntime { return Address.fromInt(value); } + @Unmanaged public static int align(int value, int alignment) { if (value == 0) { return value; @@ -108,29 +133,36 @@ public final class WasmRuntime { return value; } + @Unmanaged public static void print(int a) { WasmSupport.print(a); } + @Unmanaged public static void printString(String s) { WasmSupport.printString(s); } + @Unmanaged public static void printInt(int i) { WasmSupport.printInt(i); } + @Unmanaged public static void printOutOfMemory() { WasmSupport.printOutOfMemory(); } + @Unmanaged public static void fillZero(Address address, int count) { fill(address, (byte) 0, count); } + @Unmanaged public static void fill(Address address, byte value, int count) { } + @Unmanaged public static Address allocStack(int size) { Address stack = WasmHeap.stack; Address result = stack.add(4); @@ -140,10 +172,12 @@ public final class WasmRuntime { return result; } + @Unmanaged public static Address getStackTop() { return WasmHeap.stack != WasmHeap.stackAddress ? WasmHeap.stack : null; } + @Unmanaged public static Address getNextStackFrame(Address stackFrame) { int size = stackFrame.getInt() + 2; Address result = stackFrame.add(-size * 4); @@ -153,28 +187,47 @@ public final class WasmRuntime { return result; } + @Unmanaged public static int getStackRootCount(Address stackFrame) { return stackFrame.getInt(); } + @Unmanaged public static Address getStackRootPointer(Address stackFrame) { int size = stackFrame.getInt(); return stackFrame.add(-size * 4); } + @Unmanaged private static Address getExceptionHandlerPtr(Address stackFrame) { int size = stackFrame.getInt(); return stackFrame.add(-size * 4 - 4); } + @Unmanaged public static int getCallSiteId(Address stackFrame) { return getExceptionHandlerPtr(stackFrame).getInt(); } + @Unmanaged public static void setExceptionHandlerId(Address stackFrame, int id) { - getExceptionHandlerPtr(stackFrame).putInt(id); + var addr = getExceptionHandlerPtr(stackFrame); + addr.putInt(addr.getInt() + id + 2); } + @Unmanaged + public static void setExceptionHandlerSkip(Address stackFrame) { + var addr = getExceptionHandlerPtr(stackFrame); + addr.putInt(addr.getInt() + 1); + } + + @Unmanaged + public static void setExceptionHandlerRestore(Address stackFrame) { + var addr = getExceptionHandlerPtr(stackFrame); + addr.putInt(addr.getInt() - 1); + } + + @Unmanaged private static int hashCode(RuntimeString string) { int hashCode = 0; int length = string.characters.length; @@ -186,6 +239,7 @@ public final class WasmRuntime { return hashCode; } + @Unmanaged private static boolean equals(RuntimeString first, RuntimeString second) { if (first.characters.length != second.characters.length) { return false; @@ -210,6 +264,7 @@ public final class WasmRuntime { return result; } + @Unmanaged private static int resourceMapSize(Address map) { int result = 0; int sz = map.getInt(); @@ -224,6 +279,7 @@ public final class WasmRuntime { return result; } + @Unmanaged private static void fillResourceMapKeys(Address map, String[] target) { int sz = map.getInt(); Address data = contentStart(map); @@ -234,10 +290,11 @@ public final class WasmRuntime { targetData.putAddress(entry); targetData = targetData.add(Address.sizeOf()); } - data = data.add(Address.sizeOf()); + data = data.add(Address.sizeOf() * 2); } } + @Unmanaged private static Address contentStart(Address resource) { return resource.add(Address.sizeOf()); } @@ -287,6 +344,7 @@ public final class WasmRuntime { return null; } + @Unmanaged public static native void callFunctionFromTable(int index, RuntimeObject instance); static class RuntimeString extends RuntimeObject { 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 40526d592..936630622 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -98,6 +98,8 @@ import org.teavm.backend.wasm.model.expression.WasmInt32Subtype; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntType; +import org.teavm.backend.wasm.model.expression.WasmIntUnary; +import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; import org.teavm.backend.wasm.model.expression.WasmReturn; import org.teavm.backend.wasm.model.expression.WasmSetLocal; @@ -151,9 +153,7 @@ import org.teavm.model.classes.VirtualTableProvider; import org.teavm.model.instructions.CloneArrayInstruction; import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; -import org.teavm.model.lowlevel.CallSiteDescriptor; import org.teavm.model.lowlevel.Characteristics; -import org.teavm.model.lowlevel.CheckInstructionTransformation; import org.teavm.model.lowlevel.ClassInitializerEliminator; import org.teavm.model.lowlevel.ClassInitializerTransformer; import org.teavm.model.lowlevel.LowLevelNullCheckFilter; @@ -202,7 +202,6 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { private List additionalIntrinsics = new ArrayList<>(); private NullCheckInsertion nullCheckInsertion; private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion(); - private CheckInstructionTransformation checkTransformation = new CheckInstructionTransformation(); private int minHeapSize = 2 * 1024 * 1024; private int maxHeapSize = 128 * 1024 * 1024; private boolean obfuscated; @@ -218,7 +217,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { characteristics = new Characteristics(controller.getUnprocessedClassSource()); classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerTransformer = new ClassInitializerTransformer(); - shadowStackTransformer = new ShadowStackTransformer(characteristics, true); + shadowStackTransformer = new ShadowStackTransformer(characteristics); nullCheckInsertion = new NullCheckInsertion(new LowLevelNullCheckFilter(characteristics)); writeBarrierInsertion = new WriteBarrierInsertion(characteristics); @@ -349,6 +348,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { Address.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "setExceptionHandlerId", Address.class, int.class, void.class)).use(); + dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "setExceptionHandlerSkip", + Address.class, void.class)).use(); + dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "setExceptionHandlerRestore", + Address.class, void.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getCallSiteId", Address.class, int.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "resourceMapKeys", Address.class, @@ -377,6 +380,12 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException", Throwable.class, void.class)).use(); + dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException", + void.class)).use(); + dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwClassCastException", + void.class)).use(); + dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, + "throwArrayIndexOutOfBoundsException", void.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException", Throwable.class)).use(); @@ -445,9 +454,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { classInitializerTransformer.transform(program); new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads) .apply(program, method.getReference()); - checkTransformation.apply(program, method.getResultType()); shadowStackTransformer.apply(program, method); - checkPhis(program, method); + //checkPhis(program, method); writeBarrierInsertion.apply(program); } @@ -496,7 +504,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false); var stringPool = classGenerator.getStringPool(); var context = new WasmGenerationContext(classes, module, controller.getDiagnostics(), - vtableProvider, tagRegistry, stringPool, names); + vtableProvider, tagRegistry, stringPool, names, characteristics); context.addIntrinsic(new AddressIntrinsic(classGenerator)); context.addIntrinsic(new StructureIntrinsic(classes, classGenerator)); @@ -543,7 +551,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { generateMethods(classes, context, generator, classGenerator, binaryWriter, module, dwarfClassGen); new WasmInteropFunctionGenerator(classGenerator).generateFunctions(module); - exceptionHandlingIntrinsic.postProcess(CallSiteDescriptor.extract(classes, classes.getClassNames())); + exceptionHandlingIntrinsic.postProcess(context.callSites); generateIsSupertypeFunctions(tagRegistry, module, classGenerator); classGenerator.postProcess(); new WasmSpecialFunctionGenerator(classGenerator, gcIntrinsic.regionSizeExpressions) @@ -922,10 +930,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { int tagOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeClass.class.getName(), "tag")); - WasmExpression tagExpression = new WasmGetLocal(subtypeVar); - tagExpression = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, tagExpression, - new WasmInt32Constant(tagOffset)); - tagExpression = new WasmLoadInt32(4, tagExpression, WasmInt32Subtype.INT32); + var tagExpression = new WasmLoadInt32(4, new WasmGetLocal(subtypeVar), WasmInt32Subtype.INT32); + tagExpression.setOffset(tagOffset); body.add(new WasmSetLocal(subtypeVar, tagExpression)); ranges.sort(Comparator.comparingInt(range -> range.lower)); @@ -949,7 +955,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { int lowerHole = ranges.get(i - 1).upper; int upperHole = ranges.get(i).lower; - lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED, + lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED, new WasmGetLocal(subtypeVar), new WasmInt32Constant(lowerHole)); testLower = new WasmConditional(lowerCondition); body.add(testLower); @@ -969,15 +975,12 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { List body) { int itemOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeClass.class.getName(), "itemType")); - WasmExpression itemExpression = new WasmGetLocal(subtypeVar); - itemExpression = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, itemExpression, - new WasmInt32Constant(itemOffset)); - itemExpression = new WasmLoadInt32(4, itemExpression, WasmInt32Subtype.INT32); + var itemExpression = new WasmLoadInt32(4, new WasmGetLocal(subtypeVar), WasmInt32Subtype.INT32); + itemExpression.setOffset(itemOffset); body.add(new WasmSetLocal(subtypeVar, itemExpression)); - WasmExpression itemCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, - new WasmGetLocal(subtypeVar), new WasmInt32Constant(0)); - WasmConditional itemTest = new WasmConditional(itemCondition); + var itemTest = new WasmConditional(new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, + new WasmGetLocal(subtypeVar))); itemTest.setType(WasmType.INT32); itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0)); @@ -1197,7 +1200,17 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { TeaVMEntryPoint entryPoint = entryPointIter.next(); String name = manager.getNames().forMethod(entryPoint.getMethod()); WasmCall call = new WasmCall(name); - call.getArguments().add(manager.generate(invocation.getArguments().get(0))); + var arg = manager.generate(invocation.getArguments().get(0)); + if (manager.isManagedMethodCall(entryPoint.getMethod())) { + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + var callSiteId = manager.generateCallSiteId(invocation.getLocation()); + block.getBody().add(manager.generateRegisterCallSite(callSiteId, + invocation.getLocation())); + block.getBody().add(arg); + arg = block; + } + call.getArguments().add(arg); call.setLocation(invocation.getLocation()); return call; } else { diff --git a/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocationAnnot.java b/core/src/main/java/org/teavm/backend/wasm/generate/CachedExpression.java similarity index 71% rename from core/src/main/java/org/teavm/model/lowlevel/CallSiteLocationAnnot.java rename to core/src/main/java/org/teavm/backend/wasm/generate/CachedExpression.java index fba78397b..475edcbb8 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocationAnnot.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/CachedExpression.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 Alexey Andreev. + * 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. @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.model.lowlevel; +package org.teavm.backend.wasm.generate; -@interface CallSiteLocationAnnot { - String fileName(); +import org.teavm.backend.wasm.model.expression.WasmExpression; - String className(); +abstract class CachedExpression { + abstract WasmExpression expr(); - String methodName(); + void release() { + } - int lineNumber(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/CallSiteBinaryGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/CallSiteBinaryGenerator.java index fe0d02037..47d7dda32 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/CallSiteBinaryGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/CallSiteBinaryGenerator.java @@ -102,7 +102,7 @@ public class CallSiteBinaryGenerator { int address = writer.append(binaryExceptionHandler); binaryExceptionHandlers.add(binaryExceptionHandler); - binaryExceptionHandler.setInt(EXCEPTION_HANDLER_ID, callSiteId + i + 1); + binaryExceptionHandler.setInt(EXCEPTION_HANDLER_ID, callSite.getHandlers().get(i).getId()); if (!firstHandlerSet) { binaryCallSite.setAddress(CALL_SITE_FIRST_HANDLER, address); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/ExpressionCache.java b/core/src/main/java/org/teavm/backend/wasm/generate/ExpressionCache.java new file mode 100644 index 000000000..b70c3f5bf --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/ExpressionCache.java @@ -0,0 +1,94 @@ +/* + * 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.backend.wasm.generate; + +import java.util.List; +import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.backend.wasm.model.expression.WasmSetLocal; +import org.teavm.model.TextLocation; + +class ExpressionCache { + private TemporaryVariablePool tmpVars; + + ExpressionCache(TemporaryVariablePool tmpVars) { + this.tmpVars = tmpVars; + } + + public CachedExpression create(WasmExpression expr, WasmType type, TextLocation location, + List body) { + if (expr instanceof WasmGetLocal) { + var getLocalExpr = (WasmGetLocal) expr; + return new LocalVarCachedExpression(getLocalExpr.getLocal()); + } else if (expr instanceof WasmInt32Constant) { + var constExpr = (WasmInt32Constant) expr; + return new Int32CachedExpression(constExpr.getValue()); + } else { + var tmpVar = tmpVars.acquire(type); + var storeExpr = new WasmSetLocal(tmpVar, expr); + storeExpr.setLocation(location); + body.add(storeExpr); + return new TmpVarCachedExpression(tmpVar); + } + } + + private static class LocalVarCachedExpression extends CachedExpression { + private final WasmLocal localVar; + + LocalVarCachedExpression(WasmLocal localVar) { + this.localVar = localVar; + } + + @Override + WasmExpression expr() { + return new WasmGetLocal(localVar); + } + } + + private class TmpVarCachedExpression extends CachedExpression { + private final WasmLocal tmpVar; + + TmpVarCachedExpression(WasmLocal tmpVar) { + this.tmpVar = tmpVar; + } + + @Override + WasmExpression expr() { + return new WasmGetLocal(tmpVar); + } + + @Override + void release() { + tmpVars.release(tmpVar); + } + } + + private static class Int32CachedExpression extends CachedExpression { + private final int value; + + Int32CachedExpression(int value) { + this.value = value; + } + + @Override + WasmExpression expr() { + return new WasmInt32Constant(value); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java b/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java new file mode 100644 index 000000000..5b8c6e244 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/TemporaryVariablePool.java @@ -0,0 +1,52 @@ +/* + * 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.backend.wasm.generate; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmType; + +class TemporaryVariablePool { + private WasmFunction function; + private List> temporaryVariablesByType = new ArrayList<>(); + + TemporaryVariablePool(WasmFunction function) { + this.function = function; + int typeCount = WasmType.values().length; + for (int i = 0; i < typeCount; ++i) { + temporaryVariablesByType.add(new ArrayDeque<>()); + } + } + + WasmLocal acquire(WasmType type) { + var stack = temporaryVariablesByType.get(type.ordinal()); + WasmLocal variable = stack.pollFirst(); + if (variable == null) { + variable = new WasmLocal(type); + function.add(variable); + } + return variable; + } + + void release(WasmLocal variable) { + var stack = temporaryVariablesByType.get(variable.getType().ordinal()); + stack.push(variable); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index 1238c6cba..e52732a53 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -241,6 +241,7 @@ public class WasmClassGenerator { value.setInt(CLASS_IS_INSTANCE, getFunctionPointer(names.forSupertypeFunction(type))); value.setAddress(CLASS_SIMPLE_NAME, 0); value.setInt(CLASS_INIT, -1); + value.setInt(CLASS_TAG, Integer.MAX_VALUE); String name; if (type == ValueType.VOID) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java index ad6bd6ae8..d59382136 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java @@ -37,6 +37,8 @@ import org.teavm.model.MethodReference; import org.teavm.model.ValueType; import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.VirtualTableProvider; +import org.teavm.model.lowlevel.CallSiteDescriptor; +import org.teavm.model.lowlevel.Characteristics; public class WasmGenerationContext { private ClassReaderSource classSource; @@ -46,15 +48,17 @@ public class WasmGenerationContext { private TagRegistry tagRegistry; private WasmStringPool stringPool; public final NameProvider names; + public final Characteristics characteristics; private Map importedMethods = new HashMap<>(); private List intrinsics = new ArrayList<>(); private List generators = new ArrayList<>(); private Map intrinsicCache = new HashMap<>(); private Map generatorCache = new HashMap<>(); + public final List callSites = new ArrayList<>(); public WasmGenerationContext(ClassReaderSource classSource, WasmModule module, Diagnostics diagnostics, VirtualTableProvider vtableProvider, TagRegistry tagRegistry, WasmStringPool stringPool, - NameProvider names) { + NameProvider names, Characteristics characteristics) { this.classSource = classSource; this.module = module; this.diagnostics = diagnostics; @@ -62,6 +66,7 @@ public class WasmGenerationContext { this.tagRegistry = tagRegistry; this.stringPool = stringPool; this.names = names; + this.characteristics = characteristics; } public void addIntrinsic(WasmIntrinsic intrinsic) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 48d5c65d4..6812394c2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -15,11 +15,11 @@ */ package org.teavm.backend.wasm.generate; -import java.util.ArrayDeque; +import static org.teavm.model.lowlevel.ExceptionHandlingUtil.isManagedMethodCall; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; -import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -71,7 +71,6 @@ import org.teavm.backend.wasm.WasmHeap; import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.binary.DataPrimitives; -import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; @@ -98,6 +97,8 @@ import org.teavm.backend.wasm.model.expression.WasmInt64Subtype; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntType; +import org.teavm.backend.wasm.model.expression.WasmIntUnary; +import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation; import org.teavm.backend.wasm.model.expression.WasmLoadFloat32; import org.teavm.backend.wasm.model.expression.WasmLoadFloat64; import org.teavm.backend.wasm.model.expression.WasmLoadInt32; @@ -120,12 +121,18 @@ import org.teavm.model.MethodReference; import org.teavm.model.TextLocation; import org.teavm.model.ValueType; import org.teavm.model.classes.VirtualTable; +import org.teavm.model.lowlevel.CallSiteDescriptor; +import org.teavm.model.lowlevel.CallSiteLocation; +import org.teavm.model.lowlevel.ExceptionHandlerDescriptor; +import org.teavm.model.lowlevel.ExceptionHandlingUtil; import org.teavm.runtime.Allocator; +import org.teavm.runtime.ExceptionHandling; import org.teavm.runtime.RuntimeArray; import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.ShadowStack; class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { + private static final FieldReference MONITOR_FIELD = new FieldReference("java.lang.Object", "monitor"); private static final MethodReference MONITOR_ENTER_SYNC = new MethodReference(Object.class, "monitorEnterSync", Object.class, void.class); private static final MethodReference MONITOR_EXIT_SYNC = new MethodReference(Object.class, @@ -134,36 +141,78 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { "monitorEnter", Object.class, void.class); private static final MethodReference MONITOR_EXIT = new MethodReference(Object.class, "monitorExit", Object.class, void.class); + private static final MethodReference CATCH_METHOD = new MethodReference(ExceptionHandling.class, + "catchException", Throwable.class); + private static final MethodReference THROW_METHOD = new MethodReference(ExceptionHandling.class, + "throwException", Throwable.class, void.class); + private static final MethodReference THROW_CCE_METHOD = new MethodReference(ExceptionHandling.class, + "throwClassCastException", void.class); + private static final MethodReference THROW_NPE_METHOD = new MethodReference(ExceptionHandling.class, + "throwNullPointerException", void.class); + private static final MethodReference THROW_AIOOBE_METHOD = new MethodReference(ExceptionHandling.class, + "throwArrayIndexOutOfBoundsException", void.class); + private static final int SWITCH_TABLE_THRESHOLD = 256; private WasmGenerationContext context; private WasmClassGenerator classGenerator; private WasmTypeInference typeInference; private WasmFunction function; + private MethodReference currentMethod; private int firstVariable; private IdentifiedStatement currentContinueTarget; private IdentifiedStatement currentBreakTarget; private Map breakTargets = new HashMap<>(); private Map continueTargets = new HashMap<>(); private Set usedBlocks = new HashSet<>(); - private List> temporaryVariablesByType = new ArrayList<>(); + private TemporaryVariablePool tempVars; + private ExpressionCache exprCache; + + private List handlers = new ArrayList<>(); + private WasmBlock lastTryBlock; + private WasmBlock rethrowBlock; + private List catchLabels = new ArrayList<>(); + private WasmLocal stackVariable; private BinaryWriter binaryWriter; private boolean async; + private boolean managed; WasmExpression result; + List resultConsumer; WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator, - BinaryWriter binaryWriter, WasmFunction function, int firstVariable, boolean async) { + BinaryWriter binaryWriter, WasmFunction function, MethodReference currentMethod, + int firstVariable, boolean async) { this.context = context; this.classGenerator = classGenerator; this.binaryWriter = binaryWriter; this.function = function; + this.currentMethod = currentMethod; this.firstVariable = firstVariable; - int typeCount = WasmType.values().length; - for (int i = 0; i < typeCount; ++i) { - temporaryVariablesByType.add(new ArrayDeque<>()); - } + tempVars = new TemporaryVariablePool(function); + exprCache = new ExpressionCache(tempVars); typeInference = new WasmTypeInference(context); this.async = async; + this.managed = context.characteristics.isManaged(currentMethod); + } + + void generate(Statement statement, List target) { + var lastTargetSize = target.size(); + resultConsumer = target; + statement.acceptVisitor(this); + resultConsumer = null; + if (rethrowBlock != null) { + var body = target.subList(lastTargetSize, target.size()); + rethrowBlock.getBody().addAll(body); + body.clear(); + target.add(rethrowBlock); + var valueToReturn = WasmExpression.defaultValueOfType(function.getResult()); + if (valueToReturn != null) { + target.add(new WasmReturn(valueToReturn)); + } + if (!rethrowBlock.isTerminating()) { + rethrowBlock.getBody().add(new WasmReturn()); + } + } } private void accept(Expr expr) { @@ -449,19 +498,51 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } break; case NULL_CHECK: - accept(expr.getOperand()); + if (!managed) { + expr.getOperand().acceptVisitor(this); + } else { + result = nullCheck(expr.getOperand(), expr.getLocation()); + } break; } } + private WasmExpression nullCheck(Expr value, TextLocation location) { + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + block.setLocation(location); + + + accept(value); + var cachedValue = exprCache.create(result, WasmType.INT32, location, block.getBody()); + + var check = new WasmBranch(cachedValue.expr(), block); + check.setResult(cachedValue.expr()); + block.getBody().add(new WasmDrop(check)); + + var callSiteId = generateCallSiteId(location); + block.getBody().add(generateRegisterCallSite(callSiteId, location)); + + var call = new WasmCall(context.names.forMethod(THROW_NPE_METHOD)); + block.getBody().add(call); + + var target = throwJumpTarget(); + var breakExpr = new WasmBreak(target); + if (target != rethrowBlock) { + breakExpr.setResult(generateGetHandlerId(callSiteId, location)); + block.getBody().add(new WasmDrop(breakExpr)); + } else { + block.getBody().add(breakExpr); + } + + cachedValue.release(); + return block; + } + private WasmExpression generateArrayLength(WasmExpression array) { int sizeOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeArray.class.getName(), "size")); - - WasmExpression ptr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, - array, new WasmInt32Constant(sizeOffset)); - ptr.setLocation(array.getLocation()); - - WasmExpression length = new WasmLoadInt32(4, ptr, WasmInt32Subtype.INT32); + var length = new WasmLoadInt32(4, array, WasmInt32Subtype.INT32); + length.setOffset(sizeOffset); length.setLocation(array.getLocation()); return length; } @@ -470,18 +551,26 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { public void visit(AssignmentStatement statement) { Expr left = statement.getLeftValue(); if (left == null) { - accept(statement.getRightValue()); - result.acceptVisitor(typeInference); - if (typeInference.getResult() != null) { - result = new WasmDrop(result); - result.setLocation(statement.getLocation()); + if (statement.getRightValue() instanceof InvocationExpr) { + var invocation = (InvocationExpr) statement.getRightValue(); + invocation(invocation, resultConsumer, invocation.getMethod().getReturnType() != ValueType.VOID); + } else { + accept(statement.getRightValue()); + result.acceptVisitor(typeInference); + if (typeInference.getResult() != null) { + result = new WasmDrop(result); + result.setLocation(statement.getLocation()); + } + resultConsumer.add(result); + result = null; } } else if (left instanceof VariableExpr) { VariableExpr varExpr = (VariableExpr) left; WasmLocal local = function.getLocalVariables().get(varExpr.getIndex() - firstVariable); accept(statement.getRightValue()); - result = new WasmSetLocal(local, result); - result.setLocation(statement.getLocation()); + var setLocal = new WasmSetLocal(local, result); + setLocal.setLocation(statement.getLocation()); + resultConsumer.add(setLocal); } else if (left instanceof QualificationExpr) { QualificationExpr lhs = (QualificationExpr) left; storeField(lhs.getQualified(), lhs.getField(), statement.getRightValue(), statement.getLocation()); @@ -495,9 +584,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { private void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) { WasmExpression address = getAddress(qualified, field, location); - ValueType type = context.getFieldType(field); accept(value); + if (field.equals(MONITOR_FIELD)) { + storeMonitor(address, result, location); + return; + } + ValueType type = context.getFieldType(field); WasmMemoryAccess resultExpr; if (type instanceof ValueType.Primitive) { switch (((ValueType.Primitive) type).getKind()) { @@ -531,8 +624,20 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } resultExpr.setOffset(getOffset(qualified, field)); - result = (WasmExpression) resultExpr; + var result = (WasmExpression) resultExpr; result.setLocation(location); + resultConsumer.add(result); + } + + private void storeMonitor(WasmExpression address, WasmExpression value, TextLocation location) { + value = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_UNSIGNED, value, + new WasmInt32Constant(1)); + value = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.OR, value, + new WasmInt32Constant(0x80000000)); + var store = new WasmStoreInt32(4, address, value, WasmInt32Subtype.INT32); + store.setLocation(location); + store.setOffset(4); + resultConsumer.add(store); } private void storeArrayItem(SubscriptExpr leftValue, Expr rightValue) { @@ -542,7 +647,8 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { WasmExpression index = result; rightValue.acceptVisitor(this); WasmExpression value = result; - result = storeArrayItem(getArrayElementPointer(array, index, leftValue.getType()), value, leftValue.getType()); + resultConsumer.add(storeArrayItem(getArrayElementPointer(array, index, leftValue.getType()), value, + leftValue.getType())); } private static WasmExpression storeArrayItem(WasmExpression array, WasmExpression value, ArrayType type) { @@ -592,14 +698,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(SequentialStatement statement) { - WasmBlock block = new WasmBlock(false); - for (Statement part : statement.getSequence()) { - accept(part); - if (result != null) { - block.getBody().add(result); - } + for (var part : statement.getSequence()) { + part.acceptVisitor(this); } - result = block; } @Override @@ -628,26 +729,19 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(ConditionalStatement statement) { accept(statement.getCondition()); - WasmConditional conditional = new WasmConditional(forCondition(result)); - - for (Statement part : statement.getConsequent()) { - accept(part); - if (result != null) { - conditional.getThenBlock().getBody().add(result); - } - } - for (Statement part : statement.getAlternative()) { - accept(part); - if (result != null) { - conditional.getElseBlock().getBody().add(result); - } - } - result = conditional; + var conditional = new WasmConditional(forCondition(result)); + visitMany(statement.getConsequent(), conditional.getThenBlock().getBody()); + visitMany(statement.getAlternative(), conditional.getElseBlock().getBody()); + resultConsumer.add(conditional); } @Override public void visit(VariableExpr expr) { - result = new WasmGetLocal(function.getLocalVariables().get(expr.getIndex() - firstVariable)); + result = new WasmGetLocal(localVar(expr.getIndex())); + } + + private WasmLocal localVar(int index) { + return function.getLocalVariables().get(index - firstVariable); } @Override @@ -745,22 +839,12 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { caseBlock.getBody().add(wrapper); targets[i] = wrapper; - for (Statement part : clause.getBody()) { - accept(part); - if (result != null) { - caseBlock.getBody().add(result); - } - } + visitMany(clause.getBody(), caseBlock.getBody()); wrapper = caseBlock; } defaultBlock.getBody().add(wrapper); - for (Statement part : statement.getDefaultClause()) { - accept(part); - if (result != null) { - defaultBlock.getBody().add(result); - } - } + visitMany(statement.getDefaultClause(), defaultBlock.getBody()); WasmBlock defaultTarget = wrapper; wrapper = defaultBlock; @@ -773,7 +857,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { breakTargets.remove(statement); currentBreakTarget = oldBreakTarget; - result = wrapper; + resultConsumer.add(wrapper); } private void translateSwitchToBinarySearch(SwitchStatement statement, WasmExpression condition, @@ -787,18 +871,20 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } entries.sort(Comparator.comparingInt(entry -> entry.label)); - WasmLocal conditionVar = getTemporary(WasmType.INT32); - initialWrapper.getBody().add(new WasmSetLocal(conditionVar, condition)); + var cachedCondition = exprCache.create(condition, WasmType.INT32, statement.getValue().getLocation(), + initialWrapper.getBody()); - generateBinarySearch(entries, 0, entries.size() - 1, initialWrapper, defaultTarget, conditionVar); + generateBinarySearch(entries, 0, entries.size() - 1, initialWrapper, defaultTarget, cachedCondition); + + cachedCondition.release(); } private void generateBinarySearch(List entries, int lower, int upper, WasmBlock consumer, - WasmBlock defaultTarget, WasmLocal conditionVar) { + WasmBlock defaultTarget, CachedExpression testValue) { if (upper - lower == 0) { int label = entries.get(lower).label; - WasmExpression condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, - new WasmGetLocal(conditionVar), new WasmInt32Constant(label)); + var condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, + testValue.expr(), new WasmInt32Constant(label)); WasmConditional conditional = new WasmConditional(condition); consumer.getBody().add(conditional); @@ -809,13 +895,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } else { int mid = (upper + lower) / 2; int label = entries.get(mid).label; - WasmExpression condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED, - new WasmGetLocal(conditionVar), new WasmInt32Constant(label)); + var condition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED, + testValue.expr(), new WasmInt32Constant(label)); WasmConditional conditional = new WasmConditional(condition); consumer.getBody().add(conditional); - generateBinarySearch(entries, mid + 1, upper, conditional.getThenBlock(), defaultTarget, conditionVar); - generateBinarySearch(entries, lower, mid, conditional.getElseBlock(), defaultTarget, conditionVar); + generateBinarySearch(entries, mid + 1, upper, conditional.getThenBlock(), defaultTarget, testValue); + generateBinarySearch(entries, lower, mid, conditional.getElseBlock(), defaultTarget, testValue); } } @@ -876,12 +962,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { usedBlocks.add(wrapper); } - for (Statement part : statement.getBody()) { - accept(part); - if (result != null) { - loop.getBody().add(result); - } - } + visitMany(statement.getBody(), loop.getBody()); loop.getBody().add(new WasmBreak(loop)); currentBreakTarget = oldBreakTarget; @@ -891,49 +972,174 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { if (usedBlocks.contains(wrapper)) { wrapper.getBody().add(loop); - result = wrapper; + resultConsumer.add(wrapper); } else { - result = loop; + resultConsumer.add(loop); } } @Override public void visit(InvocationExpr expr) { + result = invocation(expr, null, false); + } + + private WasmExpression invocation(InvocationExpr expr, List resultConsumer, boolean willDrop) { if (expr.getMethod().getClassName().equals(ShadowStack.class.getName())) { switch (expr.getMethod().getName()) { case "allocStack": generateAllocStack(expr.getArguments().get(0)); result.setLocation(expr.getLocation()); - return; + if (resultConsumer != null) { + resultConsumer.add(result); + return null; + } else { + return result; + } case "releaseStack": generateReleaseStack(); result.setLocation(expr.getLocation()); - return; + if (resultConsumer != null) { + resultConsumer.add(result); + return null; + } else { + return result; + } case "registerGCRoot": generateRegisterGcRoot(expr.getArguments().get(0), expr.getArguments().get(1)); result.setLocation(expr.getLocation()); - return; + if (resultConsumer != null) { + resultConsumer.add(result); + return null; + } else { + return result; + } case "removeGCRoot": generateRemoveGcRoot(expr.getArguments().get(0)); result.setLocation(expr.getLocation()); - return; - case "registerCallSite": - generateRegisterCallSite(expr.getArguments().get(0)); - result.setLocation(expr.getLocation()); - return; - case "getExceptionHandlerId": - generateGetHandlerId(); - result.setLocation(expr.getLocation()); - return; + if (resultConsumer != null) { + resultConsumer.add(result); + return null; + } else { + return result; + } } } - WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod()); + var intrinsic = context.getIntrinsic(expr.getMethod()); if (intrinsic != null) { - result = intrinsic.apply(expr, intrinsicManager); - return; + var resultExpr = intrinsic.apply(expr, intrinsicManager); + return trivialInvocation(resultExpr, resultConsumer, expr.getLocation(), willDrop); } + var callSiteId = generateCallSiteId(expr.getLocation()); + if (needsCallSiteId() && isManagedMethodCall(context.characteristics, expr.getMethod())) { + var invocation = generateInvocation(expr, callSiteId); + var type = WasmGeneratorUtil.mapType(expr.getMethod().getReturnType()); + + List targetList; + WasmBlock block; + if (resultConsumer != null) { + targetList = resultConsumer; + result = null; + block = null; + } else { + block = new WasmBlock(false); + block.setType(type); + block.setLocation(expr.getLocation()); + targetList = block.getBody(); + result = block; + } + + if (expr.getArguments().isEmpty()) { + targetList.add(generateRegisterCallSite(callSiteId, expr.getLocation())); + } + + WasmLocal resultVar = null; + if (!willDrop) { + if (type != null) { + resultVar = tempVars.acquire(type); + var setLocal = new WasmSetLocal(resultVar, invocation); + setLocal.setLocation(expr.getLocation()); + targetList.add(setLocal); + } else { + targetList.add(invocation); + } + } else if (type == null) { + targetList.add(invocation); + } else { + var drop = new WasmDrop(invocation); + drop.setLocation(expr.getLocation()); + targetList.add(drop); + } + + checkHandlerId(targetList, callSiteId, expr.getLocation()); + if (resultVar != null) { + var getLocal = new WasmGetLocal(resultVar); + getLocal.setLocation(expr.getLocation()); + targetList.add(getLocal); + tempVars.release(resultVar); + } + + return block; + } else { + var resultExpr = generateInvocation(expr, -1); + return trivialInvocation(resultExpr, resultConsumer, expr.getLocation(), willDrop); + } + } + + private WasmExpression trivialInvocation(WasmExpression resultExpr, List resultConsumer, + TextLocation location, boolean willDrop) { + if (resultConsumer != null) { + if (willDrop) { + var drop = new WasmDrop(resultExpr); + drop.setLocation(location); + resultConsumer.add(drop); + } else { + resultConsumer.add(resultExpr); + } + result = null; + return null; + } else { + return resultExpr; + } + } + + private int generateCallSiteId(TextLocation location) { + var callSiteLocations = CallSiteLocation.fromTextLocation(location, currentMethod); + var callSite = new CallSiteDescriptor(context.callSites.size(), callSiteLocations); + var reverseHandlers = new ArrayList<>(handlers); + Collections.reverse(reverseHandlers); + callSite.getHandlers().addAll(reverseHandlers); + context.callSites.add(callSite); + return callSite.getId(); + } + + private void checkHandlerId(List target, int callSiteId, TextLocation location) { + var jumpTarget = throwJumpTarget(); + if (jumpTarget == rethrowBlock) { + var handlerId = generateGetHandlerId(callSiteId, location); + var br = new WasmBranch(handlerId, throwJumpTarget()); + target.add(br); + } else { + var handlerVar = tempVars.acquire(WasmType.INT32); + var handlerId = generateGetHandlerId(callSiteId, location); + var saveHandler = new WasmSetLocal(handlerVar, handlerId); + saveHandler.setLocation(location); + target.add(saveHandler); + var br = new WasmBranch(new WasmGetLocal(handlerVar), throwJumpTarget()); + br.setResult(new WasmGetLocal(handlerVar)); + var dropBr = new WasmDrop(br); + dropBr.setLocation(location); + target.add(dropBr); + tempVars.release(handlerVar); + } + } + + private WasmBlock throwJumpTarget() { + return lastTryBlock != null ? lastTryBlock : rethrowBlock(); + } + + private WasmExpression generateInvocation(InvocationExpr expr, int callSiteId) { if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { MethodReader method = context.getClassSource().resolve(expr.getMethod()); MethodReference reference = method != null ? method.getReference() : expr.getMethod(); @@ -947,13 +1153,17 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { accept(argument); call.getArguments().add(result); } + addCallSiteIdToLastArg(callSiteId, call.getArguments()); call.setLocation(expr.getLocation()); - result = call; + return call; } else if (expr.getType() == InvocationType.CONSTRUCTOR) { WasmBlock block = new WasmBlock(false); block.setType(WasmType.INT32); - WasmLocal tmp = getTemporary(WasmType.INT32); + WasmLocal tmp = tempVars.acquire(WasmType.INT32); + if (callSiteId >= 0) { + generateRegisterCallSite(callSiteId, expr.getLocation()); + } block.getBody().add(new WasmSetLocal(tmp, allocateObject(expr.getMethod().getClassName(), expr.getLocation()))); @@ -964,12 +1174,13 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { accept(argument); call.getArguments().add(result); } + addCallSiteIdToLastArg(callSiteId, call.getArguments()); block.getBody().add(call); block.getBody().add(new WasmGetLocal(tmp)); - releaseTemporary(tmp); + tempVars.release(tmp); - result = block; + return block; } else { MethodReference reference = expr.getMethod(); accept(expr.getArguments().get(0)); @@ -977,7 +1188,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { WasmBlock block = new WasmBlock(false); block.setType(WasmGeneratorUtil.mapType(reference.getReturnType())); - WasmLocal instanceVar = getTemporary(WasmType.INT32); + WasmLocal instanceVar = tempVars.acquire(WasmType.INT32); block.getBody().add(new WasmSetLocal(instanceVar, instance)); instance = new WasmGetLocal(instanceVar); @@ -987,16 +1198,15 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { vtable = vtable.findMethodContainer(reference.getDescriptor()); } if (vtable == null) { - result = new WasmUnreachable(); - return; + return new WasmUnreachable(); } int vtableIndex = vtable.getMethods().indexOf(reference.getDescriptor()); if (vtable.getParent() != null) { vtableIndex += vtable.getParent().size(); } - WasmExpression methodIndex = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, - getReferenceToClass(instance), new WasmInt32Constant(vtableIndex * 4 + vtableOffset)); - methodIndex = new WasmLoadInt32(4, methodIndex, WasmInt32Subtype.INT32); + var classRef = getReferenceToClass(instance); + var methodIndex = new WasmLoadInt32(4, classRef, WasmInt32Subtype.INT32); + methodIndex.setOffset(vtableIndex * 4 + vtableOffset); WasmIndirectCall call = new WasmIndirectCall(methodIndex); call.getParameterTypes().add(WasmType.INT32); @@ -1012,18 +1222,37 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { accept(expr.getArguments().get(i)); call.getArguments().add(result); } + addCallSiteIdToLastArg(callSiteId, call.getArguments()); block.getBody().add(call); - releaseTemporary(instanceVar); - result = block; + tempVars.release(instanceVar); + return block; } } + private void addCallSiteIdToLastArg(int callSiteId, List args) { + if (args.isEmpty() || callSiteId < 0) { + return; + } + var arg = args.get(args.size() - 1); + var block = new WasmBlock(false); + arg.acceptVisitor(typeInference); + block.setType(typeInference.getResult()); + block.setLocation(arg.getLocation()); + block.getBody().add(arg); + args.set(args.size() - 1, block); + block.getBody().add(generateRegisterCallSite(callSiteId, arg.getLocation())); + } + + private boolean needsCallSiteId() { + return managed; + } + private void generateAllocStack(Expr sizeExpr) { if (stackVariable != null) { throw new IllegalStateException("Call to ShadowStack.allocStack must be done only once"); } - stackVariable = getTemporary(WasmType.INT32); + stackVariable = tempVars.acquire(WasmType.INT32); stackVariable.setName("__stack__"); InvocationExpr expr = new InvocationExpr(); expr.setType(InvocationType.SPECIAL); @@ -1047,25 +1276,24 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { result = new WasmStoreInt32(4, new WasmInt32Constant(offset), oldValue, WasmInt32Subtype.INT32); } - private void generateRegisterCallSite(Expr callSiteExpr) { - if (stackVariable == null) { - throw new IllegalStateException("Call to ShadowStack.registerCallSite must be dominated by " - + "Mutator.allocStack"); - } - - callSiteExpr.acceptVisitor(this); - WasmExpression callSite = result; - - result = new WasmStoreInt32(4, new WasmGetLocal(stackVariable), callSite, WasmInt32Subtype.INT32); + private WasmExpression generateRegisterCallSite(int callSite, TextLocation location) { + return generateRegisterCallSite(new WasmInt32Constant(callSite), location); } - private void generateGetHandlerId() { - if (stackVariable == null) { - throw new IllegalStateException("Call to ShadowStack.getHandlerId must be dominated by " - + "Mutator.allocStack"); - } + private WasmExpression generateRegisterCallSite(WasmExpression callSite, TextLocation location) { + var result = new WasmStoreInt32(4, new WasmGetLocal(stackVariable), callSite, WasmInt32Subtype.INT32); + result.setLocation(location); + return result; + } - result = new WasmLoadInt32(4, new WasmGetLocal(stackVariable), WasmInt32Subtype.INT32); + private WasmExpression generateGetHandlerId(int callSite, TextLocation location) { + WasmExpression handlerId = new WasmLoadInt32(4, new WasmGetLocal(stackVariable), WasmInt32Subtype.INT32); + if (callSite > 0) { + handlerId = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, handlerId, + new WasmInt32Constant(callSite)); + } + handlerId.setLocation(location); + return handlerId; } private void generateRegisterGcRoot(Expr slotExpr, Expr gcRootExpr) { @@ -1130,23 +1358,22 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { breakTargets.put(statement, block); } - for (Statement part : statement.getBody()) { - accept(part); - if (result != null) { - block.getBody().add(result); - } - } + visitMany(statement.getBody(), block.getBody()); if (statement.getId() != null) { breakTargets.remove(statement); } - result = block; + resultConsumer.add(block); } @Override public void visit(QualificationExpr expr) { WasmExpression address = getAddress(expr.getQualified(), expr.getField(), expr.getLocation()); + if (expr.getField().equals(MONITOR_FIELD)) { + result = getMonitor(address, expr.getLocation()); + return; + } ValueType type = context.getFieldType(expr.getField()); WasmMemoryAccess resultExpr; @@ -1185,6 +1412,30 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { result = (WasmExpression) resultExpr; } + private WasmExpression getMonitor(WasmExpression address, TextLocation location) { + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + block.setLocation(location); + + var tmp = tempVars.acquire(WasmType.INT32); + var monitor = new WasmLoadInt32(4, address, WasmInt32Subtype.INT32); + monitor.setOffset(4); + block.getBody().add(new WasmSetLocal(tmp, monitor)); + + var isMonitor = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, + new WasmGetLocal(tmp), new WasmInt32Constant(0x80000000)); + var shiftMonitor = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, + new WasmGetLocal(tmp), new WasmInt32Constant(1)); + var cond = new WasmConditional(isMonitor); + cond.setType(WasmType.INT32); + cond.getThenBlock().getBody().add(shiftMonitor); + cond.getElseBlock().getBody().add(new WasmInt32Constant(0)); + block.getBody().add(cond); + + tempVars.release(tmp); + return block; + } + private WasmExpression getAddress(Expr qualified, FieldReference field, TextLocation location) { if (qualified == null) { int offset = classGenerator.getFieldOffset(field); @@ -1212,8 +1463,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } WasmBlock wasmTarget = breakTargets.get(target); usedBlocks.add(wasmTarget); - result = new WasmBreak(wasmTarget); - result.setLocation(statement.getLocation()); + var br = new WasmBreak(wasmTarget); + br.setLocation(statement.getLocation()); + resultConsumer.add(br); } @Override @@ -1224,13 +1476,20 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } WasmBlock wasmTarget = continueTargets.get(target); usedBlocks.add(wasmTarget); - result = new WasmBreak(wasmTarget); - result.setLocation(statement.getLocation()); + var br = new WasmBreak(wasmTarget); + br.setLocation(statement.getLocation()); + resultConsumer.add(br); } @Override public void visit(NewExpr expr) { - result = allocateObject(expr.getConstructedClass(), expr.getLocation()); + var block = new WasmBlock(false); + block.setLocation(expr.getLocation()); + block.setType(WasmType.INT32); + var callSiteId = generateCallSiteId(expr.getLocation()); + block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); + block.getBody().add(allocateObject(expr.getConstructedClass(), expr.getLocation())); + result = block; } private WasmExpression allocateObject(String className, TextLocation location) { @@ -1245,6 +1504,12 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(NewArrayExpr expr) { + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + + var callSiteId = generateCallSiteId(expr.getLocation()); + block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); + ValueType type = expr.getType(); int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); @@ -1255,8 +1520,9 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { accept(expr.getLength()); call.getArguments().add(result); call.setLocation(expr.getLocation()); + block.getBody().add(call); - result = call; + result = block; } @Override @@ -1292,8 +1558,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } WasmBlock block = new WasmBlock(false); + block.setType(WasmType.INT32); + var callSiteId = generateCallSiteId(expr.getLocation()); + block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); - WasmLocal array = getTemporary(WasmType.INT32); + WasmLocal array = tempVars.acquire(WasmType.INT32); int classPointer = classGenerator.getClassPointer(ValueType.arrayOf(type)); String allocName = context.names.forMethod(new MethodReference(Allocator.class, "allocateArray", RuntimeClass.class, int.class, Address.class)); @@ -1311,6 +1580,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { block.getBody().add(new WasmGetLocal(array)); block.setLocation(expr.getLocation()); + tempVars.release(array); result = block; } @@ -1353,13 +1623,23 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } else { result = null; } - result = new WasmReturn(result); - result.setLocation(statement.getLocation()); + var wasmStatement = new WasmReturn(result); + wasmStatement.setLocation(statement.getLocation()); + resultConsumer.add(wasmStatement); } @Override public void visit(InstanceOfExpr expr) { - classGenerator.getClassPointer(expr.getType()); + var type = expr.getType(); + if (type instanceof ValueType.Object) { + var className = ((ValueType.Object) type).getClassName(); + if (!context.characteristics.isManaged(className)) { + expr.getExpr().acceptVisitor(this); + return; + } + } + + classGenerator.getClassPointer(type); accept(expr.getExpr()); @@ -1367,47 +1647,103 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { block.setType(WasmType.INT32); block.setLocation(expr.getLocation()); - WasmLocal objectVar = getTemporary(WasmType.INT32); - block.getBody().add(new WasmSetLocal(objectVar, result)); + var cachedObject = exprCache.create(result, WasmType.INT32, expr.getLocation(), block.getBody()); - WasmBranch ifNull = new WasmBranch(new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, - new WasmGetLocal(objectVar), new WasmInt32Constant(0)), block); + var ifNull = new WasmBranch(genIsZero(cachedObject.expr()), block); ifNull.setResult(new WasmInt32Constant(0)); block.getBody().add(new WasmDrop(ifNull)); WasmCall supertypeCall = new WasmCall(context.names.forSupertypeFunction(expr.getType())); - WasmExpression classRef = new WasmLoadInt32(4, result, WasmInt32Subtype.INT32); + WasmExpression classRef = new WasmLoadInt32(4, cachedObject.expr(), WasmInt32Subtype.INT32); classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef, new WasmInt32Constant(3)); supertypeCall.getArguments().add(classRef); block.getBody().add(supertypeCall); + cachedObject.release(); + result = block; } @Override public void visit(ThrowStatement statement) { - WasmBlock block = new WasmBlock(false); - block.setLocation(statement.getLocation()); + var callSiteId = generateCallSiteId(statement.getLocation()); + resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation())); + accept(statement.getException()); - block.getBody().add(result); + var call = new WasmCall(context.names.forMethod(THROW_METHOD), result); + call.setLocation(statement.getLocation()); + resultConsumer.add(call); - block.getBody().add(new WasmUnreachable()); - - result = block; + var target = throwJumpTarget(); + var breakExpr = new WasmBreak(target); + breakExpr.setLocation(statement.getLocation()); + if (target != rethrowBlock) { + breakExpr.setResult(generateGetHandlerId(callSiteId, statement.getLocation())); + } + resultConsumer.add(breakExpr); } @Override public void visit(CastExpr expr) { + var type = expr.getTarget(); + if (type instanceof ValueType.Object) { + var className = ((ValueType.Object) type).getClassName(); + if (!context.characteristics.isManaged(className)) { + expr.getValue().acceptVisitor(this); + return; + } + } + + classGenerator.getClassPointer(type); + + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + block.setLocation(expr.getLocation()); + accept(expr.getValue()); + var valueToCast = exprCache.create(result, WasmType.INT32, expr.getLocation(), block.getBody()); + + var nullCheck = new WasmBranch(genIsZero(valueToCast.expr()), block); + nullCheck.setResult(valueToCast.expr()); + block.getBody().add(new WasmDrop(nullCheck)); + + var supertypeCall = new WasmCall(context.names.forSupertypeFunction(expr.getTarget())); + WasmExpression classRef = new WasmLoadInt32(4, valueToCast.expr(), WasmInt32Subtype.INT32); + classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef, + new WasmInt32Constant(3)); + supertypeCall.getArguments().add(classRef); + + var breakIfPassed = new WasmBranch(supertypeCall, block); + breakIfPassed.setResult(valueToCast.expr()); + block.getBody().add(new WasmDrop(breakIfPassed)); + + var callSiteId = generateCallSiteId(expr.getLocation()); + block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); + + var call = new WasmCall(context.names.forMethod(THROW_CCE_METHOD)); + block.getBody().add(call); + + var target = throwJumpTarget(); + var breakExpr = new WasmBreak(target); + if (target != rethrowBlock) { + breakExpr.setResult(generateGetHandlerId(callSiteId, expr.getLocation())); + } + block.getBody().add(breakExpr); + + valueToCast.release(); + result = block; } @Override public void visit(InitClassStatement statement) { if (classGenerator.hasClinit(statement.getClassName())) { - result = new WasmCall(context.names.forClassInitializer(statement.getClassName())); - result.setLocation(statement.getLocation()); - } else { - result = null; + var call = new WasmCall(context.names.forClassInitializer(statement.getClassName())); + call.setLocation(statement.getLocation()); + + var callSiteId = generateCallSiteId(statement.getLocation()); + resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation())); + resultConsumer.add(call); + checkHandlerId(resultConsumer, callSiteId, statement.getLocation()); } } @@ -1421,14 +1757,97 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(TryCatchStatement statement) { - WasmBlock block = new WasmBlock(false); - for (Statement bodyPart : statement.getProtectedBody()) { - accept(bodyPart); - if (result != null) { - block.getBody().add(result); + var tryCatchStatements = new ArrayList(); + while (true) { + if (statement.getProtectedBody().size() != 1) { + break; } + var next = statement.getProtectedBody().get(0); + if (!(next instanceof TryCatchStatement)) { + break; + } + tryCatchStatements.add(statement); + statement = (TryCatchStatement) next; } - result = block; + tryCatchStatements.add(statement); + + int firstId = handlers.size(); + + var innerCatchBlock = new WasmBlock(false); + var bodyBlock = new WasmBlock(false); + bodyBlock.setType(WasmType.INT32); + + var isTopMostTryCatch = lastTryBlock == null; + if (isTopMostTryCatch) { + catchLabels.add(rethrowBlock()); + } + + var catchBlocks = new ArrayList(); + for (int i = 0; i < tryCatchStatements.size(); ++i) { + var tryCatch = tryCatchStatements.get(i); + handlers.add(new ExceptionHandlerDescriptor(firstId + i, tryCatch.getExceptionType())); + catchBlocks.add(new WasmBlock(false)); + } + var outerCatchBlock = catchBlocks.get(0); + catchLabels.addAll(catchBlocks.subList(1, catchBlocks.size())); + catchLabels.add(innerCatchBlock); + + var lastTryBlockBackup = lastTryBlock; + lastTryBlock = bodyBlock; + visitMany(statement.getProtectedBody(), bodyBlock.getBody()); + lastTryBlock = lastTryBlockBackup; + handlers.subList(firstId, handlers.size()).clear(); + + if (!bodyBlock.isTerminating()) { + bodyBlock.getBody().add(new WasmBreak(outerCatchBlock)); + } + var currentBlock = innerCatchBlock; + var handlerIdExpr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB, + bodyBlock, new WasmInt32Constant(1)); + var switchExpr = new WasmSwitch(handlerIdExpr, outerCatchBlock); + switchExpr.getTargets().addAll(catchLabels); + innerCatchBlock.getBody().add(switchExpr); + + catchLabels.subList(catchLabels.size() - tryCatchStatements.size(), catchLabels.size()).clear(); + if (isTopMostTryCatch) { + catchLabels.remove(catchLabels.size() - 1); + assert catchLabels.isEmpty(); + } + + for (int i = tryCatchStatements.size() - 1; i >= 0; --i) { + var tryCatch = tryCatchStatements.get(i); + var catchBlock = catchBlocks.get(i); + catchBlock.getBody().add(currentBlock); + var catchMethodName = context.names.forMethod(CATCH_METHOD); + var catchCall = new WasmCall(catchMethodName); + var catchWrapper = tryCatch.getExceptionVariable() != null + ? new WasmSetLocal(localVar(tryCatch.getExceptionVariable()), catchCall) + : new WasmDrop(catchCall); + catchBlock.getBody().add(catchWrapper); + visitMany(tryCatch.getHandler(), catchBlock.getBody()); + if (!catchBlock.isTerminating() && catchBlock != outerCatchBlock) { + catchBlock.getBody().add(new WasmBreak(outerCatchBlock)); + } + currentBlock = catchBlock; + } + + resultConsumer.add(outerCatchBlock); + } + + private WasmBlock rethrowBlock() { + if (rethrowBlock == null) { + rethrowBlock = new WasmBlock(false); + } + return rethrowBlock; + } + + private void visitMany(List statements, List target) { + var oldTarget = resultConsumer; + resultConsumer = target; + for (var part : statements) { + accept(part); + } + resultConsumer = oldTarget; } @Override @@ -1441,26 +1860,82 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { call.setLocation(statement.getLocation()); statement.getObjectRef().acceptVisitor(this); call.getArguments().add(result); - result = call; + + var callSiteId = generateCallSiteId(statement.getLocation()); + resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation())); + resultConsumer.add(call); + checkHandlerId(resultConsumer, callSiteId, statement.getLocation()); } @Override public void visit(MonitorExitStatement statement) { - WasmCall call = new WasmCall(context.names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)); + var call = new WasmCall(context.names.forMethod(async ? MONITOR_EXIT : MONITOR_EXIT_SYNC)); call.setLocation(statement.getLocation()); statement.getObjectRef().acceptVisitor(this); call.getArguments().add(result); - result = call; + + var callSiteId = generateCallSiteId(statement.getLocation()); + resultConsumer.add(generateRegisterCallSite(callSiteId, statement.getLocation())); + resultConsumer.add(call); + checkHandlerId(resultConsumer, callSiteId, statement.getLocation()); } @Override public void visit(BoundCheckExpr expr) { - expr.getIndex().acceptVisitor(this); + if (!managed) { + expr.getIndex().acceptVisitor(this); + return; + } + + var block = new WasmBlock(false); + block.setType(WasmType.INT32); + block.setLocation(expr.getLocation()); + + accept(expr.getIndex()); + var index = exprCache.create(result, WasmType.INT32, expr.getLocation(), block.getBody()); + + if (expr.getArray() != null) { + var condBlock = block; + if (expr.isLower()) { + condBlock = new WasmBlock(false); + block.getBody().add(condBlock); + + var lowerCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, + index.expr(), new WasmInt32Constant(0)); + var lowerBranch = new WasmBranch(lowerCond, condBlock); + condBlock.getBody().add(lowerBranch); + } + + accept(expr.getArray()); + var upperBound = generateArrayLength(result); + var upperCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, + index.expr(), upperBound); + var upperBranch = new WasmBranch(upperCond, block); + upperBranch.setResult(index.expr()); + condBlock.getBody().add(new WasmDrop(upperBranch)); + } else if (expr.isLower()) { + var lowerCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED, + index.expr(), new WasmInt32Constant(0)); + var lowerBranch = new WasmBranch(lowerCond, block); + lowerBranch.setResult(index.expr()); + block.getBody().add(new WasmDrop(lowerBranch)); + } + + var callSiteId = generateCallSiteId(expr.getLocation()); + block.getBody().add(generateRegisterCallSite(callSiteId, expr.getLocation())); + block.getBody().add(new WasmCall(context.names.forMethod(THROW_AIOOBE_METHOD))); + var br = new WasmBreak(throwJumpTarget()); + if (br.getTarget() != rethrowBlock) { + br.setResult(generateGetHandlerId(callSiteId, expr.getLocation())); + } + block.getBody().add(br); + + result = block; } private static WasmExpression negate(WasmExpression expr) { if (expr instanceof WasmIntBinary) { - WasmIntBinary binary = (WasmIntBinary) expr; + var binary = (WasmIntBinary) expr; if (binary.getType() == WasmIntType.INT32 && binary.getOperation() == WasmIntBinaryOperation.XOR) { if (isOne(binary.getFirst())) { var result = binary.getSecond(); @@ -1478,15 +1953,15 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { } } - WasmIntBinaryOperation negatedOp = negate(binary.getOperation()); + var negatedOp = negate(binary.getOperation()); if (negatedOp != null) { var result = new WasmIntBinary(binary.getType(), negatedOp, binary.getFirst(), binary.getSecond()); result.setLocation(expr.getLocation()); return result; } } else if (expr instanceof WasmFloatBinary) { - WasmFloatBinary binary = (WasmFloatBinary) expr; - WasmFloatBinaryOperation negatedOp = negate(binary.getOperation()); + var binary = (WasmFloatBinary) expr; + var negatedOp = negate(binary.getOperation()); if (negatedOp != null) { var result = new WasmFloatBinary(binary.getType(), negatedOp, binary.getFirst(), binary.getSecond()); result.setLocation(expr.getLocation()); @@ -1642,12 +2117,12 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public WasmLocal getTemporary(WasmType type) { - return WasmGenerationVisitor.this.getTemporary(type); + return tempVars.acquire(type); } @Override public void releaseTemporary(WasmLocal local) { - WasmGenerationVisitor.this.releaseTemporary(local); + tempVars.release(local); } @Override @@ -1664,32 +2139,30 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { public int getFunctionPointer(String name) { return classGenerator.getFunctionPointer(name); } + + @Override + public boolean isManagedMethodCall(MethodReference method) { + return needsCallSiteId() && ExceptionHandlingUtil.isManagedMethodCall(context.characteristics, method); + } + + @Override + public int generateCallSiteId(TextLocation location) { + return WasmGenerationVisitor.this.generateCallSiteId(location); + } + + @Override + public WasmExpression generateRegisterCallSite(int callSite, TextLocation location) { + return WasmGenerationVisitor.this.generateRegisterCallSite(callSite, location); + } }; - private WasmLocal getTemporary(WasmType type) { - Deque stack = temporaryVariablesByType.get(type.ordinal()); - WasmLocal variable = stack.pollFirst(); - if (variable == null) { - variable = new WasmLocal(type); - function.add(variable); - } - return variable; - } - - private void releaseTemporary(WasmLocal variable) { - Deque stack = temporaryVariablesByType.get(variable.getType().ordinal()); - stack.push(variable); - } - private WasmExpression getReferenceToClass(WasmExpression instance) { - WasmExpression classIndex = new WasmLoadInt32(4, instance, WasmInt32Subtype.INT32); + var classIndex = new WasmLoadInt32(4, instance, WasmInt32Subtype.INT32); return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classIndex, new WasmInt32Constant(3)); } - private WasmExpression emptyStatement(TextLocation location) { - WasmDrop drop = new WasmDrop(new WasmInt32Constant(0)); - drop.setLocation(location); - return drop; + private WasmExpression genIsZero(WasmExpression value) { + return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java index 9ac08c182..55d849b54 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java @@ -25,7 +25,6 @@ import org.teavm.backend.wasm.debug.info.VariableType; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmType; -import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.interop.Export; import org.teavm.model.AnnotationReader; import org.teavm.model.ClassHolder; @@ -92,13 +91,9 @@ public class WasmGenerator { function.add(local); } - WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function, + var visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function, methodReference, firstVariable, asyncMethods.test(methodReference)); - methodAst.getBody().acceptVisitor(visitor); - if (visitor.result instanceof WasmBlock) { - ((WasmBlock) visitor.result).setType(function.getResult()); - } - function.getBody().add(visitor.result); + visitor.generate(methodAst.getBody(), function.getBody()); AnnotationReader exportAnnot = method.getAnnotations().get(Export.class.getName()); if (exportAnnot != null) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java index cc32febf5..ef0f30486 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java @@ -52,7 +52,7 @@ public class ArrayGenerator implements WasmMethodGenerator { "Float", "Double", "Boolean" }; private static final ValueType.Primitive[] primitiveTypes = { ValueType.BYTE, ValueType.SHORT, ValueType.CHARACTER, ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN }; - private static final int[] shift = new int[] { 0, 1, 1, 2, 3, 2, 3, 0 }; + private static final int[] shift = new int[] { 0, 0, 1, 1, 2, 3, 2, 3, 0 }; @Override public boolean isApplicable(MethodReference methodReference) { @@ -99,7 +99,11 @@ public class ArrayGenerator implements WasmMethodGenerator { function.getBody().add(currentBlock); function.getBody().add(new WasmReturn(new WasmInt32Constant(0))); - WasmSwitch primitiveSwitch = new WasmSwitch(new WasmGetLocal(flagsVar), currentBlock); + var primitiveExpr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_UNSIGNED, + new WasmGetLocal(flagsVar), new WasmInt32Constant(RuntimeClass.PRIMITIVE_SHIFT)); + primitiveExpr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, + new WasmGetLocal(flagsVar), new WasmInt32Constant(RuntimeClass.PRIMITIVE_MASK)); + WasmSwitch primitiveSwitch = new WasmSwitch(primitiveExpr, currentBlock); for (int i = 0; i <= 8; ++i) { primitiveSwitch.getTargets().add(currentBlock); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java index 8d5746d05..79fa976d6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/DoubleIntrinsic.java @@ -62,18 +62,11 @@ public class DoubleIntrinsic implements WasmIntrinsic { case "getNaN": return new WasmFloat64Constant(Double.NaN); case "isNaN": - return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager, - WasmIntBinaryOperation.NE); + return testNaN(manager.generate(invocation.getArguments().get(0)), manager); case "isInfinite": - return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager, - WasmIntBinaryOperation.NE); - case "isFinite": { - WasmExpression result = testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager, - WasmIntBinaryOperation.NE); - result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, result, - new WasmInt32Constant(0)); - return result; - } + return testIsInfinite(manager.generate(invocation.getArguments().get(0))); + case "isFinite": + return testIsFinite(manager.generate(invocation.getArguments().get(0))); case "doubleToRawLongBits": { WasmConversion conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, manager.generate(invocation.getArguments().get(0))); @@ -91,8 +84,7 @@ public class DoubleIntrinsic implements WasmIntrinsic { } } - private WasmExpression testSpecialIEEE(WasmExpression expression, WasmIntrinsicManager manager, - WasmIntBinaryOperation fractionOp) { + private WasmExpression testNaN(WasmExpression expression, WasmIntrinsicManager manager) { WasmLocal bitsVar = manager.getTemporary(WasmType.INT64); WasmBlock block = new WasmBlock(false); block.setType(WasmType.INT32); @@ -107,14 +99,36 @@ public class DoubleIntrinsic implements WasmIntrinsic { new WasmGetLocal(bitsVar), new WasmInt64Constant(FRACTION_BITS)); WasmExpression testExponent = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.NE, exponentBits, new WasmInt64Constant(EXPONENT_BITS)); - WasmExpression testFraction = new WasmIntBinary(WasmIntType.INT64, fractionOp, + WasmExpression testFraction = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.NE, fractionBits, new WasmInt64Constant(0)); WasmBranch breakIfWrongExponent = new WasmBranch(testExponent, block); breakIfWrongExponent.setResult(new WasmInt32Constant(0)); block.getBody().add(new WasmDrop(breakIfWrongExponent)); + manager.releaseTemporary(bitsVar); + block.getBody().add(testFraction); return block; } + + private WasmExpression testIsInfinite(WasmExpression expression) { + var conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, expression); + conversion.setReinterpret(true); + + var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND, + conversion, new WasmInt64Constant(EXPONENT_BITS)); + return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.EQ, result, + new WasmInt64Constant(EXPONENT_BITS)); + } + + private WasmExpression testIsFinite(WasmExpression expression) { + var conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, expression); + conversion.setReinterpret(true); + + var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND, + conversion, new WasmInt64Constant(EXPONENT_BITS)); + return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.NE, result, + new WasmInt64Constant(EXPONENT_BITS)); + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ExceptionHandlingIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ExceptionHandlingIntrinsic.java index 8d1795497..b4b81486d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ExceptionHandlingIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ExceptionHandlingIntrinsic.java @@ -18,16 +18,20 @@ package org.teavm.backend.wasm.intrinsics; import java.util.ArrayList; import java.util.List; import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.WasmHeap; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.generate.CallSiteBinaryGenerator; import org.teavm.backend.wasm.generate.WasmClassGenerator; import org.teavm.backend.wasm.generate.WasmStringPool; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.backend.wasm.model.expression.WasmInt32Subtype; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntType; +import org.teavm.backend.wasm.model.expression.WasmStoreInt32; import org.teavm.backend.wasm.model.expression.WasmUnreachable; +import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; import org.teavm.model.lowlevel.CallSiteDescriptor; import org.teavm.runtime.CallSite; @@ -87,7 +91,13 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic { case "isObfuscated": return new WasmInt32Constant(0); - case "jumpToFrame": + case "jumpToFrame": { + var offset = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "stack")); + var ptr = new WasmInt32Constant(offset); + return new WasmStoreInt32(4, ptr, manager.generate(invocation.getArguments().get(0)), + WasmInt32Subtype.INT32); + } + case "abort": return new WasmUnreachable(); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java index 881556737..813deca25 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/FloatIntrinsic.java @@ -61,18 +61,11 @@ public class FloatIntrinsic implements WasmIntrinsic { case "getNaN": return new WasmFloat32Constant(Float.NaN); case "isNaN": - return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager, - WasmIntBinaryOperation.NE); + return testNaN(manager.generate(invocation.getArguments().get(0)), manager); case "isInfinite": - return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager, - WasmIntBinaryOperation.EQ); - case "isFinite": { - WasmExpression result = testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager, - WasmIntBinaryOperation.EQ); - result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, result, - new WasmInt32Constant(0)); - return result; - } + return testIsInfinite(manager.generate(invocation.getArguments().get(0))); + case "isFinite": + return testIsFinite(manager.generate(invocation.getArguments().get(0))); case "floatToRawIntBits": { WasmConversion conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, manager.generate(invocation.getArguments().get(0))); @@ -90,8 +83,7 @@ public class FloatIntrinsic implements WasmIntrinsic { } } - private WasmExpression testSpecialIEEE(WasmExpression expression, WasmIntrinsicManager manager, - WasmIntBinaryOperation fractionOp) { + private WasmExpression testNaN(WasmExpression expression, WasmIntrinsicManager manager) { WasmLocal bitsVar = manager.getTemporary(WasmType.INT32); WasmBlock block = new WasmBlock(false); block.setType(WasmType.INT32); @@ -106,14 +98,36 @@ public class FloatIntrinsic implements WasmIntrinsic { new WasmGetLocal(bitsVar), new WasmInt32Constant(FRACTION_BITS)); WasmExpression testExponent = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, exponentBits, new WasmInt32Constant(EXPONENT_BITS)); - WasmExpression testFraction = new WasmIntBinary(WasmIntType.INT32, fractionOp, + WasmExpression testFraction = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, fractionBits, new WasmInt32Constant(0)); WasmBranch breakIfWrongExponent = new WasmBranch(testExponent, block); breakIfWrongExponent.setResult(new WasmInt32Constant(0)); block.getBody().add(new WasmDrop(breakIfWrongExponent)); + manager.releaseTemporary(bitsVar); + block.getBody().add(testFraction); return block; } + + private WasmExpression testIsInfinite(WasmExpression expression) { + var conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, expression); + conversion.setReinterpret(true); + + var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, + conversion, new WasmInt32Constant(EXPONENT_BITS)); + return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, result, + new WasmInt32Constant(EXPONENT_BITS)); + } + + private WasmExpression testIsFinite(WasmExpression expression) { + var conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, expression); + conversion.setReinterpret(true); + + var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, + conversion, new WasmInt32Constant(EXPONENT_BITS)); + return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, result, + new WasmInt32Constant(EXPONENT_BITS)); + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ShadowStackIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ShadowStackIntrinsic.java index c4f47cc90..8636b3a6a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ShadowStackIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ShadowStackIntrinsic.java @@ -35,6 +35,8 @@ public class ShadowStackIntrinsic implements WasmIntrinsic { case "getStackRootPointer": case "getCallSiteId": case "setExceptionHandlerId": + case "setExceptionHandlerSkip": + case "setExceptionHandlerRestore": return true; default: return false; diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmHeapIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmHeapIntrinsic.java index 94e577065..5e54a99ff 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmHeapIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmHeapIntrinsic.java @@ -51,7 +51,7 @@ public class WasmHeapIntrinsic implements WasmIntrinsic { case "initHeapTrace": return new WasmDrop(new WasmInt32Constant(0)); case "growMemory": - return new WasmMemoryGrow(manager.generate(invocation.getArguments().get(0))); + return new WasmDrop(new WasmMemoryGrow(manager.generate(invocation.getArguments().get(0)))); default: throw new IllegalArgumentException(invocation.getMethod().getName()); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java index f42b0393d..bb9d4b1fe 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java @@ -24,6 +24,8 @@ import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; +import org.teavm.model.TextLocation; import org.teavm.model.ValueType; public interface WasmIntrinsicManager { @@ -46,4 +48,12 @@ public interface WasmIntrinsicManager { int getFunctionPointer(String name); void releaseTemporary(WasmLocal local); + + boolean isManagedMethodCall(MethodReference method); + + int generateCallSiteId(TextLocation location); + + WasmExpression generateRegisterCallSite(int callSite, TextLocation location); + + } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmBlock.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmBlock.java index 568a9c3aa..0f66e5ebc 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmBlock.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmBlock.java @@ -52,4 +52,12 @@ public class WasmBlock extends WasmExpression { public void acceptVisitor(WasmExpressionVisitor visitor) { visitor.visit(this); } + + @Override + public boolean isTerminating() { + if (loop) { + return false; + } + return !body.isEmpty() && body.get(body.size() - 1).isTerminating(); + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmBreak.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmBreak.java index 57e954bf1..f99328064 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmBreak.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmBreak.java @@ -47,4 +47,9 @@ public class WasmBreak extends WasmExpression { public void acceptVisitor(WasmExpressionVisitor visitor) { visitor.visit(this); } + + @Override + public boolean isTerminating() { + return true; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConditional.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConditional.java index 8fb17a5b8..e0c22cb3e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConditional.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmConditional.java @@ -58,4 +58,9 @@ public class WasmConditional extends WasmExpression { public void acceptVisitor(WasmExpressionVisitor visitor) { visitor.visit(this); } + + @Override + public boolean isTerminating() { + return thenBlock.isTerminating() && elseBlock.isTerminating(); + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java index 97cb0b3f2..62089a5ab 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpression.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.wasm.model.expression; +import org.teavm.backend.wasm.model.WasmType; import org.teavm.model.TextLocation; public abstract class WasmExpression { @@ -32,4 +33,26 @@ public abstract class WasmExpression { } public abstract void acceptVisitor(WasmExpressionVisitor visitor); + + public boolean isTerminating() { + return false; + } + + public static WasmExpression defaultValueOfType(WasmType type) { + if (type == null) { + return null; + } + switch (type) { + case INT32: + return new WasmInt32Constant(0); + case INT64: + return new WasmInt64Constant(0); + case FLOAT32: + return new WasmFloat32Constant(0); + case FLOAT64: + return new WasmFloat64Constant(0); + default: + throw new IllegalArgumentException(); + } + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIntUnaryOperation.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIntUnaryOperation.java index aa5afce61..9a8c748ba 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIntUnaryOperation.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmIntUnaryOperation.java @@ -16,6 +16,7 @@ package org.teavm.backend.wasm.model.expression; public enum WasmIntUnaryOperation { + EQZ, CLZ, CTZ, POPCNT diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReturn.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReturn.java index 97c468328..18cdfdc71 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReturn.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReturn.java @@ -38,4 +38,9 @@ public class WasmReturn extends WasmExpression { public void acceptVisitor(WasmExpressionVisitor visitor) { visitor.visit(this); } + + @Override + public boolean isTerminating() { + return true; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmSwitch.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmSwitch.java index fc91aa605..50c5fcc81 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmSwitch.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmSwitch.java @@ -55,4 +55,9 @@ public class WasmSwitch extends WasmExpression { public void acceptVisitor(WasmExpressionVisitor visitor) { visitor.visit(this); } + + @Override + public boolean isTerminating() { + return true; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmUnreachable.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmUnreachable.java index ac697ba3c..e14755652 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmUnreachable.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmUnreachable.java @@ -23,4 +23,9 @@ public class WasmUnreachable extends WasmExpression { public void acceptVisitor(WasmExpressionVisitor visitor) { visitor.visit(this); } + + @Override + public boolean isTerminating() { + return true; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java index 51b009a5e..2baeb5f16 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeSectionParser.java @@ -241,6 +241,9 @@ public class CodeSectionParser { codeListener.float64Constant(Double.longBitsToDouble(readFixedLong())); break; + case 0x45: + codeListener.unary(WasmIntUnaryOperation.EQZ, WasmIntType.INT32); + break; case 0x46: codeListener.binary(WasmIntBinaryOperation.EQ, WasmIntType.INT32); break; @@ -272,6 +275,9 @@ public class CodeSectionParser { codeListener.binary(WasmIntBinaryOperation.GE_UNSIGNED, WasmIntType.INT32); break; + case 0x50: + codeListener.unary(WasmIntUnaryOperation.EQZ, WasmIntType.INT64); + break; case 0x51: codeListener.binary(WasmIntBinaryOperation.EQ, WasmIntType.INT64); break; diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java index 8807e2aea..bb61ad342 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java @@ -528,6 +528,9 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { switch (expression.getType()) { case INT32: switch (expression.getOperation()) { + case EQZ: + writer.writeByte(0x45); + break; case CLZ: writer.writeByte(0x67); break; @@ -541,6 +544,9 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { break; case INT64: switch (expression.getOperation()) { + case EQZ: + writer.writeByte(0x50); + break; case CLZ: writer.writeByte(0x79); break; diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java index 62f88034a..b9bd1eb17 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java @@ -109,9 +109,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { if (lfDeferred) { lfDeferred = false; sb.append("\n"); - for (int i = 0; i < indentLevel; ++i) { - sb.append(" "); - } + sb.append(" ".repeat(Math.max(0, indentLevel))); } sb.append(text); return this; @@ -714,6 +712,8 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { private String operation(WasmIntUnaryOperation operation) { switch (operation) { + case EQZ: + return "eqz"; case CLZ: return "clz"; case CTZ: diff --git a/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptor.java b/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptor.java index 9d2bf168f..5d78f79d9 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptor.java +++ b/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptor.java @@ -16,20 +16,7 @@ package org.teavm.model.lowlevel; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; -import org.teavm.model.AnnotationContainer; -import org.teavm.model.AnnotationContainerReader; -import org.teavm.model.AnnotationHolder; -import org.teavm.model.AnnotationReader; -import org.teavm.model.AnnotationValue; -import org.teavm.model.ClassReader; -import org.teavm.model.ClassReaderSource; -import org.teavm.model.MethodReader; -import org.teavm.model.Program; public class CallSiteDescriptor { private int id; @@ -52,88 +39,4 @@ public class CallSiteDescriptor { public List getHandlers() { return handlers; } - - public static void save(Collection descriptors, AnnotationContainer annotations) { - List descriptorsValue = new ArrayList<>(); - for (CallSiteDescriptor descriptor : descriptors) { - AnnotationHolder descriptorAnnot = new AnnotationHolder(CallSiteDescriptorAnnot.class.getName()); - descriptorAnnot.getValues().put("id", new AnnotationValue(descriptor.id)); - descriptorAnnot.getValues().put("location", new AnnotationValue( - CallSiteLocation.saveMany(Arrays.asList(descriptor.locations)))); - List handlersValue = descriptor.handlers.stream() - .map(h -> new AnnotationValue(h.save())) - .collect(Collectors.toList()); - descriptorAnnot.getValues().put("handlers", new AnnotationValue(handlersValue)); - descriptorsValue.add(new AnnotationValue(descriptorAnnot)); - } - - AnnotationHolder descriptorsAnnot = new AnnotationHolder(CallSiteDescriptorsAnnot.class.getName()); - descriptorsAnnot.getValues().put("value", new AnnotationValue(descriptorsValue)); - annotations.add(descriptorsAnnot); - } - - public static Collection load(AnnotationContainerReader annotations) { - AnnotationReader descriptorsAnnot = annotations.get(CallSiteDescriptorsAnnot.class.getName()); - if (descriptorsAnnot == null) { - return Collections.emptyList(); - } - - List descriptors = new ArrayList<>(); - for (AnnotationValue descriptorValue : descriptorsAnnot.getValue("value").getList()) { - AnnotationReader descriptorAnnot = descriptorValue.getAnnotation(); - int id = descriptorAnnot.getValue("id").getInt(); - List location = CallSiteLocation.loadMany( - descriptorAnnot.getValue("location").getAnnotation()); - List handlers = descriptorAnnot.getValue("handlers").getList().stream() - .map(a -> ExceptionHandlerDescriptor.load(a.getAnnotation())) - .collect(Collectors.toList()); - CallSiteDescriptor descriptor = new CallSiteDescriptor(id, location.toArray(new CallSiteLocation[0])); - descriptor.getHandlers().addAll(handlers); - descriptors.add(descriptor); - } - - return descriptors; - } - - public static List extract(Program program) { - List result = new ArrayList<>(); - extractTo(load(program.getAnnotations()), result); - return result; - } - - public static List extract(ClassReaderSource classes, - Collection classNames) { - List result = new ArrayList<>(); - for (String className : classNames) { - ClassReader cls = classes.get(className); - if (cls == null) { - continue; - } - for (MethodReader method : cls.getMethods()) { - if (method.getProgram() != null) { - extractTo(load(method.getProgram().getAnnotations()), result); - } - } - } - return result; - } - - private static void extractTo(Collection descriptors, - List result) { - for (CallSiteDescriptor descriptor : descriptors) { - if (descriptor.id >= result.size()) { - result.addAll(Collections.nCopies(descriptor.id - result.size() + 1, null)); - } - result.set(descriptor.id, descriptor); - } - } - - static AnnotationValue saveNullableString(String s) { - return new AnnotationValue(s != null ? "1" + s : "0"); - } - - static String loadNullableString(AnnotationValue value) { - String s = value.getString(); - return s.startsWith("0") ? null : s.substring(1); - } } diff --git a/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorAnnot.java b/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorAnnot.java deleted file mode 100644 index 04fdd152c..000000000 --- a/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorAnnot.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2019 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.model.lowlevel; - -@interface CallSiteDescriptorAnnot { - int id(); - - ExceptionHandlerDescriptorAnnot[] handlers(); - - CallSiteLocationsAnnot location(); -} diff --git a/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorsAnnot.java b/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorsAnnot.java deleted file mode 100644 index 9321fd238..000000000 --- a/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorsAnnot.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2019 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.model.lowlevel; - -@interface CallSiteDescriptorsAnnot { - CallSiteDescriptorAnnot[] value(); -} diff --git a/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocation.java b/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocation.java index 329648ea4..d9677fbf7 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocation.java +++ b/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocation.java @@ -18,9 +18,6 @@ package org.teavm.model.lowlevel; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import org.teavm.model.AnnotationHolder; -import org.teavm.model.AnnotationReader; -import org.teavm.model.AnnotationValue; import org.teavm.model.InliningInfo; import org.teavm.model.MethodReference; import org.teavm.model.TextLocation; @@ -114,39 +111,4 @@ public class CallSiteLocation { public int hashCode() { return Objects.hash(fileName, className, methodName, lineNumber); } - - public AnnotationReader save() { - AnnotationHolder annotation = new AnnotationHolder(CallSiteLocationAnnot.class.getName()); - annotation.getValues().put("fileName", CallSiteDescriptor.saveNullableString(fileName)); - annotation.getValues().put("className", CallSiteDescriptor.saveNullableString(className)); - annotation.getValues().put("methodName", CallSiteDescriptor.saveNullableString(methodName)); - annotation.getValues().put("lineNumber", new AnnotationValue(lineNumber)); - return annotation; - } - - public static CallSiteLocation load(AnnotationReader reader) { - return new CallSiteLocation( - CallSiteDescriptor.loadNullableString(reader.getValue("fileName")), - CallSiteDescriptor.loadNullableString(reader.getValue("className")), - CallSiteDescriptor.loadNullableString(reader.getValue("methodName")), - reader.getValue("lineNumber").getInt()); - } - - public static AnnotationReader saveMany(List locations) { - AnnotationHolder annotation = new AnnotationHolder(CallSiteLocationsAnnot.class.getName()); - List list = new ArrayList<>(); - for (CallSiteLocation location : locations) { - list.add(new AnnotationValue(location.save())); - } - annotation.getValues().put("value", new AnnotationValue(list)); - return annotation; - } - - public static List loadMany(AnnotationReader reader) { - List result = new ArrayList<>(); - for (AnnotationValue item : reader.getValue("value").getList()) { - result.add(load(item.getAnnotation())); - } - return result; - } } diff --git a/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocationsAnnot.java b/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocationsAnnot.java deleted file mode 100644 index c5b82695e..000000000 --- a/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocationsAnnot.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2019 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.model.lowlevel; - -@interface CallSiteLocationsAnnot { - CallSiteLocationAnnot[] value(); -} diff --git a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptor.java b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptor.java index 99152a122..ef3408865 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptor.java +++ b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptor.java @@ -15,10 +15,6 @@ */ package org.teavm.model.lowlevel; -import org.teavm.model.AnnotationHolder; -import org.teavm.model.AnnotationReader; -import org.teavm.model.AnnotationValue; - public class ExceptionHandlerDescriptor { private int id; private String className; @@ -35,17 +31,4 @@ public class ExceptionHandlerDescriptor { public String getClassName() { return className; } - - public AnnotationReader save() { - AnnotationHolder annot = new AnnotationHolder(ExceptionHandlerDescriptorAnnot.class.getName()); - annot.getValues().put("id", new AnnotationValue(id)); - annot.getValues().put("className", CallSiteDescriptor.saveNullableString(className)); - return annot; - } - - public static ExceptionHandlerDescriptor load(AnnotationReader annot) { - return new ExceptionHandlerDescriptor( - annot.getValue("id").getInt(), - CallSiteDescriptor.loadNullableString(annot.getValue("className"))); - } } diff --git a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptorAnnot.java b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptorAnnot.java deleted file mode 100644 index 60d15c57e..000000000 --- a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlerDescriptorAnnot.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2019 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.model.lowlevel; - -@interface ExceptionHandlerDescriptorAnnot { - int id(); - - String className(); -} diff --git a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java deleted file mode 100644 index 0685a0539..000000000 --- a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java +++ /dev/null @@ -1,510 +0,0 @@ -/* - * Copyright 2016 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.model.lowlevel; - -import com.carrotsearch.hppc.IntHashSet; -import com.carrotsearch.hppc.IntObjectHashMap; -import com.carrotsearch.hppc.IntSet; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import org.teavm.common.DominatorTree; -import org.teavm.common.Graph; -import org.teavm.common.GraphUtils; -import org.teavm.model.BasicBlock; -import org.teavm.model.Incoming; -import org.teavm.model.Instruction; -import org.teavm.model.MethodReference; -import org.teavm.model.Phi; -import org.teavm.model.Program; -import org.teavm.model.TextLocation; -import org.teavm.model.TryCatchBlock; -import org.teavm.model.ValueType; -import org.teavm.model.Variable; -import org.teavm.model.instructions.BinaryBranchingCondition; -import org.teavm.model.instructions.BinaryBranchingInstruction; -import org.teavm.model.instructions.BoundCheckInstruction; -import org.teavm.model.instructions.CastInstruction; -import org.teavm.model.instructions.CloneArrayInstruction; -import org.teavm.model.instructions.ConstructArrayInstruction; -import org.teavm.model.instructions.ConstructInstruction; -import org.teavm.model.instructions.ConstructMultiArrayInstruction; -import org.teavm.model.instructions.DoubleConstantInstruction; -import org.teavm.model.instructions.ExitInstruction; -import org.teavm.model.instructions.FloatConstantInstruction; -import org.teavm.model.instructions.InitClassInstruction; -import org.teavm.model.instructions.IntegerConstantInstruction; -import org.teavm.model.instructions.InvocationType; -import org.teavm.model.instructions.InvokeInstruction; -import org.teavm.model.instructions.JumpInstruction; -import org.teavm.model.instructions.LongConstantInstruction; -import org.teavm.model.instructions.MonitorEnterInstruction; -import org.teavm.model.instructions.MonitorExitInstruction; -import org.teavm.model.instructions.NullCheckInstruction; -import org.teavm.model.instructions.NullConstantInstruction; -import org.teavm.model.instructions.RaiseInstruction; -import org.teavm.model.instructions.SwitchInstruction; -import org.teavm.model.instructions.SwitchTableEntry; -import org.teavm.model.optimization.UnreachableBasicBlockEliminator; -import org.teavm.model.util.DefinitionExtractor; -import org.teavm.model.util.ProgramUtils; -import org.teavm.runtime.ExceptionHandling; -import org.teavm.runtime.ShadowStack; - -public class ExceptionHandlingShadowStackContributor { - private static final MethodReference FILL_STACK_TRACE = new MethodReference(ExceptionHandling.class, - "fillStackTrace", StackTraceElement[].class); - private Characteristics characteristics; - private List callSites; - private BasicBlock defaultExceptionHandler; - private MethodReference method; - private Program program; - private DominatorTree dom; - private BasicBlock[] variableDefinitionPlaces; - private boolean hasExceptionHandlers; - private int parameterCount; - private boolean hasDetachedBlocks; - - public int callSiteIdGen; - - public ExceptionHandlingShadowStackContributor(Characteristics characteristics, - List callSites, MethodReference method, Program program) { - this.characteristics = characteristics; - this.callSites = callSites; - this.method = method; - this.program = program; - - Graph cfg = ProgramUtils.buildControlFlowGraph(program); - dom = GraphUtils.buildDominatorTree(cfg); - variableDefinitionPlaces = ProgramUtils.getVariableDefinitionPlaces(program); - parameterCount = method.parameterCount() + 1; - } - - public boolean contribute() { - int[] blockMapping = new int[program.basicBlockCount()]; - for (int i = 0; i < blockMapping.length; ++i) { - blockMapping[i] = i; - } - - List allPhis = new ArrayList<>(); - int blockCount = program.basicBlockCount(); - for (int i = 0; i < blockCount; ++i) { - allPhis.addAll(program.basicBlockAt(i).getPhis()); - } - - Set exceptionHandlers = new HashSet<>(); - for (int i = 0; i < blockCount; ++i) { - BasicBlock block = program.basicBlockAt(i); - for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) { - exceptionHandlers.add(tryCatch.getHandler()); - } - - if (block.getExceptionVariable() != null) { - InvokeInstruction catchCall = new InvokeInstruction(); - catchCall.setType(InvocationType.SPECIAL); - catchCall.setMethod(new MethodReference(ExceptionHandling.class, "catchException", - Throwable.class)); - catchCall.setReceiver(block.getExceptionVariable()); - block.addFirst(catchCall); - block.setExceptionVariable(null); - } - - int newIndex = contributeToBasicBlock(block); - if (newIndex != i) { - blockMapping[i] = newIndex; - hasExceptionHandlers = true; - } - } - - for (Phi phi : allPhis) { - if (!exceptionHandlers.contains(phi.getBasicBlock())) { - for (Incoming incoming : phi.getIncomings()) { - int mappedSource = blockMapping[incoming.getSource().getIndex()]; - incoming.setSource(program.basicBlockAt(mappedSource)); - } - } - } - - if (hasDetachedBlocks) { - new UnreachableBasicBlockEliminator().optimize(program); - } - - return hasExceptionHandlers; - } - - private int contributeToBasicBlock(BasicBlock block) { - int[] currentJointSources = new int[program.variableCount()]; - var jointReceiverMaps = new IntObjectHashMap(); - Arrays.fill(currentJointSources, -1); - - IntSet variablesDefinedHere = new IntHashSet(); - - for (var tryCatch : block.getTryCatchBlocks()) { - int[] jointReceiverMap = new int[program.variableCount()]; - Arrays.fill(jointReceiverMap, -1); - for (Phi phi : tryCatch.getHandler().getPhis()) { - var sourceVariables = phi.getIncomings().stream() - .filter(incoming -> incoming.getSource() == tryCatch.getProtectedBlock()) - .map(incoming -> incoming.getValue()) - .collect(Collectors.toList()); - if (sourceVariables.isEmpty()) { - continue; - } - - for (Variable sourceVar : sourceVariables) { - BasicBlock sourceVarDefinedAt = variableDefinitionPlaces[sourceVar.getIndex()]; - if (sourceVar.getIndex() < parameterCount - || (dom.dominates(sourceVarDefinedAt.getIndex(), block.getIndex()) - && block != sourceVarDefinedAt)) { - currentJointSources[phi.getReceiver().getIndex()] = sourceVar.getIndex(); - if (sourceVarDefinedAt != block) { - break; - } - } - } - for (Variable sourceVar : sourceVariables) { - jointReceiverMap[sourceVar.getIndex()] = phi.getReceiver().getIndex(); - } - } - - jointReceiverMaps.put(tryCatch.getHandler().getIndex(), jointReceiverMap); - } - - for (Phi phi : block.getPhis()) { - Variable definedVar = phi.getReceiver(); - for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) { - int jointReceiver = jointReceiverMaps.get(tryCatch.getHandler().getIndex())[definedVar.getIndex()]; - if (jointReceiver >= 0) { - currentJointSources[jointReceiver] = definedVar.getIndex(); - } - } - variablesDefinedHere.add(definedVar.getIndex()); - } - - var defExtractor = new DefinitionExtractor(); - List blocksToClearHandlers = new ArrayList<>(); - blocksToClearHandlers.add(block); - BasicBlock initialBlock = block; - - for (Instruction insn : block) { - insn.acceptVisitor(defExtractor); - for (Variable definedVar : defExtractor.getDefinedVariables()) { - for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) { - int jointReceiver = jointReceiverMaps.get(tryCatch.getHandler().getIndex())[definedVar.getIndex()]; - if (jointReceiver >= 0) { - currentJointSources[jointReceiver] = definedVar.getIndex(); - } - } - variablesDefinedHere.add(definedVar.getIndex()); - } - - if (isCallInstruction(insn)) { - BasicBlock next; - boolean last = false; - if (isSpecialCallInstruction(insn)) { - next = null; - while (insn.getNext() != null) { - Instruction nextInsn = insn.getNext(); - nextInsn.delete(); - } - hasDetachedBlocks = true; - last = true; - } else if (insn instanceof RaiseInstruction) { - InvokeInstruction raise = new InvokeInstruction(); - raise.setMethod(new MethodReference(ExceptionHandling.class, "throwException", Throwable.class, - void.class)); - raise.setType(InvocationType.SPECIAL); - raise.setArguments(((RaiseInstruction) insn).getException()); - raise.setLocation(insn.getLocation()); - insn.replace(raise); - insn = raise; - next = null; - } else if (insn.getNext() != null && insn.getNext() instanceof JumpInstruction) { - next = ((JumpInstruction) insn.getNext()).getTarget(); - insn.getNext().delete(); - last = true; - } else { - next = program.createBasicBlock(); - next.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program)); - blocksToClearHandlers.add(next); - - while (insn.getNext() != null) { - Instruction nextInsn = insn.getNext(); - nextInsn.delete(); - next.add(nextInsn); - } - } - - var locations = CallSiteLocation.fromTextLocation(insn.getLocation(), method); - var callSite = new CallSiteDescriptor(callSiteIdGen++, locations); - callSites.add(callSite); - var pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation()); - var post = getInstructionsAfterCallSite(initialBlock, block, next, callSite, - currentJointSources, variablesDefinedHere); - post = setLocation(post, insn.getLocation()); - block.getLastInstruction().insertPreviousAll(pre); - block.addAll(post); - hasExceptionHandlers = true; - - if (next == null || last) { - break; - } - block = next; - variablesDefinedHere.clear(); - } - } - - removeOutgoingPhis(block); - for (BasicBlock blockToClear : blocksToClearHandlers) { - blockToClear.getTryCatchBlocks().clear(); - } - - return block.getIndex(); - } - - private boolean isCallInstruction(Instruction insn) { - return isCallInstruction(characteristics, insn); - } - - public static boolean isCallInstruction(Characteristics characteristics, Instruction insn) { - if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction - || insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction - || insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction - || insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction - || insn instanceof NullCheckInstruction || insn instanceof BoundCheckInstruction - || insn instanceof CastInstruction) { - return true; - } else if (insn instanceof InvokeInstruction) { - return isManagedMethodCall(characteristics, ((InvokeInstruction) insn).getMethod()); - } - return false; - } - - public static boolean isManagedMethodCall(Characteristics characteristics, MethodReference method) { - if (characteristics.isManaged(method) || method.equals(FILL_STACK_TRACE)) { - return true; - } - return method.getClassName().equals(ExceptionHandling.class.getName()) - && method.getName().startsWith("throw"); - } - - private boolean isSpecialCallInstruction(Instruction insn) { - if (!(insn instanceof InvokeInstruction)) { - return false; - } - MethodReference method = ((InvokeInstruction) insn).getMethod(); - return method.getClassName().equals(ExceptionHandling.class.getName()) && method.getName().startsWith("throw"); - } - - private List setLocation(List instructions, TextLocation location) { - if (location != null) { - for (Instruction instruction : instructions) { - instruction.setLocation(location); - } - } - return instructions; - } - - private List getInstructionsBeforeCallSite(CallSiteDescriptor callSite) { - List instructions = new ArrayList<>(); - - Variable idVariable = program.createVariable(); - IntegerConstantInstruction idInsn = new IntegerConstantInstruction(); - idInsn.setConstant(callSite.getId()); - idInsn.setReceiver(idVariable); - instructions.add(idInsn); - - InvokeInstruction registerInsn = new InvokeInstruction(); - registerInsn.setMethod(new MethodReference(ShadowStack.class, "registerCallSite", int.class, void.class)); - registerInsn.setType(InvocationType.SPECIAL); - registerInsn.setArguments(idVariable); - instructions.add(registerInsn); - - return instructions; - } - - private List getInstructionsAfterCallSite(BasicBlock initialBlock, BasicBlock block, BasicBlock next, - CallSiteDescriptor callSite, int[] currentJointSources, IntSet variablesDefinedHere) { - Program program = block.getProgram(); - List instructions = new ArrayList<>(); - - Variable handlerIdVariable = program.createVariable(); - InvokeInstruction getHandlerIdInsn = new InvokeInstruction(); - getHandlerIdInsn.setMethod(new MethodReference(ShadowStack.class, "getExceptionHandlerId", int.class)); - getHandlerIdInsn.setType(InvocationType.SPECIAL); - getHandlerIdInsn.setReceiver(handlerIdVariable); - instructions.add(getHandlerIdInsn); - - SwitchInstruction switchInsn = new SwitchInstruction(); - switchInsn.setCondition(handlerIdVariable); - - if (next != null) { - SwitchTableEntry continueExecutionEntry = new SwitchTableEntry(); - continueExecutionEntry.setCondition(callSite.getId()); - continueExecutionEntry.setTarget(next); - switchInsn.getEntries().add(continueExecutionEntry); - } - - boolean defaultExists = false; - int nextHandlerId = callSite.getId(); - for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) { - ExceptionHandlerDescriptor handler = new ExceptionHandlerDescriptor(++nextHandlerId, - tryCatch.getExceptionType()); - callSite.getHandlers().add(handler); - - if (tryCatch.getExceptionType() == null) { - defaultExists = true; - switchInsn.setDefaultTarget(tryCatch.getHandler()); - } else { - SwitchTableEntry catchEntry = new SwitchTableEntry(); - catchEntry.setTarget(tryCatch.getHandler()); - catchEntry.setCondition(handler.getId()); - switchInsn.getEntries().add(catchEntry); - } - } - fixOutgoingPhis(initialBlock, block, currentJointSources, variablesDefinedHere); - - if (!defaultExists) { - switchInsn.setDefaultTarget(getDefaultExceptionHandler()); - } - - if (switchInsn.getEntries().isEmpty()) { - instructions.clear(); - JumpInstruction jump = new JumpInstruction(); - jump.setTarget(switchInsn.getDefaultTarget()); - instructions.add(jump); - } else if (switchInsn.getEntries().size() == 1) { - SwitchTableEntry entry = switchInsn.getEntries().get(0); - - IntegerConstantInstruction singleTestConstant = new IntegerConstantInstruction(); - singleTestConstant.setConstant(entry.getCondition()); - singleTestConstant.setReceiver(program.createVariable()); - instructions.add(singleTestConstant); - - BinaryBranchingInstruction branching = new BinaryBranchingInstruction(BinaryBranchingCondition.EQUAL); - branching.setConsequent(entry.getTarget()); - branching.setAlternative(switchInsn.getDefaultTarget()); - branching.setFirstOperand(switchInsn.getCondition()); - branching.setSecondOperand(singleTestConstant.getReceiver()); - instructions.add(branching); - } else { - instructions.add(switchInsn); - } - - return instructions; - } - - private void fixOutgoingPhis(BasicBlock block, BasicBlock newBlock, int[] currentJointSources, - IntSet variablesDefinedHere) { - for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) { - for (Phi phi : tryCatch.getHandler().getPhis()) { - int value = currentJointSources[phi.getReceiver().getIndex()]; - if (value < 0) { - continue; - } - List additionalIncomings = new ArrayList<>(); - for (int i = 0; i < phi.getIncomings().size(); i++) { - Incoming incoming = phi.getIncomings().get(i); - if (incoming.getSource() != block) { - continue; - } - if (incoming.getValue().getIndex() == value) { - if (variablesDefinedHere.contains(value)) { - incoming.setSource(newBlock); - } else { - Incoming incomingCopy = new Incoming(); - incomingCopy.setSource(newBlock); - incomingCopy.setValue(incoming.getValue()); - additionalIncomings.add(incomingCopy); - } - } - } - - phi.getIncomings().addAll(additionalIncomings); - } - } - } - - private void removeOutgoingPhis(BasicBlock block) { - for (var tryCatch : block.getTryCatchBlocks()) { - for (var iterator = tryCatch.getHandler().getPhis().iterator(); iterator.hasNext();) { - var phi = iterator.next(); - phi.getIncomings().removeIf(incoming -> incoming.getSource() == block); - if (phi.getIncomings().isEmpty()) { - iterator.remove(); - } - } - } - } - - private BasicBlock getDefaultExceptionHandler() { - if (defaultExceptionHandler == null) { - defaultExceptionHandler = program.createBasicBlock(); - Variable result = createReturnValueInstructions(defaultExceptionHandler); - ExitInstruction exit = new ExitInstruction(); - exit.setValueToReturn(result); - defaultExceptionHandler.add(exit); - } - return defaultExceptionHandler; - } - - private Variable createReturnValueInstructions(BasicBlock block) { - ValueType returnType = method.getReturnType(); - if (returnType == ValueType.VOID) { - return null; - } - - Variable variable = program.createVariable(); - - if (returnType instanceof ValueType.Primitive) { - switch (((ValueType.Primitive) returnType).getKind()) { - case BOOLEAN: - case BYTE: - case SHORT: - case CHARACTER: - case INTEGER: - IntegerConstantInstruction intConstant = new IntegerConstantInstruction(); - intConstant.setReceiver(variable); - block.add(intConstant); - return variable; - case LONG: - LongConstantInstruction longConstant = new LongConstantInstruction(); - longConstant.setReceiver(variable); - block.add(longConstant); - return variable; - case FLOAT: - FloatConstantInstruction floatConstant = new FloatConstantInstruction(); - floatConstant.setReceiver(variable); - block.add(floatConstant); - return variable; - case DOUBLE: - DoubleConstantInstruction doubleConstant = new DoubleConstantInstruction(); - doubleConstant.setReceiver(variable); - block.add(doubleConstant); - return variable; - } - } - - NullConstantInstruction nullConstant = new NullConstantInstruction(); - nullConstant.setReceiver(variable); - block.add(nullConstant); - - return variable; - } -} diff --git a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingUtil.java b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingUtil.java new file mode 100644 index 000000000..ec75dc9b9 --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingUtil.java @@ -0,0 +1,62 @@ +/* + * Copyright 2016 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.model.lowlevel; + +import org.teavm.model.Instruction; +import org.teavm.model.MethodReference; +import org.teavm.model.instructions.BoundCheckInstruction; +import org.teavm.model.instructions.CastInstruction; +import org.teavm.model.instructions.CloneArrayInstruction; +import org.teavm.model.instructions.ConstructArrayInstruction; +import org.teavm.model.instructions.ConstructInstruction; +import org.teavm.model.instructions.ConstructMultiArrayInstruction; +import org.teavm.model.instructions.InitClassInstruction; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.instructions.MonitorEnterInstruction; +import org.teavm.model.instructions.MonitorExitInstruction; +import org.teavm.model.instructions.NullCheckInstruction; +import org.teavm.model.instructions.RaiseInstruction; +import org.teavm.runtime.ExceptionHandling; + +public final class ExceptionHandlingUtil { + private static final MethodReference FILL_STACK_TRACE = new MethodReference(ExceptionHandling.class, + "fillStackTrace", StackTraceElement[].class); + + private ExceptionHandlingUtil() { + } + + public static boolean isCallInstruction(Characteristics characteristics, Instruction insn) { + if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction + || insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction + || insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction + || insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction + || insn instanceof NullCheckInstruction || insn instanceof BoundCheckInstruction + || insn instanceof CastInstruction) { + return true; + } else if (insn instanceof InvokeInstruction) { + return isManagedMethodCall(characteristics, ((InvokeInstruction) insn).getMethod()); + } + return false; + } + + public static boolean isManagedMethodCall(Characteristics characteristics, MethodReference method) { + if (characteristics.isManaged(method) || method.equals(FILL_STACK_TRACE)) { + return true; + } + return method.getClassName().equals(ExceptionHandling.class.getName()) + && method.getName().startsWith("throw"); + } +} diff --git a/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java b/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java index 700d07786..b1dc85133 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java +++ b/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java @@ -160,7 +160,7 @@ public class GCShadowStackContributor { for (Variable definedVar : defExtractor.getDefinedVariables()) { currentLiveOut.clear(definedVar.getIndex()); } - if (ExceptionHandlingShadowStackContributor.isCallInstruction(characteristics, insn)) { + if (ExceptionHandlingUtil.isCallInstruction(characteristics, insn)) { BitSet csLiveIn = (BitSet) currentLiveOut.clone(); for (int v = csLiveIn.nextSetBit(0); v >= 0; v = csLiveIn.nextSetBit(v + 1)) { if (!isReference(typeInferer, v) || nativePointers[v] || constants.get(v)) { diff --git a/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java b/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java index 0d8897178..d13513d5d 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java +++ b/core/src/main/java/org/teavm/model/lowlevel/ShadowStackTransformer.java @@ -37,13 +37,10 @@ import org.teavm.runtime.ShadowStack; public class ShadowStackTransformer { private Characteristics characteristics; private GCShadowStackContributor gcContributor; - private boolean exceptionHandling; - private int callSiteIdGen; - public ShadowStackTransformer(Characteristics characteristics, boolean exceptionHandling) { + public ShadowStackTransformer(Characteristics characteristics) { gcContributor = new GCShadowStackContributor(characteristics); this.characteristics = characteristics; - this.exceptionHandling = exceptionHandling; } public void apply(Program program, MethodReader method) { @@ -52,27 +49,16 @@ public class ShadowStackTransformer { } int shadowStackSize = gcContributor.contribute(program, method); - boolean exceptions; - if (exceptionHandling) { - List callSites = new ArrayList<>(); - var exceptionContributor = new ExceptionHandlingShadowStackContributor(characteristics, callSites, - method.getReference(), program); - exceptionContributor.callSiteIdGen = callSiteIdGen; - exceptions = exceptionContributor.contribute(); - callSiteIdGen = exceptionContributor.callSiteIdGen; - CallSiteDescriptor.save(callSites, program.getAnnotations()); - } else { - exceptions = false; - outer: for (BasicBlock block : program.getBasicBlocks()) { - if (!block.getTryCatchBlocks().isEmpty()) { + var exceptions = false; + outer: for (BasicBlock block : program.getBasicBlocks()) { + if (!block.getTryCatchBlocks().isEmpty()) { + exceptions = true; + break; + } + for (Instruction insn : block) { + if (ExceptionHandlingUtil.isCallInstruction(characteristics, insn)) { exceptions = true; - break; - } - for (Instruction insn : block) { - if (ExceptionHandlingShadowStackContributor.isCallInstruction(characteristics, insn)) { - exceptions = true; - break outer; - } + break outer; } } } diff --git a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java index e85584a9c..20453ba1e 100644 --- a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java +++ b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java @@ -117,24 +117,26 @@ public final class ExceptionHandling { } if (!isJumpSupported()) { - ShadowStack.setExceptionHandlerId(stackFrame, callSiteId - 1); + ShadowStack.setExceptionHandlerSkip(stackFrame); } } stackFrame = ShadowStack.getNextStackFrame(stackFrame); } if (stackFrame == null) { - stackFrame = ShadowStack.getStackTop(); - while (stackFrame != null) { - int callSiteId = ShadowStack.getCallSiteId(stackFrame); - if (callSiteId >= 0) { - ShadowStack.setExceptionHandlerId(stackFrame, callSiteId + 1); + if (!isJumpSupported()) { + stackFrame = ShadowStack.getStackTop(); + while (stackFrame != null) { + int callSiteId = ShadowStack.getCallSiteId(stackFrame); + if (callSiteId >= 0) { + ShadowStack.setExceptionHandlerRestore(stackFrame); + } + stackFrame = ShadowStack.getNextStackFrame(stackFrame); } - stackFrame = ShadowStack.getNextStackFrame(stackFrame); } printStack(); abort(); - } else if (isJumpSupported()) { + } else { jumpToFrame(stackFrame, handlerId); } } diff --git a/core/src/main/java/org/teavm/runtime/ShadowStack.java b/core/src/main/java/org/teavm/runtime/ShadowStack.java index e7f4913b2..c4c3e3214 100644 --- a/core/src/main/java/org/teavm/runtime/ShadowStack.java +++ b/core/src/main/java/org/teavm/runtime/ShadowStack.java @@ -48,4 +48,8 @@ public final class ShadowStack { public static native int getExceptionHandlerId(); public static native void setExceptionHandlerId(Address stackFrame, int id); + + public static native void setExceptionHandlerSkip(Address stackFrame); + + public static native void setExceptionHandlerRestore(Address stackFrame); } diff --git a/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c b/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c index 3f6063157..03252854d 100644 --- a/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c +++ b/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c @@ -155,8 +155,15 @@ int32_t wasi_snapshot_preview1_fd_write(int32_t fd, int32_t iovs, int32_t count, } void teavm_putwcharsOut(int32_t chars, int32_t count) { - int16_t* chars_array = (int16_t*) (wasm_heap + chars); + char* chars_array = (char*) (wasm_heap + chars); for (int32_t i = 0; i < count; ++i) { - putwchar(chars_array[i]); + putc(chars_array[i], stdout); + } +} + +void teavm_putwcharsErr(int32_t chars, int32_t count) { + char* chars_array = (char*) (wasm_heap + chars); + for (int32_t i = 0; i < count; ++i) { + putc(chars_array[i], stderr); } } \ No newline at end of file diff --git a/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.js b/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.js index c84b1770b..29d9acba0 100644 --- a/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.js +++ b/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.js @@ -58,11 +58,11 @@ TeaVM.wasm = function() { function dateToString(timestamp, controller) { const s = new Date(timestamp).toString(); let instance = controller.instance; - let result = instance.allocateString(s.length); + let result = instance.exports.teavm_allocateString(s.length); if (result === 0) { return 0; } - let resultAddress = instance.objectArrayData(instance.stringData(result)); + let resultAddress = instance.exports.teavm_objectArrayData(instance.exports.teavm_stringData(result)); let resultView = new Uint16Array(instance.exports.memory.buffer, resultAddress, s.length); for (let i = 0; i < s.length; ++i) { resultView[i] = s.charCodeAt(i); diff --git a/gradle.properties b/gradle.properties index 6d1a97dec..c57deb686 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,4 +23,5 @@ teavm.project.version=0.9.0-SNAPSHOT teavm.tests.decodeStack=false teavm.tests.optimized=true teavm.tests.minified=false -teavm.tests.c=true \ No newline at end of file +teavm.tests.c=true +teavm.tests.wasm=true \ No newline at end of file diff --git a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java index df606a5bd..82094aea8 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java @@ -74,6 +74,7 @@ public class PlatformPlugin implements TeaVMPlugin, MetadataRegistration { if (!isBootstrap()) { TeaVMWasmHost wasmHost = host.getExtension(TeaVMWasmHost.class); if (wasmHost != null) { + host.add(new ResourceLowLevelTransformer()); metadataGeneratorConsumers.add((constructor, method, generator) -> { wasmHost.add(ctx -> new MetadataIntrinsic(ctx.getClassSource(), ctx.getClassLoader(), ctx.getServices(), ctx.getProperties(), constructor, method, generator)); @@ -95,7 +96,7 @@ public class PlatformPlugin implements TeaVMPlugin, MetadataRegistration { TeaVMCHost cHost = host.getExtension(TeaVMCHost.class); if (cHost != null) { - host.add(new ResourceCTransformer()); + host.add(new ResourceLowLevelTransformer()); MetadataCIntrinsic metadataCIntrinsic = new MetadataCIntrinsic(); cHost.addGenerator(ctx -> { metadataCIntrinsic.init(ctx.getClassSource(), ctx.getClassLoader(), diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceCTransformer.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceLowLevelTransformer.java similarity index 94% rename from platform/src/main/java/org/teavm/platform/plugin/ResourceCTransformer.java rename to platform/src/main/java/org/teavm/platform/plugin/ResourceLowLevelTransformer.java index 1c9ff0098..38959dadb 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceCTransformer.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceLowLevelTransformer.java @@ -21,7 +21,7 @@ import org.teavm.model.ClassHolderTransformerContext; import org.teavm.model.MethodHolder; import org.teavm.model.Program; -class ResourceCTransformer implements ClassHolderTransformer { +class ResourceLowLevelTransformer implements ClassHolderTransformer { @Override public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) { for (MethodHolder method : cls.getMethods()) { diff --git a/tests/src/test/java/org/teavm/classlib/java/io/PipedInputStreamTest.java b/tests/src/test/java/org/teavm/classlib/java/io/PipedInputStreamTest.java index 17b3f8225..779401dab 100644 --- a/tests/src/test/java/org/teavm/classlib/java/io/PipedInputStreamTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/io/PipedInputStreamTest.java @@ -106,7 +106,7 @@ public class PipedInputStreamTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void available() throws Exception { pis = new PipedInputStream(); pos = new PipedOutputStream(); @@ -150,7 +150,7 @@ public class PipedInputStreamTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void connectLjava_io_PipedOutputStream() throws Exception { pis = new PipedInputStream(); pos = new PipedOutputStream(); @@ -168,7 +168,7 @@ public class PipedInputStreamTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void test_read() throws Exception { pis = new PipedInputStream(); pos = new PipedOutputStream(); @@ -186,7 +186,7 @@ public class PipedInputStreamTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void test_read$BII() throws Exception { pis = new PipedInputStream(); pos = new PipedOutputStream(); @@ -375,7 +375,7 @@ public class PipedInputStreamTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void read_after_write_close() throws Exception { PipedInputStream in = new PipedInputStream(); PipedOutputStream out = new PipedOutputStream(); diff --git a/tests/src/test/java/org/teavm/classlib/java/io/PipedOutputStreamTest.java b/tests/src/test/java/org/teavm/classlib/java/io/PipedOutputStreamTest.java index 15168226d..99487b19f 100644 --- a/tests/src/test/java/org/teavm/classlib/java/io/PipedOutputStreamTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/io/PipedOutputStreamTest.java @@ -96,7 +96,7 @@ public class PipedOutputStreamTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void close() throws Exception { out = new PipedOutputStream(); reader = new PReader(out); @@ -132,7 +132,7 @@ public class PipedOutputStreamTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void flush() throws IOException { out = new PipedOutputStream(); reader = new PReader(out); @@ -145,7 +145,7 @@ public class PipedOutputStreamTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void write$BII() throws IOException { out = new PipedOutputStream(); reader = new PReader(out); diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/ClassLoaderTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/ClassLoaderTest.java index 06d5b8d36..1db855f46 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/ClassLoaderTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/ClassLoaderTest.java @@ -32,7 +32,7 @@ import org.teavm.junit.TestPlatform; @EachTestCompiledSeparately public class ClassLoaderTest { @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void loadsResources() { assertEquals("q", loadResource("1")); assertEquals("qw", loadResource("2")); diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java index 521f77681..58dfeea7e 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/ClassTest.java @@ -31,7 +31,7 @@ import org.teavm.junit.TeaVMTestRunner; import org.teavm.junit.TestPlatform; @RunWith(TeaVMTestRunner.class) -@SkipPlatform(TestPlatform.C) +@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public class ClassTest { @Test public void classNameEvaluated() { diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/invoke/SerializedLambdaTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/invoke/SerializedLambdaTest.java index ed0a81151..972b2a22d 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/invoke/SerializedLambdaTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/invoke/SerializedLambdaTest.java @@ -30,7 +30,7 @@ import org.teavm.junit.TestPlatform; @RunWith(TeaVMTestRunner.class) @EachTestCompiledSeparately -@SkipPlatform(TestPlatform.C) +@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public class SerializedLambdaTest { @Test public void serializableLambdaHasWriteReplaceMethod() throws NoSuchMethodException, InvocationTargetException, diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/reflect/ArrayTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/reflect/ArrayTest.java index 00313747f..9e47488bd 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/reflect/ArrayTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/reflect/ArrayTest.java @@ -43,7 +43,7 @@ public class ArrayTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void setWorks() { Object array = Array.newInstance(String.class, 2); Array.set(array, 0, "foo"); @@ -52,7 +52,7 @@ public class ArrayTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void setPrimitiveWorks() { Object array = Array.newInstance(int.class, 2); Array.set(array, 0, 23); diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/reflect/ConstructorTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/reflect/ConstructorTest.java index 332b6ac15..a86ac4262 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/reflect/ConstructorTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/reflect/ConstructorTest.java @@ -32,7 +32,7 @@ import org.teavm.junit.TestPlatform; @RunWith(TeaVMTestRunner.class) @EachTestCompiledSeparately -@SkipPlatform(TestPlatform.C) +@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public class ConstructorTest { @Test public void constructorsEnumerated() { diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/reflect/FieldTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/reflect/FieldTest.java index e162698fd..023179364 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/reflect/FieldTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/reflect/FieldTest.java @@ -29,7 +29,7 @@ import org.teavm.junit.TestPlatform; @RunWith(TeaVMTestRunner.class) @EachTestCompiledSeparately -@SkipPlatform(TestPlatform.C) +@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public class FieldTest { @Test public void fieldsEnumerated() { diff --git a/tests/src/test/java/org/teavm/classlib/java/lang/reflect/MethodTest.java b/tests/src/test/java/org/teavm/classlib/java/lang/reflect/MethodTest.java index d8d15414b..9f9e2ba7d 100644 --- a/tests/src/test/java/org/teavm/classlib/java/lang/reflect/MethodTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/lang/reflect/MethodTest.java @@ -31,7 +31,7 @@ import org.teavm.junit.TestPlatform; @RunWith(TeaVMTestRunner.class) @EachTestCompiledSeparately -@SkipPlatform(TestPlatform.C) +@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public class MethodTest { @Test public void methodsEnumerated() { diff --git a/tests/src/test/java/org/teavm/classlib/java/net/URLTest.java b/tests/src/test/java/org/teavm/classlib/java/net/URLTest.java index b97ddcfd8..f4b13ed98 100644 --- a/tests/src/test/java/org/teavm/classlib/java/net/URLTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/net/URLTest.java @@ -534,8 +534,7 @@ public class URLTest { u.toString()); assertEquals("b) Does not return the right url string", "http://www.yahoo2.com:9999", u1.toString()); - assertTrue("c) Does not return the right url string", u - .equals(new URL(u.toString()))); + assertEquals("c) Does not return the right url string", u, new URL(u.toString())); } catch (Exception e) { // Do nothing } diff --git a/tests/src/test/java/org/teavm/classlib/java/time/TestDateTimesImplementation.java b/tests/src/test/java/org/teavm/classlib/java/time/TestDateTimesImplementation.java index 6535a684d..ad5611008 100644 --- a/tests/src/test/java/org/teavm/classlib/java/time/TestDateTimesImplementation.java +++ b/tests/src/test/java/org/teavm/classlib/java/time/TestDateTimesImplementation.java @@ -310,7 +310,7 @@ public class TestDateTimesImplementation { } @Test(dataProvider = "safeMultiplyLongLongProviderOverflow", expectedExceptions = ArithmeticException.class) - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void test_safeMultiplyLongLong_overflow(long a, long b) { Jdk8Methods.safeMultiply(a, b); } diff --git a/tests/src/test/java/org/teavm/classlib/java/time/TestLocalDate.java b/tests/src/test/java/org/teavm/classlib/java/time/TestLocalDate.java index d1e692015..105988673 100644 --- a/tests/src/test/java/org/teavm/classlib/java/time/TestLocalDate.java +++ b/tests/src/test/java/org/teavm/classlib/java/time/TestLocalDate.java @@ -944,7 +944,7 @@ public class TestLocalDate extends AbstractDateTimeTest { } @Test(expectedExceptions = NullPointerException.class) - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void test_plus_longPeriodUnit_null() { test2007x07x15.plus(1, (TemporalUnit) null); } @@ -1305,7 +1305,7 @@ public class TestLocalDate extends AbstractDateTimeTest { } @Test(expectedExceptions = NullPointerException.class) - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void test_minus_longPeriodUnit_null() { test2007x07x15.minus(1, (TemporalUnit) null); } diff --git a/tests/src/test/java/org/teavm/classlib/java/time/TestLocalDateTime.java b/tests/src/test/java/org/teavm/classlib/java/time/TestLocalDateTime.java index 117e8d1a0..0573ec416 100644 --- a/tests/src/test/java/org/teavm/classlib/java/time/TestLocalDateTime.java +++ b/tests/src/test/java/org/teavm/classlib/java/time/TestLocalDateTime.java @@ -1261,7 +1261,7 @@ public class TestLocalDateTime extends AbstractDateTimeTest { } @Test(expectedExceptions = NullPointerException.class) - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void test_plus_longPeriodUnit_null() { test2007x07x15x12x30x40x987654321.plus(1, null); } @@ -1972,7 +1972,7 @@ public class TestLocalDateTime extends AbstractDateTimeTest { } @Test(expectedExceptions = NullPointerException.class) - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void test_minus_longPeriodUnit_null() { test2007x07x15x12x30x40x987654321.minus(1, null); } diff --git a/tests/src/test/java/org/teavm/classlib/java/time/TestOffsetTime.java b/tests/src/test/java/org/teavm/classlib/java/time/TestOffsetTime.java index 3caf12cba..c8d975219 100644 --- a/tests/src/test/java/org/teavm/classlib/java/time/TestOffsetTime.java +++ b/tests/src/test/java/org/teavm/classlib/java/time/TestOffsetTime.java @@ -668,7 +668,7 @@ public class TestOffsetTime extends AbstractDateTimeTest { } @Test(expectedExceptions = NullPointerException.class) - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void test_with_TemporalField_null() { test11x30x59x500pone.with((TemporalField) null, 0); } diff --git a/tests/src/test/java/org/teavm/classlib/java/time/TestZonedDateTime.java b/tests/src/test/java/org/teavm/classlib/java/time/TestZonedDateTime.java index c65e58584..849468d7b 100644 --- a/tests/src/test/java/org/teavm/classlib/java/time/TestZonedDateTime.java +++ b/tests/src/test/java/org/teavm/classlib/java/time/TestZonedDateTime.java @@ -1456,7 +1456,7 @@ public class TestZonedDateTime extends AbstractDateTimeTest { } @Test(expectedExceptions = NullPointerException.class) - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void test_plus_longUnit_null() { testDateTimeParis.plus(0, null); } diff --git a/tests/src/test/java/org/teavm/classlib/java/time/temporal/TestYear.java b/tests/src/test/java/org/teavm/classlib/java/time/temporal/TestYear.java index 6811e2f5d..e12e06a34 100644 --- a/tests/src/test/java/org/teavm/classlib/java/time/temporal/TestYear.java +++ b/tests/src/test/java/org/teavm/classlib/java/time/temporal/TestYear.java @@ -710,7 +710,7 @@ public class TestYear extends AbstractDateTimeTest { } @Test(expectedExceptions = NullPointerException.class) - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void test_compareTo_nullYear() { Year doy = null; Year test = Year.of(1); diff --git a/tests/src/test/java/org/teavm/classlib/java/util/DateTest.java b/tests/src/test/java/org/teavm/classlib/java/util/DateTest.java index 85ae3ef28..48015fec6 100644 --- a/tests/src/test/java/org/teavm/classlib/java/util/DateTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/util/DateTest.java @@ -20,13 +20,16 @@ import java.util.Date; import org.junit.Test; import org.junit.runner.RunWith; import org.teavm.junit.EachTestCompiledSeparately; +import org.teavm.junit.SkipPlatform; import org.teavm.junit.TeaVMTestRunner; +import org.teavm.junit.TestPlatform; @RunWith(TeaVMTestRunner.class) @EachTestCompiledSeparately public class DateTest { @SuppressWarnings("deprecation") @Test + @SkipPlatform(TestPlatform.WEBASSEMBLY) public void setsDateAndMonth() { Date date = new Date(); date.setMonth(0); @@ -39,6 +42,7 @@ public class DateTest { @SuppressWarnings("deprecation") @Test + @SkipPlatform(TestPlatform.WEBASSEMBLY) public void setsUTC() { long epochTime = Date.UTC(2023, 1, 20, 10, 0, 0); Date date = new Date(epochTime); diff --git a/tests/src/test/java/org/teavm/classlib/java/util/FormatterTest.java b/tests/src/test/java/org/teavm/classlib/java/util/FormatterTest.java index a88dafba4..8bc6fefc1 100644 --- a/tests/src/test/java/org/teavm/classlib/java/util/FormatterTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/util/FormatterTest.java @@ -31,7 +31,9 @@ import java.util.MissingFormatWidthException; import java.util.UnknownFormatConversionException; import org.junit.Test; import org.junit.runner.RunWith; +import org.teavm.junit.SkipPlatform; import org.teavm.junit.TeaVMTestRunner; +import org.teavm.junit.TestPlatform; @RunWith(TeaVMTestRunner.class) public class FormatterTest { @@ -108,6 +110,7 @@ public class FormatterTest { } @Test + @SkipPlatform(TestPlatform.WEBASSEMBLY) public void formatsChar() { assertEquals("x: Y:\uDBFF\uDFFF ", new Formatter().format("%c:%3C:%-3c", 'x', 'y', 0x10ffff).toString()); diff --git a/tests/src/test/java/org/teavm/metaprogramming/test/MetaprogrammingTest.java b/tests/src/test/java/org/teavm/metaprogramming/test/MetaprogrammingTest.java index 766cf1f4f..e1014b825 100644 --- a/tests/src/test/java/org/teavm/metaprogramming/test/MetaprogrammingTest.java +++ b/tests/src/test/java/org/teavm/metaprogramming/test/MetaprogrammingTest.java @@ -30,7 +30,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.teavm.junit.EachTestCompiledSeparately; import org.teavm.junit.SkipJVM; +import org.teavm.junit.SkipPlatform; import org.teavm.junit.TeaVMTestRunner; +import org.teavm.junit.TestPlatform; import org.teavm.metaprogramming.CompileTime; import org.teavm.metaprogramming.Meta; import org.teavm.metaprogramming.ReflectClass; @@ -482,6 +484,7 @@ public class MetaprogrammingTest { private static int counter; @Test + @SkipPlatform(TestPlatform.WEBASSEMBLY) public void arrayTypeSelected() { assertEquals(String[].class, createInstance(String.class, 1).getClass()); assertEquals(String[][].class, createInstance(String[].class, 1).getClass()); diff --git a/tests/src/test/java/org/teavm/vm/VMTest.java b/tests/src/test/java/org/teavm/vm/VMTest.java index 392c49b3d..1412df1f0 100644 --- a/tests/src/test/java/org/teavm/vm/VMTest.java +++ b/tests/src/test/java/org/teavm/vm/VMTest.java @@ -108,14 +108,17 @@ public class VMTest { @Test public void catchesException() { + var wasCaught = false; try { throw new IllegalArgumentException(); } catch (IllegalArgumentException e) { - // do nothing + wasCaught = true; } + assertTrue(wasCaught); } @Test + @SkipPlatform(TestPlatform.WEBASSEMBLY) public void setsVariableBeforeTryCatch() { int a = 23; try { @@ -255,7 +258,7 @@ public class VMTest { @Test @SkipJVM - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void asyncClinit() { assertEquals(0, initCount); assertEquals("foo", AsyncClinitClass.foo()); @@ -267,13 +270,13 @@ public class VMTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void asyncClinitField() { assertEquals("ok", AsyncClinitClass.state); } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void asyncClinitInstance() { AsyncClinitClass acl = new AsyncClinitClass(); assertEquals("ok", AsyncClinitClass.state); @@ -281,7 +284,7 @@ public class VMTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void asyncWait() { AsyncClinitClass acl = new AsyncClinitClass(); acl.doWait(); @@ -290,7 +293,7 @@ public class VMTest { @Test @SkipJVM - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void loopAndExceptionPhi() { int[] a = createArray(); int s = 0; @@ -309,7 +312,7 @@ public class VMTest { @Test @SkipJVM - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void asyncTryCatch() { try { throwExceptionAsync(); @@ -321,7 +324,7 @@ public class VMTest { @Test @SkipJVM - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void asyncExceptionHandler() { try { throw new RuntimeException("OK"); @@ -527,7 +530,7 @@ public class VMTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void indirectDefaultMethod() { StringBuilder sb = new StringBuilder(); for (FirstPath o : new FirstPath[] { new PathJoint(), new FirstPathOptimizationPrevention() }) { @@ -537,7 +540,7 @@ public class VMTest { } @Test - @SkipPlatform(TestPlatform.C) + @SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY}) public void indirectDefaultMethodSubclass() { StringBuilder sb = new StringBuilder(); for (FirstPath o : new FirstPath[] { new PathJointSubclass(), new FirstPathOptimizationPrevention() }) { diff --git a/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java b/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java index 5b3bd2f3b..64200f0fe 100644 --- a/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java +++ b/tools/c-incremental/src/main/java/org/teavm/tooling/c/incremental/IncrementalCBuilder.java @@ -65,7 +65,6 @@ public class IncrementalCBuilder { private String[] classPath; private int minHeapSize = 4; private int maxHeapSize = 128; - private boolean longjmpSupported = true; private boolean lineNumbersGenerated; private String targetPath; private String externalTool; @@ -139,10 +138,6 @@ public class IncrementalCBuilder { this.mainFunctionName = mainFunctionName; } - public void setLongjmpSupported(boolean longjmpSupported) { - this.longjmpSupported = longjmpSupported; - } - public void addProgressHandler(ProgressHandler handler) { synchronized (progressHandlers) { progressHandlers.add(handler); @@ -342,7 +337,6 @@ public class IncrementalCBuilder { cTarget.setMinHeapSize(minHeapSize * 1024 * 1024); cTarget.setMaxHeapSize(maxHeapSize * 1024 * 1024); cTarget.setLineNumbersGenerated(lineNumbersGenerated); - cTarget.setLongjmpUsed(longjmpSupported); cTarget.setHeapDump(true); vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE); vm.setCacheStatus(classSource); diff --git a/tools/cli/src/main/java/org/teavm/cli/TeaVMCBuilderRunner.java b/tools/cli/src/main/java/org/teavm/cli/TeaVMCBuilderRunner.java index f25b0c25f..0f6b2b061 100644 --- a/tools/cli/src/main/java/org/teavm/cli/TeaVMCBuilderRunner.java +++ b/tools/cli/src/main/java/org/teavm/cli/TeaVMCBuilderRunner.java @@ -67,10 +67,6 @@ public class TeaVMCBuilderRunner { .hasArg() .desc("Minimum heap size in megabytes") .build()); - options.addOption(Option.builder() - .longOpt("no-longjmp") - .desc("Don't use setjmp/longjmp functions to emulate exception handling") - .build()); options.addOption(Option.builder("e") .longOpt("entry-point") .argName("name") @@ -126,9 +122,6 @@ public class TeaVMCBuilderRunner { if (commandLine.hasOption('e')) { builder.setMainFunctionName(commandLine.getOptionValue('e')); } - if (commandLine.hasOption("no-longjmp")) { - builder.setLongjmpSupported(false); - } String[] args = commandLine.getArgs(); if (args.length != 1) { diff --git a/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java b/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java index e493fd04b..e5ad32617 100644 --- a/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java +++ b/tools/cli/src/main/java/org/teavm/cli/TeaVMRunner.java @@ -151,10 +151,6 @@ public final class TeaVMRunner { .desc("Maximum number of names kept in top-level scope (" + "other will be put in a separate object. 10000 by default.") .build()); - options.addOption(Option.builder() - .longOpt("no-longjmp") - .desc("Don't use setjmp/longjmp functions to emulate exceptions (C target)") - .build()); } private TeaVMRunner(CommandLine commandLine) { @@ -335,9 +331,6 @@ public final class TeaVMRunner { } private void parseCOptions() { - if (commandLine.hasOption("no-longjmp")) { - tool.setLongjmpSupported(false); - } if (commandLine.hasOption("heap-dump")) { tool.setHeapDump(true); } diff --git a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java index f8268d4df..023486a2d 100644 --- a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -110,7 +110,6 @@ public class TeaVMTool { private int minHeapSize = 4 * (1 << 20); private int maxHeapSize = 128 * (1 << 20); private ReferenceCache referenceCache; - private boolean longjmpSupported = true; private boolean heapDump; private boolean shortFileNames; private boolean assertionsRemoved; @@ -259,10 +258,6 @@ public class TeaVMTool { this.wasmVersion = wasmVersion; } - public void setLongjmpSupported(boolean longjmpSupported) { - this.longjmpSupported = longjmpSupported; - } - public void setHeapDump(boolean heapDump) { this.heapDump = heapDump; } @@ -367,7 +362,6 @@ public class TeaVMTool { cTarget.setMinHeapSize(minHeapSize); cTarget.setMaxHeapSize(maxHeapSize); cTarget.setLineNumbersGenerated(debugInformationGenerated); - cTarget.setLongjmpUsed(longjmpSupported); cTarget.setHeapDump(heapDump); cTarget.setObfuscated(obfuscated); cTarget.setFileNames(shortFileNames diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java index 1f60f31ac..50a89d786 100644 --- a/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java @@ -78,8 +78,6 @@ public interface BuildStrategy { void setMaxHeapSize(int maxHeapSize); - void setLongjmpSupported(boolean value); - void setHeapDump(boolean heapDump); void setShortFileNames(boolean shortFileNames); diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java index 30c4fa2d6..e3a768138 100644 --- a/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java @@ -63,7 +63,6 @@ public class InProcessBuildStrategy implements BuildStrategy { private int minHeapSize = 4 * 1024 * 1204; private int maxHeapSize = 128 * 1024 * 1024; private final List sourceFileProviders = new ArrayList<>(); - private boolean longjmpSupported = true; private boolean heapDump; private TeaVMProgressListener progressListener; private Properties properties = new Properties(); @@ -208,11 +207,6 @@ public class InProcessBuildStrategy implements BuildStrategy { this.maxHeapSize = maxHeapSize; } - @Override - public void setLongjmpSupported(boolean longjmpSupported) { - this.longjmpSupported = longjmpSupported; - } - @Override public void setHeapDump(boolean heapDump) { this.heapDump = heapDump; @@ -257,7 +251,6 @@ public class InProcessBuildStrategy implements BuildStrategy { tool.setWasmVersion(wasmVersion); tool.setMinHeapSize(minHeapSize); tool.setMaxHeapSize(maxHeapSize); - tool.setLongjmpSupported(longjmpSupported); tool.setHeapDump(heapDump); tool.setShortFileNames(shortFileNames); tool.setAssertionsRemoved(assertionsRemoved); diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java index 7f9e870e4..f344b64a9 100644 --- a/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java @@ -51,7 +51,6 @@ public class RemoteBuildStrategy implements BuildStrategy { request = new RemoteBuildRequest(); request.optimizationLevel = TeaVMOptimizationLevel.ADVANCED; request.wasmVersion = WasmBinaryVersion.V_0x1; - request.longjmpSupported = true; } @Override @@ -185,11 +184,6 @@ public class RemoteBuildStrategy implements BuildStrategy { request.maxHeapSize = maxHeapSize; } - @Override - public void setLongjmpSupported(boolean value) { - request.longjmpSupported = value; - } - @Override public void setHeapDump(boolean heapDump) { request.heapDump = heapDump; diff --git a/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java b/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java index 418673c46..14e9c1cd8 100644 --- a/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java @@ -160,7 +160,6 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi tool.setWasmVersion(request.wasmVersion); tool.setMinHeapSize(request.minHeapSize); tool.setMaxHeapSize(request.maxHeapSize); - tool.setLongjmpSupported(request.longjmpSupported); tool.setHeapDump(request.heapDump); tool.setShortFileNames(request.shortFileNames); tool.setAssertionsRemoved(request.assertionsRemoved); diff --git a/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java index 2f41dc4ce..727dc1b9b 100644 --- a/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java @@ -48,7 +48,6 @@ public class RemoteBuildRequest implements Serializable { public WasmBinaryVersion wasmVersion; public int minHeapSize; public int maxHeapSize; - public boolean longjmpSupported; public boolean heapDump; public boolean shortFileNames; public boolean assertionsRemoved; diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java index c574baf62..f0018056e 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java @@ -151,9 +151,6 @@ public class TeaVMCompileMojo extends AbstractMojo { @Parameter(property = "teavm.processMemory", defaultValue = "512") private int processMemory; - @Parameter(property = "teavm.longjmpSupported", defaultValue = "true") - private boolean longjmpSupported; - @Parameter(property = "teavm.heapDump", defaultValue = "false") private boolean heapDump; @@ -298,7 +295,6 @@ public class TeaVMCompileMojo extends AbstractMojo { builder.setCacheDirectory(cacheDirectory.getAbsolutePath()); builder.setTargetType(targetType); builder.setWasmVersion(wasmVersion); - builder.setLongjmpSupported(longjmpSupported); builder.setHeapDump(heapDump); BuildResult result; result = builder.build();