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

267 lines
10 KiB

// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp.Resolver
{
/// <summary>
/// Implementation of member lookup (C# 4.0 spec, §7.4).
/// </summary>
public class MemberLookup
{
#region Static helper methods
/// <summary>
/// Gets whether the member is considered to be invocable.
/// </summary>
public static bool IsInvocable(IMember member, ITypeResolveContext context)
{
if (member == null)
throw new ArgumentNullException("member");
// C# 4.0 spec, §7.4 member lookup
if (member is IEvent || member is IMethod)
return true;
if (member.ReturnType == SharedTypes.Dynamic)
return true;
return member.ReturnType.Resolve(context).IsDelegate();
}
#endregion
ITypeResolveContext context;
ITypeDefinition currentTypeDefinition;
IProjectContent currentProject;
public MemberLookup(ITypeResolveContext context, ITypeDefinition currentTypeDefinition, IProjectContent currentProject)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
this.currentTypeDefinition = currentTypeDefinition;
this.currentProject = currentProject;
}
#region IsAccessible
public bool IsProtectedAccessAllowed(IType targetType)
{
ITypeDefinition typeDef = targetType.GetDefinition();
return typeDef != null && typeDef.IsDerivedFrom(currentTypeDefinition, context);
}
/// <summary>
/// Gets whether <paramref name="entity"/> is accessible in the current class.
/// </summary>
/// <param name="entity">The entity to test</param>
/// <param name="allowProtectedAccess">Whether protected access is allowed.
/// True if the type of the reference is derived from the current class.</param>
public bool IsAccessible(IEntity entity, bool allowProtectedAccess)
{
if (entity == null)
throw new ArgumentNullException("entity");
// C# 4.0 spec, §3.5.2 Accessiblity domains
switch (entity.Accessibility) {
case Accessibility.None:
return false;
case Accessibility.Private:
return entity.DeclaringTypeDefinition == currentTypeDefinition;
case Accessibility.Public:
return true;
case Accessibility.Protected:
return allowProtectedAccess && IsProtectedAccessible(entity.DeclaringTypeDefinition);
case Accessibility.Internal:
return IsInternalAccessible(entity.ProjectContent);
case Accessibility.ProtectedOrInternal:
return (allowProtectedAccess && IsProtectedAccessible(entity.DeclaringTypeDefinition))
|| IsInternalAccessible(entity.ProjectContent);
case Accessibility.ProtectedAndInternal:
return (allowProtectedAccess && IsProtectedAccessible(entity.DeclaringTypeDefinition))
&& IsInternalAccessible(entity.ProjectContent);
default:
throw new Exception("Invalid value for Accessibility");
}
}
bool IsInternalAccessible(IProjectContent declaringProject)
{
return declaringProject != null && currentProject != null && declaringProject.InternalsVisibleTo(currentProject, context);
}
bool IsProtectedAccessible(ITypeDefinition declaringType)
{
if (declaringType == currentTypeDefinition)
return true;
// PERF: this might hurt performance as this method is called several times (once for each member)
// make sure resolving base types is cheap (caches?) or cache within the MemberLookup instance
return currentTypeDefinition != null && currentTypeDefinition.IsDerivedFrom(declaringType, context);
}
#endregion
public ResolveResult LookupType(IType declaringType, string name, IList<IType> typeArguments)
{
int typeArgumentCount = typeArguments.Count;
Predicate<ITypeDefinition> typeFilter = delegate (ITypeDefinition d) {
return d.TypeParameterCount == typeArgumentCount && d.Name == name && IsAccessible(d, true);
};
List<IType> types = declaringType.GetNestedTypes(context, typeFilter).ToList();
RemoveTypesHiddenByOtherTypes(types);
if (types.Count > 0)
return CreateTypeResolveResult(types[0], types.Count > 1, typeArguments);
else
return new UnknownMemberResolveResult(declaringType, name, typeArguments);
}
void RemoveTypesHiddenByOtherTypes(List<IType> types)
{
for (int i = types.Count - 1; i >= 0; i--) {
ITypeDefinition d = GetDeclaringTypeDef(types[i]);
if (d == null)
continue;
// nested loop depends on the fact that the members of more derived classes appear later in the list
for (int j = i + 1; j < types.Count; j++) {
if (types[i].TypeParameterCount != types[j].TypeParameterCount)
continue;
ITypeDefinition s = GetDeclaringTypeDef(types[j]);
if (s != null && s != d && s.IsDerivedFrom(d, context)) {
// types[j] hides types[i]
types.RemoveAt(i);
break;
}
}
}
}
ResolveResult CreateTypeResolveResult(IType returnedType, bool isAmbiguous, IList<IType> typeArguments)
{
if (typeArguments.Count > 0) {
// parameterize the type if necessary
ITypeDefinition returnedTypeDef = returnedType as ITypeDefinition;
if (returnedTypeDef != null)
returnedType = new ParameterizedType(returnedTypeDef, typeArguments);
}
if (isAmbiguous)
return new AmbiguousTypeResolveResult(returnedType);
else
return new TypeResolveResult(returnedType);
}
/// <summary>
/// Performs a member lookup.
/// </summary>
public ResolveResult Lookup(IType type, string name, IList<IType> typeArguments, bool isInvocation)
{
int typeArgumentCount = typeArguments.Count;
List<IType> types = new List<IType>();
List<IMember> members = new List<IMember>();
if (!isInvocation) {
// Consider nested types only if it's not an invocation. The type parameter count must match in this case.
Predicate<ITypeDefinition> typeFilter = delegate (ITypeDefinition d) {
return d.TypeParameterCount == typeArgumentCount && d.Name == name && IsAccessible(d, true);
};
types.AddRange(type.GetNestedTypes(context, typeFilter));
}
bool allowProtectedAccess = IsProtectedAccessAllowed(type);
if (typeArgumentCount == 0) {
Predicate<IMember> memberFilter = delegate(IMember member) {
return !member.IsOverride && member.Name == name && IsAccessible(member, allowProtectedAccess);
};
members.AddRange(type.GetMethods(context, memberFilter.SafeCast<IMember, IMethod>()).SafeCast<IMethod, IMember>());
members.AddRange(type.GetProperties(context, memberFilter.SafeCast<IMember, IProperty>()).SafeCast<IProperty, IMember>());
members.AddRange(type.GetFields(context, memberFilter.SafeCast<IMember, IField>()).SafeCast<IField, IMember>());
members.AddRange(type.GetEvents(context, memberFilter.SafeCast<IMember, IEvent>()).SafeCast<IEvent, IMember>());
if (isInvocation)
members.RemoveAll(m => !IsInvocable(m, context));
} else {
// No need to check for isInvocation/isInvocable here:
// we filter out all non-methods
Predicate<IMethod> memberFilter = delegate(IMethod method) {
return method.TypeParameters.Count == typeArgumentCount
&& !method.IsOverride && method.Name == name && IsAccessible(method, allowProtectedAccess);
};
members.AddRange(type.GetMethods(context, memberFilter).SafeCast<IMethod, IMember>());
}
// TODO: can't members also hide types?
RemoveTypesHiddenByOtherTypes(types);
// remove members hidden by types
for (int i = 0; i < types.Count; i++) {
ITypeDefinition d = GetDeclaringTypeDef(types[i]);
if (d != null)
members.RemoveAll(m => d.IsDerivedFrom(m.DeclaringTypeDefinition, context));
}
// remove members hidden by other members
for (int i = members.Count - 1; i >= 0; i--) {
ITypeDefinition d = members[i].DeclaringTypeDefinition;
IMethod mi = members[i] as IMethod;
// nested loop depends on the fact that the members of more derived classes appear later in the list
for (int j = i + 1; j < members.Count; j++) {
if (mi != null) {
IMethod mj = members[j] as IMethod;
if (mj != null && !ParameterListComparer.Instance.Equals(mi, mj))
continue;
}
ITypeDefinition s = members[j].DeclaringTypeDefinition;
if (s != null && s != d && s.IsDerivedFrom(d, context)) {
// members[j] hides members[i]
members.RemoveAt(i);
break;
}
}
}
// remove interface members hidden by class members
if (type is ITypeParameter) {
// this can happen only with type parameters
for (int i = members.Count - 1; i >= 0; i--) {
ITypeDefinition d = members[i].DeclaringTypeDefinition;
if (d.ClassType != ClassType.Interface)
continue;
IMethod mi = members[i] as IMethod;
for (int j = 0; j < members.Count; j++) {
if (mi != null) {
IMethod mj = members[j] as IMethod;
if (mj != null && !ParameterListComparer.Instance.Equals(mi, mj))
continue;
}
ITypeDefinition s = members[j].DeclaringTypeDefinition;
if (s != null && IsNonInterfaceType(s)) {
// members[j] hides members[i]
members.RemoveAt(i);
break;
}
}
}
}
if (types.Count > 0) {
bool isAmbiguous = !(types.Count == 1 && members.Count == 0);
return CreateTypeResolveResult(types[0], isAmbiguous, typeArguments);
}
if (members.Count == 0)
return new UnknownMemberResolveResult(type, name, typeArguments);
IMember firstNonMethod = members.FirstOrDefault(m => !(m is IMethod));
if (members.Count == 1 && firstNonMethod != null)
return new MemberResolveResult(firstNonMethod, context);
if (firstNonMethod == null)
return new MethodGroupResolveResult(type, name, members.ConvertAll(m => (IMethod)m), typeArguments);
return new AmbiguousMemberResultResult(firstNonMethod, firstNonMethod.ReturnType.Resolve(context));
}
static bool IsNonInterfaceType(ITypeDefinition def)
{
// return type if def is neither an interface nor System.Object
return def.ClassType != ClassType.Interface && !(def.Name == "Object" && def.Namespace == "System" && def.TypeParameterCount == 0);
}
static ITypeDefinition GetDeclaringTypeDef(IType type)
{
IType declType = type.DeclaringType;
return declType != null ? declType.GetDefinition() : null;
}
}
}