// 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.PortableExecutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; 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> OnEntityLoaded { get; set; } 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 Metadata.PEFile currentModule; MetadataReader currentMetadata; DefaultUnresolvedAssembly currentAssembly; static readonly ITypeResolveContext minimalCorlibContext = new SimpleTypeResolveContext(MinimalCorlib.Instance.CreateCompilation()); /// <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.IncludeInternalMembers = loader.IncludeInternalMembers; this.LazyLoad = loader.LazyLoad; this.OnEntityLoaded = loader.OnEntityLoaded; this.ShortenInterfaceImplNames = loader.ShortenInterfaceImplNames; this.currentModule = loader.currentModule; this.currentMetadata = loader.currentMetadata; 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(Metadata.PEFile module) { this.currentModule = module; this.currentMetadata = module.Metadata; // Read assembly and module attributes IList<IUnresolvedAttribute> assemblyAttributes = new List<IUnresolvedAttribute>(); IList<IUnresolvedAttribute> moduleAttributes = new List<IUnresolvedAttribute>(); if (currentMetadata.IsAssembly) { AddAttributes(currentMetadata.GetAssemblyDefinition(), assemblyAttributes); } AddAttributes(Handle.ModuleDefinition, moduleAttributes); assemblyAttributes = interningProvider.InternList(assemblyAttributes); moduleAttributes = interningProvider.InternList(moduleAttributes); this.currentAssembly = new DefaultUnresolvedAssembly(currentMetadata.IsAssembly ? currentMetadata.GetFullAssemblyName() : currentMetadata.GetString(currentMetadata.GetModuleDefinition().Name)); currentAssembly.AssemblyAttributes.AddRange(assemblyAttributes); currentAssembly.ModuleAttributes.AddRange(assemblyAttributes); // Register type forwarders: foreach (ExportedTypeHandle t in currentMetadata.ExportedTypes) { var type = currentMetadata.GetExportedType(t); if (type.IsForwarder) { IAssemblyReference assemblyRef; switch (type.Implementation.Kind) { case HandleKind.AssemblyFile: assemblyRef = DefaultAssemblyReference.CurrentAssembly; break; case HandleKind.ExportedType: throw new NotImplementedException(); case HandleKind.AssemblyReference: var asmRef = currentMetadata.GetAssemblyReference((AssemblyReferenceHandle)type.Implementation); assemblyRef = new DefaultAssemblyReference(asmRef.GetFullAssemblyName(currentMetadata)); break; default: throw new NotSupportedException(); } int typeParameterCount; string ns = currentMetadata.GetString(type.Namespace); string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(currentMetadata.GetString(type.Name), out typeParameterCount); ns = interningProvider.Intern(ns); name = interningProvider.Intern(name); var typeRef = new GetClassTypeReference(assemblyRef, 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 currentMetadata.TypeDefinitions) { this.CancellationToken.ThrowIfCancellationRequested(); var td = currentMetadata.GetTypeDefinition(h); if (!td.GetDeclaringType().IsNil) continue; if (this.IncludeInternalMembers || (td.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.Public) { string name = currentMetadata.GetString(td.Name); if (name.Length == 0) continue; if (this.LazyLoad) { var t = new LazySRMTypeDefinition(cecilLoaderCloneForLazyLoading, module, h); currentAssembly.AddTypeDefinition(t); RegisterCecilObject(t); } else { var t = CreateTopLevelTypeDefinition(h, 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.currentMetadata = 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(Metadata.PEFile module) { this.currentModule = module; this.currentMetadata = module.Metadata; } /// <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(typeDefinition, currentMetadata.GetTypeDefinition(typeDefinition)); InitTypeDefinition(typeDefinition, td); return td; } #endregion #region Load Assembly From Disk public IUnresolvedAssembly LoadAssemblyFile(string fileName, bool throwOnResolveError = true, PEStreamOptions options = PEStreamOptions.Default) { if (fileName == null) throw new ArgumentNullException(nameof(fileName)); var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read); return LoadModule(new Metadata.PEFile(fileName, fileStream, throwOnResolveError, options)); } #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 = null) { ITypeReference CreateTypeReference(TypeReferenceHandle handle) { var t = currentMetadata.GetTypeReference(handle); var asmref = handle.GetDeclaringAssembly(currentMetadata); if (asmref.IsNil) return new GetClassTypeReference(handle.GetFullTypeName(currentMetadata), DefaultAssemblyReference.CurrentAssembly); var asm = currentMetadata.GetAssemblyReference(asmref); return new GetClassTypeReference(handle.GetFullTypeName(currentMetadata), new DefaultAssemblyReference(currentMetadata.GetString(asm.Name))); } switch (type.Kind) { case HandleKind.TypeSpecification: return DynamicTypeReference.Create(currentMetadata.GetTypeSpecification((TypeSpecificationHandle)type) .DecodeSignature(TypeReferenceSignatureDecoder.Instance, default), typeAttributes, currentMetadata); case HandleKind.TypeReference: return CreateTypeReference((TypeReferenceHandle)type); case HandleKind.TypeDefinition: return new TypeDefTokenTypeReference(type); 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(currentMetadata.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(currentMetadata.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 = currentMetadata.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 || methodDefinition.GetParameters().Any(p => currentMetadata.GetParameter(p).GetCustomAttributes().Any()); } 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, currentMetadata.GetString(currentMetadata.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(currentMetadata).ToString()}.{currentMetadata.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, currentMetadata.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 = currentMetadata.GetParameter(methodDefinition.GetParameters().First()); if (retParam.SequenceNumber == 0) { var marshallingDesc = retParam.GetMarshallingDescriptor(); if (!marshallingDesc.IsNil) { returnTypeAttributes.Add(ConvertMarshalInfo(currentMetadata.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(currentMetadata) && !typeDefinition.IsEnum(currentMetadata)) ? 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; target.Add(ConvertMarshalInfo(currentMetadata.GetBlobReader(marshalInfo))); } 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; attr.AddNamedFieldArgument("SizeConst", CreateSimpleConstantValue(KnownTypeReference.Int32, size)); if (marshalInfo.RemainingBytes > 0) { type = marshalInfo.ReadByte(); if (type != 0x66) // None attr.AddNamedFieldArgument("ArraySubType", CreateSimpleConstantValue(unmanagedTypeTypeRef, type)); } break; case 0x1d: // SafeArray if (marshalInfo.RemainingBytes > 0) { 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 = currentMetadata.GetCustomAttribute(handle); var typeHandle = attribute.GetAttributeType(currentMetadata); switch (typeHandle.GetFullTypeName(currentMetadata).ReflectionName) { case "System.Runtime.CompilerServices.DynamicAttribute": case "System.Runtime.CompilerServices.ExtensionAttribute": case "System.Runtime.CompilerServices.DecimalConstantAttribute": case "System.ParamArrayAttribute": continue; } targetCollection.Add(ReadAttribute(handle)); } } public IUnresolvedAttribute ReadAttribute(CustomAttributeHandle handle) { var attribute = currentMetadata.GetCustomAttribute(handle); ITypeReference attributeType = ReadTypeReference(attribute.GetAttributeType(currentMetadata)); MethodSignature<ITypeReference> signature; switch (attribute.Constructor.Kind) { case HandleKind.MethodDefinition: var md = currentMetadata.GetMethodDefinition((MethodDefinitionHandle)attribute.Constructor); signature = md.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default); break; case HandleKind.MemberReference: var mr = currentMetadata.GetMemberReference((MemberReferenceHandle)attribute.Constructor); Debug.Assert(mr.GetKind() == MemberReferenceKind.Method); signature = mr.DecodeMethodSignature(TypeReferenceSignatureDecoder.Instance, default); break; default: throw new NotSupportedException(); } return interningProvider.Intern(new UnresolvedAttributeBlob(attributeType, signature.ParameterTypes, currentMetadata.GetBlobBytes(attribute.Value))); } #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(currentMetadata.GetDeclarativeSecurityAttribute(secDecl), targetCollection); } } void AddSecurityAttributes(DeclarativeSecurityAttribute secDecl, IList<IUnresolvedAttribute> targetCollection) { var blob = currentMetadata.GetBlobBytes(secDecl.PermissionSet); var blobSecDecl = new UnresolvedSecurityDeclarationBlob((int)secDecl.Action, blob); targetCollection.AddRange(blobSecDecl.UnresolvedAttributes); } #endregion #endregion #region Read Type Definition DefaultUnresolvedTypeDefinition CreateTopLevelTypeDefinition(TypeDefinitionHandle handle, TypeDefinition typeDefinition) { string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(currentMetadata.GetString(typeDefinition.Name)); var td = new DefaultUnresolvedTypeDefinition(currentMetadata.GetString(typeDefinition.Namespace), name); td.MetadataToken = handle; InitTypeParameters(currentMetadata, 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 = currentMetadata.GetGenericParameter(genericParams[i]); AddConstraints(tp, gp); AddAttributes(gp, tp); tp.ApplyInterningProvider(interningProvider); } } void InitTypeDefinition(TypeDefinitionHandle handle, DefaultUnresolvedTypeDefinition td) { var typeDefinition = currentMetadata.GetTypeDefinition(handle); td.Kind = GetTypeKind(currentMetadata, 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(currentMetadata, 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); } void InitBaseTypes(TypeDefinitionHandle handle, IList<ITypeReference> baseTypes) { var typeDefinition = currentMetadata.GetTypeDefinition(handle); // set base classes if (typeDefinition.IsEnum(currentMetadata)) { foreach (FieldDefinitionHandle h in typeDefinition.GetFields()) { var enumField = currentMetadata.GetFieldDefinition(h); if (!enumField.HasFlag(FieldAttributes.Static)) { baseTypes.Add(enumField.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default)); break; } } } else { if (typeDefinition.BaseType != null) { baseTypes.Add(ReadTypeReference(typeDefinition.BaseType)); } foreach (var h in typeDefinition.GetInterfaceImplementations()) { var iface = currentMetadata.GetInterfaceImplementation(h); baseTypes.Add(ReadTypeReference(iface.Interface, iface.GetCustomAttributes())); } } } void InitNestedTypes(TypeDefinitionHandle typeDefinitionHandle, IUnresolvedTypeDefinition declaringTypeDefinition, IList<IUnresolvedTypeDefinition> nestedTypes) { var typeDefinition = currentMetadata.GetTypeDefinition(typeDefinitionHandle); foreach (TypeDefinitionHandle h in typeDefinition.GetNestedTypes()) { var nestedTypeDef = currentMetadata.GetTypeDefinition(h); TypeAttributes visibility = nestedTypeDef.Attributes & TypeAttributes.VisibilityMask; if (this.IncludeInternalMembers || visibility == TypeAttributes.NestedPublic || visibility == TypeAttributes.NestedFamily || visibility == TypeAttributes.NestedFamORAssem) { string name = currentMetadata.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); nestedType.MetadataToken = h; InitTypeParameters(currentMetadata, 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.IsNil) { 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 = currentMetadata.GetMethodDefinition(h); if (IsVisible(method.Attributes) && !IsAccessor(h.GetMethodSemanticsAttributes(currentMetadata))) { SymbolKind type = SymbolKind.Method; if ((method.Attributes & MethodAttributes.SpecialName) != 0) { if (method.IsConstructor(currentMetadata)) type = SymbolKind.Constructor; else if (currentMetadata.GetString(method.Name).StartsWith("op_", StringComparison.Ordinal)) type = SymbolKind.Operator; } members.Add(ReadMethod(h, td, type)); } } foreach (FieldDefinitionHandle h in typeDefinition.GetFields()) { var field = currentMetadata.GetFieldDefinition(h); if (IsVisible(field.Attributes) && (field.Attributes & FieldAttributes.SpecialName) == 0) { members.Add(ReadField(h, td)); } } string defaultMemberName = GetDefaultMemberName(currentMetadata, typeDefinition); foreach (PropertyDefinitionHandle handle in typeDefinition.GetProperties()) { var property = currentMetadata.GetPropertyDefinition(handle); bool getterVisible = !property.GetAccessors().Getter.IsNil && IsVisible(currentMetadata.GetMethodDefinition(property.GetAccessors().Getter).Attributes); bool setterVisible = !property.GetAccessors().Setter.IsNil && IsVisible(currentMetadata.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(currentMetadata)) { // Try to detect indexer: if (currentMetadata.GetString(property.Name) == defaultMemberName) { type = SymbolKind.Indexer; // normal indexer } else if (currentMetadata.GetString(property.Name).EndsWith(".Item", StringComparison.Ordinal) && accessor.HasOverrides(currentMetadata)) { // 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 = currentMetadata.GetEventDefinition(h); if (ev.GetAccessors().Adder.IsNil) continue; var addMethod = currentMetadata.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)0 && 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 Metadata.PEFile module; readonly MetadataReader metadata; 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, Metadata.PEFile module, TypeDefinitionHandle typeDefinition) { this.loader = loader; this.module = module; this.metadata = module.Metadata; this.MetadataToken = typeDefinition; this.SymbolKind = SymbolKind.TypeDefinition; var td = metadata.GetTypeDefinition(typeDefinition); this.namespaceName = metadata.GetString(td.Namespace); this.Name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(metadata.GetString(td.Name)); var tps = new List<IUnresolvedTypeParameter>(); InitTypeParameters(metadata, td, tps); this.typeParameters = FreezableHelper.FreezeList(tps); this.kind = GetTypeKind(metadata, td); InitTypeModifiers(td, this); loader.InitTypeParameterConstraints(td, typeParameters); loader.AddAttributes(td, this); flags[FlagHasExtensionMethods] = HasExtensionAttribute(metadata, 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.currentMetadata) { var result = new List<ITypeReference>(); loader.InitBaseTypes((TypeDefinitionHandle)MetadataToken, 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.currentMetadata) { var result = new List<IUnresolvedTypeDefinition>(); loader.InitNestedTypes((TypeDefinitionHandle)MetadataToken, 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.currentMetadata) { if (this.members != null) return this.members; var result = new List<IUnresolvedMember>(); var td = metadata.GetTypeDefinition((TypeDefinitionHandle)MetadataToken); 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 = currentMetadata.GetMethodDefinition(handle); DefaultUnresolvedMethod m = new DefaultUnresolvedMethod(parentType, currentMetadata.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 = currentMetadata.GetGenericParameter(genParams[i]); if (gp.Index != i) throw new InvalidOperationException("gp.Index != i"); m.TypeParameters.Add(new DefaultUnresolvedTypeParameter( SymbolKind.Method, i, currentMetadata.GetString(gp.Name))); } for (int i = 0; i < genParams.Count; i++) { var gp = currentMetadata.GetGenericParameter(genParams[i]); var tp = (DefaultUnresolvedTypeParameter)m.TypeParameters[i]; AddConstraints(tp, gp); AddAttributes(gp, tp); tp.ApplyInterningProvider(interningProvider); } } if (HasAnyAttributes(method)) AddAttributes(method, m.Attributes, m.ReturnTypeAttributes); TranslateModifiers(handle, m); var declaringType = currentMetadata.GetTypeDefinition(method.GetDeclaringType()); var reader = currentMetadata.GetBlobReader(method.Signature); var signature = method.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default); var parameters = method.GetParameters().ToArray(); m.ReturnType = HandleReturnType(parameters.FirstOrDefault(), signature.ReturnType); int j = 0; if (signature.RequiredParameterCount > parameters.Length) { foreach (var parameterType in signature.ParameterTypes) { m.Parameters.Add(new DefaultUnresolvedParameter(DynamicTypeReference.Create(parameterType, null, currentMetadata), string.Empty)); } } else { foreach (var p in parameters) { var par = currentMetadata.GetParameter(p); if (par.SequenceNumber > 0) { 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(currentMetadata, method.GetCustomAttributes())) { m.IsExtensionMethod = true; } int lastDot = m.Name.LastIndexOf('.'); var overrides = handle.GetMethodImplementations(currentMetadata); if (lastDot >= 0 && overrides.Any()) { // To be consistent with the parser-initialized type system, shorten the method name: if (ShortenInterfaceImplNames) m.Name = m.Name.Substring(lastDot + 1); m.IsExplicitInterfaceImplementation = true; foreach (var h in overrides) { var or = currentMetadata.GetMethodImplementation(h); EntityHandle orDeclaringType; string orName; int genericParameterCount; MethodSignature<ITypeReference> orSignature; switch (or.MethodDeclaration.Kind) { case HandleKind.MethodDefinition: var md = currentMetadata.GetMethodDefinition((MethodDefinitionHandle)or.MethodDeclaration); orDeclaringType = md.GetDeclaringType(); orName = currentMetadata.GetString(md.Name); orSignature = md.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default); genericParameterCount = orSignature.GenericParameterCount; break; case HandleKind.MemberReference: var mr = currentMetadata.GetMemberReference((MemberReferenceHandle)or.MethodDeclaration); Debug.Assert(mr.GetKind() == MemberReferenceKind.Method); orDeclaringType = mr.Parent; orName = currentMetadata.GetString(mr.Name); orSignature = mr.DecodeMethodSignature(TypeReferenceSignatureDecoder.Instance, default); genericParameterCount = orSignature.GenericParameterCount; break; default: throw new NotSupportedException(); } m.ExplicitInterfaceImplementations.Add(new DefaultMemberReference( accessorOwner != null ? SymbolKind.Accessor : SymbolKind.Method, ReadTypeReference(orDeclaringType), orName, genericParameterCount, m.Parameters.Select(p => p.Type).ToList())); } } FinishReadMember(m, handle); return m; } ITypeReference HandleReturnType(ParameterHandle parameterHandle, ITypeReference returnType) { CustomAttributeHandleCollection? attributes = null; if (!parameterHandle.IsNil) { var par = currentMetadata.GetParameter(parameterHandle); if (par.SequenceNumber == 0) { attributes = par.GetCustomAttributes(); } } return DynamicTypeReference.Create(returnType, attributes, currentMetadata); } 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 = currentMetadata.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(Parameter parameter, ITypeReference type) { var p = new DefaultUnresolvedParameter(DynamicTypeReference.Create(type, parameter.GetCustomAttributes(), currentMetadata), interningProvider.Intern(currentMetadata.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 = currentMetadata.GetConstant(constantHandle); var blobReader = currentMetadata.GetBlobReader(constant.Value); p.DefaultValue = CreateSimpleConstantValue(type, blobReader.ReadConstant(constant.TypeCode)); } else { p.DefaultValue = CreateSimpleConstantValue(type, null); } } if (type is ArrayTypeReference) { foreach (CustomAttributeHandle h in parameter.GetCustomAttributes()) { var att = currentMetadata.GetCustomAttribute(h); if (att.GetAttributeType(currentMetadata).GetFullTypeName(currentMetadata).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(CustomAttributeHandle handle) { var attribute = currentMetadata.GetCustomAttribute(handle); ITypeReference attributeType = ReadTypeReference(attribute.GetAttributeType(currentMetadata)); MethodSignature<ITypeReference> signature; switch (attribute.Constructor.Kind) { case HandleKind.MethodDefinition: var md = currentMetadata.GetMethodDefinition((MethodDefinitionHandle)attribute.Constructor); signature = md.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default); break; case HandleKind.MemberReference: var mr = currentMetadata.GetMemberReference((MemberReferenceHandle)attribute.Constructor); Debug.Assert(mr.GetKind() == MemberReferenceKind.Method); signature = mr.DecodeMethodSignature(TypeReferenceSignatureDecoder.Instance, default); break; default: throw new NotSupportedException(); } if (signature.RequiredParameterCount != 5) return null; var reader = new Implementation.BlobReader(currentMetadata.GetBlobBytes(attribute.Value), 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[signature.RequiredParameterCount]; for (int i = 0; i < ctorArgs.Length; i++) { switch (signature.ParameterTypes[i].Resolve(minimalCorlibContext).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 = currentMetadata.GetFieldDefinition(handle); DefaultUnresolvedField f = new DefaultUnresolvedField(parentType, currentMetadata.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 = DynamicTypeReference.Create(field.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default), field.GetCustomAttributes(), currentMetadata); var constantHandle = field.GetDefaultValue(); if (!constantHandle.IsNil) { var constant = currentMetadata.GetConstant(constantHandle); var blobReader = currentMetadata.GetBlobReader(constant.Value); f.ConstantValue = CreateSimpleConstantValue(f.ReturnType, blobReader.ReadConstant(constant.TypeCode)); } else { var decConstant = field.GetCustomAttributes().FirstOrDefault(a => currentMetadata.GetCustomAttribute(a).GetAttributeType(currentMetadata).GetFullTypeName(currentMetadata).ReflectionName == "System.Runtime.CompilerServices.DecimalConstantAttribute"); if (!decConstant.IsNil) { 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 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 = currentMetadata.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 = currentMetadata.GetPropertyDefinition(handle); var propertyName = currentMetadata.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(currentMetadata.GetMethodDefinition(accessors.Getter).Attributes), GetAccessibility(currentMetadata.GetMethodDefinition(accessors.Setter).Attributes)); var signature = property.DecodeSignature(TypeReferenceSignatureDecoder.Instance, default); p.Getter = ReadMethod(accessors.Getter, parentType, SymbolKind.Accessor, p); p.Setter = ReadMethod(accessors.Setter, parentType, SymbolKind.Accessor, p); var parameterHandles = Empty<ParameterHandle>.Array; if (!accessors.Getter.IsNil) { var getter = currentMetadata.GetMethodDefinition(accessors.Getter); parameterHandles = getter.GetParameters().ToArray(); } else { if (!accessors.Setter.IsNil) { var setter = currentMetadata.GetMethodDefinition(accessors.Setter); parameterHandles = setter.GetParameters().ToArray(); } } p.ReturnType = HandleReturnType(parameterHandles.FirstOrDefault(), signature.ReturnType); int i = 0; if (signature.RequiredParameterCount > parameterHandles.Length) { foreach (var parameterType in signature.ParameterTypes) { p.Parameters.Add(new DefaultUnresolvedParameter(DynamicTypeReference.Create(parameterType, null, currentMetadata), string.Empty)); } } else { foreach (var h in parameterHandles) { var par = currentMetadata.GetParameter(h); if (par.SequenceNumber > 0 && i < signature.ParameterTypes.Length) { 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 = currentMetadata.GetEventDefinition(ev); var eventName = currentMetadata.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.MetadataToken = cecilDefinition; member.ApplyInterningProvider(interningProvider); member.Freeze(); RegisterCecilObject(member); } #endregion #region Type system translation table void RegisterCecilObject(IUnresolvedEntity typeSystemObject) { OnEntityLoaded?.Invoke(typeSystemObject); } #endregion } }