diff --git a/include/wabt/wast-parser.h b/include/wabt/wast-parser.h index e071c5c8d..dbceb7910 100644 --- a/include/wabt/wast-parser.h +++ b/include/wabt/wast-parser.h @@ -56,10 +56,11 @@ class WastParser { }; struct ResolveRefType { + ResolveRefType() = default; ResolveRefType(Type* target_type, Var var) : target_type(target_type), var(var) {} - Type* target_type; + Type* target_type = nullptr; Var var; }; @@ -74,18 +75,20 @@ class WastParser { typedef std::vector ReferenceVars; struct ResolveTypeVector { + ResolveTypeVector() = default; ResolveTypeVector(TypeVector* target_vector) : target_vector(target_vector) {} - TypeVector* target_vector; + TypeVector* target_vector = nullptr; ReferenceVars vars; }; struct ResolveFunc { + ResolveFunc() = default; ResolveFunc(Func* target_func) : target_func(target_func) {} - Func* target_func; + Func* target_func = nullptr; TypeVector types; ReferenceVars vars; }; @@ -207,6 +210,9 @@ class WastParser { Result ParseModuleFieldList(Module*); Result ParseModuleField(Module*); Result ParseModuleFieldImpl(Module*); + void TruncateResolveLists(size_t ref_types_size, + size_t type_vectors_size, + size_t funcs_size); Result ParseDataModuleField(Module*); Result ParseElemModuleField(Module*); Result ParseTagModuleField(Module*); diff --git a/src/wast-parser.cc b/src/wast-parser.cc index 4fcfd4dbd..f380eb002 100644 --- a/src/wast-parser.cc +++ b/src/wast-parser.cc @@ -1506,18 +1506,20 @@ Result WastParser::ParseModuleField(Module* module) { Result result = ParseModuleFieldImpl(module); if (Failed(result)) { - resolve_ref_types_.erase(resolve_ref_types_.begin() + ref_types_size, - resolve_ref_types_.end()); - resolve_type_vectors_.erase( - resolve_type_vectors_.begin() + type_vectors_size, - resolve_type_vectors_.end()); - resolve_funcs_.erase(resolve_funcs_.begin() + funcs_size, - resolve_funcs_.end()); + TruncateResolveLists(ref_types_size, type_vectors_size, funcs_size); } return result; } +void WastParser::TruncateResolveLists(size_t ref_types_size, + size_t type_vectors_size, + size_t funcs_size) { + resolve_ref_types_.resize(ref_types_size); + resolve_type_vectors_.resize(type_vectors_size); + resolve_funcs_.resize(funcs_size); +} + Result WastParser::ParseModuleFieldImpl(Module* module) { switch (Peek(1)) { case TokenType::Data: return ParseDataModuleField(module); @@ -2286,15 +2288,27 @@ Result WastParser::ParseInstrList(ExprList* exprs) { while (true) { auto pair = PeekPair(); if (IsInstr(pair)) { + // A failed instruction is destroyed along with any block signature it + // parsed, so drop the deferred type resolutions that pointed into it + // before recovering; ParseInstrList swallows the error and keeps going, + // so ParseModuleField never sees the failure and cannot do this itself. + size_t ref_types_size = resolve_ref_types_.size(); + size_t type_vectors_size = resolve_type_vectors_.size(); + size_t funcs_size = resolve_funcs_.size(); if (Succeeded(ParseInstr(&new_exprs))) { exprs->splice(exprs->end(), new_exprs); } else { + TruncateResolveLists(ref_types_size, type_vectors_size, funcs_size); CHECK_RESULT(Synchronize(IsInstr)); } } else if (IsLparAnn(pair)) { + size_t ref_types_size = resolve_ref_types_.size(); + size_t type_vectors_size = resolve_type_vectors_.size(); + size_t funcs_size = resolve_funcs_.size(); if (Succeeded(ParseCodeMetadataAnnotation(&new_exprs))) { exprs->splice(exprs->end(), new_exprs); } else { + TruncateResolveLists(ref_types_size, type_vectors_size, funcs_size); CHECK_RESULT(Synchronize(IsLparAnn)); } } else { @@ -3435,9 +3449,16 @@ Result WastParser::ParseExprList(ExprList* exprs) { WABT_TRACE(ParseExprList); ExprList new_exprs; while (PeekMatchExpr()) { + // See ParseInstrList: a discarded folded expression takes its block + // signature with it, so drop the deferred type resolutions that pointed + // into it before recovering. + size_t ref_types_size = resolve_ref_types_.size(); + size_t type_vectors_size = resolve_type_vectors_.size(); + size_t funcs_size = resolve_funcs_.size(); if (Succeeded(ParseExpr(&new_exprs))) { exprs->splice(exprs->end(), new_exprs); } else { + TruncateResolveLists(ref_types_size, type_vectors_size, funcs_size); CHECK_RESULT(Synchronize(IsExpr)); } } diff --git a/test/parse/bad-ref-type-in-recovered-instr.txt b/test/parse/bad-ref-type-in-recovered-instr.txt new file mode 100644 index 000000000..6017cbd53 --- /dev/null +++ b/test/parse/bad-ref-type-in-recovered-instr.txt @@ -0,0 +1,31 @@ +;;; TOOL: wat2wasm +;;; ARGS: --enable-function-references +;;; ERROR: 1 +(type $t (func)) + +;; A block-style instruction whose signature names a reference type registers +;; a deferred type resolution pointing into the instruction, then fails to +;; parse. ParseInstrList recovers from the failed instruction, so the enclosing +;; func still parses and ParseModuleField never sees a failure; the deferred +;; resolution must not outlive the discarded instruction. +(func block (result (ref $t))) +(func loop (result (ref $t))) +drop ) + +;; Folded form of the same bug: the failing expression is an operand of an +;; enclosing folded instruction, so the recovery happens in ParseExprList +;; rather than ParseInstrList. The if registers a deferred resolution for its +;; signature, then fails looking for (then ...) and is discarded, while drop +;; and the enclosing func still parse. +(func (drop (if (result (ref $t)) nop) (nop))) +(;; STDERR ;;; +out/test/parse/bad-ref-type-in-recovered-instr.txt:11:30: error: unexpected token ), expected end. +(func block (result (ref $t))) + ^ +out/test/parse/bad-ref-type-in-recovered-instr.txt:12:29: error: unexpected token ), expected end. +(func loop (result (ref $t))) + ^ +out/test/parse/bad-ref-type-in-recovered-instr.txt:20:35: error: unexpected token nop, expected (. +(func (drop (if (result (ref $t)) nop) (nop))) + ^^^ +;;; STDERR ;;)