mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1045 lines
35 KiB
1045 lines
35 KiB
// 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 |
|
{ |
|
/// <summary> |
|
/// Type system implementation for Metadata.PEFile. |
|
/// </summary> |
|
[DebuggerDisplay("<MetadataModule: {AssemblyName}>")] |
|
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, Metadata.PEFile peFile, TypeSystemOptions options) |
|
{ |
|
this.Compilation = compilation; |
|
this.PEFile = 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 = "<ERR: invalid assembly name>"; |
|
this.FullAssemblyName = "<ERR: invalid assembly name>"; |
|
} |
|
} |
|
else |
|
{ |
|
try |
|
{ |
|
var moddef = metadata.GetModuleDefinition(); |
|
this.AssemblyName = metadata.GetString(moddef.Name); |
|
} |
|
catch (BadImageFormatException) |
|
{ |
|
this.AssemblyName = "<ERR: invalid assembly name>"; |
|
} |
|
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 IAssembly interface |
|
public PEFile PEFile { 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<ITypeDefinition> TopLevelTypeDefinitions => TypeDefinitions.Where(td => td.DeclaringTypeDefinition == null); |
|
|
|
public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName) |
|
{ |
|
var typeDefHandle = PEFile.GetTypeDefinition(topLevelTypeName); |
|
if (typeDefHandle.IsNil) |
|
{ |
|
var forwarderHandle = PEFile.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<string>(); |
|
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<string>.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 |
|
/// <summary> |
|
/// Gets all types in the assembly, including nested types. |
|
/// </summary> |
|
public IEnumerable<ITypeDefinition> 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 |
|
var ty = ResolveType(declaringTypeReference, context, |
|
options & ~(TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations)); |
|
// 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<IType>.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; |
|
} |
|
|
|
/// <summary> |
|
/// Resolves a method reference. |
|
/// </summary> |
|
/// <remarks> |
|
/// Class type arguments are provided by the declaring type stored in the memberRef. |
|
/// Method type arguments are provided by the caller. |
|
/// </remarks> |
|
IMethod ResolveMethodReference(MemberReferenceHandle memberRefHandle, GenericContext context, IReadOnlyList<IType> 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<IType> signature; |
|
IReadOnlyList<IType> 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<IMethod> 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<IType> 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<IParameter> parameters, ImmutableArray<IType> 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; |
|
} |
|
|
|
/// <summary> |
|
/// Create a dummy IMethod from the specified MethodReference |
|
/// </summary> |
|
IMethod CreateFakeMethod(IType declaringType, string name, MethodSignature<IType> 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<ITypeParameter>(); |
|
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<IParameter>(); |
|
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<IType> signature, FakeMethod m, List<IParameter> 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 |
|
/// <summary> |
|
/// Resolves a symbol. |
|
/// </summary> |
|
/// <remarks> |
|
/// * Types are resolved to their definition, as IType does not implement ISymbol. |
|
/// * types without definition will resolve to <c>null</c> |
|
/// * 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 <c>null</c>. |
|
/// </remarks> |
|
public IEntity ResolveEntity(EntityHandle entityHandle, GenericContext context = default) |
|
{ |
|
switch (entityHandle.Kind) |
|
{ |
|
case HandleKind.TypeReference: |
|
case HandleKind.TypeDefinition: |
|
case HandleKind.TypeSpecification: |
|
case HandleKind.ExportedType: |
|
return ResolveType(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<IType> 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 |
|
/// <summary> |
|
/// Gets the list of all assembly attributes in the project. |
|
/// </summary> |
|
public IEnumerable<IAttribute> 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(); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the list of all module attributes in the project. |
|
/// </summary> |
|
public IEnumerable<IAttribute> 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 |
|
/// <summary> |
|
/// Cache for parameterless known attribute types. |
|
/// </summary> |
|
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); |
|
} |
|
|
|
/// <summary> |
|
/// Cache for parameterless known attributes. |
|
/// </summary> |
|
readonly IAttribute[] knownAttributes = new IAttribute[KnownAttributes.Count]; |
|
|
|
/// <summary> |
|
/// Construct a builtin attribute. |
|
/// </summary> |
|
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<CustomAttributeTypedArgument<IType>>(), |
|
ImmutableArray.Create<CustomAttributeNamedArgument<IType>>()); |
|
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<IType> 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 |
|
} |
|
}
|
|
|