diff --git a/src/builtins/wasm.tq b/src/builtins/wasm.tq index 89f3aaf674..100d606acb 100644 --- a/src/builtins/wasm.tq +++ b/src/builtins/wasm.tq @@ -41,7 +41,10 @@ extern macro Allocate(intptr, constexpr AllocationFlag): HeapObject; } namespace wasm { -const kFuncTableType: constexpr int31 generates 'wasm::HeapType::kFunc'; +const kExternTableType: constexpr int31 + generates 'wasm::kWasmExternRef.raw_bit_field()'; +const kExternNonNullTableType: constexpr int31 + generates 'wasm::kWasmExternNonNullableRef.raw_bit_field()'; extern macro WasmBuiltinsAssembler::LoadInstanceFromFrame(): WasmInstanceObject; @@ -177,8 +180,12 @@ builtin WasmTableSet(tableIndex: intptr, index: int32, value: Object): Object { // Fall back to the runtime to set funcrefs, since we have to update // function dispatch tables. + // TODO(7748): Update this if further table types are supported. const tableType: Smi = table.raw_type; - if (tableType == SmiConstant(kFuncTableType)) goto CallRuntime; + if (tableType != SmiConstant(kExternTableType) && + tableType != SmiConstant(kExternNonNullTableType)) { + goto CallRuntime; + } const entriesCount: intptr = Convert(table.current_length); if (entryIndex >= entriesCount) goto IndexOutOfRange; diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index 03886c3d4e..97e4ccc4df 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -3007,28 +3007,35 @@ Node* WasmGraphBuilder::BuildIndirectCall(uint32_t table_index, key = gasm_->Word32And(key, mask); } - Node* int32_scaled_key = - Uint32ToUintptr(gasm_->Word32Shl(key, Int32Constant(2))); + const wasm::ValueType table_type = env_->module->tables[table_index].type; + // Check that the table entry is not null and that the type of the function is + // a subtype of the function type declared at the call site. In the absence of + // function subtyping, the latter can only happen if the table type is (ref + // null? func). Also, subtyping reduces to normalized signature equality + // checking. + // TODO(7748): Expand this with function subtyping once we have that. + const bool needs_signature_check = + table_type.is_reference_to(wasm::HeapType::kFunc) || + table_type.is_nullable(); + if (needs_signature_check) { + Node* int32_scaled_key = + Uint32ToUintptr(gasm_->Word32Shl(key, Int32Constant(2))); - Node* loaded_sig = - gasm_->Load(MachineType::Int32(), ift_sig_ids, int32_scaled_key); - // Check that the dynamic type of the function is a subtype of its static - // (table) type. Currently, the only subtyping between function types is - // $t <: funcref for all $t: function_type. - // TODO(7748): Expand this with function subtyping. - const bool needs_typechecking = - env_->module->tables[table_index].type == wasm::kWasmFuncRef; - if (needs_typechecking) { - int32_t expected_sig_id = env_->module->canonicalized_type_ids[sig_index]; - Node* sig_match = - gasm_->Word32Equal(loaded_sig, Int32Constant(expected_sig_id)); - TrapIfFalse(wasm::kTrapFuncSigMismatch, sig_match, position); - } else { - // We still have to check that the entry is initialized. - // TODO(9495): Skip this check for non-nullable tables when they are - // allowed. - Node* function_is_null = gasm_->Word32Equal(loaded_sig, Int32Constant(-1)); - TrapIfTrue(wasm::kTrapNullDereference, function_is_null, position); + Node* loaded_sig = + gasm_->Load(MachineType::Int32(), ift_sig_ids, int32_scaled_key); + + if (table_type.is_reference_to(wasm::HeapType::kFunc)) { + int32_t expected_sig_id = env_->module->canonicalized_type_ids[sig_index]; + Node* sig_match = + gasm_->Word32Equal(loaded_sig, Int32Constant(expected_sig_id)); + TrapIfFalse(wasm::kTrapFuncSigMismatch, sig_match, position); + } else { + // If the table entries are nullable, we still have to check that the + // entry is initialized. + Node* function_is_null = + gasm_->Word32Equal(loaded_sig, Int32Constant(-1)); + TrapIfTrue(wasm::kTrapNullDereference, function_is_null, position); + } } Node* key_intptr = Uint32ToUintptr(key); diff --git a/src/wasm/baseline/liftoff-compiler.cc b/src/wasm/baseline/liftoff-compiler.cc index 85ca20d13d..bd80731638 100644 --- a/src/wasm/baseline/liftoff-compiler.cc +++ b/src/wasm/baseline/liftoff-compiler.cc @@ -5190,6 +5190,7 @@ class LiftoffCompiler { __ Load(LiftoffRegister(scratch), table, index, 0, LoadType::kI32Load, pinned); + // TODO(9495): Do not always compare signatures, same as wasm-compiler.cc. // Compare against expected signature. __ LoadConstant(LiftoffRegister(tmp_const), WasmValue(canonical_sig_num)); diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc index d9089f6da5..8b67a2e3f8 100644 --- a/src/wasm/module-decoder.cc +++ b/src/wasm/module-decoder.cc @@ -728,6 +728,9 @@ class ModuleDecoderImpl : public Decoder { "table elements", "elements", std::numeric_limits::max(), &table->initial_size, &table->has_maximum_size, std::numeric_limits::max(), &table->maximum_size, flags); + if (!table_type.is_defaultable()) { + table->initial_value = consume_init_expr(module_.get(), table_type, 0); + } } } @@ -1713,7 +1716,7 @@ class ModuleDecoderImpl : public Decoder { if (V8_UNLIKELY(!enabled_features_.has_reftypes() && !enabled_features_.has_eh())) { errorf(pc(), - "invalid opcode 0x%x in global initializer, enable with " + "invalid opcode 0x%x in initializer expression, enable with " "--experimental-wasm-reftypes or --experimental-wasm-eh", kExprRefNull); return {}; @@ -1729,7 +1732,7 @@ class ModuleDecoderImpl : public Decoder { case kExprRefFunc: { if (V8_UNLIKELY(!enabled_features_.has_reftypes())) { errorf(pc(), - "invalid opcode 0x%x in global initializer, enable with " + "invalid opcode 0x%x in initializer expression, enable with " "--experimental-wasm-reftypes", kExprRefFunc); return {}; @@ -1752,7 +1755,7 @@ class ModuleDecoderImpl : public Decoder { // the type check or stack height check at the end. opcode = read_prefixed_opcode(pc(), &len); if (V8_UNLIKELY(opcode != kExprS128Const)) { - errorf(pc(), "invalid SIMD opcode 0x%x in global initializer", + errorf(pc(), "invalid SIMD opcode 0x%x in initializer expression", opcode); return {}; } @@ -1804,7 +1807,8 @@ class ModuleDecoderImpl : public Decoder { break; } default: { - errorf(pc(), "invalid opcode 0x%x in global initializer", opcode); + errorf(pc(), "invalid opcode 0x%x in initializer expression", + opcode); return {}; } } @@ -1813,7 +1817,7 @@ class ModuleDecoderImpl : public Decoder { case kExprEnd: break; default: { - errorf(pc(), "invalid opcode 0x%x in global initializer", opcode); + errorf(pc(), "invalid opcode 0x%x in initializer expression", opcode); return {}; } } @@ -1821,16 +1825,16 @@ class ModuleDecoderImpl : public Decoder { } if (V8_UNLIKELY(pc() > end())) { - error(end(), "Global initializer extending beyond code end"); + error(end(), "Initializer expression extending beyond code end"); return {}; } if (V8_UNLIKELY(opcode != kExprEnd)) { - error(pc(), "Global initializer is missing 'end'"); + error(pc(), "Initializer expression is missing 'end'"); return {}; } if (V8_UNLIKELY(stack.size() != 1)) { errorf(pc(), - "Found 'end' in global initalizer, but %s expressions were " + "Found 'end' in initializer expression, but %s expressions were " "found on the stack", stack.size() > 1 ? "more than one" : "no"); return {}; diff --git a/src/wasm/module-instantiate.cc b/src/wasm/module-instantiate.cc index 45f6bce21c..3ae8e9773b 100644 --- a/src/wasm/module-instantiate.cc +++ b/src/wasm/module-instantiate.cc @@ -1841,12 +1841,50 @@ void InstanceBuilder::ProcessExports(Handle instance) { void InstanceBuilder::InitializeIndirectFunctionTables( Handle instance) { - for (int i = 0; i < static_cast(module_->tables.size()); ++i) { - const WasmTable& table = module_->tables[i]; + for (int table_index = 0; + table_index < static_cast(module_->tables.size()); ++table_index) { + const WasmTable& table = module_->tables[table_index]; if (IsSubtypeOf(table.type, kWasmFuncRef, module_)) { WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize( - instance, i, table.initial_size); + instance, table_index, table.initial_size); + } + + if (!table.type.is_defaultable()) { + // Function constant is currently the only viable initializer. + DCHECK(table.initial_value.kind() == WasmInitExpr::kRefFuncConst); + uint32_t func_index = table.initial_value.immediate().index; + + uint32_t sig_id = + module_->canonicalized_type_ids[module_->functions[func_index] + .sig_index]; + MaybeHandle wasm_external_function = + WasmInstanceObject::GetWasmExternalFunction(isolate_, instance, + func_index); + auto table_object = handle( + WasmTableObject::cast(instance->tables().get(table_index)), isolate_); + for (uint32_t entry_index = 0; entry_index < table.initial_size; + entry_index++) { + // Update the local dispatch table first. + IndirectFunctionTableEntry(instance, table_index, entry_index) + .Set(sig_id, instance, func_index); + + // Update the table object's other dispatch tables. + if (wasm_external_function.is_null()) { + // No JSFunction entry yet exists for this function. Create a {Tuple2} + // holding the information to lazily allocate one. + WasmTableObject::SetFunctionTablePlaceholder( + isolate_, table_object, entry_index, instance, func_index); + } else { + table_object->entries().set( + entry_index, *wasm_external_function.ToHandleChecked()); + } + // UpdateDispatchTables() updates all other dispatch tables, since + // we have not yet added the dispatch table we are currently building. + WasmTableObject::UpdateDispatchTables( + isolate_, table_object, entry_index, + module_->functions[func_index].sig, instance, func_index); + } } } } diff --git a/src/wasm/value-type.h b/src/wasm/value-type.h index 692b9f8031..b8169885d1 100644 --- a/src/wasm/value-type.h +++ b/src/wasm/value-type.h @@ -530,6 +530,8 @@ class ValueType { static_assert(sizeof(ValueType) <= kUInt32Size, "ValueType is small and can be passed by value"); +static_assert(ValueType::kLastUsedBit < 8 * sizeof(ValueType) - kSmiTagSize, + "ValueType has space to be encoded in a Smi"); inline size_t hash_value(ValueType type) { return static_cast(type.kind()); @@ -560,6 +562,10 @@ constexpr ValueType kWasmDataRef = ValueType::Ref(HeapType::kData, kNonNullable); constexpr ValueType kWasmAnyRef = ValueType::Ref(HeapType::kAny, kNullable); +// This is used in wasm.tq. +constexpr ValueType kWasmExternNonNullableRef = + ValueType::Ref(HeapType::kExtern, kNonNullable); + #define FOREACH_WASMVALUE_CTYPES(V) \ V(kI32, int32_t) \ V(kI64, int64_t) \ diff --git a/src/wasm/wasm-module-builder.cc b/src/wasm/wasm-module-builder.cc index a3bd33c1d6..a02b534b54 100644 --- a/src/wasm/wasm-module-builder.cc +++ b/src/wasm/wasm-module-builder.cc @@ -315,7 +315,7 @@ uint32_t WasmModuleBuilder::AllocateIndirectFunctions(uint32_t count) { if (tables_.empty()) { // This cannot use {AddTable} because that would flip the // {allocating_indirect_functions_allowed_} flag. - tables_.push_back({kWasmFuncRef, new_size, max, true}); + tables_.push_back({kWasmFuncRef, new_size, max, true, {}}); } else { // There can only be the indirect function table so far, otherwise the // {allocating_indirect_functions_allowed_} flag would have been false. @@ -347,7 +347,7 @@ uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size) { #if DEBUG allocating_indirect_functions_allowed_ = false; #endif - tables_.push_back({type, min_size, 0, false}); + tables_.push_back({type, min_size, 0, false, {}}); return static_cast(tables_.size() - 1); } @@ -356,7 +356,16 @@ uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size, #if DEBUG allocating_indirect_functions_allowed_ = false; #endif - tables_.push_back({type, min_size, max_size, true}); + tables_.push_back({type, min_size, max_size, true, {}}); + return static_cast(tables_.size() - 1); +} + +uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size, + uint32_t max_size, WasmInitExpr init) { +#if DEBUG + allocating_indirect_functions_allowed_ = false; +#endif + tables_.push_back({type, min_size, max_size, true, std::move(init)}); return static_cast(tables_.size() - 1); } @@ -432,8 +441,8 @@ void WriteValueType(ZoneBuffer* buffer, const ValueType& type) { } } -void WriteGlobalInitializer(ZoneBuffer* buffer, const WasmInitExpr& init, - ValueType type) { +void WriteInitializerExpression(ZoneBuffer* buffer, const WasmInitExpr& init, + ValueType type) { switch (init.kind()) { case WasmInitExpr::kI32Const: buffer->write_u8(kExprI32Const); @@ -512,7 +521,7 @@ void WriteGlobalInitializer(ZoneBuffer* buffer, const WasmInitExpr& init, break; case WasmInitExpr::kRttSub: // The operand to rtt.sub must be emitted first. - WriteGlobalInitializer(buffer, *init.operand(), kWasmBottom); + WriteInitializerExpression(buffer, *init.operand(), kWasmBottom); STATIC_ASSERT((kExprRttSub >> 8) == kGCPrefix); buffer->write_u8(kGCPrefix); buffer->write_u8(static_cast(kExprRttSub)); @@ -611,6 +620,9 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { buffer->write_u8(table.has_maximum ? kWithMaximum : kNoMaximum); buffer->write_size(table.min_size); if (table.has_maximum) buffer->write_size(table.max_size); + if (table.init.kind() != WasmInitExpr::kNone) { + WriteInitializerExpression(buffer, table.init, table.type); + } } FixupSection(buffer, start); } @@ -651,7 +663,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const { for (const WasmGlobal& global : globals_) { WriteValueType(buffer, global.type); buffer->write_u8(global.mutability ? 1 : 0); - WriteGlobalInitializer(buffer, global.init, global.type); + WriteInitializerExpression(buffer, global.init, global.type); buffer->write_u8(kExprEnd); } FixupSection(buffer, start); diff --git a/src/wasm/wasm-module-builder.h b/src/wasm/wasm-module-builder.h index f93b981d7c..59bab3e036 100644 --- a/src/wasm/wasm-module-builder.h +++ b/src/wasm/wasm-module-builder.h @@ -261,6 +261,8 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { void SetMaxTableSize(uint32_t max); uint32_t AddTable(ValueType type, uint32_t min_size); uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size); + uint32_t AddTable(ValueType type, uint32_t min_size, uint32_t max_size, + WasmInitExpr init); void MarkStartFunction(WasmFunctionBuilder* builder); void AddExport(Vector name, ImportExportKindCode kind, uint32_t index); @@ -340,6 +342,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject { uint32_t min_size; uint32_t max_size; bool has_maximum; + WasmInitExpr init; }; struct WasmDataSegment { diff --git a/src/wasm/wasm-module.h b/src/wasm/wasm-module.h index 439be1d2c7..76747b6c7c 100644 --- a/src/wasm/wasm-module.h +++ b/src/wasm/wasm-module.h @@ -354,7 +354,7 @@ struct WasmTable { // TODO(9495): Update this function as more table types are supported, or // remove it completely when all reference types are allowed. static bool IsValidTableType(ValueType type, const WasmModule* module) { - if (!type.is_nullable()) return false; + if (!type.is_object_reference()) return false; HeapType heap_type = type.heap_type(); return heap_type == HeapType::kFunc || heap_type == HeapType::kExtern || (module != nullptr && heap_type.is_index() && @@ -367,6 +367,7 @@ struct WasmTable { bool has_maximum_size = false; // true if there is a maximum size. bool imported = false; // true if imported. bool exported = false; // true if exported. + WasmInitExpr initial_value; }; inline bool is_asmjs_module(const WasmModule* module) { diff --git a/src/wasm/wasm-objects-inl.h b/src/wasm/wasm-objects-inl.h index 8e04f656db..483e28ece6 100644 --- a/src/wasm/wasm-objects-inl.h +++ b/src/wasm/wasm-objects-inl.h @@ -393,8 +393,7 @@ ACCESSORS(WasmIndirectFunctionTable, refs, FixedArray, kRefsOffset) #undef PRIMITIVE_ACCESSORS wasm::ValueType WasmTableObject::type() { - // TODO(7748): Support other table types? Wait for spec to clear up. - return wasm::ValueType::Ref(raw_type(), wasm::kNullable); + return wasm::ValueType::FromRawBitField(raw_type()); } bool WasmMemoryObject::has_maximum_pages() { return maximum_pages() >= 0; } diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc index c315d3804e..55cad21a64 100644 --- a/src/wasm/wasm-objects.cc +++ b/src/wasm/wasm-objects.cc @@ -291,7 +291,7 @@ Handle WasmTableObject::New( table_obj->set_entries(*backing_store); table_obj->set_current_length(initial); table_obj->set_maximum_length(*max); - table_obj->set_raw_type(static_cast(type.heap_representation())); + table_obj->set_raw_type(static_cast(type.raw_bit_field())); table_obj->set_dispatch_tables(ReadOnlyRoots(isolate).empty_fixed_array()); if (entries != nullptr) { diff --git a/test/mjsunit/wasm/reference-tables.js b/test/mjsunit/wasm/reference-tables.js index 756ec04d44..b1e4832231 100644 --- a/test/mjsunit/wasm/reference-tables.js +++ b/test/mjsunit/wasm/reference-tables.js @@ -6,7 +6,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); -(function Test1() { +(function TestTables() { var exporting_instance = (function () { var builder = new WasmModuleBuilder(); var binary_type = builder.addType(kSig_i_ii); @@ -15,8 +15,8 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); .addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add]) .exportFunc(); - builder.addFunction("id", kSig_i_i) - .addBody([kExprLocalGet, 0]) + builder.addFunction("succ", kSig_i_i) + .addBody([kExprLocalGet, 0, kExprI32Const, 1, kExprI32Add]) .exportFunc(); builder.addTable(wasmOptRefType(binary_type), 1, 100).exportAs("table"); @@ -57,7 +57,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); builder.addImportedTable("imports", "table", 1, 100, wasmOptRefType(binary_type)); - var table = builder.addTable(wasmOptRefType(unary_type), 1) + var table = builder.addTable(wasmOptRefType(unary_type), 10) .exportAs("table"); builder.addTable(kWasmFuncRef, 1).exportAs("generic_table"); @@ -69,6 +69,14 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); kExprCallRef]) .exportFunc(); + // Same, but with table[1] and call_indirect + builder.addFunction("table_indirect_test", + makeSig([wasmRefType(unary_type)], [kWasmI32])) + .addBody([kExprI32Const, 1, kExprLocalGet, 0, kExprTableSet, table.index, + kExprI32Const, 42, kExprI32Const, 0, + kExprCallIndirect, unary_type, table.index]) + .exportFunc(); + // Instantiate with a table of the correct type. return builder.instantiate( {imports: {table: exporting_instance.exports.table}}); @@ -79,13 +87,50 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); // The correct function reference is preserved when setting it to and getting // it back from a table. - assertEquals(42, instance.exports.table_test(exporting_instance.exports.id)); + assertEquals(43, + instance.exports.table_test(exporting_instance.exports.succ)); + // Same for call indirect (the indirect call tables are also set correctly). + assertEquals(43, instance.exports.table_indirect_test( + exporting_instance.exports.succ)); // Setting from JS API respects types. - instance.exports.generic_table.set(0, exporting_instance.exports.id); - instance.exports.table.set(0, exporting_instance.exports.id); + instance.exports.generic_table.set(0, exporting_instance.exports.succ); + instance.exports.table.set(0, exporting_instance.exports.succ); assertThrows( () => instance.exports.table.set(0, exporting_instance.exports.addition), TypeError, - /Argument 1 must be null or a WebAssembly function of type compatible to 'this'/); + /Argument 1 must be null or a WebAssembly function of type compatible to/); +})(); + +(function TestNonNullableTables() { + var builder = new WasmModuleBuilder(); + + var binary_type = builder.addType(kSig_i_ii); + + var addition = builder.addFunction("addition", kSig_i_ii) + .addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Add]); + var subtraction = builder.addFunction("subtraction", kSig_i_ii) + .addBody([kExprLocalGet, 0, kExprLocalGet, 1, kExprI32Sub]) + .exportFunc(); + + var table = builder.addTable(wasmRefType(binary_type), 3, 3, addition.index); + + builder.addFunction("init", kSig_v_v) + .addBody([kExprI32Const, 1, kExprRefFunc, subtraction.index, + kExprTableSet, table.index]) + .exportFunc(); + + // (index, arg1, arg2) -> table[index](arg1, arg2) + builder.addFunction("table_test", kSig_i_iii) + .addBody([kExprLocalGet, 1, kExprLocalGet, 2, kExprLocalGet, 0, + kExprCallIndirect, binary_type, table.index]) + .exportFunc(); + + var instance = builder.instantiate({}); + + assertTrue(!!instance); + + instance.exports.init(); + assertEquals(44, instance.exports.table_test(0, 33, 11)); + assertEquals(22, instance.exports.table_test(1, 33, 11)); })(); diff --git a/test/mjsunit/wasm/table-access.js b/test/mjsunit/wasm/table-access.js index bde5793acc..1f070d01f9 100644 --- a/test/mjsunit/wasm/table-access.js +++ b/test/mjsunit/wasm/table-access.js @@ -147,7 +147,6 @@ const dummy_func = exports.set_table_func1; kExprTableSet, table_index, // -- kExprI32Const, index, // entry index kExprCallIndirect, sig_index, table_index // -- - ]) .exportFunc(); diff --git a/test/mjsunit/wasm/wasm-module-builder.js b/test/mjsunit/wasm/wasm-module-builder.js index b5021a313c..34e9915945 100644 --- a/test/mjsunit/wasm/wasm-module-builder.js +++ b/test/mjsunit/wasm/wasm-module-builder.js @@ -961,12 +961,14 @@ class WasmGlobalBuilder { } class WasmTableBuilder { - constructor(module, type, initial_size, max_size) { + constructor(module, type, initial_size, max_size, init_func_index) { this.module = module; this.type = type; this.initial_size = initial_size; this.has_max = max_size != undefined; this.max_size = max_size; + this.init_func_index = init_func_index; + this.has_init = init_func_index != undefined; } exportAs(name) { @@ -1094,12 +1096,14 @@ class WasmModuleBuilder { return glob; } - addTable(type, initial_size, max_size = undefined) { + addTable(type, initial_size, max_size = undefined, + init_func_index = undefined) { if (type == kWasmI32 || type == kWasmI64 || type == kWasmF32 || type == kWasmF64 || type == kWasmS128 || type == kWasmStmt) { throw new Error('Tables must be of a reference type'); } - let table = new WasmTableBuilder(this, type, initial_size, max_size); + let table = new WasmTableBuilder(this, type, initial_size, max_size, + init_func_index); table.index = this.tables.length + this.num_imported_tables; this.tables.push(table); return table; @@ -1367,6 +1371,11 @@ class WasmModuleBuilder { section.emit_u8(table.has_max); section.emit_u32v(table.initial_size); if (table.has_max) section.emit_u32v(table.max_size); + if (table.has_init) { + section.emit_u8(kExprRefFunc); + section.emit_u32v(table.init_func_index); + section.emit_u8(kExprEnd); + } } }); } diff --git a/test/unittests/wasm/module-decoder-unittest.cc b/test/unittests/wasm/module-decoder-unittest.cc index f721dc33d3..7f30fc1f09 100644 --- a/test/unittests/wasm/module-decoder-unittest.cc +++ b/test/unittests/wasm/module-decoder-unittest.cc @@ -495,7 +495,7 @@ TEST_F(WasmModuleVerifyTest, GlobalInitializer) { 1) // mutable }; EXPECT_FAILURE_WITH_MSG(no_initializer_no_end, - "Global initializer is missing 'end'"); + "Initializer expression is missing 'end'"); static const byte no_initializer[] = { SECTION(Global, //-- @@ -505,7 +505,7 @@ TEST_F(WasmModuleVerifyTest, GlobalInitializer) { kExprEnd) // -- }; EXPECT_FAILURE_WITH_MSG(no_initializer, - "Found 'end' in global initalizer, but no " + "Found 'end' in initializer expression, but no " "expressions were found on the stack"); static const byte too_many_initializers_no_end[] = { @@ -517,7 +517,7 @@ TEST_F(WasmModuleVerifyTest, GlobalInitializer) { WASM_I32V_1(43)) // another value is too much }; EXPECT_FAILURE_WITH_MSG(too_many_initializers_no_end, - "Global initializer is missing 'end'"); + "Initializer expression is missing 'end'"); static const byte too_many_initializers[] = { SECTION(Global, // -- @@ -528,8 +528,8 @@ TEST_F(WasmModuleVerifyTest, GlobalInitializer) { WASM_I32V_1(43), // another value is too much kExprEnd)}; EXPECT_FAILURE_WITH_MSG(too_many_initializers, - "Found 'end' in global initalizer, but more than one " - "expressions were found on the stack"); + "Found 'end' in initializer expression, but more than" + " one expressions were found on the stack"); static const byte missing_end_opcode[] = { SECTION(Global, // -- @@ -539,7 +539,7 @@ TEST_F(WasmModuleVerifyTest, GlobalInitializer) { WASM_I32V_1(42)) // init value }; EXPECT_FAILURE_WITH_MSG(missing_end_opcode, - "Global initializer is missing 'end'"); + "Initializer expression is missing 'end'"); static const byte referencing_out_of_bounds_global[] = { SECTION(Global, ENTRY_COUNT(1), // -- @@ -1971,6 +1971,24 @@ TEST_F(WasmModuleVerifyTest, TypedFunctionTable) { EXPECT_EQ(ValueType::Ref(0, kNullable), result.value()->tables[0].type); } +TEST_F(WasmModuleVerifyTest, NullableTableIllegalInitializer) { + WASM_FEATURE_SCOPE(reftypes); + WASM_FEATURE_SCOPE(typed_funcref); + + static const byte data[] = { + SECTION(Type, ENTRY_COUNT(1), SIG_ENTRY_v_v), // type section + ONE_EMPTY_FUNCTION(0), // function section + SECTION(Table, // table section + ENTRY_COUNT(1), // 1 table + kOptRefCode, 0, // table 0: type + 0, 10, // table 0: limits + kExprRefFunc, 0, kExprEnd)}; // table 0: initializer + + EXPECT_FAILURE_WITH_MSG( + data, + "section was shorter than expected size (8 bytes expected, 5 decoded)"); +} + TEST_F(WasmModuleVerifyTest, IllegalTableTypes) { WASM_FEATURE_SCOPE(reftypes); WASM_FEATURE_SCOPE(typed_funcref); @@ -1999,13 +2017,47 @@ TEST_F(WasmModuleVerifyTest, IllegalTableTypes) { auto result = DecodeModule(data.data(), data.data() + data.size()); - EXPECT_NOT_OK( - result, - "Currently, only externref and function references are allowed " - "as table types"); + EXPECT_NOT_OK(result, + "Currently, only externref and function references are " + "allowed as table types"); } } +TEST_F(WasmModuleVerifyTest, NonNullableTable) { + WASM_FEATURE_SCOPE(reftypes); + WASM_FEATURE_SCOPE(typed_funcref); + + static const byte data[] = { + SECTION(Type, ENTRY_COUNT(1), SIG_ENTRY_v_v), // type section + ONE_EMPTY_FUNCTION(0), // function section + SECTION(Table, // table section + ENTRY_COUNT(1), // 1 table + kRefCode, 0, // table 0: type + 0, 10, // table 0: limits + kExprRefFunc, 0, kExprEnd), // table 0: init. expression + SECTION(Code, ENTRY_COUNT(1), NOP_BODY)}; + ModuleResult result = DecodeModule(data, data + sizeof(data)); + EXPECT_OK(result); + EXPECT_EQ(ValueType::Ref(0, kNonNullable), result.value()->tables[0].type); +} + +TEST_F(WasmModuleVerifyTest, NonNullableTableNoInitializer) { + WASM_FEATURE_SCOPE(reftypes); + WASM_FEATURE_SCOPE(typed_funcref); + + static const byte data[] = { + SECTION(Type, ENTRY_COUNT(1), SIG_ENTRY_v_x(kI32Code)), + SECTION(Table, // table section + ENTRY_COUNT(2), // 2 tables + kRefCode, 0, // table 0: type + 0, 10, // table 0: limits + kRefCode, 0, // table 1: type + 5, 6)}; // table 1: limits + + EXPECT_FAILURE_WITH_MSG(data, + "invalid opcode 0x6b in initializer expression"); +} + TEST_F(WasmModuleVerifyTest, TieringCompilationHints) { WASM_FEATURE_SCOPE(compilation_hints); static const byte data[] = {