diff --git a/CMakeLists.txt b/CMakeLists.txt index aff2843..62bef16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,12 @@ cmake_minimum_required(VERSION 3.13.4) project(WASM2BRS VERSION 1.0.0) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + add_subdirectory(third_party/wabt) +set(BUILD_TESTS OFF) add_subdirectory(third_party/binaryen) include_directories(${WABT_SOURCE_DIR} ${WABT_BINARY_DIR}) diff --git a/docker/Dockerfile b/docker/Dockerfile index 6e4c1b9..9ea80d6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -28,10 +28,10 @@ ENV WASMER_DIR="/home/user/.wasmer" ENV WASMER_CACHE_DIR="/home/user/.wasmer/cache" ENV PATH="/home/user/.wasmer/bin:/home/user/.wasienv/bin/:${PATH}:/home/user/.wasmer/globals/wapm_packages/.bin" -RUN curl -sSf https://sh.rustup.rs | bash -s -- -y +RUN curl -sSf https://sh.rustup.rs | bash -s -- -y --default-toolchain=1.81.0 ENV PATH="/home/user/.cargo/bin:${PATH}" RUN rustup target add wasm32-wasi -RUN cargo install cargo-wasi +RUN cargo install --locked cargo-wasi ENTRYPOINT ["/usr/bin/dumb-init", "--"] CMD echo "No command specified" diff --git a/samples/rust/rust.brs b/samples/rust/rust.brs index 5e93bf2..57ab477 100644 --- a/samples/rust/rust.brs +++ b/samples/rust/rust.brs @@ -1,6 +1,6 @@ Function Start() - w2bInit__() - wasi_init(m.w2b_memory, "rust.wasm", {}) - w2b__start() + _rust_wasmInit__() + wasi_init(m._rust_wasm_memory, "rust.wasm", {}) + _rust_wasm__start() wasi_shutdown() End Function diff --git a/src/brs-writer.cc b/src/brs-writer.cc index e75af88..c4eac9d 100644 --- a/src/brs-writer.cc +++ b/src/brs-writer.cc @@ -23,17 +23,22 @@ #include #include +#include +#include #include #include #include #include +#include +#include -#include "src/cast.h" -#include "src/common.h" -#include "src/ir.h" -#include "src/literal.h" -#include "src/stream.h" -#include "src/string-view.h" +#include "wabt/cast.h" +#include "wabt/common.h" +#include "wabt/ir.h" +#include "wabt/literal.h" +#include "wabt/sha256.h" +#include "wabt/stream.h" +#include "wabt/string-util.h" #define INDENT_SIZE 2 @@ -156,7 +161,7 @@ class CWriter { std::string GetFilename(size_t index); FileStream OpenFileStream(size_t index); - void WriteModule(const Module&); + Result WriteModule(const Module&); private: typedef std::set SymbolSet; @@ -183,15 +188,15 @@ class CWriter { static std::string Deref(const std::string&); static char MangleType(Type); - static std::string LegalizeNameNoAddons(string_view); - std::string LegalizeName(const std::string& prefix, const std::string& module_name, string_view name); - std::string DefineName(SymbolSet*, string_view, const std::string& prefix = std::string()); + static std::string LegalizeNameNoAddons(std::string_view); + std::string LegalizeName(const std::string& prefix, const std::string& module_name, std::string_view name); + std::string DefineName(SymbolSet*, std::string_view, const std::string& prefix = std::string()); std::string DefineImportName(const std::string& name, - string_view module_name, - string_view mangled_field_name); + std::string_view module_name, + std::string_view mangled_field_name); std::string DefineGlobalScopeName(const std::string&, const std::string& prefix = std::string()); std::string DefineLocalScopeName(const std::string&); - std::string DefineStackVarName(Index, Type, string_view); + std::string DefineStackVarName(Index, Type, std::string_view); void EndChunk(); void Indent(int size = INDENT_SIZE); @@ -214,7 +219,7 @@ class CWriter { void Write(OpenBrace); void Write(CloseBrace); void Write(Index); - void Write(string_view); + void Write(std::string_view); void Write(const LocalName&); void Write(const GlobalName&); void Write(const ExternalPtr&); @@ -286,6 +291,7 @@ class CWriter { size_t label_count_ = 0; MemoryStream stream_; std::vector chunks_; + Result result_ = Result::Ok; int indent_ = 0; bool should_write_indent_next_ = false; @@ -301,14 +307,6 @@ class CWriter { static const char kImplicitFuncLabel[] = "$Bfunc"; -#define SECTION_NAME(x) s_header_##x -#include "src/prebuilt/wasm2c.include.h" -#undef SECTION_NAME - -#define SECTION_NAME(x) s_source_##x -#include "src/prebuilt/wasm2c.include.c" -#undef SECTION_NAME - static bool IsReplaceableMemFunction(const wabt::Func& func) { return (func.name == "$memcpy" || func.name == "$memset") && @@ -415,7 +413,7 @@ char CWriter::MangleType(Type type) { } } -std::string CWriter::LegalizeNameNoAddons(string_view name) { +std::string CWriter::LegalizeNameNoAddons(std::string_view name) { std::string result; for (size_t i = 0; i < name.size(); ++i) result += isalnum(name[i]) ? tolower(name[i]) : '_'; @@ -432,7 +430,7 @@ uint32_t adler32(const uint8_t* data, size_t len) { return (b << 16) | a; } -std::string CWriter::LegalizeName(const std::string& prefix, const std::string& module_name, string_view name) { +std::string CWriter::LegalizeName(const std::string& prefix, const std::string& module_name, std::string_view name) { const std::string legalized = LegalizeNameNoAddons(name); const std::string module_prefix = module_name == "env" ? "" : module_name + "_"; const std::string output = prefix + module_prefix + legalized; @@ -441,7 +439,7 @@ std::string CWriter::LegalizeName(const std::string& prefix, const std::string& : output + "_" + std::to_string(adler32((const uint8_t*)name.begin(), name.length())); } -std::string CWriter::DefineName(SymbolSet* set, string_view name, const std::string& prefix) { +std::string CWriter::DefineName(SymbolSet* set, std::string_view name, const std::string& prefix) { std::string legal = LegalizeName(prefix, options_.name_prefix, name); if (set->find(legal) != set->end()) { std::string base = legal + "_"; @@ -454,7 +452,7 @@ std::string CWriter::DefineName(SymbolSet* set, string_view name, const std::str return legal; } -string_view StripLeadingDollar(string_view name) { +std::string_view StripLeadingDollar(std::string_view name) { if (!name.empty() && name[0] == '$') { name.remove_prefix(1); } @@ -462,9 +460,9 @@ string_view StripLeadingDollar(string_view name) { } std::string CWriter::DefineImportName(const std::string& name, - string_view module, - string_view mangled_field_name) { - std::string mangled = mangled_field_name.to_string(); + std::string_view module, + std::string_view mangled_field_name) { + std::string mangled(mangled_field_name); import_syms_.insert(name); global_syms_.insert(mangled); global_sym_map_.insert(SymbolMap::value_type(name, mangled)); @@ -485,7 +483,7 @@ std::string CWriter::DefineLocalScopeName(const std::string& name) { std::string CWriter::DefineStackVarName(Index index, Type type, - string_view name) { + std::string_view name) { std::string unique = DefineName(&local_syms_, name); StackTypePair stp = {index, type}; stack_var_sym_map_.insert(StackVarSymbolMap::value_type(stp, unique)); @@ -554,7 +552,7 @@ void CWriter::Write(Index index) { Writef("%" PRIindex, index); } -void CWriter::Write(string_view s) { +void CWriter::Write(std::string_view s) { WriteData(s.data(), s.size()); } @@ -612,7 +610,7 @@ void CWriter::Write(const GotoLabel& goto_label) { // We've generated names for all labels, so we should only be using an // index when branching to the implicit function label, which can't be // named. - Write("Goto ", Var(kImplicitFuncLabel)); + Write("Goto ", Var(kImplicitFuncLabel, {})); } } @@ -950,7 +948,9 @@ void CWriter::WriteDataInitializers() { uint32_t max = memory->page_limits.has_max ? memory->page_limits.max : 65536; Write(ExternalPtr(memory->name), " = CreateObject(\"roByteArray\")", Newline()); - Write(ExternalPtr(memory->name), "[", memory->page_limits.initial * WABT_PAGE_SIZE, "] = 0", Newline()); + // TODO: should this be memory->page_size? + // 0x10000 = WABT_PAGE_SIZE + Write(ExternalPtr(memory->name), "[", memory->page_limits.initial * 0x10000, "] = 0", Newline()); Write(ExternalPtr(memory->name), "Max = ", max, Newline()); } @@ -991,11 +991,13 @@ void CWriter::WriteElemInitializers() { Write(Newline()); size_t i = 0; - for (const ElemExpr& elem_expr : elem_segment->elem_exprs) { - // We don't support the bulk-memory proposal here, so we know that we - // don't have any passive segments (where ref.null can be used). - assert(elem_expr.kind == ElemExprKind::RefFunc); - const Func* func = module_->GetFunc(elem_expr.var); + for (const ExprList& expr_list : elem_segment->elem_exprs) { + assert(expr_list.size() == 1); + const Expr& expr = expr_list.front(); + assert(expr.type() == ExprType::RefFunc); + + const RefFuncExpr& ref_func_expr = *cast(&expr); + const Func* func = module_->GetFunc(ref_func_expr.var); Index func_type_index = module_->GetFuncTypeIndex(func->decl.type_var); Write(ExternalRef(table->name), "[offset + ", i, "] = ", ExternalPtr(func->name), Newline()); @@ -1511,6 +1513,20 @@ void CWriter::Write(const ExprList& exprs) { break; } + case ExprType::MemoryCopy: { + const auto inst = cast(&expr); + Memory* dest_memory = + module_->memories[module_->GetMemoryIndex(inst->destmemidx)]; + const Memory* src_memory = module_->GetMemory(inst->srcmemidx); + Write("MemoryCopy(", + ExternalRef(dest_memory->name), ", ", + StackVar(2), ", ", + ExternalRef(src_memory->name), ", ", + StackVar(1), ", ", + StackVar(0), ")", Newline()); + DropTypes(3); + } break; + case ExprType::AtomicLoad: case ExprType::AtomicRmw: case ExprType::AtomicRmwCmpxchg: @@ -1518,13 +1534,11 @@ void CWriter::Write(const ExprList& exprs) { case ExprType::AtomicWait: case ExprType::AtomicFence: case ExprType::AtomicNotify: - case ExprType::BrOnExn: case ExprType::Rethrow: case ExprType::ReturnCall: case ExprType::ReturnCallIndirect: case ExprType::Throw: case ExprType::Try: - case ExprType::MemoryCopy: case ExprType::DataDrop: case ExprType::MemoryInit: case ExprType::MemoryFill: @@ -1565,7 +1579,7 @@ void CWriter::Write(const ExprList& exprs) { case ExprType::Return: // Goto the function label instead; this way we can do shared function // cleanup code in one place. - Write(GotoLabel(Var(label_stack_.size() - 1)), Newline()); + Write(GotoLabel(Var(label_stack_.size() - 1, {})), Newline()); // Stop processing this ExprList, since the following are unreachable. return; @@ -2429,7 +2443,7 @@ FileStream CWriter::OpenFileStream(size_t index) { return FileStream(GetFilename(index).c_str()); } -void CWriter::WriteModule(const Module& module) { +Result CWriter::WriteModule(const Module& module) { WABT_USE(options_); module_ = &module; @@ -2485,14 +2499,15 @@ void CWriter::WriteModule(const Module& module) { lines += chunk_lines; bytes += chunk_bytes; } + + return result_; } } // end anonymous namespace Result WriteBrs(const Module* module, const WriteCOptions& options) { - CWriter c_writer(options); - c_writer.WriteModule(*module); - return Result::Ok; + CWriter brs_writer(options); + return brs_writer.WriteModule(*module); } } // namespace wabt diff --git a/src/brs-writer.h b/src/brs-writer.h index c8addaa..73636bf 100644 --- a/src/brs-writer.h +++ b/src/brs-writer.h @@ -22,7 +22,10 @@ #ifndef WABT_C_WRITER_H_ #define WABT_C_WRITER_H_ -#include "src/common.h" +#include +#include "wabt/common.h" +#include "wabt/feature.h" +#include "wabt/ir.h" namespace wabt { @@ -30,6 +33,8 @@ struct Module; class Stream; struct WriteCOptions { + std::string_view module_name; + Features features; std::string name_prefix; std::string out_filename; }; diff --git a/src/wasm2brs.cc b/src/wasm2brs.cc index 7c268f7..456fd42 100644 --- a/src/wasm2brs.cc +++ b/src/wasm2brs.cc @@ -24,18 +24,19 @@ #include #include -#include "src/apply-names.h" -#include "src/wast-parser.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 "wabt/apply-names.h" +#include "wabt/binary-reader-ir.h" +#include "wabt/binary-reader.h" +#include "wabt/error-formatter.h" +#include "wabt/feature.h" +#include "wabt/filenames.h" +#include "wabt/generate-names.h" +#include "wabt/ir.h" +#include "wabt/option-parser.h" +#include "wabt/result.h" +#include "wabt/stream.h" +#include "wabt/validator.h" +#include "wabt/wast-lexer.h" #include "brs-writer.h" @@ -43,39 +44,42 @@ using namespace wabt; static int s_verbose; static std::string s_infile; -static std::string s_prefix; -static Features s_features; static WriteCOptions s_write_c_options; static bool s_read_debug_names = true; static std::unique_ptr 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. + R"( Read a file in the WebAssembly binary format, and convert it to + a BrightScript source file. 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 + # parse binary file test.wasm and write test.brs + $ wasm2brs test.wasm -o test.brs )"; +// For now, we don't support any features, so this is empty. +static const std::vector supported_features = {}; + +static bool IsFeatureSupported(const std::string& feature) { + return std::find(std::begin(supported_features), std::end(supported_features), + feature) != std::end(supported_features); +}; + static void ParseOptions(int argc, char** argv) { - OptionParser parser("wasm2c", s_description); + OptionParser parser("wasm2brs", s_description); parser.AddOption('v', "verbose", "Use multiple times for more info", []() { s_verbose++; - s_log_stream = FileStream::CreateStdout(); + s_log_stream = FileStream::CreateStderr(); }); parser.AddOption( 'o', "output", "FILENAME", - "Output file for the generated C source file, by default use stdout", + "Output file for the generated BrightScript source file, by default use stdout", [](const char* argument) { s_write_c_options.out_filename = argument; ConvertBackslashToSlash(&s_write_c_options.out_filename); }); - s_features.AddOptions(&parser); + s_write_c_options.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, @@ -89,78 +93,63 @@ static void ParseOptions(int argc, char** argv) { }); 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" + bool any_non_supported_feature = false; +#define WABT_FEATURE(variable, flag, default_, help) \ + any_non_supported_feature |= \ + (s_write_c_options.features.variable##_enabled() != default_) && \ + s_write_c_options.features.variable##_enabled() && \ + !IsFeatureSupported(flag); +#include "wabt/feature.def" #undef WABT_FEATURE - if (any_non_default_feature) { - fprintf(stderr, "wasm2c currently support only default feature flags.\n"); + if (any_non_supported_feature) { + fprintf(stderr, + "wasm2c currently only supports a limited set of features.\n"); exit(1); } } +Result Wasm2BrsMain(Errors& errors) { + std::vector file_data; + CHECK_RESULT(ReadFile(s_infile.c_str(), &file_data)); + + Module module; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + ReadBinaryOptions options(s_write_c_options.features, s_log_stream.get(), + s_read_debug_names, kStopOnFirstError, + kFailOnCustomSectionError); + CHECK_RESULT(ReadBinaryIr(s_infile.c_str(), file_data.data(), + file_data.size(), options, &errors, &module)); + CHECK_RESULT(ValidateModule(&module, &errors, s_write_c_options.features)); + CHECK_RESULT(GenerateNames(&module)); + /* TODO(binji): This shouldn't fail; if a name can't be applied + * (because the index is invalid, say) it should just be skipped. */ + ApplyNames(&module); + + if (s_write_c_options.name_prefix.empty()) { + if (module.name.empty()) { + s_write_c_options.name_prefix = "w2b"; + } else { + s_write_c_options.name_prefix = module.name; + } + } + + CHECK_RESULT(WriteBrs(&module, s_write_c_options)); + + return Result::Ok; +} + int ProgramMain(int argc, char** argv) { Result result; InitStdio(); ParseOptions(argc, argv); - std::vector file_data; - result = ReadFile(s_infile.c_str(), &file_data); - if (Succeeded(result)) { - Errors errors; - std::unique_ptr module; - Location::Type location_type = Location::Type::Text; - const char first = file_data.front(); - if (first == '(' || first == ';' || first == ' ' || first == '\n') { - std::unique_ptr lexer = WastLexer::CreateBufferLexer( - s_infile, file_data.data(), file_data.size()); - WastParseOptions options(s_features); - options.debug_parsing = s_read_debug_names; - result = ParseWatModule(lexer.get(), &module, &errors, &options); - } else { - location_type = Location::Type::Binary; - module = std::make_unique(); - 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.get()); - } + Errors errors; + result = Wasm2BrsMain(errors); + FormatErrorsToFile(errors, Location::Type::Binary); - if (Succeeded(result)) { - if (Succeeded(result)) { - ValidateOptions options(s_features); - result = ValidateModule(module.get(), &errors, options); - result |= GenerateNames(module.get()); - } - - 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.get()); - WABT_USE(dummy_result); - } - - if (Succeeded(result)) { - if (s_write_c_options.name_prefix.empty()) { - if (module->name.empty()) { - s_write_c_options.name_prefix = "w2b"; - } else { - s_write_c_options.name_prefix = module->name; - } - } - result = WriteBrs(module.get(), s_write_c_options); - } - } - FormatErrorsToFile(errors, location_type); - } return result != Result::Ok; } @@ -169,4 +158,3 @@ int main(int argc, char** argv) { return ProgramMain(argc, argv); WABT_CATCH_BAD_ALLOC_AND_EXIT } - diff --git a/third_party/binaryen b/third_party/binaryen index c7ccd6d..6a6e080 160000 --- a/third_party/binaryen +++ b/third_party/binaryen @@ -1 +1 @@ -Subproject commit c7ccd6ddd6c6152e44d4fd2c77283c8839aa8842 +Subproject commit 6a6e08057c07ea4792a2ddcacfffc4d49ceea7fc diff --git a/third_party/wabt b/third_party/wabt index be5e8bf..fde4ca3 160000 --- a/third_party/wabt +++ b/third_party/wabt @@ -1 +1 @@ -Subproject commit be5e8bf8ec698f9ad3a1b6fbb412680995fe39bf +Subproject commit fde4ca34a5ad55cbaf2327f5df658149e1a53364