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); }