@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);
  }
Exemple #2
0
  @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;
  }
Exemple #3
0
  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);
  }