xamarin-macios/runtime/runtime.m

3212 строки
102 KiB
Mathematica
Исходник Обычный вид История

2016-04-21 15:19:32 +03:00
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Authors: Rolf Bjarne Kvinge
*
* Copyright (C) 2014 Xamarin Inc. (www.xamarin.com)
*
*/
#include <pthread.h>
#include <objc/runtime.h>
#include <sys/stat.h>
#include <dlfcn.h>
2016-04-21 15:19:32 +03:00
#include "product.h"
#include "shared.h"
#include "delegates.h"
#include "runtime-internal.h"
#include "xamarin/xamarin.h"
#if !defined (CORECLR_RUNTIME)
#include "xamarin/monovm-bridge.h"
#else
#include "xamarin/coreclr-bridge.h"
#endif
2016-04-21 15:19:32 +03:00
#if defined (DEBUG)
//extern BOOL NSZombieEnabled;
#endif
/*
* These are values that can be configured in xamarin_setup.
*
* The defaults values here have been chosen to minimize
* the configuration required for the simulator (in particular
* the simlauncher binaries).
*/
#if DEBUG
2016-04-21 15:19:32 +03:00
bool xamarin_gc_pump = false;
#endif
2016-04-21 15:19:32 +03:00
#if MONOMAC
// FIXME: implement release mode for monomac.
bool xamarin_debug_mode = true;
bool xamarin_mac_hybrid_aot = false;
[Do not merge yet] Update to mono 2017-04 branch (#1960) * Update to mono 2017-04 branch * Patch from Zoltan to fix build error with CppSharp.CppParser.dll * Include new linker files in Makefile, based on mareks commit * [msbuild] Fix running bgen for Xamarin.Mac. bgen must be executed with the system mono, not bmac-mobile-mono, and without the MONO_PATH variable set. * System.Data tests should act as if they are running on mobile profile * Add --runtime=mobile to mono flags in Modern * Move runtime launcher options up * System.Data tests should use Mobile profile (mac fix) * Bump 2017-04 to pick up AOT and assembly resolution fixes * Build fixes for netstandard.dll and System.Drawing.Primitives.dll The new handling went in with https://github.com/mono/mono/pull/4501. I also noticed that WatchOS was missing a target for System.Drawing.Primitives.dll, so I added that. * Add netstandard.dll to 2.1/Facades and System.Drawing.Primitives.dll to WatchOS * Fix 2.1/Facades/netstandard.dll build * Fix the netstandard targets * Bump mono to latest 2017-04 commit * [xharness] Fix adding defines to csproj by correctly detecting existing defines. * Bump mono to latest 2017-04 commit * [mtouch] Update csproj with new files. * [mtouch] Improve reporting for MarkExceptions from the linker. * Bump mono to latest 2017-04 commit * Bump mono to pick up latest 2017-04 branch commit (Fixes #55436) Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=55436 * Add a missing Makefile dependency * Chris Hamons patch to apply --runtime=mobile as necessary at AOT time (It is currently being applied for some configurations at runtime only) * Bump system mono * Bump mono for assembly loader changes * Bump system mono * Update assemblies list as some where moved to facades https://github.com/mono/mono/commit/6ca5ec442b494bed8cfb44258c1c73c091ba3122 https://github.com/mono/mono/commit/c38e4d9220b16488e6f8f9e1f05aed4a8af16e62 * Bump mono to latest 2017-04 commit * Add another new facade * Bump mono to tip of 2017-04. * Bump mono to tip of 2017-04. * [tests][mtouch] Adjust tests to cope with fewer assemblies being included in linked apps. Fixes #56307 and #56308. System.dll is now completely linked away unless the app actually uses any System.dll API. This is the change that caused this to change: https://github.com/mono/mono/commit/4960d5d2a28a08476ee4239e1746f04afce41c13 Previously the following types would always be kept by the linker: ``` $ monodis --typedef System.dll Typedef Table 1: (null) (flist=1, mlist=1, flags=0x0, extends=0x0) 2: ObjCRuntime.INativeObject (flist=1, mlist=1, flags=0xa0, extends=0x0) 3: Mono.Net.CFObject (flist=1, mlist=2, flags=0x100000, extends=0x5) 4: Mono.Net.CFArray (flist=4, mlist=19, flags=0x100, extends=0xc) 5: Mono.Net.CFNumber (flist=5, mlist=32, flags=0x100100, extends=0xc) 6: Mono.Net.CFRange (flist=5, mlist=41, flags=0x100108, extends=0x25) 7: Mono.Net.CFString (flist=7, mlist=42, flags=0x100100, extends=0xc) 8: Mono.Net.CFData (flist=8, mlist=53, flags=0x100100, extends=0xc) 9: Mono.Net.CFDictionary (flist=8, mlist=63, flags=0x0, extends=0xc) 10: Mono.Net.CFMutableDictionary (flist=10, mlist=75, flags=0x100100, extends=0x24) 11: Mono.Net.CFUrl (flist=10, mlist=80, flags=0x100100, extends=0xc) 12: Mono.Net.CFRunLoop (flist=10, mlist=83, flags=0x100100, extends=0xc) 13: Mono.Net.CFBoolean (flist=10, mlist=94, flags=0x100, extends=0x5) 14: Mono.AppleTls.SecCertificate (flist=13, mlist=106, flags=0x100100, extends=0x5) 15: Mono.AppleTls.SecIdentity (flist=14, mlist=122, flags=0x100, extends=0x5) 16: Mono.AppleTls.SecIdentity/ImportOptions (flist=19, mlist=134, flags=0x100105, extends=0x5) 17: Mono.AppleTls.SecKey (flist=19, mlist=134, flags=0x100100, extends=0x5) 18: Mono.AppleTls.SecStatusCode (flist=21, mlist=141, flags=0x100, extends=0x69) 19: Mono.AppleTls.SecTrustResult (flist=395, mlist=141, flags=0x100, extends=0x69) 20: Mono.AppleTls.SecImportExport (flist=404, mlist=141, flags=0x100100, extends=0x5) 21: Mono.AppleTls.SecImportExport/<>c (flist=404, mlist=144, flags=0x102103, extends=0x5) 22: Mono.AppleTls.SecPolicy (flist=406, mlist=147, flags=0x100100, extends=0x5) 23: Mono.AppleTls.SecTrust (flist=407, mlist=154, flags=0x100100, extends=0x5) 24: System.Security.Cryptography.OidGroup (flist=408, mlist=174, flags=0x101, extends=0x69) 25: System.Security.Cryptography.Oid (flist=420, mlist=174, flags=0x100101, extends=0x5) 26: System.Security.Cryptography.CAPI (flist=423, mlist=176, flags=0x100180, extends=0x5) 27: System.Security.Cryptography.AsnEncodedData (flist=423, mlist=178, flags=0x100101, extends=0x5) 28: System.Security.Cryptography.X509Certificates.X509Utils (flist=424, mlist=179, flags=0x100100, extends=0x5) 29: System.Security.Cryptography.X509Certificates.PublicKey (flist=424, mlist=181, flags=0x100101, extends=0x5) 30: System.Security.Cryptography.X509Certificates.X509Certificate2 (flist=429, mlist=188, flags=0x102101, extends=0x51) 31: System.Security.Cryptography.X509Certificates.X509Certificate2Impl (flist=431, mlist=204, flags=0x100080, extends=0x55) 32: System.Security.Cryptography.X509Certificates.X509CertificateCollection (flist=431, mlist=209, flags=0x102101, extends=0x6d) 33: System.Security.Cryptography.X509Certificates.X509CertificateCollection/X509CertificateEnumerator (flist=431, mlist=212, flags=0x100102, extends=0x5) 34: System.Security.Cryptography.X509Certificates.X509Helper2 (flist=432, mlist=217, flags=0x100180, extends=0x5) 35: <PrivateImplementationDetails> (flist=432, mlist=218, flags=0x100, extends=0x5) 36: <PrivateImplementationDetails>/__StaticArrayInitTypeSize=9 (flist=433, mlist=219, flags=0x113, extends=0x25) ``` Some of the above types from System.dll implemented ObjCRuntime.INativeObject (from System.dll), which our linker detected as implementing ObjCRuntime.INativeObject (from Xamarin.iOS.dll), so these types were treated as custom NSObject subclasses, and the MarkNSObjects linker step would mark them (which would in turn cause all the other types in the list to be marked). With that change, these types now implement ObjCRuntimeInternal.INativeObject, and the linker does not treat them as custom NSObject subclasses anymore. I think the new behavior is correct: these types do not actually inherit from the real NSObject/INativeObject, so the linker should not treat them as such. This may run into different bugs because the linker might now remove more stuff than before, but that would be a different issue. This means that the fix is to modify these tests accordingly. https://bugzilla.xamarin.com/show_bug.cgi?id=56307 https://bugzilla.xamarin.com/show_bug.cgi?id=56308 * Bump mono to latest. * Fix merge conflict that was missed * [mtouch] Renumber new error which clashes with an existing error number in master.
2017-05-29 19:39:29 +03:00
bool xamarin_mac_modern = false;
2016-04-21 15:19:32 +03:00
#else
bool xamarin_debug_mode = false;
#endif
bool xamarin_disable_lldb_attach = false;
bool xamarin_disable_omit_fp = false;
2016-04-21 15:19:32 +03:00
#if DEBUG
bool xamarin_init_mono_debug = true;
#else
bool xamarin_init_mono_debug = false;
#endif
int xamarin_log_level = 0;
const char *xamarin_executable_name = NULL;
#if DOTNET
const char *xamarin_icu_dat_file_name = NULL;
#endif
#if MONOMAC || TARGET_OS_MACCATALYST
NSString * xamarin_custom_bundle_name = @"MonoBundle";
#endif
#if MONOMAC
2016-04-21 15:19:32 +03:00
bool xamarin_is_mkbundle = false;
char *xamarin_entry_assembly_path = NULL;
2016-04-21 15:19:32 +03:00
#endif
#if defined (__i386__)
const char *xamarin_arch_name = "i386";
#elif defined (__x86_64__)
const char *xamarin_arch_name = "x86_64";
#else
const char *xamarin_arch_name = NULL;
#endif
#if TARGET_OS_WATCH
bool xamarin_is_gc_coop = true;
#else
bool xamarin_is_gc_coop = false;
#endif
enum MarshalObjectiveCExceptionMode xamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionModeDefault;
enum MarshalManagedExceptionMode xamarin_marshal_managed_exception_mode = MarshalManagedExceptionModeDefault;
enum XamarinTriState xamarin_log_exceptions = XamarinTriStateNone;
enum XamarinLaunchMode xamarin_launch_mode = XamarinLaunchModeApp;
bool xamarin_supports_dynamic_registration = true;
const char *xamarin_runtime_configuration_name = NULL;
#if DOTNET
enum XamarinNativeLinkMode xamarin_libmono_native_link_mode = XamarinNativeLinkModeStaticObject;
const char **xamarin_runtime_libraries = NULL;
#endif
2016-04-21 15:19:32 +03:00
/* Callbacks */
xamarin_setup_callback xamarin_setup = NULL;
xamarin_register_module_callback xamarin_register_modules = NULL;
xamarin_register_assemblies_callback xamarin_register_assemblies = NULL;
xamarin_extension_main_callback xamarin_extension_main = NULL;
/* Local variable */
static pthread_mutex_t framework_peer_release_lock;
static MonoGHashTable *xamarin_wrapper_hash;
static bool initialize_started = FALSE;
#include "delegates.inc"
/* Keep Trampolines, InitializationFlags and InitializationOptions in sync with Runtime.cs */
struct Trampolines {
void* tramp;
void* stret_tramp;
void* fpret_single_tramp;
void* fpret_double_tramp;
void* release_tramp;
void* retain_tramp;
void* static_tramp;
void* ctor_tramp;
void* x86_double_abi_stret_tramp;
void* static_fpret_single_tramp;
void* static_fpret_double_tramp;
void* static_stret_tramp;
void* x86_double_abi_static_stret_tramp;
void* long_tramp;
void* static_long_tramp;
#if MONOMAC
void* copy_with_zone1;
void* copy_with_zone2;
#endif
void* get_gchandle_tramp;
void* set_gchandle_tramp;
void* get_flags_tramp;
void* set_flags_tramp;
2016-04-21 15:19:32 +03:00
};
enum InitializationFlags : int {
Add a UserType flag for registered types, and use it to improve the performance for is_user_type. (#5017) * Refactor type map to have a separate flag field for each type instead of magic location in the array. Refactor the type map to have a separate flag field for each type instead of a magic location in the array. This simplifies some code, but also allows us to introduce more flags in the future (which becomes increasingly complex if the location in the array is to determine yet another value). This has neglible performance impacts. * Add a UserType flag for registered types, and use it to improve the performance for is_user_type. Reflection in the Objective-C runtime is apparently quite slow, so try to avoid it by computing the information we need for determining whether a particular Objective-C type represents a user type or not in the static registrar. We store this information in a flag for the type in question in the type map, and use a binary search to search the type map when needed. This provides a significant improvement, in particular in the dontlink scenario (probably because there are many more Objective-C types in the app, which made Objective-C reflection slow). In fact, it somehow made the dontlink scenario so fast that it's faster than the linkall scenario (which also improved, but not nearly as much). While quite inexplicable, it's a consistent result I've seen over multiple test runs. Numbers ======= Test case: https://github.com/rolfbjarne/TestApp/commit/004283d7b628a29fcf711d98d8842bfd4ef4393b Fix 1 refers to PR #5009. Fix 2 refers to PR #5013. Fix 3 refers to PR #5016. Fix 4 is this fix. iPad Air 2 ---------- | Configuration | Before | After fix 1 | After fix 2 | After fix 3 | After fix 4 | Improvement from fix 3 to fix 4 | Cumulative improvement | | ------------------- | ------ | ----------: | -----------: | -----------: | -----------: | ------------------------------: | ---------------------: | | Release (link all) | 477 ms | 481 ms | 224 ms | 172 ms | 148 ms | 24 ms (14%) | 329 ms (69%) | | Release (dont link) | 738 ms | 656 ms | 377 ms | 201 ms | 146 ms | 55 ms (27%) | 592 ms (80%) | iPhone X -------- | Configuration | Before | After fix 1 | After fix 2 | After fix 3 | After fix 4 | Improvement from fix 3 to fix 4 | Cumulative improvement | | ------------------- | ------ | ----------: | -----------: | -----------: | -----------: | ------------------------------: | ---------------------: | | Release (link all) | 98 ms | 99 ms | 42 ms | 31 ms | 29 ms | 2 ms ( 6%) | 69 ms (70%) | | Release (dont link) | 197 ms | 153 ms | 91 ms | 43 ms | 28 ms | 15 ms (35%) | 169 ms (86%) | When linking all assemblies, the type map has 24 entries, and when not linking at all it has 2993 entries. This is part 4 (the last) of multiple fixes for #4936. The total speed-up is 69-86% (3-7x faster).
2018-10-22 08:57:16 +03:00
InitializationFlagsIsPartialStaticRegistrar = 0x01,
/* unused = 0x02,*/
2016-04-21 15:19:32 +03:00
InitializationFlagsDynamicRegistrar = 0x04,
/* unused = 0x08,*/
2016-04-21 15:19:32 +03:00
InitializationFlagsIsSimulator = 0x10,
InitializationFlagsIsCoreCLR = 0x20,
2016-04-21 15:19:32 +03:00
};
struct InitializationOptions {
int size; // the size of this structure. This is used for version checking.
enum InitializationFlags flags;
struct Delegates* Delegates;
struct Trampolines* Trampolines;
[registrar] Use metadata tokens instead of strings to find types and methods. (#1085) Use metadata tokens instead of strings to find types and methods. This makes the code to find methods more compact (a lot less strings in the executable, and additionally in most cases a compact representation (32-bit integer) of the corresponding metadata token and additional information can be used, which results in less executable code (fewer parameters to methods, etc)), resulting in smaller executables. Size savings are around 200kb for dont link apps, and 20-60kb for linked apps (this obviously varies a lot depending on how much has to registered by the registrar). | | Before | After | Diff | |----------------|--------------:|--------------:|------------------:| | dontlink/32bit | 102.810.144 | 102.609.456 | -200.688 = -0,20% | | dontlink/64bit | 107.420.576 | 107.221.792 | -198.784 = -0,19% | | linksdk/32bit | 40.957.296 | 40.936.864 | -20.432 = -0,05% | | linksdk/64bit | 43.113.136 | 43.093.936 | -19.200 = -0,04% | | linkall/32bit | 38.410.032 | 38.348.288 |  -61.744 = -0,16% | | linkall/64bit | 40.315.200 | 40.267.344 | -47.856 = -0,12% | Additionally I've removed the `lazy_map` dictionary, which we populated at startup and was used to map between Class instances and the corresponding managed type's FullName, and instead iterate over a native array of Class -> metadata token mappings whenever we need to look up the managed type for a certain Class instance. This is slightly slower for each type we need to look up (for a non-linked app there might be a 2000-3000 entries in the native array, which would be iterated instead of using a hashtable lookup), but it's only done once per type and there's a significant startup memory improvement. For a non-linked test app I get the following using the Xamarin profiler: | | Before | After | Diff | |-------------------|--------:|--------:|----------------:| | Memory allocated | 2,8 MB | 2,4 MB | -0,4 MB = -14 % | | Objects allocated | 43678 | 38463 | -5215 = -12 % | | Private bytes | 26,6 MB | 24,4 MB | -2,2 MB = -8,3% | | Working set | 26,6 MB | 24,4 MB | -2,2 MB = -8,3% |
2016-11-01 21:34:56 +03:00
struct MTRegistrationMap* RegistrationData;
enum MarshalObjectiveCExceptionMode MarshalObjectiveCExceptionMode;
enum MarshalManagedExceptionMode MarshalManagedExceptionMode;
#if MONOMAC
enum XamarinLaunchMode LaunchMode;
const char *EntryAssemblyPath;
#endif
struct AssemblyLocations* AssemblyLocations;
#if DOTNET
// This struct must be kept in sync with the corresponding struct in Runtime.cs, and since we use the same managed code for both MonoVM and CoreCLR,
// we can't restrict the following fields to CORECLR_RUNTIME only, we can only exclude it from legacy Xamarin.
void *xamarin_objc_msgsend;
void *xamarin_objc_msgsend_super;
void *xamarin_objc_msgsend_stret;
void *xamarin_objc_msgsend_super_stret;
void *unhandled_exception_handler;
void *reference_tracking_begin_end_callback;
void *reference_tracking_is_referenced_callback;
void *reference_tracking_tracked_object_entered_finalization;
#endif
2016-04-21 15:19:32 +03:00
};
static struct Trampolines trampolines = {
(void *) &xamarin_trampoline,
(void *) &xamarin_stret_trampoline,
(void *) &xamarin_fpret_single_trampoline,
(void *) &xamarin_fpret_double_trampoline,
(void *) &xamarin_release_trampoline,
(void *) &xamarin_retain_trampoline,
(void *) &xamarin_static_trampoline,
(void *) &xamarin_ctor_trampoline,
#if defined (__i386__)
(void *) &xamarin_x86_double_abi_stret_trampoline,
#else
NULL,
#endif
(void *) &xamarin_static_fpret_single_trampoline,
(void *) &xamarin_static_fpret_double_trampoline,
(void *) &xamarin_static_stret_trampoline,
#if defined (__i386__)
(void *) &xamarin_static_x86_double_abi_stret_trampoline,
#else
NULL,
#endif
(void *) &xamarin_longret_trampoline,
(void *) &xamarin_static_longret_trampoline,
#if MONOMAC
(void *) &xamarin_copyWithZone_trampoline1,
(void *) &xamarin_copyWithZone_trampoline2,
#endif
(void *) &xamarin_get_gchandle_trampoline,
(void *) &xamarin_set_gchandle_trampoline,
(void *) &xamarin_get_flags_trampoline,
(void *) &xamarin_set_flags_trampoline,
2016-04-21 15:19:32 +03:00
};
static struct InitializationOptions options = { 0 };
#if !defined (CORECLR_RUNTIME)
void
xamarin_add_internal_call (const char *name, const void *method)
{
/* COOP: With cooperative GC, icalls will run, like managed methods,
* in GC Unsafe mode, avoiding a thread state transition. In return
* the icalls must guarantee that they won't block, or run indefinitely
* without a safepoint, by manually performing a transition to GC Safe
* mode. With backward-compatible hybrid GC, icalls run in GC Safe
* mode and the Mono API functions take care of thread state
* transitions, so don't need to perform GC thread state transitions
* themselves.
*
*/
if (xamarin_is_gc_coop)
mono_dangerous_add_raw_internal_call (name, method);
else
mono_add_internal_call (name, method);
}
#endif // !CORECLR_RUNTIME
2016-04-21 15:19:32 +03:00
id
xamarin_get_nsobject_handle (MonoObject *obj)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
#if defined (CORECLR_RUNTIME)
id rv = xamarin_get_handle_for_inativeobject (obj);
LOG_CORECLR (stderr, "xamarin_get_nsobject_handle (%p) => %p\n", obj, rv);
return rv;
#else
2016-04-21 15:19:32 +03:00
struct Managed_NSObject *mobj = (struct Managed_NSObject *) obj;
return mobj->handle;
#endif
2016-04-21 15:19:32 +03:00
}
uint8_t
xamarin_get_nsobject_flags (MonoObject *obj)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
#if defined (CORECLR_RUNTIME)
return xamarin_get_flags_for_nsobject (obj->gchandle);
#else
2016-04-21 15:19:32 +03:00
struct Managed_NSObject *mobj = (struct Managed_NSObject *) obj;
return mobj->flags;
#endif
2016-04-21 15:19:32 +03:00
}
void
xamarin_set_nsobject_flags (MonoObject *obj, uint8_t flags)
{
// COOP: Writing managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
#if defined (CORECLR_RUNTIME)
xamarin_set_flags_for_nsobject (obj->gchandle, flags);
#else
2016-04-21 15:19:32 +03:00
struct Managed_NSObject *mobj = (struct Managed_NSObject *) obj;
mobj->flags = flags;
#endif
2016-04-21 15:19:32 +03:00
}
MonoType *
xamarin_get_parameter_type (MonoMethod *managed_method, int index)
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
MonoMethodSignature *msig = mono_method_signature (managed_method);
void *iter = NULL;
MonoType *p = NULL;
if (index == -1) {
p = mono_signature_get_return_type (msig);
} else {
for (int i = 0; i < index + 1; i++) {
xamarin_mono_object_release (&p);
p = mono_signature_get_params (msig, &iter);
}
}
2016-04-21 15:19:32 +03:00
xamarin_bridge_free_mono_signature (&msig);
2016-04-21 15:19:32 +03:00
return p;
}
MonoObject *
xamarin_get_nsobject_with_type_for_ptr (id self, bool owns, MonoType* type, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
int32_t created;
return xamarin_get_nsobject_with_type_for_ptr_created (self, owns, type, &created, exception_gchandle);
2016-04-21 15:19:32 +03:00
}
MonoObject *
xamarin_get_nsobject_with_type_for_ptr_created (id self, bool owns, MonoType *type, int32_t *created, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
MonoObject *mobj = NULL;
GCHandle gchandle = INVALID_GCHANDLE;
2016-04-21 15:19:32 +03:00
*created = false;
if (self == NULL)
return NULL;
gchandle = xamarin_get_gchandle (self);
if (gchandle != INVALID_GCHANDLE) {
mobj = xamarin_gchandle_get_target (gchandle);
MonoClass *klass = mono_class_from_mono_type (type);
bool isinst = mono_object_isinst (mobj, klass) != NULL;
xamarin_mono_object_release (&klass);
if (isinst) {
2016-04-21 15:19:32 +03:00
return mobj;
} else {
xamarin_mono_object_release (&mobj);
}
2016-04-21 15:19:32 +03:00
}
MonoReflectionType *rtype = mono_type_get_object (mono_domain_get (), type);
MonoObject *rv = xamarin_get_nsobject_with_type (self, rtype, created, exception_gchandle);
xamarin_mono_object_release (&rtype);
return rv;
2016-04-21 15:19:32 +03:00
}
MonoObject *
xamarin_get_managed_object_for_ptr_fast (id self, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: Reading managed data, must be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
MonoObject *mobj = NULL;
GCHandle gchandle = INVALID_GCHANDLE;
2016-04-21 15:19:32 +03:00
gchandle = xamarin_get_gchandle (self);
if (gchandle == INVALID_GCHANDLE) {
mobj = xamarin_try_get_or_construct_nsobject (self, exception_gchandle);
2016-04-21 15:19:32 +03:00
} else {
mobj = xamarin_gchandle_get_target (gchandle);
2016-04-21 15:19:32 +03:00
#if DEBUG
if (self != xamarin_get_nsobject_handle (mobj)) {
xamarin_assertion_message ("Internal consistency error, please file a bug (https://github.com/xamarin/xamarin-macios/issues/new). Additional data: found managed object %p=%p (%s) in native object %p (%s).\n",
mobj, xamarin_get_nsobject_handle (mobj), xamarin_class_get_full_name (mono_object_get_class (mobj), exception_gchandle), self, object_getClassName (self));
2016-04-21 15:19:32 +03:00
}
#endif
}
return mobj;
}
// See comments in the following methods to explain the logic here:
// xamarin_marshal_return_value_impl in trampolines.m
// xamarin_release_managed_ref in runtime.m
void xamarin_framework_peer_waypoint ()
2016-04-21 15:19:32 +03:00
{
// COOP: CHECK
MONO_ASSERT_GC_UNSAFE;
MONO_ENTER_GC_SAFE;
2016-04-21 15:19:32 +03:00
pthread_mutex_lock (&framework_peer_release_lock);
pthread_mutex_unlock (&framework_peer_release_lock);
MONO_EXIT_GC_SAFE;
2016-04-21 15:19:32 +03:00
}
// Same as xamarin_framework_peer_waypoint, except the current mode should be GC Safe.
void xamarin_framework_peer_waypoint_safe ()
{
MONO_ASSERT_GC_SAFE_OR_DETACHED;
pthread_mutex_lock (&framework_peer_release_lock);
2016-04-21 15:19:32 +03:00
pthread_mutex_unlock (&framework_peer_release_lock);
}
MonoObject *
xamarin_new_nsobject (id self, MonoClass *klass, GCHandle *exception_gchandle)
{
MonoType *type = mono_class_get_type (klass);
MonoReflectionType *rtype = mono_type_get_object (mono_domain_get (), type);
xamarin_mono_object_release (&type);
GCHandle obj = xamarin_create_nsobject (rtype, self, NSObjectFlagsNativeRef, exception_gchandle);
xamarin_mono_object_release (&rtype);
return xamarin_gchandle_unwrap (obj);
}
// Returns if a MonoClass is nullable.
// Will also return the element type (it the type is nullable, and if out pointer is not NULL).
bool
xamarin_is_class_nullable (MonoClass *cls, MonoClass **element_type, GCHandle *exception_gchandle)
{
#ifdef DYNAMIC_MONO_RUNTIME
// mono_class_is_nullable/mono_class_get_nullable_param are private
// functions, and as such we can't call find them in libmono.dylib. In
// this case we manually call a managed function to do the work for us (we
// don't use the normal delegate mechanism, because how it's currently
// implemented it would inflict size costs on all platforms, not just
// Xamarin.Mac).
if (!mono_class_is_nullable_exists () || !mono_class_get_nullable_param_exists ()) {
static MonoMethod *get_nullable_type = NULL;
if (get_nullable_type == NULL)
get_nullable_type = mono_class_get_method_from_name (xamarin_get_runtime_class (), "GetNullableType", 1);
GCHandle type_handle = xamarin_gchandle_new ((MonoObject *) mono_type_get_object (mono_domain_get (), mono_class_get_type (cls)), false);
void *args [1] { type_handle };
MonoObject *exc = NULL;
MonoObject *nullable_type_handle = mono_runtime_invoke (get_nullable_type, NULL, args, &exc);
xamarin_gchandle_free (type_handle);
if (exc != NULL) {
*exception_gchandle = xamarin_gchandle_new (exc, FALSE);
return false;
}
MonoReflectionType *nullable_type = (MonoReflectionType *) xamarin_gchandle_unwrap (nullable_type_handle);
if (element_type != NULL && nullable_type != NULL) {
MonoType *mono_type = mono_reflection_type_get_type (nullable_type);
*element_type = mono_class_from_mono_type (mono_type);
xamarin_mono_object_release (&mono_type);
}
bool is_nullable = nullable_type != NULL;
xamarin_mono_object_release (&nullable_type);
return is_nullable;
}
#endif
bool rv = mono_class_is_nullable (cls);
if (rv && element_type)
*element_type = mono_class_get_nullable_param (cls);
return rv;
}
MonoClass *
xamarin_get_nullable_type (MonoClass *cls, GCHandle *exception_gchandle)
{
MonoClass *rv = NULL;
xamarin_is_class_nullable (cls, &rv, exception_gchandle);
return rv;
}
2016-04-21 15:19:32 +03:00
// The XamarinExtendedObject protocol is just to avoid a
// compiler warning (no 'xamarinGetGChandle' selector found).
@protocol XamarinExtendedObject
-(GCHandle) xamarinGetGCHandle;
-(bool) xamarinSetGCHandle: (GCHandle) gc_handle flags: (enum XamarinGCHandleFlags) flags;
-(enum XamarinGCHandleFlags) xamarinGetFlags;
-(void) xamarinSetFlags: (enum XamarinGCHandleFlags) flags;
2016-04-21 15:19:32 +03:00
@end
static inline GCHandle
get_gchandle_safe (id self, enum XamarinGCHandleFlags *flags)
2016-04-21 15:19:32 +03:00
{
// COOP: we call a selector, and that must only be done in SAFE mode.
[coop runtime] add a GC transition and relax some GC related assertions (#7036) * [coop] add missing gc_unsafe transition frame #3: 0x02a39fd4 monotouchtest`log_callback(log_domain=0x00000000, log_level="error", message="../../../../../mono/metadata/object.c:1905: Expected GC Unsafe mode but was in STATE_BLOCKING state", fatal=4, user_data=0x00000000) at runtime.m:1251:3 frame #4: 0x02a03f78 monotouchtest`monoeg_g_logv_nofree(log_domain=0x00000000, log_level=G_LOG_LEVEL_ERROR, format=<unavailable>, args=<unavailable>) at goutput.c:149:2 [opt] frame #5: 0x02a03f14 monotouchtest`monoeg_g_logv(log_domain=<unavailable>, log_level=<unavailable>, format=<unavailable>, args=<unavailable>) at goutput.c:156:10 [opt] frame #6: 0x02a03fa8 monotouchtest`monoeg_g_log(log_domain=<unavailable>, log_level=<unavailable>, format=<unavailable>) at goutput.c:165:2 [opt] frame #7: 0x029e54f8 monotouchtest`assert_gc_unsafe_mode(file="../../../../../mono/metadata/object.c", lineno=1905) at checked-build.c:396:3 [opt] frame #8: 0x0294ef98 monotouchtest`mono_class_vtable_checked(domain=0x16d87b70, klass=0x17bfab98, error=0x19498a08) at object.c:1905:2 [opt] frame #9: 0x0298f0dc monotouchtest`get_current_thread_ptr_for_domain(domain=0x16d87b70, thread=0x03f645d0) at threads.c:635:2 [opt] frame #10: 0x0298d9dc monotouchtest`mono_thread_current at threads.c:2026:23 [opt] frame #11: 0x02992a80 monotouchtest`mono_runtime_set_pending_exception(exc=0x0357e4a0, overwrite=0) at threads.c:5176:23 [opt] frame #12: 0x02a3c650 monotouchtest`::xamarin_process_nsexception_using_mode(ns_exception=name: "System.ApplicationException" - reason: "3,14", throwManagedAsDefault=false) at runtime.m:2369:4 frame #13: 0x02a3c478 monotouchtest`::xamarin_process_nsexception(ns_exception=name: "System.ApplicationException" - reason: "3,14") at runtime.m:2336:2 frame #14: 0x0270b488 monotouchtest`::xamarin_pinvoke_wrapper_objc_msgSendSuper18(__p__0=0x0357d0f0, __p__1=0x02ad1ef2) at pinvokes.m:5732:4 frame #15: 0x02a64764 monotouchtest`do_icall(frame=<unavailable>, sig=0x17d70bf0, op=<unavailable>, sp=0x19498fa0, ptr=<unavailable>, save_last_error=0) at interp.c:1947:3 [opt] frame #16: 0x02a632b8 monotouchtest`do_icall_wrapper(frame=0x194991b0, sig=0x17d70bf0, op=547, sp=0x19498fb0, ptr=0x0270b408, save_last_error=0) at interp.c:2037:7 [opt] Messaging::void_objc_msgSendSuper @ 396160394 "calli.nat.fast" || frame #17: 0x02a51d5c monotouchtest`interp_exec_method_full(frame=0x194991b0, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3229:9 [opt] ObjCExceptionTest::InvokeManagedExceptionThrower @ 396160296 "vcall" || frame #18: 0x02a524e8 monotouchtest`interp_exec_method_full(frame=0x19499370, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3400:4 [opt] ExceptionsTest::ManagedExceptionPassthrough @ 396150902 "vcallvirt.fast" || frame #19: 0x02a521cc monotouchtest`interp_exec_method_full(frame=0x194994d0, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3325:4 [opt] Object::runtime_invoke_direct_void__this__ @ 401347822 "vcall" || frame #20: 0x02a524e8 monotouchtest`interp_exec_method_full(frame=0x19499588, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3400:4 [opt] frame #21: 0x02a506f8 monotouchtest`interp_runtime_invoke(method=<unavailable>, obj=0x0357c4f0, params=0x00000000, exc=0x1949965c, error=0x194998b8) at interp.c:1766:2 [opt] frame #22: 0x028a739c monotouchtest`mono_jit_runtime_invoke(method=0x17534858, obj=<unavailable>, params=0x00000000, exc=<unavailable>, error=0x194998b8) at mini-runtime.c:3170:12 [opt] frame #23: 0x02951cfc monotouchtest`do_runtime_invoke(method=0x17534858, obj=0x0357c4f0, params=0x00000000, exc=0x00000000, error=0x194998b8) at object.c:3017:11 [opt] frame #24: 0x0294e6d4 monotouchtest`mono_runtime_invoke_checked(method=<unavailable>, obj=<unavailable>, params=<unavailable>, error=<unavailable>) at class-getters.h:24:1 [opt] [artificial] frame #25: 0x02955408 monotouchtest`mono_runtime_try_invoke_array(method=0x17534858, obj=0x0357c4f0, params=0x00000000, exc=0x00000000, error=0x194998b8) at object.c:5564:10 [opt] frame #26: 0x02905fac monotouchtest`ves_icall_InternalInvoke(method=<unavailable>, this_arg=<unavailable>, params=0x00000000, exc=0x19499f0c) at icall.c:3753:8 [opt] frame #27: 0x02a64788 monotouchtest`do_icall(frame=<unavailable>, sig=0x17496978, op=<unavailable>, sp=0x19499d88, ptr=<unavailable>, save_last_error=0) at interp.c:1982:20 [opt] frame #28: 0x02a632b8 monotouchtest`do_icall_wrapper(frame=0x19499fb0, sig=0x17496978, op=552, sp=0x19499da0, ptr=0x02905bec, save_last_error=0) at interp.c:2037:7 [opt] RuntimeMethodInfo::InternalInvoke @ 400702536 "calli.nat.fast" || frame #29: 0x02a51d5c monotouchtest`interp_exec_method_full(frame=0x19499fb0, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3229:9 [opt] RuntimeMethodInfo::Invoke @ 400701852 "call" || frame #30: 0x02a524e8 monotouchtest`interp_exec_method_full(frame=0x1949a110, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3400:4 [opt] MethodBase::Invoke @ 400700872 "callvirt.fast" || frame #31: 0x02a521cc monotouchtest`interp_exec_method_full(frame=0x1949a280, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3325:4 [opt] * [coop] relax a couple GC state assertions * [coop] update doc for enabling GC assertions * [coop] relax a GC assertion in release trampoline
2019-09-23 23:08:25 +03:00
MONO_ASSERT_GC_SAFE_OR_DETACHED;
2016-04-21 15:19:32 +03:00
id<XamarinExtendedObject> xself = self;
GCHandle rv = [xself xamarinGetGCHandle];
if (flags)
*flags = [xself xamarinGetFlags];
return rv;
2016-04-21 15:19:32 +03:00
}
static inline bool
set_gchandle (id self, GCHandle gc_handle, enum XamarinGCHandleFlags flags)
{
bool rv;
// COOP: we call a selector, and that must only be done in SAFE mode.
MONO_ASSERT_GC_UNSAFE;
MONO_ENTER_GC_SAFE;
id<XamarinExtendedObject> xself = self;
rv = [xself xamarinSetGCHandle: gc_handle flags: flags];
MONO_EXIT_GC_SAFE;
return rv;
}
static inline bool
set_gchandle_safe (id self, GCHandle gc_handle, enum XamarinGCHandleFlags flags)
{
bool rv;
// COOP: we call a selector, and that must only be done in SAFE mode.
MONO_ASSERT_GC_SAFE_OR_DETACHED;
id<XamarinExtendedObject> xself = self;
rv = [xself xamarinSetGCHandle: gc_handle flags: flags];
return rv;
}
static inline GCHandle
get_gchandle_without_flags (id self)
{
// COOP: we call a selector, and that must only be done in SAFE mode.
MONO_ASSERT_GC_UNSAFE;
GCHandle rv;
MONO_ENTER_GC_SAFE;
id<XamarinExtendedObject> xself = self;
rv = (GCHandle) [xself xamarinGetGCHandle];
MONO_EXIT_GC_SAFE;
return rv;
}
static inline GCHandle
get_gchandle_with_flags (id self, enum XamarinGCHandleFlags* flags)
2016-04-21 15:19:32 +03:00
{
// COOP: we call a selector, and that must only be done in SAFE mode.
MONO_ASSERT_GC_UNSAFE;
GCHandle rv;
MONO_ENTER_GC_SAFE;
2016-04-21 15:19:32 +03:00
id<XamarinExtendedObject> xself = self;
rv = (GCHandle) [xself xamarinGetGCHandle];
if (flags != NULL)
*flags = [xself xamarinGetFlags];
MONO_EXIT_GC_SAFE;
return rv;
2016-04-21 15:19:32 +03:00
}
static inline enum XamarinGCHandleFlags
get_flags (id self)
2016-04-21 15:19:32 +03:00
{
// COOP: we call a selector, and that must only be done in SAFE mode.
MONO_ASSERT_GC_UNSAFE;
enum XamarinGCHandleFlags rv;
MONO_ENTER_GC_SAFE;
id<XamarinExtendedObject> xself = self;
rv = [xself xamarinGetFlags];
MONO_EXIT_GC_SAFE;
return rv;
2016-04-21 15:19:32 +03:00
}
static inline void
set_flags_safe (id self, enum XamarinGCHandleFlags flags)
{
// COOP: we call a selector, and that must only be done in SAFE mode.
MONO_ASSERT_GC_SAFE_OR_DETACHED;
id<XamarinExtendedObject> xself = self;
[xself xamarinSetFlags: flags];
}
static inline enum XamarinGCHandleFlags
get_flags_safe (id self)
{
// COOP: we call a selector, and that must only be done in SAFE mode.
MONO_ASSERT_GC_SAFE_OR_DETACHED;
enum XamarinGCHandleFlags rv;
id<XamarinExtendedObject> xself = self;
rv = [xself xamarinGetFlags];
return rv;
}
GCHandle
2016-04-21 15:19:32 +03:00
xamarin_get_gchandle (id self)
{
// COOP: does not access managed memory: any mode
return get_gchandle_without_flags (self);
2016-04-21 15:19:32 +03:00
}
GCHandle
xamarin_get_gchandle_with_flags (id self, enum XamarinGCHandleFlags* flags)
2016-04-21 15:19:32 +03:00
{
// COOP: does not access managed memory: any mode
return get_gchandle_with_flags (self, flags);
2016-04-21 15:19:32 +03:00
}
bool
xamarin_has_managed_ref (id self)
{
// COOP: get_flags requires UNSAFE mode, so this function requires it too.
return (get_flags (self) & XamarinGCHandleFlags_HasManagedRef) == XamarinGCHandleFlags_HasManagedRef;
2016-04-21 15:19:32 +03:00
}
bool
xamarin_has_managed_ref_safe (id self)
{
// COOP: variation of xamarin_has_managed_ref for SAFE mode.
return (get_flags_safe (self) & XamarinGCHandleFlags_HasManagedRef) == XamarinGCHandleFlags_HasManagedRef;
}
2016-04-21 15:19:32 +03:00
MonoException *
xamarin_create_exception (const char *msg)
{
// COOP: calls mono, needs to be in UNSAFE mode.
MONO_ASSERT_GC_UNSAFE;
return xamarin_create_system_exception (msg);
2016-04-21 15:19:32 +03:00
}
MonoMethod *
xamarin_get_reflection_method_method (MonoReflectionMethod *method)
{
return xamarin_bridge_get_mono_method (method);
2016-04-21 15:19:32 +03:00
}
id
xamarin_get_handle (MonoObject *obj, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: Reads managed memory, needs to be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
MonoClass *klass;
id rv = nil;
if (obj == NULL)
return nil;
klass = mono_object_get_class (obj);
if (xamarin_is_class_nsobject (klass)) {
rv = xamarin_get_nsobject_handle (obj);
} else if (xamarin_is_class_inativeobject (klass)) {
rv = xamarin_get_handle_for_inativeobject (obj, exception_gchandle);
2016-04-21 15:19:32 +03:00
} else {
char *msg = xamarin_strdup_printf ("Unable to marshal from %s.%s to an Objective-C object. "
"The managed class must either inherit from NSObject or implement INativeObject.",
mono_class_get_namespace (klass), mono_class_get_name (klass));
GCHandle ex_handle = xamarin_create_runtime_exception (8039, msg, exception_gchandle);
2016-04-21 15:19:32 +03:00
xamarin_free (msg);
if (*exception_gchandle == INVALID_GCHANDLE)
*exception_gchandle = ex_handle;
2016-04-21 15:19:32 +03:00
}
xamarin_mono_object_release (&klass);
2016-04-21 15:19:32 +03:00
return rv;
}
#if DEBUG
static void
verify_cast (MonoClass *to, MonoObject *obj, Class from_class, SEL sel, MonoMethod *method, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: Reads managed memory, needs to be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
if (!to)
return;
if (mono_object_isinst (obj, to) == NULL) {
MonoClass *from = mono_object_get_class (obj);
char *method_full_name = mono_method_full_name (method, TRUE);
char *from_name = xamarin_class_get_full_name (from, exception_gchandle);
char *to_name = xamarin_class_get_full_name (to, exception_gchandle);
2016-04-21 15:19:32 +03:00
char *msg = xamarin_strdup_printf ("Unable to cast object of type '%s' (Objective-C type: '%s') to type '%s'.\n"
"Additional information:\n"
"\tSelector: %s\n"
"\tMethod: %s\n", from_name, class_getName(from_class), to_name, sel_getName (sel), method_full_name);
MonoException *mono_ex = xamarin_create_system_invalid_cast_exception (msg);
2016-04-21 15:19:32 +03:00
mono_free (from_name);
mono_free (to_name);
xamarin_free (msg);
mono_free (method_full_name);
xamarin_mono_object_release (&from);
*exception_gchandle = xamarin_gchandle_new ((MonoObject *) mono_ex, FALSE);
2016-04-21 15:19:32 +03:00
}
}
#endif
void
xamarin_check_for_gced_object (MonoObject *obj, SEL sel, id self, MonoMethod *method, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: Reads managed memory, needs to be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
if (obj != NULL) {
#if DEBUG
MonoClass *declaring_type = mono_method_get_class (method);
verify_cast (declaring_type, obj, [self class], sel, method, exception_gchandle);
xamarin_mono_object_release (&declaring_type);
2016-04-21 15:19:32 +03:00
#endif
return;
}
#if DOTNET
const char *m = "Failed to marshal the Objective-C object %p (type: %s). "
"Could not find an existing managed instance for this object, "
"nor was it possible to create a new managed instance "
"(because the type '%s' does not have a constructor that takes one NativeHandle argument).\n"
"Additional information:\n"
"\tSelector: %s\n"
"\tMethod: %s\n";
#else
2016-04-21 15:19:32 +03:00
const char *m = "Failed to marshal the Objective-C object %p (type: %s). "
"Could not find an existing managed instance for this object, "
"nor was it possible to create a new managed instance "
"(because the type '%s' does not have a constructor that takes one IntPtr argument).\n"
"Additional information:\n"
"\tSelector: %s\n"
"\tMethod: %s\n";
#endif
2016-04-21 15:19:32 +03:00
char *method_full_name = mono_method_full_name (method, TRUE);
char *type_name = xamarin_lookup_managed_type_name ([self class], exception_gchandle);
if (*exception_gchandle == INVALID_GCHANDLE) {
char *msg = xamarin_strdup_printf (m, self, object_getClassName (self), type_name, sel_getName (sel), method_full_name);
GCHandle ex_handle = xamarin_create_runtime_exception (8027, msg, exception_gchandle);
xamarin_free (msg);
if (*exception_gchandle == INVALID_GCHANDLE)
*exception_gchandle = ex_handle;
}
2016-04-21 15:19:32 +03:00
mono_free (type_name);
mono_free (method_full_name);
}
#if DEBUG
//
// We can't do the type-checks below correctly until we support multiple managed peers for each
// native objects. The problem is that Objective-C can fake multiple inheritance by
// overriding isKindOfClass: A test case can be seen in bug #23421: A parameter in a
// callback from Objective-C is typed as 'NSUrlSessionDownloadTask'. Objective-C may give us an instance
// of an internal class __NSCFBackgroundDownloadTask, which doesn't inherit from NSUrlSessionDownloadTask
// (it inherits from NSUrlSessionTask, which is a superclass of NSUrlSessionDownloadTask). In Objective-C
// the __NSCFBackgroundDownloadTask class gets away with this because it overrides isKindOfClass: to return
// YES for NSUrlSessionDownloadTask. We can't rely on isKindOfClass: when creating a managed peer for
// the native object, because we may already have an instance of the "correct" type (if we
// had support for multiple managed peers, we could just create a new instance of the expected type).
//
void
xamarin_verify_parameter (MonoObject *obj, SEL sel, id self, id arg, unsigned long index, MonoClass *expected, MonoMethod *method)
2016-04-21 15:19:32 +03:00
{
// COOP: Reads managed memory, needs to be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
// if (arg == NULL)
// return;
//
// if (obj != NULL) {
// verify_cast (expected, obj, [arg class], sel, method);
// return;
// }
//
// const char *m = "Failed to marshal the Objective-C object 0x%x (type: %s). "
// "Could not find an existing managed instance for this object, "
// "nor was it possible to create a new managed instance "
// "(because the type '%s' does not have a constructor that takes one IntPtr argument).\n"
// "Additional information:\n"
// "\tSelector: %s\n"
// "\tMethod: %s\n"
// "\tParameter: %i\n";
//
// char *method_full_name = mono_method_full_name (method, TRUE);
// char *type_name = xamarin_lookup_managed_type_name ([arg class]);
// char *msg = xamarin_strdup_printf (m, arg, object_getClassName (arg), type_name, sel, method_full_name, index);
// MonoException *mex = xamarin_create_exception (msg);
// xamarin_free (msg);
// mono_free (method_full_name);
// mono_free (type_name);
// mono_raise_exception (mex);
}
void
xamarin_check_objc_type (id obj, Class expected_class, SEL sel, id self, int index, MonoMethod *method)
{
// COOP: Reads managed memory, needs to be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
// if ([obj isKindOfClass:expected_class])
// return;
//
// const char *m = "Failed to marshal the Objective-C object 0x%x (type: %s), expected an object of type %s.\n"
// "Additional information:\n"
// "\tSelector: %s\n"
// "\tMethod: %s\n"
// "\tParameter: %i\n";
//
// char *method_full_name = mono_method_full_name (method, TRUE);
// char *msg = xamarin_strdup_printf (m, obj, object_getClassName (obj), class_getName (expected_class), sel, method_full_name, index);
// MonoException *mono_ex = xamarin_create_system_invalid_cast_exception (msg);
2016-04-21 15:19:32 +03:00
// xamarin_free (msg);
// mono_free (method_full_name);
// mono_raise_exception (mono_ex);
}
#endif
char *
xamarin_class_get_full_name (MonoClass *klass, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: Reads managed memory, needs to be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
MonoType *type = mono_class_get_type (klass);
char * rv = xamarin_type_get_full_name (type, exception_gchandle);
xamarin_mono_object_release (&type);
return rv;
2016-04-21 15:19:32 +03:00
}
char *
xamarin_type_get_full_name (MonoType *type, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: Reads managed memory, needs to be in UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
MonoReflectionType *rtype = mono_type_get_object (mono_domain_get (), type);
char *rv = xamarin_reflection_type_get_full_name (rtype, exception_gchandle);
xamarin_mono_object_release (&rtype);
return rv;
2016-04-21 15:19:32 +03:00
}
/*
* ToggleRef support
*/
// #define DEBUG_TOGGLEREF 1
MonoToggleRefStatus
xamarin_gc_toggleref_callback (uint8_t flags, id handle, xamarin_get_handle_func get_handle, MonoObject *info)
2016-04-21 15:19:32 +03:00
{
// COOP: this is a callback called by the GC, so I assume the mode here doesn't matter
2016-04-21 15:19:32 +03:00
MonoToggleRefStatus res;
bool disposed = (flags & NSObjectFlagsDisposed) == NSObjectFlagsDisposed;
bool has_managed_ref = (flags & NSObjectFlagsHasManagedRef) == NSObjectFlagsHasManagedRef;
if (disposed || !has_managed_ref) {
res = MONO_TOGGLE_REF_DROP; /* Already disposed, we don't need the managed object around */
} else {
if (handle == NULL)
handle = get_handle (info);
2016-04-21 15:19:32 +03:00
if (handle == NULL) { /* This shouldn't really happen */
res = MONO_TOGGLE_REF_DROP;
2016-04-21 15:19:32 +03:00
} else {
if ([handle retainCount] == 1)
res = MONO_TOGGLE_REF_WEAK;
else
res = MONO_TOGGLE_REF_STRONG;
}
}
#ifdef DEBUG_TOGGLEREF
const char *rv;
if (res == MONO_TOGGLE_REF_DROP) {
rv = "DROP";
} else if (res == MONO_TOGGLE_REF_STRONG) {
rv = "STRONG";
} else if (res == MONO_TOGGLE_REF_WEAK) {
rv = "WEAK";
} else {
rv = "UNKNOWN";
}
const char *cn = NULL;
if (handle == NULL)
handle = get_handle (info);
cn = object_getClassName (handle);
PRINT ("\tinspecting %p handle:%p %s flags: %i RC %d -> %s\n", object, handle, cn, (int) flags, (int) (handle ? [handle retainCount] : 0), rv);
2016-04-21 15:19:32 +03:00
#endif
return res;
}
void
xamarin_gc_event (MonoGCEvent event)
2016-04-21 15:19:32 +03:00
{
// COOP: this is a callback called by the GC, I believe the mode here doesn't matter.
2016-04-21 15:19:32 +03:00
switch (event) {
case MONO_GC_EVENT_PRE_STOP_WORLD:
pthread_mutex_lock (&framework_peer_release_lock);
break;
case MONO_GC_EVENT_POST_START_WORLD:
pthread_mutex_unlock (&framework_peer_release_lock);
break;
default: // silences a compiler warning.
break;
}
}
#if !defined (CORECLR_RUNTIME)
[runtime] Use a single mono profiler for both newrefcount and NSAutoreleasePool thread hooks. (#5495) Additionally don't use malloc'ed memory, to avoid having to free the memory. This fixes a leak, since mono does not free the MonoProfiler argument: STACK OF 1 INSTANCE OF 'ROOT LEAK: <0x7fb94495c460>': [thread 0x10f3f85c0]: 19 libdyld.dylib 0x7fff6e009ed9 start + 1 18 NameNotImportant 0x10592dec4 main + 36 launcher.m:679 17 NameNotImportant 0x10592d039 xamarin_main + 1305 launcher.m:661 16 NameNotImportant 0x105973473 mono_main + 11731 driver.g.c:2484 15 NameNotImportant 0x10597020d mono_jit_exec + 349 driver.g.c:1236 14 NameNotImportant 0x105b4217e mono_runtime_exec_main_checked + 110 object.c:0 13 NameNotImportant 0x105b3af28 mono_runtime_invoke_checked + 136 object.c:2960 12 NameNotImportant 0x105a143a1 mono_jit_runtime_invoke + 513 mini-runtime.c:3011 11 NameNotImportant 0x105a103c1 mono_jit_compile_method_with_opt + 2577 mini-runtime.c:2411 10 NameNotImportant 0x105a21aa7 mono_jit_compile_method_inner + 1207 mini.c:4184 9 NameNotImportant 0x105b3b75f mono_runtime_class_init_full + 847 object.c:527 8 NameNotImportant 0x105b3c9d4 mono_runtime_try_invoke + 148 object.c:2960 7 NameNotImportant 0x105a147d3 mono_jit_runtime_invoke + 1587 mini-runtime.c:3148 6 ??? 0x106d00fb0 0x7fffffffffffffff + 9223372041264041905 5 NameNotImportant 0x105921b0c xamarin_initialize + 812 runtime.m:1412 4 NameNotImportant 0x10591e7e7 xamarin_install_nsautoreleasepool_hooks + 55 shared.m:241 3 NameNotImportant 0x105b4fd0a mono_profiler_install + 26 profiler.c:1019 2 NameNotImportant 0x105c3127b monoeg_malloc0 + 27 gmem.c:121 1 libsystem_malloc.dylib 0x7fff6e1bacba calloc + 30 0 libsystem_malloc.dylib 0x7fff6e1bad62 malloc_zone_calloc + 139 Also remove the call to mono_profiler_set_events, it doesn't do anything anymore ([1]). [1]: https://github.com/mono/mono/blob/14b061ba65815d4580078e93c4f2df77ec84b882/mono/metadata/profiler.c#L1098-L1101
2019-01-28 17:06:40 +03:00
struct _MonoProfiler {
int dummy;
};
static void
xamarin_install_mono_profiler ()
{
static _MonoProfiler profiler = { 0 };
// This must be done before any other mono_profiler_install_* functions are called
// (currently xamarin_enable_new_refcount and xamarin_install_nsautoreleasepool_hooks).
[runtime] Use a single mono profiler for both newrefcount and NSAutoreleasePool thread hooks. (#5495) Additionally don't use malloc'ed memory, to avoid having to free the memory. This fixes a leak, since mono does not free the MonoProfiler argument: STACK OF 1 INSTANCE OF 'ROOT LEAK: <0x7fb94495c460>': [thread 0x10f3f85c0]: 19 libdyld.dylib 0x7fff6e009ed9 start + 1 18 NameNotImportant 0x10592dec4 main + 36 launcher.m:679 17 NameNotImportant 0x10592d039 xamarin_main + 1305 launcher.m:661 16 NameNotImportant 0x105973473 mono_main + 11731 driver.g.c:2484 15 NameNotImportant 0x10597020d mono_jit_exec + 349 driver.g.c:1236 14 NameNotImportant 0x105b4217e mono_runtime_exec_main_checked + 110 object.c:0 13 NameNotImportant 0x105b3af28 mono_runtime_invoke_checked + 136 object.c:2960 12 NameNotImportant 0x105a143a1 mono_jit_runtime_invoke + 513 mini-runtime.c:3011 11 NameNotImportant 0x105a103c1 mono_jit_compile_method_with_opt + 2577 mini-runtime.c:2411 10 NameNotImportant 0x105a21aa7 mono_jit_compile_method_inner + 1207 mini.c:4184 9 NameNotImportant 0x105b3b75f mono_runtime_class_init_full + 847 object.c:527 8 NameNotImportant 0x105b3c9d4 mono_runtime_try_invoke + 148 object.c:2960 7 NameNotImportant 0x105a147d3 mono_jit_runtime_invoke + 1587 mini-runtime.c:3148 6 ??? 0x106d00fb0 0x7fffffffffffffff + 9223372041264041905 5 NameNotImportant 0x105921b0c xamarin_initialize + 812 runtime.m:1412 4 NameNotImportant 0x10591e7e7 xamarin_install_nsautoreleasepool_hooks + 55 shared.m:241 3 NameNotImportant 0x105b4fd0a mono_profiler_install + 26 profiler.c:1019 2 NameNotImportant 0x105c3127b monoeg_malloc0 + 27 gmem.c:121 1 libsystem_malloc.dylib 0x7fff6e1bacba calloc + 30 0 libsystem_malloc.dylib 0x7fff6e1bad62 malloc_zone_calloc + 139 Also remove the call to mono_profiler_set_events, it doesn't do anything anymore ([1]). [1]: https://github.com/mono/mono/blob/14b061ba65815d4580078e93c4f2df77ec84b882/mono/metadata/profiler.c#L1098-L1101
2019-01-28 17:06:40 +03:00
mono_profiler_install (&profiler, NULL);
}
#endif
[runtime] Use a single mono profiler for both newrefcount and NSAutoreleasePool thread hooks. (#5495) Additionally don't use malloc'ed memory, to avoid having to free the memory. This fixes a leak, since mono does not free the MonoProfiler argument: STACK OF 1 INSTANCE OF 'ROOT LEAK: <0x7fb94495c460>': [thread 0x10f3f85c0]: 19 libdyld.dylib 0x7fff6e009ed9 start + 1 18 NameNotImportant 0x10592dec4 main + 36 launcher.m:679 17 NameNotImportant 0x10592d039 xamarin_main + 1305 launcher.m:661 16 NameNotImportant 0x105973473 mono_main + 11731 driver.g.c:2484 15 NameNotImportant 0x10597020d mono_jit_exec + 349 driver.g.c:1236 14 NameNotImportant 0x105b4217e mono_runtime_exec_main_checked + 110 object.c:0 13 NameNotImportant 0x105b3af28 mono_runtime_invoke_checked + 136 object.c:2960 12 NameNotImportant 0x105a143a1 mono_jit_runtime_invoke + 513 mini-runtime.c:3011 11 NameNotImportant 0x105a103c1 mono_jit_compile_method_with_opt + 2577 mini-runtime.c:2411 10 NameNotImportant 0x105a21aa7 mono_jit_compile_method_inner + 1207 mini.c:4184 9 NameNotImportant 0x105b3b75f mono_runtime_class_init_full + 847 object.c:527 8 NameNotImportant 0x105b3c9d4 mono_runtime_try_invoke + 148 object.c:2960 7 NameNotImportant 0x105a147d3 mono_jit_runtime_invoke + 1587 mini-runtime.c:3148 6 ??? 0x106d00fb0 0x7fffffffffffffff + 9223372041264041905 5 NameNotImportant 0x105921b0c xamarin_initialize + 812 runtime.m:1412 4 NameNotImportant 0x10591e7e7 xamarin_install_nsautoreleasepool_hooks + 55 shared.m:241 3 NameNotImportant 0x105b4fd0a mono_profiler_install + 26 profiler.c:1019 2 NameNotImportant 0x105c3127b monoeg_malloc0 + 27 gmem.c:121 1 libsystem_malloc.dylib 0x7fff6e1bacba calloc + 30 0 libsystem_malloc.dylib 0x7fff6e1bad62 malloc_zone_calloc + 139 Also remove the call to mono_profiler_set_events, it doesn't do anything anymore ([1]). [1]: https://github.com/mono/mono/blob/14b061ba65815d4580078e93c4f2df77ec84b882/mono/metadata/profiler.c#L1098-L1101
2019-01-28 17:06:40 +03:00
2016-04-21 15:19:32 +03:00
bool
xamarin_file_exists (const char *path)
{
// COOP: no managed access: any mode
2016-04-21 15:19:32 +03:00
struct stat buffer;
return stat (path, &buffer) == 0;
}
// Returns a retained MonoObject. Caller must release.
MonoAssembly *
xamarin_open_assembly (const char *name)
2016-04-21 15:19:32 +03:00
{
// COOP: this is a function executed only at startup, I believe the mode here doesn't matter.
2016-04-21 15:19:32 +03:00
char path [1024];
MonoAssembly *assembly;
bool exists = false;
#if MONOMAC
if (xamarin_get_is_mkbundle ()) {
assembly = mono_assembly_open (name, NULL);
if (assembly == NULL)
xamarin_assertion_message ("Could not find the required assembly '%s' in the app. This is usually fixed by cleaning and rebuilding your project; if that doesn't work, please file a bug report: https://github.com/xamarin/xamarin-macios/issues/new", name);
2016-04-21 15:19:32 +03:00
return assembly;
}
#endif
exists = xamarin_locate_assembly_resource (name, NULL, name, path, sizeof (path));
2016-04-21 15:19:32 +03:00
#if MONOMAC && DYLIB
if (!exists) {
2016-04-21 15:19:32 +03:00
// Check if we already have the assembly in memory
xamarin_get_assembly_name_without_extension (name, path, sizeof (path));
MonoAssemblyName *aname = mono_assembly_name_new (path);
2016-04-21 15:19:32 +03:00
assembly = mono_assembly_loaded (aname);
mono_assembly_name_free (aname);
if (assembly)
return assembly;
xamarin_assertion_message ("Could not find the assembly '%s' in the app nor as an already loaded assembly. This is usually fixed by cleaning and rebuilding your project; if that doesn't work, please file a bug report: https://github.com/xamarin/xamarin-macios/issues/new", name);
2016-04-21 15:19:32 +03:00
}
#endif
if (!exists)
xamarin_assertion_message ("Could not find the assembly '%s' in the app. This is usually fixed by cleaning and rebuilding your project; if that doesn't work, please file a bug report: https://github.com/xamarin/xamarin-macios/issues/new", name);
2016-04-21 15:19:32 +03:00
assembly = mono_assembly_open (path, NULL);
if (assembly == NULL)
xamarin_assertion_message ("Could not find the required assembly '%s' in the app. This is usually fixed by cleaning and rebuilding your project; if that doesn't work, please file a bug report: https://github.com/xamarin/xamarin-macios/issues/new", name);
2016-04-21 15:19:32 +03:00
return assembly;
}
bool
xamarin_register_monoassembly (MonoAssembly *assembly, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: this is a function executed only at startup, I believe the mode here doesn't matter.
if (!xamarin_supports_dynamic_registration) {
#if defined (CORECLR_RUNTIME)
if (xamarin_log_level > 0) {
MonoReflectionAssembly *rassembly = mono_assembly_get_object (mono_domain_get (), assembly);
GCHandle assembly_gchandle = xamarin_gchandle_new ((MonoObject *) rassembly, false);
xamarin_mono_object_release (&rassembly);
char *assembly_name = xamarin_bridge_get_assembly_name (assembly_gchandle);
xamarin_gchandle_free (assembly_gchandle);
LOG (PRODUCT ": Skipping assembly registration for %s since it's not needed (dynamic registration is not supported)", assembly_name);
mono_free (assembly_name);
}
#else
LOG (PRODUCT ": Skipping assembly registration for %s since it's not needed (dynamic registration is not supported)", mono_assembly_name_get_name (mono_assembly_get_name (assembly)));
#endif
return true;
}
MonoReflectionAssembly *rassembly = mono_assembly_get_object (mono_domain_get (), assembly);
xamarin_register_assembly (rassembly, exception_gchandle);
xamarin_mono_object_release (&rassembly);
return *exception_gchandle == INVALID_GCHANDLE;
2016-04-21 15:19:32 +03:00
}
// Returns a retained MonoObject. Caller must release.
2016-04-21 15:19:32 +03:00
MonoAssembly *
xamarin_open_and_register (const char *aname, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: this is a function executed only at startup, I believe the mode here doesn't matter.
2016-04-21 15:19:32 +03:00
MonoAssembly *assembly;
assembly = xamarin_open_assembly (aname);
2016-04-21 15:19:32 +03:00
xamarin_register_monoassembly (assembly, exception_gchandle);
2016-04-21 15:19:32 +03:00
return assembly;
}
#if !defined (CORECLR_RUNTIME)
2016-04-21 15:19:32 +03:00
static gboolean
is_class_finalization_aware (MonoClass *cls)
{
// COOP: This is a callback called by the GC, I believe the mode here doesn't matter.
2016-04-21 15:19:32 +03:00
gboolean rv = false;
MonoClass *nsobject_class = xamarin_get_nsobject_class ();
2016-04-21 15:19:32 +03:00
if (nsobject_class)
rv = cls == nsobject_class || mono_class_is_assignable_from (nsobject_class, cls);
//PRINT ("IsClass %s.%s finalization aware: %i\n", mono_class_get_namespace (cls), mono_class_get_name (cls), rv);
2016-04-21 15:19:32 +03:00
return rv;
}
static void
object_queued_for_finalization (MonoObject *object)
{
// COOP: Although this is reading managed memory, it is a callback called by the GC, so I believe the mode here doesn't matter.
2016-04-21 15:19:32 +03:00
/* This is called with the GC lock held, so it can only use signal-safe code */
struct Managed_NSObject *obj = (struct Managed_NSObject *) object;
//PRINT ("In finalization response for %s.%s %p (handle: %p class_handle: %p flags: %i)\n",
2016-04-21 15:19:32 +03:00
obj->flags |= NSObjectFlagsInFinalizerQueue;
}
#endif // !defined (CORECLR_RUNTIME)
2016-04-21 15:19:32 +03:00
/*
* Registration map
*/
[Class] Sort our array of Class -> token references so that we can do binary instead of linear searches in it. (#5009) Our type map has two sections: first come all the wrapper types, then all the custom types. This means we need to sort these two sections separately, since code elsewhere depends on this split in order to determine if a type is a custom type or not. We also need a minor modification in the array of skipped types, since it contained indexes into the type map, which won't be valid after is has been sorted. Instead store a type reference for the actual type in the array, and use that to search the type map for the corresponding Class. This is a little bit slower, but the results are cached in a dictionary, so it'll only happen once for each type. The performance is slightly slower when the type map has very few entries, but that is repaid many times over when the number of entries go up. Numbers ======= Test case: https://github.com/rolfbjarne/TestApp/commit/004283d7b628a29fcf711d98d8842bfd4ef4393b iPad Air 2 ---------- | Configuration | Before | After | Improvement | | ------------------- | ------ | ------ | ------------: | | Release (link all) | 477 ms | 481 ms | -4 ms (-0,8%) | | Release (dont link) | 738 ms | 656 ms | 82 ms (11%) | iPhone X -------- | Configuration | Before | After | Improvement | | ------------------- | ------ | ------ | ----------: | | Release (link all) | 98 ms | 99 ms | -1 ms (-1%) | | Release (dont link) | 197 ms | 153 ms | 44 ms (22%) | When linking all assemblies, the type map has 24 entries, and when not linking at all it has 2993 entries. This is part 1 of multiple fixes for #4936.
2018-10-19 08:33:15 +03:00
static int
compare_mtclassmap (const void *a, const void *b)
{
MTClassMap *mapa = (MTClassMap *) a;
MTClassMap *mapb = (MTClassMap *) b;
intptr_t diff = (intptr_t)mapa->handle - (intptr_t)mapb->handle;
const int shift = (sizeof(intptr_t) * 8) - 1;
return (diff >> shift) | !!diff;
[Class] Sort our array of Class -> token references so that we can do binary instead of linear searches in it. (#5009) Our type map has two sections: first come all the wrapper types, then all the custom types. This means we need to sort these two sections separately, since code elsewhere depends on this split in order to determine if a type is a custom type or not. We also need a minor modification in the array of skipped types, since it contained indexes into the type map, which won't be valid after is has been sorted. Instead store a type reference for the actual type in the array, and use that to search the type map for the corresponding Class. This is a little bit slower, but the results are cached in a dictionary, so it'll only happen once for each type. The performance is slightly slower when the type map has very few entries, but that is repaid many times over when the number of entries go up. Numbers ======= Test case: https://github.com/rolfbjarne/TestApp/commit/004283d7b628a29fcf711d98d8842bfd4ef4393b iPad Air 2 ---------- | Configuration | Before | After | Improvement | | ------------------- | ------ | ------ | ------------: | | Release (link all) | 477 ms | 481 ms | -4 ms (-0,8%) | | Release (dont link) | 738 ms | 656 ms | 82 ms (11%) | iPhone X -------- | Configuration | Before | After | Improvement | | ------------------- | ------ | ------ | ----------: | | Release (link all) | 98 ms | 99 ms | -1 ms (-1%) | | Release (dont link) | 197 ms | 153 ms | 44 ms (22%) | When linking all assemblies, the type map has 24 entries, and when not linking at all it has 2993 entries. This is part 1 of multiple fixes for #4936.
2018-10-19 08:33:15 +03:00
}
2016-04-21 15:19:32 +03:00
void
Add a UserType flag for registered types, and use it to improve the performance for is_user_type. (#5017) * Refactor type map to have a separate flag field for each type instead of magic location in the array. Refactor the type map to have a separate flag field for each type instead of a magic location in the array. This simplifies some code, but also allows us to introduce more flags in the future (which becomes increasingly complex if the location in the array is to determine yet another value). This has neglible performance impacts. * Add a UserType flag for registered types, and use it to improve the performance for is_user_type. Reflection in the Objective-C runtime is apparently quite slow, so try to avoid it by computing the information we need for determining whether a particular Objective-C type represents a user type or not in the static registrar. We store this information in a flag for the type in question in the type map, and use a binary search to search the type map when needed. This provides a significant improvement, in particular in the dontlink scenario (probably because there are many more Objective-C types in the app, which made Objective-C reflection slow). In fact, it somehow made the dontlink scenario so fast that it's faster than the linkall scenario (which also improved, but not nearly as much). While quite inexplicable, it's a consistent result I've seen over multiple test runs. Numbers ======= Test case: https://github.com/rolfbjarne/TestApp/commit/004283d7b628a29fcf711d98d8842bfd4ef4393b Fix 1 refers to PR #5009. Fix 2 refers to PR #5013. Fix 3 refers to PR #5016. Fix 4 is this fix. iPad Air 2 ---------- | Configuration | Before | After fix 1 | After fix 2 | After fix 3 | After fix 4 | Improvement from fix 3 to fix 4 | Cumulative improvement | | ------------------- | ------ | ----------: | -----------: | -----------: | -----------: | ------------------------------: | ---------------------: | | Release (link all) | 477 ms | 481 ms | 224 ms | 172 ms | 148 ms | 24 ms (14%) | 329 ms (69%) | | Release (dont link) | 738 ms | 656 ms | 377 ms | 201 ms | 146 ms | 55 ms (27%) | 592 ms (80%) | iPhone X -------- | Configuration | Before | After fix 1 | After fix 2 | After fix 3 | After fix 4 | Improvement from fix 3 to fix 4 | Cumulative improvement | | ------------------- | ------ | ----------: | -----------: | -----------: | -----------: | ------------------------------: | ---------------------: | | Release (link all) | 98 ms | 99 ms | 42 ms | 31 ms | 29 ms | 2 ms ( 6%) | 69 ms (70%) | | Release (dont link) | 197 ms | 153 ms | 91 ms | 43 ms | 28 ms | 15 ms (35%) | 169 ms (86%) | When linking all assemblies, the type map has 24 entries, and when not linking at all it has 2993 entries. This is part 4 (the last) of multiple fixes for #4936. The total speed-up is 69-86% (3-7x faster).
2018-10-22 08:57:16 +03:00
xamarin_add_registration_map (struct MTRegistrationMap *map, bool partial)
2016-04-21 15:19:32 +03:00
{
// COOP: no managed memory access: any mode
[registrar] Use metadata tokens instead of strings to find types and methods. (#1085) Use metadata tokens instead of strings to find types and methods. This makes the code to find methods more compact (a lot less strings in the executable, and additionally in most cases a compact representation (32-bit integer) of the corresponding metadata token and additional information can be used, which results in less executable code (fewer parameters to methods, etc)), resulting in smaller executables. Size savings are around 200kb for dont link apps, and 20-60kb for linked apps (this obviously varies a lot depending on how much has to registered by the registrar). | | Before | After | Diff | |----------------|--------------:|--------------:|------------------:| | dontlink/32bit | 102.810.144 | 102.609.456 | -200.688 = -0,20% | | dontlink/64bit | 107.420.576 | 107.221.792 | -198.784 = -0,19% | | linksdk/32bit | 40.957.296 | 40.936.864 | -20.432 = -0,05% | | linksdk/64bit | 43.113.136 | 43.093.936 | -19.200 = -0,04% | | linkall/32bit | 38.410.032 | 38.348.288 |  -61.744 = -0,16% | | linkall/64bit | 40.315.200 | 40.267.344 | -47.856 = -0,12% | Additionally I've removed the `lazy_map` dictionary, which we populated at startup and was used to map between Class instances and the corresponding managed type's FullName, and instead iterate over a native array of Class -> metadata token mappings whenever we need to look up the managed type for a certain Class instance. This is slightly slower for each type we need to look up (for a non-linked app there might be a 2000-3000 entries in the native array, which would be iterated instead of using a hashtable lookup), but it's only done once per type and there's a significant startup memory improvement. For a non-linked test app I get the following using the Xamarin profiler: | | Before | After | Diff | |-------------------|--------:|--------:|----------------:| | Memory allocated | 2,8 MB | 2,4 MB | -0,4 MB = -14 % | | Objects allocated | 43678 | 38463 | -5215 = -12 % | | Private bytes | 26,6 MB | 24,4 MB | -2,2 MB = -8,3% | | Working set | 26,6 MB | 24,4 MB | -2,2 MB = -8,3% |
2016-11-01 21:34:56 +03:00
options.RegistrationData = map;
Add a UserType flag for registered types, and use it to improve the performance for is_user_type. (#5017) * Refactor type map to have a separate flag field for each type instead of magic location in the array. Refactor the type map to have a separate flag field for each type instead of a magic location in the array. This simplifies some code, but also allows us to introduce more flags in the future (which becomes increasingly complex if the location in the array is to determine yet another value). This has neglible performance impacts. * Add a UserType flag for registered types, and use it to improve the performance for is_user_type. Reflection in the Objective-C runtime is apparently quite slow, so try to avoid it by computing the information we need for determining whether a particular Objective-C type represents a user type or not in the static registrar. We store this information in a flag for the type in question in the type map, and use a binary search to search the type map when needed. This provides a significant improvement, in particular in the dontlink scenario (probably because there are many more Objective-C types in the app, which made Objective-C reflection slow). In fact, it somehow made the dontlink scenario so fast that it's faster than the linkall scenario (which also improved, but not nearly as much). While quite inexplicable, it's a consistent result I've seen over multiple test runs. Numbers ======= Test case: https://github.com/rolfbjarne/TestApp/commit/004283d7b628a29fcf711d98d8842bfd4ef4393b Fix 1 refers to PR #5009. Fix 2 refers to PR #5013. Fix 3 refers to PR #5016. Fix 4 is this fix. iPad Air 2 ---------- | Configuration | Before | After fix 1 | After fix 2 | After fix 3 | After fix 4 | Improvement from fix 3 to fix 4 | Cumulative improvement | | ------------------- | ------ | ----------: | -----------: | -----------: | -----------: | ------------------------------: | ---------------------: | | Release (link all) | 477 ms | 481 ms | 224 ms | 172 ms | 148 ms | 24 ms (14%) | 329 ms (69%) | | Release (dont link) | 738 ms | 656 ms | 377 ms | 201 ms | 146 ms | 55 ms (27%) | 592 ms (80%) | iPhone X -------- | Configuration | Before | After fix 1 | After fix 2 | After fix 3 | After fix 4 | Improvement from fix 3 to fix 4 | Cumulative improvement | | ------------------- | ------ | ----------: | -----------: | -----------: | -----------: | ------------------------------: | ---------------------: | | Release (link all) | 98 ms | 99 ms | 42 ms | 31 ms | 29 ms | 2 ms ( 6%) | 69 ms (70%) | | Release (dont link) | 197 ms | 153 ms | 91 ms | 43 ms | 28 ms | 15 ms (35%) | 169 ms (86%) | When linking all assemblies, the type map has 24 entries, and when not linking at all it has 2993 entries. This is part 4 (the last) of multiple fixes for #4936. The total speed-up is 69-86% (3-7x faster).
2018-10-22 08:57:16 +03:00
if (partial)
options.flags = (InitializationFlags) (options.flags | InitializationFlagsIsPartialStaticRegistrar);
[Class] Sort our array of Class -> token references so that we can do binary instead of linear searches in it. (#5009) Our type map has two sections: first come all the wrapper types, then all the custom types. This means we need to sort these two sections separately, since code elsewhere depends on this split in order to determine if a type is a custom type or not. We also need a minor modification in the array of skipped types, since it contained indexes into the type map, which won't be valid after is has been sorted. Instead store a type reference for the actual type in the array, and use that to search the type map for the corresponding Class. This is a little bit slower, but the results are cached in a dictionary, so it'll only happen once for each type. The performance is slightly slower when the type map has very few entries, but that is repaid many times over when the number of entries go up. Numbers ======= Test case: https://github.com/rolfbjarne/TestApp/commit/004283d7b628a29fcf711d98d8842bfd4ef4393b iPad Air 2 ---------- | Configuration | Before | After | Improvement | | ------------------- | ------ | ------ | ------------: | | Release (link all) | 477 ms | 481 ms | -4 ms (-0,8%) | | Release (dont link) | 738 ms | 656 ms | 82 ms (11%) | iPhone X -------- | Configuration | Before | After | Improvement | | ------------------- | ------ | ------ | ----------: | | Release (link all) | 98 ms | 99 ms | -1 ms (-1%) | | Release (dont link) | 197 ms | 153 ms | 44 ms (22%) | When linking all assemblies, the type map has 24 entries, and when not linking at all it has 2993 entries. This is part 1 of multiple fixes for #4936.
2018-10-19 08:33:15 +03:00
// Sort the type map according to Class
qsort (map->map, (size_t) map->map_count, sizeof (MTClassMap), compare_mtclassmap);
2016-04-21 15:19:32 +03:00
}
/*
* Exception handling
*/
NSString *
xamarin_print_all_exceptions (GCHandle gchandle)
2016-04-21 15:19:32 +03:00
{
GCHandle exception_gchandle = INVALID_GCHANDLE;
2016-04-21 15:19:32 +03:00
char *msg = xamarin_print_all_exceptions_wrapper (gchandle, &exception_gchandle);
if (exception_gchandle != INVALID_GCHANDLE) {
// Not much we can do here but returning a very generic message, since we failed to print one exception, it's reasonable to assume that printing another won't work either.
xamarin_gchandle_free (exception_gchandle);
exception_gchandle = INVALID_GCHANDLE;
return [NSString stringWithFormat: @"An exception occurred while trying to get a string representation for the exception %p (%p)", gchandle, exception_gchandle];
}
NSString *rv = [NSString stringWithUTF8String: msg];
xamarin_free (msg);
return rv;
}
void
xamarin_ftnptr_exception_handler (GCHandle gchandle)
{
xamarin_process_managed_exception_gchandle (gchandle);
}
// Because this function won't always return, it will take ownership of the GCHandle and free it.
void
xamarin_process_managed_exception_gchandle (GCHandle gchandle)
{
if (gchandle == INVALID_GCHANDLE)
return;
MonoObject *exc = xamarin_gchandle_get_target (gchandle);
xamarin_gchandle_free (gchandle);
xamarin_process_managed_exception (exc);
}
void
xamarin_unhandled_exception_handler (MonoObject *exc, gpointer user_data)
{
GCHandle exception_gchandle = xamarin_gchandle_new (exc, false);
PRINT ("Unhandled managed exception: %@", xamarin_print_all_exceptions (exception_gchandle));
xamarin_gchandle_free (exception_gchandle);
2016-04-21 15:19:32 +03:00
abort ();
}
static void
exception_handler (NSException *exc)
{
// COOP: We won't get here in coop-mode, because we don't set the uncaught objc exception handler in that case.
2016-04-21 15:19:32 +03:00
LOG (PRODUCT ": Received unhandled ObjectiveC exception: %@ %@", [exc name], [exc reason]);
if (xamarin_is_gc_coop) {
PRINT ("Uncaught Objective-C exception: %@", exc);
assert (false); // Re-throwing the Objective-C exception will probably just end up with infinite recursion
}
2016-04-21 15:19:32 +03:00
xamarin_throw_ns_exception (exc);
}
#if defined (DEBUG)
static void *
pump_gc (void *context)
{
// COOP: this runs on a separate thread, so I'm not sure what happens here.
// We can make sure we're in safe mode while sleeping though.
#if !defined (CORECLR_RUNTIME)
2016-04-21 15:19:32 +03:00
mono_thread_attach (mono_get_root_domain ());
#endif
2016-04-21 15:19:32 +03:00
while (xamarin_gc_pump) {
GCHandle exception_gchandle = INVALID_GCHANDLE;
xamarin_gc_collect (&exception_gchandle);
xamarin_process_managed_exception_gchandle (exception_gchandle);
MONO_ENTER_GC_SAFE;
2016-04-21 15:19:32 +03:00
usleep (1000000);
MONO_EXIT_GC_SAFE;
2016-04-21 15:19:32 +03:00
}
return NULL;
}
#endif /* DEBUG */
#if !defined (CORECLR_RUNTIME)
2016-04-21 15:19:32 +03:00
static void
log_callback (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data)
{
// COOP: Not accessing managed memory: any mode
PRINT ("%s: %s", log_level, message);
2016-04-21 15:19:32 +03:00
if (fatal)
abort ();
}
static void
print_callback (const char *string, mono_bool is_stdout)
{
// COOP: Not accessing managed memory: any mode
PRINT ("%s", string);
2016-04-21 15:19:32 +03:00
}
#endif // !defined (CORECLR_RUNTIME)
2016-04-21 15:19:32 +03:00
static int
xamarin_compare_ints (const void *a, const void *b)
{
uint32_t x = *(uint32_t *) a;
uint32_t y = *(uint32_t *) b;
return x < y ? -1 : (x == y ? 0 : 1);
}
uint32_t
xamarin_find_protocol_wrapper_type (uint32_t token_ref)
{
if (options.RegistrationData == NULL || options.RegistrationData->protocol_wrappers == NULL)
return INVALID_TOKEN_REF;
void* ptr = bsearch (&token_ref, options.RegistrationData->protocol_wrappers, (size_t) options.RegistrationData->protocol_wrapper_count, sizeof (MTProtocolWrapperMap), xamarin_compare_ints);
if (ptr == NULL)
return INVALID_TOKEN_REF;
MTProtocolWrapperMap *entry = (MTProtocolWrapperMap *) ptr;
return entry->wrapper_token;
}
void
xamarin_initialize_embedded ()
{
static bool initialized = false;
if (initialized)
return;
initialized = true;
char *argv[] = { NULL };
char *libname = NULL;
Dl_info info;
if (dladdr ((void *) xamarin_initialize_embedded, &info) != 0) {
const char *last_sep = strrchr (info.dli_fname, '/');
if (last_sep == NULL) {
libname = strdup (info.dli_fname);
} else {
libname = strdup (last_sep + 1);
}
argv [0] = libname;
}
if (argv [0] == NULL)
argv [0] = (char *) "embedded";
xamarin_main (1, argv, XamarinLaunchModeEmbedded);
if (libname != NULL)
free (libname);
}
/* Installs g_print/g_error handlers that will redirect output to the system Console */
void
xamarin_install_log_callbacks ()
{
#if !defined (CORECLR_RUNTIME)
mono_trace_set_log_handler (log_callback, NULL);
mono_trace_set_print_handler (print_callback);
mono_trace_set_printerr_handler (print_callback);
#endif
}
2016-04-21 15:19:32 +03:00
void
xamarin_initialize ()
{
// COOP: accessing managed memory: UNSAFE mode
MONO_ASSERT_GC_UNSAFE;
GCHandle exception_gchandle = INVALID_GCHANDLE;
2016-04-21 15:19:32 +03:00
initialize_started = TRUE;
#ifdef DYNAMIC_MONO_RUNTIME
// We might be called from the managed Runtime.EnsureInitialized method,
// in which case xamarin_initialize_dynamic_runtime has not been called yet.
xamarin_initialize_dynamic_runtime (NULL);
#endif
#if !DOTNET
xamarin_insert_dllmap ();
#endif
MONO_ENTER_GC_UNSAFE;
xamarin_install_log_callbacks ();
2016-04-21 15:19:32 +03:00
#if !defined (CORECLR_RUNTIME)
2016-04-21 15:19:32 +03:00
MonoGCFinalizerCallbacks gc_callbacks;
gc_callbacks.version = MONO_GC_FINALIZER_EXTENSION_VERSION;
gc_callbacks.is_class_finalization_aware = is_class_finalization_aware;
gc_callbacks.object_queued_for_finalization = object_queued_for_finalization;
mono_gc_register_finalizer_callbacks (&gc_callbacks);
#endif
2016-04-21 15:19:32 +03:00
if (xamarin_is_gc_coop) {
// There should be no such thing as an unhandled ObjC exception
// when running the GC in cooperative mode, and if we run into it,
// it's a bug somewhere, in which case we must fix it.
} else {
NSSetUncaughtExceptionHandler (exception_handler);
}
2016-04-21 15:19:32 +03:00
options.size = sizeof (options);
#if TARGET_OS_SIMULATOR
2016-04-21 15:19:32 +03:00
options.flags = (enum InitializationFlags) (options.flags | InitializationFlagsIsSimulator);
#endif
#if defined (CORECLR_RUNTIME)
options.flags = (enum InitializationFlags) (options.flags | InitializationFlagsIsCoreCLR);
#endif
options.Delegates = &delegates;
options.Trampolines = &trampolines;
options.MarshalObjectiveCExceptionMode = xamarin_marshal_objectivec_exception_mode;
options.MarshalManagedExceptionMode = xamarin_marshal_managed_exception_mode;
#if MONOMAC
options.LaunchMode = xamarin_launch_mode;
options.EntryAssemblyPath = xamarin_entry_assembly_path;
#endif
2016-04-21 15:19:32 +03:00
#if defined (CORECLR_RUNTIME)
options.xamarin_objc_msgsend = (void *) xamarin_dyn_objc_msgSend;
options.xamarin_objc_msgsend_super = (void *) xamarin_dyn_objc_msgSendSuper;
#if !defined(__aarch64__)
options.xamarin_objc_msgsend_stret = (void *) xamarin_dyn_objc_msgSend_stret;
options.xamarin_objc_msgsend_super_stret = (void *) xamarin_dyn_objc_msgSendSuper_stret;
#endif // !defined(__aarch64__)
options.unhandled_exception_handler = (void *) &xamarin_coreclr_unhandled_exception_handler;
options.reference_tracking_begin_end_callback = (void *) &xamarin_coreclr_reference_tracking_begin_end_callback;
options.reference_tracking_is_referenced_callback = (void *) &xamarin_coreclr_reference_tracking_is_referenced_callback;
options.reference_tracking_tracked_object_entered_finalization = (void *) &xamarin_coreclr_reference_tracking_tracked_object_entered_finalization;
#endif // defined(CORECLR_RUNTIME)
xamarin_bridge_call_runtime_initialize (&options, &exception_gchandle);
if (exception_gchandle != INVALID_GCHANDLE) {
NSLog (@PRODUCT ": An exception occurred when calling Runtime.Initialize:\n%@", xamarin_print_all_exceptions (exception_gchandle));
xamarin_process_managed_exception_gchandle (exception_gchandle);
xamarin_assertion_message ("Can't continue if Runtime.Initialize fails.");
}
2016-04-21 15:19:32 +03:00
xamarin_bridge_register_product_assembly (&exception_gchandle);
xamarin_process_managed_exception_gchandle (exception_gchandle);
#if !defined (CORECLR_RUNTIME)
xamarin_install_mono_profiler (); // must be called before xamarin_install_nsautoreleasepool_hooks or xamarin_enable_new_refcount
#endif
[runtime] Use a single mono profiler for both newrefcount and NSAutoreleasePool thread hooks. (#5495) Additionally don't use malloc'ed memory, to avoid having to free the memory. This fixes a leak, since mono does not free the MonoProfiler argument: STACK OF 1 INSTANCE OF 'ROOT LEAK: <0x7fb94495c460>': [thread 0x10f3f85c0]: 19 libdyld.dylib 0x7fff6e009ed9 start + 1 18 NameNotImportant 0x10592dec4 main + 36 launcher.m:679 17 NameNotImportant 0x10592d039 xamarin_main + 1305 launcher.m:661 16 NameNotImportant 0x105973473 mono_main + 11731 driver.g.c:2484 15 NameNotImportant 0x10597020d mono_jit_exec + 349 driver.g.c:1236 14 NameNotImportant 0x105b4217e mono_runtime_exec_main_checked + 110 object.c:0 13 NameNotImportant 0x105b3af28 mono_runtime_invoke_checked + 136 object.c:2960 12 NameNotImportant 0x105a143a1 mono_jit_runtime_invoke + 513 mini-runtime.c:3011 11 NameNotImportant 0x105a103c1 mono_jit_compile_method_with_opt + 2577 mini-runtime.c:2411 10 NameNotImportant 0x105a21aa7 mono_jit_compile_method_inner + 1207 mini.c:4184 9 NameNotImportant 0x105b3b75f mono_runtime_class_init_full + 847 object.c:527 8 NameNotImportant 0x105b3c9d4 mono_runtime_try_invoke + 148 object.c:2960 7 NameNotImportant 0x105a147d3 mono_jit_runtime_invoke + 1587 mini-runtime.c:3148 6 ??? 0x106d00fb0 0x7fffffffffffffff + 9223372041264041905 5 NameNotImportant 0x105921b0c xamarin_initialize + 812 runtime.m:1412 4 NameNotImportant 0x10591e7e7 xamarin_install_nsautoreleasepool_hooks + 55 shared.m:241 3 NameNotImportant 0x105b4fd0a mono_profiler_install + 26 profiler.c:1019 2 NameNotImportant 0x105c3127b monoeg_malloc0 + 27 gmem.c:121 1 libsystem_malloc.dylib 0x7fff6e1bacba calloc + 30 0 libsystem_malloc.dylib 0x7fff6e1bad62 malloc_zone_calloc + 139 Also remove the call to mono_profiler_set_events, it doesn't do anything anymore ([1]). [1]: https://github.com/mono/mono/blob/14b061ba65815d4580078e93c4f2df77ec84b882/mono/metadata/profiler.c#L1098-L1101
2019-01-28 17:06:40 +03:00
xamarin_install_nsautoreleasepool_hooks ();
2016-04-21 15:19:32 +03:00
#if defined (DEBUG)
if (xamarin_gc_pump) {
pthread_t gc_thread;
pthread_create (&gc_thread, NULL, pump_gc, NULL);
}
#endif
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init (&framework_peer_release_lock, &attr);
pthread_mutexattr_destroy (&attr);
xamarin_enable_new_refcount ();
MONO_EXIT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
}
static char *x_app_bundle_path = NULL;
const char *
xamarin_get_app_bundle_path ()
{
if (x_app_bundle_path != NULL)
return x_app_bundle_path;
NSBundle *main_bundle = [NSBundle mainBundle];
if (main_bundle == NULL)
xamarin_assertion_message ("Could not find the main bundle in the app ([NSBundle mainBundle] returned nil)");
x_app_bundle_path = strdup ([[[main_bundle bundlePath] stringByStandardizingPath] UTF8String]);
return x_app_bundle_path;
}
2016-04-21 15:19:32 +03:00
static char *x_bundle_path = NULL;
const char *
xamarin_get_bundle_path ()
{
// COOP: only called at startup, so I believe the mode doesn't matter
2016-04-21 15:19:32 +03:00
if (x_bundle_path != NULL)
return x_bundle_path;
NSBundle *main_bundle = [NSBundle mainBundle];
NSString *bundle_path;
if (main_bundle == NULL)
xamarin_assertion_message ("Could not find the main bundle in the app ([NSBundle mainBundle] returned nil)");
#if TARGET_OS_MACCATALYST || TARGET_OS_OSX
if (xamarin_launch_mode == XamarinLaunchModeEmbedded) {
bundle_path = [[[NSBundle bundleForClass: [XamarinAssociatedObject class]] bundlePath] stringByAppendingPathComponent: @"Versions/Current"];
} else {
bundle_path = [[main_bundle bundlePath] stringByAppendingPathComponent:@"Contents"];
}
bundle_path = [bundle_path stringByAppendingPathComponent: xamarin_custom_bundle_name];
2016-04-21 15:19:32 +03:00
#else
bundle_path = [main_bundle bundlePath];
#endif
x_bundle_path = strdup ([[bundle_path stringByStandardizingPath] UTF8String]);
2016-04-21 15:19:32 +03:00
return x_bundle_path;
}
void
xamarin_set_bundle_path (const char *path)
{
// COOP: no managed memory access: any mode
2016-04-21 15:19:32 +03:00
free (x_bundle_path);
x_bundle_path = strdup (path);
}
void *
xamarin_calloc (size_t size)
{
// COOP: no managed memory access: any mode
return calloc (size, 1);
}
2016-04-21 15:19:32 +03:00
void
xamarin_free (void *ptr)
{
// COOP: no managed memory access: any mode
// We use this method to free memory returned by mono,
// which means we have to use the free function mono expects.
2016-04-21 15:19:32 +03:00
if (ptr)
free (ptr);
}
char *
xamarin_strdup_printf (const char *msg, ...)
{
// COOP: no managed memory access: any mode
2016-04-21 15:19:32 +03:00
va_list args;
char *formatted = NULL;
va_start (args, msg);
vasprintf (&formatted, msg, args);
va_end (args);
return formatted;
}
void
xamarin_assertion_message (const char *msg, ...)
{
// COOP: no managed memory access: any mode.
2016-04-21 15:19:32 +03:00
va_list args;
char *formatted = NULL;
va_start (args, msg);
vasprintf (&formatted, msg, args);
if (formatted) {
PRINT ( PRODUCT ": %s", formatted);
2016-04-21 15:19:32 +03:00
free (formatted);
}
va_end (args);
abort ();
}
static const char *
objc_skip_type (const char *type)
{
// COOP: no managed memory access: any mode
2016-04-21 15:19:32 +03:00
switch (type [0]) {
case _C_ID:
case _C_CLASS:
case _C_SEL:
case _C_CHR:
case _C_UCHR:
case _C_SHT:
case _C_USHT:
case _C_INT:
case _C_UINT:
case _C_LNG:
case _C_ULNG:
case _C_LNG_LNG:
case _C_ULNG_LNG:
case _C_FLT:
case _C_DBL:
case _C_CHARPTR:
case _C_BOOL:
case _C_VOID:
case _C_UNDEF:
return ++type;
case _C_PTR:
return objc_skip_type (++type);
case _C_BFLD:
type++;
while (*type && *type >= '0' && *type <= '9')
type++;
return type;
2016-04-21 15:19:32 +03:00
case _C_ATOM:
case _C_VECTOR:
case _C_CONST:
case _C_ARY_E:
case _C_UNION_E:
case _C_STRUCT_E:
xamarin_assertion_message ("Unhandled type encoding: %s", type);
break;
2016-04-21 15:19:32 +03:00
case _C_ARY_B: {
do {
type++;
} while (isdigit (*type));
type = objc_skip_type (type);
return ++type;
}
case _C_UNION_B: {
do {
type++;
} while (*type != '=');
2016-04-21 15:19:32 +03:00
type ++;
do {
type = objc_skip_type (type);
} while (*type != _C_UNION_E);
return ++type;
}
case _C_STRUCT_B: {
do {
type++;
} while (*type != '=');
type++;
do {
type = objc_skip_type (type);
} while (*type != _C_STRUCT_E);
return ++type;
}
default:
xamarin_assertion_message ("Unsupported type encoding: %s", type);
break;
2016-04-21 15:19:32 +03:00
}
}
unsigned long
2016-04-21 15:19:32 +03:00
xamarin_objc_type_size (const char *type)
{
const char *original_type = type;
// COOP: no managed memory access: any mode
2016-04-21 15:19:32 +03:00
switch (type [0]) {
case _C_ID: return sizeof (id);
case _C_CLASS: return sizeof (Class);
case _C_SEL: return sizeof (SEL);
case _C_CHR: return sizeof (char);
case _C_UCHR: return sizeof (unsigned char);
case _C_SHT: return sizeof (short);
case _C_USHT: return sizeof (unsigned short);
case _C_INT: return sizeof (int);
case _C_UINT: return sizeof (unsigned int);
case _C_LNG: return sizeof (long);
case _C_ULNG: return sizeof (unsigned long);
case _C_LNG_LNG: return sizeof (long long);
case _C_ULNG_LNG: return sizeof (unsigned long long);
case _C_FLT: return sizeof (float);
case _C_DBL: return sizeof (double);
case _C_BOOL: return sizeof (BOOL);
case _C_VOID: return 0;
case _C_PTR: return sizeof (void *);
case _C_CHARPTR: return sizeof (char *);
case _C_BFLD: {
// Example: [NSDecimalNumberPlaceholder initWithDecimal:] = @28@0:4{?=b8b4b1b1b18[8S]}8
unsigned long bits = 0;
int bc = 1;
while (type [bc] >= '0' && type [bc] <= '9') {
bits = bits * 10ul + (unsigned long) (type [bc] - '0');
bc++;
}
if (bits % sizeof (void *) == 0)
return bits / sizeof (void *);
return 1 + (bits / sizeof (void *));
}
case _C_UNDEF:
case _C_ATOM:
case _C_VECTOR:
xamarin_assertion_message ("Unhandled type encoding: %s", type);
break;
2016-04-21 15:19:32 +03:00
case _C_ARY_B: {
unsigned long size = 0;
unsigned long len = (unsigned long) atol (type+1);
2016-04-21 15:19:32 +03:00
do {
type++;
} while (isdigit (*type));
size = xamarin_objc_type_size (type);
size = (size + (sizeof (void *) - 1)) & ~((sizeof (void *) - 1));
return len * size;
}
case _C_UNION_B: {
unsigned long size = 0;
2016-04-21 15:19:32 +03:00
do {
type++;
if (*type == 0)
xamarin_assertion_message ("Unsupported union type: %s", original_type);
} while (*type != '=');
2016-04-21 15:19:32 +03:00
++type;
do {
if (*type == 0)
xamarin_assertion_message ("Unsupported union type: %s", original_type);
unsigned long tsize = xamarin_objc_type_size (type);
2016-04-21 15:19:32 +03:00
type = objc_skip_type (type);
tsize = (tsize + (sizeof (void *) - 1)) & ~((sizeof (void *) - 1));
size = size > tsize ? size : tsize;
} while (*type != _C_UNION_E);
return size;
}
case _C_STRUCT_B: {
unsigned long size = 0;
2016-04-21 15:19:32 +03:00
do {
type++;
if (*type == 0)
xamarin_assertion_message ("Unsupported struct type: %s", original_type);
2016-04-21 15:19:32 +03:00
} while (*type != '=');
type++;
while (*type != _C_STRUCT_E) {
if (*type == 0)
xamarin_assertion_message ("Unsupported struct type: %s", original_type);
unsigned long item_size = xamarin_objc_type_size (type);
2016-04-21 15:19:32 +03:00
size += (item_size + (sizeof (void *) - 1)) & ~((sizeof (void *) - 1));
type = objc_skip_type (type);
}
return size;
}
// The following are from table 6-2 here: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
case 'r': // _C_CONST
2016-04-21 15:19:32 +03:00
case 'n':
case 'N':
case 'o':
case 'O':
case 'R':
case 'V':
return xamarin_objc_type_size (type + 1);
}
xamarin_assertion_message ("Unsupported type encoding: %s", original_type);
2016-04-21 15:19:32 +03:00
}
/*
* Reference counting
* ==================
*
* There are two types of managed types:
* - Wrapper types for existing ObjectiveC types (such as UIView).
* - Other types derived from wrapper types (henceforth called User types),
* that do not have a corresponding ObjectiveC type.
*
* Wrapper types
* -------------
*
* They are simple. The managed peer's lifetime is not linked to the native object's lifetime.
* The managed peer can be freed by the GC whenever it so determines, and if a managed object
* is needed at a later stage we just recreate it.
*
* User types
* ----------
* These are not that simple. We can't free managed objects at will, since they may contain
* user state. Therefore we must ensure the managed object stays alive as long as needed
* (this is done by using a strong GCHandle) - the problem is determining when it's no longer
* needed.
*
* Historically we've had two cases:
* 1) User calls Dispose, we free the managed ref and break the link between the native and
* managed object, thus allowing the GC to free it (if nothing else is keeping it alive
* of course).
* 2) Refcount reaches 1, in which case we know that the managed ref is the only ref and we
* can safely assume that native code will not use the object again. We break the link,
* release the native object (which will be freed now) and allow the GC to free the
* managed object.
*
* Problem arises in case 1), when users call Dispose and then native code tries to use
* that object for whatever reason. MonoTouch will detect that no managed peer exists,
* and try to (re)create one. This may fail, and an exception is thrown which may kill
* the process (there may be no managed frame / exception handler on the stack at this point).
*
* This solution will do a couple of things when user calls Dispose:
* - Free the managed ref.
* - Not break the link between the native and managed object until the native object's
* refcount reaches 0.
* This will allow us to still lookup the managed object as long as the native object is
* alive.
*
* Implementation details (for user types only)
* ============================================
*
* Managed code releases its ref when either of the following conditions occur:
* - Dispose is called (manually) on the object.
* - The Handle property on the managed object changes.
* - The GC frees the object. This can only happen if refCount == 1 and that ref is a managed ref
* (since otherwise there will be a strong gchandle to the managed object preventing the GC from freeing it).
*
* Objects are removed from the native<->managed dictionary when either of the following conditions occur:
* - The native object is dealloc'ed (refcount reaches 0).
* - The managed object's Handle property changes (the link between the previous Handle
* value and the managed object is then removed from the dictionary).
*
* We need to keep track of two pieces of information in native land:
* - The GCHandle to the managed object.
* - If there is a managed ref or not.
* We already have an ObjectiveC ivar to store the GCHandle, so to not create another ivar
* we use one bit of the GCHandle to store whether there is a managed ref or not (MANAGED_REF_BIT).
*
*/
//#define DEBUG_REF_COUNTING
void
xamarin_create_gchandle (id self, void *managed_object, enum XamarinGCHandleFlags flags, bool force_weak)
2016-04-21 15:19:32 +03:00
{
// COOP: reads managed memory: unsafe mode
MONO_ASSERT_GC_UNSAFE;
2016-04-21 15:19:32 +03:00
// force_weak is to avoid calling retainCount unless needed, since some classes (UIWebView in iOS 5)
// will crash if retainCount is called before init. See bug #9261.
bool weak = force_weak || ([self retainCount] == 1);
GCHandle gchandle;
2016-04-21 15:19:32 +03:00
if (weak) {
gchandle = xamarin_gchandle_new_weakref ((MonoObject *) managed_object, TRUE);
flags = (enum XamarinGCHandleFlags) (flags | XamarinGCHandleFlags_WeakGCHandle);
2016-04-21 15:19:32 +03:00
} else {
gchandle = xamarin_gchandle_new ((MonoObject *) managed_object, FALSE);
flags = (enum XamarinGCHandleFlags) (flags & ~XamarinGCHandleFlags_WeakGCHandle);
2016-04-21 15:19:32 +03:00
}
set_gchandle (self, gchandle, flags);
2016-04-21 15:19:32 +03:00
#if defined(DEBUG_REF_COUNTING)
PRINT ("\tGCHandle created for %p: %d (flags: %p) = %s managed object: %p\n", self, gchandle, GINT_TO_POINTER (flags), weak ? "weak" : "strong", managed_object);
2016-04-21 15:19:32 +03:00
#endif
}
void
xamarin_switch_gchandle (id self, bool to_weak)
{
// COOP: reads managed memory: unsafe mode
MONO_ASSERT_GC_SAFE_OR_DETACHED;
GCHandle new_gchandle;
GCHandle old_gchandle;
2016-04-21 15:19:32 +03:00
MonoObject *managed_object;
enum XamarinGCHandleFlags flags = XamarinGCHandleFlags_None;
2016-04-21 15:19:32 +03:00
old_gchandle = get_gchandle_safe (self, &flags);
2016-04-21 15:19:32 +03:00
if (old_gchandle) {
bool is_weak = (flags & XamarinGCHandleFlags_WeakGCHandle) == XamarinGCHandleFlags_WeakGCHandle;
2016-04-21 15:19:32 +03:00
if (to_weak == is_weak) {
// we already have the GCHandle we need
#if defined(DEBUG_REF_COUNTING)
PRINT ("Object %p already has a %s GCHandle = %d\n", self, to_weak ? "weak" : "strong", old_gchandle);
2016-04-21 15:19:32 +03:00
#endif
return;
}
} else {
// We don't have a GCHandle. This means there's no managed instance for this
// native object.
// If to_weak is true, then there's obviously nothing to do
// (why create a managed object which can immediately be freed by the GC?).
// If to_weak is false, the question is if we want to create the
// managed object. Bug #30420 says no (previously we didn't, and
// if we do, managed ctors end up being executed at a different moment,
// which breaks implicit assumptions in people's code.)
#if defined(DEBUG_REF_COUNTING)
PRINT ("Object %p has no managed object to create a %s GCHandle for\n", self, to_weak ? "weak" : "strong");
2016-04-21 15:19:32 +03:00
#endif
return;
}
MONO_THREAD_ATTACH; // COOP: will switch to GC_UNSAFE
managed_object = xamarin_gchandle_get_target (old_gchandle);
if (to_weak) {
new_gchandle = xamarin_gchandle_new_weakref (managed_object, TRUE);
flags = (enum XamarinGCHandleFlags) (flags | XamarinGCHandleFlags_WeakGCHandle);
2016-04-21 15:19:32 +03:00
} else {
new_gchandle = xamarin_gchandle_new (managed_object, FALSE);
flags = (enum XamarinGCHandleFlags) (flags & ~XamarinGCHandleFlags_WeakGCHandle);
2016-04-21 15:19:32 +03:00
}
xamarin_gchandle_free (old_gchandle);
2016-04-21 15:19:32 +03:00
if (managed_object) {
// It's possible to not have a managed object if:
// 1. Objective-C holds a weak reference to the native object (and no other strong references)
// - in which case the original (old) gchandle would be a weak one.
// 2. Managed code does not reference the managed object.
// 3. The GC ran and collected the managed object, but the main thread has not gotten
// around to release the native object yet.
// If all these conditions hold, then the original gchandle will point to
// null, because the target would be collected.
xamarin_set_nsobject_flags (managed_object, xamarin_get_nsobject_flags (managed_object) | NSObjectFlagsHasManagedRef);
2016-04-21 15:19:32 +03:00
}
set_gchandle (self, new_gchandle, flags);
2016-04-21 15:19:32 +03:00
MONO_THREAD_DETACH; // COOP: this will switch to GC_SAFE
xamarin_mono_object_release (&managed_object);
2016-04-21 15:19:32 +03:00
#if defined(DEBUG_REF_COUNTING)
PRINT ("Switched object %p to %s GCHandle = %d managed object = %p\n", self, to_weak ? "weak" : "strong", new_gchandle, managed_object);
2016-04-21 15:19:32 +03:00
#endif
}
void
xamarin_free_gchandle (id self, GCHandle gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: no managed memory access, but calls mono function mono_gc_handle_free. Assuming that function can be called with any mode: this function can be called with any mode as well
2016-04-21 15:19:32 +03:00
if (gchandle) {
#if defined(DEBUG_REF_COUNTING)
PRINT ("\tGCHandle %i destroyed for object %p\n", gchandle, self);
2016-04-21 15:19:32 +03:00
#endif
xamarin_gchandle_free (gchandle);
2016-04-21 15:19:32 +03:00
set_gchandle (self, INVALID_GCHANDLE, XamarinGCHandleFlags_None);
2016-04-21 15:19:32 +03:00
} else {
#if defined(DEBUG_REF_COUNTING)
PRINT ("\tNo GCHandle for the object %p\n", self);
2016-04-21 15:19:32 +03:00
#endif
}
}
void
xamarin_clear_gchandle (id self)
{
// COOP: no managed memory access: any mode
set_gchandle (self, INVALID_GCHANDLE, XamarinGCHandleFlags_None);
2016-04-21 15:19:32 +03:00
}
bool
xamarin_set_gchandle_with_flags (id self, GCHandle gchandle, enum XamarinGCHandleFlags flags)
2016-04-21 15:19:32 +03:00
{
// COOP: no managed memory access: any mode
return set_gchandle (self, gchandle, flags);
}
bool
xamarin_set_gchandle_with_flags_safe (id self, GCHandle gchandle, enum XamarinGCHandleFlags flags)
{
// COOP: no managed memory access: any mode
return set_gchandle_safe (self, gchandle, flags);
2016-04-21 15:19:32 +03:00
}
#if defined(DEBUG_REF_COUNTING)
int
get_safe_retainCount (id self)
{
// COOP: no managed memory access: any mode
2016-04-21 15:19:32 +03:00
if ([self isKindOfClass: [NSCalendar class]] ||
[self isKindOfClass: [NSInputStream class]] ||
[self isKindOfClass: [NSOutputStream class]]) {
// NSCalendar/NSInputStream may end up with a stack overflow where CFGetRetainCount calls itself
return 666;
} else {
return (int) [self retainCount];
2016-04-21 15:19:32 +03:00
}
}
#endif
void
xamarin_release_managed_ref (id self, bool user_type)
2016-04-21 15:19:32 +03:00
{
// COOP: This is a P/Invoke, so at entry we're in safe mode.
MONO_ASSERT_GC_SAFE_OR_DETACHED;
2016-04-21 15:19:32 +03:00
#if defined(DEBUG_REF_COUNTING)
PRINT ("monotouch_release_managed_ref (%s Handle=%p) retainCount=%d; HasManagedRef=%i GCHandle=%p IsUserType=%i managed_obj=%p\n",
class_getName (object_getClass (self)), self, (int32_t) [self retainCount], user_type ? xamarin_has_managed_ref (self) : 666, user_type ? get_gchandle_without_flags (self) : (void*) 666, user_type, managed_obj);
2016-04-21 15:19:32 +03:00
#endif
if (user_type) {
/* clear MANAGED_REF_BIT */
set_flags_safe (self, (enum XamarinGCHandleFlags) (get_flags_safe (self) & ~XamarinGCHandleFlags_HasManagedRef));
2016-04-21 15:19:32 +03:00
} else {
//
// This waypoint (lock+unlock) is needed so that we can safely call retainCount in the
// toggleref callback.
//
// The race is between the following actions (given a managed object Z):
//
// a1) Thread A nulls out the handle for Z
// a2) Thread A calls release on Z's original handle
// b1) Thread B fetches the handle for Z
// b2) Thread B calls retainCount on Z's handle
//
// Possible execution orders:
//
// 1) a1-*: all such orders are safe, because b1 will read NULL and
// b2 won't do anything
// 2) b1-b2-a1-a2: retainCount before release is safe.
// 3) b1-a1-b2-a2: retainCount before release is safe.
// 4) b1-a1-a2-b2: unsafe; this tries to call retainCount after
// release.
//
// Order 4 would look like this:
//
// * Thread B runs a GC, and starts calling toggleref callbacks.
// * Thread B fetches the handle (H) for object Z in a toggleref
// callback.
// * Thread A calls xamarin_release_managed_ref for object Z, and
// calls release on H, deallocating it.
// * Thread B tries to call retainCount on H, which is now a
// deallocated object.
//
// Solution: lock/unlock the framework peer lock here. This looks
// weird (since nothing happens inside the lock), but it works:
//
// * Thread B runs a GC, locks the framework peer lock, and starts
// calling toggleref callbacks.
// * Thread B fetches the handle (H) for object Z in a toggleref
// callback.
// * Thread A calls xamarin_release_managed_ref for object Z (and
// also nulls out the handle for Z)
// * Thread A tries to lock the framework peer lock, and blocks
// (before calling release on H)
// * Thread B successfully calls retainCount on H
// * Thread B finishes processing all toggleref callbacks, completes
// the GC, and unlocks the framework peer lock.
// * Thread A wakes up, and calls release on H.
//
// An alternative phrasing would be to say that the lock prevents both
// a1 and a2 from happening between b1 and b2 from above, thus making
// order 4 impossible.
//
// Q) Why not just unlock after calling release, to avoid the strange-
// looking empty lock?
// A) Because calling release on an object might end up calling
// managed code (the native object can override dealloc and do all
// sorts of strange things, any of which may end up invoking
// managed code), and we can deadlock:
// 1) Thread T calls release on a native object.
// 2) Thread T executes managed code, which blocks on something
// that's supposed to happen on another thread U.
// 3) Thread U causes a garbage collection.
// 4) Thread U tries to lock the framework peer lock before running
// the GC, and deadlocks because thread T already has the
// framework peer lock.
//
// This is https://github.com/xamarin/xamarin-macios/issues/3943
//
// See also comment in xamarin_marshal_return_value_impl
xamarin_framework_peer_waypoint_safe ();
2016-04-21 15:19:32 +03:00
}
[self release];
2016-04-21 15:19:32 +03:00
}
/*
* Block support
*/
typedef struct {
MonoMethod *method;
int par;
2016-04-21 15:19:32 +03:00
} MethodAndPar;
static gboolean
method_and_par_compare (gconstpointer l, gconstpointer r)
{
MethodAndPar *f = (MethodAndPar *)l;
MethodAndPar *g = (MethodAndPar *) r;
return f->method == g->method && f->par == g->par;
}
static unsigned int
method_and_par_hash (gconstpointer l)
{
MethodAndPar *x = (MethodAndPar *) l;
return (unsigned int) (intptr_t) x->method;
}
static pthread_mutex_t wrapper_hash_lock = PTHREAD_MUTEX_INITIALIZER;
static MonoReferenceQueue *block_wrapper_queue;
/*
* Given a MonoMethod and a parameter, lookup the MethodInfo (MonoReflectionMethod)
* that can be used to create a new delegate, this returns the method that can
* create the method
*/
static GCHandle
get_method_block_wrapper_creator (MonoMethod *method, int par, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: accesses managed memory: unsafe mode.
MONO_ASSERT_GC_UNSAFE;
GCHandle rv = INVALID_GCHANDLE;
2016-04-21 15:19:32 +03:00
MonoObject *res = NULL;
MethodAndPar mp, *nmp;
mp.method = method;
mp.par = par;
// PRINT ("Looking up method and par (%x and %d)", (int) method, par);
MONO_ENTER_GC_SAFE;
2016-04-21 15:19:32 +03:00
pthread_mutex_lock (&wrapper_hash_lock);
MONO_EXIT_GC_SAFE;
2016-04-21 15:19:32 +03:00
if (xamarin_wrapper_hash == NULL) {
xamarin_wrapper_hash = mono_g_hash_table_new_type (method_and_par_hash, method_and_par_compare, MONO_HASH_VALUE_GC);
}
res = (MonoObject *) mono_g_hash_table_lookup (xamarin_wrapper_hash, &mp);
pthread_mutex_unlock (&wrapper_hash_lock);
if (res != NULL){
rv = xamarin_gchandle_new (res, false);
xamarin_mono_object_release (&res);
// PRINT ("Found match: %x", (int) res);
return rv;
2016-04-21 15:19:32 +03:00
}
MonoReflectionMethod *reflection_method = mono_method_get_object (mono_domain_get (), method, NULL);
res = xamarin_get_block_wrapper_creator (reflection_method, (int) par, exception_gchandle);
xamarin_mono_object_release (&reflection_method);
if (*exception_gchandle != INVALID_GCHANDLE)
return INVALID_GCHANDLE;
// PRINT ("New value: %x", (int) res);
2016-04-21 15:19:32 +03:00
nmp = (MethodAndPar *) malloc (sizeof (MethodAndPar));
*nmp = mp;
MONO_ENTER_GC_SAFE;
2016-04-21 15:19:32 +03:00
pthread_mutex_lock (&wrapper_hash_lock);
MONO_EXIT_GC_SAFE;
2016-04-21 15:19:32 +03:00
mono_g_hash_table_insert (xamarin_wrapper_hash, nmp, res);
pthread_mutex_unlock (&wrapper_hash_lock);
rv = xamarin_gchandle_new (res, false);
xamarin_mono_object_release (&res);
return rv;
2016-04-21 15:19:32 +03:00
}
void
xamarin_release_block_on_main_thread (void *obj)
{
if ([NSThread isMainThread]) {
_Block_release (obj);
} else {
dispatch_async_f (dispatch_get_main_queue (), obj, (dispatch_function_t) _Block_release);
}
}
2016-04-21 15:19:32 +03:00
/*
* Creates a System.Delegate to wrap an Objective-C proxy when surfacing parameters from Objective-C to C#.
* @method: method where the parameter is found
* @par: index of the parameter that is a delegate
*
* Given a method, and a parameter index that we have previously probed to be a Delegate,
* this method returns a strongly typed System.Delegate that wraps the underlying
* Objective-C block.
*
* This works by enlisting the help of the C# runtime to find a [BlockProxy] attrbute
* on the parameter of the function, or in one of the base definitions. That attribute
* contains a link to a proxy type that can create the delegate, which we in turn invoke
*
* Returns: the instantiated delegate.
*/
MonoObject *
xamarin_get_delegate_for_block_parameter (MonoMethod *method, guint32 token_ref, int par, void *nativeBlock, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: accesses managed memory: unsafe mode.
MONO_ASSERT_GC_UNSAFE;
MonoObject *delegate = NULL;
GCHandle obj_handle = INVALID_GCHANDLE;
2016-04-21 15:19:32 +03:00
if (nativeBlock == NULL)
return NULL;
if (token_ref != INVALID_TOKEN_REF) {
obj_handle = xamarin_get_method_from_token (token_ref, exception_gchandle);
} else {
obj_handle = get_method_block_wrapper_creator (method, par, exception_gchandle);
}
if (*exception_gchandle != INVALID_GCHANDLE)
goto cleanup;
2016-04-21 15:19:32 +03:00
/* retain or copy (if it's a stack block) the block */
nativeBlock = _Block_copy (nativeBlock);
delegate = xamarin_create_block_proxy (obj_handle, nativeBlock, exception_gchandle);
if (*exception_gchandle != INVALID_GCHANDLE) {
_Block_release (nativeBlock);
delegate = NULL;
goto cleanup;
}
2016-04-21 15:19:32 +03:00
MONO_ENTER_GC_SAFE;
2016-04-21 15:19:32 +03:00
pthread_mutex_lock (&wrapper_hash_lock);
MONO_EXIT_GC_SAFE;
if (block_wrapper_queue == NULL)
block_wrapper_queue = mono_gc_reference_queue_new (xamarin_release_block_on_main_thread);
2016-04-21 15:19:32 +03:00
mono_gc_reference_queue_add (block_wrapper_queue, delegate, nativeBlock);
pthread_mutex_unlock (&wrapper_hash_lock);
cleanup:
xamarin_gchandle_free (obj_handle);
return delegate;
2016-04-21 15:19:32 +03:00
}
id
xamarin_get_block_for_delegate (MonoMethod *method, MonoObject *delegate, const char *signature, guint32 token_ref, GCHandle *exception_gchandle)
2016-04-21 15:19:32 +03:00
{
// COOP: accesses managed memory: unsafe mode.
MonoReflectionMethod *reflection_method = mono_method_get_object (mono_domain_get (), method, NULL);
id rv = xamarin_create_delegate_proxy (reflection_method, delegate, signature, token_ref, exception_gchandle);
xamarin_mono_object_release (&reflection_method);
return rv;
2016-04-21 15:19:32 +03:00
}
void
xamarin_release_static_dictionaries ()
{
#if defined (CORECLR_RUNTIME)
// Release static dictionaries of cached objects. If we end up trying to
// add objects to these dictionaries after this point (on a background
// thread), the dictionaries will be re-created (and leak) - which
// shouldn't be a problem, because at this point the process is about to
// exit anyway.
pthread_mutex_lock (&wrapper_hash_lock);
xamarin_mono_object_release (&block_wrapper_queue);
xamarin_mono_object_release (&xamarin_wrapper_hash);
pthread_mutex_unlock (&wrapper_hash_lock);
#endif
}
2016-04-21 15:19:32 +03:00
void
xamarin_set_use_sgen (bool value)
{
}
bool
xamarin_get_use_sgen ()
{
return true;
}
void
xamarin_set_gc_pump_enabled (bool value)
{
#if DEBUG
// COOP: no managed memory access: any mode.
2016-04-21 15:19:32 +03:00
xamarin_gc_pump = value;
#endif
2016-04-21 15:19:32 +03:00
}
const char *
xamarin_skip_encoding_flags (const char *encoding)
{
// COOP: no managed memory access: any mode.
2016-04-21 15:19:32 +03:00
while (true) {
switch (*encoding) {
case 'r': // const
case 'n': // in
case 'N': // inout
case 'o': // out
case 'O': // bycopy
case 'R': // byref
case 'V': // oneway
encoding++;
continue;
default:
return encoding;
}
}
}
bool
xamarin_log_marshalled_exceptions ()
{
if (xamarin_log_exceptions == XamarinTriStateNone) {
const char *var = getenv ("XAMARIN_LOG_MARSHALLED_EXCEPTIONS");
xamarin_log_exceptions = (var && *var) ? XamarinTriStateEnabled : XamarinTriStateDisabled;
}
return xamarin_log_exceptions == XamarinTriStateEnabled;
}
void
[runtime] Don't try to log an exception for GCHandle that has been freed. (#14003) It does exactly what you'd think it does: nothing helpful at all. Make sure to use a GCHandle that's alive instead. Output before fix: Xamarin.Mac: Processing managed exception for exception marshalling (mode: 2): Failed to print exception: System.NullReferenceException: Object reference not set to an instance of an object. at ObjCRuntime.Runtime.PrintException(Exception exc, Boolean isInnerException, StringBuilder sb) at ObjCRuntime.Runtime.PrintAllExceptions(IntPtr exception_gchandle) Output after fix: Xamarin.Mac: Processing managed exception for exception marshalling (mode: 2): Failed to lookup the required marshalling information. Additional information: Selector: conformsToProtocol: Type: ViewController (ObjCRuntime.RuntimeException) Failed to get the 'this' instance in a method call to templ.ViewController.InvokeConformsToProtocol. (ObjCRuntime.RuntimeException) at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc) at ObjCRuntime.Runtime.GetMethodAndObjectForSelector(IntPtr klass, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc) at ObjCRuntime.Runtime.get_method_and_object_for_selector(IntPtr cls, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc, IntPtr& exception_gchandle) Failed to marshal the Objective-C object 0x7f89e6c2a2a0 (type: ViewController). Could not find an existing managed instance for this object, nor was it possible to create a new managed instance (because the type 'templ.ViewController' does not have a constructor that takes one NativeHandle argument). (ObjCRuntime.RuntimeException) at ObjCRuntime.Runtime.MissingCtor(IntPtr ptr, IntPtr klass, Type type, MissingCtorResolution resolution) at ObjCRuntime.Runtime.ConstructNSObject[T](IntPtr ptr, Type type, MissingCtorResolution missingCtorResolution) at ObjCRuntime.Runtime.ConstructNSObject(IntPtr ptr, IntPtr klass, MissingCtorResolution missingCtorResolution) at ObjCRuntime.Runtime.GetNSObject(IntPtr ptr, MissingCtorResolution missingCtorResolution, Boolean evenInFinalizerQueue) at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
2022-02-02 10:38:52 +03:00
xamarin_log_managed_exception (MonoObject *exception, MarshalManagedExceptionMode mode)
{
if (!xamarin_log_marshalled_exceptions ())
return;
[runtime] Don't try to log an exception for GCHandle that has been freed. (#14003) It does exactly what you'd think it does: nothing helpful at all. Make sure to use a GCHandle that's alive instead. Output before fix: Xamarin.Mac: Processing managed exception for exception marshalling (mode: 2): Failed to print exception: System.NullReferenceException: Object reference not set to an instance of an object. at ObjCRuntime.Runtime.PrintException(Exception exc, Boolean isInnerException, StringBuilder sb) at ObjCRuntime.Runtime.PrintAllExceptions(IntPtr exception_gchandle) Output after fix: Xamarin.Mac: Processing managed exception for exception marshalling (mode: 2): Failed to lookup the required marshalling information. Additional information: Selector: conformsToProtocol: Type: ViewController (ObjCRuntime.RuntimeException) Failed to get the 'this' instance in a method call to templ.ViewController.InvokeConformsToProtocol. (ObjCRuntime.RuntimeException) at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc) at ObjCRuntime.Runtime.GetMethodAndObjectForSelector(IntPtr klass, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc) at ObjCRuntime.Runtime.get_method_and_object_for_selector(IntPtr cls, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc, IntPtr& exception_gchandle) Failed to marshal the Objective-C object 0x7f89e6c2a2a0 (type: ViewController). Could not find an existing managed instance for this object, nor was it possible to create a new managed instance (because the type 'templ.ViewController' does not have a constructor that takes one NativeHandle argument). (ObjCRuntime.RuntimeException) at ObjCRuntime.Runtime.MissingCtor(IntPtr ptr, IntPtr klass, Type type, MissingCtorResolution resolution) at ObjCRuntime.Runtime.ConstructNSObject[T](IntPtr ptr, Type type, MissingCtorResolution missingCtorResolution) at ObjCRuntime.Runtime.ConstructNSObject(IntPtr ptr, IntPtr klass, MissingCtorResolution missingCtorResolution) at ObjCRuntime.Runtime.GetNSObject(IntPtr ptr, MissingCtorResolution missingCtorResolution, Boolean evenInFinalizerQueue) at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
2022-02-02 10:38:52 +03:00
GCHandle handle = xamarin_gchandle_new (exception, false);
NSLog (@PRODUCT ": Processing managed exception for exception marshalling (mode: %i):\n%@", mode, xamarin_print_all_exceptions (handle));
[runtime] Don't try to log an exception for GCHandle that has been freed. (#14003) It does exactly what you'd think it does: nothing helpful at all. Make sure to use a GCHandle that's alive instead. Output before fix: Xamarin.Mac: Processing managed exception for exception marshalling (mode: 2): Failed to print exception: System.NullReferenceException: Object reference not set to an instance of an object. at ObjCRuntime.Runtime.PrintException(Exception exc, Boolean isInnerException, StringBuilder sb) at ObjCRuntime.Runtime.PrintAllExceptions(IntPtr exception_gchandle) Output after fix: Xamarin.Mac: Processing managed exception for exception marshalling (mode: 2): Failed to lookup the required marshalling information. Additional information: Selector: conformsToProtocol: Type: ViewController (ObjCRuntime.RuntimeException) Failed to get the 'this' instance in a method call to templ.ViewController.InvokeConformsToProtocol. (ObjCRuntime.RuntimeException) at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc) at ObjCRuntime.Runtime.GetMethodAndObjectForSelector(IntPtr klass, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc) at ObjCRuntime.Runtime.get_method_and_object_for_selector(IntPtr cls, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc, IntPtr& exception_gchandle) Failed to marshal the Objective-C object 0x7f89e6c2a2a0 (type: ViewController). Could not find an existing managed instance for this object, nor was it possible to create a new managed instance (because the type 'templ.ViewController' does not have a constructor that takes one NativeHandle argument). (ObjCRuntime.RuntimeException) at ObjCRuntime.Runtime.MissingCtor(IntPtr ptr, IntPtr klass, Type type, MissingCtorResolution resolution) at ObjCRuntime.Runtime.ConstructNSObject[T](IntPtr ptr, Type type, MissingCtorResolution missingCtorResolution) at ObjCRuntime.Runtime.ConstructNSObject(IntPtr ptr, IntPtr klass, MissingCtorResolution missingCtorResolution) at ObjCRuntime.Runtime.GetNSObject(IntPtr ptr, MissingCtorResolution missingCtorResolution, Boolean evenInFinalizerQueue) at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
2022-02-02 10:38:52 +03:00
xamarin_gchandle_free (handle);
}
void
xamarin_log_objectivec_exception (NSException *exception, MarshalObjectiveCExceptionMode mode)
{
if (!xamarin_log_marshalled_exceptions ())
return;
NSLog (@PRODUCT ": Processing Objective-C exception for exception marshalling (mode: %i):\n%@", mode, [exception debugDescription]);
}
void
xamarin_process_nsexception (NSException *ns_exception)
{
xamarin_process_nsexception_using_mode (ns_exception, false, NULL);
}
void
xamarin_process_nsexception_using_mode (NSException *ns_exception, bool throwManagedAsDefault, GCHandle *output_exception)
{
XamarinGCHandle *exc_handle;
GCHandle exception_gchandle = INVALID_GCHANDLE;
MarshalObjectiveCExceptionMode mode;
mode = xamarin_on_marshal_objectivec_exception (ns_exception, throwManagedAsDefault, &exception_gchandle);
if (exception_gchandle != INVALID_GCHANDLE) {
PRINT (PRODUCT ": Got an exception while executing the MarshalObjectiveCException event (this exception will be ignored):");
PRINT ("%@", xamarin_print_all_exceptions (exception_gchandle));
xamarin_gchandle_free (exception_gchandle);
exception_gchandle = INVALID_GCHANDLE;
}
if (mode == MarshalObjectiveCExceptionModeDefault)
#if DOTNET
mode = MarshalObjectiveCExceptionModeThrowManagedException;
#else
mode = xamarin_is_gc_coop ? MarshalObjectiveCExceptionModeThrowManagedException : MarshalObjectiveCExceptionModeUnwindManagedCode;
#endif
xamarin_log_objectivec_exception (ns_exception, mode);
switch (mode) {
case MarshalObjectiveCExceptionModeUnwindManagedCode:
if (xamarin_is_gc_coop)
xamarin_assertion_message ("Cannot unwind managed frames for Objective-C exceptions when the GC is in cooperative mode.");
@throw ns_exception;
break;
case MarshalObjectiveCExceptionModeThrowManagedException:
exc_handle = [[ns_exception userInfo] objectForKey: @"XamarinManagedExceptionHandle"];
if (exc_handle != NULL) {
GCHandle handle = [exc_handle getHandle];
[coop runtime] add a GC transition and relax some GC related assertions (#7036) * [coop] add missing gc_unsafe transition frame #3: 0x02a39fd4 monotouchtest`log_callback(log_domain=0x00000000, log_level="error", message="../../../../../mono/metadata/object.c:1905: Expected GC Unsafe mode but was in STATE_BLOCKING state", fatal=4, user_data=0x00000000) at runtime.m:1251:3 frame #4: 0x02a03f78 monotouchtest`monoeg_g_logv_nofree(log_domain=0x00000000, log_level=G_LOG_LEVEL_ERROR, format=<unavailable>, args=<unavailable>) at goutput.c:149:2 [opt] frame #5: 0x02a03f14 monotouchtest`monoeg_g_logv(log_domain=<unavailable>, log_level=<unavailable>, format=<unavailable>, args=<unavailable>) at goutput.c:156:10 [opt] frame #6: 0x02a03fa8 monotouchtest`monoeg_g_log(log_domain=<unavailable>, log_level=<unavailable>, format=<unavailable>) at goutput.c:165:2 [opt] frame #7: 0x029e54f8 monotouchtest`assert_gc_unsafe_mode(file="../../../../../mono/metadata/object.c", lineno=1905) at checked-build.c:396:3 [opt] frame #8: 0x0294ef98 monotouchtest`mono_class_vtable_checked(domain=0x16d87b70, klass=0x17bfab98, error=0x19498a08) at object.c:1905:2 [opt] frame #9: 0x0298f0dc monotouchtest`get_current_thread_ptr_for_domain(domain=0x16d87b70, thread=0x03f645d0) at threads.c:635:2 [opt] frame #10: 0x0298d9dc monotouchtest`mono_thread_current at threads.c:2026:23 [opt] frame #11: 0x02992a80 monotouchtest`mono_runtime_set_pending_exception(exc=0x0357e4a0, overwrite=0) at threads.c:5176:23 [opt] frame #12: 0x02a3c650 monotouchtest`::xamarin_process_nsexception_using_mode(ns_exception=name: "System.ApplicationException" - reason: "3,14", throwManagedAsDefault=false) at runtime.m:2369:4 frame #13: 0x02a3c478 monotouchtest`::xamarin_process_nsexception(ns_exception=name: "System.ApplicationException" - reason: "3,14") at runtime.m:2336:2 frame #14: 0x0270b488 monotouchtest`::xamarin_pinvoke_wrapper_objc_msgSendSuper18(__p__0=0x0357d0f0, __p__1=0x02ad1ef2) at pinvokes.m:5732:4 frame #15: 0x02a64764 monotouchtest`do_icall(frame=<unavailable>, sig=0x17d70bf0, op=<unavailable>, sp=0x19498fa0, ptr=<unavailable>, save_last_error=0) at interp.c:1947:3 [opt] frame #16: 0x02a632b8 monotouchtest`do_icall_wrapper(frame=0x194991b0, sig=0x17d70bf0, op=547, sp=0x19498fb0, ptr=0x0270b408, save_last_error=0) at interp.c:2037:7 [opt] Messaging::void_objc_msgSendSuper @ 396160394 "calli.nat.fast" || frame #17: 0x02a51d5c monotouchtest`interp_exec_method_full(frame=0x194991b0, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3229:9 [opt] ObjCExceptionTest::InvokeManagedExceptionThrower @ 396160296 "vcall" || frame #18: 0x02a524e8 monotouchtest`interp_exec_method_full(frame=0x19499370, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3400:4 [opt] ExceptionsTest::ManagedExceptionPassthrough @ 396150902 "vcallvirt.fast" || frame #19: 0x02a521cc monotouchtest`interp_exec_method_full(frame=0x194994d0, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3325:4 [opt] Object::runtime_invoke_direct_void__this__ @ 401347822 "vcall" || frame #20: 0x02a524e8 monotouchtest`interp_exec_method_full(frame=0x19499588, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3400:4 [opt] frame #21: 0x02a506f8 monotouchtest`interp_runtime_invoke(method=<unavailable>, obj=0x0357c4f0, params=0x00000000, exc=0x1949965c, error=0x194998b8) at interp.c:1766:2 [opt] frame #22: 0x028a739c monotouchtest`mono_jit_runtime_invoke(method=0x17534858, obj=<unavailable>, params=0x00000000, exc=<unavailable>, error=0x194998b8) at mini-runtime.c:3170:12 [opt] frame #23: 0x02951cfc monotouchtest`do_runtime_invoke(method=0x17534858, obj=0x0357c4f0, params=0x00000000, exc=0x00000000, error=0x194998b8) at object.c:3017:11 [opt] frame #24: 0x0294e6d4 monotouchtest`mono_runtime_invoke_checked(method=<unavailable>, obj=<unavailable>, params=<unavailable>, error=<unavailable>) at class-getters.h:24:1 [opt] [artificial] frame #25: 0x02955408 monotouchtest`mono_runtime_try_invoke_array(method=0x17534858, obj=0x0357c4f0, params=0x00000000, exc=0x00000000, error=0x194998b8) at object.c:5564:10 [opt] frame #26: 0x02905fac monotouchtest`ves_icall_InternalInvoke(method=<unavailable>, this_arg=<unavailable>, params=0x00000000, exc=0x19499f0c) at icall.c:3753:8 [opt] frame #27: 0x02a64788 monotouchtest`do_icall(frame=<unavailable>, sig=0x17496978, op=<unavailable>, sp=0x19499d88, ptr=<unavailable>, save_last_error=0) at interp.c:1982:20 [opt] frame #28: 0x02a632b8 monotouchtest`do_icall_wrapper(frame=0x19499fb0, sig=0x17496978, op=552, sp=0x19499da0, ptr=0x02905bec, save_last_error=0) at interp.c:2037:7 [opt] RuntimeMethodInfo::InternalInvoke @ 400702536 "calli.nat.fast" || frame #29: 0x02a51d5c monotouchtest`interp_exec_method_full(frame=0x19499fb0, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3229:9 [opt] RuntimeMethodInfo::Invoke @ 400701852 "call" || frame #30: 0x02a524e8 monotouchtest`interp_exec_method_full(frame=0x1949a110, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3400:4 [opt] MethodBase::Invoke @ 400700872 "callvirt.fast" || frame #31: 0x02a521cc monotouchtest`interp_exec_method_full(frame=0x1949a280, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3325:4 [opt] * [coop] relax a couple GC state assertions * [coop] update doc for enabling GC assertions * [coop] relax a GC assertion in release trampoline
2019-09-23 23:08:25 +03:00
MONO_ENTER_GC_UNSAFE;
MonoObject *exc = xamarin_gchandle_get_target (handle);
mono_runtime_set_pending_exception ((MonoException *) exc, false);
xamarin_mono_object_release (&exc);
[coop runtime] add a GC transition and relax some GC related assertions (#7036) * [coop] add missing gc_unsafe transition frame #3: 0x02a39fd4 monotouchtest`log_callback(log_domain=0x00000000, log_level="error", message="../../../../../mono/metadata/object.c:1905: Expected GC Unsafe mode but was in STATE_BLOCKING state", fatal=4, user_data=0x00000000) at runtime.m:1251:3 frame #4: 0x02a03f78 monotouchtest`monoeg_g_logv_nofree(log_domain=0x00000000, log_level=G_LOG_LEVEL_ERROR, format=<unavailable>, args=<unavailable>) at goutput.c:149:2 [opt] frame #5: 0x02a03f14 monotouchtest`monoeg_g_logv(log_domain=<unavailable>, log_level=<unavailable>, format=<unavailable>, args=<unavailable>) at goutput.c:156:10 [opt] frame #6: 0x02a03fa8 monotouchtest`monoeg_g_log(log_domain=<unavailable>, log_level=<unavailable>, format=<unavailable>) at goutput.c:165:2 [opt] frame #7: 0x029e54f8 monotouchtest`assert_gc_unsafe_mode(file="../../../../../mono/metadata/object.c", lineno=1905) at checked-build.c:396:3 [opt] frame #8: 0x0294ef98 monotouchtest`mono_class_vtable_checked(domain=0x16d87b70, klass=0x17bfab98, error=0x19498a08) at object.c:1905:2 [opt] frame #9: 0x0298f0dc monotouchtest`get_current_thread_ptr_for_domain(domain=0x16d87b70, thread=0x03f645d0) at threads.c:635:2 [opt] frame #10: 0x0298d9dc monotouchtest`mono_thread_current at threads.c:2026:23 [opt] frame #11: 0x02992a80 monotouchtest`mono_runtime_set_pending_exception(exc=0x0357e4a0, overwrite=0) at threads.c:5176:23 [opt] frame #12: 0x02a3c650 monotouchtest`::xamarin_process_nsexception_using_mode(ns_exception=name: "System.ApplicationException" - reason: "3,14", throwManagedAsDefault=false) at runtime.m:2369:4 frame #13: 0x02a3c478 monotouchtest`::xamarin_process_nsexception(ns_exception=name: "System.ApplicationException" - reason: "3,14") at runtime.m:2336:2 frame #14: 0x0270b488 monotouchtest`::xamarin_pinvoke_wrapper_objc_msgSendSuper18(__p__0=0x0357d0f0, __p__1=0x02ad1ef2) at pinvokes.m:5732:4 frame #15: 0x02a64764 monotouchtest`do_icall(frame=<unavailable>, sig=0x17d70bf0, op=<unavailable>, sp=0x19498fa0, ptr=<unavailable>, save_last_error=0) at interp.c:1947:3 [opt] frame #16: 0x02a632b8 monotouchtest`do_icall_wrapper(frame=0x194991b0, sig=0x17d70bf0, op=547, sp=0x19498fb0, ptr=0x0270b408, save_last_error=0) at interp.c:2037:7 [opt] Messaging::void_objc_msgSendSuper @ 396160394 "calli.nat.fast" || frame #17: 0x02a51d5c monotouchtest`interp_exec_method_full(frame=0x194991b0, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3229:9 [opt] ObjCExceptionTest::InvokeManagedExceptionThrower @ 396160296 "vcall" || frame #18: 0x02a524e8 monotouchtest`interp_exec_method_full(frame=0x19499370, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3400:4 [opt] ExceptionsTest::ManagedExceptionPassthrough @ 396150902 "vcallvirt.fast" || frame #19: 0x02a521cc monotouchtest`interp_exec_method_full(frame=0x194994d0, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3325:4 [opt] Object::runtime_invoke_direct_void__this__ @ 401347822 "vcall" || frame #20: 0x02a524e8 monotouchtest`interp_exec_method_full(frame=0x19499588, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3400:4 [opt] frame #21: 0x02a506f8 monotouchtest`interp_runtime_invoke(method=<unavailable>, obj=0x0357c4f0, params=0x00000000, exc=0x1949965c, error=0x194998b8) at interp.c:1766:2 [opt] frame #22: 0x028a739c monotouchtest`mono_jit_runtime_invoke(method=0x17534858, obj=<unavailable>, params=0x00000000, exc=<unavailable>, error=0x194998b8) at mini-runtime.c:3170:12 [opt] frame #23: 0x02951cfc monotouchtest`do_runtime_invoke(method=0x17534858, obj=0x0357c4f0, params=0x00000000, exc=0x00000000, error=0x194998b8) at object.c:3017:11 [opt] frame #24: 0x0294e6d4 monotouchtest`mono_runtime_invoke_checked(method=<unavailable>, obj=<unavailable>, params=<unavailable>, error=<unavailable>) at class-getters.h:24:1 [opt] [artificial] frame #25: 0x02955408 monotouchtest`mono_runtime_try_invoke_array(method=0x17534858, obj=0x0357c4f0, params=0x00000000, exc=0x00000000, error=0x194998b8) at object.c:5564:10 [opt] frame #26: 0x02905fac monotouchtest`ves_icall_InternalInvoke(method=<unavailable>, this_arg=<unavailable>, params=0x00000000, exc=0x19499f0c) at icall.c:3753:8 [opt] frame #27: 0x02a64788 monotouchtest`do_icall(frame=<unavailable>, sig=0x17496978, op=<unavailable>, sp=0x19499d88, ptr=<unavailable>, save_last_error=0) at interp.c:1982:20 [opt] frame #28: 0x02a632b8 monotouchtest`do_icall_wrapper(frame=0x19499fb0, sig=0x17496978, op=552, sp=0x19499da0, ptr=0x02905bec, save_last_error=0) at interp.c:2037:7 [opt] RuntimeMethodInfo::InternalInvoke @ 400702536 "calli.nat.fast" || frame #29: 0x02a51d5c monotouchtest`interp_exec_method_full(frame=0x19499fb0, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3229:9 [opt] RuntimeMethodInfo::Invoke @ 400701852 "call" || frame #30: 0x02a524e8 monotouchtest`interp_exec_method_full(frame=0x1949a110, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3400:4 [opt] MethodBase::Invoke @ 400700872 "callvirt.fast" || frame #31: 0x02a521cc monotouchtest`interp_exec_method_full(frame=0x1949a280, context=<unavailable>, clause_args=<unavailable>, error=<unavailable>) at interp.c:3325:4 [opt] * [coop] relax a couple GC state assertions * [coop] update doc for enabling GC assertions * [coop] relax a GC assertion in release trampoline
2019-09-23 23:08:25 +03:00
MONO_EXIT_GC_UNSAFE;
} else {
GCHandle handle = xamarin_create_ns_exception (ns_exception, &exception_gchandle);
if (exception_gchandle != INVALID_GCHANDLE) {
PRINT (PRODUCT ": Got an exception while creating a managed NSException wrapper (will throw this exception instead):");
PRINT ("%@", xamarin_print_all_exceptions (exception_gchandle));
handle = exception_gchandle;
exception_gchandle = INVALID_GCHANDLE;
}
if (output_exception == NULL) {
MONO_ENTER_GC_UNSAFE;
MonoObject *exc = xamarin_gchandle_get_target (handle);
mono_runtime_set_pending_exception ((MonoException *) exc, false);
xamarin_mono_object_release (&exc);
xamarin_gchandle_free (handle);
MONO_EXIT_GC_UNSAFE;
} else {
*output_exception = handle;
}
}
break;
case MarshalObjectiveCExceptionModeAbort:
default:
xamarin_assertion_message ("Aborting due to unhandled Objective-C exception:\n%s\n", [[ns_exception description] UTF8String]);
break;
}
}
// Since this method may not return, it will release the given exception object.
void
xamarin_process_managed_exception (MonoObject *exception)
{
if (exception == NULL)
return;
MarshalManagedExceptionMode mode;
GCHandle exception_gchandle = INVALID_GCHANDLE;
GCHandle handle = xamarin_gchandle_new (exception, false);
mode = xamarin_on_marshal_managed_exception (handle, &exception_gchandle);
xamarin_gchandle_free (handle);
if (exception_gchandle != INVALID_GCHANDLE) {
PRINT (PRODUCT ": Got an exception while executing the MarshalManagedException event (this exception will be ignored):");
PRINT ("%@", xamarin_print_all_exceptions (exception_gchandle));
xamarin_gchandle_free (exception_gchandle);
exception_gchandle = INVALID_GCHANDLE;
mode = MarshalManagedExceptionModeDefault;
}
if (mode == MarshalManagedExceptionModeDefault) {
#if DOTNET
mode = MarshalManagedExceptionModeThrowObjectiveCException;
#else
mode = xamarin_is_gc_coop ? MarshalManagedExceptionModeThrowObjectiveCException : MarshalManagedExceptionModeUnwindNativeCode;
#endif
}
[runtime] Don't try to log an exception for GCHandle that has been freed. (#14003) It does exactly what you'd think it does: nothing helpful at all. Make sure to use a GCHandle that's alive instead. Output before fix: Xamarin.Mac: Processing managed exception for exception marshalling (mode: 2): Failed to print exception: System.NullReferenceException: Object reference not set to an instance of an object. at ObjCRuntime.Runtime.PrintException(Exception exc, Boolean isInnerException, StringBuilder sb) at ObjCRuntime.Runtime.PrintAllExceptions(IntPtr exception_gchandle) Output after fix: Xamarin.Mac: Processing managed exception for exception marshalling (mode: 2): Failed to lookup the required marshalling information. Additional information: Selector: conformsToProtocol: Type: ViewController (ObjCRuntime.RuntimeException) Failed to get the 'this' instance in a method call to templ.ViewController.InvokeConformsToProtocol. (ObjCRuntime.RuntimeException) at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc) at ObjCRuntime.Runtime.GetMethodAndObjectForSelector(IntPtr klass, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc) at ObjCRuntime.Runtime.get_method_and_object_for_selector(IntPtr cls, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc, IntPtr& exception_gchandle) Failed to marshal the Objective-C object 0x7f89e6c2a2a0 (type: ViewController). Could not find an existing managed instance for this object, nor was it possible to create a new managed instance (because the type 'templ.ViewController' does not have a constructor that takes one NativeHandle argument). (ObjCRuntime.RuntimeException) at ObjCRuntime.Runtime.MissingCtor(IntPtr ptr, IntPtr klass, Type type, MissingCtorResolution resolution) at ObjCRuntime.Runtime.ConstructNSObject[T](IntPtr ptr, Type type, MissingCtorResolution missingCtorResolution) at ObjCRuntime.Runtime.ConstructNSObject(IntPtr ptr, IntPtr klass, MissingCtorResolution missingCtorResolution) at ObjCRuntime.Runtime.GetNSObject(IntPtr ptr, MissingCtorResolution missingCtorResolution, Boolean evenInFinalizerQueue) at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
2022-02-02 10:38:52 +03:00
xamarin_log_managed_exception (exception, mode);
switch (mode) {
#if !defined (CORECLR_RUNTIME) // CoreCLR won't unwind through native frames, so we'll have to abort (in the default case statement)
case MarshalManagedExceptionModeDisable:
case MarshalManagedExceptionModeUnwindNativeCode:
if (xamarin_is_gc_coop)
xamarin_assertion_message ("Cannot unwind native frames for managed exceptions when the GC is in cooperative mode.");
Use ExceptionDispatchInfo to keep stack traces when unhandled exceptions are marshaled. Improves bug #45742 a bit. (#1040) Example code: public override void ViewDidLoad () { throw new Exception ("USELESS"); } Current output with unhandled exceptions that are marshaled: Unhandled Exception: System.Exception: USELESS at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 [ERROR] FATAL UNHANDLED EXCEPTION: UselessExceptions.CustomException: USELESS at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 Note how the managed frame where the exception was thrown does not show up. This is because we technically catch exceptions when marshaling them, and then later we call mono_raise_exception to raise the same exception. Unfortunately that leads to overwriting the initial stack trace, and we end up with a stack trace that does not include the location where the original exception was thrown. So instead of calling mono_raise_exception to rethrow the same exception, we use ExceptionDispatchInfo, which is able to capture the stack trace for both the original exception and the new exception, producing the following output: System.Exception: USELESS at UselessExceptions.ViewController.ViewDidLoad () [0x0000c] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/ViewController.cs:27 --- End of stack trace from previous location where exception was thrown --- at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 [ERROR] FATAL UNHANDLED EXCEPTION: UselessExceptions.CustomException: USELESS at UselessExceptions.ViewController.ViewDidLoad () [0x0000c] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/ViewController.cs:27 --- End of stack trace from previous location where exception was thrown --- at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 Incidently this is how Xamarin.Android does it [1]. [1]: https://github.com/xamarin/Java.Interop/commit/9387f2fe16300ea8eb3d5270e50f0513d251bd62 https://bugzilla.xamarin.com/show_bug.cgi?id=45742
2016-10-27 21:03:11 +03:00
//
// We want to maintain the original stack trace of the exception, but unfortunately
// calling mono_raise_exception directly with the original exception will overwrite
// the original stack trace.
//
// The good news is that the managed ExceptionDispatchInfo class is able to capture
// a stack trace for an exception and show it later.
//
// The xamarin_rethrow_managed_exception method will use ExceptionDispatchInfo
// to throw an exception that contains the original stack trace.
//
handle = xamarin_gchandle_new (exception, false);
Use ExceptionDispatchInfo to keep stack traces when unhandled exceptions are marshaled. Improves bug #45742 a bit. (#1040) Example code: public override void ViewDidLoad () { throw new Exception ("USELESS"); } Current output with unhandled exceptions that are marshaled: Unhandled Exception: System.Exception: USELESS at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 [ERROR] FATAL UNHANDLED EXCEPTION: UselessExceptions.CustomException: USELESS at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 Note how the managed frame where the exception was thrown does not show up. This is because we technically catch exceptions when marshaling them, and then later we call mono_raise_exception to raise the same exception. Unfortunately that leads to overwriting the initial stack trace, and we end up with a stack trace that does not include the location where the original exception was thrown. So instead of calling mono_raise_exception to rethrow the same exception, we use ExceptionDispatchInfo, which is able to capture the stack trace for both the original exception and the new exception, producing the following output: System.Exception: USELESS at UselessExceptions.ViewController.ViewDidLoad () [0x0000c] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/ViewController.cs:27 --- End of stack trace from previous location where exception was thrown --- at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 [ERROR] FATAL UNHANDLED EXCEPTION: UselessExceptions.CustomException: USELESS at UselessExceptions.ViewController.ViewDidLoad () [0x0000c] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/ViewController.cs:27 --- End of stack trace from previous location where exception was thrown --- at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 Incidently this is how Xamarin.Android does it [1]. [1]: https://github.com/xamarin/Java.Interop/commit/9387f2fe16300ea8eb3d5270e50f0513d251bd62 https://bugzilla.xamarin.com/show_bug.cgi?id=45742
2016-10-27 21:03:11 +03:00
xamarin_rethrow_managed_exception (handle, &exception_gchandle);
xamarin_gchandle_free (handle);
Use ExceptionDispatchInfo to keep stack traces when unhandled exceptions are marshaled. Improves bug #45742 a bit. (#1040) Example code: public override void ViewDidLoad () { throw new Exception ("USELESS"); } Current output with unhandled exceptions that are marshaled: Unhandled Exception: System.Exception: USELESS at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 [ERROR] FATAL UNHANDLED EXCEPTION: UselessExceptions.CustomException: USELESS at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 Note how the managed frame where the exception was thrown does not show up. This is because we technically catch exceptions when marshaling them, and then later we call mono_raise_exception to raise the same exception. Unfortunately that leads to overwriting the initial stack trace, and we end up with a stack trace that does not include the location where the original exception was thrown. So instead of calling mono_raise_exception to rethrow the same exception, we use ExceptionDispatchInfo, which is able to capture the stack trace for both the original exception and the new exception, producing the following output: System.Exception: USELESS at UselessExceptions.ViewController.ViewDidLoad () [0x0000c] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/ViewController.cs:27 --- End of stack trace from previous location where exception was thrown --- at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 [ERROR] FATAL UNHANDLED EXCEPTION: UselessExceptions.CustomException: USELESS at UselessExceptions.ViewController.ViewDidLoad () [0x0000c] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/ViewController.cs:27 --- End of stack trace from previous location where exception was thrown --- at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 Incidently this is how Xamarin.Android does it [1]. [1]: https://github.com/xamarin/Java.Interop/commit/9387f2fe16300ea8eb3d5270e50f0513d251bd62 https://bugzilla.xamarin.com/show_bug.cgi?id=45742
2016-10-27 21:03:11 +03:00
if (exception_gchandle == INVALID_GCHANDLE) {
Use ExceptionDispatchInfo to keep stack traces when unhandled exceptions are marshaled. Improves bug #45742 a bit. (#1040) Example code: public override void ViewDidLoad () { throw new Exception ("USELESS"); } Current output with unhandled exceptions that are marshaled: Unhandled Exception: System.Exception: USELESS at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 [ERROR] FATAL UNHANDLED EXCEPTION: UselessExceptions.CustomException: USELESS at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 Note how the managed frame where the exception was thrown does not show up. This is because we technically catch exceptions when marshaling them, and then later we call mono_raise_exception to raise the same exception. Unfortunately that leads to overwriting the initial stack trace, and we end up with a stack trace that does not include the location where the original exception was thrown. So instead of calling mono_raise_exception to rethrow the same exception, we use ExceptionDispatchInfo, which is able to capture the stack trace for both the original exception and the new exception, producing the following output: System.Exception: USELESS at UselessExceptions.ViewController.ViewDidLoad () [0x0000c] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/ViewController.cs:27 --- End of stack trace from previous location where exception was thrown --- at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 [ERROR] FATAL UNHANDLED EXCEPTION: UselessExceptions.CustomException: USELESS at UselessExceptions.ViewController.ViewDidLoad () [0x0000c] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/ViewController.cs:27 --- End of stack trace from previous location where exception was thrown --- at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 Incidently this is how Xamarin.Android does it [1]. [1]: https://github.com/xamarin/Java.Interop/commit/9387f2fe16300ea8eb3d5270e50f0513d251bd62 https://bugzilla.xamarin.com/show_bug.cgi?id=45742
2016-10-27 21:03:11 +03:00
PRINT (PRODUCT ": Did not get a rethrow exception, will throw the original exception. The original stack trace will be lost.");
} else {
xamarin_mono_object_release (&exception);
exception = xamarin_gchandle_get_target (exception_gchandle);
xamarin_gchandle_free (exception_gchandle);
Use ExceptionDispatchInfo to keep stack traces when unhandled exceptions are marshaled. Improves bug #45742 a bit. (#1040) Example code: public override void ViewDidLoad () { throw new Exception ("USELESS"); } Current output with unhandled exceptions that are marshaled: Unhandled Exception: System.Exception: USELESS at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 [ERROR] FATAL UNHANDLED EXCEPTION: UselessExceptions.CustomException: USELESS at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 Note how the managed frame where the exception was thrown does not show up. This is because we technically catch exceptions when marshaling them, and then later we call mono_raise_exception to raise the same exception. Unfortunately that leads to overwriting the initial stack trace, and we end up with a stack trace that does not include the location where the original exception was thrown. So instead of calling mono_raise_exception to rethrow the same exception, we use ExceptionDispatchInfo, which is able to capture the stack trace for both the original exception and the new exception, producing the following output: System.Exception: USELESS at UselessExceptions.ViewController.ViewDidLoad () [0x0000c] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/ViewController.cs:27 --- End of stack trace from previous location where exception was thrown --- at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 [ERROR] FATAL UNHANDLED EXCEPTION: UselessExceptions.CustomException: USELESS at UselessExceptions.ViewController.ViewDidLoad () [0x0000c] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/ViewController.cs:27 --- End of stack trace from previous location where exception was thrown --- at (wrapper managed-to-native) AppKit.NSApplication:NSApplicationMain (int,string[]) at AppKit.NSApplication.Main (System.String[] args) [0x00041] in /work/maccore/master/xamarin-macios/src/AppKit/NSApplication.cs:98 at UselessExceptions.MainClass.Main (System.String[] args) [0x00007] in /Users/rolf/Downloads/filed-bug-test-cases-master/Xamarin/bxc45742/Main.cs:10 Incidently this is how Xamarin.Android does it [1]. [1]: https://github.com/xamarin/Java.Interop/commit/9387f2fe16300ea8eb3d5270e50f0513d251bd62 https://bugzilla.xamarin.com/show_bug.cgi?id=45742
2016-10-27 21:03:11 +03:00
}
mono_raise_exception ((MonoException *) exception);
#endif
break;
case MarshalManagedExceptionModeThrowObjectiveCException: {
GCHandle handle = xamarin_gchandle_new (exception, false);
NSException *ns_exc = xamarin_unwrap_ns_exception (handle, &exception_gchandle);
if (exception_gchandle != INVALID_GCHANDLE) {
PRINT (PRODUCT ": Got an exception while unwrapping a managed NSException wrapper (this exception will be ignored):");
PRINT ("%@", xamarin_print_all_exceptions (exception_gchandle));
xamarin_gchandle_free (exception_gchandle);
exception_gchandle = INVALID_GCHANDLE;
ns_exc = NULL;
}
if (ns_exc != NULL) {
xamarin_gchandle_free (handle);
} else {
// Strangely enough the thread might be detached, if xamarin_process_managed_exception was called from
// xamarin_ftnptr_exception_handler for an exception that occurred in a reverse delegate that
// was called on a detached thread, since in that case the native-to-managed wrapper will have
// returned the thread to a detached state after calling the managed function.
NSString *name;
NSString *reason;
NSDictionary *userInfo;
char *fullname;
MONO_THREAD_ATTACH; // COOP: will switch to GC_UNSAFE
fullname = xamarin_get_object_type_fullname (handle, &exception_gchandle);
if (exception_gchandle != INVALID_GCHANDLE) {
PRINT (PRODUCT ": Got an exception when trying to get the typename for an exception (this exception will be ignored):");
PRINT ("%@", xamarin_print_all_exceptions (exception_gchandle));
xamarin_gchandle_free (exception_gchandle);
exception_gchandle = INVALID_GCHANDLE;
name = @"Unknown type";
} else {
name = [NSString stringWithUTF8String: fullname];
xamarin_free (fullname);
}
[runtime] Use the full managed description (including inner exceptions) as the reason when creating an NSException from a managed exception. (#14002) This makes diagnosing what happens much easier in some cases. Exhibit A, pre fix: *** Terminating app due to uncaught exception 'ObjCRuntime.RuntimeException', reason: 'Failed to lookup the required marshalling information. Additional information: Selector: conformsToProtocol: Type: ViewController Exhibit B, post fix: *** Terminating app due to uncaught exception 'ObjCRuntime.RuntimeException', reason: 'Failed to lookup the required marshalling information. Additional information: Selector: conformsToProtocol: Type: ViewController (ObjCRuntime.RuntimeException) Failed to get the 'this' instance in a method call to templ.ViewController.InvokeConformsToProtocol. (ObjCRuntime.RuntimeException) at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc) at ObjCRuntime.Runtime.GetMethodAndObjectForSelector(IntPtr klass, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc) at ObjCRuntime.Runtime.get_method_and_object_for_selector(IntPtr cls, IntPtr sel, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc, IntPtr& exception_gchandle) Failed to marshal the Objective-C object 0x7f813fd2f470 (type: ViewController). Could not find an existing managed instance for this object, nor was it possible to create a new managed instance (because the type 'templ.ViewController' does not have a constructor that takes one NativeHandle argument). (ObjCRuntime.RuntimeException) at ObjCRuntime.Runtime.MissingCtor(IntPtr ptr, IntPtr klass, Type type, MissingCtorResolution resolution) at ObjCRuntime.Runtime.ConstructNSObject[T](IntPtr ptr, Type type, MissingCtorResolution missingCtorResolution) at ObjCRuntime.Runtime.ConstructNSObject(IntPtr ptr, IntPtr klass, MissingCtorResolution missingCtorResolution) at ObjCRuntime.Runtime.GetNSObject(IntPtr ptr, MissingCtorResolution missingCtorResolution, Boolean evenInFinalizerQueue) at Registrar.DynamicRegistrar.GetMethodDescriptionAndObject(Type type, IntPtr selector, Boolean is_static, IntPtr obj, IntPtr& mthis, IntPtr desc)
2022-02-02 18:22:34 +03:00
reason = xamarin_print_all_exceptions (handle);
if (exception_gchandle != INVALID_GCHANDLE) {
PRINT (PRODUCT ": Got an exception when trying to get the message for an exception (this exception will be ignored):");
PRINT ("%@", xamarin_print_all_exceptions (exception_gchandle));
xamarin_gchandle_free (exception_gchandle);
exception_gchandle = INVALID_GCHANDLE;
reason = @"Unknown message";
}
userInfo = [NSDictionary dictionaryWithObject: [XamarinGCHandle createWithHandle: handle] forKey: @"XamarinManagedExceptionHandle"];
MONO_THREAD_DETACH; // COOP: this will switch to GC_SAFE
ns_exc = [[NSException alloc] initWithName: name reason: reason userInfo: userInfo];
}
xamarin_mono_object_release (&exception);
@throw ns_exc;
}
#if defined (CORECLR_RUNTIME)
case MarshalManagedExceptionModeDisable:
case MarshalManagedExceptionModeUnwindNativeCode:
#endif
case MarshalManagedExceptionModeAbort:
default:
handle = xamarin_gchandle_new (exception, false);
const char *msg = [xamarin_print_all_exceptions (handle) UTF8String];
xamarin_gchandle_free (handle);
xamarin_assertion_message ("Aborting due to trying to marshal managed exception:\n%s\n", msg);
break;
}
}
void
xamarin_throw_product_exception (int code, const char *message)
{
xamarin_process_managed_exception_gchandle (xamarin_create_product_exception (code, message));
}
GCHandle
xamarin_create_product_exception (int code, const char *message)
{
return xamarin_create_product_exception_with_inner_exception (code, 0, message);
}
GCHandle
xamarin_create_product_exception_with_inner_exception (int code, GCHandle inner_exception_gchandle, const char *message)
{
GCHandle exception_gchandle = INVALID_GCHANDLE;
GCHandle handle = xamarin_create_product_exception_for_error (code, inner_exception_gchandle, message, &exception_gchandle);
if (exception_gchandle != INVALID_GCHANDLE)
return exception_gchandle;
return handle;
}
#if !DOTNET
void
xamarin_insert_dllmap ()
{
#if defined (OBJC_ZEROCOST_EXCEPTIONS) && (defined (__i386__) || defined (__x86_64__) || defined (__arm64__))
if (xamarin_marshal_objectivec_exception_mode == MarshalObjectiveCExceptionModeDisable)
return;
#if DYLIB
const char *lib = "libxammac.dylib";
#else
const char *lib = "__Internal";
#endif
mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSend", lib, "xamarin_dyn_objc_msgSend");
mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSendSuper", lib, "xamarin_dyn_objc_msgSendSuper");
#if !defined (__arm64__)
mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSend_stret", lib, "xamarin_dyn_objc_msgSend_stret");
mono_dllmap_insert (NULL, "/usr/lib/libobjc.dylib", "objc_msgSendSuper_stret", lib, "xamarin_dyn_objc_msgSendSuper_stret");
#endif
LOG (PRODUCT ": Added dllmap for objc_msgSend");
#endif // defined (__i386__) || defined (__x86_64__) || defined (__arm64__)
}
#endif // !DOTNET
#if DOTNET
// List all the assemblies that we can find in the app bundle in:
// - The bundle directory
// - The runtimeidentifier-specific subdirectory
// Caller must free the return value using xamarin_free.
char *
xamarin_compute_trusted_platform_assemblies ()
{
const char *bundle_path = xamarin_get_bundle_path ();
NSMutableArray<NSString *> *files = [NSMutableArray array];
NSMutableArray<NSString *> *exes = [NSMutableArray array];
NSMutableArray<NSString *> *directories = [NSMutableArray array];
[directories addObject: [NSString stringWithUTF8String: bundle_path]];
[directories addObject: [NSString stringWithFormat: @"%s/.xamarin/%s", bundle_path, RUNTIMEIDENTIFIER]];
NSFileManager *manager = [NSFileManager defaultManager];
for (NSString *dir in directories) {
NSDirectoryEnumerator *enumerator = [manager enumeratorAtURL:[NSURL fileURLWithPath: dir]
includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey]
options:NSDirectoryEnumerationSkipsSubdirectoryDescendants
errorHandler:nil];
for (NSURL *file in enumerator) {
// skip subdirectories
NSNumber *isDirectory = nil;
if (![file getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil] || [isDirectory boolValue])
continue;
NSString *name = nil;
if (![file getResourceValue:&name forKey:NSURLNameKey error:nil])
continue;
if ([name length] < 4)
continue;
// We want dlls and exes
if ([name compare: @".dll" options: NSCaseInsensitiveSearch range: NSMakeRange ([name length] - 4, 4)] == NSOrderedSame) {
[files addObject: [dir stringByAppendingPathComponent: name]];
} else if ([name compare: @".exe" options: NSCaseInsensitiveSearch range: NSMakeRange ([name length] - 4, 4)] == NSOrderedSame) {
[exes addObject: [dir stringByAppendingPathComponent: name]];
}
}
}
// Any .exe files must be at the end, due to https://github.com/dotnet/runtime/issues/62735
[files addObjectsFromArray: exes];
// Join them all together with a colon separating them
NSString *joined = [files componentsJoinedByString: @":"];
char *rv = xamarin_strdup_printf ("%s", [joined UTF8String]);
return rv;
}
char *
xamarin_compute_native_dll_search_directories ()
{
const char *bundle_path = xamarin_get_bundle_path ();
NSMutableArray<NSString *> *directories = [NSMutableArray array];
// Native libraries might be in the app bundle
[directories addObject: [NSString stringWithUTF8String: bundle_path]];
// They won't be in the runtimeidentifier-specific directory (because they get lipo'ed into a fat file instead)
// However, might also be in the Resources/lib directory
[directories addObject: [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: @"lib"]];
// Missing:
// * The parent app bundle if launched from an app extension. This requires adding app extension tests, which we currently don't have.
// Remove the ones that don't exist
NSFileManager *manager = [NSFileManager defaultManager];
for (int i = (int) [directories count] - 1; i >= 0; i--) {
NSString *dir = [directories objectAtIndex: (NSUInteger) i];
BOOL isDirectory;
if ([manager fileExistsAtPath: dir isDirectory: &isDirectory] && isDirectory)
continue;
[directories removeObjectAtIndex: (NSUInteger) i];
}
// Join them all together with a colon separating them
NSString *joined = [directories componentsJoinedByString: @":"];
char *rv = xamarin_strdup_printf ("%s", [joined UTF8String]);
return rv;
}
void
xamarin_vm_initialize ()
{
char *pinvokeOverride = xamarin_strdup_printf ("%p", &xamarin_pinvoke_override);
char *icu_dat_file_path = NULL;
char path [1024];
if (!xamarin_locate_app_resource (xamarin_icu_dat_file_name, path, sizeof (path))) {
LOG (PRODUCT ": Could not locate the ICU data file '%s' in the app bundle.\n", xamarin_icu_dat_file_name);
} else {
icu_dat_file_path = path;
}
char *trusted_platform_assemblies = xamarin_compute_trusted_platform_assemblies ();
char *native_dll_search_directories = xamarin_compute_native_dll_search_directories ();
// All the properties we pass here must also be listed in the _RuntimeConfigReservedProperties item group
// for the _CreateRuntimeConfiguration target in dotnet/targets/Xamarin.Shared.Sdk.targets.
const char *propertyKeys[] = {
"APP_CONTEXT_BASE_DIRECTORY", // path to where the managed assemblies are (usually at least - RID-specific assemblies will be in subfolders)
"APP_PATHS",
"PINVOKE_OVERRIDE",
"ICU_DAT_FILE_PATH",
"TRUSTED_PLATFORM_ASSEMBLIES",
"NATIVE_DLL_SEARCH_DIRECTORIES",
"RUNTIME_IDENTIFIER",
};
const char *propertyValues[] = {
xamarin_get_bundle_path (),
xamarin_get_bundle_path (),
pinvokeOverride,
icu_dat_file_path,
trusted_platform_assemblies,
native_dll_search_directories,
RUNTIMEIDENTIFIER,
};
static_assert (sizeof (propertyKeys) == sizeof (propertyValues), "The number of keys and values must be the same.");
int propertyCount = sizeof (propertyValues) / sizeof (propertyValues [0]);
bool rv = xamarin_bridge_vm_initialize (propertyCount, propertyKeys, propertyValues);
xamarin_free (pinvokeOverride);
xamarin_free (trusted_platform_assemblies);
xamarin_free (native_dll_search_directories);
if (!rv)
xamarin_assertion_message ("Failed to initialize the VM");
}
static bool
xamarin_is_native_library (const char *libraryName)
{
if (xamarin_runtime_libraries == NULL)
return false;
size_t libraryNameLength = strlen (libraryName);
// The libraries in xamarin_runtime_libraries are extension-less, so we need to
// remove any .dylib extension for the library name we're comparing with too.
if (libraryNameLength > 6 && strcmp (libraryName + libraryNameLength - 6, ".dylib") == 0)
libraryNameLength -= 6;
bool rv = false;
for (int i = 0; xamarin_runtime_libraries [i] != NULL; i++) {
// Check if the start of the current xamarin_runtime_libraries entry matches libraryName
if (!strncmp (xamarin_runtime_libraries [i], libraryName, libraryNameLength)) {
// The start matches, now check if that's all there is
if (xamarin_runtime_libraries [i] [libraryNameLength] == 0) {
// If so, we've got a match
rv = true;
break;
}
}
}
return rv;
}
void*
xamarin_pinvoke_override (const char *libraryName, const char *entrypointName)
{
void* symbol = NULL;
if (!strcmp (libraryName, "__Internal")) {
symbol = dlsym (RTLD_DEFAULT, entrypointName);
#if !defined (CORECLR_RUNTIME) // we're intercepting objc_msgSend calls using the managed System.Runtime.InteropServices.ObjectiveC.Bridge.SetMessageSendCallback instead.
#if defined (__i386__) || defined (__x86_64__) || defined (__arm64__)
} else if (!strcmp (libraryName, "/usr/lib/libobjc.dylib")) {
if (xamarin_marshal_objectivec_exception_mode != MarshalObjectiveCExceptionModeDisable) {
if (!strcmp (entrypointName, "objc_msgSend")) {
symbol = (void *) &xamarin_dyn_objc_msgSend;
} else if (!strcmp (entrypointName, "objc_msgSendSuper")) {
symbol = (void *) &xamarin_dyn_objc_msgSendSuper;
#if !defined (__arm64__)
} else if (!strcmp (entrypointName, "objc_msgSend_stret")) {
symbol = (void *) &xamarin_dyn_objc_msgSend_stret;
} else if (!strcmp (entrypointName, "objc_msgSendSuper_stret")) {
symbol = (void *) &xamarin_dyn_objc_msgSendSuper_stret;
#endif // !defined (__arm64__)
} else {
return NULL;
}
} else {
return NULL;
}
#endif // defined (__i386__) || defined (__x86_64__) || defined (__arm64__)
#endif // !defined (CORECLR_RUNTIME)
} else if (xamarin_is_native_library (libraryName)) {
switch (xamarin_libmono_native_link_mode) {
case XamarinNativeLinkModeStaticObject:
// lookup the symbol in loaded memory, like __Internal does.
symbol = dlsym (RTLD_DEFAULT, entrypointName);
break;
case XamarinNativeLinkModeDynamicLibrary:
// if we're not linking statically, then don't do anything at all, let mono handle whatever needs to be done
return NULL;
case XamarinNativeLinkModeFramework:
default:
// handle this as "DynamicLibrary" for now - do nothing.
LOG (PRODUCT ": Unhandled libmono link mode: %i when looking up %s in %s", xamarin_libmono_native_link_mode, entrypointName, libraryName);
return NULL;
}
} else {
return NULL;
}
if (symbol == NULL) {
LOG (PRODUCT ": Unable to resolve P/Invoke '%s' in the library '%s'", entrypointName, libraryName);
}
return symbol;
}
#endif
void
xamarin_printf (const char *format, ...)
{
va_list list;
va_start (list, format);
xamarin_vprintf (format, list);
va_end (list);
}
void
xamarin_vprintf (const char *format, va_list args)
{
NSString *message = [[NSString alloc] initWithFormat: [NSString stringWithUTF8String: format] arguments: args];
#if TARGET_OS_WATCH && defined (__arm__) // maybe make this configurable somehow?
const char *msg = [message UTF8String];
NSUInteger len = [message lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; // does not include NULL
fwrite (msg, 1, len, stdout);
if (len == 0 || msg [len - 1] != '\n')
fwrite ("\n", 1, 1, stdout);
fflush (stdout);
#else
NSLog (@"%@", message);
#endif
[message release];
}
/*
* File/resource lookup for assemblies
*
* Assemblies can be found in several locations:
* 1. If the assembly is compiled to a framework, in the framework's MonoBundle directory.
* For extensions the framework may be in the containing app's Frameworks directory.
* If an extension is sharing code with the main app, then the assemblies whose build target isn't 'framework' will be located in the container app's root directory.
* A framework may contain multiple assemblies, so it's not possible to deduce the framework name from the assembly name.
* 2. If the assembly is not a framework, in the app's root directory.
*
* The platform assembly (Xamarin.[iOS|TVOS|WatchOS].dll) and any assemblies
* the platform assembly references (mscorlib.dll, System.dll) may be in a
* pointer-size subdirectory (ARCH_SUBDIR), or an RID-specific subdirectory.
*
* AOT data files will have an arch-specific infix.
*/
void
xamarin_get_assembly_name_without_extension (const char *aname, char *name, size_t namelen)
{
size_t len = strnlen (aname, namelen);
if (len == namelen)
return;
strlcpy (name, aname, namelen);
if (namelen <= 4 || len <= 4)
return;
const char *ext = name + (len - 4);
if (!strncmp (".exe", ext, 4) || !strncmp (".dll", ext, 4))
name [len - 4] = 0; // strip off any extensions.
}
bool
xamarin_locate_app_resource (const char *resource, char *path, size_t pathlen)
{
const char *app_path = xamarin_get_bundle_path ();
return xamarin_locate_assembly_resource_for_root (app_path, NULL, resource, path, pathlen);
}
static bool
xamarin_locate_assembly_resource_for_root (const char *root, const char *culture, const char *resource, char *path, size_t pathlen)
{
if (culture != NULL && *culture != 0) {
// culture-specific directory
if (snprintf (path, pathlen, "%s/%s/%s", root, culture, resource) < 0) {
LOG (PRODUCT ": Failed to construct path for assembly resource (root directory: '%s', culture: '%s', resource: '%s'): %s", root, culture, resource, strerror (errno));
return false;
} else if (xamarin_file_exists (path)) {
return true;
}
}
// arch-specific extension
if (snprintf (path, pathlen, "%s/%s.%s", root, resource, xamarin_arch_name) < 0) {
LOG (PRODUCT ": Failed to construct path for resource: %s (4): %s", resource, strerror (errno));
return false;
} else if (xamarin_file_exists (path)) {
return true;
}
#if !MONOMAC
// pointer-size subdirectory with arch-specific extension
if (snprintf (path, pathlen, "%s/%s/%s.%s", root, ARCH_SUBDIR, resource, xamarin_arch_name) < 0) {
LOG (PRODUCT ": Failed to construct path for resource: %s (5): %s", resource, strerror (errno));
return false;
} else if (xamarin_file_exists (path)) {
return true;
}
// pointer-size subdirectory
if (snprintf (path, pathlen, "%s/%s/%s", root, ARCH_SUBDIR, resource) < 0) {
LOG (PRODUCT ": Failed to construct path for resource: %s (5): %s", resource, strerror (errno));
return false;
} else if (xamarin_file_exists (path)) {
return true;
}
#endif // !MONOMAC
#if DOTNET
// RID-specific subdirectory
if (snprintf (path, pathlen, "%s/.xamarin/%s/%s", root, RUNTIMEIDENTIFIER, resource) < 0) {
LOG (PRODUCT ": Failed to construct path for resource: %s (5): %s", resource, strerror (errno));
return false;
} else if (xamarin_file_exists (path)) {
return true;
}
#endif
// just the file, no extensions, etc.
if (snprintf (path, pathlen, "%s/%s", root, resource) < 0) {
LOG (PRODUCT ": Failed to construct path for resource: %s (6): %s", resource, strerror (errno));
return false;
} else if (xamarin_file_exists (path)) {
return true;
}
return false;
}
#if !defined (CORECLR_RUNTIME)
bool
xamarin_locate_assembly_resource_for_name (MonoAssemblyName *assembly_name, const char *resource, char *path, size_t pathlen)
{
const char *culture = mono_assembly_name_get_culture (assembly_name);
const char *aname = mono_assembly_name_get_name (assembly_name);
return xamarin_locate_assembly_resource (aname, culture, resource, path, pathlen);
}
#endif
// #define LOG_RESOURCELOOKUP(...) do { NSLog (@ __VA_ARGS__); } while (0);
#define LOG_RESOURCELOOKUP(...)
bool
xamarin_locate_assembly_resource (const char *assembly_name, const char *culture, const char *resource, char *path, size_t pathlen)
{
char root [1024];
char aname [256];
const char *app_path;
LOG_RESOURCELOOKUP (PRODUCT ": Locating the resource '%s' for the assembly '%s' (culture: '%s').", resource, assembly_name, culture);
app_path = xamarin_get_bundle_path ();
xamarin_get_assembly_name_without_extension (assembly_name, aname, sizeof (aname));
// First check if the directory is explicitly set. This directory is relative to the app bundle.
// For extensions this might be a relative path that points to container app (../../Frameworks/...).
const char *explicit_location = xamarin_find_assembly_directory (aname);
if (explicit_location) {
snprintf (root, sizeof (root), "%s/%s", app_path, explicit_location);
if (xamarin_locate_assembly_resource_for_root (root, culture, resource, path, pathlen)) {
LOG_RESOURCELOOKUP (PRODUCT ": Located resource '%s' from explicit path '%s': %s\n", resource, explicit_location, path);
return true;
}
// If we have an explicit location, then that's where the assembly must be.
LOG_RESOURCELOOKUP (PRODUCT ": Could not find the resource '%s' for the assembly '%s' (culture: '%s') in the explicit path '%s'.", resource, assembly_name, culture, explicit_location);
return false;
}
// The root app directory
if (xamarin_locate_assembly_resource_for_root (app_path, culture, resource, path, pathlen)) {
LOG_RESOURCELOOKUP (PRODUCT ": Located resource '%s' from app bundle: %s\n", resource, path);
return true;
}
#if !MONOMAC && (defined(__i386__) || defined (__x86_64__))
// In the simulator we also check in a 'simulator' subdirectory. This is
// so that we can create a framework that works for both simulator and
// device, without affecting device builds in any way (device-specific
// files just go in the MonoBundle directory)
snprintf (root, sizeof (root), "%s/Frameworks/%s.framework/MonoBundle/simulator", app_path, aname);
if (xamarin_locate_assembly_resource_for_root (root, culture, resource, path, pathlen)) {
LOG_RESOURCELOOKUP (PRODUCT ": Located resource '%s' from framework '%s': %s\n", resource, aname, path);
return true;
}
#endif // !MONOMAC
// Then in a framework named as the assembly
snprintf (root, sizeof (root), "%s/Frameworks/%s.framework/MonoBundle", app_path, aname);
if (xamarin_locate_assembly_resource_for_root (root, culture, resource, path, pathlen)) {
LOG_RESOURCELOOKUP (PRODUCT ": Located resource '%s' from framework '%s': %s\n", resource, aname, path);
return true;
}
// Then in the container app's root directory (for extensions)
if (xamarin_launch_mode == XamarinLaunchModeExtension) {
snprintf (root, sizeof (root), "../..");
if (xamarin_locate_assembly_resource_for_root (root, culture, resource, path, pathlen)) {
LOG_RESOURCELOOKUP (PRODUCT ": Located resource '%s' from container app bundle '%s': %s\n", resource, aname, path);
return true;
}
}
#if TARGET_OS_MACCATALYST
snprintf (root, sizeof (root), "%s/Contents/%s", app_path, [xamarin_custom_bundle_name UTF8String]);
if (xamarin_locate_assembly_resource_for_root (root, culture, resource, path, pathlen)) {
LOG_RESOURCELOOKUP (PRODUCT ": Located resource '%s' from macOS content bundle '%s': %s\n", resource, aname, path);
return true;
}
#endif
return false;
}
void
xamarin_set_assembly_directories (struct AssemblyLocations *directories)
{
if (options.AssemblyLocations)
xamarin_assertion_message ("Assembly directories already set.");
options.AssemblyLocations = directories;
}
static int
compare_assembly_location (const void *key, const void *value)
{
return strcmp ((const char *) key, ((struct AssemblyLocation *) value)->assembly_name);
}
const char *
xamarin_find_assembly_directory (const char *assembly_name)
{
if (options.AssemblyLocations == NULL)
return NULL;
struct AssemblyLocation *entry;
entry = (struct AssemblyLocation *) bsearch (assembly_name, options.AssemblyLocations->locations, options.AssemblyLocations->length, sizeof (struct AssemblyLocation), compare_assembly_location);
return entry ? entry->location : NULL;
}
MonoMethod *
xamarin_get_managed_method_for_token (guint32 token_ref, GCHandle *exception_gchandle)
{
MonoReflectionMethod *reflection_method;
reflection_method = (MonoReflectionMethod *) xamarin_gchandle_unwrap (xamarin_get_method_from_token (token_ref, exception_gchandle));
if (*exception_gchandle != INVALID_GCHANDLE) return NULL;
MonoMethod *rv = xamarin_get_reflection_method_method (reflection_method);
xamarin_mono_object_release (&reflection_method);
return rv;
}
GCHandle
xamarin_gchandle_new (MonoObject *obj, bool pinned)
{
#if defined (CORECLR_RUNTIME)
return xamarin_bridge_create_gchandle (obj == NULL ? INVALID_GCHANDLE : obj->gchandle, pinned ? XamarinGCHandleTypePinned : XamarinGCHandleTypeNormal);
#else
return GINT_TO_POINTER (mono_gchandle_new (obj, pinned));
#endif
}
GCHandle
xamarin_gchandle_new_weakref (MonoObject *obj, bool track_resurrection)
{
#if defined (CORECLR_RUNTIME)
return xamarin_bridge_create_gchandle (obj == NULL ? INVALID_GCHANDLE : obj->gchandle, track_resurrection ? XamarinGCHandleTypeWeakTrackResurrection : XamarinGCHandleTypeWeak);
#else
return GINT_TO_POINTER (mono_gchandle_new_weakref (obj, track_resurrection));
#endif
}
MonoObject *
xamarin_gchandle_get_target (GCHandle handle)
{
if (handle == INVALID_GCHANDLE)
return NULL;
#if defined (CORECLR_RUNTIME)
return xamarin_bridge_get_monoobject (handle);
#else
return mono_gchandle_get_target (GPOINTER_TO_UINT (handle));
#endif
}
void
xamarin_gchandle_free (GCHandle handle)
{
if (handle == INVALID_GCHANDLE)
return;
#if defined (CORECLR_RUNTIME)
xamarin_bridge_free_gchandle (handle);
#else
mono_gchandle_free (GPOINTER_TO_UINT (handle));
#endif
}
MonoObject *
xamarin_gchandle_unwrap (GCHandle handle)
{
if (handle == INVALID_GCHANDLE)
return NULL;
MonoObject *rv = xamarin_gchandle_get_target (handle);
xamarin_gchandle_free (handle);
return rv;
}
2016-04-21 15:19:32 +03:00
/*
* Object unregistration:
*
* This is a difficult problem, because we need to be able
* to lookup the managed object (given the native object)
* from inside the dealloc implementation in super classes.
*
* Example:
*
* MyManagedObject : MyNativeObject : NSObject
*
* -[MyNativeObject dealloc] may call selectors, and in
* that case we need to be able to look up the managed
* object to invoke the corresponding managed methods.
*
* See 02474ac1dd and bug #24609.
*
* Note: it is not recommmended to invoke selectors from the
* dealloc method, but iOS still does it (and third-party
* libraries as well), so we have to cope with it.
*
* At the same time we must also not unregister after
* the native memory has been freed, because then there's
* a race condition where another thread can create a
* new native object with the same pointer and end up in
* managed code, in which case we might find the managed
* object for the first native object (which has been freed,
* but not unregistered yet), and random InvalidCastExceptions
* occur.
*
* See bug #29801.
*
* Solution:
*
* Fortunately the complete deallocation sequence is documented:
01 // The documented Deallocation Timeline (WWDC 2011, Session 322, 36:22).
02 // 1. -release to zero
03 // * Object is now deallocating and will die.
04 // * New __weak references are not allowed, and will get nil.
05 // * [self dealloc] is called
06 // 2. Subclass -dealloc
07 // * bottom-most subclass -dealloc is called
08 // * Non-ARC code manually releases iVars
09 // * Walk the super-class chain calling -dealloc
10 // 3. NSObject -dealloc
11 // * Simply calls the ObjC runtime object_dispose()
12 // 4. object_dispose()
13 // * Call destructors for C++ iVars
14 // * Call -release for ARC iVars
15 // * Erase associated references
16 // * Erase __weak references
17 // * Call free()
* and our solution to the problem is:
*
* a) For the static registrar, we execute code at line #13 above, by
* adding a C++ iVar (with a destructor). This is the XamarinObject
* type.
*
* b) For the dynamic registrar we have to use another solution, because
* it's not possible to add C++ iVars dynamically. Instead we create
* a new native object (XamarinAssociatedObject), which we associate
* with the native object we're tracking, and which will be deallocated
* when the tracked native object is erasing its associated references
* (line #15) from above.
*
* Associated references is a lot slower and more memory hungry than the C++
* iVar, which is why we're not using associated references in all cases.
*
*/
/*
* XamarinObject
*
* With the static registrar we add a C++ ivar, and
* in the destructor we unregister the managed instance.
*/
XamarinObject::~XamarinObject ()
{
// COOP: no managed memory access: any mode.
@try {
xamarin_notify_dealloc (native_object, gc_handle);
} @catch (NSException *ex) {
NSLog (@"%@", ex);
}
2016-04-21 15:19:32 +03:00
native_object = NULL;
gc_handle = INVALID_GCHANDLE;
2016-04-21 15:19:32 +03:00
}
/*
* XamarinAssociatedObject
*
* With the dynamic registrar we associate an instance
* of this object to every user type, and in this object's
* dealloc method we unregister the managed instance.
*/
@implementation XamarinAssociatedObject
-(void) dealloc
{
// COOP: no managed memory access: any mode.
xamarin_notify_dealloc (native_object, gc_handle);
2016-04-21 15:19:32 +03:00
native_object = NULL;
gc_handle = INVALID_GCHANDLE;
2016-04-21 15:19:32 +03:00
[super dealloc];
}
@end
/*
* NonXamarinObject category
*
* Inject a default xamarinGetGCHandle implementation into every object,
* so that we don't have to check if a type is a user type before
* calling xamarinGetGCHandle. TODO: verify if this is really faster than
* checking the type first.
*
* Do not add a xamarinSetGCHandle:flags: method, since we use the presence
2016-04-21 15:19:32 +03:00
* of it to detect whether a particular type is a user type or not
* (see Runtime.IsUserType).
2016-04-21 15:19:32 +03:00
*/
@implementation NSObject (NonXamarinObject)
-(GCHandle) xamarinGetGCHandle
{
// COOP: no managed memory access: any mode.
return INVALID_GCHANDLE;
}
-(enum XamarinGCHandleFlags) xamarinGetFlags
2016-04-21 15:19:32 +03:00
{
// COOP: no managed memory access: any mode.
return XamarinGCHandleFlags_None;
2016-04-21 15:19:32 +03:00
}
@end
#if MONOMAC
void
xamarin_set_is_mkbundle (bool value)
{
if (initialize_started)
xamarin_assertion_message ("Fatal error: xamarin_set_is_mkbundle called after xamarin_initialize.\n");
xamarin_is_mkbundle = value;
}
bool
xamarin_get_is_mkbundle ()
{
return xamarin_is_mkbundle;
}
#endif
void
xamarin_set_is_debug (bool value)
{
if (initialize_started)
xamarin_assertion_message ("Fatal error: xamarin_set_is_debug called after xamarin_initialize.\n");
xamarin_debug_mode = value;
}
bool
xamarin_get_is_debug ()
{
return xamarin_debug_mode;
}
bool
xamarin_is_managed_exception_marshaling_disabled ()
{
#if defined (CORECLR_RUNTIME)
return false; // never disable exception marshalling for CoreCLR.
#elif DEBUG
if (xamarin_is_gc_coop)
return false;
switch (xamarin_marshal_managed_exception_mode) {
case MarshalManagedExceptionModeDefault:
// If all of the following are true:
// * In debug mode
// * Using the default exception marshaling mode
// * The debugger is attached
// Then disable managed exception marshaling.
return mono_is_debugger_attached ();
case MarshalManagedExceptionModeDisable:
return true;
default:
return false;
}
#else
return false;
#endif
}
#if DOTNET && (TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH)
int
xamarin_get_runtime_arch ()
{
#if TARGET_OS_SIMULATOR
return 1;
#else
return 0;
#endif
}
#endif // DOTNET && (TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH)
/*
* XamarinGCHandle
*/
@implementation XamarinGCHandle
+(XamarinGCHandle *) createWithHandle: (GCHandle) h
{
XamarinGCHandle *rv = [[XamarinGCHandle alloc] init];
rv->handle = h;
[rv autorelease];
return rv;
}
-(void) dealloc
{
xamarin_gchandle_free (handle);
[super dealloc];
}
-(GCHandle) getHandle
{
return handle;
}
@end