using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
ExpressionType node_type;
Type type;
- const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;
- const BindingFlags PublicStatic = BindingFlags.Public | BindingFlags.Static;
- const BindingFlags AllInstance = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
- const BindingFlags AllStatic = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
- const BindingFlags All = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
+ internal const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
+ internal const BindingFlags NonPublicInstance = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
+ internal const BindingFlags PublicStatic = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy;
+ internal const BindingFlags AllInstance = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
+ internal const BindingFlags AllStatic = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy;
+ internal const BindingFlags All = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
public ExpressionType NodeType {
get { return node_type; }
#region Binary Expressions
- static MethodInfo GetUnaryOperator (string oper_name, Type on_type, Expression expression)
+ static MethodInfo GetUnaryOperator (string oper_name, Type declaring, Type param)
{
- var methods = on_type.GetMethods (PublicStatic);
+ return GetUnaryOperator (oper_name, declaring, param, null);
+ }
+
+ static MethodInfo GetUnaryOperator (string oper_name, Type declaring, Type param, Type ret)
+ {
+ var methods = declaring.GetNotNullableType ().GetMethods (PublicStatic);
foreach (var method in methods) {
if (method.Name != oper_name)
if (parameters.Length != 1)
continue;
- if (!IsAssignableToParameterType (expression.Type, parameters [0]))
+ if (method.IsGenericMethod)
+ continue;
+
+ if (!IsAssignableToParameterType (param.GetNotNullableType (), parameters [0]))
+ continue;
+
+ if (ret != null && method.ReturnType != ret.GetNotNullableType ())
continue;
return method;
return null;
}
+ internal static MethodInfo GetTrueOperator (Type self)
+ {
+ return GetBooleanOperator ("op_True", self);
+ }
+
+ internal static MethodInfo GetFalseOperator (Type self)
+ {
+ return GetBooleanOperator ("op_False", self);
+ }
+
+ static MethodInfo GetBooleanOperator (string op, Type self)
+ {
+ return GetUnaryOperator (op, self, self, typeof (bool));
+ }
+
static bool IsAssignableToParameterType (Type type, ParameterInfo param)
{
- return GetNotNullableOf (type).IsAssignableTo (param.ParameterType);
+ var ptype = param.ParameterType;
+ if (ptype.IsByRef)
+ ptype = ptype.GetElementType ();
+
+ return type.GetNotNullableType ().IsAssignableTo (ptype);
}
- static MethodInfo UnaryCoreCheck (string oper_name, Expression expression, MethodInfo method)
+ static MethodInfo CheckUnaryMethod (MethodInfo method, Type param)
{
- if (expression == null)
- throw new ArgumentNullException ("expression");
+ if (method.ReturnType == typeof (void))
+ throw new ArgumentException ("Specified method must return a value", "method");
- if (method != null) {
- if (method.ReturnType == typeof (void))
- throw new ArgumentException ("Specified method must return a value", "method");
+ if (!method.IsStatic)
+ throw new ArgumentException ("Method must be static", "method");
- if (!method.IsStatic)
- throw new ArgumentException ("Method must be static", "method");
+ var parameters = method.GetParameters ();
- var parameters = method.GetParameters ();
+ if (parameters.Length != 1)
+ throw new ArgumentException ("Must have only one parameters", "method");
- if (parameters.Length != 1)
- throw new ArgumentException ("Must have only one parameters", "method");
+ if (!IsAssignableToParameterType (param.GetNotNullableType (), parameters [0]))
+ throw new InvalidOperationException ("left-side argument type does not match expression type");
- if (!IsAssignableToParameterType (expression.Type, parameters [0]))
- throw new InvalidOperationException ("left-side argument type does not match left expression type");
+ return method;
+ }
- return method;
- } else {
- if (IsNumber (expression.Type))
+ static MethodInfo UnaryCoreCheck (string oper_name, Expression expression, MethodInfo method, Func<Type, bool> validator)
+ {
+ if (expression == null)
+ throw new ArgumentNullException ("expression");
+
+ if (method != null)
+ return CheckUnaryMethod (method, expression.Type);
+
+ var type = expression.Type.GetNotNullableType ();
+
+ if (validator (type))
return null;
if (oper_name != null) {
- method = GetUnaryOperator (oper_name, expression.Type, expression);
+ method = GetUnaryOperator (oper_name, type, expression.Type);
if (method != null)
return method;
}
throw new InvalidOperationException (
- String.Format ("Operation {0} not defined for {1}", oper_name != null ? oper_name.Substring (3) : "is", expression.Type));
- }
+ string.Format ("Operation {0} not defined for {1}", oper_name != null ? oper_name.Substring (3) : "is", expression.Type));
}
static MethodInfo GetBinaryOperator (string oper_name, Type on_type, Expression left, Expression right)
if (parameters.Length != 2)
continue;
+ if (method.IsGenericMethod)
+ continue;
+
if (!IsAssignableToParameterType (left.Type, parameters [0]))
continue;
} else {
Type ltype = left.Type;
Type rtype = right.Type;
- Type ultype = GetNotNullableOf (ltype);
- Type urtype = GetNotNullableOf (rtype);
+ Type ultype = ltype.GetNotNullableType ();
+ Type urtype = rtype.GetNotNullableType ();
if (oper_name == "op_BitwiseOr" || oper_name == "op_BitwiseAnd") {
if (ultype == typeof (bool)) {
}
// Use IsNumber to avoid expensive reflection.
- if (IsNumber (ultype)){
+ if (IsNumber (ultype)) {
if (ultype == urtype && ltype == rtype)
return null;
return method;
}
- //
- // == and != allow reference types without operators defined.
- //
- if (!ltype.IsValueType && !rtype.IsValueType &&
- (oper_name == "op_Equality" || oper_name == "op_Inequality"))
- return null;
+ if (oper_name == "op_Equality" || oper_name == "op_Inequality") {
+ //
+ // == and != allow reference types without operators defined.
+ //
+ if (!ltype.IsValueType && !rtype.IsValueType)
+ return null;
+
+ if (ltype == rtype && ultype.IsEnum)
+ return null;
+
+ if (ltype == rtype && ultype == typeof (bool))
+ return null;
+ }
+
+ if (oper_name == "op_LeftShift" || oper_name == "op_RightShift") {
+ if (IsInt (ultype) && urtype == typeof (int))
+ return null;
+ }
throw new InvalidOperationException (
String.Format ("Operation {0} not defined for {1} and {2}", oper_name != null ? oper_name.Substring (3) : "is", ltype, rtype));
return method;
}
- static Type GetResultType (Expression expression, MethodInfo method)
- {
- return method == null ? expression.Type : method.ReturnType;
- }
-
static BinaryExpression MakeSimpleBinary (ExpressionType et, Expression left, Expression right, MethodInfo method)
{
bool is_lifted;
+ Type type;
if (method == null) {
- if (IsNullable (left.Type)) {
- if (!IsNullable (right.Type))
- throw new InvalidOperationException ("Assertion, internal error: left is nullable, requires right to be as well");
+ is_lifted = left.Type.IsNullable ();
+ type = left.Type;
+ } else {
+ var parameters = method.GetParameters ();
+
+ var lp = parameters [0];
+ var rp = parameters [1];
+
+ if (IsAssignableToOperatorParameter (left, lp) && IsAssignableToOperatorParameter (right, rp)) {
+ is_lifted = false;
+ type = method.ReturnType;
+ } else if (left.Type.IsNullable ()
+ && right.Type.IsNullable ()
+ && left.Type.GetNotNullableType () == lp.ParameterType
+ && right.Type.GetNotNullableType () == rp.ParameterType
+ && !method.ReturnType.IsNullable ()) {
is_lifted = true;
+ type = method.ReturnType.MakeNullableType ();
} else
- is_lifted = false;
- } else {
- //
- // FIXME: implement
- //
- is_lifted = false;
+ throw new InvalidOperationException ();
}
- return new BinaryExpression (et, GetResultType (left, method), left, right, is_lifted, is_lifted, method, null);
+ return new BinaryExpression (et, type, left, right, is_lifted, is_lifted, method, null);
+ }
+
+ static bool IsAssignableToOperatorParameter (Expression expression, ParameterInfo parameter)
+ {
+ if (expression.Type == parameter.ParameterType)
+ return true;
+
+ if ((!expression.Type.IsNullable () && !parameter.ParameterType.IsNullable ())
+ && IsAssignableToParameterType (expression.Type, parameter))
+ return true;
+
+ return false;
}
static UnaryExpression MakeSimpleUnary (ExpressionType et, Expression expression, MethodInfo method)
{
- return new UnaryExpression (et, expression, GetResultType (expression, method), method);
+ bool is_lifted;
+ Type type;
+
+ if (method == null) {
+ type = expression.Type;
+ is_lifted = type.IsNullable ();
+ } else {
+ var parameter = method.GetParameters () [0];
+
+ if (IsAssignableToOperatorParameter (expression, parameter)) {
+ is_lifted = false;
+ type = method.ReturnType;
+ } else if (expression.Type.IsNullable ()
+ && expression.Type.GetNotNullableType () == parameter.ParameterType
+ && !method.ReturnType.IsNullable ()) {
+
+ is_lifted = true;
+ type = method.ReturnType.MakeNullableType ();
+ } else
+ throw new InvalidOperationException ();
+ }
+
+ return new UnaryExpression (et, expression, type, method, is_lifted);
}
static BinaryExpression MakeBoolBinary (ExpressionType et, Expression left, Expression right, bool liftToNull, MethodInfo method)
{
- Type result;
- Type ltype = left.Type;
- Type rtype = right.Type;
- bool lnullable = IsNullable (ltype);
- bool rnullable = IsNullable (rtype);
bool is_lifted;
+ Type type;
- // Implement the rules as described in "Expression.Equal" method.
if (method == null) {
- if (!lnullable && !rnullable) {
+ if (!left.Type.IsNullable () && !right.Type.IsNullable ()) {
is_lifted = false;
liftToNull = false;
- result = typeof (bool);
- } else if (lnullable && rnullable) {
+ type = typeof (bool);
+ } else if (left.Type.IsNullable () && right.Type.IsNullable ()) {
is_lifted = true;
- result = liftToNull ? typeof(bool?) : typeof (bool);
+ type = liftToNull ? typeof (bool?) : typeof (bool);
} else
- throw new InvalidOperationException ("Internal error: this should have been caught in BinaryCoreCheck");
+ throw new InvalidOperationException ();
} else {
- ParameterInfo [] pi = method.GetParameters ();
- Type mltype = pi [0].ParameterType;
- Type mrtype = pi [1].ParameterType;
+ var parameters = method.GetParameters ();
+
+ var lp = parameters [0];
+ var rp = parameters [1];
- if (ltype == mltype && rtype == mrtype) {
+ if (IsAssignableToOperatorParameter (left, lp) && IsAssignableToOperatorParameter (right, rp)) {
is_lifted = false;
liftToNull = false;
- result = method.ReturnType;
- } else if (ltype.IsValueType && rtype.IsValueType &&
- ((lnullable && GetNullableOf (ltype) == mltype) ||
- (rnullable && GetNullableOf (rtype) == mrtype))){
+ type = method.ReturnType;
+ } else if (left.Type.IsNullable ()
+ && right.Type.IsNullable ()
+ && left.Type.GetNotNullableType () == lp.ParameterType
+ && right.Type.GetNotNullableType () == rp.ParameterType) {
+
is_lifted = true;
- if (method.ReturnType == typeof(bool)){
- result = liftToNull ? typeof(bool?) : typeof(bool);
- } else {
+
+ if (method.ReturnType == typeof (bool))
+ type = liftToNull ? typeof (bool?) : typeof (bool);
+ else if (!method.ReturnType.IsNullable ()) {
//
// This behavior is not documented: what
// happens if the result is not typeof(bool), but
//
// See:
// https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=323139
- result = typeof (Nullable<>).MakeGenericType (method.ReturnType);
- }
- } else {
- is_lifted = false;
- liftToNull = false;
- result = method.ReturnType;
- }
+
+ type = method.ReturnType.MakeNullableType ();
+ } else
+ throw new InvalidOperationException ();
+ } else
+ throw new InvalidOperationException ();
}
- return new BinaryExpression (et, result, left, right, liftToNull, is_lifted, method, null);
+ return new BinaryExpression (et, type, left, right, liftToNull, is_lifted, method, null);
}
//
{
method = BinaryCoreCheck (null, left, right, method);
- if (left.Type != typeof (double))
+ if (left.Type.GetNotNullableType () != typeof (double))
throw new InvalidOperationException ("Power only supports double arguments");
return MakeSimpleBinary (ExpressionType.Power, left, right, method);
method = BinaryCoreCheck (oper, left, right, method);
if (method == null) {
- if (GetNotNullableOf (left.Type) != typeof (bool))
+ if (left.Type.GetNotNullableType () != typeof (bool))
throw new InvalidOperationException ("Only booleans are allowed");
} else {
+ var type = left.Type.GetNotNullableType ();
+
// The method should have identical parameter and return types.
- if (left.Type != right.Type || method.ReturnType != left.Type)
+ if (left.Type != right.Type || method.ReturnType != type)
throw new ArgumentException ("left, right and return type must match");
+
+ var optrue = GetTrueOperator (type);
+ var opfalse = GetFalseOperator (type);
+
+ if (optrue == null || opfalse == null)
+ throw new ArgumentException ("Operators true and false are required but not defined");
}
return method;
return Coalesce (left, right, null);
}
- public static BinaryExpression Coalesce (Expression left, Expression right, LambdaExpression conversion)
+ static BinaryExpression MakeCoalesce (Expression left, Expression right)
{
- if (left == null)
- throw new ArgumentNullException ("left");
- if (right == null)
- throw new ArgumentNullException ("right");
-
- //
- // First arg must ne nullable (either Nullable<T> or a reference type
- //
- if (left.Type.IsValueType && !IsNullable (left.Type))
- throw new InvalidOperationException ("Left expression can never be null");
-
Type result = null;
- if (IsNullable (left.Type)) {
- Type lbase = GetNullableOf (left.Type);
+ if (left.Type.IsNullable ()) {
+ Type lbase = left.Type.GetNotNullableType ();
- if (!IsNullable (right.Type) && right.Type.IsAssignableTo (lbase))
+ if (!right.Type.IsNullable () && right.Type.IsAssignableTo (lbase))
result = lbase;
}
result = left.Type;
if (result == null) {
- if (IsNullable (left.Type) && GetNullableOf (left.Type).IsAssignableTo (right.Type))
+ if (left.Type.IsNullable () && left.Type.GetNotNullableType ().IsAssignableTo (right.Type))
result = right.Type;
}
if (result == null)
throw new ArgumentException ("Incompatible argument types");
+ return new BinaryExpression (ExpressionType.Coalesce, result, left, right, false, false, null, null);
+ }
+
+ static BinaryExpression MakeConvertedCoalesce (Expression left, Expression right, LambdaExpression conversion)
+ {
+ var invoke = conversion.Type.GetInvokeMethod ();
+
+ CheckNotVoid (invoke.ReturnType);
+
+ if (invoke.ReturnType != right.Type)
+ throw new InvalidOperationException ("Conversion return type doesn't march right type");
+
+ var parameters = invoke.GetParameters ();
+
+ if (parameters.Length != 1)
+ throw new ArgumentException ("Conversion has wrong number of parameters");
+
+ if (!IsAssignableToParameterType (left.Type, parameters [0]))
+ throw new InvalidOperationException ("Conversion argument doesn't marcht left type");
+
+ return new BinaryExpression (ExpressionType.Coalesce, right.Type, left, right, false, false, null, conversion);
+ }
+
+ public static BinaryExpression Coalesce (Expression left, Expression right, LambdaExpression conversion)
+ {
+ if (left == null)
+ throw new ArgumentNullException ("left");
+ if (right == null)
+ throw new ArgumentNullException ("right");
+
//
- // FIXME: What do we do with "conversion"?
+ // First arg must ne nullable (either Nullable<T> or a reference type
//
- return new BinaryExpression (ExpressionType.Coalesce, result, left, right, false, false, null, conversion);
+ if (left.Type.IsValueType && !left.Type.IsNullable ())
+ throw new InvalidOperationException ("Left expression can never be null");
+
+ if (conversion != null)
+ return MakeConvertedCoalesce (left, right, conversion);
+
+ return MakeCoalesce (left, right);
}
//
return AddChecked (left, right, method);
case ExpressionType.AndAlso:
return AndAlso (left, right);
+ case ExpressionType.ArrayIndex:
+ return ArrayIndex (left, right);
case ExpressionType.Coalesce:
return Coalesce (left, right, conversion);
case ExpressionType.Divide:
if (expression == null)
throw new ArgumentNullException ("expression");
+ CheckNonGenericMethod (propertyAccessor);
+
var prop = GetAssociatedProperty (propertyAccessor);
if (prop == null)
throw new ArgumentException ("propertyAccessor");
throw new ArgumentNullException ("method");
if (instance == null && !method.IsStatic)
throw new ArgumentNullException ("instance");
- if (instance != null && !instance.Type.IsAssignableTo (method.DeclaringType))
+ if (method.IsStatic && instance != null)
+ throw new ArgumentException ("instance");
+ if (!method.IsStatic && !instance.Type.IsAssignableTo (method.DeclaringType))
throw new ArgumentException ("Type is not assignable to the declaring type of the method");
- var args = arguments.ToReadOnlyCollection ();
-
- CheckMethodArguments (method, args);
+ var args = CheckMethodArguments (method, arguments);
return new MethodCallExpression (instance, method, args);
}
if (method == null)
return null;
- if (!method.IsGenericMethod && args == null)
+ if (!method.IsGenericMethod && (args == null || args.Length == 0))
return method;
if (args.Length == method.GetGenericArguments ().Length)
if (methodName == null)
throw new ArgumentNullException ("methodName");
- var method = GetGenericMethod (instance.Type, methodName, AllInstance,
+ var method = TryGetMethod (instance.Type, methodName, AllInstance,
CollectTypes (arguments), typeArguments);
- var args = arguments.ToReadOnlyCollection ();
- CheckMethodArguments (method, args);
+ var args = CheckMethodArguments (method, arguments);
return new MethodCallExpression (instance, method, args);
}
- static MethodInfo GetGenericMethod (Type type, string methodName, BindingFlags flags, Type [] parameterTypes, Type [] argumentTypes)
+ static bool MethodMatch (MethodInfo method, string name, Type [] parameterTypes, Type [] argumentTypes)
+ {
+ if (method.Name != name)
+ return false;
+
+ var parameters = method.GetParameters ();
+
+ if (parameters.Length != parameterTypes.Length)
+ return false;
+
+ if (method.IsGenericMethod && method.IsGenericMethodDefinition) {
+ var closed = TryMakeGeneric (method, argumentTypes);
+ if (closed == null)
+ return false;
+
+ return MethodMatch (closed, name, parameterTypes, argumentTypes);
+ } else if (!method.IsGenericMethod && (argumentTypes != null && argumentTypes.Length > 0))
+ return false;
+
+ for (int i = 0; i < parameters.Length; i++) {
+ var type = parameterTypes [i];
+ var parameter = parameters [i];
+ if (!IsAssignableToParameterType (type, parameter)
+ && !IsExpressionOfParameter (type, parameter.ParameterType))
+ return false;
+ }
+
+ return true;
+ }
+
+ static bool IsExpressionOfParameter (Type type, Type ptype)
+ {
+ return ptype.IsGenericInstanceOf (typeof (Expression<>)) && ptype.GetFirstGenericArgument () == type;
+ }
+
+ static MethodInfo TryGetMethod (Type type, string methodName, BindingFlags flags, Type [] parameterTypes, Type [] argumentTypes)
{
- var method = type.GetMethod (methodName, flags, null, parameterTypes, null);
- method = TryMakeGeneric (method, argumentTypes);
+ var methods = from meth in type.GetMethods (flags)
+ where MethodMatch (meth, methodName, parameterTypes, argumentTypes)
+ select meth;
+
+ if (methods.Count () > 1)
+ throw new InvalidOperationException ("Too many method candidates");
+
+ var method = TryMakeGeneric (methods.FirstOrDefault (), argumentTypes);
if (method != null)
return method;
if (methodName == null)
throw new ArgumentNullException ("methodName");
- var method = GetGenericMethod (type, methodName, AllStatic,
+ var method = TryGetMethod (type, methodName, AllStatic,
CollectTypes (arguments), typeArguments);
- var args = arguments.ToReadOnlyCollection ();
- CheckMethodArguments (method, args);
+ var args = CheckMethodArguments (method, arguments);
return new MethodCallExpression (method, args);
}
// are allowed
//
if (value == null){
- if (type.IsValueType && !IsNullable (type))
+ if (type.IsValueType && !type.IsNullable ())
throw new ArgumentException ();
} else {
- if (!(type.IsValueType && IsNullable (type)) && value.GetType () != type)
+ if (!(type.IsValueType && type.IsNullable ()) && !value.GetType ().IsAssignableTo (type))
throw new ArgumentException ();
}
return new ConstantExpression (value, type);
}
- [MonoTODO]
+ static bool IsConvertiblePrimitive (Type type)
+ {
+ var t = type.GetNotNullableType ();
+
+ if (t == typeof (bool))
+ return false;
+
+ if (t.IsEnum)
+ return true;
+
+ return t.IsPrimitive;
+ }
+
+ internal static bool IsPrimitiveConversion (Type type, Type target)
+ {
+ if (type == target)
+ return true;
+
+ if (type.IsNullable () && target == type.GetNotNullableType ())
+ return true;
+
+ if (target.IsNullable () && type == target.GetNotNullableType ())
+ return true;
+
+ if (IsConvertiblePrimitive (type) && IsConvertiblePrimitive (target))
+ return true;
+
+ return false;
+ }
+
+ internal static bool IsReferenceConversion (Type type, Type target)
+ {
+ if (type == target)
+ return true;
+
+ if (type == typeof (object) || target == typeof (object))
+ return true;
+
+ if (type.IsInterface || target.IsInterface)
+ return true;
+
+ if (type.IsEnum && target == typeof (Enum))
+ return true;
+
+ if (type.IsValueType || target.IsValueType)
+ return false;
+
+ if (type.IsAssignableTo (target) || target.IsAssignableTo (type))
+ return true;
+
+ return false;
+ }
+
public static UnaryExpression Convert (Expression expression, Type type)
{
- throw new NotImplementedException ();
+ return Convert (expression, type, null);
+ }
+
+ static MethodInfo GetUserConversionMethod (Type type, Type target)
+ {
+ var method = GetUnaryOperator ("op_Explicit", type, type, target);
+ if (method == null)
+ method = GetUnaryOperator ("op_Implicit", type, type, target);
+ if (method == null)
+ method = GetUnaryOperator ("op_Explicit", target, type, target);
+ if (method == null)
+ method = GetUnaryOperator ("op_Implicit", target, type, target);
+ if (method == null)
+ throw new InvalidOperationException ();
+
+ return method;
}
- [MonoTODO]
public static UnaryExpression Convert (Expression expression, Type type, MethodInfo method)
{
- throw new NotImplementedException ();
+ if (expression == null)
+ throw new ArgumentNullException ("expression");
+ if (type == null)
+ throw new ArgumentNullException ("type");
+
+ var et = expression.Type;
+
+ if (method != null)
+ CheckUnaryMethod (method, et);
+ else if (!IsPrimitiveConversion (et, type) && !IsReferenceConversion (et, type))
+ method = GetUserConversionMethod (et, type);
+
+ return new UnaryExpression (ExpressionType.Convert,
+ expression, type, method,
+ IsConvertNodeLifted (method, expression, type));
+ }
+
+ static bool IsConvertNodeLifted (MethodInfo method, Expression operand, Type target)
+ {
+ if (method == null)
+ return operand.Type.IsNullable () || target.IsNullable ();
+
+ if (operand.Type.IsNullable () && !ParameterMatch (method, operand.Type))
+ return true;
+
+ if (target.IsNullable () && !ReturnTypeMatch (method, target))
+ return true;
+
+ return false;
+ }
+
+ static bool ParameterMatch (MethodInfo method, Type type)
+ {
+ return method.GetParameters () [0].ParameterType == type;
+ }
+
+ static bool ReturnTypeMatch (MethodInfo method, Type type)
+ {
+ return method.ReturnType == type;
}
- [MonoTODO]
public static UnaryExpression ConvertChecked (Expression expression, Type type)
{
- throw new NotImplementedException ();
+ return ConvertChecked (expression, type, null);
}
- [MonoTODO]
public static UnaryExpression ConvertChecked (Expression expression, Type type, MethodInfo method)
{
- throw new NotImplementedException ();
+ if (expression == null)
+ throw new ArgumentNullException ("expression");
+ if (type == null)
+ throw new ArgumentNullException ("type");
+
+ var et = expression.Type;
+
+ if (method != null)
+ CheckUnaryMethod (method, et);
+ else if (IsReferenceConversion (et, type))
+ return Convert (expression, type, method);
+ else if (!IsPrimitiveConversion (et, type))
+ method = GetUserConversionMethod (et, type);
+
+ return new UnaryExpression (ExpressionType.ConvertChecked,
+ expression, type, method,
+ IsConvertNodeLifted (method, expression, type));
}
public static ElementInit ElementInit (MethodInfo addMethod, params Expression [] arguments)
throw new ArgumentNullException ("addMethod");
if (arguments == null)
throw new ArgumentNullException ("arguments");
- if (addMethod.Name.ToLowerInvariant () != "add")
+ if (addMethod.Name.ToLower (CultureInfo.InvariantCulture) != "add")
throw new ArgumentException ("addMethod");
if (addMethod.IsStatic)
throw new ArgumentException ("addMethod must be an instance method", "addMethod");
- var args = arguments.ToReadOnlyCollection ();
-
- CheckMethodArguments (addMethod, args);
+ var args = CheckMethodArguments (addMethod, arguments);
return new ElementInit (addMethod, args);
}
throw new ArgumentNullException ("expression");
if (!expression.Type.IsAssignableTo (field.DeclaringType))
throw new ArgumentException ("field");
- }
+ } else if (expression != null)
+ throw new ArgumentException ("expression");
return new MemberExpression (expression, field, field.FieldType);
}
var args = arguments.ToReadOnlyCollection ();
CheckForNull (args, "arguments");
- var invoke = type.GetMethod ("Invoke");
+ var invoke = type.GetInvokeMethod ();
if (invoke == null)
throw new ArgumentException ("expression");
if (invoke.GetParameters ().Length != args.Count)
throw new InvalidOperationException ("Arguments count doesn't match parameters length");
- CheckMethodArguments (invoke, args);
+ args = CheckMethodArguments (invoke, args);
return new InvocationExpression (expression, invoke.ReturnType, args);
}
+ static bool CanAssign (Type target, Type source)
+ {
+ // This catches object and value type mixage, type compatibility is handled later
+ if (target.IsValueType ^ source.IsValueType)
+ return false;
+
+ return source.IsAssignableTo (target);
+ }
+
+ static Expression CheckLambda (Type delegateType, Expression body, ReadOnlyCollection<ParameterExpression> parameters)
+ {
+ if (!delegateType.IsSubclassOf (typeof (System.Delegate)))
+ throw new ArgumentException ("delegateType");
+
+ var invoke = delegateType.GetInvokeMethod ();
+ if (invoke == null)
+ throw new ArgumentException ("delegate must contain an Invoke method", "delegateType");
+
+ var invoke_parameters = invoke.GetParameters ();
+ if (invoke_parameters.Length != parameters.Count)
+ throw new ArgumentException (string.Format ("Different number of arguments in delegate {0}", delegateType), "delegateType");
+
+ for (int i = 0; i < invoke_parameters.Length; i++) {
+ var parameter = parameters [i];
+ if (parameter == null)
+ throw new ArgumentNullException ("parameters");
+
+ if (!CanAssign (parameter.Type, invoke_parameters [i].ParameterType))
+ throw new ArgumentException (String.Format ("Can not assign a {0} to a {1}", invoke_parameters [i].ParameterType, parameter.Type));
+ }
+
+ if (invoke.ReturnType != typeof (void)) {
+ if (!CanAssign (invoke.ReturnType, body.Type)) {
+ if (invoke.ReturnType.IsExpression ())
+ return Expression.Quote (body);
+
+ throw new ArgumentException (String.Format ("body type {0} can not be assigned to {1}", body.Type, invoke.ReturnType));
+ }
+ }
+ return body;
+ }
+
public static Expression<TDelegate> Lambda<TDelegate> (Expression body, params ParameterExpression [] parameters)
{
return Lambda<TDelegate> (body, parameters as IEnumerable<ParameterExpression>);
if (body == null)
throw new ArgumentNullException ("body");
- return new Expression<TDelegate> (body, parameters.ToReadOnlyCollection ());
+ var ps = parameters.ToReadOnlyCollection ();
+
+ body = CheckLambda (typeof (TDelegate), body, ps);
+
+ return new Expression<TDelegate> (body, ps);
}
public static LambdaExpression Lambda (Expression body, params ParameterExpression [] parameters)
return Lambda (delegateType, body, parameters as IEnumerable<ParameterExpression>);
}
+ static LambdaExpression CreateExpressionOf (Type type, Expression body, ReadOnlyCollection<ParameterExpression> parameters)
+ {
+ return (LambdaExpression) typeof (Expression<>).MakeGenericType (type).GetConstructor (
+ NonPublicInstance, null, new [] { typeof (Expression), typeof (ReadOnlyCollection<ParameterExpression>) }, null).Invoke (new object [] { body, parameters } );
+ }
+
public static LambdaExpression Lambda (Type delegateType, Expression body, IEnumerable<ParameterExpression> parameters)
{
if (delegateType == null)
if (body == null)
throw new ArgumentNullException ("body");
- return new LambdaExpression (delegateType, body, parameters.ToReadOnlyCollection ());
+ var ps = parameters.ToReadOnlyCollection ();
+
+ body = CheckLambda (delegateType, body, ps);
+
+ return CreateExpressionOf (delegateType, body, ps);
}
public static MemberListBinding ListBind (MemberInfo member, params ElementInit [] initializers)
var inits = CheckListInit (newExpression, initializers);
if (addMethod != null) {
- if (addMethod.Name.ToLowerInvariant () != "add")
+ if (addMethod.Name.ToLower (CultureInfo.InvariantCulture) != "add")
throw new ArgumentException ("addMethod");
var parameters = addMethod.GetParameters ();
public static UnaryExpression Negate (Expression expression, MethodInfo method)
{
- method = UnaryCoreCheck ("op_UnaryNegation", expression, method);
+ method = UnaryCoreCheck ("op_UnaryNegation", expression, method, type => IsSignedNumber (type));
return MakeSimpleUnary (ExpressionType.Negate, expression, method);
}
public static UnaryExpression NegateChecked (Expression expression, MethodInfo method)
{
- method = UnaryCoreCheck ("op_UnaryNegation", expression, method);
+ method = UnaryCoreCheck ("op_UnaryNegation", expression, method, type => IsSignedNumber (type));
- return MakeSimpleUnary (ExpressionType.Negate, expression, method);
+ return MakeSimpleUnary (ExpressionType.NegateChecked, expression, method);
}
public static NewExpression New (ConstructorInfo constructor)
if (type == null)
throw new ArgumentNullException ("type");
+ CheckNotVoid (type);
+
var args = (null as IEnumerable<Expression>).ToReadOnlyCollection ();
if (type.IsValueType)
if (constructor == null)
throw new ArgumentNullException ("constructor");
- var args = arguments.ToReadOnlyCollection ();
-
- CheckMethodArguments (constructor, args);
+ var args = CheckMethodArguments (constructor, arguments);
return new NewExpression (constructor, args, null);
}
- static void CheckMethodArguments (MethodBase method, ReadOnlyCollection<Expression> arguments)
+ static IList<Expression> CreateArgumentList (IEnumerable<Expression> arguments)
+ {
+ if (arguments == null)
+ return arguments.ToReadOnlyCollection ();
+
+ return arguments.ToList ();
+ }
+
+ static void CheckNonGenericMethod (MethodBase method)
{
+ if (method.IsGenericMethodDefinition || method.ContainsGenericParameters)
+ throw new ArgumentException ("Can not used open generic methods");
+ }
+
+ static ReadOnlyCollection<Expression> CheckMethodArguments (MethodBase method, IEnumerable<Expression> args)
+ {
+ CheckNonGenericMethod (method);
+
+ var arguments = CreateArgumentList (args);
var parameters = method.GetParameters ();
if (arguments.Count != parameters.Length)
if (arguments [i] == null)
throw new ArgumentNullException ("arguments");
- if (!IsAssignableToParameterType (arguments [i].Type, parameters [i]))
- throw new ArgumentException ("arguments");
+ if (!IsAssignableToParameterType (arguments [i].Type, parameters [i])) {
+ if (!parameters [i].ParameterType.IsExpression ())
+ throw new ArgumentException ("arguments");
+
+ arguments [i] = Expression.Quote (arguments [i]);
+ }
}
+
+ return arguments.ToReadOnlyCollection ();
}
public static NewExpression New (ConstructorInfo constructor, IEnumerable<Expression> arguments, params MemberInfo [] members)
CheckForNull (args, "arguments");
CheckForNull (mmbs, "members");
- CheckMethodArguments (constructor, args);
+ args = CheckMethodArguments (constructor, arguments);
if (args.Count != mmbs.Count)
throw new ArgumentException ("Arguments count does not match members count");
if (bounds == null)
throw new ArgumentNullException ("bounds");
+ CheckNotVoid (type);
+
var array_bounds = bounds.ToReadOnlyCollection ();
foreach (var expression in array_bounds)
if (!IsInt (expression.Type))
if (initializers == null)
throw new ArgumentNullException ("initializers");
- var array_initializers = initializers.ToReadOnlyCollection ();
+ CheckNotVoid (type);
+
+ var inits = initializers.ToReadOnlyCollection ();
- foreach (var expression in initializers) {
+ foreach (var expression in inits) {
if (expression == null)
throw new ArgumentNullException ("initializers");
if (!expression.Type.IsAssignableTo (type))
- throw new InvalidOperationException ();
+ throw new InvalidOperationException (
+ string.Format ("{0} IsAssignableTo {1}, expression [ {2} ] : {3}", expression.Type, type, expression.NodeType, expression));
// TODO: Quote elements if type == typeof (Expression)
}
- return new NewArrayExpression (ExpressionType.NewArrayInit, type.MakeArrayType (), array_initializers);
+ return new NewArrayExpression (ExpressionType.NewArrayInit, type.MakeArrayType (), inits);
}
public static UnaryExpression Not (Expression expression)
public static UnaryExpression Not (Expression expression, MethodInfo method)
{
- method = UnaryCoreCheck ("op_LogicalNot", expression, method);
+ Func<Type, bool> validator = type => IsIntOrBool (type);
+
+ method = UnaryCoreCheck ("op_LogicalNot", expression, method, validator);
+
+ if (method == null)
+ method = UnaryCoreCheck ("op_OnesComplement", expression, method, validator);
return MakeSimpleUnary (ExpressionType.Not, expression, method);
}
+ static void CheckNotVoid (Type type)
+ {
+ if (type == typeof (void))
+ throw new ArgumentException ("Type can't be void");
+ }
+
public static ParameterExpression Parameter (Type type, string name)
{
if (type == null)
throw new ArgumentNullException ("type");
+ CheckNotVoid (type);
+
return new ParameterExpression (type, name);
}
if (propertyAccessor == null)
throw new ArgumentNullException ("propertyAccessor");
+ CheckNonGenericMethod (propertyAccessor);
+
if (!propertyAccessor.IsStatic) {
if (expression == null)
throw new ArgumentNullException ("expression");
if (!expression.Type.IsAssignableTo (propertyAccessor.DeclaringType))
throw new ArgumentException ("expression");
}
+ //
+ // .NET does not mandate that if the property is static, that the expression must be null
+ // fixes a bug exposed by Orchard's ContentItemRecordAlteration.Alteration
+ // else if (expression != null)
+ // throw new ArgumentException ("expression");
var prop = GetAssociatedProperty (propertyAccessor);
if (prop == null)
static PropertyInfo GetAssociatedProperty (MethodInfo method)
{
+ if (method == null)
+ return null;
+
foreach (var prop in method.DeclaringType.GetProperties (All)) {
- if (prop.GetGetMethod (true) == method)
+ if (method.Equals (prop.GetGetMethod (true)))
return prop;
- if (prop.GetSetMethod (true) == method)
+ if (method.Equals (prop.GetSetMethod (true)))
return prop;
}
throw new ArgumentNullException ("expression");
if (!expression.Type.IsAssignableTo (property.DeclaringType))
throw new ArgumentException ("expression");
- }
+ } else if (expression != null)
+ throw new ArgumentException ("expression");
return new MemberExpression (expression, property, property.PropertyType);
}
throw new ArgumentNullException ("expression");
if (type == null)
throw new ArgumentNullException ("type");
- if (type.IsValueType && !IsNullable (type))
+ if (type.IsValueType && !type.IsNullable ())
throw new ArgumentException ("TypeAs expect a reference or a nullable type");
return new UnaryExpression (ExpressionType.TypeAs, expression, type);
if (type == null)
throw new ArgumentNullException ("type");
+ CheckNotVoid (type);
+
return new TypeBinaryExpression (ExpressionType.TypeIs, expression, type, typeof (bool));
}
public static UnaryExpression UnaryPlus (Expression expression, MethodInfo method)
{
- method = UnaryCoreCheck ("op_UnaryPlus", expression, method);
+ method = UnaryCoreCheck ("op_UnaryPlus", expression, method, type => IsNumber (type));
return MakeSimpleUnary (ExpressionType.UnaryPlus, expression, method);
}
if (IsInt (t))
return true;
- return t == typeof (float) || t == typeof (double) || t == typeof (decimal);
+ return t == typeof (float) || t == typeof (double);
}
- internal static bool IsNullable (Type type)
+ static bool IsSignedNumber (Type t)
{
- return type.IsGenericType && type.GetGenericTypeDefinition () == typeof (Nullable<>);
+ return IsNumber (t) && !IsUnsigned (t);
}
internal static bool IsUnsigned (Type t)
{
+#if !TARGET_JVM
if (t.IsPointer)
return IsUnsigned (t.GetElementType ());
+#endif
return t == typeof (ushort) ||
t == typeof (uint) ||
t == typeof (ulong) ||
t == typeof (byte);
}
- //
- // returns the T in a a Nullable<T> type.
- //
- internal static Type GetNullableOf (Type type)
- {
- return type.GetGenericArguments () [0];
- }
-
- internal static Type GetNotNullableOf (Type type)
- {
- return IsNullable (type) ? GetNullableOf (type) : type;
- }
-
//
// This method must be overwritten by derived classes to
// compile the expression
//
- internal abstract void Emit (EmitContext ec);
+#if !FULL_AOT_RUNTIME
+ internal virtual void Emit (EmitContext ec)
+ {
+ throw new NotImplementedException (String.Format ("Emit method is not implemented in expression type {0}", GetType ()));
+ }
+#endif
}
}