private Expression/*!*/ MakeArgumentSplatWithWarning(MetaObjectBuilder/*!*/ rule, object arg, Expression/*!*/ parameter, Expression/*!*/ codeContextExpression) { int listLength; ParameterExpression listVariable; if (ArgsBuilder.AddTestForListArg(rule, arg, parameter, out listLength, out listVariable)) { if (listLength == 0) { // return nil argument + Warning return AddWarning(codeContextExpression, Ast.Constant(null)); } else if (listLength == 1) { // return the only item of the array: return Ast.Call( listVariable, typeof(List<object>).GetMethod("get_Item"), Ast.Constant(0) ); } else { // return the array itself + Warning: return AddWarning(codeContextExpression, parameter); } } else { // not an array, return the value: return parameter; } }
private Expression/*!*/ MakeArgumentUnsplat(MetaObjectBuilder/*!*/ rule, object arg, Expression/*!*/ parameter) { int listLength; ParameterExpression listVariable; if (ArgsBuilder.AddTestForListArg(rule, arg, parameter, out listLength, out listVariable)) { // an array, return: return parameter; } else { // not an array, wrap: return AstFactory.OptimizedOpCall("MakeArray", parameter); } }
private void AddBlockArguments(MetaObjectBuilder/*!*/ rule, List<Expression>/*!*/ actualArgs, CallArguments/*!*/ args, int parameterIndex) { while (parameterIndex < args.Length) { switch (args.GetArgumentKind(parameterIndex)) { case ArgumentKind.Simple: actualArgs.Add(args.Expressions[parameterIndex]); break; case ArgumentKind.List: ArgsBuilder.SplatListToArguments(rule, actualArgs, args.Values[parameterIndex], args.Expressions[parameterIndex], false); break; case ArgumentKind.Instance: case ArgumentKind.Block: default: throw new NotImplementedException(); } parameterIndex++; } }
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, Parameters.Mandatory.Length, Parameters.LeadingMandatoryCount, Parameters.Optional.Length, Parameters.Unsplat != null); argsBuilder.SetImplicit(0, AstUtils.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] = AstUtils.Box(boxedArguments[i]); } var method = GetDelegate(); 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); } }
/// <summary> /// Implements Class#allocate feature. /// </summary> public void BuildObjectAllocation(MetaObjectBuilder/*!*/ metaBuilder, CallArguments/*!*/ args, string/*!*/ methodName) { // check for empty arguments (handles splat correctly): var argsBuilder = new ArgsBuilder(0, 0, 0, 0, false); argsBuilder.AddCallArguments(metaBuilder, args); if (!metaBuilder.Error) { if (!BuildAllocatorCall(metaBuilder, args, () => AstUtils.Constant(Name))) { metaBuilder.SetError(Methods.MakeAllocatorUndefinedError.OpCall(Ast.Convert(args.TargetExpression, typeof(RubyClass)))); } } }
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, _body.HasUnsplatParameter); argsBuilder.SetImplicit(0, AstUtils.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] = AstUtils.Box(boxedArguments[i]); } var method = GetDelegate(); 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); } }
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 = AstUtils.Constant(cls); args.SetTarget(classExpression, cls); ParameterExpression messageVariable = null; // new <exception-type>(GetClrMessage(<class>, #message = <message>)) cls.BuildAllocatorCall(metaBuilder, args, () => Ast.Call(null, new Func<RubyClass, object, string>(GetClrMessage).Method, classExpression, Ast.Assign(messageVariable = metaBuilder.GetTemporary(typeof(object), "#message"), AstFactory.Box(argsBuilder[0])) ) ); if (!metaBuilder.Error) { // ReinitializeException(<result>, #message) metaBuilder.Result = Ast.Call(null, new Func<RubyContext, Exception, object, Exception>(ReinitializeException).Method, args.ContextExpression, metaBuilder.Result, messageVariable ?? AstFactory.Box(argsBuilder[0]) ); } } } }); }
private void SetCallRuleArguments( Expression/*!*/ blockParameterExpression, // special arg #0 Expression/*!*/ selfParameterExpression, // special arg #1 CallArguments/*!*/ args, // user args Expression/*!*/ codeContextExpression, MetaObjectBuilder/*!*/ rule, ArgsBuilder/*!*/ actualArgs) { // mandatory args: actualArgs.Add(blockParameterExpression); actualArgs.Add(selfParameterExpression); int parameterIndex = 0; // mimics CompoundLeftValue.TransformWrite // // L(1,-)? bool leftOneNone = OptionalParamCount == 1 && !HasParamsArray; // L(0,*)? bool leftNoneSplat = OptionalParamCount == 0 && HasParamsArray; // R(0,*)? bool rightNoneSplat = !args.Signature.IsSimple && args.Length == 1 && args.GetArgumentKind(0) == ArgumentKind.List; // R(1,-)? bool rightOneNone = !args.Signature.IsSimple && args.Length == 1 && args.GetArgumentKind(0) == ArgumentKind.Simple || args.Signature.IsSimple && args.Length == 1; // R(1,*)? bool rightOneSplat = !args.Signature.IsSimple && args.Length == 2 && args.GetArgumentKind(0) == ArgumentKind.Simple && args.GetArgumentKind(1) == ArgumentKind.List; // R(0,-)? bool rightNoneNone = args.Length == 0; if (leftOneNone) { Expression rvalue; if (rightOneNone) { // simple assignment rvalue = args.Expressions[parameterIndex]; } else if (rightOneSplat && TestEmptyList(rule, args.Values[parameterIndex + 1], args.Expressions[parameterIndex + 1])) { // simple assignment if the splatted value is an empty array: rvalue = args.Expressions[parameterIndex]; } else if (rightNoneNone) { // nil assignment rvalue = AddWarning(codeContextExpression, AstUtils.Constant(null)); } else if (rightNoneSplat) { // Splat(RHS[*]): rvalue = MakeArgumentSplatWithWarning(rule, args.Values[parameterIndex], args.Expressions[parameterIndex], codeContextExpression); } else { // more than one argument -> pack to an array + warning // MakeArray(RHS) + SplatAppend(RHS*): List<Expression> arguments = new List<Expression>(); AddBlockArguments(rule, arguments, args, parameterIndex); rvalue = AddWarning(codeContextExpression, ArgsBuilder.MakeArgsArray(arguments)); } actualArgs.Add(rvalue); } else { // R(0,*) || R(1,-) && !L(0,*) ==> CompoundLeftValue.TransformWrite does Unsplat, MakeArray otherwise. // // However, we are not constructing a materalized resulting array (contrary to CompoundLeftValue.TransformWrite). // The resulting array is comprised of slots on the stack (loaded to the formal parameters of the block #1, ..., #n). // Therefore, we effectively need to take items of imaginary Unsplat's result and put them into the actualArgs as arguments. // // Unsplat of x makes an array containing x if x is not an array, otherwise it returns x. // So, we just need to take elements of x and push them onto the stack. // List<Expression> arguments = new List<Expression>(); if (rightNoneSplat) { ArgsBuilder.SplatListToArguments(rule, arguments, args.Values[parameterIndex], args.Expressions[parameterIndex], false); } else if (rightOneNone && !leftNoneSplat) { ArgsBuilder.SplatListToArguments(rule, arguments, args.Values[parameterIndex], args.Expressions[parameterIndex], true); } else { AddBlockArguments(rule, arguments, args, parameterIndex); } actualArgs.AddRange(arguments); } actualArgs.AddForEachMissingArgument(delegate() { return AstUtils.Constant(null); }); if (HasParamsArray) { actualArgs.AddParamsArray(); } }
private bool TestEmptyList(MetaObjectBuilder/*!*/ rule, object arg, Expression/*!*/ parameter) { int listLength; ParameterExpression listVariable; return ArgsBuilder.AddTestForListArg(rule, arg, parameter, out listLength, out listVariable) && listLength == 0; }
private void SetCallRuleArguments( Expression/*!*/ blockParameterExpression, // special arg #0 Expression/*!*/ selfParameterExpression, // special arg #1 CallArguments/*!*/ args, // user args Expression/*!*/ codeContextExpression, MetaObjectBuilder/*!*/ rule, ArgsBuilder/*!*/ actualArgs) { // mandatory args: actualArgs.Add(blockParameterExpression); actualArgs.Add(selfParameterExpression); int parameterIndex = 0; // mimics CompoundLeftValue.TransformWrite // // L(1,-)? bool leftOneNone = OptionalParamCount == 1 && !HasParamsArray; // L(0,*)? bool leftNoneSplat = OptionalParamCount == 0 && HasParamsArray; // R(0,*)? bool rightNoneSplat = !args.Signature.IsSimple && args.Length == 1 && args.GetArgumentKind(0) == ArgumentKind.List; // R(1,-)? bool rightOneNone = !args.Signature.IsSimple && args.Length == 1 && args.GetArgumentKind(0) == ArgumentKind.Simple || args.Signature.IsSimple && args.Length == 1; // R(1,*)? bool rightOneSplat = !args.Signature.IsSimple && args.Length == 2 && args.GetArgumentKind(0) == ArgumentKind.Simple && args.GetArgumentKind(1) == ArgumentKind.List; // R(0,-)? bool rightNoneNone = args.Length == 0; if (leftOneNone) { Expression rvalue; if (rightOneNone) { // simple assignment rvalue = args.Expressions[parameterIndex]; } else if (rightOneSplat && TestEmptyList(rule, args.Values[parameterIndex + 1], args.Expressions[parameterIndex + 1])) { // simple assignment if the splatted value is an empty array: rvalue = args.Expressions[parameterIndex]; } else if (rightNoneNone) { // nil assignment rvalue = AddWarning(codeContextExpression, Ast.Constant(null)); } else if (rightNoneSplat) { // Splat(RHS[*]): rvalue = MakeArgumentSplatWithWarning(rule, args.Values[parameterIndex], args.Expressions[parameterIndex], codeContextExpression); } else { // more than one argument -> pack to an array + warning // MakeArray(RHS) + SplatAppend(RHS*): List<Expression> arguments = new List<Expression>(); AddBlockArguments(rule, arguments, args, parameterIndex); rvalue = AddWarning(codeContextExpression, ArgsBuilder.MakeArgsArray(arguments)); } actualArgs.Add(rvalue); } else { // R(0,*) || R(1,-) && !L(0,*) ==> CompoundLeftValue.TransformWrite does Unsplat, MakeArray otherwise. // // However, we are not constructing a materalized resulting array (contrary to CompoundLeftValue.TransformWrite). // The resulting array is comprised of slots on the stack (loaded to the formal parameters of the block #1, ..., #n). // Therefore, we effectively need to take items of imaginary Unsplat's result and put them into the actualArgs as arguments. // // Unsplat of x makes an array containing x if x is not an array, otherwise it returns x. // So, we just need to take elements of x and push them onto the stack. // List<Expression> arguments = new List<Expression>(); if (rightNoneSplat) { ArgsBuilder.SplatListToArguments(rule, arguments, args.Values[parameterIndex], args.Expressions[parameterIndex], false); } else if (rightOneNone && !leftNoneSplat) { ArgsBuilder.SplatListToArguments(rule, arguments, args.Values[parameterIndex], args.Expressions[parameterIndex], true); } else { AddBlockArguments(rule, arguments, args, parameterIndex); } actualArgs.AddRange(arguments); } actualArgs.AddForEachMissingArgument(delegate() { return Ast.Constant(null); }); if (HasParamsArray) { actualArgs.AddParamsArray(); } }