diff --git a/src/heap/factory.cc b/src/heap/factory.cc index cefc6cc6aa..9dcd46f2c2 100644 --- a/src/heap/factory.cc +++ b/src/heap/factory.cc @@ -1675,6 +1675,36 @@ Handle Factory::NewWasmCapiFunctionData( return handle(result, isolate()); } +Handle Factory::NewWasmArray(const wasm::ArrayType* type, + uint32_t length, + wasm::WasmValue initial_value, + Handle map) { + HeapObject raw = + AllocateRaw(WasmArray::SizeFor(*map, length), AllocationType::kYoung); + DisallowGarbageCollection no_gc; + raw.set_map_after_allocation(*map); + WasmArray result = WasmArray::cast(raw); + result.set_raw_properties_or_hash(*empty_fixed_array(), kRelaxedStore); + result.set_length(length); + if (type->element_type().is_numeric()) { + if (initial_value.zero_byte_representation()) { + memset(reinterpret_cast(result.ElementAddress(0)), 0, + length * type->element_type().value_kind_size()); + } else { + wasm::WasmValue packed = initial_value.Packed(type->element_type()); + for (uint32_t i = 0; i < length; i++) { + Address address = result.ElementAddress(i); + packed.CopyTo(reinterpret_cast(address)); + } + } + } else { + for (uint32_t i = 0; i < length; i++) { + result.SetTaggedElement(i, initial_value.to_ref()); + } + } + return handle(result, isolate()); +} + Handle Factory::NewWasmArrayFromElements( const wasm::ArrayType* type, const std::vector& elements, Handle map) { @@ -1695,8 +1725,7 @@ Handle Factory::NewWasmArrayFromElements( } } else { for (uint32_t i = 0; i < length; i++) { - int offset = result.element_offset(i); - TaggedField::store(result, offset, *elements[i].to_ref()); + result.SetTaggedElement(i, elements[i].to_ref()); } } return handle(result, isolate()); diff --git a/src/heap/factory.h b/src/heap/factory.h index 8ef592dfed..69de19b2ab 100644 --- a/src/heap/factory.h +++ b/src/heap/factory.h @@ -648,6 +648,9 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase { Handle suspender, wasm::OnResume on_resume); Handle NewWasmStruct(const wasm::StructType* type, wasm::WasmValue* args, Handle map); + Handle NewWasmArray(const wasm::ArrayType* type, uint32_t length, + wasm::WasmValue initial_value, + Handle map); Handle NewWasmArrayFromElements( const wasm::ArrayType* type, const std::vector& elements, Handle map); diff --git a/src/wasm/constant-expression-interface.cc b/src/wasm/constant-expression-interface.cc index 207506fdc9..d2a9b18e5c 100644 --- a/src/wasm/constant-expression-interface.cc +++ b/src/wasm/constant-expression-interface.cc @@ -191,7 +191,35 @@ void ConstantExpressionInterface::StructNewDefault( WasmValue(isolate_->factory()->NewWasmStruct( imm.struct_type, field_values.data(), Handle::cast(rtt.runtime_value.to_ref())), - ValueType::Ref(HeapType(imm.index))); + ValueType::Ref(imm.index)); +} + +void ConstantExpressionInterface::ArrayNewWithRtt( + FullDecoder* decoder, const ArrayIndexImmediate& imm, + const Value& length, const Value& initial_value, const Value& rtt, + Value* result) { + if (!generate_value()) return; + if (length.runtime_value.to_u32() > + static_cast(WasmArray::MaxLength(imm.array_type))) { + error_ = MessageTemplate::kWasmTrapArrayTooLarge; + return; + } + result->runtime_value = + WasmValue(isolate_->factory()->NewWasmArray( + imm.array_type, length.runtime_value.to_u32(), + initial_value.runtime_value, + Handle::cast(rtt.runtime_value.to_ref())), + ValueType::Ref(imm.index)); +} + +void ConstantExpressionInterface::ArrayNewDefault( + FullDecoder* decoder, const ArrayIndexImmediate& imm, + const Value& length, const Value& rtt, Value* result) { + if (!generate_value()) return; + Value initial_value(decoder->pc(), imm.array_type->element_type()); + initial_value.runtime_value = + DefaultValueForType(imm.array_type->element_type(), isolate_); + return ArrayNewWithRtt(decoder, imm, length, initial_value, rtt, result); } void ConstantExpressionInterface::ArrayNewFixed( diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h index 11b29617d5..fd338b42de 100644 --- a/src/wasm/function-body-decoder-impl.h +++ b/src/wasm/function-body-decoder-impl.h @@ -978,6 +978,11 @@ struct ControlBase : public PcForErrors { const Value& rtt, const Value args[], Value* result) \ F(StructNewDefault, const StructIndexImmediate& imm, \ const Value& rtt, Value* result) \ + F(ArrayNewWithRtt, const ArrayIndexImmediate& imm, \ + const Value& length, const Value& initial_value, const Value& rtt, \ + Value* result) \ + F(ArrayNewDefault, const ArrayIndexImmediate& imm, \ + const Value& length, const Value& rtt, Value* result) \ F(ArrayNewFixed, const ArrayIndexImmediate& imm, \ const base::Vector& elements, const Value& rtt, Value* result) \ F(ArrayNewSegment, const ArrayIndexImmediate& array_imm, \ @@ -1084,11 +1089,6 @@ struct ControlBase : public PcForErrors { const FieldImmediate& field, bool is_signed, Value* result) \ F(StructSet, const Value& struct_object, \ const FieldImmediate& field, const Value& field_value) \ - F(ArrayNewWithRtt, const ArrayIndexImmediate& imm, \ - const Value& length, const Value& initial_value, const Value& rtt, \ - Value* result) \ - F(ArrayNewDefault, const ArrayIndexImmediate& imm, \ - const Value& length, const Value& rtt, Value* result) \ F(ArrayGet, const Value& array_obj, \ const ArrayIndexImmediate& imm, const Value& index, \ bool is_signed, Value* result) \ @@ -4316,7 +4316,6 @@ class WasmFullDecoder : public WasmDecoder { } case kExprArrayNew: case kExprArrayNewWithRtt: { - NON_CONST_ONLY ArrayIndexImmediate imm(this, this->pc_ + opcode_length); if (!this->Validate(this->pc_ + opcode_length, imm)) return 0; ValueType rtt_type = ValueType::Rtt(imm.index); @@ -4338,7 +4337,6 @@ class WasmFullDecoder : public WasmDecoder { } case kExprArrayNewDefault: case kExprArrayNewDefaultWithRtt: { - NON_CONST_ONLY ArrayIndexImmediate imm(this, this->pc_ + opcode_length); if (!this->Validate(this->pc_ + opcode_length, imm)) return 0; if (!VALIDATE(imm.array_type->element_type().is_defaultable())) { diff --git a/src/wasm/wasm-value.h b/src/wasm/wasm-value.h index e7f1b71b90..47db882efc 100644 --- a/src/wasm/wasm-value.h +++ b/src/wasm/wasm-value.h @@ -209,6 +209,13 @@ class WasmValue { } } + bool zero_byte_representation() { + DCHECK(type().is_numeric()); + uint32_t byte_count = type().value_kind_size(); + return static_cast(std::count( + bit_pattern_, bit_pattern_ + byte_count, 0)) == byte_count; + } + private: ValueType type_; uint8_t bit_pattern_[16]; diff --git a/test/fuzzer/wasm-fuzzer-common.cc b/test/fuzzer/wasm-fuzzer-common.cc index 19ef3f0975..1347e403b7 100644 --- a/test/fuzzer/wasm-fuzzer-common.cc +++ b/test/fuzzer/wasm-fuzzer-common.cc @@ -393,7 +393,7 @@ class InitExprInterface { os_ << "kWasmGlobalGet, " << index(imm.index); } - // The following three operations assume non-rtt versions of the instructions. + // The following operations assume non-rtt versions of the instructions. void StructNewWithRtt(FullDecoder* decoder, const StructIndexImmediate& imm, const Value& rtt, const Value args[], Value* result) { @@ -406,6 +406,19 @@ class InitExprInterface { os_ << "kGCPrefix, kExprStructNewDefault, " << index(imm.index); } + void ArrayNewWithRtt(FullDecoder* decoder, + const ArrayIndexImmediate& imm, + const Value& length, const Value& initial_value, + const Value& rtt, Value* result) { + os_ << "kGCPrefix, kExprArrayNew, " << index(imm.index); + } + + void ArrayNewDefault(FullDecoder* decoder, + const ArrayIndexImmediate& imm, + const Value& length, const Value& rtt, Value* result) { + os_ << "kGCPrefix, kExprArrayNewDefault, " << index(imm.index); + } + void ArrayNewFixed(FullDecoder* decoder, const ArrayIndexImmediate& imm, const base::Vector& elements, const Value& rtt, diff --git a/test/mjsunit/wasm/reference-globals.js b/test/mjsunit/wasm/reference-globals.js index 235e8b55aa..ff068ed085 100644 --- a/test/mjsunit/wasm/reference-globals.js +++ b/test/mjsunit/wasm/reference-globals.js @@ -273,6 +273,72 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); assertEquals(element2_value, instance.exports.element2()); })(); +(function TestArrayNew() { + print(arguments.callee.name); + + var builder = new WasmModuleBuilder(); + var struct_index = builder.addStruct([makeField(kWasmI64, true)]); + var array_num_index = builder.addArray(kWasmI64, true); + var array_ref_index = builder.addArray(wasmRefNullType(struct_index), true); + + let elem1 = -44; + let elem2 = 15; + let length = 20; + + let global_elem_1 = builder.addGlobal(kWasmI64, false, wasmI64Const(elem1)); + let global_elem_2 = builder.addGlobal(kWasmI64, false, wasmI64Const(elem2)); + let global_length = builder.addGlobal(kWasmI32, false, wasmI32Const(length)); + + var global_array_1 = builder.addGlobal( + wasmRefType(array_num_index), false, + [kExprGlobalGet, global_elem_1.index, + kExprGlobalGet, global_length.index, + kGCPrefix, kExprArrayNew, array_num_index]); + + var global_array_2 = builder.addGlobal( + wasmRefType(array_ref_index), false, + [kExprGlobalGet, global_elem_2.index, + kGCPrefix, kExprStructNew, struct_index, + kExprGlobalGet, global_length.index, + kGCPrefix, kExprArrayNew, array_ref_index]); + + builder.addFunction("get_elements", kSig_l_i) + .addBody([ + kExprGlobalGet, global_array_1.index, + kExprLocalGet, 0, + kGCPrefix, kExprArrayGet, array_num_index, + kExprGlobalGet, global_array_2.index, + kExprLocalGet, 0, + kGCPrefix, kExprArrayGet, array_ref_index, + kGCPrefix, kExprStructGet, struct_index, 0, + kExprI64Add]) + .exportFunc(); + + var instance = builder.instantiate({}); + + let result = BigInt(elem1 + elem2); + + assertEquals(result, instance.exports.get_elements(0)); + assertEquals(result, instance.exports.get_elements(length / 2)); + assertEquals(result, instance.exports.get_elements(length - 1)); + assertTraps(kTrapArrayOutOfBounds, + () => instance.exports.get_elements(length)); +})(); + +(function TestArrayNewArrayTooLarge() { + print(arguments.callee.name); + + var builder = new WasmModuleBuilder(); + var array_num_index = builder.addArray(kWasmI64, true); + + builder.addGlobal( + wasmRefType(array_num_index), false, + [...wasmI32Const(0x8ffffff), + kGCPrefix, kExprArrayNewDefault, array_num_index]); + + assertTraps(kTrapArrayTooLarge, () => builder.instantiate({})); +})(); + (function TestI31RefConstantExpr() { print(arguments.callee.name); let builder = new WasmModuleBuilder(); diff --git a/test/mjsunit/wasm/wasm-module-builder.js b/test/mjsunit/wasm/wasm-module-builder.js index 9a83fbc622..40fde891b7 100644 --- a/test/mjsunit/wasm/wasm-module-builder.js +++ b/test/mjsunit/wasm/wasm-module-builder.js @@ -182,6 +182,7 @@ let kSig_i_l = makeSig([kWasmI64], [kWasmI32]); let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]); let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]); let kSig_v_iiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []); +let kSig_l_i = makeSig([kWasmI32], [kWasmI64]); let kSig_f_ff = makeSig([kWasmF32, kWasmF32], [kWasmF32]); let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]); let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]); @@ -939,7 +940,7 @@ let kTrapMsgs = [ // This requires test/mjsunit/mjsunit.js. function assertTraps(trap, code) { - assertThrows(code, WebAssembly.RuntimeError, kTrapMsgs[trap]); + assertThrows(code, WebAssembly.RuntimeError, new RegExp(kTrapMsgs[trap])); } function assertTrapsOneOf(traps, code) {