From fb69ac61700ea083ad969107c453f906c7639e5a Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Thu, 16 Feb 2023 17:01:38 -0800 Subject: [PATCH 01/50] [test] Add JS API tests for Exported GC Objects Adds a test for the draft JS API spec from this PR: https://github.com/WebAssembly/gc/pull/352 This primarily tests the internal object methods described in the spec. It doesn't yet test the ToWebAssemblyValue / ToJSValue conversions. Also syncs a portion of the module builder code with V8 to allow constructing a subset of GC-using modules. --- .../gc/exported-object.tentative.any.js | 160 +++++++++++++++ test/js-api/wasm-module-builder.js | 192 ++++++++++++++++-- 2 files changed, 338 insertions(+), 14 deletions(-) create mode 100644 test/js-api/gc/exported-object.tentative.any.js diff --git a/test/js-api/gc/exported-object.tentative.any.js b/test/js-api/gc/exported-object.tentative.any.js new file mode 100644 index 0000000000..f1d3327ba7 --- /dev/null +++ b/test/js-api/gc/exported-object.tentative.any.js @@ -0,0 +1,160 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +let functions = {}; +setup(() => { + const builder = new WasmModuleBuilder(); + + const structIndex = builder.addStruct([makeField(kWasmI32, true)]); + const arrayIndex = builder.addArray(kWasmI32, true); + const structRef = wasmRefType(structIndex); + const arrayRef = wasmRefType(arrayIndex); + + builder + .addFunction("makeStruct", makeSig_r_v(structRef)) + .addBody([...wasmI32Const(42), + ...GCInstr(kExprStructNew), structIndex]) + .exportFunc(); + + builder + .addFunction("makeArray", makeSig_r_v(arrayRef)) + .addBody([...wasmI32Const(5), ...wasmI32Const(42), + ...GCInstr(kExprArrayNew), arrayIndex]) + .exportFunc(); + + const buffer = builder.toBuffer() + const module = new WebAssembly.Module(buffer); + const instance = new WebAssembly.Instance(module, {}); + functions = instance.exports; +}); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_equals(struct.foo, undefined); + assert_equals(struct[0], undefined); + assert_equals(array.foo, undefined); + assert_equals(array[0], undefined); +}, "property access"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_throws_js(TypeError, () => { struct.foo = 5; }); + assert_throws_js(TypeError, () => { array.foo = 5; }); + assert_throws_js(TypeError, () => { struct[0] = 5; }); + assert_throws_js(TypeError, () => { array[0] = 5; }); +}, "property assignment"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_equals(Object.getOwnPropertyNames(struct).length, 0); + assert_equals(Object.getOwnPropertyNames(array).length, 0); +}, "ownPropertyNames"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_throws_js(TypeError, () => Object.defineProperty(struct, "foo", { value: 1 })); + assert_throws_js(TypeError, () => Object.defineProperty(array, "foo", { value: 1 })); +}, "defineProperty"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_throws_js(TypeError, () => delete struct.foo); + assert_throws_js(TypeError, () => delete struct[0]); + assert_throws_js(TypeError, () => delete array.foo); + assert_throws_js(TypeError, () => delete array[0]); +}, "delete"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_throws_js(TypeError, () => Object.getPrototypeOf(struct)); + assert_throws_js(TypeError, () => Object.getPrototypeOf(array)); +}, "getPrototypeOf"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_throws_js(TypeError, () => Object.setPrototypeOf(struct, {})); + assert_throws_js(TypeError, () => Object.setPrototypeOf(array, {})); +}, "setPrototypeOf"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_false(Object.isExtensible(struct)); + assert_false(Object.isExtensible(array)); +}, "isExtensible"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_throws_js(TypeError, () => Object.preventExtensions(struct)); + assert_throws_js(TypeError, () => Object.preventExtensions(array)); +}, "preventExtensions"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_throws_js(TypeError, () => Object.seal(struct)); + assert_throws_js(TypeError, () => Object.seal(array)); +}, "sealing"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_equals(typeof struct, "object"); + assert_equals(typeof array, "object"); +}, "typeof"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_throws_js(TypeError, () => struct.toString()); + assert_equals(Object.prototype.toString.call(struct), "[object Object]"); + assert_throws_js(TypeError, () => array.toString()); + assert_equals(Object.prototype.toString.call(array), "[object Object]"); +}, "toString"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + assert_throws_js(TypeError, () => struct.valueOf()); + assert_equals(Object.prototype.valueOf.call(struct), struct); + assert_throws_js(TypeError, () => array.valueOf()); + assert_equals(Object.prototype.valueOf.call(array), array); +}, "valueOf"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + const map = new Map(); + map.set(struct, "struct"); + map.set(array, "array"); + assert_equals(map.get(struct), "struct"); + assert_equals(map.get(array), "array"); +}, "GC objects as map keys"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + const set = new Set(); + set.add(struct); + set.add(array); + assert_true(set.has(struct)); + assert_true(set.has(array)); +}, "GC objects as set element"); + +test(() => { + const struct = functions.makeStruct() + const array = functions.makeArray() + const map = new WeakMap(); + map.set(struct, "struct"); + map.set(array, "array"); + assert_equals(map.get(struct), "struct"); + assert_equals(map.get(array), "array"); +}, "GC objects as weak map keys"); diff --git a/test/js-api/wasm-module-builder.js b/test/js-api/wasm-module-builder.js index d0f9e78bcd..9136256a98 100644 --- a/test/js-api/wasm-module-builder.js +++ b/test/js-api/wasm-module-builder.js @@ -74,6 +74,13 @@ let kLocalNamesCode = 2; let kWasmFunctionTypeForm = 0x60; let kWasmAnyFunctionTypeForm = 0x70; +let kWasmStructTypeForm = 0x5f; +let kWasmArrayTypeForm = 0x5e; +let kWasmSubtypeForm = 0x50; +let kWasmSubtypeFinalForm = 0x4e; +let kWasmRecursiveTypeGroupForm = 0x4f; + +let kNoSuperType = 0xFFFFFFFF; let kHasMaximumFlag = 1; let kSharedHasMaximumFlag = 3; @@ -100,6 +107,15 @@ let kWasmS128 = 0x7b; let kWasmAnyRef = 0x6f; let kWasmAnyFunc = 0x70; +let kWasmRefNull = 0x6c; +let kWasmRef = 0x6b; +function wasmRefNullType(heap_type) { + return {opcode: kWasmRefNull, heap_type: heap_type}; +} +function wasmRefType(heap_type) { + return {opcode: kWasmRef, heap_type: heap_type}; +} + let kExternalFunction = 0; let kExternalTable = 1; let kExternalMemory = 2; @@ -374,10 +390,48 @@ let kExprRefIsNull = 0xd1; let kExprRefFunc = 0xd2; // Prefix opcodes +let kGCPrefix = 0xfb; let kNumericPrefix = 0xfc; let kSimdPrefix = 0xfd; let kAtomicPrefix = 0xfe; +// Use these for multi-byte instructions (opcode > 0x7F needing two LEB bytes): +function GCInstr(opcode) { + if (opcode <= 0x7F) return [kGCPrefix, opcode]; + return [kGCPrefix, 0x80 | (opcode & 0x7F), opcode >> 7]; +} + +// GC opcodes +let kExprStructNew = 0x01; +let kExprStructNewDefault = 0x02; +let kExprStructGet = 0x03; +let kExprStructGetS = 0x04; +let kExprStructGetU = 0x05; +let kExprStructSet = 0x06; +let kExprArrayNew = 0x11; +let kExprArrayNewDefault = 0x12; +let kExprArrayGet = 0x13; +let kExprArrayGetS = 0x14; +let kExprArrayGetU = 0x15; +let kExprArraySet = 0x16; +let kExprArrayLen = 0x17; +let kExprArrayNewFixed = 0x19; +let kExprArrayNewData = 0x1b; +let kExprArrayNewElem = 0x1c; +let kExprI31New = 0x20; +let kExprI31GetS = 0x21; +let kExprI31GetU = 0x22; +let kExprRefTest = 0x40; +let kExprRefTestNull = 0x48; +let kExprRefCast = 0x41; +let kExprRefCastNull = 0x49; +let kExprBrOnCast = 0x42; +let kExprBrOnCastNull = 0x4a; +let kExprBrOnCastFail = 0x43; +let kExprBrOnCastFailNull = 0x4b; +let kExprExternInternalize = 0x70; +let kExprExternExternalize = 0x71; + // Numeric opcodes. let kExprMemoryInit = 0x08; let kExprDataDrop = 0x09; @@ -554,6 +608,20 @@ class Binary { } } + emit_heap_type(heap_type) { + this.emit_bytes(wasmSignedLeb(heap_type, kMaxVarInt32Size)); + } + + emit_type(type) { + if ((typeof type) == 'number') { + this.emit_u8(type >= 0 ? type : type & kLeb128Mask); + } else { + this.emit_u8(type.opcode); + if ('depth' in type) this.emit_u8(type.depth); + this.emit_heap_type(type.heap_type); + } + } + emit_header() { this.emit_bytes([ kWasmH0, kWasmH1, kWasmH2, kWasmH3, kWasmV0, kWasmV1, kWasmV2, kWasmV3 @@ -674,6 +742,35 @@ class WasmTableBuilder { } } +function makeField(type, mutability) { + if ((typeof mutability) != 'boolean') { + throw new Error('field mutability must be boolean'); + } + return {type: type, mutability: mutability}; +} + +class WasmStruct { + constructor(fields, is_final, supertype_idx) { + if (!Array.isArray(fields)) { + throw new Error('struct fields must be an array'); + } + this.fields = fields; + this.type_form = kWasmStructTypeForm; + this.is_final = is_final; + this.supertype = supertype_idx; + } +} + +class WasmArray { + constructor(type, mutability, is_final, supertype_idx) { + this.type = type; + this.mutability = mutability; + this.type_form = kWasmArrayTypeForm; + this.is_final = is_final; + this.supertype = supertype_idx; + } +} + class WasmModuleBuilder { constructor() { this.types = []; @@ -686,6 +783,7 @@ class WasmModuleBuilder { this.element_segments = []; this.data_segments = []; this.explicit = []; + this.rec_groups = []; this.num_imported_funcs = 0; this.num_imported_globals = 0; this.num_imported_tables = 0; @@ -728,10 +826,24 @@ class WasmModuleBuilder { this.explicit.push(this.createCustomSection(name, bytes)); } - addType(type) { - this.types.push(type); - var pl = type.params.length; // should have params - var rl = type.results.length; // should have results + // We use {is_final = true} so that the MVP syntax is generated for + // signatures. + addType(type, supertype_idx = kNoSuperType, is_final = true) { + var pl = type.params.length; // should have params + var rl = type.results.length; // should have results + var type_copy = {params: type.params, results: type.results, + is_final: is_final, supertype: supertype_idx}; + this.types.push(type_copy); + return this.types.length - 1; + } + + addStruct(fields, supertype_idx = kNoSuperType, is_final = false) { + this.types.push(new WasmStruct(fields, is_final, supertype_idx)); + return this.types.length - 1; + } + + addArray(type, mutability, supertype_idx = kNoSuperType, is_final = false) { + this.types.push(new WasmArray(type, mutability, is_final, supertype_idx)); return this.types.length - 1; } @@ -877,6 +989,21 @@ class WasmModuleBuilder { return this; } + startRecGroup() { + this.rec_groups.push({start: this.types.length, size: 0}); + } + + endRecGroup() { + if (this.rec_groups.length == 0) { + throw new Error("Did not start a recursive group before ending one") + } + let last_element = this.rec_groups[this.rec_groups.length - 1] + if (last_element.size != 0) { + throw new Error("Did not start a recursive group before ending one") + } + last_element.size = this.types.length - last_element.start; + } + setName(name) { this.name = name; return this; @@ -891,18 +1018,55 @@ class WasmModuleBuilder { // Add type section if (wasm.types.length > 0) { - if (debug) print("emitting types @ " + binary.length); + if (debug) print('emitting types @ ' + binary.length); binary.emit_section(kTypeSectionCode, section => { - section.emit_u32v(wasm.types.length); - for (let type of wasm.types) { - section.emit_u8(kWasmFunctionTypeForm); - section.emit_u32v(type.params.length); - for (let param of type.params) { - section.emit_u8(param); + let length_with_groups = wasm.types.length; + for (let group of wasm.rec_groups) { + length_with_groups -= group.size - 1; + } + section.emit_u32v(length_with_groups); + + let rec_group_index = 0; + + for (let i = 0; i < wasm.types.length; i++) { + if (rec_group_index < wasm.rec_groups.length && + wasm.rec_groups[rec_group_index].start == i) { + section.emit_u8(kWasmRecursiveTypeGroupForm); + section.emit_u32v(wasm.rec_groups[rec_group_index].size); + rec_group_index++; } - section.emit_u32v(type.results.length); - for (let result of type.results) { - section.emit_u8(result); + + let type = wasm.types[i]; + if (type.supertype != kNoSuperType) { + section.emit_u8(type.is_final ? kWasmSubtypeFinalForm + : kWasmSubtypeForm); + section.emit_u8(1); // supertype count + section.emit_u32v(type.supertype); + } else if (!type.is_final) { + section.emit_u8(kWasmSubtypeForm); + section.emit_u8(0); // no supertypes + } + if (type instanceof WasmStruct) { + section.emit_u8(kWasmStructTypeForm); + section.emit_u32v(type.fields.length); + for (let field of type.fields) { + section.emit_type(field.type); + section.emit_u8(field.mutability ? 1 : 0); + } + } else if (type instanceof WasmArray) { + section.emit_u8(kWasmArrayTypeForm); + section.emit_type(type.type); + section.emit_u8(type.mutability ? 1 : 0); + } else { + section.emit_u8(kWasmFunctionTypeForm); + section.emit_u32v(type.params.length); + for (let param of type.params) { + section.emit_type(param); + } + section.emit_u32v(type.results.length); + for (let result of type.results) { + section.emit_type(result); + } } } }); From 1779efb2ee92db57b5344d64ba2552d272631feb Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Mon, 20 Feb 2023 12:52:34 -0800 Subject: [PATCH 02/50] Update getPrototypeOf test --- test/js-api/gc/exported-object.tentative.any.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/js-api/gc/exported-object.tentative.any.js b/test/js-api/gc/exported-object.tentative.any.js index f1d3327ba7..b503c6ba42 100644 --- a/test/js-api/gc/exported-object.tentative.any.js +++ b/test/js-api/gc/exported-object.tentative.any.js @@ -72,8 +72,8 @@ test(() => { test(() => { const struct = functions.makeStruct() const array = functions.makeArray() - assert_throws_js(TypeError, () => Object.getPrototypeOf(struct)); - assert_throws_js(TypeError, () => Object.getPrototypeOf(array)); + assert_equals(Object.getPrototypeOf(struct), null); + assert_equals(Object.getPrototypeOf(array), null); }, "getPrototypeOf"); test(() => { From 15156e081cf15f8c93302739845d2f14748e1a7f Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Tue, 18 Apr 2023 14:19:49 -0700 Subject: [PATCH 03/50] Address PR feedback --- .../gc/exported-object.tentative.any.js | 108 ++++++++++++------ test/js-api/wasm-module-builder.js | 24 ++-- 2 files changed, 90 insertions(+), 42 deletions(-) diff --git a/test/js-api/gc/exported-object.tentative.any.js b/test/js-api/gc/exported-object.tentative.any.js index b503c6ba42..1d6be676e9 100644 --- a/test/js-api/gc/exported-object.tentative.any.js +++ b/test/js-api/gc/exported-object.tentative.any.js @@ -22,15 +22,15 @@ setup(() => { ...GCInstr(kExprArrayNew), arrayIndex]) .exportFunc(); - const buffer = builder.toBuffer() + const buffer = builder.toBuffer(); const module = new WebAssembly.Module(buffer); const instance = new WebAssembly.Instance(module, {}); functions = instance.exports; }); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_equals(struct.foo, undefined); assert_equals(struct[0], undefined); assert_equals(array.foo, undefined); @@ -38,82 +38,110 @@ test(() => { }, "property access"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + "use strict"; + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_throws_js(TypeError, () => { struct.foo = 5; }); assert_throws_js(TypeError, () => { array.foo = 5; }); assert_throws_js(TypeError, () => { struct[0] = 5; }); assert_throws_js(TypeError, () => { array[0] = 5; }); -}, "property assignment"); +}, "property assignment (strict mode)"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); + struct.foo = 5; + assert_equals(struct.foo, undefined); + array.foo = 5; + assert_equals(array.foo, undefined); + struct[0] = 5; + assert_equals(struct[0], undefined); + array[0] = 5; + assert_equals(array[0], undefined); +}, "property assignment (non-strict mode)"); + +test(() => { + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_equals(Object.getOwnPropertyNames(struct).length, 0); assert_equals(Object.getOwnPropertyNames(array).length, 0); }, "ownPropertyNames"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_throws_js(TypeError, () => Object.defineProperty(struct, "foo", { value: 1 })); assert_throws_js(TypeError, () => Object.defineProperty(array, "foo", { value: 1 })); }, "defineProperty"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + "use strict"; + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_throws_js(TypeError, () => delete struct.foo); assert_throws_js(TypeError, () => delete struct[0]); assert_throws_js(TypeError, () => delete array.foo); assert_throws_js(TypeError, () => delete array[0]); -}, "delete"); +}, "delete (strict mode)"); + +test(() => { + const struct = functions.makeStruct(); + const array = functions.makeArray(); + const structNamesLength = Object.getOwnPropertyNames(struct).length; + const arrayNamesLength = Object.getOwnPropertyNames(array).length; + delete struct.foo; + delete struct[0]; + delete array.foo; + delete array[0]; + assert_equals(Object.getOwnPropertyNames(struct), structNamesLength); + assert_equals(Object.getOwnPropertyNames(array), arrayNamesLength); +}, "delete (non-strict mode)"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_equals(Object.getPrototypeOf(struct), null); assert_equals(Object.getPrototypeOf(array), null); }, "getPrototypeOf"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_throws_js(TypeError, () => Object.setPrototypeOf(struct, {})); assert_throws_js(TypeError, () => Object.setPrototypeOf(array, {})); }, "setPrototypeOf"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_false(Object.isExtensible(struct)); assert_false(Object.isExtensible(array)); }, "isExtensible"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_throws_js(TypeError, () => Object.preventExtensions(struct)); assert_throws_js(TypeError, () => Object.preventExtensions(array)); }, "preventExtensions"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_throws_js(TypeError, () => Object.seal(struct)); assert_throws_js(TypeError, () => Object.seal(array)); }, "sealing"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_equals(typeof struct, "object"); assert_equals(typeof array, "object"); }, "typeof"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_throws_js(TypeError, () => struct.toString()); assert_equals(Object.prototype.toString.call(struct), "[object Object]"); assert_throws_js(TypeError, () => array.toString()); @@ -121,8 +149,8 @@ test(() => { }, "toString"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); assert_throws_js(TypeError, () => struct.valueOf()); assert_equals(Object.prototype.valueOf.call(struct), struct); assert_throws_js(TypeError, () => array.valueOf()); @@ -130,8 +158,8 @@ test(() => { }, "valueOf"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); const map = new Map(); map.set(struct, "struct"); map.set(array, "array"); @@ -140,8 +168,8 @@ test(() => { }, "GC objects as map keys"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); const set = new Set(); set.add(struct); set.add(array); @@ -150,11 +178,21 @@ test(() => { }, "GC objects as set element"); test(() => { - const struct = functions.makeStruct() - const array = functions.makeArray() + const struct = functions.makeStruct(); + const array = functions.makeArray(); const map = new WeakMap(); map.set(struct, "struct"); map.set(array, "array"); assert_equals(map.get(struct), "struct"); assert_equals(map.get(array), "array"); }, "GC objects as weak map keys"); + +test(() => { + const struct = functions.makeStruct(); + const array = functions.makeArray(); + const set = new WeakSet(); + set.add(struct); + set.add(array); + assert_true(set.has(struct)); + assert_true(set.has(array)); +}, "GC objects as weak set element"); diff --git a/test/js-api/wasm-module-builder.js b/test/js-api/wasm-module-builder.js index 9136256a98..19dbaa2be7 100644 --- a/test/js-api/wasm-module-builder.js +++ b/test/js-api/wasm-module-builder.js @@ -402,22 +402,32 @@ function GCInstr(opcode) { } // GC opcodes -let kExprStructNew = 0x01; -let kExprStructNewDefault = 0x02; +// Opcodes with "Canon" are opcode numbers from the specification. +// The non-"Canon" variants such as kExprStructNew are de-facto +// opcode numbers that are in use in some web engine implementations. +let kExprStructNewCanon = 0x01; +let kExprStructNewCanonDefault = 0x02; let kExprStructGet = 0x03; let kExprStructGetS = 0x04; let kExprStructGetU = 0x05; let kExprStructSet = 0x06; -let kExprArrayNew = 0x11; -let kExprArrayNewDefault = 0x12; +let kExprStructNew = 0x07; +let kExprStructNewDefault = 0x08; +let kExprArrayNewCanon = 0x11; +let kExprArrayNewCanonDefault = 0x12; let kExprArrayGet = 0x13; let kExprArrayGetS = 0x14; let kExprArrayGetU = 0x15; let kExprArraySet = 0x16; let kExprArrayLen = 0x17; -let kExprArrayNewFixed = 0x19; -let kExprArrayNewData = 0x1b; -let kExprArrayNewElem = 0x1c; +let kExprArrayNewCanonFixed = 0x19; +let kExprArrayNewFixed = 0x1a; +let kExprArrayNew = 0x1b; +let kExprArrayNewDefault = 0x1c; +let kExprArrayNewCanonData = 0x1b; +let kExprArrayNewCanonElem = 0x1c; +let kExprArrayNewData = 0x1d; +let kExprArrayNewElem = 0x1f; let kExprI31New = 0x20; let kExprI31GetS = 0x21; let kExprI31GetU = 0x22; From 920e6367a0320ef8e2a35ef8b0dff088d7ce32f9 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Fri, 8 Sep 2023 14:19:10 -0700 Subject: [PATCH 04/50] Rebase exported object test * Update opcodes to final versions in MVP document * Undid strict/non-strict behavior difference --- .../gc/exported-object.tentative.any.js | 24 +++---- test/js-api/wasm-module-builder.js | 70 ++++++++----------- 2 files changed, 39 insertions(+), 55 deletions(-) diff --git a/test/js-api/gc/exported-object.tentative.any.js b/test/js-api/gc/exported-object.tentative.any.js index 1d6be676e9..b572f14006 100644 --- a/test/js-api/gc/exported-object.tentative.any.js +++ b/test/js-api/gc/exported-object.tentative.any.js @@ -50,14 +50,10 @@ test(() => { test(() => { const struct = functions.makeStruct(); const array = functions.makeArray(); - struct.foo = 5; - assert_equals(struct.foo, undefined); - array.foo = 5; - assert_equals(array.foo, undefined); - struct[0] = 5; - assert_equals(struct[0], undefined); - array[0] = 5; - assert_equals(array[0], undefined); + assert_throws_js(TypeError, () => { struct.foo = 5; }); + assert_throws_js(TypeError, () => { array.foo = 5; }); + assert_throws_js(TypeError, () => { struct[0] = 5; }); + assert_throws_js(TypeError, () => { array[0] = 5; }); }, "property assignment (non-strict mode)"); test(() => { @@ -87,14 +83,10 @@ test(() => { test(() => { const struct = functions.makeStruct(); const array = functions.makeArray(); - const structNamesLength = Object.getOwnPropertyNames(struct).length; - const arrayNamesLength = Object.getOwnPropertyNames(array).length; - delete struct.foo; - delete struct[0]; - delete array.foo; - delete array[0]; - assert_equals(Object.getOwnPropertyNames(struct), structNamesLength); - assert_equals(Object.getOwnPropertyNames(array), arrayNamesLength); + assert_throws_js(TypeError, () => delete struct.foo); + assert_throws_js(TypeError, () => delete struct[0]); + assert_throws_js(TypeError, () => delete array.foo); + assert_throws_js(TypeError, () => delete array[0]); }, "delete (non-strict mode)"); test(() => { diff --git a/test/js-api/wasm-module-builder.js b/test/js-api/wasm-module-builder.js index 19dbaa2be7..e26e3118ba 100644 --- a/test/js-api/wasm-module-builder.js +++ b/test/js-api/wasm-module-builder.js @@ -402,45 +402,37 @@ function GCInstr(opcode) { } // GC opcodes -// Opcodes with "Canon" are opcode numbers from the specification. -// The non-"Canon" variants such as kExprStructNew are de-facto -// opcode numbers that are in use in some web engine implementations. -let kExprStructNewCanon = 0x01; -let kExprStructNewCanonDefault = 0x02; -let kExprStructGet = 0x03; -let kExprStructGetS = 0x04; -let kExprStructGetU = 0x05; -let kExprStructSet = 0x06; -let kExprStructNew = 0x07; -let kExprStructNewDefault = 0x08; -let kExprArrayNewCanon = 0x11; -let kExprArrayNewCanonDefault = 0x12; -let kExprArrayGet = 0x13; -let kExprArrayGetS = 0x14; -let kExprArrayGetU = 0x15; -let kExprArraySet = 0x16; -let kExprArrayLen = 0x17; -let kExprArrayNewCanonFixed = 0x19; -let kExprArrayNewFixed = 0x1a; -let kExprArrayNew = 0x1b; -let kExprArrayNewDefault = 0x1c; -let kExprArrayNewCanonData = 0x1b; -let kExprArrayNewCanonElem = 0x1c; -let kExprArrayNewData = 0x1d; -let kExprArrayNewElem = 0x1f; -let kExprI31New = 0x20; -let kExprI31GetS = 0x21; -let kExprI31GetU = 0x22; -let kExprRefTest = 0x40; -let kExprRefTestNull = 0x48; -let kExprRefCast = 0x41; -let kExprRefCastNull = 0x49; -let kExprBrOnCast = 0x42; -let kExprBrOnCastNull = 0x4a; -let kExprBrOnCastFail = 0x43; -let kExprBrOnCastFailNull = 0x4b; -let kExprExternInternalize = 0x70; -let kExprExternExternalize = 0x71; +let kExprStructNew = 0x00; +let kExprStructNewDefault = 0x01; +let kExprStructGet = 0x02; +let kExprStructGetS = 0x03; +let kExprStructGetU = 0x04; +let kExprStructSet = 0x05; +let kExprArrayNew = 0x06; +let kExprArrayNewDefault = 0x07; +let kExprArrayNewFixed = 0x08; +let kExprArrayNewData = 0x09; +let kExprArrayNewElem = 0x0a; +let kExprArrayGet = 0x0b; +let kExprArrayGetS = 0x0c; +let kExprArrayGetU = 0x0d; +let kExprArraySet = 0x0e; +let kExprArrayLen = 0x0f; +let kExprArrayFill = 0x10; +let kExprArrayCopy = 0x11; +let kExprArrayInitData = 0x12; +let kExprArrayInitElem = 0x13; +let kExprRefTest = 0x14; +let kExprRefTestNull = 0x15; +let kExprRefCast = 0x16; +let kExprRefCastNull = 0x17; +let kExprBrOnCast = 0x18; +let kExprBrOnCastFail = 0x19; +let kExprExternInternalize = 0x1a; +let kExprExternExternalize = 0x1b; +let kExprI31New = 0x1c; +let kExprI31GetS = 0x1d; +let kExprI31GetU = 0x1e; // Numeric opcodes. let kExprMemoryInit = 0x08; From 25fb7d40b29c2e843188cb7cb71427863197804f Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Fri, 15 Sep 2023 11:40:25 -0700 Subject: [PATCH 05/50] Fix wasm-module-builder for some missed updates * The exported-object test should run on V8 now * Non-GC JS API tests should run correctly too --- test/js-api/wasm-module-builder.js | 68 +++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/test/js-api/wasm-module-builder.js b/test/js-api/wasm-module-builder.js index e26e3118ba..4349be2cc7 100644 --- a/test/js-api/wasm-module-builder.js +++ b/test/js-api/wasm-module-builder.js @@ -77,8 +77,8 @@ let kWasmAnyFunctionTypeForm = 0x70; let kWasmStructTypeForm = 0x5f; let kWasmArrayTypeForm = 0x5e; let kWasmSubtypeForm = 0x50; -let kWasmSubtypeFinalForm = 0x4e; -let kWasmRecursiveTypeGroupForm = 0x4f; +let kWasmSubtypeFinalForm = 0x4f; +let kWasmRecursiveTypeGroupForm = 0x4e; let kNoSuperType = 0xFFFFFFFF; @@ -104,11 +104,37 @@ let kWasmI64 = 0x7e; let kWasmF32 = 0x7d; let kWasmF64 = 0x7c; let kWasmS128 = 0x7b; -let kWasmAnyRef = 0x6f; -let kWasmAnyFunc = 0x70; -let kWasmRefNull = 0x6c; -let kWasmRef = 0x6b; +// These are defined as negative integers to distinguish them from positive type +// indices. +let kWasmNullFuncRef = -0x0d; +let kWasmNullExternRef = -0x0e; +let kWasmNullRef = -0x0f; +let kWasmFuncRef = -0x10; +let kWasmAnyFunc = kWasmFuncRef; // Alias named as in the JS API spec +let kWasmExternRef = -0x11; +let kWasmAnyRef = -0x12; +let kWasmEqRef = -0x13; +let kWasmI31Ref = -0x14; +let kWasmStructRef = -0x15; +let kWasmArrayRef = -0x16; + +// Use the positive-byte versions inside function bodies. +let kLeb128Mask = 0x7f; +let kFuncRefCode = kWasmFuncRef & kLeb128Mask; +let kAnyFuncCode = kFuncRefCode; // Alias named as in the JS API spec +let kExternRefCode = kWasmExternRef & kLeb128Mask; +let kAnyRefCode = kWasmAnyRef & kLeb128Mask; +let kEqRefCode = kWasmEqRef & kLeb128Mask; +let kI31RefCode = kWasmI31Ref & kLeb128Mask; +let kNullExternRefCode = kWasmNullExternRef & kLeb128Mask; +let kNullFuncRefCode = kWasmNullFuncRef & kLeb128Mask; +let kStructRefCode = kWasmStructRef & kLeb128Mask; +let kArrayRefCode = kWasmArrayRef & kLeb128Mask; +let kNullRefCode = kWasmNullRef & kLeb128Mask; + +let kWasmRefNull = 0x63; +let kWasmRef = 0x64; function wasmRefNullType(heap_type) { return {opcode: kWasmRefNull, heap_type: heap_type}; } @@ -162,14 +188,14 @@ let kSig_v_f = makeSig([kWasmF32], []); let kSig_f_f = makeSig([kWasmF32], [kWasmF32]); let kSig_f_d = makeSig([kWasmF64], [kWasmF32]); let kSig_d_d = makeSig([kWasmF64], [kWasmF64]); -let kSig_r_r = makeSig([kWasmAnyRef], [kWasmAnyRef]); +let kSig_r_r = makeSig([kWasmExternRef], [kWasmExternRef]); let kSig_a_a = makeSig([kWasmAnyFunc], [kWasmAnyFunc]); -let kSig_i_r = makeSig([kWasmAnyRef], [kWasmI32]); -let kSig_v_r = makeSig([kWasmAnyRef], []); +let kSig_i_r = makeSig([kWasmExternRef], [kWasmI32]); +let kSig_v_r = makeSig([kWasmExternRef], []); let kSig_v_a = makeSig([kWasmAnyFunc], []); -let kSig_v_rr = makeSig([kWasmAnyRef, kWasmAnyRef], []); +let kSig_v_rr = makeSig([kWasmExternRef, kWasmExternRef], []); let kSig_v_aa = makeSig([kWasmAnyFunc, kWasmAnyFunc], []); -let kSig_r_v = makeSig([], [kWasmAnyRef]); +let kSig_r_v = makeSig([], [kWasmExternRef]); let kSig_a_v = makeSig([], [kWasmAnyFunc]); let kSig_a_i = makeSig([kWasmI32], [kWasmAnyFunc]); @@ -857,8 +883,8 @@ class WasmModuleBuilder { } addTable(type, initial_size, max_size = undefined) { - if (type != kWasmAnyRef && type != kWasmAnyFunc) { - throw new Error('Tables must be of type kWasmAnyRef or kWasmAnyFunc'); + if (type != kWasmExternRef && type != kWasmAnyFunc) { + throw new Error('Tables must be of type kWasmExternRef or kWasmAnyFunc'); } let table = new WasmTableBuilder(this, type, initial_size, max_size); table.index = this.tables.length + this.num_imported_tables; @@ -1084,9 +1110,9 @@ class WasmModuleBuilder { section.emit_string(imp.name || ''); section.emit_u8(imp.kind); if (imp.kind == kExternalFunction) { - section.emit_u32v(imp.type); + section.emit_u32v(imp.type_index); } else if (imp.kind == kExternalGlobal) { - section.emit_u32v(imp.type); + section.emit_type(imp.type); section.emit_u8(imp.mutable); } else if (imp.kind == kExternalMemory) { var has_max = (typeof imp.maximum) != "undefined"; @@ -1099,7 +1125,7 @@ class WasmModuleBuilder { section.emit_u32v(imp.initial); // initial if (has_max) section.emit_u32v(imp.maximum); // maximum } else if (imp.kind == kExternalTable) { - section.emit_u8(imp.type); + section.emit_type(imp.type); var has_max = (typeof imp.maximum) != "undefined"; section.emit_u8(has_max ? 1 : 0); // flags section.emit_u32v(imp.initial); // initial @@ -1131,7 +1157,7 @@ class WasmModuleBuilder { binary.emit_section(kTableSectionCode, section => { section.emit_u32v(wasm.tables.length); for (let table of wasm.tables) { - section.emit_u8(table.type); + section.emit_type(table.type); section.emit_u8(table.has_max); section.emit_u32v(table.initial_size); if (table.has_max) section.emit_u32v(table.max_size); @@ -1163,7 +1189,7 @@ class WasmModuleBuilder { binary.emit_section(kGlobalSectionCode, section => { section.emit_u32v(wasm.globals.length); for (let global of wasm.globals) { - section.emit_u8(global.type); + section.emit_type(global.type); section.emit_u8(global.mutable); if ((typeof global.init_index) == "undefined") { // Emit a constant initializer. @@ -1183,7 +1209,7 @@ class WasmModuleBuilder { section.emit_bytes(wasmF64Const(global.init)); break; case kWasmAnyFunc: - case kWasmAnyRef: + case kWasmExternRef: if (global.function_index !== undefined) { section.emit_u8(kExprRefFunc); section.emit_u32v(global.function_index); @@ -1327,7 +1353,7 @@ class WasmModuleBuilder { local_decls.push({count: l.s128_count, type: kWasmS128}); } if (l.anyref_count > 0) { - local_decls.push({count: l.anyref_count, type: kWasmAnyRef}); + local_decls.push({count: l.anyref_count, type: kWasmExternRef}); } if (l.anyfunc_count > 0) { local_decls.push({count: l.anyfunc_count, type: kWasmAnyFunc}); @@ -1337,7 +1363,7 @@ class WasmModuleBuilder { header.emit_u32v(local_decls.length); for (let decl of local_decls) { header.emit_u32v(decl.count); - header.emit_u8(decl.type); + header.emit_type(decl.type); } section.emit_u32v(header.length + func.body.length); From eee7a831bdf8d86e810424fac921449444a28853 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 25 Sep 2023 13:45:04 +0200 Subject: [PATCH 06/50] [spec] A few fixes --- document/core/exec/instructions.rst | 32 +++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index f2c2fb0416..187f45feb1 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -1484,13 +1484,20 @@ Where: 2. Pop the value :math:`\reff` from the stack. -3. Let :math:`\reff'` be the reference value :math:`(\REFEXTERN~\reff)`. +3. If :math:`\reff` is :math:`\REFNULL~\X{ht}`, then: + + a. Push the reference value :math:`(\REFNULL~\EXTERN)` to the stack. + +4. Else: + + a. Let :math:`\reff'` be the reference value :math:`(\REFEXTERN~\reff)`. -5. Push the reference value :math:`\reff'` to the stack. + b. Push the reference value :math:`\reff'` to the stack. .. math:: \begin{array}{lcl@{\qquad}l} - \reff~\EXTERNEXTERNALIZE &\stepto& (\REFEXTERN~\reff) + (\REFNULL \X{ht})~\EXTERNEXTERNALIZE &\stepto& (\REFNULL~\EXTERN) \\ + \reff~\EXTERNEXTERNALIZE &\stepto& (\REFEXTERN~\reff) & (\iff \reff \neq (\REFNULL \X{ht})) \\ \end{array} @@ -1503,15 +1510,22 @@ Where: 2. Pop the value :math:`\reff` from the stack. -3. Assert: due to :ref:`validation `, a :math:`\reff` is an :ref:`external reference `. +3. If :math:`\reff` is :math:`\REFNULL~\X{ht}`, then: + + a. Push the reference value :math:`(\REFNULL~\ANY)` to the stack. + +4. Else: + + a. Assert: due to :ref:`validation `, a :math:`\reff` is an :ref:`external reference `. -4. Let :math:`\REFEXTERN~\reff'` be the reference value :math:`\reff`. + b. Let :math:`\REFEXTERN~\reff'` be the reference value :math:`\reff`. -5. Push the reference value :math:`\reff'` to the stack. + c. Push the reference value :math:`\reff'` to the stack. .. math:: \begin{array}{lcl@{\qquad}l} - (\REFEXTERN~\reff)~\EXTERNINTERNALIZE &\stepto& \reff + (\REFNULL \X{ht})~\EXTERNINTERNALIZE &\stepto& (\REFNULL~\ANY) \\ + (\REFEXTERN~\reff)~\EXTERNINTERNALIZE &\stepto& \reff \\ \end{array} @@ -4125,7 +4139,7 @@ Control Instructions .. math:: \begin{array}{lcl@{\qquad}l} - S; \reff~(\BRONCAST~l~\X{rt}_1~X{rt}_2) &\stepto& (\BR~l) + S; \reff~(\BRONCAST~l~\X{rt}_1~X{rt}_2) &\stepto& \reff~(\BR~l) & (\iff S \vdashval \reff : \X{rt} \land \vdashreftypematch \X{rt} \matchesreftype \insttype_{F.\AMODULE}(\X{rt}_2)) \\ S; \reff~(\BRONCAST~l~\X{rt}_1~\X{rt}_2) &\stepto& \reff @@ -4163,7 +4177,7 @@ Control Instructions S; \reff~(\BRONCASTFAIL~l~\X{rt}_1~X{rt}_2) &\stepto& \reff & (\iff S \vdashval \reff : \X{rt} \land \vdashreftypematch \X{rt} \matchesreftype \insttype_{F.\AMODULE}(\X{rt}_2)) \\ - S; \reff~(\BRONCASTFAIL~l~\X{rt}_1~\X{rt}_2) &\stepto& (\BR~l) + S; \reff~(\BRONCASTFAIL~l~\X{rt}_1~\X{rt}_2) &\stepto& \reff~(\BR~l) & (\otherwise) \\ \end{array} From c70e2aadf42aa5df9f3b325d0820a57acee7ba18 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Thu, 5 Oct 2023 07:45:01 +0200 Subject: [PATCH 07/50] [spec] Extend definition of constants --- document/core/valid/instructions.rst | 64 +++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index 7c7ce45da3..871df46de7 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -2453,8 +2453,24 @@ Constant Expressions * or of the form :math:`\REFNULL`, + * or of the form :math:`\REFI31`, + * or of the form :math:`\REFFUNC~x`, + * or of the form :math:`\STRUCTNEW~x`, + + * or of the form :math:`\STRUCTNEWDEFAULT~x`, + + * or of the form :math:`\ARRAYNEW~x`, + + * or of the form :math:`\ARRAYNEWDEFAULT~x`, + + * or of the form :math:`\ARRAYNEWFIXED~x`, + + * or of the form :math:`\EXTERNINTERNALIZE`, + + * or of the form :math:`\EXTERNEXTERNALIZE`, + * or of the form :math:`\GLOBALGET~x`, in which case :math:`C.\CGLOBALS[x]` must be a :ref:`global type ` of the form :math:`\CONST~t`. .. math:: @@ -2470,23 +2486,67 @@ Constant Expressions C \vdashinstrconst t.\CONST~c \const } \qquad + \frac{ + C.\CGLOBALS[x] = \CONST~t + }{ + C \vdashinstrconst \GLOBALGET~x \const + } + +.. math:: \frac{ }{ C \vdashinstrconst \REFNULL~t \const } \qquad \frac{ + }{ + C \vdashinstrconst \REFI31 \const + } + \qquad + \frac{ }{ C \vdashinstrconst \REFFUNC~x \const } .. math:: \frac{ - C.\CGLOBALS[x] = \CONST~t }{ - C \vdashinstrconst \GLOBALGET~x \const + C \vdashinstrconst \STRUCTNEW~x \const + } + \qquad + \frac{ + }{ + C \vdashinstrconst \STRUCTNEWDEFAULT~x \const + } + +.. math:: + \frac{ + }{ + C \vdashinstrconst \ARRAYNEW~x \const + } + \qquad + \frac{ + }{ + C \vdashinstrconst \ARRAYNEWDEFAULT~x \const + } + \qquad + \frac{ + }{ + C \vdashinstrconst \ARRAYNEWFIXED~x \const + } + +.. math:: + \frac{ + }{ + C \vdashinstrconst \EXTERNINTERNALIZE \const + } + \qquad + \frac{ + }{ + C \vdashinstrconst \EXTERNEXTERNALIZE \const } + .. note:: Currently, constant expressions occurring in :ref:`globals `, :ref:`element `, or :ref:`data ` segments are further constrained in that contained |GLOBALGET| instructions are only allowed to refer to *imported* globals. This is enforced in the :ref:`validation rule for modules ` by constraining the context :math:`C` accordingly. From 5bca3f7de6ad117a0d4349c138b5e32981c88d2f Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Thu, 5 Oct 2023 08:26:07 +0200 Subject: [PATCH 08/50] Rename extern.in/externalize --- document/core/appendix/changes.rst | 4 +-- document/core/appendix/index-instructions.py | 4 +-- document/core/binary/instructions.rst | 8 +++--- document/core/exec/instructions.rst | 28 ++++++++++---------- document/core/syntax/instructions.rst | 10 +++---- document/core/syntax/types.rst | 2 +- document/core/text/instructions.rst | 8 +++--- document/core/util/macros.def | 4 +-- document/core/valid/instructions.rst | 16 +++++------ interpreter/binary/decode.ml | 4 +-- interpreter/syntax/operators.ml | 4 +-- interpreter/text/arrange.ml | 6 ++--- interpreter/text/lexer.mll | 4 +-- proposals/gc/MVP.md | 18 ++++++------- test/core/gc/br_on_cast.wast | 2 +- test/core/gc/br_on_cast_fail.wast | 2 +- test/core/gc/extern.wast | 10 +++---- test/core/gc/ref_cast.wast | 2 +- test/core/gc/ref_test.wast | 10 +++---- 19 files changed, 73 insertions(+), 73 deletions(-) diff --git a/document/core/appendix/changes.rst b/document/core/appendix/changes.rst index 2817d5a5da..56bb449202 100644 --- a/document/core/appendix/changes.rst +++ b/document/core/appendix/changes.rst @@ -188,9 +188,9 @@ Added managed reference types [#proposal-gc]_. * New :ref:`reference instructions ` for :ref:`array types `: |ARRAYNEW|, |ARRAYNEWDEFAULT|, |ARRAYNEWFIXED|, |ARRAYNEWDATA|, |ARRAYNEWELEM|, :math:`\ARRAYGET\K{\_}\sx^?`, |ARRAYSET|, |ARRAYLEN|, |ARRAYFILL|, |ARRAYCOPY|, |ARRAYINITDATA|, |ARRAYINITELEM| -* New :ref:`reference instructions ` for converting :ref:`host types `: |EXTERNINTERNALIZE|, |EXTERNEXTERNALIZE| +* New :ref:`reference instructions ` for converting :ref:`host types `: |ANYCONVERTEXTERN|, |EXTERNCONVERTANY| -* Extended set of :ref:`constant instructions ` with |REFI31|, |STRUCTNEW|, |STRUCTNEWDEFAULT|, |ARRAYNEW|, |ARRAYNEWDEFAULT|, |ARRAYNEWFIXED|, |EXTERNINTERNALIZE|, |EXTERNEXTERNALIZE|, and |GLOBALGET| for any previously declared immutable :ref:`global ` +* Extended set of :ref:`constant instructions ` with |REFI31|, |STRUCTNEW|, |STRUCTNEWDEFAULT|, |ARRAYNEW|, |ARRAYNEWDEFAULT|, |ARRAYNEWFIXED|, |ANYCONVERTEXTERN|, |EXTERNCONVERTANY|, and |GLOBALGET| for any previously declared immutable :ref:`global ` .. [#proposal-signext] diff --git a/document/core/appendix/index-instructions.py b/document/core/appendix/index-instructions.py index edcddb6faf..d4a36d0c20 100755 --- a/document/core/appendix/index-instructions.py +++ b/document/core/appendix/index-instructions.py @@ -346,8 +346,8 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\REFCAST~(\REF~\NULL~t)', r'\hex{FB}~\hex{17}', r"[(\REF~\NULL~t')] \to [(\REF~\NULL~t)]", r'valid-ref.cast', r'exec-ref.cast'), Instruction(r'\BRONCAST~t_1~t_2', r'\hex{FB}~\hex{18}', r'[t_1] \to [t_1\reftypediff t_2]', r'valid-br_on_cast', r'exec-br_on_cast'), Instruction(r'\BRONCASTFAIL~t_1~t_2', r'\hex{FB}~\hex{19}', r'[t_1] \to [t_2]', r'valid-br_on_cast_fail', r'exec-br_on_cast_fail'), - Instruction(r'\EXTERNINTERNALIZE', r'\hex{FB}~\hex{1A}', r'[\EXTERNREF] \to [\ANYREF]', r'valid-extern.internalize', r'exec-extern.internalize'), - Instruction(r'\EXTERNEXTERNALIZE', r'\hex{FB}~\hex{1B}', r'[\ANYREF] \to [\EXTERNREF]', r'valid-extern.externalize', r'exec-extern.externalize'), + Instruction(r'\ANYCONVERTEXTERN', r'\hex{FB}~\hex{1A}', r'[\EXTERNREF] \to [\ANYREF]', r'valid-any.convert_extern', r'exec-any.convert_extern'), + Instruction(r'\EXTERNCONVERTANY', r'\hex{FB}~\hex{1B}', r'[\ANYREF] \to [\EXTERNREF]', r'valid-extern.convert_any', r'exec-extern.convert_any'), Instruction(r'\REFI31', r'\hex{FB}~\hex{1C}', r'[\I32] \to [\I31REF]', r'valid-ref.i31', r'exec-ref.i31'), Instruction(r'\I31GETS', r'\hex{FB}~\hex{1D}', r'[\I31REF] \to [\I32]', r'valid-i31.get_sx', r'exec-i31.get_sx'), Instruction(r'\I31GETU', r'\hex{FB}~\hex{1E}', r'[\I31REF] \to [\I32]', r'valid-i31.get_sx', r'exec-i31.get_sx'), diff --git a/document/core/binary/instructions.rst b/document/core/binary/instructions.rst index 53e32a3cd1..d4210be3c2 100644 --- a/document/core/binary/instructions.rst +++ b/document/core/binary/instructions.rst @@ -131,8 +131,8 @@ Generic :ref:`reference instructions ` are represented by sing .. _binary-i31.get_u: .. _binary-ref.test: .. _binary-ref.cast: -.. _binary-extern.internalize: -.. _binary-extern.externalize: +.. _binary-any.convert_extern: +.. _binary-extern.convert_any: .. math:: \begin{array}{llclll} @@ -166,8 +166,8 @@ Generic :ref:`reference instructions ` are represented by sing \hex{FB}~~21{:}\Bu32~~\X{ht}{:}\Bheaptype &\Rightarrow& \REFTEST~(\REF~\NULL~\X{ht}) \\ &&|& \hex{FB}~~22{:}\Bu32~~\X{ht}{:}\Bheaptype &\Rightarrow& \REFCAST~(\REF~\X{ht}) \\ &&|& \hex{FB}~~23{:}\Bu32~~\X{ht}{:}\Bheaptype &\Rightarrow& \REFCAST~(\REF~\NULL~\X{ht}) \\ &&|& - \hex{FB}~~26{:}\Bu32 &\Rightarrow& \EXTERNINTERNALIZE \\ &&|& - \hex{FB}~~27{:}\Bu32 &\Rightarrow& \EXTERNEXTERNALIZE \\ &&|& + \hex{FB}~~26{:}\Bu32 &\Rightarrow& \ANYCONVERTEXTERN \\ &&|& + \hex{FB}~~27{:}\Bu32 &\Rightarrow& \EXTERNCONVERTANY \\ &&|& \hex{FB}~~28{:}\Bu32 &\Rightarrow& \REFI31 \\ &&|& \hex{FB}~~29{:}\Bu32 &\Rightarrow& \I31GETS \\ &&|& \hex{FB}~~30{:}\Bu32 &\Rightarrow& \I31GETU \\ diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index f2c2fb0416..4ed1a876f5 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -1475,43 +1475,43 @@ Where: \end{array} -.. _exec-extern.externalize: +.. _exec-any.convert_extern: -:math:`\EXTERNEXTERNALIZE` -.......................... +:math:`\ANYCONVERTEXTERN` +......................... -1. Assert: due to :ref:`validation `, a :ref:`reference value ` is on the top of the stack. +1. Assert: due to :ref:`validation `, a :ref:`reference value ` is on the top of the stack. 2. Pop the value :math:`\reff` from the stack. -3. Let :math:`\reff'` be the reference value :math:`(\REFEXTERN~\reff)`. +3. Assert: due to :ref:`validation `, a :math:`\reff` is an :ref:`external reference `. + +4. Let :math:`\REFEXTERN~\reff'` be the reference value :math:`\reff`. 5. Push the reference value :math:`\reff'` to the stack. .. math:: \begin{array}{lcl@{\qquad}l} - \reff~\EXTERNEXTERNALIZE &\stepto& (\REFEXTERN~\reff) + (\REFEXTERN~\reff)~\ANYCONVERTEXTERN &\stepto& \reff \end{array} -.. _exec-extern.internalize: +.. _exec-extern.convert_any: -:math:`\EXTERNINTERNALIZE` -.......................... +:math:`\EXTERNCONVERTANY` +......................... -1. Assert: due to :ref:`validation `, a :ref:`reference value ` is on the top of the stack. +1. Assert: due to :ref:`validation `, a :ref:`reference value ` is on the top of the stack. 2. Pop the value :math:`\reff` from the stack. -3. Assert: due to :ref:`validation `, a :math:`\reff` is an :ref:`external reference `. - -4. Let :math:`\REFEXTERN~\reff'` be the reference value :math:`\reff`. +3. Let :math:`\reff'` be the reference value :math:`(\REFEXTERN~\reff)`. 5. Push the reference value :math:`\reff'` to the stack. .. math:: \begin{array}{lcl@{\qquad}l} - (\REFEXTERN~\reff)~\EXTERNINTERNALIZE &\stepto& \reff + \reff~\EXTERNCONVERTANY &\stepto& (\REFEXTERN~\reff) \end{array} diff --git a/document/core/syntax/instructions.rst b/document/core/syntax/instructions.rst index 1d6a61b71c..4c7baff6bc 100644 --- a/document/core/syntax/instructions.rst +++ b/document/core/syntax/instructions.rst @@ -485,8 +485,8 @@ while the latter performs a downcast and :ref:`traps ` if the operand's ty .. _syntax-ref.i31: .. _syntax-i31.get_s: .. _syntax-i31.get_u: -.. _syntax-extern.internalize: -.. _syntax-extern.externalize: +.. _syntax-any.convert_extern: +.. _syntax-extern.convert_any: .. _syntax-instr-struct: .. _syntax-instr-array: .. _syntax-instr-i31: @@ -521,8 +521,8 @@ Instructions in this group are concerned with creating and accessing :ref:`refer \ARRAYINITELEM~\typeidx~\elemidx \\&&|& \REFI31 \\&&|& \I31GET\K{\_}\sx \\&&|& - \EXTERNINTERNALIZE \\&&|& - \EXTERNEXTERNALIZE \\ + \ANYCONVERTEXTERN \\&&|& + \EXTERNCONVERTANY \\ \end{array} The instructions |STRUCTNEW| and |STRUCTNEWDEFAULT| allocate a new :ref:`structure `, initializing them either with operands or with default values. @@ -539,7 +539,7 @@ again allowing for different sign extension modes in the case of a :ref:`packed The instructions |REFI31| and :math:`\I31GET\K{\_}\sx` convert between type |I31| and an unboxed :ref:`scalar `. -The instructions |EXTERNINTERNALIZE| and |EXTERNEXTERNALIZE| allow lossless conversion between references represented as type :math:`(\REF~\NULL~\EXTERN)`| and as :math:`(\REF~\NULL~\ANY)`. +The instructions |ANYCONVERTEXTERN| and |EXTERNCONVERTANY| allow lossless conversion between references represented as type :math:`(\REF~\NULL~\EXTERN)`| and as :math:`(\REF~\NULL~\ANY)`. .. index:: ! parametric instruction, value type pair: abstract syntax; instruction diff --git a/document/core/syntax/types.rst b/document/core/syntax/types.rst index b935b0eb04..ded6e85d1c 100644 --- a/document/core/syntax/types.rst +++ b/document/core/syntax/types.rst @@ -93,7 +93,7 @@ There are three disjoint hierarchies of heap types: - *aggregate types* classify dynamically allocated *managed* data, such as *structures*, *arrays*, or *unboxed scalars*, - *external types* classify *external* references possibly owned by the :ref:`embedder `. -The values from the latter two hierarchies are interconvertible by ways of the |EXTERNINTERNALIZE| and |EXTERNEXTERNALIZE| instructions. +The values from the latter two hierarchies are interconvertible by ways of the |EXTERNCONVERTANY| and |ANYCONVERTEXTERN| instructions. That is, both type hierarchies are inhabited by an isomorphic set of values, but may have different, incompatible representations in practice. .. math:: diff --git a/document/core/text/instructions.rst b/document/core/text/instructions.rst index ad1c3e3e19..cf84de104b 100644 --- a/document/core/text/instructions.rst +++ b/document/core/text/instructions.rst @@ -204,8 +204,8 @@ Reference Instructions .. _text-i31.get_u: .. _text-ref.test: .. _text-ref.cast: -.. _text-extern.internalize: -.. _text-extern.externalize: +.. _text-any.convert_extern: +.. _text-extern.convert_any: .. math:: \begin{array}{llclll} @@ -240,8 +240,8 @@ Reference Instructions \text{ref.i31} &\Rightarrow& \REFI31 \\ &&|& \text{i31.get\_u} &\Rightarrow& \I31GETU \\ &&|& \text{i31.get\_s} &\Rightarrow& \I31GETS \\ &&|& - \text{extern.internalize} &\Rightarrow& \EXTERNINTERNALIZE \\ &&|& - \text{extern.externalize} &\Rightarrow& \EXTERNEXTERNALIZE \\ + \text{any.convert_extern} &\Rightarrow& \ANYCONVERTEXTERN \\ &&|& + \text{extern.convert_any} &\Rightarrow& \EXTERNCONVERTANY \\ \end{array} diff --git a/document/core/util/macros.def b/document/core/util/macros.def index 0f3aaa06a8..d290002f73 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -506,8 +506,8 @@ .. |I31GETS| mathdef:: \xref{syntax/instructions}{syntax-instr-i31}{\K{i31.get\_s}} .. |I31GETU| mathdef:: \xref{syntax/instructions}{syntax-instr-i31}{\K{i31.get\_u}} -.. |EXTERNINTERNALIZE| mathdef:: \xref{syntax/instructions}{syntax-instr-extern}{\K{extern.internalize}} -.. |EXTERNEXTERNALIZE| mathdef:: \xref{syntax/instructions}{syntax-instr-extern}{\K{extern.externalize}} +.. |ANYCONVERTEXTERN| mathdef:: \xref{syntax/instructions}{syntax-instr-extern}{\K{any.convert_extern}} +.. |EXTERNCONVERTANY| mathdef:: \xref{syntax/instructions}{syntax-instr-extern}{\K{extern.convert_any}} .. |CONST| mathdef:: \xref{syntax/instructions}{syntax-instr-numeric}{\K{const}} .. |EQZ| mathdef:: \xref{syntax/instructions}{syntax-instr-numeric}{\K{eqz}} diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index 7c7ce45da3..b8219f15b3 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -765,10 +765,10 @@ Scalar Reference Instructions External Reference Instructions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. _valid-extern.internalize: +.. _valid-any.convert_extern: -:math:`\EXTERNINTERNALIZE` -.......................... +:math:`\ANYCONVERTEXTERN` +......................... * The instruction is valid with type :math:`[(\REF~\NULL_1^?~\EXTERN)] \to [(\REF~\NULL_2^?~\ANY)]` for any :math:`\NULL_1^?` that equals :math:`\NULL_2^?`. @@ -776,13 +776,13 @@ External Reference Instructions \frac{ \NULL_1^? = \NULL_2^? }{ - C \vdashinstr \EXTERNINTERNALIZE : [(\REF~\NULL_1^?~\EXTERN)] \to [(\REF~\NULL_2^?~\ANY)] + C \vdashinstr \ANYCONVERTEXTERN : [(\REF~\NULL_1^?~\EXTERN)] \to [(\REF~\NULL_2^?~\ANY)] } -.. _valid-extern.externalize: +.. _valid-extern.convert_any: -:math:`\EXTERNEXTERNALIZE` -.......................... +:math:`\EXTERNCONVERTANY` +......................... * The instruction is valid with type :math:`[(\REF~\NULL_1^?~\ANY)] \to [(\REF~\NULL_2^?~\EXTERN)]` for any :math:`\NULL_1^?` that equals :math:`\NULL_2^?`. @@ -790,7 +790,7 @@ External Reference Instructions \frac{ \NULL_1^? = \NULL_2^? }{ - C \vdashinstr \EXTERNEXTERNALIZE : [(\REF~\NULL_1^?~\ANY)] \to [(\REF~\NULL_2^?~\EXTERN)] + C \vdashinstr \EXTERNCONVERTANY : [(\REF~\NULL_1^?~\ANY)] \to [(\REF~\NULL_2^?~\EXTERN)] } diff --git a/interpreter/binary/decode.ml b/interpreter/binary/decode.ml index f3ebce676c..91b2766a18 100644 --- a/interpreter/binary/decode.ml +++ b/interpreter/binary/decode.ml @@ -622,8 +622,8 @@ let rec instr s = let rt2 = ((if bit 1 flags then Null else NoNull), heap_type s) in (if opcode = 0x18l then br_on_cast else br_on_cast_fail) x rt1 rt2 - | 0x1al -> extern_internalize - | 0x1bl -> extern_externalize + | 0x1al -> any_convert_extern + | 0x1bl -> extern_convert_any | 0x1cl -> ref_i31 | 0x1dl -> i31_get_s diff --git a/interpreter/syntax/operators.ml b/interpreter/syntax/operators.ml index cc3d09b699..8ec72618cd 100644 --- a/interpreter/syntax/operators.ml +++ b/interpreter/syntax/operators.ml @@ -132,8 +132,8 @@ let array_fill x = ArrayFill x let array_init_data x y = ArrayInitData (x, y) let array_init_elem x y = ArrayInitElem (x, y) -let extern_internalize = ExternConvert Internalize -let extern_externalize = ExternConvert Externalize +let any_convert_extern = ExternConvert Internalize +let extern_convert_any = ExternConvert Externalize let i32_clz = Unary (I32 I32Op.Clz) let i32_ctz = Unary (I32 I32Op.Ctz) diff --git a/interpreter/text/arrange.ml b/interpreter/text/arrange.ml index 71590ef8a7..ebe714d874 100644 --- a/interpreter/text/arrange.ml +++ b/interpreter/text/arrange.ml @@ -464,8 +464,8 @@ let initop = function | Implicit -> "_default" let externop = function - | Internalize -> "internalize" - | Externalize -> "externalize" + | Internalize -> "any.convert_extern" + | Externalize -> "extern.convert_any" (* Expressions *) @@ -562,7 +562,7 @@ let rec instr e = | ArrayFill x -> "array.fill " ^ var x, [] | ArrayInitData (x, y) -> "array.init_data " ^ var x ^ " " ^ var y, [] | ArrayInitElem (x, y) -> "array.init_elem " ^ var x ^ " " ^ var y, [] - | ExternConvert op -> "extern." ^ externop op, [] + | ExternConvert op -> externop op, [] | Const n -> constop n.it ^ " " ^ num n, [] | Test op -> testop op, [] | Compare op -> relop op, [] diff --git a/interpreter/text/lexer.mll b/interpreter/text/lexer.mll index 44c22423cb..4e3e27bc4e 100644 --- a/interpreter/text/lexer.mll +++ b/interpreter/text/lexer.mll @@ -345,8 +345,8 @@ rule token = parse | "array.init_data" -> ARRAY_INIT_DATA | "array.init_elem" -> ARRAY_INIT_ELEM - | "extern.internalize" -> EXTERN_CONVERT extern_internalize - | "extern.externalize" -> EXTERN_CONVERT extern_externalize + | "any.convert_extern" -> EXTERN_CONVERT any_convert_extern + | "extern.convert_any" -> EXTERN_CONVERT extern_convert_any | "i32.clz" -> UNARY i32_clz | "i32.ctz" -> UNARY i32_ctz diff --git a/proposals/gc/MVP.md b/proposals/gc/MVP.md index e752a5837d..6d9589aca7 100644 --- a/proposals/gc/MVP.md +++ b/proposals/gc/MVP.md @@ -641,17 +641,17 @@ In particular, `ref.null` is typed as before, despite the introduction of `none` #### External conversion -* `extern.internalize` converts an external value into the internal representation - - `extern.internalize : [(ref null1? extern)] -> [(ref null2? any)]` +* `any.convert_extern` converts an external value into the internal representation + - `any.convert_extern : [(ref null1? extern)] -> [(ref null2? any)]` - iff `null1? = null2?` - this is a *constant instruction* - - note: this succeeds for all values, composing this with `extern.externalize` (in either order) yields the original value + - note: this succeeds for all values, composing this with `extern.convert_any` (in either order) yields the original value -* `extern.externalize` converts an internal value into the external representation - - `extern.externalize : [(ref null1? any)] -> [(ref null2? extern)]` +* `extern.convert_any` converts an internal value into the external representation + - `extern.convert_any : [(ref null1? any)] -> [(ref null2? extern)]` - iff `null1? = null2?` - this is a *constant instruction* - - note: this succeeds for all values; moreover, composing this with `extern.internalize` (in either order) yields the original value + - note: this succeeds for all values; moreover, composing this with `any.convert_extern` (in either order) yields the original value #### Casts @@ -707,7 +707,7 @@ In order to allow RTTs to be initialised as globals, the following extensions ar * `struct.new` and `struct.new_default` are constant instructions * `array.new`, `array.new_default`, and `array.new_fixed` are constant instructions - Note: `array.new_data` and `array.new_elem` are not for the time being, see above -* `extern.internalize` and `extern.externalize` are constant instructions +* `any.convert_extern` and `extern.convert_any` are constant instructions * `global.get` is a constant instruction and can access preceding (immutable) global definitions, not just imports as in the MVP @@ -832,8 +832,8 @@ The opcode for heap types is encoded as an `s33`. | 0xfb17 | `ref.cast (ref null ht)` | `ht : heaptype` | | 0xfb18 | `br_on_cast $l (ref null1? ht1) (ref null2? ht2)` | `flags : u8`, `$l : labelidx`, `ht1 : heaptype`, `ht2 : heaptype` | | 0xfb19 | `br_on_cast_fail $l (ref null1? ht1) (ref null2? ht2)` | `flags : u8`, `$l : labelidx`, `ht1 : heaptype`, `ht2 : heaptype` | -| 0xfb1a | `extern.internalize` | -| 0xfb1b | `extern.externalize` | +| 0xfb1a | `any.convert_extern` | +| 0xfb1b | `extern.convert_any` | | 0xfb1c | `ref.i31` | | 0xfb1d | `i31.get_s` | | 0xfb1e | `i31.get_u` | diff --git a/test/core/gc/br_on_cast.wast b/test/core/gc/br_on_cast.wast index ab33b1b02c..37aec62d19 100644 --- a/test/core/gc/br_on_cast.wast +++ b/test/core/gc/br_on_cast.wast @@ -15,7 +15,7 @@ (table.set (i32.const 1) (ref.i31 (i32.const 7))) (table.set (i32.const 2) (struct.new $st (i32.const 6))) (table.set (i32.const 3) (array.new $at (i32.const 5) (i32.const 3))) - (table.set (i32.const 4) (extern.internalize (local.get $x))) + (table.set (i32.const 4) (any.convert_extern (local.get $x))) ) (func (export "br_on_null") (param $i i32) (result i32) diff --git a/test/core/gc/br_on_cast_fail.wast b/test/core/gc/br_on_cast_fail.wast index 1096079d21..f5559a6110 100644 --- a/test/core/gc/br_on_cast_fail.wast +++ b/test/core/gc/br_on_cast_fail.wast @@ -15,7 +15,7 @@ (table.set (i32.const 1) (ref.i31 (i32.const 7))) (table.set (i32.const 2) (struct.new $st (i32.const 6))) (table.set (i32.const 3) (array.new $at (i32.const 5) (i32.const 3))) - (table.set (i32.const 4) (extern.internalize (local.get $x))) + (table.set (i32.const 4) (any.convert_extern (local.get $x))) ) (func (export "br_on_non_null") (param $i i32) (result i32) diff --git a/test/core/gc/extern.wast b/test/core/gc/extern.wast index 0f2fa8ec6a..abf31669eb 100644 --- a/test/core/gc/extern.wast +++ b/test/core/gc/extern.wast @@ -13,21 +13,21 @@ (table.set (i32.const 1) (ref.i31 (i32.const 7))) (table.set (i32.const 2) (struct.new_default $st)) (table.set (i32.const 3) (array.new_default $at (i32.const 0))) - (table.set (i32.const 4) (extern.internalize (local.get $x))) + (table.set (i32.const 4) (any.convert_extern (local.get $x))) ) (func (export "internalize") (param externref) (result anyref) - (extern.internalize (local.get 0)) + (any.convert_extern (local.get 0)) ) (func (export "externalize") (param anyref) (result externref) - (extern.externalize (local.get 0)) + (extern.convert_any (local.get 0)) ) (func (export "externalize-i") (param i32) (result externref) - (extern.externalize (table.get (local.get 0))) + (extern.convert_any (table.get (local.get 0))) ) (func (export "externalize-ii") (param i32) (result anyref) - (extern.internalize (extern.externalize (table.get (local.get 0)))) + (any.convert_extern (extern.convert_any (table.get (local.get 0)))) ) ) diff --git a/test/core/gc/ref_cast.wast b/test/core/gc/ref_cast.wast index 43a9587d80..8e35431193 100644 --- a/test/core/gc/ref_cast.wast +++ b/test/core/gc/ref_cast.wast @@ -15,7 +15,7 @@ (table.set (i32.const 1) (ref.i31 (i32.const 7))) (table.set (i32.const 2) (struct.new_default $st)) (table.set (i32.const 3) (array.new_default $at (i32.const 0))) - (table.set (i32.const 4) (extern.internalize (local.get $x))) + (table.set (i32.const 4) (any.convert_extern (local.get $x))) (table.set (i32.const 5) (ref.null i31)) (table.set (i32.const 6) (ref.null struct)) (table.set (i32.const 7) (ref.null none)) diff --git a/test/core/gc/ref_test.wast b/test/core/gc/ref_test.wast index 9a34c84680..39c2def151 100644 --- a/test/core/gc/ref_test.wast +++ b/test/core/gc/ref_test.wast @@ -19,8 +19,8 @@ (table.set $ta (i32.const 3) (ref.i31 (i32.const 7))) (table.set $ta (i32.const 4) (struct.new_default $st)) (table.set $ta (i32.const 5) (array.new_default $at (i32.const 0))) - (table.set $ta (i32.const 6) (extern.internalize (local.get $x))) - (table.set $ta (i32.const 7) (extern.internalize (ref.null extern))) + (table.set $ta (i32.const 6) (any.convert_extern (local.get $x))) + (table.set $ta (i32.const 7) (any.convert_extern (ref.null extern))) (table.set $tf (i32.const 0) (ref.null nofunc)) (table.set $tf (i32.const 1) (ref.null func)) @@ -29,9 +29,9 @@ (table.set $te (i32.const 0) (ref.null noextern)) (table.set $te (i32.const 1) (ref.null extern)) (table.set $te (i32.const 2) (local.get $x)) - (table.set $te (i32.const 3) (extern.externalize (ref.i31 (i32.const 8)))) - (table.set $te (i32.const 4) (extern.externalize (struct.new_default $st))) - (table.set $te (i32.const 5) (extern.externalize (ref.null any))) + (table.set $te (i32.const 3) (extern.convert_any (ref.i31 (i32.const 8)))) + (table.set $te (i32.const 4) (extern.convert_any (struct.new_default $st))) + (table.set $te (i32.const 5) (extern.convert_any (ref.null any))) ) (func (export "ref_test_null_data") (param $i i32) (result i32) From 83f9144f87dea0beda894413065b269d3596bf5a Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Fri, 6 Oct 2023 00:08:20 +0200 Subject: [PATCH 09/50] Fix cross-eyed merge --- document/core/valid/instructions.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index c0b0664320..2d76bf1a0a 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -2467,9 +2467,9 @@ Constant Expressions * or of the form :math:`\ARRAYNEWFIXED~x`, - * or of the form :math:`\EXTERNINTERNALIZE`, + * or of the form :math:`\ANYCONVERTEXTERN`, - * or of the form :math:`\EXTERNEXTERNALIZE`, + * or of the form :math:`\EXTERNCONVERTANY`, * or of the form :math:`\GLOBALGET~x`, in which case :math:`C.\CGLOBALS[x]` must be a :ref:`global type ` of the form :math:`\CONST~t`. @@ -2538,12 +2538,12 @@ Constant Expressions .. math:: \frac{ }{ - C \vdashinstrconst \EXTERNINTERNALIZE \const + C \vdashinstrconst \ANYCONVERTEXTERN \const } \qquad \frac{ }{ - C \vdashinstrconst \EXTERNEXTERNALIZE \const + C \vdashinstrconst \EXTERNCONVERTANY \const } From e19a1595592ac7c2db41a50bee93c2bc25a1f28c Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Fri, 6 Oct 2023 13:43:33 +0200 Subject: [PATCH 10/50] Add missing init expression to table text format --- document/core/text/modules.rst | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/document/core/text/modules.rst b/document/core/text/modules.rst index 84364dc572..0e13e19221 100644 --- a/document/core/text/modules.rst +++ b/document/core/text/modules.rst @@ -246,7 +246,7 @@ Functions can be defined as :ref:`imports ` or :ref:`exports `. .. math:: \begin{array}{llclll} \production{table} & \Ttable_I &::=& - \text{(}~\text{table}~~\Tid^?~~\X{tt}{:}\Ttabletype_I~\text{)} - &\Rightarrow& \{ \TTYPE~\X{tt} \} \\ + \text{(}~\text{table}~~\Tid^?~~\X{tt}{:}\Ttabletype_I~~e{:}\Texpr_I~\text{)} + &\Rightarrow& \{ \TTYPE~\X{tt}, \TINIT~e \} \\ \end{array} +.. index:: reference type, heap type .. index:: import, name pair: text format; import .. index:: export, name, index, table index @@ -276,6 +277,18 @@ Table definitions can bind a symbolic :ref:`table identifier `. Abbreviations ............. +A table's initialization :ref:`expression ` can be omitted, in which case it defaults to :math:`\REFNULL`: + +.. math:: + \begin{array}{llclll} + \production{module field} & + \text{(}~\text{table}~~\Tid^?~~\Ttabletype~\text{)} + &\equiv& + \text{(}~\text{table}~~\Tid^?~~\Ttabletype~~\text{(}~\REFNULL~\X{ht}~\text{)}~\text{)} + \\ &&& \qquad\qquad + (\iff \Ttabletype = \Tlimits~\text{(}~\text{ref}~\text{null}^?~\X{ht}~\text{)}) \\ + \end{array} + An :ref:`element segment ` can be given inline with a table definition, in which case its offset is :math:`0` and the :ref:`limits ` of the :ref:`table type ` are inferred from the length of the given segment: .. math:: From 0ed695b9c4f7d6329afd732108e62916d2e64e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20J=C3=A4genstedt?= Date: Fri, 6 Oct 2023 16:11:04 +0200 Subject: [PATCH 11/50] [ci] Don't install ocamlbuild (#1686) --- .github/workflows/ci-interpreter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-interpreter.yml b/.github/workflows/ci-interpreter.yml index 6edfa0c460..0236c49391 100644 --- a/.github/workflows/ci-interpreter.yml +++ b/.github/workflows/ci-interpreter.yml @@ -23,7 +23,7 @@ jobs: with: ocaml-compiler: 4.12.x - name: Setup OCaml tools - run: opam install --yes ocamlbuild.0.14.0 ocamlfind.1.9.5 js_of_ocaml.4.0.0 js_of_ocaml-ppx.4.0.0 + run: opam install --yes ocamlfind.1.9.5 js_of_ocaml.4.0.0 js_of_ocaml-ppx.4.0.0 - name: Setup Node.js uses: actions/setup-node@v2 with: From 64070a85c3dfb07d4d004aee1b904c2e373da58f Mon Sep 17 00:00:00 2001 From: Reuben Dunnington Date: Mon, 5 Jun 2023 22:47:11 -0700 Subject: [PATCH 12/50] [spec] Fix copypaste error for V128.Load*_Zero instructions in index (#1662) --- document/core/appendix/index-instructions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/document/core/appendix/index-instructions.py b/document/core/appendix/index-instructions.py index 955ec2c485..0c0ea6f0ac 100755 --- a/document/core/appendix/index-instructions.py +++ b/document/core/appendix/index-instructions.py @@ -431,8 +431,8 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\V128.\STORE\K{16\_lane}~\memarg~\laneidx', r'\hex{FD}~~\hex{59}', r'[\I32~\V128] \to []', r'valid-store-lane', r'exec-store-lane'), Instruction(r'\V128.\STORE\K{32\_lane}~\memarg~\laneidx', r'\hex{FD}~~\hex{5A}', r'[\I32~\V128] \to []', r'valid-store-lane', r'exec-store-lane'), Instruction(r'\V128.\STORE\K{64\_lane}~\memarg~\laneidx', r'\hex{FD}~~\hex{5B}', r'[\I32~\V128] \to []', r'valid-store-lane', r'exec-store-lane'), - Instruction(r'\V128.\LOAD\K{32\_zero}~\memarg~\laneidx', r'\hex{FD}~~\hex{5C}', r'[\I32] \to [\V128]', r'valid-load-zero', r'exec-load-zero'), - Instruction(r'\V128.\LOAD\K{64\_zero}~\memarg~\laneidx', r'\hex{FD}~~\hex{5D}', r'[\I32] \to [\V128]', r'valid-load-zero', r'exec-load-zero'), + Instruction(r'\V128.\LOAD\K{32\_zero}~\memarg', r'\hex{FD}~~\hex{5C}', r'[\I32] \to [\V128]', r'valid-load-zero', r'exec-load-zero'), + Instruction(r'\V128.\LOAD\K{64\_zero}~\memarg', r'\hex{FD}~~\hex{5D}', r'[\I32] \to [\V128]', r'valid-load-zero', r'exec-load-zero'), Instruction(r'\F32X4.\VDEMOTE\K{\_f64x2\_zero}', r'\hex{FD}~~\hex{5E}', r'[\V128] \to [\V128]', r'valid-vcvtop', r'exec-vcvtop', r'op-demote'), Instruction(r'\F64X2.\VPROMOTE\K{\_low\_f32x4}', r'\hex{FD}~~\hex{5F}', r'[\V128] \to [\V128]', r'valid-vcvtop', r'exec-vcvtop', r'op-promote'), Instruction(r'\I8X16.\VABS', r'\hex{FD}~~\hex{60}', r'[\V128] \to [\V128]', r'valid-vunop', r'exec-vunop', r'op-iabs'), From 4a150660e8b028c5cdb7010f831ff365515ae7c6 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Tue, 13 Jun 2023 19:14:35 +0200 Subject: [PATCH 13/50] [interpreter] Use explicit bounds checks instead Invalid_argument (#1659) --- interpreter/exec/eval_num.ml | 1 - interpreter/exec/v128.ml | 2 +- interpreter/runtime/data.ml | 10 +++++++++- interpreter/runtime/elem.ml | 8 +++++++- interpreter/runtime/memory.ml | 6 ++++-- interpreter/runtime/table.ml | 11 +++++++---- interpreter/util/lib.ml | 6 +++--- 7 files changed, 31 insertions(+), 13 deletions(-) diff --git a/interpreter/exec/eval_num.ml b/interpreter/exec/eval_num.ml index 8b2c1268c8..3ccfcac006 100644 --- a/interpreter/exec/eval_num.ml +++ b/interpreter/exec/eval_num.ml @@ -195,4 +195,3 @@ let eval_binop = op I32Op.binop I64Op.binop F32Op.binop F64Op.binop let eval_testop = op I32Op.testop I64Op.testop F32Op.testop F64Op.testop let eval_relop = op I32Op.relop I64Op.relop F32Op.relop F64Op.relop let eval_cvtop = op I32CvtOp.cvtop I64CvtOp.cvtop F32CvtOp.cvtop F64CvtOp.cvtop - diff --git a/interpreter/exec/v128.ml b/interpreter/exec/v128.ml index c8c3f1fe3a..c508f6b941 100644 --- a/interpreter/exec/v128.ml +++ b/interpreter/exec/v128.ml @@ -477,7 +477,7 @@ let to_hex_string s = let of_strings shape ss = if List.length ss <> num_lanes shape then - raise (Invalid_argument "wrong length"); + invalid_arg "wrong length"; let open Bytes in let b = create bytewidth in (match shape with diff --git a/interpreter/runtime/data.ml b/interpreter/runtime/data.ml index e73cd2f311..f63a2aaa3c 100644 --- a/interpreter/runtime/data.ml +++ b/interpreter/runtime/data.ml @@ -1,7 +1,15 @@ type data = string ref type t = data +exception Bounds + let alloc bs = ref bs + let size seg = I64.of_int_u (String.length !seg) -let load seg i = (!seg).[Int64.to_int i] + +let load seg i = + let i' = Int64.to_int i in + if i' < 0 || i' >= String.length !seg then raise Bounds; + !seg.[i'] + let drop seg = seg := "" diff --git a/interpreter/runtime/elem.ml b/interpreter/runtime/elem.ml index 91e5b19577..7a26055c94 100644 --- a/interpreter/runtime/elem.ml +++ b/interpreter/runtime/elem.ml @@ -1,7 +1,13 @@ type elem = Value.ref_ list ref type t = elem +exception Bounds + let alloc rs = ref rs let size seg = Lib.List32.length !seg -let load seg i = Lib.List32.nth !seg i + +let load seg i = + if i < 0l || i >= Lib.List32.length !seg then raise Bounds; + Lib.List32.nth !seg i + let drop seg = seg := [] diff --git a/interpreter/runtime/memory.ml b/interpreter/runtime/memory.ml index 91fb44211d..3e74419d9b 100644 --- a/interpreter/runtime/memory.ml +++ b/interpreter/runtime/memory.ml @@ -63,10 +63,12 @@ let grow mem delta = mem.content <- after let load_byte mem a = - try Array1_64.get mem.content a with Invalid_argument _ -> raise Bounds + if a < 0L || a >= Array1_64.dim mem.content then raise Bounds; + Array1_64.get mem.content a let store_byte mem a b = - try Array1_64.set mem.content a b with Invalid_argument _ -> raise Bounds + if a < 0L || a >= Array1_64.dim mem.content then raise Bounds; + Array1_64.set mem.content a b let load_bytes mem a n = let buf = Buffer.create n in diff --git a/interpreter/runtime/table.ml b/interpreter/runtime/table.ml index 39ef7c4e68..1374e0dcdf 100644 --- a/interpreter/runtime/table.ml +++ b/interpreter/runtime/table.ml @@ -47,14 +47,17 @@ let grow tab delta r = tab.content <- after let load tab i = - try Lib.Array32.get tab.content i with Invalid_argument _ -> raise Bounds + if i < 0l || i >= Lib.Array32.length tab.content then raise Bounds; + Lib.Array32.get tab.content i let store tab i r = let TableT (lim, t) = tab.ty in if not (Match.match_ref_type (type_of_ref r) t) then raise Type; - try Lib.Array32.set tab.content i r with Invalid_argument _ -> raise Bounds + if i < 0l || i >= Lib.Array32.length tab.content then raise Bounds; + Lib.Array32.set tab.content i r let blit tab offset rs = let data = Array.of_list rs in - try Lib.Array32.blit data 0l tab.content offset (Lib.Array32.length data) - with Invalid_argument _ -> raise Bounds + let len = Lib.Array32.length data in + if offset < 0l || offset > Int32.sub (Lib.Array32.length tab.content) len then raise Bounds; + Lib.Array32.blit data 0l tab.content offset len diff --git a/interpreter/util/lib.ml b/interpreter/util/lib.ml index 22018b049d..b0bb462a8d 100644 --- a/interpreter/util/lib.ml +++ b/interpreter/util/lib.ml @@ -175,7 +175,7 @@ module Array32 = struct let make n x = if n < 0l || Int64.of_int32 n > Int64.of_int max_int then - raise (Invalid_argument "Array32.make"); + invalid_arg "Array32.make"; Array.make (Int32.to_int n) x let length a = Int32.of_int (Array.length a) @@ -198,7 +198,7 @@ struct struct let create kind layout n = if n < 0L || n > Int64.of_int max_int then - raise (Invalid_argument "Bigarray.Array1_64.create"); + invalid_arg "Bigarray.Array1_64.create"; Array1.create kind layout (Int64.to_int n) let dim a = Int64.of_int (Array1.dim a) @@ -223,7 +223,7 @@ struct let force o = match o with | Some y -> y - | None -> raise (Invalid_argument "Option.force") + | None -> invalid_arg "Option.force" let map f = function | Some x -> Some (f x) From e23c83b081a49cff12b0c20d11043fa6843fd6e4 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Wed, 14 Jun 2023 19:14:41 +0200 Subject: [PATCH 14/50] [interpreter] Makefile tweak --- document/core/util/bikeshed_fixup.py | 2 +- interpreter/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/document/core/util/bikeshed_fixup.py b/document/core/util/bikeshed_fixup.py index 05439207f9..568c67b575 100755 --- a/document/core/util/bikeshed_fixup.py +++ b/document/core/util/bikeshed_fixup.py @@ -59,7 +59,7 @@ def Main(): ) data = data.replace( - """IEEE 754-2019""", + """IEEE 754""", "[[!IEEE-754-2019]]" ) diff --git a/interpreter/Makefile b/interpreter/Makefile index 3294e26822..014b98d557 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -41,7 +41,7 @@ all: unopt opt libunopt libopt test land: $(WINMAKE) all zip: $(ZIP) smallint: smallint.native -ci: land wast.js dunebuild +ci: land jslib dunebuild dunebuild: dune build From 05e4601b9174db895bb322344878b7f9fbc697e8 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Tue, 27 Jun 2023 07:49:55 +0200 Subject: [PATCH 15/50] [spec] Tweak math layout --- document/core/exec/numerics.rst | 49 +++++++++++++++++---------------- document/core/util/macros.def | 4 +-- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/document/core/exec/numerics.rst b/document/core/exec/numerics.rst index 71dfef6a94..fa03504ebc 100644 --- a/document/core/exec/numerics.rst +++ b/document/core/exec/numerics.rst @@ -161,48 +161,49 @@ Floating-Point where :math:`M = \significand(N)` and :math:`E = \exponent(N)`. -.. index:: byte, little endian, memory -.. _aux-littleendian: -.. _aux-bytes: +.. index:: numeric vector, shape, lane +.. _aux-lanes: -Storage +Vectors ....... -When a number is stored into :ref:`memory `, it is converted into a sequence of :ref:`bytes ` in |LittleEndian|_ byte order: +Numeric vectors have the same underlying representation as an |i128|. +They can also be interpreted as a sequence of numeric values packed into a |V128| with a particular |shape|. .. math:: + \begin{array}{l} \begin{array}{lll@{\qquad}l} - \bytes_t(i) &=& \littleendian(\bits_t(i)) \\[1ex] - \littleendian(\epsilon) &=& \epsilon \\ - \littleendian(d^8~{d'}^\ast~) &=& \littleendian({d'}^\ast)~\ibits_8^{-1}(d^8) \\ + \lanes_{t\K{x}N}(c) &=& + c_0~\dots~c_{N-1} \\ + \end{array} + \\ \qquad + \begin{array}[t]{@{}r@{~}l@{}l@{~}l@{~}l} + (\where & B &=& |t| / 8 \\ + \wedge & b^{16} &=& \bytes_{\i128}(c) \\ + \wedge & c_i &=& \bytes_{t}^{-1}(b^{16}[i \cdot B \slice B])) + \end{array} \end{array} -Again these functions are invertible bijections. +These functions are bijections, so they are invertible. -.. index:: numeric vectors, shape -.. _aux-lanes: +.. index:: byte, little endian, memory +.. _aux-littleendian: +.. _aux-bytes: -Vectors +Storage ....... -Numeric vectors have the same underlying representation as an |i128|. They can also be interpreted as a sequence of numeric values packed into a |V128| with a particular |shape|. +When a number is stored into :ref:`memory `, it is converted into a sequence of :ref:`bytes ` in |LittleEndian|_ byte order: .. math:: - \begin{array}{l} \begin{array}{lll@{\qquad}l} - \lanes_{t\K{x}N}(c) &=& - c_0~\dots~c_{N-1} \\ - \end{array} - \\ \qquad - \begin{array}[t]{@{}r@{~}l@{}} - (\where & B = |t| / 8 \\ - \wedge & b^{16} = \bytes_{\i128}(c) \\ - \wedge & c_i = \bytes_{t}^{-1}(b^{16}[i \cdot B \slice B])) - \end{array} + \bytes_t(i) &=& \littleendian(\bits_t(i)) \\[1ex] + \littleendian(\epsilon) &=& \epsilon \\ + \littleendian(d^8~{d'}^\ast~) &=& \littleendian({d'}^\ast)~\ibits_8^{-1}(d^8) \\ \end{array} -These functions are bijections, so they are invertible. +Again these functions are invertible bijections. .. index:: integer diff --git a/document/core/util/macros.def b/document/core/util/macros.def index ba3561c530..ff307ec7de 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -1282,8 +1282,8 @@ .. |ieee| mathdef:: \xref{exec/numerics}{aux-ieee}{\F{float}} .. |nans| mathdef:: \xref{exec/numerics}{aux-nans}{\F{nans}} .. |trunc| mathdef:: \xref{exec/numerics}{aux-trunc}{\F{trunc}} -.. |satu| mathdef:: \xref{exec/numerics}{aux-sat_u}{\F{sat}^{\K{u}}} -.. |sats| mathdef:: \xref{exec/numerics}{aux-sat_s}{\F{sat}^{\K{s}}} +.. |satu| mathdef:: \xref{exec/numerics}{aux-sat_u}{\F{sat\_u}} +.. |sats| mathdef:: \xref{exec/numerics}{aux-sat_s}{\F{sat\_s}} .. |lanes| mathdef:: \xref{exec/numerics}{aux-lanes}{\F{lanes}} From 2e280f66cc6cbcd5101371eda6bd78b3b245a016 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Tue, 27 Jun 2023 08:24:28 +0200 Subject: [PATCH 16/50] [spec] Some tweaks to layout and SIMD ops (#1667) --- document/core/exec/instructions.rst | 57 ++++++++++++++------------- document/core/exec/numerics.rst | 10 +++-- document/core/syntax/instructions.rst | 2 +- document/core/syntax/values.rst | 2 +- document/core/util/macros.def | 4 +- 5 files changed, 40 insertions(+), 35 deletions(-) diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index ef0c40caae..f95b9fb07b 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -20,8 +20,9 @@ The mapping of numeric instructions to their underlying operators is expressed b .. math:: \begin{array}{lll@{\qquad}l} - \X{op}_{\K{i}N}(n_1,\dots,n_k) &=& \F{i}\X{op}_N(n_1,\dots,n_k) \\ - \X{op}_{\K{f}N}(z_1,\dots,z_k) &=& \F{f}\X{op}_N(z_1,\dots,z_k) \\ + \X{op}_{\IN}(i_1,\dots,i_k) &=& \F{i}\X{op}_N(i_1,\dots,i_k) \\ + \X{op}_{\FN}(z_1,\dots,z_k) &=& \F{f}\X{op}_N(z_1,\dots,z_k) \\ + \X{op}_{\VN}(i_1,\dots,i_k) &=& \F{i}\X{op}_N(i_1,\dots,i_k) \\ \end{array} And for :ref:`conversion operators `: @@ -316,14 +317,14 @@ Most vector instructions are defined in terms of generic numeric operators appli 2. Pop the value :math:`\V128.\VCONST~c_1` from the stack. -3. Let :math:`c` be the result of computing :math:`\vvunop_{\I128}(c_1)`. +3. Let :math:`c` be the result of computing :math:`\vvunop_{\V128}(c_1)`. 4. Push the value :math:`\V128.\VCONST~c` to the stack. .. math:: \begin{array}{lcl@{\qquad}l} (\V128\K{.}\VCONST~c_1)~\V128\K{.}\vvunop &\stepto& (\V128\K{.}\VCONST~c) - & (\iff c = \vvunop_{\I128}(c_1)) \\ + & (\iff c = \vvunop_{\V128}(c_1)) \\ \end{array} @@ -338,14 +339,14 @@ Most vector instructions are defined in terms of generic numeric operators appli 3. Pop the value :math:`\V128.\VCONST~c_1` from the stack. -4. Let :math:`c` be the result of computing :math:`\vvbinop_{\I128}(c_1, c_2)`. +4. Let :math:`c` be the result of computing :math:`\vvbinop_{\V128}(c_1, c_2)`. 5. Push the value :math:`\V128.\VCONST~c` to the stack. .. math:: \begin{array}{lcl@{\qquad}l} (\V128\K{.}\VCONST~c_1)~(\V128\K{.}\VCONST~c_2)~\V128\K{.}\vvbinop &\stepto& (\V128\K{.}\VCONST~c) - & (\iff c = \vvbinop_{\I128}(c_1, c_2)) \\ + & (\iff c = \vvbinop_{\V128}(c_1, c_2)) \\ \end{array} @@ -362,14 +363,14 @@ Most vector instructions are defined in terms of generic numeric operators appli 4. Pop the value :math:`\V128.\VCONST~c_1` from the stack. -5. Let :math:`c` be the result of computing :math:`\vvternop_{\I128}(c_1, c_2, c_3)`. +5. Let :math:`c` be the result of computing :math:`\vvternop_{\V128}(c_1, c_2, c_3)`. 6. Push the value :math:`\V128.\VCONST~c` to the stack. .. math:: \begin{array}{lcl@{\qquad}l} (\V128\K{.}\VCONST~c_1)~(\V128\K{.}\VCONST~c_2)~(\V128\K{.}\VCONST~c_3)~\V128\K{.}\vvternop &\stepto& (\V128\K{.}\VCONST~c) - & (\iff c = \vvternop_{\I128}(c_1, c_2, c_3)) \\ + & (\iff c = \vvternop_{\V128}(c_1, c_2, c_3)) \\ \end{array} @@ -403,15 +404,15 @@ Most vector instructions are defined in terms of generic numeric operators appli 2. Pop the value :math:`\V128.\VCONST~c_2` from the stack. -3. Let :math:`i^\ast` be the result of computing :math:`\lanes_{i8x16}(c_2)`. +3. Let :math:`i^\ast` be the result of computing :math:`\lanes_{\I8X16}(c_2)`. 4. Pop the value :math:`\V128.\VCONST~c_1` from the stack. -5. Let :math:`j^\ast` be the result of computing :math:`\lanes_{i8x16}(c_1)`. +5. Let :math:`j^\ast` be the result of computing :math:`\lanes_{\I8X16}(c_1)`. 6. Let :math:`c^\ast` be the concatenation of the two sequences :math:`j^\ast` and :math:`0^{240}`. -7. Let :math:`c'` be the result of computing :math:`\lanes^{-1}_{i8x16}(c^\ast[ i^\ast[0] ] \dots c^\ast[ i^\ast[15] ])`. +7. Let :math:`c'` be the result of computing :math:`\lanes^{-1}_{\I8X16}(c^\ast[ i^\ast[0] ] \dots c^\ast[ i^\ast[15] ])`. 8. Push the value :math:`\V128.\VCONST~c'` onto the stack. @@ -422,9 +423,9 @@ Most vector instructions are defined in terms of generic numeric operators appli \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} - (\iff & i^\ast = \lanes_{i8x16}(c_2) \\ - \wedge & c^\ast = \lanes_{i8x16}(c_1)~0^{240} \\ - \wedge & c' = \lanes^{-1}_{i8x16}(c^\ast[ i^\ast[0] ] \dots c^\ast[ i^\ast[15] ])) + (\iff & i^\ast = \lanes_{\I8X16}(c_2) \\ + \wedge & c^\ast = \lanes_{\I8X16}(c_1)~0^{240} \\ + \wedge & c' = \lanes^{-1}_{\I8X16}(c^\ast[ i^\ast[0] ] \dots c^\ast[ i^\ast[15] ])) \end{array} \end{array} @@ -440,15 +441,15 @@ Most vector instructions are defined in terms of generic numeric operators appli 3. Pop the value :math:`\V128.\VCONST~c_2` from the stack. -4. Let :math:`i_2^\ast` be the result of computing :math:`\lanes_{i8x16}(c_2)`. +4. Let :math:`i_2^\ast` be the result of computing :math:`\lanes_{\I8X16}(c_2)`. 5. Pop the value :math:`\V128.\VCONST~c_1` from the stack. -6. Let :math:`i_1^\ast` be the result of computing :math:`\lanes_{i8x16}(c_1)`. +6. Let :math:`i_1^\ast` be the result of computing :math:`\lanes_{\I8X16}(c_1)`. 7. Let :math:`i^\ast` be the concatenation of the two sequences :math:`i_1^\ast` and :math:`i_2^\ast`. -8. Let :math:`c` be the result of computing :math:`\lanes^{-1}_{i8x16}(i^\ast[x^\ast[0]] \dots i^\ast[x^\ast[15]])`. +8. Let :math:`c` be the result of computing :math:`\lanes^{-1}_{\I8X16}(i^\ast[x^\ast[0]] \dots i^\ast[x^\ast[15]])`. 9. Push the value :math:`\V128.\VCONST~c` onto the stack. @@ -459,8 +460,8 @@ Most vector instructions are defined in terms of generic numeric operators appli \end{array} \\ \qquad \begin{array}[t]{@{}r@{~}l@{}} - (\iff & i^\ast = \lanes_{i8x16}(c_1)~\lanes_{i8x16}(c_2) \\ - \wedge & c = \lanes^{-1}_{i8x16}(i^\ast[x^\ast[0]] \dots i^\ast[x^\ast[15]])) + (\iff & i^\ast = \lanes_{\I8X16}(c_1)~\lanes_{\I8X16}(c_2) \\ + \wedge & c = \lanes^{-1}_{\I8X16}(i^\ast[x^\ast[0]] \dots i^\ast[x^\ast[15]])) \end{array} \end{array} @@ -1844,7 +1845,7 @@ Memory Instructions 13. Let :math:`n_k` be the result of computing :math:`\extend^{\sx}_{M,W}(m_k)`. -14. Let :math:`c` be the result of computing :math:`\lanes^{-1}_{\X{i}W\K{x}N}(n_0 \dots n_{N-1})`. +14. Let :math:`c` be the result of computing :math:`\lanes^{-1}_{\K{i}W\K{x}N}(n_0 \dots n_{N-1})`. 15. Push the value :math:`\V128.\CONST~c` to the stack. @@ -1861,7 +1862,7 @@ Memory Instructions \wedge & \X{ea} + M \cdot N / 8 \leq |S.\SMEMS[F.\AMODULE.\MIMEMS[0]].\MIDATA| \\ \wedge & \bytes_{\iM}(m_k) = S.\SMEMS[F.\AMODULE.\MIMEMS[0]].\MIDATA[\X{ea} + k \cdot M/8 \slice M/8] \\ \wedge & W = M \cdot 2 \\ - \wedge & c = \lanes^{-1}_{\X{i}W\K{x}N}(\extend^{\sx}_{M,W}(m_0) \dots \extend^{\sx}_{M,W}(m_{N-1}))) + \wedge & c = \lanes^{-1}_{\K{i}W\K{x}N}(\extend^{\sx}_{M,W}(m_0) \dots \extend^{\sx}_{M,W}(m_{N-1}))) \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} @@ -1903,7 +1904,7 @@ Memory Instructions 12. Let :math:`L` be the integer :math:`128 / N`. -13. Let :math:`c` be the result of computing :math:`\lanes^{-1}_{\iN\K{x}L}(n^L)`. +13. Let :math:`c` be the result of computing :math:`\lanes^{-1}_{\IN\K{x}L}(n^L)`. 14. Push the value :math:`\V128.\CONST~c` to the stack. @@ -1918,7 +1919,7 @@ Memory Instructions (\iff & \X{ea} = i + \memarg.\OFFSET \\ \wedge & \X{ea} + N/8 \leq |S.\SMEMS[F.\AMODULE.\MIMEMS[0]].\MIDATA| \\ \wedge & \bytes_{\iN}(n) = S.\SMEMS[F.\AMODULE.\MIMEMS[0]].\MIDATA[\X{ea} \slice N/8] \\ - \wedge & c = \lanes^{-1}_{\iN\K{x}L}(n^L)) + \wedge & c = \lanes^{-1}_{\IN\K{x}L}(n^L)) \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} @@ -2019,9 +2020,9 @@ Memory Instructions 14. Let :math:`L` be :math:`128 / N`. -15. Let :math:`j^\ast` be the result of computing :math:`\lanes_{\K{i}N\K{x}L}(v)`. +15. Let :math:`j^\ast` be the result of computing :math:`\lanes_{\IN\K{x}L}(v)`. -16. Let :math:`c` be the result of computing :math:`\lanes^{-1}_{\K{i}N\K{x}L}(j^\ast \with [x] = r)`. +16. Let :math:`c` be the result of computing :math:`\lanes^{-1}_{\IN\K{x}L}(j^\ast \with [x] = r)`. 17. Push the value :math:`\V128.\CONST~c` to the stack. @@ -2037,7 +2038,7 @@ Memory Instructions \wedge & \X{ea} + N/8 \leq |S.\SMEMS[F.\AMODULE.\MIMEMS[0]].\MIDATA| \\ \wedge & \bytes_{\iN}(r) = S.\SMEMS[F.\AMODULE.\MIMEMS[0]].\MIDATA[\X{ea} \slice N/8] \\ \wedge & L = 128/N \\ - \wedge & c = \lanes^{-1}_{\K{i}N\K{x}L}(\lanes_{\K{i}N\K{x}L}(v) \with [x] = r)) + \wedge & c = \lanes^{-1}_{\IN\K{x}L}(\lanes_{\IN\K{x}L}(v) \with [x] = r)) \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} @@ -2156,7 +2157,7 @@ Memory Instructions 12. Let :math:`L` be :math:`128/N`. -13. Let :math:`j^\ast` be the result of computing :math:`\lanes_{\K{i}N\K{x}L}(c)`. +13. Let :math:`j^\ast` be the result of computing :math:`\lanes_{\IN\K{x}L}(c)`. 14. Let :math:`b^\ast` be the result of computing :math:`\bytes_{\iN}(j^\ast[x])`. @@ -2173,7 +2174,7 @@ Memory Instructions (\iff & \X{ea} = i + \memarg.\OFFSET \\ \wedge & \X{ea} + N \leq |S.\SMEMS[F.\AMODULE.\MIMEMS[0]].\MIDATA| \\ \wedge & L = 128/N \\ - \wedge & S' = S \with \SMEMS[F.\AMODULE.\MIMEMS[0]].\MIDATA[\X{ea} \slice N/8] = \bytes_{\iN}(\lanes_{\K{i}N\K{x}L}(c)[x])) + \wedge & S' = S \with \SMEMS[F.\AMODULE.\MIMEMS[0]].\MIDATA[\X{ea} \slice N/8] = \bytes_{\iN}(\lanes_{\IN\K{x}L}(c)[x])) \end{array} \\[1ex] \begin{array}{lcl@{\qquad}l} diff --git a/document/core/exec/numerics.rst b/document/core/exec/numerics.rst index fa03504ebc..f9eae6b9d0 100644 --- a/document/core/exec/numerics.rst +++ b/document/core/exec/numerics.rst @@ -104,18 +104,19 @@ Conventions: -.. index:: bit, integer, floating-point +.. index:: bit, integer, floating-point, numeric vector .. _aux-bits: Representations ~~~~~~~~~~~~~~~ -Numbers have an underlying binary representation as a sequence of bits: +Numbers and numeric vectors have an underlying binary representation as a sequence of bits: .. math:: \begin{array}{lll@{\qquad}l} - \bits_{\K{i}N}(i) &=& \ibits_N(i) \\ - \bits_{\K{f}N}(z) &=& \fbits_N(z) \\ + \bits_{\IN}(i) &=& \ibits_N(i) \\ + \bits_{\FN}(z) &=& \fbits_N(z) \\ + \bits_{\VN}(i) &=& \ibits_N(i) \\ \end{array} Each of these functions is a bijection, hence they are invertible. @@ -163,6 +164,7 @@ where :math:`M = \significand(N)` and :math:`E = \exponent(N)`. .. index:: numeric vector, shape, lane .. _aux-lanes: +.. _syntax-i128: Vectors ....... diff --git a/document/core/syntax/instructions.rst b/document/core/syntax/instructions.rst index 564bf5ace4..14efc7a1a8 100644 --- a/document/core/syntax/instructions.rst +++ b/document/core/syntax/instructions.rst @@ -171,7 +171,7 @@ Occasionally, it is convenient to group operators together according to the foll \end{array} -.. index:: ! vector instruction, numeric vectors, number, value, value type, SIMD +.. index:: ! vector instruction, numeric vector, number, value, value type, SIMD pair: abstract syntax; instruction .. _syntax-laneidx: .. _syntax-shape: diff --git a/document/core/syntax/values.rst b/document/core/syntax/values.rst index 1b155df42c..72ff7d3a06 100644 --- a/document/core/syntax/values.rst +++ b/document/core/syntax/values.rst @@ -148,7 +148,7 @@ Conventions * The meta variable :math:`z` ranges over floating-point values where clear from context. -.. index:: ! numeric vectors, integer, floating-point, lane, SIMD +.. index:: ! numeric vector, integer, floating-point, lane, SIMD pair: abstract syntax; vector .. _syntax-vecnum: diff --git a/document/core/util/macros.def b/document/core/util/macros.def index ff307ec7de..81dc94de23 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -192,13 +192,15 @@ .. |F32| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{f32}} .. |F64| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{f64}} .. |V128| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{v128}} +.. |IN| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{i}N} +.. |FN| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{f}N} +.. |VN| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{v}N} .. |I8X16| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{i8x16}} .. |I16X8| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{i16x8}} .. |I32X4| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{i32x4}} .. |I64X2| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{i64x2}} .. |F32X4| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{f32x4}} .. |F64X2| mathdef:: \xref{syntax/types}{syntax-valtype}{\K{f64x2}} -.. |I128| mathdef:: \K{i128} .. |REF| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{ref}} .. |NULL| mathdef:: \xref{syntax/types}{syntax-reftype}{\K{null}} From f113ac48386b5add813066b5e2548bfd5861e0ea Mon Sep 17 00:00:00 2001 From: Ben Visness Date: Mon, 3 Jul 2023 10:52:27 -0500 Subject: [PATCH 17/50] [spec] Add missing type to elem.drop and store soundness (#1668) --- document/core/appendix/properties.rst | 4 ++-- document/core/exec/instructions.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/document/core/appendix/properties.rst b/document/core/appendix/properties.rst index 45e3084b74..d0cb4726c7 100644 --- a/document/core/appendix/properties.rst +++ b/document/core/appendix/properties.rst @@ -300,8 +300,8 @@ Module instances are classified by *module contexts*, which are regular :ref:`co .. index:: element instance, reference .. _valid-eleminst: -:ref:`Element Instances ` :math:`\{ \EIELEM~\X{fa}^\ast \}` -............................................................................ +:ref:`Element Instances ` :math:`\{ \EITYPE~t, \EIELEM~\reff^\ast \}` +...................................................................................... * The :ref:`reference type ` :math:`t` must be :ref:`valid ` under the empty :ref:`context `. diff --git a/document/core/exec/instructions.rst b/document/core/exec/instructions.rst index f95b9fb07b..69c36cc3d1 100644 --- a/document/core/exec/instructions.rst +++ b/document/core/exec/instructions.rst @@ -1706,7 +1706,7 @@ Table Instructions 4. Assert: due to :ref:`validation `, :math:`S.\SELEMS[a]` exists. -5. Replace :math:`S.\SELEMS[a]` with the :ref:`element instance ` :math:`\{\EIELEM~\epsilon\}`. +5. Replace :math:`S.\SELEMS[a].\EIELEM` with :math:`\epsilon`. .. math:: ~\\[-1ex] @@ -1715,7 +1715,7 @@ Table Instructions S; F; (\ELEMDROP~x) &\stepto& S'; F; \epsilon \end{array} \\ \qquad - (\iff S' = S \with \SELEMS[F.\AMODULE.\MIELEMS[x]] = \{ \EIELEM~\epsilon \}) \\ + (\iff S' = S \with \SELEMS[F.\AMODULE.\MIELEMS[x]].\EIELEM = \epsilon) \\ \end{array} From ef28217d7f70af47be7a91acbc6e8f433099061c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 11 Jul 2023 06:17:49 -0700 Subject: [PATCH 18/50] [test] Add tests for out-of-range NaN payloads (#1670) --- test/core/float_literals.wast | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/core/float_literals.wast b/test/core/float_literals.wast index fefb91fbb7..3b3ed76bb6 100644 --- a/test/core/float_literals.wast +++ b/test/core/float_literals.wast @@ -352,6 +352,10 @@ (module quote "(global f32 (f32.const 0x1.0p_+1))") "unknown operator" ) +(assert_malformed + (module quote "(global f32 (f32.const nan:0x80_0000))") + "constant out of range" +) (assert_malformed (module quote "(global f64 (f64.const _100))") @@ -505,3 +509,7 @@ (module quote "(global f64 (f64.const 0x1.0p_+1))") "unknown operator" ) +(assert_malformed + (module quote "(global f64 (f64.const nan:0x10_0000_0000_0000))") + "constant out of range" +) From d1d16868aa4ffaa28817b076b56c221893388703 Mon Sep 17 00:00:00 2001 From: Patrick Dubroy Date: Wed, 12 Jul 2023 20:03:10 +0200 Subject: [PATCH 19/50] [spec] Fix typo (#1671) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit depended → dependent --- document/core/binary/modules.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document/core/binary/modules.rst b/document/core/binary/modules.rst index c1a513fd94..902f424a61 100644 --- a/document/core/binary/modules.rst +++ b/document/core/binary/modules.rst @@ -60,7 +60,7 @@ Each section consists of * a one-byte section *id*, * the |U32| *size* of the contents, in bytes, -* the actual *contents*, whose structure is depended on the section id. +* the actual *contents*, whose structure is dependent on the section id. Every section is optional; an omitted section is equivalent to the section being present with empty contents. From 767f23a80b1712091a7be90f6b59d1ff704a2d15 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 24 Jul 2023 07:32:12 +0200 Subject: [PATCH 20/50] [spec] Remark about the convention of using a pattern for attribute variable --- document/core/binary/conventions.rst | 1 + document/core/text/conventions.rst | 1 + 2 files changed, 2 insertions(+) diff --git a/document/core/binary/conventions.rst b/document/core/binary/conventions.rst index 83c80399fa..7b606e1e48 100644 --- a/document/core/binary/conventions.rst +++ b/document/core/binary/conventions.rst @@ -54,6 +54,7 @@ In order to distinguish symbols of the binary syntax from symbols of the abstrac (This is a shorthand for :math:`B^n` where :math:`n \leq 1`.) * :math:`x{:}B` denotes the same language as the nonterminal :math:`B`, but also binds the variable :math:`x` to the attribute synthesized for :math:`B`. + A pattern may also be used instead of a variable, e.g., :math:`7{:}B`. * Productions are written :math:`\B{sym} ::= B_1 \Rightarrow A_1 ~|~ \dots ~|~ B_n \Rightarrow A_n`, where each :math:`A_i` is the attribute that is synthesized for :math:`\B{sym}` in the given case, usually from attribute variables bound in :math:`B_i`. diff --git a/document/core/text/conventions.rst b/document/core/text/conventions.rst index 0bd32e0339..1efc88b036 100644 --- a/document/core/text/conventions.rst +++ b/document/core/text/conventions.rst @@ -49,6 +49,7 @@ In order to distinguish symbols of the textual syntax from symbols of the abstra (This is a shorthand for :math:`T^n` where :math:`n \leq 1`.) * :math:`x{:}T` denotes the same language as the nonterminal :math:`T`, but also binds the variable :math:`x` to the attribute synthesized for :math:`T`. + A pattern may also be used instead of a variable, e.g., :math:`(x,y){:}T`. * Productions are written :math:`\T{sym} ::= T_1 \Rightarrow A_1 ~|~ \dots ~|~ T_n \Rightarrow A_n`, where each :math:`A_i` is the attribute that is synthesized for :math:`\T{sym}` in the given case, usually from attribute variables bound in :math:`T_i`. From 423bf0b3b4d41be38f7eff2ab25868a1dd53483b Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 24 Jul 2023 09:34:31 +0200 Subject: [PATCH 21/50] [interpreter] Makefile support for opam releases --- interpreter/Makefile | 10 ++++++++++ interpreter/meta/opam/opam | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 interpreter/meta/opam/opam diff --git a/interpreter/Makefile b/interpreter/Makefile index 014b98d557..9caf2a4ac2 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -206,3 +206,13 @@ install: _build/$(LIB).cmx _build/$(LIB).cmo uninstall: ocamlfind remove $(LIB) + +opam-release/%: + git tag opam-$* + git push --tags + wget https://github.com/WebAssembly/spec/archive/opam-$*.zip + cp meta/opam/opam . + sed s/@VERSION/$*/g opam >opam.tmp + sed s/@MD5/`md5 -q opam-$*.zip`/g opam.tmp >opam + rm opam.tmp + echo Created file ./opam, submit to github opam-repository/packages/wasm/wasm.$*/opam diff --git a/interpreter/meta/opam/opam b/interpreter/meta/opam/opam new file mode 100644 index 0000000000..139251f3fa --- /dev/null +++ b/interpreter/meta/opam/opam @@ -0,0 +1,22 @@ +opam-version: "2.0" +maintainer: "Andreas Rossberg " +authors: "Andreas Rossberg " +homepage: "https://github.com/WebAssembly/spec" +bug-reports: "https://github.com/WebAssembly/spec/issues" +license: "Apache-2.0" +dev-repo: "git+https://github.com/WebAssembly/spec.git" +build: [ + [make "-C" "interpreter" "opt" "unopt"] +] +install: [make "-C" "interpreter" "install"] +depends: [ + "ocaml" {>= "4.12.0"} + "ocamlfind" {build} + "ocamlbuild" {build} +] +synopsis: + "Library to read and write WebAssembly (Wasm) files and manipulate their AST" +url { + src: "https://github.com/WebAssembly/spec/archive/opam-@VERSION.zip" + checksum: "md5=@MD5" +} From 7b33c40c1e88a7e5b10a87e9b08a413ef4ae9b97 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 24 Jul 2023 09:46:23 +0200 Subject: [PATCH 22/50] [interpreter] Makefile tweaks --- interpreter/Makefile | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index 9caf2a4ac2..5a6cb64a88 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -208,11 +208,12 @@ uninstall: ocamlfind remove $(LIB) opam-release/%: - git tag opam-$* + #git tag opam-$* git push --tags + rm -f opam-$*.zip wget https://github.com/WebAssembly/spec/archive/opam-$*.zip cp meta/opam/opam . - sed s/@VERSION/$*/g opam >opam.tmp - sed s/@MD5/`md5 -q opam-$*.zip`/g opam.tmp >opam - rm opam.tmp - echo Created file ./opam, submit to github opam-repository/packages/wasm/wasm.$*/opam + sed -i "tmp" s/@VERSION/$*/g opam + sed -i "tmp" s/@MD5/`md5 -q opam-$*.zip`/g opam + rm opam-$*.zip + @echo Created file ./opam, submit to github opam-repository/packages/wasm/wasm.$*/opam From 38ea0a0488a4278dc2add40439a3491af83fffd7 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 24 Jul 2023 09:49:12 +0200 Subject: [PATCH 23/50] [interpreter] Makefile tweaks --- interpreter/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index 5a6cb64a88..aafdb2b8a8 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -208,7 +208,7 @@ uninstall: ocamlfind remove $(LIB) opam-release/%: - #git tag opam-$* + git tag opam-$* git push --tags rm -f opam-$*.zip wget https://github.com/WebAssembly/spec/archive/opam-$*.zip From 98f35cbb43e6d16f16f802d54cee7c9a005dc359 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 24 Jul 2023 11:59:40 +0200 Subject: [PATCH 24/50] [interpreter] Tune opam file to fix opam repo CI --- interpreter/Makefile | 6 +++--- interpreter/meta/opam/opam | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index aafdb2b8a8..740554d3a5 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -213,7 +213,7 @@ opam-release/%: rm -f opam-$*.zip wget https://github.com/WebAssembly/spec/archive/opam-$*.zip cp meta/opam/opam . - sed -i "tmp" s/@VERSION/$*/g opam - sed -i "tmp" s/@MD5/`md5 -q opam-$*.zip`/g opam - rm opam-$*.zip + sed -i ".tmp" s/@VERSION/$*/g opam + sed -i ".tmp" s/@MD5/`md5 -q opam-$*.zip`/g opam + rm opam.tmp opam-$*.zip @echo Created file ./opam, submit to github opam-repository/packages/wasm/wasm.$*/opam diff --git a/interpreter/meta/opam/opam b/interpreter/meta/opam/opam index 139251f3fa..845fe675ec 100644 --- a/interpreter/meta/opam/opam +++ b/interpreter/meta/opam/opam @@ -14,6 +14,7 @@ depends: [ "ocamlfind" {build} "ocamlbuild" {build} ] +conflicts: ["ocaml-option-bytecode-only"] # uses ocamlopt unconconditionally synopsis: "Library to read and write WebAssembly (Wasm) files and manipulate their AST" url { From bf6309f3b5fdd7815fffead3a4344892cd3d5ef5 Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Wed, 26 Jul 2023 14:14:44 +0200 Subject: [PATCH 25/50] [interpreter] Use dune instead of ocamlbuild (#1665) --- interpreter/.gitignore | 15 +- interpreter/Makefile | 212 +++++++-------------------- interpreter/README.md | 19 +-- interpreter/dune | 31 +++- interpreter/dune-project | 19 +++ interpreter/{meta => }/jslib/wast.ml | 1 + interpreter/meta/findlib/META | 4 - interpreter/meta/opam/opam | 23 --- interpreter/wasm.opam | 32 ++++ interpreter/winmake.bat | 75 ---------- 10 files changed, 137 insertions(+), 294 deletions(-) rename interpreter/{meta => }/jslib/wast.ml (99%) delete mode 100644 interpreter/meta/findlib/META delete mode 100644 interpreter/meta/opam/opam create mode 100644 interpreter/wasm.opam delete mode 100644 interpreter/winmake.bat diff --git a/interpreter/.gitignore b/interpreter/.gitignore index 0a02ff708c..ced4eafb60 100644 --- a/interpreter/.gitignore +++ b/interpreter/.gitignore @@ -1,13 +1,6 @@ -*.cmo -*.cmx -*.native -*.byte -*.opt -*.unopt -*.js -*.zip -*.mlpack _build wasm -wasm.debug - +*.install +*.js +*.zip +opam diff --git a/interpreter/Makefile b/interpreter/Makefile index 740554d3a5..d67882b294 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -1,4 +1,4 @@ -# This Makefile uses ocamlbuild but does not rely on ocamlfind or the Opam +# This Makefile uses dune but does not rely on ocamlfind or the Opam # package manager to build. However, Opam package management is available # optionally through the check/install/uninstall targets. # @@ -9,131 +9,45 @@ # Configuration -NAME = wasm -UNOPT = $(NAME).debug -OPT = $(NAME) -LIB = $(NAME) +NAME = wasm +OPT = $(NAME).exe ZIP = $(NAME).zip -JSLIB = wast -WINMAKE = winmake.bat - -DIRS = util syntax binary text valid runtime exec script host main tests -LIBS = -FLAGS = -lexflags -ml -cflags '-w +a-4-27-42-44-45-70 -warn-error +a-3' -OCBA = ocamlbuild $(FLAGS) $(DIRS:%=-I %) -OCB = $(OCBA) $(LIBS:%=-libs %) -JSO = js_of_ocaml -q --opt 3 -JS = # set to JS shell command to run JS tests, empty to skip +JSLIB = wast.js +BUILDDIR = _build/default -# Main targets +JS = # set to JS shell command to run JS tests, empty to skip -.PHONY: default opt unopt libopt libunopt jslib all land zip smallint dunebuild -default: opt -debug: unopt -opt: $(OPT) -unopt: $(UNOPT) -libopt: _build/$(LIB).cmx _build/$(LIB).cmxa -libunopt: _build/$(LIB).cmo _build/$(LIB).cma -jslib: $(JSLIB).js -all: unopt opt libunopt libopt test -land: $(WINMAKE) all -zip: $(ZIP) -smallint: smallint.native -ci: land jslib dunebuild +# Main targets -dunebuild: - dune build +.PHONY: default opt jslib all zip smallint +default: $(OPT) +jslib: $(JSLIB) +all: $(OPT) test +zip: $(ZIP) +smallint: smallint.exe +ci: all jslib # Building executable +.PHONY: $(NAME).exe +$(NAME).exe: + rm -f $(NAME) + dune build $@ + cp $(BUILDDIR)/$(OPT) $(NAME) -empty = -space = $(empty) $(empty) -comma = , - -.INTERMEDIATE: _tags -_tags: - echo >$@ "true: bin_annot" - echo >>$@ "true: debug" - echo >>$@ "<{$(subst $(space),$(comma),$(DIRS))}/*.cmx>: for-pack($(PACK))" - -$(UNOPT): main.byte - mv $< $@ - -$(OPT): main.native - mv $< $@ - -.PHONY: main.byte main.native -main.byte: _tags - $(OCB) -quiet $@ - -main.native: _tags - $(OCB) -quiet $@ - -.PHONY: smallint.byte smallint.native -smallint.byte: _tags - $(OCB) -quiet $@ -smallint.native: _tags - $(OCB) -quiet $@ - - -# Building library - -FILES = $(shell ls $(DIRS:%=%/*) | grep '[.]ml[^.]*$$') -PACK = $(shell echo `echo $(LIB) | sed 's/^\(.\).*$$/\\1/g' | tr [:lower:] [:upper:]``echo $(LIB) | sed 's/^.\(.*\)$$/\\1/g'`) - -.INTERMEDIATE: $(LIB).mlpack -$(LIB).mlpack: $(DIRS) - ls $(FILES) \ - | sed 's:\(.*/\)\{0,1\}\(.*\)\.[^\.]*:\2:' \ - | grep -v main \ - | sort | uniq \ - >$@ - -.INTERMEDIATE: $(LIB).mllib -$(LIB).mllib: - echo Wasm >$@ - -_build/$(LIB).cmo: $(FILES) $(LIB).mlpack _tags Makefile - $(OCB) -quiet $(LIB).cmo - -_build/$(LIB).cmx: $(FILES) $(LIB).mlpack _tags Makefile - $(OCB) -quiet $(LIB).cmx - -_build/$(LIB).cma: $(FILES) $(LIB).mllib _tags Makefile - $(OCBA) -quiet $(LIB).cma - -_build/$(LIB).cmxa: $(FILES) $(LIB).mllib _tags Makefile - $(OCBA) -quiet $(LIB).cmxa - +.PHONY: smallint.exe +smallint.exe: + dune build $@ # Building JavaScript library -JSLIB_DIR = meta/jslib -JSLIB_FLAGS = -I $(JSLIB_DIR) -use-ocamlfind -pkg js_of_ocaml -pkg js_of_ocaml-ppx - -.INTERMEDIATE: $(JSLIB).byte -$(JSLIB).byte: $(JSLIB_DIR)/$(JSLIB).ml - $(OCBA) $(JSLIB_FLAGS) $@ - -$(JSLIB).js: $(JSLIB).byte - $(JSO) $< - -# Building Windows build file - -$(WINMAKE): clean - echo rem Auto-generated from Makefile! >$@ - echo set NAME=$(NAME) >>$@ - echo if \'%1\' neq \'\' set NAME=%1 >>$@ - $(OCB) main.byte \ - | grep -v ocamldep \ - | grep -v mkdir \ - | sed s:`which ocaml`:ocaml:g \ - | sed s:main/main.d.byte:%NAME%.exe: \ - >>$@ +$(JSLIB): $(BUILDDIR)/$(JSLIB) + cp $< $@ +$(BUILDDIR)/$(JSLIB): + dune build $(JSLIB) # Executing test suite @@ -142,78 +56,60 @@ TESTDIR = ../test/core TESTFILES = $(shell cd $(TESTDIR); ls *.wast; ls [a-z]*/*.wast) TESTS = $(TESTFILES:%.wast=%) -.PHONY: test debugtest partest dune-test +.PHONY: test partest dune-test test: $(OPT) smallint - $(TESTDIR)/run.py --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) - ./smallint.native -debugtest: $(UNOPT) smallint - $(TESTDIR)/run.py --wasm `pwd`/$(UNOPT) $(if $(JS),--js '$(JS)',) - ./smallint.native + $(TESTDIR)/run.py --wasm `pwd`/$(BUILDDIR)/$(OPT) $(if $(JS),--js '$(JS)',) + dune exec ./smallint.exe test/%: $(OPT) - $(TESTDIR)/run.py --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$*.wast -debugtest/%: $(UNOPT) - $(TESTDIR)/run.py --wasm `pwd`/$(UNOPT) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$*.wast + $(TESTDIR)/run.py --wasm `pwd`/$(BUILDDIR)/$(OPT) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$*.wast run/%: $(OPT) ./$(OPT) $(TESTDIR)/$*.wast -debug/%: $(UNOPT) - ./$(UNOPT) $(TESTDIR)/$*.wast -partest: $(TESTS:%=quiettest/%) +partest: $(TESTS:%=quiettest/%) @echo All tests passed. quiettest/%: $(OPT) @ ( \ - $(TESTDIR)/run.py 2>$(@F).out --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$*.wast && \ + $(TESTDIR)/run.py 2>$(@F).out --wasm `pwd`/$(BUILDDIR)/$(OPT) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$*.wast && \ rm $(@F).out \ ) || \ cat $(@F).out || rm $(@F).out || exit 1 smallinttest: smallint - @./smallint.native + dune exec ./smallint.exe dunetest: dune test +install: + dune build -p $(NAME) @install + dune install + +opam-release/%: + git tag opam-$* + git push --tags + rm -f opam-$*.zip + wget https://github.com/WebAssembly/spec/archive/opam-$*.zip + cp wasm.opam opam + echo "url {" >> opam + echo " src: \"https://github.com/WebAssembly/spec/archive/opam-$*.zip\"" >> opam + echo " checksum: \"md5=`md5 -q opam-$*.zip`\"" >> opam + echo "}" >> opam + rm opam-$*.zip + @echo Created file ./opam, submit to github opam-repository/packages/wasm/wasm.$*/opam # Miscellaneous targets .PHONY: clean -$(ZIP): $(WINMAKE) - git archive --format=zip --prefix=$(NAME)/ -o $@ HEAD +$(ZIP): + git archive --format=zip --prefix=$(NAME)/ -o $@ HEAD clean: - rm -rf _build/jslib $(LIB).mlpack _tags $(JSLIB).js - $(OCB) -clean - - -# Opam support - -.PHONY: check install uninstall + dune clean -check: - # Check that we can find all relevant libraries - # when using ocamlfind - ocamlfind query $(LIBS) - -install: _build/$(LIB).cmx _build/$(LIB).cmo - ocamlfind install $(LIB) meta/findlib/META _build/$(LIB).o \ - $(wildcard _build/$(LIB).cm*) \ - $(wildcard $(DIRS:%=%/*.mli)) - -uninstall: - ocamlfind remove $(LIB) - -opam-release/%: - git tag opam-$* - git push --tags - rm -f opam-$*.zip - wget https://github.com/WebAssembly/spec/archive/opam-$*.zip - cp meta/opam/opam . - sed -i ".tmp" s/@VERSION/$*/g opam - sed -i ".tmp" s/@MD5/`md5 -q opam-$*.zip`/g opam - rm opam.tmp opam-$*.zip - @echo Created file ./opam, submit to github opam-repository/packages/wasm/wasm.$*/opam +distclean: clean + rm -f $(NAME) $(JSLIB) diff --git a/interpreter/README.md b/interpreter/README.md index 0c763f75ef..6195e97732 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -17,15 +17,14 @@ The text format defines modules in S-expression syntax. Moreover, it is generali You'll need OCaml 4.12 or higher. Instructions for installing a recent version of OCaml on multiple platforms are available [here](https://ocaml.org/docs/install.html). On most platforms, the recommended way is through [OPAM](https://ocaml.org/docs/install.html#OPAM). +You'll also need to install the dune build system. See the [installation instructions](https://github.com/ocaml/dune#installation-1). + Once you have OCaml, simply do ``` make ``` -You'll get an executable named `./wasm`. This is a byte code executable. If you want a (faster) native code executable, do -``` -make opt -``` +You'll get an executable named `./wasm`. To run the test suite, ``` make test @@ -34,12 +33,6 @@ To do everything: ``` make all ``` -Before committing changes, you should do -``` -make land -``` -That builds `all`, plus updates `winmake.bat`. - #### Building on Windows @@ -49,12 +42,6 @@ The instructions depend on how you [installed OCaml on Windows](https://ocaml.or 2. *Windows Subsystem for Linux* (WSL): You can build the interpreter using `make`, as described above. -3. *From source*: If you just want to build the interpreter and don't care about modifying it, you don't need to install the Cygwin core that comes with the installer. Just install OCaml itself and run -``` -winmake.bat -``` -in a Windows shell, which creates a program named `wasm`. Note that this will be a byte code executable only, i.e., somewhat slower. - In any way, in order to run the test suite you'll need to have Python installed. If you used Option 3, you can invoke the test runner `runtests.py` directly instead of doing it through `make`. diff --git a/interpreter/dune b/interpreter/dune index 48274aad99..c0e9c292f3 100644 --- a/interpreter/dune +++ b/interpreter/dune @@ -1,16 +1,16 @@ (include_subdirs unqualified) (library - (name wasm) - ; The 'main' module shall not be part of the library, as it would start the + (public_name wasm) + ; The 'wasm' module shall not be part of the library, as it would start the ; Wasm REPL every time in all the dependencies. ; We exclude the 'wast' module as it is only used for the JS build. ; 'smallint' is a separate test module. - (modules :standard \ main smallint wast)) + (modules :standard \ main wasm smallint wast)) (executable - (name main) - (modules main) + (public_name wasm) + (modules wasm) (libraries wasm) (flags (-open Wasm))) @@ -22,6 +22,23 @@ (flags (-open Wasm))) +(executable + (name wast) + (modules wast) + (modes js) + (libraries js_of_ocaml wasm) + (preprocess (pps js_of_ocaml-ppx))) + +(rule + (targets wasm.ml) + (deps main/main.ml) + (action (copy main/main.ml wasm.ml))) + +(rule + (targets wast.js) + (deps wast.bc.js) + (action (copy wast.bc.js wast.js))) + (subdir text (rule @@ -42,10 +59,10 @@ (rule (alias runtest) (deps - ./main.exe + ./wasm.exe ./smallint.exe (source_tree ../test)) (action (progn - (run ../test/core/run.py --wasm ./main.exe) + (run ../test/core/run.py --wasm ./wasm.exe) (run ./smallint.exe)))) diff --git a/interpreter/dune-project b/interpreter/dune-project index c994249ac2..0d15135d31 100644 --- a/interpreter/dune-project +++ b/interpreter/dune-project @@ -1 +1,20 @@ (lang dune 2.9) + +(name wasm) + +(generate_opam_files true) + +(license Apache-2.0) + +(source + (github WebAssembly/spec)) + +(authors "Andreas Rossberg = 4.12)))) diff --git a/interpreter/meta/jslib/wast.ml b/interpreter/jslib/wast.ml similarity index 99% rename from interpreter/meta/jslib/wast.ml rename to interpreter/jslib/wast.ml index 9af04f9189..0ab4bd8fdd 100644 --- a/interpreter/meta/jslib/wast.ml +++ b/interpreter/jslib/wast.ml @@ -1,6 +1,7 @@ (* Implements a wrapper library that allows the use of the reference * interpreter's encode/decode functionality in JavaScript. *) +open Wasm open Js_of_ocaml let _ = diff --git a/interpreter/meta/findlib/META b/interpreter/meta/findlib/META deleted file mode 100644 index 2c1d96dd03..0000000000 --- a/interpreter/meta/findlib/META +++ /dev/null @@ -1,4 +0,0 @@ -description = "A library for writing/reading/running WebAssembly binaries" -requires = "bigarray,str" -archive(byte) = "wasm.cmo" -archive(native) = "wasm.cmx" diff --git a/interpreter/meta/opam/opam b/interpreter/meta/opam/opam deleted file mode 100644 index 845fe675ec..0000000000 --- a/interpreter/meta/opam/opam +++ /dev/null @@ -1,23 +0,0 @@ -opam-version: "2.0" -maintainer: "Andreas Rossberg " -authors: "Andreas Rossberg " -homepage: "https://github.com/WebAssembly/spec" -bug-reports: "https://github.com/WebAssembly/spec/issues" -license: "Apache-2.0" -dev-repo: "git+https://github.com/WebAssembly/spec.git" -build: [ - [make "-C" "interpreter" "opt" "unopt"] -] -install: [make "-C" "interpreter" "install"] -depends: [ - "ocaml" {>= "4.12.0"} - "ocamlfind" {build} - "ocamlbuild" {build} -] -conflicts: ["ocaml-option-bytecode-only"] # uses ocamlopt unconconditionally -synopsis: - "Library to read and write WebAssembly (Wasm) files and manipulate their AST" -url { - src: "https://github.com/WebAssembly/spec/archive/opam-@VERSION.zip" - checksum: "md5=@MD5" -} diff --git a/interpreter/wasm.opam b/interpreter/wasm.opam new file mode 100644 index 0000000000..5d5984106c --- /dev/null +++ b/interpreter/wasm.opam @@ -0,0 +1,32 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: + "Library to read and write WebAssembly (Wasm) files and manipulate their AST" +maintainer: ["Andreas Rossberg = "2.9"} + "ocaml" {>= "4.12"} + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "--promote-install-files=false" + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] + ["dune" "install" "-p" name "--create-install-files" name] +] +dev-repo: "git+https://github.com/WebAssembly/spec.git" diff --git a/interpreter/winmake.bat b/interpreter/winmake.bat deleted file mode 100644 index 6ff7e8caae..0000000000 --- a/interpreter/winmake.bat +++ /dev/null @@ -1,75 +0,0 @@ -rem Auto-generated from Makefile! -set NAME=wasm -if '%1' neq '' set NAME=%1 -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/numeric_error.cmo exec/numeric_error.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/int.cmo exec/int.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I util -I main -I syntax -I text -I binary -I exec -I script -I runtime -I host -I valid -o util/lib.cmi util/lib.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/i32.cmo exec/i32.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/float.cmo exec/float.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I syntax -I main -I text -I binary -I exec -I script -I runtime -I util -I host -I valid -o syntax/types.cmo syntax/types.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/f32.cmo exec/f32.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/f64.cmo exec/f64.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/i64.cmo exec/i64.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I syntax -I main -I text -I binary -I exec -I script -I runtime -I util -I host -I valid -o syntax/values.cmo syntax/values.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I runtime -I main -I syntax -I text -I binary -I exec -I script -I util -I host -I valid -o runtime/memory.cmi runtime/memory.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I util -I main -I syntax -I text -I binary -I exec -I script -I runtime -I host -I valid -o util/source.cmi util/source.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I syntax -I main -I text -I binary -I exec -I script -I runtime -I util -I host -I valid -o syntax/ast.cmo syntax/ast.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I runtime -I main -I syntax -I text -I binary -I exec -I script -I util -I host -I valid -o runtime/func.cmi runtime/func.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I runtime -I main -I syntax -I text -I binary -I exec -I script -I util -I host -I valid -o runtime/global.cmi runtime/global.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I runtime -I main -I syntax -I text -I binary -I exec -I script -I util -I host -I valid -o runtime/table.cmi runtime/table.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I runtime -I main -I syntax -I text -I binary -I exec -I script -I util -I host -I valid -o runtime/instance.cmo runtime/instance.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/eval.cmi exec/eval.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I binary -I main -I syntax -I text -I exec -I script -I runtime -I util -I host -I valid -o binary/utf8.cmi binary/utf8.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I host -I main -I syntax -I text -I binary -I exec -I script -I runtime -I util -I valid -o host/env.cmo host/env.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I main -I syntax -I text -I binary -I exec -I script -I runtime -I util -I host -I valid -o main/flags.cmo main/flags.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I script -I main -I syntax -I text -I binary -I exec -I runtime -I util -I host -I valid -o script/import.cmi script/import.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I script -I main -I syntax -I text -I binary -I exec -I runtime -I util -I host -I valid -o script/run.cmi script/run.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I host -I main -I syntax -I text -I binary -I exec -I script -I runtime -I util -I valid -o host/spectest.cmo host/spectest.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I main -I syntax -I text -I binary -I exec -I script -I runtime -I util -I host -I valid -o main/main.cmo main/main.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I util -I main -I syntax -I text -I binary -I exec -I script -I runtime -I host -I valid -o util/error.cmi util/error.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I script -I main -I syntax -I text -I binary -I exec -I runtime -I util -I host -I valid -o script/script.cmo script/script.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I binary -I main -I syntax -I text -I exec -I script -I runtime -I util -I host -I valid -o binary/decode.cmi binary/decode.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I binary -I main -I syntax -I text -I exec -I script -I runtime -I util -I host -I valid -o binary/encode.cmi binary/encode.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I script -I main -I syntax -I text -I binary -I exec -I runtime -I util -I host -I valid -o script/js.cmi script/js.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I text -I main -I syntax -I binary -I exec -I script -I runtime -I util -I host -I valid -o text/parse.cmi text/parse.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I text -I main -I syntax -I binary -I exec -I script -I runtime -I util -I host -I valid -o text/print.cmi text/print.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I valid -I main -I syntax -I text -I binary -I exec -I script -I runtime -I util -I host -o valid/valid.cmi valid/valid.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I script -I main -I syntax -I text -I binary -I exec -I runtime -I util -I host -I valid -o script/import.cmo script/import.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I script -I main -I syntax -I text -I binary -I exec -I runtime -I util -I host -I valid -o script/run.cmo script/run.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I binary -I main -I syntax -I text -I exec -I script -I runtime -I util -I host -I valid -o binary/utf8.cmo binary/utf8.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/eval_numeric.cmi exec/eval_numeric.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/i64_convert.cmi exec/i64_convert.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/eval.cmo exec/eval.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I runtime -I main -I syntax -I text -I binary -I exec -I script -I util -I host -I valid -o runtime/func.cmo runtime/func.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I util -I main -I syntax -I text -I binary -I exec -I script -I runtime -I host -I valid -o util/source.cmo util/source.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/f32_convert.cmi exec/f32_convert.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/f64_convert.cmi exec/f64_convert.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/i32_convert.cmi exec/i32_convert.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I util -I main -I syntax -I text -I binary -I exec -I script -I runtime -I host -I valid -o util/error.cmo util/error.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/eval_numeric.cmo exec/eval_numeric.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I runtime -I main -I syntax -I text -I binary -I exec -I script -I util -I host -I valid -o runtime/global.cmo runtime/global.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/i64_convert.cmo exec/i64_convert.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I util -I main -I syntax -I text -I binary -I exec -I script -I runtime -I host -I valid -o util/lib.cmo util/lib.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I runtime -I main -I syntax -I text -I binary -I exec -I script -I util -I host -I valid -o runtime/memory.cmo runtime/memory.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I runtime -I main -I syntax -I text -I binary -I exec -I script -I util -I host -I valid -o runtime/table.cmo runtime/table.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/f32_convert.cmo exec/f32_convert.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/f64_convert.cmo exec/f64_convert.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I exec -I main -I syntax -I text -I binary -I script -I runtime -I util -I host -I valid -o exec/i32_convert.cmo exec/i32_convert.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I syntax -I main -I text -I binary -I exec -I script -I runtime -I util -I host -I valid -o syntax/operators.cmo syntax/operators.ml -ocamlyacc text/parser.mly -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I text -I main -I syntax -I binary -I exec -I script -I runtime -I util -I host -I valid -o text/parser.cmi text/parser.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I text -I main -I syntax -I binary -I exec -I script -I runtime -I util -I host -I valid -o text/lexer.cmi text/lexer.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I util -I main -I syntax -I text -I binary -I exec -I script -I runtime -I host -I valid -o util/sexpr.cmi util/sexpr.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I text -I main -I syntax -I binary -I exec -I script -I runtime -I util -I host -I valid -o text/arrange.cmi text/arrange.mli -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I binary -I main -I syntax -I text -I exec -I script -I runtime -I util -I host -I valid -o binary/decode.cmo binary/decode.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I binary -I main -I syntax -I text -I exec -I script -I runtime -I util -I host -I valid -o binary/encode.cmo binary/encode.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I script -I main -I syntax -I text -I binary -I exec -I runtime -I util -I host -I valid -o script/js.cmo script/js.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I text -I main -I syntax -I binary -I exec -I script -I runtime -I util -I host -I valid -o text/parse.cmo text/parse.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I text -I main -I syntax -I binary -I exec -I script -I runtime -I util -I host -I valid -o text/print.cmo text/print.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I valid -I main -I syntax -I text -I binary -I exec -I script -I runtime -I util -I host -o valid/valid.cmo valid/valid.ml -ocamllex.opt -q text/lexer.mll -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I text -I main -I syntax -I binary -I exec -I script -I runtime -I util -I host -I valid -o text/lexer.cmo text/lexer.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I text -I main -I syntax -I binary -I exec -I script -I runtime -I util -I host -I valid -o text/parser.cmo text/parser.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I text -I main -I syntax -I binary -I exec -I script -I runtime -I util -I host -I valid -o text/arrange.cmo text/arrange.ml -ocamlc.opt -c -w +a-3-4-27-42-44-45 -warn-error +a -I util -I main -I syntax -I text -I binary -I exec -I script -I runtime -I host -I valid -o util/sexpr.cmo util/sexpr.ml -ocamlc.opt bigarray.cma -I util -I binary -I exec -I syntax -I runtime -I host -I main -I script -I text -I valid util/lib.cmo binary/utf8.cmo exec/float.cmo exec/f32.cmo exec/f64.cmo exec/numeric_error.cmo exec/int.cmo exec/i32.cmo exec/i64.cmo exec/i32_convert.cmo exec/f32_convert.cmo exec/i64_convert.cmo exec/f64_convert.cmo syntax/types.cmo syntax/values.cmo runtime/memory.cmo util/source.cmo syntax/ast.cmo exec/eval_numeric.cmo runtime/func.cmo runtime/global.cmo runtime/table.cmo runtime/instance.cmo util/error.cmo exec/eval.cmo host/env.cmo host/spectest.cmo main/flags.cmo script/import.cmo binary/encode.cmo syntax/operators.cmo binary/decode.cmo script/script.cmo text/parser.cmo text/lexer.cmo text/parse.cmo script/js.cmo util/sexpr.cmo text/arrange.cmo text/print.cmo valid/valid.cmo script/run.cmo main/main.cmo -o main/main.byte From fe6c68c58a30fd570587267139e1376aa5d59612 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Fri, 1 Sep 2023 09:43:33 +0200 Subject: [PATCH 26/50] Fix and clean up Makefile --- interpreter/Makefile | 113 +++++++++++--------- interpreter/dune | 5 - interpreter/{tests => unittest}/smallint.ml | 0 3 files changed, 62 insertions(+), 56 deletions(-) rename interpreter/{tests => unittest}/smallint.ml (100%) diff --git a/interpreter/Makefile b/interpreter/Makefile index d67882b294..fe6accd931 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -1,6 +1,6 @@ # This Makefile uses dune but does not rely on ocamlfind or the Opam # package manager to build. However, Opam package management is available -# optionally through the check/install/uninstall targets. +# optionally through the install target. # # The $(JSLIB).js target requires Js_of_ocaml (using ocamlfind). # @@ -9,80 +9,90 @@ # Configuration -NAME = wasm -OPT = $(NAME).exe -ZIP = $(NAME).zip +NAME = wasm +LIB = $(NAME) JSLIB = wast.js +ZIP = $(NAME).zip -BUILDDIR = _build/default +BUILDDIR = _build/default JS = # set to JS shell command to run JS tests, empty to skip # Main targets -.PHONY: default opt jslib all zip smallint +.PHONY: default all ci jslib zip + +default: $(NAME) +all: default test +ci: all jslib zip -default: $(OPT) jslib: $(JSLIB) -all: $(OPT) test zip: $(ZIP) -smallint: smallint.exe -ci: all jslib -# Building executable -.PHONY: $(NAME).exe -$(NAME).exe: - rm -f $(NAME) - dune build $@ - cp $(BUILDDIR)/$(OPT) $(NAME) -.PHONY: smallint.exe -smallint.exe: - dune build $@ +# Building + +$(NAME): + rm -f $@ + dune build $@.exe + ln $(BUILDDIR)/$@.exe $@ + +$(JSLIB): + rm -f $@ + dune build $(@:%.js=%.bc.js) + ln $(BUILDDIR)/$(@:%.js=%.bc.js) $@ + + +# Unit tests + +UNITTESTDIR = unittest +UNITTESTFILES = $(shell cd $(UNITTESTDIR); ls *.ml) +UNITTESTS = $(UNITTESTFILES:%.ml=%) -# Building JavaScript library +.PHONY: unittest -$(JSLIB): $(BUILDDIR)/$(JSLIB) - cp $< $@ +unittest: $(UNITTESTS:%=unittest/%) -$(BUILDDIR)/$(JSLIB): - dune build $(JSLIB) +unittest/%: + dune build $(@F).exe + dune exec ./$(@F).exe -# Executing test suite + +# Test suite TESTDIR = ../test/core -# Skip _output directory, since that's a tmp directory, and list all other wast files. TESTFILES = $(shell cd $(TESTDIR); ls *.wast; ls [a-z]*/*.wast) TESTS = $(TESTFILES:%.wast=%) -.PHONY: test partest dune-test +.PHONY: test partest quiettest + +test: $(NAME) unittest + $(TESTDIR)/run.py --wasm `pwd`/$(NAME) $(if $(JS),--js '$(JS)',) -test: $(OPT) smallint - $(TESTDIR)/run.py --wasm `pwd`/$(BUILDDIR)/$(OPT) $(if $(JS),--js '$(JS)',) - dune exec ./smallint.exe +test/%: $(NAME) + $(TESTDIR)/run.py --wasm `pwd`/$(NAME) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$*.wast -test/%: $(OPT) - $(TESTDIR)/run.py --wasm `pwd`/$(BUILDDIR)/$(OPT) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$*.wast +run/%: $(NAME) + ./$(NAME) $(TESTDIR)/$*.wast -run/%: $(OPT) - ./$(OPT) $(TESTDIR)/$*.wast +partest: $(NAME) + make -j10 quiettest -partest: $(TESTS:%=quiettest/%) - @echo All tests passed. +quiettest: $(TESTS:%=quiettest/%) + @echo All tests passed. -quiettest/%: $(OPT) - @ ( \ - $(TESTDIR)/run.py 2>$(@F).out --wasm `pwd`/$(BUILDDIR)/$(OPT) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$*.wast && \ - rm $(@F).out \ - ) || \ - cat $(@F).out || rm $(@F).out || exit 1 +quiettest/%: $(NAME) + @ ( \ + $(TESTDIR)/run.py 2>$(@F).out --wasm `pwd`/$(NAME) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$*.wast && \ + rm $(@F).out \ + ) || \ + cat $(@F).out || rm $(@F).out || exit 1 -smallinttest: smallint - dune exec ./smallint.exe -dunetest: - dune test +# Packaging + +.PHONY: install install: dune build -p $(NAME) @install @@ -101,15 +111,16 @@ opam-release/%: rm opam-$*.zip @echo Created file ./opam, submit to github opam-repository/packages/wasm/wasm.$*/opam -# Miscellaneous targets - -.PHONY: clean - $(ZIP): git archive --format=zip --prefix=$(NAME)/ -o $@ HEAD + +# Cleanup + +.PHONY: clean distclean + clean: dune clean distclean: clean - rm -f $(NAME) $(JSLIB) + rm -f $(NAME) $(JSLIB) $(ZIP) diff --git a/interpreter/dune b/interpreter/dune index c0e9c292f3..9a853921db 100644 --- a/interpreter/dune +++ b/interpreter/dune @@ -34,11 +34,6 @@ (deps main/main.ml) (action (copy main/main.ml wasm.ml))) -(rule - (targets wast.js) - (deps wast.bc.js) - (action (copy wast.bc.js wast.js))) - (subdir text (rule diff --git a/interpreter/tests/smallint.ml b/interpreter/unittest/smallint.ml similarity index 100% rename from interpreter/tests/smallint.ml rename to interpreter/unittest/smallint.ml From 939b4096b241a8ff5a6aefc3385c643e2c7d2909 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Fri, 1 Sep 2023 09:49:14 +0200 Subject: [PATCH 27/50] Make partest the default --- interpreter/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index fe6accd931..9d345cb46d 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -24,7 +24,7 @@ JS = # set to JS shell command to run JS tests, empty to skip .PHONY: default all ci jslib zip default: $(NAME) -all: default test +all: default partest ci: all jslib zip jslib: $(JSLIB) @@ -87,7 +87,7 @@ quiettest/%: $(NAME) $(TESTDIR)/run.py 2>$(@F).out --wasm `pwd`/$(NAME) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$*.wast && \ rm $(@F).out \ ) || \ - cat $(@F).out || rm $(@F).out || exit 1 + (cat $(@F).out && rm $(@F).out && exit 1) # Packaging From 9be2b369dbbede9b4917e7e2c55fcf28fab715dc Mon Sep 17 00:00:00 2001 From: Keith Winstein Date: Wed, 6 Sep 2023 08:02:38 -0700 Subject: [PATCH 28/50] [spec] Clarify that elements are given as expressions (#1676) --- document/core/syntax/instructions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document/core/syntax/instructions.rst b/document/core/syntax/instructions.rst index 14efc7a1a8..4b8069631d 100644 --- a/document/core/syntax/instructions.rst +++ b/document/core/syntax/instructions.rst @@ -722,7 +722,7 @@ It is guaranteed that no sequence of nested calls using only these instructions Expressions ~~~~~~~~~~~ -:ref:`Function ` bodies, initialization values for :ref:`globals `, and offsets of :ref:`element ` or :ref:`data ` segments are given as expressions, which are sequences of :ref:`instructions ` terminated by an |END| marker. +:ref:`Function ` bodies, initialization values for :ref:`globals `, elements and offsets of :ref:`element ` segments, and offsets of :ref:`data ` segments are given as expressions, which are sequences of :ref:`instructions ` terminated by an |END| marker. .. math:: \begin{array}{llll} From cde89cae3a862c6124bd772927cfd69e8b69b137 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 8 Sep 2023 20:00:20 +0900 Subject: [PATCH 29/50] [test] Fix typos (#1678) --- test/core/unreachable.wast | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/unreachable.wast b/test/core/unreachable.wast index 3074847a3e..19f661be1d 100644 --- a/test/core/unreachable.wast +++ b/test/core/unreachable.wast @@ -6,8 +6,8 @@ (func $dummy3 (param i32 i32 i32)) (func (export "type-i32") (result i32) (unreachable)) - (func (export "type-i64") (result i32) (unreachable)) - (func (export "type-f32") (result f64) (unreachable)) + (func (export "type-i64") (result i64) (unreachable)) + (func (export "type-f32") (result f32) (unreachable)) (func (export "type-f64") (result f64) (unreachable)) (func (export "as-func-first") (result i32) From a994f2d63e69d3b4cdee2a08be0b7741678f8ac6 Mon Sep 17 00:00:00 2001 From: Keith Winstein Date: Wed, 20 Sep 2023 08:02:43 -0700 Subject: [PATCH 30/50] [test] text format: drop support for some pre-standard keywords (#1680) --- test/core/obsolete-keywords.wast | 82 ++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 test/core/obsolete-keywords.wast diff --git a/test/core/obsolete-keywords.wast b/test/core/obsolete-keywords.wast new file mode 100644 index 0000000000..a90eb96d23 --- /dev/null +++ b/test/core/obsolete-keywords.wast @@ -0,0 +1,82 @@ +;; Renamed in https://github.com/WebAssembly/spec/pull/720 +(assert_malformed + (module quote + "(memory 1)" + "(func (drop (current_memory)))" + ) + "unknown operator current_memory" +) + +(assert_malformed + (module quote + "(memory 1)" + "(func (drop (grow_memory (i32.const 0))))" + ) + "unknown operator grow_memory" +) + +;; Renamed in https://github.com/WebAssembly/spec/pull/926 +(assert_malformed + (module quote + "(func (local $i i32) (drop (get_local $i)))" + ) + "unknown operator get_local" +) + +(assert_malformed + (module quote + "(func (local $i i32) (set_local $i (i32.const 0)))" + ) + "unknown operator set_local" +) + +(assert_malformed + (module quote + "(func (local $i i32) (drop (tee_local $i (i32.const 0))))" + ) + "unknown operator tee_local" +) + +(assert_malformed + (module quote + "(global $g anyfunc (ref.null func))" + ) + "unknown operator anyfunc" +) + +(assert_malformed + (module quote + "(global $g i32 (i32.const 0))" + "(func (drop (get_global $g)))" + ) + "unknown operator get_global" +) + +(assert_malformed + (module quote + "(global $g (mut i32) (i32.const 0))" + "(func (set_global $g (i32.const 0)))" + ) + "unknown operator set_global" +) + +(assert_malformed + (module quote + "(func (drop (i32.wrap/i64 (i64.const 0))))" + ) + "unknown operator i32.wrap/i64" +) + +(assert_malformed + (module quote + "(func (drop (i32.trunc_s:sat/f32 (f32.const 0))))" + ) + "unknown operator i32.trunc_s:sat/f32" +) + +(assert_malformed + (module quote + "(func (drop (f32x4.convert_s/i32x4 (v128.const i64x2 0 0))))" + ) + "unknown operator f32x4.convert_s/i32x4" +) From 6d0ab8bde04261528a924f1d01b3146df5f37c8e Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Wed, 20 Sep 2023 18:28:06 -0700 Subject: [PATCH 31/50] [test] Test that atypical folded if conditions are parsed correctly (#1682) Check that the condition must be a sequence of folded (as opposed to unfolded) instructions, but also check that having zero or multiple folded instructions is allowed. --- test/core/if.wast | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/core/if.wast b/test/core/if.wast index 553f90d20e..35a4cf6cb9 100644 --- a/test/core/if.wast +++ b/test/core/if.wast @@ -524,6 +524,14 @@ ) (drop) (drop) (drop) ) + + ;; Atypical folded condition syntax + + (func (export "atypical-condition") + i32.const 0 + (if (then) (else)) + (if (i32.const 1) (i32.eqz) (then) (else)) + ) ) (assert_return (invoke "empty" (i32.const 0))) @@ -722,6 +730,8 @@ (assert_return (invoke "type-use")) +(assert_return (invoke "atypical-condition")) + (assert_malformed (module quote "(type $sig (func (param i32) (result i32)))" @@ -1548,3 +1558,7 @@ (module quote "(func i32.const 0 if $a else $l end $l)") "mismatching label" ) +(assert_malformed + (module quote "(func (if i32.const 0 (then) (else)))") + "unexpected token" +) From be6b12ad2bfb6d4a73dbe20fb89cfb3e07e74510 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Thu, 21 Sep 2023 14:44:11 +0200 Subject: [PATCH 32/50] [interpreter] Run unittests with partest --- interpreter/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index 9d345cb46d..f7ecc482ae 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -76,7 +76,7 @@ test/%: $(NAME) run/%: $(NAME) ./$(NAME) $(TESTDIR)/$*.wast -partest: $(NAME) +partest: $(NAME) unittest make -j10 quiettest quiettest: $(TESTS:%=quiettest/%) From da278f8aff3c8fd529c27b59fc4701bcf696f15e Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 21 Sep 2023 18:06:15 -0700 Subject: [PATCH 33/50] [interpreter] Update Makefile to ignore output of `cd` (#1683) The interpreter Makefile captures the output of cding into the unittest directory and listing the unittest files it contains. Under some circumstances, the `cd` command will print the new current directory, and that output was incorrectly being interpreted as the path to a unittest, causing the build to fail. Fix this problem by redirecting the output of cd, if any, to /dev/null. Fixes #1681. --- interpreter/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interpreter/Makefile b/interpreter/Makefile index f7ecc482ae..688bbc1aa8 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -47,7 +47,7 @@ $(JSLIB): # Unit tests UNITTESTDIR = unittest -UNITTESTFILES = $(shell cd $(UNITTESTDIR); ls *.ml) +UNITTESTFILES = $(shell cd $(UNITTESTDIR) > /dev/null; ls *.ml) UNITTESTS = $(UNITTESTFILES:%.ml=%) .PHONY: unittest @@ -62,7 +62,7 @@ unittest/%: # Test suite TESTDIR = ../test/core -TESTFILES = $(shell cd $(TESTDIR); ls *.wast; ls [a-z]*/*.wast) +TESTFILES = $(shell cd $(TESTDIR) > /dev/null; ls *.wast; ls [a-z]*/*.wast) TESTS = $(TESTFILES:%.wast=%) .PHONY: test partest quiettest From af59a5c96bd2a5be0f9b4d693dc93fb5b0052d7a Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 25 Sep 2023 15:04:10 +0200 Subject: [PATCH 34/50] [interpreter] Fix Makefile targets --- interpreter/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interpreter/Makefile b/interpreter/Makefile index 688bbc1aa8..5939f838ec 100644 --- a/interpreter/Makefile +++ b/interpreter/Makefile @@ -33,6 +33,8 @@ zip: $(ZIP) # Building +.PHONY: $(NAME) $(JSLIB) + $(NAME): rm -f $@ dune build $@.exe From 7284293fc49a854cd22ebf7dfe50ab2b3203efbb Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 25 Sep 2023 16:00:07 +0200 Subject: [PATCH 35/50] [test] Merge token and tokens tests --- test/core/token.wast | 276 ++++++++++++++++++++++++++++++++++++++++++ test/core/tokens.wast | 274 ----------------------------------------- 2 files changed, 276 insertions(+), 274 deletions(-) delete mode 100644 test/core/tokens.wast diff --git a/test/core/token.wast b/test/core/token.wast index 1dcd32e7f7..624bdca6d8 100644 --- a/test/core/token.wast +++ b/test/core/token.wast @@ -8,3 +8,279 @@ (module quote "(func br 0drop)") "unknown operator" ) + + +;; Tokens can be delimited by parentheses + +(module + (func(nop)) +) +(module + (func (nop)nop) +) +(module + (func nop(nop)) +) +(module + (func(nop)(nop)) +) +(module + (func $f(nop)) +) +(module + (func br 0(nop)) +) +(module + (table 1 funcref) + (func) + (elem (i32.const 0)0) +) +(module + (table 1 funcref) + (func $f) + (elem (i32.const 0)$f) +) +(module + (memory 1) + (data (i32.const 0)"a") +) +(module + (import "spectest" "print"(func)) +) + + +;; Tokens can be delimited by comments + +(module + (func;;bla + ) +) +(module + (func (nop);;bla + ) +) +(module + (func nop;;bla + ) +) +(module + (func $f;;bla + ) +) +(module + (func br 0;;bla + ) +) +(module + (data "a";;bla + ) +) + + +;; Space required between symbols and non-parenthesis tokens + +(module + (func (block $l (i32.const 0) (br_table 0 $l))) +) +(assert_malformed + (module quote + "(func (block $l (i32.const 0) (br_table 0$l)))" + ) + "unknown operator" +) + +(module + (func (block $l (i32.const 0) (br_table $l 0))) +) +(assert_malformed + (module quote + "(func (block $l (i32.const 0) (br_table $l0)))" + ) + "unknown label" +) + +(module + (func (block $l (i32.const 0) (br_table $l $l))) +) +(assert_malformed + (module quote + "(func (block $l (i32.const 0) (br_table $l$l)))" + ) + "unknown label" +) + +(module + (func (block $l0 (i32.const 0) (br_table $l0))) +) +(module + (func (block $l$l (i32.const 0) (br_table $l$l))) +) + + +;; Space required between strings and non-parenthesis tokens + +(module + (data "a") +) +(assert_malformed + (module quote + "(data\"a\")" + ) + "unknown operator" +) + +(module + (data $l "a") +) +(assert_malformed + (module quote + "(data $l\"a\")" + ) + "unknown operator" +) + +(module + (data $l " a") +) +(assert_malformed + (module quote + "(data $l\" a\")" + ) + "unknown operator" +) + +(module + (data $l "a ") +) +(assert_malformed + (module quote + "(data $l\"a \")" + ) + "unknown operator" +) + +(module + (data $l "a " "b") +) +(assert_malformed + (module quote + "(data $l\"a \"\"b\")" + ) + "unknown operator" +) + +(module + (data $l "") +) +(assert_malformed + (module quote + "(data $l\"\")" + ) + "unknown operator" +) + +(module + (data $l " ") +) +(assert_malformed + (module quote + "(data $l\" \")" + ) + "unknown operator" +) + +(module + (data $l " ") +) +(assert_malformed + (module quote + "(data $l\" \")" + ) + "unknown operator" +) + +(module + (data "a" "b") +) +(assert_malformed + (module quote + "(data \"a\"\"b\")" + ) + "unknown operator" +) + +(module + (data "a" " b") +) +(assert_malformed + (module quote + "(data \"a\"\" b\")" + ) + "unknown operator" +) + +(module + (data "a " "b") +) +(assert_malformed + (module quote + "(data \"a \"\"b\")" + ) + "unknown operator" +) + +(module + (data "" "") +) +(assert_malformed + (module quote + "(data \"\"\"\")" + ) + "unknown operator" +) + +(module + (data "" " ") +) +(assert_malformed + (module quote + "(data \"\"\" \")" + ) + "unknown operator" +) + +(module + (data " " "") +) +(assert_malformed + (module quote + "(data \" \"\"\")" + ) + "unknown operator" +) + + +(assert_malformed + (module quote + "(func \"a\"x)" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(func \"a\"0)" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(func 0\"a\")" + ) + "unknown operator" +) +(assert_malformed + (module quote + "(func \"a\"$x)" + ) + "unknown operator" +) diff --git a/test/core/tokens.wast b/test/core/tokens.wast deleted file mode 100644 index 4e785154e9..0000000000 --- a/test/core/tokens.wast +++ /dev/null @@ -1,274 +0,0 @@ -;; Tokens can be delimited by parentheses - -(module - (func(nop)) -) -(module - (func (nop)nop) -) -(module - (func nop(nop)) -) -(module - (func(nop)(nop)) -) -(module - (func $f(nop)) -) -(module - (func br 0(nop)) -) -(module - (table 1 funcref) - (func) - (elem (i32.const 0)0) -) -(module - (table 1 funcref) - (func $f) - (elem (i32.const 0)$f) -) -(module - (memory 1) - (data (i32.const 0)"a") -) -(module - (import "spectest" "print"(func)) -) - - -;; Tokens can be delimited by comments - -(module - (func;;bla - ) -) -(module - (func (nop);;bla - ) -) -(module - (func nop;;bla - ) -) -(module - (func $f;;bla - ) -) -(module - (func br 0;;bla - ) -) -(module - (data "a";;bla - ) -) - - -;; Space required between symbols and non-parenthesis tokens - -(module - (func (block $l (i32.const 0) (br_table 0 $l))) -) -(assert_malformed - (module quote - "(func (block $l (i32.const 0) (br_table 0$l)))" - ) - "unknown operator" -) - -(module - (func (block $l (i32.const 0) (br_table $l 0))) -) -(assert_malformed - (module quote - "(func (block $l (i32.const 0) (br_table $l0)))" - ) - "unknown label" -) - -(module - (func (block $l (i32.const 0) (br_table $l $l))) -) -(assert_malformed - (module quote - "(func (block $l (i32.const 0) (br_table $l$l)))" - ) - "unknown label" -) - -(module - (func (block $l0 (i32.const 0) (br_table $l0))) -) -(module - (func (block $l$l (i32.const 0) (br_table $l$l))) -) - - -;; Space required between strings and non-parenthesis tokens - -(module - (data "a") -) -(assert_malformed - (module quote - "(data\"a\")" - ) - "unknown operator" -) - -(module - (data $l "a") -) -(assert_malformed - (module quote - "(data $l\"a\")" - ) - "unknown operator" -) - -(module - (data $l " a") -) -(assert_malformed - (module quote - "(data $l\" a\")" - ) - "unknown operator" -) - -(module - (data $l "a ") -) -(assert_malformed - (module quote - "(data $l\"a \")" - ) - "unknown operator" -) - -(module - (data $l "a " "b") -) -(assert_malformed - (module quote - "(data $l\"a \"\"b\")" - ) - "unknown operator" -) - -(module - (data $l "") -) -(assert_malformed - (module quote - "(data $l\"\")" - ) - "unknown operator" -) - -(module - (data $l " ") -) -(assert_malformed - (module quote - "(data $l\" \")" - ) - "unknown operator" -) - -(module - (data $l " ") -) -(assert_malformed - (module quote - "(data $l\" \")" - ) - "unknown operator" -) - -(module - (data "a" "b") -) -(assert_malformed - (module quote - "(data \"a\"\"b\")" - ) - "unknown operator" -) - -(module - (data "a" " b") -) -(assert_malformed - (module quote - "(data \"a\"\" b\")" - ) - "unknown operator" -) - -(module - (data "a " "b") -) -(assert_malformed - (module quote - "(data \"a \"\"b\")" - ) - "unknown operator" -) - -(module - (data "" "") -) -(assert_malformed - (module quote - "(data \"\"\"\")" - ) - "unknown operator" -) - -(module - (data "" " ") -) -(assert_malformed - (module quote - "(data \"\"\" \")" - ) - "unknown operator" -) - -(module - (data " " "") -) -(assert_malformed - (module quote - "(data \" \"\"\")" - ) - "unknown operator" -) - - -(assert_malformed - (module quote - "(func \"a\"x)" - ) - "unknown operator" -) -(assert_malformed - (module quote - "(func \"a\"0)" - ) - "unknown operator" -) -(assert_malformed - (module quote - "(func 0\"a\")" - ) - "unknown operator" -) -(assert_malformed - (module quote - "(func \"a\"$x)" - ) - "unknown operator" -) From e5435b2d5590a6f971a5353bd50fac40be267f6a Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 25 Sep 2023 16:01:04 +0200 Subject: [PATCH 36/50] [test] Make runner robust wrt parallel races --- test/core/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/run.py b/test/core/run.py index 27c0694364..0b98e4e3dd 100755 --- a/test/core/run.py +++ b/test/core/run.py @@ -112,7 +112,7 @@ def _runTestFile(self, inputPath): if __name__ == "__main__": if not os.path.exists(outputDir): - os.makedirs(outputDir) + os.makedirs(outputDir, exists_ok=True) for fileName in inputFiles: testName = 'test ' + os.path.basename(fileName) setattr(RunTests, testName, lambda self, file=fileName: self._runTestFile(file)) From ef9c15754497dd92810d3529f1c95e7b91c8c73d Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Mon, 25 Sep 2023 07:03:56 -0700 Subject: [PATCH 37/50] [interpreter] Update {memory,table}.copy to match spec text (#1666) Change the interpreter implementation of backward copying in the evaluation of memory.copy and table.copy to match the implementation given in the spec. This does not change any behavior. --- interpreter/exec/eval.ml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/interpreter/exec/eval.ml b/interpreter/exec/eval.ml index 4a886fe7fc..565f1a65c1 100644 --- a/interpreter/exec/eval.ml +++ b/interpreter/exec/eval.ml @@ -351,15 +351,16 @@ let rec step (c : config) : config = Plain (TableCopy (x, y)); ] else (* d > s *) + let n' = I32.sub n 1l in vs', List.map (Lib.Fun.flip (@@) e.at) [ - Plain (Const (I32 (I32.add d 1l) @@ e.at)); - Plain (Const (I32 (I32.add s 1l) @@ e.at)); - Plain (Const (I32 (I32.sub n 1l) @@ e.at)); - Plain (TableCopy (x, y)); - Plain (Const (I32 d @@ e.at)); - Plain (Const (I32 s @@ e.at)); + Plain (Const (I32 (I32.add d n') @@ e.at)); + Plain (Const (I32 (I32.add s n') @@ e.at)); Plain (TableGet y); Plain (TableSet x); + Plain (Const (I32 d @@ e.at)); + Plain (Const (I32 s @@ e.at)); + Plain (Const (I32 n' @@ e.at)); + Plain (TableCopy (x, y)); ] | TableInit (x, y), Num (I32 n) :: Num (I32 s) :: Num (I32 d) :: vs' -> @@ -513,17 +514,18 @@ let rec step (c : config) : config = Plain (MemoryCopy); ] else (* d > s *) + let n' = I32.sub n 1l in vs', List.map (Lib.Fun.flip (@@) e.at) [ - Plain (Const (I32 (I32.add d 1l) @@ e.at)); - Plain (Const (I32 (I32.add s 1l) @@ e.at)); - Plain (Const (I32 (I32.sub n 1l) @@ e.at)); - Plain (MemoryCopy); - Plain (Const (I32 d @@ e.at)); - Plain (Const (I32 s @@ e.at)); + Plain (Const (I32 (I32.add d n') @@ e.at)); + Plain (Const (I32 (I32.add s n') @@ e.at)); Plain (Load {ty = Types.I32T; align = 0; offset = 0l; pack = Some (Pack8, ZX)}); Plain (Store {ty = Types.I32T; align = 0; offset = 0l; pack = Some Pack8}); + Plain (Const (I32 d @@ e.at)); + Plain (Const (I32 s @@ e.at)); + Plain (Const (I32 n' @@ e.at)); + Plain (MemoryCopy); ] | MemoryInit x, Num (I32 n) :: Num (I32 s) :: Num (I32 d) :: vs' -> From 245d382a8b20c2682b6c615ced60f42ae184b765 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 25 Sep 2023 16:11:13 +0200 Subject: [PATCH 38/50] [test] Fix typo in runner --- test/core/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/core/run.py b/test/core/run.py index 0b98e4e3dd..cbdb25654d 100755 --- a/test/core/run.py +++ b/test/core/run.py @@ -112,7 +112,7 @@ def _runTestFile(self, inputPath): if __name__ == "__main__": if not os.path.exists(outputDir): - os.makedirs(outputDir, exists_ok=True) + os.makedirs(outputDir, exist_ok=True) for fileName in inputFiles: testName = 'test ' + os.path.basename(fileName) setattr(RunTests, testName, lambda self, file=fileName: self._runTestFile(file)) From 953200273761d0a5032e7aa6368a3b123fad2296 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Thu, 27 Jul 2023 09:31:39 +0200 Subject: [PATCH 39/50] [js-api] Editorial: fix some references to 'agent' --- document/js-api/index.bs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index e31e47abd1..198cbe4407 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -154,6 +154,7 @@ spec:ecma-262; type:exception; for:ECMAScript; text:Error spec:ecmascript; type:exception; for:ECMAScript; text:TypeError spec:ecmascript; type:exception; for:ECMAScript; text:RangeError spec:ecmascript; type:interface; for:ECMAScript; text:ArrayBuffer +spec:ecmascript; type:dfn; text:agent spec:webidl; type:dfn; text:resolve @@ -845,7 +846,7 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
To create a global object from a [=global address=] |globaladdr|, perform the following steps: - 1. Let |map| be the current [=agent=]'s associated [=Global object cache=]. + 1. Let |map| be the [=surrounding agent=]'s associated [=Global object cache=]. 1. If |map|[|globaladdr|] [=map/exists=], 1. Return |map|[|globaladdr|]. 1. Let |global| be a [=/new=] {{Global}}. From de85aff3c61b055d6ef97847426f899f838e13b4 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Thu, 27 Jul 2023 09:32:05 +0200 Subject: [PATCH 40/50] [js-api] Editorial: replace reference to removed IterableToList abstract operation --- document/js-api/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 198cbe4407..9345955c55 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1013,7 +1013,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. Otherwise, 1. Let |method| be [=?=] [$GetMethod$](|ret|, {{@@iterator}}). 1. If |method| is undefined, [=throw=] a {{TypeError}}. - 1. Let |values| be [=?=] [$IterableToList$](|ret|, |method|). + 1. Let |values| be [=?=] [$IteratorToList$]([=?=] [$GetIteratorFromMethod$](|ret|, |method|)). 1. Let |wasmValues| be a new, empty [=list=]. 1. If |values|'s [=list/size=] is not |resultsSize|, throw a {{TypeError}} exception. 1. For each |value| and |resultType| in |values| and |results|, paired linearly, From 38885fff376de130bbb6b03e4fe7b513552214d0 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Thu, 27 Jul 2023 09:32:31 +0200 Subject: [PATCH 41/50] [js-api] Editorial: drop unused algorithm This became unused in #745. --- document/js-api/index.bs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 9345955c55..168f54800b 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -433,16 +433,6 @@ The verification of WebAssembly type requirements is deferred to the 1. Return |promise|.
-
- To synchronously instantiate a WebAssembly module from a {{Module}} |moduleObject| and imports |importObject|, perform the following steps: - 1. Let |module| be |moduleObject|.\[[Module]]. - 1. [=Read the imports=] of |module| with imports |importObject|, and let |imports| be the result. - 1. [=Instantiate the core of a WebAssembly module=] |module| with |imports|, and let |instance| be the result. - 1. Let |instanceObject| be a [=/new=] {{Instance}}. - 1. [=initialize an instance object|Initialize=] |instanceObject| from |module| and |instance|. - 1. Return |instanceObject|. -
-
To instantiate a promise of a module |promiseOfModule| with imports |importObject|, perform the following steps: From 2eb52be6c975773e30773cf382853aec71682761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20J=C3=A4genstedt?= Date: Fri, 6 Oct 2023 16:11:04 +0200 Subject: [PATCH 42/50] [ci] Don't install ocamlbuild (#1686) --- .github/workflows/ci-interpreter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-interpreter.yml b/.github/workflows/ci-interpreter.yml index 8f7b02c03f..5f57448dd3 100644 --- a/.github/workflows/ci-interpreter.yml +++ b/.github/workflows/ci-interpreter.yml @@ -23,7 +23,7 @@ jobs: with: ocaml-compiler: 4.14.x - name: Setup OCaml tools - run: opam install --yes ocamlbuild.0.14.0 ocamlfind.1.9.5 js_of_ocaml.4.0.0 js_of_ocaml-ppx.4.0.0 + run: opam install --yes ocamlfind.1.9.5 js_of_ocaml.4.0.0 js_of_ocaml-ppx.4.0.0 - name: Setup Node.js uses: actions/setup-node@v2 with: From e157117998bf12869d8d57427388fc398731255f Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Tue, 19 Sep 2023 11:39:55 -0700 Subject: [PATCH 43/50] [test] Add JS API tests for i31ref Also updates wasm-module-builder for necessary changes, such as support for global init expressions with GC operations. These changes are imported from the upstream V8 file. This module builder change also requires changes in a few other test files that used the old init API. --- test/js-api/gc/i31.tentative.any.js | 98 +++++++++++++++++++++++++++++ test/js-api/instanceTestFactory.js | 6 +- test/js-api/module/exports.any.js | 6 +- test/js-api/wasm-module-builder.js | 95 +++++++++++++++------------- 4 files changed, 157 insertions(+), 48 deletions(-) create mode 100644 test/js-api/gc/i31.tentative.any.js diff --git a/test/js-api/gc/i31.tentative.any.js b/test/js-api/gc/i31.tentative.any.js new file mode 100644 index 0000000000..17fd82440c --- /dev/null +++ b/test/js-api/gc/i31.tentative.any.js @@ -0,0 +1,98 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +let exports = {}; +setup(() => { + const builder = new WasmModuleBuilder(); + const i31Ref = wasmRefType(kWasmI31Ref); + const i31NullableRef = wasmRefNullType(kWasmI31Ref); + const anyRef = wasmRefType(kWasmAnyRef); + + builder + .addFunction("makeI31", makeSig_r_x(i31Ref, kWasmI32)) + .addBody([kExprLocalGet, 0, + ...GCInstr(kExprI31New)]) + .exportFunc(); + + builder + .addFunction("castI31", makeSig_r_x(kWasmI32, anyRef)) + .addBody([kExprLocalGet, 0, + ...GCInstr(kExprRefCast), kI31RefCode, + ...GCInstr(kExprI31GetU)]) + .exportFunc(); + + builder + .addFunction("getI31", makeSig_r_x(kWasmI32, i31Ref)) + .addBody([kExprLocalGet, 0, + ...GCInstr(kExprI31GetS)]) + .exportFunc(); + + builder + .addFunction("argI31", makeSig_v_x(i31NullableRef)) + .addBody([]) + .exportFunc(); + + builder + .addGlobal(i31NullableRef, true, [...wasmI32Const(0), ...GCInstr(kExprI31New)]) + builder + .addExportOfKind("i31Global", kExternalGlobal, 0); + + builder + .addTable(i31NullableRef, 10) + builder + .addExportOfKind("i31Table", kExternalTable, 0); + + const buffer = builder.toBuffer(); + const module = new WebAssembly.Module(buffer); + const instance = new WebAssembly.Instance(module, {}); + exports = instance.exports; +}); + +test(() => { + assert_equals(exports.makeI31(42), 42); + assert_equals(exports.makeI31(2 ** 30 - 1), 2 ** 30 - 1); + assert_equals(exports.makeI31(2 ** 30), -(2 ** 30)); + assert_equals(exports.makeI31(-(2 ** 30)), -(2 ** 30)); + assert_equals(exports.makeI31(2 ** 31 - 1), -1); + assert_equals(exports.makeI31(2 ** 31), 0); +}, "i31ref conversion to Number"); + +test(() => { + assert_equals(exports.getI31(exports.makeI31(42)), 42); + assert_equals(exports.getI31(42), 42); + assert_equals(exports.getI31(2.0 ** 30 - 1), 2 ** 30 - 1); + assert_equals(exports.getI31(-(2 ** 30)), -(2 ** 30)); +}, "Number conversion to i31ref"); + +test(() => { + exports.argI31(null); + assert_throws_js(TypeError, () => exports.argI31(2 ** 30)); + assert_throws_js(TypeError, () => exports.argI31(-(2 ** 30) - 1)); + assert_throws_js(TypeError, () => exports.argI31(2n)); + assert_throws_js(TypeError, () => exports.argI31(() => 3)); + assert_throws_js(TypeError, () => exports.argI31(exports.getI31)); +}, "Check i31ref argument type"); + +test(() => { + assert_equals(exports.castI31(42), 42); + assert_equals(exports.castI31(2 ** 30 - 1), 2 ** 30 - 1); + assert_throws_js(WebAssembly.RuntimeError, () => { exports.castI31(2 ** 30); }); + assert_throws_js(WebAssembly.RuntimeError, () => { exports.castI31(-(2 ** 30) - 1); }); + assert_throws_js(WebAssembly.RuntimeError, () => { exports.castI31(2 ** 32); }); +}, "Numbers in i31 range are i31ref, not hostref"); + +test(() => { + assert_equals(exports.i31Global.value, 0); + exports.i31Global.value = 42; + assert_throws_js(TypeError, () => exports.i31Global.value = 2 ** 30); + assert_throws_js(TypeError, () => exports.i31Global.value = -(2 ** 30) - 1); + assert_equals(exports.i31Global.value, 42); +}, "i31ref global"); + +test(() => { + assert_equals(exports.i31Table.get(0), null); + exports.i31Table.set(0, 42); + assert_throws_js(TypeError, () => exports.i31Table.set(0, 2 ** 30)); + assert_throws_js(TypeError, () => exports.i31Table.set(0, -(2 ** 30) - 1)); + assert_equals(exports.i31Table.get(0), 42); +}, "i31ref table"); diff --git a/test/js-api/instanceTestFactory.js b/test/js-api/instanceTestFactory.js index ac468947ec..7936810a52 100644 --- a/test/js-api/instanceTestFactory.js +++ b/test/js-api/instanceTestFactory.js @@ -237,7 +237,7 @@ const instanceTestFactory = [ builder.addGlobal(kWasmI32, true) .exportAs("") - .init = 7; + .init = wasmI32Const(7); const buffer = builder.toBuffer(); @@ -273,10 +273,10 @@ const instanceTestFactory = [ builder.addGlobal(kWasmI32, true) .exportAs("global") - .init = 7; + .init = wasmI32Const(7); builder.addGlobal(kWasmF64, true) .exportAs("global2") - .init = 1.2; + .init = wasmF64Const(1.2); builder.addMemory(4, 8, true); diff --git a/test/js-api/module/exports.any.js b/test/js-api/module/exports.any.js index 40a3935a4a..0d62725ae4 100644 --- a/test/js-api/module/exports.any.js +++ b/test/js-api/module/exports.any.js @@ -109,10 +109,10 @@ test(() => { builder.addGlobal(kWasmI32, true) .exportAs("global") - .init = 7; + .init = wasmI32Const(7); builder.addGlobal(kWasmF64, true) .exportAs("global2") - .init = 1.2; + .init = wasmF64Const(1.2); builder.addMemory(0, 256, true); @@ -167,7 +167,7 @@ test(() => { builder.addGlobal(kWasmI32, true) .exportAs("") - .init = 7; + .init = wasmI32Const(7); const buffer = builder.toBuffer() const module = new WebAssembly.Module(buffer); diff --git a/test/js-api/wasm-module-builder.js b/test/js-api/wasm-module-builder.js index 4349be2cc7..8c6519239b 100644 --- a/test/js-api/wasm-module-builder.js +++ b/test/js-api/wasm-module-builder.js @@ -650,6 +650,11 @@ class Binary { } } + emit_init_expr(expr) { + this.emit_bytes(expr); + this.emit_u8(kExprEnd); + } + emit_header() { this.emit_bytes([ kWasmH0, kWasmH1, kWasmH2, kWasmH3, kWasmV0, kWasmV1, kWasmV2, kWasmV3 @@ -740,11 +745,11 @@ class WasmFunctionBuilder { } class WasmGlobalBuilder { - constructor(module, type, mutable) { + constructor(module, type, mutable, init) { this.module = module; this.type = type; this.mutable = mutable; - this.init = 0; + this.init = init; } exportAs(name) { @@ -754,13 +759,24 @@ class WasmGlobalBuilder { } } +function checkExpr(expr) { + for (let b of expr) { + if (typeof b !== 'number' || (b & (~0xFF)) !== 0) { + throw new Error( + 'invalid body (entries must be 8 bit numbers): ' + expr); + } + } +} + class WasmTableBuilder { - constructor(module, type, initial_size, max_size) { + constructor(module, type, initial_size, max_size, init_expr) { this.module = module; this.type = type; this.initial_size = initial_size; this.has_max = max_size != undefined; this.max_size = max_size; + this.init_expr = init_expr; + this.has_init = init_expr !== undefined; } exportAs(name) { @@ -875,18 +891,44 @@ class WasmModuleBuilder { return this.types.length - 1; } - addGlobal(local_type, mutable) { - let glob = new WasmGlobalBuilder(this, local_type, mutable); + static defaultFor(type) { + switch (type) { + case kWasmI32: + return wasmI32Const(0); + case kWasmI64: + return wasmI64Const(0); + case kWasmF32: + return wasmF32Const(0.0); + case kWasmF64: + return wasmF64Const(0.0); + case kWasmS128: + return [kSimdPrefix, kExprS128Const, ...(new Array(16).fill(0))]; + default: + if ((typeof type) != 'number' && type.opcode != kWasmRefNull) { + throw new Error("Non-defaultable type"); + } + let heap_type = (typeof type) == 'number' ? type : type.heap_type; + return [kExprRefNull, ...wasmSignedLeb(heap_type, kMaxVarInt32Size)]; + } + } + + addGlobal(type, mutable, init) { + if (init === undefined) init = WasmModuleBuilder.defaultFor(type); + checkExpr(init); + let glob = new WasmGlobalBuilder(this, type, mutable, init); glob.index = this.globals.length + this.num_imported_globals; this.globals.push(glob); return glob; } - addTable(type, initial_size, max_size = undefined) { - if (type != kWasmExternRef && type != kWasmAnyFunc) { - throw new Error('Tables must be of type kWasmExternRef or kWasmAnyFunc'); + addTable(type, initial_size, max_size = undefined, init_expr = undefined) { + if (type == kWasmI32 || type == kWasmI64 || type == kWasmF32 || + type == kWasmF64 || type == kWasmS128 || type == kWasmStmt) { + throw new Error('Tables must be of a reference type'); } - let table = new WasmTableBuilder(this, type, initial_size, max_size); + if (init_expr != undefined) checkExpr(init_expr); + let table = new WasmTableBuilder( + this, type, initial_size, max_size, init_expr); table.index = this.tables.length + this.num_imported_tables; this.tables.push(table); return table; @@ -1161,6 +1203,7 @@ class WasmModuleBuilder { section.emit_u8(table.has_max); section.emit_u32v(table.initial_size); if (table.has_max) section.emit_u32v(table.max_size); + if (table.has_init) section.emit_init_expr(table.init_expr); } }); } @@ -1191,39 +1234,7 @@ class WasmModuleBuilder { for (let global of wasm.globals) { section.emit_type(global.type); section.emit_u8(global.mutable); - if ((typeof global.init_index) == "undefined") { - // Emit a constant initializer. - switch (global.type) { - case kWasmI32: - section.emit_u8(kExprI32Const); - section.emit_u32v(global.init); - break; - case kWasmI64: - section.emit_u8(kExprI64Const); - section.emit_u64v(global.init); - break; - case kWasmF32: - section.emit_bytes(wasmF32Const(global.init)); - break; - case kWasmF64: - section.emit_bytes(wasmF64Const(global.init)); - break; - case kWasmAnyFunc: - case kWasmExternRef: - if (global.function_index !== undefined) { - section.emit_u8(kExprRefFunc); - section.emit_u32v(global.function_index); - } else { - section.emit_u8(kExprRefNull); - } - break; - } - } else { - // Emit a global-index initializer. - section.emit_u8(kExprGlobalGet); - section.emit_u32v(global.init_index); - } - section.emit_u8(kExprEnd); // end of init expression + section.emit_init_expr(global.init); } }); } From 4e87dd2b2fa9abf7066700157f082536b906aa39 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Wed, 20 Sep 2023 14:50:01 -0700 Subject: [PATCH 44/50] [test] Add JS API tests for boundary casts Mostly tests the ToWebAssemblyValue metafunction --- test/js-api/gc/casts.tentative.any.js | 332 ++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 test/js-api/gc/casts.tentative.any.js diff --git a/test/js-api/gc/casts.tentative.any.js b/test/js-api/gc/casts.tentative.any.js new file mode 100644 index 0000000000..cce06224fd --- /dev/null +++ b/test/js-api/gc/casts.tentative.any.js @@ -0,0 +1,332 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +let exports = {}; +setup(() => { + const builder = new WasmModuleBuilder(); + const structIndex = builder.addStruct([makeField(kWasmI32, true)]); + const arrayIndex = builder.addArray(kWasmI32, true); + const structIndex2 = builder.addStruct([makeField(kWasmF32, true)]); + const arrayIndex2 = builder.addArray(kWasmF32, true); + const funcIndex = builder.addType({ params: [], results: [] }); + const funcIndex2 = builder.addType({ params: [], results: [kWasmI32] }); + + const argFunctions = [ + { name: "any", code: kWasmAnyRef }, + { name: "eq", code: kWasmEqRef }, + { name: "struct", code: kWasmStructRef }, + { name: "array", code: kWasmArrayRef }, + { name: "i31", code: kWasmI31Ref }, + { name: "func", code: kWasmFuncRef }, + { name: "extern", code: kWasmExternRef }, + { name: "none", code: kWasmNullRef }, + { name: "nofunc", code: kWasmNullFuncRef }, + { name: "noextern", code: kWasmNullExternRef }, + { name: "concreteStruct", code: structIndex }, + { name: "concreteArray", code: arrayIndex }, + { name: "concreteFunc", code: funcIndex }, + ]; + + for (const desc of argFunctions) { + builder + .addFunction(desc.name + "Arg", makeSig_v_x(wasmRefType(desc.code))) + .addBody([]) + .exportFunc(); + + builder + .addFunction(desc.name + "NullableArg", makeSig_v_x(wasmRefNullType(desc.code))) + .addBody([]) + .exportFunc(); + } + + builder + .addFunction("makeStruct", makeSig_r_v(wasmRefType(structIndex))) + .addBody([...wasmI32Const(42), + ...GCInstr(kExprStructNew), structIndex]) + .exportFunc(); + + builder + .addFunction("makeArray", makeSig_r_v(wasmRefType(arrayIndex))) + .addBody([...wasmI32Const(5), ...wasmI32Const(42), + ...GCInstr(kExprArrayNew), arrayIndex]) + .exportFunc(); + + builder + .addFunction("makeStruct2", makeSig_r_v(wasmRefType(structIndex2))) + .addBody([...wasmF32Const(42), + ...GCInstr(kExprStructNew), structIndex2]) + .exportFunc(); + + builder + .addFunction("makeArray2", makeSig_r_v(wasmRefType(arrayIndex2))) + .addBody([...wasmF32Const(42), ...wasmI32Const(5), + ...GCInstr(kExprArrayNew), arrayIndex2]) + .exportFunc(); + + builder + .addFunction("testFunc", funcIndex) + .addBody([]) + .exportFunc(); + + builder + .addFunction("testFunc2", funcIndex2) + .addBody([...wasmI32Const(42)]) + .exportFunc(); + + const buffer = builder.toBuffer(); + const module = new WebAssembly.Module(buffer); + const instance = new WebAssembly.Instance(module, {}); + exports = instance.exports; +}); + +test(() => { + exports.anyArg(exports.makeStruct()); + exports.anyArg(exports.makeArray()); + exports.anyArg(42); + exports.anyArg(42n); + exports.anyArg("foo"); + exports.anyArg({}); + exports.anyArg(() => {}); + exports.anyArg(exports.testFunc); + assert_throws_js(TypeError, () => exports.anyArg(null)); + + exports.anyNullableArg(null); + exports.anyNullableArg(exports.makeStruct()); + exports.anyNullableArg(exports.makeArray()); + exports.anyNullableArg(42); + exports.anyNullableArg(42n); + exports.anyNullableArg("foo"); + exports.anyNullableArg({}); + exports.anyNullableArg(() => {}); + exports.anyNullableArg(exports.testFunc); +}, "anyref casts"); + +test(() => { + exports.eqArg(exports.makeStruct()); + exports.eqArg(exports.makeArray()); + exports.eqArg(42); + assert_throws_js(TypeError, () => exports.eqArg(42n)); + assert_throws_js(TypeError, () => exports.eqArg("foo")); + assert_throws_js(TypeError, () => exports.eqArg({})); + assert_throws_js(TypeError, () => exports.eqArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.eqArg(() => {})); + assert_throws_js(TypeError, () => exports.eqArg(null)); + + exports.eqNullableArg(null); + exports.eqNullableArg(exports.makeStruct()); + exports.eqNullableArg(exports.makeArray()); + exports.eqNullableArg(42); + assert_throws_js(TypeError, () => exports.eqNullableArg(42n)); + assert_throws_js(TypeError, () => exports.eqNullableArg("foo")); + assert_throws_js(TypeError, () => exports.eqNullableArg({})); + assert_throws_js(TypeError, () => exports.eqNullableArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.eqNullableArg(() => {})); +}, "eqref casts"); + +test(() => { + exports.structArg(exports.makeStruct()); + assert_throws_js(TypeError, () => exports.structArg(exports.makeArray())); + assert_throws_js(TypeError, () => exports.structArg(42)); + assert_throws_js(TypeError, () => exports.structArg(42n)); + assert_throws_js(TypeError, () => exports.structArg("foo")); + assert_throws_js(TypeError, () => exports.structArg({})); + assert_throws_js(TypeError, () => exports.structArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.structArg(() => {})); + assert_throws_js(TypeError, () => exports.structArg(null)); + + exports.structNullableArg(null); + exports.structNullableArg(exports.makeStruct()); + assert_throws_js(TypeError, () => exports.structNullableArg(exports.makeArray())); + assert_throws_js(TypeError, () => exports.structNullableArg(42)); + assert_throws_js(TypeError, () => exports.structNullableArg(42n)); + assert_throws_js(TypeError, () => exports.structNullableArg("foo")); + assert_throws_js(TypeError, () => exports.structNullableArg({})); + assert_throws_js(TypeError, () => exports.structNullableArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.structNullableArg(() => {})); +}, "structref casts"); + +test(() => { + exports.arrayArg(exports.makeArray()); + assert_throws_js(TypeError, () => exports.arrayArg(exports.makeStruct())); + assert_throws_js(TypeError, () => exports.arrayArg(42)); + assert_throws_js(TypeError, () => exports.arrayArg(42n)); + assert_throws_js(TypeError, () => exports.arrayArg("foo")); + assert_throws_js(TypeError, () => exports.arrayArg({})); + assert_throws_js(TypeError, () => exports.arrayArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.arrayArg(() => {})); + assert_throws_js(TypeError, () => exports.arrayArg(null)); + + exports.arrayNullableArg(null); + exports.arrayNullableArg(exports.makeArray()); + assert_throws_js(TypeError, () => exports.arrayNullableArg(exports.makeStruct())); + assert_throws_js(TypeError, () => exports.arrayNullableArg(42)); + assert_throws_js(TypeError, () => exports.arrayNullableArg(42n)); + assert_throws_js(TypeError, () => exports.arrayNullableArg("foo")); + assert_throws_js(TypeError, () => exports.arrayNullableArg({})); + assert_throws_js(TypeError, () => exports.arrayNullableArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.arrayNullableArg(() => {})); +}, "arrayref casts"); + +test(() => { + exports.i31Arg(42); + assert_throws_js(TypeError, () => exports.i31Arg(exports.makeStruct())); + assert_throws_js(TypeError, () => exports.i31Arg(exports.makeArray())); + assert_throws_js(TypeError, () => exports.i31Arg(42n)); + assert_throws_js(TypeError, () => exports.i31Arg("foo")); + assert_throws_js(TypeError, () => exports.i31Arg({})); + assert_throws_js(TypeError, () => exports.i31Arg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.i31Arg(() => {})); + assert_throws_js(TypeError, () => exports.i31Arg(null)); + + exports.i31NullableArg(null); + exports.i31NullableArg(42); + assert_throws_js(TypeError, () => exports.i31NullableArg(exports.makeStruct())); + assert_throws_js(TypeError, () => exports.i31NullableArg(exports.makeArray())); + assert_throws_js(TypeError, () => exports.i31NullableArg(42n)); + assert_throws_js(TypeError, () => exports.i31NullableArg("foo")); + assert_throws_js(TypeError, () => exports.i31NullableArg({})); + assert_throws_js(TypeError, () => exports.i31NullableArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.i31NullableArg(() => {})); +}, "i31ref casts"); + +test(() => { + exports.funcArg(exports.testFunc); + assert_throws_js(TypeError, () => exports.funcArg(exports.makeStruct())); + assert_throws_js(TypeError, () => exports.funcArg(exports.makeArray())); + assert_throws_js(TypeError, () => exports.funcArg(42)); + assert_throws_js(TypeError, () => exports.funcArg(42n)); + assert_throws_js(TypeError, () => exports.funcArg("foo")); + assert_throws_js(TypeError, () => exports.funcArg({})); + assert_throws_js(TypeError, () => exports.funcArg(() => {})); + assert_throws_js(TypeError, () => exports.funcArg(null)); + + exports.funcNullableArg(null); + exports.funcNullableArg(exports.testFunc); + assert_throws_js(TypeError, () => exports.funcNullableArg(exports.makeStruct())); + assert_throws_js(TypeError, () => exports.funcNullableArg(exports.makeArray())); + assert_throws_js(TypeError, () => exports.funcNullableArg(42)); + assert_throws_js(TypeError, () => exports.funcNullableArg(42n)); + assert_throws_js(TypeError, () => exports.funcNullableArg("foo")); + assert_throws_js(TypeError, () => exports.funcNullableArg({})); + assert_throws_js(TypeError, () => exports.funcNullableArg(() => {})); +}, "funcref casts"); + +test(() => { + exports.externArg(exports.makeArray()); + exports.externArg(exports.makeStruct()); + exports.externArg(42); + exports.externArg(42n); + exports.externArg("foo"); + exports.externArg({}); + exports.externArg(exports.testFunc); + exports.externArg(() => {}); + assert_throws_js(TypeError, () => exports.externArg(null)); + + exports.externNullableArg(null); + exports.externNullableArg(exports.makeArray()); + exports.externNullableArg(exports.makeStruct()); + exports.externNullableArg(42); + exports.externNullableArg(42n); + exports.externNullableArg("foo"); + exports.externNullableArg({}); + exports.externNullableArg(exports.testFunc); + exports.externNullableArg(() => {}); +}, "externref casts"); + +test(() => { + for (const nullfunc of [exports.noneArg, exports.nofuncArg, exports.noexternArg]) { + assert_throws_js(TypeError, () => nullfunc(exports.makeStruct())); + assert_throws_js(TypeError, () => nullfunc(exports.makeArray())); + assert_throws_js(TypeError, () => nullfunc(42)); + assert_throws_js(TypeError, () => nullfunc(42n)); + assert_throws_js(TypeError, () => nullfunc("foo")); + assert_throws_js(TypeError, () => nullfunc({})); + assert_throws_js(TypeError, () => nullfunc(exports.testFunc)); + assert_throws_js(TypeError, () => nullfunc(() => {})); + assert_throws_js(TypeError, () => nullfunc(null)); + } + + for (const nullfunc of [exports.noneNullableArg, exports.nofuncNullableArg, exports.noexternNullableArg]) { + nullfunc(null); + assert_throws_js(TypeError, () => nullfunc(exports.makeStruct())); + assert_throws_js(TypeError, () => nullfunc(exports.makeArray())); + assert_throws_js(TypeError, () => nullfunc(42)); + assert_throws_js(TypeError, () => nullfunc(42n)); + assert_throws_js(TypeError, () => nullfunc("foo")); + assert_throws_js(TypeError, () => nullfunc({})); + assert_throws_js(TypeError, () => nullfunc(exports.testFunc)); + assert_throws_js(TypeError, () => nullfunc(() => {})); + } +}, "null casts"); + +test(() => { + exports.concreteStructArg(exports.makeStruct()); + assert_throws_js(TypeError, () => exports.concreteStructArg(exports.makeStruct2())); + assert_throws_js(TypeError, () => exports.concreteStructArg(exports.makeArray())); + assert_throws_js(TypeError, () => exports.concreteStructArg(42)); + assert_throws_js(TypeError, () => exports.concreteStructArg(42n)); + assert_throws_js(TypeError, () => exports.concreteStructArg("foo")); + assert_throws_js(TypeError, () => exports.concreteStructArg({})); + assert_throws_js(TypeError, () => exports.concreteStructArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.concreteStructArg(() => {})); + assert_throws_js(TypeError, () => exports.concreteStructArg(null)); + + exports.concreteStructNullableArg(null); + exports.concreteStructNullableArg(exports.makeStruct()); + assert_throws_js(TypeError, () => exports.concreteStructNullableArg(exports.makeStruct2())); + assert_throws_js(TypeError, () => exports.concreteStructNullableArg(exports.makeArray())); + assert_throws_js(TypeError, () => exports.concreteStructNullableArg(42)); + assert_throws_js(TypeError, () => exports.concreteStructNullableArg(42n)); + assert_throws_js(TypeError, () => exports.concreteStructNullableArg("foo")); + assert_throws_js(TypeError, () => exports.concreteStructNullableArg({})); + assert_throws_js(TypeError, () => exports.concreteStructNullableArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.concreteStructNullableArg(() => {})); +}, "concrete struct casts"); + +test(() => { + exports.concreteArrayArg(exports.makeArray()); + assert_throws_js(TypeError, () => exports.concreteArrayArg(exports.makeArray2())); + assert_throws_js(TypeError, () => exports.concreteArrayArg(exports.makeStruct())); + assert_throws_js(TypeError, () => exports.concreteArrayArg(42)); + assert_throws_js(TypeError, () => exports.concreteArrayArg(42n)); + assert_throws_js(TypeError, () => exports.concreteArrayArg("foo")); + assert_throws_js(TypeError, () => exports.concreteArrayArg({})); + assert_throws_js(TypeError, () => exports.concreteArrayArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.concreteArrayArg(() => {})); + assert_throws_js(TypeError, () => exports.concreteArrayArg(null)); + + exports.concreteArrayNullableArg(null); + exports.concreteArrayNullableArg(exports.makeArray()); + assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(exports.makeArray2())); + assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(exports.makeStruct())); + assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(42)); + assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(42n)); + assert_throws_js(TypeError, () => exports.concreteArrayNullableArg("foo")); + assert_throws_js(TypeError, () => exports.concreteArrayNullableArg({})); + assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(exports.testFunc)); + assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(() => {})); +}, "concrete array casts"); + +test(() => { + exports.concreteFuncArg(exports.testFunc); + assert_throws_js(TypeError, () => exports.concreteFuncArg(exports.testFunc2)); + assert_throws_js(TypeError, () => exports.concreteFuncArg(exports.makeArray())); + assert_throws_js(TypeError, () => exports.concreteFuncArg(exports.makeStruct())); + assert_throws_js(TypeError, () => exports.concreteFuncArg(42)); + assert_throws_js(TypeError, () => exports.concreteFuncArg(42n)); + assert_throws_js(TypeError, () => exports.concreteFuncArg("foo")); + assert_throws_js(TypeError, () => exports.concreteFuncArg({})); + assert_throws_js(TypeError, () => exports.concreteFuncArg(() => {})); + assert_throws_js(TypeError, () => exports.concreteFuncArg(null)); + + exports.concreteFuncNullableArg(null); + exports.concreteFuncNullableArg(exports.testFunc); + assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(exports.testFunc2)); + assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(exports.makeArray())); + assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(exports.makeStruct())); + assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(42)); + assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(42n)); + assert_throws_js(TypeError, () => exports.concreteFuncNullableArg("foo")); + assert_throws_js(TypeError, () => exports.concreteFuncNullableArg({})); + assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(() => {})); +}, "concrete func casts"); From 0118f2df818ce983aaf3ea009fc9925f94ead309 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Sat, 14 Oct 2023 07:13:03 +0200 Subject: [PATCH 45/50] [spec] Fix a few minor oversights --- document/core/appendix/index-instructions.py | 2 +- document/core/binary/instructions.rst | 1 + document/core/binary/modules.rst | 4 ++-- document/core/syntax/types.rst | 7 +++++++ document/core/valid/instructions.rst | 8 +++++--- interpreter/README.md | 2 -- test/core/binary.wast | 1 - 7 files changed, 16 insertions(+), 9 deletions(-) diff --git a/document/core/appendix/index-instructions.py b/document/core/appendix/index-instructions.py index 0c0ea6f0ac..c2d4fd13c8 100755 --- a/document/core/appendix/index-instructions.py +++ b/document/core/appendix/index-instructions.py @@ -279,7 +279,7 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(None, r'\hex{CF}'), Instruction(r'\REFNULL~\X{ht}', r'\hex{D0}', r'[] \to [(\REF~\NULL~\X{ht})]', r'valid-ref.null', r'exec-ref.null'), Instruction(r'\REFISNULL', r'\hex{D1}', r'[(\REF~\NULL~\X{ht})] \to [\I32]', r'valid-ref.is_null', r'exec-ref.is_null'), - Instruction(r'\REFFUNC~x', r'\hex{D2}', r'[] \to [\FUNCREF]', r'valid-ref.func', r'exec-ref.func'), + Instruction(r'\REFFUNC~x', r'\hex{D2}', r'[] \to [\REF~\X{ht}]', r'valid-ref.func', r'exec-ref.func'), Instruction(None, r'\hex{D3}'), Instruction(r'\REFASNONNULL', r'\hex{D4}', r'[(\REF~\NULL~\X{ht})] \to [(\REF~\X{ht})]', r'valid-ref.as_non_null', r'exec-ref.as_non_null'), Instruction(r'\BRONNULL~l', r'\hex{D5}', r'[t^\ast~(\REF~\NULL~\X{ht})] \to [t^\ast~(\REF~\X{ht})]', r'valid-br_on_null', r'exec-br_on_null'), diff --git a/document/core/binary/instructions.rst b/document/core/binary/instructions.rst index 80ac806cfc..9693fc1315 100644 --- a/document/core/binary/instructions.rst +++ b/document/core/binary/instructions.rst @@ -41,6 +41,7 @@ Control Instructions .. _binary-call_ref: .. _binary-call_indirect: .. _binary-return_call: +.. _binary-return_call_ref: .. _binary-return_call_indirect: .. math:: diff --git a/document/core/binary/modules.rst b/document/core/binary/modules.rst index 902f424a61..9449f6f7fd 100644 --- a/document/core/binary/modules.rst +++ b/document/core/binary/modules.rst @@ -338,7 +338,7 @@ It decodes into a vector of :ref:`element segments ` that represent \production{element segment} & \Belem &::=& 0{:}\Bu32~~e{:}\Bexpr~~y^\ast{:}\Bvec(\Bfuncidx) &\Rightarrow& \\&&&\quad - \{ \ETYPE~\FUNCREF, \EINIT~((\REFFUNC~y)~\END)^\ast, \EMODE~\EACTIVE~\{ \ETABLE~0, \EOFFSET~e \} \} \\ &&|& + \{ \ETYPE~(\REF~\NULL~\FUNC), \EINIT~((\REFFUNC~y)~\END)^\ast, \EMODE~\EACTIVE~\{ \ETABLE~0, \EOFFSET~e \} \} \\ &&|& 1{:}\Bu32~~\X{et}:\Belemkind~~y^\ast{:}\Bvec(\Bfuncidx) &\Rightarrow& \\&&&\quad \{ \ETYPE~\X{et}, \EINIT~((\REFFUNC~y)~\END)^\ast, \EMODE~\EPASSIVE \} \\ &&|& @@ -350,7 +350,7 @@ It decodes into a vector of :ref:`element segments ` that represent \{ \ETYPE~\X{et}, \EINIT~((\REFFUNC~y)~\END)^\ast, \EMODE~\EDECLARATIVE \} \\ &&|& 4{:}\Bu32~~e{:}\Bexpr~~\X{el}^\ast{:}\Bvec(\Bexpr) &\Rightarrow& \\&&&\quad - \{ \ETYPE~\FUNCREF, \EINIT~\X{el}^\ast, \EMODE~\EACTIVE~\{ \ETABLE~0, \EOFFSET~e \} \} \\ &&|& + \{ \ETYPE~(\REF~\NULL~\FUNC), \EINIT~\X{el}^\ast, \EMODE~\EACTIVE~\{ \ETABLE~0, \EOFFSET~e \} \} \\ &&|& 5{:}\Bu32~~\X{et}:\Breftype~~\X{el}^\ast{:}\Bvec(\Bexpr) &\Rightarrow& \\&&&\quad \{ \ETYPE~et, \EINIT~\X{el}^\ast, \EMODE~\EPASSIVE \} \\ &&|& diff --git a/document/core/syntax/types.rst b/document/core/syntax/types.rst index 5d1b355c7a..1eb7b83696 100644 --- a/document/core/syntax/types.rst +++ b/document/core/syntax/types.rst @@ -142,6 +142,13 @@ Other references are *non-null*. Reference types are *opaque*, meaning that neither their size nor their bit pattern can be observed. Values of reference type can be stored in :ref:`tables `. +Conventions +........... + +* The reference type |FUNCREF| is an abbreviation for :math:`\REF~\NULL~\FUNC`. + +* The reference type |EXTERNREF| is an abbreviation for :math:`\REF~\NULL~\EXTERN`. + .. index:: ! value type, number type, vector type, reference type, ! bottom type pair: abstract syntax; value type diff --git a/document/core/valid/instructions.rst b/document/core/valid/instructions.rst index 05581d4eb5..e282542269 100644 --- a/document/core/valid/instructions.rst +++ b/document/core/valid/instructions.rst @@ -1649,9 +1649,9 @@ Control Instructions * The table :math:`C.\CTABLES[x]` must be defined in the context. -* Let :math:`\limits~\reftype` be the :ref:`table type ` :math:`C.\CTABLES[x]`. +* Let :math:`\limits~t` be the :ref:`table type ` :math:`C.\CTABLES[x]`. -* The :ref:`reference type ` :math:`\reftype` must be |FUNCREF|. +* The :ref:`reference type ` :math:`t` must :ref:`match ` type :math:`\REF~\NULL~\FUNC`. * The type :math:`C.\CTYPES[y]` must be defined in the context. @@ -1663,7 +1663,9 @@ Control Instructions .. math:: \frac{ - C.\CTABLES[x] = \limits~\FUNCREF + C.\CTABLES[x] = \limits~t + \qquad + C \vdashvaltypematch t \matchesreftype \REF~\NULL~\FUNC \qquad C.\CTYPES[y] = [t_1^\ast] \toF C.\CRETURN }{ diff --git a/interpreter/README.md b/interpreter/README.md index 6195e97732..cd20819511 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -221,7 +221,6 @@ expr: ( loop ? * ) ( if ? ( then * ) ( else * )? ) ( if ? + ( then * ) ( else * )? ) ;; = + (if ? (then *) (else *)?) - ( let ? * * ) instr: @@ -230,7 +229,6 @@ instr: loop ? * end ? ;; = (loop ? *) if ? * end ? ;; = (if ? (then *)) if ? * else ? * end ? ;; = (if ? (then *) (else *)) - let ? * * end ? ;; = (let ? * *) op: unreachable diff --git a/test/core/binary.wast b/test/core/binary.wast index 65407391a0..1661a1c689 100644 --- a/test/core/binary.wast +++ b/test/core/binary.wast @@ -1132,4 +1132,3 @@ ) "unexpected content after last section" ) - From 2048b3de14c382c5d7590745bb77131cd3d644bf Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Sat, 14 Oct 2023 07:27:10 +0200 Subject: [PATCH 46/50] [spec] Add missing shorthands --- document/core/appendix/index-instructions.py | 4 ++-- document/core/syntax/types.rst | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/document/core/appendix/index-instructions.py b/document/core/appendix/index-instructions.py index ee9ff9094f..e9492d1aae 100755 --- a/document/core/appendix/index-instructions.py +++ b/document/core/appendix/index-instructions.py @@ -346,8 +346,8 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\REFCAST~(\REF~\NULL~t)', r'\hex{FB}~\hex{17}', r"[(\REF~\NULL~t')] \to [(\REF~\NULL~t)]", r'valid-ref.cast', r'exec-ref.cast'), Instruction(r'\BRONCAST~t_1~t_2', r'\hex{FB}~\hex{18}', r'[t_1] \to [t_1\reftypediff t_2]', r'valid-br_on_cast', r'exec-br_on_cast'), Instruction(r'\BRONCASTFAIL~t_1~t_2', r'\hex{FB}~\hex{19}', r'[t_1] \to [t_2]', r'valid-br_on_cast_fail', r'exec-br_on_cast_fail'), - Instruction(r'\ANYCONVERTEXTERN', r'\hex{FB}~\hex{1A}', r'[\EXTERNREF] \to [\ANYREF]', r'valid-any.convert_extern', r'exec-any.convert_extern'), - Instruction(r'\EXTERNCONVERTANY', r'\hex{FB}~\hex{1B}', r'[\ANYREF] \to [\EXTERNREF]', r'valid-extern.convert_any', r'exec-extern.convert_any'), + Instruction(r'\ANYCONVERTEXTERN', r'\hex{FB}~\hex{1A}', r'[(\REF~\NULL~\EXTERN)] \to [(\REF~\NULL~\ANY)]', r'valid-any.convert_extern', r'exec-any.convert_extern'), + Instruction(r'\EXTERNCONVERTANY', r'\hex{FB}~\hex{1B}', r'[(\REF~\NULL~\ANY)] \to [(\REF~\NULL~\EXTERN)]', r'valid-extern.convert_any', r'exec-extern.convert_any'), Instruction(r'\REFI31', r'\hex{FB}~\hex{1C}', r'[\I32] \to [\I31REF]', r'valid-ref.i31', r'exec-ref.i31'), Instruction(r'\I31GETS', r'\hex{FB}~\hex{1D}', r'[\I31REF] \to [\I32]', r'valid-i31.get_sx', r'exec-i31.get_sx'), Instruction(r'\I31GETU', r'\hex{FB}~\hex{1E}', r'[\I31REF] \to [\I32]', r'valid-i31.get_sx', r'exec-i31.get_sx'), diff --git a/document/core/syntax/types.rst b/document/core/syntax/types.rst index a817939e19..c2f6a4323b 100644 --- a/document/core/syntax/types.rst +++ b/document/core/syntax/types.rst @@ -171,10 +171,26 @@ Values of reference type can be stored in :ref:`tables `. Conventions ........... +* The reference type |ANYREF| is an abbreviation for :math:`\REF~\NULL~\ANY`. + +* The reference type |EQREF| is an abbreviation for :math:`\REF~\NULL~\EQT`. + +* The reference type |I31REF| is an abbreviation for :math:`\REF~\NULL~\I31`. + +* The reference type |STRUCTREF| is an abbreviation for :math:`\REF~\NULL~\STRUCT`. + +* The reference type |ARRAYREF| is an abbreviation for :math:`\REF~\NULL~\ARRAY`. + * The reference type |FUNCREF| is an abbreviation for :math:`\REF~\NULL~\FUNC`. * The reference type |EXTERNREF| is an abbreviation for :math:`\REF~\NULL~\EXTERN`. +* The reference type |NULLREF| is an abbreviation for :math:`\REF~\NULL~\NONE`. + +* The reference type |NULLFUNCREF| is an abbreviation for :math:`\REF~\NULL~\NOFUNC`. + +* The reference type |NULLEXTERNREF| is an abbreviation for :math:`\REF~\NULL~\NOEXTERN`. + .. index:: ! value type, number type, vector type, reference type pair: abstract syntax; value type From 84158a765ce284e978814d811424811485c043e2 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Sat, 14 Oct 2023 11:36:38 +0200 Subject: [PATCH 47/50] [spec] Fix typo --- document/core/appendix/properties.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/document/core/appendix/properties.rst b/document/core/appendix/properties.rst index 3804dd4aae..c42a85a54b 100644 --- a/document/core/appendix/properties.rst +++ b/document/core/appendix/properties.rst @@ -665,9 +665,9 @@ a store state :math:`S'` extends state :math:`S`, written :math:`S \extendsto S' * For each :ref:`global instance ` :math:`\globalinst_i` in the original :math:`S.\SGLOBALS`, the new global instance must be an :ref:`extension ` of the old. -* For each :ref:`element instance ` :math:`\eleminst_i` in the original :math:`S.\SELEMS`, the new global instance must be an :ref:`extension ` of the old. +* For each :ref:`element instance ` :math:`\eleminst_i` in the original :math:`S.\SELEMS`, the new element instance must be an :ref:`extension ` of the old. -* For each :ref:`data instance ` :math:`\datainst_i` in the original :math:`S.\SDATAS`, the new global instance must be an :ref:`extension ` of the old. +* For each :ref:`data instance ` :math:`\datainst_i` in the original :math:`S.\SDATAS`, the new data instance must be an :ref:`extension ` of the old. .. math:: \frac{ From 86cda091122548db29c48ad89dec408a35f4b52e Mon Sep 17 00:00:00 2001 From: YiYing He Date: Mon, 16 Oct 2023 15:12:16 +0800 Subject: [PATCH 48/50] [spec] Fix the instruction types of `array.new_default` and `array.len` in Appendix page. Signed-off-by: YiYing He --- document/core/appendix/index-instructions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/document/core/appendix/index-instructions.py b/document/core/appendix/index-instructions.py index e9492d1aae..8319db4a1d 100755 --- a/document/core/appendix/index-instructions.py +++ b/document/core/appendix/index-instructions.py @@ -327,7 +327,7 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\STRUCTGETU~x~y', r'\hex{FB}~\hex{04}', r'[(\REF~\NULL~x)] \to [\I32]', r'valid-struct.get', r'exec-struct.get'), Instruction(r'\STRUCTSET~x~y', r'\hex{FB}~\hex{05}', r'[(\REF~\NULL~x)~t] \to []', r'valid-struct.set', r'exec-struct.set'), Instruction(r'\ARRAYNEW~x', r'\hex{FB}~\hex{06}', r'[t] \to [(\REF~x)]', r'valid-array.new', r'exec-array.new'), - Instruction(r'\ARRAYNEWDEFAULT~x', r'\hex{FB}~\hex{07}', r'[] \to [(\REF~x)]', r'valid-array.new', r'exec-array.new'), + Instruction(r'\ARRAYNEWDEFAULT~x', r'\hex{FB}~\hex{07}', r'[\I32] \to [(\REF~x)]', r'valid-array.new', r'exec-array.new'), Instruction(r'\ARRAYNEWFIXED~x~n', r'\hex{FB}~\hex{08}', r'[t^n] \to [(\REF~x)]', r'valid-array.new_fixed', r'exec-array.new_fixed'), Instruction(r'\ARRAYNEWDATA~x~y', r'\hex{FB}~\hex{09}', r'[\I32~\I32] \to [(\REF~x)]', r'valid-array.new_data', r'exec-array.new_data'), Instruction(r'\ARRAYNEWELEM~x~y', r'\hex{FB}~\hex{0A}', r'[\I32~\I32] \to [(\REF~x)]', r'valid-array.new_elem', r'exec-array.new_elem'), @@ -335,7 +335,7 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\ARRAYGETS~x', r'\hex{FB}~\hex{0C}', r'[(\REF~\NULL~x)~\I32] \to [\I32]', r'valid-array.get', r'exec-array.get'), Instruction(r'\ARRAYGETU~x', r'\hex{FB}~\hex{0D}', r'[(\REF~\NULL~x)~\I32] \to [\I32]', r'valid-array.get', r'exec-array.get'), Instruction(r'\ARRAYSET~x', r'\hex{FB}~\hex{0E}', r'[(\REF~\NULL~x)~\I32~t] \to []', r'valid-array.set', r'exec-array.set'), - Instruction(r'\ARRAYLEN', r'\hex{FB}~\hex{0F}', r'[\ARRAYREF] \to []', r'valid-array.len', r'exec-array.len'), + Instruction(r'\ARRAYLEN', r'\hex{FB}~\hex{0F}', r'[\ARRAYREF] \to [\I32]', r'valid-array.len', r'exec-array.len'), Instruction(r'\ARRAYFILL~x', r'\hex{FB}~\hex{10}', r'[(\REF~\NULL~x)~\I32~t~\I32] \to []', r'valid-array.fill', r'exec-array.fill'), Instruction(r'\ARRAYCOPY~x~y', r'\hex{FB}~\hex{11}', r'[(\REF~\NULL~x)~\I32~(\REF~\NULL~y)~\I32~\I32] \to []', r'valid-array.copy', r'exec-array.copy'), Instruction(r'\ARRAYINITDATA~x~y', r'\hex{FB}~\hex{12}', r'[(\REF~\NULL~x)~\I32~\I32~\I32] \to []', r'valid-array.init_data', r'exec-array.init_data'), From ced37c776fd6849bb8993b839c972ccd012bbb7b Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Mon, 16 Oct 2023 09:25:17 +0200 Subject: [PATCH 49/50] Update index-instructions.py --- document/core/appendix/index-instructions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document/core/appendix/index-instructions.py b/document/core/appendix/index-instructions.py index 8319db4a1d..a433bf5b4a 100755 --- a/document/core/appendix/index-instructions.py +++ b/document/core/appendix/index-instructions.py @@ -335,7 +335,7 @@ def Instruction(name, opcode, type=None, validation=None, execution=None, operat Instruction(r'\ARRAYGETS~x', r'\hex{FB}~\hex{0C}', r'[(\REF~\NULL~x)~\I32] \to [\I32]', r'valid-array.get', r'exec-array.get'), Instruction(r'\ARRAYGETU~x', r'\hex{FB}~\hex{0D}', r'[(\REF~\NULL~x)~\I32] \to [\I32]', r'valid-array.get', r'exec-array.get'), Instruction(r'\ARRAYSET~x', r'\hex{FB}~\hex{0E}', r'[(\REF~\NULL~x)~\I32~t] \to []', r'valid-array.set', r'exec-array.set'), - Instruction(r'\ARRAYLEN', r'\hex{FB}~\hex{0F}', r'[\ARRAYREF] \to [\I32]', r'valid-array.len', r'exec-array.len'), + Instruction(r'\ARRAYLEN', r'\hex{FB}~\hex{0F}', r'[(\REF~\NULL~\ARRAY)] \to [\I32]', r'valid-array.len', r'exec-array.len'), Instruction(r'\ARRAYFILL~x', r'\hex{FB}~\hex{10}', r'[(\REF~\NULL~x)~\I32~t~\I32] \to []', r'valid-array.fill', r'exec-array.fill'), Instruction(r'\ARRAYCOPY~x~y', r'\hex{FB}~\hex{11}', r'[(\REF~\NULL~x)~\I32~(\REF~\NULL~y)~\I32~\I32] \to []', r'valid-array.copy', r'exec-array.copy'), Instruction(r'\ARRAYINITDATA~x~y', r'\hex{FB}~\hex{12}', r'[(\REF~\NULL~x)~\I32~\I32~\I32] \to []', r'valid-array.init_data', r'exec-array.init_data'), From be1f56354b251ae15b6ac4220c5ea0f0a5f894f9 Mon Sep 17 00:00:00 2001 From: RIRU <92210252+RI5255@users.noreply.github.com> Date: Mon, 16 Oct 2023 23:59:12 +0900 Subject: [PATCH 50/50] [spec] Pop dummy frame after Invocation (#1691) --- document/core/exec/modules.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/document/core/exec/modules.rst b/document/core/exec/modules.rst index ad485c3612..cdb20d03c4 100644 --- a/document/core/exec/modules.rst +++ b/document/core/exec/modules.rst @@ -798,6 +798,10 @@ Once the function has returned, the following steps are executed: 2. Pop :math:`\val_{\F{res}}^m` from the stack. +3. Assert: due to :ref:`validation `, the frame :math:`F` is now on the top of the stack. + +4. Pop the frame :math:`F` from the stack. + The values :math:`\val_{\F{res}}^m` are returned as the results of the invocation. .. math::