Skip to content
Merged
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
189 changes: 126 additions & 63 deletions src/Xamarin.Android.Build.Tasks/Utilities/TypeMapObjectsXmlFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Microsoft.Android.Build.Tasks;
using Microsoft.Build.Utilities;
using NuGet.Packaging;

using ModuleReleaseData = Xamarin.Android.Tasks.TypeMapGenerator.ModuleReleaseData;
using TypeMapDebugEntry = Xamarin.Android.Tasks.TypeMapGenerator.TypeMapDebugEntry;
Expand Down Expand Up @@ -175,13 +172,20 @@ public static TypeMapObjectsXmlFile Import (string filename)
if (fi.Length == 0)
return unscanned;

var xml = XDocument.Load (filename);
var root = xml.Root ?? throw new InvalidOperationException ($"Invalid XML file '{filename}'");
using var reader = XmlReader.Create (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);
if (!reader.ReadToFollowing ("api"))
throw new InvalidOperationException ($"Invalid XML file '{filename}'");

var type = reader.GetAttribute ("type");

if (type.IsNullOrWhiteSpace ())
throw new InvalidOperationException ($"Missing required attribute 'type' in '{filename}'");

var assemblyName = reader.GetAttribute ("assembly-name");
var mvidValue = reader.GetAttribute ("mvid");
var mvid = mvidValue.IsNullOrWhiteSpace () ? Guid.Empty : Guid.Parse (mvidValue);
var foundJniNativeRegistration = GetAttributeOrDefault (reader, "found-jni-native-registration", false);

var file = new TypeMapObjectsXmlFile {
WasScanned = true,
Expand All @@ -191,59 +195,69 @@ public static TypeMapObjectsXmlFile Import (string filename)
};

if (type == "debug")
ImportDebugData (root, file);
ImportDebugData (reader, file);
else if (type == "release")
ImportReleaseData (root, file);
ImportReleaseData (reader, file);

return file;
}

static void ImportDebugData (XElement root, TypeMapObjectsXmlFile file)
static void ImportDebugData (XmlReader reader, TypeMapObjectsXmlFile file)
{
var assemblyName = root.GetAttributeOrDefault ("assembly-name", string.Empty);
var assemblyName = file.AssemblyName ?? string.Empty;
var isMonoAndroid = assemblyName == "Mono.Android";
var javaToManaged = root.Element ("java-to-managed");

if (javaToManaged is not null) {
foreach (var entry in javaToManaged.Elements ("entry"))
file.JavaToManagedDebugEntries.Add (FromDebugEntryXml (entry, assemblyName, isMonoAndroid));
}

var managedToJava = root.Element ("managed-to-java");
while (reader.Read ()) {
if (reader.NodeType != XmlNodeType.Element)
continue;

if (managedToJava is not null) {
foreach (var entry in managedToJava.Elements ("entry"))
file.ManagedToJavaDebugEntries.Add (FromDebugEntryXml (entry, assemblyName, isMonoAndroid));
if (reader.Name == "java-to-managed")
ReadDebugEntries (reader, file.JavaToManagedDebugEntries, assemblyName, isMonoAndroid);
else if (reader.Name == "managed-to-java")
ReadDebugEntries (reader, file.ManagedToJavaDebugEntries, assemblyName, isMonoAndroid);
}
}

static void ImportReleaseData (XElement root, TypeMapObjectsXmlFile file)
static void ImportReleaseData (XmlReader reader, TypeMapObjectsXmlFile file)
{
var module = root.Element ("module");

if (module is null)
if (!reader.ReadToFollowing ("module"))
return;

var mvidValue = reader.GetAttribute ("mvid");
file.ModuleReleaseData = new ModuleReleaseData {
AssemblyName = module.GetAttributeOrDefault ("assembly-name", string.Empty),
Mvid = Guid.Parse (module.GetAttributeOrDefault ("mvid", Guid.Empty.ToString ())),
MvidBytes = Convert.FromBase64String (module.GetAttributeOrDefault ("mvid-bytes", string.Empty)),
AssemblyName = reader.GetAttribute ("assembly-name") ?? string.Empty,
Mvid = mvidValue.IsNullOrWhiteSpace () ? Guid.Empty : Guid.Parse (mvidValue),
MvidBytes = Convert.FromBase64String (GetAttributeOrDefault (reader, "mvid-bytes", string.Empty)),
TypesScratch = new Dictionary<string, TypeMapReleaseEntry> (StringComparer.Ordinal),
DuplicateTypes = new List<TypeMapReleaseEntry> (),
Comment thread
jonathanpeppers marked this conversation as resolved.
};

if (module.Element ("types") is XElement types)
file.ModuleReleaseData.Types = types.Elements ("entry")
.Select (FromReleaseEntryXml)
.ToArray ();

if (module.Element ("duplicates") is XElement duplicates)
file.ModuleReleaseData.DuplicateTypes.AddRange (duplicates.Elements ("entry")
.Select (FromReleaseEntryXml));
if (reader.IsEmptyElement)
return;

if (module.Element ("types-scratch") is XElement typesScratch)
file.ModuleReleaseData.TypesScratch.AddRange (typesScratch.Elements ("entry")
.Select (elem => new KeyValuePair<string, TypeMapReleaseEntry> (elem.GetAttributeOrDefault ("key", string.Empty), FromReleaseEntryXml (elem))));
int depth = reader.Depth;

while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.EndElement && reader.Depth == depth)
return;

if (reader.NodeType != XmlNodeType.Element)
continue;

switch (reader.Name) {
case "types":
var types = new List<TypeMapReleaseEntry> ();
ReadReleaseEntries (reader, types);
file.ModuleReleaseData.Types = types.ToArray ();
break;
case "duplicates":
ReadReleaseEntries (reader, file.ModuleReleaseData.DuplicateTypes);
break;
case "types-scratch":
ReadReleaseScratchEntries (reader, file.ModuleReleaseData.TypesScratch);
break;
}
}
}

