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

1065 lines
36 KiB

// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team
//
// 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.Linq;
using System.Text;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.CSharp.Resolver
{
sealed class CSharpOperators
{
readonly ICompilation compilation;
private CSharpOperators(ICompilation compilation)
{
this.compilation = compilation;
InitParameterArrays();
}
/// <summary>
/// Gets the CSharpOperators instance for the specified <see cref="ICompilation"/>.
/// This will make use of the context's cache manager (if available) to reuse the CSharpOperators instance.
/// </summary>
public static CSharpOperators Get(ICompilation compilation)
{
CacheManager cache = compilation.CacheManager;
CSharpOperators operators = (CSharpOperators)cache.GetShared(typeof(CSharpOperators));
if (operators == null) {
operators = (CSharpOperators)cache.GetOrAddShared(typeof(CSharpOperators), new CSharpOperators(compilation));
}
return operators;
}
#region class OperatorMethod
OperatorMethod[] Lift(params OperatorMethod[] methods)
{
List<OperatorMethod> result = new List<OperatorMethod>(methods);
foreach (OperatorMethod method in methods) {
OperatorMethod lifted = method.Lift(this);
if (lifted != null)
result.Add(lifted);
}
return result.ToArray();
}
IParameter[] normalParameters = new IParameter[(int)(TypeCode.String + 1 - TypeCode.Object)];
IParameter[] nullableParameters = new IParameter[(int)(TypeCode.Decimal + 1 - TypeCode.Boolean)];
void InitParameterArrays()
{
for (TypeCode i = TypeCode.Object; i <= TypeCode.String; i++) {
normalParameters[i - TypeCode.Object] = new DefaultParameter(compilation.FindType(i), string.Empty);
}
for (TypeCode i = TypeCode.Boolean; i <= TypeCode.Decimal; i++) {
IType type = NullableType.Create(compilation, compilation.FindType(i));
nullableParameters[i - TypeCode.Boolean] = new DefaultParameter(type, string.Empty);
}
}
IParameter MakeParameter(TypeCode code)
{
return normalParameters[code - TypeCode.Object];
}
IParameter MakeNullableParameter(IParameter normalParameter)
{
for (TypeCode i = TypeCode.Boolean; i <= TypeCode.Decimal; i++) {
if (normalParameter == normalParameters[i - TypeCode.Object])
return nullableParameters[i - TypeCode.Boolean];
}
throw new ArgumentException();
}
internal class OperatorMethod : IParameterizedMember
{
readonly ICompilation compilation;
internal readonly List<IParameter> parameters = new List<IParameter>();
protected OperatorMethod(ICompilation compilation)
{
this.compilation = compilation;
}
public IReadOnlyList<IParameter> Parameters {
get { return parameters; }
}
public IType ReturnType { get; internal set; }
public ICompilation Compilation {
get { return compilation; }
}
public virtual OperatorMethod Lift(CSharpOperators operators)
{
return null;
}
public System.Reflection.Metadata.EntityHandle MetadataToken => default;
ITypeDefinition IEntity.DeclaringTypeDefinition {
get { return null; }
}
IType IEntity.DeclaringType {
get { return null; }
}
IMember IMember.MemberDefinition {
get { return this; }
}
IEnumerable<IMember> IMember.ExplicitlyImplementedInterfaceMembers {
get { return EmptyList<IMember>.Instance; }
}
bool IMember.IsVirtual {
get { return false; }
}
bool IMember.IsOverride {
get { return false; }
}
bool IMember.IsOverridable {
get { return false; }
}
SymbolKind ISymbol.SymbolKind {
get { return SymbolKind.Operator; }
}
IEnumerable<IAttribute> IEntity.GetAttributes()
{
return EmptyList<IAttribute>.Instance;
}
Accessibility IEntity.Accessibility {
get { return Accessibility.Public; }
}
bool IEntity.IsStatic {
get { return true; }
}
bool IEntity.IsAbstract {
get { return false; }
}
bool IEntity.IsSealed {
get { return false; }
}
bool IMember.IsExplicitInterfaceImplementation {
get { return false; }
}
IModule IEntity.ParentModule {
get { return compilation.MainModule; }
}
TypeParameterSubstitution IMember.Substitution {
get {
return TypeParameterSubstitution.Identity;
}
}
IMember IMember.Specialize(TypeParameterSubstitution substitution)
{
if (TypeParameterSubstitution.Identity.Equals(substitution))
return this;
throw new NotSupportedException();
}
string INamedElement.FullName {
get { return "operator"; }
}
public string Name {
get { return "operator"; }
}
string INamedElement.Namespace {
get { return string.Empty; }
}
string INamedElement.ReflectionName {
get { return "operator"; }
}
public override string ToString()
{
StringBuilder b = new StringBuilder();
b.Append(ReturnType + " operator(");
for (int i = 0; i < parameters.Count; i++) {
if (i > 0)
b.Append(", ");
b.Append(parameters[i].Type);
}
b.Append(')');
return b.ToString();
}
bool IMember.Equals(IMember obj, TypeVisitor typeNormalization)
{
return this == obj;
}
}
#endregion
#region Unary operator class definitions
internal class UnaryOperatorMethod : OperatorMethod
{
public virtual bool CanEvaluateAtCompileTime { get { return false; } }
public virtual object Invoke(CSharpResolver resolver, object input)
{
throw new NotSupportedException();
}
public UnaryOperatorMethod(ICompilation compilation) : base(compilation)
{
}
}
sealed class LambdaUnaryOperatorMethod<T> : UnaryOperatorMethod
{
readonly Func<T, T> func;
public LambdaUnaryOperatorMethod(CSharpOperators operators, Func<T, T> func)
: base(operators.compilation)
{
TypeCode typeCode = Type.GetTypeCode(typeof(T));
this.ReturnType = operators.compilation.FindType(typeCode);
parameters.Add(operators.MakeParameter(typeCode));
this.func = func;
}
public override bool CanEvaluateAtCompileTime {
get { return true; }
}
public override object Invoke(CSharpResolver resolver, object input)
{
if (input == null)
return null;
return func((T)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T)), input));
}
public override OperatorMethod Lift(CSharpOperators operators)
{
return new LiftedUnaryOperatorMethod(operators, this);
}
}
sealed class LiftedUnaryOperatorMethod : UnaryOperatorMethod, ILiftedOperator
{
UnaryOperatorMethod baseMethod;
public LiftedUnaryOperatorMethod(CSharpOperators operators, UnaryOperatorMethod baseMethod) : base(operators.compilation)
{
this.baseMethod = baseMethod;
this.ReturnType = NullableType.Create(baseMethod.Compilation, baseMethod.ReturnType);
parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[0]));
}
public IReadOnlyList<IParameter> NonLiftedParameters => baseMethod.Parameters;
public IType NonLiftedReturnType => baseMethod.ReturnType;
}
#endregion
#region Unary operator definitions
// C# 4.0 spec: §7.7.1 Unary plus operator
OperatorMethod[] unaryPlusOperators;
public OperatorMethod[] UnaryPlusOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref unaryPlusOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref unaryPlusOperators, Lift(
new LambdaUnaryOperatorMethod<int> (this, i => +i),
new LambdaUnaryOperatorMethod<uint> (this, i => +i),
new LambdaUnaryOperatorMethod<long> (this, i => +i),
new LambdaUnaryOperatorMethod<ulong> (this, i => +i),
new LambdaUnaryOperatorMethod<float> (this, i => +i),
new LambdaUnaryOperatorMethod<double> (this, i => +i),
new LambdaUnaryOperatorMethod<decimal>(this, i => +i)
));
}
}
}
// C# 4.0 spec: §7.7.2 Unary minus operator
OperatorMethod[] uncheckedUnaryMinusOperators;
public OperatorMethod[] UncheckedUnaryMinusOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref uncheckedUnaryMinusOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref uncheckedUnaryMinusOperators, Lift(
new LambdaUnaryOperatorMethod<int> (this, i => unchecked(-i)),
new LambdaUnaryOperatorMethod<long> (this, i => unchecked(-i)),
new LambdaUnaryOperatorMethod<float> (this, i => unchecked(-i)),
new LambdaUnaryOperatorMethod<double> (this, i => unchecked(-i)),
new LambdaUnaryOperatorMethod<decimal>(this, i => unchecked(-i))
));
}
}
}
OperatorMethod[] checkedUnaryMinusOperators;
public OperatorMethod[] CheckedUnaryMinusOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref checkedUnaryMinusOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref checkedUnaryMinusOperators, Lift(
new LambdaUnaryOperatorMethod<int> (this, i => checked(-i)),
new LambdaUnaryOperatorMethod<long> (this, i => checked(-i)),
new LambdaUnaryOperatorMethod<float> (this, i => checked(-i)),
new LambdaUnaryOperatorMethod<double> (this, i => checked(-i)),
new LambdaUnaryOperatorMethod<decimal>(this, i => checked(-i))
));
}
}
}
// C# 4.0 spec: §7.7.3 Logical negation operator
OperatorMethod[] logicalNegationOperators;
public OperatorMethod[] LogicalNegationOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref logicalNegationOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref logicalNegationOperators, Lift(
new LambdaUnaryOperatorMethod<bool>(this, b => !b)
));
}
}
}
// C# 4.0 spec: §7.7.4 Bitwise complement operator
OperatorMethod[] bitwiseComplementOperators;
public OperatorMethod[] BitwiseComplementOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref bitwiseComplementOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref bitwiseComplementOperators, Lift(
new LambdaUnaryOperatorMethod<int> (this, i => ~i),
new LambdaUnaryOperatorMethod<uint> (this, i => ~i),
new LambdaUnaryOperatorMethod<long> (this, i => ~i),
new LambdaUnaryOperatorMethod<ulong>(this, i => ~i)
));
}
}
}
#endregion
#region Binary operator class definitions
internal class BinaryOperatorMethod : OperatorMethod
{
public virtual bool CanEvaluateAtCompileTime { get { return false; } }
public virtual object Invoke(CSharpResolver resolver, object lhs, object rhs) {
throw new NotSupportedException();
}
public BinaryOperatorMethod(ICompilation compilation) : base(compilation) {}
}
sealed class LambdaBinaryOperatorMethod<T1, T2> : BinaryOperatorMethod
{
readonly Func<T1, T2, T1> checkedFunc;
readonly Func<T1, T2, T1> uncheckedFunc;
public LambdaBinaryOperatorMethod(CSharpOperators operators, Func<T1, T2, T1> func)
: this(operators, func, func)
{
}
public LambdaBinaryOperatorMethod(CSharpOperators operators, Func<T1, T2, T1> checkedFunc, Func<T1, T2, T1> uncheckedFunc)
: base(operators.compilation)
{
TypeCode t1 = Type.GetTypeCode(typeof(T1));
this.ReturnType = operators.compilation.FindType(t1);
parameters.Add(operators.MakeParameter(t1));
parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T2))));
this.checkedFunc = checkedFunc;
this.uncheckedFunc = uncheckedFunc;
}
public override bool CanEvaluateAtCompileTime {
get { return true; }
}
public override object Invoke(CSharpResolver resolver, object lhs, object rhs)
{
if (lhs == null || rhs == null)
return null;
Func<T1, T2, T1> func = resolver.CheckForOverflow ? checkedFunc : uncheckedFunc;
return func((T1)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T1)), lhs),
(T2)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T2)), rhs));
}
public override OperatorMethod Lift(CSharpOperators operators)
{
return new LiftedBinaryOperatorMethod(operators, this);
}
}
sealed class LiftedBinaryOperatorMethod : BinaryOperatorMethod, ILiftedOperator
{
readonly BinaryOperatorMethod baseMethod;
public LiftedBinaryOperatorMethod(CSharpOperators operators, BinaryOperatorMethod baseMethod)
: base(operators.compilation)
{
this.baseMethod = baseMethod;
this.ReturnType = NullableType.Create(operators.compilation, baseMethod.ReturnType);
parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[0]));
parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[1]));
}
public IReadOnlyList<IParameter> NonLiftedParameters => baseMethod.Parameters;
public IType NonLiftedReturnType => baseMethod.ReturnType;
}
#endregion
#region Arithmetic operators
// C# 4.0 spec: §7.8.1 Multiplication operator
OperatorMethod[] multiplicationOperators;
public OperatorMethod[] MultiplicationOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref multiplicationOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref multiplicationOperators, Lift(
new LambdaBinaryOperatorMethod<int, int> (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<uint, uint> (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<long, long> (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<ulong, ulong> (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<float, float> (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<double, double> (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)),
new LambdaBinaryOperatorMethod<decimal, decimal>(this, (a, b) => checked(a * b), (a, b) => unchecked(a * b))
));
}
}
}
// C# 4.0 spec: §7.8.2 Division operator
OperatorMethod[] divisionOperators;
public OperatorMethod[] DivisionOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref divisionOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref divisionOperators, Lift(
new LambdaBinaryOperatorMethod<int, int> (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<uint, uint> (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<long, long> (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<ulong, ulong> (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<float, float> (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<double, double> (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)),
new LambdaBinaryOperatorMethod<decimal, decimal>(this, (a, b) => checked(a / b), (a, b) => unchecked(a / b))
));
}
}
}
// C# 4.0 spec: §7.8.3 Remainder operator
OperatorMethod[] remainderOperators;
public OperatorMethod[] RemainderOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref remainderOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref remainderOperators, Lift(
new LambdaBinaryOperatorMethod<int, int> (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<uint, uint> (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<long, long> (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<ulong, ulong> (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<float, float> (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<double, double> (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)),
new LambdaBinaryOperatorMethod<decimal, decimal>(this, (a, b) => checked(a % b), (a, b) => unchecked(a % b))
));
}
}
}
// C# 4.0 spec: §7.8.3 Addition operator
OperatorMethod[] additionOperators;
public OperatorMethod[] AdditionOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref additionOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref additionOperators, Lift(
new LambdaBinaryOperatorMethod<int, int> (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<uint, uint> (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<long, long> (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<ulong, ulong> (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<float, float> (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<double, double> (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)),
new LambdaBinaryOperatorMethod<decimal, decimal>(this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)),
new StringConcatenation(this, TypeCode.String, TypeCode.String),
new StringConcatenation(this, TypeCode.String, TypeCode.Object),
new StringConcatenation(this, TypeCode.Object, TypeCode.String)
));
}
}
}
// not in this list, but handled manually: enum addition, delegate combination
sealed class StringConcatenation : BinaryOperatorMethod
{
bool canEvaluateAtCompileTime;
public StringConcatenation(CSharpOperators operators, TypeCode p1, TypeCode p2)
: base(operators.compilation)
{
this.canEvaluateAtCompileTime = p1 == TypeCode.String && p2 == TypeCode.String;
this.ReturnType = operators.compilation.FindType(KnownTypeCode.String);
parameters.Add(operators.MakeParameter(p1));
parameters.Add(operators.MakeParameter(p2));
}
public override bool CanEvaluateAtCompileTime {
get { return canEvaluateAtCompileTime; }
}
public override object Invoke(CSharpResolver resolver, object lhs, object rhs)
{
return string.Concat(lhs, rhs);
}
}
// C# 4.0 spec: §7.8.4 Subtraction operator
OperatorMethod[] subtractionOperators;
public OperatorMethod[] SubtractionOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref subtractionOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref subtractionOperators, Lift(
new LambdaBinaryOperatorMethod<int, int> (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)),
new LambdaBinaryOperatorMethod<uint, uint> (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)),
new LambdaBinaryOperatorMethod<long, long> (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)),
new LambdaBinaryOperatorMethod<ulong, ulong> (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)),
new LambdaBinaryOperatorMethod<float, float> (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)),
new LambdaBinaryOperatorMethod<double, double> (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)),
new LambdaBinaryOperatorMethod<decimal, decimal>(this, (a, b) => checked(a - b), (a, b) => unchecked(a - b))
));
}
}
}
// C# 4.0 spec: §7.8.5 Shift operators
OperatorMethod[] shiftLeftOperators;
public OperatorMethod[] ShiftLeftOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref shiftLeftOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref shiftLeftOperators, Lift(
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => a << b),
new LambdaBinaryOperatorMethod<uint, int>(this, (a, b) => a << b),
new LambdaBinaryOperatorMethod<long, int>(this, (a, b) => a << b),
new LambdaBinaryOperatorMethod<ulong, int>(this, (a, b) => a << b)
));
}
}
}
OperatorMethod[] shiftRightOperators;
public OperatorMethod[] ShiftRightOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref shiftRightOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref shiftRightOperators, Lift(
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => a >> b),
new LambdaBinaryOperatorMethod<uint, int>(this, (a, b) => a >> b),
new LambdaBinaryOperatorMethod<long, int>(this, (a, b) => a >> b),
new LambdaBinaryOperatorMethod<ulong, int>(this, (a, b) => a >> b)
));
}
}
}
#endregion
#region Equality operators
sealed class EqualityOperatorMethod : BinaryOperatorMethod
{
public readonly TypeCode Type;
public readonly bool Negate;
public EqualityOperatorMethod(CSharpOperators operators, TypeCode type, bool negate)
: base(operators.compilation)
{
this.Negate = negate;
this.Type = type;
this.ReturnType = operators.compilation.FindType(KnownTypeCode.Boolean);
parameters.Add(operators.MakeParameter(type));
parameters.Add(operators.MakeParameter(type));
}
public override bool CanEvaluateAtCompileTime {
get { return Type != TypeCode.Object; }
}
public override object Invoke(CSharpResolver resolver, object lhs, object rhs)
{
if (lhs == null && rhs == null)
return !Negate; // ==: true; !=: false
if (lhs == null || rhs == null)
return Negate; // ==: false; !=: true
lhs = resolver.CSharpPrimitiveCast(Type, lhs);
rhs = resolver.CSharpPrimitiveCast(Type, rhs);
bool equal;
if (Type == TypeCode.Single) {
equal = (float)lhs == (float)rhs;
} else if (Type == TypeCode.Double) {
equal = (double)lhs == (double)rhs;
} else {
equal = object.Equals(lhs, rhs);
}
return equal ^ Negate;
}
public override OperatorMethod Lift(CSharpOperators operators)
{
if (Type == TypeCode.Object || Type == TypeCode.String)
return null;
else
return new LiftedEqualityOperatorMethod(operators, this);
}
}
sealed class LiftedEqualityOperatorMethod : BinaryOperatorMethod, ILiftedOperator
{
readonly EqualityOperatorMethod baseMethod;
public LiftedEqualityOperatorMethod(CSharpOperators operators, EqualityOperatorMethod baseMethod)
: base(operators.compilation)
{
this.baseMethod = baseMethod;
this.ReturnType = baseMethod.ReturnType;
IParameter p = operators.MakeNullableParameter(baseMethod.Parameters[0]);
parameters.Add(p);
parameters.Add(p);
}
public override bool CanEvaluateAtCompileTime {
get { return baseMethod.CanEvaluateAtCompileTime; }
}
public override object Invoke(CSharpResolver resolver, object lhs, object rhs)
{
return baseMethod.Invoke(resolver, lhs, rhs);
}
public IReadOnlyList<IParameter> NonLiftedParameters => baseMethod.Parameters;
public IType NonLiftedReturnType => baseMethod.ReturnType;
}
// C# 4.0 spec: §7.10 Relational and type-testing operators
static readonly TypeCode[] valueEqualityOperatorsFor = {
TypeCode.Int32, TypeCode.UInt32,
TypeCode.Int64, TypeCode.UInt64,
TypeCode.Single, TypeCode.Double,
TypeCode.Decimal,
TypeCode.Boolean
};
OperatorMethod[] valueEqualityOperators;
public OperatorMethod[] ValueEqualityOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref valueEqualityOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref valueEqualityOperators, Lift(
valueEqualityOperatorsFor.Select(c => new EqualityOperatorMethod(this, c, false)).ToArray()
));
}
}
}
OperatorMethod[] valueInequalityOperators;
public OperatorMethod[] ValueInequalityOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref valueInequalityOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref valueInequalityOperators, Lift(
valueEqualityOperatorsFor.Select(c => new EqualityOperatorMethod(this, c, true)).ToArray()
));
}
}
}
OperatorMethod[] referenceEqualityOperators;
public OperatorMethod[] ReferenceEqualityOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref referenceEqualityOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref referenceEqualityOperators, Lift(
new EqualityOperatorMethod(this, TypeCode.Object, false),
new EqualityOperatorMethod(this, TypeCode.String, false)
));
}
}
}
OperatorMethod[] referenceInequalityOperators;
public OperatorMethod[] ReferenceInequalityOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref referenceInequalityOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref referenceInequalityOperators, Lift(
new EqualityOperatorMethod(this, TypeCode.Object, true),
new EqualityOperatorMethod(this, TypeCode.String, true)
));
}
}
}
#endregion
#region Relational Operators
sealed class RelationalOperatorMethod<T1, T2> : BinaryOperatorMethod
{
readonly Func<T1, T2, bool> func;
public RelationalOperatorMethod(CSharpOperators operators, Func<T1, T2, bool> func)
: base(operators.compilation)
{
this.ReturnType = operators.compilation.FindType(KnownTypeCode.Boolean);
parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T1))));
parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T2))));
this.func = func;
}
public override bool CanEvaluateAtCompileTime {
get { return true; }
}
public override object Invoke(CSharpResolver resolver, object lhs, object rhs)
{
if (lhs == null || rhs == null)
return null;
return func((T1)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T1)), lhs),
(T2)resolver.CSharpPrimitiveCast(Type.GetTypeCode(typeof(T2)), rhs));
}
public override OperatorMethod Lift(CSharpOperators operators)
{
var lifted = new LiftedBinaryOperatorMethod(operators, this);
lifted.ReturnType = this.ReturnType; // don't lift the return type for relational operators
return lifted;
}
}
OperatorMethod[] lessThanOperators;
public OperatorMethod[] LessThanOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref lessThanOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref lessThanOperators, Lift(
new RelationalOperatorMethod<int, int> (this, (a, b) => a < b),
new RelationalOperatorMethod<uint, uint> (this, (a, b) => a < b),
new RelationalOperatorMethod<long, long> (this, (a, b) => a < b),
new RelationalOperatorMethod<ulong, ulong> (this, (a, b) => a < b),
new RelationalOperatorMethod<float, float> (this, (a, b) => a < b),
new RelationalOperatorMethod<double, double> (this, (a, b) => a < b),
new RelationalOperatorMethod<decimal, decimal>(this, (a, b) => a < b)
));
}
}
}
OperatorMethod[] lessThanOrEqualOperators;
public OperatorMethod[] LessThanOrEqualOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref lessThanOrEqualOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref lessThanOrEqualOperators, Lift(
new RelationalOperatorMethod<int, int> (this, (a, b) => a <= b),
new RelationalOperatorMethod<uint, uint> (this, (a, b) => a <= b),
new RelationalOperatorMethod<long, long> (this, (a, b) => a <= b),
new RelationalOperatorMethod<ulong, ulong> (this, (a, b) => a <= b),
new RelationalOperatorMethod<float, float> (this, (a, b) => a <= b),
new RelationalOperatorMethod<double, double> (this, (a, b) => a <= b),
new RelationalOperatorMethod<decimal, decimal>(this, (a, b) => a <= b)
));
}
}
}
OperatorMethod[] greaterThanOperators;
public OperatorMethod[] GreaterThanOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref greaterThanOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref greaterThanOperators, Lift(
new RelationalOperatorMethod<int, int> (this, (a, b) => a > b),
new RelationalOperatorMethod<uint, uint> (this, (a, b) => a > b),
new RelationalOperatorMethod<long, long> (this, (a, b) => a > b),
new RelationalOperatorMethod<ulong, ulong> (this, (a, b) => a > b),
new RelationalOperatorMethod<float, float> (this, (a, b) => a > b),
new RelationalOperatorMethod<double, double> (this, (a, b) => a > b),
new RelationalOperatorMethod<decimal, decimal>(this, (a, b) => a > b)
));
}
}
}
OperatorMethod[] greaterThanOrEqualOperators;
public OperatorMethod[] GreaterThanOrEqualOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref greaterThanOrEqualOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref greaterThanOrEqualOperators, Lift(
new RelationalOperatorMethod<int, int> (this, (a, b) => a >= b),
new RelationalOperatorMethod<uint, uint> (this, (a, b) => a >= b),
new RelationalOperatorMethod<long, long> (this, (a, b) => a >= b),
new RelationalOperatorMethod<ulong, ulong> (this, (a, b) => a >= b),
new RelationalOperatorMethod<float, float> (this, (a, b) => a >= b),
new RelationalOperatorMethod<double, double> (this, (a, b) => a >= b),
new RelationalOperatorMethod<decimal, decimal>(this, (a, b) => a >= b)
));
}
}
}
#endregion
#region Bitwise operators
OperatorMethod[] logicalAndOperators;
public OperatorMethod[] LogicalAndOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref logicalAndOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref logicalAndOperators, new OperatorMethod[] {
new LambdaBinaryOperatorMethod<bool, bool>(this, (a, b) => a & b)
});
}
}
}
OperatorMethod[] bitwiseAndOperators;
public OperatorMethod[] BitwiseAndOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref bitwiseAndOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref bitwiseAndOperators, Lift(
new LambdaBinaryOperatorMethod<int, int> (this, (a, b) => a & b),
new LambdaBinaryOperatorMethod<uint, uint> (this, (a, b) => a & b),
new LambdaBinaryOperatorMethod<long, long> (this, (a, b) => a & b),
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => a & b),
this.LogicalAndOperators[0]
));
}
}
}
OperatorMethod[] logicalOrOperators;
public OperatorMethod[] LogicalOrOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref logicalOrOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref logicalOrOperators, new OperatorMethod[] {
new LambdaBinaryOperatorMethod<bool, bool>(this, (a, b) => a | b)
});
}
}
}
OperatorMethod[] bitwiseOrOperators;
public OperatorMethod[] BitwiseOrOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref bitwiseOrOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref bitwiseOrOperators, Lift(
new LambdaBinaryOperatorMethod<int, int> (this, (a, b) => a | b),
new LambdaBinaryOperatorMethod<uint, uint> (this, (a, b) => a | b),
new LambdaBinaryOperatorMethod<long, long> (this, (a, b) => a | b),
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => a | b),
this.LogicalOrOperators[0]
));
}
}
}
// Note: the logic for the lifted bool? bitwise operators is wrong;
// we produce "true | null" = "null" when it should be true. However, this is irrelevant
// because bool? cannot be a compile-time type.
OperatorMethod[] bitwiseXorOperators;
public OperatorMethod[] BitwiseXorOperators {
get {
OperatorMethod[] ops = LazyInit.VolatileRead(ref bitwiseXorOperators);
if (ops != null) {
return ops;
} else {
return LazyInit.GetOrSet(ref bitwiseXorOperators, Lift(
new LambdaBinaryOperatorMethod<int, int> (this, (a, b) => a ^ b),
new LambdaBinaryOperatorMethod<uint, uint> (this, (a, b) => a ^ b),
new LambdaBinaryOperatorMethod<long, long> (this, (a, b) => a ^ b),
new LambdaBinaryOperatorMethod<ulong, ulong>(this, (a, b) => a ^ b),
new LambdaBinaryOperatorMethod<bool, bool> (this, (a, b) => a ^ b)
));
}
}
}
#endregion
#region User-defined operators
public static IMethod LiftUserDefinedOperator(IMethod m)
{
if (IsComparisonOperator(m)) {
if (!m.ReturnType.IsKnownType(KnownTypeCode.Boolean))
return null; // cannot lift this operator
} else {
if (!NullableType.IsNonNullableValueType(m.ReturnType))
return null; // cannot lift this operator
}
for (int i = 0; i < m.Parameters.Count; i++) {
if (!NullableType.IsNonNullableValueType(m.Parameters[i].Type))
return null; // cannot lift this operator
}
return new LiftedUserDefinedOperator(m);
}
internal static bool IsComparisonOperator(IMethod m)
{
return m.IsOperator && m.Parameters.Count == 2
&& (OperatorDeclaration.GetOperatorType(m.Name)?.IsComparisonOperator() ?? false);
}
sealed class LiftedUserDefinedOperator : SpecializedMethod, ILiftedOperator
{
internal readonly IParameterizedMember nonLiftedOperator;
public LiftedUserDefinedOperator(IMethod nonLiftedMethod)
: base((IMethod)nonLiftedMethod.MemberDefinition, nonLiftedMethod.Substitution)
{
this.nonLiftedOperator = nonLiftedMethod;
var compilation = nonLiftedMethod.Compilation;
var substitution = nonLiftedMethod.Substitution;
this.Parameters = base.CreateParameters(
type => NullableType.Create(compilation, type.AcceptVisitor(substitution)));
// Comparison operators keep the 'bool' return type even when lifted.
if (IsComparisonOperator(nonLiftedMethod))
this.ReturnType = nonLiftedMethod.ReturnType;
else
this.ReturnType = NullableType.Create(compilation, nonLiftedMethod.ReturnType.AcceptVisitor(substitution));
}
public IReadOnlyList<IParameter> NonLiftedParameters => nonLiftedOperator.Parameters;
public IType NonLiftedReturnType => nonLiftedOperator.ReturnType;
public override bool Equals(object obj)
{
LiftedUserDefinedOperator op = obj as LiftedUserDefinedOperator;
return op != null && this.nonLiftedOperator.Equals(op.nonLiftedOperator);
}
public override int GetHashCode()
{
return nonLiftedOperator.GetHashCode() ^ 0x7191254;
}
}
#endregion
}
/// <summary>
/// Implement this interface to give overload resolution a hint that the member represents a lifted operator,
/// which is used in the tie-breaking rules.
/// </summary>
public interface ILiftedOperator : IParameterizedMember
{
IType NonLiftedReturnType { get; }
IReadOnlyList<IParameter> NonLiftedParameters { get; }
}
}