261 строка
8.5 KiB
C#
261 строка
8.5 KiB
C#
//
|
|
// CoreTypeMapStep.cs
|
|
//
|
|
// Authors:
|
|
// Sebastien Pouliot <sebastien@xamarin.com>
|
|
//
|
|
// Copyright 2012-2013 Xamarin Inc.
|
|
//
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
using Mono.Cecil;
|
|
using Mono.Linker.Steps;
|
|
using Mono.Tuner;
|
|
|
|
using Xamarin.Bundler;
|
|
using Xamarin.Linker;
|
|
using Xamarin.Tuner;
|
|
|
|
namespace MonoTouch.Tuner {
|
|
|
|
// This class is shared between Xamarin.Mac and Xamarin.iOS
|
|
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 {
|
|
return (DerivedLinkContext) base.Context;
|
|
}
|
|
}
|
|
|
|
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");
|
|
#if MTOUCH
|
|
var app = LinkContext.App;
|
|
if (app.IsCodeShared) {
|
|
foreach (var appex in app.AppExtensions) {
|
|
if (!appex.IsCodeShared)
|
|
continue;
|
|
appex.Optimizations.RemoveDynamicRegistrar = app.Optimizations.RemoveDynamicRegistrar;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
protected override void MapType (TypeDefinition type)
|
|
{
|
|
base.MapType (type);
|
|
|
|
// additional checks for NSObject to check if the type is a *generated* bindings
|
|
// bonus: we cache, for every type, whether or not it inherits from NSObject (very useful later)
|
|
if (!IsNSObject (type))
|
|
return;
|
|
|
|
// if not, it's a user type, the IsDirectBinding check is required by all ancestors
|
|
SetIsDirectBindingValue (type);
|
|
}
|
|
|
|
// called once for each 'type' so it's a nice place to cache the result
|
|
// and ensure later steps re-use the same, pre-computed, result
|
|
bool IsNSObject (TypeDefinition type)
|
|
{
|
|
if (!type.IsNSObject (LinkContext))
|
|
return false;
|
|
cached_isnsobject.Add (type);
|
|
return true;
|
|
}
|
|
|
|
bool IsWrapperType (TypeDefinition type)
|
|
{
|
|
var registerAttribute = LinkContext.StaticRegistrar.GetRegisterAttribute (type);
|
|
return registerAttribute?.IsWrapper == true || registerAttribute?.SkipRegistration == true;
|
|
}
|
|
|
|
// Cache the results of the IsCIFilter check in a dictionary. It makes this method slightly faster
|
|
// (total time spent in IsCIFilter when linking monotouch-test went from 11 ms to 3ms).
|
|
static Dictionary<TypeReference, bool> ci_filter_types = new Dictionary<TypeReference, bool> ();
|
|
bool IsCIFilter (TypeReference type)
|
|
{
|
|
if (type == null)
|
|
return false;
|
|
|
|
bool rv;
|
|
if (!ci_filter_types.TryGetValue (type, out rv)) {
|
|
rv = type.Is (Namespaces.CoreImage, "CIFilter") || IsCIFilter (type.Resolve ().BaseType);
|
|
ci_filter_types [type] = rv;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void SetIsDirectBindingValue (TypeDefinition type)
|
|
{
|
|
if (isdirectbinding_value.ContainsKey (type))
|
|
return;
|
|
|
|
// We have a special implementation of CIFilters, and we do not want to
|
|
// optimize anything for those classes to not risk optimizing this wrong.
|
|
// This means we must set the IsDirectBinding value to null for CIFilter
|
|
// and all its base classes to allow both code paths and determine at runtime.
|
|
// References:
|
|
// * https://github.com/xamarin/xamarin-macios/pull/3055
|
|
// * https://bugzilla.xamarin.com/show_bug.cgi?id=15465
|
|
if (IsCIFilter (type)) {
|
|
isdirectbinding_value [type] = null;
|
|
var base_type = type.BaseType.Resolve ();
|
|
while (base_type != null && IsNSObject (base_type)) {
|
|
isdirectbinding_value [base_type] = null;
|
|
base_type = base_type.BaseType.Resolve ();
|
|
}
|
|
return;
|
|
}
|
|
|
|
var isWrapperType = IsWrapperType (type);
|
|
|
|
if (!isWrapperType) {
|
|
isdirectbinding_value [type] = false;
|
|
|
|
// We must clear IsDirectBinding for any wrapper superclasses.
|
|
var base_type = type.BaseType.Resolve ();
|
|
while (base_type != null && IsNSObject (base_type)) {
|
|
if (IsWrapperType (base_type))
|
|
isdirectbinding_value [base_type] = null;
|
|
base_type = base_type.BaseType.Resolve ();
|
|
}
|
|
} else {
|
|
isdirectbinding_value [type] = true; // Let's try 'true' first, any derived non-wrapper classes will clear it if needed
|
|
}
|
|
}
|
|
}
|
|
} |