evalhook 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ evalhook de tario <rseminara@hotmail.com>
data/CHANGELOG ADDED
@@ -0,0 +1,12 @@
1
+ 0.1.0 Created RDOC
2
+
3
+ Implemented use examples
4
+
5
+ Fixed functionality for shikashi sandbox
6
+
7
+ added method hook tree no hook the tree of a ruby method
8
+
9
+ implemented hook of global vars assignments
10
+
11
+ implemented hook of constant assigment (handle_cdecl)
12
+
data/README ADDED
@@ -0,0 +1,108 @@
1
+ = evalhook - Alternate eval which hook all methods (and more) in the evaluated code
2
+
3
+ == Installation
4
+
5
+ === Gem installation
6
+
7
+ Run in the terminal:
8
+
9
+ sudo gem install evalhook
10
+
11
+ OR
12
+
13
+ * Download the last version of the gem from http://github.com/tario/evalhook/downloads
14
+ * Install the gem with the following;
15
+
16
+ sudo gem install evalhook-X.X.X.gem.
17
+
18
+ == Documentation
19
+
20
+ Full API documentation can be found on:
21
+ http://tario.github.com/evalhook/doc/
22
+
23
+ == Usage
24
+
25
+ This examples and more can be found in examples directory
26
+
27
+ === Basic Example
28
+
29
+ Hook of method calls
30
+
31
+ require "rubygems"
32
+ require "evalhook"
33
+
34
+ class Hook < EvalHook::HookHandler
35
+ def handle_method(klass, recv, method_name)
36
+ print "called #{klass}##{method_name} over #{recv}\n"
37
+ nil
38
+ end
39
+ end
40
+
41
+ h = Hook.new
42
+ h.evalhook('print "hello world\n"')
43
+
44
+ === Basic Example 2
45
+
46
+ Hook of global variables and constants
47
+
48
+ require "rubygems"
49
+ require "evalhook"
50
+
51
+ class Hook < EvalHook::HookHandler
52
+ # global variable assignment/creation
53
+ def handle_gasgn( global_name, new_value)
54
+ print "assignment of #{global_name} = #{new_value}\n"
55
+ nil
56
+ end
57
+
58
+ # constant assignment/creation
59
+ def handle_cdecl( container, const_name, new_value)
60
+ print "assignment of #{container}::#{const_name} = #{new_value}\n"
61
+ nil
62
+ end
63
+ end
64
+
65
+ h = Hook.new
66
+ h.evalhook('
67
+ $a = 4
68
+ A = 4', binding)
69
+
70
+
71
+ === Basic Example 3
72
+
73
+ Redirect of method calls
74
+
75
+ require "rubygems"
76
+ require "evalhook"
77
+
78
+ class Hook < EvalHook::HookHandler
79
+
80
+ include RedirectHelper
81
+
82
+ def handle_method(klass, recv, method_name)
83
+ print "called #{klass}##{method_name} over #{recv}\n"
84
+
85
+ if method_name == :print
86
+ # change the method_name to alternative_print
87
+ Redirect.new(klass, recv, "alternative_print")
88
+ else
89
+ nil # do nothing
90
+ end
91
+
92
+ end
93
+ end
94
+
95
+ module Kernel
96
+ def alternative_print(*args)
97
+ print "alternative ", *args
98
+ end
99
+ end
100
+
101
+ h = Hook.new
102
+ h.evalhook('print "hello world\n"')
103
+
104
+
105
+ == Copying
106
+
107
+ Copyright (c) 2010 Dario Seminara, released under the GPL License (see LICENSE)
108
+
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/gempackagetask'
6
+
7
+ spec = Gem::Specification.new do |s|
8
+ s.name = 'evalhook'
9
+ s.version = '0.1.0'
10
+ s.author = 'Dario Seminara'
11
+ s.email = 'robertodarioseminara@gmail.com'
12
+ s.platform = Gem::Platform::RUBY
13
+ s.summary = 'Alternate eval which hook all methods executed in the evaluated code'
14
+ s.homepage = "http://github.com/tario/evalhook"
15
+ s.has_rdoc = true
16
+ s.extra_rdoc_files = [ 'README' ]
17
+ s.rdoc_options << '--main' << 'README'
18
+ s.extensions = FileList["ext/**/extconf.rb"].to_a
19
+ s.files = Dir.glob("{examples,lib,test}/**/*.rb") + Dir.glob("ext/**/*.c") + Dir.glob("ext/**/*.h") + Dir.glob("ext/**/extconf.rb") +
20
+ [ 'AUTHORS', 'CHANGELOG', 'README', 'Rakefile', 'TODO' ]
21
+ end
22
+
23
+ desc 'Run tests'
24
+ task :default => [ :test ]
25
+
26
+ Rake::TestTask.new('test') do |t|
27
+ t.libs << 'test'
28
+ t.pattern = '{test}/**/test_*.rb'
29
+ t.verbose = true
30
+ end
31
+
32
+ desc 'Generate RDoc'
33
+ Rake::RDocTask.new :rdoc do |rd|
34
+ rd.rdoc_dir = 'doc'
35
+ rd.rdoc_files.add 'lib', 'ext', 'README'
36
+ rd.main = 'README'
37
+ end
38
+
39
+ desc 'Build Gem'
40
+ Rake::GemPackageTask.new spec do |pkg|
41
+ pkg.need_tar = true
42
+ end
43
+
44
+ desc 'Clean up'
45
+ task :clean => [ :clobber_rdoc, :clobber_package ]
46
+
47
+ desc 'Clean up'
48
+ task :clobber => [ :clean ]
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+ * Using of parainterpretation in place of in-memory node tree changing
2
+ * Ruby 1.9 compatibility
@@ -0,0 +1,12 @@
1
+ require "rubygems"
2
+ require "evalhook"
3
+
4
+ class Hook < EvalHook::HookHandler
5
+ def handle_method(klass, recv, method_name)
6
+ print "called #{klass}##{method_name} over #{recv}\n"
7
+ nil
8
+ end
9
+ end
10
+
11
+ h = Hook.new
12
+ h.evalhook('print "hello world\n"')
@@ -0,0 +1,21 @@
1
+ require "rubygems"
2
+ require "evalhook"
3
+
4
+ class Hook < EvalHook::HookHandler
5
+ # global variable assignment/creation
6
+ def handle_gasgn( global_name, new_value)
7
+ print "assignment of #{global_name} = #{new_value}\n"
8
+ nil
9
+ end
10
+
11
+ # constant assignment/creation
12
+ def handle_cdecl( container, const_name, new_value)
13
+ print "assignment of #{container}::#{const_name} = #{new_value}\n"
14
+ nil
15
+ end
16
+ end
17
+
18
+ h = Hook.new
19
+ h.evalhook('
20
+ $a = 4
21
+ A = 4', binding)
@@ -0,0 +1,28 @@
1
+ require "rubygems"
2
+ require "evalhook"
3
+
4
+ class Hook < EvalHook::HookHandler
5
+
6
+ include RedirectHelper
7
+
8
+ def handle_method(klass, recv, method_name)
9
+ print "called #{klass}##{method_name} over #{recv}\n"
10
+
11
+ if method_name == :print
12
+ # change the method_name to alternative_print
13
+ Redirect.new(klass, recv, "alternative_print")
14
+ else
15
+ nil # do nothing
16
+ end
17
+
18
+ end
19
+ end
20
+
21
+ module Kernel
22
+ def alternative_print(*args)
23
+ print "alternative ", *args
24
+ end
25
+ end
26
+
27
+ h = Hook.new
28
+ h.evalhook('print "hello world\n"')
@@ -0,0 +1,679 @@
1
+ /*
2
+
3
+ This file is part of the evalhook project, http://github.com/tario/evalhook
4
+
5
+ Copyright (c) 2010 Roberto Dario Seminara <robertodarioseminara@gmail.com>
6
+
7
+ evalhook 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
+ evalhook 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 evalhook. if not, see <http://www.gnu.org/licenses/>.
19
+
20
+ */
21
+
22
+ #include <ruby.h>
23
+ #include <env.h>
24
+ #include <node.h>
25
+
26
+ VALUE m_EvalHook ;
27
+ VALUE c_HookHandler;
28
+
29
+ #define nd_3rd u3.node
30
+
31
+ ID method_call;
32
+ ID method_hooked_method;
33
+ ID method_local_hooked_method;
34
+ ID method_private_hooked_method;
35
+ ID method_public_hooked_method;
36
+ ID method_protected_hooked_method;
37
+ ID method_public, method_private, method_protected;
38
+
39
+ ID method_set_hook_handler;
40
+
41
+ struct BLOCK {
42
+ NODE *var;
43
+ NODE *body;
44
+ VALUE self;
45
+ struct FRAME frame;
46
+ struct SCOPE *scope;
47
+ VALUE klass;
48
+ NODE *cref;
49
+ int iter;
50
+ int vmode;
51
+ int flags;
52
+ int uniq;
53
+ struct RVarmap *dyna_vars;
54
+ VALUE orig_thread;
55
+ VALUE wrapper;
56
+ VALUE block_obj;
57
+ struct BLOCK *outer;
58
+ struct BLOCK *prev;
59
+ };
60
+
61
+ struct METHOD {
62
+ VALUE klass, rklass;
63
+ VALUE recv;
64
+ ID id, oid;
65
+ int safe_level;
66
+ NODE *body;
67
+ };
68
+
69
+
70
+ void process_node(NODE* node, VALUE handler);
71
+
72
+ void patch_local_call_node(NODE* node, VALUE handler) {
73
+ NODE* args1 = NEW_LIST(NEW_LIT(ID2SYM(node->nd_mid)));
74
+ NODE* args2 = NEW_LIST(NEW_LIT(handler));
75
+
76
+ node->nd_recv = NEW_CALL(NEW_SELF(), method_local_hooked_method, args1);
77
+ node->nd_recv = NEW_CALL(node->nd_recv, method_set_hook_handler, args2);
78
+ node->nd_mid = method_call;
79
+
80
+ nd_set_type(node, NODE_CALL);
81
+ }
82
+
83
+ void patch_call_node(NODE* node, VALUE handler) {
84
+ NODE* args1 = NEW_LIST(NEW_LIT(ID2SYM(node->nd_mid)));
85
+ NODE* args2 = NEW_LIST(NEW_LIT(handler));
86
+
87
+ node->nd_recv = NEW_CALL(node->nd_recv, method_hooked_method, args1);
88
+ node->nd_recv = NEW_CALL(node->nd_recv, method_set_hook_handler, args2);
89
+
90
+ node->nd_mid = method_call;
91
+ }
92
+
93
+ struct global_entry {
94
+ struct global_variable *var;
95
+ ID id;
96
+ };
97
+
98
+ void process_individual_node(NODE* node, VALUE handler) {
99
+ ID id = node->nd_mid;
100
+
101
+ switch (nd_type(node)) {
102
+ case NODE_COLON3: {
103
+ rb_raise(rb_eSecurityError, "Forbidden node type colon3 (reference to global namespace)");
104
+ }
105
+ /* case NODE_LASGN:
106
+ case NODE_IASGN:
107
+ case NODE_DASGN:
108
+ case NODE_CVASGN:
109
+ case NODE_CVDECL:*/
110
+ case NODE_GASGN: {
111
+ NODE* args1 = NEW_LIST(NEW_LIT(ID2SYM(node->nd_entry->id)));
112
+ NODE* args2 = NEW_LIST(node->nd_value);
113
+
114
+ node->nd_recv = NEW_CALL(NEW_LIT(handler), rb_intern("hooked_gasgn"), args1);
115
+ node->nd_mid = rb_intern("set_value");
116
+ node->nd_args = args2;
117
+
118
+ nd_set_type(node, NODE_CALL);
119
+ break;
120
+ }
121
+ case NODE_CDECL: {
122
+
123
+ NODE* else_node = node->nd_else;
124
+ NODE* head_node = node->nd_head;
125
+ NODE* base_class;
126
+
127
+ ID vid;
128
+
129
+ if (node->nd_vid == 0) {
130
+ base_class = else_node;
131
+ vid = node->nd_else->nd_mid;
132
+ } else {
133
+ base_class = NEW_LIT( (ruby_cref->nd_clss));
134
+ vid = node->nd_vid;
135
+ }
136
+
137
+
138
+ NODE* args1 = NEW_LIST(base_class);
139
+ NODE* args2 = NEW_LIST(NEW_LIT(ID2SYM(vid)));
140
+ NODE* args3 = NEW_LIST(node->nd_value);
141
+
142
+ node->nd_recv = NEW_CALL(NEW_LIT(handler), rb_intern("hooked_cdecl"), args1);
143
+ node->nd_recv = NEW_CALL(node->nd_recv, rb_intern("set_id"), args2);
144
+ node->nd_mid = rb_intern("set_value");
145
+ node->nd_args = args3;
146
+
147
+ nd_set_type(node, NODE_CALL);
148
+ break;
149
+ }
150
+
151
+
152
+ case NODE_SUPER:
153
+ case NODE_ZSUPER: {
154
+ node->nd_mid = rb_intern("hooked_super");
155
+ node->nd_recv = NEW_LIT(handler);
156
+ nd_set_type(node, NODE_CALL);
157
+ break;
158
+ }
159
+ case NODE_FCALL: {
160
+ if (id == method_public) break;
161
+ if (id == method_private) break;
162
+ if (id == method_protected) break;
163
+
164
+ patch_local_call_node(node, handler);
165
+ break;
166
+ }
167
+
168
+ case NODE_CALL: {
169
+ patch_call_node(node, handler);
170
+ break;
171
+
172
+ }
173
+ case NODE_VCALL: {
174
+ if (id == method_public) break;
175
+ if (id == method_private) break;
176
+ if (id == method_protected) break;
177
+
178
+ patch_local_call_node(node, handler);
179
+
180
+ break;
181
+ }
182
+ }
183
+ }
184
+
185
+ void process_recursive_node(NODE* node, VALUE handler ) {
186
+
187
+ switch (nd_type(node)) {
188
+
189
+ case NODE_BLOCK:
190
+ {
191
+ while (node) {
192
+ process_node(node->nd_head, handler);
193
+ node = node->nd_next;
194
+ }
195
+ }
196
+ break;
197
+
198
+ case NODE_FBODY:
199
+ case NODE_DEFINED:
200
+ case NODE_COLON2:
201
+ process_node(node->nd_head, handler);
202
+ break;
203
+
204
+ case NODE_MATCH2:
205
+ case NODE_MATCH3:
206
+ process_node(node->nd_recv, handler);
207
+ process_node(node->nd_value, handler);
208
+ break;
209
+
210
+ case NODE_BEGIN:
211
+ case NODE_OPT_N:
212
+ case NODE_NOT:
213
+ process_node(node->nd_body, handler);
214
+ break;
215
+
216
+ case NODE_IF:
217
+ process_node(node->nd_cond, handler);
218
+ if (node->nd_body) {
219
+ process_node(node->nd_body, handler);
220
+ }
221
+ if (node->nd_else) {
222
+ process_node(node->nd_else, handler);
223
+ }
224
+ break;
225
+
226
+ case NODE_CASE:
227
+ if (node->nd_head != NULL) {
228
+ process_node(node->nd_head, handler);
229
+ }
230
+ node = node->nd_body;
231
+ while (node) {
232
+ process_node(node, handler);
233
+ if (nd_type(node) == NODE_WHEN) { /* when */
234
+ node = node->nd_next;
235
+ } else {
236
+ break; /* else */
237
+ }
238
+ }
239
+ break;
240
+
241
+ case NODE_WHEN:
242
+ process_node(node->nd_head, handler);
243
+ if (node->nd_body) {
244
+ process_node(node->nd_body, handler);
245
+ }
246
+ break;
247
+
248
+ case NODE_WHILE:
249
+ case NODE_UNTIL:
250
+ process_node(node->nd_cond, handler);
251
+ if (node->nd_body) {
252
+ process_node(node->nd_body, handler);
253
+ }
254
+ break;
255
+
256
+ case NODE_BLOCK_PASS:
257
+ process_node(node->nd_body, handler);
258
+ process_node(node->nd_iter, handler);
259
+ break;
260
+
261
+ case NODE_ITER:
262
+ case NODE_FOR:
263
+ process_node(node->nd_iter, handler);
264
+ if (node->nd_var != (NODE *)1
265
+ && node->nd_var != (NODE *)2
266
+ && node->nd_var != NULL) {
267
+ process_node(node->nd_var, handler);
268
+ }
269
+ process_node(node->nd_body, handler);
270
+ break;
271
+
272
+ case NODE_BREAK:
273
+ case NODE_NEXT:
274
+ if (node->nd_stts)
275
+ process_node(node->nd_stts, handler);
276
+
277
+ break;
278
+
279
+ case NODE_YIELD:
280
+ if (node->nd_stts)
281
+ process_node(node->nd_stts, handler);
282
+
283
+ break;
284
+
285
+ case NODE_RESCUE:
286
+ process_node(node->nd_1st, handler);
287
+ process_node(node->nd_2nd, handler);
288
+ process_node(node->nd_3rd, handler);
289
+ break;
290
+
291
+ /*
292
+ // rescue body:
293
+ // begin stmt rescue exception => var; stmt; [rescue e2 => v2; s2;]* end
294
+ // stmt rescue stmt
295
+ // a = b rescue c
296
+ */
297
+
298
+ case NODE_RESBODY:
299
+ if (node->nd_3rd) {
300
+ process_node(node->nd_3rd, handler);
301
+ }
302
+ process_node(node->nd_2nd, handler);
303
+ process_node(node->nd_1st, handler);
304
+ break;
305
+
306
+ case NODE_ENSURE:
307
+ process_node(node->nd_head, handler);
308
+ if (node->nd_ensr) {
309
+ process_node(node->nd_ensr, handler);
310
+ }
311
+ break;
312
+
313
+ case NODE_AND:
314
+ case NODE_OR:
315
+ process_node(node->nd_1st, handler);
316
+ process_node(node->nd_2nd, handler);
317
+ break;
318
+
319
+ case NODE_FLIP2:
320
+ case NODE_FLIP3:
321
+ process_node(node->nd_beg, handler);
322
+ process_node(node->nd_end, handler);
323
+ break;
324
+
325
+ case NODE_DOT2:
326
+ case NODE_DOT3:
327
+ process_node(node->nd_beg, handler);
328
+ process_node(node->nd_end, handler);
329
+ break;
330
+
331
+ case NODE_RETURN:
332
+ if (node->nd_stts)
333
+ process_node(node->nd_stts, handler);
334
+ break;
335
+
336
+ case NODE_ARGSCAT:
337
+ case NODE_ARGSPUSH:
338
+ process_node(node->nd_head, handler);
339
+ process_node(node->nd_body, handler);
340
+ break;
341
+
342
+ case NODE_CALL:
343
+ case NODE_FCALL:
344
+ case NODE_VCALL:
345
+ if (nd_type(node) != NODE_FCALL) {
346
+ if (node->nd_recv) process_node(node->nd_recv, handler);
347
+ }
348
+ if (node->nd_args || nd_type(node) != NODE_FCALL) {
349
+ if (node->nd_args) process_node(node->nd_args, handler);
350
+ }
351
+ break;
352
+
353
+ case NODE_SUPER:
354
+ process_node(node->nd_args, handler);
355
+ break;
356
+
357
+ case NODE_BMETHOD:
358
+ {
359
+ struct BLOCK *data;
360
+ Data_Get_Struct(node->nd_cval, struct BLOCK, data);
361
+
362
+ if (data->var == 0 || data->var == (NODE *)1 || data->var == (NODE *)2) {
363
+ } else {
364
+ process_node(data->var, handler);
365
+ }
366
+ process_node(data->body, handler);
367
+ }
368
+ break;
369
+
370
+ #if RUBY_VERSION_CODE < 190
371
+ case NODE_DMETHOD:
372
+ {
373
+ struct METHOD *data;
374
+ Data_Get_Struct(node->nd_cval, struct METHOD, data);
375
+ process_node(data->body, handler);
376
+
377
+ break;
378
+ }
379
+ #endif
380
+
381
+ case NODE_METHOD:
382
+ // You should not ever get here. parse_tree_for_meth passes nd_body
383
+ process_node(node->nd_body, handler);
384
+ break;
385
+
386
+ case NODE_SCOPE:
387
+ process_node(node->nd_next, handler);
388
+ break;
389
+
390
+ case NODE_OP_ASGN1:
391
+ process_node(node->nd_recv, handler);
392
+ #if RUBY_VERSION_CODE < 185
393
+ process_node(node->nd_args->nd_next, handler);
394
+ #else
395
+ process_node(node->nd_args->nd_2nd, handler);
396
+ #endif
397
+ process_node(node->nd_args->nd_head, handler);
398
+ break;
399
+
400
+ case NODE_OP_ASGN2:
401
+ process_node(node->nd_recv, handler);
402
+ process_node(node->nd_value, handler);
403
+ break;
404
+
405
+ case NODE_OP_ASGN_AND:
406
+ case NODE_OP_ASGN_OR:
407
+ process_node(node->nd_head, handler);
408
+ process_node(node->nd_value, handler);
409
+ break;
410
+
411
+ case NODE_MASGN:
412
+ if (node->nd_head) {
413
+ process_node(node->nd_head, handler);
414
+ }
415
+ if (node->nd_args) {
416
+ if (node->nd_args != (NODE *)-1) {
417
+ process_node(node->nd_args, handler);
418
+ }
419
+ }
420
+ if (node->nd_value) {
421
+ process_node(node->nd_value, handler);
422
+ }
423
+ break;
424
+
425
+ case NODE_LASGN:
426
+ case NODE_IASGN:
427
+ case NODE_DASGN:
428
+ case NODE_CVASGN:
429
+ case NODE_CVDECL:
430
+ case NODE_GASGN:
431
+ process_node(node->nd_value, handler);
432
+ break;
433
+
434
+ case NODE_CDECL:
435
+ if (node->nd_vid) {
436
+ } else {
437
+ process_node(node->nd_else, handler);
438
+ }
439
+ process_node(node->nd_value, handler);
440
+ break;
441
+
442
+ case NODE_DASGN_CURR:
443
+ if (node->nd_value) {
444
+ process_node(node->nd_value, handler);
445
+ }
446
+ break;
447
+
448
+ case NODE_ALIAS: /* u1 u2 (alias :blah :blah2) */
449
+ #if RUBY_VERSION_CODE < 185
450
+ #else
451
+ process_node(node->nd_1st, handler);
452
+ process_node(node->nd_2nd, handler);
453
+ #endif
454
+ break;
455
+
456
+ case NODE_UNDEF: /* u2 (undef name, ...) */
457
+ #if RUBY_VERSION_CODE < 185
458
+ #else
459
+ process_node(node->nd_value, handler);
460
+ #endif
461
+ break;
462
+
463
+ case NODE_HASH:
464
+ {
465
+ NODE *list;
466
+
467
+ list = node->nd_head;
468
+ while (list) {
469
+ process_node(list->nd_head, handler);
470
+ list = list->nd_next;
471
+ }
472
+ }
473
+ break;
474
+
475
+ case NODE_ARRAY:
476
+ while (node) {
477
+ process_node(node->nd_head, handler);
478
+ node = node->nd_next;
479
+ }
480
+ break;
481
+
482
+ case NODE_DSTR:
483
+ case NODE_DSYM:
484
+ case NODE_DXSTR:
485
+ case NODE_DREGX:
486
+ case NODE_DREGX_ONCE:
487
+ {
488
+ NODE *list = node->nd_next;
489
+ while (list) {
490
+ if (list->nd_head) {
491
+ process_node(list->nd_head, handler);
492
+ }
493
+ list = list->nd_next;
494
+ }
495
+ }
496
+ break;
497
+
498
+ case NODE_DEFN:
499
+ case NODE_DEFS:
500
+ if (node->nd_defn) {
501
+ if (nd_type(node) == NODE_DEFS)
502
+ process_node(node->nd_recv, handler);
503
+
504
+ process_node(node->nd_defn, handler);
505
+ }
506
+ break;
507
+
508
+ case NODE_CLASS:
509
+ case NODE_MODULE:
510
+ if (nd_type(node->nd_cpath) == NODE_COLON2 && ! node->nd_cpath->nd_vid) {
511
+ } else {
512
+ process_node(node->nd_cpath, handler);
513
+ }
514
+
515
+ if (nd_type(node) == NODE_CLASS) {
516
+ if (node->nd_super) {
517
+ process_node(node->nd_super, handler);
518
+ }
519
+ }
520
+ process_node(node->nd_body, handler);
521
+ break;
522
+
523
+ case NODE_SCLASS:
524
+ process_node(node->nd_recv, handler);
525
+ process_node(node->nd_body, handler);
526
+ break;
527
+
528
+ case NODE_ARGS: {
529
+ NODE *optnode;
530
+ optnode = node->nd_opt;
531
+ if (optnode) {
532
+ process_node(node->nd_opt, handler);
533
+ }
534
+ } break;
535
+
536
+ case NODE_NEWLINE:
537
+ process_node(node->nd_next, handler);
538
+ break;
539
+
540
+ case NODE_SPLAT:
541
+ case NODE_TO_ARY:
542
+ case NODE_SVALUE: /* a = b, c */
543
+ process_node(node->nd_head, handler);
544
+ break;
545
+
546
+ case NODE_ATTRASGN: /* literal.meth = y u1 u2 u3 */
547
+ /* node id node */
548
+ if (node->nd_1st == RNODE(1)) {
549
+ // add_to_parse_tree(self, current, NEW_SELF(), locals);
550
+ } else {
551
+ process_node(node->nd_1st, handler);
552
+ }
553
+ process_node(node->nd_3rd, handler);
554
+ break;
555
+
556
+ case NODE_EVSTR:
557
+ process_node(node->nd_2nd, handler);
558
+ break;
559
+
560
+
561
+ #if RUBY_VERSION_CODE >= 190
562
+ case NODE_ERRINFO:
563
+ case NODE_VALUES:
564
+ case NODE_PRELUDE:
565
+ case NODE_LAMBDA:
566
+ puts("no worky in 1.9 yet");
567
+ break;
568
+ #endif
569
+
570
+ /* Nodes we found but have yet to decypher */
571
+ /* I think these are all runtime only... not positive but... */
572
+ case NODE_MEMO: /* enum.c zip */
573
+ case NODE_CREF:
574
+ /* #defines: */
575
+ /* case NODE_LMASK: */
576
+ /* case NODE_LSHIFT: */
577
+ default:
578
+ break;
579
+ }
580
+ }
581
+
582
+ void process_node(NODE* node, VALUE handler) {
583
+ if (node) {
584
+ process_recursive_node(node, handler);
585
+ process_individual_node(node, handler);
586
+ }
587
+ }
588
+
589
+ VALUE hook_method_tree(VALUE self, VALUE rb_method) {
590
+
591
+ struct METHOD* method;
592
+ Data_Get_Struct(rb_method,struct METHOD,method);
593
+
594
+ NODE* node = method->body->nd_defn;
595
+ process_node(node, self);
596
+
597
+ return Qnil;
598
+ }
599
+
600
+
601
+ VALUE hook_block(VALUE self, VALUE handler) {
602
+ process_node(ruby_frame->node->nd_recv, handler);
603
+ }
604
+
605
+ VALUE caller_method(VALUE self, VALUE rblevel) {
606
+ int level = FIX2INT(rblevel);
607
+
608
+ struct FRAME* frame = ruby_frame;
609
+ while(level--) frame = frame->prev;
610
+
611
+ return ID2SYM(frame->orig_func);
612
+ }
613
+
614
+ VALUE caller_class(VALUE self, VALUE rblevel) {
615
+ int level = FIX2INT(rblevel);
616
+
617
+ struct FRAME* frame = ruby_frame;
618
+ while(level--) frame = frame->prev;
619
+
620
+ return frame->last_class;
621
+ }
622
+
623
+ VALUE caller_obj(VALUE self, VALUE rblevel) {
624
+ int level = FIX2INT(rblevel);
625
+
626
+ struct FRAME* frame = ruby_frame;
627
+ while(level--) frame = frame->prev;
628
+
629
+ return frame->self;
630
+ }
631
+
632
+
633
+ extern void Init_evalhook_base() {
634
+ m_EvalHook = rb_define_module("EvalHook");
635
+
636
+ /*
637
+ Class to create hook instances for specific handling of method calls and/or const and global assignment
638
+
639
+ === Example:
640
+
641
+ Hook of method calls
642
+
643
+ require "rubygems"
644
+ require "evalhook"
645
+
646
+ class Hook < EvalHook::HookHandler
647
+ def handle_method(klass, recv, method_name)
648
+ print "called #{klass}##{method_name} over #{recv}\n"
649
+ nil
650
+ end
651
+ end
652
+
653
+ h = Hook.new
654
+ h.evalhook('print "hello world\n"')
655
+
656
+
657
+
658
+
659
+ See README for more examples
660
+
661
+ */
662
+ c_HookHandler = rb_define_class_under(m_EvalHook, "HookHandler", rb_cObject);
663
+
664
+ rb_define_singleton_method(m_EvalHook, "hook_block", hook_block, 1);
665
+
666
+ rb_define_method(c_HookHandler, "hook_method_tree", hook_method_tree, 1);
667
+
668
+ rb_define_method(c_HookHandler, "caller_method", caller_method, 1);
669
+ rb_define_method(c_HookHandler, "caller_class", caller_class, 1);
670
+ rb_define_method(c_HookHandler, "caller_obj", caller_obj, 1);
671
+
672
+ method_local_hooked_method = rb_intern("local_hooked_method");
673
+ method_hooked_method = rb_intern("hooked_method");
674
+ method_call = rb_intern("call");
675
+ method_private = rb_intern("private");
676
+ method_public = rb_intern("public");
677
+ method_protected = rb_intern("protected");
678
+ method_set_hook_handler = rb_intern("set_hook_handler");
679
+ }
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+ dir_config('evalhook_base')
3
+ CONFIG['CC'] = 'gcc'
4
+ create_makefile('evalhook_base')
5
+
6
+
7
+
@@ -0,0 +1,54 @@
1
+ =begin
2
+
3
+ This file is part of the evalhook project, http://github.com/tario/evalhook
4
+
5
+ Copyright (c) 2010 Roberto Dario Seminara <robertodarioseminara@gmail.com>
6
+
7
+ evalhook 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
+ evalhook 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 evalhook. if not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+ module RedirectHelper
22
+ module MethodRedirect
23
+ end
24
+
25
+ #
26
+ #Internal class used for return method redirection messages
27
+ #Example:
28
+ # ...
29
+ # class X
30
+ # def foo
31
+ # end
32
+ # end
33
+ # def handle_method
34
+ # return RedirectHelper::Redirect.new(X, X.new, :foo)
35
+ # end
36
+
37
+ class Redirect
38
+ include MethodRedirect
39
+
40
+ attr_reader :klass
41
+ attr_reader :recv
42
+
43
+ def method_name
44
+ @method
45
+ end
46
+
47
+ def initialize(klass, recv, m, unhook = nil)
48
+ @klass = klass
49
+ @recv = recv
50
+ @method = m
51
+ end
52
+ end
53
+
54
+ end
data/lib/evalhook.rb ADDED
@@ -0,0 +1,219 @@
1
+ =begin
2
+
3
+ This file is part of the evalhook project, http://github.com/tario/evalhook
4
+
5
+ Copyright (c) 2010 Roberto Dario Seminara <robertodarioseminara@gmail.com>
6
+
7
+ evalhook 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
+ evalhook 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 evalhook. if not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+ require "evalhook"
22
+ require "evalhook_base"
23
+ require "evalhook/redirect_helper"
24
+
25
+
26
+ class Object
27
+ def local_hooked_method(mname)
28
+ EvalHook::HookedMethod.new(self,mname,true)
29
+ end
30
+ def hooked_method(mname)
31
+ EvalHook::HookedMethod.new(self,mname,false)
32
+ end
33
+ end
34
+
35
+ module EvalHook
36
+
37
+ # used internally
38
+ class HookedMethod
39
+
40
+ def initialize(recv, m,localcall)
41
+ @recv = recv
42
+ @m = m
43
+ @localcall = localcall
44
+ end
45
+
46
+ # used internally
47
+ def set_hook_handler(method_handler)
48
+ @method_handler = method_handler
49
+ self
50
+ end
51
+
52
+ # used internally
53
+ def set_class(klass)
54
+ @klass = klass
55
+ self
56
+ end
57
+
58
+ # used internally
59
+ def call(*args)
60
+ method_handler = @method_handler
61
+ ret = nil
62
+
63
+ klass = @klass || @recv.method(@m).owner
64
+ method_name = @m
65
+ recv = @recv
66
+
67
+ if method_handler
68
+ ret = method_handler.handle_method(klass, recv, method_name )
69
+ end
70
+
71
+ if ret.kind_of? RedirectHelper::MethodRedirect
72
+ klass = ret.klass
73
+ method_name = ret.method_name
74
+ recv = ret.recv
75
+ end
76
+
77
+ if block_given?
78
+ klass.instance_method(method_name).bind(recv).call(*args) do |*x|
79
+ yield(*x)
80
+ end
81
+ else
82
+ klass.instance_method(method_name).bind(recv).call(*args)
83
+ end
84
+ end
85
+ end
86
+
87
+ # used internally
88
+ class FakeEvalHook
89
+ def self.hook_block(*args)
90
+ end
91
+ end
92
+
93
+ class HookHandler
94
+
95
+ # used internally
96
+ class HookCdecl
97
+ def initialize(klass, hook_handler)
98
+ @klass = klass
99
+ @hook_handler = hook_handler
100
+ end
101
+
102
+ def set_id(const_id)
103
+ @const_id = const_id
104
+ self
105
+ end
106
+
107
+ def set_value(value)
108
+ klass = @klass
109
+ const_id = @const_id
110
+
111
+ ret = @hook_handler.handle_cdecl( @klass, @const_id, value )
112
+
113
+ if ret then
114
+ klass = ret.klass
115
+ const_id = ret.const_id
116
+ value = ret.value
117
+ end
118
+
119
+ klass.const_set(const_id, value)
120
+ end
121
+ end
122
+
123
+ # used internally
124
+ class HookGasgn
125
+ def initialize(global_id, hook_handler)
126
+ @global_id = global_id
127
+ @hook_handler = hook_handler
128
+ end
129
+
130
+ def set_value(value)
131
+ global_id = @global_id
132
+
133
+ ret = @hook_handler.handle_gasgn(@global_id, value)
134
+
135
+ if ret then
136
+ global_id = ret.global_id
137
+ value = ret.value
138
+ end
139
+
140
+ eval("#{global_id} = value")
141
+ end
142
+ end
143
+
144
+ # used internally
145
+ def hooked_cdecl(context)
146
+ HookCdecl.new(context,self)
147
+ end
148
+
149
+ # used internally
150
+ def hooked_gasgn(global_id)
151
+ HookGasgn.new(global_id,self)
152
+ end
153
+
154
+ # Overwrite to handle the assignment/creation of global variables. By default do nothing but assign the variable. See examples
155
+ def handle_gasgn(*args)
156
+ nil
157
+ end
158
+
159
+ # Overwrite to handle the assignment/creation of constants. By default do nothing but assign the variable. See examples
160
+ def handle_cdecl(*args)
161
+ nil
162
+ end
163
+
164
+ # Overwrite to handle the method calls. By default do nothing and the methods are called normally
165
+ def handle_method(klass,recv,method_name)
166
+ nil
167
+ end
168
+
169
+ def hooked_super(*args)
170
+ hm = caller_obj(2).hooked_method(caller_method(2))
171
+ hm.set_class(caller_class(2).superclass)
172
+ hm.set_hook_handler(self)
173
+
174
+ if block_given?
175
+ hm.call(*args) do |*x|
176
+ yield(*x)
177
+ end
178
+ else
179
+ hm.call(*args)
180
+ end
181
+ end
182
+
183
+ def evalhook(*args)
184
+
185
+ EvalHook.method_handler = self
186
+
187
+ args[0] = "
188
+ retvalue = nil
189
+ EvalHook.double_run do |run|
190
+ ( if (run)
191
+ retvalue = begin
192
+ #{args[0]}
193
+ end
194
+ EvalHook::FakeEvalHook
195
+ else
196
+ EvalHook
197
+ end ).hook_block(ObjectSpace._id2ref(#{object_id}))
198
+ end
199
+ retvalue
200
+ "
201
+ eval(*args)
202
+
203
+ end
204
+ end
205
+
206
+ module ModuleMethods
207
+
208
+ attr_accessor :method_handler
209
+
210
+ def double_run
211
+ yield(false)
212
+ yield(true)
213
+ end
214
+ end
215
+
216
+ class << self
217
+ include ModuleMethods
218
+ end
219
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: evalhook
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Dario Seminara
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-10-30 00:00:00 -03:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description:
23
+ email: robertodarioseminara@gmail.com
24
+ executables: []
25
+
26
+ extensions:
27
+ - ext/evalhook_base/extconf.rb
28
+ extra_rdoc_files:
29
+ - README
30
+ files:
31
+ - examples/example1.rb
32
+ - examples/example3.rb
33
+ - examples/example2.rb
34
+ - lib/evalhook/redirect_helper.rb
35
+ - lib/evalhook.rb
36
+ - ext/evalhook_base/evalhook_base.c
37
+ - ext/evalhook_base/extconf.rb
38
+ - AUTHORS
39
+ - CHANGELOG
40
+ - README
41
+ - Rakefile
42
+ - TODO
43
+ has_rdoc: true
44
+ homepage: http://github.com/tario/evalhook
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --main
50
+ - README
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ hash: 3
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.3.7
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Alternate eval which hook all methods executed in the evaluated code
78
+ test_files: []
79
+