From 8038f90fd8ac7d6e4df0b3a520eb4369dad35bf7 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 15 Oct 2019 11:47:40 +0300 Subject: [PATCH] Support new format of debug information in C and WASM This allows to keep proper stack traces in FULL optimization mode, when aggressive inlining is applied --- .../console/Console.java} | 39 ++++++-- .../console/StderrOutputStream.java} | 25 ++--- .../impl/console/StdoutOutputStream.java | 31 ++++++ .../org/teavm/classlib/java/lang/TSystem.java | 7 +- .../teavm/classlib/java/lang/TThrowable.java | 13 ++- .../java/org/teavm/backend/c/CTarget.java | 7 ++ .../backend/c/generate/CallSiteGenerator.java | 63 ++++++++++-- .../c/generate/CodeGenerationVisitor.java | 12 +-- .../LowLevelInliningFilterFactory.java | 34 +++++++ .../org/teavm/backend/wasm/WasmTarget.java | 13 ++- .../generate/CallSiteBinaryGenerator.java | 98 +++++++++++++------ .../wasm/render/WasmBinaryRenderer.java | 3 +- .../backend/wasm/render/WasmCRenderer.java | 6 +- .../model/lowlevel/CallSiteDescriptor.java | 19 ++-- .../lowlevel/CallSiteDescriptorAnnot.java | 2 +- .../model/lowlevel/CallSiteLocation.java | 66 +++++++++++++ .../lowlevel/CallSiteLocationsAnnot.java | 20 ++++ ...ceptionHandlingShadowStackContributor.java | 10 +- .../teavm/model/optimization/Inlining.java | 8 +- .../model/optimization/InliningFilter.java | 24 +++++ .../optimization/InliningFilterFactory.java | 24 +++++ .../org/teavm/runtime/CallSiteLocation.java | 1 + .../org/teavm/runtime/ExceptionHandling.java | 93 +++++++++++------- core/src/main/java/org/teavm/vm/TeaVM.java | 6 +- .../main/java/org/teavm/vm/TeaVMTarget.java | 5 + .../main/resources/org/teavm/backend/c/file.h | 1 + .../resources/org/teavm/backend/c/stack.h | 1 + tools/junit/pom.xml | 5 + .../java/org/teavm/junit/TestEntryPoint.java | 4 +- .../org/teavm/junit/TestNativeEntryPoint.java | 12 ++- 30 files changed, 507 insertions(+), 145 deletions(-) rename classlib/src/main/java/org/teavm/classlib/{java/lang/TConsoleOutputStreamStderr.java => impl/console/Console.java} (59%) rename classlib/src/main/java/org/teavm/classlib/{java/lang/TConsoleOutputStreamStdout.java => impl/console/StderrOutputStream.java} (58%) create mode 100644 classlib/src/main/java/org/teavm/classlib/impl/console/StdoutOutputStream.java create mode 100644 core/src/main/java/org/teavm/backend/lowlevel/analyze/LowLevelInliningFilterFactory.java create mode 100644 core/src/main/java/org/teavm/model/lowlevel/CallSiteLocationsAnnot.java create mode 100644 core/src/main/java/org/teavm/model/optimization/InliningFilter.java create mode 100644 core/src/main/java/org/teavm/model/optimization/InliningFilterFactory.java diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TConsoleOutputStreamStderr.java b/classlib/src/main/java/org/teavm/classlib/impl/console/Console.java similarity index 59% rename from classlib/src/main/java/org/teavm/classlib/java/lang/TConsoleOutputStreamStderr.java rename to classlib/src/main/java/org/teavm/classlib/impl/console/Console.java index 0c3710b6f..217a017ea 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TConsoleOutputStreamStderr.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/console/Console.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 Alexey Andreev. + * 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. @@ -13,34 +13,53 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.classlib.java.lang; +package org.teavm.classlib.impl.console; -import java.io.IOException; import org.teavm.backend.c.intrinsic.RuntimeInclude; import org.teavm.classlib.PlatformDetector; -import org.teavm.classlib.java.io.TOutputStream; import org.teavm.interop.Import; import org.teavm.interop.Unmanaged; import org.teavm.jso.JSBody; -class TConsoleOutputStreamStderr extends TOutputStream { - @Override - public void write(int b) throws IOException { - writeImpl(b); +public final class Console { + private Console() { } - static void writeImpl(int b) { + public static void writeStderr(int b) { if (PlatformDetector.isC()) { writeC(b); + } else if (PlatformDetector.isWebAssembly()) { + writeWasm(b); } else { writeJs(b); } } + public static void writeStdout(int b) { + if (PlatformDetector.isC()) { + writeC(b); + } else if (PlatformDetector.isWebAssembly()) { + writeWasm(b); + } else { + writeJsStdout(b); + } + } + + public static void writeStdout(String s) { + for (int i = 0; i < s.length(); ++i) { + writeStderr(s.charAt(i)); + } + } + @JSBody(params = "b", script = "$rt_putStderr(b);") - @Import(name = "putwchar", module = "teavm") private static native void writeJs(int b); + @JSBody(params = "b", script = "$rt_putStdout(b);") + private static native void writeJsStdout(int b); + + @Import(name = "putwchar", module = "teavm") + private static native void writeWasm(int b); + @Unmanaged @Import(name = "teavm_logchar") @RuntimeInclude("log.h") diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TConsoleOutputStreamStdout.java b/classlib/src/main/java/org/teavm/classlib/impl/console/StderrOutputStream.java similarity index 58% rename from classlib/src/main/java/org/teavm/classlib/java/lang/TConsoleOutputStreamStdout.java rename to classlib/src/main/java/org/teavm/classlib/impl/console/StderrOutputStream.java index 5d2f37ed6..135d4f12a 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TConsoleOutputStreamStdout.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/console/StderrOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 Alexey Andreev. + * 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. @@ -13,24 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.classlib.java.lang; +package org.teavm.classlib.impl.console; import java.io.IOException; -import org.teavm.classlib.java.io.TOutputStream; -import org.teavm.interop.DelegateTo; -import org.teavm.jso.JSBody; +import java.io.OutputStream; -class TConsoleOutputStreamStdout extends TOutputStream { - @Override - @DelegateTo("writeLowLevel") - public void write(int b) throws IOException { - writeJs(b); +public class StderrOutputStream extends OutputStream { + public static final StderrOutputStream INSTANCE = new StderrOutputStream(); + + private StderrOutputStream() { } - @JSBody(params = "b", script = "$rt_putStdout(b);") - private static native void writeJs(int b); - - private void writeLowLevel(int b) { - TConsoleOutputStreamStderr.writeImpl(b); + @Override + public void write(int b) throws IOException { + Console.writeStderr(b); } } diff --git a/classlib/src/main/java/org/teavm/classlib/impl/console/StdoutOutputStream.java b/classlib/src/main/java/org/teavm/classlib/impl/console/StdoutOutputStream.java new file mode 100644 index 000000000..a831e3449 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/console/StdoutOutputStream.java @@ -0,0 +1,31 @@ +/* + * 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.classlib.impl.console; + +import java.io.IOException; +import java.io.OutputStream; + +public class StdoutOutputStream extends OutputStream { + public static final StdoutOutputStream INSTANCE = new StdoutOutputStream(); + + private StdoutOutputStream() { + } + + @Override + public void write(int b) throws IOException { + Console.writeStdout(b); + } +} 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 ccc86b15e..16421a7a6 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 @@ -23,8 +23,11 @@ import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.fs.VirtualFileSystemProvider; import org.teavm.classlib.fs.c.CFileSystem; import org.teavm.classlib.impl.c.Memory; +import org.teavm.classlib.impl.console.StderrOutputStream; +import org.teavm.classlib.impl.console.StdoutOutputStream; import org.teavm.classlib.java.io.TConsole; import org.teavm.classlib.java.io.TInputStream; +import org.teavm.classlib.java.io.TOutputStream; import org.teavm.classlib.java.io.TPrintStream; import org.teavm.classlib.java.lang.reflect.TArray; import org.teavm.interop.Address; @@ -49,14 +52,14 @@ public final class TSystem extends TObject { public static TPrintStream out() { if (outCache == null) { - outCache = new TPrintStream(new TConsoleOutputStreamStdout(), false); + outCache = new TPrintStream((TOutputStream) (Object) StdoutOutputStream.INSTANCE, false); } return outCache; } public static TPrintStream err() { if (errCache == null) { - errCache = new TPrintStream(new TConsoleOutputStreamStderr(), false); + errCache = new TPrintStream((TOutputStream) (Object) StderrOutputStream.INSTANCE, false); } return errCache; } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java index b862f40ee..7aa003028 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java @@ -22,6 +22,7 @@ import org.teavm.classlib.java.util.TArrays; import org.teavm.interop.Remove; import org.teavm.interop.Rename; import org.teavm.interop.Superclass; +import org.teavm.interop.Unmanaged; import org.teavm.runtime.ExceptionHandling; @Superclass("java.lang.Object") @@ -103,13 +104,19 @@ public class TThrowable extends RuntimeException { @Override public Throwable fillInStackTrace() { if (PlatformDetector.isLowLevel()) { - int stackSize = ExceptionHandling.callStackSize() - 1; - stackTrace = new TStackTraceElement[stackSize]; - ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace); + stackTrace = fillInStackTraceLowLevel(); } return this; } + @Unmanaged + private static TStackTraceElement[] fillInStackTraceLowLevel() { + int stackSize = ExceptionHandling.callStackSize(); + TStackTraceElement[] stackTrace = new TStackTraceElement[stackSize]; + ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace); + return stackTrace; + } + @Rename("getMessage") public String getMessage0() { return message; 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 882d3fb43..85833b771 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -69,6 +69,7 @@ import org.teavm.backend.c.intrinsic.RuntimeClassIntrinsic; import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic; import org.teavm.backend.c.intrinsic.StringsIntrinsic; import org.teavm.backend.c.intrinsic.StructureIntrinsic; +import org.teavm.backend.lowlevel.analyze.LowLevelInliningFilterFactory; import org.teavm.backend.lowlevel.dependency.ExceptionHandlingDependencyListener; import org.teavm.backend.lowlevel.dependency.WeakReferenceDependencyListener; import org.teavm.backend.lowlevel.generate.NameProvider; @@ -112,6 +113,7 @@ import org.teavm.model.lowlevel.ClassInitializerTransformer; import org.teavm.model.lowlevel.ExportDependencyListener; import org.teavm.model.lowlevel.LowLevelNullCheckFilter; import org.teavm.model.lowlevel.ShadowStackTransformer; +import org.teavm.model.optimization.InliningFilterFactory; import org.teavm.model.transformation.BoundCheckInsertion; import org.teavm.model.transformation.ClassPatch; import org.teavm.model.transformation.NullCheckInsertion; @@ -886,4 +888,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { public boolean isAsyncSupported() { return true; } + + @Override + public InliningFilterFactory getInliningFilter() { + return new LowLevelInliningFilterFactory(characteristics); + } } diff --git a/core/src/main/java/org/teavm/backend/c/generate/CallSiteGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/CallSiteGenerator.java index aa0c4be30..e8c64da3f 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/CallSiteGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/CallSiteGenerator.java @@ -30,8 +30,8 @@ public class CallSiteGenerator { private GenerationContext context; private CodeWriter writer; private IncludeManager includes; - private ObjectIntMap locationMap = new ObjectIntHashMap<>(); - private List locations = new ArrayList<>(); + private ObjectIntMap locationMap = new ObjectIntHashMap<>(); + private List locations = new ArrayList<>(); private List exceptionHandlers = new ArrayList<>(); private ObjectIntMap methodLocationMap = new ObjectIntHashMap<>(); private List methodLocations = new ArrayList<>(); @@ -79,12 +79,20 @@ public class CallSiteGenerator { first = false; int locationIndex = -1; - if (callSite.getLocation() != null) { - locationIndex = locationMap.getOrDefault(callSite.getLocation(), -1); - if (locationIndex < 0) { - locationIndex = locations.size(); - locationMap.put(callSite.getLocation(), locationIndex); - locations.add(callSite.getLocation()); + CallSiteLocation[] locations = callSite.getLocations(); + if (locations != null && locations.length > 0) { + LocationList prevList = null; + for (int i = locations.length - 1; i >= 0; --i) { + LocationList list = new LocationList(locations[i], prevList); + locationIndex = locationMap.getOrDefault(list, -1); + if (locationIndex < 0) { + locationIndex = this.locations.size(); + locationMap.put(list, locationIndex); + this.locations.add(list); + } else { + list = this.locations.get(locationIndex); + } + prevList = list; } } @@ -116,12 +124,14 @@ public class CallSiteGenerator { + "[" + locations.size() + "] = {").indent(); boolean first = true; - for (CallSiteLocation location : locations) { + for (LocationList locationList : locations) { if (!first) { writer.print(","); } first = false; + CallSiteLocation location = locationList.location; + writer.println().print("{ "); MethodLocation methodLocation = new MethodLocation(location.getFileName(), location.getClassName(), location.getMethodName()); @@ -135,6 +145,11 @@ public class CallSiteGenerator { .print(", "); writer.print(".lineNumber = ").print(String.valueOf(location.getLineNumber())); + if (locationList.next != null) { + int nextIndex = locationMap.get(locationList.next); + writer.print(", .next = callSiteLocations_" + callSitesName + " + " + nextIndex); + } + writer.print(" }"); } @@ -157,7 +172,8 @@ public class CallSiteGenerator { first = false; writer.println().print("{ "); - writer.print(".fileName = ").print(getStringExpr(location.file)).print(", "); + String fileName = location.file != null && !location.file.isEmpty() ? location.file : null; + writer.print(".fileName = ").print(getStringExpr(fileName)).print(", "); writer.print(".className = ").print(getStringExpr(location.className)).print(", "); writer.print(".methodName = ").print(getStringExpr(location.methodName)); @@ -244,4 +260,31 @@ public class CallSiteGenerator { return Objects.hash(file, className, methodName); } } + + final static class LocationList { + final CallSiteLocation location; + final LocationList next; + + LocationList(CallSiteLocation location, LocationList next) { + this.location = location; + this.next = next; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof LocationList)) { + return false; + } + LocationList that = (LocationList) o; + return location.equals(that.location) && Objects.equals(next, that.next); + } + + @Override + public int hashCode() { + return Objects.hash(location, next); + } + } } 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 ac77c3d20..74ce4d85f 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 @@ -530,16 +530,8 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor { private void withCallSite() { LocationStackEntry locationEntry = locationStack.peek(); TextLocation location = locationEntry != null ? locationEntry.location : null; - String fileName = location != null ? location.getFileName() : null; - if (fileName != null) { - fileName = fileName.substring(fileName.lastIndexOf('/') + 1); - } - CallSiteLocation callSiteLocation = new CallSiteLocation( - fileName, - callingMethod.getClassName(), - callingMethod.getName(), - location != null ? location.getLine() : 0); - CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), callSiteLocation); + CallSiteLocation[] callSiteLocations = CallSiteLocation.fromTextLocation(location, callingMethod); + CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), callSiteLocations); List reverseHandlers = new ArrayList<>(handlers); Collections.reverse(reverseHandlers); callSite.getHandlers().addAll(reverseHandlers); diff --git a/core/src/main/java/org/teavm/backend/lowlevel/analyze/LowLevelInliningFilterFactory.java b/core/src/main/java/org/teavm/backend/lowlevel/analyze/LowLevelInliningFilterFactory.java new file mode 100644 index 000000000..3fbf170b4 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/lowlevel/analyze/LowLevelInliningFilterFactory.java @@ -0,0 +1,34 @@ +/* + * 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.backend.lowlevel.analyze; + +import org.teavm.model.MethodReference; +import org.teavm.model.lowlevel.Characteristics; +import org.teavm.model.optimization.InliningFilter; +import org.teavm.model.optimization.InliningFilterFactory; + +public class LowLevelInliningFilterFactory implements InliningFilterFactory { + private Characteristics characteristics; + + public LowLevelInliningFilterFactory(Characteristics characteristics) { + this.characteristics = characteristics; + } + + @Override + public InliningFilter createFilter(MethodReference methodReference) { + return characteristics.isManaged(methodReference) ? characteristics::isManaged : m -> false; + } +} 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 3645f846d..eb8c757a9 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Properties; import java.util.Set; import org.teavm.ast.decompilation.Decompiler; +import org.teavm.backend.lowlevel.analyze.LowLevelInliningFilterFactory; import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.lowlevel.generate.NameProviderWithSpecialNames; import org.teavm.backend.wasm.binary.BinaryWriter; @@ -137,6 +138,7 @@ import org.teavm.model.lowlevel.Characteristics; import org.teavm.model.lowlevel.ClassInitializerEliminator; import org.teavm.model.lowlevel.ClassInitializerTransformer; import org.teavm.model.lowlevel.ShadowStackTransformer; +import org.teavm.model.optimization.InliningFilterFactory; import org.teavm.model.transformation.ClassPatch; import org.teavm.runtime.Allocator; import org.teavm.runtime.ExceptionHandling; @@ -158,6 +160,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { new MethodReference(Object.class, "clone", Object.class))); private TeaVMTargetController controller; + private Characteristics characteristics; private boolean debugging; private boolean wastEmitted; private boolean cEmitted; @@ -173,11 +176,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { @Override public void setController(TeaVMTargetController controller) { this.controller = controller; - Characteristics managedMethodRepository = new Characteristics( - controller.getUnprocessedClassSource()); + characteristics = new Characteristics(controller.getUnprocessedClassSource()); classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerTransformer = new ClassInitializerTransformer(); - shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository, true); + shadowStackTransformer = new ShadowStackTransformer(characteristics, true); controller.addVirtualMethods(VIRTUAL_METHODS::contains); } @@ -855,6 +857,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { return false; } + @Override + public InliningFilterFactory getInliningFilter() { + return new LowLevelInliningFilterFactory(characteristics); + } + static class MethodGeneratorContextImpl implements WasmMethodGeneratorContext { private BinaryWriter binaryWriter; private WasmStringPool stringPool; 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 6c6006fb7..7ad1af475 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 @@ -37,6 +37,7 @@ public class CallSiteBinaryGenerator { private static final int EXCEPTION_HANDLER_NEXT = 2; private static final int LOCATION_METHOD = 0; private static final int LOCATION_LINE = 1; + private static final int LOCATION_NEXT = 2; private static final int METHOD_LOCATION_FILE = 0; private static final int METHOD_LOCATION_CLASS = 1; private static final int METHOD_LOCATION_METHOD = 2; @@ -50,7 +51,8 @@ public class CallSiteBinaryGenerator { DataPrimitives.ADDRESS); private DataStructure locationStructure = new DataStructure((byte) 0, DataPrimitives.ADDRESS, - DataPrimitives.INT); + DataPrimitives.INT, + DataPrimitives.ADDRESS); private DataStructure methodLocationStructure = new DataStructure((byte) 0, DataPrimitives.ADDRESS, DataPrimitives.ADDRESS, @@ -83,7 +85,7 @@ public class CallSiteBinaryGenerator { binaryCallSites.add(binaryCallSite); } - ObjectIntMap locationCache = new ObjectIntHashMap<>(); + ObjectIntMap locationCache = new ObjectIntHashMap<>(); ObjectIntMap methodLocationCache = new ObjectIntHashMap<>(); for (int callSiteId = 0; callSiteId < callSites.size(); ++callSiteId) { @@ -118,36 +120,46 @@ public class CallSiteBinaryGenerator { } } - CallSiteLocation location = callSite.getLocation(); - int locationAddress = locationCache.getOrDefault(location, -1); - if (locationAddress < 0) { - DataValue binaryLocation = locationStructure.createValue(); - locationAddress = writer.append(binaryLocation); - locationCache.put(location, locationAddress); - MethodLocation methodLocation = new MethodLocation(location.getFileName(), location.getClassName(), - location.getMethodName()); - int methodLocationAddress = methodLocationCache.getOrDefault(methodLocation, -1); - if (methodLocationAddress < 0) { - DataValue binaryMethodLocation = methodLocationStructure.createValue(); - methodLocationAddress = writer.append(binaryMethodLocation); - methodLocationCache.put(methodLocation, methodLocationAddress); - if (location.getFileName() != null) { - binaryMethodLocation.setAddress(METHOD_LOCATION_FILE, - getStringIndirectPointer(location.getFileName())); + CallSiteLocation[] locations = callSite.getLocations(); + LocationList prevList = null; + int locationAddress = 0; + int previousLocationAddress = 0; + for (int i = locations.length - 1; i >= 0; --i) { + LocationList list = new LocationList(locations[i], prevList); + locationAddress = locationCache.getOrDefault(list, 0); + if (locationAddress == 0) { + DataValue binaryLocation = locationStructure.createValue(); + locationAddress = writer.append(binaryLocation); + locationCache.put(list, locationAddress); + CallSiteLocation location = list.location; + MethodLocation methodLocation = new MethodLocation(location.getFileName(), location.getClassName(), + location.getMethodName()); + int methodLocationAddress = methodLocationCache.getOrDefault(methodLocation, -1); + if (methodLocationAddress < 0) { + DataValue binaryMethodLocation = methodLocationStructure.createValue(); + methodLocationAddress = writer.append(binaryMethodLocation); + methodLocationCache.put(methodLocation, methodLocationAddress); + if (location.getFileName() != null) { + binaryMethodLocation.setAddress(METHOD_LOCATION_FILE, + getStringIndirectPointer(location.getFileName())); + } + if (location.getClassName() != null) { + binaryMethodLocation.setAddress(METHOD_LOCATION_CLASS, + getStringIndirectPointer(location.getClassName())); + } + if (location.getMethodName() != null) { + binaryMethodLocation.setAddress(METHOD_LOCATION_METHOD, + getStringIndirectPointer(location.getMethodName())); + } } - if (location.getClassName() != null) { - binaryMethodLocation.setAddress(METHOD_LOCATION_CLASS, - getStringIndirectPointer(location.getClassName())); - } - if (location.getMethodName() != null) { - binaryMethodLocation.setAddress(METHOD_LOCATION_METHOD, - getStringIndirectPointer(location.getMethodName())); - } - } - binaryLocation.setAddress(LOCATION_METHOD, methodLocationAddress); - binaryLocation.setInt(LOCATION_LINE, location.getLineNumber()); + binaryLocation.setAddress(LOCATION_METHOD, methodLocationAddress); + binaryLocation.setInt(LOCATION_LINE, location.getLineNumber()); + binaryLocation.setAddress(LOCATION_NEXT, previousLocationAddress); + } + previousLocationAddress = locationAddress; } + binaryCallSite.setAddress(CALL_SITE_LOCATION, locationAddress); } @@ -194,4 +206,32 @@ public class CallSiteBinaryGenerator { return Objects.hash(file, className, methodName); } } + + + final static class LocationList { + final CallSiteLocation location; + final LocationList next; + + LocationList(CallSiteLocation location, LocationList next) { + this.location = location; + this.next = next; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof LocationList)) { + return false; + } + LocationList that = (LocationList) o; + return location.equals(that.location) && Objects.equals(next, that.next); + } + + @Override + public int hashCode() { + return Objects.hash(location, next); + } + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java index 8d4394573..159009b92 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java @@ -264,7 +264,8 @@ public class WasmBinaryRenderer { WasmBinaryWriter code = new WasmBinaryWriter(); List localVariables = function.getLocalVariables(); - localVariables = localVariables.subList(function.getParameters().size(), localVariables.size()); + int parameterCount = Math.min(function.getParameters().size(), localVariables.size()); + localVariables = localVariables.subList(parameterCount, localVariables.size()); if (localVariables.isEmpty()) { code.writeLEB(0); } else { diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java index 81af01943..151fe6ed7 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java @@ -172,7 +172,8 @@ public class WasmCRenderer { renderFunctionModifiers(declaration, function); declaration.append(WasmCRenderingVisitor.mapType(function.getResult())).append(' '); declaration.append(function.getName()).append('('); - for (int i = 0; i < function.getParameters().size(); ++i) { + int sz = Math.min(function.getParameters().size(), function.getLocalVariables().size()); + for (int i = 0; i < sz; ++i) { if (i > 0) { declaration.append(", "); } @@ -184,8 +185,7 @@ public class WasmCRenderer { line(declaration.toString()); indent(); - List variables = function.getLocalVariables().subList(function.getParameters().size(), - function.getLocalVariables().size()); + List variables = function.getLocalVariables().subList(sz, function.getLocalVariables().size()); for (WasmLocal variable : variables) { line(WasmCRenderingVisitor.mapType(variable.getType()) + " " + visitor.getVariableName(variable) + ";"); } 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 ca9c31a12..9d2bf168f 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptor.java +++ b/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptor.java @@ -16,6 +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; @@ -33,19 +34,19 @@ import org.teavm.model.Program; public class CallSiteDescriptor { private int id; private List handlers = new ArrayList<>(); - private CallSiteLocation location; + private CallSiteLocation[] locations; - public CallSiteDescriptor(int id, CallSiteLocation location) { + public CallSiteDescriptor(int id, CallSiteLocation[] locations) { this.id = id; - this.location = location; + this.locations = locations != null ? locations.clone() : null; } public int getId() { return id; } - public CallSiteLocation getLocation() { - return location; + public CallSiteLocation[] getLocations() { + return locations != null ? locations.clone() : null; } public List getHandlers() { @@ -57,7 +58,8 @@ public class CallSiteDescriptor { 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(descriptor.location.save())); + 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()); @@ -80,11 +82,12 @@ public class CallSiteDescriptor { for (AnnotationValue descriptorValue : descriptorsAnnot.getValue("value").getList()) { AnnotationReader descriptorAnnot = descriptorValue.getAnnotation(); int id = descriptorAnnot.getValue("id").getInt(); - CallSiteLocation location = CallSiteLocation.load(descriptorAnnot.getValue("location").getAnnotation()); + 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); + CallSiteDescriptor descriptor = new CallSiteDescriptor(id, location.toArray(new CallSiteLocation[0])); descriptor.getHandlers().addAll(handlers); descriptors.add(descriptor); } diff --git a/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorAnnot.java b/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorAnnot.java index d244dcb8c..04fdd152c 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorAnnot.java +++ b/core/src/main/java/org/teavm/model/lowlevel/CallSiteDescriptorAnnot.java @@ -20,5 +20,5 @@ package org.teavm.model.lowlevel; ExceptionHandlerDescriptorAnnot[] handlers(); - CallSiteLocationAnnot location(); + CallSiteLocationsAnnot location(); } 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 c3885d5c6..7f5b7ac81 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocation.java +++ b/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocation.java @@ -15,10 +15,15 @@ */ 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; public class CallSiteLocation { private String fileName; @@ -33,6 +38,49 @@ public class CallSiteLocation { this.lineNumber = lineNumber; } + public static CallSiteLocation[] fromTextLocation(TextLocation location, MethodReference callingMethod) { + if (location == null) { + return new CallSiteLocation[] { + new CallSiteLocation("", callingMethod.getClassName(), callingMethod.getName(), -1) + }; + } else if (location.getInlining() == null) { + return new CallSiteLocation[] { + new CallSiteLocation( + convertFileName(location.getFileName()), + callingMethod.getClassName(), + callingMethod.getName(), + location.getLine()) + }; + } else { + List result = new ArrayList<>(); + InliningInfo inlining = location.getInlining(); + result.add(new CallSiteLocation( + convertFileName(location != null ? location.getFileName() : null), + inlining.getMethod().getClassName(), + inlining.getMethod().getName(), + location != null ? location.getLine() : 0)); + while (inlining != null) { + MethodReference method = inlining.getParent() != null + ? inlining.getParent().getMethod() + : callingMethod; + result.add(new CallSiteLocation( + convertFileName(inlining.getFileName()), + method.getClassName(), + method.getName(), + inlining.getLine())); + inlining = inlining.getParent(); + } + return result.toArray(new CallSiteLocation[0]); + } + } + + private static String convertFileName(String fileName) { + if (fileName != null) { + fileName = fileName.substring(fileName.lastIndexOf('/') + 1); + } + return fileName; + } + public String getFileName() { return fileName; } @@ -83,4 +131,22 @@ public class CallSiteLocation { 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 new file mode 100644 index 000000000..c5b82695e --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/CallSiteLocationsAnnot.java @@ -0,0 +1,20 @@ +/* + * 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/ExceptionHandlingShadowStackContributor.java b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java index 0fdc0a1af..592d20372 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java +++ b/core/src/main/java/org/teavm/model/lowlevel/ExceptionHandlingShadowStackContributor.java @@ -242,14 +242,8 @@ public class ExceptionHandlingShadowStackContributor { } } - String fileName = insn.getLocation() != null ? insn.getLocation().getFileName() : null; - int lineNumber = insn.getLocation() != null ? insn.getLocation().getLine() : -1; - if (fileName != null) { - fileName = fileName.substring(fileName.lastIndexOf('/') + 1); - } - CallSiteLocation location = new CallSiteLocation(fileName, method.getClassName(), method.getName(), - lineNumber); - CallSiteDescriptor callSite = new CallSiteDescriptor(callSiteIdGen++, location); + CallSiteLocation[] locations = CallSiteLocation.fromTextLocation(insn.getLocation(), method); + CallSiteDescriptor callSite = new CallSiteDescriptor(callSiteIdGen++, locations); callSites.add(callSite); List pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation()); List post = getInstructionsAfterCallSite(initialBlock, block, next, callSite, diff --git a/core/src/main/java/org/teavm/model/optimization/Inlining.java b/core/src/main/java/org/teavm/model/optimization/Inlining.java index 556cca562..2e5aea3a4 100644 --- a/core/src/main/java/org/teavm/model/optimization/Inlining.java +++ b/core/src/main/java/org/teavm/model/optimization/Inlining.java @@ -72,15 +72,17 @@ public class Inlining { private Set methodsUsedOnce = new HashSet<>(); private boolean devirtualization; private ClassInference classInference; + private InliningFilterFactory filterFactory; public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy, ListableClassReaderSource classes, Predicate externalMethods, - boolean devirtualization) { + boolean devirtualization, InliningFilterFactory filterFactory) { this.hierarchy = hierarchy; this.classes = classes; this.dependencyInfo = dependencyInfo; this.strategy = strategy; this.devirtualization = devirtualization; + this.filterFactory = filterFactory; usageCounter = new MethodUsageCounter(externalMethods); for (String className : classes.getClassNames()) { @@ -340,6 +342,7 @@ public class Inlining { InliningInfo inliningInfo) { List plan = new ArrayList<>(); int originalDepth = depth; + InliningFilter filter = filterFactory.createFilter(method); ContextImpl context = new ContextImpl(); for (BasicBlock block : program.getBasicBlocks()) { @@ -368,6 +371,9 @@ public class Inlining { != method.getClassName().equals(Fiber.class.getName())) { continue; } + if (!filter.apply(invoke.getMethod())) { + continue; + } MethodReader invokedMethod = getMethod(invoke.getMethod()); if (invokedMethod == null || invokedMethod.getProgram() == null diff --git a/core/src/main/java/org/teavm/model/optimization/InliningFilter.java b/core/src/main/java/org/teavm/model/optimization/InliningFilter.java new file mode 100644 index 000000000..2b8cb13c0 --- /dev/null +++ b/core/src/main/java/org/teavm/model/optimization/InliningFilter.java @@ -0,0 +1,24 @@ +/* + * 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.optimization; + +import org.teavm.model.MethodReference; + +public interface InliningFilter { + boolean apply(MethodReference methodReference); + + InliningFilter DEFAULT = m -> true; +} diff --git a/core/src/main/java/org/teavm/model/optimization/InliningFilterFactory.java b/core/src/main/java/org/teavm/model/optimization/InliningFilterFactory.java new file mode 100644 index 000000000..b7de4fbdc --- /dev/null +++ b/core/src/main/java/org/teavm/model/optimization/InliningFilterFactory.java @@ -0,0 +1,24 @@ +/* + * 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.optimization; + +import org.teavm.model.MethodReference; + +public interface InliningFilterFactory { + InliningFilter createFilter(MethodReference methodReference); + + InliningFilterFactory DEFAULT = m -> InliningFilter.DEFAULT; +} diff --git a/core/src/main/java/org/teavm/runtime/CallSiteLocation.java b/core/src/main/java/org/teavm/runtime/CallSiteLocation.java index f70731ae2..e1797dc6d 100644 --- a/core/src/main/java/org/teavm/runtime/CallSiteLocation.java +++ b/core/src/main/java/org/teavm/runtime/CallSiteLocation.java @@ -24,4 +24,5 @@ import org.teavm.interop.c.Native; public class CallSiteLocation extends Structure { public MethodLocation method; public int lineNumber; + CallSiteLocation next; } diff --git a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java index ce419abe5..793144e17 100644 --- a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java +++ b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java @@ -44,23 +44,27 @@ public final class ExceptionHandling { int callSiteId = ShadowStack.getCallSiteId(stackFrame); CallSite callSite = findCallSiteById(callSiteId, stackFrame); CallSiteLocation location = callSite.location; - MethodLocation methodLocation = location != null ? location.method : null; + while (location != null) { + MethodLocation methodLocation = location != null ? location.method : null; - Console.printString(" at "); - if (methodLocation.className == null || methodLocation.methodName == null) { - Console.printString("(Unknown method)"); - } else { - Console.printString(methodLocation.className.value); - Console.printString("."); - Console.printString(methodLocation.methodName.value); + Console.printString(" at "); + if (methodLocation.className == null || methodLocation.methodName == null) { + Console.printString("(Unknown method)"); + } else { + Console.printString(methodLocation.className.value); + Console.printString("."); + Console.printString(methodLocation.methodName.value); + } + Console.printString("("); + if (methodLocation.fileName != null && location.lineNumber >= 0) { + Console.printString(methodLocation.fileName.value); + Console.printString(":"); + Console.printInt(location.lineNumber); + } + Console.printString(")\n"); + + location = location.next; } - Console.printString("("); - if (methodLocation.fileName != null && location.lineNumber >= 0) { - Console.printString(methodLocation.fileName.value); - Console.printString(":"); - Console.printInt(location.lineNumber); - } - Console.printString(")\n"); stackFrame = ShadowStack.getNextStackFrame(stackFrame); } } @@ -137,27 +141,50 @@ public final class ExceptionHandling { Address stackFrame = ShadowStack.getStackTop(); int size = 0; while (stackFrame != null) { - stackFrame = ShadowStack.getNextStackFrame(stackFrame); - size++; - } - return size + 1; - } - - public static void fillStackTrace(StackTraceElement[] target) { - Address stackFrame = ShadowStack.getStackTop(); - stackFrame = ShadowStack.getNextStackFrame(stackFrame); - int index = 0; - while (stackFrame != null && index < target.length) { int callSiteId = ShadowStack.getCallSiteId(stackFrame); CallSite callSite = findCallSiteById(callSiteId, stackFrame); CallSiteLocation location = callSite.location; - MethodLocation methodLocation = location != null ? location.method : null; - StackTraceElement element = createElement( - methodLocation != null && methodLocation.className != null ? methodLocation.className.value : "", - methodLocation != null && methodLocation.methodName != null ? methodLocation.methodName.value : "", - methodLocation != null && methodLocation.fileName != null ? methodLocation.fileName.value : null, - location != null ? location.lineNumber : -1); - target[index++] = element; + if (location == null) { + size++; + } else { + while (location != null) { + size++; + location = location.next; + } + } + + stackFrame = ShadowStack.getNextStackFrame(stackFrame); + } + return size; + } + + @Unmanaged + public static void fillStackTrace(StackTraceElement[] target) { + Address stackFrame = ShadowStack.getStackTop(); + int index = 0; + while (stackFrame != null) { + int callSiteId = ShadowStack.getCallSiteId(stackFrame); + CallSite callSite = findCallSiteById(callSiteId, stackFrame); + CallSiteLocation location = callSite.location; + if (location == null) { + target[index++] = createElement("", "", null, location.lineNumber); + } else { + while (location != null) { + MethodLocation methodLocation = location.method; + StackTraceElement element; + if (methodLocation != null) { + element = createElement( + methodLocation.className != null ? methodLocation.className.value : "", + methodLocation.methodName != null ? methodLocation.methodName.value : "", + methodLocation.fileName != null ? methodLocation.fileName.value : null, + location.lineNumber); + } else { + element = createElement("", "", null, location.lineNumber); + } + target[index++] = element; + location = location.next; + } + } stackFrame = ShadowStack.getNextStackFrame(stackFrame); } } diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index f119f6b65..caac2a797 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -462,12 +462,13 @@ public class TeaVM implements TeaVMHost, ServiceRepository { dependencyAnalyzer.cleanupTypes(); + target.setController(targetController); + inline(classSet); if (wasCancelled()) { return null; } - target.setController(targetController); target.analyzeBeforeOptimizations(new ListableClassReaderSourceAdapter( dependencyAnalyzer.getClassSource(), new LinkedHashSet<>(dependencyAnalyzer.getReachableClasses()))); @@ -627,7 +628,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository { } Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer, inliningStrategy, - classes, this::isExternal, optimizationLevel == TeaVMOptimizationLevel.FULL); + classes, this::isExternal, optimizationLevel == TeaVMOptimizationLevel.FULL, + target.getInliningFilter()); List methodReferences = inlining.getOrder(); int classCount = classes.getClassNames().size(); int initialValue = compileProgressValue; diff --git a/core/src/main/java/org/teavm/vm/TeaVMTarget.java b/core/src/main/java/org/teavm/vm/TeaVMTarget.java index 58e81e303..2ba7cc46c 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMTarget.java +++ b/core/src/main/java/org/teavm/vm/TeaVMTarget.java @@ -24,6 +24,7 @@ import org.teavm.model.ListableClassHolderSource; import org.teavm.model.ListableClassReaderSource; import org.teavm.model.MethodReader; import org.teavm.model.Program; +import org.teavm.model.optimization.InliningFilterFactory; import org.teavm.vm.spi.TeaVMHostExtension; public interface TeaVMTarget { @@ -51,4 +52,8 @@ public interface TeaVMTarget { String[] getPlatformTags(); boolean isAsyncSupported(); + + default InliningFilterFactory getInliningFilter() { + return InliningFilterFactory.DEFAULT; + } } diff --git a/core/src/main/resources/org/teavm/backend/c/file.h b/core/src/main/resources/org/teavm/backend/c/file.h index 589aaea3c..f13d2521c 100644 --- a/core/src/main/resources/org/teavm/backend/c/file.h +++ b/core/src/main/resources/org/teavm/backend/c/file.h @@ -17,6 +17,7 @@ extern int32_t teavm_file_delete(char16_t*, int32_t); extern int32_t teavm_file_rename(char16_t*, int32_t, char16_t*, int32_t); extern int64_t teavm_file_lastModified(char16_t*, int32_t); extern int32_t teavm_file_setLastModified(char16_t*, int32_t, int64_t); +extern int32_t teavm_file_setReadonly(char16_t*, int32_t, int32_t); extern int32_t teavm_file_length(char16_t*, int32_t); extern int64_t teavm_file_open(char16_t*, int32_t, int32_t); extern int32_t teavm_file_close(int64_t); diff --git a/core/src/main/resources/org/teavm/backend/c/stack.h b/core/src/main/resources/org/teavm/backend/c/stack.h index 3b9752ca5..fb9984335 100644 --- a/core/src/main/resources/org/teavm/backend/c/stack.h +++ b/core/src/main/resources/org/teavm/backend/c/stack.h @@ -21,6 +21,7 @@ typedef struct TeaVM_MethodLocation { typedef struct TeaVM_CallSiteLocation { TeaVM_MethodLocation* method; int32_t lineNumber; + struct TeaVM_CallSiteLocation* next; } TeaVM_CallSiteLocation; typedef struct TeaVM_ExceptionHandler { diff --git a/tools/junit/pom.xml b/tools/junit/pom.xml index 2dbf72875..163ed53a8 100644 --- a/tools/junit/pom.xml +++ b/tools/junit/pom.xml @@ -40,6 +40,11 @@ teavm-tooling ${project.version} + + org.teavm + teavm-classlib + ${project.version} + net.sourceforge.htmlunit htmlunit diff --git a/tools/junit/src/main/java/org/teavm/junit/TestEntryPoint.java b/tools/junit/src/main/java/org/teavm/junit/TestEntryPoint.java index bceb69248..296be8331 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TestEntryPoint.java +++ b/tools/junit/src/main/java/org/teavm/junit/TestEntryPoint.java @@ -21,7 +21,7 @@ final class TestEntryPoint { private TestEntryPoint() { } - public static void run() { + public static void run() throws Exception { before(); try { launchTest(); @@ -36,7 +36,7 @@ final class TestEntryPoint { private static native void before(); - private static native void launchTest(); + private static native void launchTest() throws Exception; private static native void after(); diff --git a/tools/junit/src/main/java/org/teavm/junit/TestNativeEntryPoint.java b/tools/junit/src/main/java/org/teavm/junit/TestNativeEntryPoint.java index 9b6138718..79c361748 100644 --- a/tools/junit/src/main/java/org/teavm/junit/TestNativeEntryPoint.java +++ b/tools/junit/src/main/java/org/teavm/junit/TestNativeEntryPoint.java @@ -15,17 +15,21 @@ */ package org.teavm.junit; +import java.io.PrintStream; +import org.teavm.classlib.impl.console.StdoutOutputStream; + final class TestNativeEntryPoint { private TestNativeEntryPoint() { } - public static void main(String[] args) throws Exception { + public static void main(String[] args) { try { TestEntryPoint.run(); - System.out.println("SUCCESS"); + new PrintStream(StdoutOutputStream.INSTANCE).println("SUCCESS"); } catch (Throwable e) { - e.printStackTrace(System.out); - System.out.println("FAILURE"); + PrintStream out = new PrintStream(StdoutOutputStream.INSTANCE); + e.printStackTrace(out); + out.println("FAILURE"); } } }