Merge pull request #3495 (Implement support for optimizing away the dynamic registrar) from rolfbjarne/full-static-registrar
Implement support for optimizing away the dynamic registrar. * Add a new property (Runtime.DynamicRegistrationSupported) that indicates at runtime whether dynamic registration is available. * Check this new property whenever we need dynamic registration. * Add an optimization to mtouch/mmp that detects whether dynamic registration is required, and if not, changes Runtime.DynamicRegistrationSupported to return false. * Add an optimization to mtouch/mmp that inlines Runtime.DynamicRegistrationSupported as a constant value. The end result is that the linker will link away the dynamic registrar if mtouch/mmp detects that it's not needed. Benchmark --------- I've compared the size of entire apps built for device: |test | Before | After | Diff | % | |:-----------------------------|-------:|-------:|-------:|------:| |[monotouch-test/Release][1] | 95.7mb | 95.0mb | -680kb | -0.7% | |[link sdk/Release][2] | 21.2mb | 20.9mb | -245kb | -1.2% | |[minimalistic app/Release][3] | 4.58mb | 4.32mb | -259kb | -5.7% | [1]: https://gist.github.com/rolfbjarne/3871e36d2de8db1a8eee1d9f9276d3d2#monotouch-test-release-wall-optimizations-enabled [2]: https://gist.github.com/rolfbjarne/3871e36d2de8db1a8eee1d9f9276d3d2#link-sdk [3]: https://gist.github.com/rolfbjarne/3871e36d2de8db1a8eee1d9f9276d3d2#minimalistic-app
This commit is contained in:
Коммит
c838a11c4d
|
@ -236,6 +236,18 @@ Please file an [issue](https://github.com/xamarin/xamarin-macios/issues/new)
|
|||
along with a complete build log so that we can investigate what went wrong and
|
||||
possibly enable more scenarios in the future.
|
||||
|
||||
### <a name="MM2107"/>MM2107: It's not safe to remove the dynamic registrar because {reasons}
|
||||
|
||||
The linker reports this warning when the developer requests removal of the
|
||||
dynamic registrar (by passing `--optimize:remove-dynamic-registrar` to
|
||||
mmp), but the linker determines that it's not safe to do so.
|
||||
|
||||
To remove the warning either remove the optimization argument to mmp, or pass
|
||||
`--nowarn:2107` to ignore it.
|
||||
|
||||
By default this option will be automatically enabled whenever it's possible
|
||||
and safe to do so.
|
||||
|
||||
# MM3xxx: AOT
|
||||
|
||||
## MM30xx: AOT (general) errors
|
||||
|
@ -375,3 +387,11 @@ This indicates a bug in Xamarin.Mac. Please file a bug at [https://bugzilla.xama
|
|||
A potential workaround would be to disable the `register-protocols`
|
||||
optimization, by passing `--optimize:-register-protocols` as an additional mmp
|
||||
argument in the project's Mac Build options.
|
||||
|
||||
### <a name="MM8026"/>MM8026: * is not supported when the dynamic registrar has been linked away.
|
||||
|
||||
This usually indicates a bug in Xamarin.Mac, because the dynamic registrar should not be linked away if it's needed. Please file a bug at [https://bugzilla.xamarin.com](https://bugzilla.xamarin.com/enter_bug.cgi?product=iOS).
|
||||
|
||||
It's possible to force the linker to keep the dynamic registrar by adding
|
||||
`--optimize=-remove-dynamic-registrar` to the additional mmp arguments in
|
||||
the project's Mac Build options.
|
||||
|
|
|
@ -1311,6 +1311,18 @@ Please file an [issue](https://github.com/xamarin/xamarin-macios/issues/new)
|
|||
along with a complete build log so that we can investigate what went wrong and
|
||||
possibly enable more scenarios in the future.
|
||||
|
||||
### <a name="MT2107"/>MT2107: It's not safe to remove the dynamic registrar because {reasons}
|
||||
|
||||
The linker reports this warning when the developer requests removal of the
|
||||
dynamic registrar (by passing `--optimize:remove-dynamic-registrar` to
|
||||
mtouch), but the linker determines that it's not safe to do so.
|
||||
|
||||
To remove the warning either remove the optimization argument to mtouch, or pass
|
||||
`--nowarn:2107` to ignore it.
|
||||
|
||||
By default this option will be automatically enabled whenever it's possible
|
||||
and safe to do so.
|
||||
|
||||
# MT3xxx: AOT error messages
|
||||
|
||||
<!--
|
||||
|
@ -2384,3 +2396,11 @@ This indicates a bug in Xamarin.iOS. Please file a bug at [https://bugzilla.xama
|
|||
A potential workaround would be to disable the `register-protocols`
|
||||
optimization, by passing `--optimize:-register-protocols` as an additional
|
||||
mtouch argument in the project's iOS Build options.
|
||||
|
||||
### <a name="MT8026"/>MT8026: * is not supported when the dynamic registrar has been linked away.
|
||||
|
||||
This usually indicates a bug in Xamarin.iOS, because the dynamic registrar should not be linked away if it's needed. Please file a bug at [https://bugzilla.xamarin.com](https://bugzilla.xamarin.com/enter_bug.cgi?product=iOS).
|
||||
|
||||
It's possible to force the linker to keep the dynamic registrar by adding
|
||||
`--optimize=-remove-dynamic-registrar` to the additional mtouch arguments in
|
||||
the project's iOS Build options.
|
||||
|
|
|
@ -380,3 +380,70 @@ Xamarin.Mac supports loading assemblies dynamically, and those assemblies
|
|||
might not have been known at build time (and thus not optimized).
|
||||
|
||||
The default behavior can be overridden by passing `--optimize=-register-protocols` to mtouch/mmp.
|
||||
|
||||
Remove the dynamic registrar
|
||||
----------------------------
|
||||
|
||||
Both the Xamarin.iOS and the Xamarin.Mac runtime include support for
|
||||
[registering managed types](https://developer.xamarin.com/guides/ios/advanced_topics/registrar/)
|
||||
with the Objective-C runtime. It can either be done at build time or at
|
||||
runtime (or partially at build time and the rest at runtime), but if it's
|
||||
completely done at build time, it means the supporting code for doing it at
|
||||
runtime can be removed. This results in a significant decrease in app size, in
|
||||
particular for smaller apps such as extensions or watchOS apps.
|
||||
|
||||
This optimization requires both the static registrar and the linker to be
|
||||
enabled.
|
||||
|
||||
The linker will attempt to determine if it's safe to remove the dynamic
|
||||
registrar, and if so will try to remove it.
|
||||
|
||||
Since Xamarin.Mac supports dynamically loading assemblies at runtime (which
|
||||
were not known at build time), it's impossible to determine at build time
|
||||
whether this is a safe optimization. This means that this optimization is
|
||||
never enabled by default for Xamarin.Mac apps.
|
||||
|
||||
The default behavior can be overridden by passing `--optimize=[+|-]remove-dynamic-registrar` to mtouch/mmp.
|
||||
|
||||
If the default is overridden to remove the dynamic registrar, the linker will
|
||||
emit warnings if it detects that it's not safe (but the dynamic registrar will
|
||||
still be removed).
|
||||
|
||||
Inline Runtime.DynamicRegistrationSupported
|
||||
-------------------------------------------
|
||||
|
||||
Inlines the value of Runtime.DynamicRegistrationSupported as determined at
|
||||
build time.
|
||||
|
||||
If the dynamic registrar is removed (see the "Remove the dynamic registrar"
|
||||
optimization), this is a constant 'false' value, otherwise it's a constant
|
||||
'true' value.
|
||||
|
||||
This optimization will change the following type of code:
|
||||
|
||||
```csharp
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
Console.WriteLine ("do something");
|
||||
} else {
|
||||
throw new Exception ("dynamic registration is not supported");
|
||||
}
|
||||
```
|
||||
|
||||
into the following when the dynamic registrar is removed:
|
||||
|
||||
```csharp
|
||||
throw new Exception ("dynamic registration is not supported");
|
||||
```
|
||||
|
||||
into the following when the dynamic registrar is not removed:
|
||||
|
||||
```csharp
|
||||
Console.WriteLine ("do something");
|
||||
```
|
||||
|
||||
This optimization requires the linker to be enabled, and is only applied to
|
||||
methods with the `[BindingImpl (BindingImplOptions.Optimizable)]` attribute.
|
||||
|
||||
It is always enabled by default (when the linker is enabled).
|
||||
|
||||
The default behavior can be overridden by passing `--optimize=[+|-]inline-dynamic-registration-supported` to mtouch/mmp.
|
||||
|
|
|
@ -68,9 +68,23 @@ namespace ObjCRuntime {
|
|||
|
||||
<# } #>
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)] // To inline the Runtime.DynamicRegistrationSupported code if possible.
|
||||
static void RegisterDelegates (InitializationOptions* options)
|
||||
{
|
||||
<# foreach (var d in delegates) { #>
|
||||
<# foreach (var d in delegates) {
|
||||
if (d.OnlyDynamicUsage) continue; #>
|
||||
options->Delegates-><#= d.SimpleEntryPoint #> = GetFunctionPointer (new <#= d.SimpleEntryPoint #>_delegate (<#= d.SimpleEntryPoint #>));
|
||||
<# } #>
|
||||
|
||||
// The linker will remove this condition (and the subsequent method call) if possible
|
||||
if (DynamicRegistrationSupported)
|
||||
RegisterDelegatesDynamic (options);
|
||||
}
|
||||
|
||||
static void RegisterDelegatesDynamic (InitializationOptions* options)
|
||||
{
|
||||
<# foreach (var d in delegates) {
|
||||
if (!d.OnlyDynamicUsage) continue; #>
|
||||
options->Delegates-><#= d.SimpleEntryPoint #> = GetFunctionPointer (new <#= d.SimpleEntryPoint #>_delegate (<#= d.SimpleEntryPoint #>));
|
||||
<# } #>
|
||||
}
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
<# foreach (var d in delegates) { #>
|
||||
<# foreach (var d in delegates) {
|
||||
if (!d.OnlyDynamicUsage)
|
||||
continue;
|
||||
#>
|
||||
<#= d.CReturnType #>
|
||||
<#= d.EntryPoint #> (<#= d.CArgumentSignature #>);
|
||||
|
||||
|
|
|
@ -9,128 +9,204 @@
|
|||
new XDelegate ("void", "void", "xamarin_register_nsobject",
|
||||
"MonoObject *", "IntPtr", "managed_obj",
|
||||
"id", "IntPtr", "native_obj"
|
||||
) { WrappedManagedFunction = "RegisterNSObject" },
|
||||
) {
|
||||
WrappedManagedFunction = "RegisterNSObject",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("void", "void", "xamarin_register_assembly",
|
||||
"MonoReflectionAssembly *", "IntPtr", "assembly"
|
||||
) { WrappedManagedFunction = "RegisterAssembly" },
|
||||
) {
|
||||
WrappedManagedFunction = "RegisterAssembly",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("void", "void", "xamarin_throw_ns_exception",
|
||||
"NSException *", "IntPtr", "exc"
|
||||
) {
|
||||
WrappedManagedFunction = "ThrowNSException",
|
||||
ExceptionHandling = false,
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("void", "void", "xamarin_rethrow_managed_exception",
|
||||
"guint32", "uint", "original_exception_gchandle"
|
||||
) { WrappedManagedFunction = "RethrowManagedException" },
|
||||
) {
|
||||
WrappedManagedFunction = "RethrowManagedException",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("int", "int", "xamarin_create_ns_exception",
|
||||
"NSException *", "IntPtr", "exc"
|
||||
) { WrappedManagedFunction = "CreateNSException" },
|
||||
) {
|
||||
WrappedManagedFunction = "CreateNSException",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("NSException *", "IntPtr", "xamarin_unwrap_ns_exception",
|
||||
"int", "int", "exc_handle"
|
||||
) { WrappedManagedFunction = "UnwrapNSException" },
|
||||
) {
|
||||
WrappedManagedFunction = "UnwrapNSException",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoObject *", "IntPtr", "xamarin_get_block_wrapper_creator",
|
||||
"MonoObject *", "IntPtr", "method",
|
||||
"int", "int", "parameter"
|
||||
) { WrappedManagedFunction = "GetBlockWrapperCreator" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetBlockWrapperCreator",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoObject *", "IntPtr", "xamarin_create_block_proxy",
|
||||
"MonoObject *", "IntPtr", "method",
|
||||
"void *", "IntPtr", "block"
|
||||
) { WrappedManagedFunction = "CreateBlockProxy" },
|
||||
) {
|
||||
WrappedManagedFunction = "CreateBlockProxy",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("id", "IntPtr", "xamarin_create_delegate_proxy",
|
||||
"MonoObject *", "IntPtr", "method",
|
||||
"MonoObject *", "IntPtr", "block",
|
||||
"const char *", "IntPtr", "signature"
|
||||
) { WrappedManagedFunction = "CreateDelegateProxy" },
|
||||
) {
|
||||
WrappedManagedFunction = "CreateDelegateProxy",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("void", "void", "xamarin_register_entry_assembly",
|
||||
"MonoReflectionAssembly *", "IntPtr", "assembly"
|
||||
) { WrappedManagedFunction = "RegisterEntryAssembly" },
|
||||
) {
|
||||
WrappedManagedFunction = "RegisterEntryAssembly",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoObject *", "IntPtr", "xamarin_get_class",
|
||||
"Class", "IntPtr", "ptr"
|
||||
) { WrappedManagedFunction = "GetClass" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetClass",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoObject *", "IntPtr", "xamarin_get_selector",
|
||||
"SEL", "IntPtr", "ptr"
|
||||
) { WrappedManagedFunction = "GetSelector" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetSelector",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("void", "void", "xamarin_get_method_for_selector",
|
||||
"Class", "IntPtr", "cls",
|
||||
"SEL", "IntPtr", "sel",
|
||||
"bool", "bool", "is_static",
|
||||
"MethodDescription *", "IntPtr", "desc"
|
||||
) { WrappedManagedFunction = "GetMethodForSelector" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetMethodForSelector",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoObject *", "IntPtr", "xamarin_get_nsobject",
|
||||
"id", "IntPtr", "obj"
|
||||
) { WrappedManagedFunction = "GetNSObjectWrapped" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetNSObjectWrapped",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("bool", "bool", "xamarin_has_nsobject",
|
||||
"id", "IntPtr", "obj"
|
||||
) { WrappedManagedFunction = "HasNSObject" },
|
||||
) {
|
||||
WrappedManagedFunction = "HasNSObject",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("id", "IntPtr", "xamarin_get_handle_for_inativeobject",
|
||||
"MonoObject *", "IntPtr", "obj"
|
||||
) { WrappedManagedFunction = "GetHandleForINativeObject" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetHandleForINativeObject",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("void", "void", "xamarin_unregister_nsobject",
|
||||
"id", "IntPtr", "native_obj",
|
||||
"MonoObject *", "IntPtr", "managed_obj"
|
||||
) { WrappedManagedFunction = "UnregisterNSObject" },
|
||||
) {
|
||||
WrappedManagedFunction = "UnregisterNSObject",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoObject *", "IntPtr", "xamarin_try_get_or_construct_nsobject",
|
||||
"id", "IntPtr", "obj"
|
||||
) { WrappedManagedFunction = "TryGetOrConstructNSObjectWrapped" },
|
||||
) {
|
||||
WrappedManagedFunction = "TryGetOrConstructNSObjectWrapped",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoObject *", "IntPtr", "xamarin_get_inative_object_dynamic",
|
||||
"id", "IntPtr", "obj",
|
||||
"bool", "bool", "owns",
|
||||
"void *", "IntPtr", "type"
|
||||
) { WrappedManagedFunction = "GetINativeObject_Dynamic" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetINativeObject_Dynamic",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoReflectionMethod *", "IntPtr", "xamarin_get_method_from_token",
|
||||
"unsigned int", "uint", "token_ref"
|
||||
) { WrappedManagedFunction = "GetMethodFromToken" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetMethodFromToken",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoReflectionMethod *", "IntPtr", "xamarin_get_generic_method_from_token",
|
||||
"MonoObject *", "IntPtr", "obj",
|
||||
"unsigned int", "uint", "token_ref"
|
||||
) { WrappedManagedFunction = "GetGenericMethodFromToken" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetGenericMethodFromToken",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoObject *", "IntPtr", "xamarin_get_inative_object_static",
|
||||
"id", "IntPtr", "obj",
|
||||
"bool", "bool", "owns",
|
||||
"const char *", "string", "type_name",
|
||||
"const char *", "string", "iface_name"
|
||||
) { WrappedManagedFunction = "GetINativeObject_Static" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetINativeObject_Static",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("MonoObject *", "IntPtr", "xamarin_get_nsobject_with_type",
|
||||
"id", "IntPtr", "obj",
|
||||
"void *", "IntPtr", "type",
|
||||
"int32_t *", "out bool", "created"
|
||||
) { WrappedManagedFunction = "GetNSObjectWithType" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetNSObjectWithType",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("void", "void", "xamarin_dispose",
|
||||
"MonoObject *", "IntPtr", "mobj"
|
||||
) { WrappedManagedFunction = "Dispose" },
|
||||
) {
|
||||
WrappedManagedFunction = "Dispose",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("bool", "bool", "xamarin_is_parameter_transient",
|
||||
"MonoReflectionMethod *", "IntPtr", "method",
|
||||
"int", "int", "parameter"
|
||||
) { WrappedManagedFunction = "IsParameterTransient" },
|
||||
) {
|
||||
WrappedManagedFunction = "IsParameterTransient",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("bool", "bool", "xamarin_is_parameter_out",
|
||||
"MonoReflectionMethod *", "IntPtr", "method",
|
||||
"int", "int", "parameter"
|
||||
) { WrappedManagedFunction = "IsParameterOut" },
|
||||
) {
|
||||
WrappedManagedFunction = "IsParameterOut",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("void", "void", "xamarin_get_method_and_object_for_selector",
|
||||
"Class", "IntPtr", "cls",
|
||||
|
@ -139,38 +215,62 @@
|
|||
"id", "IntPtr", "obj",
|
||||
"MonoObject **", "ref IntPtr", "mthis",
|
||||
"MethodDescription *", "IntPtr", "desc"
|
||||
) { WrappedManagedFunction = "GetMethodAndObjectForSelector" },
|
||||
) {
|
||||
WrappedManagedFunction = "GetMethodAndObjectForSelector",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("guint32", "int", "xamarin_create_product_exception_for_error",
|
||||
"int", "int", "code",
|
||||
"const char *", "string", "message"
|
||||
) { WrappedManagedFunction = "CreateProductException" },
|
||||
) {
|
||||
WrappedManagedFunction = "CreateProductException",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("char *", "IntPtr", "xamarin_reflection_type_get_full_name",
|
||||
"MonoReflectionType *", "IntPtr", "type"
|
||||
) { WrappedManagedFunction = "TypeGetFullName" },
|
||||
) {
|
||||
WrappedManagedFunction = "TypeGetFullName",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("char *", "IntPtr", "xamarin_lookup_managed_type_name",
|
||||
"Class", "IntPtr", "klass"
|
||||
) { WrappedManagedFunction = "LookupManagedTypeName" },
|
||||
) {
|
||||
WrappedManagedFunction = "LookupManagedTypeName",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("MarshalManagedExceptionMode", "MarshalManagedExceptionMode", "xamarin_on_marshal_managed_exception",
|
||||
"int", "int", "exception"
|
||||
) { WrappedManagedFunction = "OnMarshalManagedException" },
|
||||
) {
|
||||
WrappedManagedFunction = "OnMarshalManagedException",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("MarshalObjectiveCExceptionMode", "MarshalObjectiveCExceptionMode", "xamarin_on_marshal_objectivec_exception",
|
||||
"id", "IntPtr", "exception",
|
||||
"bool", "bool", "throwManagedAsDefault"
|
||||
) { WrappedManagedFunction = "OnMarshalObjectiveCException" },
|
||||
) {
|
||||
WrappedManagedFunction = "OnMarshalObjectiveCException",
|
||||
OnlyDynamicUsage = false,
|
||||
},
|
||||
|
||||
new XDelegate ("NSString *", "IntPtr", "xamarin_convert_smart_enum_to_nsstring",
|
||||
"void *", "IntPtr", "value"
|
||||
) { WrappedManagedFunction = "ConvertSmartEnumToNSString" },
|
||||
) {
|
||||
WrappedManagedFunction = "ConvertSmartEnumToNSString",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
|
||||
new XDelegate ("void *", "IntPtr", "xamarin_convert_nsstring_to_smart_enum",
|
||||
"NSString *", "IntPtr", "value",
|
||||
"MonoReflectionType *", "IntPtr", "type"
|
||||
) { WrappedManagedFunction = "ConvertNSStringToSmartEnum" },
|
||||
) {
|
||||
WrappedManagedFunction = "ConvertNSStringToSmartEnum",
|
||||
OnlyDynamicUsage = true,
|
||||
},
|
||||
};
|
||||
delegates.CalculateLengths ();
|
||||
#><#+
|
||||
|
@ -204,6 +304,8 @@
|
|||
public List<Arg> Arguments;
|
||||
public string WrappedManagedFunction;
|
||||
public bool ExceptionHandling = true;
|
||||
// Detemines whether the function is only used by the dynamic registrar (in which case we might be able to link the function away if the static registrar is being used)
|
||||
public bool OnlyDynamicUsage;
|
||||
|
||||
public XDelegates Delegates;
|
||||
|
||||
|
|
|
@ -451,9 +451,11 @@ xamarin_main (int argc, char *argv[], enum XamarinLaunchMode launch_mode)
|
|||
xamarin_process_managed_exception_gchandle (exception_gchandle);
|
||||
}
|
||||
|
||||
if (xamarin_supports_dynamic_registration) {
|
||||
xamarin_register_entry_assembly (mono_assembly_get_object (mono_domain_get (), assembly), &exception_gchandle);
|
||||
if (exception_gchandle != 0)
|
||||
xamarin_process_managed_exception_gchandle (exception_gchandle);
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_LAUNCH_TIME_PRINT ("\tAssembly register time");
|
||||
|
|
|
@ -22,6 +22,8 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
<# foreach (var d in delegates) {
|
||||
if (d.OnlyDynamicUsage)
|
||||
continue;
|
||||
#>
|
||||
<#= d.CReturnType #>
|
||||
<#= d.EntryPoint #> (<#= d.CArgumentSignature #>);
|
||||
|
|
|
@ -80,6 +80,7 @@ bool xamarin_is_gc_coop = false;
|
|||
enum MarshalObjectiveCExceptionMode xamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionModeDefault;
|
||||
enum MarshalManagedExceptionMode xamarin_marshal_managed_exception_mode = MarshalManagedExceptionModeDefault;
|
||||
enum XamarinLaunchMode xamarin_launch_mode = XamarinLaunchModeApp;
|
||||
bool xamarin_supports_dynamic_registration = true;
|
||||
|
||||
/* Callbacks */
|
||||
|
||||
|
@ -951,6 +952,10 @@ static bool
|
|||
register_assembly (MonoAssembly *assembly, guint32 *exception_gchandle)
|
||||
{
|
||||
// COOP: this is a function executed only at startup, I believe the mode here doesn't matter.
|
||||
if (!xamarin_supports_dynamic_registration) {
|
||||
LOG (PRODUCT ": Skipping assembly registration for %s since it's not needed (dynamic registration is not supported)", mono_assembly_name_get_name (mono_assembly_get_name (assembly)));
|
||||
return true;
|
||||
}
|
||||
xamarin_register_assembly (mono_assembly_get_object (mono_domain_get (), assembly), exception_gchandle);
|
||||
return *exception_gchandle == 0;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ extern bool xamarin_is_gc_coop;
|
|||
extern enum MarshalObjectiveCExceptionMode xamarin_marshal_objectivec_exception_mode;
|
||||
extern enum MarshalManagedExceptionMode xamarin_marshal_managed_exception_mode;
|
||||
extern enum XamarinLaunchMode xamarin_launch_mode;
|
||||
extern bool xamarin_supports_dynamic_registration;
|
||||
|
||||
typedef void (*xamarin_setup_callback) ();
|
||||
typedef int (*xamarin_extension_main_callback) (int argc, char** argv);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#include "main.h"
|
||||
#include "mono-runtime.h"
|
||||
#include "runtime-generated.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
|
@ -282,6 +282,7 @@ namespace Foundation {
|
|||
|
||||
[Export ("conformsToProtocol:")]
|
||||
[Preserve ()]
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
public virtual bool ConformsToProtocol (IntPtr protocol)
|
||||
{
|
||||
bool does;
|
||||
|
@ -316,6 +317,9 @@ namespace Foundation {
|
|||
if (does)
|
||||
return true;
|
||||
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
return false;
|
||||
|
||||
object [] adoptedProtocols = GetType ().GetCustomAttributes (typeof (AdoptsAttribute), true);
|
||||
foreach (AdoptsAttribute adopts in adoptedProtocols){
|
||||
if (adopts.ProtocolHandle == protocol)
|
||||
|
|
|
@ -83,8 +83,12 @@ namespace ObjCRuntime {
|
|||
[DllImport ("__Internal")]
|
||||
static extern IntPtr xamarin_get_block_descriptor ();
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
void SetupBlock (Delegate trampoline, Delegate userDelegate, bool safe)
|
||||
{
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
throw ErrorHelper.CreateError (8026, "BlockLiteral.SetupBlock is not supported when the dynamic registrar has been linked away.");
|
||||
|
||||
// We need to get the signature of the target method, so that we can compute
|
||||
// the ObjC signature correctly (the generated method that's actually
|
||||
// invoked by native code does not have enough type information to compute
|
||||
|
@ -239,6 +243,7 @@ namespace ObjCRuntime {
|
|||
return descriptor->copy_helper == ((BlockDescriptor *) literal->block_descriptor)->copy_helper;
|
||||
}
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
internal static IntPtr GetBlockForDelegate (MethodInfo minfo, object @delegate, string signature)
|
||||
{
|
||||
if (@delegate == null)
|
||||
|
@ -275,7 +280,11 @@ namespace ObjCRuntime {
|
|||
// with the proper reference count.
|
||||
BlockLiteral block = new BlockLiteral ();
|
||||
if (signature == null) {
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
block.SetupBlock ((Delegate) handlerDelegate, (Delegate) @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 ((Delegate) handlerDelegate, (Delegate) @delegate, true, signature);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace ObjCRuntime {
|
|||
|
||||
internal IntPtr handle;
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
internal unsafe static void Initialize (Runtime.InitializationOptions* options)
|
||||
{
|
||||
var map = options->RegistrationMap;
|
||||
|
@ -36,6 +37,9 @@ namespace ObjCRuntime {
|
|||
if (map == null)
|
||||
return;
|
||||
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
return; // Only the dynamic registrar needs the list of registered assemblies.
|
||||
|
||||
for (int i = 0; i < map->assembly_count; i++) {
|
||||
var ptr = Marshal.ReadIntPtr (map->assembly, i * IntPtr.Size);
|
||||
Runtime.Registrar.SetAssemblyRegistered (Marshal.PtrToStringAuto (ptr));
|
||||
|
@ -98,6 +102,7 @@ namespace ObjCRuntime {
|
|||
return GetClassHandle (type);
|
||||
}
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)] // To inline the Runtime.DynamicRegistrationSupported code if possible.
|
||||
static IntPtr GetClassHandle (Type type)
|
||||
{
|
||||
IntPtr @class = IntPtr.Zero;
|
||||
|
@ -116,6 +121,8 @@ namespace ObjCRuntime {
|
|||
}
|
||||
|
||||
if (@class == IntPtr.Zero) {
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
throw ErrorHelper.CreateError (8026, $"Can't register the class {type.FullName} when the dynamic registrar has been linked away.");
|
||||
@class = Register (type);
|
||||
lock (type_to_class)
|
||||
type_to_class [type] = @class;
|
||||
|
@ -143,12 +150,36 @@ namespace ObjCRuntime {
|
|||
|
||||
internal static Type Lookup (IntPtr klass)
|
||||
{
|
||||
return Lookup (klass, true);
|
||||
return LookupClass (klass, true);
|
||||
}
|
||||
|
||||
internal static Type Lookup (IntPtr klass, bool throw_on_error)
|
||||
{
|
||||
return LookupClass (klass, throw_on_error);
|
||||
}
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)] // To inline the Runtime.DynamicRegistrationSupported code if possible.
|
||||
static Type LookupClass (IntPtr klass, bool throw_on_error)
|
||||
{
|
||||
bool is_custom_type;
|
||||
var find_class = klass;
|
||||
do {
|
||||
var tp = FindType (find_class, out is_custom_type);
|
||||
if (tp != null)
|
||||
return tp;
|
||||
if (Runtime.DynamicRegistrationSupported)
|
||||
break; // We can't continue looking up the hierarchy if we have the dynamic registrar, because we might be supposed to register this class.
|
||||
find_class = class_getSuperclass (find_class);
|
||||
} while (find_class != IntPtr.Zero);
|
||||
|
||||
// The linker will remove this condition (and the subsequent method call) if possible
|
||||
if (Runtime.DynamicRegistrationSupported)
|
||||
return Runtime.Registrar.Lookup (klass, throw_on_error);
|
||||
|
||||
if (throw_on_error)
|
||||
throw ErrorHelper.CreateError (8026, $"Can't lookup the Objective-C class 0x{klass.ToString ("x")} ({class_getName (klass)}) when the dynamic registrar has been linked away.");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static IntPtr Register (Type type)
|
||||
|
@ -439,6 +470,7 @@ namespace ObjCRuntime {
|
|||
/*
|
||||
Type must have been previously registered.
|
||||
*/
|
||||
[BindingImpl (BindingImplOptions.Optimizable)] // To inline the Runtime.DynamicRegistrationSupported code if possible.
|
||||
#if !XAMCORE_2_0 && !MONOTOUCH // Accidently exposed this to public, can't break API
|
||||
public
|
||||
#else
|
||||
|
@ -446,7 +478,15 @@ namespace ObjCRuntime {
|
|||
#endif
|
||||
static bool IsCustomType (Type type)
|
||||
{
|
||||
bool is_custom_type;
|
||||
var @class = FindClass (type, out is_custom_type);
|
||||
if (@class != IntPtr.Zero)
|
||||
return is_custom_type;
|
||||
|
||||
if (Runtime.DynamicRegistrationSupported)
|
||||
return Runtime.Registrar.IsCustomType (type);
|
||||
|
||||
throw ErrorHelper.CreateError (8026, $"Can't determine if {type.FullName} is a custom type when the dynamic registrar has been linked away.");
|
||||
}
|
||||
|
||||
[DllImport ("/usr/lib/libobjc.dylib")]
|
||||
|
|
|
@ -162,6 +162,14 @@ namespace ObjCRuntime {
|
|||
|
||||
internal static unsafe InitializationOptions* options;
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
public static bool DynamicRegistrationSupported {
|
||||
get {
|
||||
// The linker may turn calls to this property into a constant
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool Initialized {
|
||||
get { return initialized; }
|
||||
}
|
||||
|
@ -172,6 +180,7 @@ namespace ObjCRuntime {
|
|||
#endif
|
||||
|
||||
[Preserve] // called from native - runtime.m.
|
||||
[BindingImpl (BindingImplOptions.Optimizable)] // To inline the Runtime.DynamicRegistrationSupported code if possible.
|
||||
unsafe static void Initialize (InitializationOptions* options)
|
||||
{
|
||||
#if PROFILE
|
||||
|
@ -234,6 +243,7 @@ namespace ObjCRuntime {
|
|||
|
||||
NSObjectClass = NSObject.Initialize ();
|
||||
|
||||
if (DynamicRegistrationSupported)
|
||||
Registrar = new DynamicRegistrar ();
|
||||
RegisterDelegates (options);
|
||||
Class.Initialize (options);
|
||||
|
@ -533,11 +543,14 @@ namespace ObjCRuntime {
|
|||
return Registrar.ComputeSignature (method, isBlockSignature);
|
||||
}
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
public static void RegisterAssembly (Assembly a)
|
||||
{
|
||||
if (a == null)
|
||||
throw new ArgumentNullException ("a");
|
||||
|
||||
if (!DynamicRegistrationSupported)
|
||||
throw ErrorHelper.CreateError (8026, "Runtime.RegisterAssembly is not supported when the dynamic registrar has been linked away.");
|
||||
#if MONOMAC
|
||||
var attributes = a.GetCustomAttributes (typeof (RequiredFrameworkAttribute), false);
|
||||
|
||||
|
@ -1399,6 +1412,7 @@ namespace ObjCRuntime {
|
|||
ConnectMethod (type, method, new ExportAttribute (selector.Name));
|
||||
}
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
public static void ConnectMethod (Type type, MethodInfo method, ExportAttribute export)
|
||||
{
|
||||
if (type == null)
|
||||
|
@ -1410,6 +1424,9 @@ namespace ObjCRuntime {
|
|||
if (export == null)
|
||||
throw new ArgumentNullException ("export");
|
||||
|
||||
if (!DynamicRegistrationSupported)
|
||||
throw ErrorHelper.CreateError (8026, "Runtime.ConnectMethod is not supported when the dynamic registrar has been linked away.");
|
||||
|
||||
Registrar.RegisterMethod (type, method, export);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,11 @@ namespace ObjCRuntime {
|
|||
*
|
||||
* http://developer.apple.com/documentation/DeveloperTools/gcc-4.0.1/gcc/Type-encoding.html
|
||||
*/
|
||||
[BindingImpl (BindingImplOptions.Optimizable)] // To inline the Runtime.DynamicRegistrationSupported code if possible.
|
||||
public static Type ToManaged (string type) {
|
||||
if (!Runtime.DynamicRegistrationSupported) // The call to Runtime.GetAssemblies further below requires the dynamic registrar.
|
||||
throw ErrorHelper.CreateError (8026, "TypeConverter.ToManaged is not supported when the dynamic registrar has been linked away.");
|
||||
|
||||
switch (type[0]) {
|
||||
case '@':
|
||||
return typeof (IntPtr);
|
||||
|
|
|
@ -30,13 +30,22 @@ namespace Xamarin.BindingTests
|
|||
Assert.IsNotNull (IP1, "IP1");
|
||||
// with a [Protocol] attribute
|
||||
var IP1Attributes = IP1.GetCustomAttributes (typeof (ProtocolAttribute), false);
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
Assert.AreEqual (1, IP1Attributes.Length, "[Protocol] IP1");
|
||||
var IP1Protocol = (ProtocolAttribute)IP1Attributes [0];
|
||||
Assert.AreEqual ("P1", IP1Protocol.Name, "Name");
|
||||
|
||||
// and a wrapper type
|
||||
var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P1Wrapper");
|
||||
Assert.IsNotNull (wrapperType, "P1_Wrapper");
|
||||
Assert.AreEqual (wrapperType, IP1Protocol.WrapperType, "WrapperType");
|
||||
} else {
|
||||
Assert.AreEqual (0, IP1Attributes.Length, "[Protocol] IP1");
|
||||
|
||||
// and a wrapper type
|
||||
var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P1Wrapper");
|
||||
Assert.IsNotNull (wrapperType, "P1_Wrapper");
|
||||
}
|
||||
// but not the model
|
||||
Assert.IsNull (bindingAssembly.GetType ("Bindings.Test.Protocol.P1"), "P1");
|
||||
}
|
||||
|
@ -53,6 +62,7 @@ namespace Xamarin.BindingTests
|
|||
|
||||
// with a [Protocol] attribute
|
||||
var IP2Attributes = IP2.GetCustomAttributes (typeof (ProtocolAttribute), false);
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
Assert.AreEqual (1, IP2Attributes.Length, "[Protocol] IP2");
|
||||
var IP2Protocol = (ProtocolAttribute)IP2Attributes [0];
|
||||
Assert.AreEqual ("P2", IP2Protocol.Name, "Name");
|
||||
|
@ -61,6 +71,13 @@ namespace Xamarin.BindingTests
|
|||
var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P2Wrapper");
|
||||
Assert.IsNotNull (wrapperType, "P2_Wrapper");
|
||||
Assert.AreEqual (wrapperType, IP2Protocol.WrapperType, "WrapperType");
|
||||
} else {
|
||||
Assert.AreEqual (0, IP2Attributes.Length, "[Protocol] IP2");
|
||||
|
||||
// and a wrapper type
|
||||
var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P2Wrapper");
|
||||
Assert.IsNotNull (wrapperType, "P2_Wrapper");
|
||||
}
|
||||
|
||||
// and a model-like class
|
||||
var model = bindingAssembly.GetType ("Bindings.Test.Protocol.P2");
|
||||
|
@ -81,6 +98,7 @@ namespace Xamarin.BindingTests
|
|||
|
||||
// with a [Protocol] attribute
|
||||
var IP3Attributes = IP3.GetCustomAttributes (typeof (ProtocolAttribute), false);
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
Assert.AreEqual (1, IP3Attributes.Length, "[Protocol] IP3");
|
||||
var IP3Protocol = (ProtocolAttribute)IP3Attributes [0];
|
||||
Assert.AreEqual ("P3", IP3Protocol.Name, "Name");
|
||||
|
@ -89,6 +107,13 @@ namespace Xamarin.BindingTests
|
|||
var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P3Wrapper");
|
||||
Assert.IsNotNull (wrapperType, "P3_Wrapper");
|
||||
Assert.AreEqual (wrapperType, IP3Protocol.WrapperType, "WrapperType");
|
||||
} else {
|
||||
Assert.AreEqual (0, IP3Attributes.Length, "[Protocol] IP3");
|
||||
|
||||
// and a wrapper type
|
||||
var wrapperType = bindingAssembly.GetType ("Bindings.Test.Protocol.P3Wrapper");
|
||||
Assert.IsNotNull (wrapperType, "P3_Wrapper");
|
||||
}
|
||||
|
||||
// and a model class
|
||||
var model = bindingAssembly.GetType ("Bindings.Test.Protocol.P3");
|
||||
|
|
|
@ -13,7 +13,7 @@ using Xamarin.Tests;
|
|||
namespace Xamarin.ApiTest
|
||||
{
|
||||
[TestFixture]
|
||||
public class BlockLiteral
|
||||
public class ApiTest
|
||||
{
|
||||
[Test]
|
||||
#if MONOTOUCH
|
||||
|
@ -26,7 +26,11 @@ namespace Xamarin.ApiTest
|
|||
#endif
|
||||
public void AlwaysOptimizable (Profile profile)
|
||||
{
|
||||
// This test ensures that all methods that call BlockLiteral.SetupBlock[Impl] have an [BindingImpl (BindingImplOptions.Optimizable)] attribute.
|
||||
// This test ensures that all methods that call optimizable methods have an [BindingImpl (BindingImplOptions.Optimizable)] attribute.
|
||||
// Current optimizable methods:
|
||||
// * BlockLiteral.SetupBlock
|
||||
// * BlockLiteral.SetupBlockImpl
|
||||
// * Runtime.get_DynamicRegistrationSupported
|
||||
|
||||
var dll = Configuration.GetBaseLibrary (profile);
|
||||
var asm = AssemblyDefinition.ReadAssembly (dll);
|
||||
|
@ -34,51 +38,71 @@ namespace Xamarin.ApiTest
|
|||
var failures = new List<string> ();
|
||||
|
||||
foreach (var type in asm.MainModule.GetTypes ()) {
|
||||
if (type.Name == "BlockLiteral")
|
||||
continue;
|
||||
|
||||
foreach (var method in type.Methods) {
|
||||
if (!method.HasBody)
|
||||
continue;
|
||||
var callsSetupBlock = false;
|
||||
var mustBeOptimizable = false;
|
||||
MethodReference mr = null;
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
var mr = instr.Operand as MethodReference;
|
||||
mr = instr.Operand as MethodReference;
|
||||
if (mr == null)
|
||||
continue;
|
||||
if (mr.DeclaringType.Name != "BlockLiteral")
|
||||
continue;
|
||||
if (mr.Name != "SetupBlock" && mr.Name != "SetupBlockUnsafe")
|
||||
continue;
|
||||
callsSetupBlock = true;
|
||||
switch (mr.DeclaringType.Name) {
|
||||
case "BlockLiteral":
|
||||
if (type == mr.DeclaringType)
|
||||
continue; // Calls within BlockLiteral without the optimizable attribute is allowed
|
||||
|
||||
switch (mr.Name) {
|
||||
case "SetupBlock":
|
||||
case "SetupBlockUnsafe":
|
||||
mustBeOptimizable = true;
|
||||
break;
|
||||
}
|
||||
if (!callsSetupBlock)
|
||||
break;
|
||||
case "Runtime":
|
||||
switch (mr.Name) {
|
||||
case "get_DynamicRegistrationSupported":
|
||||
mustBeOptimizable = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (mustBeOptimizable)
|
||||
break;
|
||||
}
|
||||
if (!mustBeOptimizable)
|
||||
continue;
|
||||
|
||||
// This method calls BlockLiteral.SetupBlock. Ensure it has a [BindingImpl (BindingImplOptions.Optimizable)] attribute.
|
||||
// Ensure it has a [BindingImpl (BindingImplOptions.Optimizable)] attribute.
|
||||
var isOptimizable = IsOptimizable (method);
|
||||
if (!isOptimizable)
|
||||
failures.Add ($"The method {method.FullName} calls {mr.FullName}, but it does not have a [BindingImpl (BindingImplOptions.Optimizable)] attribute.");
|
||||
}
|
||||
}
|
||||
|
||||
if (failures.Count > 0)
|
||||
Assert.Fail ($"All methods calling optimizable API must be optimizable\n\t{string.Join ("\n\t", failures)}");
|
||||
}
|
||||
|
||||
bool IsOptimizable (MethodDefinition method)
|
||||
{
|
||||
var isOptimizable = IsOptimizableProvider (method);
|
||||
if (!isOptimizable && (method.IsGetter || method.IsSetter)) {
|
||||
var property = method.DeclaringType.Properties.FirstOrDefault ((v) => v.GetMethod == method || v.SetMethod == method);
|
||||
isOptimizable = IsOptimizable (property);
|
||||
if (IsOptimizableProvider (property))
|
||||
return true;
|
||||
}
|
||||
foreach (var ca in method.CustomAttributes) {
|
||||
if (ca.AttributeType.Name != "BindingImplAttribute")
|
||||
continue;
|
||||
var value = (int) ca.ConstructorArguments [0].Value;
|
||||
if ((value & 0x2) == 0x2) { // BindingImplOptions.Optimizable = 2
|
||||
isOptimizable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isOptimizable)
|
||||
failures.Add ($"The method {method.FullName} calls BlockLiteral.SetupBlock[Unsafe], but it does not have a [BindingImpl (BindingImplOptions.Optimizable)] attribute.");
|
||||
if ((value & 0x2) == 0x2) // BindingImplOptions.Optimizable = 2
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CollectionAssert.IsEmpty (failures, "All methods calling SetupBlock[Unsafe] must be optimizable");
|
||||
}
|
||||
|
||||
bool IsOptimizable (ICustomAttributeProvider provider)
|
||||
bool IsOptimizableProvider (ICustomAttributeProvider provider)
|
||||
{
|
||||
foreach (var ca in provider.CustomAttributes) {
|
||||
if (ca.AttributeType.Name != "BindingImplAttribute")
|
||||
|
|
|
@ -106,6 +106,63 @@ class T {
|
|||
}
|
||||
}
|
||||
|
||||
#if __MACOS__
|
||||
// XM doesn't support removing the dynamic registrar yet.
|
||||
//[TestCase (Profile.macOSMobile)]
|
||||
#else
|
||||
[Test]
|
||||
[TestCase (Profile.iOS)]
|
||||
#endif
|
||||
public void MX2107 (Profile profile)
|
||||
{
|
||||
using (var bundler = new BundlerTool ()) {
|
||||
var code = @"
|
||||
using System;
|
||||
using Foundation;
|
||||
using ObjCRuntime;
|
||||
class T {
|
||||
static void Main ()
|
||||
{
|
||||
TypeConverter.ToManaged (""@"");
|
||||
Runtime.ConnectMethod (typeof (NSObject), typeof (T).GetMethod (""Main""), new Selector (""sel""));
|
||||
Runtime.ConnectMethod (typeof (NSObject), typeof (T).GetMethod (""Main""), new ExportAttribute (""sel""));
|
||||
Runtime.ConnectMethod (typeof (T).GetMethod (""Main""), new Selector (""sel""));
|
||||
Runtime.RegisterAssembly (null);
|
||||
BlockLiteral bl = default (BlockLiteral);
|
||||
Action action = null;
|
||||
bl.SetupBlock (action, action);
|
||||
bl.SetupBlockUnsafe (action, action);
|
||||
}
|
||||
}
|
||||
";
|
||||
bundler.Profile = profile;
|
||||
bundler.CreateTemporaryCacheDirectory ();
|
||||
bundler.CreateTemporaryApp (profile, code: code, extraArg: "/debug:full");
|
||||
bundler.Linker = LinkerOption.LinkSdk;
|
||||
bundler.Registrar = RegistrarOption.Static;
|
||||
bundler.Optimize = new string [] { "remove-dynamic-registrar" };
|
||||
bundler.AssertExecute ();
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.TypeConverter.ToManaged (System.String)'.");
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.Runtime.ConnectMethod (System.Type, System.Reflection.MethodInfo, ObjCRuntime.Selector)'.");
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.Runtime.ConnectMethod (System.Type, System.Reflection.MethodInfo, Foundation.ExportAttribute)'.");
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.Runtime.ConnectMethod (System.Reflection.MethodInfo, ObjCRuntime.Selector)'.");
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.Runtime.RegisterAssembly (System.Reflection.Assembly)'.");
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.BlockLiteral.SetupBlock (System.Delegate, System.Delegate)'.");
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.BlockLiteral.SetupBlockUnsafe (System.Delegate, System.Delegate)'.");
|
||||
bundler.AssertWarningCount (7);
|
||||
|
||||
// try again with link all, now the warnings about SetupBlock[Unsafe] should be gone
|
||||
bundler.Linker = LinkerOption.LinkAll;
|
||||
bundler.AssertExecute ();
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.TypeConverter.ToManaged (System.String)'.");
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.Runtime.ConnectMethod (System.Type, System.Reflection.MethodInfo, ObjCRuntime.Selector)'.");
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.Runtime.ConnectMethod (System.Type, System.Reflection.MethodInfo, Foundation.ExportAttribute)'.");
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.Runtime.ConnectMethod (System.Reflection.MethodInfo, ObjCRuntime.Selector)'.");
|
||||
bundler.AssertWarning (2107, "It's not safe to remove the dynamic registrar, because testApp references 'ObjCRuntime.Runtime.RegisterAssembly (System.Reflection.Assembly)'.");
|
||||
bundler.AssertWarningCount (5);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
#if __MACOS__
|
||||
[TestCase (Profile.macOSMobile)]
|
||||
|
|
|
@ -140,17 +140,11 @@ namespace LinkAll.Attributes {
|
|||
{
|
||||
var klass = Type.GetType (NamespacePrefix + "ObjCRuntime.Runtime, " + AssemblyName);
|
||||
Assert.NotNull (klass, "Runtime");
|
||||
// Initialize, RegisterAssembly... are unconditionally preserved
|
||||
// Initialize and a few other methods are unconditionally preserved
|
||||
var method = klass.GetMethod ("Initialize", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
Assert.NotNull (method, "Initialize");
|
||||
method = klass.GetMethod ("RegisterAssembly", BindingFlags.Public | BindingFlags.Static);
|
||||
Assert.NotNull (method, "RegisterAssembly");
|
||||
method = klass.GetMethod ("RegisterNSObject", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof (NSObject), typeof (IntPtr) }, null);
|
||||
Assert.NotNull (method, "RegisterNSObject");
|
||||
method = klass.GetMethod ("GetBlockWrapperCreator", BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof (MethodInfo), typeof (int) }, null);
|
||||
Assert.NotNull (method, "GetBlockWrapperCreator");
|
||||
method = klass.GetMethod ("CreateBlockProxy", BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof (MethodInfo), typeof (IntPtr) }, null);
|
||||
Assert.NotNull (method, "CreateBlockProxy");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<MtouchDebug>True</MtouchDebug>
|
||||
<MtouchLink>Full</MtouchLink>
|
||||
<MtouchI18n>mideast,other</MtouchI18n>
|
||||
<MtouchExtraArgs>--registrar=static --optimize=all</MtouchExtraArgs>
|
||||
<MtouchExtraArgs>--registrar=static --optimize=all,-remove-dynamic-registrar</MtouchExtraArgs>
|
||||
<MtouchArch>i386, x86_64</MtouchArch>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
@ -39,7 +39,7 @@
|
|||
<MtouchI18n>mideast,other</MtouchI18n>
|
||||
<MtouchArch>i386, x86_64</MtouchArch>
|
||||
<DefineConstants>LINKALL;;$(DefineConstants)</DefineConstants>
|
||||
<MtouchExtraArgs>--optimize=all</MtouchExtraArgs>
|
||||
<MtouchExtraArgs>--optimize=all,-remove-dynamic-registrar</MtouchExtraArgs>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
|
||||
|
@ -55,7 +55,7 @@
|
|||
<MtouchLink>Full</MtouchLink>
|
||||
<MtouchI18n>mideast,other</MtouchI18n>
|
||||
<MtouchArch>ARMv7, ARM64</MtouchArch>
|
||||
<MtouchExtraArgs>-gcc_flags="-UhoItsB0rken" --optimize=all</MtouchExtraArgs>
|
||||
<MtouchExtraArgs>-gcc_flags="-UhoItsB0rken" --optimize=all,-remove-dynamic-registrar</MtouchExtraArgs>
|
||||
<MtouchNoSymbolStrip>true</MtouchNoSymbolStrip>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
@ -72,7 +72,7 @@
|
|||
<MtouchLink>Full</MtouchLink>
|
||||
<MtouchI18n>mideast,other</MtouchI18n>
|
||||
<MtouchArch>ARMv7</MtouchArch>
|
||||
<MtouchExtraArgs>-gcc_flags="-UhoItsB0rken" --optimize=all</MtouchExtraArgs>
|
||||
<MtouchExtraArgs>-gcc_flags="-UhoItsB0rken" --optimize=all,-remove-dynamic-registrar</MtouchExtraArgs>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug64|iPhone' ">
|
||||
|
@ -88,7 +88,7 @@
|
|||
<MtouchLink>Full</MtouchLink>
|
||||
<MtouchI18n>mideast,other</MtouchI18n>
|
||||
<MtouchArch>ARM64</MtouchArch>
|
||||
<MtouchExtraArgs>-gcc_flags="-UhoItsB0rken" --optimize=all</MtouchExtraArgs>
|
||||
<MtouchExtraArgs>-gcc_flags="-UhoItsB0rken" --optimize=all,-remove-dynamic-registrar</MtouchExtraArgs>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
|
||||
|
@ -102,7 +102,7 @@
|
|||
<MtouchI18n>mideast,other</MtouchI18n>
|
||||
<MtouchUseLlvm>True</MtouchUseLlvm>
|
||||
<MtouchArch>ARMv7, ARM64</MtouchArch>
|
||||
<MtouchExtraArgs>--optimize=all</MtouchExtraArgs>
|
||||
<MtouchExtraArgs>--optimize=all,-remove-dynamic-registrar</MtouchExtraArgs>
|
||||
<DefineConstants>LINKALL;;$(DefineConstants)</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
@ -117,7 +117,7 @@
|
|||
<MtouchI18n>mideast,other</MtouchI18n>
|
||||
<MtouchUseLlvm>True</MtouchUseLlvm>
|
||||
<MtouchArch>ARMv7</MtouchArch>
|
||||
<MtouchExtraArgs>--optimize=all</MtouchExtraArgs>
|
||||
<MtouchExtraArgs>--optimize=all,-remove-dynamic-registrar</MtouchExtraArgs>
|
||||
<DefineConstants>LINKALL;;$(DefineConstants)</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
@ -132,7 +132,7 @@
|
|||
<MtouchI18n>mideast,other</MtouchI18n>
|
||||
<MtouchUseLlvm>True</MtouchUseLlvm>
|
||||
<MtouchArch>ARM64</MtouchArch>
|
||||
<MtouchExtraArgs>--optimize=all</MtouchExtraArgs>
|
||||
<MtouchExtraArgs>--optimize=all,-remove-dynamic-registrar</MtouchExtraArgs>
|
||||
<DefineConstants>LINKALL;;$(DefineConstants)</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
@ -147,7 +147,7 @@
|
|||
<MtouchI18n>mideast,other</MtouchI18n>
|
||||
<MtouchUseLlvm>true</MtouchUseLlvm>
|
||||
<MtouchArch>ARMv7, ARM64</MtouchArch>
|
||||
<MtouchExtraArgs>--bitcode:full --optimize=all</MtouchExtraArgs>
|
||||
<MtouchExtraArgs>--bitcode:full --optimize=all,-remove-dynamic-registrar</MtouchExtraArgs>
|
||||
<DefineConstants>LINKALL;;$(DefineConstants)</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -842,7 +842,7 @@ namespace Xamarin.MMP.Tests
|
|||
"<LinkMode>Full</LinkMode>",
|
||||
};
|
||||
var rv = TI.TestUnifiedExecutable (test, shouldFail: false);
|
||||
rv.Messages.AssertWarning (132, $"Unknown optimization: '{opt}'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock, register-protocols.");
|
||||
rv.Messages.AssertWarning (132, $"Unknown optimization: '{opt}'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, blockliteral-setupblock, register-protocols, inline-dynamic-registration-supported.");
|
||||
rv.Messages.AssertErrorCount (0);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -39,6 +39,9 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
[Test]
|
||||
public void TestSetupBlock ()
|
||||
{
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
Assert.Ignore ("This test requires the dynamic registrar to be available.");
|
||||
|
||||
using (var obj = new TestClass ()) {
|
||||
TestClass.OnCallback = ((IntPtr blockArgument, IntPtr self, IntPtr argument) =>
|
||||
{
|
||||
|
|
|
@ -55,6 +55,75 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
Assert.That (p, Is.Not.EqualTo (IntPtr.Zero), "Class");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ctor ()
|
||||
{
|
||||
Assert.DoesNotThrow (() => new Class (typeof (NSObject)), "NSObject");
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
Assert.AreEqual (IntPtr.Zero, new Class (typeof (string)).Handle, "string");
|
||||
} else {
|
||||
try {
|
||||
new Class (typeof (string));
|
||||
} catch (Exception e) {
|
||||
Assert.AreEqual (typeof (RuntimeException), e.GetType (), "string exc");
|
||||
Assert.AreEqual ("Can't register the class System.String when the dynamic registrar has been linked away.", e.Message, "exc message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetHandle ()
|
||||
{
|
||||
Assert.AreNotEqual (IntPtr.Zero, Class.GetHandle (typeof (NSObject)), "NSObject");
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
Assert.AreEqual (IntPtr.Zero, Class.GetHandle (typeof (string)), "string 1");
|
||||
} else {
|
||||
try {
|
||||
Class.GetHandle (typeof (string));
|
||||
} catch (Exception e) {
|
||||
Assert.AreEqual (typeof (RuntimeException), e.GetType (), "string exc");
|
||||
Assert.AreEqual ("Can't register the class System.String when the dynamic registrar has been linked away.", e.Message, "exc message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Lookup ()
|
||||
{
|
||||
Assert.AreEqual (typeof (NSObject), Class.Lookup (new Class (typeof (NSObject))), "NSObject");
|
||||
Assert.AreEqual (typeof (NSString), Class.Lookup (new Class (typeof (NSString))), "NSString");
|
||||
Assert.AreNotEqual (typeof (NSObject), Class.Lookup (new Class (typeof (NSString))), "neq");
|
||||
try {
|
||||
Class.Lookup (new Class ("NSProxy"));
|
||||
} catch (Exception e) {
|
||||
Assert.AreEqual (typeof (RuntimeException), e.GetType (), "NSProxy exception");
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
Assert.AreEqual ("The ObjectiveC class 'NSProxy' could not be registered, it does not seem to derive from any known ObjectiveC class (including NSObject).", e.Message, "NSProxy exception message");
|
||||
} else {
|
||||
Assert.That (e.Message, Is.StringMatching ("Can't lookup the Objective-C class 0x.* w"), "NSProxy exception message 2");
|
||||
}
|
||||
}
|
||||
Assert.Throws<ArgumentException> (() => new Class ("InexistentClass"), "inexistent");
|
||||
// Private class which we've obviously not bound, but we've bound a super class.
|
||||
// And yes, NSMutableString is the first public superclass of __NSCFConstantString.
|
||||
Assert.AreEqual (typeof (NSMutableString), Class.Lookup (new Class ("__NSCFConstantString")), "private class");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsCustomType ()
|
||||
{
|
||||
using (var str = new DirtyType ())
|
||||
str.MarkDirty ();
|
||||
}
|
||||
|
||||
class DirtyType : NSObject
|
||||
{
|
||||
public void MarkDirty ()
|
||||
{
|
||||
base.MarkDirty ();
|
||||
}
|
||||
}
|
||||
|
||||
// Not sure what to do about this one, it doesn't compile with the static registrar (since linking fails)
|
||||
#if DYNAMIC_REGISTRAR
|
||||
[Test]
|
||||
|
|
|
@ -82,6 +82,22 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RegistrarRemoval ()
|
||||
{
|
||||
// define set by xharness when creating test variations.
|
||||
// It's not safe to remove the dynamic registrar in monotouch-test (by design; some of the tested API makes it unsafe, and the linker correcty detects this),
|
||||
// so the dynamic registrar will only be removed if manually requested.
|
||||
// Also removal of the dynamic registrar is not supported in XM
|
||||
#if OPTIMIZEALL && !__MACOS__
|
||||
var shouldBeRemoved = true;
|
||||
#else
|
||||
var shouldBeRemoved = false;
|
||||
#endif
|
||||
Assert.AreEqual (shouldBeRemoved, typeof (NSObject).Assembly.GetType ("Registrar.Registrar") == null, "Registrar removal");
|
||||
Assert.AreEqual (shouldBeRemoved, typeof (NSObject).Assembly.GetType ("Registrar.DynamicRegistrar") == null, "DynamicRegistrar removal");
|
||||
}
|
||||
|
||||
#if !MONOMAC
|
||||
[Test]
|
||||
public void TestProperties ()
|
||||
|
@ -268,6 +284,9 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
[Test]
|
||||
public void TestAction ()
|
||||
{
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
Assert.Ignore ("This test requires the dynamic registrar to be available.");
|
||||
|
||||
using (var obj = new RegistrarTestClass ()) {
|
||||
var sel = new Selector ("testAction:");
|
||||
var block = new BlockLiteral ();
|
||||
|
@ -1812,6 +1831,9 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
[Test]
|
||||
public void CustomAppDelegatePerformFetchTest ()
|
||||
{
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
Assert.Ignore ("This test requires the dynamic registrar to be available.");
|
||||
|
||||
using (var obj = new CustomApplicationDelegate ()) {
|
||||
BlockLiteral block = new BlockLiteral ();
|
||||
var performed = false;
|
||||
|
@ -2567,9 +2589,13 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
var block = new BlockLiteral ();
|
||||
var tramp = new DActionArity1V1 (SDActionArity1V1.Invoke);
|
||||
Action<NSObject> del = (v) => { };
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
block.SetupBlock (tramp, del);
|
||||
Assert.AreEqual ("v@:^v^v", GetBlockSignature (block), "a");
|
||||
block.CleanupBlock ();
|
||||
} else {
|
||||
Assert.Throws<RuntimeException> (() => block.SetupBlock (tramp, del));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -2578,9 +2604,15 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
var block = new BlockLiteral ();
|
||||
var tramp = new DActionArity1V2 (SDActionArity1V2.Invoke);
|
||||
Action<NSObject> del = (v) => { };
|
||||
if (Runtime.DynamicRegistrationSupported) {
|
||||
block.SetupBlock (tramp, del);
|
||||
Assert.AreEqual ("v@?@", GetBlockSignature (block), "a");
|
||||
block.CleanupBlock ();
|
||||
} else {
|
||||
// The linker is able to rewrite calls to BlockLiteral.SetupBlock to BlockLiteral.SetupBlockImpl (which works without the dynamic registrar),
|
||||
// but that will only happen if the code is linked, and monotouch-test is only SdkLinked. Thus this code will throw an exception
|
||||
Assert.Throws<RuntimeException> (() => block.SetupBlock (tramp, del));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
{
|
||||
if (connectMethodTestDone)
|
||||
Assert.Ignore ("This test can only be executed once, it modifies global state.");
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
Assert.Ignore ("This test requires support for dynamic registration.");
|
||||
connectMethodTestDone = true;
|
||||
// Bug 20013. This should not throw a KeyNotFoundException.
|
||||
Runtime.ConnectMethod (typeof (ConnectMethodClass).GetMethod ("Method"), new Selector ("method"));
|
||||
|
@ -356,6 +358,9 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
[Test]
|
||||
public void ConnectMethod ()
|
||||
{
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
Assert.Ignore ("This test requires support for dynamic registration.");
|
||||
|
||||
var minfo = typeof (RuntimeTest).GetMethod ("ConnectMethod");
|
||||
Assert.Throws<ArgumentNullException> (() => Runtime.ConnectMethod (null, new Selector ("")), "1");
|
||||
Assert.Throws<ArgumentNullException> (() => Runtime.ConnectMethod (minfo, null), "2");
|
||||
|
@ -371,6 +376,9 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
[Test]
|
||||
public void ConnectMethod1 ()
|
||||
{
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
Assert.Ignore ("This test requires support for dynamic registration.");
|
||||
|
||||
if (connectMethod1Done)
|
||||
Assert.Ignore ("This is a one-shot test. Restart to run again.");
|
||||
connectMethod1Done = true;
|
||||
|
@ -385,6 +393,9 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
[Test]
|
||||
public void ConnectMethod2 ()
|
||||
{
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
Assert.Ignore ("This test requires support for dynamic registration.");
|
||||
|
||||
if (connectMethod2Done)
|
||||
Assert.Ignore ("This is a one-shot test. Restart to run again.");
|
||||
connectMethod2Done = true;
|
||||
|
@ -407,6 +418,9 @@ namespace MonoTouchFixtures.ObjCRuntime {
|
|||
[Test]
|
||||
public void ConnectMethod3 ()
|
||||
{
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
Assert.Ignore ("This test requires support for dynamic registration.");
|
||||
|
||||
if (connectMethod3Done)
|
||||
Assert.Ignore ("This is a one-shot test. Restart to run again.");
|
||||
connectMethod3Done = true;
|
||||
|
|
|
@ -61,6 +61,9 @@ namespace MonoTouchFixtures.Photos {
|
|||
[Test]
|
||||
public unsafe void FrameProcessingBlock2 ()
|
||||
{
|
||||
if (!Runtime.DynamicRegistrationSupported)
|
||||
Assert.Ignore ("This test requires support for the dynamic registrar to setup the block");
|
||||
|
||||
var t = typeof (NSObject).Assembly.GetType ("ObjCRuntime.Trampolines/SDPHLivePhotoFrameProcessingBlock2");
|
||||
Assert.NotNull (t, "SDPHLivePhotoFrameProcessingBlock2");
|
||||
|
||||
|
|
|
@ -1639,7 +1639,7 @@ public class TestApp {
|
|||
mtouch.Linker = MTouchLinker.LinkSdk;
|
||||
mtouch.Optimize = new string [] { "foo" };
|
||||
mtouch.AssertExecute (MTouchAction.BuildSim, "build");
|
||||
mtouch.AssertWarning (132, "Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, inline-runtime-arch, blockliteral-setupblock, register-protocols.");
|
||||
mtouch.AssertWarning (132, "Unknown optimization: 'foo'. Valid optimizations are: remove-uithread-checks, dead-code-elimination, inline-isdirectbinding, inline-intptr-size, inline-runtime-arch, blockliteral-setupblock, register-protocols, inline-dynamic-registration-supported, remove-dynamic-registrar.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3267,8 +3267,10 @@ public partial class NotificationService : UNNotificationServiceExtension
|
|||
mtouch.AssertWarning (2003, "Option '--optimize=inline-intptr-size' will be ignored since linking is disabled");
|
||||
mtouch.AssertWarning (2003, "Option '--optimize=inline-runtime-arch' will be ignored since linking is disabled");
|
||||
mtouch.AssertWarning (2003, "Option '--optimize=blockliteral-setupblock' will be ignored since linking is disabled");
|
||||
mtouch.AssertWarning (2003, "Option '--optimize=inline-dynamic-registration-supported' will be ignored since linking is disabled");
|
||||
mtouch.AssertWarning (2003, "Option '--optimize=register-protocols' will be ignored since the static registrar is not enabled");
|
||||
mtouch.AssertWarningCount (7);
|
||||
mtouch.AssertWarning (2003, "Option '--optimize=remove-dynamic-registrar' will be ignored since the static registrar is not enabled");
|
||||
mtouch.AssertWarningCount (9);
|
||||
}
|
||||
|
||||
using (var mtouch = new MTouchTool ()) {
|
||||
|
|
|
@ -176,7 +176,7 @@ namespace xharness
|
|||
|
||||
switch (test.TestName) {
|
||||
case "monotouch-test":
|
||||
yield return new TestData { Variation = "Release (all optimizations)", MTouchExtraArgs = "--registrar:static --optimize:all", Debug = false, Profiling = false };
|
||||
yield return new TestData { Variation = "Release (all optimizations)", MTouchExtraArgs = "--registrar:static --optimize:all", Debug = false, Profiling = false, Defines = "OPTIMIZEALL" };
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -185,7 +185,7 @@ namespace xharness
|
|||
case "monotouch-test":
|
||||
// The default is to run monotouch-test with the dynamic registrar (in the simulator), so that's already covered
|
||||
yield return new TestData { Variation = "Debug (static registrar)", MTouchExtraArgs = "--registrar:static", Debug = true, Profiling = false };
|
||||
yield return new TestData { Variation = "Release (all optimizations)", MTouchExtraArgs = "--registrar:static --optimize:all", Debug = false, Profiling = false, LinkMode = "Full" };
|
||||
yield return new TestData { Variation = "Release (all optimizations)", MTouchExtraArgs = "--registrar:static --optimize:all", Debug = false, Profiling = false, LinkMode = "Full", Defines = "LINKALL;OPTIMIZEALL" };
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -194,7 +194,7 @@ namespace xharness
|
|||
switch (test.TestName) {
|
||||
case "xammac tests":
|
||||
if (test.ProjectConfiguration == "Release")
|
||||
yield return new TestData { Variation = "Release (all optimizations)", MonoBundlingExtraArgs = "--registrar:static --optimize:all", Debug = false, LinkMode = "Full", Defines = "LINKALL" };
|
||||
yield return new TestData { Variation = "Release (all optimizations)", MonoBundlingExtraArgs = "--registrar:static --optimize:all", Debug = false, LinkMode = "Full", Defines = "LINKALL;OPTIMIZEALL" };
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -226,14 +226,7 @@ namespace xharness
|
|||
var clone_task = Task.Run (async () => {
|
||||
await task.BuildTask.InitialTask; // this is the project cloning above
|
||||
await clone.CreateCopyAsync (task);
|
||||
if (!string.IsNullOrEmpty (mtouch_extra_args))
|
||||
clone.Xml.AddExtraMtouchArgs (mtouch_extra_args, task.ProjectPlatform, configuration);
|
||||
if (!string.IsNullOrEmpty (bundling_extra_args))
|
||||
clone.Xml.AddMonoBundlingExtraArgs (bundling_extra_args, task.ProjectPlatform, configuration);
|
||||
if (!string.IsNullOrEmpty (link_mode))
|
||||
clone.Xml.SetNode ("LinkMode", link_mode, task.ProjectPlatform, configuration);
|
||||
if (!string.IsNullOrEmpty (defines))
|
||||
clone.Xml.AddAdditionalDefines (defines, task.ProjectPlatform, configuration);
|
||||
|
||||
var isMac = false;
|
||||
switch (task.Platform) {
|
||||
case TestPlatform.Mac:
|
||||
|
@ -245,6 +238,15 @@ namespace xharness
|
|||
isMac = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty (mtouch_extra_args))
|
||||
clone.Xml.AddExtraMtouchArgs (mtouch_extra_args, task.ProjectPlatform, configuration);
|
||||
if (!string.IsNullOrEmpty (bundling_extra_args))
|
||||
clone.Xml.AddMonoBundlingExtraArgs (bundling_extra_args, task.ProjectPlatform, configuration);
|
||||
if (!string.IsNullOrEmpty (link_mode))
|
||||
clone.Xml.SetNode (isMac ? "LinkMode" : "MtouchLink", link_mode, task.ProjectPlatform, configuration);
|
||||
if (!string.IsNullOrEmpty (defines))
|
||||
clone.Xml.AddAdditionalDefines (defines, task.ProjectPlatform, configuration);
|
||||
clone.Xml.SetNode (isMac ? "Profiling" : "MTouchProfiling", profiling ? "True" : "False", task.ProjectPlatform, configuration);
|
||||
|
||||
if (!debug && !isMac)
|
||||
|
|
|
@ -85,6 +85,12 @@ namespace Xamarin.Bundler {
|
|||
Cache = new Cache (arguments);
|
||||
}
|
||||
|
||||
public bool DynamicRegistrationSupported {
|
||||
get {
|
||||
return Optimizations.RemoveDynamicRegistrar != true;
|
||||
}
|
||||
}
|
||||
|
||||
// This is just a name for this app to show in log/error messages, etc.
|
||||
public string Name {
|
||||
get { return Path.GetFileNameWithoutExtension (AppDirectory); }
|
||||
|
|
|
@ -136,10 +136,12 @@ namespace Xamarin.Bundler {
|
|||
" inline-isdirectbinding: By default disabled, because it may (requires the linker), because . Tries to inline calls to NSObject.IsDirectBinding to load a constant value. Makes the app smaller, and slightly faster at runtime.\n" +
|
||||
#endif
|
||||
#if MONOTOUCH
|
||||
" remove-dynamic-registrar: By default enabled when the static registrar is enabled. Removes the dynamic registrar (makes the app smaller).\n" +
|
||||
" inline-runtime-arch: By default always enabled (requires the linker). Inlines calls to ObjCRuntime.Runtime.Arch to load a constant value. Makes the app smaller, and slightly faster at runtime.\n" +
|
||||
#endif
|
||||
" blockliteral-setupblock: By default enabled when using the static registrar. Optimizes calls to BlockLiteral.SetupBlock to avoid having to calculate the block signature at runtime.\n" +
|
||||
" inline-intptr-size: By default enabled for builds that target a single architecture (requires the linker). Inlines calls to IntPtr.Size to load a constant value. Makes the app smaller, and slightly faster at runtime.\n" +
|
||||
" inline-dynamic-registration-supported: By default always enabled (requires the linker). Optimizes calls to Runtime.DynamicRegistrationSupported to be a constant value. Makes the app smaller, and slightly faster at runtime.\n" +
|
||||
" register-protocols: Remove unneeded metadata for protocol support. Makes the app smaller and reduces memory requirements.\n",
|
||||
(v) => {
|
||||
app.Optimizations.Parse (v);
|
||||
|
|
|
@ -17,6 +17,12 @@ namespace Xamarin.Bundler
|
|||
#endif
|
||||
"blockliteral-setupblock",
|
||||
"register-protocols",
|
||||
"inline-dynamic-registration-supported",
|
||||
#if MONOTOUCH
|
||||
"remove-dynamic-registrar",
|
||||
#else
|
||||
"", // dummy value to make indices match up between XM and XI
|
||||
#endif
|
||||
};
|
||||
|
||||
enum Opt
|
||||
|
@ -64,6 +70,15 @@ namespace Xamarin.Bundler
|
|||
get { return values [(int) Opt.RegisterProtocols]; }
|
||||
set { values [(int) Opt.RegisterProtocols] = value; }
|
||||
}
|
||||
public bool? InlineDynamicRegistrationSupported {
|
||||
get { return values [(int) Opt.InlineDynamicRegistrationSupported]; }
|
||||
set { values [(int) Opt.InlineDynamicRegistrationSupported] = value; }
|
||||
}
|
||||
|
||||
public bool? RemoveDynamicRegistrar {
|
||||
get { return values [(int) Opt.RemoveDynamicRegistrar]; }
|
||||
set { values [(int) Opt.RemoveDynamicRegistrar] = value; }
|
||||
}
|
||||
|
||||
public Optimizations ()
|
||||
{
|
||||
|
@ -78,6 +93,7 @@ namespace Xamarin.Bundler
|
|||
continue;
|
||||
switch ((Opt) i) {
|
||||
case Opt.RegisterProtocols:
|
||||
case Opt.RemoveDynamicRegistrar:
|
||||
if (app.Registrar != RegistrarMode.Static) {
|
||||
ErrorHelper.Warning (2003, $"Option '--optimize={(values [i].Value ? "" : "-")}{opt_names [i]}' will be ignored since the static registrar is not enabled");
|
||||
values [i] = false;
|
||||
|
@ -143,6 +159,28 @@ namespace Xamarin.Bundler
|
|||
RegisterProtocols = false; // we've already shown a warning for this.
|
||||
}
|
||||
|
||||
// By default we always inline calls to Runtime.DynamicRegistrationSupported
|
||||
if (!InlineDynamicRegistrationSupported.HasValue)
|
||||
InlineDynamicRegistrationSupported = true;
|
||||
|
||||
if (!RemoveDynamicRegistrar.HasValue) {
|
||||
if (InlineDynamicRegistrationSupported != true) {
|
||||
// Can't remove the dynamic registrar unless also inlining Runtime.DynamicRegistrationSupported
|
||||
RemoveDynamicRegistrar = false;
|
||||
} else if (app.Registrar != RegistrarMode.Static || app.LinkMode == LinkMode.None) {
|
||||
// Both the linker and the static registrar are also required
|
||||
RemoveDynamicRegistrar = false;
|
||||
} else {
|
||||
#if MONOTOUCH
|
||||
// We don't have enough information yet to determine if we can remove the dynamic
|
||||
// registrar or not, so let the value stay unset until we do know (when running the linker).
|
||||
#else
|
||||
// By default disabled for XM apps
|
||||
RemoveDynamicRegistrar = false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (Driver.Verbosity > 3)
|
||||
Driver.Log (4, "Enabled optimizations: {0}", string.Join (", ", values.Select ((v, idx) => v == true ? opt_names [idx] : string.Empty).Where ((v) => !string.IsNullOrEmpty (v))));
|
||||
}
|
||||
|
@ -177,8 +215,10 @@ namespace Xamarin.Bundler
|
|||
}
|
||||
|
||||
if (opt == "all") {
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
for (int i = 0; i < values.Length; i++) {
|
||||
if (!string.IsNullOrEmpty (opt_names [i]))
|
||||
values [i] = enabled;
|
||||
}
|
||||
} else {
|
||||
var found = false;
|
||||
for (int i = 0; i < values.Length; i++) {
|
||||
|
|
|
@ -212,11 +212,7 @@ namespace Xamarin.Linker {
|
|||
return true; // We've already marked this section of code
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
if (instructions [i].OpCode.Code != Code.Nop) {
|
||||
// Not marking nop instructios makes it possible to remove catch clauses if
|
||||
// the protected region is only nops (or empty).
|
||||
reachable [i] = true;
|
||||
}
|
||||
|
||||
var ins = instructions [i];
|
||||
switch (ins.OpCode.FlowControl) {
|
||||
|
@ -226,10 +222,31 @@ namespace Xamarin.Linker {
|
|||
return MarkInstructions (method, instructions, reachable, instructions.IndexOf (br_target), end);
|
||||
case FlowControl.Cond_Branch:
|
||||
// Conditional instruction, we need to check if we can calculate a constant value for the condition
|
||||
var cond_target = (Instruction) ins.Operand;
|
||||
var cond_target = ins.Operand as Instruction;
|
||||
bool? branch = null; // did we get a constant value for the condition, and if so, did we branch or not?
|
||||
var cond_instruction_count = 0; // The number of instructions that compose the condition
|
||||
|
||||
if (ins.OpCode.Code == Code.Switch) {
|
||||
// Treat all branches of the switch statement as reachable.
|
||||
// FIXME: calculate the potential constant branch (currently there are no optimizable methods where the switch condition is constant, so this is not needed for now)
|
||||
var targets = ins.Operand as Instruction [];
|
||||
if (targets == null) {
|
||||
Driver.Log (4, $"Can't optimize {0} because of unknown target of branch instruction {1} {2}", method, ins, ins.Operand);
|
||||
return false;
|
||||
}
|
||||
foreach (var target in targets) {
|
||||
// not constant, continue marking both this code sequence and the branched sequence
|
||||
if (!MarkInstructions (method, instructions, reachable, instructions.IndexOf (target), end))
|
||||
return false;
|
||||
}
|
||||
return MarkInstructions (method, instructions, reachable, instructions.IndexOf (ins.Next), end);
|
||||
}
|
||||
|
||||
if (cond_target == null) {
|
||||
Driver.Log (4, $"Can't optimize {0} because of unknown target of branch instruction {1} {2}", method, ins, ins.Operand);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (ins.OpCode.Code) {
|
||||
case Code.Brtrue:
|
||||
case Code.Brtrue_S: {
|
||||
|
@ -400,21 +417,36 @@ namespace Xamarin.Linker {
|
|||
return;
|
||||
|
||||
// Handle exception handlers specially, they do not follow normal code flow.
|
||||
bool[] reachableExceptionHandlers = null;
|
||||
if (caller.Body.HasExceptionHandlers) {
|
||||
foreach (var eh in caller.Body.ExceptionHandlers) {
|
||||
switch (eh.HandlerType) {
|
||||
case ExceptionHandlerType.Catch:
|
||||
// We don't need catch handlers if the protected region does not have any reachable instructions.
|
||||
reachableExceptionHandlers = new bool [caller.Body.ExceptionHandlers.Count];
|
||||
for (var e = 0; e < reachableExceptionHandlers.Length; e++) {
|
||||
var eh = caller.Body.ExceptionHandlers [e];
|
||||
|
||||
// First check if the protected region is reachable
|
||||
var startI = instructions.IndexOf (eh.TryStart);
|
||||
var endI = instructions.IndexOf (eh.TryEnd);
|
||||
var anyReachable = false;
|
||||
for (int i = startI; i < endI; i++) {
|
||||
if (reachable [i]) {
|
||||
anyReachable = true;
|
||||
reachableExceptionHandlers [e] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (anyReachable) {
|
||||
// The protected code isn't reachable, none of the handlers will be executed
|
||||
if (!reachableExceptionHandlers [e])
|
||||
continue;
|
||||
|
||||
switch (eh.HandlerType) {
|
||||
case ExceptionHandlerType.Catch:
|
||||
// We don't need catch handlers the reachable instructions are all nops
|
||||
var allNops = true;
|
||||
for (int i = startI; i < endI; i++) {
|
||||
if (instructions [i].OpCode.Code != Code.Nop) {
|
||||
allNops = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allNops) {
|
||||
if (!MarkInstructions (caller, instructions, reachable, instructions.IndexOf (eh.HandlerStart), instructions.IndexOf (eh.HandlerEnd)))
|
||||
return;
|
||||
}
|
||||
|
@ -506,6 +538,15 @@ namespace Xamarin.Linker {
|
|||
Nop (instructions [i]);
|
||||
}
|
||||
|
||||
// Remove exception handlers
|
||||
if (reachableExceptionHandlers != null) {
|
||||
for (int i = reachableExceptionHandlers.Length - 1; i >= 0; i--) {
|
||||
if (reachableExceptionHandlers [i])
|
||||
continue;
|
||||
caller.Body.ExceptionHandlers.RemoveAt (i);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove unreachable instructions (nops) at the end, because the last instruction can only be ret/throw/backwards branch.
|
||||
for (int i = last_reachable + 1; i < reachable.Length; i++)
|
||||
instructions.RemoveAt (last_reachable + 1);
|
||||
|
@ -551,6 +592,15 @@ namespace Xamarin.Linker {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!LinkContext.App.DynamicRegistrationSupported && method.Name == "get_DynamicRegistrationSupported" && method.DeclaringType.Is (Namespaces.ObjCRuntime, "Runtime")) {
|
||||
// Rewrite to return 'false'
|
||||
var instr = method.Body.Instructions;
|
||||
instr.Clear ();
|
||||
instr.Add (Instruction.Create (OpCodes.Ldc_I4_0));
|
||||
instr.Add (Instruction.Create (OpCodes.Ret));
|
||||
return; // nothing else to do here.
|
||||
}
|
||||
|
||||
var instructions = method.Body.Instructions;
|
||||
for (int i = 0; i < instructions.Count; i++) {
|
||||
var ins = instructions [i];
|
||||
|
@ -581,6 +631,9 @@ namespace Xamarin.Linker {
|
|||
case "get_IsDirectBinding":
|
||||
ProcessIsDirectBinding (caller, ins);
|
||||
break;
|
||||
case "get_DynamicRegistrationSupported":
|
||||
ProcessIsDynamicSupported (caller, ins);
|
||||
break;
|
||||
case "SetupBlock":
|
||||
case "SetupBlockUnsafe":
|
||||
return ProcessSetupBlock (caller, ins);
|
||||
|
@ -670,6 +723,26 @@ namespace Xamarin.Linker {
|
|||
ins.Operand = null;
|
||||
}
|
||||
|
||||
void ProcessIsDynamicSupported (MethodDefinition caller, Instruction ins)
|
||||
{
|
||||
const string operation = "inline Runtime.DynamicRegistrationSupported";
|
||||
|
||||
if (Optimizations.InlineDynamicRegistrationSupported != true)
|
||||
return;
|
||||
|
||||
// Verify we're checking the right Runtime.IsDynamicSupported call
|
||||
var mr = ins.Operand as MethodReference;
|
||||
if (!mr.DeclaringType.Is (Namespaces.ObjCRuntime, "Runtime"))
|
||||
return;
|
||||
|
||||
if (!ValidateInstruction (caller, ins, operation, Code.Call))
|
||||
return;
|
||||
|
||||
// We're fine, inline the Runtime.IsDynamicSupported condition
|
||||
ins.OpCode = LinkContext.App.DynamicRegistrationSupported ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0;
|
||||
ins.Operand = null;
|
||||
}
|
||||
|
||||
int ProcessSetupBlock (MethodDefinition caller, Instruction ins)
|
||||
{
|
||||
if (Optimizations.OptimizeBlockLiteralSetupBlock != true)
|
||||
|
@ -685,6 +758,9 @@ 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.
|
||||
|
||||
string signature = null;
|
||||
try {
|
||||
// We need to figure out the type of the first argument to the call to SetupBlock[Impl].
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Mono.Cecil;
|
||||
using Mono.Linker.Steps;
|
||||
|
@ -25,6 +26,7 @@ namespace MonoTouch.Tuner {
|
|||
public class CoreTypeMapStep : TypeMapStep {
|
||||
HashSet<TypeDefinition> cached_isnsobject = new HashSet<TypeDefinition> ();
|
||||
Dictionary<TypeDefinition, bool?> isdirectbinding_value = new Dictionary<TypeDefinition, bool?> ();
|
||||
bool dynamic_registration_support_required;
|
||||
|
||||
DerivedLinkContext LinkContext {
|
||||
get {
|
||||
|
@ -32,12 +34,134 @@ namespace MonoTouch.Tuner {
|
|||
}
|
||||
}
|
||||
|
||||
protected override void ProcessAssembly (AssemblyDefinition assembly)
|
||||
{
|
||||
if (LinkContext.App.Optimizations.RemoveDynamicRegistrar != false)
|
||||
dynamic_registration_support_required |= RequiresDynamicRegistrar (assembly, LinkContext.App.Optimizations.RemoveDynamicRegistrar == true);
|
||||
|
||||
base.ProcessAssembly (assembly);
|
||||
}
|
||||
|
||||
// If certain conditions are met, we can optimize away the code for the dynamic registrar.
|
||||
bool RequiresDynamicRegistrar (AssemblyDefinition assembly, bool warnIfRequired)
|
||||
{
|
||||
// Disable removing the dynamic registrar for XM/Classic to simplify the code a little bit.
|
||||
if (!Driver.IsUnified)
|
||||
return true;
|
||||
|
||||
// We know that the SDK assemblies we ship don't use the methods we're looking for.
|
||||
if (Profile.IsSdkAssembly (assembly))
|
||||
return false;
|
||||
|
||||
// The product assembly itself is safe as long as it's linked
|
||||
if (Profile.IsProductAssembly (assembly))
|
||||
return LinkContext.Annotations.GetAction (assembly) != Mono.Linker.AssemblyAction.Link;
|
||||
|
||||
// Can't touch the forbidden fruit in the product assembly unless there's a reference to it
|
||||
var hasProductReference = false;
|
||||
foreach (var ar in assembly.MainModule.AssemblyReferences) {
|
||||
if (!Profile.IsProductAssembly (ar.Name))
|
||||
continue;
|
||||
hasProductReference = true;
|
||||
break;
|
||||
}
|
||||
if (!hasProductReference)
|
||||
return false;
|
||||
|
||||
// Check if the assembly references any methods that require the dynamic registrar
|
||||
var productAssemblyName = ((MobileProfile) Profile.Current).ProductAssembly;
|
||||
var requires = false;
|
||||
foreach (var mr in assembly.MainModule.GetMemberReferences ()) {
|
||||
if (mr.DeclaringType == null || string.IsNullOrEmpty (mr.DeclaringType.Namespace))
|
||||
continue;
|
||||
|
||||
var scope = mr.DeclaringType.Scope;
|
||||
var name = string.Empty;
|
||||
switch (scope.MetadataScopeType) {
|
||||
case MetadataScopeType.ModuleDefinition:
|
||||
name = ((ModuleDefinition) scope).Assembly.Name.Name;
|
||||
break;
|
||||
default:
|
||||
name = scope.Name;
|
||||
break;
|
||||
}
|
||||
if (name != productAssemblyName)
|
||||
continue;
|
||||
|
||||
switch (mr.DeclaringType.Namespace) {
|
||||
case "ObjCRuntime":
|
||||
switch (mr.DeclaringType.Name) {
|
||||
case "Runtime":
|
||||
switch (mr.Name) {
|
||||
case "ConnectMethod":
|
||||
// Req 1: Nobody must call Runtime.ConnectMethod.
|
||||
if (warnIfRequired)
|
||||
Show2107 (assembly, mr);
|
||||
requires = true;
|
||||
break;
|
||||
case "RegisterAssembly":
|
||||
// Req 3: Nobody must call Runtime.RegisterAssembly
|
||||
if (warnIfRequired)
|
||||
Show2107 (assembly, mr);
|
||||
requires = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "BlockLiteral":
|
||||
switch (mr.Name) {
|
||||
case "SetupBlock":
|
||||
case "SetupBlockUnsafe":
|
||||
// Req 2: Nobody must call BlockLiteral.SetupBlock[Unsafe].
|
||||
//
|
||||
// Fortunately the linker is able to rewrite calls to SetupBlock[Unsafe] to call
|
||||
// SetupBlockImpl (which doesn't need the dynamic registrar), which means we only have
|
||||
// to look in assemblies that aren't linked.
|
||||
if (LinkContext.Annotations.GetAction (assembly) == Mono.Linker.AssemblyAction.Link && LinkContext.App.Optimizations.OptimizeBlockLiteralSetupBlock == true)
|
||||
break;
|
||||
|
||||
if (warnIfRequired)
|
||||
Show2107 (assembly, mr);
|
||||
|
||||
requires = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "TypeConverter":
|
||||
switch (mr.Name) {
|
||||
case "ToManaged":
|
||||
// Req 4: Nobody must call TypeConverter.ToManaged
|
||||
if (warnIfRequired)
|
||||
Show2107 (assembly, mr);
|
||||
requires = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return requires;
|
||||
}
|
||||
|
||||
void Show2107 (AssemblyDefinition assembly, MemberReference mr)
|
||||
{
|
||||
ErrorHelper.Warning (2107, $"It's not safe to remove the dynamic registrar, because {assembly.Name.Name} references '{mr.DeclaringType.FullName}.{mr.Name} ({string.Join (", ", ((MethodReference) mr).Parameters.Select ((v) => v.ParameterType.FullName))})'.");
|
||||
}
|
||||
|
||||
protected override void EndProcess ()
|
||||
{
|
||||
base.EndProcess ();
|
||||
|
||||
LinkContext.CachedIsNSObject = cached_isnsobject;
|
||||
LinkContext.IsDirectBindingValue = isdirectbinding_value;
|
||||
|
||||
if (!LinkContext.App.Optimizations.RemoveDynamicRegistrar.HasValue) {
|
||||
// If dynamic registration is not required, and removal of the dynamic registrar hasn't already
|
||||
// been disabled, then we can remove it!
|
||||
LinkContext.App.Optimizations.RemoveDynamicRegistrar = !dynamic_registration_support_required;
|
||||
Driver.Log (4, "Optimization dynamic registrar removal: {0}", LinkContext.App.Optimizations.RemoveDynamicRegistrar.Value ? "enabled" : "disabled");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void MapType (TypeDefinition type)
|
||||
|
|
|
@ -1130,6 +1130,9 @@ namespace Xamarin.Bundler {
|
|||
else
|
||||
sw.WriteLine ("\tsetenv (\"MONO_GC_PARAMS\", \"major=marksweep\", 1);");
|
||||
|
||||
if (IsUnified)
|
||||
sw.WriteLine ("\txamarin_supports_dynamic_registration = {0};", App.DynamicRegistrationSupported ? "TRUE" : "FALSE");
|
||||
|
||||
if (aotOptions != null && aotOptions.IsHybridAOT)
|
||||
sw.WriteLine ("\txamarin_mac_hybrid_aot = TRUE;");
|
||||
|
||||
|
|
|
@ -669,6 +669,7 @@ namespace Xamarin.Bundler
|
|||
sw.WriteLine ("\tsetenv (\"MONO_GC_PARAMS\", \"{0}\", 1);", app.MonoGCParams);
|
||||
foreach (var kvp in app.EnvironmentVariables)
|
||||
sw.WriteLine ("\tsetenv (\"{0}\", \"{1}\", 1);", kvp.Key.Replace ("\"", "\\\""), kvp.Value.Replace ("\"", "\\\""));
|
||||
sw.WriteLine ("\txamarin_supports_dynamic_registration = {0};", app.DynamicRegistrationSupported ? "TRUE" : "FALSE");
|
||||
sw.WriteLine ("}");
|
||||
sw.WriteLine ();
|
||||
sw.Write ("int ");
|
||||
|
|
Загрузка…
Ссылка в новой задаче