| // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #ifdef DART_ENABLE_WASM |
| |
| #include <memory> |
| #include <sstream> |
| |
| #include "platform/unicode.h" |
| #include "third_party/wasmer/wasmer.hh" |
| #include "vm/bootstrap_natives.h" |
| #include "vm/dart_api_state.h" |
| #include "vm/dart_entry.h" |
| #include "vm/exceptions.h" |
| |
| namespace dart { |
| |
| static void ThrowWasmerError() { |
| String& error = String::Handle(); |
| { |
| int len = wasmer_last_error_length(); |
| auto raw_error = std::unique_ptr<char[]>(new char[len]); |
| int read_len = wasmer_last_error_message(raw_error.get(), len); |
| ASSERT(read_len == len); |
| error = String::NewFormatted("Wasmer error: %s", raw_error.get()); |
| } |
| Exceptions::ThrowArgumentError(error); |
| UNREACHABLE(); |
| } |
| |
| template <typename T> |
| static void Finalize(void* isolate_callback_data, |
| Dart_WeakPersistentHandle handle, |
| void* peer) { |
| delete reinterpret_cast<T*>(peer); |
| } |
| |
| static void FinalizeWasmModule(void* isolate_callback_data, |
| Dart_WeakPersistentHandle handle, |
| void* module) { |
| wasmer_module_destroy(reinterpret_cast<wasmer_module_t*>(module)); |
| } |
| |
| static void FinalizeWasmMemory(void* isolate_callback_data, |
| Dart_WeakPersistentHandle handle, |
| void* memory) { |
| wasmer_memory_destroy(reinterpret_cast<wasmer_memory_t*>(memory)); |
| } |
| |
| static std::unique_ptr<char[]> ToUTF8(const String& str) { |
| const intptr_t str_size = Utf8::Length(str); |
| auto str_raw = std::unique_ptr<char[]>(new char[str_size + 1]); |
| str.ToUTF8(reinterpret_cast<uint8_t*>(str_raw.get()), str_size); |
| str_raw[str_size] = '\0'; |
| return str_raw; |
| } |
| |
| static bool ToWasmValue(const Number& value, |
| classid_t type, |
| wasmer_value_t* out) { |
| switch (type) { |
| case kWasmInt32Cid: |
| if (!value.IsInteger()) return false; |
| out->tag = wasmer_value_tag::WASM_I32; |
| out->value.I32 = Integer::Cast(value).AsInt64Value(); |
| return true; |
| case kWasmInt64Cid: |
| if (!value.IsInteger()) return false; |
| out->tag = wasmer_value_tag::WASM_I64; |
| out->value.I64 = Integer::Cast(value).AsInt64Value(); |
| return true; |
| case kWasmFloatCid: |
| if (!value.IsDouble()) return false; |
| out->tag = wasmer_value_tag::WASM_F32; |
| out->value.F32 = Double::Cast(value).value(); |
| return true; |
| case kWasmDoubleCid: |
| if (!value.IsDouble()) return false; |
| out->tag = wasmer_value_tag::WASM_F64; |
| out->value.F64 = Double::Cast(value).value(); |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static Dart_Handle ToWasmValue(Dart_Handle value, |
| wasmer_value_tag type, |
| wasmer_value* out) { |
| switch (type) { |
| case wasmer_value_tag::WASM_I32: { |
| int64_t i64; |
| Dart_Handle result = Dart_IntegerToInt64(value, &i64); |
| out->I32 = i64; |
| if (out->I32 != i64) { |
| return Dart_NewApiError("Int doesn't fit into 32-bits"); |
| } |
| return result; |
| } |
| case wasmer_value_tag::WASM_I64: |
| return Dart_IntegerToInt64(value, &out->I64); |
| case wasmer_value_tag::WASM_F32: { |
| double f64; |
| Dart_Handle result = Dart_DoubleValue(value, &f64); |
| out->F32 = f64; |
| return result; |
| } |
| case wasmer_value_tag::WASM_F64: |
| return Dart_DoubleValue(value, &out->F64); |
| default: |
| FATAL("Unknown WASM type"); |
| return nullptr; |
| } |
| } |
| |
| static bool ToWasmValueTag(classid_t type, wasmer_value_tag* out) { |
| switch (type) { |
| case kWasmInt32Cid: |
| *out = wasmer_value_tag::WASM_I32; |
| return true; |
| case kWasmInt64Cid: |
| *out = wasmer_value_tag::WASM_I64; |
| return true; |
| case kWasmFloatCid: |
| *out = wasmer_value_tag::WASM_F32; |
| return true; |
| case kWasmDoubleCid: |
| *out = wasmer_value_tag::WASM_F64; |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static ObjectPtr ToDartObject(wasmer_value_t ret) { |
| switch (ret.tag) { |
| case wasmer_value_tag::WASM_I32: |
| return Integer::New(ret.value.I32); |
| case wasmer_value_tag::WASM_I64: |
| return Integer::New(ret.value.I64); |
| case wasmer_value_tag::WASM_F32: |
| return Double::New(ret.value.F32); |
| case wasmer_value_tag::WASM_F64: |
| return Double::New(ret.value.F64); |
| default: |
| FATAL("Unknown WASM type"); |
| return nullptr; |
| } |
| } |
| |
| static Dart_Handle ToDartApiObject(wasmer_value value, wasmer_value_tag type) { |
| switch (type) { |
| case wasmer_value_tag::WASM_I32: |
| return Dart_NewInteger(value.I32); |
| case wasmer_value_tag::WASM_I64: |
| return Dart_NewInteger(value.I64); |
| case wasmer_value_tag::WASM_F32: |
| return Dart_NewDouble(value.F32); |
| case wasmer_value_tag::WASM_F64: |
| return Dart_NewDouble(value.F64); |
| default: |
| FATAL("Unknown WASM type"); |
| return nullptr; |
| } |
| } |
| |
| ExternalTypedDataPtr WasmMemoryToExternalTypedData(wasmer_memory_t* memory) { |
| uint8_t* data = wasmer_memory_data(memory); |
| uint32_t size = wasmer_memory_data_length(memory); |
| return ExternalTypedData::New(kExternalTypedDataUint8ArrayCid, data, size); |
| } |
| |
| std::ostream& operator<<(std::ostream& o, const wasmer_byte_array& str) { |
| for (uint32_t i = 0; i < str.bytes_len; ++i) { |
| o << str.bytes[i]; |
| } |
| return o; |
| } |
| |
| std::ostream& operator<<(std::ostream& o, const wasmer_import_export_kind& io) { |
| switch (io) { |
| case wasmer_import_export_kind::WASM_FUNCTION: |
| return o << "WASM_FUNCTION"; |
| case wasmer_import_export_kind::WASM_GLOBAL: |
| return o << "WASM_GLOBAL"; |
| case wasmer_import_export_kind::WASM_MEMORY: |
| return o << "WASM_MEMORY"; |
| case wasmer_import_export_kind::WASM_TABLE: |
| return o << "WASM_TABLE"; |
| } |
| } |
| |
| StringPtr DescribeModule(const wasmer_module_t* module) { |
| std::stringstream desc; |
| |
| desc << "Imports:\n"; |
| wasmer_import_descriptors_t* imports; |
| wasmer_import_descriptors(module, &imports); |
| unsigned num_imports = wasmer_import_descriptors_len(imports); |
| for (unsigned i = 0; i < num_imports; ++i) { |
| wasmer_import_descriptor_t* imp = wasmer_import_descriptors_get(imports, i); |
| desc << "\t" << wasmer_import_descriptor_module_name(imp); |
| desc << "\t" << wasmer_import_descriptor_name(imp); |
| desc << "\t" << wasmer_import_descriptor_kind(imp); |
| desc << "\n"; |
| } |
| wasmer_import_descriptors_destroy(imports); |
| |
| desc << "\nExports:\n"; |
| wasmer_export_descriptors_t* exports; |
| wasmer_export_descriptors(module, &exports); |
| unsigned num_exports = wasmer_export_descriptors_len(exports); |
| for (unsigned i = 0; i < num_exports; ++i) { |
| wasmer_export_descriptor_t* exp = wasmer_export_descriptors_get(exports, i); |
| desc << "\t" << wasmer_export_descriptor_name(exp); |
| desc << "\t" << wasmer_export_descriptor_kind(exp); |
| desc << "\n"; |
| } |
| wasmer_export_descriptors_destroy(exports); |
| |
| return String::New(desc.str().c_str()); |
| } |
| |
| class WasmImports; |
| |
| struct WasmFunctionImport { |
| WasmImports* imports; |
| std::unique_ptr<wasmer_value_tag[]> args; |
| intptr_t num_args; |
| wasmer_value_tag ret; |
| intptr_t num_rets; |
| int64_t fn_id; |
| wasmer_import_func_t* wasm_fn; |
| wasmer_trampoline_buffer_t* buffer; |
| WasmFunctionImport(WasmImports* imports_, |
| std::unique_ptr<wasmer_value_tag[]> args_, |
| intptr_t num_args_, |
| wasmer_value_tag ret_, |
| intptr_t num_rets_, |
| int64_t fn_id_) |
| : imports(imports_), |
| args(std::move(args_)), |
| num_args(num_args_), |
| ret(ret_), |
| num_rets(num_rets_), |
| fn_id(fn_id_), |
| wasm_fn(nullptr), |
| buffer(nullptr) {} |
| ~WasmFunctionImport() { |
| wasmer_trampoline_buffer_destroy(buffer); |
| wasmer_import_func_destroy(wasm_fn); |
| } |
| }; |
| |
| extern "C" { |
| int64_t Trampoline(void* context, int64_t* args); |
| } |
| |
| class WasmImports { |
| public: |
| WasmImports() {} |
| |
| ~WasmImports() { |
| for (wasmer_global_t* global : _globals) { |
| wasmer_global_destroy(global); |
| } |
| for (WasmFunctionImport* fn_imp : _functions) { |
| delete fn_imp; |
| } |
| for (const char* name : _import_names) { |
| delete[] name; |
| } |
| } |
| |
| void SetHandle(FinalizablePersistentHandle* handle) { _handle = handle; } |
| size_t NumImports() const { return _imports.length(); } |
| wasmer_import_t* RawImports() { return _imports.data(); } |
| |
| void AddMemory(std::unique_ptr<char[]> module_name, |
| std::unique_ptr<char[]> name, |
| wasmer_memory_t* memory) { |
| AddImport(std::move(module_name), std::move(name), |
| wasmer_import_export_kind::WASM_MEMORY) |
| ->memory = memory; |
| } |
| |
| void AddGlobal(std::unique_ptr<char[]> module_name, |
| std::unique_ptr<char[]> name, |
| wasmer_value_t value, |
| bool mutable_) { |
| wasmer_global_t* global = wasmer_global_new(value, mutable_); |
| _globals.Add(global); |
| AddImport(std::move(module_name), std::move(name), |
| wasmer_import_export_kind::WASM_GLOBAL) |
| ->global = global; |
| } |
| |
| void AddFunction(std::unique_ptr<char[]> module_name, |
| std::unique_ptr<char[]> name, |
| int64_t fn_id, |
| std::unique_ptr<wasmer_value_tag[]> args, |
| intptr_t num_args, |
| wasmer_value_tag ret, |
| intptr_t num_rets) { |
| // Trampoline args include the context pointer. |
| const intptr_t num_trampoline_args = num_args + 1; |
| |
| WasmFunctionImport* fn_imp = new WasmFunctionImport( |
| this, std::move(args), num_args, ret, num_rets, fn_id); |
| _functions.Add(fn_imp); |
| |
| wasmer_trampoline_buffer_builder_t* builder = |
| wasmer_trampoline_buffer_builder_new(); |
| uintptr_t trampoline_id = |
| wasmer_trampoline_buffer_builder_add_callinfo_trampoline( |
| builder, |
| reinterpret_cast<wasmer_trampoline_callable_t*>(Trampoline), |
| reinterpret_cast<void*>(fn_imp), num_trampoline_args); |
| fn_imp->buffer = wasmer_trampoline_buffer_builder_build(builder); |
| |
| const wasmer_trampoline_callable_t* trampoline = |
| wasmer_trampoline_buffer_get_trampoline(fn_imp->buffer, trampoline_id); |
| fn_imp->wasm_fn = wasmer_import_func_new( |
| reinterpret_cast<void (*)(void*)>( |
| const_cast<wasmer_trampoline_callable_t*>(trampoline)), |
| fn_imp->args.get(), num_args, &ret, num_rets); |
| |
| AddImport(std::move(module_name), std::move(name), |
| wasmer_import_export_kind::WASM_FUNCTION) |
| ->func = fn_imp->wasm_fn; |
| } |
| |
| int64_t CallImportedFunction(WasmFunctionImport* fn_imp, int64_t* raw_args) { |
| wasmer_value* wasm_args = reinterpret_cast<wasmer_value*>(raw_args); |
| Dart_Handle inst = Dart_HandleFromWeakPersistent(_handle->apiHandle()); |
| Dart_Handle dart_args[2] = { |
| inst, |
| Dart_NewInteger(fn_imp->fn_id), |
| }; |
| Dart_Handle closure = Dart_Invoke(Dart_InstanceGetType(inst), |
| Dart_NewStringFromCString("getFunction"), |
| ARRAY_SIZE(dart_args), dart_args); |
| if (Dart_IsError(closure)) { |
| Dart_ThrowException(closure); |
| UNREACHABLE(); |
| } |
| Dart_Handle result; |
| { |
| auto args = |
| std::unique_ptr<Dart_Handle[]>(new Dart_Handle[fn_imp->num_args]); |
| for (intptr_t i = 0; i < fn_imp->num_args; ++i) { |
| args[i] = ToDartApiObject(wasm_args[i], fn_imp->args[i]); |
| } |
| result = Dart_InvokeClosure(closure, fn_imp->num_args, args.get()); |
| } |
| if (Dart_IsError(result)) { |
| Dart_ThrowException(result); |
| UNREACHABLE(); |
| } |
| if (fn_imp->num_rets == 0) { |
| // Wasmer ignores the result of this function if it expects no results, |
| // so skip the converters below (we get errors if we run them). |
| return 0; |
| } |
| wasmer_value wasm_result; |
| result = ToWasmValue(result, fn_imp->ret, &wasm_result); |
| if (Dart_IsError(result)) { |
| Dart_ThrowException(result); |
| UNREACHABLE(); |
| } |
| return wasm_result.I64; |
| } |
| |
| private: |
| FinalizablePersistentHandle* _handle; |
| MallocGrowableArray<const char*> _import_names; |
| MallocGrowableArray<wasmer_global_t*> _globals; |
| MallocGrowableArray<WasmFunctionImport*> _functions; |
| MallocGrowableArray<wasmer_import_t> _imports; |
| |
| wasmer_import_export_value* AddImport(std::unique_ptr<char[]> module_name, |
| std::unique_ptr<char[]> name, |
| wasmer_import_export_kind tag) { |
| wasmer_import_t import; |
| import.module_name.bytes = |
| reinterpret_cast<const uint8_t*>(module_name.get()); |
| import.module_name.bytes_len = (uint32_t)strlen(module_name.get()); |
| import.import_name.bytes = reinterpret_cast<const uint8_t*>(name.get()); |
| import.import_name.bytes_len = (uint32_t)strlen(name.get()); |
| import.tag = tag; |
| _import_names.Add(module_name.release()); |
| _import_names.Add(name.release()); |
| _imports.Add(import); |
| return &_imports.Last().value; |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(WasmImports); |
| }; |
| |
| extern "C" { |
| int64_t Trampoline(void* context, int64_t* args) { |
| WasmFunctionImport* fn_imp = reinterpret_cast<WasmFunctionImport*>(context); |
| // Skip the first arg (it's another context pointer). |
| return fn_imp->imports->CallImportedFunction(fn_imp, args + 1); |
| } |
| } |
| |
| class WasmFunction { |
| public: |
| WasmFunction(MallocGrowableArray<classid_t> args, |
| classid_t ret, |
| const wasmer_export_func_t* fn) |
| : _args(std::move(args)), _ret(ret), _fn(fn) {} |
| bool IsVoid() const { return _ret == kWasmVoidCid; } |
| const MallocGrowableArray<classid_t>& args() const { return _args; } |
| |
| bool SignatureMatches(const MallocGrowableArray<classid_t>& dart_args, |
| classid_t dart_ret) { |
| if (dart_args.length() != _args.length()) { |
| return false; |
| } |
| for (intptr_t i = 0; i < dart_args.length(); ++i) { |
| if (dart_args[i] != _args[i]) { |
| return false; |
| } |
| } |
| return dart_ret == _ret; |
| } |
| |
| wasmer_result_t Call(const wasmer_value_t* params, wasmer_value_t* result) { |
| return wasmer_export_func_call(_fn, params, _args.length(), result, |
| IsVoid() ? 0 : 1); |
| } |
| |
| void Print(std::ostream& o, const char* name) const { |
| PrintDartType(o, _ret); |
| o << ' ' << name << '('; |
| for (intptr_t i = 0; i < _args.length(); ++i) { |
| if (i > 0) o << ", "; |
| PrintDartType(o, _args[i]); |
| } |
| o << ')'; |
| } |
| |
| private: |
| MallocGrowableArray<classid_t> _args; |
| const classid_t _ret; |
| const wasmer_export_func_t* _fn; |
| |
| static void PrintDartType(std::ostream& o, classid_t type) { |
| switch (type) { |
| case kWasmInt32Cid: |
| o << "i32"; |
| break; |
| case kWasmInt64Cid: |
| o << "i64"; |
| break; |
| case kWasmFloatCid: |
| o << "f32"; |
| break; |
| case kWasmDoubleCid: |
| o << "f64"; |
| break; |
| case kWasmVoidCid: |
| o << "void"; |
| break; |
| } |
| } |
| }; |
| |
| class WasmInstance { |
| public: |
| WasmInstance() : _instance(nullptr), _exports(nullptr), _memory(nullptr) {} |
| |
| bool Instantiate(wasmer_module_t* module, WasmImports* imports) { |
| ASSERT(_instance == nullptr); |
| |
| // Instantiate module. |
| if (wasmer_module_instantiate(module, &_instance, imports->RawImports(), |
| imports->NumImports()) != |
| wasmer_result_t::WASMER_OK) { |
| return false; |
| } |
| |
| // Load all exports. |
| wasmer_instance_exports(_instance, &_exports); |
| intptr_t num_exports = wasmer_exports_len(_exports); |
| for (intptr_t i = 0; i < num_exports; ++i) { |
| wasmer_export_t* exp = wasmer_exports_get(_exports, i); |
| wasmer_import_export_kind kind = wasmer_export_kind(exp); |
| if (kind == wasmer_import_export_kind::WASM_FUNCTION) { |
| if (!AddFunction(exp)) { |
| return false; |
| } |
| } else if (kind == wasmer_import_export_kind::WASM_MEMORY) { |
| ASSERT(_memory == nullptr); |
| if (wasmer_export_to_memory(exp, &_memory) != |
| wasmer_result_t::WASMER_OK) { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| ~WasmInstance() { |
| auto it = _functions.GetIterator(); |
| for (auto* kv = it.Next(); kv; kv = it.Next()) { |
| delete[] kv->key; |
| delete kv->value; |
| } |
| if (_exports != nullptr) { |
| wasmer_exports_destroy(_exports); |
| } |
| if (_instance != nullptr) { |
| wasmer_instance_destroy(_instance); |
| } |
| } |
| |
| WasmFunction* GetFunction(const char* name, |
| const MallocGrowableArray<classid_t>& dart_args, |
| classid_t dart_ret, |
| String* error) { |
| WasmFunction* fn = _functions.LookupValue(name); |
| if (fn == nullptr) { |
| *error = String::NewFormatted( |
| "Couldn't find a function called %s in the WASM module's exports", |
| name); |
| return nullptr; |
| } |
| if (!fn->SignatureMatches(dart_args, dart_ret)) { |
| std::stringstream sig; |
| fn->Print(sig, name); |
| *error = String::NewFormatted("Function signature doesn't match: %s", |
| sig.str().c_str()); |
| return nullptr; |
| } |
| return fn; |
| } |
| |
| void PrintFunctions(std::ostream& o) const { |
| o << '{' << std::endl; |
| auto it = _functions.GetIterator(); |
| for (auto* kv = it.Next(); kv; kv = it.Next()) { |
| kv->value->Print(o, kv->key); |
| o << std::endl; |
| } |
| o << '}' << std::endl; |
| } |
| |
| wasmer_memory_t* memory() { return _memory; } |
| |
| private: |
| wasmer_instance_t* _instance; |
| wasmer_exports_t* _exports; |
| MallocDirectChainedHashMap<CStringKeyValueTrait<WasmFunction*>> _functions; |
| wasmer_memory_t* _memory; |
| |
| static classid_t ToDartType(wasmer_value_tag wasm_type) { |
| switch (wasm_type) { |
| case wasmer_value_tag::WASM_I32: |
| return kWasmInt32Cid; |
| case wasmer_value_tag::WASM_I64: |
| return kWasmInt64Cid; |
| case wasmer_value_tag::WASM_F32: |
| return kWasmFloatCid; |
| case wasmer_value_tag::WASM_F64: |
| return kWasmDoubleCid; |
| } |
| FATAL("Unknown WASM type"); |
| return 0; |
| } |
| |
| bool AddFunction(wasmer_export_t* exp) { |
| const wasmer_export_func_t* fn = wasmer_export_to_func(exp); |
| |
| uint32_t num_rets; |
| if (wasmer_export_func_returns_arity(fn, &num_rets) != |
| wasmer_result_t::WASMER_OK) { |
| return false; |
| } |
| ASSERT(num_rets <= 1); |
| wasmer_value_tag wasm_ret; |
| if (wasmer_export_func_returns(fn, &wasm_ret, num_rets) != |
| wasmer_result_t::WASMER_OK) { |
| return false; |
| } |
| classid_t ret = num_rets == 0 ? kWasmVoidCid : ToDartType(wasm_ret); |
| |
| uint32_t num_args; |
| if (wasmer_export_func_params_arity(fn, &num_args) != |
| wasmer_result_t::WASMER_OK) { |
| return false; |
| } |
| auto wasm_args = |
| std::unique_ptr<wasmer_value_tag[]>(new wasmer_value_tag[num_args]); |
| if (wasmer_export_func_params(fn, wasm_args.get(), num_args) != |
| wasmer_result_t::WASMER_OK) { |
| return false; |
| } |
| MallocGrowableArray<classid_t> args; |
| for (intptr_t i = 0; i < num_args; ++i) { |
| args.Add(ToDartType(wasm_args[i])); |
| } |
| |
| wasmer_byte_array name_bytes = wasmer_export_name(exp); |
| char* name = new char[name_bytes.bytes_len + 1]; |
| for (size_t i = 0; i < name_bytes.bytes_len; ++i) { |
| name[i] = name_bytes.bytes[i]; |
| } |
| name[name_bytes.bytes_len] = '\0'; |
| |
| _functions.Insert({name, new WasmFunction(std::move(args), ret, fn)}); |
| return true; |
| } |
| |
| DISALLOW_COPY_AND_ASSIGN(WasmInstance); |
| }; |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initModule, 0, 2) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, mod_wrap, arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(TypedDataBase, data, arguments->NativeArgAt(1)); |
| |
| ASSERT(mod_wrap.NumNativeFields() == 1); |
| |
| std::unique_ptr<uint8_t[]> data_copy; |
| intptr_t len; |
| { |
| NoSafepointScope scope(thread); |
| len = data.LengthInBytes(); |
| data_copy = std::unique_ptr<uint8_t[]>(new uint8_t[len]); |
| // The memory does not overlap. |
| memcpy(data_copy.get(), data.DataAddr(0), len); // NOLINT |
| } |
| |
| wasmer_module_t* module; |
| wasmer_result_t result; |
| { |
| TransitionVMToNative transition(thread); |
| result = wasmer_compile(&module, data_copy.get(), len); |
| } |
| if (result != wasmer_result_t::WASMER_OK) { |
| data_copy.reset(); |
| ThrowWasmerError(); |
| UNREACHABLE(); |
| } |
| |
| mod_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(module)); |
| FinalizablePersistentHandle::New(thread->isolate(), mod_wrap, module, |
| FinalizeWasmModule, len); |
| |
| return Object::null(); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_describeModule, 0, 1) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, mod_wrap, arguments->NativeArgAt(0)); |
| |
| ASSERT(mod_wrap.NumNativeFields() == 1); |
| |
| wasmer_module_t* module = |
| reinterpret_cast<wasmer_module_t*>(mod_wrap.GetNativeField(0)); |
| |
| return DescribeModule(module); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initImports, 0, 1) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0)); |
| |
| ASSERT(imp_wrap.NumNativeFields() == 1); |
| |
| WasmImports* imports = new WasmImports(); |
| |
| imp_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(imports)); |
| imports->SetHandle(FinalizablePersistentHandle::New( |
| thread->isolate(), imp_wrap, imports, Finalize<WasmImports>, |
| sizeof(WasmImports))); |
| |
| return Object::null(); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_addMemoryImport, 0, 4) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(String, module_name, arguments->NativeArgAt(1)); |
| GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(2)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(3)); |
| |
| ASSERT(imp_wrap.NumNativeFields() == 1); |
| ASSERT(mem_wrap.NumNativeFields() == 1); |
| |
| WasmImports* imports = |
| reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0)); |
| wasmer_memory_t* memory = |
| reinterpret_cast<wasmer_memory_t*>(mem_wrap.GetNativeField(0)); |
| |
| imports->AddMemory(ToUTF8(module_name), ToUTF8(name), memory); |
| |
| return Object::null(); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_addGlobalImport, 0, 6) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(String, module_name, arguments->NativeArgAt(1)); |
| GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(2)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Number, value, arguments->NativeArgAt(3)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Type, type, arguments->NativeArgAt(4)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Bool, mutable_, arguments->NativeArgAt(5)); |
| |
| ASSERT(imp_wrap.NumNativeFields() == 1); |
| |
| WasmImports* imports = |
| reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0)); |
| wasmer_value_t wasm_value; |
| if (!ToWasmValue(value, type.type_class_id(), &wasm_value)) { |
| Exceptions::ThrowArgumentError(String::Handle( |
| zone, String::NewFormatted( |
| "Can't convert dart value to WASM global variable"))); |
| UNREACHABLE(); |
| } |
| |
| imports->AddGlobal(ToUTF8(module_name), ToUTF8(name), wasm_value, |
| mutable_.value()); |
| |
| return Object::null(); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_addFunctionImport, 0, 5) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(String, module_name, arguments->NativeArgAt(1)); |
| GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(2)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, fn_id, arguments->NativeArgAt(3)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Type, fn_type, arguments->NativeArgAt(4)); |
| |
| ASSERT(imp_wrap.NumNativeFields() == 1); |
| |
| Function& sig = Function::Handle(zone, fn_type.signature()); |
| |
| classid_t ret = AbstractType::Handle(zone, sig.result_type()).type_class_id(); |
| intptr_t num_rets = ret == kWasmVoidCid ? 0 : 1; |
| wasmer_value_tag wasm_ret = wasmer_value_tag::WASM_I64; |
| if (num_rets != 0) { |
| if (!ToWasmValueTag(ret, &wasm_ret)) { |
| Exceptions::ThrowArgumentError(String::Handle( |
| zone, String::NewFormatted("Return type is not a valid WASM type"))); |
| UNREACHABLE(); |
| } |
| } |
| |
| Array& args = Array::Handle(zone, sig.parameter_types()); |
| AbstractType& arg_type = AbstractType::Handle(zone); |
| intptr_t first_arg_index = sig.NumImplicitParameters(); |
| intptr_t num_args = args.Length() - first_arg_index; |
| auto wasm_args = |
| std::unique_ptr<wasmer_value_tag[]>(new wasmer_value_tag[num_args]); |
| for (intptr_t i = 0; i < num_args; ++i) { |
| arg_type ^= args.At(i + first_arg_index); |
| classid_t dart_arg = arg_type.type_class_id(); |
| if (!ToWasmValueTag(dart_arg, &wasm_args[i])) { |
| wasm_args.reset(); |
| Exceptions::ThrowArgumentError(String::Handle( |
| zone, String::NewFormatted( |
| "Type of arg %" Pd " is not a valid WASM type", i))); |
| UNREACHABLE(); |
| } |
| } |
| |
| WasmImports* imports = |
| reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0)); |
| imports->AddFunction(ToUTF8(module_name), ToUTF8(name), fn_id.AsInt64Value(), |
| std::move(wasm_args), num_args, wasm_ret, num_rets); |
| |
| return Object::null(); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initMemory, 0, 3) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, init, arguments->NativeArgAt(1)); |
| GET_NATIVE_ARGUMENT(Integer, max, arguments->NativeArgAt(2)); |
| |
| ASSERT(mem_wrap.NumNativeFields() == 1); |
| const int64_t init_size = init.AsInt64Value(); |
| |
| wasmer_memory_t* memory; |
| wasmer_limits_t descriptor; |
| descriptor.min = init_size; |
| if (max.IsNull()) { |
| descriptor.max.has_some = false; |
| } else { |
| descriptor.max.has_some = true; |
| descriptor.max.some = max.AsInt64Value(); |
| } |
| if (wasmer_memory_new(&memory, descriptor) != wasmer_result_t::WASMER_OK) { |
| ThrowWasmerError(); |
| UNREACHABLE(); |
| } |
| mem_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(memory)); |
| FinalizablePersistentHandle::New(thread->isolate(), mem_wrap, memory, |
| FinalizeWasmMemory, init_size); |
| return WasmMemoryToExternalTypedData(memory); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_growMemory, 0, 2) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, delta, arguments->NativeArgAt(1)); |
| |
| ASSERT(mem_wrap.NumNativeFields() == 1); |
| |
| wasmer_memory_t* memory = |
| reinterpret_cast<wasmer_memory_t*>(mem_wrap.GetNativeField(0)); |
| if (wasmer_memory_grow(memory, delta.AsInt64Value()) != |
| wasmer_result_t::WASMER_OK) { |
| ThrowWasmerError(); |
| UNREACHABLE(); |
| } |
| return WasmMemoryToExternalTypedData(memory); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initInstance, 0, 3) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, inst_wrap, arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, mod_wrap, arguments->NativeArgAt(1)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(2)); |
| |
| ASSERT(inst_wrap.NumNativeFields() == 1); |
| ASSERT(mod_wrap.NumNativeFields() == 1); |
| ASSERT(imp_wrap.NumNativeFields() == 1); |
| |
| wasmer_module_t* module = |
| reinterpret_cast<wasmer_module_t*>(mod_wrap.GetNativeField(0)); |
| WasmImports* imports = |
| reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0)); |
| |
| WasmInstance* inst = nullptr; |
| { |
| TransitionVMToNative transition(thread); |
| inst = new WasmInstance(); |
| if (!inst->Instantiate(module, imports)) { |
| delete inst; |
| inst = nullptr; |
| } |
| } |
| if (inst == nullptr) { |
| ThrowWasmerError(); |
| UNREACHABLE(); |
| } |
| |
| inst_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(inst)); |
| FinalizablePersistentHandle::New(thread->isolate(), inst_wrap, inst, |
| Finalize<WasmInstance>, |
| sizeof(WasmInstance)); |
| |
| return Object::null(); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initMemoryFromInstance, 0, 2) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, inst_wrap, arguments->NativeArgAt(1)); |
| |
| ASSERT(mem_wrap.NumNativeFields() == 1); |
| ASSERT(inst_wrap.NumNativeFields() == 1); |
| |
| WasmInstance* inst = |
| reinterpret_cast<WasmInstance*>(inst_wrap.GetNativeField(0)); |
| |
| wasmer_memory_t* memory = inst->memory(); |
| |
| mem_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(memory)); |
| FinalizablePersistentHandle::New(thread->isolate(), mem_wrap, memory, |
| FinalizeWasmMemory, |
| wasmer_memory_length(memory)); |
| return WasmMemoryToExternalTypedData(memory); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_getMemoryPages, 0, 1) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0)); |
| |
| ASSERT(mem_wrap.NumNativeFields() == 1); |
| |
| wasmer_memory_t* memory = |
| reinterpret_cast<wasmer_memory_t*>(mem_wrap.GetNativeField(0)); |
| |
| return Integer::New(wasmer_memory_length(memory)); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initFunction, 0, 4) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, fn_wrap, arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, inst_wrap, arguments->NativeArgAt(1)); |
| GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(2)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Type, fn_type, arguments->NativeArgAt(3)); |
| |
| ASSERT(fn_wrap.NumNativeFields() == 1); |
| ASSERT(inst_wrap.NumNativeFields() == 1); |
| |
| WasmInstance* inst = |
| reinterpret_cast<WasmInstance*>(inst_wrap.GetNativeField(0)); |
| |
| WasmFunction* fn; |
| String& error = String::Handle(zone); |
| |
| { |
| Function& sig = Function::Handle(zone, fn_type.signature()); |
| Array& args = Array::Handle(zone, sig.parameter_types()); |
| AbstractType& arg_type = AbstractType::Handle(zone); |
| MallocGrowableArray<classid_t> dart_args; |
| for (intptr_t i = sig.NumImplicitParameters(); i < args.Length(); ++i) { |
| arg_type ^= args.At(i); |
| dart_args.Add(arg_type.type_class_id()); |
| } |
| classid_t dart_ret = |
| AbstractType::Handle(zone, sig.result_type()).type_class_id(); |
| |
| std::unique_ptr<char[]> name_raw = ToUTF8(name); |
| fn = inst->GetFunction(name_raw.get(), dart_args, dart_ret, &error); |
| } |
| |
| if (fn == nullptr) { |
| Exceptions::ThrowArgumentError(error); |
| UNREACHABLE(); |
| } |
| |
| fn_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(fn)); |
| // Don't need a finalizer because WasmFunctions are owned their WasmInstance. |
| |
| return Object::null(); |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_callFunction, 0, 2) { |
| GET_NON_NULL_NATIVE_ARGUMENT(Instance, fn_wrap, arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(1)); |
| |
| ASSERT(fn_wrap.NumNativeFields() == 1); |
| WasmFunction* fn = reinterpret_cast<WasmFunction*>(fn_wrap.GetNativeField(0)); |
| |
| if (args.Length() != fn->args().length()) { |
| Exceptions::ThrowArgumentError(String::Handle( |
| zone, String::NewFormatted("Wrong number of args. Expected %" Pu |
| " but found %" Pd ".", |
| fn->args().length(), args.Length()))); |
| UNREACHABLE(); |
| } |
| auto params = std::unique_ptr<wasmer_value_t[]>( |
| new wasmer_value_t[fn->args().length()]); |
| Number& arg_num = Number::Handle(zone); |
| for (intptr_t i = 0; i < args.Length(); ++i) { |
| arg_num ^= args.At(i); |
| if (!ToWasmValue(arg_num, fn->args()[i], ¶ms[i])) { |
| params.reset(); |
| Exceptions::ThrowArgumentError(String::Handle( |
| zone, String::NewFormatted("Arg %" Pd " is the wrong type.", i))); |
| UNREACHABLE(); |
| } |
| } |
| |
| wasmer_value_t ret; |
| wasmer_result_t result; |
| { |
| TransitionVMToNative transition(thread); |
| result = fn->Call(params.get(), &ret); |
| } |
| if (result != wasmer_result_t::WASMER_OK) { |
| params.reset(); |
| ThrowWasmerError(); |
| UNREACHABLE(); |
| } |
| return fn->IsVoid() ? Object::null() : ToDartObject(ret); |
| } |
| |
| } // namespace dart |
| |
| #else // DART_ENABLE_WASM |
| |
| #include "vm/bootstrap_natives.h" |
| #include "vm/dart_entry.h" |
| #include "vm/exceptions.h" |
| |
| namespace dart { |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initModule, 0, 2) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_describeModule, 0, 1) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initImports, 0, 1) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_addMemoryImport, 0, 4) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_addGlobalImport, 0, 6) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_addFunctionImport, 0, 5) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initMemory, 0, 3) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_growMemory, 0, 3) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initInstance, 0, 3) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initMemoryFromInstance, 0, 2) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_getMemoryPages, 0, 1) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_initFunction, 0, 4) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| DEFINE_NATIVE_ENTRY(Wasm_callFunction, 0, 2) { |
| Exceptions::ThrowUnsupportedError("WASM is disabled"); |
| return nullptr; |
| } |
| |
| } // namespace dart |
| |
| #endif // DART_ENABLE_WASM |