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 ;
2016-05-13 16:49:08 +03:00
# if DEBUG && ( defined ( __i386 __ ) || defined ( __x86 _64 __ ) )
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 ;
}