Skip to content

Commit 38cf0ba

Browse files
committed
[GR-18163] Add rb_proc_call_with_block function
PullRequest: truffleruby/3838
2 parents b2f7328 + f083011 commit 38cf0ba

File tree

11 files changed

+218
-11
lines changed

11 files changed

+218
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Compatibility:
1919
* Fix `Pathname#relative_path_from` to convert string arguments to Pathname objects (@rwstauner).
2020
* Add `String#bytesplice` (#3039, @itarato).
2121
* Add `String#byteindex` and `String#byterindex` (#3039, @itarato).
22+
* Add implementations of `rb_proc_call_with_block`, `rb_proc_call_kw`, `rb_proc_call_with_block_kw` and `rb_funcall_with_block_kw` (#3068, @andrykonchin).
2223

2324
Performance:
2425

lib/cext/ABI_version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
7
1+
8

lib/truffle/truffle/cext.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,10 @@ def rb_funcall_with_block(recv, meth, argv, block)
903903
Primitive.public_send_argv_without_cext_lock(recv, meth, argv, block)
904904
end
905905

906+
def rb_funcall_with_block_keywords(recv, meth, argv, block)
907+
Primitive.public_send_argv_keywords_without_cext_lock(recv, meth, argv, block)
908+
end
909+
906910
def rb_funcallv_public(recv, meth, argv)
907911
Primitive.public_send_argv_without_cext_lock(recv, meth, argv, nil)
908912
end

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,12 @@ static VALUE kernel_spec_rb_funcallv_public(VALUE self, VALUE obj, VALUE method)
340340
return rb_funcallv_public(obj, SYM2ID(method), 0, NULL);
341341
}
342342

343-
static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE block) {
344-
return rb_funcall_with_block(obj, SYM2ID(method), 0, NULL, block);
343+
static VALUE kernel_spec_rb_funcall_with_block(VALUE self, VALUE obj, VALUE method, VALUE args, VALUE block) {
344+
return rb_funcall_with_block(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), block);
345+
}
346+
347+
static VALUE kernel_spec_rb_funcall_with_block_kw(VALUE self, VALUE obj, VALUE method, VALUE args, VALUE block) {
348+
return rb_funcall_with_block_kw(obj, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args), block, RB_PASS_KEYWORDS);
345349
}
346350

347351
static VALUE kernel_spec_rb_funcall_many_args(VALUE self, VALUE obj, VALUE method) {
@@ -397,7 +401,8 @@ void Init_kernel_spec(void) {
397401
#endif
398402
rb_define_method(cls, "rb_funcallv_public", kernel_spec_rb_funcallv_public, 2);
399403
rb_define_method(cls, "rb_funcall_many_args", kernel_spec_rb_funcall_many_args, 2);
400-
rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 3);
404+
rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 4);
405+
rb_define_method(cls, "rb_funcall_with_block_kw", kernel_spec_rb_funcall_with_block_kw, 4);
401406
}
402407

403408
#ifdef __cplusplus

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@ VALUE proc_spec_rb_proc_call(VALUE self, VALUE prc, VALUE args) {
7676
return rb_proc_call(prc, args);
7777
}
7878

