1// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifdef DART_ENABLE_WASM
6
7#include <memory>
8#include <sstream>
9
10#include "platform/unicode.h"
11#include "third_party/wasmer/wasmer.hh"
12#include "vm/bootstrap_natives.h"
13#include "vm/dart_api_state.h"
14#include "vm/dart_entry.h"
15#include "vm/exceptions.h"
16
17namespace dart {
18
19static void ThrowWasmerError() {
20 String& error = String::Handle();
21 {
22 int len = wasmer_last_error_length();
23 auto raw_error = std::unique_ptr<char[]>(new char[len]);
24 int read_len = wasmer_last_error_message(raw_error.get(), len);
25 ASSERT(read_len == len);
26 error = String::NewFormatted("Wasmer error: %s", raw_error.get());
27 }
28 Exceptions::ThrowArgumentError(error);
29 UNREACHABLE();
30}
31
32template <typename T>
33static void Finalize(void* isolate_callback_data,
34 Dart_WeakPersistentHandle handle,
35 void* peer) {
36 delete reinterpret_cast<T*>(peer);
37}
38
39static void FinalizeWasmModule(void* isolate_callback_data,
40 Dart_WeakPersistentHandle handle,
41 void* module) {
42 wasmer_module_destroy(reinterpret_cast<wasmer_module_t*>(module));
43}
44
45static void FinalizeWasmMemory(void* isolate_callback_data,
46 Dart_WeakPersistentHandle handle,
47 void* memory) {
48 wasmer_memory_destroy(reinterpret_cast<wasmer_memory_t*>(memory));
49}
50
51static std::unique_ptr<char[]> ToUTF8(const String& str) {
52 const intptr_t str_size = Utf8::Length(str);
53 auto str_raw = std::unique_ptr<char[]>(new char[str_size + 1]);
54 str.ToUTF8(reinterpret_cast<uint8_t*>(str_raw.get()), str_size);
55 str_raw[str_size] = '\0';
56 return str_raw;
57}
58
59static bool ToWasmValue(const Number& value,
60 classid_t type,
61 wasmer_value_t* out) {
62 switch (type) {
63 case kWasmInt32Cid:
64 if (!value.IsInteger()) return false;
65 out->tag = wasmer_value_tag::WASM_I32;
66 out->value.I32 = Integer::Cast(value).AsInt64Value();
67 return true;
68 case kWasmInt64Cid:
69 if (!value.IsInteger()) return false;
70 out->tag = wasmer_value_tag::WASM_I64;
71 out->value.I64 = Integer::Cast(value).AsInt64Value();
72 return true;
73 case kWasmFloatCid:
74 if (!value.IsDouble()) return false;
75 out->tag = wasmer_value_tag::WASM_F32;
76 out->value.F32 = Double::Cast(value).value();
77 return true;
78 case kWasmDoubleCid:
79 if (!value.IsDouble()) return false;
80 out->tag = wasmer_value_tag::WASM_F64;
81 out->value.F64 = Double::Cast(value).value();
82 return true;
83 default:
84 return false;
85 }
86}
87
88static Dart_Handle ToWasmValue(Dart_Handle value,
89 wasmer_value_tag type,
90 wasmer_value* out) {
91 switch (type) {
92 case wasmer_value_tag::WASM_I32: {
93 int64_t i64;
94 Dart_Handle result = Dart_IntegerToInt64(value, &i64);
95 out->I32 = i64;
96 if (out->I32 != i64) {
97 return Dart_NewApiError("Int doesn't fit into 32-bits");
98 }
99 return result;
100 }
101 case wasmer_value_tag::WASM_I64:
102 return Dart_IntegerToInt64(value, &out->I64);
103 case wasmer_value_tag::WASM_F32: {
104 double f64;
105 Dart_Handle result = Dart_DoubleValue(value, &f64);
106 out->F32 = f64;
107 return result;
108 }
109 case wasmer_value_tag::WASM_F64:
110 return Dart_DoubleValue(value, &out->F64);
111 default:
112 FATAL("Unknown WASM type");
113 return nullptr;
114 }
115}
116
117static bool ToWasmValueTag(classid_t type, wasmer_value_tag* out) {
118 switch (type) {
119 case kWasmInt32Cid:
120 *out = wasmer_value_tag::WASM_I32;
121 return true;
122 case kWasmInt64Cid:
123 *out = wasmer_value_tag::WASM_I64;
124 return true;
125 case kWasmFloatCid:
126 *out = wasmer_value_tag::WASM_F32;
127 return true;
128 case kWasmDoubleCid:
129 *out = wasmer_value_tag::WASM_F64;
130 return true;
131 default:
132 return false;
133 }
134}
135
136static ObjectPtr ToDartObject(wasmer_value_t ret) {
137 switch (ret.tag) {
138 case wasmer_value_tag::WASM_I32:
139 return Integer::New(ret.value.I32);
140 case wasmer_value_tag::WASM_I64:
141 return Integer::New(ret.value.I64);
142 case wasmer_value_tag::WASM_F32:
143 return Double::New(ret.value.F32);
144 case wasmer_value_tag::WASM_F64:
145 return Double::New(ret.value.F64);
146 default:
147 FATAL("Unknown WASM type");
148 return nullptr;
149 }
150}
151
152static Dart_Handle ToDartApiObject(wasmer_value value, wasmer_value_tag type) {
153 switch (type) {
154 case wasmer_value_tag::WASM_I32:
155 return Dart_NewInteger(value.I32);
156 case wasmer_value_tag::WASM_I64:
157 return Dart_NewInteger(value.I64);
158 case wasmer_value_tag::WASM_F32:
159 return Dart_NewDouble(value.F32);
160 case wasmer_value_tag::WASM_F64:
161 return Dart_NewDouble(value.F64);
162 default:
163 FATAL("Unknown WASM type");
164 return nullptr;
165 }
166}
167
168ExternalTypedDataPtr WasmMemoryToExternalTypedData(wasmer_memory_t* memory) {
169 uint8_t* data = wasmer_memory_data(memory);
170 uint32_t size = wasmer_memory_data_length(memory);
171 return ExternalTypedData::New(kExternalTypedDataUint8ArrayCid, data, size);
172}
173
174std::ostream& operator<<(std::ostream& o, const wasmer_byte_array& str) {
175 for (uint32_t i = 0; i < str.bytes_len; ++i) {
176 o << str.bytes[i];
177 }
178 return o;
179}
180
181std::ostream& operator<<(std::ostream& o, const wasmer_import_export_kind& io) {
182 switch (io) {
183 case wasmer_import_export_kind::WASM_FUNCTION:
184 return o << "WASM_FUNCTION";
185 case wasmer_import_export_kind::WASM_GLOBAL:
186 return o << "WASM_GLOBAL";
187 case wasmer_import_export_kind::WASM_MEMORY:
188 return o << "WASM_MEMORY";
189 case wasmer_import_export_kind::WASM_TABLE:
190 return o << "WASM_TABLE";
191 }
192}
193
194StringPtr DescribeModule(const wasmer_module_t* module) {
195 std::stringstream desc;
196
197 desc << "Imports:\n";
198 wasmer_import_descriptors_t* imports;
199 wasmer_import_descriptors(module, &imports);
200 unsigned num_imports = wasmer_import_descriptors_len(imports);
201 for (unsigned i = 0; i < num_imports; ++i) {
202 wasmer_import_descriptor_t* imp = wasmer_import_descriptors_get(imports, i);
203 desc << "\t" << wasmer_import_descriptor_module_name(imp);
204 desc << "\t" << wasmer_import_descriptor_name(imp);
205 desc << "\t" << wasmer_import_descriptor_kind(imp);
206 desc << "\n";
207 }
208 wasmer_import_descriptors_destroy(imports);
209
210 desc << "\nExports:\n";
211 wasmer_export_descriptors_t* exports;
212 wasmer_export_descriptors(module, &exports);
213 unsigned num_exports = wasmer_export_descriptors_len(exports);
214 for (unsigned i = 0; i < num_exports; ++i) {
215 wasmer_export_descriptor_t* exp = wasmer_export_descriptors_get(exports, i);
216 desc << "\t" << wasmer_export_descriptor_name(exp);
217 desc << "\t" << wasmer_export_descriptor_kind(exp);
218 desc << "\n";
219 }
220 wasmer_export_descriptors_destroy(exports);
221
222 return String::New(desc.str().c_str());
223}
224
225class WasmImports;
226
227struct WasmFunctionImport {
228 WasmImports* imports;
229 std::unique_ptr<wasmer_value_tag[]> args;
230 intptr_t num_args;
231 wasmer_value_tag ret;
232 intptr_t num_rets;
233 int64_t fn_id;
234 wasmer_import_func_t* wasm_fn;
235 wasmer_trampoline_buffer_t* buffer;
236 WasmFunctionImport(WasmImports* imports_,
237 std::unique_ptr<wasmer_value_tag[]> args_,
238 intptr_t num_args_,
239 wasmer_value_tag ret_,
240 intptr_t num_rets_,
241 int64_t fn_id_)
242 : imports(imports_),
243 args(std::move(args_)),
244 num_args(num_args_),
245 ret(ret_),
246 num_rets(num_rets_),
247 fn_id(fn_id_),
248 wasm_fn(nullptr),
249 buffer(nullptr) {}
250 ~WasmFunctionImport() {
251 wasmer_trampoline_buffer_destroy(buffer);
252 wasmer_import_func_destroy(wasm_fn);
253 }
254};
255
256extern "C" {
257int64_t Trampoline(void* context, int64_t* args);
258}
259
260class WasmImports {
261 public:
262 WasmImports() {}
263
264 ~WasmImports() {
265 for (wasmer_global_t* global : _globals) {
266 wasmer_global_destroy(global);
267 }
268 for (WasmFunctionImport* fn_imp : _functions) {
269 delete fn_imp;
270 }
271 for (const char* name : _import_names) {
272 delete[] name;
273 }
274 }
275
276 void SetHandle(FinalizablePersistentHandle* handle) { _handle = handle; }
277 size_t NumImports() const { return _imports.length(); }
278 wasmer_import_t* RawImports() { return _imports.data(); }
279
280 void AddMemory(std::unique_ptr<char[]> module_name,
281 std::unique_ptr<char[]> name,
282 wasmer_memory_t* memory) {
283 AddImport(std::move(module_name), std::move(name),
284 wasmer_import_export_kind::WASM_MEMORY)
285 ->memory = memory;
286 }
287
288 void AddGlobal(std::unique_ptr<char[]> module_name,
289 std::unique_ptr<char[]> name,
290 wasmer_value_t value,
291 bool mutable_) {
292 wasmer_global_t* global = wasmer_global_new(value, mutable_);
293 _globals.Add(global);
294 AddImport(std::move(module_name), std::move(name),
295 wasmer_import_export_kind::WASM_GLOBAL)
296 ->global = global;
297 }
298
299 void AddFunction(std::unique_ptr<char[]> module_name,
300 std::unique_ptr<char[]> name,
301 int64_t fn_id,
302 std::unique_ptr<wasmer_value_tag[]> args,
303 intptr_t num_args,
304 wasmer_value_tag ret,
305 intptr_t num_rets) {
306 // Trampoline args include the context pointer.
307 const intptr_t num_trampoline_args = num_args + 1;
308
309 WasmFunctionImport* fn_imp = new WasmFunctionImport(
310 this, std::move(args), num_args, ret, num_rets, fn_id);
311 _functions.Add(fn_imp);
312
313 wasmer_trampoline_buffer_builder_t* builder =
314 wasmer_trampoline_buffer_builder_new();
315 uintptr_t trampoline_id =
316 wasmer_trampoline_buffer_builder_add_callinfo_trampoline(
317 builder,
318 reinterpret_cast<wasmer_trampoline_callable_t*>(Trampoline),
319 reinterpret_cast<void*>(fn_imp), num_trampoline_args);
320 fn_imp->buffer = wasmer_trampoline_buffer_builder_build(builder);
321
322 const wasmer_trampoline_callable_t* trampoline =
323 wasmer_trampoline_buffer_get_trampoline(fn_imp->buffer, trampoline_id);
324 fn_imp->wasm_fn = wasmer_import_func_new(
325 reinterpret_cast<void (*)(void*)>(
326 const_cast<wasmer_trampoline_callable_t*>(trampoline)),
327 fn_imp->args.get(), num_args, &ret, num_rets);
328
329 AddImport(std::move(module_name), std::move(name),
330 wasmer_import_export_kind::WASM_FUNCTION)
331 ->func = fn_imp->wasm_fn;
332 }
333
334 int64_t CallImportedFunction(WasmFunctionImport* fn_imp, int64_t* raw_args) {
335 wasmer_value* wasm_args = reinterpret_cast<wasmer_value*>(raw_args);
336 Dart_Handle inst = Dart_HandleFromWeakPersistent(_handle->apiHandle());
337 Dart_Handle dart_args[2] = {
338 inst,
339 Dart_NewInteger(fn_imp->fn_id),
340 };
341 Dart_Handle closure = Dart_Invoke(Dart_InstanceGetType(inst),
342 Dart_NewStringFromCString("getFunction"),
343 ARRAY_SIZE(dart_args), dart_args);
344 if (Dart_IsError(closure)) {
345 Dart_ThrowException(closure);
346 UNREACHABLE();
347 }
348 Dart_Handle result;
349 {
350 auto args =
351 std::unique_ptr<Dart_Handle[]>(new Dart_Handle[fn_imp->num_args]);
352 for (intptr_t i = 0; i < fn_imp->num_args; ++i) {
353 args[i] = ToDartApiObject(wasm_args[i], fn_imp->args[i]);
354 }
355 result = Dart_InvokeClosure(closure, fn_imp->num_args, args.get());
356 }
357 if (Dart_IsError(result)) {
358 Dart_ThrowException(result);
359 UNREACHABLE();
360 }
361 if (fn_imp->num_rets == 0) {
362 // Wasmer ignores the result of this function if it expects no results,
363 // so skip the converters below (we get errors if we run them).
364 return 0;
365 }
366 wasmer_value wasm_result;
367 result = ToWasmValue(result, fn_imp->ret, &wasm_result);
368 if (Dart_IsError(result)) {
369 Dart_ThrowException(result);
370 UNREACHABLE();
371 }
372 return wasm_result.I64;
373 }
374
375 private:
376 FinalizablePersistentHandle* _handle;
377 MallocGrowableArray<const char*> _import_names;
378 MallocGrowableArray<wasmer_global_t*> _globals;
379 MallocGrowableArray<WasmFunctionImport*> _functions;
380 MallocGrowableArray<wasmer_import_t> _imports;
381
382 wasmer_import_export_value* AddImport(std::unique_ptr<char[]> module_name,
383 std::unique_ptr<char[]> name,
384 wasmer_import_export_kind tag) {
385 wasmer_import_t import;
386 import.module_name.bytes =
387 reinterpret_cast<const uint8_t*>(module_name.get());
388 import.module_name.bytes_len = (uint32_t)strlen(module_name.get());
389 import.import_name.bytes = reinterpret_cast<const uint8_t*>(name.get());
390 import.import_name.bytes_len = (uint32_t)strlen(name.get());
391 import.tag = tag;
392 _import_names.Add(module_name.release());
393 _import_names.Add(name.release());
394 _imports.Add(import);
395 return &_imports.Last().value;
396 }
397
398 DISALLOW_COPY_AND_ASSIGN(WasmImports);
399};
400
401extern "C" {
402int64_t Trampoline(void* context, int64_t* args) {
403 WasmFunctionImport* fn_imp = reinterpret_cast<WasmFunctionImport*>(context);
404 // Skip the first arg (it's another context pointer).
405 return fn_imp->imports->CallImportedFunction(fn_imp, args + 1);
406}
407}
408
409class WasmFunction {
410 public:
411 WasmFunction(MallocGrowableArray<classid_t> args,
412 classid_t ret,
413 const wasmer_export_func_t* fn)
414 : _args(std::move(args)), _ret(ret), _fn(fn) {}
415 bool IsVoid() const { return _ret == kWasmVoidCid; }
416 const MallocGrowableArray<classid_t>& args() const { return _args; }
417
418 bool SignatureMatches(const MallocGrowableArray<classid_t>& dart_args,
419 classid_t dart_ret) {
420 if (dart_args.length() != _args.length()) {
421 return false;
422 }
423 for (intptr_t i = 0; i < dart_args.length(); ++i) {
424 if (dart_args[i] != _args[i]) {
425 return false;
426 }
427 }
428 return dart_ret == _ret;
429 }
430
431 wasmer_result_t Call(const wasmer_value_t* params, wasmer_value_t* result) {
432 return wasmer_export_func_call(_fn, params, _args.length(), result,
433 IsVoid() ? 0 : 1);
434 }
435
436 void Print(std::ostream& o, const char* name) const {
437 PrintDartType(o, _ret);
438 o << ' ' << name << '(';
439 for (intptr_t i = 0; i < _args.length(); ++i) {
440 if (i > 0) o << ", ";
441 PrintDartType(o, _args[i]);
442 }
443 o << ')';
444 }
445
446 private:
447 MallocGrowableArray<classid_t> _args;
448 const classid_t _ret;
449 const wasmer_export_func_t* _fn;
450
451 static void PrintDartType(std::ostream& o, classid_t type) {
452 switch (type) {
453 case kWasmInt32Cid:
454 o << "i32";
455 break;
456 case kWasmInt64Cid:
457 o << "i64";
458 break;
459 case kWasmFloatCid:
460 o << "f32";
461 break;
462 case kWasmDoubleCid:
463 o << "f64";
464 break;
465 case kWasmVoidCid:
466 o << "void";
467 break;
468 }
469 }
470};
471
472class WasmInstance {
473 public:
474 WasmInstance() : _instance(nullptr), _exports(nullptr), _memory(nullptr) {}
475
476 bool Instantiate(wasmer_module_t* module, WasmImports* imports) {
477 ASSERT(_instance == nullptr);
478
479 // Instantiate module.
480 if (wasmer_module_instantiate(module, &_instance, imports->RawImports(),
481 imports->NumImports()) !=
482 wasmer_result_t::WASMER_OK) {
483 return false;
484 }
485
486 // Load all exports.
487 wasmer_instance_exports(_instance, &_exports);
488 intptr_t num_exports = wasmer_exports_len(_exports);
489 for (intptr_t i = 0; i < num_exports; ++i) {
490 wasmer_export_t* exp = wasmer_exports_get(_exports, i);
491 wasmer_import_export_kind kind = wasmer_export_kind(exp);
492 if (kind == wasmer_import_export_kind::WASM_FUNCTION) {
493 if (!AddFunction(exp)) {
494 return false;
495 }
496 } else if (kind == wasmer_import_export_kind::WASM_MEMORY) {
497 ASSERT(_memory == nullptr);
498 if (wasmer_export_to_memory(exp, &_memory) !=
499 wasmer_result_t::WASMER_OK) {
500 return false;
501 }
502 }
503 }
504
505 return true;
506 }
507
508 ~WasmInstance() {
509 auto it = _functions.GetIterator();
510 for (auto* kv = it.Next(); kv; kv = it.Next()) {
511 delete[] kv->key;
512 delete kv->value;
513 }
514 if (_exports != nullptr) {
515 wasmer_exports_destroy(_exports);
516 }
517 if (_instance != nullptr) {
518 wasmer_instance_destroy(_instance);
519 }
520 }
521
522 WasmFunction* GetFunction(const char* name,
523 const MallocGrowableArray<classid_t>& dart_args,
524 classid_t dart_ret,
525 String* error) {
526 WasmFunction* fn = _functions.LookupValue(name);
527 if (fn == nullptr) {
528 *error = String::NewFormatted(
529 "Couldn't find a function called %s in the WASM module's exports",
530 name);
531 return nullptr;
532 }
533 if (!fn->SignatureMatches(dart_args, dart_ret)) {
534 std::stringstream sig;
535 fn->Print(sig, name);
536 *error = String::NewFormatted("Function signature doesn't match: %s",
537 sig.str().c_str());
538 return nullptr;
539 }
540 return fn;
541 }
542
543 void PrintFunctions(std::ostream& o) const {
544 o << '{' << std::endl;
545 auto it = _functions.GetIterator();
546 for (auto* kv = it.Next(); kv; kv = it.Next()) {
547 kv->value->Print(o, kv->key);
548 o << std::endl;
549 }
550 o << '}' << std::endl;
551 }
552
553 wasmer_memory_t* memory() { return _memory; }
554
555 private:
556 wasmer_instance_t* _instance;
557 wasmer_exports_t* _exports;
558 MallocDirectChainedHashMap<CStringKeyValueTrait<WasmFunction*>> _functions;
559 wasmer_memory_t* _memory;
560
561 static classid_t ToDartType(wasmer_value_tag wasm_type) {
562 switch (wasm_type) {
563 case wasmer_value_tag::WASM_I32:
564 return kWasmInt32Cid;
565 case wasmer_value_tag::WASM_I64:
566 return kWasmInt64Cid;
567 case wasmer_value_tag::WASM_F32:
568 return kWasmFloatCid;
569 case wasmer_value_tag::WASM_F64:
570 return kWasmDoubleCid;
571 }
572 FATAL("Unknown WASM type");
573 return 0;
574 }
575
576 bool AddFunction(wasmer_export_t* exp) {
577 const wasmer_export_func_t* fn = wasmer_export_to_func(exp);
578
579 uint32_t num_rets;
580 if (wasmer_export_func_returns_arity(fn, &num_rets) !=
581 wasmer_result_t::WASMER_OK) {
582 return false;
583 }
584 ASSERT(num_rets <= 1);
585 wasmer_value_tag wasm_ret;
586 if (wasmer_export_func_returns(fn, &wasm_ret, num_rets) !=
587 wasmer_result_t::WASMER_OK) {
588 return false;
589 }
590 classid_t ret = num_rets == 0 ? kWasmVoidCid : ToDartType(wasm_ret);
591
592 uint32_t num_args;
593 if (wasmer_export_func_params_arity(fn, &num_args) !=
594 wasmer_result_t::WASMER_OK) {
595 return false;
596 }
597 auto wasm_args =
598 std::unique_ptr<wasmer_value_tag[]>(new wasmer_value_tag[num_args]);
599 if (wasmer_export_func_params(fn, wasm_args.get(), num_args) !=
600 wasmer_result_t::WASMER_OK) {
601 return false;
602 }
603 MallocGrowableArray<classid_t> args;
604 for (intptr_t i = 0; i < num_args; ++i) {
605 args.Add(ToDartType(wasm_args[i]));
606 }
607
608 wasmer_byte_array name_bytes = wasmer_export_name(exp);
609 char* name = new char[name_bytes.bytes_len + 1];
610 for (size_t i = 0; i < name_bytes.bytes_len; ++i) {
611 name[i] = name_bytes.bytes[i];
612 }
613 name[name_bytes.bytes_len] = '\0';
614
615 _functions.Insert({name, new WasmFunction(std::move(args), ret, fn)});
616 return true;
617 }
618
619 DISALLOW_COPY_AND_ASSIGN(WasmInstance);
620};
621
622DEFINE_NATIVE_ENTRY(Wasm_initModule, 0, 2) {
623 GET_NON_NULL_NATIVE_ARGUMENT(Instance, mod_wrap, arguments->NativeArgAt(0));
624 GET_NON_NULL_NATIVE_ARGUMENT(TypedDataBase, data, arguments->NativeArgAt(1));
625
626 ASSERT(mod_wrap.NumNativeFields() == 1);
627
628 std::unique_ptr<uint8_t[]> data_copy;
629 intptr_t len;
630 {
631 NoSafepointScope scope(thread);
632 len = data.LengthInBytes();
633 data_copy = std::unique_ptr<uint8_t[]>(new uint8_t[len]);
634 // The memory does not overlap.
635 memcpy(data_copy.get(), data.DataAddr(0), len); // NOLINT
636 }
637
638 wasmer_module_t* module;
639 wasmer_result_t result;
640 {
641 TransitionVMToNative transition(thread);
642 result = wasmer_compile(&module, data_copy.get(), len);
643 }
644 if (result != wasmer_result_t::WASMER_OK) {
645 data_copy.reset();
646 ThrowWasmerError();
647 UNREACHABLE();
648 }
649
650 mod_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(module));
651 FinalizablePersistentHandle::New(thread->isolate(), mod_wrap, module,
652 FinalizeWasmModule, len);
653
654 return Object::null();
655}
656
657DEFINE_NATIVE_ENTRY(Wasm_describeModule, 0, 1) {
658 GET_NON_NULL_NATIVE_ARGUMENT(Instance, mod_wrap, arguments->NativeArgAt(0));
659
660 ASSERT(mod_wrap.NumNativeFields() == 1);
661
662 wasmer_module_t* module =
663 reinterpret_cast<wasmer_module_t*>(mod_wrap.GetNativeField(0));
664
665 return DescribeModule(module);
666}
667
668DEFINE_NATIVE_ENTRY(Wasm_initImports, 0, 1) {
669 GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0));
670
671 ASSERT(imp_wrap.NumNativeFields() == 1);
672
673 WasmImports* imports = new WasmImports();
674
675 imp_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(imports));
676 imports->SetHandle(FinalizablePersistentHandle::New(
677 thread->isolate(), imp_wrap, imports, Finalize<WasmImports>,
678 sizeof(WasmImports)));
679
680 return Object::null();
681}
682
683DEFINE_NATIVE_ENTRY(Wasm_addMemoryImport, 0, 4) {
684 GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0));
685 GET_NON_NULL_NATIVE_ARGUMENT(String, module_name, arguments->NativeArgAt(1));
686 GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(2));
687 GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(3));
688
689 ASSERT(imp_wrap.NumNativeFields() == 1);
690 ASSERT(mem_wrap.NumNativeFields() == 1);
691
692 WasmImports* imports =
693 reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0));
694 wasmer_memory_t* memory =
695 reinterpret_cast<wasmer_memory_t*>(mem_wrap.GetNativeField(0));
696
697 imports->AddMemory(ToUTF8(module_name), ToUTF8(name), memory);
698
699 return Object::null();
700}
701
702DEFINE_NATIVE_ENTRY(Wasm_addGlobalImport, 0, 6) {
703 GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0));
704 GET_NON_NULL_NATIVE_ARGUMENT(String, module_name, arguments->NativeArgAt(1));
705 GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(2));
706 GET_NON_NULL_NATIVE_ARGUMENT(Number, value, arguments->NativeArgAt(3));
707 GET_NON_NULL_NATIVE_ARGUMENT(Type, type, arguments->NativeArgAt(4));
708 GET_NON_NULL_NATIVE_ARGUMENT(Bool, mutable_, arguments->NativeArgAt(5));
709
710 ASSERT(imp_wrap.NumNativeFields() == 1);
711
712 WasmImports* imports =
713 reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0));
714 wasmer_value_t wasm_value;
715 if (!ToWasmValue(value, type.type_class_id(), &wasm_value)) {
716 Exceptions::ThrowArgumentError(String::Handle(
717 zone, String::NewFormatted(
718 "Can't convert dart value to WASM global variable")));
719 UNREACHABLE();
720 }
721
722 imports->AddGlobal(ToUTF8(module_name), ToUTF8(name), wasm_value,
723 mutable_.value());
724
725 return Object::null();
726}
727
728DEFINE_NATIVE_ENTRY(Wasm_addFunctionImport, 0, 5) {
729 GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0));
730 GET_NON_NULL_NATIVE_ARGUMENT(String, module_name, arguments->NativeArgAt(1));
731 GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(2));
732 GET_NON_NULL_NATIVE_ARGUMENT(Integer, fn_id, arguments->NativeArgAt(3));
733 GET_NON_NULL_NATIVE_ARGUMENT(Type, fn_type, arguments->NativeArgAt(4));
734
735 ASSERT(imp_wrap.NumNativeFields() == 1);
736
737 Function& sig = Function::Handle(zone, fn_type.signature());
738
739 classid_t ret = AbstractType::Handle(zone, sig.result_type()).type_class_id();
740 intptr_t num_rets = ret == kWasmVoidCid ? 0 : 1;
741 wasmer_value_tag wasm_ret = wasmer_value_tag::WASM_I64;
742 if (num_rets != 0) {
743 if (!ToWasmValueTag(ret, &wasm_ret)) {
744 Exceptions::ThrowArgumentError(String::Handle(
745 zone, String::NewFormatted("Return type is not a valid WASM type")));
746 UNREACHABLE();
747 }
748 }
749
750 Array& args = Array::Handle(zone, sig.parameter_types());
751 AbstractType& arg_type = AbstractType::Handle(zone);
752 intptr_t first_arg_index = sig.NumImplicitParameters();
753 intptr_t num_args = args.Length() - first_arg_index;
754 auto wasm_args =
755 std::unique_ptr<wasmer_value_tag[]>(new wasmer_value_tag[num_args]);
756 for (intptr_t i = 0; i < num_args; ++i) {
757 arg_type ^= args.At(i + first_arg_index);
758 classid_t dart_arg = arg_type.type_class_id();
759 if (!ToWasmValueTag(dart_arg, &wasm_args[i])) {
760 wasm_args.reset();
761 Exceptions::ThrowArgumentError(String::Handle(
762 zone, String::NewFormatted(
763 "Type of arg %" Pd " is not a valid WASM type", i)));
764 UNREACHABLE();
765 }
766 }
767
768 WasmImports* imports =
769 reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0));
770 imports->AddFunction(ToUTF8(module_name), ToUTF8(name), fn_id.AsInt64Value(),
771 std::move(wasm_args), num_args, wasm_ret, num_rets);
772
773 return Object::null();
774}
775
776DEFINE_NATIVE_ENTRY(Wasm_initMemory, 0, 3) {
777 GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0));
778 GET_NON_NULL_NATIVE_ARGUMENT(Integer, init, arguments->NativeArgAt(1));
779 GET_NATIVE_ARGUMENT(Integer, max, arguments->NativeArgAt(2));
780
781 ASSERT(mem_wrap.NumNativeFields() == 1);
782 const int64_t init_size = init.AsInt64Value();
783
784 wasmer_memory_t* memory;
785 wasmer_limits_t descriptor;
786 descriptor.min = init_size;
787 if (max.IsNull()) {
788 descriptor.max.has_some = false;
789 } else {
790 descriptor.max.has_some = true;
791 descriptor.max.some = max.AsInt64Value();
792 }
793 if (wasmer_memory_new(&memory, descriptor) != wasmer_result_t::WASMER_OK) {
794 ThrowWasmerError();
795 UNREACHABLE();
796 }
797 mem_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(memory));
798 FinalizablePersistentHandle::New(thread->isolate(), mem_wrap, memory,
799 FinalizeWasmMemory, init_size);
800 return WasmMemoryToExternalTypedData(memory);
801}
802
803DEFINE_NATIVE_ENTRY(Wasm_growMemory, 0, 2) {
804 GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0));
805 GET_NON_NULL_NATIVE_ARGUMENT(Integer, delta, arguments->NativeArgAt(1));
806
807 ASSERT(mem_wrap.NumNativeFields() == 1);
808
809 wasmer_memory_t* memory =
810 reinterpret_cast<wasmer_memory_t*>(mem_wrap.GetNativeField(0));
811 if (wasmer_memory_grow(memory, delta.AsInt64Value()) !=
812 wasmer_result_t::WASMER_OK) {
813 ThrowWasmerError();
814 UNREACHABLE();
815 }
816 return WasmMemoryToExternalTypedData(memory);
817}
818
819DEFINE_NATIVE_ENTRY(Wasm_initInstance, 0, 3) {
820 GET_NON_NULL_NATIVE_ARGUMENT(Instance, inst_wrap, arguments->NativeArgAt(0));
821 GET_NON_NULL_NATIVE_ARGUMENT(Instance, mod_wrap, arguments->NativeArgAt(1));
822 GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(2));
823
824 ASSERT(inst_wrap.NumNativeFields() == 1);
825 ASSERT(mod_wrap.NumNativeFields() == 1);
826 ASSERT(imp_wrap.NumNativeFields() == 1);
827
828 wasmer_module_t* module =
829 reinterpret_cast<wasmer_module_t*>(mod_wrap.GetNativeField(0));
830 WasmImports* imports =
831 reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0));
832
833 WasmInstance* inst = nullptr;
834 {
835 TransitionVMToNative transition(thread);
836 inst = new WasmInstance();
837 if (!inst->Instantiate(module, imports)) {
838 delete inst;
839 inst = nullptr;
840 }
841 }
842 if (inst == nullptr) {
843 ThrowWasmerError();
844 UNREACHABLE();
845 }
846
847 inst_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(inst));
848 FinalizablePersistentHandle::New(thread->isolate(), inst_wrap, inst,
849 Finalize<WasmInstance>,
850 sizeof(WasmInstance));
851
852 return Object::null();
853}
854
855DEFINE_NATIVE_ENTRY(Wasm_initMemoryFromInstance, 0, 2) {
856 GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0));
857 GET_NON_NULL_NATIVE_ARGUMENT(Instance, inst_wrap, arguments->NativeArgAt(1));
858
859 ASSERT(mem_wrap.NumNativeFields() == 1);
860 ASSERT(inst_wrap.NumNativeFields() == 1);
861
862 WasmInstance* inst =
863 reinterpret_cast<WasmInstance*>(inst_wrap.GetNativeField(0));
864
865 wasmer_memory_t* memory = inst->memory();
866
867 mem_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(memory));
868 FinalizablePersistentHandle::New(thread->isolate(), mem_wrap, memory,
869 FinalizeWasmMemory,
870 wasmer_memory_length(memory));
871 return WasmMemoryToExternalTypedData(memory);
872}
873
874DEFINE_NATIVE_ENTRY(Wasm_getMemoryPages, 0, 1) {
875 GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0));
876
877 ASSERT(mem_wrap.NumNativeFields() == 1);
878
879 wasmer_memory_t* memory =
880 reinterpret_cast<wasmer_memory_t*>(mem_wrap.GetNativeField(0));
881
882 return Integer::New(wasmer_memory_length(memory));
883}
884
885DEFINE_NATIVE_ENTRY(Wasm_initFunction, 0, 4) {
886 GET_NON_NULL_NATIVE_ARGUMENT(Instance, fn_wrap, arguments->NativeArgAt(0));
887 GET_NON_NULL_NATIVE_ARGUMENT(Instance, inst_wrap, arguments->NativeArgAt(1));
888 GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(2));
889 GET_NON_NULL_NATIVE_ARGUMENT(Type, fn_type, arguments->NativeArgAt(3));
890
891 ASSERT(fn_wrap.NumNativeFields() == 1);
892 ASSERT(inst_wrap.NumNativeFields() == 1);
893
894 WasmInstance* inst =
895 reinterpret_cast<WasmInstance*>(inst_wrap.GetNativeField(0));
896
897 WasmFunction* fn;
898 String& error = String::Handle(zone);
899
900 {
901 Function& sig = Function::Handle(zone, fn_type.signature());
902 Array& args = Array::Handle(zone, sig.parameter_types());
903 AbstractType& arg_type = AbstractType::Handle(zone);
904 MallocGrowableArray<classid_t> dart_args;
905 for (intptr_t i = sig.NumImplicitParameters(); i < args.Length(); ++i) {
906 arg_type ^= args.At(i);
907 dart_args.Add(arg_type.type_class_id());
908 }
909 classid_t dart_ret =
910 AbstractType::Handle(zone, sig.result_type()).type_class_id();
911
912 std::unique_ptr<char[]> name_raw = ToUTF8(name);
913 fn = inst->GetFunction(name_raw.get(), dart_args, dart_ret, &error);
914 }
915
916 if (fn == nullptr) {
917 Exceptions::ThrowArgumentError(error);
918 UNREACHABLE();
919 }
920
921 fn_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(fn));
922 // Don't need a finalizer because WasmFunctions are owned their WasmInstance.
923
924 return Object::null();
925}
926
927DEFINE_NATIVE_ENTRY(Wasm_callFunction, 0, 2) {
928 GET_NON_NULL_NATIVE_ARGUMENT(Instance, fn_wrap, arguments->NativeArgAt(0));
929 GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(1));
930
931 ASSERT(fn_wrap.NumNativeFields() == 1);
932 WasmFunction* fn = reinterpret_cast<WasmFunction*>(fn_wrap.GetNativeField(0));
933
934 if (args.Length() != fn->args().length()) {
935 Exceptions::ThrowArgumentError(String::Handle(
936 zone, String::NewFormatted("Wrong number of args. Expected %" Pu
937 " but found %" Pd ".",
938 fn->args().length(), args.Length())));
939 UNREACHABLE();
940 }
941 auto params = std::unique_ptr<wasmer_value_t[]>(
942 new wasmer_value_t[fn->args().length()]);
943 Number& arg_num = Number::Handle(zone);
944 for (intptr_t i = 0; i < args.Length(); ++i) {
945 arg_num ^= args.At(i);
946 if (!ToWasmValue(arg_num, fn->args()[i], &params[i])) {
947 params.reset();
948 Exceptions::ThrowArgumentError(String::Handle(
949 zone, String::NewFormatted("Arg %" Pd " is the wrong type.", i)));
950 UNREACHABLE();
951 }
952 }
953
954 wasmer_value_t ret;
955 wasmer_result_t result;
956 {
957 TransitionVMToNative transition(thread);
958 result = fn->Call(params.get(), &ret);
959 }
960 if (result != wasmer_result_t::WASMER_OK) {
961 params.reset();
962 ThrowWasmerError();
963 UNREACHABLE();
964 }
965 return fn->IsVoid() ? Object::null() : ToDartObject(ret);
966}
967
968} // namespace dart
969
970#else // DART_ENABLE_WASM
971
972#include "vm/bootstrap_natives.h"
973#include "vm/dart_entry.h"
974#include "vm/exceptions.h"
975
976namespace dart {
977
978DEFINE_NATIVE_ENTRY(Wasm_initModule, 0, 2) {
979 Exceptions::ThrowUnsupportedError("WASM is disabled");
980 return nullptr;
981}
982
983DEFINE_NATIVE_ENTRY(Wasm_describeModule, 0, 1) {
984 Exceptions::ThrowUnsupportedError("WASM is disabled");
985 return nullptr;
986}
987
988DEFINE_NATIVE_ENTRY(Wasm_initImports, 0, 1) {
989 Exceptions::ThrowUnsupportedError("WASM is disabled");
990 return nullptr;
991}
992
993DEFINE_NATIVE_ENTRY(Wasm_addMemoryImport, 0, 4) {
994 Exceptions::ThrowUnsupportedError("WASM is disabled");
995 return nullptr;
996}
997
998DEFINE_NATIVE_ENTRY(Wasm_addGlobalImport, 0, 6) {
999 Exceptions::ThrowUnsupportedError("WASM is disabled");
1000 return nullptr;
1001}
1002
1003DEFINE_NATIVE_ENTRY(Wasm_addFunctionImport, 0, 5) {
1004 Exceptions::ThrowUnsupportedError("WASM is disabled");
1005 return nullptr;
1006}
1007
1008DEFINE_NATIVE_ENTRY(Wasm_initMemory, 0, 3) {
1009 Exceptions::ThrowUnsupportedError("WASM is disabled");
1010 return nullptr;
1011}
1012
1013DEFINE_NATIVE_ENTRY(Wasm_growMemory, 0, 3) {
1014 Exceptions::ThrowUnsupportedError("WASM is disabled");
1015 return nullptr;
1016}
1017
1018DEFINE_NATIVE_ENTRY(Wasm_initInstance, 0, 3) {
1019 Exceptions::ThrowUnsupportedError("WASM is disabled");
1020 return nullptr;
1021}
1022
1023DEFINE_NATIVE_ENTRY(Wasm_initMemoryFromInstance, 0, 2) {
1024 Exceptions::ThrowUnsupportedError("WASM is disabled");
1025 return nullptr;
1026}
1027
1028DEFINE_NATIVE_ENTRY(Wasm_getMemoryPages, 0, 1) {
1029 Exceptions::ThrowUnsupportedError("WASM is disabled");
1030 return nullptr;
1031}
1032
1033DEFINE_NATIVE_ENTRY(Wasm_initFunction, 0, 4) {
1034 Exceptions::ThrowUnsupportedError("WASM is disabled");
1035 return nullptr;
1036}
1037
1038DEFINE_NATIVE_ENTRY(Wasm_callFunction, 0, 2) {
1039 Exceptions::ThrowUnsupportedError("WASM is disabled");
1040 return nullptr;
1041}
1042
1043} // namespace dart
1044
1045#endif // DART_ENABLE_WASM
1046