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.
1858 lines
72 KiB
1858 lines
72 KiB
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// 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.Collections.Generic; |
|
using System.Collections.Immutable; |
|
using System.Diagnostics; |
|
using System.Linq; |
|
using System.Runtime.CompilerServices; |
|
using System.Runtime.InteropServices; |
|
using System.Threading; |
|
using ICSharpCode.Decompiler.TypeSystem.Implementation; |
|
using ICSharpCode.Decompiler.Util; |
|
using Mono.Cecil; |
|
|
|
namespace ICSharpCode.Decompiler.TypeSystem |
|
{ |
|
using BlobReader = BlobReader; |
|
|
|
/// <summary> |
|
/// Allows loading an IProjectContent from an already compiled assembly. |
|
/// </summary> |
|
/// <remarks>Instance methods are not thread-safe; you need to create multiple instances of CecilLoader |
|
/// if you want to load multiple project contents in parallel.</remarks> |
|
public sealed class CecilLoader : AssemblyLoader |
|
{ |
|
#region Options |
|
// Most options are defined in the AssemblyLoader base class |
|
|
|
/// <summary> |
|
/// Specifies whether to use lazy loading. The default is false. |
|
/// If this property is set to true, the CecilLoader will not copy all the relevant information |
|
/// out of the Cecil object model, but will maintain references to the Cecil objects. |
|
/// This speeds up the loading process and avoids loading unnecessary information, but it causes |
|
/// the Cecil objects to stay in memory (which can significantly increase memory usage). |
|
/// It also prevents serialization of the Cecil-loaded type system. |
|
/// </summary> |
|
/// <remarks> |
|
/// Because the type system can be used on multiple threads, but Cecil is not |
|
/// thread-safe for concurrent read access, the CecilLoader will lock on the <see cref="ModuleDefinition"/> instance |
|
/// for every delay-loading operation. |
|
/// If you access the Cecil objects directly in your application, you may need to take the same lock. |
|
/// </remarks> |
|
public bool LazyLoad { get; set; } |
|
|
|
/// <summary> |
|
/// Gets/Sets whether to use the <c>dynamic</c> type. |
|
/// </summary> |
|
public bool UseDynamicType { get; set; } = true; |
|
|
|
/// <summary> |
|
/// Gets/Sets whether to use the tuple types. |
|
/// </summary> |
|
public bool UseTupleTypes { get; set; } = true; |
|
|
|
/// <summary> |
|
/// This delegate gets executed whenever an entity was loaded. |
|
/// </summary> |
|
/// <remarks> |
|
/// This callback may be to build a dictionary that maps between |
|
/// entities and cecil objects. |
|
/// Warning: if delay-loading is used and the type system is accessed by multiple threads, |
|
/// the callback may be invoked concurrently on multiple threads. |
|
/// </remarks> |
|
public Action<IUnresolvedEntity, MemberReference> OnEntityLoaded { get; set; } |
|
|
|
/// <summary> |
|
/// Specifies whether method names of explicit interface-implementations should be shortened. |
|
/// </summary> |
|
/// <remarks>This is important when working with parser-initialized type-systems in order to be consistent.</remarks> |
|
public bool ShortenInterfaceImplNames { get; set; } = true; |
|
#endregion |
|
|
|
ModuleDefinition currentModule; |
|
DefaultUnresolvedAssembly currentAssembly; |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="CecilLoader"/> class. |
|
/// </summary> |
|
public CecilLoader() |
|
{ |
|
} |
|
|
|
/// <summary> |
|
/// Creates a nested CecilLoader for lazy-loading. |
|
/// </summary> |
|
private CecilLoader(CecilLoader loader) |
|
{ |
|
// use a shared typeSystemTranslationTable |
|
this.IncludeInternalMembers = loader.IncludeInternalMembers; |
|
this.UseDynamicType = loader.UseDynamicType; |
|
this.UseTupleTypes = loader.UseTupleTypes; |
|
this.LazyLoad = loader.LazyLoad; |
|
this.OnEntityLoaded = loader.OnEntityLoaded; |
|
this.ShortenInterfaceImplNames = loader.ShortenInterfaceImplNames; |
|
this.currentModule = loader.currentModule; |
|
this.currentAssembly = loader.currentAssembly; |
|
// don't use interning - the interning provider is most likely not thread-safe |
|
this.interningProvider = InterningProvider.Dummy; |
|
// don't use cancellation for delay-loaded members |
|
} |
|
|
|
#region Load From AssemblyDefinition |
|
/// <summary> |
|
/// Loads the assembly definition into a project content. |
|
/// </summary> |
|
/// <returns>Unresolved type system representing the assembly</returns> |
|
public IUnresolvedAssembly LoadAssembly(AssemblyDefinition assemblyDefinition) |
|
{ |
|
if (assemblyDefinition == null) |
|
throw new ArgumentNullException("assemblyDefinition"); |
|
return LoadModule(assemblyDefinition.MainModule); |
|
} |
|
|
|
/// <summary> |
|
/// Loads the module definition into a project content. |
|
/// </summary> |
|
/// <returns>Unresolved type system representing the assembly</returns> |
|
public IUnresolvedAssembly LoadModule(ModuleDefinition moduleDefinition) |
|
{ |
|
if (moduleDefinition == null) |
|
throw new ArgumentNullException("moduleDefinition"); |
|
|
|
this.currentModule = moduleDefinition; |
|
|
|
// Read assembly and module attributes |
|
IList<IUnresolvedAttribute> assemblyAttributes = new List<IUnresolvedAttribute>(); |
|
IList<IUnresolvedAttribute> moduleAttributes = new List<IUnresolvedAttribute>(); |
|
AssemblyDefinition assemblyDefinition = moduleDefinition.Assembly; |
|
if (assemblyDefinition != null) { |
|
AddAttributes(assemblyDefinition, assemblyAttributes); |
|
} |
|
AddAttributes(moduleDefinition, moduleAttributes); |
|
|
|
assemblyAttributes = interningProvider.InternList(assemblyAttributes); |
|
moduleAttributes = interningProvider.InternList(moduleAttributes); |
|
|
|
this.currentAssembly = new DefaultUnresolvedAssembly(assemblyDefinition != null ? assemblyDefinition.Name.FullName : moduleDefinition.Name); |
|
currentAssembly.Location = moduleDefinition.FileName; |
|
currentAssembly.AssemblyAttributes.AddRange(assemblyAttributes); |
|
currentAssembly.ModuleAttributes.AddRange(assemblyAttributes); |
|
|
|
// Register type forwarders: |
|
foreach (ExportedType type in moduleDefinition.ExportedTypes) { |
|
if (type.IsForwarder) { |
|
int typeParameterCount; |
|
string ns = type.Namespace; |
|
string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out typeParameterCount); |
|
ns = interningProvider.Intern(ns); |
|
name = interningProvider.Intern(name); |
|
var typeRef = new GetClassTypeReference(GetAssemblyReference(type.Scope), ns, name, typeParameterCount); |
|
typeRef = interningProvider.Intern(typeRef); |
|
var key = new TopLevelTypeName(ns, name, typeParameterCount); |
|
currentAssembly.AddTypeForwarder(key, typeRef); |
|
} |
|
} |
|
|
|
// Create and register all types: |
|
CecilLoader cecilLoaderCloneForLazyLoading = LazyLoad ? new CecilLoader(this) : null; |
|
List<TypeDefinition> cecilTypeDefs = new List<TypeDefinition>(); |
|
List<DefaultUnresolvedTypeDefinition> typeDefs = new List<DefaultUnresolvedTypeDefinition>(); |
|
foreach (TypeDefinition td in moduleDefinition.Types) { |
|
this.CancellationToken.ThrowIfCancellationRequested(); |
|
if (this.IncludeInternalMembers || (td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) { |
|
string name = td.Name; |
|
if (name.Length == 0) |
|
continue; |
|
|
|
if (this.LazyLoad) { |
|
var t = new LazyCecilTypeDefinition(cecilLoaderCloneForLazyLoading, td); |
|
currentAssembly.AddTypeDefinition(t); |
|
RegisterCecilObject(t, td); |
|
} else { |
|
var t = CreateTopLevelTypeDefinition(td); |
|
currentAssembly.AddTypeDefinition(t); |
|
cecilTypeDefs.Add(td); |
|
typeDefs.Add(t); |
|
// The registration will happen after the members are initialized |
|
} |
|
} |
|
} |
|
// Initialize the type's members (but only if not lazy-init) |
|
for (int i = 0; i < typeDefs.Count; i++) { |
|
InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]); |
|
} |
|
|
|
// Freezing the assembly here is important: |
|
// otherwise it will be frozen when a compilation is first created |
|
// from it. But freezing has the effect of changing some collection instances |
|
// (to ReadOnlyCollection). This hidden mutation was causing a crash |
|
// when the FastSerializer was saving the assembly at the same time as |
|
// the first compilation was created from it. |
|
// By freezing the assembly now, we ensure it is usable on multiple |
|
// threads without issues. |
|
currentAssembly.Freeze(); |
|
|
|
var result = this.currentAssembly; |
|
this.currentAssembly = null; |
|
this.currentModule = null; |
|
return result; |
|
} |
|
|
|
/// <summary> |
|
/// Sets the current module. |
|
/// This causes ReadTypeReference() to use <see cref="DefaultAssemblyReference.CurrentAssembly"/> for references |
|
/// in that module. |
|
/// </summary> |
|
public void SetCurrentModule(ModuleDefinition module) |
|
{ |
|
this.currentModule = module; |
|
} |
|
|
|
/// <summary> |
|
/// Loads a type from Cecil. |
|
/// </summary> |
|
/// <param name="typeDefinition">The Cecil TypeDefinition.</param> |
|
/// <returns>ITypeDefinition representing the Cecil type.</returns> |
|
public IUnresolvedTypeDefinition LoadType(TypeDefinition typeDefinition) |
|
{ |
|
if (typeDefinition == null) |
|
throw new ArgumentNullException("typeDefinition"); |
|
var td = CreateTopLevelTypeDefinition(typeDefinition); |
|
InitTypeDefinition(typeDefinition, td); |
|
return td; |
|
} |
|
#endregion |
|
|
|
#region Load Assembly From Disk |
|
public override IUnresolvedAssembly LoadAssemblyFile(string fileName) |
|
{ |
|
if (fileName == null) |
|
throw new ArgumentNullException("fileName"); |
|
var param = new ReaderParameters { AssemblyResolver = new DummyAssemblyResolver() }; |
|
using (ModuleDefinition module = ModuleDefinition.ReadModule(fileName, param)) { |
|
return LoadModule(module); |
|
} |
|
} |
|
|
|
// used to prevent Cecil from loading referenced assemblies |
|
sealed class DummyAssemblyResolver : IAssemblyResolver |
|
{ |
|
public AssemblyDefinition Resolve(AssemblyNameReference name) |
|
{ |
|
return null; |
|
} |
|
|
|
public AssemblyDefinition Resolve(string fullName) |
|
{ |
|
return null; |
|
} |
|
|
|
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) |
|
{ |
|
return null; |
|
} |
|
|
|
public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters) |
|
{ |
|
return null; |
|
} |
|
|
|
public void Dispose() |
|
{ |
|
} |
|
} |
|
#endregion |
|
|
|
#region Read Type Reference |
|
/// <summary> |
|
/// Reads a type reference. |
|
/// </summary> |
|
/// <param name="type">The Cecil type reference that should be converted into |
|
/// a type system type reference.</param> |
|
/// <param name="typeAttributes">Attributes associated with the Cecil type reference. |
|
/// This is used to support the 'dynamic' type.</param> |
|
/// <param name="isFromSignature">Whether this TypeReference is from a context where |
|
/// IsValueType is set correctly.</param> |
|
public ITypeReference ReadTypeReference(TypeReference type, ICustomAttributeProvider typeAttributes = null, bool isFromSignature = false) |
|
{ |
|
int dynamicTypeIndex = 0; |
|
int tupleTypeIndex = 0; |
|
return CreateType(type, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature); |
|
} |
|
|
|
ITypeReference CreateType(TypeReference type, ICustomAttributeProvider typeAttributes, ref int dynamicTypeIndex, ref int tupleTypeIndex, bool isFromSignature) |
|
{ |
|
if (type == null) { |
|
return SpecialType.UnknownType; |
|
} |
|
|
|
switch (type.MetadataType) { |
|
case MetadataType.Void: |
|
return KnownTypeReference.Void; |
|
case MetadataType.Boolean: |
|
return KnownTypeReference.Boolean; |
|
case MetadataType.Char: |
|
return KnownTypeReference.Char; |
|
case MetadataType.SByte: |
|
return KnownTypeReference.SByte; |
|
case MetadataType.Byte: |
|
return KnownTypeReference.Byte; |
|
case MetadataType.Int16: |
|
return KnownTypeReference.Int16; |
|
case MetadataType.UInt16: |
|
return KnownTypeReference.UInt16; |
|
case MetadataType.Int32: |
|
return KnownTypeReference.Int32; |
|
case MetadataType.UInt32: |
|
return KnownTypeReference.UInt32; |
|
case MetadataType.Int64: |
|
return KnownTypeReference.Int64; |
|
case MetadataType.UInt64: |
|
return KnownTypeReference.UInt64; |
|
case MetadataType.Single: |
|
return KnownTypeReference.Single; |
|
case MetadataType.Double: |
|
return KnownTypeReference.Double; |
|
case MetadataType.String: |
|
return KnownTypeReference.String; |
|
case MetadataType.Pointer: |
|
dynamicTypeIndex++; |
|
return interningProvider.Intern( |
|
new PointerTypeReference( |
|
CreateType( |
|
(type as Mono.Cecil.PointerType).ElementType, |
|
typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true))); |
|
case MetadataType.ByReference: |
|
dynamicTypeIndex++; |
|
return interningProvider.Intern( |
|
new ByReferenceTypeReference( |
|
CreateType( |
|
(type as Mono.Cecil.ByReferenceType).ElementType, |
|
typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true))); |
|
case MetadataType.Var: |
|
return TypeParameterReference.Create(SymbolKind.TypeDefinition, ((GenericParameter)type).Position); |
|
case MetadataType.MVar: |
|
return TypeParameterReference.Create(SymbolKind.Method, ((GenericParameter)type).Position); |
|
case MetadataType.Array: |
|
dynamicTypeIndex++; |
|
return interningProvider.Intern( |
|
new ArrayTypeReference( |
|
CreateType( |
|
(type as Mono.Cecil.ArrayType).ElementType, |
|
typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true), |
|
(type as Mono.Cecil.ArrayType).Rank)); |
|
case MetadataType.GenericInstance: |
|
GenericInstanceType gType = (GenericInstanceType)type; |
|
ITypeReference baseType = CreateType(gType.ElementType, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true); |
|
if (UseTupleTypes && IsValueTuple(gType, out int tupleCardinality)) { |
|
if (tupleCardinality > 1) { |
|
var assemblyRef = GetAssemblyReference(gType.ElementType.Scope); |
|
var elementNames = GetTupleElementNames(typeAttributes, tupleTypeIndex, tupleCardinality); |
|
tupleTypeIndex += tupleCardinality; |
|
ITypeReference[] elementTypeRefs = new ITypeReference[tupleCardinality]; |
|
int outPos = 0; |
|
do { |
|
int normalArgCount = Math.Min(gType.GenericArguments.Count, TupleType.RestPosition - 1); |
|
for (int i = 0; i < normalArgCount; i++) { |
|
dynamicTypeIndex++; |
|
elementTypeRefs[outPos++] = CreateType(gType.GenericArguments[i], typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true); |
|
} |
|
if (gType.GenericArguments.Count == TupleType.RestPosition) { |
|
gType = (GenericInstanceType)gType.GenericArguments.Last(); |
|
dynamicTypeIndex++; |
|
if (IsValueTuple(gType, out int nestedCardinality)) { |
|
tupleTypeIndex += nestedCardinality; |
|
} else { |
|
Debug.Fail("TRest should be another value tuple"); |
|
} |
|
} else { |
|
gType = null; |
|
} |
|
} while (gType != null); |
|
return new TupleTypeReference( |
|
elementTypeRefs.ToImmutableArray(), elementNames, |
|
assemblyRef); |
|
} else { |
|
// C# doesn't have syntax for tuples of cardinality <= 1 |
|
tupleTypeIndex += tupleCardinality; |
|
} |
|
} |
|
ITypeReference[] para = new ITypeReference[gType.GenericArguments.Count]; |
|
for (int i = 0; i < para.Length; ++i) { |
|
dynamicTypeIndex++; |
|
para[i] = CreateType(gType.GenericArguments[i], typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true); |
|
} |
|
return interningProvider.Intern(new ParameterizedTypeReference(baseType, para)); |
|
case MetadataType.IntPtr: |
|
return KnownTypeReference.IntPtr; |
|
case MetadataType.UIntPtr: |
|
return KnownTypeReference.UIntPtr; |
|
case MetadataType.FunctionPointer: |
|
// C# and the NR typesystem don't support function pointer types. |
|
// Function pointer types map to StackType.I, so we'll use IntPtr instead. |
|
return KnownTypeReference.IntPtr; |
|
case MetadataType.Object: |
|
if (UseDynamicType && HasDynamicAttribute(typeAttributes, dynamicTypeIndex)) { |
|
return SpecialType.Dynamic; |
|
} else { |
|
return KnownTypeReference.Object; |
|
} |
|
case MetadataType.RequiredModifier: |
|
case MetadataType.OptionalModifier: |
|
// we don't store modopts/modreqs in the NR type system |
|
return CreateType(((TypeSpecification)type).ElementType, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true); |
|
case MetadataType.Sentinel: |
|
return SpecialType.ArgList; |
|
case MetadataType.Pinned: |
|
return CreateType(((PinnedType)type).ElementType, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature: true); |
|
} |
|
// valuetype/class/typedbyreference |
|
if (type is TypeDefinition) { |
|
return new TypeDefTokenTypeReference(type.MetadataToken); |
|
} |
|
// type.IsValueType is only reliable if we got this TypeReference from a signature, |
|
// or if it's a TypeSpecification. |
|
bool? isReferenceType = isFromSignature ? (bool?)!type.IsValueType : null; |
|
if (type.IsNested) { |
|
ITypeReference typeRef = CreateType(type.DeclaringType, typeAttributes, ref dynamicTypeIndex, ref tupleTypeIndex, isFromSignature); |
|
int partTypeParameterCount; |
|
string namepart = ReflectionHelper.SplitTypeParameterCountFromReflectionName(type.Name, out partTypeParameterCount); |
|
namepart = interningProvider.Intern(namepart); |
|
return interningProvider.Intern(new NestedTypeReference(typeRef, namepart, partTypeParameterCount, isReferenceType)); |
|
} else { |
|
string ns = interningProvider.Intern(type.Namespace ?? string.Empty); |
|
string name = type.Name; |
|
if (name == null) |
|
throw new InvalidOperationException("type.Name returned null. Type: " + type.ToString()); |
|
|
|
if (UseDynamicType && name == "Object" && ns == "System" && HasDynamicAttribute(typeAttributes, dynamicTypeIndex)) { |
|
return SpecialType.Dynamic; |
|
} |
|
int typeParameterCount; |
|
name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name, out typeParameterCount); |
|
name = interningProvider.Intern(name); |
|
return interningProvider.Intern(new GetClassTypeReference( |
|
GetAssemblyReference(type.Scope), ns, name, typeParameterCount, |
|
isReferenceType)); |
|
} |
|
} |
|
|
|
static internal bool IsValueTuple(GenericInstanceType gType, out int tupleCardinality) |
|
{ |
|
tupleCardinality = 0; |
|
if (gType == null || gType.DeclaringType != null || !gType.Name.StartsWith("ValueTuple`", StringComparison.Ordinal) || gType.Namespace != "System") |
|
return false; |
|
if (gType.GenericArguments.Count == TupleType.RestPosition) { |
|
if (IsValueTuple(gType.GenericArguments.Last() as GenericInstanceType, out tupleCardinality)) { |
|
tupleCardinality += TupleType.RestPosition - 1; |
|
return true; |
|
} |
|
} |
|
tupleCardinality = gType.GenericArguments.Count; |
|
return tupleCardinality > 0 && tupleCardinality < TupleType.RestPosition; |
|
} |
|
|
|
static ImmutableArray<string> GetTupleElementNames(ICustomAttributeProvider attributeProvider, int tupleTypeIndex, int tupleCardinality) |
|
{ |
|
if (attributeProvider == null || !attributeProvider.HasCustomAttributes) |
|
return default(ImmutableArray<string>); |
|
foreach (CustomAttribute a in attributeProvider.CustomAttributes) { |
|
TypeReference type = a.AttributeType; |
|
if (type.Name == "TupleElementNamesAttribute" && type.Namespace == "System.Runtime.CompilerServices") { |
|
if (a.ConstructorArguments.Count == 1) { |
|
CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[]; |
|
if (values != null) { |
|
string[] extractedValues = new string[tupleCardinality]; |
|
for (int i = 0; i < tupleCardinality; i++) { |
|
if (tupleTypeIndex + i < values.Length) { |
|
extractedValues[i] = values[tupleTypeIndex + i].Value as string; |
|
} |
|
} |
|
return extractedValues.ToImmutableArray(); |
|
} |
|
} |
|
} |
|
} |
|
return default(ImmutableArray<string>); |
|
} |
|
|
|
IAssemblyReference GetAssemblyReference(IMetadataScope scope) |
|
{ |
|
if (scope == null || scope == currentModule) |
|
return DefaultAssemblyReference.CurrentAssembly; |
|
else |
|
return interningProvider.Intern(new DefaultAssemblyReference(scope.Name)); |
|
} |
|
|
|
static bool HasDynamicAttribute(ICustomAttributeProvider attributeProvider, int typeIndex) |
|
{ |
|
if (attributeProvider == null || !attributeProvider.HasCustomAttributes) |
|
return false; |
|
foreach (CustomAttribute a in attributeProvider.CustomAttributes) { |
|
TypeReference type = a.AttributeType; |
|
if (type.Name == "DynamicAttribute" && type.Namespace == "System.Runtime.CompilerServices") { |
|
if (a.ConstructorArguments.Count == 1) { |
|
CustomAttributeArgument[] values = a.ConstructorArguments[0].Value as CustomAttributeArgument[]; |
|
if (values != null && typeIndex < values.Length && values[typeIndex].Value is bool) |
|
return (bool)values[typeIndex].Value; |
|
} |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
sealed class TypeDefTokenTypeReference : ITypeReference |
|
{ |
|
readonly MetadataToken token; |
|
|
|
public TypeDefTokenTypeReference(MetadataToken token) |
|
{ |
|
if (token.TokenType != TokenType.TypeDef) |
|
throw new ArgumentException(nameof(token), "must be TypeDef token"); |
|
this.token = token; |
|
} |
|
|
|
public IType Resolve(ITypeResolveContext context) |
|
{ |
|
ITypeDefinition td = context.CurrentAssembly.ResolveTypeDefToken(token); |
|
if (td != null) |
|
return td; |
|
return SpecialType.UnknownType; |
|
} |
|
} |
|
#endregion |
|
|
|
#region Read Attributes |
|
#region Assembly Attributes |
|
static readonly ITypeReference assemblyVersionAttributeTypeRef = typeof(System.Reflection.AssemblyVersionAttribute).ToTypeReference(); |
|
|
|
void AddAttributes(AssemblyDefinition assembly, IList<IUnresolvedAttribute> outputList) |
|
{ |
|
if (assembly.HasCustomAttributes) { |
|
AddCustomAttributes(assembly.CustomAttributes, outputList); |
|
} |
|
if (assembly.HasSecurityDeclarations) { |
|
AddSecurityAttributes(assembly.SecurityDeclarations, outputList); |
|
} |
|
|
|
// AssemblyVersionAttribute |
|
if (assembly.Name.Version != null) { |
|
var assemblyVersion = new DefaultUnresolvedAttribute(assemblyVersionAttributeTypeRef, new[] { KnownTypeReference.String }); |
|
assemblyVersion.PositionalArguments.Add(CreateSimpleConstantValue(KnownTypeReference.String, assembly.Name.Version.ToString())); |
|
outputList.Add(interningProvider.Intern(assemblyVersion)); |
|
} |
|
} |
|
|
|
IConstantValue CreateSimpleConstantValue(ITypeReference type, object value) |
|
{ |
|
return interningProvider.Intern(new SimpleConstantValue(type, interningProvider.InternValue(value))); |
|
} |
|
#endregion |
|
|
|
#region Module Attributes |
|
void AddAttributes(ModuleDefinition module, IList<IUnresolvedAttribute> outputList) |
|
{ |
|
if (module.HasCustomAttributes) { |
|
AddCustomAttributes(module.CustomAttributes, outputList); |
|
} |
|
} |
|
#endregion |
|
|
|
#region Parameter Attributes |
|
static readonly IUnresolvedAttribute inAttribute = new DefaultUnresolvedAttribute(typeof(InAttribute).ToTypeReference()); |
|
static readonly IUnresolvedAttribute outAttribute = new DefaultUnresolvedAttribute(typeof(OutAttribute).ToTypeReference()); |
|
|
|
void AddAttributes(ParameterDefinition parameter, DefaultUnresolvedParameter targetParameter) |
|
{ |
|
if (!targetParameter.IsOut) { |
|
if (parameter.IsIn) |
|
targetParameter.Attributes.Add(inAttribute); |
|
if (parameter.IsOut) |
|
targetParameter.Attributes.Add(outAttribute); |
|
} |
|
if (parameter.HasCustomAttributes) { |
|
AddCustomAttributes(parameter.CustomAttributes, targetParameter.Attributes); |
|
} |
|
if (parameter.HasMarshalInfo) { |
|
targetParameter.Attributes.Add(ConvertMarshalInfo(parameter.MarshalInfo)); |
|
} |
|
} |
|
#endregion |
|
|
|
#region Method Attributes |
|
static readonly ITypeReference dllImportAttributeTypeRef = typeof(DllImportAttribute).ToTypeReference(); |
|
static readonly SimpleConstantValue trueValue = new SimpleConstantValue(KnownTypeReference.Boolean, true); |
|
static readonly SimpleConstantValue falseValue = new SimpleConstantValue(KnownTypeReference.Boolean, false); |
|
static readonly ITypeReference callingConventionTypeRef = typeof(CallingConvention).ToTypeReference(); |
|
static readonly IUnresolvedAttribute preserveSigAttribute = new DefaultUnresolvedAttribute(typeof(PreserveSigAttribute).ToTypeReference()); |
|
static readonly ITypeReference methodImplAttributeTypeRef = typeof(MethodImplAttribute).ToTypeReference(); |
|
static readonly ITypeReference methodImplOptionsTypeRef = typeof(MethodImplOptions).ToTypeReference(); |
|
|
|
static bool HasAnyAttributes(MethodDefinition methodDefinition) |
|
{ |
|
if (methodDefinition.HasPInvokeInfo) |
|
return true; |
|
if ((methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask) != 0) |
|
return true; |
|
if (methodDefinition.MethodReturnType.HasFieldMarshal) |
|
return true; |
|
return methodDefinition.HasCustomAttributes || methodDefinition.MethodReturnType.HasCustomAttributes; |
|
} |
|
|
|
void AddAttributes(MethodDefinition methodDefinition, IList<IUnresolvedAttribute> attributes, IList<IUnresolvedAttribute> returnTypeAttributes) |
|
{ |
|
MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; |
|
|
|
#region DllImportAttribute |
|
if (methodDefinition.HasPInvokeInfo && methodDefinition.PInvokeInfo != null) { |
|
PInvokeInfo info = methodDefinition.PInvokeInfo; |
|
var dllImport = new DefaultUnresolvedAttribute(dllImportAttributeTypeRef, new[] { KnownTypeReference.String }); |
|
dllImport.PositionalArguments.Add(CreateSimpleConstantValue(KnownTypeReference.String, info.Module.Name)); |
|
|
|
if (info.IsBestFitDisabled) |
|
dllImport.AddNamedFieldArgument("BestFitMapping", falseValue); |
|
if (info.IsBestFitEnabled) |
|
dllImport.AddNamedFieldArgument("BestFitMapping", trueValue); |
|
|
|
CallingConvention callingConvention; |
|
switch (info.Attributes & PInvokeAttributes.CallConvMask) { |
|
case (PInvokeAttributes)0: |
|
Debug.WriteLine ("P/Invoke calling convention not set on:" + methodDefinition.FullName); |
|
callingConvention = 0; |
|
break; |
|
case PInvokeAttributes.CallConvCdecl: |
|
callingConvention = CallingConvention.Cdecl; |
|
break; |
|
case PInvokeAttributes.CallConvFastcall: |
|
callingConvention = CallingConvention.FastCall; |
|
break; |
|
case PInvokeAttributes.CallConvStdCall: |
|
callingConvention = CallingConvention.StdCall; |
|
break; |
|
case PInvokeAttributes.CallConvThiscall: |
|
callingConvention = CallingConvention.ThisCall; |
|
break; |
|
case PInvokeAttributes.CallConvWinapi: |
|
callingConvention = CallingConvention.Winapi; |
|
break; |
|
default: |
|
throw new NotSupportedException("unknown calling convention"); |
|
} |
|
if (callingConvention != CallingConvention.Winapi) |
|
dllImport.AddNamedFieldArgument("CallingConvention", CreateSimpleConstantValue(callingConventionTypeRef, (int)callingConvention)); |
|
|
|
CharSet charSet = CharSet.None; |
|
switch (info.Attributes & PInvokeAttributes.CharSetMask) { |
|
case PInvokeAttributes.CharSetAnsi: |
|
charSet = CharSet.Ansi; |
|
break; |
|
case PInvokeAttributes.CharSetAuto: |
|
charSet = CharSet.Auto; |
|
break; |
|
case PInvokeAttributes.CharSetUnicode: |
|
charSet = CharSet.Unicode; |
|
break; |
|
} |
|
if (charSet != CharSet.None) |
|
dllImport.AddNamedFieldArgument("CharSet", CreateSimpleConstantValue(charSetTypeRef, (int)charSet)); |
|
|
|
if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != methodDefinition.Name) |
|
dllImport.AddNamedFieldArgument("EntryPoint", CreateSimpleConstantValue(KnownTypeReference.String, info.EntryPoint)); |
|
|
|
if (info.IsNoMangle) |
|
dllImport.AddNamedFieldArgument("ExactSpelling", trueValue); |
|
|
|
if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig) |
|
implAttributes &= ~MethodImplAttributes.PreserveSig; |
|
else |
|
dllImport.AddNamedFieldArgument("PreserveSig", falseValue); |
|
|
|
if (info.SupportsLastError) |
|
dllImport.AddNamedFieldArgument("SetLastError", trueValue); |
|
|
|
if (info.IsThrowOnUnmappableCharDisabled) |
|
dllImport.AddNamedFieldArgument("ThrowOnUnmappableChar", falseValue); |
|
if (info.IsThrowOnUnmappableCharEnabled) |
|
dllImport.AddNamedFieldArgument("ThrowOnUnmappableChar", trueValue); |
|
|
|
attributes.Add(interningProvider.Intern(dllImport)); |
|
} |
|
#endregion |
|
|
|
#region PreserveSigAttribute |
|
if (implAttributes == MethodImplAttributes.PreserveSig) { |
|
attributes.Add(preserveSigAttribute); |
|
implAttributes = 0; |
|
} |
|
#endregion |
|
|
|
#region MethodImplAttribute |
|
if (implAttributes != 0) { |
|
var methodImpl = new DefaultUnresolvedAttribute(methodImplAttributeTypeRef, new[] { methodImplOptionsTypeRef }); |
|
methodImpl.PositionalArguments.Add(CreateSimpleConstantValue(methodImplOptionsTypeRef, (int)implAttributes)); |
|
attributes.Add(interningProvider.Intern(methodImpl)); |
|
} |
|
#endregion |
|
|
|
if (methodDefinition.HasCustomAttributes) { |
|
AddCustomAttributes(methodDefinition.CustomAttributes, attributes); |
|
} |
|
if (methodDefinition.HasSecurityDeclarations) { |
|
AddSecurityAttributes(methodDefinition.SecurityDeclarations, attributes); |
|
} |
|
if (methodDefinition.MethodReturnType.HasMarshalInfo) { |
|
returnTypeAttributes.Add(ConvertMarshalInfo(methodDefinition.MethodReturnType.MarshalInfo)); |
|
} |
|
if (methodDefinition.MethodReturnType.HasCustomAttributes) { |
|
AddCustomAttributes(methodDefinition.MethodReturnType.CustomAttributes, returnTypeAttributes); |
|
} |
|
} |
|
#endregion |
|
|
|
#region Type Attributes |
|
static readonly DefaultUnresolvedAttribute serializableAttribute = new DefaultUnresolvedAttribute(typeof(SerializableAttribute).ToTypeReference()); |
|
static readonly DefaultUnresolvedAttribute comImportAttribute = new DefaultUnresolvedAttribute(typeof(ComImportAttribute).ToTypeReference()); |
|
static readonly ITypeReference structLayoutAttributeTypeRef = typeof(StructLayoutAttribute).ToTypeReference(); |
|
static readonly ITypeReference layoutKindTypeRef = typeof(LayoutKind).ToTypeReference(); |
|
static readonly ITypeReference charSetTypeRef = typeof(CharSet).ToTypeReference(); |
|
|
|
void AddAttributes(TypeDefinition typeDefinition, IUnresolvedTypeDefinition targetEntity) |
|
{ |
|
// SerializableAttribute |
|
if (typeDefinition.IsSerializable) |
|
targetEntity.Attributes.Add(serializableAttribute); |
|
|
|
// ComImportAttribute |
|
if (typeDefinition.IsImport) |
|
targetEntity.Attributes.Add(comImportAttribute); |
|
|
|
#region StructLayoutAttribute |
|
LayoutKind layoutKind = LayoutKind.Auto; |
|
switch (typeDefinition.Attributes & TypeAttributes.LayoutMask) { |
|
case TypeAttributes.SequentialLayout: |
|
layoutKind = LayoutKind.Sequential; |
|
break; |
|
case TypeAttributes.ExplicitLayout: |
|
layoutKind = LayoutKind.Explicit; |
|
break; |
|
} |
|
CharSet charSet = CharSet.None; |
|
switch (typeDefinition.Attributes & TypeAttributes.StringFormatMask) { |
|
case TypeAttributes.AnsiClass: |
|
charSet = CharSet.Ansi; |
|
break; |
|
case TypeAttributes.AutoClass: |
|
charSet = CharSet.Auto; |
|
break; |
|
case TypeAttributes.UnicodeClass: |
|
charSet = CharSet.Unicode; |
|
break; |
|
} |
|
LayoutKind defaultLayoutKind = (typeDefinition.IsValueType && !typeDefinition.IsEnum) ? LayoutKind.Sequential: LayoutKind.Auto; |
|
if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || typeDefinition.PackingSize > 0 || typeDefinition.ClassSize > 0) { |
|
DefaultUnresolvedAttribute structLayout = new DefaultUnresolvedAttribute(structLayoutAttributeTypeRef, new[] { layoutKindTypeRef }); |
|
structLayout.PositionalArguments.Add(CreateSimpleConstantValue(layoutKindTypeRef, (int)layoutKind)); |
|
if (charSet != CharSet.Ansi) { |
|
structLayout.AddNamedFieldArgument("CharSet", CreateSimpleConstantValue(charSetTypeRef, (int)charSet)); |
|
} |
|
if (typeDefinition.PackingSize > 0) { |
|
structLayout.AddNamedFieldArgument("Pack", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)typeDefinition.PackingSize)); |
|
} |
|
if (typeDefinition.ClassSize > 0) { |
|
structLayout.AddNamedFieldArgument("Size", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)typeDefinition.ClassSize)); |
|
} |
|
targetEntity.Attributes.Add(interningProvider.Intern(structLayout)); |
|
} |
|
#endregion |
|
|
|
if (typeDefinition.HasCustomAttributes) { |
|
AddCustomAttributes(typeDefinition.CustomAttributes, targetEntity.Attributes); |
|
} |
|
if (typeDefinition.HasSecurityDeclarations) { |
|
AddSecurityAttributes(typeDefinition.SecurityDeclarations, targetEntity.Attributes); |
|
} |
|
} |
|
#endregion |
|
|
|
#region Field Attributes |
|
static readonly ITypeReference fieldOffsetAttributeTypeRef = typeof(FieldOffsetAttribute).ToTypeReference(); |
|
static readonly IUnresolvedAttribute nonSerializedAttribute = new DefaultUnresolvedAttribute(typeof(NonSerializedAttribute).ToTypeReference()); |
|
|
|
void AddAttributes(FieldDefinition fieldDefinition, IUnresolvedEntity targetEntity) |
|
{ |
|
// FieldOffsetAttribute |
|
if (fieldDefinition.HasLayoutInfo) { |
|
DefaultUnresolvedAttribute fieldOffset = new DefaultUnresolvedAttribute(fieldOffsetAttributeTypeRef, new[] { KnownTypeReference.Int32 }); |
|
fieldOffset.PositionalArguments.Add(CreateSimpleConstantValue(KnownTypeReference.Int32, fieldDefinition.Offset)); |
|
targetEntity.Attributes.Add(interningProvider.Intern(fieldOffset)); |
|
} |
|
|
|
// NonSerializedAttribute |
|
if (fieldDefinition.IsNotSerialized) { |
|
targetEntity.Attributes.Add(nonSerializedAttribute); |
|
} |
|
|
|
if (fieldDefinition.HasMarshalInfo) { |
|
targetEntity.Attributes.Add(ConvertMarshalInfo(fieldDefinition.MarshalInfo)); |
|
} |
|
|
|
if (fieldDefinition.HasCustomAttributes) { |
|
AddCustomAttributes(fieldDefinition.CustomAttributes, targetEntity.Attributes); |
|
} |
|
} |
|
#endregion |
|
|
|
#region Event Attributes |
|
void AddAttributes(EventDefinition eventDefinition, IUnresolvedEntity targetEntity) |
|
{ |
|
if (eventDefinition.HasCustomAttributes) { |
|
AddCustomAttributes(eventDefinition.CustomAttributes, targetEntity.Attributes); |
|
} |
|
} |
|
#endregion |
|
|
|
#region Property Attributes |
|
void AddAttributes(PropertyDefinition propertyDefinition, IUnresolvedEntity targetEntity) |
|
{ |
|
if (propertyDefinition.HasCustomAttributes) { |
|
AddCustomAttributes(propertyDefinition.CustomAttributes, targetEntity.Attributes); |
|
} |
|
} |
|
#endregion |
|
|
|
#region Type Parameter Attributes |
|
void AddAttributes(GenericParameter genericParameter, IUnresolvedTypeParameter targetTP) |
|
{ |
|
if (genericParameter.HasCustomAttributes) { |
|
AddCustomAttributes(genericParameter.CustomAttributes, targetTP.Attributes); |
|
} |
|
} |
|
#endregion |
|
|
|
#region MarshalAsAttribute (ConvertMarshalInfo) |
|
static readonly ITypeReference marshalAsAttributeTypeRef = typeof(MarshalAsAttribute).ToTypeReference(); |
|
static readonly ITypeReference unmanagedTypeTypeRef = typeof(UnmanagedType).ToTypeReference(); |
|
|
|
IUnresolvedAttribute ConvertMarshalInfo(MarshalInfo marshalInfo) |
|
{ |
|
DefaultUnresolvedAttribute attr = new DefaultUnresolvedAttribute(marshalAsAttributeTypeRef, new[] { unmanagedTypeTypeRef }); |
|
attr.PositionalArguments.Add(CreateSimpleConstantValue(unmanagedTypeTypeRef, (int)marshalInfo.NativeType)); |
|
|
|
FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo; |
|
if (fami != null) { |
|
attr.AddNamedFieldArgument("SizeConst", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)fami.Size)); |
|
if (fami.ElementType != NativeType.None) |
|
attr.AddNamedFieldArgument("ArraySubType", CreateSimpleConstantValue(unmanagedTypeTypeRef, (int)fami.ElementType)); |
|
} |
|
SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo; |
|
if (sami != null && sami.ElementType != VariantType.None) { |
|
attr.AddNamedFieldArgument("SafeArraySubType", CreateSimpleConstantValue(typeof(VarEnum).ToTypeReference(), (int)sami.ElementType)); |
|
} |
|
ArrayMarshalInfo ami = marshalInfo as ArrayMarshalInfo; |
|
if (ami != null) { |
|
if (ami.ElementType != NativeType.Max) |
|
attr.AddNamedFieldArgument("ArraySubType", CreateSimpleConstantValue(unmanagedTypeTypeRef, (int)ami.ElementType)); |
|
if (ami.Size >= 0) |
|
attr.AddNamedFieldArgument("SizeConst", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)ami.Size)); |
|
if (ami.SizeParameterMultiplier != 0 && ami.SizeParameterIndex >= 0) |
|
attr.AddNamedFieldArgument("SizeParamIndex", CreateSimpleConstantValue(KnownTypeReference.Int16, (short)ami.SizeParameterIndex)); |
|
} |
|
CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo; |
|
if (cmi != null) { |
|
if (cmi.ManagedType != null) |
|
attr.AddNamedFieldArgument("MarshalType", CreateSimpleConstantValue(KnownTypeReference.String, cmi.ManagedType.FullName)); |
|
if (!string.IsNullOrEmpty(cmi.Cookie)) |
|
attr.AddNamedFieldArgument("MarshalCookie", CreateSimpleConstantValue(KnownTypeReference.String, cmi.Cookie)); |
|
} |
|
FixedSysStringMarshalInfo fssmi = marshalInfo as FixedSysStringMarshalInfo; |
|
if (fssmi != null) { |
|
attr.AddNamedFieldArgument("SizeConst", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)fssmi.Size)); |
|
} |
|
|
|
return InterningProvider.Intern(attr); |
|
} |
|
#endregion |
|
|
|
#region Custom Attributes (ReadAttribute) |
|
void AddCustomAttributes(Mono.Collections.Generic.Collection<CustomAttribute> attributes, IList<IUnresolvedAttribute> targetCollection) |
|
{ |
|
foreach (var cecilAttribute in attributes) { |
|
TypeReference type = cecilAttribute.AttributeType; |
|
if (type.Namespace == "System.Runtime.CompilerServices") { |
|
if (type.Name == "ExtensionAttribute" || type.Name == "DecimalConstantAttribute") |
|
continue; |
|
if (UseDynamicType && type.Name == "DynamicAttribute") |
|
continue; |
|
if (UseTupleTypes && type.Name == "TupleElementNamesAttribute") |
|
continue; |
|
} else if (type.Name == "ParamArrayAttribute" && type.Namespace == "System") { |
|
continue; |
|
} |
|
targetCollection.Add(ReadAttribute(cecilAttribute)); |
|
} |
|
} |
|
|
|
public IUnresolvedAttribute ReadAttribute(CustomAttribute attribute) |
|
{ |
|
if (attribute == null) |
|
throw new ArgumentNullException("attribute"); |
|
MethodReference ctor = attribute.Constructor; |
|
ITypeReference attributeType = ReadTypeReference(attribute.AttributeType); |
|
IList<ITypeReference> ctorParameterTypes = EmptyList<ITypeReference>.Instance; |
|
if (ctor.HasParameters) { |
|
ctorParameterTypes = new ITypeReference[ctor.Parameters.Count]; |
|
for (int i = 0; i < ctorParameterTypes.Count; i++) { |
|
ctorParameterTypes[i] = ReadTypeReference(ctor.Parameters[i].ParameterType); |
|
} |
|
ctorParameterTypes = interningProvider.InternList(ctorParameterTypes); |
|
} |
|
return interningProvider.Intern(new UnresolvedAttributeBlob(attributeType, ctorParameterTypes, attribute.GetBlob())); |
|
} |
|
#endregion |
|
|
|
#region Security Attributes |
|
/// <summary> |
|
/// Reads a security declaration. |
|
/// </summary> |
|
public IList<IUnresolvedAttribute> ReadSecurityDeclaration(SecurityDeclaration secDecl) |
|
{ |
|
if (secDecl == null) |
|
throw new ArgumentNullException("secDecl"); |
|
var result = new List<IUnresolvedAttribute>(); |
|
AddSecurityAttributes(secDecl, result); |
|
return result; |
|
} |
|
|
|
void AddSecurityAttributes(Mono.Collections.Generic.Collection<SecurityDeclaration> securityDeclarations, IList<IUnresolvedAttribute> targetCollection) |
|
{ |
|
foreach (var secDecl in securityDeclarations) { |
|
AddSecurityAttributes(secDecl, targetCollection); |
|
} |
|
} |
|
|
|
void AddSecurityAttributes(SecurityDeclaration secDecl, IList<IUnresolvedAttribute> targetCollection) |
|
{ |
|
byte[] blob; |
|
try { |
|
blob = secDecl.GetBlob(); |
|
} catch (NotSupportedException) { |
|
return; // https://github.com/icsharpcode/SharpDevelop/issues/284 |
|
} |
|
var blobSecDecl = new UnresolvedSecurityDeclarationBlob((int)secDecl.Action, blob); |
|
targetCollection.AddRange(blobSecDecl.UnresolvedAttributes); |
|
} |
|
#endregion |
|
#endregion |
|
|
|
#region Read Type Definition |
|
DefaultUnresolvedTypeDefinition CreateTopLevelTypeDefinition(TypeDefinition typeDefinition) |
|
{ |
|
string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(typeDefinition.Name); |
|
var td = new DefaultUnresolvedTypeDefinition(typeDefinition.Namespace, name); |
|
td.MetadataToken = typeDefinition.MetadataToken; |
|
if (typeDefinition.HasGenericParameters) |
|
InitTypeParameters(typeDefinition, td.TypeParameters); |
|
return td; |
|
} |
|
|
|
static void InitTypeParameters(TypeDefinition typeDefinition, IList<IUnresolvedTypeParameter> typeParameters) |
|
{ |
|
// Type parameters are initialized within the constructor so that the class can be put into the type storage |
|
// before the rest of the initialization runs - this allows it to be available for early binding as soon as possible. |
|
for (int i = 0; i < typeDefinition.GenericParameters.Count; i++) { |
|
if (typeDefinition.GenericParameters[i].Position != i) |
|
throw new InvalidOperationException("g.Position != i"); |
|
typeParameters.Add(new DefaultUnresolvedTypeParameter( |
|
SymbolKind.TypeDefinition, i, typeDefinition.GenericParameters[i].Name)); |
|
} |
|
} |
|
|
|
void InitTypeParameterConstraints(TypeDefinition typeDefinition, IList<IUnresolvedTypeParameter> typeParameters) |
|
{ |
|
for (int i = 0; i < typeParameters.Count; i++) { |
|
var tp = (DefaultUnresolvedTypeParameter)typeParameters[i]; |
|
AddConstraints(tp, typeDefinition.GenericParameters[i]); |
|
AddAttributes(typeDefinition.GenericParameters[i], tp); |
|
tp.ApplyInterningProvider(interningProvider); |
|
} |
|
} |
|
|
|
void InitTypeDefinition(TypeDefinition typeDefinition, DefaultUnresolvedTypeDefinition td) |
|
{ |
|
td.Kind = GetTypeKind(typeDefinition); |
|
InitTypeModifiers(typeDefinition, td); |
|
InitTypeParameterConstraints(typeDefinition, td.TypeParameters); |
|
|
|
// nested types can be initialized only after generic parameters were created |
|
InitNestedTypes(typeDefinition, td, td.NestedTypes); |
|
AddAttributes(typeDefinition, td); |
|
td.HasExtensionMethods = HasExtensionAttribute(typeDefinition); |
|
|
|
InitBaseTypes(typeDefinition, td.BaseTypes); |
|
|
|
td.AddDefaultConstructorIfRequired = (td.Kind == TypeKind.Struct || td.Kind == TypeKind.Enum); |
|
InitMembers(typeDefinition, td, td.Members); |
|
td.ApplyInterningProvider(interningProvider); |
|
td.Freeze(); |
|
RegisterCecilObject(td, typeDefinition); |
|
} |
|
|
|
void InitBaseTypes(TypeDefinition typeDefinition, IList<ITypeReference> baseTypes) |
|
{ |
|
// set base classes |
|
if (typeDefinition.IsEnum) { |
|
foreach (FieldDefinition enumField in typeDefinition.Fields) { |
|
if (!enumField.IsStatic) { |
|
baseTypes.Add(ReadTypeReference(enumField.FieldType)); |
|
break; |
|
} |
|
} |
|
} else { |
|
if (typeDefinition.BaseType != null) { |
|
baseTypes.Add(ReadTypeReference(typeDefinition.BaseType)); |
|
} |
|
if (typeDefinition.HasInterfaces) { |
|
foreach (var iface in typeDefinition.Interfaces) { |
|
baseTypes.Add(ReadTypeReference(iface.InterfaceType)); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void InitNestedTypes(TypeDefinition typeDefinition, IUnresolvedTypeDefinition declaringTypeDefinition, IList<IUnresolvedTypeDefinition> nestedTypes) |
|
{ |
|
if (!typeDefinition.HasNestedTypes) |
|
return; |
|
foreach (TypeDefinition nestedTypeDef in typeDefinition.NestedTypes) { |
|
TypeAttributes visibility = nestedTypeDef.Attributes & TypeAttributes.VisibilityMask; |
|
if (this.IncludeInternalMembers |
|
|| visibility == TypeAttributes.NestedPublic |
|
|| visibility == TypeAttributes.NestedFamily |
|
|| visibility == TypeAttributes.NestedFamORAssem) |
|
{ |
|
string name = nestedTypeDef.Name; |
|
int pos = name.LastIndexOf('/'); |
|
if (pos > 0) |
|
name = name.Substring(pos + 1); |
|
if (LazyLoad) { |
|
var nestedTd = new LazyCecilTypeDefinition(this, nestedTypeDef, declaringTypeDefinition, name); |
|
nestedTypes.Add(nestedTd); |
|
RegisterCecilObject(nestedTd, nestedTypeDef); |
|
} else { |
|
name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name); |
|
var nestedType = new DefaultUnresolvedTypeDefinition(declaringTypeDefinition, name); |
|
nestedType.MetadataToken = nestedTypeDef.MetadataToken; |
|
InitTypeParameters(nestedTypeDef, nestedType.TypeParameters); |
|
nestedTypes.Add(nestedType); |
|
InitTypeDefinition(nestedTypeDef, nestedType); |
|
} |
|
} |
|
} |
|
} |
|
|
|
static TypeKind GetTypeKind(TypeDefinition typeDefinition) |
|
{ |
|
// set classtype |
|
if (typeDefinition.IsInterface) { |
|
return TypeKind.Interface; |
|
} else if (typeDefinition.IsEnum) { |
|
return TypeKind.Enum; |
|
} else if (typeDefinition.IsValueType) { |
|
return TypeKind.Struct; |
|
} else if (IsDelegate(typeDefinition)) { |
|
return TypeKind.Delegate; |
|
} else if (IsModule(typeDefinition)) { |
|
return TypeKind.Module; |
|
} else { |
|
return TypeKind.Class; |
|
} |
|
} |
|
|
|
static void InitTypeModifiers(TypeDefinition typeDefinition, AbstractUnresolvedEntity td) |
|
{ |
|
td.IsSealed = typeDefinition.IsSealed; |
|
td.IsAbstract = typeDefinition.IsAbstract; |
|
switch (typeDefinition.Attributes & TypeAttributes.VisibilityMask) { |
|
case TypeAttributes.NotPublic: |
|
case TypeAttributes.NestedAssembly: |
|
td.Accessibility = Accessibility.Internal; |
|
break; |
|
case TypeAttributes.Public: |
|
case TypeAttributes.NestedPublic: |
|
td.Accessibility = Accessibility.Public; |
|
break; |
|
case TypeAttributes.NestedPrivate: |
|
td.Accessibility = Accessibility.Private; |
|
break; |
|
case TypeAttributes.NestedFamily: |
|
td.Accessibility = Accessibility.Protected; |
|
break; |
|
case TypeAttributes.NestedFamANDAssem: |
|
td.Accessibility = Accessibility.ProtectedAndInternal; |
|
break; |
|
case TypeAttributes.NestedFamORAssem: |
|
td.Accessibility = Accessibility.ProtectedOrInternal; |
|
break; |
|
} |
|
} |
|
|
|
static bool IsDelegate(TypeDefinition type) |
|
{ |
|
if (type.BaseType != null && type.BaseType.Namespace == "System") { |
|
if (type.BaseType.Name == "MulticastDelegate") |
|
return true; |
|
if (type.BaseType.Name == "Delegate" && type.Name != "MulticastDelegate") |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
static bool IsModule(TypeDefinition type) |
|
{ |
|
if (!type.HasCustomAttributes) |
|
return false; |
|
foreach (var att in type.CustomAttributes) { |
|
if (att.AttributeType.FullName == "Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute" |
|
|| att.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGlobalScopeAttribute") |
|
{ |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
void InitMembers(TypeDefinition typeDefinition, IUnresolvedTypeDefinition td, IList<IUnresolvedMember> members) |
|
{ |
|
if (typeDefinition.HasMethods) { |
|
foreach (MethodDefinition method in typeDefinition.Methods) { |
|
if (IsVisible(method.Attributes) && !IsAccessor(method.SemanticsAttributes)) { |
|
SymbolKind type = SymbolKind.Method; |
|
if (method.IsSpecialName) { |
|
if (method.IsConstructor) |
|
type = SymbolKind.Constructor; |
|
else if (method.Name.StartsWith("op_", StringComparison.Ordinal)) |
|
type = SymbolKind.Operator; |
|
} |
|
members.Add(ReadMethod(method, td, type)); |
|
} |
|
} |
|
} |
|
if (typeDefinition.HasFields) { |
|
foreach (FieldDefinition field in typeDefinition.Fields) { |
|
if (IsVisible(field.Attributes) && !field.IsSpecialName) { |
|
members.Add(ReadField(field, td)); |
|
} |
|
} |
|
} |
|
if (typeDefinition.HasProperties) { |
|
string defaultMemberName = null; |
|
var defaultMemberAttribute = typeDefinition.CustomAttributes.FirstOrDefault( |
|
a => a.AttributeType.FullName == typeof(System.Reflection.DefaultMemberAttribute).FullName); |
|
if (defaultMemberAttribute != null && defaultMemberAttribute.ConstructorArguments.Count == 1) { |
|
defaultMemberName = defaultMemberAttribute.ConstructorArguments[0].Value as string; |
|
} |
|
foreach (PropertyDefinition property in typeDefinition.Properties) { |
|
bool getterVisible = property.GetMethod != null && IsVisible(property.GetMethod.Attributes); |
|
bool setterVisible = property.SetMethod != null && IsVisible(property.SetMethod.Attributes); |
|
if (getterVisible || setterVisible) { |
|
SymbolKind type = SymbolKind.Property; |
|
if (property.HasParameters) { |
|
// Try to detect indexer: |
|
if (property.Name == defaultMemberName) { |
|
type = SymbolKind.Indexer; // normal indexer |
|
} else if (property.Name.EndsWith(".Item", StringComparison.Ordinal) && (property.GetMethod ?? property.SetMethod).HasOverrides) { |
|
// explicit interface implementation of indexer |
|
type = SymbolKind.Indexer; |
|
// We can't really tell parameterized properties and indexers apart in this case without |
|
// resolving the interface, so we rely on the "Item" naming convention instead. |
|
} |
|
} |
|
members.Add(ReadProperty(property, td, type)); |
|
} |
|
} |
|
} |
|
if (typeDefinition.HasEvents) { |
|
foreach (EventDefinition ev in typeDefinition.Events) { |
|
if (ev.AddMethod != null && IsVisible(ev.AddMethod.Attributes)) { |
|
members.Add(ReadEvent(ev, td)); |
|
} |
|
} |
|
} |
|
} |
|
|
|
static bool IsAccessor(MethodSemanticsAttributes semantics) |
|
{ |
|
return !(semantics == MethodSemanticsAttributes.None || semantics == MethodSemanticsAttributes.Other); |
|
} |
|
#endregion |
|
|
|
#region Lazy-Loaded Type Definition |
|
/// <summary> |
|
/// Given an assembly that was created by the CecilLoader with lazy-loading enabled, |
|
/// this method will eagerly load all classes, and free any references to the source Cecil objects. |
|
/// |
|
/// The intended usage pattern for this method is: |
|
/// 1. use lazy-loading to improve the latency when new assemblies have to be loaded |
|
/// 2. later, when the CPU is idle, call FinishLazyLoading() to free up the memory consumed by the Cecil objects |
|
/// </summary> |
|
public static void FinishLazyLoading(IUnresolvedAssembly assembly) |
|
{ |
|
if (assembly == null) |
|
throw new ArgumentNullException("assembly"); |
|
foreach (var type in assembly.TopLevelTypeDefinitions) { |
|
var lctd = type as LazyCecilTypeDefinition; |
|
if (lctd != null) |
|
lctd.InitAndReleaseReferences(); |
|
} |
|
} |
|
|
|
sealed class LazyCecilTypeDefinition : AbstractUnresolvedEntity, IUnresolvedTypeDefinition |
|
{ |
|
// loader + cecilTypeDef, used for lazy-loading; and set to null after lazy loading is complete |
|
CecilLoader loader; |
|
TypeDefinition cecilTypeDef; |
|
|
|
readonly string namespaceName; |
|
readonly TypeKind kind; |
|
readonly IList<IUnresolvedTypeParameter> typeParameters; |
|
|
|
// lazy-loaded fields |
|
IList<ITypeReference> baseTypes; |
|
IList<IUnresolvedTypeDefinition> nestedTypes; |
|
IList<IUnresolvedMember> members; |
|
|
|
public LazyCecilTypeDefinition(CecilLoader loader, TypeDefinition typeDefinition, IUnresolvedTypeDefinition declaringTypeDefinition = null, string name = null) |
|
{ |
|
this.loader = loader; |
|
this.cecilTypeDef = typeDefinition; |
|
this.MetadataToken = typeDefinition.MetadataToken; |
|
this.SymbolKind = SymbolKind.TypeDefinition; |
|
if (declaringTypeDefinition != null) { |
|
this.DeclaringTypeDefinition = declaringTypeDefinition; |
|
this.namespaceName = declaringTypeDefinition.Namespace; |
|
} else { |
|
this.namespaceName = typeDefinition.Namespace; |
|
} |
|
this.Name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name ?? typeDefinition.Name); |
|
var tps = new List<IUnresolvedTypeParameter>(); |
|
InitTypeParameters(typeDefinition, tps); |
|
this.typeParameters = FreezableHelper.FreezeList(tps); |
|
|
|
this.kind = GetTypeKind(typeDefinition); |
|
InitTypeModifiers(typeDefinition, this); |
|
loader.InitTypeParameterConstraints(typeDefinition, typeParameters); |
|
|
|
loader.AddAttributes(typeDefinition, this); |
|
flags[FlagHasExtensionMethods] = HasExtensionAttribute(typeDefinition); |
|
|
|
this.ApplyInterningProvider(loader.interningProvider); |
|
this.Freeze(); |
|
} |
|
|
|
public override string Namespace { |
|
get { return namespaceName; } |
|
set { throw new NotSupportedException(); } |
|
} |
|
|
|
public override string ReflectionName { |
|
get { return this.FullTypeName.ReflectionName; } |
|
} |
|
|
|
public FullTypeName FullTypeName { |
|
get { |
|
IUnresolvedTypeDefinition declaringTypeDef = this.DeclaringTypeDefinition; |
|
if (declaringTypeDef != null) { |
|
return declaringTypeDef.FullTypeName.NestedType(this.Name, typeParameters.Count - declaringTypeDef.TypeParameters.Count); |
|
} else { |
|
return new TopLevelTypeName(namespaceName, this.Name, typeParameters.Count); |
|
} |
|
} |
|
} |
|
|
|
public TypeKind Kind { |
|
get { return kind; } |
|
} |
|
|
|
public IList<IUnresolvedTypeParameter> TypeParameters { |
|
get { return typeParameters; } |
|
} |
|
|
|
public IList<ITypeReference> BaseTypes { |
|
get { |
|
var result = LazyInit.VolatileRead(ref this.baseTypes); |
|
if (result != null) { |
|
return result; |
|
} else { |
|
return LazyInit.GetOrSet(ref this.baseTypes, TryInitBaseTypes()); |
|
} |
|
} |
|
} |
|
|
|
IList<ITypeReference> TryInitBaseTypes() |
|
{ |
|
var loader = LazyInit.VolatileRead(ref this.loader); |
|
var cecilTypeDef = LazyInit.VolatileRead(ref this.cecilTypeDef); |
|
if (loader == null || cecilTypeDef == null) { |
|
// Cannot initialize because the references to loader/cecilTypeDef |
|
// have already been cleared. |
|
// This can only happen if the class was loaded by another thread concurrently to the TryInitBaseTypes() call, |
|
// so the GetOrSet() call in the property will retrieve the value set by the other thread. |
|
return null; |
|
} |
|
lock (loader.currentModule) { |
|
var result = new List<ITypeReference>(); |
|
loader.InitBaseTypes(cecilTypeDef, result); |
|
return FreezableHelper.FreezeList(result); |
|
} |
|
} |
|
|
|
public IList<IUnresolvedTypeDefinition> NestedTypes { |
|
get { |
|
var result = LazyInit.VolatileRead(ref this.nestedTypes); |
|
if (result != null) { |
|
return result; |
|
} else { |
|
return LazyInit.GetOrSet(ref this.nestedTypes, TryInitNestedTypes()); |
|
} |
|
} |
|
} |
|
|
|
IList<IUnresolvedTypeDefinition> TryInitNestedTypes() |
|
{ |
|
var loader = LazyInit.VolatileRead(ref this.loader); |
|
var cecilTypeDef = LazyInit.VolatileRead(ref this.cecilTypeDef); |
|
if (loader == null || cecilTypeDef == null) { |
|
// Cannot initialize because the references to loader/cecilTypeDef |
|
// have already been cleared. |
|
// This can only happen if the class was loaded by another thread concurrently to the TryInitNestedTypes() call, |
|
// so the GetOrSet() call in the property will retrieve the value set by the other thread. |
|
return null; |
|
} |
|
lock (loader.currentModule) { |
|
var result = new List<IUnresolvedTypeDefinition>(); |
|
loader.InitNestedTypes(cecilTypeDef, this, result); |
|
return FreezableHelper.FreezeList(result); |
|
} |
|
} |
|
|
|
public IList<IUnresolvedMember> Members { |
|
get { |
|
var result = LazyInit.VolatileRead(ref this.members); |
|
if (result != null) { |
|
return result; |
|
} else { |
|
return LazyInit.GetOrSet(ref this.members, TryInitMembers()); |
|
} |
|
} |
|
} |
|
|
|
IList<IUnresolvedMember> TryInitMembers() |
|
{ |
|
var loader = LazyInit.VolatileRead(ref this.loader); |
|
var cecilTypeDef = LazyInit.VolatileRead(ref this.cecilTypeDef); |
|
if (loader == null || cecilTypeDef == null) { |
|
// Cannot initialize because the references to loader/cecilTypeDef |
|
// have already been cleared. |
|
// This can only happen if the class was loaded by another thread concurrently to the TryInitMembers() call, |
|
// so the GetOrSet() call in the property will retrieve the value set by the other thread. |
|
return null; |
|
} |
|
lock (loader.currentModule) { |
|
if (this.members != null) |
|
return this.members; |
|
var result = new List<IUnresolvedMember>(); |
|
loader.InitMembers(cecilTypeDef, this, result); |
|
return FreezableHelper.FreezeList(result); |
|
} |
|
} |
|
|
|
public void InitAndReleaseReferences() |
|
{ |
|
if (LazyInit.VolatileRead(ref this.baseTypes) == null) |
|
LazyInit.GetOrSet(ref this.baseTypes, TryInitBaseTypes()); |
|
if (LazyInit.VolatileRead(ref this.nestedTypes) == null) |
|
LazyInit.GetOrSet(ref this.nestedTypes, TryInitNestedTypes()); |
|
if (LazyInit.VolatileRead(ref this.members) == null) |
|
LazyInit.GetOrSet(ref this.members, TryInitMembers()); |
|
Thread.MemoryBarrier(); // commit lazily-initialized fields to memory before nulling out the references |
|
// Allow the GC to collect the cecil type definition |
|
loader = null; |
|
cecilTypeDef = null; |
|
} |
|
|
|
public IEnumerable<IUnresolvedMethod> Methods { |
|
get { return Members.OfType<IUnresolvedMethod>(); } |
|
} |
|
|
|
public IEnumerable<IUnresolvedProperty> Properties { |
|
get { return Members.OfType<IUnresolvedProperty>(); } |
|
} |
|
|
|
public IEnumerable<IUnresolvedField> Fields { |
|
get { return Members.OfType<IUnresolvedField>(); } |
|
} |
|
|
|
public IEnumerable<IUnresolvedEvent> Events { |
|
get { return Members.OfType<IUnresolvedEvent>(); } |
|
} |
|
|
|
public bool AddDefaultConstructorIfRequired { |
|
get { return kind == TypeKind.Struct || kind == TypeKind.Enum; } |
|
} |
|
|
|
public bool? HasExtensionMethods { |
|
get { return flags[FlagHasExtensionMethods]; } |
|
// we always return true or false, never null. |
|
// FlagHasNoExtensionMethods is unused in LazyCecilTypeDefinition |
|
} |
|
|
|
public bool IsPartial { |
|
get { return false; } |
|
} |
|
|
|
public override object Clone() |
|
{ |
|
throw new NotSupportedException(); |
|
} |
|
|
|
public IType Resolve(ITypeResolveContext context) |
|
{ |
|
if (context == null) |
|
throw new ArgumentNullException("context"); |
|
if (context.CurrentAssembly == null) |
|
throw new ArgumentException("An ITypeDefinition cannot be resolved in a context without a current assembly."); |
|
return context.CurrentAssembly.ResolveTypeDefToken(this.MetadataToken) |
|
?? (IType)new UnknownType(this.Namespace, this.Name, this.TypeParameters.Count); |
|
} |
|
|
|
public ITypeResolveContext CreateResolveContext(ITypeResolveContext parentContext) |
|
{ |
|
return parentContext; |
|
} |
|
} |
|
#endregion |
|
|
|
#region Read Method |
|
public IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, SymbolKind methodType = SymbolKind.Method) |
|
{ |
|
return ReadMethod(method, parentType, methodType, null); |
|
} |
|
|
|
IUnresolvedMethod ReadMethod(MethodDefinition method, IUnresolvedTypeDefinition parentType, SymbolKind methodType, IUnresolvedMember accessorOwner) |
|
{ |
|
if (method == null) |
|
return null; |
|
DefaultUnresolvedMethod m = new DefaultUnresolvedMethod(parentType, method.Name); |
|
m.SymbolKind = methodType; |
|
m.AccessorOwner = accessorOwner; |
|
m.HasBody = method.HasBody; |
|
if (method.HasGenericParameters) { |
|
for (int i = 0; i < method.GenericParameters.Count; i++) { |
|
if (method.GenericParameters[i].Position != i) |
|
throw new InvalidOperationException("g.Position != i"); |
|
m.TypeParameters.Add(new DefaultUnresolvedTypeParameter( |
|
SymbolKind.Method, i, method.GenericParameters[i].Name)); |
|
} |
|
for (int i = 0; i < method.GenericParameters.Count; i++) { |
|
var tp = (DefaultUnresolvedTypeParameter)m.TypeParameters[i]; |
|
AddConstraints(tp, method.GenericParameters[i]); |
|
AddAttributes(method.GenericParameters[i], tp); |
|
tp.ApplyInterningProvider(interningProvider); |
|
} |
|
} |
|
|
|
m.ReturnType = ReadTypeReference(method.ReturnType, typeAttributes: method.MethodReturnType, isFromSignature: true); |
|
|
|
if (HasAnyAttributes(method)) |
|
AddAttributes(method, m.Attributes, m.ReturnTypeAttributes); |
|
TranslateModifiers(method, m); |
|
|
|
if (method.HasParameters) { |
|
foreach (ParameterDefinition p in method.Parameters) { |
|
m.Parameters.Add(ReadParameter(p)); |
|
} |
|
} |
|
if (method.CallingConvention == MethodCallingConvention.VarArg) { |
|
m.Parameters.Add(new DefaultUnresolvedParameter(SpecialType.ArgList, string.Empty)); |
|
} |
|
|
|
// mark as extension method if the attribute is set |
|
if (method.IsStatic && HasExtensionAttribute(method)) { |
|
m.IsExtensionMethod = true; |
|
} |
|
|
|
int lastDot = method.Name.LastIndexOf('.'); |
|
if (lastDot >= 0 && method.HasOverrides) { |
|
// To be consistent with the parser-initialized type system, shorten the method name: |
|
if (ShortenInterfaceImplNames) |
|
m.Name = method.Name.Substring(lastDot + 1); |
|
m.IsExplicitInterfaceImplementation = true; |
|
foreach (var or in method.Overrides) { |
|
m.ExplicitInterfaceImplementations.Add(new DefaultMemberReference( |
|
accessorOwner != null ? SymbolKind.Accessor : SymbolKind.Method, |
|
ReadTypeReference(or.DeclaringType), |
|
or.Name, or.GenericParameters.Count, m.Parameters.Select(p => p.Type).ToList())); |
|
} |
|
} |
|
|
|
FinishReadMember(m, method); |
|
return m; |
|
} |
|
|
|
static bool HasExtensionAttribute(ICustomAttributeProvider provider) |
|
{ |
|
if (provider.HasCustomAttributes) { |
|
foreach (var attr in provider.CustomAttributes) { |
|
if (attr.AttributeType.Name == "ExtensionAttribute" && attr.AttributeType.Namespace == "System.Runtime.CompilerServices") |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
bool IsVisible(MethodAttributes att) |
|
{ |
|
att &= MethodAttributes.MemberAccessMask; |
|
return IncludeInternalMembers |
|
|| att == MethodAttributes.Public |
|
|| att == MethodAttributes.Family |
|
|| att == MethodAttributes.FamORAssem; |
|
} |
|
|
|
static Accessibility GetAccessibility(MethodAttributes attr) |
|
{ |
|
switch (attr & MethodAttributes.MemberAccessMask) { |
|
case MethodAttributes.Public: |
|
return Accessibility.Public; |
|
case MethodAttributes.FamANDAssem: |
|
return Accessibility.ProtectedAndInternal; |
|
case MethodAttributes.Assembly: |
|
return Accessibility.Internal; |
|
case MethodAttributes.Family: |
|
return Accessibility.Protected; |
|
case MethodAttributes.FamORAssem: |
|
return Accessibility.ProtectedOrInternal; |
|
default: |
|
return Accessibility.Private; |
|
} |
|
} |
|
|
|
void TranslateModifiers(MethodDefinition method, AbstractUnresolvedMember m) |
|
{ |
|
if (m.DeclaringTypeDefinition.Kind == TypeKind.Interface) { |
|
// interface members don't have modifiers, but we want to handle them as "public abstract" |
|
m.Accessibility = Accessibility.Public; |
|
m.IsAbstract = true; |
|
} else { |
|
m.Accessibility = GetAccessibility(method.Attributes); |
|
if (method.IsAbstract) { |
|
m.IsAbstract = true; |
|
m.IsOverride = !method.IsNewSlot; |
|
} else if (method.IsFinal) { |
|
if (!method.IsNewSlot) { |
|
m.IsSealed = true; |
|
m.IsOverride = true; |
|
} |
|
} else if (method.IsVirtual) { |
|
if (method.IsNewSlot) |
|
m.IsVirtual = true; |
|
else |
|
m.IsOverride = true; |
|
} |
|
m.IsStatic = method.IsStatic; |
|
} |
|
} |
|
#endregion |
|
|
|
#region Read Parameter |
|
public IUnresolvedParameter ReadParameter(ParameterDefinition parameter) |
|
{ |
|
if (parameter == null) |
|
throw new ArgumentNullException("parameter"); |
|
var type = ReadTypeReference(parameter.ParameterType, typeAttributes: parameter, isFromSignature: true); |
|
var p = new DefaultUnresolvedParameter(type, interningProvider.Intern(parameter.Name)); |
|
|
|
if (parameter.ParameterType is Mono.Cecil.ByReferenceType) { |
|
if (!parameter.IsIn && parameter.IsOut) |
|
p.IsOut = true; |
|
else |
|
p.IsRef = true; |
|
} |
|
AddAttributes(parameter, p); |
|
|
|
if (parameter.IsOptional) { |
|
p.DefaultValue = CreateSimpleConstantValue(type, parameter.Constant); |
|
} |
|
|
|
if (parameter.ParameterType is Mono.Cecil.ArrayType) { |
|
foreach (CustomAttribute att in parameter.CustomAttributes) { |
|
if (att.AttributeType.FullName == typeof(ParamArrayAttribute).FullName) { |
|
p.IsParams = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
return interningProvider.Intern(p); |
|
} |
|
#endregion |
|
|
|
#region Read Field |
|
bool IsVisible(FieldAttributes att) |
|
{ |
|
att &= FieldAttributes.FieldAccessMask; |
|
return IncludeInternalMembers |
|
|| att == FieldAttributes.Public |
|
|| att == FieldAttributes.Family |
|
|| att == FieldAttributes.FamORAssem; |
|
} |
|
|
|
decimal? TryDecodeDecimalConstantAttribute(CustomAttribute attribute) |
|
{ |
|
if (attribute.ConstructorArguments.Count != 5) |
|
return null; |
|
|
|
BlobReader reader = new BlobReader(attribute.GetBlob(), null); |
|
if (reader.ReadUInt16() != 0x0001) { |
|
Debug.WriteLine("Unknown blob prolog"); |
|
return null; |
|
} |
|
|
|
// DecimalConstantAttribute has the arguments (byte scale, byte sign, uint hi, uint mid, uint low) or (byte scale, byte sign, int hi, int mid, int low) |
|
// Both of these invoke the Decimal constructor (int lo, int mid, int hi, bool isNegative, byte scale) with explicit argument conversions if required. |
|
var ctorArgs = new object[attribute.ConstructorArguments.Count]; |
|
for (int i = 0; i < ctorArgs.Length; i++) { |
|
switch (attribute.ConstructorArguments[i].Type.FullName) { |
|
case "System.Byte": |
|
ctorArgs[i] = reader.ReadByte(); |
|
break; |
|
case "System.Int32": |
|
ctorArgs[i] = reader.ReadInt32(); |
|
break; |
|
case "System.UInt32": |
|
ctorArgs[i] = unchecked((int)reader.ReadUInt32()); |
|
break; |
|
default: |
|
return null; |
|
} |
|
} |
|
|
|
if (!ctorArgs.Select(a => a.GetType()).SequenceEqual(new[] { typeof(byte), typeof(byte), typeof(int), typeof(int), typeof(int) })) |
|
return null; |
|
|
|
return new decimal((int)ctorArgs[4], (int)ctorArgs[3], (int)ctorArgs[2], (byte)ctorArgs[1] != 0, (byte)ctorArgs[0]); |
|
} |
|
|
|
public IUnresolvedField ReadField(FieldDefinition field, IUnresolvedTypeDefinition parentType) |
|
{ |
|
if (field == null) |
|
throw new ArgumentNullException("field"); |
|
if (parentType == null) |
|
throw new ArgumentNullException("parentType"); |
|
|
|
DefaultUnresolvedField f = new DefaultUnresolvedField(parentType, field.Name); |
|
f.Accessibility = GetAccessibility(field.Attributes); |
|
f.IsReadOnly = field.IsInitOnly; |
|
f.IsStatic = field.IsStatic; |
|
f.ReturnType = ReadTypeReference(field.FieldType, typeAttributes: field, isFromSignature: true); |
|
if (field.HasConstant) { |
|
f.ConstantValue = CreateSimpleConstantValue(f.ReturnType, field.Constant); |
|
} |
|
else { |
|
var decConstant = field.CustomAttributes.FirstOrDefault(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.DecimalConstantAttribute"); |
|
if (decConstant != null) { |
|
var constValue = TryDecodeDecimalConstantAttribute(decConstant); |
|
if (constValue != null) |
|
f.ConstantValue = CreateSimpleConstantValue(f.ReturnType, constValue); |
|
} |
|
} |
|
AddAttributes(field, f); |
|
|
|
RequiredModifierType modreq = field.FieldType as RequiredModifierType; |
|
if (modreq != null && modreq.ModifierType.FullName == typeof(IsVolatile).FullName) { |
|
f.IsVolatile = true; |
|
} |
|
|
|
FinishReadMember(f, field); |
|
return f; |
|
} |
|
|
|
static Accessibility GetAccessibility(FieldAttributes attr) |
|
{ |
|
switch (attr & FieldAttributes.FieldAccessMask) { |
|
case FieldAttributes.Public: |
|
return Accessibility.Public; |
|
case FieldAttributes.FamANDAssem: |
|
return Accessibility.ProtectedAndInternal; |
|
case FieldAttributes.Assembly: |
|
return Accessibility.Internal; |
|
case FieldAttributes.Family: |
|
return Accessibility.Protected; |
|
case FieldAttributes.FamORAssem: |
|
return Accessibility.ProtectedOrInternal; |
|
default: |
|
return Accessibility.Private; |
|
} |
|
} |
|
#endregion |
|
|
|
#region Type Parameter Constraints |
|
void AddConstraints(DefaultUnresolvedTypeParameter tp, GenericParameter g) |
|
{ |
|
switch (g.Attributes & GenericParameterAttributes.VarianceMask) { |
|
case GenericParameterAttributes.Contravariant: |
|
tp.Variance = VarianceModifier.Contravariant; |
|
break; |
|
case GenericParameterAttributes.Covariant: |
|
tp.Variance = VarianceModifier.Covariant; |
|
break; |
|
} |
|
|
|
tp.HasReferenceTypeConstraint = g.HasReferenceTypeConstraint; |
|
tp.HasValueTypeConstraint = g.HasNotNullableValueTypeConstraint; |
|
tp.HasDefaultConstructorConstraint = g.HasDefaultConstructorConstraint; |
|
|
|
if (g.HasConstraints) { |
|
foreach (TypeReference constraint in g.Constraints) { |
|
tp.Constraints.Add(ReadTypeReference(constraint)); |
|
} |
|
} |
|
} |
|
#endregion |
|
|
|
#region Read Property |
|
|
|
Accessibility MergePropertyAccessibility (Accessibility left, Accessibility right) |
|
{ |
|
if (left == Accessibility.Public || right == Accessibility.Public) |
|
return Accessibility.Public; |
|
|
|
if (left == Accessibility.ProtectedOrInternal || right == Accessibility.ProtectedOrInternal) |
|
return Accessibility.ProtectedOrInternal; |
|
|
|
if (left == Accessibility.Protected && right == Accessibility.Internal || |
|
left == Accessibility.Internal && right == Accessibility.Protected) |
|
return Accessibility.ProtectedOrInternal; |
|
|
|
if (left == Accessibility.Protected || right == Accessibility.Protected) |
|
return Accessibility.Protected; |
|
|
|
if (left == Accessibility.Internal || right == Accessibility.Internal) |
|
return Accessibility.Internal; |
|
|
|
if (left == Accessibility.ProtectedAndInternal || right == Accessibility.ProtectedAndInternal) |
|
return Accessibility.ProtectedAndInternal; |
|
|
|
return left; |
|
} |
|
|
|
public IUnresolvedProperty ReadProperty(PropertyDefinition property, IUnresolvedTypeDefinition parentType, SymbolKind propertyType = SymbolKind.Property) |
|
{ |
|
if (property == null) |
|
throw new ArgumentNullException("property"); |
|
if (parentType == null) |
|
throw new ArgumentNullException("parentType"); |
|
DefaultUnresolvedProperty p = new DefaultUnresolvedProperty(parentType, property.Name); |
|
p.SymbolKind = propertyType; |
|
TranslateModifiers(property.GetMethod ?? property.SetMethod, p); |
|
if (property.GetMethod != null && property.SetMethod != null) |
|
p.Accessibility = MergePropertyAccessibility (GetAccessibility (property.GetMethod.Attributes), GetAccessibility (property.SetMethod.Attributes)); |
|
|
|
p.ReturnType = ReadTypeReference(property.PropertyType, typeAttributes: property); |
|
|
|
p.Getter = ReadMethod(property.GetMethod, parentType, SymbolKind.Accessor, p); |
|
p.Setter = ReadMethod(property.SetMethod, parentType, SymbolKind.Accessor, p); |
|
|
|
if (property.HasParameters) { |
|
foreach (ParameterDefinition par in property.Parameters) { |
|
p.Parameters.Add(ReadParameter(par)); |
|
} |
|
} |
|
AddAttributes(property, p); |
|
|
|
var accessor = p.Getter ?? p.Setter; |
|
if (accessor != null && accessor.IsExplicitInterfaceImplementation) { |
|
if (ShortenInterfaceImplNames) |
|
p.Name = property.Name.Substring(property.Name.LastIndexOf('.') + 1); |
|
p.IsExplicitInterfaceImplementation = true; |
|
foreach (var mr in accessor.ExplicitInterfaceImplementations) { |
|
p.ExplicitInterfaceImplementations.Add(new AccessorOwnerMemberReference(mr)); |
|
} |
|
} |
|
|
|
FinishReadMember(p, property); |
|
return p; |
|
} |
|
#endregion |
|
|
|
#region Read Event |
|
public IUnresolvedEvent ReadEvent(EventDefinition ev, IUnresolvedTypeDefinition parentType) |
|
{ |
|
if (ev == null) |
|
throw new ArgumentNullException("ev"); |
|
if (parentType == null) |
|
throw new ArgumentNullException("parentType"); |
|
|
|
DefaultUnresolvedEvent e = new DefaultUnresolvedEvent(parentType, ev.Name); |
|
TranslateModifiers(ev.AddMethod, e); |
|
e.ReturnType = ReadTypeReference(ev.EventType, typeAttributes: ev); |
|
|
|
e.AddAccessor = ReadMethod(ev.AddMethod, parentType, SymbolKind.Accessor, e); |
|
e.RemoveAccessor = ReadMethod(ev.RemoveMethod, parentType, SymbolKind.Accessor, e); |
|
e.InvokeAccessor = ReadMethod(ev.InvokeMethod, parentType, SymbolKind.Accessor, e); |
|
|
|
AddAttributes(ev, e); |
|
|
|
var accessor = e.AddAccessor ?? e.RemoveAccessor ?? e.InvokeAccessor; |
|
if (accessor != null && accessor.IsExplicitInterfaceImplementation) { |
|
if (ShortenInterfaceImplNames) |
|
e.Name = ev.Name.Substring(ev.Name.LastIndexOf('.') + 1); |
|
e.IsExplicitInterfaceImplementation = true; |
|
foreach (var mr in accessor.ExplicitInterfaceImplementations) { |
|
e.ExplicitInterfaceImplementations.Add(new AccessorOwnerMemberReference(mr)); |
|
} |
|
} |
|
|
|
FinishReadMember(e, ev); |
|
|
|
return e; |
|
} |
|
#endregion |
|
|
|
#region FinishReadMember / Interning |
|
void FinishReadMember(AbstractUnresolvedMember member, MemberReference cecilDefinition) |
|
{ |
|
member.MetadataToken = cecilDefinition.MetadataToken; |
|
member.ApplyInterningProvider(interningProvider); |
|
member.Freeze(); |
|
RegisterCecilObject(member, cecilDefinition); |
|
} |
|
#endregion |
|
|
|
#region Type system translation table |
|
void RegisterCecilObject(IUnresolvedEntity typeSystemObject, MemberReference cecilObject) |
|
{ |
|
if (OnEntityLoaded != null) |
|
OnEntityLoaded(typeSystemObject, cecilObject); |
|
} |
|
#endregion |
|
} |
|
}
|
|
|