Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
13 changes: 6 additions & 7 deletions docs/design/features/typemap.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ Given the above types the following would take place.
`TypeMapAttribute` assembly attribute that declared the external type system name, a target
type, and optionally a "trim-target" to determine if the target
type should be included in the map. If the `TypeMapAttribute` constructor that doesn't
take a trim-target is used, the "target type" will be treated as the "trim-target".
take a trim-target is used the entry will always be emitted into the type map.

2. Types used in a managed-to-unmanaged interop operation would use `TypeMapAssociationAttribute`
to define a conditional link between the source and proxy type. In other words, if the
Expand Down Expand Up @@ -188,14 +188,12 @@ An entry in an External Type Map is included when the "trim target" type is refe
- The argument to the `isinst` IL instruction.
- The argument to the `castclass` IL instruction.
- The argument to the `box` instruction.
- If the trimming tool can determine that this box does not escape and could be stack allocated, it can ignore this `box` instruction and any corresponding `unbox` or `unbox.any` instructions.
- The argument to the `mkrefany` instruction.
- The argument to the `refanyval` instruction.
- The argument to the `newarr` instruction.
- The argument to the `ldobj` instruction.
- The argument to the `stobj` instruction.
- The argument to the `.constrained` instruction prefix.
- The type of a method argument to the `newobj` instruction.
- The owning type of the method argument to `call`, `callvirt`, `ldftn`, or `ldvirtftn`.
- The type of a method argument to the `newobj` instruction if it is a class type.
- The owning type of an instance method argument to `call` or `ldftn`, or the owning type of any method argument to `callvirt` or `ldvirtftn`.
- If the owning type is an interface and the trimming tool can determine that there is only one implementation of the interface, it is free to interpret the method token argument as though it is the method on the only implementing type.
- The generic argument to the `Activator.CreateInstance<T>` method.
- Calls to `Type.GetType` with a constant string representing the type name.
Expand All @@ -212,7 +210,6 @@ An entry in the Proxy Type Map is included when the "source type" is referenced
- The generic argument to the `Activator.CreateInstance<T>` method.
- The argument to the `box` instruction.
- The argument to the `newarr` instruction.
- The argument to the `.constrained` instruction prefix.
- The argument to the `mkrefany` instruction.
- The argument to the `refanyval` instruction.

