mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
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:
parent
e46f204b4e
commit
8038f90fd8
|
@ -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")
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) + ";");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,5 @@ package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
ExceptionHandlerDescriptorAnnot[] handlers();
|
ExceptionHandlerDescriptorAnnot[] handlers();
|
||||||
|
|
||||||
CallSiteLocationAnnot location();
|
CallSiteLocationsAnnot location();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user