| /* |
| * 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 <cassert> |
| #include <cstdio> |
| |
| #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<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(¤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->destmemidx); |
| ResolveMemoryVar(&expr->srcmemidx); |
| 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([func, this](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<RefFuncExpr>(&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<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::ScriptModule: |
| VisitModule(&cast<ScriptModuleCommand>(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<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 |