ruby-libjit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/method_data.c ADDED
@@ -0,0 +1,385 @@
1
+ #include "method_data.h"
2
+
3
+ #include <ruby.h>
4
+
5
+ #ifndef HAVE_RUBINIUS
6
+
7
+
8
+ #if defined(HAVE_NODE_H)
9
+ /* pre-YARV */
10
+ #include <node.h>
11
+ #elif defined(REALLY_HAVE_RUBY_NODE_H)
12
+ /* YARV */
13
+ #include <ruby/node.h>
14
+ #elif defined(RUBY_VM)
15
+ /* YARV without node.h */
16
+ #include "minimal_node.h"
17
+ #else
18
+ /* something else */
19
+ #error "Need node.h"
20
+ #endif
21
+
22
+ #ifdef HAVE_ENV_H
23
+ /* pre-YARV */
24
+ #include <env.h>
25
+ #endif
26
+
27
+ #ifdef RUBY_VM
28
+
29
+ /* YARV */
30
+
31
+ struct rb_thread_struct
32
+ {
33
+ VALUE self;
34
+ void *vm;
35
+ VALUE *stack;
36
+ unsigned long stack_size;
37
+ VALUE *cfp;
38
+ /* ... */
39
+ };
40
+
41
+ typedef struct rb_thread_struct rb_thread_t;
42
+
43
+ #define CFP_DATA_MEMO_NODE_AND_PC cfp[0]
44
+ #define CFP_METHOD_CLASS cfp[11]
45
+
46
+ /* On YARV, we store the method data on the stack. We don't have to pop
47
+ * it off the stack, because the stack pointer will be reset to the
48
+ * previous frame's stack pointer when the function returns.
49
+ */
50
+ static void fix_frame()
51
+ {
52
+ do {
53
+ extern rb_thread_t * ruby_current_thread;
54
+ VALUE * cfp = ruby_current_thread->cfp;
55
+ CFP_DATA_MEMO_NODE_AND_PC = RBASIC(CFP_METHOD_CLASS)->klass;
56
+
57
+ if(rb_type(CFP_DATA_MEMO_NODE_AND_PC) != T_NODE)
58
+ {
59
+ /* This can happen for module functions that are created after
60
+ * the stub function */
61
+ rb_raise(
62
+ rb_eRuntimeError,
63
+ "Cannot find method data for module function");
64
+ }
65
+ else
66
+ {
67
+ CFP_METHOD_CLASS = RCLASS_SUPER(CFP_METHOD_CLASS);
68
+ }
69
+ } while(0);
70
+ }
71
+
72
+ #define FIX_FRAME() \
73
+ fix_frame()
74
+
75
+ static NODE * data_memo_node()
76
+ {
77
+ extern rb_thread_t * ruby_current_thread;
78
+ VALUE * cfp = ruby_current_thread->cfp;
79
+ return (NODE *)CFP_DATA_MEMO_NODE_AND_PC;
80
+ }
81
+
82
+ #else
83
+
84
+ /* pre-YARV */
85
+
86
+ /* Okay to not pop this temporary frame, since it will be popped by the
87
+ * caller
88
+ */
89
+ #define FIX_FRAME() \
90
+ struct FRAME _frame = *ruby_frame; \
91
+ _frame.last_class = RCLASS(ruby_frame->last_class)->super; \
92
+ _frame.prev = ruby_frame; \
93
+ ruby_frame = &_frame; \
94
+
95
+ static NODE * data_memo_node()
96
+ {
97
+ return (NODE *)(RBASIC(ruby_frame->prev->last_class)->klass);
98
+ }
99
+
100
+ #endif
101
+
102
+ typedef VALUE (*Method_Func)(ANYARGS);
103
+
104
+ static Method_Func actual_cfunc()
105
+ {
106
+ return data_memo_node()->nd_cfnc;
107
+ }
108
+
109
+ static VALUE data_wrapper_m1(int argc, VALUE * argv, VALUE self)
110
+ {
111
+ VALUE result;
112
+ FIX_FRAME();
113
+ result = (*actual_cfunc())(argc, argv, self);
114
+ return result;
115
+ }
116
+
117
+ static VALUE data_wrapper_0(VALUE self)
118
+ {
119
+ VALUE result;
120
+ FIX_FRAME();
121
+ result = (*actual_cfunc())(self);
122
+ return result;
123
+ }
124
+
125
+ static VALUE data_wrapper_1(VALUE self, VALUE arg1)
126
+ {
127
+ VALUE result;
128
+ FIX_FRAME();
129
+ result = (*actual_cfunc())(self, arg1);
130
+ return result;
131
+ }
132
+ static VALUE data_wrapper_2(VALUE self, VALUE arg1, VALUE arg2)
133
+ {
134
+ VALUE result;
135
+ FIX_FRAME();
136
+ result = (*actual_cfunc())(self, arg1, arg2);
137
+ return result;
138
+ }
139
+ static VALUE data_wrapper_3(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3)
140
+ {
141
+ VALUE result;
142
+ FIX_FRAME();
143
+ result = (*actual_cfunc())(self, arg1, arg2, arg3);
144
+ return result;
145
+ }
146
+ static VALUE data_wrapper_4(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4)
147
+ {
148
+ VALUE result;
149
+ FIX_FRAME();
150
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4);
151
+ return result;
152
+ }
153
+ static VALUE data_wrapper_5(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5)
154
+ {
155
+ VALUE result;
156
+ FIX_FRAME();
157
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5);
158
+ return result;
159
+ }
160
+ static VALUE data_wrapper_6(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5, VALUE arg6)
161
+ {
162
+ VALUE result;
163
+ FIX_FRAME();
164
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5, arg6);
165
+ return result;
166
+ }
167
+ static VALUE data_wrapper_7(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5, VALUE arg6, VALUE arg7)
168
+ {
169
+ VALUE result;
170
+ FIX_FRAME();
171
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
172
+ return result;
173
+ }
174
+ static VALUE data_wrapper_8(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5, VALUE arg6, VALUE arg7, VALUE arg8)
175
+ {
176
+ VALUE result;
177
+ FIX_FRAME();
178
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
179
+ return result;
180
+ }
181
+ static VALUE data_wrapper_9(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5, VALUE arg6, VALUE arg7, VALUE arg8, VALUE arg9)
182
+ {
183
+ VALUE result;
184
+ FIX_FRAME();
185
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
186
+ return result;
187
+ }
188
+ static VALUE data_wrapper_10(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5, VALUE arg6, VALUE arg7, VALUE arg8, VALUE arg9, VALUE arg10)
189
+ {
190
+ VALUE result;
191
+ FIX_FRAME();
192
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
193
+ return result;
194
+ }
195
+ static VALUE data_wrapper_11(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5, VALUE arg6, VALUE arg7, VALUE arg8, VALUE arg9, VALUE arg10, VALUE arg11)
196
+ {
197
+ VALUE result;
198
+ FIX_FRAME();
199
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
200
+ return result;
201
+ }
202
+ static VALUE data_wrapper_12(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5, VALUE arg6, VALUE arg7, VALUE arg8, VALUE arg9, VALUE arg10, VALUE arg11, VALUE arg12)
203
+ {
204
+ VALUE result;
205
+ FIX_FRAME();
206
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
207
+ return result;
208
+ }
209
+ static VALUE data_wrapper_13(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5, VALUE arg6, VALUE arg7, VALUE arg8, VALUE arg9, VALUE arg10, VALUE arg11, VALUE arg12, VALUE arg13)
210
+ {
211
+ VALUE result;
212
+ FIX_FRAME();
213
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
214
+ return result;
215
+ }
216
+ static VALUE data_wrapper_14(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5, VALUE arg6, VALUE arg7, VALUE arg8, VALUE arg9, VALUE arg10, VALUE arg11, VALUE arg12, VALUE arg13, VALUE arg14)
217
+ {
218
+ VALUE result;
219
+ FIX_FRAME();
220
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
221
+ return result;
222
+ }
223
+ static VALUE data_wrapper_15(VALUE self, VALUE arg1, VALUE arg2, VALUE arg3, VALUE arg4, VALUE arg5, VALUE arg6, VALUE arg7, VALUE arg8, VALUE arg9, VALUE arg10, VALUE arg11, VALUE arg12, VALUE arg13, VALUE arg14, VALUE arg15)
224
+ {
225
+ VALUE result;
226
+ FIX_FRAME();
227
+ result = (*actual_cfunc())(self, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
228
+ return result;
229
+ }
230
+
231
+ /* Define a method and attach data to it.
232
+ *
233
+ * The method looks to ruby like a normal aliased CFUNC, with a modified
234
+ * origin class:
235
+ *
236
+ * NODE_FBODY
237
+ * |- (u1) orig - origin class
238
+ * | |- basic
239
+ * | | |- flags - origin class flags + FL_SINGLETON
240
+ * | | +- klass - NODE_MEMO
241
+ * | | |- (u1) cfnc - actual C function to call
242
+ * | | |- (u2) rval - stored data
243
+ * | | +- (u3) 0
244
+ * | |- iv_tbl - 0
245
+ * | |- m_tbl - 0
246
+ * | +- super - actual origin class
247
+ * |- (u2) mid - name of the method
248
+ * +- (u3) head - NODE_CFUNC
249
+ * |- (u1) cfnc - wrapper function to call
250
+ * +- (u2) argc - function arity
251
+ *
252
+ * Or, on YARV:
253
+ *
254
+ * NODE_FBODY
255
+ * |- (u1) oid - name of the method
256
+ * +- (u2) body - NODE_METHOD
257
+ * |- (u1) clss - origin class
258
+ * | |- basic
259
+ * | | |- flags - origin class flags + FL_SINGLETON
260
+ * | | +- klass - NODE_MEMO
261
+ * | | |- (u1) cfnc - actual C function to call
262
+ * | | |- (u2) rval - stored data
263
+ * | | +- (u3) 0
264
+ * | |- ptr - rb_classext_t
265
+ * | | |- super - actual origin class
266
+ * | | +- iv_tbl - 0
267
+ * | |- m_tbl - 0
268
+ * | +- iv_index_tbl - 0?
269
+ * |- (u2) body - NODE_CFUNC
270
+ * | |- (u1) cfnc - wrapper function to call
271
+ * | |- (u2) argc - function arity
272
+ * +- (u3) noex - NOEX_PUBLIC
273
+ *
274
+ * When the wrapper function is called, last_class is set to the origin
275
+ * class found in the FBODY node. So that the method data will be
276
+ * accessible, and so last_class will point to klass and not to our MEMO
277
+ * node, it is necessary to "fix" the current frame.
278
+ *
279
+ * Pre-YARV, this means we duplicate the current frame and set last_class:
280
+ *
281
+ * ruby_frame
282
+ * |- last_class - klass
283
+ * |- prev
284
+ * | |- last_class - NODE_MEMO
285
+ * | | |- (u1) cfnc - actual C function to call
286
+ * | | |- (u2) rval - stored data
287
+ * | | +- (u3) 0
288
+ * | |- prev - the real previous frame
289
+ * | +- ...
290
+ * +- ...
291
+ *
292
+ * The method data is then accessible via
293
+ * ruby_frame->prev->last_class->rval.
294
+ *
295
+ * On YARV, the current frame is not duplicated; rather, the method data
296
+ * is placed on the stack and is referenced by one of the unused members
297
+ * of the control frame (the program counter):
298
+ *
299
+ * ruby_current_thread->cfp
300
+ * |- pc - NODE_MEMO
301
+ * | |- (u1) cfnc - actual C function to call
302
+ * | |- (u2) rval - stored data
303
+ * | +- (u3) 0
304
+ * |- method_class - klass
305
+ * +- ...
306
+ *
307
+ */
308
+ void define_method_with_data(
309
+ VALUE klass, ID id, VALUE (*cfunc)(ANYARGS), int arity, VALUE data)
310
+ {
311
+ /* TODO: origin should have #to_s and #inspect methods defined */
312
+ #ifdef HAVE_RB_CLASS_BOOT
313
+ VALUE origin = rb_class_boot(klass);
314
+ #else
315
+ VALUE origin = rb_class_new(klass);
316
+ #endif
317
+ NODE * node;
318
+
319
+ VALUE (*data_wrapper)(ANYARGS);
320
+ switch(arity)
321
+ {
322
+ case 0: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_0); break;
323
+ case 1: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_1); break;
324
+ case 2: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_2); break;
325
+ case 3: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_3); break;
326
+ case 4: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_4); break;
327
+ case 5: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_5); break;
328
+ case 6: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_6); break;
329
+ case 7: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_7); break;
330
+ case 8: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_8); break;
331
+ case 9: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_9); break;
332
+ case 10: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_10); break;
333
+ case 11: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_11); break;
334
+ case 12: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_12); break;
335
+ case 13: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_13); break;
336
+ case 14: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_14); break;
337
+ case 15: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_15); break;
338
+ case -1: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_m1); break;
339
+ default: rb_raise(rb_eArgError, "unsupported arity %d", arity);
340
+ }
341
+
342
+ FL_SET(origin, FL_SINGLETON);
343
+ rb_singleton_class_attached(origin, klass);
344
+ rb_name_class(origin, SYM2ID(rb_class_name(klass)));
345
+
346
+ RBASIC(origin)->klass = (VALUE)NEW_NODE(NODE_MEMO, cfunc, data, 0);
347
+
348
+ #ifdef RUBY_VM
349
+ /* YARV */
350
+ node = NEW_FBODY(
351
+ NEW_METHOD(
352
+ NEW_CFUNC(data_wrapper, arity),
353
+ origin,
354
+ NOEX_PUBLIC),
355
+ id);
356
+ st_insert(RCLASS_M_TBL(klass), id, (st_data_t)node);
357
+ #else
358
+ /* pre-YARV */
359
+ node = NEW_FBODY(
360
+ NEW_CFUNC(data_wrapper, arity),
361
+ id,
362
+ origin);
363
+ rb_add_method(klass, id, node, NOEX_PUBLIC);
364
+ #endif
365
+ }
366
+
367
+ VALUE get_method_data()
368
+ {
369
+ return data_memo_node()->nd_rval;
370
+ }
371
+
372
+ #else /* HAVE_RUBINIUS */
373
+
374
+ void define_method_with_data(
375
+ VALUE klass, ID id, VALUE (*cfunc)(ANYARGS), int arity, VALUE data)
376
+ {
377
+ rb_raise(rb_eNotImpError, "Not implemented: define_method_with_data");
378
+ }
379
+
380
+ VALUE get_method_data()
381
+ {
382
+ rb_raise(rb_eNotImpError, "Not implemented: get_method_data");
383
+ }
384
+
385
+ #endif
@@ -0,0 +1,294 @@
1
+ #include "method_data.h"
2
+
3
+ #include <ruby.h>
4
+
5
+ #ifndef HAVE_RUBINIUS
6
+
7
+ #ruby <<END
8
+ MAX_ARGS = 15
9
+ nil
10
+ END
11
+
12
+ #if defined(HAVE_NODE_H)
13
+ /* pre-YARV */
14
+ #include <node.h>
15
+ #elif defined(REALLY_HAVE_RUBY_NODE_H)
16
+ /* YARV */
17
+ #include <ruby/node.h>
18
+ #elif defined(RUBY_VM)
19
+ /* YARV without node.h */
20
+ #include "minimal_node.h"
21
+ #else
22
+ /* something else */
23
+ #error "Need node.h"
24
+ #endif
25
+
26
+ #ifdef HAVE_ENV_H
27
+ /* pre-YARV */
28
+ #include <env.h>
29
+ #endif
30
+
31
+ #ifdef RUBY_VM
32
+
33
+ /* YARV */
34
+
35
+ struct rb_thread_struct
36
+ {
37
+ VALUE self;
38
+ void *vm;
39
+ VALUE *stack;
40
+ unsigned long stack_size;
41
+ VALUE *cfp;
42
+ /* ... */
43
+ };
44
+
45
+ typedef struct rb_thread_struct rb_thread_t;
46
+
47
+ #define CFP_DATA_MEMO_NODE_AND_PC cfp[0]
48
+ #define CFP_METHOD_CLASS cfp[11]
49
+
50
+ /* On YARV, we store the method data on the stack. We don't have to pop
51
+ * it off the stack, because the stack pointer will be reset to the
52
+ * previous frame's stack pointer when the function returns.
53
+ */
54
+ static void fix_frame()
55
+ {
56
+ do {
57
+ extern rb_thread_t * ruby_current_thread;
58
+ VALUE * cfp = ruby_current_thread->cfp;
59
+ CFP_DATA_MEMO_NODE_AND_PC = RBASIC(CFP_METHOD_CLASS)->klass;
60
+
61
+ if(rb_type(CFP_DATA_MEMO_NODE_AND_PC) != T_NODE)
62
+ {
63
+ /* This can happen for module functions that are created after
64
+ * the stub function */
65
+ rb_raise(
66
+ rb_eRuntimeError,
67
+ "Cannot find method data for module function");
68
+ }
69
+ else
70
+ {
71
+ CFP_METHOD_CLASS = RCLASS_SUPER(CFP_METHOD_CLASS);
72
+ }
73
+ } while(0);
74
+ }
75
+
76
+ #define FIX_FRAME() \
77
+ fix_frame()
78
+
79
+ static NODE * data_memo_node()
80
+ {
81
+ extern rb_thread_t * ruby_current_thread;
82
+ VALUE * cfp = ruby_current_thread->cfp;
83
+ return (NODE *)CFP_DATA_MEMO_NODE_AND_PC;
84
+ }
85
+
86
+ #else
87
+
88
+ /* pre-YARV */
89
+
90
+ /* Okay to not pop this temporary frame, since it will be popped by the
91
+ * caller
92
+ */
93
+ #define FIX_FRAME() \
94
+ struct FRAME _frame = *ruby_frame; \
95
+ _frame.last_class = RCLASS(ruby_frame->last_class)->super; \
96
+ _frame.prev = ruby_frame; \
97
+ ruby_frame = &_frame; \
98
+
99
+ static NODE * data_memo_node()
100
+ {
101
+ return (NODE *)(RBASIC(ruby_frame->prev->last_class)->klass);
102
+ }
103
+
104
+ #endif
105
+
106
+ typedef VALUE (*Method_Func)(ANYARGS);
107
+
108
+ static Method_Func actual_cfunc()
109
+ {
110
+ return data_memo_node()->nd_cfnc;
111
+ }
112
+
113
+ static VALUE data_wrapper_m1(int argc, VALUE * argv, VALUE self)
114
+ {
115
+ VALUE result;
116
+ FIX_FRAME();
117
+ result = (*actual_cfunc())(argc, argv, self);
118
+ return result;
119
+ }
120
+
121
+ static VALUE data_wrapper_0(VALUE self)
122
+ {
123
+ VALUE result;
124
+ FIX_FRAME();
125
+ result = (*actual_cfunc())(self);
126
+ return result;
127
+ }
128
+
129
+ #ruby <<END
130
+ (1..MAX_ARGS).each do |i|
131
+ params = (1..i).map { |j| "VALUE arg#{j}" }.join(', ')
132
+ args = (1..i).map { |j| "arg#{j}" }.join(', ')
133
+
134
+ puts <<-END
135
+ static VALUE data_wrapper_#{i}(VALUE self, #{params})
136
+ {
137
+ VALUE result;
138
+ FIX_FRAME();
139
+ result = (*actual_cfunc())(self, #{args});
140
+ return result;
141
+ }
142
+ END
143
+ end
144
+
145
+ nil
146
+ END
147
+
148
+ /* Define a method and attach data to it.
149
+ *
150
+ * The method looks to ruby like a normal aliased CFUNC, with a modified
151
+ * origin class:
152
+ *
153
+ * NODE_FBODY
154
+ * |- (u1) orig - origin class
155
+ * | |- basic
156
+ * | | |- flags - origin class flags + FL_SINGLETON
157
+ * | | +- klass - NODE_MEMO
158
+ * | | |- (u1) cfnc - actual C function to call
159
+ * | | |- (u2) rval - stored data
160
+ * | | +- (u3) 0
161
+ * | |- iv_tbl - 0
162
+ * | |- m_tbl - 0
163
+ * | +- super - actual origin class
164
+ * |- (u2) mid - name of the method
165
+ * +- (u3) head - NODE_CFUNC
166
+ * |- (u1) cfnc - wrapper function to call
167
+ * +- (u2) argc - function arity
168
+ *
169
+ * Or, on YARV:
170
+ *
171
+ * NODE_FBODY
172
+ * |- (u1) oid - name of the method
173
+ * +- (u2) body - NODE_METHOD
174
+ * |- (u1) clss - origin class
175
+ * | |- basic
176
+ * | | |- flags - origin class flags + FL_SINGLETON
177
+ * | | +- klass - NODE_MEMO
178
+ * | | |- (u1) cfnc - actual C function to call
179
+ * | | |- (u2) rval - stored data
180
+ * | | +- (u3) 0
181
+ * | |- ptr - rb_classext_t
182
+ * | | |- super - actual origin class
183
+ * | | +- iv_tbl - 0
184
+ * | |- m_tbl - 0
185
+ * | +- iv_index_tbl - 0?
186
+ * |- (u2) body - NODE_CFUNC
187
+ * | |- (u1) cfnc - wrapper function to call
188
+ * | |- (u2) argc - function arity
189
+ * +- (u3) noex - NOEX_PUBLIC
190
+ *
191
+ * When the wrapper function is called, last_class is set to the origin
192
+ * class found in the FBODY node. So that the method data will be
193
+ * accessible, and so last_class will point to klass and not to our MEMO
194
+ * node, it is necessary to "fix" the current frame.
195
+ *
196
+ * Pre-YARV, this means we duplicate the current frame and set last_class:
197
+ *
198
+ * ruby_frame
199
+ * |- last_class - klass
200
+ * |- prev
201
+ * | |- last_class - NODE_MEMO
202
+ * | | |- (u1) cfnc - actual C function to call
203
+ * | | |- (u2) rval - stored data
204
+ * | | +- (u3) 0
205
+ * | |- prev - the real previous frame
206
+ * | +- ...
207
+ * +- ...
208
+ *
209
+ * The method data is then accessible via
210
+ * ruby_frame->prev->last_class->rval.
211
+ *
212
+ * On YARV, the current frame is not duplicated; rather, the method data
213
+ * is placed on the stack and is referenced by one of the unused members
214
+ * of the control frame (the program counter):
215
+ *
216
+ * ruby_current_thread->cfp
217
+ * |- pc - NODE_MEMO
218
+ * | |- (u1) cfnc - actual C function to call
219
+ * | |- (u2) rval - stored data
220
+ * | +- (u3) 0
221
+ * |- method_class - klass
222
+ * +- ...
223
+ *
224
+ */
225
+ void define_method_with_data(
226
+ VALUE klass, ID id, VALUE (*cfunc)(ANYARGS), int arity, VALUE data)
227
+ {
228
+ /* TODO: origin should have #to_s and #inspect methods defined */
229
+ #ifdef HAVE_RB_CLASS_BOOT
230
+ VALUE origin = rb_class_boot(klass);
231
+ #else
232
+ VALUE origin = rb_class_new(klass);
233
+ #endif
234
+ NODE * node;
235
+
236
+ VALUE (*data_wrapper)(ANYARGS);
237
+ switch(arity)
238
+ {
239
+ #ruby <<END
240
+ (0..MAX_ARGS).each do |i|
241
+ puts <<-END
242
+ case #{i}: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_#{i}); break;
243
+ END
244
+ end
245
+ nil
246
+ END
247
+ case -1: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_m1); break;
248
+ default: rb_raise(rb_eArgError, "unsupported arity %d", arity);
249
+ }
250
+
251
+ FL_SET(origin, FL_SINGLETON);
252
+ rb_singleton_class_attached(origin, klass);
253
+ rb_name_class(origin, SYM2ID(rb_class_name(klass)));
254
+
255
+ RBASIC(origin)->klass = (VALUE)NEW_NODE(NODE_MEMO, cfunc, data, 0);
256
+
257
+ #ifdef RUBY_VM
258
+ /* YARV */
259
+ node = NEW_FBODY(
260
+ NEW_METHOD(
261
+ NEW_CFUNC(data_wrapper, arity),
262
+ origin,
263
+ NOEX_PUBLIC),
264
+ id);
265
+ st_insert(RCLASS_M_TBL(klass), id, (st_data_t)node);
266
+ #else
267
+ /* pre-YARV */
268
+ node = NEW_FBODY(
269
+ NEW_CFUNC(data_wrapper, arity),
270
+ id,
271
+ origin);
272
+ rb_add_method(klass, id, node, NOEX_PUBLIC);
273
+ #endif
274
+ }
275
+
276
+ VALUE get_method_data()
277
+ {
278
+ return data_memo_node()->nd_rval;
279
+ }
280
+
281
+ #else /* HAVE_RUBINIUS */
282
+
283
+ void define_method_with_data(
284
+ VALUE klass, ID id, VALUE (*cfunc)(ANYARGS), int arity, VALUE data)
285
+ {
286
+ rb_raise(rb_eNotImpError, "Not implemented: define_method_with_data");
287
+ }
288
+
289
+ VALUE get_method_data()
290
+ {
291
+ rb_raise(rb_eNotImpError, "Not implemented: get_method_data");
292
+ }
293
+
294
+ #endif
data/ext/method_data.h ADDED
@@ -0,0 +1,11 @@
1
+ #ifndef METHOD_DATA_H
2
+ #define METHOD_DATA_H
3
+
4
+ #include <ruby.h>
5
+
6
+ void define_method_with_data(
7
+ VALUE klass, ID id, VALUE (*cfunc)(ANYARGS), int arity, VALUE data);
8
+
9
+ VALUE get_method_data();
10
+
11
+ #endif // METHOD_DATA_H
@@ -0,0 +1,35 @@
1
+ #ifdef NEED_MINIMAL_NODE
2
+
3
+ #include "minimal_node.h"
4
+ #include <string.h>
5
+
6
+ int NODE_MEMO = 0;
7
+ int NODE_METHOD = 0;
8
+ int NODE_FBODY = 0;
9
+ int NODE_CFUNC = 0;
10
+
11
+ char const * ruby_node_name(int node);
12
+
13
+ static int node_value(char const * name)
14
+ {
15
+ /* TODO: any way to end the block? */
16
+ int j;
17
+ for(j = 0; ; ++j)
18
+ {
19
+ if(!strcmp(name, ruby_node_name(j)))
20
+ {
21
+ return j;
22
+ }
23
+ }
24
+ }
25
+
26
+ void Init_minimal_node()
27
+ {
28
+ NODE_MEMO = node_value("NODE_MEMO");
29
+ NODE_METHOD = node_value("NODE_METHOD");
30
+ NODE_FBODY = node_value("NODE_FBODY");
31
+ NODE_CFUNC = node_value("NODE_CFUNC");
32
+ }
33
+
34
+ #endif
35
+