示例#1
0
  @Override
  public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {
    final SourceSection sourceSection = translate(node.getPosition());

    // (MultipleAsgn19Node 0, (ArrayNode 0, (LocalAsgnNode:a 0, ), (LocalAsgnNode:b 0, )), null,
    // null))

    final int arrayIndex = index;

    final String arrayName = methodBodyTranslator.getEnvironment().allocateLocalTemp("destructure");
    final FrameSlot arraySlot = methodBodyTranslator.getEnvironment().declareVar(arrayName);

    pushArraySlot(arraySlot);

    final List<org.jruby.ast.Node> childNodes = node.childNodes().get(0).childNodes();

    final List<RubyNode> notNilSequence = new ArrayList<>();

    for (int n = 0; n < childNodes.size(); n++) {
      index = n;
      notNilSequence.add(childNodes.get(n).accept(this));
    }

    final RubyNode notNil = SequenceNode.sequence(context, sourceSection, notNilSequence);

    popArraySlot(arraySlot);

    final List<RubyNode> nilSequence = new ArrayList<>();

    if (!childNodes.isEmpty()) {
      // We haven't pushed a new array slot, so this will read the value which we couldn't convert
      // to an array into the first destructured argument
      index = arrayIndex;
      nilSequence.add(childNodes.get(0).accept(this));
    }

    final RubyNode nil = SequenceNode.sequence(context, sourceSection, nilSequence);

    return SequenceNode.sequence(
        context,
        sourceSection,
        WriteLocalVariableNodeFactory.create(
            context,
            sourceSection,
            arraySlot,
            ArrayCastNodeFactory.create(
                context, sourceSection, readArgument(sourceSection, arrayIndex))),
        new IfNode(
            context,
            sourceSection,
            BooleanCastNodeFactory.create(
                context,
                sourceSection,
                new IsNilNode(
                    context,
                    sourceSection,
                    ReadLocalVariableNodeFactory.create(context, sourceSection, arraySlot))),
            nil,
            notNil));
  }
示例#2
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);
  }