2016-04-21 15:19:32 +03:00
/ * - * - Mode : C ; tab - width : 8 ; indent - tabs - mode : t ; c - basic - offset : 8 - * - * /
/ *
* Authors : Rolf Bjarne Kvinge
*
* Copyright ( C ) 2014 Xamarin Inc . ( www . xamarin . com )
*
* /
# include < pthread . h >
# include < objc / runtime . h >
# include < sys / stat . h >
# include "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
bool xamarin_use _new _assemblies = false ;
[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 ;
# else
bool xamarin_debug _mode = false ;
# endif
// true if either OldDynamic or OldStatic ( since the static registrar still needs
// a dynamic registrar available too ) .
bool xamarin_use _old _dynamic _registrar = false ;
bool xamarin_use _il _registrar = false ;
# if DEBUG
bool xamarin_init _mono _debug = true ;
# else
bool xamarin_init _mono _debug = false ;
# endif
# if DEBUG && ( defined ( __i386 __ ) || defined ( __x86 _64 __ ) )
bool xamarin_compact _seq _points = false ;
# else
bool xamarin_compact _seq _points = true ;
# 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 ;
# 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
/ * 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 * /
typedef struct {
struct MTRegistrationMap * map ;
int total_count ; // SUM ( registration_map -> map_count )
} RegistrationData ;
static RegistrationData registration_data ;
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 , * /
InitializationFlagsUseOldDynamicRegistrar = 0 x02 ,
InitializationFlagsDynamicRegistrar = 0 x04 ,
InitializationFlagsILRegistrar = 0 x08 ,
InitializationFlagsIsSimulator = 0 x10 ,
} ;
struct InitializationOptions {
int size ; // the size of this structure . This is used for version checking .
enum InitializationFlags flags ;
struct Delegates Delegates ;
struct Trampolines Trampolines ;
RegistrationData * RegistrationData ;
} ;
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 ,
} ;
struct Managed_NSObject {
MonoObject obj ;
id handle ;
void * class_handle ;
uint8_t flags ;
} ;
id
xamarin_get _nsobject _handle ( MonoObject * obj )
{
struct Managed_NSObject * mobj = ( struct Managed_NSObject * ) obj ;
return mobj -> handle ;
}
void
xamarin_set _nsobject _handle ( MonoObject * obj , id handle )
{
struct Managed_NSObject * mobj = ( struct Managed_NSObject * ) obj ;
mobj -> handle = handle ;
}
uint8_t
xamarin_get _nsobject _flags ( MonoObject * obj )
{
struct Managed_NSObject * mobj = ( struct Managed_NSObject * ) obj ;
return mobj -> flags ;
}
void
xamarin_set _nsobject _flags ( MonoObject * obj , uint8_t flags )
{
struct Managed_NSObject * mobj = ( struct Managed_NSObject * ) obj ;
mobj -> flags = flags ;
}
MonoType *
xamarin_get _parameter _type ( MonoMethod * managed_method , int index )
{
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 *
xamarin_get _nsobject _with _type _for _ptr ( id self , bool owns , MonoType * type )
{
int32_t created ;
return xamarin_get _nsobject _with _type _for _ptr _created ( self , owns , type , & created ) ;
}
MonoObject *
xamarin_get _nsobject _with _type _for _ptr _created ( id self , bool owns , MonoType * type , int32_t * created )
{
MonoObject * mobj = NULL ;
uint32_t gchandle = 0 ;
* created = false ;
if ( self = = NULL )
return NULL ;
if ( mono_domain _get ( ) = = NULL )
mono_jit _thread _attach ( 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 ;
}
return xamarin_get _nsobject _with _type ( self , mono_type _get _object ( mono_domain _get ( ) , type ) , created ) ;
}
MonoObject *
xamarin_get _managed _object _for _ptr ( id self )
{
MonoObject * mobj = NULL ;
uint32_t gchandle = 0 ;
if ( self = = NULL )
return NULL ;
if ( mono_domain _get ( ) = = NULL )
mono_jit _thread _attach ( NULL ) ;
gchandle = xamarin_get _gchandle ( self ) ;
if ( gchandle = = 0 ) {
mobj = ( MonoObject * ) xamarin_try _get _or _construct _nsobject ( self ) ;
} else {
mobj = mono_gchandle _get _target ( gchandle ) ;
}
return mobj ;
}
MonoObject *
xamarin_get _managed _object _for _ptr _fast ( id self )
{
MonoObject * mobj = NULL ;
uint32_t gchandle = 0 ;
gchandle = xamarin_get _gchandle ( self ) ;
if ( gchandle = = 0 ) {
mobj = ( MonoObject * ) xamarin_try _get _or _construct _nsobject ( self ) ;
} 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" ,
mobj , xamarin_get _nsobject _handle ( mobj ) , xamarin_class _get _full _name ( mono_object _get _class ( mobj ) ) , self , object_getClassName ( self ) ) ;
}
# endif
}
return mobj ;
}
void xamarin_framework _peer _lock ( )
{
pthread_mutex _lock ( & framework_peer _release _lock ) ;
}
void xamarin_framework _peer _unlock ( )
{
pthread_mutex _unlock ( & framework_peer _release _lock ) ;
}
bool
xamarin_is _class _nsobject ( MonoClass * cls )
{
return mono_class _is _subclass _of ( cls , nsobject_class , false ) ;
}
bool
xamarin_is _class _inativeobject ( MonoClass * cls )
{
return mono_class _is _subclass _of ( cls , inativeobject_class , true ) ;
}
bool
xamarin_is _class _array ( MonoClass * cls )
{
// return cls -> type = = MONO_TYPE _SZARRAY ;
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
get_raw _gchandle ( id self )
{
id < XamarinExtendedObject > xself = self ;
return ( int ) [ xself xamarinGetGCHandle ] ;
}
static inline void
set_raw _gchandle ( id self , int gc_handle )
{
id < XamarinExtendedObject > xself = self ;
[ xself xamarinSetGCHandle : gc_handle ] ;
}
static inline int
get_gchandle ( id self )
{
return get_raw _gchandle ( self ) & ~ GCHANDLE_MASK ;
}
int
xamarin_get _gchandle ( id self )
{
return get_gchandle ( self ) ;
}
int
xamarin_get _gchandle _with _flags ( id self )
{
return get_raw _gchandle ( self ) ;
}
bool
xamarin_has _managed _ref ( id self )
{
return get_raw _gchandle ( self ) & MANAGED_REF _BIT ;
}
MonoException *
xamarin_create _exception ( const char * msg )
{
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 )
{
PublicMonoReflectionMethod * rm = ( PublicMonoReflectionMethod * ) method ;
return rm -> method ;
}
id
xamarin_get _handle ( MonoObject * obj )
{
MonoClass * klass ;
id rv = nil ;
if ( obj = = NULL )
return nil ;
klass = mono_object _get _class ( obj ) ;
if ( xamarin_is _class _nsobject ( klass ) ) {
rv = xamarin_get _nsobject _handle ( obj ) ;
} else if ( xamarin_is _class _inativeobject ( klass ) ) {
rv = xamarin_get _handle _for _inativeobject ( obj ) ;
} 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 ) ;
mono_raise _exception ( exc ) ;
}
return rv ;
}
# if DEBUG
static void
verify_cast ( MonoClass * to , MonoObject * obj , Class from_class , SEL sel , MonoMethod * method )
{
if ( ! to )
return ;
if ( mono_object _isinst ( obj , to ) = = NULL ) {
MonoClass * from = mono_object _get _class ( obj ) ;
char * method_full _name = mono_method _full _name ( method , TRUE ) ;
char * from_name = xamarin_class _get _full _name ( from ) ;
char * to_name = xamarin_class _get _full _name ( to ) ;
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 ) ;
mono_raise _exception ( mono_ex ) ;
}
}
# endif
void
xamarin_check _for _gced _object ( MonoObject * obj , SEL sel , id self , MonoMethod * method )
{
if ( obj ! = NULL ) {
# if DEBUG
verify_cast ( mono_method _get _class ( method ) , obj , [ self class ] , sel , method ) ;
# 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 ) ;
char * type_name = xamarin_lookup _managed _type _name ( [ self class ] ) ;
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 ) ;
mono_free ( type_name ) ;
mono_free ( method_full _name ) ;
mono_raise _exception ( mex ) ;
}
# 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 )
{
// 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 )
{
// 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 *
xamarin_class _get _full _name ( MonoClass * klass )
{
return xamarin_type _get _full _name ( mono_class _get _type ( klass ) ) ;
}
char *
xamarin_type _get _full _name ( MonoType * type )
{
return xamarin_reflection _type _get _full _name ( mono_type _get _object ( mono_domain _get ( ) , type ) ) ;
}
/ *
* ToggleRef support
* /
// # define DEBUG_TOGGLEREF 1
static void
gc_register _toggleref ( MonoObject * obj , id self , bool isCustomType )
{
# ifdef DEBUG_TOGGLEREF
id handle = xamarin_get _nsobject _handle ( obj ) ;
NSLog ( @ "**Registering object %p handle %p RC %d flags: %i" ,
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 .
if ( isCustomType )
xamarin_switch _gchandle ( self , true ) ;
}
static MonoToggleRefStatus
gc_toggleref _callback ( MonoObject * object )
{
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 ) ;
}
NSLog ( @ "\tinspecting %p handle:%p %s flags: %i RC %d -> %s\n" , object , handle , cn , ( int ) flags , ( int ) ( handle ? [ handle retainCount ] : 0 ) , rv ) ;
# endif
return res ;
}
typedef struct {
int dummy ;
} NRCProfiler ;
static void
gc_event _callback ( MonoProfiler * prof , MonoGCEvent event , int generation )
{
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 )
{
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 )
{
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 )
{
struct stat buffer ;
return stat ( path , & buffer ) = = 0 ;
}
static MonoAssembly *
open_assembly ( const char * name )
{
char path [ 1024 ] ;
MonoAssembly * assembly ;
bool exists = false ;
# if MONOMAC
if ( xamarin_get _is _mkbundle ( ) ) {
assembly = mono_assembly _open ( name , NULL ) ;
if ( assembly = = NULL ) {
NSLog ( @ PRODUCT ": 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 ) ;
exit ( 1 ) ;
}
return assembly ;
}
# endif
if ( xamarin_use _new _assemblies ) {
snprintf ( path , sizeof ( path ) , "%s/" ARCH_SUBDIR "/%s" , xamarin_get _bundle _path ( ) , name ) ;
exists = xamarin_file _exists ( path ) ;
}
if ( ! exists )
snprintf ( path , sizeof ( path ) , "%s/%s" , xamarin_get _bundle _path ( ) , name ) ;
# if MONOMAC && DYLIB
if ( ! xamarin_file _exists ( path ) ) {
// Check if we already have the assembly in memory
char path2 [ 1024 ] ;
snprintf ( path2 , sizeof ( path2 ) , "%s" , name ) ;
// strip off the extension
char * dot = strrchr ( path2 , ' . ' ) ;
if ( strncmp ( dot , ".dll" , 4 ) = = 0 )
* dot = 0 ;
MonoAssemblyName * aname = mono_assembly _name _new ( path2 ) ;
assembly = mono_assembly _loaded ( aname ) ;
mono_assembly _name _free ( aname ) ;
if ( assembly )
return assembly ;
NSLog ( @ PRODUCT ": 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 ) ;
exit ( 1 ) ;
}
# endif
if ( ! xamarin_file _exists ( path ) ) {
NSLog ( @ PRODUCT ": 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 ) ;
exit ( 1 ) ;
}
assembly = mono_assembly _open ( path , NULL ) ;
if ( assembly = = NULL ) {
NSLog ( @ PRODUCT ": 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 ) ;
exit ( 1 ) ;
}
return assembly ;
}
static void
register_assembly ( MonoAssembly * assembly )
{
xamarin_register _assembly ( mono_assembly _get _object ( mono_domain _get ( ) , assembly ) ) ;
}
MonoAssembly *
xamarin_open _and _register ( const char * aname )
{
MonoAssembly * assembly ;
assembly = open_assembly ( aname ) ;
register_assembly ( assembly ) ;
return assembly ;
}
static gboolean
is_class _finalization _aware ( MonoClass * cls )
{
gboolean rv = false ;
if ( nsobject_class )
rv = cls = = nsobject_class || mono_class _is _assignable _from ( nsobject_class , cls ) ;
// NSLog ( @ "IsClass %s.%s finalization aware: %i\n" , mono_class _get _namespace ( cls ) , mono_class _get _name ( cls ) , rv ) ;
return rv ;
}
static void
object_queued _for _finalization ( MonoObject * object )
{
/ * This is called with the GC lock held , so it can only use signal - safe code * /
struct Managed_NSObject * obj = ( struct Managed_NSObject * ) object ;
// NSLog ( @ "In finalization response for %s.%s %p (handle: %p class_handle: %p flags: %i)\n" ,
obj -> flags | = NSObjectFlagsInFinalizerQueue ;
}
/ *
* Registration map
* /
void
xamarin_add _registration _map ( struct MTRegistrationMap * map )
{
map -> next = registration_data . map ;
registration_data . map = map ;
registration_data . total_count + = map -> map_count ;
}
/ *
* Exception handling
* /
static XamarinUnhandledExceptionFunc unhandled_exception _func ;
void
xamarin_install _unhandled _exception _hook ( XamarinUnhandledExceptionFunc func )
{
unhandled_exception _func = func ;
}
static MonoObject *
fetch_exception _property ( MonoObject * obj , const char * name , bool is_virtual )
{
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 {
NSLog ( @ "Could not find the property System.Exception.%s" , name ) ;
}
return NULL ;
}
static char *
fetch_exception _property _string ( MonoObject * obj , const char * name , bool is_virtual )
{
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 )
{
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 ) ;
}
void
xamarin_unhandled _exception _handler ( MonoObject * exc , gpointer user_data )
{
int counter = 0 ;
// fetch the field , since the property might have been linked away .
MonoClassField * inner_exception = mono_class _get _field _from _name ( mono_object _get _class ( exc ) , "inner_exception" ) ;
NSMutableString * str = [ [ NSMutableString alloc ] init ] ;
do {
print_exception ( exc , counter > 0 , str ) ;
if ( inner_exception ) {
mono_field _get _value ( exc , inner_exception , & exc ) ;
} else {
LOG ( "Could not find the field inner_exception in System.Exception\n" ) ;
break ;
}
} while ( counter + + < 10 && exc ) ;
NSLog ( @ "%@" , str ) ;
abort ( ) ;
}
static void
exception_handler ( NSException * exc )
{
LOG ( PRODUCT ": Received unhandled ObjectiveC exception: %@ %@" , [ exc name ] , [ exc reason ] ) ;
// This might happen on a thread we haven ' t heard about before
if ( mono_domain _get ( ) = = NULL )
mono_jit _thread _attach ( NULL ) ;
xamarin_throw _ns _exception ( exc ) ;
}
# if defined ( DEBUG )
static void *
pump_gc ( void * context )
{
mono_thread _attach ( mono_get _root _domain ( ) ) ;
while ( xamarin_gc _pump ) {
mono_gc _collect ( mono_gc _max _generation ( ) ) ;
usleep ( 1000000 ) ;
}
return NULL ;
}
# endif / * DEBUG * /
static void
detect_product _assembly ( )
{
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 ;
}
}
static void
log_callback ( const char * log_domain , const char * log_level , const char * message , mono_bool fatal , void * user_data )
{
NSLog ( @ "%s: %s" , log_level , message ) ;
if ( fatal )
abort ( ) ;
}
static void
print_callback ( const char * string , mono_bool is_stdout )
{
NSLog ( @ "%s" , string ) ;
}
void
xamarin_initialize ( )
{
MonoClass * runtime_class ;
MonoAssembly * assembly = NULL ;
MonoImage * image ;
MonoMethod * runtime_initialize ;
struct InitializationOptions options ;
void * params [ 2 ] ;
const char * product_dll = NULL ;
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
mono_trace _set _log _handler ( log_callback , NULL ) ;
mono_trace _set _print _handler ( print_callback ) ;
mono_trace _set _printerr _handler ( print_callback ) ;
detect_product _assembly ( ) ;
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 ) ;
NSSetUncaughtExceptionHandler ( exception_handler ) ;
product_dll = xamarin_use _new _assemblies ? PRODUCT_DUAL _ASSEMBLY : PRODUCT_COMPAT _ASSEMBLY ;
assembly = open_assembly ( product_dll ) ;
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" ) ;
runtime_initialize = mono_class _get _method _from _name ( runtime_class , "Initialize" , 1 ) ;
memset ( & options , 0 , sizeof ( options ) ) ;
options . size = sizeof ( options ) ;
if ( xamarin_use _new _assemblies && xamarin_use _old _dynamic _registrar )
options . flags = ( enum InitializationFlags ) ( options . flags | InitializationFlagsUseOldDynamicRegistrar ) ;
if ( xamarin_use _il _registrar )
options . flags = ( enum InitializationFlags ) ( options . flags | InitializationFlagsILRegistrar ) ;
# if MONOTOUCH && ( defined ( __i386 __ ) || defined ( __x86 _64 __ ) )
options . flags = ( enum InitializationFlags ) ( options . flags | InitializationFlagsIsSimulator ) ;
# endif
options . Trampolines = trampolines ;
options . RegistrationData = & registration_data ;
params [ 0 ] = & options ;
mono_runtime _invoke ( runtime_initialize , NULL , params , NULL ) ;
delegates = options . Delegates ;
register_assembly ( assembly ) ;
install_nsautoreleasepool _hooks ( ) ;
# if defined ( DEBUG )
2016-04-26 19:48:10 +03:00
// Disable this for watchOS for now , since we still have known bugs with the COOP GC causing crashes .
# if ! TARGET_OS _WATCH
2016-04-21 15:19:32 +03:00
if ( xamarin_gc _pump ) {
pthread_t gc_thread ;
pthread_create ( & gc_thread , NULL , pump_gc , NULL ) ;
}
2016-04-26 19:48:10 +03:00
# endif // ! TARGET_OS _WATCH
2016-04-21 15:19:32 +03:00
# endif
gc_enable _new _refcount ( ) ;
}
static char * x_bundle _path = NULL ;
const char *
xamarin_get _bundle _path ( )
{
if ( x_bundle _path ! = NULL )
return x_bundle _path ;
NSBundle * main_bundle = [ NSBundle mainBundle ] ;
NSString * bundle_path ;
char * result ;
# if MONOMAC
if ( xamarin_custom _bundle _name ! = nil )
bundle_path = [ [ main_bundle bundlePath ] stringByAppendingPathComponent : [ @ "Contents/" stringByAppendingString : xamarin_custom _bundle _name ] ] ;
else
bundle_path = [ [ main_bundle bundlePath ] stringByAppendingPathComponent : @ "Contents/MonoBundle" ] ;
# 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 )
{
free ( x_bundle _path ) ;
x_bundle _path = strdup ( path ) ;
}
void
xamarin_free ( void * ptr )
{
if ( ptr )
free ( ptr ) ;
}
char *
xamarin_strdup _printf ( const char * msg , . . . )
{
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 , . . . )
{
va_list args ;
char * formatted = NULL ;
va_start ( args , msg ) ;
vasprintf ( & formatted , msg , args ) ;
if ( formatted ) {
NSLog ( @ PRODUCT ": %s" , formatted ) ;
free ( formatted ) ;
}
va_end ( args ) ;
abort ( ) ;
}
static const char *
objc_skip _type ( const char * type )
{
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 :
case _C _ATOM :
case _C _VECTOR :
case _C _CONST :
assert ( 0 ) ;
case _C _ARY _E :
case _C _UNION _E :
case _C _STRUCT _E :
assert ( 0 ) ;
case _C _ARY _B : {
do {
type + + ;
} while ( isdigit ( * type ) ) ;
type = objc_skip _type ( type ) ;
return + + type ;
}
case _C _UNION _B : {
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 ;
}
}
assert ( 0 ) ;
}
int
xamarin_objc _type _size ( const char * type )
{
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 _BFLD : assert ( 0 ) ;
case _C _BOOL : return sizeof ( BOOL ) ;
case _C _VOID : return 0 ;
case _C _UNDEF : assert ( 0 ) ;
case _C _PTR : return sizeof ( void * ) ;
case _C _CHARPTR : return sizeof ( char * ) ;
case _C _ATOM : assert ( 0 ) ;
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 ;
+ + 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 ;
}
case _C _VECTOR : assert ( 0 ) ;
case _C _CONST : assert ( 0 ) ;
// The following are from table 6 -2 here : https : // developer . apple . com / library / mac / # documentation / Cocoa / Conceptual / ObjCRuntimeGuide / Articles / ocrtTypeEncodings . html
// case ' r ' : _C _CONST
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 )
{
// 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 )
NSLog ( @ "\tGCHandle created for %p: %d (flags: %p) = %d %s\n" , self , gchandle , GINT_TO _POINTER ( flags ) , get_raw _gchandle ( self ) , weak ? "weak" : "strong" ) ;
# endif
}
void
xamarin_switch _gchandle ( id self , bool to_weak )
{
int new_gchandle ;
int old_gchandle ;
int old_gchandle _raw ;
MonoObject * managed_object ;
int flags = MANAGED_REF _BIT ;
old_gchandle _raw = get_raw _gchandle ( self ) ;
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 )
NSLog ( @ "Object %p already has a %s GCHandle = %d\n" , self , to_weak ? "weak" : "strong" , old_gchandle ) ;
# 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 )
NSLog ( @ "Object %p has no managed object to create a %s GCHandle for\n" , self , to_weak ? "weak" : "strong" ) ;
# endif
return ;
}
if ( mono_domain _get ( ) = = NULL )
mono_jit _thread _attach ( NULL ) ;
if ( old_gchandle ) {
managed_object = mono_gchandle _get _target ( old_gchandle ) ;
} else {
managed_object = xamarin_get _managed _object _for _ptr ( self ) ;
}
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 ) ;
xamarin_set _nsobject _flags ( managed_object , xamarin_get _nsobject _flags ( managed_object ) | NSObjectFlagsHasManagedRef ) ;
set_raw _gchandle ( self , new_gchandle | flags ) ;
# if defined ( DEBUG_REF _COUNTING )
NSLog ( @ "Switched object %p to %s GCHandle = %d\n" , self , to_weak ? "weak" : "strong" , new_gchandle ) ;
# endif
}
void
xamarin_free _gchandle ( id self , int gchandle )
{
if ( gchandle ) {
# if defined ( DEBUG_REF _COUNTING )
NSLog ( @ "\tGCHandle %i destroyed for object %p\n" , gchandle , self ) ;
# endif
mono_gchandle _free ( gchandle ) ;
set_raw _gchandle ( self , 0 ) ;
} else {
# if defined ( DEBUG_REF _COUNTING )
NSLog ( @ "\tNo GCHandle for the object %p\n" , self ) ;
# endif
}
}
void
xamarin_clear _gchandle ( id self )
{
set_raw _gchandle ( self , 0 ) ;
}
void
xamarin_set _gchandle ( id self , int gchandle )
{
set_raw _gchandle ( self , gchandle ) ;
}
static inline bool
is_user _type ( id self )
{
return class_getInstanceMethod ( object_getClass ( self ) , @ selector ( xamarinSetGCHandle : ) ) ! = NULL ;
}
# if defined ( DEBUG_REF _COUNTING )
int
get_safe _retainCount ( id self )
{
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
extern "C" void
xamarin_release _managed _ref ( id self , MonoObject * managed_obj )
{
bool user_type = is_user _type ( self ) ;
# if defined ( DEBUG_REF _COUNTING )
NSLog ( @ "monotouch_release_managed_ref (%s Handle=%p) retainCount=%d; HasManagedRef=%i GCHandle=%i IsUserType=%i\n" ,
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 ) ;
[ self release ] ;
} 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 * /
xamarin_unregister _nsobject ( self , managed_obj ) ;
[ self release ] ;
xamarin_framework _peer _unlock ( ) ;
}
}
void
xamarin_create _managed _ref ( id self , gpointer managed_object , bool retain )
{
int gchandle ;
bool user_type = is_user _type ( self ) ;
# if defined ( DEBUG_REF _COUNTING )
NSLog ( @ "monotouch_create_managed_ref (%s Handle=%p) retainCount=%d; HasManagedRef=%i GCHandle=%i IsUserType=%i\n" ,
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
}
}
if ( retain )
[ self retain ] ;
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 *
get_method _block _wrapper _creator ( MonoMethod * method , int par )
{
MonoObject * res = NULL ;
MethodAndPar mp , * nmp ;
mp . method = method ;
mp . par = par ;
// NSLog ( @ "Looking up method and par (%x and %d)" , ( int ) method , par ) ;
pthread_mutex _lock ( & wrapper_hash _lock ) ;
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 ) {
// NSLog ( @ "Found match: %x" , ( int ) res ) ;
return res ;
}
res = xamarin_get _block _wrapper _creator ( ( MonoObject * ) mono_method _get _object ( mono_domain _get ( ) , method , NULL ) , par ) ;
// NSLog ( @ "New value: %x" , ( int ) res ) ;
nmp = ( MethodAndPar * ) malloc ( sizeof ( MethodAndPar ) ) ;
* nmp = mp ;
pthread_mutex _lock ( & wrapper_hash _lock ) ;
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 *
xamarin_get _delegate _for _block _parameter ( MonoMethod * method , int par , void * nativeBlock )
{
MonoObject * delegate ;
if ( nativeBlock = = NULL )
return NULL ;
/ * retain or copy ( if it ' s a stack block ) the block * /
nativeBlock = _Block _copy ( nativeBlock ) ;
delegate = delegates . create_block _proxy ( get_method _block _wrapper _creator ( method , par ) , nativeBlock ) ;
pthread_mutex _lock ( & wrapper_hash _lock ) ;
mono_gc _reference _queue _add ( block_wrapper _queue , delegate , nativeBlock ) ;
pthread_mutex _unlock ( & wrapper_hash _lock ) ;
return ( int * ) delegate ;
}
id
xamarin_get _block _for _delegate ( MonoMethod * method , MonoObject * delegate )
{
return delegates . create_delegate _proxy ( ( MonoObject * ) mono_method _get _object ( mono_domain _get ( ) , method , NULL ) , delegate ) ;
}
void
xamarin_set _use _sgen ( bool value )
{
}
bool
xamarin_get _use _sgen ( )
{
return true ;
}
void
xamarin_set _is _unified ( bool value )
{
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 ;
}
bool
xamarin_get _is _unified ( )
{
return xamarin_use _new _assemblies ;
}
void
xamarin_set _gc _pump _enabled ( bool value )
{
xamarin_gc _pump = value ;
}
const char *
xamarin_skip _encoding _flags ( const char * encoding )
{
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 ;
}
}
}
/ *
* 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 ( )
{
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
{
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
{
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 ;
}