mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* iseq.c (simple_default_value): extracts simplest default
argument value. * iseq.c (rb_iseq_parameters): returns parameter list. * proc.c (get_proc_iseq, get_method_iseq): handles ifunc and bmethod. * proc.c (rb_proc_parameters, rb_method_parameters): added Proc#parameters and Method#parameters. [ruby-core:19759] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@20384 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
4d3f3af836
commit
46e3b0fe7b
5 changed files with 273 additions and 2 deletions
13
ChangeLog
13
ChangeLog
|
@ -1,3 +1,16 @@
|
|||
Fri Nov 28 13:19:34 2008 Nobuyoshi Nakada <nobu@ruby-lang.org>
|
||||
|
||||
* iseq.c (simple_default_value): extracts simplest default
|
||||
argument value.
|
||||
|
||||
* iseq.c (rb_iseq_parameters): returns parameter list.
|
||||
|
||||
* proc.c (get_proc_iseq, get_method_iseq): handles ifunc and
|
||||
bmethod.
|
||||
|
||||
* proc.c (rb_proc_parameters, rb_method_parameters): added
|
||||
Proc#parameters and Method#parameters. [ruby-core:19759]
|
||||
|
||||
Fri Nov 28 02:18:47 2008 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||
|
||||
* ext/bigdecimal/bigdecimal.c (BigDecimal_DoDivmod): bigdecimal
|
||||
|
|
78
iseq.c
78
iseq.c
|
@ -1274,6 +1274,84 @@ rb_iseq_clone(VALUE iseqval, VALUE newcbase)
|
|||
return newiseq;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
simple_default_value(const VALUE *seq, const VALUE *eseq)
|
||||
{
|
||||
VALUE val;
|
||||
|
||||
again:
|
||||
switch (*seq++) {
|
||||
case BIN(trace):
|
||||
if (++seq >= eseq) return Qundef;
|
||||
goto again;
|
||||
case BIN(putnil):
|
||||
val = Qnil;
|
||||
goto got;
|
||||
case BIN(putobject):
|
||||
val = *seq++;
|
||||
got:
|
||||
switch (*seq++) {
|
||||
case BIN(setlocal):
|
||||
if ((seq+=1) != eseq) return Qundef;
|
||||
break;
|
||||
case BIN(setdynamic):
|
||||
if ((seq+=2) != eseq) return Qundef;
|
||||
break;
|
||||
}
|
||||
return val;
|
||||
default:
|
||||
return Qundef;
|
||||
}
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_iseq_parameters(const rb_iseq_t *iseq)
|
||||
{
|
||||
int i, r, s;
|
||||
VALUE a, args = rb_ary_new2(iseq->arg_size);
|
||||
ID req, opt, rest, block;
|
||||
#define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type))
|
||||
#define PARAM_ID(i) iseq->local_table[i]
|
||||
#define PARAM(i, type) ( \
|
||||
PARAM_TYPE(type), \
|
||||
rb_id2name(PARAM_ID(i)) ? \
|
||||
rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \
|
||||
a)
|
||||
|
||||
CONST_ID(req, "req");
|
||||
for (i = 0; i < iseq->argc; i++) {
|
||||
rb_ary_push(args, PARAM(i, req));
|
||||
}
|
||||
r = iseq->arg_rest != -1 ? iseq->arg_rest :
|
||||
iseq->arg_post_len > 0 ? iseq->arg_post_start :
|
||||
iseq->arg_block != -1 ? iseq->arg_block :
|
||||
iseq->arg_size;
|
||||
CONST_ID(opt, "opt");
|
||||
for (s = i; i < r; i++) {
|
||||
PARAM_TYPE(opt);
|
||||
if (rb_id2name(PARAM_ID(i))) {
|
||||
VALUE defval = simple_default_value(iseq->iseq + iseq->arg_opt_table[i-s],
|
||||
iseq->iseq + iseq->arg_opt_table[i-s+1]);
|
||||
rb_ary_push(a, ID2SYM(PARAM_ID(i)));
|
||||
if (defval != Qundef) rb_ary_push(a, defval);
|
||||
}
|
||||
rb_ary_push(args, a);
|
||||
}
|
||||
if (iseq->arg_rest != -1) {
|
||||
CONST_ID(rest, "rest");
|
||||
rb_ary_push(args, PARAM(iseq->arg_rest, rest));
|
||||
}
|
||||
r = iseq->arg_post_start + iseq->arg_post_len;
|
||||
for (i = iseq->arg_post_start; i < r; i++) {
|
||||
rb_ary_push(args, PARAM(i, req));
|
||||
}
|
||||
if (iseq->arg_block != -1) {
|
||||
CONST_ID(block, "block");
|
||||
rb_ary_push(args, PARAM(iseq->arg_block, block));
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
/* ruby2cext */
|
||||
|
||||
VALUE
|
||||
|
|
71
proc.c
71
proc.c
|
@ -25,9 +25,12 @@ VALUE rb_cMethod;
|
|||
VALUE rb_cBinding;
|
||||
VALUE rb_cProc;
|
||||
|
||||
VALUE rb_iseq_parameters(const rb_iseq_t *iseq);
|
||||
|
||||
static VALUE bmcall(VALUE, VALUE);
|
||||
static int method_arity(VALUE);
|
||||
static VALUE rb_obj_is_method(VALUE m);
|
||||
static rb_iseq_t *get_method_iseq(VALUE method);
|
||||
|
||||
/* Proc */
|
||||
|
||||
|
@ -615,8 +618,14 @@ get_proc_iseq(VALUE self)
|
|||
|
||||
GetProcPtr(self, proc);
|
||||
iseq = proc->block.iseq;
|
||||
if (!RUBY_VM_NORMAL_ISEQ_P(iseq))
|
||||
return 0;
|
||||
if (!RUBY_VM_NORMAL_ISEQ_P(iseq)) {
|
||||
NODE *node = (NODE *)iseq;
|
||||
iseq = 0;
|
||||
if (nd_type(node) == NODE_IFUNC && node->nd_cfnc == bmcall) {
|
||||
/* method(:foo).to_proc */
|
||||
iseq = get_method_iseq(node->nd_tval);
|
||||
}
|
||||
}
|
||||
return iseq;
|
||||
}
|
||||
|
||||
|
@ -650,6 +659,42 @@ rb_proc_location(VALUE self)
|
|||
return iseq_location(get_proc_iseq(self));
|
||||
}
|
||||
|
||||
static VALUE
|
||||
unnamed_parameters(int arity)
|
||||
{
|
||||
VALUE a, param = rb_ary_new2((arity < 0) ? -arity : arity);
|
||||
int n = (arity < 0) ? ~arity : arity;
|
||||
ID req, rest;
|
||||
CONST_ID(req, "req");
|
||||
a = rb_ary_new3(1, ID2SYM(req));
|
||||
OBJ_FREEZE(a);
|
||||
for (; n; --n) {
|
||||
rb_ary_push(param, a);
|
||||
}
|
||||
if (arity < 0) {
|
||||
CONST_ID(rest, "rest");
|
||||
rb_ary_store(param, ~arity, rb_ary_new3(1, ID2SYM(rest)));
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* proc.parameters => array
|
||||
*
|
||||
* returns the parameter information of this proc
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_proc_parameters(VALUE self)
|
||||
{
|
||||
rb_iseq_t *iseq = get_proc_iseq(self);
|
||||
if (!iseq) {
|
||||
return unnamed_parameters(proc_arity(self));
|
||||
}
|
||||
return rb_iseq_parameters(iseq);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* prc == other_proc => true or false
|
||||
|
@ -1463,6 +1508,8 @@ get_method_iseq(VALUE method)
|
|||
Data_Get_Struct(method, struct METHOD, data);
|
||||
body = data->body;
|
||||
switch (nd_type(body)) {
|
||||
case NODE_BMETHOD:
|
||||
return get_proc_iseq(body->nd_cval);
|
||||
case RUBY_VM_METHOD_NODE:
|
||||
GetISeqPtr((VALUE)body->nd_body, iseq);
|
||||
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) break;
|
||||
|
@ -1486,6 +1533,23 @@ rb_method_location(VALUE method)
|
|||
return iseq_location(get_method_iseq(method));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* meth.parameters => array
|
||||
*
|
||||
* returns the parameter information of this method
|
||||
*/
|
||||
|
||||
static VALUE
|
||||
rb_method_parameters(VALUE method)
|
||||
{
|
||||
rb_iseq_t *iseq = get_method_iseq(method);
|
||||
if (!iseq) {
|
||||
return unnamed_parameters(method_arity(method));
|
||||
}
|
||||
return rb_iseq_parameters(iseq);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* meth.to_s => string
|
||||
|
@ -1814,6 +1878,7 @@ Init_Proc(void)
|
|||
rb_define_method(rb_cProc, "binding", proc_binding, 0);
|
||||
rb_define_method(rb_cProc, "curry", proc_curry, -1);
|
||||
rb_define_method(rb_cProc, "source_location", rb_proc_location, 0);
|
||||
rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0);
|
||||
|
||||
/* Exceptions */
|
||||
rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError);
|
||||
|
@ -1849,6 +1914,7 @@ Init_Proc(void)
|
|||
rb_define_method(rb_cMethod, "owner", method_owner, 0);
|
||||
rb_define_method(rb_cMethod, "unbind", method_unbind, 0);
|
||||
rb_define_method(rb_cMethod, "source_location", rb_method_location, 0);
|
||||
rb_define_method(rb_cMethod, "parameters", rb_method_parameters, 0);
|
||||
rb_define_method(rb_mKernel, "method", rb_obj_method, 1);
|
||||
rb_define_method(rb_mKernel, "public_method", rb_obj_public_method, 1);
|
||||
|
||||
|
@ -1867,6 +1933,7 @@ Init_Proc(void)
|
|||
rb_define_method(rb_cUnboundMethod, "owner", method_owner, 0);
|
||||
rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1);
|
||||
rb_define_method(rb_cUnboundMethod, "source_location", rb_method_location, 0);
|
||||
rb_define_method(rb_cUnboundMethod, "parameters", rb_method_parameters, 0);
|
||||
|
||||
/* Module#*_method */
|
||||
rb_define_method(rb_cModule, "instance_method", rb_mod_instance_method, 1);
|
||||
|
|
|
@ -20,6 +20,8 @@ class TestMethod < Test::Unit::TestCase
|
|||
def mo4(a, *b, &c) end
|
||||
def mo5(a, *b, c) end
|
||||
def mo6(a, *b, c, &d) end
|
||||
def mo7(a, b = nil, *c, d, &e) end
|
||||
def ma1((a), &b) end
|
||||
|
||||
class Base
|
||||
def foo() :base end
|
||||
|
@ -237,4 +239,72 @@ class TestMethod < Test::Unit::TestCase
|
|||
assert !M.public_instance_methods.include?(:func), 'module methods are private by default'
|
||||
assert M.public_instance_methods.include?(:meth), 'normal methods are public by default'
|
||||
end
|
||||
|
||||
define_method(:pm0) {||}
|
||||
define_method(:pm1) {|a|}
|
||||
define_method(:pm2) {|a, b|}
|
||||
define_method(:pmo1) {|a = nil, &b|}
|
||||
define_method(:pmo2) {|a, b = nil|}
|
||||
define_method(:pmo3) {|*a|}
|
||||
define_method(:pmo4) {|a, *b, &c|}
|
||||
define_method(:pmo5) {|a, *b, c|}
|
||||
define_method(:pmo6) {|a, *b, c, &d|}
|
||||
define_method(:pmo7) {|a, b = nil, *c, d, &e|}
|
||||
define_method(:pma1) {|(a), &b|}
|
||||
|
||||
def test_bound_parameters
|
||||
assert_equal([], method(:m0).parameters)
|
||||
assert_equal([[:req, :a]], method(:m1).parameters)
|
||||
assert_equal([[:req, :a], [:req, :b]], method(:m2).parameters)
|
||||
assert_equal([[:opt, :a, nil], [:block, :b]], method(:mo1).parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, nil]], method(:mo2).parameters)
|
||||
assert_equal([[:rest, :a]], method(:mo3).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:mo4).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:mo5).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:mo6).parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], method(:mo7).parameters)
|
||||
assert_equal([[:req], [:block, :b]], method(:ma1).parameters)
|
||||
end
|
||||
|
||||
def test_unbound_parameters
|
||||
assert_equal([], self.class.instance_method(:m0).parameters)
|
||||
assert_equal([[:req, :a]], self.class.instance_method(:m1).parameters)
|
||||
assert_equal([[:req, :a], [:req, :b]], self.class.instance_method(:m2).parameters)
|
||||
assert_equal([[:opt, :a, nil], [:block, :b]], self.class.instance_method(:mo1).parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, nil]], self.class.instance_method(:mo2).parameters)
|
||||
assert_equal([[:rest, :a]], self.class.instance_method(:mo3).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:block, :c]], self.class.instance_method(:mo4).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c]], self.class.instance_method(:mo5).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:mo6).parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:mo7).parameters)
|
||||
assert_equal([[:req], [:block, :b]], self.class.instance_method(:ma1).parameters)
|
||||
end
|
||||
|
||||
def test_bmethod_bound_parameters
|
||||
assert_equal([], method(:pm0).parameters)
|
||||
assert_equal([[:req, :a]], method(:pm1).parameters)
|
||||
assert_equal([[:req, :a], [:req, :b]], method(:pm2).parameters)
|
||||
assert_equal([[:opt, :a, nil], [:block, :b]], method(:pmo1).parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, nil]], method(:pmo2).parameters)
|
||||
assert_equal([[:rest, :a]], method(:pmo3).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:pmo4).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:pmo5).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).parameters)
|
||||
assert_equal([[:req], [:block, :b]], method(:pma1).parameters)
|
||||
end
|
||||
|
||||
def test_bmethod_unbound_parameters
|
||||
assert_equal([], self.class.instance_method(:pm0).parameters)
|
||||
assert_equal([[:req, :a]], self.class.instance_method(:pm1).parameters)
|
||||
assert_equal([[:req, :a], [:req, :b]], self.class.instance_method(:pm2).parameters)
|
||||
assert_equal([[:opt, :a, nil], [:block, :b]], self.class.instance_method(:pmo1).parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, nil]], self.class.instance_method(:pmo2).parameters)
|
||||
assert_equal([[:rest, :a]], self.class.instance_method(:pmo3).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:block, :c]], self.class.instance_method(:pmo4).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c]], self.class.instance_method(:pmo5).parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:pmo6).parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, nil], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:pmo7).parameters)
|
||||
assert_equal([[:req], [:block, :b]], self.class.instance_method(:pma1).parameters)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -673,4 +673,47 @@ class TestProc < Test::Unit::TestCase
|
|||
}.call(1,2,3,4,5)
|
||||
assert_equal([1,2,[3],4,5], r, "[ruby-core:19485]")
|
||||
end
|
||||
|
||||
def test_parameters
|
||||
assert_equal([], proc {}.parameters)
|
||||
assert_equal([], proc {||}.parameters)
|
||||
assert_equal([[:req, :a]], proc {|a|}.parameters)
|
||||
assert_equal([[:req, :a], [:req, :b]], proc {|a, b|}.parameters)
|
||||
assert_equal([[:opt, :a, :a], [:block, :b]], proc {|a=:a, &b|}.parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, :b]], proc {|a, b=:b|}.parameters)
|
||||
assert_equal([[:rest, :a]], proc {|*a|}.parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:block, :c]], proc {|a, *b, &c|}.parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c]], proc {|a, *b, c|}.parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], proc {|a, *b, c, &d|}.parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, :b], [:rest, :c], [:req, :d], [:block, :e]], proc {|a, b=:b, *c, d, &e|}.parameters)
|
||||
assert_equal([[:req], [:block, :b]], proc {|(a), &b|}.parameters)
|
||||
assert_equal([[:req, :a], [:req, :b], [:opt, :c, :c], [:opt, :d, :d], [:rest, :e], [:req, :f], [:req, :g], [:block, :h]], proc {|a,b,c=:c,d=:d,*e,f,g,&h|}.parameters)
|
||||
end
|
||||
|
||||
def pm0() end
|
||||
def pm1(a) end
|
||||
def pm2(a, b) end
|
||||
def pmo1(a = :a, &b) end
|
||||
def pmo2(a, b = :b) end
|
||||
def pmo3(*a) end
|
||||
def pmo4(a, *b, &c) end
|
||||
def pmo5(a, *b, c) end
|
||||
def pmo6(a, *b, c, &d) end
|
||||
def pmo7(a, b = :b, *c, d, &e) end
|
||||
def pma1((a), &b) end
|
||||
|
||||
|
||||
def test_bound_parameters
|
||||
assert_equal([], method(:pm0).to_proc.parameters)
|
||||
assert_equal([[:req, :a]], method(:pm1).to_proc.parameters)
|
||||
assert_equal([[:req, :a], [:req, :b]], method(:pm2).to_proc.parameters)
|
||||
assert_equal([[:opt, :a, :a], [:block, :b]], method(:pmo1).to_proc.parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, :b]], method(:pmo2).to_proc.parameters)
|
||||
assert_equal([[:rest, :a]], method(:pmo3).to_proc.parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:block, :c]], method(:pmo4).to_proc.parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:pmo5).to_proc.parameters)
|
||||
assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).to_proc.parameters)
|
||||
assert_equal([[:req, :a], [:opt, :b, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).to_proc.parameters)
|
||||
assert_equal([[:req], [:block, :b]], method(:pma1).to_proc.parameters)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue