Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions include/wabt/wast-parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand All @@ -74,18 +75,20 @@ class WastParser {
typedef std::vector<ReferenceVar> 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;
};
Expand Down Expand Up @@ -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*);
Expand Down
35 changes: 28 additions & 7 deletions src/wast-parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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));
}
}
Expand Down
31 changes: 31 additions & 0 deletions test/parse/bad-ref-type-in-recovered-instr.txt
Original file line number Diff line number Diff line change
@@ -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 ;;)
Loading