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

#eql?(other_meth) ⇒ Boolean #==(other_meth) ⇒ Boolean

Two method objects are equal if they are bound to the same object and refer to the same method definition and the classes defining the methods are the same class or module.

Overloads:

  • #eql?(other_meth) ⇒ Boolean

    Returns:

    • (Boolean)
  • #==(other_meth) ⇒ Boolean

    Returns:

    • (Boolean)


1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
# File 'proc.c', line 1757

static VALUE
method_eq(VALUE method, VALUE other)
{
    struct METHOD *m1, *m2;
    VALUE klass1, klass2;

    if (!rb_obj_is_method(other))
	return Qfalse;
    if (CLASS_OF(method) != CLASS_OF(other))
	return Qfalse;

    Check_TypedStruct(method, &method_data_type);
    m1 = (struct METHOD *)DATA_PTR(method);
    m2 = (struct METHOD *)DATA_PTR(other);

    klass1 = method_entry_defined_class(m1->me);
    klass2 = method_entry_defined_class(m2->me);

    if (!rb_method_entry_eq(m1->me, m2->me) ||
	klass1 != klass2 ||
	m1->klass != m2->klass ||
	m1->recv != m2->recv) {
	return Qfalse;
    }

    return Qtrue;
}

#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:



2733
2734
2735
2736
2737
2738
# File 'proc.c', line 2733

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


2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
# File 'proc.c', line 2576

static VALUE
umethod_bind(VALUE method, VALUE recv)
{
    VALUE methclass, klass, iclass;
    const rb_method_entry_t *me;
    convert_umethod_to_method_components(method, recv, &methclass, &klass, &iclass, &me);

    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->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:



2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
# File 'proc.c', line 2601

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 methclass, klass, iclass;
    const rb_method_entry_t *me;
    convert_umethod_to_method_components(method, recv, &methclass, &klass, &iclass, &me);
    struct METHOD bound = { recv, klass, 0, me };

    VALUE passed_procval = rb_block_given_p() ? rb_block_proc() : Qnil;

    rb_execution_context_t *ec = GET_EC();
    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"


2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
# File 'proc.c', line 2324

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);
    CLONESETUP(clone, self);
    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->me, rb_method_entry_clone(orig->me));
    return clone;
}

#eql?(other_meth) ⇒ Boolean #==(other_meth) ⇒ Boolean

Two method objects are equal if they are bound to the same object and refer to the same method definition and the classes defining the methods are the same class or module.

Overloads:

  • #eql?(other_meth) ⇒ Boolean

    Returns:

    • (Boolean)
  • #==(other_meth) ⇒ Boolean

    Returns:

    • (Boolean)


1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
# File 'proc.c', line 1757

static VALUE
method_eq(VALUE method, VALUE other)
{
    struct METHOD *m1, *m2;
    VALUE klass1, klass2;

    if (!rb_obj_is_method(other))
	return Qfalse;
    if (CLASS_OF(method) != CLASS_OF(other))
	return Qfalse;

    Check_TypedStruct(method, &method_data_type);
    m1 = (struct METHOD *)DATA_PTR(method);
    m2 = (struct METHOD *)DATA_PTR(other);

    klass1 = method_entry_defined_class(m1->me);
    klass2 = method_entry_defined_class(m2->me);

    if (!rb_method_entry_eq(m1->me, m2->me) ||
	klass1 != klass2 ||
	m1->klass != m2->klass ||
	m1->recv != m2->recv) {
	return Qfalse;
    }

    return Qtrue;
}

#hashInteger

Returns a hash value corresponding to the method object.

See also Object#hash.

Returns:



1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
# File 'proc.c', line 1794

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:



2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
# File 'proc.c', line 2951

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 (FL_TEST(mklass, FL_SINGLETON)) {
	VALUE v = rb_ivar_get(mklass, attached);

	if (data->recv == Qundef) {
	    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 = rb_ivar_get(mklass, attached);
            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, "(");

        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('*')) {
                    forwarding = 1;
                    rb_str_cat_cstr(str, "...");
                }
                else {
                    rb_str_catf(str, "*%"PRIsVALUE, name);
                }
            }
            else if (kind == keyrest) {
                rb_str_catf(str, "**%"PRIsVALUE, name);
            }
            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.



1859
1860
1861
1862
1863
1864
1865
1866
# File 'proc.c', line 1859

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


1881
1882
1883
1884
1885
1886
1887
1888
# File 'proc.c', line 1881

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 that defines the method. See also Method#receiver.

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


1900
1901
1902
1903
1904
1905
1906
# File 'proc.c', line 1900

static VALUE
method_owner(VALUE obj)
{
    struct METHOD *data;
    TypedData_Get_Struct(obj, struct METHOD, &method_data_type, data);
    return data->me->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:



2908
2909
2910
2911
2912
2913
2914
2915
2916
# File 'proc.c', line 2908

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);
}

#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:



2883
2884
2885
2886
2887
# File 'proc.c', line 2883

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.



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
# File 'proc.c', line 3191

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:



2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
# File 'proc.c', line 2951

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 (FL_TEST(mklass, FL_SINGLETON)) {
	VALUE v = rb_ivar_get(mklass, attached);

	if (data->recv == Qundef) {
	    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 = rb_ivar_get(mklass, attached);
            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, "(");

        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('*')) {
                    forwarding = 1;
                    rb_str_cat_cstr(str, "...");
                }
                else {
                    rb_str_catf(str, "*%"PRIsVALUE, name);
                }
            }
            else if (kind == keyrest) {
                rb_str_catf(str, "**%"PRIsVALUE, name);
            }
            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;
}