Bug 1284155 - Baldr: add element type to Tables (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Tue, 02 Aug 2016 10:13:10 -0500
changeset 333598 feceaaf64da6da8378d6eef074333e8c6a2e902d
parent 333597 a2624cfb132a1dfc1d76ed8e581bc05b36cd7119
child 333599 a4b528711d2f3bd3d4cfe2a9eaa91c1b19f3d572
push id10033
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:50:26 +0000
treeherdermozilla-aurora@5dddbefdf759 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1284155
milestone51.0a1
Bug 1284155 - Baldr: add element type to Tables (r=bbouvier) MozReview-Commit-ID: 236qwoLkmH8
js/src/asmjs/WasmBinary.h
js/src/asmjs/WasmCompile.cpp
js/src/asmjs/WasmJS.cpp
js/src/asmjs/WasmTextToBinary.cpp
js/src/jit-test/tests/wasm/import-export.js
js/src/jit-test/tests/wasm/jsapi.js
js/src/jit-test/tests/wasm/table-gc.js
js/src/jit-test/tests/wasm/tables.js
js/src/js.msg
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -59,16 +59,17 @@ enum class ValType
     B16x8,
     B32x4,
 
     Limit
 };
 
 enum class TypeConstructor
 {
+    AnyFunc                              = 0x20,
     Function                             = 0x40
 };
 
 enum class DefinitionKind
 {
     Function                             = 0x00,
     Table                                = 0x01,
     Memory                               = 0x02,
--- a/js/src/asmjs/WasmCompile.cpp
+++ b/js/src/asmjs/WasmCompile.cpp
@@ -722,16 +722,23 @@ DecodeResizableMemory(Decoder& d, Module
     }
 
     return true;
 }
 
 static bool
 DecodeResizableTable(Decoder& d, ModuleGeneratorData* init)
 {
+    uint32_t elementType;
+    if (!d.readVarU32(&elementType))
+        return Fail(d, "expected table element type");
+
+    if (elementType != uint32_t(TypeConstructor::AnyFunc))
+        return Fail(d, "expected 'anyfunc' element type");
+
     Resizable resizable;
     if (!DecodeResizable(d, &resizable))
         return false;
 
     if (!init->tables.empty())
         return Fail(d, "already have default table");
 
     TableDesc table(TableKind::AnyFunction);
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -895,28 +895,50 @@ WasmTableObject::construct(JSContext* cx
     if (!args.requireAtLeast(cx, "WebAssembly.Table", 1))
         return false;
 
     if (!args.get(0).isObject()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_DESC_ARG, "table");
         return false;
     }
 
+    RootedObject obj(cx, &args[0].toObject());
+    RootedId id(cx);
+    RootedValue val(cx);
+
+    JSAtom* elementAtom = Atomize(cx, "element", strlen("element"));
+    if (!elementAtom)
+        return false;
+    id = AtomToId(elementAtom);
+    if (!GetProperty(cx, obj, obj, id, &val))
+        return false;
+
+    if (!val.isString()) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_ELEMENT);
+        return false;
+    }
+
+    JSLinearString* str = val.toString()->ensureLinear(cx);
+    if (!str)
+        return false;
+
+    if (!StringEqualsAscii(str, "anyfunc")) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_ELEMENT);
+        return false;
+    }
+
     JSAtom* initialAtom = Atomize(cx, "initial", strlen("initial"));
     if (!initialAtom)
         return false;
-
-    RootedObject obj(cx, &args[0].toObject());
-    RootedId id(cx, AtomToId(initialAtom));
-    RootedValue initialVal(cx);
-    if (!GetProperty(cx, obj, obj, id, &initialVal))
+    id = AtomToId(initialAtom);
+    if (!GetProperty(cx, obj, obj, id, &val))
         return false;
 
     double initialDbl;
-    if (!ToInteger(cx, initialVal, &initialDbl))
+    if (!ToInteger(cx, val, &initialDbl))
         return false;
 
     if (initialDbl < 0 || initialDbl > INT32_MAX) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_SIZE, "Table", "initial");
         return false;
     }
 
     uint32_t initial = uint32_t(initialDbl);
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -3713,16 +3713,25 @@ EncodeResizable(Encoder& e, const AstRes
         if (!e.writeVarU32(*resizable.maximum()))
             return false;
     }
 
     return true;
 }
 
 static bool
