diff --git a/spec/ruby/language/range_spec.rb b/spec/ruby/language/range_spec.rb index 4cde7e9488a0..d076dd344fca 100644 --- a/spec/ruby/language/range_spec.rb +++ b/spec/ruby/language/range_spec.rb @@ -19,6 +19,10 @@ it "is frozen" do (42..).should.frozen? end + + it "is not frozen if duplicated" do + (42..).dup.should_not.frozen? + end end ruby_version_is "2.7" do @@ -28,3 +32,25 @@ end end end + +describe "Object Ranges" do + ruby_version_is "3.0" do + + it "is frozen" do + Range.new(1, 2).should.frozen? + end + + it "is not frozen if duplicated" do + Range.new(1, 2).dup.should_not.frozen? + end + + it "is not frozen if it is a subclass of Range" do + change_range = Class.new(Range).new(1, 2) + change_range.should_not.frozen? + end + + it "is not frozen if it is created through allocate" do + Range.allocate.should_not.frozen? + end + end +end diff --git a/spec/tags/language/range_tags.txt b/spec/tags/language/range_tags.txt deleted file mode 100644 index 9c1460fb2559..000000000000 --- a/spec/tags/language/range_tags.txt +++ /dev/null @@ -1 +0,0 @@ -fails:Literal Ranges is frozen diff --git a/src/main/java/org/truffleruby/core/range/RangeNodes.java b/src/main/java/org/truffleruby/core/range/RangeNodes.java index 0e07dd607425..a0d182f5d24a 100644 --- a/src/main/java/org/truffleruby/core/range/RangeNodes.java +++ b/src/main/java/org/truffleruby/core/range/RangeNodes.java @@ -9,6 +9,7 @@ */ package org.truffleruby.core.range; +import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.profiles.LoopConditionProfile; import org.truffleruby.builtins.CoreMethod; import org.truffleruby.builtins.CoreMethodArrayArgumentsNode; @@ -196,7 +197,8 @@ protected RubyIntRange dupIntRange(RubyIntRange range) { shape, range.excludedEnd, range.begin, - range.end); + range.end, + false); AllocationTracing.trace(copy, this); return copy; } @@ -210,7 +212,8 @@ protected RubyLongRange dupLongRange(RubyLongRange range) { shape, range.excludedEnd, range.begin, - range.end); + range.end, + false); AllocationTracing.trace(copy, this); return copy; } @@ -223,7 +226,8 @@ protected RubyObjectRange dup(RubyObjectRange range) { getLanguage().objectRangeShape, range.excludedEnd, range.begin, - range.end); + range.end, + false); AllocationTracing.trace(copy, this); return copy; } @@ -423,7 +427,8 @@ protected RubyIntRange longRange(RubyLongRange range, RubyArray array) { getLanguage().intRangeShape, range.excludedEnd, begin, - end); + end, + range.frozen); } @Specialization(guards = "range.isBounded()") @@ -435,7 +440,8 @@ protected RubyIntRange boundedObjectRange(RubyObjectRange range, RubyArray array getLanguage().intRangeShape, range.excludedEnd, begin, - end); + end, + range.frozen); } @Specialization(guards = "range.isEndless()") @@ -446,7 +452,8 @@ protected RubyIntRange endlessObjectRange(RubyObjectRange range, RubyArray array getLanguage().intRangeShape, true, toInt(range.begin), - end); + end, + range.frozen); } @Specialization(guards = "range.isBeginless()") @@ -458,7 +465,8 @@ protected RubyIntRange beginlessObjectRange(RubyObjectRange range, RubyArray arr getLanguage().intRangeShape, range.excludedEnd, begin, - end); + end, + range.frozen); } @Specialization(guards = "range.isBoundless()") @@ -470,7 +478,8 @@ protected RubyIntRange nilNilObjectRange(RubyObjectRange range, RubyArray array) getLanguage().intRangeShape, false, begin, - end); + end, + range.frozen); } private int toInt(Object indexObject) { @@ -514,7 +523,8 @@ protected RubyIntRange intRange(RubyClass rubyClass, int begin, int end, boolean getLanguage().intRangeShape, excludeEnd, begin, - end); + end, + true); AllocationTracing.trace(range, this); return range; } @@ -528,7 +538,8 @@ protected RubyIntRange longFittingIntRange(RubyClass rubyClass, long begin, long shape, excludeEnd, (int) begin, - (int) end); + (int) end, + true); AllocationTracing.trace(range, this); return range; } @@ -541,21 +552,24 @@ protected RubyLongRange longRange(RubyClass rubyClass, long begin, long end, boo getLanguage().longRangeShape, excludeEnd, begin, - end); + end, + true); AllocationTracing.trace(range, this); return range; } - @Specialization(guards = { "rubyClass != getRangeClass() || (!isImplicitLong(begin) || !isImplicitLong(end))" }) + @Specialization(guards = { "!standardClass || (!isImplicitLong(begin) || !isImplicitLong(end))" }) protected RubyObjectRange objectRange(RubyClass rubyClass, Object begin, Object end, boolean excludeEnd, - @Cached DispatchNode compare) { + @Cached DispatchNode compare, + @Bind("rubyClass == getRangeClass()") boolean standardClass) { if (compare.call(begin, "<=>", end) == nil && end != nil && begin != nil) { throw new RaiseException(getContext(), coreExceptions().argumentError("bad value for range", this)); } final Shape shape = getLanguage().objectRangeShape; - final RubyObjectRange range = new RubyObjectRange(rubyClass, shape, excludeEnd, begin, end); + final RubyObjectRange range = new RubyObjectRange(rubyClass, shape, excludeEnd, begin, end, + standardClass); AllocationTracing.trace(range, this); return range; } @@ -571,7 +585,7 @@ public abstract static class AllocateNode extends UnaryCoreMethodNode { @Specialization protected RubyObjectRange allocate(RubyClass rubyClass) { final Shape shape = getLanguage().objectRangeShape; - final RubyObjectRange range = new RubyObjectRange(rubyClass, shape, false, nil, nil); + final RubyObjectRange range = new RubyObjectRange(rubyClass, shape, false, nil, nil, false); AllocationTracing.trace(range, this); return range; } diff --git a/src/main/java/org/truffleruby/core/range/RubyIntRange.java b/src/main/java/org/truffleruby/core/range/RubyIntRange.java index 98b7b5a2c93d..42b7b78dc792 100644 --- a/src/main/java/org/truffleruby/core/range/RubyIntRange.java +++ b/src/main/java/org/truffleruby/core/range/RubyIntRange.java @@ -17,8 +17,8 @@ public final class RubyIntRange extends RubyRange { public final int begin; public final int end; - public RubyIntRange(RubyClass rubyClass, Shape shape, boolean excludedEnd, int begin, int end) { - super(rubyClass, shape, excludedEnd); + public RubyIntRange(RubyClass rubyClass, Shape shape, boolean excludedEnd, int begin, int end, boolean frozen) { + super(rubyClass, shape, excludedEnd, frozen); this.begin = begin; this.end = end; } diff --git a/src/main/java/org/truffleruby/core/range/RubyLongRange.java b/src/main/java/org/truffleruby/core/range/RubyLongRange.java index 970b51980cf3..ce49ca27c46b 100644 --- a/src/main/java/org/truffleruby/core/range/RubyLongRange.java +++ b/src/main/java/org/truffleruby/core/range/RubyLongRange.java @@ -17,8 +17,8 @@ public final class RubyLongRange extends RubyRange { public final long begin; public final long end; - public RubyLongRange(RubyClass rubyClass, Shape shape, boolean excludedEnd, long begin, long end) { - super(rubyClass, shape, excludedEnd); + public RubyLongRange(RubyClass rubyClass, Shape shape, boolean excludedEnd, long begin, long end, boolean frozen) { + super(rubyClass, shape, excludedEnd, frozen); this.begin = begin; this.end = end; } diff --git a/src/main/java/org/truffleruby/core/range/RubyObjectRange.java b/src/main/java/org/truffleruby/core/range/RubyObjectRange.java index b39de4cb229b..ae49643fb573 100644 --- a/src/main/java/org/truffleruby/core/range/RubyObjectRange.java +++ b/src/main/java/org/truffleruby/core/range/RubyObjectRange.java @@ -23,8 +23,14 @@ public final class RubyObjectRange extends RubyRange implements ObjectGraphNode public Object begin; public Object end; - public RubyObjectRange(RubyClass rubyClass, Shape shape, boolean excludedEnd, Object begin, Object end) { - super(rubyClass, shape, excludedEnd); + public RubyObjectRange( + RubyClass rubyClass, + Shape shape, + boolean excludedEnd, + Object begin, + Object end, + boolean frozen) { + super(rubyClass, shape, excludedEnd, frozen); this.begin = begin; this.end = end; } diff --git a/src/main/java/org/truffleruby/core/range/RubyRange.java b/src/main/java/org/truffleruby/core/range/RubyRange.java index 65eeffc4695c..9218b3fdfd2e 100644 --- a/src/main/java/org/truffleruby/core/range/RubyRange.java +++ b/src/main/java/org/truffleruby/core/range/RubyRange.java @@ -9,17 +9,32 @@ */ package org.truffleruby.core.range; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.object.Shape; import org.truffleruby.core.klass.RubyClass; import org.truffleruby.language.RubyDynamicObject; +import org.truffleruby.language.library.RubyLibrary; +@ExportLibrary(RubyLibrary.class) public abstract class RubyRange extends RubyDynamicObject { public boolean excludedEnd; + public boolean frozen; - public RubyRange(RubyClass rubyClass, Shape shape, boolean excludedEnd) { + public RubyRange(RubyClass rubyClass, Shape shape, boolean excludedEnd, boolean frozen) { super(rubyClass, shape); this.excludedEnd = excludedEnd; + this.frozen = frozen; } + @ExportMessage + protected void freeze() { + frozen = true; + } + + @ExportMessage + protected boolean isFrozen() { + return frozen; + } }