Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable processing of dynamically referenced assemblies
Browse files Browse the repository at this point in the history
This introduces the ability to add assemblies to LinkContext lazily and have them
processed in MarkStep. For now, the only additional processing is to
load references and run the TypeMap logic. Later, embedded XML processing and
constant propagation will also happen for lazy assemblies.

This is Phase 1 outlined in dotnet#1164 (comment).

Fixes dotnet#943
Fixes dotnet#1079
sbomer committed Dec 10, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 9c88abd commit 6606168
Showing 27 changed files with 134 additions and 48 deletions.
5 changes: 4 additions & 1 deletion src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

@@ -820,6 +820,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c
// 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 {
_context.ProcessReferenceClosure (foundType.Module.Assembly);
reflectionContext.RecordRecognizedPattern (foundType, () => _markStep.MarkTypeVisibleToReflection (foundTypeRef, new DependencyInfo (DependencyKind.AccessedViaReflection, callingMethodDefinition), callingMethodDefinition));
methodReturnValue = MergePointValue.MergeValues (methodReturnValue, new SystemTypeValue (foundType));
}
@@ -1361,6 +1362,7 @@ void ProcessCreateInstanceByName (ref ReflectionPatternContext reflectionContext
reflectionContext.RecordUnrecognizedPattern (2061, $"The assembly name '{assemblyNameStringValue.Contents}' passed to method '{calledMethod.GetDisplayName ()}' references assembly which is not available.");
continue;
}
_context.ProcessReferenceClosure (resolvedAssembly);

var typeRef = _context.TypeNameResolver.ResolveTypeName (resolvedAssembly, typeNameStringValue.Contents);
var resolvedType = typeRef?.Resolve ();
@@ -1611,6 +1613,7 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC
// 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 {
_context.ProcessReferenceClosure (foundType.Module.Assembly);
MarkType (ref reflectionContext, typeRef);
MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, foundType, requiredMemberTypes);
}
8 changes: 3 additions & 5 deletions src/linker/Linker.Steps/DynamicDependencyLookupStep.cs
Original file line number Diff line number Diff line change
@@ -4,14 +4,13 @@

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Mono.Cecil;

#nullable enable

namespace Mono.Linker.Steps
{
public class DynamicDependencyLookupStep : LoadReferencesStep
public class DynamicDependencyLookupStep : BaseStep
{
protected override void ProcessAssembly (AssemblyDefinition assembly)
{
@@ -74,7 +73,7 @@ void ProcessDynamicDependencyAttributes (IMemberDefinition member)
var assembly = Context.Resolve (new AssemblyNameReference (assemblyName, new Version ()));
if (assembly == null)
continue;
ProcessReferences (assembly);
Context.ProcessReferenceClosure (assembly);
}
}

@@ -90,8 +89,7 @@ void ProcessDynamicDependencyAttributes (IMemberDefinition member)
Context.LogWarning ($"Unresolved assembly '{dynamicDependency.AssemblyName}' in 'DynamicDependencyAttribute'", 2035, member);
continue;
}

ProcessReferences (assembly);
Context.ProcessReferenceClosure (assembly);
}
}

15 changes: 15 additions & 0 deletions src/linker/Linker.Steps/IAssemblyStep.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Mono.Cecil;
using System.Collections.Generic;

namespace Mono.Linker.Steps
{
public interface IAssemblyStep
{
void Initialize (LinkContext context);
void ProcessAssemblies (HashSet<AssemblyDefinition> assembly);
}
}
7 changes: 4 additions & 3 deletions src/linker/Linker.Steps/LinkAttributesStep.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

@@ -173,6 +173,8 @@ bool GetAttributeType (XPathNodeIterator iterator, string attributeFullName, out
}

attributeType = Context.TypeNameResolver.ResolveTypeName (assembly, attributeFullName)?.Resolve ();
if (attributeType != null)
Context.ProcessReferenceClosure (attributeType.Module.Assembly);
}

if (attributeType == null) {
@@ -357,8 +359,7 @@ protected override AssemblyDefinition GetAssembly (LinkContext context, Assembly
{
var assembly = context.Resolve (assemblyName);
if (assembly != null)
ProcessReferences (assembly);

context.ProcessReferenceClosure (assembly);
return assembly;
}
}
30 changes: 26 additions & 4 deletions src/linker/Linker.Steps/LoadReferencesStep.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//
//
// LoadReferencesStep.cs
//
// Author:
@@ -32,20 +32,42 @@

namespace Mono.Linker.Steps
{
public class LoadReferencesStep : BaseStep
public class LoadReferencesStep : IAssemblyStep
{
LinkContext _context;

LinkContext Context => _context;

readonly HashSet<AssemblyNameDefinition> references = new HashSet<AssemblyNameDefinition> ();

protected override void ProcessAssembly (AssemblyDefinition assembly)
readonly HashSet<AssemblyDefinition> newReferences = new HashSet<AssemblyDefinition> ();

public void Initialize (LinkContext context)
{
_context = context;
}

public virtual void ProcessAssemblies (HashSet<AssemblyDefinition> assemblies)
{
ProcessReferences (assembly);
newReferences.Clear ();

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

// Ensure that subsequent IAssemblySteps only process assemblies
// which have not already been processed.
assemblies.Clear ();
foreach (var assembly in newReferences)
assemblies.Add (assembly);
}

protected void ProcessReferences (AssemblyDefinition assembly)
{
if (!references.Add (assembly.Name))
return;

newReferences.Add (assembly);

Context.RegisterAssembly (assembly);

foreach (AssemblyDefinition referenceDefinition in Context.ResolveReferences (assembly)) {
23 changes: 19 additions & 4 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
@@ -350,6 +350,9 @@ void Process ()
if (!assembly.MainModule.HasExportedTypes)
continue;

// We may have resolved new type forwarder assemblies which have not been processed.
_context.ProcessReferenceClosure (assembly);

foreach (var exported in assembly.MainModule.ExportedTypes) {
bool isForwarder = exported.IsForwarder;
var declaringType = exported.DeclaringType;
@@ -657,11 +660,12 @@ void MarkDynamicDependency (DynamicDependency dynamicDependency, IMemberDefiniti
Debug.Assert (context is MethodDefinition || context is FieldDefinition);
AssemblyDefinition assembly;
if (dynamicDependency.AssemblyName != null) {
assembly = _context.GetLoadedAssembly (dynamicDependency.AssemblyName);
assembly = _context.Resolve (dynamicDependency.AssemblyName);
if (assembly == null) {
_context.LogWarning ($"Unresolved assembly '{dynamicDependency.AssemblyName}' in 'DynamicDependencyAttribute'", 2035, context);
return;
}
_context.ProcessReferenceClosure (assembly);
} else {
assembly = context.DeclaringType.Module.Assembly;
Debug.Assert (assembly != null);
@@ -765,12 +769,13 @@ protected virtual void MarkUserDependency (MemberReference context, CustomAttrib
AssemblyDefinition assembly;
var args = ca.ConstructorArguments;
if (args.Count >= 3 && args[2].Value is string assemblyName) {
assembly = _context.GetLoadedAssembly (assemblyName);
assembly = _context.Resolve (assemblyName);
if (assembly == null) {
_context.LogWarning (
$"Could not resolve dependency assembly '{assemblyName}' specified in a 'PreserveDependency' attribute", 2003, context.Resolve ());
return;
}
_context.ProcessReferenceClosure (assembly);
} else {
assembly = null;
}
@@ -1590,12 +1595,19 @@ TypeDefinition GetDebuggerAttributeTargetType (CustomAttribute ca, AssemblyDefin
if (property.Name == "TargetTypeName") {
string targetTypeName = (string) property.Argument.Value;
TypeName typeName = TypeParser.ParseTypeName (targetTypeName);
TypeDefinition typeDef;
if (typeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) {
AssemblyDefinition assembly = _context.GetLoadedAssembly (assemblyQualifiedTypeName.AssemblyName.Name);
return _context.TypeNameResolver.ResolveTypeName (assembly, targetTypeName)?.Resolve ();
typeDef = _context.TypeNameResolver.ResolveTypeName (assembly, targetTypeName)?.Resolve ();
if (typeDef != null)
_context.ProcessReferenceClosure (typeDef.Module.Assembly);
return typeDef;
}

return _context.TypeNameResolver.ResolveTypeName (asm, targetTypeName)?.Resolve ();
typeDef = _context.TypeNameResolver.ResolveTypeName (asm, targetTypeName)?.Resolve ();
if (typeDef != null)
_context.ProcessReferenceClosure (typeDef.Module.Assembly);
return typeDef;
}
}

@@ -1689,6 +1701,9 @@ protected virtual void MarkTypeConverterLikeDependency (CustomAttribute attribut
switch (attribute.ConstructorArguments[0].Value) {
case string s:
tdef = _context.TypeNameResolver.ResolveTypeName (s)?.Resolve ();
if (tdef != null)
_context.ProcessReferenceClosure (tdef.Module.Assembly);
// ResolveTypeName might resolve an assembly (or multiple assemblies for generic arguments...)
break;
case TypeReference type:
tdef = type.Resolve ();
2 changes: 1 addition & 1 deletion src/linker/Linker.Steps/ProcessLinkerXmlStepBase.cs
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ public enum AllowedAssemblies
AllAssemblies = 0x4 | AnyAssembly
}

public abstract class ProcessLinkerXmlStepBase : LoadReferencesStep
public abstract class ProcessLinkerXmlStepBase : BaseStep
{
const string FullNameAttributeName = "fullname";
const string LinkerElementName = "linker";
1 change: 1 addition & 0 deletions src/linker/Linker.Steps/ResolveFromAssemblyStep.cs
Original file line number Diff line number Diff line change
@@ -67,6 +67,7 @@ protected override void Process ()
Context.Resolver.IgnoreUnresolved = false;
AssemblyDefinition assembly = _assembly ?? Context.Resolve (_file);
Context.Resolver.IgnoreUnresolved = ignoreUnresolved;
Context.ProcessReferenceClosure (assembly);

if (_rootVisibility != RootVisibility.Any && HasInternalsVisibleTo (assembly))
_rootVisibility = RootVisibility.PublicAndFamilyAndAssembly;
3 changes: 1 addition & 2 deletions src/linker/Linker.Steps/ResolveFromXmlStep.cs
Original file line number Diff line number Diff line change
@@ -322,8 +322,7 @@ protected override AssemblyDefinition GetAssembly (LinkContext context, Assembly
{
var assembly = context.Resolve (assemblyName);
if (assembly != null)
ProcessReferences (assembly);

context.ProcessReferenceClosure (assembly);
return assembly;
}

17 changes: 13 additions & 4 deletions src/linker/Linker.Steps/TypeMapStep.cs
Original file line number Diff line number Diff line change
@@ -32,13 +32,22 @@
namespace Mono.Linker.Steps
{

public class TypeMapStep : BaseStep
public class TypeMapStep : IAssemblyStep
{
LinkContext _context;
AnnotationStore Annotations => _context.Annotations;

protected override void ProcessAssembly (AssemblyDefinition assembly)
public void Initialize (LinkContext context)
{
foreach (TypeDefinition type in assembly.MainModule.Types)
MapType (type);
_context = context;
}

public void ProcessAssemblies (HashSet<AssemblyDefinition> assemblies)
{
foreach (var assembly in assemblies) {
foreach (TypeDefinition type in assembly.MainModule.Types)
MapType (type);
}
}

protected virtual void MapType (TypeDefinition type)
12 changes: 8 additions & 4 deletions src/linker/Linker/Driver.cs
Original file line number Diff line number Diff line change
@@ -707,7 +707,6 @@ protected int SetupContext (ILogger customLogger = null)
// ResolveFromAssemblyStep [optional, possibly many]
// ResolveFromXmlStep [optional, possibly many]
// [mono only] ResolveFromXApiStep [optional, possibly many]
// LoadReferencesStep
// [mono only] LoadI18nAssemblies
// BlacklistStep
// dynamically adds steps:
@@ -717,7 +716,6 @@ protected int SetupContext (ILogger customLogger = null)
// LinkAttributesStep [optional, possibly many]
// DynamicDependencyLookupStep
// [mono only] PreserveCalendarsStep [optional]
// TypeMapStep
// BodySubstituterStep [optional]
// RemoveSecurityStep [optional]
// [mono only] RemoveFeaturesStep [optional]
@@ -732,6 +730,12 @@ protected int SetupContext (ILogger customLogger = null)
// RegenerateGuidStep [optional]
// SealerStep
// OutputStep

//
// Pipeline Steps which run when new assemblies are processed
//
// LoadReferencesStep
// TypeMapStep
//

foreach (string custom_step in custom_steps) {
@@ -1213,10 +1217,8 @@ static void About ()
static Pipeline GetStandardPipeline ()
{
Pipeline p = new Pipeline ();
p.AppendStep (new LoadReferencesStep ());
p.AppendStep (new BlacklistStep ());
p.AppendStep (new DynamicDependencyLookupStep ());
p.AppendStep (new TypeMapStep ());
p.AppendStep (new MarkStep ());
p.AppendStep (new ValidateVirtualMethodAnnotationsStep ());
p.AppendStep (new ProcessWarningsStep ());
@@ -1225,6 +1227,8 @@ static Pipeline GetStandardPipeline ()
p.AppendStep (new CleanStep ());
p.AppendStep (new RegenerateGuidStep ());
p.AppendStep (new OutputStep ());
p.AppendAssemblyStep (new LoadReferencesStep ());
p.AppendAssemblyStep (new TypeMapStep ());
return p;
}

9 changes: 9 additions & 0 deletions src/linker/Linker/LinkContext.cs
Original file line number Diff line number Diff line change
@@ -305,6 +305,15 @@ public TypeDefinition GetType (string fullName)
return assembly.MainModule.GetType (fullName);
}

// Run the per-assembly logic on a new assembly.
// Each step may modify the context of assemblies passed to the next.
public void ProcessReferenceClosure (AssemblyDefinition assembly)
{
var assemblies = new HashSet<AssemblyDefinition> { assembly };
foreach (var step in Pipeline.GetAssemblySteps ())
step.ProcessAssemblies (assemblies);
}

public AssemblyDefinition Resolve (string name)
{
if (File.Exists (name)) {
15 changes: 15 additions & 0 deletions src/linker/Linker/Pipeline.cs
Original file line number Diff line number Diff line change
@@ -38,10 +38,12 @@ public class Pipeline
{

readonly List<IStep> _steps;
readonly List<IAssemblyStep> _assemblySteps;

public Pipeline ()
{
_steps = new List<IStep> ();
_assemblySteps = new List<IAssemblyStep> ();
}

public void PrependStep (IStep step)
@@ -54,6 +56,11 @@ public void AppendStep (IStep step)
_steps.Add (step);
}

public void AppendAssemblyStep (IAssemblyStep step)
{
_assemblySteps.Add (step);
}

public void AddStepBefore (Type target, IStep step)
{
for (int i = 0; i < _steps.Count; i++) {
@@ -123,6 +130,9 @@ public void RemoveStep (Type target)

public void Process (LinkContext context)
{
foreach (var step in _assemblySteps)
step.Initialize (context);

while (_steps.Count > 0) {
IStep step = _steps[0];
ProcessStep (context, step);
@@ -140,6 +150,11 @@ public IStep[] GetSteps ()
return _steps.ToArray ();
}

public IAssemblyStep[] GetAssemblySteps ()
{
return _assemblySteps.ToArray ();
}

public bool ContainsStep (Type type)
{
foreach (IStep step in _steps)
Loading

0 comments on commit 6606168

Please sign in to comment.