+EncodeResizableTable(Encoder& e, const AstResizable& resizable)
+{
+    if (!e.writeVarU32(uint32_t(TypeConstructor::AnyFunc)))
+        return false;
+
+    return EncodeResizable(e, resizable);
+}
+
+static bool
 EncodeImport(Encoder& e, bool newFormat, AstImport& imp)
 {
     if (!newFormat) {
         if (!e.writeVarU32(imp.funcSig().index()))
             return false;
 
         if (!EncodeBytes(e, imp.module()))
             return false;
@@ -3750,16 +3759,19 @@ EncodeImport(Encoder& e, bool newFormat,
       case DefinitionKind::Global:
         MOZ_ASSERT(!imp.global().hasInit());
         if (!e.writeValType(imp.global().type()))
             return false;
         if (!e.writeVarU32(imp.global().flags()))
             return false;
         break;
       case DefinitionKind::Table:
+        if (!EncodeResizableTable(e, imp.resizable()))
+            return false;
+        break;
       case DefinitionKind::Memory:
         if (!EncodeResizable(e, imp.resizable()))
             return false;
         break;
     }
 
     return true;
 }
@@ -3931,17 +3943,17 @@ EncodeTableSection(Encoder& e, bool newF
 
     size_t offset;
     if (!e.startSection(TableSectionId, &offset))
         return false;
 
     const AstResizable& table = module.table();
 
     if (newFormat) {
-        if (!EncodeResizable(e, table))
+        if (!EncodeResizableTable(e, table))
             return false;
     } else {
         if (module.elemSegments().length() != 1)
             return false;
 
         const AstElemSegment& seg = *module.elemSegments()[0];
 
         if (!e.writeVarU32(seg.elems().length()))
--- a/js/src/jit-test/tests/wasm/import-export.js
+++ b/js/src/jit-test/tests/wasm/import-export.js
@@ -5,20 +5,20 @@ const Module = WebAssembly.Module;
 const Instance = WebAssembly.Instance;
 const Memory = WebAssembly.Memory;
 const Table = WebAssembly.Table;
 
 const mem1Page = new Memory({initial:1});
 const mem2Page = new Memory({initial:2});
 const mem3Page = new Memory({initial:3});
 const mem4Page = new Memory({initial:4});
-const tab1Elem = new Table({initial:1});
-const tab2Elem = new Table({initial:2});
-const tab3Elem = new Table({initial:3});
-const tab4Elem = new Table({initial:4});
+const tab1Elem = new Table({initial:1, element:"anyfunc"});
+const tab2Elem = new Table({initial:2, element:"anyfunc"});
+const tab3Elem = new Table({initial:3, element:"anyfunc"});
+const tab4Elem = new Table({initial:4, element:"anyfunc"});
 
 // Explicitly opt into the new binary format for imports and exports until it
 // is used by default everywhere.
 const textToBinary = str => wasmTextToBinary(str, 'new-format');
 
 const m1 = new Module(textToBinary('(module (import "foo" "bar") (import "baz" "quux"))'));
 assertErrorMessage(() => new Instance(m1), TypeError, /no import object given/);
 assertErrorMessage(() => new Instance(m1, {foo:null}), TypeError, /import object field is not an Object/);
@@ -254,17 +254,17 @@ assertEq(e.tbl1.get(0), e.tbl1.get(3));
 
 var code = textToBinary('(module (import "a" "b" (memory 1 1)) (export "foo" memory) (export "bar" memory))');
 var mem = new Memory({initial:1});
 var e = new Instance(new Module(code), {a:{b:mem}}).exports;
 assertEq(mem, e.foo);
 assertEq(mem, e.bar);
 
 var code = textToBinary('(module (import "a" "b" (table 1 1)) (export "foo" table) (export "bar" table))');
-var tbl = new Table({initial:1});
+var tbl = new Table({initial:1, element:"anyfunc"});
 var e = new Instance(new Module(code), {a:{b:tbl}}).exports;
 assertEq(tbl, e.foo);
 assertEq(tbl, e.bar);
 
 // Non-existent export errors
 
 assertErrorMessage(() => new Module(textToBinary('(module (export "a" 0))')), TypeError, /exported function index out of bounds/);
 assertErrorMessage(() => new Module(textToBinary('(module (export "a" global 0))')), TypeError, /exported global index out of bounds/);
@@ -312,17 +312,17 @@ var m = new Module(textToBinary(`
         (import "a" "b" (table 10))
         (elem (i32.const 0) $one $two)
         (elem (i32.const 3) $three $four)
         (func $one (result i32) (i32.const 1))
         (func $two (result i32) (i32.const 2))
         (func $three (result i32) (i32.const 3))
         (func $four (result i32) (i32.const 4)))
 `));
-var tbl = new Table({initial:10});
+var tbl = new Table({initial:10, element:"anyfunc"});
 new Instance(m, {a:{b:tbl}});
 assertEq(tbl.get(0)(), 1);
 assertEq(tbl.get(1)(), 2);
 assertEq(tbl.get(2), null);
 assertEq(tbl.get(3)(), 3);
 assertEq(tbl.get(4)(), 4);
 for (var i = 5; i < 10; i++)
     assertEq(tbl.get(i), null);
--- a/js/src/jit-test/tests/wasm/jsapi.js
+++ b/js/src/jit-test/tests/wasm/jsapi.js
@@ -166,37 +166,40 @@ assertEq(tableDesc.configurable, true);
 
 // 'WebAssembly.Table' constructor function
 const Table = WebAssembly.Table;
 assertEq(Table, tableDesc.value);
 assertEq(Table.length, 1);
 assertEq(Table.name, "Table");
 assertErrorMessage(() => Table(), TypeError, /constructor without new is forbidden/);
 assertErrorMessage(() => new Table(1), TypeError, "first argument must be a table descriptor");
-assertErrorMessage(() => new Table({initial:{valueOf() { throw new Error("here")}}}), Error, "here");
-assertErrorMessage(() => new Table({initial:-1}), TypeError, /bad Table initial size/);
-assertErrorMessage(() => new Table({initial:Math.pow(2,32)}), TypeError, /bad Table initial size/);
-assertEq(new Table({initial:1}) instanceof Table, true);
-assertEq(new Table({initial:1.5}) instanceof Table, true);
+assertErrorMessage(() => new Table({initial:1, element:1}), TypeError, /must be "anyfunc"/);
+assertErrorMessage(() => new Table({initial:1, element:"any"}), TypeError, /must be "anyfunc"/);
+assertErrorMessage(() => new Table({initial:1, element:{valueOf() { return "anyfunc" }}}), TypeError, /must be "anyfunc"/);
+assertErrorMessage(() => new Table({initial:{valueOf() { throw new Error("here")}}, element:"anyfunc"}), Error, "here");
+assertErrorMessage(() => new Table({initial:-1, element:"anyfunc"}), TypeError, /bad Table initial size/);
+assertErrorMessage(() => new Table({initial:Math.pow(2,32), element:"anyfunc"}), TypeError, /bad Table initial size/);
+assertEq(new Table({initial:1, element:"anyfunc"}) instanceof Table, true);
+assertEq(new Table({initial:1.5, element:"anyfunc"}) instanceof Table, true);
 
 // 'WebAssembly.Table.prototype' property
 const tableProtoDesc = Object.getOwnPropertyDescriptor(Table, 'prototype');
 assertEq(typeof tableProtoDesc.value, "object");
 assertEq(tableProtoDesc.writable, false);
 assertEq(tableProtoDesc.enumerable, false);
 assertEq(tableProtoDesc.configurable, false);
 
 // 'WebAssembly.Table.prototype' object
 const tableProto = Table.prototype;
 assertEq(tableProto, tableProtoDesc.value);
 assertEq(String(tableProto), "[object Object]");
 assertEq(Object.getPrototypeOf(tableProto), Object.prototype);
 
 // 'WebAssembly.Table' instance objects
-const tbl1 = new Table({initial:2});
+const tbl1 = new Table({initial:2, element:"anyfunc"});
 assertEq(typeof tbl1, "object");
 assertEq(String(tbl1), "[object WebAssembly.Table]");
 assertEq(Object.getPrototypeOf(tbl1), tableProto);
 
 // 'WebAssembly.Table.prototype.length' accessor property
 const lengthDesc = Object.getOwnPropertyDescriptor(tableProto, 'length');
 assertEq(typeof lengthDesc.get, "function");
 assertEq(lengthDesc.set, undefined);
--- a/js/src/jit-test/tests/wasm/table-gc.js
+++ b/js/src/jit-test/tests/wasm/table-gc.js
@@ -103,34 +103,34 @@ gc();
 assertEq(finalizeCount(), 1);
 t = null;
 gc();
 assertEq(finalizeCount(), 3);
 
 // Before initialization, a table is not bound to any instance.
 resetFinalizeCount();
 var i = evalText(`(module (func $f0 (result i32) (i32.const 0)) (export "f0" $f0))`);
-var t = new Table({initial:4});
+var t = new Table({initial:4, element:"anyfunc"});
 i.edge = makeFinalizeObserver();
 t.edge = makeFinalizeObserver();
 gc();
 assertEq(finalizeCount(), 0);
 i = null;
 gc();
 assertEq(finalizeCount(), 1);
 t = null;
 gc();
 assertEq(finalizeCount(), 2);
 
 // When a Table is created (uninitialized) and then first assigned, it keeps the
 // first element's Instance alive (as above).
 resetFinalizeCount();
 var i = evalText(`(module (func $f (result i32) (i32.const 42)) (export "f" $f))`);
 var f = i.exports.f;
-var t = new Table({initial:1});
+var t = new Table({initial:1, element:"anyfunc"});
 i.edge = makeFinalizeObserver();
 f.edge = makeFinalizeObserver();
 t.edge = makeFinalizeObserver();
 t.set(0, f);
 assertEq(t.get(0), f);
 assertEq(t.get(0)(), 42);
 gc();
 assertEq(finalizeCount(), 0);
--- a/js/src/jit-test/tests/wasm/tables.js
+++ b/js/src/jit-test/tests/wasm/tables.js
@@ -24,17 +24,17 @@ assertErrorMessage(() => new Module(text
 assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f0) (elem (i32.const 0) $f0) ${callee(0)})`)), TypeError, /must be.*ordered/);
 assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`)), TypeError, /must be.*disjoint/);
 
 assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:10}}), TypeError, /element segment does not fit/);
 assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0 $f0) ${callee(0)})`, {globals:{a:8}}), TypeError, /element segment does not fit/);
 assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (i32.const 1) $f0 $f0) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:0}}), TypeError, /must be.*ordered/);
 assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`, {globals:{a:1}}), TypeError, /must be.*disjoint/);
 
