// // TypeParser.cs // // Author: // Jb Evain (jbevain@gmail.com) // // Copyright (c) 2008 - 2010 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 (ref T [] array, T item) { if (array == null) { array = new [] { item }; return; } #if !CF Array.Resize (ref array, array.Length + 1); #else var copy = new T [array.Length + 1]; Array.Copy (array, copy, array.Length); array = copy; #endif 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 (fullname == null) 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.TryProcessPrimitiveType (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; } } }