Skip to content

Commit d7dfa0b

Browse files
jonpryoratsushieno
authored andcommitted
[generator] Better support aliased types (dotnet#173)
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.
1 parent 0ff255e commit d7dfa0b

File tree

1 file changed

+25
-14
lines changed

1 file changed

+25
-14
lines changed

tools/generator/SymbolTable.cs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace MonoDroid.Generation {
77

88
public static class SymbolTable {
99

10-
static Dictionary<string, ISymbol> symbols = new Dictionary<string, ISymbol> ();
10+
static Dictionary<string, List<ISymbol>> symbols = new Dictionary<string, List<ISymbol>> ();
1111
static ISymbol char_seq;
1212
static ISymbol fileinstream_sym;
1313
static ISymbol fileoutstream_sym;
@@ -32,7 +32,7 @@ public static class SymbolTable {
3232

3333
public static IEnumerable<ISymbol> AllRegisteredSymbols ()
3434
{
35-
return symbols.Values;
35+
return symbols.Values.SelectMany (v => v);
3636
}
3737

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

124-
if (!ShouldAddType (key))
125-
return;
126-
127-
symbols [key] = symbol;
124+
AddType (key, symbol);
128125
}
129126

130127
static bool ShouldAddType (string key)
@@ -138,10 +135,14 @@ static bool ShouldAddType (string key)
138135

139136
public static void AddType (string key, ISymbol symbol)
140137
{
141-
if (!ShouldAddType (key))
142-
return;
143-
144-
symbols [key] = symbol;
138+
List<ISymbol> values;
139+
if (!symbols.TryGetValue (key, out values)) {
140+
symbols.Add (key, new List<ISymbol> { symbol });
141+
}
142+
else {
143+
if (!values.Any (v => object.ReferenceEquals (v, symbol)))
144+
values.Add (symbol);
145+
}
145146
}
146147

147148
public static string FilterPrimitiveFullName (string s)
@@ -254,8 +255,14 @@ public static ISymbol Lookup (string java_type)
254255
int ar;
255256
bool he;
256257
string key = GetSymbolInfo (java_type, out type_params, out ar, out he);
258+
ISymbol sym;
259+
List<ISymbol> values;
260+
if (symbols.TryGetValue (key, out values)) {
261+
sym = values [values.Count-1];
262+
} else {
263+
sym = AllRegisteredSymbols ().FirstOrDefault (v => v.FullName == key);
264+
}
257265
ISymbol result;
258-
ISymbol sym = symbols.ContainsKey (key) ? symbols [key] : symbols.FirstOrDefault (s => s.Value.FullName == key).Value;
259266
if (sym != null) {
260267
if (type_params.Length > 0) {
261268
GenBase gen = sym as GenBase;
@@ -279,9 +286,13 @@ public static ISymbol Lookup (string java_type)
279286

280287
public static void Dump ()
281288
{
282-
foreach (var p in symbols)
283-
if (!p.Key.StartsWith ("System"))
284-
Console.Error.WriteLine ("[{0}]: {1} {2}", p.Key, p.Value.GetType (), p.Value.FullName);
289+
foreach (var p in symbols) {
290+
if (p.Key.StartsWith ("System"))
291+
continue;
292+
foreach (var s in p.Value) {
293+
Console.Error.WriteLine ("[{0}]: {1} {2}", p.Key, s.GetType (), s.FullName);
294+
}
295+
}
285296
}
286297

287298
public static string GetNativeName (string name)

0 commit comments

Comments
 (0)