GetMetaBlock() public method

public GetMetaBlock ( ) : DynamicMetaObject
return DynamicMetaObject
Example #1
0
        /// <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>
        public static void RuleControlFlowBuilder(MetaObjectBuilder /*!*/ metaBuilder, CallArguments /*!*/ args)
        {
            if (metaBuilder.Error)
            {
                return;
            }

            var metaBlock = args.GetMetaBlock();

            Debug.Assert(metaBlock != null, "RuleControlFlowBuilder should only be used if the signature has a block");

            // We construct CF only for non-nil blocks thus we need a test for it:
            if (metaBlock.Value == null)
            {
                metaBuilder.AddRestriction(Ast.Equal(metaBlock.Expression, AstUtils.Constant(null)));
                return;
            }

            // don't need to test the exact type of the Proc since the code is subclass agnostic:
            metaBuilder.AddRestriction(Ast.NotEqual(metaBlock.Expression, AstUtils.Constant(null)));
            Expression bfcVariable = metaBuilder.BfcVariable;

            Debug.Assert(bfcVariable != null);

            // 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.
            Expression          resultVariable = metaBuilder.GetTemporary(typeof(object), "#result");
            ParameterExpression unwinder;

            metaBuilder.Result = Ast.Block(
                Ast.Assign(bfcVariable, Methods.CreateBfcForLibraryMethod.OpCall(AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)))),
                AstUtils.Try(
                    Ast.Assign(resultVariable, AstUtils.Convert(metaBuilder.Result, typeof(object)))
                    ).Filter(unwinder = Ast.Parameter(typeof(MethodUnwinder), "#unwinder"),
                             Methods.IsProcConverterTarget.OpCall(bfcVariable, unwinder),

                             Ast.Assign(resultVariable, Ast.Field(unwinder, MethodUnwinder.ReturnValueField)),
                             AstUtils.Default(typeof(object))
                             ).Finally(
                    Methods.LeaveProcConverter.OpCall(bfcVariable)
                    ),
                resultVariable
                );
        }
Example #2
0
        /// <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>
        public static void RuleControlFlowBuilder(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args) {
            if (metaBuilder.Error) {
                return;
            }

            var metaBlock = args.GetMetaBlock();
            Debug.Assert(metaBlock != null, "RuleControlFlowBuilder should only be used if the signature has a block");
            
            // We construct CF only for non-nil blocks thus we need a test for it:
            if (metaBlock.Value == null) {
                metaBuilder.AddRestriction(Ast.Equal(metaBlock.Expression, AstUtils.Constant(null)));
                return;
            }

            // don't need to test the exact type of the Proc since the code is subclass agnostic:
            metaBuilder.AddRestriction(Ast.NotEqual(metaBlock.Expression, AstUtils.Constant(null)));
            Expression bfcVariable = metaBuilder.BfcVariable;
            Debug.Assert(bfcVariable != null);
            
            // 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.
            Expression resultVariable = metaBuilder.GetTemporary(typeof(object), "#result");
            ParameterExpression unwinder;

            metaBuilder.Result = Ast.Block(
                Ast.Assign(bfcVariable, Methods.CreateBfcForLibraryMethod.OpCall(AstUtils.Convert(args.GetBlockExpression(), typeof(Proc)))),
                AstUtils.Try(
                    Ast.Assign(resultVariable, AstUtils.Convert(metaBuilder.Result, typeof(object)))
                ).Filter(unwinder = Ast.Parameter(typeof(MethodUnwinder), "#unwinder"),
                    Methods.IsProcConverterTarget.OpCall(bfcVariable, unwinder),

                    Ast.Assign(resultVariable, Ast.Field(unwinder, MethodUnwinder.ReturnValueField)),
                    AstUtils.Default(typeof(object))
                ).Finally(
                    Methods.LeaveProcConverter.OpCall(bfcVariable)
                ),
                resultVariable
            );
        }
Example #3
0
        // Normalizes arguments: inserts self, expands splats, and inserts rhs arg. 
        // Adds any restrictions/conditions applied to the arguments to the given meta-builder.
        internal static DynamicMetaObject[]/*!*/ NormalizeArguments(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args,
            SelfCallConvention callConvention, bool calleeHasBlockParam, bool injectMissingBlockParam) {

            var result = new List<DynamicMetaObject>();

            // self (instance):
            if (callConvention == SelfCallConvention.SelfIsInstance) {
                result.Add(args.MetaTarget);
            }

            // block:
            if (calleeHasBlockParam) {
                if (args.Signature.HasBlock) {
                    if (args.GetMetaBlock() == null) {
                        // the user explicitly passed nil as a block arg:
                        result.Add(RubyBinder.NullMetaObject);
                    } else {
                        // pass BlockParam:
                        Debug.Assert(metaBuilder.BfcVariable != null);
                        result.Add(new DynamicMetaObject(metaBuilder.BfcVariable, BindingRestrictions.Empty));
                    }
                } else {
                    // no block passed into a method with a BlockParam:
                    result.Add(RubyBinder.NullMetaObject);
                }
            } else if (injectMissingBlockParam) {
                // no block passed into a method w/o a BlockParam (we still need to fill the missing block argument):
                result.Add(RubyBinder.NullMetaObject);
            }

            // self (parameter):
            if (callConvention == SelfCallConvention.SelfIsParameter) {
                result.Add(args.MetaTarget);
            }

            // simple arguments:
            for (int i = 0; i < args.SimpleArgumentCount; i++) {
                result.Add(args.GetSimpleMetaArgument(i));
            }

            // splat argument:
            int listLength;
            ParameterExpression listVariable;
            if (args.Signature.HasSplattedArgument) {
                var splatted = args.GetSplattedMetaArgument();

                if (metaBuilder.AddSplattedArgumentTest(splatted.Value, splatted.Expression, out listLength, out listVariable)) {

                    // AddTestForListArg only returns 'true' if the argument is a List<object>
                    var list = (List<object>)splatted.Value;

                    // get arguments, add tests
                    for (int j = 0; j < listLength; j++) {
                        result.Add(DynamicMetaObject.Create(
                            list[j], 
                            Ast.Call(listVariable, typeof(List<object>).GetMethod("get_Item"), AstUtils.Constant(j))
                        ));
                    }

                } else {
                    // argument is not an array => add the argument itself:
                    result.Add(splatted);
                }
            }

            // rhs argument:
            if (args.Signature.HasRhsArgument) {
                result.Add(args.GetRhsMetaArgument());
            }

            return result.ToArray();
        }
Example #4
0
        /// <summary>
        /// Resolves an library method overload and builds call expression.
        /// The resulting expression on meta-builder doesn't handle block control flow yet.
        /// </summary>
        internal static void BuildCallNoFlow(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name, 
            IList<MethodBase>/*!*/ overloads, SelfCallConvention callConvention) {

            var bindingTarget = ResolveOverload(name, overloads, args, callConvention);
            bool calleeHasBlockParam = bindingTarget.Success && HasBlockParameter(bindingTarget.Method);

            // Allocates a variable holding BlockParam. At runtime the BlockParam is created with a new RFC instance that
            // identifies the library method frame as a proc-converter target of a method unwinder triggered by break from a block.
            if (args.Signature.HasBlock) {
                var metaBlock = args.GetMetaBlock();
                if (metaBlock.Value != null && calleeHasBlockParam) {
                    if (metaBuilder.BfcVariable == null) {
                        metaBuilder.BfcVariable = metaBuilder.GetTemporary(typeof(BlockParam), "#bfc");
                    }
                    metaBuilder.ControlFlowBuilder = RuleControlFlowBuilder;
                }

                // Block test - we need to test for a block regardless of whether it is actually passed to the method or not
                // since the information that the block is not null is used for overload resolution.
                if (metaBlock.Value == null) {
                    metaBuilder.AddRestriction(Ast.Equal(metaBlock.Expression, AstUtils.Constant(null)));
                } else {
                    // don't need to test the exact type of the Proc since the code is subclass agnostic:
                    metaBuilder.AddRestriction(Ast.NotEqual(metaBlock.Expression, AstUtils.Constant(null)));
                }
            }

            var actualArgs = MakeActualArgs(metaBuilder, args, callConvention, calleeHasBlockParam, true);

            if (bindingTarget.Success) {
                var parameterBinder = new RubyParameterBinder(args.RubyContext.Binder, args.MetaContext.Expression, args.Signature.HasScope);
                metaBuilder.Result = bindingTarget.MakeExpression(parameterBinder, actualArgs);
            } else {
                metaBuilder.SetError(args.RubyContext.RubyBinder.MakeInvalidParametersError(bindingTarget).Expression);
            }
        }
Example #5
0
        internal static BindingTarget/*!*/ ResolveOverload(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ name,
            IList<MethodBase>/*!*/ overloads, SelfCallConvention callConvention, out RubyOverloadResolver/*!*/ resolver) {

            resolver = new RubyOverloadResolver(metaBuilder, args, callConvention);
            var bindingTarget = resolver.ResolveOverload(name, overloads, NarrowingLevel.None, NarrowingLevel.All);

            bool calleeHasBlockParam = bindingTarget.Success && HasBlockParameter(bindingTarget.Method);
            
            // At runtime the BlockParam is created with a new RFC instance that identifies the library method frame as 
            // a proc-converter target of a method unwinder triggered by break from a block.
            if (args.Signature.HasBlock) {
                var metaBlock = args.GetMetaBlock();
                if (metaBlock.Value != null && calleeHasBlockParam) {
                    Debug.Assert(metaBuilder.BfcVariable != null);
                    metaBuilder.ControlFlowBuilder = RuleControlFlowBuilder;
                }

                // Overload resolution might not need to distinguish between nil and non-nil block.
                // However, we still do since we construct CF only for non-nil blocks.
                if (metaBlock.Value == null) {
                    metaBuilder.AddRestriction(Ast.Equal(metaBlock.Expression, AstUtils.Constant(null)));
                } else {
                    // don't need to test the exact type of the Proc since the code is subclass agnostic:
                    metaBuilder.AddRestriction(Ast.NotEqual(metaBlock.Expression, AstUtils.Constant(null)));
                }
            }

            // add restrictions used for overload resolution:
            resolver.AddArgumentRestrictions(metaBuilder, bindingTarget);
            
            return bindingTarget;
        }