diff options
Diffstat (limited to 'src/tools/spectest-interp.cc')
-rw-r--r-- | src/tools/spectest-interp.cc | 1309 |
1 files changed, 724 insertions, 585 deletions
diff --git a/src/tools/spectest-interp.cc b/src/tools/spectest-interp.cc index e7a7c1ba..e6577cc3 100644 --- a/src/tools/spectest-interp.cc +++ b/src/tools/spectest-interp.cc @@ -54,7 +54,7 @@ enum class RunVerbosity { }; static const char s_description[] = -R"( read a Spectest JSON file, and run its tests in the interpreter. + R"( read a Spectest JSON file, and run its tests in the interpreter. examples: # parse test.json and run the spec tests @@ -91,221 +91,120 @@ static void ParseOptions(int argc, char** argv) { parser.Parse(argc, argv); } -enum class ModuleType { - Text, - Binary, -}; - -static string_view GetDirname(string_view path) { - // Strip everything after and including the last slash (or backslash), e.g.: - // - // s = "foo/bar/baz", => "foo/bar" - // s = "/usr/local/include/stdio.h", => "/usr/local/include" - // s = "foo.bar", => "" - // s = "some\windows\directory", => "some\windows" - size_t last_slash = path.find_last_of('/'); - size_t last_backslash = path.find_last_of('\\'); - if (last_slash == string_view::npos) - last_slash = 0; - if (last_backslash == string_view::npos) - last_backslash = 0; - - return path.substr(0, std::max(last_slash, last_backslash)); -} - -static interpreter::Result GetGlobalExportByName(Thread* thread, - interpreter::Module* module, - string_view name, - TypedValues* out_results) { - interpreter::Export* export_ = module->GetExport(name); - if (!export_) - return interpreter::Result::UnknownExport; - if (export_->kind != ExternalKind::Global) - return interpreter::Result::ExportKindMismatch; - - interpreter::Global* global = thread->env()->GetGlobal(export_->index); - out_results->clear(); - out_results->push_back(global->typed_value); - return interpreter::Result::Ok; -} +namespace spectest { -static wabt::Result ReadModule(const char* module_filename, - Environment* env, - ErrorHandler* error_handler, - DefinedModule** out_module) { - wabt::Result result; - std::vector<uint8_t> file_data; +class Command; +typedef std::unique_ptr<Command> CommandPtr; +typedef std::vector<CommandPtr> CommandPtrVector; - *out_module = nullptr; - - result = ReadFile(module_filename, &file_data); - if (Succeeded(result)) { - const bool kReadDebugNames = true; - const bool kStopOnFirstError = true; - ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, - kStopOnFirstError); - result = ReadBinaryInterpreter(env, DataOrNull(file_data), file_data.size(), - &options, error_handler, out_module); - - if (Succeeded(result)) { - if (s_verbose) - env->DisassembleModule(s_stdout_stream.get(), *out_module); - } - } - return result; -} - -static interpreter::Result DefaultHostCallback( - const HostFunc* func, - const interpreter::FuncSignature* sig, - Index num_args, - TypedValue* args, - Index num_results, - TypedValue* out_results, - void* user_data) { - memset(out_results, 0, sizeof(TypedValue) * num_results); - for (Index i = 0; i < num_results; ++i) - out_results[i].type = sig->result_types[i]; +class Script { + public: + std::string filename; + CommandPtrVector commands; +}; - TypedValues vec_args(args, args + num_args); - TypedValues vec_results(out_results, out_results + num_results); +class Command { + public: + WABT_DISALLOW_COPY_AND_ASSIGN(Command); + Command() = delete; + virtual ~Command() = default; - printf("called host "); - WriteCall(s_stdout_stream.get(), func->module_name, func->field_name, - vec_args, vec_results, interpreter::Result::Ok); - return interpreter::Result::Ok; -} + CommandType type; + uint32_t line = 0; -#define PRIimport "\"" PRIstringview "." PRIstringview "\"" -#define PRINTF_IMPORT_ARG(x) \ - WABT_PRINTF_STRING_VIEW_ARG((x).module_name) \ - , WABT_PRINTF_STRING_VIEW_ARG((x).field_name) + protected: + explicit Command(CommandType type) : type(type) {} +}; -class SpectestHostImportDelegate : public HostImportDelegate { +template <CommandType TypeEnum> +class CommandMixin : public Command { public: - wabt::Result ImportFunc(interpreter::FuncImport* import, - interpreter::Func* func, - interpreter::FuncSignature* func_sig, - const ErrorCallback& callback) override { - if (import->field_name == "print") { - cast<HostFunc>(func)->callback = DefaultHostCallback; - return wabt::Result::Ok; - } else { - PrintError(callback, "unknown host function import " PRIimport, - PRINTF_IMPORT_ARG(*import)); - return wabt::Result::Error; - } - } - - wabt::Result ImportTable(interpreter::TableImport* import, - interpreter::Table* table, - const ErrorCallback& callback) override { - if (import->field_name == "table") { - table->limits.has_max = true; - table->limits.initial = 10; - table->limits.max = 20; - return wabt::Result::Ok; - } else { - PrintError(callback, "unknown host table import " PRIimport, - PRINTF_IMPORT_ARG(*import)); - return wabt::Result::Error; - } - } + static bool classof(const Command* cmd) { return cmd->type == TypeEnum; } + CommandMixin() : Command(TypeEnum) {} +}; - wabt::Result ImportMemory(interpreter::MemoryImport* import, - interpreter::Memory* memory, - const ErrorCallback& callback) override { - if (import->field_name == "memory") { - memory->page_limits.has_max = true; - memory->page_limits.initial = 1; - memory->page_limits.max = 2; - memory->data.resize(memory->page_limits.initial * WABT_MAX_PAGES); - return wabt::Result::Ok; - } else { - PrintError(callback, "unknown host memory import " PRIimport, - PRINTF_IMPORT_ARG(*import)); - return wabt::Result::Error; - } - } +enum class ModuleType { + Text, + Binary, +}; - wabt::Result ImportGlobal(interpreter::GlobalImport* import, - interpreter::Global* global, - const ErrorCallback& callback) override { - if (import->field_name == "global") { - switch (global->typed_value.type) { - case Type::I32: - global->typed_value.value.i32 = 666; - break; +class ModuleCommand : public CommandMixin<CommandType::Module> { + public: + ModuleType module = ModuleType::Binary; + std::string filename; + std::string name; +}; - case Type::F32: { - float value = 666.6f; - memcpy(&global->typed_value.value.f32_bits, &value, sizeof(value)); - break; - } +class Action { + public: + ActionType type = ActionType::Invoke; + std::string module_name; + std::string field_name; + TypedValues args; +}; - case Type::I64: - global->typed_value.value.i64 = 666; - break; +template <CommandType TypeEnum> +class ActionCommandBase : public CommandMixin<TypeEnum> { + public: + Action action; +}; - case Type::F64: { - double value = 666.6; - memcpy(&global->typed_value.value.f64_bits, &value, sizeof(value)); - break; - } +typedef ActionCommandBase<CommandType::Action> ActionCommand; +typedef ActionCommandBase<CommandType::AssertReturnCanonicalNan> + AssertReturnCanonicalNanCommand; +typedef ActionCommandBase<CommandType::AssertReturnArithmeticNan> + AssertReturnArithmeticNanCommand; - default: - PrintError(callback, "bad type for host global import " PRIimport, - PRINTF_IMPORT_ARG(*import)); - return wabt::Result::Error; - } +class RegisterCommand : public CommandMixin<CommandType::Register> { + public: + std::string as; + std::string name; +}; - return wabt::Result::Ok; - } else { - PrintError(callback, "unknown host global import " PRIimport, - PRINTF_IMPORT_ARG(*import)); - return wabt::Result::Error; - } - } +class AssertReturnCommand : public CommandMixin<CommandType::AssertReturn> { + public: + Action action; + TypedValues expected; +}; - private: - void PrintError(const ErrorCallback& callback, const char* format, ...) { - WABT_SNPRINTF_ALLOCA(buffer, length, format); - callback(buffer); - } +template <CommandType TypeEnum> +class AssertTrapCommandBase : public CommandMixin<TypeEnum> { + public: + Action action; + std::string text; }; -static void InitEnvironment(Environment* env) { - HostModule* host_module = env->AppendHostModule("spectest"); - host_module->import_delegate.reset(new SpectestHostImportDelegate()); -} +typedef AssertTrapCommandBase<CommandType::AssertTrap> AssertTrapCommand; +typedef AssertTrapCommandBase<CommandType::AssertExhaustion> + AssertExhaustionCommand; -enum class ActionType { - Invoke, - Get, +template <CommandType TypeEnum> +class AssertModuleCommand : public CommandMixin<TypeEnum> { + public: + ModuleType type = ModuleType::Binary; + std::string filename; + std::string text; }; -struct Action { - ::ActionType type = ::ActionType::Invoke; - std::string module_name; - std::string field_name; - TypedValues args; -}; +typedef AssertModuleCommand<CommandType::AssertMalformed> + AssertMalformedCommand; +typedef AssertModuleCommand<CommandType::AssertInvalid> AssertInvalidCommand; +typedef AssertModuleCommand<CommandType::AssertUnlinkable> + AssertUnlinkableCommand; +typedef AssertModuleCommand<CommandType::AssertUninstantiable> + AssertUninstantiableCommand; // An extremely simple JSON parser that only knows how to parse the expected // format from wat2wasm. -class SpecJSONParser { +class JSONParser { public: - SpecJSONParser() : thread_(&env_, s_thread_options) {} + JSONParser() {} wabt::Result ReadFile(const char* spec_json_filename); - wabt::Result ParseCommands(); - - int passed() const { return passed_; } - int total() const { return total_; } + wabt::Result ParseScript(Script* out_script); private: - void WABT_PRINTF_FORMAT(2, 3) PrintParseError(const char* format, ...); - void WABT_PRINTF_FORMAT(2, 3) PrintCommandError(const char* format, ...); + void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...); void PutbackChar(); int ReadChar(); @@ -317,65 +216,24 @@ class SpecJSONParser { wabt::Result ParseString(std::string* out_string); wabt::Result ParseKeyStringValue(const char* key, std::string* out_string); wabt::Result ParseOptNameStringValue(std::string* out_string); - wabt::Result ParseLine(); + wabt::Result ParseLine(uint32_t* out_line_number); wabt::Result ParseTypeObject(Type* out_type); wabt::Result ParseTypeVector(TypeVector* out_types); wabt::Result ParseConst(TypedValue* out_value); wabt::Result ParseConstVector(TypedValues* out_values); - wabt::Result ParseAction(::Action* out_action); + wabt::Result ParseAction(Action* out_action); wabt::Result ParseModuleType(ModuleType* out_type); std::string CreateModulePath(string_view filename); - - wabt::Result OnModuleCommand(string_view filename, string_view name); - wabt::Result RunAction(::Action* action, - interpreter::Result* out_iresult, - TypedValues* out_results, - RunVerbosity verbose); - wabt::Result OnActionCommand(::Action* action); - wabt::Result ReadInvalidTextModule(const char* module_filename, - Environment* env, - ErrorHandler* error_handler); - wabt::Result ReadInvalidModule(const char* module_filename, - Environment* env, - ModuleType module_type, - const char* desc); - wabt::Result OnAssertMalformedCommand(string_view filename, - string_view text, - ModuleType module_type); - wabt::Result OnRegisterCommand(string_view name, string_view as); - wabt::Result OnAssertUnlinkableCommand(string_view filename, - string_view text, - ModuleType module_type); - wabt::Result OnAssertInvalidCommand(string_view filename, - string_view text, - ModuleType module_type); - wabt::Result OnAssertUninstantiableCommand(string_view filename, - string_view text, - ModuleType module_type); - wabt::Result OnAssertReturnCommand(::Action* action, - const TypedValues& expected); - wabt::Result OnAssertReturnNanCommand(::Action* action, bool canonical); - wabt::Result OnAssertTrapCommand(::Action* action, string_view text); - wabt::Result OnAssertExhaustionCommand(::Action* action); - wabt::Result ParseCommand(); - - Environment env_; - Thread thread_; - DefinedModule* last_module_ = nullptr; + wabt::Result ParseFilename(std::string* out_filename); + wabt::Result ParseCommand(CommandPtr* out_command); // Parsing info. std::vector<uint8_t> json_data_; - std::string source_filename_; size_t json_offset_ = 0; Location loc_; Location prev_loc_; bool has_prev_loc_ = false; - uint32_t command_line_number_ = 0; - - // Test info. - int passed_ = 0; - int total_ = 0; }; #define EXPECT(x) CHECK_RESULT(Expect(x)) @@ -383,36 +241,28 @@ class SpecJSONParser { #define PARSE_KEY_STRING_VALUE(key, value) \ CHECK_RESULT(ParseKeyStringValue(key, value)) -wabt::Result SpecJSONParser::ReadFile(const char* spec_json_filename) { +wabt::Result JSONParser::ReadFile(const char* spec_json_filename) { loc_.filename = spec_json_filename; loc_.line = 1; loc_.first_column = 1; - InitEnvironment(&env_); return wabt::ReadFile(spec_json_filename, &json_data_); } -void SpecJSONParser::PrintParseError(const char* format, ...) { +void JSONParser::PrintError(const char* format, ...) { WABT_SNPRINTF_ALLOCA(buffer, length, format); fprintf(stderr, "%s:%d:%d: %s\n", loc_.filename, loc_.line, loc_.first_column, buffer); } -void SpecJSONParser::PrintCommandError(const char* format, ...) { - WABT_SNPRINTF_ALLOCA(buffer, length, format); - printf(PRIstringview ":%u: %s\n", - WABT_PRINTF_STRING_VIEW_ARG(source_filename_), command_line_number_, - buffer); -} - -void SpecJSONParser::PutbackChar() { +void JSONParser::PutbackChar() { assert(has_prev_loc_); json_offset_--; loc_ = prev_loc_; has_prev_loc_ = false; } -int SpecJSONParser::ReadChar() { +int JSONParser::ReadChar() { if (json_offset_ >= json_data_.size()) return -1; prev_loc_ = loc_; @@ -427,7 +277,7 @@ int SpecJSONParser::ReadChar() { return c; } -void SpecJSONParser::SkipWhitespace() { +void JSONParser::SkipWhitespace() { while (1) { switch (ReadChar()) { case -1: @@ -446,7 +296,7 @@ void SpecJSONParser::SkipWhitespace() { } } -bool SpecJSONParser::Match(const char* s) { +bool JSONParser::Match(const char* s) { SkipWhitespace(); Location start_loc = loc_; size_t start_offset = json_offset_; @@ -462,16 +312,16 @@ bool SpecJSONParser::Match(const char* s) { } } -wabt::Result SpecJSONParser::Expect(const char* s) { +wabt::Result JSONParser::Expect(const char* s) { if (Match(s)) { return wabt::Result::Ok; } else { - PrintParseError("expected %s", s); + PrintError("expected %s", s); return wabt::Result::Error; } } -wabt::Result SpecJSONParser::ExpectKey(const char* key) { +wabt::Result JSONParser::ExpectKey(const char* key) { size_t keylen = strlen(key); size_t quoted_len = keylen + 2 + 1; char* quoted = static_cast<char*>(alloca(quoted_len)); @@ -481,7 +331,7 @@ wabt::Result SpecJSONParser::ExpectKey(const char* key) { return wabt::Result::Ok; } -wabt::Result SpecJSONParser::ParseUint32(uint32_t* out_int) { +wabt::Result JSONParser::ParseUint32(uint32_t* out_int) { uint32_t result = 0; SkipWhitespace(); while (1) { @@ -490,7 +340,7 @@ wabt::Result SpecJSONParser::ParseUint32(uint32_t* out_int) { uint32_t last_result = result; result = result * 10 + static_cast<uint32_t>(c - '0'); if (result < last_result) { - PrintParseError("uint32 overflow"); + PrintError("uint32 overflow"); return wabt::Result::Error; } } else { @@ -502,12 +352,12 @@ wabt::Result SpecJSONParser::ParseUint32(uint32_t* out_int) { return wabt::Result::Ok; } -wabt::Result SpecJSONParser::ParseString(std::string* out_string) { +wabt::Result JSONParser::ParseString(std::string* out_string) { out_string->clear(); SkipWhitespace(); if (ReadChar() != '"') { - PrintParseError("expected string"); + PrintError("expected string"); return wabt::Result::Error; } @@ -519,7 +369,7 @@ wabt::Result SpecJSONParser::ParseString(std::string* out_string) { /* The only escape supported is \uxxxx. */ c = ReadChar(); if (c != 'u') { - PrintParseError("expected escape: \\uxxxx"); + PrintError("expected escape: \\uxxxx"); return wabt::Result::Error; } uint16_t code = 0; @@ -533,7 +383,7 @@ wabt::Result SpecJSONParser::ParseString(std::string* out_string) { } else if (c >= 'A' && c <= 'F') { cval = c - 'A' + 10; } else { - PrintParseError("expected hex char"); + PrintError("expected hex char"); return wabt::Result::Error; } code = (code << 4) + cval; @@ -542,7 +392,7 @@ wabt::Result SpecJSONParser::ParseString(std::string* out_string) { if (code < 256) { *out_string += code; } else { - PrintParseError("only escape codes < 256 allowed, got %u\n", code); + PrintError("only escape codes < 256 allowed, got %u\n", code); } } else { *out_string += c; @@ -551,14 +401,14 @@ wabt::Result SpecJSONParser::ParseString(std::string* out_string) { return wabt::Result::Ok; } -wabt::Result SpecJSONParser::ParseKeyStringValue(const char* key, - std::string* out_string) { +wabt::Result JSONParser::ParseKeyStringValue(const char* key, + std::string* out_string) { out_string->clear(); EXPECT_KEY(key); return ParseString(out_string); } -wabt::Result SpecJSONParser::ParseOptNameStringValue(std::string* out_string) { +wabt::Result JSONParser::ParseOptNameStringValue(std::string* out_string) { out_string->clear(); if (Match("\"name\"")) { EXPECT(":"); @@ -568,13 +418,13 @@ wabt::Result SpecJSONParser::ParseOptNameStringValue(std::string* out_string) { return wabt::Result::Ok; } -wabt::Result SpecJSONParser::ParseLine() { +wabt::Result JSONParser::ParseLine(uint32_t* out_line_number) { EXPECT_KEY("line"); - CHECK_RESULT(ParseUint32(&command_line_number_)); + CHECK_RESULT(ParseUint32(out_line_number)); return wabt::Result::Ok; } -wabt::Result SpecJSONParser::ParseTypeObject(Type* out_type) { +wabt::Result JSONParser::ParseTypeObject(Type* out_type) { std::string type_str; EXPECT("{"); PARSE_KEY_STRING_VALUE("type", &type_str); @@ -593,13 +443,12 @@ wabt::Result SpecJSONParser::ParseTypeObject(Type* out_type) { *out_type = Type::F64; return wabt::Result::Ok; } else { - PrintParseError("unknown type: \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(type_str)); + PrintError("unknown type: \"%s\"", type_str.c_str()); return wabt::Result::Error; } } -wabt::Result SpecJSONParser::ParseTypeVector(TypeVector* out_types) { +wabt::Result JSONParser::ParseTypeVector(TypeVector* out_types) { out_types->clear(); EXPECT("["); bool first = true; @@ -614,7 +463,7 @@ wabt::Result SpecJSONParser::ParseTypeVector(TypeVector* out_types) { return wabt::Result::Ok; } -wabt::Result SpecJSONParser::ParseConst(TypedValue* out_value) { +wabt::Result JSONParser::ParseConst(TypedValue* out_value) { std::string type_str; std::string value_str; EXPECT("{"); @@ -655,13 +504,12 @@ wabt::Result SpecJSONParser::ParseConst(TypedValue* out_value) { out_value->value.f64_bits = value_bits; return wabt::Result::Ok; } else { - PrintParseError("unknown type: \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(type_str)); + PrintError("unknown type: \"%s\"", type_str.c_str()); return wabt::Result::Error; } } -wabt::Result SpecJSONParser::ParseConstVector(TypedValues* out_values) { +wabt::Result JSONParser::ParseConstVector(TypedValues* out_values) { out_values->clear(); EXPECT("["); bool first = true; @@ -676,15 +524,15 @@ wabt::Result SpecJSONParser::ParseConstVector(TypedValues* out_values) { return wabt::Result::Ok; } -wabt::Result SpecJSONParser::ParseAction(::Action* out_action) { +wabt::Result JSONParser::ParseAction(Action* out_action) { EXPECT_KEY("action"); EXPECT("{"); EXPECT_KEY("type"); if (Match("\"invoke\"")) { - out_action->type = ::ActionType::Invoke; + out_action->type = ActionType::Invoke; } else { EXPECT("\"get\""); - out_action->type = ::ActionType::Get; + out_action->type = ActionType::Get; } EXPECT(","); if (Match("\"module\"")) { @@ -693,7 +541,7 @@ wabt::Result SpecJSONParser::ParseAction(::Action* out_action) { EXPECT(","); } PARSE_KEY_STRING_VALUE("field", &out_action->field_name); - if (out_action->type == ::ActionType::Invoke) { + if (out_action->type == ActionType::Invoke) { EXPECT(","); EXPECT_KEY("args"); CHECK_RESULT(ParseConstVector(&out_action->args)); @@ -702,7 +550,7 @@ wabt::Result SpecJSONParser::ParseAction(::Action* out_action) { return wabt::Result::Ok; } -wabt::Result SpecJSONParser::ParseModuleType(ModuleType* out_type) { +wabt::Result JSONParser::ParseModuleType(ModuleType* out_type) { std::string module_type_str; PARSE_KEY_STRING_VALUE("module_type", &module_type_str); @@ -713,13 +561,29 @@ wabt::Result SpecJSONParser::ParseModuleType(ModuleType* out_type) { *out_type = ModuleType::Binary; return wabt::Result::Ok; } else { - PrintParseError("unknown module type: \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(module_type_str)); + PrintError("unknown module type: \"%s\"", module_type_str.c_str()); return wabt::Result::Error; } } -std::string SpecJSONParser::CreateModulePath(string_view filename) { +static string_view GetDirname(string_view path) { + // Strip everything after and including the last slash (or backslash), e.g.: + // + // s = "foo/bar/baz", => "foo/bar" + // s = "/usr/local/include/stdio.h", => "/usr/local/include" + // s = "foo.bar", => "" + // s = "some\windows\directory", => "some\windows" + size_t last_slash = path.find_last_of('/'); + size_t last_backslash = path.find_last_of('\\'); + if (last_slash == string_view::npos) + last_slash = 0; + if (last_backslash == string_view::npos) + last_backslash = 0; + + return path.substr(0, std::max(last_slash, last_backslash)); +} + +std::string JSONParser::CreateModulePath(string_view filename) { const char* spec_json_filename = loc_.filename; string_view dirname = GetDirname(spec_json_filename); std::string path; @@ -736,39 +600,422 @@ std::string SpecJSONParser::CreateModulePath(string_view filename) { return path; } -wabt::Result SpecJSONParser::OnModuleCommand(string_view filename, - string_view name) { - std::string path = CreateModulePath(filename); - Environment::MarkPoint mark = env_.Mark(); - ErrorHandlerFile error_handler(Location::Type::Binary); - wabt::Result result = - ReadModule(path.c_str(), &env_, &error_handler, &last_module_); +wabt::Result JSONParser::ParseFilename(std::string* out_filename) { + PARSE_KEY_STRING_VALUE("filename", out_filename); + *out_filename = CreateModulePath(*out_filename); + return wabt::Result::Ok; +} - if (Failed(result)) { - env_.ResetToMarkPoint(mark); - PrintCommandError("error reading module: \"%s\"", path.c_str()); +wabt::Result JSONParser::ParseCommand(CommandPtr* out_command) { + EXPECT("{"); + EXPECT_KEY("type"); + if (Match("\"module\"")) { + auto command = MakeUnique<ModuleCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseOptNameStringValue(&command->name)); + CHECK_RESULT(ParseFilename(&command->filename)); + *out_command = std::move(command); + } else if (Match("\"action\"")) { + auto command = MakeUnique<ActionCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseAction(&command->action)); + *out_command = std::move(command); + } else if (Match("\"register\"")) { + auto command = MakeUnique<RegisterCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseOptNameStringValue(&command->name)); + PARSE_KEY_STRING_VALUE("as", &command->as); + *out_command = std::move(command); + } else if (Match("\"assert_malformed\"")) { + auto command = MakeUnique<AssertMalformedCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseFilename(&command->filename)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + EXPECT(","); + CHECK_RESULT(ParseModuleType(&command->type)); + *out_command = std::move(command); + } else if (Match("\"assert_invalid\"")) { + auto command = MakeUnique<AssertInvalidCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseFilename(&command->filename)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + EXPECT(","); + CHECK_RESULT(ParseModuleType(&command->type)); + *out_command = std::move(command); + } else if (Match("\"assert_unlinkable\"")) { + auto command = MakeUnique<AssertUnlinkableCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseFilename(&command->filename)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + EXPECT(","); + CHECK_RESULT(ParseModuleType(&command->type)); + *out_command = std::move(command); + } else if (Match("\"assert_uninstantiable\"")) { + auto command = MakeUnique<AssertUninstantiableCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseFilename(&command->filename)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + EXPECT(","); + CHECK_RESULT(ParseModuleType(&command->type)); + *out_command = std::move(command); + } else if (Match("\"assert_return\"")) { + auto command = MakeUnique<AssertReturnCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseAction(&command->action)); + EXPECT(","); + EXPECT_KEY("expected"); + CHECK_RESULT(ParseConstVector(&command->expected)); + *out_command = std::move(command); + } else if (Match("\"assert_return_canonical_nan\"")) { + auto command = MakeUnique<AssertReturnCanonicalNanCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseAction(&command->action)); + EXPECT(","); + // Not needed for wabt-interp, but useful for other parsers. + EXPECT_KEY("expected"); + TypeVector expected; + CHECK_RESULT(ParseTypeVector(&expected)); + *out_command = std::move(command); + } else if (Match("\"assert_return_arithmetic_nan\"")) { + auto command = MakeUnique<AssertReturnArithmeticNanCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseAction(&command->action)); + EXPECT(","); + // Not needed for wabt-interp, but useful for other parsers. + EXPECT_KEY("expected"); + TypeVector expected; + CHECK_RESULT(ParseTypeVector(&expected)); + *out_command = std::move(command); + } else if (Match("\"assert_trap\"")) { + auto command = MakeUnique<AssertTrapCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseAction(&command->action)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &command->text); + *out_command = std::move(command); + } else if (Match("\"assert_exhaustion\"")) { + auto command = MakeUnique<AssertExhaustionCommand>(); + EXPECT(","); + CHECK_RESULT(ParseLine(&command->line)); + EXPECT(","); + CHECK_RESULT(ParseAction(&command->action)); + *out_command = std::move(command); + } else { + PrintError("unknown command type"); return wabt::Result::Error; } + EXPECT("}"); + return wabt::Result::Ok; +} - interpreter::Result iresult = thread_.RunStartFunction(last_module_); - if (iresult != interpreter::Result::Ok) { - env_.ResetToMarkPoint(mark); - WriteResult(s_stdout_stream.get(), "error running start function", iresult); - return wabt::Result::Error; +wabt::Result JSONParser::ParseScript(Script* out_script) { + EXPECT("{"); + PARSE_KEY_STRING_VALUE("source_filename", &out_script->filename); + EXPECT(","); + EXPECT_KEY("commands"); + EXPECT("["); + bool first = true; + while (!Match("]")) { + CommandPtr command; + if (!first) + EXPECT(","); + CHECK_RESULT(ParseCommand(&command)); + out_script->commands.push_back(std::move(command)); + first = false; } + EXPECT("}"); + return wabt::Result::Ok; +} + +class CommandRunner { + public: + CommandRunner(); + + wabt::Result Run(const Script& script); - if (!name.empty()) { - last_module_->name = name.to_string(); - env_.EmplaceModuleBinding(name.to_string(), - Binding(env_.GetModuleCount() - 1)); + int passed() const { return passed_; } + int total() const { return total_; } + + private: + void WABT_PRINTF_FORMAT(3, 4) + PrintError(uint32_t line_number, const char* format, ...); + wabt::Result RunAction(int line_number, + const Action* action, + interpreter::Result* out_iresult, + TypedValues* out_results, + RunVerbosity verbose); + + wabt::Result OnModuleCommand(const ModuleCommand*); + wabt::Result OnActionCommand(const ActionCommand*); + wabt::Result OnRegisterCommand(const RegisterCommand*); + wabt::Result OnAssertMalformedCommand(const AssertMalformedCommand*); + wabt::Result OnAssertUnlinkableCommand(const AssertUnlinkableCommand*); + wabt::Result OnAssertInvalidCommand(const AssertInvalidCommand*); + wabt::Result OnAssertUninstantiableCommand( + const AssertUninstantiableCommand*); + wabt::Result OnAssertReturnCommand(const AssertReturnCommand*); + template <typename NanCommand> + wabt::Result OnAssertReturnNanCommand(const NanCommand*); + wabt::Result OnAssertTrapCommand(const AssertTrapCommand*); + wabt::Result OnAssertExhaustionCommand(const AssertExhaustionCommand*); + + wabt::Result ReadInvalidTextModule(const char* module_filename, + Environment* env, + ErrorHandler* error_handler); + wabt::Result ReadInvalidModule(int line_number, + const char* module_filename, + Environment* env, + ModuleType module_type, + const char* desc); + + Environment env_; + Thread thread_; + DefinedModule* last_module_ = nullptr; + int passed_ = 0; + int total_ = 0; + + std::string source_filename_; +}; + +static interpreter::Result DefaultHostCallback( + const HostFunc* func, + const interpreter::FuncSignature* sig, + Index num_args, + TypedValue* args, + Index num_results, + TypedValue* out_results, + void* user_data) { + memset(out_results, 0, sizeof(TypedValue) * num_results); + for (Index i = 0; i < num_results; ++i) + out_results[i].type = sig->result_types[i]; + + TypedValues vec_args(args, args + num_args); + TypedValues vec_results(out_results, out_results + num_results); + + printf("called host "); + WriteCall(s_stdout_stream.get(), func->module_name, func->field_name, + vec_args, vec_results, interpreter::Result::Ok); + return interpreter::Result::Ok; +} + +#define PRIimport "\"%s.%s\"" +#define PRINTF_IMPORT_ARG(x) ((x).module_name.c_str()), ((x).field_name.c_str()) + +class SpectestHostImportDelegate : public HostImportDelegate { + public: + wabt::Result ImportFunc(interpreter::FuncImport* import, + interpreter::Func* func, + interpreter::FuncSignature* func_sig, + const ErrorCallback& callback) override { + if (import->field_name == "print") { + cast<HostFunc>(func)->callback = DefaultHostCallback; + return wabt::Result::Ok; + } else { + PrintError(callback, "unknown host function import " PRIimport, + PRINTF_IMPORT_ARG(*import)); + return wabt::Result::Error; + } + } + + wabt::Result ImportTable(interpreter::TableImport* import, + interpreter::Table* table, + const ErrorCallback& callback) override { + if (import->field_name == "table") { + table->limits.has_max = true; + table->limits.initial = 10; + table->limits.max = 20; + return wabt::Result::Ok; + } else { + PrintError(callback, "unknown host table import " PRIimport, + PRINTF_IMPORT_ARG(*import)); + return wabt::Result::Error; + } + } + + wabt::Result ImportMemory(interpreter::MemoryImport* import, + interpreter::Memory* memory, + const ErrorCallback& callback) override { + if (import->field_name == "memory") { + memory->page_limits.has_max = true; + memory->page_limits.initial = 1; + memory->page_limits.max = 2; + memory->data.resize(memory->page_limits.initial * WABT_MAX_PAGES); + return wabt::Result::Ok; + } else { + PrintError(callback, "unknown host memory import " PRIimport, + PRINTF_IMPORT_ARG(*import)); + return wabt::Result::Error; + } + } + + wabt::Result ImportGlobal(interpreter::GlobalImport* import, + interpreter::Global* global, + const ErrorCallback& callback) override { + if (import->field_name == "global") { + switch (global->typed_value.type) { + case Type::I32: + global->typed_value.value.i32 = 666; + break; + + case Type::F32: { + float value = 666.6f; + memcpy(&global->typed_value.value.f32_bits, &value, sizeof(value)); + break; + } + + case Type::I64: + global->typed_value.value.i64 = 666; + break; + + case Type::F64: { + double value = 666.6; + memcpy(&global->typed_value.value.f64_bits, &value, sizeof(value)); + break; + } + + default: + PrintError(callback, "bad type for host global import " PRIimport, + PRINTF_IMPORT_ARG(*import)); + return wabt::Result::Error; + } + + return wabt::Result::Ok; + } else { + PrintError(callback, "unknown host global import " PRIimport, + PRINTF_IMPORT_ARG(*import)); + return wabt::Result::Error; + } + } + + private: + void PrintError(const ErrorCallback& callback, const char* format, ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + callback(buffer); + } +}; + +static void InitEnvironment(Environment* env) { + HostModule* host_module = env->AppendHostModule("spectest"); + host_module->import_delegate.reset(new SpectestHostImportDelegate()); +} + +CommandRunner::CommandRunner() : thread_(&env_, s_thread_options) { + InitEnvironment(&env_); +} + +wabt::Result CommandRunner::Run(const Script& script) { + source_filename_ = script.filename; + + for (const CommandPtr& command : script.commands) { + switch (command->type) { + case CommandType::Module: + OnModuleCommand(cast<ModuleCommand>(command.get())); + break; + + case CommandType::Action: + OnActionCommand(cast<ActionCommand>(command.get())); + break; + + case CommandType::Register: + OnRegisterCommand(cast<RegisterCommand>(command.get())); + break; + + case CommandType::AssertMalformed: + OnAssertMalformedCommand(cast<AssertMalformedCommand>(command.get())); + break; + + case CommandType::AssertInvalid: + OnAssertInvalidCommand(cast<AssertInvalidCommand>(command.get())); + break; + + case CommandType::AssertUnlinkable: + OnAssertUnlinkableCommand(cast<AssertUnlinkableCommand>(command.get())); + break; + + case CommandType::AssertUninstantiable: + OnAssertUninstantiableCommand( + cast<AssertUninstantiableCommand>(command.get())); + break; + + case CommandType::AssertReturn: + OnAssertReturnCommand(cast<AssertReturnCommand>(command.get())); + break; + + case CommandType::AssertReturnCanonicalNan: + OnAssertReturnNanCommand( + cast<AssertReturnCanonicalNanCommand>(command.get())); + break; + + case CommandType::AssertReturnArithmeticNan: + OnAssertReturnNanCommand( + cast<AssertReturnArithmeticNanCommand>(command.get())); + break; + + case CommandType::AssertTrap: + OnAssertTrapCommand(cast<AssertTrapCommand>(command.get())); + break; + + case CommandType::AssertExhaustion: + OnAssertExhaustionCommand(cast<AssertExhaustionCommand>(command.get())); + break; + } } + return wabt::Result::Ok; } -wabt::Result SpecJSONParser::RunAction(::Action* action, - interpreter::Result* out_iresult, - TypedValues* out_results, - RunVerbosity verbose) { +void CommandRunner::PrintError(uint32_t line_number, const char* format, ...) { + WABT_SNPRINTF_ALLOCA(buffer, length, format); + printf("%s:%u: %s\n", source_filename_.c_str(), line_number, buffer); +} + +static interpreter::Result GetGlobalExportByName(Thread* thread, + interpreter::Module* module, + string_view name, + TypedValues* out_results) { + interpreter::Export* export_ = module->GetExport(name); + if (!export_) + return interpreter::Result::UnknownExport; + if (export_->kind != ExternalKind::Global) + return interpreter::Result::ExportKindMismatch; + + interpreter::Global* global = thread->env()->GetGlobal(export_->index); + out_results->clear(); + out_results->push_back(global->typed_value); + return interpreter::Result::Ok; +} + +wabt::Result CommandRunner::RunAction(int line_number, + const Action* action, + interpreter::Result* out_iresult, + TypedValues* out_results, + RunVerbosity verbose) { out_results->clear(); interpreter::Module* module; @@ -780,7 +1027,7 @@ wabt::Result SpecJSONParser::RunAction(::Action* action, assert(module); switch (action->type) { - case ::ActionType::Invoke: + case ActionType::Invoke: *out_iresult = thread_.RunExportByName(module, action->field_name, action->args, out_results); if (verbose == RunVerbosity::Verbose) { @@ -789,45 +1036,25 @@ wabt::Result SpecJSONParser::RunAction(::Action* action, } return wabt::Result::Ok; - case ::ActionType::Get: { + case ActionType::Get: { *out_iresult = GetGlobalExportByName(&thread_, module, action->field_name, out_results); return wabt::Result::Ok; } default: - PrintCommandError("invalid action type %d", - static_cast<int>(action->type)); + PrintError(line_number, "invalid action type %d", + static_cast<int>(action->type)); return wabt::Result::Error; } } -wabt::Result SpecJSONParser::OnActionCommand(::Action* action) { - TypedValues results; - interpreter::Result iresult; - - total_++; - wabt::Result result = - RunAction(action, &iresult, &results, RunVerbosity::Verbose); - if (Succeeded(result)) { - if (iresult == interpreter::Result::Ok) { - passed_++; - } else { - PrintCommandError("unexpected trap: %s", ResultToString(iresult)); - result = wabt::Result::Error; - } - } - - return result; -} - -wabt::Result SpecJSONParser::ReadInvalidTextModule( - const char* module_filename, - Environment* env, - ErrorHandler* error_handler) { +wabt::Result CommandRunner::ReadInvalidTextModule(const char* module_filename, + Environment* env, + ErrorHandler* error_handler) { std::unique_ptr<WastLexer> lexer = WastLexer::CreateFileLexer(module_filename); - std::unique_ptr<Script> script; + std::unique_ptr<::Script> script; wabt::Result result = ParseWastScript(lexer.get(), &script, error_handler); if (Succeeded(result)) { wabt::Module* module = script->GetFirstModule(); @@ -840,14 +1067,39 @@ wabt::Result SpecJSONParser::ReadInvalidTextModule( return result; } -wabt::Result SpecJSONParser::ReadInvalidModule(const char* module_filename, - Environment* env, - ModuleType module_type, - const char* desc) { - std::string header = - StringPrintf(PRIstringview ":%d: %s passed", - WABT_PRINTF_STRING_VIEW_ARG(source_filename_), - command_line_number_, desc); +static wabt::Result ReadModule(const char* module_filename, + Environment* env, + ErrorHandler* error_handler, + DefinedModule** out_module) { + wabt::Result result; + std::vector<uint8_t> file_data; + + *out_module = nullptr; + + result = ReadFile(module_filename, &file_data); + if (Succeeded(result)) { + const bool kReadDebugNames = true; + const bool kStopOnFirstError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, + kStopOnFirstError); + result = ReadBinaryInterpreter(env, DataOrNull(file_data), file_data.size(), + &options, error_handler, out_module); + + if (Succeeded(result)) { + if (s_verbose) + env->DisassembleModule(s_stdout_stream.get(), *out_module); + } + } + return result; +} + +wabt::Result CommandRunner::ReadInvalidModule(int line_number, + const char* module_filename, + Environment* env, + ModuleType module_type, + const char* desc) { + std::string header = StringPrintf( + "%s:%d: %s passed", source_filename_.c_str(), line_number, desc); switch (module_type) { case ModuleType::Text: { @@ -867,111 +1119,155 @@ wabt::Result SpecJSONParser::ReadInvalidModule(const char* module_filename, WABT_UNREACHABLE; } -wabt::Result SpecJSONParser::OnAssertMalformedCommand(string_view filename, - string_view text, - ModuleType module_type) { +wabt::Result CommandRunner::OnModuleCommand(const ModuleCommand* command) { + Environment::MarkPoint mark = env_.Mark(); + ErrorHandlerFile error_handler(Location::Type::Binary); + wabt::Result result = ReadModule(command->filename.c_str(), &env_, + &error_handler, &last_module_); + + if (Failed(result)) { + env_.ResetToMarkPoint(mark); + PrintError(command->line, "error reading module: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; + } + + interpreter::Result iresult = thread_.RunStartFunction(last_module_); + if (iresult != interpreter::Result::Ok) { + env_.ResetToMarkPoint(mark); + WriteResult(s_stdout_stream.get(), "error running start function", iresult); + return wabt::Result::Error; + } + + if (!command->name.empty()) { + last_module_->name = command->name; + env_.EmplaceModuleBinding(command->name, + Binding(env_.GetModuleCount() - 1)); + } + return wabt::Result::Ok; +} + +wabt::Result CommandRunner::OnActionCommand(const ActionCommand* command) { + TypedValues results; + interpreter::Result iresult; + + total_++; + wabt::Result result = RunAction(command->line, &command->action, &iresult, + &results, RunVerbosity::Verbose); + if (Succeeded(result)) { + if (iresult == interpreter::Result::Ok) { + passed_++; + } else { + PrintError(command->line, "unexpected trap: %s", ResultToString(iresult)); + result = wabt::Result::Error; + } + } + + return result; +} + +wabt::Result CommandRunner::OnAssertMalformedCommand( + const AssertMalformedCommand* command) { Environment env; InitEnvironment(&env); total_++; - std::string path = CreateModulePath(filename); wabt::Result result = - ReadInvalidModule(path.c_str(), &env, module_type, "assert_malformed"); + ReadInvalidModule(command->line, command->filename.c_str(), &env, + command->type, "assert_malformed"); if (Failed(result)) { passed_++; result = wabt::Result::Ok; } else { - PrintCommandError("expected module to be malformed: \"%s\"", path.c_str()); + PrintError(command->line, "expected module to be malformed: \"%s\"", + command->filename.c_str()); result = wabt::Result::Error; } return result; } -wabt::Result SpecJSONParser::OnRegisterCommand(string_view name, - string_view as) { +wabt::Result CommandRunner::OnRegisterCommand(const RegisterCommand* command) { Index module_index; - if (!name.empty()) { - module_index = env_.FindModuleIndex(name); + if (!command->name.empty()) { + module_index = env_.FindModuleIndex(command->name); } else { module_index = env_.GetLastModuleIndex(); } if (module_index == kInvalidIndex) { - PrintCommandError("unknown module in register"); + PrintError(command->line, "unknown module in register"); return wabt::Result::Error; } - env_.EmplaceRegisteredModuleBinding(as.to_string(), Binding(module_index)); + env_.EmplaceRegisteredModuleBinding(command->as, Binding(module_index)); return wabt::Result::Ok; } -wabt::Result SpecJSONParser::OnAssertUnlinkableCommand(string_view filename, - string_view text, - ModuleType module_type) { +wabt::Result CommandRunner::OnAssertUnlinkableCommand( + const AssertUnlinkableCommand* command) { total_++; - std::string path = CreateModulePath(filename); Environment::MarkPoint mark = env_.Mark(); wabt::Result result = - ReadInvalidModule(path.c_str(), &env_, module_type, "assert_unlinkable"); + ReadInvalidModule(command->line, command->filename.c_str(), &env_, + command->type, "assert_unlinkable"); env_.ResetToMarkPoint(mark); if (Failed(result)) { passed_++; result = wabt::Result::Ok; } else { - PrintCommandError("expected module to be unlinkable: \"%s\"", path.c_str()); + PrintError(command->line, "expected module to be unlinkable: \"%s\"", + command->filename.c_str()); result = wabt::Result::Error; } return result; } -wabt::Result SpecJSONParser::OnAssertInvalidCommand(string_view filename, - string_view text, - ModuleType module_type) { +wabt::Result CommandRunner::OnAssertInvalidCommand( + const AssertInvalidCommand* command) { Environment env; InitEnvironment(&env); total_++; - std::string path = CreateModulePath(filename); wabt::Result result = - ReadInvalidModule(path.c_str(), &env, module_type, "assert_invalid"); + ReadInvalidModule(command->line, command->filename.c_str(), &env, + command->type, "assert_invalid"); if (Failed(result)) { passed_++; result = wabt::Result::Ok; } else { - PrintCommandError("expected module to be invalid: \"%s\"", path.c_str()); + PrintError(command->line, "expected module to be invalid: \"%s\"", + command->filename.c_str()); result = wabt::Result::Error; } return result; } -wabt::Result SpecJSONParser::OnAssertUninstantiableCommand( - string_view filename, - string_view text, - ModuleType module_type) { +wabt::Result CommandRunner::OnAssertUninstantiableCommand( + const AssertUninstantiableCommand* command) { ErrorHandlerFile error_handler(Location::Type::Binary); total_++; - std::string path = CreateModulePath(filename); DefinedModule* module; Environment::MarkPoint mark = env_.Mark(); wabt::Result result = - ReadModule(path.c_str(), &env_, &error_handler, &module); + ReadModule(command->filename.c_str(), &env_, &error_handler, &module); if (Succeeded(result)) { interpreter::Result iresult = thread_.RunStartFunction(module); if (iresult == interpreter::Result::Ok) { - PrintCommandError("expected error running start function: \"%s\"", - path.c_str()); + PrintError(command->line, "expected error running start function: \"%s\"", + command->filename.c_str()); result = wabt::Result::Error; } else { passed_++; result = wabt::Result::Ok; } } else { - PrintCommandError("error reading module: \"%s\"", path.c_str()); + PrintError(command->line, "error reading module: \"%s\"", + command->filename.c_str()); result = wabt::Result::Error; } @@ -997,39 +1293,39 @@ static bool TypedValuesAreEqual(const TypedValue* tv1, const TypedValue* tv2) { } } -wabt::Result SpecJSONParser::OnAssertReturnCommand( - ::Action* action, - const TypedValues& expected) { +wabt::Result CommandRunner::OnAssertReturnCommand( + const AssertReturnCommand* command) { TypedValues results; interpreter::Result iresult; total_++; - wabt::Result result = - RunAction(action, &iresult, &results, RunVerbosity::Quiet); + wabt::Result result = RunAction(command->line, &command->action, &iresult, + &results, RunVerbosity::Quiet); if (Succeeded(result)) { if (iresult == interpreter::Result::Ok) { - if (results.size() == expected.size()) { + if (results.size() == command->expected.size()) { for (size_t i = 0; i < results.size(); ++i) { - const TypedValue* expected_tv = &expected[i]; + const TypedValue* expected_tv = &command->expected[i]; const TypedValue* actual_tv = &results[i]; if (!TypedValuesAreEqual(expected_tv, actual_tv)) { - PrintCommandError("mismatch in result %" PRIzd - " of assert_return: expected %s, got %s", - i, TypedValueToString(*expected_tv).c_str(), - TypedValueToString(*actual_tv).c_str()); + PrintError(command->line, + "mismatch in result %" PRIzd + " of assert_return: expected %s, got %s", + i, TypedValueToString(*expected_tv).c_str(), + TypedValueToString(*actual_tv).c_str()); result = wabt::Result::Error; } } } else { - PrintCommandError( - "result length mismatch in assert_return: expected %" PRIzd - ", got %" PRIzd, - expected.size(), results.size()); + PrintError(command->line, + "result length mismatch in assert_return: expected %" PRIzd + ", got %" PRIzd, + command->expected.size(), results.size()); result = wabt::Result::Error; } } else { - PrintCommandError("unexpected trap: %s", ResultToString(iresult)); + PrintError(command->line, "unexpected trap: %s", ResultToString(iresult)); result = wabt::Result::Error; } } @@ -1040,53 +1336,58 @@ wabt::Result SpecJSONParser::OnAssertReturnCommand( return result; } -wabt::Result SpecJSONParser::OnAssertReturnNanCommand(::Action* action, - bool canonical) { +template <typename NanCommand> +wabt::Result CommandRunner::OnAssertReturnNanCommand( + const NanCommand* command) { + const bool is_canonical = + command->type == CommandType::AssertReturnCanonicalNan; TypedValues results; interpreter::Result iresult; total_++; - wabt::Result result = - RunAction(action, &iresult, &results, RunVerbosity::Quiet); + wabt::Result result = RunAction(command->line, &command->action, &iresult, + &results, RunVerbosity::Quiet); if (Succeeded(result)) { if (iresult == interpreter::Result::Ok) { if (results.size() != 1) { - PrintCommandError("expected one result, got %" PRIzd, results.size()); + PrintError(command->line, "expected one result, got %" PRIzd, + results.size()); result = wabt::Result::Error; } const TypedValue& actual = results[0]; switch (actual.type) { case Type::F32: { - bool is_nan = canonical ? IsCanonicalNan(actual.value.f32_bits) - : IsArithmeticNan(actual.value.f32_bits); + bool is_nan = is_canonical ? IsCanonicalNan(actual.value.f32_bits) + : IsArithmeticNan(actual.value.f32_bits); if (!is_nan) { - PrintCommandError("expected result to be nan, got %s", - TypedValueToString(actual).c_str()); + PrintError(command->line, "expected result to be nan, got %s", + TypedValueToString(actual).c_str()); result = wabt::Result::Error; } break; } case Type::F64: { - bool is_nan = canonical ? IsCanonicalNan(actual.value.f64_bits) - : IsArithmeticNan(actual.value.f64_bits); + bool is_nan = is_canonical ? IsCanonicalNan(actual.value.f64_bits) + : IsArithmeticNan(actual.value.f64_bits); if (!is_nan) { - PrintCommandError("expected result to be nan, got %s", - TypedValueToString(actual).c_str()); + PrintError(command->line, "expected result to be nan, got %s", + TypedValueToString(actual).c_str()); result = wabt::Result::Error; } break; } default: - PrintCommandError("expected result type to be f32 or f64, got %s", - GetTypeName(actual.type)); + PrintError(command->line, + "expected result type to be f32 or f64, got %s", + GetTypeName(actual.type)); result = wabt::Result::Error; break; } } else { - PrintCommandError("unexpected trap: %s", ResultToString(iresult)); + PrintError(command->line, "unexpected trap: %s", ResultToString(iresult)); result = wabt::Result::Error; } } @@ -1097,20 +1398,19 @@ wabt::Result SpecJSONParser::OnAssertReturnNanCommand(::Action* action, return wabt::Result::Ok; } -wabt::Result SpecJSONParser::OnAssertTrapCommand(::Action* action, - string_view text) { +wabt::Result CommandRunner::OnAssertTrapCommand( + const AssertTrapCommand* command) { TypedValues results; interpreter::Result iresult; total_++; - wabt::Result result = - RunAction(action, &iresult, &results, RunVerbosity::Quiet); + wabt::Result result = RunAction(command->line, &command->action, &iresult, + &results, RunVerbosity::Quiet); if (Succeeded(result)) { if (iresult != interpreter::Result::Ok) { passed_++; } else { - PrintCommandError("expected trap: \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(text)); + PrintError(command->line, "expected trap: \"%s\"", command->text.c_str()); result = wabt::Result::Error; } } @@ -1118,19 +1418,20 @@ wabt::Result SpecJSONParser::OnAssertTrapCommand(::Action* action, return result; } -wabt::Result SpecJSONParser::OnAssertExhaustionCommand(::Action* action) { +wabt::Result CommandRunner::OnAssertExhaustionCommand( + const AssertExhaustionCommand* command) { TypedValues results; interpreter::Result iresult; total_++; - wabt::Result result = - RunAction(action, &iresult, &results, RunVerbosity::Quiet); + wabt::Result result = RunAction(command->line, &command->action, &iresult, + &results, RunVerbosity::Quiet); if (Succeeded(result)) { if (iresult == interpreter::Result::TrapCallStackExhausted || iresult == interpreter::Result::TrapValueStackExhausted) { passed_++; } else { - PrintCommandError("expected call stack exhaustion"); + PrintError(command->line, "expected call stack exhaustion"); result = wabt::Result::Error; } } @@ -1138,184 +1439,22 @@ wabt::Result SpecJSONParser::OnAssertExhaustionCommand(::Action* action) { return result; } -wabt::Result SpecJSONParser::ParseCommand() { - EXPECT("{"); - EXPECT_KEY("type"); - if (Match("\"module\"")) { - std::string name; - std::string filename; - - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - CHECK_RESULT(ParseOptNameStringValue(&name)); - PARSE_KEY_STRING_VALUE("filename", &filename); - OnModuleCommand(filename, name); - } else if (Match("\"action\"")) { - ::Action action; - - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - CHECK_RESULT(ParseAction(&action)); - OnActionCommand(&action); - } else if (Match("\"register\"")) { - std::string as; - std::string name; - - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - CHECK_RESULT(ParseOptNameStringValue(&name)); - PARSE_KEY_STRING_VALUE("as", &as); - OnRegisterCommand(name, as); - } else if (Match("\"assert_malformed\"")) { - std::string filename; - std::string text; - ModuleType module_type; - - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - PARSE_KEY_STRING_VALUE("filename", &filename); - EXPECT(","); - PARSE_KEY_STRING_VALUE("text", &text); - EXPECT(","); - CHECK_RESULT(ParseModuleType(&module_type)); - OnAssertMalformedCommand(filename, text, module_type); - } else if (Match("\"assert_invalid\"")) { - std::string filename; - std::string text; - ModuleType module_type; - - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - PARSE_KEY_STRING_VALUE("filename", &filename); - EXPECT(","); - PARSE_KEY_STRING_VALUE("text", &text); - EXPECT(","); - CHECK_RESULT(ParseModuleType(&module_type)); - OnAssertInvalidCommand(filename, text, module_type); - } else if (Match("\"assert_unlinkable\"")) { - std::string filename; - std::string text; - ModuleType module_type; - - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - PARSE_KEY_STRING_VALUE("filename", &filename); - EXPECT(","); - PARSE_KEY_STRING_VALUE("text", &text); - EXPECT(","); - CHECK_RESULT(ParseModuleType(&module_type)); - OnAssertUnlinkableCommand(filename, text, module_type); - } else if (Match("\"assert_uninstantiable\"")) { - std::string filename; - std::string text; - ModuleType module_type; - - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - PARSE_KEY_STRING_VALUE("filename", &filename); - EXPECT(","); - PARSE_KEY_STRING_VALUE("text", &text); - EXPECT(","); - CHECK_RESULT(ParseModuleType(&module_type)); - OnAssertUninstantiableCommand(filename, text, module_type); - } else if (Match("\"assert_return\"")) { - ::Action action; - TypedValues expected; - - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - CHECK_RESULT(ParseAction(&action)); - EXPECT(","); - EXPECT_KEY("expected"); - CHECK_RESULT(ParseConstVector(&expected)); - OnAssertReturnCommand(&action, expected); - } else if (Match("\"assert_return_canonical_nan\"")) { - ::Action action; - TypeVector expected; - - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - CHECK_RESULT(ParseAction(&action)); - EXPECT(","); - /* Not needed for wabt-interp, but useful for other parsers. */ - EXPECT_KEY("expected"); - CHECK_RESULT(ParseTypeVector(&expected)); - OnAssertReturnNanCommand(&action, true); - } else if (Match("\"assert_return_arithmetic_nan\"")) { - ::Action action; - TypeVector expected; - - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - CHECK_RESULT(ParseAction(&action)); - EXPECT(","); - /* Not needed for wabt-interp, but useful for other parsers. */ - EXPECT_KEY("expected"); - CHECK_RESULT(ParseTypeVector(&expected)); - OnAssertReturnNanCommand(&action, false); - } else if (Match("\"assert_trap\"")) { - ::Action action; - std::string text; +static wabt::Result ReadAndRunSpecJSON(const char* spec_json_filename) { + JSONParser parser; + CHECK_RESULT(parser.ReadFile(spec_json_filename)); - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - CHECK_RESULT(ParseAction(&action)); - EXPECT(","); - PARSE_KEY_STRING_VALUE("text", &text); - OnAssertTrapCommand(&action, text); - } else if (Match("\"assert_exhaustion\"")) { - ::Action action; - std::string text; + Script script; + CHECK_RESULT(parser.ParseScript(&script)); - EXPECT(","); - CHECK_RESULT(ParseLine()); - EXPECT(","); - CHECK_RESULT(ParseAction(&action)); - OnAssertExhaustionCommand(&action); - } else { - PrintCommandError("unknown command type"); - return wabt::Result::Error; - } - EXPECT("}"); - return wabt::Result::Ok; -} + CommandRunner runner; + wabt::Result result = runner.Run(script); -wabt::Result SpecJSONParser::ParseCommands() { - EXPECT("{"); - PARSE_KEY_STRING_VALUE("source_filename", &source_filename_); - EXPECT(","); - EXPECT_KEY("commands"); - EXPECT("["); - bool first = true; - while (!Match("]")) { - if (!first) - EXPECT(","); - CHECK_RESULT(ParseCommand()); - first = false; - } - EXPECT("}"); - return wabt::Result::Ok; -} - -static wabt::Result ReadAndRunSpecJSON(const char* spec_json_filename) { - SpecJSONParser parser; - CHECK_RESULT(parser.ReadFile(spec_json_filename)); - wabt::Result result = parser.ParseCommands(); - printf("%d/%d tests passed.\n", parser.passed(), parser.total()); + printf("%d/%d tests passed.\n", runner.passed(), runner.total()); return result; } +} // namespace spectest + int ProgramMain(int argc, char** argv) { InitStdio(); s_stdout_stream = FileStream::CreateStdout(); @@ -1323,7 +1462,7 @@ int ProgramMain(int argc, char** argv) { ParseOptions(argc, argv); wabt::Result result; - result = ReadAndRunSpecJSON(s_infile); + result = spectest::ReadAndRunSpecJSON(s_infile); return result != wabt::Result::Ok; } |