summaryrefslogtreecommitdiffstats
path: root/third_party/wasm2c/src/tools
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/wasm2c/src/tools
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/wasm2c/src/tools')
-rw-r--r--third_party/wasm2c/src/tools/spectest-interp.cc1852
-rw-r--r--third_party/wasm2c/src/tools/wasm-decompile.cc119
-rw-r--r--third_party/wasm2c/src/tools/wasm-interp.cc337
-rw-r--r--third_party/wasm2c/src/tools/wasm-objdump.cc149
-rw-r--r--third_party/wasm2c/src/tools/wasm-opcodecnt.cc188
-rw-r--r--third_party/wasm2c/src/tools/wasm-strip.cc114
-rw-r--r--third_party/wasm2c/src/tools/wasm-validate.cc99
-rw-r--r--third_party/wasm2c/src/tools/wasm2c.cc169
-rw-r--r--third_party/wasm2c/src/tools/wasm2wat-fuzz.cc30
-rw-r--r--third_party/wasm2c/src/tools/wasm2wat.cc149
-rw-r--r--third_party/wasm2c/src/tools/wast2json.cc162
-rw-r--r--third_party/wasm2c/src/tools/wat-desugar.cc130
-rw-r--r--third_party/wasm2c/src/tools/wat2wasm.cc170
13 files changed, 3668 insertions, 0 deletions
diff --git a/third_party/wasm2c/src/tools/spectest-interp.cc b/third_party/wasm2c/src/tools/spectest-interp.cc
new file mode 100644
index 0000000000..c27e7a9f69
--- /dev/null
+++ b/third_party/wasm2c/src/tools/spectest-interp.cc
@@ -0,0 +1,1852 @@
+/*
+ * 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 <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "src/binary-reader.h"
+#include "src/cast.h"
+#include "src/common.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/interp/binary-reader-interp.h"
+#include "src/interp/interp-util.h"
+#include "src/interp/interp.h"
+#include "src/literal.h"
+#include "src/option-parser.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::interp;
+
+static int s_verbose;
+static const char* s_infile;
+static Thread::Options s_thread_options;
+static Stream* s_trace_stream;
+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::CreateStderr();
+ });
+ 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_stream = s_stdout_stream.get(); });
+
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+ parser.Parse(argc, argv);
+}
+
+namespace spectest {
+
+class Command;
+typedef std::unique_ptr<Command> CommandPtr;
+typedef std::vector<CommandPtr> CommandPtrVector;
+
+class Script {
+ public:
+ std::string filename;
+ CommandPtrVector commands;
+};
+
+class Command {
+ public:
+ WABT_DISALLOW_COPY_AND_ASSIGN(Command);
+ Command() = delete;
+ virtual ~Command() = default;
+
+ CommandType type;
+ uint32_t line = 0;
+
+ protected:
+ explicit Command(CommandType type) : type(type) {}
+};
+
+template <CommandType TypeEnum>
+class CommandMixin : public Command {
+ public:
+ static bool classof(const Command* cmd) { return cmd->type == TypeEnum; }
+ CommandMixin() : Command(TypeEnum) {}
+};
+
+enum class ModuleType {
+ Text,
+ Binary,
+};
+
+class ModuleCommand : public CommandMixin<CommandType::Module> {
+ public:
+ ModuleType module = ModuleType::Binary;
+ std::string filename;
+ std::string name;
+};
+
+class Action {
+ public:
+ ActionType type = ActionType::Invoke;
+ std::string module_name;
+ std::string field_name;
+ ValueTypes types;
+ Values args;
+};
+
+template <CommandType TypeEnum>
+class ActionCommandBase : public CommandMixin<TypeEnum> {
+ public:
+ Action action;
+};
+
+typedef ActionCommandBase<CommandType::Action> ActionCommand;
+
+class RegisterCommand : public CommandMixin<CommandType::Register> {
+ public:
+ std::string as;
+ std::string name;
+};
+
+struct ExpectedValue {
+ TypedValue value;
+ Type lane_type; // Only valid if value.type == Type::V128.
+ // Up to 4 NaN values used, depending on |value.type| and |lane_type|:
+ // | type | lane_type | valid |
+ // | f32 | | nan[0] |
+ // | f64 | | nan[0] |
+ // | v128 | f32 | nan[0] through nan[3] |
+ // | v128 | f64 | nan[0],nan[1] |
+ // | * | * | none valid |
+ ExpectedNan nan[4];
+};
+
+int LaneCountFromType(Type type) {
+ switch (type) {
+ case Type::I8: return 16;
+ case Type::I16: return 8;
+ case Type::I32: return 4;
+ case Type::I64: return 2;
+ case Type::F32: return 4;
+ case Type::F64: return 2;
+ default: assert(false); return 0;
+ }
+}
+
+ExpectedValue GetLane(const ExpectedValue& ev, int lane) {
+ int lane_count = LaneCountFromType(ev.lane_type);
+ assert(ev.value.type == Type::V128);
+ assert(lane < lane_count);
+
+ ExpectedValue result;
+ result.value.type = ev.lane_type;
+
+ v128 vec = ev.value.value.Get<v128>();
+
+ for (int lane = 0; lane < lane_count; ++lane) {
+ switch (ev.lane_type) {
+ case Type::I8:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u32>(vec.u8(lane));
+ break;
+
+ case Type::I16:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u32>(vec.u16(lane));
+ break;
+
+ case Type::I32:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u32>(vec.u32(lane));
+ break;
+
+ case Type::I64:
+ result.nan[0] = ExpectedNan::None;
+ result.value.value.Set<u64>(vec.u64(lane));
+ break;
+
+ case Type::F32:
+ result.nan[0] = ev.nan[lane];
+ result.value.value.Set<f32>(Bitcast<f32>(vec.f32_bits(lane)));
+ break;
+
+ case Type::F64:
+ result.nan[0] = ev.nan[lane];
+ result.value.value.Set<f64>(Bitcast<f64>(vec.f64_bits(lane)));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+ return result;
+}
+
+TypedValue GetLane(const TypedValue& tv, Type lane_type, int lane) {
+ int lane_count = LaneCountFromType(lane_type);
+ assert(tv.type == Type::V128);
+ assert(lane < lane_count);
+
+ TypedValue result;
+ result.type = lane_type;
+
+ v128 vec = tv.value.Get<v128>();
+
+ for (int lane = 0; lane < lane_count; ++lane) {
+ switch (lane_type) {
+ case Type::I8:
+ result.value.Set<u32>(vec.u8(lane));
+ break;
+
+ case Type::I16:
+ result.value.Set<u32>(vec.u16(lane));
+ break;
+
+ case Type::I32:
+ result.value.Set<u32>(vec.u32(lane));
+ break;
+
+ case Type::I64:
+ result.value.Set<u64>(vec.u64(lane));
+ break;
+
+ case Type::F32:
+ result.value.Set<f32>(Bitcast<f32>(vec.f32_bits(lane)));
+ break;
+
+ case Type::F64:
+ result.value.Set<f64>(Bitcast<f64>(vec.f64_bits(lane)));
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+ }
+ return result;
+}
+
+class AssertReturnCommand : public CommandMixin<CommandType::AssertReturn> {
+ public:
+ Action action;
+ std::vector<ExpectedValue> expected;
+};
+
+template <CommandType TypeEnum>
+class AssertTrapCommandBase : public CommandMixin<TypeEnum> {
+ public:
+ Action action;
+ std::string text;
+};
+
+typedef AssertTrapCommandBase<CommandType::AssertTrap> AssertTrapCommand;
+typedef AssertTrapCommandBase<CommandType::AssertExhaustion>
+ AssertExhaustionCommand;
+
+template <CommandType TypeEnum>
+class AssertModuleCommand : public CommandMixin<TypeEnum> {
+ public:
+ ModuleType type = ModuleType::Binary;
+ std::string filename;
+ std::string text;
+};
+
+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 JSONParser {
+ public:
+ JSONParser() {}
+
+ wabt::Result ReadFile(string_view spec_json_filename);
+ wabt::Result ParseScript(Script* out_script);
+
+ private:
+ void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...);
+
+ // Whether to allow parsing of expectation-only forms (e.g. `nan:canonical`,
+ // `nan:arithmetic`, etc.)
+ enum class AllowExpected { No, Yes };
+
+ 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(uint32_t* out_line_number);
+ wabt::Result ParseType(Type* out_type);
+ wabt::Result ParseTypeObject(Type* out_type);
+ wabt::Result ParseTypeVector(TypeVector* out_types);
+ wabt::Result ParseConst(TypedValue* out_value);
+ wabt::Result ParseI32Value(uint32_t* out_value, string_view value_str);
+ wabt::Result ParseI64Value(uint64_t* out_value, string_view value_str);
+ wabt::Result ParseF32Value(uint32_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseF64Value(uint64_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseLaneConstValue(Type lane_type,
+ int lane,
+ ExpectedValue* out_value,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseConstValue(Type type,
+ Value* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected);
+ wabt::Result ParseConstVector(ValueTypes* out_types, Values* out_values);
+ wabt::Result ParseExpectedValue(ExpectedValue* out_value, AllowExpected);
+ wabt::Result ParseExpectedValues(std::vector<ExpectedValue>* out_values);
+ wabt::Result ParseAction(Action* out_action);
+ wabt::Result ParseActionResult();
+ wabt::Result ParseModuleType(ModuleType* out_type);
+
+ std::string CreateModulePath(string_view filename);
+ wabt::Result ParseFilename(std::string* out_filename);
+ wabt::Result ParseCommand(CommandPtr* out_command);
+
+ // Parsing info.
+ std::vector<uint8_t> json_data_;
+ size_t json_offset_ = 0;
+ Location loc_;
+ Location prev_loc_;
+ bool has_prev_loc_ = false;
+};
+
+#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 JSONParser::ReadFile(string_view spec_json_filename) {
+ loc_.filename = spec_json_filename;
+ loc_.line = 1;
+ loc_.first_column = 1;
+
+ return wabt::ReadFile(spec_json_filename, &json_data_);
+}
+
+void JSONParser::PrintError(const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ fprintf(stderr, "%s:%d:%d: %s\n", loc_.filename.to_string().c_str(),
+ loc_.line, loc_.first_column, buffer);
+}
+
+void JSONParser::PutbackChar() {
+ assert(has_prev_loc_);
+ json_offset_--;
+ loc_ = prev_loc_;
+ has_prev_loc_ = false;
+}
+
+int JSONParser::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 JSONParser::SkipWhitespace() {
+ while (1) {
+ switch (ReadChar()) {
+ case -1:
+ return;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ break;
+
+ default:
+ PutbackChar();
+ return;
+ }
+ }
+}
+
+bool JSONParser::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 JSONParser::Expect(const char* s) {
+ if (Match(s)) {
+ return wabt::Result::Ok;
+ } else {
+ PrintError("expected %s", s);
+ return wabt::Result::Error;
+ }
+}
+
+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));
+ snprintf(quoted, quoted_len, "\"%s\"", key);
+ EXPECT(quoted);
+ EXPECT(":");
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::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) {
+ PrintError("uint32 overflow");
+ return wabt::Result::Error;
+ }
+ } else {
+ PutbackChar();
+ break;
+ }
+ }
+ *out_int = result;
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseString(std::string* out_string) {
+ out_string->clear();
+
+ SkipWhitespace();
+ if (ReadChar() != '"') {
+ PrintError("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') {
+ PrintError("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 {
+ PrintError("expected hex char");
+ return wabt::Result::Error;
+ }
+ code = (code << 4) + cval;
+ }
+
+ if (code < 256) {
+ *out_string += code;
+ } else {
+ PrintError("only escape codes < 256 allowed, got %u\n", code);
+ }
+ } else {
+ *out_string += c;
+ }
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseKeyStringValue(const char* key,
+ std::string* out_string) {
+ out_string->clear();
+ EXPECT_KEY(key);
+ return ParseString(out_string);
+}
+
+wabt::Result JSONParser::ParseOptNameStringValue(std::string* out_string) {
+ out_string->clear();
+ if (Match("\"name\"")) {
+ EXPECT(":");
+ CHECK_RESULT(ParseString(out_string));
+ EXPECT(",");
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseLine(uint32_t* out_line_number) {
+ EXPECT_KEY("line");
+ CHECK_RESULT(ParseUint32(out_line_number));
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseType(Type* out_type) {
+ std::string type_str;
+ CHECK_RESULT(ParseString(&type_str));
+
+ if (type_str == "i32") {
+ *out_type = Type::I32;
+ } else if (type_str == "f32") {
+ *out_type = Type::F32;
+ } else if (type_str == "i64") {
+ *out_type = Type::I64;
+ } else if (type_str == "f64") {
+ *out_type = Type::F64;
+ } else if (type_str == "v128") {
+ *out_type = Type::V128;
+ } else if (type_str == "i8") {
+ *out_type = Type::I8;
+ } else if (type_str == "i16") {
+ *out_type = Type::I16;
+ } else if (type_str == "funcref") {
+ *out_type = Type::FuncRef;
+ } else if (type_str == "externref") {
+ *out_type = Type::ExternRef;
+ } else {
+ PrintError("unknown type: \"%s\"", type_str.c_str());
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseTypeObject(Type* out_type) {
+ EXPECT("{");
+ EXPECT_KEY("type");
+ CHECK_RESULT(ParseType(out_type));
+ EXPECT("}");
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::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 JSONParser::ParseConst(TypedValue* out_value) {
+ ExpectedValue expected;
+ CHECK_RESULT(ParseExpectedValue(&expected, AllowExpected::No));
+ *out_value = expected.value;
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseI32Value(uint32_t* out_value,
+ string_view value_str) {
+ if (Failed(ParseInt32(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid i32 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseI64Value(uint64_t* out_value,
+ string_view value_str) {
+ if (Failed(ParseInt64(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid i64 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseF32Value(uint32_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ if (allow_expected == AllowExpected::Yes) {
+ *out_value = 0;
+ if (value_str == "nan:canonical") {
+ *out_nan = ExpectedNan::Canonical;
+ return wabt::Result::Ok;
+ } else if (value_str == "nan:arithmetic") {
+ *out_nan = ExpectedNan::Arithmetic;
+ return wabt::Result::Ok;
+ }
+ }
+
+ *out_nan = ExpectedNan::None;
+ if (Failed(ParseInt32(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid f32 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseF64Value(uint64_t* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ if (allow_expected == AllowExpected::Yes) {
+ *out_value = 0;
+ if (value_str == "nan:canonical") {
+ *out_nan = ExpectedNan::Canonical;
+ return wabt::Result::Ok;
+ } else if (value_str == "nan:arithmetic") {
+ *out_nan = ExpectedNan::Arithmetic;
+ return wabt::Result::Ok;
+ }
+ }
+
+ *out_nan = ExpectedNan::None;
+ if (Failed(ParseInt64(value_str.begin(), value_str.end(), out_value,
+ ParseIntType::UnsignedOnly))) {
+ PrintError("invalid f64 literal");
+ return wabt::Result::Error;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseLaneConstValue(Type lane_type,
+ int lane,
+ ExpectedValue* out_value,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ v128 v = out_value->value.value.Get<v128>();
+
+ switch (lane_type) {
+ case Type::I8: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ v.set_u8(lane, value);
+ break;
+ }
+
+ case Type::I16: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ v.set_u16(lane, value);
+ break;
+ }
+
+ case Type::I32: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ v.set_u32(lane, value);
+ break;
+ }
+
+ case Type::I64: {
+ uint64_t value;
+ CHECK_RESULT(ParseI64Value(&value, value_str));
+ v.set_u64(lane, value);
+ break;
+ }
+
+ case Type::F32: {
+ ExpectedNan nan;
+ uint32_t value_bits;
+ CHECK_RESULT(ParseF32Value(&value_bits, &nan, value_str, allow_expected));
+ v.set_f32_bits(lane, value_bits);
+ assert(lane < 4);
+ out_value->nan[lane] = nan;
+ break;
+ }
+
+ case Type::F64: {
+ ExpectedNan nan;
+ uint64_t value_bits;
+ CHECK_RESULT(ParseF64Value(&value_bits, &nan, value_str, allow_expected));
+ v.set_f64_bits(lane, value_bits);
+ assert(lane < 2);
+ out_value->nan[lane] = nan;
+ break;
+ }
+
+ default:
+ PrintError("unknown concrete type: \"%s\"", lane_type.GetName());
+ return wabt::Result::Error;
+ }
+
+ out_value->value.value.Set<v128>(v);
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseConstValue(Type type,
+ Value* out_value,
+ ExpectedNan* out_nan,
+ string_view value_str,
+ AllowExpected allow_expected) {
+ *out_nan = ExpectedNan::None;
+
+ switch (type) {
+ case Type::I32: {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ out_value->Set(value);
+ break;
+ }
+
+ case Type::F32: {
+ uint32_t value_bits;
+ CHECK_RESULT(
+ ParseF32Value(&value_bits, out_nan, value_str, allow_expected));
+ out_value->Set(Bitcast<f32>(value_bits));
+ break;
+ }
+
+ case Type::I64: {
+ uint64_t value;
+ CHECK_RESULT(ParseI64Value(&value, value_str));
+ out_value->Set(value);
+ break;
+ }
+
+ case Type::F64: {
+ uint64_t value_bits;
+ CHECK_RESULT(
+ ParseF64Value(&value_bits, out_nan, value_str, allow_expected));
+ out_value->Set(Bitcast<f64>(value_bits));
+ break;
+ }
+
+ case Type::V128:
+ assert(false); // Should use ParseLaneConstValue instead.
+ break;
+
+ case Type::FuncRef:
+ if (value_str == "null") {
+ out_value->Set(Ref::Null);
+ } else {
+ assert(allow_expected == AllowExpected::Yes);
+ out_value->Set(Ref{1});
+ }
+ break;
+
+ case Type::ExternRef:
+ if (value_str == "null") {
+ out_value->Set(Ref::Null);
+ } else {
+ uint32_t value;
+ CHECK_RESULT(ParseI32Value(&value, value_str));
+ // TODO: hack, just whatever ref is at this index; but skip null (which
+ // is always 0).
+ out_value->Set(Ref{value + 1});
+ }
+ break;
+
+ default:
+ PrintError("unknown concrete type: \"%s\"", type.GetName());
+ return wabt::Result::Error;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseExpectedValue(ExpectedValue* out_value,
+ AllowExpected allow_expected) {
+ Type type;
+ std::string value_str;
+ EXPECT("{");
+ EXPECT_KEY("type");
+ CHECK_RESULT(ParseType(&type));
+ EXPECT(",");
+ if (type == Type::V128) {
+ Type lane_type;
+ EXPECT_KEY("lane_type");
+ CHECK_RESULT(ParseType(&lane_type));
+ EXPECT(",");
+ EXPECT_KEY("value");
+ EXPECT("[");
+
+ int lane_count = LaneCountFromType(lane_type);
+ for (int lane = 0; lane < lane_count; ++lane) {
+ CHECK_RESULT(ParseString(&value_str));
+ CHECK_RESULT(ParseLaneConstValue(lane_type, lane, out_value, value_str,
+ allow_expected));
+ if (lane < lane_count - 1) {
+ EXPECT(",");
+ }
+ }
+ EXPECT("]");
+ out_value->value.type = type;
+ out_value->lane_type = lane_type;
+ } else {
+ PARSE_KEY_STRING_VALUE("value", &value_str);
+ CHECK_RESULT(ParseConstValue(type, &out_value->value.value,
+ &out_value->nan[0], value_str,
+ allow_expected));
+ out_value->value.type = type;
+ }
+ EXPECT("}");
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseExpectedValues(
+ std::vector<ExpectedValue>* out_values) {
+ out_values->clear();
+ EXPECT("[");
+ bool first = true;
+ while (!Match("]")) {
+ if (!first) {
+ EXPECT(",");
+ }
+ ExpectedValue value;
+ CHECK_RESULT(ParseExpectedValue(&value, AllowExpected::Yes));
+ out_values->push_back(value);
+ first = false;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseConstVector(ValueTypes* out_types, Values* out_values) {
+ out_values->clear();
+ EXPECT("[");
+ bool first = true;
+ while (!Match("]")) {
+ if (!first) {
+ EXPECT(",");
+ }
+ TypedValue tv;
+ CHECK_RESULT(ParseConst(&tv));
+ out_types->push_back(tv.type);
+ out_values->push_back(tv.value);
+ first = false;
+ }
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::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->types, &out_action->args));
+ }
+ EXPECT("}");
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::ParseActionResult() {
+ // Not needed for wabt-interp, but useful for other parsers.
+ EXPECT_KEY("expected");
+ TypeVector expected;
+ CHECK_RESULT(ParseTypeVector(&expected));
+ return wabt::Result::Ok;
+}
+
+wabt::Result JSONParser::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 {
+ PrintError("unknown module type: \"%s\"", module_type_str.c_str());
+ return wabt::Result::Error;
+ }
+}
+
+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) {
+ string_view 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 JSONParser::ParseFilename(std::string* out_filename) {
+ PARSE_KEY_STRING_VALUE("filename", out_filename);
+ *out_filename = CreateModulePath(*out_filename);
+ return wabt::Result::Ok;
+}
+
+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));
+ EXPECT(",");
+ CHECK_RESULT(ParseActionResult());
+ *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(ParseExpectedValues(&command->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);
+ EXPECT(",");
+ CHECK_RESULT(ParseActionResult());
+ *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));
+ EXPECT(",");
+ PARSE_KEY_STRING_VALUE("text", &command->text);
+ EXPECT(",");
+ CHECK_RESULT(ParseActionResult());
+ *out_command = std::move(command);
+ } else {
+ PrintError("unknown command type");
+ return wabt::Result::Error;
+ }
+ EXPECT("}");
+ return wabt::Result::Ok;
+}
+
+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;
+}
+
+struct ActionResult {
+ ValueTypes types;
+ Values values;
+ Trap::Ptr trap;
+};
+
+class CommandRunner {
+ public:
+ CommandRunner();
+ wabt::Result Run(const Script& script);
+
+ int passed() const { return passed_; }
+ int total() const { return total_; }
+
+ private:
+ using ExportMap = std::map<std::string, Extern::Ptr>;
+ using Registry = std::map<std::string, ExportMap>;
+
+ void WABT_PRINTF_FORMAT(3, 4)
+ PrintError(uint32_t line_number, const char* format, ...);
+ ActionResult RunAction(int line_number,
+ const Action* action,
+ RunVerbosity verbose);
+
+ interp::Module::Ptr ReadModule(string_view module_filename, Errors* errors);
+ Extern::Ptr GetImport(const std::string&, const std::string&);
+ void PopulateImports(const interp::Module::Ptr&, RefVec*);
+ void PopulateExports(const Instance::Ptr&, ExportMap*);
+
+ 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*);
+ wabt::Result OnAssertTrapCommand(const AssertTrapCommand*);
+ wabt::Result OnAssertExhaustionCommand(const AssertExhaustionCommand*);
+
+ wabt::Result CheckAssertReturnResult(const AssertReturnCommand* command,
+ int index,
+ ExpectedValue expected,
+ TypedValue actual,
+ bool print_error);
+
+ void TallyCommand(wabt::Result);
+
+ wabt::Result ReadInvalidTextModule(string_view module_filename,
+ const std::string& header);
+ wabt::Result ReadInvalidModule(int line_number,
+ string_view module_filename,
+ ModuleType module_type,
+ const char* desc);
+ wabt::Result ReadUnlinkableModule(int line_number,
+ string_view module_filename,
+ ModuleType module_type,
+ const char* desc);
+
+ Store store_;
+ Registry registry_; // Used when importing.
+ Registry instances_; // Used when referencing module by name in invoke.
+ ExportMap last_instance_;
+ int passed_ = 0;
+ int total_ = 0;
+
+ std::string source_filename_;
+};
+
+CommandRunner::CommandRunner() : store_(s_features) {
+ auto&& spectest = registry_["spectest"];
+
+ // Initialize print functions for the spec test.
+ struct {
+ const char* name;
+ interp::FuncType type;
+ } const print_funcs[] = {
+ {"print", interp::FuncType{{}, {}}},
+ {"print_i32", interp::FuncType{{ValueType::I32}, {}}},
+ {"print_f32", interp::FuncType{{ValueType::F32}, {}}},
+ {"print_f64", interp::FuncType{{ValueType::F64}, {}}},
+ {"print_i32_f32", interp::FuncType{{ValueType::I32, ValueType::F32}, {}}},
+ {"print_f64_f64", interp::FuncType{{ValueType::F64, ValueType::F64}, {}}},
+ };
+
+ for (auto&& print : print_funcs) {
+ auto import_name = StringPrintf("spectest.%s", print.name);
+ spectest[print.name] = HostFunc::New(
+ store_, print.type,
+ [=](Thread& inst, const Values& params, Values& results,
+ Trap::Ptr* trap) -> wabt::Result {
+ printf("called host ");
+ WriteCall(s_stdout_stream.get(), import_name, print.type, params,
+ results, *trap);
+ return wabt::Result::Ok;
+ });
+ }
+
+ spectest["table"] =
+ interp::Table::New(store_, TableType{ValueType::FuncRef, Limits{10, 20}});
+
+ spectest["memory"] = interp::Memory::New(store_, MemoryType{Limits{1, 2}});
+
+ spectest["global_i32"] = interp::Global::New(
+ store_, GlobalType{ValueType::I32, Mutability::Const}, Value::Make(u32{666}));
+ spectest["global_i64"] = interp::Global::New(
+ store_, GlobalType{ValueType::I64, Mutability::Const}, Value::Make(u64{666}));
+ spectest["global_f32"] = interp::Global::New(
+ store_, GlobalType{ValueType::F32, Mutability::Const}, Value::Make(f32{666}));
+ spectest["global_f64"] = interp::Global::New(
+ store_, GlobalType{ValueType::F64, Mutability::Const}, Value::Make(f64{666}));
+}
+
+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:
+ TallyCommand(OnActionCommand(cast<ActionCommand>(command.get())));
+ break;
+
+ case CommandType::Register:
+ OnRegisterCommand(cast<RegisterCommand>(command.get()));
+ break;
+
+ case CommandType::AssertMalformed:
+ TallyCommand(OnAssertMalformedCommand(
+ cast<AssertMalformedCommand>(command.get())));
+ break;
+
+ case CommandType::AssertInvalid:
+ TallyCommand(
+ OnAssertInvalidCommand(cast<AssertInvalidCommand>(command.get())));
+ break;
+
+ case CommandType::AssertUnlinkable:
+ TallyCommand(OnAssertUnlinkableCommand(
+ cast<AssertUnlinkableCommand>(command.get())));
+ break;
+
+ case CommandType::AssertUninstantiable:
+ TallyCommand(OnAssertUninstantiableCommand(
+ cast<AssertUninstantiableCommand>(command.get())));
+ break;
+
+ case CommandType::AssertReturn:
+ TallyCommand(
+ OnAssertReturnCommand(cast<AssertReturnCommand>(command.get())));
+ break;
+
+ case CommandType::AssertTrap:
+ TallyCommand(
+ OnAssertTrapCommand(cast<AssertTrapCommand>(command.get())));
+ break;
+
+ case CommandType::AssertExhaustion:
+ TallyCommand(OnAssertExhaustionCommand(
+ cast<AssertExhaustionCommand>(command.get())));
+ break;
+ }
+ }
+
+ return wabt::Result::Ok;
+}
+
+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);
+}
+
+ActionResult CommandRunner::RunAction(int line_number,
+ const Action* action,
+ RunVerbosity verbose) {
+ ExportMap& module = !action->module_name.empty()
+ ? instances_[action->module_name]
+ : last_instance_;
+ Extern::Ptr extern_ = module[action->field_name];
+ if (!extern_) {
+ PrintError(line_number, "unknown invoke \"%s.%s\"",
+ action->module_name.c_str(), action->field_name.c_str());
+ return {};
+ }
+
+ ActionResult result;
+
+ switch (action->type) {
+ case ActionType::Invoke: {
+ auto* func = cast<interp::Func>(extern_.get());
+ func->Call(store_, action->args, result.values, &result.trap,
+ s_trace_stream);
+ result.types = func->type().results;
+ if (verbose == RunVerbosity::Verbose) {
+ WriteCall(s_stdout_stream.get(), action->field_name, func->type(),
+ action->args, result.values, result.trap);
+ }
+ break;
+ }
+
+ case ActionType::Get: {
+ auto* global = cast<interp::Global>(extern_.get());
+ result.values.push_back(global->Get());
+ result.types.push_back(global->type().type);
+ break;
+ }
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ return result;
+}
+
+wabt::Result CommandRunner::ReadInvalidTextModule(string_view module_filename,
+ const std::string& header) {
+ std::vector<uint8_t> file_data;
+ wabt::Result result = ReadFile(module_filename, &file_data);
+ std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer(
+ module_filename, file_data.data(), file_data.size());
+ Errors errors;
+ if (Succeeded(result)) {
+ std::unique_ptr<wabt::Module> module;
+ WastParseOptions options(s_features);
+ result = ParseWatModule(lexer.get(), &module, &errors, &options);
+ }
+
+ auto line_finder = lexer->MakeLineFinder();
+ FormatErrorsToFile(errors, Location::Type::Text, line_finder.get(), stdout,
+ header, PrintHeader::Once);
+ return result;
+}
+
+interp::Module::Ptr CommandRunner::ReadModule(string_view module_filename,
+ Errors* errors) {
+ std::vector<uint8_t> file_data;
+
+ if (Failed(ReadFile(module_filename, &file_data))) {
+ return {};
+ }
+
+ const bool kReadDebugNames = true;
+ const bool kStopOnFirstError = true;
+ const bool kFailOnCustomSectionError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames,
+ kStopOnFirstError, kFailOnCustomSectionError);
+ ModuleDesc module_desc;
+ if (Failed(ReadBinaryInterp(file_data.data(), file_data.size(), options,
+ errors, &module_desc))) {
+ return {};
+ }
+
+ if (s_verbose) {
+ module_desc.istream.Disassemble(s_stdout_stream.get());
+ }
+
+ return interp::Module::New(store_, module_desc);
+}
+
+wabt::Result CommandRunner::ReadInvalidModule(int line_number,
+ string_view module_filename,
+ 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: {
+ return ReadInvalidTextModule(module_filename, header);
+ }
+
+ case ModuleType::Binary: {
+ Errors errors;
+ auto module = ReadModule(module_filename, &errors);
+ if (!module) {
+ FormatErrorsToFile(errors, Location::Type::Binary, {}, stdout, header,
+ PrintHeader::Once);
+ return wabt::Result::Error;
+ } else {
+ return wabt::Result::Ok;
+ }
+ }
+ }
+
+ WABT_UNREACHABLE;
+}
+
+Extern::Ptr CommandRunner::GetImport(const std::string& module,
+ const std::string& name) {
+ auto mod_iter = registry_.find(module);
+ if (mod_iter != registry_.end()) {
+ auto extern_iter = mod_iter->second.find(name);
+ if (extern_iter != mod_iter->second.end()) {
+ return extern_iter->second;
+ }
+ }
+ return {};
+}
+
+void CommandRunner::PopulateImports(const interp::Module::Ptr& module,
+ RefVec* imports) {
+ for (auto&& import : module->desc().imports) {
+ auto extern_ = GetImport(import.type.module, import.type.name);
+ imports->push_back(extern_ ? extern_.ref() : Ref::Null);
+ }
+}
+
+void CommandRunner::PopulateExports(const Instance::Ptr& instance,
+ ExportMap* map) {
+ map->clear();
+ interp::Module::Ptr module{store_, instance->module()};
+ for (size_t i = 0; i < module->export_types().size(); ++i) {
+ const ExportType& export_type = module->export_types()[i];
+ (*map)[export_type.name] = store_.UnsafeGet<Extern>(instance->exports()[i]);
+ }
+}
+
+wabt::Result CommandRunner::OnModuleCommand(const ModuleCommand* command) {
+ Errors errors;
+ auto module = ReadModule(command->filename, &errors);
+ FormatErrorsToFile(errors, Location::Type::Binary);
+
+ if (!module) {
+ PrintError(command->line, "error reading module: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ RefVec imports;
+ PopulateImports(module, &imports);
+
+ Trap::Ptr trap;
+ auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap);
+ if (trap) {
+ assert(!instance);
+ PrintError(command->line, "error instantiating module: \"%s\"",
+ trap->message().c_str());
+ return wabt::Result::Error;
+ }
+
+ PopulateExports(instance, &last_instance_);
+ if (!command->name.empty()) {
+ instances_[command->name] = last_instance_;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnActionCommand(const ActionCommand* command) {
+ ActionResult result =
+ RunAction(command->line, &command->action, RunVerbosity::Verbose);
+
+ if (result.trap) {
+ PrintError(command->line, "unexpected trap: %s",
+ result.trap->message().c_str());
+ return wabt::Result::Error;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnAssertMalformedCommand(
+ const AssertMalformedCommand* command) {
+ wabt::Result result = ReadInvalidModule(command->line, command->filename,
+ command->type, "assert_malformed");
+ if (Succeeded(result)) {
+ PrintError(command->line, "expected module to be malformed: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnRegisterCommand(const RegisterCommand* command) {
+ if (!command->name.empty()) {
+ auto instance_iter = instances_.find(command->name);
+ if (instance_iter == instances_.end()) {
+ PrintError(command->line, "unknown module in register");
+ return wabt::Result::Error;
+ }
+ registry_[command->as] = instance_iter->second;
+ } else {
+ registry_[command->as] = last_instance_;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnAssertUnlinkableCommand(
+ const AssertUnlinkableCommand* command) {
+ Errors errors;
+ auto module = ReadModule(command->filename, &errors);
+
+ if (!module) {
+ PrintError(command->line, "unable to compile unlinkable module: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ RefVec imports;
+ PopulateImports(module, &imports);
+
+ Trap::Ptr trap;
+ auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap);
+ if (!trap) {
+ PrintError(command->line, "expected module to be unlinkable: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ // TODO: Change to one-line error.
+ PrintError(command->line, "assert_unlinkable passed:\n error: %s",
+ trap->message().c_str());
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnAssertInvalidCommand(
+ const AssertInvalidCommand* command) {
+ wabt::Result result = ReadInvalidModule(command->line, command->filename,
+ command->type, "assert_invalid");
+ if (Succeeded(result)) {
+ PrintError(command->line, "expected module to be invalid: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnAssertUninstantiableCommand(
+ const AssertUninstantiableCommand* command) {
+ Errors errors;
+ auto module = ReadModule(command->filename, &errors);
+
+ if (!module) {
+ PrintError(command->line, "unable to compile uninstantiable module: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ RefVec imports;
+ PopulateImports(module, &imports);
+
+ Trap::Ptr trap;
+ auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap);
+ if (!trap) {
+ PrintError(command->line, "expected module to be uninstantiable: \"%s\"",
+ command->filename.c_str());
+ return wabt::Result::Error;
+ }
+
+ // TODO: print error when assertion passes.
+#if 0
+ PrintError(command->line, "assert_uninstantiable passed: %s",
+ trap->message().c_str());
+#endif
+ return wabt::Result::Ok;
+}
+
+static bool WABT_VECTORCALL IsCanonicalNan(f32 val) {
+ const u32 kQuietNan = 0x7fc00000U;
+ const u32 kQuietNegNan = 0xffc00000U;
+ u32 bits = Bitcast<u32>(val);
+ return bits == kQuietNan || bits == kQuietNegNan;
+}
+
+static bool WABT_VECTORCALL IsCanonicalNan(f64 val) {
+ const u64 kQuietNan = 0x7ff8000000000000ULL;
+ const u64 kQuietNegNan = 0xfff8000000000000ULL;
+ u64 bits = Bitcast<u64>(val);
+ return bits == kQuietNan || bits == kQuietNegNan;
+}
+
+static bool WABT_VECTORCALL IsArithmeticNan(f32 val) {
+ const u32 kQuietNan = 0x7fc00000U;
+ return (Bitcast<u32>(val) & kQuietNan) == kQuietNan;
+}
+
+static bool WABT_VECTORCALL IsArithmeticNan(f64 val) {
+ const u64 kQuietNan = 0x7ff8000000000000ULL;
+ return (Bitcast<u64>(val) & kQuietNan) == kQuietNan;
+}
+
+static std::string ExpectedValueToString(const ExpectedValue& ev) {
+ // Extend TypedValueToString to print expected nan values too.
+ switch (ev.value.type) {
+ case Type::F32:
+ case Type::F64:
+ switch (ev.nan[0]) {
+ case ExpectedNan::None:
+ return TypedValueToString(ev.value);
+
+ case ExpectedNan::Arithmetic:
+ return StringPrintf("%s:nan:arithmetic", ev.value.type.GetName());
+
+ case ExpectedNan::Canonical:
+ return StringPrintf("%s:nan:canonical", ev.value.type.GetName());
+ }
+ break;
+
+ case Type::V128: {
+ int lane_count = LaneCountFromType(ev.lane_type);
+ std::string result = "v128 ";
+ for (int lane = 0; lane < lane_count; ++lane) {
+ result += ExpectedValueToString(GetLane(ev, lane));
+ }
+ return result;
+ }
+
+ default:
+ break;
+ }
+ return TypedValueToString(ev.value);
+}
+
+wabt::Result CommandRunner::CheckAssertReturnResult(
+ const AssertReturnCommand* command,
+ int index,
+ ExpectedValue expected,
+ TypedValue actual,
+ bool print_error) {
+ assert(expected.value.type == actual.type ||
+ IsReference(expected.value.type));
+ bool ok = true;
+ switch (expected.value.type) {
+ case Type::I8:
+ case Type::I16:
+ case Type::I32:
+ ok = expected.value.value.Get<u32>() == actual.value.Get<u32>();
+ break;
+
+ case Type::I64:
+ ok = expected.value.value.Get<u64>() == actual.value.Get<u64>();
+ break;
+
+ case Type::F32:
+ switch (expected.nan[0]) {
+ case ExpectedNan::Arithmetic:
+ ok = IsArithmeticNan(actual.value.Get<f32>());
+ break;
+
+ case ExpectedNan::Canonical:
+ ok = IsCanonicalNan(actual.value.Get<f32>());
+ break;
+
+ case ExpectedNan::None:
+ ok = Bitcast<u32>(expected.value.value.Get<f32>()) ==
+ Bitcast<u32>(actual.value.Get<f32>());
+ break;
+ }
+ break;
+
+ case Type::F64:
+ switch (expected.nan[0]) {
+ case ExpectedNan::Arithmetic:
+ ok = IsArithmeticNan(actual.value.Get<f64>());
+ break;
+
+ case ExpectedNan::Canonical:
+ ok = IsCanonicalNan(actual.value.Get<f64>());
+ break;
+
+ case ExpectedNan::None:
+ ok = Bitcast<u64>(expected.value.value.Get<f64>()) ==
+ Bitcast<u64>(actual.value.Get<f64>());
+ break;
+ }
+ break;
+
+ case Type::V128: {
+ // Compare each lane as if it were its own value.
+ for (int lane = 0; lane < LaneCountFromType(expected.lane_type); ++lane) {
+ ExpectedValue lane_expected = GetLane(expected, lane);
+ TypedValue lane_actual = GetLane(actual, expected.lane_type, lane);
+
+ if (Failed(CheckAssertReturnResult(command, index, lane_expected,
+ lane_actual, false))) {
+ PrintError(command->line,
+ "mismatch in lane %u of result %u of assert_return: "
+ "expected %s, got %s",
+ lane, index, ExpectedValueToString(lane_expected).c_str(),
+ TypedValueToString(lane_actual).c_str());
+ ok = false;
+ }
+ }
+ break;
+ }
+
+ case Type::FuncRef:
+ // A funcref expectation only requires that the reference be a function,
+ // but it doesn't check the actual index.
+ ok = (actual.type == Type::FuncRef);
+ break;
+
+ case Type::ExternRef:
+ ok = expected.value.value.Get<Ref>() == actual.value.Get<Ref>();
+ break;
+
+ default:
+ WABT_UNREACHABLE;
+ }
+
+ if (!ok && print_error) {
+ PrintError(command->line,
+ "mismatch in result %u of assert_return: expected %s, got %s",
+ index, ExpectedValueToString(expected).c_str(),
+ TypedValueToString(actual).c_str());
+ }
+ return ok ? wabt::Result::Ok : wabt::Result::Error;
+}
+
+wabt::Result CommandRunner::OnAssertReturnCommand(
+ const AssertReturnCommand* command) {
+ ActionResult action_result =
+ RunAction(command->line, &command->action, RunVerbosity::Quiet);
+
+ if (action_result.trap) {
+ PrintError(command->line, "unexpected trap: %s",
+ action_result.trap->message().c_str());
+ return wabt::Result::Error;
+ }
+
+ if (action_result.values.size() != command->expected.size()) {
+ PrintError(command->line,
+ "result length mismatch in assert_return: expected %" PRIzd
+ ", got %" PRIzd,
+ command->expected.size(), action_result.values.size());
+ return wabt::Result::Error;
+ }
+
+ wabt::Result result = wabt::Result::Ok;
+ for (size_t i = 0; i < action_result.values.size(); ++i) {
+ const ExpectedValue& expected = command->expected[i];
+ TypedValue actual{action_result.types[i], action_result.values[i]};
+
+ result |= CheckAssertReturnResult(command, i, expected, actual, true);
+ }
+
+ return result;
+}
+
+wabt::Result CommandRunner::OnAssertTrapCommand(
+ const AssertTrapCommand* command) {
+ ActionResult result =
+ RunAction(command->line, &command->action, RunVerbosity::Quiet);
+ if (!result.trap) {
+ PrintError(command->line, "expected trap: \"%s\"", command->text.c_str());
+ return wabt::Result::Error;
+ }
+
+ PrintError(command->line, "assert_trap passed: %s",
+ result.trap->message().c_str());
+ return wabt::Result::Ok;
+}
+
+wabt::Result CommandRunner::OnAssertExhaustionCommand(
+ const AssertExhaustionCommand* command) {
+ ActionResult result =
+ RunAction(command->line, &command->action, RunVerbosity::Quiet);
+ if (!result.trap || result.trap->message() != "call stack exhausted") {
+ PrintError(command->line, "expected trap: \"%s\"", command->text.c_str());
+ return wabt::Result::Error;
+ }
+
+ // TODO: print message when assertion passes.
+#if 0
+ PrintError(command->line, "assert_exhaustion passed: %s",
+ result.trap->message().c_str());
+#endif
+ return wabt::Result::Ok;
+}
+
+void CommandRunner::TallyCommand(wabt::Result result) {
+ if (Succeeded(result)) {
+ passed_++;
+ }
+ total_++;
+}
+
+static int ReadAndRunSpecJSON(string_view spec_json_filename) {
+ JSONParser parser;
+ if (parser.ReadFile(spec_json_filename) == wabt::Result::Error) {
+ return 1;
+ }
+
+ Script script;
+ if (parser.ParseScript(&script) == wabt::Result::Error) {
+ return 1;
+ }
+
+ CommandRunner runner;
+ if (runner.Run(script) == wabt::Result::Error) {
+ return 1;
+ }
+
+ printf("%d/%d tests passed.\n", runner.passed(), runner.total());
+ const int failed = runner.total() - runner.passed();
+ return failed;
+}
+
+} // namespace spectest
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+ s_stdout_stream = FileStream::CreateStdout();
+
+ ParseOptions(argc, argv);
+ return spectest::ReadAndRunSpecJSON(s_infile);
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-decompile.cc b/third_party/wasm2c/src/tools/wasm-decompile.cc
new file mode 100644
index 0000000000..74491e55c3
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-decompile.cc
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019 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 <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+
+#include "src/apply-names.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-ir.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+#include "src/decompiler.h"
+
+using namespace wabt;
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+
+ std::string infile;
+ std::string outfile;
+ Features features;
+ DecompileOptions decompile_options;
+ bool fail_on_custom_section_error = true;
+
+ {
+ const char s_description[] =
+ " Read a file in the WebAssembly binary format, and convert it to\n"
+ " a decompiled text file.\n"
+ "\n"
+ "examples:\n"
+ " # parse binary file test.wasm and write text file test.dcmp\n"
+ " $ wasm-decompile test.wasm -o test.dcmp\n";
+ OptionParser parser("wasm-decompile", s_description);
+ parser.AddOption(
+ 'o', "output", "FILENAME",
+ "Output file for the decompiled file, by default use stdout",
+ [&](const char* argument) {
+ outfile = argument;
+ ConvertBackslashToSlash(&outfile);
+ });
+ features.AddOptions(&parser);
+ parser.AddOption("ignore-custom-section-errors",
+ "Ignore errors in custom sections",
+ [&]() { fail_on_custom_section_error = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [&](const char* argument) {
+ infile = argument;
+ ConvertBackslashToSlash(&infile);
+ });
+ parser.Parse(argc, argv);
+ }
+
+ std::vector<uint8_t> file_data;
+ Result result = ReadFile(infile.c_str(), &file_data);
+ if (Succeeded(result)) {
+ Errors errors;
+ Module module;
+ const bool kStopOnFirstError = true;
+ ReadBinaryOptions options(features, nullptr,
+ true, kStopOnFirstError,
+ fail_on_custom_section_error);
+ result = ReadBinaryIr(infile.c_str(), file_data.data(), file_data.size(),
+ options, &errors, &module);
+ if (Succeeded(result)) {
+ ValidateOptions options(features);
+ result = ValidateModule(&module, &errors, options);
+ if (Succeeded(result)) {
+ result = GenerateNames(&module,
+ static_cast<NameOpts>(NameOpts::AlphaNames));
+ }
+ if (Succeeded(result)) {
+ // Must be called after ReadBinaryIr & GenerateNames, and before
+ // ApplyNames, see comments at definition.
+ RenameAll(module);
+ }
+ if (Succeeded(result)) {
+ /* TODO(binji): This shouldn't fail; if a name can't be applied
+ * (because the index is invalid, say) it should just be skipped. */
+ Result dummy_result = ApplyNames(&module);
+ WABT_USE(dummy_result);
+ }
+ if (Succeeded(result)) {
+ auto s = Decompile(module, decompile_options);
+ FileStream stream(!outfile.empty() ? FileStream(outfile)
+ : FileStream(stdout));
+ stream.WriteData(s.data(), s.size());
+ }
+ }
+ FormatErrorsToFile(errors, Location::Type::Binary);
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-interp.cc b/third_party/wasm2c/src/tools/wasm-interp.cc
new file mode 100644
index 0000000000..e349a9e323
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-interp.cc
@@ -0,0 +1,337 @@
+/*
+ * 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 <cstdio>
+#include <cstdlib>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "src/binary-reader.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/interp/binary-reader-interp.h"
+#include "src/interp/interp-util.h"
+#include "src/interp/interp-wasi.h"
+#include "src/interp/interp.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+
+#ifdef WITH_WASI
+#include "uvwasi.h"
+#endif
+
+using namespace wabt;
+using namespace wabt::interp;
+
+static int s_verbose;
+static const char* s_infile;
+static Thread::Options s_thread_options;
+static Stream* s_trace_stream;
+static bool s_run_all_exports;
+static bool s_host_print;
+static bool s_dummy_import_func;
+static Features s_features;
+static bool s_wasi;
+static std::vector<std::string> s_wasi_env;
+static std::vector<std::string> s_wasi_argv;
+static std::vector<std::string> s_wasi_dirs;
+
+static std::unique_ptr<FileStream> s_log_stream;
+static std::unique_ptr<FileStream> s_stdout_stream;
+static std::unique_ptr<FileStream> s_stderr_stream;
+
+static Store s_store;
+
+static const char s_description[] =
+ R"( read a file in the wasm binary format, and run in it a stack-based
+ interpreter.
+
+examples:
+ # parse binary file test.wasm, and type-check it
+ $ wasm-interp test.wasm
+
+ # parse test.wasm and run all its exported functions
+ $ wasm-interp test.wasm --run-all-exports
+
+ # parse test.wasm, run the exported functions and trace the output
+ $ wasm-interp test.wasm --run-all-exports --trace
+
+ # 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
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm-interp", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ 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_stream = s_stdout_stream.get(); });
+ parser.AddOption("wasi",
+ "Assume input module is WASI compliant (Export "
+ " WASI API the the module and invoke _start function)",
+ []() { s_wasi = true; });
+ parser.AddOption(
+ 'e', "env", "ENV",
+ "Pass the given environment string in the WASI runtime",
+ [](const std::string& argument) { s_wasi_env.push_back(argument); });
+ parser.AddOption(
+ 'd', "dir", "DIR", "Pass the given directory the the WASI runtime",
+ [](const std::string& argument) { s_wasi_dirs.push_back(argument); });
+ 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.AddOption(
+ "dummy-import-func",
+ "Provide a dummy implementation of all imported functions. The function "
+ "will log the call and return an appropriate zero value.",
+ []() { s_dummy_import_func = true; });
+
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+ parser.AddArgument(
+ "arg", OptionParser::ArgumentCount::ZeroOrMore,
+ [](const char* argument) { s_wasi_argv.push_back(argument); });
+ parser.Parse(argc, argv);
+}
+
+Result RunAllExports(const Instance::Ptr& instance, Errors* errors) {
+ Result result = Result::Ok;
+
+ auto module = s_store.UnsafeGet<Module>(instance->module());
+ auto&& module_desc = module->desc();
+
+ for (auto&& export_ : module_desc.exports) {
+ if (export_.type.type->kind != ExternalKind::Func) {
+ continue;
+ }
+ auto* func_type = cast<FuncType>(export_.type.type.get());
+ if (func_type->params.empty()) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef(">>> running export \"%s\":\n",
+ export_.type.name.c_str());
+ }
+ auto func = s_store.UnsafeGet<Func>(instance->funcs()[export_.index]);
+ Values params;
+ Values results;
+ Trap::Ptr trap;
+ result |= func->Call(s_store, params, results, &trap, s_trace_stream);
+ WriteCall(s_stdout_stream.get(), export_.type.name, *func_type, params,
+ results, trap);
+ }
+ }
+
+ return result;
+}
+
+static void BindImports(const Module::Ptr& module, RefVec& imports) {
+ auto* stream = s_stdout_stream.get();
+
+ for (auto&& import : module->desc().imports) {
+ if (import.type.type->kind == ExternKind::Func &&
+ ((s_host_print && import.type.module == "host" &&
+ import.type.name == "print") ||
+ s_dummy_import_func)) {
+ auto func_type = *cast<FuncType>(import.type.type.get());
+ auto import_name = StringPrintf("%s.%s", import.type.module.c_str(),
+ import.type.name.c_str());
+
+ auto host_func =
+ HostFunc::New(s_store, func_type,
+ [=](Thread& thread, const Values& params,
+ Values& results, Trap::Ptr* trap) -> Result {
+ printf("called host ");
+ WriteCall(stream, import_name, func_type, params,
+ results, *trap);
+ return Result::Ok;
+ });
+ imports.push_back(host_func.ref());
+ continue;
+ }
+
+ // By default, just push an null reference. This won't resolve, and
+ // instantiation will fail.
+ imports.push_back(Ref::Null);
+ }
+}
+
+static Result ReadModule(const char* module_filename,
+ Errors* errors,
+ Module::Ptr* out_module) {
+ auto* stream = s_stdout_stream.get();
+ std::vector<uint8_t> file_data;
+ CHECK_RESULT(ReadFile(module_filename, &file_data));
+
+ ModuleDesc module_desc;
+ const bool kReadDebugNames = true;
+ const bool kStopOnFirstError = true;
+ const bool kFailOnCustomSectionError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames,
+ kStopOnFirstError, kFailOnCustomSectionError);
+ CHECK_RESULT(ReadBinaryInterp(file_data.data(), file_data.size(), options,
+ errors, &module_desc));
+
+ if (s_verbose) {
+ module_desc.istream.Disassemble(stream);
+ }
+
+ *out_module = Module::New(s_store, module_desc);
+ return Result::Ok;
+}
+
+static Result InstantiateModule(RefVec& imports,
+ const Module::Ptr& module,
+ Instance::Ptr* out_instance) {
+ RefPtr<Trap> trap;
+ *out_instance = Instance::Instantiate(s_store, module.ref(), imports, &trap);
+ if (!*out_instance) {
+ WriteTrap(s_stderr_stream.get(), "error initializing module", trap);
+ return Result::Error;
+ }
+ return Result::Ok;
+}
+
+static Result ReadAndRunModule(const char* module_filename) {
+ Errors errors;
+ Module::Ptr module;
+ Result result = ReadModule(module_filename, &errors, &module);
+ if (!Succeeded(result)) {
+ FormatErrorsToFile(errors, Location::Type::Binary);
+ return result;
+ }
+
+ RefVec imports;
+
+#if WITH_WASI
+ uvwasi_t uvwasi;
+#endif
+
+ if (s_wasi) {
+#if WITH_WASI
+ uvwasi_errno_t err;
+ uvwasi_options_t init_options;
+
+ std::vector<const char*> argv;
+ argv.push_back(module_filename);
+ for (auto& s : s_wasi_argv) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef("wasi: arg: \"%s\"\n", s.c_str());
+ }
+ argv.push_back(s.c_str());
+ }
+ argv.push_back(nullptr);
+
+ std::vector<const char*> envp;
+ for (auto& s : s_wasi_env) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef("wasi: env: \"%s\"\n", s.c_str());
+ }
+ envp.push_back(s.c_str());
+ }
+ envp.push_back(nullptr);
+
+ std::vector<uvwasi_preopen_t> dirs;
+ for (auto& dir : s_wasi_dirs) {
+ if (s_trace_stream) {
+ s_trace_stream->Writef("wasi: dir: \"%s\"\n", dir.c_str());
+ }
+ dirs.push_back({dir.c_str(), dir.c_str()});
+ }
+
+ /* Setup the initialization options. */
+ init_options.in = 0;
+ init_options.out = 1;
+ init_options.err = 2;
+ init_options.fd_table_size = 3;
+ init_options.argc = argv.size() - 1;
+ init_options.argv = argv.data();
+ init_options.envp = envp.data();
+ init_options.preopenc = dirs.size();
+ init_options.preopens = dirs.data();
+ init_options.allocator = NULL;
+
+ err = uvwasi_init(&uvwasi, &init_options);
+ if (err != UVWASI_ESUCCESS) {
+ s_stderr_stream.get()->Writef("error initialiazing uvwasi: %d\n", err);
+ return Result::Error;
+ }
+ CHECK_RESULT(WasiBindImports(module, imports, s_stderr_stream.get(),
+ s_trace_stream));
+#else
+ s_stderr_stream.get()->Writef("wasi support not compiled in\n");
+ return Result::Error;
+#endif
+ } else {
+ BindImports(module, imports);
+ }
+ BindImports(module, imports);
+
+ Instance::Ptr instance;
+ CHECK_RESULT(InstantiateModule(imports, module, &instance));
+
+ if (s_run_all_exports) {
+ RunAllExports(instance, &errors);
+ }
+#ifdef WITH_WASI
+ if (s_wasi) {
+ CHECK_RESULT(
+ WasiRunStart(instance, &uvwasi, s_stderr_stream.get(), s_trace_stream));
+ }
+#endif
+
+ return Result::Ok;
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+ s_stdout_stream = FileStream::CreateStdout();
+ s_stderr_stream = FileStream::CreateStderr();
+
+ ParseOptions(argc, argv);
+
+ wabt::Result result = ReadAndRunModule(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/third_party/wasm2c/src/tools/wasm-objdump.cc b/third_party/wasm2c/src/tools/wasm-objdump.cc
new file mode 100644
index 0000000000..fbfed12cf1
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-objdump.cc
@@ -0,0 +1,149 @@
+/*
+ * 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 <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include "src/common.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-objdump.h"
+
+using namespace wabt;
+
+static const char s_description[] =
+R"( Print information about the contents of wasm binaries.
+
+examples:
+ $ wasm-objdump test.wasm
+)";
+
+static ObjdumpOptions s_objdump_options;
+
+static std::vector<const char*> s_infiles;
+
+static std::unique_ptr<FileStream> s_log_stream;
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm-objdump", s_description);
+
+ parser.AddOption('h', "headers", "Print headers",
+ []() { s_objdump_options.headers = true; });
+ parser.AddOption(
+ 'j', "section", "SECTION", "Select just one section",
+ [](const char* argument) { s_objdump_options.section_name = argument; });
+ parser.AddOption('s', "full-contents", "Print raw section contents",
+ []() { s_objdump_options.raw = true; });
+ parser.AddOption('d', "disassemble", "Disassemble function bodies",
+ []() { s_objdump_options.disassemble = true; });
+ parser.AddOption("debug", "Print extra debug information", []() {
+ s_objdump_options.debug = true;
+ s_log_stream = FileStream::CreateStderr();
+ s_objdump_options.log_stream = s_log_stream.get();
+ });
+ parser.AddOption('x', "details", "Show section details",
+ []() { s_objdump_options.details = true; });
+ parser.AddOption('r', "reloc", "Show relocations inline with disassembly",
+ []() { s_objdump_options.relocs = true; });
+ parser.AddOption(0, "section-offsets",
+ "Print section offsets instead of file offsets "
+ "in code disassembly",
+ []() { s_objdump_options.section_offsets = true; });
+ parser.AddArgument(
+ "filename", OptionParser::ArgumentCount::OneOrMore,
+ [](const char* argument) { s_infiles.push_back(argument); });
+
+ parser.Parse(argc, argv);
+}
+
+Result dump_file(const char* filename) {
+ std::vector<uint8_t> file_data;
+ CHECK_RESULT(ReadFile(filename, &file_data));
+
+ uint8_t* data = file_data.data();
+ size_t size = file_data.size();
+
+ // Perform serveral passed over the binary in order to print out different
+ // types of information.
+ s_objdump_options.filename = filename;
+ printf("\n");
+
+ ObjdumpState state;
+
+ Result result = Result::Ok;
+
+ // Pass 0: Prepass
+ s_objdump_options.mode = ObjdumpMode::Prepass;
+ result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state);
+ s_objdump_options.log_stream = nullptr;
+
+ // Pass 1: Print the section headers
+ if (s_objdump_options.headers) {
+ s_objdump_options.mode = ObjdumpMode::Headers;
+ result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state);
+ }
+
+ // Pass 2: Print extra information based on section type
+ if (s_objdump_options.details) {
+ s_objdump_options.mode = ObjdumpMode::Details;
+ result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state);
+ }
+
+ // Pass 3: Disassemble code section
+ if (s_objdump_options.disassemble) {
+ s_objdump_options.mode = ObjdumpMode::Disassemble;
+ result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state);
+ }
+
+ // Pass 4: Dump to raw contents of the sections
+ if (s_objdump_options.raw) {
+ s_objdump_options.mode = ObjdumpMode::RawData;
+ result |= ReadBinaryObjdump(data, size, &s_objdump_options, &state);
+ }
+
+ return result;
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+
+ ParseOptions(argc, argv);
+ if (!s_objdump_options.headers && !s_objdump_options.details &&
+ !s_objdump_options.disassemble && !s_objdump_options.raw) {
+ fprintf(stderr, "At least one of the following switches must be given:\n");
+ fprintf(stderr, " -d/--disassemble\n");
+ fprintf(stderr, " -h/--headers\n");
+ fprintf(stderr, " -x/--details\n");
+ fprintf(stderr, " -s/--full-contents\n");
+ return 1;
+ }
+
+ for (const char* filename: s_infiles) {
+ if (Failed(dump_file(filename))) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-opcodecnt.cc b/third_party/wasm2c/src/tools/wasm-opcodecnt.cc
new file mode 100644
index 0000000000..5d6225f098
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-opcodecnt.cc
@@ -0,0 +1,188 @@
+/*
+ * 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 <cerrno>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+#include <map>
+#include <vector>
+
+#include "src/binary-reader.h"
+#include "src/binary-reader-opcnt.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+
+#define ERROR(fmt, ...) \
+ fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__)
+
+using namespace wabt;
+
+static int s_verbose;
+static const char* s_infile;
+static const char* s_outfile;
+static size_t s_cutoff = 0;
+static const char* s_separator = ": ";
+
+static ReadBinaryOptions s_read_binary_options;
+static std::unique_ptr<FileStream> s_log_stream;
+static Features s_features;
+
+static const char s_description[] =
+R"( Read a file in the wasm binary format, and count opcode usage for
+ instructions.
+
+examples:
+ # parse binary file test.wasm and write pcode dist file test.dist
+ $ wasm-opcodecnt test.wasm -o test.dist
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm-opcodecnt", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ s_read_binary_options.log_stream = s_log_stream.get();
+ });
+ s_features.AddOptions(&parser);
+ parser.AddOption('o', "output", "FILENAME",
+ "Output file for the opcode counts, by default use stdout",
+ [](const char* argument) { s_outfile = argument; });
+ parser.AddOption(
+ 'c', "cutoff", "N", "Cutoff for reporting counts less than N",
+ [](const std::string& argument) { s_cutoff = atol(argument.c_str()); });
+ parser.AddOption(
+ 's', "separator", "SEPARATOR",
+ "Separator text between element and count when reporting counts",
+ [](const char* argument) { s_separator = argument; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::OneOrMore,
+ [](const char* argument) { s_infile = argument; });
+ parser.Parse(argc, argv);
+}
+
+template <typename T>
+struct SortByCountDescending {
+ bool operator()(const T& lhs, const T& rhs) const {
+ return lhs.second > rhs.second;
+ }
+};
+
+template <typename T>
+struct WithinCutoff {
+ bool operator()(const T& pair) const {
+ return pair.second >= s_cutoff;
+ }
+};
+
+static size_t SumCounts(const OpcodeInfoCounts& info_counts) {
+ size_t sum = 0;
+ for (auto& pair : info_counts) {
+ sum += pair.second;
+ }
+ return sum;
+}
+
+void WriteCounts(Stream& stream, const OpcodeInfoCounts& info_counts) {
+ typedef std::pair<Opcode, size_t> OpcodeCountPair;
+
+ std::map<Opcode, size_t> counts;
+ for (auto& info_count_pair: info_counts) {
+ Opcode opcode = info_count_pair.first.opcode();
+ size_t count = info_count_pair.second;
+ counts[opcode] += count;
+ }
+
+ std::vector<OpcodeCountPair> sorted;
+ std::copy_if(counts.begin(), counts.end(), std::back_inserter(sorted),
+ WithinCutoff<OpcodeCountPair>());
+
+ // Use a stable sort to keep the elements with the same count in opcode
+ // order (since the Opcode map is sorted).
+ std::stable_sort(sorted.begin(), sorted.end(),
+ SortByCountDescending<OpcodeCountPair>());
+
+ for (auto& pair : sorted) {
+ Opcode opcode = pair.first;
+ size_t count = pair.second;
+ stream.Writef("%s%s%" PRIzd "\n", opcode.GetName(), s_separator, count);
+ }
+}
+
+void WriteCountsWithImmediates(Stream& stream,
+ const OpcodeInfoCounts& counts) {
+ // Remove const from the key type so we can sort below.
+ typedef std::pair<std::remove_const<OpcodeInfoCounts::key_type>::type,
+ OpcodeInfoCounts::mapped_type>
+ OpcodeInfoCountPair;
+
+ std::vector<OpcodeInfoCountPair> sorted;
+ std::copy_if(counts.begin(), counts.end(), std::back_inserter(sorted),
+ WithinCutoff<OpcodeInfoCountPair>());
+
+ // Use a stable sort to keep the elements with the same count in opcode info
+ // order (since the OpcodeInfoCounts map is sorted).
+ std::stable_sort(sorted.begin(), sorted.end(),
+ SortByCountDescending<OpcodeInfoCountPair>());
+
+ for (auto& pair : sorted) {
+ auto&& info = pair.first;
+ size_t count = pair.second;
+ info.Write(stream);
+ stream.Writef("%s%" PRIzd "\n", s_separator, count);
+ }
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ Result result = ReadFile(s_infile, &file_data);
+ if (Failed(result)) {
+ const char* input_name = s_infile ? s_infile : "stdin";
+ ERROR("Unable to parse: %s", input_name);
+ return 1;
+ }
+
+ FileStream stream(s_outfile ? FileStream(s_outfile) : FileStream(stdout));
+
+ if (Succeeded(result)) {
+ OpcodeInfoCounts counts;
+ s_read_binary_options.features = s_features;
+ result = ReadBinaryOpcnt(file_data.data(), file_data.size(),
+ s_read_binary_options, &counts);
+ if (Succeeded(result)) {
+ stream.Writef("Total opcodes: %" PRIzd "\n\n", SumCounts(counts));
+
+ stream.Writef("Opcode counts:\n");
+ WriteCounts(stream, counts);
+
+ stream.Writef("\nOpcode counts with immediates:\n");
+ WriteCountsWithImmediates(stream, counts);
+ }
+ }
+
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-strip.cc b/third_party/wasm2c/src/tools/wasm-strip.cc
new file mode 100644
index 0000000000..f838672064
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-strip.cc
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2018 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 "src/binary.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-nop.h"
+#include "src/error-formatter.h"
+#include "src/leb128.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+
+using namespace wabt;
+
+static std::string s_filename;
+
+static const char s_description[] =
+R"( Remove sections of a WebAssembly binary file.
+
+examples:
+ # Remove all custom sections from test.wasm
+ $ wasm-strip test.wasm
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm-strip", s_description);
+
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) {
+ s_filename = argument;
+ ConvertBackslashToSlash(&s_filename);
+ });
+ parser.Parse(argc, argv);
+}
+
+class BinaryReaderStrip : public BinaryReaderNop {
+ public:
+ explicit BinaryReaderStrip(Errors* errors)
+ : errors_(errors) {
+ stream_.WriteU32(WABT_BINARY_MAGIC, "WASM_BINARY_MAGIC");
+ stream_.WriteU32(WABT_BINARY_VERSION, "WASM_BINARY_VERSION");
+ }
+
+ bool OnError(const Error& error) override {
+ errors_->push_back(error);
+ return true;
+ }
+
+ Result BeginSection(Index section_index,
+ BinarySection section_type,
+ Offset size) override {
+ if (section_type == BinarySection::Custom) {
+ return Result::Ok;
+ }
+ stream_.WriteU8Enum(section_type, "section code");
+ WriteU32Leb128(&stream_, size, "section size");
+ stream_.WriteData(state->data + state->offset, size, "section data");
+ return Result::Ok;
+ }
+
+ Result WriteToFile(string_view filename) {
+ return stream_.WriteToFile(filename);
+ }
+
+ private:
+ MemoryStream stream_;
+ Errors* errors_;
+};
+
+int ProgramMain(int argc, char** argv) {
+ Result result;
+
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ result = ReadFile(s_filename.c_str(), &file_data);
+ if (Succeeded(result)) {
+ Errors errors;
+ Features features;
+ const bool kReadDebugNames = false;
+ const bool kStopOnFirstError = true;
+ const bool kFailOnCustomSectionError = false;
+ ReadBinaryOptions options(features, nullptr, kReadDebugNames,
+ kStopOnFirstError, kFailOnCustomSectionError);
+
+ BinaryReaderStrip reader(&errors);
+ result = ReadBinary(file_data.data(), file_data.size(), &reader, options);
+ FormatErrorsToFile(errors, Location::Type::Binary);
+
+ if (Succeeded(result)) {
+ result = reader.WriteToFile(s_filename);
+ }
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm-validate.cc b/third_party/wasm2c/src/tools/wasm-validate.cc
new file mode 100644
index 0000000000..a55c5ed898
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm-validate.cc
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2017 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 <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+
+#include "src/binary-reader.h"
+#include "src/binary-reader-ir.h"
+#include "src/error-formatter.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+
+using namespace wabt;
+
+static int s_verbose;
+static std::string s_infile;
+static Features s_features;
+static bool s_read_debug_names = true;
+static bool s_fail_on_custom_section_error = true;
+static std::unique_ptr<FileStream> s_log_stream;
+
+static const char s_description[] =
+R"( Read a file in the WebAssembly binary format, and validate it.
+
+examples:
+ # validate binary file test.wasm
+ $ wasm-validate test.wasm
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm-validate", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ s_features.AddOptions(&parser);
+ parser.AddOption("no-debug-names", "Ignore debug names in the binary file",
+ []() { s_read_debug_names = false; });
+ parser.AddOption("ignore-custom-section-errors",
+ "Ignore errors in custom sections",
+ []() { s_fail_on_custom_section_error = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) {
+ s_infile = argument;
+ ConvertBackslashToSlash(&s_infile);
+ });
+ parser.Parse(argc, argv);
+}
+
+int ProgramMain(int argc, char** argv) {
+ Result result;
+
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ result = ReadFile(s_infile.c_str(), &file_data);
+ if (Succeeded(result)) {
+ Errors errors;
+ Module module;
+ const bool kStopOnFirstError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(),
+ s_read_debug_names, kStopOnFirstError,
+ s_fail_on_custom_section_error);
+ result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(),
+ options, &errors, &module);
+ if (Succeeded(result)) {
+ ValidateOptions options(s_features);
+ result = ValidateModule(&module, &errors, options);
+ }
+ FormatErrorsToFile(errors, Location::Type::Binary);
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wasm2c.cc b/third_party/wasm2c/src/tools/wasm2c.cc
new file mode 100644
index 0000000000..680be01045
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm2c.cc
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2017 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 <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+
+#include "src/apply-names.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-ir.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+
+#include "src/c-writer.h"
+
+using namespace wabt;
+
+static int s_verbose;
+static std::string s_infile;
+static std::string s_outfile;
+static Features s_features;
+static WriteCOptions s_write_c_options;
+static bool s_read_debug_names = true;
+static std::unique_ptr<FileStream> s_log_stream;
+
+static const char s_description[] =
+R"( Read a file in the WebAssembly binary format, and convert it to
+ a C source file and header.
+
+examples:
+ # parse binary file test.wasm and write test.c and test.h
+ $ wasm2c test.wasm -o test.c
+
+ # parse test.wasm, write test.c and test.h, but ignore the debug names, if any
+ $ wasm2c test.wasm --no-debug-names -o test.c
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm2c", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ parser.AddOption(
+ 'o', "output", "FILENAME",
+ "Output file for the generated C source file, by default use stdout",
+ [](const char* argument) {
+ s_outfile = argument;
+ ConvertBackslashToSlash(&s_outfile);
+ });
+ parser.AddOption(
+ 'n', "modname", "MODNAME",
+ "Unique name for the module being generated. Each wasm sandboxed module in a single application should have a unique name.",
+ [](const char* argument) {
+ s_write_c_options.mod_name = argument;
+ });
+ s_features.AddOptions(&parser);
+ parser.AddOption("no-debug-names", "Ignore debug names in the binary file",
+ []() { s_read_debug_names = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) {
+ s_infile = argument;
+ ConvertBackslashToSlash(&s_infile);
+ });
+ parser.Parse(argc, argv);
+
+ // TODO(binji): currently wasm2c doesn't support any non-default feature
+ // flags.
+ bool any_non_default_feature = false;
+#define WABT_FEATURE(variable, flag, default_, help) \
+ any_non_default_feature |= (s_features.variable##_enabled() != default_);
+#include "src/feature.def"
+#undef WABT_FEATURE
+
+ if (any_non_default_feature) {
+ fprintf(stderr, "wasm2c currently support only default feature flags.\n");
+ exit(1);
+ }
+}
+
+// TODO(binji): copied from binary-writer-spec.cc, probably should share.
+static string_view strip_extension(string_view s) {
+ string_view ext = s.substr(s.find_last_of('.'));
+ string_view result = s;
+
+ if (ext == ".c")
+ result.remove_suffix(ext.length());
+ return result;
+}
+
+int ProgramMain(int argc, char** argv) {
+ Result result;
+
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ result = ReadFile(s_infile.c_str(), &file_data);
+ if (Succeeded(result)) {
+ Errors errors;
+ Module module;
+ const bool kStopOnFirstError = true;
+ const bool kFailOnCustomSectionError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(),
+ s_read_debug_names, kStopOnFirstError,
+ kFailOnCustomSectionError);
+ result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(),
+ options, &errors, &module);
+ if (Succeeded(result)) {
+ if (Succeeded(result)) {
+ ValidateOptions options(s_features);
+ result = ValidateModule(&module, &errors, options);
+ result |= GenerateNames(&module);
+ }
+
+ if (Succeeded(result)) {
+ /* TODO(binji): This shouldn't fail; if a name can't be applied
+ * (because the index is invalid, say) it should just be skipped. */
+ Result dummy_result = ApplyNames(&module);
+ WABT_USE(dummy_result);
+ }
+
+ if (Succeeded(result)) {
+ if (!s_outfile.empty()) {
+ std::string header_name =
+ strip_extension(s_outfile).to_string() + ".h";
+ FileStream c_stream(s_outfile.c_str());
+ FileStream h_stream(header_name);
+ result = WriteC(&c_stream, &h_stream, header_name.c_str(), &module,
+ s_write_c_options);
+ } else {
+ FileStream stream(stdout);
+ result =
+ WriteC(&stream, &stream, "wasm.h", &module, s_write_c_options);
+ }
+ }
+ }
+ FormatErrorsToFile(errors, Location::Type::Binary);
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
+
diff --git a/third_party/wasm2c/src/tools/wasm2wat-fuzz.cc b/third_party/wasm2c/src/tools/wasm2wat-fuzz.cc
new file mode 100644
index 0000000000..1318ef6284
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm2wat-fuzz.cc
@@ -0,0 +1,30 @@
+// Copyright 2019 Google LLC
+//
+// 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.
+//
+// This file is copied from the oss-fuzz project:
+//
+// https://github.com/google/oss-fuzz/blob/master/projects/wabt/wasm2wat_fuzzer.cc
+
+#include "src/binary-reader-ir.h"
+#include "src/binary-reader.h"
+#include "src/common.h"
+#include "src/ir.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ wabt::ReadBinaryOptions options;
+ wabt::Errors errors;
+ wabt::Module module;
+ wabt::ReadBinaryIr("dummy filename", data, size, options, &errors, &module);
+ return 0;
+}
diff --git a/third_party/wasm2c/src/tools/wasm2wat.cc b/third_party/wasm2c/src/tools/wasm2wat.cc
new file mode 100644
index 0000000000..58e89849fe
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wasm2wat.cc
@@ -0,0 +1,149 @@
+/*
+ * 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 <cassert>
+#include <cinttypes>
+#include <cstdio>
+#include <cstdlib>
+
+#include "src/apply-names.h"
+#include "src/binary-reader.h"
+#include "src/binary-reader-ir.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-lexer.h"
+#include "src/wat-writer.h"
+
+using namespace wabt;
+
+static int s_verbose;
+static std::string s_infile;
+static std::string s_outfile;
+static Features s_features;
+static WriteWatOptions s_write_wat_options;
+static bool s_generate_names = false;
+static bool s_read_debug_names = true;
+static bool s_fail_on_custom_section_error = true;
+static std::unique_ptr<FileStream> s_log_stream;
+static bool s_validate = true;
+
+static const char s_description[] =
+R"( Read a file in the WebAssembly binary format, and convert it to
+ the WebAssembly text format.
+
+examples:
+ # parse binary file test.wasm and write text file test.wast
+ $ wasm2wat test.wasm -o test.wat
+
+ # parse test.wasm, write test.wat, but ignore the debug names, if any
+ $ wasm2wat test.wasm --no-debug-names -o test.wat
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wasm2wat", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ parser.AddOption(
+ 'o', "output", "FILENAME",
+ "Output file for the generated wast file, by default use stdout",
+ [](const char* argument) {
+ s_outfile = argument;
+ ConvertBackslashToSlash(&s_outfile);
+ });
+ parser.AddOption('f', "fold-exprs", "Write folded expressions where possible",
+ []() { s_write_wat_options.fold_exprs = true; });
+ s_features.AddOptions(&parser);
+ parser.AddOption("inline-exports", "Write all exports inline",
+ []() { s_write_wat_options.inline_export = true; });
+ parser.AddOption("inline-imports", "Write all imports inline",
+ []() { s_write_wat_options.inline_import = true; });
+ parser.AddOption("no-debug-names", "Ignore debug names in the binary file",
+ []() { s_read_debug_names = false; });
+ parser.AddOption("ignore-custom-section-errors",
+ "Ignore errors in custom sections",
+ []() { s_fail_on_custom_section_error = false; });
+ parser.AddOption(
+ "generate-names",
+ "Give auto-generated names to non-named functions, types, etc.",
+ []() { s_generate_names = true; });
+ parser.AddOption("no-check", "Don't check for invalid modules",
+ []() { s_validate = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) {
+ s_infile = argument;
+ ConvertBackslashToSlash(&s_infile);
+ });
+ parser.Parse(argc, argv);
+}
+
+int ProgramMain(int argc, char** argv) {
+ Result result;
+
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ result = ReadFile(s_infile.c_str(), &file_data);
+ if (Succeeded(result)) {
+ Errors errors;
+ Module module;
+ const bool kStopOnFirstError = true;
+ ReadBinaryOptions options(s_features, s_log_stream.get(),
+ s_read_debug_names, kStopOnFirstError,
+ s_fail_on_custom_section_error);
+ result = ReadBinaryIr(s_infile.c_str(), file_data.data(), file_data.size(),
+ options, &errors, &module);
+ if (Succeeded(result)) {
+ if (Succeeded(result) && s_validate) {
+ ValidateOptions options(s_features);
+ result = ValidateModule(&module, &errors, options);
+ }
+
+ if (s_generate_names) {
+ result = GenerateNames(&module);
+ }
+
+ if (Succeeded(result)) {
+ /* TODO(binji): This shouldn't fail; if a name can't be applied
+ * (because the index is invalid, say) it should just be skipped. */
+ Result dummy_result = ApplyNames(&module);
+ WABT_USE(dummy_result);
+ }
+
+ if (Succeeded(result)) {
+ FileStream stream(!s_outfile.empty() ? FileStream(s_outfile)
+ : FileStream(stdout));
+ result = WriteWat(&stream, &module, s_write_wat_options);
+ }
+ }
+ FormatErrorsToFile(errors, Location::Type::Binary);
+ }
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wast2json.cc b/third_party/wasm2c/src/tools/wast2json.cc
new file mode 100644
index 0000000000..a5c9a47d1d
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wast2json.cc
@@ -0,0 +1,162 @@
+/*
+ * 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 <cassert>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdlib>
+#include <cstdio>
+#include <string>
+
+#include "config.h"
+
+#include "src/binary-writer.h"
+#include "src/binary-writer-spec.h"
+#include "src/common.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/filenames.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/resolve-names.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-parser.h"
+
+using namespace wabt;
+
+static const char* s_infile;
+static std::string s_outfile;
+static int s_verbose;
+static WriteBinaryOptions s_write_binary_options;
+static bool s_validate = true;
+static bool s_debug_parsing;
+static Features s_features;
+
+static std::unique_ptr<FileStream> s_log_stream;
+
+static const char s_description[] =
+R"( read a file in the wasm spec test format, check it for errors, and
+ convert it to a JSON file and associated wasm binary files.
+
+examples:
+ # parse spec-test.wast, and write files to spec-test.json. Modules are
+ # written to spec-test.0.wasm, spec-test.1.wasm, etc.
+ $ wast2json spec-test.wast -o spec-test.json
+)";
+
+static void ParseOptions(int argc, char* argv[]) {
+ OptionParser parser("wast2json", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ parser.AddOption("debug-parser", "Turn on debugging the parser of wast files",
+ []() { s_debug_parsing = true; });
+ s_features.AddOptions(&parser);
+ parser.AddOption('o', "output", "FILE", "output JSON file",
+ [](const char* argument) { s_outfile = argument; });
+ parser.AddOption(
+ 'r', "relocatable",
+ "Create a relocatable wasm binary (suitable for linking with e.g. lld)",
+ []() { s_write_binary_options.relocatable = true; });
+ parser.AddOption(
+ "no-canonicalize-leb128s",
+ "Write all LEB128 sizes as 5-bytes instead of their minimal size",
+ []() { s_write_binary_options.canonicalize_lebs = false; });
+ parser.AddOption("debug-names",
+ "Write debug names to the generated binary file",
+ []() { s_write_binary_options.write_debug_names = true; });
+ parser.AddOption("no-check", "Don't check for invalid modules",
+ []() { s_validate = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+
+ parser.Parse(argc, argv);
+}
+
+static std::string DefaultOuputName(string_view input_name) {
+ // Strip existing extension and add .json
+ std::string result(StripExtension(GetBasename(input_name)));
+ result += ".json";
+
+ return result;
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ Result result = ReadFile(s_infile, &file_data);
+ std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer(
+ s_infile, file_data.data(), file_data.size());
+ if (Failed(result)) {
+ WABT_FATAL("unable to read file: %s\n", s_infile);
+ }
+
+ Errors errors;
+ std::unique_ptr<Script> script;
+ WastParseOptions parse_wast_options(s_features);
+ result = ParseWastScript(lexer.get(), &script, &errors, &parse_wast_options);
+
+ if (Succeeded(result) && s_validate) {
+ ValidateOptions options(s_features);
+ result = ValidateScript(script.get(), &errors, options);
+ }
+
+ if (Succeeded(result)) {
+ if (s_outfile.empty()) {
+ s_outfile = DefaultOuputName(s_infile);
+ }
+
+ std::vector<FilenameMemoryStreamPair> module_streams;
+ MemoryStream json_stream;
+
+ std::string output_basename = StripExtension(s_outfile).to_string();
+ s_write_binary_options.features = s_features;
+ result = WriteBinarySpecScript(&json_stream, script.get(), s_infile,
+ output_basename, s_write_binary_options,
+ &module_streams, s_log_stream.get());
+
+ if (Succeeded(result)) {
+ result = json_stream.WriteToFile(s_outfile);
+ }
+
+ if (Succeeded(result)) {
+ for (auto iter = module_streams.begin(); iter != module_streams.end();
+ ++iter) {
+ result = iter->stream->WriteToFile(iter->filename);
+ if (!Succeeded(result)) {
+ break;
+ }
+ }
+ }
+ }
+
+ auto line_finder = lexer->MakeLineFinder();
+ FormatErrorsToFile(errors, Location::Type::Text, line_finder.get());
+
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wat-desugar.cc b/third_party/wasm2c/src/tools/wat-desugar.cc
new file mode 100644
index 0000000000..cc6a2cba8e
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wat-desugar.cc
@@ -0,0 +1,130 @@
+/*
+ * 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 <cassert>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+#include "config.h"
+
+#include "src/apply-names.h"
+#include "src/common.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/generate-names.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/stream.h"
+#include "src/wast-parser.h"
+#include "src/wat-writer.h"
+
+using namespace wabt;
+
+static const char* s_infile;
+static const char* s_outfile;
+static WriteWatOptions s_write_wat_options;
+static bool s_generate_names;
+static bool s_debug_parsing;
+static Features s_features;
+
+static const char s_description[] =
+R"( read a file in the wasm s-expression format and format it.
+
+examples:
+ # write output to stdout
+ $ wat-desugar test.wat
+
+ # write output to test2.wat
+ $ wat-desugar test.wat -o test2.wat
+
+ # generate names for indexed variables
+ $ wat-desugar --generate-names test.wat
+)";
+
+static void ParseOptions(int argc, char** argv) {
+ OptionParser parser("wat-desugar", s_description);
+
+ parser.AddOption('o', "output", "FILE", "Output file for the formatted file",
+ [](const char* argument) { s_outfile = argument; });
+ parser.AddOption("debug-parser", "Turn on debugging the parser of wat files",
+ []() { s_debug_parsing = true; });
+ parser.AddOption('f', "fold-exprs", "Write folded expressions where possible",
+ []() { s_write_wat_options.fold_exprs = true; });
+ parser.AddOption("inline-exports", "Write all exports inline",
+ []() { s_write_wat_options.inline_export = true; });
+ parser.AddOption("inline-imports", "Write all imports inline",
+ []() { s_write_wat_options.inline_import = true; });
+ s_features.AddOptions(&parser);
+ parser.AddOption(
+ "generate-names",
+ "Give auto-generated names to non-named functions, types, etc.",
+ []() { s_generate_names = true; });
+
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+ parser.Parse(argc, argv);
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ Result result = ReadFile(s_infile, &file_data);
+ if (Failed(result)) {
+ WABT_FATAL("unable to read %s\n", s_infile);
+ }
+
+ std::unique_ptr<WastLexer> lexer(WastLexer::CreateBufferLexer(
+ s_infile, file_data.data(), file_data.size()));
+
+ Errors errors;
+ std::unique_ptr<Script> script;
+ WastParseOptions parse_wast_options(s_features);
+ result = ParseWastScript(lexer.get(), &script, &errors, &parse_wast_options);
+ auto line_finder = lexer->MakeLineFinder();
+ FormatErrorsToFile(errors, Location::Type::Text);
+
+ if (Succeeded(result)) {
+ Module* module = script->GetFirstModule();
+ if (!module) {
+ WABT_FATAL("no module in file.\n");
+ }
+
+ if (s_generate_names) {
+ result = GenerateNames(module);
+ }
+
+ if (Succeeded(result)) {
+ result = ApplyNames(module);
+ }
+
+ if (Succeeded(result)) {
+ FileStream stream(s_outfile ? FileStream(s_outfile) : FileStream(stdout));
+ result = WriteWat(&stream, module, s_write_wat_options);
+ }
+ }
+
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}
diff --git a/third_party/wasm2c/src/tools/wat2wasm.cc b/third_party/wasm2c/src/tools/wat2wasm.cc
new file mode 100644
index 0000000000..f77dad2778
--- /dev/null
+++ b/third_party/wasm2c/src/tools/wat2wasm.cc
@@ -0,0 +1,170 @@
+/*
+ * 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 <cassert>
+#include <cstdarg>
+#include <cstdint>
+#include <cstdlib>
+#include <cstdio>
+#include <string>
+
+#include "config.h"
+
+#include "src/binary-writer.h"
+#include "src/common.h"
+#include "src/error-formatter.h"
+#include "src/feature.h"
+#include "src/filenames.h"
+#include "src/ir.h"
+#include "src/option-parser.h"
+#include "src/resolve-names.h"
+#include "src/stream.h"
+#include "src/validator.h"
+#include "src/wast-parser.h"
+
+using namespace wabt;
+
+static const char* s_infile;
+static std::string s_outfile;
+static bool s_dump_module;
+static int s_verbose;
+static WriteBinaryOptions s_write_binary_options;
+static bool s_validate = true;
+static bool s_debug_parsing;
+static Features s_features;
+
+static std::unique_ptr<FileStream> s_log_stream;
+
+static const char s_description[] =
+R"( read a file in the wasm text format, check it for errors, and
+ convert it to the wasm binary format.
+
+examples:
+ # parse and typecheck test.wat
+ $ wat2wasm test.wat
+
+ # parse test.wat and write to binary file test.wasm
+ $ wat2wasm test.wat -o test.wasm
+
+ # parse spec-test.wast, and write verbose output to stdout (including
+ # the meaning of every byte)
+ $ wat2wasm spec-test.wast -v
+)";
+
+static void ParseOptions(int argc, char* argv[]) {
+ OptionParser parser("wat2wasm", s_description);
+
+ parser.AddOption('v', "verbose", "Use multiple times for more info", []() {
+ s_verbose++;
+ s_log_stream = FileStream::CreateStderr();
+ });
+ parser.AddOption("debug-parser", "Turn on debugging the parser of wat files",
+ []() { s_debug_parsing = true; });
+ parser.AddOption('d', "dump-module",
+ "Print a hexdump of the module to stdout",
+ []() { s_dump_module = true; });
+ s_features.AddOptions(&parser);
+ parser.AddOption('o', "output", "FILE", "output wasm binary file",
+ [](const char* argument) { s_outfile = argument; });
+ parser.AddOption(
+ 'r', "relocatable",
+ "Create a relocatable wasm binary (suitable for linking with e.g. lld)",
+ []() { s_write_binary_options.relocatable = true; });
+ parser.AddOption(
+ "no-canonicalize-leb128s",
+ "Write all LEB128 sizes as 5-bytes instead of their minimal size",
+ []() { s_write_binary_options.canonicalize_lebs = false; });
+ parser.AddOption("debug-names",
+ "Write debug names to the generated binary file",
+ []() { s_write_binary_options.write_debug_names = true; });
+ parser.AddOption("no-check", "Don't check for invalid modules",
+ []() { s_validate = false; });
+ parser.AddArgument("filename", OptionParser::ArgumentCount::One,
+ [](const char* argument) { s_infile = argument; });
+
+ parser.Parse(argc, argv);
+}
+
+static void WriteBufferToFile(string_view filename,
+ const OutputBuffer& buffer) {
+ if (s_dump_module) {
+ std::unique_ptr<FileStream> stream = FileStream::CreateStdout();
+ if (s_verbose) {
+ stream->Writef(";; dump\n");
+ }
+ if (!buffer.data.empty()) {
+ stream->WriteMemoryDump(buffer.data.data(), buffer.data.size());
+ }
+ }
+
+ buffer.WriteToFile(filename);
+}
+
+static std::string DefaultOuputName(string_view input_name) {
+ // Strip existing extension and add .wasm
+ std::string result(StripExtension(GetBasename(input_name)));
+ result += kWasmExtension;
+
+ return result;
+}
+
+int ProgramMain(int argc, char** argv) {
+ InitStdio();
+
+ ParseOptions(argc, argv);
+
+ std::vector<uint8_t> file_data;
+ Result result = ReadFile(s_infile, &file_data);
+ std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer(
+ s_infile, file_data.data(), file_data.size());
+ if (Failed(result)) {
+ WABT_FATAL("unable to read file: %s\n", s_infile);
+ }
+
+ Errors errors;
+ std::unique_ptr<Module> module;
+ WastParseOptions parse_wast_options(s_features);
+ result = ParseWatModule(lexer.get(), &module, &errors, &parse_wast_options);
+
+ if (Succeeded(result) && s_validate) {
+ ValidateOptions options(s_features);
+ result = ValidateModule(module.get(), &errors, options);
+ }
+
+ if (Succeeded(result)) {
+ MemoryStream stream(s_log_stream.get());
+ s_write_binary_options.features = s_features;
+ result = WriteBinaryModule(&stream, module.get(), s_write_binary_options);
+
+ if (Succeeded(result)) {
+ if (s_outfile.empty()) {
+ s_outfile = DefaultOuputName(s_infile);
+ }
+ WriteBufferToFile(s_outfile.c_str(), stream.output_buffer());
+ }
+ }
+
+ auto line_finder = lexer->MakeLineFinder();
+ FormatErrorsToFile(errors, Location::Type::Text, line_finder.get());
+
+ return result != Result::Ok;
+}
+
+int main(int argc, char** argv) {
+ WABT_TRY
+ return ProgramMain(argc, argv);
+ WABT_CATCH_BAD_ALLOC_AND_EXIT
+}