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
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,8 @@ private static String abstractRefShorthand(int typeIdx) {
return "NoFuncRef";
} else if (typeIdx == ValType.TypeIdxCode.NOEXTERN.code()) {
return "NoExternRef";
} else if (typeIdx == ValType.TypeIdxCode.NOEXN.code()) {
return "NoExnRef";
} else {
return null;
}
Expand Down
1 change: 1 addition & 0 deletions compiler-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@
<wast>proposals/threads/exports.wast</wast>
<wast>proposals/threads/imports.wast</wast>
<wast>proposals/threads/memory.wast</wast>
<wast>proposals/wasm-3.0/ref_null.wast</wast>
<wast>proposals/wasm-3.0/try_table.wast</wast>
<wast>ref_func.wast</wast>
<wast>ref_is_null.wast</wast>
Expand Down
75 changes: 75 additions & 0 deletions machine-tests/src/test/java/run/endive/testing/NoExnTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package run.endive.testing;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import run.endive.compiler.MachineFactoryCompiler;
import run.endive.corpus.CorpusResources;
import run.endive.runtime.ImportValues;
import run.endive.runtime.Instance;
import run.endive.runtime.InterpreterMachine;
import run.endive.wasm.Parser;
import run.endive.wasm.WasmModule;
import run.endive.wasm.types.Value;

