зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1784499 - Add WebAssembly.Table initiazer for non-nullable type. r=rhunt
Differential Revision: https://phabricator.services.mozilla.com/D162772
This commit is contained in:
Родитель
57664750c6
Коммит
ee4bdd5b2e
|
@ -0,0 +1,79 @@
|
|||
// |jit-test| skip-if: !wasmFunctionReferencesEnabled()
|
||||
|
||||
// non-null table initialization
|
||||
var { get1, get2, get3, get4 } = wasmEvalText(`(module
|
||||
(type $dummy (func))
|
||||
(func $dummy)
|
||||
|
||||
(table $t1 10 funcref)
|
||||
(table $t2 10 funcref (ref.func $dummy))
|
||||
(table $t3 10 (ref $dummy) (ref.func $dummy))
|
||||
(table $t4 10 (ref func) (ref.func $dummy))
|
||||
|
||||
(func (export "get1") (result funcref) (table.get $t1 (i32.const 1)))
|
||||
(func (export "get2") (result funcref) (table.get $t2 (i32.const 4)))
|
||||
(func (export "get3") (result funcref) (table.get $t3 (i32.const 7)))
|
||||
(func (export "get4") (result funcref) (table.get $t4 (i32.const 5)))
|
||||
)`).exports;
|
||||
assertEq(get1(), null);
|
||||
assertEq(get2() != null, true);
|
||||
assertEq(get3() != null, true);
|
||||
assertEq(get4() != null, true);
|
||||
|
||||
const sampleWasmFunction = get2();
|
||||
sampleWasmFunction();
|
||||
|
||||
// Invalid initializers
|
||||
for (let i of [
|
||||
`(table $t1 10 (ref $dummy) (ref.func $dummy1))`,
|
||||
`(table $t2 5 10 (ref func))`,
|
||||
`(table $t3 10 10 (ref func) (ref.null $dummy1))`,
|
||||
`(table $t4 10 (ref $dummy))`,
|
||||
'(table $t5 1 (ref $dummy) (ref.null $dummy))',
|
||||
]) {
|
||||
wasmFailValidateText(`(module
|
||||
(type $dummy (func))
|
||||
(type $dummy1 (func (param i32)))
|
||||
(func $dummy1 (param i32))
|
||||
|
||||
${i}
|
||||
)`, /(type mismatch|table with non-nullable references requires initializer)/);
|
||||
}
|
||||
|
||||
var t1 = new WebAssembly.Table({initial: 10, element: {ref: 'func', nullable: false }}, sampleWasmFunction);
|
||||
assertEq(t1.get(2) != null, true);
|
||||
assertThrows(() => {
|
||||
new WebAssembly.Table({initial: 10, element: {ref: 'func', nullable: false }});
|
||||
});
|
||||
assertThrows(() => {
|
||||
new WebAssembly.Table({initial: 10, element: {ref: 'func', nullable: false }}, null);
|
||||
});
|
||||
|
||||
var t2 = new WebAssembly.Table({initial: 6, maximum: 20, element: {ref: 'extern', nullable: false }}, {foo: "bar"});
|
||||
assertEq(t2.get(1).foo, "bar");
|
||||
assertThrows(() => { t2.get(7) });
|
||||
assertThrows(() => { t2.grow(9, null) });
|
||||
t2.grow(8, {t: "test"});
|
||||
assertEq(t2.get(3).foo, "bar");
|
||||
assertEq(t2.get(7).t, "test");
|
||||
assertThrows(() => {
|
||||
new WebAssembly.Table({initial: 10, element: {ref: 'extern', nullable: false }}, null);
|
||||
});
|
||||
|
||||
// Fail because tables come before globals in the binary format, so tables
|
||||
// cannot refer to globals.
|
||||
wasmFailValidateText(`(module
|
||||
(global $g1 externref)
|
||||
(table 10 externref (global.get $g1))
|
||||
)`, /global.get index out of range/);
|
||||
|
||||
function assertThrows(f) {
|
||||
var ok = false;
|
||||
try {
|
||||
f();
|
||||
} catch (exc) {
|
||||
ok = true;
|
||||
}
|
||||
if (!ok)
|
||||
throw new TypeError("Assertion failed: " + f + " did not throw as expected");
|
||||
}
|
|
@ -159,10 +159,10 @@ assertErrorMessage(() => runMultiNullStack(), TypeError, /cannot pass null to no
|
|||
)`);
|
||||
}
|
||||
|
||||
// cannot have non-nullable tables
|
||||
// cannot have non-nullable tables without initializer
|
||||
wasmFailValidateText(`(module
|
||||
(table (ref extern) (elem))
|
||||
)`, /non-nullable references not supported in tables/);
|
||||
)`, /table with non-nullable references requires initializer/);
|
||||
|
||||
// Testing internal wasmLosslessInvoke to pass non-nullable as params and arguments.
|
||||
let {t} = wasmEvalText(`(module
|
||||
|
|
|
@ -20,7 +20,14 @@ assertErrorMessage(
|
|||
// RefType/ValueType can be specified as an {ref: 'func', ...} object
|
||||
const t11 = new WebAssembly.Table({element: {ref: 'func', nullable: true}, initial: 3});
|
||||
const t12 = new WebAssembly.Table({element: {ref: 'extern', nullable: true}, initial: 3});
|
||||
// TODO new WebAssembly.Table({element: {ref: 'func', nullable: false}, initial: 3}),
|
||||
const t13 = new WebAssembly.Table({element: {ref: 'extern', nullable: false}, initial: 3}, {});
|
||||
|
||||
assertErrorMessage(
|
||||
() => new WebAssembly.Table({element: {ref: 'func', nullable: false}, initial: 1}, null),
|
||||
TypeError, /cannot pass null to non-nullable WebAssembly reference/);
|
||||
assertErrorMessage(
|
||||
() => new WebAssembly.Table({element: {ref: 'extern', nullable: false}, initial: 1}, null),
|
||||
TypeError, /cannot pass null to non-nullable WebAssembly reference/);
|
||||
|
||||
assertErrorMessage(
|
||||
() => new WebAssembly.Table({element: {ref: 'bar', nullable: true}, initial: 1}),
|
||||
|
@ -28,8 +35,17 @@ assertErrorMessage(
|
|||
|
||||
const g11 = new WebAssembly.Global({value: {ref: 'func', nullable: true}, mutable: true});
|
||||
const g12 = new WebAssembly.Global({value: {ref: 'extern', nullable: true}, mutable: true});
|
||||
// TODO new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true});
|
||||
const g13 = new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}, {});
|
||||
const g14 = new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true});
|
||||
const g15 = new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}, void 0);
|
||||
|
||||
assertErrorMessage(
|
||||
() => new WebAssembly.Global({value: {ref: 'func', nullable: false}, mutable: true}),
|
||||
TypeError, /cannot pass null to non-nullable WebAssembly reference/);
|
||||
assertErrorMessage(
|
||||
() => new WebAssembly.Global({value: {ref: 'extern', nullable: false}, mutable: true}, null),
|
||||
TypeError, /cannot pass null to non-nullable WebAssembly reference/);
|
||||
|
||||
assertErrorMessage(
|
||||
() => new WebAssembly.Global({value: {ref: 'bar', nullable: true}, mutable: true}),
|
||||
TypeError, /bad value type/);
|
||||
|
|
|
@ -10,7 +10,7 @@ assertEq(new WebAssembly.Global({value: "externref"}) instanceof WebAssembly.Glo
|
|||
(function() {
|
||||
// Test initialization without a value.
|
||||
let g = new WebAssembly.Global({value: "externref"});
|
||||
assertEq(g.value, null);
|
||||
assertEq(g.value, void 0);
|
||||
assertErrorMessage(() => g.value = 42, TypeError, /immutable global/);
|
||||
})();
|
||||
|
||||
|
|
|
@ -2053,6 +2053,7 @@ class MOZ_STACK_CLASS ModuleValidator : public ModuleValidatorShared {
|
|||
|
||||
moduleEnv_.asmJSSigToTableIndex[sigIndex] = moduleEnv_.tables.length();
|
||||
if (!moduleEnv_.tables.emplaceBack(RefType::func(), mask + 1, Nothing(),
|
||||
/* initExpr */ Nothing(),
|
||||
/*isAsmJS*/ true)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -99,6 +99,9 @@ enum class TypeCode {
|
|||
// Type constructor for array types - gc proposal
|
||||
Array = 0x5e, // SLEB128(-0x22)
|
||||
|
||||
// Value for non-nullable type present.
|
||||
TableHasInitExpr = 0x40,
|
||||
|
||||
// The 'empty' case of blocktype.
|
||||
BlockVoid = 0x40, // SLEB128(-0x40)
|
||||
|
||||
|
|
|
@ -1029,18 +1029,14 @@ static int32_t MemDiscardShared(Instance* instance, I byteOffset, I byteLen,
|
|||
uint32_t oldSize = table.grow(delta);
|
||||
|
||||
if (oldSize != uint32_t(-1) && initValue != nullptr) {
|
||||
switch (table.repr()) {
|
||||
case TableRepr::Ref:
|
||||
table.fillAnyRef(oldSize, delta, ref);
|
||||
break;
|
||||
case TableRepr::Func:
|
||||
MOZ_RELEASE_ASSERT(!table.isAsmJS());
|
||||
table.fillFuncRef(oldSize, delta, FuncRef::fromAnyRefUnchecked(ref),
|
||||
cx);
|
||||
break;
|
||||
}
|
||||
table.fillUninitialized(oldSize, delta, ref, cx);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (!table.elemType().isNullable()) {
|
||||
table.assertRangeNotNull(oldSize, delta);
|
||||
}
|
||||
#endif // DEBUG
|
||||
return oldSize;
|
||||
}
|
||||
|
||||
|
@ -1707,8 +1703,29 @@ bool Instance::init(JSContext* cx, const JSObjectVector& funcImports,
|
|||
TableInstanceData& table = tableInstanceData(td);
|
||||
table.length = tables_[i]->length();
|
||||
table.elements = tables_[i]->instanceElements();
|
||||
// Non-imported tables, with init_expr, has to be initialized with
|
||||
// the evaluated value.
|
||||
if (!td.isImported && td.initExpr) {
|
||||
Rooted<WasmInstanceObject*> instanceObj(cx, object());
|
||||
RootedVal val(cx);
|
||||
if (!td.initExpr->evaluate(cx, instanceObj, &val)) {
|
||||
return false;
|
||||
}
|
||||
RootedAnyRef ref(cx, val.get().ref());
|
||||
tables_[i]->fillUninitialized(0, tables_[i]->length(), ref, cx);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// All (linked) tables with non-nullable types must be initialized.
|
||||
for (size_t i = 0; i < tables_.length(); i++) {
|
||||
const TableDesc& td = metadata().tables[i];
|
||||
if (!td.elemType.isNullable()) {
|
||||
tables_[i]->assertRangeNotNull(0, tables_[i]->length());
|
||||
}
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
// Initialize tags in the instance data
|
||||
for (size_t i = 0; i < metadata().tags.length(); i++) {
|
||||
const TagDesc& td = metadata().tags[i];
|
||||
|
|
|
@ -3044,10 +3044,18 @@ void WasmTableObject::trace(JSTracer* trc, JSObject* obj) {
|
|||
//
|
||||
// [1]
|
||||
// https://webassembly.github.io/reference-types/js-api/index.html#defaultvalue
|
||||
static Value TableDefaultValue(wasm::RefType tableType) {
|
||||
static Value RefTypeDefautValue(wasm::RefType tableType) {
|
||||
return tableType.isExtern() ? UndefinedValue() : NullValue();
|
||||
}
|
||||
|
||||
static bool CheckRefTypeValue(JSContext* cx, wasm::RefType type,
|
||||
HandleValue value) {
|
||||
RootedFunction fun(cx);
|
||||
RootedAnyRef any(cx, AnyRef::null());
|
||||
|
||||
return CheckRefType(cx, type, value, &fun, &any);
|
||||
}
|
||||
|
||||
/* static */
|
||||
WasmTableObject* WasmTableObject::create(JSContext* cx, uint32_t initialLength,
|
||||
Maybe<uint32_t> maximumLength,
|
||||
|
@ -3062,8 +3070,9 @@ WasmTableObject* WasmTableObject::create(JSContext* cx, uint32_t initialLength,
|
|||
|
||||
MOZ_ASSERT(obj->isNewborn());
|
||||
|
||||
TableDesc td(tableType, initialLength, maximumLength, /*isAsmJS*/ false,
|
||||
/*isImportedOrExported=*/true);
|
||||
TableDesc td(tableType, initialLength, maximumLength, Nothing(),
|
||||
/*isAsmJS*/ false,
|
||||
/*isImported=*/true, /*isExported=*/true);
|
||||
|
||||
SharedTable table = Table::create(cx, td, obj);
|
||||
if (!table) {
|
||||
|
@ -3153,7 +3162,10 @@ bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) {
|
|||
|
||||
// Initialize the table to a default value
|
||||
RootedValue initValue(
|
||||
cx, args.length() < 2 ? TableDefaultValue(tableType) : args[1]);
|
||||
cx, args.length() < 2 ? RefTypeDefautValue(tableType) : args[1]);
|
||||
if (!CheckRefTypeValue(cx, tableType, initValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip initializing the table if the fill value is null, as that is the
|
||||
// default value.
|
||||
|
@ -3164,7 +3176,10 @@ bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) {
|
|||
#ifdef DEBUG
|
||||
// Assert that null is the default value of a new table.
|
||||
if (initValue.isNull()) {
|
||||
table->assertRangeNull(0, initialLength);
|
||||
table->table().assertRangeNull(0, initialLength);
|
||||
}
|
||||
if (!tableType.isNullable()) {
|
||||
table->table().assertRangeNotNull(0, initialLength);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -3269,7 +3284,7 @@ bool WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) {
|
|||
}
|
||||
|
||||
RootedValue fillValue(
|
||||
cx, args.length() < 2 ? TableDefaultValue(table.elemType()) : args[1]);
|
||||
cx, args.length() < 2 ? RefTypeDefautValue(table.elemType()) : args[1]);
|
||||
if (!tableObj->fillRange(cx, index, 1, fillValue)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -3299,6 +3314,12 @@ bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
|
|||
return false;
|
||||
}
|
||||
|
||||
RootedValue fillValue(
|
||||
cx, args.length() < 2 ? RefTypeDefautValue(table.elemType()) : args[1]);
|
||||
if (!CheckRefTypeValue(cx, table.elemType(), fillValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t oldLength = table.grow(delta);
|
||||
|
||||
if (oldLength == uint32_t(-1)) {
|
||||
|
@ -3307,10 +3328,6 @@ bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Fill the grown range of the table
|
||||
RootedValue fillValue(
|
||||
cx, args.length() < 2 ? TableDefaultValue(table.elemType()) : args[1]);
|
||||
|
||||
// Skip filling the grown range of the table if the fill value is null, as
|
||||
// that is the default value.
|
||||
if (!fillValue.isNull() &&
|
||||
|
@ -3320,7 +3337,10 @@ bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
|
|||
#ifdef DEBUG
|
||||
// Assert that null is the default value of the grown range.
|
||||
if (fillValue.isNull()) {
|
||||
tableObj->assertRangeNull(oldLength, delta);
|
||||
table.assertRangeNull(oldLength, delta);
|
||||
}
|
||||
if (!table.elemType().isNullable()) {
|
||||
table.assertRangeNotNull(oldLength, delta);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -3373,25 +3393,6 @@ bool WasmTableObject::fillRange(JSContext* cx, uint32_t index, uint32_t length,
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void WasmTableObject::assertRangeNull(uint32_t index, uint32_t length) const {
|
||||
Table& tab = table();
|
||||
switch (tab.repr()) {
|
||||
case TableRepr::Func:
|
||||
for (uint32_t i = index; i < index + length; i++) {
|
||||
MOZ_ASSERT(tab.getFuncRef(i).instance == nullptr);
|
||||
MOZ_ASSERT(tab.getFuncRef(i).code == nullptr);
|
||||
}
|
||||
break;
|
||||
case TableRepr::Ref:
|
||||
for (uint32_t i = index; i < index + length; i++) {
|
||||
MOZ_ASSERT(tab.getAnyRef(i).isNull());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// WebAssembly.global class and methods
|
||||
|
||||
|
@ -3527,12 +3528,19 @@ bool WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp) {
|
|||
RootedVal globalVal(cx, globalType);
|
||||
|
||||
// Override with non-undefined value, if provided.
|
||||
RootedValue valueVal(cx, args.get(1));
|
||||
if (!valueVal.isUndefined() ||
|
||||
(args.length() >= 2 && globalType.isRefType())) {
|
||||
RootedValue valueVal(cx);
|
||||
if (globalType.isRefType()) {
|
||||
valueVal.set(args.length() < 2 ? RefTypeDefautValue(globalType.refType())
|
||||
: args[1]);
|
||||
if (!Val::fromJSValue(cx, globalType, valueVal, &globalVal)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
valueVal.set(args.get(1));
|
||||
if (!valueVal.isUndefined() &&
|
||||
!Val::fromJSValue(cx, globalType, valueVal, &globalVal)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
RootedObject proto(cx,
|
||||
|
|
|
@ -481,9 +481,6 @@ class WasmTableObject : public NativeObject {
|
|||
// the range is within bounds. Returns false if the coercion failed.
|
||||
bool fillRange(JSContext* cx, uint32_t index, uint32_t length,
|
||||
HandleValue value) const;
|
||||
#ifdef DEBUG
|
||||
void assertRangeNull(uint32_t index, uint32_t length) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
// The class of WebAssembly.Tag. This class is used to track exception tag
|
||||
|
|
|
@ -749,7 +749,7 @@ bool Module::instantiateLocalTable(JSContext* cx, const TableDesc& td,
|
|||
|
||||
SharedTable table;
|
||||
Rooted<WasmTableObject*> tableObj(cx);
|
||||
if (td.isImportedOrExported) {
|
||||
if (td.isExported) {
|
||||
RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTable));
|
||||
tableObj.set(WasmTableObject::create(cx, td.initialLength, td.maximumLength,
|
||||
td.elemType, proto));
|
||||
|
|
|
@ -608,22 +608,26 @@ static_assert(MaxMemory32LimitField <= UINT64_MAX / PageSize);
|
|||
|
||||
struct TableDesc {
|
||||
RefType elemType;
|
||||
bool isImportedOrExported;
|
||||
bool isImported;
|
||||
bool isExported;
|
||||
bool isAsmJS;
|
||||
uint32_t globalDataOffset;
|
||||
uint32_t initialLength;
|
||||
Maybe<uint32_t> maximumLength;
|
||||
Maybe<InitExpr> initExpr;
|
||||
|
||||
TableDesc() = default;
|
||||
TableDesc(RefType elemType, uint32_t initialLength,
|
||||
Maybe<uint32_t> maximumLength, bool isAsmJS,
|
||||
bool isImportedOrExported = false)
|
||||
Maybe<uint32_t> maximumLength, Maybe<InitExpr>&& initExpr,
|
||||
bool isAsmJS, bool isImported = false, bool isExported = false)
|
||||
: elemType(elemType),
|
||||
isImportedOrExported(isImportedOrExported),
|
||||
isImported(isImported),
|
||||
isExported(isExported),
|
||||
isAsmJS(isAsmJS),
|
||||
globalDataOffset(UINT32_MAX),
|
||||
initialLength(initialLength),
|
||||
maximumLength(maximumLength) {}
|
||||
maximumLength(maximumLength),
|
||||
initExpr(std::move(initExpr)) {}
|
||||
};
|
||||
|
||||
using TableDescVector = Vector<TableDesc, 0, SystemAllocPolicy>;
|
||||
|
|
|
@ -720,13 +720,16 @@ CoderResult CodeCustomSection(Coder<mode>& coder,
|
|||
|
||||
template <CoderMode mode>
|
||||
CoderResult CodeTableDesc(Coder<mode>& coder, CoderArg<mode, TableDesc> item) {
|
||||
WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TableDesc, 32);
|
||||
WASM_VERIFY_SERIALIZATION_FOR_SIZE(wasm::TableDesc, 120);
|
||||
MOZ_TRY(CodeRefType(coder, &item->elemType));
|
||||
MOZ_TRY(CodePod(coder, &item->isImportedOrExported));
|
||||
MOZ_TRY(CodePod(coder, &item->isImported));
|
||||
MOZ_TRY(CodePod(coder, &item->isExported));
|
||||
MOZ_TRY(CodePod(coder, &item->isAsmJS));
|
||||
MOZ_TRY(CodePod(coder, &item->globalDataOffset));
|
||||
MOZ_TRY(CodePod(coder, &item->initialLength));
|
||||
MOZ_TRY(CodePod(coder, &item->maximumLength));
|
||||
MOZ_TRY(
|
||||
(CodeMaybe<mode, InitExpr, &CodeInitExpr<mode>>(coder, &item->initExpr)));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
|
|
@ -60,8 +60,8 @@ Table::Table(JSContext* cx, const TableDesc& desc,
|
|||
/* static */
|
||||
SharedTable Table::create(JSContext* cx, const TableDesc& desc,
|
||||
Handle<WasmTableObject*> maybeObject) {
|
||||
// We don't support non-nullable references in tables yet.
|
||||
MOZ_RELEASE_ASSERT(desc.elemType.isNullable());
|
||||
// Tables are initialized with init_expr values at Instance::init or
|
||||
// WasmTableObject::create.
|
||||
|
||||
switch (desc.elemType.tableRepr()) {
|
||||
case TableRepr::Func: {
|
||||
|
@ -330,8 +330,6 @@ bool Table::copy(JSContext* cx, const Table& srcTable, uint32_t dstIndex,
|
|||
}
|
||||
|
||||
uint32_t Table::grow(uint32_t delta) {
|
||||
MOZ_RELEASE_ASSERT(elemType_.isNullable());
|
||||
|
||||
// This isn't just an optimization: movingGrowable() assumes that
|
||||
// onMovingGrowTable does not fire when length == maximum.
|
||||
if (!delta) {
|
||||
|
@ -403,6 +401,58 @@ bool Table::addMovingGrowObserver(JSContext* cx, WasmInstanceObject* instance) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void Table::fillUninitialized(uint32_t index, uint32_t fillCount,
|
||||
HandleAnyRef ref, JSContext* cx) {
|
||||
#ifdef DEBUG
|
||||
assertRangeNull(index, fillCount);
|
||||
#endif // DEBUG
|
||||
switch (repr()) {
|
||||
case TableRepr::Func: {
|
||||
MOZ_RELEASE_ASSERT(!isAsmJS_);
|
||||
fillFuncRef(index, fillCount, FuncRef::fromAnyRefUnchecked(ref), cx);
|
||||
break;
|
||||
}
|
||||
case TableRepr::Ref: {
|
||||
fillAnyRef(index, fillCount, ref);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void Table::assertRangeNull(uint32_t index, uint32_t length) const {
|
||||
switch (repr()) {
|
||||
case TableRepr::Func:
|
||||
for (uint32_t i = index; i < index + length; i++) {
|
||||
MOZ_ASSERT(getFuncRef(i).instance == nullptr);
|
||||
MOZ_ASSERT(getFuncRef(i).code == nullptr);
|
||||
}
|
||||
break;
|
||||
case TableRepr::Ref:
|
||||
for (uint32_t i = index; i < index + length; i++) {
|
||||
MOZ_ASSERT(getAnyRef(i).isNull());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Table::assertRangeNotNull(uint32_t index, uint32_t length) const {
|
||||
switch (repr()) {
|
||||
case TableRepr::Func:
|
||||
for (uint32_t i = index; i < index + length; i++) {
|
||||
MOZ_ASSERT_IF(!isAsmJS_, getFuncRef(i).instance != nullptr);
|
||||
MOZ_ASSERT(getFuncRef(i).code != nullptr);
|
||||
}
|
||||
break;
|
||||
case TableRepr::Ref:
|
||||
for (uint32_t i = index; i < index + length; i++) {
|
||||
MOZ_ASSERT(!getAnyRef(i).isNull());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
size_t Table::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const {
|
||||
if (isFunction()) {
|
||||
return functions_.sizeOfExcludingThis(mallocSizeOf);
|
||||
|
|
|
@ -116,6 +116,13 @@ class Table : public ShareableBase<Table> {
|
|||
[[nodiscard]] bool addMovingGrowObserver(JSContext* cx,
|
||||
WasmInstanceObject* instance);
|
||||
|
||||
void fillUninitialized(uint32_t index, uint32_t fillCount, HandleAnyRef ref,
|
||||
JSContext* cx);
|
||||
#ifdef DEBUG
|
||||
void assertRangeNull(uint32_t index, uint32_t length) const;
|
||||
void assertRangeNotNull(uint32_t index, uint32_t length) const;
|
||||
#endif // DEBUG
|
||||
|
||||
// about:memory reporting:
|
||||
|
||||
size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const;
|
||||
|
|
|
@ -1877,15 +1877,24 @@ static bool DecodeLimits(Decoder& d, LimitsKind kind, Limits* limits) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool DecodeTableTypeAndLimits(Decoder& d, const FeatureArgs& features,
|
||||
const SharedTypeContext& types,
|
||||
TableDescVector* tables) {
|
||||
RefType tableElemType;
|
||||
if (!d.readRefType(*types, features, &tableElemType)) {
|
||||
return false;
|
||||
static bool DecodeTableTypeAndLimits(Decoder& d, ModuleEnvironment* env) {
|
||||
bool initExprPresent = false;
|
||||
uint8_t typeCode;
|
||||
if (!d.peekByte(&typeCode)) {
|
||||
return d.fail("expected type code");
|
||||
}
|
||||
if (!tableElemType.isNullable()) {
|
||||
return d.fail("non-nullable references not supported in tables");
|
||||
if (typeCode == (uint8_t)TypeCode::TableHasInitExpr) {
|
||||
d.uncheckedReadFixedU8();
|
||||
uint8_t flags;
|
||||
if (!d.readFixedU8(&flags) || flags != 0) {
|
||||
return d.fail("expected reserved byte to be 0");
|
||||
}
|
||||
initExprPresent = true;
|
||||
}
|
||||
|
||||
RefType tableElemType;
|
||||
if (!d.readRefType(*env->types, env->features, &tableElemType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Limits limits;
|
||||
|
@ -1905,7 +1914,7 @@ static bool DecodeTableTypeAndLimits(Decoder& d, const FeatureArgs& features,
|
|||
return d.fail("too many table elements");
|
||||
}
|
||||
|
||||
if (tables->length() >= MaxTables) {
|
||||
if (env->tables.length() >= MaxTables) {
|
||||
return d.fail("too many tables");
|
||||
}
|
||||
|
||||
|
@ -1917,8 +1926,22 @@ static bool DecodeTableTypeAndLimits(Decoder& d, const FeatureArgs& features,
|
|||
maximumLength = Some(uint32_t(*limits.maximum));
|
||||
}
|
||||
|
||||
return tables->emplaceBack(tableElemType, initialLength, maximumLength,
|
||||
/* isAsmJS */ false);
|
||||
Maybe<InitExpr> initExpr;
|
||||
if (initExprPresent) {
|
||||
InitExpr initializer;
|
||||
if (!InitExpr::decodeAndValidate(d, env, tableElemType,
|
||||
env->globals.length(), &initializer)) {
|
||||
return false;
|
||||
}
|
||||
initExpr = Some(std::move(initializer));
|
||||
} else {
|
||||
if (!tableElemType.isNullable()) {
|
||||
return d.fail("table with non-nullable references requires initializer");
|
||||
}
|
||||
}
|
||||
|
||||
return env->tables.emplaceBack(tableElemType, initialLength, maximumLength,
|
||||
std::move(initExpr), /* isAsmJS */ false);
|
||||
}
|
||||
|
||||
static bool DecodeGlobalType(Decoder& d, const SharedTypeContext& types,
|
||||
|
@ -2035,11 +2058,10 @@ static bool DecodeImport(Decoder& d, ModuleEnvironment* env) {
|
|||
break;
|
||||
}
|
||||
case DefinitionKind::Table: {
|
||||
if (!DecodeTableTypeAndLimits(d, env->features, env->types,
|
||||
&env->tables)) {
|
||||
if (!DecodeTableTypeAndLimits(d, env)) {
|
||||
return false;
|
||||
}
|
||||
env->tables.back().isImportedOrExported = true;
|
||||
env->tables.back().isImported = true;
|
||||
break;
|
||||
}
|
||||
case DefinitionKind::Memory: {
|
||||
|
@ -2176,7 +2198,7 @@ static bool DecodeTableSection(Decoder& d, ModuleEnvironment* env) {
|
|||
}
|
||||
|
||||
for (uint32_t i = 0; i < numTables; ++i) {
|
||||
if (!DecodeTableTypeAndLimits(d, env->features, env->types, &env->tables)) {
|
||||
if (!DecodeTableTypeAndLimits(d, env)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -2356,7 +2378,7 @@ static bool DecodeExport(Decoder& d, ModuleEnvironment* env, NameSet* dupSet) {
|
|||
if (tableIndex >= env->tables.length()) {
|
||||
return d.fail("exported table index out of bounds");
|
||||
}
|
||||
env->tables[tableIndex].isImportedOrExported = true;
|
||||
env->tables[tableIndex].isExported = true;
|
||||
return env->exports.emplaceBack(std::move(fieldName), tableIndex,
|
||||
DefinitionKind::Table);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче