summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/parser/context-decls.cpp75
-rw-r--r--src/parser/context-defs.cpp32
-rw-r--r--src/parser/contexts.h163
-rw-r--r--src/parser/parsers.h267
-rw-r--r--src/parser/wat-parser.cpp20
-rw-r--r--src/support/result.h3
6 files changed, 540 insertions, 20 deletions
diff --git a/src/parser/context-decls.cpp b/src/parser/context-decls.cpp
index 721127614..7d2d8a0a9 100644
--- a/src/parser/context-decls.cpp
+++ b/src/parser/context-decls.cpp
@@ -77,6 +77,59 @@ Result<> ParseDeclsCtx::addFunc(Name name,
return Ok{};
}
+Result<Table*> ParseDeclsCtx::addTableDecl(Index pos,
+ Name name,
+ ImportNames* importNames,
+ Limits limits) {
+ auto t = std::make_unique<Table>();
+ t->initial = limits.initial;
+ t->max = limits.max ? *limits.max : Table::kUnlimitedSize;
+ if (name.is()) {
+ if (wasm.getTableOrNull(name)) {
+ // TODO: if the existing table is not explicitly named, fix its name and
+ // continue.
+ return in.err(pos, "repeated table name");
+ }
+ t->setExplicitName(name);
+ } else {
+ name = (importNames ? "timport$" : "") + std::to_string(tableCounter++);
+ name = Names::getValidTableName(wasm, name);
+ t->name = name;
+ }
+ applyImportNames(*t, importNames);
+ return wasm.addTable(std::move(t));
+}
+
+Result<> ParseDeclsCtx::addTable(Name name,
+ const std::vector<Name>& exports,
+ ImportNames* import,
+ Limits limits,
+ Index pos) {
+ CHECK_ERR(checkImport(pos, import));
+ auto t = addTableDecl(pos, name, import, limits);
+ CHECK_ERR(t);
+ CHECK_ERR(addExports(in, wasm, *t, exports, ExternalKind::Table));
+ tableDefs.push_back({name, pos, Index(tableDefs.size())});
+ return Ok{};
+}
+
+Result<> ParseDeclsCtx::addImplicitElems(TypeT, ElemListT&& elems) {
+ auto& table = *wasm.tables.back();
+ auto e = std::make_unique<ElementSegment>();
+ e->table = table.name;
+ e->offset = Builder(wasm).makeConstPtr(0, Type::i32);
+ e->name = Names::getValidElementSegmentName(wasm, "implicit-elem");
+ wasm.addElementSegment(std::move(e));
+
+ // Record the index mapping so we can find this segment again to set its type
+ // and elements in later phases.
+ Index tableIndex = wasm.tables.size() - 1;
+ Index elemIndex = wasm.elementSegments.size() - 1;
+ implicitElemIndices[tableIndex] = elemIndex;
+
+ return Ok{};
+}
+
Result<Memory*> ParseDeclsCtx::addMemoryDecl(Index pos,
Name name,
ImportNames* importNames,
@@ -84,7 +137,7 @@ Result<Memory*> ParseDeclsCtx::addMemoryDecl(Index pos,
auto m = std::make_unique<Memory>();
m->indexType = type.type;
m->initial = type.limits.initial;
- m->max = type.limits.max;
+ m->max = type.limits.max ? *type.limits.max : Memory::kUnlimitedSize;
m->shared = type.shared;
if (name) {
// TODO: if the existing memory is not explicitly named, fix its name
@@ -160,6 +213,26 @@ Result<> ParseDeclsCtx::addGlobal(Name name,
return Ok{};
}
+Result<> ParseDeclsCtx::addElem(
+ Name name, TableIdxT*, std::optional<ExprT>, ElemListT&&, Index pos) {
+ auto e = std::make_unique<ElementSegment>();
+ if (name) {
+ if (wasm.getElementSegmentOrNull(name)) {
+ // TDOO: if the existing segment is not explicitly named, fix its name and
+ // continue.
+ return in.err(pos, "repeated element segment name");
+ }
+ e->setExplicitName(name);
+ } else {
+ name = std::to_string(elemCounter++);
+ name = Names::getValidElementSegmentName(wasm, name);
+ e->name = name;
+ }
+ elemDefs.push_back({name, pos, Index(wasm.elementSegments.size())});
+ wasm.addElementSegment(std::move(e));
+ return Ok{};
+}
+
Result<> ParseDeclsCtx::addData(Name name,
MemoryIdxT*,
std::optional<ExprT>,
diff --git a/src/parser/context-defs.cpp b/src/parser/context-defs.cpp
index 76e61d4fb..e5c598b6b 100644
--- a/src/parser/context-defs.cpp
+++ b/src/parser/context-defs.cpp
@@ -72,6 +72,36 @@ Result<> ParseDefsCtx::addGlobal(Name,
return Ok{};
}
+Result<> ParseDefsCtx::addImplicitElems(Type,
+ std::vector<Expression*>&& elems) {
+ auto& e = wasm.elementSegments[implicitElemIndices.at(index)];
+ e->data = std::move(elems);
+ return Ok{};
+}
+
+Result<> ParseDefsCtx::addElem(Name,
+ Name* table,
+ std::optional<Expression*> offset,
+ std::vector<Expression*>&& elems,
+ Index pos) {
+ auto& e = wasm.elementSegments[index];
+ if (offset) {
+ e->offset = *offset;
+ if (table) {
+ e->table = *table;
+ } else if (wasm.tables.size() > 0) {
+ e->table = wasm.tables[0]->name;
+ } else {
+ return in.err(pos, "active element segment with no table");
+ }
+ } else {
+ e->offset = nullptr;
+ e->table = Name();
+ }
+ e->data = std::move(elems);
+ return Ok{};
+}
+
Result<> ParseDefsCtx::addData(
Name, Name* mem, std::optional<ExprT> offset, DataStringT, Index pos) {
auto& d = wasm.dataSegments[index];
@@ -83,7 +113,7 @@ Result<> ParseDefsCtx::addData(
} else if (wasm.memories.size() > 0) {
d->memory = wasm.memories[0]->name;
} else {
- return in.err(pos, "active segment with no memory");
+ return in.err(pos, "active data segment with no memory");
}
} else {
d->isPassive = true;
diff --git a/src/parser/contexts.h b/src/parser/contexts.h
index adb7694f6..8395f4dc4 100644
--- a/src/parser/contexts.h
+++ b/src/parser/contexts.h
@@ -41,7 +41,7 @@ inline std::vector<Type> getUnnamedTypes(const std::vector<NameType>& named) {
struct Limits {
uint64_t initial;
- uint64_t max;
+ std::optional<uint64_t> max;
};
struct MemType {
@@ -94,6 +94,7 @@ struct NullTypeParserCtx {
using GlobalTypeT = Ok;
using TypeUseT = Ok;
using LocalsT = Ok;
+ using ElemListT = Ok;
using DataStringT = Ok;
HeapTypeT makeFunc() { return Ok{}; }
@@ -265,7 +266,7 @@ template<typename Ctx> struct TypeParserCtx {
DataStringT makeDataString() { return Ok{}; }
void appendDataString(DataStringT&, std::string_view) {}
- LimitsT makeLimits(uint64_t, std::optional<uint64_t>) { return Ok{}; }
+ Result<LimitsT> makeLimits(uint64_t, std::optional<uint64_t>) { return Ok{}; }
LimitsT getLimitsFromData(DataStringT) { return Ok{}; }
MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; }
@@ -282,6 +283,7 @@ struct NullInstrParserCtx {
using FieldIdxT = Ok;
using FuncIdxT = Ok;
using LocalIdxT = Ok;
+ using TableIdxT = Ok;
using GlobalIdxT = Ok;
using MemoryIdxT = Ok;
using DataIdxT = Ok;
@@ -304,6 +306,8 @@ struct NullInstrParserCtx {
LocalIdxT getLocalFromName(Name) { return Ok{}; }
GlobalIdxT getGlobalFromIdx(uint32_t) { return Ok{}; }
GlobalIdxT getGlobalFromName(Name) { return Ok{}; }
+ TableIdxT getTableFromIdx(uint32_t) { return Ok{}; }
+ TableIdxT getTableFromName(Name) { return Ok{}; }
MemoryIdxT getMemoryFromIdx(uint32_t) { return Ok{}; }
MemoryIdxT getMemoryFromName(Name) { return Ok{}; }
DataIdxT getDataFromIdx(uint32_t) { return Ok{}; }
@@ -470,8 +474,11 @@ struct NullInstrParserCtx {
// Phase 1: Parse definition spans for top-level module elements and determine
// their indices and names.
struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
- using DataStringT = std::vector<char>;
+ using ExprT = Ok;
using LimitsT = Limits;
+ using ElemListT = Index;
+ using DataStringT = std::vector<char>;
+ using TableTypeT = Limits;
using MemTypeT = MemType;
ParseInput in;
@@ -488,18 +495,27 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
std::vector<DefPos> typeDefs;
std::vector<DefPos> subtypeDefs;
std::vector<DefPos> funcDefs;
+ std::vector<DefPos> tableDefs;
std::vector<DefPos> memoryDefs;
std::vector<DefPos> globalDefs;
+ std::vector<DefPos> elemDefs;
std::vector<DefPos> dataDefs;
std::vector<DefPos> tagDefs;
// Positions of typeuses that might implicitly define new types.
std::vector<Index> implicitTypeDefs;
+ // Map table indices to the indices of their implicit, in-line element
+ // segments. We need these to find associated segments in later parsing phases
+ // where we can parse their types and instructions.
+ std::unordered_map<Index, Index> implicitElemIndices;
+
// Counters used for generating names for module elements.
int funcCounter = 0;
+ int tableCounter = 0;
int memoryCounter = 0;
int globalCounter = 0;
+ int elemCounter = 0;
int dataCounter = 0;
int tagCounter = 0;
@@ -534,14 +550,24 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
typeDefs.push_back({{}, pos, Index(typeDefs.size())});
}
+ Limits makeLimits(uint64_t n, std::optional<uint64_t> m) {
+ return Limits{n, m};
+ }
+
+ Index makeElemList(TypeT) { return 0; }
+ Index makeFuncElemList() { return 0; }
+ void appendElem(Index& elems, ExprT) { ++elems; }
+ void appendFuncElem(Index& elems, FuncIdxT) { ++elems; }
+
+ Limits getLimitsFromElems(Index elems) { return {elems, elems}; }
+
+ Limits makeTableType(Limits limits, TypeT) { return limits; }
+
std::vector<char> makeDataString() { return {}; }
void appendDataString(std::vector<char>& data, std::string_view str) {
data.insert(data.end(), str.begin(), str.end());
}
- Limits makeLimits(uint64_t n, std::optional<uint64_t> m) {
- return m ? Limits{n, *m} : Limits{n, Memory::kUnlimitedSize};
- }
Limits getLimitsFromData(const std::vector<char>& data) {
uint64_t size = (data.size() + Memory::kPageSize - 1) / Memory::kPageSize;
return {size, size};
@@ -567,6 +593,14 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
std::optional<LocalsT>,
Index pos);
+ Result<Table*>
+ addTableDecl(Index pos, Name name, ImportNames* importNames, Limits limits);
+ Result<>
+ addTable(Name, const std::vector<Name>&, ImportNames*, Limits, Index);
+
+ // TODO: Record index of implicit elem for use when parsing types and instrs.
+ Result<> addImplicitElems(TypeT, ElemListT&& elems);
+
Result<Memory*>
addMemoryDecl(Index pos, Name name, ImportNames* importNames, MemType type);
@@ -587,6 +621,10 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
std::optional<ExprT>,
Index pos);
+ Result<> addElem(Name, TableIdxT*, std::optional<ExprT>, ElemListT&&, Index);
+
+ Result<> addDeclareElem(Name, ElemListT&&, Index) { return Ok{}; }
+
Result<> addData(Name name,
MemoryIdxT*,
std::optional<ExprT>,
@@ -741,25 +779,32 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
// validate them when they are used.
using GlobalTypeT = GlobalType;
+ using TableTypeT = Type;
using TypeUseT = TypeUse;
+ using ElemListT = Type;
+
ParseInput in;
Module& wasm;
const std::vector<HeapType>& types;
const std::unordered_map<Index, HeapType>& implicitTypes;
+ const std::unordered_map<Index, Index>& implicitElemIndices;
// The index of the current type.
Index index = 0;
- ParseModuleTypesCtx(std::string_view in,
- Module& wasm,
- const std::vector<HeapType>& types,
- const std::unordered_map<Index, HeapType>& implicitTypes,
- const IndexMap& typeIndices)
+ ParseModuleTypesCtx(
+ std::string_view in,
+ Module& wasm,
+ const std::vector<HeapType>& types,
+ const std::unordered_map<Index, HeapType>& implicitTypes,
+ const std::unordered_map<Index, Index>& implicitElemIndices,
+ const IndexMap& typeIndices)
: TypeParserCtx<ParseModuleTypesCtx>(typeIndices), in(in), wasm(wasm),
- types(types), implicitTypes(implicitTypes) {}
+ types(types), implicitTypes(implicitTypes),
+ implicitElemIndices(implicitElemIndices) {}
Result<HeapTypeT> getHeapTypeFromIdx(Index idx) {
if (idx >= types.size()) {
@@ -804,6 +849,18 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
return {mutability, type};
}
+ Type makeElemList(Type type) { return type; }
+ Type makeFuncElemList() { return Type(HeapType::func, Nullable); }
+ void appendElem(ElemListT&, ExprT) {}
+ void appendFuncElem(ElemListT&, FuncIdxT) {}
+
+ LimitsT getLimitsFromElems(ElemListT) { return Ok{}; }
+
+ Type makeTableType(LimitsT, Type type) { return type; }
+
+ LimitsT getLimitsFromData(DataStringT) { return Ok{}; }
+ MemTypeT makeMemType(Type, LimitsT, bool) { return Ok{}; }
+
Result<> addFunc(Name name,
const std::vector<Name>&,
ImportNames*,
@@ -828,6 +885,23 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
return Ok{};
}
+ Result<> addTable(
+ Name, const std::vector<Name>&, ImportNames*, Type ttype, Index pos) {
+ auto& t = wasm.tables[index];
+ if (!ttype.isRef()) {
+ return in.err(pos, "expected reference type");
+ }
+ t->type = ttype;
+ return Ok{};
+ }
+
+ Result<> addImplicitElems(Type type, ElemListT&&) {
+ auto& t = wasm.tables[index];
+ auto& e = wasm.elementSegments[implicitElemIndices.at(index)];
+ e->type = t->type;
+ return Ok{};
+ }
+
Result<>
addMemory(Name, const std::vector<Name>&, ImportNames*, MemTypeT, Index) {
return Ok{};
@@ -848,6 +922,15 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
}
Result<>
+ addElem(Name, TableIdxT*, std::optional<ExprT>, ElemListT&& type, Index) {
+ auto& e = wasm.elementSegments[index];
+ e->type = type;
+ return Ok{};
+ }
+
+ Result<> addDeclareElem(Name, ElemListT&&, Index) { return Ok{}; }
+
+ Result<>
addTag(Name, const std::vector<Name>&, ImportNames*, TypeUse use, Index pos) {
auto& t = wasm.tags[index];
if (!use.type.isSignature()) {
@@ -861,15 +944,18 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
// Phase 5: Parse module element definitions, including instructions.
struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
using GlobalTypeT = Ok;
+ using TableTypeT = Ok;
using TypeUseT = HeapType;
using ExprT = Expression*;
+ using ElemListT = std::vector<Expression*>;
using FieldIdxT = Index;
using FuncIdxT = Name;
using LocalIdxT = Index;
using LabelIdxT = Index;
using GlobalIdxT = Name;
+ using TableIdxT = Name;
using MemoryIdxT = Name;
using DataIdxT = Name;
using TagIdxT = Name;
@@ -883,6 +969,7 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
const std::vector<HeapType>& types;
const std::unordered_map<Index, HeapType>& implicitTypes;
+ const std::unordered_map<Index, Index>& implicitElemIndices;
// The index of the current module element.
Index index = 0;
@@ -903,9 +990,11 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
Module& wasm,
const std::vector<HeapType>& types,
const std::unordered_map<Index, HeapType>& implicitTypes,
+ const std::unordered_map<Index, Index>& implicitElemIndices,
const IndexMap& typeIndices)
: TypeParserCtx(typeIndices), in(in), wasm(wasm), builder(wasm),
- types(types), implicitTypes(implicitTypes), irBuilder(wasm) {}
+ types(types), implicitTypes(implicitTypes),
+ implicitElemIndices(implicitElemIndices), irBuilder(wasm) {}
template<typename T> Result<T> withLoc(Index pos, Result<T> res) {
if (auto err = res.getErr()) {
@@ -929,6 +1018,20 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; }
+ std::vector<Expression*> makeElemList(TypeT) { return {}; }
+ std::vector<Expression*> makeFuncElemList() { return {}; }
+ void appendElem(std::vector<Expression*>& elems, Expression* expr) {
+ elems.push_back(expr);
+ }
+ void appendFuncElem(std::vector<Expression*>& elems, Name func) {
+ auto type = wasm.getFunction(func)->type;
+ elems.push_back(builder.makeRefFunc(func, type));
+ }
+
+ LimitsT getLimitsFromElems(std::vector<Expression*>& elems) { return Ok{}; }
+
+ TableTypeT makeTableType(LimitsT, Type) { return Ok{}; }
+
Result<HeapTypeT> getHeapTypeFromIdx(Index idx) {
if (idx >= types.size()) {
return in.err("type index out of bounds");
@@ -999,6 +1102,20 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
return name;
}
+ Result<Name> getTableFromIdx(uint32_t idx) {
+ if (idx >= wasm.tables.size()) {
+ return in.err("table index out of bounds");
+ }
+ return wasm.tables[idx]->name;
+ }
+
+ Result<Name> getTableFromName(Name name) {
+ if (!wasm.getTableOrNull(name)) {
+ return in.err("table $" + name.toString() + " does not exist");
+ }
+ return name;
+ }
+
Result<Name> getMemoryFromIdx(uint32_t idx) {
if (idx >= wasm.memories.size()) {
return in.err("memory index out of bounds");
@@ -1058,12 +1175,32 @@ struct ParseDefsCtx : TypeParserCtx<ParseDefsCtx> {
std::optional<LocalsT>,
Index pos);
+ Result<>
+ addTable(Name, const std::vector<Name>&, ImportNames*, TableTypeT, Index) {
+ return Ok{};
+ }
+
Result<> addGlobal(Name,
const std::vector<Name>&,
ImportNames*,
GlobalTypeT,
std::optional<ExprT> exp,
Index);
+
+ Result<> addImplicitElems(Type type, std::vector<Expression*>&& elems);
+
+ Result<> addDeclareElem(Name, std::vector<Expression*>&&, Index) {
+ // TODO: Validate that referenced functions appear in a declaratve element
+ // segment.
+ return Ok{};
+ }
+
+ Result<> addElem(Name,
+ Name* table,
+ std::optional<Expression*> offset,
+ std::vector<Expression*>&& elems,
+ Index pos);
+
Result<>
addData(Name, Name* mem, std::optional<ExprT> offset, DataStringT, Index pos);
diff --git a/src/parser/parsers.h b/src/parser/parsers.h
index f16cca2b5..2bf9915eb 100644
--- a/src/parser/parsers.h
+++ b/src/parser/parsers.h
@@ -39,6 +39,7 @@ template<typename Ctx> MaybeResult<typename Ctx::ArrayT> arraytype(Ctx&);
template<typename Ctx> Result<typename Ctx::LimitsT> limits32(Ctx&);
template<typename Ctx> Result<typename Ctx::LimitsT> limits64(Ctx&);
template<typename Ctx> Result<typename Ctx::MemTypeT> memtype(Ctx&);
+template<typename Ctx> Result<typename Ctx::TableTypeT> tabletype(Ctx&);
template<typename Ctx> Result<typename Ctx::GlobalTypeT> globaltype(Ctx&);
// Instructions
@@ -165,7 +166,11 @@ template<typename Ctx> MaybeResult<Index> maybeTypeidx(Ctx& ctx);
template<typename Ctx> Result<typename Ctx::HeapTypeT> typeidx(Ctx&);
template<typename Ctx>
Result<typename Ctx::FieldIdxT> fieldidx(Ctx&, typename Ctx::HeapTypeT);
+template<typename Ctx> MaybeResult<typename Ctx::FuncIdxT> maybeFuncidx(Ctx&);
template<typename Ctx> Result<typename Ctx::FuncIdxT> funcidx(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::TableIdxT> maybeTableidx(Ctx&);
+template<typename Ctx> Result<typename Ctx::TableIdxT> tableidx(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::TableIdxT> maybeTableuse(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::MemoryIdxT> maybeMemidx(Ctx&);
template<typename Ctx> Result<typename Ctx::MemoryIdxT> memidx(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::MemoryIdxT> maybeMemuse(Ctx&);
@@ -182,8 +187,12 @@ template<typename Ctx> MaybeResult<typename Ctx::ModuleNameT> subtype(Ctx&);
template<typename Ctx> MaybeResult<> deftype(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::LocalsT> locals(Ctx&);
template<typename Ctx> MaybeResult<> func(Ctx&);
+template<typename Ctx> MaybeResult<> table(Ctx&);
template<typename Ctx> MaybeResult<> memory(Ctx&);
template<typename Ctx> MaybeResult<> global(Ctx&);
+template<typename Ctx> MaybeResult<typename Ctx::ExprT> maybeElemexpr(Ctx&);
+template<typename Ctx> Result<typename Ctx::ElemListT> elemlist(Ctx&, bool);
+template<typename Ctx> MaybeResult<> elem(Ctx&);
template<typename Ctx> Result<typename Ctx::DataStringT> datastring(Ctx&);
template<typename Ctx> MaybeResult<> data(Ctx&);
template<typename Ctx> MaybeResult<> tag(Ctx&);
@@ -539,6 +548,18 @@ template<typename Ctx> Result<typename Ctx::MemTypeT> memtype(Ctx& ctx) {
return ctx.makeMemType(type, *limits, shared);
}
+// tabletype ::= limits32 reftype
+template<typename Ctx> Result<typename Ctx::TableTypeT> tabletype(Ctx& ctx) {
+ auto limits = limits32(ctx);
+ CHECK_ERR(limits);
+ auto type = reftype(ctx);
+ CHECK_ERR(type);
+ if (!type) {
+ return ctx.in.err("expected reftype");
+ }
+ return ctx.makeTableType(*limits, *type);
+}
+
// globaltype ::= t:valtype => const t
// | '(' 'mut' t:valtype ')' => var t
template<typename Ctx> Result<typename Ctx::GlobalTypeT> globaltype(Ctx& ctx) {
@@ -626,7 +647,7 @@ template<typename Ctx> MaybeResult<> instr(Ctx& ctx) {
if (auto keyword = tok->getKeyword()) {
if (keyword == "end"sv || keyword == "then"sv || keyword == "else"sv ||
keyword == "catch"sv || keyword == "catch_all"sv ||
- keyword == "delegate"sv) {
+ keyword == "delegate"sv || keyword == "ref"sv) {
return {};
}
}
@@ -1693,16 +1714,60 @@ Result<typename Ctx::FieldIdxT> fieldidx(Ctx& ctx,
// funcidx ::= x:u32 => x
// | v:id => x (if t.funcs[x] = v)
-template<typename Ctx> Result<typename Ctx::FuncIdxT> funcidx(Ctx& ctx) {
+template<typename Ctx>
+MaybeResult<typename Ctx::FuncIdxT> maybeFuncidx(Ctx& ctx) {
if (auto x = ctx.in.takeU32()) {
return ctx.getFuncFromIdx(*x);
}
if (auto id = ctx.in.takeID()) {
return ctx.getFuncFromName(*id);
}
+ return {};
+}
+
+template<typename Ctx> Result<typename Ctx::FuncIdxT> funcidx(Ctx& ctx) {
+ if (auto idx = maybeFuncidx(ctx)) {
+ CHECK_ERR(idx);
+ return *idx;
+ }
return ctx.in.err("expected function index or identifier");
}
+// tableidx ::= x:u23 => x
+// | v:id => x (if tables[x] = v)
+template<typename Ctx>
+MaybeResult<typename Ctx::TableIdxT> maybeTableidx(Ctx& ctx) {
+ if (auto x = ctx.in.takeU32()) {
+ return ctx.getTableFromIdx(*x);
+ }
+ if (auto id = ctx.in.takeID()) {
+ return ctx.getTableFromName(*id);
+ }
+ return {};
+}
+
+template<typename Ctx> Result<typename Ctx::TableIdxT> tableidx(Ctx& ctx) {
+ if (auto idx = maybeTableidx(ctx)) {
+ CHECK_ERR(idx);
+ return *idx;
+ }
+ return ctx.in.err("expected table index or identifier");
+}
+
+// tableuse ::= '(' 'table' x:tableidx ')'
+template<typename Ctx>
+MaybeResult<typename Ctx::TableIdxT> maybeTableuse(Ctx& ctx) {
+ if (!ctx.in.takeSExprStart("table"sv)) {
+ return {};
+ }
+ auto idx = tableidx(ctx);
+ CHECK_ERR(idx);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("Expected end of memory use");
+ }
+ return *idx;
+}
+
// memidx ::= x:u32 => x
// | v:id => x (if memories[x] = v)
template<typename Ctx>
@@ -2031,6 +2096,82 @@ template<typename Ctx> MaybeResult<> func(Ctx& ctx) {
return Ok{};
}
+// table ::= '(' 'table' id? ('(' 'export' name ')')*
+// '(' 'import' mod:name nm:name ')'? tabletype ')'
+// | '(' 'table' id? ('(' 'export' name ')')*
+// reftype '(' 'elem' (elemexpr* | funcidx*) ')' ')'
+template<typename Ctx> MaybeResult<> table(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+ if (!ctx.in.takeSExprStart("table"sv)) {
+ return {};
+ }
+
+ Name name;
+ if (auto id = ctx.in.takeID()) {
+ name = *id;
+ }
+
+ auto exports = inlineExports(ctx.in);
+ CHECK_ERR(exports);
+
+ auto import = inlineImport(ctx.in);
+ CHECK_ERR(import);
+
+ // Reftype if we have inline elements.
+ auto type = reftype(ctx);
+ CHECK_ERR(type);
+
+ std::optional<typename Ctx::TableTypeT> ttype;
+ std::optional<typename Ctx::ElemListT> elems;
+ if (type) {
+ // We should have inline elements.
+ if (!ctx.in.takeSExprStart("elem"sv)) {
+ return ctx.in.err("expected table limits or inline elements");
+ }
+ if (import) {
+ return ctx.in.err("imported tables cannot have inline elements");
+ }
+
+ auto list = ctx.makeElemList(*type);
+ bool foundElem = false;
+ while (auto elem = maybeElemexpr(ctx)) {
+ CHECK_ERR(elem);
+ ctx.appendElem(list, *elem);
+ foundElem = true;
+ }
+
+ // If there were no elemexprs, then maybe we have funcidxs instead.
+ if (!foundElem) {
+ while (auto func = maybeFuncidx(ctx)) {
+ CHECK_ERR(func);
+ ctx.appendFuncElem(list, *func);
+ }
+ }
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of inline elems");
+ }
+ ttype = ctx.makeTableType(ctx.getLimitsFromElems(list), *type);
+ elems = std::move(list);
+ } else {
+ auto tabtype = tabletype(ctx);
+ CHECK_ERR(tabtype);
+ ttype = *tabtype;
+ }
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of table declaration");
+ }
+
+ CHECK_ERR(ctx.addTable(name, *exports, import.getPtr(), *ttype, pos));
+
+ if (elems) {
+ CHECK_ERR(ctx.addImplicitElems(*type, std::move(*elems)));
+ }
+
+ return Ok{};
+}
+
// mem ::= '(' 'memory' id? ('(' 'export' name ')')*
// ('(' 'data' b:datastring ')' | memtype) ')'
// | '(' 'memory' id? ('(' 'export' name ')')*
@@ -2122,6 +2263,120 @@ template<typename Ctx> MaybeResult<> global(Ctx& ctx) {
return Ok{};
}
+// elemexpr ::= '(' 'item' expr ')' | '(' instr ')'
+template<typename Ctx>
+MaybeResult<typename Ctx::ExprT> maybeElemexpr(Ctx& ctx) {
+ MaybeResult<typename Ctx::ExprT> result;
+ if (ctx.in.takeSExprStart("item"sv)) {
+ result = expr(ctx);
+ } else if (ctx.in.takeLParen()) {
+ // TODO: `instr` should included both folded and unfolded instrs.
+ if (auto inst = instr(ctx)) {
+ CHECK_ERR(inst);
+ } else {
+ return ctx.in.err("expected instruction");
+ }
+ result = ctx.makeExpr();
+ } else {
+ return {};
+ }
+ CHECK_ERR(result);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of element expression");
+ }
+ return result;
+}
+
+// elemlist ::= reftype elemexpr* | 'func' funcidx*
+// | funcidx* (iff the tableuse is omitted)
+template<typename Ctx>
+Result<typename Ctx::ElemListT> elemlist(Ctx& ctx, bool legacy) {
+ if (auto type = reftype(ctx)) {
+ auto res = ctx.makeElemList(*type);
+ while (auto elem = maybeElemexpr(ctx)) {
+ CHECK_ERR(elem);
+ ctx.appendElem(res, *elem);
+ }
+ return res;
+ } else if (ctx.in.takeKeyword("func"sv) || legacy) {
+ auto res = ctx.makeFuncElemList();
+ while (auto func = maybeFuncidx(ctx)) {
+ CHECK_ERR(func);
+ ctx.appendFuncElem(res, *func);
+ }
+ return res;
+ }
+ return ctx.in.err("expected element list");
+}
+
+// elem ::= '(' 'elem' id? x:tableuse? ('(' ('offset' e:expr | e:instr) ')')?
+// elemlist ')'
+// | '(' 'elem' id? 'declare' elemlist ')'
+template<typename Ctx> MaybeResult<> elem(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+ if (!ctx.in.takeSExprStart("elem"sv)) {
+ return {};
+ }
+
+ Name name;
+ if (auto id = ctx.in.takeID()) {
+ name = *id;
+ }
+
+ bool isDeclare = false;
+ MaybeResult<typename Ctx::TableIdxT> table;
+ std::optional<typename Ctx::ExprT> offset;
+
+ if (ctx.in.takeKeyword("declare"sv)) {
+ isDeclare = true;
+ } else {
+ table = maybeTableuse(ctx);
+ CHECK_ERR(table);
+
+ if (ctx.in.takeSExprStart("offset")) {
+ auto off = expr(ctx);
+ CHECK_ERR(off);
+ offset = *off;
+ } else {
+ // This may be an abbreviated offset instruction or it may be the
+ // beginning of the elemlist.
+ auto beforeLParen = ctx.in.getPos();
+ if (ctx.in.takeLParen()) {
+ if (auto inst = instr(ctx)) {
+ CHECK_ERR(inst);
+ auto off = ctx.makeExpr();
+ CHECK_ERR(off);
+ offset = *off;
+ } else {
+ // This must be the beginning of the elemlist instead.
+ ctx.in.lexer.setIndex(beforeLParen);
+ }
+ }
+ }
+ if (offset && !ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of offset expression");
+ }
+ }
+
+ // If there is no explicit tableuse, we can use the legacy elemlist format.
+ bool legacy = !table;
+ auto elems = elemlist(ctx, legacy);
+ CHECK_ERR(elems);
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of element segment");
+ }
+
+ if (isDeclare) {
+ CHECK_ERR(ctx.addDeclareElem(name, std::move(*elems), pos));
+ } else {
+ CHECK_ERR(
+ ctx.addElem(name, table.getPtr(), offset, std::move(*elems), pos));
+ }
+
+ return Ok{};
+}
+
// datastring ::= (b:string)* => concat(b*)
template<typename Ctx> Result<typename Ctx::DataStringT> datastring(Ctx& ctx) {
auto data = ctx.makeDataString();
@@ -2236,6 +2491,10 @@ template<typename Ctx> MaybeResult<> modulefield(Ctx& ctx) {
CHECK_ERR(res);
return Ok{};
}
+ if (auto res = table(ctx)) {
+ CHECK_ERR(res);
+ return Ok{};
+ }
if (auto res = memory(ctx)) {
CHECK_ERR(res);
return Ok{};
@@ -2244,6 +2503,10 @@ template<typename Ctx> MaybeResult<> modulefield(Ctx& ctx) {
CHECK_ERR(res);
return Ok{};
}
+ if (auto res = elem(ctx)) {
+ CHECK_ERR(res);
+ return Ok{};
+ }
if (auto res = data(ctx)) {
CHECK_ERR(res);
return Ok{};
diff --git a/src/parser/wat-parser.cpp b/src/parser/wat-parser.cpp
index be3bca5c8..cc0f582fc 100644
--- a/src/parser/wat-parser.cpp
+++ b/src/parser/wat-parser.cpp
@@ -132,6 +132,7 @@ Result<> parseModule(Module& wasm, std::string_view input) {
// Parse implicit type definitions and map typeuses without explicit types to
// the correct types.
std::unordered_map<Index, HeapType> implicitTypes;
+
{
ParseImplicitTypeDefsCtx ctx(input, types, implicitTypes, *typeIndices);
for (Index pos : decls.implicitTypeDefs) {
@@ -142,18 +143,31 @@ Result<> parseModule(Module& wasm, std::string_view input) {
{
// Parse module-level types.
- ParseModuleTypesCtx ctx(input, wasm, types, implicitTypes, *typeIndices);
+ ParseModuleTypesCtx ctx(input,
+ wasm,
+ types,
+ implicitTypes,
+ decls.implicitElemIndices,
+ *typeIndices);
CHECK_ERR(parseDefs(ctx, decls.funcDefs, func));
+ CHECK_ERR(parseDefs(ctx, decls.tableDefs, table));
CHECK_ERR(parseDefs(ctx, decls.memoryDefs, memory));
CHECK_ERR(parseDefs(ctx, decls.globalDefs, global));
+ CHECK_ERR(parseDefs(ctx, decls.elemDefs, elem));
CHECK_ERR(parseDefs(ctx, decls.tagDefs, tag));
- // TODO: Parse types of other module elements.
}
{
// Parse definitions.
// TODO: Parallelize this.
- ParseDefsCtx ctx(input, wasm, types, implicitTypes, *typeIndices);
+ ParseDefsCtx ctx(input,
+ wasm,
+ types,
+ implicitTypes,
+ decls.implicitElemIndices,
+ *typeIndices);
+ CHECK_ERR(parseDefs(ctx, decls.tableDefs, table));
CHECK_ERR(parseDefs(ctx, decls.globalDefs, global));
+ CHECK_ERR(parseDefs(ctx, decls.elemDefs, elem));
CHECK_ERR(parseDefs(ctx, decls.dataDefs, data));
for (Index i = 0; i < decls.funcDefs.size(); ++i) {
diff --git a/src/support/result.h b/src/support/result.h
index acbf92966..ab71d3a53 100644
--- a/src/support/result.h
+++ b/src/support/result.h
@@ -75,6 +75,9 @@ template<typename T = Ok> struct MaybeResult {
// conditions where errors should not get lost.
operator bool() const { return !std::holds_alternative<None>(val); }
+ MaybeResult<T>& operator=(const MaybeResult<T>&) = default;
+ MaybeResult<T>& operator=(MaybeResult<T>&&) = default;
+
Err* getErr() { return std::get_if<Err>(&val); }
T& operator*() { return *std::get_if<T>(&val); }
T* operator->() { return std::get_if<T>(&val); }