// Copyright (c) 2018 Daniel Grunwald // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { /// /// Type system implementation for Metadata.PEFile. /// [DebuggerDisplay("")] public class MetadataModule : IModule { public ICompilation Compilation { get; } internal readonly MetadataReader metadata; readonly TypeSystemOptions options; internal readonly TypeProvider TypeProvider; internal readonly Nullability NullableContext; readonly MetadataNamespace rootNamespace; readonly MetadataTypeDefinition[] typeDefs; readonly MetadataField[] fieldDefs; readonly MetadataMethod[] methodDefs; readonly MetadataProperty[] propertyDefs; readonly MetadataEvent[] eventDefs; readonly IModule[] referencedAssemblies; internal MetadataModule(ICompilation compilation, MetadataFile peFile, TypeSystemOptions options) { this.Compilation = compilation; this.MetadataFile = peFile; this.metadata = peFile.Metadata; this.options = options; this.TypeProvider = new TypeProvider(this); // assembly metadata if (metadata.IsAssembly) { var asmdef = metadata.GetAssemblyDefinition(); try { this.AssemblyName = metadata.GetString(asmdef.Name); this.AssemblyVersion = asmdef.Version; this.FullAssemblyName = metadata.GetFullAssemblyName(); } catch (BadImageFormatException) { this.AssemblyName = ""; this.FullAssemblyName = ""; } } else { try { var moddef = metadata.GetModuleDefinition(); this.AssemblyName = metadata.GetString(moddef.Name); } catch (BadImageFormatException) { this.AssemblyName = ""; } this.FullAssemblyName = this.AssemblyName; } var customAttrs = metadata.GetModuleDefinition().GetCustomAttributes(); this.NullableContext = customAttrs.GetNullableContext(metadata) ?? Nullability.Oblivious; this.minAccessibilityForNRT = FindMinimumAccessibilityForNRT(metadata, customAttrs); this.rootNamespace = new MetadataNamespace(this, null, string.Empty, metadata.GetNamespaceDefinitionRoot()); if (!options.HasFlag(TypeSystemOptions.Uncached)) { // create arrays for resolved entities, indexed by row index this.typeDefs = new MetadataTypeDefinition[metadata.TypeDefinitions.Count + 1]; this.fieldDefs = new MetadataField[metadata.FieldDefinitions.Count + 1]; this.methodDefs = new MetadataMethod[metadata.MethodDefinitions.Count + 1]; this.propertyDefs = new MetadataProperty[metadata.PropertyDefinitions.Count + 1]; this.eventDefs = new MetadataEvent[metadata.EventDefinitions.Count + 1]; this.referencedAssemblies = new IModule[metadata.AssemblyReferences.Count + 1]; } } internal string GetString(StringHandle name) { return metadata.GetString(name); } public TypeSystemOptions TypeSystemOptions => options; #region IModule interface public MetadataFile MetadataFile { get; } public bool IsMainModule => this == Compilation.MainModule; public string AssemblyName { get; } public Version AssemblyVersion { get; } public string FullAssemblyName { get; } string ISymbol.Name => AssemblyName; SymbolKind ISymbol.SymbolKind => SymbolKind.Module; public INamespace RootNamespace => rootNamespace; public IEnumerable TopLevelTypeDefinitions => TypeDefinitions.Where(td => td.DeclaringTypeDefinition == null); public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName) { var typeDefHandle = MetadataFile.GetTypeDefinition(topLevelTypeName); if (typeDefHandle.IsNil) { var forwarderHandle = MetadataFile.GetTypeForwarder(topLevelTypeName); if (!forwarderHandle.IsNil) { var forwarder = metadata.GetExportedType(forwarderHandle); return ResolveForwardedType(forwarder).GetDefinition(); } } return GetDefinition(typeDefHandle); } #endregion #region InternalsVisibleTo public bool InternalsVisibleTo(IModule module) { if (this == module) return true; foreach (string shortName in GetInternalsVisibleTo()) { if (string.Equals(module.AssemblyName, shortName, StringComparison.OrdinalIgnoreCase)) return true; } return false; } string[] internalsVisibleTo; string[] GetInternalsVisibleTo() { var result = LazyInit.VolatileRead(ref this.internalsVisibleTo); if (result != null) { return result; } if (metadata.IsAssembly) { var list = new List(); foreach (var attrHandle in metadata.GetAssemblyDefinition().GetCustomAttributes()) { var attr = metadata.GetCustomAttribute(attrHandle); if (attr.IsKnownAttribute(metadata, KnownAttribute.InternalsVisibleTo)) { var attrValue = attr.DecodeValue(this.TypeProvider); if (attrValue.FixedArguments.Length == 1) { if (attrValue.FixedArguments[0].Value is string s) { list.Add(GetShortName(s)); } } } } result = list.ToArray(); } else { result = Empty.Array; } return LazyInit.GetOrSet(ref this.internalsVisibleTo, result); } static string GetShortName(string fullAssemblyName) { if (fullAssemblyName == null) return null; int pos = fullAssemblyName.IndexOf(','); if (pos < 0) return fullAssemblyName; else return fullAssemblyName.Substring(0, pos); } #endregion #region GetDefinition /// /// Gets all types in the assembly, including nested types. /// public IEnumerable TypeDefinitions { get { foreach (var tdHandle in metadata.TypeDefinitions) { yield return GetDefinition(tdHandle); } } } public ITypeDefinition GetDefinition(TypeDefinitionHandle handle) { if (handle.IsNil) return null; if (typeDefs == null) return new MetadataTypeDefinition(this, handle); int row = MetadataTokens.GetRowNumber(handle); if (row >= typeDefs.Length) HandleOutOfRange(handle); var typeDef = LazyInit.VolatileRead(ref typeDefs[row]); if (typeDef != null) return typeDef; typeDef = new MetadataTypeDefinition(this, handle); return LazyInit.GetOrSet(ref typeDefs[row], typeDef); } public IField GetDefinition(FieldDefinitionHandle handle) { if (handle.IsNil) return null; if (fieldDefs == null) return new MetadataField(this, handle); int row = MetadataTokens.GetRowNumber(handle); if (row >= fieldDefs.Length) HandleOutOfRange(handle); var field = LazyInit.VolatileRead(ref fieldDefs[row]); if (field != null) return field; field = new MetadataField(this, handle); return LazyInit.GetOrSet(ref fieldDefs[row], field); } public IMethod GetDefinition(MethodDefinitionHandle handle) { if (handle.IsNil) return null; if (methodDefs == null) return new MetadataMethod(this, handle); int row = MetadataTokens.GetRowNumber(handle); Debug.Assert(row != 0); if (row >= methodDefs.Length) HandleOutOfRange(handle); var method = LazyInit.VolatileRead(ref methodDefs[row]); if (method != null) return method; method = new MetadataMethod(this, handle); return LazyInit.GetOrSet(ref methodDefs[row], method); } public IProperty GetDefinition(PropertyDefinitionHandle handle) { if (handle.IsNil) return null; if (propertyDefs == null) return new MetadataProperty(this, handle); int row = MetadataTokens.GetRowNumber(handle); Debug.Assert(row != 0); if (row >= methodDefs.Length) HandleOutOfRange(handle); var property = LazyInit.VolatileRead(ref propertyDefs[row]); if (property != null) return property; property = new MetadataProperty(this, handle); return LazyInit.GetOrSet(ref propertyDefs[row], property); } public IEvent GetDefinition(EventDefinitionHandle handle) { if (handle.IsNil) return null; if (eventDefs == null) return new MetadataEvent(this, handle); int row = MetadataTokens.GetRowNumber(handle); Debug.Assert(row != 0); if (row >= methodDefs.Length) HandleOutOfRange(handle); var ev = LazyInit.VolatileRead(ref eventDefs[row]); if (ev != null) return ev; ev = new MetadataEvent(this, handle); return LazyInit.GetOrSet(ref eventDefs[row], ev); } void HandleOutOfRange(EntityHandle handle) { throw new BadImageFormatException("Handle with invalid row number."); } #endregion #region Resolve Module public IModule ResolveModule(AssemblyReferenceHandle handle) { if (handle.IsNil) return null; if (referencedAssemblies == null) return ResolveModuleUncached(handle); int row = metadata.GetRowNumber(handle); Debug.Assert(row != 0); if (row >= referencedAssemblies.Length) HandleOutOfRange(handle); var module = LazyInit.VolatileRead(ref referencedAssemblies[row]); if (module != null) return module; module = ResolveModuleUncached(handle); return LazyInit.GetOrSet(ref referencedAssemblies[row], module); } IModule ResolveModuleUncached(AssemblyReferenceHandle handle) { var asmRef = new Metadata.AssemblyReference(metadata, handle); return Compilation.FindModuleByReference(asmRef); } public IModule ResolveModule(ModuleReferenceHandle handle) { if (handle.IsNil) return null; var modRef = metadata.GetModuleReference(handle); string name = metadata.GetString(modRef.Name); foreach (var mod in Compilation.Modules) { if (mod.Name == name) { return mod; } } return null; } public IModule GetDeclaringModule(TypeReferenceHandle handle) { if (handle.IsNil) return null; var tr = metadata.GetTypeReference(handle); switch (tr.ResolutionScope.Kind) { case HandleKind.TypeReference: return GetDeclaringModule((TypeReferenceHandle)tr.ResolutionScope); case HandleKind.AssemblyReference: return ResolveModule((AssemblyReferenceHandle)tr.ResolutionScope); case HandleKind.ModuleReference: return ResolveModule((ModuleReferenceHandle)tr.ResolutionScope); default: return this; } } #endregion #region Resolve Type public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious) { return ResolveType(typeRefDefSpec, context, options, typeAttributes, nullableContext); } public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, TypeSystemOptions customOptions, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious) { if (typeRefDefSpec.IsNil) return SpecialType.UnknownType; IType ty; switch (typeRefDefSpec.Kind) { case HandleKind.TypeDefinition: ty = TypeProvider.GetTypeFromDefinition(metadata, (TypeDefinitionHandle)typeRefDefSpec, 0); break; case HandleKind.TypeReference: ty = TypeProvider.GetTypeFromReference(metadata, (TypeReferenceHandle)typeRefDefSpec, 0); break; case HandleKind.TypeSpecification: var typeSpec = metadata.GetTypeSpecification((TypeSpecificationHandle)typeRefDefSpec); ty = typeSpec.DecodeSignature(TypeProvider, context); break; case HandleKind.ExportedType: return ResolveForwardedType(metadata.GetExportedType((ExportedTypeHandle)typeRefDefSpec)); default: throw new BadImageFormatException("Not a type handle"); } ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, typeAttributes, metadata, customOptions, nullableContext); return ty; } IType ResolveDeclaringType(EntityHandle declaringTypeReference, GenericContext context) { // resolve without substituting dynamic/tuple types const TypeSystemOptions removedOptions = TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations | TypeSystemOptions.NativeIntegers | TypeSystemOptions.NativeIntegersWithoutAttribute; var ty = ResolveType(declaringTypeReference, context, options & ~removedOptions); // but substitute tuple types in type arguments: ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious, typeChildrenOnly: true); return ty; } IType IntroduceTupleTypes(IType ty) { // run ApplyAttributeTypeVisitor without attributes, in order to introduce tuple types return ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious); } #endregion #region Resolve Method public IMethod ResolveMethod(EntityHandle methodReference, GenericContext context) { if (methodReference.IsNil) throw new ArgumentNullException(nameof(methodReference)); switch (methodReference.Kind) { case HandleKind.MethodDefinition: return ResolveMethodDefinition((MethodDefinitionHandle)methodReference, expandVarArgs: true); case HandleKind.MemberReference: return ResolveMethodReference((MemberReferenceHandle)methodReference, context, expandVarArgs: true); case HandleKind.MethodSpecification: return ResolveMethodSpecification((MethodSpecificationHandle)methodReference, context, expandVarArgs: true); default: throw new BadImageFormatException("Metadata token must be either a methoddef, memberref or methodspec"); } } IMethod ResolveMethodDefinition(MethodDefinitionHandle methodDefHandle, bool expandVarArgs) { var method = GetDefinition(methodDefHandle); if (expandVarArgs && method.Parameters.LastOrDefault()?.Type.Kind == TypeKind.ArgList) { method = new VarArgInstanceMethod(method, EmptyList.Instance); } return method; } IMethod ResolveMethodSpecification(MethodSpecificationHandle methodSpecHandle, GenericContext context, bool expandVarArgs) { var methodSpec = metadata.GetMethodSpecification(methodSpecHandle); var methodTypeArgs = methodSpec.DecodeSignature(TypeProvider, context) .SelectReadOnlyArray(IntroduceTupleTypes); IMethod method; if (methodSpec.Method.Kind == HandleKind.MethodDefinition) { // generic instance of a methoddef (=generic method in non-generic class in current assembly) method = ResolveMethodDefinition((MethodDefinitionHandle)methodSpec.Method, expandVarArgs); method = method.Specialize(new TypeParameterSubstitution(null, methodTypeArgs)); } else { method = ResolveMethodReference((MemberReferenceHandle)methodSpec.Method, context, methodTypeArgs, expandVarArgs); } return method; } /// /// Resolves a method reference. /// /// /// Class type arguments are provided by the declaring type stored in the memberRef. /// Method type arguments are provided by the caller. /// IMethod ResolveMethodReference(MemberReferenceHandle memberRefHandle, GenericContext context, IReadOnlyList methodTypeArguments = null, bool expandVarArgs = true) { var memberRef = metadata.GetMemberReference(memberRefHandle); if (memberRef.GetKind() != MemberReferenceKind.Method) { throw new BadImageFormatException($"Member reference must be method, but was: {memberRef.GetKind()}"); } MethodSignature signature; IReadOnlyList classTypeArguments = null; IMethod method; if (memberRef.Parent.Kind == HandleKind.MethodDefinition) { method = ResolveMethodDefinition((MethodDefinitionHandle)memberRef.Parent, expandVarArgs: false); signature = memberRef.DecodeMethodSignature(TypeProvider, context); } else { var declaringType = ResolveDeclaringType(memberRef.Parent, context); var declaringTypeDefinition = declaringType.GetDefinition(); if (declaringType.TypeArguments.Count > 0) { classTypeArguments = declaringType.TypeArguments; } // Note: declaringType might be parameterized, but the signature is for the original method definition. // We'll have to search the member directly on declaringTypeDefinition. string name = metadata.GetString(memberRef.Name); signature = memberRef.DecodeMethodSignature(TypeProvider, new GenericContext(declaringTypeDefinition?.TypeParameters)); if (declaringTypeDefinition != null) { // Find the set of overloads to search: IEnumerable methods; if (name == ".ctor") { methods = declaringTypeDefinition.GetConstructors(); } else if (name == ".cctor") { methods = declaringTypeDefinition.Methods.Where(m => m.IsConstructor && m.IsStatic); } else { methods = declaringTypeDefinition.GetMethods(m => m.Name == name, GetMemberOptions.IgnoreInheritedMembers) .Concat(declaringTypeDefinition.GetAccessors(m => m.Name == name, GetMemberOptions.IgnoreInheritedMembers)); } // Determine the expected parameters from the signature: ImmutableArray parameterTypes; if (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { parameterTypes = signature.ParameterTypes .Take(signature.RequiredParameterCount) .Concat(new[] { SpecialType.ArgList }) .ToImmutableArray(); } else { parameterTypes = signature.ParameterTypes; } // Search for the matching method: method = null; foreach (var m in methods) { if (m.TypeParameters.Count != signature.GenericParameterCount) continue; if (CompareSignatures(m.Parameters, parameterTypes) && CompareTypes(m.ReturnType, signature.ReturnType)) { method = m; break; } } } else { method = null; } if (method == null) { method = CreateFakeMethod(declaringType, name, signature); } } if (classTypeArguments != null || methodTypeArguments != null) { method = method.Specialize(new TypeParameterSubstitution(classTypeArguments, methodTypeArguments)); } if (expandVarArgs && signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { method = new VarArgInstanceMethod(method, signature.ParameterTypes.Skip(signature.RequiredParameterCount)); } return method; } static readonly NormalizeTypeVisitor normalizeTypeVisitor = new NormalizeTypeVisitor { ReplaceClassTypeParametersWithDummy = true, ReplaceMethodTypeParametersWithDummy = true, }; static bool CompareTypes(IType a, IType b) { IType type1 = a.AcceptVisitor(normalizeTypeVisitor); IType type2 = b.AcceptVisitor(normalizeTypeVisitor); return type1.Equals(type2); } static bool CompareSignatures(IReadOnlyList parameters, ImmutableArray parameterTypes) { if (parameterTypes.Length != parameters.Count) return false; for (int i = 0; i < parameterTypes.Length; i++) { if (!CompareTypes(parameterTypes[i], parameters[i].Type)) return false; } return true; } /// /// Create a dummy IMethod from the specified MethodReference /// IMethod CreateFakeMethod(IType declaringType, string name, MethodSignature signature) { SymbolKind symbolKind = SymbolKind.Method; if (name == ".ctor" || name == ".cctor") symbolKind = SymbolKind.Constructor; var m = new FakeMethod(Compilation, symbolKind); m.DeclaringType = declaringType; m.Name = name; m.ReturnType = signature.ReturnType; m.IsStatic = !signature.Header.IsInstance; TypeParameterSubstitution substitution = null; if (signature.GenericParameterCount > 0) { var typeParameters = new List(); for (int i = 0; i < signature.GenericParameterCount; i++) { typeParameters.Add(new DefaultTypeParameter(m, i)); } m.TypeParameters = typeParameters; substitution = new TypeParameterSubstitution(declaringType.TypeArguments, typeParameters); } else if (declaringType.TypeArguments.Count > 0) { substitution = declaringType.GetSubstitution(); } var parameters = new List(); for (int i = 0; i < signature.RequiredParameterCount; i++) { var type = signature.ParameterTypes[i]; if (substitution != null) { // replace the dummy method type parameters with the owned instances we just created type = type.AcceptVisitor(substitution); } parameters.Add(new DefaultParameter(type, "")); } m.Parameters = parameters; GuessFakeMethodAccessor(declaringType, name, signature, m, parameters); return m; } private void GuessFakeMethodAccessor(IType declaringType, string name, MethodSignature signature, FakeMethod m, List parameters) { if (signature.GenericParameterCount > 0) return; var guessedGetter = name.StartsWith("get_", StringComparison.Ordinal); var guessedSetter = name.StartsWith("set_", StringComparison.Ordinal); if (guessedGetter || guessedSetter) { var propertyName = name.Substring(4); var fakeProperty = new FakeProperty(Compilation) { Name = propertyName, DeclaringType = declaringType, IsStatic = m.IsStatic, }; if (guessedGetter) { if (signature.ReturnType.Kind == TypeKind.Void) return; m.AccessorKind = MethodSemanticsAttributes.Getter; m.AccessorOwner = fakeProperty; fakeProperty.Getter = m; fakeProperty.ReturnType = signature.ReturnType; fakeProperty.IsIndexer = parameters.Count > 0; fakeProperty.Parameters = parameters; return; } if (guessedSetter) { if (parameters.Count < 1 || signature.ReturnType.Kind != TypeKind.Void) return; m.AccessorKind = MethodSemanticsAttributes.Setter; m.AccessorOwner = fakeProperty; fakeProperty.Setter = m; fakeProperty.ReturnType = parameters.Last().Type; fakeProperty.IsIndexer = parameters.Count > 1; fakeProperty.Parameters = parameters.SkipLast(1).ToArray(); return; } } const string addPrefix = "add_"; const string removePrefix = "remove_"; const string raisePrefix = "raise_"; var guessedAdd = name.StartsWith(addPrefix, StringComparison.Ordinal); var guessedRemove = name.StartsWith(removePrefix, StringComparison.Ordinal); var guessedRaise = name.StartsWith(raisePrefix, StringComparison.Ordinal); if (guessedAdd || guessedRemove || guessedRaise) { var fakeEvent = new FakeEvent(Compilation) { DeclaringType = declaringType, IsStatic = m.IsStatic, }; if (guessedAdd) { if (parameters.Count != 1) return; m.AccessorKind = MethodSemanticsAttributes.Adder; m.AccessorOwner = fakeEvent; fakeEvent.Name = name.Substring(addPrefix.Length); fakeEvent.AddAccessor = m; fakeEvent.ReturnType = parameters.Single().Type; return; } if (guessedRemove) { if (parameters.Count != 1) return; m.AccessorKind = MethodSemanticsAttributes.Remover; m.AccessorOwner = fakeEvent; fakeEvent.Name = name.Substring(removePrefix.Length); fakeEvent.RemoveAccessor = m; fakeEvent.ReturnType = parameters.Single().Type; return; } if (guessedRaise) { fakeEvent.Name = name.Substring(raisePrefix.Length); fakeEvent.InvokeAccessor = m; m.AccessorKind = MethodSemanticsAttributes.Raiser; m.AccessorOwner = fakeEvent; return; } } } #endregion #region Resolve Entity /// /// Resolves a symbol. /// /// /// * Types are resolved to their definition, as IType does not implement ISymbol. /// * types without definition will resolve to null /// * use ResolveType() to properly resolve types /// * When resolving methods, varargs signatures are not expanded. /// * use ResolveMethod() instead to get an IMethod instance suitable for call-sites /// * May return specialized members, where generics are involved. /// * Other types of handles that don't correspond to TS entities, will return null. /// public IEntity ResolveEntity(EntityHandle entityHandle, GenericContext context = default) { switch (entityHandle.Kind) { case HandleKind.TypeReference: case HandleKind.TypeDefinition: case HandleKind.TypeSpecification: case HandleKind.ExportedType: // Using ResolveDeclaringType() here because ResolveType() might return // nint/nuint which are SpecialTypes without a definition. return ResolveDeclaringType(entityHandle, context).GetDefinition(); case HandleKind.MemberReference: var memberReferenceHandle = (MemberReferenceHandle)entityHandle; switch (metadata.GetMemberReference(memberReferenceHandle).GetKind()) { case MemberReferenceKind.Method: // for consistency with the MethodDefinition case, never expand varargs return ResolveMethodReference(memberReferenceHandle, context, expandVarArgs: false); case MemberReferenceKind.Field: return ResolveFieldReference(memberReferenceHandle, context); default: throw new BadImageFormatException("Unknown MemberReferenceKind"); } case HandleKind.MethodDefinition: return GetDefinition((MethodDefinitionHandle)entityHandle); case HandleKind.MethodSpecification: return ResolveMethodSpecification((MethodSpecificationHandle)entityHandle, context, expandVarArgs: false); case HandleKind.FieldDefinition: return GetDefinition((FieldDefinitionHandle)entityHandle); case HandleKind.EventDefinition: return GetDefinition((EventDefinitionHandle)entityHandle); case HandleKind.PropertyDefinition: return GetDefinition((PropertyDefinitionHandle)entityHandle); default: return null; } } IField ResolveFieldReference(MemberReferenceHandle memberReferenceHandle, GenericContext context) { var memberRef = metadata.GetMemberReference(memberReferenceHandle); var declaringType = ResolveDeclaringType(memberRef.Parent, context); var declaringTypeDefinition = declaringType.GetDefinition(); string name = metadata.GetString(memberRef.Name); // field signature is for the definition, not the generic instance var signature = memberRef.DecodeFieldSignature(TypeProvider, new GenericContext(declaringTypeDefinition?.TypeParameters)); // 'f' in the predicate is also the definition, even if declaringType is a ParameterizedType var field = declaringType.GetFields(f => f.Name == name && CompareTypes(f.ReturnType, signature), GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); if (field == null) { // If it's a field in a generic type, we need to substitute the type arguments: if (declaringType.TypeArguments.Count > 0) { signature = signature.AcceptVisitor(declaringType.GetSubstitution()); } field = new FakeField(Compilation) { ReturnType = signature, Name = name, DeclaringType = declaringType, }; } return field; } #endregion #region Decode Standalone Signature public (SignatureHeader, FunctionPointerType) DecodeMethodSignature(StandaloneSignatureHandle handle, GenericContext genericContext) { var standaloneSignature = metadata.GetStandaloneSignature(handle); if (standaloneSignature.GetKind() != StandaloneSignatureKind.Method) throw new BadImageFormatException("Expected Method signature"); var sig = standaloneSignature.DecodeMethodSignature(TypeProvider, genericContext); var fpt = FunctionPointerType.FromSignature(sig, this); return (sig.Header, (FunctionPointerType)IntroduceTupleTypes(fpt)); } public ImmutableArray DecodeLocalSignature(StandaloneSignatureHandle handle, GenericContext genericContext) { var standaloneSignature = metadata.GetStandaloneSignature(handle); if (standaloneSignature.GetKind() != StandaloneSignatureKind.LocalVariables) throw new BadImageFormatException("Expected LocalVariables signature"); var types = standaloneSignature.DecodeLocalSignature(TypeProvider, genericContext); return ImmutableArray.CreateRange(types, IntroduceTupleTypes); } #endregion #region Module / Assembly attributes /// /// Gets the list of all assembly attributes in the project. /// public IEnumerable GetAssemblyAttributes() { var b = new AttributeListBuilder(this); if (metadata.IsAssembly) { var assembly = metadata.GetAssemblyDefinition(); b.Add(metadata.GetCustomAttributes(Handle.AssemblyDefinition), SymbolKind.Module); b.AddSecurityAttributes(assembly.GetDeclarativeSecurityAttributes()); // AssemblyVersionAttribute if (assembly.Version != null) { b.Add(KnownAttribute.AssemblyVersion, KnownTypeCode.String, assembly.Version.ToString()); } AddTypeForwarderAttributes(ref b); } return b.Build(); } /// /// Gets the list of all module attributes in the project. /// public IEnumerable GetModuleAttributes() { var b = new AttributeListBuilder(this); b.Add(metadata.GetCustomAttributes(Handle.ModuleDefinition), SymbolKind.Module); if (!metadata.IsAssembly) { AddTypeForwarderAttributes(ref b); } return b.Build(); } void AddTypeForwarderAttributes(ref AttributeListBuilder b) { foreach (ExportedTypeHandle t in metadata.ExportedTypes) { var type = metadata.GetExportedType(t); if (type.IsForwarder) { b.Add(KnownAttribute.TypeForwardedTo, KnownTypeCode.Type, ResolveForwardedType(type)); } } } IType ResolveForwardedType(ExportedType forwarder) { IModule module = ResolveModule(forwarder); var typeName = forwarder.GetFullTypeName(metadata); if (module == null) return new UnknownType(typeName); using (var busyLock = BusyManager.Enter(this)) { if (busyLock.Success) { var td = module.GetTypeDefinition(typeName); if (td != null) { return td; } } } return new UnknownType(typeName); IModule ResolveModule(ExportedType type) { switch (type.Implementation.Kind) { case HandleKind.AssemblyFile: // TODO : Resolve assembly file (module)... return this; case HandleKind.ExportedType: var outerType = metadata.GetExportedType((ExportedTypeHandle)type.Implementation); return ResolveModule(outerType); case HandleKind.AssemblyReference: var asmRef = metadata.GetAssemblyReference((AssemblyReferenceHandle)type.Implementation); string shortName = metadata.GetString(asmRef.Name); foreach (var asm in Compilation.Modules) { if (string.Equals(asm.AssemblyName, shortName, StringComparison.OrdinalIgnoreCase)) { return asm; } } return null; default: throw new BadImageFormatException("Expected implementation to be either an AssemblyFile, ExportedType or AssemblyReference."); } } } #endregion #region Attribute Helpers /// /// Cache for parameterless known attribute types. /// readonly IType[] knownAttributeTypes = new IType[KnownAttributes.Count]; internal IType GetAttributeType(KnownAttribute attr) { var ty = LazyInit.VolatileRead(ref knownAttributeTypes[(int)attr]); if (ty != null) return ty; ty = Compilation.FindType(attr.GetTypeName()); return LazyInit.GetOrSet(ref knownAttributeTypes[(int)attr], ty); } /// /// Cache for parameterless known attributes. /// readonly IAttribute[] knownAttributes = new IAttribute[KnownAttributes.Count]; /// /// Construct a builtin attribute. /// internal IAttribute MakeAttribute(KnownAttribute type) { var attr = LazyInit.VolatileRead(ref knownAttributes[(int)type]); if (attr != null) return attr; attr = new DefaultAttribute(GetAttributeType(type), ImmutableArray.Create>(), ImmutableArray.Create>()); return LazyInit.GetOrSet(ref knownAttributes[(int)type], attr); } #endregion #region Visibility Filter internal bool IncludeInternalMembers => (options & TypeSystemOptions.OnlyPublicAPI) == 0; internal bool IsVisible(FieldAttributes att) { att &= FieldAttributes.FieldAccessMask; return IncludeInternalMembers || att == FieldAttributes.Public || att == FieldAttributes.Family || att == FieldAttributes.FamORAssem; } internal bool IsVisible(MethodAttributes att) { att &= MethodAttributes.MemberAccessMask; return IncludeInternalMembers || att == MethodAttributes.Public || att == MethodAttributes.Family || att == MethodAttributes.FamORAssem; } #endregion #region Nullability Reference Type Support readonly Accessibility minAccessibilityForNRT; static Accessibility FindMinimumAccessibilityForNRT(MetadataReader metadata, CustomAttributeHandleCollection customAttributes) { // Determine the minimum effective accessibility an entity must have, so that the metadata stores the nullability for its type. foreach (var handle in customAttributes) { var customAttribute = metadata.GetCustomAttribute(handle); if (customAttribute.IsKnownAttribute(metadata, KnownAttribute.NullablePublicOnly)) { CustomAttributeValue value; try { value = customAttribute.DecodeValue(Metadata.MetadataExtensions.MinimalAttributeTypeProvider); } catch (BadImageFormatException) { continue; } catch (EnumUnderlyingTypeResolveException) { continue; } if (value.FixedArguments.Length == 1 && value.FixedArguments[0].Value is bool includesInternals) { return includesInternals ? Accessibility.ProtectedAndInternal : Accessibility.Protected; } } } return Accessibility.None; } internal bool ShouldDecodeNullableAttributes(IEntity entity) { if ((options & TypeSystemOptions.NullabilityAnnotations) == 0) return false; if (minAccessibilityForNRT == Accessibility.None || entity == null) return true; return minAccessibilityForNRT.LessThanOrEqual(entity.EffectiveAccessibility()); } internal TypeSystemOptions OptionsForEntity(IEntity entity) { var opt = this.options; if ((opt & TypeSystemOptions.NullabilityAnnotations) != 0) { if (!ShouldDecodeNullableAttributes(entity)) { opt &= ~TypeSystemOptions.NullabilityAnnotations; } } return opt; } #endregion } }