Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,13 @@ private unsafe struct TypeNameUtf8
{
public required void* Utf8TypeName { get; init; }
public required int Utf8TypeNameLen { get; init; }

public bool Equals(TypeNameUtf8 other)
{
ReadOnlySpan<byte> thisSpan = new ReadOnlySpan<byte>(Utf8TypeName, Utf8TypeNameLen);
ReadOnlySpan<byte> otherSpan = new ReadOnlySpan<byte>(other.Utf8TypeName, other.Utf8TypeNameLen);
return thisSpan.SequenceEqual(otherSpan);
}
}

[RequiresUnreferencedCode("Lazy TypeMap isn't supported for Trimmer scenarios")]
Expand All @@ -282,6 +289,8 @@ public DelayedType(TypeNameUtf8 typeNameUtf8, RuntimeAssembly fallbackAssembly)
_type = null;
}

public TypeNameUtf8 TypeName => _typeNameUtf8;

public unsafe Type GetOrLoadType()
{
if (_type is null)
Expand Down Expand Up @@ -328,12 +337,16 @@ protected override bool TryGetOrLoadType(string key, [NotNullWhen(true)] out Typ
public void Add(string key, TypeNameUtf8 targetType, RuntimeAssembly fallbackAssembly)
{
int hash = ComputeHashCode(key);
if (_lazyData.ContainsKey(hash))
// Allow duplicates that have the same string -> mapping. They may have different trimTargets.
// Warn if the mapping conflicts with an existing mapping.
if (!_lazyData.TryGetValue(hash, out DelayedType? existing))
{
_lazyData.Add(hash, new DelayedType(targetType, fallbackAssembly));
}
else if (!existing.TypeName.Equals(targetType))
{
ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key);
}

_lazyData.Add(hash, new DelayedType(targetType, fallbackAssembly));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

[assembly: TypeMap<UsedTypeMap>("TrimTargetIsTarget", typeof(TargetAndTrimTarget), typeof(TargetAndTrimTarget))]
[assembly: TypeMap<UsedTypeMap>("TrimTargetIsUnrelated", typeof(TargetType), typeof(TrimTarget))]
[assembly: TypeMap<UsedTypeMap>("DuplicateMappingWithDifferentTrimTargets", typeof(TargetType2), typeof(TrimTarget2))]
[assembly: TypeMap<UsedTypeMap>("DuplicateMappingWithDifferentTrimTargets", typeof(TargetType2), typeof(TrimTarget3))]
[assembly: TypeMap<UsedTypeMap>("TrimTargetIsUnreferenced", typeof(UnreferencedTargetType), typeof(UnreferencedTrimTarget))]
[assembly: TypeMapAssociation<UsedTypeMap>(typeof(SourceClass), typeof(ProxyType))]

Expand All @@ -34,7 +36,7 @@

IReadOnlyDictionary<string, Type> usedTypeMap = TypeMapping.GetOrCreateExternalTypeMapping<UsedTypeMap>();

if (!usedTypeMap.TryGetValue("TrimTargetIsTarget", out Type targetAndTrimTargetType))
if (!usedTypeMap.TryGetValue("TrimTargetIsTarget", out Type? targetAndTrimTargetType))
{
Console.WriteLine("TrimTargetIsTarget not found in used type map.");
return 1;
Expand All @@ -46,7 +48,7 @@
return 2;
}

if (!usedTypeMap.TryGetValue("TrimTargetIsUnrelated", out Type targetType))
if (!usedTypeMap.TryGetValue("TrimTargetIsUnrelated", out Type? targetType))
{
Console.WriteLine("TrimTargetIsUnrelated not found in used type map.");
return 3;
Expand All @@ -71,7 +73,7 @@
}

IReadOnlyDictionary<Type, Type> usedProxyTypeMap = TypeMapping.GetOrCreateProxyTypeMapping<UsedTypeMap>();
if (!usedProxyTypeMap.TryGetValue(typeof(SourceClass), out Type proxyType))
if (!usedProxyTypeMap.TryGetValue(typeof(SourceClass), out Type? proxyType))
{
Console.WriteLine("SourceClass not found in used proxy type map.");
return 7;
Expand All @@ -95,6 +97,12 @@
return 10;
}

if (!usedTypeMap.TryGetValue("DuplicateMappingWithDifferentTrimTargets", out Type? duplicatedTarget))
{
Console.WriteLine("Could not find duplicated target type");
return 11;
}

return 100;

[MethodImpl(MethodImplOptions.NoInlining)]
Expand All @@ -106,7 +114,10 @@ static Type GetTypeWithoutTrimAnalysis(string typeName)
class UsedTypeMap;
class TargetAndTrimTarget;
class TargetType;
class TargetType2;
class TrimTarget;
class TrimTarget2;
class TrimTarget3;
class UnreferencedTargetType;
class UnreferencedTrimTarget;
class SourceClass;
Expand Down
4 changes: 3 additions & 1 deletion src/tests/Interop/TypeMap/GroupTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public class I2<U> {}

public class TypicalUseCase { }

public class DuplicateTypeNameKey { }
public class ValidDuplicateTypeNameKey { }

public class InvalidDuplicateTypeNameKey { }

public class InvalidTypeNameKey { }

Expand Down
29 changes: 21 additions & 8 deletions src/tests/Interop/TypeMap/TypeMapApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,15 @@
[assembly: TypeMap<InvalidTypeNameKey>(null!, typeof(object))]
[assembly: TypeMapAssociation<InvalidTypeNameKey>(null!, typeof(object))]

[assembly: TypeMap<DuplicateTypeNameKey>("1", typeof(object))]
[assembly: TypeMap<DuplicateTypeNameKey>("1", typeof(object))]
[assembly: TypeMap<ValidDuplicateTypeNameKey>("1", typeof(object))]
[assembly: TypeMap<ValidDuplicateTypeNameKey>("1", typeof(object), typeof(object))]
[assembly: TypeMap<ValidDuplicateTypeNameKey>("1", typeof(object), typeof(string))]

[assembly: TypeMapAssociation<DuplicateTypeNameKey>(typeof(DupType_MapObject), typeof(object))]
[assembly: TypeMapAssociation<DuplicateTypeNameKey>(typeof(DupType_MapString), typeof(string))]
[assembly: TypeMap<InvalidDuplicateTypeNameKey>("1", typeof(object))]
[assembly: TypeMap<InvalidDuplicateTypeNameKey>("1", typeof(string))]

[assembly: TypeMapAssociation<InvalidDuplicateTypeNameKey>(typeof(DupType_MapObject), typeof(object))]
[assembly: TypeMapAssociation<InvalidDuplicateTypeNameKey>(typeof(DupType_MapString), typeof(string))]

// Redefine the same type as in the TypeMapLib2 assembly
// This is testing the duplicate type name key for the
Expand Down Expand Up @@ -178,19 +182,28 @@ public static void Validate_ProxyTypeMapping()
}

[Fact]
public static void Validate_ExternalTypeMapping_DuplicateTypeKey()
public static void Validate_ExternalTypeMapping_InvalidDuplicateTypeKey()
{
Console.WriteLine(nameof(Validate_ExternalTypeMapping_DuplicateTypeKey));
Console.WriteLine(nameof(Validate_ExternalTypeMapping_InvalidDuplicateTypeKey));

AssertExtensions.ThrowsAny<ArgumentException, BadImageFormatException>(() => TypeMapping.GetOrCreateExternalTypeMapping<DuplicateTypeNameKey>());
AssertExtensions.ThrowsAny<ArgumentException, BadImageFormatException>(() => TypeMapping.GetOrCreateExternalTypeMapping<InvalidDuplicateTypeNameKey>());
}

[Fact]
public static void Validate_ExternalTypeMapping_ValidDuplicateTypeKey()
{
Console.WriteLine(nameof(Validate_ExternalTypeMapping_ValidDuplicateTypeKey));

var mapping = TypeMapping.GetOrCreateExternalTypeMapping<ValidDuplicateTypeNameKey>();
Assert.Equal(typeof(object), mapping["1"]);
}
[Fact]
public static void Validate_ProxyTypeMapping_DuplicateTypeKey()
{
Console.WriteLine(nameof(Validate_ProxyTypeMapping_DuplicateTypeKey));

IReadOnlyDictionary<Type, Type> map = TypeMapping.GetOrCreateProxyTypeMapping<DuplicateTypeNameKey>();
// Invalid external mapping shouldn't impact proxy mapping
IReadOnlyDictionary<Type, Type> map = TypeMapping.GetOrCreateProxyTypeMapping<InvalidDuplicateTypeNameKey>();

Assert.Equal(typeof(object), map[typeof(DupType_MapObject)]);
Assert.Equal(typeof(string), map[typeof(DupType_MapString)]);
Expand Down
Loading