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:
Alexey Andreev 2023-10-03 21:25:50 +02:00
parent d24da7eded
commit 3d65d38375
90 changed files with 1265 additions and 1209 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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");
}
popLocation(statement.getLocation());
}

View File

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

View File

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

View File

@ -57,7 +57,5 @@ public interface GeneratorContext {
void importMethod(MethodReference method, boolean isStatic);
boolean usesLongjmp();
CallSiteDescriptor createCallSite(CallSiteLocation[] locations, ExceptionHandlerDescriptor[] exceptionHandlers);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,6 +35,8 @@ public class ShadowStackIntrinsic implements WasmIntrinsic {
case "getStackRootPointer":
case "getCallSiteId":
case "setExceptionHandlerId":
case "setExceptionHandlerSkip":
case "setExceptionHandlerRestore":
return true;
default:
return false;

View File

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

View File

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

View File

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

View File

@ -47,4 +47,9 @@ public class WasmBreak extends WasmExpression {
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);
}
@Override
public boolean isTerminating() {
return true;
}
}

View File

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

View File

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

View File

@ -16,6 +16,7 @@
package org.teavm.backend.wasm.model.expression;
public enum WasmIntUnaryOperation {
EQZ,
CLZ,
CTZ,
POPCNT

View File

@ -38,4 +38,9 @@ public class WasmReturn extends WasmExpression {
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);
}
@Override
public boolean isTerminating() {
return true;
}
}

View File

@ -55,4 +55,9 @@ public class WasmSwitch extends WasmExpression {
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);
}
@Override
public boolean isTerminating() {
return true;
}
}

View File

@ -23,4 +23,9 @@ public class WasmUnreachable extends WasmExpression {
public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this);
}
@Override
public boolean isTerminating() {
return true;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,30 +49,19 @@ 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;
var exceptions = false;
outer: for (BasicBlock block : program.getBasicBlocks()) {
if (!block.getTryCatchBlocks().isEmpty()) {
exceptions = true;
break;
}
for (Instruction insn : block) {
if (ExceptionHandlingShadowStackContributor.isCallInstruction(characteristics, insn)) {
if (ExceptionHandlingUtil.isCallInstruction(characteristics, insn)) {
exceptions = true;
break outer;
}
}
}
}
if (shadowStackSize > 0 || exceptions) {
addStackAllocation(program, shadowStackSize);

View File

@ -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) {
if (!isJumpSupported()) {
stackFrame = ShadowStack.getStackTop();
while (stackFrame != null) {
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
if (callSiteId >= 0) {
ShadowStack.setExceptionHandlerId(stackFrame, callSiteId + 1);
ShadowStack.setExceptionHandlerRestore(stackFrame);
}
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
}
}
printStack();
abort();
} else if (isJumpSupported()) {
} else {
jumpToFrame(stackFrame, handlerId);
}
}

View File

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

View File

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

View File

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

View File

@ -24,3 +24,4 @@ teavm.tests.decodeStack=false
teavm.tests.optimized=true
teavm.tests.minified=false
teavm.tests.c=true
teavm.tests.wasm=true

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -78,8 +78,6 @@ public interface BuildStrategy {
void setMaxHeapSize(int maxHeapSize);
void setLongjmpSupported(boolean value);
void setHeapDump(boolean heapDump);
void setShortFileNames(boolean shortFileNames);

View File

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

View File

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

View File

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

View File

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

View File

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