Skip to content

Commit

Permalink
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
  • Loading branch information
sbomer committed Dec 14, 2020
1 parent bc94748 commit a5532da
Show file tree
Hide file tree
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.

Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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 ();
Expand Down Expand Up @@ -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);
}
Expand Down
8 changes: 3 additions & 5 deletions src/linker/Linker.Steps/DynamicDependencyLookupStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
}
}

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

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

Expand Down
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.

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
}
Expand Down
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:
Expand Down Expand Up @@ -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)) {
Expand Down
23 changes: 19 additions & 4 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -1597,12 +1602,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;
}
}

Expand Down Expand Up @@ -1696,6 +1708,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 ();
Expand Down
2 changes: 1 addition & 1 deletion src/linker/Linker.Steps/ProcessLinkerXmlStepBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
1 change: 1 addition & 0 deletions src/linker/Linker.Steps/ResolveFromAssemblyStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions src/linker/Linker.Steps/ResolveFromXmlStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
17 changes: 13 additions & 4 deletions src/linker/Linker.Steps/TypeMapStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 8 additions & 4 deletions src/linker/Linker/Driver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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]
Expand All @@ -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) {
Expand Down Expand Up @@ -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 ());
Expand All @@ -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;
}

Expand Down
9 changes: 9 additions & 0 deletions src/linker/Linker/LinkContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,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)) {
Expand Down
15 changes: 15 additions & 0 deletions src/linker/Linker/Pipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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++) {
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down
Loading

0 comments on commit a5532da

Please sign in to comment.