Example #1
0
  @Override
  public IRubyObject call(
      ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
    Ruby runtime = context.getRuntime();
    int callNumber = context.callNumber;

    try {
      pre(context, name, self, Block.NULL_BLOCK, runtime);
      argsNode.checkArgCount(runtime, name, 1);
      argsNode.prepare(context, runtime, self, arg0, Block.NULL_BLOCK);

      return ASTInterpreter.INTERPRET_METHOD(
          runtime,
          context,
          file,
          line,
          getImplementationClass(),
          body,
          name,
          self,
          Block.NULL_BLOCK,
          isTraceable());
    } catch (JumpException.ReturnJump rj) {
      return handleReturn(context, rj, callNumber);
    } catch (JumpException.RedoJump rj) {
      return handleRedo(runtime);
    } catch (JumpException.BreakJump bj) {
      return handleBreak(context, runtime, bj, callNumber);
    } finally {
      post(runtime, context, name);
    }
  }
Example #2
0
  @Override
  public RubyNode visitArgumentNode(org.jruby.ast.ArgumentNode node) {
    final SourceSection sourceSection = translate(node.getPosition());

    final RubyNode readNode;

    if (useArray()) {
      readNode = readArgument(sourceSection, index);
    } else {
      if (state == State.PRE) {
        readNode = readArgument(sourceSection, index);
      } else if (state == State.POST) {
        readNode =
            readArgument(
                sourceSection,
                (argsNode.getPreCount() + argsNode.getOptionalArgsCount() + argsNode.getPostCount())
                    - index
                    - 1);
      } else {
        throw new IllegalStateException();
      }
    }

    final FrameSlot slot =
        methodBodyTranslator.getEnvironment().getFrameDescriptor().findFrameSlot(node.getName());
    return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode);
  }
Example #3
0
  public Arity(
      int required,
      int optional,
      boolean allowsMore,
      boolean hasKeywords,
      boolean hasKeyRest,
      int definedKeywords,
      ArgsNode argsNode) {
    this.required = required;
    this.optional = optional;
    this.allowsMore = allowsMore;
    this.definedKeywords = definedKeywords;
    this.hasKeywords = hasKeywords;
    this.hasKeyRest = hasKeyRest;

    if (argsNode.hasKwargs()) {
      keywordArguments = new ArrayList<>();
      if (argsNode.getKeywords() != null) {
        for (Node node : argsNode.getKeywords().children()) {
          final KeywordArgNode kwarg = (KeywordArgNode) node;
          final AssignableNode assignableNode = kwarg.getAssignable();

          if (assignableNode instanceof LocalAsgnNode) {
            keywordArguments.add(((LocalAsgnNode) assignableNode).getName());
          } else if (assignableNode instanceof DAsgnNode) {
            keywordArguments.add(((DAsgnNode) assignableNode).getName());
          } else {
            throw new UnsupportedOperationException("unsupported keyword arg " + node);
          }
        }
      }
    } else {
      keywordArguments = null;
    }
  }
Example #4
0
  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());
  }
Example #5
0
  private RubyNode translateLocalAssignment(
      ISourcePosition sourcePosition, String name, org.jruby.ast.Node valueNode) {
    final SourceSection sourceSection = translate(sourcePosition);

    final RubyNode readNode;

    if (valueNode instanceof org.jruby.ast.NilImplicitNode) {
      // Multiple assignment

      if (useArray()) {
        readNode =
            ArrayIndexNodeFactory.create(context, sourceSection, index, loadArray(sourceSection));
      } else {
        readNode = readArgument(sourceSection, index);
      }
    } else {
      // Optional argument
      final RubyNode defaultValue = valueNode.accept(this);
      readNode =
          new ReadOptionalArgumentNode(
              context, sourceSection, index, index + argsNode.getPostCount() + 1, defaultValue);
    }

    final FrameSlot slot =
        methodBodyTranslator.getEnvironment().getFrameDescriptor().findFrameSlot(name);
    return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode);
  }
Example #6
0
  @Override
  public RubyNode visitRestArgNode(org.jruby.ast.RestArgNode node) {
    final SourceSection sourceSection = translate(node.getPosition());

    final RubyNode readNode;

    if (useArray()) {
      readNode =
          ArrayGetTailNodeFactory.create(
              context, sourceSection, argsNode.getPreCount(), loadArray(sourceSection));
    } else {
      readNode = new ReadRestArgumentNode(context, sourceSection, argsNode.getPreCount());
    }

    final FrameSlot slot =
        methodBodyTranslator.getEnvironment().getFrameDescriptor().findFrameSlot(node.getName());
    return WriteLocalVariableNodeFactory.create(context, sourceSection, slot, readNode);
  }
