Skip to content

ILLink crashes with NotSupportedException on TypeMapAttribute with closed array Type arguments #127546

@simonrozsival

Description

@simonrozsival

Note

This issue was created with the help of Copilot.

Description

ILLink (Mono.Linker) crashes with System.NotSupportedException when an assembly contains [assembly: TypeMap<TGroup>(jniName, typeof(T[]), typeof(T[]))] (or any TypeMapAttribute whose Type argument is a closed array type). The same code works correctly without trimming and partially works under NativeAOT, so the failure is specific to the ILLink trimmer.

The crash happens in Mono.Linker.TypeMapHandler.RecordTypeMapEntry when it calls LinkContext.Resolve on the dependency-source slot (3-arg form) or in MarkTypeMapAttribute on the target-type slot (2-arg form). Cecil's ArrayType is not a TypeDefinition, so Resolve throws.

This blocks the design pattern where a TypeMap entry maps a JNI-style array name ([Ljava/lang/String;, [[Ljava/lang/String;) to the corresponding closed managed array type — a pattern we want to use in dotnet/android's trimmable typemap to eliminate ~2000 per-T JavaPeerContainerFactory<T> instantiations under NativeAOT (see dotnet/android#11234, dotnet/android#11238).

Reproduction

Program.cs:

using System.Runtime.InteropServices;

#pragma warning disable IL2026
[assembly: TypeMap<object>("Ljava/lang/String;",   typeof(string),     typeof(string))]
[assembly: TypeMap<object>("[Ljava/lang/String;",  typeof(string[]),   typeof(string[]))]
[assembly: TypeMap<object>("[[Ljava/lang/String;", typeof(string[][]), typeof(string[][]))]
#pragma warning restore IL2026

var tm = TypeMapping.GetOrCreateExternalTypeMapping<object>();

X(args);

Console.WriteLine(tm.TryGetValue("Ljava/lang/String;",   out var x1) ? x1 : "Not found");
Console.WriteLine(tm.TryGetValue("[Ljava/lang/String;",  out var x2) ? x2 : "Not found");
Console.WriteLine(tm.TryGetValue("[[Ljava/lang/String;", out var x3) ? x3 : "Not found");

void X(string[] args)
{
    foreach (var s in args)
    {
        Console.WriteLine(s);
    }
}

TestTypeMapArrays.csproj targets net11.0.

Without trimming — works

> dotnet run
System.String
System.String[]
System.String[][]

With ILLink trimming — crashes

> dotnet publish -p:PublishTrimmed=true
ILLink : error IL1012: IL Trimmer has encountered an unexpected error.
  Fatal error in IL Linker
  Unhandled exception. System.NotSupportedException: TypeDefinition cannot be resolved from 'Mono.Cecil.ArrayType' type
     at Mono.Linker.LinkContext.Resolve(TypeReference typeReference)
     at Mono.Linker.TypeMapHandler.RecordTypeMapEntry(ValueTuple`2 attr, TypeReference group, TypeReference dependencySource, …)
     at Mono.Linker.TypeMapHandler.AddExternalTypeMapEntry(TypeReference group, ValueTuple`2 attr)
     at Mono.Linker.TypeMapHandler.TypeMapResolver.Resolve(LinkContext context, TypeMapHandler manager)
     at Mono.Linker.TypeMapHandler.Initialize(…)
     at Mono.Linker.Steps.MarkStep.Initialize()
     at Mono.Linker.Steps.MarkStep.Process(LinkContext context)
     at Mono.Linker.Pipeline.Process(LinkContext context)
     at Mono.Linker.Driver.Run(ILogger customLogger)
NETSDK1144: Optimizing assemblies for size failed.

With NativeAOT — succeeds (2/3 entries kept; rank-2 dropped because not live)

> dotnet publish -p:PublishAot=true
> bin/Release/net11.0/osx-arm64/publish/TestTypeMapArrays
System.String
System.String[]
Not found

NativeAOT correctly keeps string and string[] (referenced by the parameter to X(string[] args)), and drops string[][] (not referenced anywhere) — exactly the conditional-trim behaviour the 3-arg form is meant to provide.

Expected behavior

ILLink should accept TypeMapAttribute arguments whose Type token is a closed array (SZARRAY or ARRAY element type). Specifically:

  • MarkTypeMapAttribute (target-type slot) and RecordTypeMapEntry (dependency-source slot) should not crash when the TypeReference is a Mono.Cecil.ArrayType.
  • Live-ness analysis should mirror NativeAOT: an array Type token is "live" iff its element type is reachable from user code (matches the rank-2 drop in the NativeAOT output above).

Environment

  • .NET SDK: 11.0.100-preview.3 (also reproduced on preview.4.26215.121)
  • Microsoft.NET.ILLink.Tasks: 11.0.0-preview.3.26209.109 / 11.0.0-preview.4.26215.121
  • OS: macOS (osx-arm64)

Suggested fix

In Mono.Linker.TypeMapHandler:

  • RecordTypeMapEntry: when dependencySource is an ArrayType, walk down to the element TypeReference and use that for the Resolve call (and the dependency tracking) — the array Type itself doesn't have a TypeDefinition, but its element does, and "element type is live" is the correct trim condition for typeof(T[]) to be kept.
  • MarkTypeMapAttribute: same treatment for the target-type slot — null-check after Resolve is already there, but Resolve throws before reaching it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-Tools-ILLink.NET linker development as well as trimming analyzers

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions