// 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 #include #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(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 static void Finalize(void* isolate_callback_data, Dart_WeakPersistentHandle handle, void* peer) { delete reinterpret_cast(peer); } static void FinalizeWasmModule(void* isolate_callback_data, Dart_WeakPersistentHandle handle, void* module) { wasmer_module_destroy(reinterpret_cast(module)); } static void FinalizeWasmMemory(void* isolate_callback_data, Dart_WeakPersistentHandle handle, void* memory) { wasmer_memory_destroy(reinterpret_cast(memory)); } static std::unique_ptr ToUTF8(const String& str) { const intptr_t str_size = Utf8::Length(str); auto str_raw = std::unique_ptr(new char[str_size + 1]); str.ToUTF8(reinterpret_cast(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 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 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 module_name, std::unique_ptr 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 module_name, std::unique_ptr 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 module_name, std::unique_ptr name, int64_t fn_id, std::unique_ptr 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(Trampoline), reinterpret_cast(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( const_cast(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(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(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 _import_names; MallocGrowableArray _globals; MallocGrowableArray _functions; MallocGrowableArray _imports; wasmer_import_export_value* AddImport(std::unique_ptr module_name, std::unique_ptr name, wasmer_import_export_kind tag) { wasmer_import_t import; import.module_name.bytes = reinterpret_cast(module_name.get()); import.module_name.bytes_len = (uint32_t)strlen(module_name.get()); import.import_name.bytes = reinterpret_cast(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(context); // Skip the first arg (it's another context pointer). return fn_imp->imports->CallImportedFunction(fn_imp, args + 1); } } class WasmFunction { public: WasmFunction(MallocGrowableArray 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& args() const { return _args; } bool SignatureMatches(const MallocGrowableArray& 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 _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& 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> _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(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 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 data_copy; intptr_t len; { NoSafepointScope scope(thread); len = data.LengthInBytes(); data_copy = std::unique_ptr(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(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(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(imports)); imports->SetHandle(FinalizablePersistentHandle::New( thread->isolate(), imp_wrap, imports, Finalize, 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(imp_wrap.GetNativeField(0)); wasmer_memory_t* memory = reinterpret_cast(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(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(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(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(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(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(mod_wrap.GetNativeField(0)); WasmImports* imports = reinterpret_cast(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(inst)); FinalizablePersistentHandle::New(thread->isolate(), inst_wrap, inst, Finalize, 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(inst_wrap.GetNativeField(0)); wasmer_memory_t* memory = inst->memory(); mem_wrap.SetNativeField(0, reinterpret_cast(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(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(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 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 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(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(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( 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