public static Arity getArity(org.jruby.ast.ArgsNode argsNode) { final String[] keywordArguments; if (argsNode.hasKwargs() && argsNode.getKeywords() != null) { final org.jruby.ast.Node[] keywordNodes = argsNode.getKeywords().children(); final int keywordsCount = keywordNodes.length; keywordArguments = new String[keywordsCount]; for (int i = 0; i < keywordsCount; i++) { final KeywordArgNode kwarg = (KeywordArgNode) keywordNodes[i]; final AssignableNode assignableNode = kwarg.getAssignable(); if (assignableNode instanceof LocalAsgnNode) { keywordArguments[i] = ((LocalAsgnNode) assignableNode).getName(); } else if (assignableNode instanceof DAsgnNode) { keywordArguments[i] = ((DAsgnNode) assignableNode).getName(); } else { throw new UnsupportedOperationException("unsupported keyword arg " + kwarg); } } } else { keywordArguments = Arity.NO_KEYWORDS; } return new Arity( argsNode.getPreCount(), argsNode.getOptionalArgsCount(), argsNode.hasRestArg(), argsNode.getPostCount(), keywordArguments, argsNode.hasKeyRest()); }
private void declareArguments( SourceSection sourceSection, String methodName, SharedMethodInfo sharedMethodInfo) { final ParameterCollector parameterCollector = new ParameterCollector(); argsNode.accept(parameterCollector); for (String parameter : parameterCollector.getParameters()) { environment.declareVar(parameter); } }
public RubyNode doCompileMethodBody( SourceSection sourceSection, String methodName, org.jruby.ast.Node bodyNode, SharedMethodInfo sharedMethodInfo) { declareArguments(sourceSection, methodName, sharedMethodInfo); final Arity arity = getArity(argsNode); final LoadArgumentsTranslator loadArgumentsTranslator = new LoadArgumentsTranslator(currentNode, context, source, false, this); final RubyNode loadArguments = argsNode.accept(loadArgumentsTranslator); RubyNode body; parentSourceSection.push(sourceSection); try { body = translateNodeOrNil(sourceSection, bodyNode); } finally { parentSourceSection.pop(); } final RubyNode prelude; if (usesRubiniusPrimitive) { // Use Rubinius.primitive seems to turn off arity checking. See Time.from_array for example. prelude = loadArguments; } else { prelude = SequenceNode.sequence( context, sourceSection, CheckArityNode.create(context, sourceSection, arity), loadArguments); } body = SequenceNode.sequence(context, body.getSourceSection(), prelude, body); if (environment.getFlipFlopStates().size() > 0) { body = SequenceNode.sequence( context, body.getSourceSection(), initFlipFlopStates(sourceSection), body); } body = new CatchForMethodNode(context, body.getSourceSection(), body, environment.getReturnID()); // TODO(CS, 10-Jan-15) why do we only translate exceptions in methods and not blocks? body = new ExceptionTranslatingNode(context, body.getSourceSection(), body); return body; }
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); }