.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
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

//
// 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;
}
}
}