summaryrefslogtreecommitdiff
path: root/src/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm')
-rw-r--r--src/wasm/wasm-binary.cpp57
-rw-r--r--src/wasm/wasm-s-parser.cpp34
-rw-r--r--src/wasm/wasm-validator.cpp83
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);
}
}