Skip to content

Commit

Permalink
[WIP] Handle duplicate java to managed type maps properly
Browse files Browse the repository at this point in the history
Fixes: dotnet#4596
Context: 7117414

Do the heavy lifting on the build time instead of the run time.  Prefer
base types and always map all the duplicate entries to the **first**
entry encountered.
  • Loading branch information
grendello committed May 6, 2020
1 parent 7dab4ee commit b00d4bc
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 15 deletions.
6 changes: 3 additions & 3 deletions src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ void Run (DirectoryAssemblyResolver res)

// Step 2 - Generate type maps
// Type mappings need to use all the assemblies, always.
WriteTypeMappings (allJavaTypes);
WriteTypeMappings (allJavaTypes, cache);

var javaTypes = new List<TypeDefinition> ();
foreach (TypeDefinition td in allJavaTypes) {
Expand Down Expand Up @@ -399,10 +399,10 @@ void SaveResource (string resource, string filename, string destDir, Func<string
MonoAndroidHelper.CopyIfStringChanged (template, Path.Combine (destDir, filename));
}

void WriteTypeMappings (List<TypeDefinition> types)
void WriteTypeMappings (List<TypeDefinition> types, TypeDefinitionCache cache)
{
var tmg = new TypeMapGenerator ((string message) => Log.LogDebugMessage (message), SupportedAbis);
if (!tmg.Generate (Debug, SkipJniAddNativeMethodRegistrationAttributeScan, types, TypemapOutputDirectory, GenerateNativeAssembly, out ApplicationConfigTaskState appConfState))
if (!tmg.Generate (Debug, SkipJniAddNativeMethodRegistrationAttributeScan, types, cache, TypemapOutputDirectory, GenerateNativeAssembly, out ApplicationConfigTaskState appConfState))
throw new XamarinAndroidException (4308, Properties.Resources.XA4308);
GeneratedBinaryTypeMaps = tmg.GeneratedBinaryTypeMaps.ToArray ();
BuildEngine4.RegisterTaskObject (ApplicationConfigTaskState.RegisterTaskObjectKey, appConfState, RegisteredTaskObjectLifetime.Build, allowEarlyCollection: false);
Expand Down
71 changes: 59 additions & 12 deletions src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Text;

using Java.Interop.Tools.Cecil;
using Mono.Cecil;

namespace Xamarin.Android.Tasks
Expand Down Expand Up @@ -76,6 +77,7 @@ internal sealed class TypeMapDebugEntry
public string ManagedLabel;
public int JavaIndex;
public int ManagedIndex;
public TypeDefinition TypeDefinition;
}

// Widths include the terminating nul character but not the padding!
Expand Down Expand Up @@ -125,7 +127,7 @@ void UpdateApplicationConfig (TypeDefinition javaType, ApplicationConfigTaskStat
}
}

