summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/wasm/wat-parser.cpp227
1 files changed, 204 insertions, 23 deletions
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp
index e05475517..9a9acd7a2 100644
--- a/src/wasm/wat-parser.cpp
+++ b/src/wasm/wat-parser.cpp
@@ -324,6 +324,16 @@ struct ImportNames {
Name nm;
};
+struct Limits {
+ uint64_t initial;
+ uint64_t max;
+};
+
+struct MemType {
+ Type type;
+ Limits limits;
+};
+
// RAII utility for temporarily changing the parsing position of a parsing
// context.
template<typename Ctx> struct WithPosition {
@@ -342,8 +352,7 @@ template<typename Ctx> WithPosition(Ctx& ctx, Index) -> WithPosition<Ctx>;
using IndexMap = std::unordered_map<Name, Index>;
-void applyImportNames(Importable& item,
- const std::optional<ImportNames>& names) {
+void applyImportNames(Importable& item, ImportNames* names) {
if (names) {
item.module = names->mod;
item.base = names->nm;
@@ -417,9 +426,12 @@ struct NullTypeParserCtx {
using FieldsT = Ok;
using StructT = Ok;
using ArrayT = Ok;
+ using LimitsT = Ok;
+ using MemTypeT = Ok;
using GlobalTypeT = Ok;
using TypeUseT = Ok;
using LocalsT = Ok;
+ using DataStringT = Ok;
HeapTypeT makeFunc() { return Ok{}; }
HeapTypeT makeAny() { return Ok{}; }
@@ -464,6 +476,15 @@ struct NullTypeParserCtx {
Result<Index> getTypeIndex(Name) { return 1; }
Result<HeapTypeT> getHeapTypeFromIdx(Index) { return Ok{}; }
+
+ DataStringT makeDataString() { return Ok{}; }
+ void appendDataString(DataStringT&, std::string_view) {}
+
+ LimitsT makeLimits(uint64_t, std::optional<uint64_t>) { return Ok{}; }
+ LimitsT getLimitsFromData(DataStringT) { return Ok{}; }
+
+ MemTypeT makeMemType32(LimitsT) { return Ok{}; }
+ MemTypeT makeMemType64(LimitsT) { return Ok{}; }
};
template<typename Ctx> struct TypeParserCtx {
@@ -478,7 +499,10 @@ template<typename Ctx> struct TypeParserCtx {
using FieldsT = std::pair<std::vector<Name>, std::vector<Field>>;
using StructT = std::pair<std::vector<Name>, Struct>;
using ArrayT = Array;
+ using LimitsT = Limits;
+ using MemTypeT = MemType;
using LocalsT = std::vector<NameType>;
+ using DataStringT = std::vector<char>;
// Map heap type names to their indices.
const IndexMap& typeIndices;
@@ -562,6 +586,22 @@ template<typename Ctx> struct TypeParserCtx {
}
return it->second;
}
+
+ 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};
+ }
+
+ MemType makeMemType32(Limits limits) { return {Type::i32, limits}; }
+ MemType makeMemType64(Limits limits) { return {Type::i64, limits}; }
};
struct NullInstrParserCtx {
@@ -820,14 +860,16 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
std::vector<DefPos> typeDefs;
std::vector<DefPos> subtypeDefs;
std::vector<DefPos> funcDefs;
+ std::vector<DefPos> memoryDefs;
std::vector<DefPos> globalDefs;
// Positions of typeuses that might implicitly define new types.
std::vector<Index> implicitTypeDefs;
// Counters used for generating names for module elements.
- int globalCounter = 0;
int funcCounter = 0;
+ int memoryCounter = 0;
+ int globalCounter = 0;
// Used to verify that all imports come before all non-imports.
bool hasNonImport = false;
@@ -853,16 +895,14 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
return Ok{};
}
- Result<Function*> addFuncDecl(ParseInput& in,
- Name name,
- std::optional<ImportNames> importNames) {
+ Result<Function*>
+ addFuncDecl(Index pos, Name name, ImportNames* importNames) {
auto f = std::make_unique<Function>();
if (name.is()) {
if (wasm.getFunctionOrNull(name)) {
// TDOO: if the existing function is not explicitly named, fix its name
// and continue.
- // TODO: Fix error location to point to name.
- return in.err("repeated function name");
+ return in.err(pos, "repeated function name");
}
f->setExplicitName(name);
} else {
@@ -884,24 +924,55 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
if (import && hasNonImport) {
return in.err("import after non-import");
}
- auto imp = import ? std::make_optional(*import) : std::nullopt;
- auto f = addFuncDecl(in, name, imp);
+ auto f = addFuncDecl(pos, name, import);
CHECK_ERR(f);
CHECK_ERR(addExports(in, wasm, *f, exports, ExternalKind::Function));
funcDefs.push_back({name, pos});
return Ok{};
}
- Result<Global*> addGlobalDecl(ParseInput& in,
- Name name,
- std::optional<ImportNames> importNames) {
+ Result<Memory*>
+ addMemoryDecl(Index pos, Name name, ImportNames* importNames) {
+ auto m = std::make_unique<Memory>();
+ if (name) {
+ // TODO: if the existing memory is not explicitly named, fix its name
+ // and continue.
+ if (wasm.getMemoryOrNull(name)) {
+ return in.err(pos, "repeated memory name");
+ }
+ m->setExplicitName(name);
+ } else {
+ name = (importNames ? "mimport$" : "") + std::to_string(memoryCounter++);
+ name = Names::getValidMemoryName(wasm, name);
+ m->name = name;
+ }
+ applyImportNames(*m, importNames);
+ return wasm.addMemory(std::move(m));
+ }
+
+ Result<> addMemory(Name name,
+ const std::vector<Name>& exports,
+ ImportNames* import,
+ MemTypeT,
+ Index pos) {
+ if (import && hasNonImport) {
+ return in.err(pos, "import after non-import");
+ }
+ auto m = addMemoryDecl(pos, name, import);
+ CHECK_ERR(m);
+ CHECK_ERR(addExports(in, wasm, *m, exports, ExternalKind::Memory));
+ memoryDefs.push_back({name, pos});
+ return Ok{};
+ }
+
+ Result<Global*>
+ addGlobalDecl(Index pos, Name name, ImportNames* importNames) {
auto g = std::make_unique<Global>();
- if (name.is()) {
+ if (name) {
if (wasm.getGlobalOrNull(name)) {
// TODO: if the existing global is not explicitly named, fix its name
// and continue.
- // TODO: Fix error location to point to name.
- return in.err("repeated global name");
+ return in.err(pos, "repeated global name");
}
g->setExplicitName(name);
} else {
@@ -922,8 +993,7 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
if (import && hasNonImport) {
return in.err(pos, "import after non-import");
}
- auto imp = import ? std::make_optional(*import) : std::nullopt;
- auto g = addGlobalDecl(in, name, imp);
+ auto g = addGlobalDecl(pos, name, import);
CHECK_ERR(g);
CHECK_ERR(addExports(in, wasm, *g, exports, ExternalKind::Global));
globalDefs.push_back({name, pos});
@@ -1123,7 +1193,7 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
Result<> addFunc(Name name,
const std::vector<Name>&,
ImportNames*,
- TypeUseT type,
+ TypeUse type,
std::optional<LocalsT> locals,
std::optional<InstrsT>,
Index) {
@@ -1142,10 +1212,21 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
return Ok{};
}
+ Result<> addMemory(
+ Name, const std::vector<Name>&, ImportNames*, MemType type, Index pos) {
+ auto& m = wasm.memories[index];
+ m->indexType = type.type;
+ m->initial = type.limits.initial;
+ m->max = type.limits.max;
+ // TODO: shared memories.
+ m->shared = false;
+ return Ok{};
+ }
+
Result<> addGlobal(Name,
const std::vector<Name>&,
ImportNames*,
- GlobalTypeT type,
+ GlobalType type,
std::optional<ExprT>,
Index) {
auto& g = wasm.globals[index];
@@ -1403,6 +1484,9 @@ template<typename Ctx> Result<typename Ctx::FieldT> fieldtype(Ctx&);
template<typename Ctx> Result<typename Ctx::FieldsT> fields(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::StructT> structtype(Ctx&);
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::GlobalTypeT> globaltype(Ctx&);
// Instructions
@@ -1572,7 +1656,9 @@ 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<> memory(Ctx&);
template<typename Ctx> MaybeResult<> global(Ctx&);
+template<typename Ctx> Result<typename Ctx::DataStringT> datastring(Ctx&);
MaybeResult<> modulefield(ParseDeclsCtx&);
Result<> module(ParseDeclsCtx&);
@@ -1851,6 +1937,39 @@ template<typename Ctx> MaybeResult<typename Ctx::ArrayT> arraytype(Ctx& ctx) {
return ctx.in.err("expected exactly one field in array definition");
}
+// limits32 ::= n:u32 m:u32?
+template<typename Ctx> Result<typename Ctx::LimitsT> limits32(Ctx& ctx) {
+ auto n = ctx.in.takeU32();
+ if (!n) {
+ return ctx.in.err("expected initial size");
+ }
+ std::optional<uint64_t> m = ctx.in.takeU32();
+ return ctx.makeLimits(uint64_t(*n), m);
+}
+
+// limits64 ::= n:u64 m:u64?
+template<typename Ctx> Result<typename Ctx::LimitsT> limits64(Ctx& ctx) {
+ auto n = ctx.in.takeU64();
+ if (!n) {
+ return ctx.in.err("expected initial size");
+ }
+ std::optional<uint64_t> m = ctx.in.takeU64();
+ return ctx.makeLimits(uint64_t(*n), m);
+}
+
+// memtype ::= limits32 | 'i32' limits32 | 'i64' limit64
+template<typename Ctx> Result<typename Ctx::MemTypeT> memtype(Ctx& ctx) {
+ if (ctx.in.takeKeyword("i64"sv)) {
+ auto limits = limits64(ctx);
+ CHECK_ERR(limits);
+ return ctx.makeMemType64(*limits);
+ }
+ ctx.in.takeKeyword("i32"sv);
+ auto limits = limits32(ctx);
+ CHECK_ERR(limits);
+ return ctx.makeMemType32(*limits);
+}
+
// globaltype ::= t:valtype => const t
// | '(' 'mut' t:valtype ')' => var t
template<typename Ctx> Result<typename Ctx::GlobalTypeT> globaltype(Ctx& ctx) {
@@ -2725,7 +2844,7 @@ template<typename Ctx> MaybeResult<typename Ctx::LocalsT> locals(Ctx& ctx) {
// func ::= '(' 'func' id? ('(' 'export' name ')')*
// x,I:typeuse t*:vec(local) (in:instr)* ')'
// | '(' 'func' id? ('(' 'export' name ')')*
-// '(' 'import' mode:name nm:name ')' typeuse ')'
+// '(' 'import' mod:name nm:name ')' typeuse ')'
template<typename Ctx> MaybeResult<> func(Ctx& ctx) {
auto pos = ctx.in.getPos();
if (!ctx.in.takeSExprStart("func"sv)) {
@@ -2771,6 +2890,54 @@ template<typename Ctx> MaybeResult<> func(Ctx& ctx) {
return Ok{};
}
+// mem ::= '(' 'memory' id? ('(' 'export' name ')')*
+// ('(' 'data' b:datastring ')' | memtype) ')'
+// | '(' 'memory' id? ('(' 'export' name ')')*
+// '(' 'import' mod:name nm:name ')' memtype ')'
+template<typename Ctx> MaybeResult<> memory(Ctx& ctx) {
+ auto pos = ctx.in.getPos();
+ if (!ctx.in.takeSExprStart("memory"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);
+
+ std::optional<typename Ctx::MemTypeT> mtype;
+
+ if (ctx.in.takeSExprStart("data"sv)) {
+ if (import) {
+ return ctx.in.err("imported memories cannot have inline data");
+ }
+ auto data = datastring(ctx);
+ CHECK_ERR(data);
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of inline data");
+ }
+ mtype = ctx.makeMemType32(ctx.getLimitsFromData(*data));
+ // TODO: addDataSegment as well.
+ } else {
+ auto type = memtype(ctx);
+ CHECK_ERR(type);
+ mtype = *type;
+ }
+
+ if (!ctx.in.takeRParen()) {
+ return ctx.in.err("expected end of memory declaration");
+ }
+
+ CHECK_ERR(ctx.addMemory(name, *exports, import.getPtr(), *mtype, pos));
+ return Ok{};
+}
+
// global ::= '(' 'global' id? ('(' 'export' name ')')* gt:globaltype e:expr ')'
// | '(' 'global' id? ('(' 'export' name ')')*
// '(' 'import' mod:name nm:name ')' gt:globaltype ')'
@@ -2809,11 +2976,20 @@ template<typename Ctx> MaybeResult<> global(Ctx& ctx) {
return Ok{};
}
+// datastring ::= (b:string)* => concat(b*)
+template<typename Ctx> Result<typename Ctx::DataStringT> datastring(Ctx& ctx) {
+ auto data = ctx.makeDataString();
+ while (auto str = ctx.in.takeString()) {
+ ctx.appendDataString(data, *str);
+ }
+ return data;
+}
+
// modulefield ::= deftype
// | import
// | func
// | table
-// | mem
+// | memory
// | global
// | export
// | start
@@ -2831,6 +3007,10 @@ MaybeResult<> modulefield(ParseDeclsCtx& ctx) {
CHECK_ERR(res);
return Ok{};
}
+ if (auto res = memory(ctx)) {
+ CHECK_ERR(res);
+ return Ok{};
+ }
if (auto res = global(ctx)) {
CHECK_ERR(res);
return Ok{};
@@ -2912,8 +3092,9 @@ Result<> parseModule(Module& wasm, std::string_view input) {
{
// Parse module-level types.
ParseModuleTypesCtx ctx(input, wasm, types, implicitTypes, *typeIndices);
- CHECK_ERR(parseDefs(ctx, decls.globalDefs, global));
CHECK_ERR(parseDefs(ctx, decls.funcDefs, func));
+ CHECK_ERR(parseDefs(ctx, decls.memoryDefs, memory));
+ CHECK_ERR(parseDefs(ctx, decls.globalDefs, global));
// TODO: Parse types of other module elements.
}
{