Skip to content

Commit

Permalink
Keep facades of dynamically accessed exported types
Browse files Browse the repository at this point in the history
  • Loading branch information
Mateo Torres Ruiz committed Feb 26, 2021
1 parent ed016c4 commit 39411d5
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 5 deletions.
5 changes: 5 additions & 0 deletions src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,11 @@ void MarkDynamicDependency (DynamicDependency dynamicDependency, IMemberDefiniti
_context.LogWarning ($"Unresolved type '{typeName}' in DynamicDependencyAttribute", 2036, context);
return;
}

var module = assembly.MainModule;
if (module.GetMatchingExportedType (type, out var exportedType)) {
_context.MarkingHelpers.MarkExportedType (exportedType, module, new DependencyInfo (DependencyKind.ModuleOfExportedType, module));
}
} else if (dynamicDependency.Type is TypeReference typeReference) {
type = typeReference.Resolve ();
if (type == null) {
Expand Down
25 changes: 21 additions & 4 deletions src/linker/Linker/MarkingHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using Mono.Cecil;

namespace Mono.Linker
Expand All @@ -12,12 +11,30 @@ public MarkingHelpers (LinkContext context)
_context = context;
}

public void MarkExportedType (ExportedType type, ModuleDefinition module, in DependencyInfo reason)
public void MarkExportedType (ExportedType exportedType, ModuleDefinition module, in DependencyInfo reason)
{
if (!_context.Annotations.MarkProcessed (type, reason))
if (!_context.Annotations.MarkProcessed (exportedType, reason))
return;

if (reason.Kind == DependencyKind.ModuleOfExportedType) {
var facadeAssembly = (reason.Source as ModuleDefinition).Assembly;
TypeDefinition resolvedType = exportedType.Resolve ();
while (facadeAssembly.FullName != resolvedType.Module.Assembly.FullName) {
_context.Annotations.Mark (module, new DependencyInfo (DependencyKind.ModuleOfExportedType, exportedType));
foreach (var assemblyReference in module.AssemblyReferences) {
if (assemblyReference.MetadataToken == exportedType.Scope.MetadataToken) {
facadeAssembly = module.AssemblyResolver.Resolve (assemblyReference);
module = facadeAssembly.MainModule;
break;
}
}
}

return;
}

if (_context.KeepTypeForwarderOnlyAssemblies)
_context.Annotations.Mark (module, new DependencyInfo (DependencyKind.ModuleOfExportedType, type));
_context.Annotations.Mark (module, new DependencyInfo (DependencyKind.ModuleOfExportedType, exportedType));
}
}
}
15 changes: 15 additions & 0 deletions src/linker/Linker/ModuleDefinitionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,21 @@ public static bool IsCrossgened (this ModuleDefinition module)
(module.Attributes & ModuleAttributes.ILLibrary) != 0;
}

public static bool GetMatchingExportedType (this ModuleDefinition module, TypeDefinition typeDefinition, out ExportedType exportedType)
{
exportedType = null;
if (!module.HasExportedTypes || typeDefinition == null)
return false;

foreach (var et in module.ExportedTypes)
if (et.FullName == typeDefinition.FullName) {
exportedType = et;
return true;
}

return false;
}

public static TypeDefinition ResolveType (this ModuleDefinition module, string typeFullName)
{
if (typeFullName == null)
Expand Down
8 changes: 7 additions & 1 deletion src/linker/Linker/TypeNameResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,13 @@ TypeReference ResolveTypeName (AssemblyDefinition assembly, TypeName typeName)
};
}

return assembly.MainModule.ResolveType (typeName.ToString ());
ModuleDefinition module = assembly.MainModule;
TypeDefinition typeDefinition = module.ResolveType (typeName.ToString ());
if (module.GetMatchingExportedType (typeDefinition, out var exportedType)) {
_context.MarkingHelpers.MarkExportedType (exportedType, module, new DependencyInfo (DependencyKind.ModuleOfExportedType, module));
}

return typeDefinition;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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 System.Diagnostics.CodeAnalysis;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies;

namespace Mono.Linker.Tests.Cases.TypeForwarding
{

[SetupLinkerAction ("link", "test")]
[SetupLinkerUserAction ("copyused")]
[KeepTypeForwarderOnlyAssemblies ("false")]

[SetupCompileBefore ("Forwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })]

// After compiling the test case we then replace the reference impl with implementation + type forwarder
[SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })]
[SetupCompileAfter ("Forwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })]

[KeptAssembly ("Forwarder.dll")]
[KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "GetSomeValue()")]
[RemovedForwarder ("Forwarder.dll", nameof (ImplementationStruct))]
class UsedForwarderIsDynamicallyAccessedWithAssemblyCopyUsed
{
static void Main ()
{
PointToTypeInFacade ("Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibrary, Forwarder");
}

[Kept]
static void PointToTypeInFacade (
[KeptAttributeAttribute (typeof(DynamicallyAccessedMembersAttribute))]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string typeName)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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 System.Diagnostics.CodeAnalysis;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
using Mono.Linker.Tests.Cases.TypeForwarding.Dependencies;

namespace Mono.Linker.Tests.Cases.TypeForwarding
{
[SetupLinkerAction ("link", "test")]
[SetupLinkerUserAction ("copyused")]
[KeepTypeForwarderOnlyAssemblies ("false")]

[SetupCompileBefore ("SecondForwarder.dll", new[] { "Dependencies/ReferenceImplementationLibrary.cs" }, defines: new[] { "INCLUDE_REFERENCE_IMPL" })]
[SetupCompileBefore ("FirstForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "SecondForwarder.dll" })]

// After compiling the test case we then replace the reference impl with implementation + type forwarder
[SetupCompileAfter ("Implementation.dll", new[] { "Dependencies/ImplementationLibrary.cs" })]
[SetupCompileAfter ("SecondForwarder.dll", new[] { "Dependencies/ForwarderLibrary.cs" }, references: new[] { "Implementation.dll" })]

[KeptAssembly ("FirstForwarder.dll")]
[KeptAssembly ("SecondForwarder.dll")]
[KeptMemberInAssembly ("Implementation.dll", typeof (ImplementationLibrary), "GetSomeValue()")]
[RemovedForwarder ("FirstForwarder.dll", nameof (ImplementationStruct))]
[RemovedForwarder ("SecondForwarder.dll", nameof (ImplementationStruct))]
class UsedTransitiveForwarderIsDynamicallyAccessed
{
static void Main ()
{
// FirstForwarder -> SecondForwarder -> Implementation
PointToTypeInFacade ("Mono.Linker.Tests.Cases.TypeForwarding.Dependencies.ImplementationLibrary, FirstForwarder");
}

[Kept]
static void PointToTypeInFacade (
[KeptAttributeAttribute (typeof(DynamicallyAccessedMembersAttribute))]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] string typeName)
{
}
}
}

0 comments on commit 39411d5

Please sign in to comment.