mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
549 lines
12 KiB
549 lines
12 KiB
// |
|
// TypeParser.cs |
|
// |
|
// Author: |
|
// Jb Evain (jbevain@gmail.com) |
|
// |
|
// Copyright (c) 2008 - 2011 Jb Evain |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining |
|
// a copy of this software and associated documentation files (the |
|
// "Software"), to deal in the Software without restriction, including |
|
// without limitation the rights to use, copy, modify, merge, publish, |
|
// distribute, sublicense, and/or sell copies of the Software, and to |
|
// permit persons to whom the Software is furnished to do so, subject to |
|
// the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be |
|
// included in all copies or substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
// |
|
|
|
using System; |
|
using System.Text; |
|
|
|
using Mono.Cecil.Metadata; |
|
|
|
namespace Mono.Cecil { |
|
|
|
class TypeParser { |
|
|
|
class Type { |
|
public const int Ptr = -1; |
|
public const int ByRef = -2; |
|
public const int SzArray = -3; |
|
|
|
public string type_fullname; |
|
public string [] nested_names; |
|
public int arity; |
|
public int [] specs; |
|
public Type [] generic_arguments; |
|
public string assembly; |
|
} |
|
|
|
readonly string fullname; |
|
readonly int length; |
|
|
|
int position; |
|
|
|
TypeParser (string fullname) |
|
{ |
|
this.fullname = fullname; |
|
this.length = fullname.Length; |
|
} |
|
|
|
Type ParseType (bool fq_name) |
|
{ |
|
var type = new Type (); |
|
type.type_fullname = ParsePart (); |
|
|
|
type.nested_names = ParseNestedNames (); |
|
|
|
if (TryGetArity (type)) |
|
type.generic_arguments = ParseGenericArguments (type.arity); |
|
|
|
type.specs = ParseSpecs (); |
|
|
|
if (fq_name) |
|
type.assembly = ParseAssemblyName (); |
|
|
|
return type; |
|
} |
|
|
|
static bool TryGetArity (Type type) |
|
{ |
|
int arity = 0; |
|
|
|
TryAddArity (type.type_fullname, ref arity); |
|
|
|
var nested_names = type.nested_names; |
|
if (!nested_names.IsNullOrEmpty ()) { |
|
for (int i = 0; i < nested_names.Length; i++) |
|
TryAddArity (nested_names [i], ref arity); |
|
} |
|
|
|
type.arity = arity; |
|
return arity > 0; |
|
} |
|
|
|
static bool TryGetArity (string name, out int arity) |
|
{ |
|
arity = 0; |
|
var index = name.LastIndexOf ('`'); |
|
if (index == -1) |
|
return false; |
|
|
|
return ParseInt32 (name.Substring (index + 1), out arity); |
|
} |
|
|
|
static bool ParseInt32 (string value, out int result) |
|
{ |
|
#if CF |
|
try { |
|
result = int.Parse (value); |
|
return true; |
|
} catch { |
|
result = 0; |
|
return false; |
|
} |
|
#else |
|
return int.TryParse (value, out result); |
|
#endif |
|
} |
|
|
|
static void TryAddArity (string name, ref int arity) |
|
{ |
|
int type_arity; |
|
if (!TryGetArity (name, out type_arity)) |
|
return; |
|
|
|
arity += type_arity; |
|
} |
|
|
|
string ParsePart () |
|
{ |
|
int start = position; |
|
while (position < length && !IsDelimiter (fullname [position])) |
|
position++; |
|
|
|
return fullname.Substring (start, position - start); |
|
} |
|
|
|
static bool IsDelimiter (char chr) |
|
{ |
|
return "+,[]*&".IndexOf (chr) != -1; |
|
} |
|
|
|
void TryParseWhiteSpace () |
|
{ |
|
while (position < length && Char.IsWhiteSpace (fullname [position])) |
|
position++; |
|
} |
|
|
|
string [] ParseNestedNames () |
|
{ |
|
string [] nested_names = null; |
|
while (TryParse ('+')) |
|
Add (ref nested_names, ParsePart ()); |
|
|
|
return nested_names; |
|
} |
|
|
|
bool TryParse (char chr) |
|
{ |
|
if (position < length && fullname [position] == chr) { |
|
position++; |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
static void Add<T> (ref T [] array, T item) |
|
{ |
|
if (array == null) { |
|
array = new [] { item }; |
|
return; |
|
} |
|
|
|
array = array.Resize (array.Length + 1); |
|
array [array.Length - 1] = item; |
|
} |
|
|
|
int [] ParseSpecs () |
|
{ |
|
int [] specs = null; |
|
|
|
while (position < length) { |
|
switch (fullname [position]) { |
|
case '*': |
|
position++; |
|
Add (ref specs, Type.Ptr); |
|
break; |
|
case '&': |
|
position++; |
|
Add (ref specs, Type.ByRef); |
|
break; |
|
case '[': |
|
position++; |
|
switch (fullname [position]) { |
|
case ']': |
|
position++; |
|
Add (ref specs, Type.SzArray); |
|
break; |
|
case '*': |
|
position++; |
|
Add (ref specs, 1); |
|
break; |
|
default: |
|
var rank = 1; |
|
while (TryParse (',')) |
|
rank++; |
|
|
|
Add (ref specs, rank); |
|
|
|
TryParse (']'); |
|
break; |
|
} |
|
break; |
|
default: |
|
return specs; |
|
} |
|
} |
|
|
|
return specs; |
|
} |
|
|
|
Type [] ParseGenericArguments (int arity) |
|
{ |
|
Type [] generic_arguments = null; |
|
|
|
if (position == length || fullname [position] != '[') |
|
return generic_arguments; |
|
|
|
TryParse ('['); |
|
|
|
for (int i = 0; i < arity; i++) { |
|
var fq_argument = TryParse ('['); |
|
Add (ref generic_arguments, ParseType (fq_argument)); |
|
if (fq_argument) |
|
TryParse (']'); |
|
|
|
TryParse (','); |
|
TryParseWhiteSpace (); |
|
} |
|
|
|
TryParse (']'); |
|
|
|
return generic_arguments; |
|
} |
|
|
|
string ParseAssemblyName () |
|
{ |
|
if (!TryParse (',')) |
|
return string.Empty; |
|
|
|
TryParseWhiteSpace (); |
|
|
|
var start = position; |
|
while (position < length) { |
|
var chr = fullname [position]; |
|
if (chr == '[' || chr == ']') |
|
break; |
|
|
|
position++; |
|
} |
|
|
|
return fullname.Substring (start, position - start); |
|
} |
|
|
|
public static TypeReference ParseType (ModuleDefinition module, string fullname) |
|
{ |
|
if (string.IsNullOrEmpty (fullname)) |
|
return null; |
|
|
|
var parser = new TypeParser (fullname); |
|
return GetTypeReference (module, parser.ParseType (true)); |
|
} |
|
|
|
static TypeReference GetTypeReference (ModuleDefinition module, Type type_info) |
|
{ |
|
TypeReference type; |
|
if (!TryGetDefinition (module, type_info, out type)) |
|
type = CreateReference (type_info, module, GetMetadataScope (module, type_info)); |
|
|
|
return CreateSpecs (type, type_info); |
|
} |
|
|
|
static TypeReference CreateSpecs (TypeReference type, Type type_info) |
|
{ |
|
type = TryCreateGenericInstanceType (type, type_info); |
|
|
|
var specs = type_info.specs; |
|
if (specs.IsNullOrEmpty ()) |
|
return type; |
|
|
|
for (int i = 0; i < specs.Length; i++) { |
|
switch (specs [i]) { |
|
case Type.Ptr: |
|
type = new PointerType (type); |
|
break; |
|
case Type.ByRef: |
|
type = new ByReferenceType (type); |
|
break; |
|
case Type.SzArray: |
|
type = new ArrayType (type); |
|
break; |
|
default: |
|
var array = new ArrayType (type); |
|
array.Dimensions.Clear (); |
|
|
|
for (int j = 0; j < specs [i]; j++) |
|
array.Dimensions.Add (new ArrayDimension ()); |
|
|
|
type = array; |
|
break; |
|
} |
|
} |
|
|
|
return type; |
|
} |
|
|
|
static TypeReference TryCreateGenericInstanceType (TypeReference type, Type type_info) |
|
{ |
|
var generic_arguments = type_info.generic_arguments; |
|
if (generic_arguments.IsNullOrEmpty ()) |
|
return type; |
|
|
|
var instance = new GenericInstanceType (type); |
|
var instance_arguments = instance.GenericArguments; |
|
|
|
for (int i = 0; i < generic_arguments.Length; i++) |
|
instance_arguments.Add (GetTypeReference (type.Module, generic_arguments [i])); |
|
|
|
return instance; |
|
} |
|
|
|
public static void SplitFullName (string fullname, out string @namespace, out string name) |
|
{ |
|
var last_dot = fullname.LastIndexOf ('.'); |
|
|
|
if (last_dot == -1) { |
|
@namespace = string.Empty; |
|
name = fullname; |
|
} else { |
|
@namespace = fullname.Substring (0, last_dot); |
|
name = fullname.Substring (last_dot + 1); |
|
} |
|
} |
|
|
|
static TypeReference CreateReference (Type type_info, ModuleDefinition module, IMetadataScope scope) |
|
{ |
|
string @namespace, name; |
|
SplitFullName (type_info.type_fullname, out @namespace, out name); |
|
|
|
var type = new TypeReference (@namespace, name, module, scope); |
|
MetadataSystem.TryProcessPrimitiveTypeReference (type); |
|
|
|
AdjustGenericParameters (type); |
|
|
|
var nested_names = type_info.nested_names; |
|
if (nested_names.IsNullOrEmpty ()) |
|
return type; |
|
|
|
for (int i = 0; i < nested_names.Length; i++) { |
|
type = new TypeReference (string.Empty, nested_names [i], module, null) { |
|
DeclaringType = type, |
|
}; |
|
|
|
AdjustGenericParameters (type); |
|
} |
|
|
|
return type; |
|
} |
|
|
|
static void AdjustGenericParameters (TypeReference type) |
|
{ |
|
int arity; |
|
if (!TryGetArity (type.Name, out arity)) |
|
return; |
|
|
|
for (int i = 0; i < arity; i++) |
|
type.GenericParameters.Add (new GenericParameter (type)); |
|
} |
|
|
|
static IMetadataScope GetMetadataScope (ModuleDefinition module, Type type_info) |
|
{ |
|
if (string.IsNullOrEmpty (type_info.assembly)) |
|
return module.TypeSystem.Corlib; |
|
|
|
return MatchReference (module, AssemblyNameReference.Parse (type_info.assembly)); |
|
} |
|
|
|
static AssemblyNameReference MatchReference (ModuleDefinition module, AssemblyNameReference pattern) |
|
{ |
|
var references = module.AssemblyReferences; |
|
|
|
for (int i = 0; i < references.Count; i++) { |
|
var reference = references [i]; |
|
if (reference.FullName == pattern.FullName) |
|
return reference; |
|
} |
|
|
|
return pattern; |
|
} |
|
|
|
static bool TryGetDefinition (ModuleDefinition module, Type type_info, out TypeReference type) |
|
{ |
|
type = null; |
|
if (!TryCurrentModule (module, type_info)) |
|
return false; |
|
|
|
var typedef = module.GetType (type_info.type_fullname); |
|
if (typedef == null) |
|
return false; |
|
|
|
var nested_names = type_info.nested_names; |
|
if (!nested_names.IsNullOrEmpty ()) { |
|
for (int i = 0; i < nested_names.Length; i++) |
|
typedef = typedef.GetNestedType (nested_names [i]); |
|
} |
|
|
|
type = typedef; |
|
return true; |
|
} |
|
|
|
static bool TryCurrentModule (ModuleDefinition module, Type type_info) |
|
{ |
|
if (string.IsNullOrEmpty (type_info.assembly)) |
|
return true; |
|
|
|
if (module.assembly != null && module.assembly.Name.FullName == type_info.assembly) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
public static string ToParseable (TypeReference type) |
|
{ |
|
if (type == null) |
|
return null; |
|
|
|
var name = new StringBuilder (); |
|
AppendType (type, name, true, true); |
|
return name.ToString (); |
|
} |
|
|
|
static void AppendType (TypeReference type, StringBuilder name, bool fq_name, bool top_level) |
|
{ |
|
var declaring_type = type.DeclaringType; |
|
if (declaring_type != null) { |
|
AppendType (declaring_type, name, false, top_level); |
|
name.Append ('+'); |
|
} |
|
|
|
var @namespace = type.Namespace; |
|
if (!string.IsNullOrEmpty (@namespace)) { |
|
name.Append (@namespace); |
|
name.Append ('.'); |
|
} |
|
|
|
name.Append (type.GetElementType ().Name); |
|
|
|
if (!fq_name) |
|
return; |
|
|
|
if (type.IsTypeSpecification ()) |
|
AppendTypeSpecification ((TypeSpecification) type, name); |
|
|
|
if (RequiresFullyQualifiedName (type, top_level)) { |
|
name.Append (", "); |
|
name.Append (GetScopeFullName (type)); |
|
} |
|
} |
|
|
|
static string GetScopeFullName (TypeReference type) |
|
{ |
|
var scope = type.Scope; |
|
switch (scope.MetadataScopeType) { |
|
case MetadataScopeType.AssemblyNameReference: |
|
return ((AssemblyNameReference) scope).FullName; |
|
case MetadataScopeType.ModuleDefinition: |
|
return ((ModuleDefinition) scope).Assembly.Name.FullName; |
|
} |
|
|
|
throw new ArgumentException (); |
|
} |
|
|
|
static void AppendTypeSpecification (TypeSpecification type, StringBuilder name) |
|
{ |
|
if (type.ElementType.IsTypeSpecification ()) |
|
AppendTypeSpecification ((TypeSpecification) type.ElementType, name); |
|
|
|
switch (type.etype) { |
|
case ElementType.Ptr: |
|
name.Append ('*'); |
|
break; |
|
case ElementType.ByRef: |
|
name.Append ('&'); |
|
break; |
|
case ElementType.SzArray: |
|
case ElementType.Array: |
|
var array = (ArrayType) type; |
|
if (array.IsVector) { |
|
name.Append ("[]"); |
|
} else { |
|
name.Append ('['); |
|
for (int i = 1; i < array.Rank; i++) |
|
name.Append (','); |
|
name.Append (']'); |
|
} |
|
break; |
|
case ElementType.GenericInst: |
|
var instance = (GenericInstanceType) type; |
|
var arguments = instance.GenericArguments; |
|
|
|
name.Append ('['); |
|
|
|
for (int i = 0; i < arguments.Count; i++) { |
|
if (i > 0) |
|
name.Append (','); |
|
|
|
var argument = arguments [i]; |
|
var requires_fqname = argument.Scope != argument.Module; |
|
|
|
if (requires_fqname) |
|
name.Append ('['); |
|
|
|
AppendType (argument, name, true, false); |
|
|
|
if (requires_fqname) |
|
name.Append (']'); |
|
} |
|
|
|
name.Append (']'); |
|
break; |
|
default: |
|
return; |
|
} |
|
} |
|
|
|
static bool RequiresFullyQualifiedName (TypeReference type, bool top_level) |
|
{ |
|
if (type.Scope == type.Module) |
|
return false; |
|
|
|
if (type.Scope.Name == "mscorlib" && top_level) |
|
return false; |
|
|
|
return true; |
|
} |
|
} |
|
}
|
|
|