rallhook 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/AUTHORS +3 -0
  2. data/CHANGELOG +82 -0
  3. data/README +207 -0
  4. data/Rakefile +49 -0
  5. data/TODO +8 -0
  6. data/examples/hook/example1.rb +19 -0
  7. data/examples/hook/example2.rb +30 -0
  8. data/examples/hook/example3.rb +24 -0
  9. data/examples/hook/example4.rb +18 -0
  10. data/examples/hook/intercept.rb +41 -0
  11. data/examples/hook/intercept2.rb +49 -0
  12. data/examples/hook/redirect.rb +55 -0
  13. data/examples/hook/redirect_inherited.rb +28 -0
  14. data/examples/hook/shadow.rb +44 -0
  15. data/examples/instrospection/main.rb +13 -0
  16. data/examples/instrospection/source1.rb +4 -0
  17. data/examples/instrospection/source2.rb +4 -0
  18. data/ext/rallhook_base/distorm.h +401 -0
  19. data/ext/rallhook_base/extconf.rb +21 -0
  20. data/ext/rallhook_base/hook.c +165 -0
  21. data/ext/rallhook_base/hook.h +30 -0
  22. data/ext/rallhook_base/hook_rb_call.c +88 -0
  23. data/ext/rallhook_base/hook_rb_call.h +33 -0
  24. data/ext/rallhook_base/method_node.c +212 -0
  25. data/ext/rallhook_base/method_node.h +27 -0
  26. data/ext/rallhook_base/node_defs.h +294 -0
  27. data/ext/rallhook_base/rallhook.c +396 -0
  28. data/ext/rallhook_base/rb_call_fake.c +398 -0
  29. data/ext/rallhook_base/rb_call_fake.h +138 -0
  30. data/ext/rallhook_base/restrict_def.c +176 -0
  31. data/ext/rallhook_base/restrict_def.h +37 -0
  32. data/ext/rallhook_base/ruby_redirect.c +122 -0
  33. data/ext/rallhook_base/ruby_redirect.h +33 -0
  34. data/ext/rallhook_base/ruby_symbols.c +43 -0
  35. data/ext/rallhook_base/ruby_symbols.h +28 -0
  36. data/ext/rallhook_base/ruby_version.h +21 -0
  37. data/lib/rallhook/thread_hook.rb +37 -0
  38. data/lib/rallhook.rb +384 -0
  39. data/test/basic_proc.rb +45 -0
  40. data/test/integrity/test_array.rb +42 -0
  41. data/test/integrity/test_binding.rb +26 -0
  42. data/test/integrity/test_block.rb +37 -0
  43. data/test/integrity/test_call.rb +1 -0
  44. data/test/integrity/test_class_methods.rb +1 -0
  45. data/test/integrity/test_exception.rb +1 -0
  46. data/test/integrity/test_super.rb +34 -0
  47. data/test/introspection/test_call.rb +29 -0
  48. data/test/introspection/test_class_method.rb +41 -0
  49. data/test/introspection/test_file.rb +15 -0
  50. metadata +113 -0
