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 >
2017-05-16 13:46:51 +03:00
# 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 ( 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 MONOMAC
bool xamarin_detect _unified _build = true ;
# else
// no automatic detection for XI , mtouch should do the right thing in the generated main .
bool xamarin_detect _unified _build = false ;
# endif
2016-09-30 22:02:17 +03:00
# if MONOMAC
2016-04-21 15:19:32 +03:00
bool xamarin_use _new _assemblies = false ;
2016-09-30 22:02:17 +03:00
# endif
[runtime] Disable the GC pump for Xamarin.Mac. (#52)
It seems it prevents the app from exiting; Mono will wait
for the GC pump thread to exit, which never happens.
The GC pump was never enabled for Xamarin.Mac apps
until recently (d9677714a), so this will just revert
to the previous behavior.
thread 1: tid = 0x2ab2bf5, 0x00007fff97815eb2 libsystem_kernel.dylib`__psynch_cvwait + 10, name = 'tid_50f', queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame 0: 0x00007fff97815eb2 libsystem_kernel.dylib`__psynch_cvwait + 10
frame 1: 0x00007fff89015150 libsystem_pthread.dylib`_pthread_cond_wait + 767
frame 2: 0x000000010ceb1283 MobileTestApp`_wapi_handle_timedwait_signal_handle [inlined] mono_os_cond_wait(cond=<unavailable>, mutex=<unavailable>) + 8 at mono-os-mutex.h:107 [opt]
frame 3: 0x000000010ceb127b MobileTestApp`_wapi_handle_timedwait_signal_handle [inlined] mono_os_cond_timedwait(cond=<unavailable>, mutex=0x00007fb085016210, timeout_ms=<unavailable>) at mono-os-mutex.h:122 [opt]
frame 4: 0x000000010ceb127b MobileTestApp`_wapi_handle_timedwait_signal_handle(handle=0x0000000000000400, timeout=<unavailable>, alertable=1, poll=0, alerted=0x00007fff52fb4984) + 1003 at handles.c:1555 [opt]
frame 5: 0x000000010cec3d87 MobileTestApp`wapi_WaitForMultipleObjectsEx(numobjects=2, handles=<unavailable>, waitall=0, timeout=4294967295, alertable=<unavailable>) + 1527 at wait.c:615 [opt]
frame 6: 0x000000010ce692b6 MobileTestApp`mono_thread_manage + 57 at threads.c:2956 [opt]
frame 7: 0x000000010ce6927d MobileTestApp`mono_thread_manage + 301 at threads.c:3162 [opt]
frame 8: 0x000000010ccbb533 MobileTestApp`mono_main(argc=<unavailable>, argv=<unavailable>) + 7987 at driver.g.c:2181 [opt]
frame 9: 0x000000010cc61c67 MobileTestApp`main(argc=1, argv=0x00007fff52fb5268) + 775 at launcher.m:562
frame 10: 0x000000010cc63150 MobileTestApp`start + 52
thread 2: tid = 0x2ab2bf7, 0x00007fff97816ff6 libsystem_kernel.dylib`kevent_qos + 10, queue = 'com.apple.libdispatch-manager'
frame 0: 0x00007fff97816ff6 libsystem_kernel.dylib`kevent_qos + 10
frame 1: 0x00007fff88f18099 libdispatch.dylib`_dispatch_mgr_invoke + 216
frame 2: 0x00007fff88f17d01 libdispatch.dylib`_dispatch_mgr_thread + 52
thread 3: tid = 0x2ab2bfa, 0x00007fff97815eb2 libsystem_kernel.dylib`__psynch_cvwait + 10, name = 'SGen worker'
frame 0: 0x00007fff97815eb2 libsystem_kernel.dylib`__psynch_cvwait + 10
frame 1: 0x00007fff89015150 libsystem_pthread.dylib`_pthread_cond_wait + 767
frame 2: 0x000000010ceab31c MobileTestApp`thread_func [inlined] mono_os_cond_wait(mutex=0x000000010d02e190) + 15 at mono-os-mutex.h:107 [opt]
frame 3: 0x000000010ceab30d MobileTestApp`thread_func(thread_data=0x0000000000000000) + 333 at sgen-thread-pool.c:110 [opt]
frame 4: 0x00007fff89014c13 libsystem_pthread.dylib`_pthread_body + 131
frame 5: 0x00007fff89014b90 libsystem_pthread.dylib`_pthread_start + 168
frame 6: 0x00007fff89012375 libsystem_pthread.dylib`thread_start + 13
thread 4: tid = 0x2ab2bfb, 0x00007fff978103c2 libsystem_kernel.dylib`semaphore_wait_trap + 10, name = 'Finalizer'
frame 0: 0x00007fff978103c2 libsystem_kernel.dylib`semaphore_wait_trap + 10
frame 1: 0x000000010cdb847b MobileTestApp`finalizer_thread [inlined] mono_os_sem_wait(flags=MONO_SEM_FLAGS_ALERTABLE) + 11 at mono-os-semaphore.h:72 [opt]
frame 2: 0x000000010cdb8470 MobileTestApp`finalizer_thread + 11 at mono-coop-semaphore.h:40 [opt]
frame 3: 0x000000010cdb8465 MobileTestApp`finalizer_thread(unused=<unavailable>) + 181 at gc.c:770 [opt]
frame 4: 0x000000010ce6bb05 MobileTestApp`start_wrapper [inlined] start_wrapper_internal + 548 at threads.c:738 [opt]
frame 5: 0x000000010ce6b8e1 MobileTestApp`start_wrapper(data=<unavailable>) + 49 at threads.c:785 [opt]
frame 6: 0x000000010ced50f6 MobileTestApp`inner_start_thread(arg=<unavailable>) + 406 at mono-threads-posix.c:92 [opt]
frame 7: 0x00007fff89014c13 libsystem_pthread.dylib`_pthread_body + 131
frame 8: 0x00007fff89014b90 libsystem_pthread.dylib`_pthread_start + 168
frame 9: 0x00007fff89012375 libsystem_pthread.dylib`thread_start + 13
thread 5: tid = 0x2ab2c07, 0x00007fff97816206 libsystem_kernel.dylib`__semwait_signal + 10, name = 'tid_320b'
frame 0: 0x00007fff97816206 libsystem_kernel.dylib`__semwait_signal + 10
frame 1: 0x00007fff82df1d17 libsystem_c.dylib`nanosleep + 199
frame 2: 0x00007fff82df1c0a libsystem_c.dylib`usleep + 54
frame 3: 0x000000010cc5bc60 MobileTestApp`pump_gc(context=0x0000000000000000) + 64 at runtime.m:904
frame 4: 0x00007fff89014c13 libsystem_pthread.dylib`_pthread_body + 131
frame 5: 0x00007fff89014b90 libsystem_pthread.dylib`_pthread_start + 168
frame 6: 0x00007fff89012375 libsystem_pthread.dylib`thread_start + 13
2016-05-16 15:12:49 +03:00
# if MONOTOUCH && DEBUG && ( defined ( __i386 __ ) || defined ( __x86 _64 __ ) )
2016-05-13 16:49:08 +03:00
bool xamarin_gc _pump = true ;
# else
2016-04-21 15:19:32 +03:00
bool xamarin_gc _pump = false ;
2016-05-13 16:49:08 +03:00
# endif
2016-04-21 15:19:32 +03:00
# if MONOMAC
// FIXME : implement release mode for monomac .
bool xamarin_debug _mode = true ;
2017-02-08 22:40:48 +03:00
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
2016-08-25 21:06:38 +03:00
bool xamarin_disable _lldb _attach = 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 MONOMAC
NSString * xamarin_custom _bundle _name = nil ;
bool xamarin_is _mkbundle = false ;
2017-05-11 18:44:58 +03:00
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
2016-05-11 13:24:55 +03:00
# if TARGET_OS _WATCH
bool xamarin_is _gc _coop = true ;
# else
bool xamarin_is _gc _coop = false ;
# endif
2016-05-11 13:47:26 +03:00
enum MarshalObjectiveCExceptionMode xamarin_marshal _objectivec _exception _mode = MarshalObjectiveCExceptionModeDefault ;
enum MarshalManagedExceptionMode xamarin_marshal _managed _exception _mode = MarshalManagedExceptionModeDefault ;
2017-04-28 15:02:38 +03:00
enum XamarinLaunchMode xamarin_launch _mode = XamarinLaunchModeApp ;
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 MonoClass * inativeobject_class ;
static MonoClass * nsobject_class ;
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 ;
} ;
enum InitializationFlags : int {
/ * unused = 0 x01 , * /
2016-09-30 22:02:17 +03:00
/ * unused = 0 x02 , * /
2016-04-21 15:19:32 +03:00
InitializationFlagsDynamicRegistrar = 0 x04 ,
2016-09-22 15:27:54 +03:00
/ * unused = 0 x08 , * /
2016-04-21 15:19:32 +03:00
InitializationFlagsIsSimulator = 0 x10 ,
} ;
struct InitializationOptions {
int size ; // the size of this structure . This is used for version checking .
enum InitializationFlags flags ;
2016-11-01 14:40:51 +03:00
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 ;
2016-05-11 14:27:51 +03:00
enum MarshalObjectiveCExceptionMode MarshalObjectiveCExceptionMode ;
enum MarshalManagedExceptionMode MarshalManagedExceptionMode ;
2017-05-11 18:44:58 +03:00
# if MONOMAC
enum XamarinLaunchMode LaunchMode ;
const char * EntryAssemblyPath ;
# endif
2016-12-12 20:57:02 +03:00
struct AssemblyLocations * AssemblyLocations ;
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 ,
} ;
2016-11-01 14:40:51 +03:00
struct InitializationOptions options = { 0 } ;
2016-04-21 15:19:32 +03:00
struct Managed_NSObject {
MonoObject obj ;
id handle ;
void * class_handle ;
uint8_t flags ;
} ;
id
xamarin_get _nsobject _handle ( MonoObject * obj )
{
2016-02-15 21:02:14 +03:00
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
struct Managed_NSObject * mobj = ( struct Managed_NSObject * ) obj ;
return mobj -> handle ;
}
void
xamarin_set _nsobject _handle ( MonoObject * obj , id handle )
{
2016-02-15 21:02:14 +03:00
// COOP : Writing managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
struct Managed_NSObject * mobj = ( struct Managed_NSObject * ) obj ;
mobj -> handle = handle ;
}
uint8_t
xamarin_get _nsobject _flags ( MonoObject * obj )
{
2016-02-15 21:02:14 +03:00
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
struct Managed_NSObject * mobj = ( struct Managed_NSObject * ) obj ;
return mobj -> flags ;
}
void
xamarin_set _nsobject _flags ( MonoObject * obj , uint8_t flags )
{
2016-02-15 21:02:14 +03:00
// COOP : Writing managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
struct Managed_NSObject * mobj = ( struct Managed_NSObject * ) obj ;
mobj -> flags = flags ;
}
MonoType *
xamarin_get _parameter _type ( MonoMethod * managed_method , int index )
{
2016-02-15 21:02:14 +03:00
// 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 ;
for ( int i = 0 ; i < index + 1 ; i + + )
p = mono_signature _get _params ( msig , & iter ) ;
return p ;
}
MonoObject *
2016-06-02 13:18:45 +03:00
xamarin_get _nsobject _with _type _for _ptr ( id self , bool owns , MonoType * type , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +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 ;
2016-06-02 13:18:45 +03:00
return xamarin_get _nsobject _with _type _for _ptr _created ( self , owns , type , & created , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
}
MonoObject *
2016-06-02 13:18:45 +03:00
xamarin_get _nsobject _with _type _for _ptr _created ( id self , bool owns , MonoType * type , int32_t * created , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +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 ;
uint32_t gchandle = 0 ;
* created = false ;
if ( self = = NULL )
return NULL ;
gchandle = xamarin_get _gchandle ( self ) ;
if ( gchandle ! = 0 ) {
mobj = mono_gchandle _get _target ( gchandle ) ;
if ( mono_object _isinst ( mobj , mono_class _from _mono _type ( type ) ) ! = NULL )
return mobj ;
}
2016-06-02 13:18:45 +03:00
return xamarin_get _nsobject _with _type ( self , mono_type _get _object ( mono_domain _get ( ) , type ) , created , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
}
MonoObject *
2016-06-02 13:18:45 +03:00
xamarin_get _managed _object _for _ptr ( id self , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +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 ;
uint32_t gchandle = 0 ;
if ( self = = NULL )
return NULL ;
gchandle = xamarin_get _gchandle ( self ) ;
if ( gchandle = = 0 ) {
2016-06-02 13:18:45 +03:00
mobj = ( MonoObject * ) xamarin_try _get _or _construct _nsobject ( self , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
} else {
mobj = mono_gchandle _get _target ( gchandle ) ;
}
return mobj ;
}
MonoObject *
2016-06-02 13:18:45 +03:00
xamarin_get _managed _object _for _ptr _fast ( id self , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +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 ;
uint32_t gchandle = 0 ;
gchandle = xamarin_get _gchandle ( self ) ;
if ( gchandle = = 0 ) {
2016-06-02 13:18:45 +03:00
mobj = ( MonoObject * ) xamarin_try _get _or _construct _nsobject ( self , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
} else {
mobj = mono_gchandle _get _target ( gchandle ) ;
# if DEBUG
if ( self ! = xamarin_get _nsobject _handle ( mobj ) ) {
xamarin_assertion _message ( "Internal consistency error, please file a bug (http://bugzilla.xamarin.com). Additional data: found managed object %p=%p (%s) in native object %p (%s).\n" ,
2016-06-02 13:18:45 +03:00
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 ;
}
void xamarin_framework _peer _lock ( )
{
2016-02-15 21:02:14 +03:00
// COOP : CHECK
MONO_ASSERT _GC _UNSAFE ;
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-04-21 15:19:32 +03:00
pthread_mutex _lock ( & framework_peer _release _lock ) ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
}
void xamarin_framework _peer _unlock ( )
{
pthread_mutex _unlock ( & framework_peer _release _lock ) ;
}
bool
xamarin_is _class _nsobject ( MonoClass * cls )
{
2016-02-15 21:02:14 +03:00
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
return mono_class _is _subclass _of ( cls , nsobject_class , false ) ;
}
bool
xamarin_is _class _inativeobject ( MonoClass * cls )
{
2016-02-15 21:02:14 +03:00
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
return mono_class _is _subclass _of ( cls , inativeobject_class , true ) ;
}
bool
xamarin_is _class _array ( MonoClass * cls )
{
2016-02-15 21:02:14 +03:00
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
return mono_class _is _subclass _of ( cls , mono_get _array _class ( ) , false ) ;
}
# define MANAGED_REF _BIT ( 1 < < 31 )
# define GCHANDLE_WEAK ( 1 < < 30 )
# define GCHANDLE_MASK ( MANAGED_REF _BIT | GCHANDLE_WEAK )
// The XamarinExtendedObject protocol is just to avoid a
// compiler warning ( no ' xamarinGetGChandle ' selector found ) .
@ protocol XamarinExtendedObject
- ( int ) xamarinGetGCHandle ;
- ( void ) xamarinSetGCHandle : ( int ) gc_handle ;
@ end
static inline int
2016-02-15 21:02:14 +03:00
get_raw _gchandle _safe ( id self )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +03:00
// COOP : we call a selector , and that must only be done in SAFE mode .
MONO_ASSERT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
id < XamarinExtendedObject > xself = self ;
return ( int ) [ xself xamarinGetGCHandle ] ;
}
2016-02-15 21:02:14 +03:00
static inline int
get_raw _gchandle ( id self )
{
// COOP : we call a selector , and that must only be done in SAFE mode .
MONO_ASSERT _GC _UNSAFE ;
int rv ;
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-02-15 21:02:14 +03:00
id < XamarinExtendedObject > xself = self ;
rv = ( int ) [ xself xamarinGetGCHandle ] ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _SAFE ;
2016-02-15 21:02:14 +03:00
return rv ;
}
2016-04-21 15:19:32 +03:00
static inline void
set_raw _gchandle ( id self , int gc_handle )
{
2016-02-15 21:02:14 +03:00
// COOP : we call a selector , and that must only be done in SAFE mode .
MONO_ASSERT _GC _UNSAFE ;
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-04-21 15:19:32 +03:00
id < XamarinExtendedObject > xself = self ;
[ xself xamarinSetGCHandle : gc_handle ] ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
}
static inline int
get_gchandle ( id self )
{
2016-02-15 21:02:14 +03:00
// COOP : does not access managed memory : any mode
2016-04-21 15:19:32 +03:00
return get_raw _gchandle ( self ) & ~ GCHANDLE_MASK ;
}
int
xamarin_get _gchandle ( id self )
{
2016-02-15 21:02:14 +03:00
// COOP : does not access managed memory : any mode
2016-04-21 15:19:32 +03:00
return get_gchandle ( self ) ;
}
int
xamarin_get _gchandle _with _flags ( id self )
{
2016-02-15 21:02:14 +03:00
// COOP : does not access managed memory : any mode
2016-04-21 15:19:32 +03:00
return get_raw _gchandle ( self ) ;
}
bool
xamarin_has _managed _ref ( id self )
{
2016-02-15 21:02:14 +03:00
// COOP : get_raw _gchandle requires UNSAFE mode , so this function requires it too .
2016-04-21 15:19:32 +03:00
return get_raw _gchandle ( self ) & MANAGED_REF _BIT ;
}
2016-02-15 21:02:14 +03:00
bool
xamarin_has _managed _ref _safe ( id self )
{
// COOP : variation of xamarin_has _managed _ref for SAFE mode .
return get_raw _gchandle _safe ( self ) & MANAGED_REF _BIT ;
}
2016-04-21 15:19:32 +03:00
MonoException *
xamarin_create _exception ( const char * msg )
{
2016-02-15 21:02:14 +03:00
// COOP : calls mono , needs to be in UNSAFE mode .
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
return ( MonoException * ) mono_exception _from _name _msg ( mono_get _corlib ( ) , "System" , "Exception" , msg ) ;
}
typedef struct {
MonoObject object ;
MonoMethod * method ;
MonoString * name ;
MonoReflectionType * reftype ;
} PublicMonoReflectionMethod ;
MonoMethod *
xamarin_get _reflection _method _method ( MonoReflectionMethod * method )
{
2016-02-15 21:02:14 +03:00
// COOP : Reads managed memory , needs to be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
PublicMonoReflectionMethod * rm = ( PublicMonoReflectionMethod * ) method ;
return rm -> method ;
}
id
2016-06-02 13:18:45 +03:00
xamarin_get _handle ( MonoObject * obj , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +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 ) ) {
2016-06-02 13:18:45 +03:00
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 ) ) ;
MonoException * exc = mono_get _exception _execution _engine ( msg ) ;
xamarin_free ( msg ) ;
2016-06-02 13:18:45 +03:00
* exception_gchandle = mono_gchandle _new ( ( MonoObject * ) exc , FALSE ) ;
2016-04-21 15:19:32 +03:00
}
return rv ;
}
# if DEBUG
static void
2016-06-02 13:18:45 +03:00
verify_cast ( MonoClass * to , MonoObject * obj , Class from_class , SEL sel , MonoMethod * method , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +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 ) ;
2016-06-02 13:18:45 +03:00
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 = mono_exception _from _name _msg ( mono_get _corlib ( ) , "System" , "InvalidCastException" , msg ) ;
mono_free ( from_name ) ;
mono_free ( to_name ) ;
xamarin_free ( msg ) ;
mono_free ( method_full _name ) ;
2016-06-02 13:18:45 +03:00
* exception_gchandle = mono_gchandle _new ( ( MonoObject * ) mono_ex , FALSE ) ;
2016-04-21 15:19:32 +03:00
}
}
# endif
void
2016-06-02 13:18:45 +03:00
xamarin_check _for _gced _object ( MonoObject * obj , SEL sel , id self , MonoMethod * method , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +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
2016-06-02 13:18:45 +03:00
verify_cast ( mono_method _get _class ( method ) , obj , [ self class ] , sel , method , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
# endif
return ;
}
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" ;
char * method_full _name = mono_method _full _name ( method , TRUE ) ;
2016-06-02 13:18:45 +03:00
char * type_name = xamarin_lookup _managed _type _name ( [ self class ] , exception_gchandle ) ;
if ( * exception_gchandle = = 0 ) {
char * msg = xamarin_strdup _printf ( m , self , object_getClassName ( self ) , type_name , sel_getName ( sel ) , method_full _name ) ;
MonoException * mex = xamarin_create _exception ( msg ) ;
xamarin_free ( msg ) ;
* exception_gchandle = mono_gchandle _new ( ( MonoObject * ) mex , FALSE ) ;
}
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 , int index , MonoClass * expected , MonoMethod * method )
{
2016-02-15 21:02:14 +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 )
{
2016-02-15 21:02:14 +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 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 = mono_exception _from _name _msg ( mono_get _corlib ( ) , "System" , "InvalidCastException" , msg ) ;
// xamarin_free ( msg ) ;
// mono_free ( method_full _name ) ;
// mono_raise _exception ( mono_ex ) ;
}
# endif
char *
2016-06-02 13:18:45 +03:00
xamarin_class _get _full _name ( MonoClass * klass , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +03:00
// COOP : Reads managed memory , needs to be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-06-02 13:18:45 +03:00
return xamarin_type _get _full _name ( mono_class _get _type ( klass ) , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
}
char *
2016-06-02 13:18:45 +03:00
xamarin_type _get _full _name ( MonoType * type , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +03:00
// COOP : Reads managed memory , needs to be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-06-02 13:18:45 +03:00
return xamarin_reflection _type _get _full _name ( mono_type _get _object ( mono_domain _get ( ) , type ) , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
}
/ *
* ToggleRef support
* /
// # define DEBUG_TOGGLEREF 1
static void
gc_register _toggleref ( MonoObject * obj , id self , bool isCustomType )
{
2016-02-15 21:02:14 +03:00
// COOP : This is an icall , at entry we ' re in unsafe mode . Managed memory is accessed , so we stay in unsafe mode .
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
# ifdef DEBUG_TOGGLEREF
id handle = xamarin_get _nsobject _handle ( obj ) ;
2016-09-06 23:55:23 +03:00
PRINT ( "**Registering object %p handle %p RC %d flags: %i" ,
2016-04-21 15:19:32 +03:00
obj ,
handle ,
( int ) ( handle ? [ handle retainCount ] : 0 ) ,
xamarin_get _nsobject _flags ( obj ) ) ;
# endif
mono_gc _toggleref _add ( obj , TRUE ) ;
// Make sure the GCHandle we have is a weak one for custom types .
2016-05-18 12:14:10 +03:00
if ( isCustomType ) {
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-04-21 15:19:32 +03:00
xamarin_switch _gchandle ( self , true ) ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _SAFE ;
2016-05-18 12:14:10 +03:00
}
2016-04-21 15:19:32 +03:00
}
static MonoToggleRefStatus
gc_toggleref _callback ( MonoObject * object )
{
2016-02-15 21:02:14 +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
id handle = NULL ;
MonoToggleRefStatus res ;
uint8_t flags = xamarin_get _nsobject _flags ( object ) ;
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 {
handle = xamarin_get _nsobject _handle ( object ) ;
if ( handle = = NULL ) { / * This shouldn ' t really happen * /
return MONO_TOGGLE _REF _DROP ;
} 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 ) {
cn = object_getClassName ( xamarin_get _nsobject _handle ( object ) ) ;
} else {
cn = object_getClassName ( handle ) ;
}
2016-09-06 23:55:23 +03:00
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 ;
}
typedef struct {
int dummy ;
} NRCProfiler ;
static void
gc_event _callback ( MonoProfiler * prof , MonoGCEvent event , int generation )
{
2016-02-15 21:02:14 +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 ;
}
}
static void
gc_enable _new _refcount ( void )
{
2016-02-15 21:02:14 +03:00
// COOP : this is executed at startup , I believe the mode here doesn ' t matter .
2016-04-21 15:19:32 +03:00
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 ) ;
NRCProfiler * prof = ( NRCProfiler * ) malloc ( sizeof ( NRCProfiler ) ) ;
mono_gc _toggleref _register _callback ( gc_toggleref _callback ) ;
mono_add _internal _call ( xamarin_use _new _assemblies ? "Foundation.NSObject::RegisterToggleRef" : PRODUCT_COMPAT _NAMESPACE ".Foundation.NSObject::RegisterToggleRef" , ( const void * ) gc_register _toggleref ) ;
mono_profiler _install ( ( MonoProfiler * ) prof , NULL ) ;
mono_profiler _install _gc ( gc_event _callback , NULL ) ;
}
static MonoClass *
get_class _from _name ( MonoImage * image , const char * nmspace , const char * name )
{
2016-02-15 21:02:14 +03:00
// COOP : this is a convenience function executed only at startup , I believe the mode here doesn ' t matter .
2016-04-21 15:19:32 +03:00
MonoClass * rv = mono_class _from _name ( image , nmspace , name ) ;
if ( ! rv )
xamarin_assertion _message ( "Fatal error: failed to load the class '%s.%s'\n." , nmspace , name ) ;
return rv ;
}
bool
xamarin_file _exists ( const char * path )
{
2016-02-15 21:02:14 +03:00
// COOP : no managed access : any mode
2016-04-21 15:19:32 +03:00
struct stat buffer ;
return stat ( path , & buffer ) = = 0 ;
}
2016-05-26 00:20:33 +03:00
MonoAssembly *
xamarin_open _assembly ( const char * name )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +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 ) ;
2016-11-21 16:39:16 +03:00
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: http://bugzilla.xamarin.com" , name ) ;
2016-04-21 15:19:32 +03:00
return assembly ;
}
# endif
2016-12-12 20:57:02 +03:00
exists = xamarin_locate _assembly _resource ( name , NULL , name , path , sizeof ( path ) ) ;
2016-04-21 15:19:32 +03:00
# if MONOMAC && DYLIB
2016-12-12 20:57:02 +03:00
if ( ! exists ) {
2016-04-21 15:19:32 +03:00
// Check if we already have the assembly in memory
2016-12-12 20:57:02 +03:00
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 ;
2016-11-21 16:39:16 +03:00
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: http://bugzilla.xamarin.com" , name ) ;
2016-04-21 15:19:32 +03:00
}
# endif
2016-12-12 20:57:02 +03:00
if ( ! exists )
2016-11-21 16:39:16 +03:00
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: http://bugzilla.xamarin.com" , name ) ;
2016-04-21 15:19:32 +03:00
assembly = mono_assembly _open ( path , NULL ) ;
2016-11-21 16:39:16 +03:00
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: http://bugzilla.xamarin.com" , name ) ;
2016-04-21 15:19:32 +03:00
return assembly ;
}
2016-06-02 13:18:45 +03:00
static bool
register_assembly ( MonoAssembly * assembly , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +03:00
// COOP : this is a function executed only at startup , I believe the mode here doesn ' t matter .
2016-06-02 13:18:45 +03:00
xamarin_register _assembly ( mono_assembly _get _object ( mono_domain _get ( ) , assembly ) , exception_gchandle ) ;
return * exception_gchandle = = 0 ;
2016-04-21 15:19:32 +03:00
}
MonoAssembly *
2016-06-02 13:18:45 +03:00
xamarin_open _and _register ( const char * aname , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +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 ;
2016-05-26 00:20:33 +03:00
assembly = xamarin_open _assembly ( aname ) ;
2016-04-21 15:19:32 +03:00
2016-06-02 13:18:45 +03:00
register_assembly ( assembly , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
return assembly ;
}
static gboolean
is_class _finalization _aware ( MonoClass * cls )
{
2016-02-15 21:02:14 +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
gboolean rv = false ;
if ( nsobject_class )
rv = cls = = nsobject_class || mono_class _is _assignable _from ( nsobject_class , cls ) ;
2016-09-06 23:55:23 +03:00
// 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 )
{
2016-02-15 21:02:14 +03:00
// 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 ;
2016-09-06 23:55:23 +03:00
// 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 ;
}
/ *
* Registration map
* /
void
xamarin_add _registration _map ( struct MTRegistrationMap * map )
{
2016-02-15 21:02:14 +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 ;
2016-04-21 15:19:32 +03:00
}
/ *
* Exception handling
* /
static XamarinUnhandledExceptionFunc unhandled_exception _func ;
void
xamarin_install _unhandled _exception _hook ( XamarinUnhandledExceptionFunc func )
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode
2016-04-21 15:19:32 +03:00
unhandled_exception _func = func ;
}
static MonoObject *
fetch_exception _property ( MonoObject * obj , const char * name , bool is_virtual )
{
2016-02-15 21:02:14 +03:00
// COOP : reading managed memory and executing managed code : must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
MonoMethod * get = NULL ;
MonoMethod * get_virt = NULL ;
MonoObject * exc = NULL ;
get = mono_class _get _method _from _name ( mono_get _exception _class ( ) , name , 0 ) ;
if ( get ) {
if ( is_virtual ) {
get_virt = mono_object _get _virtual _method ( obj , get ) ;
if ( get_virt )
get = get_virt ;
}
return ( MonoObject * ) mono_runtime _invoke ( get , obj , NULL , & exc ) ;
} else {
2016-09-06 23:55:23 +03:00
PRINT ( "Could not find the property System.Exception.%s" , name ) ;
2016-04-21 15:19:32 +03:00
}
return NULL ;
}
static char *
fetch_exception _property _string ( MonoObject * obj , const char * name , bool is_virtual )
{
2016-02-15 21:02:14 +03:00
// COOP : reading managed memory and executing managed code : must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
MonoString * str = ( MonoString * ) fetch_exception _property ( obj , name , is_virtual ) ;
return str ? mono_string _to _utf8 ( str ) : NULL ;
}
static void
print_exception ( MonoObject * exc , bool is_inner , NSMutableString * msg )
{
2016-02-15 21:02:14 +03:00
// COOP : reading managed memory and executing managed code : must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
MonoClass * type = mono_object _get _class ( exc ) ;
char * type_name = xamarin_strdup _printf ( "%s.%s" , mono_class _get _namespace ( type ) , mono_class _get _name ( type ) ) ;
char * trace = fetch_exception _property _string ( exc , "get_StackTrace" , true ) ;
char * message = fetch_exception _property _string ( exc , "get_Message" , true ) ;
if ( ! is_inner ) {
[ msg appendString : @ "Unhandled managed exception:\n" ] ;
} else {
[ msg appendString : @ " --- inner exception ---\n" ] ;
}
[ msg appendFormat : @ "%s (%s)\n%s\n" , message , type_name , trace ] ;
if ( unhandled_exception _func && ! is_inner )
unhandled_exception _func ( exc , type_name , message , trace ) ;
mono_free ( trace ) ;
mono_free ( message ) ;
xamarin_free ( type_name ) ;
}
2016-05-11 13:14:34 +03:00
static NSMutableString *
print_all _exceptions ( MonoObject * exc )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +03:00
// COOP : reading managed memory and executing managed code : must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-05-11 13:14:34 +03:00
NSMutableString * str = [ [ NSMutableString alloc ] init ] ;
2016-04-21 15:19:32 +03:00
// fetch the field , since the property might have been linked away .
2016-05-11 13:14:34 +03:00
int counter = 0 ;
2016-05-12 13:03:52 +03:00
MonoClassField * inner_exception = mono_class _get _field _from _name ( mono_object _get _class ( exc ) , "_innerException" ) ;
2016-04-21 15:19:32 +03:00
do {
print_exception ( exc , counter > 0 , str ) ;
if ( inner_exception ) {
mono_field _get _value ( exc , inner_exception , & exc ) ;
} else {
2016-05-12 13:03:52 +03:00
LOG ( "Could not find the field _innerException in System.Exception\n" ) ;
2016-04-21 15:19:32 +03:00
break ;
}
} while ( counter + + < 10 && exc ) ;
2016-05-11 13:14:34 +03:00
[ str autorelease ] ;
return str ;
}
2017-02-20 21:29:56 +03:00
NSString *
xamarin_print _all _exceptions ( MonoObject * exc )
{
return print_all _exceptions ( exc ) ;
}
2016-05-31 21:01:03 +03:00
void
xamarin_ftnptr _exception _handler ( guint32 gchandle )
{
2016-06-02 13:18:45 +03:00
xamarin_process _managed _exception _gchandle ( gchandle ) ;
}
void
xamarin_process _managed _exception _gchandle ( guint32 gchandle )
{
if ( gchandle = = 0 )
return ;
2016-05-31 21:01:03 +03:00
MonoObject * exc = mono_gchandle _get _target ( gchandle ) ;
mono_gchandle _free ( gchandle ) ;
2016-06-02 13:18:45 +03:00
xamarin_process _managed _exception ( exc ) ;
2016-05-31 21:01:03 +03:00
}
2016-05-11 13:14:34 +03:00
void
xamarin_unhandled _exception _handler ( MonoObject * exc , gpointer user_data )
{
2016-09-06 23:55:23 +03:00
PRINT ( "%@" , print_all _exceptions ( exc ) ) ;
2016-04-21 15:19:32 +03:00
abort ( ) ;
}
static void
exception_handler ( NSException * exc )
{
2016-06-02 13:18:45 +03:00
// 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 ] ) ;
2016-06-02 13:18:45 +03:00
if ( xamarin_is _gc _coop ) {
2016-09-06 23:55:23 +03:00
PRINT ( "Uncaught Objective-C exception: %@" , exc ) ;
2016-06-02 13:18:45 +03:00
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 )
{
2016-02-15 21:02:14 +03:00
// 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 .
2016-04-21 15:19:32 +03:00
mono_thread _attach ( mono_get _root _domain ( ) ) ;
while ( xamarin_gc _pump ) {
mono_gc _collect ( mono_gc _max _generation ( ) ) ;
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-04-21 15:19:32 +03:00
usleep ( 1000000 ) ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
}
return NULL ;
}
# endif / * DEBUG * /
2016-09-30 22:02:17 +03:00
# if MONOMAC
2016-04-21 15:19:32 +03:00
static void
detect_product _assembly ( )
{
2016-02-15 21:02:14 +03:00
// COOP : Function only called at startup , I believe the mode here doesn ' t matter
2016-04-21 15:19:32 +03:00
if ( ! xamarin_detect _unified _build )
return ;
char path [ 1024 ] ;
bool unified ;
bool compat ;
snprintf ( path , sizeof ( path ) , "%s/" ARCH_SUBDIR "/%s" , xamarin_get _bundle _path ( ) , PRODUCT_DUAL _ASSEMBLY ) ;
unified = xamarin_file _exists ( path ) ;
snprintf ( path , sizeof ( path ) , "%s/" ARCH_SUBDIR "/%s" , xamarin_get _bundle _path ( ) , PRODUCT_COMPAT _ASSEMBLY ) ;
compat = xamarin_file _exists ( path ) ;
if ( unified && compat ) {
xamarin_assertion _message ( "Found both " PRODUCT_COMPAT _ASSEMBLY " and " PRODUCT_DUAL _ASSEMBLY " in the app. Only one can be present" ) ;
} else if ( ! unified && ! compat ) {
xamarin_assertion _message ( "Found neither " PRODUCT_COMPAT _ASSEMBLY " nor " PRODUCT_DUAL _ASSEMBLY " in the app." ) ;
} else if ( unified ) {
xamarin_use _new _assemblies = true ;
} else {
xamarin_use _new _assemblies = false ;
}
}
2016-09-30 22:02:17 +03:00
# endif
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 )
{
2016-02-15 21:02:14 +03:00
// COOP : Not accessing managed memory : any mode
2016-09-06 23:55:23 +03:00
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 )
{
2016-02-15 21:02:14 +03:00
// COOP : Not accessing managed memory : any mode
2016-09-06 23:55:23 +03:00
PRINT ( "%s" , string ) ;
2016-04-21 15:19:32 +03:00
}
2017-04-10 07:48:35 +03:00
void
xamarin_initialize _embedded ( )
{
static bool initialized = false ;
if ( initialized )
return ;
initialized = true ;
2017-05-16 13:46:51 +03:00
char * argv [ ] = { NULL } ;
char * libname = NULL ;
Dl_info info ;
if ( dladdr ( ( void * ) xamarin_initialize _embedded , & info ) ! = 0 ) {
2017-06-06 23:21:26 +03:00
const char * last_sep = strrchr ( info . dli_fname , ' / ' ) ;
2017-05-16 13:46:51 +03:00
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" ;
2017-04-10 07:48:35 +03:00
xamarin_main ( 1 , argv , XamarinLaunchModeEmbedded ) ;
2017-05-16 13:46:51 +03:00
if ( libname ! = NULL )
free ( libname ) ;
2017-04-10 07:48:35 +03:00
}
2017-05-11 18:44:58 +03:00
/ * Installs g_print / g_error handlers that will redirect output to the system Console * /
void
xamarin_install _log _callbacks ( )
{
mono_trace _set _log _handler ( log_callback , NULL ) ;
mono_trace _set _print _handler ( print_callback ) ;
mono_trace _set _printerr _handler ( print_callback ) ;
}
2016-04-21 15:19:32 +03:00
void
xamarin_initialize ( )
{
2016-02-15 21:02:14 +03:00
// COOP : accessing managed memory : UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
MonoClass * runtime_class ;
MonoAssembly * assembly = NULL ;
MonoImage * image ;
MonoMethod * runtime_initialize ;
void * params [ 2 ] ;
const char * product_dll = NULL ;
2016-06-02 13:18:45 +03:00
guint32 exception_gchandle = 0 ;
MonoObject * exc = NULL ;
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
2016-05-11 14:27:51 +03:00
xamarin_insert _dllmap ( ) ;
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _UNSAFE ;
2016-02-15 21:02:14 +03:00
2017-05-11 18:44:58 +03:00
xamarin_install _log _callbacks ( ) ;
2016-04-21 15:19:32 +03:00
2016-09-30 22:02:17 +03:00
# if MONOMAC
2016-04-21 15:19:32 +03:00
detect_product _assembly ( ) ;
2016-09-30 22:02:17 +03:00
# endif
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 ) ;
2016-06-02 13:18:45 +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
product_dll = xamarin_use _new _assemblies ? PRODUCT_DUAL _ASSEMBLY : PRODUCT_COMPAT _ASSEMBLY ;
2016-05-26 00:20:33 +03:00
assembly = xamarin_open _assembly ( product_dll ) ;
2016-04-21 15:19:32 +03:00
if ( ! assembly )
xamarin_assertion _message ( "Failed to load %s." , product_dll ) ;
image = mono_assembly _get _image ( assembly ) ;
const char * objcruntime = xamarin_use _new _assemblies ? "ObjCRuntime" : PRODUCT_COMPAT _NAMESPACE ".ObjCRuntime" ;
const char * foundation = xamarin_use _new _assemblies ? "Foundation" : PRODUCT_COMPAT _NAMESPACE ".Foundation" ;
runtime_class = get_class _from _name ( image , objcruntime , "Runtime" ) ;
inativeobject_class = get_class _from _name ( image , objcruntime , "INativeObject" ) ;
nsobject_class = get_class _from _name ( image , foundation , "NSObject" ) ;
2016-02-15 20:31:01 +03:00
mono_add _internal _call ( xamarin_use _new _assemblies ? "Foundation.NSObject::xamarin_release_managed_ref" : PRODUCT_COMPAT _NAMESPACE ".Foundation.NSObject::xamarin_release_managed_ref" , ( const void * ) xamarin_release _managed _ref ) ;
mono_add _internal _call ( xamarin_use _new _assemblies ? "Foundation.NSObject::xamarin_create_managed_ref" : PRODUCT_COMPAT _NAMESPACE ".Foundation.NSObject::xamarin_create_managed_ref" , ( const void * ) xamarin_create _managed _ref ) ;
2016-04-21 15:19:32 +03:00
runtime_initialize = mono_class _get _method _from _name ( runtime_class , "Initialize" , 1 ) ;
options . size = sizeof ( options ) ;
# if MONOTOUCH && ( defined ( __i386 __ ) || defined ( __x86 _64 __ ) )
options . flags = ( enum InitializationFlags ) ( options . flags | InitializationFlagsIsSimulator ) ;
# endif
2016-11-01 14:40:51 +03:00
options . Delegates = & delegates ;
options . Trampolines = & trampolines ;
2016-05-11 14:27:51 +03:00
options . MarshalObjectiveCExceptionMode = xamarin_marshal _objectivec _exception _mode ;
options . MarshalManagedExceptionMode = xamarin_marshal _managed _exception _mode ;
2017-05-11 18:44:58 +03:00
# if MONOMAC
options . LaunchMode = xamarin_launch _mode ;
options . EntryAssemblyPath = xamarin_entry _assembly _path ;
# endif
2016-04-21 15:19:32 +03:00
params [ 0 ] = & options ;
2016-06-02 13:18:45 +03:00
mono_runtime _invoke ( runtime_initialize , NULL , params , & exc ) ;
if ( exc )
xamarin_process _managed _exception ( exc ) ;
2016-04-21 15:19:32 +03:00
2016-06-02 13:18:45 +03:00
if ( ! register_assembly ( assembly , & exception_gchandle ) )
xamarin_process _managed _exception _gchandle ( exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
install_nsautoreleasepool _hooks ( ) ;
# if defined ( DEBUG )
if ( xamarin_gc _pump ) {
pthread_t gc_thread ;
pthread_create ( & gc_thread , NULL , pump_gc , NULL ) ;
}
# endif
gc_enable _new _refcount ( ) ;
2016-02-15 21:02:14 +03:00
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
}
static char * x_bundle _path = NULL ;
const char *
xamarin_get _bundle _path ( )
{
2016-02-15 21:02:14 +03:00
// 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 ;
char * result ;
# if MONOMAC
2017-04-28 15:17:42 +03:00
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 = = NULL ? @ "MonoBundle" : xamarin_custom _bundle _name ] ;
2016-04-21 15:19:32 +03:00
# else
bundle_path = [ main_bundle bundlePath ] ;
# endif
if ( main_bundle = = NULL )
xamarin_assertion _message ( "Could not find the main bundle in the app ([NSBundle mainBundle] returned nil)" ) ;
result = mono_path _resolve _symlinks ( [ bundle_path UTF8String ] ) ;
x_bundle _path = strdup ( result ) ;
mono_free ( result ) ;
return x_bundle _path ;
}
void
xamarin_set _bundle _path ( const char * path )
{
2016-02-15 21:02:14 +03:00
// 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_free ( void * ptr )
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode
2016-04-21 15:19:32 +03:00
if ( ptr )
free ( ptr ) ;
}
char *
xamarin_strdup _printf ( const char * msg , . . . )
{
2016-02-15 21:02:14 +03:00
// 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 , . . . )
{
2016-02-15 21:02:14 +03:00
// 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 ) {
2016-09-06 23:55:23 +03:00
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 )
{
2016-02-15 21:02:14 +03:00
// 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 :
2016-05-12 12:36:36 +03:00
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 :
2016-05-12 11:52:58 +03:00
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 : {
2016-07-13 03:19:06 +03:00
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 ;
}
2016-05-12 11:52:58 +03:00
default :
xamarin_assertion _message ( "Unsupported type encoding: %s" , type ) ;
break ;
2016-04-21 15:19:32 +03:00
}
}
int
xamarin_objc _type _size ( const char * type )
{
2016-02-15 21:02:14 +03:00
// 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 * ) ;
2016-05-12 11:52:58 +03:00
case _C _BFLD : {
2016-05-12 12:36:36 +03:00
// Example : [ NSDecimalNumberPlaceholder initWithDecimal : ] = @ 28 @ 0 : 4 { ? = b8b4b1b1b18 [ 8 S ] } 8
int bits = 0 ;
int bc = 1 ;
while ( type [ bc ] >= ' 0 ' && type [ bc ] <= ' 9 ' ) {
bits = bits * 10 + ( type [ bc ] - ' 0 ' ) ;
bc + + ;
}
if ( bits % sizeof ( void * ) = = 0 )
return bits / sizeof ( void * ) ;
return 1 + ( bits / sizeof ( void * ) ) ;
}
2016-05-12 11:52:58 +03:00
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 : {
int size = 0 ;
int len = atoi ( type + 1 ) ;
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 : {
int size = 0 ;
2016-07-13 03:19:06 +03:00
do {
type + + ;
} while ( * type ! = ' = ' ) ;
2016-04-21 15:19:32 +03:00
+ + type ;
do {
int tsize = xamarin_objc _type _size ( type ) ;
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 : {
int size = 0 ;
do {
type + + ;
} while ( * type ! = ' = ' ) ;
type + + ;
while ( * type ! = _C _STRUCT _E ) {
int item_size = xamarin_objc _type _size ( type ) ;
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
2016-05-12 11:52:58 +03:00
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" , type ) ;
}
/ *
* 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 , int flags , bool force_weak )
{
2016-02-15 21:02:14 +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 ) ;
int gchandle ;
if ( weak ) {
gchandle = mono_gchandle _new _weakref ( ( MonoObject * ) managed_object , TRUE ) ;
flags | = GCHANDLE_WEAK ;
} else {
gchandle = mono_gchandle _new ( ( MonoObject * ) managed_object , FALSE ) ;
flags & = ~ GCHANDLE_WEAK ;
}
assert ( ( gchandle & GCHANDLE_MASK ) = = 0 ) ; // Make sure we don ' t create too many gchandles . . .
set_raw _gchandle ( self , gchandle | flags ) ;
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
PRINT ( "\tGCHandle created for %p: %d (flags: %p) = %d %s\n" , self , gchandle , GINT_TO _POINTER ( flags ) , get_raw _gchandle ( self ) , weak ? "weak" : "strong" ) ;
2016-04-21 15:19:32 +03:00
# endif
}
void
xamarin_switch _gchandle ( id self , bool to_weak )
{
2016-02-15 21:02:14 +03:00
// COOP : reads managed memory : unsafe mode
2016-05-16 20:30:58 +03:00
MONO_ASSERT _GC _SAFE _OR _DETACHED ;
2016-02-15 21:02:14 +03:00
2016-06-02 13:18:45 +03:00
guint32 exception_gchandle = 0 ;
2016-04-21 15:19:32 +03:00
int new_gchandle ;
int old_gchandle ;
int old_gchandle _raw ;
MonoObject * managed_object ;
int flags = MANAGED_REF _BIT ;
2016-02-15 21:02:14 +03:00
old_gchandle _raw = get_raw _gchandle _safe ( self ) ;
2016-04-21 15:19:32 +03:00
old_gchandle = old_gchandle _raw & ~ GCHANDLE_MASK ;
if ( old_gchandle ) {
bool is_weak = ( old_gchandle _raw & GCHANDLE_WEAK ) = = GCHANDLE_WEAK ;
if ( to_weak = = is_weak ) {
// we already have the GCHandle we need
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
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 )
2016-09-06 23:55:23 +03:00
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 ;
}
2016-02-15 21:02:14 +03:00
MONO_THREAD _ATTACH ; // COOP : will switch to GC_UNSAFE
2016-04-21 15:19:32 +03:00
if ( old_gchandle ) {
managed_object = mono_gchandle _get _target ( old_gchandle ) ;
} else {
2016-06-02 13:18:45 +03:00
managed_object = xamarin_get _managed _object _for _ptr ( self , & exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
}
2016-06-02 13:18:45 +03:00
if ( exception_gchandle = = 0 ) {
if ( to_weak ) {
new_gchandle = mono_gchandle _new _weakref ( managed_object , TRUE ) ;
flags | = GCHANDLE_WEAK ;
} else {
new_gchandle = mono_gchandle _new ( managed_object , FALSE ) ;
}
if ( old_gchandle )
mono_gchandle _free ( old_gchandle ) ;
2016-07-27 13:47:35 +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-06-02 13:18:45 +03:00
set_raw _gchandle ( self , new_gchandle | flags ) ;
2016-04-21 15:19:32 +03:00
}
2016-02-15 21:02:14 +03:00
MONO_THREAD _DETACH ; // COOP : this will switch to GC_SAFE
2016-04-21 15:19:32 +03:00
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
PRINT ( "Switched object %p to %s GCHandle = %d\n" , self , to_weak ? "weak" : "strong" , new_gchandle ) ;
2016-04-21 15:19:32 +03:00
# endif
2016-06-02 13:18:45 +03:00
xamarin_process _managed _exception _gchandle ( exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
}
void
xamarin_free _gchandle ( id self , int gchandle )
{
2016-02-15 21:02:14 +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 )
2016-09-06 23:55:23 +03:00
PRINT ( "\tGCHandle %i destroyed for object %p\n" , gchandle , self ) ;
2016-04-21 15:19:32 +03:00
# endif
mono_gchandle _free ( gchandle ) ;
set_raw _gchandle ( self , 0 ) ;
} else {
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
PRINT ( "\tNo GCHandle for the object %p\n" , self ) ;
2016-04-21 15:19:32 +03:00
# endif
}
}
void
xamarin_clear _gchandle ( id self )
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode
2016-04-21 15:19:32 +03:00
set_raw _gchandle ( self , 0 ) ;
}
void
xamarin_set _gchandle ( id self , int gchandle )
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode
2016-04-21 15:19:32 +03:00
set_raw _gchandle ( self , gchandle ) ;
}
static inline bool
is_user _type ( id self )
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode
2016-04-21 15:19:32 +03:00
return class_getInstanceMethod ( object_getClass ( self ) , @ selector ( xamarinSetGCHandle : ) ) ! = NULL ;
}
# if defined ( DEBUG_REF _COUNTING )
int
get_safe _retainCount ( id self )
{
2016-02-15 21:02:14 +03:00
// 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 [ self retainCount ] ;
}
}
# endif
2016-02-15 20:31:01 +03:00
void
2016-04-21 15:19:32 +03:00
xamarin_release _managed _ref ( id self , MonoObject * managed_obj )
{
2016-02-15 21:02:14 +03:00
// COOP : This is an icall , so at entry we ' re in unsafe mode .
// COOP : we stay in unsafe mode ( since we write to the managed memory ) unless calling a selector ( which must be done in safe mode )
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
bool user_type = is_user _type ( self ) ;
2016-06-02 13:18:45 +03:00
guint32 exception_gchandle = 0 ;
2016-04-21 15:19:32 +03:00
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
PRINT ( "monotouch_release_managed_ref (%s Handle=%p) retainCount=%d; HasManagedRef=%i GCHandle=%i IsUserType=%i\n" ,
2016-04-21 15:19:32 +03:00
class_getName ( object_getClass ( self ) ) , self , ( int32_t ) [ self retainCount ] , user_type ? xamarin_has _managed _ref ( self ) : 666 , user_type ? get_gchandle ( self ) : 666 , user_type ) ;
# endif
xamarin_set _nsobject _flags ( managed_obj , xamarin_get _nsobject _flags ( managed_obj ) & ~ NSObjectFlagsHasManagedRef ) ;
if ( user_type ) {
/ * clear MANAGED_REF _BIT * /
set_raw _gchandle ( self , get_raw _gchandle ( self ) & ~ MANAGED_REF _BIT ) ;
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-04-21 15:19:32 +03:00
[ self release ] ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
} else {
// This lock is needed so that we can safely call retainCount in the toggleref callback .
xamarin_framework _peer _lock ( ) ;
/ * If we ' re a wrapper type , we need to unregister here , since we won ' t enter the release trampoline * /
2016-06-02 13:18:45 +03:00
xamarin_unregister _nsobject ( self , managed_obj , & exception_gchandle ) ;
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-04-21 15:19:32 +03:00
[ self release ] ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
xamarin_framework _peer _unlock ( ) ;
}
2016-06-02 13:18:45 +03:00
xamarin_process _managed _exception _gchandle ( exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
}
void
xamarin_create _managed _ref ( id self , gpointer managed_object , bool retain )
{
2016-02-15 21:02:14 +03:00
// COOP : This is an icall , so at entry we ' re in unsafe mode .
// COOP : we stay in unsafe mode ( since we write to the managed memory ) unless calling a selector ( which must be done in safe mode )
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
int gchandle ;
bool user_type = is_user _type ( self ) ;
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
PRINT ( "monotouch_create_managed_ref (%s Handle=%p) retainCount=%d; HasManagedRef=%i GCHandle=%i IsUserType=%i\n" ,
2016-04-21 15:19:32 +03:00
class_getName ( [ self class ] ) , self , get_safe _retainCount ( self ) , user_type ? xamarin_has _managed _ref ( self ) : 666 , user_type ? get_gchandle ( self ) : 666 , user_type ) ;
# endif
xamarin_set _nsobject _flags ( ( MonoObject * ) managed_object , xamarin_get _nsobject _flags ( ( MonoObject * ) managed_object ) | NSObjectFlagsHasManagedRef ) ;
if ( user_type ) {
gchandle = get_gchandle ( self ) ;
if ( ! gchandle ) {
xamarin_create _gchandle ( self , managed_object , MANAGED_REF _BIT , ! retain ) ;
} else {
# if defined ( DEBUG_REF _COUNTING )
xamarin_assertion _message ( "GCHandle already exists for %p: %d\n" , self , gchandle ) ;
# endif
}
}
2016-02-15 21:02:14 +03:00
if ( retain ) {
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-04-21 15:19:32 +03:00
[ self retain ] ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _SAFE ;
2016-02-15 21:02:14 +03:00
}
2016-04-21 15:19:32 +03:00
mt_dummy _use ( managed_object ) ;
}
/ *
* Block support
* /
typedef struct {
MonoMethod * method ;
int par ;
} 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 MonoObject *
2016-06-02 13:18:45 +03:00
get_method _block _wrapper _creator ( MonoMethod * method , int par , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +03:00
// COOP : accesses managed memory : unsafe mode .
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
MonoObject * res = NULL ;
MethodAndPar mp , * nmp ;
mp . method = method ;
mp . par = par ;
2016-09-06 23:55:23 +03:00
// PRINT ( "Looking up method and par (%x and %d)" , ( int ) method , par ) ;
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-04-21 15:19:32 +03:00
pthread_mutex _lock ( & wrapper_hash _lock ) ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _SAFE ;
2016-02-15 21:02:14 +03:00
2016-04-21 15:19:32 +03:00
if ( block_wrapper _queue = = NULL )
block_wrapper _queue = mono_gc _reference _queue _new ( ( void (*)(void*) ) _Block _release ) ;
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 ) {
2016-09-06 23:55:23 +03:00
// PRINT ( "Found match: %x" , ( int ) res ) ;
2016-04-21 15:19:32 +03:00
return res ;
}
2016-06-02 13:18:45 +03:00
res = xamarin_get _block _wrapper _creator ( ( MonoObject * ) mono_method _get _object ( mono_domain _get ( ) , method , NULL ) , par , exception_gchandle ) ;
if ( * exception_gchandle ! = 0 )
return NULL ;
2016-09-06 23:55:23 +03:00
// PRINT ( "New value: %x" , ( int ) res ) ;
2016-04-21 15:19:32 +03:00
nmp = ( MethodAndPar * ) malloc ( sizeof ( MethodAndPar ) ) ;
* nmp = mp ;
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-04-21 15:19:32 +03:00
pthread_mutex _lock ( & wrapper_hash _lock ) ;
2016-05-20 18:40:05 +03:00
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 ) ;
return res ;
}
/ *
* 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 .
* /
int *
2016-06-02 13:18:45 +03:00
xamarin_get _delegate _for _block _parameter ( MonoMethod * method , int par , void * nativeBlock , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +03:00
// COOP : accesses managed memory : unsafe mode .
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
MonoObject * delegate ;
if ( nativeBlock = = NULL )
return NULL ;
2016-06-02 13:18:45 +03:00
MonoObject * obj = get_method _block _wrapper _creator ( method , par , exception_gchandle ) ;
if ( * exception_gchandle ! = 0 )
return NULL ;
2016-04-21 15:19:32 +03:00
/ * retain or copy ( if it ' s a stack block ) the block * /
nativeBlock = _Block _copy ( nativeBlock ) ;
2016-06-02 13:18:45 +03:00
delegate = delegates . create_block _proxy ( obj , nativeBlock , exception_gchandle ) ;
if ( * exception_gchandle ! = 0 ) {
_Block _release ( nativeBlock ) ;
return NULL ;
}
2016-04-21 15:19:32 +03:00
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _SAFE ;
2016-04-21 15:19:32 +03:00
pthread_mutex _lock ( & wrapper_hash _lock ) ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
mono_gc _reference _queue _add ( block_wrapper _queue , delegate , nativeBlock ) ;
pthread_mutex _unlock ( & wrapper_hash _lock ) ;
return ( int * ) delegate ;
}
id
2016-06-02 13:18:45 +03:00
xamarin_get _block _for _delegate ( MonoMethod * method , MonoObject * delegate , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +03:00
// COOP : accesses managed memory : unsafe mode .
2016-06-02 13:18:45 +03:00
return delegates . create_delegate _proxy ( ( MonoObject * ) mono_method _get _object ( mono_domain _get ( ) , method , NULL ) , delegate , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
}
void
xamarin_set _use _sgen ( bool value )
{
}
bool
xamarin_get _use _sgen ( )
{
return true ;
}
2016-09-30 22:02:17 +03:00
# if MONOMAC
2016-04-21 15:19:32 +03:00
void
xamarin_set _is _unified ( bool value )
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode .
2016-04-21 15:19:32 +03:00
if ( initialize_started )
xamarin_assertion _message ( "Fatal error: xamarin_set_is_unified called after xamarin_initialize.\n" ) ;
xamarin_use _new _assemblies = value ;
xamarin_detect _unified _build = false ;
}
2016-09-30 22:02:17 +03:00
# endif
2016-04-21 15:19:32 +03:00
bool
xamarin_get _is _unified ( )
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode .
2016-04-21 15:19:32 +03:00
return xamarin_use _new _assemblies ;
}
void
xamarin_set _gc _pump _enabled ( bool value )
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode .
2016-04-21 15:19:32 +03:00
xamarin_gc _pump = value ;
}
const char *
xamarin_skip _encoding _flags ( const char * encoding )
{
2016-02-15 21:02:14 +03:00
// 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 ;
}
}
}
2016-05-11 14:27:51 +03:00
void
xamarin_process _nsexception ( NSException * ns_exception )
{
2016-06-06 15:33:37 +03:00
xamarin_process _nsexception _using _mode ( ns_exception , false ) ;
}
void
xamarin_process _nsexception _using _mode ( NSException * ns_exception , bool throwManagedAsDefault )
{
2016-05-11 14:27:51 +03:00
XamarinGCHandle * exc_handle ;
2016-06-02 13:18:45 +03:00
guint32 exception_gchandle = 0 ;
2016-06-06 15:33:37 +03:00
MarshalObjectiveCExceptionMode mode ;
2016-06-02 13:18:45 +03:00
mode = xamarin_on _marshal _objectivec _exception ( ns_exception , throwManagedAsDefault , & exception_gchandle ) ;
if ( exception_gchandle ! = 0 ) {
2016-09-06 23:55:23 +03:00
PRINT ( PRODUCT ": Got an exception while executing the MarshalObjectiveCException event (this exception will be ignored):" ) ;
PRINT ( "%@" , print_all _exceptions ( mono_gchandle _get _target ( exception_gchandle ) ) ) ;
2016-06-02 13:18:45 +03:00
mono_gchandle _free ( exception_gchandle ) ;
exception_gchandle = 0 ;
}
2016-05-11 14:27:51 +03:00
if ( mode = = MarshalObjectiveCExceptionModeDefault )
mode = xamarin_is _gc _coop ? MarshalObjectiveCExceptionModeThrowManagedException : MarshalObjectiveCExceptionModeUnwindManagedCode ;
2016-06-06 15:33:37 +03:00
2016-05-11 14:27:51 +03:00
switch ( mode ) {
case MarshalObjectiveCExceptionModeUnwindManagedCode :
if ( xamarin_is _gc _coop )
2016-05-17 12:17:47 +03:00
xamarin_assertion _message ( "Cannot unwind managed frames for Objective-C exceptions when the GC is in cooperative mode." ) ;
2016-05-11 14:27:51 +03:00
@ throw ns_exception ;
break ;
case MarshalObjectiveCExceptionModeThrowManagedException :
exc_handle = [ [ ns_exception userInfo ] objectForKey : @ "XamarinManagedExceptionHandle" ] ;
if ( exc_handle ! = NULL ) {
int handle = [ exc_handle getHandle ] ;
MonoObject * exc = mono_gchandle _get _target ( handle ) ;
mono_set _pending _exception ( ( MonoException * ) exc ) ;
} else {
2016-06-02 13:18:45 +03:00
int handle = xamarin_create _ns _exception ( ns_exception , & exception_gchandle ) ;
if ( exception_gchandle ! = 0 ) {
2016-09-06 23:55:23 +03:00
PRINT ( PRODUCT ": Got an exception while creating a managed NSException wrapper (will throw this exception instead):" ) ;
PRINT ( "%@" , print_all _exceptions ( mono_gchandle _get _target ( exception_gchandle ) ) ) ;
2016-06-02 13:18:45 +03:00
handle = exception_gchandle ;
exception_gchandle = 0 ;
}
2016-05-20 18:40:05 +03:00
MONO_ENTER _GC _UNSAFE ;
2016-05-11 14:27:51 +03:00
MonoObject * exc = mono_gchandle _get _target ( handle ) ;
mono_set _pending _exception ( ( MonoException * ) exc ) ;
mono_gchandle _free ( handle ) ;
2016-05-20 18:40:05 +03:00
MONO_EXIT _GC _UNSAFE ;
2016-05-11 14:27:51 +03:00
}
break ;
case MarshalObjectiveCExceptionModeAbort :
default :
xamarin_assertion _message ( "Aborting due to unhandled Objective-C exception:\n%s\n" , [ [ ns_exception description ] UTF8String ] ) ;
break ;
}
}
void
xamarin_process _managed _exception ( MonoObject * exception )
{
if ( exception = = NULL )
return ;
MarshalManagedExceptionMode mode ;
2016-06-02 13:18:45 +03:00
guint32 exception_gchandle = 0 ;
2016-05-11 14:27:51 +03:00
int handle = mono_gchandle _new ( exception , false ) ;
2016-06-02 13:18:45 +03:00
mode = xamarin_on _marshal _managed _exception ( handle , & exception_gchandle ) ;
2016-05-11 14:27:51 +03:00
mono_gchandle _free ( handle ) ;
2016-06-02 13:18:45 +03:00
if ( exception_gchandle ! = 0 ) {
2016-09-06 23:55:23 +03:00
PRINT ( PRODUCT ": Got an exception while executing the MarshalManagedCException event (this exception will be ignored):" ) ;
PRINT ( "%@" , print_all _exceptions ( mono_gchandle _get _target ( exception_gchandle ) ) ) ;
2016-06-02 13:18:45 +03:00
mono_gchandle _free ( exception_gchandle ) ;
exception_gchandle = 0 ;
mode = MarshalManagedExceptionModeDefault ;
}
2016-05-11 14:27:51 +03:00
if ( mode = = MarshalManagedExceptionModeDefault )
mode = xamarin_is _gc _coop ? MarshalManagedExceptionModeThrowObjectiveCException : MarshalManagedExceptionModeUnwindNativeCode ;
switch ( mode ) {
2016-06-02 13:18:45 +03:00
case MarshalManagedExceptionModeDisable :
2016-05-11 14:27:51 +03:00
case MarshalManagedExceptionModeUnwindNativeCode :
if ( xamarin_is _gc _coop )
2016-05-17 12:17:47 +03:00
xamarin_assertion _message ( "Cannot unwind native frames for managed exceptions when the GC is in cooperative mode." ) ;
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 = mono_gchandle _new ( exception , false ) ;
xamarin_rethrow _managed _exception ( handle , & exception_gchandle ) ;
mono_gchandle _free ( handle ) ;
if ( exception_gchandle = = 0 ) {
PRINT ( PRODUCT ": Did not get a rethrow exception, will throw the original exception. The original stack trace will be lost." ) ;
} else {
exception = mono_gchandle _get _target ( exception_gchandle ) ;
mono_gchandle _free ( exception_gchandle ) ;
}
2016-05-11 14:27:51 +03:00
mono_raise _exception ( ( MonoException * ) exception ) ;
2016-10-27 21:03:11 +03:00
2016-05-11 14:27:51 +03:00
break ;
case MarshalManagedExceptionModeThrowObjectiveCException : {
int handle = mono_gchandle _new ( exception , false ) ;
2016-06-02 13:18:45 +03:00
NSException * ns_exc = xamarin_unwrap _ns _exception ( handle , & exception_gchandle ) ;
2016-05-11 14:27:51 +03:00
2016-06-02 13:18:45 +03:00
if ( exception_gchandle ! = 0 ) {
2016-09-06 23:55:23 +03:00
PRINT ( PRODUCT ": Got an exception while unwrapping a managed NSException wrapper (this exception will be ignored):" ) ;
PRINT ( "%@" , print_all _exceptions ( mono_gchandle _get _target ( exception_gchandle ) ) ) ;
2016-06-02 13:18:45 +03:00
mono_gchandle _free ( exception_gchandle ) ;
exception_gchandle = 0 ;
ns_exc = NULL ;
}
2016-05-11 14:27:51 +03:00
if ( ns_exc ! = NULL ) {
mono_gchandle _free ( handle ) ;
@ throw ns_exc ;
} else {
2016-06-02 13:18:45 +03:00
// 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 ;
const char * fullname ;
MONO_THREAD _ATTACH ; // COOP : will switch to GC_UNSAFE
fullname = xamarin_type _get _full _name ( mono_class _get _type ( mono_object _get _class ( exception ) ) , & exception_gchandle ) ;
if ( exception_gchandle ! = 0 ) {
2016-09-06 23:55:23 +03:00
PRINT ( PRODUCT ": Got an exception when trying to get the typename for an exception (this exception will be ignored):" ) ;
PRINT ( "%@" , print_all _exceptions ( mono_gchandle _get _target ( exception_gchandle ) ) ) ;
2016-06-02 13:18:45 +03:00
mono_gchandle _free ( exception_gchandle ) ;
exception_gchandle = 0 ;
fullname = "Unknown" ;
}
name = [ NSString stringWithUTF8String : fullname ] ;
2016-05-11 14:27:51 +03:00
char * message = fetch_exception _property _string ( exception , "get_Message" , true ) ;
2016-06-02 13:18:45 +03:00
reason = [ NSString stringWithUTF8String : message ] ;
2016-05-11 14:27:51 +03:00
mono_free ( message ) ;
2016-06-02 13:18:45 +03:00
userInfo = [ NSDictionary dictionaryWithObject : [ XamarinGCHandle createWithHandle : handle ] forKey : @ "XamarinManagedExceptionHandle" ] ;
MONO_THREAD _DETACH ; // COOP : this will switch to GC_SAFE
2016-05-11 14:27:51 +03:00
@ throw [ [ NSException alloc ] initWithName : name reason : reason userInfo : userInfo ] ;
}
break ;
}
case MarshalManagedExceptionModeAbort :
default :
xamarin_assertion _message ( "Aborting due to:\n%s\n" , [ print_all _exceptions ( exception ) UTF8String ] ) ;
break ;
}
}
2016-06-02 13:18:45 +03:00
void
xamarin_throw _product _exception ( int code , const char * message )
{
guint32 exception_gchandle = 0 ;
guint32 handle = xamarin_create _product _exception _for _error ( code , message , & exception_gchandle ) ;
if ( exception_gchandle ! = 0 ) {
xamarin_process _managed _exception _gchandle ( exception_gchandle ) ;
} else {
xamarin_process _managed _exception _gchandle ( handle ) ;
}
}
2016-05-11 14:27:51 +03:00
void
xamarin_insert _dllmap ( )
{
2016-05-12 15:29:50 +03:00
# if defined ( OBJC_ZEROCOST _EXCEPTIONS ) && ( defined ( __i386 __ ) || defined ( __x86 _64 __ ) )
2016-05-11 14:27:51 +03:00
if ( xamarin_marshal _objectivec _exception _mode = = MarshalObjectiveCExceptionModeDisable )
return ;
2016-05-12 15:30:23 +03:00
# if DYLIB
2016-05-11 14:27:51 +03:00
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" ) ;
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" ) ;
LOG ( PRODUCT ": Added dllmap for objc_msgSend" ) ;
# endif // defined ( __i386 __ ) || defined ( __x86 _64 __ )
}
2016-09-06 23:55:23 +03:00
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 ] ;
int len = strlen ( msg ) ;
fwrite ( msg , 1 , len , stdout ) ;
2016-09-13 17:04:15 +03:00
if ( len = = 0 || msg [ len - 1 ] ! = ' \ n ' )
2016-09-06 23:55:23 +03:00
fwrite ( "\n" , 1 , 1 , stdout ) ;
fflush ( stdout ) ;
# else
NSLog ( @ "%@" , message ) ;
# endif
[ message release ] ;
}
2016-12-12 20:57:02 +03:00
/ *
* 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 .
2017-01-24 12:30:23 +03:00
* 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 .
2016-12-12 20:57:02 +03:00
* 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 ) .
*
* AOT data files will have an arch - specific infix .
* /
void
xamarin_get _assembly _name _without _extension ( const char * aname , char * name , int namelen )
{
int len = strlen ( aname ) ;
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 .
}
static bool
xamarin_locate _assembly _resource _for _root ( const char * root , const char * culture , const char * resource , char * path , int 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
// 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 ;
}
bool
xamarin_locate _assembly _resource _for _name ( MonoAssemblyName * assembly_name , const char * resource , char * path , int 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 ) ;
}
// # 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 , int 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 ;
}
2017-04-18 14:34:56 +03:00
# 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
2016-12-12 20:57:02 +03:00
// Then in a framework named as the assembly
2017-04-12 16:54:30 +03:00
snprintf ( root , sizeof ( root ) , "%s/Frameworks/%s.framework/MonoBundle" , app_path , aname ) ;
2016-12-12 20:57:02 +03:00
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 ;
}
2017-01-24 12:30:23 +03:00
// Then in the container app ' s root directory ( for extensions )
2017-04-28 15:02:38 +03:00
if ( xamarin_launch _mode = = XamarinLaunchModeExtension ) {
2017-01-24 12:30:23 +03:00
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 ;
}
}
2016-12-12 20:57:02 +03:00
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 ;
}
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 02474 ac1dd 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 ( )
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode .
2016-04-21 15:19:32 +03:00
xamarin_notify _dealloc ( native_object , gc_handle & ~ GCHANDLE_MASK ) ;
native_object = NULL ;
gc_handle = 0 ;
}
/ *
* 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
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode .
2016-04-21 15:19:32 +03:00
xamarin_notify _dealloc ( native_object , gc_handle & ~ GCHANDLE_MASK ) ;
native_object = NULL ;
gc_handle = 0 ;
[ 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 : method , since we use the presence
* of it to detect whether a particular type is a user type or not
* ( see is_user _type ) .
* /
@ implementation NSObject ( NonXamarinObject )
- ( int ) xamarinGetGCHandle
{
2016-02-15 21:02:14 +03:00
// COOP : no managed memory access : any mode .
2016-04-21 15:19:32 +03:00
return 0 ;
}
@ 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 ;
}
2016-05-11 14:27:51 +03:00
2016-10-28 20:07:01 +03:00
bool
xamarin_is _managed _exception _marshaling _disabled ( )
{
# if 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
}
2016-05-11 14:27:51 +03:00
/ *
* XamarinGCHandle
* /
@ implementation XamarinGCHandle
+ ( XamarinGCHandle * ) createWithHandle : ( int ) h
{
XamarinGCHandle * rv = [ [ XamarinGCHandle alloc ] init ] ;
rv -> handle = h ;
[ rv autorelease ] ;
return rv ;
}
- ( void ) dealloc
{
mono_gchandle _free ( handle ) ;
[ super dealloc ] ;
}
- ( int ) getHandle
{
return handle ;
}
@ end