Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
080d3ea
Introduced typed function references to GraalWasm type system
jirkamarsik Sep 23, 2025
039f950
Replace FunctionType with ClosedFunctionType
jirkamarsik Sep 23, 2025
9e563ad
Use subtype matching during static type checking
jirkamarsik Sep 24, 2025
0f4dfb6
Add typed function references instructions
jirkamarsik Sep 23, 2025
a2914f4
Check that non-defaultable locals are initialized before access
jirkamarsik Sep 29, 2025
065cd0b
Support initializer expressions for tables
jirkamarsik Sep 30, 2025
2df775a
Fixes for typed references spec tests
jirkamarsik Sep 30, 2025
48ad15b
Fix br_table type checking in the presence of subtyping
jirkamarsik Oct 6, 2025
0c22243
Added a context option for typed references, updated CHANGELOG
jirkamarsik Oct 6, 2025
16d9a90
Avoid GraalWasm code duplcation when emitting branches
jirkamarsik Oct 6, 2025
a11ef3f
Fix immutable global error message
jirkamarsik Oct 7, 2025
feab738
Fix malformed limits flags failure type
jirkamarsik Oct 7, 2025
1885228
Fix unexpected end of const expr failure message
jirkamarsik Oct 7, 2025
68d6b78
Fix failure type reported for illegal opcodes
jirkamarsik Oct 7, 2025
0654ce2
Use TYPE_MISMATCH failure for uninitialized tables
jirkamarsik Oct 7, 2025
df4e3bb
Re-run table initializer during table state reset
jirkamarsik Oct 7, 2025
c8d6163
Enforce types at the typed wasm / untyped interop boundary
jirkamarsik Oct 7, 2025
26ea719
Use subtype matching for module imports
jirkamarsik Oct 8, 2025
8c4c4f5
Store pre-computed closed types in SymbolTable
jirkamarsik Oct 10, 2025
d58b3ce
Divide matchesType into isSupertypeOf and isSubtypeOf
jirkamarsik Oct 17, 2025
c2ba927
Add missing ExplodeLoop to subtype checking
jirkamarsik Oct 17, 2025
1792ee5
Do not pass in non-wasm functions as funcref values
jirkamarsik Oct 9, 2025
130b966
Drop unused branches in set_callback functions
jirkamarsik Oct 9, 2025
1006ca5
Use subtype matching when checking the type of empty else branch
jirkamarsik Oct 9, 2025
1a25822
Revert to exnref in ValueType, forbid exnref in JS global helpers
jirkamarsik Oct 9, 2025
8576bb2
Document the new GraalWasm type system
jirkamarsik Oct 9, 2025
5484f31
Try checking type equivalence classes before subtype matching in call…
jirkamarsik Oct 10, 2025
0b78d7f
Fix LABEL_U16 parsing in the interpreter
jirkamarsik Oct 17, 2025
6469236
Factor out bytecode handlers for MISC bytecodes
jirkamarsik Oct 17, 2025
a26351d
Cleanup and restored fixes after reverting back to closed types
jirkamarsik Oct 19, 2025
e16aea9
Use hexadecimal in opcode definitions consistently
jirkamarsik Oct 19, 2025
20b8d83
Stabilize memory footprint benchmark
jirkamarsik Oct 20, 2025
1c9f8f9
Check type equivalence for mutable global imports, clean up global im…
jirkamarsik Oct 21, 2025
d5ca223
Add toString impl for ClosedValueType hierarchy
jirkamarsik Oct 21, 2025
c278cf7
Use type equality when checking type of imported tables
jirkamarsik Oct 21, 2025
a347f3a
Allow executing functions from Wasm modules that have failed linking
jirkamarsik Oct 21, 2025
2a0f44c
Set correct order of elem and data link actions in Instantiator
jirkamarsik Oct 21, 2025
538847e
Merge bytecode handlers for call_indirect and call_ref
jirkamarsik Oct 24, 2025
231c7a9
Profile out subtyping check in call_indirect
jirkamarsik Oct 27, 2025
a1962e6
Outline call_indirect/call_ref not-a-function error cases
jirkamarsik Oct 27, 2025
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
1 change: 1 addition & 0 deletions wasm/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This changelog summarizes major changes to the WebAssembly engine implemented in
## Version 25.1.0

