[wasm] Add support for pinvokes in user assemblies. (#14253)
[mono-project.git] / sdks / wasm / driver.c
blobec3bc3025027241f8fc88a3176aa681e5936f4ed
1 #include <emscripten.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdint.h>
6 #include <assert.h>
8 #include <mono/metadata/assembly.h>
9 #include <mono/metadata/tokentype.h>
10 #include <mono/utils/mono-logger.h>
11 #include <mono/utils/mono-dl-fallback.h>
12 #include <mono/jit/jit.h>
14 #ifdef GEN_PINVOKE
15 #include "pinvoke-table.h"
16 #else
17 #include "pinvoke-tables-default.h"
18 #endif
20 #ifdef CORE_BINDINGS
21 void core_initialize_internals ();
22 #endif
24 // Blazor specific custom routines - see dotnet_support.js for backing code
25 extern void* mono_wasm_invoke_js_marshalled (MonoString **exceptionMessage, void *asyncHandleLongPtr, MonoString *funcName, MonoString *argsJson);
26 extern void* mono_wasm_invoke_js_unmarshalled (MonoString **exceptionMessage, MonoString *funcName, void* arg0, void* arg1, void* arg2);
28 void mono_wasm_enable_debugging (void);
30 void mono_ee_interp_init (const char *opts);
31 void mono_marshal_ilgen_init (void);
32 void mono_method_builder_ilgen_init (void);
33 void mono_sgen_mono_ilgen_init (void);
34 void mono_icall_table_init (void);
35 void mono_aot_register_module (void **aot_info);
36 char *monoeg_g_getenv(const char *variable);
37 int monoeg_g_setenv(const char *variable, const char *value, int overwrite);
38 void mono_free (void*);
40 /* Not part of public headers */
41 #define MONO_ICALL_TABLE_CALLBACKS_VERSION 2
43 typedef struct {
44 int version;
45 void* (*lookup) (MonoMethod *method, char *classname, char *methodname, char *sigstart, int32_t *uses_handles);
46 const char* (*lookup_icall_symbol) (void* func);
47 } MonoIcallTableCallbacks;
49 void
50 mono_install_icall_table_callbacks (MonoIcallTableCallbacks *cb);
52 int mono_regression_test_step (int verbose_level, char *image, char *method_name);
53 void mono_trace_init (void);
55 static char*
56 m_strdup (const char *str)
58 if (!str)
59 return NULL;
61 int len = strlen (str) + 1;
62 char *res = malloc (len);
63 memcpy (res, str, len);
64 return res;
67 static MonoDomain *root_domain;
69 static MonoString*
70 mono_wasm_invoke_js (MonoString *str, int *is_exception)
72 if (str == NULL)
73 return NULL;
75 char *native_val = mono_string_to_utf8 (str);
76 mono_unichar2 *native_res = (mono_unichar2*)EM_ASM_INT ({
77 var str = UTF8ToString ($0);
78 try {
79 var res = eval (str);
80 if (res === null || res == undefined)
81 return 0;
82 res = res.toString ();
83 setValue ($1, 0, "i32");
84 } catch (e) {
85 res = e.toString ();
86 setValue ($1, 1, "i32");
87 if (res === null || res === undefined)
88 res = "unknown exception";
90 var buff = Module._malloc((res.length + 1) * 2);
91 stringToUTF16 (res, buff, (res.length + 1) * 2);
92 return buff;
93 }, (int)native_val, is_exception);
95 mono_free (native_val);
97 if (native_res == NULL)
98 return NULL;
100 MonoString *res = mono_string_from_utf16 (native_res);
101 free (native_res);
102 return res;
105 static void
106 wasm_logger (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data)
108 if (fatal) {
109 EM_ASM(
110 var err = new Error();
111 console.log ("Stacktrace: \n");
112 console.log (err.stack);
115 fprintf (stderr, "%s", message);
117 abort ();
118 } else {
119 fprintf (stdout, "%s\n", message);
123 #ifdef DRIVER_GEN
124 #include "driver-gen.c"
125 #endif
127 typedef struct WasmAssembly_ WasmAssembly;
129 struct WasmAssembly_ {
130 MonoBundledAssembly assembly;
131 WasmAssembly *next;
134 static WasmAssembly *assemblies;
135 static int assembly_count;
137 EMSCRIPTEN_KEEPALIVE void
138 mono_wasm_add_assembly (const char *name, const unsigned char *data, unsigned int size)
140 int len = strlen (name);
141 if (!strcasecmp (".pdb", &name [len - 4])) {
142 char *new_name = m_strdup (name);
143 //FIXME handle debugging assemblies with .exe extension
144 strcpy (&new_name [len - 3], "dll");
145 mono_register_symfile_for_assembly (new_name, data, size);
146 return;
148 WasmAssembly *entry = (WasmAssembly *)malloc(sizeof (MonoBundledAssembly));
149 entry->assembly.name = m_strdup (name);
150 entry->assembly.data = data;
151 entry->assembly.size = size;
152 entry->next = assemblies;
153 assemblies = entry;
154 ++assembly_count;
157 EMSCRIPTEN_KEEPALIVE void
158 mono_wasm_setenv (const char *name, const char *value)
160 monoeg_g_setenv (strdup (name), strdup (value), 1);
163 static void*
164 wasm_dl_load (const char *name, int flags, char **err, void *user_data)
166 for (int i = 0; i < sizeof (pinvoke_tables) / sizeof (void*); ++i) {
167 if (!strcmp (name, pinvoke_names [i]))
168 return pinvoke_tables [i];
170 return NULL;
173 static void*
174 wasm_dl_symbol (void *handle, const char *name, char **err, void *user_data)
176 PinvokeImport *table = (PinvokeImport*)handle;
177 for (int i = 0; table [i].name; ++i) {
178 if (!strcmp (table [i].name, name))
179 return table [i].func;
181 return NULL;
184 #if !defined(ENABLE_AOT) || defined(EE_MODE_LLVMONLY_INTERP)
185 #define NEED_INTERP 1
186 #ifndef LINK_ICALLS
187 // FIXME: llvm+interp mode needs this to call icalls
188 #define NEED_NORMAL_ICALL_TABLES 1
189 #endif
190 #endif
192 #ifdef LINK_ICALLS
194 #include "icall-table.h"
196 static int
197 compare_int (const void *k1, const void *k2)
199 return *(int*)k1 - *(int*)k2;
202 static void*
203 icall_table_lookup (MonoMethod *method, char *classname, char *methodname, char *sigstart, int32_t *uses_handles)
205 uint32_t token = mono_method_get_token (method);
206 assert (token);
207 assert ((token & MONO_TOKEN_METHOD_DEF) == MONO_TOKEN_METHOD_DEF);
208 uint32_t token_idx = token - MONO_TOKEN_METHOD_DEF;
210 int *indexes = NULL;
211 int indexes_size = 0;
212 uint8_t *handles = NULL;
213 void **funcs = NULL;
215 *uses_handles = 0;
217 const char *image_name = mono_image_get_name (mono_class_get_image (mono_method_get_class (method)));
219 #ifdef ICALL_TABLE_mscorlib
220 if (!strcmp (image_name, "mscorlib")) {
221 indexes = mscorlib_icall_indexes;
222 indexes_size = sizeof (mscorlib_icall_indexes) / 4;
223 handles = mscorlib_icall_handles;
224 funcs = mscorlib_icall_funcs;
225 assert (sizeof (mscorlib_icall_indexes [0]) == 4);
227 #ifdef ICALL_TABLE_System
228 if (!strcmp (image_name, "System")) {
229 indexes = System_icall_indexes;
230 indexes_size = sizeof (System_icall_indexes) / 4;
231 handles = System_icall_handles;
232 funcs = System_icall_funcs;
234 #endif
235 assert (indexes);
237 void *p = bsearch (&token_idx, indexes, indexes_size, 4, compare_int);
238 if (!p) {
239 return NULL;
240 printf ("wasm: Unable to lookup icall: %s\n", mono_method_get_name (method));
241 exit (1);
244 uint32_t idx = (int*)p - indexes;
245 *uses_handles = handles [idx];
247 //printf ("ICALL: %s %x %d %d\n", methodname, token, idx, (int)(funcs [idx]));
249 return funcs [idx];
250 #endif
253 static const char*
254 icall_table_lookup_symbol (void *func)
256 assert (0);
257 return NULL;
260 #endif
262 void mono_initialize_internals ()
264 mono_add_internal_call ("WebAssembly.Runtime::InvokeJS", mono_wasm_invoke_js);
266 // Blazor specific custom routines - see dotnet_support.js for backing code
267 mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJSMarshalled", mono_wasm_invoke_js_marshalled);
268 mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJSUnmarshalled", mono_wasm_invoke_js_unmarshalled);
270 #ifdef CORE_BINDINGS
271 core_initialize_internals();
272 #endif
276 EMSCRIPTEN_KEEPALIVE void
277 mono_wasm_load_runtime (const char *managed_path, int enable_debugging)
279 monoeg_g_setenv ("MONO_LOG_LEVEL", "debug", 0);
280 monoeg_g_setenv ("MONO_LOG_MASK", "gc", 0);
282 mono_dl_fallback_register (wasm_dl_load, wasm_dl_symbol, NULL, NULL);
284 #ifdef ENABLE_AOT
285 // Defined in driver-gen.c
286 register_aot_modules ();
287 #ifdef EE_MODE_LLVMONLY_INTERP
288 mono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY_INTERP);
289 #else
290 mono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY);
291 #endif
292 #else
293 mono_jit_set_aot_mode (MONO_AOT_MODE_INTERP_LLVMONLY);
294 if (enable_debugging)
295 mono_wasm_enable_debugging ();
296 #endif
298 #ifdef LINK_ICALLS
299 /* Link in our own linked icall table */
300 MonoIcallTableCallbacks cb;
301 memset (&cb, 0, sizeof (MonoIcallTableCallbacks));
302 cb.version = MONO_ICALL_TABLE_CALLBACKS_VERSION;
303 cb.lookup = icall_table_lookup;
304 cb.lookup_icall_symbol = icall_table_lookup_symbol;
306 mono_install_icall_table_callbacks (&cb);
307 #endif
309 #ifdef NEED_NORMAL_ICALL_TABLES
310 mono_icall_table_init ();
311 #endif
312 #ifdef NEED_INTERP
313 mono_ee_interp_init ("");
314 mono_marshal_ilgen_init ();
315 mono_method_builder_ilgen_init ();
316 mono_sgen_mono_ilgen_init ();
317 #endif
319 if (assembly_count) {
320 MonoBundledAssembly **bundle_array = (MonoBundledAssembly **)calloc (1, sizeof (MonoBundledAssembly*) * (assembly_count + 1));
321 WasmAssembly *cur = assemblies;
322 bundle_array [assembly_count] = NULL;
323 int i = 0;
324 while (cur) {
325 bundle_array [i] = &cur->assembly;
326 cur = cur->next;
327 ++i;
329 mono_register_bundled_assemblies ((const MonoBundledAssembly**)bundle_array);
332 mono_trace_init ();
333 mono_trace_set_log_handler (wasm_logger, NULL);
334 root_domain = mono_jit_init_version ("mono", "v4.0.30319");
336 mono_initialize_internals();
339 EMSCRIPTEN_KEEPALIVE MonoAssembly*
340 mono_wasm_assembly_load (const char *name)
342 MonoImageOpenStatus status;
343 MonoAssemblyName* aname = mono_assembly_name_new (name);
344 if (!name)
345 return NULL;
347 MonoAssembly *res = mono_assembly_load (aname, NULL, &status);
348 mono_assembly_name_free (aname);
350 return res;
353 EMSCRIPTEN_KEEPALIVE MonoClass*
354 mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, const char *name)
356 return mono_class_from_name (mono_assembly_get_image (assembly), namespace, name);
359 EMSCRIPTEN_KEEPALIVE MonoMethod*
360 mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int arguments)
362 return mono_class_get_method_from_name (klass, name, arguments);
365 EMSCRIPTEN_KEEPALIVE MonoObject*
366 mono_wasm_invoke_method (MonoMethod *method, MonoObject *this_arg, void *params[], int* got_exception)
368 MonoObject *exc = NULL;
369 MonoObject *res = mono_runtime_invoke (method, this_arg, params, &exc);
370 *got_exception = 0;
372 if (exc) {
373 *got_exception = 1;
375 MonoObject *exc2 = NULL;
376 res = (MonoObject*)mono_object_to_string (exc, &exc2);
377 if (exc2)
378 res = (MonoObject*) mono_string_new (root_domain, "Exception Double Fault");
379 return res;
382 MonoMethodSignature *sig = mono_method_signature (method);
383 MonoType *type = mono_signature_get_return_type (sig);
384 // If the method return type is void return null
385 // This gets around a memory access crash when the result return a value when
386 // a void method is invoked.
387 if (mono_type_get_type (type) == MONO_TYPE_VOID)
388 return NULL;
390 return res;
393 EMSCRIPTEN_KEEPALIVE MonoMethod*
394 mono_wasm_assembly_get_entry_point (MonoAssembly *assembly)
396 MonoImage *image;
397 MonoMethod *method;
399 image = mono_assembly_get_image (assembly);
400 uint32_t entry = mono_image_get_entry_point (image);
401 if (!entry)
402 return NULL;
404 return mono_get_method (image, entry, NULL);
407 EMSCRIPTEN_KEEPALIVE char *
408 mono_wasm_string_get_utf8 (MonoString *str)
410 return mono_string_to_utf8 (str); //XXX JS is responsible for freeing this
413 EMSCRIPTEN_KEEPALIVE MonoString *
414 mono_wasm_string_from_js (const char *str)
416 return mono_string_new (root_domain, str);
419 static int
420 class_is_task (MonoClass *klass)
422 if (!strcmp ("System.Threading.Tasks", mono_class_get_namespace (klass)) &&
423 (!strcmp ("Task", mono_class_get_name (klass)) || !strcmp ("Task`1", mono_class_get_name (klass))))
424 return 1;
426 return 0;
429 #define MARSHAL_TYPE_INT 1
430 #define MARSHAL_TYPE_FP 2
431 #define MARSHAL_TYPE_STRING 3
432 #define MARSHAL_TYPE_VT 4
433 #define MARSHAL_TYPE_DELEGATE 5
434 #define MARSHAL_TYPE_TASK 6
435 #define MARSHAL_TYPE_OBJECT 7
436 #define MARSHAL_TYPE_BOOL 8
437 #define MARSHAL_TYPE_ENUM 9
439 // typed array marshalling
440 #define MARSHAL_ARRAY_BYTE 11
441 #define MARSHAL_ARRAY_UBYTE 12
442 #define MARSHAL_ARRAY_SHORT 13
443 #define MARSHAL_ARRAY_USHORT 14
444 #define MARSHAL_ARRAY_INT 15
445 #define MARSHAL_ARRAY_UINT 16
446 #define MARSHAL_ARRAY_FLOAT 17
447 #define MARSHAL_ARRAY_DOUBLE 18
449 EMSCRIPTEN_KEEPALIVE int
450 mono_wasm_get_obj_type (MonoObject *obj)
452 if (!obj)
453 return 0;
454 MonoClass *klass = mono_object_get_class (obj);
455 MonoType *type = mono_class_get_type (klass);
457 switch (mono_type_get_type (type)) {
458 // case MONO_TYPE_CHAR: prob should be done not as a number?
459 case MONO_TYPE_BOOLEAN:
460 return MARSHAL_TYPE_BOOL;
461 case MONO_TYPE_I1:
462 case MONO_TYPE_U1:
463 case MONO_TYPE_I2:
464 case MONO_TYPE_U2:
465 case MONO_TYPE_I4:
466 case MONO_TYPE_U4:
467 case MONO_TYPE_I8:
468 case MONO_TYPE_U8:
469 return MARSHAL_TYPE_INT;
470 case MONO_TYPE_R4:
471 case MONO_TYPE_R8:
472 return MARSHAL_TYPE_FP;
473 case MONO_TYPE_STRING:
474 return MARSHAL_TYPE_STRING;
475 case MONO_TYPE_SZARRAY: { // simple zero based one-dim-array
476 MonoClass *eklass = mono_class_get_element_class(klass);
477 MonoType *etype = mono_class_get_type (eklass);
479 switch (mono_type_get_type (etype)) {
480 case MONO_TYPE_U1:
481 return MARSHAL_ARRAY_UBYTE;
482 case MONO_TYPE_I1:
483 return MARSHAL_ARRAY_BYTE;
484 case MONO_TYPE_U2:
485 return MARSHAL_ARRAY_USHORT;
486 case MONO_TYPE_I2:
487 return MARSHAL_ARRAY_SHORT;
488 case MONO_TYPE_U4:
489 return MARSHAL_ARRAY_UINT;
490 case MONO_TYPE_I4:
491 return MARSHAL_ARRAY_INT;
492 case MONO_TYPE_R4:
493 return MARSHAL_ARRAY_FLOAT;
494 case MONO_TYPE_R8:
495 return MARSHAL_ARRAY_DOUBLE;
496 default:
497 return MARSHAL_TYPE_OBJECT;
500 default:
501 if (mono_class_is_enum (klass))
502 return MARSHAL_TYPE_ENUM;
503 if (!mono_type_is_reference (type)) //vt
504 return MARSHAL_TYPE_VT;
505 if (mono_class_is_delegate (klass))
506 return MARSHAL_TYPE_DELEGATE;
507 if (class_is_task(klass))
508 return MARSHAL_TYPE_TASK;
510 return MARSHAL_TYPE_OBJECT;
514 EMSCRIPTEN_KEEPALIVE int
515 mono_unbox_int (MonoObject *obj)
517 if (!obj)
518 return 0;
519 MonoType *type = mono_class_get_type (mono_object_get_class(obj));
521 void *ptr = mono_object_unbox (obj);
522 switch (mono_type_get_type (type)) {
523 case MONO_TYPE_I1:
524 case MONO_TYPE_BOOLEAN:
525 return *(signed char*)ptr;
526 case MONO_TYPE_U1:
527 return *(unsigned char*)ptr;
528 case MONO_TYPE_I2:
529 return *(short*)ptr;
530 case MONO_TYPE_U2:
531 return *(unsigned short*)ptr;
532 case MONO_TYPE_I4:
533 return *(int*)ptr;
534 case MONO_TYPE_U4:
535 return *(unsigned int*)ptr;
536 // WASM doesn't support returning longs to JS
537 // case MONO_TYPE_I8:
538 // case MONO_TYPE_U8:
539 default:
540 printf ("Invalid type %d to mono_unbox_int\n", mono_type_get_type (type));
541 return 0;
545 EMSCRIPTEN_KEEPALIVE double
546 mono_wasm_unbox_float (MonoObject *obj)
548 if (!obj)
549 return 0;
550 MonoType *type = mono_class_get_type (mono_object_get_class(obj));
552 void *ptr = mono_object_unbox (obj);
553 switch (mono_type_get_type (type)) {
554 case MONO_TYPE_R4:
555 return *(float*)ptr;
556 case MONO_TYPE_R8:
557 return *(double*)ptr;
558 default:
559 printf ("Invalid type %d to mono_wasm_unbox_float\n", mono_type_get_type (type));
560 return 0;
564 EMSCRIPTEN_KEEPALIVE int
565 mono_wasm_array_length (MonoArray *array)
567 return mono_array_length (array);
570 EMSCRIPTEN_KEEPALIVE MonoObject*
571 mono_wasm_array_get (MonoArray *array, int idx)
573 return mono_array_get (array, MonoObject*, idx);
576 EMSCRIPTEN_KEEPALIVE MonoArray*
577 mono_wasm_obj_array_new (int size)
579 return mono_array_new (root_domain, mono_get_object_class (), size);
582 EMSCRIPTEN_KEEPALIVE void
583 mono_wasm_obj_array_set (MonoArray *array, int idx, MonoObject *obj)
585 mono_array_setref (array, idx, obj);
588 EMSCRIPTEN_KEEPALIVE MonoArray*
589 mono_wasm_string_array_new (int size)
591 return mono_array_new (root_domain, mono_get_string_class (), size);
594 EMSCRIPTEN_KEEPALIVE int
595 mono_wasm_exec_regression (int verbose_level, char *image)
597 return mono_regression_test_step (verbose_level, image, NULL) ? 0 : 1;
600 EMSCRIPTEN_KEEPALIVE int
601 mono_wasm_exit (int exit_code)
603 exit (exit_code);
606 EMSCRIPTEN_KEEPALIVE void
607 mono_wasm_set_main_args (int argc, char* argv[])
609 mono_runtime_set_main_args (argc, argv);
612 EMSCRIPTEN_KEEPALIVE int
613 mono_wasm_strdup (const char *s)
615 return (int)strdup (s);