Imported Upstream version 5.20.0.180

Former-commit-id: ff953ca879339fe1e1211f7220f563e1342e66cb
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2019-02-04 20:11:37 +00:00
parent 0e2d47d1c8
commit 0510252385
3360 changed files with 83827 additions and 39243 deletions

View File

@@ -21,3 +21,95 @@ TODO
## Build & Test Status
[![Build Status](https://jenkins.mono-project.com/buildStatus/icon?job=test-linker-mainline)](https://jenkins.mono-project.com/job/test-linker-mainline/)
## Link xml file examples
A link xml file can be used to explicitly preserve assemblies, types, and members. Below is a sample file containing examples of various usages.
```xml
<linker>
<!--
Preserve types and members in an assembly
-->
<assembly fullname="Assembly1">
<!--Preserve an entire type-->
<type fullname="Assembly1.A" preserve="all"/>
<!--No "preserve" attribute and no members specified means preserve all members-->
<type fullname="Assembly1.B"/>
<!--Preserve all fields on a type-->
<type fullname="Assembly1.C" preserve="fields"/>
<!--Preserve all fields on a type-->
<type fullname="Assembly1.D" preserve="methods"/>
<!--Preserve the type only-->
<type fullname="Assembly1.E" preserve="nothing"/>
<!--Preserving only specific members of a type-->
<type fullname="Assembly1.F">
<!--
Fields
-->
<field signature="System.Int32 field1" />
<!--Preserve a field by name rather than signature-->
<field name="field2" />
<!--
Methods
-->
<method signature="System.Void Method1()" />
<!--Preserve a method with parameters-->
<method signature="System.Void Method2(System.Int32,System.String)" />
<!--Preserve a method by name rather than signature-->
<method name="Method3" />
<!--
Properties
-->
<!--Preserve a property, it's backing field (if present), getter, and setter methods-->
<property signature="System.Int32 Property1" />
<property signature="System.Int32 Property2" accessors="all" />
<!--Preserve a property, it's backing field (if present), and getter method-->
<property signature="System.Int32 Property3" accessors="get" />
<!--Preserve a property, it's backing field (if present), and setter method-->
<property signature="System.Int32 Property4" accessors="set" />
<!--Preserve a property by name rather than signature-->
<property name="Property5" />
<!--
Events
-->
<!--Preserve an event, it's backing field (if present), add, and remove methods-->
<event signature="System.EventHandler Event1" />
<!--Preserve an event by name rather than signature-->
<event name="Event2" />
</type>
<!--Examples with generics-->
<type fullname="Assembly1.G`1">
<!--Preserve a field with generics in the signature-->
<field signature="System.Collections.Generic.List`1&lt;System.Int32&gt; field1" />
<field signature="System.Collections.Generic.List`1&lt;T&gt; field2" />
<!--Preserve a method with generics in the signature-->
<method signature="System.Void Method1(System.Collections.Generic.List`1&lt;System.Int32&gt;)" />
<!--Preserve an event with generics in the signature-->
<event signature="System.EventHandler`1&lt;System.EventArgs&gt; Event1" />
</type>
<!--Preserve a nested type-->
<type fullname="Assembly1.H/Nested" preserve="all"/>
<!--Preserve all fields of a type if the type is used. If the type is not used it will be removed-->
<type fullname="Assembly1.I" preserve="fields" required="0"/>
<!--Preserve all methods of a type if the type is used. If the type is not used it will be removed-->
<type fullname="Assembly1.J" preserve="methods" required="0"/>
<!--Preserve all types in a namespace-->
<type fullname="Assembly1.SomeNamespace*" />
<!--Preserve all types with a common prefix in their name-->
<type fullname="Prefix*" />
</assembly>
<!--
Preserve an entire assembly
-->
<assembly fullname="Assembly2" preserve="all"/>
<!--No "preserve" attribute and no types specified means preserve all-->
<assembly fullname="Assembly3"/>
<!--
Fully qualified assembly name
-->
<assembly fullname="Assembly4, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<type fullname="Assembly4.Foo" preserve="all"/>
</assembly>
</linker>
```

View File

@@ -34,21 +34,19 @@ using Mono.Cecil;
namespace Mono.Linker.Steps {
public class LoadReferencesStep : BaseStep {
readonly Dictionary<AssemblyNameDefinition, AssemblyDefinition> _references = new Dictionary<AssemblyNameDefinition, AssemblyDefinition> ();
readonly HashSet<AssemblyNameDefinition> references = new HashSet<AssemblyNameDefinition> ();
protected override void ProcessAssembly (AssemblyDefinition assembly)
{
ProcessReferences (assembly);
}
void ProcessReferences (AssemblyDefinition assembly)
protected void ProcessReferences (AssemblyDefinition assembly)
{
if (_references.ContainsKey (assembly.Name))
if (!references.Add (assembly.Name))
return;
_references.Add (assembly.Name, assembly);
Context.RegisterAssembly (assembly);
foreach (AssemblyDefinition referenceDefinition in Context.ResolveReferences (assembly)) {

View File

@@ -253,8 +253,8 @@ namespace Mono.Linker.Steps {
Tracer.Push (provider);
try {
foreach (CustomAttribute ca in provider.CustomAttributes) {
if (IsUserDependencyMarker (ca.AttributeType)) {
MarkUserDependency (provider as MethodReference, ca);
if (IsUserDependencyMarker (ca.AttributeType) && provider is MemberReference mr) {
MarkUserDependency (mr, ca);
continue;
}
@@ -274,14 +274,13 @@ namespace Mono.Linker.Steps {
protected virtual bool IsUserDependencyMarker (TypeReference type)
{
return type.Name == "PreserveDependencyAttribute" &&
type.Namespace == "System.Runtime.CompilerServices";
return PreserveDependencyLookupStep.IsPreserveDependencyAttribute (type);
}
protected virtual void MarkUserDependency (MethodReference context, CustomAttribute ca)
protected virtual void MarkUserDependency (MemberReference context, CustomAttribute ca)
{
var args = ca.ConstructorArguments;
if (args.Count == 2 && args[1].Value is string condition) {
if (ca.HasProperties && ca.Properties [0].Name == "Condition") {
var condition = ca.Properties [0].Argument.Value as string;
switch (condition) {
case "":
case null:
@@ -297,57 +296,51 @@ namespace Mono.Linker.Steps {
}
}
if (args.Count >= 1 && args[0].Value is string dependency) {
string member = null;
string type = null;
string[] signature = null;
TypeDefinition td = null;
var sign_start = dependency.IndexOf ('(');
var sign_end = dependency.LastIndexOf (')');
if (sign_start > 0 && sign_end > sign_start) {
var parameters = dependency.Substring (sign_start + 1, sign_end - sign_start - 1).Replace (" ", "");
signature = string.IsNullOrEmpty (parameters) ? Array.Empty<string> () : parameters.Split (',');
var idx = dependency.LastIndexOf ('.', sign_start);
if (idx > 0) {
member = dependency.Substring (idx + 1, sign_start - idx - 1).TrimEnd ();
type = dependency.Substring (0, idx);
} else {
member = dependency.Substring (0, sign_start - 1);
td = context.DeclaringType.Resolve ();
}
} else if (sign_start < 0) {
var idx = dependency.LastIndexOf ('.');
if (idx > 0) {
member = dependency.Substring (idx + 1);
type = dependency.Substring (0, idx);
} else {
member = dependency;
td = context.DeclaringType.Resolve ();
}
AssemblyDefinition assembly;
var args = ca.ConstructorArguments;
if (args.Count >= 3 && args [2].Value is string assemblyName) {
if (!_context.Resolver.AssemblyCache.TryGetValue (assemblyName, out assembly)) {
_context.Logger.LogMessage (MessageImportance.Low, $"Could not resolve '{assemblyName}' assembly dependency");
return;
}
} else {
assembly = null;
}
TypeDefinition td = null;
if (args.Count >= 2 && args [1].Value is string typeName) {
td = FindType (assembly ?? context.Module.Assembly, typeName);
if (td == null) {
if (type == null) {
_context.Logger.LogMessage (MessageImportance.Low, $"Could not resolve '{dependency}' dependency");
return;
}
td = FindType (context.Module.Assembly, type);
if (td == null) {
_context.Logger.LogMessage (MessageImportance.Low, $"Could not find '{dependency}' dependency");
return;
}
_context.Logger.LogMessage (MessageImportance.Low, $"Could not resolve '{typeName}' type dependency");
return;
}
if (MarkDependencyMethod (td, member, signature))
return;
if (MarkDependencyField (td, member))
return;
_context.Logger.LogMessage (MessageImportance.High, $"Could not resolve dependency member '{member}' declared in type '{dependency}'");
} else {
td = context.DeclaringType.Resolve ();
}
string member = null;
string[] signature = null;
if (args.Count >= 1 && args [0].Value is string memberSignature) {
memberSignature = memberSignature.Replace (" ", "");
var sign_start = memberSignature.IndexOf ('(');
var sign_end = memberSignature.LastIndexOf (')');
if (sign_start > 0 && sign_end > sign_start) {
var parameters = memberSignature.Substring (sign_start + 1, sign_end - sign_start - 1);
signature = string.IsNullOrEmpty (parameters) ? Array.Empty<string> () : parameters.Split (',');
member = memberSignature.Substring (0, sign_start);
} else {
member = memberSignature;
}
}
if (MarkDependencyMethod (td, member, signature))
return;
if (MarkDependencyField (td, member))
return;
_context.Logger.LogMessage (MessageImportance.High, $"Could not resolve dependency member '{member}' declared in type '{td.FullName}'");
}
static TypeDefinition FindType (AssemblyDefinition assembly, string fullName)
@@ -460,6 +453,9 @@ namespace Mono.Linker.Steps {
case "System.ThreadStaticAttribute":
case "System.ContextStaticAttribute":
return true;
case "System.Runtime.InteropServices.InterfaceTypeAttribute":
case "System.Runtime.InteropServices.GuidAttribute":
return !_context.IsFeatureExcluded ("com");
}
if (!Annotations.IsMarked (attr_type.Resolve ()))
@@ -480,7 +476,7 @@ namespace Mono.Linker.Steps {
// then surely nothing is using this attribute and there is no need to mark it
if (!Annotations.IsMarked (resolvedConstructor.Module) && !Annotations.IsMarked (ca.AttributeType))
return false;
if (ca.Constructor.DeclaringType.Namespace == "System.Diagnostics") {
string attributeName = ca.Constructor.DeclaringType.Name;
if (attributeName == "DebuggerDisplayAttribute" || attributeName == "DebuggerTypeProxyAttribute") {
@@ -815,6 +811,7 @@ namespace Mono.Linker.Steps {
MarkType (field.FieldType);
MarkCustomAttributes (field);
MarkMarshalSpec (field);
DoAdditionalFieldProcessing (field);
Annotations.Mark (field);
}
@@ -930,8 +927,23 @@ namespace Mono.Linker.Steps {
{
}
// Allow subclassers to mark additional things when marking a method
protected virtual void DoAdditionalTypeProcessing (TypeDefinition method)
// Allow subclassers to mark additional things
protected virtual void DoAdditionalTypeProcessing (TypeDefinition type)
{
}
// Allow subclassers to mark additional things
protected virtual void DoAdditionalFieldProcessing (FieldDefinition field)
{
}
// Allow subclassers to mark additional things
protected virtual void DoAdditionalPropertyProcessing (PropertyDefinition property)
{
}
// Allow subclassers to mark additional things
protected virtual void DoAdditionalEventProcessing (EventDefinition evt)
{
}
@@ -1744,6 +1756,7 @@ namespace Mono.Linker.Steps {
protected void MarkProperty (PropertyDefinition prop)
{
MarkCustomAttributes (prop);
DoAdditionalPropertyProcessing (prop);
}
protected virtual void MarkEvent (EventDefinition evt)
@@ -1752,6 +1765,7 @@ namespace Mono.Linker.Steps {
MarkMethodIfNotNull (evt.AddMethod);
MarkMethodIfNotNull (evt.InvokeMethod);
MarkMethodIfNotNull (evt.RemoveMethod);
DoAdditionalEventProcessing (evt);
}
void MarkMethodIfNotNull (MethodReference method)
@@ -1856,7 +1870,6 @@ namespace Mono.Linker.Steps {
if (!CheckReflectionMethod (instruction, reflectionMethod))
continue;
_context.Tracer.Push ($"Reflection-{instruction.Operand as MethodReference}");
var nameOfThingUsedViaReflection = OperandOfNearestInstructionBefore<string> (i, OpCodes.Ldstr, instructions);
var bindingFlags = (BindingFlags) OperandOfNearestInstructionBefore<sbyte> (i, OpCodes.Ldc_I4_S, instructions);
@@ -1868,7 +1881,6 @@ namespace Mono.Linker.Steps {
if (typeDefinition != null)
markMethod (instructions, nameOfThingUsedViaReflection, typeDefinition, bindingFlags);
}
_context.Tracer.Pop ();
}
}
@@ -1880,32 +1892,44 @@ namespace Mono.Linker.Steps {
if (!CheckReflectionMethod (instruction, "GetType"))
continue;
_context.Tracer.Push ($"Reflection-{instruction.Operand as MethodReference}");
var typeAssemblyQualifiedName = OperandOfNearestInstructionBefore<string> (i, OpCodes.Ldstr, instructions);
if (!TypeNameParser.TryParseTypeAssemblyQualifiedName (typeAssemblyQualifiedName, out string typeName, out string assemblyName))
continue;
TypeDefinition foundType = null;
foreach (var assemblyDefinition in _context.GetAssemblies ()) {
if (assemblyName != null && assemblyDefinition.Name.Name != assemblyName)
continue;
var type = assemblyDefinition.MainModule.GetType (typeName);
if (type != null)
{
MarkType(type);
foundType = assemblyDefinition.MainModule.GetType (typeName);
if (foundType != null)
break;
}
}
_context.Tracer.Pop ();
if (foundType == null)
continue;
_context.Tracer.Push ($"Reflection-{foundType}");
try {
MarkType (foundType);
} finally {
_context.Tracer.Pop ();
}
}
}
void MarkConstructorsUsedViaReflection (Collection<Instruction> instructions, string unused, TypeDefinition declaringType, BindingFlags bindingFlags)
{
foreach (var method in declaringType.Methods) {
if ((bindingFlags == BindingFlags.Default || bindingFlags.IsSet(BindingFlags.Public) == method.IsPublic) && method.Name == ".ctor")
MarkMethod (method);
if ((bindingFlags == BindingFlags.Default || bindingFlags.IsSet(BindingFlags.Public) == method.IsPublic) && method.Name == ".ctor") {
Tracer.Push ($"Reflection-{method}");
try {
MarkMethod (method);
} finally {
Tracer.Pop ();
}
}
}
}
@@ -1916,8 +1940,14 @@ namespace Mono.Linker.Steps {
foreach (var method in declaringType.Methods) {
if ((bindingFlags == BindingFlags.Default || bindingFlags.IsSet(BindingFlags.Public) == method.IsPublic && bindingFlags.IsSet(BindingFlags.Static) == method.IsStatic)
&& method.Name == name)
MarkMethod (method);
&& method.Name == name) {
Tracer.Push ($"Reflection-{method}");
try {
MarkMethod (method);
} finally {
Tracer.Pop ();
}
}
}
}
@@ -1928,11 +1958,16 @@ namespace Mono.Linker.Steps {
foreach (var property in declaringType.Properties) {
if (property.Name == name) {
// It is not easy to reliably detect in the IL code whether the getter or setter (or both) are used.
// Be conservative and mark everything for the property.
MarkProperty (property);
MarkMethodIfNotNull (property.GetMethod);
MarkMethodIfNotNull (property.SetMethod);
Tracer.Push ($"Reflection-{property}");
try {
// It is not easy to reliably detect in the IL code whether the getter or setter (or both) are used.
// Be conservative and mark everything for the property.
MarkProperty (property);
MarkMethodIfNotNull (property.GetMethod);
MarkMethodIfNotNull (property.SetMethod);
} finally {
Tracer.Pop ();
}
}
}
}
@@ -1943,8 +1978,14 @@ namespace Mono.Linker.Steps {
return;
foreach (var field in declaringType.Fields) {
if (field.Name == name)
MarkField (field);
if (field.Name == name) {
Tracer.Push ($"Reflection-{field}");
try {
MarkField (field);
} finally {
Tracer.Pop ();
}
}
}
}
@@ -1954,8 +1995,14 @@ namespace Mono.Linker.Steps {
return;
foreach (var eventInfo in declaringType.Events) {
if (eventInfo.Name == name)
MarkEvent (eventInfo);
if (eventInfo.Name == name) {
Tracer.Push ($"Reflection-{eventInfo}");
try {
MarkEvent (eventInfo);
} finally {
Tracer.Pop ();
}
}
}
}

View File

@@ -0,0 +1,86 @@
//
// PreserveDependencyLookupStep.cs
//
// Author:
// Marek Safar (marek.safar@gmail.com)
//
// Copyright (C) 2018 Microsoft Corporation
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using Mono.Cecil;
using Mono.Collections.Generic;
namespace Mono.Linker.Steps {
public class PreserveDependencyLookupStep : LoadReferencesStep {
protected override void ProcessAssembly (AssemblyDefinition assembly)
{
var module = assembly.MainModule;
foreach (var type in module.Types) {
if (type.HasMethods) {
foreach (var method in type.GetMethods ()) {
var md = method.Resolve ();
if (md?.HasCustomAttributes != true)
continue;
ProcessPreserveDependencyAttribute (md.CustomAttributes);
}
}
if (type.HasFields) {
foreach (var field in type.Fields) {
var md = field.Resolve ();
if (md?.HasCustomAttributes != true)
continue;
ProcessPreserveDependencyAttribute (md.CustomAttributes);
}
}
}
}
public static bool IsPreserveDependencyAttribute (TypeReference tr)
{
return tr.Name == "PreserveDependencyAttribute" && tr.Namespace == "System.Runtime.CompilerServices";
}
void ProcessPreserveDependencyAttribute (Collection<CustomAttribute> attributes)
{
foreach (var ca in attributes) {
if (!IsPreserveDependencyAttribute (ca.AttributeType))
continue;
if (ca.ConstructorArguments.Count != 3)
continue;
var assemblyName = ca.ConstructorArguments [2].Value as string;
if (assemblyName == null)
continue;
var newDependency = Context.Resolve (new AssemblyNameReference (assemblyName, new Version ()));
if (newDependency != null)
ProcessReferences (newDependency);
}
}
}
}

View File

@@ -50,7 +50,7 @@ namespace Mono.Linker.Steps {
{
assemblies = Context.Annotations.GetAssemblies ().ToArray ();
foreach (var assembly in assemblies) {
SweepAssembly (assembly);
ProcessAssemblyAction (assembly);
if ((Annotations.GetAction (assembly) == AssemblyAction.Copy) &&
!Context.KeepTypeForwarderOnlyAssemblies) {
// Copy assemblies can still contain Type references with
@@ -73,36 +73,41 @@ namespace Mono.Linker.Steps {
}
}
protected virtual void SweepAssembly (AssemblyDefinition assembly)
protected void ProcessAssemblyAction (AssemblyDefinition assembly)
{
switch (Annotations.GetAction (assembly)) {
case AssemblyAction.Link:
if (!IsMarkedAssembly (assembly)) {
RemoveAssembly (assembly);
case AssemblyAction.Link:
if (!IsMarkedAssembly (assembly)) {
RemoveAssembly (assembly);
return;
}
break;
case AssemblyAction.AddBypassNGenUsed:
if (!IsMarkedAssembly (assembly)) {
RemoveAssembly (assembly);
} else {
Annotations.SetAction (assembly, AssemblyAction.AddBypassNGen);
}
return;
}
break;
case AssemblyAction.AddBypassNGenUsed:
if (!IsMarkedAssembly (assembly)) {
RemoveAssembly (assembly);
} else {
Annotations.SetAction (assembly, AssemblyAction.AddBypassNGen);
}
return;
case AssemblyAction.CopyUsed:
if (!IsMarkedAssembly (assembly)) {
RemoveAssembly (assembly);
} else {
Annotations.SetAction (assembly, AssemblyAction.Copy);
}
return;
case AssemblyAction.CopyUsed:
if (!IsMarkedAssembly (assembly)) {
RemoveAssembly (assembly);
} else {
Annotations.SetAction (assembly, AssemblyAction.Copy);
}
return;
default:
return;
default:
return;
}
SweepAssembly (assembly);
}
protected virtual void SweepAssembly (AssemblyDefinition assembly)
{
var types = new List<TypeDefinition> ();
foreach (TypeDefinition type in assembly.MainModule.Types) {
@@ -134,7 +139,7 @@ namespace Mono.Linker.Steps {
return Annotations.IsMarked (assembly.MainModule);
}
void RemoveAssembly (AssemblyDefinition assembly)
protected virtual void RemoveAssembly (AssemblyDefinition assembly)
{
Annotations.SetAction (assembly, AssemblyAction.Delete);
@@ -394,6 +399,8 @@ namespace Mono.Linker.Steps {
foreach (var parameter in method.Parameters)
SweepCustomAttributes (parameter);
SweepCustomAttributes (method.MethodReturnType);
}
}

View File

@@ -434,6 +434,7 @@ namespace Mono.Linker {
Pipeline p = new Pipeline ();
p.AppendStep (new LoadReferencesStep ());
p.AppendStep (new BlacklistStep ());
p.AppendStep (new PreserveDependencyLookupStep ());
p.AppendStep (new TypeMapStep ());
p.AppendStep (new MarkStep ());
p.AppendStep (new SweepStep ());

View File

@@ -123,12 +123,17 @@ namespace Mono.Linker {
{
while (_steps.Count > 0) {
IStep step = _steps [0];
context.Tracer.Push (step);
step.Process (context);
context.Tracer.Pop ();
ProcessStep (context, step);
_steps.Remove (step);
}
}
protected virtual void ProcessStep (LinkContext context, IStep step)
{
context.Tracer.Push (step);
step.Process (context);
context.Tracer.Pop ();
}
public IStep [] GetSteps ()
{

View File

@@ -99,6 +99,7 @@
<Compile Include="Linker\ILogger.cs" />
<Compile Include="Linker\ConsoleLogger.cs" />
<Compile Include="Linker\Tracer.cs" />
<Compile Include="Linker.Steps\PreserveDependencyLookupStep.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />