' 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 Imports System.Reflection.Emit Friend Enum BinaryArithmeticOperation Add Subtract Multiply Divide [Mod] Power End Enum Friend Enum LogicalCompareOperation LessThan GreaterThan Equal NotEqual LessThanOrEqual GreaterThanOrEqual End Enum Friend Enum AndOrOperation [And] [Or] End Enum Friend Enum ShiftOperation LeftShift RightShift End Enum Friend Delegate Function ExpressionEvaluator(Of T)(ByVal owner As Object, ByVal context As ExpressionContext, ByVal variables As VariableCollection) As T Friend MustInherit Class CustomBinder Inherits Binder Public Overrides Function BindToField(ByVal bindingAttr As System.Reflection.BindingFlags, ByVal match() As System.Reflection.FieldInfo, ByVal value As Object, ByVal culture As System.Globalization.CultureInfo) As System.Reflection.FieldInfo Return Nothing End Function Public Overrides Function BindToMethod(ByVal bindingAttr As System.Reflection.BindingFlags, ByVal match() As System.Reflection.MethodBase, ByRef args() As Object, ByVal modifiers() As System.Reflection.ParameterModifier, ByVal culture As System.Globalization.CultureInfo, ByVal names() As String, ByRef state As Object) As System.Reflection.MethodBase Return Nothing End Function Public Overrides Function ChangeType(ByVal value As Object, ByVal type As System.Type, ByVal culture As System.Globalization.CultureInfo) As Object Return Nothing End Function Public Overrides Sub ReorderArgumentArray(ByRef args() As Object, ByVal state As Object) End Sub Public Overrides Function SelectProperty(ByVal bindingAttr As System.Reflection.BindingFlags, ByVal match() As System.Reflection.PropertyInfo, ByVal returnType As System.Type, ByVal indexes() As System.Type, ByVal modifiers() As System.Reflection.ParameterModifier) As System.Reflection.PropertyInfo Return Nothing End Function End Class Friend Class ExplicitOperatorMethodBinder Inherits CustomBinder Private MyReturnType As Type Private MyArgType As Type Public Sub New(ByVal returnType As Type, ByVal argType As Type) MyReturnType = returnType MyArgType = argType End Sub Public Overrides Function SelectMethod(ByVal bindingAttr As System.Reflection.BindingFlags, ByVal match() As System.Reflection.MethodBase, ByVal types() As System.Type, ByVal modifiers() As System.Reflection.ParameterModifier) As System.Reflection.MethodBase For Each mi As MethodInfo In match Dim parameters As ParameterInfo() = mi.GetParameters() Dim firstParameter As ParameterInfo = parameters(0) If firstParameter.ParameterType Is MyArgType And mi.ReturnType Is MyReturnType Then Return mi End If Next Return Nothing End Function End Class Friend Class BinaryOperatorBinder Inherits CustomBinder Private MyLeftType As Type Private MyRightType As Type Public Sub New(ByVal leftType As Type, ByVal rightType As Type) MyLeftType = leftType MyRightType = rightType End Sub Public Overrides Function SelectMethod(ByVal bindingAttr As System.Reflection.BindingFlags, ByVal match() As System.Reflection.MethodBase, ByVal types() As System.Type, ByVal modifiers() As System.Reflection.ParameterModifier) As System.Reflection.MethodBase For Each mi As MethodInfo In match Dim parameters As ParameterInfo() = mi.GetParameters() Dim leftValid As Boolean = ImplicitConverter.EmitImplicitConvert(MyLeftType, parameters(0).ParameterType, Nothing) Dim rightValid As Boolean = ImplicitConverter.EmitImplicitConvert(MyRightType, parameters(1).ParameterType, Nothing) If leftValid = True And rightValid = True Then Return mi End If Next Return Nothing End Function End Class Friend Class Null End Class Friend Class DefaultExpressionOwner Private Shared OurInstance As New DefaultExpressionOwner() Private Sub New() End Sub Public Shared ReadOnly Property Instance() As Object Get Return OurInstance End Get End Property End Class ' Helper class to resolve overloads Friend Class CustomMethodInfo Implements IComparable(Of CustomMethodInfo) Implements IEquatable(Of CustomMethodInfo) Private MyTarget As MethodInfo ' Method we are wrapping Private MyScore As Single ' The rating of how close the method matches the given arguments (0 is best) Public IsParamArray As Boolean Public MyFixedArgTypes As Type() Public MyParamArrayArgTypes As Type() Public ParamArrayElementType As Type Public Sub New(ByVal target As MethodInfo) MyTarget = target End Sub Public Sub ComputeScore(ByVal argTypes As Type()) Dim params As ParameterInfo() = MyTarget.GetParameters() If params.Length = 0 Then MyScore = 0.0 ElseIf IsParamArray = True Then MyScore = Me.ComputeScoreForParamArray(params, argTypes) Else MyScore = Me.ComputeScoreInternal(params, argTypes) End If End Sub ' Compute a score showing how close our method matches the given argument types Private Function ComputeScoreInternal(ByVal parameters As ParameterInfo(), ByVal argTypes As Type()) As Single ' Our score is the average of the scores of each parameter. The lower the score, the better the match. Dim sum As Integer = ComputeSum(parameters, argTypes) Return sum / argTypes.Length End Function Private Shared Function ComputeSum(ByVal parameters As ParameterInfo(), ByVal argTypes As Type()) As Integer Debug.Assert(parameters.Length = argTypes.Length) Dim sum As Integer = 0 For i As Integer = 0 To parameters.Length - 1 sum += ImplicitConverter.GetImplicitConvertScore(argTypes(i), parameters(i).ParameterType) Next Return sum End Function Private Function ComputeScoreForParamArray(ByVal parameters As ParameterInfo(), ByVal argTypes As Type()) As Single Dim paramArrayParameter As ParameterInfo = parameters(parameters.Length - 1) Dim fixedParameterCount As Integer = paramArrayParameter.Position Dim fixedParameters(fixedParameterCount - 1) As ParameterInfo System.Array.Copy(parameters, fixedParameters, fixedParameterCount) Dim fixedSum As Integer = ComputeSum(fixedParameters, MyFixedArgTypes) Dim paramArrayElementType As Type = paramArrayParameter.ParameterType.GetElementType() Dim paramArraySum As Integer = 0 For Each argType As Type In MyParamArrayArgTypes paramArraySum += ImplicitConverter.GetImplicitConvertScore(argType, paramArrayElementType) Next Dim score As Single If argTypes.Length > 0 Then score = (fixedSum + paramArraySum) / argTypes.Length Else score = 0 End If ' The param array score gets a slight penalty so that it scores worse than direct matches Return score + 1 End Function Public Function IsAccessible(ByVal owner As MemberElement) As Boolean Return owner.IsMemberAccessible(MyTarget) End Function ' Is the given MethodInfo usable as an overload? Public Function IsMatch(ByVal argTypes As Type()) As Boolean Dim parameters As ParameterInfo() = MyTarget.GetParameters() ' If there are no parameters and no arguments were passed, then we are a match. If parameters.Length = 0 And argTypes.Length = 0 Then Return True End If ' If there are no parameters but there are arguments, we cannot be a match If parameters.Length = 0 And argTypes.Length > 0 Then Return False End If ' Is the last parameter a paramArray? Dim lastParam As ParameterInfo = parameters(parameters.Length - 1) If lastParam.IsDefined(GetType(ParamArrayAttribute), False) = False Then If (parameters.Length <> argTypes.Length) Then ' Not a paramArray and parameter and argument counts don't match Return False Else ' Regular method call, do the test Return AreValidArgumentsForParameters(argTypes, parameters) End If End If ' At this point, we are dealing with a paramArray call ' If the parameter and argument counts are equal and there is an implicit conversion from one to the other, we are a match. If parameters.Length = argTypes.Length AndAlso AreValidArgumentsForParameters(argTypes, parameters) = True Then Return True ElseIf Me.IsParamArrayMatch(argTypes, parameters, lastParam) = True Then IsParamArray = True Return True Else Return False End If End Function Private Function IsParamArrayMatch(ByVal argTypes As Type(), ByVal parameters As ParameterInfo(), ByVal paramArrayParameter As ParameterInfo) As Boolean ' Get the count of arguments before the paramArray parameter Dim fixedParameterCount As Integer = paramArrayParameter.Position Dim fixedArgTypes(fixedParameterCount - 1) As Type Dim fixedParameters(fixedParameterCount - 1) As ParameterInfo ' Get the argument types and parameters before the paramArray System.Array.Copy(argTypes, fixedArgTypes, fixedParameterCount) System.Array.Copy(parameters, fixedParameters, fixedParameterCount) ' If the fixed arguments don't match, we are not a match If AreValidArgumentsForParameters(fixedArgTypes, fixedParameters) = False Then Return False End If ' Get the type of the paramArray ParamArrayElementType = paramArrayParameter.ParameterType.GetElementType() ' Get the types of the arguments passed to the paramArray Dim paramArrayArgTypes(argTypes.Length - fixedParameterCount - 1) As Type System.Array.Copy(argTypes, fixedParameterCount, paramArrayArgTypes, 0, paramArrayArgTypes.Length) ' Check each argument For Each argType As Type In paramArrayArgTypes If ImplicitConverter.EmitImplicitConvert(argType, ParamArrayElementType, Nothing) = False Then Return False End If Next MyFixedArgTypes = fixedArgTypes MyParamArrayArgTypes = paramArrayArgTypes ' They all match, so we are a match Return True End Function Private Shared Function AreValidArgumentsForParameters(ByVal argTypes As Type(), ByVal parameters As ParameterInfo()) As Boolean Debug.Assert(argTypes.Length = parameters.Length) ' Match if every given argument is implicitly convertible to the method's corresponding parameter For i As Integer = 0 To argTypes.Length - 1 If ImplicitConverter.EmitImplicitConvert(argTypes(i), parameters(i).ParameterType, Nothing) = False Then Return False End If Next Return True End Function Private Function CompareTo(ByVal other As CustomMethodInfo) As Integer Implements System.IComparable(Of CustomMethodInfo).CompareTo Return MyScore.CompareTo(other.MyScore) End Function Private Function Equals1(ByVal other As CustomMethodInfo) As Boolean Implements System.IEquatable(Of CustomMethodInfo).Equals Return MyScore = other.MyScore End Function Public ReadOnly Property Target() As MethodInfo Get Return MyTarget End Get End Property End Class Friend Class ShortCircuitInfo Public Operands As Stack Public Operators As Stack Public Branches As BranchManager Public Sub New() Me.Operands = New Stack() Me.Operators = New Stack() Me.Branches = New BranchManager() End Sub Public Sub ClearTempState() Me.Operands.Clear() Me.Operators.Clear() End Sub End Class ' Wraps an expression element so that it is loaded from a local slot Friend Class LocalBasedElement Inherits ExpressionElement Private MyIndex As Integer Private MyTarget As ExpressionElement Public Sub New(ByVal target As ExpressionElement, ByVal index As Integer) MyTarget = target MyIndex = index End Sub Public Overrides Sub Emit(ByVal ilg As FleeILGenerator, ByVal services As IServiceProvider) Utility.EmitLoadLocal(ilg, MyIndex) End Sub Public Overrides ReadOnly Property ResultType() As System.Type Get Return MyTarget.ResultType End Get End Property End Class ''' ''' Helper class for storing strongly-typed properties ''' ''' Friend Class PropertyDictionary Private MyProperties As Dictionary(Of String, Object) Public Sub New() MyProperties = New Dictionary(Of String, Object)(StringComparer.OrdinalIgnoreCase) End Sub Public Function Clone() As PropertyDictionary Dim copy As New PropertyDictionary() For Each pair As KeyValuePair(Of String, Object) In MyProperties copy.SetValue(pair.Key, pair.Value) Next Return copy End Function Public Function GetValue(Of T)(ByVal name As String) As T Dim value As T = Nothing If MyProperties.TryGetValue(name, value) = False Then Debug.Fail(String.Format("Unknown property '{0}'", name)) End If Return value End Function Public Sub SetToDefault(Of T)(ByVal name As String) Dim value As T = Nothing Me.SetValue(name, value) End Sub Public Sub SetValue(ByVal name As String, ByVal value As Object) MyProperties.Item(name) = value End Sub Public Function Contains(ByVal name As String) As Boolean Return MyProperties.ContainsKey(name) End Function End Class