diff options
Diffstat (limited to 'src/wasm')
-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 |
3 files changed, 121 insertions, 53 deletions
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); } } |