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:
parent
481ad16db6
commit
a6b7f1f3f4
@ -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
|
||||
|
@ -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()) {
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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")
|
||||
|
@ -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, _) \
|
||||
|
@ -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());
|
||||
})();
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user