evalhook 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,15 @@
1
+ 0.4.0 Removed evalmimic dependency
2
+
3
+ Implemented hooking of super as methods calls
4
+
5
+ Added hooking for colon2 namespace access (handle_colon2)
6
+
7
+ Added hooking for global variable read acess (handle_gvar)
8
+
9
+ Added hooking for constant read access (handle_const)
10
+
11
+ Refactor of AST processing code (using SexpProcessor)
12
+
1
13
  0.3.1 Fixed bug when use reflection on call handler (thanks jembezmamy for the feedback!)
2
14
 
3
15
  0.3.0 Refactor: removed C extension for hooking and use partialruby instead
data/Rakefile CHANGED
@@ -6,18 +6,18 @@ require 'rake/gempackagetask'
6
6
 
7
7
  spec = Gem::Specification.new do |s|
8
8
  s.name = 'evalhook'
9
- s.version = '0.3.1'
9
+ s.version = '0.4.0'
10
10
  s.author = 'Dario Seminara'
11
11
  s.email = 'robertodarioseminara@gmail.com'
12
12
  s.platform = Gem::Platform::RUBY
13
13
  s.summary = 'Alternate eval which hook all methods executed in the evaluated code'
14
14
  s.homepage = "http://github.com/tario/evalhook"
15
- s.add_dependency "partialruby", ">= 0.1.0"
15
+ s.add_dependency "partialruby", ">= 0.2.0"
16
16
  s.add_dependency "ruby_parser", ">= 2.0.6"
17
17
  s.has_rdoc = true
18
18
  s.extra_rdoc_files = [ 'README' ]
19
19
  s.rdoc_options << '--main' << 'README'
20
- s.files = Dir.glob("{examples,lib,spec}/**/*.rb") + Dir.glob("ext/**/*.c") + Dir.glob("ext/**/*.h") + Dir.glob("ext/**/extconf.rb") +
20
+ s.files = Dir.glob("{examples,lib,spec}/**/*.rb") +
21
21
  [ 'LICENSE', 'AUTHORS', 'CHANGELOG', 'README', 'Rakefile', 'TODO' ]
22
22
  end
23
23
 
@@ -33,7 +33,7 @@ end
33
33
  desc 'Generate RDoc'
34
34
  Rake::RDocTask.new :rdoc do |rd|
35
35
  rd.rdoc_dir = 'doc'
36
- rd.rdoc_files.add 'lib', 'ext', 'README'
36
+ rd.rdoc_files.add 'lib', 'README'
37
37
  rd.main = 'README'
38
38
  end
39
39
 
@@ -73,6 +73,21 @@ module RedirectHelper
73
73
  end
74
74
  end
75
75
 
76
+ class Value
77
+ attr_reader :value
78
+ def initialize(value)
79
+ @value = value
80
+ end
81
+ end
82
+
83
+ def global_value(value)
84
+ Value.new(value)
85
+ end
86
+
87
+ def const_value(value)
88
+ Value.new(value)
89
+ end
90
+
76
91
  def redirect_method(*args)
77
92
  Redirect.new(*args)
78
93
  end
@@ -0,0 +1,279 @@
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 "ruby_parser"
22
+ require "partialruby"
23
+ require "sexp_processor"
24
+
25
+ module EvalHook
26
+
27
+
28
+ class TreeProcessor < SexpProcessor
29
+ def initialize(hook_handler)
30
+ super()
31
+ @hook_handler = hook_handler
32
+ @def_scope = Array.new
33
+ self.require_empty = false
34
+ end
35
+
36
+ def const_path_emul(code)
37
+ if code.instance_of? Array
38
+ if (code.size == 1)
39
+ s(:const, code[-1].to_sym)
40
+ else
41
+ s(:colon2, const_path_emul(code[0..-2]), code[-1].to_sym)
42
+ end
43
+ else
44
+ const_path_emul code.split("::")
45
+ end
46
+ end
47
+
48
+ def hook_handler_reference
49
+ # create a global_variable name
50
+ unless @hh_ref_global_name
51
+ global_variable_name = "$hook_handler_" + rand(10000000000).to_s
52
+ eval("#{global_variable_name} = @hook_handler")
53
+
54
+ @hh_ref_global_name = global_variable_name.to_sym
55
+ end
56
+
57
+ s(:gvar, @hh_ref_global_name)
58
+ end
59
+
60
+ def process_gasgn(tree)
61
+
62
+ args1 = s(:arglist, s(:lit, tree[1]))
63
+ args2 = s(:arglist, process(tree[2]))
64
+
65
+ firstcall = s(:call, hook_handler_reference, :hooked_gasgn, args1)
66
+
67
+ s(:call, firstcall, :set_value, args2)
68
+
69
+ end
70
+
71
+
72
+ def process_call(tree)
73
+
74
+ original_receiver = tree[1]
75
+
76
+ receiver = process(original_receiver|| s(:self))
77
+
78
+ args1 = s(:arglist, s(:lit, tree[2]), s(:call, nil, :binding, s(:arglist)))
79
+ args2 = s(:arglist, hook_handler_reference)
80
+
81
+ firstcall = nil
82
+ secondcall = nil
83
+
84
+ if original_receiver
85
+ firstcall = s(:call, receiver, :local_hooked_method, args1)
86
+ secondcall = s(:call, firstcall, :set_hook_handler, args2)
87
+ else
88
+ firstcall = s(:call, receiver, :hooked_method, args1)
89
+ secondcall = s(:call, firstcall, :set_hook_handler, args2)
90
+ end
91
+
92
+ s(:call, secondcall, :call, process(tree[3]))
93
+ end
94
+
95
+ def process_cdecl(tree)
96
+
97
+ const_tree = tree[1]
98
+ value_tree = tree[2]
99
+
100
+ base_class_tree = nil
101
+ const_id = nil
102
+
103
+ unless const_tree.instance_of? Symbol
104
+ if const_tree[0] == :colon2
105
+ base_class_tree = const_tree[1]
106
+ const_id = const_tree[2]
107
+ elsif const_tree[0] == :colon3
108
+ base_class_tree = s(:lit, Object)
109
+ const_id = const_tree[1]
110
+ end
111
+ else
112
+ base_class_tree = s(:lit, Object)
113
+ const_id = const_tree
114
+ end
115
+
116
+ args1 = s(:arglist, base_class_tree)
117
+ args2 = s(:arglist, s(:lit, const_id))
118
+ args3 = s(:arglist, process(value_tree))
119
+
120
+ firstcall = s(:call, hook_handler_reference, :hooked_cdecl, args1 )
121
+ secondcall = s(:call, firstcall, :set_id, args2)
122
+
123
+ s(:call, secondcall, :set_value, args3)
124
+ end
125
+
126
+ def process_dxstr(tree)
127
+ dstr_tree = tree.dup
128
+ dstr_tree[0] = :dstr
129
+
130
+ args = s(:arglist, process(dstr_tree) )
131
+ s(:call, hook_handler_reference, :hooked_xstr, args)
132
+ end
133
+
134
+ def process_xstr(tree)
135
+ args = s(:arglist, s(:lit, tree[1]) )
136
+ s(:call, hook_handler_reference, :hooked_xstr, args)
137
+ end
138
+
139
+ def process_module(tree)
140
+ module_name_tree = tree[1]
141
+ if module_name_tree.instance_of? Sexp
142
+ if module_name_tree.first == :colon3
143
+ module_name_tree = process_colon3(module_name_tree)
144
+ end
145
+ end
146
+
147
+ class_scope(nil) {
148
+ s(:module, module_name_tree, process(tree[2]))
149
+ }
150
+ end
151
+
152
+ def class_scope(class_name)
153
+ @def_scope.push(class_name)
154
+ ret = yield
155
+ @def_scope.pop
156
+ ret
157
+ end
158
+
159
+ def process_class(tree)
160
+ class_name_tree = tree[1]
161
+ if class_name_tree.instance_of? Sexp
162
+ if class_name_tree.first == :colon3
163
+ class_name_tree = process_colon3(class_name_tree)
164
+ end
165
+ end
166
+
167
+ if tree[2]
168
+ class_scope(tree[1]) do
169
+ s(:class, class_name_tree, process(tree[2]), process(tree[3]))
170
+ end
171
+ else
172
+ class_scope(tree[1]) do
173
+ s(:class, class_name_tree, nil, process(tree[3]))
174
+ end
175
+ end
176
+ end
177
+
178
+ def process_colon2(tree)
179
+ name = tree[2].to_s
180
+ args = s(:arglist, process(tree[1]), s(:str, name))
181
+ s(:call, hook_handler_reference, :hooked_colon2, args)
182
+ end
183
+
184
+ def process_colon3(tree)
185
+ if @hook_handler.base_namespace
186
+ s(:colon2, const_path_emul(@hook_handler.base_namespace.to_s), tree[1])
187
+ else
188
+ s(:colon3, tree[1])
189
+ end
190
+ end
191
+
192
+ def process_gvar(tree)
193
+ name = tree[1]
194
+
195
+ args = s(:arglist, s(:lit, name))
196
+ s(:call, hook_handler_reference, :hooked_gvar, args)
197
+ end
198
+
199
+ def process_const(tree)
200
+ name = tree[1].to_s
201
+ args = s(:arglist, s(:str, name))
202
+ s(:call, hook_handler_reference, :hooked_const, args)
203
+ end
204
+
205
+ def process_defn(tree)
206
+ @last_args = tree[2]
207
+ @last_method_name = tree[1]
208
+
209
+ s(tree[0],tree[1],tree[2],process(tree[3]))
210
+ end
211
+
212
+ def superclass_call_tree
213
+ current_class_call = nil
214
+
215
+ def_class = @def_scope.last
216
+ if def_class.instance_of? Symbol
217
+ current_class_call = s(:const, def_class)
218
+ s(:call, current_class_call, :superclass, s(:arglist))
219
+ elsif def_class.instance_of? Sexp
220
+ current_class_call = def_class
221
+ s(:call, current_class_call, :superclass, s(:arglist))
222
+ elsif def_class.instance_of? String
223
+ s(:call, s(:self), :class, s(:arglist))
224
+ else
225
+ current_class_call = s(:call, nil, :raise, s(:arglist, s(:const, :SecurityError)))
226
+ s(:call, current_class_call, :superclass, s(:arglist))
227
+ end
228
+ end
229
+
230
+ def process_super(tree)
231
+
232
+ receiver = s(:self)
233
+
234
+ args1 = s(:arglist, s(:lit, @last_method_name), s(:call, nil, :binding, s(:arglist)))
235
+ args2 = s(:arglist, hook_handler_reference)
236
+
237
+ firstcall = s(:call, receiver, :local_hooked_method, args1)
238
+ secondcall = s(:call, firstcall, :set_hook_handler, args2)
239
+ thirdcall = s(:call,secondcall,:set_class,s(:arglist, superclass_call_tree))
240
+
241
+ # pass the args passed to super
242
+ s(:call, thirdcall, :call, s(:arglist, *tree[1..-1]))
243
+
244
+ end
245
+
246
+ def process_defs(tree)
247
+ block_tree = class_scope( "" ) {
248
+ process(tree[4])
249
+ }
250
+ s(:defs, process(tree[1]), tree[2], tree[3], block_tree)
251
+ end
252
+
253
+ def process_zsuper(tree)
254
+
255
+ receiver = s(:self)
256
+
257
+ args1 = s(:arglist, s(:lit, @last_method_name), s(:call, nil, :binding, s(:arglist)))
258
+ args2 = s(:arglist, hook_handler_reference)
259
+
260
+ firstcall = s(:call, receiver, :local_hooked_method, args1)
261
+ secondcall = s(:call, firstcall, :set_hook_handler, args2)
262
+ thirdcall = s(:call,secondcall,:set_class,s(:arglist, superclass_call_tree))
263
+
264
+ # pass the args of the current defn
265
+
266
+ args = s(:arglist)
267
+ @last_args[1..-1].each do |arg_sym|
268
+ if arg_sym.to_s[0] == "*"
269
+ args << s(:splat, s(:lvar, arg_sym.to_s[1..-1].to_sym))
270
+ else
271
+ args << s(:lvar, arg_sym)
272
+ end
273
+ end
274
+
275
+ s(:call, thirdcall, :call, args)
276
+ end
277
+
278
+ end
279
+ end
data/lib/evalhook.rb CHANGED
@@ -22,7 +22,8 @@ require "partialruby"
22
22
  require "evalhook/redirect_helper"
23
23
  require "evalhook/multi_hook_handler"
24
24
  require "evalhook/hook_handler"
25
- require "evalhook/hook_context"
25
+ require "evalhook/tree_processor"
26
+
26
27
  begin
27
28
  require "evalmimic"
28
29
  $evalmimic_defined = true
@@ -182,6 +183,36 @@ module EvalHook
182
183
  end
183
184
  end
184
185
 
186
+ # used internally
187
+ def hooked_gvar(global_id)
188
+ ret = handle_gvar(global_id)
189
+ if ret
190
+ ret.value
191
+ else
192
+ eval(global_id.to_s)
193
+ end
194
+ end
195
+
196
+ # used internally
197
+ def hooked_const(name)
198
+ ret = handle_const(name)
199
+ if ret
200
+ ret.value
201
+ else
202
+ Object.const_get(name)
203
+ end
204
+ end
205
+
206
+ # used internally
207
+ def hooked_colon2(context,name)
208
+ ret = handle_colon2(context, name)
209
+ if ret
210
+ ret.value
211
+ else
212
+ context.const_get(name)
213
+ end
214
+ end
215
+
185
216
  # used internally
186
217
  def hooked_cdecl(context)
187
218
  HookCdecl.new(context,self)
@@ -217,6 +248,20 @@ module EvalHook
217
248
  nil
218
249
  end
219
250
 
251
+ # Overwrite to handle const read access
252
+ def handle_const(name)
253
+ nil
254
+ end
255
+
256
+ def handle_colon2(context,name)
257
+ nil
258
+ end
259
+
260
+ # Overwrite to handle global variable read access
261
+ def handle_gvar(global_id)
262
+ nil
263
+ end
264
+
220
265
  # used internally
221
266
  def hooked_super(*args)
222
267
  hm = caller_obj(2).hooked_method(caller_method(2))
