Skip to content

Commit 2ba8bd7

Browse files
committed
[generator] Fix invalid parsing of complex generic types.
1 parent 5136ef9 commit 2ba8bd7

File tree

3 files changed

+168
-32
lines changed

3 files changed

+168
-32
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using MonoDroid.Generation;
3+
using NUnit.Framework;
4+
5+
namespace generatortests
6+
{
7+
[TestFixture]
8+
public class CodeGenerationOptionsTests
9+
{
10+
[Test]
11+
public void GetOutputNameUseGlobal ()
12+
{
13+
var opt = new CodeGenerationOptions { UseGlobal = true };
14+
15+
Assert.AreEqual (string.Empty, opt.GetOutputName (string.Empty));
16+
Assert.AreEqual ("int", opt.GetOutputName ("int"));
17+
Assert.AreEqual ("void", opt.GetOutputName ("void"));
18+
Assert.AreEqual ("void", opt.GetOutputName ("System.Void"));
19+
Assert.AreEqual ("params int[]", opt.GetOutputName ("params int[]"));
20+
Assert.AreEqual ("params global::System.Object[]", opt.GetOutputName ("params System.Object[]"));
21+
Assert.AreEqual ("int[][][]", opt.GetOutputName ("int[][][]"));
22+
Assert.AreEqual ("global::System.Object[][][]", opt.GetOutputName ("System.Object[][][]"));
23+
24+
Assert.AreEqual ("global::System.Collections.Generic.List<string[]>",
25+
opt.GetOutputName ("System.Collections.Generic.List<string[]>"));
26+
27+
Assert.AreEqual ("global::System.Collections.Generic.Dictionary<string, string>",
28+
opt.GetOutputName ("System.Collections.Generic.Dictionary<string, string>"));
29+
30+
Assert.AreEqual ("global::System.Collections.Generic.List<global::System.Collections.Generic.List<string>>",
31+
opt.GetOutputName ("System.Collections.Generic.List<System.Collections.Generic.List<string>>"));
32+
33+
Assert.AreEqual ("global::System.Collections.Generic.List<global::System.Collections.Generic.Dictionary<string, global::System.Collections.Generic.Dictionary<global::System.Object, global::System.Object>>>",
34+
opt.GetOutputName ("System.Collections.Generic.List<System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<System.Object, System.Object>>>"));
35+
36+
Assert.AreEqual ("global::System.Collections.Generic.IList<global::Kotlin.Pair>",
37+
opt.GetOutputName ("System.Collections.Generic.IList<Kotlin.Pair>"));
38+
39+
Assert.AreEqual ("global::System.Collections.Generic.IDictionary<string, global::System.Collections.Generic.IList<global::Kotlin.Pair>>",
40+
opt.GetOutputName ("System.Collections.Generic.IDictionary<string, System.Collections.Generic.IList<Kotlin.Pair>>"));
41+
42+
Assert.AreEqual ("global::System.Collections.Generic.IDictionary<global::System.Collections.Generic.IList<string>, global::Kotlin.Pair>",
43+
opt.GetOutputName ("System.Collections.Generic.IDictionary<System.Collections.Generic.IList<string>, Kotlin.Pair>"));
44+
45+
Assert.AreEqual ("global::System.Collections.Generic.IDictionary<global::System.Collections.Generic.IList<string>, global::System.Collections.Generic.IList<global::Kotlin.Pair>>",
46+
opt.GetOutputName ("System.Collections.Generic.IDictionary<System.Collections.Generic.IList<string>, System.Collections.Generic.IList<Kotlin.Pair>>"));
47+
48+
Assert.AreEqual ("global::System.Collections.Generic.List<global::System.Collections.Generic.List<string>[]>[]",
49+
opt.GetOutputName ("System.Collections.Generic.List<System.Collections.Generic.List<string>[]>[]"));
50+
51+
Assert.AreEqual ("global::System.Collections.Generic.List<global::System.Collections.Generic.List<string>.Enumerator[]>",
52+
opt.GetOutputName ("System.Collections.Generic.List<System.Collections.Generic.List<string>.Enumerator[]>"));
53+
}
54+
}
55+
}

tools/generator/CodeGenerationOptions.cs

+10-32
Original file line numberDiff line numberDiff line change
@@ -212,42 +212,20 @@ internal IEnumerable<string> GetJniMarshalDelegates ()
212212
return jni_marshal_delegates;
213213
}
214214

