Skip to content

Commit 87fe4ae

Browse files
committed
[GR-44710] Fix String#slice with negative offset
PullRequest: truffleruby/4484
2 parents 84048ba + be9d64f commit 87fe4ae

File tree

5 files changed

+29
-26
lines changed

5 files changed

+29
-26
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Compatibility:
1111
* Implement `StringScanner#{peek_byte,scan_byte,scan_integer,named_captures}` methods (#3788, @andrykonchin).
1212
* Support String patterns in `StringScanner#{exist?,scan_until,skip_until,check_until,search_full}` methods (@andrykonchin).
1313
* Implement `ObjectSpace::WeakKeyMap` (#3681, @andrykonchin).
14+
* Fix `String#slice` called with negative offset (@andrykonchin).
1415

1516
Performance:
1617

doc/contributor/updating-ruby.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,11 @@ rm -rf lib/gems/specifications
135135
cd ~/.rubies/ruby-$VERSION
136136
cp -R lib/ruby/gems/*.0/gems $TRUFFLERUBY/lib/gems
137137
cp -R lib/ruby/gems/*.0/specifications $TRUFFLERUBY/lib/gems
138-
rm -f lib/gems/gems/**/*.{o,a,so,bundle} lib/gems/gems/**/{Makefile,extconf.h,mkmf.log} lib/gems/gems/**/*.mk
139138
140139
cd $TRUFFLERUBY
140+
rm -f lib/gems/gems/**/*.{o,a,so,bundle} lib/gems/gems/**/{Makefile,extconf.h,mkmf.log} lib/gems/gems/**/*.mk
141141
rm -rf lib/gems/gems/typeprof-* lib/gems/specifications/typeprof-*.gemspec
142+
rm lib/gems/gems/rbs-*/Gemfile.lock
142143
ruby tool/patch-default-gemspecs.rb
143144
```
144145

spec/ruby/core/string/shared/slice.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@
119119
"hello there".send(@method, -4,-3).should == nil
120120
end
121121

122+
platform_is pointer_size: 64 do
123+
it "returns nil if the length is negative big value" do
124+
"hello there".send(@method, 4, -(1 << 31)).should == nil
125+
"hello there".send(@method, 4, -(1 << 63)).should == nil
126+
end
127+
end
128+
122129
it "calls to_int on the given index and the given length" do
123130
"hello".send(@method, 0.5, 1).should == "h"
124131
"hello".send(@method, 0.5, 2.5).should == "he"
@@ -152,6 +159,11 @@
152159
-> { "hello".send(@method, 0, bignum_value) }.should raise_error(RangeError)
153160
end
154161

162+
it "raises a RangeError if the index or length is too small" do
163+
-> { "hello".send(@method, -bignum_value, 1) }.should raise_error(RangeError)
164+
-> { "hello".send(@method, 0, -bignum_value) }.should raise_error(RangeError)
165+
end
166+
155167
it "returns String instances" do
156168
s = StringSpecs::MyString.new("hello")
157169
s.send(@method, 0,0).should be_an_instance_of(String)

spec/truffle/objectspace/weak_key_map_spec.rb

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,6 @@
2727
map.getkey(key).should == nil
2828
end
2929

30-
it "has iterators methods that exclude unreferenced objects" do
31-
# This spec does not pass on MRI because the garbage collector is presumably too conservative and will not get rid
32-
# of the references eagerly enough.
33-
34-
map = ObjectSpace::WeakKeyMap.new
35-
k1, k2 = %w[a b].map(&:upcase)
36-
v1, v2 = %w[x y].map(&:upcase)
37-
map[k1] = v1
38-
map[k2] = v2
39-
k2 = nil
40-
41-
Primitive.gc_force
42-
43-
map.key?(k2).should == false
44-
map[k2].should == nil
45-
46-
map.key?(k1).should == true
47-
map[k1].should == v1
48-
end
49-
5030
it "supports frozen objects" do
5131
map = ObjectSpace::WeakKeyMap.new
5232
k = "x".freeze

src/main/java/org/truffleruby/core/string/StringNodes.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,13 @@ Object slice(Object string, int start, int length) {
575575
}
576576

577577
@Specialization
578-
Object slice(Object string, long start, long length) {
578+
Object slice(Object string, long start, long length,
579+
@Cached @Shared InlinedBranchProfile negativeLengthProfile) {
580+
if (length < 0) {
581+
negativeLengthProfile.enter(this);
582+
return nil;
583+
}
584+
579585
int lengthInt = (int) length;
580586
if (lengthInt != length) {
581587
lengthInt = Integer.MAX_VALUE; // go to end of string
@@ -589,8 +595,9 @@ Object slice(Object string, long start, long length) {
589595

590596
@Specialization(guards = "wasProvided(length)")
591597
Object slice(Object string, long start, Object length,
592-
@Cached @Shared ToLongNode toLongNode) {
593-
return slice(string, start, toLongNode.execute(this, length));
598+
@Cached @Shared ToLongNode toLongNode,
599+
@Cached @Shared InlinedBranchProfile negativeLengthProfile) {
600+
return slice(string, start, toLongNode.execute(this, length), negativeLengthProfile);
594601
}
595602

596603
@Specialization(
@@ -600,8 +607,10 @@ Object slice(Object string, long start, Object length,
600607
"isNotRubyString(start)",
601608
"wasProvided(length)" })
602609
Object slice(Object string, Object start, Object length,
603-
@Cached @Shared ToLongNode toLongNode) {
604-
return slice(string, toLongNode.execute(this, start), toLongNode.execute(this, length));
610+
@Cached @Shared ToLongNode toLongNode,
611+
@Cached @Shared InlinedBranchProfile negativeLengthProfile) {
612+
return slice(string, toLongNode.execute(this, start), toLongNode.execute(this, length),
613+
negativeLengthProfile);
605614
}
606615

607616
// endregion

0 commit comments

Comments
 (0)