IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
TypeNameParser.cs
Go to the documentation of this file.
1/*
2 Copyright (C) 2009-2012 Jeroen Frijters
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
19
20 Jeroen Frijters
21 jeroen@frijters.net
22
23*/
24using System;
25using System.Diagnostics;
26using System.Text;
27
28namespace IKVM.Reflection
29{
30
32 {
33
34 const string SpecialChars = "\\+,[]*&";
35 const short SZARRAY = -1;
36 const short BYREF = -2;
37 const short POINTER = -3;
38
39 readonly string name;
40 readonly string[] nested;
41 readonly string assemblyName;
42 readonly short[] modifiers;
44
45 internal static string Escape(string name)
46 {
47 if (name == null)
48 return null;
49
50 StringBuilder sb = null;
51 for (int pos = 0; pos < name.Length; pos++)
52 {
53 var c = name[pos];
54 switch (c)
55 {
56 case '\\':
57 case '+':
58 case ',':
59 case '[':
60 case ']':
61 case '*':
62 case '&':
63 sb ??= new StringBuilder(name, 0, pos, name.Length + 3);
64 sb.Append("\\").Append(c);
65 break;
66 default:
67 sb?.Append(c);
68 break;
69 }
70 }
71
72 return sb != null ? sb.ToString() : name;
73 }
74
75 internal static string Unescape(string name)
76 {
77 int pos = name.IndexOf('\\');
78 if (pos == -1)
79 return name;
80
81 var sb = new StringBuilder(name, 0, pos, name.Length - 1);
82 for (; pos < name.Length; pos++)
83 {
84 var c = name[pos];
85 if (c == '\\')
86 c = name[++pos];
87
88 sb.Append(c);
89 }
90
91 return sb.ToString();
92 }
93
94 internal static TypeNameParser Parse(string typeName, bool throwOnError)
95 {
96 if (throwOnError)
97 {
98 var parser = new Parser(typeName);
99 return new TypeNameParser(ref parser, true);
100 }
101 else
102 {
103 try
104 {
105 var parser = new Parser(typeName);
106 return new TypeNameParser(ref parser, true);
107 }
108 catch (ArgumentException)
109 {
110 return new TypeNameParser();
111 }
112 }
113 }
114
115 TypeNameParser(ref Parser parser, bool withAssemblyName)
116 {
117 var genericParameter = parser.pos != 0;
118 name = parser.NextNamePart();
119
120 nested = null;
121 parser.ParseNested(ref nested);
122
123 genericParameters = null;
124 parser.ParseGenericParameters(ref genericParameters);
125
126 modifiers = null;
127 parser.ParseModifiers(ref modifiers);
128
129 assemblyName = null;
130 if (withAssemblyName)
131 parser.ParseAssemblyName(genericParameter, ref assemblyName);
132 }
133
134 internal readonly bool Error => name == null;
135
136 internal readonly string FirstNamePart => name;
137
138 internal readonly string AssemblyName => assemblyName;
139
140 private struct Parser
141 {
142
143 readonly string typeName;
144 internal int pos;
145
150 internal Parser(string typeName)
151 {
152 this.typeName = typeName;
153 this.pos = 0;
154 }
155
156 void Check(bool condition)
157 {
158 if (!condition)
159 throw new ArgumentException("Invalid type name '" + typeName + "'");
160 }
161
162 void Consume(char c)
163 {
164 Check(pos < typeName.Length && typeName[pos++] == c);
165 }
166
167 bool TryConsume(char c)
168 {
169 if (pos < typeName.Length && typeName[pos] == c)
170 {
171 pos++;
172 return true;
173 }
174 else
175 {
176 return false;
177 }
178 }
179
180 internal string NextNamePart()
181 {
182 SkipWhiteSpace();
183
184 int start = pos;
185 for (; pos < typeName.Length; pos++)
186 {
187 var c = typeName[pos];
188 if (c == '\\')
189 {
190 pos++;
191 Check(pos < typeName.Length && SpecialChars.IndexOf(typeName[pos]) != -1);
192 }
193 else if (SpecialChars.IndexOf(c) != -1)
194 {
195 break;
196 }
197 }
198
199 Check(pos - start != 0);
200 if (start == 0 && pos == typeName.Length)
201 return typeName;
202 else
203 return typeName.Substring(start, pos - start);
204 }
205
206 internal void ParseNested(ref string[] nested)
207 {
208 while (TryConsume('+'))
209 Add(ref nested, NextNamePart());
210 }
211
212 internal void ParseGenericParameters(ref TypeNameParser[] genericParameters)
213 {
214 int saved = pos;
215 if (TryConsume('['))
216 {
217 SkipWhiteSpace();
218
219 if (TryConsume(']') || TryConsume('*') || TryConsume(','))
220 {
221 // it's not a generic parameter list, but an array instead
222 pos = saved;
223 return;
224 }
225
226 do
227 {
228 SkipWhiteSpace();
229
230 if (TryConsume('['))
231 {
232 Add(ref genericParameters, new TypeNameParser(ref this, true));
233 Consume(']');
234 }
235 else
236 {
237 Add(ref genericParameters, new TypeNameParser(ref this, false));
238 }
239 }
240
241 while (TryConsume(','));
242
243 Consume(']');
244 SkipWhiteSpace();
245 }
246 }
247
248 internal void ParseModifiers(ref short[] modifiers)
249 {
250 while (pos < typeName.Length)
251 {
252 switch (typeName[pos])
253 {
254 case '*':
255 pos++;
256 Add(ref modifiers, POINTER);
257 break;
258 case '&':
259 pos++;
260 Add(ref modifiers, BYREF);
261 break;
262 case '[':
263 pos++;
264 Add(ref modifiers, ParseArray());
265 Consume(']');
266 break;
267 default:
268 return;
269 }
270
271 SkipWhiteSpace();
272 }
273 }
274
275 internal void ParseAssemblyName(bool genericParameter, ref string assemblyName)
276 {
277 if (pos < typeName.Length)
278 {
279 if (typeName[pos] == ']' && genericParameter)
280 {
281 // ok
282 }
283 else
284 {
285 Consume(',');
286 SkipWhiteSpace();
287
288 if (genericParameter)
289 {
290 int start = pos;
291 while (pos < typeName.Length)
292 {
293 char c = typeName[pos];
294 if (c == '\\')
295 {
296 pos++;
297 // a backslash itself is not legal in an assembly name, so we don't need to check for an escaped backslash
298 Check(pos < typeName.Length && typeName[pos++] == ']');
299 }
300 else if (c == ']')
301 {
302 break;
303 }
304 else
305 {
306 pos++;
307 }
308 }
309 Check(pos < typeName.Length && typeName[pos] == ']');
310 assemblyName = typeName.Substring(start, pos - start).Replace("\\]", "]");
311 }
312 else
313 {
314 // only when an assembly name is used in a generic type parameter, will it be escaped
315 assemblyName = typeName.Substring(pos);
316 }
317 Check(assemblyName.Length != 0);
318 }
319 }
320 else
321 {
322 Check(!genericParameter);
323 }
324 }
325
326 short ParseArray()
327 {
328 SkipWhiteSpace();
329 Check(pos < typeName.Length);
330
331 var c = typeName[pos];
332 if (c == ']')
333 {
334 return SZARRAY;
335 }
336 else if (c == '*')
337 {
338 pos++;
339 SkipWhiteSpace();
340 return 1;
341 }
342 else
343 {
344 short rank = 1;
345 while (TryConsume(','))
346 {
347 Check(rank < short.MaxValue);
348 rank++;
349 SkipWhiteSpace();
350 }
351
352 return rank;
353 }
354 }
355
356 void SkipWhiteSpace()
357 {
358 while (pos < typeName.Length && Char.IsWhiteSpace(typeName[pos]))
359 pos++;
360 }
361
362 static void Add<T>(ref T[] array, T elem)
363 {
364 if (array == null)
365 {
366 array = new T[] { elem };
367 return;
368 }
369
370 Array.Resize(ref array, array.Length + 1);
371 array[array.Length - 1] = elem;
372 }
373 }
374
375 internal Type GetType(Universe universe, Module context, bool throwOnError, string originalName, bool resolve, bool ignoreCase)
376 {
377 Debug.Assert(!resolve || !ignoreCase);
378 var name = TypeName.Split(this.name);
379
380 Type type;
381 if (assemblyName != null)
382 {
383 Assembly asm = universe.Load(assemblyName, context, throwOnError);
384 if (asm == null)
385 {
386 return null;
387 }
388 if (resolve)
389 {
390 type = asm.ResolveType(context, name);
391 }
392 else if (ignoreCase)
393 {
394 type = asm.FindTypeIgnoreCase(name.ToLowerInvariant());
395 }
396 else
397 {
398 type = asm.FindType(name);
399 }
400 }
401 else if (context == null)
402 {
403 if (resolve)
404 {
405 type = universe.CoreLib.ResolveType(context, name);
406 }
407 else if (ignoreCase)
408 {
409 type = universe.CoreLib.FindTypeIgnoreCase(name.ToLowerInvariant());
410 }
411 else
412 {
413 type = universe.CoreLib.FindType(name);
414 }
415 }
416 else
417 {
418 if (ignoreCase)
419 {
420 name = name.ToLowerInvariant();
421 type = context.FindTypeIgnoreCase(name);
422 }
423 else
424 {
425 type = context.FindType(name);
426 }
427 if (type == null && context != universe.CoreLib.ManifestModule)
428 {
429 if (ignoreCase)
430 {
431 type = universe.CoreLib.FindTypeIgnoreCase(name);
432 }
433 else
434 {
435 type = universe.CoreLib.FindType(name);
436 }
437 }
438 if (type == null && resolve)
439 {
440 if (universe.CoreLib.__IsMissing && !context.__IsMissing)
441 {
442 type = universe.CoreLib.ResolveType(context, name);
443 }
444 else
445 {
446 type = context.Assembly.ResolveType(context, name);
447 }
448 }
449 }
450
451 return Expand(type, context, throwOnError, originalName, resolve, ignoreCase);
452 }
453
454 internal Type Expand(Type type, Module context, bool throwOnError, string originalName, bool resolve, bool ignoreCase)
455 {
456 Debug.Assert(!resolve || !ignoreCase);
457
458 if (type == null)
459 {
460 if (throwOnError)
461 throw new TypeLoadException(originalName);
462
463 return null;
464 }
465
466 if (nested != null)
467 {
468 Type outer;
469 foreach (string nest in nested)
470 {
471 outer = type;
472 var name = TypeName.Split(TypeNameParser.Unescape(nest));
473 type = ignoreCase ? outer.FindNestedTypeIgnoreCase(name.ToLowerInvariant()) : outer.FindNestedType(name);
474 if (type == null)
475 {
476 if (resolve)
477 type = outer.Module.Universe.GetMissingTypeOrThrow(context, outer.Module, outer, name);
478 else if (throwOnError)
479 throw new TypeLoadException(originalName);
480 else
481 return null;
482 }
483 }
484 }
485
486 if (genericParameters != null)
487 {
488 var typeArgs = new Type[genericParameters.Length];
489 for (int i = 0; i < typeArgs.Length; i++)
490 {
491 typeArgs[i] = genericParameters[i].GetType(type.Assembly.Universe, context, throwOnError, originalName, resolve, ignoreCase);
492 if (typeArgs[i] == null)
493 return null;
494 }
495
496 type = type.MakeGenericType(typeArgs);
497 }
498
499 if (modifiers != null)
500 {
501 foreach (var modifier in modifiers)
502 {
503 type = modifier switch
504 {
505 SZARRAY => type.MakeArrayType(),
506 BYREF => type.MakeByRefType(),
507 POINTER => type.MakePointerType(),
508 _ => type.MakeArrayType(modifier),
509 };
510 }
511 }
512
513 return type;
514 }
515
516 }
517
518}
IKVM.Reflection.Module Module
IKVM.Reflection.Type Type
IKVM.Reflection.Assembly Assembly
TypeNameParser(ref Parser parser, bool withAssemblyName)
readonly TypeNameParser[] genericParameters