summaryrefslogtreecommitdiff
path: root/src/tools/spectest-interp.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/spectest-interp.cc')
-rw-r--r--src/tools/spectest-interp.cc1309
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;
}