Skip to content

Commit 5009f32

Browse files
jpobstjonpryor
authored andcommitted
[generator] Improve generic type lookup (#552)
Fixes: #543 In commit 262743b we began stripping the arity from Cecil imported types so that we wouldn't write invalid types like ``System.Collections.Generic.IList`1<string>``, in order to correctly emit `System.Collections.Generic.IList<string>`. However when we try to resolve these types in the `SymbolTable` we only look at the type name, ignoring the generic type parameters. In this case we are looking for `System.Collections.Generic.IList` but the type is stored in the symbol table with the arity to disambiguate types like ``Action`1`` and ``Action`2``. Therefore our lookup fails because the table key is ``System.Collections.Generic.IList`1``. For example, if we have `LibraryA.dll` with: public class Foo : Java.Lang.Object { public virtual void Bar (IList<string> p0) {…} } And we attempt to bind a `LibraryB.jar` which references `LibraryA.dll`, overriding `Foo.Bar()`: public class Foo2 : Foo { public override void Bar (IList<string> p0) {…} } Then `Foo2.Bar()` would elicit a BG8800 warning: warning BG8800: Unknown parameter type System.Collections.Generic.IList<System.String> in method Bar in managed type Foo2. Fix type lookup so that if the type is not found in the `SymbolTable` and there are generic type parameters, calculate the arity from the generic type parameters and look in the table again. That is, `System.Collections.Generic.IList<string>` is rechecked as ``System.Collections.Generic.IList`1``, which allows the type to be found.
1 parent 3bf5333 commit 5009f32

File tree

2 files changed

+75
-2
lines changed

2 files changed

+75
-2
lines changed

tools/generator/Java.Interop.Tools.Generator.ObjectModel/Symbols/SymbolTable.cs

+35-2
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,15 @@ public ISymbol Lookup (string java_type)
260260
if (all_symbols_cache == null)
261261
all_symbols_cache = new ConcurrentDictionary<string, ISymbol> (symbols.Values.SelectMany (v => v).GroupBy (s => s.FullName).ToDictionary (s => s.Key, s => s.FirstOrDefault ()));
262262

263-
all_symbols_cache.TryGetValue (key, out sym);
263+
if (!all_symbols_cache.TryGetValue (key, out sym)) {
264+
// We may be looking for a type like:
265+
// - System.Collections.Generic.IList<Java.Util.Locale.LanguageRange>
266+
// Our key is "System.Collections.Generic.IList", but it's stored in
267+
// the symbol table with the arity so we need to look for
268+
// "System.Collections.Generic.IList`1" to find a match
269+
key = AddArity (key, type_params);
270+
all_symbols_cache.TryGetValue (key, out sym);
271+
}
264272
}
265273
}
266274
ISymbol result;
@@ -290,7 +298,32 @@ public ISymbol Lookup (string java_type)
290298

291299
return result;
292300
}
293-
301+
302+
private string AddArity (string key, string typeParams)
303+
{
304+
if (string.IsNullOrWhiteSpace (typeParams) || !typeParams.StartsWith ("<") || !typeParams.EndsWith (">"))
305+
return key;
306+
307+
var nested_count = 0;
308+
var arity = 1;
309+
310+
// Remove the outer <>
311+
typeParams = typeParams.Substring (1, typeParams.Length - 2);
312+
313+
foreach (var c in typeParams) {
314+
if (c == '>')
315+
nested_count--;
316+
317+
if (c == '<')
318+
nested_count++;
319+
320+
if (nested_count == 0 && c == ',')
321+
arity++;
322+
}
323+
324+
return $"{key}`{arity}";
325+
}
326+
294327
public void Dump ()
295328
{
296329
foreach (var p in symbols) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using MonoDroid.Generation;
3+
using NUnit.Framework;
4+
5+
namespace generatortests
6+
{
7+
[TestFixture]
8+
public class SymbolTableTests
9+
{
10+
[Test]
11+
public void FindGenericTypes ()
12+
{
13+
var table = new SymbolTable ();
14+
15+
var list = new InterfaceGen (new GenBaseSupport {
16+
Name = "System.Collections.Generic.IList`1",
17+
FullName = "System.Collections.Generic.IList`1",
18+
JavaSimpleName = "System.Collections.Generic.IList`1"
19+
});
20+
21+
table.AddType (list);
22+
23+
var dict = new InterfaceGen (new GenBaseSupport {
24+
Name = "System.Collections.Generic.IDictionary`2",
25+
FullName = "System.Collections.Generic.IDictionary`2",
26+
JavaSimpleName = "System.Collections.Generic.IDictionary`2"
27+
});
28+
29+
table.AddType (dict);
30+
31+
Assert.AreEqual ("System.Collections.Generic.IList`1", table.Lookup ("System.Collections.Generic.IList<Java.Util.Locale.LanguageRange>").FullName);
32+
Assert.AreEqual ("System.Collections.Generic.IList`1", table.Lookup ("System.Collections.Generic.IList<List<Java.Util.Locale.LanguageRange>>").FullName);
33+
34+
Assert.AreEqual ("System.Collections.Generic.IDictionary`2", table.Lookup ("System.Collections.Generic.IDictionary<string, Java.Util.Locale.LanguageRange>").FullName);
35+
Assert.AreEqual ("System.Collections.Generic.IDictionary`2", table.Lookup ("System.Collections.Generic.IDictionary<string, List<Java.Util.Locale.LanguageRange>>").FullName);
36+
37+
Assert.AreEqual ("System.Collections.Generic.IList`1", table.Lookup ("System.Collections.Generic.IList<Dictionary<string, Java.Util.Locale.LanguageRange>>").FullName);
38+
}
39+
}
40+
}

0 commit comments

Comments
 (0)