diff options
Diffstat (limited to 'src/tools/wasm-interp.cc')
-rw-r--r-- | src/tools/wasm-interp.cc | 1230 |
1 files changed, 37 insertions, 1193 deletions
diff --git a/src/tools/wasm-interp.cc b/src/tools/wasm-interp.cc index 1c4cf958..00ec38ae 100644 --- a/src/tools/wasm-interp.cc +++ b/src/tools/wasm-interp.cc @@ -48,8 +48,8 @@ static int s_verbose; static const char* s_infile; static Thread::Options s_thread_options; static bool s_trace; -static bool s_spec; static bool s_run_all_exports; +static bool s_host_print; static Features s_features; static std::unique_ptr<FileStream> s_log_stream; @@ -74,9 +74,6 @@ examples: # parse test.wasm, run the exported functions and trace the output $ wasm-interp test.wasm --run-all-exports --trace - # parse test.json and run the spec tests - $ wasm-interp test.json --spec - # parse test.wasm and run all its exported functions, setting the # value stack size to 100 elements $ wasm-interp test.wasm -V 100 --run-all-exports @@ -104,42 +101,21 @@ static void ParseOptions(int argc, char** argv) { s_thread_options.call_stack_size = atoi(argument.c_str()); }); parser.AddOption('t', "trace", "Trace execution", []() { s_trace = true; }); - parser.AddOption("spec", "Run spec tests (input file should be .json)", - []() { s_spec = true; }); parser.AddOption( "run-all-exports", "Run all the exported functions, in order. Useful for testing", []() { s_run_all_exports = true; }); + parser.AddOption("host-print", + "Include an importable function named \"host.print\" for " + "printing to stdout", + []() { s_host_print = true; }); parser.AddArgument("filename", OptionParser::ArgumentCount::One, [](const char* argument) { s_infile = argument; }); parser.Parse(argc, argv); - - if (s_spec && s_run_all_exports) - WABT_FATAL("--spec and --run-all-exports are incompatible.\n"); } -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)); -} +// TODO(binji): Share these helper functions w/ spectest-interp as well. /* Not sure, but 100 chars is probably safe */ #define MAX_TYPED_VALUE_CHARS 100 @@ -250,37 +226,6 @@ static interpreter::Result RunExport(Thread* thread, return RunFunction(thread, export_->index, args, out_results); } -static interpreter::Result RunExportByName(Thread* thread, - interpreter::Module* module, - string_view name, - const std::vector<TypedValue>& args, - std::vector<TypedValue>* out_results, - RunVerbosity verbose) { - interpreter::Export* export_ = module->GetExport(name); - if (!export_) - return interpreter::Result::UnknownExport; - if (export_->kind != ExternalKind::Func) - return interpreter::Result::ExportKindMismatch; - return RunExport(thread, export_, args, out_results); -} - -static interpreter::Result GetGlobalExportByName( - Thread* thread, - interpreter::Module* module, - string_view name, - std::vector<TypedValue>* 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; -} - static void RunAllExports(interpreter::Module* module, Thread* thread, RunVerbosity verbose) { @@ -320,40 +265,19 @@ static wabt::Result ReadModule(const char* module_filename, 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]; - - std::vector<TypedValue> vec_args(args, args + num_args); - std::vector<TypedValue> vec_results(out_results, out_results + num_results); - - printf("called host "); - PrintCall(func->module_name, func->field_name, vec_args, vec_results, - interpreter::Result::Ok); - return interpreter::Result::Ok; -} - #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) -class SpectestHostImportDelegate : public HostImportDelegate { +class WasmInterpHostImportDelegate : 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; + cast<HostFunc>(func)->callback = PrintCallback; return wabt::Result::Ok; } else { PrintError(callback, "unknown host function import " PRIimport, @@ -365,74 +289,43 @@ class SpectestHostImportDelegate : public HostImportDelegate { 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; - } + 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; - } + 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; - } + return wabt::Result::Error; } private: + static interpreter::Result PrintCallback( + 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]; + + std::vector<TypedValue> vec_args(args, args + num_args); + std::vector<TypedValue> vec_results(out_results, out_results + num_results); + + printf("called host "); + PrintCall(func->module_name, func->field_name, vec_args, vec_results, + interpreter::Result::Ok); + return interpreter::Result::Ok; + } + void PrintError(const ErrorCallback& callback, const char* format, ...) { WABT_SNPRINTF_ALLOCA(buffer, length, format); callback(buffer); @@ -440,8 +333,10 @@ class SpectestHostImportDelegate : public HostImportDelegate { }; static void InitEnvironment(Environment* env) { - HostModule* host_module = env->AppendHostModule("spectest"); - host_module->import_delegate.reset(new SpectestHostImportDelegate()); + if (s_host_print) { + HostModule* host_module = env->AppendHostModule("host"); + host_module->import_delegate.reset(new WasmInterpHostImportDelegate()); + } } static wabt::Result ReadAndRunModule(const char* module_filename) { @@ -465,1064 +360,13 @@ static wabt::Result ReadAndRunModule(const char* module_filename) { return result; } -enum class ActionType { - Invoke, - Get, -}; - -struct Action { - ::ActionType type = ::ActionType::Invoke; - std::string module_name; - std::string field_name; - std::vector<TypedValue> args; -}; - -// An extremely simple JSON parser that only knows how to parse the expected -// format from wat2wasm. -class SpecJSONParser { - public: - SpecJSONParser() : thread_(&env_, s_thread_options) {} - - wabt::Result ReadFile(const char* spec_json_filename); - wabt::Result ParseCommands(); - - int passed() const { return passed_; } - int total() const { return total_; } - - private: - void WABT_PRINTF_FORMAT(2, 3) PrintParseError(const char* format, ...); - void WABT_PRINTF_FORMAT(2, 3) PrintCommandError(const char* format, ...); - - void PutbackChar(); - int ReadChar(); - void SkipWhitespace(); - bool Match(const char* s); - wabt::Result Expect(const char* s); - wabt::Result ExpectKey(const char* key); - wabt::Result ParseUint32(uint32_t* out_int); - 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 ParseTypeObject(Type* out_type); - wabt::Result ParseTypeVector(TypeVector* out_types); - wabt::Result ParseConst(TypedValue* out_value); - wabt::Result ParseConstVector(std::vector<TypedValue>* out_values); - 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, - std::vector<TypedValue>* 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 std::vector<TypedValue>& 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; - - // 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)) -#define EXPECT_KEY(x) CHECK_RESULT(ExpectKey(x)) -#define PARSE_KEY_STRING_VALUE(key, value) \ - CHECK_RESULT(ParseKeyStringValue(key, value)) - -wabt::Result SpecJSONParser::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, ...) { - 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() { - assert(has_prev_loc_); - json_offset_--; - loc_ = prev_loc_; - has_prev_loc_ = false; -} - -int SpecJSONParser::ReadChar() { - if (json_offset_ >= json_data_.size()) - return -1; - prev_loc_ = loc_; - char c = json_data_[json_offset_++]; - if (c == '\n') { - loc_.line++; - loc_.first_column = 1; - } else { - loc_.first_column++; - } - has_prev_loc_ = true; - return c; -} - -void SpecJSONParser::SkipWhitespace() { - while (1) { - switch (ReadChar()) { - case -1: - return; - - case ' ': - case '\t': - case '\n': - case '\r': - break; - - default: - PutbackChar(); - return; - } - } -} - -bool SpecJSONParser::Match(const char* s) { - SkipWhitespace(); - Location start_loc = loc_; - size_t start_offset = json_offset_; - while (*s && *s == ReadChar()) - s++; - - if (*s == 0) { - return true; - } else { - json_offset_ = start_offset; - loc_ = start_loc; - return false; - } -} - -wabt::Result SpecJSONParser::Expect(const char* s) { - if (Match(s)) { - return wabt::Result::Ok; - } else { - PrintParseError("expected %s", s); - return wabt::Result::Error; - } -} - -wabt::Result SpecJSONParser::ExpectKey(const char* key) { - size_t keylen = strlen(key); - size_t quoted_len = keylen + 2 + 1; - char* quoted = static_cast<char*>(alloca(quoted_len)); - snprintf(quoted, quoted_len, "\"%s\"", key); - EXPECT(quoted); - EXPECT(":"); - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::ParseUint32(uint32_t* out_int) { - uint32_t result = 0; - SkipWhitespace(); - while (1) { - int c = ReadChar(); - if (c >= '0' && c <= '9') { - uint32_t last_result = result; - result = result * 10 + static_cast<uint32_t>(c - '0'); - if (result < last_result) { - PrintParseError("uint32 overflow"); - return wabt::Result::Error; - } - } else { - PutbackChar(); - break; - } - } - *out_int = result; - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::ParseString(std::string* out_string) { - out_string->clear(); - - SkipWhitespace(); - if (ReadChar() != '"') { - PrintParseError("expected string"); - return wabt::Result::Error; - } - - while (1) { - int c = ReadChar(); - if (c == '"') { - break; - } else if (c == '\\') { - /* The only escape supported is \uxxxx. */ - c = ReadChar(); - if (c != 'u') { - PrintParseError("expected escape: \\uxxxx"); - return wabt::Result::Error; - } - uint16_t code = 0; - for (int i = 0; i < 4; ++i) { - c = ReadChar(); - int cval; - if (c >= '0' && c <= '9') { - cval = c - '0'; - } else if (c >= 'a' && c <= 'f') { - cval = c - 'a' + 10; - } else if (c >= 'A' && c <= 'F') { - cval = c - 'A' + 10; - } else { - PrintParseError("expected hex char"); - return wabt::Result::Error; - } - code = (code << 4) + cval; - } - - if (code < 256) { - *out_string += code; - } else { - PrintParseError("only escape codes < 256 allowed, got %u\n", code); - } - } else { - *out_string += c; - } - } - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::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) { - out_string->clear(); - if (Match("\"name\"")) { - EXPECT(":"); - CHECK_RESULT(ParseString(out_string)); - EXPECT(","); - } - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::ParseLine() { - EXPECT_KEY("line"); - CHECK_RESULT(ParseUint32(&command_line_number_)); - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::ParseTypeObject(Type* out_type) { - std::string type_str; - EXPECT("{"); - PARSE_KEY_STRING_VALUE("type", &type_str); - EXPECT("}"); - - if (type_str == "i32") { - *out_type = Type::I32; - return wabt::Result::Ok; - } else if (type_str == "f32") { - *out_type = Type::F32; - return wabt::Result::Ok; - } else if (type_str == "i64") { - *out_type = Type::I64; - return wabt::Result::Ok; - } else if (type_str == "f64") { - *out_type = Type::F64; - return wabt::Result::Ok; - } else { - PrintParseError("unknown type: \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(type_str)); - return wabt::Result::Error; - } -} - -wabt::Result SpecJSONParser::ParseTypeVector(TypeVector* out_types) { - out_types->clear(); - EXPECT("["); - bool first = true; - while (!Match("]")) { - if (!first) - EXPECT(","); - Type type; - CHECK_RESULT(ParseTypeObject(&type)); - first = false; - out_types->push_back(type); - } - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::ParseConst(TypedValue* out_value) { - std::string type_str; - std::string value_str; - EXPECT("{"); - PARSE_KEY_STRING_VALUE("type", &type_str); - EXPECT(","); - PARSE_KEY_STRING_VALUE("value", &value_str); - EXPECT("}"); - - const char* value_start = value_str.data(); - const char* value_end = value_str.data() + value_str.size(); - - if (type_str == "i32") { - uint32_t value; - CHECK_RESULT( - ParseInt32(value_start, value_end, &value, ParseIntType::UnsignedOnly)); - out_value->type = Type::I32; - out_value->value.i32 = value; - return wabt::Result::Ok; - } else if (type_str == "f32") { - uint32_t value_bits; - CHECK_RESULT(ParseInt32(value_start, value_end, &value_bits, - ParseIntType::UnsignedOnly)); - out_value->type = Type::F32; - out_value->value.f32_bits = value_bits; - return wabt::Result::Ok; - } else if (type_str == "i64") { - uint64_t value; - CHECK_RESULT( - ParseInt64(value_start, value_end, &value, ParseIntType::UnsignedOnly)); - out_value->type = Type::I64; - out_value->value.i64 = value; - return wabt::Result::Ok; - } else if (type_str == "f64") { - uint64_t value_bits; - CHECK_RESULT(ParseInt64(value_start, value_end, &value_bits, - ParseIntType::UnsignedOnly)); - out_value->type = Type::F64; - out_value->value.f64_bits = value_bits; - return wabt::Result::Ok; - } else { - PrintParseError("unknown type: \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(type_str)); - return wabt::Result::Error; - } -} - -wabt::Result SpecJSONParser::ParseConstVector( - std::vector<TypedValue>* out_values) { - out_values->clear(); - EXPECT("["); - bool first = true; - while (!Match("]")) { - if (!first) - EXPECT(","); - TypedValue value; - CHECK_RESULT(ParseConst(&value)); - out_values->push_back(value); - first = false; - } - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::ParseAction(::Action* out_action) { - EXPECT_KEY("action"); - EXPECT("{"); - EXPECT_KEY("type"); - if (Match("\"invoke\"")) { - out_action->type = ::ActionType::Invoke; - } else { - EXPECT("\"get\""); - out_action->type = ::ActionType::Get; - } - EXPECT(","); - if (Match("\"module\"")) { - EXPECT(":"); - CHECK_RESULT(ParseString(&out_action->module_name)); - EXPECT(","); - } - PARSE_KEY_STRING_VALUE("field", &out_action->field_name); - if (out_action->type == ::ActionType::Invoke) { - EXPECT(","); - EXPECT_KEY("args"); - CHECK_RESULT(ParseConstVector(&out_action->args)); - } - EXPECT("}"); - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::ParseModuleType(ModuleType* out_type) { - std::string module_type_str; - - PARSE_KEY_STRING_VALUE("module_type", &module_type_str); - if (module_type_str == "text") { - *out_type = ModuleType::Text; - return wabt::Result::Ok; - } else if (module_type_str == "binary") { - *out_type = ModuleType::Binary; - return wabt::Result::Ok; - } else { - PrintParseError("unknown module type: \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(module_type_str)); - return wabt::Result::Error; - } -} - -std::string SpecJSONParser::CreateModulePath(string_view filename) { - const char* spec_json_filename = loc_.filename; - string_view dirname = GetDirname(spec_json_filename); - std::string path; - - if (dirname.size() == 0) { - path = filename.to_string(); - } else { - path = dirname.to_string(); - path += '/'; - path += filename.to_string(); - } - - ConvertBackslashToSlash(&path); - 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_); - - if (Failed(result)) { - env_.ResetToMarkPoint(mark); - PrintCommandError("error reading module: \"%s\"", path.c_str()); - return wabt::Result::Error; - } - - interpreter::Result iresult = RunStartFunction(&thread_, last_module_); - if (iresult != interpreter::Result::Ok) { - env_.ResetToMarkPoint(mark); - PrintInterpreterResult("error running start function", iresult); - return wabt::Result::Error; - } - - if (!name.empty()) { - last_module_->name = name.to_string(); - env_.EmplaceModuleBinding(name.to_string(), - Binding(env_.GetModuleCount() - 1)); - } - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::RunAction(::Action* action, - interpreter::Result* out_iresult, - std::vector<TypedValue>* out_results, - RunVerbosity verbose) { - out_results->clear(); - - interpreter::Module* module; - if (!action->module_name.empty()) { - module = env_.FindModule(action->module_name); - } else { - module = env_.GetLastModule(); - } - assert(module); - - switch (action->type) { - case ::ActionType::Invoke: - *out_iresult = RunExportByName(&thread_, module, action->field_name, - action->args, out_results, verbose); - if (verbose == RunVerbosity::Verbose) { - PrintCall(string_view(), action->field_name, action->args, *out_results, - *out_iresult); - } - return wabt::Result::Ok; - - 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)); - return wabt::Result::Error; - } -} - -wabt::Result SpecJSONParser::OnActionCommand(::Action* action) { - std::vector<TypedValue> 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", - s_trap_strings[static_cast<size_t>(iresult)]); - result = wabt::Result::Error; - } - } - - return result; -} - -wabt::Result SpecJSONParser::ReadInvalidTextModule( - const char* module_filename, - Environment* env, - ErrorHandler* error_handler) { - std::unique_ptr<WastLexer> lexer = - WastLexer::CreateFileLexer(module_filename); - std::unique_ptr<Script> script; - wabt::Result result = ParseWastScript(lexer.get(), &script, error_handler); - if (Succeeded(result)) { - wabt::Module* module = script->GetFirstModule(); - result = ResolveNamesModule(lexer.get(), module, error_handler); - if (Succeeded(result)) { - // Don't do a full validation, just validate the function signatures. - result = ValidateFuncSignatures(lexer.get(), module, error_handler); - } - } - 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); - - switch (module_type) { - case ModuleType::Text: { - ErrorHandlerFile error_handler(Location::Type::Text, stdout, header, - ErrorHandlerFile::PrintHeader::Once); - return ReadInvalidTextModule(module_filename, env, &error_handler); - } - - case ModuleType::Binary: { - DefinedModule* module; - ErrorHandlerFile error_handler(Location::Type::Binary, stdout, header, - ErrorHandlerFile::PrintHeader::Once); - return ReadModule(module_filename, env, &error_handler, &module); - } - } - - WABT_UNREACHABLE; -} - -wabt::Result SpecJSONParser::OnAssertMalformedCommand(string_view filename, - string_view text, - ModuleType module_type) { - Environment env; - InitEnvironment(&env); - - total_++; - std::string path = CreateModulePath(filename); - wabt::Result result = - ReadInvalidModule(path.c_str(), &env, module_type, "assert_malformed"); - if (Failed(result)) { - passed_++; - result = wabt::Result::Ok; - } else { - PrintCommandError("expected module to be malformed: \"%s\"", path.c_str()); - result = wabt::Result::Error; - } - - return result; -} - -wabt::Result SpecJSONParser::OnRegisterCommand(string_view name, - string_view as) { - Index module_index; - if (!name.empty()) { - module_index = env_.FindModuleIndex(name); - } else { - module_index = env_.GetLastModuleIndex(); - } - - if (module_index == kInvalidIndex) { - PrintCommandError("unknown module in register"); - return wabt::Result::Error; - } - - env_.EmplaceRegisteredModuleBinding(as.to_string(), Binding(module_index)); - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::OnAssertUnlinkableCommand(string_view filename, - string_view text, - ModuleType module_type) { - total_++; - std::string path = CreateModulePath(filename); - Environment::MarkPoint mark = env_.Mark(); - wabt::Result result = - ReadInvalidModule(path.c_str(), &env_, module_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()); - result = wabt::Result::Error; - } - - return result; -} - -wabt::Result SpecJSONParser::OnAssertInvalidCommand(string_view filename, - string_view text, - ModuleType module_type) { - Environment env; - InitEnvironment(&env); - - total_++; - std::string path = CreateModulePath(filename); - wabt::Result result = - ReadInvalidModule(path.c_str(), &env, module_type, "assert_invalid"); - if (Failed(result)) { - passed_++; - result = wabt::Result::Ok; - } else { - PrintCommandError("expected module to be invalid: \"%s\"", path.c_str()); - result = wabt::Result::Error; - } - - return result; -} - -wabt::Result SpecJSONParser::OnAssertUninstantiableCommand( - string_view filename, - string_view text, - ModuleType module_type) { - 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); - - if (Succeeded(result)) { - interpreter::Result iresult = RunStartFunction(&thread_, module); - if (iresult == interpreter::Result::Ok) { - PrintCommandError("expected error running start function: \"%s\"", - path.c_str()); - result = wabt::Result::Error; - } else { - passed_++; - result = wabt::Result::Ok; - } - } else { - PrintCommandError("error reading module: \"%s\"", path.c_str()); - result = wabt::Result::Error; - } - - env_.ResetToMarkPoint(mark); - return result; -} - -static bool TypedValuesAreEqual(const TypedValue* tv1, const TypedValue* tv2) { - if (tv1->type != tv2->type) - return false; - - switch (tv1->type) { - case Type::I32: - return tv1->value.i32 == tv2->value.i32; - case Type::F32: - return tv1->value.f32_bits == tv2->value.f32_bits; - case Type::I64: - return tv1->value.i64 == tv2->value.i64; - case Type::F64: - return tv1->value.f64_bits == tv2->value.f64_bits; - default: - WABT_UNREACHABLE; - } -} - -wabt::Result SpecJSONParser::OnAssertReturnCommand( - ::Action* action, - const std::vector<TypedValue>& expected) { - std::vector<TypedValue> results; - interpreter::Result iresult; - - total_++; - wabt::Result result = - RunAction(action, &iresult, &results, RunVerbosity::Quiet); - - if (Succeeded(result)) { - if (iresult == interpreter::Result::Ok) { - if (results.size() == expected.size()) { - for (size_t i = 0; i < results.size(); ++i) { - const TypedValue* expected_tv = &expected[i]; - const TypedValue* actual_tv = &results[i]; - if (!TypedValuesAreEqual(expected_tv, actual_tv)) { - char expected_str[MAX_TYPED_VALUE_CHARS]; - char actual_str[MAX_TYPED_VALUE_CHARS]; - SPrintTypedValue(expected_str, sizeof(expected_str), expected_tv); - SPrintTypedValue(actual_str, sizeof(actual_str), actual_tv); - PrintCommandError("mismatch in result %" PRIzd - " of assert_return: expected %s, got %s", - i, expected_str, actual_str); - result = wabt::Result::Error; - } - } - } else { - PrintCommandError( - "result length mismatch in assert_return: expected %" PRIzd - ", got %" PRIzd, - expected.size(), results.size()); - result = wabt::Result::Error; - } - } else { - PrintCommandError("unexpected trap: %s", - s_trap_strings[static_cast<size_t>(iresult)]); - result = wabt::Result::Error; - } - } - - if (Succeeded(result)) - passed_++; - - return result; -} - -wabt::Result SpecJSONParser::OnAssertReturnNanCommand(::Action* action, - bool canonical) { - std::vector<TypedValue> results; - interpreter::Result iresult; - - total_++; - wabt::Result result = - RunAction(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()); - 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); - if (!is_nan) { - char actual_str[MAX_TYPED_VALUE_CHARS]; - SPrintTypedValue(actual_str, sizeof(actual_str), &actual); - PrintCommandError("expected result to be nan, got %s", actual_str); - result = wabt::Result::Error; - } - break; - } - - case Type::F64: { - bool is_nan = canonical ? IsCanonicalNan(actual.value.f64_bits) - : IsArithmeticNan(actual.value.f64_bits); - if (!is_nan) { - char actual_str[MAX_TYPED_VALUE_CHARS]; - SPrintTypedValue(actual_str, sizeof(actual_str), &actual); - PrintCommandError("expected result to be nan, got %s", actual_str); - result = wabt::Result::Error; - } - break; - } - - default: - PrintCommandError("expected result type to be f32 or f64, got %s", - GetTypeName(actual.type)); - result = wabt::Result::Error; - break; - } - } else { - PrintCommandError("unexpected trap: %s", - s_trap_strings[static_cast<int>(iresult)]); - result = wabt::Result::Error; - } - } - - if (Succeeded(result)) - passed_++; - - return wabt::Result::Ok; -} - -wabt::Result SpecJSONParser::OnAssertTrapCommand(::Action* action, - string_view text) { - std::vector<TypedValue> results; - interpreter::Result iresult; - - total_++; - wabt::Result result = - RunAction(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)); - result = wabt::Result::Error; - } - } - - return result; -} - -wabt::Result SpecJSONParser::OnAssertExhaustionCommand(::Action* action) { - std::vector<TypedValue> results; - interpreter::Result iresult; - - total_++; - wabt::Result result = - RunAction(action, &iresult, &results, RunVerbosity::Quiet); - if (Succeeded(result)) { - if (iresult == interpreter::Result::TrapCallStackExhausted || - iresult == interpreter::Result::TrapValueStackExhausted) { - passed_++; - } else { - PrintCommandError("expected call stack exhaustion"); - result = wabt::Result::Error; - } - } - - 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; - std::vector<TypedValue> 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; - - 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; - - 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; -} - -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()); - return result; -} - int ProgramMain(int argc, char** argv) { InitStdio(); ParseOptions(argc, argv); s_stdout_stream = FileStream::CreateStdout(); - wabt::Result result; - if (s_spec) { - result = ReadAndRunSpecJSON(s_infile); - } else { - result = ReadAndRunModule(s_infile); - } + wabt::Result result = ReadAndRunModule(s_infile); return result != wabt::Result::Ok; } |