Skip to content

Commit 7f6bbb3

Browse files
committed
[GR-51310] Implement rb_check_funcall()
PullRequest: truffleruby/4122
2 parents 3f0834f + 6d79de8 commit 7f6bbb3

File tree

7 files changed

+65
-8
lines changed

7 files changed

+65
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Compatibility:
3030
* Support `Socket.sockaddr_in(port, Socket::INADDR_ANY)` (#3361, @mtortonesi).
3131
* Implement the `Data` class from Ruby 3.2 (#3039, @moste00, @eregon).
3232
* Make `Coverage.start` and `Coverage.result` accept parameters (#3149, @mtortonesi, @andrykonchin).
33+
* Implement `rb_check_funcall()` (@eregon).
3334

3435
Performance:
3536

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

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

13-
#define TRUFFLERUBY_ABI_VERSION "3.2.2.9"
13+
#define TRUFFLERUBY_ABI_VERSION "3.2.2.10"
1414

1515
#endif

lib/truffle/truffle/cext.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,10 @@ def rb_funcallv(recv, meth, argv)
10601060
Primitive.send_argv_without_cext_lock(recv, meth, argv, nil)
10611061
end
10621062

1063+
def rb_check_funcall(recv, meth, args)
1064+
Primitive.send_without_cext_lock(Truffle::Type, :check_funcall, [recv, meth, args], nil)
1065+
end
1066+
10631067
def rb_funcallv_keywords(recv, meth, argv)
10641068
Primitive.send_argv_keywords_without_cext_lock(recv, meth, argv, nil)
10651069
end

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,15 @@ static VALUE kernel_spec_rb_funcall_many_args(VALUE self, VALUE obj, VALUE metho
355355
INT2FIX(5), INT2FIX(4), INT2FIX(3), INT2FIX(2), INT2FIX(1));
356356
}
357357

358+
static VALUE kernel_spec_rb_check_funcall(VALUE self, VALUE receiver, VALUE method, VALUE args) {
359+
VALUE ret = rb_check_funcall(receiver, SYM2ID(method), RARRAY_LENINT(args), RARRAY_PTR(args));
360+
if (ret == Qundef) {
361+
return ID2SYM(rb_intern("Qundef"));
362+
} else {
363+
return ret;
364+
}
365+
}
366+
358367
void Init_kernel_spec(void) {
359368
VALUE cls = rb_define_class("CApiKernelSpecs", rb_cObject);
360369
rb_define_method(cls, "rb_block_given_p", kernel_spec_rb_block_given_p, 0);
@@ -403,6 +412,7 @@ void Init_kernel_spec(void) {
403412
rb_define_method(cls, "rb_funcall_many_args", kernel_spec_rb_funcall_many_args, 2);
404413
rb_define_method(cls, "rb_funcall_with_block", kernel_spec_rb_funcall_with_block, 4);
405414
rb_define_method(cls, "rb_funcall_with_block_kw", kernel_spec_rb_funcall_with_block_kw, 4);
415+
rb_define_method(cls, "rb_check_funcall", kernel_spec_rb_check_funcall, 3);
406416
}
407417

408418
#ifdef __cplusplus
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
class CApiKernelSpecs
22
class ClassWithPublicMethod
33
def public_method(*, **)
4-
0
4+
:public
55
end
66
end
77

88
class ClassWithPrivateMethod
99
private def private_method(*, **)
10-
0
10+
:private
1111
end
1212
end
1313

1414
class ClassWithProtectedMethod
1515
protected def protected_method(*, **)
16-
0
16+
:protected
1717
end
1818
end
1919
end

spec/ruby/optional/capi/kernel_spec.rb

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -606,12 +606,12 @@ def sum(a, b)
606606

607607
it "calls a private method" do
608608
object = CApiKernelSpecs::ClassWithPrivateMethod.new
609-
@s.rb_funcallv(object, :private_method, []).should == 0
609+
@s.rb_funcallv(object, :private_method, []).should == :private
610610
end
611611

612612
it "calls a protected method" do
613613
object = CApiKernelSpecs::ClassWithProtectedMethod.new
614-
@s.rb_funcallv(object, :protected_method, []).should == 0
614+
@s.rb_funcallv(object, :protected_method, []).should == :protected
615615
end
616616
end
617617

@@ -629,12 +629,12 @@ def m(*args, **kwargs)
629629

630630
it "calls a private method" do
631631
object = CApiKernelSpecs::ClassWithPrivateMethod.new
632-
@s.rb_funcallv_kw(object, :private_method, [{}]).should == 0
632+
@s.rb_funcallv_kw(object, :private_method, [{}]).should == :private
633633
end
634634

635635
it "calls a protected method" do
636636
object = CApiKernelSpecs::ClassWithProtectedMethod.new
637-
@s.rb_funcallv_kw(object, :protected_method, [{}]).should == 0
637+
@s.rb_funcallv_kw(object, :protected_method, [{}]).should == :protected
638638
end
639639

640640
it "raises TypeError if the last argument is not a Hash" do
@@ -752,4 +752,39 @@ def method_public(*args, **kw, &block); [args, kw, block.call] end
752752
}.should raise_error(NoMethodError, /protected/)
753753
end
754754
end
755+
756+
describe "rb_check_funcall" do
757+
it "calls a method" do
758+
@s.rb_check_funcall(1, :+, [2]).should == 3
759+
end
760+
761+
it "returns Qundef if the method is not defined" do
762+
obj = Object.new
763+
@s.rb_check_funcall(obj, :foo, []).should == :Qundef
764+
end
765+
766+
it "uses #respond_to? to check if the method is defined" do
767+
ScratchPad.record []
768+
obj = Object.new
769+
def obj.respond_to?(name, priv)
770+
ScratchPad << name
771+
name == :foo || super
772+
end
773+
def obj.method_missing(name, *args)
774+
name == :foo ? [name, 42] : super
775+
end
776+
@s.rb_check_funcall(obj, :foo, []).should == [:foo, 42]
777+
ScratchPad.recorded.should == [:foo]
778+
end
779+
780+
it "calls a private method" do
781+
object = CApiKernelSpecs::ClassWithPrivateMethod.new
782+
@s.rb_check_funcall(object, :private_method, []).should == :private
783+
end
784+
785+
it "calls a protected method" do
786+
object = CApiKernelSpecs::ClassWithProtectedMethod.new
787+
@s.rb_check_funcall(object, :protected_method, []).should == :protected
788+
end
789+
end
755790
end

src/main/c/cext/call.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ VALUE rb_funcallv_public(VALUE object, ID name, int args_count, const VALUE *arg
4949
polyglot_from_VALUE_array(args, args_count)));
5050
}
5151

52+
VALUE rb_check_funcall(VALUE object, ID name, int args_count, const VALUE *args) {
53+
return rb_tr_wrap(polyglot_invoke(RUBY_CEXT, "rb_check_funcall",
54+
rb_tr_unwrap(object),
55+
rb_tr_unwrap(ID2SYM(name)),
56+
rb_tr_unwrap(rb_ary_new_from_values(args_count, args))));
57+
}
58+
5259
VALUE rb_apply(VALUE object, ID name, VALUE args) {
5360
return RUBY_CEXT_INVOKE("rb_apply", object, ID2SYM(name), args);
5461
}

0 commit comments

Comments
 (0)