@@ -0,0 +1,396 @@
1
+ /*
2
+
3
+ This file is part of the rallhook project, http://github.com/tario/rallhook
4
+
5
+ Copyright (c) 2009-2010 Roberto Dario Seminara <robertodarioseminara@gmail.com>
6
+
7
+ rallhook is free software: you can redistribute it and/or modify
8
+ it under the terms of the gnu general public license as published by
9
+ the free software foundation, either version 3 of the license, or
10
+ (at your option) any later version.
11
+
12
+ rallhook is distributed in the hope that it will be useful,
13
+ but without any warranty; without even the implied warranty of
14
+ merchantability or fitness for a particular purpose. see the
15
+ gnu general public license for more details.
16
+
17
+ you should have received a copy of the gnu general public license
18
+ along with rallhook. if not, see <http://www.gnu.org/licenses/>.
19
+
20
+ */
21
+
22
+ #include <ruby.h>
23
+ #include "method_node.h"
24
+ #include "ruby_redirect.h"
25
+ #include "restrict_def.h"
26
+
27
+ VALUE rb_cHook;
28
+ VALUE rb_mRallHook;
29
+ VALUE rb_mMethodRedirect;
30
+ VALUE rb_mMethodReturn;
31
+ ID id_call_;
32
+
33
+ ID id_call;
34
+ ID id_method_wrapper;
35
+ ID id_handle_method;
36
+ ID id_binding;
37
+ ID id_method_added;
38
+ ID id_hook_enabled;
39
+ ID id_hook_enable_left;
40
+ ID id_hook_proc;
41
+
42
+ ID id_return_value_var, id_klass_var, id_recv_var, id_method_var, id_unhook_var;
43
+
44
+ void *rb_get_method_body(VALUE klass, ID id, ID *idp);
45
+
46
+ typedef struct rb_thread_struct
47
+ {
48
+ VALUE self;
49
+ void *vm; // rb_vm_t_
50
+
51
+ /* execution information */
52
+ VALUE *stack; /* must free, must mark */
53
+ unsigned long stack_size;
54
+ void *cfp; // rb_control_frame_t_
55
+ int safe_level;
56
+ int raised_flag;
57
+ VALUE last_status; /* $? */
58
+
59
+ /* passing state */
60
+ int state;
61
+
62
+ /* for rb_iterate */
63
+ void *passed_block; // rb_block_t_
64
+
65
+ // ...
66
+ } rb_thread_t__;
67
+
68
+ typedef struct AttachedThreadInfo_ {
69
+ int hook_enabled;
70
+ int hook_enable_left;
71
+ VALUE hook_proc;
72
+ int handle_method_arity;
73
+ } AttachedThreadInfo;
74
+
75
+ AttachedThreadInfo* tinfo_from_thread(VALUE thread) {
76
+ VALUE tmp = rb_ivar_get( thread, rb_intern("__tinfo") );
77
+
78
+ if (tmp == Qnil) {
79
+ AttachedThreadInfo* tinfo = malloc(sizeof(AttachedThreadInfo));
80
+ tinfo->hook_enabled = 0;
81
+ tinfo->hook_enable_left = 0;
82
+ tinfo->hook_proc = Qnil;
83
+
84
+ rb_ivar_set( thread, rb_intern("__tinfo"), (VALUE)tinfo);
85
+
86
+ return tinfo;
87
+ } else {
88
+ return (AttachedThreadInfo*)tmp;
89
+ }
90
+ }
91
+
92
+ void redirect_left(AttachedThreadInfo* tinfo, int left) {
93
+ tinfo->hook_enable_left = left;
94
+ }
95
+
96
+ void enable_redirect(AttachedThreadInfo* tinfo) {
97
+ tinfo->hook_enabled = 1;
98
+ }
99
+
100
+ void disable_redirect(AttachedThreadInfo* tinfo) {
101
+ tinfo->hook_enabled = 0;
102
+ }
103
+
104
+ int get_hook_enable_left(AttachedThreadInfo* tinfo) {
105
+ return tinfo->hook_enable_left;
106
+ }
107
+
108
+ int get_hook_enabled(AttachedThreadInfo* tinfo) {
109
+ return tinfo->hook_enabled;
110
+ }
111
+
112
+ VALUE get_hook_proc() {
113
+ return tinfo_from_thread( rb_thread_current() )->hook_proc ;
114
+ }
115
+
116
+ /*
117
+ Disable the hook. Is not usually necesary because of the RAII feature of Hook#hook
118
+ */
119
+ VALUE unhook(VALUE self) {
120
+ disable_redirect(tinfo_from_thread( rb_thread_current() ) );
121
+ return Qnil;
122
+ }
123
+
124
+ VALUE ensured_handle_method( VALUE params ) {
125
+ int argc = (int)((VALUE*)params)[0];
126
+ VALUE* argv = ((VALUE*)params)+1;
127
+ return rb_funcall2( get_hook_proc(), id_handle_method, argc, argv);
128
+ }
129
+
130
+ VALUE restore_hook_status(AttachedThreadInfo* tinfo) {
131
+ enable_redirect(tinfo);
132
+ return Qnil;
133
+ }
134
+
135
+ void set_handle_method_arity(int value) {
136
+ tinfo_from_thread(rb_thread_current() )->handle_method_arity = value;
137
+ }
138
+
139
+ void rallhook_redirect_handler ( VALUE* klass, VALUE* recv, ID* mid ) {
140
+
141
+ VALUE current_thread = rb_thread_current();
142
+ AttachedThreadInfo* tinfo = tinfo_from_thread(current_thread);
143
+ int hook_enable_left = get_hook_enable_left(tinfo);
144
+
145
+ if (get_hook_enabled(tinfo) == 0 || hook_enable_left > 0){
146
+ if(hook_enable_left>0) hook_enable_left --;
147
+ redirect_left( tinfo, hook_enable_left );
148
+ return;
149
+ }
150
+
151
+ VALUE argv_[6];
152
+ if(*mid == id_method_added) {
153
+ *recv = unshadow(*recv); // recv is THE class
154
+ *klass = CLASS_OF(*recv);
155
+ }
156
+
157
+ argv_[0] = tinfo->handle_method_arity;
158
+ if (tinfo->handle_method_arity == 4) {
159
+
160
+ VALUE sym;
161
+ if (rb_id2name(*mid)) {
162
+ sym = ID2SYM(*mid);
163
+ } else {
164
+ sym =Qnil;
165
+ }
166
+
167
+
168
+ argv_[1] = unshadow(*klass);
169
+ argv_[2] = *recv;
170
+ argv_[3] = sym;
171
+ argv_[4] = LONG2FIX(*mid);
172
+ } else {
173
+ argv_[1] = unshadow(*klass);
174
+ argv_[2] = *recv;
175
+ argv_[3] = LONG2FIX(*mid);
176
+ }
177
+
178
+ ID original_id = *mid;
179
+
180
+ disable_redirect(tinfo);
181
+
182
+ rb_thread_t__* th;
183
+ Data_Get_Struct( current_thread, rb_thread_t__, th );
184
+
185
+ void* blockptr = th->passed_block;
186
+ VALUE result = rb_ensure(ensured_handle_method,(VALUE)argv_,restore_hook_status, (VALUE)tinfo);
187
+
188
+ th->passed_block = blockptr;
189
+
190
+ if (original_id != id_binding ) {
191
+
192
+ // method named "binding" cannot be redirected
193
+ if (rb_obj_is_kind_of(result,rb_mMethodRedirect) == Qtrue ) {
194
+ *klass = rb_ivar_get(result,id_klass_var );
195
+ *recv = rb_ivar_get(result,id_recv_var );
196
+ *mid = rb_to_id( rb_ivar_get(result,id_method_var) );
197
+
198
+ if (rb_ivar_get(result,id_unhook_var) != Qnil ) {
199
+ disable_redirect(tinfo);
200
+ }
201
+
202
+ } else {
203
+ shadow_redirect(klass, recv, mid);
204
+ }
205
+ }
206
+
207
+ // methods over class hook are illegal, may change the state of hook
208
+ if (*recv == rb_cHook ) {
209
+ rb_raise(rb_eSecurityError, "Illegal method call: Hook.%s", rb_id2name(*mid) );
210
+ }
211
+
212
+
213
+ }
214
+
215
+ /*
216
+ Activate the hook, it is desirable to use the RAII call to make the hook block exception safe:
217
+
218
+ rallhook.hook method_hadler do
219
+ print "hello world\n" # calls to print, write or whatever are intercepted by method_handler#method_handle
220
+ end # in the finish of the block, the hook are disabled
221
+
222
+ */
223
+ VALUE hook(VALUE self, VALUE hook_proc) {
224
+
225
+ tinfo_from_thread( rb_thread_current() )->hook_proc = hook_proc;
226
+
227
+ VALUE handle_method_method =rb_obj_method(hook_proc, ID2SYM(id_handle_method) );
228
+ VALUE handle_method_method_arity = rb_funcall( handle_method_method, rb_intern("arity"), 0 );
229
+ tinfo_from_thread( rb_thread_current() )->handle_method_arity = FIX2INT( handle_method_method_arity );
230
+
231
+ put_redirect_handler( rallhook_redirect_handler );
232
+
233
+ enable_redirect(tinfo_from_thread(rb_thread_current()));
234
+
235
+ if (rb_block_given_p() ) {
236
+ return rb_ensure(rb_yield, Qnil, unhook, self);
237
+ }
238
+
239
+ return Qnil;
240
+ }
241
+
242
+
243
+ /*
244
+ Disable the hook in the next N calls and reenable them. Useful to avoid infinite recursion and used
245
+ in RallHook::Helper::MethodWrapper
246
+ */
247
+ VALUE from(VALUE self, VALUE num) {
248
+ redirect_left(tinfo_from_thread( rb_thread_current() ), FIX2INT(num)+1);
249
+ return self;
250
+ }
251
+
252
+
253
+ /*
254
+ Re-enable the hook if the hook was disabled and was activated previously with Hook#hook
255
+ If no call to Hook#hook has made, rehook does nothing
256
+ */
257
+ VALUE rehook(VALUE unused) {
258
+ enable_redirect(tinfo_from_thread( rb_thread_current() ));
259
+ if (rb_block_given_p() ) {
260
+ return rb_ensure(rb_yield, Qnil, unhook, Qnil);
261
+ }
262
+ return Qnil;
263
+ }
264
+
265
+
266
+
267
+ VALUE thread_restore_attributes(AttachedThreadInfo* tinfo) {
268
+
269
+ AttachedThreadInfo* current_tinfo = tinfo_from_thread(rb_thread_current());
270
+
271
+ current_tinfo->hook_enabled = tinfo->hook_enabled;
272
+ current_tinfo->hook_enable_left = tinfo->hook_enable_left;
273
+ current_tinfo->hook_proc = tinfo->hook_proc;
274
+ current_tinfo->handle_method_arity = tinfo->handle_method_arity;
275
+
276
+ return Qnil;
277
+ }
278
+
279
+ VALUE rb_thread_acquire_attributes( VALUE thread ) {
280
+
281
+ AttachedThreadInfo* orig = tinfo_from_thread(thread);
282
+ AttachedThreadInfo* dest = tinfo_from_thread(rb_thread_current() );
283
+
284
+ AttachedThreadInfo oldattr;
285
+
286
+ oldattr.hook_enabled = dest->hook_enabled;
287
+ oldattr.hook_enable_left = dest->hook_enable_left;
288
+ oldattr.hook_proc = dest->hook_proc;
289
+ oldattr.handle_method_arity = dest->handle_method_arity;
290
+
291
+ dest->hook_enabled = orig->hook_enabled;
292
+ dest->hook_enable_left = orig->hook_enable_left;
293
+ dest->hook_proc = orig->hook_proc;
294
+ dest->handle_method_arity = orig->handle_method_arity;
295
+
296
+ if (rb_block_given_p() ) {
297
+ return rb_ensure(rb_yield, Qnil, thread_restore_attributes, (VALUE)&oldattr);
298
+ }
299
+
300
+ return Qnil;
301
+ }
302
+
303
+
304
+ extern void Init_rallhook_base() {
305
+
306
+ const char* initcode = "require 'rubygems'\n"
307
+ "require 'ruby-cymbol'\n";
308
+
309
+ rb_eval_string(initcode);
310
+
311
+ /*
312
+ This module brings together all classes and methods belonging to rallhook with the exception of Node
313
+ */
314
+ rb_mRallHook = rb_define_module("RallHook");
315
+ /*
316
+ This class handles the hook, enabling and disable it.
317
+
318
+ Example:
319
+
320
+ # ... instanciate method_handler ... (see README and examples)
321
+
322
+ RallHook::Hook.hook method_hadler do
323
+ print "hello world\n" # calls to print, write or whatever are intercepted by method_handler#method_handle
324
+ end # in the finish of the block, the hook are disabled
325
+
326
+ */
327
+
328
+ rb_cHook = rb_define_class_under(rb_mRallHook, "Hook", rb_cObject);
329
+
330
+ rb_define_singleton_method(rb_cHook, "rehook", rehook, 0 );
331
+ rb_define_singleton_method(rb_cHook, "hook", hook, 1);
332
+ rb_define_singleton_method(rb_cHook, "unhook", unhook, 0);
333
+ rb_define_singleton_method(rb_cHook, "from", from, 1);
334
+
335
+ /*
336
+ Marker module to indicate messages of method redirection
337
+
338
+ Example:
339
+
340
+ class MyMessage
341
+ include MethodRedirect # indicates that this message is about method redirection
342
+
343
+ def initialize(klass, recv, m)
344
+ @klass = klass
345
+ @recv = recv
346
+ @method = m
347
+ end
348
+ end
349
+ class MethodHandler
350
+ class X
351
+ def foo
352
+ end
353
+ end
354
+ def method_handle( ... )
355
+ MyMessage.new(X, x.new, :foo)
356
+ end
357
+ end
358
+
359
+ Don't use this module, use x.redirect(:foo) instead.
360
+
361
+ Example:
362
+ class MethodHandler
363
+ class X
364
+ def foo
365
+ end
366
+ end
367
+ def method_handle( ... )
368
+ x.redirect(:foo)
369
+ end
370
+ end
371
+ */
372
+ rb_mMethodRedirect = rb_define_module_under(rb_mRallHook, "MethodRedirect");
373
+
374
+ init_node();
375
+ init_restrict_def();
376
+ init_redirect();
377
+
378
+ id_call_ = rb_intern("call");
379
+
380
+ id_call = rb_intern("call");
381
+ id_method_wrapper = rb_intern("method_wrapper");
382
+ id_handle_method = rb_intern("handle_method");
383
+ id_return_value_var = rb_intern("@return_value");
384
+ id_klass_var = rb_intern("@klass");
385
+ id_recv_var = rb_intern("@recv");
386
+ id_method_var = rb_intern("@method");
387
+ id_unhook_var = rb_intern("@unhook");
388
+ id_binding = rb_intern("binding");
389
+ id_method_added = rb_intern("method_added");
390
+ id_hook_enabled = rb_intern("__hook_enabled");
391
+ id_hook_enable_left = rb_intern("__hook_enable_left");
392
+ id_hook_proc = rb_intern("__hook_proc");
393
+
394
+ rb_define_method(rb_cThread, "acquire_attributes", rb_thread_acquire_attributes,0);
395
+
396
+ }