diff options
-rw-r--r-- | .github/workflows/build.yml | 2 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 62 | ||||
-rw-r--r-- | src/interp/binary-reader-interp.cc | 102 | ||||
-rw-r--r-- | src/interp/binary-reader-interp.h | 12 | ||||
-rw-r--r-- | src/interp/binary-reader-metadata.cc | 74 | ||||
-rw-r--r-- | src/interp/binary-reader-metadata.h | 43 | ||||
-rw-r--r-- | src/interp/interp-wasm-c-api.cc | 1432 | ||||
-rw-r--r-- | src/interp/interp.h | 7 | ||||
-rwxr-xr-x | test/run-c-api-examples.py | 123 | ||||
m--------- | third_party/wasm-c-api | 0 |
11 files changed, 1815 insertions, 45 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7fa4b55d..1ea6dc43 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,5 +31,7 @@ jobs: run: cmake --build out - name: unittests run: cmake --build out --target run-unittests + - name: c-api-tests + run: cmake --build out --target run-c-api-tests - name: tests run: cmake --build out --target run-tests diff --git a/.gitmodules b/.gitmodules index 435ad379..7352235c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "third_party/ply"] path = third_party/ply url = https://github.com/dabeaz/ply +[submodule "third_party/wasm-c-api"] + path = third_party/wasm-c-api + url = https://github.com/WebAssembly/wasm-c-api diff --git a/CMakeLists.txt b/CMakeLists.txt index edbb072e..363a5660 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -224,7 +224,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${WABT_SOURCE_DIR}/cmake) add_custom_target(everything) -add_library(wabt STATIC +set(WABT_LIBRARY_SRC src/apply-names.h src/apply-names.cc src/binary.h @@ -308,12 +308,27 @@ add_library(wabt STATIC # TODO(binji): Move this into its own library? src/interp/binary-reader-interp.h src/interp/binary-reader-interp.cc + src/interp/binary-reader-metadata.h + src/interp/binary-reader-metadata.cc src/interp/interp.h src/interp/interp.cc src/interp/interp-disassemble.cc src/interp/interp-trace.cc ) +add_library(wabt STATIC ${WABT_LIBRARY_SRC}) + +# libwasm, which implenents the wasm C API +add_library(wasm SHARED ${WABT_LIBRARY_SRC} src/interp/interp-wasm-c-api.cc) +target_link_libraries(wasm wabt) +target_include_directories(wasm PUBLIC third_party/wasm-c-api/include) +if (COMPILER_IS_MSVC) + target_compile_definitions(wasm PRIVATE "WASM_API_EXTERN=__declspec(dllexport)") +else () + target_compile_options(wasm PRIVATE -Wno-old-style-cast) + target_compile_definitions(wasm PRIVATE "WASM_API_EXTERN=__attribute__((visibility(\"default\")))") +endif () +set_target_properties(wasm PROPERTIES CXX_VISIBILITY_PRESET hidden) if (NOT EMSCRIPTEN) if (CODE_COVERAGE) @@ -507,6 +522,7 @@ if (NOT EMSCRIPTEN) # See: https://github.com/actions/setup-python/issues/40 find_package(PythonInterp REQUIRED) set(RUN_TESTS_PY ${WABT_SOURCE_DIR}/test/run-tests.py) + add_custom_target(run-tests COMMAND ${PYTHON_EXECUTABLE} ${RUN_TESTS_PY} --bindir $<TARGET_FILE_DIR:wat2wasm> DEPENDS ${WABT_EXECUTABLES} @@ -521,7 +537,49 @@ if (NOT EMSCRIPTEN) ${USES_TERMINAL} ) - add_custom_target(check DEPENDS run-tests wabt-unittests) + add_custom_target(run-c-api-tests + COMMAND ${PYTHON_EXECUTABLE} ${WABT_SOURCE_DIR}/test/run-c-api-examples.py --bindir $<TARGET_FILE_DIR:wat2wasm> + WORKING_DIRECTORY ${WABT_SOURCE_DIR} + ${USES_TERMINAL} + ) + + add_custom_target(check DEPENDS run-unittests run-tests run-c-api-tests) + + function(c_api_example NAME) + set(EXENAME wasm-c-api-${NAME}) + add_executable(${EXENAME} third_party/wasm-c-api/example/${NAME}.c) + if (NOT COMPILER_IS_MSVC) + set_target_properties(${EXENAME} PROPERTIES COMPILE_FLAGS "-std=gnu11 -Wno-pointer-to-int-cast") + endif () + target_link_libraries(${EXENAME} wasm Threads::Threads) + add_custom_target(${EXENAME}-copy-to-bin ALL + COMMAND ${CMAKE_COMMAND} -E make_directory ${WABT_SOURCE_DIR}/bin + COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${EXENAME}> ${WABT_SOURCE_DIR}/bin/ + COMMAND ${CMAKE_COMMAND} -E copy ${WABT_SOURCE_DIR}/third_party/wasm-c-api/example/${NAME}.wasm $<TARGET_FILE_DIR:${EXENAME}>/ + COMMAND ${CMAKE_COMMAND} -E copy ${WABT_SOURCE_DIR}/third_party/wasm-c-api/example/${NAME}.wasm ${WABT_SOURCE_DIR}/bin/ + DEPENDS ${EXENAME} + ) + add_dependencies(run-c-api-tests ${EXENAME}) + endfunction() + + c_api_example(callback) + c_api_example(finalize) + c_api_example(global) + c_api_example(hello) + c_api_example(hostref) + c_api_example(multi) + c_api_example(memory) + c_api_example(reflect) + c_api_example(serialize) + c_api_example(start) + c_api_example(table) + c_api_example(trap) + if (NOT WIN32) + # depends on pthreads + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + c_api_example(threads) + endif () endif () # install diff --git a/src/interp/binary-reader-interp.cc b/src/interp/binary-reader-interp.cc index 5312e78b..fc40bc67 100644 --- a/src/interp/binary-reader-interp.cc +++ b/src/interp/binary-reader-interp.cc @@ -60,6 +60,7 @@ class BinaryReaderInterp : public BinaryReaderNop { BinaryReaderInterp(Environment* env, DefinedModule* module, std::unique_ptr<OutputBuffer> istream, + const std::vector<Export*>& imports_, Errors* errors, const Features& features); @@ -323,11 +324,15 @@ class BinaryReaderInterp : public BinaryReaderNop { string_view name); wabt::Result FindRegisteredModule(string_view module_name, Module** out_module); - wabt::Result GetModuleExport(Module* module, - string_view field_name, - Export** out_export); + wabt::Result ResolveImport(Index import_index, + ExternalKind kind, + string_view module_name, + string_view field_name, + Index sig_index, + Export** out_export); Features features_; + const std::vector<Export*>& imports_; Errors* errors_ = nullptr; Environment* env_ = nullptr; DefinedModule* module_ = nullptr; @@ -363,9 +368,11 @@ class BinaryReaderInterp : public BinaryReaderNop { BinaryReaderInterp::BinaryReaderInterp(Environment* env, DefinedModule* module, std::unique_ptr<OutputBuffer> istream, + const std::vector<Export*>& imports, Errors* errors, const Features& features) : features_(features), + imports_(imports), errors_(errors), env_(env), module_(module), @@ -765,16 +772,35 @@ wabt::Result BinaryReaderInterp::FindRegisteredModule(string_view module_name, return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::GetModuleExport(Module* module, - string_view field_name, - Export** out_export) { - Export* export_ = module->GetExport(field_name); - if (!export_) { - PrintError("unknown module field \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(field_name)); - return wabt::Result::Error; +wabt::Result BinaryReaderInterp::ResolveImport(Index import_index, + ExternalKind kind, + string_view module_name, + string_view field_name, + Index sig_index, + Export** out_export) { + Export* export_ = nullptr; + if (!imports_.empty()) { + export_ = imports_[import_index]; + } else { + Module* module; + CHECK_RESULT(FindRegisteredModule(module_name, &module)); + + // Func imports get special handled due to the face that they can be + // overloaded on signature. + if (kind == ExternalKind::Func) { + export_ = module->GetFuncExport(env_, field_name, sig_index); + } + if (!export_) { + export_ = module->GetExport(field_name); + if (!export_) { + PrintError("unknown module field \"" PRIstringview "\"", + WABT_PRINTF_STRING_VIEW_ARG(field_name)); + return wabt::Result::Error; + } + } } + CHECK_RESULT(CheckImportKind(module_name, field_name, kind, export_->kind)); *out_export = export_; return wabt::Result::Ok; } @@ -786,20 +812,9 @@ wabt::Result BinaryReaderInterp::OnImportFunc(Index import_index, Index sig_index) { Index env_sig_index = TranslateSigIndexToEnv(sig_index); - Module* import_module; - CHECK_RESULT(FindRegisteredModule(module_name, &import_module)); - - Export* export_ = - import_module->GetFuncExport(env_, field_name, env_sig_index); - if (!export_) { - // If GetFuncExport fails then GetModuleExport will fail too. But it's - // useful to call here to share the same error handling code as other - // imports. - CHECK_RESULT(GetModuleExport(import_module, field_name, &export_)); - } - - CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Func, - export_->kind)); + Export* export_; + CHECK_RESULT(ResolveImport(import_index, ExternalKind::Func, module_name, + field_name, env_sig_index, &export_)); Func* func = env_->GetFunc(export_->index); if (!env_->FuncSignaturesAreEqual(env_sig_index, func->sig_index)) { @@ -819,12 +834,9 @@ wabt::Result BinaryReaderInterp::OnImportTable(Index import_index, Type elem_type, const Limits* elem_limits) { has_table = true; - Module* import_module; - CHECK_RESULT(FindRegisteredModule(module_name, &import_module)); - Export* export_; - CHECK_RESULT(GetModuleExport(import_module, field_name, &export_)); - CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Table, export_->kind)); + CHECK_RESULT(ResolveImport(import_index, ExternalKind::Table, module_name, + field_name, 0, &export_)); Table* table = env_->GetTable(export_->index); if (elem_type != table->elem_type) { @@ -849,12 +861,9 @@ wabt::Result BinaryReaderInterp::OnImportMemory(Index import_index, return wabt::Result::Error; } - Module* import_module; - CHECK_RESULT(FindRegisteredModule(module_name, &import_module)); - Export* export_; - CHECK_RESULT(GetModuleExport(import_module, field_name, &export_)); - CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Memory, export_->kind)); + CHECK_RESULT(ResolveImport(import_index, ExternalKind::Memory, module_name, + field_name, 0, &export_)); Memory* memory = env_->GetMemory(export_->index); CHECK_RESULT(CheckImportLimits(page_limits, &memory->page_limits)); @@ -890,18 +899,13 @@ wabt::Result BinaryReaderInterp::OnImportGlobal(Index import_index, Index global_index, Type type, bool mutable_) { - Module* import_module; - CHECK_RESULT(FindRegisteredModule(module_name, &import_module)); - Export* export_; - CHECK_RESULT(GetModuleExport(import_module, field_name, &export_)); - CHECK_RESULT(CheckImportKind(module_name, field_name, ExternalKind::Global, - export_->kind)); + CHECK_RESULT(ResolveImport(import_index, ExternalKind::Global, module_name, + field_name, 0, &export_)); Global* exported_global = env_->GetGlobal(export_->index); GlobalType expected = {type, mutable_}; GlobalType actual = {exported_global->type, exported_global->mutable_}; - if (Failed(CheckGlobalType(actual, expected))) { return wabt::Result::Error; } @@ -1932,6 +1936,7 @@ wabt::Result ReadBinaryInterp(Environment* env, const void* data, size_t size, const ReadBinaryOptions& options, + const std::vector<Export*>& imports, Errors* errors, DefinedModule** out_module) { // Need to mark before taking ownership of env->istream. @@ -1941,7 +1946,7 @@ wabt::Result ReadBinaryInterp(Environment* env, IstreamOffset istream_offset = istream->size(); DefinedModule* module = new DefinedModule(env); - BinaryReaderInterp reader(env, module, std::move(istream), errors, + BinaryReaderInterp reader(env, module, std::move(istream), imports, errors, options.features); env->EmplaceBackModule(module); @@ -1959,4 +1964,15 @@ wabt::Result ReadBinaryInterp(Environment* env, return result; } +wabt::Result ReadBinaryInterp(Environment* env, + const void* data, + size_t size, + const ReadBinaryOptions& options, + Errors* errors, + DefinedModule** out_module) { + std::vector<Export*> empty_imports; + return ReadBinaryInterp(env, data, size, options, empty_imports, errors, + out_module); +} + } // namespace wabt diff --git a/src/interp/binary-reader-interp.h b/src/interp/binary-reader-interp.h index 767b9479..e6110f5e 100644 --- a/src/interp/binary-reader-interp.h +++ b/src/interp/binary-reader-interp.h @@ -24,6 +24,7 @@ namespace wabt { namespace interp { +struct Export; struct DefinedModule; class Environment; @@ -31,6 +32,7 @@ class Environment; struct ReadBinaryOptions; +// Read and instantiate a module in the given environment. Result ReadBinaryInterp(interp::Environment* env, const void* data, size_t size, @@ -38,6 +40,16 @@ Result ReadBinaryInterp(interp::Environment* env, Errors*, interp::DefinedModule** out_module); +// Read and instantiate a module in the given environment. Similar to above but +// using using a fixed set of exports to resolve the module exports. +Result ReadBinaryInterp(interp::Environment* env, + const void* data, + size_t size, + const ReadBinaryOptions& options, + const std::vector<interp::Export*>& imports, + Errors*, + interp::DefinedModule** out_module); + } // namespace wabt #endif /* WABT_BINARY_READER_INTERP_H_ */ diff --git a/src/interp/binary-reader-metadata.cc b/src/interp/binary-reader-metadata.cc new file mode 100644 index 00000000..8cbed78e --- /dev/null +++ b/src/interp/binary-reader-metadata.cc @@ -0,0 +1,74 @@ +/* + * Copyright 2020 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/interp/binary-reader-metadata.h" + +#include "src/binary-reader-nop.h" +#include "src/binary-reader.h" +#include "src/interp/interp.h" +#include "src/string-view.h" + +namespace wabt { + +using namespace interp; + +namespace { + +class BinaryReaderMetadata : public BinaryReaderNop { + public: + BinaryReaderMetadata(ModuleMetadata* metadata, + Errors* errors, + const Features& features) + : metadata_(metadata) {} + wabt::Result OnImport(Index index, + ExternalKind kind, + string_view module_name, + string_view field_name) override { + metadata_->imports.emplace_back(kind, module_name, field_name); + return wabt::Result::Ok; + } + + wabt::Result OnExport(Index index, + ExternalKind kind, + Index item_index, + string_view name) override { + metadata_->exports.emplace_back(name, kind, item_index); + return wabt::Result::Ok; + } + + private: + ModuleMetadata* metadata_; +}; + +} // end anonymous namespace + +wabt::Result ReadBinaryMetadata(const void* data, + size_t size, + const ReadBinaryOptions& options, + Errors* errors, + ModuleMetadata** out_metadata) { + ModuleMetadata* metadata = new ModuleMetadata(); + BinaryReaderMetadata reader(metadata, errors, options.features); + wabt::Result result = ReadBinary(data, size, &reader, options); + if (!Succeeded(result)) { + delete metadata; + return result; + } + *out_metadata = metadata; + return result; +} + +} // namespace wabt diff --git a/src/interp/binary-reader-metadata.h b/src/interp/binary-reader-metadata.h new file mode 100644 index 00000000..13834560 --- /dev/null +++ b/src/interp/binary-reader-metadata.h @@ -0,0 +1,43 @@ +/* + * Copyright 2020 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. + */ + +#ifndef WABT_BINARY_READER_METADATA_H_ +#define WABT_BINARY_READER_METADATA_H_ + +#include "src/common.h" +#include "src/error.h" + +namespace wabt { + +namespace interp { + +struct ModuleMetadata; + +} // namespace interp + +struct ReadBinaryOptions; + +// Reads just the binary metadata, used by C-API to get module imports names +// (and potentially other metadata) before instantiation time. +Result ReadBinaryMetadata(const void* data, + size_t size, + const ReadBinaryOptions& options, + Errors*, + interp::ModuleMetadata** out_metadata); + +} // namespace wabt + +#endif /* WABT_BINARY_READER_METADATA_H_ */ diff --git a/src/interp/interp-wasm-c-api.cc b/src/interp/interp-wasm-c-api.cc new file mode 100644 index 00000000..d5ff9ae3 --- /dev/null +++ b/src/interp/interp-wasm-c-api.cc @@ -0,0 +1,1432 @@ +/* + * Copyright 2020 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 <wasm.h> + +#include "src/binary-reader.h" +#include "src/error-formatter.h" +#include "src/error.h" +#include "src/interp/binary-reader-interp.h" +#include "src/interp/binary-reader-metadata.h" +#include "src/interp/interp.h" +#include "src/ir.h" +#include "src/stream.h" + +using namespace wabt; +using namespace wabt::interp; + +#ifndef NDEBUG +#define TRACE(str, ...) fprintf(stderr, "CAPI: " str, ##__VA_ARGS__) +#else +#define TRACE(...) +#endif + +static Features s_features; +static Stream* s_trace_stream; +static Thread::Options s_thread_options; +static std::unique_ptr<FileStream> s_log_stream; +static std::unique_ptr<FileStream> s_stdout_stream; + +struct wasm_valtype_t { + wasm_valkind_t kind; +}; + +struct wasm_config_t {}; + +struct wasm_externtype_t { + wasm_externkind_t kind; + + protected: + wasm_externtype_t(wasm_externkind_t kind) : kind(kind) {} +}; + +struct wasm_globaltype_t : wasm_externtype_t { + wasm_globaltype_t(wasm_valtype_t type, bool mutable_) + : wasm_externtype_t(WASM_EXTERN_GLOBAL), type(type), mutable_(mutable_) {} + wasm_valtype_t type; + bool mutable_; +}; + +struct wasm_tabletype_t : wasm_externtype_t { + wasm_tabletype_t(wasm_valtype_t elemtype, wasm_limits_t limits) + : wasm_externtype_t(WASM_EXTERN_TABLE), + elemtype(elemtype), + limits(limits) {} + wasm_valtype_t elemtype; + wasm_limits_t limits; +}; + +struct wasm_memorytype_t : wasm_externtype_t { + wasm_memorytype_t(wasm_limits_t limits) + : wasm_externtype_t(WASM_EXTERN_MEMORY), limits(limits) {} + wasm_limits_t limits; +}; + +struct wasm_importtype_t {}; + +struct wasm_exporttype_t { + wasm_exporttype_t(wasm_name_t name, wasm_externtype_t* type) + : name(name), type(type) {} + + wasm_exporttype_t(const wasm_exporttype_t& other) { + name = other.name; + type = MakeUnique<wasm_externtype_t>(*other.type.get()); + } + + wasm_name_t name; + std::unique_ptr<wasm_externtype_t> type; +}; + +struct wasm_engine_t {}; + +struct wasm_store_t { + wasm_store_t(Environment* env, Executor* executor) + : env(env), executor(executor) {} + + ~wasm_store_t() { + TRACE("~store\n"); + } + + Environment* env; + std::unique_ptr<Executor> executor; +}; + +enum class WasmRefType { Extern, Module, Instance, Trap, Foreign }; + +std::string WasmRefTypeToString(WasmRefType t) { + switch (t) { + case WasmRefType::Extern: + return "extern"; + case WasmRefType::Module: + return "module"; + case WasmRefType::Instance: + return "instance"; + case WasmRefType::Trap: + return "trap"; + case WasmRefType::Foreign: + return "foreign"; + } + WABT_UNREACHABLE; +} + +struct wasm_ref_t { + wasm_ref_t(WasmRefType kind) : kind(kind) {} + wasm_ref_t(const wasm_ref_t& other) + : kind(other.kind), + host_info(other.host_info), + finalizer(other.finalizer) {} + + virtual ~wasm_ref_t() { + if (finalizer) { + finalizer(host_info); + } + } + + virtual wasm_ref_t* Copy() const { return new wasm_ref_t(kind); } + + bool Same(const wasm_ref_t& other) const; + + WasmRefType kind; + void* host_info = nullptr; + void (*finalizer)(void*) = nullptr; +}; + +struct wasm_extern_ref_t : wasm_ref_t { + wasm_extern_ref_t(ExternalKind kind, Index index) + : wasm_ref_t(WasmRefType::Extern), kind(kind), index(index) {} + + wasm_extern_ref_t(const wasm_extern_ref_t& other) + : wasm_ref_t(other), kind(other.kind), index(other.index) {} + + wasm_ref_t* Copy() const override { return new wasm_extern_ref_t(*this); } + + bool Same(const wasm_extern_ref_t& other) const { + return kind == other.kind && index == other.index; + } + + ExternalKind kind; + Index index; +}; + +struct wasm_foreign_ref_t : wasm_ref_t { + wasm_foreign_ref_t(Index index) : wasm_ref_t(WasmRefType::Foreign), index(index) {} + wasm_foreign_ref_t(const wasm_foreign_ref_t& other) : wasm_ref_t(other), index(other.index) {} + + wasm_ref_t* Copy() const override { return new wasm_foreign_ref_t(*this); } + + Index index; +}; + +struct wasm_foreign_t : wasm_foreign_ref_t { + wasm_foreign_t(Index index) : wasm_foreign_ref_t(index) {} + + bool Same(const wasm_foreign_t& other) const { + TRACE("wasm_foreign_t %u %u\n", index, other.index); + return index == other.index; + } +}; + +struct WasmInstance { + WasmInstance(wasm_store_t* store, DefinedModule* module) + : store(store), module(module) {} + + ~WasmInstance() { + TRACE("~WasmInstance\n"); + } + + wasm_store_t* store; + std::unique_ptr<DefinedModule> module; +}; + +struct wasm_instance_t : wasm_ref_t { + wasm_instance_t(wasm_store_t* store, DefinedModule* module) + : wasm_ref_t(WasmRefType::Instance), + ptr(std::make_shared<WasmInstance>(store, module)) {} + + wasm_instance_t(std::shared_ptr<WasmInstance> ptr) + : wasm_ref_t(WasmRefType::Instance), ptr(ptr) {} + + wasm_instance_t(const wasm_instance_t& other) + : wasm_ref_t(other), ptr(other.ptr) {} + + wasm_ref_t* Copy() const override { return new wasm_instance_t(*this); } + + bool Same(const wasm_instance_t& other) const { return ptr == other.ptr; } + + std::shared_ptr<WasmInstance> ptr; +}; + +struct wasm_module_t : wasm_ref_t { + wasm_module_t(wasm_store_t* store, + const wasm_byte_vec_t* in, + ModuleMetadata* metadata) + : wasm_ref_t(WasmRefType::Module), store(store), metadata(metadata) { + wasm_byte_vec_copy(&binary, in); + } + + bool Same(const wasm_module_t& other) const { + assert(false); + return true; + } + + ~wasm_module_t() { + TRACE("~module\n"); + wasm_byte_vec_delete(&binary); + } + + wasm_store_t* store; + wasm_byte_vec_t binary; + std::unique_ptr<ModuleMetadata> metadata; +}; + +struct wasm_shared_module_t : wasm_module_t {}; + +bool wasm_ref_t::Same(const wasm_ref_t& other) const { + TRACE("wasm_ref_t::Same kind=%d other=%d\n", static_cast<int>(kind), + static_cast<int>(other.kind)); + if (kind != other.kind) + return false; + TRACE("wasm_ref_t::Same x\n"); + if (other.host_info != host_info && other.finalizer != finalizer) + return false; + TRACE("wasm_ref_t::Same 2\n"); + switch (kind) { + case WasmRefType::Extern: + return static_cast<const wasm_extern_ref_t*>(this)->Same( + static_cast<const wasm_extern_ref_t&>(other)); + case WasmRefType::Instance: + return static_cast<const wasm_instance_t*>(this)->Same( + static_cast<const wasm_instance_t&>(other)); + case WasmRefType::Module: + return static_cast<const wasm_module_t*>(this)->Same( + static_cast<const wasm_module_t&>(other)); + case WasmRefType::Foreign: + return static_cast<const wasm_foreign_t*>(this)->Same( + static_cast<const wasm_foreign_t&>(other)); + case WasmRefType::Trap: + return true; + } + WABT_UNREACHABLE; +} + +struct wasm_frame_t { + wasm_instance_t* instance; + size_t offset; + uint32_t func_index; +}; + +// Type conversion utilities +static Type ToWabtType(wasm_valkind_t kind); +static wasm_valkind_t FromWabtType(Type type); + +Limits ToWabtLimits(const wasm_limits_t& limits) { + return Limits(limits.min, limits.max); +} + +wasm_limits_t FromWabtLimits(const Limits& limits) { + return wasm_limits_t{(uint32_t)limits.initial, (uint32_t)limits.max}; +} + +// Value conversion utilities +static TypedValue ToWabtValue(const wasm_val_t& value); +static void ToWabtValues(TypedValues& out_values, + const wasm_val_t values[], + size_t count); +static wasm_val_t FromWabtValue(std::shared_ptr<WasmInstance> instance, + const TypedValue& value); +static void FromWabtValues(std::shared_ptr<WasmInstance> instance, + wasm_val_t values[], + const TypedValues& wabt_values); + +struct wasm_functype_t : wasm_externtype_t { + wasm_functype_t(interp::FuncSignature& sig) + : wasm_externtype_t(WASM_EXTERN_FUNC) { + TRACE("new wasm_functype_t %" PRIzx " -> %" PRIzx "\n", sig.param_types.size(), + sig.result_types.size()); + wasm_valtype_vec_new_uninitialized(¶ms, sig.param_types.size()); + wasm_valtype_vec_new_uninitialized(&results, sig.result_types.size()); + + for (size_t i = 0; i < sig.param_types.size(); i++) { + params.data[i] = new wasm_valtype_t{FromWabtType(sig.param_types[i])}; + } + for (size_t i = 0; i < sig.result_types.size(); i++) { + results.data[i] = new wasm_valtype_t{FromWabtType(sig.result_types[i])}; + } + } + + wasm_functype_t(wasm_valtype_vec_t* params, wasm_valtype_vec_t* results) + : wasm_externtype_t(WASM_EXTERN_FUNC), + params(*params), + results(*results) {} + + ~wasm_functype_t() { + TRACE("~wasm_functype_t\n"); + wasm_valtype_vec_delete(¶ms); + wasm_valtype_vec_delete(&results); + } + + interp::FuncSignature Sig() const { + std::vector<Type> param_vec; + std::vector<Type> result_vec; + for (size_t i = 0; i < params.size; i++) { + param_vec.push_back(ToWabtType(wasm_valtype_kind(params.data[i]))); + } + for (size_t i = 0; i < results.size; i++) { + result_vec.push_back(ToWabtType(wasm_valtype_kind(results.data[i]))); + } + return interp::FuncSignature{param_vec, result_vec}; + } + + wasm_valtype_vec_t params; + wasm_valtype_vec_t results; +}; + +struct wasm_extern_t : wasm_extern_ref_t { + virtual ~wasm_extern_t() = default; + + interp::Environment* Env() const { return instance.get()->store->env; } + + wasm_extern_t(const wasm_extern_t& other) + : wasm_extern_ref_t(other), instance(other.instance) {} + + virtual wasm_externtype_t* ExternType() const = 0; + + std::shared_ptr<WasmInstance> instance; + + protected: + wasm_extern_t(std::shared_ptr<WasmInstance> instance, + ExternalKind kind, + Index index) + : wasm_extern_ref_t(kind, index), instance(instance) {} +}; + +struct wasm_func_t : wasm_extern_t { + wasm_func_t(std::shared_ptr<WasmInstance> instance, Index index) + : wasm_extern_t(instance, ExternalKind::Func, index) {} + + interp::Func* GetFunc() const { return Env()->GetFunc(index); } + + interp::FuncSignature* GetSig() const { + return Env()->GetFuncSignature(GetFunc()->sig_index); + } + + wasm_externtype_t* ExternType() const override { + return new wasm_functype_t(*GetSig()); + } +}; + +struct wasm_global_t : wasm_extern_t { + wasm_global_t(std::shared_ptr<WasmInstance> instance, Index index) + : wasm_extern_t(instance, ExternalKind::Global, index) {} + + interp::Global* GetGlobal() const { return Env()->GetGlobal(index); } + + wasm_externtype_t* ExternType() const override { + auto* g = GetGlobal(); + return new wasm_globaltype_t({FromWabtType(g->type)}, g->mutable_); + } +}; + +struct wasm_table_t : wasm_extern_t { + wasm_table_t(std::shared_ptr<WasmInstance> instance, Index index) + : wasm_extern_t(instance, ExternalKind::Table, index) {} + + interp::Table* GetTable() const { return Env()->GetTable(index); } + + wasm_externtype_t* ExternType() const override { + auto* t = GetTable(); + return new wasm_tabletype_t({FromWabtType(t->elem_type)}, + FromWabtLimits(t->limits)); + } +}; + +struct wasm_memory_t : wasm_extern_t { + wasm_memory_t(std::shared_ptr<WasmInstance> instance, Index index) + : wasm_extern_t(instance, ExternalKind::Memory, index) {} + + interp::Memory* GetMemory() const { + auto* env = instance.get()->store->env; + return env->GetMemory(index); + } + + wasm_externtype_t* ExternType() const override { + auto* mem = GetMemory(); + return new wasm_memorytype_t(FromWabtLimits(mem->page_limits)); + } +}; + +struct wasm_trap_t : wasm_ref_t { + wasm_trap_t(const wasm_message_t* msg) : wasm_ref_t(WasmRefType::Trap) { + wasm_name_copy(&message, msg); + } + wasm_message_t message; +}; + +// Helper functions + +static Type ToWabtType(wasm_valkind_t kind) { + switch (kind) { + case WASM_I32: + return Type::I32; + case WASM_I64: + return Type::I64; + case WASM_F32: + return Type::F32; + case WASM_F64: + return Type::F64; + case WASM_ANYREF: + return Type::Anyref; + case WASM_FUNCREF: + return Type::Funcref; + default: + TRACE("unexpected wasm_valkind_t: %d\n", kind); + WABT_UNREACHABLE; + } + WABT_UNREACHABLE; +} + +static wasm_valkind_t FromWabtType(Type type) { + switch (type) { + case Type::I32: + return WASM_I32; + case Type::I64: + return WASM_I64; + case Type::F32: + return WASM_F32; + case Type::F64: + return WASM_F64; + case Type::Anyref: + return WASM_ANYREF; + case Type::Funcref: + return WASM_FUNCREF; + default: + WABT_UNREACHABLE; + } +} + +static TypedValue ToWabtValue(const wasm_val_t& value) { + TypedValue out(ToWabtType(value.kind)); + switch (value.kind) { + case WASM_I32: + out.set_i32(value.of.i32); + break; + case WASM_I64: + out.set_i64(value.of.i64); + break; + case WASM_F32: + out.set_f32(value.of.f32); + break; + case WASM_F64: + out.set_f64(value.of.f64); + break; + case WASM_FUNCREF: { + auto* ext = static_cast<wasm_extern_t*>(value.of.ref); + assert(ext->kind == ExternalKind::Func); + out.set_ref(Ref{RefType::Func, ext->index}); + break; + } + case WASM_ANYREF: { + wasm_ref_t* ref = value.of.ref; + if (!ref) { + out.set_ref(Ref{RefType::Null, kInvalidIndex}); + break; + } + if (ref->kind == WasmRefType::Foreign) { + auto* f = static_cast<wasm_foreign_t*>(ref); + out.set_ref(Ref{RefType::Host, f->index}); + out.type = Type::Hostref; + break; + } + printf("unexpected WASM_ANYREF in ToWabtValue: %d\n", + static_cast<int>(ref->kind)); + WABT_UNREACHABLE; + break; + } + default: + TRACE("unexpected wasm type: %d\n", value.kind); + assert(false); + } + TRACE("ToWabtValue -> %s\n", TypedValueToString(out).c_str()); + return out; +} + +static void ToWabtValues(TypedValues& wabt_values, + const wasm_val_t values[], + size_t count) { + for (size_t i = 0; i < count; i++) { + wabt_values.push_back(ToWabtValue(values[i])); + } +} + +// wasm_byte_vec + +static wasm_val_t FromWabtValue(std::shared_ptr<WasmInstance> instance, + const TypedValue& value) { + TRACE("FromWabtValue: %s\n", TypedValueToString(value).c_str()); + wasm_val_t out_value; + switch (value.type) { + case Type::I32: + out_value.kind = WASM_I32; + out_value.of.i32 = value.get_i32(); + break; + case Type::I64: + out_value.kind = WASM_I64; + out_value.of.i64 = value.get_i64(); + break; + case Type::F32: + out_value.kind = WASM_F32; + out_value.of.f32 = value.get_f32(); + break; + case Type::F64: + out_value.kind = WASM_F64; + out_value.of.f64 = value.get_f64(); + break; + case Type::Funcref: + out_value.kind = WASM_FUNCREF; + out_value.of.ref = new wasm_func_t(instance, value.get_ref().index); + break; + case Type::Hostref: + out_value.kind = WASM_ANYREF; + out_value.of.ref = new wasm_foreign_t(value.get_ref().index); + break; + case Type::Nullref: + out_value.kind = WASM_ANYREF; + out_value.of.ref = nullptr; + break; + default: + TRACE("unexpected wabt type: %d\n", static_cast<int>(value.type)); + assert(false); + } + return out_value; +} + +static void FromWabtValues(std::shared_ptr<WasmInstance> instance, + wasm_val_t values[], + const TypedValues& wabt_values) { + for (size_t i = 0; i < wabt_values.size(); i++) { + values[i] = FromWabtValue(instance, wabt_values[i]); + } +} + +static wasm_externkind_t FromWabtExternalKind(ExternalKind kind) { + switch (kind) { + case ExternalKind::Func: + return WASM_EXTERN_FUNC; + case ExternalKind::Global: + return WASM_EXTERN_GLOBAL; + case ExternalKind::Table: + return WASM_EXTERN_TABLE; + case ExternalKind::Memory: + return WASM_EXTERN_MEMORY; + case ExternalKind::Event: + WABT_UNREACHABLE; + } + WABT_UNREACHABLE; +} + +static ReadBinaryOptions GetOptions() { + const bool kReadDebugNames = true; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + s_features.EnableAll(); + if (getenv("WASM_API_DEBUG") != nullptr) { + s_log_stream = FileStream::CreateStdout(); + } + return ReadBinaryOptions(s_features, s_log_stream.get(), kReadDebugNames, + kStopOnFirstError, kFailOnCustomSectionError); +} + +extern "C" { + +// wasm_valtype + +wasm_valtype_t* wasm_valtype_new(wasm_valkind_t kind) { + return new wasm_valtype_t{kind}; +} + +wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t* type) { + assert(type); + return type->kind; +} + +// Helpers + +static void print_sig(const interp::FuncSignature& sig) { +#ifndef NDEBUG + fprintf(stderr, "("); + bool first = true; + for (auto Type : sig.param_types) { + if (!first) { + fprintf(stderr, ", "); + } + first = false; + fprintf(stderr, "%s", GetTypeName(Type)); + } + fprintf(stderr, ") -> ("); + first = true; + for (auto Type : sig.result_types) { + if (!first) { + fprintf(stderr, ", "); + } + first = false; + fprintf(stderr, "%s", GetTypeName(Type)); + } + fprintf(stderr, ")\n"); +#endif +} + +// wasm_val + +void wasm_val_delete(wasm_val_t* val) { + assert(val); + if (wasm_valkind_is_ref(val->kind)) { + delete val->of.ref; + val->of.ref = nullptr; + } +} + +void wasm_val_copy(wasm_val_t* out, const wasm_val_t* in) { + TRACE("wasm_val_copy %s\n", TypedValueToString(ToWabtValue(*in)).c_str()); + *out = *in; + TRACE("wasm_val_copy -> %p %s\n", out, + TypedValueToString(ToWabtValue(*out)).c_str()); +} + +// wasm_trap + +wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t* msg) { + return new wasm_trap_t(msg); +} + +void wasm_trap_message(const wasm_trap_t* trap, wasm_message_t* out) { + assert(trap); + wasm_name_copy(out, &trap->message); +} + +wasm_frame_t* wasm_trap_origin(const wasm_trap_t* trap) { + // TODO(sbc): Implement stack traces + return nullptr; +} + +void wasm_trap_trace(const wasm_trap_t* trap, wasm_frame_vec_t* out) { + assert(trap); + assert(out); + wasm_frame_vec_new_empty(out); + // std::string msg(trap->message.data, trap->message.size); + // fTRACE(stderr, "error: %s\n", msg.c_str()); + return; +} + +// wasm_engine + +wasm_engine_t* wasm_engine_new() { + return new wasm_engine_t(); +} + +wasm_engine_t* wasm_engine_new_with_config(wasm_config_t*) { + assert(false); + return nullptr; +} + +// wasm_store + +wasm_store_t* wasm_store_new(wasm_engine_t* engine) { + assert(engine); + if (!s_trace_stream) { + s_trace_stream = s_stdout_stream.get(); + } + Environment* env = new Environment(s_features); + Executor* executor = new Executor(env, s_trace_stream, s_thread_options); + return new wasm_store_t(env, executor); +} + +// wasm_module + +wasm_module_t* wasm_module_new(wasm_store_t* store, + const wasm_byte_vec_t* binary) { + Errors errors; + ModuleMetadata* metadata = nullptr; + wabt::Result result = ReadBinaryMetadata(binary->data, binary->size, + GetOptions(), &errors, &metadata); + if (!Succeeded(result)) { + return nullptr; + } + return new wasm_module_t(store, binary, metadata); +} + +void wasm_module_exports(const wasm_module_t* module, + wasm_exporttype_vec_t* out) { + size_t num_exports = module->metadata->exports.size(); + TRACE("wasm_module_exports: %" PRIzx "\n", num_exports); + wasm_exporttype_vec_new_uninitialized(out, num_exports); + auto* env = module->store->env; + for (size_t i = 0; i < num_exports; i++) { + interp::Export& exp = module->metadata->exports[i]; + wasm_name_t name; + wasm_name_new_from_string(&name, exp.name.c_str()); + TRACE("module_export: %s\n", exp.name.c_str()); + switch (exp.kind) { + case ExternalKind::Func: { + interp::Func* func = env->GetFunc(exp.index); + auto* type = + new wasm_functype_t(*env->GetFuncSignature(func->sig_index)); + out->data[i] = new wasm_exporttype_t{name, type}; + break; + } + case ExternalKind::Global: { + auto* g = env->GetGlobal(exp.index); + auto* type = + new wasm_globaltype_t({FromWabtType(g->type)}, g->mutable_); + out->data[i] = new wasm_exporttype_t{name, type}; + break; + } + case ExternalKind::Table: { + auto* t = env->GetTable(exp.index); + auto* type = new wasm_tabletype_t({FromWabtType(t->elem_type)}, + FromWabtLimits(t->limits)); + out->data[i] = new wasm_exporttype_t{name, type}; + break; + } + case ExternalKind::Memory: { + auto* mem = env->GetMemory(exp.index); + auto* type = new wasm_memorytype_t(FromWabtLimits(mem->page_limits)); + out->data[i] = new wasm_exporttype_t{name, type}; + break; + } + case ExternalKind::Event: + WABT_UNREACHABLE; + } + } +} + +void wasm_module_serialize(const wasm_module_t* module, wasm_byte_vec_t* out) { + wasm_byte_vec_copy(out, &module->binary); +} + +wasm_module_t* wasm_module_deserialize(wasm_store_t* store, + const wasm_byte_vec_t* bytes) { + return wasm_module_new(store, bytes); +} + +// wasm_exporttype + +wasm_exporttype_t* wasm_exporttype_new(wasm_name_t* name, + wasm_externtype_t* type) { + return new wasm_exporttype_t{*name, type}; +} + +const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t* ex) { + return &ex->name; +} + +const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t* ex) { + TRACE("wasm_exporttype_type %p\n", ex); + assert(ex); + return ex->type.get(); +} + +// wasm_instance + +wasm_instance_t* wasm_instance_new(wasm_store_t* store, + const wasm_module_t* module, + const wasm_extern_t* const imports[], + wasm_trap_t** trap_out) { + TRACE("wasm_instance_new: %p %p\n", store, module); + assert(module); + assert(module->metadata); + assert(store); + assert(store->env); + + Errors errors; + DefinedModule* interp_module = nullptr; + std::vector<interp::Export> wabt_imports; + std::vector<interp::Export*> wabt_import_ptrs; + + for (size_t i = 0; i < module->metadata->imports.size(); i++) { + wabt_imports.emplace_back("", imports[i]->kind, imports[i]->index); + } + + for (auto& i : wabt_imports) { + wabt_import_ptrs.push_back(&i); + } + + wabt::Result result = + ReadBinaryInterp(store->env, module->binary.data, module->binary.size, + GetOptions(), wabt_import_ptrs, &errors, &interp_module); + + FormatErrorsToFile(errors, Location::Type::Binary); + if (!Succeeded(result)) { + TRACE("ReadBinaryInterp failed!\n"); + return nullptr; + } + + ExecResult exec_result = store->executor->Initialize(interp_module); + if (!exec_result.ok()) { + TRACE("Initialize failed!\n"); + wasm_name_t msg; + wasm_name_new_from_string(&msg, ResultToString(exec_result.result).c_str()); + wasm_trap_t* trap = wasm_trap_new(store, &msg); + *trap_out = trap; + return nullptr; + } + return new wasm_instance_t(store, interp_module); +} + +void wasm_instance_exports(const wasm_instance_t* instance, + wasm_extern_vec_t* out) { + WasmInstance& wasm_instance = *instance->ptr.get(); + DefinedModule* module = wasm_instance.module.get(); + size_t num_exports = module->exports.size(); + wasm_extern_vec_new_uninitialized(out, num_exports); + TRACE("wasm_instance_exports: %" PRIzx "\n", num_exports); + + for (size_t i = 0; i < num_exports; i++) { + interp::Export* export_ = &module->exports[i]; + TRACE("getexport: '%s' %d\n", export_->name.c_str(), + static_cast<int>(export_->kind)); + switch (export_->kind) { + case ExternalKind::Func: + out->data[i] = new wasm_func_t(instance->ptr, export_->index); + break; + case ExternalKind::Global: + out->data[i] = new wasm_global_t(instance->ptr, export_->index); + break; + case ExternalKind::Table: + out->data[i] = new wasm_table_t(instance->ptr, export_->index); + break; + case ExternalKind::Memory: + out->data[i] = new wasm_memory_t(instance->ptr, export_->index); + break; + case ExternalKind::Event: + WABT_UNREACHABLE; + } + } +} + +// wasm_functype + +wasm_functype_t* wasm_functype_new(wasm_valtype_vec_t* params, + wasm_valtype_vec_t* results) { + TRACE("wasm_functype_new params=%" PRIzx " args=%" PRIzx "\n", params->size, results->size); + auto* res = new wasm_functype_t(params, results); + interp::FuncSignature sig = res->Sig(); + TRACE("wasm_functype_new -> "); + print_sig(sig); + return res; +} + +const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t* f) { + TRACE("wasm_functype_params: %" PRIzx "\n", f->params.size); + return &f->params; +} + +const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t* f) { + return &f->results; +} + +// wasm_func + +static wasm_func_t* do_wasm_func_new( + wasm_store_t* store, + const wasm_functype_t* type, + wasm_func_callback_t host_callback, + wasm_func_callback_with_env_t host_callback_with_env, + void* env, + void (*finalizer)(void*)) { + TRACE("do_wasm_func_new\n"); + type->Sig(); + auto instance = std::make_shared<WasmInstance>(store, nullptr); + + HostFunc::Callback callback = + [instance, host_callback, host_callback_with_env, env]( + const HostFunc* func, const interp::FuncSignature* sig, + const TypedValues& args, TypedValues& results) -> interp::Result { + TRACE("calling host function: "); + print_sig(*sig); + wasm_val_t* host_args = new wasm_val_t[args.size()]; + wasm_val_t* host_results = new wasm_val_t[sig->result_types.size()]; + FromWabtValues(instance, host_args, args); + wasm_trap_t* trap; + if (host_callback) { + trap = host_callback(host_args, host_results); + } else { + assert(host_callback_with_env); + trap = host_callback_with_env(env, host_args, host_results); + } + if (trap) { + TRACE("host function trapped\n"); + delete[] host_args; + delete[] host_results; + return interp::ResultType::TrapHostTrapped; + } + TRACE("adding result types: %" PRIzx "\n", sig->result_types.size()); + for (size_t i = 0; i < sig->result_types.size(); i++) { + TRACE("result: %p\n", &host_results[i]); + results[i] = ToWabtValue(host_results[i]); + TRACE("added result value: %s\n", TypedValueToString(results[i]).c_str()); + } + delete[] host_args; + delete[] host_results; + return interp::ResultType::Ok; + }; + + type->Sig(); + type->Sig(); + static int function_count = 0; + std::string name = std::string("function") + std::to_string(function_count++); + store->env->EmplaceBackFuncSignature(type->Sig()); + Index sig_index = store->env->GetFuncSignatureCount() - 1; + auto* host_func = new HostFunc("extern", name, sig_index, callback); + store->env->EmplaceBackFunc(host_func); + Index func_index = store->env->GetFuncCount() - 1; + return new wasm_func_t(instance, func_index); +} + +wasm_func_t* wasm_func_new(wasm_store_t* store, + const wasm_functype_t* type, + wasm_func_callback_t callback) { + return do_wasm_func_new(store, type, callback, nullptr, nullptr, nullptr); +} + +wasm_func_t* wasm_func_new_with_env(wasm_store_t* store, + const wasm_functype_t* type, + wasm_func_callback_with_env_t callback, + void* env, + void (*finalizer)(void*)) { + return do_wasm_func_new(store, type, nullptr, callback, env, finalizer); +} + +wasm_functype_t* wasm_func_type(const wasm_func_t* func) { + TRACE("wasm_func_type\n"); + return new wasm_functype_t(*func->GetSig()); +} + +size_t wasm_func_result_arity(const wasm_func_t* func) { + interp::Func* wabt_func = func->GetFunc(); + auto* env = func->instance.get()->store->env; + interp::FuncSignature* sig = env->GetFuncSignature(wabt_func->sig_index); + return sig->result_types.size(); +} + +size_t wasm_func_param_arity(const wasm_func_t* func) { + interp::Func* wabt_func = func->GetFunc(); + auto* env = func->instance.get()->store->env; + interp::FuncSignature* sig = env->GetFuncSignature(wabt_func->sig_index); + return sig->param_types.size(); +} + +wasm_trap_t* wasm_func_call(const wasm_func_t* f, + const wasm_val_t args[], + wasm_val_t results[]) { + TRACE("wasm_func_call %d\n", f->index); + assert(f); + wasm_store_t* store = f->instance.get()->store; + assert(store); + wasm_functype_t* functype = wasm_func_type(f); + TypedValues wabt_args; + ToWabtValues(wabt_args, args, functype->params.size); + wasm_functype_delete(functype); + assert(f->kind == ExternalKind::Func); + ExecResult res = store->executor->RunFunction(f->index, wabt_args); + if (!res.ok()) { + std::string msg = ResultToString(res.result); + TRACE("wasm_func_call failed: %s\n", msg.c_str()); + wasm_name_t message; + wasm_name_new_from_string(&message, msg.c_str()); + wasm_trap_t* trap = wasm_trap_new(store, &message); + wasm_name_delete(&message); + return trap; + } + FromWabtValues(f->instance, results, res.values); + return nullptr; +} + +// wasm_globaltype + +wasm_globaltype_t* wasm_globaltype_new(wasm_valtype_t* type, + wasm_mutability_t mut) { + assert(type); + return new wasm_globaltype_t(*type, mut == WASM_VAR); +} + +wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t* type) { + assert(type); + return type->mutable_; +} + +const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t* type) { + assert(type); + return &type->type; +} + +// wasm_tabletype + +wasm_tabletype_t* wasm_tabletype_new(wasm_valtype_t* type, + const wasm_limits_t* limits) { + return new wasm_tabletype_t(*type, *limits); +} + +const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t* type) { + return &type->elemtype; +} + +const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t* type) { + return &type->limits; +} + +// wasm_memorytype + +wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t* limits) { + return new wasm_memorytype_t(*limits); +} + +const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t* t) { + return &t->limits; +} + +// wasm_global + +wasm_global_t* wasm_global_new(wasm_store_t* store, + const wasm_globaltype_t* type, + const wasm_val_t* val) { + assert(store && store && type); + TypedValue value = ToWabtValue(*val); + TRACE("wasm_global_new: %s\n", TypedValueToString(value).c_str()); + interp::Global* g = store->env->EmplaceBackGlobal(value.type, type->mutable_); + g->typed_value = value; + Index global_index = store->env->GetGlobalCount() - 1; + auto instance = std::make_shared<WasmInstance>(store, nullptr); + return new wasm_global_t(instance, global_index); +} + +wasm_globaltype_t* wasm_global_type(const wasm_global_t* global) { + assert(global); + assert(false); + WABT_UNREACHABLE; +} + +void wasm_global_get(const wasm_global_t* global, wasm_val_t* out) { + assert(global); + TRACE("wasm_global_get"); + interp::Global* interp_global = global->GetGlobal(); + TRACE(" -> %s\n", TypedValueToString(interp_global->typed_value).c_str()); + *out = FromWabtValue(global->instance, interp_global->typed_value); + return; +} + +void wasm_global_set(wasm_global_t* global, const wasm_val_t* val) { + TRACE("wasm_global_set\n"); + interp::Global* g = global->GetGlobal(); + g->typed_value = ToWabtValue(*val); +} + +// wasm_table + +wasm_table_t* wasm_table_new(wasm_store_t* store, + const wasm_tabletype_t* type, + wasm_ref_t* init) { + store->env->EmplaceBackTable(ToWabtType(type->elemtype.kind), + ToWabtLimits(type->limits)); + Index index = store->env->GetTableCount() - 1; + auto instance = std::make_shared<WasmInstance>(store, nullptr); + return new wasm_table_t(instance, index); +} + +wasm_tabletype_t* wasm_table_type(const wasm_table_t*) { + assert(false); + return nullptr; +} + +wasm_table_size_t wasm_table_size(const wasm_table_t* table) { + return table->GetTable()->size(); +} + +wasm_ref_t* wasm_table_get(const wasm_table_t* t, wasm_table_size_t index) { + interp::Table* table = t->GetTable(); + // TODO(sbc): This duplicates code from the CallIndirect handler. I imagine + // we will refactor this when we implment the TableGet opcode. + if (index >= table->size()) + return nullptr; + switch (table->entries[index].kind) { + case RefType::Func: { + Index func_index = table->entries[index].index; + if (func_index == kInvalidIndex) + return nullptr; + TRACE("wasm_table_get: %u -> %u\n", index, func_index); + return new wasm_extern_ref_t(ExternalKind::Func, func_index); + } + case RefType::Host: + return new wasm_foreign_ref_t(table->entries[index].index); + case RefType::Null: + return nullptr; + } + WABT_UNREACHABLE; +} + +bool wasm_table_set(wasm_table_t* t, wasm_table_size_t index, wasm_ref_t* ref) { + interp::Table* table = t->GetTable(); + if (index >= table->size()) { + return false; + } + TRACE("wasm_table_set %p\n", ref); + if (!ref) { + table->entries[index] = Ref{RefType::Null, kInvalidIndex}; + return true; + } + TRACE("wasm_table_set %s\n", WasmRefTypeToString(ref->kind).c_str()); + switch (ref->kind) { + case WasmRefType::Extern: { + auto* ext = static_cast<wasm_extern_ref_t*>(ref); + switch (ext->kind) { + case ExternalKind::Func: + table->entries[index] = Ref{RefType::Func, ext->index}; + break; + case ExternalKind::Table: + case ExternalKind::Memory: + case ExternalKind::Global: + case ExternalKind::Event: + return false; + } + break; + } + case WasmRefType::Foreign: { + auto* f = static_cast<wasm_foreign_ref_t*>(ref); + table->entries[index] = Ref{RefType::Host, f->index}; + break; + } + case WasmRefType::Module: + case WasmRefType::Instance: + case WasmRefType::Trap: + return false; + } + return true; +} + +bool wasm_table_grow(wasm_table_t* t, + wasm_table_size_t delta, + wasm_ref_t* init) { + interp::Table* table = t->GetTable(); + size_t cursize = table->size(); + size_t newsize = cursize + delta; + if (newsize > table->limits.max) { + return false; + } + TRACE("wasm_table_grow %" PRIzx " -> %" PRIzx "\n", cursize, newsize); + auto* ext = static_cast<wasm_extern_ref_t*>(init); + if (ext && ext->kind != ExternalKind::Func) { + return false; + } + Ref init_ref{RefType::Null, kInvalidIndex}; + if (ext) { + init_ref = Ref{RefType::Func, ext->index}; + } + table->entries.resize(newsize, init_ref); + return true; +} + +// wams_memory + +wasm_memory_t* wasm_memory_new(wasm_store_t* store, + const wasm_memorytype_t* type) { + TRACE("wasm_memory_new\n"); + store->env->EmplaceBackMemory(Limits(type->limits.min, type->limits.max)); + Index index = store->env->GetMemoryCount() - 1; + auto instance = std::make_shared<WasmInstance>(store, nullptr); + return new wasm_memory_t(instance, index); +} + +byte_t* wasm_memory_data(wasm_memory_t* m) { + interp::Memory* memory = m->GetMemory(); + return memory->data.data(); +} + +wasm_memory_pages_t wasm_memory_size(const wasm_memory_t* m) { + interp::Memory* memory = m->GetMemory(); + return memory->data.size() / WABT_PAGE_SIZE; +} + +size_t wasm_memory_data_size(const wasm_memory_t* m) { + interp::Memory* memory = m->GetMemory(); + return memory->data.size(); +} + +bool wasm_memory_grow(wasm_memory_t* m, wasm_memory_pages_t delta) { + interp::Memory* memory = m->GetMemory(); + size_t cursize = memory->data.size() / WABT_PAGE_SIZE; + size_t newsize = cursize + delta; + if (newsize > memory->page_limits.max) { + return false; + } + TRACE("wasm_memory_grow %" PRIzx " -> %" PRIzx "\n", cursize, newsize); + memory->data.resize(newsize * WABT_PAGE_SIZE); + return true; +} + +// wasm_frame + +wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame) { + assert(frame); + return frame->instance; +} + +size_t wasm_frame_module_offset(const wasm_frame_t* frame) { + assert(frame); + return frame->offset; +} + +size_t wasm_frame_func_offset(const wasm_frame_t* frame) { + assert(false); + return 0; +} + +uint32_t wasm_frame_func_index(const wasm_frame_t* frame) { + assert(frame); + return frame->func_index; +} + +// wasm_externtype + +wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t* type) { + assert(type); + return type->kind; +} + +// wasm_extern + +wasm_externtype_t* wasm_extern_type(const wasm_extern_t* extern_) { + return extern_->ExternType(); +} + +wasm_externkind_t wasm_extern_kind(const wasm_extern_t* extern_) { + return FromWabtExternalKind(extern_->kind); +} + +// wasm_foreign + +wasm_foreign_t* wasm_foreign_new(wasm_store_t* store) { + store->env->EmplaceBackHostObject(); + Index new_index = store->env->GetHostCount() - 1; + return new wasm_foreign_t(new_index); +} + +// vector types + +#define WASM_IMPL_OWN(name) \ + void wasm_##name##_delete(wasm_##name##_t* t) { \ + assert(t); \ + TRACE("wasm_" #name "_delete\n"); \ + delete t; \ + } + +WASM_IMPL_OWN(frame); +WASM_IMPL_OWN(config); +WASM_IMPL_OWN(engine); +WASM_IMPL_OWN(store); + +#define WASM_IMPL_VEC(name, ptr_or_none) \ + void wasm_##name##_vec_new(wasm_##name##_vec_t* vec, size_t size, \ + wasm_##name##_t ptr_or_none const src[]) { \ + TRACE("wasm_" #name "_vec_new\n"); \ + wasm_##name##_vec_new_uninitialized(vec, size); \ + memcpy(vec->data, src, size * sizeof(wasm_##name##_t ptr_or_none)); \ + } \ + void wasm_##name##_vec_new_empty(wasm_##name##_vec_t* out) { \ + TRACE("wasm_" #name "_vec_new_empty\n"); \ + out->data = nullptr; \ + out->size = 0; \ + } \ + void wasm_##name##_vec_new_uninitialized(wasm_##name##_vec_t* vec, \ + size_t size) { \ + TRACE("wasm_" #name "_vec_new_uninitialized %" PRIzx "\n", size); \ + vec->size = size; \ + if (size) \ + vec->data = new wasm_##name##_t ptr_or_none[size]; \ + else \ + vec->data = nullptr; \ + } \ + void wasm_##name##_vec_copy(wasm_##name##_vec_t* out, \ + const wasm_##name##_vec_t* vec) { \ + TRACE("wasm_" #name "_vec_copy %" PRIzx "\n", vec->size); \ + wasm_##name##_vec_new_uninitialized(out, vec->size); \ + memcpy(out->data, vec->data, \ + vec->size * sizeof(wasm_##name##_t ptr_or_none)); \ + } \ + void wasm_##name##_vec_delete(wasm_##name##_vec_t* vec) { \ + TRACE("wasm_" #name "_vec_delete\n"); \ + if (vec->data) { \ + delete vec->data; \ + } \ + vec->size = 0; \ + } + +WASM_IMPL_VEC(byte, ); +WASM_IMPL_VEC(val, ); +WASM_IMPL_VEC(frame, *); +WASM_IMPL_VEC(extern, *); + +#define WASM_IMPL_TYPE(name) \ + WASM_IMPL_OWN(name) \ + WASM_IMPL_VEC(name, *) \ + wasm_##name##_t* wasm_##name##_copy(wasm_##name##_t* other) { \ + return new wasm_##name##_t(*other); \ + } + +WASM_IMPL_TYPE(valtype); +WASM_IMPL_TYPE(functype); +WASM_IMPL_TYPE(globaltype); +WASM_IMPL_TYPE(tabletype); +WASM_IMPL_TYPE(memorytype); +WASM_IMPL_TYPE(externtype); +WASM_IMPL_TYPE(importtype); +WASM_IMPL_TYPE(exporttype); + +#define WASM_IMPL_REF_BASE(name) \ + WASM_IMPL_OWN(name) \ + wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t* ref) { \ + TRACE("wasm_" #name "_copy\n"); \ + return static_cast<wasm_##name##_t*>(ref->Copy()); \ + } \ + bool wasm_##name##_same(const wasm_##name##_t* ref, \ + const wasm_##name##_t* other) { \ + TRACE("wasm_" #name "_same\n"); \ + return ref->Same(*other); \ + } \ + void* wasm_##name##_get_host_info(const wasm_##name##_t* ref) { \ + return ref->host_info; \ + } \ + void wasm_##name##_set_host_info(wasm_##name##_t* ref, void* info) { \ + ref->host_info = info; \ + } \ + void wasm_##name##_set_host_info_with_finalizer( \ + wasm_##name##_t* ref, void* info, void (*finalizer)(void*)) { \ + ref->host_info = info; \ + ref->finalizer = finalizer; \ + } + +#define WASM_IMPL_REF(name) \ + WASM_IMPL_REF_BASE(name) \ + wasm_ref_t* wasm_##name##_as_ref(wasm_##name##_t* subclass) { \ + return subclass; \ + } \ + wasm_##name##_t* wasm_ref_as_##name(wasm_ref_t* ref) { \ + return static_cast<wasm_##name##_t*>(ref); \ + } \ + const wasm_ref_t* wasm_##name##_as_ref_const( \ + const wasm_##name##_t* subclass) { \ + return subclass; \ + } \ + const wasm_##name##_t* wasm_ref_as_##name##_const(const wasm_ref_t* ref) { \ + return static_cast<const wasm_##name##_t*>(ref); \ + } + +WASM_IMPL_REF_BASE(ref); + +WASM_IMPL_REF(extern); +WASM_IMPL_REF(foreign); +WASM_IMPL_REF(func); +WASM_IMPL_REF(global); +WASM_IMPL_REF(instance); +WASM_IMPL_REF(memory); +WASM_IMPL_REF(table); +WASM_IMPL_REF(trap); + +#define WASM_IMPL_SHARABLE_REF(name) \ + WASM_IMPL_REF(name) \ + WASM_IMPL_OWN(shared_##name) \ + wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t* t) { \ + return static_cast<wasm_shared_##name##_t*>( \ + const_cast<wasm_##name##_t*>(t)); \ + } \ + wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, \ + const wasm_shared_##name##_t* t) { \ + return static_cast<wasm_##name##_t*>( \ + const_cast<wasm_shared_##name##_t*>(t)); \ + } + +WASM_IMPL_SHARABLE_REF(module) + +#define WASM_IMPL_EXTERN(name) \ + const wasm_##name##type_t* wasm_externtype_as_##name##type_const( \ + const wasm_externtype_t* t) { \ + return static_cast<const wasm_##name##type_t*>(t); \ + } \ + wasm_##name##type_t* wasm_externtype_as_##name##type(wasm_externtype_t* t) { \ + return static_cast<wasm_##name##type_t*>(t); \ + } \ + wasm_externtype_t* wasm_##name##type_as_externtype(wasm_##name##type_t* t) { \ + return static_cast<wasm_externtype_t*>(t); \ + } \ + const wasm_externtype_t* wasm_##name##type_as_externtype_const( \ + const wasm_##name##type_t* t) { \ + return static_cast<const wasm_externtype_t*>(t); \ + } \ + wasm_extern_t* wasm_##name##_as_extern(wasm_##name##_t* name) { \ + return static_cast<wasm_extern_t*>(name); \ + } \ + const wasm_extern_t* wasm_##name##_as_extern_const( \ + const wasm_##name##_t* name) { \ + return static_cast<const wasm_extern_t*>(name); \ + } \ + wasm_##name##_t* wasm_extern_as_##name(wasm_extern_t* ext) { \ + return static_cast<wasm_##name##_t*>(ext); \ + } + +WASM_IMPL_EXTERN(table); +WASM_IMPL_EXTERN(func); +WASM_IMPL_EXTERN(global); +WASM_IMPL_EXTERN(memory); + +} // extern "C" diff --git a/src/interp/interp.h b/src/interp/interp.h index 6d445b56..bbc1c407 100644 --- a/src/interp/interp.h +++ b/src/interp/interp.h @@ -342,6 +342,13 @@ struct Export { class Environment; +struct ModuleMetadata { + WABT_DISALLOW_COPY_AND_ASSIGN(ModuleMetadata); + ModuleMetadata() = default; + std::vector<Import> imports; + std::vector<Export> exports; +}; + struct Module { WABT_DISALLOW_COPY_AND_ASSIGN(Module); Module(Environment* env, bool is_host); diff --git a/test/run-c-api-examples.py b/test/run-c-api-examples.py new file mode 100755 index 00000000..7511a002 --- /dev/null +++ b/test/run-c-api-examples.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# +# Copyright 2020 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. +# + +from __future__ import print_function + +import argparse +import os +import subprocess +import sys + +import find_exe + +ALL_EXAMPLES = [ + 'callback', + 'finalize', + 'global', + 'hello', + 'hostref', + 'memory', + 'multi', + 'reflect', + 'serialize', + 'start', + 'table', + 'threads', + 'trap', +] + +# We don't currently yet support shared modules which is required for threads. +SKIP_EXAMPLES = [ + 'threads', # We don't yet support threads + 'finalize', # This test is really slow +] + +IS_WINDOWS = sys.platform == 'win32' + + +def run_test(test_exe): + print('Running.. %s' % test_exe) + proc = subprocess.Popen([test_exe], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + stdout, _ = proc.communicate() + if proc.returncode == 0: + return 0 + print("Failed with returncode=%d" % proc.returncode) + print(stdout) + print('FAIL(%d): %s' % (proc.returncode, test_exe)) + return 1 + + +def error(msg): + print(msg) + sys.exit(1) + + +def check_for_missing(upstream_examples): + # Check that all the expected examples are found + + upstream_examples = set(upstream_examples) + all_examples = set(ALL_EXAMPLES) + unexpected = upstream_examples - all_examples + if unexpected: + error('Unexpected examples found: %s' % str(unexpected)) + missing = all_examples - upstream_examples + if missing: + error('Missing example binaries not found: %s' % str(missing)) + + +def main(args): + print('Running c-api examples..') + parser = argparse.ArgumentParser() + parser.add_argument('--bindir', metavar='PATH', + default=find_exe.GetDefaultPath(), + help='directory to search for all executables.') + + options = parser.parse_args(args) + script_dir = os.path.dirname(os.path.abspath(__file__)) + root = os.path.dirname(script_dir) + upstream_dir = os.path.join(root, 'third_party', 'wasm-c-api', 'example') + upstream_examples = [f for f in os.listdir(upstream_dir) if os.path.splitext(f)[1] == '.wasm'] + upstream_examples = [os.path.splitext(f)[0] for f in upstream_examples] + check_for_missing(upstream_examples) + + def should_skip(e): + return any(e.startswith(skip) for skip in SKIP_EXAMPLES) + + to_run = [e for e in upstream_examples if not should_skip(e)] + + os.chdir(options.bindir) + count = 0 + fail_count = 0 + for f in to_run: + if IS_WINDOWS: + f += '.exe' + exe = os.path.join(options.bindir, 'wasm-c-api-' + f) + if not os.path.exists(exe): + error('test executable not found: %s' % exe) + + count += 1 + if run_test(exe) != 0: + fail_count += 1 + + if fail_count: + print('[%d/%d] c-api examples failed' % (fail_count, count)) + return 1 + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/third_party/wasm-c-api b/third_party/wasm-c-api new file mode 160000 +Subproject d9a80099d496b5cdba6f3fe8fc77586e0e505dd |