.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.
 
 
 
 

1759 lines
70 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.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.TypeSystem
{
/// <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 MetadataLoader
{
#region Options
/// <summary>
/// Specifies whether to include internal members. The default is false.
/// </summary>
public bool IncludeInternalMembers { get; set; }
/// <summary>
/// Gets/Sets the cancellation token used by the assembly loader.
/// </summary>
public CancellationToken CancellationToken { get; set; }
InterningProvider interningProvider = new SimpleInterningProvider();
/// <summary>
/// Gets/Sets the interning provider.
/// </summary>
public InterningProvider InterningProvider {
get { return interningProvider; }
set {
if (value == null)
throw new ArgumentNullException();
interningProvider = value;
}
}
/// <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>
/// 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, EntityHandle> OnEntityLoaded { get; set; }
/// <summary>
/// Gets a value indicating whether this instance stores references to the cecil objects.
/// </summary>
/// <value>
/// <c>true</c> if this instance has references to the cecil objects; otherwise, <c>false</c>.
/// </value>
public bool HasCecilReferences { get { return typeSystemTranslationTable != null; } }
bool shortenInterfaceImplNames = true;
/// <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 {
return shortenInterfaceImplNames;
}
set {
shortenInterfaceImplNames = value;
}
}
#endregion
MetadataReader currentModule;
DefaultUnresolvedAssembly currentAssembly;
/// <summary>
/// Initializes a new instance of the <see cref="MetadataLoader"/> class.
/// </summary>
public MetadataLoader()
{
}
/// <summary>
/// Creates a nested CecilLoader for lazy-loading.
/// </summary>
private MetadataLoader(MetadataLoader loader)
{
// use a shared typeSystemTranslationTable
this.typeSystemTranslationTable = loader.typeSystemTranslationTable;
this.IncludeInternalMembers = loader.IncludeInternalMembers;
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 a module definition into a project content.
/// </summary>
/// <returns>Unresolved type system representing the assembly</returns>
public IUnresolvedAssembly LoadModule(MetadataReader module)
{
this.currentModule = module;
// Read assembly and module attributes
IList<IUnresolvedAttribute> assemblyAttributes = new List<IUnresolvedAttribute>();
IList<IUnresolvedAttribute> moduleAttributes = new List<IUnresolvedAttribute>();
AssemblyDefinition assemblyDefinition = default(AssemblyDefinition);
if (currentModule.IsAssembly) {
AddAttributes(currentModule.GetAssemblyDefinition(), assemblyAttributes);
}
AddAttributes(Handle.ModuleDefinition, moduleAttributes);
assemblyAttributes = interningProvider.InternList(assemblyAttributes);
moduleAttributes = interningProvider.InternList(moduleAttributes);
this.currentAssembly = new DefaultUnresolvedAssembly(currentModule.IsAssembly ? currentModule.GetFullAssemblyName() : currentModule.GetString(currentModule.GetModuleDefinition().Name));
currentAssembly.AssemblyAttributes.AddRange(assemblyAttributes);
currentAssembly.ModuleAttributes.AddRange(assemblyAttributes);
// Register type forwarders:
foreach (ExportedTypeHandle t in currentModule.ExportedTypes) {
var type = currentModule.GetExportedType(t);
if (type.IsForwarder) {
throw new NotImplementedException();/*
switch (type.Implementation.Kind) {
case HandleKind.AssemblyFile:
throw new NotImplementedException(); // type is defined in another module.
case HandleKind.ExportedType:
break;
case HandleKind.AssemblyReference:
break;
}
int typeParameterCount;
string ns = currentModule.GetString(type.Namespace);
string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(currentModule.GetString(type.Name), out typeParameterCount);
ns = interningProvider.Intern(ns);
name = interningProvider.Intern(name);
var typeRef = new GetClassTypeReference(, ns, name, typeParameterCount);
typeRef = interningProvider.Intern(typeRef);
var key = new TopLevelTypeName(ns, name, typeParameterCount);
currentAssembly.AddTypeForwarder(key, typeRef);*/
}
}
// Create and register all types:
MetadataLoader cecilLoaderCloneForLazyLoading = LazyLoad ? new MetadataLoader(this) : null;
List<TypeDefinitionHandle> cecilTypeDefs = new List<TypeDefinitionHandle>();
List<DefaultUnresolvedTypeDefinition> typeDefs = new List<DefaultUnresolvedTypeDefinition>();
foreach (TypeDefinitionHandle h in module.TypeDefinitions) {
this.CancellationToken.ThrowIfCancellationRequested();
var td = module.GetTypeDefinition(h);
if (this.IncludeInternalMembers || (td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) {
string name = module.GetString(td.Name);
if (name.Length == 0)
continue;
if (this.LazyLoad) {
var t = new LazySRMTypeDefinition(cecilLoaderCloneForLazyLoading, module, h);
currentAssembly.AddTypeDefinition(t);
RegisterCecilObject(t, h);
} else {
var t = CreateTopLevelTypeDefinition(td);
cecilTypeDefs.Add(h);
typeDefs.Add(t);
currentAssembly.AddTypeDefinition(t);
// The registration will happen after the members are initialized
}
}
}
// Initialize the type's members:
for (int i = 0; i < typeDefs.Count; i++) {
InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]);
}
//AddToTypeSystemTranslationTable(this.currentAssembly, assemblyDefinition);
// 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(MetadataReader 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(TypeDefinitionHandle typeDefinition)
{
if (typeDefinition.IsNil)
throw new ArgumentNullException(nameof(typeDefinition));
var td = CreateTopLevelTypeDefinition(currentModule.GetTypeDefinition(typeDefinition));
InitTypeDefinition(typeDefinition, td);
return td;
}
#endregion
#region Load Assembly From Disk
public IUnresolvedAssembly LoadAssemblyFile(string fileName)
{
if (fileName == null)
throw new ArgumentNullException(nameof(fileName));
using (var module = new PEReader(new FileStream(fileName, FileMode.Open)))
return LoadModule(module.GetMetadataReader());
}
#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>
public ITypeReference ReadTypeReference(EntityHandle type, CustomAttributeHandleCollection typeAttributes = default(CustomAttributeHandleCollection))
{
int typeIndex = 0;
return CreateType(type, typeAttributes, ref typeIndex);
}
class DynamicTypeVisitor : TypeVisitor
{
public override IType VisitPointerType(PointerType type)
{
return base.VisitPointerType(type);
}
}
ITypeReference CreateType(EntityHandle type, CustomAttributeHandleCollection typeAttributes, ref int typeIndex)
{
ITypeReference CreateTypeReference(TypeReferenceHandle handle)
{
var t = currentModule.GetTypeReference(handle);
var asmref = handle.GetDeclaringAssembly(currentModule);
if (asmref.IsNil)
return new GetClassTypeReference(handle.GetFullTypeName(currentModule), DefaultAssemblyReference.CurrentAssembly);
var asm = currentModule.GetAssemblyReference(asmref);
return new GetClassTypeReference(handle.GetFullTypeName(currentModule), new DefaultAssemblyReference(currentModule.GetString(asm.Name)));
}
switch (type.Kind) {
case HandleKind.TypeSpecification:
return new SignatureTypeReference((TypeSpecificationHandle)type, currentModule);
case HandleKind.TypeReference:
return CreateTypeReference((TypeReferenceHandle)type);
case HandleKind.TypeDefinition:
return new DefaultUnresolvedTypeDefinition(type.GetFullTypeName(currentModule).ReflectionName);
default:
throw new NotSupportedException();
}
}
#endregion
#region Read Attributes
#region Assembly Attributes
static readonly ITypeReference assemblyVersionAttributeTypeRef = typeof(System.Reflection.AssemblyVersionAttribute).ToTypeReference();
void AddAttributes(AssemblyDefinition assembly, IList<IUnresolvedAttribute> outputList)
{
AddCustomAttributes(currentModule.GetCustomAttributes((EntityHandle)Handle.AssemblyDefinition), outputList);
AddSecurityAttributes(assembly.GetDeclarativeSecurityAttributes(), outputList);
// AssemblyVersionAttribute
if (assembly.Version != null) {
var assemblyVersion = new DefaultUnresolvedAttribute(assemblyVersionAttributeTypeRef, new[] { KnownTypeReference.String });
assemblyVersion.PositionalArguments.Add(CreateSimpleConstantValue(KnownTypeReference.String, assembly.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(ModuleDefinitionHandle module, IList<IUnresolvedAttribute> outputList)
{
AddCustomAttributes(currentModule.GetCustomAttributes(module), 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(Parameter parameter, DefaultUnresolvedParameter targetParameter)
{
if (!targetParameter.IsOut) {
if ((parameter.Attributes & ParameterAttributes.In) == ParameterAttributes.In)
targetParameter.Attributes.Add(inAttribute);
if ((parameter.Attributes & ParameterAttributes.Out) == ParameterAttributes.Out)
targetParameter.Attributes.Add(outAttribute);
}
AddCustomAttributes(parameter.GetCustomAttributes(), targetParameter.Attributes);
AddMarshalInfo(parameter.GetMarshallingDescriptor(), targetParameter.Attributes);
}
#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();
bool HasAnyAttributes(MethodDefinition methodDefinition)
{
if ((methodDefinition.Attributes & MethodAttributes.PinvokeImpl) == MethodAttributes.PinvokeImpl)
return true;
if ((methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask) != 0)
return true;
if (methodDefinition.GetParameters().Count > 0) {
var retParam = currentModule.GetParameter(methodDefinition.GetParameters().First());
if (retParam.GetCustomAttributes().Count > 0)
return true;
if ((retParam.Attributes & ParameterAttributes.HasFieldMarshal) == ParameterAttributes.HasFieldMarshal)
return true;
}
return methodDefinition.GetCustomAttributes().Count > 0;
}
void AddAttributes(MethodDefinition methodDefinition, IList<IUnresolvedAttribute> attributes, IList<IUnresolvedAttribute> returnTypeAttributes)
{
MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask;
#region DllImportAttribute
var info = methodDefinition.GetImport();
if ((methodDefinition.Attributes & MethodAttributes.PinvokeImpl) == MethodAttributes.PinvokeImpl && !info.Module.IsNil) {
var dllImport = new DefaultUnresolvedAttribute(dllImportAttributeTypeRef, new[] { KnownTypeReference.String });
dllImport.PositionalArguments.Add(CreateSimpleConstantValue(KnownTypeReference.String, currentModule.GetString(currentModule.GetModuleReference(info.Module).Name)));
if ((info.Attributes & MethodImportAttributes.BestFitMappingDisable) == MethodImportAttributes.BestFitMappingDisable)
dllImport.AddNamedFieldArgument("BestFitMapping", falseValue);
if ((info.Attributes & MethodImportAttributes.BestFitMappingEnable) == MethodImportAttributes.BestFitMappingEnable)
dllImport.AddNamedFieldArgument("BestFitMapping", trueValue);
CallingConvention callingConvention;
switch (info.Attributes & MethodImportAttributes.CallingConventionMask) {
case 0:
Debug.WriteLine ($"P/Invoke calling convention not set on: {methodDefinition.GetDeclaringType().GetFullTypeName(currentModule).ToString()}.{currentModule.GetString(methodDefinition.Name)}");
callingConvention = 0;
break;
case MethodImportAttributes.CallingConventionCDecl:
callingConvention = CallingConvention.Cdecl;
break;
case MethodImportAttributes.CallingConventionFastCall:
callingConvention = CallingConvention.FastCall;
break;
case MethodImportAttributes.CallingConventionStdCall:
callingConvention = CallingConvention.StdCall;
break;
case MethodImportAttributes.CallingConventionThisCall:
callingConvention = CallingConvention.ThisCall;
break;
case MethodImportAttributes.CallingConventionWinApi:
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 & MethodImportAttributes.CharSetMask) {
case MethodImportAttributes.CharSetAnsi:
charSet = CharSet.Ansi;
break;
case MethodImportAttributes.CharSetAuto:
charSet = CharSet.Auto;
break;
case MethodImportAttributes.CharSetUnicode:
charSet = CharSet.Unicode;
break;
}
if (charSet != CharSet.None)
dllImport.AddNamedFieldArgument("CharSet", CreateSimpleConstantValue(charSetTypeRef, (int)charSet));
if (!info.Name.IsNil && info.Name != methodDefinition.Name)
dllImport.AddNamedFieldArgument("EntryPoint", CreateSimpleConstantValue(KnownTypeReference.String, currentModule.GetString(info.Name)));
if ((info.Attributes & MethodImportAttributes.ExactSpelling) == MethodImportAttributes.ExactSpelling)
dllImport.AddNamedFieldArgument("ExactSpelling", trueValue);
if ((implAttributes & MethodImplAttributes.PreserveSig) == MethodImplAttributes.PreserveSig)
implAttributes &= ~MethodImplAttributes.PreserveSig;
else
dllImport.AddNamedFieldArgument("PreserveSig", falseValue);
if ((info.Attributes & MethodImportAttributes.SetLastError) == MethodImportAttributes.SetLastError)
dllImport.AddNamedFieldArgument("SetLastError", trueValue);
if ((info.Attributes & MethodImportAttributes.ThrowOnUnmappableCharDisable) == MethodImportAttributes.ThrowOnUnmappableCharDisable)
dllImport.AddNamedFieldArgument("ThrowOnUnmappableChar", falseValue);
if ((info.Attributes & MethodImportAttributes.ThrowOnUnmappableCharEnable) == MethodImportAttributes.ThrowOnUnmappableCharEnable)
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
AddCustomAttributes(methodDefinition.GetCustomAttributes(), attributes);
AddSecurityAttributes(methodDefinition.GetDeclarativeSecurityAttributes(), attributes);
if (methodDefinition.GetParameters().Count > 0) {
var retParam = currentModule.GetParameter(methodDefinition.GetParameters().First());
var marshallingDesc = retParam.GetMarshallingDescriptor();
if (!marshallingDesc.IsNil) {
returnTypeAttributes.Add(ConvertMarshalInfo(currentModule.GetBlobReader(marshallingDesc)));
}
AddCustomAttributes(retParam.GetCustomAttributes(), 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.Attributes & TypeAttributes.Serializable) != 0)
targetEntity.Attributes.Add(serializableAttribute);
// ComImportAttribute
if ((typeDefinition.Attributes & TypeAttributes.Import) != 0)
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;
}
var layout = typeDefinition.GetLayout();
LayoutKind defaultLayoutKind = (typeDefinition.IsValueType(currentModule) && !typeDefinition.IsEnum(currentModule)) ? LayoutKind.Sequential : LayoutKind.Auto;
if (layoutKind != defaultLayoutKind || charSet != CharSet.Ansi || layout.PackingSize > 0 || layout.Size > 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 (layout.PackingSize > 0) {
structLayout.AddNamedFieldArgument("Pack", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)layout.PackingSize));
}
if (layout.Size > 0) {
structLayout.AddNamedFieldArgument("Size", CreateSimpleConstantValue(KnownTypeReference.Int32, (int)layout.Size));
}
targetEntity.Attributes.Add(interningProvider.Intern(structLayout));
}
#endregion
AddCustomAttributes(typeDefinition.GetCustomAttributes(), targetEntity.Attributes);
AddSecurityAttributes(typeDefinition.GetDeclarativeSecurityAttributes(), 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
int offset = fieldDefinition.GetOffset();
if (offset != -1) {
DefaultUnresolvedAttribute fieldOffset = new DefaultUnresolvedAttribute(fieldOffsetAttributeTypeRef, new[] { KnownTypeReference.Int32 });
fieldOffset.PositionalArguments.Add(CreateSimpleConstantValue(KnownTypeReference.Int32, offset));
targetEntity.Attributes.Add(interningProvider.Intern(fieldOffset));
}
// NonSerializedAttribute
if ((fieldDefinition.Attributes & FieldAttributes.NotSerialized) != 0) {
targetEntity.Attributes.Add(nonSerializedAttribute);
}
AddMarshalInfo(fieldDefinition.GetMarshallingDescriptor(), targetEntity.Attributes);
AddCustomAttributes(fieldDefinition.GetCustomAttributes(), targetEntity.Attributes);
}
#endregion
#region Event Attributes
void AddAttributes(EventDefinition eventDefinition, IUnresolvedEntity targetEntity)
{
AddCustomAttributes(eventDefinition.GetCustomAttributes(), targetEntity.Attributes);
}
#endregion
#region Property Attributes
void AddAttributes(PropertyDefinition propertyDefinition, IUnresolvedEntity targetEntity)
{
AddCustomAttributes(propertyDefinition.GetCustomAttributes(), targetEntity.Attributes);
}
#endregion
#region Type Parameter Attributes
void AddAttributes(GenericParameter genericParameter, IUnresolvedTypeParameter targetTP)
{
AddCustomAttributes(genericParameter.GetCustomAttributes(), targetTP.Attributes);
}
#endregion
#region MarshalAsAttribute (ConvertMarshalInfo)
static readonly ITypeReference marshalAsAttributeTypeRef = typeof(MarshalAsAttribute).ToTypeReference();
static readonly ITypeReference unmanagedTypeTypeRef = typeof(UnmanagedType).ToTypeReference();
void AddMarshalInfo(BlobHandle marshalInfo, IList<IUnresolvedAttribute> target)
{
if (marshalInfo.IsNil) return;
}
IUnresolvedAttribute ConvertMarshalInfo(System.Reflection.Metadata.BlobReader marshalInfo)
{
int type = marshalInfo.ReadByte();
DefaultUnresolvedAttribute attr = new DefaultUnresolvedAttribute(marshalAsAttributeTypeRef, new[] { unmanagedTypeTypeRef });
attr.PositionalArguments.Add(CreateSimpleConstantValue(unmanagedTypeTypeRef, type));
int size;
switch (type) {
case 0x1e: // FixedArray
if (!marshalInfo.TryReadCompressedInteger(out size))
size = 0;
type = marshalInfo.ReadByte();
if (type != 0x66) // None
attr.AddNamedFieldArgument("ArraySubType", CreateSimpleConstantValue(unmanagedTypeTypeRef, type));
break;
case 0x1d: // SafeArray
VarEnum varType = (VarEnum)marshalInfo.ReadByte();
if (varType != VarEnum.VT_EMPTY)
attr.AddNamedFieldArgument("SafeArraySubType", CreateSimpleConstantValue(typeof(VarEnum).ToTypeReference(), (int)varType));
break;
case 0x2a: // NATIVE_TYPE_ARRAY
if (marshalInfo.RemainingBytes > 0) {
type = marshalInfo.ReadByte();
} else {
type = 0x66; // Cecil uses NativeType.None as default.
}
if (type != 0x50) // Max
attr.AddNamedFieldArgument("ArraySubType", CreateSimpleConstantValue(unmanagedTypeTypeRef, type));
int sizeParameterIndex = marshalInfo.TryReadCompressedInteger(out int value) ? value : -1;
size = marshalInfo.TryReadCompressedInteger(out value) ? value : -1;
int sizeParameterMultiplier = marshalInfo.TryReadCompressedInteger(out value) ? value : -1;
if (size >= 0)
attr.AddNamedFieldArgument("SizeConst", CreateSimpleConstantValue(KnownTypeReference.Int32, size));
if (sizeParameterMultiplier != 0 && sizeParameterIndex >= 0)
attr.AddNamedFieldArgument("SizeParamIndex", CreateSimpleConstantValue(KnownTypeReference.Int16, (short)sizeParameterIndex));
break;
case 0x2c: // CustomMarshaler
string guidValue = marshalInfo.ReadSerializedString();
string unmanagedType = marshalInfo.ReadSerializedString();
string managedType = marshalInfo.ReadSerializedString();
string cookie = marshalInfo.ReadSerializedString();
if (managedType != null)
attr.AddNamedFieldArgument("MarshalType", CreateSimpleConstantValue(KnownTypeReference.String, managedType));
if (!string.IsNullOrEmpty(cookie))
attr.AddNamedFieldArgument("MarshalCookie", CreateSimpleConstantValue(KnownTypeReference.String, cookie));
break;
case 0x17: // FixedSysString
attr.AddNamedFieldArgument("SizeConst", CreateSimpleConstantValue(KnownTypeReference.Int32, marshalInfo.ReadCompressedInteger()));
break;
}
return InterningProvider.Intern(attr);
}
#endregion
#region Custom Attributes (ReadAttribute)
void AddCustomAttributes(CustomAttributeHandleCollection attributes, IList<IUnresolvedAttribute> targetCollection)
{
foreach (var handle in attributes) {
var attribute = currentModule.GetCustomAttribute(handle);
var typeHandle = attribute.GetAttributeType(currentModule);
switch (typeHandle.GetFullTypeName(currentModule).ReflectionName) {
case "System.Runtime.CompilerServices.DynamicAttribute":
case "System.Runtime.CompilerServices.ExtensionAttribute":
case "System.Runtime.CompilerServices.DecimalConstantAttribute":
case "System.ParamArrayAttribute":
continue;
}
targetCollection.Add(ReadAttribute(attribute));
}
}
public IUnresolvedAttribute ReadAttribute(CustomAttribute attribute)
{
ITypeReference attributeType = ReadTypeReference(attribute.GetAttributeType(currentModule));
return interningProvider.Intern(new MetadataUnresolvedAttributeBlob(currentModule, attributeType, attribute));
}
#endregion
#region Security Attributes
/// <summary>
/// Reads a security declaration.
/// </summary>
public IList<IUnresolvedAttribute> ReadSecurityDeclaration(DeclarativeSecurityAttributeHandleCollection secDecl)
{
var result = new List<IUnresolvedAttribute>();
AddSecurityAttributes(secDecl, result);
return result;
}
void AddSecurityAttributes(DeclarativeSecurityAttributeHandleCollection securityDeclarations, IList<IUnresolvedAttribute> targetCollection)
{
foreach (var secDecl in securityDeclarations) {
if (secDecl.IsNil) continue;
AddSecurityAttributes(currentModule.GetDeclarativeSecurityAttribute(secDecl), targetCollection);
}
}
void AddSecurityAttributes(DeclarativeSecurityAttribute secDecl, IList<IUnresolvedAttribute> targetCollection)
{
var blob = currentModule.GetBlobBytes(secDecl.PermissionSet);
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(currentModule.GetString(typeDefinition.Name));
var td = new DefaultUnresolvedTypeDefinition(currentModule.GetString(typeDefinition.Namespace), name);
InitTypeParameters(currentModule, typeDefinition, td.TypeParameters);
return td;
}
static void InitTypeParameters(MetadataReader currentModule, 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.
var genericParams = typeDefinition.GetGenericParameters();
for (int i = 0; i < genericParams.Count; i++) {
var gp = currentModule.GetGenericParameter(genericParams[i]);
if (gp.Index != i)
throw new InvalidOperationException("g.Position != i");
typeParameters.Add(new DefaultUnresolvedTypeParameter(
SymbolKind.TypeDefinition, i, currentModule.GetString(gp.Name)));
}
}
void InitTypeParameterConstraints(TypeDefinition typeDefinition, IList<IUnresolvedTypeParameter> typeParameters)
{
var genericParams = typeDefinition.GetGenericParameters().ToArray();
for (int i = 0; i < typeParameters.Count; i++) {
var tp = (DefaultUnresolvedTypeParameter)typeParameters[i];
var gp = currentModule.GetGenericParameter(genericParams[i]);
AddConstraints(tp, gp);
AddAttributes(gp, tp);
tp.ApplyInterningProvider(interningProvider);
}
}
void InitTypeDefinition(TypeDefinitionHandle handle, DefaultUnresolvedTypeDefinition td)
{
var typeDefinition = currentModule.GetTypeDefinition(handle);
td.Kind = GetTypeKind(currentModule, typeDefinition);
InitTypeModifiers(typeDefinition, td);
InitTypeParameterConstraints(typeDefinition, td.TypeParameters);
// nested types can be initialized only after generic parameters were created
InitNestedTypes(handle, td, td.NestedTypes);
AddAttributes(typeDefinition, td);
td.HasExtensionMethods = HasExtensionAttribute(currentModule, typeDefinition.GetCustomAttributes());
InitBaseTypes(handle, td.BaseTypes);
td.AddDefaultConstructorIfRequired = (td.Kind == TypeKind.Struct || td.Kind == TypeKind.Enum);
InitMembers(typeDefinition, td, td.Members);
td.ApplyInterningProvider(interningProvider);
td.Freeze();
RegisterCecilObject(td, handle);
}
void InitBaseTypes(TypeDefinitionHandle handle, IList<ITypeReference> baseTypes)
{
var typeDefinition = currentModule.GetTypeDefinition(handle);
// set base classes
if (typeDefinition.IsEnum(currentModule)) {
foreach (FieldDefinitionHandle h in typeDefinition.GetFields()) {
var enumField = currentModule.GetFieldDefinition(h);
if ((enumField.Attributes & FieldAttributes.Static) == 0) {
baseTypes.Add(enumField.DecodeSignature(new TypeReferenceSignatureDecoder(), default(Unit)));
break;
}
}
} else {
if (typeDefinition.BaseType != null) {
baseTypes.Add(ReadTypeReference(typeDefinition.BaseType));
}
foreach (var h in typeDefinition.GetInterfaceImplementations()) {
var iface = currentModule.GetInterfaceImplementation(h);
baseTypes.Add(ReadTypeReference(iface.Interface, iface.GetCustomAttributes()));
}
}
}
void InitNestedTypes(TypeDefinitionHandle typeDefinitionHandle, IUnresolvedTypeDefinition declaringTypeDefinition, IList<IUnresolvedTypeDefinition> nestedTypes)
{
var typeDefinition = currentModule.GetTypeDefinition(typeDefinitionHandle);
foreach (TypeDefinitionHandle h in typeDefinition.GetNestedTypes()) {
var nestedTypeDef = currentModule.GetTypeDefinition(h);
TypeAttributes visibility = nestedTypeDef.Attributes & TypeAttributes.VisibilityMask;
if (this.IncludeInternalMembers
|| visibility == TypeAttributes.NestedPublic
|| visibility == TypeAttributes.NestedFamily
|| visibility == TypeAttributes.NestedFamORAssem)
{
string name = currentModule.GetString(nestedTypeDef.Name);
int pos = name.LastIndexOf('/');
if (pos > 0)
name = name.Substring(pos + 1);
name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name);
var nestedType = new DefaultUnresolvedTypeDefinition(declaringTypeDefinition, name);
InitTypeParameters(currentModule, nestedTypeDef, nestedType.TypeParameters);
nestedTypes.Add(nestedType);
InitTypeDefinition(h, nestedType);
}
}
}
static TypeKind GetTypeKind(MetadataReader module, TypeDefinition typeDefinition)
{
// set classtype
if ((typeDefinition.Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface) {
return TypeKind.Interface;
} else if (typeDefinition.IsEnum(module)) {
return TypeKind.Enum;
} else if (typeDefinition.IsValueType(module)) {
return TypeKind.Struct;
} else if (IsDelegate(module, typeDefinition)) {
return TypeKind.Delegate;
} else if (IsModule(module, typeDefinition)) {
return TypeKind.Module;
} else {
return TypeKind.Class;
}
}
static void InitTypeModifiers(TypeDefinition typeDefinition, AbstractUnresolvedEntity td)
{
td.IsSealed = (typeDefinition.Attributes & TypeAttributes.Sealed) == TypeAttributes.Sealed;
td.IsAbstract = (typeDefinition.Attributes & TypeAttributes.Abstract) == TypeAttributes.Abstract;
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(MetadataReader currentModule, TypeDefinition type)
{
if (type.BaseType != null) {
var baseTypeName = type.BaseType.GetFullTypeName(currentModule).ToString();
if (baseTypeName == "System.MulticastDelegate")
return true;
var thisTypeName = type.GetFullTypeName(currentModule).ToString();
if (baseTypeName == "Delegate" && thisTypeName != "System.MulticastDelegate")
return true;
}
return false;
}
static bool IsModule(MetadataReader reader, TypeDefinition type)
{
foreach (var h in type.GetCustomAttributes()) {
var attType = reader.GetCustomAttribute(h).GetAttributeType(reader).GetFullTypeName(reader);
if (attType.ToString() == "Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute"
|| attType.ToString() == "System.Runtime.CompilerServices.CompilerGlobalScopeAttribute")
{
return true;
}
}
return false;
}
void InitMembers(TypeDefinition typeDefinition, IUnresolvedTypeDefinition td, IList<IUnresolvedMember> members)
{
foreach (MethodDefinitionHandle h in typeDefinition.GetMethods()) {
var method = currentModule.GetMethodDefinition(h);
/*if (IsVisible(method.Attributes) && !IsAccessor(h.GetMethodSemanticsAttributes(currentModule))) {
SymbolKind type = SymbolKind.Method;
if ((method.Attributes & MethodAttributes.SpecialName) != 0) {
if (method.IsConstructor(currentModule))
type = SymbolKind.Constructor;
else if (currentModule.GetString(method.Name).StartsWith("op_", StringComparison.Ordinal))
type = SymbolKind.Operator;
}
members.Add(ReadMethod(h, td, type));
}*/
}
foreach (FieldDefinitionHandle h in typeDefinition.GetFields()) {
var field = currentModule.GetFieldDefinition(h);
if (IsVisible(field.Attributes) && (field.Attributes & FieldAttributes.SpecialName) != 0) {
members.Add(ReadField(h, td));
}
}
string defaultMemberName = GetDefaultMemberName(currentModule, typeDefinition);
foreach (PropertyDefinitionHandle handle in typeDefinition.GetProperties()) {
var property = currentModule.GetPropertyDefinition(handle);
bool getterVisible = !property.GetAccessors().Getter.IsNil && IsVisible(currentModule.GetMethodDefinition(property.GetAccessors().Getter).Attributes);
bool setterVisible = !property.GetAccessors().Setter.IsNil && IsVisible(currentModule.GetMethodDefinition(property.GetAccessors().Setter).Attributes);
if (getterVisible || setterVisible) {
var accessor = property.GetAccessors().Getter.IsNil ? property.GetAccessors().Setter : property.GetAccessors().Getter;
SymbolKind type = SymbolKind.Property;
if (handle.HasParameters(currentModule)) {
// Try to detect indexer:
if (currentModule.GetString(property.Name) == defaultMemberName) {
type = SymbolKind.Indexer; // normal indexer
} else if (currentModule.GetString(property.Name).EndsWith(".Item", StringComparison.Ordinal) && accessor.HasOverrides(currentModule)) {
// 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(handle, td, type));
}
}
foreach (EventDefinitionHandle h in typeDefinition.GetEvents()) {
var ev = currentModule.GetEventDefinition(h);
if (ev.GetAccessors().Adder.IsNil) continue;
var addMethod = currentModule.GetMethodDefinition(ev.GetAccessors().Adder);
if (IsVisible(addMethod.Attributes)) {
members.Add(ReadEvent(h, td));
}
}
}
static string GetDefaultMemberName(MetadataReader reader, TypeDefinition typeDefinition)
{
foreach (var h in typeDefinition.GetCustomAttributes()) {
var a = reader.GetCustomAttribute(h);
var type = a.GetAttributeType(reader).GetFullTypeName(reader);
if (type.ToString() != typeof(System.Reflection.DefaultMemberAttribute).FullName)
continue;
var value = a.DecodeValue(new TypeSystemAttributeTypeProvider(minimalCorlibContext));
if (value.FixedArguments.Length == 1 && value.FixedArguments[0].Value is string name)
return name;
}
return null;
}
static bool IsAccessor(MethodSemanticsAttributes semantics)
{
return semantics != MethodSemanticsAttributes.Other;
}
#endregion
#region Lazy-Loaded Type Definition
sealed class LazySRMTypeDefinition : AbstractUnresolvedEntity, IUnresolvedTypeDefinition
{
// loader + cecilTypeDef, used for lazy-loading; and set to null after lazy loading is complete
readonly MetadataLoader loader;
readonly MetadataReader module;
readonly TypeDefinitionHandle cecilTypeDef;
readonly string namespaceName;
readonly TypeKind kind;
readonly IList<IUnresolvedTypeParameter> typeParameters;
// lazy-loaded fields
IList<ITypeReference> baseTypes;
IList<IUnresolvedTypeDefinition> nestedTypes;
IList<IUnresolvedMember> members;
public LazySRMTypeDefinition(MetadataLoader loader, MetadataReader module, TypeDefinitionHandle typeDefinition)
{
this.loader = loader;
this.cecilTypeDef = typeDefinition;
this.SymbolKind = SymbolKind.TypeDefinition;
var td = module.GetTypeDefinition(typeDefinition);
this.namespaceName = module.GetString(td.Namespace);
this.Name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(module.GetString(td.Name));
var tps = new List<IUnresolvedTypeParameter>();
InitTypeParameters(module, td, tps);
this.typeParameters = FreezableHelper.FreezeList(tps);
this.kind = GetTypeKind(module, td);
InitTypeModifiers(td, this);
loader.InitTypeParameterConstraints(td, typeParameters);
loader.AddAttributes(td, this);
flags[FlagHasExtensionMethods] = HasExtensionAttribute(module, td.GetCustomAttributes());
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 {
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()
{
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()
{
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()
{
lock (loader.currentModule) {
if (this.members != null)
return this.members;
var result = new List<IUnresolvedMember>();
var td = module.GetTypeDefinition(cecilTypeDef);
loader.InitMembers(td, this, result);
return FreezableHelper.FreezeList(result);
}
}
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.GetTypeDefinition(this.FullTypeName)
?? (IType)new UnknownType(this.Namespace, this.Name, this.TypeParameters.Count);
}
public ITypeResolveContext CreateResolveContext(ITypeResolveContext parentContext)
{
return parentContext;
}
}
#endregion
#region Read Method
public IUnresolvedMethod ReadMethod(MethodDefinitionHandle method, IUnresolvedTypeDefinition parentType, SymbolKind methodType = SymbolKind.Method)
{
return ReadMethod(method, parentType, methodType, null);
}
IUnresolvedMethod ReadMethod(MethodDefinitionHandle handle, IUnresolvedTypeDefinition parentType, SymbolKind methodType, IUnresolvedMember accessorOwner)
{
if (handle.IsNil)
return null;
var method = currentModule.GetMethodDefinition(handle);
DefaultUnresolvedMethod m = new DefaultUnresolvedMethod(parentType, currentModule.GetString(method.Name));
m.SymbolKind = methodType;
m.AccessorOwner = accessorOwner;
m.HasBody = (method.Attributes & MethodAttributes.Abstract) == 0 &&
(method.Attributes & MethodAttributes.PinvokeImpl) == 0 &&
(method.ImplAttributes & MethodImplAttributes.InternalCall) == 0 &&
(method.ImplAttributes & MethodImplAttributes.Native) == 0 &&
(method.ImplAttributes & MethodImplAttributes.Unmanaged) == 0 &&
(method.ImplAttributes & MethodImplAttributes.Runtime) == 0;
var genParams = method.GetGenericParameters();
if (genParams.Count > 0) {
for (int i = 0; i < genParams.Count; i++) {
var gp = currentModule.GetGenericParameter(genParams[i]);
if (gp.Index != i)
throw new InvalidOperationException("gp.Index != i");
m.TypeParameters.Add(new DefaultUnresolvedTypeParameter(
SymbolKind.Method, i, currentModule.GetString(gp.Name)));
}
for (int i = 0; i < genParams.Count; i++) {
var gp = currentModule.GetGenericParameter(genParams[i]);
var tp = (DefaultUnresolvedTypeParameter)m.TypeParameters[i];
AddConstraints(tp, gp);
AddAttributes(gp, tp);
tp.ApplyInterningProvider(interningProvider);
}
}
var declaringType = currentModule.GetTypeDefinition(method.GetDeclaringType());
var reader = currentModule.GetBlobReader(method.Signature);
var signature = method.DecodeSignature(new TypeReferenceSignatureDecoder(), default(Unit));
m.ReturnType = signature.ReturnType;
if (HasAnyAttributes(method))
AddAttributes(method, m.Attributes, m.ReturnTypeAttributes);
TranslateModifiers(handle, m);
int j = 0;
foreach (var par in method.GetParameters()) {
m.Parameters.Add(ReadParameter(par, signature.ParameterTypes[j]));
j++;
}
if (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) {
m.Parameters.Add(new DefaultUnresolvedParameter(SpecialType.ArgList, string.Empty));
}
// mark as extension method if the attribute is set
if ((method.Attributes & MethodAttributes.Static) == MethodAttributes.Static && HasExtensionAttribute(currentModule, method.GetCustomAttributes())) {
m.IsExtensionMethod = true;
}
/* TODO overrides
int lastDot = m.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, handle);
return m;
}
static bool HasExtensionAttribute(MetadataReader currentModule, CustomAttributeHandleCollection attributes)
{
foreach (var h in attributes) {
var attr = currentModule.GetCustomAttribute(h);
var type = attr.GetAttributeType(currentModule);
if (type.GetFullTypeName(currentModule).ToString() == "System.Runtime.CompilerServices.ExtensionAttribute")
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(MethodDefinitionHandle handle, AbstractUnresolvedMember m)
{
var method = currentModule.GetMethodDefinition(handle);
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.Attributes & MethodAttributes.Abstract) == MethodAttributes.Abstract) {
m.IsAbstract = true;
m.IsOverride = (method.Attributes & MethodAttributes.NewSlot) != MethodAttributes.NewSlot;
} else if ((method.Attributes & MethodAttributes.Final) == MethodAttributes.Final) {
if ((method.Attributes & MethodAttributes.NewSlot) != MethodAttributes.NewSlot) {
m.IsSealed = true;
m.IsOverride = true;
}
} else if ((method.Attributes & MethodAttributes.Virtual) == MethodAttributes.Virtual) {
if ((method.Attributes & MethodAttributes.NewSlot) == MethodAttributes.NewSlot)
m.IsVirtual = true;
else
m.IsOverride = true;
}
m.IsStatic = (method.Attributes & MethodAttributes.Static) == MethodAttributes.Static;
}
}
#endregion
#region Read Parameter
public IUnresolvedParameter ReadParameter(ParameterHandle handle, ITypeReference type)
{
if (handle.IsNil)
throw new ArgumentNullException(nameof(handle));
var parameter = currentModule.GetParameter(handle);
var p = new DefaultUnresolvedParameter(type, interningProvider.Intern(currentModule.GetString(parameter.Name)));
if (type is ByReferenceTypeReference) {
if ((parameter.Attributes & ParameterAttributes.In) == 0 && (parameter.Attributes & ParameterAttributes.Out) != 0)
p.IsOut = true;
else
p.IsRef = true;
}
AddAttributes(parameter, p);
if ((parameter.Attributes & ParameterAttributes.Optional) != 0) {
var constantHandle = parameter.GetDefaultValue();
if (!constantHandle.IsNil) {
var constant = currentModule.GetConstant(constantHandle);
var blobReader = currentModule.GetBlobReader(constant.Value);
p.DefaultValue = CreateSimpleConstantValue(type, blobReader.ReadConstant(constant.TypeCode));
}
}
if (type is ArrayTypeReference) {
foreach (CustomAttributeHandle h in parameter.GetCustomAttributes()) {
var att = currentModule.GetCustomAttribute(h);
if (att.GetAttributeType(currentModule).GetFullTypeName(currentModule).ToString() == 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(FieldDefinitionHandle handle, IUnresolvedTypeDefinition parentType)
{
if (handle.IsNil)
throw new ArgumentNullException(nameof(handle));
if (parentType == null)
throw new ArgumentNullException(nameof(parentType));
var field = currentModule.GetFieldDefinition(handle);
DefaultUnresolvedField f = new DefaultUnresolvedField(parentType, currentModule.GetString(field.Name));
f.Accessibility = GetAccessibility(field.Attributes);
f.IsReadOnly = (field.Attributes & FieldAttributes.InitOnly) == FieldAttributes.InitOnly;
f.IsStatic = (field.Attributes & FieldAttributes.Static) == FieldAttributes.Static;
f.ReturnType = ResolveDynamicTypes(field.DecodeSignature(new TypeReferenceSignatureDecoder(), default(Unit)), field.GetCustomAttributes());
var constantHandle = field.GetDefaultValue();
if (!constantHandle.IsNil) {
var constant = currentModule.GetConstant(constantHandle);
var blobReader = currentModule.GetBlobReader(constant.Value);
f.ConstantValue = CreateSimpleConstantValue(f.ReturnType, blobReader.ReadConstant(constant.TypeCode));
} else {
// TODO decimal constants
/*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);
if (f.ReturnType is ModifiedTypeReference mod
&& mod.IsRequired
&& mod.ModifierType is GetClassTypeReference modifier
&& modifier.FullTypeName.ToString() == typeof(IsVolatile).FullName) {
f.IsVolatile = true;
}
FinishReadMember(f, handle);
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 Resolve dynamic type
ITypeReference ResolveDynamicTypes(ITypeReference type, CustomAttributeHandleCollection attributes)
{
int typeIndex = 0;
return ResolveDynamicTypes(type, attributes, ref typeIndex);
}
ITypeReference ResolveDynamicTypes(ITypeReference type, CustomAttributeHandleCollection attributes, ref int typeIndex)
{
// TODO : interning??
ITypeReference replacement;
switch (type) {
case ByReferenceTypeReference brtr:
typeIndex++;
replacement = ResolveDynamicTypes(brtr.ElementType, attributes, ref typeIndex);
if (replacement == brtr.ElementType)
return brtr;
else
return new ByReferenceTypeReference(replacement);
case PointerTypeReference ptr:
typeIndex++;
replacement = ResolveDynamicTypes(ptr.ElementType, attributes, ref typeIndex);
if (replacement == ptr.ElementType)
return ptr;
else
return new PointerTypeReference(replacement);
case TypeParameterReference tpr:
return tpr;
case ParameterizedTypeReference genericType:
ITypeReference baseType = ResolveDynamicTypes(genericType.GenericType, attributes, ref typeIndex);
ITypeReference[] para = new ITypeReference[genericType.TypeArguments.Count];
for (int i = 0; i < para.Length; ++i) {
typeIndex++;
para[i] = ResolveDynamicTypes(genericType.TypeArguments[i], attributes, ref typeIndex);
}
return new ParameterizedTypeReference(baseType, para);
case NestedTypeReference ntr:
replacement = ResolveDynamicTypes(ntr.DeclaringTypeReference, attributes, ref typeIndex);
if (replacement == ntr.DeclaringTypeReference)
return ntr;
else
return new NestedTypeReference(replacement, ntr.Name, ntr.AdditionalTypeParameterCount);
case GetClassTypeReference gctr:
return gctr;
case KnownTypeReference ktr:
if (ktr.KnownTypeCode == KnownTypeCode.Object && HasDynamicAttribute(attributes, typeIndex))
return SpecialType.Dynamic.ToTypeReference();
else
return ktr;
default:
return type;
}
}
static readonly ITypeResolveContext minimalCorlibContext = new SimpleTypeResolveContext(MinimalCorlib.Instance.CreateCompilation());
bool HasDynamicAttribute(CustomAttributeHandleCollection attributes, int typeIndex)
{
foreach (CustomAttributeHandle handle in attributes) {
var a = currentModule.GetCustomAttribute(handle);
var type = a.GetAttributeType(currentModule);
if (type.GetFullTypeName(currentModule).ToString() == "System.Runtime.CompilerServices.DynamicAttribute") {
var ctor = a.DecodeValue(new TypeSystemAttributeTypeProvider(minimalCorlibContext));
if (ctor.FixedArguments.Length == 1) {
if (ctor.FixedArguments[0].Value is bool[] values && typeIndex < values.Length)
return values[typeIndex];
}
return true;
}
}
return false;
}
#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.Attributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0;
tp.HasValueTypeConstraint = (g.Attributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0;
tp.HasDefaultConstructorConstraint = (g.Attributes & GenericParameterAttributes.DefaultConstructorConstraint) != 0;
foreach (GenericParameterConstraintHandle h in g.GetConstraints()) {
var constraint = currentModule.GetGenericParameterConstraint(h);
tp.Constraints.Add(ReadTypeReference(constraint.Type));
}
}
#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(PropertyDefinitionHandle handle, IUnresolvedTypeDefinition parentType, SymbolKind propertyType = SymbolKind.Property)
{
if (handle.IsNil)
throw new ArgumentNullException(nameof(handle));
if (parentType == null)
throw new ArgumentNullException(nameof(parentType));
var property = currentModule.GetPropertyDefinition(handle);
var propertyName = currentModule.GetString(property.Name);
DefaultUnresolvedProperty p = new DefaultUnresolvedProperty(parentType, propertyName);
p.SymbolKind = propertyType;
var accessors = property.GetAccessors();
TranslateModifiers(accessors.Getter.IsNil ? accessors.Setter : accessors.Getter, p);
if (!accessors.Getter.IsNil && !accessors.Setter.IsNil)
p.Accessibility = MergePropertyAccessibility(GetAccessibility(currentModule.GetMethodDefinition(accessors.Getter).Attributes), GetAccessibility (currentModule.GetMethodDefinition(accessors.Setter).Attributes));
var signature = property.DecodeSignature(new TypeReferenceSignatureDecoder(), default(Unit));
p.ReturnType = signature.ReturnType;
p.Getter = ReadMethod(accessors.Getter, parentType, SymbolKind.Accessor, p);
p.Setter = ReadMethod(accessors.Setter, parentType, SymbolKind.Accessor, p);
ParameterHandleCollection parameterHandles = default(ParameterHandleCollection);
int parameterCount = 0;
if (!accessors.Getter.IsNil) {
var getter = currentModule.GetMethodDefinition(accessors.Getter);
parameterHandles = getter.GetParameters();
parameterCount = parameterHandles.Count;
} else {
if (!accessors.Setter.IsNil) {
var setter = currentModule.GetMethodDefinition(accessors.Setter);
parameterHandles = setter.GetParameters();
parameterCount = parameterHandles.Count - 1;
}
}
int i = 0;
foreach (var par in parameterHandles) {
if (i >= parameterCount) break;
p.Parameters.Add(ReadParameter(par, signature.ParameterTypes[i]));
i++;
}
AddAttributes(property, p);
var accessor = p.Getter ?? p.Setter;
if (accessor != null && accessor.IsExplicitInterfaceImplementation) {
if (ShortenInterfaceImplNames)
p.Name = propertyName.Substring(propertyName.LastIndexOf('.') + 1);
p.IsExplicitInterfaceImplementation = true;
foreach (var mr in accessor.ExplicitInterfaceImplementations) {
p.ExplicitInterfaceImplementations.Add(new AccessorOwnerMemberReference(mr));
}
}
FinishReadMember(p, handle);
return p;
}
#endregion
#region Read Event
public IUnresolvedEvent ReadEvent(EventDefinitionHandle ev, IUnresolvedTypeDefinition parentType)
{
if (ev.IsNil)
throw new ArgumentNullException(nameof(ev));
if (parentType == null)
throw new ArgumentNullException(nameof(parentType));
var ed = currentModule.GetEventDefinition(ev);
var eventName = currentModule.GetString(ed.Name);
DefaultUnresolvedEvent e = new DefaultUnresolvedEvent(parentType, eventName);
var accessors = ed.GetAccessors();
TranslateModifiers(accessors.Adder, e);
e.ReturnType = ReadTypeReference(ed.Type, typeAttributes: ed.GetCustomAttributes());
e.AddAccessor = ReadMethod(accessors.Adder, parentType, SymbolKind.Accessor, e);
e.RemoveAccessor = ReadMethod(accessors.Remover, parentType, SymbolKind.Accessor, e);
e.InvokeAccessor = ReadMethod(accessors.Raiser, parentType, SymbolKind.Accessor, e);
AddAttributes(ed, e);
var accessor = e.AddAccessor ?? e.RemoveAccessor ?? e.InvokeAccessor;
if (accessor != null && accessor.IsExplicitInterfaceImplementation) {
if (ShortenInterfaceImplNames)
e.Name = eventName.Substring(eventName.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, EntityHandle cecilDefinition)
{
member.ApplyInterningProvider(interningProvider);
member.Freeze();
RegisterCecilObject(member, cecilDefinition);
}
#endregion
#region Type system translation table
readonly Dictionary<object, EntityHandle> typeSystemTranslationTable;
void RegisterCecilObject(IUnresolvedEntity typeSystemObject, EntityHandle cecilObject)
{
OnEntityLoaded?.Invoke(typeSystemObject, cecilObject);
AddToTypeSystemTranslationTable(typeSystemObject, cecilObject);
}
void AddToTypeSystemTranslationTable(object typeSystemObject, EntityHandle cecilObject)
{
if (typeSystemTranslationTable != null) {
// When lazy-loading, the dictionary might be shared between multiple cecil-loaders that are used concurrently
lock (typeSystemTranslationTable) {
typeSystemTranslationTable[typeSystemObject] = cecilObject;
}
}
}
T InternalGetCecilObject<T> (object typeSystemObject) where T : class
{
if (typeSystemObject == null)
throw new ArgumentNullException ("typeSystemObject");
if (!HasCecilReferences)
throw new NotSupportedException ("This instance contains no cecil references.");
EntityHandle result;
lock (typeSystemTranslationTable) {
if (!typeSystemTranslationTable.TryGetValue (typeSystemObject, out result))
return null;
}
return result as T;
}
public AssemblyDefinition GetCecilObject (IUnresolvedAssembly content)
{
throw new NotImplementedException();
//return InternalGetCecilObject<AssemblyDefinition> (content);
}
public TypeDefinition GetCecilObject (IUnresolvedTypeDefinition type)
{
if (type == null)
throw new ArgumentNullException ("type");
throw new NotImplementedException();
//return InternalGetCecilObject<TypeDefinition> (type);
}
public MethodDefinition GetCecilObject (IUnresolvedMethod method)
{
throw new NotImplementedException();
//return InternalGetCecilObject<MethodDefinition> (method);
}
public FieldDefinition GetCecilObject (IUnresolvedField field)
{
throw new NotImplementedException();
//return InternalGetCecilObject<FieldDefinition> (field);
}
public EventDefinition GetCecilObject (IUnresolvedEvent evt)
{
throw new NotImplementedException();
//return InternalGetCecilObject<EventDefinition> (evt);
}
public PropertyDefinition GetCecilObject (IUnresolvedProperty property)
{
throw new NotImplementedException();
//return InternalGetCecilObject<PropertyDefinition> (property);
}
#endregion
}
}