Skip to content
Merged
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 @@ -10,7 +10,7 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap;
/// <summary>
/// Generates the root <c>_Microsoft.Android.TypeMaps.dll</c> assembly that:
/// <list type="bullet">
/// <item>References all per-assembly typemap assemblies via <c>[assembly: TypeMapAssemblyTargetAttribute&lt;__TypeMapAnchor&gt;("name")]</c>.</item>
/// <item>References all per-assembly typemap assemblies via <c>[assembly: TypeMapAssemblyTargetAttribute&lt;T&gt;("name")]</c>.</item>
/// <item>Emits a <c>TypeMapLoader</c> class whose <c>Initialize()</c> method calls
/// <see cref="Microsoft.Android.Runtime.TrimmableTypeMap.Initialize"/> with the appropriate
/// type mapping dictionaries.</item>
Expand All @@ -36,7 +36,7 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap;
/// TrimmableTypeMap.Initialize(
/// TypeMapping.GetOrCreateExternalTypeMapping&lt;Java.Lang.Object&gt;(),
/// TypeMapping.GetOrCreateProxyTypeMapping&lt;Java.Lang.Object&gt;(),
/// arrayMapsByRank);
/// arrayMapsByAssemblyAndRank);
///
/// // Option B: Per-assembly universes (aggregated)
/// var typeMaps = new IReadOnlyDictionary&lt;string, Type&gt;[] {
Expand All @@ -47,7 +47,7 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap;
/// TypeMapping.GetOrCreateProxyTypeMapping&lt;_Mono_Android_TypeMap.__TypeMapAnchor&gt;(),
/// TypeMapping.GetOrCreateProxyTypeMapping&lt;_MyApp_TypeMap.__TypeMapAnchor&gt;(),
/// };
/// TrimmableTypeMap.Initialize(typeMaps, proxyMaps, arrayMapsByRank);
/// TrimmableTypeMap.Initialize(typeMaps, proxyMaps, arrayMapsByAssemblyAndRank);
/// }
/// }
/// }
Expand Down Expand Up @@ -76,7 +76,7 @@ public RootTypeMapAssemblyGenerator (Version systemRuntimeVersion)
/// <param name="maxArrayRank">
/// Maximum array rank for which per-assembly typemaps emitted <c>__ArrayMapRank{N}</c>
/// sentinels. Must match the value passed to the per-assembly generators. 0 means
/// no array sentinels were emitted; the loader passes <c>null</c> for <c>arrayMapsByRank</c>.
/// no array sentinels were emitted; the loader passes <c>null</c> for array maps.
/// </param>
public void Generate (IReadOnlyList<string> perAssemblyTypeMapNames, bool useSharedTypemapUniverse, Stream stream, string? assemblyName = null, string? moduleName = null, int maxArrayRank = 0)
{
Expand Down Expand Up @@ -121,13 +121,13 @@ public void Generate (IReadOnlyList<string> perAssemblyTypeMapNames, bool useSha
} else {
EmitPerAssemblyUniverseAssemblyTargetAttributes (pe, perAssemblyTypeMapNames);
}
EmitArrayAssemblyTargetAttributes (pe, perAssemblyTypeMapNames, maxArrayRank);

// Emit [assembly: IgnoresAccessChecksTo("...")] so TypeMapLoader.Initialize() can access
// internal types (TrimmableTypeMap and friends in Mono.Android, and __TypeMapAnchor
// in each per-assembly typemap DLL when in aggregate mode). Shared rank anchors
// (__ArrayMapRank{N}) live in Mono.Android already.
// internal types (TrimmableTypeMap and friends in Mono.Android, and private anchors
// in each per-assembly typemap DLL when aggregate universes or array maps are used).
var accessTargets = new List<string> { "Mono.Android" };
if (!useSharedTypemapUniverse) {
if (!useSharedTypemapUniverse || maxArrayRank > 0) {
accessTargets.AddRange (perAssemblyTypeMapNames);
}
pe.EmitIgnoresAccessChecksToAttribute (accessTargets);
Expand Down Expand Up @@ -159,6 +159,24 @@ static void EmitPerAssemblyUniverseAssemblyTargetAttributes (PEAssemblyBuilder p
}
}

static void EmitArrayAssemblyTargetAttributes (PEAssemblyBuilder pe, IReadOnlyList<string> perAssemblyTypeMapNames, int maxArrayRank)
{
if (maxArrayRank == 0) {
return;
}

var openAttrRef = GetTypeMapAssemblyTargetAttributeRef (pe);
foreach (var name in perAssemblyTypeMapNames) {
var asmRef = pe.FindOrAddAssemblyRef (name);
for (int rank = 1; rank <= maxArrayRank; rank++) {
var rankAnchorRef = pe.Metadata.AddTypeReference (asmRef,
default, pe.Metadata.GetOrAddString ($"__ArrayMapRank{rank}"));
var ctorRef = GetTypeMapAssemblyTargetAttributeCtorRef (pe, openAttrRef, rankAnchorRef);
EmitAssemblyTargetAttribute (pe, ctorRef, name);
}
}
}

static TypeReferenceHandle GetTypeMapAssemblyTargetAttributeRef (PEAssemblyBuilder pe)
{
return pe.Metadata.AddTypeReference (pe.SystemRuntimeInteropServicesRef,
Expand Down Expand Up @@ -217,29 +235,31 @@ static void EmitTypeMapLoader (PEAssemblyBuilder pe, EntityHandle anchorTypeHand
MetadataTokens.MethodDefinitionHandle (metadata.GetRowCount (TableIndex.MethodDef) + 1));

var externalDictTypeSpec = MakeIReadOnlyDictTypeSpec (pe, iReadOnlyDictOpenRef, systemTypeRef, keyIsString: true);
var externalDictArrayTypeSpec = MakeIReadOnlyDictArrayTypeSpec (pe, iReadOnlyDictOpenRef, systemTypeRef, keyIsString: true);

if (useSharedTypemapUniverse) {
var initializeRef = AddInitializeSingleWithArraysRef (pe, trimmableTypeMapRef, iReadOnlyDictOpenRef, systemTypeRef);
EmitInitializeWithSingleTypeMap (pe, anchorTypeHandle, getExternalMemberRef, getProxyMemberRef,
initializeRef, externalDictTypeSpec, maxArrayRank);
initializeRef, externalDictTypeSpec, externalDictArrayTypeSpec, perAssemblyTypeMapNames, maxArrayRank);
} else {
var initializeRef = AddInitializeAggregateWithArraysRef (pe, trimmableTypeMapRef, iReadOnlyDictOpenRef, systemTypeRef);
var proxyDictTypeSpec = MakeIReadOnlyDictTypeSpec (pe, iReadOnlyDictOpenRef, systemTypeRef, keyIsString: false);
EmitInitializeWithAggregateTypeMap (pe, perAssemblyTypeMapNames, getExternalMemberRef, getProxyMemberRef,
initializeRef, externalDictTypeSpec, proxyDictTypeSpec, iReadOnlyDictOpenRef, systemTypeRef, maxArrayRank);
initializeRef, externalDictTypeSpec, proxyDictTypeSpec, externalDictArrayTypeSpec, iReadOnlyDictOpenRef, systemTypeRef, maxArrayRank);
}
}

