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 < stdio . h >
# include < objc / objc . h >
# include < objc / runtime . h >
# include < objc / message . h >
# include < Foundation / Foundation . h >
# include < pthread . h >
2016-05-12 12:36:36 +03:00
# include "product.h"
2016-04-21 15:19:32 +03:00
# include "delegates.h"
# include "xamarin/xamarin.h"
# include "slinked-list.h"
# include "trampolines-internal.h"
2016-02-15 21:02:14 +03:00
# include "runtime-internal.h"
2016-04-21 15:19:32 +03:00
// # define DEBUG_REF _COUNTING
static pthread_mutex _t refcount_mutex ;
static void
x_init _mutex ( ) __attribute __ ( ( constructor ) ) ;
static void
x_init _mutex ( )
{
pthread_mutexattr _t attr ;
pthread_mutexattr _init ( & attr ) ;
pthread_mutexattr _settype ( & attr , PTHREAD_MUTEX _RECURSIVE ) ;
pthread_mutex _init ( & refcount_mutex , & attr ) ;
}
void *
2016-06-02 13:18:45 +03:00
xamarin_marshal _return _value ( MonoType * mtype , const char * type , MonoObject * retval , bool retain , MonoMethod * method , guint32 * exception_gchandle )
2016-04-21 15:19:32 +03:00
{
2016-02-15 21:02:14 +03:00
// COOP : accesses managed memory : unsafe mode .
MONO_ASSERT _GC _UNSAFE ;
2016-04-21 15:19:32 +03:00
/ * Any changes in this method probably need to be reflected in the static registrar as well * /
switch ( type [ 0 ] ) {
case _C _CLASS :
2016-06-02 13:18:45 +03:00
return xamarin_get _class _handle ( retval , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
case _C _SEL :
2016-06-02 13:18:45 +03:00
return xamarin_get _selector _handle ( retval , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
case _C _PTR : {
MonoClass * klass = mono_class _from _mono _type ( mtype ) ;
if ( mono_class _is _delegate ( klass ) ) {
2016-06-02 13:18:45 +03:00
return xamarin_get _block _for _delegate ( method , retval , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
} else {
return * ( void * * ) mono_object _unbox ( retval ) ;
}
}
case _C _ID : {
MonoClass * r_klass = mono_object _get _class ( ( MonoObject * ) retval ) ;
if ( r_klass = = mono_get _string _class ( ) ) {
char * str = mono_string _to _utf8 ( ( MonoString * ) retval ) ;
NSString * rv = [ [ NSString alloc ] initWithUTF8String : str ] ;
if ( ! retain )
[ rv autorelease ] ;
mono_free ( str ) ;
return ( void * ) rv ;
} else if ( xamarin_is _class _array ( r_klass ) ) {
MonoClass * e_klass = mono_class _get _element _class ( r_klass ) ;
bool is_string = e_klass = = mono_get _string _class ( ) ;
MonoArray * m_arr = ( MonoArray * ) retval ;
int length = mono_array _length ( m_arr ) ;
id * buf = ( id * ) malloc ( sizeof ( void * ) * length ) ;
NSArray * arr ;
int i ;
id v ;
for ( i = 0 ; i < length ; i + + ) {
MonoObject * value = mono_array _get ( m_arr , MonoObject * , i ) ;
if ( is_string ) {
char * str = mono_string _to _utf8 ( ( MonoString * ) value ) ;
NSString * sv = [ [ NSString alloc ] initWithUTF8String : str ] ;
[ sv autorelease ] ;
mono_free ( str ) ;
v = sv ;
} else {
2016-06-02 13:18:45 +03:00
v = xamarin_get _handle ( value , exception_gchandle ) ;
if ( * exception_gchandle ! = 0 )
return NULL ;
2016-04-21 15:19:32 +03:00
}
buf [ i ] = v ;
}
arr = [ [ NSArray alloc ] initWithObjects : buf count : length ] ;
free ( buf ) ;
if ( ! retain )
[ arr autorelease ] ;
return ( void * ) arr ;
} else if ( xamarin_is _class _nsobject ( r_klass ) ) {
2016-06-02 13:18:45 +03:00
id i = xamarin_get _handle ( retval , exception_gchandle ) ;
if ( * exception_gchandle ! = 0 )
return NULL ;
2016-04-21 15:19:32 +03:00
xamarin_framework _peer _lock ( ) ;
[ i retain ] ;
xamarin_framework _peer _unlock ( ) ;
if ( ! retain )
[ i autorelease ] ;
mt_dummy _use ( retval ) ;
return i ;
} else if ( xamarin_is _class _inativeobject ( r_klass ) ) {
2016-06-02 13:18:45 +03:00
return xamarin_get _handle _for _inativeobject ( retval , exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
} else {
xamarin_assertion _message ( "Don't know how to marshal a return value of type '%s.%s'. Please file a bug with a test case at http://bugzilla.xamarin.com\n" , mono_class _get _namespace ( r_klass ) , mono_class _get _name ( r_klass ) ) ;
}
}
case _C _CHARPTR :
return ( void * ) mono_string _to _utf8 ( ( MonoString * ) retval ) ;
case _C _VOID :
return ( void * ) 0 x0 ;
default :
return * ( void * * ) mono_object _unbox ( retval ) ;
}
}
2016-05-12 12:36:36 +03:00
static const char *
get_method _description ( Class cls , SEL sel )
{
Protocol * * protocols ;
unsigned int p_count ;
Class p_cls = cls ;
struct objc_method _description desc ;
while ( p_cls ) {
protocols = class_copyProtocolList ( p_cls , & p_count ) ;
for ( unsigned int i = 0 ; i < p_count ; i + + ) {
desc = protocol_getMethodDescription ( protocols [ i ] , sel , YES , ! class_isMetaClass ( p_cls ) ) ;
if ( desc . types ! = NULL ) {
free ( protocols ) ;
return desc . types ;
}
}
free ( protocols ) ;
p_cls = class_getSuperclass ( p_cls ) ;
}
Method method = class_getInstanceMethod ( cls , sel ) ;
if ( ! method )
return NULL ;
struct objc_method _description * m_desc ;
m_desc = method_getDescription ( method ) ;
return m_desc ? m_desc -> types : NULL ;
}
static int
count_until ( const char * desc , char start , char end )
{
// Counts the number of characters until a certain character is found : ' end '
// If the ' start ' character is found , nesting is assumed , and an additional
// ' end ' character must be found before the function returns .
int i = 1 ;
int sub = 0 ;
while ( * desc ) {
if ( start = = * desc ) {
sub + + ;
} else if ( end = = * desc ) {
sub - - ;
if ( sub = = 0 )
return i ;
}
i + + ;
// This is not multi - byte safe . . .
desc + + ;
}
fprintf ( stderr , PRODUCT ": Unexpected type encoding, did not find end character '%c' in '%s'." , end , desc ) ;
return i ;
}
static int
get_type _description _length ( const char * desc )
{
int length = 0 ;
// This function returns the length of the first encoded type string in desc .
switch ( desc [ 0 ] ) {
case _C _ID :
if ( desc [ 1 ] = = ' ? ' ) {
// Example : [ AVAssetImageGenerator generateCGImagesAsynchronouslyForTimes : completionHandler : ] = ' v16 @ 0 : 4 @ 8 @ ? 12 '
length = 2 ;
} else {
length = 1 ;
}
break ;
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 _BOOL :
case _C _VOID :
case _C _CHARPTR :
length = 1 ;
break ;
case _C _PTR :
length = 1 ;
// handle broken encoding where simd types don ' t show up at all
// Example : [ GKPath pathWithPoints : count : radius : cyclical : ] = ' @ 24 @ 0 : 4 ^ 8 L12f16c20 '
// Here we assume that we ' re pointing to a simd type if we find
// a number ( i . e . only get the size of what we ' re pointing to
// if the next character isn ' t a number ) .
if ( desc [ 1 ] < ' 0 ' || desc [ 1 ] > ' 9 ' )
length + = get_type _description _length ( desc + 1 ) ;
break ;
case _C _ARY _B :
length = count_until ( desc , _C _ARY _B , _C _ARY _E ) ;
break ;
case _C _UNION _B :
length = count_until ( desc , _C _UNION _B , _C _UNION _E ) ;
break ;
case _C _STRUCT _B :
length = count_until ( desc , _C _STRUCT _B , _C _STRUCT _E ) ;
break ;
// 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 ' :
length = 1 + get_type _description _length ( desc + 1 ) ;
break ;
case _C _BFLD :
length = 1 ;
break ;
case _C _UNDEF :
case _C _ATOM :
case _C _VECTOR :
xamarin_assertion _message ( "Unhandled type encoding: %s" , desc ) ;
break ;
default :
xamarin_assertion _message ( "Unsupported type encoding: %s" , desc ) ;
break ;
}
// Every type encoding _may _ be followed by the stack frame offset for that type
while ( desc [ length ] >= ' 0 ' && desc [ length ] <= ' 9 ' )
length + + ;
return length ;
}
2016-04-21 15:19:32 +03:00
int
xamarin_get _frame _length ( id self , SEL sel )
{
2016-05-12 12:36:36 +03:00
if ( self = = NULL )
return sizeof ( void * ) * 3 ; // we might be in objc_msgStret , in which case we ' ll need to copy three arguments .
// [ NSDecimalNumber initWithDecimal : ] has this descriptor : "@36@0:8{?=b8b4b1b1b18[8S]}16"
// which NSMethodSignature chokes on : NSInvalidArgumentException Reason : + [ NSMethodSignature signatureWithObjCTypes : ] : unsupported type encoding spec ' { ? } '
// So instead parse the description ourselves .
int length = 0 ;
Class cls = object_getClass ( self ) ;
const char * method_description = get_method _description ( cls , sel ) ;
const char * desc = method_description ;
if ( desc = = NULL ) {
// This happens with [ [ UITableViewCell appearance ] backgroundColor ]
@ try {
NSMethodSignature * sig = [ self methodSignatureForSelector : sel ] ;
length = [ sig frameLength ] ;
} @ catch ( NSException * ex ) {
length = sizeof ( void * ) * 64 ; // some high - ish number .
fprintf ( stderr , PRODUCT ": Failed to calculate the frame size for the method [%s %s] (%s). Using a value of %i instead.\n" , class_getName ( cls ) , sel_getName ( sel ) , [ [ ex description ] UTF8String ] , length ) ;
}
} else {
// The format of the method type encoding is described here : http : // stackoverflow . com / a / 11492151 / 183422
// the return type might have a number after it , which is the size of the argument frame
// first get this number ( if it ' s there ) , and use it as a minimum value for the frame length
int rvlength = get_type _description _length ( desc ) ;
int min_length = 0 ;
if ( rvlength > 0 ) {
const char * min_start = desc + rvlength ;
// the number is at the end of the return type encoding , so find any numbers
// at the end of the type encoding .
while ( min_start > desc && min_start [ -1 ] >= ' 0 ' && min_start [ -1 ] <= ' 9 ' )
min_start - - ;
if ( min_start < desc + rvlength ) {
for ( int i = 0 ; i < desc + rvlength - min_start ; i + + )
min_length = min_length * 10 + ( min_start [ i ] - ' 0 ' ) ;
}
}
// fprintf ( stderr , "Found desc '%s' for [%s %s] with min frame length %i\n" , desc , class_getName ( cls ) , sel_getName ( sel ) , min_length ) ;
// skip the return value .
desc + = rvlength ;
while ( * desc ) {
int tl = xamarin_objc _type _size ( desc ) ;
// round up to pointer size
if ( tl % sizeof ( void * ) ! = 0 )
tl + = sizeof ( void * ) - ( tl % sizeof ( void * ) ) ;
length + = tl ;
// fprintf ( stderr , " argument=%s length=%i totallength=%i\n" , desc , tl , length ) ;
desc + = get_type _description _length ( desc ) ;
}
if ( min_length > length ) {
// this might happen for methods that take simd types , since those arguments don ' t show up in the
// method signature encoding at all , but they ' re still added to the frame size .
// fprintf ( stderr , " min length: %i is higher than calculated length: %i for [%s %s] with description %s\n" , min_length , length , class_getName ( cls ) , sel_getName ( sel ) , method_description ) ;
length = min_length ;
}
}
// we can ' t detect varargs , so just add 16 more pointer sized arguments to be on the safe - ish side .
length + = sizeof ( void * ) * 16 ;
2016-04-21 15:19:32 +03:00
2016-05-12 12:36:36 +03:00
return length ;
2016-04-21 15:19:32 +03:00
}
static inline void
find_objc _method _implementation ( struct objc_super * sup , id self , SEL sel , IMP xamarin_impl )
{
2016-02-15 21:02:14 +03:00
// COOP : does not access managed memory : any mode
2016-04-21 15:19:32 +03:00
Class klass = object_getClass ( self ) ;
Class sklass = class_getSuperclass ( klass ) ;
IMP imp = class_getMethodImplementation ( klass , sel ) ;
IMP simp = class_getMethodImplementation ( sklass , sel ) ;
while ( imp = = simp || simp = = xamarin_impl ) {
sklass = class_getSuperclass ( sklass ) ;
simp = class_getMethodImplementation ( sklass , sel ) ;
}
sup -> receiver = self ;
# if ! defined ( __cplusplus ) && ! __OBJC2 __
sup -> class = sklass ;
# else
sup -> super_class = sklass ;
# endif
}
id
xamarin_invoke _objc _method _implementation ( id self , SEL sel , IMP xamarin_impl )
{
2016-02-15 21:02:14 +03:00
// COOP : does not access managed memory : any mode
2016-04-21 15:19:32 +03:00
struct objc_super sup ;
find_objc _method _implementation ( & sup , self , sel , xamarin_impl ) ;
return objc_msgSendSuper ( & sup , sel ) ;
}
# if MONOMAC
id
xamarin_copyWithZone _trampoline1 ( id self , SEL sel , NSZone * zone )
{
2016-02-15 21:02:14 +03:00
// COOP : does not access managed memory : any mode
2016-04-21 15:19:32 +03:00
// This is for subclasses that themselves do not implement Copy ( NSZone )
id rv ;
int gchandle ;
struct objc_super sup ;
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
PRINT ( "xamarin_copyWithZone_trampoline1 (%p, %s, %p)\n" , self , sel_getName ( sel ) , zone ) ;
2016-04-21 15:19:32 +03:00
# endif
// Clear out our own GCHandle
gchandle = xamarin_get _gchandle _with _flags ( self ) ;
if ( gchandle ! = 0 )
xamarin_set _gchandle ( self , 0 ) ;
// Call the base class implementation
id (*invoke) (struct objc_super *, SEL, NSZone*) = ( id (*)(struct objc_super *, SEL, NSZone*) ) objc_msgSendSuper ;
find_objc _method _implementation ( & sup , self , sel , ( IMP ) xamarin_copyWithZone _trampoline1 ) ;
rv = invoke ( & sup , sel , zone ) ;
// Restore our GCHandle
if ( gchandle ! = 0 )
xamarin_set _gchandle ( self , gchandle ) ;
return rv ;
}
id
xamarin_copyWithZone _trampoline2 ( id self , SEL sel , NSZone * zone )
{
2016-02-15 21:02:14 +03:00
// COOP : does not access managed memory : any mode
2016-04-21 15:19:32 +03:00
// This is for subclasses that already implement Copy ( NSZone )
id rv ;
int gchandle ;
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
PRINT ( "xamarin_copyWithZone_trampoline2 (%p, %s, %p)\n" , self , sel_getName ( sel ) , zone ) ;
2016-04-21 15:19:32 +03:00
# endif
// Clear out our own GCHandle
gchandle = xamarin_get _gchandle _with _flags ( self ) ;
if ( gchandle ! = 0 )
xamarin_set _gchandle ( self , 0 ) ;
// Call the managed implementation
id (*invoke) (id, SEL, NSZone*) = ( id (*)(id, SEL, NSZone*) ) xamarin_trampoline ;
rv = invoke ( self , sel , zone ) ;
// Restore our GCHandle
if ( gchandle ! = 0 )
xamarin_set _gchandle ( self , gchandle ) ;
return rv ;
}
# endif
void
xamarin_release _trampoline ( id self , SEL sel )
{
2016-02-15 21:02:14 +03:00
// COOP : does not access managed memory : any mode , but it assumes safe mode upon entry ( it takes locks , and doesn ' t switch to safe mode ) .
MONO_ASSERT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
int ref_count ;
bool detach = false ;
pthread_mutex _lock ( & refcount_mutex ) ;
ref_count = [ self retainCount ] ;
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
PRINT ( "xamarin_release_trampoline (%s Handle=%p) retainCount=%d; HasManagedRef=%i GCHandle=%i\n" ,
2016-04-21 15:19:32 +03:00
class_getName ( [ self class ] ) , self , ref_count , xamarin_has _managed _ref ( self ) , xamarin_get _gchandle ( self ) ) ;
# endif
/ *
* We need to decide if the gchandle should become a weak one .
* This happens if managed code will end up holding the only ref .
* /
2016-02-15 21:02:14 +03:00
if ( ref_count = = 2 && xamarin_has _managed _ref _safe ( self ) ) {
2016-04-21 15:19:32 +03:00
xamarin_switch _gchandle ( self , true / * weak * / ) ;
detach = true ;
}
pthread_mutex _unlock ( & refcount_mutex ) ;
/ * Invoke the real retain method * /
xamarin_invoke _objc _method _implementation ( self , sel , ( IMP ) xamarin_release _trampoline ) ;
if ( detach )
mono_thread _detach _if _exiting ( ) ;
}
void
xamarin_notify _dealloc ( id self , int gchandle )
{
2016-06-02 13:18:45 +03:00
guint32 exception_gchandle = 0 ;
2016-02-15 21:02:14 +03:00
// COOP : safe mode upon entry , switches to unsafe when acccessing managed memory .
2016-05-16 20:30:58 +03:00
MONO_ASSERT _GC _SAFE _OR _DETACHED ;
2016-02-15 21:02:14 +03:00
/ * This is needed because we call into managed code below ( xamarin_unregister _nsobject ) * /
MONO_THREAD _ATTACH ; // COOP : This will swith to GC_UNSAFE
2016-04-21 15:19:32 +03:00
/ * Object is about to die . Unregister it and free any gchandles we may have * /
MonoObject * mobj = mono_gchandle _get _target ( gchandle ) ;
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
PRINT ( "xamarin_notify_dealloc (%p, %i) target: %p\n" , self , gchandle , mobj ) ;
2016-04-21 15:19:32 +03:00
# endif
xamarin_free _gchandle ( self , gchandle ) ;
2016-06-02 13:18:45 +03:00
xamarin_unregister _nsobject ( self , mobj , & exception_gchandle ) ;
2016-02-15 21:02:14 +03:00
MONO_THREAD _DETACH ; // COOP : This will switch to GC_SAFE
2016-06-02 13:18:45 +03:00
xamarin_process _managed _exception _gchandle ( exception_gchandle ) ;
2016-04-21 15:19:32 +03:00
mono_thread _detach _if _exiting ( ) ;
}
id
xamarin_retain _trampoline ( id self , SEL sel )
{
2016-02-15 21:02:14 +03:00
// COOP : safe mode upon entry , switches to unsafe when acccessing managed memory .
MONO_ASSERT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
pthread_mutex _lock ( & refcount_mutex ) ;
# if defined ( DEBUG_REF _COUNTING )
int ref_count = [ self retainCount ] ;
bool had_managed _ref = xamarin_has _managed _ref ( self ) ;
int pre_gchandle = xamarin_get _gchandle ( self ) ;
# endif
/ *
* We need to make sure we have a strong GCHandle .
* We can not rely the retainCount changing from 1 to 2 , since
* we can not monitor all retains ( see bug #26532 ) .
* So just always make sure we have a strong GCHandle after a retain .
* /
xamarin_switch _gchandle ( self , false / * strong * / ) ;
pthread_mutex _unlock ( & refcount_mutex ) ;
/ * Invoke the real retain method * /
self = xamarin_invoke _objc _method _implementation ( self , sel , ( IMP ) xamarin_retain _trampoline ) ;
# if defined ( DEBUG_REF _COUNTING )
2016-09-06 23:55:23 +03:00
PRINT ( "xamarin_retain_trampoline (%s Handle=%p) initial retainCount=%d; new retainCount=%d HadManagedRef=%i HasManagedRef=%i old GCHandle=%i new GCHandle=%i\n" ,
2016-04-21 15:19:32 +03:00
class_getName ( [ self class ] ) , self , ref_count , ( int ) [ self retainCount ] , had_managed _ref , xamarin_has _managed _ref ( self ) , pre_gchandle , xamarin_get _gchandle ( self ) ) ;
# endif
return self ;
}
// We try to use the associated object API as little as possible , because the API does
// not like recursion ( see bug #35017 ) , and it calls retain / release , which we might
// have overridden with our own code that calls these functions . So in addition to
// keeping the gchandle inside the associated object , we also keep it in a hash
// table , so that xamarin_get _gchandle _trampoline does not have to call any
// associated object API to get the gchandle .
static CFMutableDictionaryRef gchandle_hash = NULL ;
static pthread_mutex _t gchandle_hash _lock = PTHREAD_MUTEX _INITIALIZER ;
static const char * associated_key = "x" ; // the string value doesn ' t matter , only the pointer value .
void
xamarin_set _gchandle _trampoline ( id self , SEL sel , int gc_handle )
{
2016-02-15 21:02:14 +03:00
// COOP : Called by ObjC ( when the setGCHandle : selector is called on an object ) .
// COOP : Safe mode upon entry , and doesn ' t access managed memory , so no need to change .
MONO_ASSERT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
/ * This is for types registered using the dynamic registrar * /
XamarinAssociatedObject * obj ;
obj = objc_getAssociatedObject ( self , associated_key ) ;
if ( obj = = NULL && gc_handle ! = 0 ) {
obj = [ [ XamarinAssociatedObject alloc ] init ] ;
obj -> gc_handle = gc_handle ;
obj -> native_object = self ;
objc_setAssociatedObject ( self , associated_key , obj , OBJC_ASSOCIATION _RETAIN _NONATOMIC ) ;
[ obj release ] ;
}
if ( obj ! = NULL )
obj -> gc_handle = gc_handle ;
pthread_mutex _lock ( & gchandle_hash _lock ) ;
if ( gchandle_hash = = NULL )
gchandle_hash = CFDictionaryCreateMutable ( kCFAllocatorDefault , 0 , NULL , NULL ) ;
if ( gc_handle = = 0 ) {
CFDictionaryRemoveValue ( gchandle_hash , self ) ;
} else {
CFDictionarySetValue ( gchandle_hash , self , GINT_TO _POINTER ( gc_handle ) ) ;
}
pthread_mutex _unlock ( & gchandle_hash _lock ) ;
}
int
xamarin_get _gchandle _trampoline ( id self , SEL sel )
{
2016-02-15 21:02:14 +03:00
// COOP : Called by ObjC ( when the getGCHandle selector is called on an object ) .
// COOP : Safe mode upon entry , and doesn ' t access managed memory , so no need to switch .
MONO_ASSERT _GC _SAFE ;
2016-04-21 15:19:32 +03:00
/ * This is for types registered using the dynamic registrar * /
int gc_handle = 0 ;
pthread_mutex _lock ( & gchandle_hash _lock ) ;
if ( gchandle_hash ! = NULL )
gc_handle = GPOINTER_TO _INT ( CFDictionaryGetValue ( gchandle_hash , self ) ) ;
pthread_mutex _unlock ( & gchandle_hash _lock ) ;
return gc_handle ;
}