summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBen Smith <binjimin@gmail.com>2017-10-03 10:28:34 -0700
committerGitHub <noreply@github.com>2017-10-03 10:28:34 -0700
commit0e08c4974e614e05ecb72f25df07c04ff02b1ad0 (patch)
treebd52ec46e8aff4140935205b349c1889d0d04108 /src
parentb10a3ab9187003f9e8ba84ef714821227981e77b (diff)
downloadwabt-0e08c4974e614e05ecb72f25df07c04ff02b1ad0.tar.gz
wabt-0e08c4974e614e05ecb72f25df07c04ff02b1ad0.tar.bz2
wabt-0e08c4974e614e05ecb72f25df07c04ff02b1ad0.zip
Add spectest-interp tool; split from wasm-interp (#643)
* `wasm-interp` tool now only runs `.wasm` file, not spec tests * `wasm-interp` has a new flag `--host-print` for importing a print function named "host.print" * `spectest-interp` tool runs only `.json` files
Diffstat (limited to 'src')
-rw-r--r--src/tools/spectest-interp.cc1470
-rw-r--r--src/tools/wasm-interp.cc1230
2 files changed, 1507 insertions, 1193 deletions
diff --git a/src/tools/spectest-interp.cc b/src/tools/spectest-interp.cc
new file mode 100644
index 00000000..24253c9d
--- /dev/null
+++ b/src/tools/spectest-interp.cc
@@ -0,0 +1,1470 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "src/binary-reader-interpreter.h"
+#include "src/binary-reader.h"
+#include "src/cast.h"
+#include "src/error-handler.h"
+#include "src/feature.h"
+#include "src/interpreter.h"
+#include "src/literal.h"
+#include "src/option-parser.h"
+#include "src/resolve-names.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+#include "src/wast-parser.h"
+
+using namespace wabt;
+using namespace wabt::interpreter;
+
+#define V(name, str) str,
+static const char* s_trap_strings[] = {FOREACH_INTERPRETER_RESULT(V)};
+#undef V
+
+static int s_verbose;
+static const char* s_infile;
+static Thread::Options s_thread_options;
+static bool s_trace;
+static Features s_features;
+
+static std::unique_ptr<FileStream> s_log_stream;
+static std::unique_ptr<FileStream> s_stdout_stream;
+
+enum class RunVerbosity {
+ Quiet = 0,
+ Verbose = 1,
+};
+
+static const char s_description[] =
+R"( read a Spectest JSON file, and run its tests in the interpreter.
+
+examples:
+ # parse test.json and run the spec tests
+ $ spectest-interp test.json
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("spectest-interp", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStdout();
+ });
+ parser.AddHelpOption();
+ s_features.AddOptions(&parser);
+ parser.AddOption('V', "value-stack-size", "SIZE",
+ "Size in elements of the value stack",
+ [](const std::string& argument) {
+ // TODO(binji): validate.
+ s_thread_options.value_stack_size = atoi(argument.c_str());
+ });
+ parser.AddOption('C', "call-stack-size", "SIZE",
+ "Size in elements of the call stack",
+ [](const std::string& argument) {
+ // TODO(binji): validate.
+ s_thread_options.call_stack_size = atoi(argument.c_str());
+ });
+ parser.AddOption('t', "trace", "Trace execution", []() { s_trace = true; });
+
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+ 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));
+}
+
+/* Not sure, but 100 chars is probably safe */
+#define MAX_TYPED_VALUE_CHARS 100
+
+static void SPrintTypedValue(char* buffer, size_t size, const TypedValue* tv) {
+ switch (tv->type) {
+ case Type::I32:
+ snprintf(buffer, size, "i32:%u", tv->value.i32);
+ break;
+
+ case Type::I64:
+ snprintf(buffer, size, "i64:%" PRIu64, tv->value.i64);
+ break;
+
+ case Type::F32: {
+ float value;
+ memcpy(&value, &tv->value.f32_bits, sizeof(float));
+ snprintf(buffer, size, "f32:%f", value);
+ break;
+ }
+
+ case Type::F64: {
+ double value;
+ memcpy(&value, &tv->value.f64_bits, sizeof(double));
+ snprintf(buffer, size, "f64:%f", value);
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+}
+
+static void PrintTypedValue(const TypedValue* tv) {
+ char buffer[MAX_TYPED_VALUE_CHARS];
+ SPrintTypedValue(buffer, sizeof(buffer), tv);
+ printf("%s", buffer);
+}
+
+static void PrintTypedValueVector(const std::vector<TypedValue>& values) {
+ for (size_t i = 0; i < values.size(); ++i) {
+ PrintTypedValue(&values[i]);
+ if (i != values.size() - 1)
+ printf(", ");
+ }
+}
+
+static void PrintInterpreterResult(const char* desc,
+ interpreter::Result iresult) {
+ printf("%s: %s\n", desc, s_trap_strings[static_cast<size_t>(iresult)]);
+}
+
+static void PrintCall(string_view module_name,
+ string_view func_name,
+ const std::vector<TypedValue>& args,
+ const std::vector<TypedValue>& results,
+ interpreter::Result iresult) {
+ if (!module_name.empty())
+ printf(PRIstringview ".", WABT_PRINTF_STRING_VIEW_ARG(module_name));
+ printf(PRIstringview "(", WABT_PRINTF_STRING_VIEW_ARG(func_name));
+ PrintTypedValueVector(args);
+ printf(") =>");
+ if (iresult == interpreter::Result::Ok) {
+ if (results.size() > 0) {
+ printf(" ");
+ PrintTypedValueVector(results);
+ }
+ printf("\n");
+ } else {
+ PrintInterpreterResult(" error", iresult);
+ }
+}
+
+static interpreter::Result RunFunction(Thread* thread,
+ Index func_index,
+ const std::vector<TypedValue>& args,
+ std::vector<TypedValue>* out_results) {
+ return s_trace ? thread->TraceFunction(func_index, s_stdout_stream.get(),
+ args, out_results)
+ : thread->RunFunction(func_index, args, out_results);
+}
+
+static interpreter::Result RunStartFunction(Thread* thread,
+ DefinedModule* module) {
+ if (module->start_func_index == kInvalidIndex)
+ return interpreter::Result::Ok;
+
+ if (s_trace)
+ printf(">>> running start function:\n");
+ std::vector<TypedValue> args;
+ std::vector<TypedValue> results;
+ interpreter::Result iresult =
+ RunFunction(thread, module->start_func_index, args, &results);
+ assert(results.size() == 0);
+ return iresult;
+}
+
+static interpreter::Result RunExport(Thread* thread,
+ const interpreter::Export* export_,
+ const std::vector<TypedValue>& args,
+ std::vector<TypedValue>* out_results) {
+ if (s_trace) {
+ printf(">>> running export \"" PRIstringview "\":\n",
+ WABT_PRINTF_STRING_VIEW_ARG(export_->name));
+ }
+
+ assert(export_->kind == ExternalKind::Func);
+ 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 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;
+}
+
+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 {
+ 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());
+}
+
+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;
+ result = ReadAndRunSpecJSON(s_infile);
+ return result != wabt::Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
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;
}