// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. #include #include #include #include #include #include #include #include #include #include #include #include #include // FIXME: unavailable in emscripten // #include #include #include #include #include #include #include "pinvoke.h" #ifdef CORE_BINDINGS void core_initialize_internals (); #endif // Blazor specific custom routines - see dotnet_support.js for backing code extern void* mono_wasm_invoke_js_blazor (MonoString **exceptionMessage, void *callInfo, void* arg0, void* arg1, void* arg2); // The following two are for back-compat and will eventually be removed extern void* mono_wasm_invoke_js_marshalled (MonoString **exceptionMessage, void *asyncHandleLongPtr, MonoString *funcName, MonoString *argsJson); extern void* mono_wasm_invoke_js_unmarshalled (MonoString **exceptionMessage, MonoString *funcName, void* arg0, void* arg1, void* arg2); void mono_wasm_enable_debugging (int); int mono_wasm_register_root (char *start, size_t size, const char *name); void mono_wasm_deregister_root (char *addr); void mono_ee_interp_init (const char *opts); void mono_marshal_ilgen_init (void); void mono_method_builder_ilgen_init (void); void mono_sgen_mono_ilgen_init (void); void mono_icall_table_init (void); void mono_aot_register_module (void **aot_info); char *monoeg_g_getenv(const char *variable); int monoeg_g_setenv(const char *variable, const char *value, int overwrite); int32_t monoeg_g_hasenv(const char *variable); void mono_free (void*); int32_t mini_parse_debug_option (const char *option); char *mono_method_get_full_name (MonoMethod *method); static MonoClass* datetime_class; static MonoClass* datetimeoffset_class; static MonoClass* uri_class; static MonoClass* task_class; static MonoClass* safehandle_class; static MonoClass* voidtaskresult_class; static int resolved_datetime_class = 0, resolved_datetimeoffset_class = 0, resolved_uri_class = 0, resolved_task_class = 0, resolved_safehandle_class = 0, resolved_voidtaskresult_class = 0; int mono_wasm_enable_gc = 1; /* Not part of public headers */ #define MONO_ICALL_TABLE_CALLBACKS_VERSION 2 typedef struct { int version; void* (*lookup) (MonoMethod *method, char *classname, char *methodname, char *sigstart, int32_t *uses_handles); const char* (*lookup_icall_symbol) (void* func); } MonoIcallTableCallbacks; int mono_string_instance_is_interned (MonoString *str_raw); void mono_install_icall_table_callbacks (const MonoIcallTableCallbacks *cb); int mono_regression_test_step (int verbose_level, char *image, char *method_name); void mono_trace_init (void); #define g_new(type, size) ((type *) malloc (sizeof (type) * (size))) #define g_new0(type, size) ((type *) calloc (sizeof (type), (size))) static MonoDomain *root_domain; #define RUNTIMECONFIG_BIN_FILE "runtimeconfig.bin" static MonoString* mono_wasm_invoke_js (MonoString *str, int *is_exception) { if (str == NULL) return NULL; int native_res_len = 0; int *p_native_res_len = &native_res_len; mono_unichar2 *native_res = (mono_unichar2*)EM_ASM_INT ({ var js_str = MONO.string_decoder.copy ($0); try { var res = eval (js_str); setValue ($2, 0, "i32"); if (res === null || res === undefined) return 0; else res = res.toString (); } catch (e) { res = e.toString(); setValue ($2, 1, "i32"); if (res === null || res === undefined) res = "unknown exception"; var stack = e.stack; if (stack) { // Some JS runtimes insert the error message at the top of the stack, some don't, // so normalize it by using the stack as the result if it already contains the error if (stack.startsWith(res)) res = stack; else res += "\n" + stack; } } var buff = Module._malloc((res.length + 1) * 2); stringToUTF16 (res, buff, (res.length + 1) * 2); setValue ($1, res.length, "i32"); return buff; }, (int)str, p_native_res_len, is_exception); if (native_res == NULL) return NULL; MonoString *res = mono_string_new_utf16 (mono_domain_get (), native_res, native_res_len); free (native_res); return res; } static void wasm_trace_logger (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data) { EM_ASM({ var log_level = $0; var message = Module.UTF8ToString ($1); var isFatal = $2; var domain = Module.UTF8ToString ($3); // is this always Mono? var dataPtr = $4; if (MONO["logging"] && MONO.logging["trace"]) { MONO.logging.trace(domain, log_level, message, isFatal, dataPtr); return; } if (isFatal) console.trace (message); switch (Module.UTF8ToString ($0)) { case "critical": case "error": console.error (message); break; case "warning": console.warn (message); break; case "message": console.log (message); break; case "info": console.info (message); break; case "debug": console.debug (message); break; default: console.log (message); break; } }, log_level, message, fatal, log_domain, user_data); if (fatal) exit (1); } typedef uint32_t target_mword; typedef target_mword SgenDescriptor; typedef SgenDescriptor MonoGCDescriptor; MONO_API int mono_gc_register_root (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, void *key, const char *msg); void mono_gc_deregister_root (char* addr); EMSCRIPTEN_KEEPALIVE int mono_wasm_register_root (char *start, size_t size, const char *name) { return mono_gc_register_root (start, size, (MonoGCDescriptor)NULL, MONO_ROOT_SOURCE_EXTERNAL, NULL, name ? name : "mono_wasm_register_root"); } EMSCRIPTEN_KEEPALIVE void mono_wasm_deregister_root (char *addr) { mono_gc_deregister_root (addr); } #ifdef DRIVER_GEN #include "driver-gen.c" #endif typedef struct WasmAssembly_ WasmAssembly; struct WasmAssembly_ { MonoBundledAssembly assembly; WasmAssembly *next; }; static WasmAssembly *assemblies; static int assembly_count; EMSCRIPTEN_KEEPALIVE int mono_wasm_add_assembly (const char *name, const unsigned char *data, unsigned int size) { int len = strlen (name); if (!strcasecmp (".pdb", &name [len - 4])) { char *new_name = strdup (name); //FIXME handle debugging assemblies with .exe extension strcpy (&new_name [len - 3], "dll"); mono_register_symfile_for_assembly (new_name, data, size); return 1; } WasmAssembly *entry = g_new0 (WasmAssembly, 1); entry->assembly.name = strdup (name); entry->assembly.data = data; entry->assembly.size = size; entry->next = assemblies; assemblies = entry; ++assembly_count; return mono_has_pdb_checksum ((char*)data, size); } int mono_wasm_assembly_already_added (const char *assembly_name) { if (assembly_count == 0) return 0; WasmAssembly *entry = assemblies; while (entry != NULL) { int entry_name_minus_extn_len = strlen(entry->assembly.name) - 4; if (entry_name_minus_extn_len == strlen(assembly_name) && strncmp (entry->assembly.name, assembly_name, entry_name_minus_extn_len) == 0) return 1; entry = entry->next; } return 0; } typedef struct WasmSatelliteAssembly_ WasmSatelliteAssembly; struct WasmSatelliteAssembly_ { MonoBundledSatelliteAssembly *assembly; WasmSatelliteAssembly *next; }; static WasmSatelliteAssembly *satellite_assemblies; static int satellite_assembly_count; EMSCRIPTEN_KEEPALIVE void mono_wasm_add_satellite_assembly (const char *name, const char *culture, const unsigned char *data, unsigned int size) { WasmSatelliteAssembly *entry = g_new0 (WasmSatelliteAssembly, 1); entry->assembly = mono_create_new_bundled_satellite_assembly (name, culture, data, size); entry->next = satellite_assemblies; satellite_assemblies = entry; ++satellite_assembly_count; } EMSCRIPTEN_KEEPALIVE void mono_wasm_setenv (const char *name, const char *value) { monoeg_g_setenv (strdup (name), strdup (value), 1); } static void *sysglobal_native_handle; static void* wasm_dl_load (const char *name, int flags, char **err, void *user_data) { void* handle = wasm_dl_lookup_pinvoke_table (name); if (handle) return handle; if (!strcmp (name, "System.Globalization.Native")) return sysglobal_native_handle; #if WASM_SUPPORTS_DLOPEN return dlopen(name, flags); #endif return NULL; } static void* wasm_dl_symbol (void *handle, const char *name, char **err, void *user_data) { if (handle == sysglobal_native_handle) assert (0); #if WASM_SUPPORTS_DLOPEN if (!wasm_dl_is_pinvoke_tables (handle)) { return dlsym (handle, name); } #endif PinvokeImport *table = (PinvokeImport*)handle; for (int i = 0; table [i].name; ++i) { if (!strcmp (table [i].name, name)) return table [i].func; } return NULL; } #if !defined(ENABLE_AOT) || defined(EE_MODE_LLVMONLY_INTERP) #define NEED_INTERP 1 #ifndef LINK_ICALLS // FIXME: llvm+interp mode needs this to call icalls #define NEED_NORMAL_ICALL_TABLES 1 #endif #endif #ifdef LINK_ICALLS #include "icall-table.h" static int compare_int (const void *k1, const void *k2) { return *(int*)k1 - *(int*)k2; } static void* icall_table_lookup (MonoMethod *method, char *classname, char *methodname, char *sigstart, int32_t *uses_handles) { uint32_t token = mono_method_get_token (method); assert (token); assert ((token & MONO_TOKEN_METHOD_DEF) == MONO_TOKEN_METHOD_DEF); uint32_t token_idx = token - MONO_TOKEN_METHOD_DEF; int *indexes = NULL; int indexes_size = 0; uint8_t *handles = NULL; void **funcs = NULL; *uses_handles = 0; const char *image_name = mono_image_get_name (mono_class_get_image (mono_method_get_class (method))); #if defined(ICALL_TABLE_mscorlib) if (!strcmp (image_name, "mscorlib")) { indexes = mscorlib_icall_indexes; indexes_size = sizeof (mscorlib_icall_indexes) / 4; handles = mscorlib_icall_handles; funcs = mscorlib_icall_funcs; assert (sizeof (mscorlib_icall_indexes [0]) == 4); } #endif #if defined(ICALL_TABLE_corlib) if (!strcmp (image_name, "System.Private.CoreLib")) { indexes = corlib_icall_indexes; indexes_size = sizeof (corlib_icall_indexes) / 4; handles = corlib_icall_handles; funcs = corlib_icall_funcs; assert (sizeof (corlib_icall_indexes [0]) == 4); } #endif #ifdef ICALL_TABLE_System if (!strcmp (image_name, "System")) { indexes = System_icall_indexes; indexes_size = sizeof (System_icall_indexes) / 4; handles = System_icall_handles; funcs = System_icall_funcs; } #endif assert (indexes); void *p = bsearch (&token_idx, indexes, indexes_size, 4, compare_int); if (!p) { return NULL; printf ("wasm: Unable to lookup icall: %s\n", mono_method_get_name (method)); exit (1); } uint32_t idx = (int*)p - indexes; *uses_handles = handles [idx]; //printf ("ICALL: %s %x %d %d\n", methodname, token, idx, (int)(funcs [idx])); return funcs [idx]; } static const char* icall_table_lookup_symbol (void *func) { assert (0); return NULL; } #endif /* * get_native_to_interp: * * Return a pointer to a wasm function which can be used to enter the interpreter to * execute METHOD from native code. * EXTRA_ARG is the argument passed to the interp entry functions in the runtime. */ void* get_native_to_interp (MonoMethod *method, void *extra_arg) { MonoClass *klass = mono_method_get_class (method); MonoImage *image = mono_class_get_image (klass); MonoAssembly *assembly = mono_image_get_assembly (image); MonoAssemblyName *aname = mono_assembly_get_name (assembly); const char *name = mono_assembly_name_get_name (aname); const char *class_name = mono_class_get_name (klass); const char *method_name = mono_method_get_name (method); char key [128]; int len; assert (strlen (name) < 100); snprintf (key, sizeof(key), "%s_%s_%s", name, class_name, method_name); len = strlen (key); for (int i = 0; i < len; ++i) { if (key [i] == '.') key [i] = '_'; } void *addr = wasm_dl_get_native_to_interp (key, extra_arg); return addr; } void mono_initialize_internals () { mono_add_internal_call ("Interop/Runtime::InvokeJS", mono_wasm_invoke_js); // TODO: what happens when two types in different assemblies have the same FQN? // Blazor specific custom routines - see dotnet_support.js for backing code mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJS", mono_wasm_invoke_js_blazor); // The following two are for back-compat and will eventually be removed mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJSMarshalled", mono_wasm_invoke_js_marshalled); mono_add_internal_call ("WebAssembly.JSInterop.InternalCalls::InvokeJSUnmarshalled", mono_wasm_invoke_js_unmarshalled); #ifdef CORE_BINDINGS core_initialize_internals(); #endif } EMSCRIPTEN_KEEPALIVE void mono_wasm_register_bundled_satellite_assemblies () { /* In legacy satellite_assembly_count is always false */ if (satellite_assembly_count) { MonoBundledSatelliteAssembly **satellite_bundle_array = g_new0 (MonoBundledSatelliteAssembly *, satellite_assembly_count + 1); WasmSatelliteAssembly *cur = satellite_assemblies; int i = 0; while (cur) { satellite_bundle_array [i] = cur->assembly; cur = cur->next; ++i; } mono_register_bundled_satellite_assemblies ((const MonoBundledSatelliteAssembly **)satellite_bundle_array); } } void mono_wasm_link_icu_shim (void); void cleanup_runtime_config (MonovmRuntimeConfigArguments *args, void *user_data) { free (args); free (user_data); } EMSCRIPTEN_KEEPALIVE void mono_wasm_load_runtime (const char *unused, int debug_level) { const char *interp_opts = ""; #ifndef INVARIANT_GLOBALIZATION mono_wasm_link_icu_shim (); #endif #ifdef DEBUG monoeg_g_setenv ("MONO_LOG_LEVEL", "debug", 0); monoeg_g_setenv ("MONO_LOG_MASK", "gc", 0); // Setting this env var allows Diagnostic.Debug to write to stderr. In a browser environment this // output will be sent to the console. Right now this is the only way to emit debug logging from // corlib assemblies. monoeg_g_setenv ("COMPlus_DebugWriteToStdErr", "1", 0); #endif // When the list of app context properties changes, please update RuntimeConfigReservedProperties for // target _WasmGenerateRuntimeConfig in WasmApp.targets file const char *appctx_keys[2]; appctx_keys [0] = "APP_CONTEXT_BASE_DIRECTORY"; appctx_keys [1] = "RUNTIME_IDENTIFIER"; const char *appctx_values[2]; appctx_values [0] = "/"; appctx_values [1] = "browser-wasm"; char *file_name = RUNTIMECONFIG_BIN_FILE; int str_len = strlen (file_name) + 1; // +1 is for the "/" char *file_path = (char *)malloc (sizeof (char) * (str_len +1)); // +1 is for the terminating null character int num_char = snprintf (file_path, (str_len + 1), "/%s", file_name); struct stat buffer; assert (num_char > 0 && num_char == str_len); if (stat (file_path, &buffer) == 0) { MonovmRuntimeConfigArguments *arg = (MonovmRuntimeConfigArguments *)malloc (sizeof (MonovmRuntimeConfigArguments)); arg->kind = 0; arg->runtimeconfig.name.path = file_path; monovm_runtimeconfig_initialize (arg, cleanup_runtime_config, file_path); } else { free (file_path); } monovm_initialize (2, appctx_keys, appctx_values); mini_parse_debug_option ("top-runtime-invoke-unhandled"); mono_dl_fallback_register (wasm_dl_load, wasm_dl_symbol, NULL, NULL); mono_wasm_install_get_native_to_interp_tramp (get_native_to_interp); #ifdef ENABLE_AOT monoeg_g_setenv ("MONO_AOT_MODE", "aot", 1); // Defined in driver-gen.c register_aot_modules (); #ifdef EE_MODE_LLVMONLY_INTERP mono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY_INTERP); #else mono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY); #endif #else mono_jit_set_aot_mode (MONO_AOT_MODE_INTERP_ONLY); /* * debug_level > 0 enables debugging and sets the debug log level to debug_level * debug_level == 0 disables debugging and enables interpreter optimizations * debug_level < 0 enabled debugging and disables debug logging. * * Note: when debugging is enabled interpreter optimizations are disabled. */ if (debug_level) { // Disable optimizations which interfere with debugging interp_opts = "-all"; mono_wasm_enable_debugging (debug_level); } #endif #ifdef LINK_ICALLS /* Link in our own linked icall table */ static const MonoIcallTableCallbacks mono_icall_table_callbacks = { MONO_ICALL_TABLE_CALLBACKS_VERSION, icall_table_lookup, icall_table_lookup_symbol }; mono_install_icall_table_callbacks (&mono_icall_table_callbacks); #endif #ifdef NEED_NORMAL_ICALL_TABLES mono_icall_table_init (); #endif #ifdef NEED_INTERP mono_ee_interp_init (interp_opts); mono_marshal_ilgen_init (); mono_method_builder_ilgen_init (); mono_sgen_mono_ilgen_init (); #endif if (assembly_count) { MonoBundledAssembly **bundle_array = g_new0 (MonoBundledAssembly*, assembly_count + 1); WasmAssembly *cur = assemblies; int i = 0; while (cur) { bundle_array [i] = &cur->assembly; cur = cur->next; ++i; } mono_register_bundled_assemblies ((const MonoBundledAssembly **)bundle_array); } mono_wasm_register_bundled_satellite_assemblies (); mono_trace_init (); mono_trace_set_log_handler (wasm_trace_logger, NULL); root_domain = mono_jit_init_version ("mono", "v4.0.30319"); mono_initialize_internals(); mono_thread_set_main (mono_thread_current ()); } EMSCRIPTEN_KEEPALIVE MonoAssembly* mono_wasm_assembly_load (const char *name) { MonoImageOpenStatus status; MonoAssemblyName* aname = mono_assembly_name_new (name); if (!name) return NULL; MonoAssembly *res = mono_assembly_load (aname, NULL, &status); mono_assembly_name_free (aname); return res; } EMSCRIPTEN_KEEPALIVE MonoClass* mono_wasm_find_corlib_class (const char *namespace, const char *name) { return mono_class_from_name (mono_get_corlib (), namespace, name); } EMSCRIPTEN_KEEPALIVE MonoClass* mono_wasm_assembly_find_class (MonoAssembly *assembly, const char *namespace, const char *name) { return mono_class_from_name (mono_assembly_get_image (assembly), namespace, name); } EMSCRIPTEN_KEEPALIVE MonoMethod* mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int arguments) { return mono_class_get_method_from_name (klass, name, arguments); } EMSCRIPTEN_KEEPALIVE MonoMethod* mono_wasm_get_delegate_invoke (MonoObject *delegate) { return mono_get_delegate_invoke(mono_object_get_class (delegate)); } EMSCRIPTEN_KEEPALIVE MonoObject* mono_wasm_box_primitive (MonoClass *klass, void *value, int value_size) { if (!klass) return NULL; MonoType *type = mono_class_get_type (klass); int alignment; if (mono_type_size (type, &alignment) > value_size) return NULL; // TODO: use mono_value_box_checked and propagate error out return mono_value_box (root_domain, klass, value); } EMSCRIPTEN_KEEPALIVE MonoObject* mono_wasm_invoke_method (MonoMethod *method, MonoObject *this_arg, void *params[], MonoObject **out_exc) { MonoObject *exc = NULL; MonoObject *res; if (out_exc) *out_exc = NULL; res = mono_runtime_invoke (method, this_arg, params, &exc); if (exc) { if (out_exc) *out_exc = exc; MonoObject *exc2 = NULL; res = (MonoObject*)mono_object_to_string (exc, &exc2); if (exc2) res = (MonoObject*) mono_string_new (root_domain, "Exception Double Fault"); return res; } MonoMethodSignature *sig = mono_method_signature (method); MonoType *type = mono_signature_get_return_type (sig); // If the method return type is void return null // This gets around a memory access crash when the result return a value when // a void method is invoked. if (mono_type_get_type (type) == MONO_TYPE_VOID) return NULL; return res; } EMSCRIPTEN_KEEPALIVE MonoMethod* mono_wasm_assembly_get_entry_point (MonoAssembly *assembly) { MonoImage *image; MonoMethod *method; image = mono_assembly_get_image (assembly); uint32_t entry = mono_image_get_entry_point (image); if (!entry) return NULL; mono_domain_ensure_entry_assembly (root_domain, assembly); method = mono_get_method (image, entry, NULL); /* * If the entry point looks like a compiler generated wrapper around * an async method in the form "" then try to look up the async methods * "$" and "Name" it could be wrapping. We do this because the generated * sync wrapper will call task.GetAwaiter().GetResult() when we actually want * to yield to the host runtime. */ if (mono_method_get_flags (method, NULL) & 0x0800 /* METHOD_ATTRIBUTE_SPECIAL_NAME */) { const char *name = mono_method_get_name (method); int name_length = strlen (name); if ((*name != '<') || (name [name_length - 1] != '>')) return method; MonoClass *klass = mono_method_get_class (method); char *async_name = malloc (name_length + 2); snprintf (async_name, name_length + 2, "%s$", name); // look for "$" MonoMethodSignature *sig = mono_method_get_signature (method, image, mono_method_get_token (method)); MonoMethod *async_method = mono_class_get_method_from_name (klass, async_name, mono_signature_get_param_count (sig)); if (async_method != NULL) { free (async_name); return async_method; } // look for "Name" by trimming the first and last character of "" async_name [name_length - 1] = '\0'; async_method = mono_class_get_method_from_name (klass, async_name + 1, mono_signature_get_param_count (sig)); free (async_name); if (async_method != NULL) return async_method; } return method; } EMSCRIPTEN_KEEPALIVE char * mono_wasm_string_get_utf8 (MonoString *str) { return mono_string_to_utf8 (str); //XXX JS is responsible for freeing this } // Deprecated EMSCRIPTEN_KEEPALIVE void mono_wasm_string_convert (MonoString *str) { if (str == NULL) return; mono_unichar2 *native_val = mono_string_chars (str); int native_len = mono_string_length (str) * 2; EM_ASM ({ MONO.string_decoder.decode($0, $0 + $1, true); }, (int)native_val, native_len); } EMSCRIPTEN_KEEPALIVE MonoString * mono_wasm_string_from_js (const char *str) { if (str) return mono_string_new (root_domain, str); else return NULL; } EMSCRIPTEN_KEEPALIVE MonoString * mono_wasm_string_from_utf16 (const mono_unichar2 * chars, int length) { assert (length >= 0); if (chars) return mono_string_new_utf16 (root_domain, chars, length); else return NULL; } static int class_is_task (MonoClass *klass) { if (!task_class && !resolved_task_class) { task_class = mono_class_from_name (mono_get_corlib(), "System.Threading.Tasks", "Task"); resolved_task_class = 1; } if (task_class && (klass == task_class || mono_class_is_subclass_of(klass, task_class, 0))) return 1; return 0; } MonoClass* mono_get_uri_class(MonoException** exc) { MonoAssembly* assembly = mono_wasm_assembly_load ("System"); if (!assembly) return NULL; MonoClass* klass = mono_wasm_assembly_find_class(assembly, "System", "Uri"); return klass; } #define MARSHAL_TYPE_INT 1 #define MARSHAL_TYPE_FP64 2 #define MARSHAL_TYPE_STRING 3 #define MARSHAL_TYPE_VT 4 #define MARSHAL_TYPE_DELEGATE 5 #define MARSHAL_TYPE_TASK 6 #define MARSHAL_TYPE_OBJECT 7 #define MARSHAL_TYPE_BOOL 8 #define MARSHAL_TYPE_ENUM 9 #define MARSHAL_TYPE_DATE 20 #define MARSHAL_TYPE_DATEOFFSET 21 #define MARSHAL_TYPE_URI 22 #define MARSHAL_TYPE_SAFEHANDLE 23 // typed array marshalling #define MARSHAL_ARRAY_BYTE 10 #define MARSHAL_ARRAY_UBYTE 11 #define MARSHAL_ARRAY_UBYTE_C 12 #define MARSHAL_ARRAY_SHORT 13 #define MARSHAL_ARRAY_USHORT 14 #define MARSHAL_ARRAY_INT 15 #define MARSHAL_ARRAY_UINT 16 #define MARSHAL_ARRAY_FLOAT 17 #define MARSHAL_ARRAY_DOUBLE 18 #define MARSHAL_TYPE_FP32 24 #define MARSHAL_TYPE_UINT32 25 #define MARSHAL_TYPE_INT64 26 #define MARSHAL_TYPE_UINT64 27 #define MARSHAL_TYPE_CHAR 28 #define MARSHAL_TYPE_STRING_INTERNED 29 #define MARSHAL_TYPE_VOID 30 void mono_wasm_ensure_classes_resolved () { if (!datetime_class && !resolved_datetime_class) { datetime_class = mono_class_from_name (mono_get_corlib(), "System", "DateTime"); resolved_datetime_class = 1; } if (!datetimeoffset_class && !resolved_datetimeoffset_class) { datetimeoffset_class = mono_class_from_name (mono_get_corlib(), "System", "DateTimeOffset"); resolved_datetimeoffset_class = 1; } if (!uri_class && !resolved_uri_class) { MonoException** exc = NULL; uri_class = mono_get_uri_class(exc); resolved_uri_class = 1; } if (!safehandle_class && !resolved_safehandle_class) { safehandle_class = mono_class_from_name (mono_get_corlib(), "System.Runtime.InteropServices", "SafeHandle"); resolved_safehandle_class = 1; } if (!voidtaskresult_class && !resolved_voidtaskresult_class) { voidtaskresult_class = mono_class_from_name (mono_get_corlib(), "System.Threading.Tasks", "VoidTaskResult"); resolved_voidtaskresult_class = 1; } } int mono_wasm_marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType *type) { switch (mono_type) { // case MONO_TYPE_CHAR: prob should be done not as a number? case MONO_TYPE_BOOLEAN: return MARSHAL_TYPE_BOOL; case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_I4: case MONO_TYPE_I: // IntPtr return MARSHAL_TYPE_INT; case MONO_TYPE_CHAR: return MARSHAL_TYPE_CHAR; case MONO_TYPE_U4: // The distinction between this and signed int is // important due to how numbers work in JavaScript return MARSHAL_TYPE_UINT32; case MONO_TYPE_I8: return MARSHAL_TYPE_INT64; case MONO_TYPE_U8: return MARSHAL_TYPE_UINT64; case MONO_TYPE_R4: return MARSHAL_TYPE_FP32; case MONO_TYPE_R8: return MARSHAL_TYPE_FP64; case MONO_TYPE_STRING: return MARSHAL_TYPE_STRING; case MONO_TYPE_SZARRAY: { // simple zero based one-dim-array MonoClass *eklass = mono_class_get_element_class (klass); MonoType *etype = mono_class_get_type (eklass); switch (mono_type_get_type (etype)) { case MONO_TYPE_U1: return MARSHAL_ARRAY_UBYTE; case MONO_TYPE_I1: return MARSHAL_ARRAY_BYTE; case MONO_TYPE_U2: return MARSHAL_ARRAY_USHORT; case MONO_TYPE_I2: return MARSHAL_ARRAY_SHORT; case MONO_TYPE_U4: return MARSHAL_ARRAY_UINT; case MONO_TYPE_I4: return MARSHAL_ARRAY_INT; case MONO_TYPE_R4: return MARSHAL_ARRAY_FLOAT; case MONO_TYPE_R8: return MARSHAL_ARRAY_DOUBLE; default: return MARSHAL_TYPE_OBJECT; } } default: mono_wasm_ensure_classes_resolved (); if (klass == datetime_class) return MARSHAL_TYPE_DATE; if (klass == datetimeoffset_class) return MARSHAL_TYPE_DATEOFFSET; if (uri_class && mono_class_is_assignable_from(uri_class, klass)) return MARSHAL_TYPE_URI; if (klass == voidtaskresult_class) return MARSHAL_TYPE_VOID; if (mono_class_is_enum (klass)) return MARSHAL_TYPE_ENUM; if (!mono_type_is_reference (type)) //vt return MARSHAL_TYPE_VT; if (mono_class_is_delegate (klass)) return MARSHAL_TYPE_DELEGATE; if (class_is_task(klass)) return MARSHAL_TYPE_TASK; if (safehandle_class && (klass == safehandle_class || mono_class_is_subclass_of(klass, safehandle_class, 0))) { return MARSHAL_TYPE_SAFEHANDLE; } return MARSHAL_TYPE_OBJECT; } } EMSCRIPTEN_KEEPALIVE int mono_wasm_get_obj_type (MonoObject *obj) { if (!obj) return 0; /* Process obj before calling into the runtime, class_from_name () can invoke managed code */ MonoClass *klass = mono_object_get_class (obj); if ((klass == mono_get_string_class ()) && mono_string_instance_is_interned ((MonoString *)obj)) return MARSHAL_TYPE_STRING_INTERNED; MonoType *type = mono_class_get_type (klass); obj = NULL; int mono_type = mono_type_get_type (type); return mono_wasm_marshal_type_from_mono_type (mono_type, klass, type); } EMSCRIPTEN_KEEPALIVE int mono_wasm_try_unbox_primitive_and_get_type (MonoObject *obj, void *result) { int *resultI = result; int64_t *resultL = result; float *resultF = result; double *resultD = result; if (!obj) { *resultL = 0; return 0; } /* Process obj before calling into the runtime, class_from_name () can invoke managed code */ MonoClass *klass = mono_object_get_class (obj); if ((klass == mono_get_string_class ()) && mono_string_instance_is_interned ((MonoString *)obj)) { *resultL = 0; return MARSHAL_TYPE_STRING_INTERNED; } MonoType *type = mono_class_get_type (klass), *original_type = type; if (mono_class_is_enum (klass)) type = mono_type_get_underlying_type (type); int mono_type = mono_type_get_type (type); // FIXME: We would prefer to unbox once here but it will fail if the value isn't unboxable switch (mono_type) { case MONO_TYPE_I1: case MONO_TYPE_BOOLEAN: *resultI = *(signed char*)mono_object_unbox (obj); break; case MONO_TYPE_U1: *resultI = *(unsigned char*)mono_object_unbox (obj); break; case MONO_TYPE_I2: case MONO_TYPE_CHAR: *resultI = *(short*)mono_object_unbox (obj); break; case MONO_TYPE_U2: *resultI = *(unsigned short*)mono_object_unbox (obj); break; case MONO_TYPE_I4: case MONO_TYPE_I: *resultI = *(int*)mono_object_unbox (obj); break; case MONO_TYPE_U4: // FIXME: Will this behave the way we want for large unsigned values? *resultI = *(int*)mono_object_unbox (obj); break; case MONO_TYPE_R4: *resultF = *(float*)mono_object_unbox (obj); break; case MONO_TYPE_R8: *resultD = *(double*)mono_object_unbox (obj); break; case MONO_TYPE_I8: case MONO_TYPE_U8: // FIXME: At present the javascript side of things can't handle this, // but there's no reason not to future-proof this API *resultL = *(int64_t*)mono_object_unbox (obj); break; default: // If we failed to do a fast unboxing, return the original type information so // that the caller can do a proper, slow unboxing later *resultL = 0; obj = NULL; return mono_wasm_marshal_type_from_mono_type (mono_type, klass, original_type); } // We successfully performed a fast unboxing here so use the type information // matching what we unboxed (i.e. an enum's underlying type instead of its type) obj = NULL; return mono_wasm_marshal_type_from_mono_type (mono_type, klass, type); } // FIXME: This function is retained specifically because runtime-test.js uses it EMSCRIPTEN_KEEPALIVE int mono_unbox_int (MonoObject *obj) { if (!obj) return 0; MonoType *type = mono_class_get_type (mono_object_get_class(obj)); void *ptr = mono_object_unbox (obj); switch (mono_type_get_type (type)) { case MONO_TYPE_I1: case MONO_TYPE_BOOLEAN: return *(signed char*)ptr; case MONO_TYPE_U1: return *(unsigned char*)ptr; case MONO_TYPE_I2: return *(short*)ptr; case MONO_TYPE_U2: return *(unsigned short*)ptr; case MONO_TYPE_I4: case MONO_TYPE_I: return *(int*)ptr; case MONO_TYPE_U4: return *(unsigned int*)ptr; case MONO_TYPE_CHAR: return *(short*)ptr; // WASM doesn't support returning longs to JS // case MONO_TYPE_I8: // case MONO_TYPE_U8: default: printf ("Invalid type %d to mono_unbox_int\n", mono_type_get_type (type)); return 0; } } EMSCRIPTEN_KEEPALIVE int mono_wasm_array_length (MonoArray *array) { return mono_array_length (array); } EMSCRIPTEN_KEEPALIVE MonoObject* mono_wasm_array_get (MonoArray *array, int idx) { return mono_array_get (array, MonoObject*, idx); } EMSCRIPTEN_KEEPALIVE MonoArray* mono_wasm_obj_array_new (int size) { return mono_array_new (root_domain, mono_get_object_class (), size); } EMSCRIPTEN_KEEPALIVE void mono_wasm_obj_array_set (MonoArray *array, int idx, MonoObject *obj) { mono_array_setref (array, idx, obj); } EMSCRIPTEN_KEEPALIVE MonoArray* mono_wasm_string_array_new (int size) { return mono_array_new (root_domain, mono_get_string_class (), size); } EMSCRIPTEN_KEEPALIVE int mono_wasm_exec_regression (int verbose_level, char *image) { return mono_regression_test_step (verbose_level, image, NULL) ? 0 : 1; } EMSCRIPTEN_KEEPALIVE int mono_wasm_exit (int exit_code) { exit (exit_code); } EMSCRIPTEN_KEEPALIVE void mono_wasm_set_main_args (int argc, char* argv[]) { mono_runtime_set_main_args (argc, argv); } EMSCRIPTEN_KEEPALIVE int mono_wasm_strdup (const char *s) { return (int)strdup (s); } EMSCRIPTEN_KEEPALIVE void mono_wasm_parse_runtime_options (int argc, char* argv[]) { mono_jit_parse_options (argc, argv); } EMSCRIPTEN_KEEPALIVE void mono_wasm_enable_on_demand_gc (int enable) { mono_wasm_enable_gc = enable ? 1 : 0; } EMSCRIPTEN_KEEPALIVE MonoString * mono_wasm_intern_string (MonoString *string) { return mono_string_intern (string); } EMSCRIPTEN_KEEPALIVE void mono_wasm_string_get_data ( MonoString *string, mono_unichar2 **outChars, int *outLengthBytes, int *outIsInterned ) { if (!string) { if (outChars) *outChars = 0; if (outLengthBytes) *outLengthBytes = 0; if (outIsInterned) *outIsInterned = 1; return; } if (outChars) *outChars = mono_string_chars (string); if (outLengthBytes) *outLengthBytes = mono_string_length (string) * 2; if (outIsInterned) *outIsInterned = mono_string_instance_is_interned (string); return; }