Class: UnboundMethod

Inherits:
Object show all
Defined in:
proc.c,
proc.c

Overview

********************************************************************

Ruby supports two forms of objectified methods. Class Method is
used to represent methods that are associated with a particular
object: these method objects are bound to that object. Bound
method objects for an object can be created using Object#method.

Ruby also supports unbound methods; methods objects that are not
associated with a particular object. These can be created either
by calling Module#instance_method or by calling #unbind on a bound
method object. The result of both of these is an UnboundMethod
object.

Unbound methods can only be called after they are bound to an
object. That object must be a kind_of? the method's original
class.

   class Square
     def area
       @side * @side
     end
     def initialize(side)
       @side = side
     end
   end

   area_un = Square.instance_method(:area)

   s = Square.new(12)
   area = area_un.bind(s)
   area.call   #=> 144

Unbound methods are a reference to the method at the time it was
objectified: subsequent changes to the underlying class will not
affect the unbound method.

   class Test
     def test
       :original
     end
   end
   um = Test.instance_method(:test)
   class Test
     def test
       :modified
     end
   end
   t = Test.new
   t.test            #=> :modified
   um.bind(t).call   #=> :original

Instance Method Summary collapse

Instance Method Details

#==Object

#arityInteger

Returns an indication of the number of arguments accepted by a method. Returns a nonnegative integer for methods that take a fixed number of arguments. For Ruby methods that take a variable number of arguments, returns -n-1, where n is the number of required arguments. Keyword arguments will be considered as a single additional argument, that argument being mandatory if any keyword argument is mandatory. For methods written in C, returns -1 if the call takes a variable number of arguments.

class C
  def one;    end
  def two(a); end
  def three(*a);  end
  def four(a, b); end
  def five(a, b, *c);    end
  def six(a, b, *c, &d); end
  def seven(a, b, x:0); end
  def eight(x:, y:); end
  def nine(x:, y:, **z); end
  def ten(*a, x:, y:); end
end
c = C.new
c.method(:one).arity     #=> 0
c.method(:two).arity     #=> 1
c.method(:three).arity   #=> -1
c.method(:four).arity    #=> 2
c.method(:five).arity    #=> -3
c.method(:six).arity     #=> -3
c.method(:seven).arity   #=> -3
c.method(:eight).arity   #=> 1
c.method(:nine).arity    #=> 1
c.method(:ten).arity     #=> -2

"cat".method(:size).arity      #=> 0
"cat".method(:replace).arity   #=> 1
"cat".method(:squeeze).arity   #=> -1
"cat".method(:count).arity     #=> -1

Returns:



2858
2859
2860
2861
2862
2863
# File 'proc.c', line 2858

static VALUE
method_arity_m(VALUE method)
{
    int n = method_arity(method);
    return INT2FIX(n);
}

#bind(obj) ⇒ Object

Bind umeth to obj. If Klass was the class from which umeth was obtained, obj.kind_of?(Klass) must be true.

class A
  def test
    puts "In test, class = #{self.class}"
  end
end
class B < A
end
class C < B
end

um = B.instance_method(:test)
bm = um.bind(C.new)
bm.call
bm = um.bind(B.new)
bm.call
bm = um.bind(A.new)
bm.call

produces:

