/
mri.c
213 lines (183 loc) · 6.55 KB
/
mri.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#include "ruby.h"
#if RUBY_VERSION >= 192
# include "vm_core.h"
# include "method.h"
# include "ruby/st.h"
#elif RUBY_VERSION >= 190
# include "node-1.9.h"
# include "ruby/st.h"
#else
# include "node.h"
# include "st.h"
#endif
#if RUBY_VERSION < 187
# define RCLASS_IV_TBL(c) (RCLASS(c)->iv_tbl)
# define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
# define RCLASS_SUPER(c) (RCLASS(c)->super)
#endif
/*
* Return the internal superclass of this class.
*
* This is either a Class or "IClass." IClasses represent Modules
* included in the ancestry, and should be treated as opaque objects
* in ruby space. Convert the IClass to a Module using #iclass_to_module
* before using it in ruby.
*/
VALUE Looksee_internal_superclass(VALUE self, VALUE internal_class) {
VALUE super = RCLASS_SUPER(internal_class);
if (!super)
return Qnil;
return super;
}
/*
* Return the internal class of the given object.
*
* This is either the object's singleton class, if it exists, or the
* object's birth class.
*/
VALUE Looksee_internal_class(VALUE self, VALUE object) {
return CLASS_OF(object);
}
/*
* Return the class or module that the given internal class
* represents.
*
* If a class is given, this is the class. If an iclass is given,
* this is the module it represents in the lookup chain.
*/
VALUE Looksee_internal_class_to_module(VALUE self, VALUE internal_class) {
if (!SPECIAL_CONST_P(internal_class)) {
switch (BUILTIN_TYPE(internal_class)) {
case T_ICLASS:
return RBASIC(internal_class)->klass;
case T_CLASS:
return internal_class;
}
}
rb_raise(rb_eArgError, "not an internal class: %s", RSTRING_PTR(rb_inspect(internal_class)));
}
#if RUBY_VERSION >= 192
# define VISIBILITY_TYPE rb_method_flag_t
typedef struct add_method_if_matching_arg {
VALUE names;
VISIBILITY_TYPE visibility;
} add_method_if_matching_arg_t;
static int add_method_if_matching(ID method_name, rb_method_entry_t *me, add_method_if_matching_arg_t *arg) {
if (method_name == ID_ALLOCATOR)
return ST_CONTINUE;
if (UNDEFINED_METHOD_ENTRY_P(me))
return ST_CONTINUE;
if ((me->flag & NOEX_MASK) == arg->visibility)
rb_ary_push(arg->names, ID2SYM(method_name));
return ST_CONTINUE;
}
static int add_method_if_undefined(ID method_name, rb_method_entry_t *me, VALUE *names) {
if (UNDEFINED_METHOD_ENTRY_P(me))
rb_ary_push(*names, ID2SYM(method_name));
return ST_CONTINUE;
}
#else
# if RUBY_VERSION >= 190
# define VISIBILITY(node) ((node)->nd_body->nd_noex & NOEX_MASK)
# else
# define VISIBILITY(node) ((node)->nd_noex & NOEX_MASK)
# endif
# define VISIBILITY_TYPE unsigned long
typedef struct add_method_if_matching_arg {
VALUE names;
VISIBILITY_TYPE visibility;
} add_method_if_matching_arg_t;
static int add_method_if_matching(ID method_name, NODE *body, add_method_if_matching_arg_t *arg) {
/* This entry is for the internal allocator function. */
if (method_name == ID_ALLOCATOR)
return ST_CONTINUE;
/* Module#undef_method:
* * sets body->nd_body to NULL in ruby <= 1.8
* * sets body to NULL in ruby >= 1.9
*/
if (!body || !body->nd_body)
return ST_CONTINUE;
if (VISIBILITY(body) == arg->visibility)
rb_ary_push(arg->names, ID2SYM(method_name));
return ST_CONTINUE;
}
static int add_method_if_undefined(ID method_name, NODE *body, VALUE *names) {
if (!body || !body->nd_body)
rb_ary_push(*names, ID2SYM(method_name));
return ST_CONTINUE;
}
#endif
static VALUE internal_instance_methods(VALUE klass, VISIBILITY_TYPE visibility) {
add_method_if_matching_arg_t arg;
arg.names = rb_ary_new();
arg.visibility = visibility;
st_foreach(RCLASS_M_TBL(klass), add_method_if_matching, (st_data_t)&arg);
return arg.names;
}
/*
* Return the list of public instance methods (as Symbols) of the
* given internal class.
*/
VALUE Looksee_internal_public_instance_methods(VALUE self, VALUE klass) {
return internal_instance_methods(klass, NOEX_PUBLIC);
}
/*
* Return the list of protected instance methods (as Symbols) of the
* given internal class.
*/
VALUE Looksee_internal_protected_instance_methods(VALUE self, VALUE klass) {
return internal_instance_methods(klass, NOEX_PROTECTED);
}
/*
* Return the list of private instance methods (as Symbols) of the
* given internal class.
*/
VALUE Looksee_internal_private_instance_methods(VALUE self, VALUE klass) {
return internal_instance_methods(klass, NOEX_PRIVATE);
}
/*
* Return the list of undefined instance methods (as Symbols) of the
* given internal class.
*/
VALUE Looksee_internal_undefined_instance_methods(VALUE self, VALUE klass) {
VALUE names = rb_ary_new();
st_foreach(RCLASS_M_TBL(klass), add_method_if_undefined, (st_data_t)&names);
return names;
}
#if RUBY_VERSION < 190
/* Return the source file and line number of the given object and method. */
VALUE Looksee_source_location(VALUE self, VALUE unbound_method) {
if (!rb_obj_is_kind_of(unbound_method, rb_cUnboundMethod))
rb_raise(rb_eTypeError, "expected UnboundMethod, got %s", rb_obj_classname(unbound_method));
VALUE klass = rb_funcall(unbound_method, rb_intern("owner"), 0);
VALUE name = rb_funcall(unbound_method, rb_intern("name"), 0);
ID method_id = rb_intern(RSTRING_PTR(name));
NODE *body;
if (!st_lookup(RCLASS_M_TBL(klass), method_id, (st_data_t *)&body))
return Qnil;
if (!body || !body->nd_body)
return Qnil;
VALUE file = rb_str_new2(body->nd_file);
VALUE line = INT2NUM(nd_line(body));
VALUE location = rb_ary_new2(2);
rb_ary_store(location, 0, file);
rb_ary_store(location, 1, line);
return location;
}
#endif
void Init_mri(void) {
VALUE mLooksee = rb_const_get(rb_cObject, rb_intern("Looksee"));
VALUE mAdapter = rb_const_get(mLooksee, rb_intern("Adapter"));
VALUE mBase = rb_const_get(mAdapter, rb_intern("Base"));
VALUE mMRI = rb_define_class_under(mAdapter, "MRI", mBase);
rb_define_method(mMRI, "internal_superclass", Looksee_internal_superclass, 1);
rb_define_method(mMRI, "internal_class", Looksee_internal_class, 1);
rb_define_method(mMRI, "internal_class_to_module", Looksee_internal_class_to_module, 1);
rb_define_method(mMRI, "internal_public_instance_methods", Looksee_internal_public_instance_methods, 1);
rb_define_method(mMRI, "internal_protected_instance_methods", Looksee_internal_protected_instance_methods, 1);
rb_define_method(mMRI, "internal_private_instance_methods", Looksee_internal_private_instance_methods, 1);
rb_define_method(mMRI, "internal_undefined_instance_methods", Looksee_internal_undefined_instance_methods, 1);
#if RUBY_VERSION < 190
rb_define_method(mMRI, "source_location", Looksee_source_location, 1);
#endif
}