Skip to content

Commit

Permalink
Correctly handle assembly qualified names in generic parameters (dotn…
Browse files Browse the repository at this point in the history
…et/linker#1546)

We always assumed that the generic parameters would come from the same assembly as the generic type itself.
Adds a new test to validate the scenario.


Commit migrated from dotnet/linker@82ae21e
  • Loading branch information
vitek-karas authored Oct 7, 2020
1 parent b090314 commit 03d7afd
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1336,7 +1336,7 @@ void ProcessCreateInstanceByName (ref ReflectionPatternContext reflectionContext
continue;
}

var resolvedType = (TypeNameResolver.ResolveTypeName (resolvedAssembly, typeNameStringValue.Contents))?.Resolve ();
var resolvedType = (_context.TypeNameResolver.ResolveTypeName (resolvedAssembly, typeNameStringValue.Contents))?.Resolve ();
if (resolvedType == null) {
// It's not wrong to have a reference to non-existing type - the code may well expect to get an exception in this case
// Note that we did find the assembly, so it's not a linker config problem, it's either intentional, or wrong versions of assemblies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ bool GetAttributeType (XPathNodeIterator iterator, string attributeFullName, out
return false;
}

attributeType = TypeNameResolver.ResolveTypeName (assembly, attributeFullName)?.Resolve ();
attributeType = Context.TypeNameResolver.ResolveTypeName (assembly, attributeFullName)?.Resolve ();
}

if (attributeType == null) {
Expand Down
6 changes: 3 additions & 3 deletions src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ protected virtual void MarkUserDependency (MemberReference context, CustomAttrib

TypeDefinition td;
if (args.Count >= 2 && args[1].Value is string typeName) {
td = TypeNameResolver.ResolveTypeName (assembly ?? context.Module.Assembly, typeName)?.Resolve ();
td = _context.TypeNameResolver.ResolveTypeName (assembly ?? context.Module.Assembly, typeName)?.Resolve ();
if (td == null) {
_context.LogWarning (
$"Could not resolve dependency type '{typeName}' specified in a `PreserveDependency` attribute", 2004, context.Resolve ());
Expand Down Expand Up @@ -1562,10 +1562,10 @@ TypeDefinition GetDebuggerAttributeTargetType (CustomAttribute ca, AssemblyDefin
TypeName typeName = TypeParser.ParseTypeName (targetTypeName);
if (typeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) {
AssemblyDefinition assembly = _context.GetLoadedAssembly (assemblyQualifiedTypeName.AssemblyName.Name);
return TypeNameResolver.ResolveTypeName (assembly, targetTypeName)?.Resolve ();
return _context.TypeNameResolver.ResolveTypeName (assembly, targetTypeName)?.Resolve ();
}

return TypeNameResolver.ResolveTypeName (asm, targetTypeName)?.Resolve ();
return _context.TypeNameResolver.ResolveTypeName (asm, targetTypeName)?.Resolve ();
}
}

Expand Down
17 changes: 10 additions & 7 deletions src/tools/illink/src/linker/Linker/TypeNameResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ public TypeReference ResolveTypeName (string typeNameString)
}

if (parsedTypeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) {
AssemblyDefinition assembly = _context.GetLoadedAssembly (assemblyQualifiedTypeName.AssemblyName.Name);
return ResolveTypeName (assembly, assemblyQualifiedTypeName.TypeName);
return ResolveTypeName (null, assemblyQualifiedTypeName);
}

foreach (var assemblyDefiniton in _context.GetAssemblies ()) {
Expand All @@ -41,19 +40,23 @@ public TypeReference ResolveTypeName (string typeNameString)
return null;
}

public static TypeReference ResolveTypeName (AssemblyDefinition assembly, string typeNameString)
public TypeReference ResolveTypeName (AssemblyDefinition assembly, string typeNameString)
{
return ResolveTypeName (assembly, TypeParser.ParseTypeName (typeNameString));
}

static TypeReference ResolveTypeName (AssemblyDefinition assembly, TypeName typeName)
TypeReference ResolveTypeName (AssemblyDefinition assembly, TypeName typeName)
{
if (typeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) {
// In this case we ignore the assembly parameter since the type name has assembly in it
var assemblyFromName = _context.GetLoadedAssembly (assemblyQualifiedTypeName.AssemblyName.Name);
return ResolveTypeName (assemblyFromName, assemblyQualifiedTypeName.TypeName);
}

if (assembly == null || typeName == null)
return null;

if (typeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName) {
return ResolveTypeName (assembly, assemblyQualifiedTypeName.TypeName);
} else if (typeName is ConstructedGenericTypeName constructedGenericTypeName) {
if (typeName is ConstructedGenericTypeName constructedGenericTypeName) {
var genericTypeRef = ResolveTypeName (assembly, constructedGenericTypeName.GenericType);
if (genericTypeRef == null)
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ public static void Main ()
TestFromTypeOf ();
TestFromTypeGetTypeOverConstant ();
TestFromStringContantWithAnnotation ();
TestFromStringConstantWithGeneric ();
TestFromStringConstantWithGenericAndAssemblyQualified ();
TestFromStringConstantWithGenericAndAssemblyQualifiedInvalidAssembly ();
TestFromStringConstantWithGenericAndAssemblyQualifiedNonExistingAssembly ();
}

[Kept]
Expand Down Expand Up @@ -123,5 +127,65 @@ private static void RequireCombinationOnString (
string typeName)
{
}

// Issue: https://github.com/mono/linker/issues/1537
//[Kept]
//[KeptMember (".ctor()")]
class FromStringConstantWithGenericInner
{
}

[Kept]
[KeptMember (".ctor()")]
class FromStringConstantWithGeneric<T>
{
[Kept]
public T GetValue () { return default (T); }
}

[Kept]
static void TestFromStringConstantWithGeneric ()
{
RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGeneric`1[[Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericInner]]");
}

[Kept]
[KeptMember (".ctor()")]
class FromStringConstantWithGenericAndAssemblyQualified<T>
{
[Kept]
public T GetValue () { return default (T); }
}

[Kept]
// This is a workaround for the inability to lazy load assemblies. The type name resolver will not load new assemblies
// and since the KeptAttribute is otherwise not referenced by the test anywhere (the test-validation attributes are removed before processing normally)
// it would not resolve from name - since its assembly is not loaded.
// Adding DynamicDependency solves this problem as it is basically the only attribute which has the ability to load new assemblies.
[DynamicDependency (DynamicallyAccessedMemberTypes.None, typeof (KeptAttribute))]
static void TestFromStringConstantWithGenericAndAssemblyQualified ()
{
RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericAndAssemblyQualified`1[[Mono.Linker.Tests.Cases.Expectations.Assertions.KeptAttribute,Mono.Linker.Tests.Cases.Expectations]]");
}

class InvalidAssemblyNameType
{
}

[Kept]
static void TestFromStringConstantWithGenericAndAssemblyQualifiedInvalidAssembly ()
{
RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+InvalidAssemblyNameType,Invalid/Assembly/Name");
}

class NonExistingAssemblyType
{
}

[Kept]
static void TestFromStringConstantWithGenericAndAssemblyQualifiedNonExistingAssembly ()
{
RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+InvalidAssemblyNameType,NonExistingAssembly");
}
}
}

0 comments on commit 03d7afd

Please sign in to comment.