/// <summary>
/// Aggregate IL emit. Builds <c>typeMaps[N]</c>, <c>proxyMaps[N]</c>, and either a
/// flat <c>arrayMapsByRank[maxArrayRank]</c> from shared <c>__ArrayMapRank{N}</c>
/// anchors or <c>null</c> when <paramref name="maxArrayRank"/> is 0.
/// Aggregate IL emit. Builds <c>typeMaps[N]</c>, <c>proxyMaps[N]</c>, and either
/// <c>arrayMapsByAssemblyAndRank[N][maxArrayRank]</c> from per-assembly
/// <c>__ArrayMapRank{N}</c> anchors or <c>null</c> when <paramref name="maxArrayRank"/> is 0.
/// </summary>
static void EmitInitializeWithAggregateTypeMap (PEAssemblyBuilder pe,
IReadOnlyList<string> perAssemblyTypeMapNames,
MemberReferenceHandle getExternalMemberRef, MemberReferenceHandle getProxyMemberRef,
MemberReferenceHandle initializeRef,
TypeSpecificationHandle externalDictTypeSpec, TypeSpecificationHandle proxyDictTypeSpec,
TypeSpecificationHandle externalDictArrayTypeSpec,
TypeReferenceHandle iReadOnlyDictOpenRef, TypeReferenceHandle systemTypeRef,
int maxArrayRank)
{
Expand Down Expand Up @@ -267,10 +287,10 @@ static void EmitInitializeWithAggregateTypeMap (PEAssemblyBuilder pe,
EmitNewArrayLocal (encoder, count, proxyDictTypeSpec, slot: 1);
EmitFillArrayLocal (encoder, count, getProxySpecs, slot: 1);

// TrimmableTypeMap.Initialize(typeMaps, proxyMaps, arrayMapsByRank-or-null)
// TrimmableTypeMap.Initialize(typeMaps, proxyMaps, arrayMapsByAssemblyAndRank-or-null)
encoder.LoadLocal (0);
encoder.LoadLocal (1);
EmitArrayMapsByRankOrNull (pe, encoder, getExternalMemberRef, externalDictTypeSpec, maxArrayRank);
EmitArrayMapsByAssemblyAndRankOrNull (pe, encoder, perAssemblyTypeMapNames, getExternalMemberRef, externalDictTypeSpec, externalDictArrayTypeSpec, maxArrayRank);
encoder.OpCode (ILOpCode.Call);
encoder.Token (initializeRef);
encoder.OpCode (ILOpCode.Ret);
Expand Down Expand Up @@ -306,7 +326,7 @@ static void EmitFillArrayLocal (InstructionEncoder encoder, int count, EntityHan
}
}

/// <summary>MemberRef for <c>TrimmableTypeMap.Initialize(typeMaps[], proxyMaps[], arrayMapsByRank[])</c>.</summary>
/// <summary>MemberRef for <c>TrimmableTypeMap.Initialize(typeMaps[], proxyMaps[], arrayMapsByAssemblyAndRank[][])</c>.</summary>
static MemberReferenceHandle AddInitializeAggregateWithArraysRef (PEAssemblyBuilder pe, TypeReferenceHandle trimmableTypeMapRef,
TypeReferenceHandle iReadOnlyDictOpenRef, TypeReferenceHandle systemTypeRef)
{
Expand All @@ -320,7 +340,8 @@ static MemberReferenceHandle AddInitializeAggregateWithArraysRef (PEAssemblyBuil
// Param 2: IReadOnlyDictionary<Type, Type>[]
blob.WriteByte (0x1D);
EncodeIReadOnlyDictType (blob, iReadOnlyDictOpenRef, systemTypeRef, keyIsString: false);
// Param 3: IReadOnlyDictionary<string, Type>?[]
// Param 3: IReadOnlyDictionary<string, Type>?[][]
blob.WriteByte (0x1D);
blob.WriteByte (0x1D);
EncodeIReadOnlyDictType (blob, iReadOnlyDictOpenRef, systemTypeRef, keyIsString: true);
return pe.Metadata.AddMemberReference (trimmableTypeMapRef,
Expand All @@ -329,13 +350,14 @@ static MemberReferenceHandle AddInitializeAggregateWithArraysRef (PEAssemblyBuil

/// <summary>
/// Shared-universe IL emit. Single merged main map (anchored on <c>Java.Lang.Object</c>)
/// plus either a flat <c>arrayMapsByRank[maxArrayRank]</c> from shared
/// plus either <c>arrayMapsByAssemblyAndRank[N][maxArrayRank]</c> from per-assembly
/// <c>__ArrayMapRank{N}</c> anchors or <c>null</c> when <paramref name="maxArrayRank"/> is 0.
/// </summary>
static void EmitInitializeWithSingleTypeMap (PEAssemblyBuilder pe, EntityHandle anchorTypeHandle,
MemberReferenceHandle getExternalMemberRef, MemberReferenceHandle getProxyMemberRef,
MemberReferenceHandle initializeRef,
TypeSpecificationHandle externalDictTypeSpec,
TypeSpecificationHandle externalDictTypeSpec, TypeSpecificationHandle externalDictArrayTypeSpec,
IReadOnlyList<string> perAssemblyTypeMapNames,
int maxArrayRank)
{
var getExternalSpec = MakeGenericMethodSpec (pe, getExternalMemberRef, anchorTypeHandle);
Expand All @@ -345,19 +367,19 @@ static void EmitInitializeWithSingleTypeMap (PEAssemblyBuilder pe, EntityHandle
MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig,
sig => sig.MethodSignature ().Parameters (0, rt => rt.Void (), p => { }),
encoder => {
// TrimmableTypeMap.Initialize(GetExternal<JL.Object>(), GetProxy<JL.Object>(), arrayMapsByRank-or-null)
// TrimmableTypeMap.Initialize(GetExternal<JL.Object>(), GetProxy<JL.Object>(), arrayMapsByAssemblyAndRank-or-null)
encoder.OpCode (ILOpCode.Call);
encoder.Token (getExternalSpec);
encoder.OpCode (ILOpCode.Call);
encoder.Token (getProxySpec);
EmitArrayMapsByRankOrNull (pe, encoder, getExternalMemberRef, externalDictTypeSpec, maxArrayRank);
EmitArrayMapsByAssemblyAndRankOrNull (pe, encoder, perAssemblyTypeMapNames, getExternalMemberRef, externalDictTypeSpec, externalDictArrayTypeSpec, maxArrayRank);
encoder.OpCode (ILOpCode.Call);
encoder.Token (initializeRef);
encoder.OpCode (ILOpCode.Ret);
});
}

/// <summary>MemberRef for <c>TrimmableTypeMap.Initialize(typeMap, proxyMap, arrayMapsByRank[])</c>.</summary>
/// <summary>MemberRef for <c>TrimmableTypeMap.Initialize(typeMap, proxyMap, arrayMapsByAssemblyAndRank[][])</c>.</summary>
static MemberReferenceHandle AddInitializeSingleWithArraysRef (PEAssemblyBuilder pe, TypeReferenceHandle trimmableTypeMapRef,
TypeReferenceHandle iReadOnlyDictOpenRef, TypeReferenceHandle systemTypeRef)
{
Expand All @@ -368,29 +390,49 @@ static MemberReferenceHandle AddInitializeSingleWithArraysRef (PEAssemblyBuilder
EncodeIReadOnlyDictType (blob, iReadOnlyDictOpenRef, systemTypeRef, keyIsString: true);
EncodeIReadOnlyDictType (blob, iReadOnlyDictOpenRef, systemTypeRef, keyIsString: false);
blob.WriteByte (0x1D);
blob.WriteByte (0x1D);
EncodeIReadOnlyDictType (blob, iReadOnlyDictOpenRef, systemTypeRef, keyIsString: true);
return pe.Metadata.AddMemberReference (trimmableTypeMapRef,
pe.Metadata.GetOrAddString ("Initialize"), pe.Metadata.GetOrAddBlob (blob));
}

/// <summary>
/// Emits IL that pushes either a fresh <c>IReadOnlyDictionary&lt;string, Type&gt;?[maxArrayRank]</c>
/// Emits IL that pushes either a fresh
/// <c>IReadOnlyDictionary&lt;string, Type&gt;?[assemblyCount][maxArrayRank]</c>
/// (when <paramref name="maxArrayRank"/> &gt; 0) or <c>ldnull</c>.
/// </summary>
static void EmitArrayMapsByRankOrNull (PEAssemblyBuilder pe, InstructionEncoder encoder,
MemberReferenceHandle getExternalMemberRef, TypeSpecificationHandle externalDictTypeSpec, int maxArrayRank)
static void EmitArrayMapsByAssemblyAndRankOrNull (PEAssemblyBuilder pe, InstructionEncoder encoder,
IReadOnlyList<string> perAssemblyTypeMapNames,
MemberReferenceHandle getExternalMemberRef,
TypeSpecificationHandle externalDictTypeSpec, TypeSpecificationHandle externalDictArrayTypeSpec,
int maxArrayRank)
{
if (maxArrayRank == 0) {
encoder.OpCode (ILOpCode.Ldnull);
return;
}

var monoAndroidRuntimeNs = pe.Metadata.GetOrAddString ("Microsoft.Android.Runtime");
encoder.LoadConstantI4 (perAssemblyTypeMapNames.Count);
encoder.OpCode (ILOpCode.Newarr);
encoder.Token (externalDictArrayTypeSpec);
for (int i = 0; i < perAssemblyTypeMapNames.Count; i++) {
var asmRef = pe.FindOrAddAssemblyRef (perAssemblyTypeMapNames [i]);
encoder.OpCode (ILOpCode.Dup);
encoder.LoadConstantI4 (i);
EmitArrayMapsByRank (pe, encoder, asmRef, getExternalMemberRef, externalDictTypeSpec, maxArrayRank);
encoder.OpCode (ILOpCode.Stelem_ref);
}
}

static void EmitArrayMapsByRank (PEAssemblyBuilder pe, InstructionEncoder encoder,
AssemblyReferenceHandle assemblyRef,
MemberReferenceHandle getExternalMemberRef, TypeSpecificationHandle externalDictTypeSpec, int maxArrayRank)
{
encoder.LoadConstantI4 (maxArrayRank);
encoder.OpCode (ILOpCode.Newarr);
encoder.Token (externalDictTypeSpec);
for (int r = 0; r < maxArrayRank; r++) {
var rankRef = pe.Metadata.AddTypeReference (pe.MonoAndroidRef, monoAndroidRuntimeNs,
var rankRef = pe.Metadata.AddTypeReference (assemblyRef, default,
pe.Metadata.GetOrAddString ($"__ArrayMapRank{r + 1}"));
var rankSpec = MakeGenericMethodSpec (pe, getExternalMemberRef, rankRef);
encoder.OpCode (ILOpCode.Dup);
Expand Down Expand Up @@ -443,6 +485,18 @@ static TypeSpecificationHandle MakeIReadOnlyDictTypeSpec (PEAssemblyBuilder pe,
return pe.Metadata.AddTypeSpecification (pe.Metadata.GetOrAddBlob (blob));
}

/// <summary>
/// Creates a TypeSpec for <c>IReadOnlyDictionary&lt;K, V&gt;[]</c> (for jagged-array <c>newarr</c>).
/// </summary>
static TypeSpecificationHandle MakeIReadOnlyDictArrayTypeSpec (PEAssemblyBuilder pe,
TypeReferenceHandle iReadOnlyDictOpenRef, TypeReferenceHandle systemTypeRef, bool keyIsString)
{
var blob = new BlobBuilder (32);
blob.WriteByte (0x1D);
EncodeIReadOnlyDictType (blob, iReadOnlyDictOpenRef, systemTypeRef, keyIsString);
return pe.Metadata.AddTypeSpecification (pe.Metadata.GetOrAddBlob (blob));
}

static void EncodeIReadOnlyDictType (BlobBuilder blob, TypeReferenceHandle iReadOnlyDictOpenRef, TypeReferenceHandle systemTypeRef, bool keyIsString)
{
blob.WriteByte (0x15); // ELEMENT_TYPE_GENERICINST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,9 @@ void EmitAnchorType ()
}

/// <summary>
/// Populates <c>_rankAnchorHandles</c> with TypeRefs to the shared
/// <c>Microsoft.Android.Runtime.__ArrayMapRank{N}</c> types in Mono.Android. All per-asm
/// typemap DLLs reference the same anchors so each rank's entries merge into one dict
/// at runtime via <c>TypeMapping.GetOrCreateExternalTypeMapping&lt;__ArrayMapRank{N}&gt;()</c>.
/// Emits private <c>__ArrayMapRank{N}</c> classes used as group type parameters
/// for array <c>TypeMap&lt;T&gt;</c> entries. Each per-assembly typemap owns its
/// rank anchors so array maps stay scoped to the generated assembly.
/// </summary>
void EmitRankSentinels (TypeMapAssemblyData model)
{
Expand All @@ -304,11 +303,16 @@ void EmitRankSentinels (TypeMapAssemblyData model)
}

_rankAnchorHandles = new EntityHandle [model.MaxArrayRank];
var ns = _pe.Metadata.GetOrAddString ("Microsoft.Android.Runtime");
var objectRef = _pe.Metadata.AddTypeReference (_pe.SystemRuntimeRef,
_pe.Metadata.GetOrAddString ("System"), _pe.Metadata.GetOrAddString ("Object"));
for (int i = 0; i < model.MaxArrayRank; i++) {
_rankAnchorHandles [i] = _pe.Metadata.AddTypeReference (
_pe.MonoAndroidRef, ns,
_pe.Metadata.GetOrAddString ($"__ArrayMapRank{i + 1}"));
_rankAnchorHandles [i] = _pe.Metadata.AddTypeDefinition (
TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.Class,
default,
_pe.Metadata.GetOrAddString ($"__ArrayMapRank{i + 1}"),
objectRef,
MetadataTokens.FieldDefinitionHandle (_pe.Metadata.GetRowCount (TableIndex.Field) + 1),
MetadataTokens.MethodDefinitionHandle (_pe.Metadata.GetRowCount (TableIndex.MethodDef) + 1));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap;

public class TrimmableTypeMapGenerator
{
/// <summary>
/// Runtime-supported maximum array rank — must match the number of
/// <c>__ArrayMapRank{N}</c> types pre-defined in <c>Mono.Android</c>.
/// </summary>
public const int MaxSupportedArrayRank = 8;

readonly ITrimmableTypeMapLogger logger;

static readonly HashSet<string> RequiredFrameworkDeferredRegistrationTypes = new (StringComparer.Ordinal) {
Expand Down Expand Up @@ -47,12 +41,6 @@ public TrimmableTypeMapResult Execute (
if (maxArrayRank < 0) {
throw new ArgumentOutOfRangeException (nameof (maxArrayRank), maxArrayRank, "Must be >= 0.");
}
if (maxArrayRank > MaxSupportedArrayRank) {
throw new ArgumentOutOfRangeException (nameof (maxArrayRank), maxArrayRank,
$"_AndroidTrimmableTypeMapMaxArrayRank={maxArrayRank} exceeds the runtime's supported maximum ({MaxSupportedArrayRank}). " +
$"To raise the limit, add additional __ArrayMapRank{{N}} types to Mono.Android.");
}

var (allPeers, assemblyManifestInfo) = ScanAssemblies (assemblies);
if (allPeers.Count == 0) {
logger.LogNoJavaPeerTypesFound ();
Expand Down
14 changes: 13 additions & 1 deletion src/Mono.Android/Microsoft.Android.Runtime/AggregateTypeMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Microsoft.Android.Runtime;
/// results across all universes. Debug-only — each assembly has its own
/// universe with an isolated <c>TypeMapLazyDictionary</c>.
/// </summary>
sealed class AggregateTypeMap : ITypeMapWithAliasing
sealed class AggregateTypeMap : ITypeMap
{
readonly SingleUniverseTypeMap[] _universes;

Expand Down Expand Up @@ -41,4 +41,16 @@ public bool TryGetProxyType (Type managedType, [NotNullWhen (true)] out Type? pr
proxyType = null;
return false;
}

public bool TryGetArrayType (string jniName, int rankIndex, [NotNullWhen (true)] out Type? arrayType)
{
foreach (var universe in _universes) {
if (universe.TryGetArrayType (jniName, rankIndex, out arrayType)) {
return true;
}
}

arrayType = null;
return false;
}
}
19 changes: 0 additions & 19 deletions src/Mono.Android/Microsoft.Android.Runtime/ArrayMapAnchors.cs

This file was deleted.

Loading