diff --git a/src/heap/factory.cc b/src/heap/factory.cc index 389d09ba17..ae62f04ae3 100644 --- a/src/heap/factory.cc +++ b/src/heap/factory.cc @@ -1457,6 +1457,29 @@ Handle Factory::NewWasmCapiFunctionData( return handle(result, isolate()); } +Handle Factory::NewWasmArray( + const wasm::ArrayType* type, const std::vector& elements, + Handle map) { + uint32_t length = static_cast(elements.size()); + HeapObject raw = + AllocateRaw(WasmArray::SizeFor(*map, length), AllocationType::kYoung); + raw.set_map_after_allocation(*map); + WasmArray result = WasmArray::cast(raw); + result.set_raw_properties_or_hash(*empty_fixed_array()); + result.set_length(length); + for (uint32_t i = 0; i < length; i++) { + Address address = result.ElementAddress(i); + if (type->element_type().is_numeric()) { + elements[i] + .Packed(type->element_type()) + .CopyTo(reinterpret_cast(address)); + } else { + base::WriteUnalignedValue(address, *elements[i].to_ref()); + } + } + return handle(result, isolate()); +} + Handle Factory::NewWasmStruct(const wasm::StructType* type, wasm::WasmValue* args, Handle map) { diff --git a/src/heap/factory.h b/src/heap/factory.h index 5b531e2b52..14dd5994d4 100644 --- a/src/heap/factory.h +++ b/src/heap/factory.h @@ -71,6 +71,7 @@ class WasmJSFunctionData; class WeakCell; #if V8_ENABLE_WEBASSEMBLY namespace wasm { +class ArrayType; class StructType; class WasmValue; } // namespace wasm @@ -578,6 +579,9 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase { Handle wrapper_code); Handle NewWasmStruct(const wasm::StructType* type, wasm::WasmValue* args, Handle map); + Handle NewWasmArray(const wasm::ArrayType* type, + const std::vector& elements, + Handle map); Handle NewSharedFunctionInfoForWasmExportedFunction( Handle name, Handle data); diff --git a/src/wasm/module-decoder.cc b/src/wasm/module-decoder.cc index 0682cd0548..0b69957c7e 100644 --- a/src/wasm/module-decoder.cc +++ b/src/wasm/module-decoder.cc @@ -1823,7 +1823,10 @@ class ModuleDecoderImpl : public Decoder { len += imm.length; const StructType* type = module->struct_type(imm.index); if (!V8_LIKELY(stack.size() >= type->field_count() + 1)) { - error(pc(), "not enough arguments for struct.new"); + errorf(pc(), + "not enough arguments on the stack for struct.new: " + "expected %u, found %zu", + type->field_count() + 1, stack.size()); return {}; } std::vector arguments(type->field_count() + 1); @@ -1856,6 +1859,71 @@ class ModuleDecoderImpl : public Decoder { imm.index, std::move(arguments))); break; } + case kExprArrayInit: { + if (!V8_LIKELY(enabled_features_.has_gc_experiments())) { + error(pc(), + "invalid opcode array.init in init. expression, enable " + "with --experimental-wasm-gc-experiments"); + return {}; + } + IndexImmediate array_imm(this, pc() + len, + "array index"); + if (!V8_LIKELY(module->has_array(array_imm.index))) { + errorf(pc() + len, "invalid array type index #%u", + array_imm.index); + return {}; + } + IndexImmediate length_imm( + this, pc() + len + array_imm.length, "array.init length"); + uint32_t elem_count = length_imm.index; + if (elem_count > kV8MaxWasmArrayInitLength) { + errorf(pc() + len + array_imm.length, + "Requested length %u for array.init too large, maximum " + "is %zu", + length_imm.index, kV8MaxWasmArrayInitLength); + return {}; + } + len += array_imm.length + length_imm.length; + const ArrayType* array_type = + module_->array_type(array_imm.index); + if (stack.size() < elem_count + 1) { + errorf(pc(), + "not enough arguments on the stack for array.init: " + "expected %u, found %zu", + elem_count + 1, stack.size()); + return {}; + } + std::vector arguments(elem_count + 1); + WasmInitExpr* stack_args = &stack.back() - elem_count; + for (uint32_t i = 0; i < elem_count; i++) { + WasmInitExpr& argument = stack_args[i]; + if (!IsSubtypeOf(TypeOf(argument), + array_type->element_type().Unpacked(), + module)) { + errorf(pc(), "array.init[%u]: expected %s, found %s instead", + i, array_type->element_type().name().c_str(), + TypeOf(argument).name().c_str()); + return {}; + } + arguments[i] = std::move(argument); + } + WasmInitExpr& rtt = stack.back(); + if (!IsSubtypeOf(TypeOf(rtt), ValueType::Rtt(array_imm.index), + module)) { + errorf(pc(), "array.init[%u]: expected %s, found %s instead", + elem_count, + ValueType::Rtt(array_imm.index).name().c_str(), + TypeOf(rtt).name().c_str()); + return {}; + } + arguments[elem_count] = std::move(rtt); + for (uint32_t i = 0; i <= elem_count; i++) { + stack.pop_back(); + } + stack.push_back(WasmInitExpr::ArrayInit(array_imm.index, + std::move(arguments))); + break; + } case kExprRttCanon: { IndexImmediate imm(this, pc() + len, "type index"); if (V8_UNLIKELY(!module_->has_type(imm.index))) { @@ -2049,7 +2117,7 @@ class ModuleDecoderImpl : public Decoder { ValueType field = consume_storage_type(); if (failed()) return nullptr; bool mutability = consume_mutability(); - if (!mutability) { + if (!V8_LIKELY(mutability)) { error(this->pc() - 1, "immutable arrays are not supported yet"); } return zone->New(field, mutability); diff --git a/src/wasm/module-instantiate.cc b/src/wasm/module-instantiate.cc index 05d0354b33..55bd6d58e2 100644 --- a/src/wasm/module-instantiate.cc +++ b/src/wasm/module-instantiate.cc @@ -1576,6 +1576,17 @@ WasmValue InstanceBuilder::EvaluateInitExpression( isolate_->factory()->NewWasmStruct(type, fields.data(), rtt), init.type(module_, enabled_)); } + case WasmInitExpr::kArrayInit: { + const ArrayType* type = module_->array_type(init.immediate().index); + std::vector elements(init.operands().size() - 1); + for (uint32_t i = 0; i < elements.size(); i++) { + elements[i] = EvaluateInitExpression(init.operands()[i], instance); + } + auto rtt = Handle::cast( + EvaluateInitExpression(init.operands().back(), instance).to_ref()); + return WasmValue(isolate_->factory()->NewWasmArray(type, elements, rtt), + init.type(module_, enabled_)); + } case WasmInitExpr::kRttCanon: { int map_index = init.immediate().index; return WasmValue( diff --git a/src/wasm/wasm-init-expr.cc b/src/wasm/wasm-init-expr.cc index b028473ef1..14a7e3b6a6 100644 --- a/src/wasm/wasm-init-expr.cc +++ b/src/wasm/wasm-init-expr.cc @@ -39,6 +39,7 @@ ValueType WasmInitExpr::type(const WasmModule* module, case kRefNullConst: return ValueType::Ref(immediate().heap_type, kNullable); case kStructNewWithRtt: + case kArrayInit: return ValueType::Ref(immediate().index, kNonNullable); case kRttCanon: return ValueType::Rtt(immediate().heap_type, 0); diff --git a/src/wasm/wasm-init-expr.h b/src/wasm/wasm-init-expr.h index 7b3cff572f..bf68265b2a 100644 --- a/src/wasm/wasm-init-expr.h +++ b/src/wasm/wasm-init-expr.h @@ -34,6 +34,7 @@ class WasmInitExpr { kRefNullConst, kRefFuncConst, kStructNewWithRtt, + kArrayInit, kRttCanon, kRttSub, kRttFreshSub, @@ -98,6 +99,15 @@ class WasmInitExpr { return expr; } + static WasmInitExpr ArrayInit(uint32_t index, + std::vector elements) { + WasmInitExpr expr; + expr.kind_ = kArrayInit; + expr.immediate_.index = index; + expr.operands_ = std::move(elements); + return expr; + } + static WasmInitExpr RttCanon(uint32_t index) { WasmInitExpr expr; expr.kind_ = kRttCanon; @@ -153,6 +163,13 @@ class WasmInitExpr { if (operands()[i] != other.operands()[i]) return false; } return true; + case kArrayInit: + if (immediate().index != other.immediate().index) return false; + if (operands().size() != other.operands().size()) return false; + for (uint32_t i = 0; i < operands().size(); i++) { + if (operands()[i] != other.operands()[i]) return false; + } + return true; case kRttSub: case kRttFreshSub: return immediate().index == other.immediate().index && diff --git a/src/wasm/wasm-limits.h b/src/wasm/wasm-limits.h index f1d7184445..9e2ddc7fcc 100644 --- a/src/wasm/wasm-limits.h +++ b/src/wasm/wasm-limits.h @@ -61,6 +61,7 @@ constexpr uint32_t kV8MaxRttSubtypingDepth = 31; // Maximum supported by implementation: ((1<<27)-3). // Reason: total object size in bytes must fit into a Smi, for filler objects. constexpr size_t kV8MaxWasmArrayLength = 1u << 26; +constexpr size_t kV8MaxWasmArrayInitLength = 999; static_assert(kV8MaxWasmTableSize <= 4294967295, // 2^32 - 1 "v8 should not exceed WebAssembly's non-web embedding limits"); diff --git a/src/wasm/wasm-module-builder.cc b/src/wasm/wasm-module-builder.cc index f2484e2fc8..2e0f641267 100644 --- a/src/wasm/wasm-module-builder.cc +++ b/src/wasm/wasm-module-builder.cc @@ -526,6 +526,16 @@ void WriteInitializerExpression(ZoneBuffer* buffer, const WasmInitExpr& init, buffer->write_u8(static_cast(kExprStructNewWithRtt)); buffer->write_u32v(init.immediate().index); break; + case WasmInitExpr::kArrayInit: + STATIC_ASSERT((kExprArrayInit >> 8) == kGCPrefix); + for (const WasmInitExpr& operand : init.operands()) { + WriteInitializerExpression(buffer, operand, kWasmBottom); + } + buffer->write_u8(kGCPrefix); + buffer->write_u8(static_cast(kExprArrayInit)); + buffer->write_u32v(init.immediate().index); + buffer->write_u32v(static_cast(init.operands().size() - 1)); + break; case WasmInitExpr::kRttCanon: STATIC_ASSERT((kExprRttCanon >> 8) == kGCPrefix); buffer->write_u8(kGCPrefix); diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc index 64972f27c0..f9b59cccbf 100644 --- a/src/wasm/wasm-objects.cc +++ b/src/wasm/wasm-objects.cc @@ -1685,6 +1685,12 @@ ObjectSlot WasmArray::ElementSlot(uint32_t index) { return RawField(kHeaderSize + kTaggedSize * index); } +Address WasmArray::ElementAddress(uint32_t index) { + DCHECK_LE(index, length()); + return ptr() + WasmArray::kHeaderSize + + index * type()->element_type().element_size_bytes() - kHeapObjectTag; +} + // static Handle WasmExceptionObject::New( Isolate* isolate, const wasm::FunctionSig* sig, diff --git a/src/wasm/wasm-objects.h b/src/wasm/wasm-objects.h index e5086a3012..13a131e1f7 100644 --- a/src/wasm/wasm-objects.h +++ b/src/wasm/wasm-objects.h @@ -1000,6 +1000,9 @@ class WasmArray : public TorqueGeneratedWasmArray { Handle array, uint32_t index); + // Returns the Address of the element at {index}. + Address ElementAddress(uint32_t index); + DECL_CAST(WasmArray) DECL_PRINTER(WasmArray) diff --git a/src/wasm/wasm-opcodes-inl.h b/src/wasm/wasm-opcodes-inl.h index 671b12a8fc..ebb364e4f0 100644 --- a/src/wasm/wasm-opcodes-inl.h +++ b/src/wasm/wasm-opcodes-inl.h @@ -396,6 +396,7 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) { CASE_OP(ArraySet, "array.set") CASE_OP(ArrayLen, "array.len") CASE_OP(ArrayCopy, "array.copy") + CASE_OP(ArrayInit, "array.init") CASE_OP(I31New, "i31.new") CASE_OP(I31GetS, "i31.get_s") CASE_OP(I31GetU, "i31.get_u") diff --git a/src/wasm/wasm-opcodes.h b/src/wasm/wasm-opcodes.h index 08cf9ae2ec..c05578f092 100644 --- a/src/wasm/wasm-opcodes.h +++ b/src/wasm/wasm-opcodes.h @@ -664,6 +664,7 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig, V(ArraySet, 0xfb16, _) \ V(ArrayLen, 0xfb17, _) \ V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \ + V(ArrayInit, 0xfb19, _) /* not standardized - V8 experimental */ \ V(I31New, 0xfb20, _) \ V(I31GetS, 0xfb21, _) \ V(I31GetU, 0xfb22, _) \ diff --git a/test/common/wasm/wasm-macro-gen.h b/test/common/wasm/wasm-macro-gen.h index 6aea179a1c..9cfed99e0d 100644 --- a/test/common/wasm/wasm-macro-gen.h +++ b/test/common/wasm/wasm-macro-gen.h @@ -560,6 +560,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { dst_array, dst_index, src_array, src_index, length, \ WASM_GC_OP(kExprArrayCopy), static_cast(dst_idx), \ static_cast(src_idx) +#define WASM_ARRAY_INIT(index, length, ...) \ + __VA_ARGS__, WASM_GC_OP(kExprArrayInit), static_cast(index), \ + static_cast(length) #define WASM_RTT_WITH_DEPTH(depth, typeidx) \ kRttWithDepthCode, U32V_1(depth), U32V_1(typeidx) diff --git a/test/fuzzer/wasm-fuzzer-common.cc b/test/fuzzer/wasm-fuzzer-common.cc index 9fe7b99951..fa33a898a2 100644 --- a/test/fuzzer/wasm-fuzzer-common.cc +++ b/test/fuzzer/wasm-fuzzer-common.cc @@ -170,6 +170,7 @@ std::ostream& operator<<(std::ostream& os, const WasmInitExpr& expr) { case WasmInitExpr::kRttFreshSub: case WasmInitExpr::kRefNullConst: case WasmInitExpr::kStructNewWithRtt: + case WasmInitExpr::kArrayInit: // TODO(manoskouk): Implement these. UNIMPLEMENTED(); case WasmInitExpr::kGlobalGet: diff --git a/test/mjsunit/wasm/reference-globals.js b/test/mjsunit/wasm/reference-globals.js index 9da73f7ae6..0dd3282ecc 100644 --- a/test/mjsunit/wasm/reference-globals.js +++ b/test/mjsunit/wasm/reference-globals.js @@ -7,6 +7,8 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); (function TestReferenceGlobals() { + print(arguments.callee.name); + var exporting_instance = (function() { var builder = new WasmModuleBuilder(); @@ -105,6 +107,8 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); })(); (function TestStructInitExpr() { + print(arguments.callee.name); + var builder = new WasmModuleBuilder(); var struct_index = builder.addStruct([{type: kWasmI32, mutability: false}]); var composite_struct_index = builder.addStruct( @@ -158,3 +162,96 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); assertEquals(field2_value, instance.exports.field_2()); assertEquals((field3_value << 24) >> 24, instance.exports.field_3()); })(); + +(function TestArrayInitExprNumeric() { + print(arguments.callee.name); + + var builder = new WasmModuleBuilder(); + var array_index = builder.addArray(kWasmI16, true); + + let element0_value = -44; + let element1_value = 55; + + var global0 = builder.addGlobal( + kWasmI32, false, + WasmInitExpr.I32Const(element0_value)); + + var global = builder.addGlobal( + wasmRefType(array_index), false, + WasmInitExpr.ArrayInit( + array_index, + [WasmInitExpr.GlobalGet(global0.index), + WasmInitExpr.I32Const(element1_value), + WasmInitExpr.RttCanon(array_index)])); + + builder.addFunction("get_element", kSig_i_i) + .addBody([ + kExprGlobalGet, global.index, + kExprLocalGet, 0, + kGCPrefix, kExprArrayGetS, array_index]) + .exportFunc(); + + var instance = builder.instantiate({}); + + assertEquals(element0_value, instance.exports.get_element(0)); + assertEquals(element1_value, instance.exports.get_element(1)); +})(); + +(function TestArrayInitExprRef() { + print(arguments.callee.name); + + var builder = new WasmModuleBuilder(); + var struct_index = builder.addStruct([{type: kWasmI32, mutability: false}]); + var array_index = builder.addArray(wasmOptRefType(struct_index), true); + + let element0_value = 44; + let element2_value = 55; + + var global0 = builder.addGlobal( + wasmRefType(struct_index), false, + WasmInitExpr.StructNewWithRtt( + struct_index, + [WasmInitExpr.I32Const(element0_value), + WasmInitExpr.RttCanon(struct_index)])); + + var global = builder.addGlobal( + wasmRefType(array_index), false, + WasmInitExpr.ArrayInit( + array_index, + [WasmInitExpr.GlobalGet(global0.index), + WasmInitExpr.RefNull(struct_index), + WasmInitExpr.StructNewWithRtt( + struct_index, + [WasmInitExpr.I32Const(element2_value), + WasmInitExpr.RttCanon(struct_index)]), + WasmInitExpr.RttCanon(array_index)])); + + builder.addFunction("element0", kSig_i_v) + .addBody([ + kExprGlobalGet, global.index, + kExprI32Const, 0, + kGCPrefix, kExprArrayGet, array_index, + kGCPrefix, kExprStructGet, struct_index, 0]) + .exportFunc(); + + builder.addFunction("element1", makeSig([], [kWasmAnyRef])) + .addBody([ + kExprGlobalGet, global.index, + kExprI32Const, 1, + kGCPrefix, kExprArrayGet, array_index]) + .exportFunc(); + + builder.addFunction("element2", kSig_i_v) + .addBody([ + kExprGlobalGet, global.index, + kExprI32Const, 2, + kGCPrefix, kExprArrayGet, array_index, + kGCPrefix, kExprStructGet, struct_index, 0]) + .exportFunc(); + + var instance = builder.instantiate({}); + + assertEquals(element0_value, instance.exports.element0()); + assertEquals(null, instance.exports.element1()); + assertEquals(element2_value, instance.exports.element2()); +})(); diff --git a/test/mjsunit/wasm/wasm-array-js-interop.js b/test/mjsunit/wasm/wasm-array-js-interop.js index e339fd8ad5..7f7c411c34 100644 --- a/test/mjsunit/wasm/wasm-array-js-interop.js +++ b/test/mjsunit/wasm/wasm-array-js-interop.js @@ -15,7 +15,7 @@ let instances = []; function createArray_i() { let builder = new WasmModuleBuilder(); - const type_index = builder.addArray(kWasmI32); + const type_index = builder.addArray(kWasmI32, true); let sig_a_i = makeSig_r_x(kWasmDataRef, kWasmI32); let sig_i_ai = makeSig([kWasmDataRef, kWasmI32], [kWasmI32]); diff --git a/test/mjsunit/wasm/wasm-module-builder.js b/test/mjsunit/wasm/wasm-module-builder.js index 23d43246a9..e07435ee83 100644 --- a/test/mjsunit/wasm/wasm-module-builder.js +++ b/test/mjsunit/wasm/wasm-module-builder.js @@ -458,6 +458,8 @@ let kExprArrayGetS = 0x14; let kExprArrayGetU = 0x15; let kExprArraySet = 0x16; let kExprArrayLen = 0x17; +let kExprArrayCopy = 0x18; +let kExprArrayInit = 0x19; let kExprI31New = 0x20; let kExprI31GetS = 0x21; let kExprI31GetU = 0x22; @@ -982,6 +984,15 @@ class Binary { this.emit_u8(kExprStructNewWithRtt); this.emit_u32v(expr.value); break; + case kExprArrayInit: + for (let operand of expr.operands) { + this.emit_init_expr_recursive(operand); + } + this.emit_u8(kGCPrefix); + this.emit_u8(kExprArrayInit); + this.emit_u32v(expr.value); + this.emit_u32v(expr.operands.length - 1); + break; case kExprRttCanon: this.emit_u8(kGCPrefix); this.emit_u8(kExprRttCanon); @@ -992,11 +1003,13 @@ class Binary { this.emit_u8(kGcPrefix); this.emit_u8(kExprRttSub); this.emit_u32v(expr.value); + break; case kExprRttFreshSub: this.emit_init_expr_recursive(expr.parent); this.emit_u8(kGcPrefix); this.emit_u8(kExprRttFreshSub); this.emit_u32v(expr.value); + break; } } @@ -1129,6 +1142,9 @@ class WasmInitExpr { static StructNewWithRtt(type, args) { return {kind: kExprStructNewWithRtt, value: type, operands: args}; } + static ArrayInit(type, args) { + return {kind: kExprArrayInit, value: type, operands: args}; + } static RttCanon(type) { return {kind: kExprRttCanon, value: type}; } @@ -1212,8 +1228,10 @@ class WasmStruct { } class WasmArray { - constructor(type) { + constructor(type, mutability) { this.type = type; + if (!mutability) throw new Error("Immutable arrays are not supported yet"); + this.mutability = mutability; } } @@ -1339,8 +1357,8 @@ class WasmModuleBuilder { return this.types.length - 1; } - addArray(type) { - this.types.push(new WasmArray(type)); + addArray(type, mutability) { + this.types.push(new WasmArray(type, mutability)); return this.types.length - 1; } @@ -1581,7 +1599,7 @@ class WasmModuleBuilder { } else if (type instanceof WasmArray) { section.emit_u8(kWasmArrayTypeForm); section.emit_type(type.type); - section.emit_u8(1); // Only mutable arrays supported currently. + section.emit_u8(type.mutability ? 1 : 0); } else { section.emit_u8(kWasmFunctionTypeForm); section.emit_u32v(type.params.length); diff --git a/test/unittests/wasm/module-decoder-unittest.cc b/test/unittests/wasm/module-decoder-unittest.cc index 62f5331df6..709d4be1e5 100644 --- a/test/unittests/wasm/module-decoder-unittest.cc +++ b/test/unittests/wasm/module-decoder-unittest.cc @@ -37,6 +37,8 @@ namespace module_decoder_unittest { #define WASM_INIT_EXPR_GLOBAL(index) WASM_GLOBAL_GET(index), kExprEnd #define WASM_INIT_EXPR_STRUCT_NEW(index, ...) \ WASM_STRUCT_NEW_WITH_RTT(index, __VA_ARGS__), kExprEnd +#define WASM_INIT_EXPR_ARRAY_INIT(index, length, ...) \ + WASM_ARRAY_INIT(index, length, __VA_ARGS__), kExprEnd #define WASM_INIT_EXPR_RTT_CANON(index) WASM_RTT_CANON(index), kExprEnd #define REF_NULL_ELEMENT kExprRefNull, kFuncRefCode, kExprEnd @@ -1131,6 +1133,52 @@ TEST_F(WasmModuleVerifyTest, StructNewInitExpr) { "struct.new[1]: expected (rtt 0), found (rtt 0 1) instead"); } +TEST_F(WasmModuleVerifyTest, ArrayInitInitExpr) { + WASM_FEATURE_SCOPE(reftypes); + WASM_FEATURE_SCOPE(typed_funcref); + WASM_FEATURE_SCOPE(gc); + WASM_FEATURE_SCOPE(gc_experiments); + + static const byte basic[] = { + SECTION(Type, ENTRY_COUNT(1), WASM_ARRAY_DEF(kI16Code, true)), + SECTION(Global, ENTRY_COUNT(1), // -- + kRefCode, 0, 0, // type, mutability + WASM_INIT_EXPR_ARRAY_INIT(0, 3, WASM_I32V(10), WASM_I32V(20), + WASM_I32V(30), WASM_RTT_CANON(0)))}; + EXPECT_VERIFIES(basic); + + static const byte type_error[] = { + SECTION(Type, ENTRY_COUNT(2), // -- + WASM_ARRAY_DEF(kI32Code, true), + WASM_ARRAY_DEF(WASM_SEQ(kRefCode, 0), true)), + SECTION( + Global, ENTRY_COUNT(1), // -- + kRefCode, 1, 0, // type, mutability + WASM_INIT_EXPR_ARRAY_INIT(0, 1, WASM_I32V(42), WASM_RTT_CANON(0)))}; + EXPECT_FAILURE_WITH_MSG( + type_error, + "type error in init expression, expected (ref 1), got (ref 0)"); + + static const byte subexpr_type_error[] = { + SECTION(Type, ENTRY_COUNT(1), WASM_ARRAY_DEF(kI64Code, true)), + SECTION(Global, ENTRY_COUNT(1), // -- + kRefCode, 0, 0, // type, mutability + WASM_INIT_EXPR_ARRAY_INIT(0, 2, WASM_I64V(42), WASM_I32V(142), + WASM_RTT_CANON(0)))}; + EXPECT_FAILURE_WITH_MSG(subexpr_type_error, + "array.init[1]: expected i64, found i32 instead"); + + static const byte length_error[] = { + SECTION(Type, ENTRY_COUNT(1), WASM_ARRAY_DEF(kI16Code, true)), + SECTION(Global, ENTRY_COUNT(1), // -- + kRefCode, 0, 0, // type, mutability + WASM_INIT_EXPR_ARRAY_INIT(0, 10, WASM_I32V(10), WASM_I32V(20), + WASM_I32V(30), WASM_RTT_CANON(0)))}; + EXPECT_FAILURE_WITH_MSG( + length_error, + "not enough arguments on the stack for array.init: expected 11, found 4"); +} + TEST_F(WasmModuleVerifyTest, EmptyStruct) { WASM_FEATURE_SCOPE(reftypes); WASM_FEATURE_SCOPE(typed_funcref);