xamarin-macios/tools/linker/MobileMarkStep.cs

642 строки
21 KiB
C#

// Copyright 2011-2013 Xamarin Inc., All rights reserved.
// adapted from MonoTouchMarkStep.cs, itself
// adapted from xtouch/tools/mtouch/Touch.Tuner/ManualMarkStep.cs
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Linker;
using Mono.Linker.Steps;
using Mono.Tuner;
using Xamarin.Tuner;
namespace Xamarin.Linker.Steps {
// XML definition files have their limits, i.e. they are good to keep stuff around unconditionnally
// e.g. we do not want to force all/most Socket code around (non-network apps) because some types have unmanaged representation
//
// Generated backend fields inside monomac.dll are also removed if only used (i.e. set to null)
// inside the Dispose method
public class MobileMarkStep : MarkStep {
protected virtual bool DebugBuild { get; set; }
protected DerivedLinkContext LinkContext {
get {
return (DerivedLinkContext) base._context;
}
}
protected AssemblyDefinition GetAssembly (string assemblyName)
{
AssemblyDefinition ad;
_context.TryGetLinkedAssembly (assemblyName, out ad);
return ad;
}
protected TypeDefinition GetType (string assemblyName, string typeName)
{
AssemblyDefinition ad = GetAssembly (assemblyName);
return ad is null ? null : GetType (ad, typeName);
}
protected TypeDefinition GetType (AssemblyDefinition assembly, string typeName)
{
return assembly.MainModule.GetType (typeName);
}
protected override TypeDefinition MarkType (TypeReference reference)
{
TypeDefinition type = base.MarkType (GetOriginalType (reference));
if (type is null)
return null;
switch (type.Module.Assembly.Name.Name) {
case "mscorlib":
ProcessCorlib (type);
break;
case "System":
ProcessSystem (type);
break;
case "System.Core":
ProcessSystemCore (type);
break;
case "System.Data":
ProcessSystemData (type);
break;
case "System.Data.Services.Client":
ProcessSystemDataServicesClient (type);
break;
case "System.Runtime.Serialization":
ProcessSystemRuntimeSerialization (type);
break;
case "System.ServiceModel":
ProcessSystemServiceModel (type);
break;
case "System.Web.Services":
ProcessSystemWebServices (type);
break;
case "System.Xml":
ProcessSystemXml (type);
break;
case "System.Xml.Linq":
ProcessSystemXmlLinq (type);
break;
case "Mono.Security":
ProcessMonoSecurity (type);
break;
case "System.ComponentModel.Composition":
ProcessSystemComponentModelComposition (type);
break;
}
return type;
}
protected override bool MarkMethods (TypeDefinition type)
{
// type can be null if we're not linking the assembly where the type reside,
// e.g. --linkskip=System, in such case GetType returns null and an NRE can occur
if (type is null)
return false;
return base.MarkMethods (type);
}
protected void MarkStaticMethods (TypeDefinition type)
{
if ((type is null) || !type.HasMethods)
return;
foreach (MethodDefinition m in type.Methods) {
if (m.IsStatic)
MarkMethod (m);
}
}
protected void MarkConstructors (TypeDefinition type)
{
if ((type is null) || !type.HasMethods)
return;
foreach (MethodDefinition ctor in type.Methods) {
if (ctor.IsConstructor)
MarkMethod (ctor);
}
}
protected IDictionary<string, List<MethodDefinition>> PInvokeModules { get; set; }
protected override MethodDefinition MarkMethod (MethodReference reference)
{
var method = base.MarkMethod (reference);
if ((method is not null) && !method.HasBody && method.IsPInvokeImpl) {
// for some C++ stuff HasPInvokeInfo can be true without giving back any info
PInvokeInfo info = method.PInvokeInfo;
if (info is not null) {
var m = info.Module;
Annotations.Mark (m);
string name = m.Name;
if (PInvokeModules is not null && !String.IsNullOrEmpty (name)) {
List<MethodDefinition> methods;
if (!PInvokeModules.TryGetValue (name, out methods))
PInvokeModules.Add (name, methods = new List<MethodDefinition> ());
methods.Add (method);
}
}
}
return method;
}
protected override void MarkCustomAttribute (CustomAttribute ca)
{
base.MarkCustomAttribute (ca);
if (ca.HasConstructorArguments) {
PreserveTypeConverters (ca);
if (DebugBuild)
PreserveDebuggerAttributes (ca);
}
}
void PreserveTypeConverters (CustomAttribute ca)
{
if (!ca.Constructor.DeclaringType.Is ("System.ComponentModel", "TypeConverterAttribute"))
return;
var type = (ca.ConstructorArguments [0].Value as TypeReference);
// note: it could be a string (valid or not) and we do not support that since it's too late
// to add new assemblies into the pipeline
if (type is not null)
MarkDefaultConstructor (type.Resolve ());
}
void PreserveDebuggerAttributes (CustomAttribute ca)
{
if (!ca.AttributeType.Is ("System.Diagnostics", "DebuggerTypeProxyAttribute"))
return;
// preserve the type of the proxy for better debugging
var arg = ca.ConstructorArguments [0];
var type = arg.Type;
if (!type.Is ("System", "Type"))
return;
var tr = (arg.Value as TypeReference);
if (tr is null)
return;
var td = tr.Resolve ();
// the debugger will instantiate the ctor (signature based on the type)
MarkConstructors (td);
// and will query its properties (getters), so they must be preserved
// anything else will be marked when the linker process the code
if (!td.HasProperties)
return;
foreach (var p in td.Properties) {
MarkProperty (p);
MarkMethod (p.GetMethod);
}
}
// let product filters what get's included to help preserve extraneous serialization members
// e.g. ensure we don't keep stuff that triggers DRM checks
protected virtual IMetadataTokenProvider FilterExtraSerializationMembers (IMetadataTokenProvider provider)
{
return provider;
}
// in the cases where tracking the type is not possible (or easy), e.g. instances kept in a collection
void MarkMetadata (IMetadataTokenProvider tp)
{
var provider = FilterExtraSerializationMembers (tp);
if (provider is null)
return;
TypeReference tr = (provider as TypeReference);
if (tr is not null) {
MarkType (tr);
return;
}
MethodReference mr = (provider as MethodReference);
if (mr is not null) {
MarkMethod (mr);
return;
}
PropertyDefinition pd = (provider as PropertyDefinition);
if (pd is not null) {
MarkProperty (pd);
if (pd.GetMethod is not null)
MarkMethod (pd.GetMethod);
if (pd.SetMethod is not null)
MarkMethod (pd.SetMethod);
return;
}
FieldReference fr = (provider as FieldReference);
if (fr is not null) {
MarkField (fr);
return;
}
EventDefinition ed = (provider as EventDefinition);
if (ed is not null) {
MarkEvent (ed);
return;
}
throw new NotImplementedException (provider.ToString ());
}
// FIXME: we could be more precise (per field) but that would require a lot more maintenance for a very small gain
void ProcessCorlib (TypeDefinition type)
{
switch (type.Namespace) {
case "System.Runtime.CompilerServices":
switch (type.Name) {
case "AsyncTaskMethodBuilder":
case "AsyncTaskMethodBuilder`1":
if (DebugBuild) {
MarkNamedMethod (type, "SetNotificationForWaitCompletion");
MarkNamedMethod (type, "get_ObjectIdForDebugger");
}
break;
}
break;
case "System.Threading.Tasks":
switch (type.Name) {
case "Task":
if (DebugBuild)
MarkNamedMethod (type, "NotifyDebuggerOfWaitCompletion");
break;
}
break;
case "System.Security.Cryptography":
switch (type.Name) {
case "Aes":
#if MONOTOUCH
// Aes uses Activator.Create to be able to instantiate types from System.Core
TypeDefinition aes = GetType ("System.Core", "System.Security.Cryptography.AesManaged");
MarkMethods (aes);
#else
// for historical reasons monotouch.dll shipped AesManaged and XA used (mono default's) CSP
TypeDefinition aes = GetType ("System.Core", "System.Security.Cryptography.AesCryptoServiceProvider");
MarkMethods (aes);
#endif
break;
}
break;
}
}
void ProcessSystem (TypeDefinition type)
{
switch (type.Namespace) {
case "System.ComponentModel":
switch (type.Name) {
case "TypeDescriptor":
// DefaultConverters are created using Activator.CreateInstance
// keep in sync with ReflectTypeDescriptionProvider.cs
MarkMethods (GetType ("System", "System.ComponentModel.BooleanConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.ByteConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.SByteConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.CharConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.DoubleConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.StringConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.Int32Converter"));
MarkMethods (GetType ("System", "System.ComponentModel.Int16Converter"));
MarkMethods (GetType ("System", "System.ComponentModel.Int64Converter"));
MarkMethods (GetType ("System", "System.ComponentModel.SingleConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.UInt16Converter"));
MarkMethods (GetType ("System", "System.ComponentModel.UInt32Converter"));
MarkMethods (GetType ("System", "System.ComponentModel.UInt64Converter"));
MarkMethods (GetType ("System", "System.ComponentModel.TypeConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.CultureInfoConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.DateTimeConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.DateTimeOffsetConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.DecimalConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.TimeSpanConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.GuidConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.ArrayConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.CollectionConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.EnumConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.ReferenceConverter"));
MarkMethods (GetType ("System", "System.ComponentModel.NullableConverter"));
// special case - it's not in the Hashtable from the above file (but needed)
MarkMethods (GetType ("System", "System.ComponentModel.ComponentConverter"));
break;
}
break;
case "System.Diagnostics":
switch (type.Name) {
// see mono/metadata/process.c
case "FileVersionInfo":
case "ProcessModule":
// fields are initialized by the runtime, if the type is here then all (instance) fields must be present
MarkFields (type, false);
break;
}
break;
case "System.Net.Sockets":
switch (type.Name) {
case "IPAddress":
// mono/metadata/socket-io.c directly access 'm_Address' and 'm_Numbers'
MarkFields (type, false);
break;
case "IPv6MulticastOption":
// mono/metadata/socket-io.c directly access 'group' and 'ifIndex' private instance fields
MarkFields (type, false);
break;
case "LingerOption":
// mono/metadata/socket-io.c directly access 'enabled' and 'seconds' private instance fields
MarkFields (type, false);
break;
case "MulticastOption":
// mono/metadata/socket-io.c directly access 'group' and 'local' private instance fields
MarkFields (type, false);
break;
case "Socket":
// mono/metadata/socket-io.c directly access 'ipv4Supported', 'ipv6Supported' (static) and 'socket' (instance)
MarkFields (type, true);
break;
case "SocketAddress":
// mono/metadata/socket-io.c directly access 'data'
MarkFields (type, false);
break;
}
break;
case "":
if (!type.IsNested)
break;
switch (type.Name) {
case "SocketAsyncResult":
// mono/metadata/socket-io.h defines this structure (MonoSocketAsyncResult) for the runtime usage
MarkFields (type, false);
break;
case "ProcessAsyncReader":
// mono/metadata/socket-io.h defines this structure (MonoSocketAsyncResult) for the runtime usage
MarkFields (type, false);
break;
}
break;
}
}
void ProcessSystemCore (TypeDefinition type)
{
switch (type.Namespace) {
case "System.Linq":
switch (type.Name) {
case "Queryable":
// reflection is used and the generic version might not be used elsewhere in the app, ref: #37563
TypeDefinition enumquery = GetType ("System.Core", "System.Linq.EnumerableQuery`1");
MarkConstructors (enumquery);
// corefx/src/System.Linq.Queryable/src/System/Linq/EnumerableRewriter.cs relies on reflection
// and assume that <quote>All static methods with arguments on Queryable have equivalents on Enumerable.</quote>
// ref: https://github.com/xamarin/xamarin-macios/issues/6346
TypeDefinition enumerable = GetType ("System.Core", "System.Linq.Enumerable");
MarkStaticMethods (enumerable);
break;
}
break;
case "System.Linq.Expressions":
switch (type.Name) {
case "Expression`1":
// internal Expression (Expression body, ReadOnlyCollection<ParameterExpression> parameters)
// is called thru reflection by Expression.CreateExpressionOf and since it does a
// `typeof(Expression<>)` we have our clue to include the (lone) .ctor - ref bug #14863
MarkConstructors (type);
break;
}
break;
}
}
void ProcessSystemData (TypeDefinition type)
{
switch (type.Namespace) {
case "System.Data.SqlTypes":
switch (type.Name) {
case "SqlXml":
// TODO: Needed only if CreateSqlReaderDelegate is used
TypeDefinition xml_reader = GetType ("System.Xml", "System.Xml.XmlReader");
MarkNamedMethod (xml_reader, "CreateSqlReader");
break;
}
break;
}
}
void ProcessSystemDataServicesClient (TypeDefinition type)
{
switch (type.Namespace) {
case "System.Data.Services.Client":
switch (type.Name) {
case "TypeSystem":
// the .cctor does a bunch of reflection on String, DateTime and Math members
// some stuff on Microsoft.VisualBasic too but we do not ship that
TypeDefinition string_td = GetType ("mscorlib", "System.String");
MarkNamedMethod (string_td, "Contains");
MarkNamedMethod (string_td, "EndsWith");
MarkNamedMethod (string_td, "StartsWith");
MarkNamedMethod (string_td, "IndexOf");
MarkNamedMethod (string_td, "Replace");
MarkNamedMethod (string_td, "Substring");
MarkNamedMethod (string_td, "ToLower");
MarkNamedMethod (string_td, "ToUpper");
MarkNamedMethod (string_td, "Trim");
MarkNamedMethod (string_td, "Concat");
MarkNamedMethod (string_td, "get_Length");
TypeDefinition datetime_td = GetType ("mscorlib", "System.DateTime");
MarkNamedMethod (datetime_td, "get_Day");
MarkNamedMethod (datetime_td, "get_Hour");
MarkNamedMethod (datetime_td, "get_Month");
MarkNamedMethod (datetime_td, "get_Minute");
MarkNamedMethod (datetime_td, "get_Second");
MarkNamedMethod (datetime_td, "get_Year");
TypeDefinition math_td = GetType ("mscorlib", "System.Math");
MarkNamedMethod (math_td, "Round");
MarkNamedMethod (math_td, "Floor");
MarkNamedMethod (math_td, "Ceiling");
break;
}
break;
}
}
bool system_runtime_serialization = false;
void ProcessSystemRuntimeSerialization (TypeDefinition type)
{
switch (type.Namespace) {
case "System.Runtime.Serialization.Json":
switch (type.Name) {
case "JsonSerializationWriter":
// note: "Item" (indexer) is also used thru reflection but you can't create an app without it
TypeDefinition dic = GetType ("mscorlib", "System.Collections.Generic.Dictionary`2");
MarkNamedMethod (dic, "get_Keys");
break;
case "JsonFormatWriterInterpreter":
TypeDefinition jwd = GetType ("System.Runtime.Serialization", "System.Runtime.Serialization.Json.JsonWriterDelegator");
MarkMethods (jwd);
break;
}
break;
case "System.Runtime.Serialization":
// MS referencesource use reflection to call the required methods to serialize each PrimitiveDataContract subclasses
// this goes thru XmlFormatGeneratorStatics and it's a better candidate (than PrimitiveDataContract) as there are other callers
switch (type.Name) {
case "XmlFormatGeneratorStatics":
TypeDefinition xwd = GetType ("System.Runtime.Serialization", "System.Runtime.Serialization.XmlWriterDelegator");
MarkMethods (xwd);
TypeDefinition xoswc = GetType ("System.Runtime.Serialization", "System.Runtime.Serialization.XmlObjectSerializerWriteContext");
MarkMethods (xoswc);
TypeDefinition xosrc = GetType ("System.Runtime.Serialization", "System.Runtime.Serialization.XmlObjectSerializerReadContext");
MarkMethods (xosrc);
TypeDefinition xrd = GetType ("System.Runtime.Serialization", "System.Runtime.Serialization.XmlReaderDelegator");
MarkMethods (xrd);
break;
case "CollectionDataContract":
// ensure the nested type, DictionaryEnumerator and GenericDictionaryEnumerator`2, can be created thru reflection
foreach (var nt in type.NestedTypes)
MarkConstructors (nt);
break;
}
if (system_runtime_serialization)
break;
system_runtime_serialization = true;
// if we're keeping this assembly and use the Serialization namespace inside user code then we
// must bring the all the members decorated with [Data[Contract|Member]] attributes from the SDK
var members = LinkContext.DataContract;
foreach (var member in members)
MarkMetadata (member);
members.Clear ();
break;
}
}
void ProcessSystemServiceModel (TypeDefinition type)
{
switch (type.Namespace) {
case "System.ServiceModel.MonoInternal":
switch (type.Name) {
case "ClientRuntimeChannel":
TypeDefinition nop = GetType ("System.ServiceModel", "System.ServiceModel.Channels.IChannelFactory`1");
MarkNamedMethod (nop, "CreateChannel");
break;
}
break;
}
}
bool system_web_services_description = false;
void ProcessSystemWebServices (TypeDefinition type)
{
switch (type.Namespace) {
case "System.Web.Services.Description":
if (system_web_services_description)
break;
system_web_services_description = true;
// that's an all-or-nothing proposition: the whole hand will be taken if you point a finger at it
foreach (var t in type.Module.Assembly.MainModule.Types) {
if (t.Namespace != "System.Web.Services.Description")
continue;
MarkMethods (GetType ("System.Web.Services", t.FullName));
}
break;
}
}
bool system_xml_serialization = false;
void ProcessSystemXml (TypeDefinition type)
{
switch (type.Namespace) {
case "System.Xml.Xsl":
switch (type.Name) {
case "XslCompiledTransform":
TypeDefinition nop = GetType ("System.Xml", "System.Xml.Xsl.NoOperationDebugger");
// only available on the mobile profile
if (nop is not null) {
MarkNamedMethod (nop, "OnCompile");
MarkNamedMethod (nop, "OnExecute");
}
break;
}
break;
case "System.Xml.Serialization":
if (system_xml_serialization)
break;
switch (type.Name) {
case "XmlIgnoreAttribute":
break;
default:
// if we're keeping this assembly and use the Serialization namespace inside user code
// then we must bring the all the members decorated with [Xml*] attributes from the SDK
system_xml_serialization = true;
var members = LinkContext.XmlSerialization;
foreach (var member in members)
MarkMetadata (member);
members.Clear ();
break;
}
break;
}
}
void ProcessSystemXmlLinq (TypeDefinition type)
{
switch (type.Namespace) {
case "System.Xml.Linq":
switch (type.Name) {
case "XElement":
// The addition of [XmlTypeConvertor ("ConvertForAssignment")] means the linker must keep the
// ConvertForAssignment method around
MarkNamedMethod (type, "ConvertForAssignment");
break;
}
break;
}
}
void ProcessMonoSecurity (TypeDefinition type)
{
switch (type.Namespace) {
case "Mono.Security.Protocol.Tls":
switch (type.Name) {
case "SslClientStream":
MarkNamedMethod (type, "get_SelectedClientCertificate");
break;
case "SslStreamBase":
MarkNamedMethod (type, "get_ServerCertificate");
break;
}
break;
}
}
void ProcessSystemComponentModelComposition (TypeDefinition type)
{
switch (type.Namespace) {
case "System.ComponentModel.Composition":
switch (type.Name) {
case "ExportServices":
MarkNamedMethod (type, "CreateStronglyTypedLazyOfT");
MarkNamedMethod (type, "CreateStronglyTypedLazyOfTM");
MarkNamedMethod (type, "CreateSemiStronglyTypedLazy");
break;
}
break;
case "System.ComponentModel.Composition.ReflectionModel":
switch (type.Name) {
case "ExportFactoryCreator":
MarkNamedMethod (type, "CreateStronglyTypedExportFactoryOfT");
MarkNamedMethod (type, "CreateStronglyTypedExportFactoryOfTM");
break;
}
break;
}
}
}
}