@@ -243,7 +288,10 @@ module EvalHook
243
288
 
244
289
  tree = RubyParser.new.parse code
245
290
 
246
- context = EvalHook::HookContext.new(self)
291
+ context = PartialRuby::PureRubyContext.new
292
+
293
+ tree = EvalHook::TreeProcessor.new(self).process(tree)
294
+
247
295
  emulationcode = context.emul tree
248
296
 
249
297
  eval emulationcode, b_, name, line
@@ -23,7 +23,7 @@ describe EvalHook::HookHandler, "hook handler defaults" do
23
23
  hook_handler = EvalHook::HookHandler.new
24
24
 
25
25
  expected = eval(expr)
26
- hook_handler.evalhook(expr).should be == expected
26
+ hook_handler.evalhook(expr, binding).should be == expected
27
27
 
28
28
  end
29
29
  end
@@ -32,14 +32,14 @@ describe EvalHook::HookHandler, "hook handler defaults" do
32
32
  hook_handler = EvalHook::HookHandler.new
33
33
 
34
34
  $global_variable_test = 5
35
- hook_handler.evalhook("$global_variable_test").should be == $global_variable_test
35
+ hook_handler.evalhook("$global_variable_test", binding).should be == $global_variable_test
36
36
  end
37
37
 
38
38
  it "should allow reference to constants" do
39
39
  hook_handler = EvalHook::HookHandler.new
40
40
 
41
41
  CONSTANTTEST = 5
42
- hook_handler.evalhook("CONSTANTTEST").should be == CONSTANTTEST
42
+ hook_handler.evalhook("CONSTANTTEST", binding).should be == CONSTANTTEST
43
43
  end
44
44
 
45
45
  it "should allow reference to local variables" do
@@ -71,7 +71,7 @@ describe EvalHook::HookHandler, "hook handler defaults" do
71
71
 
72
72
  it "should allow method calls" do
73
73
  hook_handler = EvalHook::HookHandler.new
74
- hook_handler.evalhook("X.new.foo").should be X.new.foo
74
+ hook_handler.evalhook("X.new.foo", binding).should be X.new.foo
75
75
  end
76
76
 
77
77
  it "should capture method calls" do
@@ -80,14 +80,14 @@ describe EvalHook::HookHandler, "hook handler defaults" do
80
80
  hook_handler.should_receive(:handle_method).with(X.class,X,:new)
81
81
  hook_handler.should_receive(:handle_method).with(X,anything(),:foo)
82
82
 
83
- hook_handler.evalhook("X.new.foo")
83
+ hook_handler.evalhook("X.new.foo", binding)
84
84
  end
85
85
 
86
86
  it "should capture constant assignment" do
87
87
  hook_handler = EvalHook::HookHandler.new
88
88
 
89
89
  hook_handler.should_receive(:handle_cdecl).with(Object,:TEST_CONSTANT,4)
90
- hook_handler.evalhook("TEST_CONSTANT = 4")
90
+ hook_handler.evalhook("TEST_CONSTANT = 4", binding)
91
91
 
92
92
  end
93
93
 
@@ -95,7 +95,7 @@ describe EvalHook::HookHandler, "hook handler defaults" do
95
95
  hook_handler = EvalHook::HookHandler.new
96
96
 
97
97
  hook_handler.should_receive(:handle_gasgn).with(:$test_global_variable,4)
98
- hook_handler.evalhook("$test_global_variable = 4")
98
+ hook_handler.evalhook("$test_global_variable = 4", binding)
99
99
 
100
100
  end
101
101
 
@@ -103,7 +103,7 @@ describe EvalHook::HookHandler, "hook handler defaults" do
103
103
  hook_handler = EvalHook::HookHandler.new
104
104
 
105
105
  hook_handler.should_receive(:handle_xstr).with("echo test")
106
- hook_handler.evalhook("`echo test`")
106
+ hook_handler.evalhook("`echo test`", binding)
107
107
 
108
108
  end
109
109
 
@@ -111,7 +111,7 @@ describe EvalHook::HookHandler, "hook handler defaults" do
111
111
  hook_handler = EvalHook::HookHandler.new
112
112
 
113
113
  hook_handler.should_receive(:handle_xstr).with("echo test")
114
- hook_handler.evalhook("`echo \#{}test`")
114
+ hook_handler.evalhook("`echo \#{}test`", binding)
115
115
 
116
116
  end
117
117
 
@@ -119,7 +119,7 @@ describe EvalHook::HookHandler, "hook handler defaults" do
119
119
  hook_handler = EvalHook::HookHandler.new
120
120
 
121
121
  hook_handler.should_receive(:handle_xstr).with("echo test")
122
- hook_handler.evalhook("%x[echo test]")
122
+ hook_handler.evalhook("%x[echo test]", binding)
123
123
  end
