public DynamicMetaObject FallbackOperation(DynamicMetaObject target, DynamicMetaObject[] args, DynamicMetaObject errorSuggestion) { Expression convertedTarget = Expression.Convert(target.Expression, target.LimitType); Expression[] convertedArgs = args.Select(x => Expression.Convert(x.Expression, x.LimitType)).ToArray(); Expression exp = null; var restrictions = target.Restrictions.Merge(GetTypeRestriction(target)); int usedArgs = 0; switch (OperationKind) { // Unary case TjsOperationKind.CharCodeToChar: exp = Expression.Call(Context.Convert(convertedTarget, typeof(char)), "ToString", null); break; case TjsOperationKind.CharToCharCode: exp = Expression.Call(new Func <string, long>(TjsOperationHelper.CharToCharCode).Method, Context.Convert(convertedTarget, typeof(string))); break; case TjsOperationKind.Evaluate: case TjsOperationKind.Invalidate: case TjsOperationKind.IsValid: if (errorSuggestion == null) { errorSuggestion = new DynamicMetaObject(Expression.Throw(Expression.Constant(new NotImplementedException())), BindingRestrictions.Empty); } break; case TjsOperationKind.TypeOf: if (target.RuntimeType == null) { exp = Expression.Constant("Object"); } else if (target.RuntimeType == typeof(IronTjs.Builtins.Void)) { exp = Expression.Constant("void"); } else if (target.RuntimeType == typeof(string)) { exp = Expression.Constant("String"); } else if (Binders.IsInteger(target.RuntimeType)) { exp = Expression.Constant("Integer"); } else if (Binders.IsFloatingPoint(target.RuntimeType)) { exp = Expression.Constant("Real"); } else { exp = Expression.Constant("Object"); } break; // Unary (Special) case TjsOperationKind.InvokePropertyHandler: if (target.LimitType == typeof(Property)) { if (args.Length == 0) { exp = Expression.Property(convertedTarget, (System.Reflection.PropertyInfo)Utils.GetMember <Property>(x => x.Value)); } else if (args.Length == 1) { exp = Expression.Assign(Expression.Property(convertedTarget, (System.Reflection.PropertyInfo)Utils.GetMember <Property>(x => x.Value)), args[0].Expression); usedArgs = 1; } else { if (errorSuggestion == null) { errorSuggestion = new DynamicMetaObject(Expression.Throw(Expression.Constant(new InvalidOperationException("引数の数が * 演算子に適用できる許容範囲を超えています。"))), BindingRestrictions.Empty); } } } else if (target.LimitType == typeof(BoundMemberTracker)) { var tracker = (BoundMemberTracker)target.Value; if (tracker.Instance == null) { tracker = new BoundMemberTracker(tracker.BoundTo, new DynamicMetaObject(Expression.Constant(tracker.ObjectInstance), BindingRestrictions.Empty, tracker.ObjectInstance)); } var type = tracker.Instance.GetLimitType(); if (args.Length == 0) { var res = tracker.GetValue(new TjsOverloadResolverFactory(Context.Binder), Context.Binder, type); if (res != null) { exp = res.Expression; restrictions = restrictions.Merge(res.Restrictions); } else { if (errorSuggestion == null) { errorSuggestion = new DynamicMetaObject(Expression.Throw(Expression.Constant(new InvalidOperationException("メンバの取得に失敗しました。"))), BindingRestrictions.Empty); } } } else if (args.Length == 1) { var res = tracker.SetValue(new TjsOverloadResolverFactory(Context.Binder), Context.Binder, type, args[0]); if (res != null) { exp = res.Expression; restrictions = restrictions.Merge(res.Restrictions); usedArgs = 1; } else { if (errorSuggestion == null) { errorSuggestion = new DynamicMetaObject(Expression.Throw(Expression.Constant(new InvalidOperationException("メンバの設定に失敗しました。"))), BindingRestrictions.Empty); } } } else { if (errorSuggestion == null) { errorSuggestion = new DynamicMetaObject(Expression.Throw(Expression.Constant(new InvalidOperationException("引数の数が * 演算子に適用できる許容範囲を超えています。"))), BindingRestrictions.Empty); } } } else { if (errorSuggestion == null) { errorSuggestion = new DynamicMetaObject(Expression.Throw(Expression.Constant(new InvalidOperationException("プロパティ以外に対して * 演算子が使用されました。"))), BindingRestrictions.Empty); } } break; // Binary (Arithmetic & Logical) case TjsOperationKind.FloorDivide: exp = Expression.Divide(Context.Convert(convertedTarget, typeof(long)), Context.Convert(convertedArgs[0], typeof(long))); usedArgs = 1; break; case TjsOperationKind.RightShiftLogical: exp = Expression.Convert(Expression.RightShift(Context.Convert(convertedTarget, typeof(ulong)), Context.Convert(convertedArgs[0], typeof(int))), typeof(long)); usedArgs = 1; break; // Binary (Comparison) case TjsOperationKind.DistinctEqual: if (convertedTarget.Type == convertedArgs[0].Type) { exp = Expression.Condition(Expression.Equal(convertedTarget, convertedArgs[0]), Expression.Constant(1L), Expression.Constant(0L)); } else { exp = Expression.Constant(0L); } usedArgs = 1; break; case TjsOperationKind.DistinctNotEqual: if (convertedTarget.Type == convertedArgs[0].Type) { exp = Expression.Condition(Expression.Equal(convertedTarget, convertedArgs[0]), Expression.Constant(0L), Expression.Constant(1L)); } else { exp = Expression.Constant(1L); } usedArgs = 1; break; // Binary (Special) case TjsOperationKind.InstanceOf: exp = Expression.Condition(Expression.Equal(Expression.Constant(convertedTarget.Type.Name), Context.Convert(convertedArgs[0], typeof(string))), Expression.Constant(1L), Expression.Constant(0L) ); usedArgs = 1; break; case TjsOperationKind.InContextOf: if (target.Value is IContextChangeable) { exp = Expression.Call(Expression.Convert(convertedTarget, typeof(IContextChangeable)), "ChangeContext", null, args[0].Expression); } else if (target.LimitType == typeof(BoundMemberTracker)) { exp = Expression.New((System.Reflection.ConstructorInfo)Utils.GetMember(() => new BoundMemberTracker(null, (object)null)), Expression.PropertyOrField(convertedTarget, "BoundTo"), args[0].Expression); } else { if (errorSuggestion == null) { errorSuggestion = new DynamicMetaObject(Expression.Throw(Expression.Constant(new InvalidOperationException("incontextof 演算子は指定されたオブジェクトに適用できません。"))), BindingRestrictions.Empty); } } usedArgs = 1; break; } for (int i = 0; i < usedArgs; i++) { restrictions = restrictions.Merge(args[i].Restrictions).Merge(GetTypeRestriction(args[i])); } if (exp == null) { return(new DynamicMetaObject(errorSuggestion.Expression, errorSuggestion.Restrictions.Merge(restrictions))); } if (exp.Type != typeof(object)) { exp = Expression.Convert(exp, typeof(object)); } return(new DynamicMetaObject(exp, restrictions)); }
internal static Expression TryConvertExpression(Expression expression, Type toType, ParameterExpression succeeded) { var nonNullable = Binders.GetNonNullableType(toType); if (toType == typeof(object)) { return(InSuccess(Expression.Convert(expression, toType), succeeded)); } else if (toType == expression.Type) { return(InSuccess(expression, succeeded)); } else if (expression.Type == typeof(IronTjs.Builtins.Void)) { if (toType == typeof(string)) { return(InSuccess(Expression.Constant(string.Empty), succeeded)); } else { return(InSuccess(Expression.Default(toType), succeeded)); } } else if (Binders.IsNumber(nonNullable)) { if (Binders.IsNumber(expression.Type)) { return(InSuccess(NewNullableOrThrough(Expression.Convert(expression, nonNullable), toType, nonNullable), succeeded)); } else if (expression.Type == typeof(string)) { ParameterExpression v; Expression test; if (Binders.IsInteger(nonNullable)) { v = Expression.Variable(typeof(long)); test = Expression.Call(typeof(Binders).GetMethod("TryConvertInt64"), expression, v); } else if (Binders.IsFloatingPoint(nonNullable)) { v = Expression.Variable(typeof(double)); test = Expression.Call(typeof(Binders).GetMethod("TryConvertDouble"), expression, v); } else { v = Expression.Variable(nonNullable); test = Expression.Call(nonNullable.GetMethod("TryParse", new[] { typeof(string), nonNullable.MakeByRefType() }), expression, v); } if (succeeded != null) { test = Expression.Assign(succeeded, test); } return(Expression.Block(new[] { v }, Expression.Condition(test, NewNullableOrThrough(v, toType, nonNullable), Expression.Default(toType) ) )); } } else if (toType == typeof(string)) { if (expression.Type.IsValueType) { return(InSuccess(Expression.Call(expression, expression.Type.GetMethod("ToString", Type.EmptyTypes)), succeeded)); } var v = Expression.Variable(expression.Type); return(InSuccess(Expression.Block(new[] { v }, Expression.Condition(Expression.NotEqual(Expression.Assign(v, expression), Expression.Constant(null)), Expression.Call(v, expression.Type.GetMethod("ToString", Type.EmptyTypes)), Expression.Constant("null") ) ), succeeded)); } else if (nonNullable == typeof(bool)) { Expression exp; if (Binders.IsNumber(expression.Type)) { exp = Expression.NotEqual(expression, Expression.Default(expression.Type)); } else if (expression.Type == typeof(string)) { var v = Expression.Variable(typeof(long)); exp = Expression.Block(new[] { v }, Expression.AndAlso( Expression.Call(typeof(long).GetMethod("TryParse", new[] { typeof(string), typeof(long).MakeByRefType() }), expression, v), Expression.NotEqual(v, Expression.Constant(0L)) ) ); } else if (!expression.Type.IsValueType) { exp = Expression.NotEqual(expression, Expression.Constant(null)); } else if (expression.Type.IsGenericType && expression.Type.GetGenericTypeDefinition() == typeof(Nullable <>)) { exp = Expression.Property(expression, "HasValue"); } else { return(InSuccess(nonNullable == toType ? Expression.Constant(true) : Expression.Constant(new Nullable <bool>(true)), succeeded)); } return(InSuccess(NewNullableOrThrough(exp, toType, nonNullable), succeeded)); } return(null); }
public override DynamicMetaObject FallbackUnaryOperation(DynamicMetaObject target, DynamicMetaObject errorSuggestion) { var arg = Expression.Convert(target.Expression, target.LimitType); var restrictions = BindingRestrictionsHelpers.GetRuntimeTypeRestriction(target); Expression res = null; switch (Operation) { case ExpressionType.Decrement: if (Binders.IsFloatingPoint(target.LimitType)) { res = Expression.Decrement(_context.Convert(arg, typeof(double))); } else { res = Expression.Decrement(_context.Convert(arg, typeof(long))); } break; case ExpressionType.Increment: if (Binders.IsFloatingPoint(target.LimitType)) { res = Expression.Increment(_context.Convert(arg, typeof(double))); } else { res = Expression.Increment(_context.Convert(arg, typeof(long))); } break; case ExpressionType.Negate: if (Binders.IsFloatingPoint(target.LimitType)) { res = Expression.Negate(_context.Convert(arg, typeof(double))); } else { res = Expression.Negate(_context.Convert(arg, typeof(long))); } break; case ExpressionType.Not: res = Expression.Condition(_context.Convert(arg, typeof(bool)), Expression.Constant(0L), Expression.Constant(1L)); break; case ExpressionType.OnesComplement: res = Expression.OnesComplement(_context.Convert(arg, typeof(long))); break; case ExpressionType.UnaryPlus: if (Binders.IsFloatingPoint(target.LimitType)) { res = _context.Convert(arg, typeof(double)); } else if (Binders.IsInteger(target.LimitType)) { res = _context.Convert(arg, typeof(long)); } else if (target.LimitType == typeof(string)) { res = Expression.Call(new Func <string, object>(Binders.ConvertNumber).Method, arg); } break; } if (res != null) { if (res.Type != typeof(object)) { return(new DynamicMetaObject(Expression.Convert(res, typeof(object)), restrictions)); } else { return(new DynamicMetaObject(res, restrictions)); } } else { return(errorSuggestion ?? new DynamicMetaObject(Expression.Throw(Expression.Constant(new InvalidOperationException("不正な単項演算です。")), typeof(object)), restrictions)); } }