Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
bafa7e5
[Mono.Android] Trimmable typemap: fix JavaCast/JavaAs behavior
simonrozsival Apr 28, 2026
a804994
[TrimmableTypeMap] Scanner fixes: invoker ctor, keyword types, array …
simonrozsival Apr 28, 2026
c5062a6
[Tests] Re-enable 7 trimmable typemap tests
simonrozsival Apr 28, 2026
bd207f4
[TrimmableTypeMap] Generator: emit per-rank array TypeMap entries
simonrozsival Apr 29, 2026
956a999
[TrimmableTypeMap] Runtime: array typemap fork on IsDynamicCodeSupported
simonrozsival Apr 29, 2026
b12791a
[TrimmableTypeMap] Address PR feedback
simonrozsival Apr 29, 2026
ce92d44
[TrimmableTypeMap] Trim verbose doc comments
simonrozsival Apr 29, 2026
b19a2ce
[TrimmableTypeMap] Wire up merged-universe + array entries path
simonrozsival Apr 29, 2026
e29ca74
[TrimmableTypeMap] Tighten comments in shared+arrays IL emit
simonrozsival Apr 29, 2026
90b0b8f
[TrimmableTypeMap] Reject multi-dim arrays in TryGetArrayType
simonrozsival Apr 29, 2026
86eaeff
[TrimmableTypeMap] Inline rank sentinel name generation
simonrozsival Apr 29, 2026
6c814ce
[TrimmableTypeMap] Drop CompositeStringTypeReadOnlyDictionary
simonrozsival Apr 29, 2026
f4f288a
[TrimmableTypeMap] Use shared rank anchors in Mono.Android
simonrozsival Apr 29, 2026
1df33a7
[TrimmableTypeMap] Simplify TryGetArrayType
simonrozsival Apr 29, 2026
b53a07b
[TrimmableTypeMap] Unify Initialize IL emit paths
simonrozsival Apr 29, 2026
f4564df
[TrimmableTypeMap] Two small simplifications
simonrozsival Apr 29, 2026
8e95788
[TrimmableTypeMap] Fold model setup into a single object initializer
simonrozsival Apr 29, 2026
af650a5
[TrimmableTypeMap] Drop stale 'TypeDef was emitted' check + message
simonrozsival Apr 29, 2026
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 @@ -40,6 +40,12 @@ sealed class TypeMapAssemblyData
/// </summary>
public List<AliasHolderData> AliasHolders { get; } = new ();

/// <summary>
/// Maximum array rank for which the generator emits per-rank <c>__ArrayMapRank{N}</c>
/// sentinel TypeDefs and <c>TypeMap</c> entries. 0 disables.
/// </summary>
public int MaxArrayRank { get; set; }

/// <summary>
/// Assembly names that need [IgnoresAccessChecksTo] for cross-assembly n_* calls.
/// </summary>
Expand Down Expand Up @@ -77,6 +83,12 @@ sealed record TypeMapAttributeData
/// True for 2-arg unconditional entries (ACW types, essential runtime types).
/// </summary>
public bool IsUnconditional => TargetTypeReference == null;

/// <summary>
/// 1-based array rank when this entry should use a <c>__ArrayMapRank{value}</c>
/// sentinel as its <c>TGroup</c> instead of the default model anchor.
/// </summary>
public int? AnchorRank { get; init; }
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,28 @@ static class ModelBuilder
/// <param name="peers">Scanned Java peer types (typically from a single input assembly).</param>
/// <param name="outputPath">Output .dll path — used to derive assembly/module names if not specified.</param>
/// <param name="assemblyName">Explicit assembly name. If null, derived from <paramref name="outputPath"/>.</param>
public static TypeMapAssemblyData Build (IReadOnlyList<JavaPeerInfo> peers, string outputPath, string? assemblyName = null)
/// <param name="maxArrayRank">
/// Emit per-rank array <c>TypeMap</c> entries + <c>__ArrayMapRank{N}</c> sentinels
/// for ranks 1..<paramref name="maxArrayRank"/>. 0 disables array entry emission.
/// </param>
public static TypeMapAssemblyData Build (IReadOnlyList<JavaPeerInfo> peers, string outputPath, string? assemblyName = null, int maxArrayRank = 0)
{
if (peers is null) {
throw new ArgumentNullException (nameof (peers));
}
if (outputPath is null) {
throw new ArgumentNullException (nameof (outputPath));
}
if (maxArrayRank < 0) {
throw new ArgumentOutOfRangeException (nameof (maxArrayRank), maxArrayRank, "Must be >= 0.");
}

assemblyName ??= Path.GetFileNameWithoutExtension (outputPath);
string moduleName = Path.GetFileName (outputPath);

var model = new TypeMapAssemblyData {
AssemblyName = assemblyName,
ModuleName = moduleName,
ModuleName = Path.GetFileName (outputPath),
MaxArrayRank = maxArrayRank,
};

// Invoker types are NOT emitted as separate proxies or TypeMap entries —
Expand Down Expand Up @@ -89,6 +96,10 @@ public static TypeMapAssemblyData Build (IReadOnlyList<JavaPeerInfo> peers, stri
}

EmitPeers (model, jniName, peersForName, assemblyName, usedProxyNames);

if (maxArrayRank > 0) {
EmitArrayEntries (model, jniName, peersForName, maxArrayRank);
}
}

