Example #1
0
        /// <summary>
        /// From control flow perspective it "calls" the proc.
        /// </summary>
        internal static void SetProcCallRule(
            MetaObjectBuilder /*!*/ metaBuilder,
            Expression /*!*/ procExpression,    // proc object
            Expression /*!*/ selfExpression,    // self passed to the proc
            Expression callingMethodExpression, // RubyLambdaMethodInfo passed to the proc via BlockParam
            CallArguments /*!*/ args            // user arguments passed to the proc
            )
        {
            var bfcVariable    = metaBuilder.GetTemporary(typeof(BlockParam), "#bfc");
            var resultVariable = metaBuilder.GetTemporary(typeof(object), "#result");

            metaBuilder.Result = AstFactory.Block(
                Ast.Assign(bfcVariable,
                           (callingMethodExpression != null) ?
                           Methods.CreateBfcForMethodProcCall.OpCall(
                               AstUtils.Convert(procExpression, typeof(Proc)),
                               callingMethodExpression
                               ) :
                           Methods.CreateBfcForProcCall.OpCall(
                               AstUtils.Convert(procExpression, typeof(Proc))
                               )
                           ),
                Ast.Assign(resultVariable, AstFactory.YieldExpression(
                               args.GetSimpleArgumentExpressions(),
                               args.GetSplattedArgumentExpression(),
                               args.GetRhsArgumentExpression(),
                               bfcVariable,
                               selfExpression
                               )),
                Methods.MethodProcCall.OpCall(bfcVariable, resultVariable),
                resultVariable
                );
        }
        /// <summary>
        /// Takes current result and wraps it into try-filter(MethodUnwinder)-finally block that ensures correct "break" behavior for
        /// library method calls with block given in bfcVariable (BlockParam).
        /// </summary>
        internal static void ApplyBlockFlowHandlingInternal(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args)
        {
            if (metaBuilder.Error)
            {
                return;
            }

            Expression expression  = metaBuilder.Result;
            Expression bfcVariable = metaBuilder.BfcVariable;

            // Method call with proc can invoke control flow that returns an arbitrary value from the call, so we need to type result to Object.
            // Otherwise, the result could only be result of targetExpression unless its return type is void.
            Type resultType = (bfcVariable != null) ? typeof(object) : expression.Type;

            Expression resultVariable;

            if (resultType != typeof(void))
            {
                resultVariable = metaBuilder.GetTemporary(resultType, "#result");
            }
            else
            {
                resultVariable = Expression.Empty();
            }

            if (expression.Type != typeof(void))
            {
                expression = Ast.Assign(resultVariable, AstUtils.Convert(expression, resultType));
            }

            // a non-null proc is being passed to the callee:
            if (bfcVariable != null)
            {
                ParameterExpression methodUnwinder = metaBuilder.GetTemporary(typeof(MethodUnwinder), "#unwinder");

                expression = AstFactory.Block(
                    Ast.Assign(bfcVariable, Methods.CreateBfcForLibraryMethod.OpCall(AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)))),
                    AstUtils.Try(
                        expression
                        ).Filter(methodUnwinder, Methods.IsProcConverterTarget.OpCall(bfcVariable, methodUnwinder),
                                 Ast.Assign(resultVariable, Ast.Field(methodUnwinder, MethodUnwinder.ReturnValueField))
                                 ).Finally(
                        Methods.LeaveProcConverter.OpCall(bfcVariable)
                        ),
                    resultVariable
                    );
            }

            metaBuilder.Result = expression;
        }
Example #3
0
        /// <summary>
        /// OldCallAction on Proc target.
        /// From control flow perspective it "yields" to the proc.
        /// </summary>
        internal void SetCallActionRule(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args)
        {
            Assert.NotNull(metaBuilder, args);
            Debug.Assert(!args.Signature.HasBlock);

            var convertedTarget = AstUtils.Convert(args.TargetExpression, typeof(BlockParam));

            // test for target type:
            metaBuilder.AddTypeRestriction(args.Target.GetType(), args.TargetExpression);

            metaBuilder.Result = AstFactory.YieldExpression(
                args.GetSimpleArgumentExpressions(),
                args.GetSplattedArgumentExpression(),
                args.GetRhsArgumentExpression(),
                convertedTarget,                              // block param
                Ast.Property(convertedTarget, SelfProperty)   // self
                );
        }
