template <typename OutputIter> void RemoveEscapes(std::string_view text, OutputIter dest) { // Remove surrounding quotes; if any. This may be empty if the string was // invalid (e.g. if it contained a bad escape sequence).
if (text.size() <= 2) { return;
}
*dest++ = static_cast<uint8_t>(0x80 | (scalar_value & 0x3f));
} break;
} default: { // The string should be validated already, so we know this is a hex // sequence.
uint32_t hi;
uint32_t lo;
if (Succeeded(ParseHexdigit(src[0], &hi)) &&
Succeeded(ParseHexdigit(src[1], &lo))) {
*dest++ = (hi << 4) | lo;
} else {
assert(0);
}
src++; break;
}
}
src++;
} else {
*dest++ = *src++;
}
}
}
using TextVector = std::vector<std::string_view>;
template <typename OutputIter> void RemoveEscapes(const TextVector& texts, OutputIter out) {
for (std::string_view text : texts)
RemoveEscapes(text, out);
}
bool IsPlainInstr(TokenType token_type) { switch (token_type) { case TokenType::Unreachable: case TokenType::Nop: case TokenType::Drop: case TokenType::Select: case TokenType::Br: case TokenType::BrIf: case TokenType::BrTable: case TokenType::Return: case TokenType::ReturnCall: case TokenType::ReturnCallIndirect: case TokenType::Call: case TokenType::CallIndirect: case TokenType::CallRef: case TokenType::LocalGet: case TokenType::LocalSet: case TokenType::LocalTee: case TokenType::GlobalGet: case TokenType::GlobalSet: case TokenType::Load: case TokenType::Store: case TokenType::Const: case TokenType::Unary: case TokenType::Binary: case TokenType::Compare: case TokenType::Convert: case TokenType::MemoryCopy: case TokenType::DataDrop: case TokenType::MemoryFill: case TokenType::MemoryGrow: case TokenType::MemoryInit: case TokenType::MemorySize: case TokenType::TableCopy: case TokenType::ElemDrop: case TokenType::TableInit: case TokenType::TableGet: case TokenType::TableSet: case TokenType::TableGrow: case TokenType::TableSize: case TokenType::TableFill: case TokenType::Throw: case TokenType::Rethrow: case TokenType::RefFunc: case TokenType::RefNull: case TokenType::RefIsNull: case TokenType::AtomicLoad: case TokenType::AtomicStore: case TokenType::AtomicRmw: case TokenType::AtomicRmwCmpxchg: case TokenType::AtomicNotify: case TokenType::AtomicFence: case TokenType::AtomicWait: case TokenType::Ternary: case TokenType::SimdLaneOp: case TokenType::SimdLoadLane: case TokenType::SimdStoreLane: case TokenType::SimdShuffleOp: returntrue; default: return false;
}
}
bool IsBlockInstr(TokenType token_type) { switch (token_type) { case TokenType::Block: case TokenType::Loop: case TokenType::If: case TokenType::Try: returntrue; default: return false;
}
}
switch (pair[1]) { case TokenType::Data: case TokenType::Elem: case TokenType::Tag: case TokenType::Export: case TokenType::Func: case TokenType::Type: case TokenType::Global: case TokenType::Import: case TokenType::Memory: case TokenType::Start: case TokenType::Table: returntrue; default: return false;
}
}
switch (pair[1]) { case TokenType::AssertException: case TokenType::AssertExhaustion: case TokenType::AssertInvalid: case TokenType::AssertMalformed: case TokenType::AssertReturn: case TokenType::AssertTrap: case TokenType::AssertUnlinkable: case TokenType::Get: case TokenType::Invoke: case TokenType::Input: case TokenType::Module: case TokenType::Output: case TokenType::Register: returntrue; default: return false;
}
}
void ResolveImplicitlyDefinedFunctionType(const Location& loc,
Module* module, const FuncDeclaration& decl) { // Resolve implicitly defined function types, e.g.: (func (param i32) ...)
if (!decl.has_func_type) {
Index func_type_index = module->GetFuncTypeIndex(decl.sig);
if (func_type_index == kInvalidIndex) { auto func_type_field = std::make_unique<TypeModuleField>(loc); auto func_type = std::make_unique<FuncType>();
func_type->sig = decl.sig;
func_type_field->type = std::move(func_type);
module->AppendField(std::move(func_type_field));
}
}
}
Result CheckTypeIndex(const Location& loc,
Type actual,
Type expected, const char* desc,
Index index, const char* index_kind,
Errors* errors) { // Types must match exactly; no subtyping should be allowed.
if (actual != expected) {
errors->emplace_back(
ErrorLevel::Error, loc,
StringPrintf("type mismatch for %s %" PRIindex " of %s. got %s, expected %s",
index_kind, index, desc, actual.GetName().c_str(),
expected.GetName().c_str())); return Result::Error;
} return Result::Ok;
}
Result CheckTypes(const Location& loc, const TypeVector& actual, const TypeVector& expected, const char* desc, const char* index_kind,
Errors* errors) {
Result result = Result::Ok;
if (actual.size() == expected.size()) {
for (size_t i = 0; i < actual.size(); ++i) {
result |= CheckTypeIndex(loc, actual[i], expected[i], desc, i, index_kind,
errors);
}
} else {
errors->emplace_back(
ErrorLevel::Error, loc,
StringPrintf("expected %" PRIzd " %ss, got %" PRIzd, expected.size(),
index_kind, actual.size()));
result = Result::Error;
} return result;
}
Result CheckFuncTypeVarMatchesExplicit(const Location& loc, const Module& module, const FuncDeclaration& decl,
Errors* errors) {
Result result = Result::Ok;
if (decl.has_func_type) { const FuncType* func_type = module.GetFuncType(decl.type_var);
if (func_type) {
result |=
CheckTypes(loc, decl.sig.result_types, func_type->sig.result_types, "function", "result", errors);
result |=
CheckTypes(loc, decl.sig.param_types, func_type->sig.param_types, "function", "argument", errors);
} else if (!(decl.sig.param_types.empty() &&
decl.sig.result_types.empty())) { // We want to check whether the function type at the explicit index // matches the given param and result types. If they were omitted then // they'll be resolved automatically (see // ResolveFuncTypeWithEmptySignature), but if they are provided then we // have to check. If we get here then the type var is invalid, so we // can't check whether they match.
if (decl.type_var.is_index()) {
errors->emplace_back(ErrorLevel::Error, loc,
StringPrintf("invalid func type index %" PRIindex,
decl.type_var.index()));
} else {
errors->emplace_back(ErrorLevel::Error, loc,
StringPrintf("expected func type identifier %s",
decl.type_var.name().c_str()));
}
result = Result::Error;
}
} return result;
}
Result ResolveFuncTypes(Module* module, Errors* errors) {
Result result = Result::Ok;
for (ModuleField& field : module->fields) {
Func* func = nullptr;
FuncDeclaration* decl = nullptr;
if (auto* func_field = dyn_cast<FuncModuleField>(&field)) {
func = &func_field->func;
decl = &func->decl;
} else if (auto* tag_field = dyn_cast<TagModuleField>(&field)) {
decl = &tag_field->tag.decl;
} else if (auto* import_field = dyn_cast<ImportModuleField>(&field)) {
if (auto* func_import =
dyn_cast<FuncImport>(import_field->import.get())) { // Only check the declaration, not the function itself, since it is an // import.
decl = &func_import->func.decl;
} else if (auto* tag_import =
dyn_cast<TagImport>(import_field->import.get())) {
decl = &tag_import->tag.decl;
} else { continue;
}
} else { continue;
}
bool has_func_type_and_empty_signature = false;
if (decl) {
ResolveTypeNames(*module, decl);
has_func_type_and_empty_signature =
ResolveFuncTypeWithEmptySignature(*module, decl);
ResolveImplicitlyDefinedFunctionType(field.loc, module, *decl);
result |=
CheckFuncTypeVarMatchesExplicit(field.loc, *module, *decl, errors);
}
if (func) {
if (has_func_type_and_empty_signature) { // The call to ResolveFuncTypeWithEmptySignature may have updated the // function signature so there are parameters. Since parameters and // local variables share the same index space, we need to increment the // local indexes bound to a given name by the number of parameters in // the function.
for (auto& [name, binding] : func->bindings) {
binding.index += func->GetNumParams();
}
}
TokenType WastParser::Peek(size_t n) {
assert(n <= 1); while (tokens_.size() <= n) {
Token cur = lexer_->GetToken();
if (cur.token_type() != TokenType::LparAnn) {
tokens_.push_back(cur);
} else { // Custom annotation. For now, discard until matching Rpar, unless it is // a code metadata annotation or custom section. In those cases, we know // how to parse it.
if (!options_->features.annotations_enabled()) {
Error(cur.loc, "annotations not enabled: %s", cur.to_string().c_str());
tokens_.push_back(Token(cur.loc, TokenType::Invalid)); continue;
}
if ((options_->features.code_metadata_enabled() &&
cur.text().find("metadata.code.") == 0) ||
cur.text() == "custom") {
tokens_.push_back(cur); continue;
}
int indent = 1; while (indent > 0) {
cur = lexer_->GetToken(); switch (cur.token_type()) { case TokenType::Lpar: case TokenType::LparAnn:
indent++; break;
case TokenType::Rpar:
indent--; break;
case TokenType::Eof:
indent = 0;
Error(cur.loc, "unterminated annotation"); break;
Result WastParser::Synchronize(SynchronizeFunc func) { staticconst int kMaxConsumed = 10;
for (int i = 0; i < kMaxConsumed; ++i) {
if (func(PeekPair())) { return Result::Ok;
}
bool WastParser::ParseVarOpt(Var* out_var, Var default_var) {
WABT_TRACE(ParseVarOpt);
if (PeekMatch(TokenType::Nat) || PeekMatch(TokenType::Var)) {
Result result = ParseVar(out_var); // Should always succeed, the only way it could fail is if the token // doesn't match.
assert(Succeeded(result));
WABT_USE(result); returntrue;
} else {
*out_var = default_var; return false;
}
}
Result WastParser::ParseOffsetExpr(ExprList* out_expr_list) {
WABT_TRACE(ParseOffsetExpr);
if (!ParseOffsetExprOpt(out_expr_list)) { return ErrorExpected({"an offset expr"}, "(i32.const 123)");
} return Result::Ok;
}
Result WastParser::ParseRefKind(Type* out_type) {
WABT_TRACE(ParseRefKind);
if (!IsTokenTypeRefKind(Peek())) { return ErrorExpected({"func", "extern", "exn"});
}
Token token = Consume();
Type type = token.type();
if ((type == Type::ExternRef &&
!options_->features.reference_types_enabled()) ||
((type == Type::Struct || type == Type::Array) &&
!options_->features.gc_enabled())) {
Error(token.loc, "value type not allowed: %s", type.GetName().c_str()); return Result::Error;
}
*out_type = type; return Result::Ok;
}
Result WastParser::ParseRefType(Type* out_type) {
WABT_TRACE(ParseRefType);
if (!PeekMatch(TokenType::ValueType)) { return ErrorExpected({"funcref", "externref"});
}
Token token = Consume();
Type type = token.type();
if (type == Type::ExternRef &&
!options_->features.reference_types_enabled()) {
Error(token.loc, "value type not allowed: %s", type.GetName().c_str()); return Result::Error;
}
Result WastParser::ParseScript(std::unique_ptr<Script>* out_script) {
WABT_TRACE(ParseScript); auto script = std::make_unique<Script>();
// Don't consume the Lpar yet, even though it is required. This way the // sub-parser functions (e.g. ParseFuncModuleField) can consume it and keep // the parsing structure more regular.
if (IsModuleField(PeekPair()) || PeekIsCustom()) { // Parse an inline module (i.e. one with no surrounding (module)). auto command = std::make_unique<ModuleCommand>();
command->module.loc = GetLocation();
CHECK_RESULT(ParseModuleFieldList(&command->module));
script->commands.emplace_back(std::move(command));
} else if (IsCommand(PeekPair())) {
CHECK_RESULT(ParseCommandList(script.get(), &script->commands));
} else if (PeekMatch(TokenType::Eof)) {
errors_->emplace_back(ErrorLevel::Warning, GetLocation(), "empty script");
} else {
ConsumeIfLpar();
ErrorExpected({"a module field", "a command"});
}
Result WastParser::ParseCustomSectionAnnotation(Module* module) {
WABT_TRACE(ParseCustomSectionAnnotation);
Location loc = GetLocation();
Token token = Consume();
if (token.text() != "custom") {
assert(
!"ParseCustomSectionAnnotation should only be called if PeekIsCustom() is true"); return Result::Error;
}
std::string section_name;
CHECK_RESULT(ParseQuotedText(§ion_name));
if (Match(TokenType::Lpar)) {
if (!PeekMatch(TokenType::After) && !PeekMatch(TokenType::Before)) { return ErrorExpected({"before", "after"});
}
Consume(); switch (Peek()) { case TokenType::Function: case TokenType::Type: case TokenType::Import: case TokenType::Export: case TokenType::Table: case TokenType::Global: case TokenType::Elem: case TokenType::Data: case TokenType::Memory: case TokenType::Code: case TokenType::Start: {
Consume(); break;
} default: { return ErrorExpected({"type", "import", "function", "table", "memory", "global", "export", "start", "elem", "code", "data"});
}
}
EXPECT(Rpar);
}
std::vector<uint8_t> data;
CHECK_RESULT(ParseTextList(&data));
EXPECT(Rpar);
bool WastParser::PeekIsCustom() { // If IsLparAnn succeeds, tokens_.front() must have text, as it is an LparAnn // token. return options_->features.annotations_enabled() && IsLparAnn(PeekPair()) &&
tokens_.front().text() == "custom";
}
Result WastParser::ParseModuleFieldList(Module* module) {
WABT_TRACE(ParseModuleFieldList); while (IsModuleField(PeekPair()) || PeekIsCustom()) {
if (PeekIsCustom()) {
CHECK_RESULT(ParseCustomSectionAnnotation(module)); continue;
}
if (Failed(ParseModuleField(module))) {
CHECK_RESULT(Synchronize(IsModuleField));
}
}
CHECK_RESULT(ResolveFuncTypes(module, errors_));
CHECK_RESULT(ResolveNamesModule(module, errors_)); return Result::Ok;
}
Result WastParser::ParseModuleField(Module* module) {
WABT_TRACE(ParseModuleField); switch (Peek(1)) { case TokenType::Data: return ParseDataModuleField(module); case TokenType::Elem: return ParseElemModuleField(module); case TokenType::Tag: return ParseTagModuleField(module); case TokenType::Export: return ParseExportModuleField(module); case TokenType::Func: return ParseFuncModuleField(module); case TokenType::Type: return ParseTypeModuleField(module); case TokenType::Global: return ParseGlobalModuleField(module); case TokenType::Import: return ParseImportModuleField(module); case TokenType::Memory: return ParseMemoryModuleField(module); case TokenType::Start: return ParseStartModuleField(module); case TokenType::Table: return ParseTableModuleField(module); default:
assert(
!"ParseModuleField should only be called if IsModuleField() is true"); return Result::Error;
}
}
Result WastParser::ParseDataModuleField(Module* module) {
WABT_TRACE(ParseDataModuleField);
EXPECT(Lpar);
Location loc = GetLocation();
EXPECT(Data);
std::string name;
ParseBindVarOpt(&name); auto field = std::make_unique<DataSegmentModuleField>(loc, name);
if (PeekMatchLpar(TokenType::Memory)) {
EXPECT(Lpar);
EXPECT(Memory);
CHECK_RESULT(ParseVar(&field->data_segment.memory_var));
EXPECT(Rpar);
CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset));
} else if (ParseVarOpt(&field->data_segment.memory_var, Var(0, loc))) {
CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset));
} else if (!ParseOffsetExprOpt(&field->data_segment.offset)) {
if (!options_->features.bulk_memory_enabled()) {
Error(loc, "passive data segments are not allowed"); return Result::Error;
}
Result WastParser::ParseElemModuleField(Module* module) {
WABT_TRACE(ParseElemModuleField);
EXPECT(Lpar);
Location loc = GetLocation();
EXPECT(Elem);
// With MVP text format the name here was intended to refer to the table // that the elem segment was part of, but we never did anything with this name // since there was only one table anyway. // With bulk-memory enabled this introduces a new name for the particular // elem segment.
std::string initial_name; bool has_name = ParseBindVarOpt(&initial_name);
std::string segment_name = initial_name;
if (!options_->features.bulk_memory_enabled()) {
segment_name = "";
} auto field = std::make_unique<ElemSegmentModuleField>(loc, segment_name);
if (options_->features.reference_types_enabled() &&
Match(TokenType::Declare)) {
field->elem_segment.kind = SegmentKind::Declared;
}
Result WastParser::ParseTypeModuleField(Module* module) {
WABT_TRACE(ParseTypeModuleField);
EXPECT(Lpar); auto field = std::make_unique<TypeModuleField>(GetLocation());
EXPECT(Type);
std::string name;
ParseBindVarOpt(&name);
EXPECT(Lpar);
Location loc = GetLocation();
if (Match(TokenType::Func)) { auto func_type = std::make_unique<FuncType>(name);
BindingHash bindings;
CHECK_RESULT(ParseFuncSignature(&func_type->sig, &bindings));
CHECK_RESULT(ErrorIfLpar({"param", "result"}));
field->type = std::move(func_type);
} else if (Match(TokenType::Struct)) {
if (!options_->features.gc_enabled()) {
Error(loc, "struct not allowed"); return Result::Error;
} auto struct_type = std::make_unique<StructType>(name);
CHECK_RESULT(ParseFieldList(&struct_type->fields));
field->type = std::move(struct_type);
} else if (Match(TokenType::Array)) {
if (!options_->features.gc_enabled()) {
Error(loc, "array type not allowed");
} auto array_type = std::make_unique<ArrayType>(name);
CHECK_RESULT(ParseField(&array_type->field));
field->type = std::move(array_type);
} else { return ErrorExpected({"func", "struct", "array"});
}
if (PeekMatchLpar(TokenType::Import)) {
CheckImportOrdering(module); auto import = std::make_unique<GlobalImport>(name);
CHECK_RESULT(ParseInlineImport(import.get()));
CHECK_RESULT(ParseGlobalType(&import->global)); auto field =
std::make_unique<ImportModuleField>(std::move(import), GetLocation());
module->AppendField(std::move(field));
} else { auto field = std::make_unique<GlobalModuleField>(loc, name);
CHECK_RESULT(ParseGlobalType(&field->global));
CHECK_RESULT(ParseTerminatingInstrList(&field->global.init_expr));
module->AppendField(std::move(field));
}
if (PeekMatchLpar(TokenType::Import)) {
CheckImportOrdering(module); auto import = std::make_unique<TableImport>(name);
CHECK_RESULT(ParseInlineImport(import.get()));
CHECK_RESULT(ParseLimitsIndex(&import->table.elem_limits));
CHECK_RESULT(ParseLimits(&import->table.elem_limits));
CHECK_RESULT(ParseRefType(&import->table.elem_type)); auto field =
std::make_unique<ImportModuleField>(std::move(import), GetLocation());
module->AppendField(std::move(field));
} else { auto field = std::make_unique<TableModuleField>(loc, name); auto& table = field->table;
CHECK_RESULT(ParseLimitsIndex(&table.elem_limits));
if (PeekMatch(TokenType::ValueType)) {
Type elem_type;
CHECK_RESULT(ParseRefType(&elem_type));
EXPECT(Lpar);
EXPECT(Elem);
auto elem_segment_field = std::make_unique<ElemSegmentModuleField>(loc);
ElemSegment& elem_segment = elem_segment_field->elem_segment;
elem_segment.table_var = Var(module->tables.size(), GetLocation()); auto offset = table.elem_limits.is_64 ? Const::I64(0) : Const::I32(0);
elem_segment.offset.push_back(std::make_unique<ConstExpr>(offset));
elem_segment.offset.back().loc = loc;
elem_segment.elem_type = elem_type; // Syntax is either an optional list of var (legacy), or a non-empty list // of elem expr.
ExprList elem_expr;
if (ParseElemExprOpt(&elem_expr)) {
elem_segment.elem_exprs.push_back(std::move(elem_expr)); // Parse the rest.
ParseElemExprListOpt(&elem_segment.elem_exprs);
} else {
ParseElemExprVarListOpt(&elem_segment.elem_exprs);
}
EXPECT(Rpar);
Result WastParser::ParseInstrList(ExprList* exprs) {
WABT_TRACE(ParseInstrList);
ExprList new_exprs; while (true) { auto pair = PeekPair();
if (IsInstr(pair)) {
if (Succeeded(ParseInstr(&new_exprs))) {
exprs->splice(exprs->end(), new_exprs);
} else {
CHECK_RESULT(Synchronize(IsInstr));
}
} else if (IsLparAnn(pair)) {
if (Succeeded(ParseCodeMetadataAnnotation(&new_exprs))) {
exprs->splice(exprs->end(), new_exprs);
} else {
CHECK_RESULT(Synchronize(IsLparAnn));
}
} else { break;
}
} return Result::Ok;
}
Result WastParser::ParseTerminatingInstrList(ExprList* exprs) {
WABT_TRACE(ParseTerminatingInstrList);
Result result = ParseInstrList(exprs); // An InstrList often has no further Lpar following it, because it would have // gobbled it up. So if there is a following Lpar it is an error. If we // handle it here we can produce a nicer error message.
CHECK_RESULT(ErrorIfLpar({"an instr"})); return result;
}
Result WastParser::ParseInstr(ExprList* exprs) {
WABT_TRACE(ParseInstr);
if (IsPlainInstr(Peek())) {
std::unique_ptr<Expr> expr;
CHECK_RESULT(ParsePlainInstr(&expr));
exprs->push_back(std::move(expr)); return Result::Ok;
} else if (IsBlockInstr(Peek())) {
std::unique_ptr<Expr> expr;
CHECK_RESULT(ParseBlockInstr(&expr));
exprs->push_back(std::move(expr)); return Result::Ok;
} else if (PeekMatchExpr()) { return ParseExpr(exprs);
} else {
assert(!"ParseInstr should only be called when IsInstr() is true"); return Result::Error;
}
}
if (options_->features.multi_memory_enabled()) { // We have to be a little careful when reading the memeory index. // If there is just a single integer folloing the instruction that // represents the lane index, so we check for either a pair of intergers // or an integers followed by offset= or align=. bool try_read_mem_index = true;
if (PeekMatch(TokenType::Nat)) { // The next token could be a memory index or a lane index
if (!PeekMatch(TokenType::OffsetEqNat, 1) &&
!PeekMatch(TokenType::AlignEqNat, 1) &&
!PeekMatch(TokenType::Nat, 1)) {
try_read_mem_index = false;
}
}
if (try_read_mem_index) {
CHECK_RESULT(ParseMemidx(loc, &memidx));
}
}
Address offset;
Address align;
ParseOffsetOpt(&offset);
ParseAlignOpt(&align);
uint64_t lane_idx = 0;
Result result = ParseSimdLane(loc, &lane_idx);
template <typename T>
Result WastParser::ParseMemoryExpr(Location loc,
std::unique_ptr<Expr>* out_expr) {
Var memidx;
CHECK_RESULT(ParseMemidx(loc, &memidx));
out_expr->reset(new T(memidx, loc)); return Result::Ok;
}
template <typename T>
Result WastParser::ParseMemoryBinaryExpr(Location loc,
std::unique_ptr<Expr>* out_expr) {
Var destmemidx;
Var srcmemidx;
CHECK_RESULT(ParseMemidx(loc, &destmemidx));
CHECK_RESULT(ParseMemidx(loc, &srcmemidx));
out_expr->reset(new T(destmemidx, srcmemidx, loc)); return Result::Ok;
}
Result WastParser::ParseSimdLane(Location loc, uint64_t* lane_idx) {
if (!PeekMatch(TokenType::Nat) && !PeekMatch(TokenType::Int)) { return ErrorExpected({"a natural number in range [0, 32)"});
}
Literal literal = Consume().literal();
Result result =
ParseInt64(literal.text, lane_idx, ParseIntType::UnsignedOnly);
// The valid range is only [0, 32), but it's only malformed if it can't // fit in a byte.
if (*lane_idx > 255) {
Error(loc, "lane index \"" PRIstringview "\" out-of-range [0, 32)",
WABT_PRINTF_STRING_VIEW_ARG(literal.text)); return Result::Error;
}
return Result::Ok;
}
Result WastParser::ParsePlainInstr(std::unique_ptr<Expr>* out_expr) {
WABT_TRACE(ParsePlainInstr);
Location loc = GetLocation(); switch (Peek()) { case TokenType::Unreachable:
Consume();
out_expr->reset(new UnreachableExpr(loc)); break;
case TokenType::Nop:
Consume();
out_expr->reset(new NopExpr(loc)); break;
case TokenType::Drop:
Consume();
out_expr->reset(new DropExpr(loc)); break;
case TokenType::Select: {
Consume();
TypeVector result;
if (options_->features.reference_types_enabled() &&
PeekMatchLpar(TokenType::Result)) {
CHECK_RESULT(ParseResultList(&result, nullptr));
}
out_expr->reset(new SelectExpr(result, loc)); break;
}
case TokenType::Br:
Consume();
CHECK_RESULT(ParsePlainInstrVar<BrExpr>(loc, out_expr)); break;
case TokenType::BrIf:
Consume();
CHECK_RESULT(ParsePlainInstrVar<BrIfExpr>(loc, out_expr)); break;
case TokenType::BrTable: {
Consume(); auto expr = std::make_unique<BrTableExpr>(loc);
CHECK_RESULT(ParseVarList(&expr->targets));
expr->default_target = expr->targets.back();
expr->targets.pop_back();
*out_expr = std::move(expr); break;
}
case TokenType::Return:
Consume();
out_expr->reset(new ReturnExpr(loc)); break;
case TokenType::Call:
Consume();
CHECK_RESULT(ParsePlainInstrVar<CallExpr>(loc, out_expr)); break;
case TokenType::CallIndirect: {
Consume(); auto expr = std::make_unique<CallIndirectExpr>(loc);
ParseVarOpt(&expr->table, Var(0, loc));
CHECK_RESULT(ParseTypeUseOpt(&expr->decl));
CHECK_RESULT(ParseUnboundFuncSignature(&expr->decl.sig));
*out_expr = std::move(expr); break;
}
case TokenType::CallRef: {
ErrorUnlessOpcodeEnabled(Consume());
out_expr->reset(new CallRefExpr(loc)); break;
}
case TokenType::ReturnCall:
ErrorUnlessOpcodeEnabled(Consume());
CHECK_RESULT(ParsePlainInstrVar<ReturnCallExpr>(loc, out_expr)); break;
case TokenType::ReturnCallIndirect: {
ErrorUnlessOpcodeEnabled(Consume()); auto expr = std::make_unique<ReturnCallIndirectExpr>(loc);
ParseVarOpt(&expr->table, Var(0, loc));
CHECK_RESULT(ParseTypeUseOpt(&expr->decl));
CHECK_RESULT(ParseUnboundFuncSignature(&expr->decl.sig));
*out_expr = std::move(expr); break;
}
case TokenType::LocalGet:
Consume();
CHECK_RESULT(ParsePlainInstrVar<LocalGetExpr>(loc, out_expr)); break;
case TokenType::LocalSet:
Consume();
CHECK_RESULT(ParsePlainInstrVar<LocalSetExpr>(loc, out_expr)); break;
case TokenType::LocalTee:
Consume();
CHECK_RESULT(ParsePlainInstrVar<LocalTeeExpr>(loc, out_expr)); break;
case TokenType::GlobalGet:
Consume();
CHECK_RESULT(ParsePlainInstrVar<GlobalGetExpr>(loc, out_expr)); break;
case TokenType::GlobalSet:
Consume();
CHECK_RESULT(ParsePlainInstrVar<GlobalSetExpr>(loc, out_expr)); break;
case TokenType::Load:
CHECK_RESULT(ParseLoadStoreInstr<LoadExpr>(loc, Consume(), out_expr)); break;
case TokenType::Store:
CHECK_RESULT(ParseLoadStoreInstr<StoreExpr>(loc, Consume(), out_expr)); break;
case TokenType::MemoryCopy:
ErrorUnlessOpcodeEnabled(Consume());
CHECK_RESULT(ParseMemoryBinaryExpr<MemoryCopyExpr>(loc, out_expr)); break;
case TokenType::MemoryFill:
ErrorUnlessOpcodeEnabled(Consume());
CHECK_RESULT(ParseMemoryExpr<MemoryFillExpr>(loc, out_expr)); break;
case TokenType::DataDrop:
ErrorUnlessOpcodeEnabled(Consume());
CHECK_RESULT(ParsePlainInstrVar<DataDropExpr>(loc, out_expr)); break;
case TokenType::MemoryInit:
ErrorUnlessOpcodeEnabled(Consume());
CHECK_RESULT(ParseMemoryInstrVar<MemoryInitExpr>(loc, out_expr)); break;
case TokenType::MemorySize:
Consume();
CHECK_RESULT(ParseMemoryExpr<MemorySizeExpr>(loc, out_expr)); break;
case TokenType::MemoryGrow:
Consume();
CHECK_RESULT(ParseMemoryExpr<MemoryGrowExpr>(loc, out_expr)); break;
case TokenType::TableCopy: {
ErrorUnlessOpcodeEnabled(Consume());
Var dst(0, loc);
Var src(0, loc);
if (options_->features.reference_types_enabled()) {
ParseVarOpt(&dst, dst);
ParseVarOpt(&src, src);
}
out_expr->reset(new TableCopyExpr(dst, src, loc)); break;
}
case TokenType::ElemDrop:
ErrorUnlessOpcodeEnabled(Consume());
CHECK_RESULT(ParsePlainInstrVar<ElemDropExpr>(loc, out_expr)); break;
case TokenType::TableInit: {
ErrorUnlessOpcodeEnabled(Consume());
Var segment_index(0, loc);
CHECK_RESULT(ParseVar(&segment_index));
Var table_index(0, loc);
if (ParseVarOpt(&table_index, table_index)) { // Here are the two forms: // // table.init $elemidx ... // table.init $tableidx $elemidx ... // // So if both indexes are provided, we need to swap them.
std::swap(segment_index, table_index);
}
out_expr->reset(new TableInitExpr(segment_index, table_index, loc)); break;
}
case TokenType::TableGet: {
ErrorUnlessOpcodeEnabled(Consume());
Var table_index(0, loc);
ParseVarOpt(&table_index, table_index);
out_expr->reset(new TableGetExpr(table_index, loc)); break;
}
case TokenType::TableSet: {
ErrorUnlessOpcodeEnabled(Consume());
Var table_index(0, loc);
ParseVarOpt(&table_index, table_index);
out_expr->reset(new TableSetExpr(table_index, loc)); break;
}
case TokenType::TableGrow: {
ErrorUnlessOpcodeEnabled(Consume());
Var table_index(0, loc);
ParseVarOpt(&table_index, table_index);
out_expr->reset(new TableGrowExpr(table_index, loc)); break;
}
case TokenType::TableSize: {
ErrorUnlessOpcodeEnabled(Consume());
Var table_index(0, loc);
ParseVarOpt(&table_index, table_index);
out_expr->reset(new TableSizeExpr(table_index, loc)); break;
}
case TokenType::TableFill: {
ErrorUnlessOpcodeEnabled(Consume());
Var table_index(0, loc);
ParseVarOpt(&table_index, table_index);
out_expr->reset(new TableFillExpr(table_index, loc)); break;
}
case TokenType::RefFunc:
ErrorUnlessOpcodeEnabled(Consume());
CHECK_RESULT(ParsePlainInstrVar<RefFuncExpr>(loc, out_expr)); break;
case TokenType::RefNull: {
ErrorUnlessOpcodeEnabled(Consume());
Type type;
CHECK_RESULT(ParseRefKind(&type));
out_expr->reset(new RefNullExpr(type, loc)); break;
}
case TokenType::RefIsNull:
ErrorUnlessOpcodeEnabled(Consume());
out_expr->reset(new RefIsNullExpr(loc)); break;
case TokenType::Throw:
ErrorUnlessOpcodeEnabled(Consume());
CHECK_RESULT(ParsePlainInstrVar<ThrowExpr>(loc, out_expr)); break;
case TokenType::Rethrow:
ErrorUnlessOpcodeEnabled(Consume());
CHECK_RESULT(ParsePlainInstrVar<RethrowExpr>(loc, out_expr)); break;
case TokenType::SimdLoadLane: {
CHECK_RESULT(
ParseSIMDLoadStoreInstr<SimdLoadLaneExpr>(loc, Consume(), out_expr)); break;
}
case TokenType::SimdStoreLane: {
CHECK_RESULT(
ParseSIMDLoadStoreInstr<SimdStoreLaneExpr>(loc, Consume(), out_expr)); break;
}
case TokenType::SimdShuffleOp: {
Token token = Consume();
ErrorUnlessOpcodeEnabled(token);
v128 values;
for (int lane = 0; lane < 16; ++lane) {
Location loc = GetLocation();
uint64_t lane_idx;
Result result = ParseSimdLane(loc, &lane_idx);
if (Failed(result)) { return Result::Error;
}
default:
assert(
!"ParsePlainInstr should only be called when IsPlainInstr() is true"); return Result::Error;
}
return Result::Ok;
}
Result WastParser::ParseSimdV128Const(Const* const_,
TokenType token_type,
ConstType const_type) {
WABT_TRACE(ParseSimdV128Const);
uint8_t lane_count = 0; bool integer = true; switch (token_type) { case TokenType::I8X16: { lane_count = 16; break; } case TokenType::I16X8: { lane_count = 8; break; } case TokenType::I32X4: { lane_count = 4; break; } case TokenType::I64X2: { lane_count = 2; break; } case TokenType::F32X4: { lane_count = 4; integer = false; break; } case TokenType::F64X2: { lane_count = 2; integer = false; break; } default: {
Error(const_->loc, "Unexpected type at start of simd constant. " "Expected one of: i8x16, i16x8, i32x4, i64x2, f32x4, f64x2. " "Found \"%s\".",
GetTokenTypeName(token_type)); return Result::Error;
}
}
Consume();
const_->loc = GetLocation();
for (int lane = 0; lane < lane_count; ++lane) {
Location loc = GetLocation();
// Check that the lane literal type matches the element type of the v128:
Token token = GetToken(); switch (token.token_type()) { case TokenType::Nat: case TokenType::Int: // OK. break;
case TokenType::Float: case TokenType::NanArithmetic: case TokenType::NanCanonical:
if (integer) {
goto error;
} break;
// For each type, parse the next literal, bound check it, and write it to // the array of bytes:
if (integer) {
std::string_view sv = Consume().literal().text;
switch (lane_count) { case16: {
uint8_t value = 0;
result = ParseInt8(sv, &value, ParseIntType::SignedAndUnsigned);
const_->set_v128_u8(lane, value); break;
} case8: {
uint16_t value = 0;
result = ParseInt16(sv, &value, ParseIntType::SignedAndUnsigned);
const_->set_v128_u16(lane, value); break;
} case4: {
uint32_t value = 0;
result = ParseInt32(sv, &value, ParseIntType::SignedAndUnsigned);
const_->set_v128_u32(lane, value); break;
} case2: {
uint64_t value = 0;
result = ParseInt64(sv, &value, ParseIntType::SignedAndUnsigned);
const_->set_v128_u64(lane, value); break;
}
}
} else { Const lane_const_; switch (lane_count) { case4:
result = ParseF32(&lane_const_, const_type);
const_->set_v128_f32(lane, lane_const_.f32_bits()); break;
case2:
result = ParseF64(&lane_const_, const_type);
const_->set_v128_f64(lane, lane_const_.f64_bits()); break;
}
// V128 is fully handled by ParseSimdV128Const:
if (opcode != Opcode::V128Const) { switch (token.token_type()) { case TokenType::Nat: case TokenType::Int: case TokenType::Float: // OK. break; case TokenType::NanArithmetic: case TokenType::NanCanonical: break; default: return ErrorExpected({"a numeric literal"}, "123, -45, 6.7e8");
}
}
Result result; switch (opcode) { case Opcode::I32Const: { auto token = Consume();
if (!token.HasLiteral()) {
result = Result::Error; break;
} auto sv = token.literal().text;
uint32_t u32;
result = ParseInt32(sv, &u32, ParseIntType::SignedAndUnsigned);
const_->set_u32(u32); break;
}
case Opcode::I64Const: { auto token = Consume();
if (!token.HasLiteral()) {
result = Result::Error; break;
} auto sv = token.literal().text;
uint64_t u64;
result = ParseInt64(sv, &u64, ParseIntType::SignedAndUnsigned);
const_->set_u64(u64); break;
}
case Opcode::F32Const:
result = ParseF32(const_, const_type); break;
case Opcode::F64Const:
result = ParseF64(const_, const_type); break;
case Opcode::V128Const:
ErrorUnlessOpcodeEnabled(opcode_token); // Parse V128 Simd Const (16 bytes).
result = ParseSimdV128Const(const_, token.token_type(), const_type); // ParseSimdV128Const report error already, just return here if parser get // errors.
if (Failed(result)) { return Result::Error;
} break;
default:
assert(!"ParseConst called with invalid opcode"); return Result::Error;
}
if (Failed(result)) {
Error(const_->loc, "invalid literal \"%s\"", token.to_string().c_str()); // Return if parser get errors. return Result::Error;
}
return Result::Ok;
}
Result WastParser::ParseExternref(Const* const_) {
WABT_TRACE(ParseExternref);
Token token = Consume();
if (!options_->features.reference_types_enabled()) {
Error(token.loc, "externref not allowed"); return Result::Error;
}
// script is nullptr when ParseModuleCommand is called from ParseModule.
if (script) {
Index command_index = script->commands.size();
if (!module->name.empty()) {
script->module_bindings.emplace(module->name,
Binding(module->loc, command_index));
}
last_module_index_ = command_index;
}
return Result::Ok;
}
Result WastParser::ParseRegisterCommand(CommandPtr* out_command) {
WABT_TRACE(ParseRegisterCommand);
EXPECT(Lpar);
Location loc = GetLocation();
EXPECT(Register);
std::string text;
Var var;
CHECK_RESULT(ParseQuotedText(&text));
ParseVarOpt(&var, Var(last_module_index_, loc));
EXPECT(Rpar);
out_command->reset(new RegisterCommand(text, var)); return Result::Ok;
}
Result WastParser::ParseInputCommand(CommandPtr*) { // Parse the input command, but always fail since this command is not // actually supported.
WABT_TRACE(ParseInputCommand);
EXPECT(Lpar);
Location loc = GetLocation();
EXPECT(Input);
Error(loc, "input command is not supported");
Var var;
std::string text;
ParseVarOpt(&var);
CHECK_RESULT(ParseQuotedText(&text));
EXPECT(Rpar); return Result::Error;
}
Result WastParser::ParseOutputCommand(CommandPtr*) { // Parse the output command, but always fail since this command is not // actually supported.
WABT_TRACE(ParseOutputCommand);
EXPECT(Lpar);
Location loc = GetLocation();
EXPECT(Output);
Error(loc, "output command is not supported");
Var var;
std::string text;
ParseVarOpt(&var);
if (Peek() == TokenType::Text) {
CHECK_RESULT(ParseQuotedText(&text));
}
EXPECT(Rpar); return Result::Error;
}
Result WastParser::ParseAction(ActionPtr* out_action) {
WABT_TRACE(ParseAction);
EXPECT(Lpar);
Location loc = GetLocation();
Result WastParser::ParseScriptModule(
std::unique_ptr<ScriptModule>* out_module) {
WABT_TRACE(ParseScriptModule);
EXPECT(Lpar);
Location loc = GetLocation();
EXPECT(Module);
std::string name;
ParseBindVarOpt(&name);
switch (Peek()) { case TokenType::Bin: {
Consume();
std::vector<uint8_t> data; // TODO(binji): The spec allows this to be empty, switch to // ParseTextListOpt.
CHECK_RESULT(ParseTextList(&data));
case TokenType::Quote: {
Consume();
std::vector<uint8_t> data; // TODO(binji): The spec allows this to be empty, switch to // ParseTextListOpt.
CHECK_RESULT(ParseTextList(&data));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.