Imported Upstream version 5.12.0.220

Former-commit-id: c477e03582759447177c6d4bf412cd2355aad476
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-04-24 09:31:23 +00:00
parent 8bd104cef2
commit 8fc30896db
1200 changed files with 29534 additions and 26161 deletions

View File

@@ -46,6 +46,7 @@ namespace Mono.Linker.Steps {
protected Dictionary<TypeDefinition, CustomAttribute> _assemblyDebuggerDisplayAttributes;
protected Dictionary<TypeDefinition, CustomAttribute> _assemblyDebuggerTypeProxyAttributes;
protected Queue<CustomAttribute> _topLevelAttributes;
protected Queue<CustomAttribute> _lateMarkedAttributes;
public AnnotationStore Annotations {
get { return _context.Annotations; }
@@ -62,6 +63,7 @@ namespace Mono.Linker.Steps {
_methods = new Queue<MethodDefinition> ();
_virtual_methods = new List<MethodDefinition> ();
_topLevelAttributes = new Queue<CustomAttribute> ();
_lateMarkedAttributes = new Queue<CustomAttribute> ();
_assemblyDebuggerDisplayAttributes = new Dictionary<TypeDefinition, CustomAttribute> ();
_assemblyDebuggerTypeProxyAttributes = new Dictionary<TypeDefinition, CustomAttribute> ();
@@ -131,7 +133,7 @@ namespace Mono.Linker.Steps {
if (QueueIsEmpty ())
throw new InvalidOperationException ("No entry methods");
while (ProcessPrimaryQueue () || ProcessLazyAttributes ())
while (ProcessPrimaryQueue () || ProcessLazyAttributes () || ProcessLateMarkedAttributes ())
// deal with [TypeForwardedTo] pseudo-attributes
foreach (AssemblyDefinition assembly in _context.GetAssemblies ()) {
@@ -148,13 +150,9 @@ namespace Mono.Linker.Steps {
if (!isForwarder)
continue;
TypeDefinition type = null;
try {
type = exported.Resolve ();
}
catch (AssemblyResolutionException) {
TypeDefinition type = exported.Resolve ();
if (type == null)
continue;
}
if (!Annotations.IsMarked (type))
continue;
Tracer.Push (type);
@@ -260,10 +258,15 @@ namespace Mono.Linker.Steps {
Tracer.Push (provider);
try {
foreach (CustomAttribute ca in provider.CustomAttributes) {
if (!ShouldMarkCustomAttribute (ca))
continue;
MarkCustomAttribute (ca);
if (_context.KeepUsedAttributeTypesOnly) {
_lateMarkedAttributes.Enqueue (ca);
} else {
if (!ShouldMarkCustomAttribute (ca))
continue;
MarkCustomAttribute (ca);
}
}
} finally {
Tracer.Pop ();
@@ -305,11 +308,16 @@ namespace Mono.Linker.Steps {
protected virtual bool ShouldMarkCustomAttribute (CustomAttribute ca)
{
if (_context.KeepUsedAttributeTypesOnly && !Annotations.IsMarked (ca.AttributeType.Resolve ()))
return false;
return true;
}
protected virtual bool ShouldMarkTopLevelCustomAttribute (CustomAttribute ca, MethodDefinition resolvedConstructor)
{
if (!ShouldMarkCustomAttribute (ca))
return false;
// If an attribute's module has not been marked after processing all types in all assemblies and the attribute itself has not been marked,
// then surely nothing is using this attribute and there is no need to mark it
if (!Annotations.IsMarked (resolvedConstructor.Module) && !Annotations.IsMarked (ca.AttributeType))
@@ -574,6 +582,40 @@ namespace Mono.Linker.Steps {
return markOccurred;
}
bool ProcessLateMarkedAttributes ()
{
var startingQueueCount = _lateMarkedAttributes.Count;
if (startingQueueCount == 0)
return false;
var skippedItems = new List<CustomAttribute> ();
var markOccurred = false;
while (_lateMarkedAttributes.Count != 0) {
var customAttribute = _lateMarkedAttributes.Dequeue ();
var resolved = customAttribute.Constructor.Resolve ();
if (resolved == null) {
HandleUnresolvedMethod (customAttribute.Constructor);
continue;
}
if (!ShouldMarkCustomAttribute (customAttribute)) {
skippedItems.Add (customAttribute);
continue;
}
markOccurred = true;
MarkCustomAttribute (customAttribute);
}
// requeue the items we skipped in case we need to make another pass
foreach (var item in skippedItems)
_lateMarkedAttributes.Enqueue (item);
return markOccurred;
}
protected void MarkField (FieldReference reference)
{
// if (IgnoreScope (reference.DeclaringType.Scope))
@@ -663,7 +705,7 @@ namespace Mono.Linker.Steps {
MarkSecurityDeclarations (type);
if (IsMulticastDelegate (type)) {
MarkMethodCollection (type.Methods);
MarkMulticastDelegate (type);
}
if (IsSerializable (type))
@@ -1104,6 +1146,11 @@ namespace Mono.Linker.Steps {
}
}
protected virtual void MarkMulticastDelegate (TypeDefinition type)
{
MarkMethodCollection (type.Methods);
}
protected TypeDefinition ResolveTypeDefinition (TypeReference type)
{
TypeDefinition td = type as TypeDefinition;
@@ -1541,7 +1588,7 @@ namespace Mono.Linker.Steps {
MarkCustomAttributes (prop);
}
protected void MarkEvent (EventDefinition evt)
protected virtual void MarkEvent (EventDefinition evt)
{
MarkCustomAttributes (evt);
MarkMethodIfNotNull (evt.AddMethod);
@@ -1568,6 +1615,17 @@ namespace Mono.Linker.Steps {
foreach (Instruction instruction in body.Instructions)
MarkInstruction (instruction);
MarkThingsUsedViaReflection (body);
}
protected virtual void MarkThingsUsedViaReflection (MethodBody body)
{
MarkSomethingUsedViaReflection ("GetConstructor", MarkConstructorsUsedViaReflection, body.Instructions);
MarkSomethingUsedViaReflection ("GetMethod", MarkMethodsUsedViaReflection, body.Instructions);
MarkSomethingUsedViaReflection ("GetProperty", MarkPropertyUsedViaReflection, body.Instructions);
MarkSomethingUsedViaReflection ("GetField", MarkFieldUsedViaReflection, body.Instructions);
MarkSomethingUsedViaReflection ("GetEvent", MarkEventUsedViaReflection, body.Instructions);
}
protected virtual void MarkInstruction (Instruction instruction)
@@ -1615,5 +1673,152 @@ namespace Mono.Linker.Steps {
MarkCustomAttributes (iface);
MarkType (iface.InterfaceType);
}
void MarkSomethingUsedViaReflection (string reflectionMethod, Action<Collection<Instruction>, string, TypeDefinition, BindingFlags> markMethod, Collection<Instruction> instructions)
{
for (var i = 0; i < instructions.Count; i++) {
var instruction = instructions [i];
if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt)
continue;
var methodBeingCalled = instruction.Operand as MethodReference;
if (methodBeingCalled == null || methodBeingCalled.DeclaringType.Name != "Type" || methodBeingCalled.DeclaringType.Namespace != "System")
continue;
if (methodBeingCalled.Name != reflectionMethod)
continue;
_context.Tracer.Push ($"Reflection-{methodBeingCalled}");
var nameOfThingUsedViaReflection = OperandOfNearestInstructionBefore<string> (i, OpCodes.Ldstr, instructions);
var bindingFlags = (BindingFlags) OperandOfNearestInstructionBefore<sbyte> (i, OpCodes.Ldc_I4_S, instructions);
// There might be more than one ldtoken opcode above the call in the IL stream. Be conservative and check all of
// the types which were loaded for the method being used.
var declaringTypesOfThingInvokedViaReflection = OperandsOfInstructionsBefore (i, OpCodes.Ldtoken, instructions);
foreach (var declaringTypeOfThingInvokedViaReflection in declaringTypesOfThingInvokedViaReflection) {
var typeDefinition = declaringTypeOfThingInvokedViaReflection?.Resolve ();
if (typeDefinition != null)
markMethod (instructions, nameOfThingUsedViaReflection, typeDefinition, bindingFlags);
}
_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);
}
}
void MarkMethodsUsedViaReflection (Collection<Instruction> instructions, string name, TypeDefinition declaringType, BindingFlags bindingFlags)
{
if (name == null)
return;
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);
}
}
void MarkPropertyUsedViaReflection (Collection<Instruction> instructions, string name, TypeDefinition declaringType, BindingFlags unused)
{
if (name == null)
return;
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);
}
}
}
void MarkFieldUsedViaReflection (Collection<Instruction> instructions, string name, TypeDefinition declaringType, BindingFlags unused)
{
if (name == null)
return;
foreach (var field in declaringType.Fields) {
if (field.Name == name)
MarkField (field);
}
}
void MarkEventUsedViaReflection (Collection<Instruction> instructions, string name, TypeDefinition declaringType, BindingFlags unused)
{
if (name == null)
return;
foreach (var eventInfo in declaringType.Events) {
if (eventInfo.Name == name)
MarkEvent (eventInfo);
}
}
static TOperand OperandOfNearestInstructionBefore<TOperand> (int startingInstructionIndex, OpCode opCode, IList<Instruction> instructions)
{
for (var i = startingInstructionIndex; i >= 0; i--) {
if (instructions [i].OpCode == opCode)
return (TOperand) instructions [i].Operand;
}
return default (TOperand);
}
static List<TypeReference> OperandsOfInstructionsBefore (int startingInstructionIndex, OpCode opCode, IList<Instruction> instructions)
{
var operands = new List<TypeReference> ();
for (var i = startingInstructionIndex; i >= 0; i--) {
if (instructions [i].OpCode == opCode) {
var type = instructions [i].Operand as TypeReference;
if (type != null)
operands.Add (type);
}
}
return operands;
}
}
// Make our own copy of the BindingFlags enum, so that we don't depend on System.Reflection.
[Flags]
enum BindingFlags
{
Default = 0,
IgnoreCase = 1,
DeclaredOnly = 2,
Instance = 4,
Static = 8,
Public = 16,
NonPublic = 32,
FlattenHierarchy = 64,
InvokeMethod = 256,
CreateInstance = 512,
GetField = 1024,
SetField = 2048,
GetProperty = 4096,
SetProperty = 8192,
PutDispProperty = 16384,
PutRefDispProperty = 32768,
ExactBinding = 65536,
SuppressChangeType = 131072,
OptionalParamBinding = 262144,
IgnoreReturn = 16777216
}
static class BindingFlagsExtensions
{
public static bool IsSet(this BindingFlags flags, BindingFlags check)
{
return (flags & check) == check;
}
}
}

View File

@@ -93,7 +93,12 @@ namespace Mono.Linker.Steps {
(module.Attributes & (ModuleAttributes) 0x04) != 0;
}
void WriteAssembly (AssemblyDefinition assembly, string directory)
protected void WriteAssembly (AssemblyDefinition assembly, string directory)
{
WriteAssembly (assembly, directory, SaveSymbols (assembly));
}
protected virtual void WriteAssembly (AssemblyDefinition assembly, string directory, WriterParameters writerParameters)
{
foreach (var module in assembly.Modules) {
// Write back pure IL even for R2R assemblies
@@ -104,7 +109,7 @@ namespace Mono.Linker.Steps {
}
}
assembly.Write (GetAssemblyFileName (assembly, directory), SaveSymbols (assembly));
assembly.Write (GetAssemblyFileName (assembly, directory), writerParameters);
}
void OutputAssembly (AssemblyDefinition assembly)
@@ -126,17 +131,11 @@ namespace Mono.Linker.Steps {
case AssemblyAction.Copy:
Context.Tracer.AddDependency (assembly);
CloseSymbols (assembly);
CopyAssembly (GetOriginalAssemblyFileInfo (assembly), directory, Context.LinkSymbols);
CopyAssembly (assembly, directory);
break;
case AssemblyAction.Delete:
CloseSymbols (assembly);
var target = GetAssemblyFileName (assembly, directory);
if (File.Exists (target)) {
File.Delete (target);
File.Delete (target + ".mdb");
File.Delete (Path.ChangeExtension (target, "pdb"));
File.Delete (GetConfigFile (target));
}
DeleteAssembly (assembly, directory);
break;
default:
CloseSymbols (assembly);
@@ -144,6 +143,17 @@ namespace Mono.Linker.Steps {
}
}
protected virtual void DeleteAssembly(AssemblyDefinition assembly, string directory)
{
var target = GetAssemblyFileName (assembly, directory);
if (File.Exists (target)) {
File.Delete (target);
File.Delete (target + ".mdb");
File.Delete (Path.ChangeExtension (target, "pdb"));
File.Delete (GetConfigFile (target));
}
}
void CloseSymbols (AssemblyDefinition assembly)
{
Annotations.CloseSymbolReader (assembly);
@@ -158,6 +168,12 @@ namespace Mono.Linker.Steps {
if (!assembly.MainModule.HasSymbols)
return parameters;
#if NATIVE_READER_SUPPORT
// NativePdb's can't be written on non-windows platforms
if (Environment.OSVersion.Platform != PlatformID.Win32NT && assembly.MainModule.SymbolReader is Mono.Cecil.Pdb.NativePdbReader)
return parameters;
#endif
if (Context.SymbolWriterProvider != null)
parameters.SymbolWriterProvider = Context.SymbolWriterProvider;
else
@@ -165,7 +181,7 @@ namespace Mono.Linker.Steps {
return parameters;
}
static void CopyConfigFileIfNeeded (AssemblyDefinition assembly, string directory)
void CopyConfigFileIfNeeded (AssemblyDefinition assembly, string directory)
{
string config = GetConfigFile (GetOriginalAssemblyFileInfo (assembly).FullName);
if (!File.Exists (config))
@@ -189,8 +205,17 @@ namespace Mono.Linker.Steps {
return new FileInfo (assembly.MainModule.FileName);
}
static void CopyAssembly (FileInfo fi, string directory, bool symbols)
protected virtual void CopyAssembly (AssemblyDefinition assembly, string directory)
{
// Special case. When an assembly has embedded pdbs, link symbols is not enabled, and the assembly's action is copy,
// we want to match the behavior of assemblies with the other symbol types and end up with an assembly that does not have symbols.
// In order to do that, we can't simply copy files. We need to write the assembly without symbols
if (assembly.MainModule.HasSymbols && !Context.LinkSymbols && assembly.MainModule.SymbolReader is EmbeddedPortablePdbReader) {
WriteAssembly (assembly, directory, new WriterParameters ());
return;
}
FileInfo fi = GetOriginalAssemblyFileInfo (assembly);
string target = Path.GetFullPath (Path.Combine (directory, fi.Name));
string source = fi.FullName;
if (source == target)
@@ -198,7 +223,7 @@ namespace Mono.Linker.Steps {
File.Copy (source, target, true);
if (!symbols)
if (!Context.LinkSymbols)
return;
var mdb = source + ".mdb";
@@ -210,7 +235,7 @@ namespace Mono.Linker.Steps {
File.Copy (pdb, Path.ChangeExtension (target, "pdb"), true);
}
static string GetAssemblyFileName (AssemblyDefinition assembly, string directory)
protected virtual string GetAssemblyFileName (AssemblyDefinition assembly, string directory)
{
string file = GetOriginalAssemblyFileInfo (assembly).Name;
return Path.Combine (directory, file);

View File

@@ -0,0 +1,68 @@
using System;
using System.Linq;
using Mono.Cecil;
namespace Mono.Linker.Steps {
public class RemoveSecurityStep : BaseStep {
protected override void ProcessAssembly (AssemblyDefinition assembly)
{
if (Annotations.GetAction (assembly) == AssemblyAction.Link) {
ClearSecurityDeclarations (assembly);
RemoveCustomAttributesThatAreForSecurity (assembly);
foreach (var type in assembly.MainModule.Types)
ProcessType (type);
}
}
static void ProcessType (TypeDefinition type)
{
RemoveCustomAttributesThatAreForSecurity (type);
ClearSecurityDeclarations (type);
type.HasSecurity = false;
foreach (var field in type.Fields)
RemoveCustomAttributesThatAreForSecurity (field);
foreach (var method in type.Methods) {
ClearSecurityDeclarations (method);
RemoveCustomAttributesThatAreForSecurity (method);
method.HasSecurity = false;
}
foreach (var nested in type.NestedTypes)
ProcessType (nested);
}
static void ClearSecurityDeclarations (ISecurityDeclarationProvider provider)
{
provider.SecurityDeclarations.Clear ();
}
/// <summary>
/// We have to remove some security attributes, otherwise pe verify will complain that a type has HasSecurity = false
/// </summary>
/// <param name="provider"></param>
static void RemoveCustomAttributesThatAreForSecurity (ICustomAttributeProvider provider)
{
if (!provider.HasCustomAttributes)
return;
var attrsToRemove = provider.CustomAttributes.Where (IsCustomAttributeForSecurity).ToArray ();
foreach (var remove in attrsToRemove)
provider.CustomAttributes.Remove (remove);
}
static bool IsCustomAttributeForSecurity (CustomAttribute attr)
{
switch (attr.AttributeType.FullName) {
case "System.Security.SecurityCriticalAttribute":
case "System.Security.SecuritySafeCriticalAttribute":
case "System.Security.SuppressUnmanagedCodeSecurityAttribute":
return true;
}
return false;
}
}
}

View File

@@ -117,12 +117,7 @@ namespace Mono.Linker.Steps
if (!isForwarder)
continue;
TypeDefinition resolvedExportedType = null;
try {
resolvedExportedType = exported.Resolve ();
} catch (AssemblyResolutionException) {
continue;
}
TypeDefinition resolvedExportedType = exported.Resolve ();
if (resolvedExportedType == null) {
//

View File

@@ -108,7 +108,7 @@ namespace Mono.Linker.Steps {
}
}
protected void ProcessAssembly (AssemblyDefinition assembly, XPathNodeIterator iterator)
protected virtual void ProcessAssembly (AssemblyDefinition assembly, XPathNodeIterator iterator)
{
Tracer.Push (assembly);
if (GetTypePreserve (iterator.Current) == TypePreserve.All) {
@@ -213,12 +213,7 @@ namespace Mono.Linker.Steps {
{
if (regex.Match (exportedType.FullName).Success) {
MarkingHelpers.MarkExportedType (exportedType, module);
TypeDefinition type = null;
try {
type = exportedType.Resolve ();
}
catch (AssemblyResolutionException) {
}
TypeDefinition type = exportedType.Resolve ();
if (type != null) {
ProcessType (type, nav);
}
@@ -241,7 +236,7 @@ namespace Mono.Linker.Steps {
}
}
void ProcessType (TypeDefinition type, XPathNavigator nav)
protected virtual void ProcessType (TypeDefinition type, XPathNavigator nav)
{
TypePreserve preserve = GetTypePreserve (nav);
@@ -323,15 +318,19 @@ namespace Mono.Linker.Steps {
void ProcessFields (TypeDefinition type, XPathNodeIterator iterator)
{
while (iterator.MoveNext ()) {
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
ProcessFieldSignature (type, value);
while (iterator.MoveNext ())
ProcessField (type, iterator);
}
value = GetAttribute (iterator.Current, "name");
if (!String.IsNullOrEmpty (value))
ProcessFieldName (type, value);
}
protected virtual void ProcessField (TypeDefinition type, XPathNodeIterator iterator)
{
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
ProcessFieldSignature (type, value);
value = GetAttribute (iterator.Current, "name");
if (!String.IsNullOrEmpty (value))
ProcessFieldName (type, value);
}
void ProcessFieldSignature (TypeDefinition type, string signature)
@@ -375,13 +374,13 @@ namespace Mono.Linker.Steps {
return field.FieldType.FullName + " " + field.Name;
}
protected virtual void ProcessMethods (TypeDefinition type, XPathNodeIterator iterator)
void ProcessMethods (TypeDefinition type, XPathNodeIterator iterator)
{
while (iterator.MoveNext ())
ProcessMethod (type, iterator);
}
protected void ProcessMethod(TypeDefinition type, XPathNodeIterator iterator)
protected virtual void ProcessMethod (TypeDefinition type, XPathNodeIterator iterator)
{
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
@@ -462,15 +461,19 @@ namespace Mono.Linker.Steps {
void ProcessEvents (TypeDefinition type, XPathNodeIterator iterator)
{
while (iterator.MoveNext ()) {
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
ProcessEventSignature (type, value);
while (iterator.MoveNext ())
ProcessEvent (type, iterator);
}
value = GetAttribute (iterator.Current, "name");
if (!String.IsNullOrEmpty (value))
ProcessEventName (type, value);
}
protected virtual void ProcessEvent (TypeDefinition type, XPathNodeIterator iterator)
{
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
ProcessEventSignature (type, value);
value = GetAttribute (iterator.Current, "name");
if (!String.IsNullOrEmpty (value))
ProcessEventName (type, value);
}
void ProcessEventSignature (TypeDefinition type, string signature)
@@ -520,15 +523,19 @@ namespace Mono.Linker.Steps {
void ProcessProperties (TypeDefinition type, XPathNodeIterator iterator)
{
while (iterator.MoveNext ()) {
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
ProcessPropertySignature (type, value, GetAccessors (iterator.Current));
while (iterator.MoveNext ())
ProcessProperty (type, iterator);
}
value = GetAttribute (iterator.Current, "name");
if (!String.IsNullOrEmpty (value))
ProcessPropertyName (type, value, _accessorsAll);
}
protected virtual void ProcessProperty (TypeDefinition type, XPathNodeIterator iterator)
{
string value = GetSignature (iterator.Current);
if (!String.IsNullOrEmpty (value))
ProcessPropertySignature (type, value, GetAccessors (iterator.Current));
value = GetAttribute (iterator.Current, "name");
if (!String.IsNullOrEmpty (value))
ProcessPropertyName (type, value, _accessorsAll);
}
void ProcessPropertySignature (TypeDefinition type, string signature, string[] accessors)

View File

@@ -124,6 +124,9 @@ namespace Mono.Linker.Steps {
SweepResources (assembly);
SweepCustomAttributes (assembly);
foreach (var module in assembly.Modules)
SweepCustomAttributes (module);
}
bool IsMarkedAssembly (AssemblyDefinition assembly)
@@ -169,13 +172,9 @@ namespace Mono.Linker.Steps {
var references = assembly.MainModule.AssemblyReferences;
for (int i = 0; i < references.Count; i++) {
var reference = references [i];
AssemblyDefinition r = null;
try {
r = Context.Resolver.Resolve (reference);
}
catch (AssemblyResolutionException) {
AssemblyDefinition r = Context.Resolver.Resolve (reference);
if (r == null)
continue;
}
if (!AreSameReference (r.Name, target.Name))
continue;
@@ -282,6 +281,9 @@ namespace Mono.Linker.Steps {
if (type.HasProperties)
SweepCustomAttributeCollection (type.Properties);
if (type.HasEvents)
SweepCustomAttributeCollection (type.Events);
}
protected void SweepNestedTypes (TypeDefinition type)
@@ -308,15 +310,70 @@ namespace Mono.Linker.Steps {
}
}
protected void SweepCustomAttributes (ICustomAttributeProvider provider)
protected void SweepCustomAttributes (TypeDefinition type)
{
var removed = SweepCustomAttributes (type as ICustomAttributeProvider);
if (ShouldSetHasSecurityToFalse (type, type, type.HasSecurity, removed))
type.HasSecurity = false;
}
protected void SweepCustomAttributes (MethodDefinition method)
{
var removed = SweepCustomAttributes (method as ICustomAttributeProvider);
if (ShouldSetHasSecurityToFalse (method, method, method.HasSecurity, removed))
method.HasSecurity = false;
}
bool ShouldSetHasSecurityToFalse (ISecurityDeclarationProvider providerAsSecurity, ICustomAttributeProvider provider, bool existingHasSecurity, IList<CustomAttribute> removedAttributes)
{
if (existingHasSecurity && removedAttributes.Count > 0 && !providerAsSecurity.HasSecurityDeclarations) {
// If the method or type had security before and all attributes were removed, or no remaining attributes are security attributes,
// then we need to set HasSecurity to false
if (provider.CustomAttributes.Count == 0 || provider.CustomAttributes.All (attr => !IsSecurityAttributeType (attr.AttributeType.Resolve ())))
return true;
}
return false;
}
static bool IsSecurityAttributeType (TypeDefinition definition)
{
if (definition == null)
return false;
if (definition.Namespace == "System.Security") {
switch (definition.FullName) {
// This seems to be one attribute in the System.Security namespace that doesn't count
// as an attribute that requires HasSecurity to be true
case "System.Security.SecurityCriticalAttribute":
return false;
}
return true;
}
if (definition.BaseType == null)
return false;
return IsSecurityAttributeType (definition.BaseType.Resolve ());
}
protected IList<CustomAttribute> SweepCustomAttributes (ICustomAttributeProvider provider)
{
var removed = new List<CustomAttribute>();
for (int i = provider.CustomAttributes.Count - 1; i >= 0; i--) {
var attribute = provider.CustomAttributes [i];
if (!Annotations.IsMarked (attribute)) {
CustomAttributeUsageRemoved (provider, attribute);
removed.Add (provider.CustomAttributes [i]);
provider.CustomAttributes.RemoveAt (i);
}
}
return removed;
}
protected void SweepCustomAttributeCollection<T> (Collection<T> providers) where T : ICustomAttributeProvider
@@ -330,6 +387,14 @@ namespace Mono.Linker.Steps {
SweepCollection (methods);
if (sweepSymbols)
SweepDebugInfo (methods);
foreach (var method in methods) {
if (!method.HasParameters)
continue;
foreach (var parameter in method.Parameters)
SweepCustomAttributes (parameter);
}
}
void SweepDebugInfo (Collection<MethodDefinition> methods)
@@ -377,6 +442,17 @@ namespace Mono.Linker.Steps {
}
}
protected void SweepCollection (IList<MethodDefinition> list)
{
for (int i = 0; i < list.Count; i++)
if (ShouldRemove (list [i])) {
ElementRemoved (list [i]);
list.RemoveAt (i--);
} else {
SweepCustomAttributes (list [i]);
}
}
protected void SweepCollection<T> (IList<T> list) where T : ICustomAttributeProvider
{
for (int i = 0; i < list.Count; i++)

View File

@@ -40,6 +40,9 @@ namespace Mono.Linker {
#endif
readonly Dictionary<string, AssemblyDefinition> _assemblies;
HashSet<string> _unresolvedAssemblies;
bool _ignoreUnresolved;
LinkContext _context;
public IDictionary<string, AssemblyDefinition> AssemblyCache {
get { return _assemblies; }
@@ -55,12 +58,32 @@ namespace Mono.Linker {
_assemblies = assembly_cache;
}
public bool IgnoreUnresolved {
get { return _ignoreUnresolved; }
set { _ignoreUnresolved = value; }
}
public LinkContext Context {
get { return _context; }
set { _context = value; }
}
public override AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters)
{
AssemblyDefinition asm;
if (!_assemblies.TryGetValue (name.Name, out asm)) {
asm = base.Resolve (name, parameters);
_assemblies [asm.Name.Name] = asm;
AssemblyDefinition asm = null;
if (!_assemblies.TryGetValue (name.Name, out asm) && (_unresolvedAssemblies == null || !_unresolvedAssemblies.Contains (name.Name))) {
try {
asm = base.Resolve (name, parameters);
_assemblies [name.Name] = asm;
} catch (AssemblyResolutionException) {
if (!_ignoreUnresolved)
throw;
_context.LogMessage ($"warning: unresolved assembly {name.Name}");
if (_unresolvedAssemblies == null)
_unresolvedAssemblies = new HashSet<string> ();
_unresolvedAssemblies.Add (name.Name);
}
}
return asm;
@@ -80,6 +103,8 @@ namespace Mono.Linker {
}
_assemblies.Clear ();
if (_unresolvedAssemblies != null)
_unresolvedAssemblies.Clear ();
}
}
}

View File

@@ -44,6 +44,11 @@ namespace Mono.Linker {
#endif
public static int Main (string [] args)
{
return Execute (args);
}
public static int Execute (string[] args, ILogger customLogger = null)
{
if (args.Length == 0)
Usage ("No parameters specified");
@@ -51,7 +56,7 @@ namespace Mono.Linker {
try {
Driver driver = new Driver (args);
driver.Run ();
driver.Run (customLogger);
} catch (Exception e) {
Console.WriteLine ("Fatal error in {0}", _linker);
@@ -75,10 +80,13 @@ namespace Mono.Linker {
return _queue.Count > 0;
}
void Run ()
public void Run (ILogger customLogger = null)
{
Pipeline p = GetStandardPipeline ();
using (LinkContext context = GetDefaultContext (p)) {
if (customLogger != null)
context.Logger = customLogger;
I18nAssemblies assemblies = I18nAssemblies.All;
var custom_steps = new List<string> ();
bool dumpDependencies = false;
@@ -98,12 +106,18 @@ namespace Mono.Linker {
Usage ("Option is too short");
if (token == "--skip-unresolved") {
context.IgnoreUnresolved = bool.Parse (GetParam ());
bool ignoreUnresolved = bool.Parse (GetParam ());
context.IgnoreUnresolved = ignoreUnresolved;
context.Resolver.IgnoreUnresolved = ignoreUnresolved;
continue;
}
if (token == "--dependencies-file")
{
if (token == "--verbose") {
context.LogMessages = true;
continue;
}
if (token == "--dependencies-file") {
context.Tracer.DependenciesFileName = GetParam ();
continue;
}
@@ -118,6 +132,17 @@ namespace Mono.Linker {
continue;
}
if (token == "--used-attrs-only") {
context.KeepUsedAttributeTypesOnly = bool.Parse (GetParam ());
continue;
}
if (token == "--strip-security") {
if (bool.Parse (GetParam ()))
p.AddStepBefore (typeof (MarkStep), new RemoveSecurityStep ());
continue;
}
switch (token [2]) {
case 'v':
Version ();
@@ -330,10 +355,13 @@ namespace Mono.Linker {
Console.WriteLine (" --about About the {0}", _linker);
Console.WriteLine (" --version Print the version number of the {0}", _linker);
Console.WriteLine (" --skip-unresolved Ignore unresolved types and methods (true or false)");
Console.WriteLine (" --skip-unresolved Ignore unresolved types, methods, and assemblies (true or false)");
Console.WriteLine (" --verbose Log messages indicating progress and warnings");
Console.WriteLine (" --dependencies-file Specify the dependencies file path, if unset the default path is used: <output directory>/linker-dependencies.xml.gz");
Console.WriteLine (" --dump-dependencies Dump dependencies for the linker analyzer tool");
Console.WriteLine (" --reduced-tracing Reduces dependency output related to assemblies that will not be modified");
Console.WriteLine (" --used-attrs-only Attributes on types, methods, etc will be removed if the attribute type is not used");
Console.WriteLine (" --strip-security In linked assemblies, attributes on assemblies, types, and methods related to security will be removed");
Console.WriteLine (" -out Specify the output directory, default to `output'");
Console.WriteLine (" -c Action on the core assemblies, skip, copy, copyused, addbypassngen, addbypassngenused or link, default to skip");
Console.WriteLine (" -u Action on the user assemblies, skip, copy, copyused, addbypassngen, addbypassngenused or link, default to link");

View File

@@ -109,6 +109,8 @@ namespace Mono.Linker {
public bool EnableReducedTracing { get; set; }
public bool KeepUsedAttributeTypesOnly { get; set; }
public System.Collections.IDictionary Actions {
get { return _actions; }
}
@@ -131,7 +133,7 @@ namespace Mono.Linker {
set { _symbolWriterProvider = value; }
}
public bool LogInternalExceptions { get; set; } = false;
public bool LogMessages { get; set; } = false;
public ILogger Logger { get; set; } = new ConsoleLogger ();
@@ -156,9 +158,12 @@ namespace Mono.Linker {
{
_pipeline = pipeline;
_resolver = resolver;
_resolver.Context = this;
_actions = new Dictionary<string, AssemblyAction> ();
_parameters = new Dictionary<string, string> ();
_readerParameters = readerParameters;
SymbolReaderProvider = new DefaultSymbolReaderProvider (false);
if (factory == null)
throw new ArgumentNullException (nameof (factory));
@@ -204,7 +209,7 @@ namespace Mono.Linker {
try {
AssemblyDefinition assembly = _resolver.Resolve (reference, _readerParameters);
if (SeenFirstTime (assembly)) {
if (assembly != null && SeenFirstTime (assembly)) {
SafeReadSymbols (assembly);
SetAction (assembly);
}
@@ -223,34 +228,32 @@ namespace Mono.Linker {
public virtual void SafeReadSymbols (AssemblyDefinition assembly)
{
if (!_linkSymbols)
return;
if (assembly.MainModule.HasSymbols)
return;
try {
if (_symbolReaderProvider != null) {
var symbolReader = _symbolReaderProvider.GetSymbolReader (
assembly.MainModule,
assembly.MainModule.FileName);
if (_symbolReaderProvider == null)
throw new ArgumentNullException (nameof (_symbolReaderProvider));
_annotations.AddSymbolReader (assembly, symbolReader);
assembly.MainModule.ReadSymbols (symbolReader);
} else
assembly.MainModule.ReadSymbols ();
} catch {}
try {
var symbolReader = _symbolReaderProvider.GetSymbolReader (
assembly.MainModule,
assembly.MainModule.FileName);
if (symbolReader == null)
return;
_annotations.AddSymbolReader (assembly, symbolReader);
assembly.MainModule.ReadSymbols (symbolReader);
} catch { }
}
public virtual ICollection<AssemblyDefinition> ResolveReferences (AssemblyDefinition assembly)
{
List<AssemblyDefinition> references = new List<AssemblyDefinition> ();
foreach (AssemblyNameReference reference in assembly.MainModule.AssemblyReferences) {
try {
references.Add (Resolve (reference));
}
catch (AssemblyResolutionException) {
}
AssemblyDefinition definition = Resolve (reference);
if (definition != null)
references.Add (definition);
}
return references;
}
@@ -341,7 +344,7 @@ namespace Mono.Linker {
public void LogMessage (MessageImportance importance, string message, params object [] values)
{
if (LogInternalExceptions && Logger != null)
if (LogMessages && Logger != null)
Logger.LogMessage (importance, message, values);
}
}

View File

@@ -33,7 +33,7 @@
<DebugType>full</DebugType>
<Optimize>False</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>DEBUG;TRACE;NATIVE_READER_SUPPORT</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
@@ -41,7 +41,7 @@
<DebugType>pdbonly</DebugType>
<Optimize>True</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>TRACE;NATIVE_READER_SUPPORT</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
@@ -64,6 +64,7 @@
<Compile Include="Linker.Steps\CleanStep.cs" />
<Compile Include="Linker.Steps\RegenerateGuidStep.cs" />
<Compile Include="Linker.Steps\LoadI18nAssemblies.cs" />
<Compile Include="Linker.Steps\RemoveSecurityStep.cs" />
<Compile Include="Linker\IXApiVisitor.cs" />
<Compile Include="Linker\I18nAssemblies.cs" />
<Compile Include="Linker.Steps\IStep.cs" />
@@ -110,6 +111,12 @@
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
</ProjectReference>
<ProjectReference Include="..\cecil\symbols\mdb\Mono.Cecil.Mdb.csproj">
<SetConfiguration Condition=" '$(Configuration)' == 'illink_Debug' ">Configuration=netstandard_Debug</SetConfiguration>
<SetConfiguration Condition=" '$(Configuration)' == 'illink_Release' ">Configuration=netstandard_Release</SetConfiguration>
<Project>{8559dd7f-a16f-46d0-a05a-9139faeba8fd}</Project>
<Name>Mono.Cecil.Mdb</Name>
</ProjectReference>
<ProjectReference Include="..\cecil\symbols\pdb\Mono.Cecil.Pdb.csproj">
<SetConfiguration Condition=" '$(Configuration)' == 'illink_Debug' ">Configuration=netstandard_Debug</SetConfiguration>
<SetConfiguration Condition=" '$(Configuration)' == 'illink_Release' ">Configuration=netstandard_Release</SetConfiguration>

View File

@@ -1,164 +1,102 @@
monolinker
====
monolinker is the Mono CIL Linker.
# IL Linker
The linker is a tool one can use to only ship the minimal possible set of
functions that a set of programs might require to run as opposed to the full
libraries.
* How does the linker work?
## How does the linker work?
The linker analyses the intermediate code (CIL) produced by every compiler
targeting the Mono platform like mcs, gmcs, vbnc, booc or others. It will walk
targeting the .NET platform like mcs, csc, vbnc, booc or others. It will walk
through all the code that it is given to it, and basically, perform a mark and
sweep operations on all the code that it is referenced, to only keep what is
necessary for the source program to run.
* Usage
## Usage
1) Linking from a source assembly
### Linking from a source assembly
The command:
monolinker -a Program.exe
`illinker -a Program.exe`
will use the assembly Program.exe as a source. That means that the linker will
walk through all the methods of Program.exe to generate only what is necessary
for this assembly to run.
2) Linking from an xml descriptor
### Linking from an [XML descriptor](#syntax-of-xml-descriptor)
The command:
monolinker -x desc.xml
`illinker -x desc.xml`
will use the XML descriptor as a source. That means that the linker will
use this file to decide what to link in a set of assemblies. The format of the
descriptors is described further on in this document.
3) Linking from an api info file
### Linking from an API info file
The command:
monolinker -i assembly.info
`illinker -i assembly.info`
will use a file produced by mono-api-info as a source. The linker will use
will use a file produced by `mono-api-info` as a source. The linker will use
this file to link only what is necessary to match the public API defined in
the info file.
4) Actions on the assemblies
### Actions on the assemblies
You can specify what the linker should do exactly per assembly.
The linker can do 3 things:
- skip them, and do nothing with them,
- copy them to the output directory,
- link them, to reduce their size.
- skip them, and do nothing with them,
- copy them to the output directory,
- link them, to reduce their size.
You can specify an action per assembly like this:
monolinker -p link Foo
`illinker -p link Foo`
or
monolinker -p skip System.Windows.Forms
`illinker -p skip System.Windows.Forms`
Or you can specify what to do for the core assemblies.
Core assemblies are the assemblies that belongs to the base class library,
Core assemblies are the assemblies that belong to the base class library,
like mscorlib.dll, System.dll or System.Windows.Forms.dll.
You can specify what action to do on the core assemblies with the option:
-c skip|copy|link
`-c skip|copy|link`
5) The output directory
### The output directory
By default, the linker will create an `output' directory in the current
By default, the linker will create an `output` directory in the current
directory where it will emit the linked files, to avoid erasing source
assemblies. You can specify the output directory with the option:
-o output_directory
`-o output_directory`
If you specify the directory `.', please ensure that you won't write over
important assemblies of yours.
* Syntax of a xml descriptor
### Specifying directories where the linker should look for assemblies
Here is an example that shows all the possibilities of this format:
---
<linker>
<assembly fullname="Library">
<type fullname="Foo" />
<type fullname="Bar" preserve="nothing" required="false" />
<type fullname="Baz" preserve="fields" required="false" />
<type fullname="Gazonk">
<method signature="System.Void .ctor(System.String)" />
<field signature="System.String _blah" />
</type>
</assembly>
</linker>
---
In this example, the linker will link the types Foo, Bar, Baz and Gazonk.
The fullname attribute specifies the fullname of the type in the format
specified by ECMA-335. This is in Mono and certain cases not the same
as the one reported by Type.FullName (nested classes e.g.).
The preserve attribute ensures that all the fields of the type Baz will be
always be linked, not matter if they are used or not, but that neither the
fields or the methods of Bar will be linked if they are not used. Not
specifying a preserve attribute implies that we are preserving everything in
the specified type.
The required attribute specifies that if the type is not marked, during the
mark operation, it will not be linked.
The type Gazonk will be linked, as well as its constructor taking a string as a
parameter, and it's _blah field.
You can have multiple assembly nodes.
6) The i18n Assemblies
Mono have a few assemblies which contains everything region specific:
I18N.CJK.dll
I18N.MidEast.dll
I18N.Other.dll
I18N.Rare.dll
I18N.West.dll
By default, they will all be copied to the output directory. But you can
specify which one you want using the command:
monolinker -l choice
Where choice can either be: none, all, cjk, mideast, other, rare or west. You can
combine the values with a comma.
By default, the linker will first look for assemblies in the directories `.`
and `bin`. You can specify
Example:
monolinker -a assembly -l mideast,cjk
`illinker -d ../../libs -a program.exe`
7) Specifying directories where the linker should look for assemblies
By default, the linker will first look for assemblies in the directories `.'
and `bin'. You can specify
Example:
monolinker -d ../../libs -a program.exe
8) Adding custom steps to the linker.
### Adding custom steps to the linker.
You can write custom steps for the linker and tell the linker to use them.
Let's take a simple example:
```csharp
using System;
using Mono.Linker;
@@ -176,58 +114,120 @@ namespace Foo {
}
}
}
```
That is compiled against the linker to a Foo.dll assembly.
That is compiled against the linker to `Foo.dll` assembly.
You can ask the linker to add it at the end of the pipeline:
monolinker -s Foo.FooStep,Foo -a program.exe
`illinker -s Foo.FooStep,Foo -a program.exe`
Or you can ask the linker to add it after a specific step:
monolinker -s MarkStep:Foo.FooStep,Foo -a program.exe
`illinker -s MarkStep:Foo.FooStep,Foo -a program.exe`
Or before a specific step:
monolinker -s Foo.FooStep,Foo:MarkStep
`illinker -s Foo.FooStep,Foo:MarkStep`
* Inside the linker
## Mono specific options
### The i18n Assemblies
Mono has a few assemblies which contains everything region specific:
I18N.CJK.dll
I18N.MidEast.dll
I18N.Other.dll
I18N.Rare.dll
I18N.West.dll
By default, they will all be copied to the output directory. But you can
specify which one you want using the command:
`illinker -l choice`
Where choice can either be: none, all, cjk, mideast, other, rare or west. You can
combine the values with a comma.
Example:
`illinker -a assembly -l mideast,cjk`
## Syntax of xml descriptor
Here is an example that shows all the possibilities of this format:
```xml
<linker>
<assembly fullname="Library">
<type fullname="Foo" />
<type fullname="Bar" preserve="nothing" required="false" />
<type fullname="Baz" preserve="fields" required="false" />
<type fullname="Gazonk">
<method signature="System.Void .ctor(System.String)" />
<field signature="System.String _blah" />
</type>
</assembly>
</linker>
```
In this example, the linker will link the types Foo, Bar, Baz and Gazonk.
The fullname attribute specifies the fullname of the type in the format
specified by ECMA-335. This is in certain cases not the same
as the one reported by Type.FullName (nested classes e.g.).
The preserve attribute ensures that all the fields of the type Baz will be
always be linked, not matter if they are used or not, but that neither the
fields or the methods of Bar will be linked if they are not used. Not
specifying a preserve attribute implies that we are preserving everything in
the specified type.
The required attribute specifies that if the type is not marked, during the
mark operation, it will not be linked.
The type Gazonk will be linked, as well as its constructor taking a string as a
parameter, and it's _blah field.
You can have multiple assembly nodes.
# Inside the linker
The linker is a quite small piece of code, and it pretty simple to address.
Its only dependency is Mono.Cecil, that is used to read, modify and write back
Its only dependency is `Mono.Cecil`, that is used to read, modify and write back
the assemblies.
Everything is located in the namespace Mono.Linker, or in sub namespaces.
Everything is located in the namespace Linker, or in sub namespaces.
Being a command line utility, its entry point function is in the class Driver.
This class is in charge of analyzing the command line, and to instantiate two
important objects, a LinkContext, and a Pipeline.
The LinkContext contains all the informations that will be used during the
The LinkContext contains all the information that will be used during the
linking process, such as the assemblies involved, the output directory and
probably other useful stuff.
The Pipeline is simply a queue of actions (steps), to be applied to the current
context. The whole process of linking is split into those differents steps
that are all located in the Mono.Linker.Steps namespace.
context. The whole process of linking is split into those different steps
that are all located in the Linker.Steps namespace.
Here are the current steps that are implemented, in the order they are used:
1) ResolveFromAssembly or ResolveFromXml
## ResolveFromAssembly or ResolveFromXml
Those steps are used to initialize the context, and pre-mark the root code
Those steps are used to initialize the context and pre-mark the root code
that will be used as a source for the linker.
Resolving from an assembly or resolving from a xml descriptor is a decision
Resolving from an assembly or resolving from an xml descriptor is a decision
taken in the command line parsing.
2) LoadReferences
## LoadReferences
This step will load all the references of all the assemblies involved in the
current context.
3) Blacklist
## Blacklist
This step is used if and only if you have specified that the core should be
linked. It will load a bunch of resources from the assemblies, that are
@@ -237,7 +237,7 @@ that are used from inside the runtime are properly linked and not removed.
It is doing so by inserting a ResolveFromXml step per blacklist in the
pipeline.
4) Mark
## Mark
This is the most complex step. The linker will get from the context the list
of types, fields and methods that have been pre-marked in the resolve steps,
@@ -252,53 +252,47 @@ mscorlib assembly, and add it to the queue. When this WriteLine method will be
dequeued, and processed, the linker will go through everything that is used in
it, and add it to the queue, if they have not been processed already.
To know if something have been marked to be linked, or processed, the linker
To know if something has been marked to be linked, or processed, the linker
is using a functionality of Cecil called annotations. Almost everything in
Cecil can be annotated. Concretely, it means that almost everything own an
Cecil can be annotated. Concretely, it means that almost everything owns an
Hashtable in which you can add what you want, using the keys and the values you
want.
So the linker will annotate assemblies, types, methods and fields to know
what should be linked or not, and what have been processed, and how it should
what should be linked or not, and what has been processed, and how it should
process them.
This is really useful as we don't have to recreate a full hierarchy of classes
to encapsulate the different Cecil types to add the few informations we want.
to encapsulate the different Cecil types to add the few pieces of information we want.
5) Sweep
## Sweep
This simple step will walk through all the elements of an assembly, and based
on their annotations, remove them or keep them.
6) Clean
## Clean
This step will clean parts of the assemblies, like properties. If a proprety
This step will clean parts of the assemblies, like properties. If a property
used to have a getter and a setter, and that after the mark & sweep steps,
only the getter is linked, it will update the property to reflect that.
There is a few things to keep clean like properties has we've seen, events,
There are a few things to keep clean like properties we've seen, events,
nested classes, and probably a few others.
7) Output
## Output
For each assembly in the context, this step will act on the action associated
to the assembly. If the assembly is marked as skip, it won't do anything,
with the assembly. If the assembly is marked as skip, it won't do anything,
if it's marked as copy, it will copy the assembly to the output directory,
and if it's link, it will save the modified assembly to the output directory.
* Reporting a bug
# Reporting a bug
If you face a bug in the linker, please report it to:
If you face a bug in the linker, please report it using GitHub issues
http://bugzilla.ximian.com
Product: Mono tools, Component: linker.
* Mailing lists
# Mailing lists
You can ask questions about the linker of the cecil Google Group:
http://groups.google.com/group/mono-cecil
--
Jb Evain <jbevain@novell.com>

View File

@@ -1,122 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
namespace Mono.Linker.Tests.Extensions {
public static class CecilExtensions {
public static IEnumerable<TypeDefinition> AllDefinedTypes (this AssemblyDefinition assemblyDefinition)
{
return assemblyDefinition.Modules.SelectMany (m => m.AllDefinedTypes ());
}
public static IEnumerable<TypeDefinition> AllDefinedTypes (this ModuleDefinition moduleDefinition)
{
foreach (var typeDefinition in moduleDefinition.Types) {
yield return typeDefinition;
foreach (var definition in typeDefinition.AllDefinedTypes ())
yield return definition;
}
}
public static IEnumerable<TypeDefinition> AllDefinedTypes (this TypeDefinition typeDefinition)
{
foreach (var nestedType in typeDefinition.NestedTypes) {
yield return nestedType;
foreach (var definition in nestedType.AllDefinedTypes ())
yield return definition;
}
}
public static IEnumerable<IMemberDefinition> AllMembers (this ModuleDefinition module)
{
foreach (var type in module.AllDefinedTypes ()) {
yield return type;
foreach (var member in type.AllMembers ())
yield return member;
}
}
public static IEnumerable<IMemberDefinition> AllMembers (this TypeDefinition type)
{
foreach (var field in type.Fields)
yield return field;
foreach (var prop in type.Properties)
yield return prop;
foreach (var method in type.Methods)
yield return method;
foreach (var @event in type.Events)
yield return @event;
}
public static bool HasAttribute (this ICustomAttributeProvider provider, string name)
{
return provider.CustomAttributes.Any (ca => ca.AttributeType.Name == name);
}
public static bool HasAttributeDerivedFrom (this ICustomAttributeProvider provider, string name)
{
return provider.CustomAttributes.Any (ca => ca.AttributeType.Resolve ().DerivesFrom (name));
}
public static bool DerivesFrom (this TypeDefinition type, string baseTypeName)
{
if (type.Name == baseTypeName)
return true;
if (type.BaseType == null)
return false;
if (type.BaseType.Name == baseTypeName)
return true;
return type.BaseType.Resolve ().DerivesFrom (baseTypeName);
}
public static PropertyDefinition GetPropertyDefinition (this MethodDefinition method)
{
if (!method.IsSetter && !method.IsGetter)
throw new ArgumentException ();
var propertyName = method.Name.Substring (4);
return method.DeclaringType.Properties.First (p => p.Name == propertyName);
}
public static string GetSignature (this MethodDefinition method)
{
var builder = new StringBuilder ();
builder.Append (method.Name);
if (method.HasGenericParameters) {
builder.Append ('<');
for (int i = 0; i < method.GenericParameters.Count - 1; i++)
builder.Append ($"{method.GenericParameters [i]},");
builder.Append ($"{method.GenericParameters [method.GenericParameters.Count - 1]}>");
}
builder.Append ("(");
if (method.HasParameters) {
for (int i = 0; i < method.Parameters.Count - 1; i++) {
// TODO: modifiers
// TODO: default values
builder.Append ($"{method.Parameters [i].ParameterType},");
}
builder.Append (method.Parameters [method.Parameters.Count - 1].ParameterType);
}
builder.Append (")");
return builder.ToString ();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +0,0 @@
using System;
using System.Diagnostics;
namespace Mono.Linker.Tests.Cases.Expectations.Assertions {
/// <summary>
/// Base attribute for attributes that mark up the expected behavior of the linker on a member
/// </summary>
[Conditional("INCLUDE_EXPECTATIONS")]
public abstract class BaseExpectedLinkedBehaviorAttribute : Attribute {
}
}

View File

@@ -1,5 +0,0 @@
namespace Mono.Linker.Tests.Cases.Expectations.Assertions
{
public abstract class BaseInAssemblyAttribute : BaseExpectedLinkedBehaviorAttribute {
}
}

View File

@@ -1,13 +0,0 @@
using System;
namespace Mono.Linker.Tests.Cases.Expectations.Assertions {
[AttributeUsage (AttributeTargets.Class)]
public class IgnoreTestCaseAttribute : Attribute {
public IgnoreTestCaseAttribute (string reason)
{
if (reason == null)
throw new ArgumentNullException (nameof (reason));
}
}
}

View File

@@ -1,16 +0,0 @@
using System;
namespace Mono.Linker.Tests.Cases.Expectations.Assertions {
/// <summary>
/// Verifies that an assembly does exist in the output directory
/// </summary>
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Delegate, AllowMultiple = true, Inherited = false)]
public class KeptAssemblyAttribute : KeptAttribute {
public KeptAssemblyAttribute (string fileName)
{
if (string.IsNullOrEmpty (fileName))
throw new ArgumentException ("Value cannot be null or empty.", nameof (fileName));
}
}
}

View File

@@ -1,7 +0,0 @@
using System;
namespace Mono.Linker.Tests.Cases.Expectations.Assertions {
[AttributeUsage (AttributeTargets.All, Inherited = false)]
public class KeptAttribute : BaseExpectedLinkedBehaviorAttribute {
}
}

View File

@@ -1,21 +0,0 @@
using System;
namespace Mono.Linker.Tests.Cases.Expectations.Assertions
{
[AttributeUsage (AttributeTargets.All, AllowMultiple = true, Inherited = false)]
public class KeptAttributeAttribute : KeptAttribute
{
public KeptAttributeAttribute (string attributeName)
{
if (string.IsNullOrEmpty (attributeName))
throw new ArgumentException ("Value cannot be null or empty.", nameof (attributeName));
}
public KeptAttributeAttribute (Type type)
{
if (type == null)
throw new ArgumentNullException (nameof (type));
}
}
}

View File

@@ -1,7 +0,0 @@
using System;
namespace Mono.Linker.Tests.Cases.Expectations.Assertions {
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Event, AllowMultiple = false, Inherited = false)]
public sealed class KeptBackingFieldAttribute : KeptAttribute {
}
}

Some files were not shown because too many files have changed in this diff Show More