124
124
 
125
125
 
@@ -139,14 +139,14 @@ describe EvalHook::HookHandler, "hook handler defaults" do
139
139
  hook_handler = EvalHook::HookHandler.new
140
140
 
141
141
  hook_handler.base_namespace = :A
142
- hook_handler.evalhook("::B").should be == A::B
142
+ hook_handler.evalhook("::B", binding).should be == A::B
143
143
  end
144
144
 
145
145
  it "should allow define base_namespace (const)" do
146
146
  hook_handler = EvalHook::HookHandler.new
147
147
 
148
148
  hook_handler.base_namespace = A
149
- hook_handler.evalhook("::B").should be == A::B
149
+ hook_handler.evalhook("::B", binding).should be == A::B
150
150
  end
151
151
 
152
152
  class C1
@@ -170,7 +170,7 @@ describe EvalHook::HookHandler, "hook handler defaults" do
170
170
  def foo
171
171
  'A1::C1#foo at evalhook'
172
172
  end
173
- end")
173
+ end" , binding)
174
174
 
175
175
  C1.new.foo.should be == "C1#foo" # C1#foo class remains unchanged
176
176
  A1::C1.new.foo.should be == "A1::C1#foo at evalhook" # A1::C1#foo changes
@@ -196,7 +196,7 @@ describe EvalHook::HookHandler, "hook handler defaults" do
196
196
  end
197
197
 
198
198
  end
199
- ")
199
+ ", binding)
200
200
 
201
201
  C2.new.foo.should be == "::C2#foo at evalhook"
202
202
  end
@@ -216,15 +216,14 @@ describe EvalHook::HookHandler, "hook handler defaults" do
216
216
  def foo
217
217
  'A1::A2::C1#foo at evalhook'
218
218
  end
219
- end")
219
+ end", binding)
220
220
 
221
221
  C1.new.foo.should be == "C1#foo" # C1#foo class remains unchanged
222
222
  A1::A2::C1.new.foo.should be == "A1::A2::C1#foo at evalhook" # A1::C1#foo changes
223
223
  end
224
224
 
225
- it "should use current binding when not specified" do
226
- a = 9
227
- EvalHook::HookHandler.new.evalhook("a").should be == 9
225
+ it "should allow declaring classes with ::" do
226
+ EvalHook::HookHandler.new.evalhook("class Fixnum::TestClass12345; end", binding)
228
227
  end
229
228
 
230
229
  module TestInheritedClassMethodError
@@ -243,5 +242,187 @@ describe EvalHook::HookHandler, "hook handler defaults" do
243
242
  hh.evalhook('TestInheritedClassMethodError::B.foo', binding)
244
243
  end
245
244
 
