Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion aot-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,9 @@
<wast>utf8-import-module.wast</wast>
<wast>utf8-invalid-encoding.wast</wast>
</includedWasts>
<!-- Skip Binary as there is a THROW_REF in a Global initializer -->
<!-- Skip stack-overflows in tail calls -->
<excludedTests>SpecV1TcReturnCallTest.test19,SpecV1TcReturnCallTest.test24,SpecV1TcReturnCallTest.test25,SpecV1TcReturnCallTest.test30,SpecV1TcReturnCallTest.test31,SpecV1TcReturnCallIndirectTest.test40,SpecV1TcReturnCallIndirectTest.test41,SpecV1TcReturnCallIndirectTest.test46,SpecV1TcReturnCallIndirectTest.test47</excludedTests>
<excludedTests>SpecV1BinaryTest.test40,SpecV1TcReturnCallTest.test19,SpecV1TcReturnCallTest.test24,SpecV1TcReturnCallTest.test25,SpecV1TcReturnCallTest.test30,SpecV1TcReturnCallTest.test31,SpecV1TcReturnCallIndirectTest.test40,SpecV1TcReturnCallIndirectTest.test41,SpecV1TcReturnCallIndirectTest.test46,SpecV1TcReturnCallIndirectTest.test47</excludedTests>
<excludedMalformedWasts/>
<excludedInvalidWasts/>
<excludedUnlinkableWasts/>
Expand Down
16 changes: 14 additions & 2 deletions runtime-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@
<wast>proposals/exception-handling/binary.wast</wast>
<wast>proposals/exception-handling/exports.wast</wast>
<wast>proposals/exception-handling/imports.wast</wast>
<wast>proposals/exception-handling/ref_null.wast</wast>
<wast>proposals/exception-handling/tag.wast</wast>
<wast>proposals/exception-handling/throw.wast</wast>
<wast>proposals/exception-handling/throw_ref.wast</wast>
<wast>proposals/exception-handling/try_table.wast</wast>
<wast>proposals/tail-call/return_call.wast</wast>
<wast>proposals/tail-call/return_call_indirect.wast</wast>
<wast>ref_func.wast</wast>
Expand Down Expand Up @@ -149,7 +153,9 @@
<wast>utf8-import-module.wast</wast>
<wast>utf8-invalid-encoding.wast</wast>
</includedWasts>
<excludedTests/>
<!-- This test is supposed to fail because there is a THROW_REF in a Global initializer -->
<!-- it's a different error with EH -->
<excludedTests>SpecV1BinaryTest.test40,SpecV1EhBinaryTest.test40</excludedTests>
<excludedMalformedWasts/>
<excludedInvalidWasts/>
<excludedUnlinkableWasts/>
Expand Down Expand Up @@ -328,7 +334,11 @@
<wast>proposals/exception-handling/binary.wast</wast>
<wast>proposals/exception-handling/exports.wast</wast>
<wast>proposals/exception-handling/imports.wast</wast>
<wast>proposals/exception-handling/ref_null.wast</wast>
<wast>proposals/exception-handling/tag.wast</wast>
<wast>proposals/exception-handling/throw.wast</wast>
<wast>proposals/exception-handling/throw_ref.wast</wast>
<wast>proposals/exception-handling/try_table.wast</wast>
<wast>proposals/tail-call/return_call.wast</wast>
<wast>proposals/tail-call/return_call_indirect.wast</wast>
<wast>ref_func.wast</wast>
Expand Down Expand Up @@ -419,7 +429,9 @@
<wast>utf8-import-module.wast</wast>
<wast>utf8-invalid-encoding.wast</wast>
</includedWasts>
<excludedTests/>
<!-- This test is supposed to fail because there is a THROW_REF in a Global initializer -->
<!-- it's a different error with EH -->
<excludedTests>SpecV1BinaryTest.test40,SpecV1EhBinaryTest.test40</excludedTests>
<excludedMalformedWasts/>
<excludedInvalidWasts/>
<excludedUnlinkableWasts/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@ final class CtrlFrame {
// the height of the stack before entering the current Control Flow instruction
public final int height;

// the program counter of a TRY_TABLE block
// TODO: do we have a better way of doing this?
public final int pc;

public CtrlFrame(OpCode opCode, int startValues, int endValues, int height) {
this(opCode, startValues, endValues, height, 0);
}

public CtrlFrame(OpCode opCode, int startValues, int endValues, int height, int pc) {
this.opCode = opCode;
this.startValues = startValues;
this.endValues = endValues;
this.height = height;
this.pc = pc;
}
}
17 changes: 17 additions & 0 deletions runtime/src/main/java/com/dylibso/chicory/runtime/Instance.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public class Instance {
private final ExecutionListener listener;
private final Exports fluentExports;

private final Map<Integer, WasmException> exnRefs;

Instance(
WasmModule module,
Global[] globalInitializers,
Expand Down Expand Up @@ -106,6 +108,8 @@ public class Instance {
this.listener = listener;
this.fluentExports = new Exports(this);

this.exnRefs = new HashMap<>();

if (initialize) {
initialize(start);
}
Expand Down Expand Up @@ -317,6 +321,19 @@ public TagInstance tag(int idx) {
return tags[idx - imports.tagCount()];
}

public int tagCount() {
return tags.length;
}

public int registerException(WasmException ex) {
exnRefs.put(ex.tagIdx(), ex);
return ex.tagIdx();
}

public WasmException exn(int idx) {
return exnRefs.get(idx);
}

public Machine getMachine() {
return machine;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.dylibso.chicory.wasm.ChicoryException;
import com.dylibso.chicory.wasm.types.AnnotatedInstruction;
import com.dylibso.chicory.wasm.types.CatchOpCode;
import com.dylibso.chicory.wasm.types.FunctionType;
import com.dylibso.chicory.wasm.types.Instruction;
import com.dylibso.chicory.wasm.types.OpCode;
Expand Down Expand Up @@ -96,13 +97,18 @@ private long[] call(
callStack.push(stackFrame);

var imprt = instance.imports().function(funcId);
var results = imprt.handle().apply(instance, args);
// a host function can return null or an array of ints
// which we will push onto the stack
if (results != null) {
for (var result : results) {
stack.push(result);

try {
var results = imprt.handle().apply(instance, args);
// a host function can return null or an array of ints
// which we will push onto the stack
if (results != null) {
for (var result : results) {
stack.push(result);
}
}
} catch (WasmException e) {
THROW_REF(instance, instance.registerException(e), stack, stackFrame, callStack);
}
}

Expand Down Expand Up @@ -162,6 +168,9 @@ protected void eval(MStack stack, Instance instance, Deque<StackFrame> callStack
case BLOCK:
BLOCK(frame, stack, instance, instruction);
break;
case TRY_TABLE:
TRY_TABLE(frame, stack, instance, instruction, frame.currentPc());
break;
case IF:
IF(frame, stack, instance, instruction);
break;
Expand Down Expand Up @@ -207,6 +216,24 @@ protected void eval(MStack stack, Instance instance, Deque<StackFrame> callStack
// swap in place the current frame
frame = RETURN_CALL_INDIRECT(stack, instance, callStack, operands, frame);
break;
case THROW:
{
int tagNumber = (int) operands.get(0);
var tag = instance.tag(tagNumber);
var type = instance.type(tag.tagType().typeIdx());

var args = extractArgsForParams(stack, type.params());
var exception = new WasmException(instance, tagNumber, args);
var exceptionIdx = instance.registerException(exception);
frame = THROW_REF(instance, exceptionIdx, stack, frame, callStack);
break;
}
case THROW_REF:
{
var exceptionIdx = (int) stack.pop();
frame = THROW_REF(instance, exceptionIdx, stack, frame, callStack);
break;
}
case CALL_INDIRECT:
CALL_INDIRECT(stack, instance, callStack, operands);
break;
Expand Down Expand Up @@ -2035,13 +2062,112 @@ private static int numberOfValuesToReturn(Instance instance, AnnotatedInstructio
return sizeOf(instance.type(typeId).returns());
}

private static StackFrame THROW_REF(
Instance instance,
int exceptionIdx,
MStack stack,
StackFrame frame,
Deque<StackFrame> callStack) {
var exception = instance.exn(exceptionIdx);
boolean found = false;
while (!found) {
while (frame.ctrlStackSize() > 0) {
var ctrlFrame = frame.popCtrl();
if (ctrlFrame.opCode != OpCode.TRY_TABLE) {
continue;
}

frame.jumpTo(ctrlFrame.pc);
var tryInst = frame.loadCurrentInstruction();

var catches = CatchOpCode.decode(tryInst.operands());
for (int i = 0; i < catches.size() && !found; i++) {
var currentCatch = catches.get(i);

// verify import compatibility
var compatibleImport = false;
if ((currentCatch.opcode() == CatchOpCode.CATCH
|| currentCatch.opcode() == CatchOpCode.CATCH_REF)) {
var currentCatchTag = instance.tag(currentCatch.tag());
var exceptionTag = exception.instance().tag(exception.tagIdx());

// if it's an import we verify the compatibility
Comment thread
andreaTP marked this conversation as resolved.
if (currentCatch.tag() < instance.imports().tagCount()
&& currentCatchTag.type().paramsMatch(exceptionTag.type())
&& currentCatchTag.type().returnsMatch(exceptionTag.type())) {
compatibleImport = true;
} else if (exceptionTag != currentCatchTag) {
// if it's not an import the tag should be the same
continue;
}
}

switch (currentCatch.opcode()) {
case CATCH:
if (currentCatch.tag() == exception.tagIdx() || compatibleImport) {
found = true;
for (var arg : exception.args()) {
stack.push(arg);
}
}
break;
case CATCH_REF:
if (currentCatch.tag() == exception.tagIdx() || compatibleImport) {
found = true;
for (var arg : exception.args()) {
stack.push(arg);
}
stack.push(exceptionIdx);
}
break;
case CATCH_ALL:
found = true;
break;
case CATCH_ALL_REF:
found = true;
stack.push(exceptionIdx);
break;
}

if (found) {
var resolvedLabel = tryInst.labelTable().get(i);
// BR l
ctrlJump(frame, stack, currentCatch.label());
frame.jumpTo(resolvedLabel);
return frame;
}
}
}
if (!found) {
if (callStack.isEmpty()) {
throw exception;
} else {
frame = callStack.pop();
}
}
}
throw new RuntimeException("unreacheable");
}

private static void BLOCK(
StackFrame frame, MStack stack, Instance instance, AnnotatedInstruction instruction) {
var paramsSize = numberOfParams(instance, instruction);
var returnsSize = numberOfValuesToReturn(instance, instruction);
frame.pushCtrl(instruction.opcode(), paramsSize, returnsSize, stack.size() - paramsSize);
}

private static void TRY_TABLE(
StackFrame frame,
MStack stack,
Instance instance,
AnnotatedInstruction instruction,
int pc) {
var paramsSize = numberOfParams(instance, instruction);
var returnsSize = numberOfValuesToReturn(instance, instruction);
frame.pushCtrl(
instruction.opcode(), paramsSize, returnsSize, stack.size() - paramsSize, pc);
}

private static void IF(
StackFrame frame, MStack stack, Instance instance, AnnotatedInstruction instruction) {
var predValue = stack.pop();
Expand Down
12 changes: 12 additions & 0 deletions runtime/src/main/java/com/dylibso/chicory/runtime/StackFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ AnnotatedInstruction loadCurrentInstruction() {
return currentInstruction;
}

int currentPc() {
return pc - 1;
}

boolean isLastBlock() {
return currentInstruction.depth() == 0;
}
Expand All @@ -152,6 +156,14 @@ void pushCtrl(OpCode opcode, int startValues, int returnValues, int height) {
ctrlStack.add(new CtrlFrame(opcode, startValues, returnValues, height));
}

void pushCtrl(OpCode opcode, int startValues, int returnValues, int height, int pc) {
ctrlStack.add(new CtrlFrame(opcode, startValues, returnValues, height, pc));
}

int ctrlStackSize() {
return ctrlStack.size();
}

CtrlFrame popCtrl() {
var ctrlFrame = ctrlStack.remove(ctrlStack.size() - 1);
return ctrlFrame;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.dylibso.chicory.runtime;

public class WasmException extends RuntimeException {
private final int tagIdx;
private final long[] args;
private final Instance instance;

public WasmException(Instance instance, int tagIdx, long[] args) {
this.instance = instance;
this.tagIdx = tagIdx;
this.args = args.clone();
this.setStackTrace(new StackTraceElement[0]);
}

public Instance instance() {
return instance;
}

public int tagIdx() {
return tagIdx;
}

public long[] args() {
return args;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.dylibso.chicory.wasm.Parser;
import com.dylibso.chicory.wasm.UninstantiableException;
import com.dylibso.chicory.wasm.WasmModule;
import com.dylibso.chicory.wasm.types.CatchOpCode;
import com.dylibso.chicory.wasm.types.MemoryLimits;
import com.dylibso.chicory.wasm.types.Table;
import com.dylibso.chicory.wasm.types.TableLimits;
Expand Down Expand Up @@ -541,4 +542,18 @@ public void shouldResolveMultipleAliasesByTypeForAllImports() {
assertEquals(0, instance.imports().tag(0).tag().tagType().typeIdx());
assertEquals(1, instance.imports().tag(1).tag().tagType().typeIdx());
}

@Test
public void correctlyReturnAllLabels() {
// Arrange
var operands = new long[] {64, 2, 0, 0, 0, 0, 0, 0};

// Act
var result = CatchOpCode.allLabels(operands);

// Assert
assertEquals(2, result.size());
assertEquals(0, result.get(0));
assertEquals(0, result.get(1));
}
}
Loading