Example #4
0
        internal override void BuildCallNoFlow(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args, string /*!*/ name)
        {
            Assert.NotNull(metaBuilder, args, name);

            // 2 implicit args: self, block
            var argsBuilder = new ArgsBuilder(2, _mandatoryParamCount, _optionalParamCount, _hasUnsplatParameter);

            argsBuilder.SetImplicit(0, AstFactory.Box(args.TargetExpression));
            argsBuilder.SetImplicit(1, args.Signature.HasBlock ? AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)) : AstFactory.NullOfProc);
            argsBuilder.AddCallArguments(metaBuilder, args);

            if (metaBuilder.Error)
            {
                return;
            }

            // box explicit arguments:
            var boxedArguments = argsBuilder.GetArguments();

            for (int i = 2; i < boxedArguments.Length; i++)
            {
                boxedArguments[i] = AstFactory.Box(boxedArguments[i]);
            }

            if (_method.GetType() == ParamsArrayDelegateType)
            {
                // Func<object, Proc, object[], object>
                metaBuilder.Result = AstFactory.CallDelegate(_method, new[] {
                    boxedArguments[0],
                    boxedArguments[1],
                    Ast.NewArrayInit(typeof(object), ArrayUtils.ShiftLeft(boxedArguments, 2))
                });
            }
            else
            {
                metaBuilder.Result = AstFactory.CallDelegate(_method, boxedArguments);
            }
        }
Example #5
0
        public static MSA.Expression /*!*/ MakeArrayOpCall(List <MSA.Expression> /*!*/ args)
        {
            Assert.NotNull(args);

            switch (args.Count)
            {
            case 0: return(Methods.MakeArray0.OpCall());

            case 1: return(Methods.MakeArray1.OpCall(AstFactory.Box(args[0])));

            case 2: return(Methods.MakeArray2.OpCall(AstFactory.Box(args[0]), AstFactory.Box(args[1])));

            case 3: return(Methods.MakeArray3.OpCall(AstFactory.Box(args[0]), AstFactory.Box(args[1]), AstFactory.Box(args[2])));

            case 4: return(Methods.MakeArray4.OpCall(AstFactory.Box(args[0]), AstFactory.Box(args[1]), AstFactory.Box(args[2]), AstFactory.Box(args[3])));

            case 5: return(Methods.MakeArray5.OpCall(AstFactory.Box(args[0]), AstFactory.Box(args[1]), AstFactory.Box(args[2]), AstFactory.Box(args[3]), AstFactory.Box(args[4])));

            default:
                Debug.Assert(args.Count > Runtime.RubyOps.OptimizedOpCallParamCount);
                return(Methods.MakeArrayN.OpCall(AstUtils.NewArrayHelper(typeof(object), args)));
            }
        }
Example #6
0
        public static RuleGenerator /*!*/ GetException()
        {
            return(new RuleGenerator((metaBuilder, args, name) => {
                Debug.Assert(args.Target is Exception);

                // 1 optional parameter (exceptionArg):
                var argsBuilder = new ArgsBuilder(0, 0, 1, false);
                argsBuilder.AddCallArguments(metaBuilder, args);

                if (!metaBuilder.Error)
                {
                    if (argsBuilder.ExplicitArgumentCount == 0)
                    {
                        metaBuilder.Result = args.TargetExpression;
                    }
                    else
                    {
                        RubyClass cls = args.RubyContext.GetClassOf(args.Target);
                        var classExpression = Ast.Constant(cls);
                        args.SetTarget(classExpression, cls);

                        ParameterExpression messageVariable = null;

                        // ReinitializeException(new <exception-type>(GetClrMessage(<class>, #message = <message>)), #message)
                        metaBuilder.Result = Ast.Call(null, new Func <Exception, object, Exception>(ReinitializeException).Method,
                                                      cls.MakeAllocatorCall(args, () =>
                                                                            Ast.Call(null, new Func <RubyClass, object, string>(GetClrMessage).Method,
                                                                                     classExpression,
                                                                                     Ast.Assign(messageVariable = metaBuilder.GetTemporary(typeof(object), "#message"), AstFactory.Box(argsBuilder[0]))
                                                                                     )
                                                                            ),
                                                      messageVariable ?? AstFactory.Box(argsBuilder[0])
                                                      );
                    }
                }
            }));
        }