245
+ it "should allow declaring modules with ::" do
246
+ EvalHook::HookHandler.new.evalhook("module Fixnum::TestModule12345; end", binding)
247
+ end
248
+
249
+ module TestModule12347
250
+ end
251
+
252
+ it "should allow assignment of constants nested on modules" do
253
+ EvalHook::HookHandler.new.evalhook("TestModule12347::A = 9", binding)
254
+ end
255
+
256
+ class XTEST44
257
+ def foo(a)
258
+ a+1
259
+ end
260
+ end
261
+ it "should pass arguments on super" do
262
+ EvalHook::HookHandler.new.evalhook('
263
+ class YTEST44 < XTEST44
264
+ def foo(a)
265
+ super
266
+ end
267
+ end
268
+ YTEST44.new.foo(9)
269
+ ', binding).should be == 10
270
+ end
271
+
272
+ it "should pass arguments on super (explicitly)" do
273
+ EvalHook::HookHandler.new.evalhook('
274
+ class YTEST45 < XTEST44
275
+ def foo(a)
276
+ super(a)
277
+ end
278
+ end
279
+ YTEST45.new.foo(9)
280
+ ', binding).should be == 10
281
+ end
282
+
283
+ it "should pass arguments on super with three levels" do
284
+ EvalHook::HookHandler.new.evalhook('
285
+ class YTEST46 < XTEST44
286
+ def foo(a)
287
+ super(a)
288
+ end
289
+ end
290
+
291
+ class ZTEST46 < YTEST46
292
+ def foo(a)
293
+ super(a)
294
+ end
295
+ end
296
+ ZTEST46.new.foo(9)
297
+ ', binding).should be == 10
298
+
299
+ end
300
+
301
+ it "should raise SecurityError when use super outside a class" do
302
+
303
+ lambda {
304
+
305
+ EvalHook::HookHandler.new.evalhook('
306
+ module MODULETEST48
307
+ def foo(a)
308
+ super(a)
309
+ end
310
+ end
311
+
312
+ class XTEST48 < XTEST44
313
+ include MODULETEST48
314
+ end
315
+ XTEST48.new.foo(9)
316
+ ', binding)
317
+
318
+ }.should raise_error(SecurityError)
319
+
320
+ end
321
+
322
+ it "should raise SecurityError when use super inside a module nested on class" do
323
+
324
+ lambda {
325
+
326
+ EvalHook::HookHandler.new.evalhook('
327
+
328
+ class CLASSTEST49
329
+ module MODULETEST49
330
+ def foo(a)
331
+ super(a)
332
+ end
333
+ end
334
+ end
335
+
336
+ class YTEST49 < XTEST44
337
+ include CLASSTEST49::MODULETEST49
338
+ end
339
+ YTEST49.new.foo(9)
340
+ ', binding)
341
+
342
+ }.should raise_error(SecurityError)
343
+
344
+ end
345
+
346
+ it "should pass arguments on super with no arguments throught three levels" do
347
+ EvalHook::HookHandler.new.evalhook('
348
+ class YTEST50 < XTEST44
349
+ def foo(a)
350
+ super
351
+ end
352
+ end
353
+
354
+ class ZTEST50 < YTEST50
355
+ def foo(a)
356
+ super
357
+ end
358
+ end
359
+ ZTEST50.new.foo(9)
360
+ ', binding).should be == 10
361
+
362
+ end
363
+
364
+ it "should raise SecurityError when use super with no arguments outside a class" do
365
+
366
+ lambda {
367
+
368
+ EvalHook::HookHandler.new.evalhook('
369
+ module MODULETEST51
370
+ def foo(a)
371
+ super
372
+ end
373
+ end
374
+
375
+ class XTEST51 < XTEST44
376
+ include MODULETEST51
377
+ end
378
+ XTEST51.new.foo(9)
379
+ ', binding)
380
+
381
+ }.should raise_error(SecurityError)
382
+
383
+ end
384
+
385
+ it "should raise SecurityError when use super with no arguments inside a module nested on class" do
386
+
387
+ lambda {
388
+
389
+ EvalHook::HookHandler.new.evalhook('
390
+
391
+ class CLASSTEST52
392
+ module MODULETEST52
393
+ def foo(a)
394
+ super
395
+ end
396
+ end
397
+ end
398
+
399
+ class YTEST52 < XTEST44
400
+ include CLASSTEST52::MODULETEST52
401
+ end
402
+ YTEST52.new.foo(9)
403
+ ', binding)
404
+
405
+ }.should raise_error(SecurityError)
406
+
407
+ end
408
+
409
+ it "should execute super call from singleton methods" do
410
+ EvalHook::HookHandler.new.evalhook('
411
+
412
+ class CLASSTEST53
413
+ def bar(a)
414
+ a+1
415
+ end
416
+ end
417
+
418
+ obj = CLASSTEST53.new
419
+
420
+ def obj.bar(a)
421
+ super(a+1)
422
+ end
423
+ obj.bar(10)
424
+ ', binding).should be == 12
425
+ end
426
+
246
427
  end
247
428
 
@@ -43,4 +43,140 @@ describe EvalHook::HookHandler, "hook handler hooks" do
43
43
  TEST_CONSTANT.should be == 7
44
44
  end
45
45
 
46
+ it "should intercept constant access" do
47
+ hh = EvalHook::HookHandler.new
48
+ def hh.handle_const(name)
49
+ const_value(77)
50
+ end
51
+
52
+ TEST_CONSTANT_12345 = 8
53
+ hh.evalhook("TEST_CONSTANT_12345").should be == 77
54
+ end
55
+
56
+ it "should allow change constant values using hooking" do
57
+ hh = EvalHook::HookHandler.new
58
+ def hh.handle_colon2(context, name)
59
+ const_value(77)
60
+ end
61
+
62
+ TEST_CONSTANT_12346 = 8
63
+ hh.evalhook("Object::TEST_CONSTANT_12346").should be == 77
64
+ end
65
+
66
+ it "should allow change global variable values using hooking" do
67
+ hh = EvalHook::HookHandler.new
68
+ def hh.handle_gvar(global_id)
69
+ global_value(88)
70
+ end
71
+
72
+ $test_global_12345 = 9
73
+ hh.evalhook("$test_global_12345").should be == 88
74
+ end
75
+
76
+ it "should intercept constant access" do
77
+ hh = EvalHook::HookHandler.new
78
+
79
+ hh.should_receive(:handle_const).with("A")
80
+
81
+ hh.evalhook("A")
82
+ end
83
+
84
+ module TestModule321
85
+ end
86
+
87
+ it "should intercept nested constant access" do
88
+ hh = EvalHook::HookHandler.new
89
+
90
+ hh.should_receive(:handle_const).once.with("TestModule321")
91
+ hh.should_receive(:handle_colon2).once.with(TestModule321,"B")
92
+
93
+ hh.evalhook("TestModule321::B")
94
+ end
95
+
96
+ it "should intercept global_variable access" do
97
+ hh = EvalHook::HookHandler.new
98
+
99
+ hh.should_receive(:handle_gvar).with(:$global_variable_test)
100
+
101
+ hh.evalhook("$global_variable_test")
102
+ end
103
+
104
+
105
+ class X
106
+ def foo
107
+ end
108
+ end
109
+
110
+ class Y < X
111
+ end
112
+
113
+ it "should intercept super as a method call" do
114
+ h = EvalHook::HookHandler.new
115
+
116
+ # reference to X for inheritance
117
+ h.should_receive(:handle_const).with("X")
118
+
119
+ # reference to Y
120
+ h.should_receive(:handle_const).with("Y")
121
+
122
+ # call to new
123
+ h.should_receive(:handle_method).with(Y.class,Y,:new)
124
+
125
+ # first Y#foo call
126
+ h.should_receive(:handle_method).with(Y,anything(),:foo)
127
+
128
+ # super call
129
+ h.should_receive(:handle_method).with(X,anything(),:foo)
130
+
131
+ h.evalhook('
132
+
133
+ class Y < X
134
+ def foo
135
+ super
136
+ end
137
+ end
138
+ Y.new.foo
139
+
140
+ ', binding)
141
+ end
142
+
143
+ class X2
144
+ def foo(a)
145
+ a+1
146
+ end
147
+ end
148
+
149
+ class Y2 < X2
150
+ end
151
+
152
+ it "should intercept super with explicit arguments as a method call" do
153
+ h = EvalHook::HookHandler.new
154
+
155
+ # reference to X for inheritance
156
+ h.should_receive(:handle_const).with("X2")
157
+
158
+ # reference to Y
159
+ h.should_receive(:handle_const).with("Y2")
160
+
161
+ # call to new
162
+ h.should_receive(:handle_method).with(Y2.class,Y2,:new)
163
+
164
+ # first Y#foo call
165
+ h.should_receive(:handle_method).with(Y2,anything(),:foo)
166
+
167
+ # super call
168
+ h.should_receive(:handle_method).with(X2,anything(),:foo)
169
+
170
+ h.evalhook('
171
+
172
+ class Y2 < X2
173
+ def foo(a)
174
+ super(a)
175
+ end
176
+ end
177
+ Y2.new.foo(9)
178
+
179
+ ', binding).should be == 10
180
+ end
181
+
46
182
  end
@@ -7,6 +7,10 @@ describe EvalHook::HookHandler, "hook handler visitor" do
7
7
  def foo
8
8
 
9
9
  end
10
+
11
+ def bar(*x)
12
+
13
+ end
10
14
  end
11
15
 
12
16
  def self.visitor_it(code, name)
@@ -100,4 +104,57 @@ describe EvalHook::HookHandler, "hook handler visitor" do
100
104
 
101
105
  end
102
106
 
107
+ it "should capture inside parameters" do
108
+ x = X.new
109
+ hh = EvalHook::HookHandler.new
110
+
111
+ hh.should_receive(:handle_method).with(X,x,:foo)
112
+ hh.should_receive(:handle_method).with(X,x,:bar)
113
+
114
+ c = nil
115
+ hh.evalhook("
116
+ x.bar(x.foo)
117
+ ", binding
118
+ )
119
+ end
120
+
121
+ it "should capture in global assignment" do
122
+ x = X.new
123
+ hh = EvalHook::HookHandler.new
124
+
125
+ hh.should_receive(:handle_method).with(X,x,:foo)
126
+
127
+ c = nil
128
+ hh.evalhook("
129
+ $a = x.foo
130
+ ", binding
131
+ )
132
+ end
133
+
134
+ it "should capture in constant assignment" do
135
+ x = X.new
136
+ hh = EvalHook::HookHandler.new
137
+
138
+ hh.should_receive(:handle_method).with(X,x,:foo)
139
+
140
+ c = nil
141
+ hh.evalhook("
142
+ TESTCONSTANTA = x.foo
143
+ ", binding
144
+ )
145
+ end
146
+
147
+ it "should capture in dxstr system calls" do
148
+ x = X.new
149
+ hh = EvalHook::HookHandler.new
150
+
151
+ hh.should_receive(:handle_method).with(X,x,:foo)
152
+
153
+ c = nil
154
+ hh.evalhook("
155
+ `\#{x.foo}`
156
+ ", binding
157
+ )
158
+ end
159
+
103
160
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evalhook
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 15
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 3
9
- - 1
10
- version: 0.3.1
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Dario Seminara
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-04-10 00:00:00 -03:00
18
+ date: 2011-04-28 00:00:00 -03:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -26,12 +26,12 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- hash: 27
29
+ hash: 23
30
30
  segments:
31
31
  - 0
32
- - 1
32
+ - 2
33
33
  - 0
34
- version: 0.1.0
34
+ version: 0.2.0
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency
@@ -65,9 +65,9 @@ files:
65
65
  - examples/example4.rb
66
66
  - examples/example2.rb
67
67
  - lib/evalhook/multi_hook_handler.rb
68
+ - lib/evalhook/tree_processor.rb
68
69
  - lib/evalhook/hook_handler.rb
69
70
  - lib/evalhook/redirect_helper.rb
70
- - lib/evalhook/hook_context.rb
71
71
  - lib/evalhook.rb
72
72
  - spec/hook_handler/hook_handler_arguments_spec.rb
73
73
  - spec/hook_handler/hook_handler_hook_spec.rb
@@ -1,144 +0,0 @@
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 "ruby_parser"
22
- require "partialruby"
23
-
24
- module EvalHook
25
- class HookContext < PartialRuby::PureRubyContext
26
-
27
- def initialize(hook_handler)
28
- @hook_handler = hook_handler
29
- end
30
-
31
- def const_path_emul(code)
32
- if code.instance_of? Array
33
- if (code.size == 1)
34
- s(:const, code[-1].to_sym)
35
- else
36
- s(:colon2, const_path_emul(code[0..-2]), code[-1].to_sym)
37
- end
38
- else
39
- const_path_emul code.split("::")
40
- end
41
- end
42
-
43
- def ruby_emul_colon3(tree)
44
- if @hook_handler.base_namespace
45
- emul s(:colon2, const_path_emul(@hook_handler.base_namespace.to_s), tree[1])
46
- else
47
- super tree
48
- end
49
- end
50
-
51
- def ruby_emul_call(tree)
52
-
53
- method_name = tree[2]
54
-
55
- if tree.respond_to?(:is_marked?)
56
- return super(tree)
57
- end
58
-
59
- args1 = s(:arglist, s(:lit, method_name), marked(s(:call, nil, :binding, s(:arglist))))
60
-
61
- args2 = s(:arglist, s(:lit, @hook_handler))
62
-
63
- receiver = tree[1] || s(:self)
64
-
65
- firstcall = nil
66
- secondcall = nil
67
-
68
- if tree[1]
69
- firstcall = marked(s(:call, receiver, :local_hooked_method, args1))
70
- secondcall = marked(s(:call, firstcall, :set_hook_handler, args2))
71
- else
72
- firstcall = marked(s(:call, receiver, :hooked_method, args1))
73
- secondcall = marked(s(:call, firstcall, :set_hook_handler, args2))
74
- end
75
-
76
- super marked(s(:call, secondcall, :call, tree[3]))
77
- end
78
-
79
- def ruby_emul_dxstr(tree)
80
-
81
- dstr_tree = tree.dup
82
- dstr_tree[0] = :dstr
83
-
84
- args = s(:arglist, dstr_tree )
85
-
86
- emul marked(s(:call, s(:lit, @hook_handler), :hooked_xstr, args))
87
- end
88
-
89
- def ruby_emul_xstr(tree)
90
- args = s(:arglist, s(:lit, tree[1]) )
91
-
92
- emul marked(s(:call, s(:lit, @hook_handler), :hooked_xstr, args))
93
- end
94
-
95
- def ruby_emul_cdecl(tree)
96
- const_tree = tree[1]
97
- value_tree = tree[2]
98
-
99
- base_class_tree = nil
100
- const_id = nil
101
-
102
- unless const_tree.instance_of? Symbol
103
- if const_tree[0] == :colon2
104
- base_class_tree = const_tree[1]
105
- const_id = const_tree[2]
106
- elsif const_tree[0] == :colon3
107
- base_class_tree = s(:lit, Object)
108
- const_id = const_tree[1]
109
- end
110
- else
111
- base_class_tree = s(:lit, Object)
112
- const_id = const_tree
113
- end
114
-
115
- args1 = s(:arglist, base_class_tree)
116
- args2 = s(:arglist, s(:lit, const_id))
117
- args3 = s(:arglist, value_tree)
118
-
119
- firstcall = marked(s(:call, s(:lit, @hook_handler), :hooked_cdecl, args1 ))
120
- secondcall = marked(s(:call, firstcall, :set_id, args2))
121
- thirdcall = marked(s(:call, secondcall, :set_value, args3))
122
-
123
- emul thirdcall
124
- end
125
-
126
- def ruby_emul_gasgn(tree)
127
- args1 = s(:arglist, s(:lit, tree[1]))
128
- args2 = s(:arglist, tree[2] )
129
-
130
- firstcall = marked(s(:call, s(:lit, @hook_handler), :hooked_gasgn, args1))
131
- secondcall = marked(s(:call, firstcall, :set_value, args2))
132
- emul secondcall
133
- end
134
-
135
- private
136
-
137
- def marked(ast)
138
- def ast.is_marked?
139
- true
140
- end
141
- ast
142
- end
143
- end
144
- end