' This library is free software; you can redistribute it and/or
' modify it under the terms of the GNU Lesser General Public License
' as published by the Free Software Foundation; either version 2.1
' of the License, or (at your option) any later version.
'
' This library is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
' Lesser General Public License for more details.
'
' You should have received a copy of the GNU Lesser General Public
' License along with this library; if not, write to the Free
' Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
' MA 02111-1307, USA.
'
' Flee - Fast Lightweight Expression Evaluator
' Copyright � 2007 Eugene Ciloci
'
Imports System.Reflection.Emit
Imports System.Reflection
Imports System.ComponentModel.Design
' Element that compares values and generates a boolean result
Friend Class CompareElement
Inherits BinaryExpressionElement
Private MyOperation As LogicalCompareOperation
Public Sub New()
End Sub
Protected Overrides Sub GetOperation(ByVal operation As Object)
MyOperation = DirectCast(operation, LogicalCompareOperation)
End Sub
Protected Overrides Function GetResultType(ByVal leftType As System.Type, ByVal rightType As System.Type) As System.Type
Dim binaryResultType As Type = ImplicitConverter.GetBinaryResultType(leftType, rightType)
Dim overloadedOperator As MethodInfo = Me.GetOverloadedCompareOperator()
' Use our string equality instead of overloaded operator
If leftType Is GetType(String) And rightType Is GetType(String) And Me.IsOpTypeEqualOrNotEqual(MyOperation) = True Then
' String equality
Return GetType(Boolean)
ElseIf Not overloadedOperator Is Nothing Then
Return overloadedOperator.ReturnType
ElseIf Not binaryResultType Is Nothing Then
' Comparison of numeric operands
Return GetType(Boolean)
ElseIf leftType Is GetType(Boolean) And rightType Is GetType(Boolean) And Me.IsOpTypeEqualOrNotEqual(MyOperation) = True Then
' Boolean equality
Return GetType(Boolean)
Else
' Invalid operands
Return Nothing
End If
End Function
Private Function GetOverloadedCompareOperator() As MethodInfo
Dim name As String = Me.GetCompareOperatorName(MyOperation)
Return MyBase.GetOverloadedBinaryOperator(name, MyOperation)
End Function
Private Function GetCompareOperatorName(ByVal op As LogicalCompareOperation) As String
Select Case op
Case LogicalCompareOperation.Equal
Return "Equality"
Case LogicalCompareOperation.NotEqual
Return "Inequality"
Case LogicalCompareOperation.GreaterThan
Return "GreaterThan"
Case LogicalCompareOperation.LessThan
Return "LessThan"
Case LogicalCompareOperation.GreaterThanOrEqual
Return "GreaterThanOrEqual"
Case LogicalCompareOperation.LessThanOrEqual
Return "LessThanOrEqual"
Case Else
Debug.Assert(False, "unknown compare type")
Return Nothing
End Select
End Function
Public Overrides Sub Emit(ByVal ilg As System.Reflection.Emit.ILGenerator, ByVal services As IServiceContainer)
Dim binaryResultType As Type = ImplicitConverter.GetBinaryResultType(MyLeftChild.ResultType, MyRightChild.ResultType)
Dim overloadedOperator As MethodInfo = Me.GetOverloadedCompareOperator()
If Me.AreBothChildrenOfType(GetType(String)) Then
' String equality
MyLeftChild.Emit(ilg, services)
MyRightChild.Emit(ilg, services)
Me.EmitStringEquality(ilg, MyOperation, services)
ElseIf Not overloadedOperator Is Nothing Then
MyBase.EmitOverloadedOperatorCall(overloadedOperator, ilg, services)
ElseIf Not binaryResultType Is Nothing Then
' Emit a compare of numeric operands
Me.EmitChildWithConvert(MyLeftChild, binaryResultType, ilg, services)
Me.EmitChildWithConvert(MyRightChild, binaryResultType, ilg, services)
Me.EmitCompareOperation(ilg, MyOperation)
ElseIf Me.AreBothChildrenOfType(GetType(Boolean)) Then
' Boolean equality
MyLeftChild.Emit(ilg, services)
MyRightChild.Emit(ilg, services)
Me.EmitCompareOperation(ilg, MyOperation)
Else
Throw New InvalidOperationException("unknown operand types")
End If
End Sub
Private Sub EmitStringEquality(ByVal ilg As ILGenerator, ByVal op As LogicalCompareOperation, ByVal services As IServiceContainer)
' Get the StringComparison from the options
Dim options As ExpressionOptions = services.GetService(GetType(ExpressionOptions))
Dim ic As New Int32Constant(options.StringComparison)
ic.Emit(ilg, services)
' and emit the method call
Dim mi As System.Reflection.MethodInfo = GetType(String).GetMethod("Equals", Reflection.BindingFlags.Public Or Reflection.BindingFlags.Static, Nothing, New Type() {GetType(String), GetType(String), GetType(StringComparison)}, Nothing)
ilg.EmitCall(OpCodes.Call, mi, Nothing)
If op = LogicalCompareOperation.NotEqual Then
ilg.Emit(OpCodes.Ldc_I4_0)
ilg.Emit(OpCodes.Ceq)
End If
End Sub
Private Function IsOpTypeEqualOrNotEqual(ByVal op As LogicalCompareOperation) As Boolean
Return op = LogicalCompareOperation.Equal Or op = LogicalCompareOperation.NotEqual
End Function
' Emit the actual compare
Private Sub EmitCompareOperation(ByVal ilg As ILGenerator, ByVal op As LogicalCompareOperation)
Dim ltOpcode As OpCode = Me.GetCompareGTLTOpcode(False)
Dim gtOpcode As OpCode = Me.GetCompareGTLTOpcode(True)
Select Case op
Case LogicalCompareOperation.Equal
ilg.Emit(OpCodes.Ceq)
Case LogicalCompareOperation.LessThan
ilg.Emit(ltOpcode)
Case LogicalCompareOperation.GreaterThan
ilg.Emit(gtOpcode)
Case LogicalCompareOperation.NotEqual
ilg.Emit(OpCodes.Ceq)
ilg.Emit(OpCodes.Ldc_I4_0)
ilg.Emit(OpCodes.Ceq)
Case LogicalCompareOperation.LessThanOrEqual
ilg.Emit(gtOpcode)
ilg.Emit(OpCodes.Ldc_I4_0)
ilg.Emit(OpCodes.Ceq)
Case LogicalCompareOperation.GreaterThanOrEqual
ilg.Emit(ltOpcode)
ilg.Emit(OpCodes.Ldc_I4_0)
ilg.Emit(OpCodes.Ceq)
Case Else
Throw New ArgumentException("Unknown op type")
End Select
End Sub
' Get the correct greater/less than opcode
Private Function GetCompareGTLTOpcode(ByVal greaterThan As Boolean) As OpCode
Dim leftType As Type = MyLeftChild.ResultType
If leftType Is MyRightChild.ResultType Then
If leftType Is GetType(UInt32) Or leftType Is GetType(UInt64) Then
If greaterThan = True Then
Return OpCodes.Cgt_Un
Else
Return OpCodes.Clt_Un
End If
Else
Return Me.GetCompareOpcode(greaterThan)
End If
Else
Return Me.GetCompareOpcode(greaterThan)
End If
End Function
Private Function GetCompareOpcode(ByVal greaterThan As Boolean) As OpCode
If greaterThan = True Then
Return OpCodes.Cgt
Else
Return OpCodes.Clt
End If
End Function
Protected Overrides ReadOnly Property Name() As String
Get
Return "Compare Operator"
End Get
End Property
End Class