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
This commit is contained in:
Alexey Andreev 2019-10-15 11:47:40 +03:00
parent e46f204b4e
commit 8038f90fd8
30 changed files with 507 additions and 145 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013 Alexey Andreev. * Copyright 2019 Alexey Andreev.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.backend.c.intrinsic.RuntimeInclude;
import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.java.io.TOutputStream;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
class TConsoleOutputStreamStderr extends TOutputStream { public final class Console {
@Override private Console() {
public void write(int b) throws IOException {
writeImpl(b);
} }
static void writeImpl(int b) { public static void writeStderr(int b) {
if (PlatformDetector.isC()) { if (PlatformDetector.isC()) {
writeC(b); writeC(b);
} else if (PlatformDetector.isWebAssembly()) {
writeWasm(b);
} else { } else {
writeJs(b); 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);") @JSBody(params = "b", script = "$rt_putStderr(b);")
@Import(name = "putwchar", module = "teavm")
private static native void writeJs(int b); 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 @Unmanaged
@Import(name = "teavm_logchar") @Import(name = "teavm_logchar")
@RuntimeInclude("log.h") @RuntimeInclude("log.h")

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013 Alexey Andreev. * Copyright 2019 Alexey Andreev.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.impl.console;
import java.io.IOException; import java.io.IOException;
import org.teavm.classlib.java.io.TOutputStream; import java.io.OutputStream;
import org.teavm.interop.DelegateTo;
import org.teavm.jso.JSBody;
class TConsoleOutputStreamStdout extends TOutputStream { public class StderrOutputStream extends OutputStream {
@Override public static final StderrOutputStream INSTANCE = new StderrOutputStream();
@DelegateTo("writeLowLevel")
public void write(int b) throws IOException { private StderrOutputStream() {
writeJs(b);
} }
@JSBody(params = "b", script = "$rt_putStdout(b);") @Override
private static native void writeJs(int b); public void write(int b) throws IOException {
Console.writeStderr(b);
private void writeLowLevel(int b) {
TConsoleOutputStreamStderr.writeImpl(b);
} }
} }

View File

@ -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);
}
}

View File

@ -23,8 +23,11 @@ import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.fs.VirtualFileSystemProvider; import org.teavm.classlib.fs.VirtualFileSystemProvider;
import org.teavm.classlib.fs.c.CFileSystem; import org.teavm.classlib.fs.c.CFileSystem;
import org.teavm.classlib.impl.c.Memory; 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.TConsole;
import org.teavm.classlib.java.io.TInputStream; 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.io.TPrintStream;
import org.teavm.classlib.java.lang.reflect.TArray; import org.teavm.classlib.java.lang.reflect.TArray;
import org.teavm.interop.Address; import org.teavm.interop.Address;
@ -49,14 +52,14 @@ public final class TSystem extends TObject {
public static TPrintStream out() { public static TPrintStream out() {
if (outCache == null) { if (outCache == null) {
outCache = new TPrintStream(new TConsoleOutputStreamStdout(), false); outCache = new TPrintStream((TOutputStream) (Object) StdoutOutputStream.INSTANCE, false);
} }
return outCache; return outCache;
} }
public static TPrintStream err() { public static TPrintStream err() {
if (errCache == null) { if (errCache == null) {
errCache = new TPrintStream(new TConsoleOutputStreamStderr(), false); errCache = new TPrintStream((TOutputStream) (Object) StderrOutputStream.INSTANCE, false);
} }
return errCache; return errCache;
} }

View File

@ -22,6 +22,7 @@ import org.teavm.classlib.java.util.TArrays;
import org.teavm.interop.Remove; import org.teavm.interop.Remove;
import org.teavm.interop.Rename; import org.teavm.interop.Rename;
import org.teavm.interop.Superclass; import org.teavm.interop.Superclass;
import org.teavm.interop.Unmanaged;
import org.teavm.runtime.ExceptionHandling; import org.teavm.runtime.ExceptionHandling;
@Superclass("java.lang.Object") @Superclass("java.lang.Object")
@ -103,13 +104,19 @@ public class TThrowable extends RuntimeException {
@Override @Override
public Throwable fillInStackTrace() { public Throwable fillInStackTrace() {
if (PlatformDetector.isLowLevel()) { if (PlatformDetector.isLowLevel()) {
int stackSize = ExceptionHandling.callStackSize() - 1; stackTrace = fillInStackTraceLowLevel();
stackTrace = new TStackTraceElement[stackSize];
ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace);
} }
return this; 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") @Rename("getMessage")
public String getMessage0() { public String getMessage0() {
return message; return message;

View File

@ -69,6 +69,7 @@ import org.teavm.backend.c.intrinsic.RuntimeClassIntrinsic;
import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic; import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
import org.teavm.backend.c.intrinsic.StringsIntrinsic; import org.teavm.backend.c.intrinsic.StringsIntrinsic;
import org.teavm.backend.c.intrinsic.StructureIntrinsic; 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.ExceptionHandlingDependencyListener;
import org.teavm.backend.lowlevel.dependency.WeakReferenceDependencyListener; import org.teavm.backend.lowlevel.dependency.WeakReferenceDependencyListener;
import org.teavm.backend.lowlevel.generate.NameProvider; 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.ExportDependencyListener;
import org.teavm.model.lowlevel.LowLevelNullCheckFilter; import org.teavm.model.lowlevel.LowLevelNullCheckFilter;
import org.teavm.model.lowlevel.ShadowStackTransformer; import org.teavm.model.lowlevel.ShadowStackTransformer;
import org.teavm.model.optimization.InliningFilterFactory;
import org.teavm.model.transformation.BoundCheckInsertion; import org.teavm.model.transformation.BoundCheckInsertion;
import org.teavm.model.transformation.ClassPatch; import org.teavm.model.transformation.ClassPatch;
import org.teavm.model.transformation.NullCheckInsertion; import org.teavm.model.transformation.NullCheckInsertion;
@ -886,4 +888,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
public boolean isAsyncSupported() { public boolean isAsyncSupported() {
return true; return true;
} }
@Override
public InliningFilterFactory getInliningFilter() {
return new LowLevelInliningFilterFactory(characteristics);
}
} }

View File

@ -30,8 +30,8 @@ public class CallSiteGenerator {
private GenerationContext context; private GenerationContext context;
private CodeWriter writer; private CodeWriter writer;
private IncludeManager includes; private IncludeManager includes;
private ObjectIntMap<CallSiteLocation> locationMap = new ObjectIntHashMap<>(); private ObjectIntMap<LocationList> locationMap = new ObjectIntHashMap<>();
private List<CallSiteLocation> locations = new ArrayList<>(); private List<LocationList> locations = new ArrayList<>();
private List<HandlerContainer> exceptionHandlers = new ArrayList<>(); private List<HandlerContainer> exceptionHandlers = new ArrayList<>();
private ObjectIntMap<MethodLocation> methodLocationMap = new ObjectIntHashMap<>(); private ObjectIntMap<MethodLocation> methodLocationMap = new ObjectIntHashMap<>();
private List<MethodLocation> methodLocations = new ArrayList<>(); private List<MethodLocation> methodLocations = new ArrayList<>();
@ -79,12 +79,20 @@ public class CallSiteGenerator {
first = false; first = false;
int locationIndex = -1; int locationIndex = -1;
if (callSite.getLocation() != null) { CallSiteLocation[] locations = callSite.getLocations();
locationIndex = locationMap.getOrDefault(callSite.getLocation(), -1); if (locations != null && locations.length > 0) {
if (locationIndex < 0) { LocationList prevList = null;
locationIndex = locations.size(); for (int i = locations.length - 1; i >= 0; --i) {
locationMap.put(callSite.getLocation(), locationIndex); LocationList list = new LocationList(locations[i], prevList);
locations.add(callSite.getLocation()); 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(); + "[" + locations.size() + "] = {").indent();
boolean first = true; boolean first = true;
for (CallSiteLocation location : locations) { for (LocationList locationList : locations) {
if (!first) { if (!first) {
writer.print(","); writer.print(",");
} }
first = false; first = false;
CallSiteLocation location = locationList.location;
writer.println().print("{ "); writer.println().print("{ ");
MethodLocation methodLocation = new MethodLocation(location.getFileName(), location.getClassName(), MethodLocation methodLocation = new MethodLocation(location.getFileName(), location.getClassName(),
location.getMethodName()); location.getMethodName());
@ -135,6 +145,11 @@ public class CallSiteGenerator {
.print(", "); .print(", ");
writer.print(".lineNumber = ").print(String.valueOf(location.getLineNumber())); 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(" }"); writer.print(" }");
} }
@ -157,7 +172,8 @@ public class CallSiteGenerator {
first = false; first = false;
writer.println().print("{ "); 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(".className = ").print(getStringExpr(location.className)).print(", ");
writer.print(".methodName = ").print(getStringExpr(location.methodName)); writer.print(".methodName = ").print(getStringExpr(location.methodName));
@ -244,4 +260,31 @@ public class CallSiteGenerator {
return Objects.hash(file, className, methodName); 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);
}
}
} }

View File

@ -530,16 +530,8 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
private void withCallSite() { private void withCallSite() {
LocationStackEntry locationEntry = locationStack.peek(); LocationStackEntry locationEntry = locationStack.peek();
TextLocation location = locationEntry != null ? locationEntry.location : null; TextLocation location = locationEntry != null ? locationEntry.location : null;
String fileName = location != null ? location.getFileName() : null; CallSiteLocation[] callSiteLocations = CallSiteLocation.fromTextLocation(location, callingMethod);
if (fileName != null) { CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), callSiteLocations);
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);
List<ExceptionHandlerDescriptor> reverseHandlers = new ArrayList<>(handlers); List<ExceptionHandlerDescriptor> reverseHandlers = new ArrayList<>(handlers);
Collections.reverse(reverseHandlers); Collections.reverse(reverseHandlers);
callSite.getHandlers().addAll(reverseHandlers); callSite.getHandlers().addAll(reverseHandlers);

View File

@ -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;
}
}

View File

@ -31,6 +31,7 @@ import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import org.teavm.ast.decompilation.Decompiler; 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.NameProvider;
import org.teavm.backend.lowlevel.generate.NameProviderWithSpecialNames; import org.teavm.backend.lowlevel.generate.NameProviderWithSpecialNames;
import org.teavm.backend.wasm.binary.BinaryWriter; 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.ClassInitializerEliminator;
import org.teavm.model.lowlevel.ClassInitializerTransformer; import org.teavm.model.lowlevel.ClassInitializerTransformer;
import org.teavm.model.lowlevel.ShadowStackTransformer; import org.teavm.model.lowlevel.ShadowStackTransformer;
import org.teavm.model.optimization.InliningFilterFactory;
import org.teavm.model.transformation.ClassPatch; import org.teavm.model.transformation.ClassPatch;
import org.teavm.runtime.Allocator; import org.teavm.runtime.Allocator;
import org.teavm.runtime.ExceptionHandling; import org.teavm.runtime.ExceptionHandling;
@ -158,6 +160,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
new MethodReference(Object.class, "clone", Object.class))); new MethodReference(Object.class, "clone", Object.class)));
private TeaVMTargetController controller; private TeaVMTargetController controller;
private Characteristics characteristics;
private boolean debugging; private boolean debugging;
private boolean wastEmitted; private boolean wastEmitted;
private boolean cEmitted; private boolean cEmitted;
@ -173,11 +176,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
@Override @Override
public void setController(TeaVMTargetController controller) { public void setController(TeaVMTargetController controller) {
this.controller = controller; this.controller = controller;
Characteristics managedMethodRepository = new Characteristics( characteristics = new Characteristics(controller.getUnprocessedClassSource());
controller.getUnprocessedClassSource());
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource()); classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
classInitializerTransformer = new ClassInitializerTransformer(); classInitializerTransformer = new ClassInitializerTransformer();
shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository, true); shadowStackTransformer = new ShadowStackTransformer(characteristics, true);
controller.addVirtualMethods(VIRTUAL_METHODS::contains); controller.addVirtualMethods(VIRTUAL_METHODS::contains);
} }
@ -855,6 +857,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
return false; return false;
} }
@Override
public InliningFilterFactory getInliningFilter() {
return new LowLevelInliningFilterFactory(characteristics);
}
static class MethodGeneratorContextImpl implements WasmMethodGeneratorContext { static class MethodGeneratorContextImpl implements WasmMethodGeneratorContext {
private BinaryWriter binaryWriter; private BinaryWriter binaryWriter;
private WasmStringPool stringPool; private WasmStringPool stringPool;

View File

@ -37,6 +37,7 @@ public class CallSiteBinaryGenerator {
private static final int EXCEPTION_HANDLER_NEXT = 2; private static final int EXCEPTION_HANDLER_NEXT = 2;
private static final int LOCATION_METHOD = 0; private static final int LOCATION_METHOD = 0;
private static final int LOCATION_LINE = 1; 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_FILE = 0;
private static final int METHOD_LOCATION_CLASS = 1; private static final int METHOD_LOCATION_CLASS = 1;
private static final int METHOD_LOCATION_METHOD = 2; private static final int METHOD_LOCATION_METHOD = 2;
@ -50,7 +51,8 @@ public class CallSiteBinaryGenerator {
DataPrimitives.ADDRESS); DataPrimitives.ADDRESS);
private DataStructure locationStructure = new DataStructure((byte) 0, private DataStructure locationStructure = new DataStructure((byte) 0,
DataPrimitives.ADDRESS, DataPrimitives.ADDRESS,
DataPrimitives.INT); DataPrimitives.INT,
DataPrimitives.ADDRESS);
private DataStructure methodLocationStructure = new DataStructure((byte) 0, private DataStructure methodLocationStructure = new DataStructure((byte) 0,
DataPrimitives.ADDRESS, DataPrimitives.ADDRESS,
DataPrimitives.ADDRESS, DataPrimitives.ADDRESS,
@ -83,7 +85,7 @@ public class CallSiteBinaryGenerator {
binaryCallSites.add(binaryCallSite); binaryCallSites.add(binaryCallSite);
} }
ObjectIntMap<CallSiteLocation> locationCache = new ObjectIntHashMap<>(); ObjectIntMap<LocationList> locationCache = new ObjectIntHashMap<>();
ObjectIntMap<MethodLocation> methodLocationCache = new ObjectIntHashMap<>(); ObjectIntMap<MethodLocation> methodLocationCache = new ObjectIntHashMap<>();
for (int callSiteId = 0; callSiteId < callSites.size(); ++callSiteId) { for (int callSiteId = 0; callSiteId < callSites.size(); ++callSiteId) {
@ -118,36 +120,46 @@ public class CallSiteBinaryGenerator {
} }
} }
CallSiteLocation location = callSite.getLocation(); CallSiteLocation[] locations = callSite.getLocations();
int locationAddress = locationCache.getOrDefault(location, -1); LocationList prevList = null;
if (locationAddress < 0) { int locationAddress = 0;
DataValue binaryLocation = locationStructure.createValue(); int previousLocationAddress = 0;
locationAddress = writer.append(binaryLocation); for (int i = locations.length - 1; i >= 0; --i) {
locationCache.put(location, locationAddress); LocationList list = new LocationList(locations[i], prevList);
MethodLocation methodLocation = new MethodLocation(location.getFileName(), location.getClassName(), locationAddress = locationCache.getOrDefault(list, 0);
location.getMethodName()); if (locationAddress == 0) {
int methodLocationAddress = methodLocationCache.getOrDefault(methodLocation, -1); DataValue binaryLocation = locationStructure.createValue();
if (methodLocationAddress < 0) { locationAddress = writer.append(binaryLocation);
DataValue binaryMethodLocation = methodLocationStructure.createValue(); locationCache.put(list, locationAddress);
methodLocationAddress = writer.append(binaryMethodLocation); CallSiteLocation location = list.location;
methodLocationCache.put(methodLocation, methodLocationAddress); MethodLocation methodLocation = new MethodLocation(location.getFileName(), location.getClassName(),
if (location.getFileName() != null) { location.getMethodName());
binaryMethodLocation.setAddress(METHOD_LOCATION_FILE, int methodLocationAddress = methodLocationCache.getOrDefault(methodLocation, -1);
getStringIndirectPointer(location.getFileName())); 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.setAddress(LOCATION_METHOD, methodLocationAddress);
binaryLocation.setInt(LOCATION_LINE, location.getLineNumber()); binaryLocation.setInt(LOCATION_LINE, location.getLineNumber());
binaryLocation.setAddress(LOCATION_NEXT, previousLocationAddress);
}
previousLocationAddress = locationAddress;
} }
binaryCallSite.setAddress(CALL_SITE_LOCATION, locationAddress); binaryCallSite.setAddress(CALL_SITE_LOCATION, locationAddress);
} }
@ -194,4 +206,32 @@ public class CallSiteBinaryGenerator {
return Objects.hash(file, className, methodName); 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);
}
}
} }