215-
public string GetOutputName (string s)
215+
public string GetOutputName (string type)
216216
{
217-
if (s == "System.Void")
217+
// Handle a few special cases
218+
if (type == "System.Void")
218219
return "void";
219-
if (s.StartsWith ("params "))
220-
return "params " + GetOutputName (s.Substring ("params ".Length));
221-
if (s.StartsWith ("global::"))
220+
if (type.StartsWith ("params "))
221+
return "params " + GetOutputName (type.Substring ("params ".Length));
222+
if (type.StartsWith ("global::"))
222223
Report.LogCodedError (Report.ErrorUnexpectedGlobal);
223224
if (!UseGlobal)
224-
return s;
225-
int idx = s.IndexOf ('<');
226-
if (idx < 0) {
227-
if (s.IndexOf ('.') < 0)
228-
return s; // hack, to prevent things like global::int
229-
return "global::" + s;
230-
}
231-
int idx2 = s.LastIndexOf ('>');
232-
string sub = s.Substring (idx + 1, idx2 - idx - 1);
233-
var typeParams = new List<string> ();
234-
while (true) {
235-
int idx3 = sub.IndexOf ('<');
236-
int idx4 = sub.IndexOf (',');
237-
if (idx4 < 0) {
238-
typeParams.Add (GetOutputName (sub));
239-
break;
240-
} else if (idx3 < 0 || idx4 < idx3) { // more than one type params.
241-
typeParams.Add (GetOutputName (sub.Substring (0, idx4)));
242-
if (idx4 + 1 == sub.Length)
243-
break;
244-
sub = sub.Substring (idx4 + 1).Trim ();
245-
} else {
246-
typeParams.Add (GetOutputName (sub));
247-
break;
248-
}
249-
}
250-
return GetOutputName (s.Substring (0, idx)) + '<' + String.Join (", ", typeParams.ToArray ()) + '>';
225+
return type;
226+
227+
// Add "global::" in front of types
228+
return ParsedType.Parse (type).ToString (UseGlobal);
251229
}
252230

253231
public string GetSafeIdentifier (string name)
+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace MonoDroid.Generation
5+
{
6+
// Parses a type string into its type and optionally its generic type arguments
7+
// ex: Dictionary<string, List<string>>
8+
// -> Type: Dictionary<{0}>
9+
// - GenericArguments:
10+
// - Type: string
11+
// - Type: List<{0}>
12+
// - GenericArguments:
13+
// - Type: string
14+
// A placeholder "{0}" is added because the type may extend past the generics:
15+
// ex: "List<string>.Enumerator[]" becomes "List<{0}>.Enumerator[]"
16+
public class ParsedType
17+
{
18+
public string Type { get; set; }
19+
public List<ParsedType> GenericArguments { get; } = new List<ParsedType> ();
20+
public bool HasGenerics => GenericArguments.Count > 0;
21+
22+
ParsedType () { }
23+
24+
public static ParsedType Parse (string type)
25+
{
26+
var less_than = type.IndexOf ('<');
27+
28+
// No generics
29+
if (less_than < 0)
30+
return new ParsedType { Type = type };
31+
32+
var greater_than = type.LastIndexOf ('>');
33+
var type_args = type.Substring (less_than + 1, greater_than - less_than - 1);
34+
var type_string = type.Substring (0, less_than) + "<{0}>" + (greater_than + 1 < type.Length ? type.Substring (greater_than + 1) : string.Empty);
35+
36+
var parsed_args = ParseTypeList (type_args);
37+
38+
var t = new ParsedType { Type = type_string };
39+
40+
foreach (var p in parsed_args)
41+
t.GenericArguments.Add (Parse (p));
42+
43+
return t;
44+
}
45+
46+
public override string ToString ()
47+
{
48+
return ToString (false);
49+
}
50+
51+
public string ToString (bool useGlobal = false)
52+
{
53+
var type = (useGlobal && Type.IndexOf ('.') >= 0 ? "global::" : string.Empty) + Type;
54+
55+
if (!HasGenerics)
56+
return type;
57+
58+
return type.Replace ("{0}", string.Join (", ", GenericArguments.Select (p => p.ToString (useGlobal))));
59+
}
60+
61+
static List<string> ParseTypeList (string type)
62+
{
63+
var list = new List<string> ();
64+
65+
// Only one type
66+
if (type.IndexOf (',') < 0) {
67+
list.Add (type);
68+
return list;
69+
}
70+
71+
// Remove any whitespace
72+
type = type.Replace (" ", "");
73+
74+
var start = 0;
75+
var counter = -1;
76+
var depth = 0;
77+
78+
while (++counter < type.Length) {
79+
if (type [counter] == '<') {
80+
depth++;
81+
continue;
82+
}
83+
84+
if (type [counter] == '>') {
85+
depth--;
86+
continue;
87+
}
88+
89+
// This is a list separator, add the previous type
90+
if (depth == 0 && type [counter] == ',') {
91+
list.Add (type.Substring (start, counter - start));
92+
start = counter + 1;
93+
continue;
94+
}
95+
}
96+
97+
// Add the final type
98+
list.Add (type.Substring (start, counter - start));
99+
100+
return list;
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)