Example #7
0
  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);
    }
  }
Example #8
0
 private RubyNode readArgument(SourceSection sourceSection, int index) {
   if (useArray()) {
     return ArrayIndexNodeFactory.create(context, sourceSection, index, loadArray(sourceSection));
   } else {
     if (state == State.PRE) {
       return new ReadPreArgumentNode(
           context,
           sourceSection,
           index,
           isBlock ? MissingArgumentBehaviour.NIL : MissingArgumentBehaviour.RUNTIME_ERROR);
     } else if (state == State.POST) {
       return new ReadPostArgumentNode(
           context,
           sourceSection,
           (argsNode.getPreCount() + argsNode.getOptionalArgsCount() + argsNode.getPostCount())
               - index
               - 1);
     } else {
       throw new IllegalStateException();
     }
   }
 }
Example #9
0
  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;
  }
Example #10
0
 @Override
 public void prepare(
     ThreadContext context, Ruby runtime, IRubyObject self, IRubyObject[] args, Block block) {
   super.prepare(context, runtime, self, args, block);
 }
Example #11
0
 @Override
 public Arity getArity() {
   return argsNode.getArity();
 }
Example #12
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;
  }
Example #13
0
  @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);
  }
Example #14
0
  public void inspect(Node node) {
    if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
      disable();
      // we still inspect since some nodes change state as a result (JRUBY-6836)
    }

    if (node == null) return;

    switch (node.getNodeType()) {
      case ALIASNODE:
        setFlag(node, METHOD);
        break;
      case ANDNODE:
        AndNode andNode = (AndNode) node;
        inspect(andNode.getFirstNode());
        inspect(andNode.getSecondNode());
        break;
      case ARGSCATNODE:
        ArgsCatNode argsCatNode = (ArgsCatNode) node;
        inspect(argsCatNode.getFirstNode());
        inspect(argsCatNode.getSecondNode());
        break;
      case ARGSPUSHNODE:
        ArgsPushNode argsPushNode = (ArgsPushNode) node;
        inspect(argsPushNode.getFirstNode());
        inspect(argsPushNode.getSecondNode());
        break;
      case ARGUMENTNODE:
        break;
      case ARRAYNODE:
      case BLOCKNODE:
      case DREGEXPNODE:
      case DSTRNODE:
      case DSYMBOLNODE:
      case DXSTRNODE:
      case LISTNODE:
        ListNode listNode = (ListNode) node;
        for (int i = 0; i < listNode.size(); i++) {
          inspect(listNode.get(i));
        }
        break;
      case ARGSNODE:
        ArgsNode argsNode = (ArgsNode) node;
        if (argsNode.getBlock() != null) setFlag(node, BLOCK_ARG);
        if (argsNode.getOptArgs() != null) {
          setFlag(node, OPT_ARGS);
          inspect(argsNode.getOptArgs());
        }
        if (argsNode.getRestArg() == -2 || argsNode.getRestArg() >= 0) setFlag(node, REST_ARG);
        break;
      case ATTRASSIGNNODE:
        AttrAssignNode attrAssignNode = (AttrAssignNode) node;
        inspect(attrAssignNode.getArgsNode());
        inspect(attrAssignNode.getReceiverNode());
        break;
      case BACKREFNODE:
        setFlag(node, BACKREF);
        break;
      case BEGINNODE:
        inspect(((BeginNode) node).getBodyNode());
        break;
      case BIGNUMNODE:
        break;
      case BINARYOPERATORNODE:
        BinaryOperatorNode binaryOperatorNode = (BinaryOperatorNode) node;
        inspect(binaryOperatorNode.getFirstNode());
        inspect(binaryOperatorNode.getSecondNode());
        break;
      case BLOCKARGNODE:
        break;
      case BLOCKPASSNODE:
        BlockPassNode blockPassNode = (BlockPassNode) node;
        inspect(blockPassNode.getArgsNode());
        inspect(blockPassNode.getBodyNode());
        break;
      case BREAKNODE:
        inspect(((BreakNode) node).getValueNode());
        break;
      case CALLNODE:
        CallNode callNode = (CallNode) node;
        inspect(callNode.getReceiverNode());
        // check for Proc.new, an especially magic method
        if (callNode.getName() == "new"
            && callNode.getReceiverNode() instanceof ConstNode
            && ((ConstNode) callNode.getReceiverNode()).getName() == "Proc") {
          // Proc.new needs the caller's block to instantiate a proc
          setFlag(node, FRAME_BLOCK);
        }
        if (callNode.getArgsNode() == null && callNode.getIterNode() == null) {
          switch (callNode.getReceiverNode().getNodeType()) {
              // no unary methods on literal numbers, symbols, or strings have frame/scope effects
            case FIXNUMNODE:
            case FLOATNODE:
            case BIGNUMNODE:
            case STRNODE:
            case SYMBOLNODE:
              return;
          }
        }
      case FCALLNODE:
        inspect(((IArgumentNode) node).getArgsNode());
        inspect(((BlockAcceptingNode) node).getIterNode());
      case VCALLNODE:
        INameNode nameNode = (INameNode) node;
        if (FRAME_AWARE_METHODS.contains(nameNode.getName())) {
          setFlag(node, FRAME_AWARE);
          if (nameNode.getName().indexOf("eval") != -1) {
            setFlag(node, EVAL);
          }
        }
        if (SCOPE_AWARE_METHODS.contains(nameNode.getName())) {
          setFlag(node, SCOPE_AWARE);
        }
        break;
      case CASENODE:
        CaseNode caseNode = (CaseNode) node;
        inspect(caseNode.getCaseNode());
        if (caseNode.getCases().size() > Options.COMPILE_OUTLINE_CASECOUNT.load()) {
          // if more than N cases, disable; we'll compile them as separate bodies
          // see BaseBodyCompiler#compiledSequencedConditional and ASTCompiler#compileCase
          disable();
          return;
        } else {
          for (Node when : caseNode.getCases().childNodes()) {
            inspect(when);
          }
          inspect(caseNode.getElseNode());
        }
        break;
      case CLASSNODE:
        setFlag(node, CLASS);
        ClassNode classNode = (ClassNode) node;
        inspect(classNode.getCPath());
        inspect(classNode.getSuperNode());
        break;
      case CLASSVARNODE:
        setFlag(node, CLASS_VAR);
        break;
      case CONSTDECLNODE:
        inspect(((AssignableNode) node).getValueNode());
        setFlag(node, CONSTANT);
        break;
      case CLASSVARASGNNODE:
        inspect(((AssignableNode) node).getValueNode());
        setFlag(node, CLASS_VAR);
        break;
      case CLASSVARDECLNODE:
        inspect(((AssignableNode) node).getValueNode());
        setFlag(node, CLASS_VAR);
        break;
      case COLON2NODE:
        inspect(((Colon2Node) node).getLeftNode());
        break;
      case COLON3NODE:
        break;
      case CONSTNODE:
        setFlag(node, CONSTANT);
        break;
      case DEFNNODE:
      case DEFSNODE:
        setFlag(node, METHOD);
        setFlag(node, FRAME_VISIBILITY);
        setFlag(node, SCOPE_AWARE);
        break;
      case DEFINEDNODE:
        switch (((DefinedNode) node).getExpressionNode().getNodeType()) {
          case CLASSVARASGNNODE:
          case CLASSVARDECLNODE:
          case CONSTDECLNODE:
          case DASGNNODE:
          case GLOBALASGNNODE:
          case LOCALASGNNODE:
          case MULTIPLEASGNNODE:
          case OPASGNNODE:
          case OPELEMENTASGNNODE:
          case DVARNODE:
          case FALSENODE:
          case TRUENODE:
          case LOCALVARNODE:
          case INSTVARNODE:
          case BACKREFNODE:
          case SELFNODE:
          case VCALLNODE:
          case YIELDNODE:
          case GLOBALVARNODE:
          case CONSTNODE:
          case FCALLNODE:
          case CLASSVARNODE:
            // ok, we have fast paths
            inspect(((DefinedNode) node).getExpressionNode());
            break;
          default:
            // long, slow way causes disabling
            // we still inspect because some nodes may change state (JRUBY-6836)
            inspect(((DefinedNode) node).getExpressionNode());
            disable();
        }
        break;
      case DOTNODE:
        DotNode dotNode = (DotNode) node;
        inspect(dotNode.getBeginNode());
        inspect(dotNode.getEndNode());
        break;
      case DASGNNODE:
        inspect(((AssignableNode) node).getValueNode());
        break;
      case DVARNODE:
        break;
      case ENSURENODE:
        inspect(((EnsureNode) node).getBodyNode());
        inspect(((EnsureNode) node).getEnsureNode());
        disable();
        break;
      case ENCODINGNODE:
        break;
      case EVSTRNODE:
        inspect(((EvStrNode) node).getBody());
        break;
      case FALSENODE:
        break;
      case FIXNUMNODE:
        break;
      case FLIPNODE:
        inspect(((FlipNode) node).getBeginNode());
        inspect(((FlipNode) node).getEndNode());
        break;
      case FLOATNODE:
        break;
      case FORNODE:
        setFlag(node, CLOSURE);
        setFlag(node, SCOPE_AWARE);
        inspect(((ForNode) node).getIterNode());
        inspect(((ForNode) node).getBodyNode());
        inspect(((ForNode) node).getVarNode());
        break;
      case GLOBALASGNNODE:
        GlobalAsgnNode globalAsgnNode = (GlobalAsgnNode) node;
        if (globalAsgnNode.getName().equals("$_")) {
          setFlag(node, LASTLINE);
        } else if (globalAsgnNode.getName().equals("$~")) {
          setFlag(node, BACKREF);
        }
        inspect(globalAsgnNode.getValueNode());
        break;
      case GLOBALVARNODE:
        {
          String name = ((GlobalVarNode) node).getName();
          if (name.equals("$_") || name.equals("$LAST_READ_LINE")) {
            setFlag(node, LASTLINE);
          } else if (name.equals("$~")
              || name.equals("$`")
              || name.equals("$'")
              || name.equals("$+")
              || name.equals("$LAST_MATCH_INFO")
              || name.equals("$PREMATCH")
              || name.equals("$POSTMATCH")
              || name.equals("$LAST_PAREN_MATCH")) {
            setFlag(node, BACKREF);
          }
          break;
        }
      case HASHNODE:
        HashNode hashNode = (HashNode) node;
        inspect(hashNode.getListNode());
        break;
      case IFNODE:
        IfNode ifNode = (IfNode) node;
        inspect(ifNode.getCondition());
        inspect(ifNode.getThenBody());
        inspect(ifNode.getElseBody());
        break;
      case INSTASGNNODE:
        inspect(((AssignableNode) node).getValueNode());
        break;
      case INSTVARNODE:
        break;
      case ISCOPINGNODE:
        IScopingNode iscopingNode = (IScopingNode) node;
        inspect(iscopingNode.getCPath());
        break;
      case ITERNODE:
        setFlag(node, CLOSURE);
        break;
      case LAMBDANODE:
        setFlag(node, CLOSURE);
        break;
      case LOCALASGNNODE:
        LocalAsgnNode localAsgnNode = (LocalAsgnNode) node;
        if (PRAGMAS.contains(localAsgnNode.getName())) {
          if (localAsgnNode.getName().equals("__NOFRAME__")) {
            noFrame = localAsgnNode.getValueNode() instanceof TrueNode;
          }
          break;
        }
        inspect(localAsgnNode.getValueNode());
        break;
      case LOCALVARNODE:
        break;
      case MATCHNODE:
        inspect(((MatchNode) node).getRegexpNode());
        setFlag(node, BACKREF);
        break;
      case MATCH2NODE:
        Match2Node match2Node = (Match2Node) node;
        inspect(match2Node.getReceiverNode());
        inspect(match2Node.getValueNode());
        setFlag(node, BACKREF);
        if (match2Node instanceof Match2CaptureNode) {
          // additionally need scope, to set local vars
          // FIXME: this can be done without heap scope
          setFlag(node, SCOPE_AWARE);
        }
        break;
      case MATCH3NODE:
        Match3Node match3Node = (Match3Node) node;
        inspect(match3Node.getReceiverNode());
        inspect(match3Node.getValueNode());
        setFlag(node, BACKREF);
        break;
      case MODULENODE:
        setFlag(node, CLASS);
        inspect(((ModuleNode) node).getCPath());
        break;
      case MULTIPLEASGN19NODE:
        MultipleAsgn19Node multipleAsgn19Node = (MultipleAsgn19Node) node;
        inspect(multipleAsgn19Node.getPre());
        inspect(multipleAsgn19Node.getPost());
        inspect(multipleAsgn19Node.getRest());
        inspect(multipleAsgn19Node.getValueNode());
        break;
      case MULTIPLEASGNNODE:
        MultipleAsgnNode multipleAsgnNode = (MultipleAsgnNode) node;
        inspect(multipleAsgnNode.getArgsNode());
        inspect(multipleAsgnNode.getHeadNode());
        inspect(multipleAsgnNode.getValueNode());
        break;
      case NEWLINENODE:
        inspect(((NewlineNode) node).getNextNode());
        break;
      case NEXTNODE:
        inspect(((NextNode) node).getValueNode());
        break;
      case NILNODE:
        break;
      case NOTNODE:
        inspect(((NotNode) node).getConditionNode());
        break;
      case NTHREFNODE:
        break;
      case OPASGNANDNODE:
        OpAsgnAndNode opAsgnAndNode = (OpAsgnAndNode) node;
        inspect(opAsgnAndNode.getFirstNode());
        inspect(opAsgnAndNode.getSecondNode());
        break;
      case OPASGNNODE:
        OpAsgnNode opAsgnNode = (OpAsgnNode) node;
        inspect(opAsgnNode.getReceiverNode());
        inspect(opAsgnNode.getValueNode());
        break;
      case OPASGNORNODE:
        switch (((OpAsgnOrNode) node).getFirstNode().getNodeType()) {
          case CLASSVARASGNNODE:
          case CLASSVARDECLNODE:
          case CONSTDECLNODE:
          case DASGNNODE:
          case GLOBALASGNNODE:
          case LOCALASGNNODE:
          case MULTIPLEASGNNODE:
          case OPASGNNODE:
          case OPELEMENTASGNNODE:
          case DVARNODE:
          case FALSENODE:
          case TRUENODE:
          case LOCALVARNODE:
          case INSTVARNODE:
          case BACKREFNODE:
          case SELFNODE:
          case VCALLNODE:
          case YIELDNODE:
          case GLOBALVARNODE:
          case CONSTNODE:
          case FCALLNODE:
          case CLASSVARNODE:
            // ok, we have fast paths
            inspect(((OpAsgnOrNode) node).getSecondNode());
            break;
          default:
            // long, slow way causes disabling for defined
            inspect(((OpAsgnOrNode) node).getFirstNode());
            inspect(((OpAsgnOrNode) node).getSecondNode());
            disable();
        }
        break;
      case OPELEMENTASGNNODE:
        OpElementAsgnNode opElementAsgnNode = (OpElementAsgnNode) node;
        inspect(opElementAsgnNode.getArgsNode());
        inspect(opElementAsgnNode.getReceiverNode());
        inspect(opElementAsgnNode.getValueNode());
        break;
      case OPTARGNODE:
        inspect(((OptArgNode) node).getValue());
        break;
      case ORNODE:
        OrNode orNode = (OrNode) node;
        inspect(orNode.getFirstNode());
        inspect(orNode.getSecondNode());
        break;
      case POSTEXENODE:
        PostExeNode postExeNode = (PostExeNode) node;
        setFlag(node, CLOSURE);
        setFlag(node, SCOPE_AWARE);
        inspect(postExeNode.getBodyNode());
        inspect(postExeNode.getVarNode());
        break;
      case PREEXENODE:
        PreExeNode preExeNode = (PreExeNode) node;
        setFlag(node, CLOSURE);
        setFlag(node, SCOPE_AWARE);
        inspect(preExeNode.getBodyNode());
        inspect(preExeNode.getVarNode());
        break;
      case REDONODE:
        break;
      case REGEXPNODE:
        break;
      case ROOTNODE:
        inspect(((RootNode) node).getBodyNode());
        if (((RootNode) node).getBodyNode() instanceof BlockNode) {
          BlockNode blockNode = (BlockNode) ((RootNode) node).getBodyNode();
          if (blockNode.size() > 500) {
            // method has more than 500 lines; we'll need to split it
            // and therefore need to use a heap-based scope
            setFlag(node, SCOPE_AWARE);
          }
        }
        break;
      case RESCUEBODYNODE:
        RescueBodyNode rescueBody = (RescueBodyNode) node;
        inspect(rescueBody.getExceptionNodes());
        inspect(rescueBody.getBodyNode());
        inspect(rescueBody.getOptRescueNode());
        break;
      case RESCUENODE:
        RescueNode rescueNode = (RescueNode) node;
        inspect(rescueNode.getBodyNode());
        inspect(rescueNode.getElseNode());
        inspect(rescueNode.getRescueNode());
        disable();
        break;
      case RETRYNODE:
        setFlag(node, RETRY);
        break;
      case RETURNNODE:
        inspect(((ReturnNode) node).getValueNode());
        break;
      case SCLASSNODE:
        setFlag(node, CLASS);
        setFlag(node, FRAME_AWARE);
        SClassNode sclassNode = (SClassNode) node;
        inspect(sclassNode.getReceiverNode());
        break;
      case SCOPENODE:
        break;
      case SELFNODE:
        break;
      case SPLATNODE:
        inspect(((SplatNode) node).getValue());
        break;
      case STARNODE:
        break;
      case STRNODE:
        break;
      case SUPERNODE:
        SuperNode superNode = (SuperNode) node;
        inspect(superNode.getArgsNode());
        inspect(superNode.getIterNode());
        setFlag(node, SUPER);
        break;
      case SVALUENODE:
        inspect(((SValueNode) node).getValue());
        break;
      case SYMBOLNODE:
        break;
      case TOARYNODE:
        inspect(((ToAryNode) node).getValue());
        break;
      case TRUENODE:
        break;
      case UNDEFNODE:
        setFlag(node, METHOD);
        break;
      case UNTILNODE:
        UntilNode untilNode = (UntilNode) node;
        ASTInspector untilInspector =
            subInspect(untilNode.getConditionNode(), untilNode.getBodyNode());
        // a while node could receive non-local flow control from any of these:
        // * a closure within the loop
        // * an eval within the loop
        // * a block-arg-based proc called within the loop
        if (untilInspector.getFlag(CLOSURE) || untilInspector.getFlag(EVAL)) {
          untilNode.containsNonlocalFlow = true;

          // we set scope-aware to true to force heap-based locals
          setFlag(node, SCOPE_AWARE);
        }
        integrate(untilInspector);
        break;
      case VALIASNODE:
        break;
      case WHENNODE:
        {
          inspect(((WhenNode) node).getBodyNode());
          inspect(((WhenNode) node).getExpressionNodes());
          inspect(((WhenNode) node).getNextCase());
          // if any elements are not literals or are regexp, set backref
          Node expr = ((WhenNode) node).getExpressionNodes();
          if (!(expr instanceof ILiteralNode) || expr.getNodeType() == NodeType.REGEXPNODE) {
            setFlag(node, BACKREF);
          }
          break;
        }
      case WHILENODE:
        WhileNode whileNode = (WhileNode) node;
        ASTInspector whileInspector =
            subInspect(whileNode.getConditionNode(), whileNode.getBodyNode());
        // a while node could receive non-local flow control from any of these:
        // * a closure within the loop
        // * an eval within the loop
        // * a block-arg-based proc called within the loop
        // * any case that disables optimization, like rescues and ensures
        if (whileInspector.getFlag(CLOSURE) || whileInspector.getFlag(EVAL) || getFlag(BLOCK_ARG)) {
          whileNode.containsNonlocalFlow = true;

          // we set scope-aware to true to force heap-based locals
          setFlag(node, SCOPE_AWARE);
        }
        integrate(whileInspector);
        break;
      case XSTRNODE:
        break;
      case YIELDNODE:
        inspect(((YieldNode) node).getArgsNode());
        break;
      case ZARRAYNODE:
        break;
      case ZEROARGNODE:
        break;
      case ZSUPERNODE:
        setFlag(node, SCOPE_AWARE);
        setFlag(node, ZSUPER);
        inspect(((ZSuperNode) node).getIterNode());
        break;
      default:
        // encountered a node we don't recognize, set everything to true to disable optz
        assert false : "All nodes should be accounted for in AST inspector: " + node;
        disable();
    }
  }
  private static String calculateFilename(ArgsNode argsNode, Node bodyNode) {
    if (bodyNode != null) return bodyNode.getPosition().getFile();
    if (argsNode != null) return argsNode.getPosition().getFile();

    return "__eval__";
  }
Example #16
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);
  }