Example #7
0
        internal void SetRule(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args)
        {
            Assert.NotNull(metaBuilder, args);
            Debug.Assert(args.SimpleArgumentCount == 0 && !args.Signature.HasBlock && !args.Signature.HasSplattedArgument && !args.Signature.HasRhsArgument);
            Debug.Assert(args.Signature.HasScope);

            var ec = args.RubyContext;

            // implicit conversions should only depend on a static type:
            if (TryImplicitConversion(metaBuilder, args))
            {
                if (args.Target == null)
                {
                    metaBuilder.AddRestriction(Ast.Equal(args.TargetExpression, Ast.Constant(null, args.TargetExpression.Type)));
                }
                else
                {
                    metaBuilder.AddTypeRestriction(args.Target.GetType(), args.TargetExpression);
                }
                return;
            }

            // check for type version:
            metaBuilder.AddTargetTypeTest(args);

            string     toMethodName            = ToMethodName;
            Expression targetClassNameConstant = Ast.Constant(ec.GetClassOf(args.Target).Name);

            // Kernel#respond_to? method is not overridden => we can optimize
            RubyMemberInfo respondToMethod = ec.ResolveMethod(args.Target, Symbols.RespondTo, true).InvalidateSitesOnOverride();

            if (respondToMethod == null ||
                // the method is defined in library, hasn't been replaced by user defined method (TODO: maybe we should make this check better)
                (respondToMethod.DeclaringModule == ec.KernelModule && respondToMethod is RubyMethodGroupInfo))
            {
                RubyMemberInfo conversionMethod = ec.ResolveMethod(args.Target, toMethodName, false).InvalidateSitesOnOverride();
                if (conversionMethod == null)
                {
                    // error:
                    SetError(metaBuilder, targetClassNameConstant, args);
                    return;
                }
                else
                {
                    // invoke target.to_xxx() and validate it; returns an instance of TTargetType:
                    conversionMethod.BuildCall(metaBuilder, args, toMethodName);

                    if (!metaBuilder.Error && ConversionResultValidator != null)
                    {
                        metaBuilder.Result = ConversionResultValidator.OpCall(targetClassNameConstant, AstFactory.Box(metaBuilder.Result));
                    }
                    return;
                }
            }
            else if (!RubyModule.IsMethodVisible(respondToMethod, false))
            {
                // respond_to? is private:
                SetError(metaBuilder, targetClassNameConstant, args);
                return;
            }

            // slow path: invoke respond_to?, to_xxx and result validation:

            var conversionCallSite = Ast.Dynamic(
                RubyCallAction.Make(toMethodName, RubyCallSignature.WithScope(0)),
                typeof(object),
                args.ScopeExpression, args.TargetExpression
                );

            Expression opCall;

            metaBuilder.Result = Ast.Condition(
                // If

                // respond_to?()
                Methods.IsTrue.OpCall(
                    Ast.Dynamic(
                        RubyCallAction.Make(Symbols.RespondTo, RubyCallSignature.WithScope(1)),
                        typeof(object),
                        args.ScopeExpression, args.TargetExpression, Ast.Constant(SymbolTable.StringToId(toMethodName))
                        )
                    ),

                // Then

                // to_xxx():
                opCall = (ConversionResultValidator == null) ? conversionCallSite :
                         ConversionResultValidator.OpCall(targetClassNameConstant, conversionCallSite),

                // Else

                AstUtils.Convert(
                    (ConversionResultValidator == null) ? args.TargetExpression :
                    Ast.Convert(
                        Ast.Throw(Methods.CreateTypeConversionError.OpCall(targetClassNameConstant, Ast.Constant(TargetTypeName))),
                        typeof(object)
                        ),
                    opCall.Type
                    )
                );
        }