Revert "[wasm-gc] Implement array.init_from_elem"

This reverts commit 76a07814b2.

Reason for revert: Failing on GC stress: https://ci.chromium.org/ui/p/v8/builders/ci/V8%20Mac64%20GC%20Stress/23140/overview

Original change's description:
> [wasm-gc] Implement array.init_from_elem
>
> Bug: v8:7748
> Change-Id: I65dbb496302045820063bd0f4f9ea054e6a645bd
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3695580
> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#81128}

Bug: v8:7748
Change-Id: Ia72cc121c50af7906e54a1742529bf081c511a8a
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3704506
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Owners-Override: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81143}
This commit is contained in:
Leszek Swirski 2022-06-14 12:43:49 +00:00 committed by V8 LUCI CQ
parent 481ad16db6
commit a6b7f1f3f4
11 changed files with 44 additions and 543 deletions

View File

@ -373,18 +373,9 @@ builtin WasmAllocateArray_Uninitialized(
builtin WasmArrayInitFromSegment(
dataSegment: uint32, offset: uint32, length: uint32, rtt: Map): Object {
const instance = LoadInstanceFromFrame();
try {
const smiOffset =
Convert<PositiveSmi>(offset) otherwise ElementSegmentOutOfBounds;
const smiLength = Convert<PositiveSmi>(length) otherwise ArrayTooLarge;
tail runtime::WasmArrayInitFromSegment(
LoadContextFromInstance(instance), instance, SmiFromUint32(dataSegment),
smiOffset, smiLength, rtt);
} label ElementSegmentOutOfBounds {
tail ThrowWasmTrapElementSegmentOutOfBounds();
} label ArrayTooLarge {
tail ThrowWasmTrapArrayTooLarge();
}
tail runtime::WasmArrayInitFromSegment(
LoadContextFromInstance(instance), instance, SmiFromUint32(dataSegment),
SmiFromUint32(offset), SmiFromUint32(length), rtt);
}
// We put all uint32 parameters at the beginning so that they are assigned to

View File

@ -1900,7 +1900,7 @@ void WasmArray::WasmArrayPrint(std::ostream& os) {
PrintHeader(os, "WasmArray");
wasm::ArrayType* array_type = type();
uint32_t len = length();
os << "\n - element type: " << array_type->element_type().name();
os << "\n - type: " << array_type->element_type().name();
os << "\n - length: " << len;
Address data_ptr = ptr() + WasmArray::kHeaderSize - kHeapObjectTag;
switch (array_type->element_type().kind()) {

View File

@ -69,8 +69,6 @@
#include "src/roots/roots.h"
#include "src/strings/unicode-inl.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/module-instantiate.h"
#include "src/wasm/wasm-result.h"
#include "src/wasm/wasm-value.h"
#endif
@ -1750,46 +1748,6 @@ Handle<WasmArray> Factory::NewWasmArrayFromMemory(uint32_t length,
return handle(result, isolate());
}
Handle<Object> Factory::NewWasmArrayFromElementSegment(
Handle<WasmInstanceObject> instance, const wasm::WasmElemSegment* segment,
uint32_t start_offset, uint32_t length, Handle<Map> map) {
wasm::ValueType element_type = WasmArray::type(*map)->element_type();
DCHECK(element_type.is_reference());
Handle<HeapObject> raw = handle(
AllocateRaw(WasmArray::SizeFor(*map, length), AllocationType::kYoung),
isolate());
{
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);
// We have to initialize the elements to a default value, because we might
// allocate new objects while computing the elements below.
for (int i = 0; static_cast<uint32_t>(i) < length; i++) {
int offset = result.element_offset(i);
TaggedField<Object>::store(result, offset, *undefined_value());
}
}
Handle<WasmArray> result = Handle<WasmArray>::cast(raw);
AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
for (int i = 0; static_cast<uint32_t>(i) < length; i++) {
int offset = result->element_offset(i);
wasm::ValueOrError maybe_element =
wasm::EvaluateInitExpression(&zone, segment->entries[start_offset + i],
element_type, isolate(), instance);
if (wasm::is_error(maybe_element)) {
return handle(Smi::FromEnum(wasm::to_error(maybe_element)), isolate());
}
TaggedField<Object>::store(*result, offset,
*wasm::to_value(maybe_element).to_ref());
}
return result;
}
Handle<WasmStruct> Factory::NewWasmStruct(const wasm::StructType* type,
wasm::WasmValue* args,
Handle<Map> map) {

View File

@ -76,7 +76,6 @@ class WeakCell;
namespace wasm {
class ArrayType;
class StructType;
struct WasmElemSegment;
class WasmValue;
} // namespace wasm
#endif
@ -647,11 +646,6 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Handle<Map> map);
Handle<WasmArray> NewWasmArrayFromMemory(uint32_t length, Handle<Map> map,
Address source);
// Returns a handle to a WasmArray if successful, or a Smi containing a
// {MessageTemplate} if computing the array's elements leads to an error.
Handle<Object> NewWasmArrayFromElementSegment(
Handle<WasmInstanceObject> instance, const wasm::WasmElemSegment* segment,
uint32_t start_offset, uint32_t length, Handle<Map> map);
Handle<SharedFunctionInfo> NewSharedFunctionInfoForWasmExportedFunction(
Handle<String> name, Handle<WasmExportedFunctionData> data);

View File

@ -734,40 +734,18 @@ RUNTIME_FUNCTION(Runtime_WasmArrayInitFromSegment) {
return ThrowWasmError(isolate, MessageTemplate::kWasmTrapArrayTooLarge);
}
if (type->element_type().is_numeric()) {
uint32_t length_in_bytes = length * element_size;
uint32_t length_in_bytes = length * element_size;
DCHECK_EQ(length_in_bytes / element_size, length);
if (!base::IsInBounds<uint32_t>(
offset, length_in_bytes,
instance->data_segment_sizes()[segment_index])) {
return ThrowWasmError(isolate,
MessageTemplate::kWasmTrapDataSegmentOutOfBounds);
}
Address source = instance->data_segment_starts()[segment_index] + offset;
return *isolate->factory()->NewWasmArrayFromMemory(length, rtt, source);
} else {
const wasm::WasmElemSegment* elem_segment =
&instance->module()->elem_segments[segment_index];
if (!base::IsInBounds<size_t>(
offset, length,
instance->dropped_elem_segments()[segment_index]
? 0
: elem_segment->entries.size())) {
return ThrowWasmError(
isolate, MessageTemplate::kWasmTrapElementSegmentOutOfBounds);
}
Handle<Object> result = isolate->factory()->NewWasmArrayFromElementSegment(
instance, elem_segment, offset, length, rtt);
if (result->IsSmi()) {
return ThrowWasmError(
isolate, static_cast<MessageTemplate>(result->ToSmi().value()));
} else {
return *result;
}
DCHECK_EQ(length_in_bytes / element_size, length);
if (!base::IsInBounds<uint32_t>(
offset, length_in_bytes,
instance->data_segment_sizes()[segment_index])) {
return ThrowWasmError(isolate,
MessageTemplate::kWasmTrapDataSegmentOutOfBounds);
}
Address source = instance->data_segment_starts()[segment_index] + offset;
return *isolate->factory()->NewWasmArrayFromMemory(length, rtt, source);
}
namespace {

View File

@ -1975,8 +1975,7 @@ class WasmDecoder : public Decoder {
return length + dst_imm.length + src_imm.length;
}
case kExprArrayInitFromData:
case kExprArrayInitFromDataStatic:
case kExprArrayInitFromElemStatic: {
case kExprArrayInitFromDataStatic: {
ArrayIndexImmediate<validate> array_imm(decoder, pc + length);
IndexImmediate<validate> data_imm(
decoder, pc + length + array_imm.length, "segment index");
@ -2211,7 +2210,6 @@ class WasmDecoder : public Decoder {
case kExprArrayNew:
case kExprArrayNewDefaultWithRtt:
case kExprArrayInitFromDataStatic:
case kExprArrayInitFromElemStatic:
case kExprArrayGet:
case kExprArrayGetS:
case kExprArrayGetU:
@ -4452,53 +4450,6 @@ class WasmFullDecoder : public WasmDecoder<validate, decoding_mode> {
Push(array);
return opcode_length + array_imm.length + data_segment.length;
}
case kExprArrayInitFromElemStatic: {
ArrayIndexImmediate<validate> array_imm(this,
this->pc_ + opcode_length);
if (!this->Validate(this->pc_ + opcode_length, array_imm)) return 0;
ValueType element_type = array_imm.array_type->element_type();
if (element_type.is_numeric()) {
this->DecodeError(
"array.init_from_elem can only be used with reference-type "
"arrays, found array type #%d instead",
array_imm.index);
return 0;
}
const byte* elem_index_pc =
this->pc_ + opcode_length + array_imm.length;
IndexImmediate<validate> elem_segment(this, elem_index_pc,
"data segment");
if (!this->ValidateElementSegment(elem_index_pc, elem_segment)) {
return 0;
}
ValueType rtt_type = ValueType::Rtt(array_imm.index);
Value rtt = CreateValue(rtt_type);
CALL_INTERFACE_IF_OK_AND_REACHABLE(RttCanon, array_imm.index, &rtt);
Push(rtt);
Value array =
CreateValue(ValueType::Ref(array_imm.index, kNonNullable));
ValueType elem_segment_type =
this->module_->elem_segments[elem_segment.index].type;
if (V8_UNLIKELY(
!IsSubtypeOf(elem_segment_type, element_type, this->module_))) {
this->DecodeError(
"array.init_from_elem: segment type %s is not a subtype of array "
"element type %s",
elem_segment_type.name().c_str(), element_type.name().c_str());
return 0;
}
Value length = Peek(1, 1, kWasmI32);
Value offset = Peek(2, 0, kWasmI32);
CALL_INTERFACE_IF_OK_AND_REACHABLE(ArrayInitFromSegment, array_imm,
elem_segment, offset, length, rtt,
&array);
Drop(3); // rtt, length, offset
Push(array);
return opcode_length + array_imm.length + elem_segment.length;
}
case kExprArrayGetS:
case kExprArrayGetU: {
NON_CONST_ONLY

View File

@ -209,62 +209,35 @@ void InitExprInterface::ArrayInit(FullDecoder* decoder,
void InitExprInterface::ArrayInitFromSegment(
FullDecoder* decoder, const ArrayIndexImmediate<validate>& array_imm,
const IndexImmediate<validate>& segment_imm, const Value& offset_value,
const IndexImmediate<validate>& data_segment_imm, const Value& offset_value,
const Value& length_value, const Value& rtt, Value* result) {
if (!generate_value()) return;
uint32_t length = length_value.runtime_value.to_u32();
uint32_t offset = offset_value.runtime_value.to_u32();
const WasmDataSegment& data_segment =
module_->data_segments[data_segment_imm.index];
uint32_t length_in_bytes =
length * array_imm.array_type->element_type().value_kind_size();
// Error handling.
if (length >
static_cast<uint32_t>(WasmArray::MaxLength(array_imm.array_type))) {
error_ = MessageTemplate::kWasmTrapArrayTooLarge;
return;
}
ValueType element_type = array_imm.array_type->element_type();
ValueType result_type =
ValueType::Ref(HeapType(array_imm.index), kNonNullable);
if (element_type.is_numeric()) {
const WasmDataSegment& data_segment =
module_->data_segments[segment_imm.index];
uint32_t length_in_bytes =
length * array_imm.array_type->element_type().value_kind_size();
if (!base::IsInBounds<uint32_t>(offset, length_in_bytes,
data_segment.source.length())) {
error_ = MessageTemplate::kWasmTrapDataSegmentOutOfBounds;
return;
}
Address source =
instance_->data_segment_starts()[segment_imm.index] + offset;
Handle<WasmArray> array_value = isolate_->factory()->NewWasmArrayFromMemory(
length, Handle<Map>::cast(rtt.runtime_value.to_ref()), source);
result->runtime_value = WasmValue(array_value, result_type);
} else {
const wasm::WasmElemSegment* elem_segment =
&decoder->module_->elem_segments[segment_imm.index];
// A constant expression should not observe if a passive segment is dropped.
// However, it should consider active and declarative segments as empty.
if (!base::IsInBounds<size_t>(
offset, length,
elem_segment->status == WasmElemSegment::kStatusPassive
? elem_segment->entries.size()
: 0)) {
error_ = MessageTemplate::kWasmTrapElementSegmentOutOfBounds;
return;
}
Handle<Object> array_object =
isolate_->factory()->NewWasmArrayFromElementSegment(
instance_, elem_segment, offset, length,
Handle<Map>::cast(rtt.runtime_value.to_ref()));
if (array_object->IsSmi()) {
// A smi result stands for an error code.
error_ = static_cast<MessageTemplate>(array_object->ToSmi().value());
} else {
result->runtime_value = WasmValue(array_object, result_type);
}
if (!base::IsInBounds<uint32_t>(offset, length_in_bytes,
data_segment.source.length())) {
error_ = MessageTemplate::kWasmTrapDataSegmentOutOfBounds;
return;
}
Address source =
instance_->data_segment_starts()[data_segment_imm.index] + offset;
Handle<WasmArray> array_value = isolate_->factory()->NewWasmArrayFromMemory(
length, Handle<Map>::cast(rtt.runtime_value.to_ref()), source);
result->runtime_value = WasmValue(
array_value, ValueType::Ref(HeapType(array_imm.index), kNonNullable));
}
void InitExprInterface::RttCanon(FullDecoder* decoder, uint32_t type_index,

View File

@ -423,7 +423,6 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(ArrayInitStatic, "array.init_static")
CASE_OP(ArrayInitFromData, "array.init_from_data")
CASE_OP(ArrayInitFromDataStatic, "array.init_from_data_static")
CASE_OP(ArrayInitFromElemStatic, "array.init_from_elem_static")
CASE_OP(I31New, "i31.new")
CASE_OP(I31GetS, "i31.get_s")
CASE_OP(I31GetU, "i31.get_u")

View File

@ -691,14 +691,13 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(ArrayGetU, 0xfb15, _) \
V(ArraySet, 0xfb16, _) \
V(ArrayLen, 0xfb17, _) \
V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \
V(ArrayInit, 0xfb19, _) \
V(ArrayInitStatic, 0xfb1a, _) \
V(ArrayCopy, 0xfb18, _) /* not standardized - V8 experimental */ \
V(ArrayInit, 0xfb19, _) /* not standardized - V8 experimental */ \
V(ArrayInitStatic, 0xfb1a, _) /* not standardized - V8 experimental */ \
V(ArrayNew, 0xfb1b, _) \
V(ArrayNewDefault, 0xfb1c, _) \
V(ArrayInitFromData, 0xfb1e, _) \
V(ArrayInitFromDataStatic, 0xfb1d, _) \
V(ArrayInitFromElemStatic, 0xfb1f, _) \
V(ArrayInitFromData, 0xfb1e, _) /* not stand. - V8 experimental */ \
V(ArrayInitFromDataStatic, 0xfb1d, _) /* not stand. - V8 experimental */ \
V(I31New, 0xfb20, _) \
V(I31GetS, 0xfb21, _) \
V(I31GetU, 0xfb22, _) \

View File

@ -1,331 +0,0 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-gc
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestArrayInitFromElemStatic() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmOptRefType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
function makeStruct(element) {
return WasmInitExpr.StructNew(
struct_type_index, [WasmInitExpr.I32Const(element)]);
}
builder.addTable(kWasmAnyRef, 10, 10);
let elems = [10, -10, 42, 55];
let passive_segment = builder.addPassiveElementSegment(
[makeStruct(elems[0]), makeStruct(elems[1]),
WasmInitExpr.RefNull(struct_type_index)],
struct_type);
let active_segment = builder.addActiveElementSegment(
0, WasmInitExpr.I32Const(0), [makeStruct(elems[2]), makeStruct(elems[3])],
struct_type);
function generator(name, segment) {
builder.addFunction(name, makeSig([kWasmI32, kWasmI32], [kWasmI32]))
.addBody([
kExprI32Const, 0, // offset
kExprLocalGet, 0, // length
kGCPrefix, kExprArrayInitFromElemStatic, array_type_index,
segment,
kExprLocalGet, 1, // index in the array
kGCPrefix, kExprArrayGet, array_type_index,
kGCPrefix, kExprStructGet, struct_type_index, 0])
.exportFunc()
}
generator("init_and_get", passive_segment);
generator("init_and_get_active", active_segment);
builder.addFunction("drop", kSig_v_v)
.addBody([kNumericPrefix, kExprElemDrop, passive_segment])
.exportFunc();
let instance = builder.instantiate();
let init_and_get = instance.exports.init_and_get;
let init_and_get_active = instance.exports.init_and_get_active;
// Initializing from a passive segment works. The third element is null, so
// we get a null dereference.
assertEquals(elems[0], init_and_get(3, 0));
assertEquals(elems[1], init_and_get(3, 1));
assertTraps(kTrapNullDereference, () => init_and_get(3, 2));
// The array has the correct length.
assertTraps(kTrapArrayOutOfBounds, () => init_and_get(3, 3));
// Too large arrays are disallowed, in and out of Smi range.
assertTraps(kTrapArrayTooLarge, () => init_and_get(1000000000, 10));
assertTraps(kTrapArrayTooLarge, () => init_and_get(1 << 31, 10));
// Element is out of bounds.
assertTraps(kTrapElementSegmentOutOfBounds, () => init_and_get(5, 0));
// Now drop the segment.
instance.exports.drop();
// A 0-length array should still be created...
assertTraps(kTrapArrayOutOfBounds, () => init_and_get(0, 0));
// ... but not a longer one.
assertTraps(kTrapElementSegmentOutOfBounds, () => init_and_get(1, 0));
// Same holds for an active segment.
assertTraps(kTrapArrayOutOfBounds, () => init_and_get_active(0, 0));
assertTraps(kTrapElementSegmentOutOfBounds, () => init_and_get_active(1, 0));
})();
(function TestArrayInitFromElemStaticConstant() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmOptRefType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let array_type = wasmOptRefType(array_type_index);
function makeStruct(element) {
return WasmInitExpr.StructNew(
struct_type_index, [WasmInitExpr.I32Const(element)]);
}
builder.addTable(kWasmAnyRef, 10, 10);
let table = 0;
let elems = [10, -10, 42, 55];
let passive_segment = builder.addPassiveElementSegment(
[makeStruct(elems[0]), makeStruct(elems[1]),
WasmInitExpr.RefNull(struct_type_index)],
struct_type);
let active_segment = builder.addActiveElementSegment(
0, WasmInitExpr.I32Const(0), [makeStruct(elems[2]), makeStruct(elems[3])],
struct_type);
let array_segment = builder.addPassiveElementSegment(
[WasmInitExpr.ArrayInitFromElemStatic(
array_type_index, passive_segment,
[WasmInitExpr.I32Const(0), WasmInitExpr.I32Const(3)]),
WasmInitExpr.ArrayInitFromElemStatic(
array_type_index, active_segment,
[WasmInitExpr.I32Const(0), WasmInitExpr.I32Const(0)])],
array_type
);
builder.addFunction("init", kSig_v_v)
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 2,
kNumericPrefix, kExprTableInit, array_segment, table])
.exportFunc();
builder.addFunction("table_get", makeSig([kWasmI32, kWasmI32], [kWasmI32]))
.addBody([
kExprLocalGet, 0, // offset in table
kExprTableGet, table,
kGCPrefix, kExprRefAsData,
kGCPrefix, kExprRefCastStatic, array_type_index,
kExprLocalGet, 1, // index in the array
kGCPrefix, kExprArrayGet, array_type_index,
kGCPrefix, kExprStructGet, struct_type_index, 0])
.exportFunc()
builder.addFunction("drop", kSig_v_v)
.addBody([kNumericPrefix, kExprElemDrop, passive_segment])
.exportFunc();
let instance = builder.instantiate();
// First, initialize the table.
instance.exports.init();
let table_get = instance.exports.table_get;
// Initializing from a passive segment works. The third element is null, so
// we get a null dereference.
assertEquals(elems[0], table_get(0, 0));
assertEquals(elems[1], table_get(0, 1));
assertTraps(kTrapNullDereference, () => table_get(0, 2));
// The array has the correct length.
assertTraps(kTrapArrayOutOfBounds, () => table_get(0, 3));
// The array generated from the active segment should have length 0.
assertTraps(kTrapArrayOutOfBounds, () => table_get(1, 0));
// Now drop the segment with the array elements and reload the table.
instance.exports.drop();
instance.exports.init();
// Nothing should change: a constant expression should not observe the dropped
// segments.
assertEquals(elems[0], table_get(0, 0));
assertEquals(elems[1], table_get(0, 1));
assertTraps(kTrapNullDereference, () => table_get(0, 2));
assertTraps(kTrapArrayOutOfBounds, () => table_get(0, 3));
})();
(function TestArrayInitFromElemStaticMistypedSegment() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmOptRefType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let passive_segment = builder.addPassiveElementSegment([
WasmInitExpr.RefNull(array_type_index)],
wasmOptRefType(array_type_index));
builder.addFunction("mistyped", makeSig([kWasmI32, kWasmI32], [kWasmI32]))
.addBody([
kExprI32Const, 0, // offset
kExprLocalGet, 0, // length
kGCPrefix, kExprArrayInitFromElemStatic, array_type_index,
passive_segment,
kExprLocalGet, 1, // index in the array
kGCPrefix, kExprArrayGet, array_type_index,
kGCPrefix, kExprStructGet, struct_type_index, 0])
.exportFunc()
assertThrows(() => builder.instantiate(), WebAssembly.CompileError,
/segment type.*is not a subtype of array element type.*/);
})();
// Element segments are defined after globals, so currently it is not valid
// to refer to an element segment in the global section.
(function TestArrayInitFromElemInGlobal() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmOptRefType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let passive_segment = builder.addPassiveElementSegment([
WasmInitExpr.RefNull(struct_type_index)],
struct_type_index);
builder.addGlobal(wasmOptRefType(array_type_index), false,
WasmInitExpr.ArrayInitFromElemStatic(
array_type_index, passive_segment,
[WasmInitExpr.I32Const(0), // offset
WasmInitExpr.I32Const(1) // length
])
);
assertThrows(() => builder.instantiate(), WebAssembly.CompileError,
/invalid element segment index/);
})();
(function TestArrayInitFromElemStaticConstantArrayTooLarge() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmOptRefType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let array_type = wasmOptRefType(array_type_index);
function makeStruct(element) {
return WasmInitExpr.StructNew(
struct_type_index, [WasmInitExpr.I32Const(element)]);
}
builder.addTable(kWasmAnyRef, 10, 10);
let table = 0;
let elems = [10, -10];
let passive_segment = builder.addPassiveElementSegment(
[makeStruct(elems[0]), makeStruct(elems[1]),
WasmInitExpr.RefNull(struct_type_index)],
struct_type);
let array_segment = builder.addPassiveElementSegment(
[WasmInitExpr.ArrayInitFromElemStatic(
array_type_index, passive_segment,
[WasmInitExpr.I32Const(0), WasmInitExpr.I32Const(1 << 30)])],
array_type
);
builder.addFunction("init", kSig_v_v)
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 1,
kNumericPrefix, kExprTableInit, array_segment, table])
.exportFunc();
let instance = builder.instantiate();
assertTraps(kTrapArrayTooLarge, () => instance.exports.init());
})();
(function TestArrayInitFromElemStaticConstantElementSegmentOutOfBounds() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmOptRefType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let array_type = wasmOptRefType(array_type_index);
function makeStruct(element) {
return WasmInitExpr.StructNew(
struct_type_index, [WasmInitExpr.I32Const(element)]);
}
builder.addTable(kWasmAnyRef, 10, 10);
let table = 0;
let elems = [10, -10];
let passive_segment = builder.addPassiveElementSegment(
[makeStruct(elems[0]), makeStruct(elems[1]),
WasmInitExpr.RefNull(struct_type_index)],
struct_type);
let array_segment = builder.addPassiveElementSegment(
[WasmInitExpr.ArrayInitFromElemStatic(
array_type_index, passive_segment,
[WasmInitExpr.I32Const(0), WasmInitExpr.I32Const(10)])],
array_type
);
builder.addFunction("init", kSig_v_v)
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 1,
kNumericPrefix, kExprTableInit, array_segment, table])
.exportFunc();
let instance = builder.instantiate();
assertTraps(kTrapElementSegmentOutOfBounds, () => instance.exports.init());
})();
(function TestArrayInitFromElemStaticConstantActiveSegment() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct_type_index = builder.addStruct([makeField(kWasmI32, false)]);
let struct_type = wasmOptRefType(struct_type_index);
let array_type_index = builder.addArray(struct_type, true);
let array_type = wasmOptRefType(array_type_index);
function makeStruct(element) {
return WasmInitExpr.StructNew(
struct_type_index, [WasmInitExpr.I32Const(element)]);
}
builder.addTable(kWasmAnyRef, 10, 10);
let table = 0;
let elems = [10, -10];
let active_segment = builder.addActiveElementSegment(
table, WasmInitExpr.I32Const(0),
[makeStruct(elems[0]), makeStruct(elems[1]),
WasmInitExpr.RefNull(struct_type_index)],
struct_type);
let array_segment = builder.addPassiveElementSegment(
[WasmInitExpr.ArrayInitFromElemStatic(
array_type_index, active_segment,
[WasmInitExpr.I32Const(0), WasmInitExpr.I32Const(3)])],
array_type
);
builder.addFunction("init", kSig_v_v)
.addBody([kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 1,
kNumericPrefix, kExprTableInit, array_segment, table])
.exportFunc();
let instance = builder.instantiate();
// An active segment counts as having 0 length.
assertTraps(kTrapElementSegmentOutOfBounds, () => instance.exports.init());
})();

