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 @@ -139,6 +139,7 @@ public void LogCustomJavaObjectWarning (string managedTypeName) =>
public string? CheckedBuild { get; set; }
public string? ApplicationJavaClass { get; set; }
public bool GenerateTypeMapAssemblies { get; set; } = true;
public bool IncludeBuiltInValueTypeUniverses { get; set; }
public bool CleanJavaSourceOutputDirectory { get; set; }

/// <summary>
Expand Down Expand Up @@ -257,6 +258,7 @@ public override bool RunTask ()
errorOnCustomJavaObject: ErrorOnCustomJavaObject,
customViewTypeNames: customViewTypeNames,
collectMarshalMethodsForNonAcw: false,
includeBuiltInValueTypeUniverses: IncludeBuiltInValueTypeUniverses,
shouldGenerateTypeMapAssembly: TypeMapFingerprintsFile.IsNullOrEmpty () ? null : ShouldGenerateTypeMapAssembly);
if (Log.HasLoggedErrors) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,16 @@ public static ModelFingerprints ComputeFingerprints (
public static byte [] ComputeRootIncrementalFingerprint (
IReadOnlyList<string> perAssemblyTypeMapNames,
Version systemRuntimeVersion,
bool useSharedTypemapUniverse)
bool useSharedTypemapUniverse,
bool includeBuiltInValueTypeUniverses)
{
// This method needs only one hash. The content sink is used as the writer's always-present
// sink; the returned value is still solely the incremental-build fingerprint for the root.
using var writer = new FingerprintWriter (includeIncremental: false);
writer.WriteRaw (Sink.Content, GeneratorModuleVersionId.ToByteArray ());
writer.WriteString (Sink.Content, systemRuntimeVersion.ToString ());
writer.WriteBoolean (Sink.Content, useSharedTypemapUniverse);
writer.WriteBoolean (Sink.Content, includeBuiltInValueTypeUniverses);
writer.WriteInt32 (Sink.Content, perAssemblyTypeMapNames.Count);
foreach (var assemblyName in perAssemblyTypeMapNames) {
writer.WriteString (Sink.Content, assemblyName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,13 @@ public RootTypeMapAssemblyGenerator (Version systemRuntimeVersion)
/// <param name="stream">Stream to write the output PE to.</param>
/// <param name="assemblyName">Optional assembly name (defaults to _Microsoft.Android.TypeMaps).</param>
/// <param name="moduleName">Optional module name for the PE metadata.</param>
public void Generate (IReadOnlyList<string> perAssemblyTypeMapNames, bool useSharedTypemapUniverse, Stream stream, string? assemblyName = null, string? moduleName = null)
public void Generate (
IReadOnlyList<string> perAssemblyTypeMapNames,
bool useSharedTypemapUniverse,
Stream stream,
string? assemblyName = null,
string? moduleName = null,
bool includeBuiltInValueTypeUniverses = false)
{
if (perAssemblyTypeMapNames is null) {
throw new ArgumentNullException (nameof (perAssemblyTypeMapNames));
Expand All @@ -83,18 +89,38 @@ public void Generate (IReadOnlyList<string> perAssemblyTypeMapNames, bool useSha
throw new ArgumentNullException (nameof (stream));
}

CreatePEBuilder (perAssemblyTypeMapNames, useSharedTypemapUniverse, assemblyName, moduleName).WritePE (stream);
CreatePEBuilder (
perAssemblyTypeMapNames,
useSharedTypemapUniverse,
assemblyName,
moduleName,
includeBuiltInValueTypeUniverses).WritePE (stream);
}

/// <summary>
/// Generates the root typemap assembly and returns a read-only stream over the serialised image.
/// </summary>
internal Stream GenerateToStream (IReadOnlyList<string> perAssemblyTypeMapNames, bool useSharedTypemapUniverse, string? assemblyName = null, string? moduleName = null)
internal Stream GenerateToStream (
IReadOnlyList<string> perAssemblyTypeMapNames,
bool useSharedTypemapUniverse,
bool includeBuiltInValueTypeUniverses = false,
string? assemblyName = null,
string? moduleName = null)
{
return CreatePEBuilder (perAssemblyTypeMapNames, useSharedTypemapUniverse, assemblyName, moduleName).CreatePEStream ();
return CreatePEBuilder (
perAssemblyTypeMapNames,
useSharedTypemapUniverse,
assemblyName,
moduleName,
includeBuiltInValueTypeUniverses).CreatePEStream ();
}

PEAssemblyBuilder CreatePEBuilder (IReadOnlyList<string> perAssemblyTypeMapNames, bool useSharedTypemapUniverse, string? assemblyName, string? moduleName)
PEAssemblyBuilder CreatePEBuilder (
IReadOnlyList<string> perAssemblyTypeMapNames,
bool useSharedTypemapUniverse,
string? assemblyName,
string? moduleName,
bool includeBuiltInValueTypeUniverses)
{
if (perAssemblyTypeMapNames is null) {
throw new ArgumentNullException (nameof (perAssemblyTypeMapNames));
Expand Down Expand Up @@ -134,6 +160,9 @@ PEAssemblyBuilder CreatePEBuilder (IReadOnlyList<string> perAssemblyTypeMapNames
} else {
EmitPerAssemblyUniverseAssemblyTargetAttributes (pe, perAssemblyTypeMapNames);
}
if (includeBuiltInValueTypeUniverses) {
EmitValueTypeDictionaryAssemblyTargetAttribute (pe);
}

// Emit [assembly: IgnoresAccessChecksTo("...")] so TypeMapLoader.Initialize() can access
// internal types (TrimmableTypeMap and friends in Mono.Android, and private anchors
Expand Down Expand Up @@ -171,6 +200,16 @@ static void EmitPerAssemblyUniverseAssemblyTargetAttributes (PEAssemblyBuilder p
}
}

static void EmitValueTypeDictionaryAssemblyTargetAttribute (PEAssemblyBuilder pe)
{
var openAttrRef = GetTypeMapAssemblyTargetAttributeRef (pe);
var javaDictionaryRef = pe.Metadata.AddTypeReference (pe.MonoAndroidRef,
pe.Metadata.GetOrAddString ("Android.Runtime"),
pe.Metadata.GetOrAddString ("JavaDictionary"));
var ctorRef = GetTypeMapAssemblyTargetAttributeCtorRef (pe, openAttrRef, javaDictionaryRef);
EmitAssemblyTargetAttribute (pe, ctorRef, "Mono.Android");
}

static TypeReferenceHandle GetTypeMapAssemblyTargetAttributeRef (PEAssemblyBuilder pe)
{
return pe.Metadata.AddTypeReference (pe.SystemRuntimeInteropServicesRef,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public TrimmableTypeMapResult Execute (
bool errorOnCustomJavaObject = true,
IReadOnlyCollection<string>? customViewTypeNames = null,
bool collectMarshalMethodsForNonAcw = true,
bool includeBuiltInValueTypeUniverses = false,
Func<string, byte [], bool>? shouldGenerateTypeMapAssembly = null)
{
_ = assemblies ?? throw new ArgumentNullException (nameof (assemblies));
Expand Down Expand Up @@ -79,7 +80,12 @@ public TrimmableTypeMapResult Execute (
}

var generatedAssemblies = generateTypeMapAssemblies
? GenerateTypeMapAssemblies (allPeers, systemRuntimeVersion, useSharedTypemapUniverse, shouldGenerateTypeMapAssembly)
? GenerateTypeMapAssemblies (
allPeers,
systemRuntimeVersion,
useSharedTypemapUniverse,
shouldGenerateTypeMapAssembly,
includeBuiltInValueTypeUniverses)
: [];
var jcwPeers = allPeers.Where (ShouldGenerateJcw).ToList ();
logger.LogGeneratingJcwFilesInfo (jcwPeers.Count, allPeers.Count);
Expand Down Expand Up @@ -413,7 +419,8 @@ internal List<GeneratedAssembly> GenerateTypeMapAssemblies (
List<JavaPeerInfo> allPeers,
Version systemRuntimeVersion,
bool useSharedTypemapUniverse,
Func<string, byte [], bool>? shouldGenerateTypeMapAssembly = null)
Func<string, byte [], bool>? shouldGenerateTypeMapAssembly = null,
bool includeBuiltInValueTypeUniverses = false)
{
List<(string AssemblyName, List<JavaPeerInfo> Peers)> peersByAssembly;

Expand Down Expand Up @@ -459,12 +466,19 @@ internal List<GeneratedAssembly> GenerateTypeMapAssemblies (
const string rootAssemblyName = "_Microsoft.Android.TypeMaps";
bool generateRoot = true;
if (shouldGenerateTypeMapAssembly is not null) {
var rootFingerprint = MetadataHelper.ComputeRootIncrementalFingerprint (perAssemblyNames, systemRuntimeVersion, useSharedTypemapUniverse);
var rootFingerprint = MetadataHelper.ComputeRootIncrementalFingerprint (
perAssemblyNames,
systemRuntimeVersion,
useSharedTypemapUniverse,
includeBuiltInValueTypeUniverses);
generateRoot = shouldGenerateTypeMapAssembly (rootAssemblyName, rootFingerprint);
}
if (generateRoot) {
var rootGenerator = new RootTypeMapAssemblyGenerator (systemRuntimeVersion);
var rootStream = rootGenerator.GenerateToStream (perAssemblyNames, useSharedTypemapUniverse);
var rootStream = rootGenerator.GenerateToStream (
perAssemblyNames,
useSharedTypemapUniverse,
includeBuiltInValueTypeUniverses);
generatedAssemblies.Add (new GeneratedAssembly (rootAssemblyName, rootStream));
logger.LogGeneratedRootTypeMapInfo (perAssemblyNames.Count);
}
Expand Down
23 changes: 16 additions & 7 deletions src/Mono.Android/Java.Interop/JavaConvert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ static class JavaConvert {
return (h, t) => JNIEnv.GetArray (h, t, target.GetElementType ());

if (target.IsGenericType && !target.IsGenericTypeDefinition) {
if (RuntimeFeature.TrimmableTypeMap) {
if (RuntimeFeature.IsNativeAotRuntime) {
if (SafeJavaCollectionFactory.TryGetFromJniHandleConverter (target, out var collectionConverter))
return collectionConverter;
} else if (System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported) {
Expand All @@ -98,19 +98,28 @@ static class JavaConvert {

[UnconditionalSuppressMessage ("ReflectionAnalysis", "IL2055:RequiresUnreferencedCode",
Justification = "The target generic type is expected to be preserved by the trimmer as the target type in marshaling.")]
[UnconditionalSuppressMessage ("Trimming", "IL2067:UnrecognizedReflectionPattern",
Justification = "This method is used only when dynamic code is supported. The target closed generic collection type is preserved by the marshaling call site, including its public FromJniHandle method.")]
[RequiresDynamicCode ("This API uses reflection to create generic types at runtime, which is not supported in AOT scenarios.")]
static Func<IntPtr, JniHandleOwnership, object?>? TryMakeGenericCollectionTypeFactory (Type target)
{
if (target.GetGenericTypeDefinition() == typeof (IDictionary<,>)) {
Type t = typeof (JavaDictionary<,>).MakeGenericType (target.GetGenericArguments ());
var genericDefinition = target.GetGenericTypeDefinition ();
if (genericDefinition == typeof (IDictionary<,>) || genericDefinition == typeof (JavaDictionary<,>)) {
Type t = genericDefinition == typeof (JavaDictionary<,>)
? target
: typeof (JavaDictionary<,>).MakeGenericType (target.GetGenericArguments ());
return GetJniHandleConverterForType (t);
}
if (target.GetGenericTypeDefinition() == typeof (IList<>)) {
Type t = typeof (JavaList<>).MakeGenericType (target.GetGenericArguments ());
if (genericDefinition == typeof (IList<>) || genericDefinition == typeof (JavaList<>)) {
Type t = genericDefinition == typeof (JavaList<>)
? target
: typeof (JavaList<>).MakeGenericType (target.GetGenericArguments ());
return GetJniHandleConverterForType (t);
}
if (target.GetGenericTypeDefinition() == typeof (ICollection<>)) {
Type t = typeof (JavaCollection<>).MakeGenericType (target.GetGenericArguments ());
if (genericDefinition == typeof (ICollection<>) || genericDefinition == typeof (JavaCollection<>)) {
Type t = genericDefinition == typeof (JavaCollection<>)
? target
: typeof (JavaCollection<>).MakeGenericType (target.GetGenericArguments ());
return GetJniHandleConverterForType (t);
}

Expand Down
24 changes: 14 additions & 10 deletions src/Mono.Android/Java.Interop/SafeJavaCollectionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,24 @@ internal static bool TryGetFromJniHandleConverter (

var keyType = arguments [0];
var valueType = arguments [1];
ValueTypeFactory? keyFactory = null;
ValueTypeFactory? valueFactory = null;
if ((keyType.IsValueType && !ValueTypeFactory.PrimitiveTypeFactories.TryGetValue (keyType, out keyFactory))
|| (valueType.IsValueType && !ValueTypeFactory.PrimitiveTypeFactories.TryGetValue (valueType, out valueFactory))) {
converter = GetUntypedFromJniHandleConverter (genericDefinition);
if (keyType.IsValueType && valueType.IsValueType) {
if (!ValueTypeDictionaryFactory.TryGetFromJniHandleConverter (targetType, out converter))
converter = GetUntypedFromJniHandleConverter (genericDefinition);
return true;
}
if (keyFactory != null) {
converter = valueFactory != null
? (handle, transfer) => handle == IntPtr.Zero ? null : keyFactory.CreateDictionary (valueFactory, handle, transfer)
: (handle, transfer) => handle == IntPtr.Zero ? null : keyFactory.CreateDictionaryWithReferenceValue (valueType, handle, transfer);
if (keyType.IsValueType) {
if (!ValueTypeFactory.PrimitiveTypeFactories.TryGetValue (keyType, out var keyFactory)) {
converter = GetUntypedFromJniHandleConverter (genericDefinition);
return true;
}
converter = (handle, transfer) => handle == IntPtr.Zero ? null : keyFactory.CreateDictionaryWithReferenceValue (valueType, handle, transfer);
return true;
}
if (valueFactory != null) {
if (valueType.IsValueType) {
if (!ValueTypeFactory.PrimitiveTypeFactories.TryGetValue (valueType, out var valueFactory)) {
converter = GetUntypedFromJniHandleConverter (genericDefinition);
return true;
}
converter = (handle, transfer) => handle == IntPtr.Zero ? null : valueFactory.CreateDictionaryWithReferenceKey (keyType, handle, transfer);
return true;
}
Expand Down
Loading