.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

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