In test, class = C
In test, class = B
prog.rb:16:in `bind': bind argument must be an instance of B (TypeError)
	from prog.rb:16


2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
# File 'proc.c', line 2678

static VALUE
umethod_bind(VALUE method, VALUE recv)
{
    VALUE methclass, klass, iclass;
    const rb_method_entry_t *me;
    const struct METHOD *data;
    TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
    convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, true);

    struct METHOD *bound;
    method = TypedData_Make_Struct(rb_cMethod, struct METHOD, &method_data_type, bound);
    RB_OBJ_WRITE(method, &bound->recv, recv);
    RB_OBJ_WRITE(method, &bound->klass, klass);
    RB_OBJ_WRITE(method, &bound->iclass, iclass);
    RB_OBJ_WRITE(method, &bound->owner, methclass);
    RB_OBJ_WRITE(method, &bound->me, me);

    return method;
}

#bind_call(recv, args, ...) ⇒ Object

Bind umeth to recv and then invokes the method with the specified arguments. This is semantically equivalent to umeth.bind(recv).call(args, ...).

Returns:



2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
# File 'proc.c', line 2706

static VALUE
umethod_bind_call(int argc, VALUE *argv, VALUE method)
{
    rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
    VALUE recv = argv[0];
    argc--;
    argv++;

    VALUE passed_procval = rb_block_given_p() ? rb_block_proc() : Qnil;
    rb_execution_context_t *ec = GET_EC();

    const struct METHOD *data;
    TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);

    const rb_callable_method_entry_t *cme = rb_callable_method_entry(CLASS_OF(recv), data->me->called_id);
    if (data->me == (const rb_method_entry_t *)cme) {
        vm_passed_block_handler_set(ec, proc_to_block_handler(passed_procval));
        return rb_vm_call_kw(ec, recv, cme->called_id, argc, argv, cme, RB_PASS_CALLED_KEYWORDS);
    }
    else {
        VALUE methclass, klass, iclass;
        const rb_method_entry_t *me;
        convert_umethod_to_method_components(data, recv, &methclass, &klass, &iclass, &me, false);
        struct METHOD bound = { recv, klass, 0, methclass, me };

        return call_method_data(ec, &bound, argc, argv, passed_procval, RB_PASS_CALLED_KEYWORDS);
    }
}

#cloneObject

Returns a clone of this method.

class A
  def foo
    return "bar"
  end
end

m = A.new.method(:foo)
m.call # => "bar"
n = m.clone.call # => "bar"


2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
# File 'proc.c', line 2402

static VALUE
method_clone(VALUE self)
{
    VALUE clone;
    struct METHOD *orig, *data;

    TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
    clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data);
    rb_obj_clone_setup(self, clone, Qnil);
    RB_OBJ_WRITE(clone, &data->recv, orig->recv);
    RB_OBJ_WRITE(clone, &data->klass, orig->klass);
    RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);
    RB_OBJ_WRITE(clone, &data->owner, orig->owner);
    RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me));
    return clone;
}

#dupObject

:nodoc:



2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
# File 'proc.c', line 2420

static VALUE
method_dup(VALUE self)
{
    VALUE clone;
    struct METHOD *orig, *data;

    TypedData_Get_Struct(self, struct METHOD, &method_data_type, orig);
    clone = TypedData_Make_Struct(CLASS_OF(self), struct METHOD, &method_data_type, data);
    rb_obj_dup_setup(self, clone);
    RB_OBJ_WRITE(clone, &data->recv, orig->recv);
    RB_OBJ_WRITE(clone, &data->klass, orig->klass);
    RB_OBJ_WRITE(clone, &data->iclass, orig->iclass);
    RB_OBJ_WRITE(clone, &data->owner, orig->owner);
    RB_OBJ_WRITE(clone, &data->me, rb_method_entry_clone(orig->me));
    return clone;
}

#eql?Boolean

Returns:

  • (Boolean)

#hashInteger

Returns a hash value corresponding to the method object.

See also Object#hash.

Returns:



1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
# File 'proc.c', line 1866

static VALUE
method_hash(VALUE method)
{
    struct METHOD *m;
    st_index_t hash;

    TypedData_Get_Struct(method, struct METHOD, &method_data_type, m);
    hash = rb_hash_start((st_index_t)m->recv);
    hash = rb_hash_method_entry(hash, m->me);
    hash = rb_hash_end(hash);

    return ST2FIX(hash);
}

#to_sString #inspectString

Returns a human-readable description of the underlying method.

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
(1..3).method(:map).inspect    #=> "#<Method: Range(Enumerable)#map()>"

In the latter case, the method description includes the “owner” of the original method (Enumerable module, which is included into Range).

inspect also provides, when possible, method argument names (call sequence) and source location.

require 'net/http'
Net::HTTP.method(:get).inspect
#=> "#<Method: Net::HTTP.get(uri_or_host, path=..., port=...) <skip>/lib/ruby/2.7.0/net/http.rb:457>"

... in argument definition means argument is optional (has some default value).

For methods defined in C (language core and extensions), location and argument names can’t be extracted, and only generic information is provided in form of * (any number of arguments) or _ (some positional argument).

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
"cat".method(:+).inspect       #=> "#<Method: String#+(_)>""

Overloads:



3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
# File 'proc.c', line 3135

static VALUE
method_inspect(VALUE method)
{
    struct METHOD *data;
    VALUE str;
    const char *sharp = "#";
    VALUE mklass;
    VALUE defined_class;

    TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
    str = rb_sprintf("#<% "PRIsVALUE": ", rb_obj_class(method));

    mklass = data->iclass;
    if (!mklass) mklass = data->klass;

    if (RB_TYPE_P(mklass, T_ICLASS)) {
        /* TODO: I'm not sure why mklass is T_ICLASS.
         * UnboundMethod#bind() can set it as T_ICLASS at convert_umethod_to_method_components()
         * but not sure it is needed.
         */
        mklass = RBASIC_CLASS(mklass);
    }

    if (data->me->def->type == VM_METHOD_TYPE_ALIAS) {
        defined_class = data->me->def->body.alias.original_me->owner;
    }
    else {
        defined_class = method_entry_defined_class(data->me);
    }

    if (RB_TYPE_P(defined_class, T_ICLASS)) {
        defined_class = RBASIC_CLASS(defined_class);
    }

    if (data->recv == Qundef) {
        // UnboundMethod
        rb_str_buf_append(str, rb_inspect(defined_class));
    }
    else if (FL_TEST(mklass, FL_SINGLETON)) {
        VALUE v = RCLASS_ATTACHED_OBJECT(mklass);

        if (UNDEF_P(data->recv)) {
            rb_str_buf_append(str, rb_inspect(mklass));
        }
        else if (data->recv == v) {
            rb_str_buf_append(str, rb_inspect(v));
            sharp = ".";
        }
        else {
            rb_str_buf_append(str, rb_inspect(data->recv));
            rb_str_buf_cat2(str, "(");
            rb_str_buf_append(str, rb_inspect(v));
            rb_str_buf_cat2(str, ")");
            sharp = ".";
        }
    }
    else {
        mklass = data->klass;
        if (FL_TEST(mklass, FL_SINGLETON)) {
            VALUE v = RCLASS_ATTACHED_OBJECT(mklass);
            if (!(RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE))) {
                do {
                   mklass = RCLASS_SUPER(mklass);
                } while (RB_TYPE_P(mklass, T_ICLASS));
            }
        }
        rb_str_buf_append(str, rb_inspect(mklass));
        if (defined_class != mklass) {
            rb_str_catf(str, "(% "PRIsVALUE")", defined_class);
        }
    }
    rb_str_buf_cat2(str, sharp);
    rb_str_append(str, rb_id2str(data->me->called_id));
    if (data->me->called_id != data->me->def->original_id) {
        rb_str_catf(str, "(%"PRIsVALUE")",
                    rb_id2str(data->me->def->original_id));
    }
    if (data->me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) {
        rb_str_buf_cat2(str, " (not-implemented)");
    }

    // parameter information
    {
        VALUE params = rb_method_parameters(method);
        VALUE pair, name, kind;
        const VALUE req = ID2SYM(rb_intern("req"));
        const VALUE opt = ID2SYM(rb_intern("opt"));
        const VALUE keyreq = ID2SYM(rb_intern("keyreq"));
        const VALUE key = ID2SYM(rb_intern("key"));
        const VALUE rest = ID2SYM(rb_intern("rest"));
        const VALUE keyrest = ID2SYM(rb_intern("keyrest"));
        const VALUE block = ID2SYM(rb_intern("block"));
        const VALUE nokey = ID2SYM(rb_intern("nokey"));
        int forwarding = 0;

        rb_str_buf_cat2(str, "(");

        if (RARRAY_LEN(params) == 3 &&
            RARRAY_AREF(RARRAY_AREF(params, 0), 0) == rest &&
            RARRAY_AREF(RARRAY_AREF(params, 0), 1) == ID2SYM('*') &&
            RARRAY_AREF(RARRAY_AREF(params, 1), 0) == keyrest &&
            RARRAY_AREF(RARRAY_AREF(params, 1), 1) == ID2SYM(idPow) &&
            RARRAY_AREF(RARRAY_AREF(params, 2), 0) == block &&
            RARRAY_AREF(RARRAY_AREF(params, 2), 1) == ID2SYM('&')) {
            forwarding = 1;
        }

        for (int i = 0; i < RARRAY_LEN(params); i++) {
            pair = RARRAY_AREF(params, i);
            kind = RARRAY_AREF(pair, 0);
            name = RARRAY_AREF(pair, 1);
            // FIXME: in tests it turns out that kind, name = [:req] produces name to be false. Why?..
            if (NIL_P(name) || name == Qfalse) {
                // FIXME: can it be reduced to switch/case?
                if (kind == req || kind == opt) {
                    name = rb_str_new2("_");
                }
                else if (kind == rest || kind == keyrest) {
                    name = rb_str_new2("");
                }
                else if (kind == block) {
                    name = rb_str_new2("block");
                }
                else if (kind == nokey) {
                    name = rb_str_new2("nil");
                }
            }

            if (kind == req) {
                rb_str_catf(str, "%"PRIsVALUE, name);
            }
            else if (kind == opt) {
                rb_str_catf(str, "%"PRIsVALUE"=...", name);
            }
            else if (kind == keyreq) {
                rb_str_catf(str, "%"PRIsVALUE":", name);
            }
            else if (kind == key) {
                rb_str_catf(str, "%"PRIsVALUE": ...", name);
            }
            else if (kind == rest) {
                if (name == ID2SYM('*')) {
                    rb_str_cat_cstr(str, forwarding ? "..." : "*");
                }
                else {
                    rb_str_catf(str, "*%"PRIsVALUE, name);
                }
            }
            else if (kind == keyrest) {
                if (name != ID2SYM(idPow)) {
                    rb_str_catf(str, "**%"PRIsVALUE, name);
                }
                else if (i > 0) {
                    rb_str_set_len(str, RSTRING_LEN(str) - 2);
                }
                else {
                    rb_str_cat_cstr(str, "**");
                }
            }
            else if (kind == block) {
                if (name == ID2SYM('&')) {
                    if (forwarding) {
                        rb_str_set_len(str, RSTRING_LEN(str) - 2);
                    }
                    else {
                        rb_str_cat_cstr(str, "...");
                    }
                }
                else {
                    rb_str_catf(str, "&%"PRIsVALUE, name);
                }
            }
            else if (kind == nokey) {
                rb_str_buf_cat2(str, "**nil");
            }

            if (i < RARRAY_LEN(params) - 1) {
                rb_str_buf_cat2(str, ", ");
            }
        }
        rb_str_buf_cat2(str, ")");
    }

    { // source location
        VALUE loc = rb_method_location(method);
        if (!NIL_P(loc)) {
            rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE,
                        RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
        }
    }

    rb_str_buf_cat2(str, ">");

    return str;
}

#nameObject

Returns the name of the method.



1932
1933
1934
1935
1936
1937
1938
1939
# File 'proc.c', line 1932

static VALUE
method_name(VALUE obj)
{
    struct METHOD *data;

    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    return ID2SYM(data->me->called_id);
}

#original_nameObject

Returns the original name of the method.

class C
  def foo; end
  alias bar foo
end
C.instance_method(:bar).original_name # => :foo


1954
1955
1956
1957
1958
1959
1960
1961
# File 'proc.c', line 1954

static VALUE
method_original_name(VALUE obj)
{
    struct METHOD *data;

    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    return ID2SYM(data->me->def->original_id);
}

#ownerObject

Returns the class or module on which this method is defined. In other words,

meth.owner.instance_methods(false).include?(meth.name) # => true

holds as long as the method is not removed/undefined/replaced, (with private_instance_methods instead of instance_methods if the method is private).

See also Method#receiver.

(1..3).method(:map).owner #=> Enumerable


1981
1982
1983
1984
1985
1986
1987
# File 'proc.c', line 1981

static VALUE
method_owner(VALUE obj)
{
    struct METHOD *data;
    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    return data->owner;
}

#parametersArray

Returns the parameter information of this method.

def foo(bar); end
method(:foo).parameters #=> [[:req, :bar]]

def foo(bar, baz, bat, &blk); end
method(:foo).parameters #=> [[:req, :bar], [:req, :baz], [:req, :bat], [:block, :blk]]

def foo(bar, *args); end
method(:foo).parameters #=> [[:req, :bar], [:rest, :args]]

def foo(bar, baz, *args, &blk); end
method(:foo).parameters #=> [[:req, :bar], [:req, :baz], [:rest, :args], [:block, :blk]]

Returns:



3096
3097
3098
3099
3100
# File 'proc.c', line 3096

static VALUE
rb_method_parameters(VALUE method)
{
    return method_def_parameters(rb_method_def(method));
}

#source_locationArray, Integer

Returns the Ruby source filename and line number containing this method or nil if this method was not defined in Ruby (i.e. native).

Returns ].

Returns:



3008
3009
3010
3011
3012
# File 'proc.c', line 3008

VALUE
rb_method_location(VALUE method)
{
    return method_def_location(rb_method_def(method));
}

#super_methodObject

Returns a Method of superclass which would be called when super is used or nil if there is no method on superclass.



3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
# File 'proc.c', line 3384

static VALUE
method_super_method(VALUE method)
{
    const struct METHOD *data;
    VALUE super_class, iclass;
    ID mid;
    const rb_method_entry_t *me;

    TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
    iclass = data->iclass;
    if (!iclass) return Qnil;
    if (data->me->def->type == VM_METHOD_TYPE_ALIAS && data->me->defined_class) {
        super_class = RCLASS_SUPER(rb_find_defined_class_by_owner(data->me->defined_class,
            data->me->def->body.alias.original_me->owner));
        mid = data->me->def->body.alias.original_me->def->original_id;
    }
    else {
        super_class = RCLASS_SUPER(RCLASS_ORIGIN(iclass));
        mid = data->me->def->original_id;
    }
    if (!super_class) return Qnil;
    me = (rb_method_entry_t *)rb_callable_method_entry_with_refinements(super_class, mid, &iclass);
    if (!me) return Qnil;
    return mnew_internal(me, me->owner, iclass, data->recv, mid, rb_obj_class(method), FALSE, FALSE);
}

#to_sString #inspectString

Returns a human-readable description of the underlying method.

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
(1..3).method(:map).inspect    #=> "#<Method: Range(Enumerable)#map()>"

In the latter case, the method description includes the “owner” of the original method (Enumerable module, which is included into Range).

inspect also provides, when possible, method argument names (call sequence) and source location.

require 'net/http'
Net::HTTP.method(:get).inspect
#=> "#<Method: Net::HTTP.get(uri_or_host, path=..., port=...) <skip>/lib/ruby/2.7.0/net/http.rb:457>"

... in argument definition means argument is optional (has some default value).

For methods defined in C (language core and extensions), location and argument names can’t be extracted, and only generic information is provided in form of * (any number of arguments) or _ (some positional argument).

"cat".method(:count).inspect   #=> "#<Method: String#count(*)>"
"cat".method(:+).inspect       #=> "#<Method: String#+(_)>""

Overloads:



3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
# File 'proc.c', line 3135

static VALUE
method_inspect(VALUE method)
{
    struct METHOD *data;
    VALUE str;
    const char *sharp = "#";
    VALUE mklass;
    VALUE defined_class;

    TypedData_Get_Struct(method, struct METHOD, &method_data_type, data);
    str = rb_sprintf("#<% "PRIsVALUE": ", rb_obj_class(method));

    mklass = data->iclass;
    if (!mklass) mklass = data->klass;

    if (RB_TYPE_P(mklass, T_ICLASS)) {
        /* TODO: I'm not sure why mklass is T_ICLASS.
         * UnboundMethod#bind() can set it as T_ICLASS at convert_umethod_to_method_components()
         * but not sure it is needed.
         */
        mklass = RBASIC_CLASS(mklass);
    }

    if (data->me->def->type == VM_METHOD_TYPE_ALIAS) {
        defined_class = data->me->def->body.alias.original_me->owner;
    }
    else {
        defined_class = method_entry_defined_class(data->me);
    }

    if (RB_TYPE_P(defined_class, T_ICLASS)) {
        defined_class = RBASIC_CLASS(defined_class);
    }

    if (data->recv == Qundef) {
        // UnboundMethod
        rb_str_buf_append(str, rb_inspect(defined_class));
    }
    else if (FL_TEST(mklass, FL_SINGLETON)) {
        VALUE v = RCLASS_ATTACHED_OBJECT(mklass);

        if (UNDEF_P(data->recv)) {
            rb_str_buf_append(str, rb_inspect(mklass));
        }
        else if (data->recv == v) {
            rb_str_buf_append(str, rb_inspect(v));
            sharp = ".";
        }
        else {
            rb_str_buf_append(str, rb_inspect(data->recv));
            rb_str_buf_cat2(str, "(");
            rb_str_buf_append(str, rb_inspect(v));
            rb_str_buf_cat2(str, ")");
            sharp = ".";
        }
    }
    else {
        mklass = data->klass;
        if (FL_TEST(mklass, FL_SINGLETON)) {
            VALUE v = RCLASS_ATTACHED_OBJECT(mklass);
            if (!(RB_TYPE_P(v, T_CLASS) || RB_TYPE_P(v, T_MODULE))) {
                do {
                   mklass = RCLASS_SUPER(mklass);
                } while (RB_TYPE_P(mklass, T_ICLASS));
            }
        }
        rb_str_buf_append(str, rb_inspect(mklass));
        if (defined_class != mklass) {
            rb_str_catf(str, "(% "PRIsVALUE")", defined_class);
        }
    }
    rb_str_buf_cat2(str, sharp);
    rb_str_append(str, rb_id2str(data->me->called_id));
    if (data->me->called_id != data->me->def->original_id) {
        rb_str_catf(str, "(%"PRIsVALUE")",
                    rb_id2str(data->me->def->original_id));
    }
    if (data->me->def->type == VM_METHOD_TYPE_NOTIMPLEMENTED) {
        rb_str_buf_cat2(str, " (not-implemented)");
    }

    // parameter information
    {
        VALUE params = rb_method_parameters(method);
        VALUE pair, name, kind;
        const VALUE req = ID2SYM(rb_intern("req"));
        const VALUE opt = ID2SYM(rb_intern("opt"));
        const VALUE keyreq = ID2SYM(rb_intern("keyreq"));
        const VALUE key = ID2SYM(rb_intern("key"));
        const VALUE rest = ID2SYM(rb_intern("rest"));
        const VALUE keyrest = ID2SYM(rb_intern("keyrest"));
        const VALUE block = ID2SYM(rb_intern("block"));
        const VALUE nokey = ID2SYM(rb_intern("nokey"));
        int forwarding = 0;

        rb_str_buf_cat2(str, "(");

        if (RARRAY_LEN(params) == 3 &&
            RARRAY_AREF(RARRAY_AREF(params, 0), 0) == rest &&
            RARRAY_AREF(RARRAY_AREF(params, 0), 1) == ID2SYM('*') &&
            RARRAY_AREF(RARRAY_AREF(params, 1), 0) == keyrest &&
            RARRAY_AREF(RARRAY_AREF(params, 1), 1) == ID2SYM(idPow) &&
            RARRAY_AREF(RARRAY_AREF(params, 2), 0) == block &&
            RARRAY_AREF(RARRAY_AREF(params, 2), 1) == ID2SYM('&')) {
            forwarding = 1;
        }

        for (int i = 0; i < RARRAY_LEN(params); i++) {
            pair = RARRAY_AREF(params, i);
            kind = RARRAY_AREF(pair, 0);
            name = RARRAY_AREF(pair, 1);
            // FIXME: in tests it turns out that kind, name = [:req] produces name to be false. Why?..
            if (NIL_P(name) || name == Qfalse) {
                // FIXME: can it be reduced to switch/case?
                if (kind == req || kind == opt) {
                    name = rb_str_new2("_");
                }
                else if (kind == rest || kind == keyrest) {
                    name = rb_str_new2("");
                }
                else if (kind == block) {
                    name = rb_str_new2("block");
                }
                else if (kind == nokey) {
                    name = rb_str_new2("nil");
                }
            }

            if (kind == req) {
                rb_str_catf(str, "%"PRIsVALUE, name);
            }
            else if (kind == opt) {
                rb_str_catf(str, "%"PRIsVALUE"=...", name);
            }
            else if (kind == keyreq) {
                rb_str_catf(str, "%"PRIsVALUE":", name);
            }
            else if (kind == key) {
                rb_str_catf(str, "%"PRIsVALUE": ...", name);
            }
            else if (kind == rest) {
                if (name == ID2SYM('*')) {
                    rb_str_cat_cstr(str, forwarding ? "..." : "*");
                }
                else {
                    rb_str_catf(str, "*%"PRIsVALUE, name);
                }
            }
            else if (kind == keyrest) {
                if (name != ID2SYM(idPow)) {
                    rb_str_catf(str, "**%"PRIsVALUE, name);
                }
                else if (i > 0) {
                    rb_str_set_len(str, RSTRING_LEN(str) - 2);
                }
                else {
                    rb_str_cat_cstr(str, "**");
                }
            }
            else if (kind == block) {
                if (name == ID2SYM('&')) {
                    if (forwarding) {
                        rb_str_set_len(str, RSTRING_LEN(str) - 2);
                    }
                    else {
                        rb_str_cat_cstr(str, "...");
                    }
                }
                else {
                    rb_str_catf(str, "&%"PRIsVALUE, name);
                }
            }
            else if (kind == nokey) {
                rb_str_buf_cat2(str, "**nil");
            }

            if (i < RARRAY_LEN(params) - 1) {
                rb_str_buf_cat2(str, ", ");
            }
        }
        rb_str_buf_cat2(str, ")");
    }

    { // source location
        VALUE loc = rb_method_location(method);
        if (!NIL_P(loc)) {
            rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE,
                        RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
        }
    }

    rb_str_buf_cat2(str, ">");

    return str;
}