зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
ec43d5678c
|
@ -84,8 +84,8 @@ for (let v of VALUES)
|
|||
//
|
||||
// - through WebAssembly.Table.prototype.set()
|
||||
// - through the table.set, table.copy, and table.grow instructions
|
||||
// - unimplemented: through table.fill
|
||||
// - unimplemented: through WebAssembly.Table.prototype.grow()
|
||||
// - through table.fill
|
||||
// - through WebAssembly.Table.prototype.grow()
|
||||
//
|
||||
// Their values can be read in several ways:
|
||||
//
|
||||
|
|
|
@ -1,145 +1,145 @@
|
|||
// |jit-test| skip-if: !wasmReftypesEnabled()
|
||||
|
||||
let ins
|
||||
= wasmEvalText(
|
||||
`(module
|
||||
(table 8 anyref) ;; table 0
|
||||
(table $t 10 anyref) ;; table 1
|
||||
const N = 8;
|
||||
|
||||
;; fill/get for table 0, referenced implicitly
|
||||
(func (export "fill0") (param $i i32) (param $r anyref) (param $n i32)
|
||||
(table.fill (local.get $i) (local.get $r) (local.get $n))
|
||||
)
|
||||
(func (export "get0") (param $i i32) (result anyref)
|
||||
(table.get (local.get $i))
|
||||
)
|
||||
function testTableFill(type, obj) {
|
||||
assertEq(obj.length, N);
|
||||
|
||||
;; fill/get for table 1, referenced explicitly
|
||||
(func (export "fill1") (param $i i32) (param $r anyref) (param $n i32)
|
||||
(table.fill $t (local.get $i) (local.get $r) (local.get $n))
|
||||
)
|
||||
(func (export "get1") (param $i i32) (result anyref)
|
||||
(table.get $t (local.get $i))
|
||||
)
|
||||
)`);
|
||||
let ins
|
||||
= wasmEvalText(
|
||||
`(module
|
||||
(table 8 ${type}) ;; table 0
|
||||
(table $t 10 ${type}) ;; table 1
|
||||
|
||||
function Obj(n) {
|
||||
this.n = n;
|
||||
;; fill/get for table 0, referenced implicitly
|
||||
(func (export "fill0") (param $i i32) (param $r ${type}) (param $n i32)
|
||||
(table.fill (local.get $i) (local.get $r) (local.get $n))
|
||||
)
|
||||
(func (export "get0") (param $i i32) (result ${type})
|
||||
(table.get (local.get $i))
|
||||
)
|
||||
|
||||
;; fill/get for table 1, referenced explicitly
|
||||
(func (export "fill1") (param $i i32) (param $r ${type}) (param $n i32)
|
||||
(table.fill $t (local.get $i) (local.get $r) (local.get $n))
|
||||
)
|
||||
(func (export "get1") (param $i i32) (result ${type})
|
||||
(table.get $t (local.get $i))
|
||||
)
|
||||
)`);
|
||||
|
||||
// An initial test to ascertain that tables 0 and 1 are independent
|
||||
|
||||
// Fill in table 0, then check it.
|
||||
assertEq(ins.exports.fill0(2, obj[6], 5), undefined)
|
||||
assertEq(ins.exports.fill0(1, obj[7], 3), undefined);
|
||||
|
||||
function check_table0() {
|
||||
assertEq(ins.exports.get0(0), null);
|
||||
assertEq(ins.exports.get0(1), obj[7]);
|
||||
assertEq(ins.exports.get0(2), obj[7]);
|
||||
assertEq(ins.exports.get0(3), obj[7]);
|
||||
assertEq(ins.exports.get0(4), obj[6]);
|
||||
assertEq(ins.exports.get0(5), obj[6]);
|
||||
assertEq(ins.exports.get0(6), obj[6]);
|
||||
assertEq(ins.exports.get0(7), null);
|
||||
}
|
||||
|
||||
// Check that table 0 has the expected content.
|
||||
check_table0();
|
||||
|
||||
// Check that messing with table 0 above hasn't changed table 1.
|
||||
for (let i = 0; i < 10; i++) {
|
||||
assertEq(ins.exports.get1(i), null);
|
||||
}
|
||||
|
||||
// Now a bunch of tests involving only table 1.
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(2, obj[0], 3), undefined);
|
||||
assertEq(ins.exports.get1(1), null);
|
||||
assertEq(ins.exports.get1(2), obj[0]);
|
||||
assertEq(ins.exports.get1(3), obj[0]);
|
||||
assertEq(ins.exports.get1(4), obj[0]);
|
||||
assertEq(ins.exports.get1(5), null);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(4, obj[1], 2), undefined);
|
||||
assertEq(ins.exports.get1(3), obj[0]);
|
||||
assertEq(ins.exports.get1(4), obj[1]);
|
||||
assertEq(ins.exports.get1(5), obj[1]);
|
||||
assertEq(ins.exports.get1(6), null);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(4, obj[2], 0), undefined);
|
||||
assertEq(ins.exports.get1(3), obj[0]);
|
||||
assertEq(ins.exports.get1(4), obj[1]);
|
||||
assertEq(ins.exports.get1(5), obj[1]);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(8, obj[3], 2), undefined);
|
||||
assertEq(ins.exports.get1(7), null);
|
||||
assertEq(ins.exports.get1(8), obj[3]);
|
||||
assertEq(ins.exports.get1(9), obj[3]);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(9, null, 1), undefined);
|
||||
assertEq(ins.exports.get1(8), obj[3]);
|
||||
assertEq(ins.exports.get1(9), null);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(10, obj[4], 0), undefined);
|
||||
assertEq(ins.exports.get1(9), null);
|
||||
|
||||
// Partly outside the table
|
||||
assertErrorMessage(() => ins.exports.fill1(8, obj[5], 3),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
assertEq(ins.exports.get1(7), null);
|
||||
assertEq(ins.exports.get1(8), obj[5]);
|
||||
assertEq(ins.exports.get1(9), obj[5]);
|
||||
|
||||
// Boundary tests on table 1: at the edge of the table.
|
||||
|
||||
// Length-zero fill1 at the edge of the table must succeed
|
||||
assertEq(ins.exports.fill1(10, null, 0), undefined);
|
||||
|
||||
// Length-one fill1 at the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(10, null, 1),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
// Length-more-than-one fill1 at the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(10, null, 2),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
|
||||
// Boundary tests on table 1: beyond the edge of the table:
|
||||
|
||||
// Length-zero fill1 beyond the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(11, null, 0),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
// Length-one fill1 beyond the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(11, null, 1),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
// Length-more-than-one fill1 beyond the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(11, null, 2),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
// Following all the above tests on table 1, check table 0 hasn't changed.
|
||||
check_table0();
|
||||
}
|
||||
|
||||
function mkObj(n) {
|
||||
return new Obj(n);
|
||||
}
|
||||
var objs = [];
|
||||
for (var i = 0; i < N; i++)
|
||||
objs[i] = {n:i};
|
||||
testTableFill('anyref', objs);
|
||||
|
||||
const obj1 = mkObj(1);
|
||||
const obj2 = mkObj(2);
|
||||
const obj3 = mkObj(3);
|
||||
const obj4 = mkObj(4);
|
||||
const obj5 = mkObj(5);
|
||||
const obj6 = mkObj(6);
|
||||
const obj7 = mkObj(7);
|
||||
const obj8 = mkObj(8);
|
||||
|
||||
// An initial test to ascertain that tables 0 and 1 are independent
|
||||
|
||||
// Fill in table 0, then check it.
|
||||
assertEq(ins.exports.fill0(2, obj7, 5), undefined)
|
||||
assertEq(ins.exports.fill0(1, obj8, 3), undefined);
|
||||
|
||||
function check_table0() {
|
||||
assertEq(ins.exports.get0(0), null);
|
||||
assertEq(ins.exports.get0(1), obj8);
|
||||
assertEq(ins.exports.get0(2), obj8);
|
||||
assertEq(ins.exports.get0(3), obj8);
|
||||
assertEq(ins.exports.get0(4), obj7);
|
||||
assertEq(ins.exports.get0(5), obj7);
|
||||
assertEq(ins.exports.get0(6), obj7);
|
||||
assertEq(ins.exports.get0(7), null);
|
||||
}
|
||||
|
||||
// Check that table 0 has the expected content.
|
||||
check_table0();
|
||||
|
||||
// Check that messing with table 0 above hasn't changed table 1.
|
||||
for (let i = 0; i < 10; i++) {
|
||||
assertEq(ins.exports.get1(i), null);
|
||||
}
|
||||
|
||||
|
||||
// Now a bunch of tests involving only table 1.
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(2, obj1, 3), undefined);
|
||||
assertEq(ins.exports.get1(1), null);
|
||||
assertEq(ins.exports.get1(2), obj1);
|
||||
assertEq(ins.exports.get1(3), obj1);
|
||||
assertEq(ins.exports.get1(4), obj1);
|
||||
assertEq(ins.exports.get1(5), null);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(4, obj2, 2), undefined);
|
||||
assertEq(ins.exports.get1(3), obj1);
|
||||
assertEq(ins.exports.get1(4), obj2);
|
||||
assertEq(ins.exports.get1(5), obj2);
|
||||
assertEq(ins.exports.get1(6), null);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(4, obj3, 0), undefined);
|
||||
assertEq(ins.exports.get1(3), obj1);
|
||||
assertEq(ins.exports.get1(4), obj2);
|
||||
assertEq(ins.exports.get1(5), obj2);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(8, obj4, 2), undefined);
|
||||
assertEq(ins.exports.get1(7), null);
|
||||
assertEq(ins.exports.get1(8), obj4);
|
||||
assertEq(ins.exports.get1(9), obj4);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(9, null, 1), undefined);
|
||||
assertEq(ins.exports.get1(8), obj4);
|
||||
assertEq(ins.exports.get1(9), null);
|
||||
|
||||
// Within the table
|
||||
assertEq(ins.exports.fill1(10, obj5, 0), undefined);
|
||||
assertEq(ins.exports.get1(9), null);
|
||||
|
||||
// Partly outside the table
|
||||
assertErrorMessage(() => ins.exports.fill1(8, obj6, 3),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
assertEq(ins.exports.get1(7), null);
|
||||
assertEq(ins.exports.get1(8), obj6);
|
||||
assertEq(ins.exports.get1(9), obj6);
|
||||
|
||||
|
||||
// Boundary tests on table 1: at the edge of the table.
|
||||
|
||||
// Length-zero fill1 at the edge of the table must succeed
|
||||
assertEq(ins.exports.fill1(10, null, 0), undefined);
|
||||
|
||||
// Length-one fill1 at the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(10, null, 1),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
// Length-more-than-one fill1 at the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(10, null, 2),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
|
||||
// Boundary tests on table 1: beyond the edge of the table:
|
||||
|
||||
// Length-zero fill1 beyond the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(11, null, 0),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
// Length-one fill1 beyond the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(11, null, 1),
|
||||
RangeError, /table index out of bounds/);
|
||||
|
||||
// Length-more-than-one fill1 beyond the edge of the table fails
|
||||
assertErrorMessage(() => ins.exports.fill1(11, null, 2),
|
||||
RangeError, /table index out of bounds/);
|
||||
var funcs = [];
|
||||
for (var i = 0; i < N; i++)
|
||||
funcs[i] = wasmEvalText(`(module (func (export "x") (result i32) (i32.const ${i})))`).exports.x;
|
||||
testTableFill('funcref', funcs);
|
||||
|
||||
|
||||
// Type errors. Required sig is: (i32, anyref, i32) -> void
|
||||
|
@ -203,24 +203,3 @@ assertErrorMessage(() => wasmEvalText(
|
|||
))`),
|
||||
WebAssembly.CompileError,
|
||||
/popping value from empty stack/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t 0 funcref)
|
||||
(func $tables-of-funcref-not-allowed (param $r anyref)
|
||||
(table.fill $t (i32.const 1) (local.get $r) (i32.const 1))
|
||||
))`),
|
||||
WebAssembly.CompileError, /table.fill only on tables of anyref/);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t1 1 anyref)
|
||||
(table $t2 1 funcref)
|
||||
(func $tables-of-funcref-not-allowed-2 (param $r anyref)
|
||||
(table.fill $t2 (i32.const 0) (local.get $r) (i32.const 1))
|
||||
))`),
|
||||
WebAssembly.CompileError, /table.fill only on tables of anyref/);
|
||||
|
||||
|
||||
// Following all the above tests on table 1, check table 0 hasn't changed.
|
||||
check_table0();
|
||||
|
|
|
@ -154,22 +154,25 @@ function dummy() { return 37 }
|
|||
//
|
||||
// table.get and table.set
|
||||
|
||||
const wasmFun = wasmEvalText(`(module (func (export "x")))`).exports.x;
|
||||
|
||||
// table.get in bounds - returns right value type & value
|
||||
// table.get out of bounds - fails
|
||||
|
||||
{
|
||||
function testTableGet(type, x) {
|
||||
let ins = wasmEvalText(
|
||||
`(module
|
||||
(table (export "t") 10 anyref)
|
||||
(func (export "f") (param i32) (result anyref)
|
||||
(table (export "t") 10 ${type})
|
||||
(func (export "f") (param i32) (result ${type})
|
||||
(table.get (local.get 0))))`);
|
||||
let x = {};
|
||||
ins.exports.t.set(0, x);
|
||||
assertEq(ins.exports.f(0), x);
|
||||
assertEq(ins.exports.f(1), null);
|
||||
assertErrorMessage(() => ins.exports.f(10), RangeError, /index out of bounds/);
|
||||
assertErrorMessage(() => ins.exports.f(-5), RangeError, /index out of bounds/);
|
||||
}
|
||||
testTableGet('anyref', {});
|
||||
testTableGet('funcref', wasmFun);
|
||||
|
||||
// table.get with non-i32 index - fails validation
|
||||
|
||||
|
@ -181,25 +184,6 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
WebAssembly.CompileError,
|
||||
/type mismatch/);
|
||||
|
||||
// table.get on table of funcref - fails validation because funcref is not expressible
|
||||
// Both with and without anyref support
|
||||
|
||||
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
||||
`(module
|
||||
(table 10 funcref)
|
||||
(func (export "f") (param i32)
|
||||
(drop (table.get (local.get 0)))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/table.get only on tables of anyref/);
|
||||
|
||||
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
||||
`(module
|
||||
(table 10 funcref)
|
||||
(func (export "f") (param i32)
|
||||
(drop (table.get (local.get 0)))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/table.get only on tables of anyref/);
|
||||
|
||||
// table.get when there are no tables - fails validation
|
||||
|
||||
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
||||
|
@ -213,15 +197,14 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
// table.set with null - works
|
||||
// table.set out of bounds - fails
|
||||
|
||||
{
|
||||
function testTableSet(type, x) {
|
||||
let ins = wasmEvalText(
|
||||
`(module
|
||||
(table (export "t") 10 anyref)
|
||||
(func (export "set_anyref") (param i32) (param anyref)
|
||||
(table (export "t") 10 ${type})
|
||||
(func (export "set_anyref") (param i32) (param ${type})
|
||||
(table.set (local.get 0) (local.get 1)))
|
||||
(func (export "set_null") (param i32)
|
||||
(table.set (local.get 0) (ref.null))))`);
|
||||
let x = {};
|
||||
ins.exports.set_anyref(3, x);
|
||||
assertEq(ins.exports.t.get(3), x);
|
||||
ins.exports.set_null(3);
|
||||
|
@ -230,6 +213,8 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
assertErrorMessage(() => ins.exports.set_anyref(10, x), RangeError, /index out of bounds/);
|
||||
assertErrorMessage(() => ins.exports.set_anyref(-1, x), RangeError, /index out of bounds/);
|
||||
}
|
||||
testTableSet('anyref', {});
|
||||
testTableSet('funcref', wasmFun);
|
||||
|
||||
// table.set with non-i32 index - fails validation
|
||||
|
||||
|
@ -241,7 +226,7 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
WebAssembly.CompileError,
|
||||
/type mismatch/);
|
||||
|
||||
// table.set with non-anyref value - fails validation
|
||||
// table.set with non-ref value - fails validation
|
||||
|
||||
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
||||
`(module
|
||||
|
@ -250,16 +235,13 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
(table.set (i32.const 0) (local.get 0))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/type mismatch/);
|
||||
|
||||
// table.set on table of funcref - fails validation
|
||||
|
||||
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
||||
`(module
|
||||
(table 10 funcref)
|
||||
(func (export "f") (param anyref)
|
||||
(table.set (i32.const 0) (local.get 0))))`)),
|
||||
(table 10 funcref)
|
||||
(func (export "f") (param f64)
|
||||
(table.set (i32.const 0) (local.get 0))))`)),
|
||||
WebAssembly.CompileError,
|
||||
/table.set only on tables of anyref/);
|
||||
/type mismatch/);
|
||||
|
||||
// table.set when there are no tables - fails validation
|
||||
|
||||
|
@ -270,36 +252,43 @@ assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
|||
WebAssembly.CompileError,
|
||||
/table index out of range for table.set/);
|
||||
|
||||
// we can grow table of anyref
|
||||
// table.grow with zero delta - always works even at maximum
|
||||
// table.grow with delta - works and returns correct old value
|
||||
// table.grow with delta at upper limit - fails
|
||||
// table.grow with negative delta - fails
|
||||
function testTableGrow(type, x) {
|
||||
let ins = wasmEvalText(
|
||||
`(module
|
||||
(table (export "t") 10 20 ${type})
|
||||
(func (export "grow") (param i32) (result i32)
|
||||
(table.grow (ref.null) (local.get 0)))
|
||||
(func (export "grow2") (param i32) (param ${type}) (result i32)
|
||||
(table.grow (local.get 1) (local.get 0))))`);
|
||||
|
||||
let ins = wasmEvalText(
|
||||
`(module
|
||||
(table (export "t") 10 20 anyref)
|
||||
(func (export "grow") (param i32) (result i32)
|
||||
(table.grow (ref.null) (local.get 0))))`);
|
||||
assertEq(ins.exports.grow(0), 10);
|
||||
assertEq(ins.exports.t.length, 10);
|
||||
assertEq(ins.exports.grow(1), 10);
|
||||
assertEq(ins.exports.t.length, 11);
|
||||
assertEq(ins.exports.t.get(10), null);
|
||||
assertEq(ins.exports.grow(9), 11);
|
||||
assertEq(ins.exports.t.length, 20);
|
||||
assertEq(ins.exports.t.get(19), null);
|
||||
assertEq(ins.exports.grow(0), 20);
|
||||
// we can grow table of references
|
||||
// table.grow with zero delta - always works even at maximum
|
||||
// table.grow with delta - works and returns correct old value
|
||||
// table.grow with delta at upper limit - fails
|
||||
// table.grow with negative delta - fails
|
||||
assertEq(ins.exports.grow(0), 10);
|
||||
assertEq(ins.exports.t.length, 10);
|
||||
assertEq(ins.exports.grow(1), 10);
|
||||
assertEq(ins.exports.t.length, 11);
|
||||
assertEq(ins.exports.t.get(10), null);
|
||||
assertEq(ins.exports.grow2(9, x), 11);
|
||||
assertEq(ins.exports.t.length, 20);
|
||||
for (var i = 11; i < 20; i++)
|
||||
assertEq(ins.exports.t.get(i), x);
|
||||
assertEq(ins.exports.grow(0), 20);
|
||||
|
||||
// The JS API throws if it can't grow
|
||||
assertErrorMessage(() => ins.exports.t.grow(1), RangeError, /failed to grow table/);
|
||||
assertErrorMessage(() => ins.exports.t.grow(-1), TypeError, /bad [Tt]able grow delta/);
|
||||
// The JS API throws if it can't grow
|
||||
assertErrorMessage(() => ins.exports.t.grow(1), RangeError, /failed to grow table/);
|
||||
assertErrorMessage(() => ins.exports.t.grow(-1), TypeError, /bad [Tt]able grow delta/);
|
||||
|
||||
// The wasm API does not throw if it can't grow, but returns -1
|
||||
assertEq(ins.exports.grow(1), -1);
|
||||
assertEq(ins.exports.t.length, 20);
|
||||
assertEq(ins.exports.grow(-1), -1);
|
||||
assertEq(ins.exports.t.length, 20)
|
||||
// The wasm API does not throw if it can't grow, but returns -1
|
||||
assertEq(ins.exports.grow(1), -1);
|
||||
assertEq(ins.exports.t.length, 20);
|
||||
assertEq(ins.exports.grow(-1), -1);
|
||||
assertEq(ins.exports.t.length, 20)
|
||||
}
|
||||
testTableGrow('anyref', 42);
|
||||
testTableGrow('funcref', wasmFun);
|
||||
|
||||
// Special case for private tables without a maximum
|
||||
|
||||
|
@ -315,16 +304,6 @@ assertEq(ins.exports.t.length, 20)
|
|||
assertEq(ins.exports.grow(0), 20);
|
||||
}
|
||||
|
||||
// Can't grow table of funcref yet
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(
|
||||
`(module
|
||||
(table $t 2 funcref)
|
||||
(func $f
|
||||
(drop (table.grow (ref.null) (i32.const 1)))))`),
|
||||
WebAssembly.CompileError,
|
||||
/table.grow only on tables of anyref/);
|
||||
|
||||
// table.grow with non-i32 argument - fails validation
|
||||
|
||||
assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(
|
||||
|
|
|
@ -886,8 +886,8 @@ void Instance::initElems(uint32_t tableIndex, const ElemSegment& seg,
|
|||
uint32_t tableIndex) {
|
||||
MOZ_ASSERT(SASigTableFill.failureMode == FailureMode::FailOnNegI32);
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
Table& table = *instance->tables()[tableIndex];
|
||||
MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
|
||||
|
||||
if (len == 0) {
|
||||
// Even though the length is zero, we must check for a valid offset. But
|
||||
|
@ -912,10 +912,17 @@ void Instance::initElems(uint32_t tableIndex, const ElemSegment& seg,
|
|||
mustTrap = true;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
uint32_t index = start + i;
|
||||
MOZ_ASSERT(index < table.length());
|
||||
table.setAnyRef(index, AnyRef::fromCompiledCode(value));
|
||||
AnyRef ref = AnyRef::fromCompiledCode(value);
|
||||
|
||||
switch (table.kind()) {
|
||||
case TableKind::AnyRef:
|
||||
table.fillAnyRef(start, len, ref);
|
||||
break;
|
||||
case TableKind::FuncRef:
|
||||
table.fillFuncRef(start, len, ref, cx);
|
||||
break;
|
||||
case TableKind::AsmJS:
|
||||
MOZ_CRASH("not asm.js");
|
||||
}
|
||||
|
||||
if (!mustTrap) {
|
||||
|
@ -923,7 +930,6 @@ void Instance::initElems(uint32_t tableIndex, const ElemSegment& seg,
|
|||
}
|
||||
}
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_TABLE_OUT_OF_BOUNDS);
|
||||
return -1;
|
||||
|
@ -932,30 +938,52 @@ void Instance::initElems(uint32_t tableIndex, const ElemSegment& seg,
|
|||
/* static */ void* Instance::tableGet(Instance* instance, uint32_t index,
|
||||
uint32_t tableIndex) {
|
||||
MOZ_ASSERT(SASigTableGet.failureMode == FailureMode::FailOnInvalidRef);
|
||||
|
||||
const Table& table = *instance->tables()[tableIndex];
|
||||
MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
|
||||
if (index >= table.length()) {
|
||||
JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_TABLE_OUT_OF_BOUNDS);
|
||||
return AnyRef::invalid().forCompiledCode();
|
||||
}
|
||||
return table.getAnyRef(index).forCompiledCode();
|
||||
|
||||
if (table.kind() == TableKind::AnyRef) {
|
||||
return table.getAnyRef(index).forCompiledCode();
|
||||
}
|
||||
|
||||
MOZ_RELEASE_ASSERT(table.kind() == TableKind::FuncRef);
|
||||
|
||||
JSContext* cx = TlsContext.get();
|
||||
RootedFunction fun(cx);
|
||||
if (!table.getFuncRef(cx, index, &fun)) {
|
||||
return AnyRef::invalid().forCompiledCode();
|
||||
}
|
||||
|
||||
return AnyRef::fromJSObject(fun).forCompiledCode();
|
||||
}
|
||||
|
||||
/* static */ uint32_t Instance::tableGrow(Instance* instance, void* initValue,
|
||||
uint32_t delta, uint32_t tableIndex) {
|
||||
MOZ_ASSERT(SASigTableGrow.failureMode == FailureMode::Infallible);
|
||||
|
||||
RootedAnyRef obj(TlsContext.get(), AnyRef::fromCompiledCode(initValue));
|
||||
RootedAnyRef ref(TlsContext.get(), AnyRef::fromCompiledCode(initValue));
|
||||
Table& table = *instance->tables()[tableIndex];
|
||||
MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
|
||||
|
||||
uint32_t oldSize = table.grow(delta, TlsContext.get());
|
||||
JSContext* cx = TlsContext.get();
|
||||
uint32_t oldSize = table.grow(delta, cx);
|
||||
|
||||
if (oldSize != uint32_t(-1) && initValue != nullptr) {
|
||||
for (uint32_t i = 0; i < delta; i++) {
|
||||
table.setAnyRef(oldSize + i, obj.get());
|
||||
switch (table.kind()) {
|
||||
case TableKind::AnyRef:
|
||||
table.fillAnyRef(oldSize, delta, ref);
|
||||
break;
|
||||
case TableKind::FuncRef:
|
||||
table.fillFuncRef(oldSize, delta, ref, cx);
|
||||
break;
|
||||
case TableKind::AsmJS:
|
||||
MOZ_CRASH("not asm.js");
|
||||
}
|
||||
}
|
||||
|
||||
return oldSize;
|
||||
}
|
||||
|
||||
|
@ -964,13 +992,25 @@ void Instance::initElems(uint32_t tableIndex, const ElemSegment& seg,
|
|||
MOZ_ASSERT(SASigTableSet.failureMode == FailureMode::FailOnNegI32);
|
||||
|
||||
Table& table = *instance->tables()[tableIndex];
|
||||
MOZ_RELEASE_ASSERT(table.kind() == TableKind::AnyRef);
|
||||
if (index >= table.length()) {
|
||||
JS_ReportErrorNumberASCII(TlsContext.get(), GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_TABLE_OUT_OF_BOUNDS);
|
||||
return -1;
|
||||
}
|
||||
table.setAnyRef(index, AnyRef::fromCompiledCode(value));
|
||||
|
||||
AnyRef ref = AnyRef::fromCompiledCode(value);
|
||||
|
||||
switch (table.kind()) {
|
||||
case TableKind::AnyRef:
|
||||
table.fillAnyRef(index, 1, ref);
|
||||
break;
|
||||
case TableKind::FuncRef:
|
||||
table.fillFuncRef(index, 1, ref, TlsContext.get());
|
||||
break;
|
||||
case TableKind::AsmJS:
|
||||
MOZ_CRASH("not asm.js");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2128,23 +2128,11 @@ bool WasmTableObject::getImpl(JSContext* cx, const CallArgs& args) {
|
|||
|
||||
switch (table.kind()) {
|
||||
case TableKind::FuncRef: {
|
||||
const FunctionTableElem& elem = table.getFuncRef(index);
|
||||
if (!elem.code) {
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
Instance& instance = *elem.tls->instance;
|
||||
const CodeRange& codeRange = *instance.code().lookupFuncRange(elem.code);
|
||||
|
||||
RootedWasmInstanceObject instanceObj(cx, instance.object());
|
||||
RootedFunction fun(cx);
|
||||
if (!instanceObj->getExportedFunction(cx, instanceObj,
|
||||
codeRange.funcIndex(), &fun)) {
|
||||
if (!table.getFuncRef(cx, index, &fun)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*fun);
|
||||
args.rval().setObjectOrNull(fun);
|
||||
break;
|
||||
}
|
||||
case TableKind::AnyRef: {
|
||||
|
@ -2164,36 +2152,6 @@ bool WasmTableObject::get(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return CallNonGenericMethod<IsTable, getImpl>(cx, args);
|
||||
}
|
||||
|
||||
static void TableFunctionFill(JSContext* cx, Table* table, HandleFunction value,
|
||||
uint32_t index, uint32_t limit) {
|
||||
if (!value) {
|
||||
while (index < limit) {
|
||||
table->setNull(index++);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
RootedWasmInstanceObject instanceObj(cx,
|
||||
ExportedFunctionToInstanceObject(value));
|
||||
uint32_t funcIndex = ExportedFunctionToFuncIndex(value);
|
||||
|
||||
#ifdef DEBUG
|
||||
RootedFunction f(cx);
|
||||
MOZ_ASSERT(instanceObj->getExportedFunction(cx, instanceObj, funcIndex, &f));
|
||||
MOZ_ASSERT(value == f);
|
||||
#endif
|
||||
|
||||
Instance& instance = instanceObj->instance();
|
||||
Tier tier = instance.code().bestTier();
|
||||
const MetadataTier& metadata = instance.metadata(tier);
|
||||
const CodeRange& codeRange =
|
||||
metadata.codeRange(metadata.lookupFuncExport(funcIndex));
|
||||
void* code = instance.codeBase(tier) + codeRange.funcTableEntry();
|
||||
while (index < limit) {
|
||||
table->setFuncRef(index++, code, &instance);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) {
|
||||
RootedWasmTableObject tableObj(
|
||||
|
@ -2218,7 +2176,7 @@ bool WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) {
|
|||
}
|
||||
MOZ_ASSERT(index < MaxTableLength);
|
||||
static_assert(MaxTableLength < UINT32_MAX, "Invariant");
|
||||
TableFunctionFill(cx, &table, fun, index, index + 1);
|
||||
table.fillFuncRef(index, 1, AnyRef::fromJSObject(fun), cx);
|
||||
break;
|
||||
}
|
||||
case TableKind::AnyRef: {
|
||||
|
@ -2226,7 +2184,7 @@ bool WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) {
|
|||
if (!BoxAnyRef(cx, fillValue, &tmp)) {
|
||||
return false;
|
||||
}
|
||||
table.setAnyRef(index, tmp);
|
||||
table.fillAnyRef(index, 1, tmp);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -2246,8 +2204,9 @@ bool WasmTableObject::set(JSContext* cx, unsigned argc, Value* vp) {
|
|||
|
||||
/* static */
|
||||
bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
|
||||
RootedWasmTableObject table(cx,
|
||||
&args.thisv().toObject().as<WasmTableObject>());
|
||||
RootedWasmTableObject tableObj(
|
||||
cx, &args.thisv().toObject().as<WasmTableObject>());
|
||||
Table& table = tableObj->table();
|
||||
|
||||
if (!args.requireAtLeast(cx, "WebAssembly.Table.grow", 1)) {
|
||||
return false;
|
||||
|
@ -2258,7 +2217,7 @@ bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
|
|||
return false;
|
||||
}
|
||||
|
||||
uint32_t oldLength = table->table().grow(delta, cx);
|
||||
uint32_t oldLength = table.grow(delta, cx);
|
||||
|
||||
if (oldLength == uint32_t(-1)) {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW,
|
||||
|
@ -2277,12 +2236,12 @@ bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
|
|||
|
||||
static_assert(MaxTableLength < UINT32_MAX, "Invariant");
|
||||
|
||||
switch (table->table().kind()) {
|
||||
switch (table.kind()) {
|
||||
case TableKind::FuncRef: {
|
||||
if (fillValue.isNull()) {
|
||||
#ifdef DEBUG
|
||||
for (uint32_t index = oldLength; index < oldLength + delta; index++) {
|
||||
MOZ_ASSERT(table->table().getFuncRef(index).code == nullptr);
|
||||
MOZ_ASSERT(table.getFuncRef(index).code == nullptr);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
|
@ -2290,8 +2249,7 @@ bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
|
|||
if (!CheckFuncRefValue(cx, fillValue, &fun)) {
|
||||
return false;
|
||||
}
|
||||
TableFunctionFill(cx, &table->table(), fun, oldLength,
|
||||
oldLength + delta);
|
||||
table.fillFuncRef(oldLength, delta, AnyRef::fromJSObject(fun), cx);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2301,13 +2259,11 @@ bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
|
|||
return false;
|
||||
}
|
||||
if (!tmp.get().isNull()) {
|
||||
for (uint32_t index = oldLength; index < oldLength + delta; index++) {
|
||||
table->table().setAnyRef(index, tmp);
|
||||
}
|
||||
table.fillAnyRef(oldLength, delta, tmp);
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
for (uint32_t index = oldLength; index < oldLength + delta; index++) {
|
||||
MOZ_ASSERT(table->table().getAnyRef(index).isNull());
|
||||
MOZ_ASSERT(table.getAnyRef(index).isNull());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1966,28 +1966,21 @@ inline bool OpIter<Policy>::readTableFill(uint32_t* tableIndex, Value* start,
|
|||
Value* val, Value* len) {
|
||||
MOZ_ASSERT(Classify(op_) == OpKind::TableFill);
|
||||
|
||||
if (!popWithType(ValType::I32, len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!popWithType(ValType::AnyRef, val)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!popWithType(ValType::I32, start)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readVarU32(tableIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*tableIndex >= env_.tables.length()) {
|
||||
return fail("table index out of range for table.fill");
|
||||
}
|
||||
|
||||
if (env_.tables[*tableIndex].kind != TableKind::AnyRef) {
|
||||
return fail("table.fill only on tables of anyref");
|
||||
if (!popWithType(ValType::I32, len)) {
|
||||
return false;
|
||||
}
|
||||
if (!popWithType(ToElemValType(env_.tables[*tableIndex].kind), val)) {
|
||||
return false;
|
||||
}
|
||||
if (!popWithType(ValType::I32, start)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1997,23 +1990,18 @@ template <typename Policy>
|
|||
inline bool OpIter<Policy>::readTableGet(uint32_t* tableIndex, Value* index) {
|
||||
MOZ_ASSERT(Classify(op_) == OpKind::TableGet);
|
||||
|
||||
if (!popWithType(ValType::I32, index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readVarU32(tableIndex)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*tableIndex >= env_.tables.length()) {
|
||||
return fail("table index out of range for table.get");
|
||||
}
|
||||
|
||||
if (env_.tables[*tableIndex].kind != TableKind::AnyRef) {
|
||||
return fail("table.get only on tables of anyref");
|
||||
if (!popWithType(ValType::I32, index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
infalliblePush(ValType::AnyRef);
|
||||
infalliblePush(ToElemValType(env_.tables[*tableIndex].kind));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2022,21 +2010,18 @@ inline bool OpIter<Policy>::readTableGrow(uint32_t* tableIndex,
|
|||
Value* initValue, Value* delta) {
|
||||
MOZ_ASSERT(Classify(op_) == OpKind::TableGrow);
|
||||
|
||||
if (!popWithType(ValType::I32, delta)) {
|
||||
return false;
|
||||
}
|
||||
if (!popWithType(ValType::AnyRef, initValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readVarU32(tableIndex)) {
|
||||
return false;
|
||||
}
|
||||
if (*tableIndex >= env_.tables.length()) {
|
||||
return fail("table index out of range for table.grow");
|
||||
}
|
||||
if (env_.tables[*tableIndex].kind != TableKind::AnyRef) {
|
||||
return fail("table.grow only on tables of anyref");
|
||||
|
||||
if (!popWithType(ValType::I32, delta)) {
|
||||
return false;
|
||||
}
|
||||
if (!popWithType(ToElemValType(env_.tables[*tableIndex].kind), initValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
infalliblePush(ValType::I32);
|
||||
|
@ -2048,21 +2033,18 @@ inline bool OpIter<Policy>::readTableSet(uint32_t* tableIndex, Value* index,
|
|||
Value* value) {
|
||||
MOZ_ASSERT(Classify(op_) == OpKind::TableSet);
|
||||
|
||||
if (!popWithType(ValType::AnyRef, value)) {
|
||||
return false;
|
||||
}
|
||||
if (!popWithType(ValType::I32, index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readVarU32(tableIndex)) {
|
||||
return false;
|
||||
}
|
||||
if (*tableIndex >= env_.tables.length()) {
|
||||
return fail("table index out of range for table.set");
|
||||
}
|
||||
if (env_.tables[*tableIndex].kind != TableKind::AnyRef) {
|
||||
return fail("table.set only on tables of anyref");
|
||||
|
||||
if (!popWithType(ToElemValType(env_.tables[*tableIndex].kind), value)) {
|
||||
return false;
|
||||
}
|
||||
if (!popWithType(ValType::I32, index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -139,12 +139,22 @@ const FunctionTableElem& Table::getFuncRef(uint32_t index) const {
|
|||
return functions_[index];
|
||||
}
|
||||
|
||||
AnyRef Table::getAnyRef(uint32_t index) const {
|
||||
MOZ_ASSERT(!isFunction());
|
||||
// TODO/AnyRef-boxing: With boxed immediates and strings, the write barrier
|
||||
// is going to have to be more complicated.
|
||||
ASSERT_ANYREF_IS_JSOBJECT;
|
||||
return AnyRef::fromJSObject(objects_[index]);
|
||||
bool Table::getFuncRef(JSContext* cx, uint32_t index,
|
||||
MutableHandleFunction fun) const {
|
||||
MOZ_ASSERT(isFunction());
|
||||
|
||||
const FunctionTableElem& elem = getFuncRef(index);
|
||||
if (!elem.code) {
|
||||
fun.set(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
Instance& instance = *elem.tls->instance;
|
||||
const CodeRange& codeRange = *instance.code().lookupFuncRange(elem.code);
|
||||
|
||||
RootedWasmInstanceObject instanceObj(cx, instance.object());
|
||||
return instanceObj->getExportedFunction(cx, instanceObj,
|
||||
codeRange.funcIndex(), fun);
|
||||
}
|
||||
|
||||
void Table::setFuncRef(uint32_t index, void* code, const Instance* instance) {
|
||||
|
@ -171,12 +181,57 @@ void Table::setFuncRef(uint32_t index, void* code, const Instance* instance) {
|
|||
}
|
||||
}
|
||||
|
||||
void Table::setAnyRef(uint32_t index, AnyRef new_obj) {
|
||||
void Table::fillFuncRef(uint32_t index, uint32_t fillCount, AnyRef ref,
|
||||
JSContext* cx) {
|
||||
MOZ_ASSERT(isFunction());
|
||||
|
||||
if (ref.isNull()) {
|
||||
for (uint32_t i = index, end = index + fillCount; i != end; i++) {
|
||||
setNull(i);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
RootedFunction fun(cx, &ref.asJSObject()->as<JSFunction>());
|
||||
MOZ_RELEASE_ASSERT(IsWasmExportedFunction(fun));
|
||||
|
||||
RootedWasmInstanceObject instanceObj(cx,
|
||||
ExportedFunctionToInstanceObject(fun));
|
||||
uint32_t funcIndex = ExportedFunctionToFuncIndex(fun);
|
||||
|
||||
#ifdef DEBUG
|
||||
RootedFunction f(cx);
|
||||
MOZ_ASSERT(instanceObj->getExportedFunction(cx, instanceObj, funcIndex, &f));
|
||||
MOZ_ASSERT(fun == f);
|
||||
#endif
|
||||
|
||||
Instance& instance = instanceObj->instance();
|
||||
Tier tier = instance.code().bestTier();
|
||||
const MetadataTier& metadata = instance.metadata(tier);
|
||||
const CodeRange& codeRange =
|
||||
metadata.codeRange(metadata.lookupFuncExport(funcIndex));
|
||||
void* code = instance.codeBase(tier) + codeRange.funcTableEntry();
|
||||
for (uint32_t i = index, end = index + fillCount; i != end; i++) {
|
||||
setFuncRef(i, code, &instance);
|
||||
}
|
||||
}
|
||||
|
||||
AnyRef Table::getAnyRef(uint32_t index) const {
|
||||
MOZ_ASSERT(!isFunction());
|
||||
// TODO/AnyRef-boxing: With boxed immediates and strings, the write barrier
|
||||
// is going to have to be more complicated.
|
||||
ASSERT_ANYREF_IS_JSOBJECT;
|
||||
objects_[index] = new_obj.asJSObject();
|
||||
return AnyRef::fromJSObject(objects_[index]);
|
||||
}
|
||||
|
||||
void Table::fillAnyRef(uint32_t index, uint32_t fillCount, AnyRef ref) {
|
||||
MOZ_ASSERT(!isFunction());
|
||||
// TODO/AnyRef-boxing: With boxed immediates and strings, the write barrier
|
||||
// is going to have to be more complicated.
|
||||
ASSERT_ANYREF_IS_JSOBJECT;
|
||||
for (uint32_t i = index, end = index + fillCount; i != end; i++) {
|
||||
objects_[i] = ref.asJSObject();
|
||||
}
|
||||
}
|
||||
|
||||
void Table::setNull(uint32_t index) {
|
||||
|
@ -192,7 +247,7 @@ void Table::setNull(uint32_t index) {
|
|||
break;
|
||||
}
|
||||
case TableKind::AnyRef: {
|
||||
setAnyRef(index, AnyRef::null());
|
||||
fillAnyRef(index, 1, AnyRef::null());
|
||||
break;
|
||||
}
|
||||
case TableKind::AsmJS: {
|
||||
|
@ -223,7 +278,7 @@ void Table::copy(const Table& srcTable, uint32_t dstIndex, uint32_t srcIndex) {
|
|||
break;
|
||||
}
|
||||
case TableKind::AnyRef: {
|
||||
setAnyRef(dstIndex, srcTable.getAnyRef(srcIndex));
|
||||
fillAnyRef(dstIndex, 1, srcTable.getAnyRef(srcIndex));
|
||||
break;
|
||||
}
|
||||
case TableKind::AsmJS: {
|
||||
|
|
|
@ -80,14 +80,19 @@ class Table : public ShareableBase<Table> {
|
|||
// Only for function values. Raw pointer to the table.
|
||||
uint8_t* functionBase() const;
|
||||
|
||||
// get/setFuncRef is allowed only on table-of-funcref.
|
||||
// get/setAnyRef is allowed only on table-of-anyref.
|
||||
// set/get/fillFuncRef is allowed only on table-of-funcref.
|
||||
// get/fillAnyRef is allowed only on table-of-anyref.
|
||||
// setNull is allowed on either.
|
||||
|
||||
const FunctionTableElem& getFuncRef(uint32_t index) const;
|
||||
bool getFuncRef(JSContext* cx, uint32_t index,
|
||||
MutableHandleFunction fun) const;
|
||||
void setFuncRef(uint32_t index, void* code, const Instance* instance);
|
||||
void fillFuncRef(uint32_t index, uint32_t fillCount, AnyRef ref,
|
||||
JSContext* cx);
|
||||
|
||||
AnyRef getAnyRef(uint32_t index) const;
|
||||
void setAnyRef(uint32_t index, AnyRef);
|
||||
void fillAnyRef(uint32_t index, uint32_t fillCount, AnyRef ref);
|
||||
|
||||
void setNull(uint32_t index);
|
||||
|
||||
|
|
|
@ -1980,6 +1980,18 @@ struct Limits {
|
|||
|
||||
enum class TableKind { AnyRef, FuncRef, AsmJS };
|
||||
|
||||
static inline ValType ToElemValType(TableKind tk) {
|
||||
switch (tk) {
|
||||
case TableKind::AnyRef:
|
||||
return ValType::AnyRef;
|
||||
case TableKind::FuncRef:
|
||||
return ValType::FuncRef;
|
||||
case TableKind::AsmJS:
|
||||
break;
|
||||
}
|
||||
MOZ_CRASH("not used for asm.js");
|
||||
}
|
||||
|
||||
struct TableDesc {
|
||||
TableKind kind;
|
||||
bool importedOrExported;
|
||||
|
|
|
@ -118,6 +118,75 @@ static int x_error_handler(Display*, XErrorEvent* ev) {
|
|||
// care about leaking memory
|
||||
extern "C" {
|
||||
|
||||
static int get_egl_status(char* buf, int bufsize) {
|
||||
void* libegl = dlopen("libEGL.so.1", RTLD_LAZY);
|
||||
if (!libegl) {
|
||||
libegl = dlopen("libEGL.so", RTLD_LAZY);
|
||||
}
|
||||
if (!libegl) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef void* EGLDisplay;
|
||||
typedef int EGLBoolean;
|
||||
typedef int EGLint;
|
||||
|
||||
typedef void* (*PFNEGLGETPROCADDRESS)(const char*);
|
||||
PFNEGLGETPROCADDRESS eglGetProcAddress =
|
||||
cast<PFNEGLGETPROCADDRESS>(dlsym(libegl, "eglGetProcAddress"));
|
||||
|
||||
if (!eglGetProcAddress) {
|
||||
dlclose(libegl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef EGLDisplay (*PFNEGLGETDISPLAYPROC)(void* native_display);
|
||||
PFNEGLGETDISPLAYPROC eglGetDisplay =
|
||||
cast<PFNEGLGETDISPLAYPROC>(eglGetProcAddress("eglGetDisplay"));
|
||||
|
||||
typedef EGLBoolean (*PFNEGLINITIALIZEPROC)(EGLDisplay dpy, EGLint * major,
|
||||
EGLint * minor);
|
||||
PFNEGLINITIALIZEPROC eglInitialize =
|
||||
cast<PFNEGLINITIALIZEPROC>(eglGetProcAddress("eglInitialize"));
|
||||
|
||||
typedef EGLBoolean (*PFNEGLTERMINATEPROC)(EGLDisplay dpy);
|
||||
PFNEGLTERMINATEPROC eglTerminate =
|
||||
cast<PFNEGLTERMINATEPROC>(eglGetProcAddress("eglTerminate"));
|
||||
|
||||
typedef const char* (*PFNEGLGETDISPLAYDRIVERNAMEPROC)(EGLDisplay dpy);
|
||||
PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName =
|
||||
cast<PFNEGLGETDISPLAYDRIVERNAMEPROC>(
|
||||
eglGetProcAddress("eglGetDisplayDriverName"));
|
||||
|
||||
if (!eglGetDisplay || !eglInitialize || !eglTerminate ||
|
||||
!eglGetDisplayDriverName) {
|
||||
dlclose(libegl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EGLDisplay dpy = eglGetDisplay(nullptr);
|
||||
if (!dpy) {
|
||||
dlclose(libegl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EGLint major, minor;
|
||||
if (!eglInitialize(dpy, &major, &minor)) {
|
||||
dlclose(libegl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int length = 0;
|
||||
const char* driDriver = eglGetDisplayDriverName(dpy);
|
||||
if (driDriver) {
|
||||
length = snprintf(buf, bufsize, "DRI_DRIVER\n%s\n", driDriver);
|
||||
}
|
||||
|
||||
eglTerminate(dpy);
|
||||
dlclose(libegl);
|
||||
return length;
|
||||
}
|
||||
|
||||
void glxtest() {
|
||||
// we want to redirect to /dev/null stdout, stderr, and while we're at it,
|
||||
// any PR logging file descriptors. To that effect, we redirect all positive
|
||||
|
@ -255,7 +324,7 @@ void glxtest() {
|
|||
vendorId &= 0xFFFF;
|
||||
deviceId &= 0xFFFF;
|
||||
|
||||
length += snprintf(buf + length, bufsize,
|
||||
length += snprintf(buf + length, bufsize - length,
|
||||
"MESA_VENDOR_ID\n0x%04x\n"
|
||||
"MESA_DEVICE_ID\n0x%04x\n"
|
||||
"MESA_ACCELERATED\n%s\n"
|
||||
|
@ -268,13 +337,15 @@ void glxtest() {
|
|||
}
|
||||
|
||||
// From Mesa's GL/internal/dri_interface.h, to be used by DRI clients.
|
||||
int gotDriDriver = 0;
|
||||
typedef const char* (*PFNGLXGETSCREENDRIVERPROC)(Display * dpy, int scrNum);
|
||||
PFNGLXGETSCREENDRIVERPROC glXGetScreenDriverProc =
|
||||
cast<PFNGLXGETSCREENDRIVERPROC>(glXGetProcAddress("glXGetScreenDriver"));
|
||||
if (glXGetScreenDriverProc) {
|
||||
const char* driDriver = glXGetScreenDriverProc(dpy, DefaultScreen(dpy));
|
||||
if (driDriver) {
|
||||
length += snprintf(buf + length, bufsize, "DRI_DRIVER\n%s\n", driDriver);
|
||||
gotDriDriver = 1;
|
||||
length += snprintf(buf + length, bufsize - length, "DRI_DRIVER\n%s\n", driDriver);
|
||||
if (length >= bufsize)
|
||||
fatal_error("GL strings length too large for buffer size");
|
||||
}
|
||||
|
@ -304,6 +375,15 @@ void glxtest() {
|
|||
|
||||
dlclose(libgl);
|
||||
|
||||
// If we failed to get the driver name from X, try via EGL_MESA_query_driver.
|
||||
// We are probably using Wayland.
|
||||
if (!gotDriDriver) {
|
||||
length += get_egl_status(buf + length, bufsize - length);
|
||||
if (length >= bufsize) {
|
||||
fatal_error("GL strings length too large for buffer size");
|
||||
}
|
||||
}
|
||||
|
||||
///// Finally write data to the pipe
|
||||
mozilla::Unused << write(write_end_of_the_pipe, buf, length);
|
||||
}
|
||||
|
|
|
@ -274,9 +274,11 @@ void GfxInfo::GetData() {
|
|||
}
|
||||
} else if (glVendor.EqualsLiteral("NVIDIA Corporation")) {
|
||||
CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(VendorNVIDIA), mVendorId);
|
||||
mDriverVendor.AssignLiteral("nvidia/unknown");
|
||||
// TODO: Use NV-CONTROL X11 extension to query Device ID and VRAM.
|
||||
} else if (glVendor.EqualsLiteral("ATI Technologies Inc.")) {
|
||||
CopyUTF16toUTF8(GfxDriverInfo::GetDeviceVendor(VendorATI), mVendorId);
|
||||
mDriverVendor.AssignLiteral("ati/unknown");
|
||||
// TODO: Look into ways to find the device ID on FGLRX.
|
||||
} else {
|
||||
NS_WARNING("Failed to detect GL vendor!");
|
||||
|
|
Загрузка…
Ссылка в новой задаче