mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-12-15 02:10:30 +08:00
Massive refactoring around Wasm support
1. Get rid of old exception handling IR transformer in favor of generation of EH code inside BE 2. Get rid of no-setjmp/longjmp support in C BE 3. Fix various bugs in WebAssembly BE 4. Suppress remaining failing tests for Wasm 5. Enable running Wasm tests by default
This commit is contained in:
parent
d24da7eded
commit
3d65d38375
@ -124,22 +124,13 @@ public class ServiceLoaderCSupport implements GeneratorFactory {
|
||||
.print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&")
|
||||
.print(names.forClassInstance(ValueType.parse(Object[].class))).print(", ")
|
||||
.println("services->size);");
|
||||
if (!context.usesLongjmp()) {
|
||||
writer.println("if (TEAVM_EXCEPTION_HANDLER != " + callSite.getId() + ") goto exit;");
|
||||
}
|
||||
|
||||
writer.println("TEAVM_GC_ROOT(0, result);");
|
||||
writer.println("void** arrayData = (void**) TEAVM_ARRAY_DATA(result, void*);");
|
||||
writer.println("for (int32_t i = 0; i < services->size; ++i) {").indent();
|
||||
writer.print("void* obj = ").print(names.forMethod(ALLOC_METHOD)).println("(services->entries[i].cls);");
|
||||
if (!context.usesLongjmp()) {
|
||||
writer.println("if (TEAVM_EXCEPTION_HANDLER != " + callSite.getId() + ") goto exit;");
|
||||
}
|
||||
writer.println("TEAVM_GC_ROOT(1, obj);");
|
||||
writer.println("services->entries[i].constructor(obj);");
|
||||
if (!context.usesLongjmp()) {
|
||||
writer.println("if (TEAVM_EXCEPTION_HANDLER != " + callSite.getId() + ") goto exit;");
|
||||
}
|
||||
writer.println("arrayData[i] = obj;");
|
||||
writer.outdent().println("}");
|
||||
|
||||
|
@ -134,7 +134,7 @@ public class TPrintStream extends TFilterOutputStream {
|
||||
|
||||
private void print(char[] s, int begin, int end) {
|
||||
TCharBuffer src = TCharBuffer.wrap(s, begin, end - begin);
|
||||
byte[] destBytes = new byte[TMath.max(16, TMath.min(s.length, 1024))];
|
||||
byte[] destBytes = new byte[TMath.max(16, TMath.min(end - begin, 1024))];
|
||||
TByteBuffer dest = TByteBuffer.wrap(destBytes);
|
||||
TCharsetEncoder encoder = charset.newEncoder()
|
||||
.onMalformedInput(TCodingErrorAction.REPLACE)
|
||||
|
@ -114,7 +114,6 @@ public class TClass<T> extends TObject implements TAnnotatedElement, TType {
|
||||
return Address.ofObject(this).<RuntimeClass>toStructure().isSupertypeOf.apply(other);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public String getName() {
|
||||
if (PlatformDetector.isLowLevel()) {
|
||||
String result = getNameCache(this);
|
||||
|
@ -273,6 +273,7 @@ public class TObject {
|
||||
|
||||
@DelegateTo("hashCodeLowLevelImpl")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native int hashCodeLowLevel(TObject obj);
|
||||
|
||||
@Unmanaged
|
||||
@ -282,6 +283,7 @@ public class TObject {
|
||||
|
||||
@DelegateTo("setHashCodeLowLevelImpl")
|
||||
@NoSideEffects
|
||||
@Unmanaged
|
||||
private static native void setHashCodeLowLevel(TObject obj, int value);
|
||||
|
||||
@Unmanaged
|
||||
|
@ -138,10 +138,11 @@ public final class TSystem extends TObject {
|
||||
GC.writeBarrier(dest);
|
||||
}
|
||||
|
||||
Address srcAddress = Address.align(src.toAddress().add(RuntimeArray.class, 1), itemSize);
|
||||
var offset = Address.align(Address.fromInt(0).add(RuntimeArray.class, 1), itemSize).toInt();
|
||||
Address srcAddress = src.toAddress().add(offset);
|
||||
srcAddress = srcAddress.add(itemSize * srcPos);
|
||||
|
||||
Address destAddress = Address.align(dest.toAddress().add(RuntimeArray.class, 1), itemSize);
|
||||
Address destAddress = dest.toAddress().add(offset);
|
||||
destAddress = destAddress.add(itemSize * destPos);
|
||||
|
||||
Allocator.moveMemoryBlock(srcAddress, destAddress, length * itemSize);
|
||||
|
@ -17,7 +17,10 @@ package org.teavm.classlib.java.util;
|
||||
|
||||
import org.teavm.classlib.impl.Base64Impl;
|
||||
|
||||
public class TBase64 {
|
||||
public final class TBase64 {
|
||||
private TBase64() {
|
||||
}
|
||||
|
||||
public static Encoder getEncoder() {
|
||||
return new Encoder(Base64Impl.alphabet, true);
|
||||
}
|
||||
@ -53,11 +56,11 @@ public class TBase64 {
|
||||
}
|
||||
}
|
||||
|
||||
public Decoder getDecoder() {
|
||||
public static Decoder getDecoder() {
|
||||
return new Decoder(Base64Impl.reverse);
|
||||
}
|
||||
|
||||
public Decoder getUrlDecoder() {
|
||||
public static Decoder getUrlDecoder() {
|
||||
return new Decoder(Base64Impl.urlReverse);
|
||||
}
|
||||
|
||||
|
@ -549,7 +549,7 @@ public final class TTemplateCollections {
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
if (key == null) {
|
||||
if (key == null || data.length == 0) {
|
||||
return false;
|
||||
}
|
||||
int suggestedIndex = Math.abs(key.hashCode()) % data.length;
|
||||
|
@ -617,7 +617,9 @@ class StatementGenerator implements InstructionVisitor {
|
||||
|
||||
@Override
|
||||
public void visit(NullCheckInstruction insn) {
|
||||
assign(Expr.unary(UnaryOperation.NULL_CHECK, null, Expr.var(insn.getValue().getIndex())), insn.getReceiver());
|
||||
var expr = Expr.unary(UnaryOperation.NULL_CHECK, null, Expr.var(insn.getValue().getIndex()));
|
||||
expr.setLocation(insn.getLocation());
|
||||
assign(expr, insn.getReceiver());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,7 +116,6 @@ import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.model.lowlevel.Characteristics;
|
||||
import org.teavm.model.lowlevel.CheckInstructionTransformation;
|
||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||
import org.teavm.model.lowlevel.ExportDependencyListener;
|
||||
@ -164,7 +163,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||
private WriteBarrierInsertion writeBarrierInsertion;
|
||||
private NullCheckInsertion nullCheckInsertion;
|
||||
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||
private CheckInstructionTransformation checkTransformation;
|
||||
private ExportDependencyListener exportDependencyListener = new ExportDependencyListener();
|
||||
private int minHeapSize = 4 * 1024 * 1024;
|
||||
private int maxHeapSize = 128 * 1024 * 1024;
|
||||
@ -177,7 +175,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||
private boolean incremental;
|
||||
private boolean lineNumbersGenerated;
|
||||
private SimpleStringPool stringPool;
|
||||
private boolean longjmpUsed = true;
|
||||
private boolean heapDump;
|
||||
private boolean obfuscated;
|
||||
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
@ -202,10 +199,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||
this.lineNumbersGenerated = lineNumbersGenerated;
|
||||
}
|
||||
|
||||
public void setLongjmpUsed(boolean longjmpUsed) {
|
||||
this.longjmpUsed = longjmpUsed;
|
||||
}
|
||||
|
||||
public void setHeapDump(boolean heapDump) {
|
||||
this.heapDump = heapDump;
|
||||
}
|
||||
@ -244,9 +237,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||
characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||
classInitializerTransformer = new ClassInitializerTransformer();
|
||||
shadowStackTransformer = new ShadowStackTransformer(characteristics, !longjmpUsed);
|
||||
shadowStackTransformer = new ShadowStackTransformer(characteristics);
|
||||
nullCheckInsertion = new NullCheckInsertion(new LowLevelNullCheckFilter(characteristics));
|
||||
checkTransformation = new CheckInstructionTransformation();
|
||||
writeBarrierInsertion = new WriteBarrierInsertion(characteristics);
|
||||
|
||||
controller.addVirtualMethods(VIRTUAL_METHODS::contains);
|
||||
@ -357,14 +349,11 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||
public void afterOptimizations(Program program, MethodReader method) {
|
||||
classInitializerEliminator.apply(program);
|
||||
classInitializerTransformer.transform(program);
|
||||
if (!longjmpUsed) {
|
||||
checkTransformation.apply(program, method.getResultType());
|
||||
}
|
||||
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads)
|
||||
.apply(program, method.getReference());
|
||||
ShadowStackTransformer shadowStackTransformer = !incremental
|
||||
var shadowStackTransformer = !incremental
|
||||
? this.shadowStackTransformer
|
||||
: new ShadowStackTransformer(characteristics, !longjmpUsed);
|
||||
: new ShadowStackTransformer(characteristics);
|
||||
shadowStackTransformer.apply(program, method);
|
||||
writeBarrierInsertion.apply(program);
|
||||
}
|
||||
@ -412,7 +401,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
|
||||
controller.getDependencyInfo(), stringPool, nameProvider, fileNames,
|
||||
controller.getDiagnostics(), classes, intrinsics, generators, asyncMethods::contains, buildTarget,
|
||||
controller.getClassInitializerInfo(), incremental, longjmpUsed,
|
||||
controller.getClassInitializerInfo(), incremental,
|
||||
vmAssertions, vmAssertions || heapDump, obfuscated);
|
||||
|
||||
BufferedCodeWriter specialWriter = new BufferedCodeWriter(false);
|
||||
@ -422,9 +411,6 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||
if (incremental) {
|
||||
configHeaderWriter.println("#define TEAVM_INCREMENTAL 1");
|
||||
}
|
||||
if (!longjmpUsed) {
|
||||
configHeaderWriter.println("#define TEAVM_USE_SETJMP 0");
|
||||
}
|
||||
if (vmAssertions) {
|
||||
configHeaderWriter.println("#define TEAVM_MEMORY_TRACE 1");
|
||||
}
|
||||
@ -441,7 +427,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||
ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler,
|
||||
controller.getCacheStatus());
|
||||
classGenerator.setAstCache(astCache);
|
||||
if (context.isLongjmp() && !context.isIncremental()) {
|
||||
if (!context.isIncremental()) {
|
||||
classGenerator.setCallSites(callSites);
|
||||
}
|
||||
IntrinsicFactoryContextImpl intrinsicFactoryContext = new IntrinsicFactoryContextImpl(
|
||||
@ -545,9 +531,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||
|
||||
private void generateFastCallSites(GenerationContext context, CodeWriter writer, IncludeManager includes,
|
||||
Collection<? extends String> classNames) {
|
||||
List<? extends CallSiteDescriptor> callSites = context.isLongjmp()
|
||||
? this.callSites
|
||||
: CallSiteDescriptor.extract(context.getClassSource(), classNames);
|
||||
var callSites = this.callSites;
|
||||
new CallSiteGenerator(context, writer, includes, "teavm_callSites").generate(callSites);
|
||||
if (obfuscated) {
|
||||
generateCallSitesJson(context.getBuildTarget(), callSites);
|
||||
|
@ -258,7 +258,7 @@ public class ClassGenerator {
|
||||
ClassGenerationContext classContext = new ClassGenerationContext(context, includes, prologueWriter,
|
||||
initWriter, currentClassName);
|
||||
codeGenerator = new CodeGenerator(classContext, codeWriter, includes);
|
||||
if (context.isLongjmp() && !context.isIncremental()) {
|
||||
if (!context.isIncremental()) {
|
||||
codeGenerator.setCallSites(callSites);
|
||||
}
|
||||
}
|
||||
@ -334,18 +334,15 @@ public class ClassGenerator {
|
||||
}
|
||||
|
||||
List<CallSiteDescriptor> callSites = null;
|
||||
if (context.isLongjmp()) {
|
||||
if (context.isIncremental()) {
|
||||
callSites = new ArrayList<>();
|
||||
codeGenerator.setCallSites(callSites);
|
||||
}
|
||||
if (context.isIncremental()) {
|
||||
callSites = new ArrayList<>();
|
||||
codeGenerator.setCallSites(callSites);
|
||||
}
|
||||
|
||||
codeGenerator.generateMethod(methodNode);
|
||||
|
||||
if (context.isIncremental()) {
|
||||
generateCallSites(method.getReference(),
|
||||
context.isLongjmp() ? callSites : CallSiteDescriptor.extract(method.getProgram()));
|
||||
generateCallSites(method.getReference(), callSites);
|
||||
codeWriter.println("#undef TEAVM_ALLOC_STACK");
|
||||
}
|
||||
}
|
||||
@ -1273,7 +1270,7 @@ public class ClassGenerator {
|
||||
codeWriter.outdent().println("}");
|
||||
|
||||
GeneratorContextImpl generatorContext = new GeneratorContextImpl(codeGenerator.getClassContext(),
|
||||
bodyWriter, writerBefore, codeWriter, includes, callSites, context.isLongjmp());
|
||||
bodyWriter, writerBefore, codeWriter, includes, callSites);
|
||||
generator.generate(generatorContext, methodRef);
|
||||
try {
|
||||
generatorContext.flush();
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.teavm.backend.c.generate;
|
||||
|
||||
import static org.teavm.model.lowlevel.ExceptionHandlingShadowStackContributor.isManagedMethodCall;
|
||||
import static org.teavm.model.lowlevel.ExceptionHandlingUtil.isManagedMethodCall;
|
||||
import com.carrotsearch.hppc.IntContainer;
|
||||
import com.carrotsearch.hppc.IntHashSet;
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
@ -469,7 +469,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||
}
|
||||
|
||||
private boolean needsCallSiteId() {
|
||||
return context.isLongjmp() && managed;
|
||||
return managed;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1357,9 +1357,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||
}
|
||||
writer.println(";");
|
||||
|
||||
if (context.isLongjmp()) {
|
||||
writer.println("TEAVM_UNREACHABLE");
|
||||
}
|
||||
writer.println("TEAVM_UNREACHABLE");
|
||||
|
||||
popLocation(statement.getLocation());
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ public class GenerationContext {
|
||||
private BuildTarget buildTarget;
|
||||
private ClassInitializerInfo classInitializerInfo;
|
||||
private boolean incremental;
|
||||
private boolean longjmp;
|
||||
private boolean vmAssertions;
|
||||
private boolean heapDump;
|
||||
private boolean obfuscated;
|
||||
@ -57,7 +56,7 @@ public class GenerationContext {
|
||||
DependencyInfo dependencies, StringPool stringPool, NameProvider names, FileNameProvider fileNames,
|
||||
Diagnostics diagnostics, ClassReaderSource classSource, List<Intrinsic> intrinsics,
|
||||
List<Generator> generators, Predicate<MethodReference> asyncMethods, BuildTarget buildTarget,
|
||||
ClassInitializerInfo classInitializerInfo, boolean incremental, boolean longjmp, boolean vmAssertions,
|
||||
ClassInitializerInfo classInitializerInfo, boolean incremental, boolean vmAssertions,
|
||||
boolean heapDump, boolean obfuscated) {
|
||||
this.virtualTableProvider = virtualTableProvider;
|
||||
this.characteristics = characteristics;
|
||||
@ -73,7 +72,6 @@ public class GenerationContext {
|
||||
this.buildTarget = buildTarget;
|
||||
this.classInitializerInfo = classInitializerInfo;
|
||||
this.incremental = incremental;
|
||||
this.longjmp = longjmp;
|
||||
this.vmAssertions = vmAssertions;
|
||||
this.heapDump = heapDump;
|
||||
this.obfuscated = obfuscated;
|
||||
@ -149,10 +147,6 @@ public class GenerationContext {
|
||||
return incremental;
|
||||
}
|
||||
|
||||
public boolean isLongjmp() {
|
||||
return longjmp;
|
||||
}
|
||||
|
||||
public boolean isHeapDump() {
|
||||
return heapDump;
|
||||
}
|
||||
|
@ -38,11 +38,10 @@ class GeneratorContextImpl implements GeneratorContext {
|
||||
private IncludeManager includes;
|
||||
private List<FileGeneratorImpl> fileGenerators = new ArrayList<>();
|
||||
private List<CallSiteDescriptor> callSites;
|
||||
private boolean longjmp;
|
||||
|
||||
public GeneratorContextImpl(ClassGenerationContext classContext, CodeWriter bodyWriter,
|
||||
CodeWriter writerBefore, CodeWriter writerAfter, IncludeManager includes,
|
||||
List<CallSiteDescriptor> callSites, boolean longjmp) {
|
||||
List<CallSiteDescriptor> callSites) {
|
||||
this.context = classContext.getContext();
|
||||
this.classContext = classContext;
|
||||
this.bodyWriter = bodyWriter;
|
||||
@ -50,7 +49,6 @@ class GeneratorContextImpl implements GeneratorContext {
|
||||
this.writerAfter = writerAfter;
|
||||
this.includes = includes;
|
||||
this.callSites = callSites;
|
||||
this.longjmp = longjmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -142,11 +140,6 @@ class GeneratorContextImpl implements GeneratorContext {
|
||||
return callSite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean usesLongjmp() {
|
||||
return longjmp;
|
||||
}
|
||||
|
||||
void flush() throws IOException {
|
||||
for (FileGeneratorImpl generator : fileGenerators) {
|
||||
OutputFileUtil.write(generator.writer, generator.path, context.getBuildTarget());
|
||||
|
@ -57,7 +57,5 @@ public interface GeneratorContext {
|
||||
|
||||
void importMethod(MethodReference method, boolean isStatic);
|
||||
|
||||
boolean usesLongjmp();
|
||||
|
||||
CallSiteDescriptor createCallSite(CallSiteLocation[] locations, ExceptionHandlerDescriptor[] exceptionHandlers);
|
||||
}
|
||||
|
@ -22,75 +22,99 @@ import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.runtime.RuntimeObject;
|
||||
|
||||
@StaticInit
|
||||
@Unmanaged
|
||||
public final class WasmRuntime {
|
||||
private WasmRuntime() {
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int compare(int a, int b) {
|
||||
return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int compareUnsigned(int a, int b) {
|
||||
return gtu(a, b) ? 1 : ltu(a, b) ? -1 : 0;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int compareUnsigned(long a, long b) {
|
||||
return gtu(a, b) ? 1 : ltu(a, b) ? -1 : 0;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int compare(long a, long b) {
|
||||
return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int compare(float a, float b) {
|
||||
return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int compare(double a, double b) {
|
||||
return gt(a, b) ? 1 : lt(a, b) ? -1 : 0;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static native float min(float a, float b);
|
||||
|
||||
@Unmanaged
|
||||
public static native double min(double a, double b);
|
||||
|
||||
@Unmanaged
|
||||
public static native float max(float a, float b);
|
||||
|
||||
@Unmanaged
|
||||
public static native double max(double a, double b);
|
||||
|
||||
@Unmanaged
|
||||
public static float remainder(float a, float b) {
|
||||
return a - (float) (int) (a / b) * b;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static double remainder(double a, double b) {
|
||||
return a - (double) (long) (a / b) * b;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean lt(int a, int b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean gt(int a, int b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean ltu(int a, int b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean gtu(int a, int b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean lt(long a, long b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean gt(long a, long b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean ltu(long a, long b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean gtu(long a, long b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean lt(float a, float b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean gt(float a, float b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean lt(double a, double b);
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean gt(double a, double b);
|
||||
|
||||
@Unmanaged
|
||||
public static Address align(Address address, int alignment) {
|
||||
int value = address.toInt();
|
||||
if (value == 0) {
|
||||
@ -100,6 +124,7 @@ public final class WasmRuntime {
|
||||
return Address.fromInt(value);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int align(int value, int alignment) {
|
||||
if (value == 0) {
|
||||
return value;
|
||||
@ -108,29 +133,36 @@ public final class WasmRuntime {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void print(int a) {
|
||||
WasmSupport.print(a);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void printString(String s) {
|
||||
WasmSupport.printString(s);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void printInt(int i) {
|
||||
WasmSupport.printInt(i);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void printOutOfMemory() {
|
||||
WasmSupport.printOutOfMemory();
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void fillZero(Address address, int count) {
|
||||
fill(address, (byte) 0, count);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void fill(Address address, byte value, int count) {
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static Address allocStack(int size) {
|
||||
Address stack = WasmHeap.stack;
|
||||
Address result = stack.add(4);
|
||||
@ -140,10 +172,12 @@ public final class WasmRuntime {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static Address getStackTop() {
|
||||
return WasmHeap.stack != WasmHeap.stackAddress ? WasmHeap.stack : null;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static Address getNextStackFrame(Address stackFrame) {
|
||||
int size = stackFrame.getInt() + 2;
|
||||
Address result = stackFrame.add(-size * 4);
|
||||
@ -153,28 +187,47 @@ public final class WasmRuntime {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int getStackRootCount(Address stackFrame) {
|
||||
return stackFrame.getInt();
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static Address getStackRootPointer(Address stackFrame) {
|
||||
int size = stackFrame.getInt();
|
||||
return stackFrame.add(-size * 4);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
private static Address getExceptionHandlerPtr(Address stackFrame) {
|
||||
int size = stackFrame.getInt();
|
||||
return stackFrame.add(-size * 4 - 4);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int getCallSiteId(Address stackFrame) {
|
||||
return getExceptionHandlerPtr(stackFrame).getInt();
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void setExceptionHandlerId(Address stackFrame, int id) {
|
||||
getExceptionHandlerPtr(stackFrame).putInt(id);
|
||||
var addr = getExceptionHandlerPtr(stackFrame);
|
||||
addr.putInt(addr.getInt() + id + 2);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void setExceptionHandlerSkip(Address stackFrame) {
|
||||
var addr = getExceptionHandlerPtr(stackFrame);
|
||||
addr.putInt(addr.getInt() + 1);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void setExceptionHandlerRestore(Address stackFrame) {
|
||||
var addr = getExceptionHandlerPtr(stackFrame);
|
||||
addr.putInt(addr.getInt() - 1);
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
private static int hashCode(RuntimeString string) {
|
||||
int hashCode = 0;
|
||||
int length = string.characters.length;
|
||||
@ -186,6 +239,7 @@ public final class WasmRuntime {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
private static boolean equals(RuntimeString first, RuntimeString second) {
|
||||
if (first.characters.length != second.characters.length) {
|
||||
return false;
|
||||
@ -210,6 +264,7 @@ public final class WasmRuntime {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
private static int resourceMapSize(Address map) {
|
||||
int result = 0;
|
||||
int sz = map.getInt();
|
||||
@ -224,6 +279,7 @@ public final class WasmRuntime {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
private static void fillResourceMapKeys(Address map, String[] target) {
|
||||
int sz = map.getInt();
|
||||
Address data = contentStart(map);
|
||||
@ -234,10 +290,11 @@ public final class WasmRuntime {
|
||||
targetData.putAddress(entry);
|
||||
targetData = targetData.add(Address.sizeOf());
|
||||
}
|
||||
data = data.add(Address.sizeOf());
|
||||
data = data.add(Address.sizeOf() * 2);
|
||||
}
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
private static Address contentStart(Address resource) {
|
||||
return resource.add(Address.sizeOf());
|
||||
}
|
||||
@ -287,6 +344,7 @@ public final class WasmRuntime {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Unmanaged
|
||||
public static native void callFunctionFromTable(int index, RuntimeObject instance);
|
||||
|
||||
static class RuntimeString extends RuntimeObject {
|
||||
|
@ -98,6 +98,8 @@ import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntUnary;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation;
|
||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||
@ -151,9 +153,7 @@ import org.teavm.model.classes.VirtualTableProvider;
|
||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.model.lowlevel.Characteristics;
|
||||
import org.teavm.model.lowlevel.CheckInstructionTransformation;
|
||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||
import org.teavm.model.lowlevel.LowLevelNullCheckFilter;
|
||||
@ -202,7 +202,6 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
private List<WasmIntrinsicFactory> additionalIntrinsics = new ArrayList<>();
|
||||
private NullCheckInsertion nullCheckInsertion;
|
||||
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||
private CheckInstructionTransformation checkTransformation = new CheckInstructionTransformation();
|
||||
private int minHeapSize = 2 * 1024 * 1024;
|
||||
private int maxHeapSize = 128 * 1024 * 1024;
|
||||
private boolean obfuscated;
|
||||
@ -218,7 +217,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||
classInitializerTransformer = new ClassInitializerTransformer();
|
||||
shadowStackTransformer = new ShadowStackTransformer(characteristics, true);
|
||||
shadowStackTransformer = new ShadowStackTransformer(characteristics);
|
||||
nullCheckInsertion = new NullCheckInsertion(new LowLevelNullCheckFilter(characteristics));
|
||||
writeBarrierInsertion = new WriteBarrierInsertion(characteristics);
|
||||
|
||||
@ -349,6 +348,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
Address.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "setExceptionHandlerId", Address.class,
|
||||
int.class, void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "setExceptionHandlerSkip",
|
||||
Address.class, void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "setExceptionHandlerRestore",
|
||||
Address.class, void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getCallSiteId", Address.class,
|
||||
int.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "resourceMapKeys", Address.class,
|
||||
@ -377,6 +380,12 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwException",
|
||||
Throwable.class, void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
|
||||
void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwClassCastException",
|
||||
void.class)).use();
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class,
|
||||
"throwArrayIndexOutOfBoundsException", void.class)).use();
|
||||
|
||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException",
|
||||
Throwable.class)).use();
|
||||
@ -445,9 +454,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
classInitializerTransformer.transform(program);
|
||||
new CoroutineTransformation(controller.getUnprocessedClassSource(), asyncMethods, hasThreads)
|
||||
.apply(program, method.getReference());
|
||||
checkTransformation.apply(program, method.getResultType());
|
||||
shadowStackTransformer.apply(program, method);
|
||||
checkPhis(program, method);
|
||||
//checkPhis(program, method);
|
||||
writeBarrierInsertion.apply(program);
|
||||
}
|
||||
|
||||
@ -496,7 +504,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
Decompiler decompiler = new Decompiler(classes, new HashSet<>(), false);
|
||||
var stringPool = classGenerator.getStringPool();
|
||||
var context = new WasmGenerationContext(classes, module, controller.getDiagnostics(),
|
||||
vtableProvider, tagRegistry, stringPool, names);
|
||||
vtableProvider, tagRegistry, stringPool, names, characteristics);
|
||||
|
||||
context.addIntrinsic(new AddressIntrinsic(classGenerator));
|
||||
context.addIntrinsic(new StructureIntrinsic(classes, classGenerator));
|
||||
@ -543,7 +551,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
|
||||
generateMethods(classes, context, generator, classGenerator, binaryWriter, module, dwarfClassGen);
|
||||
new WasmInteropFunctionGenerator(classGenerator).generateFunctions(module);
|
||||
exceptionHandlingIntrinsic.postProcess(CallSiteDescriptor.extract(classes, classes.getClassNames()));
|
||||
exceptionHandlingIntrinsic.postProcess(context.callSites);
|
||||
generateIsSupertypeFunctions(tagRegistry, module, classGenerator);
|
||||
classGenerator.postProcess();
|
||||
new WasmSpecialFunctionGenerator(classGenerator, gcIntrinsic.regionSizeExpressions)
|
||||
@ -922,10 +930,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
|
||||
int tagOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeClass.class.getName(), "tag"));
|
||||
|
||||
WasmExpression tagExpression = new WasmGetLocal(subtypeVar);
|
||||
tagExpression = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, tagExpression,
|
||||
new WasmInt32Constant(tagOffset));
|
||||
tagExpression = new WasmLoadInt32(4, tagExpression, WasmInt32Subtype.INT32);
|
||||
var tagExpression = new WasmLoadInt32(4, new WasmGetLocal(subtypeVar), WasmInt32Subtype.INT32);
|
||||
tagExpression.setOffset(tagOffset);
|
||||
body.add(new WasmSetLocal(subtypeVar, tagExpression));
|
||||
|
||||
ranges.sort(Comparator.comparingInt(range -> range.lower));
|
||||
@ -949,7 +955,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
int lowerHole = ranges.get(i - 1).upper;
|
||||
int upperHole = ranges.get(i).lower;
|
||||
|
||||
lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED,
|
||||
lowerCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GE_SIGNED,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(lowerHole));
|
||||
testLower = new WasmConditional(lowerCondition);
|
||||
body.add(testLower);
|
||||
@ -969,15 +975,12 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
List<WasmExpression> body) {
|
||||
int itemOffset = classGenerator.getFieldOffset(new FieldReference(RuntimeClass.class.getName(), "itemType"));
|
||||
|
||||
WasmExpression itemExpression = new WasmGetLocal(subtypeVar);
|
||||
itemExpression = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, itemExpression,
|
||||
new WasmInt32Constant(itemOffset));
|
||||
itemExpression = new WasmLoadInt32(4, itemExpression, WasmInt32Subtype.INT32);
|
||||
var itemExpression = new WasmLoadInt32(4, new WasmGetLocal(subtypeVar), WasmInt32Subtype.INT32);
|
||||
itemExpression.setOffset(itemOffset);
|
||||
body.add(new WasmSetLocal(subtypeVar, itemExpression));
|
||||
|
||||
WasmExpression itemCondition = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ,
|
||||
new WasmGetLocal(subtypeVar), new WasmInt32Constant(0));
|
||||
WasmConditional itemTest = new WasmConditional(itemCondition);
|
||||
var itemTest = new WasmConditional(new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ,
|
||||
new WasmGetLocal(subtypeVar)));
|
||||
itemTest.setType(WasmType.INT32);
|
||||
itemTest.getThenBlock().getBody().add(new WasmInt32Constant(0));
|
||||
|
||||
@ -1197,7 +1200,17 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||
TeaVMEntryPoint entryPoint = entryPointIter.next();
|
||||
String name = manager.getNames().forMethod(entryPoint.getMethod());
|
||||
WasmCall call = new WasmCall(name);
|
||||
call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
|
||||
var arg = manager.generate(invocation.getArguments().get(0));
|
||||
if (manager.isManagedMethodCall(entryPoint.getMethod())) {
|
||||
var block = new WasmBlock(false);
|
||||
block.setType(WasmType.INT32);
|
||||
var callSiteId = manager.generateCallSiteId(invocation.getLocation());
|
||||
block.getBody().add(manager.generateRegisterCallSite(callSiteId,
|
||||
invocation.getLocation()));
|
||||
block.getBody().add(arg);
|
||||
arg = block;
|
||||
}
|
||||
call.getArguments().add(arg);
|
||||
call.setLocation(invocation.getLocation());
|
||||
return call;
|
||||
} else {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
* Copyright 2023 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -13,14 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
package org.teavm.backend.wasm.generate;
|
||||
|
||||
@interface CallSiteLocationAnnot {
|
||||
String fileName();
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
|
||||
String className();
|
||||
abstract class CachedExpression {
|
||||
abstract WasmExpression expr();
|
||||
|
||||
String methodName();
|
||||
void release() {
|
||||
}
|
||||
|
||||
int lineNumber();
|
||||
}
|
@ -102,7 +102,7 @@ public class CallSiteBinaryGenerator {
|
||||
int address = writer.append(binaryExceptionHandler);
|
||||
binaryExceptionHandlers.add(binaryExceptionHandler);
|
||||
|
||||
binaryExceptionHandler.setInt(EXCEPTION_HANDLER_ID, callSiteId + i + 1);
|
||||
binaryExceptionHandler.setInt(EXCEPTION_HANDLER_ID, callSite.getHandlers().get(i).getId());
|
||||
|
||||
if (!firstHandlerSet) {
|
||||
binaryCallSite.setAddress(CALL_SITE_FIRST_HANDLER, address);
|
||||
|
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2023 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.generate;
|
||||
|
||||
import java.util.List;
|
||||
import org.teavm.backend.wasm.model.WasmLocal;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||
import org.teavm.model.TextLocation;
|
||||
|
||||
class ExpressionCache {
|
||||
private TemporaryVariablePool tmpVars;
|
||||
|
||||
ExpressionCache(TemporaryVariablePool tmpVars) {
|
||||
this.tmpVars = tmpVars;
|
||||
}
|
||||
|
||||
public CachedExpression create(WasmExpression expr, WasmType type, TextLocation location,
|
||||
List<WasmExpression> body) {
|
||||
if (expr instanceof WasmGetLocal) {
|
||||
var getLocalExpr = (WasmGetLocal) expr;
|
||||
return new LocalVarCachedExpression(getLocalExpr.getLocal());
|
||||
} else if (expr instanceof WasmInt32Constant) {
|
||||
var constExpr = (WasmInt32Constant) expr;
|
||||
return new Int32CachedExpression(constExpr.getValue());
|
||||
} else {
|
||||
var tmpVar = tmpVars.acquire(type);
|
||||
var storeExpr = new WasmSetLocal(tmpVar, expr);
|
||||
storeExpr.setLocation(location);
|
||||
body.add(storeExpr);
|
||||
return new TmpVarCachedExpression(tmpVar);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LocalVarCachedExpression extends CachedExpression {
|
||||
private final WasmLocal localVar;
|
||||
|
||||
LocalVarCachedExpression(WasmLocal localVar) {
|
||||
this.localVar = localVar;
|
||||
}
|
||||
|
||||
@Override
|
||||
WasmExpression expr() {
|
||||
return new WasmGetLocal(localVar);
|
||||
}
|
||||
}
|
||||
|
||||
private class TmpVarCachedExpression extends CachedExpression {
|
||||
private final WasmLocal tmpVar;
|
||||
|
||||
TmpVarCachedExpression(WasmLocal tmpVar) {
|
||||
this.tmpVar = tmpVar;
|
||||
}
|
||||
|
||||
@Override
|
||||
WasmExpression expr() {
|
||||
return new WasmGetLocal(tmpVar);
|
||||
}
|
||||
|
||||
@Override
|
||||
void release() {
|
||||
tmpVars.release(tmpVar);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Int32CachedExpression extends CachedExpression {
|
||||
private final int value;
|
||||
|
||||
Int32CachedExpression(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
WasmExpression expr() {
|
||||
return new WasmInt32Constant(value);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2023 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.backend.wasm.generate;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmLocal;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
|
||||
class TemporaryVariablePool {
|
||||
private WasmFunction function;
|
||||
private List<Deque<WasmLocal>> temporaryVariablesByType = new ArrayList<>();
|
||||
|
||||
TemporaryVariablePool(WasmFunction function) {
|
||||
this.function = function;
|
||||
int typeCount = WasmType.values().length;
|
||||
for (int i = 0; i < typeCount; ++i) {
|
||||
temporaryVariablesByType.add(new ArrayDeque<>());
|
||||
}
|
||||
}
|
||||
|
||||
WasmLocal acquire(WasmType type) {
|
||||
var stack = temporaryVariablesByType.get(type.ordinal());
|
||||
WasmLocal variable = stack.pollFirst();
|
||||
if (variable == null) {
|
||||
variable = new WasmLocal(type);
|
||||
function.add(variable);
|
||||
}
|
||||
return variable;
|
||||
}
|
||||
|
||||
void release(WasmLocal variable) {
|
||||
var stack = temporaryVariablesByType.get(variable.getType().ordinal());
|
||||
stack.push(variable);
|
||||
}
|
||||
}
|
@ -241,6 +241,7 @@ public class WasmClassGenerator {
|
||||
value.setInt(CLASS_IS_INSTANCE, getFunctionPointer(names.forSupertypeFunction(type)));
|
||||
value.setAddress(CLASS_SIMPLE_NAME, 0);
|
||||
value.setInt(CLASS_INIT, -1);
|
||||
value.setInt(CLASS_TAG, Integer.MAX_VALUE);
|
||||
|
||||
String name;
|
||||
if (type == ValueType.VOID) {
|
||||
|
@ -37,6 +37,8 @@ import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.classes.TagRegistry;
|
||||
import org.teavm.model.classes.VirtualTableProvider;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.model.lowlevel.Characteristics;
|
||||
|
||||
public class WasmGenerationContext {
|
||||
private ClassReaderSource classSource;
|
||||
@ -46,15 +48,17 @@ public class WasmGenerationContext {
|
||||
private TagRegistry tagRegistry;
|
||||
private WasmStringPool stringPool;
|
||||
public final NameProvider names;
|
||||
public final Characteristics characteristics;
|
||||
private Map<MethodReference, ImportedMethod> importedMethods = new HashMap<>();
|
||||
private List<WasmIntrinsic> intrinsics = new ArrayList<>();
|
||||
private List<WasmMethodGenerator> generators = new ArrayList<>();
|
||||
private Map<MethodReference, IntrinsicHolder> intrinsicCache = new HashMap<>();
|
||||
private Map<MethodReference, GeneratorHolder> generatorCache = new HashMap<>();
|
||||
public final List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
|
||||
public WasmGenerationContext(ClassReaderSource classSource, WasmModule module, Diagnostics diagnostics,
|
||||
VirtualTableProvider vtableProvider, TagRegistry tagRegistry, WasmStringPool stringPool,
|
||||
NameProvider names) {
|
||||
NameProvider names, Characteristics characteristics) {
|
||||
this.classSource = classSource;
|
||||
this.module = module;
|
||||
this.diagnostics = diagnostics;
|
||||
@ -62,6 +66,7 @@ public class WasmGenerationContext {
|
||||
this.tagRegistry = tagRegistry;
|
||||
this.stringPool = stringPool;
|
||||
this.names = names;
|
||||
this.characteristics = characteristics;
|
||||
}
|
||||
|
||||
public void addIntrinsic(WasmIntrinsic intrinsic) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,6 @@ import org.teavm.backend.wasm.debug.info.VariableType;
|
||||
import org.teavm.backend.wasm.model.WasmFunction;
|
||||
import org.teavm.backend.wasm.model.WasmLocal;
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||
import org.teavm.interop.Export;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.ClassHolder;
|
||||
@ -92,13 +91,9 @@ public class WasmGenerator {
|
||||
function.add(local);
|
||||
}
|
||||
|
||||
WasmGenerationVisitor visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function,
|
||||
var visitor = new WasmGenerationVisitor(context, classGenerator, binaryWriter, function, methodReference,
|
||||
firstVariable, asyncMethods.test(methodReference));
|
||||
methodAst.getBody().acceptVisitor(visitor);
|
||||
if (visitor.result instanceof WasmBlock) {
|
||||
((WasmBlock) visitor.result).setType(function.getResult());
|
||||
}
|
||||
function.getBody().add(visitor.result);
|
||||
visitor.generate(methodAst.getBody(), function.getBody());
|
||||
|
||||
AnnotationReader exportAnnot = method.getAnnotations().get(Export.class.getName());
|
||||
if (exportAnnot != null) {
|
||||
|
@ -52,7 +52,7 @@ public class ArrayGenerator implements WasmMethodGenerator {
|
||||
"Float", "Double", "Boolean" };
|
||||
private static final ValueType.Primitive[] primitiveTypes = { ValueType.BYTE, ValueType.SHORT, ValueType.CHARACTER,
|
||||
ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN };
|
||||
private static final int[] shift = new int[] { 0, 1, 1, 2, 3, 2, 3, 0 };
|
||||
private static final int[] shift = new int[] { 0, 0, 1, 1, 2, 3, 2, 3, 0 };
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(MethodReference methodReference) {
|
||||
@ -99,7 +99,11 @@ public class ArrayGenerator implements WasmMethodGenerator {
|
||||
function.getBody().add(currentBlock);
|
||||
function.getBody().add(new WasmReturn(new WasmInt32Constant(0)));
|
||||
|
||||
WasmSwitch primitiveSwitch = new WasmSwitch(new WasmGetLocal(flagsVar), currentBlock);
|
||||
var primitiveExpr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHR_UNSIGNED,
|
||||
new WasmGetLocal(flagsVar), new WasmInt32Constant(RuntimeClass.PRIMITIVE_SHIFT));
|
||||
primitiveExpr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
|
||||
new WasmGetLocal(flagsVar), new WasmInt32Constant(RuntimeClass.PRIMITIVE_MASK));
|
||||
WasmSwitch primitiveSwitch = new WasmSwitch(primitiveExpr, currentBlock);
|
||||
for (int i = 0; i <= 8; ++i) {
|
||||
primitiveSwitch.getTargets().add(currentBlock);
|
||||
}
|
||||
|
@ -62,18 +62,11 @@ public class DoubleIntrinsic implements WasmIntrinsic {
|
||||
case "getNaN":
|
||||
return new WasmFloat64Constant(Double.NaN);
|
||||
case "isNaN":
|
||||
return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager,
|
||||
WasmIntBinaryOperation.NE);
|
||||
return testNaN(manager.generate(invocation.getArguments().get(0)), manager);
|
||||
case "isInfinite":
|
||||
return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager,
|
||||
WasmIntBinaryOperation.NE);
|
||||
case "isFinite": {
|
||||
WasmExpression result = testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager,
|
||||
WasmIntBinaryOperation.NE);
|
||||
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, result,
|
||||
new WasmInt32Constant(0));
|
||||
return result;
|
||||
}
|
||||
return testIsInfinite(manager.generate(invocation.getArguments().get(0)));
|
||||
case "isFinite":
|
||||
return testIsFinite(manager.generate(invocation.getArguments().get(0)));
|
||||
case "doubleToRawLongBits": {
|
||||
WasmConversion conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false,
|
||||
manager.generate(invocation.getArguments().get(0)));
|
||||
@ -91,8 +84,7 @@ public class DoubleIntrinsic implements WasmIntrinsic {
|
||||
}
|
||||
}
|
||||
|
||||
private WasmExpression testSpecialIEEE(WasmExpression expression, WasmIntrinsicManager manager,
|
||||
WasmIntBinaryOperation fractionOp) {
|
||||
private WasmExpression testNaN(WasmExpression expression, WasmIntrinsicManager manager) {
|
||||
WasmLocal bitsVar = manager.getTemporary(WasmType.INT64);
|
||||
WasmBlock block = new WasmBlock(false);
|
||||
block.setType(WasmType.INT32);
|
||||
@ -107,14 +99,36 @@ public class DoubleIntrinsic implements WasmIntrinsic {
|
||||
new WasmGetLocal(bitsVar), new WasmInt64Constant(FRACTION_BITS));
|
||||
WasmExpression testExponent = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.NE,
|
||||
exponentBits, new WasmInt64Constant(EXPONENT_BITS));
|
||||
WasmExpression testFraction = new WasmIntBinary(WasmIntType.INT64, fractionOp,
|
||||
WasmExpression testFraction = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.NE,
|
||||
fractionBits, new WasmInt64Constant(0));
|
||||
|
||||
WasmBranch breakIfWrongExponent = new WasmBranch(testExponent, block);
|
||||
breakIfWrongExponent.setResult(new WasmInt32Constant(0));
|
||||
block.getBody().add(new WasmDrop(breakIfWrongExponent));
|
||||
|
||||
manager.releaseTemporary(bitsVar);
|
||||
|
||||
block.getBody().add(testFraction);
|
||||
return block;
|
||||
}
|
||||
|
||||
private WasmExpression testIsInfinite(WasmExpression expression) {
|
||||
var conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, expression);
|
||||
conversion.setReinterpret(true);
|
||||
|
||||
var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND,
|
||||
conversion, new WasmInt64Constant(EXPONENT_BITS));
|
||||
return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.EQ, result,
|
||||
new WasmInt64Constant(EXPONENT_BITS));
|
||||
}
|
||||
|
||||
private WasmExpression testIsFinite(WasmExpression expression) {
|
||||
var conversion = new WasmConversion(WasmType.FLOAT64, WasmType.INT64, false, expression);
|
||||
conversion.setReinterpret(true);
|
||||
|
||||
var result = new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.AND,
|
||||
conversion, new WasmInt64Constant(EXPONENT_BITS));
|
||||
return new WasmIntBinary(WasmIntType.INT64, WasmIntBinaryOperation.NE, result,
|
||||
new WasmInt64Constant(EXPONENT_BITS));
|
||||
}
|
||||
}
|
||||
|
@ -18,16 +18,20 @@ package org.teavm.backend.wasm.intrinsics;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.teavm.ast.InvocationExpr;
|
||||
import org.teavm.backend.wasm.WasmHeap;
|
||||
import org.teavm.backend.wasm.binary.BinaryWriter;
|
||||
import org.teavm.backend.wasm.generate.CallSiteBinaryGenerator;
|
||||
import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
||||
import org.teavm.backend.wasm.generate.WasmStringPool;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||
import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmStoreInt32;
|
||||
import org.teavm.backend.wasm.model.expression.WasmUnreachable;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||
import org.teavm.runtime.CallSite;
|
||||
@ -87,7 +91,13 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
|
||||
case "isObfuscated":
|
||||
return new WasmInt32Constant(0);
|
||||
|
||||
case "jumpToFrame":
|
||||
case "jumpToFrame": {
|
||||
var offset = classGenerator.getFieldOffset(new FieldReference(WasmHeap.class.getName(), "stack"));
|
||||
var ptr = new WasmInt32Constant(offset);
|
||||
return new WasmStoreInt32(4, ptr, manager.generate(invocation.getArguments().get(0)),
|
||||
WasmInt32Subtype.INT32);
|
||||
}
|
||||
|
||||
case "abort":
|
||||
return new WasmUnreachable();
|
||||
|
||||
|
@ -61,18 +61,11 @@ public class FloatIntrinsic implements WasmIntrinsic {
|
||||
case "getNaN":
|
||||
return new WasmFloat32Constant(Float.NaN);
|
||||
case "isNaN":
|
||||
return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager,
|
||||
WasmIntBinaryOperation.NE);
|
||||
return testNaN(manager.generate(invocation.getArguments().get(0)), manager);
|
||||
case "isInfinite":
|
||||
return testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager,
|
||||
WasmIntBinaryOperation.EQ);
|
||||
case "isFinite": {
|
||||
WasmExpression result = testSpecialIEEE(manager.generate(invocation.getArguments().get(0)), manager,
|
||||
WasmIntBinaryOperation.EQ);
|
||||
result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, result,
|
||||
new WasmInt32Constant(0));
|
||||
return result;
|
||||
}
|
||||
return testIsInfinite(manager.generate(invocation.getArguments().get(0)));
|
||||
case "isFinite":
|
||||
return testIsFinite(manager.generate(invocation.getArguments().get(0)));
|
||||
case "floatToRawIntBits": {
|
||||
WasmConversion conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false,
|
||||
manager.generate(invocation.getArguments().get(0)));
|
||||
@ -90,8 +83,7 @@ public class FloatIntrinsic implements WasmIntrinsic {
|
||||
}
|
||||
}
|
||||
|
||||
private WasmExpression testSpecialIEEE(WasmExpression expression, WasmIntrinsicManager manager,
|
||||
WasmIntBinaryOperation fractionOp) {
|
||||
private WasmExpression testNaN(WasmExpression expression, WasmIntrinsicManager manager) {
|
||||
WasmLocal bitsVar = manager.getTemporary(WasmType.INT32);
|
||||
WasmBlock block = new WasmBlock(false);
|
||||
block.setType(WasmType.INT32);
|
||||
@ -106,14 +98,36 @@ public class FloatIntrinsic implements WasmIntrinsic {
|
||||
new WasmGetLocal(bitsVar), new WasmInt32Constant(FRACTION_BITS));
|
||||
WasmExpression testExponent = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE,
|
||||
exponentBits, new WasmInt32Constant(EXPONENT_BITS));
|
||||
WasmExpression testFraction = new WasmIntBinary(WasmIntType.INT32, fractionOp,
|
||||
WasmExpression testFraction = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE,
|
||||
fractionBits, new WasmInt32Constant(0));
|
||||
|
||||
WasmBranch breakIfWrongExponent = new WasmBranch(testExponent, block);
|
||||
breakIfWrongExponent.setResult(new WasmInt32Constant(0));
|
||||
block.getBody().add(new WasmDrop(breakIfWrongExponent));
|
||||
|
||||
manager.releaseTemporary(bitsVar);
|
||||
|
||||
block.getBody().add(testFraction);
|
||||
return block;
|
||||
}
|
||||
|
||||
private WasmExpression testIsInfinite(WasmExpression expression) {
|
||||
var conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, expression);
|
||||
conversion.setReinterpret(true);
|
||||
|
||||
var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND,
|
||||
conversion, new WasmInt32Constant(EXPONENT_BITS));
|
||||
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, result,
|
||||
new WasmInt32Constant(EXPONENT_BITS));
|
||||
}
|
||||
|
||||
private WasmExpression testIsFinite(WasmExpression expression) {
|
||||
var conversion = new WasmConversion(WasmType.FLOAT32, WasmType.INT32, false, expression);
|
||||
conversion.setReinterpret(true);
|
||||
|
||||
var result = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND,
|
||||
conversion, new WasmInt32Constant(EXPONENT_BITS));
|
||||
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, result,
|
||||
new WasmInt32Constant(EXPONENT_BITS));
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ public class ShadowStackIntrinsic implements WasmIntrinsic {
|
||||
case "getStackRootPointer":
|
||||
case "getCallSiteId":
|
||||
case "setExceptionHandlerId":
|
||||
case "setExceptionHandlerSkip":
|
||||
case "setExceptionHandlerRestore":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -51,7 +51,7 @@ public class WasmHeapIntrinsic implements WasmIntrinsic {
|
||||
case "initHeapTrace":
|
||||
return new WasmDrop(new WasmInt32Constant(0));
|
||||
case "growMemory":
|
||||
return new WasmMemoryGrow(manager.generate(invocation.getArguments().get(0)));
|
||||
return new WasmDrop(new WasmMemoryGrow(manager.generate(invocation.getArguments().get(0))));
|
||||
default:
|
||||
throw new IllegalArgumentException(invocation.getMethod().getName());
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.TextLocation;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
public interface WasmIntrinsicManager {
|
||||
@ -46,4 +48,12 @@ public interface WasmIntrinsicManager {
|
||||
int getFunctionPointer(String name);
|
||||
|
||||
void releaseTemporary(WasmLocal local);
|
||||
|
||||
boolean isManagedMethodCall(MethodReference method);
|
||||
|
||||
int generateCallSiteId(TextLocation location);
|
||||
|
||||
WasmExpression generateRegisterCallSite(int callSite, TextLocation location);
|
||||
|
||||
|
||||
}
|
||||
|
@ -52,4 +52,12 @@ public class WasmBlock extends WasmExpression {
|
||||
public void acceptVisitor(WasmExpressionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminating() {
|
||||
if (loop) {
|
||||
return false;
|
||||
}
|
||||
return !body.isEmpty() && body.get(body.size() - 1).isTerminating();
|
||||
}
|
||||
}
|
||||
|
@ -47,4 +47,9 @@ public class WasmBreak extends WasmExpression {
|
||||
public void acceptVisitor(WasmExpressionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminating() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -58,4 +58,9 @@ public class WasmConditional extends WasmExpression {
|
||||
public void acceptVisitor(WasmExpressionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminating() {
|
||||
return thenBlock.isTerminating() && elseBlock.isTerminating();
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.teavm.backend.wasm.model.expression;
|
||||
|
||||
import org.teavm.backend.wasm.model.WasmType;
|
||||
import org.teavm.model.TextLocation;
|
||||
|
||||
public abstract class WasmExpression {
|
||||
@ -32,4 +33,26 @@ public abstract class WasmExpression {
|
||||
}
|
||||
|
||||
public abstract void acceptVisitor(WasmExpressionVisitor visitor);
|
||||
|
||||
public boolean isTerminating() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static WasmExpression defaultValueOfType(WasmType type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
switch (type) {
|
||||
case INT32:
|
||||
return new WasmInt32Constant(0);
|
||||
case INT64:
|
||||
return new WasmInt64Constant(0);
|
||||
case FLOAT32:
|
||||
return new WasmFloat32Constant(0);
|
||||
case FLOAT64:
|
||||
return new WasmFloat64Constant(0);
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
package org.teavm.backend.wasm.model.expression;
|
||||
|
||||
public enum WasmIntUnaryOperation {
|
||||
EQZ,
|
||||
CLZ,
|
||||
CTZ,
|
||||
POPCNT
|
||||
|
@ -38,4 +38,9 @@ public class WasmReturn extends WasmExpression {
|
||||
public void acceptVisitor(WasmExpressionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminating() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -55,4 +55,9 @@ public class WasmSwitch extends WasmExpression {
|
||||
public void acceptVisitor(WasmExpressionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminating() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -23,4 +23,9 @@ public class WasmUnreachable extends WasmExpression {
|
||||
public void acceptVisitor(WasmExpressionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTerminating() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -241,6 +241,9 @@ public class CodeSectionParser {
|
||||
codeListener.float64Constant(Double.longBitsToDouble(readFixedLong()));
|
||||
break;
|
||||
|
||||
case 0x45:
|
||||
codeListener.unary(WasmIntUnaryOperation.EQZ, WasmIntType.INT32);
|
||||
break;
|
||||
case 0x46:
|
||||
codeListener.binary(WasmIntBinaryOperation.EQ, WasmIntType.INT32);
|
||||
break;
|
||||
@ -272,6 +275,9 @@ public class CodeSectionParser {
|
||||
codeListener.binary(WasmIntBinaryOperation.GE_UNSIGNED, WasmIntType.INT32);
|
||||
break;
|
||||
|
||||
case 0x50:
|
||||
codeListener.unary(WasmIntUnaryOperation.EQZ, WasmIntType.INT64);
|
||||
break;
|
||||
case 0x51:
|
||||
codeListener.binary(WasmIntBinaryOperation.EQ, WasmIntType.INT64);
|
||||
break;
|
||||
|
@ -528,6 +528,9 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
|
||||
switch (expression.getType()) {
|
||||
case INT32:
|
||||
switch (expression.getOperation()) {
|
||||
case EQZ:
|
||||
writer.writeByte(0x45);
|
||||
break;
|
||||
case CLZ:
|
||||
writer.writeByte(0x67);
|
||||
break;
|
||||
@ -541,6 +544,9 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
|
||||
break;
|
||||
case INT64:
|
||||
switch (expression.getOperation()) {
|
||||
case EQZ:
|
||||
writer.writeByte(0x50);
|
||||
break;
|
||||
case CLZ:
|
||||
writer.writeByte(0x79);
|
||||
break;
|
||||
|
@ -109,9 +109,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
|
||||
if (lfDeferred) {
|
||||
lfDeferred = false;
|
||||
sb.append("\n");
|
||||
for (int i = 0; i < indentLevel; ++i) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(" ".repeat(Math.max(0, indentLevel)));
|
||||
}
|
||||
sb.append(text);
|
||||
return this;
|
||||
@ -714,6 +712,8 @@ class WasmRenderingVisitor implements WasmExpressionVisitor {
|
||||
|
||||
private String operation(WasmIntUnaryOperation operation) {
|
||||
switch (operation) {
|
||||
case EQZ:
|
||||
return "eqz";
|
||||
case CLZ:
|
||||
return "clz";
|
||||
case CTZ:
|
||||
|
@ -16,20 +16,7 @@
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.model.AnnotationContainer;
|
||||
import org.teavm.model.AnnotationContainerReader;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.Program;
|
||||
|
||||
public class CallSiteDescriptor {
|
||||
private int id;
|
||||
@ -52,88 +39,4 @@ public class CallSiteDescriptor {
|
||||
public List<ExceptionHandlerDescriptor> getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static void save(Collection<? extends CallSiteDescriptor> descriptors, AnnotationContainer annotations) {
|
||||
List<AnnotationValue> descriptorsValue = new ArrayList<>();
|
||||
for (CallSiteDescriptor descriptor : descriptors) {
|
||||
AnnotationHolder descriptorAnnot = new AnnotationHolder(CallSiteDescriptorAnnot.class.getName());
|
||||
descriptorAnnot.getValues().put("id", new AnnotationValue(descriptor.id));
|
||||
descriptorAnnot.getValues().put("location", new AnnotationValue(
|
||||
CallSiteLocation.saveMany(Arrays.asList(descriptor.locations))));
|
||||
List<AnnotationValue> handlersValue = descriptor.handlers.stream()
|
||||
.map(h -> new AnnotationValue(h.save()))
|
||||
.collect(Collectors.toList());
|
||||
descriptorAnnot.getValues().put("handlers", new AnnotationValue(handlersValue));
|
||||
descriptorsValue.add(new AnnotationValue(descriptorAnnot));
|
||||
}
|
||||
|
||||
AnnotationHolder descriptorsAnnot = new AnnotationHolder(CallSiteDescriptorsAnnot.class.getName());
|
||||
descriptorsAnnot.getValues().put("value", new AnnotationValue(descriptorsValue));
|
||||
annotations.add(descriptorsAnnot);
|
||||
}
|
||||
|
||||
public static Collection<? extends CallSiteDescriptor> load(AnnotationContainerReader annotations) {
|
||||
AnnotationReader descriptorsAnnot = annotations.get(CallSiteDescriptorsAnnot.class.getName());
|
||||
if (descriptorsAnnot == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<CallSiteDescriptor> descriptors = new ArrayList<>();
|
||||
for (AnnotationValue descriptorValue : descriptorsAnnot.getValue("value").getList()) {
|
||||
AnnotationReader descriptorAnnot = descriptorValue.getAnnotation();
|
||||
int id = descriptorAnnot.getValue("id").getInt();
|
||||
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.toArray(new CallSiteLocation[0]));
|
||||
descriptor.getHandlers().addAll(handlers);
|
||||
descriptors.add(descriptor);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public static List<? extends CallSiteDescriptor> extract(Program program) {
|
||||
List<CallSiteDescriptor> result = new ArrayList<>();
|
||||
extractTo(load(program.getAnnotations()), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<? extends CallSiteDescriptor> extract(ClassReaderSource classes,
|
||||
Collection<? extends String> classNames) {
|
||||
List<CallSiteDescriptor> result = new ArrayList<>();
|
||||
for (String className : classNames) {
|
||||
ClassReader cls = classes.get(className);
|
||||
if (cls == null) {
|
||||
continue;
|
||||
}
|
||||
for (MethodReader method : cls.getMethods()) {
|
||||
if (method.getProgram() != null) {
|
||||
extractTo(load(method.getProgram().getAnnotations()), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void extractTo(Collection<? extends CallSiteDescriptor> descriptors,
|
||||
List<CallSiteDescriptor> result) {
|
||||
for (CallSiteDescriptor descriptor : descriptors) {
|
||||
if (descriptor.id >= result.size()) {
|
||||
result.addAll(Collections.nCopies(descriptor.id - result.size() + 1, null));
|
||||
}
|
||||
result.set(descriptor.id, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
static AnnotationValue saveNullableString(String s) {
|
||||
return new AnnotationValue(s != null ? "1" + s : "0");
|
||||
}
|
||||
|
||||
static String loadNullableString(AnnotationValue value) {
|
||||
String s = value.getString();
|
||||
return s.startsWith("0") ? null : s.substring(1);
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
@interface CallSiteDescriptorAnnot {
|
||||
int id();
|
||||
|
||||
ExceptionHandlerDescriptorAnnot[] handlers();
|
||||
|
||||
CallSiteLocationsAnnot location();
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
@interface CallSiteDescriptorsAnnot {
|
||||
CallSiteDescriptorAnnot[] value();
|
||||
}
|
@ -18,9 +18,6 @@ package org.teavm.model.lowlevel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
import org.teavm.model.InliningInfo;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.TextLocation;
|
||||
@ -114,39 +111,4 @@ public class CallSiteLocation {
|
||||
public int hashCode() {
|
||||
return Objects.hash(fileName, className, methodName, lineNumber);
|
||||
}
|
||||
|
||||
public AnnotationReader save() {
|
||||
AnnotationHolder annotation = new AnnotationHolder(CallSiteLocationAnnot.class.getName());
|
||||
annotation.getValues().put("fileName", CallSiteDescriptor.saveNullableString(fileName));
|
||||
annotation.getValues().put("className", CallSiteDescriptor.saveNullableString(className));
|
||||
annotation.getValues().put("methodName", CallSiteDescriptor.saveNullableString(methodName));
|
||||
annotation.getValues().put("lineNumber", new AnnotationValue(lineNumber));
|
||||
return annotation;
|
||||
}
|
||||
|
||||
public static CallSiteLocation load(AnnotationReader reader) {
|
||||
return new CallSiteLocation(
|
||||
CallSiteDescriptor.loadNullableString(reader.getValue("fileName")),
|
||||
CallSiteDescriptor.loadNullableString(reader.getValue("className")),
|
||||
CallSiteDescriptor.loadNullableString(reader.getValue("methodName")),
|
||||
reader.getValue("lineNumber").getInt());
|
||||
}
|
||||
|
||||
public static AnnotationReader saveMany(List<? 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;
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
@interface CallSiteLocationsAnnot {
|
||||
CallSiteLocationAnnot[] value();
|
||||
}
|
@ -15,10 +15,6 @@
|
||||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.AnnotationReader;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
|
||||
public class ExceptionHandlerDescriptor {
|
||||
private int id;
|
||||
private String className;
|
||||
@ -35,17 +31,4 @@ public class ExceptionHandlerDescriptor {
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public AnnotationReader save() {
|
||||
AnnotationHolder annot = new AnnotationHolder(ExceptionHandlerDescriptorAnnot.class.getName());
|
||||
annot.getValues().put("id", new AnnotationValue(id));
|
||||
annot.getValues().put("className", CallSiteDescriptor.saveNullableString(className));
|
||||
return annot;
|
||||
}
|
||||
|
||||
public static ExceptionHandlerDescriptor load(AnnotationReader annot) {
|
||||
return new ExceptionHandlerDescriptor(
|
||||
annot.getValue("id").getInt(),
|
||||
CallSiteDescriptor.loadNullableString(annot.getValue("className")));
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
@interface ExceptionHandlerDescriptorAnnot {
|
||||
int id();
|
||||
|
||||
String className();
|
||||
}
|
@ -1,510 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
import com.carrotsearch.hppc.IntHashSet;
|
||||
import com.carrotsearch.hppc.IntObjectHashMap;
|
||||
import com.carrotsearch.hppc.IntSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.common.DominatorTree;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.common.GraphUtils;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.Incoming;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Phi;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.TextLocation;
|
||||
import org.teavm.model.TryCatchBlock;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.BinaryBranchingCondition;
|
||||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||
import org.teavm.model.instructions.BoundCheckInstruction;
|
||||
import org.teavm.model.instructions.CastInstruction;
|
||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||
import org.teavm.model.instructions.ConstructInstruction;
|
||||
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
||||
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||
import org.teavm.model.instructions.ExitInstruction;
|
||||
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||
import org.teavm.model.instructions.InitClassInstruction;
|
||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.JumpInstruction;
|
||||
import org.teavm.model.instructions.LongConstantInstruction;
|
||||
import org.teavm.model.instructions.MonitorEnterInstruction;
|
||||
import org.teavm.model.instructions.MonitorExitInstruction;
|
||||
import org.teavm.model.instructions.NullCheckInstruction;
|
||||
import org.teavm.model.instructions.NullConstantInstruction;
|
||||
import org.teavm.model.instructions.RaiseInstruction;
|
||||
import org.teavm.model.instructions.SwitchInstruction;
|
||||
import org.teavm.model.instructions.SwitchTableEntry;
|
||||
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||
import org.teavm.model.util.DefinitionExtractor;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
import org.teavm.runtime.ExceptionHandling;
|
||||
import org.teavm.runtime.ShadowStack;
|
||||
|
||||
public class ExceptionHandlingShadowStackContributor {
|
||||
private static final MethodReference FILL_STACK_TRACE = new MethodReference(ExceptionHandling.class,
|
||||
"fillStackTrace", StackTraceElement[].class);
|
||||
private Characteristics characteristics;
|
||||
private List<CallSiteDescriptor> callSites;
|
||||
private BasicBlock defaultExceptionHandler;
|
||||
private MethodReference method;
|
||||
private Program program;
|
||||
private DominatorTree dom;
|
||||
private BasicBlock[] variableDefinitionPlaces;
|
||||
private boolean hasExceptionHandlers;
|
||||
private int parameterCount;
|
||||
private boolean hasDetachedBlocks;
|
||||
|
||||
public int callSiteIdGen;
|
||||
|
||||
public ExceptionHandlingShadowStackContributor(Characteristics characteristics,
|
||||
List<CallSiteDescriptor> callSites, MethodReference method, Program program) {
|
||||
this.characteristics = characteristics;
|
||||
this.callSites = callSites;
|
||||
this.method = method;
|
||||
this.program = program;
|
||||
|
||||
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||
dom = GraphUtils.buildDominatorTree(cfg);
|
||||
variableDefinitionPlaces = ProgramUtils.getVariableDefinitionPlaces(program);
|
||||
parameterCount = method.parameterCount() + 1;
|
||||
}
|
||||
|
||||
public boolean contribute() {
|
||||
int[] blockMapping = new int[program.basicBlockCount()];
|
||||
for (int i = 0; i < blockMapping.length; ++i) {
|
||||
blockMapping[i] = i;
|
||||
}
|
||||
|
||||
List<Phi> allPhis = new ArrayList<>();
|
||||
int blockCount = program.basicBlockCount();
|
||||
for (int i = 0; i < blockCount; ++i) {
|
||||
allPhis.addAll(program.basicBlockAt(i).getPhis());
|
||||
}
|
||||
|
||||
Set<BasicBlock> exceptionHandlers = new HashSet<>();
|
||||
for (int i = 0; i < blockCount; ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||
exceptionHandlers.add(tryCatch.getHandler());
|
||||
}
|
||||
|
||||
if (block.getExceptionVariable() != null) {
|
||||
InvokeInstruction catchCall = new InvokeInstruction();
|
||||
catchCall.setType(InvocationType.SPECIAL);
|
||||
catchCall.setMethod(new MethodReference(ExceptionHandling.class, "catchException",
|
||||
Throwable.class));
|
||||
catchCall.setReceiver(block.getExceptionVariable());
|
||||
block.addFirst(catchCall);
|
||||
block.setExceptionVariable(null);
|
||||
}
|
||||
|
||||
int newIndex = contributeToBasicBlock(block);
|
||||
if (newIndex != i) {
|
||||
blockMapping[i] = newIndex;
|
||||
hasExceptionHandlers = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (Phi phi : allPhis) {
|
||||
if (!exceptionHandlers.contains(phi.getBasicBlock())) {
|
||||
for (Incoming incoming : phi.getIncomings()) {
|
||||
int mappedSource = blockMapping[incoming.getSource().getIndex()];
|
||||
incoming.setSource(program.basicBlockAt(mappedSource));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDetachedBlocks) {
|
||||
new UnreachableBasicBlockEliminator().optimize(program);
|
||||
}
|
||||
|
||||
return hasExceptionHandlers;
|
||||
}
|
||||
|
||||
private int contributeToBasicBlock(BasicBlock block) {
|
||||
int[] currentJointSources = new int[program.variableCount()];
|
||||
var jointReceiverMaps = new IntObjectHashMap<int[]>();
|
||||
Arrays.fill(currentJointSources, -1);
|
||||
|
||||
IntSet variablesDefinedHere = new IntHashSet();
|
||||
|
||||
for (var tryCatch : block.getTryCatchBlocks()) {
|
||||
int[] jointReceiverMap = new int[program.variableCount()];
|
||||
Arrays.fill(jointReceiverMap, -1);
|
||||
for (Phi phi : tryCatch.getHandler().getPhis()) {
|
||||
var sourceVariables = phi.getIncomings().stream()
|
||||
.filter(incoming -> incoming.getSource() == tryCatch.getProtectedBlock())
|
||||
.map(incoming -> incoming.getValue())
|
||||
.collect(Collectors.toList());
|
||||
if (sourceVariables.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Variable sourceVar : sourceVariables) {
|
||||
BasicBlock sourceVarDefinedAt = variableDefinitionPlaces[sourceVar.getIndex()];
|
||||
if (sourceVar.getIndex() < parameterCount
|
||||
|| (dom.dominates(sourceVarDefinedAt.getIndex(), block.getIndex())
|
||||
&& block != sourceVarDefinedAt)) {
|
||||
currentJointSources[phi.getReceiver().getIndex()] = sourceVar.getIndex();
|
||||
if (sourceVarDefinedAt != block) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Variable sourceVar : sourceVariables) {
|
||||
jointReceiverMap[sourceVar.getIndex()] = phi.getReceiver().getIndex();
|
||||
}
|
||||
}
|
||||
|
||||
jointReceiverMaps.put(tryCatch.getHandler().getIndex(), jointReceiverMap);
|
||||
}
|
||||
|
||||
for (Phi phi : block.getPhis()) {
|
||||
Variable definedVar = phi.getReceiver();
|
||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||
int jointReceiver = jointReceiverMaps.get(tryCatch.getHandler().getIndex())[definedVar.getIndex()];
|
||||
if (jointReceiver >= 0) {
|
||||
currentJointSources[jointReceiver] = definedVar.getIndex();
|
||||
}
|
||||
}
|
||||
variablesDefinedHere.add(definedVar.getIndex());
|
||||
}
|
||||
|
||||
var defExtractor = new DefinitionExtractor();
|
||||
List<BasicBlock> blocksToClearHandlers = new ArrayList<>();
|
||||
blocksToClearHandlers.add(block);
|
||||
BasicBlock initialBlock = block;
|
||||
|
||||
for (Instruction insn : block) {
|
||||
insn.acceptVisitor(defExtractor);
|
||||
for (Variable definedVar : defExtractor.getDefinedVariables()) {
|
||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||
int jointReceiver = jointReceiverMaps.get(tryCatch.getHandler().getIndex())[definedVar.getIndex()];
|
||||
if (jointReceiver >= 0) {
|
||||
currentJointSources[jointReceiver] = definedVar.getIndex();
|
||||
}
|
||||
}
|
||||
variablesDefinedHere.add(definedVar.getIndex());
|
||||
}
|
||||
|
||||
if (isCallInstruction(insn)) {
|
||||
BasicBlock next;
|
||||
boolean last = false;
|
||||
if (isSpecialCallInstruction(insn)) {
|
||||
next = null;
|
||||
while (insn.getNext() != null) {
|
||||
Instruction nextInsn = insn.getNext();
|
||||
nextInsn.delete();
|
||||
}
|
||||
hasDetachedBlocks = true;
|
||||
last = true;
|
||||
} else if (insn instanceof RaiseInstruction) {
|
||||
InvokeInstruction raise = new InvokeInstruction();
|
||||
raise.setMethod(new MethodReference(ExceptionHandling.class, "throwException", Throwable.class,
|
||||
void.class));
|
||||
raise.setType(InvocationType.SPECIAL);
|
||||
raise.setArguments(((RaiseInstruction) insn).getException());
|
||||
raise.setLocation(insn.getLocation());
|
||||
insn.replace(raise);
|
||||
insn = raise;
|
||||
next = null;
|
||||
} else if (insn.getNext() != null && insn.getNext() instanceof JumpInstruction) {
|
||||
next = ((JumpInstruction) insn.getNext()).getTarget();
|
||||
insn.getNext().delete();
|
||||
last = true;
|
||||
} else {
|
||||
next = program.createBasicBlock();
|
||||
next.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
||||
blocksToClearHandlers.add(next);
|
||||
|
||||
while (insn.getNext() != null) {
|
||||
Instruction nextInsn = insn.getNext();
|
||||
nextInsn.delete();
|
||||
next.add(nextInsn);
|
||||
}
|
||||
}
|
||||
|
||||
var locations = CallSiteLocation.fromTextLocation(insn.getLocation(), method);
|
||||
var callSite = new CallSiteDescriptor(callSiteIdGen++, locations);
|
||||
callSites.add(callSite);
|
||||
var pre = setLocation(getInstructionsBeforeCallSite(callSite), insn.getLocation());
|
||||
var post = getInstructionsAfterCallSite(initialBlock, block, next, callSite,
|
||||
currentJointSources, variablesDefinedHere);
|
||||
post = setLocation(post, insn.getLocation());
|
||||
block.getLastInstruction().insertPreviousAll(pre);
|
||||
block.addAll(post);
|
||||
hasExceptionHandlers = true;
|
||||
|
||||
if (next == null || last) {
|
||||
break;
|
||||
}
|
||||
block = next;
|
||||
variablesDefinedHere.clear();
|
||||
}
|
||||
}
|
||||
|
||||
removeOutgoingPhis(block);
|
||||
for (BasicBlock blockToClear : blocksToClearHandlers) {
|
||||
blockToClear.getTryCatchBlocks().clear();
|
||||
}
|
||||
|
||||
return block.getIndex();
|
||||
}
|
||||
|
||||
private boolean isCallInstruction(Instruction insn) {
|
||||
return isCallInstruction(characteristics, insn);
|
||||
}
|
||||
|
||||
public static boolean isCallInstruction(Characteristics characteristics, Instruction insn) {
|
||||
if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction
|
||||
|| insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|
||||
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction
|
||||
|| insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction
|
||||
|| insn instanceof NullCheckInstruction || insn instanceof BoundCheckInstruction
|
||||
|| insn instanceof CastInstruction) {
|
||||
return true;
|
||||
} else if (insn instanceof InvokeInstruction) {
|
||||
return isManagedMethodCall(characteristics, ((InvokeInstruction) insn).getMethod());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isManagedMethodCall(Characteristics characteristics, MethodReference method) {
|
||||
if (characteristics.isManaged(method) || method.equals(FILL_STACK_TRACE)) {
|
||||
return true;
|
||||
}
|
||||
return method.getClassName().equals(ExceptionHandling.class.getName())
|
||||
&& method.getName().startsWith("throw");
|
||||
}
|
||||
|
||||
private boolean isSpecialCallInstruction(Instruction insn) {
|
||||
if (!(insn instanceof InvokeInstruction)) {
|
||||
return false;
|
||||
}
|
||||
MethodReference method = ((InvokeInstruction) insn).getMethod();
|
||||
return method.getClassName().equals(ExceptionHandling.class.getName()) && method.getName().startsWith("throw");
|
||||
}
|
||||
|
||||
private List<Instruction> setLocation(List<Instruction> instructions, TextLocation location) {
|
||||
if (location != null) {
|
||||
for (Instruction instruction : instructions) {
|
||||
instruction.setLocation(location);
|
||||
}
|
||||
}
|
||||
return instructions;
|
||||
}
|
||||
|
||||
private List<Instruction> getInstructionsBeforeCallSite(CallSiteDescriptor callSite) {
|
||||
List<Instruction> instructions = new ArrayList<>();
|
||||
|
||||
Variable idVariable = program.createVariable();
|
||||
IntegerConstantInstruction idInsn = new IntegerConstantInstruction();
|
||||
idInsn.setConstant(callSite.getId());
|
||||
idInsn.setReceiver(idVariable);
|
||||
instructions.add(idInsn);
|
||||
|
||||
InvokeInstruction registerInsn = new InvokeInstruction();
|
||||
registerInsn.setMethod(new MethodReference(ShadowStack.class, "registerCallSite", int.class, void.class));
|
||||
registerInsn.setType(InvocationType.SPECIAL);
|
||||
registerInsn.setArguments(idVariable);
|
||||
instructions.add(registerInsn);
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
private List<Instruction> getInstructionsAfterCallSite(BasicBlock initialBlock, BasicBlock block, BasicBlock next,
|
||||
CallSiteDescriptor callSite, int[] currentJointSources, IntSet variablesDefinedHere) {
|
||||
Program program = block.getProgram();
|
||||
List<Instruction> instructions = new ArrayList<>();
|
||||
|
||||
Variable handlerIdVariable = program.createVariable();
|
||||
InvokeInstruction getHandlerIdInsn = new InvokeInstruction();
|
||||
getHandlerIdInsn.setMethod(new MethodReference(ShadowStack.class, "getExceptionHandlerId", int.class));
|
||||
getHandlerIdInsn.setType(InvocationType.SPECIAL);
|
||||
getHandlerIdInsn.setReceiver(handlerIdVariable);
|
||||
instructions.add(getHandlerIdInsn);
|
||||
|
||||
SwitchInstruction switchInsn = new SwitchInstruction();
|
||||
switchInsn.setCondition(handlerIdVariable);
|
||||
|
||||
if (next != null) {
|
||||
SwitchTableEntry continueExecutionEntry = new SwitchTableEntry();
|
||||
continueExecutionEntry.setCondition(callSite.getId());
|
||||
continueExecutionEntry.setTarget(next);
|
||||
switchInsn.getEntries().add(continueExecutionEntry);
|
||||
}
|
||||
|
||||
boolean defaultExists = false;
|
||||
int nextHandlerId = callSite.getId();
|
||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||
ExceptionHandlerDescriptor handler = new ExceptionHandlerDescriptor(++nextHandlerId,
|
||||
tryCatch.getExceptionType());
|
||||
callSite.getHandlers().add(handler);
|
||||
|
||||
if (tryCatch.getExceptionType() == null) {
|
||||
defaultExists = true;
|
||||
switchInsn.setDefaultTarget(tryCatch.getHandler());
|
||||
} else {
|
||||
SwitchTableEntry catchEntry = new SwitchTableEntry();
|
||||
catchEntry.setTarget(tryCatch.getHandler());
|
||||
catchEntry.setCondition(handler.getId());
|
||||
switchInsn.getEntries().add(catchEntry);
|
||||
}
|
||||
}
|
||||
fixOutgoingPhis(initialBlock, block, currentJointSources, variablesDefinedHere);
|
||||
|
||||
if (!defaultExists) {
|
||||
switchInsn.setDefaultTarget(getDefaultExceptionHandler());
|
||||
}
|
||||
|
||||
if (switchInsn.getEntries().isEmpty()) {
|
||||
instructions.clear();
|
||||
JumpInstruction jump = new JumpInstruction();
|
||||
jump.setTarget(switchInsn.getDefaultTarget());
|
||||
instructions.add(jump);
|
||||
} else if (switchInsn.getEntries().size() == 1) {
|
||||
SwitchTableEntry entry = switchInsn.getEntries().get(0);
|
||||
|
||||
IntegerConstantInstruction singleTestConstant = new IntegerConstantInstruction();
|
||||
singleTestConstant.setConstant(entry.getCondition());
|
||||
singleTestConstant.setReceiver(program.createVariable());
|
||||
instructions.add(singleTestConstant);
|
||||
|
||||
BinaryBranchingInstruction branching = new BinaryBranchingInstruction(BinaryBranchingCondition.EQUAL);
|
||||
branching.setConsequent(entry.getTarget());
|
||||
branching.setAlternative(switchInsn.getDefaultTarget());
|
||||
branching.setFirstOperand(switchInsn.getCondition());
|
||||
branching.setSecondOperand(singleTestConstant.getReceiver());
|
||||
instructions.add(branching);
|
||||
} else {
|
||||
instructions.add(switchInsn);
|
||||
}
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
private void fixOutgoingPhis(BasicBlock block, BasicBlock newBlock, int[] currentJointSources,
|
||||
IntSet variablesDefinedHere) {
|
||||
for (TryCatchBlock tryCatch : block.getTryCatchBlocks()) {
|
||||
for (Phi phi : tryCatch.getHandler().getPhis()) {
|
||||
int value = currentJointSources[phi.getReceiver().getIndex()];
|
||||
if (value < 0) {
|
||||
continue;
|
||||
}
|
||||
List<Incoming> additionalIncomings = new ArrayList<>();
|
||||
for (int i = 0; i < phi.getIncomings().size(); i++) {
|
||||
Incoming incoming = phi.getIncomings().get(i);
|
||||
if (incoming.getSource() != block) {
|
||||
continue;
|
||||
}
|
||||
if (incoming.getValue().getIndex() == value) {
|
||||
if (variablesDefinedHere.contains(value)) {
|
||||
incoming.setSource(newBlock);
|
||||
} else {
|
||||
Incoming incomingCopy = new Incoming();
|
||||
incomingCopy.setSource(newBlock);
|
||||
incomingCopy.setValue(incoming.getValue());
|
||||
additionalIncomings.add(incomingCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
phi.getIncomings().addAll(additionalIncomings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeOutgoingPhis(BasicBlock block) {
|
||||
for (var tryCatch : block.getTryCatchBlocks()) {
|
||||
for (var iterator = tryCatch.getHandler().getPhis().iterator(); iterator.hasNext();) {
|
||||
var phi = iterator.next();
|
||||
phi.getIncomings().removeIf(incoming -> incoming.getSource() == block);
|
||||
if (phi.getIncomings().isEmpty()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BasicBlock getDefaultExceptionHandler() {
|
||||
if (defaultExceptionHandler == null) {
|
||||
defaultExceptionHandler = program.createBasicBlock();
|
||||
Variable result = createReturnValueInstructions(defaultExceptionHandler);
|
||||
ExitInstruction exit = new ExitInstruction();
|
||||
exit.setValueToReturn(result);
|
||||
defaultExceptionHandler.add(exit);
|
||||
}
|
||||
return defaultExceptionHandler;
|
||||
}
|
||||
|
||||
private Variable createReturnValueInstructions(BasicBlock block) {
|
||||
ValueType returnType = method.getReturnType();
|
||||
if (returnType == ValueType.VOID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Variable variable = program.createVariable();
|
||||
|
||||
if (returnType instanceof ValueType.Primitive) {
|
||||
switch (((ValueType.Primitive) returnType).getKind()) {
|
||||
case BOOLEAN:
|
||||
case BYTE:
|
||||
case SHORT:
|
||||
case CHARACTER:
|
||||
case INTEGER:
|
||||
IntegerConstantInstruction intConstant = new IntegerConstantInstruction();
|
||||
intConstant.setReceiver(variable);
|
||||
block.add(intConstant);
|
||||
return variable;
|
||||
case LONG:
|
||||
LongConstantInstruction longConstant = new LongConstantInstruction();
|
||||
longConstant.setReceiver(variable);
|
||||
block.add(longConstant);
|
||||
return variable;
|
||||
case FLOAT:
|
||||
FloatConstantInstruction floatConstant = new FloatConstantInstruction();
|
||||
floatConstant.setReceiver(variable);
|
||||
block.add(floatConstant);
|
||||
return variable;
|
||||
case DOUBLE:
|
||||
DoubleConstantInstruction doubleConstant = new DoubleConstantInstruction();
|
||||
doubleConstant.setReceiver(variable);
|
||||
block.add(doubleConstant);
|
||||
return variable;
|
||||
}
|
||||
}
|
||||
|
||||
NullConstantInstruction nullConstant = new NullConstantInstruction();
|
||||
nullConstant.setReceiver(variable);
|
||||
block.add(nullConstant);
|
||||
|
||||
return variable;
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2016 Alexey Andreev.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.model.lowlevel;
|
||||
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.instructions.BoundCheckInstruction;
|
||||
import org.teavm.model.instructions.CastInstruction;
|
||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||
import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||
import org.teavm.model.instructions.ConstructInstruction;
|
||||
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
||||
import org.teavm.model.instructions.InitClassInstruction;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.MonitorEnterInstruction;
|
||||
import org.teavm.model.instructions.MonitorExitInstruction;
|
||||
import org.teavm.model.instructions.NullCheckInstruction;
|
||||
import org.teavm.model.instructions.RaiseInstruction;
|
||||
import org.teavm.runtime.ExceptionHandling;
|
||||
|
||||
public final class ExceptionHandlingUtil {
|
||||
private static final MethodReference FILL_STACK_TRACE = new MethodReference(ExceptionHandling.class,
|
||||
"fillStackTrace", StackTraceElement[].class);
|
||||
|
||||
private ExceptionHandlingUtil() {
|
||||
}
|
||||
|
||||
public static boolean isCallInstruction(Characteristics characteristics, Instruction insn) {
|
||||
if (insn instanceof InitClassInstruction || insn instanceof ConstructInstruction
|
||||
|| insn instanceof ConstructArrayInstruction || insn instanceof ConstructMultiArrayInstruction
|
||||
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction
|
||||
|| insn instanceof MonitorEnterInstruction || insn instanceof MonitorExitInstruction
|
||||
|| insn instanceof NullCheckInstruction || insn instanceof BoundCheckInstruction
|
||||
|| insn instanceof CastInstruction) {
|
||||
return true;
|
||||
} else if (insn instanceof InvokeInstruction) {
|
||||
return isManagedMethodCall(characteristics, ((InvokeInstruction) insn).getMethod());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isManagedMethodCall(Characteristics characteristics, MethodReference method) {
|
||||
if (characteristics.isManaged(method) || method.equals(FILL_STACK_TRACE)) {
|
||||
return true;
|
||||
}
|
||||
return method.getClassName().equals(ExceptionHandling.class.getName())
|
||||
&& method.getName().startsWith("throw");
|
||||
}
|
||||
}
|
@ -160,7 +160,7 @@ public class GCShadowStackContributor {
|
||||
for (Variable definedVar : defExtractor.getDefinedVariables()) {
|
||||
currentLiveOut.clear(definedVar.getIndex());
|
||||
}
|
||||
if (ExceptionHandlingShadowStackContributor.isCallInstruction(characteristics, insn)) {
|
||||
if (ExceptionHandlingUtil.isCallInstruction(characteristics, insn)) {
|
||||
BitSet csLiveIn = (BitSet) currentLiveOut.clone();
|
||||
for (int v = csLiveIn.nextSetBit(0); v >= 0; v = csLiveIn.nextSetBit(v + 1)) {
|
||||
if (!isReference(typeInferer, v) || nativePointers[v] || constants.get(v)) {
|
||||
|
@ -37,13 +37,10 @@ import org.teavm.runtime.ShadowStack;
|
||||
public class ShadowStackTransformer {
|
||||
private Characteristics characteristics;
|
||||
private GCShadowStackContributor gcContributor;
|
||||
private boolean exceptionHandling;
|
||||
private int callSiteIdGen;
|
||||
|
||||
public ShadowStackTransformer(Characteristics characteristics, boolean exceptionHandling) {
|
||||
public ShadowStackTransformer(Characteristics characteristics) {
|
||||
gcContributor = new GCShadowStackContributor(characteristics);
|
||||
this.characteristics = characteristics;
|
||||
this.exceptionHandling = exceptionHandling;
|
||||
}
|
||||
|
||||
public void apply(Program program, MethodReader method) {
|
||||
@ -52,27 +49,16 @@ public class ShadowStackTransformer {
|
||||
}
|
||||
|
||||
int shadowStackSize = gcContributor.contribute(program, method);
|
||||
boolean exceptions;
|
||||
if (exceptionHandling) {
|
||||
List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
var exceptionContributor = new ExceptionHandlingShadowStackContributor(characteristics, callSites,
|
||||
method.getReference(), program);
|
||||
exceptionContributor.callSiteIdGen = callSiteIdGen;
|
||||
exceptions = exceptionContributor.contribute();
|
||||
callSiteIdGen = exceptionContributor.callSiteIdGen;
|
||||
CallSiteDescriptor.save(callSites, program.getAnnotations());
|
||||
} else {
|
||||
exceptions = false;
|
||||
outer: for (BasicBlock block : program.getBasicBlocks()) {
|
||||
if (!block.getTryCatchBlocks().isEmpty()) {
|
||||
var exceptions = false;
|
||||
outer: for (BasicBlock block : program.getBasicBlocks()) {
|
||||
if (!block.getTryCatchBlocks().isEmpty()) {
|
||||
exceptions = true;
|
||||
break;
|
||||
}
|
||||
for (Instruction insn : block) {
|
||||
if (ExceptionHandlingUtil.isCallInstruction(characteristics, insn)) {
|
||||
exceptions = true;
|
||||
break;
|
||||
}
|
||||
for (Instruction insn : block) {
|
||||
if (ExceptionHandlingShadowStackContributor.isCallInstruction(characteristics, insn)) {
|
||||
exceptions = true;
|
||||
break outer;
|
||||
}
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,24 +117,26 @@ public final class ExceptionHandling {
|
||||
}
|
||||
|
||||
if (!isJumpSupported()) {
|
||||
ShadowStack.setExceptionHandlerId(stackFrame, callSiteId - 1);
|
||||
ShadowStack.setExceptionHandlerSkip(stackFrame);
|
||||
}
|
||||
}
|
||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||
}
|
||||
|
||||
if (stackFrame == null) {
|
||||
stackFrame = ShadowStack.getStackTop();
|
||||
while (stackFrame != null) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
if (callSiteId >= 0) {
|
||||
ShadowStack.setExceptionHandlerId(stackFrame, callSiteId + 1);
|
||||
if (!isJumpSupported()) {
|
||||
stackFrame = ShadowStack.getStackTop();
|
||||
while (stackFrame != null) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
if (callSiteId >= 0) {
|
||||
ShadowStack.setExceptionHandlerRestore(stackFrame);
|
||||
}
|
||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||
}
|
||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||
}
|
||||
printStack();
|
||||
abort();
|
||||
} else if (isJumpSupported()) {
|
||||
} else {
|
||||
jumpToFrame(stackFrame, handlerId);
|
||||
}
|
||||
}
|
||||
|
@ -48,4 +48,8 @@ public final class ShadowStack {
|
||||
public static native int getExceptionHandlerId();
|
||||
|
||||
public static native void setExceptionHandlerId(Address stackFrame, int id);
|
||||
|
||||
public static native void setExceptionHandlerSkip(Address stackFrame);
|
||||
|
||||
public static native void setExceptionHandlerRestore(Address stackFrame);
|
||||
}
|
||||
|
@ -155,8 +155,15 @@ int32_t wasi_snapshot_preview1_fd_write(int32_t fd, int32_t iovs, int32_t count,
|
||||
}
|
||||
|
||||
void teavm_putwcharsOut(int32_t chars, int32_t count) {
|
||||
int16_t* chars_array = (int16_t*) (wasm_heap + chars);
|
||||
char* chars_array = (char*) (wasm_heap + chars);
|
||||
for (int32_t i = 0; i < count; ++i) {
|
||||
putwchar(chars_array[i]);
|
||||
putc(chars_array[i], stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void teavm_putwcharsErr(int32_t chars, int32_t count) {
|
||||
char* chars_array = (char*) (wasm_heap + chars);
|
||||
for (int32_t i = 0; i < count; ++i) {
|
||||
putc(chars_array[i], stderr);
|
||||
}
|
||||
}
|
@ -58,11 +58,11 @@ TeaVM.wasm = function() {
|
||||
function dateToString(timestamp, controller) {
|
||||
const s = new Date(timestamp).toString();
|
||||
let instance = controller.instance;
|
||||
let result = instance.allocateString(s.length);
|
||||
let result = instance.exports.teavm_allocateString(s.length);
|
||||
if (result === 0) {
|
||||
return 0;
|
||||
}
|
||||
let resultAddress = instance.objectArrayData(instance.stringData(result));
|
||||
let resultAddress = instance.exports.teavm_objectArrayData(instance.exports.teavm_stringData(result));
|
||||
let resultView = new Uint16Array(instance.exports.memory.buffer, resultAddress, s.length);
|
||||
for (let i = 0; i < s.length; ++i) {
|
||||
resultView[i] = s.charCodeAt(i);
|
||||
|
@ -23,4 +23,5 @@ teavm.project.version=0.9.0-SNAPSHOT
|
||||
teavm.tests.decodeStack=false
|
||||
teavm.tests.optimized=true
|
||||
teavm.tests.minified=false
|
||||
teavm.tests.c=true
|
||||
teavm.tests.c=true
|
||||
teavm.tests.wasm=true
|
@ -74,6 +74,7 @@ public class PlatformPlugin implements TeaVMPlugin, MetadataRegistration {
|
||||
if (!isBootstrap()) {
|
||||
TeaVMWasmHost wasmHost = host.getExtension(TeaVMWasmHost.class);
|
||||
if (wasmHost != null) {
|
||||
host.add(new ResourceLowLevelTransformer());
|
||||
metadataGeneratorConsumers.add((constructor, method, generator) -> {
|
||||
wasmHost.add(ctx -> new MetadataIntrinsic(ctx.getClassSource(), ctx.getClassLoader(),
|
||||
ctx.getServices(), ctx.getProperties(), constructor, method, generator));
|
||||
@ -95,7 +96,7 @@ public class PlatformPlugin implements TeaVMPlugin, MetadataRegistration {
|
||||
|
||||
TeaVMCHost cHost = host.getExtension(TeaVMCHost.class);
|
||||
if (cHost != null) {
|
||||
host.add(new ResourceCTransformer());
|
||||
host.add(new ResourceLowLevelTransformer());
|
||||
MetadataCIntrinsic metadataCIntrinsic = new MetadataCIntrinsic();
|
||||
cHost.addGenerator(ctx -> {
|
||||
metadataCIntrinsic.init(ctx.getClassSource(), ctx.getClassLoader(),
|
||||
|
@ -21,7 +21,7 @@ import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.Program;
|
||||
|
||||
class ResourceCTransformer implements ClassHolderTransformer {
|
||||
class ResourceLowLevelTransformer implements ClassHolderTransformer {
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
@ -106,7 +106,7 @@ public class PipedInputStreamTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void available() throws Exception {
|
||||
pis = new PipedInputStream();
|
||||
pos = new PipedOutputStream();
|
||||
@ -150,7 +150,7 @@ public class PipedInputStreamTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void connectLjava_io_PipedOutputStream() throws Exception {
|
||||
pis = new PipedInputStream();
|
||||
pos = new PipedOutputStream();
|
||||
@ -168,7 +168,7 @@ public class PipedInputStreamTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void test_read() throws Exception {
|
||||
pis = new PipedInputStream();
|
||||
pos = new PipedOutputStream();
|
||||
@ -186,7 +186,7 @@ public class PipedInputStreamTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void test_read$BII() throws Exception {
|
||||
pis = new PipedInputStream();
|
||||
pos = new PipedOutputStream();
|
||||
@ -375,7 +375,7 @@ public class PipedInputStreamTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void read_after_write_close() throws Exception {
|
||||
PipedInputStream in = new PipedInputStream();
|
||||
PipedOutputStream out = new PipedOutputStream();
|
||||
|
@ -96,7 +96,7 @@ public class PipedOutputStreamTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void close() throws Exception {
|
||||
out = new PipedOutputStream();
|
||||
reader = new PReader(out);
|
||||
@ -132,7 +132,7 @@ public class PipedOutputStreamTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void flush() throws IOException {
|
||||
out = new PipedOutputStream();
|
||||
reader = new PReader(out);
|
||||
@ -145,7 +145,7 @@ public class PipedOutputStreamTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void write$BII() throws IOException {
|
||||
out = new PipedOutputStream();
|
||||
reader = new PReader(out);
|
||||
|
@ -32,7 +32,7 @@ import org.teavm.junit.TestPlatform;
|
||||
@EachTestCompiledSeparately
|
||||
public class ClassLoaderTest {
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void loadsResources() {
|
||||
assertEquals("q", loadResource("1"));
|
||||
assertEquals("qw", loadResource("2"));
|
||||
|
@ -31,7 +31,7 @@ import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public class ClassTest {
|
||||
@Test
|
||||
public void classNameEvaluated() {
|
||||
|
@ -30,7 +30,7 @@ import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@EachTestCompiledSeparately
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public class SerializedLambdaTest {
|
||||
@Test
|
||||
public void serializableLambdaHasWriteReplaceMethod() throws NoSuchMethodException, InvocationTargetException,
|
||||
|
@ -43,7 +43,7 @@ public class ArrayTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void setWorks() {
|
||||
Object array = Array.newInstance(String.class, 2);
|
||||
Array.set(array, 0, "foo");
|
||||
@ -52,7 +52,7 @@ public class ArrayTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void setPrimitiveWorks() {
|
||||
Object array = Array.newInstance(int.class, 2);
|
||||
Array.set(array, 0, 23);
|
||||
|
@ -32,7 +32,7 @@ import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@EachTestCompiledSeparately
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public class ConstructorTest {
|
||||
@Test
|
||||
public void constructorsEnumerated() {
|
||||
|
@ -29,7 +29,7 @@ import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@EachTestCompiledSeparately
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public class FieldTest {
|
||||
@Test
|
||||
public void fieldsEnumerated() {
|
||||
|
@ -31,7 +31,7 @@ import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@EachTestCompiledSeparately
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public class MethodTest {
|
||||
@Test
|
||||
public void methodsEnumerated() {
|
||||
|
@ -534,8 +534,7 @@ public class URLTest {
|
||||
u.toString());
|
||||
assertEquals("b) Does not return the right url string",
|
||||
"http://www.yahoo2.com:9999", u1.toString());
|
||||
assertTrue("c) Does not return the right url string", u
|
||||
.equals(new URL(u.toString())));
|
||||
assertEquals("c) Does not return the right url string", u, new URL(u.toString()));
|
||||
} catch (Exception e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ public class TestDateTimesImplementation {
|
||||
}
|
||||
|
||||
@Test(dataProvider = "safeMultiplyLongLongProviderOverflow", expectedExceptions = ArithmeticException.class)
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void test_safeMultiplyLongLong_overflow(long a, long b) {
|
||||
Jdk8Methods.safeMultiply(a, b);
|
||||
}
|
||||
|
@ -944,7 +944,7 @@ public class TestLocalDate extends AbstractDateTimeTest {
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void test_plus_longPeriodUnit_null() {
|
||||
test2007x07x15.plus(1, (TemporalUnit) null);
|
||||
}
|
||||
@ -1305,7 +1305,7 @@ public class TestLocalDate extends AbstractDateTimeTest {
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void test_minus_longPeriodUnit_null() {
|
||||
test2007x07x15.minus(1, (TemporalUnit) null);
|
||||
}
|
||||
|
@ -1261,7 +1261,7 @@ public class TestLocalDateTime extends AbstractDateTimeTest {
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void test_plus_longPeriodUnit_null() {
|
||||
test2007x07x15x12x30x40x987654321.plus(1, null);
|
||||
}
|
||||
@ -1972,7 +1972,7 @@ public class TestLocalDateTime extends AbstractDateTimeTest {
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void test_minus_longPeriodUnit_null() {
|
||||
test2007x07x15x12x30x40x987654321.minus(1, null);
|
||||
}
|
||||
|
@ -668,7 +668,7 @@ public class TestOffsetTime extends AbstractDateTimeTest {
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void test_with_TemporalField_null() {
|
||||
test11x30x59x500pone.with((TemporalField) null, 0);
|
||||
}
|
||||
|
@ -1456,7 +1456,7 @@ public class TestZonedDateTime extends AbstractDateTimeTest {
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void test_plus_longUnit_null() {
|
||||
testDateTimeParis.plus(0, null);
|
||||
}
|
||||
|
@ -710,7 +710,7 @@ public class TestYear extends AbstractDateTimeTest {
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void test_compareTo_nullYear() {
|
||||
Year doy = null;
|
||||
Year test = Year.of(1);
|
||||
|
@ -20,13 +20,16 @@ import java.util.Date;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.EachTestCompiledSeparately;
|
||||
import org.teavm.junit.SkipPlatform;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@EachTestCompiledSeparately
|
||||
public class DateTest {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.WEBASSEMBLY)
|
||||
public void setsDateAndMonth() {
|
||||
Date date = new Date();
|
||||
date.setMonth(0);
|
||||
@ -39,6 +42,7 @@ public class DateTest {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.WEBASSEMBLY)
|
||||
public void setsUTC() {
|
||||
long epochTime = Date.UTC(2023, 1, 20, 10, 0, 0);
|
||||
Date date = new Date(epochTime);
|
||||
|
@ -31,7 +31,9 @@ import java.util.MissingFormatWidthException;
|
||||
import java.util.UnknownFormatConversionException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.SkipPlatform;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
public class FormatterTest {
|
||||
@ -108,6 +110,7 @@ public class FormatterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.WEBASSEMBLY)
|
||||
public void formatsChar() {
|
||||
assertEquals("x: Y:\uDBFF\uDFFF ", new Formatter().format("%c:%3C:%-3c", 'x', 'y', 0x10ffff).toString());
|
||||
|
||||
|
@ -30,7 +30,9 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.EachTestCompiledSeparately;
|
||||
import org.teavm.junit.SkipJVM;
|
||||
import org.teavm.junit.SkipPlatform;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
import org.teavm.junit.TestPlatform;
|
||||
import org.teavm.metaprogramming.CompileTime;
|
||||
import org.teavm.metaprogramming.Meta;
|
||||
import org.teavm.metaprogramming.ReflectClass;
|
||||
@ -482,6 +484,7 @@ public class MetaprogrammingTest {
|
||||
private static int counter;
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.WEBASSEMBLY)
|
||||
public void arrayTypeSelected() {
|
||||
assertEquals(String[].class, createInstance(String.class, 1).getClass());
|
||||
assertEquals(String[][].class, createInstance(String[].class, 1).getClass());
|
||||
|
@ -108,14 +108,17 @@ public class VMTest {
|
||||
|
||||
@Test
|
||||
public void catchesException() {
|
||||
var wasCaught = false;
|
||||
try {
|
||||
throw new IllegalArgumentException();
|
||||
} catch (IllegalArgumentException e) {
|
||||
// do nothing
|
||||
wasCaught = true;
|
||||
}
|
||||
assertTrue(wasCaught);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.WEBASSEMBLY)
|
||||
public void setsVariableBeforeTryCatch() {
|
||||
int a = 23;
|
||||
try {
|
||||
@ -255,7 +258,7 @@ public class VMTest {
|
||||
|
||||
@Test
|
||||
@SkipJVM
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void asyncClinit() {
|
||||
assertEquals(0, initCount);
|
||||
assertEquals("foo", AsyncClinitClass.foo());
|
||||
@ -267,13 +270,13 @@ public class VMTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void asyncClinitField() {
|
||||
assertEquals("ok", AsyncClinitClass.state);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void asyncClinitInstance() {
|
||||
AsyncClinitClass acl = new AsyncClinitClass();
|
||||
assertEquals("ok", AsyncClinitClass.state);
|
||||
@ -281,7 +284,7 @@ public class VMTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void asyncWait() {
|
||||
AsyncClinitClass acl = new AsyncClinitClass();
|
||||
acl.doWait();
|
||||
@ -290,7 +293,7 @@ public class VMTest {
|
||||
|
||||
@Test
|
||||
@SkipJVM
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void loopAndExceptionPhi() {
|
||||
int[] a = createArray();
|
||||
int s = 0;
|
||||
@ -309,7 +312,7 @@ public class VMTest {
|
||||
|
||||
@Test
|
||||
@SkipJVM
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void asyncTryCatch() {
|
||||
try {
|
||||
throwExceptionAsync();
|
||||
@ -321,7 +324,7 @@ public class VMTest {
|
||||
|
||||
@Test
|
||||
@SkipJVM
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void asyncExceptionHandler() {
|
||||
try {
|
||||
throw new RuntimeException("OK");
|
||||
@ -527,7 +530,7 @@ public class VMTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void indirectDefaultMethod() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (FirstPath o : new FirstPath[] { new PathJoint(), new FirstPathOptimizationPrevention() }) {
|
||||
@ -537,7 +540,7 @@ public class VMTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SkipPlatform(TestPlatform.C)
|
||||
@SkipPlatform({TestPlatform.C, TestPlatform.WEBASSEMBLY})
|
||||
public void indirectDefaultMethodSubclass() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (FirstPath o : new FirstPath[] { new PathJointSubclass(), new FirstPathOptimizationPrevention() }) {
|
||||
|
@ -65,7 +65,6 @@ public class IncrementalCBuilder {
|
||||
private String[] classPath;
|
||||
private int minHeapSize = 4;
|
||||
private int maxHeapSize = 128;
|
||||
private boolean longjmpSupported = true;
|
||||
private boolean lineNumbersGenerated;
|
||||
private String targetPath;
|
||||
private String externalTool;
|
||||
@ -139,10 +138,6 @@ public class IncrementalCBuilder {
|
||||
this.mainFunctionName = mainFunctionName;
|
||||
}
|
||||
|
||||
public void setLongjmpSupported(boolean longjmpSupported) {
|
||||
this.longjmpSupported = longjmpSupported;
|
||||
}
|
||||
|
||||
public void addProgressHandler(ProgressHandler handler) {
|
||||
synchronized (progressHandlers) {
|
||||
progressHandlers.add(handler);
|
||||
@ -342,7 +337,6 @@ public class IncrementalCBuilder {
|
||||
cTarget.setMinHeapSize(minHeapSize * 1024 * 1024);
|
||||
cTarget.setMaxHeapSize(maxHeapSize * 1024 * 1024);
|
||||
cTarget.setLineNumbersGenerated(lineNumbersGenerated);
|
||||
cTarget.setLongjmpUsed(longjmpSupported);
|
||||
cTarget.setHeapDump(true);
|
||||
vm.setOptimizationLevel(TeaVMOptimizationLevel.SIMPLE);
|
||||
vm.setCacheStatus(classSource);
|
||||
|
@ -67,10 +67,6 @@ public class TeaVMCBuilderRunner {
|
||||
.hasArg()
|
||||
.desc("Minimum heap size in megabytes")
|
||||
.build());
|
||||
options.addOption(Option.builder()
|
||||
.longOpt("no-longjmp")
|
||||
.desc("Don't use setjmp/longjmp functions to emulate exception handling")
|
||||
.build());
|
||||
options.addOption(Option.builder("e")
|
||||
.longOpt("entry-point")
|
||||
.argName("name")
|
||||
@ -126,9 +122,6 @@ public class TeaVMCBuilderRunner {
|
||||
if (commandLine.hasOption('e')) {
|
||||
builder.setMainFunctionName(commandLine.getOptionValue('e'));
|
||||
}
|
||||
if (commandLine.hasOption("no-longjmp")) {
|
||||
builder.setLongjmpSupported(false);
|
||||
}
|
||||
|
||||
String[] args = commandLine.getArgs();
|
||||
if (args.length != 1) {
|
||||
|
@ -151,10 +151,6 @@ public final class TeaVMRunner {
|
||||
.desc("Maximum number of names kept in top-level scope ("
|
||||
+ "other will be put in a separate object. 10000 by default.")
|
||||
.build());
|
||||
options.addOption(Option.builder()
|
||||
.longOpt("no-longjmp")
|
||||
.desc("Don't use setjmp/longjmp functions to emulate exceptions (C target)")
|
||||
.build());
|
||||
}
|
||||
|
||||
private TeaVMRunner(CommandLine commandLine) {
|
||||
@ -335,9 +331,6 @@ public final class TeaVMRunner {
|
||||
}
|
||||
|
||||
private void parseCOptions() {
|
||||
if (commandLine.hasOption("no-longjmp")) {
|
||||
tool.setLongjmpSupported(false);
|
||||
}
|
||||
if (commandLine.hasOption("heap-dump")) {
|
||||
tool.setHeapDump(true);
|
||||
}
|
||||
|
@ -110,7 +110,6 @@ public class TeaVMTool {
|
||||
private int minHeapSize = 4 * (1 << 20);
|
||||
private int maxHeapSize = 128 * (1 << 20);
|
||||
private ReferenceCache referenceCache;
|
||||
private boolean longjmpSupported = true;
|
||||
private boolean heapDump;
|
||||
private boolean shortFileNames;
|
||||
private boolean assertionsRemoved;
|
||||
@ -259,10 +258,6 @@ public class TeaVMTool {
|
||||
this.wasmVersion = wasmVersion;
|
||||
}
|
||||
|
||||
public void setLongjmpSupported(boolean longjmpSupported) {
|
||||
this.longjmpSupported = longjmpSupported;
|
||||
}
|
||||
|
||||
public void setHeapDump(boolean heapDump) {
|
||||
this.heapDump = heapDump;
|
||||
}
|
||||
@ -367,7 +362,6 @@ public class TeaVMTool {
|
||||
cTarget.setMinHeapSize(minHeapSize);
|
||||
cTarget.setMaxHeapSize(maxHeapSize);
|
||||
cTarget.setLineNumbersGenerated(debugInformationGenerated);
|
||||
cTarget.setLongjmpUsed(longjmpSupported);
|
||||
cTarget.setHeapDump(heapDump);
|
||||
cTarget.setObfuscated(obfuscated);
|
||||
cTarget.setFileNames(shortFileNames
|
||||
|
@ -78,8 +78,6 @@ public interface BuildStrategy {
|
||||
|
||||
void setMaxHeapSize(int maxHeapSize);
|
||||
|
||||
void setLongjmpSupported(boolean value);
|
||||
|
||||
void setHeapDump(boolean heapDump);
|
||||
|
||||
void setShortFileNames(boolean shortFileNames);
|
||||
|
@ -63,7 +63,6 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
||||
private int minHeapSize = 4 * 1024 * 1204;
|
||||
private int maxHeapSize = 128 * 1024 * 1024;
|
||||
private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>();
|
||||
private boolean longjmpSupported = true;
|
||||
private boolean heapDump;
|
||||
private TeaVMProgressListener progressListener;
|
||||
private Properties properties = new Properties();
|
||||
@ -208,11 +207,6 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
||||
this.maxHeapSize = maxHeapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLongjmpSupported(boolean longjmpSupported) {
|
||||
this.longjmpSupported = longjmpSupported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeapDump(boolean heapDump) {
|
||||
this.heapDump = heapDump;
|
||||
@ -257,7 +251,6 @@ public class InProcessBuildStrategy implements BuildStrategy {
|
||||
tool.setWasmVersion(wasmVersion);
|
||||
tool.setMinHeapSize(minHeapSize);
|
||||
tool.setMaxHeapSize(maxHeapSize);
|
||||
tool.setLongjmpSupported(longjmpSupported);
|
||||
tool.setHeapDump(heapDump);
|
||||
tool.setShortFileNames(shortFileNames);
|
||||
tool.setAssertionsRemoved(assertionsRemoved);
|
||||
|
@ -51,7 +51,6 @@ public class RemoteBuildStrategy implements BuildStrategy {
|
||||
request = new RemoteBuildRequest();
|
||||
request.optimizationLevel = TeaVMOptimizationLevel.ADVANCED;
|
||||
request.wasmVersion = WasmBinaryVersion.V_0x1;
|
||||
request.longjmpSupported = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -185,11 +184,6 @@ public class RemoteBuildStrategy implements BuildStrategy {
|
||||
request.maxHeapSize = maxHeapSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLongjmpSupported(boolean value) {
|
||||
request.longjmpSupported = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHeapDump(boolean heapDump) {
|
||||
request.heapDump = heapDump;
|
||||
|
@ -160,7 +160,6 @@ public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildServi
|
||||
tool.setWasmVersion(request.wasmVersion);
|
||||
tool.setMinHeapSize(request.minHeapSize);
|
||||
tool.setMaxHeapSize(request.maxHeapSize);
|
||||
tool.setLongjmpSupported(request.longjmpSupported);
|
||||
tool.setHeapDump(request.heapDump);
|
||||
tool.setShortFileNames(request.shortFileNames);
|
||||
tool.setAssertionsRemoved(request.assertionsRemoved);
|
||||
|
@ -48,7 +48,6 @@ public class RemoteBuildRequest implements Serializable {
|
||||
public WasmBinaryVersion wasmVersion;
|
||||
public int minHeapSize;
|
||||
public int maxHeapSize;
|
||||
public boolean longjmpSupported;
|
||||
public boolean heapDump;
|
||||
public boolean shortFileNames;
|
||||
public boolean assertionsRemoved;
|
||||
|
@ -151,9 +151,6 @@ public class TeaVMCompileMojo extends AbstractMojo {
|
||||
@Parameter(property = "teavm.processMemory", defaultValue = "512")
|
||||
private int processMemory;
|
||||
|
||||
@Parameter(property = "teavm.longjmpSupported", defaultValue = "true")
|
||||
private boolean longjmpSupported;
|
||||
|
||||
@Parameter(property = "teavm.heapDump", defaultValue = "false")
|
||||
private boolean heapDump;
|
||||
|
||||
@ -298,7 +295,6 @@ public class TeaVMCompileMojo extends AbstractMojo {
|
||||
builder.setCacheDirectory(cacheDirectory.getAbsolutePath());
|
||||
builder.setTargetType(targetType);
|
||||
builder.setWasmVersion(wasmVersion);
|
||||
builder.setLongjmpSupported(longjmpSupported);
|
||||
builder.setHeapDump(heapDump);
|
||||
BuildResult result;
|
||||
result = builder.build();
|
||||
|
Loading…
Reference in New Issue
Block a user