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.
582 lines
17 KiB
582 lines
17 KiB
// Copyright (c) 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.Generic; |
|
using System.Linq; |
|
using ICSharpCode.NRefactory.CSharp.Analysis; |
|
using ICSharpCode.NRefactory.TypeSystem; |
|
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
|
using ICSharpCode.NRefactory.Utils; |
|
|
|
namespace ICSharpCode.NRefactory.CSharp.Resolver.ConstantValues |
|
{ |
|
// Contains representations for constant C# expressions. |
|
// We use these instead of storing the full AST to reduce the memory usage. |
|
|
|
public sealed class CSharpConstantValue : Immutable, IConstantValue, ISupportsInterning |
|
{ |
|
ConstantExpression expression; |
|
UsingScope parentUsingScope; |
|
ITypeDefinition parentTypeDefinition; |
|
|
|
public CSharpConstantValue(ConstantExpression expression, UsingScope parentUsingScope, ITypeDefinition parentTypeDefinition) |
|
{ |
|
if (expression == null) |
|
throw new ArgumentNullException("expression"); |
|
this.expression = expression; |
|
this.parentUsingScope = parentUsingScope; |
|
this.parentTypeDefinition = parentTypeDefinition; |
|
} |
|
|
|
CSharpResolver CreateResolver(ITypeResolveContext context) |
|
{ |
|
// Because constants are evaluated by the compiler, we need to evaluate them in the resolve context |
|
// of the project where they are defined, not in that where the constant value is used. |
|
// TODO: how do we get the correct resolve context? |
|
return new CSharpResolver(context) { |
|
CheckForOverflow = false, // TODO: get project-wide overflow setting |
|
CurrentTypeDefinition = parentTypeDefinition, |
|
UsingScope = parentUsingScope |
|
}; |
|
} |
|
|
|
public IType GetValueType(ITypeResolveContext context) |
|
{ |
|
CSharpResolver resolver = CreateResolver(context); |
|
IType type = expression.Resolve(resolver).Type; |
|
if (resolver.Context != context) { |
|
// Retrieve the equivalent type in the new resolve context. |
|
// E.g. if the constant is defined in a .NET 2.0 project, type might be Int32 from mscorlib 2.0. |
|
// However, the calling project might be a .NET 4.0 project, so we need to return Int32 from mscorlib 4.0. |
|
return type.AcceptVisitor(new MapTypeIntoNewContext(context)); |
|
} |
|
return type; |
|
} |
|
|
|
public object GetValue(ITypeResolveContext context) |
|
{ |
|
CSharpResolver resolver = CreateResolver(context); |
|
object val = expression.Resolve(resolver).ConstantValue; |
|
if (resolver.Context != context) { |
|
// If 'val' is a type or an array containing types, we need to map it to the new context. |
|
val = MapToNewContext(val, context); |
|
} |
|
return val; |
|
} |
|
|
|
static object MapToNewContext(object val, ITypeResolveContext context) |
|
{ |
|
IType type = val as IType; |
|
if (type != null) { |
|
return type.AcceptVisitor(new MapTypeIntoNewContext(context)); |
|
} |
|
object[] arr = val as object[]; |
|
if (arr != null) { |
|
object[] newArr = new object[arr.Length]; |
|
bool modified = false; |
|
for (int i = 0; i < arr.Length; i++) { |
|
newArr[i] = MapToNewContext(arr[i], context); |
|
modified |= arr[i] != newArr[i]; |
|
} |
|
if (modified) |
|
return newArr; |
|
} |
|
return val; |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
expression = provider.Intern(expression); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
return expression.GetHashCode() |
|
^ (parentUsingScope != null ? parentUsingScope.GetHashCode() : 0) |
|
^ (parentTypeDefinition != null ? parentTypeDefinition.GetHashCode() : 0); |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
CSharpConstantValue cv = other as CSharpConstantValue; |
|
return cv != null |
|
&& expression == cv.expression |
|
&& parentUsingScope == cv.parentUsingScope |
|
&& parentTypeDefinition == cv.parentTypeDefinition; |
|
} |
|
} |
|
|
|
public abstract class ConstantExpression |
|
{ |
|
public abstract ResolveResult Resolve(CSharpResolver resolver); |
|
} |
|
|
|
/// <summary> |
|
/// C#'s equivalent to the SimpleConstantValue. |
|
/// </summary> |
|
public sealed class PrimitiveConstantExpression : ConstantExpression, ISupportsInterning |
|
{ |
|
ITypeReference type; |
|
object value; |
|
|
|
public ITypeReference Type { |
|
get { return type; } |
|
} |
|
|
|
public object Value { |
|
get { return value; } |
|
} |
|
|
|
public PrimitiveConstantExpression(ITypeReference type, object value) |
|
{ |
|
if (type == null) |
|
throw new ArgumentNullException("type"); |
|
this.type = type; |
|
this.value = value; |
|
} |
|
|
|
public override ResolveResult Resolve(CSharpResolver resolver) |
|
{ |
|
object val = value; |
|
if (val is ITypeReference) |
|
val = ((ITypeReference)val).Resolve(resolver.Context); |
|
return new ConstantResolveResult(type.Resolve(resolver.Context), val); |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
type = provider.Intern(type); |
|
value = provider.Intern(value); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
return type.GetHashCode() ^ (value != null ? value.GetHashCode() : 0); |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
PrimitiveConstantExpression scv = other as PrimitiveConstantExpression; |
|
return scv != null && type == scv.type && value == scv.value; |
|
} |
|
} |
|
|
|
public sealed class ConstantCast : ConstantExpression, ISupportsInterning |
|
{ |
|
ITypeReference targetType; |
|
ConstantExpression expression; |
|
|
|
public ConstantCast(ITypeReference targetType, ConstantExpression expression) |
|
{ |
|
if (targetType == null) |
|
throw new ArgumentNullException("targetType"); |
|
if (expression == null) |
|
throw new ArgumentNullException("expression"); |
|
this.targetType = targetType; |
|
this.expression = expression; |
|
} |
|
|
|
public override ResolveResult Resolve(CSharpResolver resolver) |
|
{ |
|
return resolver.ResolveCast(targetType.Resolve(resolver.Context), expression.Resolve(resolver)); |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
targetType = provider.Intern(targetType); |
|
expression = provider.Intern(expression); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
unchecked { |
|
return targetType.GetHashCode() + expression.GetHashCode() * 1018829; |
|
} |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
ConstantCast cast = other as ConstantCast; |
|
return cast != null |
|
&& this.targetType == cast.targetType && this.expression == cast.expression; |
|
} |
|
} |
|
|
|
public sealed class ConstantIdentifierReference : ConstantExpression, ISupportsInterning |
|
{ |
|
string identifier; |
|
IList<ITypeReference> typeArguments; |
|
|
|
public ConstantIdentifierReference(string identifier, IList<ITypeReference> typeArguments = null) |
|
{ |
|
if (identifier == null) |
|
throw new ArgumentNullException("identifier"); |
|
this.identifier = identifier; |
|
this.typeArguments = typeArguments; |
|
} |
|
|
|
public override ResolveResult Resolve(CSharpResolver resolver) |
|
{ |
|
return resolver.ResolveSimpleName(identifier, ResolveTypes(resolver, typeArguments)); |
|
} |
|
|
|
internal static IList<IType> ResolveTypes(CSharpResolver resolver, IList<ITypeReference> typeArguments) |
|
{ |
|
if (typeArguments == null) |
|
return EmptyList<IType>.Instance; |
|
IType[] types = new IType[typeArguments.Count]; |
|
for (int i = 0; i < types.Length; i++) { |
|
types[i] = typeArguments[i].Resolve(resolver.Context); |
|
} |
|
return types; |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
identifier = provider.Intern(identifier); |
|
typeArguments = provider.InternList(typeArguments); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
unchecked { |
|
int hashCode = identifier.GetHashCode(); |
|
if (typeArguments != null) |
|
hashCode ^= typeArguments.GetHashCode(); |
|
return hashCode; |
|
} |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
ConstantIdentifierReference cir = other as ConstantIdentifierReference; |
|
return cir != null && |
|
this.identifier == cir.identifier && this.typeArguments == cir.typeArguments; |
|
} |
|
} |
|
|
|
public sealed class ConstantMemberReference : ConstantExpression, ISupportsInterning |
|
{ |
|
ITypeReference targetType; |
|
ConstantExpression targetExpression; |
|
string memberName; |
|
IList<ITypeReference> typeArguments; |
|
|
|
public ConstantMemberReference(ITypeReference targetType, string memberName, IList<ITypeReference> typeArguments = null) |
|
{ |
|
if (targetType == null) |
|
throw new ArgumentNullException("targetType"); |
|
if (memberName == null) |
|
throw new ArgumentNullException("memberName"); |
|
this.targetType = targetType; |
|
this.memberName = memberName; |
|
this.typeArguments = typeArguments; |
|
} |
|
|
|
public ConstantMemberReference(ConstantExpression targetExpression, string memberName, IList<ITypeReference> typeArguments = null) |
|
{ |
|
if (targetExpression == null) |
|
throw new ArgumentNullException("targetExpression"); |
|
if (memberName == null) |
|
throw new ArgumentNullException("memberName"); |
|
this.targetExpression = targetExpression; |
|
this.memberName = memberName; |
|
this.typeArguments = typeArguments; |
|
} |
|
|
|
public override ResolveResult Resolve(CSharpResolver resolver) |
|
{ |
|
ResolveResult rr; |
|
if (targetType != null) |
|
rr = new TypeResolveResult(targetType.Resolve(resolver.Context)); |
|
else |
|
rr = targetExpression.Resolve(resolver); |
|
return resolver.ResolveMemberAccess(rr, memberName, ConstantIdentifierReference.ResolveTypes(resolver, typeArguments)); |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
targetType = provider.Intern(targetType); |
|
targetExpression = provider.Intern(targetExpression); |
|
memberName = provider.Intern(memberName); |
|
typeArguments = provider.InternList(typeArguments); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
unchecked { |
|
int hashCode; |
|
if (targetType != null) |
|
hashCode = targetType.GetHashCode(); |
|
else |
|
hashCode = targetExpression.GetHashCode(); |
|
hashCode ^= memberName.GetHashCode(); |
|
if (typeArguments != null) |
|
hashCode ^= typeArguments.GetHashCode(); |
|
return hashCode; |
|
} |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
ConstantMemberReference cmr = other as ConstantMemberReference; |
|
return cmr != null |
|
&& this.targetType == cmr.targetType && this.targetExpression == cmr.targetExpression |
|
&& this.memberName == cmr.memberName && this.typeArguments == cmr.typeArguments; |
|
} |
|
} |
|
|
|
public sealed class ConstantCheckedExpression : ConstantExpression, ISupportsInterning |
|
{ |
|
bool checkForOverflow; |
|
ConstantExpression expression; |
|
|
|
public ConstantCheckedExpression(bool checkForOverflow, ConstantExpression expression) |
|
{ |
|
if (expression == null) |
|
throw new ArgumentNullException("expression"); |
|
this.checkForOverflow = checkForOverflow; |
|
this.expression = expression; |
|
} |
|
|
|
public override ResolveResult Resolve(CSharpResolver resolver) |
|
{ |
|
bool oldCheckForOverflow = resolver.CheckForOverflow; |
|
try { |
|
resolver.CheckForOverflow = this.checkForOverflow; |
|
return expression.Resolve(resolver); |
|
} finally { |
|
resolver.CheckForOverflow = oldCheckForOverflow; |
|
} |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
expression = provider.Intern(expression); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
return expression.GetHashCode() ^ (checkForOverflow ? 161851612 : 75163517); |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
ConstantCheckedExpression cce = other as ConstantCheckedExpression; |
|
return cce != null |
|
&& this.expression == cce.expression |
|
&& this.checkForOverflow == cce.checkForOverflow; |
|
} |
|
} |
|
|
|
public sealed class ConstantDefaultValue : ConstantExpression, ISupportsInterning |
|
{ |
|
ITypeReference type; |
|
|
|
public ConstantDefaultValue(ITypeReference type) |
|
{ |
|
if (type == null) |
|
throw new ArgumentNullException("type"); |
|
this.type = type; |
|
} |
|
|
|
public override ResolveResult Resolve(CSharpResolver resolver) |
|
{ |
|
return resolver.ResolveDefaultValue(type.Resolve(resolver.Context)); |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
type = provider.Intern(type); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
return type.GetHashCode(); |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
ConstantDefaultValue o = other as ConstantDefaultValue; |
|
return o != null && this.type == o.type; |
|
} |
|
} |
|
|
|
public sealed class ConstantUnaryOperator : ConstantExpression, ISupportsInterning |
|
{ |
|
UnaryOperatorType operatorType; |
|
ConstantExpression expression; |
|
|
|
public ConstantUnaryOperator(UnaryOperatorType operatorType, ConstantExpression expression) |
|
{ |
|
if (expression == null) |
|
throw new ArgumentNullException("expression"); |
|
this.operatorType = operatorType; |
|
this.expression = expression; |
|
} |
|
|
|
public override ResolveResult Resolve(CSharpResolver resolver) |
|
{ |
|
return resolver.ResolveUnaryOperator(operatorType, expression.Resolve(resolver)); |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
expression = provider.Intern(expression); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
unchecked { |
|
return expression.GetHashCode() * 811 + operatorType.GetHashCode(); |
|
} |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
ConstantUnaryOperator uop = other as ConstantUnaryOperator; |
|
return uop != null |
|
&& this.operatorType == uop.operatorType |
|
&& this.expression == uop.expression; |
|
} |
|
} |
|
|
|
public sealed class ConstantBinaryOperator : ConstantExpression, ISupportsInterning |
|
{ |
|
ConstantExpression left; |
|
BinaryOperatorType operatorType; |
|
ConstantExpression right; |
|
|
|
public ConstantBinaryOperator(ConstantExpression left, BinaryOperatorType operatorType, ConstantExpression right) |
|
{ |
|
if (left == null) |
|
throw new ArgumentNullException("left"); |
|
if (right == null) |
|
throw new ArgumentNullException("right"); |
|
this.left = left; |
|
this.operatorType = operatorType; |
|
this.right = right; |
|
} |
|
|
|
public override ResolveResult Resolve(CSharpResolver resolver) |
|
{ |
|
ResolveResult lhs = left.Resolve(resolver); |
|
ResolveResult rhs = right.Resolve(resolver); |
|
return resolver.ResolveBinaryOperator(operatorType, lhs, rhs); |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
left = provider.Intern(left); |
|
right = provider.Intern(right); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
unchecked { |
|
return left.GetHashCode() * 811 + operatorType.GetHashCode() + right.GetHashCode() * 91781; |
|
} |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
ConstantBinaryOperator bop = other as ConstantBinaryOperator; |
|
return bop != null |
|
&& this.operatorType == bop.operatorType |
|
&& this.left == bop.left && this.right == bop.right; |
|
} |
|
} |
|
|
|
public sealed class ConstantConditionalOperator : ConstantExpression, ISupportsInterning |
|
{ |
|
ConstantExpression condition, trueExpr, falseExpr; |
|
|
|
public ConstantConditionalOperator(ConstantExpression condition, ConstantExpression trueExpr, ConstantExpression falseExpr) |
|
{ |
|
if (condition == null) |
|
throw new ArgumentNullException("condition"); |
|
if (trueExpr == null) |
|
throw new ArgumentNullException("trueExpr"); |
|
if (falseExpr == null) |
|
throw new ArgumentNullException("falseExpr"); |
|
this.condition = condition; |
|
this.trueExpr = trueExpr; |
|
this.falseExpr = falseExpr; |
|
} |
|
|
|
public override ResolveResult Resolve(CSharpResolver resolver) |
|
{ |
|
return resolver.ResolveConditional( |
|
condition.Resolve(resolver), |
|
trueExpr.Resolve(resolver), |
|
falseExpr.Resolve(resolver) |
|
); |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
condition = provider.Intern(condition); |
|
trueExpr = provider.Intern(trueExpr); |
|
falseExpr = provider.Intern(falseExpr); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
unchecked { |
|
return condition.GetHashCode() * 182981713 |
|
+ trueExpr.GetHashCode() * 917517169 |
|
+ falseExpr.GetHashCode() * 611651; |
|
} |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
ConstantConditionalOperator coo = other as ConstantConditionalOperator; |
|
return coo != null |
|
&& this.condition == coo.condition |
|
&& this.trueExpr == coo.trueExpr |
|
&& this.falseExpr == coo.falseExpr; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Represents an array creation (as used within an attribute argument) |
|
/// </summary> |
|
public sealed class ConstantArrayCreation : ConstantExpression, ISupportsInterning |
|
{ |
|
// type may be null when the element is being inferred |
|
ITypeReference type; |
|
IList<ConstantExpression> arrayElements; |
|
|
|
public ConstantArrayCreation(ITypeReference type, IList<ConstantExpression> arrayElements) |
|
{ |
|
if (arrayElements == null) |
|
throw new ArgumentNullException("arrayElements"); |
|
this.type = type; |
|
this.arrayElements = arrayElements; |
|
} |
|
|
|
public override ResolveResult Resolve(CSharpResolver resolver) |
|
{ |
|
throw new NotImplementedException(); |
|
} |
|
|
|
void ISupportsInterning.PrepareForInterning(IInterningProvider provider) |
|
{ |
|
type = provider.Intern(type); |
|
arrayElements = provider.InternList(arrayElements); |
|
} |
|
|
|
int ISupportsInterning.GetHashCodeForInterning() |
|
{ |
|
return (type != null ? type.GetHashCode() : 0) ^ arrayElements.GetHashCode(); |
|
} |
|
|
|
bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) |
|
{ |
|
ConstantArrayCreation cac = other as ConstantArrayCreation; |
|
return cac != null && this.type == cac.type && this.arrayElements == cac.arrayElements; |
|
} |
|
} |
|
}
|
|
|