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");
* 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")

View File

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

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

View File

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

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

View File

@ -30,8 +30,8 @@ public class CallSiteGenerator {
private GenerationContext context;
private CodeWriter writer;
private IncludeManager includes;
private ObjectIntMap<CallSiteLocation> locationMap = new ObjectIntHashMap<>();
private List<CallSiteLocation> locations = new ArrayList<>();
private ObjectIntMap<LocationList> locationMap = new ObjectIntHashMap<>();
private List<LocationList> locations = new ArrayList<>();
private List<HandlerContainer> exceptionHandlers = new ArrayList<>();
private ObjectIntMap<MethodLocation> methodLocationMap = new ObjectIntHashMap<>();
private List<MethodLocation> 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);
}
}
}

View File

@ -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<ExceptionHandlerDescriptor> reverseHandlers = new ArrayList<>(handlers);
Collections.reverse(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.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;

View File

@ -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<CallSiteLocation> locationCache = new ObjectIntHashMap<>();
ObjectIntMap<LocationList> locationCache = new ObjectIntHashMap<>();
ObjectIntMap<MethodLocation> 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);
}
}
}

View File

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

View File

@ -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<WasmLocal> variables = function.getLocalVariables().subList(function.getParameters().size(),
function.getLocalVariables().size());
List<WasmLocal> variables = function.getLocalVariables().subList(sz, function.getLocalVariables().size());
for (WasmLocal variable : variables) {
line(WasmCRenderingVisitor.mapType(variable.getType()) + " " + visitor.getVariableName(variable) + ";");
}

View File

@ -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<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.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<ExceptionHandlerDescriptor> 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<AnnotationValue> 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<? extends CallSiteLocation> location = CallSiteLocation.loadMany(
descriptorAnnot.getValue("location").getAnnotation());
List<ExceptionHandlerDescriptor> 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);
}

View File

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

View File

@ -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<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() {
return fileName;
}
@ -83,4 +131,22 @@ public class CallSiteLocation {
CallSiteDescriptor.loadNullableString(reader.getValue("methodName")),
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;
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<Instruction> pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation());
List<Instruction> post = getInstructionsAfterCallSite(initialBlock, block, next, callSite,

View File

@ -72,15 +72,17 @@ public class Inlining {
private Set<MethodReference> methodsUsedOnce = new HashSet<>();
private boolean devirtualization;
private ClassInference classInference;
private InliningFilterFactory filterFactory;
public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy,
ListableClassReaderSource classes, Predicate<MethodReference> 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<PlanEntry> 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

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 MethodLocation method;
public int lineNumber;
CallSiteLocation next;
}

View File

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

View File

@ -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<MethodReference> methodReferences = inlining.getOrder();
int classCount = classes.getClassNames().size();
int initialValue = compileProgressValue;

View File

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

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

View File

@ -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 {

View File

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

View File

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

View File

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