View File

@ -264,7 +264,8 @@ public class WasmBinaryRenderer {
WasmBinaryWriter code = new WasmBinaryWriter(); WasmBinaryWriter code = new WasmBinaryWriter();
List<WasmLocal> localVariables = function.getLocalVariables(); List<WasmLocal> 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()) { if (localVariables.isEmpty()) {
code.writeLEB(0); code.writeLEB(0);
} else { } else {

View File

@ -172,7 +172,8 @@ public class WasmCRenderer {
renderFunctionModifiers(declaration, function); renderFunctionModifiers(declaration, function);
declaration.append(WasmCRenderingVisitor.mapType(function.getResult())).append(' '); declaration.append(WasmCRenderingVisitor.mapType(function.getResult())).append(' ');
declaration.append(function.getName()).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) { if (i > 0) {
declaration.append(", "); declaration.append(", ");
} }
@ -184,8 +185,7 @@ public class WasmCRenderer {
line(declaration.toString()); line(declaration.toString());
indent(); indent();
List<WasmLocal> variables = function.getLocalVariables().subList(function.getParameters().size(), List<WasmLocal> variables = function.getLocalVariables().subList(sz, function.getLocalVariables().size());
function.getLocalVariables().size());
for (WasmLocal variable : variables) { for (WasmLocal variable : variables) {
line(WasmCRenderingVisitor.mapType(variable.getType()) + " " + visitor.getVariableName(variable) + ";"); line(WasmCRenderingVisitor.mapType(variable.getType()) + " " + visitor.getVariableName(variable) + ";");
} }

View File

@ -16,6 +16,7 @@
package org.teavm.model.lowlevel; package org.teavm.model.lowlevel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -33,19 +34,19 @@ import org.teavm.model.Program;
public class CallSiteDescriptor { public class CallSiteDescriptor {
private int id; private int id;
private List<ExceptionHandlerDescriptor> handlers = new ArrayList<>(); private List<ExceptionHandlerDescriptor> 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.id = id;
this.location = location; this.locations = locations != null ? locations.clone() : null;
} }
public int getId() { public int getId() {
return id; return id;
} }
public CallSiteLocation getLocation() { public CallSiteLocation[] getLocations() {
return location; return locations != null ? locations.clone() : null;
} }
public List<ExceptionHandlerDescriptor> getHandlers() { public List<ExceptionHandlerDescriptor> getHandlers() {
@ -57,7 +58,8 @@ public class CallSiteDescriptor {
for (CallSiteDescriptor descriptor : descriptors) { for (CallSiteDescriptor descriptor : descriptors) {
AnnotationHolder descriptorAnnot = new AnnotationHolder(CallSiteDescriptorAnnot.class.getName()); AnnotationHolder descriptorAnnot = new AnnotationHolder(CallSiteDescriptorAnnot.class.getName());
descriptorAnnot.getValues().put("id", new AnnotationValue(descriptor.id)); 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<AnnotationValue> handlersValue = descriptor.handlers.stream() List<AnnotationValue> handlersValue = descriptor.handlers.stream()
.map(h -> new AnnotationValue(h.save())) .map(h -> new AnnotationValue(h.save()))
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -80,11 +82,12 @@ public class CallSiteDescriptor {
for (AnnotationValue descriptorValue : descriptorsAnnot.getValue("value").getList()) { for (AnnotationValue descriptorValue : descriptorsAnnot.getValue("value").getList()) {
AnnotationReader descriptorAnnot = descriptorValue.getAnnotation(); AnnotationReader descriptorAnnot = descriptorValue.getAnnotation();
int id = descriptorAnnot.getValue("id").getInt(); int id = descriptorAnnot.getValue("id").getInt();
CallSiteLocation location = CallSiteLocation.load(descriptorAnnot.getValue("location").getAnnotation()); List<? extends CallSiteLocation> location = CallSiteLocation.loadMany(
descriptorAnnot.getValue("location").getAnnotation());
List<ExceptionHandlerDescriptor> handlers = descriptorAnnot.getValue("handlers").getList().stream() List<ExceptionHandlerDescriptor> handlers = descriptorAnnot.getValue("handlers").getList().stream()
.map(a -> ExceptionHandlerDescriptor.load(a.getAnnotation())) .map(a -> ExceptionHandlerDescriptor.load(a.getAnnotation()))
.collect(Collectors.toList()); .collect(Collectors.toList());
CallSiteDescriptor descriptor = new CallSiteDescriptor(id, location); CallSiteDescriptor descriptor = new CallSiteDescriptor(id, location.toArray(new CallSiteLocation[0]));
descriptor.getHandlers().addAll(handlers); descriptor.getHandlers().addAll(handlers);
descriptors.add(descriptor); descriptors.add(descriptor);
} }

View File

@ -20,5 +20,5 @@ package org.teavm.model.lowlevel;
ExceptionHandlerDescriptorAnnot[] handlers(); ExceptionHandlerDescriptorAnnot[] handlers();
CallSiteLocationAnnot location(); CallSiteLocationsAnnot location();
} }

View File

@ -15,10 +15,15 @@
*/ */
package org.teavm.model.lowlevel; package org.teavm.model.lowlevel;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.teavm.model.AnnotationHolder; import org.teavm.model.AnnotationHolder;
import org.teavm.model.AnnotationReader; import org.teavm.model.AnnotationReader;
import org.teavm.model.AnnotationValue; import org.teavm.model.AnnotationValue;
import org.teavm.model.InliningInfo;
import org.teavm.model.MethodReference;
import org.teavm.model.TextLocation;
public class CallSiteLocation { public class CallSiteLocation {
private String fileName; private String fileName;
@ -33,6 +38,49 @@ public class CallSiteLocation {
this.lineNumber = lineNumber; 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<CallSiteLocation> 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() { public String getFileName() {
return fileName; return fileName;
} }
@ -83,4 +131,22 @@ public class CallSiteLocation {
CallSiteDescriptor.loadNullableString(reader.getValue("methodName")), CallSiteDescriptor.loadNullableString(reader.getValue("methodName")),
reader.getValue("lineNumber").getInt()); reader.getValue("lineNumber").getInt());
} }
public static AnnotationReader saveMany(List<? extends CallSiteLocation> locations) {
AnnotationHolder annotation = new AnnotationHolder(CallSiteLocationsAnnot.class.getName());
List<AnnotationValue> 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<? extends CallSiteLocation> loadMany(AnnotationReader reader) {
List<CallSiteLocation> result = new ArrayList<>();
for (AnnotationValue item : reader.getValue("value").getList()) {
result.add(load(item.getAnnotation()));
}
return result;
}
} }

View File

@ -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();
}

View File

@ -242,14 +242,8 @@ public class ExceptionHandlingShadowStackContributor {
} }
} }
String fileName = insn.getLocation() != null ? insn.getLocation().getFileName() : null; CallSiteLocation[] locations = CallSiteLocation.fromTextLocation(insn.getLocation(), method);
int lineNumber = insn.getLocation() != null ? insn.getLocation().getLine() : -1; CallSiteDescriptor callSite = new CallSiteDescriptor(callSiteIdGen++, locations);
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);
callSites.add(callSite); callSites.add(callSite);
List<Instruction> pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation()); List<Instruction> pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation());
List<Instruction> post = getInstructionsAfterCallSite(initialBlock, block, next, callSite, List<Instruction> post = getInstructionsAfterCallSite(initialBlock, block, next, callSite,

View File

@ -72,15 +72,17 @@ public class Inlining {
private Set<MethodReference> methodsUsedOnce = new HashSet<>(); private Set<MethodReference> methodsUsedOnce = new HashSet<>();
private boolean devirtualization; private boolean devirtualization;
private ClassInference classInference; private ClassInference classInference;
private InliningFilterFactory filterFactory;
public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy, public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy,
ListableClassReaderSource classes, Predicate<MethodReference> externalMethods, ListableClassReaderSource classes, Predicate<MethodReference> externalMethods,
boolean devirtualization) { boolean devirtualization, InliningFilterFactory filterFactory) {
this.hierarchy = hierarchy; this.hierarchy = hierarchy;
this.classes = classes; this.classes = classes;
this.dependencyInfo = dependencyInfo; this.dependencyInfo = dependencyInfo;
this.strategy = strategy; this.strategy = strategy;
this.devirtualization = devirtualization; this.devirtualization = devirtualization;
this.filterFactory = filterFactory;
usageCounter = new MethodUsageCounter(externalMethods); usageCounter = new MethodUsageCounter(externalMethods);
for (String className : classes.getClassNames()) { for (String className : classes.getClassNames()) {
@ -340,6 +342,7 @@ public class Inlining {
InliningInfo inliningInfo) { InliningInfo inliningInfo) {
List<PlanEntry> plan = new ArrayList<>(); List<PlanEntry> plan = new ArrayList<>();
int originalDepth = depth; int originalDepth = depth;
InliningFilter filter = filterFactory.createFilter(method);
ContextImpl context = new ContextImpl(); ContextImpl context = new ContextImpl();
for (BasicBlock block : program.getBasicBlocks()) { for (BasicBlock block : program.getBasicBlocks()) {
@ -368,6 +371,9 @@ public class Inlining {
!= method.getClassName().equals(Fiber.class.getName())) { != method.getClassName().equals(Fiber.class.getName())) {
continue; continue;
} }
if (!filter.apply(invoke.getMethod())) {
continue;
}
MethodReader invokedMethod = getMethod(invoke.getMethod()); MethodReader invokedMethod = getMethod(invoke.getMethod());
if (invokedMethod == null || invokedMethod.getProgram() == null if (invokedMethod == null || invokedMethod.getProgram() == null

View File

@ -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;
}

View File

@ -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;
}

View File

@ -24,4 +24,5 @@ import org.teavm.interop.c.Native;
public class CallSiteLocation extends Structure { public class CallSiteLocation extends Structure {
public MethodLocation method; public MethodLocation method;
public int lineNumber; public int lineNumber;
CallSiteLocation next;
} }

View File

@ -44,23 +44,27 @@ public final class ExceptionHandling {
int callSiteId = ShadowStack.getCallSiteId(stackFrame); int callSiteId = ShadowStack.getCallSiteId(stackFrame);
CallSite callSite = findCallSiteById(callSiteId, stackFrame); CallSite callSite = findCallSiteById(callSiteId, stackFrame);
CallSiteLocation location = callSite.location; CallSiteLocation location = callSite.location;
MethodLocation methodLocation = location != null ? location.method : null; while (location != null) {
MethodLocation methodLocation = location != null ? location.method : null;
Console.printString(" at "); Console.printString(" at ");
if (methodLocation.className == null || methodLocation.methodName == null) { if (methodLocation.className == null || methodLocation.methodName == null) {
Console.printString("(Unknown method)"); Console.printString("(Unknown method)");
} else { } else {
Console.printString(methodLocation.className.value); Console.printString(methodLocation.className.value);
Console.printString("."); Console.printString(".");
Console.printString(methodLocation.methodName.value); 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); stackFrame = ShadowStack.getNextStackFrame(stackFrame);
} }
} }
@ -137,27 +141,50 @@ public final class ExceptionHandling {
Address stackFrame = ShadowStack.getStackTop(); Address stackFrame = ShadowStack.getStackTop();
int size = 0; int size = 0;
while (stackFrame != null) { 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); int callSiteId = ShadowStack.getCallSiteId(stackFrame);
CallSite callSite = findCallSiteById(callSiteId, stackFrame); CallSite callSite = findCallSiteById(callSiteId, stackFrame);
CallSiteLocation location = callSite.location; CallSiteLocation location = callSite.location;
MethodLocation methodLocation = location != null ? location.method : null; if (location == null) {
StackTraceElement element = createElement( size++;
methodLocation != null && methodLocation.className != null ? methodLocation.className.value : "", } else {
methodLocation != null && methodLocation.methodName != null ? methodLocation.methodName.value : "", while (location != null) {
methodLocation != null && methodLocation.fileName != null ? methodLocation.fileName.value : null, size++;
location != null ? location.lineNumber : -1); location = location.next;
target[index++] = element; }
}
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); stackFrame = ShadowStack.getNextStackFrame(stackFrame);
} }
} }

View File

@ -462,12 +462,13 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
dependencyAnalyzer.cleanupTypes(); dependencyAnalyzer.cleanupTypes();
target.setController(targetController);
inline(classSet); inline(classSet);
if (wasCancelled()) { if (wasCancelled()) {
return null; return null;
} }
target.setController(targetController);
target.analyzeBeforeOptimizations(new ListableClassReaderSourceAdapter( target.analyzeBeforeOptimizations(new ListableClassReaderSourceAdapter(
dependencyAnalyzer.getClassSource(), dependencyAnalyzer.getClassSource(),
new LinkedHashSet<>(dependencyAnalyzer.getReachableClasses()))); new LinkedHashSet<>(dependencyAnalyzer.getReachableClasses())));
@ -627,7 +628,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer, inliningStrategy, Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer, inliningStrategy,
classes, this::isExternal, optimizationLevel == TeaVMOptimizationLevel.FULL); classes, this::isExternal, optimizationLevel == TeaVMOptimizationLevel.FULL,
target.getInliningFilter());
List<MethodReference> methodReferences = inlining.getOrder(); List<MethodReference> methodReferences = inlining.getOrder();
int classCount = classes.getClassNames().size(); int classCount = classes.getClassNames().size();
int initialValue = compileProgressValue; int initialValue = compileProgressValue;

