diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/binaryen-c.cpp | 2 | ||||
-rw-r--r-- | src/ir/element-utils.h | 5 | ||||
-rw-r--r-- | src/ir/module-splitting.cpp | 19 | ||||
-rw-r--r-- | src/ir/module-utils.h | 11 | ||||
-rw-r--r-- | src/ir/table-utils.h | 2 | ||||
-rw-r--r-- | src/passes/Print.cpp | 8 | ||||
-rw-r--r-- | src/shell-interface.h | 16 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 45 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 3 | ||||
-rw-r--r-- | src/wasm-builder.h | 7 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 3 | ||||
-rw-r--r-- | src/wasm.h | 13 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 57 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 34 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 83 | ||||
-rw-r--r-- | src/wasm2js.h | 4 |
16 files changed, 222 insertions, 90 deletions
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index e257c976c..b45f20857 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -3359,7 +3359,7 @@ BinaryenTableRef BinaryenAddTable(BinaryenModuleRef module, const char* name, BinaryenIndex initial, BinaryenIndex maximum) { - auto table = Builder::makeTable(name, initial, maximum); + auto table = Builder::makeTable(name, Type::funcref, initial, maximum); table->hasExplicitName = true; return ((Module*)module)->addTable(std::move(table)); } diff --git a/src/ir/element-utils.h b/src/ir/element-utils.h index adfd9955f..1ec6c7e20 100644 --- a/src/ir/element-utils.h +++ b/src/ir/element-utils.h @@ -28,7 +28,10 @@ namespace ElementUtils { template<typename T> inline void iterElementSegmentFunctionNames(ElementSegment* segment, T visitor) { - // TODO(reference-types): return early if segment type is non-funcref + if (!segment->type.isFunction()) { + return; + } + for (Index i = 0; i < segment->data.size(); i++) { if (auto* get = segment->data[i]->dynCast<RefFunc>()) { visitor(get->func, i); diff --git a/src/ir/module-splitting.cpp b/src/ir/module-splitting.cpp index 5b86dd494..0634171ce 100644 --- a/src/ir/module-splitting.cpp +++ b/src/ir/module-splitting.cpp @@ -157,11 +157,16 @@ void TableSlotManager::addSlot(Name func, Slot slot) { TableSlotManager::TableSlotManager(Module& module) : module(module) { // TODO: Reject or handle passive element segments - if (module.tables.empty()) { + auto it = std::find_if(module.tables.begin(), + module.tables.end(), + [&](std::unique_ptr<Table>& table) { + return table->type == Type::funcref; + }); + if (it == module.tables.end()) { return; } - activeTable = module.tables.front().get(); + activeTable = it->get(); ModuleUtils::iterTableSegments( module, activeTable->name, [&](ElementSegment* segment) { activeTableSegments.push_back(segment); @@ -172,6 +177,7 @@ TableSlotManager::TableSlotManager(Module& module) : module(module) { // append new items at constant offsets after all existing items at constant // offsets. if (activeTableSegments.size() == 1 && + activeTableSegments[0]->type == Type::funcref && !activeTableSegments[0]->offset->is<Const>()) { assert(activeTableSegments[0]->offset->is<GlobalGet>() && "Unexpected initializer instruction"); @@ -204,7 +210,8 @@ TableSlotManager::TableSlotManager(Module& module) : module(module) { } Table* TableSlotManager::makeTable() { - return module.addTable(Builder::makeTable(Name::fromInt(0))); + return module.addTable( + Builder::makeTable(Names::getValidTableName(module, Name::fromInt(0)))); } TableSlotManager::Slot TableSlotManager::getSlot(RefFunc* entry) { @@ -533,7 +540,7 @@ void ModuleSplitter::setupTablePatching() { auto offset = ExpressionManipulator::copy(primarySeg->offset, secondary); auto secondarySeg = std::make_unique<ElementSegment>( - secondaryTable->name, offset, secondaryElems); + secondaryTable->name, offset, secondaryTable->type, secondaryElems); secondarySeg->setName(primarySeg->name, primarySeg->hasExplicitName); secondary.addElementSegment(std::move(secondarySeg)); return; @@ -545,8 +552,8 @@ void ModuleSplitter::setupTablePatching() { std::vector<Expression*> currData; auto finishSegment = [&]() { auto* offset = Builder(secondary).makeConst(int32_t(currBase)); - auto secondarySeg = - std::make_unique<ElementSegment>(secondaryTable->name, offset, currData); + auto secondarySeg = std::make_unique<ElementSegment>( + secondaryTable->name, offset, secondaryTable->type, currData); secondarySeg->setName(Name::fromInt(secondary.elementSegments.size()), false); secondary.addElementSegment(std::move(secondarySeg)); diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index 666718c92..8d778bc3c 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -76,6 +76,7 @@ inline ElementSegment* copyElementSegment(const ElementSegment* segment, auto copy = [&](std::unique_ptr<ElementSegment>&& ret) { ret->name = segment->name; ret->hasExplicitName = segment->hasExplicitName; + ret->type = segment->type; ret->data.reserve(segment->data.size()); for (auto* item : segment->data) { ret->data.push_back(ExpressionManipulator::copy(item, out)); @@ -92,9 +93,11 @@ inline ElementSegment* copyElementSegment(const ElementSegment* segment, } } -inline Table* copyTable(Table* table, Module& out) { +inline Table* copyTable(const Table* table, Module& out) { auto ret = std::make_unique<Table>(); ret->name = table->name; + ret->hasExplicitName = table->hasExplicitName; + ret->type = table->type; ret->module = table->module; ret->base = table->base; @@ -510,6 +513,12 @@ inline void collectHeapTypes(Module& wasm, for (auto& curr : wasm.events) { counts.note(curr->sig); } + for (auto& curr : wasm.tables) { + counts.maybeNote(curr->type); + } + for (auto& curr : wasm.elementSegments) { + counts.maybeNote(curr->type); + } // Collect info from functions in parallel. ModuleUtils::ParallelFunctionAnalysis<Counts> analysis( diff --git a/src/ir/table-utils.h b/src/ir/table-utils.h index 2d91f0035..d3151bd2c 100644 --- a/src/ir/table-utils.h +++ b/src/ir/table-utils.h @@ -36,7 +36,7 @@ struct FlatTable { ModuleUtils::iterTableSegments( wasm, table.name, [&](ElementSegment* segment) { auto offset = segment->offset; - if (!offset->is<Const>()) { + if (!offset->is<Const>() || !segment->type.isFunction()) { // TODO: handle some non-constant segments valid = false; return; diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 5f8c9f163..929822309 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2619,7 +2619,8 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { if (curr->hasMax()) { o << ' ' << curr->max; } - o << " funcref)"; + o << ' '; + printType(o, curr->type, currModule) << ')'; } void visitTable(Table* curr) { if (curr->imported()) { @@ -2656,9 +2657,9 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { }); auto printElemType = [&]() { if (allElementsRefFunc) { - TypeNamePrinter(o, currModule).print(HeapType::func); + o << "func"; } else { - TypeNamePrinter(o, currModule).print(Type::funcref); + printType(o, curr->type, currModule); } }; @@ -2671,7 +2672,6 @@ struct PrintSExpression : public UnifiedExpressionVisitor<PrintSExpression> { } if (curr->table.is()) { - // TODO(reference-types): check for old-style based on the complete spec if (!allElementsRefFunc || currModule->tables.size() > 1) { // tableuse o << " (table "; diff --git a/src/shell-interface.h b/src/shell-interface.h index 0ba4946dd..eb0e9f91c 100644 --- a/src/shell-interface.h +++ b/src/shell-interface.h @@ -143,6 +143,13 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { wasm.memory.initial = 1; wasm.memory.max = 2; } + + ModuleUtils::iterImportedTables(wasm, [&](Table* table) { + if (table->module == SPECTEST && table->base == TABLE) { + table->initial = 10; + table->max = 20; + } + }); } Literals callImport(Function* import, LiteralList& arguments) override { @@ -234,8 +241,13 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { memory.set<std::array<uint8_t, 16>>(addr, value); } - void tableStore(Name tableName, Address addr, Literal entry) override { - tables[tableName][addr] = entry; + void tableStore(Name tableName, Address addr, const Literal& entry) override { + auto& table = tables[tableName]; + if (addr >= table.size()) { + trap("out of bounds table access"); + } else { + table.emplace(table.begin() + addr, entry); + } } bool growMemory(Address /*oldSize*/, Address newSize) override { diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 81eab1d28..b5b7671cd 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -426,18 +426,31 @@ private: // TODO(reference-types): allow the fuzzer to create multiple tables void setupTables() { - // Ensure an element segment, adding one or even adding a whole table as - // needed. - if (wasm.tables.empty()) { - auto table = builder.makeTable( - Names::getValidTableName(wasm, "fuzzing_table"), 0, 0); - table->hasExplicitName = true; - wasm.addTable(std::move(table)); - } - if (wasm.elementSegments.empty()) { + // Ensure a funcref element segment and table exist. Segments with more + // specific function types may have a smaller chance of getting functions. + Table* table = nullptr; + auto iter = + std::find_if(wasm.tables.begin(), wasm.tables.end(), [&](auto& table) { + return table->type == Type::funcref; + }); + if (iter != wasm.tables.end()) { + table = iter->get(); + } else { + auto tablePtr = builder.makeTable( + Names::getValidTableName(wasm, "fuzzing_table"), Type::funcref, 0, 0); + tablePtr->hasExplicitName = true; + table = wasm.addTable(std::move(tablePtr)); + } + bool hasFuncrefElemSegment = std::any_of( + wasm.elementSegments.begin(), + wasm.elementSegments.end(), + [&](auto& segment) { + return segment->table.is() && segment->type == Type::funcref; + }); + if (!hasFuncrefElemSegment) { // TODO: use a random table auto segment = std::make_unique<ElementSegment>( - wasm.tables[0]->name, builder.makeConst(int32_t(0))); + table->name, builder.makeConst(int32_t(0))); segment->setName(Names::getValidElementSegmentName(wasm, "elem$"), false); wasm.addElementSegment(std::move(segment)); } @@ -722,10 +735,16 @@ private: } // add some to an elem segment while (oneIn(3) && !finishedInput) { - auto& randomElem = - wasm.elementSegments[upTo(wasm.elementSegments.size())]; - // FIXME: make the type NonNullable when we support it! auto type = Type(HeapType(func->sig), Nullable); + std::vector<ElementSegment*> compatibleSegments; + ModuleUtils::iterActiveElementSegments( + wasm, [&](ElementSegment* segment) { + if (Type::isSubType(type, segment->type)) { + compatibleSegments.push_back(segment); + } + }); + auto& randomElem = compatibleSegments[upTo(compatibleSegments.size())]; + // FIXME: make the type NonNullable when we support it! randomElem->data.push_back(builder.makeRefFunc(func->name, type)); } numAddedFunctions++; diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 13a522135..6662dd176 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -301,7 +301,8 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { } // called during initialization, but we don't keep track of a table - void tableStore(Name tableName, Address addr, Literal value) override {} + void tableStore(Name tableName, Address addr, const Literal& value) override { + } bool growMemory(Address /*oldSize*/, Address newSize) override { throw FailToEvalException("grow memory"); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index c0d6cbeeb..d1e06b5be 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -79,10 +79,13 @@ public: return func; } - static std::unique_ptr<Table> - makeTable(Name name, Address initial = 0, Address max = Table::kMaxSize) { + static std::unique_ptr<Table> makeTable(Name name, + Type type = Type::funcref, + Address initial = 0, + Address max = Table::kMaxSize) { auto table = std::make_unique<Table>(); table->name = name; + table->type = type; table->initial = initial; table->max = max; diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 249ec8fdb..9118eaae1 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2213,7 +2213,8 @@ public: WASM_UNREACHABLE("unimp"); } - virtual void tableStore(Name tableName, Address addr, Literal entry) { + virtual void + tableStore(Name tableName, Address addr, const Literal& entry) { WASM_UNREACHABLE("unimp"); } }; diff --git a/src/wasm.h b/src/wasm.h index 8e9f93500..dd2643d0b 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1678,13 +1678,17 @@ class ElementSegment : public Named { public: Name table; Expression* offset; + Type type = Type::funcref; std::vector<Expression*> data; ElementSegment() = default; - ElementSegment(Name table, Expression* offset) - : table(table), offset(offset) {} - ElementSegment(Name table, Expression* offset, std::vector<Expression*>& init) - : table(table), offset(offset) { + ElementSegment(Name table, Expression* offset, Type type = Type::funcref) + : table(table), offset(offset), type(type) {} + ElementSegment(Name table, + Expression* offset, + Type type, + std::vector<Expression*>& init) + : table(table), offset(offset), type(type) { data.swap(init); } }; @@ -1698,6 +1702,7 @@ public: Address initial = 0; Address max = kMaxSize; + Type type = Type::funcref; bool hasMax() { return max != kUnlimitedSize; } void clear() { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 3a9a1eece..df2bc8eba 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -291,7 +291,7 @@ void WasmBinaryWriter::writeImports() { BYN_TRACE("write one table\n"); writeImportHeader(table); o << U32LEB(int32_t(ExternalKind::Table)); - o << S32LEB(BinaryConsts::EncodedType::funcref); + writeType(table->type); writeResizableLimits(table->initial, table->max, table->hasMax(), @@ -543,7 +543,7 @@ void WasmBinaryWriter::writeTableDeclarations() { auto num = importInfo->getNumDefinedTables(); o << U32LEB(num); ModuleUtils::iterDefinedTables(*wasm, [&](Table* table) { - o << S32LEB(BinaryConsts::EncodedType::funcref); + writeType(table->type); writeResizableLimits(table->initial, table->max, table->hasMax(), @@ -604,8 +604,8 @@ void WasmBinaryWriter::writeElementSegments() { if (isPassive || hasTableIndex) { if (usesExpressions) { - // elemType funcref - writeType(Type::funcref); + // elemType + writeType(segment->type); } else { // elemKind funcref o << U32LEB(0); @@ -1994,11 +1994,7 @@ void WasmBinaryBuilder::readImports() { auto table = builder.makeTable(name); table->module = module; table->base = base; - auto elementType = getS32LEB(); - WASM_UNUSED(elementType); - if (elementType != BinaryConsts::EncodedType::funcref) { - throwError("Imported table type is not funcref"); - } + table->type = getType(); bool is_shared; Type indexType; @@ -2765,11 +2761,11 @@ void WasmBinaryBuilder::readTableDeclarations() { auto numTables = getU32LEB(); for (size_t i = 0; i < numTables; i++) { - auto elemType = getS32LEB(); - if (elemType != BinaryConsts::EncodedType::funcref) { - throwError("Non-funcref tables not yet supported"); + auto elemType = getType(); + if (!elemType.isRef()) { + throwError("Table type must be a reference type"); } - auto table = Builder::makeTable(Name::fromInt(i)); + auto table = Builder::makeTable(Name::fromInt(i), elemType); bool is_shared; Type indexType; getResizableLimits( @@ -2811,38 +2807,35 @@ void WasmBinaryBuilder::readElementSegments() { continue; } + auto segment = std::make_unique<ElementSegment>(); + segment->setName(Name::fromInt(i), false); + if (!isPassive) { Index tableIdx = 0; if (hasTableIdx) { tableIdx = getU32LEB(); } - auto makeActiveElem = [&](Table* table) { - auto segment = - std::make_unique<ElementSegment>(table->name, readExpression()); - segment->setName(Name::fromInt(i), false); - elementSegments.push_back(std::move(segment)); - }; - + Table* table = nullptr; auto numTableImports = tableImports.size(); if (tableIdx < numTableImports) { - makeActiveElem(tableImports[tableIdx]); + table = tableImports[tableIdx]; } else if (tableIdx - numTableImports < tables.size()) { - makeActiveElem(tables[tableIdx - numTableImports].get()); - } else { + table = tables[tableIdx - numTableImports].get(); + } + if (!table) { throwError("Table index out of range."); } - } else { - auto segment = std::make_unique<ElementSegment>(); - segment->setName(Name::fromInt(i), false); - elementSegments.push_back(std::move(segment)); + + segment->table = table->name; + segment->offset = readExpression(); } if (isPassive || hasTableIdx) { if (usesExpressions) { - auto type = getType(); - if (type != Type::funcref) { - throwError("Only funcref elem kinds are valid."); + segment->type = getType(); + if (!segment->type.isFunction()) { + throwError("Invalid type for an element segment"); } } else { auto elemKind = getU32LEB(); @@ -2852,7 +2845,7 @@ void WasmBinaryBuilder::readElementSegments() { } } - auto& segmentData = elementSegments.back()->data; + auto& segmentData = segment->data; auto size = getU32LEB(); if (usesExpressions) { for (Index j = 0; j < size; j++) { @@ -2869,6 +2862,8 @@ void WasmBinaryBuilder::readElementSegments() { segmentData.push_back(refFunc); } } + + elementSegments.push_back(std::move(segment)); } } diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 98a6cb827..a1ec5f134 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -3197,7 +3197,7 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { table->module = inner[1]->str(); table->base = inner[2]->str(); i++; - } else { + } else if (!elementStartsWith(inner, REF)) { throw ParseException("invalid table", inner.line, inner.col); } } @@ -3208,15 +3208,13 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { table->initial = atoi(s[i++]->c_str()); hasExplicitLimit = true; } - if (s[i]->isStr() && String::isNumber(s[i]->c_str())) { table->max = atoi(s[i++]->c_str()); } - if (!s[i]->isStr() || s[i]->str() != FUNCREF) { - throw ParseException("Expected funcref"); - } else { - i += 1; + table->type = elementToType(*s[i++]); + if (!table->type.isRef()) { + throw ParseException("Only reference types are valid for tables"); } if (i < s.size() && s[i]->isList()) { @@ -3242,14 +3240,16 @@ void SExpressionWasmBuilder::parseTable(Element& s, bool preParseImport) { } // parses an elem segment -// elem ::= (elem (expr) vec(funcidx)) -// | (elem (offset (expr)) func vec(funcidx)) -// | (elem (table tableidx) (offset (expr)) func vec(funcidx)) -// | (elem func vec(funcidx)) -// | (elem declare func vec(funcidx)) +// elem ::= (elem (table tableidx)? (offset (expr)) reftype vec(item (expr))) +// | (elem reftype vec(item (expr))) +// | (elem declare reftype vec(item (expr))) // // abbreviation: // (offset (expr)) ≡ (expr) +// (item (expr)) ≡ (expr) +// ϵ ≡ (table 0) +// +// funcref vec(ref.func) ≡ func vec(funcidx) // (elem (expr) vec(funcidx)) ≡ (elem (table 0) (offset (expr)) func // vec(funcidx)) // @@ -3280,7 +3280,7 @@ void SExpressionWasmBuilder::parseElem(Element& s, Table* table) { auto segment = std::make_unique<ElementSegment>(); segment->setName(name, hasExplicitName); - if (s[i]->isList()) { + if (s[i]->isList() && !elementStartsWith(s[i], REF)) { // Optional (table <tableidx>) if (elementStartsWith(s[i], TABLE)) { auto& inner = *s[i++]; @@ -3303,11 +3303,15 @@ void SExpressionWasmBuilder::parseElem(Element& s, Table* table) { } else if (s[i]->isStr() && s[i]->str() == FUNC) { usesExpressions = false; i += 1; - } else if (s[i]->isStr() && s[i]->str() == FUNCREF) { + } else { + segment->type = elementToType(*s[i]); usesExpressions = true; i += 1; - } else { - throw ParseException("expected func or funcref."); + + if (!segment->type.isFunction()) { + throw ParseException( + "Invalid type for an element segment.", s.line, s.col); + } } } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index a8399bfdf..3c11f1ee3 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -797,6 +797,9 @@ void FunctionValidator::visitCallIndirect(CallIndirect* curr) { if (curr->target->type != Type::unreachable) { auto* table = getModule()->getTableOrNull(curr->table); shouldBeTrue(!!table, curr, "call-indirect table must exist"); + shouldBeTrue(table->type.isFunction(), + curr, + "call-indirect table must be of function type."); } validateCallParamsAndResult(curr, curr->sig); @@ -2829,6 +2832,10 @@ static void validateTables(Module& module, ValidationInfo& info) { "--enable-reference-types)"); if (!module.tables.empty()) { auto& table = module.tables.front(); + info.shouldBeTrue(table->type == Type::funcref, + "table", + "Only funcref is valid for table type (when reference " + "types are disabled)"); for (auto& segment : module.elementSegments) { info.shouldBeTrue(segment->table == table->name, "elem", @@ -2845,23 +2852,73 @@ static void validateTables(Module& module, ValidationInfo& info) { } } + for (auto& table : module.tables) { + info.shouldBeTrue(table->initial <= table->max, + "table", + "size minimum must not be greater than maximum"); + info.shouldBeTrue( + table->type.isNullable(), + "table", + "Non-nullable reference types are not yet supported for tables"); + if (!module.features.hasGC()) { + info.shouldBeTrue(table->type.isFunction() || + table->type == Type::externref, + "table", + "Only function reference types or externref are valid " + "for table type (when GC is disabled)"); + } + if (!module.features.hasTypedFunctionReferences()) { + info.shouldBeTrue(table->type == Type::funcref || + table->type == Type::externref, + "table", + "Only funcref and externref are valid for table type " + "(when typed-function references are disabled)"); + } + } + for (auto& segment : module.elementSegments) { + // Since element segment items need to be constant expressions, that leaves + // us with ref.null, ref.func and global.get. The GC proposal adds rtt.canon + // and rtt.sub to the list, but Binaryen doesn't consider RTTs as reference- + // types yet. As a result, the only possible type for element segments will + // be function references. + info.shouldBeTrue(segment->type.isFunction(), + "elem", + "element segment type must be of function type."); + info.shouldBeTrue( + segment->type.isNullable(), + "elem", + "Non-nullable reference types are not yet supported for tables"); + if (segment->table.is()) { auto table = module.getTableOrNull(segment->table); - info.shouldBeTrue( - table != nullptr, "elem", "elem segment must have a valid table name"); + info.shouldBeTrue(table != nullptr, + "elem", + "element segment must have a valid table name"); info.shouldBeTrue(!!segment->offset, "elem", "table segment offset should have an offset"); info.shouldBeEqual(segment->offset->type, Type(Type::i32), segment->offset, - "elem segment offset should be i32"); + "element segment offset should be i32"); info.shouldBeTrue(checkSegmentOffset(segment->offset, segment->data.size(), table->initial * Table::kPageSize), segment->offset, "table segment offset should be reasonable"); + if (module.features.hasTypedFunctionReferences()) { + info.shouldBeTrue( + Type::isSubType(segment->type, table->type), + "elem", + "element segment type must be a subtype of the table type"); + } else { + info.shouldBeEqual( + segment->type, + table->type, + "elem", + "element segment type must be the same as the table type"); + } validator.validate(segment->offset); } else { info.shouldBeTrue(!segment->offset, @@ -2871,10 +2928,22 @@ static void validateTables(Module& module, ValidationInfo& info) { // Avoid double checking items if (module.features.hasReferenceTypes()) { for (auto* expr : segment->data) { - info.shouldBeTrue( - expr->is<RefFunc>() || expr->is<RefNull>(), - expr, - "element segment items must be either ref.func or ref.null func."); + if (auto* globalExpr = expr->dynCast<GlobalGet>()) { + auto* global = module.getGlobal(globalExpr->name); + info.shouldBeFalse( + global->mutable_, expr, "expected a constant expression"); + } else { + info.shouldBeTrue(expr->is<RefFunc>() || expr->is<RefNull>() || + expr->is<GlobalGet>(), + expr, + "element segment items must be one of global.get, " + "ref.func, ref.null func"); + } + info.shouldBeSubType(expr->type, + segment->type, + expr, + "element segment item expressions must return a " + "subtype of the segment type"); validator.validate(expr); } } diff --git a/src/wasm2js.h b/src/wasm2js.h index 9a1a93b2b..add543b47 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -630,6 +630,10 @@ void Wasm2JSBuilder::addTable(Ref ast, Module* wasm) { // emit assignments separately for each index. Ref theArray = ValueBuilder::makeArray(); for (auto& table : wasm->tables) { + if (!table->type.isFunction()) { + Fatal() << "wasm2js doesn't support non-function tables\n"; + } + if (!table->imported()) { TableUtils::FlatTable flat(*wasm, *table); if (flat.valid) { |