@Override public RubyNode visitArgsNode(org.jruby.ast.ArgsNode node) { argsNode = node; final SourceSection sourceSection = translate(node.getPosition()); final List<RubyNode> sequence = new ArrayList<>(); int localIndex = 0; if (node.getPre() != null) { state = State.PRE; for (org.jruby.ast.Node arg : node.getPre().childNodes()) { sequence.add(arg.accept(this)); index = ++localIndex; } } if (node.getOptArgs() != null) { // (BlockNode 0, (OptArgNode:a 0, (LocalAsgnNode:a 0, (FixnumNode 0))), ...) state = State.OPT; for (org.jruby.ast.Node arg : node.getOptArgs().childNodes()) { sequence.add(arg.accept(this)); index = ++localIndex; } } if (node.getPost() != null) { state = State.POST; for (org.jruby.ast.Node arg : node.getPost().childNodes()) { sequence.add(arg.accept(this)); index = ++localIndex; } } if (node.getRestArgNode() != null) { methodBodyTranslator.getEnvironment().hasRestParameter = true; sequence.add(node.getRestArgNode().accept(this)); } if (node.getBlock() != null) { sequence.add(node.getBlock().accept(this)); } return SequenceNode.sequence(context, sourceSection, sequence); }
@JRubyMethod(name = "parameters", compat = CompatVersion.RUBY1_9) public IRubyObject parameters(ThreadContext context) { Ruby runtime = context.getRuntime(); RubyArray parms = RubyArray.newEmptyArray(runtime); ArgsNode args; if (!(this.getBlock().getBody() instanceof Interpreted19Block)) { return parms; } // argument names are easily accessible from interpreter RubyArray elem = RubyArray.newEmptyArray(runtime); args = ((Interpreted19Block) this.getBlock().getBody()).getArgs(); // required parameters List<Node> children = new ArrayList(); if (args.getPreCount() > 0) children.addAll(args.getPre().childNodes()); if (args.getPostCount() > 0) children.addAll(args.getPost().childNodes()); Iterator iter = children.iterator(); while (iter.hasNext()) { Node node = (Node) iter.next(); elem = RubyArray.newEmptyArray(runtime); elem.add(RubySymbol.newSymbol(runtime, this.isLambda() ? "req" : "opt")); if (node instanceof ArgumentNode) { elem.add(RubySymbol.newSymbol(runtime, ((ArgumentNode) node).getName())); } parms.add(elem); } // optional parameters if (args.getOptArgs() != null) { children = args.getOptArgs().childNodes(); if (!children.isEmpty()) { iter = children.iterator(); while (iter.hasNext()) { Node node = (Node) iter.next(); elem = RubyArray.newEmptyArray(runtime); elem.add(RubySymbol.newSymbol(runtime, "opt")); if (node instanceof OptArgNode) { elem.add(RubySymbol.newSymbol(runtime, ((OptArgNode) node).getName())); } parms.add(elem); } } } ArgumentNode rest = args.getRestArgNode(); if (rest != null) { elem = RubyArray.newEmptyArray(runtime); elem.add(RubySymbol.newSymbol(runtime, "rest")); elem.add(RubySymbol.newSymbol(runtime, rest.getName())); parms.add(elem); } BlockArgNode blockArg = args.getBlock(); if (blockArg != null) { elem = RubyArray.newEmptyArray(runtime); elem.add(RubySymbol.newSymbol(runtime, "block")); elem.add(RubySymbol.newSymbol(runtime, blockArg.getName())); parms.add(elem); } return parms; }
public BlockDefinitionNode compileBlockNode( SourceSection sourceSection, String methodName, org.jruby.ast.Node bodyNode, SharedMethodInfo sharedMethodInfo, Type type) { declareArguments(sourceSection, methodName, sharedMethodInfo); final Arity arity = getArity(argsNode); final Arity arityForCheck; /* * If you have a block with parameters |a,| Ruby checks the arity as if was minimum 1, maximum 1. That's * counter-intuitive - as you'd expect the anonymous rest argument to cause it to have no maximum. Indeed, * that's how JRuby reports it, and by the look of their failing spec they consider this to be correct. We'll * follow the specs for now until we see a reason to do something else. */ if (argsNode.getRestArgNode() instanceof org.jruby.ast.UnnamedRestArgNode && !((UnnamedRestArgNode) argsNode.getRestArgNode()).isStar()) { arityForCheck = arity.withRest(false); } else { arityForCheck = arity; } final boolean isProc = type == Type.PROC; final LoadArgumentsTranslator loadArgumentsTranslator = new LoadArgumentsTranslator(currentNode, context, source, isProc, this); final RubyNode loadArguments = argsNode.accept(loadArgumentsTranslator); final RubyNode preludeProc; if (shouldConsiderDestructuringArrayArg(arity)) { final RubyNode readArrayNode = new ReadPreArgumentNode( context, sourceSection, 0, MissingArgumentBehaviour.RUNTIME_ERROR); final RubyNode castArrayNode = ArrayCastNodeGen.create(context, sourceSection, readArrayNode); final FrameSlot arraySlot = environment.declareVar(environment.allocateLocalTemp("destructure")); final RubyNode writeArrayNode = new WriteLocalVariableNode(context, sourceSection, castArrayNode, arraySlot); final LoadArgumentsTranslator destructureArgumentsTranslator = new LoadArgumentsTranslator(currentNode, context, source, isProc, this); destructureArgumentsTranslator.pushArraySlot(arraySlot); final RubyNode newDestructureArguments = argsNode.accept(destructureArgumentsTranslator); final RubyNode shouldDestructure = new ShouldDestructureNode( context, sourceSection, new RespondToNode(context, sourceSection, readArrayNode, "to_ary")); final RubyNode arrayWasNotNil = SequenceNode.sequence( context, sourceSection, writeArrayNode, new NotNode( context, sourceSection, new IsNilNode( context, sourceSection, new ReadLocalVariableNode(context, sourceSection, arraySlot)))); final RubyNode shouldDestructureAndArrayWasNotNil = new AndNode(context, sourceSection, shouldDestructure, arrayWasNotNil); preludeProc = new IfNode( context, sourceSection, shouldDestructureAndArrayWasNotNil, newDestructureArguments, loadArguments); } else { preludeProc = loadArguments; } final RubyNode preludeLambda = SequenceNode.sequence( context, sourceSection, CheckArityNode.create(context, sourceSection, arityForCheck), NodeUtil.cloneNode(loadArguments)); RubyNode body; parentSourceSection.push(sourceSection); try { if (argsNode.getBlockLocalVariables() != null && !argsNode.getBlockLocalVariables().isEmpty()) { for (org.jruby.ast.Node var : argsNode.getBlockLocalVariables().children()) { environment.declareVar(((INameNode) var).getName()); } } body = translateNodeOrNil(sourceSection, bodyNode); } finally { parentSourceSection.pop(); } // Procs final RubyNode bodyProc = new CatchForProcNode( context, SequenceNode.enclosing(sourceSection, body.getEncapsulatingSourceSection()), composeBody(preludeProc, NodeUtil.cloneNode(body))); final RubyRootNode newRootNodeForProcs = new RubyRootNode( context, considerExtendingMethodToCoverEnd(bodyProc.getEncapsulatingSourceSection()), environment.getFrameDescriptor(), environment.getSharedMethodInfo(), bodyProc, environment.needsDeclarationFrame()); // Lambdas final RubyNode composed = composeBody(preludeLambda, body /* no copy, last usage */); final RubyNode bodyLambda = new CatchForLambdaNode( context, composed.getEncapsulatingSourceSection(), composed, environment.getReturnID()); final RubyRootNode newRootNodeForLambdas = new RubyRootNode( context, considerExtendingMethodToCoverEnd(bodyLambda.getEncapsulatingSourceSection()), environment.getFrameDescriptor(), environment.getSharedMethodInfo(), bodyLambda, environment.needsDeclarationFrame()); // TODO CS 23-Nov-15 only the second one will get instrumented properly! final CallTarget callTargetAsLambda = Truffle.getRuntime().createCallTarget(newRootNodeForLambdas); final CallTarget callTargetAsProc = Truffle.getRuntime().createCallTarget(newRootNodeForProcs); FrameSlot frameOnStackMarkerSlot; if (frameOnStackMarkerSlotStack.isEmpty()) { frameOnStackMarkerSlot = null; } else { frameOnStackMarkerSlot = frameOnStackMarkerSlotStack.peek(); if (frameOnStackMarkerSlot == BAD_FRAME_SLOT) { frameOnStackMarkerSlot = null; } } return new BlockDefinitionNode( context, newRootNodeForProcs.getEncapsulatingSourceSection(), type, environment.getSharedMethodInfo(), callTargetAsProc, callTargetAsLambda, environment.getBreakID(), frameOnStackMarkerSlot); }