evalhook 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/Rakefile +4 -4
- data/lib/evalhook/hook_context.rb +144 -0
- data/lib/evalhook/hook_handler.rb +35 -0
- data/lib/evalhook/multi_hook_handler.rb +1 -1
- data/lib/evalhook.rb +44 -30
- data/spec/hook_handler/hook_handler_arguments_spec.rb +15 -0
- data/spec/hook_handler/hook_handler_defaults_spec.rb +230 -0
- data/spec/hook_handler/hook_handler_hook_spec.rb +46 -0
- data/spec/hook_handler/hook_handler_multiple_redirect_spec.rb +67 -0
- data/spec/hook_handler/hook_handler_multiple_spec.rb +99 -0
- data/spec/hook_handler/hook_handler_ruby_spec.rb +47 -0
- data/spec/hook_handler/hook_handler_visitor_spec.rb +103 -0
- data/spec/validation/hook_handler_spec.rb +19 -0
- metadata +33 -9
- data/ext/evalhook_base/evalhook_base.c +0 -795
- data/ext/evalhook_base/extconf.rb +0 -7
data/CHANGELOG
CHANGED
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.
|
9
|
+
s.version = '0.3.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 "
|
15
|
+
s.add_dependency "partialruby", ">= 0.1.0"
|
16
|
+
s.add_dependency "ruby_parser", ">= 2.0.6"
|
16
17
|
s.has_rdoc = true
|
17
18
|
s.extra_rdoc_files = [ 'README' ]
|
18
19
|
s.rdoc_options << '--main' << 'README'
|
19
|
-
s.
|
20
|
-
s.files = Dir.glob("{examples,lib,test}/**/*.rb") + Dir.glob("ext/**/*.c") + Dir.glob("ext/**/*.h") + Dir.glob("ext/**/extconf.rb") +
|
20
|
+
s.files = Dir.glob("{examples,lib,spec}/**/*.rb") + Dir.glob("ext/**/*.c") + Dir.glob("ext/**/*.h") + Dir.glob("ext/**/extconf.rb") +
|
21
21
|
[ 'LICENSE', 'AUTHORS', 'CHANGELOG', 'README', 'Rakefile', 'TODO' ]
|
22
22
|
end
|
23
23
|
|
@@ -0,0 +1,144 @@
|
|
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
|
@@ -0,0 +1,35 @@
|
|
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
|
+
|
23
|
+
module EvalHook
|
24
|
+
def self.validate_syntax(code)
|
25
|
+
begin
|
26
|
+
RubyParser.new.parse(code)
|
27
|
+
rescue
|
28
|
+
raise SyntaxError
|
29
|
+
end
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
class HookHandler
|
34
|
+
end
|
35
|
+
end
|
data/lib/evalhook.rb
CHANGED
@@ -18,19 +18,25 @@ you should have received a copy of the gnu general public license
|
|
18
18
|
along with evalhook. if not, see <http://www.gnu.org/licenses/>.
|
19
19
|
|
20
20
|
=end
|
21
|
-
require "
|
22
|
-
require "evalhook_base"
|
21
|
+
require "partialruby"
|
23
22
|
require "evalhook/redirect_helper"
|
24
23
|
require "evalhook/multi_hook_handler"
|
24
|
+
require "evalhook/hook_handler"
|
25
|
+
require "evalhook/hook_context"
|
26
|
+
begin
|
25
27
|
require "evalmimic"
|
28
|
+
$evalmimic_defined = true
|
29
|
+
rescue LoadError
|
30
|
+
$evalmimic_defined = false
|
31
|
+
end
|
26
32
|
|
27
33
|
|
28
34
|
class Object
|
29
|
-
def local_hooked_method(mname)
|
30
|
-
EvalHook::HookedMethod.new(self,mname,true)
|
35
|
+
def local_hooked_method(mname,_binding)
|
36
|
+
EvalHook::HookedMethod.new(self,mname,true,_binding)
|
31
37
|
end
|
32
|
-
def hooked_method(mname)
|
33
|
-
EvalHook::HookedMethod.new(self,mname,false)
|
38
|
+
def hooked_method(mname,_binding)
|
39
|
+
EvalHook::HookedMethod.new(self,mname,false,_binding)
|
34
40
|
end
|
35
41
|
end
|
36
42
|
|
@@ -39,10 +45,11 @@ module EvalHook
|
|
39
45
|
# used internally
|
40
46
|
class HookedMethod
|
41
47
|
|
42
|
-
def initialize(recv, m,localcall)
|
48
|
+
def initialize(recv, m,localcall,_binding)
|
43
49
|
@recv = recv
|
44
50
|
@m = m
|
45
51
|
@localcall = localcall
|
52
|
+
@_binding = _binding
|
46
53
|
end
|
47
54
|
|
48
55
|
# used internally
|
@@ -62,8 +69,15 @@ module EvalHook
|
|
62
69
|
method_handler = @method_handler
|
63
70
|
ret = nil
|
64
71
|
|
65
|
-
klass = @klass || @recv.method(@m).owner
|
66
72
|
method_name = @m
|
73
|
+
if args.length == 0
|
74
|
+
local_vars = @_binding.eval("local_variables").map(&:to_s)
|
75
|
+
if local_vars.include? method_name.to_s
|
76
|
+
return @_binding.eval(method_name.to_s)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
klass = @klass || @recv.method(@m).owner
|
67
81
|
recv = @recv
|
68
82
|
|
69
83
|
if method_handler
|
@@ -215,35 +229,35 @@ module EvalHook
|
|
215
229
|
runstr = handle_xstr(str) || str
|
216
230
|
end
|
217
231
|
|
218
|
-
|
232
|
+
def evalhook_i(code, b_ = nil, name = "(eval)", line = 1)
|
233
|
+
|
234
|
+
EvalHook.validate_syntax code
|
235
|
+
|
236
|
+
tree = RubyParser.new.parse code
|
237
|
+
|
238
|
+
context = EvalHook::HookContext.new(self)
|
239
|
+
emulationcode = context.emul tree
|
240
|
+
|
241
|
+
eval emulationcode, b_, name, line
|
219
242
|
|
220
|
-
# used internally
|
221
|
-
def internal_eval(b_, original_args)
|
222
|
-
raise ArgumentError if original_args.size == 0
|
223
|
-
evalhook_i(original_args[0], original_args[1] || b_, original_args[2] || "(eval)", original_args[3] || 0)
|
224
243
|
end
|
225
244
|
|
226
|
-
|
245
|
+
if ($evalmimic_defined)
|
227
246
|
|
228
|
-
|
247
|
+
define_eval_method :evalhook
|
229
248
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
#{code}
|
236
|
-
end
|
237
|
-
EvalHook::FakeEvalHook
|
238
|
-
else
|
239
|
-
EvalHook
|
240
|
-
end ).hook_block(ObjectSpace._id2ref(#{object_id}))
|
241
|
-
end
|
242
|
-
retvalue
|
243
|
-
"
|
244
|
-
eval(code, b_, name, line)
|
249
|
+
# used internally
|
250
|
+
def internal_eval(b_, original_args)
|
251
|
+
raise ArgumentError if original_args.size == 0
|
252
|
+
evalhook_i(original_args[0], original_args[1] || b_, original_args[2] || "(eval)", original_args[3] || 0)
|
253
|
+
end
|
245
254
|
|
255
|
+
else
|
256
|
+
def evalhook(*args)
|
257
|
+
evalhook_i(*args)
|
258
|
+
end
|
246
259
|
end
|
260
|
+
|
247
261
|
end
|
248
262
|
|
249
263
|
module ModuleMethods
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "evalhook"
|
3
|
+
|
4
|
+
describe EvalHook::HookHandler, "hook handler" do
|
5
|
+
|
6
|
+
it "should accept specified source name" do
|
7
|
+
hh = EvalHook::HookHandler.new
|
8
|
+
|
9
|
+
begin
|
10
|
+
hh.evalhook("raise '0'",binding, "sourcename", 1)
|
11
|
+
rescue Exception => e
|
12
|
+
e.backtrace.join.include? ("sourcename").should be == true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "evalhook"
|
3
|
+
|
4
|
+
class NilClass
|
5
|
+
def strip
|
6
|
+
""
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe EvalHook::HookHandler, "hook handler defaults" do
|
11
|
+
it "should throw exception when call evalhook with no parameters" do
|
12
|
+
hook_handler = EvalHook::HookHandler.new
|
13
|
+
|
14
|
+
lambda {
|
15
|
+
hook_handler.evalhook
|
16
|
+
}.should raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
|
19
|
+
exprlist = ["1+1", "[1,2,3].map{|x| x*2}"]
|
20
|
+
|
21
|
+
exprlist.each do |expr|
|
22
|
+
it "should eval expresion '#{expr}' same as eval" do
|
23
|
+
hook_handler = EvalHook::HookHandler.new
|
24
|
+
|
25
|
+
expected = eval(expr)
|
26
|
+
hook_handler.evalhook(expr).should be == expected
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should allow reference to global variables" do
|
32
|
+
hook_handler = EvalHook::HookHandler.new
|
33
|
+
|
34
|
+
$global_variable_test = 5
|
35
|
+
hook_handler.evalhook("$global_variable_test").should be == $global_variable_test
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should allow reference to constants" do
|
39
|
+
hook_handler = EvalHook::HookHandler.new
|
40
|
+
|
41
|
+
CONSTANTTEST = 5
|
42
|
+
hook_handler.evalhook("CONSTANTTEST").should be == CONSTANTTEST
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allow reference to local variables" do
|
46
|
+
hook_handler = EvalHook::HookHandler.new
|
47
|
+
|
48
|
+
a = 5
|
49
|
+
hook_handler.evalhook("a", binding).should be == a
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
class N
|
54
|
+
def foo(hook_handler)
|
55
|
+
@a = 5
|
56
|
+
hook_handler.evalhook("@a", binding)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
it "should allow reference to instance variables" do
|
62
|
+
hook_handler = EvalHook::HookHandler.new
|
63
|
+
N.new.foo(hook_handler).should be == 5
|
64
|
+
end
|
65
|
+
|
66
|
+
class X
|
67
|
+
def foo
|
68
|
+
3
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should allow method calls" do
|
73
|
+
hook_handler = EvalHook::HookHandler.new
|
74
|
+
hook_handler.evalhook("X.new.foo").should be X.new.foo
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should capture method calls" do
|
78
|
+
hook_handler = EvalHook::HookHandler.new
|
79
|
+
|
80
|
+
hook_handler.should_receive(:handle_method).with(X.class,X,:new)
|
81
|
+
hook_handler.should_receive(:handle_method).with(X,anything(),:foo)
|
82
|
+
|
83
|
+
hook_handler.evalhook("X.new.foo")
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should capture constant assignment" do
|
87
|
+
hook_handler = EvalHook::HookHandler.new
|
88
|
+
|
89
|
+
hook_handler.should_receive(:handle_cdecl).with(Object,:TEST_CONSTANT,4)
|
90
|
+
hook_handler.evalhook("TEST_CONSTANT = 4")
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should capture global assignment" do
|
95
|
+
hook_handler = EvalHook::HookHandler.new
|
96
|
+
|
97
|
+
hook_handler.should_receive(:handle_gasgn).with(:$test_global_variable,4)
|
98
|
+
hook_handler.evalhook("$test_global_variable = 4")
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should capture system exec with backsticks" do
|
103
|
+
hook_handler = EvalHook::HookHandler.new
|
104
|
+
|
105
|
+
hook_handler.should_receive(:handle_xstr).with("echo test")
|
106
|
+
hook_handler.evalhook("`echo test`")
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should capture system exec with backsticks and dynamic strings" do
|
111
|
+
hook_handler = EvalHook::HookHandler.new
|
112
|
+
|
113
|
+
hook_handler.should_receive(:handle_xstr).with("echo test")
|
114
|
+
hook_handler.evalhook("`echo \#{}test`")
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should capture system exec with %x" do
|
119
|
+
hook_handler = EvalHook::HookHandler.new
|
120
|
+
|
121
|
+
hook_handler.should_receive(:handle_xstr).with("echo test")
|
122
|
+
hook_handler.evalhook("%x[echo test]")
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
module B
|
127
|
+
|
128
|
+
end
|
129
|
+
module A
|
130
|
+
module B
|
131
|
+
class C
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should allow define base_namespace" do
|
139
|
+
hook_handler = EvalHook::HookHandler.new
|
140
|
+
|
141
|
+
hook_handler.base_namespace = :A
|
142
|
+
hook_handler.evalhook("::B").should be == A::B
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should allow define base_namespace (const)" do
|
146
|
+
hook_handler = EvalHook::HookHandler.new
|
147
|
+
|
148
|
+
hook_handler.base_namespace = A
|
149
|
+
hook_handler.evalhook("::B").should be == A::B
|
150
|
+
end
|
151
|
+
|
152
|
+
class C1
|
153
|
+
def foo
|
154
|
+
"C1#foo"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
module A1
|
158
|
+
class C1
|
159
|
+
def foo
|
160
|
+
"A1::C1#foo"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should allow define base_namespace (class)" do
|
166
|
+
hook_handler = EvalHook::HookHandler.new
|
167
|
+
|
168
|
+
hook_handler.base_namespace = A1
|
169
|
+
hook_handler.evalhook("class ::C1
|
170
|
+
def foo
|
171
|
+
'A1::C1#foo at evalhook'
|
172
|
+
end
|
173
|
+
end")
|
174
|
+
|
175
|
+
C1.new.foo.should be == "C1#foo" # C1#foo class remains unchanged
|
176
|
+
A1::C1.new.foo.should be == "A1::C1#foo at evalhook" # A1::C1#foo changes
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
class C2
|
181
|
+
def foo
|
182
|
+
"C2#foo"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should default base_namespace to Object" do
|
187
|
+
hook_handler = EvalHook::HookHandler.new
|
188
|
+
|
189
|
+
hook_handler.evalhook("
|
190
|
+
|
191
|
+
module Z
|
192
|
+
class ::C2
|
193
|
+
def foo
|
194
|
+
'::C2#foo at evalhook'
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
")
|
200
|
+
|
201
|
+
C2.new.foo.should be == "::C2#foo at evalhook"
|
202
|
+
end
|
203
|
+
|
204
|
+
module A1
|
205
|
+
module A2
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
it "should allow define base_namespace (3 levels)" do
|
212
|
+
hook_handler = EvalHook::HookHandler.new
|
213
|
+
|
214
|
+
hook_handler.base_namespace = A1::A2
|
215
|
+
hook_handler.evalhook("class ::C1
|
216
|
+
def foo
|
217
|
+
'A1::A2::C1#foo at evalhook'
|
218
|
+
end
|
219
|
+
end")
|
220
|
+
|
221
|
+
C1.new.foo.should be == "C1#foo" # C1#foo class remains unchanged
|
222
|
+
A1::A2::C1.new.foo.should be == "A1::A2::C1#foo at evalhook" # A1::C1#foo changes
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should use current binding when not specified" do
|
226
|
+
a = 9
|
227
|
+
EvalHook::HookHandler.new.evalhook("a").should be == 9
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "evalhook"
|
3
|
+
|
4
|
+
describe EvalHook::HookHandler, "hook handler hooks" do
|
5
|
+
|
6
|
+
class X2
|
7
|
+
def foo
|
8
|
+
9
|
9
|
+
end
|
10
|
+
|
11
|
+
def bar
|
12
|
+
4
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should intercept and change method calls" do
|
17
|
+
hh = EvalHook::HookHandler.new
|
18
|
+
def hh.handle_method(klass, recv, method_name)
|
19
|
+
redirect_method(klass, recv, :bar)
|
20
|
+
end
|
21
|
+
|
22
|
+
x = X2.new
|
23
|
+
hh.evalhook("x.foo", binding).should be == 4
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should intercept and change global variable assignments" do
|
27
|
+
hh = EvalHook::HookHandler.new
|
28
|
+
def hh.handle_gasgn(global_id, value)
|
29
|
+
redirect_gasgn(global_id, 7)
|
30
|
+
end
|
31
|
+
|
32
|
+
hh.evalhook("$test_global_variable = 9")
|
33
|
+
$test_global_variable.should be == 7
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should intercept and change constant assignments" do
|
37
|
+
hh = EvalHook::HookHandler.new
|
38
|
+
def hh.handle_cdecl(context, name, value)
|
39
|
+
redirect_cdecl(context,name,7)
|
40
|
+
end
|
41
|
+
|
42
|
+
hh.evalhook("TEST_CONSTANT = 9")
|
43
|
+
TEST_CONSTANT.should be == 7
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "evalhook"
|
3
|
+
|
4
|
+
describe EvalHook::MultiHookHandler, "multiple hook handler redirect" do
|
5
|
+
|
6
|
+
class X
|
7
|
+
def self.foo
|
8
|
+
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
handles = [
|
13
|
+
[:handle_method, "X.foo"],
|
14
|
+
[:handle_cdecl, "TEST_CONSTANT = 4"],
|
15
|
+
[:handle_gasgn, "$test_global_variable = 4"] ]
|
16
|
+
|
17
|
+
handles.each do |handle|
|
18
|
+
it "should recall #{handle[0]} in correct order" do
|
19
|
+
hook_handler = EvalHook::MultiHookHandler.new
|
20
|
+
|
21
|
+
nested_handler_1 = EvalHook::HookHandler.new
|
22
|
+
nested_handler_2 = EvalHook::HookHandler.new
|
23
|
+
hook_handler.add nested_handler_1
|
24
|
+
hook_handler.add nested_handler_2
|
25
|
+
|
26
|
+
nested_handler_1.should_receive(handle[0])
|
27
|
+
nested_handler_2.should_receive(handle[0])
|
28
|
+
|
29
|
+
hook_handler.evalhook(handle[1])
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class X_
|
34
|
+
def foo
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def bar
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
it "should handle redirects on recall of handle_method" do
|
45
|
+
hook_handler = EvalHook::MultiHookHandler.new
|
46
|
+
|
47
|
+
nested_handler_1 = EvalHook::HookHandler.new
|
48
|
+
nested_handler_2 = EvalHook::HookHandler.new
|
49
|
+
hook_handler.add nested_handler_1
|
50
|
+
hook_handler.add nested_handler_2
|
51
|
+
|
52
|
+
x = X_.new
|
53
|
+
|
54
|
+
def nested_handler_1.handle_method(klass, recv, m )
|
55
|
+
RedirectHelper::Redirect.new(klass, recv, :bar)
|
56
|
+
end
|
57
|
+
|
58
|
+
nested_handler_2.should_receive(:handle_method).
|
59
|
+
with(X_, x, :bar).
|
60
|
+
and_return(nil)
|
61
|
+
|
62
|
+
hook_handler.evalhook("x.foo", binding)
|
63
|
+
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|