public bool Generate (bool debugBuild, bool skipJniAddNativeMethodRegistrationAttributeScan, List<TypeDefinition> javaTypes, string outputDirectory, bool generateNativeAssembly, out ApplicationConfigTaskState appConfState)
public bool Generate (bool debugBuild, bool skipJniAddNativeMethodRegistrationAttributeScan, List<TypeDefinition> javaTypes, TypeDefinitionCache cache, string outputDirectory, bool generateNativeAssembly, out ApplicationConfigTaskState appConfState)
{
if (String.IsNullOrEmpty (outputDirectory))
throw new ArgumentException ("must not be null or empty", nameof (outputDirectory));
Expand All @@ -140,25 +142,26 @@ public bool Generate (bool debugBuild, bool skipJniAddNativeMethodRegistrationAt
string typemapsOutputDirectory = Path.Combine (outputDirectory, "typemaps");

if (debugBuild) {
return GenerateDebug (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, typemapsOutputDirectory, generateNativeAssembly, appConfState);
return GenerateDebug (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, cache, typemapsOutputDirectory, generateNativeAssembly, appConfState);
}

return GenerateRelease (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, typemapsOutputDirectory, appConfState);
}

bool GenerateDebug (bool skipJniAddNativeMethodRegistrationAttributeScan, List<TypeDefinition> javaTypes, string outputDirectory, bool generateNativeAssembly, ApplicationConfigTaskState appConfState)
bool GenerateDebug (bool skipJniAddNativeMethodRegistrationAttributeScan, List<TypeDefinition> javaTypes, TypeDefinitionCache cache, string outputDirectory, bool generateNativeAssembly, ApplicationConfigTaskState appConfState)
{
if (generateNativeAssembly)
return GenerateDebugNativeAssembly (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, outputDirectory, appConfState);
return GenerateDebugFiles (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, outputDirectory, appConfState);
return GenerateDebugNativeAssembly (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, cache, outputDirectory, appConfState);
return GenerateDebugFiles (skipJniAddNativeMethodRegistrationAttributeScan, javaTypes, cache, outputDirectory, appConfState);
}

bool GenerateDebugFiles (bool skipJniAddNativeMethodRegistrationAttributeScan, List<TypeDefinition> javaTypes, string outputDirectory, ApplicationConfigTaskState appConfState)
bool GenerateDebugFiles (bool skipJniAddNativeMethodRegistrationAttributeScan, List<TypeDefinition> javaTypes, TypeDefinitionCache cache, string outputDirectory, ApplicationConfigTaskState appConfState)
{
var modules = new Dictionary<string, ModuleDebugData> (StringComparer.Ordinal);
int maxModuleFileNameWidth = 0;
int maxModuleNameWidth = 0;

var javaDuplicates = new Dictionary<string, List<TypeMapDebugEntry>> (StringComparer.Ordinal);
foreach (TypeDefinition td in javaTypes) {
UpdateApplicationConfig (td, appConfState);
string moduleName = td.Module.Assembly.Name.Name;
Expand Down Expand Up @@ -186,6 +189,7 @@ bool GenerateDebugFiles (bool skipJniAddNativeMethodRegistrationAttributeScan, L
}

TypeMapDebugEntry entry = GetDebugEntry (td);
HandleDebugDuplicates (javaDuplicates, entry, td, cache);
if (entry.JavaName.Length > module.JavaNameWidth)
module.JavaNameWidth = (uint)entry.JavaName.Length + 1;

Expand All @@ -195,6 +199,7 @@ bool GenerateDebugFiles (bool skipJniAddNativeMethodRegistrationAttributeScan, L
module.JavaToManagedMap.Add (entry);
module.ManagedToJavaMap.Add (entry);
}
SyncDebugDuplicates (javaDuplicates);

foreach (ModuleDebugData module in modules.Values) {
PrepareDebugMaps (module);
Expand All @@ -217,18 +222,22 @@ bool GenerateDebugFiles (bool skipJniAddNativeMethodRegistrationAttributeScan, L
return true;
}

bool GenerateDebugNativeAssembly (bool skipJniAddNativeMethodRegistrationAttributeScan, List<TypeDefinition> javaTypes, string outputDirectory, ApplicationConfigTaskState appConfState)
bool GenerateDebugNativeAssembly (bool skipJniAddNativeMethodRegistrationAttributeScan, List<TypeDefinition> javaTypes, TypeDefinitionCache cache, string outputDirectory, ApplicationConfigTaskState appConfState)
{
var javaToManaged = new List<TypeMapDebugEntry> ();
var managedToJava = new List<TypeMapDebugEntry> ();

var javaDuplicates = new Dictionary<string, List<TypeMapDebugEntry>> (StringComparer.Ordinal);
foreach (TypeDefinition td in javaTypes) {
UpdateApplicationConfig (td, appConfState);

TypeMapDebugEntry entry = GetDebugEntry (td);
HandleDebugDuplicates (javaDuplicates, entry, td, cache);

javaToManaged.Add (entry);
managedToJava.Add (entry);
}
SyncDebugDuplicates (javaDuplicates);

var data = new ModuleDebugData {
EntryCount = (uint)javaToManaged.Count,
Expand All @@ -246,6 +255,39 @@ bool GenerateDebugNativeAssembly (bool skipJniAddNativeMethodRegistrationAttribu
return true;
}

void SyncDebugDuplicates (Dictionary<string, List<TypeMapDebugEntry>> javaDuplicates)
{
foreach (List<TypeMapDebugEntry> duplicates in javaDuplicates.Values) {
if (duplicates.Count < 2) {
continue;
}

TypeMapDebugEntry template = duplicates [0];
for (int i = 1; i < duplicates.Count; i++) {
duplicates[i].TypeDefinition = template.TypeDefinition;
duplicates[i].ManagedName = template.ManagedName;
}
}
}

void HandleDebugDuplicates (Dictionary<string, List<TypeMapDebugEntry>> javaDuplicates, TypeMapDebugEntry entry, TypeDefinition td, TypeDefinitionCache cache)
{
List<TypeMapDebugEntry> duplicates;

if (!javaDuplicates.TryGetValue (entry.JavaName, out duplicates)) {
javaDuplicates.Add (entry.JavaName, new List<TypeMapDebugEntry> { entry });
} else {
duplicates.Add (entry);
TypeMapDebugEntry oldEntry = duplicates[0];
if (td.IsAbstract || td.IsInterface || oldEntry.TypeDefinition.IsAbstract || oldEntry.TypeDefinition.IsInterface) {
if (td.IsAssignableFrom (oldEntry.TypeDefinition, cache)) {
oldEntry.TypeDefinition = td;
oldEntry.ManagedName = GetManagedTypeName (td);
}
}
}
}

void PrepareDebugMaps (ModuleDebugData module)
{
module.JavaToManagedMap.Sort ((TypeMapDebugEntry a, TypeMapDebugEntry b) => String.Compare (a.JavaName, b.JavaName, StringComparison.Ordinal));
Expand All @@ -261,6 +303,15 @@ void PrepareDebugMaps (ModuleDebugData module)
}

TypeMapDebugEntry GetDebugEntry (TypeDefinition td)
{
return new TypeMapDebugEntry {
JavaName = Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager.ToJniName (td),
ManagedName = GetManagedTypeName (td),
TypeDefinition = td,
};
}

string GetManagedTypeName (TypeDefinition td)
{
// This is necessary because Mono runtime will return to us type name with a `.` for nested types (not a
// `/` or a `+`. So, for instance, a type named `DefaultRenderer` found in the
Expand All @@ -277,17 +328,13 @@ TypeMapDebugEntry GetDebugEntry (TypeDefinition td)
//
string managedTypeName = td.FullName.Replace ('/', '+');

return new TypeMapDebugEntry {
JavaName = Java.Interop.Tools.TypeNameMappings.JavaNativeTypeManager.ToJniName (td),
ManagedName = $"{managedTypeName}, {td.Module.Assembly.Name.Name}",
};
return $"{managedTypeName}, {td.Module.Assembly.Name.Name}";
}

bool GenerateRelease (bool skipJniAddNativeMethodRegistrationAttributeScan, List<TypeDefinition> javaTypes, string outputDirectory, ApplicationConfigTaskState appConfState)
{
int assemblyId = 0;
int maxJavaNameLength = 0;
int maxModuleFileNameLength = 0;
var knownAssemblies = new Dictionary<string, int> (StringComparer.Ordinal);
var tempModules = new Dictionary<byte[], ModuleReleaseData> ();
Dictionary <AssemblyDefinition, int> moduleCounter = null;
Expand Down

0 comments on commit b00d4bc

Please sign in to comment.