Skip to content

Commit

Permalink
[generator] Better support aliased types
Browse files Browse the repository at this point in the history
Fixes: https://bugzilla.xamarin.com/show_bug.cgi?id=56436

`generator` relies on the existance of a mapping from Java types to
managed types which bind the Java type. The `generator --ref` option
will cause `generator` to make this mapping based on the managed types
from an assembly, and the mapping in turn is handled by `[Register]`
and related custom attributes:

	namespace Android.Runtime {
	  [Register ("java/util/Collection", DoNotRegisterAcw=true)]
	  partial class JavaCollection {
	  }
	}

For better or worse, nothing enforces that there be only one such
mapping, It is thus possible to have multiple types partake in the
mapping process:

	namespace Java.Util {
	  [Register ("java/util/Collection", DoNotRegisterAcw=true)]
	  partial interface ICollection {
	  }
	}

A Java type is *aliased* when two or more managed types bind the same
Java type, as with the above `Android.Runtime.JavaCollection` and
`Java.Util.ICollection` types;

This is a scenario which as long existed.

Question: What does `generator` do when a type is aliased?
Answer: `generator` registers mappings from assemblies in the order of
types in the assembly it's processing. When an aliased type is found,
the *last registered type* "wins".

For example, if an assembly defines `JavaCollection` before
`ICollection`, then a type lookup for `java.util.Collection` will
return `ICollection`. If instead the assembly defines `ICollection`
before `JavaCollection`, then a type lookup for `java.util.Collection`
willl instead return `JavaCollection`.

This is not necessarily desirable, but I don't see much alternative.

Unfortunately, that's only *half* the scenario. There are a number of
situations in which `SymbolTable.Lookup()` is not given a Java name,
but is instead given the *managed binding type name*. In particular,
this happens when `ManagedClassGen.Validate()` is called, and it
attempts to validate the base class and all implemented interfaces,
using the *managed* type names for this process.

Here is where the "last registered type wins" behavior becomes
problematic, as it means that a Lookup for `Java.Util.ICollection` can
fail when `JavaCollection` is registered after `ICollection`, because
the Java name was shared.

Which brings us to Bug #56436: building a Xamarin.Android binding
project changes behavior based on whether Xamarin.Android 7.2 or
Xamarin.Android 7.3 is used, and the primary difference between these
differences is the order that types are defined in `Mono.Android.dll`.
In XA 7.2, `JavaCollection` is defined before `ICollection`.
In XA 7.3, `ICollection` is defined before `JavaCollection`.
This (subtle!) change interacts with `generator`, and within XA 7.3
means that `Java.Util.ICollection` can't be found:

	warning BG8C00: For type Java.Util.AbstractQueue, base interface Java.Util.ICollection does not exist.

Improve this state of affairs by changing `SymbolTable.symbols` from a
`Dictionary<string, ISymbol>` to a `Dictionary<string, List<ISymbol>>`,
allowing `generator` to preserve all the managed types which bind a
given Java type. This in turn allows *both* `ICollection` and
`JavaCollection` to be preserved, so that a lookup for
`Java.Util.ICollection` will now work consistently, without any type
definition ordering issues.

Unfortunately lookups for the Java type `java.util.Collection` will
still be order-dependent, continuing to return the *last* registered
type.  Hopefully this won't cause any problems.
  • Loading branch information
jonpryor committed Aug 4, 2017
1 parent 0ff255e commit 2194c90
Showing 1 changed file with 25 additions and 14 deletions.
39 changes: 25 additions & 14 deletions tools/generator/SymbolTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace MonoDroid.Generation {

public static class SymbolTable {

static Dictionary<string, ISymbol> symbols = new Dictionary<string, ISymbol> ();
static Dictionary<string, List<ISymbol>> symbols = new Dictionary<string, List<ISymbol>> ();
static ISymbol char_seq;
static ISymbol fileinstream_sym;
static ISymbol fileoutstream_sym;
Expand All @@ -32,7 +32,7 @@ public static class SymbolTable {

public static IEnumerable<ISymbol> AllRegisteredSymbols ()
{
return symbols.Values;
return symbols.Values.SelectMany (v => v);
}

static SymbolTable ()
Expand Down Expand Up @@ -121,10 +121,7 @@ public static void AddType (ISymbol symbol)
bool he;
string key = symbol.IsEnum ? symbol.FullName : GetSymbolInfo (symbol.JavaName, out dummy, out ar, out he);

if (!ShouldAddType (key))
return;

symbols [key] = symbol;
AddType (key, symbol);
}

static bool ShouldAddType (string key)
Expand All @@ -138,10 +135,14 @@ static bool ShouldAddType (string key)

public static void AddType (string key, ISymbol symbol)
{
if (!ShouldAddType (key))
return;

symbols [key] = symbol;
List<ISymbol> values;
if (!symbols.TryGetValue (key, out values)) {
symbols.Add (key, new List<ISymbol> { symbol });
}
else {
if (!values.Any (v => object.ReferenceEquals (v, symbol)))
values.Add (symbol);
}
}

public static string FilterPrimitiveFullName (string s)
Expand Down Expand Up @@ -254,8 +255,14 @@ public static ISymbol Lookup (string java_type)
int ar;
bool he;
string key = GetSymbolInfo (java_type, out type_params, out ar, out he);
ISymbol sym;
List<ISymbol> values;
if (symbols.TryGetValue (key, out values)) {
sym = values [values.Count-1];
} else {
sym = AllRegisteredSymbols ().FirstOrDefault (v => v.FullName == key);
}
ISymbol result;
ISymbol sym = symbols.ContainsKey (key) ? symbols [key] : symbols.FirstOrDefault (s => s.Value.FullName == key).Value;
if (sym != null) {
if (type_params.Length > 0) {
GenBase gen = sym as GenBase;
Expand All @@ -279,9 +286,13 @@ public static ISymbol Lookup (string java_type)

public static void Dump ()
{
foreach (var p in symbols)
if (!p.Key.StartsWith ("System"))
Console.Error.WriteLine ("[{0}]: {1} {2}", p.Key, p.Value.GetType (), p.Value.FullName);
foreach (var p in symbols) {
if (p.Key.StartsWith ("System"))
continue;
foreach (var s in p.Value) {
Console.Error.WriteLine ("[{0}]: {1} {2}", p.Key, s.GetType (), s.FullName);
}
}
}

public static string GetNativeName (string name)
Expand Down

0 comments on commit 2194c90

Please sign in to comment.