| /* |
| * 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 "ir.h" |
| |
| #include <assert.h> |
| #include <stddef.h> |
| |
| namespace wabt { |
| |
| int get_index_from_var(const BindingHash* hash, const Var* var) { |
| if (var->type == VarType::Name) |
| return hash->find_index(var->name); |
| return static_cast<int>(var->index); |
| } |
| |
| Export* get_export_by_name(const Module* module, const StringSlice* name) { |
| int index = module->export_bindings.find_index(*name); |
| if (index == -1) |
| return nullptr; |
| return module->exports[index]; |
| } |
| |
| int get_func_index_by_var(const Module* module, const Var* var) { |
| return get_index_from_var(&module->func_bindings, var); |
| } |
| |
| int get_global_index_by_var(const Module* module, const Var* var) { |
| return get_index_from_var(&module->global_bindings, var); |
| } |
| |
| int get_table_index_by_var(const Module* module, const Var* var) { |
| return get_index_from_var(&module->table_bindings, var); |
| } |
| |
| int get_memory_index_by_var(const Module* module, const Var* var) { |
| return get_index_from_var(&module->memory_bindings, var); |
| } |
| |
| int get_func_type_index_by_var(const Module* module, const Var* var) { |
| return get_index_from_var(&module->func_type_bindings, var); |
| } |
| |
| int get_local_index_by_var(const Func* func, const Var* var) { |
| if (var->type == VarType::Index) |
| return static_cast<int>(var->index); |
| |
| int result = func->param_bindings.find_index(var->name); |
| if (result != -1) |
| return result; |
| |
| result = func->local_bindings.find_index(var->name); |
| if (result == -1) |
| return result; |
| |
| /* the locals start after all the params */ |
| return func->decl.sig.param_types.size() + result; |
| } |
| |
| int get_module_index_by_var(const Script* script, const Var* var) { |
| return get_index_from_var(&script->module_bindings, var); |
| } |
| |
| Func* get_func_by_var(const Module* module, const Var* var) { |
| int index = get_index_from_var(&module->func_bindings, var); |
| if (index < 0 || static_cast<size_t>(index) >= module->funcs.size()) |
| return nullptr; |
| return module->funcs[index]; |
| } |
| |
| Global* get_global_by_var(const Module* module, const Var* var) { |
| int index = get_index_from_var(&module->global_bindings, var); |
| if (index < 0 || static_cast<size_t>(index) >= module->globals.size()) |
| return nullptr; |
| return module->globals[index]; |
| } |
| |
| Table* get_table_by_var(const Module* module, const Var* var) { |
| int index = get_index_from_var(&module->table_bindings, var); |
| if (index < 0 || static_cast<size_t>(index) >= module->tables.size()) |
| return nullptr; |
| return module->tables[index]; |
| } |
| |
| Memory* get_memory_by_var(const Module* module, const Var* var) { |
| int index = get_index_from_var(&module->memory_bindings, var); |
| if (index < 0 || static_cast<size_t>(index) >= module->memories.size()) |
| return nullptr; |
| return module->memories[index]; |
| } |
| |
| FuncType* get_func_type_by_var(const Module* module, const Var* var) { |
| int index = get_index_from_var(&module->func_type_bindings, var); |
| if (index < 0 || static_cast<size_t>(index) >= module->func_types.size()) |
| return nullptr; |
| return module->func_types[index]; |
| } |
| |
| int get_func_type_index_by_sig(const Module* module, const FuncSignature* sig) { |
| for (size_t i = 0; i < module->func_types.size(); ++i) |
| if (signatures_are_equal(&module->func_types[i]->sig, sig)) |
| return i; |
| return -1; |
| } |
| |
| int get_func_type_index_by_decl(const Module* module, |
| const FuncDeclaration* decl) { |
| if (decl_has_func_type(decl)) { |
| return get_func_type_index_by_var(module, &decl->type_var); |
| } else { |
| return get_func_type_index_by_sig(module, &decl->sig); |
| } |
| } |
| |
| Module* get_first_module(const Script* script) { |
| for (const std::unique_ptr<Command>& command : script->commands) { |
| if (command->type == CommandType::Module) |
| return command->module; |
| } |
| return nullptr; |
| } |
| |
| Module* get_module_by_var(const Script* script, const Var* var) { |
| int index = get_index_from_var(&script->module_bindings, var); |
| if (index < 0 || static_cast<size_t>(index) >= script->commands.size()) |
| return nullptr; |
| const Command& command = *script->commands[index].get(); |
| assert(command.type == CommandType::Module); |
| return command.module; |
| } |
| |
| void make_type_binding_reverse_mapping( |
| const TypeVector& types, |
| const BindingHash& bindings, |
| std::vector<std::string>* out_reverse_mapping) { |
| out_reverse_mapping->clear(); |
| out_reverse_mapping->resize(types.size()); |
| for (const auto& pair : bindings) { |
| assert(static_cast<size_t>(pair.second.index) < |
| out_reverse_mapping->size()); |
| (*out_reverse_mapping)[pair.second.index] = pair.first; |
| } |
| } |
| |
| ModuleField* append_module_field(Module* module) { |
| ModuleField* result = new ModuleField(); |
| if (!module->first_field) |
| module->first_field = result; |
| else if (module->last_field) |
| module->last_field->next = result; |
| module->last_field = result; |
| return result; |
| } |
| |
| FuncType* append_implicit_func_type(Location* loc, |
| Module* module, |
| FuncSignature* sig) { |
| ModuleField* field = append_module_field(module); |
| field->loc = *loc; |
| field->type = ModuleFieldType::FuncType; |
| field->func_type = new FuncType(); |
| field->func_type->sig = *sig; |
| |
| module->func_types.push_back(field->func_type); |
| return field->func_type; |
| } |
| |
| void destroy_var(Var* var) { |
| if (var->type == VarType::Name) |
| destroy_string_slice(&var->name); |
| } |
| |
| void destroy_expr_list(Expr* first) { |
| Expr* expr = first; |
| while (expr) { |
| Expr* next = expr->next; |
| delete expr; |
| expr = next; |
| } |
| } |
| |
| Var::Var(int64_t index) : type(VarType::Index), index(index) { |
| WABT_ZERO_MEMORY(loc); |
| } |
| |
| Var::Var(const StringSlice& name) : type(VarType::Name), name(name) { |
| WABT_ZERO_MEMORY(loc); |
| } |
| |
| Const::Const(I32, uint32_t value) : type(Type::I32), u32(value) { |
| WABT_ZERO_MEMORY(loc); |
| } |
| |
| Const::Const(I64, uint64_t value) : type(Type::I64), u64(value) { |
| WABT_ZERO_MEMORY(loc); |
| } |
| |
| Const::Const(F32, uint32_t value) : type(Type::F32), f32_bits(value) { |
| WABT_ZERO_MEMORY(loc); |
| } |
| |
| Const::Const(F64, uint64_t value) : type(Type::F64), f64_bits(value) { |
| WABT_ZERO_MEMORY(loc); |
| } |
| |
| Block::Block(): first(nullptr) { |
| WABT_ZERO_MEMORY(label); |
| } |
| |
| Block::Block(Expr* first) : first(first) { |
| WABT_ZERO_MEMORY(label); |
| } |
| |
| Block::~Block() { |
| destroy_string_slice(&label); |
| destroy_expr_list(first); |
| } |
| |
| Expr::Expr() : type(ExprType::Binary), next(nullptr) { |
| WABT_ZERO_MEMORY(loc); |
| binary.opcode = Opcode::Nop; |
| } |
| |
| Expr::Expr(ExprType type) : type(type), next(nullptr) { |
| WABT_ZERO_MEMORY(loc); |
| } |
| |
| Expr::~Expr() { |
| switch (type) { |
| case ExprType::Block: |
| delete block; |
| break; |
| case ExprType::Br: |
| destroy_var(&br.var); |
| break; |
| case ExprType::BrIf: |
| destroy_var(&br_if.var); |
| break; |
| case ExprType::BrTable: |
| for (Var& var : *br_table.targets) |
| destroy_var(&var); |
| delete br_table.targets; |
| destroy_var(&br_table.default_target); |
| break; |
| case ExprType::Call: |
| destroy_var(&call.var); |
| break; |
| case ExprType::CallIndirect: |
| destroy_var(&call_indirect.var); |
| break; |
| case ExprType::GetGlobal: |
| destroy_var(&get_global.var); |
| break; |
| case ExprType::GetLocal: |
| destroy_var(&get_local.var); |
| break; |
| case ExprType::If: |
| delete if_.true_; |
| destroy_expr_list(if_.false_); |
| break; |
| case ExprType::Loop: |
| delete loop; |
| break; |
| case ExprType::SetGlobal: |
| destroy_var(&set_global.var); |
| break; |
| case ExprType::SetLocal: |
| destroy_var(&set_local.var); |
| break; |
| case ExprType::TeeLocal: |
| destroy_var(&tee_local.var); |
| break; |
| |
| case ExprType::Binary: |
| case ExprType::Compare: |
| case ExprType::Const: |
| case ExprType::Convert: |
| case ExprType::Drop: |
| case ExprType::CurrentMemory: |
| case ExprType::GrowMemory: |
| case ExprType::Load: |
| case ExprType::Nop: |
| case ExprType::Return: |
| case ExprType::Select: |
| case ExprType::Store: |
| case ExprType::Unary: |
| case ExprType::Unreachable: |
| break; |
| } |
| } |
| |
| // static |
| Expr* Expr::CreateBinary(Opcode opcode) { |
| Expr* expr = new Expr(ExprType::Binary); |
| expr->binary.opcode = opcode; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateBlock(Block* block) { |
| Expr* expr = new Expr(ExprType::Block); |
| expr->block = block; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateBr(Var var) { |
| Expr* expr = new Expr(ExprType::Br); |
| expr->br.var = var; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateBrIf(Var var) { |
| Expr* expr = new Expr(ExprType::BrIf); |
| expr->br_if.var = var; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateBrTable(VarVector* targets, Var default_target) { |
| Expr* expr = new Expr(ExprType::BrTable); |
| expr->br_table.targets = targets; |
| expr->br_table.default_target = default_target; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateCall(Var var) { |
| Expr* expr = new Expr(ExprType::Call); |
| expr->call.var = var; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateCallIndirect(Var var) { |
| Expr* expr = new Expr(ExprType::CallIndirect); |
| expr->call_indirect.var = var; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateCompare(Opcode opcode) { |
| Expr* expr = new Expr(ExprType::Compare); |
| expr->compare.opcode = opcode; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateConst(const Const& const_) { |
| Expr* expr = new Expr(ExprType::Const); |
| expr->const_ = const_; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateConvert(Opcode opcode) { |
| Expr* expr = new Expr(ExprType::Convert); |
| expr->convert.opcode = opcode; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateCurrentMemory() { |
| return new Expr(ExprType::CurrentMemory); |
| } |
| |
| // static |
| Expr* Expr::CreateDrop() { |
| return new Expr(ExprType::Drop); |
| } |
| |
| // static |
| Expr* Expr::CreateGetGlobal(Var var) { |
| Expr* expr = new Expr(ExprType::GetGlobal); |
| expr->get_global.var = var; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateGetLocal(Var var) { |
| Expr* expr = new Expr(ExprType::GetLocal); |
| expr->get_local.var = var; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateGrowMemory() { |
| return new Expr(ExprType::GrowMemory); |
| } |
| |
| // static |
| Expr* Expr::CreateIf(Block* true_, Expr* false_) { |
| Expr* expr = new Expr(ExprType::If); |
| expr->if_.true_ = true_; |
| expr->if_.false_ = false_; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateLoad(Opcode opcode, uint32_t align, uint64_t offset) { |
| Expr* expr = new Expr(ExprType::Load); |
| expr->load.opcode = opcode; |
| expr->load.align = align; |
| expr->load.offset = offset; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateLoop(Block* block) { |
| Expr* expr = new Expr(ExprType::Loop); |
| expr->loop = block; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateNop() { |
| return new Expr(ExprType::Nop); |
| } |
| |
| // static |
| Expr* Expr::CreateReturn() { |
| return new Expr(ExprType::Return); |
| } |
| |
| // static |
| Expr* Expr::CreateSelect() { |
| return new Expr(ExprType::Select); |
| } |
| |
| // static |
| Expr* Expr::CreateSetGlobal(Var var) { |
| Expr* expr = new Expr(ExprType::SetGlobal); |
| expr->set_global.var = var; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateSetLocal(Var var) { |
| Expr* expr = new Expr(ExprType::SetLocal); |
| expr->set_local.var = var; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateStore(Opcode opcode, uint32_t align, uint64_t offset) { |
| Expr* expr = new Expr(ExprType::Store); |
| expr->store.opcode = opcode; |
| expr->store.align = align; |
| expr->store.offset = offset; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateTeeLocal(Var var) { |
| Expr* expr = new Expr(ExprType::TeeLocal); |
| expr->tee_local.var = var; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateUnary(Opcode opcode) { |
| Expr* expr = new Expr(ExprType::Unary); |
| expr->unary.opcode = opcode; |
| return expr; |
| } |
| |
| // static |
| Expr* Expr::CreateUnreachable() { |
| return new Expr(ExprType::Unreachable); |
| } |
| |
| FuncType::FuncType() { |
| WABT_ZERO_MEMORY(name); |
| } |
| |
| FuncType::~FuncType() { |
| destroy_string_slice(&name); |
| } |
| |
| FuncDeclaration::FuncDeclaration() : has_func_type(false) { |
| WABT_ZERO_MEMORY(type_var); |
| } |
| |
| FuncDeclaration::~FuncDeclaration() { |
| destroy_var(&type_var); |
| } |
| |
| Func::Func() : first_expr(nullptr) { |
| WABT_ZERO_MEMORY(name); |
| } |
| |
| Func::~Func() { |
| destroy_string_slice(&name); |
| destroy_expr_list(first_expr); |
| } |
| |
| Global::Global() : type(Type::Void), mutable_(false), init_expr(nullptr) { |
| WABT_ZERO_MEMORY(name); |
| } |
| |
| Global::~Global() { |
| destroy_string_slice(&name); |
| destroy_expr_list(init_expr); |
| } |
| |
| Table::Table() { |
| WABT_ZERO_MEMORY(name); |
| WABT_ZERO_MEMORY(elem_limits); |
| } |
| |
| Table::~Table() { |
| destroy_string_slice(&name); |
| } |
| |
| ElemSegment::ElemSegment() : offset(nullptr) { |
| WABT_ZERO_MEMORY(table_var); |
| } |
| |
| ElemSegment::~ElemSegment() { |
| destroy_var(&table_var); |
| destroy_expr_list(offset); |
| for (Var& var : vars) |
| destroy_var(&var); |
| } |
| |
| DataSegment::DataSegment() : offset(nullptr), data(nullptr), size(0) { |
| WABT_ZERO_MEMORY(memory_var); |
| } |
| |
| DataSegment::~DataSegment() { |
| destroy_var(&memory_var); |
| destroy_expr_list(offset); |
| delete [] data; |
| } |
| |
| Memory::Memory() { |
| WABT_ZERO_MEMORY(name); |
| WABT_ZERO_MEMORY(page_limits); |
| } |
| |
| Memory::~Memory() { |
| destroy_string_slice(&name); |
| } |
| |
| Import::Import() : kind(ExternalKind::Func), func(nullptr) { |
| WABT_ZERO_MEMORY(module_name); |
| WABT_ZERO_MEMORY(field_name); |
| } |
| |
| Import::~Import() { |
| destroy_string_slice(&module_name); |
| destroy_string_slice(&field_name); |
| switch (kind) { |
| case ExternalKind::Func: |
| delete func; |
| break; |
| case ExternalKind::Table: |
| delete table; |
| break; |
| case ExternalKind::Memory: |
| delete memory; |
| break; |
| case ExternalKind::Global: |
| delete global; |
| break; |
| } |
| } |
| |
| Export::Export() { |
| WABT_ZERO_MEMORY(name); |
| WABT_ZERO_MEMORY(var); |
| } |
| |
| Export::~Export() { |
| destroy_string_slice(&name); |
| destroy_var(&var); |
| } |
| |
| void destroy_memory(Memory* memory) { |
| destroy_string_slice(&memory->name); |
| } |
| |
| void destroy_table(Table* table) { |
| destroy_string_slice(&table->name); |
| } |
| |
| ModuleField::ModuleField() : type(ModuleFieldType::Start), next(nullptr) { |
| WABT_ZERO_MEMORY(loc); |
| WABT_ZERO_MEMORY(start); |
| } |
| |
| ModuleField::~ModuleField() { |
| switch (type) { |
| case ModuleFieldType::Func: |
| delete func; |
| break; |
| case ModuleFieldType::Global: |
| delete global; |
| break; |
| case ModuleFieldType::Import: |
| delete import; |
| break; |
| case ModuleFieldType::Export: |
| delete export_; |
| break; |
| case ModuleFieldType::FuncType: |
| delete func_type; |
| break; |
| case ModuleFieldType::Table: |
| delete table; |
| break; |
| case ModuleFieldType::ElemSegment: |
| delete elem_segment; |
| break; |
| case ModuleFieldType::Memory: |
| delete memory; |
| break; |
| case ModuleFieldType::DataSegment: |
| delete data_segment; |
| break; |
| case ModuleFieldType::Start: |
| destroy_var(&start); |
| break; |
| } |
| } |
| |
| Module::Module() |
| : first_field(nullptr), |
| last_field(nullptr), |
| num_func_imports(0), |
| num_table_imports(0), |
| num_memory_imports(0), |
| num_global_imports(0), |
| start(0) { |
| WABT_ZERO_MEMORY(loc); |
| WABT_ZERO_MEMORY(name); |
| } |
| |
| Module::~Module() { |
| destroy_string_slice(&name); |
| |
| ModuleField* field = first_field; |
| while (field) { |
| ModuleField* next_field = field->next; |
| delete field; |
| field = next_field; |
| } |
| } |
| |
| RawModule::RawModule() : type(RawModuleType::Text), text(nullptr) {} |
| |
| RawModule::~RawModule() { |
| if (type == RawModuleType::Text) { |
| delete text; |
| } else { |
| destroy_string_slice(&binary.name); |
| delete [] binary.data; |
| } |
| } |
| |
| ActionInvoke::ActionInvoke() {} |
| |
| Action::Action() : type(ActionType::Get) { |
| WABT_ZERO_MEMORY(loc); |
| WABT_ZERO_MEMORY(module_var); |
| WABT_ZERO_MEMORY(name); |
| } |
| |
| Action::~Action() { |
| destroy_var(&module_var); |
| destroy_string_slice(&name); |
| switch (type) { |
| case ActionType::Invoke: |
| delete invoke; |
| break; |
| case ActionType::Get: |
| break; |
| } |
| } |
| |
| Command::Command() : type(CommandType::Module), module(nullptr) {} |
| |
| Command::~Command() { |
| switch (type) { |
| case CommandType::Module: |
| delete module; |
| break; |
| case CommandType::Action: |
| delete action; |
| break; |
| case CommandType::Register: |
| destroy_string_slice(®ister_.module_name); |
| destroy_var(®ister_.var); |
| break; |
| case CommandType::AssertMalformed: |
| delete assert_malformed.module; |
| destroy_string_slice(&assert_malformed.text); |
| break; |
| case CommandType::AssertInvalid: |
| case CommandType::AssertInvalidNonBinary: |
| delete assert_invalid.module; |
| destroy_string_slice(&assert_invalid.text); |
| break; |
| case CommandType::AssertUnlinkable: |
| delete assert_unlinkable.module; |
| destroy_string_slice(&assert_unlinkable.text); |
| break; |
| case CommandType::AssertUninstantiable: |
| delete assert_uninstantiable.module; |
| destroy_string_slice(&assert_uninstantiable.text); |
| break; |
| case CommandType::AssertReturn: |
| delete assert_return.action; |
| delete assert_return.expected; |
| break; |
| case CommandType::AssertReturnCanonicalNan: |
| delete assert_return_arithmetic_nan.action; |
| break; |
| case CommandType::AssertReturnArithmeticNan: |
| delete assert_return_canonical_nan.action; |
| break; |
| case CommandType::AssertTrap: |
| case CommandType::AssertExhaustion: |
| delete assert_trap.action; |
| destroy_string_slice(&assert_trap.text); |
| break; |
| } |
| } |
| |
| Script::Script() {} |
| |
| #define CHECK_RESULT(expr) \ |
| do { \ |
| if (WABT_FAILED((expr))) \ |
| return Result::Error; \ |
| } while (0) |
| |
| #define CALLBACK(member) \ |
| CHECK_RESULT((visitor)->member \ |
| ? (visitor)->member(expr, (visitor)->user_data) \ |
| : Result::Ok) |
| |
| static Result visit_expr(Expr* expr, ExprVisitor* visitor); |
| |
| Result visit_expr_list(Expr* first, ExprVisitor* visitor) { |
| for (Expr* expr = first; expr; expr = expr->next) |
| CHECK_RESULT(visit_expr(expr, visitor)); |
| return Result::Ok; |
| } |
| |
| static Result visit_expr(Expr* expr, ExprVisitor* visitor) { |
| switch (expr->type) { |
| case ExprType::Binary: |
| CALLBACK(on_binary_expr); |
| break; |
| |
| case ExprType::Block: |
| CALLBACK(begin_block_expr); |
| CHECK_RESULT(visit_expr_list(expr->block->first, visitor)); |
| CALLBACK(end_block_expr); |
| break; |
| |
| case ExprType::Br: |
| CALLBACK(on_br_expr); |
| break; |
| |
| case ExprType::BrIf: |
| CALLBACK(on_br_if_expr); |
| break; |
| |
| case ExprType::BrTable: |
| CALLBACK(on_br_table_expr); |
| break; |
| |
| case ExprType::Call: |
| CALLBACK(on_call_expr); |
| break; |
| |
| case ExprType::CallIndirect: |
| CALLBACK(on_call_indirect_expr); |
| break; |
| |
| case ExprType::Compare: |
| CALLBACK(on_compare_expr); |
| break; |
| |
| case ExprType::Const: |
| CALLBACK(on_const_expr); |
| break; |
| |
| case ExprType::Convert: |
| CALLBACK(on_convert_expr); |
| break; |
| |
| case ExprType::CurrentMemory: |
| CALLBACK(on_current_memory_expr); |
| break; |
| |
| case ExprType::Drop: |
| CALLBACK(on_drop_expr); |
| break; |
| |
| case ExprType::GetGlobal: |
| CALLBACK(on_get_global_expr); |
| break; |
| |
| case ExprType::GetLocal: |
| CALLBACK(on_get_local_expr); |
| break; |
| |
| case ExprType::GrowMemory: |
| CALLBACK(on_grow_memory_expr); |
| break; |
| |
| case ExprType::If: |
| CALLBACK(begin_if_expr); |
| CHECK_RESULT(visit_expr_list(expr->if_.true_->first, visitor)); |
| CALLBACK(after_if_true_expr); |
| CHECK_RESULT(visit_expr_list(expr->if_.false_, visitor)); |
| CALLBACK(end_if_expr); |
| break; |
| |
| case ExprType::Load: |
| CALLBACK(on_load_expr); |
| break; |
| |
| case ExprType::Loop: |
| CALLBACK(begin_loop_expr); |
| CHECK_RESULT(visit_expr_list(expr->loop->first, visitor)); |
| CALLBACK(end_loop_expr); |
| break; |
| |
| case ExprType::Nop: |
| CALLBACK(on_nop_expr); |
| break; |
| |
| case ExprType::Return: |
| CALLBACK(on_return_expr); |
| break; |
| |
| case ExprType::Select: |
| CALLBACK(on_select_expr); |
| break; |
| |
| case ExprType::SetGlobal: |
| CALLBACK(on_set_global_expr); |
| break; |
| |
| case ExprType::SetLocal: |
| CALLBACK(on_set_local_expr); |
| break; |
| |
| case ExprType::Store: |
| CALLBACK(on_store_expr); |
| break; |
| |
| case ExprType::TeeLocal: |
| CALLBACK(on_tee_local_expr); |
| break; |
| |
| case ExprType::Unary: |
| CALLBACK(on_unary_expr); |
| break; |
| |
| case ExprType::Unreachable: |
| CALLBACK(on_unreachable_expr); |
| break; |
| } |
| |
| return Result::Ok; |
| } |
| |
| /* TODO(binji): make the visitor non-recursive */ |
| Result visit_func(Func* func, ExprVisitor* visitor) { |
| return visit_expr_list(func->first_expr, visitor); |
| } |
| |
| } // namespace wabt |