Skip to content

Commit a0bd3f4

Browse files
authored
Send a non-nullable (ref exn) to catch_ref and catch_all_ref handlers (#194)
1 parent d08c27f commit a0bd3f4

8 files changed

Lines changed: 173 additions & 25 deletions

File tree

‎codegen/src/main/java/run/endive/codegen/ModuleInterfaceCodegen.java‎

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -830,31 +830,42 @@ private Expression listOfValueTypes(List<ValType> valTypes) {
830830
return new MethodCallExpr(new NameExpr("List"), "of", NodeList.nodeList(values));
831831
}
832832

833+
/** The {@code ValType} shorthand constant for an abstract heap type, or null if there is none. */
834+
private static String abstractRefShorthand(int typeIdx) {
835+
if (typeIdx == ValType.TypeIdxCode.EXTERN.code()) {
836+
return "ExternRef";
837+
} else if (typeIdx == ValType.TypeIdxCode.ANY.code()) {
838+
return "AnyRef";
839+
} else if (typeIdx == ValType.TypeIdxCode.EQ.code()) {
840+
return "EqRef";
841+
} else if (typeIdx == ValType.TypeIdxCode.I31.code()) {
842+
return "I31Ref";
843+
} else if (typeIdx == ValType.TypeIdxCode.STRUCT.code()) {
844+
return "StructRef";
845+
} else if (typeIdx == ValType.TypeIdxCode.ARRAY.code()) {
846+
return "ArrayRef";
847+
} else if (typeIdx == ValType.TypeIdxCode.NONE.code()) {
848+
return "NoneRef";
849+
} else if (typeIdx == ValType.TypeIdxCode.FUNC.code()) {
850+
return "FuncRef";
851+
} else if (typeIdx == ValType.TypeIdxCode.EXN.code()) {
852+
return "ExnRef";
853+
} else if (typeIdx == ValType.TypeIdxCode.NOFUNC.code()) {
854+
return "NoFuncRef";
855+
} else if (typeIdx == ValType.TypeIdxCode.NOEXTERN.code()) {
856+
return "NoExternRef";
857+
} else {
858+
return null;
859+
}
860+
}
861+
833862
private static Expression valTypeRefExpr(ValType vt) {
834863
int ti = vt.typeIdx();
835-
if (ti == ValType.TypeIdxCode.EXTERN.code()) {
836-
return new FieldAccessExpr(new NameExpr("ValType"), "ExternRef");
837-
} else if (ti == ValType.TypeIdxCode.ANY.code()) {
838-
return new FieldAccessExpr(new NameExpr("ValType"), "AnyRef");
839-
} else if (ti == ValType.TypeIdxCode.EQ.code()) {
840-
return new FieldAccessExpr(new NameExpr("ValType"), "EqRef");
841-
} else if (ti == ValType.TypeIdxCode.I31.code()) {
842-
return new FieldAccessExpr(new NameExpr("ValType"), "I31Ref");
843-
} else if (ti == ValType.TypeIdxCode.STRUCT.code()) {
844-
return new FieldAccessExpr(new NameExpr("ValType"), "StructRef");
845-
} else if (ti == ValType.TypeIdxCode.ARRAY.code()) {
846-
return new FieldAccessExpr(new NameExpr("ValType"), "ArrayRef");
847-
} else if (ti == ValType.TypeIdxCode.NONE.code()) {
848-
return new FieldAccessExpr(new NameExpr("ValType"), "NoneRef");
849-
} else if (ti == ValType.TypeIdxCode.FUNC.code()) {
850-
return new FieldAccessExpr(new NameExpr("ValType"), "FuncRef");
851-
} else if (ti == ValType.TypeIdxCode.EXN.code()) {
852-
return new FieldAccessExpr(new NameExpr("ValType"), "ExnRef");
853-
} else if (ti == ValType.TypeIdxCode.NOFUNC.code()) {
854-
return new FieldAccessExpr(new NameExpr("ValType"), "NoFuncRef");
855-
} else if (ti == ValType.TypeIdxCode.NOEXTERN.code()) {
856-
return new FieldAccessExpr(new NameExpr("ValType"), "NoExternRef");
857-
} else if (ti >= 0) {
864+
String shorthand = abstractRefShorthand(ti);
865+
// the shorthands are all nullable, a non-nullable ref has to be built explicitly
866+
if (shorthand != null && vt.opcode() == ValType.ID.RefNull) {
867+
return new FieldAccessExpr(new NameExpr("ValType"), shorthand);
868+
} else if (shorthand != null || ti >= 0) {
858869
String opcName = vt.opcode() == ValType.ID.Ref ? "Ref" : "RefNull";
859870
return new MethodCallExpr(
860871
new MethodCallExpr(

‎compiler-tests/pom.xml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@
262262
<wast>proposals/threads/exports.wast</wast>
263263
<wast>proposals/threads/imports.wast</wast>
264264
<wast>proposals/threads/memory.wast</wast>
265+
<wast>proposals/wasm-3.0/try_table.wast</wast>
265266
<wast>ref_func.wast</wast>
266267
<wast>ref_is_null.wast</wast>
267268
<wast>ref_null.wast</wast>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package run.endive.testing;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
5+
import java.util.function.Function;
6+
import java.util.stream.Stream;
7+
import org.junit.jupiter.params.ParameterizedTest;
8+
import org.junit.jupiter.params.provider.Arguments;
9+
import org.junit.jupiter.params.provider.MethodSource;
10+
import run.endive.compiler.MachineFactoryCompiler;
11+
import run.endive.corpus.CorpusResources;
12+
import run.endive.runtime.ImportValues;
13+
import run.endive.runtime.Instance;
14+
import run.endive.runtime.InterpreterMachine;
15+
import run.endive.wasm.Parser;
16+
import run.endive.wasm.WasmModule;
17+
18+
/** Tests for `catch_ref` and `catch_all_ref` sending a non-nullable `(ref exn)`. */
19+
public class CatchRefNonNullTest {
20+
21+
private static final WasmModule MODULE =
22+
Parser.parse(CorpusResources.getResource("compiled/catch_ref_non_null.wat.wasm"));
23+
24+
private static Stream<Arguments> machineImplementations() {
25+
return Stream.of(
26+
Arguments.of(
27+
(Function<Instance.Builder, Instance.Builder>)
28+
(b) -> b.withMachineFactory(InterpreterMachine::new)),
29+
Arguments.of(
30+
(Function<Instance.Builder, Instance.Builder>)
31+
(b) -> b.withMachineFactory(MachineFactoryCompiler::compile)));
32+
}
33+
34+
private static Instance instance(Function<Instance.Builder, Instance.Builder> machineInject) {
35+
return machineInject
36+
.apply(Instance.builder(MODULE).withImportValues(ImportValues.builder().build()))
37+
.build();
38+
}
39+
40+
@ParameterizedTest
41+
@MethodSource("machineImplementations")
42+
public void catchRefNonNull(Function<Instance.Builder, Instance.Builder> machineInject) {
43+
var instance = instance(machineInject);
44+
assertEquals(42, instance.export("catch-ref-non-null").apply(42)[0]);
45+
}
46+
47+
@ParameterizedTest
48+
@MethodSource("machineImplementations")
49+
public void catchAllRefNonNull(Function<Instance.Builder, Instance.Builder> machineInject) {
50+
var instance = instance(machineInject);
51+
assertEquals(42, instance.export("catch-all-ref-non-null").apply(42)[0]);
52+
}
53+
54+
@ParameterizedTest
55+
@MethodSource("machineImplementations")
56+
public void rethrowNonNull(Function<Instance.Builder, Instance.Builder> machineInject) {
57+
var instance = instance(machineInject);
58+
assertEquals(42, instance.export("rethrow-non-null").apply(42)[0]);
59+
}
60+
61+
@ParameterizedTest
62+
@MethodSource("machineImplementations")
63+
public void catchRefNullable(Function<Instance.Builder, Instance.Builder> machineInject) {
64+
var instance = instance(machineInject);
65+
assertEquals(42, instance.export("catch-ref-nullable").apply(42)[0]);
66+
}
67+
}

‎runtime-tests/pom.xml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@
267267
<wast>proposals/threads/exports.wast</wast>
268268
<wast>proposals/threads/imports.wast</wast>
269269
<wast>proposals/threads/memory.wast</wast>
270+
<wast>proposals/wasm-3.0/try_table.wast</wast>
270271
<wast>ref_func.wast</wast>
271272
<wast>ref_is_null.wast</wast>
272273
<wast>ref_null.wast</wast>
@@ -737,6 +738,7 @@
737738
<wast>proposals/threads/exports.wast</wast>
738739
<wast>proposals/threads/imports.wast</wast>
739740
<wast>proposals/threads/memory.wast</wast>
741+
<wast>proposals/wasm-3.0/try_table.wast</wast>
740742
<wast>ref_func.wast</wast>
741743
<wast>ref_is_null.wast</wast>
742744
<wast>ref_null.wast</wast>
340 Bytes
Binary file not shown.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
(module
2+
;; `catch_ref` and `catch_all_ref` send a non-nullable `(ref exn)` to the handler label.
3+
4+
(tag $e (param i32))
5+
6+
(func $throw (param $val i32)
7+
(throw $e (local.get $val))
8+
)
9+
10+
(func (export "catch-ref-non-null") (param $val i32) (result i32)
11+
(block $h (result i32 (ref exn))
12+
(try_table (result i32) (catch_ref $e $h)
13+
(call $throw (local.get $val))
14+
(i32.const 0)
15+
)
16+
(return)
17+
)
18+
(drop)
19+
)
20+
21+
(func (export "catch-all-ref-non-null") (param $val i32) (result i32)
22+
(block $h (result (ref exn))
23+
(try_table (catch_all_ref $h)
24+
(call $throw (local.get $val))
25+
)
26+
(return (i32.const 0))
27+
)
28+
(drop)
29+
(local.get $val)
30+
)
31+
32+
;; the caught `(ref exn)` is usable: store it in a nullable local and rethrow it
33+
(func (export "rethrow-non-null") (param $val i32) (result i32)
34+
(local $exn exnref)
35+
(block $h (result (ref exn))
36+
(try_table (catch_all_ref $h)
37+
(call $throw (local.get $val))
38+
)
39+
(return (i32.const 0))
40+
)
41+
(local.set $exn)
42+
(block $h2 (result i32 (ref exn))
43+
(try_table (result i32) (catch_ref $e $h2)
44+
(local.get $exn)
45+
(throw_ref)
46+
)
47+
(return)
48+
)
49+
(drop)
50+
)
51+
52+
;; the nullable spelling has to keep working, `(ref exn)` is a subtype of `exnref`
53+
(func (export "catch-ref-nullable") (param $val i32) (result i32)
54+
(block $h (result i32 exnref)
55+
(try_table (result i32) (catch_ref $e $h)
56+
(call $throw (local.get $val))
57+
(i32.const 0)
58+
)
59+
(return)
60+
)
61+
(drop)
62+
)
63+
)

‎wasm/src/main/java/run/endive/wasm/Validator.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,13 +1009,13 @@ void validateFunction(int funcIdx, FunctionBody body, FunctionType functionType)
10091009
getTagType(currentCatch.tag())
10101010
.typeIdx());
10111011
pushVals(tagType.params());
1012-
pushVal(ValType.ExnRef);
1012+
pushVal(ValType.RefExn);
10131013
break;
10141014
}
10151015
case CATCH_ALL:
10161016
break;
10171017
case CATCH_ALL_REF:
1018-
pushVal(ValType.ExnRef);
1018+
pushVal(ValType.RefExn);
10191019
break;
10201020
}
10211021
popCtrl();

‎wasm/src/main/java/run/endive/wasm/types/ValType.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ public final class ValType {
3737
public static final ValType RefBot =
3838
new ValType(ValType.ID.Ref, ValType.TypeIdxCode.BOT.code());
3939

40+
/** The non-nullable {@code (ref exn)}. */
41+
public static final ValType RefExn =
42+
new ValType(ValType.ID.Ref, ValType.TypeIdxCode.EXN.code());
43+
4044
private final long id;
4145

4246
// defined function type. This is not representable in the binary or textual representation

0 commit comments

Comments
 (0)