[net8.0] Merge main into net8.0.
This commit is contained in:
Коммит
ab73088c0b
|
@ -0,0 +1,227 @@
|
|||
# Managed static registrar
|
||||
|
||||
The managed static registrar is a variation of the static registrar where we
|
||||
don't use features the NativeAOT compiler doesn't support (most notably
|
||||
metadata tokens).
|
||||
|
||||
It also takes advantage of new features in C# and managed code since the
|
||||
original static registrar code was written - in particular it tries to do as
|
||||
much as possible in managed code instead of native code, as well as various
|
||||
other performance improvements. The actual performance characteristics
|
||||
compared to the original static registrar will vary between the specific
|
||||
exported method signatures, but in general it's expected that method calls
|
||||
from native code to managed code will be faster.
|
||||
|
||||
In order to make the managed static registrar easily testable and debuggable,
|
||||
it's also implemented for the other runtimes as well (Mono and CoreCLR as
|
||||
well), as well as when not using AOT in any form.
|
||||
|
||||
## Design
|
||||
|
||||
### Exported methods
|
||||
|
||||
For each method exported to Objective-C, the managed static registrar will
|
||||
generate a managed method we'll call directly from native code, and which does
|
||||
all the marshalling.
|
||||
|
||||
This method will have the [UnmanagedCallersOnly] attribute, so that it doesn't
|
||||
need any additional marshalling from the managed runtime - which makes it
|
||||
possible to obtain a native function pointer for it. It will also have a
|
||||
native entry point, which means that for AOT we can just directly call it from
|
||||
the generated Objective-C code.
|
||||
|
||||
Given the following method:
|
||||
|
||||
```csharp
|
||||
class AppDelegate : NSObject, IUIApplicationDelegate {
|
||||
// this method is written by the app developer
|
||||
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The managed static registrar will add the following method to the `AppDelegate` class:
|
||||
|
||||
```csharp
|
||||
class AppDelegate {
|
||||
[UnmanagedCallersOnly (EntryPoint = "__registrar__uiapplicationdelegate_didFinishLaunching")]
|
||||
static byte __registrar__DidFinishLaunchingWithOptions (IntPtr handle, IntPtr selector, IntPtr p0, IntPtr p1)
|
||||
{
|
||||
var obj = Runtime.GetNSObject (handle);
|
||||
var p0Obj = (UIApplication) Runtime.GetNSObject (p0);
|
||||
var p1Obj = (NSDictionary) Runtime.GetNSObject (p1);
|
||||
var rv = obj.DidFinishLaunchingWithOptions (p0Obj, p1Obj);
|
||||
return rv ? (byte) 1 : (byte) 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
and the generated Objective-C code will look something like this:
|
||||
|
||||
```objective-c
|
||||
extern BOOL __registrar__uiapplicationdelegate_init (AppDelegate self, SEL _cmd, UIApplication* p0, NSDictionary* p1);
|
||||
|
||||
@interface AppDelegate : NSObject<UIApplicationDelegate, UIApplicationDelegate> {
|
||||
}
|
||||
-(BOOL) application:(UIApplication *)p0 didFinishLaunchingWithOptions:(NSDictionary *)p1;
|
||||
@end
|
||||
@implementation AppDelegate {
|
||||
}
|
||||
-(BOOL) application:(UIApplication *)p0 didFinishLaunchingWithOptions:(NSDictionary *)p1
|
||||
{
|
||||
return __registrar__uiapplicationdelegate_didFinishLaunching (self, _cmd, p0, p1);
|
||||
}
|
||||
@end
|
||||
```
|
||||
|
||||
Note: the actual code is somewhat more complex in order to properly support
|
||||
managed exceptions and a few other corner cases.
|
||||
|
||||
### Type mapping
|
||||
|
||||
The runtime needs to quickly and efficiently do lookups between an Objective-C
|
||||
type and the corresponding managed type. In order to support this, the managed
|
||||
static registrar will add lookup tables in each assembly. The managed static
|
||||
registrar will create a numeric ID for each managed type, which is then
|
||||
emitted into the generated Objective-C code, and which we can use to look up
|
||||
the corresponding managed type. There is also a table in Objective-C that maps
|
||||
between the numeric ID and the corresponding Objective-C type.
|
||||
|
||||
We also need to be able to find the wrapper type for interfaces representing
|
||||
Objective-C protocols - this is accomplished by generating a table in
|
||||
unmanaged code that maps the ID for the interface to the ID for the wrapper
|
||||
type.
|
||||
|
||||
This is all supported by the `ObjCRuntime.IManagedRegistrar.LookupTypeId` and
|
||||
`ObjCRuntime.IManagedRegistrar.LookupType` methods.
|
||||
|
||||
Note that in many ways the type ID is similar to the metadata token for a type
|
||||
(and is sometimes referred to as such in the code, especially code that
|
||||
already existed before the managed static registrar was implemented).
|
||||
|
||||
### Method mapping
|
||||
|
||||
When AOT-compiling code, the generated Objective-C code can call the entry
|
||||
point for the UnmanagedCallersOnly trampoline directly (the AOT compiler will
|
||||
emit a native symbol with the name of the entry point).
|
||||
|
||||
However, when not AOT-compiling code, the generated Objective-C code needs to
|
||||
find the function pointer for the UnmanagedCallersOnly methods. This is
|
||||
implemented using another lookup table in managed code.
|
||||
|
||||
For technical reasons, this is implemented using multiple levels of functions if
|
||||
there is a significant number of UnmanagedCallersOnly methods. As it seems
|
||||
that the JIT will compile the target for every function pointer in a method,
|
||||
even if the function pointer isn't loaded at runtime. This means that if
|
||||
there are 1.000 methods in the lookup table and if the lookup was
|
||||
implemented in a single function, the JIT will have to compile all
|
||||
the 1.000 methods the first time the lookup method is called, even
|
||||
if the lookup method will eventually just find a single callback.
|
||||
|
||||
This might be easier to describe with some code.
|
||||
|
||||
Instead of this:
|
||||
|
||||
```csharp
|
||||
class __Registrar_Callbacks__ {
|
||||
IntPtr LookupUnmanagedFunction (int id)
|
||||
{
|
||||
switch (id) {
|
||||
case 0: return (IntPtr) (delegate* unmanaged<void>) &Callback0;
|
||||
case 1: return (IntPtr) (delegate* unmanaged<void>) &Callback1;
|
||||
...
|
||||
case 999: return (IntPtr) (delegate* unmanaged<void>) &Callback999;
|
||||
}
|
||||
return (IntPtr) -1);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
we do this instead:
|
||||
|
||||
```csharp
|
||||
class __Registrar_Callbacks__ {
|
||||
IntPtr LookupUnmanagedFunction (int id)
|
||||
{
|
||||
if (id < 100)
|
||||
return LookupUnmanagedFunction_0 (id);
|
||||
if (id < 200)
|
||||
return LookupUnmanagedFunction_1 (id);
|
||||
...
|
||||
if (id < 1000)
|
||||
LookupUnmanagedFunction_9 (id);
|
||||
return (IntPtr) -1;
|
||||
}
|
||||
|
||||
IntPtr LookupUnmanagedFunction_0 (int id)
|
||||
{
|
||||
switch (id) {
|
||||
case 0: return (IntPtr) (delegate* unmanaged<void>) &Callback0;
|
||||
case 1: return (IntPtr) (delegate* unmanaged<void>) &Callback1;
|
||||
/// ...
|
||||
case 9: return (IntPtr) (delegate* unmanaged<void>) &Callback9;
|
||||
}
|
||||
return (IntPtr) -1;
|
||||
}
|
||||
|
||||
|
||||
IntPtr LookupUnmanagedFunction_1 (int id)
|
||||
{
|
||||
switch (id) {
|
||||
case 10: return (IntPtr) (delegate* unmanaged<void>) &Callback10;
|
||||
case 11: return (IntPtr) (delegate* unmanaged<void>) &Callback11;
|
||||
/// ...
|
||||
case 19: return (IntPtr) (delegate* unmanaged<void>) &Callback19;
|
||||
}
|
||||
return (IntPtr) -1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Generation
|
||||
|
||||
All the generated IL is done in two separate custom linker steps. The first
|
||||
one, ManagedRegistrarStep, will generate the UnmanagedCallersOnly trampolines
|
||||
for every method exported to Objective-C. This happens before the trimmer has
|
||||
done any work (i.e. before marking), because the generated code will cause
|
||||
more code to be marked (and this way we don't have to replicate what the
|
||||
trimmer does when it traverses IL and metadata to figure out what else to
|
||||
mark).
|
||||
|
||||
The trimmer will then trim away any UnmanagedCallersOnly trampoline that's no
|
||||
longer needed because the target method has been trimmed away.
|
||||
|
||||
On the other hand, the lookup tables for the type mapping are generated after
|
||||
trimming, because we only want to add types that aren't trimmed away to the
|
||||
lookup tables (otherwise we'd end up causing all those types to be kept).
|
||||
|
||||
## Interpreter / JIT
|
||||
|
||||
When not using the AOT compiler, we need to look up the native entry points
|
||||
for UnmanagedCallersOnly methods at runtime. In order to support this, the
|
||||
managed static registrar will add lookup tables in each assembly. The managed
|
||||
static registrar will create a numeric ID for each UnmanagedCallersOnly
|
||||
method, which is then emitted into the generated Objective-C code, and which
|
||||
we can use to look up the managed UnmanagedCallersOnly method at runtime (in
|
||||
the lookup table).
|
||||
|
||||
This is the `ObjCRuntime.IManagedRegistrar.LookupUnmanagedFunction` method.
|
||||
|
||||
## Performance
|
||||
|
||||
Preliminary testing shows the following:
|
||||
|
||||
### macOS
|
||||
|
||||
Calling an exported managed method from Objective-C is 3-6x faster for simple method signatures.
|
||||
|
||||
### Mac Catalyst
|
||||
|
||||
Calling an exported managed method from Objective-C is 30-50% faster for simple method signatures.
|
||||
|
||||
## References
|
||||
|
||||
* https://github.com/dotnet/runtime/issues/80912
|
|
@ -559,6 +559,10 @@
|
|||
<_ExtraTrimmerArgs Condition="('$(_PlatformName)' == 'iOS' Or '$(_PlatformName)' == 'tvOS') And '$(_SdkIsSimulator)' == 'true'">$(_ExtraTrimmerArgs) --feature ObjCRuntime.Runtime.Arch.IsSimulator true</_ExtraTrimmerArgs>
|
||||
<_ExtraTrimmerArgs Condition="('$(_PlatformName)' == 'iOS' Or '$(_PlatformName)' == 'tvOS') And '$(_SdkIsSimulator)' != 'true'">$(_ExtraTrimmerArgs) --feature ObjCRuntime.Runtime.Arch.IsSimulator false</_ExtraTrimmerArgs>
|
||||
|
||||
<!-- Set managed static registrar value -->
|
||||
<_ExtraTrimmerArgs Condition="'$(Registrar)' == 'managed-static'">$(_ExtraTrimmerArgs) --feature ObjCRuntime.Runtime.IsManagedStaticRegistrar true</_ExtraTrimmerArgs>
|
||||
<_ExtraTrimmerArgs Condition="'$(Registrar)' != 'managed-static'">$(_ExtraTrimmerArgs) --feature ObjCRuntime.Runtime.IsManagedStaticRegistrar false</_ExtraTrimmerArgs>
|
||||
|
||||
<!-- Enable serialization discovery. Ref: https://github.com/xamarin/xamarin-macios/issues/15676 -->
|
||||
<_ExtraTrimmerArgs>$(_ExtraTrimmerArgs) --enable-serialization-discovery</_ExtraTrimmerArgs>
|
||||
|
||||
|
@ -591,6 +595,7 @@
|
|||
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="MonoTouch.Tuner.RegistrarRemovalTrackingStep" />
|
||||
<!-- TODO: these steps should probably run after mark. -->
|
||||
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true'" Type="Xamarin.Linker.Steps.PreMarkDispatcher" />
|
||||
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="MarkStep" Type="Xamarin.Linker.ManagedRegistrarStep" Condition="'$(Registrar)' == 'managed-static'" />
|
||||
|
||||
<!--
|
||||
IMarkHandlers which run during Mark
|
||||
|
@ -603,6 +608,11 @@
|
|||
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true'" Type="Xamarin.Linker.Steps.MarkDispatcher" />
|
||||
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" Condition="'$(_AreAnyAssembliesTrimmed)' == 'true'" Type="Xamarin.Linker.Steps.PreserveSmartEnumConversionsHandler" />
|
||||
|
||||
<!--
|
||||
pre-sweep custom steps
|
||||
-->
|
||||
<_TrimmerCustomSteps Include="$(_AdditionalTaskAssembly)" BeforeStep="SweepStep" Type="Xamarin.Linker.ManagedRegistrarLookupTablesStep" Condition="'$(Registrar)' == 'managed-static'" />
|
||||
|
||||
<!--
|
||||
post-sweep custom steps
|
||||
-->
|
||||
|
@ -1910,6 +1920,10 @@ global using nfloat = global::System.Runtime.InteropServices.NFloat%3B
|
|||
<_BindingPackagesFromReferencedAssembliesDirectoriesExists Include="@(_BindingPackagesFromReferencedAssembliesDirectoriesCandidates->Distinct())" Condition="Exists('%(Identity)')" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(IsMacEnabled)' == 'true'">
|
||||
<BuildSessionIdIfConnected>$(BuildSessionId)</BuildSessionIdIfConnected>
|
||||
</PropertyGroup>
|
||||
|
||||
<!--
|
||||
We need to expand items in an item group using globs, which is
|
||||
kind of tricky, because globs in item transformations aren't
|
||||
|
@ -1918,11 +1932,12 @@ global using nfloat = global::System.Runtime.InteropServices.NFloat%3B
|
|||
Note that this task should run remotely from Windows when there's
|
||||
a Mac connected, but locally when there's not a Mac connected (for
|
||||
Hot Restart builds for instance), so we're not conditioning this
|
||||
task on IsMacEnabled.
|
||||
task on IsMacEnabled. Instead we're only setting SessionId if we
|
||||
have a session id *and* we're connected to a Mac.
|
||||
|
||||
-->
|
||||
<GetFileSystemEntries
|
||||
SessionId="$(BuildSessionId)"
|
||||
SessionId="$(BuildSessionIdIfConnected)"
|
||||
DirectoryPath="@(_BindingPackagesFromReferencedAssembliesDirectoriesExists)"
|
||||
Pattern="*"
|
||||
Recursive="true"
|
||||
|
|
|
@ -667,6 +667,14 @@
|
|||
) {
|
||||
WrappedManagedFunction = "InvokeConformsToProtocol",
|
||||
},
|
||||
|
||||
new XDelegate ("void *", "IntPtr", "xamarin_lookup_unmanaged_function",
|
||||
"const char *", "IntPtr", "assembly",
|
||||
"const char *", "IntPtr", "symbol",
|
||||
"int32_t", "int", "id"
|
||||
) {
|
||||
WrappedManagedFunction = "LookupUnmanagedFunction",
|
||||
},
|
||||
};
|
||||
delegates.CalculateLengths ();
|
||||
#><#+
|
||||
|
|
|
@ -136,7 +136,7 @@ struct Trampolines {
|
|||
|
||||
enum InitializationFlags : int {
|
||||
InitializationFlagsIsPartialStaticRegistrar = 0x01,
|
||||
/* unused = 0x02,*/
|
||||
InitializationFlagsIsManagedStaticRegistrar = 0x02,
|
||||
/* unused = 0x04,*/
|
||||
/* unused = 0x08,*/
|
||||
InitializationFlagsIsSimulator = 0x10,
|
||||
|
@ -2736,6 +2736,30 @@ xamarin_vprintf (const char *format, va_list args)
|
|||
[message release];
|
||||
}
|
||||
|
||||
void
|
||||
xamarin_registrar_dlsym (void **function_pointer, const char *assembly, const char *symbol, int32_t id)
|
||||
{
|
||||
if (*function_pointer != NULL)
|
||||
return;
|
||||
|
||||
*function_pointer = dlsym (RTLD_MAIN_ONLY, symbol);
|
||||
if (*function_pointer != NULL)
|
||||
return;
|
||||
|
||||
GCHandle exception_gchandle = INVALID_GCHANDLE;
|
||||
*function_pointer = xamarin_lookup_unmanaged_function (assembly, symbol, id, &exception_gchandle);
|
||||
if (*function_pointer != NULL)
|
||||
return;
|
||||
|
||||
if (exception_gchandle != INVALID_GCHANDLE)
|
||||
xamarin_process_managed_exception_gchandle (exception_gchandle);
|
||||
|
||||
// This shouldn't really happen
|
||||
NSString *msg = [NSString stringWithFormat: @"Unable to load the symbol '%s' to call managed code: %@", symbol, xamarin_print_all_exceptions (exception_gchandle)];
|
||||
NSLog (@"%@", msg);
|
||||
@throw [[NSException alloc] initWithName: @"SymbolNotFoundException" reason: msg userInfo: NULL];
|
||||
}
|
||||
|
||||
/*
|
||||
* File/resource lookup for assemblies
|
||||
*
|
||||
|
@ -3195,6 +3219,16 @@ xamarin_get_is_debug ()
|
|||
return xamarin_debug_mode;
|
||||
}
|
||||
|
||||
void
|
||||
xamarin_set_is_managed_static_registrar (bool value)
|
||||
{
|
||||
if (value) {
|
||||
options.flags = (InitializationFlags) (options.flags | InitializationFlagsIsManagedStaticRegistrar);
|
||||
} else {
|
||||
options.flags = (InitializationFlags) (options.flags & ~InitializationFlagsIsManagedStaticRegistrar);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
xamarin_is_managed_exception_marshaling_disabled ()
|
||||
{
|
||||
|
|
|
@ -254,6 +254,7 @@ void xamarin_check_objc_type (id obj, Class expected_class, SEL sel, id self,
|
|||
#endif
|
||||
|
||||
void xamarin_set_gc_pump_enabled (bool value);
|
||||
void xamarin_set_is_managed_static_registrar (bool value);
|
||||
|
||||
void xamarin_process_nsexception (NSException *exc);
|
||||
void xamarin_process_nsexception_using_mode (NSException *ns_exception, bool throwManagedAsDefault, GCHandle *output_exception);
|
||||
|
@ -295,6 +296,15 @@ void xamarin_printf (const char *format, ...);
|
|||
void xamarin_vprintf (const char *format, va_list args);
|
||||
void xamarin_install_log_callbacks ();
|
||||
|
||||
/*
|
||||
* Looks up a native function pointer for a managed [UnmanagedCallersOnly] method.
|
||||
* function_pointer: the return value, lookup will only be performed if this points to NULL.
|
||||
* assembly: the assembly to look in. Might be NULL if the app was not built with support for loading additional assemblies at runtime.
|
||||
* symbol: the symbol to look up. Can be NULL to save space (this value isn't used except in error messages).
|
||||
* id: a numerical id for faster lookup (than doing string comparisons on the symbol name).
|
||||
*/
|
||||
void xamarin_registrar_dlsym (void **function_pointer, const char *assembly, const char *symbol, int32_t id);
|
||||
|
||||
/*
|
||||
* Wrapper GCHandle functions that takes pointer sized handles instead of ints,
|
||||
* so that we can adapt our code incrementally to use pointers instead of ints
|
||||
|
|
|
@ -295,6 +295,19 @@ namespace Foundation {
|
|||
return ret;
|
||||
}
|
||||
|
||||
static Array ArrayFromHandle (NativeHandle handle, Type elementType)
|
||||
{
|
||||
if (handle == NativeHandle.Zero)
|
||||
return null;
|
||||
|
||||
var c = (int) GetCount (handle);
|
||||
var rv = Array.CreateInstance (elementType, c);
|
||||
for (int i = 0; i < c; i++) {
|
||||
rv.SetValue (UnsafeGetItem (handle, (nuint) i, elementType), i);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static public T [] EnumsFromHandle<T> (NativeHandle handle) where T : struct, IConvertible
|
||||
{
|
||||
if (handle == NativeHandle.Zero)
|
||||
|
@ -395,6 +408,18 @@ namespace Foundation {
|
|||
return Runtime.GetINativeObject<T> (val, false);
|
||||
}
|
||||
|
||||
static object UnsafeGetItem (NativeHandle handle, nuint index, Type type)
|
||||
{
|
||||
var val = GetAtIndex (handle, index);
|
||||
// A native code could return NSArray with NSNull.Null elements
|
||||
// and they should be valid for things like T : NSDate so we handle
|
||||
// them as just null values inside the array
|
||||
if (val == NSNull.Null.Handle)
|
||||
return null;
|
||||
|
||||
return Runtime.GetINativeObject (val, false, type);
|
||||
}
|
||||
|
||||
// can return an INativeObject or an NSObject
|
||||
public T GetItem<T> (nuint index) where T : class, INativeObject
|
||||
{
|
||||
|
|
|
@ -231,6 +231,14 @@ namespace Foundation {
|
|||
GC.SuppressFinalize (this);
|
||||
}
|
||||
|
||||
static T AllocateNSObject<T> (IntPtr handle) where T : NSObject
|
||||
{
|
||||
var obj = (T) RuntimeHelpers.GetUninitializedObject (typeof (T));
|
||||
obj.handle = handle;
|
||||
obj.flags = Flags.NativeRef;
|
||||
return obj;
|
||||
}
|
||||
|
||||
internal static IntPtr CreateNSObject (IntPtr type_gchandle, IntPtr handle, Flags flags)
|
||||
{
|
||||
// This function is called from native code before any constructors have executed.
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
</type>
|
||||
<type fullname="ObjCRuntime.Runtime">
|
||||
<method signature="System.Boolean get_IsCoreCLR()" body="stub" value="false" />
|
||||
<method signature="System.Boolean get_IsManagedStaticRegistrar()" body="stub" feature="ObjCRuntime.Runtime.IsManagedStaticRegistrar" featurevalue="false" value="false" />
|
||||
<method signature="System.Boolean get_IsManagedStaticRegistrar()" body="stub" feature="ObjCRuntime.Runtime.IsManagedStaticRegistrar" featurevalue="true" value="true" />
|
||||
</type>
|
||||
<type fullname="UIKit.UIApplication">
|
||||
<method signature="System.Void EnsureEventAndDelegateAreNotMismatched(System.Object,System.Type)" body="stub" feature="System.Diagnostics.Debugger.IsSupported" featurevalue="false" />
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
<method signature="System.Boolean get_IsCoreCLR()" body="stub" value="false" />
|
||||
<method signature="System.Int32 GetRuntimeArch()" body="stub" feature="ObjCRuntime.Runtime.Arch.IsSimulator" featurevalue="false" value="0" />
|
||||
<method signature="System.Int32 GetRuntimeArch()" body="stub" feature="ObjCRuntime.Runtime.Arch.IsSimulator" featurevalue="true" value="1" />
|
||||
<method signature="System.Boolean get_IsManagedStaticRegistrar()" body="stub" feature="ObjCRuntime.Runtime.IsManagedStaticRegistrar" featurevalue="false" value="false" />
|
||||
<method signature="System.Boolean get_IsManagedStaticRegistrar()" body="stub" feature="ObjCRuntime.Runtime.IsManagedStaticRegistrar" featurevalue="true" value="true" />
|
||||
</type>
|
||||
<type fullname="UIKit.UIApplication">
|
||||
<method signature="System.Void EnsureEventAndDelegateAreNotMismatched(System.Object,System.Type)" body="stub" feature="System.Diagnostics.Debugger.IsSupported" featurevalue="false" />
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
</type>
|
||||
<type fullname="ObjCRuntime.Runtime">
|
||||
<method signature="System.Boolean get_IsCoreCLR()" body="stub" value="true" />
|
||||
<method signature="System.Boolean get_IsManagedStaticRegistrar()" body="stub" feature="ObjCRuntime.Runtime.IsManagedStaticRegistrar" featurevalue="false" value="false" />
|
||||
<method signature="System.Boolean get_IsManagedStaticRegistrar()" body="stub" feature="ObjCRuntime.Runtime.IsManagedStaticRegistrar" featurevalue="true" value="true" />
|
||||
</type>
|
||||
<type fullname="AppKit.NSApplication">
|
||||
<method signature="System.Void EnsureEventAndDelegateAreNotMismatched(System.Object,System.Type)" body="stub" feature="System.Diagnostics.Debugger.IsSupported" featurevalue="false" />
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
<method signature="System.Boolean get_IsCoreCLR()" body="stub" value="false" />
|
||||
<method signature="System.Int32 GetRuntimeArch()" body="stub" feature="ObjCRuntime.Runtime.Arch.IsSimulator" featurevalue="false" value="0" />
|
||||
<method signature="System.Int32 GetRuntimeArch()" body="stub" feature="ObjCRuntime.Runtime.Arch.IsSimulator" featurevalue="true" value="1" />
|
||||
<method signature="System.Boolean get_IsManagedStaticRegistrar()" body="stub" feature="ObjCRuntime.Runtime.IsManagedStaticRegistrar" featurevalue="false" value="false" />
|
||||
<method signature="System.Boolean get_IsManagedStaticRegistrar()" body="stub" feature="ObjCRuntime.Runtime.IsManagedStaticRegistrar" featurevalue="true" value="true" />
|
||||
</type>
|
||||
<type fullname="UIKit.UIApplication">
|
||||
<method signature="System.Void EnsureEventAndDelegateAreNotMismatched(System.Object,System.Type)" body="stub" feature="System.Diagnostics.Debugger.IsSupported" featurevalue="false" />
|
||||
|
|
|
@ -54,6 +54,7 @@ DOTNET_REFERENCES = \
|
|||
/r:$(DOTNET_BCL_DIR)/System.Console.dll \
|
||||
/r:$(DOTNET_BCL_DIR)/System.Diagnostics.Debug.dll \
|
||||
/r:$(DOTNET_BCL_DIR)/System.Diagnostics.Tools.dll \
|
||||
/r:$(DOTNET_BCL_DIR)/System.Diagnostics.StackTrace.dll \
|
||||
/r:$(DOTNET_BCL_DIR)/System.Drawing.Primitives.dll \
|
||||
/r:$(DOTNET_BCL_DIR)/System.IO.Compression.dll \
|
||||
/r:$(DOTNET_BCL_DIR)/System.IO.FileSystem.dll \
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
//
|
||||
// BindAs.cs: Helper code for BindAs support.
|
||||
//
|
||||
// Authors:
|
||||
// Rolf Bjarne Kvinge
|
||||
//
|
||||
// Copyright 2023 Microsoft Corp
|
||||
|
||||
#if NET
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using CoreFoundation;
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
|
||||
using Registrar;
|
||||
|
||||
namespace ObjCRuntime {
|
||||
// Helper code for BindAs support.
|
||||
// The managed static registrar will make any API here it uses public.
|
||||
static class BindAs {
|
||||
// xamarin_convert_nsarray_to_managed_with_func
|
||||
static T Identity<T> (T obj)
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
unsafe static T[]? ConvertNSArrayToManagedArray<T> (IntPtr nsarray, delegate*<IntPtr, T> convert) where T: struct
|
||||
{
|
||||
if (nsarray == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
return ConvertNSArrayToManagedArray2<T, T> (nsarray, convert, &Identity<T>);
|
||||
}
|
||||
|
||||
unsafe static IntPtr ConvertManagedArrayToNSArray<T> (T[]? array, delegate*<T, IntPtr> convert) where T: struct
|
||||
{
|
||||
if (array is null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
return ConvertManagedArrayToNSArray2<T, T> (array, &Identity<T>, convert);
|
||||
}
|
||||
|
||||
unsafe static T[]? ConvertNSArrayToManagedArray2<T,U> (IntPtr nsarray, delegate*<IntPtr, U> convert1, delegate*<U, T> convert2) where T: struct
|
||||
{
|
||||
if (nsarray == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
return NSArray.ArrayFromHandleFunc<T> (nsarray, (ptr) => convert2 (convert1 (ptr)));
|
||||
}
|
||||
|
||||
unsafe static IntPtr ConvertManagedArrayToNSArray2<T,U> (T[]? array, delegate*<T, U> convert1, delegate*<U, IntPtr> convert2) where T: struct
|
||||
{
|
||||
if (array is null)
|
||||
return IntPtr.Zero;
|
||||
|
||||
NSArray arr;
|
||||
var count = array.Length;
|
||||
if (count == 0) {
|
||||
arr = new NSArray ();
|
||||
} else {
|
||||
var ptrs = new IntPtr [count];
|
||||
for (nint i = 0; i < count; i++) {
|
||||
var item = convert2 (convert1 (array [i]));
|
||||
if (item == IntPtr.Zero)
|
||||
item = NSNull.Null.Handle;
|
||||
ptrs [i] = item;
|
||||
}
|
||||
fixed (void* ptr = &ptrs[0]) {
|
||||
arr = Runtime.GetNSObject<NSArray> (NSArray.FromObjects ((IntPtr) ptr, count))!;
|
||||
}
|
||||
}
|
||||
|
||||
arr.DangerousRetain ();
|
||||
arr.DangerousAutorelease ();
|
||||
var rv = arr.Handle;
|
||||
arr.Dispose ();
|
||||
return rv;
|
||||
}
|
||||
|
||||
unsafe static T? CreateNullable<T> (IntPtr handle, delegate*<IntPtr, T> convert) where T: struct
|
||||
{
|
||||
if (handle == IntPtr.Zero)
|
||||
return null;
|
||||
return convert (handle);
|
||||
}
|
||||
|
||||
unsafe static T? CreateNullable2<T, U> (IntPtr handle, delegate*<IntPtr, U> convert1, delegate*<U, T> convert2) where T: struct
|
||||
{
|
||||
if (handle == IntPtr.Zero)
|
||||
return null;
|
||||
return convert2 (convert1 (handle));
|
||||
}
|
||||
|
||||
static Foundation.NSRange xamarin_nsvalue_to_nsrange (IntPtr value) { if (value == IntPtr.Zero) return default (Foundation.NSRange); return Runtime.GetNSObject<NSValue> (value)?.RangeValue ?? default (Foundation.NSRange); }
|
||||
static CoreGraphics.CGAffineTransform xamarin_nsvalue_to_cgaffinetransform (IntPtr value) { if (value == IntPtr.Zero) return default (CoreGraphics.CGAffineTransform); return Runtime.GetNSObject<NSValue> (value)?.CGAffineTransformValue ?? default (CoreGraphics.CGAffineTransform); }
|
||||
static CoreGraphics.CGPoint xamarin_nsvalue_to_cgpoint (IntPtr value) { if (value == IntPtr.Zero) return default (CoreGraphics.CGPoint); return Runtime.GetNSObject<NSValue> (value)?.CGPointValue ?? default (CoreGraphics.CGPoint); }
|
||||
static CoreGraphics.CGRect xamarin_nsvalue_to_cgrect (IntPtr value) { if (value == IntPtr.Zero) return default (CoreGraphics.CGRect); return Runtime.GetNSObject<NSValue> (value)?.CGRectValue ?? default (CoreGraphics.CGRect); }
|
||||
static CoreGraphics.CGSize xamarin_nsvalue_to_cgsize (IntPtr value) { if (value == IntPtr.Zero) return default (CoreGraphics.CGSize); return Runtime.GetNSObject<NSValue> (value)?.CGSizeValue ?? default (CoreGraphics.CGSize); }
|
||||
#if !__MACOS__
|
||||
static CoreGraphics.CGVector xamarin_nsvalue_to_cgvector (IntPtr value) { if (value == IntPtr.Zero) return default (CoreGraphics.CGVector); return Runtime.GetNSObject<NSValue> (value)?.CGVectorValue ?? default (CoreGraphics.CGVector); }
|
||||
#endif
|
||||
static CoreAnimation.CATransform3D xamarin_nsvalue_to_catransform3d (IntPtr value) { if (value == IntPtr.Zero) return default (CoreAnimation.CATransform3D); return Runtime.GetNSObject<NSValue> (value)?.CATransform3DValue ?? default (CoreAnimation.CATransform3D); }
|
||||
static CoreLocation.CLLocationCoordinate2D xamarin_nsvalue_to_cllocationcoordinate2d (IntPtr value) { if (value == IntPtr.Zero) return default (CoreLocation.CLLocationCoordinate2D); return Runtime.GetNSObject<NSValue> (value)?.CoordinateValue ?? default (CoreLocation.CLLocationCoordinate2D); }
|
||||
static CoreMedia.CMTime xamarin_nsvalue_to_cmtime (IntPtr value) { if (value == IntPtr.Zero) return default (CoreMedia.CMTime); return Runtime.GetNSObject<NSValue> (value)?.CMTimeValue ?? default (CoreMedia.CMTime); }
|
||||
static CoreMedia.CMTimeMapping xamarin_nsvalue_to_cmtimemapping (IntPtr value) { if (value == IntPtr.Zero) return default (CoreMedia.CMTimeMapping); return Runtime.GetNSObject<NSValue> (value)?.CMTimeMappingValue ?? default (CoreMedia.CMTimeMapping); }
|
||||
static CoreMedia.CMTimeRange xamarin_nsvalue_to_cmtimerange (IntPtr value) { if (value == IntPtr.Zero) return default (CoreMedia.CMTimeRange); return Runtime.GetNSObject<NSValue> (value)?.CMTimeRangeValue ?? default (CoreMedia.CMTimeRange); }
|
||||
static CoreMedia.CMVideoDimensions xamarin_nsvalue_to_cmvideodimensions (IntPtr value) { if (value == IntPtr.Zero) return default (CoreMedia.CMVideoDimensions); return Runtime.GetNSObject<NSValue> (value)?.CMVideoDimensionsValue ?? default (CoreMedia.CMVideoDimensions); }
|
||||
static MapKit.MKCoordinateSpan xamarin_nsvalue_to_mkcoordinatespan (IntPtr value) { if (value == IntPtr.Zero) return default (MapKit.MKCoordinateSpan); return Runtime.GetNSObject<NSValue> (value)?.CoordinateSpanValue ?? default (MapKit.MKCoordinateSpan); }
|
||||
static SceneKit.SCNMatrix4 xamarin_nsvalue_to_scnmatrix4 (IntPtr value) { if (value == IntPtr.Zero) return default (SceneKit.SCNMatrix4); return Runtime.GetNSObject<NSValue> (value)?.SCNMatrix4Value ?? default (SceneKit.SCNMatrix4); }
|
||||
static SceneKit.SCNVector3 xamarin_nsvalue_to_scnvector3 (IntPtr value) { if (value == IntPtr.Zero) return default (SceneKit.SCNVector3); return Runtime.GetNSObject<NSValue> (value)?.Vector3Value ?? default (SceneKit.SCNVector3); }
|
||||
static SceneKit.SCNVector4 xamarin_nsvalue_to_scnvector4 (IntPtr value) { if (value == IntPtr.Zero) return default (SceneKit.SCNVector4); return Runtime.GetNSObject<NSValue> (value)?.Vector4Value ?? default (SceneKit.SCNVector4); }
|
||||
#if HAS_UIKIT
|
||||
static UIKit.UIEdgeInsets xamarin_nsvalue_to_uiedgeinsets (IntPtr value) { if (value == IntPtr.Zero) return default (UIKit.UIEdgeInsets); return Runtime.GetNSObject<NSValue> (value)?.UIEdgeInsetsValue ?? default (UIKit.UIEdgeInsets); }
|
||||
static UIKit.UIOffset xamarin_nsvalue_to_uioffset (IntPtr value) { if (value == IntPtr.Zero) return default (UIKit.UIOffset); return Runtime.GetNSObject<NSValue> (value)?.UIOffsetValue ?? default (UIKit.UIOffset); }
|
||||
static UIKit.NSDirectionalEdgeInsets xamarin_nsvalue_to_nsdirectionaledgeinsets (IntPtr value) { if (value == IntPtr.Zero) return default (UIKit.NSDirectionalEdgeInsets); return Runtime.GetNSObject<NSValue> (value)?.DirectionalEdgeInsetsValue ?? default (UIKit.NSDirectionalEdgeInsets); }
|
||||
#endif
|
||||
|
||||
static IntPtr xamarin_nsrange_to_nsvalue (Foundation.NSRange value) { using var rv = NSValue.FromRange (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_cgaffinetransform_to_nsvalue (CoreGraphics.CGAffineTransform value) { using var rv = NSValue.FromCGAffineTransform (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_cgpoint_to_nsvalue (CoreGraphics.CGPoint value) { using var rv = NSValue.FromCGPoint (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_cgrect_to_nsvalue (CoreGraphics.CGRect value) { using var rv = NSValue.FromCGRect (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_cgsize_to_nsvalue (CoreGraphics.CGSize value) { using var rv = NSValue.FromCGSize (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
#if !__MACOS__
|
||||
static IntPtr xamarin_cgvector_to_nsvalue (CoreGraphics.CGVector value) { using var rv = NSValue.FromCGVector (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
#endif
|
||||
static IntPtr xamarin_catransform3d_to_nsvalue (CoreAnimation.CATransform3D value) { using var rv = NSValue.FromCATransform3D (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_cllocationcoordinate2d_to_nsvalue (CoreLocation.CLLocationCoordinate2D value) { using var rv = NSValue.FromMKCoordinate (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_cmtime_to_nsvalue (CoreMedia.CMTime value) { using var rv = NSValue.FromCMTime (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_cmtimemapping_to_nsvalue (CoreMedia.CMTimeMapping value) { using var rv = NSValue.FromCMTimeMapping (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_cmtimerange_to_nsvalue (CoreMedia.CMTimeRange value) { using var rv = NSValue.FromCMTimeRange (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_cmvideodimensions_to_nsvalue (CoreMedia.CMVideoDimensions value) { using var rv = NSValue.FromCMVideoDimensions (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_mkcoordinatespan_to_nsvalue (MapKit.MKCoordinateSpan value) { using var rv = NSValue.FromMKCoordinateSpan (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_scnmatrix4_to_nsvalue (SceneKit.SCNMatrix4 value) { using var rv = NSValue.FromSCNMatrix4 (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_scnvector3_to_nsvalue (SceneKit.SCNVector3 value) { using var rv = NSValue.FromVector (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_scnvector4_to_nsvalue (SceneKit.SCNVector4 value) { using var rv = NSValue.FromVector (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
#if HAS_UIKIT
|
||||
static IntPtr xamarin_uiedgeinsets_to_nsvalue (UIKit.UIEdgeInsets value) { using var rv = NSValue.FromUIEdgeInsets (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_uioffset_to_nsvalue (UIKit.UIOffset value) { using var rv = NSValue.FromUIOffset (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
static IntPtr xamarin_nsdirectionaledgeinsets_to_nsvalue (UIKit.NSDirectionalEdgeInsets value) { using var rv = NSValue.FromDirectionalEdgeInsets (value); rv.DangerousRetain ().DangerousAutorelease (); return rv.Handle; }
|
||||
#endif
|
||||
|
||||
static System.SByte xamarin_nsnumber_to_sbyte (IntPtr value) { if (value == IntPtr.Zero) return default (System.SByte); return Runtime.GetNSObject<NSNumber> (value)?.SByteValue ?? default (System.SByte); }
|
||||
static System.Byte xamarin_nsnumber_to_byte (IntPtr value) { if (value == IntPtr.Zero) return default (System.Byte); return Runtime.GetNSObject<NSNumber> (value)?.ByteValue ?? default (System.Byte); }
|
||||
static System.Int16 xamarin_nsnumber_to_short (IntPtr value) { if (value == IntPtr.Zero) return default (System.Int16); return Runtime.GetNSObject<NSNumber> (value)?.Int16Value ?? default (System.Int16); }
|
||||
static System.UInt16 xamarin_nsnumber_to_ushort (IntPtr value) { if (value == IntPtr.Zero) return default (System.UInt16); return Runtime.GetNSObject<NSNumber> (value)?.UInt16Value ?? default (System.UInt16); }
|
||||
static System.Int32 xamarin_nsnumber_to_int (IntPtr value) { if (value == IntPtr.Zero) return default (System.Int32); return Runtime.GetNSObject<NSNumber> (value)?.Int32Value ?? default (System.Int32); }
|
||||
static System.UInt32 xamarin_nsnumber_to_uint (IntPtr value) { if (value == IntPtr.Zero) return default (System.UInt32); return Runtime.GetNSObject<NSNumber> (value)?.UInt32Value ?? default (System.UInt32); }
|
||||
static System.Int64 xamarin_nsnumber_to_long (IntPtr value) { if (value == IntPtr.Zero) return default (System.Int64); return Runtime.GetNSObject<NSNumber> (value)?.Int64Value ?? default (System.Int64); }
|
||||
static System.UInt64 xamarin_nsnumber_to_ulong (IntPtr value) { if (value == IntPtr.Zero) return default (System.UInt64); return Runtime.GetNSObject<NSNumber> (value)?.UInt64Value ?? default (System.UInt64); }
|
||||
static nint xamarin_nsnumber_to_nint (IntPtr value) { if (value == IntPtr.Zero) return default (nint); return Runtime.GetNSObject<NSNumber> (value)?.NIntValue ?? default (nint); }
|
||||
static nuint xamarin_nsnumber_to_nuint (IntPtr value) { if (value == IntPtr.Zero) return default (nuint); return Runtime.GetNSObject<NSNumber> (value)?.NUIntValue ?? default (nuint); }
|
||||
static System.Single xamarin_nsnumber_to_float (IntPtr value) { if (value == IntPtr.Zero) return default (System.Single); return Runtime.GetNSObject<NSNumber> (value)?.FloatValue ?? default (System.Single); }
|
||||
static System.Double xamarin_nsnumber_to_double (IntPtr value) { if (value == IntPtr.Zero) return default (System.Double); return Runtime.GetNSObject<NSNumber> (value)?.DoubleValue ?? default (System.Double); }
|
||||
static System.Boolean xamarin_nsnumber_to_bool (IntPtr value) { if (value == IntPtr.Zero) return default (System.Boolean); return Runtime.GetNSObject<NSNumber> (value)?.BoolValue ?? default (System.Boolean); }
|
||||
static nfloat xamarin_nsnumber_to_nfloat (IntPtr value)
|
||||
{
|
||||
if (value == IntPtr.Zero)
|
||||
return default (nfloat);
|
||||
var number = Runtime.GetNSObject<NSNumber> (value);
|
||||
if (number is null)
|
||||
return default (nfloat);
|
||||
if (IntPtr.Size == 4)
|
||||
return (nfloat) number.FloatValue;
|
||||
return (nfloat) number.DoubleValue;
|
||||
}
|
||||
|
||||
static System.SByte? xamarin_nsnumber_to_nullable_sbyte (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.SByteValue ?? null; }
|
||||
static System.Byte? xamarin_nsnumber_to_nullable_byte (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.ByteValue ?? null; }
|
||||
static System.Int16? xamarin_nsnumber_to_nullable_short (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.Int16Value ?? null; }
|
||||
static System.UInt16? xamarin_nsnumber_to_nullable_ushort (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.UInt16Value ?? null; }
|
||||
static System.Int32? xamarin_nsnumber_to_nullable_int (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.Int32Value ?? null; }
|
||||
static System.UInt32? xamarin_nsnumber_to_nullable_uint (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.UInt32Value ?? null; }
|
||||
static System.Int64? xamarin_nsnumber_to_nullable_long (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.Int64Value ?? null; }
|
||||
static System.UInt64? xamarin_nsnumber_to_nullable_ulong (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.UInt64Value ?? null; }
|
||||
static nint? xamarin_nsnumber_to_nullable_nint (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.NIntValue ?? null; }
|
||||
static nuint? xamarin_nsnumber_to_nullable_nuint (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.NUIntValue ?? null; }
|
||||
static System.Single? xamarin_nsnumber_to_nullable_float (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.FloatValue ?? null; }
|
||||
static System.Double? xamarin_nsnumber_to_nullable_double (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.DoubleValue ?? null; }
|
||||
static System.Boolean? xamarin_nsnumber_to_nullable_bool (IntPtr value) { return Runtime.GetNSObject<NSNumber> (value)?.BoolValue ?? null; }
|
||||
static nfloat? xamarin_nsnumber_to_nullable_nfloat (IntPtr value)
|
||||
{
|
||||
if (value == IntPtr.Zero)
|
||||
return null;
|
||||
var number = Runtime.GetNSObject<NSNumber> (value);
|
||||
if (number is null)
|
||||
return null;
|
||||
if (IntPtr.Size == 4)
|
||||
return (nfloat) number.FloatValue;
|
||||
return (nfloat) number.DoubleValue;
|
||||
}
|
||||
|
||||
static IntPtr xamarin_sbyte_to_nsnumber (System.SByte value) { return NSNumber.FromSByte (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_byte_to_nsnumber (System.Byte value) { return NSNumber.FromByte (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_short_to_nsnumber (System.Int16 value) { return NSNumber.FromInt16 (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_ushort_to_nsnumber (System.UInt16 value) { return NSNumber.FromUInt16 (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_int_to_nsnumber (System.Int32 value) { return NSNumber.FromInt32 (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_uint_to_nsnumber (System.UInt32 value) { return NSNumber.FromUInt32 (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_long_to_nsnumber (System.Int64 value) { return NSNumber.FromInt64 (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_ulong_to_nsnumber (System.UInt64 value) { return NSNumber.FromUInt64 (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nint_to_nsnumber (nint value) { return NSNumber.FromNInt (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nuint_to_nsnumber (nuint value) { return NSNumber.FromNUInt (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_float_to_nsnumber (System.Single value) { return NSNumber.FromFloat (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_double_to_nsnumber (System.Double value) { return NSNumber.FromDouble (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_bool_to_nsnumber (System.Boolean value) { return NSNumber.FromBoolean (value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nfloat_to_nsnumber (nfloat value)
|
||||
{
|
||||
if (IntPtr.Size == 4)
|
||||
return NSNumber.FromFloat ((float) value).DangerousRetain ().DangerousAutorelease ().Handle;
|
||||
return NSNumber.FromDouble ((double) value).DangerousRetain ().DangerousAutorelease ().Handle;
|
||||
}
|
||||
|
||||
static IntPtr xamarin_nullable_sbyte_to_nsnumber (System.SByte? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromSByte (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_byte_to_nsnumber (System.Byte? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromByte (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_short_to_nsnumber (System.Int16? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromInt16 (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_ushort_to_nsnumber (System.UInt16? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromUInt16 (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_int_to_nsnumber (System.Int32? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromInt32 (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_uint_to_nsnumber (System.UInt32? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromUInt32 (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_long_to_nsnumber (System.Int64? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromInt64 (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_ulong_to_nsnumber (System.UInt64? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromUInt64 (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_nint_to_nsnumber (nint? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromNInt (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_nuint_to_nsnumber (nuint? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromNUInt (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_float_to_nsnumber (System.Single? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromFloat (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_double_to_nsnumber (System.Double? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromDouble (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_bool_to_nsnumber (System.Boolean? value) { if (!value.HasValue) return IntPtr.Zero; return NSNumber.FromBoolean (value.Value).DangerousRetain ().DangerousAutorelease ().Handle; }
|
||||
static IntPtr xamarin_nullable_nfloat_to_nsnumber (nfloat? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
return IntPtr.Zero;
|
||||
if (IntPtr.Size == 4)
|
||||
return NSNumber.FromFloat ((float) value.Value).DangerousRetain ().DangerousAutorelease ().Handle;
|
||||
return NSNumber.FromDouble ((double) value.Value).DangerousRetain ().DangerousAutorelease ().Handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NET
|
|
@ -456,6 +456,33 @@ namespace ObjCRuntime {
|
|||
}
|
||||
#endif // NET
|
||||
|
||||
[EditorBrowsable (EditorBrowsableState.Never)]
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
static IntPtr CreateBlockForDelegate (Delegate @delegate, Delegate delegateProxyFieldValue, string /*?*/ signature)
|
||||
{
|
||||
if (@delegate is null)
|
||||
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (@delegate));
|
||||
|
||||
if (delegateProxyFieldValue is null)
|
||||
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (delegateProxyFieldValue));
|
||||
|
||||
// Note that we must create a heap-allocated block, so we
|
||||
// start off by creating a stack-allocated block, and then
|
||||
// call _Block_copy, which will create a heap-allocated block
|
||||
// with the proper reference count.
|
||||
using var block = new BlockLiteral ();
|
||||
if (signature is null) {
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
block.SetupBlock (delegateProxyFieldValue, @delegate);
|
||||
} else {
|
||||
throw ErrorHelper.CreateError (8026, $"BlockLiteral.GetBlockForDelegate with a null signature is not supported when the dynamic registrar has been linked away (delegate type: {@delegate.GetType ().FullName}).");
|
||||
}
|
||||
} else {
|
||||
block.SetupBlockImpl (delegateProxyFieldValue, @delegate, true, signature);
|
||||
}
|
||||
return _Block_copy (&block);
|
||||
}
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
internal static IntPtr GetBlockForDelegate (MethodInfo minfo, object @delegate, uint token_ref, string signature)
|
||||
{
|
||||
|
|
|
@ -254,8 +254,28 @@ namespace ObjCRuntime {
|
|||
|
||||
// Look for the type in the type map.
|
||||
var asm_name = type.Assembly.GetName ().Name!;
|
||||
var mod_token = type.Module.MetadataToken;
|
||||
var type_token = type.MetadataToken & ~0x02000000;
|
||||
int mod_token;
|
||||
int type_token;
|
||||
|
||||
if (Runtime.IsManagedStaticRegistrar) {
|
||||
#if NET
|
||||
mod_token = unchecked((int) Runtime.INVALID_TOKEN_REF);
|
||||
type_token = unchecked((int) RegistrarHelper.LookupRegisteredTypeId (type));
|
||||
|
||||
#if LOG_TYPELOAD
|
||||
Runtime.NSLog ($"FindClass ({type.FullName}, {is_custom_type}): type token: 0x{type_token.ToString ("x")}");
|
||||
#endif
|
||||
|
||||
if (type_token == -1)
|
||||
return IntPtr.Zero;
|
||||
#else
|
||||
throw ErrorHelper.CreateError (99, Xamarin.Bundler.Errors.MX0099 /* Internal error */, "The managed static registrar is only available for .NET");
|
||||
#endif // NET
|
||||
} else {
|
||||
mod_token = type.Module.MetadataToken;
|
||||
type_token = type.MetadataToken & ~0x02000000 /* TokenType.TypeDef */;
|
||||
}
|
||||
|
||||
for (int i = 0; i < map->map_count; i++) {
|
||||
var class_map = map->map [i];
|
||||
var token_reference = class_map.type_reference;
|
||||
|
@ -308,7 +328,7 @@ namespace ObjCRuntime {
|
|||
|
||||
// then the module token
|
||||
var module_token = entry.module_token;
|
||||
if (mod_token != module_token)
|
||||
if (unchecked((uint) mod_token) != module_token)
|
||||
return false;
|
||||
|
||||
// leave the assembly name for the end, since it's the most expensive comparison (string comparison)
|
||||
|
@ -410,7 +430,7 @@ namespace ObjCRuntime {
|
|||
|
||||
var assembly = ResolveAssembly (assembly_name);
|
||||
var module = ResolveModule (assembly, module_token);
|
||||
return ResolveToken (module, token);
|
||||
return ResolveToken (assembly, module, token);
|
||||
}
|
||||
|
||||
internal static Type? ResolveTypeTokenReference (uint token_reference)
|
||||
|
@ -453,21 +473,41 @@ namespace ObjCRuntime {
|
|||
var assembly = ResolveAssembly (assembly_name);
|
||||
var module = ResolveModule (assembly, 0x1);
|
||||
|
||||
return ResolveToken (module, token | implicit_token_type);
|
||||
return ResolveToken (assembly, module, token | implicit_token_type);
|
||||
}
|
||||
|
||||
static MemberInfo? ResolveToken (Module module, uint token)
|
||||
static MemberInfo? ResolveToken (Assembly assembly, Module? module, uint token)
|
||||
{
|
||||
// Finally resolve the token.
|
||||
var token_type = token & 0xFF000000;
|
||||
switch (token & 0xFF000000) {
|
||||
case 0x02000000: // TypeDef
|
||||
var type = module.ResolveType ((int) token);
|
||||
Type type;
|
||||
#if NET
|
||||
if (Runtime.IsManagedStaticRegistrar) {
|
||||
type = RegistrarHelper.LookupRegisteredType (assembly, token & 0x00FFFFFF);
|
||||
#if LOG_TYPELOAD
|
||||
Runtime.NSLog ($"ResolveToken (0x{token:X}) => Type: {type.FullName}");
|
||||
#endif
|
||||
return type;
|
||||
}
|
||||
#endif // NET
|
||||
if (module is null) {
|
||||
throw ErrorHelper.CreateError (8053, Errors.MX8053 /* Could not resolve the module in the assembly {0}. */, assembly.FullName);
|
||||
} else {
|
||||
type = module.ResolveType ((int) token);
|
||||
}
|
||||
#if LOG_TYPELOAD
|
||||
Runtime.NSLog ($"ResolveToken (0x{token:X}) => Type: {type.FullName}");
|
||||
#endif
|
||||
return type;
|
||||
case 0x06000000: // Method
|
||||
if (Runtime.IsManagedStaticRegistrar)
|
||||
throw ErrorHelper.CreateError (8054, Errors.MX8054 /* Can't resolve metadata tokens for methods when using the managed static registrar (token: 0x{0}). */, token.ToString ("x"));
|
||||
|
||||
if (module is null)
|
||||
throw ErrorHelper.CreateError (8053, Errors.MX8053 /* Could not resolve the module in the assembly {0}. */, assembly.FullName);
|
||||
|
||||
var method = module.ResolveMethod ((int) token);
|
||||
#if LOG_TYPELOAD
|
||||
Runtime.NSLog ($"ResolveToken (0x{token:X}) => Method: {method?.DeclaringType?.FullName}.{method?.Name}");
|
||||
|
@ -478,8 +518,11 @@ namespace ObjCRuntime {
|
|||
}
|
||||
}
|
||||
|
||||
static Module ResolveModule (Assembly assembly, uint token)
|
||||
static Module? ResolveModule (Assembly assembly, uint token)
|
||||
{
|
||||
if (token == Runtime.INVALID_TOKEN_REF)
|
||||
return null;
|
||||
|
||||
foreach (var mod in assembly.GetModules ()) {
|
||||
if (mod.MetadataToken != token)
|
||||
continue;
|
||||
|
@ -577,7 +620,20 @@ namespace ObjCRuntime {
|
|||
var asm_name = type.Module.Assembly.GetName ().Name!;
|
||||
|
||||
// First check if there's a full token reference to this type
|
||||
var token = GetFullTokenReference (asm_name, type.Module.MetadataToken, type.MetadataToken);
|
||||
uint token;
|
||||
if (Runtime.IsManagedStaticRegistrar) {
|
||||
#if NET
|
||||
var id = RegistrarHelper.LookupRegisteredTypeId (type);
|
||||
token = GetFullTokenReference (asm_name, unchecked((int) Runtime.INVALID_TOKEN_REF), 0x2000000 /* TokenType.TypeDef */ | unchecked((int) id));
|
||||
#if LOG_TYPELOAD
|
||||
Runtime.NSLog ($"GetTokenReference ({type}, {throw_exception}) id: {id} token: 0x{token.ToString ("x")}");
|
||||
#endif
|
||||
#else
|
||||
throw ErrorHelper.CreateError (99, Xamarin.Bundler.Errors.MX0099 /* Internal error */, "The managed static registrar is only available for .NET");
|
||||
#endif // NET
|
||||
} else {
|
||||
token = GetFullTokenReference (asm_name, type.Module.MetadataToken, type.MetadataToken);
|
||||
}
|
||||
if (token != uint.MaxValue)
|
||||
return token;
|
||||
|
||||
|
@ -626,7 +682,7 @@ namespace ObjCRuntime {
|
|||
if (token != metadata_token)
|
||||
continue;
|
||||
var mod_token = ftr.module_token;
|
||||
if (mod_token != module_token)
|
||||
if (unchecked((int) mod_token) != module_token)
|
||||
continue;
|
||||
var assembly_index = ftr.assembly_index;
|
||||
var assembly = map->assemblies [assembly_index];
|
||||
|
|
|
@ -219,12 +219,12 @@ namespace Registrar {
|
|||
return assembly.GetTypes ();
|
||||
}
|
||||
|
||||
protected override BindAsAttribute GetBindAsAttribute (PropertyInfo property)
|
||||
public override BindAsAttribute GetBindAsAttribute (PropertyInfo property)
|
||||
{
|
||||
return property?.GetCustomAttribute<BindAsAttribute> (false);
|
||||
}
|
||||
|
||||
protected override BindAsAttribute GetBindAsAttribute (MethodBase method, int parameter_index)
|
||||
public override BindAsAttribute GetBindAsAttribute (MethodBase method, int parameter_index)
|
||||
{
|
||||
ICustomAttributeProvider provider;
|
||||
|
||||
|
@ -343,7 +343,7 @@ namespace Registrar {
|
|||
return type.MakeByRefType ();
|
||||
}
|
||||
|
||||
protected override CategoryAttribute GetCategoryAttribute (Type type)
|
||||
public override CategoryAttribute GetCategoryAttribute (Type type)
|
||||
{
|
||||
return SharedDynamic.GetOneAttribute<CategoryAttribute> (type);
|
||||
}
|
||||
|
@ -374,7 +374,7 @@ namespace Registrar {
|
|||
return ((MethodInfo) method).GetBaseDefinition ();
|
||||
}
|
||||
|
||||
protected override Type GetElementType (Type type)
|
||||
public override Type GetElementType (Type type)
|
||||
{
|
||||
return type.GetElementType ();
|
||||
}
|
||||
|
@ -460,7 +460,7 @@ namespace Registrar {
|
|||
return type.FullName;
|
||||
}
|
||||
|
||||
protected override bool VerifyIsConstrainedToNSObject (Type type, out Type constrained_type)
|
||||
public override bool VerifyIsConstrainedToNSObject (Type type, out Type constrained_type)
|
||||
{
|
||||
constrained_type = null;
|
||||
|
||||
|
@ -523,7 +523,7 @@ namespace Registrar {
|
|||
return type.AssemblyQualifiedName;
|
||||
}
|
||||
|
||||
protected override bool HasReleaseAttribute (MethodBase method)
|
||||
public override bool HasReleaseAttribute (MethodBase method)
|
||||
{
|
||||
var mi = method as MethodInfo;
|
||||
if (mi is null)
|
||||
|
@ -539,7 +539,7 @@ namespace Registrar {
|
|||
return mi.IsDefined (typeof (System.Runtime.CompilerServices.ExtensionAttribute), false);
|
||||
}
|
||||
|
||||
protected override bool HasThisAttribute (MethodBase method)
|
||||
public override bool HasThisAttribute (MethodBase method)
|
||||
{
|
||||
return HasThisAttributeImpl (method);
|
||||
}
|
||||
|
@ -554,7 +554,7 @@ namespace Registrar {
|
|||
return type.IsDefined (typeof (ModelAttribute), false);
|
||||
}
|
||||
|
||||
protected override bool IsArray (Type type, out int rank)
|
||||
public override bool IsArray (Type type, out int rank)
|
||||
{
|
||||
if (!type.IsArray) {
|
||||
rank = 0;
|
||||
|
@ -594,7 +594,7 @@ namespace Registrar {
|
|||
return type.IsSubclassOf (typeof (System.Delegate));
|
||||
}
|
||||
|
||||
protected override bool IsNullable (Type type)
|
||||
public override bool IsNullable (Type type)
|
||||
{
|
||||
if (!type.IsGenericType)
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// IManagedRegistrar.cs
|
||||
//
|
||||
// Authors:
|
||||
// Rolf Bjarne Kvinge
|
||||
//
|
||||
// Copyright 2023 Microsoft Corp
|
||||
|
||||
|
||||
#if NET
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ObjCRuntime {
|
||||
// The managed static registrar will generate/inject a type that implements this method
|
||||
// in every assembly it processes. At runtime we'll instantiate a singleton instance
|
||||
// of this type.
|
||||
// The managed static registrar will make this interface public when needed.
|
||||
interface IManagedRegistrar {
|
||||
// Find a function pointer for a given [UnmanagedCallersOnly] method.
|
||||
// The entryPoint parameter is the EntryPoint value in the attribute, but it's not used for lookup (only tracing/logging/error messages).
|
||||
// The 'id' is instead used - the values and the lookup tables are generated and injected by the managed static registrar at build time.
|
||||
IntPtr LookupUnmanagedFunction (string? entryPoint, int id);
|
||||
// Find a type given an id generated by the managed static registrar.
|
||||
// This method is the mirror method of LookupTypeId.
|
||||
RuntimeTypeHandle LookupType (uint id);
|
||||
// Find an id generated by the managed static registrar given an id.
|
||||
// This method is the mirror method of LookupType.
|
||||
uint LookupTypeId (RuntimeTypeHandle handle);
|
||||
// Called by the runtime when looking up a wrapper type given an interface type.
|
||||
// This method will be called once per assembly, and the implementation has to
|
||||
// add all the interface -> wrapper type mappings to the dictionary.
|
||||
void RegisterWrapperTypes (Dictionary<RuntimeTypeHandle, RuntimeTypeHandle> type);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NET
|
|
@ -980,10 +980,7 @@ namespace Registrar {
|
|||
|
||||
public bool IsPropertyAccessor {
|
||||
get {
|
||||
if (Method is null)
|
||||
return false;
|
||||
|
||||
return Method.IsSpecialName && (Method.Name.StartsWith ("get_", StringComparison.Ordinal) || Method.Name.StartsWith ("set_", StringComparison.Ordinal));
|
||||
return Registrar.IsPropertyAccessor (Method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1044,6 +1041,7 @@ namespace Registrar {
|
|||
public byte Alignment;
|
||||
#else
|
||||
public bool IsPrivate;
|
||||
public TProperty Property;
|
||||
#endif
|
||||
public string FieldType;
|
||||
public bool IsProperty;
|
||||
|
@ -1094,9 +1092,9 @@ namespace Registrar {
|
|||
protected abstract bool IsStatic (TField field);
|
||||
protected abstract bool IsStatic (TMethod method);
|
||||
protected abstract TType MakeByRef (TType type);
|
||||
protected abstract bool HasThisAttribute (TMethod method);
|
||||
public abstract bool HasThisAttribute (TMethod method);
|
||||
protected abstract bool IsConstructor (TMethod method);
|
||||
protected abstract TType GetElementType (TType type);
|
||||
public abstract TType GetElementType (TType type);
|
||||
protected abstract TType GetReturnType (TMethod method);
|
||||
protected abstract void GetNamespaceAndName (TType type, out string @namespace, out string name);
|
||||
protected abstract bool TryGetAttribute (TType type, string attributeNamespace, string attributeType, out object attribute);
|
||||
|
@ -1104,23 +1102,23 @@ namespace Registrar {
|
|||
protected abstract ExportAttribute GetExportAttribute (TMethod method); // Return null if no attribute is found. Must check the base method (i.e. if method is overriding a method in a base class, must check the overridden method for the attribute).
|
||||
protected abstract Dictionary<TMethod, List<TMethod>> PrepareMethodMapping (TType type);
|
||||
public abstract RegisterAttribute GetRegisterAttribute (TType type); // Return null if no attribute is found. Do not consider base types.
|
||||
protected abstract CategoryAttribute GetCategoryAttribute (TType type); // Return null if no attribute is found. Do not consider base types.
|
||||
public abstract CategoryAttribute GetCategoryAttribute (TType type); // Return null if no attribute is found. Do not consider base types.
|
||||
protected abstract ConnectAttribute GetConnectAttribute (TProperty property); // Return null if no attribute is found. Do not consider inherited properties.
|
||||
public abstract ProtocolAttribute GetProtocolAttribute (TType type); // Return null if no attribute is found. Do not consider base types.
|
||||
protected abstract IEnumerable<ProtocolMemberAttribute> GetProtocolMemberAttributes (TType type); // Return null if no attributes found. Do not consider base types.
|
||||
protected virtual Version GetSdkIntroducedVersion (TType obj, out string message) { message = null; return null; } // returns the sdk version when the type was introduced for the current platform (null if all supported versions)
|
||||
protected abstract Version GetSDKVersion ();
|
||||
protected abstract TType GetProtocolAttributeWrapperType (TType type); // Return null if no attribute is found. Do not consider base types.
|
||||
protected abstract BindAsAttribute GetBindAsAttribute (TMethod method, int parameter_index); // If parameter_index = -1 then get the attribute for the return type. Return null if no attribute is found. Must consider base method.
|
||||
protected abstract BindAsAttribute GetBindAsAttribute (TProperty property);
|
||||
public abstract BindAsAttribute GetBindAsAttribute (TMethod method, int parameter_index); // If parameter_index = -1 then get the attribute for the return type. Return null if no attribute is found. Must consider base method.
|
||||
public abstract BindAsAttribute GetBindAsAttribute (TProperty property);
|
||||
protected abstract IList<AdoptsAttribute> GetAdoptsAttributes (TType type);
|
||||
public abstract TType GetNullableType (TType type); // For T? returns T. For T returns null.
|
||||
protected abstract bool HasReleaseAttribute (TMethod method); // Returns true of the method's return type/value has a [Release] attribute.
|
||||
public abstract bool HasReleaseAttribute (TMethod method); // Returns true of the method's return type/value has a [Release] attribute.
|
||||
protected abstract bool IsINativeObject (TType type);
|
||||
protected abstract bool IsValueType (TType type);
|
||||
protected abstract bool IsArray (TType type, out int rank);
|
||||
public abstract bool IsArray (TType type, out int rank);
|
||||
protected abstract bool IsEnum (TType type, out bool isNativeEnum);
|
||||
protected abstract bool IsNullable (TType type);
|
||||
public abstract bool IsNullable (TType type);
|
||||
protected abstract bool IsDelegate (TType type);
|
||||
protected abstract bool IsGenericType (TType type);
|
||||
protected abstract bool IsGenericMethod (TMethod method);
|
||||
|
@ -1128,7 +1126,7 @@ namespace Registrar {
|
|||
protected abstract bool IsAbstract (TType type);
|
||||
protected abstract bool IsPointer (TType type);
|
||||
protected abstract TType GetGenericTypeDefinition (TType type);
|
||||
protected abstract bool VerifyIsConstrainedToNSObject (TType type, out TType constrained_type);
|
||||
public abstract bool VerifyIsConstrainedToNSObject (TType type, out TType constrained_type);
|
||||
protected abstract TType GetEnumUnderlyingType (TType type);
|
||||
protected abstract IEnumerable<TField> GetFields (TType type); // Must return all instance fields. May return static fields (they are filtered out automatically).
|
||||
protected abstract TType GetFieldType (TField field);
|
||||
|
@ -1160,7 +1158,33 @@ namespace Registrar {
|
|||
{
|
||||
}
|
||||
|
||||
protected bool IsArray (TType type)
|
||||
public static bool IsPropertyAccessor (TMethod method)
|
||||
{
|
||||
if (method is null)
|
||||
return false;
|
||||
|
||||
if (!method.IsSpecialName)
|
||||
return false;
|
||||
|
||||
var name = method.Name;
|
||||
if (!name.StartsWith ("get_", StringComparison.Ordinal) && !name.StartsWith ("set_", StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsPropertyAccessor (TMethod method, out TProperty property)
|
||||
{
|
||||
property = null;
|
||||
|
||||
if (!IsPropertyAccessor (method))
|
||||
return false;
|
||||
|
||||
property = FindProperty (method.DeclaringType, method.Name.Substring (4));
|
||||
return property is not null;
|
||||
}
|
||||
|
||||
public bool IsArray (TType type)
|
||||
{
|
||||
int rank;
|
||||
return IsArray (type, out rank);
|
||||
|
@ -2243,6 +2267,9 @@ namespace Registrar {
|
|||
FieldType = "@",
|
||||
IsProperty = true,
|
||||
IsStatic = IsStatic (property),
|
||||
#if MTOUCH || MMP || BUNDLER
|
||||
Property = property,
|
||||
#endif
|
||||
}, ref exceptions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,401 @@
|
|||
//
|
||||
// RegistrarHelper.cs: Helper code for the managed static registra.
|
||||
//
|
||||
// Authors:
|
||||
// Rolf Bjarne Kvinge
|
||||
//
|
||||
// Copyright 2023 Microsoft Corp
|
||||
|
||||
|
||||
// #define TRACE
|
||||
|
||||
#if NET
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using CoreFoundation;
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
|
||||
using Registrar;
|
||||
|
||||
using Xamarin.Bundler;
|
||||
|
||||
namespace ObjCRuntime {
|
||||
// This class contains helper methods for the managed static registrar.
|
||||
// The managed static registrar will make it public when needed.
|
||||
static class RegistrarHelper {
|
||||
class MapInfo {
|
||||
public IManagedRegistrar Registrar;
|
||||
public bool RegisteredWrapperTypes;
|
||||
|
||||
public MapInfo (IManagedRegistrar registrar)
|
||||
{
|
||||
Registrar = registrar;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore CS8618 for these two variables:
|
||||
// Non-nullable variable must contain a non-null value when exiting constructor.
|
||||
// Because we won't use a static constructor to initialize them, instead we're using a module initializer,
|
||||
// it's safe to ignore this warning.
|
||||
#pragma warning disable 8618
|
||||
static Dictionary<string, MapInfo> assembly_map;
|
||||
static Dictionary<RuntimeTypeHandle, RuntimeTypeHandle> wrapper_types;
|
||||
static StringEqualityComparer StringEqualityComparer;
|
||||
static RuntimeTypeHandleEqualityComparer RuntimeTypeHandleEqualityComparer;
|
||||
#pragma warning restore 8618
|
||||
|
||||
[ModuleInitializer]
|
||||
internal static void Initialize ()
|
||||
{
|
||||
StringEqualityComparer = new StringEqualityComparer ();
|
||||
RuntimeTypeHandleEqualityComparer = new RuntimeTypeHandleEqualityComparer ();
|
||||
assembly_map = new Dictionary<string, MapInfo> (StringEqualityComparer);
|
||||
wrapper_types = new Dictionary<RuntimeTypeHandle, RuntimeTypeHandle> (RuntimeTypeHandleEqualityComparer);
|
||||
}
|
||||
|
||||
static NativeHandle CreateCFArray (params string[]? values)
|
||||
{
|
||||
if (values is null)
|
||||
return NativeHandle.Zero;
|
||||
return CFArray.Create (values);
|
||||
}
|
||||
|
||||
unsafe static IntPtr GetBlockPointer (BlockLiteral block)
|
||||
{
|
||||
var rv = BlockLiteral._Block_copy (&block);
|
||||
block.Dispose ();
|
||||
return rv;
|
||||
}
|
||||
|
||||
static IntPtr GetBlockForDelegate (object @delegate, RuntimeMethodHandle method_handle)
|
||||
{
|
||||
var method = (MethodInfo) MethodBase.GetMethodFromHandle (method_handle)!;
|
||||
return BlockLiteral.GetBlockForDelegate (method, @delegate, Runtime.INVALID_TOKEN_REF, null);
|
||||
}
|
||||
|
||||
|
||||
static MapInfo GetMapEntry (Assembly assembly)
|
||||
{
|
||||
return GetMapEntry (assembly.GetName ().Name!);
|
||||
}
|
||||
|
||||
static MapInfo GetMapEntry (IntPtr assembly)
|
||||
{
|
||||
return GetMapEntry (Marshal.PtrToStringAuto (assembly)!);
|
||||
}
|
||||
|
||||
static MapInfo GetMapEntry (string assemblyName)
|
||||
{
|
||||
if (TryGetMapEntry (assemblyName, out var rv))
|
||||
return rv;
|
||||
throw ErrorHelper.CreateError (8055, Errors.MX8055 /* Could not find the type 'ObjCRuntime.__Registrar__' in the assembly '{0}' */, assemblyName);
|
||||
}
|
||||
|
||||
static bool TryGetMapEntry (string assemblyName, [NotNullWhen (true)] out MapInfo? entry)
|
||||
{
|
||||
entry = null;
|
||||
|
||||
lock (assembly_map) {
|
||||
return assembly_map.TryGetValue (assemblyName, out entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void Register (IManagedRegistrar registrar)
|
||||
{
|
||||
var assembly = registrar.GetType ().Assembly;
|
||||
var assemblyName = assembly.GetName ().Name!;
|
||||
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"RegistrarHelper.Register ('{assemblyName}', '{registrar.GetType ().AssemblyQualifiedName}')");
|
||||
#endif
|
||||
|
||||
lock (assembly_map) {
|
||||
assembly_map.Add (assemblyName, new MapInfo (registrar));
|
||||
}
|
||||
}
|
||||
|
||||
internal static Type? FindProtocolWrapperType (Type type)
|
||||
{
|
||||
var typeHandle = type.TypeHandle;
|
||||
|
||||
lock (assembly_map) {
|
||||
// First check if the type is already in our dictionary.
|
||||
if (wrapper_types.TryGetValue (typeHandle, out var wrapperType))
|
||||
return Type.GetTypeFromHandle (wrapperType);
|
||||
|
||||
// Not in our dictionary, get the map entry to see if we've already
|
||||
// called RegisterWrapperTypes for this assembly,
|
||||
var entry = GetMapEntry (type.Assembly);
|
||||
if (!entry.RegisteredWrapperTypes) {
|
||||
entry.Registrar.RegisterWrapperTypes (wrapper_types);
|
||||
entry.RegisteredWrapperTypes = true;
|
||||
}
|
||||
|
||||
// Return whatever's in the dictionary now.
|
||||
if (wrapper_types.TryGetValue (typeHandle, out wrapperType))
|
||||
return Type.GetTypeFromHandle (wrapperType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
[ThreadStatic]
|
||||
static Stopwatch? lookupWatch;
|
||||
#endif
|
||||
|
||||
internal static IntPtr LookupUnmanagedFunction (IntPtr assembly, string? symbol, int id)
|
||||
{
|
||||
IntPtr rv;
|
||||
|
||||
#if TRACE
|
||||
if (lookupWatch is null)
|
||||
lookupWatch = new Stopwatch ();
|
||||
|
||||
lookupWatch.Start ();
|
||||
Console.WriteLine ("LookupUnmanagedFunction (0x{0} = {1}, {2}, {3})", assembly.ToString ("x"), Marshal.PtrToStringAuto (assembly), symbol, id);
|
||||
#endif
|
||||
|
||||
if (id == -1) {
|
||||
rv = IntPtr.Zero;
|
||||
} else {
|
||||
rv = LookupUnmanagedFunctionInAssembly (assembly, symbol, id);
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
lookupWatch.Stop ();
|
||||
|
||||
Console.WriteLine ("LookupUnmanagedFunction (0x{0} = {1}, {2}, {3}) => 0x{4} ElapsedMilliseconds: {5}", assembly.ToString ("x"), Marshal.PtrToStringAuto (assembly), symbol, id, rv.ToString ("x"), lookupWatch.ElapsedMilliseconds);
|
||||
#endif
|
||||
|
||||
if (rv != IntPtr.Zero)
|
||||
return rv;
|
||||
|
||||
throw ErrorHelper.CreateError (8001, "Unable to find the managed function with id {0} ({1})", id, symbol);
|
||||
}
|
||||
|
||||
static IntPtr LookupUnmanagedFunctionInAssembly (IntPtr assembly_name, string? symbol, int id)
|
||||
{
|
||||
var entry = GetMapEntry (assembly_name);
|
||||
return entry.Registrar.LookupUnmanagedFunction (symbol, id);
|
||||
}
|
||||
|
||||
internal static Type LookupRegisteredType (Assembly assembly, uint id)
|
||||
{
|
||||
var entry = GetMapEntry (assembly);
|
||||
var handle = entry.Registrar.LookupType (id);
|
||||
return Type.GetTypeFromHandle (handle)!;
|
||||
}
|
||||
|
||||
internal static uint LookupRegisteredTypeId (Type type)
|
||||
{
|
||||
if (!TryGetMapEntry (type.Assembly.GetName ().Name!, out var entry))
|
||||
return Runtime.INVALID_TOKEN_REF;
|
||||
return entry.Registrar.LookupTypeId (type.TypeHandle);
|
||||
}
|
||||
|
||||
// helper functions for converting between native and managed objects
|
||||
static NativeHandle ManagedArrayToNSArray (object array, bool retain)
|
||||
{
|
||||
if (array is null)
|
||||
return NativeHandle.Zero;
|
||||
|
||||
NSObject rv;
|
||||
if (array is NSObject[] nsobjs) {
|
||||
rv = NSArray.FromNSObjects (nsobjs);
|
||||
} else if (array is INativeObject[] inativeobjs) {
|
||||
rv = NSArray.FromNSObjects (inativeobjs);
|
||||
} else {
|
||||
throw new InvalidOperationException ($"Can't convert {array.GetType ()} to an NSArray."); // FIXME: better error
|
||||
}
|
||||
|
||||
if (retain)
|
||||
return Runtime.RetainNSObject (rv);
|
||||
return Runtime.RetainAndAutoreleaseNSObject (rv);
|
||||
}
|
||||
|
||||
unsafe static void NSArray_string_native_to_managed (IntPtr* ptr, ref string[]? value, ref string[]? copy)
|
||||
{
|
||||
if (ptr is not null) {
|
||||
value = NSArray.StringArrayFromHandle (*ptr);
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
copy = value;
|
||||
}
|
||||
|
||||
unsafe static void NSArray_string_managed_to_native (IntPtr* ptr, string[] value, string[] copy, bool isOut)
|
||||
{
|
||||
if (ptr is null)
|
||||
return;
|
||||
|
||||
// Note that we won't notice if individual array elements change, only if the array itself changes
|
||||
if (!isOut && (object) value == (object) copy) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"NSArray_string_managed_to_native (0x{(*ptr).ToString ("x")}, equal)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (value is null) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"NSArray_string_managed_to_native (0x{(*ptr).ToString ("x")}, null, !null)");
|
||||
#endif
|
||||
*ptr = IntPtr.Zero;
|
||||
return;
|
||||
}
|
||||
IntPtr rv = Runtime.RetainAndAutoreleaseNSObject (NSArray.FromStrings (value));
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"NSArray_string_managed_to_native (0x{(*ptr).ToString ("x")}, value != copy: {value?.Length} != {copy?.Length}): 0x{rv.ToString ("x")} => {value?.GetType ()}");
|
||||
#endif
|
||||
*ptr = rv;
|
||||
}
|
||||
|
||||
unsafe static void NSArray_native_to_managed<T> (IntPtr* ptr, ref T[]? value, ref T[]? copy) where T: class, INativeObject
|
||||
{
|
||||
if (ptr is not null) {
|
||||
value = NSArray.ArrayFromHandle<T> (*ptr);
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
copy = value;
|
||||
}
|
||||
|
||||
unsafe static void NSArray_managed_to_native<T> (IntPtr* ptr, T[] value, T[] copy, bool isOut) where T: class, INativeObject
|
||||
{
|
||||
if (ptr is null) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"NSArray_managed_to_native (NULL, ?, ?)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
// Note that we won't notice if individual array elements change, only if the array itself changes
|
||||
if (!isOut && (object) value == (object) copy) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"NSArray_managed_to_native (0x{(*ptr).ToString ("x")}, equal)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (value is null) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"NSArray_managed_to_native (0x{(*ptr).ToString ("x")}, null, !null)");
|
||||
#endif
|
||||
*ptr = IntPtr.Zero;
|
||||
return;
|
||||
}
|
||||
IntPtr rv = Runtime.RetainAndAutoreleaseNSObject (NSArray.FromNSObjects<T> (value));
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"NSArray_managed_to_native (0x{(*ptr).ToString ("x")}, value != copy: {value?.Length} != {copy?.Length}): 0x{rv.ToString ("x")} => {value?.GetType ()}");
|
||||
#endif
|
||||
*ptr = rv;
|
||||
}
|
||||
|
||||
unsafe static void NSObject_native_to_managed<T> (IntPtr* ptr, ref T? value, ref T? copy) where T: NSObject
|
||||
{
|
||||
if (ptr is not null) {
|
||||
value = Runtime.GetNSObject<T> (*ptr, owns: false);
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
copy = value;
|
||||
}
|
||||
|
||||
unsafe static void NSObject_managed_to_native (IntPtr* ptr, NSObject value, NSObject copy, bool isOut)
|
||||
{
|
||||
if (ptr is null) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"NSObject_managed_to_native (NULL, ?, ?)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (!isOut && (object) value == (object) copy) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"NSObject_managed_to_native (0x{(*ptr).ToString ("x")}, equal)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
IntPtr rv = Runtime.RetainAndAutoreleaseNSObject (value);
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"NSObject_managed_to_native (0x{(*ptr).ToString ("x")}, ? != ?): 0x{rv.ToString ("x")} => {value?.GetType ()}");
|
||||
#endif
|
||||
*ptr = rv;
|
||||
}
|
||||
|
||||
unsafe static void string_native_to_managed (NativeHandle *ptr, ref string? value, ref string? copy)
|
||||
{
|
||||
if (ptr is not null) {
|
||||
value = CFString.FromHandle (*ptr);
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
copy = value;
|
||||
}
|
||||
|
||||
unsafe static void string_managed_to_native (NativeHandle *ptr, string value, string copy, bool isOut)
|
||||
{
|
||||
if (ptr is null) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"string_managed_to_native (NULL, ?, ?)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (!isOut && (object) value == (object) copy) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"string_managed_to_native (0x{(*ptr).ToString ()}, equal)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
var rv = CFString.CreateNative (value);
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"string_managed_to_native (0x{(*ptr).ToString ()}, ? != ?): 0x{rv.ToString ()} => {value}");
|
||||
#endif
|
||||
*ptr = rv;
|
||||
}
|
||||
|
||||
unsafe static void INativeObject_native_to_managed<T> (IntPtr* ptr, ref T? value, ref T? copy, RuntimeTypeHandle implementationType) where T: class, INativeObject
|
||||
{
|
||||
if (ptr is not null) {
|
||||
value = Runtime.GetINativeObject<T> (*ptr, implementation: Type.GetTypeFromHandle (implementationType), forced_type: false, owns: false);
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
copy = value;
|
||||
}
|
||||
|
||||
unsafe static void INativeObject_managed_to_native (IntPtr *ptr, INativeObject value, INativeObject copy, bool isOut)
|
||||
{
|
||||
if (ptr is null) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"INativeObject_managed_to_native (NULL, ?, ?)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (!isOut && (object) value == (object) copy) {
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"INativeObject_managed_to_native (0x{(*ptr).ToString ("x")}, equal)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
IntPtr rv = value.GetHandle ();
|
||||
#if TRACE
|
||||
Runtime.NSLog ($"INativeObject_managed_to_native (0x{(*ptr).ToString ("x")}, ? != ?): 0x{rv.ToString ("x")} => {value?.GetType ()}");
|
||||
#endif
|
||||
*ptr = rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // NET
|
|
@ -27,6 +27,10 @@ using Registrar;
|
|||
using AppKit;
|
||||
#endif
|
||||
|
||||
#if !NET
|
||||
using NativeHandle = System.IntPtr;
|
||||
#endif
|
||||
|
||||
namespace ObjCRuntime {
|
||||
|
||||
public partial class Runtime {
|
||||
|
@ -149,7 +153,7 @@ namespace ObjCRuntime {
|
|||
[Flags]
|
||||
internal enum InitializationFlags : int {
|
||||
IsPartialStaticRegistrar = 0x01,
|
||||
/* unused = 0x02,*/
|
||||
IsManagedStaticRegistrar = 0x02,
|
||||
/* unused = 0x04,*/
|
||||
/* unused = 0x08,*/
|
||||
IsSimulator = 0x10,
|
||||
|
@ -220,6 +224,14 @@ namespace ObjCRuntime {
|
|||
}
|
||||
#endif
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
internal unsafe static bool IsManagedStaticRegistrar {
|
||||
get {
|
||||
// The linker may turn calls to this property into a constant
|
||||
return (options->Flags.HasFlag (InitializationFlags.IsManagedStaticRegistrar));
|
||||
}
|
||||
}
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
public static bool DynamicRegistrationSupported {
|
||||
get {
|
||||
|
@ -486,6 +498,11 @@ namespace ObjCRuntime {
|
|||
return AllocGCHandle (ex);
|
||||
}
|
||||
|
||||
static Exception CreateRuntimeException (int code, string message)
|
||||
{
|
||||
return ErrorHelper.CreateError (code, message);
|
||||
}
|
||||
|
||||
static IntPtr CreateRuntimeException (int code, IntPtr message)
|
||||
{
|
||||
var ex = ErrorHelper.CreateError (code, Marshal.PtrToStringAuto (message)!);
|
||||
|
@ -735,7 +752,14 @@ namespace ObjCRuntime {
|
|||
Registrar.GetMethodDescription (Class.Lookup (cls), sel, is_static != 0, desc);
|
||||
}
|
||||
|
||||
static sbyte HasNSObject (IntPtr ptr)
|
||||
#if NET
|
||||
internal static bool HasNSObject (NativeHandle ptr)
|
||||
{
|
||||
return TryGetNSObject (ptr, evenInFinalizerQueue: false) is not null;
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static sbyte HasNSObject (IntPtr ptr)
|
||||
{
|
||||
var rv = TryGetNSObject (ptr, evenInFinalizerQueue: false) is not null;
|
||||
return (sbyte) (rv ? 1 : 0);
|
||||
|
@ -767,10 +791,15 @@ namespace ObjCRuntime {
|
|||
return IntPtr.Zero;
|
||||
|
||||
var nsobj = GetGCHandleTarget (obj) as NSObject;
|
||||
if (nsobj is null)
|
||||
throw ErrorHelper.CreateError (8023, $"An instance object is required to construct a closed generic method for the open generic method: {method.DeclaringType!.FullName}.{method.Name} (token reference: 0x{token_ref:X}). {Constants.PleaseFileBugReport}");
|
||||
return AllocGCHandle (FindClosedMethodForObject (nsobj, method));
|
||||
}
|
||||
|
||||
return AllocGCHandle (FindClosedMethod (nsobj.GetType (), method));
|
||||
static MethodInfo FindClosedMethodForObject (NSObject? nsobj, MethodBase method)
|
||||
{
|
||||
if (nsobj is null)
|
||||
throw ErrorHelper.CreateError (8023, $"An instance object is required to construct a closed generic method for the open generic method: {method.DeclaringType!.FullName}.{method.Name}. {Constants.PleaseFileBugReport}");
|
||||
|
||||
return FindClosedMethod (nsobj.GetType (), method);
|
||||
}
|
||||
|
||||
static IntPtr TryGetOrConstructNSObjectWrapped (IntPtr ptr)
|
||||
|
@ -1190,7 +1219,7 @@ namespace ObjCRuntime {
|
|||
Ignore,
|
||||
}
|
||||
|
||||
static void MissingCtor (IntPtr ptr, IntPtr klass, Type type, MissingCtorResolution resolution)
|
||||
static void MissingCtor (IntPtr ptr, IntPtr klass, Type type, MissingCtorResolution resolution, IntPtr sel, RuntimeMethodHandle method_handle)
|
||||
{
|
||||
if (resolution == MissingCtorResolution.Ignore)
|
||||
return;
|
||||
|
@ -1225,6 +1254,37 @@ namespace ObjCRuntime {
|
|||
}
|
||||
|
||||
msg.Append (").");
|
||||
if (sel != IntPtr.Zero || method_handle.Value != IntPtr.Zero) {
|
||||
msg.AppendLine ();
|
||||
msg.AppendLine ("Additional information:");
|
||||
if (sel != IntPtr.Zero)
|
||||
msg.Append ("\tSelector: ").Append (Selector.GetName (sel)).AppendLine ();
|
||||
if (method_handle.Value != IntPtr.Zero) {
|
||||
try {
|
||||
var method = MethodBase.GetMethodFromHandle (method_handle);
|
||||
msg.Append ($"\tMethod: ");
|
||||
if (method is not null) {
|
||||
// there's no good built-in function to format a MethodInfo :/
|
||||
msg.Append (method.DeclaringType?.FullName ?? string.Empty);
|
||||
msg.Append (".");
|
||||
msg.Append (method.Name);
|
||||
msg.Append ("(");
|
||||
var parameters = method.GetParameters ();
|
||||
for (var i = 0; i < parameters.Length; i++) {
|
||||
if (i > 0)
|
||||
msg.Append (", ");
|
||||
msg.Append (parameters [i].ParameterType.FullName);
|
||||
}
|
||||
msg.Append (")");
|
||||
} else {
|
||||
msg.Append ($"Unable to resolve RuntimeMethodHandle 0x{method_handle.Value.ToString ("x")}");
|
||||
}
|
||||
msg.AppendLine ();
|
||||
} catch (Exception ex) {
|
||||
msg.Append ($"\tMethod: Unable to resolve RuntimeMethodHandle 0x{method_handle.Value.ToString ("x")}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
throw ErrorHelper.CreateError (8027, msg.ToString ());
|
||||
}
|
||||
|
||||
|
@ -1247,6 +1307,13 @@ namespace ObjCRuntime {
|
|||
// The generic argument T is only used to cast the return value.
|
||||
// The 'selector' and 'method' arguments are only used in error messages.
|
||||
static T? ConstructNSObject<T> (IntPtr ptr, Type type, MissingCtorResolution missingCtorResolution) where T : class, INativeObject
|
||||
{
|
||||
return ConstructNSObject<T> (ptr, type, missingCtorResolution, IntPtr.Zero, default (RuntimeMethodHandle));
|
||||
}
|
||||
|
||||
// The generic argument T is only used to cast the return value.
|
||||
// The 'selector' and 'method' arguments are only used in error messages.
|
||||
static T? ConstructNSObject<T> (IntPtr ptr, Type type, MissingCtorResolution missingCtorResolution, IntPtr sel, RuntimeMethodHandle method_handle) where T : class, INativeObject
|
||||
{
|
||||
if (type is null)
|
||||
throw new ArgumentNullException (nameof (type));
|
||||
|
@ -1254,7 +1321,7 @@ namespace ObjCRuntime {
|
|||
var ctor = GetIntPtrConstructor (type);
|
||||
|
||||
if (ctor is null) {
|
||||
MissingCtor (ptr, IntPtr.Zero, type, missingCtorResolution);
|
||||
MissingCtor (ptr, IntPtr.Zero, type, missingCtorResolution, sel, method_handle);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1273,7 +1340,7 @@ namespace ObjCRuntime {
|
|||
}
|
||||
|
||||
// The generic argument T is only used to cast the return value.
|
||||
static T? ConstructINativeObject<T> (IntPtr ptr, bool owns, Type type, MissingCtorResolution missingCtorResolution) where T : class, INativeObject
|
||||
static T? ConstructINativeObject<T> (IntPtr ptr, bool owns, Type type, MissingCtorResolution missingCtorResolution, IntPtr sel, RuntimeMethodHandle method_handle) where T : class, INativeObject
|
||||
{
|
||||
if (type is null)
|
||||
throw new ArgumentNullException (nameof (type));
|
||||
|
@ -1284,7 +1351,7 @@ namespace ObjCRuntime {
|
|||
var ctor = GetIntPtr_BoolConstructor (type);
|
||||
|
||||
if (ctor is null) {
|
||||
MissingCtor (ptr, IntPtr.Zero, type, missingCtorResolution);
|
||||
MissingCtor (ptr, IntPtr.Zero, type, missingCtorResolution, sel, method_handle);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1439,6 +1506,13 @@ namespace ObjCRuntime {
|
|||
return null;
|
||||
}
|
||||
|
||||
#if NET
|
||||
public static NSObject? GetNSObject (NativeHandle ptr)
|
||||
{
|
||||
return GetNSObject ((IntPtr) ptr, MissingCtorResolution.ThrowConstructor1NotFound);
|
||||
}
|
||||
#endif
|
||||
|
||||
public static NSObject? GetNSObject (IntPtr ptr)
|
||||
{
|
||||
return GetNSObject (ptr, MissingCtorResolution.ThrowConstructor1NotFound);
|
||||
|
@ -1458,11 +1532,21 @@ namespace ObjCRuntime {
|
|||
}
|
||||
|
||||
static public T? GetNSObject<T> (IntPtr ptr) where T : NSObject
|
||||
{
|
||||
return GetNSObject<T> (ptr, IntPtr.Zero, default (RuntimeMethodHandle));
|
||||
}
|
||||
|
||||
static T? GetNSObject<T> (IntPtr ptr, IntPtr sel, RuntimeMethodHandle method_handle) where T : NSObject
|
||||
{
|
||||
return GetNSObject<T> (ptr, sel, method_handle, false);
|
||||
}
|
||||
|
||||
static T? GetNSObject<T> (IntPtr ptr, IntPtr sel, RuntimeMethodHandle method_handle, bool evenInFinalizerQueue) where T : NSObject
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
var obj = TryGetNSObject (ptr, evenInFinalizerQueue: false);
|
||||
var obj = TryGetNSObject (ptr, evenInFinalizerQueue: evenInFinalizerQueue);
|
||||
|
||||
// First check if we got an object of the expected type
|
||||
if (obj is T o)
|
||||
|
@ -1492,7 +1576,7 @@ namespace ObjCRuntime {
|
|||
target_type = typeof (NSObject);
|
||||
}
|
||||
|
||||
return ConstructNSObject<T> (ptr, target_type, MissingCtorResolution.ThrowConstructor1NotFound);
|
||||
return ConstructNSObject<T> (ptr, target_type, MissingCtorResolution.ThrowConstructor1NotFound, sel, method_handle);
|
||||
}
|
||||
|
||||
static public T? GetNSObject<T> (IntPtr ptr, bool owns) where T : NSObject
|
||||
|
@ -1623,6 +1707,12 @@ namespace ObjCRuntime {
|
|||
|
||||
// this method is identical in behavior to the generic one.
|
||||
static INativeObject? GetINativeObject (IntPtr ptr, bool owns, Type target_type, Type? implementation)
|
||||
{
|
||||
return GetINativeObject (ptr, owns, target_type, implementation, IntPtr.Zero, default (RuntimeMethodHandle));
|
||||
}
|
||||
|
||||
// this method is identical in behavior to the generic one.
|
||||
static INativeObject? GetINativeObject (IntPtr ptr, bool owns, Type target_type, Type? implementation, IntPtr sel, RuntimeMethodHandle method_handle)
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
return null;
|
||||
|
@ -1657,10 +1747,10 @@ namespace ObjCRuntime {
|
|||
// native objects and NSObject instances.
|
||||
throw ErrorHelper.CreateError (8004, $"Cannot create an instance of {implementation.FullName} for the native object 0x{ptr:x} (of type '{Class.class_getName (Class.GetClassForObject (ptr))}'), because another instance already exists for this native object (of type {o.GetType ().FullName}).");
|
||||
}
|
||||
return ConstructNSObject<INativeObject> (ptr, implementation, MissingCtorResolution.ThrowConstructor1NotFound);
|
||||
return ConstructNSObject<INativeObject> (ptr, implementation, MissingCtorResolution.ThrowConstructor1NotFound, sel, method_handle);
|
||||
}
|
||||
|
||||
return ConstructINativeObject<INativeObject> (ptr, owns, implementation, MissingCtorResolution.ThrowConstructor2NotFound);
|
||||
return ConstructINativeObject<INativeObject> (ptr, owns, implementation, MissingCtorResolution.ThrowConstructor2NotFound, sel, method_handle);
|
||||
}
|
||||
|
||||
// this method is identical in behavior to the non-generic one.
|
||||
|
@ -1670,6 +1760,16 @@ namespace ObjCRuntime {
|
|||
}
|
||||
|
||||
public static T? GetINativeObject<T> (IntPtr ptr, bool forced_type, bool owns) where T : class, INativeObject
|
||||
{
|
||||
return GetINativeObject<T> (ptr, forced_type, null, owns);
|
||||
}
|
||||
|
||||
internal static T? GetINativeObject<T> (IntPtr ptr, bool forced_type, Type? implementation, bool owns) where T : class, INativeObject
|
||||
{
|
||||
return GetINativeObject<T> (ptr, forced_type, implementation, owns, IntPtr.Zero, default (RuntimeMethodHandle));
|
||||
}
|
||||
|
||||
static T? GetINativeObject<T> (IntPtr ptr, bool forced_type, Type? implementation, bool owns, IntPtr sel, RuntimeMethodHandle method_handle) where T : class, INativeObject
|
||||
{
|
||||
if (ptr == IntPtr.Zero)
|
||||
return null;
|
||||
|
@ -1695,7 +1795,7 @@ namespace ObjCRuntime {
|
|||
}
|
||||
|
||||
// Lookup the ObjC type of the ptr and see if we can use it.
|
||||
var implementation = LookupINativeObjectImplementation (ptr, typeof (T), forced_type: forced_type);
|
||||
implementation = LookupINativeObjectImplementation (ptr, typeof (T), implementation, forced_type: forced_type);
|
||||
|
||||
if (implementation.IsSubclassOf (typeof (NSObject))) {
|
||||
if (o is not null && !forced_type) {
|
||||
|
@ -1710,7 +1810,7 @@ namespace ObjCRuntime {
|
|||
return rv;
|
||||
}
|
||||
|
||||
return ConstructINativeObject<T> (ptr, owns, implementation, MissingCtorResolution.ThrowConstructor2NotFound);
|
||||
return ConstructINativeObject<T> (ptr, owns, implementation, MissingCtorResolution.ThrowConstructor2NotFound, sel, method_handle);
|
||||
}
|
||||
|
||||
static void TryReleaseINativeObject (INativeObject? obj)
|
||||
|
@ -1744,14 +1844,24 @@ namespace ObjCRuntime {
|
|||
return null;
|
||||
|
||||
// Check if the static registrar knows about this protocol
|
||||
unsafe {
|
||||
var map = options->RegistrationMap;
|
||||
if (map is not null) {
|
||||
var token = Class.GetTokenReference (type, throw_exception: false);
|
||||
if (token != INVALID_TOKEN_REF) {
|
||||
var wrapper_token = xamarin_find_protocol_wrapper_type (token);
|
||||
if (wrapper_token != INVALID_TOKEN_REF)
|
||||
return Class.ResolveTypeTokenReference (wrapper_token);
|
||||
if (IsManagedStaticRegistrar) {
|
||||
#if NET
|
||||
var rv = RegistrarHelper.FindProtocolWrapperType (type);
|
||||
if (rv is not null)
|
||||
return rv;
|
||||
#else
|
||||
throw ErrorHelper.CreateError (99, Xamarin.Bundler.Errors.MX0099 /* Internal error */, "The managed static registrar is only available for .NET");
|
||||
#endif
|
||||
} else {
|
||||
unsafe {
|
||||
var map = options->RegistrationMap;
|
||||
if (map is not null) {
|
||||
var token = Class.GetTokenReference (type, throw_exception: false);
|
||||
if (token != INVALID_TOKEN_REF) {
|
||||
var wrapper_token = xamarin_find_protocol_wrapper_type (token);
|
||||
if (wrapper_token != INVALID_TOKEN_REF)
|
||||
return Class.ResolveTypeTokenReference (wrapper_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1919,10 +2029,54 @@ namespace ObjCRuntime {
|
|||
static void RetainNativeObject (IntPtr gchandle)
|
||||
{
|
||||
var obj = GetGCHandleTarget (gchandle);
|
||||
if (obj is NativeObject nobj)
|
||||
RetainNativeObject ((INativeObject?) obj);
|
||||
}
|
||||
|
||||
// Retain the input if it's either an NSObject or a NativeObject.
|
||||
static NativeHandle RetainNativeObject (INativeObject? obj)
|
||||
{
|
||||
if (obj is null)
|
||||
return NativeHandle.Zero;
|
||||
if (obj is NSObject nsobj)
|
||||
RetainNSObject (nsobj);
|
||||
else if (obj is NativeObject nobj)
|
||||
nobj.Retain ();
|
||||
else if (obj is NSObject nsobj)
|
||||
return obj.GetHandle ();
|
||||
}
|
||||
|
||||
internal static NativeHandle RetainNSObject (NSObject? obj)
|
||||
{
|
||||
if (obj is null)
|
||||
return NativeHandle.Zero;
|
||||
obj.DangerousRetain ();
|
||||
return obj.GetHandle ();
|
||||
}
|
||||
|
||||
internal static NativeHandle RetainAndAutoreleaseNSObject (NSObject? obj)
|
||||
{
|
||||
if (obj is null)
|
||||
return NativeHandle.Zero;
|
||||
obj.DangerousRetain ();
|
||||
obj.DangerousAutorelease ();
|
||||
return obj.GetHandle ();
|
||||
}
|
||||
|
||||
internal static NativeHandle RetainAndAutoreleaseNativeObject (INativeObject? obj)
|
||||
{
|
||||
if (obj is null)
|
||||
return NativeHandle.Zero;
|
||||
if (obj is NSObject nsobj) {
|
||||
nsobj.DangerousRetain ();
|
||||
nsobj.DangerousAutorelease ();
|
||||
}
|
||||
return obj.GetHandle ();
|
||||
}
|
||||
|
||||
static IntPtr CopyAndAutorelease (IntPtr ptr)
|
||||
{
|
||||
ptr = Messaging.IntPtr_objc_msgSend (ptr, Selector.GetHandle ("copy"));
|
||||
NSObject.DangerousAutorelease (ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Check if the input is an NSObject, and in that case retain it (and return true)
|
||||
|
@ -2025,6 +2179,47 @@ namespace ObjCRuntime {
|
|||
throw ErrorHelper.CreateError (8003, $"Failed to find the closed generic method '{open_method.Name}' on the type '{closed_type.FullName}'.");
|
||||
}
|
||||
|
||||
internal static MethodInfo FindClosedMethod (object instance, RuntimeTypeHandle open_type_handle, RuntimeMethodHandle open_method_handle)
|
||||
{
|
||||
var closed_type = instance.GetType ()!;
|
||||
var open_type = Type.GetTypeFromHandle (open_type_handle)!;
|
||||
closed_type = FindClosedTypeInHierarchy (open_type, closed_type)!;
|
||||
var closedMethod = MethodBase.GetMethodFromHandle (open_method_handle, closed_type.TypeHandle)!;
|
||||
return (MethodInfo) closedMethod;
|
||||
}
|
||||
|
||||
static Type? FindClosedTypeInHierarchy (Type open_type, Type? closed_type)
|
||||
{
|
||||
if (closed_type is null)
|
||||
return null;
|
||||
|
||||
var closed_type_definition = closed_type;
|
||||
if (closed_type_definition.IsGenericType)
|
||||
closed_type_definition = closed_type_definition.GetGenericTypeDefinition ();
|
||||
if (closed_type_definition == open_type)
|
||||
return closed_type;
|
||||
return FindClosedTypeInHierarchy (open_type, closed_type.BaseType);
|
||||
}
|
||||
|
||||
internal static Type FindClosedParameterType (object instance, RuntimeTypeHandle open_type_handle, RuntimeMethodHandle open_method_handle, int parameter)
|
||||
{
|
||||
var closed_type = instance.GetType ()!;
|
||||
var open_type = Type.GetTypeFromHandle (open_type_handle)!;
|
||||
closed_type = FindClosedTypeInHierarchy (open_type, closed_type)!;
|
||||
var closedMethod = MethodBase.GetMethodFromHandle (open_method_handle, closed_type.TypeHandle)!;
|
||||
var parameters = closedMethod.GetParameters ();
|
||||
return parameters [parameter].ParameterType.GetElementType ()!; // FIX NAMING
|
||||
}
|
||||
|
||||
#if NET
|
||||
// This method might be called by the generated code from the managed static registrar.
|
||||
static void TraceCaller (string message)
|
||||
{
|
||||
var caller = new System.Diagnostics.StackFrame (1);
|
||||
NSLog ($"{caller?.GetMethod ()?.ToString ()}: {message}");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void GCCollect ()
|
||||
{
|
||||
GC.Collect ();
|
||||
|
@ -2125,7 +2320,13 @@ namespace ObjCRuntime {
|
|||
}
|
||||
|
||||
// Allocate a GCHandle and return the IntPtr to it.
|
||||
internal static IntPtr AllocGCHandle (object? value, GCHandleType type = GCHandleType.Normal)
|
||||
internal static IntPtr AllocGCHandle (object? value)
|
||||
{
|
||||
return AllocGCHandle (value, GCHandleType.Normal);
|
||||
}
|
||||
|
||||
// Allocate a GCHandle and return the IntPtr to it.
|
||||
internal static IntPtr AllocGCHandle (object? value, GCHandleType type)
|
||||
{
|
||||
return GCHandle.ToIntPtr (GCHandle.Alloc (value, type));
|
||||
}
|
||||
|
@ -2218,6 +2419,14 @@ namespace ObjCRuntime {
|
|||
return (sbyte) (rv ? 1 : 0);
|
||||
}
|
||||
|
||||
static IntPtr LookupUnmanagedFunction (IntPtr assembly, IntPtr symbol, int id)
|
||||
{
|
||||
#if NET
|
||||
return RegistrarHelper.LookupUnmanagedFunction (assembly, Marshal.PtrToStringAuto (symbol), id);
|
||||
#else
|
||||
return IntPtr.Zero;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
internal class IntPtrEqualityComparer : IEqualityComparer<IntPtr> {
|
||||
|
@ -2244,6 +2453,28 @@ namespace ObjCRuntime {
|
|||
}
|
||||
}
|
||||
|
||||
internal class StringEqualityComparer : IEqualityComparer<string> {
|
||||
public bool Equals (string? x, string? y)
|
||||
{
|
||||
return string.Equals (x, y, StringComparison.Ordinal);
|
||||
}
|
||||
public int GetHashCode (string? obj)
|
||||
{
|
||||
return obj?.GetHashCode () ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal class RuntimeTypeHandleEqualityComparer : IEqualityComparer<RuntimeTypeHandle> {
|
||||
public bool Equals (RuntimeTypeHandle x, RuntimeTypeHandle y)
|
||||
{
|
||||
return x.Equals (y);
|
||||
}
|
||||
public int GetHashCode (RuntimeTypeHandle obj)
|
||||
{
|
||||
return obj.GetHashCode ();
|
||||
}
|
||||
}
|
||||
|
||||
internal struct IntPtrTypeValueTuple : IEquatable<IntPtrTypeValueTuple> {
|
||||
static readonly IEqualityComparer<IntPtr> item1Comparer = Runtime.IntPtrEqualityComparer;
|
||||
static readonly IEqualityComparer<Type> item2Comparer = Runtime.TypeEqualityComparer;
|
||||
|
|
|
@ -1947,6 +1947,7 @@ SHARED_SOURCES = \
|
|||
ObjCRuntime/AdoptsAttribute.cs \
|
||||
ObjCRuntime/BackingField.cs \
|
||||
ObjCRuntime/BaseWrapper.cs \
|
||||
ObjCRuntime/BindAs.cs \
|
||||
ObjCRuntime/BlockProxyAttribute.cs \
|
||||
ObjCRuntime/BindingImplAttribute.cs \
|
||||
ObjCRuntime/CategoryAttribute.cs \
|
||||
|
@ -1958,8 +1959,10 @@ SHARED_SOURCES = \
|
|||
ObjCRuntime/ErrorHelper.runtime.cs \
|
||||
ObjCRuntime/Exceptions.cs \
|
||||
ObjCRuntime/ExceptionMode.cs \
|
||||
ObjCRuntime/IManagedRegistrar.cs \
|
||||
ObjCRuntime/Method.cs \
|
||||
ObjCRuntime/Registrar.cs \
|
||||
ObjCRuntime/RegistrarHelper.cs \
|
||||
ObjCRuntime/ReleaseAttribute.cs \
|
||||
ObjCRuntime/RequiredFrameworkAttribute.cs \
|
||||
ObjCRuntime/Runtime.cs \
|
||||
|
|
|
@ -16,10 +16,12 @@ using ObjCRuntime;
|
|||
|
||||
namespace XamarinTests.ObjCRuntime {
|
||||
|
||||
[Flags]
|
||||
public enum Registrars {
|
||||
Static = 1,
|
||||
ManagedStatic = Static | 2,
|
||||
Dynamic = 4,
|
||||
AllStatic = Static,
|
||||
AllStatic = Static | ManagedStatic,
|
||||
AllDynamic = Dynamic,
|
||||
}
|
||||
|
||||
|
@ -27,8 +29,23 @@ namespace XamarinTests.ObjCRuntime {
|
|||
[Register ("__registration_test_CLASS")]
|
||||
class RegistrationTestClass : NSObject { }
|
||||
|
||||
public static bool IsStaticRegistrar {
|
||||
get {
|
||||
return CurrentRegistrar.HasFlag (Registrars.Static);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsDynamicRegistrar {
|
||||
get {
|
||||
return CurrentRegistrar.HasFlag (Registrars.Dynamic);
|
||||
}
|
||||
}
|
||||
|
||||
public static Registrars CurrentRegistrar {
|
||||
get {
|
||||
var __registrar__ = typeof (Class).Assembly.GetType ("ObjCRuntime.__Registrar__");
|
||||
if (__registrar__ is not null)
|
||||
return Registrars.ManagedStatic;
|
||||
#if NET
|
||||
var types = new Type [] { typeof (NativeHandle), typeof (bool).MakeByRefType () };
|
||||
#else
|
||||
|
|
|
@ -215,7 +215,7 @@ namespace Xamarin.BindingTests {
|
|||
// The ObjC runtime won't add optional properties dynamically (the code is commented out,
|
||||
// see file objc4-647/runtime/objc-runtime-old.mm in Apple's open source code),
|
||||
// so we need to verify differently for the dynamic registrar.
|
||||
if (XamarinTests.ObjCRuntime.Registrar.CurrentRegistrar == XamarinTests.ObjCRuntime.Registrars.Static) {
|
||||
if (XamarinTests.ObjCRuntime.Registrar.IsStaticRegistrar) {
|
||||
Assert.AreEqual (9, properties.Length, "Properties: Count");
|
||||
} else {
|
||||
Assert.AreEqual (2, properties.Length, "Properties: Count");
|
||||
|
@ -232,7 +232,7 @@ namespace Xamarin.BindingTests {
|
|||
new objc_property_attribute ("N", "")
|
||||
})), "Properties: requiredReadonlyProperty");
|
||||
|
||||
if (XamarinTests.ObjCRuntime.Registrar.CurrentRegistrar == XamarinTests.ObjCRuntime.Registrars.Static) {
|
||||
if (XamarinTests.ObjCRuntime.Registrar.IsStaticRegistrar) {
|
||||
Assert.That (properties, Contains.Item (new objc_property ("optionalInstanceProperty", "T@\"NSString\",N", new objc_property_attribute [] {
|
||||
new objc_property_attribute ("T", "@\"NSString\""),
|
||||
new objc_property_attribute ("N", "")
|
||||
|
|
|
@ -332,14 +332,12 @@ namespace Xamarin.BindingTests {
|
|||
Messaging.void_objc_msgSend_IntPtr_bool_bool (Class.GetHandle (typeof (ObjCBlockTester)), Selector.GetHandle ("setProtocolWithBlockProperties:required:instance:"), pb.Handle, required, instance);
|
||||
Assert.Fail ("Expected an MT8028 error");
|
||||
} catch (RuntimeException re) {
|
||||
#if __MACOS__
|
||||
Assert.AreEqual (8009, re.Code, "Code");
|
||||
Console.WriteLine (re.Message);
|
||||
Assert.That (re.Message, Does.StartWith ("Unable to locate the block to delegate conversion method for the method Xamarin.BindingTests.RegistrarBindingTest+FakePropertyBlock.set_"), re.Message, "Message");
|
||||
#else
|
||||
Assert.AreEqual (8028, re.Code, "Code");
|
||||
Assert.AreEqual ("The runtime function get_block_wrapper_creator has been linked away.", re.Message, "Message");
|
||||
#endif
|
||||
Assert.That (re.Code, Is.EqualTo (8009).Or.EqualTo (8028), "Code");
|
||||
if (re.Code == 8009) {
|
||||
Assert.That (re.Message, Does.StartWith ("Unable to locate the block to delegate conversion method for the method Xamarin.BindingTests.RegistrarBindingTest+FakePropertyBlock.set_"), re.Message, "Message");
|
||||
} else {
|
||||
Assert.AreEqual ("The runtime function get_block_wrapper_creator has been linked away.", re.Message, "Message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -385,6 +385,7 @@ namespace Cecil.Tests {
|
|||
}
|
||||
|
||||
static HashSet<string> knownFailuresBlockLiterals = new HashSet<string> {
|
||||
"The call to SetupBlock in ObjCRuntime.BlockLiteral.CreateBlockForDelegate(System.Delegate, System.Delegate, System.String) must be converted to new Block syntax.",
|
||||
"The call to SetupBlock in ObjCRuntime.BlockLiteral.GetBlockForDelegate(System.Reflection.MethodInfo, System.Object, System.UInt32, System.String) must be converted to new Block syntax.",
|
||||
"The call to SetupBlock in ObjCRuntime.BlockLiteral.SetupBlock(System.Delegate, System.Delegate) must be converted to new Block syntax.",
|
||||
"The call to SetupBlock in ObjCRuntime.BlockLiteral.SetupBlockUnsafe(System.Delegate, System.Delegate) must be converted to new Block syntax.",
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace MonoTouchFixtures.JavascriptCore {
|
|||
{
|
||||
TestRuntime.AssertXcodeVersion (5, 0, 1);
|
||||
|
||||
if (RegistrarTest.CurrentRegistrar != Registrars.Static)
|
||||
if (!global::XamarinTests.ObjCRuntime.Registrar.IsStaticRegistrar)
|
||||
Assert.Ignore ("Exporting protocols to JavaScriptCore requires the static registrar.");
|
||||
|
||||
var context = new JSContext ();
|
||||
|
|
|
@ -213,7 +213,7 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
NativeHandle ptr;
|
||||
CGPath path;
|
||||
|
||||
if ((CurrentRegistrar & Registrars.AllStatic) == 0)
|
||||
if (!global::XamarinTests.ObjCRuntime.Registrar.IsStaticRegistrar)
|
||||
Assert.Ignore ("This test only passes with the static registrars.");
|
||||
|
||||
Assert.False (Messaging.bool_objc_msgSend_IntPtr (receiver, new Selector ("INativeObject1:").Handle, NativeHandle.Zero), "#a1");
|
||||
|
@ -1296,12 +1296,14 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
void ThrowsICEIfDebug (TestDelegate code, string message, bool execute_release_mode = true)
|
||||
{
|
||||
#if NET
|
||||
if (TestRuntime.IsCoreCLR) {
|
||||
if (TestRuntime.IsCoreCLR || global::XamarinTests.ObjCRuntime.Registrar.CurrentRegistrar == Registrars.ManagedStatic) {
|
||||
if (execute_release_mode) {
|
||||
// In CoreCLR will either throw an ArgumentException:
|
||||
// <System.ArgumentException: Object of type 'Foundation.NSObject' cannot be converted to type 'Foundation.NSSet'.
|
||||
// or a RuntimeException:
|
||||
// <ObjCRuntime.RuntimeException: Failed to marshal the value at index 0.
|
||||
// or an InvalidCastException
|
||||
// System.InvalidCastException: Unable to cast object of type 'Foundation.NSObject' to type 'Foundation.NSSet'.
|
||||
var noException = false;
|
||||
try {
|
||||
code ();
|
||||
|
@ -1310,6 +1312,8 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
// OK
|
||||
} catch (RuntimeException) {
|
||||
// OK
|
||||
} catch (InvalidCastException) {
|
||||
// OK
|
||||
} catch (Exception e) {
|
||||
Assert.Fail ($"Unexpectedly failed with exception of type {e.GetType ()} - expected either ArgumentException or RuntimeException: {message}");
|
||||
}
|
||||
|
@ -5733,19 +5737,19 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
#endif // !__WATCHOS__
|
||||
|
||||
#if HAS_COREMIDI
|
||||
// This type exports methods with 'MidiCIDeviceIdentification' parameters, which is a struct with different casing in Objective-C ("MIDI...")
|
||||
// This type exports methods with 'MidiThruConnectionEndpoint' parameters, which is a struct with different casing in Objective-C ("MIDI...")
|
||||
class ExportedMethodWithStructWithManagedCasing : NSObject {
|
||||
[Export ("doSomething:")]
|
||||
public void DoSomething (MidiCIDeviceIdentification arg) { }
|
||||
public void DoSomething (MidiThruConnectionEndpoint arg) { }
|
||||
|
||||
[Export ("doSomething2:")]
|
||||
public void DoSomething2 (ref MidiCIDeviceIdentification arg) { }
|
||||
public void DoSomething2 (ref MidiThruConnectionEndpoint arg) { }
|
||||
|
||||
[Export ("doSomething3")]
|
||||
public MidiCIDeviceIdentification DoSomething3 () { return default (MidiCIDeviceIdentification); }
|
||||
public MidiThruConnectionEndpoint DoSomething3 () { return default (MidiThruConnectionEndpoint); }
|
||||
|
||||
[Export ("doSomething4:")]
|
||||
public void DoSomething4 (out MidiCIDeviceIdentification arg) { arg = default (MidiCIDeviceIdentification); }
|
||||
public void DoSomething4 (out MidiThruConnectionEndpoint arg) { arg = default (MidiThruConnectionEndpoint); }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -630,7 +630,7 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
Messaging.void_objc_msgSend_IntPtr (Class.GetHandle (typeof (Dummy)), Selector.GetHandle ("doSomethingElse:"), handle);
|
||||
Assert.Fail ("Expected an MX8029 exception (A)");
|
||||
} catch (RuntimeException mex) {
|
||||
Assert.AreEqual (8029, mex.Code, "Exception code (A)");
|
||||
Assert.That (mex.Code, Is.EqualTo (8029).Or.EqualTo (8027), "Exception code (A)");
|
||||
var failure = mex.ToString ();
|
||||
Assert.That (failure, Does.Contain ("Failed to marshal the Objective-C object"), "Failed to marshal (A)");
|
||||
Assert.That (failure, Does.Contain ("Additional information:"), "Additional information: (A)");
|
||||
|
|
|
@ -96,8 +96,11 @@ namespace Xharness.Jenkins {
|
|||
}
|
||||
yield return new TestData { Variation = "Release (interpreter -mscorlib)", BundlerArguments = "--interpreter=-mscorlib", Debug = false, Profiling = false, Undefines = "FULL_AOT_RUNTIME", Ignored = ignore };
|
||||
}
|
||||
if (test.TestProject.IsDotNetProject)
|
||||
if (test.TestProject.IsDotNetProject) {
|
||||
yield return new TestData { Variation = "Release (LLVM)", Debug = false, UseLlvm = true, Ignored = ignore };
|
||||
yield return new TestData { Variation = "Debug (managed static registrar)", Registrar = "managed-static", Debug = true, Profiling = false, Ignored = ignore };
|
||||
yield return new TestData { Variation = "Release (managed static registrar, all optimizations)", BundlerArguments = "--optimize:all", Registrar = "managed-static", Debug = false, Profiling = false, LinkMode = "Full", Defines = "OPTIMIZEALL", Ignored = ignore };
|
||||
}
|
||||
break;
|
||||
case string name when name.StartsWith ("mscorlib", StringComparison.Ordinal):
|
||||
if (supports_debug)
|
||||
|
@ -126,6 +129,10 @@ namespace Xharness.Jenkins {
|
|||
|
||||
if (test.TestProject.IsDotNetProject && mac_supports_arm64)
|
||||
yield return new TestData { Variation = "Debug (ARM64)", Debug = true, Profiling = false, Ignored = !mac_supports_arm64 ? true : ignore, RuntimeIdentifier = arm64_sim_runtime_identifier, };
|
||||
if (test.TestProject.IsDotNetProject) {
|
||||
yield return new TestData { Variation = "Debug (managed static registrar)", Registrar = "managed-static", Debug = true, Profiling = false, Ignored = ignore };
|
||||
yield return new TestData { Variation = "Release (managed static registrar, all optimizations)", BundlerArguments = "--optimize:all", Registrar = "managed-static", Debug = false, Profiling = false, LinkMode = "Full", Defines = "OPTIMIZEALL", Ignored = ignore };
|
||||
}
|
||||
break;
|
||||
case "introspection":
|
||||
if (test.TestProject.IsDotNetProject && mac_supports_arm64)
|
||||
|
@ -160,6 +167,8 @@ namespace Xharness.Jenkins {
|
|||
}
|
||||
if (test.TestProject.IsDotNetProject) {
|
||||
yield return new TestData { Variation = "Release (all optimizations)", BundlerArguments = "--optimize:all", Registrar = "static", Debug = false, Profiling = false, LinkMode = "Full", Defines = "OPTIMIZEALL", Ignored = ignore };
|
||||
yield return new TestData { Variation = "Debug (managed static registrar)", Registrar = "managed-static", Debug = true, Profiling = false, Ignored = ignore };
|
||||
yield return new TestData { Variation = "Release (managed static registrar, all optimizations)", BundlerArguments = "--optimize:all", Registrar = "managed-static", Debug = false, Profiling = false, LinkMode = "Full", Defines = "OPTIMIZEALL", Ignored = ignore };
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace Xamarin.Bundler {
|
|||
Dynamic,
|
||||
PartialStatic,
|
||||
Static,
|
||||
ManagedStatic,
|
||||
}
|
||||
|
||||
public partial class Application {
|
||||
|
@ -350,7 +351,7 @@ namespace Xamarin.Bundler {
|
|||
case ApplePlatform.MacCatalyst:
|
||||
return !AreAnyAssembliesTrimmed;
|
||||
case ApplePlatform.MacOSX:
|
||||
return Registrar == RegistrarMode.Static && !AreAnyAssembliesTrimmed;
|
||||
return (Registrar == RegistrarMode.Static || Registrar == RegistrarMode.ManagedStatic) && !AreAnyAssembliesTrimmed;
|
||||
default:
|
||||
throw ErrorHelper.CreateError (71, Errors.MX0071, Platform, ProductName);
|
||||
}
|
||||
|
@ -1234,9 +1235,18 @@ namespace Xamarin.Bundler {
|
|||
case "partial-static":
|
||||
Registrar = RegistrarMode.PartialStatic;
|
||||
break;
|
||||
#endif
|
||||
#if NET
|
||||
case "managed-static":
|
||||
Registrar = RegistrarMode.ManagedStatic;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
#if NET
|
||||
throw ErrorHelper.CreateError (20, Errors.MX0020, "--registrar", "managed-static, static, dynamic or default");
|
||||
#else
|
||||
throw ErrorHelper.CreateError (20, Errors.MX0020, "--registrar", "static, dynamic or default");
|
||||
#endif
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
|
|
|
@ -313,7 +313,7 @@ namespace Xamarin.Bundler {
|
|||
}
|
||||
|
||||
// Don't add -force_load if the binding's SmartLink value is set and the static registrar is being used.
|
||||
if (metadata.ForceLoad && !(metadata.SmartLink && App.Registrar == RegistrarMode.Static))
|
||||
if (metadata.ForceLoad && !(metadata.SmartLink && (App.Registrar == RegistrarMode.Static || App.Registrar == RegistrarMode.ManagedStatic)))
|
||||
ForceLoad = true;
|
||||
|
||||
if (!string.IsNullOrEmpty (metadata.LinkerFlags)) {
|
||||
|
|
|
@ -186,7 +186,7 @@ namespace Xamarin.Bundler {
|
|||
|
||||
switch ((Opt) i) {
|
||||
case Opt.StaticBlockToDelegateLookup:
|
||||
if (app.Registrar != RegistrarMode.Static) {
|
||||
if (app.Registrar != RegistrarMode.Static && app.Registrar != RegistrarMode.ManagedStatic) {
|
||||
messages.Add (ErrorHelper.CreateWarning (2003, Errors.MT2003, (values [i].Value ? "" : "-"), opt_names [i]));
|
||||
values [i] = false;
|
||||
continue;
|
||||
|
@ -196,7 +196,7 @@ namespace Xamarin.Bundler {
|
|||
break; // Does not require linker
|
||||
case Opt.RegisterProtocols:
|
||||
case Opt.RemoveDynamicRegistrar:
|
||||
if (app.Registrar != RegistrarMode.Static) {
|
||||
if (app.Registrar != RegistrarMode.Static && app.Registrar != RegistrarMode.ManagedStatic) {
|
||||
messages.Add (ErrorHelper.CreateWarning (2003, Errors.MT2003, (values [i].Value ? "" : "-"), opt_names [i]));
|
||||
values [i] = false;
|
||||
continue;
|
||||
|
@ -243,17 +243,17 @@ namespace Xamarin.Bundler {
|
|||
|
||||
// We try to optimize calls to BlockLiteral.SetupBlock and certain BlockLiteral constructors if the static registrar is enabled
|
||||
if (!OptimizeBlockLiteralSetupBlock.HasValue) {
|
||||
OptimizeBlockLiteralSetupBlock = app.Registrar == RegistrarMode.Static;
|
||||
OptimizeBlockLiteralSetupBlock = app.Registrar == RegistrarMode.Static || app.Registrar == RegistrarMode.ManagedStatic;
|
||||
}
|
||||
|
||||
// We will register protocols if the static registrar is enabled and loading assemblies is not possible
|
||||
if (!RegisterProtocols.HasValue) {
|
||||
if (app.Platform != ApplePlatform.MacOSX) {
|
||||
RegisterProtocols = (app.Registrar == RegistrarMode.Static) && !app.UseInterpreter;
|
||||
RegisterProtocols = (app.Registrar == RegistrarMode.Static || app.Registrar == RegistrarMode.ManagedStatic) && !app.UseInterpreter;
|
||||
} else {
|
||||
RegisterProtocols = false;
|
||||
}
|
||||
} else if (app.Registrar != RegistrarMode.Static && RegisterProtocols == true) {
|
||||
} else if (app.Registrar != RegistrarMode.Static && app.Registrar != RegistrarMode.ManagedStatic && RegisterProtocols == true) {
|
||||
RegisterProtocols = false; // we've already shown a warning for this.
|
||||
}
|
||||
|
||||
|
@ -272,7 +272,7 @@ namespace Xamarin.Bundler {
|
|||
} else if (StaticBlockToDelegateLookup != true) {
|
||||
// Can't remove the dynamic registrar unless also generating static lookup of block-to-delegates in the static registrar.
|
||||
RemoveDynamicRegistrar = false;
|
||||
} else if (app.Registrar != RegistrarMode.Static || !app.AreAnyAssembliesTrimmed) {
|
||||
} else if ((app.Registrar != RegistrarMode.Static && app.Registrar != RegistrarMode.ManagedStatic) || !app.AreAnyAssembliesTrimmed) {
|
||||
// Both the linker and the static registrar are also required
|
||||
RemoveDynamicRegistrar = false;
|
||||
} else {
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -856,6 +856,8 @@ namespace Xamarin.Bundler {
|
|||
#if NET
|
||||
sw.WriteLine ("\txamarin_runtime_configuration_name = {0};", string.IsNullOrEmpty (app.RuntimeConfigurationFile) ? "NULL" : $"\"{app.RuntimeConfigurationFile}\"");
|
||||
#endif
|
||||
if (app.Registrar == RegistrarMode.ManagedStatic)
|
||||
sw.WriteLine ("\txamarin_set_is_managed_static_registrar (true);");
|
||||
sw.WriteLine ("}");
|
||||
sw.WriteLine ();
|
||||
sw.Write ("int ");
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Logging;
|
||||
using Microsoft.Build.Logging.StructuredLogger;
|
||||
|
||||
using Mono.Options;
|
||||
|
||||
class Program {
|
||||
static int Main (string [] args)
|
||||
{
|
||||
var printHelp = false;
|
||||
var binlog = string.Empty;
|
||||
var rootDirectory = string.Empty;
|
||||
var skippedLinkerCommands = 0;
|
||||
var options = new OptionSet {
|
||||
{ "h|?|help", "Print this help message", (v) => printHelp = true },
|
||||
{ "r|root=", "The root directory", (v) => rootDirectory = v },
|
||||
{ "bl|binlog=", "The binlog", (v) => binlog = v },
|
||||
{ "s|skip", "Task invocations to skip", (v) => skippedLinkerCommands++ },
|
||||
};
|
||||
|
||||
if (printHelp) {
|
||||
options.WriteOptionDescriptions (Console.Out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
var others = options.Parse (args);
|
||||
if (others.Any ()) {
|
||||
Console.WriteLine ("Unexpected arguments:");
|
||||
foreach (var arg in others)
|
||||
Console.WriteLine ("\t{0}", arg);
|
||||
Console.WriteLine ("Expected arguments are:");
|
||||
options.WriteOptionDescriptions (Console.Out);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty (binlog)) {
|
||||
Console.Error.WriteLine ("A binlog is required");
|
||||
Console.WriteLine ("Expected arguments are:");
|
||||
options.WriteOptionDescriptions (Console.Out);
|
||||
return 1;
|
||||
}
|
||||
|
||||
var path = Path.GetFullPath (binlog);
|
||||
|
||||
if (string.IsNullOrEmpty (rootDirectory))
|
||||
rootDirectory = Path.GetDirectoryName (path)!;
|
||||
|
||||
Console.WriteLine ($"Processing {path} with root directory {rootDirectory}...");
|
||||
|
||||
|
||||
var reader = new BinLogReader ();
|
||||
var records = reader.ReadRecords (path).ToArray ();
|
||||
foreach (var record in records) {
|
||||
if (record is null)
|
||||
continue;
|
||||
|
||||
if (record.Args is null)
|
||||
continue;
|
||||
|
||||
if (record.Args is TaskStartedEventArgs tsea && tsea.TaskName == "ILLink") {
|
||||
if (skippedLinkerCommands > 0) {
|
||||
Console.WriteLine ($"Skipped an ILLink task invocation, {skippedLinkerCommands} left to skip...");
|
||||
skippedLinkerCommands--;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var relevantRecords = records.Where (v => v?.Args?.BuildEventContext?.TaskId == tsea.BuildEventContext.TaskId).Select (v => v.Args).ToArray ();
|
||||
var cla = relevantRecords.Where (v => v is BuildMessageEventArgs).Cast<BuildMessageEventArgs> ().Where (v => v?.ToString ()?.Contains ("CommandLineArguments") == true).ToArray ();
|
||||
foreach (var rr in relevantRecords) {
|
||||
if (rr is TaskCommandLineEventArgs tclea) {
|
||||
if (!Xamarin.Utils.StringUtils.TryParseArguments (tclea.CommandLine.Replace ('\n', ' '), out var arguments, out var ex)) {
|
||||
Console.WriteLine ($"Failed to parse command line arguments: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
WriteLaunchJson (CreateLaunchJson (rootDirectory, arguments));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.Error.WriteLine ($"Unable to find command line arguments for ILLink in {path}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void WriteLaunchJson (string contents)
|
||||
{
|
||||
var dir = Environment.CurrentDirectory!;
|
||||
while (!Directory.Exists (Path.Combine (dir, "tools", "dotnet-linker")))
|
||||
dir = Path.GetDirectoryName (dir)!;
|
||||
var path = Path.Combine (dir, "tools", "dotnet-linker", ".vscode", "launch.json");
|
||||
File.WriteAllText (path, contents);
|
||||
Console.WriteLine ($"Created {path}");
|
||||
}
|
||||
|
||||
static string CreateLaunchJson (string workingDirectory, string [] arguments)
|
||||
{
|
||||
var dotnet = arguments [0];
|
||||
var sb = new StringBuilder ();
|
||||
sb.AppendLine ("{");
|
||||
sb.AppendLine (" // Use IntelliSense to learn about possible attributes.");
|
||||
sb.AppendLine (" // Hover to view descriptions of existing attributes.");
|
||||
sb.AppendLine (" // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387");
|
||||
sb.AppendLine (" \"version\": \"0.2.0\",");
|
||||
sb.AppendLine (" \"configurations\": [");
|
||||
sb.AppendLine (" {");
|
||||
sb.AppendLine (" \"justMyCode\": false,");
|
||||
sb.AppendLine (" \"preLaunchTask\": \"make\",");
|
||||
sb.AppendLine (" \"name\": \".NET Core Launch (console)\",");
|
||||
sb.AppendLine (" \"type\": \"coreclr\",");
|
||||
sb.AppendLine (" \"request\": \"launch\",");
|
||||
sb.AppendLine ($" \"program\": \"{dotnet}\",");
|
||||
sb.AppendLine (" \"args\": [");
|
||||
for (var i = 1; i < arguments.Length; i++) {
|
||||
sb.AppendLine ($" \"{arguments [i]}\"{(i < arguments.Length - 1 ? "," : "")}");
|
||||
}
|
||||
sb.AppendLine (" ],");
|
||||
sb.AppendLine ($" \"cwd\": \"{Path.GetFullPath (workingDirectory)}\",");
|
||||
sb.AppendLine (" \"console\": \"internalConsole\",");
|
||||
sb.AppendLine (" \"stopAtEntry\": false");
|
||||
sb.AppendLine (" },");
|
||||
sb.AppendLine (" {");
|
||||
sb.AppendLine (" \"name\": \".NET Core Attach\",");
|
||||
sb.AppendLine (" \"type\": \"coreclr\",");
|
||||
sb.AppendLine (" \"request\": \"attach\"");
|
||||
sb.AppendLine (" }");
|
||||
sb.AppendLine (" ]");
|
||||
sb.AppendLine ("}");
|
||||
return sb.ToString ();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
This tool takes a binlog that executes dotnet-linker, and creates a
|
||||
launch.json file for the dotnet-linker directory so that the steps can be
|
||||
debugged in VSCode.
|
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<RootNamespace>create_dotnet_linker_launch_json</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSBuild.StructuredLogger" Version="2.1.758" />
|
||||
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\common\StringUtils.cs">
|
||||
<Link>StringUtils.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 25.0.1706.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "create-dotnet-linker-launch-json", "create-dotnet-linker-launch-json.csproj", "{9D626D84-80BA-43E8-ADDF-EAE8944F73D8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{9D626D84-80BA-43E8-ADDF-EAE8944F73D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9D626D84-80BA-43E8-ADDF-EAE8944F73D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9D626D84-80BA-43E8-ADDF-EAE8944F73D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9D626D84-80BA-43E8-ADDF-EAE8944F73D8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B197B398-E8C7-444F-B2E2-1F479AE20CA2}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -166,6 +166,8 @@ variables:
|
|||
value: ''
|
||||
- name: MaciosUploadPrefix
|
||||
value: ''
|
||||
- name: Packaging.EnableSBOMSigning
|
||||
value: false
|
||||
|
||||
trigger: none
|
||||
|
||||
|
|
|
@ -586,6 +586,51 @@ function Set-BuildTags {
|
|||
}
|
||||
}
|
||||
|
||||
function Get-YamlPreview {
|
||||
param (
|
||||
[String]
|
||||
$Org,
|
||||
|
||||
[String]
|
||||
$Project,
|
||||
|
||||
[String]
|
||||
$AccessToken,
|
||||
|
||||
[String]
|
||||
$PipelineId,
|
||||
|
||||
[String]
|
||||
$Branch,
|
||||
|
||||
[String]
|
||||
$OutputFile
|
||||
)
|
||||
|
||||
$headers = Get-AuthHeader -AccessToken $AccessToken
|
||||
|
||||
# create the payload, this payload will point to the correct branch of the repo we want to work with, the repository is always 'self'
|
||||
$payload=@{
|
||||
"previewRun"=$true
|
||||
"resources"=@{
|
||||
"repositories"=@{
|
||||
"self"=@{
|
||||
"refName"="refs/heads/$Branch"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$body = ConvertTo-Json $payload -Depth 100
|
||||
|
||||
$url="https://dev.azure.com/$Org/$Project/_apis/pipelines/$PipelineId/preview?api-version=7.1-preview.1"
|
||||
try {
|
||||
$response=Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -ContentType 'application/json' -Body $body
|
||||
} catch {
|
||||
Write-Host $_
|
||||
}
|
||||
Set-Content -Path $OutputFile -Value $response.finalYaml
|
||||
}
|
||||
|
||||
function New-BuildConfiguration {
|
||||
param
|
||||
(
|
||||
|
@ -617,3 +662,4 @@ Export-ModuleMember -Function Set-BuildTags
|
|||
Export-ModuleMember -Function New-VSTS
|
||||
Export-ModuleMember -Function New-BuildConfiguration
|
||||
Export-ModuleMember -Function Import-BuildConfiguration
|
||||
Export-ModuleMember -Function Get-YamlPreview
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Peform a dry run of the xamarin-macios pipelines that will
|
||||
download the expanded yaml.
|
||||
.DESCRIPTION
|
||||
This script helps tp debug issues that happen in the azure devops
|
||||
pipelines by performing a dry run of the yaml and returning its expanded
|
||||
version. That way a developer can see the full yaml and find issues without running
|
||||
the pipelines.
|
||||
.EXAMPLE
|
||||
|
||||
Query the expanded template from the ci pipeline in main:
|
||||
|
||||
./preview_yaml.ps1 -AccessToken $pat -OutputFile ./full.yaml
|
||||
|
||||
Query the expansion in the pr pipeline:
|
||||
|
||||
./preview_yaml.ps1 -AccessToken $pat -OutputFile ./full.yaml -Pipeline "pr"
|
||||
|
||||
Query the expansion in the ci pipeline for a diff branch than main:
|
||||
|
||||
./preview_yaml.ps1 -AccessToken $pat -OutputFile ./full.yaml -Branch "trigger-issues"
|
||||
|
||||
.PARAMETER AccessToken
|
||||
Personal access token for VSTS that has at least access to the pipelines API.
|
||||
|
||||
.PARAMETER Pipeline
|
||||
Name of the pipeline to use for the dry run, it accepts two values:
|
||||
- ci
|
||||
- pr
|
||||
The defualt value is ci.
|
||||
.PARAMETER Branch
|
||||
The branch to be used for the dry run. Takes the name of the branch, it should
|
||||
not have the prefix refs/head.
|
||||
|
||||
.PARAMETER OutputFile
|
||||
Path to the file where the expanded yaml will be written. The output file is
|
||||
ALLWAYS overwritten.
|
||||
|
||||
#>
|
||||
param (
|
||||
[Parameter(Mandatory)]
|
||||
[String]
|
||||
$AccessToken,
|
||||
|
||||
[String]
|
||||
$Pipeline="ci",
|
||||
|
||||
[String]
|
||||
$Branch="main",
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[String]
|
||||
$OutputFile
|
||||
)
|
||||
|
||||
Import-Module ./VSTS.psm1 -Force
|
||||
|
||||
if ($Pipeline -eq "pr") {
|
||||
Write-Host "Querying the PR pipeline."
|
||||
$PipelineId="16533"
|
||||
} else {
|
||||
Write-Host "Querying the CI pipeline."
|
||||
$PipelineId="14411"
|
||||
}
|
||||
|
||||
Get-YamlPreview -Org "devdiv" -Project "DevDiv" -AccessToken $AccessToken -PipelineId $PipelineId -Branch $Branch -OutputFile $OutputFile
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "make",
|
||||
"type": "shell",
|
||||
"command": "make"
|
||||
}
|
||||
]
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,123 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Xamarin.Linker {
|
||||
|
||||
static class Cecil_Extensions {
|
||||
public static VariableDefinition AddVariable (this MethodBody self, TypeReference variableType)
|
||||
{
|
||||
var rv = new VariableDefinition (variableType);
|
||||
self.Variables.Add (rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static ParameterDefinition AddParameter (this MethodDefinition self, string name, TypeReference parameterType)
|
||||
{
|
||||
var rv = new ParameterDefinition (name, ParameterAttributes.None, parameterType);
|
||||
self.Parameters.Add (rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static MethodDefinition AddMethod (this TypeDefinition self, string name, MethodAttributes attributes, TypeReference returnType)
|
||||
{
|
||||
var rv = new MethodDefinition (name, attributes, returnType);
|
||||
rv.DeclaringType = self;
|
||||
self.Methods.Add (rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static MethodBody CreateBody (this MethodDefinition self, out ILProcessor il)
|
||||
{
|
||||
var body = new MethodBody (self);
|
||||
body.InitLocals = true;
|
||||
self.Body = body;
|
||||
il = body.GetILProcessor ();
|
||||
return body;
|
||||
}
|
||||
|
||||
public static void AddRange<T> (this Mono.Collections.Generic.Collection<T> self, IEnumerable<T>? items)
|
||||
{
|
||||
if (items is null)
|
||||
return;
|
||||
|
||||
foreach (var item in items) {
|
||||
self.Add (item);
|
||||
}
|
||||
}
|
||||
|
||||
public static void EmitLoadArgument (this ILProcessor il, int argument)
|
||||
{
|
||||
il.Append (il.CreateLoadArgument (argument));
|
||||
}
|
||||
|
||||
public static Instruction CreateLoadArgument (this ILProcessor il, int argument)
|
||||
{
|
||||
switch (argument) {
|
||||
case 0:
|
||||
return il.Create (OpCodes.Ldarg_0);
|
||||
case 1:
|
||||
return il.Create (OpCodes.Ldarg_1);
|
||||
case 2:
|
||||
return il.Create (OpCodes.Ldarg_2);
|
||||
case 3:
|
||||
return il.Create (OpCodes.Ldarg_3);
|
||||
default:
|
||||
return il.Create (OpCodes.Ldarg, argument);
|
||||
}
|
||||
}
|
||||
|
||||
public static Instruction CreateLdc (this ILProcessor il, bool value)
|
||||
{
|
||||
if (value)
|
||||
return il.Create (OpCodes.Ldc_I4_1);
|
||||
return il.Create (OpCodes.Ldc_I4_0);
|
||||
}
|
||||
|
||||
public static void EmitLdc (this ILProcessor il, bool value)
|
||||
{
|
||||
il.Append (il.CreateLdc (value));
|
||||
}
|
||||
|
||||
public static GenericInstanceMethod CreateGenericInstanceMethod (this MethodReference mr, params TypeReference [] genericTypeArguments)
|
||||
{
|
||||
var gim = new GenericInstanceMethod (mr);
|
||||
gim.GenericArguments.AddRange (genericTypeArguments);
|
||||
return gim;
|
||||
}
|
||||
|
||||
public static MethodReference CreateMethodReferenceOnGenericType (this TypeReference type, MethodReference mr, params TypeReference [] genericTypeArguments)
|
||||
{
|
||||
var git = new GenericInstanceType (type);
|
||||
git.GenericArguments.AddRange (genericTypeArguments);
|
||||
|
||||
var rv = new MethodReference (mr.Name, mr.ReturnType, git);
|
||||
rv.HasThis = mr.HasThis;
|
||||
rv.ExplicitThis = mr.ExplicitThis;
|
||||
rv.CallingConvention = mr.CallingConvention;
|
||||
rv.Parameters.AddRange (mr.Parameters);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static GenericInstanceType CreateGenericInstanceType (this TypeReference type, params TypeReference [] genericTypeArguments)
|
||||
{
|
||||
var git = new GenericInstanceType (type);
|
||||
git.GenericArguments.AddRange (genericTypeArguments);
|
||||
return git;
|
||||
}
|
||||
|
||||
public static MethodDefinition AddDefaultConstructor (this TypeDefinition type, AppBundleRewriter abr)
|
||||
{
|
||||
// Add default ctor that just calls the base ctor
|
||||
var defaultCtor = type.AddMethod (".ctor", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, abr.System_Void);
|
||||
defaultCtor.CreateBody (out var il);
|
||||
il.Emit (OpCodes.Ldarg_0);
|
||||
il.Emit (OpCodes.Call, abr.System_Object__ctor);
|
||||
il.Emit (OpCodes.Ret);
|
||||
return defaultCtor;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ using System.Xml.Linq;
|
|||
|
||||
using Mono.Cecil;
|
||||
using Mono.Linker;
|
||||
using Mono.Linker.Steps;
|
||||
|
||||
using Xamarin.Bundler;
|
||||
using Xamarin.Utils;
|
||||
|
@ -60,6 +61,18 @@ namespace Xamarin.Linker {
|
|||
|
||||
Dictionary<string, List<MSBuildItem>> msbuild_items = new Dictionary<string, List<MSBuildItem>> ();
|
||||
|
||||
AppBundleRewriter? abr;
|
||||
internal AppBundleRewriter AppBundleRewriter {
|
||||
get {
|
||||
if (abr is null)
|
||||
abr = new AppBundleRewriter (this);
|
||||
return abr;
|
||||
}
|
||||
}
|
||||
|
||||
// This dictionary contains information about the trampolines created for each assembly.
|
||||
public AssemblyTrampolineInfos AssemblyTrampolineInfos = new ();
|
||||
|
||||
internal PInvokeWrapperGenerator? PInvokeWrapperGenerationState;
|
||||
|
||||
public static bool TryGetInstance (LinkContext context, [NotNullWhen (true)] out LinkerConfiguration? configuration)
|
||||
|
@ -503,6 +516,15 @@ namespace Xamarin.Linker {
|
|||
// ErrorHelper.Show will print our errors and warnings to stderr.
|
||||
ErrorHelper.Show (list);
|
||||
}
|
||||
|
||||
public IEnumerable<AssemblyDefinition> GetNonDeletedAssemblies (BaseStep step)
|
||||
{
|
||||
foreach (var assembly in Assemblies) {
|
||||
if (step.Annotations.GetAction (assembly) == Mono.Linker.AssemblyAction.Delete)
|
||||
continue;
|
||||
yield return assembly;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,3 +34,7 @@ $(DOTNET_DIRECTORIES):
|
|||
|
||||
all-local:: $(DOTNET_TARGETS)
|
||||
install-local:: $(DOTNET_TARGETS)
|
||||
|
||||
VSCODE?="/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"
|
||||
vscode:
|
||||
PATH=$(DOTNET_DIR):$(PATH) $(VSCODE) .
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
|
||||
using Mono.Cecil;
|
||||
using Mono.Linker.Steps;
|
||||
using Xamarin.Tuner;
|
||||
|
||||
using Xamarin.Bundler;
|
||||
|
||||
|
@ -16,16 +17,25 @@ namespace Xamarin.Linker {
|
|||
get { return LinkerConfiguration.GetInstance (Context); }
|
||||
}
|
||||
|
||||
protected void Report (Exception exception)
|
||||
{
|
||||
LinkerConfiguration.Report (Context, exception);
|
||||
public DerivedLinkContext DerivedLinkContext {
|
||||
get { return Configuration.DerivedLinkContext; }
|
||||
}
|
||||
|
||||
protected void Report (List<Exception> exceptions)
|
||||
public Application App {
|
||||
get { return DerivedLinkContext.App; }
|
||||
}
|
||||
|
||||
protected void Report (params Exception [] exceptions)
|
||||
{
|
||||
// Maybe there's a better way to show errors that integrates with the linker?
|
||||
// We can't just throw an exception or exit here, since there might be only warnings in the list of exceptions.
|
||||
ErrorHelper.Show (exceptions);
|
||||
Report ((IList<Exception>) exceptions);
|
||||
}
|
||||
|
||||
protected void Report (IList<Exception>? exceptions)
|
||||
{
|
||||
if (exceptions is null)
|
||||
return;
|
||||
|
||||
LinkerConfiguration.Report (Context, exceptions);
|
||||
}
|
||||
|
||||
protected sealed override void Process ()
|
||||
|
@ -49,7 +59,8 @@ namespace Xamarin.Linker {
|
|||
protected sealed override void EndProcess ()
|
||||
{
|
||||
try {
|
||||
TryEndProcess ();
|
||||
TryEndProcess (out var exceptions);
|
||||
Report (exceptions);
|
||||
} catch (Exception e) {
|
||||
Report (FailEnd (e));
|
||||
}
|
||||
|
@ -68,6 +79,12 @@ namespace Xamarin.Linker {
|
|||
{
|
||||
}
|
||||
|
||||
protected virtual void TryEndProcess (out List<Exception>? exceptions)
|
||||
{
|
||||
exceptions = null;
|
||||
TryEndProcess ();
|
||||
}
|
||||
|
||||
// failure overrides, with defaults
|
||||
|
||||
bool CollectProductExceptions (Exception e, [NotNullWhen (true)] out List<ProductException>? productExceptions)
|
||||
|
@ -87,55 +104,39 @@ namespace Xamarin.Linker {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected virtual Exception Fail (AssemblyDefinition assembly, Exception e)
|
||||
protected virtual Exception [] Fail (AssemblyDefinition assembly, Exception e)
|
||||
{
|
||||
// Detect if we're reporting one or more ProductExceptions (and no other exceptions), and in that case
|
||||
// report the product exceptions as top-level exceptions + the step-specific exception at the end,
|
||||
// instead of the step-specific exception with all the other exceptions as an inner exception.
|
||||
// This makes the errors show up nicer in the output.
|
||||
if (CollectProductExceptions (e, out var productExceptions)) {
|
||||
// don't add inner exception
|
||||
var ex = ErrorHelper.CreateError (ErrorCode, Errors.MX_ConfigurationAwareStepWithAssembly, Name, assembly?.FullName, e.Message);
|
||||
// instead return an aggregate exception with the original exception and all the ProductExceptions we're reporting.
|
||||
productExceptions.Add (ex);
|
||||
return new AggregateException (productExceptions);
|
||||
}
|
||||
|
||||
return ErrorHelper.CreateError (ErrorCode, e, Errors.MX_ConfigurationAwareStepWithAssembly, Name, assembly?.FullName, e.Message);
|
||||
return CollectExceptions (e, () => ErrorHelper.CreateError (ErrorCode, Errors.MX_ConfigurationAwareStepWithAssembly, Name, assembly?.FullName, e.Message));
|
||||
}
|
||||
|
||||
protected virtual Exception Fail (Exception e)
|
||||
protected virtual Exception [] Fail (Exception e)
|
||||
{
|
||||
// Detect if we're reporting one or more ProductExceptions (and no other exceptions), and in that case
|
||||
// report the product exceptions as top-level exceptions + the step-specific exception at the end,
|
||||
// instead of the step-specific exception with all the other exceptions as an inner exception.
|
||||
// This makes the errors show up nicer in the output.
|
||||
if (CollectProductExceptions (e, out var productExceptions)) {
|
||||
// don't add inner exception
|
||||
var ex = ErrorHelper.CreateError (ErrorCode | 1, Errors.MX_ConfigurationAwareStep, Name, e.Message);
|
||||
// instead return an aggregate exception with the original exception and all the ProductExceptions we're reporting.
|
||||
productExceptions.Add (ex);
|
||||
return new AggregateException (productExceptions);
|
||||
}
|
||||
|
||||
return ErrorHelper.CreateError (ErrorCode | 1, e, Errors.MX_ConfigurationAwareStep, Name, e.Message);
|
||||
return CollectExceptions (e, () => ErrorHelper.CreateError (ErrorCode | 1, Errors.MX_ConfigurationAwareStep, Name, e.Message));
|
||||
}
|
||||
|
||||
protected virtual Exception FailEnd (Exception e)
|
||||
protected virtual Exception [] FailEnd (Exception e)
|
||||
{
|
||||
return CollectExceptions (e, () => ErrorHelper.CreateError (ErrorCode | 2, Errors.MX_ConfigurationAwareStep, Name, e.Message));
|
||||
}
|
||||
|
||||
Exception [] CollectExceptions (Exception e, Func<ProductException> createException)
|
||||
{
|
||||
// Detect if we're reporting one or more ProductExceptions (and no other exceptions), and in that case
|
||||
// report the product exceptions as top-level exceptions + the step-specific exception at the end,
|
||||
// instead of the step-specific exception with all the other exceptions as an inner exception.
|
||||
// This makes the errors show up nicer in the output.
|
||||
// If we're only reporting warnings, then don't add the step-specific exception at all.
|
||||
if (CollectProductExceptions (e, out var productExceptions)) {
|
||||
// don't add inner exception
|
||||
var ex = ErrorHelper.CreateError (ErrorCode | 2, Errors.MX_ConfigurationAwareStep, Name, e.Message);
|
||||
// instead return an aggregate exception with the original exception and all the ProductExceptions we're reporting.
|
||||
productExceptions.Add (ex);
|
||||
return new AggregateException (productExceptions);
|
||||
if (productExceptions.Any (v => v.Error)) {
|
||||
var ex = createException ();
|
||||
// instead return an aggregate exception with the original exception and all the ProductExceptions we're reporting.
|
||||
productExceptions.Add (ex);
|
||||
}
|
||||
return productExceptions.ToArray ();
|
||||
}
|
||||
|
||||
return ErrorHelper.CreateError (ErrorCode | 2, e, Errors.MX_ConfigurationAwareStep, Name, e.Message);
|
||||
return new Exception [] { createException () };
|
||||
}
|
||||
|
||||
// abstracts
|
||||
|
|
|
@ -0,0 +1,491 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Xamarin.Bundler;
|
||||
using Xamarin.Utils;
|
||||
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using Mono.Linker;
|
||||
using Mono.Tuner;
|
||||
|
||||
using Registrar;
|
||||
using System.Globalization;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Xamarin.Linker {
|
||||
// This type will generate the lookup code to:
|
||||
// * Convert between types and their type IDs.
|
||||
// * Map between a protocol interface and its wrapper type.
|
||||
// * Find the UnmanagedCallersOnly method for a given method ID.
|
||||
// This must be done after the linker has trimmed away any unused code,
|
||||
// because otherwise the lookup code would reference (and thus keep)
|
||||
// every exported type and method.
|
||||
public class ManagedRegistrarLookupTablesStep : ConfigurationAwareStep {
|
||||
class TypeData {
|
||||
public TypeReference Reference;
|
||||
public TypeDefinition Definition;
|
||||
|
||||
public TypeData (TypeReference reference, TypeDefinition definition)
|
||||
{
|
||||
Reference = reference;
|
||||
Definition = definition;
|
||||
}
|
||||
}
|
||||
|
||||
protected override string Name { get; } = "ManagedRegistrarLookupTables";
|
||||
protected override int ErrorCode { get; } = 2440;
|
||||
|
||||
AppBundleRewriter abr { get { return Configuration.AppBundleRewriter; } }
|
||||
|
||||
protected override void TryProcessAssembly (AssemblyDefinition assembly)
|
||||
{
|
||||
base.TryProcessAssembly (assembly);
|
||||
|
||||
if (App.Registrar != RegistrarMode.ManagedStatic)
|
||||
return;
|
||||
|
||||
var annotation = DerivedLinkContext.Annotations.GetCustomAnnotation ("ManagedRegistrarStep", assembly);
|
||||
var info = annotation as AssemblyTrampolineInfo;
|
||||
if (info is null)
|
||||
return;
|
||||
|
||||
abr.SetCurrentAssembly (assembly);
|
||||
|
||||
CreateRegistrarType (info);
|
||||
|
||||
abr.ClearCurrentAssembly ();
|
||||
}
|
||||
|
||||
void CreateRegistrarType (AssemblyTrampolineInfo info)
|
||||
{
|
||||
var registrarType = new TypeDefinition ("ObjCRuntime", "__Registrar__", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit);
|
||||
registrarType.BaseType = abr.System_Object;
|
||||
registrarType.Interfaces.Add (new InterfaceImplementation (abr.ObjCRuntime_IManagedRegistrar));
|
||||
abr.CurrentAssembly.MainModule.Types.Add (registrarType);
|
||||
|
||||
info.RegistrarType = registrarType;
|
||||
|
||||
// Remove any methods that were linked away
|
||||
for (var i = info.Count - 1; i >= 0; i--) {
|
||||
var tinfo = info [i];
|
||||
if (IsTrimmed (tinfo.Target))
|
||||
info.RemoveAt (i);
|
||||
}
|
||||
|
||||
info.SetIds ();
|
||||
var sorted = info.OrderBy (v => v.Id).ToList ();
|
||||
|
||||
//
|
||||
// The callback methods themselves are all public, and thus accessible from anywhere inside
|
||||
// the assembly even if the containing type is not public, as long as the containing type is not nested.
|
||||
// However, if the containing type is nested inside another type, it gets complicated.
|
||||
//
|
||||
// We have two options:
|
||||
//
|
||||
// 1. Just change the visibility on the nested type to make it visible inside the assembly.
|
||||
// 2. Add a method in the containing type (which has access to any directly nested private types) that can look up any unmanaged trampolines.
|
||||
// If the containing type is also a private nested type, when we'd have to add another method in its containing type, and so on.
|
||||
//
|
||||
// The second option is more complicated to implement than the first, so we're doing the first option. If someone
|
||||
// runs into any problems (there might be with reflection: looking up a type using the wrong visibility will fail to find that type).
|
||||
// That said, there may be all sorts of problems with reflection (we're adding methods to types,
|
||||
// any logic that depends on a type having a certain number of methods will fail for instance).
|
||||
//
|
||||
foreach (var md in sorted) {
|
||||
var declType = md.Trampoline.DeclaringType;
|
||||
while (declType.IsNested) {
|
||||
if (declType.IsNestedPrivate) {
|
||||
declType.IsNestedAssembly = true;
|
||||
} else if (declType.IsNestedFamilyAndAssembly || declType.IsNestedFamily) {
|
||||
declType.IsNestedFamilyOrAssembly = true;
|
||||
}
|
||||
declType = declType.DeclaringType;
|
||||
}
|
||||
}
|
||||
|
||||
// Add default ctor that just calls the base ctor
|
||||
var defaultCtor = registrarType.AddDefaultConstructor (abr);
|
||||
|
||||
// Create an instance of the registrar type in the module constructor,
|
||||
// and call ObjCRuntime.RegistrarHelper.Register with the instance.
|
||||
AddLoadTypeToModuleConstructor (registrarType);
|
||||
|
||||
// Compute the list of types that we need to register
|
||||
var types = GetTypesToRegister (registrarType, info);
|
||||
|
||||
GenerateLookupUnmanagedFunction (registrarType, sorted);
|
||||
GenerateLookupType (info, registrarType, types);
|
||||
GenerateLookupTypeId (info, registrarType, types);
|
||||
GenerateRegisterWrapperTypes (registrarType);
|
||||
|
||||
// Make sure the linker doesn't sweep away anything we just generated.
|
||||
Annotations.Mark (registrarType);
|
||||
foreach (var method in registrarType.Methods)
|
||||
Annotations.Mark (method);
|
||||
foreach (var iface in registrarType.Interfaces) {
|
||||
Annotations.Mark (iface);
|
||||
Annotations.Mark (iface.InterfaceType);
|
||||
Annotations.Mark (iface.InterfaceType.Resolve ());
|
||||
}
|
||||
}
|
||||
|
||||
void AddLoadTypeToModuleConstructor (TypeDefinition registrarType)
|
||||
{
|
||||
// Add a module constructor to initialize the callbacks
|
||||
var moduleType = registrarType.Module.Types.SingleOrDefault (v => v.Name == "<Module>");
|
||||
if (moduleType is null)
|
||||
throw ErrorHelper.CreateError (99, $"No <Module> type found in {registrarType.Module.Name}");
|
||||
var moduleConstructor = moduleType.GetTypeConstructor ();
|
||||
MethodBody body;
|
||||
ILProcessor il;
|
||||
if (moduleConstructor is null) {
|
||||
moduleConstructor = moduleType.AddMethod (".cctor", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.Static, abr.System_Void);
|
||||
body = moduleConstructor.CreateBody (out il);
|
||||
il.Emit (OpCodes.Ret);
|
||||
} else {
|
||||
body = moduleConstructor.Body;
|
||||
il = body.GetILProcessor ();
|
||||
}
|
||||
var lastInstruction = body.Instructions.Last ();
|
||||
|
||||
il.InsertBefore (lastInstruction, il.Create (OpCodes.Newobj, registrarType.GetDefaultInstanceConstructor ()));
|
||||
il.InsertBefore (lastInstruction, il.Create (OpCodes.Call, abr.RegistrarHelper_Register));
|
||||
}
|
||||
|
||||
List<TypeData> GetTypesToRegister (TypeDefinition registrarType, AssemblyTrampolineInfo info)
|
||||
{
|
||||
// Compute the list of types that we need to register
|
||||
var types = new List<TypeData> ();
|
||||
|
||||
// We want all the types that have been registered
|
||||
types.AddRange (StaticRegistrar.Types.Select (v => {
|
||||
var tr = v.Value.Type;
|
||||
var td = tr.Resolve ();
|
||||
return new TypeData (tr, td);
|
||||
}));
|
||||
|
||||
// We also want all the types the registrar skipped (otherwise we won't be able to generate the corresponding table of skipped types).
|
||||
foreach (var st in StaticRegistrar.SkippedTypes) {
|
||||
if (!types.Any (v => v.Reference == st.Skipped))
|
||||
types.Add (new (st.Skipped, st.Skipped.Resolve ()));
|
||||
if (!types.Any (v => v.Reference == st.Actual.Type))
|
||||
types.Add (new (st.Actual.Type, st.Actual.Type.Resolve ()));
|
||||
}
|
||||
|
||||
// Skip any types that are not defined in the current assembly
|
||||
types.RemoveAll (v => v.Definition.Module.Assembly != abr.CurrentAssembly);
|
||||
|
||||
// Skip any types that have been linked away
|
||||
types.RemoveAll (v => IsTrimmed (v.Definition));
|
||||
|
||||
// We also want all the protocol wrapper types
|
||||
foreach (var type in registrarType.Module.Types) {
|
||||
if (IsTrimmed (type))
|
||||
continue;
|
||||
var wrapperType = StaticRegistrar.GetProtocolAttributeWrapperType (type);
|
||||
if (wrapperType is null)
|
||||
continue;
|
||||
types.Add (new (wrapperType, wrapperType.Resolve ()));
|
||||
}
|
||||
|
||||
// Now create a mapping from type to index
|
||||
for (var i = 0; i < types.Count; i++)
|
||||
info.RegisterType (types [i].Definition, (uint) i);
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
bool IsTrimmed (MemberReference type)
|
||||
{
|
||||
return StaticRegistrar.IsTrimmed (type, Annotations);
|
||||
}
|
||||
|
||||
void GenerateLookupTypeId (AssemblyTrampolineInfo infos, TypeDefinition registrarType, List<TypeData> types)
|
||||
{
|
||||
var lookupTypeMethod = registrarType.AddMethod ("LookupTypeId", MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.HideBySig, abr.System_UInt32);
|
||||
var handleParameter = lookupTypeMethod.AddParameter ("handle", abr.System_RuntimeTypeHandle);
|
||||
lookupTypeMethod.Overrides.Add (abr.IManagedRegistrar_LookupTypeId);
|
||||
var body = lookupTypeMethod.CreateBody (out var il);
|
||||
|
||||
// The current implementation does something like this:
|
||||
//
|
||||
// if (RuntimeTypeHandle.Equals (handle, <ldtoken TypeA>)
|
||||
// return 0;
|
||||
// if (RuntimeTypeHandle.Equals (handle, <ldtoken TypeB>)
|
||||
// return 1;
|
||||
//
|
||||
// This can potentially be improved to do a dictionary lookup. The downside would be higher memory usage
|
||||
// (a simple implementation that's just a series of if conditions doesn't consume any dirty memory).
|
||||
// One idea could be to use a dictionary lookup if we have more than X types, and then fall back to the
|
||||
// linear search otherwise.
|
||||
|
||||
for (var i = 0; i < types.Count; i++) {
|
||||
il.Emit (OpCodes.Ldarga_S, handleParameter);
|
||||
il.Emit (OpCodes.Ldtoken, types [i].Reference);
|
||||
il.Emit (OpCodes.Call, abr.RuntimeTypeHandle_Equals);
|
||||
var falseTarget = il.Create (OpCodes.Nop);
|
||||
il.Emit (OpCodes.Brfalse_S, falseTarget);
|
||||
il.Emit (OpCodes.Ldc_I4, i);
|
||||
il.Emit (OpCodes.Ret);
|
||||
il.Append (falseTarget);
|
||||
}
|
||||
|
||||
// No match, return -1
|
||||
il.Emit (OpCodes.Ldc_I4_M1);
|
||||
il.Emit (OpCodes.Ret);
|
||||
}
|
||||
|
||||
void GenerateLookupType (AssemblyTrampolineInfo infos, TypeDefinition registrarType, List<TypeData> types)
|
||||
{
|
||||
var lookupTypeMethod = registrarType.AddMethod ("LookupType", MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.HideBySig, abr.System_RuntimeTypeHandle);
|
||||
lookupTypeMethod.AddParameter ("id", abr.System_UInt32);
|
||||
lookupTypeMethod.Overrides.Add (abr.IManagedRegistrar_LookupType);
|
||||
var body = lookupTypeMethod.CreateBody (out var il);
|
||||
|
||||
// We know all the IDs are contiguous, so we can just do a switch statement.
|
||||
//
|
||||
// The current implementation does something like this:
|
||||
// switch (id) {
|
||||
// case 0: return <ldtoken TYPE1>;
|
||||
// case 1: return <ldtoken TYPE2>;
|
||||
// default: return default (RuntimeTypeHandle);
|
||||
// }
|
||||
|
||||
var targets = new Instruction [types.Count];
|
||||
|
||||
for (var i = 0; i < targets.Length; i++) {
|
||||
targets [i] = Instruction.Create (OpCodes.Ldtoken, types [i].Reference);
|
||||
var td = types [i].Definition;
|
||||
if (IsTrimmed (td))
|
||||
throw ErrorHelper.CreateError (99, $"Trying to add the type {td.FullName} to the registrar's lookup tables, but it's been trimmed away.");
|
||||
}
|
||||
|
||||
il.Emit (OpCodes.Ldarg_1);
|
||||
il.Emit (OpCodes.Switch, targets);
|
||||
for (var i = 0; i < targets.Length; i++) {
|
||||
il.Append (targets [i]);
|
||||
il.Emit (OpCodes.Ret);
|
||||
}
|
||||
|
||||
// return default (RuntimeTypeHandle)
|
||||
var temporary = body.AddVariable (abr.System_RuntimeTypeHandle);
|
||||
il.Emit (OpCodes.Ldloca, temporary);
|
||||
il.Emit (OpCodes.Initobj, abr.System_RuntimeTypeHandle);
|
||||
il.Emit (OpCodes.Ldloc, temporary);
|
||||
il.Emit (OpCodes.Ret);
|
||||
}
|
||||
|
||||
void GenerateRegisterWrapperTypes (TypeDefinition type)
|
||||
{
|
||||
var method = type.AddMethod ("RegisterWrapperTypes", MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.HideBySig, abr.System_Void);
|
||||
method.AddParameter ("type", abr.System_Collections_Generic_Dictionary2.CreateGenericInstanceType (abr.System_RuntimeTypeHandle, abr.System_RuntimeTypeHandle));
|
||||
method.Overrides.Add (abr.IManagedRegistrar_RegisterWrapperTypes);
|
||||
method.CreateBody (out var il);
|
||||
|
||||
// Find all the protocol interfaces that are defined in the current assembly, and their corresponding wrapper type,
|
||||
// and add the pair to the dictionary.
|
||||
var addMethodReference = abr.System_Collections_Generic_Dictionary2.CreateMethodReferenceOnGenericType (abr.Dictionary2_Add, abr.System_RuntimeTypeHandle, abr.System_RuntimeTypeHandle);
|
||||
var currentTypes = StaticRegistrar.Types.Where (v => v.Value.Type.Resolve ().Module.Assembly == abr.CurrentAssembly);
|
||||
foreach (var ct in currentTypes) {
|
||||
if (!ct.Value.IsProtocol)
|
||||
continue;
|
||||
if (ct.Value.ProtocolWrapperType is null)
|
||||
continue;
|
||||
|
||||
// If both the protocol interface type and the wrapper type are trimmed away, skip them.
|
||||
var keyMarked = !IsTrimmed (ct.Key.Resolve ());
|
||||
var wrapperTypeMarked = !IsTrimmed (ct.Value.ProtocolWrapperType.Resolve ());
|
||||
if (!keyMarked && !wrapperTypeMarked)
|
||||
continue;
|
||||
// If only one of them is trimmed, throw an error
|
||||
if (keyMarked ^ wrapperTypeMarked)
|
||||
throw ErrorHelper.CreateError (99, $"Mismatched trimming results between the protocol interface {ct.Key.FullName} (trimmed: {!keyMarked}) and its wrapper type {ct.Value.ProtocolWrapperType.FullName} (trimmed: {!wrapperTypeMarked})");
|
||||
|
||||
il.Emit (OpCodes.Ldarg_1);
|
||||
il.Emit (OpCodes.Ldtoken, type.Module.ImportReference (ct.Key));
|
||||
il.Emit (OpCodes.Ldtoken, type.Module.ImportReference (ct.Value.ProtocolWrapperType));
|
||||
il.Emit (OpCodes.Call, addMethodReference);
|
||||
}
|
||||
|
||||
il.Emit (OpCodes.Ret);
|
||||
}
|
||||
|
||||
void GenerateLookupUnmanagedFunction (TypeDefinition registrar_type, IList<TrampolineInfo> trampolineInfos)
|
||||
{
|
||||
MethodDefinition? lookupMethods = null;
|
||||
if (App.IsAOTCompiled (abr.CurrentAssembly.Name.Name)) {
|
||||
// Don't generate lookup code, because native code will call the EntryPoint for the UnmanagedCallerOnly methods directly.
|
||||
Driver.Log (9, $"Not generating method lookup code for {abr.CurrentAssembly.Name.Name}, because it's AOT compiled");
|
||||
} else if (trampolineInfos.Count > 0) {
|
||||
// All the methods in a given assembly will have consecutive IDs (but might not start at 0).
|
||||
if (trampolineInfos.First ().Id + trampolineInfos.Count - 1 != trampolineInfos.Last ().Id)
|
||||
throw ErrorHelper.CreateError (99, $"Invalid ID range: {trampolineInfos.First ().Id} + {trampolineInfos.Count - 1} != {trampolineInfos.Last ().Id}");
|
||||
|
||||
const int methodsPerLevel = 10;
|
||||
var levels = (int) Math.Ceiling (Math.Log (trampolineInfos.Count, methodsPerLevel));
|
||||
levels = levels == 0 ? 1 : levels;
|
||||
GenerateLookupMethods (registrar_type, trampolineInfos, methodsPerLevel, 1, levels, 0, trampolineInfos.Count - 1, out lookupMethods);
|
||||
}
|
||||
|
||||
var method = registrar_type.AddMethod ("LookupUnmanagedFunction", MethodAttributes.Private | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.HideBySig, abr.System_IntPtr);
|
||||
method.AddParameter ("symbol", abr.System_String);
|
||||
method.AddParameter ("id", abr.System_Int32);
|
||||
method.Overrides.Add (abr.IManagedRegistrar_LookupUnmanagedFunction);
|
||||
method.CreateBody (out var il);
|
||||
if (lookupMethods is null) {
|
||||
il.Emit (OpCodes.Ldc_I4_M1);
|
||||
il.Emit (OpCodes.Conv_I);
|
||||
} else {
|
||||
il.Emit (OpCodes.Ldarg_1);
|
||||
il.Emit (OpCodes.Ldarg_2);
|
||||
il.Emit (OpCodes.Call, lookupMethods);
|
||||
}
|
||||
il.Emit (OpCodes.Ret);
|
||||
}
|
||||
|
||||
// If WrappedLook is true we'll wrap the ldftn instruction in a separate method, which can be useful for debugging,
|
||||
// because if there's a problem with the IL in the lookup method, it can be hard to figure out which ldftn
|
||||
// instruction is causing the problem (because the JIT will just fail when compiling the method, not necessarily
|
||||
// saying which instruction is broken).
|
||||
static bool? wrappedLookup;
|
||||
static bool WrappedLookup {
|
||||
get {
|
||||
if (!wrappedLookup.HasValue)
|
||||
wrappedLookup = !string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("XAMARIN_MSR_WRAPPED_LOOKUP"));
|
||||
return wrappedLookup.Value;
|
||||
}
|
||||
}
|
||||
MethodDefinition GenerateLookupMethods (TypeDefinition type, IList<TrampolineInfo> trampolineInfos, int methodsPerLevel, int level, int levels, int startIndex, int endIndex, out MethodDefinition method)
|
||||
{
|
||||
if (startIndex > endIndex)
|
||||
throw ErrorHelper.CreateError (99, $"startIndex ({startIndex}) can't be higher than endIndex ({endIndex})");
|
||||
|
||||
var startId = trampolineInfos [startIndex].Id;
|
||||
var name = level == 1 ? "LookupUnmanagedFunctionImpl" : $"LookupUnmanagedFunction_{level}_{levels}__{startIndex}_{endIndex}__";
|
||||
method = type.AddMethod (name, MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, abr.System_IntPtr);
|
||||
method.AddParameter ("symbol", abr.System_String);
|
||||
method.AddParameter ("id", abr.System_Int32);
|
||||
method.CreateBody (out var il);
|
||||
|
||||
if (level == levels) {
|
||||
// This is the leaf method where we do the actual lookup.
|
||||
//
|
||||
// The code is something like this:
|
||||
// switch (id - <startId>) {
|
||||
// case 0: return <ldftn for first method>;
|
||||
// case 1: return <ldftn for second method>;
|
||||
// case <methodsPerLevel - 1>: return <ldftn for last method>;
|
||||
// default: return -1;
|
||||
// }
|
||||
var targetCount = endIndex - startIndex + 1;
|
||||
var targets = new Instruction [targetCount];
|
||||
for (var i = 0; i < targets.Length; i++) {
|
||||
var ti = trampolineInfos [startIndex + i];
|
||||
var md = ti.Trampoline;
|
||||
var mr = abr.CurrentAssembly.MainModule.ImportReference (md);
|
||||
if (WrappedLookup) {
|
||||
var wrappedLookup = type.AddMethod (name + ti.Id, MethodAttributes.Private | MethodAttributes.Static | MethodAttributes.HideBySig, abr.System_IntPtr);
|
||||
wrappedLookup.CreateBody (out var wrappedIl);
|
||||
wrappedIl.Emit (OpCodes.Ldftn, mr);
|
||||
wrappedIl.Emit (OpCodes.Ret);
|
||||
|
||||
targets [i] = Instruction.Create (OpCodes.Call, wrappedLookup);
|
||||
} else {
|
||||
targets [i] = Instruction.Create (OpCodes.Ldftn, mr);
|
||||
}
|
||||
}
|
||||
|
||||
il.Emit (OpCodes.Ldarg_1);
|
||||
if (startId != 0) {
|
||||
il.Emit (OpCodes.Ldc_I4, startId);
|
||||
il.Emit (OpCodes.Sub_Ovf_Un);
|
||||
}
|
||||
il.Emit (OpCodes.Switch, targets);
|
||||
for (var k = 0; k < targetCount; k++) {
|
||||
il.Append (targets [k]);
|
||||
il.Emit (OpCodes.Ret);
|
||||
}
|
||||
} else {
|
||||
// This is an intermediate method to not have too many ldftn instructions in a single method (it takes a long time to JIT).
|
||||
var chunkSize = (int) Math.Pow (methodsPerLevel, levels - level);
|
||||
|
||||
// Some validation
|
||||
if (level == 1) {
|
||||
if (chunkSize * methodsPerLevel < trampolineInfos.Count)
|
||||
throw ErrorHelper.CreateError (99, $"chunkSize ({chunkSize}) * methodsPerLevel ({methodsPerLevel}) < trampolineInfos.Count {trampolineInfos.Count}");
|
||||
}
|
||||
|
||||
// The code is something like this:
|
||||
// switch ((id - <startId>) / <chunkSize>) {
|
||||
// case 0: return Lookup_1_2__0_10__ (symbol, id);
|
||||
// case 1: return Lookup_1_2__11_20__ (symbol, id);
|
||||
// case ...
|
||||
// default: return -1;
|
||||
// }
|
||||
|
||||
var count = endIndex - startIndex + 1;
|
||||
var chunks = (int) Math.Ceiling (count / (double) chunkSize);
|
||||
var targets = new Instruction [chunks];
|
||||
|
||||
var lookupMethods = new MethodDefinition [targets.Length];
|
||||
for (var i = 0; i < targets.Length; i++) {
|
||||
var subStartIndex = startIndex + (chunkSize) * i;
|
||||
var subEndIndex = subStartIndex + (chunkSize) - 1;
|
||||
if (subEndIndex > endIndex)
|
||||
subEndIndex = endIndex;
|
||||
var md = GenerateLookupMethods (type, trampolineInfos, methodsPerLevel, level + 1, levels, subStartIndex, subEndIndex, out _);
|
||||
lookupMethods [i] = md;
|
||||
targets [i] = Instruction.Create (OpCodes.Ldarg_0);
|
||||
}
|
||||
|
||||
il.Emit (OpCodes.Ldarg_1);
|
||||
if (startId != 0) {
|
||||
il.Emit (OpCodes.Ldc_I4, startId);
|
||||
il.Emit (OpCodes.Sub_Ovf_Un);
|
||||
}
|
||||
il.Emit (OpCodes.Ldc_I4, chunkSize);
|
||||
il.Emit (OpCodes.Div);
|
||||
il.Emit (OpCodes.Switch, targets);
|
||||
for (var k = 0; k < targets.Length; k++) {
|
||||
il.Append (targets [k]); // OpCodes.Ldarg_0
|
||||
il.Emit (OpCodes.Ldarg_1);
|
||||
il.Emit (OpCodes.Call, lookupMethods [k]);
|
||||
il.Emit (OpCodes.Ret);
|
||||
}
|
||||
}
|
||||
|
||||
// no hit? this shouldn't happen
|
||||
il.Emit (OpCodes.Ldc_I4_M1);
|
||||
il.Emit (OpCodes.Conv_I);
|
||||
il.Emit (OpCodes.Ret);
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
static string GetMethodSignature (MethodDefinition method)
|
||||
{
|
||||
return $"{method?.ReturnType?.FullName ?? "(null)"} {method?.DeclaringType?.FullName ?? "(null)"}::{method?.Name ?? "(null)"} ({string.Join (", ", method?.Parameters?.Select (v => v?.ParameterType?.FullName + " " + v?.Name) ?? Array.Empty<string> ())})";
|
||||
}
|
||||
|
||||
void EnsureVisible (MethodDefinition caller, TypeDefinition type)
|
||||
{
|
||||
if (type.IsNested) {
|
||||
type.IsNestedPublic = true;
|
||||
EnsureVisible (caller, type.DeclaringType);
|
||||
} else {
|
||||
type.IsPublic = true;
|
||||
}
|
||||
}
|
||||
|
||||
StaticRegistrar StaticRegistrar {
|
||||
get { return DerivedLinkContext.StaticRegistrar; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -30,15 +30,19 @@ namespace Xamarin.Linker {
|
|||
Configuration.CompilerFlags.AddLinkWith (Configuration.PartialStaticRegistrarLibrary);
|
||||
break;
|
||||
case RegistrarMode.Static:
|
||||
Configuration.Target.StaticRegistrar.Register (Configuration.GetNonDeletedAssemblies (this));
|
||||
goto case RegistrarMode.ManagedStatic;
|
||||
case RegistrarMode.ManagedStatic:
|
||||
var dir = Configuration.CacheDirectory;
|
||||
var header = Path.Combine (dir, "registrar.h");
|
||||
var code = Path.Combine (dir, "registrar.mm");
|
||||
var bundled_assemblies = new List<AssemblyDefinition> ();
|
||||
foreach (var assembly in Configuration.Assemblies) {
|
||||
if (Annotations.GetAction (assembly) != Mono.Linker.AssemblyAction.Delete)
|
||||
bundled_assemblies.Add (assembly);
|
||||
if (app.Registrar == RegistrarMode.ManagedStatic) {
|
||||
// Every api has been registered if we're using the managed registrar
|
||||
// (since we registered types before the trimmer did anything),
|
||||
// so we need to remove those that were later trimmed away by the trimmer.
|
||||
Configuration.Target.StaticRegistrar.FilterTrimmedApi (Annotations);
|
||||
}
|
||||
Configuration.Target.StaticRegistrar.Generate (bundled_assemblies, header, code, out var initialization_method, app.ClassMapPath);
|
||||
Configuration.Target.StaticRegistrar.Generate (header, code, out var initialization_method, app.ClassMapPath);
|
||||
|
||||
var items = new List<MSBuildItem> ();
|
||||
foreach (var abi in Configuration.Abis) {
|
||||
|
|
|
@ -31,6 +31,13 @@ namespace Xamarin.Linker.Steps {
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case "Foundation":
|
||||
switch (attr_type.Name) {
|
||||
case "ProtocolAttribute":
|
||||
store = LinkContext.App.Optimizations.RegisterProtocols == true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (store)
|
||||
|
|
|
@ -938,8 +938,14 @@ namespace Xamarin.Linker {
|
|||
if (!mr.DeclaringType.Is (Namespaces.ObjCRuntime, "BlockLiteral"))
|
||||
return 0;
|
||||
|
||||
if (caller.Name == "GetBlockForDelegate" && caller.DeclaringType.Is ("ObjCRuntime", "BlockLiteral"))
|
||||
return 0; // BlockLiteral.GetBlockForDelegate contains a non-optimizable call to SetupBlock, and this way we don't show any warnings to users about things they can't do anything about.
|
||||
if (caller.DeclaringType.Is ("ObjCRuntime", "BlockLiteral")) {
|
||||
switch (caller.Name) {
|
||||
case "GetBlockForDelegate":
|
||||
case "CreateBlockForDelegate":
|
||||
// These methods contain a non-optimizable call to SetupBlock, and this way we don't show any warnings to users about things they can't do anything about.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
string signature = null;
|
||||
try {
|
||||
|
@ -986,33 +992,11 @@ namespace Xamarin.Linker {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Calculate the block signature.
|
||||
var blockSignature = false;
|
||||
MethodReference userMethod = null;
|
||||
|
||||
// First look for any [UserDelegateType] attributes on the trampoline delegate type.
|
||||
var userDelegateType = GetUserDelegateType (trampolineDelegateType);
|
||||
if (userDelegateType is not null) {
|
||||
var userMethodDefinition = GetDelegateInvoke (userDelegateType);
|
||||
userMethod = InflateMethod (userDelegateType, userMethodDefinition);
|
||||
blockSignature = true;
|
||||
} else {
|
||||
// Couldn't find a [UserDelegateType] attribute, use the type of the actual trampoline instead.
|
||||
var userMethodDefinition = GetDelegateInvoke (trampolineDelegateType);
|
||||
userMethod = InflateMethod (trampolineDelegateType, userMethodDefinition);
|
||||
blockSignature = false;
|
||||
}
|
||||
|
||||
// No luck finding the signature, so give up.
|
||||
if (userMethod is null) {
|
||||
ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, caller, ins, Errors.MM2106_C, caller, ins.Offset, trampolineDelegateType.FullName));
|
||||
if (!LinkContext.Target.StaticRegistrar.TryComputeBlockSignature (caller, trampolineDelegateType, out var exception, out signature)) {
|
||||
ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, exception, caller, ins, Errors.MM2106_D, caller, ins.Offset, exception.Message));
|
||||
return 0;
|
||||
}
|
||||
|
||||
var parameters = new TypeReference [userMethod.Parameters.Count];
|
||||
for (int p = 0; p < parameters.Length; p++)
|
||||
parameters [p] = userMethod.Parameters [p].ParameterType;
|
||||
signature = LinkContext.Target.StaticRegistrar.ComputeSignature (userMethod.DeclaringType, false, userMethod.ReturnType, parameters, userMethod.Resolve (), isBlockSignature: blockSignature);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ErrorHelper.Show (ErrorHelper.CreateWarning (LinkContext.App, 2106, e, caller, ins, Errors.MM2106_D, caller, ins.Offset, e.Message));
|
||||
return 0;
|
||||
|
@ -1309,29 +1293,6 @@ namespace Xamarin.Linker {
|
|||
}
|
||||
}
|
||||
|
||||
// Find the value of the [UserDelegateType] attribute on the specified delegate
|
||||
TypeReference GetUserDelegateType (TypeReference delegateType)
|
||||
{
|
||||
var delegateTypeDefinition = delegateType.Resolve ();
|
||||
foreach (var attrib in delegateTypeDefinition.CustomAttributes) {
|
||||
var attribType = attrib.AttributeType;
|
||||
if (!attribType.Is (Namespaces.ObjCRuntime, "UserDelegateTypeAttribute"))
|
||||
continue;
|
||||
return attrib.ConstructorArguments [0].Value as TypeReference;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodDefinition GetDelegateInvoke (TypeReference delegateType)
|
||||
{
|
||||
var td = delegateType.Resolve ();
|
||||
foreach (var method in td.Methods) {
|
||||
if (method.Name == "Invoke")
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodDefinition setupblock_def;
|
||||
MethodReference GetBlockSetupImpl (MethodDefinition caller, Instruction ins)
|
||||
{
|
||||
|
@ -1376,23 +1337,5 @@ namespace Xamarin.Linker {
|
|||
}
|
||||
return caller.Module.ImportReference (block_ctor_def);
|
||||
}
|
||||
|
||||
MethodReference InflateMethod (TypeReference inflatedDeclaringType, MethodDefinition openMethod)
|
||||
{
|
||||
var git = inflatedDeclaringType as GenericInstanceType;
|
||||
if (git is null)
|
||||
return openMethod;
|
||||
|
||||
var inflatedReturnType = TypeReferenceExtensions.InflateGenericType (git, openMethod.ReturnType);
|
||||
var mr = new MethodReference (openMethod.Name, inflatedReturnType, git);
|
||||
if (openMethod.HasParameters) {
|
||||
for (int i = 0; i < openMethod.Parameters.Count; i++) {
|
||||
var inflatedParameterType = TypeReferenceExtensions.InflateGenericType (git, openMethod.Parameters [i].ParameterType);
|
||||
var p = new ParameterDefinition (openMethod.Parameters [i].Name, openMethod.Parameters [i].Attributes, inflatedParameterType);
|
||||
mr.Parameters.Add (p);
|
||||
}
|
||||
}
|
||||
return mr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,17 @@ namespace Xamarin.Linker {
|
|||
return provider.ToString ();
|
||||
}
|
||||
|
||||
public static bool HasCustomAttribute (this ICustomAttributeProvider provider, DerivedLinkContext context, string @namespace, string name, out ICustomAttribute attrib)
|
||||
{
|
||||
attrib = null;
|
||||
if (provider?.HasCustomAttribute (@namespace, name, out attrib) == true)
|
||||
return true;
|
||||
|
||||
var attribs = context?.GetCustomAttributes (provider, @namespace, name);
|
||||
attrib = attribs?.FirstOrDefault ();
|
||||
return attrib is not null;
|
||||
|
||||
}
|
||||
// This method will look in any stored attributes in the link context as well as the provider itself.
|
||||
public static bool HasCustomAttribute (this ICustomAttributeProvider provider, DerivedLinkContext context, string @namespace, string name)
|
||||
{
|
||||
|
@ -34,13 +45,22 @@ namespace Xamarin.Linker {
|
|||
|
||||
public static bool HasCustomAttribute (this ICustomAttributeProvider provider, string @namespace, string name)
|
||||
{
|
||||
return HasCustomAttribute (provider, @namespace, name, out _);
|
||||
}
|
||||
|
||||
public static bool HasCustomAttribute (this ICustomAttributeProvider provider, string @namespace, string name, out ICustomAttribute attrib)
|
||||
{
|
||||
attrib = null;
|
||||
|
||||
if (provider is null || !provider.HasCustomAttributes)
|
||||
return false;
|
||||
|
||||
foreach (CustomAttribute attribute in provider.CustomAttributes) {
|
||||
TypeReference tr = attribute.Constructor.DeclaringType;
|
||||
if (tr.Is (@namespace, name))
|
||||
if (tr.Is (@namespace, name)) {
|
||||
attrib = attribute;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -337,16 +337,6 @@ namespace Xamarin.Bundler {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Could not optimize the call to BlockLiteral.SetupBlock in {0} at offset {1} because no [UserDelegateType] attribute could be found on {2}.
|
||||
/// .
|
||||
/// </summary>
|
||||
public static string MM2106_C {
|
||||
get {
|
||||
return ResourceManager.GetString("MM2106_C", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Could not optimize the call to BlockLiteral.SetupBlock in {0} at offset {1}: {2}.
|
||||
/// .
|
||||
|
@ -4006,6 +3996,24 @@ namespace Xamarin.Bundler {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Could not find a [UserDelegateType] attribute on the type '{0}'..
|
||||
/// </summary>
|
||||
public static string MX4187 {
|
||||
get {
|
||||
return ResourceManager.GetString("MX4187", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unable to compute the block signature for the type '{0}': {1}.
|
||||
/// </summary>
|
||||
public static string MX4188 {
|
||||
get {
|
||||
return ResourceManager.GetString("MX4188", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The native linker failed to execute: {0}. Please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new
|
||||
/// .
|
||||
|
@ -4257,5 +4265,32 @@ namespace Xamarin.Bundler {
|
|||
return ResourceManager.GetString("MX8052", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Could not resolve the module in the assembly {0}..
|
||||
/// </summary>
|
||||
public static string MX8053 {
|
||||
get {
|
||||
return ResourceManager.GetString("MX8053", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Can't resolve metadata tokens for methods when using the managed static registrar (token: 0x{0})..
|
||||
/// </summary>
|
||||
public static string MX8054 {
|
||||
get {
|
||||
return ResourceManager.GetString("MX8054", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Could not find the type 'ObjCRuntime.__Registrar__' in the assembly '{0}'..
|
||||
/// </summary>
|
||||
public static string MX8055 {
|
||||
get {
|
||||
return ResourceManager.GetString("MX8055", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1289,11 +1289,6 @@
|
|||
</value>
|
||||
</data>
|
||||
|
||||
<data name="MM2106_C" xml:space="preserve">
|
||||
<value>Could not optimize the call to BlockLiteral.SetupBlock in {0} at offset {1} because no [UserDelegateType] attribute could be found on {2}.
|
||||
</value>
|
||||
</data>
|
||||
|
||||
<data name="MM2106_D" xml:space="preserve">
|
||||
<value>Could not optimize the call to BlockLiteral.SetupBlock in {0} at offset {1}: {2}.
|
||||
</value>
|
||||
|
@ -1914,6 +1909,14 @@
|
|||
<value>The registrar found a non-optimal type `{0}`: the type does not have a constructor that takes two (ObjCRuntime.NativeHandle, bool) arguments. However, a constructor that takes two (System.IntPtr, bool) arguments was found (and will be used instead). It's highly recommended to change the signature of the (System.IntPtr, bool) constructor to be (ObjCRuntime.NativeHandle, bool).</value>
|
||||
</data>
|
||||
|
||||
<data name="MX4187" xml:space="preserve">
|
||||
<value>Could not find a [UserDelegateType] attribute on the type '{0}'.</value>
|
||||
</data>
|
||||
|
||||
<data name="MX4188" xml:space="preserve">
|
||||
<value>Unable to compute the block signature for the type '{0}': {1}</value>
|
||||
</data>
|
||||
|
||||
<data name="MT5101" xml:space="preserve">
|
||||
<value>Missing '{0}' compiler. Please install Xcode 'Command-Line Tools' component
|
||||
</value>
|
||||
|
@ -2240,4 +2243,15 @@
|
|||
<value>The signature must be a non-empty string.</value>
|
||||
</data>
|
||||
|
||||
<data name="MX8053" xml:space="preserve">
|
||||
<value>Could not resolve the module in the assembly {0}.</value>
|
||||
</data>
|
||||
|
||||
<data name="MX8054" xml:space="preserve">
|
||||
<value>Can't resolve metadata tokens for methods when using the managed static registrar (token: 0x{0}).</value>
|
||||
</data>
|
||||
|
||||
<data name="MX8055" xml:space="preserve">
|
||||
<value>Could not find the type 'ObjCRuntime.__Registrar__' in the assembly '{0}'.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
Загрузка…
Ссылка в новой задаче