diff --git a/document/js-api/index.bs b/document/js-api/index.bs
index 5fe37d01..b0184d0e 100644
--- a/document/js-api/index.bs
+++ b/document/js-api/index.bs
@@ -795,6 +795,7 @@ The type() method steps are:
enum TableKind {
"externref",
"anyfunc",
+ "funcref",
// Note: More values may be added in future iterations,
// e.g., typed function references, typed GC references
};
@@ -848,7 +849,7 @@ Each {{Table}} object has a \[[Table]] internal slot, which is a [=table address
The algorithm ToTableKind(|t|) performs the following steps:
-1. If |t| equals [=funcref=], return "{{TableKind/anyfunc}}".
+1. If |t| equals [=funcref=], return "{{TableKind/funcref}}".
1. If |t| equals [=externref=], return "{{TableKind/externref}}".
1. Assert: This step is not reached.
@@ -950,6 +951,7 @@ enum ValueType {
"f32",
"f64",
"externref",
+ "funcref",
"anyfunc",
};
@@ -1002,6 +1004,7 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
1. If |s| equals "i64", return [=i64=].
1. If |s| equals "f32", return [=f32=].
1. If |s| equals "f64", return [=f64=].
+ 1. If |s| equals "funcref", return [=funcref=].
1. If |s| equals "anyfunc", return [=funcref=].
1. If |s| equals "externref", return [=externref=].
1. Assert: This step is not reached.
@@ -1015,7 +1018,7 @@ The algorithm FromValueType(|s|) performs the following s
1. If |s| equals [=i64=], return "{{ValueType/i64}}".
1. If |s| equals [=f32=], return "{{ValueType/f32}}".
1. If |s| equals [=f64=], return "{{ValueType/f64}}".
-1. If |s| equals [=funcref=], return "{{ValueType/anyfunc}}".
+1. If |s| equals [=funcref=], return "{{ValueType/funcref}}".
1. If |s| equals [=externref=], return "{{ValueType/externref}}".
1. Assert: This step is not reached.
diff --git a/proposals/js-types/Overview.md b/proposals/js-types/Overview.md
index 28a040be..1c863b2c 100644
--- a/proposals/js-types/Overview.md
+++ b/proposals/js-types/Overview.md
@@ -32,7 +32,7 @@ All Wasm types can be defined by a simple grammar. This grammar could be mapped
```TypeScript
type ValueType = "i32" | "i64" | "f32" | "f64"
-type ElemType = "anyfunc"
+type ElemType = "funcref"
type GlobalType = {value: ValueType, mutable: boolean}
type MemoryType = {limits: Limits}
type TableType = {limits: Limits, element: ElemType}
@@ -200,7 +200,7 @@ function print(...args) {
for (let x of args) console.log(x + "\n")
}
-let table = new Table({element: "anyfunc", minimum: 10});
+let table = new Table({element: "funcref", minimum: 10});
let print_i32 = new WebAssembly.Function({parameters: ["i32"], results: []}, print);
table.set(0, print_i32);
diff --git a/test/js-api/global/constructor.any.js b/test/js-api/global/constructor.any.js
index f536f5d7..4e123164 100644
--- a/test/js-api/global/constructor.any.js
+++ b/test/js-api/global/constructor.any.js
@@ -164,3 +164,15 @@ test(() => {
const global = new WebAssembly.Global(argument, 0, {});
assert_Global(global, 0);
}, "Stray argument");
+
+test(() => {
+ const argument = { "value": "anyfunc" };
+ const global = new WebAssembly.Global(argument);
+ assert_Global(global, null);
+}, "funcref with default");
+
+test(() => {
+ const argument = { "value": "funcref" };
+ const global = new WebAssembly.Global(argument);
+ assert_Global(global, null);
+}, "funcref with default");
diff --git a/test/js-api/global/type.any.js b/test/js-api/global/type.any.js
new file mode 100644
index 00000000..68a51fba
--- /dev/null
+++ b/test/js-api/global/type.any.js
@@ -0,0 +1,83 @@
+// META: global=window,dedicatedworker,jsshell
+// META: script=/wasm/jsapi/assertions.js
+
+function assert_type(argument) {
+ const myglobal = new WebAssembly.Global(argument);
+ const globaltype = myglobal.type();
+
+ assert_equals(globaltype.value, argument.value);
+ assert_equals(globaltype.mutable, argument.mutable);
+}
+
+test(() => {
+ assert_type({ "value": "i32", "mutable": true});
+}, "i32, mutable");
+
+test(() => {
+ assert_type({ "value": "i32", "mutable": false});
+}, "i32, immutable");
+
+test(() => {
+ assert_type({ "value": "i64", "mutable": true});
+}, "i64, mutable");
+
+test(() => {
+ assert_type({ "value": "i64", "mutable": false});
+}, "i64, immutable");
+
+test(() => {
+ assert_type({ "value": "f32", "mutable": true});
+}, "f32, mutable");
+
+test(() => {
+ assert_type({ "value": "f32", "mutable": false});
+}, "f32, immutable");
+
+test(() => {
+ assert_type({ "value": "f64", "mutable": true});
+}, "f64, mutable");
+
+test(() => {
+ assert_type({ "value": "f64", "mutable": false});
+}, "f64, immutable");
+
+test(() => {
+ assert_type({"value": "externref", "mutable": true})
+}, "externref, mutable")
+
+test(() => {
+ assert_type({"value": "externref", "mutable": false})
+}, "externref, immutable")
+
+test(() => {
+ const argument = {"value": "anyfunc", "mutable": true};
+ const myglobal = new WebAssembly.Global(argument);
+ const globaltype = myglobal.type();
+
+ assert_equals(globaltype.value, "funcref");
+ assert_equals(globaltype.mutable, argument.mutable);
+}, "anyfunc, mutable")
+
+test(() => {
+ const argument = {"value": "anyfunc", "mutable": false};
+ const myglobal = new WebAssembly.Global(argument);
+ const globaltype = myglobal.type();
+
+ assert_equals(globaltype.value, "funcref");
+ assert_equals(globaltype.mutable, argument.mutable);
+}, "anyfunc, immutable")
+
+test(() => {
+ assert_type({"value": "funcref", "mutable": true})
+}, "funcref, mutable")
+
+test(() => {
+ assert_type({"value": "funcref", "mutable": false})
+}, "funcref, immutable")
+
+test(() => {
+ const myglobal = new WebAssembly.Global({"value": "i32", "mutable": true});
+ const propertyNames = Object.getOwnPropertyNames(myglobal.type());
+ assert_equals(propertyNames[0], "mutable");
+ assert_equals(propertyNames[1], "value");
+}, "key ordering");
diff --git a/test/js-api/module/exports.any.js b/test/js-api/module/exports.any.js
index 60b2c04f..0a179319 100644
--- a/test/js-api/module/exports.any.js
+++ b/test/js-api/module/exports.any.js
@@ -21,6 +21,35 @@ function assert_ModuleExportDescriptor(export_, expected) {
assert_true(kind.enumerable, "kind: enumerable");
assert_true(kind.configurable, "kind: configurable");
assert_equals(kind.value, expected.kind);
+
+ if (expected.type) {
+ const type = Object.getOwnPropertyDescriptor(export_, 'type');
+ assert_true(type.writable, 'type: writable');
+ assert_true(type.enumerable, 'type: enumerable');
+ assert_true(type.configurable, 'type: configurable');
+
+ if (expected.type.parameters !== undefined) {
+ assert_array_equals(type.value.parameters, expected.type.parameters);
+ }
+ if (expected.type.results !== undefined) {
+ assert_array_equals(type.value.results, expected.type.results);
+ }
+ if (expected.type.value !== undefined) {
+ assert_equals(type.value.value, expected.type.value);
+ }
+ if (expected.type.mutable !== undefined) {
+ assert_equals(type.value.mutable, expected.type.mutable);
+ }
+ if (expected.type.mimimum !== undefined) {
+ assert_equals(type.value.mimimum, expected.type.mimimum);
+ }
+ if (expected.type.maximum !== undefined) {
+ assert_equals(type.value.maximum, expected.type.maximum);
+ }
+ if (expected.type.element !== undefined) {
+ assert_equals(type.value.element, expected.type.element);
+ }
+ }
}
function assert_exports(exports, expected) {
@@ -75,6 +104,12 @@ test(() => {
}
}, "Branding");
+test(() => {
+ const module = new WebAssembly.Module(emptyModuleBinary);
+ const exports = WebAssembly.Module.exports(module);
+ assert_true(Array.isArray(exports));
+}, "Return type");
+
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const exports = WebAssembly.Module.exports(module);
@@ -83,7 +118,8 @@ test(() => {
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
- assert_not_equals(WebAssembly.Module.exports(module), WebAssembly.Module.exports(module));
+ assert_not_equals(
+ WebAssembly.Module.exports(module), WebAssembly.Module.exports(module));
}, "Empty module: array caching");
test(() => {
@@ -114,18 +150,112 @@ test(() => {
const module = new WebAssembly.Module(buffer);
const exports = WebAssembly.Module.exports(module);
const expected = [
- { "kind": "function", "name": "fn" },
- { "kind": "function", "name": "fn2" },
- { "kind": "table", "name": "table" },
- { "kind": "global", "name": "global" },
- { "kind": "global", "name": "global2" },
- { "kind": "memory", "name": "memory" },
+ {
+ 'kind': 'function',
+ 'name': 'fn',
+ 'type': {'parameters': [], 'results': []}
+ },
+ {
+ 'kind': 'function',
+ 'name': 'fn2',
+ 'type': {'parameters': [], 'results': []}
+ },
+ {'kind': 'table', 'name': 'table'},
+ {'kind': 'global', 'name': 'global'},
+ {'kind': 'global', 'name': 'global2'},
+ {'kind': 'memory', 'name': 'memory'},
];
assert_exports(exports, expected);
}, "exports");
+test(() => {
+ const builder = new WasmModuleBuilder();
+
+ builder
+ .addFunction("", kSig_v_v)
+ .addBody([])
+ .exportFunc();
+
+ const buffer = builder.toBuffer()
+ const module = new WebAssembly.Module(buffer);
+ const exports = WebAssembly.Module.exports(module);
+ const expected = [
+ {'kind': 'function', 'name': '', 'type': {'parameters': [], 'results': []}},
+ ];
+ assert_exports(exports, expected);
+}, "exports with empty name: function");
+
+test(() => {
+ const builder = new WasmModuleBuilder();
+
+ builder.setTableBounds(1);
+ builder.addExportOfKind("", kExternalTable, 0);
+
+ const buffer = builder.toBuffer()
+ const module = new WebAssembly.Module(buffer);
+ const exports = WebAssembly.Module.exports(module);
+ const expected = [
+ { "kind": "table", "name": "" },
+ ];
+ assert_exports(exports, expected);
+}, "exports with empty name: table");
+
+test(() => {
+ const builder = new WasmModuleBuilder();
+
+ builder.addGlobal(kWasmI32, true)
+ .exportAs("")
+ .init = 7;
+
+ const buffer = builder.toBuffer()
+ const module = new WebAssembly.Module(buffer);
+ const exports = WebAssembly.Module.exports(module);
+ const expected = [
+ { "kind": "global", "name": "" },
+ ];
+ assert_exports(exports, expected);
+}, "exports with empty name: global");
+
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const exports = WebAssembly.Module.exports(module, {});
assert_exports(exports, []);
}, "Stray argument");
+
+test(() => {
+ const builder = new WasmModuleBuilder();
+
+ builder
+ .addFunction("fn", kSig_a_a)
+ .addBody([kExprLocalGet, 0])
+ .exportFunc();
+
+ builder.addTable(kWasmAnyFunc, 10, 100);
+ builder.addExportOfKind("table", kExternalTable, 0);
+
+ builder.addGlobal(kWasmAnyFunc, true)
+ .exportAs("global").function_index = 0;
+
+ const buffer = builder.toBuffer();
+ const module = new WebAssembly.Module(buffer);
+ const exports = WebAssembly.Module.exports(module);
+ const expected = [
+ {
+ 'kind': 'function',
+ 'name': 'fn',
+ 'type': {'parameters': ['funcref'], 'results': ['funcref']}
+ },
+ {
+ 'kind': 'table',
+ 'name': 'table',
+ 'type': {'minimum': 10, 'maximum': 100, 'element': 'funcref'}
+ },
+ {
+ 'kind': 'global',
+ 'name': 'global',
+ 'type': {'value': 'funcref', 'mutable': true}
+ },
+ ];
+ assert_exports(exports, expected);
+}, "exports with type funcref");
+
diff --git a/test/js-api/module/imports.any.js b/test/js-api/module/imports.any.js
index d6754c9e..0f0b2017 100644
--- a/test/js-api/module/imports.any.js
+++ b/test/js-api/module/imports.any.js
@@ -22,6 +22,34 @@ function assert_ModuleImportDescriptor(import_, expected) {
assert_true(kind.enumerable, "kind: enumerable");
assert_true(kind.configurable, "kind: configurable");
assert_equals(kind.value, expected.kind);
+
+ if (expected.type) {
+ const type = Object.getOwnPropertyDescriptor(import_, 'type');
+ assert_true(type.writable, 'type: writable');
+ assert_true(type.enumerable, 'type: enumerable');
+ assert_true(type.configurable, 'type: configurable');
+ if (expected.type.parameters) {
+ assert_array_equals(type.value.parameters, expected.type.parameters);
+ }
+ if (expected.type.results !== undefined) {
+ assert_array_equals(type.value.results, expected.type.results);
+ }
+ if (expected.type.value !== undefined) {
+ assert_equals(type.value.value, expected.type.value);
+ }
+ if (expected.type.mutable !== undefined) {
+ assert_equals(type.value.mutable, expected.type.mutable);
+ }
+ if (expected.type.mimimum !== undefined) {
+ assert_equals(type.value.mimimum, expected.type.mimimum);
+ }
+ if (expected.type.maximum !== undefined) {
+ assert_equals(type.value.maximum, expected.type.maximum);
+ }
+ if (expected.type.element !== undefined) {
+ assert_equals(type.value.element, expected.type.element);
+ }
+ }
}
function assert_imports(imports, expected) {
@@ -110,16 +138,128 @@ test(() => {
const module = new WebAssembly.Module(buffer);
const imports = WebAssembly.Module.imports(module);
const expected = [
- { "module": "module", "kind": "function", "name": "fn" },
- { "module": "module", "kind": "global", "name": "global" },
- { "module": "module", "kind": "memory", "name": "memory" },
- { "module": "module", "kind": "table", "name": "table" },
+ {
+ 'module': 'module',
+ 'kind': 'function',
+ 'name': 'fn',
+ 'type': {'parameters': [], 'results': []}
+ },
+ {'module': 'module', 'kind': 'global', 'name': 'global', 'value': 'i32'},
+ {'module': 'module', 'kind': 'memory', 'name': 'memory'},
+ {'module': 'module', 'kind': 'table', 'name': 'table'},
];
assert_imports(imports, expected);
}, "imports");
+test(() => {
+ const builder = new WasmModuleBuilder();
+
+ builder.addImport("", "fn", kSig_v_v);
+ builder.addImportedGlobal("", "global", kWasmI32);
+ builder.addImportedMemory("", "memory", 0, 128);
+ builder.addImportedTable("", "table", 0, 128);
+
+ const buffer = builder.toBuffer()
+ const module = new WebAssembly.Module(buffer);
+ const imports = WebAssembly.Module.imports(module);
+ const expected = [
+ {
+ 'module': '',
+ 'kind': 'function',
+ 'name': 'fn',
+ 'type': {'parameters': [], 'results': []}
+ },
+ {'module': '', 'kind': 'global', 'name': 'global', 'value': 'i32'},
+ {'module': '', 'kind': 'memory', 'name': 'memory'},
+ {'module': '', 'kind': 'table', 'name': 'table'},
+ ];
+ assert_imports(imports, expected);
+}, "imports with empty module name");
+
+test(() => {
+ const builder = new WasmModuleBuilder();
+
+ builder.addImport("a", "", kSig_v_v);
+ builder.addImportedGlobal("b", "", kWasmI32);
+ builder.addImportedMemory("c", "", 0, 128);
+ builder.addImportedTable("d", "", 0, 128);
+
+ const buffer = builder.toBuffer()
+ const module = new WebAssembly.Module(buffer);
+ const imports = WebAssembly.Module.imports(module);
+ const expected = [
+ {
+ 'module': 'a',
+ 'kind': 'function',
+ 'name': '',
+ 'type': {'parameters': [], 'results': []}
+ },
+ {'module': 'b', 'kind': 'global', 'name': '', 'value': 'i32'},
+ {'module': 'c', 'kind': 'memory', 'name': ''},
+ {'module': 'd', 'kind': 'table', 'name': ''},
+ ];
+ assert_imports(imports, expected);
+}, "imports with empty names");
+
+test(() => {
+ const builder = new WasmModuleBuilder();
+
+ builder.addImport("", "", kSig_v_v);
+ builder.addImportedGlobal("", "", kWasmI32);
+ builder.addImportedMemory("", "", 0, 128);
+ builder.addImportedTable("", "", 0, 128);
+
+ const buffer = builder.toBuffer()
+ const module = new WebAssembly.Module(buffer);
+ const imports = WebAssembly.Module.imports(module);
+ const expected = [
+ {
+ 'module': '',
+ 'kind': 'function',
+ 'name': '',
+ 'type': {'parameters': [], 'results': []}
+ },
+ {'module': '', 'kind': 'global', 'name': '', 'value': 'i32'},
+ {'module': '', 'kind': 'memory', 'name': ''},
+ {'module': '', 'kind': 'table', 'name': ''},
+ ];
+ assert_imports(imports, expected);
+}, "imports with empty module names and names");
+
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const imports = WebAssembly.Module.imports(module, {});
assert_imports(imports, []);
}, "Stray argument");
+
+test(() => {
+ const builder = new WasmModuleBuilder();
+ builder.addImport("module", "fn", kSig_a_a);
+ builder.addImportedGlobal("module", "g", kWasmAnyFunc);
+ builder.addImportedTable("module", "t", 0, 128);
+
+ const buffer = builder.toBuffer()
+ const module = new WebAssembly.Module(buffer);
+ const imports = WebAssembly.Module.imports(module);
+ const expected = [
+ {
+ 'kind': 'function',
+ 'module': 'module',
+ 'name': 'fn',
+ 'type': {'parameters': ['funcref'], 'results': ['funcref']}
+ },
+ {
+ 'module': 'module',
+ 'kind': 'global',
+ 'name': 'g',
+ 'type': {'value': 'funcref', 'mutable': false}
+ },
+ {
+ 'module': 'module',
+ 'kind': 'table',
+ 'name': 't',
+ 'type': {'minimum': 0, 'maximum': 128, 'element': 'funcref'}
+ },
+ ];
+ assert_imports(imports, expected);
+}, "imports with type funcref");
diff --git a/test/js-api/table/constructor.any.js b/test/js-api/table/constructor.any.js
index e9e77a13..cfc73b2b 100644
--- a/test/js-api/table/constructor.any.js
+++ b/test/js-api/table/constructor.any.js
@@ -96,6 +96,12 @@ test(() => {
assert_Table(table, { "length": 5 });
}, "Basic (non-zero)");
+test(() => {
+ const argument = { "element": "funcref", "initial": 5 };
+ const table = new WebAssembly.Table(argument);
+ assert_Table(table, { "length": 5 });
+}, "Basic with 'funcref'");
+
test(() => {
const argument = { "element": "anyfunc", "initial": 0 };
const table = new WebAssembly.Table(argument, {});
diff --git a/test/js-api/table/type.any.js b/test/js-api/table/type.any.js
new file mode 100644
index 00000000..eedb80b5
--- /dev/null
+++ b/test/js-api/table/type.any.js
@@ -0,0 +1,42 @@
+// META: global=window,dedicatedworker,jsshell
+// META: script=/wasm/jsapi/assertions.js
+
+function assert_type(argument, element) {
+ const mytable = new WebAssembly.Table(argument);
+ const tabletype = mytable.type()
+ assert_equals(tabletype.minimum, argument.minimum);
+ assert_equals(tabletype.maximum, argument.maximum);
+ assert_equals(tabletype.element, element);
+}
+
+test(() => {
+ assert_type({ "minimum": 0, "element": "anyfunc"}, "funcref");
+}, "anyfunc, Zero initial, no maximum");
+
+test(() => {
+ assert_type({ "minimum": 5, "element": "anyfunc" }, "funcref");
+}, "anyfunc, Non-zero initial, no maximum");
+
+test(() => {
+ assert_type({ "minimum": 0, "maximum": 0, "element": "anyfunc" }, "funcref");
+}, "anyfunc, Zero maximum");
+
+test(() => {
+ assert_type({ "minimum": 0, "maximum": 5, "element": "anyfunc" }, "funcref");
+}, "anyfunc, Non-zero maximum");
+
+test(() => {
+ assert_type({ "minimum": 0, "element": "funcref"}, "funcref");
+}, "funcref, Zero initial, no maximum");
+
+test(() => {
+ assert_type({ "minimum": 5, "element": "funcref" }, "funcref");
+}, "funcref, Non-zero initial, no maximum");
+
+test(() => {
+ assert_type({ "minimum": 0, "maximum": 0, "element": "funcref" }, "funcref");
+}, "funcref, Zero maximum");
+
+test(() => {
+ assert_type({ "minimum": 0, "maximum": 5, "element": "funcref" }, "funcref");
+}, "funcref, Non-zero maximum");