summaryrefslogtreecommitdiffstats
path: root/third_party/wasm2c/src/resolve-names.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/wasm2c/src/resolve-names.cc')
-rw-r--r--third_party/wasm2c/src/resolve-names.cc595
1 files changed, 595 insertions, 0 deletions
diff --git a/third_party/wasm2c/src/resolve-names.cc b/third_party/wasm2c/src/resolve-names.cc
new file mode 100644
index 0000000000..099a8e5754
--- /dev/null
+++ b/third_party/wasm2c/src/resolve-names.cc
@@ -0,0 +1,595 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/resolve-names.h"
+
+#include <cassert>
+#include <cstdio>
+
+#include "src/cast.h"
+#include "src/expr-visitor.h"
+#include "src/ir.h"
+#include "src/wast-lexer.h"
+
+namespace wabt {
+
+namespace {
+
+class NameResolver : public ExprVisitor::DelegateNop {
+ public:
+ NameResolver(Script* script, Errors* errors);
+
+ Result VisitModule(Module* module);
+ Result VisitScript(Script* script);
+
+ // Implementation of ExprVisitor::DelegateNop.
+ Result BeginBlockExpr(BlockExpr*) override;
+ Result EndBlockExpr(BlockExpr*) override;
+ Result OnBrExpr(BrExpr*) override;
+ Result OnBrIfExpr(BrIfExpr*) override;
+ Result OnBrTableExpr(BrTableExpr*) override;
+ Result OnCallExpr(CallExpr*) override;
+ Result OnCallIndirectExpr(CallIndirectExpr*) override;
+ Result OnCatchExpr(TryExpr*, Catch*) override;
+ Result OnDelegateExpr(TryExpr*) override;
+ Result OnReturnCallExpr(ReturnCallExpr *) override;
+ Result OnReturnCallIndirectExpr(ReturnCallIndirectExpr*) override;
+ Result OnGlobalGetExpr(GlobalGetExpr*) override;
+ Result OnGlobalSetExpr(GlobalSetExpr*) override;
+ Result BeginIfExpr(IfExpr*) override;
+ Result EndIfExpr(IfExpr*) override;
+ Result OnLocalGetExpr(LocalGetExpr*) override;
+ Result OnLocalSetExpr(LocalSetExpr*) override;
+ Result OnLocalTeeExpr(LocalTeeExpr*) override;
+ Result BeginLoopExpr(LoopExpr*) override;
+ Result EndLoopExpr(LoopExpr*) override;
+ Result OnDataDropExpr(DataDropExpr*) override;
+ Result OnMemoryInitExpr(MemoryInitExpr*) override;
+ Result OnElemDropExpr(ElemDropExpr*) override;
+ Result OnTableCopyExpr(TableCopyExpr*) override;
+ Result OnTableInitExpr(TableInitExpr*) override;
+ Result OnTableGetExpr(TableGetExpr*) override;
+ Result OnTableSetExpr(TableSetExpr*) override;
+ Result OnTableGrowExpr(TableGrowExpr*) override;
+ Result OnTableSizeExpr(TableSizeExpr*) override;
+ Result OnTableFillExpr(TableFillExpr*) override;
+ Result OnRefFuncExpr(RefFuncExpr*) override;
+ Result BeginTryExpr(TryExpr*) override;
+ Result EndTryExpr(TryExpr*) override;
+ Result OnThrowExpr(ThrowExpr*) override;
+ Result OnRethrowExpr(RethrowExpr*) override;
+
+ private:
+ void PrintError(const Location* loc, const char* fmt, ...);
+ void PushLabel(const std::string& label);
+ void PopLabel();
+ void CheckDuplicateBindings(const BindingHash* bindings, const char* desc);
+ void PrintDuplicateBindingsError(const BindingHash::value_type&,
+ const BindingHash::value_type&,
+ const char* desc);
+ void ResolveLabelVar(Var* var);
+ void ResolveVar(const BindingHash* bindings, Var* var, const char* desc);
+ void ResolveFuncVar(Var* var);
+ void ResolveGlobalVar(Var* var);
+ void ResolveFuncTypeVar(Var* var);
+ void ResolveTableVar(Var* var);
+ void ResolveMemoryVar(Var* var);
+ void ResolveTagVar(Var* var);
+ void ResolveDataSegmentVar(Var* var);
+ void ResolveElemSegmentVar(Var* var);
+ void ResolveLocalVar(Var* var);
+ void ResolveBlockDeclarationVar(BlockDeclaration* decl);
+ void VisitFunc(Func* func);
+ void VisitExport(Export* export_);
+ void VisitGlobal(Global* global);
+ void VisitTag(Tag* tag);
+ void VisitElemSegment(ElemSegment* segment);
+ void VisitDataSegment(DataSegment* segment);
+ void VisitScriptModule(ScriptModule* script_module);
+ void VisitCommand(Command* command);
+
+ Errors* errors_ = nullptr;
+ Script* script_ = nullptr;
+ Module* current_module_ = nullptr;
+ Func* current_func_ = nullptr;
+ ExprVisitor visitor_;
+ std::vector<std::string> labels_;
+ Result result_ = Result::Ok;
+};
+
+NameResolver::NameResolver(Script* script, Errors* errors)
+ : errors_(errors),
+ script_(script),
+ visitor_(this) {}
+
+} // end anonymous namespace
+
+void WABT_PRINTF_FORMAT(3, 4) NameResolver::PrintError(const Location* loc,
+ const char* format,
+ ...) {
+ result_ = Result::Error;
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ errors_->emplace_back(ErrorLevel::Error, *loc, buffer);
+}
+
+void NameResolver::PushLabel(const std::string& label) {
+ labels_.push_back(label);
+}
+
+void NameResolver::PopLabel() {
+ labels_.pop_back();
+}
+
+void NameResolver::CheckDuplicateBindings(const BindingHash* bindings,
+ const char* desc) {
+ bindings->FindDuplicates([this, desc](const BindingHash::value_type& a,
+ const BindingHash::value_type& b) {
+ PrintDuplicateBindingsError(a, b, desc);
+ });
+}
+
+void NameResolver::PrintDuplicateBindingsError(const BindingHash::value_type& a,
+ const BindingHash::value_type& b,
+ const char* desc) {
+ // Choose the location that is later in the file.
+ const Location& a_loc = a.second.loc;
+ const Location& b_loc = b.second.loc;
+ const Location& loc = a_loc.line > b_loc.line ? a_loc : b_loc;
+ PrintError(&loc, "redefinition of %s \"%s\"", desc, a.first.c_str());
+}
+
+void NameResolver::ResolveLabelVar(Var* var) {
+ if (var->is_name()) {
+ for (int i = labels_.size() - 1; i >= 0; --i) {
+ const std::string& label = labels_[i];
+ if (label == var->name()) {
+ var->set_index(labels_.size() - i - 1);
+ return;
+ }
+ }
+ PrintError(&var->loc, "undefined label variable \"%s\"",
+ var->name().c_str());
+ }
+}
+
+void NameResolver::ResolveVar(const BindingHash* bindings,
+ Var* var,
+ const char* desc) {
+ if (var->is_name()) {
+ Index index = bindings->FindIndex(*var);
+ if (index == kInvalidIndex) {
+ PrintError(&var->loc, "undefined %s variable \"%s\"", desc,
+ var->name().c_str());
+ return;
+ }
+
+ var->set_index(index);
+ }
+}
+
+void NameResolver::ResolveFuncVar(Var* var) {
+ ResolveVar(&current_module_->func_bindings, var, "function");
+}
+
+void NameResolver::ResolveGlobalVar(Var* var) {
+ ResolveVar(&current_module_->global_bindings, var, "global");
+}
+
+void NameResolver::ResolveFuncTypeVar(Var* var) {
+ ResolveVar(&current_module_->type_bindings, var, "type");
+}
+
+void NameResolver::ResolveTableVar(Var* var) {
+ ResolveVar(&current_module_->table_bindings, var, "table");
+}
+
+void NameResolver::ResolveMemoryVar(Var* var) {
+ ResolveVar(&current_module_->memory_bindings, var, "memory");
+}
+
+void NameResolver::ResolveTagVar(Var* var) {
+ ResolveVar(&current_module_->tag_bindings, var, "tag");
+}
+
+void NameResolver::ResolveDataSegmentVar(Var* var) {
+ ResolveVar(&current_module_->data_segment_bindings, var, "data segment");
+}
+
+void NameResolver::ResolveElemSegmentVar(Var* var) {
+ ResolveVar(&current_module_->elem_segment_bindings, var, "elem segment");
+}
+
+void NameResolver::ResolveLocalVar(Var* var) {
+ if (var->is_name()) {
+ if (!current_func_) {
+ return;
+ }
+
+ Index index = current_func_->GetLocalIndex(*var);
+ if (index == kInvalidIndex) {
+ PrintError(&var->loc, "undefined local variable \"%s\"",
+ var->name().c_str());
+ return;
+ }
+
+ var->set_index(index);
+ }
+}
+
+void NameResolver::ResolveBlockDeclarationVar(BlockDeclaration* decl) {
+ if (decl->has_func_type) {
+ ResolveFuncTypeVar(&decl->type_var);
+ }
+}
+
+Result NameResolver::BeginBlockExpr(BlockExpr* expr) {
+ PushLabel(expr->block.label);
+ ResolveBlockDeclarationVar(&expr->block.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndBlockExpr(BlockExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::BeginLoopExpr(LoopExpr* expr) {
+ PushLabel(expr->block.label);
+ ResolveBlockDeclarationVar(&expr->block.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndLoopExpr(LoopExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::OnBrExpr(BrExpr* expr) {
+ ResolveLabelVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnBrIfExpr(BrIfExpr* expr) {
+ ResolveLabelVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnBrTableExpr(BrTableExpr* expr) {
+ for (Var& target : expr->targets)
+ ResolveLabelVar(&target);
+ ResolveLabelVar(&expr->default_target);
+ return Result::Ok;
+}
+
+Result NameResolver::OnCallExpr(CallExpr* expr) {
+ ResolveFuncVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnCallIndirectExpr(CallIndirectExpr* expr) {
+ if (expr->decl.has_func_type) {
+ ResolveFuncTypeVar(&expr->decl.type_var);
+ }
+ ResolveTableVar(&expr->table);
+ return Result::Ok;
+}
+
+Result NameResolver::OnReturnCallExpr(ReturnCallExpr* expr) {
+ ResolveFuncVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnReturnCallIndirectExpr(ReturnCallIndirectExpr* expr) {
+ if (expr->decl.has_func_type) {
+ ResolveFuncTypeVar(&expr->decl.type_var);
+ }
+ ResolveTableVar(&expr->table);
+ return Result::Ok;
+}
+
+Result NameResolver::OnGlobalGetExpr(GlobalGetExpr* expr) {
+ ResolveGlobalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnGlobalSetExpr(GlobalSetExpr* expr) {
+ ResolveGlobalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::BeginIfExpr(IfExpr* expr) {
+ PushLabel(expr->true_.label);
+ ResolveBlockDeclarationVar(&expr->true_.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndIfExpr(IfExpr* expr) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::OnLocalGetExpr(LocalGetExpr* expr) {
+ ResolveLocalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnLocalSetExpr(LocalSetExpr* expr) {
+ ResolveLocalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnLocalTeeExpr(LocalTeeExpr* expr) {
+ ResolveLocalVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnDataDropExpr(DataDropExpr* expr) {
+ ResolveDataSegmentVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnMemoryInitExpr(MemoryInitExpr* expr) {
+ ResolveDataSegmentVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnElemDropExpr(ElemDropExpr* expr) {
+ ResolveElemSegmentVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableCopyExpr(TableCopyExpr* expr) {
+ ResolveTableVar(&expr->dst_table);
+ ResolveTableVar(&expr->src_table);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableInitExpr(TableInitExpr* expr) {
+ ResolveElemSegmentVar(&expr->segment_index);
+ ResolveTableVar(&expr->table_index);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableGetExpr(TableGetExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableSetExpr(TableSetExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableGrowExpr(TableGrowExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableSizeExpr(TableSizeExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnTableFillExpr(TableFillExpr* expr) {
+ ResolveTableVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnRefFuncExpr(RefFuncExpr* expr) {
+ ResolveFuncVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::BeginTryExpr(TryExpr* expr) {
+ PushLabel(expr->block.label);
+ ResolveBlockDeclarationVar(&expr->block.decl);
+ return Result::Ok;
+}
+
+Result NameResolver::EndTryExpr(TryExpr*) {
+ PopLabel();
+ return Result::Ok;
+}
+
+Result NameResolver::OnCatchExpr(TryExpr*, Catch* catch_) {
+ if (!catch_->IsCatchAll()) {
+ ResolveTagVar(&catch_->var);
+ }
+ return Result::Ok;
+}
+
+Result NameResolver::OnDelegateExpr(TryExpr* expr) {
+ // Pop the label here as a try-delegate has no `end` instruction.
+ PopLabel();
+
+ // We resolve *after* popping the label in order to ensure that the
+ // delegate label starts counting after the current try-delegate.
+ ResolveLabelVar(&expr->delegate_target);
+
+ return Result::Ok;
+}
+
+Result NameResolver::OnThrowExpr(ThrowExpr* expr) {
+ ResolveTagVar(&expr->var);
+ return Result::Ok;
+}
+
+Result NameResolver::OnRethrowExpr(RethrowExpr* expr) {
+ // Note: the variable refers to corresponding (enclosing) catch, using the try
+ // block label for context.
+ ResolveLabelVar(&expr->var);
+ return Result::Ok;
+}
+
+void NameResolver::VisitFunc(Func* func) {
+ current_func_ = func;
+ if (func->decl.has_func_type) {
+ ResolveFuncTypeVar(&func->decl.type_var);
+ }
+
+ func->bindings.FindDuplicates(
+ [=](const BindingHash::value_type& a, const BindingHash::value_type& b) {
+ const char* desc =
+ (a.second.index < func->GetNumParams()) ? "parameter" : "local";
+ PrintDuplicateBindingsError(a, b, desc);
+ });
+
+ visitor_.VisitFunc(func);
+ current_func_ = nullptr;
+}
+
+void NameResolver::VisitExport(Export* export_) {
+ switch (export_->kind) {
+ case ExternalKind::Func:
+ ResolveFuncVar(&export_->var);
+ break;
+
+ case ExternalKind::Table:
+ ResolveTableVar(&export_->var);
+ break;
+
+ case ExternalKind::Memory:
+ ResolveMemoryVar(&export_->var);
+ break;
+
+ case ExternalKind::Global:
+ ResolveGlobalVar(&export_->var);
+ break;
+
+ case ExternalKind::Tag:
+ ResolveTagVar(&export_->var);
+ break;
+ }
+}
+
+void NameResolver::VisitGlobal(Global* global) {
+ visitor_.VisitExprList(global->init_expr);
+}
+
+void NameResolver::VisitTag(Tag* tag) {
+ if (tag->decl.has_func_type) {
+ ResolveFuncTypeVar(&tag->decl.type_var);
+ }
+}
+
+void NameResolver::VisitElemSegment(ElemSegment* segment) {
+ ResolveTableVar(&segment->table_var);
+ visitor_.VisitExprList(segment->offset);
+ for (ElemExpr& elem_expr : segment->elem_exprs) {
+ if (elem_expr.kind == ElemExprKind::RefFunc) {
+ ResolveFuncVar(&elem_expr.var);
+ }
+ }
+}
+
+void NameResolver::VisitDataSegment(DataSegment* segment) {
+ ResolveMemoryVar(&segment->memory_var);
+ visitor_.VisitExprList(segment->offset);
+}
+
+Result NameResolver::VisitModule(Module* module) {
+ current_module_ = module;
+ CheckDuplicateBindings(&module->elem_segment_bindings, "elem");
+ CheckDuplicateBindings(&module->func_bindings, "function");
+ CheckDuplicateBindings(&module->global_bindings, "global");
+ CheckDuplicateBindings(&module->type_bindings, "type");
+ CheckDuplicateBindings(&module->table_bindings, "table");
+ CheckDuplicateBindings(&module->memory_bindings, "memory");
+ CheckDuplicateBindings(&module->tag_bindings, "tag");
+
+ for (Func* func : module->funcs)
+ VisitFunc(func);
+ for (Export* export_ : module->exports)
+ VisitExport(export_);
+ for (Global* global : module->globals)
+ VisitGlobal(global);
+ for (Tag* tag : module->tags)
+ VisitTag(tag);
+ for (ElemSegment* elem_segment : module->elem_segments)
+ VisitElemSegment(elem_segment);
+ for (DataSegment* data_segment : module->data_segments)
+ VisitDataSegment(data_segment);
+ for (Var* start : module->starts)
+ ResolveFuncVar(start);
+ current_module_ = nullptr;
+ return result_;
+}
+
+void NameResolver::VisitScriptModule(ScriptModule* script_module) {
+ if (auto* tsm = dyn_cast<TextScriptModule>(script_module)) {
+ VisitModule(&tsm->module);
+ }
+}
+
+void NameResolver::VisitCommand(Command* command) {
+ switch (command->type) {
+ case CommandType::Module:
+ VisitModule(&cast<ModuleCommand>(command)->module);
+ break;
+
+ case CommandType::Action:
+ case CommandType::AssertReturn:
+ case CommandType::AssertTrap:
+ case CommandType::AssertExhaustion:
+ case CommandType::Register:
+ /* Don't resolve a module_var, since it doesn't really behave like other
+ * vars. You can't reference a module by index. */
+ break;
+
+ case CommandType::AssertMalformed:
+ /* Malformed modules should not be text; the whole point of this
+ * assertion is to test for malformed binary modules. */
+ break;
+
+ case CommandType::AssertInvalid: {
+ auto* assert_invalid_command = cast<AssertInvalidCommand>(command);
+ /* The module may be invalid because the names cannot be resolved; we
+ * don't want to print errors or fail if that's the case, but we still
+ * should try to resolve names when possible. */
+ Errors errors;
+ NameResolver new_resolver(script_, &errors);
+ new_resolver.VisitScriptModule(assert_invalid_command->module.get());
+ break;
+ }
+
+ case CommandType::AssertUnlinkable:
+ VisitScriptModule(cast<AssertUnlinkableCommand>(command)->module.get());
+ break;
+
+ case CommandType::AssertUninstantiable:
+ VisitScriptModule(
+ cast<AssertUninstantiableCommand>(command)->module.get());
+ break;
+ }
+}
+
+Result NameResolver::VisitScript(Script* script) {
+ for (const std::unique_ptr<Command>& command : script->commands)
+ VisitCommand(command.get());
+ return result_;
+}
+
+Result ResolveNamesModule(Module* module, Errors* errors) {
+ NameResolver resolver(nullptr, errors);
+ return resolver.VisitModule(module);
+}
+
+Result ResolveNamesScript(Script* script, Errors* errors) {
+ NameResolver resolver(script, errors);
+ return resolver.VisitScript(script);
+}
+
+} // namespace wabt