[wasm] Add normal GC support. (#15577)
[mono-project.git] / sdks / wasm / driver.c
blob99e3a4118970a9fda5d02b97c8c4f4a9ce9d52c9
1 #include <emscripten.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdint.h>
6 #include <assert.h>
7 #include <dlfcn.h>
9 #include <mono/metadata/assembly.h>
10 #include <mono/metadata/tokentype.h>
11 #include <mono/utils/mono-logger.h>
12 #include <mono/utils/mono-dl-fallback.h>
13 #include <mono/jit/jit.h>
15 #ifdef GEN_PINVOKE
16 #include "pinvoke-table.h"
17 #else
18 #include "pinvoke-tables-default.h"
19 #endif
21 #ifdef CORE_BINDINGS
22 void core_initialize_internals ();
23 #endif
25 // Blazor specific custom routines - see dotnet_support.js for backing code
26 extern void* mono_wasm_invoke_js_marshalled (MonoString **exceptionMessage, void *asyncHandleLongPtr, MonoString *funcName, MonoString *argsJson);
27 extern void* mono_wasm_invoke_js_unmarshalled (MonoString **exceptionMessage, MonoString *funcName, void* arg0, void* arg1, void* arg2);
29 void mono_wasm_enable_debugging (void);
31 void mono_ee_interp_init (const char *opts);
32 void mono_marshal_ilgen_init (void);
33 void mono_method_builder_ilgen_init (void);
34 void mono_sgen_mono_ilgen_init (void);
35 void mono_icall_table_init (void);
36 void mono_aot_register_module (void **aot_info);
37 char *monoeg_g_getenv(const char *variable);
38 int monoeg_g_setenv(const char *variable, const char *value, int overwrite);
39 void mono_free (void*);
41 static MonoClass* datetime_class;
42 static MonoClass* datetimeoffset_class;
44 int mono_wasm_enable_gc;
46 /* Not part of public headers */
47 #define MONO_ICALL_TABLE_CALLBACKS_VERSION 2
49 typedef struct {
50 int version;
51 void* (*lookup) (MonoMethod *method, char *classname, char *methodname, char *sigstart, int32_t *uses_handles);
52 const char* (*lookup_icall_symbol) (void* func);
53 } MonoIcallTableCallbacks;
55 void
56 mono_install_icall_table_callbacks (const MonoIcallTableCallbacks *cb);
58 int mono_regression_test_step (int verbose_level, char *image, char *method_name);
59 void mono_trace_init (void);
61 #define g_new(type, size) ((type *) malloc (sizeof (type) * (size)))
62 #define g_new0(type, size) ((type *) calloc (sizeof (type), (size)))
64 static char*
65 m_strdup (const char *str)
67 if (!str)
68 return NULL;
70 const size_t len = strlen (str) + 1;
71 return memcpy (g_new (char, len), str, len);
74 static MonoDomain *root_domain;
76 static MonoString*
77 mono_wasm_invoke_js (MonoString *str, int *is_exception)
79 if (str == NULL)
80 return NULL;
82 char *native_val = mono_string_to_utf8 (str);
83 mono_unichar2 *native_res = (mono_unichar2*)EM_ASM_INT ({
84 var str = UTF8ToString ($0);
85 try {
86 var res = eval (str);
87 if (res === null || res == undefined)
88 return 0;
89 res = res.toString ();
90 setValue ($1, 0, "i32");
91 } catch (e) {
92 res = e.toString ();
93 setValue ($1, 1, "i32");
94 if (res === null || res === undefined)
95 res = "unknown exception";
97 var buff = Module._malloc((res.length + 1) * 2);
98 stringToUTF16 (res, buff, (res.length + 1) * 2);
99 return buff;
100 }, (int)native_val, is_exception);
102 mono_free (native_val);
104 if (native_res == NULL)
105 return NULL;
107 MonoString *res = mono_string_from_utf16 (native_res);
108 free (native_res);
109 return res;
112 static void
113 wasm_logger (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data)
115 if (fatal) {
116 EM_ASM(
117 var err = new Error();
118 console.log ("Stacktrace: \n");
119 console.log (err.stack);
122 fprintf (stderr, "%s", message);
124 abort ();
125 } else {
126 fprintf (stdout, "%s\n", message);
130 #ifdef DRIVER_GEN
131 #include "driver-gen.c"
132 #endif
134 typedef struct WasmAssembly_ WasmAssembly;
136 struct WasmAssembly_ {
137 MonoBundledAssembly assembly;
138 WasmAssembly *next;
141 static WasmAssembly *assemblies;
142 static int assembly_count;
144 EMSCRIPTEN_KEEPALIVE void
145 mono_wasm_add_assembly (const char *name, const unsigned char *data, unsigned int size)
147 int len = strlen (name);
148 if (!strcasecmp (".pdb", &name [len - 4])) {
149 char *new_name = m_strdup (name);
150 //FIXME handle debugging assemblies with .exe extension
151 strcpy (&new_name [len - 3], "dll");
152 mono_register_symfile_for_assembly (new_name, data, size);
153 return;
155 WasmAssembly *entry = g_new0 (WasmAssembly, 1);
156 entry->assembly.name = m_strdup (name);
157 entry->assembly.data = data;
158 entry->assembly.size = size;
159 entry->next = assemblies;
160 assemblies = entry;
161 ++assembly_count;
164 EMSCRIPTEN_KEEPALIVE void
165 mono_wasm_setenv (const char *name, const char *value)
167 monoeg_g_setenv (strdup (name), strdup (value), 1);
170 static void*
171 wasm_dl_load (const char *name, int flags, char **err, void *user_data)
173 for (int i = 0; i < sizeof (pinvoke_tables) / sizeof (void*); ++i) {
174 if (!strcmp (name, pinvoke_names [i]))
175 return pinvoke_tables [i];
178 #if WASM_SUPPORTS_DLOPEN
179 return dlopen(name, flags);
180 #endif
182 return NULL;
185 static mono_bool
186 wasm_dl_is_pinvoke_tables (void* handle) {
187 for (int i = 0; i < sizeof (pinvoke_tables) / sizeof (void*); ++i) {
188 if (pinvoke_tables [i] == handle) {
189 return 1;
192 return 0;
195 static void*
196 wasm_dl_symbol (void *handle, const char *name, char **err, void *user_data)
198 #if WASM_SUPPORTS_DLOPEN
199 if (!wasm_dl_is_pinvoke_tables (handle)) {
200 return dlsym (handle, name);
202 #endif
204 PinvokeImport *table = (PinvokeImport*)handle;
205 for (int i = 0; table [i].name; ++i) {
206 if (!strcmp (table [i].name, name))
207 return table [i].func;
209 return NULL;
212 #if !defined(ENABLE_AOT) || defined(EE_MODE_LLVMONLY_INTERP)
213 #define NEED_INTERP 1
214 #ifndef LINK_ICALLS
215 // FIXME: llvm+interp mode needs this to call icalls
216 #define NEED_NORMAL_ICALL_TABLES 1
217 #endif
218 #endif
220 #ifdef LINK_ICALLS
222 #include "icall-table.h"
224 static int
225 compare_int (const void *k1, const void *k2)
227 return *(int*)k1 - *(int*)k2;
230 static void*
231 icall_table_lookup (MonoMethod *method, char *classname, char *methodname, char *sigstart, int32_t *uses_handles)
233 uint32_t token = mono_method_get_token (method);
234 assert (token);
235 assert ((token & MONO_TOKEN_METHOD_DEF) == MONO_TOKEN_METHOD_DEF);
236 uint32_t token_idx = token - MONO_TOKEN_METHOD_DEF;
238 int *indexes = NULL;
239 int indexes_size = 0;
240 uint8_t *handles = NULL;
241 void **funcs = NULL;
243 *uses_handles = 0;
245 const char *image_name = mono_image_get_name (mono_class_get_image (mono_method_get_class (method)));
247 #ifdef ICALL_TABLE_mscorlib
248 if (!strcmp (image_name, "mscorlib")) {
249 indexes = mscorlib_icall_indexes;
250 indexes_size = sizeof (mscorlib_icall_indexes) / 4;
251 handles = mscorlib_icall_handles;
252 funcs = mscorlib_icall_funcs;
253 assert (sizeof (mscorlib_icall_indexes [0]) == 4);
255 #ifdef ICALL_TABLE_System
256 if (!strcmp (image_name, "System")) {
257 indexes = System_icall_indexes;
258 indexes_size = sizeof (System_icall_indexes) / 4;
259 handles = System_icall_handles;
260 funcs = System_icall_funcs;
262 #endif
263 assert (indexes);
265 void *p = bsearch (&token_idx, indexes, indexes_size, 4, compare_int);
266 if (!p) {
267 return NULL;
268 printf ("wasm: Unable to lookup icall: %s\n", mono_method_get_name (method));
269 exit (1);
272 uint32_t idx = (int*)p - indexes;
273 *uses_handles = handles [idx];
275 //printf ("ICALL: %s %x %d %d\n", methodname, token, idx, (int)(funcs [idx]));
277 return funcs [idx];
278 #endif
281 static const char*
282 icall_table_lookup_symbol (void *func)
284 assert (0);
285 return NULL;
288 #endif
290 void mono_initialize_internals ()
292 mono_add_internal_call ("WebAssembly.Runtime::InvokeJS", mono_wasm_invoke_js);
294 // Blazor specific custom routines - see dotnet_support.js for backing code
295 mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJSMarshalled", mono_wasm_invoke_js_marshalled);
296 mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJSUnmarshalled", mono_wasm_invoke_js_unmarshalled);
298 #ifdef CORE_BINDINGS
299 core_initialize_internals();
300 #endif
304 EMSCRIPTEN_KEEPALIVE void
305 mono_wasm_load_runtime (const char *managed_path, int enable_debugging)
307 monoeg_g_setenv ("MONO_LOG_LEVEL", "debug", 0);
308 monoeg_g_setenv ("MONO_LOG_MASK", "gc", 0);
310 mono_dl_fallback_register (wasm_dl_load, wasm_dl_symbol, NULL, NULL);
312 #ifdef ENABLE_AOT
313 // Defined in driver-gen.c
314 register_aot_modules ();
315 #ifdef EE_MODE_LLVMONLY_INTERP
316 mono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY_INTERP);
317 #else
318 mono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY);
319 #endif
320 #else
321 mono_jit_set_aot_mode (MONO_AOT_MODE_INTERP_LLVMONLY);
322 if (enable_debugging)
323 mono_wasm_enable_debugging ();
324 #endif
326 #ifdef LINK_ICALLS
327 /* Link in our own linked icall table */
328 static const MonoIcallTableCallbacks mono_icall_table_callbacks =
330 MONO_ICALL_TABLE_CALLBACKS_VERSION,
331 icall_table_lookup,
332 icall_table_lookup_symbol
334 mono_install_icall_table_callbacks (&mono_icall_table_callbacks);
335 #endif
337 #ifdef NEED_NORMAL_ICALL_TABLES
338 mono_icall_table_init ();
339 #endif
340 #ifdef NEED_INTERP
341 mono_ee_interp_init ("");
342 mono_marshal_ilgen_init ();
343 mono_method_builder_ilgen_init ();
344 mono_sgen_mono_ilgen_init ();
345 #endif
347 if (assembly_count) {
348 MonoBundledAssembly **bundle_array = g_new0 (MonoBundledAssembly*, assembly_count + 1);
349 WasmAssembly *cur = assemblies;
350 int i = 0;
351 while (cur) {
352 bundle_array [i] = &cur->assembly;
353 cur = cur->next;
354 ++i;
356 mono_register_bundled_assemblies ((const MonoBundledAssembly **)bundle_array);
359 mono_trace_init ();
360 mono_trace_set_log_handler (wasm_logger, NULL);
361 root_domain = mono_jit_init_version ("mono", "v4.0.30319");
363 mono_initialize_internals();
366 EMSCRIPTEN_KEEPALIVE MonoAssembly*
367 mono_wasm_assembly_load (const char *name)
369 MonoImageOpenStatus status;
370 MonoAssemblyName* aname = mono_assembly_name_new (name);
371 if (!name)
372 return NULL;
374 MonoAssembly *res = mono_assembly_load (aname, NULL, &status);
375 mono_assembly_name_free (aname);
377 return res;
380 EMSCRIPTEN_KEEPALIVE MonoClass*
381 mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, const char *name)
383 return mono_class_from_name (mono_assembly_get_image (assembly), namespace, name);
386 EMSCRIPTEN_KEEPALIVE MonoMethod*
387 mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int arguments)
389 return mono_class_get_method_from_name (klass, name, arguments);
392 EMSCRIPTEN_KEEPALIVE MonoObject*
393 mono_wasm_invoke_method (MonoMethod *method, MonoObject *this_arg, void *params[], int* got_exception)
395 MonoObject *exc = NULL;
396 MonoObject *res = mono_runtime_invoke (method, this_arg, params, &exc);
397 *got_exception = 0;
399 if (exc) {
400 *got_exception = 1;
402 MonoObject *exc2 = NULL;
403 res = (MonoObject*)mono_object_to_string (exc, &exc2);
404 if (exc2)
405 res = (MonoObject*) mono_string_new (root_domain, "Exception Double Fault");
406 return res;
409 MonoMethodSignature *sig = mono_method_signature (method);
410 MonoType *type = mono_signature_get_return_type (sig);
411 // If the method return type is void return null
412 // This gets around a memory access crash when the result return a value when
413 // a void method is invoked.
414 if (mono_type_get_type (type) == MONO_TYPE_VOID)
415 return NULL;
417 return res;
420 EMSCRIPTEN_KEEPALIVE MonoMethod*
421 mono_wasm_assembly_get_entry_point (MonoAssembly *assembly)
423 MonoImage *image;
424 MonoMethod *method;
426 image = mono_assembly_get_image (assembly);
427 uint32_t entry = mono_image_get_entry_point (image);
428 if (!entry)
429 return NULL;
431 return mono_get_method (image, entry, NULL);
434 EMSCRIPTEN_KEEPALIVE char *
435 mono_wasm_string_get_utf8 (MonoString *str)
437 return mono_string_to_utf8 (str); //XXX JS is responsible for freeing this
440 EMSCRIPTEN_KEEPALIVE MonoString *
441 mono_wasm_string_from_js (const char *str)
443 return mono_string_new (root_domain, str);
446 static int
447 class_is_task (MonoClass *klass)
449 if (!strcmp ("System.Threading.Tasks", mono_class_get_namespace (klass)) &&
450 (!strcmp ("Task", mono_class_get_name (klass)) || !strcmp ("Task`1", mono_class_get_name (klass))))
451 return 1;
453 return 0;
456 #define MARSHAL_TYPE_INT 1
457 #define MARSHAL_TYPE_FP 2
458 #define MARSHAL_TYPE_STRING 3
459 #define MARSHAL_TYPE_VT 4
460 #define MARSHAL_TYPE_DELEGATE 5
461 #define MARSHAL_TYPE_TASK 6
462 #define MARSHAL_TYPE_OBJECT 7
463 #define MARSHAL_TYPE_BOOL 8
464 #define MARSHAL_TYPE_ENUM 9
465 #define MARSHAL_TYPE_DATE 20
466 #define MARSHAL_TYPE_DATEOFFSET 21
468 // typed array marshalling
469 #define MARSHAL_ARRAY_BYTE 11
470 #define MARSHAL_ARRAY_UBYTE 12
471 #define MARSHAL_ARRAY_SHORT 13
472 #define MARSHAL_ARRAY_USHORT 14
473 #define MARSHAL_ARRAY_INT 15
474 #define MARSHAL_ARRAY_UINT 16
475 #define MARSHAL_ARRAY_FLOAT 17
476 #define MARSHAL_ARRAY_DOUBLE 18
478 EMSCRIPTEN_KEEPALIVE int
479 mono_wasm_get_obj_type (MonoObject *obj)
481 if (!obj)
482 return 0;
484 if (!datetime_class)
485 datetime_class = mono_class_from_name (mono_get_corlib(), "System", "DateTime");
486 if (!datetimeoffset_class)
487 datetimeoffset_class = mono_class_from_name (mono_get_corlib(), "System", "DateTimeOffset");
489 MonoClass *klass = mono_object_get_class (obj);
490 MonoType *type = mono_class_get_type (klass);
492 switch (mono_type_get_type (type)) {
493 // case MONO_TYPE_CHAR: prob should be done not as a number?
494 case MONO_TYPE_BOOLEAN:
495 return MARSHAL_TYPE_BOOL;
496 case MONO_TYPE_I1:
497 case MONO_TYPE_U1:
498 case MONO_TYPE_I2:
499 case MONO_TYPE_U2:
500 case MONO_TYPE_I4:
501 case MONO_TYPE_U4:
502 case MONO_TYPE_I8:
503 case MONO_TYPE_U8:
504 case MONO_TYPE_I: // IntPtr
505 return MARSHAL_TYPE_INT;
506 case MONO_TYPE_R4:
507 case MONO_TYPE_R8:
508 return MARSHAL_TYPE_FP;
509 case MONO_TYPE_STRING:
510 return MARSHAL_TYPE_STRING;
511 case MONO_TYPE_SZARRAY: { // simple zero based one-dim-array
512 MonoClass *eklass = mono_class_get_element_class(klass);
513 MonoType *etype = mono_class_get_type (eklass);
515 switch (mono_type_get_type (etype)) {
516 case MONO_TYPE_U1:
517 return MARSHAL_ARRAY_UBYTE;
518 case MONO_TYPE_I1:
519 return MARSHAL_ARRAY_BYTE;
520 case MONO_TYPE_U2:
521 return MARSHAL_ARRAY_USHORT;
522 case MONO_TYPE_I2:
523 return MARSHAL_ARRAY_SHORT;
524 case MONO_TYPE_U4:
525 return MARSHAL_ARRAY_UINT;
526 case MONO_TYPE_I4:
527 return MARSHAL_ARRAY_INT;
528 case MONO_TYPE_R4:
529 return MARSHAL_ARRAY_FLOAT;
530 case MONO_TYPE_R8:
531 return MARSHAL_ARRAY_DOUBLE;
532 default:
533 return MARSHAL_TYPE_OBJECT;
536 default:
537 if (klass == datetime_class)
538 return MARSHAL_TYPE_DATE;
539 if (klass == datetimeoffset_class)
540 return MARSHAL_TYPE_DATEOFFSET;
541 if (mono_class_is_enum (klass))
542 return MARSHAL_TYPE_ENUM;
543 if (!mono_type_is_reference (type)) //vt
544 return MARSHAL_TYPE_VT;
545 if (mono_class_is_delegate (klass))
546 return MARSHAL_TYPE_DELEGATE;
547 if (class_is_task(klass))
548 return MARSHAL_TYPE_TASK;
550 return MARSHAL_TYPE_OBJECT;
554 EMSCRIPTEN_KEEPALIVE int
555 mono_unbox_int (MonoObject *obj)
557 if (!obj)
558 return 0;
559 MonoType *type = mono_class_get_type (mono_object_get_class(obj));
561 void *ptr = mono_object_unbox (obj);
562 switch (mono_type_get_type (type)) {
563 case MONO_TYPE_I1:
564 case MONO_TYPE_BOOLEAN:
565 return *(signed char*)ptr;
566 case MONO_TYPE_U1:
567 return *(unsigned char*)ptr;
568 case MONO_TYPE_I2:
569 return *(short*)ptr;
570 case MONO_TYPE_U2:
571 return *(unsigned short*)ptr;
572 case MONO_TYPE_I4:
573 case MONO_TYPE_I:
574 return *(int*)ptr;
575 case MONO_TYPE_U4:
576 return *(unsigned int*)ptr;
577 // WASM doesn't support returning longs to JS
578 // case MONO_TYPE_I8:
579 // case MONO_TYPE_U8:
580 default:
581 printf ("Invalid type %d to mono_unbox_int\n", mono_type_get_type (type));
582 return 0;
586 EMSCRIPTEN_KEEPALIVE double
587 mono_wasm_unbox_float (MonoObject *obj)
589 if (!obj)
590 return 0;
591 MonoType *type = mono_class_get_type (mono_object_get_class(obj));
593 void *ptr = mono_object_unbox (obj);
594 switch (mono_type_get_type (type)) {
595 case MONO_TYPE_R4:
596 return *(float*)ptr;
597 case MONO_TYPE_R8:
598 return *(double*)ptr;
599 default:
600 printf ("Invalid type %d to mono_wasm_unbox_float\n", mono_type_get_type (type));
601 return 0;
605 EMSCRIPTEN_KEEPALIVE int
606 mono_wasm_array_length (MonoArray *array)
608 return mono_array_length (array);
611 EMSCRIPTEN_KEEPALIVE MonoObject*
612 mono_wasm_array_get (MonoArray *array, int idx)
614 return mono_array_get (array, MonoObject*, idx);
617 EMSCRIPTEN_KEEPALIVE MonoArray*
618 mono_wasm_obj_array_new (int size)
620 return mono_array_new (root_domain, mono_get_object_class (), size);
623 EMSCRIPTEN_KEEPALIVE void
624 mono_wasm_obj_array_set (MonoArray *array, int idx, MonoObject *obj)
626 mono_array_setref (array, idx, obj);
629 EMSCRIPTEN_KEEPALIVE MonoArray*
630 mono_wasm_string_array_new (int size)
632 return mono_array_new (root_domain, mono_get_string_class (), size);
635 EMSCRIPTEN_KEEPALIVE int
636 mono_wasm_exec_regression (int verbose_level, char *image)
638 return mono_regression_test_step (verbose_level, image, NULL) ? 0 : 1;
641 EMSCRIPTEN_KEEPALIVE int
642 mono_wasm_exit (int exit_code)
644 exit (exit_code);
647 EMSCRIPTEN_KEEPALIVE void
648 mono_wasm_set_main_args (int argc, char* argv[])
650 mono_runtime_set_main_args (argc, argv);
653 EMSCRIPTEN_KEEPALIVE int
654 mono_wasm_strdup (const char *s)
656 return (int)strdup (s);
659 EMSCRIPTEN_KEEPALIVE void
660 mono_wasm_parse_runtime_options (int argc, char* argv[])
662 mono_jit_parse_options (argc, argv);
665 EMSCRIPTEN_KEEPALIVE void
666 mono_wasm_enable_on_demand_gc (void)
668 mono_wasm_enable_gc = 1;