public static void WriteEmptyFile (string destination, TaskLoggingHelper log)
Expand All @@ -254,37 +268,86 @@ public static void WriteEmptyFile (string destination, TaskLoggingHelper log)
File.Create (destination).Dispose ();
}

static TypeMapDebugEntry FromDebugEntryXml (XElement entry, string assemblyName, bool isMonoAndroid)
static TypeMapDebugEntry FromDebugEntryXml (XmlReader reader, string assemblyName, bool isMonoAndroid)
{
var javaName = entry.GetAttributeOrDefault ("java-name", string.Empty);
var managedName = entry.GetAttributeOrDefault ("managed-name", string.Empty);
var skipInJavaToManaged = entry.GetAttributeOrDefault ("skip-in-java-to-managed", false);
var isInvoker = entry.GetAttributeOrDefault ("is-invoker", false);
var managedTokenId = entry.GetAttributeOrDefault ("managed-type-token-id", (uint)0);

return new TypeMapDebugEntry {
JavaName = javaName,
ManagedName = managedName,
ManagedTypeTokenId = managedTokenId,
SkipInJavaToManaged = skipInJavaToManaged,
IsInvoker = isInvoker,
JavaName = reader.GetAttribute ("java-name") ?? string.Empty,
ManagedName = reader.GetAttribute ("managed-name") ?? string.Empty,
ManagedTypeTokenId = GetAttributeOrDefault (reader, "managed-type-token-id", 0u),
SkipInJavaToManaged = GetAttributeOrDefault (reader, "skip-in-java-to-managed", false),
IsInvoker = GetAttributeOrDefault (reader, "is-invoker", false),
IsMonoAndroid = isMonoAndroid,
AssemblyName = assemblyName,
};
}

static TypeMapReleaseEntry FromReleaseEntryXml (XElement entry)
static TypeMapReleaseEntry FromReleaseEntryXml (XmlReader reader)
{
var javaName = entry.GetAttributeOrDefault ("java-name", string.Empty);
var managedTypeName = entry.GetAttributeOrDefault ("managed-type-name", string.Empty);
var token = entry.GetAttributeOrDefault ("token", 0u);
var skipInJavaToManaged = entry.GetAttributeOrDefault ("skip-in-java-to-managed", false);

return new TypeMapReleaseEntry {
JavaName = javaName,
ManagedTypeName = managedTypeName,
Token = token,
SkipInJavaToManaged = skipInJavaToManaged,
JavaName = reader.GetAttribute ("java-name") ?? string.Empty,
ManagedTypeName = reader.GetAttribute ("managed-type-name") ?? string.Empty,
Token = GetAttributeOrDefault (reader, "token", 0u),
SkipInJavaToManaged = GetAttributeOrDefault (reader, "skip-in-java-to-managed", false),
};
}

static T GetAttributeOrDefault<T> (XmlReader reader, string name, T defaultValue)
{
var value = reader.GetAttribute (name);

if (value.IsNullOrWhiteSpace ())
return defaultValue;

return (T) Convert.ChangeType (value, typeof (T), CultureInfo.InvariantCulture);
}

static void ReadDebugEntries (XmlReader reader, List<TypeMapDebugEntry> entries, string assemblyName, bool isMonoAndroid)
{
if (reader.IsEmptyElement)
return;

int depth = reader.Depth;

while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.EndElement && reader.Depth == depth)
return;

if (reader.NodeType == XmlNodeType.Element && reader.Name == "entry")
entries.Add (FromDebugEntryXml (reader, assemblyName, isMonoAndroid));
}
}

static void ReadReleaseEntries (XmlReader reader, List<TypeMapReleaseEntry> entries)
{
if (reader.IsEmptyElement)
return;

int depth = reader.Depth;

while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.EndElement && reader.Depth == depth)
return;

if (reader.NodeType == XmlNodeType.Element && reader.Name == "entry")
entries.Add (FromReleaseEntryXml (reader));
}
}

static void ReadReleaseScratchEntries (XmlReader reader, Dictionary<string, TypeMapReleaseEntry> entries)
{
if (reader.IsEmptyElement)
return;

int depth = reader.Depth;

while (reader.Read ()) {
if (reader.NodeType == XmlNodeType.EndElement && reader.Depth == depth)
return;

if (reader.NodeType == XmlNodeType.Element && reader.Name == "entry") {
var key = reader.GetAttribute ("key") ?? string.Empty;
entries.Add (key, FromReleaseEntryXml (reader));
}
}
}
}