View File

@ -24,6 +24,7 @@ import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodReader; import org.teavm.model.MethodReader;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.optimization.InliningFilterFactory;
import org.teavm.vm.spi.TeaVMHostExtension; import org.teavm.vm.spi.TeaVMHostExtension;
public interface TeaVMTarget { public interface TeaVMTarget {
@ -51,4 +52,8 @@ public interface TeaVMTarget {
String[] getPlatformTags(); String[] getPlatformTags();
boolean isAsyncSupported(); boolean isAsyncSupported();
default InliningFilterFactory getInliningFilter() {
return InliningFilterFactory.DEFAULT;
}
} }

View File

@ -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 int32_t teavm_file_rename(char16_t*, int32_t, char16_t*, int32_t);
extern int64_t teavm_file_lastModified(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_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 int32_t teavm_file_length(char16_t*, int32_t);
extern int64_t teavm_file_open(char16_t*, int32_t, int32_t); extern int64_t teavm_file_open(char16_t*, int32_t, int32_t);
extern int32_t teavm_file_close(int64_t); extern int32_t teavm_file_close(int64_t);

View File

@ -21,6 +21,7 @@ typedef struct TeaVM_MethodLocation {
typedef struct TeaVM_CallSiteLocation { typedef struct TeaVM_CallSiteLocation {
TeaVM_MethodLocation* method; TeaVM_MethodLocation* method;
int32_t lineNumber; int32_t lineNumber;
struct TeaVM_CallSiteLocation* next;
} TeaVM_CallSiteLocation; } TeaVM_CallSiteLocation;
typedef struct TeaVM_ExceptionHandler { typedef struct TeaVM_ExceptionHandler {

View File

@ -40,6 +40,11 @@
<artifactId>teavm-tooling</artifactId> <artifactId>teavm-tooling</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-classlib</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>net.sourceforge.htmlunit</groupId> <groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId> <artifactId>htmlunit</artifactId>

View File

@ -21,7 +21,7 @@ final class TestEntryPoint {
private TestEntryPoint() { private TestEntryPoint() {
} }
public static void run() { public static void run() throws Exception {
before(); before();
try { try {
launchTest(); launchTest();
@ -36,7 +36,7 @@ final class TestEntryPoint {
private static native void before(); private static native void before();
private static native void launchTest(); private static native void launchTest() throws Exception;
private static native void after(); private static native void after();

View File

@ -15,17 +15,21 @@
*/ */
package org.teavm.junit; package org.teavm.junit;
import java.io.PrintStream;
import org.teavm.classlib.impl.console.StdoutOutputStream;
final class TestNativeEntryPoint { final class TestNativeEntryPoint {
private TestNativeEntryPoint() { private TestNativeEntryPoint() {
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) {
try { try {
TestEntryPoint.run(); TestEntryPoint.run();
System.out.println("SUCCESS"); new PrintStream(StdoutOutputStream.INSTANCE).println("SUCCESS");
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(System.out); PrintStream out = new PrintStream(StdoutOutputStream.INSTANCE);
System.out.println("FAILURE"); e.printStackTrace(out);
out.println("FAILURE");
} }
} }
} }