diff options
Diffstat (limited to 'third_party/wasm2c/src/binary-writer-spec.cc')
-rw-r--r-- | third_party/wasm2c/src/binary-writer-spec.cc | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/third_party/wasm2c/src/binary-writer-spec.cc b/third_party/wasm2c/src/binary-writer-spec.cc new file mode 100644 index 0000000000..3cb6674de3 --- /dev/null +++ b/third_party/wasm2c/src/binary-writer-spec.cc @@ -0,0 +1,672 @@ +/* + * 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 "wabt/binary-writer-spec.h" + +#include <cassert> +#include <cinttypes> +#include <string_view> + +#include "wabt/config.h" + +#include "wabt/binary-writer.h" +#include "wabt/binary.h" +#include "wabt/cast.h" +#include "wabt/filenames.h" +#include "wabt/ir.h" +#include "wabt/literal.h" +#include "wabt/stream.h" + +namespace wabt { + +namespace { + +class BinaryWriterSpec { + public: + BinaryWriterSpec(Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options); + + Result WriteScript(const Script& script); + + private: + std::string GetModuleFilename(const char* extension); + void WriteString(const char* s); + void WriteKey(const char* key); + void WriteSeparator(); + void WriteEscapedString(std::string_view); + void WriteCommandType(const Command& command); + void WriteLocation(const Location& loc); + void WriteVar(const Var& var); + void WriteTypeObject(Type type); + void WriteF32(uint32_t, ExpectedNan); + void WriteF64(uint64_t, ExpectedNan); + void WriteRefBits(uintptr_t ref_bits); + void WriteConst(const Const& const_); + void WriteConstVector(const ConstVector& consts); + void WriteAction(const Action& action); + void WriteActionResultType(const Action& action); + void WriteModule(std::string_view filename, const Module& module); + void WriteScriptModule(std::string_view filename, + const ScriptModule& script_module); + void WriteInvalidModule(const ScriptModule& module, std::string_view text); + void WriteCommands(); + + const Script* script_ = nullptr; + Stream* json_stream_ = nullptr; + WriteBinarySpecStreamFactory module_stream_factory_; + std::string source_filename_; + std::string module_filename_noext_; + const WriteBinaryOptions& options_; + Result result_ = Result::Ok; + size_t num_modules_ = 0; +}; + +BinaryWriterSpec::BinaryWriterSpec( + Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options) + : json_stream_(json_stream), + module_stream_factory_(module_stream_factory), + source_filename_(source_filename), + module_filename_noext_(module_filename_noext), + options_(options) {} + +std::string BinaryWriterSpec::GetModuleFilename(const char* extension) { + std::string result = module_filename_noext_; + result += '.'; + result += std::to_string(num_modules_); + result += extension; + ConvertBackslashToSlash(&result); + return result; +} + +void BinaryWriterSpec::WriteString(const char* s) { + json_stream_->Writef("\"%s\"", s); +} + +void BinaryWriterSpec::WriteKey(const char* key) { + json_stream_->Writef("\"%s\": ", key); +} + +void BinaryWriterSpec::WriteSeparator() { + json_stream_->Writef(", "); +} + +void BinaryWriterSpec::WriteEscapedString(std::string_view s) { + json_stream_->WriteChar('"'); + for (size_t i = 0; i < s.length(); ++i) { + uint8_t c = s[i]; + if (c < 0x20 || c == '\\' || c == '"') { + json_stream_->Writef("\\u%04x", c); + } else { + json_stream_->WriteChar(c); + } + } + json_stream_->WriteChar('"'); +} + +void BinaryWriterSpec::WriteCommandType(const Command& command) { + static const char* s_command_names[] = { + "module", + "module", + "action", + "register", + "assert_malformed", + "assert_invalid", + "assert_unlinkable", + "assert_uninstantiable", + "assert_return", + "assert_trap", + "assert_exhaustion", + "assert_exception", + }; + WABT_STATIC_ASSERT(WABT_ARRAY_SIZE(s_command_names) == kCommandTypeCount); + + WriteKey("type"); + assert(s_command_names[static_cast<size_t>(command.type)]); + WriteString(s_command_names[static_cast<size_t>(command.type)]); +} + +void BinaryWriterSpec::WriteLocation(const Location& loc) { + WriteKey("line"); + json_stream_->Writef("%d", loc.line); +} + +void BinaryWriterSpec::WriteVar(const Var& var) { + if (var.is_index()) { + json_stream_->Writef("\"%" PRIindex "\"", var.index()); + } else { + WriteEscapedString(var.name()); + } +} + +void BinaryWriterSpec::WriteTypeObject(Type type) { + json_stream_->Writef("{"); + WriteKey("type"); + WriteString(type.GetName().c_str()); + json_stream_->Writef("}"); +} + +void BinaryWriterSpec::WriteF32(uint32_t f32_bits, ExpectedNan expected) { + switch (expected) { + case ExpectedNan::None: + json_stream_->Writef("\"%u\"", f32_bits); + break; + + case ExpectedNan::Arithmetic: + WriteString("nan:arithmetic"); + break; + + case ExpectedNan::Canonical: + WriteString("nan:canonical"); + break; + } +} + +void BinaryWriterSpec::WriteF64(uint64_t f64_bits, ExpectedNan expected) { + switch (expected) { + case ExpectedNan::None: + json_stream_->Writef("\"%" PRIu64 "\"", f64_bits); + break; + + case ExpectedNan::Arithmetic: + WriteString("nan:arithmetic"); + break; + + case ExpectedNan::Canonical: + WriteString("nan:canonical"); + break; + } +} + +void BinaryWriterSpec::WriteRefBits(uintptr_t ref_bits) { + if (ref_bits == Const::kRefNullBits) { + json_stream_->Writef("\"null\""); + } else { + json_stream_->Writef("\"%" PRIuPTR "\"", ref_bits); + } +} + +void BinaryWriterSpec::WriteConst(const Const& const_) { + json_stream_->Writef("{"); + WriteKey("type"); + + /* Always write the values as strings, even though they may be representable + * as JSON numbers. This way the formatting is consistent. */ + switch (const_.type()) { + case Type::I32: + WriteString("i32"); + WriteSeparator(); + WriteKey("value"); + json_stream_->Writef("\"%u\"", const_.u32()); + break; + + case Type::I64: + WriteString("i64"); + WriteSeparator(); + WriteKey("value"); + json_stream_->Writef("\"%" PRIu64 "\"", const_.u64()); + break; + + case Type::F32: + WriteString("f32"); + WriteSeparator(); + WriteKey("value"); + WriteF32(const_.f32_bits(), const_.expected_nan()); + break; + + case Type::F64: + WriteString("f64"); + WriteSeparator(); + WriteKey("value"); + WriteF64(const_.f64_bits(), const_.expected_nan()); + break; + + case Type::FuncRef: { + WriteString("funcref"); + WriteSeparator(); + WriteKey("value"); + WriteRefBits(const_.ref_bits()); + break; + } + + case Type::ExternRef: { + WriteString("externref"); + WriteSeparator(); + WriteKey("value"); + WriteRefBits(const_.ref_bits()); + break; + } + + case Type::V128: { + WriteString("v128"); + WriteSeparator(); + WriteKey("lane_type"); + WriteString(const_.lane_type().GetName().c_str()); + WriteSeparator(); + WriteKey("value"); + json_stream_->Writef("["); + + for (int lane = 0; lane < const_.lane_count(); ++lane) { + switch (const_.lane_type()) { + case Type::I8: + json_stream_->Writef("\"%u\"", const_.v128_lane<uint8_t>(lane)); + break; + + case Type::I16: + json_stream_->Writef("\"%u\"", const_.v128_lane<uint16_t>(lane)); + break; + + case Type::I32: + json_stream_->Writef("\"%u\"", const_.v128_lane<uint32_t>(lane)); + break; + + case Type::I64: + json_stream_->Writef("\"%" PRIu64 "\"", + const_.v128_lane<uint64_t>(lane)); + break; + + case Type::F32: + WriteF32(const_.v128_lane<uint32_t>(lane), + const_.expected_nan(lane)); + break; + + case Type::F64: + WriteF64(const_.v128_lane<uint64_t>(lane), + const_.expected_nan(lane)); + break; + + default: + WABT_UNREACHABLE; + } + + if (lane != const_.lane_count() - 1) { + WriteSeparator(); + } + } + + json_stream_->Writef("]"); + + break; + } + + default: + WABT_UNREACHABLE; + } + + json_stream_->Writef("}"); +} + +void BinaryWriterSpec::WriteConstVector(const ConstVector& consts) { + json_stream_->Writef("["); + for (size_t i = 0; i < consts.size(); ++i) { + const Const& const_ = consts[i]; + WriteConst(const_); + if (i != consts.size() - 1) { + WriteSeparator(); + } + } + json_stream_->Writef("]"); +} + +void BinaryWriterSpec::WriteAction(const Action& action) { + WriteKey("action"); + json_stream_->Writef("{"); + WriteKey("type"); + if (action.type() == ActionType::Invoke) { + WriteString("invoke"); + } else { + assert(action.type() == ActionType::Get); + WriteString("get"); + } + WriteSeparator(); + if (action.module_var.is_name()) { + WriteKey("module"); + WriteVar(action.module_var); + WriteSeparator(); + } + if (action.type() == ActionType::Invoke) { + WriteKey("field"); + WriteEscapedString(action.name); + WriteSeparator(); + WriteKey("args"); + WriteConstVector(cast<InvokeAction>(&action)->args); + } else { + WriteKey("field"); + WriteEscapedString(action.name); + } + json_stream_->Writef("}"); +} + +void BinaryWriterSpec::WriteActionResultType(const Action& action) { + const Module* module = script_->GetModule(action.module_var); + const Export* export_; + json_stream_->Writef("["); + switch (action.type()) { + case ActionType::Invoke: { + export_ = module->GetExport(action.name); + assert(export_->kind == ExternalKind::Func); + const Func* func = module->GetFunc(export_->var); + Index num_results = func->GetNumResults(); + for (Index i = 0; i < num_results; ++i) + WriteTypeObject(func->GetResultType(i)); + break; + } + + case ActionType::Get: { + export_ = module->GetExport(action.name); + assert(export_->kind == ExternalKind::Global); + const Global* global = module->GetGlobal(export_->var); + WriteTypeObject(global->type); + break; + } + } + json_stream_->Writef("]"); +} + +void BinaryWriterSpec::WriteModule(std::string_view filename, + const Module& module) { + result_ |= + WriteBinaryModule(module_stream_factory_(filename), &module, options_); +} + +void BinaryWriterSpec::WriteScriptModule(std::string_view filename, + const ScriptModule& script_module) { + switch (script_module.type()) { + case ScriptModuleType::Text: + WriteModule(filename, cast<TextScriptModule>(&script_module)->module); + break; + + case ScriptModuleType::Binary: + module_stream_factory_(filename)->WriteData( + cast<BinaryScriptModule>(&script_module)->data, ""); + break; + + case ScriptModuleType::Quoted: + module_stream_factory_(filename)->WriteData( + cast<QuotedScriptModule>(&script_module)->data, ""); + break; + } +} + +void BinaryWriterSpec::WriteInvalidModule(const ScriptModule& module, + std::string_view text) { + const char* extension = ""; + const char* module_type = ""; + switch (module.type()) { + case ScriptModuleType::Text: + extension = kWasmExtension; + module_type = "binary"; + break; + + case ScriptModuleType::Binary: + extension = kWasmExtension; + module_type = "binary"; + break; + + case ScriptModuleType::Quoted: + extension = kWatExtension; + module_type = "text"; + break; + } + + WriteLocation(module.location()); + WriteSeparator(); + std::string filename = GetModuleFilename(extension); + WriteKey("filename"); + WriteEscapedString(GetBasename(filename)); + WriteSeparator(); + WriteKey("text"); + WriteEscapedString(text); + WriteSeparator(); + WriteKey("module_type"); + WriteString(module_type); + WriteScriptModule(filename, module); +} + +void BinaryWriterSpec::WriteCommands() { + json_stream_->Writef("{\"source_filename\": "); + WriteEscapedString(source_filename_); + json_stream_->Writef(",\n \"commands\": [\n"); + Index last_module_index = kInvalidIndex; + for (size_t i = 0; i < script_->commands.size(); ++i) { + const Command* command = script_->commands[i].get(); + + if (i != 0) { + WriteSeparator(); + json_stream_->Writef("\n"); + } + + json_stream_->Writef(" {"); + WriteCommandType(*command); + WriteSeparator(); + + switch (command->type) { + case CommandType::Module: { + const Module& module = cast<ModuleCommand>(command)->module; + std::string filename = GetModuleFilename(kWasmExtension); + WriteLocation(module.loc); + WriteSeparator(); + if (!module.name.empty()) { + WriteKey("name"); + WriteEscapedString(module.name); + WriteSeparator(); + } + WriteKey("filename"); + WriteEscapedString(GetBasename(filename)); + WriteModule(filename, module); + num_modules_++; + last_module_index = i; + break; + } + + case CommandType::ScriptModule: { + auto* script_module_command = cast<ScriptModuleCommand>(command); + const auto& module = script_module_command->module; + std::string filename = GetModuleFilename(kWasmExtension); + WriteLocation(module.loc); + WriteSeparator(); + if (!module.name.empty()) { + WriteKey("name"); + WriteEscapedString(module.name); + WriteSeparator(); + } + WriteKey("filename"); + WriteEscapedString(GetBasename(filename)); + WriteScriptModule(filename, *script_module_command->script_module); + num_modules_++; + last_module_index = i; + break; + } + + case CommandType::Action: { + const Action& action = *cast<ActionCommand>(command)->action; + WriteLocation(action.loc); + WriteSeparator(); + WriteAction(action); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(action); + break; + } + + case CommandType::Register: { + auto* register_command = cast<RegisterCommand>(command); + const Var& var = register_command->var; + WriteLocation(var.loc); + WriteSeparator(); + if (var.is_name()) { + WriteKey("name"); + WriteVar(var); + WriteSeparator(); + } else { + /* If we're not registering by name, then we should only be + * registering the last module. */ + WABT_USE(last_module_index); + assert(var.index() == last_module_index); + } + WriteKey("as"); + WriteEscapedString(register_command->module_name); + break; + } + + case CommandType::AssertMalformed: { + auto* assert_malformed_command = cast<AssertMalformedCommand>(command); + WriteInvalidModule(*assert_malformed_command->module, + assert_malformed_command->text); + num_modules_++; + break; + } + + case CommandType::AssertInvalid: { + auto* assert_invalid_command = cast<AssertInvalidCommand>(command); + WriteInvalidModule(*assert_invalid_command->module, + assert_invalid_command->text); + num_modules_++; + break; + } + + case CommandType::AssertUnlinkable: { + auto* assert_unlinkable_command = + cast<AssertUnlinkableCommand>(command); + WriteInvalidModule(*assert_unlinkable_command->module, + assert_unlinkable_command->text); + num_modules_++; + break; + } + + case CommandType::AssertUninstantiable: { + auto* assert_uninstantiable_command = + cast<AssertUninstantiableCommand>(command); + WriteInvalidModule(*assert_uninstantiable_command->module, + assert_uninstantiable_command->text); + num_modules_++; + break; + } + + case CommandType::AssertReturn: { + auto* assert_return_command = cast<AssertReturnCommand>(command); + WriteLocation(assert_return_command->action->loc); + WriteSeparator(); + WriteAction(*assert_return_command->action); + WriteSeparator(); + const Expectation* expectation = assert_return_command->expected.get(); + switch (expectation->type()) { + case ExpectationType::Values: + WriteKey("expected"); + break; + + case ExpectationType::Either: + WriteKey("either"); + break; + } + WriteConstVector(expectation->expected); + break; + } + + case CommandType::AssertTrap: { + auto* assert_trap_command = cast<AssertTrapCommand>(command); + WriteLocation(assert_trap_command->action->loc); + WriteSeparator(); + WriteAction(*assert_trap_command->action); + WriteSeparator(); + WriteKey("text"); + WriteEscapedString(assert_trap_command->text); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(*assert_trap_command->action); + break; + } + + case CommandType::AssertExhaustion: { + auto* assert_exhaustion_command = + cast<AssertExhaustionCommand>(command); + WriteLocation(assert_exhaustion_command->action->loc); + WriteSeparator(); + WriteAction(*assert_exhaustion_command->action); + WriteSeparator(); + WriteKey("text"); + WriteEscapedString(assert_exhaustion_command->text); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(*assert_exhaustion_command->action); + break; + } + + case CommandType::AssertException: { + auto* assert_exception_command = cast<AssertExceptionCommand>(command); + WriteLocation(assert_exception_command->action->loc); + WriteSeparator(); + WriteAction(*assert_exception_command->action); + WriteSeparator(); + WriteKey("expected"); + WriteActionResultType(*assert_exception_command->action); + break; + } + } + + json_stream_->Writef("}"); + } + json_stream_->Writef("]}\n"); +} + +Result BinaryWriterSpec::WriteScript(const Script& script) { + script_ = &script; + WriteCommands(); + return result_; +} + +} // end anonymous namespace + +Result WriteBinarySpecScript(Stream* json_stream, + WriteBinarySpecStreamFactory module_stream_factory, + Script* script, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options) { + BinaryWriterSpec binary_writer_spec(json_stream, module_stream_factory, + source_filename, module_filename_noext, + options); + return binary_writer_spec.WriteScript(*script); +} + +Result WriteBinarySpecScript( + Stream* json_stream, + Script* script, + std::string_view source_filename, + std::string_view module_filename_noext, + const WriteBinaryOptions& options, + std::vector<FilenameMemoryStreamPair>* out_module_streams, + Stream* log_stream) { + WriteBinarySpecStreamFactory module_stream_factory = + [&](std::string_view filename) { + out_module_streams->emplace_back( + filename, std::make_unique<MemoryStream>(log_stream)); + return out_module_streams->back().stream.get(); + }; + + BinaryWriterSpec binary_writer_spec(json_stream, module_stream_factory, + source_filename, module_filename_noext, + options); + return binary_writer_spec.WriteScript(*script); +} + +} // namespace wabt |