RangeNodes.java
- /*
- * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This
- * code is released under a tri EPL/GPL/LGPL license. You can use it,
- * redistribute it and/or modify it under the terms of the:
- *
- * Eclipse Public License version 1.0
- * GNU General Public License version 2
- * GNU Lesser General Public License version 2.1
- */
- package org.jruby.truffle.nodes.core;
- import com.oracle.truffle.api.CompilerDirectives;
- import com.oracle.truffle.api.dsl.Specialization;
- import com.oracle.truffle.api.frame.VirtualFrame;
- import com.oracle.truffle.api.source.SourceSection;
- import com.oracle.truffle.api.utilities.BranchProfile;
- import org.jruby.truffle.nodes.RubyNode;
- import org.jruby.truffle.nodes.RubyRootNode;
- import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
- import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
- import org.jruby.truffle.runtime.RubyContext;
- import org.jruby.truffle.runtime.control.BreakException;
- import org.jruby.truffle.runtime.control.NextException;
- import org.jruby.truffle.runtime.control.RedoException;
- import org.jruby.truffle.runtime.core.RubyArray;
- import org.jruby.truffle.runtime.core.RubyProc;
- import org.jruby.truffle.runtime.core.RubyRange;
- import org.jruby.truffle.runtime.core.RubyString;
- @CoreClass(name = "Range")
- public abstract class RangeNodes {
- @CoreMethod(names = "==", required = 1)
- public abstract static class EqualNode extends CoreMethodNode {
- @Child protected KernelNodes.SameOrEqualNode equalNode;
- public EqualNode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- }
- public EqualNode(EqualNode prev) {
- super(prev);
- }
- protected boolean equal(VirtualFrame frame, Object a, Object b) {
- if (equalNode == null) {
- CompilerDirectives.transferToInterpreterAndInvalidate();
- equalNode = insert(KernelNodesFactory.SameOrEqualNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{null, null}));
- }
- return equalNode.executeSameOrEqual(frame, a, b);
- }
- @Specialization
- public boolean equal(RubyRange.IntegerFixnumRange a, RubyRange.IntegerFixnumRange b) {
- notDesignedForCompilation();
- return a.doesExcludeEnd() == b.doesExcludeEnd() && a.getBegin() == b.getBegin() && a.getEnd() == b.getEnd();
- }
- @Specialization
- public boolean equal(RubyRange.IntegerFixnumRange a, RubyRange.LongFixnumRange b) {
- notDesignedForCompilation();
- return a.doesExcludeEnd() == b.doesExcludeEnd() && a.getBegin() == b.getBegin() && a.getEnd() == b.getEnd();
- }
- @Specialization
- public boolean equal(RubyRange.LongFixnumRange a, RubyRange.LongFixnumRange b) {
- notDesignedForCompilation();
- return a.doesExcludeEnd() == b.doesExcludeEnd() && a.getBegin() == b.getBegin() && a.getEnd() == b.getEnd();
- }
- @Specialization
- public boolean equal(RubyRange.LongFixnumRange a, RubyRange.IntegerFixnumRange b) {
- notDesignedForCompilation();
- return a.doesExcludeEnd() == b.doesExcludeEnd() && a.getBegin() == b.getBegin() && a.getEnd() == b.getEnd();
- }
- @Specialization
- public boolean equal(VirtualFrame frame, RubyRange.ObjectRange a, RubyRange.ObjectRange b) {
- notDesignedForCompilation();
- return a.doesExcludeEnd() == b.doesExcludeEnd() &&
- equal(frame, a.getBegin(), b.getBegin()) &&
- equal(frame, a.getEnd(), b.getEnd());
- }
- }
- @CoreMethod(names = {"collect", "map"}, needsBlock = true, lowerFixnumSelf = true)
- public abstract static class CollectNode extends YieldingCoreMethodNode {
- @Child protected ArrayBuilderNode arrayBuilder;
- public CollectNode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- arrayBuilder = new ArrayBuilderNode.UninitializedArrayBuilderNode(context);
- }
- public CollectNode(CollectNode prev) {
- super(prev);
- arrayBuilder = prev.arrayBuilder;
- }
- @Specialization
- public RubyArray collect(VirtualFrame frame, RubyRange.IntegerFixnumRange range, RubyProc block) {
- final int begin = range.getBegin();
- final int exclusiveEnd = range.getExclusiveEnd();
- final int length = exclusiveEnd - begin;
- Object store = arrayBuilder.start(length);
- int count = 0;
- try {
- for (int n = 0; n < length; n++) {
- if (CompilerDirectives.inInterpreter()) {
- count++;
- }
- store = arrayBuilder.append(store, n, yield(frame, block, n));
- }
- } finally {
- if (CompilerDirectives.inInterpreter()) {
- ((RubyRootNode) getRootNode()).reportLoopCount(count);
- }
- }
- return new RubyArray(getContext().getCoreLibrary().getArrayClass(), arrayBuilder.finish(store, length), length);
- }
- }
- @CoreMethod(names = "each", needsBlock = true, lowerFixnumSelf = true)
- public abstract static class EachNode extends YieldingCoreMethodNode {
- private final BranchProfile breakProfile = BranchProfile.create();
- private final BranchProfile nextProfile = BranchProfile.create();
- private final BranchProfile redoProfile = BranchProfile.create();
- public EachNode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- }
- public EachNode(EachNode prev) {
- super(prev);
- }
- @Specialization
- public Object each(VirtualFrame frame, RubyRange.IntegerFixnumRange range, RubyProc block) {
- final int exclusiveEnd = range.getExclusiveEnd();
- int count = 0;
- try {
- outer:
- for (int n = range.getBegin(); n < exclusiveEnd; n++) {
- while (true) {
- if (CompilerDirectives.inInterpreter()) {
- count++;
- }
- try {
- yield(frame, block, n);
- continue outer;
- } catch (BreakException e) {
- breakProfile.enter();
- return e.getResult();
- } catch (NextException e) {
- nextProfile.enter();
- continue outer;
- } catch (RedoException e) {
- redoProfile.enter();
- }
- }
- }
- } finally {
- if (CompilerDirectives.inInterpreter()) {
- ((RubyRootNode) getRootNode()).reportLoopCount(count);
- }
- }
- return range;
- }
- }
- @CoreMethod(names = "exclude_end?")
- public abstract static class ExcludeEndNode extends CoreMethodNode {
- public ExcludeEndNode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- }
- public ExcludeEndNode(ExcludeEndNode prev) {
- super(prev);
- }
- @Specialization
- public boolean excludeEnd(RubyRange range) {
- return range.doesExcludeEnd();
- }
- }
- @CoreMethod(names = {"first", "begin"})
- public abstract static class FirstNode extends CoreMethodNode {
- public FirstNode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- }
- public FirstNode(FirstNode prev) {
- super(prev);
- }
- @Specialization
- public int each(RubyRange.IntegerFixnumRange range) {
- return range.getBegin();
- }
- @Specialization
- public Object each(RubyRange.ObjectRange range) {
- return range.getBegin();
- }
- }
- @CoreMethod(names = {"include?", "==="}, required = 1)
- public abstract static class IncludeNode extends CoreMethodNode {
- @Child protected PredicateDispatchHeadNode callLess;
- @Child protected PredicateDispatchHeadNode callGreater;
- @Child protected PredicateDispatchHeadNode callGreaterEqual;
- public IncludeNode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- callLess = new PredicateDispatchHeadNode(context);
- callGreater = new PredicateDispatchHeadNode(context);
- callGreaterEqual = new PredicateDispatchHeadNode(context);
- }
- public IncludeNode(IncludeNode prev) {
- super(prev);
- callLess = prev.callLess;
- callGreater = prev.callGreater;
- callGreaterEqual = prev.callGreaterEqual;
- }
- @Specialization
- public boolean include(RubyRange.IntegerFixnumRange range, int value) {
- return value >= range.getBegin() && value < range.getExclusiveEnd();
- }
- @Specialization
- public boolean include(VirtualFrame frame, RubyRange.ObjectRange range, Object value) {
- notDesignedForCompilation();
- if (callLess.call(frame, value, "<", null, range.getBegin())) {
- return false;
- }
- if (range.doesExcludeEnd()) {
- if (callGreaterEqual.call(frame, value, ">=", null, range.getEnd())) {
- return false;
- }
- } else {
- if (callGreater.call(frame, value, ">", null, range.getEnd())) {
- return false;
- }
- }
- return true;
- }
- }
- @CoreMethod(names = {"last", "end"})
- public abstract static class LastNode extends CoreMethodNode {
- public LastNode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- }
- public LastNode(LastNode prev) {
- super(prev);
- }
- @Specialization
- public int last(RubyRange.IntegerFixnumRange range) {
- return range.getEnd();
- }
- @Specialization
- public Object last(RubyRange.ObjectRange range) {
- return range.getEnd();
- }
- }
- @CoreMethod(names = "step", needsBlock = true, required = 1)
- public abstract static class StepNode extends YieldingCoreMethodNode {
- private final BranchProfile breakProfile = BranchProfile.create();
- private final BranchProfile nextProfile = BranchProfile.create();
- private final BranchProfile redoProfile = BranchProfile.create();
- public StepNode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- }
- public StepNode(StepNode prev) {
- super(prev);
- }
- @Specialization
- public Object step(VirtualFrame frame, RubyRange.IntegerFixnumRange range, int step, RubyProc block) {
- int count = 0;
- try {
- outer:
- for (int n = range.getBegin(); n < range.getExclusiveEnd(); n += step) {
- while (true) {
- if (CompilerDirectives.inInterpreter()) {
- count++;
- }
- try {
- yield(frame, block, n);
- continue outer;
- } catch (BreakException e) {
- breakProfile.enter();
- return e.getResult();
- } catch (NextException e) {
- nextProfile.enter();
- continue outer;
- } catch (RedoException e) {
- redoProfile.enter();
- }
- }
- }
- } finally {
- if (CompilerDirectives.inInterpreter()) {
- ((RubyRootNode) getRootNode()).reportLoopCount(count);
- }
- }
- return range;
- }
- }
- @CoreMethod(names = "to_a", lowerFixnumSelf = true)
- public abstract static class ToANode extends CoreMethodNode {
- public ToANode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- }
- public ToANode(ToANode prev) {
- super(prev);
- }
- @Specialization
- public RubyArray toA(RubyRange.IntegerFixnumRange range) {
- final int begin = range.getBegin();
- final int length = range.getExclusiveEnd() - begin;
- if (length < 0) {
- return new RubyArray(getContext().getCoreLibrary().getArrayClass());
- } else {
- final int[] values = new int[length];
- for (int n = 0; n < length; n++) {
- values[n] = begin + n;
- }
- return new RubyArray(getContext().getCoreLibrary().getArrayClass(), values, length);
- }
- }
- }
- @CoreMethod(names = "to_s")
- public abstract static class ToSNode extends CoreMethodNode {
- @Child protected DispatchHeadNode toS;
- public ToSNode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- toS = new DispatchHeadNode(context);
- }
- public ToSNode(ToSNode prev) {
- super(prev);
- toS = prev.toS;
- }
- @Specialization
- public RubyString toS(RubyRange.IntegerFixnumRange range) {
- notDesignedForCompilation();
- return getContext().makeString(range.getBegin() + (range.doesExcludeEnd() ? "..." : "..") + range.getEnd());
- }
- @Specialization
- public RubyString toS(VirtualFrame frame, RubyRange.ObjectRange range) {
- notDesignedForCompilation();
- // TODO(CS): cast?
- final RubyString begin = (RubyString) toS.call(frame, range.getBegin(), "to_s", null);
- final RubyString end = (RubyString) toS.call(frame, range.getBegin(), "to_s", null);
- return getContext().makeString(begin + (range.doesExcludeEnd() ? "..." : "..") + end);
- }
- }
- }