// Compute IgnoresAccessChecksTo from cross-assembly references
Expand Down Expand Up @@ -392,4 +403,54 @@ static TypeMapAttributeData BuildEntry (JavaPeerInfo peer, JavaPeerProxyData? pr

static string AssemblyQualify (string typeName, string assemblyName)
=> $"{typeName}, {assemblyName}";

/// <summary>
/// Emits per-rank <c>[L&lt;jni&gt;;</c>-shaped TypeMap entries for one peer, anchored to
/// the per-assembly <c>__ArrayMapRank{N}</c> sentinels. Skips open generics, primitive
/// JNI keyword keys (handled by the legacy primitive-array path), and alias groups.
Comment on lines +408 to +410
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 💡 Documentation — The doc comment says "[L<jni>;-shaped TypeMap entries", but the design choice here is specifically to use the bare element JNI name as the key (no [L...; wrapping). The test Build_EmitArrayEntries_KeyIsElementJniName documents this explicitly. Consider rewording to avoid confusion:

Suggested change
/// Emits per-rank <c>[L&lt;jni&gt;;</c>-shaped TypeMap entries for one peer, anchored to
/// the per-assembly <c>__ArrayMapRank{N}</c> sentinels. Skips open generics, primitive
/// JNI keyword keys (handled by the legacy primitive-array path), and alias groups.
/// Emits per-rank array TypeMap entries for one peer, anchored to
/// the per-assembly <c>__ArrayMapRank{N}</c> sentinels. Keys are bare element JNI names
/// (rank is encoded by the sentinel anchor, not by JNI array prefixes). Skips open generics,
/// primitive JNI keyword keys (handled by the legacy primitive-array path), and alias groups.

Rule: Comments explain "why", not "what" — and shouldn't contradict the implementation

/// </summary>
static void EmitArrayEntries (TypeMapAssemblyData model, string jniName, List<JavaPeerInfo> peersForName, int maxArrayRank)
{
if (jniName.Length == 1 && IsJniPrimitiveKeyword (jniName [0])) {
return;
}
if (peersForName.Count != 1) {
return;
}

var peer = peersForName [0];
if (peer.IsGenericDefinition) {
return;
}

for (int rank = 1; rank <= maxArrayRank; rank++) {
string arrayTypeRef = AssemblyQualify (peer.ManagedTypeName + Brackets (rank), peer.AssemblyName);
model.Entries.Add (new TypeMapAttributeData {
JniName = jniName,
ProxyTypeReference = arrayTypeRef,
TargetTypeReference = arrayTypeRef,
AnchorRank = rank,
});
}
}

static string Brackets (int rank) => rank switch {
1 => "[]",
2 => "[][]",
3 => "[][][]",
_ => BuildBrackets (rank),
};

static string BuildBrackets (int rank)
{
var sb = new StringBuilder (rank * 2);
for (int i = 0; i < rank; i++) {
sb.Append ("[]");
}
return sb.ToString ();
}

static bool IsJniPrimitiveKeyword (char c)
=> c == 'Z' || c == 'B' || c == 'C' || c == 'S' || c == 'I'
|| c == 'J' || c == 'F' || c == 'D' || c == 'V';
}
Loading