// 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);
}
///
/// C#'s equivalent to the SimpleConstantValue.
///
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 typeArguments;
public ConstantIdentifierReference(string identifier, IList 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 ResolveTypes(CSharpResolver resolver, IList typeArguments)
{
if (typeArguments == null)
return EmptyList.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 typeArguments;
public ConstantMemberReference(ITypeReference targetType, string memberName, IList 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 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;
}
}
///
/// Represents an array creation (as used within an attribute argument)
///
public sealed class ConstantArrayCreation : ConstantExpression, ISupportsInterning
{
// type may be null when the element is being inferred
ITypeReference type;
IList arrayElements;
public ConstantArrayCreation(ITypeReference type, IList 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;
}
}
}