[wasm] Unify treatment of expressions in elem. segments
We unify the implementation of element segment expression entries with other initializer expressions: we represent them with a {WireBytesRef} and decode them with {InitExprInterface}. Except for reducing code duplication, this also fixes a bug where {global.get} entries in element segments could reference invalid globals. Changes: - Change {WasmElemSegment::Entry} to a union of a {WireBytesRef} initializer expression and a {uint32_t} function index. - In module-decoder, change parsing of expression entries to use {consume_init_expr}. Add type checking to {consume_element_func_index}, to complement type checking happening in {consume_init_expr}. - In module-instantiate.cc: - Move instantiation of indirect tables before loading of element segments. This way, when we call {UpdateDispatchTables} in {SetTableEntry}, the indirect table for the current table will also be updated. - Consolidate table entry instantiation into {SetTableEntry}, which handles lazily instantiated functions, or dispatches to {WasmTableObject::Set}. - Rename {InitializeIndirectFunctionTables} to {InitializeNonDefaultableTables}. - Change {InitializeNonDefaultableTables} and {LoadElemSegmentImpl} to use {EvaluateInitExpression}. - Add a test to exclude mutable/non-imported globals from the element section. - Update tests as needed. - Update .js module emission in wasm-fuzzer-common. Change-Id: I29c541bbca8531e8d0312ed95869c8e78a5a0c57 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3364082 Reviewed-by: Andreas Haas <ahaas@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#78476}
This commit is contained in:
parent
7bf42ad6a4
commit
e9440c45fa
@ -998,20 +998,13 @@ class ModuleDecoderImpl : public Decoder {
|
||||
consume_count("number of elements", max_table_init_entries());
|
||||
|
||||
for (uint32_t j = 0; j < num_elem; j++) {
|
||||
WasmElemSegment::Entry init =
|
||||
using Entry = WasmElemSegment::Entry;
|
||||
Entry entry =
|
||||
segment.element_type == WasmElemSegment::kExpressionElements
|
||||
? consume_element_expr()
|
||||
: WasmElemSegment::Entry(WasmElemSegment::Entry::kRefFuncEntry,
|
||||
consume_element_func_index());
|
||||
? Entry(consume_init_expr(module_.get(), segment.type))
|
||||
: Entry(consume_element_func_index(segment.type));
|
||||
if (failed()) return;
|
||||
if (!IsSubtypeOf(TypeOf(init), segment.type, module_.get())) {
|
||||
errorf(pc_,
|
||||
"Invalid type in the init expression. The expected type is "
|
||||
"'%s', but the actual type is '%s'.",
|
||||
segment.type.name().c_str(), TypeOf(init).name().c_str());
|
||||
return;
|
||||
}
|
||||
segment.entries.push_back(init);
|
||||
segment.entries.push_back(entry);
|
||||
}
|
||||
module_->elem_segments.push_back(std::move(segment));
|
||||
}
|
||||
@ -1504,18 +1497,6 @@ class ModuleDecoderImpl : public Decoder {
|
||||
AccountingAllocator allocator_;
|
||||
Zone init_expr_zone_{&allocator_, "initializer expression zone"};
|
||||
|
||||
ValueType TypeOf(WasmElemSegment::Entry entry) {
|
||||
switch (entry.kind) {
|
||||
case WasmElemSegment::Entry::kGlobalGetEntry:
|
||||
return module_->globals[entry.index].type;
|
||||
case WasmElemSegment::Entry::kRefFuncEntry:
|
||||
return ValueType::Ref(module_->functions[entry.index].sig_index,
|
||||
kNonNullable);
|
||||
case WasmElemSegment::Entry::kRefNullEntry:
|
||||
return ValueType::Ref(entry.index, kNullable);
|
||||
}
|
||||
}
|
||||
|
||||
bool has_seen_unordered_section(SectionCode section_code) {
|
||||
return seen_unordered_sections_ & (1 << section_code);
|
||||
}
|
||||
@ -2046,51 +2027,23 @@ class ModuleDecoderImpl : public Decoder {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t consume_element_func_index() {
|
||||
uint32_t consume_element_func_index(ValueType expected) {
|
||||
WasmFunction* func = nullptr;
|
||||
const byte* initial_pc = pc();
|
||||
uint32_t index =
|
||||
consume_func_index(module_.get(), &func, "element function index");
|
||||
if (failed()) return index;
|
||||
func->declared = true;
|
||||
DCHECK_NE(func, nullptr);
|
||||
DCHECK_NOT_NULL(func);
|
||||
DCHECK_EQ(index, func->func_index);
|
||||
ValueType entry_type = ValueType::Ref(func->sig_index, kNonNullable);
|
||||
if (V8_UNLIKELY(!IsSubtypeOf(entry_type, expected, module_.get()))) {
|
||||
errorf(initial_pc,
|
||||
"Invalid type in element entry: expected %s, got %s instead.",
|
||||
expected.name().c_str(), entry_type.name().c_str());
|
||||
return index;
|
||||
}
|
||||
|
||||
// TODO(manoskouk): Implement this with consume_init_expr(). It will require
|
||||
// changes in module-instantiate.cc, in {LoadElemSegmentImpl}.
|
||||
WasmElemSegment::Entry consume_element_expr() {
|
||||
uint8_t opcode = consume_u8("element opcode");
|
||||
if (failed()) return {};
|
||||
switch (opcode) {
|
||||
case kExprRefNull: {
|
||||
HeapTypeImmediate<kFullValidation> imm(WasmFeatures::All(), this,
|
||||
this->pc(), module_.get());
|
||||
consume_bytes(imm.length, "ref.null immediate");
|
||||
expect_u8("end opcode", kExprEnd);
|
||||
return {WasmElemSegment::Entry::kRefNullEntry,
|
||||
static_cast<uint32_t>(imm.type.representation())};
|
||||
}
|
||||
case kExprRefFunc: {
|
||||
uint32_t index = consume_element_func_index();
|
||||
if (failed()) return {};
|
||||
expect_u8("end opcode", kExprEnd);
|
||||
return {WasmElemSegment::Entry::kRefFuncEntry, index};
|
||||
}
|
||||
case kExprGlobalGet: {
|
||||
uint32_t index = this->consume_u32v("global index");
|
||||
if (failed()) return {};
|
||||
if (index >= module_->globals.size()) {
|
||||
errorf("Out-of-bounds global index %d", index);
|
||||
return {};
|
||||
}
|
||||
expect_u8("end opcode", kExprEnd);
|
||||
return {WasmElemSegment::Entry::kGlobalGetEntry, index};
|
||||
}
|
||||
default:
|
||||
error("invalid opcode in element");
|
||||
return {};
|
||||
}
|
||||
func->declared = true;
|
||||
return index;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -455,7 +455,7 @@ class InstanceBuilder {
|
||||
// and globals.
|
||||
void ProcessExports(Handle<WasmInstanceObject> instance);
|
||||
|
||||
void InitializeIndirectFunctionTables(Handle<WasmInstanceObject> instance);
|
||||
void InitializeNonDefaultableTables(Handle<WasmInstanceObject> instance);
|
||||
|
||||
void LoadTableSegments(Handle<WasmInstanceObject> instance);
|
||||
|
||||
@ -668,7 +668,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
|
||||
for (int i = module_->num_imported_tables; i < table_count; i++) {
|
||||
const WasmTable& table = module_->tables[i];
|
||||
// Initialize tables with null for now. We will initialize non-defaultable
|
||||
// tables later, in {InitializeIndirectFunctionTables}.
|
||||
// tables later, in {InitializeNonDefaultableTables}.
|
||||
Handle<WasmTableObject> table_obj = WasmTableObject::New(
|
||||
isolate_, instance, table.type, table.initial_size,
|
||||
table.has_maximum_size, table.maximum_size, nullptr,
|
||||
@ -745,11 +745,31 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
|
||||
InitGlobals(instance);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialize the indirect tables.
|
||||
// Initialize the indirect function tables and dispatch tables. We do this
|
||||
// before initializing non-defaultable tables and loading element segments, so
|
||||
// that indirect function tables in this module are included in the updates
|
||||
// when we do so.
|
||||
//--------------------------------------------------------------------------
|
||||
if (table_count > 0) {
|
||||
InitializeIndirectFunctionTables(instance);
|
||||
for (int table_index = 0;
|
||||
table_index < static_cast<int>(module_->tables.size()); ++table_index) {
|
||||
const WasmTable& table = module_->tables[table_index];
|
||||
|
||||
if (IsSubtypeOf(table.type, kWasmFuncRef, module_)) {
|
||||
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
|
||||
instance, table_index, table.initial_size);
|
||||
if (thrower_->error()) return {};
|
||||
auto table_object = handle(
|
||||
WasmTableObject::cast(instance->tables().get(table_index)), isolate_);
|
||||
WasmTableObject::AddDispatchTable(isolate_, table_object, instance,
|
||||
table_index);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialize non-defaultable tables.
|
||||
//--------------------------------------------------------------------------
|
||||
if (FLAG_experimental_wasm_typed_funcref) {
|
||||
InitializeNonDefaultableTables(instance);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@ -766,7 +786,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
|
||||
if (thrower_->error()) return {};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialize the indirect function tables.
|
||||
// Load element segments into tables.
|
||||
//--------------------------------------------------------------------------
|
||||
if (table_count > 0) {
|
||||
LoadTableSegments(instance);
|
||||
@ -1891,108 +1911,61 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
|
||||
}
|
||||
}
|
||||
|
||||
void SetNullTableEntry(Isolate* isolate, Handle<WasmInstanceObject> instance,
|
||||
Handle<WasmTableObject> table_object,
|
||||
uint32_t table_index, uint32_t entry_index) {
|
||||
const WasmModule* module = instance->module();
|
||||
if (IsSubtypeOf(table_object->type(), kWasmFuncRef, module)) {
|
||||
instance->GetIndirectFunctionTable(isolate, table_index)
|
||||
->Clear(entry_index);
|
||||
}
|
||||
WasmTableObject::Set(isolate, table_object, entry_index,
|
||||
isolate->factory()->null_value());
|
||||
}
|
||||
|
||||
void SetFunctionTableEntry(Isolate* isolate,
|
||||
Handle<WasmInstanceObject> instance,
|
||||
Handle<WasmTableObject> table_object,
|
||||
uint32_t table_index, uint32_t entry_index,
|
||||
uint32_t func_index) {
|
||||
namespace {
|
||||
void SetTableEntry(Isolate* isolate, Handle<WasmInstanceObject> instance,
|
||||
Handle<WasmTableObject> table_object, uint32_t table_index,
|
||||
uint32_t entry_index, Handle<Object> entry) {
|
||||
const WasmModule* module = instance->module();
|
||||
if (IsSubtypeOf(table_object->type(), kWasmFuncRef, module) &&
|
||||
entry->IsSmi()) {
|
||||
// We might get a Smi entry for a function table, representing the
|
||||
// function's index. In this case, we need to initialize the table with a
|
||||
// placeholder if the function has not been initialized.
|
||||
const uint32_t func_index = entry->ToSmi().value();
|
||||
const WasmFunction* function = &module->functions[func_index];
|
||||
|
||||
// For externref tables, we have to generate the WasmExternalFunction eagerly.
|
||||
// Later we cannot know if an entry is a placeholder or not.
|
||||
if (table_object->type().is_reference_to(HeapType::kExtern)) {
|
||||
Handle<WasmInternalFunction> wasm_internal_function =
|
||||
WasmInstanceObject::GetOrCreateWasmInternalFunction(isolate, instance,
|
||||
func_index);
|
||||
WasmTableObject::Set(isolate, table_object, entry_index,
|
||||
wasm_internal_function);
|
||||
} else {
|
||||
DCHECK(IsSubtypeOf(table_object->type(), kWasmFuncRef, module));
|
||||
|
||||
// Update the local dispatch table first if necessary.
|
||||
uint32_t sig_id = module->canonicalized_type_ids[function->sig_index];
|
||||
FunctionTargetAndRef entry(instance, func_index);
|
||||
instance->GetIndirectFunctionTable(isolate, table_index)
|
||||
->Set(entry_index, sig_id, entry.call_target(), *entry.ref());
|
||||
|
||||
// Update the table object's other dispatch tables.
|
||||
MaybeHandle<WasmInternalFunction> wasm_internal_function =
|
||||
WasmInstanceObject::GetWasmInternalFunction(isolate, instance,
|
||||
func_index);
|
||||
if (wasm_internal_function.is_null()) {
|
||||
// No JSFunction entry yet exists for this function. Create a
|
||||
// {Tuple2} holding the information to lazily allocate one.
|
||||
// No JSFunction entry yet exists for this function. Create a {Tuple2}
|
||||
// holding the information to lazily allocate one.
|
||||
WasmTableObject::SetFunctionTablePlaceholder(
|
||||
isolate, table_object, entry_index, instance, func_index);
|
||||
} else {
|
||||
table_object->entries().set(entry_index,
|
||||
*wasm_internal_function.ToHandleChecked());
|
||||
}
|
||||
// UpdateDispatchTables() updates all other dispatch tables, since
|
||||
// we have not yet added the dispatch table we are currently building.
|
||||
|
||||
WasmTableObject::UpdateDispatchTables(isolate, table_object, entry_index,
|
||||
function->sig, instance, func_index);
|
||||
} else {
|
||||
// In all other cases, simply call {WasmTableObject::Set}.
|
||||
WasmTableObject::Set(isolate, table_object, entry_index, entry);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void InstanceBuilder::InitializeIndirectFunctionTables(
|
||||
void InstanceBuilder::InitializeNonDefaultableTables(
|
||||
Handle<WasmInstanceObject> instance) {
|
||||
for (int table_index = 0;
|
||||
table_index < static_cast<int>(module_->tables.size()); ++table_index) {
|
||||
const WasmTable& table = module_->tables[table_index];
|
||||
|
||||
if (IsSubtypeOf(table.type, kWasmFuncRef, module_)) {
|
||||
WasmInstanceObject::EnsureIndirectFunctionTableWithMinimumSize(
|
||||
instance, table_index, table.initial_size);
|
||||
}
|
||||
|
||||
if (!table.type.is_defaultable()) {
|
||||
auto table_object = handle(
|
||||
WasmTableObject::cast(instance->tables().get(table_index)), isolate_);
|
||||
Handle<Object> value =
|
||||
EvaluateInitExpression(&init_expr_zone_, table.initial_value,
|
||||
table.type, isolate_, instance)
|
||||
table.type, isolate_, instance,
|
||||
IsSubtypeOf(table_object->type(), kWasmFuncRef,
|
||||
instance->module())
|
||||
? InitExprInterface::kLazyFunctions
|
||||
: InitExprInterface::kStrictFunctions)
|
||||
.to_ref();
|
||||
if (value.is_null()) {
|
||||
for (uint32_t entry_index = 0; entry_index < table.initial_size;
|
||||
entry_index++) {
|
||||
SetNullTableEntry(isolate_, instance, table_object, table_index,
|
||||
entry_index);
|
||||
}
|
||||
} else if (value->IsWasmInternalFunction()) {
|
||||
Handle<Object> external = handle(
|
||||
Handle<WasmInternalFunction>::cast(value)->external(), isolate_);
|
||||
// TODO(manoskouk): Support WasmJSFunction/WasmCapiFunction.
|
||||
if (!WasmExportedFunction::IsWasmExportedFunction(*external)) {
|
||||
thrower_->TypeError(
|
||||
"Initializing a table with a Webassembly.Function object is not "
|
||||
"supported yet");
|
||||
}
|
||||
uint32_t function_index =
|
||||
Handle<WasmExportedFunction>::cast(external)->function_index();
|
||||
for (uint32_t entry_index = 0; entry_index < table.initial_size;
|
||||
entry_index++) {
|
||||
SetFunctionTableEntry(isolate_, instance, table_object, table_index,
|
||||
entry_index, function_index);
|
||||
}
|
||||
} else {
|
||||
for (uint32_t entry_index = 0; entry_index < table.initial_size;
|
||||
entry_index++) {
|
||||
WasmTableObject::Set(isolate_, table_object, entry_index, value);
|
||||
}
|
||||
SetTableEntry(isolate_, instance, table_object, table_index,
|
||||
entry_index, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2018,40 +1991,22 @@ bool LoadElemSegmentImpl(Zone* zone, Isolate* isolate,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_function_table =
|
||||
IsSubtypeOf(table_object->type(), kWasmFuncRef, instance->module());
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
WasmElemSegment::Entry init = elem_segment.entries[src + i];
|
||||
WasmElemSegment::Entry entry = elem_segment.entries[src + i];
|
||||
int entry_index = static_cast<int>(dst + i);
|
||||
switch (init.kind) {
|
||||
case WasmElemSegment::Entry::kRefNullEntry:
|
||||
SetNullTableEntry(isolate, instance, table_object, table_index,
|
||||
entry_index);
|
||||
break;
|
||||
case WasmElemSegment::Entry::kRefFuncEntry:
|
||||
SetFunctionTableEntry(isolate, instance, table_object, table_index,
|
||||
entry_index, init.index);
|
||||
break;
|
||||
case WasmElemSegment::Entry::kGlobalGetEntry: {
|
||||
Handle<Object> value =
|
||||
WasmInstanceObject::GetGlobalValue(
|
||||
instance, instance->module()->globals[init.index])
|
||||
.to_ref();
|
||||
if (value.is_null()) {
|
||||
SetNullTableEntry(isolate, instance, table_object, table_index,
|
||||
entry_index);
|
||||
} else if (WasmExportedFunction::IsWasmExportedFunction(*value)) {
|
||||
uint32_t function_index =
|
||||
Handle<WasmExportedFunction>::cast(value)->function_index();
|
||||
SetFunctionTableEntry(isolate, instance, table_object, table_index,
|
||||
entry_index, function_index);
|
||||
} else if (WasmJSFunction::IsWasmJSFunction(*value)) {
|
||||
// TODO(manoskouk): Support WasmJSFunction.
|
||||
return false;
|
||||
} else {
|
||||
WasmTableObject::Set(isolate, table_object, entry_index, value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
elem_segment.element_type == WasmElemSegment::kExpressionElements
|
||||
? EvaluateInitExpression(
|
||||
zone, entry.ref, elem_segment.type, isolate, instance,
|
||||
is_function_table ? InitExprInterface::kLazyFunctions
|
||||
: InitExprInterface::kStrictFunctions)
|
||||
.to_ref()
|
||||
: handle(Smi::FromInt(entry.index), isolate);
|
||||
SetTableEntry(isolate, instance, table_object, table_index, entry_index,
|
||||
value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -2089,18 +2044,6 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int table_count = static_cast<int>(module_->tables.size());
|
||||
for (int index = 0; index < table_count; ++index) {
|
||||
if (IsSubtypeOf(module_->tables[index].type, kWasmFuncRef, module_)) {
|
||||
auto table_object = handle(
|
||||
WasmTableObject::cast(instance->tables().get(index)), isolate_);
|
||||
|
||||
// Add the new dispatch table at the end to avoid redundant lookups.
|
||||
WasmTableObject::AddDispatchTable(isolate_, table_object, instance,
|
||||
index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceBuilder::InitializeTags(Handle<WasmInstanceObject> instance) {
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "include/v8-metrics.h"
|
||||
#include "include/v8config.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -20,7 +19,6 @@ namespace internal {
|
||||
class Isolate;
|
||||
class JSArrayBuffer;
|
||||
class JSReceiver;
|
||||
class WasmInitExpr;
|
||||
class WasmModuleObject;
|
||||
class WasmInstanceObject;
|
||||
class Zone;
|
||||
@ -43,9 +41,6 @@ bool LoadElemSegment(Isolate* isolate, Handle<WasmInstanceObject> instance,
|
||||
uint32_t table_index, uint32_t segment_index, uint32_t dst,
|
||||
uint32_t src, uint32_t count) V8_WARN_UNUSED_RESULT;
|
||||
|
||||
uint32_t EvalUint32InitExpr(Handle<WasmInstanceObject> instance,
|
||||
const WasmInitExpr& expr);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -121,13 +121,17 @@ struct WasmElemSegment {
|
||||
kStatusPassive, // copied explicitly after instantiation.
|
||||
kStatusDeclarative // purely declarative and never copied.
|
||||
};
|
||||
struct Entry {
|
||||
enum Kind { kGlobalGetEntry, kRefFuncEntry, kRefNullEntry } kind;
|
||||
uint32_t index;
|
||||
Entry(Kind kind, uint32_t index) : kind(kind), index(index) {}
|
||||
Entry() : kind(kRefNullEntry), index(0) {}
|
||||
};
|
||||
enum ElementType { kFunctionIndexElements, kExpressionElements };
|
||||
// An element segment entry. If {element_type == kExpressionElements}, it
|
||||
// refers to an initializer expression (via a {WireBytesRef}); otherwise, it
|
||||
// represents a function index.
|
||||
union Entry {
|
||||
WireBytesRef ref;
|
||||
uint32_t index;
|
||||
|
||||
explicit Entry(uint32_t index) : index(index) {}
|
||||
explicit Entry(WireBytesRef ref) : ref(ref) {}
|
||||
};
|
||||
|
||||
// Construct an active segment.
|
||||
WasmElemSegment(ValueType type, uint32_t table_index, WireBytesRef offset,
|
||||
|
@ -331,8 +331,7 @@ uint32_t TestingModuleBuilder::AddPassiveElementSegment(
|
||||
WasmElemSegment::kFunctionIndexElements);
|
||||
auto& elem_segment = test_module_->elem_segments.back();
|
||||
for (uint32_t entry : entries) {
|
||||
elem_segment.entries.push_back(
|
||||
WasmElemSegment::Entry(WasmElemSegment::Entry::kRefFuncEntry, entry));
|
||||
elem_segment.entries.emplace_back(entry);
|
||||
}
|
||||
|
||||
// The vector pointers may have moved, so update the instance object.
|
||||
|
@ -298,22 +298,6 @@ std::ostream& operator<<(std::ostream& os, const PrintName& name) {
|
||||
return os.write(name.name.begin(), name.name.size());
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, WasmElemSegment::Entry entry) {
|
||||
os << "WasmInitExpr.";
|
||||
switch (entry.kind) {
|
||||
case WasmElemSegment::Entry::kGlobalGetEntry:
|
||||
os << "GlobalGet(" << entry.index;
|
||||
break;
|
||||
case WasmElemSegment::Entry::kRefFuncEntry:
|
||||
os << "RefFunc(" << entry.index;
|
||||
break;
|
||||
case WasmElemSegment::Entry::kRefNullEntry:
|
||||
os << "RefNull(" << HeapType(entry.index).name().c_str();
|
||||
break;
|
||||
}
|
||||
return os << ")";
|
||||
}
|
||||
|
||||
// An interface for WasmFullDecoder used to decode initializer expressions. As
|
||||
// opposed to the one in src/wasm/, this emits {WasmInitExpr} as opposed to a
|
||||
// {WasmValue}.
|
||||
@ -664,10 +648,19 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
|
||||
}
|
||||
os << "[";
|
||||
for (uint32_t i = 0; i < elem_segment.entries.size(); i++) {
|
||||
os << elem_segment.entries[i];
|
||||
if (elem_segment.element_type == WasmElemSegment::kExpressionElements) {
|
||||
DecodeAndAppendInitExpr(os, &zone, module, wire_bytes,
|
||||
elem_segment.entries[i].ref, elem_segment.type);
|
||||
} else {
|
||||
os << elem_segment.entries[i].index;
|
||||
}
|
||||
if (i < elem_segment.entries.size() - 1) os << ", ";
|
||||
}
|
||||
os << "], " << ValueTypeToConstantName(elem_segment.type) << ");\n";
|
||||
os << "], "
|
||||
<< (elem_segment.element_type == WasmElemSegment::kExpressionElements
|
||||
? ValueTypeToConstantName(elem_segment.type)
|
||||
: "undefined")
|
||||
<< ");\n";
|
||||
}
|
||||
|
||||
for (const WasmTag& tag : module->tags) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --experimental-wasm-typed-funcref
|
||||
// Flags: --experimental-wasm-gc
|
||||
|
||||
d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
|
||||
@ -97,3 +97,22 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js');
|
||||
assertEquals(instance.exports.table.get(2)(10), 20);
|
||||
assertEquals(instance.exports.table.get(3)(10), 11);
|
||||
})();
|
||||
|
||||
// Test that mutable globals cannot be used in element segments, even under
|
||||
// --experimental-wasm-gc.
|
||||
(function TestMutableGlobalInElementSegment() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
let global = builder.addImportedGlobal("m", "g", kWasmFuncRef, true);
|
||||
let table = builder.addTable(kWasmFuncRef, 10, 10);
|
||||
builder.addActiveElementSegment(
|
||||
table.index, WasmInitExpr.I32Const(0),
|
||||
[WasmInitExpr.GlobalGet(global.index)], kWasmFuncRef);
|
||||
builder.addExportOfKind("table", kExternalTable, table.index);
|
||||
|
||||
assertThrows(
|
||||
() => builder.instantiate({m : {g :
|
||||
new WebAssembly.Global({value: "anyfunc", mutable: true}, null)}}),
|
||||
WebAssembly.CompileError,
|
||||
/mutable globals cannot be used in initializer expressions/);
|
||||
})();
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --expose-wasm --expose-gc
|
||||
// Flags: --expose-gc
|
||||
|
||||
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
@ -900,3 +900,19 @@ function js_div(a, b) { return (a / b) | 0; }
|
||||
assertEquals(300, main(2));
|
||||
assertEquals(400, main(3));
|
||||
})();
|
||||
|
||||
(function TestNonImportedGlobalInElementSegment() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
let global = builder.addGlobal(kWasmFuncRef, false,
|
||||
WasmInitExpr.RefNull(kWasmFuncRef));
|
||||
let table = builder.addTable(kWasmFuncRef, 10, 10);
|
||||
builder.addActiveElementSegment(
|
||||
table.index, WasmInitExpr.I32Const(0),
|
||||
[WasmInitExpr.GlobalGet(global.index)], kWasmFuncRef);
|
||||
builder.addExportOfKind("table", kExternalTable, table.index);
|
||||
|
||||
assertThrows(
|
||||
() => builder.instantiate(), WebAssembly.CompileError,
|
||||
/non-imported globals cannot be used in initializer expressions/);
|
||||
})();
|
||||
|
@ -2210,8 +2210,7 @@ TEST_F(WasmModuleVerifyTest, ElementSectionInitFuncRefTableWithExternRefNull) {
|
||||
|
||||
EXPECT_FAILURE_WITH_MSG(
|
||||
data,
|
||||
"Invalid type in the init expression. The expected "
|
||||
"type is 'funcref', but the actual type is 'externref'.");
|
||||
"type error in init. expression[0] (expected funcref, got externref)");
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, ElementSectionDontInitExternRefImportedTable) {
|
||||
@ -2265,7 +2264,7 @@ TEST_F(WasmModuleVerifyTest, ElementSectionGlobalGetOutOfBounds) {
|
||||
kFuncRefCode, // type
|
||||
ENTRY_COUNT(1), // element count
|
||||
kExprGlobalGet, 0x00, kExprEnd)}; // init. expression
|
||||
EXPECT_FAILURE_WITH_MSG(data, "Out-of-bounds global index 0");
|
||||
EXPECT_FAILURE_WITH_MSG(data, "Invalid global index: 0");
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, IndirectFunctionNoFunctions) {
|
||||
|
Loading…
Reference in New Issue
Block a user