* Implemented the [exception handling](https://github.com/WebAssembly/exception-handling) proposal. This feature can be enabled with the experimental option `wasm.Exceptions=true`.
* Implemented the [typed function references](https://github.com/WebAssembly/function-references) proposal. This feature can be enabled with the experimental option `wasm.TypedFunctionReferences=true`.

## Version 25.0.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ static double getHeapSize() {

static void sleep() {
try {
Thread.sleep(2000);
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@
import org.graalvm.polyglot.io.ByteSequence;
import org.graalvm.wasm.WasmLanguage;
import org.graalvm.wasm.collection.ByteArrayList;
import org.graalvm.wasm.collection.IntArrayList;

public abstract class AbstractBinarySuite {
protected static final byte[] EMPTY_BYTES = {};
protected static final int[] EMPTY_INTS = {};

protected static void runRuntimeTest(byte[] binary, Consumer<Context.Builder> options, Consumer<Value> testCase) throws IOException {
final Context.Builder contextBuilder = Context.newBuilder(WasmLanguage.ID);
Expand Down Expand Up @@ -100,10 +101,10 @@ private static byte getByte(String hexString) {

private static final class BinaryTypes {

private final List<byte[]> paramEntries = new ArrayList<>();
private final List<byte[]> resultEntries = new ArrayList<>();
private final List<int[]> paramEntries = new ArrayList<>();
private final List<int[]> resultEntries = new ArrayList<>();

private void add(byte[] params, byte[] results) {
private void add(int[] params, int[] results) {
paramEntries.add(params);
resultEntries.add(results);
}
Expand All @@ -112,18 +113,18 @@ private byte[] generateTypeSection() {
ByteArrayList b = new ByteArrayList();
b.add(getByte("01"));
b.add((byte) 0); // length is patched in the end
b.add((byte) paramEntries.size());
b.addUnsignedInt32(paramEntries.size());
for (int i = 0; i < paramEntries.size(); i++) {
b.add(getByte("60"));
byte[] params = paramEntries.get(i);
byte[] results = resultEntries.get(i);
b.add((byte) params.length);
for (byte param : params) {
b.add(param);
int[] params = paramEntries.get(i);
int[] results = resultEntries.get(i);
b.addUnsignedInt32(params.length);
for (int param : params) {
b.addSignedInt32(param);
}
b.add((byte) results.length);
for (byte result : results) {
b.add(result);
b.addUnsignedInt32(results.length);
for (int result : results) {
b.addSignedInt32(result);
}
}
b.set(1, (byte) (b.size() - 2));
Expand All @@ -132,9 +133,9 @@ private byte[] generateTypeSection() {
}

private static final class BinaryTables {
private final ByteArrayList tables = new ByteArrayList();
private final IntArrayList tables = new IntArrayList();

private void add(byte initSize, byte maxSize, byte elemType) {
private void add(int initSize, int maxSize, int elemType) {
tables.add(initSize);
tables.add(maxSize);
tables.add(elemType);
Expand All @@ -147,20 +148,20 @@ private byte[] generateTableSection() {
final int tableCount = tables.size() / 3;
b.add((byte) tableCount);
for (int i = 0; i < tables.size(); i += 3) {
b.add(tables.get(i + 2));
b.addSignedInt32(tables.get(i + 2));
b.add(getByte("01"));
b.add(tables.get(i));
b.add(tables.get(i + 1));
b.addUnsignedInt32(tables.get(i));
b.addUnsignedInt32(tables.get(i + 1));
}
b.set(1, (byte) (b.size() - 2));
return b.toArray();
}
}

private static final class BinaryMemories {
private final ByteArrayList memories = new ByteArrayList();
private final IntArrayList memories = new IntArrayList();

private void add(byte initSize, byte maxSize) {
private void add(int initSize, int maxSize) {
memories.add(initSize);
memories.add(maxSize);
}
Expand All @@ -173,20 +174,20 @@ private byte[] generateMemorySection() {
b.add((byte) memoryCount);
for (int i = 0; i < memories.size(); i += 2) {
b.add(getByte("01"));
b.add(memories.get(i));
b.add(memories.get(i + 1));
b.addUnsignedInt32(memories.get(i));
b.addUnsignedInt32(memories.get(i + 1));
}
b.set(1, (byte) (b.size() - 2));
return b.toArray();
}
}

private static final class BinaryFunctions {
private final ByteArrayList types = new ByteArrayList();
private final List<byte[]> localEntries = new ArrayList<>();
private final IntArrayList types = new IntArrayList();
private final List<int[]> localEntries = new ArrayList<>();
private final List<byte[]> codeEntries = new ArrayList<>();

private void add(byte typeIndex, byte[] locals, byte[] code) {
private void add(int typeIndex, int[] locals, byte[] code) {
types.add(typeIndex);
localEntries.add(locals);
codeEntries.add(code);
Expand All @@ -197,9 +198,9 @@ private byte[] generateFunctionSection() {
b.add(getByte("03"));
b.add((byte) 0); // length is patched at the end
final int functionCount = types.size();
b.add((byte) functionCount);
b.addUnsignedInt32(functionCount);
for (int i = 0; i < functionCount; i++) {
b.add(types.get(i));
b.addUnsignedInt32(types.get(i));
}
b.set(1, (byte) (b.size() - 2));
return b.toArray();
Expand All @@ -210,15 +211,15 @@ private byte[] generateCodeSection() {
b.add(getByte("0A"));
b.add((byte) 0); // length is patched at the end
final int functionCount = types.size();
b.add((byte) functionCount);
b.addUnsignedInt32(functionCount);
for (int i = 0; i < functionCount; i++) {
byte[] locals = localEntries.get(i);
int[] locals = localEntries.get(i);
byte[] code = codeEntries.get(i);
int length = 1 + locals.length + code.length;
b.add((byte) length);
b.add((byte) locals.length);
for (byte l : locals) {
b.add(l);
b.addUnsignedInt32(length);
b.addUnsignedInt32(locals.length);
for (int l : locals) {
b.addSignedInt32(l);
}
for (byte op : code) {
b.add(op);
Expand All @@ -231,10 +232,10 @@ private byte[] generateCodeSection() {

private static final class BinaryExports {
private final ByteArrayList types = new ByteArrayList();
private final ByteArrayList indices = new ByteArrayList();
private final IntArrayList indices = new IntArrayList();
private final List<byte[]> names = new ArrayList<>();

private void addFunctionExport(byte functionIndex, String name) {
private void addFunctionExport(int functionIndex, String name) {
types.add(getByte("00"));
indices.add(functionIndex);
names.add(name.getBytes(StandardCharsets.UTF_8));
Expand All @@ -244,15 +245,15 @@ private byte[] generateExportSection() {
ByteArrayList b = new ByteArrayList();
b.add(getByte("07"));
b.add((byte) 0); // length is patched at the end
b.add((byte) types.size());
b.addUnsignedInt32(types.size());
for (int i = 0; i < types.size(); i++) {
final byte[] name = names.get(i);
b.add((byte) name.length);
b.addUnsignedInt32(name.length);
for (byte value : name) {
b.add(value);
}
b.add(types.get(i));
b.add(indices.get(i));
b.addUnsignedInt32(indices.get(i));
}
b.set(1, (byte) (b.size() - 2));
return b.toArray();
Expand Down Expand Up @@ -313,10 +314,10 @@ private byte[] generateDataSection() {

private static final class BinaryGlobals {
private final ByteArrayList mutabilities = new ByteArrayList();
private final ByteArrayList valueTypes = new ByteArrayList();
private final IntArrayList valueTypes = new IntArrayList();
private final List<byte[]> expressions = new ArrayList<>();

private void add(byte mutability, byte valueType, byte[] expression) {
private void add(byte mutability, int valueType, byte[] expression) {
mutabilities.add(mutability);
valueTypes.add(valueType);
expressions.add(expression);
Expand All @@ -328,7 +329,7 @@ private byte[] generateGlobalSection() {
b.add((byte) 0); // length is patched at the end
b.add((byte) mutabilities.size());
for (int i = 0; i < mutabilities.size(); i++) {
b.add(valueTypes.get(i));
b.addSignedInt32(valueTypes.get(i));
b.add(mutabilities.get(i));
for (byte e : expressions.get(i)) {
b.add(e);
Expand Down Expand Up @@ -378,8 +379,8 @@ private byte[] generateCustomSections() {
final byte[] name = names.get(i);
final byte[] section = sections.get(i);
final int size = 1 + name.length + section.length;
b.add((byte) size); // length is patched at the end
b.add((byte) name.length);
b.addUnsignedInt32(size);
b.addUnsignedInt32(name.length);
b.addRange(name, 0, name.length);
b.addRange(section, 0, section.length);
}
Expand All @@ -400,27 +401,27 @@ protected static class BinaryBuilder {

private final BinaryCustomSections binaryCustomSections = new BinaryCustomSections();

public BinaryBuilder addType(byte[] params, byte[] results) {
public BinaryBuilder addType(int[] params, int[] results) {
binaryTypes.add(params, results);
return this;
}

public BinaryBuilder addTable(byte initSize, byte maxSize, byte elemType) {
public BinaryBuilder addTable(int initSize, int maxSize, int elemType) {
binaryTables.add(initSize, maxSize, elemType);
return this;
}

public BinaryBuilder addMemory(byte initSize, byte maxSize) {
public BinaryBuilder addMemory(int initSize, int maxSize) {
binaryMemories.add(initSize, maxSize);
return this;
}

public BinaryBuilder addFunction(byte typeIndex, byte[] locals, String hexCode) {
public BinaryBuilder addFunction(int typeIndex, int[] locals, String hexCode) {
binaryFunctions.add(typeIndex, locals, WasmTestUtils.hexStringToByteArray(hexCode));
return this;
}

public BinaryBuilder addFunctionExport(byte functionIndex, String name) {
public BinaryBuilder addFunctionExport(int functionIndex, String name) {
binaryExports.addFunctionExport(functionIndex, name);
return this;
}
Expand All @@ -435,7 +436,7 @@ public BinaryBuilder addData(String hexCode) {
return this;
}

public BinaryBuilder addGlobal(byte mutability, byte valueType, String hexCode) {
public BinaryBuilder addGlobal(byte mutability, int valueType, String hexCode) {
binaryGlobals.add(mutability, valueType, WasmTestUtils.hexStringToByteArray(hexCode));
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
public class WasmJsApiSuite {
private static final String REF_TYPES_OPTION = "wasm.BulkMemoryAndRefTypes";

private static WasmFunctionInstance createWasmFunctionInstance(WasmContext context, byte[] paramTypes, byte[] resultTypes, RootNode functionRootNode) {
private static WasmFunctionInstance createWasmFunctionInstance(WasmContext context, int[] paramTypes, int[] resultTypes, RootNode functionRootNode) {
WasmModule module = WasmModule.createBuiltin("dummyModule");
module.allocateFunctionType(paramTypes, resultTypes, context.getContextOptions().supportMultiValue());
WasmFunction func = module.declareFunction(0);
Expand All @@ -111,7 +111,6 @@ private static WasmFunctionInstance createWasmFunctionInstance(WasmContext conte
// Perform normal linking steps, incl. assignTypeEquivalenceClasses().
// Functions need to have type equivalence classes assigned for indirect calls.
moduleInstance.store().linker().tryLink(moduleInstance);
assert func.typeEquivalenceClass() >= 0 : "type equivalence class must be assigned";
return new WasmFunctionInstance(moduleInstance, func, functionRootNode.getCallTarget());
}

Expand Down Expand Up @@ -503,7 +502,7 @@ public void testGlobalWriteNull() throws IOException {
public void testGlobalWriteAnyfuncRefTypesDisabled() throws IOException {
runTest(WasmJsApiSuite::disableRefTypes, context -> {
final WebAssembly wasm = new WebAssembly(context);
final WasmGlobal global = new WasmGlobal(ValueType.anyfunc, true, WasmConstant.NULL);
final WasmGlobal global = WasmGlobal.allocRef(ValueType.anyfunc, true, WasmConstant.NULL);
try {
wasm.globalWrite(global, WasmConstant.NULL);
Assert.fail("Should have failed - ref types not enabled");
Expand All @@ -517,7 +516,7 @@ public void testGlobalWriteAnyfuncRefTypesDisabled() throws IOException {
public void testGlobalWriteExternrefRefTypesDisabled() throws IOException {
runTest(WasmJsApiSuite::disableRefTypes, context -> {
final WebAssembly wasm = new WebAssembly(context);
final WasmGlobal global = new WasmGlobal(ValueType.externref, true, WasmConstant.NULL);
final WasmGlobal global = WasmGlobal.allocRef(ValueType.externref, true, WasmConstant.NULL);
try {
wasm.globalWrite(global, WasmConstant.NULL);
Assert.fail("Should have failed - ref types not enabled");
Expand Down Expand Up @@ -1412,7 +1411,14 @@ public void testFuncTypeMultiValue() throws IOException, InterruptedException {

@Test
public void testMultiValueReferencePassThrough() throws IOException, InterruptedException {
final byte[] source = compileWat("data", """
final byte[] source1 = compileWat("data", """
(module
(type (func (result i32)))
(func (export "func") (type 0)
i32.const 42
))
""");
final byte[] source2 = compileWat("data", """
(module
(type (func (result funcref externref)))
(import "m" "f" (func (type 0)))
Expand All @@ -1422,16 +1428,17 @@ public void testMultiValueReferencePassThrough() throws IOException, Interrupted
""");
runTest(context -> {
final WebAssembly wasm = new WebAssembly(context);
final var func = new Executable((args) -> 0);
final WasmInstance instance1 = moduleInstantiate(wasm, source1, null);
final Object func = WebAssembly.instanceExport(instance1, "func");
final var f = new Executable((args) -> {
final Object[] result = new Object[2];
result[0] = func;
result[1] = "foo";
return InteropArray.create(result);
});
final Dictionary importObject = Dictionary.create(new Object[]{"m", Dictionary.create(new Object[]{"f", f})});
final WasmInstance instance = moduleInstantiate(wasm, source, importObject);
final Object main = WebAssembly.instanceExport(instance, "main");
final WasmInstance instance2 = moduleInstantiate(wasm, source2, importObject);
final Object main = WebAssembly.instanceExport(instance2, "main");
final InteropLibrary lib = InteropLibrary.getUncached();
try {
Object result = lib.execute(main);
Expand Down
Loading