Commit de17316a authored by Emanuel Ziegler's avatar Emanuel Ziegler Committed by Commit Bot

[wasm][reference-types] Implement declarative segments

Implement the latest spec changes:
  - Allow declarative segments to behave like passive & dropped segments.
  - Enforce that only declared functions may be returned or used in globals
    as funcref.
  - Ensure that table fill does not modify any entries if OOB.

Spec tests for select and br_table are still failing due to proposal issue

Bug: v8:10156

R=ahaas@chromium.org

Change-Id: I5b95be36a67bc7482a84b848908cc4cbdf94af03
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2027458Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Emanuel Ziegler <ecmziegler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66297}
parent 94cdc18a
...@@ -584,11 +584,11 @@ RUNTIME_FUNCTION(Runtime_WasmTableFill) { ...@@ -584,11 +584,11 @@ RUNTIME_FUNCTION(Runtime_WasmTableFill) {
// Even when table.fill goes out-of-bounds, as many entries as possible are // Even when table.fill goes out-of-bounds, as many entries as possible are
// put into the table. Only afterwards we trap. // put into the table. Only afterwards we trap.
uint32_t fill_count = std::min(count, table_size - start); uint32_t fill_count = std::min(count, table_size - start);
WasmTableObject::Fill(isolate, table, start, value, fill_count);
if (fill_count < count) { if (fill_count < count) {
return ThrowTableOutOfBounds(isolate, instance); return ThrowTableOutOfBounds(isolate, instance);
} }
WasmTableObject::Fill(isolate, table, start, value, fill_count);
return ReadOnlyRoots(isolate).undefined_value(); return ReadOnlyRoots(isolate).undefined_value();
} }
......
...@@ -1138,6 +1138,11 @@ class WasmDecoder : public Decoder { ...@@ -1138,6 +1138,11 @@ class WasmDecoder : public Decoder {
errorf(pc, "invalid function index: %u", imm.index); errorf(pc, "invalid function index: %u", imm.index);
return false; return false;
} }
if (!VALIDATE(module_ != nullptr &&
module_->functions[imm.index].declared)) {
this->errorf(pc, "undeclared reference to function #%u", imm.index);
return false;
}
return true; return true;
} }
......
...@@ -367,6 +367,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -367,6 +367,7 @@ class ModuleDecoderImpl : public Decoder {
void DecodeSection(SectionCode section_code, Vector<const uint8_t> bytes, void DecodeSection(SectionCode section_code, Vector<const uint8_t> bytes,
uint32_t offset, bool verify_functions = true) { uint32_t offset, bool verify_functions = true) {
VerifyFunctionDeclarations(section_code);
if (failed()) return; if (failed()) return;
Reset(bytes, offset); Reset(bytes, offset);
TRACE("Section: %s\n", SectionName(section_code)); TRACE("Section: %s\n", SectionName(section_code));
...@@ -548,7 +549,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -548,7 +549,8 @@ class ModuleDecoderImpl : public Decoder {
0, // sig_index 0, // sig_index
{0, 0}, // code {0, 0}, // code
true, // imported true, // imported
false}); // exported false, // exported
false}); // declared
WasmFunction* function = &module_->functions.back(); WasmFunction* function = &module_->functions.back();
function->sig_index = function->sig_index =
consume_sig_index(module_.get(), &function->sig); consume_sig_index(module_.get(), &function->sig);
...@@ -638,7 +640,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -638,7 +640,8 @@ class ModuleDecoderImpl : public Decoder {
0, // sig_index 0, // sig_index
{0, 0}, // code {0, 0}, // code
false, // imported false, // imported
false}); // exported false, // exported
false}); // declared
WasmFunction* function = &module_->functions.back(); WasmFunction* function = &module_->functions.back();
function->sig_index = consume_sig_index(module_.get(), &function->sig); function->sig_index = consume_sig_index(module_.get(), &function->sig);
if (!ok()) return; if (!ok()) return;
...@@ -806,21 +809,18 @@ class ModuleDecoderImpl : public Decoder { ...@@ -806,21 +809,18 @@ class ModuleDecoderImpl : public Decoder {
uint32_t element_count = uint32_t element_count =
consume_count("element count", FLAG_wasm_max_table_size); consume_count("element count", FLAG_wasm_max_table_size);
if (element_count > 0 && module_->tables.size() == 0) {
error(pc_, "The element section requires a table");
}
for (uint32_t i = 0; ok() && i < element_count; ++i) { for (uint32_t i = 0; ok() && i < element_count; ++i) {
const byte* pos = pc(); const byte* pos = pc();
bool is_active; WasmElemSegment::Status status;
bool functions_as_elements; bool functions_as_elements;
uint32_t table_index; uint32_t table_index;
WasmInitExpr offset; WasmInitExpr offset;
consume_element_segment_header(&is_active, &functions_as_elements, consume_element_segment_header(&status, &functions_as_elements,
&table_index, &offset); &table_index, &offset);
if (failed()) return; if (failed()) return;
if (is_active) { if (status == WasmElemSegment::kStatusActive) {
if (table_index >= module_->tables.size()) { if (table_index >= module_->tables.size()) {
errorf(pos, "out of bounds table index %u", table_index); errorf(pos, "out of bounds table index %u", table_index);
break; break;
...@@ -836,10 +836,11 @@ class ModuleDecoderImpl : public Decoder { ...@@ -836,10 +836,11 @@ class ModuleDecoderImpl : public Decoder {
uint32_t num_elem = uint32_t num_elem =
consume_count("number of elements", max_table_init_entries()); consume_count("number of elements", max_table_init_entries());
if (is_active) { if (status == WasmElemSegment::kStatusActive) {
module_->elem_segments.emplace_back(table_index, offset); module_->elem_segments.emplace_back(table_index, offset);
} else { } else {
module_->elem_segments.emplace_back(); module_->elem_segments.emplace_back(
status == WasmElemSegment::kStatusDeclarative);
} }
WasmElemSegment* init = &module_->elem_segments.back(); WasmElemSegment* init = &module_->elem_segments.back();
...@@ -1116,7 +1117,41 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1116,7 +1117,41 @@ class ModuleDecoderImpl : public Decoder {
return true; return true;
} }
void VerifyFunctionDeclarations(SectionCode section_code) {
// Since we will only know if a function was properly declared after all the
// element sections have been parsed, but we need to verify the proper use
// within global initialization, we are deferring those checks.
if (deferred_funcref_error_offsets_.empty()) {
// No verifications to do be done.
return;
}
if (!ok()) {
// Previous errors exist.
return;
}
// TODO(ecmziegler): Adjust logic if module order changes (e.g. event
// section).
if (section_code <= kElementSectionCode &&
section_code != kUnknownSectionCode) {
// Before the element section and not at end of decoding.
return;
}
for (auto& func_offset : deferred_funcref_error_offsets_) {
DCHECK_LT(func_offset.first, module_->functions.size());
if (!module_->functions[func_offset.first].declared) {
errorf(func_offset.second, "undeclared reference to function #%u",
func_offset.first);
break;
}
}
deferred_funcref_error_offsets_.clear();
}
ModuleResult FinishDecoding(bool verify_functions = true) { ModuleResult FinishDecoding(bool verify_functions = true) {
// Ensure that function verifications were done even if no section followed
// the global section.
VerifyFunctionDeclarations(kUnknownSectionCode);
if (ok() && CheckMismatchedCounts()) { if (ok() && CheckMismatchedCounts()) {
CalculateGlobalOffsets(module_.get()); CalculateGlobalOffsets(module_.get());
} }
...@@ -1231,6 +1266,10 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1231,6 +1266,10 @@ class ModuleDecoderImpl : public Decoder {
kLastKnownModuleSection, kLastKnownModuleSection,
"not enough bits"); "not enough bits");
WasmError intermediate_error_; WasmError intermediate_error_;
// Map from function index to wire byte offset of first funcref initialization
// in global section. Used for deferred checking and proper error reporting if
// these were not properly declared in the element section.
std::unordered_map<uint32_t, int> deferred_funcref_error_offsets_;
ModuleOrigin origin_; ModuleOrigin origin_;
bool has_seen_unordered_section(SectionCode section_code) { bool has_seen_unordered_section(SectionCode section_code) {
...@@ -1566,6 +1605,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1566,6 +1605,8 @@ class ModuleDecoderImpl : public Decoder {
errorf(pc() - 1, "invalid function index: %u", imm.index); errorf(pc() - 1, "invalid function index: %u", imm.index);
break; break;
} }
// Defer check for declaration of function reference.
deferred_funcref_error_offsets_.emplace(imm.index, pc_offset());
expr.kind = WasmInitExpr::kRefFuncConst; expr.kind = WasmInitExpr::kRefFuncConst;
expr.val.function_index = imm.index; expr.val.function_index = imm.index;
len = imm.length; len = imm.length;
...@@ -1716,7 +1757,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1716,7 +1757,7 @@ class ModuleDecoderImpl : public Decoder {
return attribute; return attribute;
} }
void consume_element_segment_header(bool* is_active, void consume_element_segment_header(WasmElemSegment::Status* status,
bool* functions_as_elements, bool* functions_as_elements,
uint32_t* table_index, uint32_t* table_index,
WasmInitExpr* offset) { WasmInitExpr* offset) {
...@@ -1749,11 +1790,31 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1749,11 +1790,31 @@ class ModuleDecoderImpl : public Decoder {
kIsPassiveMask | kHasTableIndexMask | kFunctionsAsElementsMask; kIsPassiveMask | kHasTableIndexMask | kFunctionsAsElementsMask;
bool is_passive = flag & kIsPassiveMask; bool is_passive = flag & kIsPassiveMask;
*is_active = !is_passive; if (!is_passive) {
*status = WasmElemSegment::kStatusActive;
if (module_->tables.size() == 0) {
error(pc_, "Active element sections require a table");
}
} else if ((flag & kHasTableIndexMask)) { // Special bit combination for
// declarative segments.
*status = WasmElemSegment::kStatusDeclarative;
} else {
*status = WasmElemSegment::kStatusPassive;
if (module_->tables.size() == 0) {
error(pc_, "Passive element sections require a table");
}
}
*functions_as_elements = flag & kFunctionsAsElementsMask; *functions_as_elements = flag & kFunctionsAsElementsMask;
bool has_table_index = flag & kHasTableIndexMask; bool has_table_index = (flag & kHasTableIndexMask) &&
*status == WasmElemSegment::kStatusActive;
if (is_passive && !enabled_features_.has_bulk_memory()) { if (*status == WasmElemSegment::kStatusDeclarative &&
!enabled_features_.has_anyref()) {
error("Declarative element segments require --experimental-wasm-anyref");
return;
}
if (*status == WasmElemSegment::kStatusPassive &&
!enabled_features_.has_bulk_memory()) {
error("Passive element segments require --experimental-wasm-bulk-memory"); error("Passive element segments require --experimental-wasm-bulk-memory");
return; return;
} }
...@@ -1770,8 +1831,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1770,8 +1831,8 @@ class ModuleDecoderImpl : public Decoder {
"--experimental-wasm-bulk-memory or --experimental-wasm-anyref?"); "--experimental-wasm-bulk-memory or --experimental-wasm-anyref?");
return; return;
} }
if ((flag & kFullMask) != flag || (!(*is_active) && has_table_index)) { if ((flag & kFullMask) != flag) {
errorf(pos, "illegal flag value %u. Must be 0, 1, 2, 4, 5 or 6", flag); errorf(pos, "illegal flag value %u. Must be between 0 and 7", flag);
} }
if (has_table_index) { if (has_table_index) {
...@@ -1780,11 +1841,11 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1780,11 +1841,11 @@ class ModuleDecoderImpl : public Decoder {
*table_index = 0; *table_index = 0;
} }
if (*is_active) { if (*status == WasmElemSegment::kStatusActive) {
*offset = consume_init_expr(module_.get(), kWasmI32); *offset = consume_init_expr(module_.get(), kWasmI32);
} }
if (*is_active && !has_table_index) { if (*status == WasmElemSegment::kStatusActive && !has_table_index) {
// Active segments without table indices are a special case for backwards // Active segments without table indices are a special case for backwards
// compatibility. These cases have an implicit element kind or element // compatibility. These cases have an implicit element kind or element
// type, so we are done already with the segment header. // type, so we are done already with the segment header.
...@@ -1859,6 +1920,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1859,6 +1920,7 @@ class ModuleDecoderImpl : public Decoder {
uint32_t index = uint32_t index =
consume_func_index(module_.get(), &func, "element function index"); consume_func_index(module_.get(), &func, "element function index");
if (failed()) return index; if (failed()) return index;
func->declared = true;
DCHECK_NE(func, nullptr); DCHECK_NE(func, nullptr);
DCHECK_EQ(index, func->func_index); DCHECK_EQ(index, func->func_index);
DCHECK_NE(index, WasmElemSegment::kNullIndex); DCHECK_NE(index, WasmElemSegment::kNullIndex);
......
...@@ -482,7 +482,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() { ...@@ -482,7 +482,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
// Check that indirect function table segments are within bounds. // Check that indirect function table segments are within bounds.
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
for (const WasmElemSegment& elem_segment : module_->elem_segments) { for (const WasmElemSegment& elem_segment : module_->elem_segments) {
if (!elem_segment.active) continue; if (elem_segment.status != WasmElemSegment::kStatusActive) continue;
DCHECK_LT(elem_segment.table_index, table_count); DCHECK_LT(elem_segment.table_index, table_count);
uint32_t base = EvalUint32InitExpr(instance, elem_segment.offset); uint32_t base = EvalUint32InitExpr(instance, elem_segment.offset);
// Because of imported tables, {table_size} has to come from the table // Because of imported tables, {table_size} has to come from the table
...@@ -1685,7 +1685,7 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) { ...@@ -1685,7 +1685,7 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
segment_index < module_->elem_segments.size(); ++segment_index) { segment_index < module_->elem_segments.size(); ++segment_index) {
auto& elem_segment = instance->module()->elem_segments[segment_index]; auto& elem_segment = instance->module()->elem_segments[segment_index];
// Passive segments are not copied during instantiation. // Passive segments are not copied during instantiation.
if (!elem_segment.active) continue; if (elem_segment.status != WasmElemSegment::kStatusActive) continue;
uint32_t table_index = elem_segment.table_index; uint32_t table_index = elem_segment.table_index;
uint32_t dst = EvalUint32InitExpr(instance, elem_segment.offset); uint32_t dst = EvalUint32InitExpr(instance, elem_segment.offset);
......
...@@ -1951,12 +1951,12 @@ class ThreadImpl { ...@@ -1951,12 +1951,12 @@ class ThreadImpl {
// Even when table.fill goes out-of-bounds, as many entries as possible // Even when table.fill goes out-of-bounds, as many entries as possible
// are put into the table. Only afterwards we trap. // are put into the table. Only afterwards we trap.
uint32_t fill_count = std::min(count, table_size - start); uint32_t fill_count = std::min(count, table_size - start);
WasmTableObject::Fill(isolate_, table, start, value, fill_count);
if (fill_count < count) { if (fill_count < count) {
DoTrap(kTrapTableOutOfBounds, pc); DoTrap(kTrapTableOutOfBounds, pc);
return false; return false;
} }
WasmTableObject::Fill(isolate_, table, start, value, fill_count);
*len += imm.length; *len += imm.length;
return true; return true;
} }
...@@ -4319,7 +4319,13 @@ ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting( ...@@ -4319,7 +4319,13 @@ ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting(
// Create some dummy structures, to avoid special-casing the implementation // Create some dummy structures, to avoid special-casing the implementation
// just for testing. // just for testing.
FunctionSig sig(0, 0, nullptr); FunctionSig sig(0, 0, nullptr);
WasmFunction function{&sig, 0, 0, {0, 0}, false, false}; WasmFunction function{&sig, // sig
0, // func_index
0, // sig_index
{0, 0}, // code
false, // imported
false, // exported
false}; // declared
InterpreterCode code{ InterpreterCode code{
&function, BodyLocalDecls(zone), start, end, nullptr, nullptr, nullptr}; &function, BodyLocalDecls(zone), start, end, nullptr, nullptr, nullptr};
......
...@@ -57,6 +57,7 @@ struct WasmFunction { ...@@ -57,6 +57,7 @@ struct WasmFunction {
WireBytesRef code; // code of this function. WireBytesRef code; // code of this function.
bool imported; bool imported;
bool exported; bool exported;
bool declared;
}; };
// Static representation of a wasm global variable. // Static representation of a wasm global variable.
...@@ -115,10 +116,13 @@ struct WasmElemSegment { ...@@ -115,10 +116,13 @@ struct WasmElemSegment {
// Construct an active segment. // Construct an active segment.
WasmElemSegment(uint32_t table_index, WasmInitExpr offset) WasmElemSegment(uint32_t table_index, WasmInitExpr offset)
: table_index(table_index), offset(offset), active(true) {} : table_index(table_index), offset(offset), status(kStatusActive) {}
// Construct a passive segment, which has no table index or offset. // Construct a passive or declarative segment, which has no table index or
WasmElemSegment() : table_index(0), active(false) {} // offset.
explicit WasmElemSegment(bool declarative)
: table_index(0),
status(declarative ? kStatusDeclarative : kStatusPassive) {}
// Used in the {entries} vector to represent a `ref.null` entry in a passive // Used in the {entries} vector to represent a `ref.null` entry in a passive
// segment. // segment.
...@@ -127,7 +131,11 @@ struct WasmElemSegment { ...@@ -127,7 +131,11 @@ struct WasmElemSegment {
uint32_t table_index; uint32_t table_index;
WasmInitExpr offset; WasmInitExpr offset;
std::vector<uint32_t> entries; std::vector<uint32_t> entries;
bool active; // true if copied automatically during instantiation. enum Status {
kStatusActive, // copied automatically during instantiation.
kStatusPassive, // copied explicitly after instantiation.
kStatusDeclarative // purely declarative and never copied.
} status;
}; };
// Static representation of a wasm import. // Static representation of a wasm import.
......
...@@ -1286,7 +1286,11 @@ void WasmInstanceObject::InitElemSegmentArrays( ...@@ -1286,7 +1286,11 @@ void WasmInstanceObject::InitElemSegmentArrays(
auto module = module_object->module(); auto module = module_object->module();
auto num_elem_segments = module->elem_segments.size(); auto num_elem_segments = module->elem_segments.size();
for (size_t i = 0; i < num_elem_segments; ++i) { for (size_t i = 0; i < num_elem_segments; ++i) {
instance->dropped_elem_segments()[i] = 0; instance->dropped_elem_segments()[i] =
module->elem_segments[i].status ==
wasm::WasmElemSegment::kStatusDeclarative
? 1
: 0;
} }
} }
......
...@@ -113,7 +113,13 @@ uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, const char* name, ...@@ -113,7 +113,13 @@ uint32_t TestingModuleBuilder::AddFunction(FunctionSig* sig, const char* name,
test_module_->functions.reserve(kMaxFunctions); test_module_->functions.reserve(kMaxFunctions);
} }
uint32_t index = static_cast<uint32_t>(test_module_->functions.size()); uint32_t index = static_cast<uint32_t>(test_module_->functions.size());
test_module_->functions.push_back({sig, index, 0, {0, 0}, false, false}); test_module_->functions.push_back({sig, // sig
index, // func_index
0, // sig_index
{0, 0}, // code
false, // imported
false, // exported
false}); // declared
if (type == kImport) { if (type == kImport) {
DCHECK_EQ(0, test_module_->num_declared_functions); DCHECK_EQ(0, test_module_->num_declared_functions);
++test_module_->num_imported_functions; ++test_module_->num_imported_functions;
...@@ -289,7 +295,7 @@ uint32_t TestingModuleBuilder::AddPassiveElementSegment( ...@@ -289,7 +295,7 @@ uint32_t TestingModuleBuilder::AddPassiveElementSegment(
uint32_t index = static_cast<uint32_t>(test_module_->elem_segments.size()); uint32_t index = static_cast<uint32_t>(test_module_->elem_segments.size());
DCHECK_EQ(index, dropped_elem_segments_.size()); DCHECK_EQ(index, dropped_elem_segments_.size());
test_module_->elem_segments.emplace_back(); test_module_->elem_segments.emplace_back(false);
auto& elem_segment = test_module_->elem_segments.back(); auto& elem_segment = test_module_->elem_segments.back();
elem_segment.entries = entries; elem_segment.entries = entries;
......
...@@ -27,8 +27,10 @@ ...@@ -27,8 +27,10 @@
#define ACTIVE_NO_INDEX 0 #define ACTIVE_NO_INDEX 0
#define PASSIVE 1 #define PASSIVE 1
#define ACTIVE_WITH_INDEX 2 #define ACTIVE_WITH_INDEX 2
#define DECLARATIVE 3
#define PASSIVE_WITH_ELEMENTS 5 #define PASSIVE_WITH_ELEMENTS 5
#define ACTIVE_WITH_ELEMENTS 6 #define ACTIVE_WITH_ELEMENTS 6
#define DECLARATIVE_WITH_ELEMENTS 7
// The table index field in an element segment was repurposed as a flags field. // The table index field in an element segment was repurposed as a flags field.
// To specify a table index, we have to set the flag value to 2, followed by // To specify a table index, we have to set the flag value to 2, followed by
......
...@@ -222,6 +222,7 @@ load('test/mjsunit/wasm/wasm-module-builder.js'); ...@@ -222,6 +222,7 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
const function_index = builder.addFunction('hidden', kSig_i_v) const function_index = builder.addFunction('hidden', kSig_i_v)
.addBody([kExprI32Const, expected]) .addBody([kExprI32Const, expected])
.index; .index;
builder.addDeclarativeElementSegment([function_index]);
builder.addFunction('main', kSig_a_v) builder.addFunction('main', kSig_a_v)
.addBody([kExprRefFunc, function_index]) .addBody([kExprRefFunc, function_index])
.exportFunc(); .exportFunc();
...@@ -237,6 +238,7 @@ load('test/mjsunit/wasm/wasm-module-builder.js'); ...@@ -237,6 +238,7 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
const foo = builder.addFunction('foo', kSig_i_v) const foo = builder.addFunction('foo', kSig_i_v)
.addBody([kExprI32Const, expected]) .addBody([kExprI32Const, expected])
.exportFunc(); .exportFunc();
builder.addDeclarativeElementSegment([foo.index]);
builder.addFunction('main', kSig_a_v) builder.addFunction('main', kSig_a_v)
.addBody([kExprRefFunc, foo.index]) .addBody([kExprRefFunc, foo.index])
.exportFunc(); .exportFunc();
......
...@@ -568,7 +568,7 @@ function dummy_func() { ...@@ -568,7 +568,7 @@ function dummy_func() {
const f_func = builder.addFunction('get_anyfunc_global', kSig_a_v) const f_func = builder.addFunction('get_anyfunc_global', kSig_a_v)
.addBody([kExprGlobalGet, g_func.index]) .addBody([kExprGlobalGet, g_func.index])
.exportAs('get_anyfunc_global'); .exportAs('get_anyfunc_global');
builder.addDeclarativeElementSegment([f_ref.index, f_func.index]);
g_ref.function_index = f_ref.index; g_ref.function_index = f_ref.index;
g_func.function_index = f_func.index; g_func.function_index = f_func.index;
...@@ -580,6 +580,18 @@ function dummy_func() { ...@@ -580,6 +580,18 @@ function dummy_func() {
instance.exports.get_anyfunc_global()); instance.exports.get_anyfunc_global());
})(); })();
(function TestRefFuncGlobalInitUndeclared() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const global_func = builder.addGlobal(kWasmAnyFunc, true);
const func = builder.addFunction('get_anyfunc_global', kSig_v_v).addBody([]);
global_func.function_index = func.index;
assertThrows(
() => builder.toModule(), WebAssembly.CompileError,
/undeclared reference to function/);
})();
(function TestRefFuncGlobalInitWithImport() { (function TestRefFuncGlobalInitWithImport() {
print(arguments.callee.name); print(arguments.callee.name);
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
...@@ -588,6 +600,7 @@ function dummy_func() { ...@@ -588,6 +600,7 @@ function dummy_func() {
const import_js = builder.addImport('m', 'js', sig_index); const import_js = builder.addImport('m', 'js', sig_index);
const g_wasm = builder.addGlobal(kWasmAnyFunc, true); const g_wasm = builder.addGlobal(kWasmAnyFunc, true);
const g_js = builder.addGlobal(kWasmAnyFunc, true); const g_js = builder.addGlobal(kWasmAnyFunc, true);
builder.addDeclarativeElementSegment([import_wasm, import_js]);
g_wasm.function_index = import_wasm; g_wasm.function_index = import_wasm;
g_js.function_index = import_js; g_js.function_index = import_js;
builder.addFunction('get_global_wasm', kSig_a_v) builder.addFunction('get_global_wasm', kSig_a_v)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --experimental-wasm-anyref // Flags: --experimental-wasm-anyref --experimental-wasm-bulk-memory
load("test/mjsunit/wasm/wasm-module-builder.js"); load("test/mjsunit/wasm/wasm-module-builder.js");
...@@ -45,3 +45,35 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); ...@@ -45,3 +45,35 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertThrows(() => builder.instantiate({ imp: { table: table_func } }), assertThrows(() => builder.instantiate({ imp: { table: table_func } }),
WebAssembly.LinkError, /imported table does not match the expected type/); WebAssembly.LinkError, /imported table does not match the expected type/);
})(); })();
(function TestAnyRefDropDeclarativeElementSegment() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addDeclarativeElementSegment([null]);
builder.addFunction('drop', kSig_v_v)
.addBody([kNumericPrefix, kExprElemDrop, 0])
.exportFunc();
const instance = builder.instantiate();
// Counts as double-drop because declarative segments are dropped on
// initialization and is therefore not expected to throw.
instance.exports.drop();
})();
(function TestAnyRefTableInitFromDeclarativeElementSegment() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const table = builder.addTable(kWasmAnyFunc, 10);
builder.addDeclarativeElementSegment([null]);
builder.addFunction('init', kSig_v_v)
.addBody([
kExprI32Const, 0, kExprI32Const, 0, kExprI32Const, 1, kNumericPrefix,
kExprTableInit, table.index, 0
])
.exportFunc();
const instance = builder.instantiate();
assertTraps(kTrapTableOutOfBounds, () => instance.exports.init());
})();
...@@ -138,6 +138,7 @@ const dummy_func = exports.set_table_func1; ...@@ -138,6 +138,7 @@ const dummy_func = exports.set_table_func1;
const function_index = builder.addFunction('hidden', sig_index) const function_index = builder.addFunction('hidden', sig_index)
.addBody([kExprI32Const, expected]) .addBody([kExprI32Const, expected])
.index; .index;
builder.addDeclarativeElementSegment([function_index]);
builder.addFunction('main', kSig_i_v) builder.addFunction('main', kSig_i_v)
.addBody([ .addBody([
......
...@@ -93,8 +93,7 @@ function checkAnyRefTable(getter, start, count, value) { ...@@ -93,8 +93,7 @@ function checkAnyRefTable(getter, start, count, value) {
(function testAnyRefTableFillOOB() { (function testAnyRefTableFillOOB() {
print(arguments.callee.name); print(arguments.callee.name);
// Fill table out-of-bounds, check if the table got filled as much as // Fill table out-of-bounds, check if the table wasn't altered.
// possible.
let start = 7; let start = 7;
let value = {foo: 27}; let value = {foo: 27};
// {maximum + 4} elements definitely don't fit into the table. // {maximum + 4} elements definitely don't fit into the table.
...@@ -103,14 +102,14 @@ function checkAnyRefTable(getter, start, count, value) { ...@@ -103,14 +102,14 @@ function checkAnyRefTable(getter, start, count, value) {
kTrapTableOutOfBounds, kTrapTableOutOfBounds,
() => instance.exports[`fill${import_ref}`](start, value, count)); () => instance.exports[`fill${import_ref}`](start, value, count));
checkAnyRefTable( checkAnyRefTable(
instance.exports[`get${import_ref}`], start, size - start, value); instance.exports[`get${import_ref}`], start, size - start, null);
value = 45; value = 45;
assertTraps( assertTraps(
kTrapTableOutOfBounds, kTrapTableOutOfBounds,
() => instance.exports[`fill${internal_ref}`](start, value, count)); () => instance.exports[`fill${internal_ref}`](start, value, count));
checkAnyRefTable( checkAnyRefTable(
instance.exports[`get${internal_ref}`], start, size - start, value); instance.exports[`get${internal_ref}`], start, size - start, null);
})(); })();
(function testAnyRefTableFillOOBCountZero() { (function testAnyRefTableFillOOBCountZero() {
...@@ -160,8 +159,7 @@ function checkAnyFuncTable(call, start, count, value) { ...@@ -160,8 +159,7 @@ function checkAnyFuncTable(call, start, count, value) {
(function testAnyFuncTableFillOOB() { (function testAnyFuncTableFillOOB() {
print(arguments.callee.name); print(arguments.callee.name);
// Fill table out-of-bounds, check if the table got filled as much as // Fill table out-of-bounds, check if the table wasn't altered.
// possible.
let start = 7; let start = 7;
let value = 38; let value = 38;
// {maximum + 4} elements definitely don't fit into the table. // {maximum + 4} elements definitely don't fit into the table.
...@@ -171,7 +169,7 @@ function checkAnyFuncTable(call, start, count, value) { ...@@ -171,7 +169,7 @@ function checkAnyFuncTable(call, start, count, value) {
() => instance.exports[`fill${import_func}`]( () => instance.exports[`fill${import_func}`](
start, dummy_func(value), count)); start, dummy_func(value), count));
checkAnyFuncTable( checkAnyFuncTable(
instance.exports[`call${import_func}`], start, size - start, value); instance.exports[`call${import_func}`], start, size - start, null);
value = 46; value = 46;
assertTraps( assertTraps(
...@@ -179,7 +177,7 @@ function checkAnyFuncTable(call, start, count, value) { ...@@ -179,7 +177,7 @@ function checkAnyFuncTable(call, start, count, value) {
() => instance.exports[`fill${internal_func}`]( () => instance.exports[`fill${internal_func}`](
start, dummy_func(value), count)); start, dummy_func(value), count));
checkAnyFuncTable( checkAnyFuncTable(
instance.exports[`call${internal_func}`], start, size - start, value); instance.exports[`call${internal_func}`], start, size - start, null);
})(); })();
(function testAnyFuncTableFillOOBCountZero() { (function testAnyFuncTableFillOOBCountZero() {
......
...@@ -82,7 +82,9 @@ let kSharedHasMaximumFlag = 3; ...@@ -82,7 +82,9 @@ let kSharedHasMaximumFlag = 3;
let kActiveNoIndex = 0; let kActiveNoIndex = 0;
let kPassive = 1; let kPassive = 1;
let kActiveWithIndex = 2; let kActiveWithIndex = 2;
let kDeclarative = 3;
let kPassiveWithElements = 5; let kPassiveWithElements = 5;
let kDeclarativeWithElements = 7;
// Function declaration flags // Function declaration flags
let kDeclFunctionName = 0x01; let kDeclFunctionName = 0x01;
...@@ -906,13 +908,26 @@ class WasmModuleBuilder { ...@@ -906,13 +908,26 @@ class WasmModuleBuilder {
} }
addElementSegment(table, base, is_global, array) { addElementSegment(table, base, is_global, array) {
this.element_segments.push({table: table, base: base, is_global: is_global, this.element_segments.push({
array: array, is_active: true}); table: table,
base: base,
is_global: is_global,
array: array,
is_active: true,
is_declarative: false
});
return this; return this;
} }
addPassiveElementSegment(array, is_import = false) { addPassiveElementSegment(array, is_import = false) {
this.element_segments.push({array: array, is_active: false}); this.element_segments.push(
{array: array, is_active: false, is_declarative: false});
return this;
}
addDeclarativeElementSegment(array, is_import = false) {
this.element_segments.push(
{array: array, is_active: false, is_declarative: true});
return this; return this;
} }
...@@ -1180,9 +1195,20 @@ class WasmModuleBuilder { ...@@ -1180,9 +1195,20 @@ class WasmModuleBuilder {
for (let index of init.array) { for (let index of init.array) {
section.emit_u32v(index); section.emit_u32v(index);
} }
} else if (
init.is_declarative &&
init.array.every(index => index !== null)) {
section.emit_u8(kDeclarative);
section.emit_u8(kExternalFunction);
section.emit_u32v(init.array.length);
for (let index of init.array) {
section.emit_u32v(index);
}
} else { } else {
// Passive segment. // Passive or declarative segment with elements.
section.emit_u8(kPassiveWithElements); // flags section.emit_u8(
init.is_declarative ? kDeclarativeWithElements :
kPassiveWithElements); // flags
section.emit_u8(kWasmAnyFunc); section.emit_u8(kWasmAnyFunc);
section.emit_u32v(init.array.length); section.emit_u32v(init.array.length);
for (let index of init.array) { for (let index of init.array) {
......
...@@ -220,13 +220,14 @@ class TestModuleBuilder { ...@@ -220,13 +220,14 @@ class TestModuleBuilder {
CHECK_LE(mod.signatures.size(), kMaxByteSizedLeb128); CHECK_LE(mod.signatures.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.signatures.size() - 1); return static_cast<byte>(mod.signatures.size() - 1);
} }
byte AddFunction(FunctionSig* sig) { byte AddFunction(FunctionSig* sig, bool declared = true) {
mod.functions.push_back({sig, // sig mod.functions.push_back({sig, // sig
0, // func_index 0, // func_index
0, // sig_index 0, // sig_index
{0, 0}, // code {0, 0}, // code
false, // import false, // import
false}); // export false, // export
declared}); // declared
CHECK_LE(mod.functions.size(), kMaxByteSizedLeb128); CHECK_LE(mod.functions.size(), kMaxByteSizedLeb128);
return static_cast<byte>(mod.functions.size() - 1); return static_cast<byte>(mod.functions.size() - 1);
} }
...@@ -262,7 +263,7 @@ class TestModuleBuilder { ...@@ -262,7 +263,7 @@ class TestModuleBuilder {
void InitializeTable() { mod.tables.emplace_back(); } void InitializeTable() { mod.tables.emplace_back(); }
byte AddPassiveElementSegment() { byte AddPassiveElementSegment() {
mod.elem_segments.emplace_back(); mod.elem_segments.emplace_back(false);
auto& init = mod.elem_segments.back(); auto& init = mod.elem_segments.back();
// Add 5 empty elements. // Add 5 empty elements.
for (uint32_t j = 0; j < 5; j++) { for (uint32_t j = 0; j < 5; j++) {
...@@ -271,6 +272,12 @@ class TestModuleBuilder { ...@@ -271,6 +272,12 @@ class TestModuleBuilder {
return static_cast<byte>(mod.elem_segments.size() - 1); return static_cast<byte>(mod.elem_segments.size() - 1);
} }
byte AddDeclarativeElementSegment() {
mod.elem_segments.emplace_back(true);
mod.elem_segments.back().entries.push_back(WasmElemSegment::kNullIndex);
return static_cast<byte>(mod.elem_segments.size() - 1);
}
// Set the number of data segments as declared by the DataCount section. // Set the number of data segments as declared by the DataCount section.
void SetDataSegmentCount(uint32_t data_segment_count) { void SetDataSegmentCount(uint32_t data_segment_count) {
// The Data section occurs after the Code section, so we don't need to // The Data section occurs after the Code section, so we don't need to
...@@ -3242,6 +3249,57 @@ TEST_F(FunctionBodyDecoderTest, ElemDrop) { ...@@ -3242,6 +3249,57 @@ TEST_F(FunctionBodyDecoderTest, ElemDrop) {
ExpectFailure(sigs.v_v(), {WASM_ELEM_DROP(1)}); ExpectFailure(sigs.v_v(), {WASM_ELEM_DROP(1)});
} }
TEST_F(FunctionBodyDecoderTest, TableInitDeclarativeElem) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.AddDeclarativeElementSegment();
module = builder.module();
WASM_FEATURE_SCOPE(bulk_memory);
WASM_FEATURE_SCOPE(anyref);
byte code[] = {WASM_TABLE_INIT(0, 0, WASM_ZERO, WASM_ZERO, WASM_ZERO),
WASM_END};
for (size_t i = 0; i <= arraysize(code); ++i) {
Validate(i == arraysize(code), sigs.v_v(), VectorOf(code, i), kOmitEnd);
}
}
TEST_F(FunctionBodyDecoderTest, DeclarativeElemDrop) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.AddDeclarativeElementSegment();
module = builder.module();
ExpectFailure(sigs.v_v(), {WASM_ELEM_DROP(0)});
WASM_FEATURE_SCOPE(bulk_memory);
WASM_FEATURE_SCOPE(anyref);
ExpectValidates(sigs.v_v(), {WASM_ELEM_DROP(0)});
ExpectFailure(sigs.v_v(), {WASM_ELEM_DROP(1)});
}
TEST_F(FunctionBodyDecoderTest, RefFuncDeclared) {
TestModuleBuilder builder;
builder.InitializeTable();
byte function_index = builder.AddFunction(sigs.v_i());
module = builder.module();
ExpectFailure(sigs.a_v(), {WASM_REF_FUNC(function_index)});
WASM_FEATURE_SCOPE(bulk_memory);
WASM_FEATURE_SCOPE(anyref);
ExpectValidates(sigs.a_v(), {WASM_REF_FUNC(function_index)});
}
TEST_F(FunctionBodyDecoderTest, RefFuncUndeclared) {
TestModuleBuilder builder;
builder.InitializeTable();
byte function_index = builder.AddFunction(sigs.v_i(), false);
module = builder.module();
WASM_FEATURE_SCOPE(bulk_memory);
WASM_FEATURE_SCOPE(anyref);
ExpectFailure(sigs.a_v(), {WASM_REF_FUNC(function_index)});
}
TEST_F(FunctionBodyDecoderTest, ElemSegmentIndexUnsigned) { TEST_F(FunctionBodyDecoderTest, ElemSegmentIndexUnsigned) {
TestModuleBuilder builder; TestModuleBuilder builder;
builder.InitializeTable(); builder.InitializeTable();
......
...@@ -130,6 +130,13 @@ struct CheckLEB1 : std::integral_constant<size_t, num> { ...@@ -130,6 +130,13 @@ struct CheckLEB1 : std::integral_constant<size_t, num> {
#define EXPECT_FAILURE(data) EXPECT_FAILURE_LEN(data, sizeof(data)) #define EXPECT_FAILURE(data) EXPECT_FAILURE_LEN(data, sizeof(data))
#define EXPECT_FAILURE_WITH_MSG(data, msg) \
do { \
ModuleResult result = DecodeModule(data, data + sizeof(data)); \
EXPECT_FALSE(result.ok()); \
EXPECT_THAT(result.error().message(), HasSubstr(msg)); \
} while (false)
#define EXPECT_OFF_END_FAILURE(data, min) \ #define EXPECT_OFF_END_FAILURE(data, min) \
do { \ do { \
STATIC_ASSERT(min < arraysize(data)); \ STATIC_ASSERT(min < arraysize(data)); \
...@@ -252,6 +259,7 @@ TEST_F(WasmModuleVerifyTest, OneGlobal) { ...@@ -252,6 +259,7 @@ TEST_F(WasmModuleVerifyTest, OneGlobal) {
TEST_F(WasmModuleVerifyTest, AnyRefGlobal) { TEST_F(WasmModuleVerifyTest, AnyRefGlobal) {
WASM_FEATURE_SCOPE(anyref); WASM_FEATURE_SCOPE(anyref);
WASM_FEATURE_SCOPE(bulk_memory);
static const byte data[] = { static const byte data[] = {
// sig#0 --------------------------------------------------------------- // sig#0 ---------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID, SIGNATURES_SECTION_VOID_VOID,
...@@ -265,6 +273,16 @@ TEST_F(WasmModuleVerifyTest, AnyRefGlobal) { ...@@ -265,6 +273,16 @@ TEST_F(WasmModuleVerifyTest, AnyRefGlobal) {
kLocalAnyRef, // local type kLocalAnyRef, // local type
0, // immutable 0, // immutable
WASM_INIT_EXPR_REF_FUNC(1)), // init WASM_INIT_EXPR_REF_FUNC(1)), // init
SECTION(Element, // section name
ENTRY_COUNT(2), // entry count
DECLARATIVE, // flags 0
kExternalFunction, // type
ENTRY_COUNT(1), // func entry count
FUNC_INDEX(0), // func index
DECLARATIVE_WITH_ELEMENTS, // flags 1
kLocalFuncRef, // local type
ENTRY_COUNT(1), // func ref count
REF_FUNC_ELEMENT(1)), // func ref
TWO_EMPTY_BODIES}; TWO_EMPTY_BODIES};
{ {
...@@ -290,6 +308,7 @@ TEST_F(WasmModuleVerifyTest, AnyRefGlobal) { ...@@ -290,6 +308,7 @@ TEST_F(WasmModuleVerifyTest, AnyRefGlobal) {
TEST_F(WasmModuleVerifyTest, FuncRefGlobal) { TEST_F(WasmModuleVerifyTest, FuncRefGlobal) {
WASM_FEATURE_SCOPE(anyref); WASM_FEATURE_SCOPE(anyref);
WASM_FEATURE_SCOPE(bulk_memory);
static const byte data[] = { static const byte data[] = {
// sig#0 --------------------------------------------------------------- // sig#0 ---------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID, SIGNATURES_SECTION_VOID_VOID,
...@@ -303,6 +322,16 @@ TEST_F(WasmModuleVerifyTest, FuncRefGlobal) { ...@@ -303,6 +322,16 @@ TEST_F(WasmModuleVerifyTest, FuncRefGlobal) {
kLocalFuncRef, // local type kLocalFuncRef, // local type
0, // immutable 0, // immutable
WASM_INIT_EXPR_REF_FUNC(1)), // init WASM_INIT_EXPR_REF_FUNC(1)), // init
SECTION(Element, // section name
ENTRY_COUNT(2), // entry count
DECLARATIVE, // flags 0
kExternalFunction, // type
ENTRY_COUNT(1), // func entry count
FUNC_INDEX(0), // func index
DECLARATIVE_WITH_ELEMENTS, // flags 1
kLocalFuncRef, // local type
ENTRY_COUNT(1), // func ref count
REF_FUNC_ELEMENT(1)), // func ref
TWO_EMPTY_BODIES}; TWO_EMPTY_BODIES};
{ {
// Should decode to two globals. // Should decode to two globals.
...@@ -2507,7 +2536,7 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegmentWithIndices) { ...@@ -2507,7 +2536,7 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegmentWithIndices) {
ONE_EMPTY_FUNCTION(SIG_INDEX(0)), ONE_EMPTY_FUNCTION(SIG_INDEX(0)),
// table declaration ----------------------------------------------------- // table declaration -----------------------------------------------------
SECTION(Table, ENTRY_COUNT(1), kLocalFuncRef, 0, 1), SECTION(Table, ENTRY_COUNT(1), kLocalFuncRef, 0, 1),
// element segments ----------------------------------------------------- // element segments ------------------------------------------------------
SECTION(Element, ENTRY_COUNT(1), PASSIVE, kExternalFunction, SECTION(Element, ENTRY_COUNT(1), PASSIVE, kExternalFunction,
ENTRY_COUNT(3), U32V_1(0), U32V_1(0), U32V_1(0)), ENTRY_COUNT(3), U32V_1(0), U32V_1(0), U32V_1(0)),
// code ------------------------------------------------------------------ // code ------------------------------------------------------------------
...@@ -2518,6 +2547,67 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegmentWithIndices) { ...@@ -2518,6 +2547,67 @@ TEST_F(WasmModuleVerifyTest, PassiveElementSegmentWithIndices) {
EXPECT_OFF_END_FAILURE(data, arraysize(data) - 5); EXPECT_OFF_END_FAILURE(data, arraysize(data) - 5);
} }
TEST_F(WasmModuleVerifyTest, DeclarativeElementSegmentFuncRef) {
static const byte data[] = {
// sig#0 -----------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// funcs -----------------------------------------------------------------
ONE_EMPTY_FUNCTION(SIG_INDEX(0)),
// element segments -----------------------------------------------------
SECTION(Element, // section name
ENTRY_COUNT(1), // entry count
DECLARATIVE_WITH_ELEMENTS, // flags
kLocalFuncRef, // local type
U32V_1(0)), // func ref count
// code ------------------------------------------------------------------
ONE_EMPTY_BODY};
EXPECT_FAILURE(data);
WASM_FEATURE_SCOPE(bulk_memory);
EXPECT_FAILURE(data);
WASM_FEATURE_SCOPE(anyref);
EXPECT_VERIFIES(data);
}
TEST_F(WasmModuleVerifyTest, DeclarativeElementSegmentWithInvalidIndex) {
WASM_FEATURE_SCOPE(bulk_memory);
WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {
// sig#0 -----------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// funcs -----------------------------------------------------------------
ONE_EMPTY_FUNCTION(SIG_INDEX(0)),
// element segments -----------------------------------------------------
SECTION(Element, // section name
ENTRY_COUNT(1), // entry count
DECLARATIVE, // flags
kExternalFunction, // type
ENTRY_COUNT(2), // func index count
U32V_1(0), // func index
U32V_1(1)), // func index
// code ------------------------------------------------------------------
ONE_EMPTY_BODY};
EXPECT_FAILURE_WITH_MSG(data, "element function index 1 out of bounds");
}
TEST_F(WasmModuleVerifyTest, DeclarativeElementSegmentMissingForGlobal) {
WASM_FEATURE_SCOPE(bulk_memory);
WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {
// sig#0 -----------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// funcs -----------------------------------------------------------------
ONE_EMPTY_FUNCTION(SIG_INDEX(0)),
// global definitions ----------------------------------------------------
SECTION(Global, // section name
ENTRY_COUNT(1), // entry count
kLocalAnyRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_FUNC(0)), // init
// code ------------------------------------------------------------------
ONE_EMPTY_BODY};
EXPECT_FAILURE_WITH_MSG(data, "undeclared reference to function");
}
TEST_F(WasmModuleVerifyTest, DataCountSectionCorrectPlacement) { TEST_F(WasmModuleVerifyTest, DataCountSectionCorrectPlacement) {
static const byte data[] = {SECTION(Element, ENTRY_COUNT(0)), static const byte data[] = {SECTION(Element, ENTRY_COUNT(0)),
SECTION(DataCount, ENTRY_COUNT(0)), SECTION(DataCount, ENTRY_COUNT(0)),
......
...@@ -24,13 +24,6 @@ ...@@ -24,13 +24,6 @@
'proposals/multi-value/call': [FAIL], 'proposals/multi-value/call': [FAIL],
'proposals/multi-value/if': [FAIL], 'proposals/multi-value/if': [FAIL],
'proposals/multi-value/func': [FAIL], 'proposals/multi-value/func': [FAIL],
# TODO(v8:10156): Spec tests are failing after rebasing the reference-types
# proposal on the bulk-operations proposal.
'proposals/reference-types/elem': [FAIL],
'proposals/reference-types/ref_func': [FAIL],
'proposals/reference-types/table_fill': [FAIL],
'proposals/reference-types/table_grow': [FAIL],
}], # ALWAYS }], # ALWAYS
['arch == mipsel or arch == mips64el or arch == mips or arch == mips64', { ['arch == mipsel or arch == mips64el or arch == mips or arch == mips64', {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment