summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/binaryen-c.cpp2
-rw-r--r--src/ir/element-utils.h5
-rw-r--r--src/ir/module-splitting.cpp19
-rw-r--r--src/ir/module-utils.h11
-rw-r--r--src/ir/table-utils.h2
-rw-r--r--src/passes/Print.cpp8
-rw-r--r--src/shell-interface.h16
-rw-r--r--src/tools/fuzzing.h45
-rw-r--r--src/tools/wasm-ctor-eval.cpp3
-rw-r--r--src/wasm-builder.h7
-rw-r--r--src/wasm-interpreter.h3
-rw-r--r--src/wasm.h13
-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
-rw-r--r--src/wasm2js.h4
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) {