/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Authors: Rolf Bjarne Kvinge * * Copyright (C) 2021 Microsoft Corp. * */ #if defined (CORECLR_RUNTIME) #include #include #include #include #include "product.h" #include "runtime-internal.h" #include "slinked-list.h" #include "xamarin/xamarin.h" #include "xamarin/coreclr-bridge.h" #include "coreclrhost.h" unsigned int coreclr_domainId = 0; void *coreclr_handle = NULL; pthread_mutex_t monoobject_lock = PTHREAD_MUTEX_INITIALIZER; SList *release_at_exit = NULL; // A list of MonoObject*s to be released at process exit #if defined (TRACK_MONOOBJECTS) // To enable tracking of MonoObject* instances, uncomment the TRACK_MONOOBJECTS define in: // * runtime/runtime-internal.h // * src/ObjCRuntime/Runtime.CoreCLR.cs // Both defines must be uncommented for tracking to work. Once enabled, you can opt-in to // capturing the stack trace of when the MonoObject* was created, by setting the // MONOOBJECT_TRACKING_WITH_STACKTRACES environment variable. #include static int _Atomic monoobject_created = 0; static int _Atomic monoobject_destroyed = 0; static CFMutableDictionaryRef monoobject_dict = NULL; struct monoobject_tracked_entry { char *managed; void *addresses [128]; int frames; char *native; }; static char * get_stacktrace (void **addresses, int frames) { // get the symbols for the addresses char** strs = backtrace_symbols (addresses, frames); const int max_symbol_length = 512; // this is just to have a maximum value, so that we can use strnlen instead of strlen // compute the total length of all the symbols, adding 1 for every line (for the newline) size_t length = 0; int i; for (i = 0; i < frames; i++) length += strnlen (strs [i], max_symbol_length) + 1; length++; // format the symbols as one long string with newlines char *rv = (char *) calloc (1, length); char *buffer = rv; size_t left = length; for (i = 0; i < frames; i++) { snprintf (buffer, left, "%s\n", strs [i]); size_t slen = strnlen (strs [i], max_symbol_length) + 1; left -= slen; buffer += slen; } free (strs); return rv; } void xamarin_bridge_log_monoobject (MonoObject *mobj, const char *stacktrace) { // add stack traces if we have them / they've been been requested if (monoobject_dict != NULL) { // create a new entry struct monoobject_tracked_entry *value = (struct monoobject_tracked_entry *) calloc (1, sizeof (struct monoobject_tracked_entry)); value->managed = stacktrace ? xamarin_strdup_printf ("%s", stacktrace) : NULL; value->frames = backtrace ((void **) &value->addresses, sizeof (value->addresses) / sizeof (&value->addresses [0])); // insert into our dictionary of monoobjects pthread_mutex_lock (&monoobject_lock); CFDictionarySetValue (monoobject_dict, mobj, value); pthread_mutex_unlock (&monoobject_lock); } atomic_fetch_add (&monoobject_created, 1); } void xamarin_bridge_dump_monoobjects () { if (monoobject_dict != NULL) { // dump the monoobject's that haven't been freed (max 10 entries). pthread_mutex_lock (&monoobject_lock); // get the keys and values unsigned int length = (unsigned int) CFDictionaryGetCount (monoobject_dict); MonoObject** keys = (MonoObject **) calloc (1, sizeof (void*) * length); char** values = (char **) calloc (1, sizeof (char*) * length); CFDictionaryGetKeysAndValues (monoobject_dict, (const void **) keys, (const void **) values); // is there anything left in the dictionary? if so, show that unsigned int items_to_show = length > 10 ? 10 : length; if (items_to_show > 0) { fprintf (stderr, "⚠️ There were %i MonoObjects created, %i MonoObjects freed, so %i were not freed.\n", (int) monoobject_created, (int) monoobject_destroyed, (int) (monoobject_created - monoobject_destroyed)); fprintf (stderr, "Showing the first %i (of %i) MonoObjects:\n", items_to_show, length); for (unsigned int i = 0; i < items_to_show; i++) { MonoObject *obj = keys [i]; struct monoobject_tracked_entry *value = (struct monoobject_tracked_entry *) values [i]; char *fullname = xamarin_get_object_type_fullname (obj->gchandle); fprintf (stderr, "Object %i/%i %p RC: %i Type: %s\n", i + 1, (int) length, obj, (int) obj->reference_count, fullname); xamarin_free (fullname); if (value->managed && *value->managed) fprintf (stderr, "\tManaged stack trace:\n%s\n", value->managed); if (value->native == NULL && value->frames > 0) value->native = get_stacktrace (value->addresses, value->frames); if (value->native && *value->native) fprintf (stderr, "\tNative stack trace:\n%s\n", value->native); } fprintf (stderr, "⚠️ There were %i MonoObjects created, %i MonoObjects freed, so %i were not freed.\n", (int) monoobject_created, (int) monoobject_destroyed, (int) (monoobject_created - monoobject_destroyed)); } else { fprintf (stderr, "✅ There were %i MonoObjects created, %i MonoObjects freed, so no leaked MonoObjects.\n", (int) monoobject_created, (int) monoobject_destroyed); } pthread_mutex_unlock (&monoobject_lock); free (keys); free (values); } else { fprintf (stderr, "There were %i MonoObjects created, %i MonoObjects freed, so %i were not freed.\n", (int) monoobject_created, (int) monoobject_destroyed, (int) (monoobject_created - monoobject_destroyed)); } } static void monoobject_dict_free_value (CFAllocatorRef allocator, const void *value) { struct monoobject_tracked_entry* v = (struct monoobject_tracked_entry *) value; xamarin_free (v->managed); if (v->native) free (v->native); free (v); } #endif // defined (TRACK_MONOOBJECTS) /* * Toggle-ref support for CoreCLR is a bit different than for MonoVM. It goes like this: * * 1) We have to opt-in for the required GC support by calling * ObjectiveCMarshal.Initialize (in managed code) at startup. This happens * in Runtime.InitializeCoreCLRBridge (in Runtime.CoreCLR.cs). * * 2) Types that can be toggled, must have the [ObjectiveCTrackedType] * attribute (on any subclass). We put this attribute on NSObject. * * 3) We have to call ObjectiveCMarshal.CreateReferenceTrackingHandle when an * object is toggled. This callback returns a GCHandle for the managed * object, and a pointer to native memory (size: 2 pointers) where we can * store whatever we want. We store the native Handle, and the Flags * property. Unfortunately this means duplicating information, and we have * to make sure they're in sync. It didn't see a sane way around this * though, because we need the Handle and the Flags somewhere accessible * from native code during the GC (so we can't store them in managed * memory), while at the same time we don't want to create an additional * native block of memory for every NSObject, nor use some complex logic to * support either a managed or a native storage. With the current solution * we're only using additional native memory for toggled objects. * Additionally, updates have to flow: * * a) From managed to native for Handle and Flags, so we update the native * memory (if it's there) when the Handle or Flags properties are set. * b) From native to managed for a single flag value * (NSObjectFlagsInFinalizerQueue), which we fetch in managed code in * the Flags getter. * * Note: we call ObjectiveCMarshal.CreateReferenceTrackingHandle for all * NSObjects, not only toggled ones, because we need point 5) below to * happen for all NSObjects, not just toggled ones. * * 4) The CoreCLR GC will invoke a callback we installed when calling * ObjectiveCMarshal.Initialize to check if that toggled managed object can * be collected or not. This callback is executed during the GC, which * means it's very limited what we can do safely: but we can read and write * to the memory given to us when the managed object was toggled, which is * why we store the Handle and the Flags property - that's what we need to * know to determine whether the managed object can be collected or not. * * 5) When the managed object is finalized, the GC will invoke another * callback (xamarin_coreclr_reference_tracking_tracked_object_entered_finalization) * to let us know, and we'll set the corresponding flag in the flags * * 6) Finally, the GCHandle we got in step 3) is freed when the managed peer * is freed and removed from our object map. * * Caveat: we don't support the server GC (because it uses multiple threads, * and thus may call xamarin_coreclr_reference_tracking_begin_end_callback * from multiple threads for the same garbage collection, which we don't * support right now - but it may be possible to implement by using a * different lock in xamarin_gc_event). * * Ref: https://github.com/dotnet/runtime/issues/44659 * Ref: https://github.com/dotnet/designs/blob/1bb5844c165195e2f633cb1dbe042c4b92aefc4d/accepted/2021/objectivec-interop.md */ struct TrackedObjectInfo { id handle; enum NSObjectFlags flags; }; void xamarin_bridge_setup () { } void xamarin_bridge_initialize () { #if defined (TRACK_MONOOBJECTS) // Only capture the stack trace if requested explicitly, it has a very significant perf hit (monotouch-test is 3x slower). const char *with_stacktraces = getenv ("MONOOBJECT_TRACKING_WITH_STACKTRACES"); if (with_stacktraces && *with_stacktraces) { // Create a dictionary to store the stack traces CFDictionaryValueCallBacks value_callbacks = { 0 }; value_callbacks.release = monoobject_dict_free_value; monoobject_dict = CFDictionaryCreateMutable (kCFAllocatorDefault, 0, NULL, &value_callbacks); fprintf (stderr, "Stack traces enabled for MonoObject tracking.\n"); } #endif // defined (TRACK_MONOOBJECTS) } void xamarin_bridge_shutdown () { SList *list; // Free our list of MonoObject*s to free at process exist. // No need to keep the lock locked while we traverse the list, the only thing we need to protect // are reads and writes to the 'release_at_exit' variable, so let's do just that. pthread_mutex_lock (&monoobject_lock); list = release_at_exit; release_at_exit = NULL; pthread_mutex_unlock (&monoobject_lock); while (list) { xamarin_mono_object_release ((MonoObject **) &list->data); list = list->next; } s_list_free (list); #if defined (TRACK_MONOOBJECTS) xamarin_bridge_dump_monoobjects (); #endif } static bool reference_tracking_end = false; // This callback will be called once before the GC starts calling xamarin_coreclr_reference_tracking_is_referenced_callback, // and once the GC is done. We keep track of which case we're in in the 'reference_tracking_end' variable, and raise the // corresponding GC event. It will only be called once for each GC, both the begin and the end on the same thread. void xamarin_coreclr_reference_tracking_begin_end_callback () { LOG_CORECLR (stderr, "%s () reference_tracking_end: %i\n", __func__, reference_tracking_end); if (reference_tracking_end) { xamarin_gc_event (MONO_GC_EVENT_POST_START_WORLD); } else { xamarin_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD); } reference_tracking_end = !reference_tracking_end; } // This callback is called by the GC to check whether a given managed object // can be collected or not. The single 'ptr' argument is the native memory // returned by the managed call to // ObjectiveCMarshal.CreateReferenceTrackingHandle, and this memory can be // accessed while the GC is running. In here we store the native Handle for // the managed object, and any flags, both of which we need to know in this // method to determine whether the corresponding managed object can be // collected or not. int xamarin_coreclr_reference_tracking_is_referenced_callback (void* ptr) { // This is a callback called by the GC, so there's not much we can do here safely. // Most importantly we can't call managed code, nor access managed memory. // But we can access the native memory given to us when the object was toggled // (and which is passed as the 'ptr' argument), so let's get the data we need from there. int rv = 0; struct TrackedObjectInfo *info = (struct TrackedObjectInfo *) ptr; enum NSObjectFlags flags = info->flags; bool isRegisteredToggleRef = (flags & NSObjectFlagsRegisteredToggleRef) == NSObjectFlagsRegisteredToggleRef; id handle = info->handle; MonoToggleRefStatus res = (MonoToggleRefStatus) 0; if (isRegisteredToggleRef) { res = xamarin_gc_toggleref_callback (flags, handle, NULL, NULL); switch (res) { case MONO_TOGGLE_REF_DROP: // There's no equivalent to DROP in CoreCLR, so just treat it as weak. case MONO_TOGGLE_REF_WEAK: rv = 0; break; case MONO_TOGGLE_REF_STRONG: rv = 1; break; default: LOG_CORECLR (stderr, "%s (%p -> handle: %p flags: %i): INVALID toggle ref value: %i\n", __func__, ptr, handle, flags, res); break; } } else { // If this isn't a toggle ref, it's effectively a weak gchandle rv = 0; } LOG_CORECLR (stderr, "%s (%p -> handle: %p flags: %i) => %i (res: %i) isRegisteredToggleRef: %i\n", __func__, ptr, handle, flags, rv, res, isRegisteredToggleRef); return rv; } // This callback is called when an object is queued for finalization. The // single 'ptr' argument is the native memory returned by the managed call to // ObjectiveCMarshal.CreateReferenceTrackingHandle, and this memory can be // accessed while the GC is running (which it is when this method is called). // In here we set the NSObjectFlagsInFinalizerQueue flag, which managed code // (the NSObject.flags property) will fetch. void xamarin_coreclr_reference_tracking_tracked_object_entered_finalization (void* ptr) { struct TrackedObjectInfo *info = (struct TrackedObjectInfo *) ptr; info->flags = (enum NSObjectFlags) (info->flags | NSObjectFlagsInFinalizerQueue); LOG_CORECLR (stderr, "%s (%p) flags: %i\n", __func__, ptr, (int) info->flags); } void xamarin_coreclr_unhandled_exception_handler (void *context) { // 'context' is the GCHandle returned by the managed Runtime.UnhandledExceptionPropagationHandler function. GCHandle exception_gchandle = (GCHandle) context; LOG_CORECLR (stderr, "%s (%p)\n", __func__, context); // xamarin_process_managed_exception_gchandle will free the GCHandle xamarin_process_managed_exception_gchandle (exception_gchandle); // The call to xamarin_process_managed_exception_gchandle should either abort or throw an Objective-C exception, // and in neither case should we end up here, so just assert. xamarin_assertion_message ("Failed to process unhandled managed exception."); } void xamarin_enable_new_refcount () { // Nothing to do here. } /** * xamarin_bridge_decode_value: * * This implementation is a slightly modified copy (to make it compile) of mono_metadata_decode_value * https://github.com/dotnet/runtime/blob/08a7b2382799082eedb94d70fca6c66eb75f2872/src/mono/mono/metadata/metadata.c#L1525 */ guint32 xamarin_bridge_decode_value (const char *_ptr, const char **rptr) { const unsigned char *ptr = (const unsigned char *) _ptr; unsigned char b = *ptr; guint32 len; if ((b & 0x80) == 0){ len = b; ++ptr; } else if ((b & 0x40) == 0){ len = (guint32) ((b & 0x3f) << 8 | ptr [1]); ptr += 2; } else { len = (guint32) (((b & 0x1f) << 24) | (ptr [1] << 16) | (ptr [2] << 8) | ptr [3]); ptr += 4; } if (rptr) *rptr = (char*)ptr; return len; } static char * xamarin_read_config_string (const char **buf) { guint32 configLength = xamarin_bridge_decode_value (*buf, buf); char *value = strndup (*buf, configLength); *buf = *buf + configLength; return value; } static void * xamarin_mmap_runtime_config_file (size_t *length) { if (xamarin_runtime_configuration_name == NULL) { LOG (PRODUCT ": No runtime config file provided at build time.\n"); return NULL; } char path [1024]; if (!xamarin_locate_app_resource (xamarin_runtime_configuration_name, path, sizeof (path))) { LOG (PRODUCT ": Could not locate the runtime config file '%s' in the app bundle.\n", xamarin_runtime_configuration_name); return NULL; } int fd = open (path, O_RDONLY); if (fd == -1) { LOG (PRODUCT ": Could not open the runtime config file '%s' in the app bundle: %s\n", path, strerror (errno)); return NULL; } struct stat stat_buf = { 0 }; if (fstat (fd, &stat_buf) == -1) { LOG (PRODUCT ": Could not stat the runtime config file '%s' in the app bundle: %s\n", path, strerror (errno)); close (fd); return NULL; } *length = (size_t) stat_buf.st_size; void *buffer = mmap (NULL, *length, PROT_READ, MAP_PRIVATE, fd, 0); close (fd); return buffer; } // Input: the property keys + values passed to xamarin_bridge_vm_initialize // Output: newly allocated arrays of property keys + values that include those passed to xamarin_bridge_vm_initialize together with those in the runtimeconfig.bin file // Caller must free the allocated arrays + their elements void xamarin_bridge_compute_properties (int inputCount, const char **inputKeys, const char **inputValues, int* outputCount, const char ***outputKeys, const char ***outputValues) { size_t fd_len = 0; const char *buf = (const char *) xamarin_mmap_runtime_config_file (&fd_len); int runtimeConfigCount = 0; if (buf != NULL) runtimeConfigCount = (int) xamarin_bridge_decode_value (buf, &buf); // Allocate the output arrays *outputCount = inputCount + runtimeConfigCount; *outputKeys = (const char **) calloc ((size_t) *outputCount, sizeof (char *)); *outputValues = (const char **) calloc ((size_t) *outputCount, sizeof (char *)); // Read the runtimeconfig properties // https://github.com/dotnet/runtime/blob/57bfe474518ab5b7cfe6bf7424a79ce3af9d6657/docs/design/mono/mobile-runtimeconfig-json.md#the-encoded-runtimeconfig-format for (int i = 0; i < runtimeConfigCount; i++) { char *key = xamarin_read_config_string (&buf); char *value = xamarin_read_config_string (&buf); (*outputKeys) [i] = key; (*outputValues) [i] = value; } // Copy the input properties for (int i = 0; i < inputCount; i++) { if (inputKeys [i] != NULL && inputValues [i] != NULL) { (*outputKeys) [i + runtimeConfigCount] = strdup (inputKeys [i]); (*outputValues) [i + runtimeConfigCount] = strdup (inputValues [i]); } else { NSLog (@PRODUCT ": No name/value specified for runtime property %s=%s", inputKeys [i], inputValues [i]); } } if (buf != NULL) munmap ((void *) buf, fd_len); } #if !defined (NATIVEAOT) bool xamarin_bridge_vm_initialize (int propertyCount, const char **propertyKeys, const char **propertyValues) { int rv; int combinedPropertyCount = 0; const char **combinedPropertyKeys = NULL; const char **combinedPropertyValues = NULL; xamarin_bridge_compute_properties (propertyCount, propertyKeys, propertyValues, &combinedPropertyCount, &combinedPropertyKeys, &combinedPropertyValues); const char *executablePath = [[[[NSBundle mainBundle] executableURL] path] UTF8String]; rv = coreclr_initialize ( executablePath, xamarin_executable_name, combinedPropertyCount, combinedPropertyKeys, combinedPropertyValues, &coreclr_handle, &coreclr_domainId ); for (int i = 0; i < combinedPropertyCount; i++) { free ((void *) combinedPropertyKeys [i]); free ((void *) combinedPropertyValues [i]); } free ((void *) combinedPropertyKeys); free ((void *) combinedPropertyValues); LOG_CORECLR (stderr, "xamarin_vm_initialize (%i, %p, %p): rv: %i domainId: %i handle: %p\n", combinedPropertyCount, combinedPropertyKeys, combinedPropertyValues, rv, coreclr_domainId, coreclr_handle); return rv == 0; } #endif // !defined (NATIVEAOT) void xamarin_install_nsautoreleasepool_hooks () { // No need to do anything here for CoreCLR. } void mono_runtime_set_pending_exception (MonoException *exc, mono_bool overwrite) { LOG_CORECLR (stderr, "%s (%p, %i)\n", __func__, exc, overwrite); xamarin_bridge_set_pending_exception (exc); } void xamarin_handle_bridge_exception (GCHandle gchandle, const char *method) { if (gchandle == INVALID_GCHANDLE) return; if (method == NULL) method = ""; fprintf (stderr, "%s threw an exception: %p => %s\n", method, gchandle, [xamarin_print_all_exceptions (gchandle) UTF8String]); xamarin_assertion_message ("%s threw an exception: %p = %s", method, gchandle, [xamarin_print_all_exceptions (gchandle) UTF8String]); } #if !defined (NATIVEAOT) typedef void (*xamarin_runtime_initialize_decl)(struct InitializationOptions* options, GCHandle* exception_gchandle); void xamarin_bridge_call_runtime_initialize (struct InitializationOptions* options, GCHandle* exception_gchandle) { void *del = NULL; int rv = coreclr_create_delegate (coreclr_handle, coreclr_domainId, PRODUCT ", Version=0.0.0.0", "ObjCRuntime.Runtime", "SafeInitialize", &del); if (rv != 0) xamarin_assertion_message ("xamarin_bridge_call_runtime_initialize: failed to create delegate: %i\n", rv); xamarin_runtime_initialize_decl runtime_initialize = (xamarin_runtime_initialize_decl) del; runtime_initialize (options, exception_gchandle); } #endif // !defined (NATIVEAOT) void xamarin_bridge_register_product_assembly (GCHandle* exception_gchandle) { #if !defined (NATIVEAOT) MonoAssembly *assembly; assembly = xamarin_open_and_register (PRODUCT_DUAL_ASSEMBLY, exception_gchandle); xamarin_mono_object_release (&assembly); #endif // !defined (NATIVEAOT) } MonoMethod * xamarin_bridge_get_mono_method (MonoReflectionMethod *method) { // MonoMethod and MonoReflectionMethod are identical in CoreCLR (both are actually MonoObjects). // However, we're returning a retained object, so we need to retain here. xamarin_mono_object_retain (method); LOG_CORECLR (stderr, "%s (%p): rv: %p\n", __func__, method, method); return method; } MonoType * xamarin_get_nsnumber_type () { // xamarin_bridge_lookup_class returns a MonoClass*, and this method returns a MonoType*, // but they're interchangeable for CoreCLR (they're all just MonoObject*s), so this is fine. MonoClass *rv = xamarin_bridge_lookup_class (XamarinLookupTypes_Foundation_NSNumber); LOG_CORECLR (stderr, "%s () => %p\n", __func__, rv); return rv; } MonoType * xamarin_get_nsvalue_type () { // xamarin_bridge_lookup_class returns a MonoClass*, and this method returns a MonoType*, // but they're interchangeable for CoreCLR (they're all just MonoObject*s), so this is fine. MonoClass *rv = xamarin_bridge_lookup_class (XamarinLookupTypes_Foundation_NSValue); LOG_CORECLR (stderr, "%s () => %p\n", __func__, rv); return rv; } void xamarin_mono_object_retain (MonoObject *mobj) { atomic_fetch_add (&mobj->reference_count, 1); } void xamarin_mono_object_release (MonoObject **mobj_ref) { MonoObject *mobj = *mobj_ref; if (mobj == NULL) return; int rc = atomic_fetch_sub (&mobj->reference_count, 1) - 1; if (rc == 0) { if (mobj->gchandle != INVALID_GCHANDLE) { xamarin_gchandle_free (mobj->gchandle); mobj->gchandle = INVALID_GCHANDLE; } xamarin_free (mobj->struct_value); // allocated using Marshal.AllocHGlobal. xamarin_free (mobj); // allocated using Marshal.AllocHGlobal. #if defined (TRACK_MONOOBJECTS) if (monoobject_dict != NULL) { pthread_mutex_lock (&monoobject_lock); CFDictionaryRemoveValue (monoobject_dict, mobj); pthread_mutex_unlock (&monoobject_lock); } atomic_fetch_add (&monoobject_destroyed, 1); #endif } *mobj_ref = NULL; } void xamarin_mono_object_release_at_process_exit (MonoObject *mobj) { pthread_mutex_lock (&monoobject_lock); release_at_exit = s_list_prepend (release_at_exit, mobj); pthread_mutex_unlock (&monoobject_lock); } /* Implementation of the Mono Embedding API */ // returns a retained MonoAssembly * MonoAssembly * mono_assembly_open (const char * filename, MonoImageOpenStatus * status) { MonoAssembly *rv = xamarin_find_assembly (filename); LOG_CORECLR (stderr, "mono_assembly_open (%s, %p) => MonoObject=%p GCHandle=%p\n", filename, status, rv, rv->gchandle); if (status != NULL) *status = rv == NULL ? MONO_IMAGE_ERROR_ERRNO : MONO_IMAGE_OK; return rv; } const char * mono_class_get_namespace (MonoClass * klass) { char *rv = xamarin_bridge_class_get_namespace (klass); LOG_CORECLR (stderr, "%s (%p) => %s\n", __func__, klass, rv); return rv; } const char * mono_class_get_name (MonoClass * klass) { char *rv = xamarin_bridge_class_get_name (klass); LOG_CORECLR (stderr, "%s (%p) => %s\n", __func__, klass, rv); return rv; } char * mono_method_full_name (MonoMethod *method, mono_bool signature) { char *rv = xamarin_bridge_get_method_full_name (method); LOG_CORECLR (stderr, "%s (%p, %i) => %s\n", __func__, method, signature, rv); return rv; } MonoDomain * mono_domain_get (void) { // This is not needed for CoreCLR. return NULL; } MonoType * mono_class_get_type (MonoClass *klass) { // MonoClass and MonoType are identical in CoreCLR (both are actually MonoObjects). // However, we're returning a retained object, so we need to retain here. MonoType *rv = klass; xamarin_mono_object_retain (rv); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, klass, rv); return rv; } // returns a retained MonoReflectionAssembly * MonoReflectionAssembly * mono_assembly_get_object (MonoDomain * domain, MonoAssembly * assembly) { // MonoAssembly and MonoReflectionAssembly are identical in CoreCLR (both are actually MonoObjects). // However, we're returning a retained object, so we need to retain here. xamarin_mono_object_retain (assembly); LOG_CORECLR (stderr, "mono_assembly_get_object (%p, %p): rv: %p\n", domain, assembly, assembly); return assembly; } MonoReflectionMethod * mono_method_get_object (MonoDomain *domain, MonoMethod *method, MonoClass *refclass) { // MonoMethod and MonoReflectionMethod are identical in CoreCLR (both are actually MonoObjects). // However, we're returning a retained object, so we need to retain here. MonoReflectionMethod *rv = method; xamarin_mono_object_retain (rv); LOG_CORECLR (stderr, "%s (%p, %p, %p) => %p\n", __func__, domain, method, refclass, rv); return rv; } MonoType * mono_reflection_type_get_type (MonoReflectionType *reftype) { // MonoType and MonoReflectionType are identical in CoreCLR (both are actually MonoObjects). // However, we're returning a retained object, so we need to retain here. MonoType *rv = reftype; xamarin_mono_object_retain (rv); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, reftype, rv); return rv; } #if !defined (NATIVEAOT) int mono_jit_exec (MonoDomain * domain, MonoAssembly * assembly, int argc, const char** argv) { unsigned int exitCode = 0; char *assemblyPath = xamarin_bridge_get_assembly_location (assembly->gchandle); if (argc > 0) { // The first argument is to the native executable, which we don't want to pass on to native code. argc--; argv = &argv [1]; } LOG_CORECLR (stderr, "mono_jit_exec (%p, %p, %i, %p) => EXECUTING %s\n", domain, assembly, argc, argv, assemblyPath); for (int i = 0; i < argc; i++) { LOG_CORECLR (stderr, " Argument #%i: %s\n", i + 1, argv [i]); } int rv = coreclr_execute_assembly (coreclr_handle, coreclr_domainId, argc, argv, assemblyPath, &exitCode); LOG_CORECLR (stderr, "mono_jit_exec (%p, %p, %i, %p) => EXECUTING %s rv: %i exitCode: %i\n", domain, assembly, argc, argv, assemblyPath, rv, exitCode); xamarin_free (assemblyPath); if (rv != 0) xamarin_assertion_message ("mono_jit_exec failed: %i\n", rv); return (int) exitCode; } #endif // !defined (NATIVEAOT) MonoGHashTable * mono_g_hash_table_new_type (GHashFunc hash_func, GEqualFunc key_equal_func, MonoGHashGCType type) { MonoGHashTable *rv = xamarin_bridge_mono_hash_table_create (hash_func, key_equal_func, type); LOG_CORECLR (stderr, "%s (%p, %p, %u) => %p\n", __func__, hash_func, key_equal_func, type, rv); return rv; } gpointer mono_g_hash_table_lookup (MonoGHashTable *hash, gconstpointer key) { MonoObject *rv = xamarin_bridge_mono_hash_table_lookup (hash, key); LOG_CORECLR (stderr, "%s (%p, %p) => %p\n", __func__, hash, key, rv); return rv; } void mono_g_hash_table_insert (MonoGHashTable *hash, gpointer k, gpointer v) { MonoObject *obj = (MonoObject *) v; LOG_CORECLR (stderr, "%s (%p, %p, %p)\n", __func__, hash, k, v); xamarin_bridge_mono_hash_table_insert (hash, k, obj); } MonoClass * mono_method_get_class (MonoMethod * method) { MonoClass *rv = xamarin_bridge_get_method_declaring_type (method); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, method, rv); return rv; } MonoClass * mono_object_get_class (MonoObject * obj) { MonoClass *rv = xamarin_bridge_object_get_type (obj); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, obj, rv); return rv; } MonoObject * mono_object_isinst (MonoObject * obj, MonoClass * klass) { bool rv = xamarin_bridge_isinstance (obj, klass); LOG_CORECLR (stderr, "%s (%p, %p) => %i\n", __func__, obj, klass, rv); return rv ? obj : NULL; } MonoObject * mono_value_box (MonoDomain *domain, MonoClass *klass, void *val) { MonoObject *rv = xamarin_bridge_box (klass, val); LOG_CORECLR (stderr, "%s (%p, %p, %p) => %p\n", __func__, domain, klass, val, rv); return rv; } void * mono_object_unbox (MonoObject *obj) { void *rv = obj->struct_value; if (rv == NULL) xamarin_assertion_message ("%s (%p) => no struct value?\n", __func__); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, obj, rv); return rv; } // Return value: NULL, or a retained MonoObject* that must be freed with xamarin_mono_object_release. // Returns NULL in case of exception. MonoObject * mono_runtime_invoke (MonoMethod * method, void * obj, void ** params, MonoObject ** exc) { MonoObject *rv = NULL; GCHandle exception_gchandle = INVALID_GCHANDLE; LOG_CORECLR (stderr, "%s (%p, %p, %p, %p)\n", __func__, method, obj, params, exc); rv = xamarin_bridge_runtime_invoke_method (method, (MonoObject *) obj, params, &exception_gchandle); if (exc == NULL) { xamarin_handle_bridge_exception (exception_gchandle, __func__); } else { *exc = xamarin_gchandle_unwrap (exception_gchandle); } return rv; } MonoException * xamarin_create_system_exception (const char *message) { MonoException *rv = xamarin_bridge_create_exception (XamarinExceptionTypes_System_Exception, message); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, message, rv); return rv; } MonoException * xamarin_create_system_invalid_cast_exception (const char *message) { MonoException *rv = xamarin_bridge_create_exception (XamarinExceptionTypes_System_InvalidCastException, message); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, message, rv); return rv; } MonoException * xamarin_create_system_entry_point_not_found_exception (const char *entrypoint) { MonoException *rv = xamarin_bridge_create_exception (XamarinExceptionTypes_System_EntryPointNotFoundException, entrypoint); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, entrypoint, rv); return rv; } MonoException * mono_get_exception_out_of_memory () { MonoException *rv = xamarin_bridge_create_exception (XamarinExceptionTypes_System_OutOfMemoryException, NULL); LOG_CORECLR (stderr, "%s () => %p\n", __func__, rv); return rv; } MonoMethodSignature * mono_method_signature (MonoMethod* method) { MonoMethodSignature *rv = xamarin_bridge_method_get_signature (method); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, method, rv); return rv; } MonoType * mono_signature_get_params (MonoMethodSignature* sig, void ** iter) { int* p = (int *) iter; if (*p >= sig->parameter_count) { LOG_CORECLR (stderr, "%s (%p, %p => %i) => DONE\n", __func__, sig, iter, *p); return NULL; } MonoObject *rv = sig->parameters [*p]; xamarin_mono_object_retain (rv); LOG_CORECLR (stderr, "%s (%p, %p => %i) => %p NEXT\n", __func__, sig, iter, *p, rv->gchandle); *p = *p + 1; return rv; } MonoType * mono_signature_get_return_type (MonoMethodSignature* sig) { MonoType *rv = sig->return_type; xamarin_mono_object_retain (rv); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, sig, rv); return rv; } MonoReflectionType * mono_type_get_object (MonoDomain *domain, MonoType *type) { MonoReflectionType *rv = type; xamarin_mono_object_retain (rv); LOG_CORECLR (stderr, "%s (%p, %p) => %p\n", __func__, domain, type, rv); return rv; } void xamarin_bridge_free_mono_signature (MonoMethodSignature **psig) { MonoMethodSignature *sig = *psig; if (sig == NULL) return; xamarin_mono_object_release (&sig->method); for (int i = 0; i < sig->parameter_count; i++) { xamarin_mono_object_release (&sig->parameters [i]); } xamarin_mono_object_release (&sig->return_type); mono_free (sig); *psig = NULL; } void mono_free (void *ptr) { free (ptr); } mono_bool mono_thread_detach_if_exiting () { // Nothing to do here for CoreCLR. return true; } MonoClass * mono_class_from_mono_type (MonoType *type) { MonoClass *rv = xamarin_bridge_type_to_class (type); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, type, rv); return rv; } MonoClass * mono_get_string_class () { MonoClass *rv = xamarin_bridge_lookup_class (XamarinLookupTypes_System_String); LOG_CORECLR (stderr, "%s () => %p.\n", __func__, rv); return rv; } mono_bool mono_class_is_enum (MonoClass *klass) { bool rv = xamarin_bridge_is_enum (klass); LOG_CORECLR (stderr, "%s (%p) => %i\n", __func__, klass, rv); return rv; } MonoType * mono_class_enum_basetype (MonoClass *klass) { MonoType *rv = xamarin_bridge_get_enum_basetype (klass); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, klass, rv); return rv; } mono_bool mono_type_is_byref (MonoType *type) { bool rv = xamarin_bridge_is_byref (type); LOG_CORECLR (stderr, "%s (%p) => %i\n", __func__, type, rv); return rv; } mono_bool mono_class_is_delegate (MonoClass *klass) { bool rv = xamarin_bridge_is_delegate (klass); LOG_CORECLR (stderr, "%s (%p) => %i\n", __func__, klass, rv); return rv; } mono_bool mono_class_is_valuetype (MonoClass * klass) { bool rv = xamarin_bridge_is_valuetype (klass); LOG_CORECLR (stderr, "%s (%p) => %i\n", __func__, klass, rv); return rv; } int32_t mono_class_value_size (MonoClass *klass, uint32_t *align) { int32_t rv = xamarin_bridge_sizeof (klass); LOG_CORECLR (stderr, "%s (%p, %p) => %i\n", __func__, klass, align, rv); return rv; } gboolean mono_class_is_nullable (MonoClass * klass) { bool rv = xamarin_bridge_is_nullable (klass); LOG_CORECLR (stderr, "%s (%p) => %i\n", __func__, klass, rv); return rv; } MonoClass * mono_class_get_element_class (MonoClass *klass) { MonoClass *rv = xamarin_bridge_get_element_class (klass); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, klass, rv); return rv; } MonoClass * mono_class_get_nullable_param (MonoClass * klass) { MonoClass *rv = xamarin_bridge_get_nullable_element_type (klass); LOG_CORECLR (stderr, "%s (%p) => %p\n", __func__, klass, rv); return rv; } bool xamarin_is_class_nsobject (MonoClass *cls) { return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_Foundation_NSObject); } bool xamarin_is_class_inativeobject (MonoClass *cls) { return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_ObjCRuntime_INativeObject); } bool xamarin_is_class_nativehandle (MonoClass *cls) { return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_ObjCRuntime_NativeHandle); } bool xamarin_is_class_array (MonoClass *cls) { return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_System_Array); } bool xamarin_is_class_nsnumber (MonoClass *cls) { return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_Foundation_NSNumber); } bool xamarin_is_class_nsvalue (MonoClass *cls) { return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_Foundation_NSValue); } bool xamarin_is_class_nsstring (MonoClass *cls) { return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_Foundation_NSString); } bool xamarin_is_class_intptr (MonoClass *cls) { return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_System_IntPtr); } bool xamarin_is_class_string (MonoClass *cls) { return xamarin_bridge_is_class_of_type (cls, XamarinLookupTypes_System_String); } MonoArray * mono_array_new (MonoDomain *domain, MonoClass *eclass, uintptr_t n) { MonoArray *rv = xamarin_bridge_create_array (eclass, n); LOG_CORECLR (stderr, "%s (%p, %p, %" PRIdPTR ") => %p\n", __func__, domain, eclass, n, rv); return rv; } uintptr_t mono_array_length (MonoArray *array) { uintptr_t rv = (uintptr_t) xamarin_bridge_get_array_length (array); LOG_CORECLR (stderr, "%s (%p) => %llu\n", __func__, array, (uint64_t) rv); return rv; } char * mono_string_to_utf8 (MonoString *string_obj) { char *rv = xamarin_bridge_string_to_utf8 (string_obj); LOG_CORECLR (stderr, "%s (%p) => %s\n", __func__, string_obj, rv); return rv; } MonoString * mono_string_new (MonoDomain *domain, const char *text) { MonoString *rv = xamarin_bridge_new_string (text); LOG_CORECLR (stderr, "%s (%p, %s) => %p\n", __func__, domain, text, rv); return rv; } void xamarin_bridge_raise_unhandled_exception_event (GCHandle exception_gchandle) { // There's no way to raise the AppDomain.UnhandledException event. // https://github.com/dotnet/runtime/issues/102730 } #endif // CORECLR_RUNTIME