79+
VALUE proc_spec_rb_proc_call_kw(VALUE self, VALUE prc, VALUE args) {
80+
return rb_proc_call_kw(prc, args, RB_PASS_KEYWORDS);
81+
}
82+
83+
VALUE proc_spec_rb_proc_call_with_block(VALUE self, VALUE prc, VALUE args, VALUE block) {
84+
return rb_proc_call_with_block(prc, RARRAY_LENINT(args), RARRAY_PTR(args), block);
85+
}
86+
87+
static VALUE proc_spec_rb_proc_call_with_block_kw(VALUE self, VALUE prc, VALUE args, VALUE block) {
88+
return rb_proc_call_with_block_kw(prc, RARRAY_LENINT(args), RARRAY_PTR(args), block, RB_PASS_KEYWORDS);
89+
}
90+
7991
VALUE proc_spec_rb_obj_is_proc(VALUE self, VALUE prc) {
8092
return rb_obj_is_proc(prc);
8193
}
@@ -123,6 +135,9 @@ void Init_proc_spec(void) {
123135
rb_define_method(cls, "rb_proc_new_block_given_p", proc_spec_rb_proc_new_block_given_p, 0);
124136
rb_define_method(cls, "rb_proc_arity", proc_spec_rb_proc_arity, 1);
125137
rb_define_method(cls, "rb_proc_call", proc_spec_rb_proc_call, 2);
138+
rb_define_method(cls, "rb_proc_call_kw", proc_spec_rb_proc_call_kw, 2);
139+
rb_define_method(cls, "rb_proc_call_with_block", proc_spec_rb_proc_call_with_block, 3);
140+
rb_define_method(cls, "rb_proc_call_with_block_kw", proc_spec_rb_proc_call_with_block_kw, 3);
126141
rb_define_method(cls, "rb_Proc_new", proc_spec_rb_Proc_new, 1);
127142
rb_define_method(cls, "rb_obj_is_proc", proc_spec_rb_obj_is_proc, 1);
128143
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class CApiKernelSpecs
2+
class ClassWithPublicMethod
3+
def public_method(*, **)
4+
0
5+
end
6+
end
7+
8+
class ClassWithPrivateMethod
9+
private def private_method(*, **)
10+
0
11+
end
12+
end
13+
14+
class ClassWithProtectedMethod
15+
protected def protected_method(*, **)
16+
0
17+
end
18+
end
19+
end

spec/ruby/optional/capi/kernel_spec.rb

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require_relative 'spec_helper'
2+
require_relative 'fixtures/kernel'
23

34
kernel_path = load_extension("kernel")
45

@@ -597,6 +598,16 @@ def sum(a, b)
597598
@s.rb_funcallv(self, :empty, []).should == 42
598599
@s.rb_funcallv(self, :sum, [1, 2]).should == 3
599600
end
601+
602+
it "calls a private method" do
603+
object = CApiKernelSpecs::ClassWithPrivateMethod.new
604+
@s.rb_funcallv(object, :private_method, []).should == 0
605+
end
606+
607+
it "calls a protected method" do
608+
object = CApiKernelSpecs::ClassWithProtectedMethod.new
609+
@s.rb_funcallv(object, :protected_method, []).should == 0
610+
end
600611
end
601612

602613
ruby_version_is "3.0" do
@@ -621,6 +632,16 @@ def m(*args, **kwargs)
621632
@s.rb_funcallv_kw(self, :m, [42])
622633
}.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
623634
end
635+
636+
it "calls a private method" do
637+
object = CApiKernelSpecs::ClassWithPrivateMethod.new
638+
@s.rb_funcallv_kw(object, :private_method, [{}]).should == 0
639+
end
640+
641+
it "calls a protected method" do
642+
object = CApiKernelSpecs::ClassWithProtectedMethod.new
643+
@s.rb_funcallv_kw(object, :protected_method, [{}]).should == 0
644+
end
624645
end
625646

626647
describe "rb_keyword_given_p" do
@@ -676,21 +697,56 @@ def many_args(*args)
676697
end
677698

678699
describe 'rb_funcall_with_block' do
679-
before :each do
700+
it "calls a method with block" do
680701
@obj = Object.new
681702
class << @obj
682-
def method_public; yield end
683-
def method_private; yield end
684-
private :method_private
703+
def method_public(*args); [args, yield] end
685704
end
705+
706+
@s.rb_funcall_with_block(@obj, :method_public, [1, 2], proc { :result }).should == [[1, 2], :result]
686707
end
687708

688-
it "calls a method with block" do
689-
@s.rb_funcall_with_block(@obj, :method_public, proc { :result }).should == :result
709+
it "does not call a private method" do
710+
object = CApiKernelSpecs::ClassWithPrivateMethod.new
711+
712+
-> {
713+
@s.rb_funcall_with_block(object, :private_method, [], proc { })
714+
}.should raise_error(NoMethodError, /private/)
715+
end
716+
717+
it "does not call a protected method" do
718+
object = CApiKernelSpecs::ClassWithProtectedMethod.new
719+
720+
-> {
721+
@s.rb_funcall_with_block(object, :protected_method, [], proc { })
722+
}.should raise_error(NoMethodError, /protected/)
723+
end
724+
end
725+
726+
describe 'rb_funcall_with_block_kw' do
727+
it "calls a method with keyword arguments and a block" do
728+
@obj = Object.new
729+
class << @obj
730+
def method_public(*args, **kw, &block); [args, kw, block.call] end
731+
end
732+
733+
@s.rb_funcall_with_block_kw(@obj, :method_public, [1, 2, {a: 2}], proc { :result }).should == [[1, 2], {a: 2}, :result]
690734
end
691735

692736
it "does not call a private method" do
693-
-> { @s.rb_funcall_with_block(@obj, :method_private, proc { :result }) }.should raise_error(NoMethodError, /private/)
737+
object = CApiKernelSpecs::ClassWithPrivateMethod.new
738+
739+
-> {
740+
@s.rb_funcall_with_block_kw(object, :private_method, [{}], proc { })
741+
}.should raise_error(NoMethodError, /private/)
742+
end
743+
744+
it "does not call a protected method" do
745+
object = CApiKernelSpecs::ClassWithProtectedMethod.new
746+
747+
-> {
748+
@s.rb_funcall_with_block_kw(object, :protected_method, [{}], proc { })
749+
}.should raise_error(NoMethodError, /protected/)
694750
end
695751
end
696752
end

spec/ruby/optional/capi/proc_spec.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,63 @@
8282
end
8383
end
8484

85+
describe "rb_proc_call_kw" do
86+
it "passes keyword arguments to the proc" do
87+
prc = proc { |*args, **kw| [args, kw] }
88+
89+
@p.rb_proc_call_kw(prc, [{}]).should == [[], {}]
90+
@p.rb_proc_call_kw(prc, [{a: 1}]).should == [[], {a: 1}]
91+
@p.rb_proc_call_kw(prc, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}]
92+
@p.rb_proc_call_kw(prc, [{b: 2}, {}]).should == [[{b: 2}], {}]
93+
end
94+
95+
it "raises TypeError if the last argument is not a Hash" do
96+
prc = proc { |*args, **kwargs| [args, kw] }
97+
98+
-> {
99+
@p.rb_proc_call_kw(prc, [42])
100+
}.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
101+
end
102+
end
103+
104+
describe "rb_proc_call_with_block" do
105+
it "calls the Proc and passes arguments and a block" do
106+
prc = Proc.new { |a, b, &block| block.call(a * b) }
107+
@p.rb_proc_call_with_block(prc, [6, 7], proc { |n| n * 2 }).should == 6 * 7 * 2
108+
end
109+
110+
it "calls the Proc and passes arguments when a block is nil" do
111+
prc = Proc.new { |a, b| a * b }
112+
@p.rb_proc_call_with_block(prc, [6, 7], nil).should == 6 * 7
113+
end
114+
end
115+
116+
describe "rb_proc_call_with_block_kw" do
117+
it "passes keyword arguments and a block to the proc" do
118+
prc = proc { |*args, **kw, &block| [args, kw, block.call(42)] }
119+
block = proc { |n| n }
120+
121+
@p.rb_proc_call_with_block_kw(prc, [{}], block).should == [[], {}, 42]
122+
@p.rb_proc_call_with_block_kw(prc, [{a: 1}], block).should == [[], {a: 1}, 42]
123+
@p.rb_proc_call_with_block_kw(prc, [{b: 2}, {a: 1}], block).should == [[{b: 2}], {a: 1}, 42]
124+
@p.rb_proc_call_with_block_kw(prc, [{b: 2}, {}], block).should == [[{b: 2}], {}, 42]
125+
end
126+
127+
it "raises TypeError if the last argument is not a Hash" do
128+
prc = proc { |*args, **kwargs, &block| [args, kw, block.call(42)] }
129+
130+
-> {
131+
@p.rb_proc_call_with_block_kw(prc, [42], proc {})
132+
}.should raise_error(TypeError, 'no implicit conversion of Integer into Hash')
133+
end
134+
135+
it "passes keyword arguments to the proc when a block is nil" do
136+
prc = proc { |*args, **kw| [args, kw] }
137+
138+
@p.rb_proc_call_with_block_kw(prc, [{}], nil).should == [[], {}]
139+
end
140+
end
141+
85142
describe "rb_obj_is_proc" do
86143
it "returns true for Proc" do
87144
prc = Proc.new {|a,b| a * b }

src/main/c/cext/call.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,18 @@ VALUE rb_funcall_with_block(VALUE recv, ID mid, int argc, const VALUE *argv, VAL
106106
rb_tr_unwrap(pass_procval)));
107107
}
108108

109+
VALUE rb_funcall_with_block_kw(VALUE recv, ID mid, int argc, const VALUE *argv, VALUE procval, int kw_splat) {
110+
if (kw_splat && argc > 0) {
111+
return rb_tr_wrap(polyglot_invoke(RUBY_CEXT, "rb_funcall_with_block_keywords",
112+
rb_tr_unwrap(recv),
113+
rb_tr_unwrap(ID2SYM(mid)),
114+
polyglot_from_VALUE_array(argv, argc),
115+
rb_tr_unwrap(procval)));
116+
} else {
117+
return rb_funcall_with_block(recv, mid, argc, argv, procval);
118+
}
119+
}
120+
109121
VALUE rb_yield_splat(VALUE values) {
110122
if (rb_block_given_p()) {
111123
return RUBY_CEXT_INVOKE("rb_yield_splat", values);

src/main/c/cext/proc.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ VALUE rb_proc_call(VALUE self, VALUE args) {
1919
return RUBY_CEXT_INVOKE("rb_proc_call", self, args);
2020
}
2121

22+
VALUE rb_proc_call_kw(VALUE recv, VALUE args, int kw_splat) {
23+
return rb_funcallv_kw(recv, rb_intern("call"), RARRAY_LENINT(args), RARRAY_PTR(args), kw_splat);
24+
}
25+
26+
VALUE rb_proc_call_with_block(VALUE recv, int argc, const VALUE *argv, VALUE proc) {
27+
return rb_funcall_with_block(recv, rb_intern("call"), argc, argv, proc);
28+
}
29+
30+
VALUE rb_proc_call_with_block_kw(VALUE recv, int argc, const VALUE *argv, VALUE proc, int kw_splat) {
31+
return rb_funcall_with_block_kw(recv, rb_intern("call"), argc, argv, proc, kw_splat);
32+
}
33+
2234
int rb_proc_arity(VALUE self) {
2335
return polyglot_as_i32(RUBY_INVOKE_NO_WRAP(self, "arity"));
2436
}

0 commit comments

Comments
 (0)