Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy action behavior #1941

Merged
merged 41 commits into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
d132ff5
Stop rewriting scopes for copy assemblies with removed references
Mar 9, 2021
ebf608f
Keep exported types in copy assemblies
Mar 9, 2021
4bde1a5
Transitive forwarders are linked
Mar 11, 2021
9b1dbd9
Keep copyused behavior
Mar 12, 2021
c9d9740
Tests
Mar 12, 2021
72be601
Mark type if it's exported
Mar 15, 2021
2e5d99f
PR feedback
Mar 15, 2021
7a84586
Transitively mark forwarders when a facade is dynamically accessed an…
Mar 18, 2021
8759a89
Transitively mark forwarders
Mar 18, 2021
be095af
Fix formatting
Mar 18, 2021
6e6e3ca
Update MarkExportedType
Mar 20, 2021
67e79a7
Keep mscorlib assert
Mar 22, 2021
50e59aa
Mark forwarders when there's a type ref to an exported type in a copy…
Mar 22, 2021
818e700
Update DependencyKind
Mar 22, 2021
5c0c14f
Keep forwarders when member ref is resolved from a copy assembly
Mar 22, 2021
0254866
Add more tests, mark forwarded CAs
Mar 23, 2021
60c8f7a
Mark forwarded interfaces
Mar 23, 2021
7bc4e15
Simplify logic for typerefs
Mar 24, 2021
bbb7681
Clean
Mar 24, 2021
c37ca41
Merge branch 'main' into copyOriginal
mateoatr Mar 24, 2021
57adefb
Keep ProcessInternalsVisibleAttributes in process while-loop
Mar 24, 2021
2bee11e
Fix whitespace formatting
Mar 24, 2021
ea35250
Remove unused param
Mar 24, 2021
c15329d
Feedback
Mar 25, 2021
5da6cf9
Whitespace formatting
Mar 25, 2021
e4a4ab2
Remove unnecessary assembly
Mar 25, 2021
dc4d1ff
Add IL2104 warning
Mar 25, 2021
1258fa4
Remove unnecessary marking
Mar 25, 2021
b2b5a09
Update comment
Mar 25, 2021
9e4f6bc
Remove ExportedTypeExtensions
Mar 26, 2021
95889f0
Remove formatDiagnostics and revert cecil subm change
Mar 30, 2021
0f54051
Remove warning
Mar 30, 2021
8001127
Comment out check for removed warning
marek-safar Apr 1, 2021
512b028
Merge remote-tracking branch 'upstream/main' into copyOriginal
marek-safar Apr 1, 2021
ce214e2
Update more of existing calls
marek-safar Apr 1, 2021
e460f7e
Reproduce attribute issue
sbomer Apr 5, 2021
5fbd9fe
Merge pull request #1 from sbomer/copyOriginal
mateoatr Apr 5, 2021
b101637
Update scopes before sweeping forwarders
Apr 5, 2021
78406d4
Remove my enum from compilation
Apr 5, 2021
d23b2a0
Fix formatting
Apr 5, 2021
3c36208
Keep same behavior for scopes in copyused assemblies
Apr 6, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -966,14 +966,15 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
}
foreach (var typeNameValue in methodParams[0].UniqueValues ()) {
if (typeNameValue is KnownStringValue knownStringValue) {
TypeReference foundTypeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents);
TypeReference foundTypeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents, out AssemblyDefinition typeAssembly);
TypeDefinition foundType = foundTypeRef?.ResolveToMainTypeDefinition ();
if (foundType == null) {
// Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back.
reflectionContext.RecordHandledPattern ();
} else {
reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkTypeVisibleToReflection (foundTypeRef, new DependencyInfo (DependencyKind.AccessedViaReflection, callingMethodDefinition), callingMethodDefinition));
methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (foundType));
_context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.AccessedViaReflection, foundType));
}
} else if (typeNameValue == NullValue.Instance) {
reflectionContext.RecordHandledPattern ();
Expand Down Expand Up @@ -1977,14 +1978,15 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC
} else if (uniqueValue is SystemTypeValue systemTypeValue) {
MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes);
} else if (uniqueValue is KnownStringValue knownStringValue) {
TypeReference typeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents);
TypeReference typeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents, out AssemblyDefinition typeAssembly);
TypeDefinition foundType = typeRef?.ResolveToMainTypeDefinition ();
if (foundType == null) {
// Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back.
reflectionContext.RecordHandledPattern ();
} else {
MarkType (ref reflectionContext, typeRef);
MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, foundType, requiredMemberTypes);
_context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.DynamicallyAccessedMember, foundType));
}
} else if (uniqueValue == NullValue.Instance) {
// Ignore - probably unreachable path as it would fail at runtime anyway.
Expand Down
3 changes: 3 additions & 0 deletions src/linker/Linker.Steps/DescriptorMarker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ protected override void ProcessAssembly (AssemblyDefinition assembly, XPathNavig
if (GetTypePreserve (nav) == TypePreserve.All) {
foreach (var type in assembly.MainModule.Types)
MarkAndPreserveAll (type);

foreach (var exportedType in assembly.MainModule.ExportedTypes)
_context.MarkingHelpers.MarkExportedType (exportedType, assembly.MainModule, new DependencyInfo (DependencyKind.XmlDescriptor, assembly.MainModule));
} else {
ProcessTypes (assembly, nav, warnOnUnresolvedTypes);
ProcessNamespaces (assembly, nav);
Expand Down
4 changes: 2 additions & 2 deletions src/linker/Linker.Steps/LinkAttributesParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ CustomAttributeArgument[] ReadCustomAttributeArguments (XPathNodeIterator iterat
if (!typeref.IsTypeOf ("System", "Type"))
goto default;

TypeReference type = _context.TypeNameResolver.ResolveTypeName (svalue);
TypeReference type = _context.TypeNameResolver.ResolveTypeName (svalue, out _);
if (type == null) {
_context.LogError ($"Could not resolve custom attribute type value '{svalue}'", 1044, _xmlDocumentLocation);
return null;
Expand All @@ -284,7 +284,7 @@ TypeReference ResolveArgumentType (XPathNodeIterator iterator)
if (string.IsNullOrEmpty (typeName))
typeName = "System.String";

TypeReference typeref = _context.TypeNameResolver.ResolveTypeName (typeName);
TypeReference typeref = _context.TypeNameResolver.ResolveTypeName (typeName, out _);
if (typeref == null) {
_context.LogError ($"The type '{typeName}' used with attribute value '{iterator.Current.Value}' could not be found", 1041, _xmlDocumentLocation);
return null;
Expand Down
81 changes: 35 additions & 46 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ public MarkStep ()
}

public AnnotationStore Annotations => _context.Annotations;
public MarkingHelpers MarkingHelpers => _context.MarkingHelpers;
public Tracer Tracer => _context.Tracer;

public virtual void Process (LinkContext context)
Expand Down Expand Up @@ -365,40 +366,12 @@ private void MarkEntireTypeInternal (TypeDefinition type, bool includeBaseTypes,

void Process ()
{
while (ProcessPrimaryQueue () || ProcessMarkedPending () || ProcessLazyAttributes () || ProcessLateMarkedAttributes () || MarkFullyPreservedAssemblies () || ProcessInternalsVisibleAttributes ()) {

// deal with [TypeForwardedTo] pseudo-attributes

// This marks all exported types that resolve to a marked type. Note that it
// may mark unused exported types that happen to resolve to a type which was
// marked from a different reference. This seems incorrect.
// Note also that we may still remove type forwarder assemblies with marked exports in SweepStep,
// if they don't contain other used code.
// https://github.com/mono/linker/issues/1740
foreach (AssemblyDefinition assembly in _context.GetAssemblies ()) {
if (!assembly.MainModule.HasExportedTypes)
continue;

foreach (var exported in assembly.MainModule.ExportedTypes) {
bool isForwarder = exported.IsForwarder;
var declaringType = exported.DeclaringType;
while (!isForwarder && (declaringType != null)) {
isForwarder = declaringType.IsForwarder;
declaringType = declaringType.DeclaringType;
}

if (!isForwarder)
continue;
TypeDefinition type = exported.Resolve ();
if (type == null)
continue;
if (!Annotations.IsMarked (type))
continue;
var di = new DependencyInfo (DependencyKind.ExportedType, type);
_context.MarkingHelpers.MarkExportedType (exported, assembly.MainModule, di);
}
}
}
while (ProcessPrimaryQueue () ||
ProcessMarkedPending () ||
ProcessLazyAttributes () ||
ProcessLateMarkedAttributes () ||
MarkFullyPreservedAssemblies () ||
ProcessInternalsVisibleAttributes ()) ;

ProcessPendingTypeChecks ();
}
Expand Down Expand Up @@ -858,6 +831,8 @@ void MarkDynamicDependency (DynamicDependency dynamicDependency, IMemberDefiniti
_context.LogWarning ($"Unresolved type '{typeName}' in DynamicDependencyAttribute", 2036, context);
return;
}

MarkingHelpers.MarkMatchingExportedType (type, assembly, new DependencyInfo (DependencyKind.DynamicDependency, type));
} else if (dynamicDependency.Type is TypeReference typeReference) {
type = typeReference.Resolve ();
if (type == null) {
Expand Down Expand Up @@ -947,14 +922,20 @@ protected virtual void MarkUserDependency (IMemberDefinition context, CustomAttr
assembly = null;
}

TypeDefinition td;
TypeDefinition td = null;
if (args.Count >= 2 && args[1].Value is string typeName) {
td = _context.TypeNameResolver.ResolveTypeName (assembly ?? (context as MemberReference).Module.Assembly, typeName)?.Resolve ();
AssemblyDefinition assemblyDef = assembly ?? (context as MemberReference).Module.Assembly;
TypeReference tr = _context.TypeNameResolver.ResolveTypeName (assemblyDef, typeName);
if (tr != null)
td = tr.Resolve ();

if (td == null) {
_context.LogWarning (
$"Could not resolve dependency type '{typeName}' specified in a `PreserveDependency` attribute", 2004, context);
return;
}

MarkingHelpers.MarkMatchingExportedType (td, assemblyDef, new DependencyInfo (DependencyKind.PreservedDependency, ca));
} else {
td = context.DeclaringType.Resolve ();
}
Expand Down Expand Up @@ -1357,15 +1338,20 @@ void MarkEntireAssembly (AssemblyDefinition assembly)
{
Debug.Assert (Annotations.IsProcessed (assembly));

ModuleDefinition module = assembly.MainModule;
MarkCustomAttributes (assembly, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, assembly), null);
MarkCustomAttributes (assembly.MainModule, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, assembly.MainModule), null);
MarkCustomAttributes (module, new DependencyInfo (DependencyKind.AssemblyOrModuleAttribute, module), null);

if (assembly.MainModule.HasExportedTypes) {
// TODO: This needs more work accross all steps
foreach (TypeDefinition type in module.Types)
MarkEntireType (type, includeBaseTypes: false, includeInterfaceTypes: false, new DependencyInfo (DependencyKind.TypeInAssembly, assembly), null);

foreach (ExportedType exportedType in module.ExportedTypes) {
MarkingHelpers.MarkExportedType (exportedType, module, new DependencyInfo (DependencyKind.ExportedType, assembly));
MarkingHelpers.MarkForwardedScope (new TypeReference (exportedType.Namespace, exportedType.Name, module, exportedType.Scope));
}

foreach (TypeDefinition type in assembly.MainModule.Types)
MarkEntireType (type, includeBaseTypes: false, includeInterfaceTypes: false, new DependencyInfo (DependencyKind.TypeInAssembly, assembly), null);
foreach (TypeReference typeReference in module.GetTypeReferences ())
MarkingHelpers.MarkForwardedScope (typeReference);
}

void ProcessModuleType (AssemblyDefinition assembly)
Expand Down Expand Up @@ -1867,21 +1853,24 @@ protected virtual void MarkTypeConverterLikeDependency (CustomAttribute attribut
if (args.Count < 1)
return;

TypeDefinition tdef = null;
TypeDefinition typeDefinition = null;
switch (attribute.ConstructorArguments[0].Value) {
case string s:
tdef = _context.TypeNameResolver.ResolveTypeName (s)?.Resolve ();
typeDefinition = _context.TypeNameResolver.ResolveTypeName (s, out AssemblyDefinition assemblyDefinition)?.Resolve ();
if (typeDefinition != null)
MarkingHelpers.MarkMatchingExportedType (typeDefinition, assemblyDefinition, new DependencyInfo (DependencyKind.CustomAttribute, provider));

break;
case TypeReference type:
tdef = type.Resolve ();
typeDefinition = type.Resolve ();
break;
}

if (tdef == null)
if (typeDefinition == null)
return;

Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, provider), marked: false);
MarkMethodsIf (tdef.Methods, predicate, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), sourceLocationMember);
MarkMethodsIf (typeDefinition.Methods, predicate, new DependencyInfo (DependencyKind.ReferencedBySpecialAttribute, attribute), sourceLocationMember);
}

void MarkTypeWithDebuggerDisplayAttribute (TypeDefinition type, CustomAttribute attribute)
Expand Down
26 changes: 4 additions & 22 deletions src/linker/Linker.Steps/OutputStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -271,42 +271,24 @@ FileInfo GetOriginalAssemblyFileInfo (AssemblyDefinition assembly)

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)
return;

CopyFileAndRemoveReadOnly (source, target);

File.Copy (source, target, true);
if (!Context.LinkSymbols)
return;

var mdb = source + ".mdb";
if (File.Exists (mdb))
CopyFileAndRemoveReadOnly (mdb, target + ".mdb");
File.Copy (mdb, target + ".mdb", true);

var pdb = Path.ChangeExtension (source, "pdb");
if (File.Exists (pdb))
CopyFileAndRemoveReadOnly (pdb, Path.ChangeExtension (target, "pdb"));
}

static void CopyFileAndRemoveReadOnly (string src, string dest)
{
File.Copy (src, dest, true);

System.IO.FileAttributes attrs = File.GetAttributes (dest);

if ((attrs & System.IO.FileAttributes.ReadOnly) == System.IO.FileAttributes.ReadOnly)
File.SetAttributes (dest, attrs & ~System.IO.FileAttributes.ReadOnly);
File.Copy (pdb, Path.ChangeExtension (target, "pdb"), true);
}

protected virtual string GetAssemblyFileName (AssemblyDefinition assembly, string directory)
Expand Down
54 changes: 25 additions & 29 deletions src/linker/Linker.Steps/SweepStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ protected override void Process ()
foreach (var assembly in assemblies)
UpdateAssemblyReferencesToRemovedAssemblies (assembly);

// Update scopes before removing any type forwarder.
foreach (var assembly in assemblies) {
switch (Annotations.GetAction (assembly)) {
case AssemblyAction.Link:
case AssemblyAction.Save:
SweepAssemblyReferences (assembly);
break;
}
}

foreach (var assembly in assemblies)
ProcessAssemblyAction (assembly);

Expand Down Expand Up @@ -97,13 +107,13 @@ void UpdateAssemblyReferencesToRemovedAssemblies (AssemblyDefinition assembly)
{
var action = Annotations.GetAction (assembly);
switch (action) {
case AssemblyAction.Skip:
case AssemblyAction.Copy:
case AssemblyAction.Delete:
case AssemblyAction.Link:
case AssemblyAction.Save:
case AssemblyAction.Skip:
return;

case AssemblyAction.Copy:
case AssemblyAction.CopyUsed:
case AssemblyAction.AddBypassNGen:
case AssemblyAction.AddBypassNGenUsed:
Expand All @@ -121,7 +131,6 @@ void UpdateAssemblyReferencesToRemovedAssemblies (AssemblyDefinition assembly)

switch (action) {
case AssemblyAction.CopyUsed:
case AssemblyAction.Copy:
//
// Assembly has a reference to another assembly which has been fully removed. This can
// happen when for example the reference assembly is 'copy-used' and it's not needed.
Expand Down Expand Up @@ -172,22 +181,14 @@ protected void ProcessAssemblyAction (AssemblyDefinition assembly)
break;

case AssemblyAction.CopyUsed:
Annotations.SetAction (assembly, AssemblyAction.Copy);
goto case AssemblyAction.Copy;
AssemblyAction assemblyAction = AssemblyAction.Copy;
mateoatr marked this conversation as resolved.
Show resolved Hide resolved
if (!Context.KeepTypeForwarderOnlyAssemblies && SweepTypeForwarders (assembly))
assemblyAction = AssemblyAction.Save;

case AssemblyAction.Copy:
//
// Facade assemblies can have unused forwarders pointing to
// removed type (when facades are kept)
//
// main.exe -> facade.dll -> lib.dll
// link | copy | link
//
// when main.exe has unused reference to type in lib.dll
//
if (SweepTypeForwarders (assembly))
Annotations.SetAction (assembly, AssemblyAction.Save);
Annotations.SetAction (assembly, assemblyAction);
break;

case AssemblyAction.Copy:
break;

case AssemblyAction.Link:
Expand All @@ -196,12 +197,6 @@ protected void ProcessAssemblyAction (AssemblyDefinition assembly)

case AssemblyAction.Save:
SweepTypeForwarders (assembly);

//
// Save means we need to rewrite the assembly due to removed assembly
// references
//
SweepAssemblyReferences (assembly);
break;
}
}
Expand All @@ -210,11 +205,13 @@ protected virtual void SweepAssembly (AssemblyDefinition assembly)
{
var types = new List<TypeDefinition> ();
ModuleDefinition main = assembly.MainModule;
bool updateScopes = false;

foreach (TypeDefinition type in main.Types) {
if (!ShouldRemove (type)) {
SweepType (type);
types.Add (type);
updateScopes = true;
continue;
}

Expand All @@ -230,24 +227,23 @@ protected virtual void SweepAssembly (AssemblyDefinition assembly)
main.Types.Add (type);

SweepResources (assembly);
SweepCustomAttributes (assembly);
updateScopes |= SweepCustomAttributes (assembly);

foreach (var module in assembly.Modules)
SweepCustomAttributes (module);
updateScopes |= SweepCustomAttributes (module);

//
// MainModule module references are used by pinvoke
//
if (main.HasModuleReferences)
SweepCollectionMetadata (main.ModuleReferences);
updateScopes |= SweepCollectionMetadata (main.ModuleReferences);

if (main.EntryPoint != null && !Annotations.IsMarked (main.EntryPoint)) {
main.EntryPoint = null;
}

SweepTypeForwarders (assembly);

SweepAssemblyReferences (assembly);
if (SweepTypeForwarders (assembly) || updateScopes)
SweepAssemblyReferences (assembly);
}

static void SweepAssemblyReferences (AssemblyDefinition assembly)
Expand Down
Loading