/* * 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 "wabt/resolve-names.h" #include #include #include "wabt/cast.h" #include "wabt/expr-visitor.h" #include "wabt/ir.h" #include "wabt/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 OnLoadExpr(LoadExpr*) override; Result OnLocalGetExpr(LocalGetExpr*) override; Result OnLocalSetExpr(LocalSetExpr*) override; Result OnLocalTeeExpr(LocalTeeExpr*) override; Result BeginLoopExpr(LoopExpr*) override; Result EndLoopExpr(LoopExpr*) override; Result OnMemoryCopyExpr(MemoryCopyExpr*) override; Result OnDataDropExpr(DataDropExpr*) override; Result OnMemoryFillExpr(MemoryFillExpr*) override; Result OnMemoryGrowExpr(MemoryGrowExpr*) override; Result OnMemoryInitExpr(MemoryInitExpr*) override; Result OnMemorySizeExpr(MemorySizeExpr*) 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 OnStoreExpr(StoreExpr*) override; Result BeginTryExpr(TryExpr*) override; Result EndTryExpr(TryExpr*) override; Result OnThrowExpr(ThrowExpr*) override; Result OnRethrowExpr(RethrowExpr*) override; Result OnSimdLoadLaneExpr(SimdLoadLaneExpr*) override; Result OnSimdStoreLaneExpr(SimdStoreLaneExpr*) 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 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(¤t_module_->func_bindings, var, "function"); } void NameResolver::ResolveGlobalVar(Var* var) { ResolveVar(¤t_module_->global_bindings, var, "global"); } void NameResolver::ResolveFuncTypeVar(Var* var) { ResolveVar(¤t_module_->type_bindings, var, "type"); } void NameResolver::ResolveTableVar(Var* var) { ResolveVar(¤t_module_->table_bindings, var, "table"); } void NameResolver::ResolveMemoryVar(Var* var) { ResolveVar(¤t_module_->memory_bindings, var, "memory"); } void NameResolver::ResolveTagVar(Var* var) { ResolveVar(¤t_module_->tag_bindings, var, "tag"); } void NameResolver::ResolveDataSegmentVar(Var* var) { ResolveVar(¤t_module_->data_segment_bindings, var, "data segment"); } void NameResolver::ResolveElemSegmentVar(Var* var) { ResolveVar(¤t_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::OnLoadExpr(LoadExpr* expr) { ResolveMemoryVar(&expr->memidx); 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::OnMemoryCopyExpr(MemoryCopyExpr* expr) { ResolveMemoryVar(&expr->srcmemidx); ResolveMemoryVar(&expr->destmemidx); return Result::Ok; } Result NameResolver::OnDataDropExpr(DataDropExpr* expr) { ResolveDataSegmentVar(&expr->var); return Result::Ok; } Result NameResolver::OnMemoryFillExpr(MemoryFillExpr* expr) { ResolveMemoryVar(&expr->memidx); return Result::Ok; } Result NameResolver::OnMemoryGrowExpr(MemoryGrowExpr* expr) { ResolveMemoryVar(&expr->memidx); return Result::Ok; } Result NameResolver::OnMemoryInitExpr(MemoryInitExpr* expr) { ResolveDataSegmentVar(&expr->var); ResolveMemoryVar(&expr->memidx); return Result::Ok; } Result NameResolver::OnMemorySizeExpr(MemorySizeExpr* expr) { ResolveMemoryVar(&expr->memidx); 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::OnStoreExpr(StoreExpr* expr) { ResolveMemoryVar(&expr->memidx); 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; } Result NameResolver::OnSimdLoadLaneExpr(SimdLoadLaneExpr* expr) { ResolveMemoryVar(&expr->memidx); return Result::Ok; } Result NameResolver::OnSimdStoreLaneExpr(SimdStoreLaneExpr* expr) { ResolveMemoryVar(&expr->memidx); 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 (ExprList& elem_expr : segment->elem_exprs) { if (elem_expr.size() == 1 && elem_expr.front().type() == ExprType::RefFunc) { ResolveFuncVar(&cast(&elem_expr.front())->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(script_module)) { VisitModule(&tsm->module); } } void NameResolver::VisitCommand(Command* command) { switch (command->type) { case CommandType::Module: VisitModule(&cast(command)->module); break; case CommandType::ScriptModule: VisitModule(&cast(command)->module); break; case CommandType::Action: case CommandType::AssertReturn: case CommandType::AssertTrap: case CommandType::AssertExhaustion: case CommandType::AssertException: 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(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(command)->module.get()); break; case CommandType::AssertUninstantiable: VisitScriptModule( cast(command)->module.get()); break; } } Result NameResolver::VisitScript(Script* script) { for (const std::unique_ptr& 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