evalhook 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+