diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/validator.cc | 889 |
1 files changed, 460 insertions, 429 deletions
diff --git a/src/validator.cc b/src/validator.cc index d9179ba5..16a1f7ca 100644 --- a/src/validator.cc +++ b/src/validator.cc @@ -30,62 +30,141 @@ namespace wabt { namespace { -enum class ActionResultKind { - Error, - Types, - Type, -}; - -struct ActionResult { - ActionResultKind kind; - union { - const TypeVector* types; - Type type; +class Validator { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(Validator); + Validator(SourceErrorHandler*, WastLexer*, const Script*); + + Result CheckScript(const Script* script); + + private: + struct ActionResult { + enum class Kind { + Error, + Types, + Type, + } kind; + + union { + const TypeVector* types; + Type type; + }; }; -}; - -struct Context { - WABT_DISALLOW_COPY_AND_ASSIGN(Context); - Context(SourceErrorHandler*, WastLexer*, const Script*); + void WABT_PRINTF_FORMAT(3, 4) + PrintError(const Location* loc, const char* fmt, ...); void OnTypecheckerError(const char* msg); - - SourceErrorHandler* error_handler = nullptr; - WastLexer* lexer = nullptr; - const Script* script = nullptr; - const Module* current_module = nullptr; - const Func* current_func = nullptr; - Index current_table_index = 0; - Index current_memory_index = 0; - Index current_global_index = 0; - Index num_imported_globals = 0; - TypeChecker typechecker; + Result CheckVar(Index max_index, + const Var* var, + const char* desc, + Index* out_index); + Result CheckFuncVar(const Var* var, const Func** out_func); + Result CheckGlobalVar(const Var* var, + const Global** out_global, + Index* out_global_index); + Type GetGlobalVarTypeOrAny(const Var* var); + Result CheckFuncTypeVar(const Var* var, const FuncType** out_func_type); + Result CheckTableVar(const Var* var, const Table** out_table); + Result CheckMemoryVar(const Var* var, const Memory** out_memory); + Result CheckLocalVar(const Var* var, Type* out_type); + Type GetLocalVarTypeOrAny(const Var* var); + void CheckAlign(const Location* loc, + Address alignment, + Address natural_alignment); + void CheckOffset(const Location* loc, uint64_t offset); + void CheckType(const Location* loc, + Type actual, + Type expected, + const char* desc); + void CheckTypeIndex(const Location* loc, + Type actual, + Type expected, + const char* desc, + Index index, + const char* index_kind); + void CheckTypes(const Location* loc, + const TypeVector& actual, + const TypeVector& expected, + const char* desc, + const char* index_kind); + void CheckConstTypes(const Location* loc, + const TypeVector& actual, + const ConstVector& expected, + const char* desc); + void CheckConstType(const Location* loc, + Type actual, + const ConstVector& expected, + const char* desc); + void CheckAssertReturnNanType(const Location* loc, + Type actual, + const char* desc); + void CheckExprList(const Location* loc, const Expr* first); + void CheckHasMemory(const Location* loc, Opcode opcode); + void CheckExpr(const Expr* expr); + void CheckFuncSignatureMatchesFuncType(const Location* loc, + const FuncSignature& sig, + const FuncType* func_type); + void CheckFunc(const Location* loc, const Func* func); + void PrintConstExprError(const Location* loc, const char* desc); + void CheckConstInitExpr(const Location* loc, + const Expr* expr, + Type expected_type, + const char* desc); + void CheckGlobal(const Location* loc, const Global* global); + void CheckLimits(const Location* loc, + const Limits* limits, + uint64_t absolute_max, + const char* desc); + void CheckTable(const Location* loc, const Table* table); + void CheckElemSegments(const Module* module); + void CheckMemory(const Location* loc, const Memory* memory); + void CheckDataSegments(const Module* module); + void CheckImport(const Location* loc, const Import* import); + void CheckExport(const Export* export_); + + void OnDuplicateBinding(const BindingHash::value_type& a, + const BindingHash::value_type& b); + void CheckDuplicateExportBindings(const Module* module); + void CheckModule(const Module* module); + const TypeVector* CheckInvoke(const Action* action); + Result CheckGet(const Action* action, Type* out_type); + ActionResult CheckAction(const Action* action); + void CheckAssertReturnNanCommand(const Action* action); + void CheckCommand(const Command* command); + + SourceErrorHandler* error_handler_ = nullptr; + WastLexer* lexer_ = nullptr; + const Script* script_ = nullptr; + const Module* current_module_ = nullptr; + const Func* current_func_ = nullptr; + Index current_table_index_ = 0; + Index current_memory_index_ = 0; + Index current_global_index_ = 0; + Index num_imported_globals_ = 0; + TypeChecker typechecker_; // Cached for access by OnTypecheckerError. - const Location* expr_loc = nullptr; - Result result = Result::Ok; + const Location* expr_loc_ = nullptr; + Result result_ = Result::Ok; }; -Context::Context(SourceErrorHandler* error_handler, - WastLexer* lexer, - const Script* script) - : error_handler(error_handler), lexer(lexer), script(script) { - typechecker.set_error_callback( +Validator::Validator(SourceErrorHandler* error_handler, + WastLexer* lexer, + const Script* script) + : error_handler_(error_handler), lexer_(lexer), script_(script) { + typechecker_.set_error_callback( [this](const char* msg) { OnTypecheckerError(msg); }); } -} // namespace - -static void WABT_PRINTF_FORMAT(3, 4) - print_error(Context* ctx, const Location* loc, const char* fmt, ...) { - ctx->result = Result::Error; +void Validator::PrintError(const Location* loc, const char* fmt, ...) { + result_ = Result::Error; va_list args; va_start(args, fmt); - wast_format_error(ctx->error_handler, loc, ctx->lexer, fmt, args); + wast_format_error(error_handler_, loc, lexer_, fmt, args); va_end(args); } -void Context::OnTypecheckerError(const char* msg) { - print_error(this, expr_loc, "%s", msg); +void Validator::OnTypecheckerError(const char* msg) { + PrintError(expr_loc_, "%s", msg); } static bool is_power_of_two(uint32_t x) { @@ -98,104 +177,95 @@ static Address get_opcode_natural_alignment(Opcode opcode) { return memory_size; } -static Result check_var(Context* ctx, - Index max_index, - const Var* var, - const char* desc, - Index* out_index) { +Result Validator::CheckVar(Index max_index, + const Var* var, + const char* desc, + Index* out_index) { assert(var->type == VarType::Index); if (var->index < max_index) { if (out_index) *out_index = var->index; return Result::Ok; } - print_error(ctx, &var->loc, "%s variable out of range (max %" PRIindex ")", - desc, max_index); + PrintError(&var->loc, "%s variable out of range (max %" PRIindex ")", desc, + max_index); return Result::Error; } -static Result check_func_var(Context* ctx, - const Var* var, - const Func** out_func) { +Result Validator::CheckFuncVar(const Var* var, const Func** out_func) { Index index; - if (WABT_FAILED(check_var(ctx, ctx->current_module->funcs.size(), var, - "function", &index))) { + if (WABT_FAILED( + CheckVar(current_module_->funcs.size(), var, "function", &index))) { return Result::Error; } if (out_func) - *out_func = ctx->current_module->funcs[index]; + *out_func = current_module_->funcs[index]; return Result::Ok; } -static Result check_global_var(Context* ctx, - const Var* var, - const Global** out_global, - Index* out_global_index) { +Result Validator::CheckGlobalVar(const Var* var, + const Global** out_global, + Index* out_global_index) { Index index; - if (WABT_FAILED(check_var(ctx, ctx->current_module->globals.size(), var, - "global", &index))) { + if (WABT_FAILED( + CheckVar(current_module_->globals.size(), var, "global", &index))) { return Result::Error; } if (out_global) - *out_global = ctx->current_module->globals[index]; + *out_global = current_module_->globals[index]; if (out_global_index) *out_global_index = index; return Result::Ok; } -static Type get_global_var_type_or_any(Context* ctx, const Var* var) { +Type Validator::GetGlobalVarTypeOrAny(const Var* var) { const Global* global; - if (WABT_SUCCEEDED(check_global_var(ctx, var, &global, nullptr))) + if (WABT_SUCCEEDED(CheckGlobalVar(var, &global, nullptr))) return global->type; return Type::Any; } -static Result check_func_type_var(Context* ctx, - const Var* var, - const FuncType** out_func_type) { +Result Validator::CheckFuncTypeVar(const Var* var, + const FuncType** out_func_type) { Index index; - if (WABT_FAILED(check_var(ctx, ctx->current_module->func_types.size(), var, - "function type", &index))) { + if (WABT_FAILED(CheckVar(current_module_->func_types.size(), var, + "function type", &index))) { return Result::Error; } if (out_func_type) - *out_func_type = ctx->current_module->func_types[index]; + *out_func_type = current_module_->func_types[index]; return Result::Ok; } -static Result check_table_var(Context* ctx, - const Var* var, - const Table** out_table) { +Result Validator::CheckTableVar(const Var* var, const Table** out_table) { Index index; - if (WABT_FAILED(check_var(ctx, ctx->current_module->tables.size(), var, - "table", &index))) { + if (WABT_FAILED( + CheckVar(current_module_->tables.size(), var, "table", &index))) { return Result::Error; } if (out_table) - *out_table = ctx->current_module->tables[index]; + *out_table = current_module_->tables[index]; return Result::Ok; } -static Result check_memory_var(Context* ctx, - const Var* var, - const Memory** out_memory) { +Result Validator::CheckMemoryVar(const Var* var, const Memory** out_memory) { Index index; - if (WABT_FAILED(check_var(ctx, ctx->current_module->memories.size(), var, - "memory", &index))) { + if (WABT_FAILED( + CheckVar(current_module_->memories.size(), var, "memory", &index))) { return Result::Error; } if (out_memory) - *out_memory = ctx->current_module->memories[index]; + *out_memory = current_module_->memories[index]; return Result::Ok; } -static Result check_local_var(Context* ctx, const Var* var, Type* out_type) { - const Func* func = ctx->current_func; +Result Validator::CheckLocalVar(const Var* var, Type* out_type) { + const Func* func = current_func_; Index max_index = get_num_params_and_locals(func); Index index = get_local_index_by_var(func, var); if (index < max_index) { @@ -204,359 +274,334 @@ static Result check_local_var(Context* ctx, const Var* var, Type* out_type) { if (index < num_params) { *out_type = get_param_type(func, index); } else { - *out_type = ctx->current_func->local_types[index - num_params]; + *out_type = current_func_->local_types[index - num_params]; } } return Result::Ok; } if (var->type == VarType::Name) { - print_error(ctx, &var->loc, - "undefined local variable \"" PRIstringslice "\"", - WABT_PRINTF_STRING_SLICE_ARG(var->name)); + PrintError(&var->loc, "undefined local variable \"" PRIstringslice "\"", + WABT_PRINTF_STRING_SLICE_ARG(var->name)); } else { - print_error(ctx, &var->loc, - "local variable out of range (max %" PRIindex ")", max_index); + PrintError(&var->loc, "local variable out of range (max %" PRIindex ")", + max_index); } return Result::Error; } -static Type get_local_var_type_or_any(Context* ctx, const Var* var) { +Type Validator::GetLocalVarTypeOrAny(const Var* var) { Type type = Type::Any; - check_local_var(ctx, var, &type); + CheckLocalVar(var, &type); return type; } -static void check_align(Context* ctx, - const Location* loc, - Address alignment, - Address natural_alignment) { +void Validator::CheckAlign(const Location* loc, + Address alignment, + Address natural_alignment) { if (alignment != WABT_USE_NATURAL_ALIGNMENT) { if (!is_power_of_two(alignment)) - print_error(ctx, loc, "alignment must be power-of-two"); + PrintError(loc, "alignment must be power-of-two"); if (alignment > natural_alignment) { - print_error(ctx, loc, - "alignment must not be larger than natural alignment (%u)", - natural_alignment); + PrintError(loc, + "alignment must not be larger than natural alignment (%u)", + natural_alignment); } } } -static void check_offset(Context* ctx, const Location* loc, uint64_t offset) { +void Validator::CheckOffset(const Location* loc, uint64_t offset) { if (offset > UINT32_MAX) { - print_error(ctx, loc, "offset must be less than or equal to 0xffffffff"); + PrintError(loc, "offset must be less than or equal to 0xffffffff"); } } -static void check_type(Context* ctx, - const Location* loc, - Type actual, - Type expected, - const char* desc) { +void Validator::CheckType(const Location* loc, + Type actual, + Type expected, + const char* desc) { if (expected != actual) { - print_error(ctx, loc, "type mismatch at %s. got %s, expected %s", desc, - get_type_name(actual), get_type_name(expected)); + PrintError(loc, "type mismatch at %s. got %s, expected %s", desc, + get_type_name(actual), get_type_name(expected)); } } -static void check_type_index(Context* ctx, - const Location* loc, - Type actual, - Type expected, - const char* desc, - Index index, - const char* index_kind) { +void Validator::CheckTypeIndex(const Location* loc, + Type actual, + Type expected, + const char* desc, + Index index, + const char* index_kind) { if (expected != actual && expected != Type::Any && actual != Type::Any) { - print_error(ctx, loc, - "type mismatch for %s %" PRIindex " of %s. got %s, expected %s", - index_kind, index, desc, get_type_name(actual), - get_type_name(expected)); + PrintError(loc, + "type mismatch for %s %" PRIindex " of %s. got %s, expected %s", + index_kind, index, desc, get_type_name(actual), + get_type_name(expected)); } } -static void check_types(Context* ctx, - const Location* loc, - const TypeVector& actual, - const TypeVector& expected, - const char* desc, - const char* index_kind) { +void Validator::CheckTypes(const Location* loc, + const TypeVector& actual, + const TypeVector& expected, + const char* desc, + const char* index_kind) { if (actual.size() == expected.size()) { for (size_t i = 0; i < actual.size(); ++i) { - check_type_index(ctx, loc, actual[i], expected[i], desc, i, index_kind); + CheckTypeIndex(loc, actual[i], expected[i], desc, i, index_kind); } } else { - print_error(ctx, loc, "expected %" PRIzd " %ss, got %" PRIzd, - expected.size(), index_kind, actual.size()); + PrintError(loc, "expected %" PRIzd " %ss, got %" PRIzd, expected.size(), + index_kind, actual.size()); } } -static void check_const_types(Context* ctx, - const Location* loc, - const TypeVector& actual, - const ConstVector& expected, - const char* desc) { +void Validator::CheckConstTypes(const Location* loc, + const TypeVector& actual, + const ConstVector& expected, + const char* desc) { if (actual.size() == expected.size()) { for (size_t i = 0; i < actual.size(); ++i) { - check_type_index(ctx, loc, actual[i], expected[i].type, desc, i, - "result"); + CheckTypeIndex(loc, actual[i], expected[i].type, desc, i, "result"); } } else { - print_error(ctx, loc, "expected %" PRIzd " results, got %" PRIzd, - expected.size(), actual.size()); + PrintError(loc, "expected %" PRIzd " results, got %" PRIzd, expected.size(), + actual.size()); } } -static void check_const_type(Context* ctx, - const Location* loc, - Type actual, - const ConstVector& expected, - const char* desc) { +void Validator::CheckConstType(const Location* loc, + Type actual, + const ConstVector& expected, + const char* desc) { TypeVector actual_types; if (actual != Type::Void) actual_types.push_back(actual); - check_const_types(ctx, loc, actual_types, expected, desc); + CheckConstTypes(loc, actual_types, expected, desc); } -static void check_assert_return_nan_type(Context* ctx, - const Location* loc, +void Validator::CheckAssertReturnNanType(const Location* loc, Type actual, const char* desc) { - /* when using assert_return_nan, the result can be either a f32 or f64 type - * so we special case it here. */ + // When using assert_return_nan, the result can be either a f32 or f64 type + // so we special case it here. if (actual != Type::F32 && actual != Type::F64) { - print_error(ctx, loc, "type mismatch at %s. got %s, expected f32 or f64", - desc, get_type_name(actual)); + PrintError(loc, "type mismatch at %s. got %s, expected f32 or f64", desc, + get_type_name(actual)); } } -static void check_expr(Context* ctx, const Expr* expr); - -static void check_expr_list(Context* ctx, - const Location* loc, - const Expr* first) { +void Validator::CheckExprList(const Location* loc, const Expr* first) { if (first) { for (const Expr* expr = first; expr; expr = expr->next) - check_expr(ctx, expr); + CheckExpr(expr); } } -static void check_has_memory(Context* ctx, const Location* loc, Opcode opcode) { - if (ctx->current_module->memories.size() == 0) { - print_error(ctx, loc, "%s requires an imported or defined memory.", - get_opcode_name(opcode)); +void Validator::CheckHasMemory(const Location* loc, Opcode opcode) { + if (current_module_->memories.size() == 0) { + PrintError(loc, "%s requires an imported or defined memory.", + get_opcode_name(opcode)); } } -static void check_expr(Context* ctx, const Expr* expr) { - ctx->expr_loc = &expr->loc; +void Validator::CheckExpr(const Expr* expr) { + expr_loc_ = &expr->loc; switch (expr->type) { case ExprType::Binary: - ctx->typechecker.OnBinary(expr->binary.opcode); + typechecker_.OnBinary(expr->binary.opcode); break; case ExprType::Block: - ctx->typechecker.OnBlock(&expr->block->sig); - check_expr_list(ctx, &expr->loc, expr->block->first); - ctx->typechecker.OnEnd(); + typechecker_.OnBlock(&expr->block->sig); + CheckExprList(&expr->loc, expr->block->first); + typechecker_.OnEnd(); break; case ExprType::Br: - ctx->typechecker.OnBr(expr->br.var.index); + typechecker_.OnBr(expr->br.var.index); break; case ExprType::BrIf: - ctx->typechecker.OnBrIf(expr->br_if.var.index); + typechecker_.OnBrIf(expr->br_if.var.index); break; case ExprType::BrTable: { - ctx->typechecker.BeginBrTable(); - for (Var& var: *expr->br_table.targets) { - ctx->typechecker.OnBrTableTarget(var.index); + typechecker_.BeginBrTable(); + for (Var& var : *expr->br_table.targets) { + typechecker_.OnBrTableTarget(var.index); } - ctx->typechecker.OnBrTableTarget(expr->br_table.default_target.index); - ctx->typechecker.EndBrTable(); + typechecker_.OnBrTableTarget(expr->br_table.default_target.index); + typechecker_.EndBrTable(); break; } case ExprType::Call: { const Func* callee; - if (WABT_SUCCEEDED(check_func_var(ctx, &expr->call.var, &callee))) { - ctx->typechecker.OnCall(&callee->decl.sig.param_types, - &callee->decl.sig.result_types); + if (WABT_SUCCEEDED(CheckFuncVar(&expr->call.var, &callee))) { + typechecker_.OnCall(&callee->decl.sig.param_types, + &callee->decl.sig.result_types); } break; } case ExprType::CallIndirect: { const FuncType* func_type; - if (ctx->current_module->tables.size() == 0) { - print_error(ctx, &expr->loc, - "found call_indirect operator, but no table"); + if (current_module_->tables.size() == 0) { + PrintError(&expr->loc, "found call_indirect operator, but no table"); } if (WABT_SUCCEEDED( - check_func_type_var(ctx, &expr->call_indirect.var, &func_type))) { - ctx->typechecker.OnCallIndirect(&func_type->sig.param_types, - &func_type->sig.result_types); + CheckFuncTypeVar(&expr->call_indirect.var, &func_type))) { + typechecker_.OnCallIndirect(&func_type->sig.param_types, + &func_type->sig.result_types); } break; } case ExprType::Compare: - ctx->typechecker.OnCompare(expr->compare.opcode); + typechecker_.OnCompare(expr->compare.opcode); break; case ExprType::Const: - ctx->typechecker.OnConst(expr->const_.type); + typechecker_.OnConst(expr->const_.type); break; case ExprType::Convert: - ctx->typechecker.OnConvert(expr->convert.opcode); + typechecker_.OnConvert(expr->convert.opcode); break; case ExprType::Drop: - ctx->typechecker.OnDrop(); + typechecker_.OnDrop(); break; case ExprType::GetGlobal: - ctx->typechecker.OnGetGlobal( - get_global_var_type_or_any(ctx, &expr->get_global.var)); + typechecker_.OnGetGlobal(GetGlobalVarTypeOrAny(&expr->get_global.var)); break; case ExprType::GetLocal: - ctx->typechecker.OnGetLocal( - get_local_var_type_or_any(ctx, &expr->get_local.var)); + typechecker_.OnGetLocal(GetLocalVarTypeOrAny(&expr->get_local.var)); break; case ExprType::GrowMemory: - check_has_memory(ctx, &expr->loc, Opcode::GrowMemory); - ctx->typechecker.OnGrowMemory(); + CheckHasMemory(&expr->loc, Opcode::GrowMemory); + typechecker_.OnGrowMemory(); break; case ExprType::If: - ctx->typechecker.OnIf(&expr->if_.true_->sig); - check_expr_list(ctx, &expr->loc, expr->if_.true_->first); + typechecker_.OnIf(&expr->if_.true_->sig); + CheckExprList(&expr->loc, expr->if_.true_->first); if (expr->if_.false_) { - ctx->typechecker.OnElse(); - check_expr_list(ctx, &expr->loc, expr->if_.false_); + typechecker_.OnElse(); + CheckExprList(&expr->loc, expr->if_.false_); } - ctx->typechecker.OnEnd(); + typechecker_.OnEnd(); break; case ExprType::Load: - check_has_memory(ctx, &expr->loc, expr->load.opcode); - check_align(ctx, &expr->loc, expr->load.align, - get_opcode_natural_alignment(expr->load.opcode)); - check_offset(ctx, &expr->loc, expr->load.offset); - ctx->typechecker.OnLoad(expr->load.opcode); + CheckHasMemory(&expr->loc, expr->load.opcode); + CheckAlign(&expr->loc, expr->load.align, + get_opcode_natural_alignment(expr->load.opcode)); + CheckOffset(&expr->loc, expr->load.offset); + typechecker_.OnLoad(expr->load.opcode); break; case ExprType::Loop: - ctx->typechecker.OnLoop(&expr->loop->sig); - check_expr_list(ctx, &expr->loc, expr->loop->first); - ctx->typechecker.OnEnd(); + typechecker_.OnLoop(&expr->loop->sig); + CheckExprList(&expr->loc, expr->loop->first); + typechecker_.OnEnd(); break; case ExprType::CurrentMemory: - check_has_memory(ctx, &expr->loc, Opcode::CurrentMemory); - ctx->typechecker.OnCurrentMemory(); + CheckHasMemory(&expr->loc, Opcode::CurrentMemory); + typechecker_.OnCurrentMemory(); break; case ExprType::Nop: break; case ExprType::Return: - ctx->typechecker.OnReturn(); + typechecker_.OnReturn(); break; case ExprType::Select: - ctx->typechecker.OnSelect(); + typechecker_.OnSelect(); break; case ExprType::SetGlobal: - ctx->typechecker.OnSetGlobal( - get_global_var_type_or_any(ctx, &expr->set_global.var)); + typechecker_.OnSetGlobal(GetGlobalVarTypeOrAny(&expr->set_global.var)); break; case ExprType::SetLocal: - ctx->typechecker.OnSetLocal( - get_local_var_type_or_any(ctx, &expr->set_local.var)); + typechecker_.OnSetLocal(GetLocalVarTypeOrAny(&expr->set_local.var)); break; case ExprType::Store: - check_has_memory(ctx, &expr->loc, expr->store.opcode); - check_align(ctx, &expr->loc, expr->store.align, - get_opcode_natural_alignment(expr->store.opcode)); - check_offset(ctx, &expr->loc, expr->store.offset); - ctx->typechecker.OnStore(expr->store.opcode); + CheckHasMemory(&expr->loc, expr->store.opcode); + CheckAlign(&expr->loc, expr->store.align, + get_opcode_natural_alignment(expr->store.opcode)); + CheckOffset(&expr->loc, expr->store.offset); + typechecker_.OnStore(expr->store.opcode); break; case ExprType::TeeLocal: - ctx->typechecker.OnTeeLocal( - get_local_var_type_or_any(ctx, &expr->tee_local.var)); + typechecker_.OnTeeLocal(GetLocalVarTypeOrAny(&expr->tee_local.var)); break; case ExprType::Unary: - ctx->typechecker.OnUnary(expr->unary.opcode); + typechecker_.OnUnary(expr->unary.opcode); break; case ExprType::Unreachable: - ctx->typechecker.OnUnreachable(); + typechecker_.OnUnreachable(); break; } } -static void check_func_signature_matches_func_type(Context* ctx, - const Location* loc, - const FuncSignature& sig, - const FuncType* func_type) { - check_types(ctx, loc, sig.result_types, func_type->sig.result_types, - "function", "result"); - check_types(ctx, loc, sig.param_types, func_type->sig.param_types, "function", - "argument"); +void Validator::CheckFuncSignatureMatchesFuncType(const Location* loc, + const FuncSignature& sig, + const FuncType* func_type) { + CheckTypes(loc, sig.result_types, func_type->sig.result_types, "function", + "result"); + CheckTypes(loc, sig.param_types, func_type->sig.param_types, "function", + "argument"); } -static void check_func(Context* ctx, const Location* loc, const Func* func) { - ctx->current_func = func; +void Validator::CheckFunc(const Location* loc, const Func* func) { + current_func_ = func; if (get_num_results(func) > 1) { - print_error(ctx, loc, "multiple result values not currently supported."); - /* don't run any other checks, the won't test the result_type properly */ + PrintError(loc, "multiple result values not currently supported."); + // Don't run any other checks, the won't test the result_type properly. return; } if (decl_has_func_type(&func->decl)) { const FuncType* func_type; - if (WABT_SUCCEEDED( - check_func_type_var(ctx, &func->decl.type_var, &func_type))) { - check_func_signature_matches_func_type(ctx, loc, func->decl.sig, - func_type); + if (WABT_SUCCEEDED(CheckFuncTypeVar(&func->decl.type_var, &func_type))) { + CheckFuncSignatureMatchesFuncType(loc, func->decl.sig, func_type); } } - ctx->expr_loc = loc; - ctx->typechecker.BeginFunction(&func->decl.sig.result_types); - check_expr_list(ctx, loc, func->first_expr); - ctx->typechecker.EndFunction(); - ctx->current_func = nullptr; + expr_loc_ = loc; + typechecker_.BeginFunction(&func->decl.sig.result_types); + CheckExprList(loc, func->first_expr); + typechecker_.EndFunction(); + current_func_ = nullptr; } -static void print_const_expr_error(Context* ctx, - const Location* loc, - const char* desc) { - print_error(ctx, loc, - "invalid %s, must be a constant expression; either *.const or " - "get_global.", - desc); +void Validator::PrintConstExprError(const Location* loc, const char* desc) { + PrintError(loc, + "invalid %s, must be a constant expression; either *.const or " + "get_global.", + desc); } -static void check_const_init_expr(Context* ctx, - const Location* loc, - const Expr* expr, - Type expected_type, - const char* desc) { +void Validator::CheckConstInitExpr(const Location* loc, + const Expr* expr, + Type expected_type, + const char* desc) { Type type = Type::Void; if (expr) { if (expr->next) { - print_const_expr_error(ctx, loc, desc); + PrintConstExprError(loc, desc); return; } @@ -568,165 +613,149 @@ static void check_const_init_expr(Context* ctx, case ExprType::GetGlobal: { const Global* ref_global = nullptr; Index ref_global_index; - if (WABT_FAILED(check_global_var(ctx, &expr->get_global.var, - &ref_global, &ref_global_index))) { + if (WABT_FAILED(CheckGlobalVar(&expr->get_global.var, &ref_global, + &ref_global_index))) { return; } type = ref_global->type; - /* globals can only reference previously defined, internal globals */ - if (ref_global_index >= ctx->current_global_index) { - print_error(ctx, loc, - "initializer expression can only reference a previously " - "defined global"); - } else if (ref_global_index >= ctx->num_imported_globals) { - print_error( - ctx, loc, + if (ref_global_index >= num_imported_globals_) { + PrintError( + loc, "initializer expression can only reference an imported global"); } if (ref_global->mutable_) { - print_error( - ctx, loc, - "initializer expression cannot reference a mutable global"); + PrintError( + loc, "initializer expression cannot reference a mutable global"); } break; } default: - print_const_expr_error(ctx, loc, desc); + PrintConstExprError(loc, desc); return; } } - check_type(ctx, expr ? &expr->loc : loc, type, expected_type, desc); + CheckType(expr ? &expr->loc : loc, type, expected_type, desc); } -static void check_global(Context* ctx, - const Location* loc, - const Global* global) { - check_const_init_expr(ctx, loc, global->init_expr, global->type, - "global initializer expression"); +void Validator::CheckGlobal(const Location* loc, const Global* global) { + CheckConstInitExpr(loc, global->init_expr, global->type, + "global initializer expression"); } -static void check_limits(Context* ctx, - const Location* loc, - const Limits* limits, - uint64_t absolute_max, - const char* desc) { +void Validator::CheckLimits(const Location* loc, + const Limits* limits, + uint64_t absolute_max, + const char* desc) { if (limits->initial > absolute_max) { - print_error(ctx, loc, "initial %s (%" PRIu64 ") must be <= (%" PRIu64 ")", - desc, limits->initial, absolute_max); + PrintError(loc, "initial %s (%" PRIu64 ") must be <= (%" PRIu64 ")", desc, + limits->initial, absolute_max); } if (limits->has_max) { if (limits->max > absolute_max) { - print_error(ctx, loc, "max %s (%" PRIu64 ") must be <= (%" PRIu64 ")", - desc, limits->max, absolute_max); + PrintError(loc, "max %s (%" PRIu64 ") must be <= (%" PRIu64 ")", desc, + limits->max, absolute_max); } if (limits->max < limits->initial) { - print_error(ctx, loc, - "max %s (%" PRIu64 ") must be >= initial %s (%" PRIu64 ")", - desc, limits->max, desc, limits->initial); + PrintError(loc, + "max %s (%" PRIu64 ") must be >= initial %s (%" PRIu64 ")", + desc, limits->max, desc, limits->initial); } } } -static void check_table(Context* ctx, const Location* loc, const Table* table) { - if (ctx->current_table_index == 1) - print_error(ctx, loc, "only one table allowed"); - check_limits(ctx, loc, &table->elem_limits, UINT32_MAX, "elems"); +void Validator::CheckTable(const Location* loc, const Table* table) { + if (current_table_index_ == 1) + PrintError(loc, "only one table allowed"); + CheckLimits(loc, &table->elem_limits, UINT32_MAX, "elems"); } -static void check_elem_segments(Context* ctx, const Module* module) { +void Validator::CheckElemSegments(const Module* module) { for (ModuleField* field = module->first_field; field; field = field->next) { if (field->type != ModuleFieldType::ElemSegment) continue; ElemSegment* elem_segment = field->elem_segment; const Table* table; - if (!WABT_SUCCEEDED(check_table_var(ctx, &elem_segment->table_var, &table))) + if (!WABT_SUCCEEDED(CheckTableVar(&elem_segment->table_var, &table))) continue; - for (const Var& var: elem_segment->vars) { - if (!WABT_SUCCEEDED(check_func_var(ctx, &var, nullptr))) + for (const Var& var : elem_segment->vars) { + if (!WABT_SUCCEEDED(CheckFuncVar(&var, nullptr))) continue; } - check_const_init_expr(ctx, &field->loc, elem_segment->offset, Type::I32, - "elem segment offset"); + CheckConstInitExpr(&field->loc, elem_segment->offset, Type::I32, + "elem segment offset"); } } -static void check_memory(Context* ctx, - const Location* loc, - const Memory* memory) { - if (ctx->current_memory_index == 1) - print_error(ctx, loc, "only one memory block allowed"); - check_limits(ctx, loc, &memory->page_limits, WABT_MAX_PAGES, "pages"); +void Validator::CheckMemory(const Location* loc, const Memory* memory) { + if (current_memory_index_ == 1) + PrintError(loc, "only one memory block allowed"); + CheckLimits(loc, &memory->page_limits, WABT_MAX_PAGES, "pages"); } -static void check_data_segments(Context* ctx, const Module* module) { +void Validator::CheckDataSegments(const Module* module) { for (ModuleField* field = module->first_field; field; field = field->next) { if (field->type != ModuleFieldType::DataSegment) continue; DataSegment* data_segment = field->data_segment; const Memory* memory; - if (!WABT_SUCCEEDED( - check_memory_var(ctx, &data_segment->memory_var, &memory))) + if (!WABT_SUCCEEDED(CheckMemoryVar(&data_segment->memory_var, &memory))) continue; - check_const_init_expr(ctx, &field->loc, data_segment->offset, Type::I32, - "data segment offset"); + CheckConstInitExpr(&field->loc, data_segment->offset, Type::I32, + "data segment offset"); } } -static void check_import(Context* ctx, - const Location* loc, - const Import* import) { +void Validator::CheckImport(const Location* loc, const Import* import) { switch (import->kind) { case ExternalKind::Func: if (decl_has_func_type(&import->func->decl)) - check_func_type_var(ctx, &import->func->decl.type_var, nullptr); + CheckFuncTypeVar(&import->func->decl.type_var, nullptr); break; case ExternalKind::Table: - check_table(ctx, loc, import->table); - ctx->current_table_index++; + CheckTable(loc, import->table); + current_table_index_++; break; case ExternalKind::Memory: - check_memory(ctx, loc, import->memory); - ctx->current_memory_index++; + CheckMemory(loc, import->memory); + current_memory_index_++; break; case ExternalKind::Global: if (import->global->mutable_) { - print_error(ctx, loc, "mutable globals cannot be imported"); + PrintError(loc, "mutable globals cannot be imported"); } - ctx->num_imported_globals++; - ctx->current_global_index++; + num_imported_globals_++; + current_global_index_++; break; } } -static void check_export(Context* ctx, const Export* export_) { +void Validator::CheckExport(const Export* export_) { switch (export_->kind) { case ExternalKind::Func: - check_func_var(ctx, &export_->var, nullptr); + CheckFuncVar(&export_->var, nullptr); break; case ExternalKind::Table: - check_table_var(ctx, &export_->var, nullptr); + CheckTableVar(&export_->var, nullptr); break; case ExternalKind::Memory: - check_memory_var(ctx, &export_->var, nullptr); + CheckMemoryVar(&export_->var, nullptr); break; case ExternalKind::Global: { const Global* global; - if (WABT_SUCCEEDED( - check_global_var(ctx, &export_->var, &global, nullptr))) { + if (WABT_SUCCEEDED(CheckGlobalVar(&export_->var, &global, nullptr))) { if (global->mutable_) { - print_error(ctx, &export_->var.loc, - "mutable globals cannot be exported"); + PrintError(&export_->var.loc, "mutable globals cannot be exported"); } } break; @@ -734,66 +763,68 @@ static void check_export(Context* ctx, const Export* export_) { } } -static void on_duplicate_binding(const BindingHash::value_type& a, - const BindingHash::value_type& b, - void* user_data) { - Context* ctx = static_cast<Context*>(user_data); - /* choose the location that is later in the file */ +void Validator::OnDuplicateBinding(const BindingHash::value_type& a, + const BindingHash::value_type& b) { + // 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; - print_error(ctx, &loc, "redefinition of export \"%s\"", a.first.c_str()); + PrintError(&loc, "redefinition of export \"%s\"", a.first.c_str()); } -static void check_duplicate_export_bindings(Context* ctx, - const Module* module) { - module->export_bindings.find_duplicates(on_duplicate_binding, ctx); +void Validator::CheckDuplicateExportBindings(const Module* module) { + module->export_bindings.find_duplicates( + [](const BindingHash::value_type& a, const BindingHash::value_type& b, + void* user_data) { + static_cast<Validator*>(user_data)->OnDuplicateBinding(a, b); + }, + this); } -static void check_module(Context* ctx, const Module* module) { +void Validator::CheckModule(const Module* module) { bool seen_start = false; - ctx->current_module = module; - ctx->current_table_index = 0; - ctx->current_memory_index = 0; - ctx->current_global_index = 0; - ctx->num_imported_globals = 0; + current_module_ = module; + current_table_index_ = 0; + current_memory_index_ = 0; + current_global_index_ = 0; + num_imported_globals_ = 0; for (ModuleField* field = module->first_field; field; field = field->next) { switch (field->type) { case ModuleFieldType::Func: - check_func(ctx, &field->loc, field->func); + CheckFunc(&field->loc, field->func); break; case ModuleFieldType::Global: - check_global(ctx, &field->loc, field->global); - ctx->current_global_index++; + CheckGlobal(&field->loc, field->global); + current_global_index_++; break; case ModuleFieldType::Import: - check_import(ctx, &field->loc, field->import); + CheckImport(&field->loc, field->import); break; case ModuleFieldType::Export: - check_export(ctx, field->export_); + CheckExport(field->export_); break; case ModuleFieldType::Table: - check_table(ctx, &field->loc, field->table); - ctx->current_table_index++; + CheckTable(&field->loc, field->table); + current_table_index_++; break; case ModuleFieldType::ElemSegment: - /* checked below */ + // Checked below. break; case ModuleFieldType::Memory: - check_memory(ctx, &field->loc, field->memory); - ctx->current_memory_index++; + CheckMemory(&field->loc, field->memory); + current_memory_index_++; break; case ModuleFieldType::DataSegment: - /* checked below */ + // Checked below. break; case ModuleFieldType::FuncType: @@ -801,19 +832,18 @@ static void check_module(Context* ctx, const Module* module) { case ModuleFieldType::Start: { if (seen_start) { - print_error(ctx, &field->loc, "only one start function allowed"); + PrintError(&field->loc, "only one start function allowed"); } const Func* start_func = nullptr; - check_func_var(ctx, &field->start, &start_func); + CheckFuncVar(&field->start, &start_func); if (start_func) { if (get_num_params(start_func) != 0) { - print_error(ctx, &field->loc, "start function must be nullary"); + PrintError(&field->loc, "start function must be nullary"); } if (get_num_results(start_func) != 0) { - print_error(ctx, &field->loc, - "start function must not return anything"); + PrintError(&field->loc, "start function must not return anything"); } } seen_start = true; @@ -822,72 +852,70 @@ static void check_module(Context* ctx, const Module* module) { } } - check_elem_segments(ctx, module); - check_data_segments(ctx, module); - check_duplicate_export_bindings(ctx, module); + CheckElemSegments(module); + CheckDataSegments(module); + CheckDuplicateExportBindings(module); } -/* returns the result type of the invoked function, checked by the caller; - * returning nullptr means that another error occured first, so the result type - * should be ignored. */ -static const TypeVector* check_invoke(Context* ctx, const Action* action) { +// Returns the result type of the invoked function, checked by the caller; +// returning nullptr means that another error occured first, so the result type +// should be ignored. +const TypeVector* Validator::CheckInvoke(const Action* action) { const ActionInvoke* invoke = action->invoke; - const Module* module = get_module_by_var(ctx->script, &action->module_var); + const Module* module = get_module_by_var(script_, &action->module_var); if (!module) { - print_error(ctx, &action->loc, "unknown module"); + PrintError(&action->loc, "unknown module"); return nullptr; } Export* export_ = get_export_by_name(module, &action->name); if (!export_) { - print_error(ctx, &action->loc, - "unknown function export \"" PRIstringslice "\"", - WABT_PRINTF_STRING_SLICE_ARG(action->name)); + PrintError(&action->loc, "unknown function export \"" PRIstringslice "\"", + WABT_PRINTF_STRING_SLICE_ARG(action->name)); return nullptr; } Func* func = get_func_by_var(module, &export_->var); if (!func) { - /* this error will have already been reported, just skip it */ + // This error will have already been reported, just skip it. return nullptr; } size_t actual_args = invoke->args.size(); size_t expected_args = get_num_params(func); if (expected_args != actual_args) { - print_error(ctx, &action->loc, "too %s parameters to function. got %" PRIzd - ", expected %" PRIzd, - actual_args > expected_args ? "many" : "few", actual_args, - expected_args); + PrintError(&action->loc, "too %s parameters to function. got %" PRIzd + ", expected %" PRIzd, + actual_args > expected_args ? "many" : "few", actual_args, + expected_args); return nullptr; } for (size_t i = 0; i < actual_args; ++i) { const Const* const_ = &invoke->args[i]; - check_type_index(ctx, &const_->loc, const_->type, get_param_type(func, i), - "invoke", i, "argument"); + CheckTypeIndex(&const_->loc, const_->type, get_param_type(func, i), + "invoke", i, "argument"); } return &func->decl.sig.result_types; } -static Result check_get(Context* ctx, const Action* action, Type* out_type) { - const Module* module = get_module_by_var(ctx->script, &action->module_var); +Result Validator::CheckGet(const Action* action, Type* out_type) { + const Module* module = get_module_by_var(script_, &action->module_var); if (!module) { - print_error(ctx, &action->loc, "unknown module"); + PrintError(&action->loc, "unknown module"); return Result::Error; } Export* export_ = get_export_by_name(module, &action->name); if (!export_) { - print_error(ctx, &action->loc, - "unknown global export \"" PRIstringslice "\"", - WABT_PRINTF_STRING_SLICE_ARG(action->name)); + PrintError(&action->loc, "unknown global export \"" PRIstringslice "\"", + WABT_PRINTF_STRING_SLICE_ARG(action->name)); return Result::Error; } Global* global = get_global_by_var(module, &export_->var); if (!global) { - /* this error will have already been reported, just skip it */ + // This error will have already been reported, just skip it. return Result::Error; } @@ -895,60 +923,59 @@ static Result check_get(Context* ctx, const Action* action, Type* out_type) { return Result::Ok; } -static ActionResult check_action(Context* ctx, const Action* action) { +Validator::ActionResult Validator::CheckAction(const Action* action) { ActionResult result; WABT_ZERO_MEMORY(result); switch (action->type) { case ActionType::Invoke: - result.types = check_invoke(ctx, action); + result.types = CheckInvoke(action); result.kind = - result.types ? ActionResultKind::Types : ActionResultKind::Error; + result.types ? ActionResult::Kind::Types : ActionResult::Kind::Error; break; case ActionType::Get: - if (WABT_SUCCEEDED(check_get(ctx, action, &result.type))) - result.kind = ActionResultKind::Type; + if (WABT_SUCCEEDED(CheckGet(action, &result.type))) + result.kind = ActionResult::Kind::Type; else - result.kind = ActionResultKind::Error; + result.kind = ActionResult::Kind::Error; break; } return result; } -static void check_assert_return_nan_command(Context* ctx, - const Action* action) { - ActionResult result = check_action(ctx, action); +void Validator::CheckAssertReturnNanCommand(const Action* action) { + ActionResult result = CheckAction(action); - /* a valid result type will either be f32 or f64; convert a TYPES result - * into a TYPE result, so it is easier to check below. Type::Any is - * used to specify a type that should not be checked (because an earlier - * error occurred). */ - if (result.kind == ActionResultKind::Types) { + // A valid result type will either be f32 or f64; convert a Types result into + // a Type result, so it is easier to check below. Type::Any is used to + // specify a type that should not be checked (because an earlier error + // occurred). + if (result.kind == ActionResult::Kind::Types) { if (result.types->size() == 1) { - result.kind = ActionResultKind::Type; + result.kind = ActionResult::Kind::Type; result.type = (*result.types)[0]; } else { - print_error(ctx, &action->loc, "expected 1 result, got %" PRIzd, - result.types->size()); + PrintError(&action->loc, "expected 1 result, got %" PRIzd, + result.types->size()); result.type = Type::Any; } } - if (result.kind == ActionResultKind::Type && result.type != Type::Any) - check_assert_return_nan_type(ctx, &action->loc, result.type, "action"); + if (result.kind == ActionResult::Kind::Type && result.type != Type::Any) + CheckAssertReturnNanType(&action->loc, result.type, "action"); } -static void check_command(Context* ctx, const Command* command) { +void Validator::CheckCommand(const Command* command) { switch (command->type) { case CommandType::Module: - check_module(ctx, command->module); + CheckModule(command->module); break; case CommandType::Action: - /* ignore result type */ - check_action(ctx, command->action); + // Ignore result type. + CheckAction(command->action); break; case CommandType::Register: @@ -957,56 +984,60 @@ static void check_command(Context* ctx, const Command* command) { case CommandType::AssertInvalidNonBinary: case CommandType::AssertUnlinkable: case CommandType::AssertUninstantiable: - /* ignore */ + // Ignore. break; case CommandType::AssertReturn: { const Action* action = command->assert_return.action; - ActionResult result = check_action(ctx, action); + ActionResult result = CheckAction(action); switch (result.kind) { - case ActionResultKind::Types: - check_const_types(ctx, &action->loc, *result.types, - *command->assert_return.expected, "action"); + case ActionResult::Kind::Types: + CheckConstTypes(&action->loc, *result.types, + *command->assert_return.expected, "action"); break; - case ActionResultKind::Type: - check_const_type(ctx, &action->loc, result.type, - *command->assert_return.expected, "action"); + case ActionResult::Kind::Type: + CheckConstType(&action->loc, result.type, + *command->assert_return.expected, "action"); break; - case ActionResultKind::Error: - /* error occurred, don't do any further checks */ + case ActionResult::Kind::Error: + // Error occurred, don't do any further checks. break; } break; } case CommandType::AssertReturnCanonicalNan: - check_assert_return_nan_command( - ctx, command->assert_return_canonical_nan.action); + CheckAssertReturnNanCommand(command->assert_return_canonical_nan.action); break; case CommandType::AssertReturnArithmeticNan: - check_assert_return_nan_command( - ctx, command->assert_return_arithmetic_nan.action); + CheckAssertReturnNanCommand(command->assert_return_arithmetic_nan.action); break; case CommandType::AssertTrap: case CommandType::AssertExhaustion: - /* ignore result type */ - check_action(ctx, command->assert_trap.action); + // ignore result type. + CheckAction(command->assert_trap.action); break; } } +Result Validator::CheckScript(const Script* script) { + for (const std::unique_ptr<Command>& command : script->commands) + CheckCommand(command.get()); + return result_; +} + +} // namespace + Result validate_script(WastLexer* lexer, - const struct Script* script, + const Script* script, SourceErrorHandler* error_handler) { - Context ctx(error_handler, lexer, script); + Validator validator(error_handler, lexer, script); - for (const std::unique_ptr<Command>& command : script->commands) - check_command(&ctx, command.get()); - return ctx.result; + return validator.CheckScript(script); } } // namespace wabt |