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 | |
17 | namespace dart { |
18 | |
19 | static 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 | |
32 | template <typename T> |
33 | static void Finalize(void* isolate_callback_data, |
34 | Dart_WeakPersistentHandle handle, |
35 | void* peer) { |
36 | delete reinterpret_cast<T*>(peer); |
37 | } |
38 | |
39 | static 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 | |
45 | static 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 | |
51 | static 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 | |
59 | static 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 | |
88 | static 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 | |
117 | static 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 | |
136 | static 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 | |
152 | static 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 | |
168 | ExternalTypedDataPtr 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 | |
174 | std::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 | |
181 | std::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 | |
194 | StringPtr 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 | |
225 | class WasmImports; |
226 | |
227 | struct 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 | |
256 | extern "C" { |
257 | int64_t Trampoline(void* context, int64_t* args); |
258 | } |
259 | |
260 | class 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 | |
401 | extern "C" { |
402 | int64_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 | |
409 | class 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 | |
472 | class 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 | |
622 | DEFINE_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 | |
657 | DEFINE_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 | |
668 | DEFINE_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 | |
683 | DEFINE_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 | |
702 | DEFINE_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 | |
728 | DEFINE_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 | |
776 | DEFINE_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 | |
803 | DEFINE_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 | |
819 | DEFINE_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 | |
855 | DEFINE_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 | |
874 | DEFINE_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 | |
885 | DEFINE_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 | |
927 | DEFINE_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], ¶ms[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 | |
976 | namespace dart { |
977 | |
978 | DEFINE_NATIVE_ENTRY(Wasm_initModule, 0, 2) { |
979 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
980 | return nullptr; |
981 | } |
982 | |
983 | DEFINE_NATIVE_ENTRY(Wasm_describeModule, 0, 1) { |
984 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
985 | return nullptr; |
986 | } |
987 | |
988 | DEFINE_NATIVE_ENTRY(Wasm_initImports, 0, 1) { |
989 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
990 | return nullptr; |
991 | } |
992 | |
993 | DEFINE_NATIVE_ENTRY(Wasm_addMemoryImport, 0, 4) { |
994 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
995 | return nullptr; |
996 | } |
997 | |
998 | DEFINE_NATIVE_ENTRY(Wasm_addGlobalImport, 0, 6) { |
999 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
1000 | return nullptr; |
1001 | } |
1002 | |
1003 | DEFINE_NATIVE_ENTRY(Wasm_addFunctionImport, 0, 5) { |
1004 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
1005 | return nullptr; |
1006 | } |
1007 | |
1008 | DEFINE_NATIVE_ENTRY(Wasm_initMemory, 0, 3) { |
1009 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
1010 | return nullptr; |
1011 | } |
1012 | |
1013 | DEFINE_NATIVE_ENTRY(Wasm_growMemory, 0, 3) { |
1014 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
1015 | return nullptr; |
1016 | } |
1017 | |
1018 | DEFINE_NATIVE_ENTRY(Wasm_initInstance, 0, 3) { |
1019 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
1020 | return nullptr; |
1021 | } |
1022 | |
1023 | DEFINE_NATIVE_ENTRY(Wasm_initMemoryFromInstance, 0, 2) { |
1024 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
1025 | return nullptr; |
1026 | } |
1027 | |
1028 | DEFINE_NATIVE_ENTRY(Wasm_getMemoryPages, 0, 1) { |
1029 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
1030 | return nullptr; |
1031 | } |
1032 | |
1033 | DEFINE_NATIVE_ENTRY(Wasm_initFunction, 0, 4) { |
1034 | Exceptions::ThrowUnsupportedError("WASM is disabled" ); |
1035 | return nullptr; |
1036 | } |
1037 | |
1038 | DEFINE_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 | |