// 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.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.CSharp.Resolver { sealed class CSharpOperators { readonly ICompilation compilation; private CSharpOperators(ICompilation compilation) { this.compilation = compilation; InitParameterArrays(); } /// /// Gets the CSharpOperators instance for the specified . /// This will make use of the context's cache manager (if available) to reuse the CSharpOperators instance. /// 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 result = new List(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; readonly IList parameters = new List(); protected OperatorMethod(ICompilation compilation) { this.compilation = compilation; } public IList Parameters { get { return parameters; } } public IType ReturnType { get; internal set; } public ICompilation Compilation { get { return compilation; } } public virtual OperatorMethod Lift(CSharpOperators operators) { return null; } ITypeDefinition IEntity.DeclaringTypeDefinition { get { return null; } } IType IEntity.DeclaringType { get { return SpecialType.UnknownType; } } IMember IMember.MemberDefinition { get { return this; } } IUnresolvedMember IMember.UnresolvedMember { get { return null; } } IList IMember.ImplementedInterfaceMembers { get { return EmptyList.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; } } [Obsolete("Use the SymbolKind property instead.")] EntityType IEntity.EntityType { get { return EntityType.Operator; } } DomRegion IEntity.Region { get { return DomRegion.Empty; } } DomRegion IEntity.BodyRegion { get { return DomRegion.Empty; } } IList IEntity.Attributes { get { return EmptyList.Instance; } } Documentation.DocumentationComment IEntity.Documentation { get { return null; } } Accessibility IHasAccessibility.Accessibility { get { return Accessibility.Public; } } bool IEntity.IsStatic { get { return true; } } bool IEntity.IsAbstract { get { return false; } } bool IEntity.IsSealed { get { return false; } } bool IEntity.IsShadowing { get { return false; } } bool IEntity.IsSynthetic { get { return true; } } bool IHasAccessibility.IsPrivate { get { return false; } } bool IHasAccessibility.IsPublic { get { return true; } } bool IHasAccessibility.IsProtected { get { return false; } } bool IHasAccessibility.IsInternal { get { return false; } } bool IHasAccessibility.IsProtectedOrInternal { get { return false; } } bool IHasAccessibility.IsProtectedAndInternal { get { return false; } } bool IMember.IsExplicitInterfaceImplementation { get { return false; } } IAssembly IEntity.ParentAssembly { get { return compilation.MainAssembly; } } IMemberReference IMember.ToMemberReference() { throw new NotSupportedException(); } ISymbolReference ISymbol.ToReference() { throw new NotSupportedException(); } IMemberReference IMember.ToReference() { throw new NotSupportedException(); } 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(); } } #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 compilaton) : base(compilaton) { } } sealed class LambdaUnaryOperatorMethod : UnaryOperatorMethod { readonly Func func; public LambdaUnaryOperatorMethod(CSharpOperators operators, Func func) : base(operators.compilation) { TypeCode typeCode = Type.GetTypeCode(typeof(T)); this.ReturnType = operators.compilation.FindType(typeCode); this.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, OverloadResolution.ILiftedOperator { UnaryOperatorMethod baseMethod; public LiftedUnaryOperatorMethod(CSharpOperators operators, UnaryOperatorMethod baseMethod) : base(operators.compilation) { this.baseMethod = baseMethod; this.ReturnType = NullableType.Create(baseMethod.Compilation, baseMethod.ReturnType); this.Parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[0])); } public IList NonLiftedParameters { get { return baseMethod.Parameters; } } } #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 (this, i => +i), new LambdaUnaryOperatorMethod (this, i => +i), new LambdaUnaryOperatorMethod (this, i => +i), new LambdaUnaryOperatorMethod (this, i => +i), new LambdaUnaryOperatorMethod (this, i => +i), new LambdaUnaryOperatorMethod (this, i => +i), new LambdaUnaryOperatorMethod(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 (this, i => unchecked(-i)), new LambdaUnaryOperatorMethod (this, i => unchecked(-i)), new LambdaUnaryOperatorMethod (this, i => unchecked(-i)), new LambdaUnaryOperatorMethod (this, i => unchecked(-i)), new LambdaUnaryOperatorMethod(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 (this, i => checked(-i)), new LambdaUnaryOperatorMethod (this, i => checked(-i)), new LambdaUnaryOperatorMethod (this, i => checked(-i)), new LambdaUnaryOperatorMethod (this, i => checked(-i)), new LambdaUnaryOperatorMethod(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(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 (this, i => ~i), new LambdaUnaryOperatorMethod (this, i => ~i), new LambdaUnaryOperatorMethod (this, i => ~i), new LambdaUnaryOperatorMethod(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 : BinaryOperatorMethod { readonly Func checkedFunc; readonly Func uncheckedFunc; public LambdaBinaryOperatorMethod(CSharpOperators operators, Func func) : this(operators, func, func) { } public LambdaBinaryOperatorMethod(CSharpOperators operators, Func checkedFunc, Func uncheckedFunc) : base(operators.compilation) { TypeCode t1 = Type.GetTypeCode(typeof(T1)); this.ReturnType = operators.compilation.FindType(t1); this.Parameters.Add(operators.MakeParameter(t1)); this.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 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, OverloadResolution.ILiftedOperator { readonly BinaryOperatorMethod baseMethod; public LiftedBinaryOperatorMethod(CSharpOperators operators, BinaryOperatorMethod baseMethod) : base(operators.compilation) { this.baseMethod = baseMethod; this.ReturnType = NullableType.Create(operators.compilation, baseMethod.ReturnType); this.Parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[0])); this.Parameters.Add(operators.MakeNullableParameter(baseMethod.Parameters[1])); } public IList NonLiftedParameters { get { return baseMethod.Parameters; } } } #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 (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a * b), (a, b) => unchecked(a * b)), new LambdaBinaryOperatorMethod(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 (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a / b), (a, b) => unchecked(a / b)), new LambdaBinaryOperatorMethod(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 (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a % b), (a, b) => unchecked(a % b)), new LambdaBinaryOperatorMethod(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 (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a + b), (a, b) => unchecked(a + b)), new LambdaBinaryOperatorMethod(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); this.Parameters.Add(operators.MakeParameter(p1)); this.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 (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod (this, (a, b) => checked(a - b), (a, b) => unchecked(a - b)), new LambdaBinaryOperatorMethod(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(this, (a, b) => a << b), new LambdaBinaryOperatorMethod(this, (a, b) => a << b), new LambdaBinaryOperatorMethod(this, (a, b) => a << b), new LambdaBinaryOperatorMethod(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(this, (a, b) => a >> b), new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), new LambdaBinaryOperatorMethod(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); this.Parameters.Add(operators.MakeParameter(type)); this.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, OverloadResolution.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]); this.Parameters.Add(p); this.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 IList NonLiftedParameters { get { return baseMethod.Parameters; } } } // 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 : BinaryOperatorMethod { readonly Func func; public RelationalOperatorMethod(CSharpOperators operators, Func func) : base(operators.compilation) { this.ReturnType = operators.compilation.FindType(KnownTypeCode.Boolean); this.Parameters.Add(operators.MakeParameter(Type.GetTypeCode(typeof(T1)))); this.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 (this, (a, b) => a < b), new RelationalOperatorMethod (this, (a, b) => a < b), new RelationalOperatorMethod (this, (a, b) => a < b), new RelationalOperatorMethod (this, (a, b) => a < b), new RelationalOperatorMethod (this, (a, b) => a < b), new RelationalOperatorMethod (this, (a, b) => a < b), new RelationalOperatorMethod(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 (this, (a, b) => a <= b), new RelationalOperatorMethod (this, (a, b) => a <= b), new RelationalOperatorMethod (this, (a, b) => a <= b), new RelationalOperatorMethod (this, (a, b) => a <= b), new RelationalOperatorMethod (this, (a, b) => a <= b), new RelationalOperatorMethod (this, (a, b) => a <= b), new RelationalOperatorMethod(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 (this, (a, b) => a > b), new RelationalOperatorMethod (this, (a, b) => a > b), new RelationalOperatorMethod (this, (a, b) => a > b), new RelationalOperatorMethod (this, (a, b) => a > b), new RelationalOperatorMethod (this, (a, b) => a > b), new RelationalOperatorMethod (this, (a, b) => a > b), new RelationalOperatorMethod(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 (this, (a, b) => a >= b), new RelationalOperatorMethod (this, (a, b) => a >= b), new RelationalOperatorMethod (this, (a, b) => a >= b), new RelationalOperatorMethod (this, (a, b) => a >= b), new RelationalOperatorMethod (this, (a, b) => a >= b), new RelationalOperatorMethod (this, (a, b) => a >= b), new RelationalOperatorMethod(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(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 (this, (a, b) => a & b), new LambdaBinaryOperatorMethod (this, (a, b) => a & b), new LambdaBinaryOperatorMethod (this, (a, b) => a & b), new LambdaBinaryOperatorMethod(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(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 (this, (a, b) => a | b), new LambdaBinaryOperatorMethod (this, (a, b) => a | b), new LambdaBinaryOperatorMethod (this, (a, b) => a | b), new LambdaBinaryOperatorMethod(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 (this, (a, b) => a ^ b), new LambdaBinaryOperatorMethod (this, (a, b) => a ^ b), new LambdaBinaryOperatorMethod (this, (a, b) => a ^ b), new LambdaBinaryOperatorMethod(this, (a, b) => a ^ b), new LambdaBinaryOperatorMethod (this, (a, b) => a ^ b) )); } } } #endregion } }