RangeNodes.java

  1. /*
  2.  * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This
  3.  * code is released under a tri EPL/GPL/LGPL license. You can use it,
  4.  * redistribute it and/or modify it under the terms of the:
  5.  *
  6.  * Eclipse Public License version 1.0
  7.  * GNU General Public License version 2
  8.  * GNU Lesser General Public License version 2.1
  9.  */
  10. package org.jruby.truffle.nodes.core;

  11. import com.oracle.truffle.api.CompilerDirectives;
  12. import com.oracle.truffle.api.dsl.Specialization;
  13. import com.oracle.truffle.api.frame.VirtualFrame;
  14. import com.oracle.truffle.api.source.SourceSection;
  15. import com.oracle.truffle.api.utilities.BranchProfile;
  16. import org.jruby.truffle.nodes.RubyNode;
  17. import org.jruby.truffle.nodes.RubyRootNode;
  18. import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
  19. import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
  20. import org.jruby.truffle.runtime.RubyContext;
  21. import org.jruby.truffle.runtime.control.BreakException;
  22. import org.jruby.truffle.runtime.control.NextException;
  23. import org.jruby.truffle.runtime.control.RedoException;
  24. import org.jruby.truffle.runtime.core.RubyArray;
  25. import org.jruby.truffle.runtime.core.RubyProc;
  26. import org.jruby.truffle.runtime.core.RubyRange;
  27. import org.jruby.truffle.runtime.core.RubyString;

  28. @CoreClass(name = "Range")
  29. public abstract class RangeNodes {

  30.     @CoreMethod(names = "==", required = 1)
  31.     public abstract static class EqualNode extends CoreMethodNode {

  32.         @Child protected KernelNodes.SameOrEqualNode equalNode;

  33.         public EqualNode(RubyContext context, SourceSection sourceSection) {
  34.             super(context, sourceSection);
  35.         }

  36.         public EqualNode(EqualNode prev) {
  37.             super(prev);
  38.         }

  39.         protected boolean equal(VirtualFrame frame, Object a, Object b) {
  40.             if (equalNode == null) {
  41.                 CompilerDirectives.transferToInterpreterAndInvalidate();
  42.                 equalNode = insert(KernelNodesFactory.SameOrEqualNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{null, null}));
  43.             }
  44.             return equalNode.executeSameOrEqual(frame, a, b);

  45.         }

  46.         @Specialization
  47.         public boolean equal(RubyRange.IntegerFixnumRange a, RubyRange.IntegerFixnumRange b) {
  48.             notDesignedForCompilation();

  49.             return a.doesExcludeEnd() == b.doesExcludeEnd() && a.getBegin() == b.getBegin() && a.getEnd() == b.getEnd();
  50.         }

  51.         @Specialization
  52.         public boolean equal(RubyRange.IntegerFixnumRange a, RubyRange.LongFixnumRange b) {
  53.             notDesignedForCompilation();

  54.             return a.doesExcludeEnd() == b.doesExcludeEnd() && a.getBegin() == b.getBegin() && a.getEnd() == b.getEnd();
  55.         }

  56.         @Specialization
  57.         public boolean equal(RubyRange.LongFixnumRange a, RubyRange.LongFixnumRange b) {
  58.             notDesignedForCompilation();

  59.             return a.doesExcludeEnd() == b.doesExcludeEnd() && a.getBegin() == b.getBegin() && a.getEnd() == b.getEnd();
  60.         }

  61.         @Specialization
  62.         public boolean equal(RubyRange.LongFixnumRange a, RubyRange.IntegerFixnumRange b) {
  63.             notDesignedForCompilation();

  64.             return a.doesExcludeEnd() == b.doesExcludeEnd() && a.getBegin() == b.getBegin() && a.getEnd() == b.getEnd();
  65.         }

  66.         @Specialization
  67.         public boolean equal(VirtualFrame frame, RubyRange.ObjectRange a, RubyRange.ObjectRange b) {
  68.             notDesignedForCompilation();

  69.             return a.doesExcludeEnd() == b.doesExcludeEnd() &&
  70.                     equal(frame, a.getBegin(), b.getBegin()) &&
  71.                     equal(frame, a.getEnd(), b.getEnd());
  72.         }
  73.     }

  74.     @CoreMethod(names = {"collect", "map"}, needsBlock = true, lowerFixnumSelf = true)
  75.     public abstract static class CollectNode extends YieldingCoreMethodNode {

  76.         @Child protected ArrayBuilderNode arrayBuilder;

  77.         public CollectNode(RubyContext context, SourceSection sourceSection) {
  78.             super(context, sourceSection);
  79.             arrayBuilder = new ArrayBuilderNode.UninitializedArrayBuilderNode(context);
  80.         }

  81.         public CollectNode(CollectNode prev) {
  82.             super(prev);
  83.             arrayBuilder = prev.arrayBuilder;
  84.         }

  85.         @Specialization
  86.         public RubyArray collect(VirtualFrame frame, RubyRange.IntegerFixnumRange range, RubyProc block) {
  87.             final int begin = range.getBegin();
  88.             final int exclusiveEnd = range.getExclusiveEnd();
  89.             final int length = exclusiveEnd - begin;

  90.             Object store = arrayBuilder.start(length);

  91.             int count = 0;

  92.             try {
  93.                 for (int n = 0; n < length; n++) {
  94.                     if (CompilerDirectives.inInterpreter()) {
  95.                         count++;
  96.                     }

  97.                     store = arrayBuilder.append(store, n, yield(frame, block, n));
  98.                 }
  99.             } finally {
  100.                 if (CompilerDirectives.inInterpreter()) {
  101.                     ((RubyRootNode) getRootNode()).reportLoopCount(count);
  102.                 }
  103.             }

  104.             return new RubyArray(getContext().getCoreLibrary().getArrayClass(), arrayBuilder.finish(store, length), length);
  105.         }

  106.     }

  107.     @CoreMethod(names = "each", needsBlock = true, lowerFixnumSelf = true)
  108.     public abstract static class EachNode extends YieldingCoreMethodNode {

  109.         private final BranchProfile breakProfile = BranchProfile.create();
  110.         private final BranchProfile nextProfile = BranchProfile.create();
  111.         private final BranchProfile redoProfile = BranchProfile.create();

  112.         public EachNode(RubyContext context, SourceSection sourceSection) {
  113.             super(context, sourceSection);
  114.         }

  115.         public EachNode(EachNode prev) {
  116.             super(prev);
  117.         }

  118.         @Specialization
  119.         public Object each(VirtualFrame frame, RubyRange.IntegerFixnumRange range, RubyProc block) {
  120.             final int exclusiveEnd = range.getExclusiveEnd();

  121.             int count = 0;

  122.             try {
  123.                 outer:
  124.                 for (int n = range.getBegin(); n < exclusiveEnd; n++) {
  125.                     while (true) {
  126.                         if (CompilerDirectives.inInterpreter()) {
  127.                             count++;
  128.                         }

  129.                         try {
  130.                             yield(frame, block, n);
  131.                             continue outer;
  132.                         } catch (BreakException e) {
  133.                             breakProfile.enter();
  134.                             return e.getResult();
  135.                         } catch (NextException e) {
  136.                             nextProfile.enter();
  137.                             continue outer;
  138.                         } catch (RedoException e) {
  139.                             redoProfile.enter();
  140.                         }
  141.                     }
  142.                 }
  143.             } finally {
  144.                 if (CompilerDirectives.inInterpreter()) {
  145.                     ((RubyRootNode) getRootNode()).reportLoopCount(count);
  146.                 }
  147.             }

  148.             return range;
  149.         }

  150.     }

  151.     @CoreMethod(names = "exclude_end?")
  152.     public abstract static class ExcludeEndNode extends CoreMethodNode {

  153.         public ExcludeEndNode(RubyContext context, SourceSection sourceSection) {
  154.             super(context, sourceSection);
  155.         }

  156.         public ExcludeEndNode(ExcludeEndNode prev) {
  157.             super(prev);
  158.         }

  159.         @Specialization
  160.         public boolean excludeEnd(RubyRange range) {
  161.             return range.doesExcludeEnd();
  162.         }

  163.     }

  164.     @CoreMethod(names = {"first", "begin"})
  165.     public abstract static class FirstNode extends CoreMethodNode {

  166.         public FirstNode(RubyContext context, SourceSection sourceSection) {
  167.             super(context, sourceSection);
  168.         }

  169.         public FirstNode(FirstNode prev) {
  170.             super(prev);
  171.         }

  172.         @Specialization
  173.         public int each(RubyRange.IntegerFixnumRange range) {
  174.             return range.getBegin();
  175.         }

  176.         @Specialization
  177.         public Object each(RubyRange.ObjectRange range) {
  178.             return range.getBegin();
  179.         }

  180.     }

  181.     @CoreMethod(names = {"include?", "==="}, required = 1)
  182.     public abstract static class IncludeNode extends CoreMethodNode {

  183.         @Child protected PredicateDispatchHeadNode callLess;
  184.         @Child protected PredicateDispatchHeadNode callGreater;
  185.         @Child protected PredicateDispatchHeadNode callGreaterEqual;

  186.         public IncludeNode(RubyContext context, SourceSection sourceSection) {
  187.             super(context, sourceSection);
  188.             callLess = new PredicateDispatchHeadNode(context);
  189.             callGreater = new PredicateDispatchHeadNode(context);
  190.             callGreaterEqual = new PredicateDispatchHeadNode(context);
  191.         }

  192.         public IncludeNode(IncludeNode prev) {
  193.             super(prev);
  194.             callLess = prev.callLess;
  195.             callGreater = prev.callGreater;
  196.             callGreaterEqual = prev.callGreaterEqual;
  197.         }

  198.         @Specialization
  199.         public boolean include(RubyRange.IntegerFixnumRange range, int value) {
  200.             return value >= range.getBegin() && value < range.getExclusiveEnd();
  201.         }

  202.         @Specialization
  203.         public boolean include(VirtualFrame frame, RubyRange.ObjectRange range, Object value) {
  204.             notDesignedForCompilation();

  205.             if (callLess.call(frame, value, "<", null, range.getBegin())) {
  206.                 return false;
  207.             }

  208.             if (range.doesExcludeEnd()) {
  209.                 if (callGreaterEqual.call(frame, value, ">=", null, range.getEnd())) {
  210.                     return false;
  211.                 }
  212.             } else {
  213.                 if (callGreater.call(frame, value, ">", null, range.getEnd())) {
  214.                     return false;
  215.                 }
  216.             }

  217.             return true;
  218.         }
  219.     }

  220.     @CoreMethod(names = {"last", "end"})
  221.     public abstract static class LastNode extends CoreMethodNode {

  222.         public LastNode(RubyContext context, SourceSection sourceSection) {
  223.             super(context, sourceSection);
  224.         }

  225.         public LastNode(LastNode prev) {
  226.             super(prev);
  227.         }

  228.         @Specialization
  229.         public int last(RubyRange.IntegerFixnumRange range) {
  230.             return range.getEnd();
  231.         }

  232.         @Specialization
  233.         public Object last(RubyRange.ObjectRange range) {
  234.             return range.getEnd();
  235.         }

  236.     }

  237.     @CoreMethod(names = "step", needsBlock = true, required = 1)
  238.     public abstract static class StepNode extends YieldingCoreMethodNode {

  239.         private final BranchProfile breakProfile = BranchProfile.create();
  240.         private final BranchProfile nextProfile = BranchProfile.create();
  241.         private final BranchProfile redoProfile = BranchProfile.create();

  242.         public StepNode(RubyContext context, SourceSection sourceSection) {
  243.             super(context, sourceSection);
  244.         }

  245.         public StepNode(StepNode prev) {
  246.             super(prev);
  247.         }

  248.         @Specialization
  249.         public Object step(VirtualFrame frame, RubyRange.IntegerFixnumRange range, int step, RubyProc block) {
  250.             int count = 0;

  251.             try {
  252.                 outer:
  253.                 for (int n = range.getBegin(); n < range.getExclusiveEnd(); n += step) {
  254.                     while (true) {
  255.                         if (CompilerDirectives.inInterpreter()) {
  256.                             count++;
  257.                         }

  258.                         try {
  259.                             yield(frame, block, n);
  260.                             continue outer;
  261.                         } catch (BreakException e) {
  262.                             breakProfile.enter();
  263.                             return e.getResult();
  264.                         } catch (NextException e) {
  265.                             nextProfile.enter();
  266.                             continue outer;
  267.                         } catch (RedoException e) {
  268.                             redoProfile.enter();
  269.                         }
  270.                     }
  271.                 }
  272.             } finally {
  273.                 if (CompilerDirectives.inInterpreter()) {
  274.                     ((RubyRootNode) getRootNode()).reportLoopCount(count);
  275.                 }
  276.             }

  277.             return range;
  278.         }

  279.     }

  280.     @CoreMethod(names = "to_a", lowerFixnumSelf = true)
  281.     public abstract static class ToANode extends CoreMethodNode {

  282.         public ToANode(RubyContext context, SourceSection sourceSection) {
  283.             super(context, sourceSection);
  284.         }

  285.         public ToANode(ToANode prev) {
  286.             super(prev);
  287.         }

  288.         @Specialization
  289.         public RubyArray toA(RubyRange.IntegerFixnumRange range) {
  290.             final int begin = range.getBegin();
  291.             final int length = range.getExclusiveEnd() - begin;

  292.             if (length < 0) {
  293.                 return new RubyArray(getContext().getCoreLibrary().getArrayClass());
  294.             } else {
  295.                 final int[] values = new int[length];

  296.                 for (int n = 0; n < length; n++) {
  297.                     values[n] = begin + n;
  298.                 }

  299.                 return new RubyArray(getContext().getCoreLibrary().getArrayClass(), values, length);
  300.             }
  301.         }

  302.     }

  303.     @CoreMethod(names = "to_s")
  304.     public abstract static class ToSNode extends CoreMethodNode {

  305.         @Child protected DispatchHeadNode toS;

  306.         public ToSNode(RubyContext context, SourceSection sourceSection) {
  307.             super(context, sourceSection);
  308.             toS = new DispatchHeadNode(context);
  309.         }

  310.         public ToSNode(ToSNode prev) {
  311.             super(prev);
  312.             toS = prev.toS;
  313.         }

  314.         @Specialization
  315.         public RubyString toS(RubyRange.IntegerFixnumRange range) {
  316.             notDesignedForCompilation();

  317.             return getContext().makeString(range.getBegin() + (range.doesExcludeEnd() ? "..." : "..") + range.getEnd());
  318.         }

  319.         @Specialization
  320.         public RubyString toS(VirtualFrame frame, RubyRange.ObjectRange range) {
  321.             notDesignedForCompilation();

  322.             // TODO(CS): cast?
  323.             final RubyString begin = (RubyString) toS.call(frame, range.getBegin(), "to_s", null);
  324.             final RubyString end = (RubyString) toS.call(frame, range.getBegin(), "to_s", null);

  325.             return getContext().makeString(begin + (range.doesExcludeEnd() ? "..." : "..") + end);
  326.         }
  327.     }

  328. }