-var tbl = new Table({initial:50});
+var tbl = new Table({initial:50, element:"anyfunc"});
 assertErrorMessage(() => evalText(`(module
     (import "globals" "table" (table 10 100))
     (import "globals" "a" (global i32 immutable))
     (elem (get_global 0) $f0 $f0)
     ${callee(0)})
 `, {globals:{a:20, table:tbl}}), Error, /element segment does not fit/);
 
 var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (get_local $i))) (export "call" $call)`
@@ -59,17 +59,17 @@ assertErrorMessage(() => call(0), Error,
 assertEq(call(1), 0);
 assertEq(call(2), 1);
 assertErrorMessage(() => call(3), Error, /bad wasm indirect call/);
 assertEq(call(4), 0);
 assertEq(call(5), 2);
 assertErrorMessage(() => call(6), Error, /bad wasm indirect call/);
 assertErrorMessage(() => call(10), Error, /out-of-range/);
 
-var tbl = new Table({initial:3});
+var tbl = new Table({initial:3, element:"anyfunc"});
 var call = evalText(`(module (import "a" "b" (table 2)) (export "tbl" table) (elem (i32.const 0) $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
 assertEq(call(0), 0);
 assertEq(call(1), 1);
 assertEq(tbl.get(0)(), 0);
 assertEq(tbl.get(1)(), 1);
 
 // Call signatures are matched structurally:
 
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -345,16 +345,17 @@ MSG_DEF(JSMSG_WASM_FAIL,               1
 MSG_DEF(JSMSG_WASM_DECODE_FAIL,        2, JSEXN_TYPEERR,     "wasm validation error at offset {0}: {1}")
 MSG_DEF(JSMSG_WASM_TEXT_FAIL,          1, JSEXN_SYNTAXERR,   "wasm text error: {0}")
 MSG_DEF(JSMSG_WASM_BAD_IND_CALL,       0, JSEXN_ERR,         "bad wasm indirect call")
 MSG_DEF(JSMSG_WASM_BAD_BUF_ARG,        0, JSEXN_TYPEERR,     "first argument must be an ArrayBuffer or typed array object")
 MSG_DEF(JSMSG_WASM_BAD_MOD_ARG,        0, JSEXN_TYPEERR,     "first argument must be a WebAssembly.Module")
 MSG_DEF(JSMSG_WASM_BAD_DESC_ARG,       1, JSEXN_TYPEERR,     "first argument must be a {0} descriptor")
 MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE,       1, JSEXN_TYPEERR,     "imported {0} with incompatible size")
 MSG_DEF(JSMSG_WASM_BAD_SIZE,           2, JSEXN_TYPEERR,     "bad {0} {1} size")
+MSG_DEF(JSMSG_WASM_BAD_ELEMENT,        0, JSEXN_TYPEERR,     "\"element\" property of table descriptor must be \"anyfunc\"")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG,     0, JSEXN_TYPEERR,     "second argument, if present, must be an object")
 MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD,   1, JSEXN_TYPEERR,     "import object field is not {0}")
 MSG_DEF(JSMSG_WASM_BAD_SET_VALUE,      0, JSEXN_TYPEERR,     "second argument must be null or an exported WebAssembly Function object")
 MSG_DEF(JSMSG_WASM_UNREACHABLE,        0, JSEXN_ERR,         "unreachable executed")
 MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW,   0, JSEXN_ERR,         "integer overflow")
 MSG_DEF(JSMSG_WASM_INVALID_CONVERSION, 0, JSEXN_ERR,         "invalid conversion to integer")
 MSG_DEF(JSMSG_WASM_INT_DIVIDE_BY_ZERO, 0, JSEXN_ERR,         "integer divide by zero")
 MSG_DEF(JSMSG_WASM_UNALIGNED_ACCESS,   0, JSEXN_ERR,         "unaligned memory access")