示例#1
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);
  }