/** Tests for the `noexn` bottom type of the exception hierarchy. */
public class NoExnTest {

private static final WasmModule MODULE =
Parser.parse(CorpusResources.getResource("compiled/noexn.wat.wasm"));

private static Stream<Arguments> machineImplementations() {
return Stream.of(
Arguments.of(
(Function<Instance.Builder, Instance.Builder>)
(b) -> b.withMachineFactory(InterpreterMachine::new)),
Arguments.of(
(Function<Instance.Builder, Instance.Builder>)
(b) -> b.withMachineFactory(MachineFactoryCompiler::compile)));
}

private static Instance instance(Function<Instance.Builder, Instance.Builder> machineInject) {
return machineInject
.apply(Instance.builder(MODULE).withImportValues(ImportValues.builder().build()))
.build();
}

@ParameterizedTest
@MethodSource("machineImplementations")
public void nullToExnRef(Function<Instance.Builder, Instance.Builder> machineInject) {
var instance = instance(machineInject);
assertEquals(Value.REF_NULL_VALUE, instance.export("null-to-exnref").apply()[0]);
}

@ParameterizedTest
@MethodSource("machineImplementations")
public void isNull(Function<Instance.Builder, Instance.Builder> machineInject) {
var instance = instance(machineInject);
assertEquals(1, instance.export("is-null").apply()[0]);
}

@ParameterizedTest
@MethodSource("machineImplementations")
public void roundtrip(Function<Instance.Builder, Instance.Builder> machineInject) {
var instance = instance(machineInject);
assertEquals(1, instance.export("roundtrip").apply()[0]);
}

@ParameterizedTest
@MethodSource("machineImplementations")
public void fromGlobal(Function<Instance.Builder, Instance.Builder> machineInject) {
var instance = instance(machineInject);
assertEquals(1, instance.export("from-global").apply()[0]);
}

@ParameterizedTest
@MethodSource("machineImplementations")
public void fromTable(Function<Instance.Builder, Instance.Builder> machineInject) {
var instance = instance(machineInject);
assertEquals(1, instance.export("from-table").apply()[0]);
}
}
2 changes: 2 additions & 0 deletions runtime-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@
<wast>proposals/threads/exports.wast</wast>
<wast>proposals/threads/imports.wast</wast>
<wast>proposals/threads/memory.wast</wast>
<wast>proposals/wasm-3.0/ref_null.wast</wast>
<wast>proposals/wasm-3.0/try_table.wast</wast>
<wast>ref_func.wast</wast>
<wast>ref_is_null.wast</wast>
Expand Down Expand Up @@ -738,6 +739,7 @@
<wast>proposals/threads/exports.wast</wast>
<wast>proposals/threads/imports.wast</wast>
<wast>proposals/threads/memory.wast</wast>
<wast>proposals/wasm-3.0/ref_null.wast</wast>
<wast>proposals/wasm-3.0/try_table.wast</wast>
<wast>ref_func.wast</wast>
<wast>ref_is_null.wast</wast>
Expand Down
6 changes: 4 additions & 2 deletions runtime/src/main/java/run/endive/runtime/Instance.java
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,8 @@ public boolean heapTypeMatch(
// Bottom types never match non-null values
if (targetHeapType == ValType.TypeIdxCode.NONE.code()
|| targetHeapType == ValType.TypeIdxCode.NOFUNC.code()
|| targetHeapType == ValType.TypeIdxCode.NOEXTERN.code()) {
|| targetHeapType == ValType.TypeIdxCode.NOEXTERN.code()
|| targetHeapType == ValType.TypeIdxCode.NOEXN.code()) {
return false;
}
// For abstract func/extern targets: the validator guarantees the operand
Expand Down Expand Up @@ -529,7 +530,8 @@ public boolean heapTypeMatchRef(
}
if (targetHeapType == ValType.TypeIdxCode.NONE.code()
|| targetHeapType == ValType.TypeIdxCode.NOFUNC.code()
|| targetHeapType == ValType.TypeIdxCode.NOEXTERN.code()) {
|| targetHeapType == ValType.TypeIdxCode.NOEXTERN.code()
|| targetHeapType == ValType.TypeIdxCode.NOEXN.code()) {
return false;
}
if (targetHeapType == ValType.TypeIdxCode.FUNC.code()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1875,7 +1875,8 @@ private static void REF_NULL(MStack stack, Operands operands) {
var heapType = (int) operands.get(0);
if (heapType == ValType.TypeIdxCode.FUNC.code()
|| heapType == ValType.TypeIdxCode.NOFUNC.code()
|| heapType == ValType.TypeIdxCode.EXN.code()) {
|| heapType == ValType.TypeIdxCode.EXN.code()
|| heapType == ValType.TypeIdxCode.NOEXN.code()) {
stack.push(REF_NULL_VALUE);
} else {
// GC refs, externref, noexternref all use Object null
Expand Down Expand Up @@ -3984,7 +3985,8 @@ private static boolean isSourceGcRef(int sourceHeapType) {
&& sourceHeapType != ValType.TypeIdxCode.NOFUNC.code()
&& sourceHeapType != ValType.TypeIdxCode.EXTERN.code()
&& sourceHeapType != ValType.TypeIdxCode.NOEXTERN.code()
&& sourceHeapType != ValType.TypeIdxCode.EXN.code();
&& sourceHeapType != ValType.TypeIdxCode.EXN.code()
&& sourceHeapType != ValType.TypeIdxCode.NOEXN.code();
}

private static void REF_TEST(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public String toResultValue(String result) {
return "Double.longBitsToDouble(" + result + "), 0.0";
case EXTERN_REF:
case EXN_REF:
case NULL_EXN_REF:
case FUNC_REF:
case STRUCT_REF:
case ANY_REF:
Expand Down Expand Up @@ -155,6 +156,8 @@ public NameExpr toAssertion(String resultVar, String moduleName) {
case NULL_REF:
case NULL_FUNC_REF:
case NULL_EXTERN_REF:
case NULL_EXN_REF:
case EXN_REF:
return new NameExpr(
"assertEquals(" + resultVar + ", " + "REF_NULL_VALUE" + ")");
case STRUCT_REF:
Expand Down Expand Up @@ -208,6 +211,7 @@ public String toExpectedValue() {
}
case EXTERN_REF:
case EXN_REF:
case NULL_EXN_REF:
case STRUCT_REF:
case ANY_REF:
case NULL_REF:
Expand Down Expand Up @@ -337,6 +341,7 @@ public NameExpr toRefAssertion(String resultVar, String moduleName) {
return new NameExpr("assertNotNull(" + resultVar + ")");
case EXTERN_REF:
case EXN_REF:
case NULL_EXN_REF:
case FUNC_REF:
case NULL_FUNC_REF:
case NULL_EXTERN_REF:
Expand Down Expand Up @@ -383,6 +388,7 @@ public String toArgsValue() {
}
case EXTERN_REF:
case EXN_REF:
case NULL_EXN_REF:
case STRUCT_REF:
case ANY_REF:
case NULL_REF:
Expand Down Expand Up @@ -506,6 +512,7 @@ public String toRefResultValue(String crVar, int index) {
return "Double.longBitsToDouble(" + crVar + ".longResult(" + index + ")), 0.0";
case EXTERN_REF:
case EXN_REF:
case NULL_EXN_REF:
case FUNC_REF:
case NULL_FUNC_REF:
case NULL_EXTERN_REF:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public enum WasmValueType {
NULL_FUNC_REF("nullfuncref"),
@JsonProperty("nullexternref")
NULL_EXTERN_REF("nullexternref"),
@JsonProperty("nullexnref")
NULL_EXN_REF("nullexnref"),
@JsonProperty("arrayref")
ARRAY_REF("arrayref"),
@JsonProperty("eqref")
Expand Down
Binary file not shown.
37 changes: 37 additions & 0 deletions wasm-corpus/src/main/resources/wat/noexn.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
(module
;; `noexn` is the bottom of the exception hierarchy, a subtype of `exn` only.

(global $g (ref null noexn) (ref.null noexn))

(func (export "null-to-exnref") (result exnref)
(ref.null noexn)
)

(func (export "is-null") (result i32)
(ref.null noexn)
(ref.is_null)
)

;; a noexn-typed parameter widens to exnref
(func $widen (param $x (ref null noexn)) (result exnref)
(local.get $x)
)

(func (export "roundtrip") (result i32)
(call $widen (ref.null noexn))
(ref.is_null)
)

(func (export "from-global") (result i32)
(global.get $g)
(ref.is_null)
)

;; a table of noexn
(table $t 2 (ref null noexn))

(func (export "from-table") (result i32)
(table.get $t (i32.const 0))
(ref.is_null)
)
)
1 change: 1 addition & 0 deletions wasm/src/main/java/run/endive/wasm/Validator.java
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ private int topOfHeapType(int heapTypeIdx) {
case -14: // NOEXTERN
return ValType.TypeIdxCode.EXTERN.code();
case -23: // EXN
case -12: // NOEXN
return ValType.TypeIdxCode.EXN.code();
default:
return heapTypeIdx;
Expand Down
20 changes: 18 additions & 2 deletions wasm/src/main/java/run/endive/wasm/types/ValType.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public final class ValType {
public static final ValType NoneRef = new ValType(ID.NoneRef);
public static final ValType NoFuncRef = new ValType(ID.NoFuncRef);
public static final ValType NoExternRef = new ValType(ID.NoExternRef);
public static final ValType NoExnRef = new ValType(ID.NoExnRef);

public static final ValType RefBot =
new ValType(ValType.ID.Ref, ValType.TypeIdxCode.BOT.code());
Expand Down Expand Up @@ -101,6 +102,9 @@ private ValType(
} else if (opcode == ID.NoFuncRef) {
typeIdx = TypeIdxCode.NOFUNC.code();
opcode = ID.RefNull;
} else if (opcode == ID.NoExnRef) {
typeIdx = TypeIdxCode.NOEXN.code();
opcode = ID.RefNull;
} else if ((opcode == ID.RefNull || opcode == ID.Ref) && typeIdx >= 0) {
assert resolvedFunctionTypeId >= 0;
}
Expand Down Expand Up @@ -146,7 +150,8 @@ private boolean computeIsGcReference(TypeSection ts) {
|| ht == TypeIdxCode.NOFUNC.code()
|| ht == TypeIdxCode.EXTERN.code()
|| ht == TypeIdxCode.NOEXTERN.code()
|| ht == TypeIdxCode.EXN.code()) {
|| ht == TypeIdxCode.EXN.code()
|| ht == TypeIdxCode.NOEXN.code()) {
return false;
}
if (ht >= 0 && ts != null) {
Expand Down Expand Up @@ -272,6 +277,7 @@ private static boolean isReference(int opcode) {
case ID.NoneRef:
case ID.NoExternRef:
case ID.NoFuncRef:
case ID.NoExnRef:
return true;
default:
return false;
Expand Down Expand Up @@ -311,6 +317,7 @@ private boolean computeIsExternRef() {
public static boolean isAbsHeapType(int opcode) {
return (opcode == ID.NoFuncRef
|| opcode == ID.NoExternRef
|| opcode == ID.NoExnRef
|| opcode == ID.NoneRef
|| opcode == ID.FuncRef
|| opcode == ID.ExternRef
Expand Down Expand Up @@ -339,6 +346,7 @@ private static boolean isValidOpcode(int opcode) {
case ID.NoneRef:
case ID.NoExternRef:
case ID.NoFuncRef:
case ID.NoExnRef:
case ID.V128:
case ID.I32:
case ID.I64:
Expand Down Expand Up @@ -399,6 +407,10 @@ public static boolean heapTypeSubtype(int ht1, int ht2, TypeSection ts) {
if (ht1 == TypeIdxCode.NOEXTERN.code()) {
return ht2 == TypeIdxCode.EXTERN.code();
}
// noexn <: exn
if (ht1 == TypeIdxCode.NOEXN.code()) {
return ht2 == TypeIdxCode.EXN.code();
}

// i31 <: eq <: any
if (ht1 == TypeIdxCode.I31.code()) {
Expand Down Expand Up @@ -608,6 +620,7 @@ public String name() {

public enum TypeIdxCode {
// heap type
NOEXN(-12), // 0x74
NOFUNC(-13), // 0x73
NOEXTERN(-14), // 0x72
NONE(-15), // 0x71
Expand Down Expand Up @@ -654,6 +667,7 @@ private ID() {}
public static final int NoneRef = 0x71;
public static final int NoExternRef = 0x72;
public static final int NoFuncRef = 0x73;
public static final int NoExnRef = 0x74;
public static final int V128 = 0x7B;
public static final int F64 = 0x7C;
public static final int F32 = 0x7D;
Expand Down Expand Up @@ -698,6 +712,7 @@ public static boolean isValidOpcode(int opcode) {
|| opcode == ArrayRef
|| opcode == NoneRef
|| opcode == NoExternRef
|| opcode == NoExnRef
|| opcode == NoFuncRef
|| opcode == V128
|| opcode == F64
Expand Down Expand Up @@ -767,7 +782,8 @@ public boolean isGcReference(TypeSection ts) {
|| typeIdx == TypeIdxCode.NOFUNC.code()
|| typeIdx == TypeIdxCode.EXTERN.code()
|| typeIdx == TypeIdxCode.NOEXTERN.code()
|| typeIdx == TypeIdxCode.EXN.code()) {
|| typeIdx == TypeIdxCode.EXN.code()
|| typeIdx == TypeIdxCode.NOEXN.code()) {
return false;
}
if (typeIdx >= 0 && ts != null) {
Expand Down
Loading