2021-03-18 09:23:39 +03:00
/ * - * - Mode : C ; tab - width : 8 ; indent - tabs - mode : t ; c - basic - offset : 8 - * - * /
/ *
* Authors : Rolf Bjarne Kvinge
*
* Copyright ( C ) 2021 Microsoft Corp .
*
* /
/ * Support code for using MonoVM * /
# if ! defined ( CORECLR_RUNTIME )
# include < TargetConditionals . h >
2021-04-21 17:21:09 +03:00
# include < pthread . h >
2021-06-16 16:22:02 +03:00
# include < sys / mman . h >
# include < sys / stat . h >
2021-03-18 09:23:39 +03:00
# if ! DOTNET && TARGET_OS _OSX
# define LEGACY_XAMARIN _MAC 1
# else
# define LEGACY_XAMARIN _MAC 0
# endif
2021-03-25 09:25:20 +03:00
# include "product.h"
2021-03-18 09:23:39 +03:00
# include "monotouch-debug.h"
# include "runtime-internal.h"
# include "xamarin/xamarin.h"
# include "xamarin/monovm-bridge.h"
2021-03-25 09:25:20 +03:00
static MonoAssembly * entry_assembly = NULL ;
static MonoClass * inativeobject_class = NULL ;
static MonoClass * nsobject_class = NULL ;
static MonoClass * nsvalue_class = NULL ;
static MonoClass * nsnumber_class = NULL ;
static MonoClass * nsstring_class = NULL ;
static MonoClass * runtime_class = NULL ;
2021-11-17 12:30:52 +03:00
# if DOTNET
static MonoClass * nativehandle_class = NULL ;
# endif
2021-03-25 09:25:20 +03:00
2021-03-18 09:23:39 +03:00
# if ! LEGACY_XAMARIN _MAC
void
xamarin_bridge _setup ( )
{
const char * c_bundle _path = xamarin_get _bundle _path ( ) ;
setenv ( "MONO_PATH" , c_bundle _path , 1 ) ;
setenv ( "MONO_XMLSERIALIZER_THS" , "no" , 1 ) ;
setenv ( "MONO_REFLECTION_SERIALIZER" , "yes" , 1 ) ;
# if TARGET_OS _WATCH || TARGET_OS _TV
mini_parse _debug _option ( "explicit-null-checks" ) ;
# endif
// see http : // bugzilla . xamarin . com / show_bug . cgi ? id = 820
// take this line out once the bug is fixed
mini_parse _debug _option ( "no-gdb-backtrace" ) ;
}
void
xamarin_bridge _initialize ( )
{
2021-06-28 18:12:37 +03:00
if ( xamarin_register _modules ! = NULL )
xamarin_register _modules ( ) ;
2021-03-18 09:23:39 +03:00
DEBUG_LAUNCH _TIME _PRINT ( "\tAOT register time" ) ;
# ifdef DEBUG
monotouch_start _debugging ( ) ;
DEBUG_LAUNCH _TIME _PRINT ( "\tDebug init time" ) ;
# endif
2022-09-01 21:49:18 +03:00
if ( xamarin_init _mono _debug )
2021-03-18 09:23:39 +03:00
mono_debug _init ( MONO_DEBUG _FORMAT _MONO ) ;
2022-09-01 21:49:18 +03:00
2021-03-18 09:23:39 +03:00
mono_install _assembly _preload _hook ( xamarin_assembly _preload _hook , NULL ) ;
mono_install _load _aot _data _hook ( xamarin_load _aot _data , xamarin_free _aot _data , NULL ) ;
# ifdef DEBUG
monotouch_start _profiling ( ) ;
DEBUG_LAUNCH _TIME _PRINT ( "\tProfiler config time" ) ;
# endif
mono_set _signal _chaining ( TRUE ) ;
mono_set _crash _chaining ( TRUE ) ;
mono_install _unhandled _exception _hook ( xamarin_unhandled _exception _handler , NULL ) ;
mono_install _ftnptr _eh _callback ( xamarin_ftnptr _exception _handler ) ;
2022-09-01 21:49:18 +03:00
mono_jit _init _version ( "MonoTouch" , "mobile" ) ;
2021-03-18 09:23:39 +03:00
/ *
As part of mono initialization a preload hook is added that overrides ours , so we need to re - instate it here .
This is wasteful , but there ' s no way to manipulate the preload hook list except by adding to it .
* /
mono_install _assembly _preload _hook ( xamarin_assembly _preload _hook , NULL ) ;
DEBUG_LAUNCH _TIME _PRINT ( "\tJIT init time" ) ;
}
2021-05-25 09:19:27 +03:00
void
xamarin_bridge _shutdown ( )
{
}
2021-03-18 09:23:39 +03:00
# endif // ! LEGACY_XAMARIN _MAC
2021-03-25 09:25:20 +03:00
static MonoClass *
get_class _from _name ( MonoImage * image , const char * nmspace , const char * name , bool optional = false )
{
// COOP : this is a convenience function executed only at startup , I believe the mode here doesn ' t matter .
MonoClass * rv = mono_class _from _name ( image , nmspace , name ) ;
if ( ! rv && ! optional )
xamarin_assertion _message ( "Fatal error: failed to load the class '%s.%s'\n." , nmspace , name ) ;
return rv ;
}
void
xamarin_bridge _call _runtime _initialize ( struct InitializationOptions * options , GCHandle * exception_gchandle )
{
MonoMethod * runtime_initialize ;
void * params [ 2 ] ;
MonoObject * exc = NULL ;
MonoImage * platform_image = NULL ;
entry_assembly = xamarin_open _assembly ( PRODUCT_DUAL _ASSEMBLY ) ;
if ( ! entry_assembly )
xamarin_assertion _message ( "Failed to load %s." , PRODUCT_DUAL _ASSEMBLY ) ;
platform_image = mono_assembly _get _image ( entry_assembly ) ;
const char * objcruntime = "ObjCRuntime" ;
const char * foundation = "Foundation" ;
runtime_class = get_class _from _name ( platform_image , objcruntime , "Runtime" ) ;
inativeobject_class = get_class _from _name ( platform_image , objcruntime , "INativeObject" ) ;
2021-11-17 12:30:52 +03:00
# if DOTNET
nativehandle_class = get_class _from _name ( platform_image , objcruntime , "NativeHandle" ) ;
# endif
2021-03-25 09:25:20 +03:00
nsobject_class = get_class _from _name ( platform_image , foundation , "NSObject" ) ;
nsnumber_class = get_class _from _name ( platform_image , foundation , "NSNumber" , true ) ;
nsvalue_class = get_class _from _name ( platform_image , foundation , "NSValue" , true ) ;
nsstring_class = get_class _from _name ( platform_image , foundation , "NSString" , true ) ;
runtime_initialize = mono_class _get _method _from _name ( runtime_class , "Initialize" , 1 ) ;
if ( runtime_initialize = = NULL )
xamarin_assertion _message ( "Fatal error: failed to load the %s.%s method" , "Runtime" , "Initialize" ) ;
params [ 0 ] = options ;
mono_runtime _invoke ( runtime_initialize , NULL , params , & exc ) ;
if ( exc )
* exception_gchandle = xamarin_gchandle _new ( exc , false ) ;
}
void
xamarin_bridge _register _product _assembly ( GCHandle * exception_gchandle )
{
xamarin_register _monoassembly ( entry_assembly , exception_gchandle ) ;
2021-03-22 14:53:52 +03:00
// We don ' t need the entry_assembly around anymore , so release it .
xamarin_mono _object _release ( & entry_assembly ) ;
2021-03-25 09:25:20 +03:00
}
2021-04-30 08:49:25 +03:00
MonoMethod *
xamarin_bridge _get _mono _method ( MonoReflectionMethod * method )
{
// COOP : Reads managed memory , needs to be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
PublicMonoReflectionMethod * rm = ( PublicMonoReflectionMethod * ) method ;
return rm -> method ;
}
2021-03-25 09:25:20 +03:00
MonoClass *
xamarin_get _inativeobject _class ( )
{
if ( inativeobject_class = = NULL )
xamarin_assertion _message ( "Internal consistency error, please file a bug (https://github.com/xamarin/xamarin-macios/issues/new). Additional data: can't get the %s class because it's been linked away.\n" , "INativeObject" ) ;
return inativeobject_class ;
}
2021-11-17 12:30:52 +03:00
# if DOTNET
MonoClass *
xamarin_get _nativehandle _class ( )
{
if ( nativehandle_class = = NULL )
xamarin_assertion _message ( "Internal consistency error, please file a bug (https://github.com/xamarin/xamarin-macios/issues/new). Additional data: can't get the %s class because it's been linked away.\n" , "NativeHandle" ) ;
return nativehandle_class ;
}
# endif
2021-03-25 09:25:20 +03:00
MonoClass *
xamarin_get _nsobject _class ( )
{
if ( nsobject_class = = NULL )
xamarin_assertion _message ( "Internal consistency error, please file a bug (https://github.com/xamarin/xamarin-macios/issues/new). Additional data: can't get the %s class because it's been linked away.\n" , "NSObject" ) ;
return nsobject_class ;
}
2021-05-14 08:29:48 +03:00
MonoType *
xamarin_get _nsvalue _type ( )
2021-03-25 09:25:20 +03:00
{
if ( nsvalue_class = = NULL )
xamarin_assertion _message ( "Internal consistency error, please file a bug (https://github.com/xamarin/xamarin-macios/issues/new). Additional data: can't get the %s class because it's been linked away.\n" , "NSValue" ) ;
2021-05-14 08:29:48 +03:00
return mono_class _get _type ( nsvalue_class ) ;
2021-03-25 09:25:20 +03:00
}
2021-05-14 08:29:48 +03:00
MonoType *
xamarin_get _nsnumber _type ( )
2021-03-25 09:25:20 +03:00
{
if ( nsnumber_class = = NULL )
xamarin_assertion _message ( "Internal consistency error, please file a bug (https://github.com/xamarin/xamarin-macios/issues/new). Additional data: can't get the %s class because it's been linked away.\n" , "NSNumber" ) ;
2021-05-14 08:29:48 +03:00
return mono_class _get _type ( nsnumber_class ) ;
2021-03-25 09:25:20 +03:00
}
MonoClass *
xamarin_get _nsstring _class ( )
{
if ( nsstring_class = = NULL )
xamarin_assertion _message ( "Internal consistency error, please file a bug (https://github.com/xamarin/xamarin-macios/issues/new). Additional data: can't get the %s class because it's been linked away.\n" , "NSString" ) ;
return nsstring_class ;
}
MonoClass *
xamarin_get _runtime _class ( )
{
if ( runtime_class = = NULL )
xamarin_assertion _message ( "Internal consistency error, please file a bug (https://github.com/xamarin/xamarin-macios/issues/new). Additional data: can't get the %s class because it's been linked away.\n" , "Runtime" ) ;
return runtime_class ;
}
2021-04-21 17:21:09 +03:00
/ * Wrapping threads with NSAutoreleasePool
*
* We must create an NSAutoreleasePool for each thread , so users
* don ' t have to do it manually .
*
* Use mono ' s profiling API to get notified for thread start / stop ,
* and create a pool that spans the thread ' s entire lifetime .
* /
2022-11-15 19:44:41 +03:00
# if ! DOTNET
2021-04-21 17:21:09 +03:00
static CFMutableDictionaryRef xamarin_thread _hash = NULL ;
static pthread_mutex _t thread_hash _lock = PTHREAD_MUTEX _INITIALIZER ;
static void
xamarin_thread _start ( void * user_data )
{
// COOP : no managed memory access : any mode . Switching to safe mode since we ' re locking a mutex .
NSAutoreleasePool * pool ;
if ( mono_thread _is _foreign ( mono_thread _current ( ) ) )
return ;
MONO_ENTER _GC _SAFE ;
pool = [ [ NSAutoreleasePool alloc ] init ] ;
pthread_mutex _lock ( & thread_hash _lock ) ;
CFDictionarySetValue ( xamarin_thread _hash , GINT_TO _POINTER ( pthread_self ( ) ) , pool ) ;
pthread_mutex _unlock ( & thread_hash _lock ) ;
MONO_EXIT _GC _SAFE ;
}
static void
xamarin_thread _finish ( void * user_data )
{
// COOP : no managed memory access : any mode . Switching to safe mode since we ' re locking a mutex .
NSAutoreleasePool * pool ;
MONO_ENTER _GC _SAFE ;
/ * Don ' t drain the pool while holding the thread hash lock . * /
pthread_mutex _lock ( & thread_hash _lock ) ;
pool = ( NSAutoreleasePool * ) CFDictionaryGetValue ( xamarin_thread _hash , GINT_TO _POINTER ( pthread_self ( ) ) ) ;
if ( pool )
CFDictionaryRemoveValue ( xamarin_thread _hash , GINT_TO _POINTER ( pthread_self ( ) ) ) ;
pthread_mutex _unlock ( & thread_hash _lock ) ;
if ( pool )
[ pool drain ] ;
MONO_EXIT _GC _SAFE ;
}
static void
thread_start ( MonoProfiler * prof , uintptr_t tid )
{
// COOP : no managed memory access : any mode .
xamarin_thread _start ( NULL ) ;
}
static void
thread_end ( MonoProfiler * prof , uintptr_t tid )
{
// COOP : no managed memory access : any mode .
xamarin_thread _finish ( NULL ) ;
}
2022-11-15 19:44:41 +03:00
# endif // ! DOTNET
2021-04-21 17:21:09 +03:00
void
xamarin_install _nsautoreleasepool _hooks ( )
{
2022-11-15 19:44:41 +03:00
// No need to do anything here for CoreCLR .
# if ! DOTNET
2021-04-21 17:21:09 +03:00
// COOP : executed at startup ( and no managed memory access ) : any mode .
xamarin_thread _hash = CFDictionaryCreateMutable ( kCFAllocatorDefault , 0 , NULL , NULL ) ;
mono_profiler _install _thread ( thread_start , thread_end ) ;
2022-11-15 19:44:41 +03:00
# endif // ! DOTNET
2021-04-21 17:21:09 +03:00
}
2021-05-06 17:19:59 +03:00
void
xamarin_bridge _free _mono _signature ( MonoMethodSignature * * psig )
{
// nothing to free here
* psig = NULL ;
}
2021-05-11 00:12:52 +03:00
bool
xamarin_is _class _nsobject ( MonoClass * cls )
{
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
return mono_class _is _subclass _of ( cls , xamarin_get _nsobject _class ( ) , false ) ;
}
bool
xamarin_is _class _inativeobject ( MonoClass * cls )
{
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
return mono_class _is _subclass _of ( cls , xamarin_get _inativeobject _class ( ) , true ) ;
}
2021-11-17 12:30:52 +03:00
# if DOTNET
bool
xamarin_is _class _nativehandle ( MonoClass * cls )
{
return cls = = xamarin_get _nativehandle _class ( ) ;
}
# endif
2021-05-11 00:12:52 +03:00
bool
xamarin_is _class _array ( MonoClass * cls )
{
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
return mono_class _is _subclass _of ( cls , mono_get _array _class ( ) , false ) ;
}
bool
xamarin_is _class _nsnumber ( MonoClass * cls )
{
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
if ( nsnumber_class = = NULL )
return false ;
return mono_class _is _subclass _of ( cls , nsnumber_class , false ) ;
}
bool
xamarin_is _class _nsvalue ( MonoClass * cls )
{
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
if ( nsvalue_class = = NULL )
return false ;
return mono_class _is _subclass _of ( cls , nsvalue_class , false ) ;
}
bool
xamarin_is _class _nsstring ( MonoClass * cls )
{
// COOP : Reading managed data , must be in UNSAFE mode
MONO_ASSERT _GC _UNSAFE ;
MonoClass * nsstring_class = xamarin_get _nsstring _class ( ) ;
if ( nsstring_class = = NULL )
return false ;
return mono_class _is _subclass _of ( cls , nsstring_class , false ) ;
}
bool
xamarin_is _class _intptr ( MonoClass * cls )
{
return cls = = mono_get _intptr _class ( ) ;
}
bool
xamarin_is _class _string ( MonoClass * cls )
{
return cls = = mono_get _string _class ( ) ;
}
2021-05-14 08:27:42 +03:00
MonoException *
xamarin_create _system _invalid _cast _exception ( const char * message )
{
return ( MonoException * ) mono_exception _from _name _msg ( mono_get _corlib ( ) , "System" , "InvalidCastException" , message ) ;
}
MonoException *
xamarin_create _system _exception ( const char * message )
{
return ( MonoException * ) mono_exception _from _name _msg ( mono_get _corlib ( ) , "System" , "Exception" , message ) ;
}
MonoException *
xamarin_create _system _entry _point _not _found _exception ( const char * entrypoint )
{
return ( MonoException * ) mono_exception _from _name _msg ( mono_get _corlib ( ) , "System" , "EntryPointNotFoundException" , entrypoint ) ;
}
2021-03-22 10:04:56 +03:00
# if DOTNET
2021-06-16 16:22:02 +03:00
static void
xamarin_runtime _config _cleanup ( MonovmRuntimeConfigArguments * args , void * user_data )
{
free ( ( char * ) args -> runtimeconfig . name . path ) ;
free ( args ) ;
}
static void
xamarin_initialize _runtime _config ( )
{
if ( xamarin_runtime _configuration _name = = NULL ) {
LOG ( PRODUCT ": No runtime config file provided at build time.\n" ) ;
return ;
}
char path [ 1024 ] ;
2021-06-23 10:44:40 +03:00
if ( ! xamarin_locate _app _resource ( xamarin_runtime _configuration _name , path , sizeof ( path ) ) ) {
LOG ( PRODUCT ": Could not locate the runtime config file '%s' in the app bundle.\n" , xamarin_runtime _configuration _name ) ;
2021-06-16 16:22:02 +03:00
return ;
}
MonovmRuntimeConfigArguments * args = ( MonovmRuntimeConfigArguments * ) calloc ( sizeof ( MonovmRuntimeConfigArguments ) , 1 ) ;
args -> kind = 0 ; // Path of runtimeconfig . blob
args -> runtimeconfig . name . path = strdup ( path ) ;
int rv = monovm_runtimeconfig _initialize ( args , xamarin_runtime _config _cleanup , NULL ) ;
if ( rv ! = 0 ) {
LOG_MONOVM ( PRODUCT ": Failed to load the runtime config file %s: %i\n" , path , rv ) ;
return ;
}
LOG_MONOVM ( PRODUCT ": Loaded the runtime config file %s\n" , path ) ;
}
2021-03-22 10:04:56 +03:00
bool
xamarin_bridge _vm _initialize ( int propertyCount , const char * * propertyKeys , const char * * propertyValues )
{
int rv ;
2021-06-16 16:22:02 +03:00
xamarin_initialize _runtime _config ( ) ;
2021-03-22 10:04:56 +03:00
rv = monovm_initialize ( propertyCount , propertyKeys , propertyValues ) ;
LOG_MONOVM ( stderr , "xamarin_vm_initialize (%i, %p, %p): rv: %i\n" , propertyCount , propertyKeys , propertyValues , rv ) ;
return rv = = 0 ;
}
2021-05-06 08:25:43 +03:00
// We have a P / Invoke to xamarin_mono _object _retain in managed code , but the
// corresponding native method only really exists when using CoreCLR . However ,
// the P / Invoke might not always be linked away ( if the linker isn ' t enabled
// for instance ) , in which case we must still have a native function . So
// provide an empty implementation of xamarin_mono _object _retain ( since it
// doesn ' t have to do anything when using MonoVM ) . We still keep the # define
// that does nothing , so that all the native code that calls
// xamarin_mono _object _retain , will completely disappear when using MonoVM .
# undef xamarin_mono _object _retain
extern "C" {
void xamarin_mono _object _retain ( MonoObject * mobj ) ;
}
void
xamarin_mono _object _retain ( MonoObject * mobj )
{
// Nothing to do here
}
2021-05-25 09:19:27 +03:00
# if defined ( TRACK_MONOOBJECTS )
// This function is needed for the corresponding managed P / Invoke to not make
// the native linker fail due to an unresolved symbol . This method should
// never end up being called ( it ' ll be linked away by the native linker if the
// managed linker removes the P / Invoke , and never called from managed code
// otherwise ) .
void
xamarin_bridge _log _monoobject ( MonoObject * mobj , const char * stacktrace )
{
xamarin_assertion _message ( "%s is not available on MonoVM" , __func __ ) ;
}
# endif // defined ( TRACK_MONOOBJECTS )
2021-03-22 10:04:56 +03:00
# endif // DOTNET
2021-03-18 09:23:39 +03:00
2021-05-20 08:35:23 +03:00
/ *
* ToggleRef support
* /
// # define DEBUG_TOGGLEREF 1
static void
gc_register _toggleref ( MonoObject * obj , id self , bool isCustomType )
{
// COOP : This is an icall , at entry we ' re in unsafe mode . Managed memory is accessed , so we stay in unsafe mode .
MONO_ASSERT _GC _UNSAFE ;
# ifdef DEBUG_TOGGLEREF
id handle = xamarin_get _nsobject _handle ( obj ) ;
PRINT ( "**Registering object %p handle %p RC %d flags: %i isCustomType: %i" ,
obj ,
handle ,
( int ) ( handle ? [ handle retainCount ] : 0 ) ,
xamarin_get _nsobject _flags ( obj ) ,
isCustomType
) ;
# endif
mono_gc _toggleref _add ( obj , TRUE ) ;
// Make sure the GCHandle we have is a weak one for custom types .
if ( isCustomType ) {
MONO_ENTER _GC _SAFE ;
xamarin_switch _gchandle ( self , true ) ;
MONO_EXIT _GC _SAFE ;
}
}
static MonoToggleRefStatus
gc_toggleref _callback ( MonoObject * object )
{
// COOP : this is a callback called by the GC , so I assume the mode here doesn ' t matter
MonoToggleRefStatus res ;
uint8_t flags = xamarin_get _nsobject _flags ( object ) ;
res = xamarin_gc _toggleref _callback ( flags , NULL , xamarin_get _nsobject _handle , object ) ;
return res ;
}
static void
gc_event _callback ( MonoProfiler * prof , MonoGCEvent event , int generation )
{
// COOP : this is a callback called by the GC , I believe the mode here doesn ' t matter .
xamarin_gc _event ( event ) ;
}
void
xamarin_enable _new _refcount ( )
{
mono_gc _toggleref _register _callback ( gc_toggleref _callback ) ;
xamarin_add _internal _call ( "Foundation.NSObject::RegisterToggleRef" , ( const void * ) gc_register _toggleref ) ;
mono_profiler _install _gc ( gc_event _callback , NULL ) ;
}
2021-03-18 09:23:39 +03:00
# endif // ! CORECLR_RUNTIME