Expand All @@ -221,3 +218,5 @@ If the type is an interface type and the user could possibly see a `RuntimeTypeH
- The argument to the `isinst` IL instruction.
- The argument to the `castclass` IL instruction.
- The owning type of the method argument to `callvirt`, or `ldvirtftn`.

Finally, if the trimming tool determines that it is impossible to retrieve a `System.Type` instance the represents the "source type" at runtime, then the entry may be omitted from the Proxy Type Map as its existence is unobservable.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ internal partial ParameterProxy GetParameter(ParameterIndex index)

internal partial bool HasGenericParameters() => Method.HasInstantiation;

internal partial bool HasGenericParametersCount(int genericParameterCount) => Method.Instantiation.Length == genericParameterCount;
internal partial bool HasGenericArgumentsCount(int genericArgumentCount) => Method.Instantiation.Length == genericArgumentCount;

internal partial ImmutableArray<GenericParameterProxy> GetGenericParameters()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,6 @@ private static void AddDependenciesDueToCustomAttributes(ref DependencyList depe
{
MethodDesc constructor = module.GetMethod(attribute.Constructor);

if (TypeMapManager.LookupTypeMapType(constructor.OwningType) != TypeMapManager.TypeMapAttributeKind.None)
continue;

if (!mdManager.GeneratesAttributeMetadata(constructor.OwningType))
continue;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,29 @@ public override IEnumerable<CombinedDependencyListEntry> GetConditionalStaticDep
foreach (var entry in _mapEntries)
{
var (targetType, trimmingTargetType) = entry.Value;
yield return new CombinedDependencyListEntry(
context.MaximallyConstructableType(targetType),
context.NecessaryTypeSymbol(trimmingTargetType),
"Type in external type map is cast target");
if (trimmingTargetType is not null)
{
yield return new CombinedDependencyListEntry(
context.MaximallyConstructableType(targetType),
context.NecessaryTypeSymbol(trimmingTargetType),
"Type in external type map is cast target");
}
}
}

public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context) => [];
public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory context)
{
foreach (var entry in _mapEntries)
{
var (targetType, trimmingTargetType) = entry.Value;
if (trimmingTargetType is null)
{
yield return new DependencyListEntry(
context.MaximallyConstructableType(targetType),
"External type map entry target type");
}
}
}

public override IEnumerable<CombinedDependencyListEntry> SearchDynamicDependencies(List<DependencyNodeCore<NodeFactory>> markedNodes, int firstNode, NodeFactory context) => Array.Empty<CombinedDependencyListEntry>();
protected override string GetName(NodeFactory context) => $"External type map: {TypeMapGroup}";
Expand All @@ -62,7 +77,8 @@ public int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
var (targetType, trimmingTargetType) = entry.Value;

if (factory.NecessaryTypeSymbol(trimmingTargetType).Marked)
if (trimmingTargetType is null
|| factory.NecessaryTypeSymbol(trimmingTargetType).Marked)
{
IEETypeNode targetNode = factory.MaximallyConstructableType(targetType);
Debug.Assert(targetNode.Marked);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ void ProcessTypeMapAttribute(CustomAttributeValue<TypeDesc> attrValue, TypeDesc
{
typeMapStates[typeMapGroup] = typeMapState = new Map(typeMapGroup);
}
typeMapState.AddExternalTypeMapEntry(typeName, targetType, targetType);
typeMapState.AddExternalTypeMapEntry(typeName, targetType, null);
break;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
<linker>
<!-- Type Map attributes will be preserved by NativeAOT or the trimmer when necessary. -->
<assembly fullname="*">
<type fullname="System.Runtime.InteropServices.TypeMapAttribute`1">
<attribute internal="RemoveAttributeInstances" />
</type>
<type fullname="System.Runtime.InteropServices.TypeMapAssociationAttribute`1">
<attribute internal="RemoveAttributeInstances" />
</type>
<type fullname="System.Runtime.InteropServices.TypeMapAssemblyTargetAttribute`1">
<attribute internal="RemoveAttributeInstances" />
</type>
</assembly>
<!-- The following attributes are only necessary when debugging is supported -->
<assembly fullname="System.Private.CoreLib" feature="System.Diagnostics.Debugger.IsSupported" featurevalue="false">
<type fullname="System.Diagnostics.DebuggableAttribute">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ internal readonly partial struct MethodProxy

internal partial bool HasGenericParameters() => Method.IsGenericMethod;

internal partial bool HasGenericParametersCount(int genericParameterCount) => Method.TypeParameters.Length == genericParameterCount;
internal partial bool HasGenericArgumentsCount(int genericArgumentCount) => Method.TypeArguments.Length == genericArgumentCount;

internal partial ImmutableArray<GenericParameterProxy> GetGenericParameters()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,13 +422,13 @@ public static IntrinsicId GetIntrinsicIdForMethod(MethodProxy calledMethod)
// static System.Runtime.InteropServices.TypeMapping.GetOrCreateExternalTypeMapping<T> ()
"GetOrCreateExternalTypeMapping" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices.TypeMapping")
&& calledMethod.IsStatic()
&& calledMethod.HasGenericParametersCount(1)
&& calledMethod.HasGenericArgumentsCount(1)
=> IntrinsicId.TypeMapping_GetOrCreateExternalTypeMapping,

