[net8.0] Merge main into net8.0.

This commit is contained in:
Rolf Bjarne Kvinge 2023-05-19 08:06:07 +02:00
Родитель b8baa07418 1337268bbf
Коммит ab73088c0b
55 изменённых файлов: 5730 добавлений и 561 удалений

Просмотреть файл

@ -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 \

249
src/ObjCRuntime/BindAs.cs Normal file
Просмотреть файл

@ -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

12
tools/dotnet-linker/.vscode/tasks.json поставляемый Normal file
Просмотреть файл

@ -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;
}

55
tools/mtouch/Errors.designer.cs сгенерированный
Просмотреть файл

@ -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 &apos;{0}&apos;..
/// </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 &apos;{0}&apos;: {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&apos;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 &apos;ObjCRuntime.__Registrar__&apos; in the assembly &apos;{0}&apos;..
/// </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>