summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoichi Sasada <ko1@atdot.net>2021-12-13 01:58:21 +0900
committerKoichi Sasada <ko1@atdot.net>2021-12-13 10:23:52 +0900
commit6659253cc6c807641e23d469b425ddcf18de7af4 (patch)
tree0fb1ac56a26c1c045bec12965b6ebdabab58ce9c
parent4d0cb1a54ba5e8e053e6acc860fd1cb9ca5e1b19 (diff)
Struct setter's parameters == `[:req, :_]`
fix [Bug #18405] Note that the parameter name `_` is not a spec, so we shouldn't rely on this behavior.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/5252
-rw-r--r--proc.c71
-rw-r--r--test/ruby/test_struct.rb15
2 files changed, 80 insertions, 6 deletions
diff --git a/proc.c b/proc.c
index 6933db36d6..d0451244ce 100644
--- a/proc.c
+++ b/proc.c
@@ -1259,7 +1259,7 @@ rb_proc_get_iseq(VALUE self, int *is_proc)
return rb_proc_get_iseq(block->as.proc, is_proc);
case block_type_ifunc:
{
- const struct vm_ifunc *ifunc = block->as.captured.code.ifunc;
+ const struct vm_ifunc *ifunc = block->as.captured.code.ifunc;
if (IS_METHOD_PROC_IFUNC(ifunc)) {
/* method(:foo).to_proc */
if (is_proc) *is_proc = 0;
@@ -2910,6 +2910,69 @@ rb_method_location(VALUE method)
return method_def_location(rb_method_def(method));
}
+static const rb_method_definition_t *
+vm_proc_method_def(VALUE procval)
+{
+ const rb_proc_t *proc;
+ const struct rb_block *block;
+ const struct vm_ifunc *ifunc;
+
+ GetProcPtr(procval, proc);
+ block = &proc->block;
+
+ if (vm_block_type(block) == block_type_ifunc &&
+ IS_METHOD_PROC_IFUNC(ifunc = block->as.captured.code.ifunc)) {
+ return rb_method_def((VALUE)ifunc->data);
+ }
+ else {
+ return NULL;
+ }
+}
+
+static VALUE
+method_def_parameters(const rb_method_definition_t *def)
+{
+ const rb_iseq_t *iseq;
+ const rb_method_definition_t *bmethod_def;
+
+ switch (def->type) {
+ case VM_METHOD_TYPE_ISEQ:
+ iseq = method_def_iseq(def);
+ return rb_iseq_parameters(iseq, 0);
+ case VM_METHOD_TYPE_BMETHOD:
+ if ((iseq = method_def_iseq(def)) != NULL) {
+ return rb_iseq_parameters(iseq, 0);
+ }
+ else if ((bmethod_def = vm_proc_method_def(def->body.bmethod.proc)) != NULL) {
+ return method_def_parameters(bmethod_def);
+ }
+ break;
+
+ case VM_METHOD_TYPE_ALIAS:
+ return method_def_parameters(def->body.alias.original_me->def);
+
+ case VM_METHOD_TYPE_OPTIMIZED:
+ if (def->body.optimized.type == OPTIMIZED_METHOD_TYPE_STRUCT_ASET) {
+ VALUE param = rb_ary_new_from_args(2, ID2SYM(rb_intern("req")), ID2SYM(rb_intern("_")));
+ return rb_ary_new_from_args(1, param);
+ }
+ break;
+
+ case VM_METHOD_TYPE_CFUNC:
+ case VM_METHOD_TYPE_ATTRSET:
+ case VM_METHOD_TYPE_IVAR:
+ case VM_METHOD_TYPE_ZSUPER:
+ case VM_METHOD_TYPE_UNDEF:
+ case VM_METHOD_TYPE_NOTIMPLEMENTED:
+ case VM_METHOD_TYPE_MISSING:
+ case VM_METHOD_TYPE_REFINED:
+ break;
+ }
+
+ return rb_unnamed_parameters(method_def_aritry(def));
+
+}
+
/*
* call-seq:
* meth.parameters -> array
@@ -2932,11 +2995,7 @@ rb_method_location(VALUE method)
static VALUE
rb_method_parameters(VALUE method)
{
- const rb_iseq_t *iseq = rb_method_iseq(method);
- if (!iseq) {
- return rb_unnamed_parameters(method_arity(method));
- }
- return rb_iseq_parameters(iseq, 0);
+ return method_def_parameters(rb_method_def(method));
}
/*
diff --git a/test/ruby/test_struct.rb b/test/ruby/test_struct.rb
index 0301612395..b0e6bae9e3 100644
--- a/test/ruby/test_struct.rb
+++ b/test/ruby/test_struct.rb
@@ -497,6 +497,21 @@ module TestStruct
assert_equal(42, x.public_send("a"))
end
+ def test_parameters
+ klass = @Struct.new(:a)
+ assert_equal [], klass.instance_method(:a).parameters
+ # NOTE: :_ may not be a spec.
+ assert_equal [[:req, :_]], klass.instance_method(:a=).parameters
+
+ klass.module_eval do
+ define_method(:b=, &klass.new.method(:a=).to_proc)
+ alias c= a=
+ end
+
+ assert_equal [[:req, :_]], klass.instance_method(:b=).parameters
+ assert_equal [[:req, :_]], klass.instance_method(:c=).parameters
+ end
+
class TopStruct < Test::Unit::TestCase
include TestStruct