// static System.Runtime.InteropServices.TypeMapping.GetOrCreateProxyTypeMapping<T> ()
"GetOrCreateProxyTypeMapping" when calledMethod.IsDeclaredOnType("System.Runtime.InteropServices.TypeMapping")
&& calledMethod.IsStatic()
&& calledMethod.HasGenericParametersCount(1)
&& calledMethod.HasGenericArgumentsCount(1)
=> IntrinsicId.TypeMapping_GetOrCreateProxyTypeMapping,

_ => IntrinsicId.None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ namespace ILLink.Shared.TypeSystemProxy
internal bool HasParameterOfType(ParameterIndex parameterIndex, string fullTypeName)
=> (int)parameterIndex < GetParametersCount() && GetParameter(parameterIndex).IsTypeOf(fullTypeName);
internal partial bool HasGenericParameters();
internal partial bool HasGenericParametersCount(int genericParameterCount);
internal partial ImmutableArray<GenericParameterProxy> GetGenericParameters();
internal partial bool HasGenericArgumentsCount(int genericArgumentCount);
internal partial bool IsConstructor();
internal partial bool IsStatic();
internal partial bool HasImplicitThis();
Expand Down
12 changes: 10 additions & 2 deletions src/tools/illink/src/linker/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
Expand Down Expand Up @@ -869,6 +869,14 @@
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Mono.Linker.AnnotationStore.TryGetPreservedMembers(Mono.Cecil.TypeDefinition,Mono.Linker.TypePreserveMembers@)</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Mono.Linker.AnnotationStore.SetEntryPointAssembly(Mono.Cecil.AssemblyDefinition)</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Mono.Linker.AnnotationStore.GetEntryPointAssembly</Target>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Mono.Linker.LinkContext.#ctor(Mono.Linker.Pipeline,Mono.Linker.ILogger,System.String,Mono.Linker.UnintializedContextFactory)</Target>
Expand Down Expand Up @@ -1561,4 +1569,4 @@
<DiagnosticId>CP0017</DiagnosticId>
<Target>M:Mono.Linker.LinkContext.Resolve(Mono.Cecil.AssemblyNameReference)$0</Target>
</Suppression>
</Suppressions>
</Suppressions>
21 changes: 21 additions & 0 deletions src/tools/illink/src/linker/Linker.Dataflow/HandleCallAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,27 @@ private partial bool TryHandleIntrinsic(
}
break;

case IntrinsicId.TypeMapping_GetOrCreateExternalTypeMapping:
case IntrinsicId.TypeMapping_GetOrCreateProxyTypeMapping:
{
GenericInstanceMethod method = ((GenericInstanceMethod)calledMethod.Method);
if (method.GenericArguments[0].ContainsGenericParameter)
{
_diagnosticContext.AddDiagnostic(DiagnosticId.TypeMapGroupTypeCannotBeStaticallyDetermined, method.GenericArguments[0].FullName);
return true;
}

if (intrinsicId == IntrinsicId.TypeMapping_GetOrCreateExternalTypeMapping)
{
_markStep.TypeMapHandler.ProcessExternalTypeMapGroupSeen(_callingMethodDefinition, method.GenericArguments[0]);
}
else
{
_markStep.TypeMapHandler.ProcessProxyTypeMapGroupSeen(_callingMethodDefinition, method.GenericArguments[0]);
}
}
break;

// Note about Activator.CreateInstance<T>
// There are 2 interesting cases:
// - The generic argument for T is either specific type or annotated - in that case generic instantiation will handle this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ internal partial ParameterProxyEnumerable GetParameters()

internal partial bool HasGenericParameters() => Method.HasGenericParameters;

internal partial bool HasGenericParametersCount(int genericParameterCount) => Method.GenericParameters.Count == genericParameterCount;
internal partial bool HasGenericArgumentsCount(int genericArgumentCount) => Method is GenericInstanceMethod generic && generic.GenericArguments.Count == genericArgumentCount;

internal partial ImmutableArray<GenericParameterProxy> GetGenericParameters()
{
Expand Down
Loading
Loading