Skip to content

Commit 3f59d69

Browse files
committed
Add rb_arithmetic_sequence_beg_len_step function
Fix test/mri/tests/cext-ruby/arith_seq/test_arith_seq_beg_len_step.rb test failed with error: ``` assert_separately failed with error message pid 23810 exit 1 | <internal:core> core/truffle/polyglot.rb:324:in `marshal_dump': Foreign exception cannot be dumped: #<Polyglot::ForeignException:0x4a3bf37c: External LLVMFunction rb_arithmetic_sequence_beg_len_step cannot be found.> (TypeError) | from <internal:core> core/marshal.rb:1289:in `serialize_user_marshal' | from <internal:core> core/marshal.rb:1107:in `serialize' | from <internal:core> core/marshal.rb:1453:in `dump' | from /Users/graal/slave/e/main/test/mri/tests/lib/core_assertions.rb:260:in `block in separated_runner' | /Users/graal/slave/e/main/test/mri/tests/cext-c/arith_seq/beg_len_step/beg_len_step.c:4:in `arith_seq_s_beg_len_step': External LLVMFunction rb_arithmetic_sequence_beg_len_step cannot be found. (Polyglot::ForeignException) | from /Users/graal/slave/e/graal/sdk/mxbuild/darwin-amd64/GRAALVM_A629582E5D_JAVA21/graalvm-a629582e5d-java21-23.1.0-dev/Contents/Home/languages/ruby/lib/truffle/truffle/cext_ruby.rb:42:in `<unknown>' | from /Users/graal/slave/e/graal/sdk/mxbuild/darwin-amd64/GRAALVM_A629582E5D_JAVA21/graalvm-a629582e5d-java21-23.1.0-dev/Contents/Home/languages/ruby/lib/truffle/truffle/cext_ruby.rb:42:in `Enumerator::ArithmeticSequence.__beg_len_step_ ```
1 parent ae51afc commit 3f59d69

File tree

9 files changed

+318
-5
lines changed

9 files changed

+318
-5
lines changed

lib/cext/include/ruby/internal/globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ RUBY_EXTERN VALUE rb_cClass; /**< `Class` class. */
7272
RUBY_EXTERN VALUE rb_cDir; /**< `Dir` class. */
7373
RUBY_EXTERN VALUE rb_cEncoding; /**< `Encoding` class. */
7474
RUBY_EXTERN VALUE rb_cEnumerator; /**< `Enumerator` class. */
75+
RUBY_EXTERN VALUE rb_cArithSeq; /**< `Enumerator::ArithmeticSequence` class. */
7576
RUBY_EXTERN VALUE rb_cFalseClass; /**< `FalseClass` class. */
7677
RUBY_EXTERN VALUE rb_cFile; /**< `File` class. */
7778
RUBY_EXTERN VALUE rb_cComplex; /**< `Complex` class. */

lib/cext/include/truffleruby/truffleruby-abi-version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@
2020
// $RUBY_VERSION must be the same as TruffleRuby.LANGUAGE_VERSION.
2121
// $ABI_NUMBER starts at 1 and is incremented for every ABI-incompatible change.
2222

23-
#define TRUFFLERUBY_ABI_VERSION "3.3.7.1"
23+
#define TRUFFLERUBY_ABI_VERSION "3.3.7.2"
2424

2525
#endif

lib/truffle/truffle/cext_constants.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# GNU General Public License version 2, or
99
# GNU Lesser General Public License version 2.1.
1010

11-
# GENERATED BY tool/generate-cext-constants.rb
11+
# GENERATED BY tool/generate-cext-constants.rb AND SHOULD NOT BE MODIFIED MANUALLY
1212

1313
module Truffle::CExt
1414
def rb_cArray
@@ -51,6 +51,10 @@ def rb_cEnumerator
5151
Enumerator
5252
end
5353

54+
def rb_cArithSeq
55+
Enumerator::ArithmeticSequence
56+
end
57+
5458
def rb_cFalseClass
5559
FalseClass
5660
end

spec/ruby/optional/capi/ext/range_spec.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,49 @@ VALUE range_spec_rb_range_beg_len(VALUE self, VALUE range, VALUE begpv, VALUE le
3838
return ary;
3939
}
4040

41+
VALUE range_spec_rb_arithmetic_sequence_extract(VALUE self, VALUE object) {
42+
VALUE ary = rb_ary_new();
43+
rb_arithmetic_sequence_components_t components;
44+
45+
int status = rb_arithmetic_sequence_extract(object, &components);
46+
47+
if (!status) {
48+
rb_ary_store(ary, 0, LONG2FIX(status));
49+
return ary;
50+
}
51+
52+
rb_ary_store(ary, 0, LONG2FIX(status));
53+
rb_ary_store(ary, 1, components.begin);
54+
rb_ary_store(ary, 2, components.end);
55+
rb_ary_store(ary, 3, components.step);
56+
rb_ary_store(ary, 4, components.exclude_end ? Qtrue : Qfalse);
57+
return ary;
58+
}
59+
60+
VALUE range_spec_rb_arithmetic_sequence_beg_len_step(VALUE self, VALUE aseq, VALUE lenv, VALUE errv) {
61+
long begp, lenp, stepp;
62+
63+
long len = FIX2LONG(lenv);
64+
int err = FIX2INT(errv);
65+
66+
VALUE success = rb_arithmetic_sequence_beg_len_step(aseq, &begp, &lenp, &stepp, len, err);
67+
68+
VALUE ary = rb_ary_new();
69+
rb_ary_store(ary, 0, success);
70+
rb_ary_store(ary, 1, LONG2FIX(begp));
71+
rb_ary_store(ary, 2, LONG2FIX(lenp));
72+
rb_ary_store(ary, 3, LONG2FIX(stepp));
73+
74+
return ary;
75+
}
76+
4177
void Init_range_spec(void) {
4278
VALUE cls = rb_define_class("CApiRangeSpecs", rb_cObject);
4379
rb_define_method(cls, "rb_range_new", range_spec_rb_range_new, -1);
4480
rb_define_method(cls, "rb_range_values", range_spec_rb_range_values, 1);
4581
rb_define_method(cls, "rb_range_beg_len", range_spec_rb_range_beg_len, 5);
82+
rb_define_method(cls, "rb_arithmetic_sequence_extract", range_spec_rb_arithmetic_sequence_extract, 1);
83+
rb_define_method(cls, "rb_arithmetic_sequence_beg_len_step", range_spec_rb_arithmetic_sequence_beg_len_step, 3);
4684
}
4785

4886
#ifdef __cplusplus

spec/ruby/optional/capi/range_spec.rb

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,140 @@ def range_like.exclude_end?
9292
result.should be_nil
9393
end
9494
end
95+
96+
describe "rb_arithmetic_sequence_extract" do
97+
it "returns begin, end, step, exclude end of an instance of an Enumerator::ArithmeticSequence" do
98+
enum = (10..20).step(5)
99+
enum.should.kind_of?(Enumerator::ArithmeticSequence)
100+
101+
@s.rb_arithmetic_sequence_extract(enum).should == [1, 10, 20, 5, false]
102+
end
103+
104+
it "returns begin, end, step, exclude end of an instance of a Range" do
105+
range = (10..20)
106+
@s.rb_arithmetic_sequence_extract(range).should == [1, 10, 20, 1, false]
107+
end
108+
109+
it "returns begin, end, step, exclude end of a non-Range object with Range properties" do
110+
object = Object.new
111+
def object.begin
112+
10
113+
end
114+
def object.end
115+
20
116+
end
117+
def object.exclude_end?
118+
false
119+
end
120+
121+
@s.rb_arithmetic_sequence_extract(object).should == [1, 10, 20, 1, false]
122+
end
123+
124+
it "returns failed status if given object is not Enumerator::ArithmeticSequence or Range or Range-like object" do
125+
object = Object.new
126+
@s.rb_arithmetic_sequence_extract(object).should == [0]
127+
end
128+
end
129+
130+
describe "rb_arithmetic_sequence_beg_len_step" do
131+
it "returns correct begin, length, step and result" do
132+
as = (2..5).step(5)
133+
error_code = 0
134+
135+
success, beg, len, step = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
136+
success.should be_true
137+
138+
beg.should == 2
139+
len.should == 4
140+
step.should == 5
141+
end
142+
143+
it "takes into account excluded end boundary" do
144+
as = (2...5).step(1)
145+
error_code = 0
146+
147+
success, _, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
148+
success.should be_true
149+
len.should == 3
150+
end
151+
152+
it "adds length to negative begin boundary" do
153+
as = (-2..5).step(1)
154+
error_code = 0
155+
156+
success, beg, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
157+
success.should be_true
158+
159+
beg.should == 4
160+
len.should == 2
161+
end
162+
163+
it "adds length to negative end boundary" do
164+
as = (2..-1).step(1)
165+
error_code = 0
166+
167+
success, beg, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
168+
success.should be_true
169+
170+
beg.should == 2
171+
len.should == 4
172+
end
173+
174+
it "truncates arithmetic sequence length if end boundary greater than specified length value" do
175+
as = (2..10).step(1)
176+
error_code = 0
177+
178+
success, _, len, _ = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
179+
success.should be_true
180+
len.should == 4
181+
end
182+
183+
it "returns inverted begin and end boundaries when step is negative" do
184+
as = (2..5).step(-2)
185+
error_code = 0
186+
187+
success, beg, len, step = @s.rb_arithmetic_sequence_beg_len_step(as, 6, error_code)
188+
success.should be_true
189+
190+
beg.should == 5
191+
len.should == 0
192+
step.should == -2
193+
end
194+
195+
it "returns nil when not in range and error code = 0" do
196+
as = (2..5).step(1)
197+
error_code = 0
198+
199+
success, = @s.rb_arithmetic_sequence_beg_len_step(as, 1, error_code)
200+
success.should be_nil
201+
end
202+
203+
it "returns nil when not in range, negative boundaries and error code = 0" do
204+
as = (-5..-1).step(1)
205+
error_code = 0
206+
207+
success, = @s.rb_arithmetic_sequence_beg_len_step(as, 1, 0)
208+
success.should be_nil
209+
end
210+
211+
it "returns begin, length and step and doesn't raise a RangeError when not in range and error code = 1" do
212+
as = (2..5).step(1)
213+
error_code = 1
214+
215+
success, beg, len, step = @s.rb_arithmetic_sequence_beg_len_step(as, 1, error_code)
216+
success.should be_true
217+
218+
beg.should == 2
219+
len.should == 4
220+
step.should == 1
221+
end
222+
223+
it "returns nil and doesn't raise a RangeError when not in range, negative boundaries and error code = 1" do
224+
as = (-5..-1).step(1)
225+
error_code = 1
226+
227+
success, = @s.rb_arithmetic_sequence_beg_len_step(as, 1, error_code)
228+
success.should be_nil
229+
end
230+
end
95231
end

src/main/c/cext/cext_constants.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* GNU Lesser General Public License version 2.1.
99
*/
1010

11-
// GENERATED BY tool/generate-cext-constants.rb
11+
// GENERATED BY tool/generate-cext-constants.rb AND SHOULD NOT BE MODIFIED MANUALLY
1212

1313
#include <ruby.h>
1414

@@ -22,6 +22,7 @@ VALUE rb_cDir;
2222
VALUE rb_cEncoding;
2323
VALUE rb_mEnumerable;
2424
VALUE rb_cEnumerator;
25+
VALUE rb_cArithSeq;
2526
VALUE rb_cFalseClass;
2627
VALUE rb_cFile;
2728
VALUE rb_mFileTest;
@@ -102,6 +103,7 @@ void rb_tr_init_global_constants(VALUE (*get_constant)(const char*)) {
102103
rb_cEncoding = get_constant("rb_cEncoding");
103104
rb_mEnumerable = get_constant("rb_mEnumerable");
104105
rb_cEnumerator = get_constant("rb_cEnumerator");
106+
rb_cArithSeq = get_constant("rb_cArithSeq");
105107
rb_cFalseClass = get_constant("rb_cFalseClass");
106108
rb_cFile = get_constant("rb_cFile");
107109
rb_mFileTest = get_constant("rb_mFileTest");

src/main/c/cext/enum.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
// Enumerable and Enumerator, rb_enum*
1313

14+
VALUE rb_range_component_beg_len(VALUE b, VALUE e, int excl, long *begp, long *lenp, long len, int err);
15+
1416
VALUE rb_enumeratorize(VALUE obj, VALUE meth, int argc, const VALUE *argv) {
1517
return RUBY_CEXT_INVOKE("rb_enumeratorize", obj, meth, rb_ary_new4(argc, argv));
1618
}
@@ -19,3 +21,63 @@ VALUE rb_enumeratorize(VALUE obj, VALUE meth, int argc, const VALUE *argv) {
1921
VALUE rb_enumeratorize_with_size(VALUE obj, VALUE meth, int argc, const VALUE *argv, rb_enumerator_size_func *size_fn) {
2022
return rb_tr_wrap(polyglot_invoke(RUBY_CEXT, "rb_enumeratorize_with_size", rb_tr_unwrap(obj), rb_tr_unwrap(meth), rb_tr_unwrap(rb_ary_new4(argc, argv)), size_fn));
2123
}
24+
25+
int rb_arithmetic_sequence_extract(VALUE obj, rb_arithmetic_sequence_components_t *component) {
26+
if (rb_obj_is_kind_of(obj, rb_cArithSeq)) {
27+
component->begin = rb_ivar_get(obj, rb_intern("@begin"));
28+
component->end = rb_ivar_get(obj, rb_intern("@end"));
29+
component->step = rb_ivar_get(obj, rb_intern("@step"));
30+
component->exclude_end = rb_ivar_get(obj, rb_intern("@exclude_end"));
31+
return 1;
32+
} else if (rb_range_values(obj, &component->begin, &component->end, &component->exclude_end)) {
33+
component->step = INT2FIX(1);
34+
return 1;
35+
}
36+
37+
return 0;
38+
}
39+
40+
VALUE rb_arithmetic_sequence_beg_len_step(VALUE obj, long *begp, long *lenp, long *stepp, long len, int err) {
41+
RBIMPL_NONNULL_ARG(begp);
42+
RBIMPL_NONNULL_ARG(lenp);
43+
RBIMPL_NONNULL_ARG(stepp);
44+
45+
rb_arithmetic_sequence_components_t aseq;
46+
if (!rb_arithmetic_sequence_extract(obj, &aseq)) {
47+
return Qfalse;
48+
}
49+
50+
long step = NIL_P(aseq.step) ? 1 : NUM2LONG(aseq.step);
51+
*stepp = step;
52+
53+
if (step < 0) {
54+
if (aseq.exclude_end && !NIL_P(aseq.end)) {
55+
/* Handle exclusion before range reversal */
56+
aseq.end = LONG2NUM(NUM2LONG(aseq.end) + 1);
57+
58+
/* Don't exclude the previous beginning */
59+
aseq.exclude_end = 0;
60+
}
61+
VALUE tmp = aseq.begin;
62+
aseq.begin = aseq.end;
63+
aseq.end = tmp;
64+
}
65+
66+
if (err == 0 && (step < -1 || step > 1)) {
67+
if (rb_range_component_beg_len(aseq.begin, aseq.end, aseq.exclude_end, begp, lenp, len, 1) == Qtrue) {
68+
if (*begp > len) {
69+
goto out_of_range;
70+
}
71+
if (*lenp > len) {
72+
goto out_of_range;
73+
}
74+
return Qtrue;
75+
}
76+
} else {
77+
return rb_range_component_beg_len(aseq.begin, aseq.end, aseq.exclude_end, begp, lenp, len, err);
78+
}
79+
80+
out_of_range:
81+
rb_raise(rb_eRangeError, "%+"PRIsVALUE" out of range", obj);
82+
return Qnil;
83+
}

src/main/c/cext/range.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,72 @@ int rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp) {
3434
return Qtrue;
3535
}
3636

37+
// NOTE: this is not a public API function but a helper function from CRuby
38+
39+
/* Extract the components of a Range.
40+
*
41+
* You can use +err+ to control the behavior of out-of-range and exception.
42+
*
43+
* When +err+ is 0 or 2, if the begin offset is greater than +len+,
44+
* it is out-of-range. The +RangeError+ is raised only if +err+ is 2,
45+
* in this case. If +err+ is 0, +Qnil+ will be returned.
46+
*
47+
* When +err+ is 1, the begin and end offsets won't be adjusted even if they
48+
* are greater than +len+. It allows +rb_ary_aset+ extends arrays.
49+
*
50+
* If the begin component of the given range is negative and is too-large
51+
* abstract value, the +RangeError+ is raised only +err+ is 1 or 2.
52+
*
53+
* The case of <code>err = 0</code> is used in item accessing methods such as
54+
* +rb_ary_aref+, +rb_ary_slice_bang+, and +rb_str_aref+.
55+
*
56+
* The case of <code>err = 1</code> is used in Array's methods such as
57+
* +rb_ary_aset+ and +rb_ary_fill+.
58+
*
59+
* The case of <code>err = 2</code> is used in +rb_str_aset+.
60+
*/
61+
VALUE rb_range_component_beg_len(VALUE b, VALUE e, int excl, long *begp, long *lenp, long len, int err) {
62+
long beg, end;
63+
64+
beg = NIL_P(b) ? 0 : NUM2LONG(b);
65+
end = NIL_P(e) ? -1 : NUM2LONG(e);
66+
if (NIL_P(e)) {
67+
excl = 0;
68+
}
69+
70+
if (beg < 0) {
71+
beg += len;
72+
if (beg < 0) {
73+
goto out_of_range;
74+
}
75+
}
76+
if (end < 0) {
77+
end += len;
78+
}
79+
if (!excl) {
80+
end++; /* include end point */
81+
}
82+
if (err == 0 || err == 2) {
83+
if (beg > len) {
84+
goto out_of_range;
85+
}
86+
if (end > len) {
87+
end = len;
88+
}
89+
}
90+
len = end - beg;
91+
if (len < 0) {
92+
len = 0;
93+
}
94+
95+
*begp = beg;
96+
*lenp = len;
97+
return Qtrue;
98+
99+
out_of_range:
100+
return Qnil;
101+
}
102+
37103
VALUE rb_range_beg_len(VALUE range, long *begp, long *lenp, long len, int err) {
38104
long beg, end, origbeg, origend;
39105
VALUE b, e;

0 commit comments

Comments
 (0)