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 @@ -37,13 +37,14 @@ public void ProcessAssembly (AssemblyDefinition assembly, StepContext context)

var xml = new TypeMapObjectsXmlFile {
AssemblyName = assembly.Name.Name,
AssemblyMvid = assembly.MainModule.Mvid,
};

if (Debug) {
var (javaToManaged, managedToJava, foundJniNativeRegistration) = TypeMapCecilAdapter.GetDebugNativeEntries (types, Context);
var (typeMapDebugSets, foundJniNativeRegistration) = TypeMapCecilAdapter.GetDebugNativeEntries (types, Context, needUniqueAssemblies: false);

xml.JavaToManagedDebugEntries.AddRange (javaToManaged);
xml.ManagedToJavaDebugEntries.AddRange (managedToJava);
xml.JavaToManagedDebugEntries.AddRange (typeMapDebugSets.JavaToManaged);
xml.ManagedToJavaDebugEntries.AddRange (typeMapDebugSets.ManagedToJava);
xml.FoundJniNativeRegistration = foundJniNativeRegistration;
} else {
var genState = TypeMapCecilAdapter.GetReleaseGenerationState (types, Context, out var foundJniNativeRegistration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public void BuildLibraryWithAssetPack ([Values (true, false)] bool isRelease)
[Category ("SmokeTests")]
[TestCase (false, AndroidRuntime.MonoVM)]
[TestCase (true, AndroidRuntime.MonoVM)]
[TestCase (false, AndroidRuntime.CoreCLR)]
[TestCase (true, AndroidRuntime.CoreCLR)]
[TestCase (true, AndroidRuntime.NativeAOT)]
public void BuildApplicationWithAssetPackThatHasInvalidName (bool isRelease, AndroidRuntime runtime)
Expand Down Expand Up @@ -67,6 +68,7 @@ public void BuildApplicationWithAssetPackThatHasInvalidName (bool isRelease, And
[Category ("SmokeTests")]
[TestCase (false, AndroidRuntime.MonoVM)]
[TestCase (true, AndroidRuntime.MonoVM)]
[TestCase (false, AndroidRuntime.CoreCLR)]
[TestCase (true, AndroidRuntime.CoreCLR)]
[TestCase (true, AndroidRuntime.NativeAOT)]
public void BuildApplicationWithAssetPackOutsideProjectDirectory (bool isRelease, AndroidRuntime runtime)
Expand Down Expand Up @@ -117,6 +119,7 @@ public void BuildApplicationWithAssetPackOutsideProjectDirectory (bool isRelease
[Category ("SmokeTests")]
[TestCase (false, AndroidRuntime.MonoVM)]
[TestCase (true, AndroidRuntime.MonoVM)]
[TestCase (false, AndroidRuntime.CoreCLR)]
[TestCase (true, AndroidRuntime.CoreCLR)]
[TestCase (true, AndroidRuntime.NativeAOT)]
public void BuildApplicationWithAssetPackOverrides (bool isRelease, AndroidRuntime runtime)
Expand Down Expand Up @@ -160,6 +163,7 @@ public void BuildApplicationWithAssetPackOverrides (bool isRelease, AndroidRunti
[Category ("SmokeTests")]
[TestCase (false, AndroidRuntime.MonoVM)]
[TestCase (true, AndroidRuntime.MonoVM)]
[TestCase (false, AndroidRuntime.CoreCLR)]
[TestCase (true, AndroidRuntime.CoreCLR)]
[TestCase (true, AndroidRuntime.NativeAOT)]
public void BuildApplicationWithAssetPack (bool isRelease, AndroidRuntime runtime)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ public partial class BuildTest : BaseTest
/* usesAssemblyStore */ true,
/* runtime */ AndroidRuntime.CoreCLR,
},
new object [] {
/* runtimeIdentifiers */ "android-arm64",
/* isRelease */ false,
/* aot */ false,
/* usesAssemblyStore */ true,
/* runtime */ AndroidRuntime.CoreCLR,
},
new object [] {
/* runtimeIdentifiers */ "android-arm64",
/* isRelease */ true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,27 @@
using ReleaseGenerationState = Xamarin.Android.Tasks.TypeMapGenerator.ReleaseGenerationState;
using TypeMapDebugEntry = Xamarin.Android.Tasks.TypeMapGenerator.TypeMapDebugEntry;
using TypeMapReleaseEntry = Xamarin.Android.Tasks.TypeMapGenerator.TypeMapReleaseEntry;
using TypeMapDebugDataSets = Xamarin.Android.Tasks.TypeMapGenerator.TypeMapDebugDataSets;
using TypeMapDebugAssembly = Xamarin.Android.Tasks.TypeMapGenerator.TypeMapDebugAssembly;

namespace Xamarin.Android.Tasks;

// Converts types from Mono.Cecil to the format used by the typemap generator.
class TypeMapCecilAdapter
{
public static (List<TypeMapDebugEntry> javaToManaged, List<TypeMapDebugEntry> managedToJava) GetDebugNativeEntries (NativeCodeGenState state)
public static TypeMapDebugDataSets GetDebugNativeEntries (NativeCodeGenState state, bool needUniqueAssemblies)
{
var (javaToManaged, managedToJava, foundJniNativeRegistration) = GetDebugNativeEntries (state.AllJavaTypes, state.TypeCache);
var (dataSets, foundJniNativeRegistration) = GetDebugNativeEntries (state.AllJavaTypes, state.TypeCache, needUniqueAssemblies);

state.JniAddNativeMethodRegistrationAttributePresent = foundJniNativeRegistration;

return (javaToManaged, managedToJava);
return dataSets;
}

public static (List<TypeMapDebugEntry> javaToManaged, List<TypeMapDebugEntry> managedToJava, bool foundJniNativeRegistration) GetDebugNativeEntries (List<TypeDefinition> types, TypeDefinitionCache cache)
public static (TypeMapDebugDataSets dataSets, bool foundJniNativeRegistration) GetDebugNativeEntries (List<TypeDefinition> types, TypeDefinitionCache cache, bool needUniqueAssemblies)
{
var javaDuplicates = new Dictionary<string, List<TypeMapDebugEntry>> (StringComparer.Ordinal);
var uniqueAssemblies = needUniqueAssemblies ? new Dictionary<string, TypeMapDebugAssembly> (StringComparer.OrdinalIgnoreCase) : null;
var javaToManaged = new List<TypeMapDebugEntry> ();
var managedToJava = new List<TypeMapDebugEntry> ();
var foundJniNativeRegistration = false;
Expand All @@ -37,11 +40,31 @@ public static (List<TypeMapDebugEntry> javaToManaged, List<TypeMapDebugEntry> ma

javaToManaged.Add (entry);
managedToJava.Add (entry);

if (uniqueAssemblies == null) {
continue;
}

string? asmName = td.Module.Assembly.Name.Name;
if (String.IsNullOrEmpty (asmName) || uniqueAssemblies.ContainsKey (asmName)) {
continue;
}

var asmInfo = new TypeMapDebugAssembly {
MVID = td.Module.Mvid,
Name = asmName,
};
asmInfo.MVIDBytes = asmInfo.MVID.ToByteArray ();
uniqueAssemblies.Add (asmName, asmInfo);
}

SyncDebugDuplicates (javaDuplicates);

return (javaToManaged, managedToJava, foundJniNativeRegistration);
return (new TypeMapDebugDataSets {
JavaToManaged = javaToManaged,
ManagedToJava = managedToJava,
UniqueAssemblies = uniqueAssemblies != null ? new List<TypeMapDebugAssembly> (uniqueAssemblies.Values) : null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should UniqueAssemblies be a List<T>? If it were e.g. IEnumerable<TypeMapDebugAssembly>, this could avoid an "extra" List<TypeMapDebugAssembly> allocation. (Though that would also prolong the lifetime of the Dictionary, so maybe the List<T> is a better tradeoff?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think List<T> is fine, it's lighter than the dictionary

}, foundJniNativeRegistration);
}

public static ReleaseGenerationState GetReleaseGenerationState (NativeCodeGenState state)
Expand Down Expand Up @@ -204,7 +227,7 @@ static bool JniAddNativeMethodRegistrationAttributeFound (bool alreadyFound, Typ
if (alreadyFound || !javaType.HasCustomAttributes) {
return alreadyFound;
}

foreach (CustomAttribute ca in javaType.CustomAttributes) {
if (string.Equals ("JniAddNativeMethodRegistrationAttribute", ca.AttributeType.Name, StringComparison.Ordinal)) {
return true;
Expand Down
54 changes: 44 additions & 10 deletions src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,27 @@ public override string ToString ()
}
}

internal sealed class TypeMapDebugAssembly
{
public Guid MVID;
public byte[] MVIDBytes;
public string Name;
}

internal sealed class TypeMapDebugDataSets
{
public List<TypeMapDebugEntry> JavaToManaged ;
public List<TypeMapDebugEntry> ManagedToJava;
public List<TypeMapDebugAssembly>? UniqueAssemblies;
}

// Widths include the terminating nul character but not the padding!
internal sealed class ModuleDebugData
{
public uint EntryCount;
public List<TypeMapDebugEntry> JavaToManagedMap;
public List<TypeMapDebugEntry> ManagedToJavaMap;
public List<TypeMapDebugAssembly>? UniqueAssemblies;
}

internal sealed class ReleaseGenerationState
Expand Down Expand Up @@ -138,18 +153,23 @@ public void Generate (bool debugBuild, bool skipJniAddNativeMethodRegistrationAt

void GenerateDebugNativeAssembly (string outputDirectory)
{
(var javaToManaged, var managedToJava) = state.GetDebugNativeEntries ();
TypeMapDebugDataSets dataSets = state.GetDebugNativeEntries (needUniqueAssemblies: runtime == AndroidRuntime.CoreCLR);

var data = new ModuleDebugData {
EntryCount = (uint)javaToManaged.Count,
JavaToManagedMap = javaToManaged,
ManagedToJavaMap = managedToJava,
EntryCount = (uint)dataSets.JavaToManaged.Count,
JavaToManagedMap = dataSets.JavaToManaged,
ManagedToJavaMap = dataSets.ManagedToJava,
UniqueAssemblies = dataSets.UniqueAssemblies,
};

data.JavaToManagedMap.Sort ((TypeMapDebugEntry a, TypeMapDebugEntry b) => String.Compare (a.JavaName, b.JavaName, StringComparison.Ordinal));
data.ManagedToJavaMap.Sort ((TypeMapDebugEntry a, TypeMapDebugEntry b) => String.Compare (a.ManagedName, b.ManagedName, StringComparison.Ordinal));

var composer = new TypeMappingDebugNativeAssemblyGenerator (log, data);
LLVMIR.LlvmIrComposer composer = runtime switch {
AndroidRuntime.MonoVM => new TypeMappingDebugNativeAssemblyGenerator (log, data),
AndroidRuntime.CoreCLR => new TypeMappingDebugNativeAssemblyGeneratorCLR (log, data),
_ => throw new NotSupportedException ($"Internal error: unsupported runtime {runtime}")
};
GenerateNativeAssembly (composer, composer.Construct (), outputDirectory);
}

Expand Down Expand Up @@ -216,7 +236,7 @@ interface ITypeMapGeneratorAdapter
{
AndroidTargetArch TargetArch { get; }
bool JniAddNativeMethodRegistrationAttributePresent { get; set; }
(List<TypeMapDebugEntry> javaToManaged, List<TypeMapDebugEntry> managedToJava) GetDebugNativeEntries ();
TypeMapDebugDataSets GetDebugNativeEntries (bool needUniqueAssemblies);
ReleaseGenerationState GetReleaseGenerationState ();
}

Expand All @@ -236,9 +256,9 @@ public bool JniAddNativeMethodRegistrationAttributePresent {
set => state.JniAddNativeMethodRegistrationAttributePresent = value;
}

public (List<TypeMapDebugEntry> javaToManaged, List<TypeMapDebugEntry> managedToJava) GetDebugNativeEntries ()
public TypeMapDebugDataSets GetDebugNativeEntries (bool needUniqueAssemblies)
{
return TypeMapCecilAdapter.GetDebugNativeEntries (state);
return TypeMapCecilAdapter.GetDebugNativeEntries (state, needUniqueAssemblies);
}

public ReleaseGenerationState GetReleaseGenerationState ()
Expand All @@ -259,12 +279,22 @@ public TypeMapObjectsFileAdapter (AndroidTargetArch targetArch)

public bool JniAddNativeMethodRegistrationAttributePresent { get; set; }

public (List<TypeMapDebugEntry> javaToManaged, List<TypeMapDebugEntry> managedToJava) GetDebugNativeEntries ()
public TypeMapDebugDataSets GetDebugNativeEntries (bool needUniqueAssemblies)
{
var javaToManaged = new List<TypeMapDebugEntry> ();
var managedToJava = new List<TypeMapDebugEntry> ();
var uniqueAssemblies = new Dictionary<string, TypeMapDebugAssembly> (StringComparer.OrdinalIgnoreCase);

foreach (var xml in XmlFiles) {
if (!uniqueAssemblies.ContainsKey (xml.AssemblyName)) {
var assm = new TypeMapDebugAssembly {
MVID = xml.AssemblyMvid,
MVIDBytes = xml.AssemblyMvid.ToByteArray (),
Name = xml.AssemblyName,
};
uniqueAssemblies.Add (xml.AssemblyName, assm);
}

javaToManaged.AddRange (xml.JavaToManagedDebugEntries);
managedToJava.AddRange (xml.ManagedToJavaDebugEntries);
}
Expand All @@ -273,7 +303,11 @@ public TypeMapObjectsFileAdapter (AndroidTargetArch targetArch)
GroupDuplicateDebugEntries (javaToManaged);
GroupDuplicateDebugEntries (managedToJava);

return (javaToManaged, managedToJava);
return new TypeMapDebugDataSets {
JavaToManaged = javaToManaged,
ManagedToJava = managedToJava,
UniqueAssemblies = uniqueAssemblies.Values.ToList (),
};
}

void GroupDuplicateDebugEntries (List<TypeMapDebugEntry> debugEntries)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class TypeMapObjectsXmlFile
static readonly TypeMapObjectsXmlFile unscanned = new TypeMapObjectsXmlFile { WasScanned = false };

public string? AssemblyName { get; set; }
public Guid AssemblyMvid { get; set; } = Guid.Empty;
public bool FoundJniNativeRegistration { get; set; }
public List<TypeMapDebugEntry> JavaToManagedDebugEntries { get; } = [];
public List<TypeMapDebugEntry> ManagedToJavaDebugEntries { get; } = [];
Expand Down Expand Up @@ -58,6 +59,10 @@ void Export (XmlWriter xml)
xml.WriteStartElement ("api");
xml.WriteAttributeString ("type", HasDebugEntries ? "debug" : "release");
xml.WriteAttributeStringIfNotDefault ("assembly-name", AssemblyName);

if (AssemblyMvid != Guid.Empty) {
xml.WriteAttributeString ("mvid", AssemblyMvid.ToString ("N"));
}
xml.WriteAttributeStringIfNotDefault ("found-jni-native-registration", FoundJniNativeRegistration);

if (HasDebugEntries)
Expand Down Expand Up @@ -173,11 +178,13 @@ public static TypeMapObjectsXmlFile Import (string filename)

var type = root.GetRequiredAttribute ("type");
var assemblyName = root.GetAttributeOrDefault ("assembly-name", (string?)null);
var mvid = Guid.Parse (root.GetAttributeOrDefault ("mvid", Guid.Empty.ToString ()));
var foundJniNativeRegistration = root.GetAttributeOrDefault ("found-jni-native-registration", false);

var file = new TypeMapObjectsXmlFile {
WasScanned = true,
AssemblyName = assemblyName,
AssemblyMvid = mvid,
FoundJniNativeRegistration = foundJniNativeRegistration,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class TypeMappingDebugNativeAssemblyGenerator : LlvmIrComposer
{
const string JavaToManagedSymbol = "map_java_to_managed";
const string ManagedToJavaSymbol = "map_managed_to_java";
const string TypeMapSymbol = "type_map"; // MUST match src/monodroid/xamarin-app.hh
const string TypeMapSymbol = "type_map"; // MUST match src/native/mono/xamarin-app-stub/xamarin-app.hh

sealed class TypeMapContextDataProvider : NativeAssemblerStructContextDataProvider
{
Expand Down Expand Up @@ -66,19 +66,19 @@ public override string GetComment (object data, string fieldName)
var entry = EnsureType<TypeMapEntry> (data);

if (String.Compare ("from", fieldName, StringComparison.Ordinal) == 0) {
return $"from: entry.from";
return $"from: {entry.from}";
}

if (String.Compare ("to", fieldName, StringComparison.Ordinal) == 0) {
return $"to: entry.to";
return $"to: {entry.to}";
}

return String.Empty;
}
}

// Order of fields and their type must correspond *exactly* to that in
// src/monodroid/jni/xamarin-app.hh TypeMapEntry structure
// src/native/mono/xamarin-app-stub/xamarin-app.hh TypeMapEntry structure
[NativeAssemblerStructContextDataProvider (typeof (TypeMapEntryContextDataProvider))]
sealed class TypeMapEntry
{
Expand All @@ -87,7 +87,7 @@ sealed class TypeMapEntry
};

// Order of fields and their type must correspond *exactly* to that in
// src/monodroid/jni/xamarin-app.hh TypeMap structure
// src/native/mono/xamarin-app-stub/xamarin-app.hh TypeMap structure
[NativeAssemblerStructContextDataProvider (typeof (TypeMapContextDataProvider))]
sealed class TypeMap
{
Expand Down
Loading