View File

@ -501,7 +501,6 @@ let kExprArrayNew = 0x1b;
let kExprArrayNewDefault = 0x1c;
let kExprArrayInitFromData = 0x1e;
let kExprArrayInitFromDataStatic = 0x1d;
let kExprArrayInitFromElemStatic = 0x1f;
let kExprI31New = 0x20;
let kExprI31GetS = 0x21;
let kExprI31GetU = 0x22;
@ -909,8 +908,6 @@ let kTrapDataSegmentOutOfBounds = 9;
let kTrapElementSegmentOutOfBounds = 10;
let kTrapRethrowNull = 11;
let kTrapArrayTooLarge = 12;
let kTrapArrayOutOfBounds = 13;
let kTrapNullDereference = 14;
let kTrapMsgs = [
'unreachable', // --
@ -925,9 +922,7 @@ let kTrapMsgs = [
'data segment out of bounds', // --
'element segment out of bounds', // --
'rethrowing null value', // --
'requested new array is too large', // --
'array element access out of bounds', // --
'dereferencing a null pointer' // --
'requested new array is too large' // --
];
// This requires test/mjsunit/mjsunit.js.
@ -1098,14 +1093,13 @@ class Binary {
break;
case kExprArrayInitFromData:
case kExprArrayInitFromDataStatic:
case kExprArrayInitFromElemStatic:
for (let operand of expr.operands) {
this.emit_init_expr_recursive(operand);
}
this.emit_u8(kGCPrefix);
this.emit_u8(expr.kind);
this.emit_u32v(expr.array_index);
this.emit_u32v(expr.segment_index);
this.emit_u32v(expr.data_segment);
break;
case kExprRttCanon:
this.emit_u8(kGCPrefix);
@ -1294,23 +1288,19 @@ class WasmInitExpr {
static ArrayInitStatic(type, args) {
return {kind: kExprArrayInitStatic, value: type, operands: args};
}
static ArrayInitFromData(array_index, segment_index, args, builder) {
static ArrayInitFromData(array_index, data_segment, args, builder) {
// array.init_from_data means we need to pull the data count section before
// any section that may include init. expressions.
builder.early_data_count_section = true;
return {kind: kExprArrayInitFromData, array_index: array_index,
segment_index: segment_index, operands: args};
data_segment: data_segment, operands: args};
}
static ArrayInitFromDataStatic(array_index, segment_index, args, builder) {
static ArrayInitFromDataStatic(array_index, data_segment, args, builder) {
// array.init_from_data means we need to pull the data count section before
// any section that may include init. expressions.
builder.early_data_count_section = true;
return {kind: kExprArrayInitFromDataStatic, array_index: array_index,
segment_index: segment_index, operands: args};
}
static ArrayInitFromElemStatic(array_index, segment_index, args) {
return {kind: kExprArrayInitFromElemStatic, array_index: array_index,
segment_index: segment_index, operands: args};
data_segment: data_segment, operands: args};
}
static RttCanon(type) {
return {kind: kExprRttCanon, value: type};
@ -1365,7 +1355,6 @@ class WasmGlobalBuilder {
class WasmTableBuilder {
constructor(module, type, initial_size, max_size, init_expr) {
// TODO(manoskouk): Add the table index.
this.module = module;
this.type = type;
this.initial_size = initial_size;