rubybreaker 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS +3 -0
- data/README.md +1 -2
- data/Rakefile +1 -0
- data/TODO +0 -8
- data/TUTORIAL.md +33 -7
- data/VERSION +1 -1
- data/lib/rubybreaker.rb +12 -4
- data/lib/rubybreaker/debug/error.rb +1 -1
- data/lib/rubybreaker/runtime.rb +16 -12
- data/lib/rubybreaker/runtime/monitor.rb +116 -84
- data/lib/rubybreaker/runtime/pluggable.rb +8 -2
- data/lib/rubybreaker/runtime/type_system.rb +77 -10
- data/lib/rubybreaker/task.rb +6 -0
- data/lib/rubybreaker/util.rb +23 -0
- data/test/integrated/tc_checking.rb +71 -0
- data/test/integrated/tc_class_methods.rb +14 -4
- data/test/testtask/sample.rb +1 -0
- data/test/testtask/tc_testtask.rb +14 -2
- data/test/ts_integrated.rb +1 -0
- data/webpage/index.html +1 -2
- data/webpage/rdoc/Object.html +0 -2
- data/webpage/rdoc/Rake.html +0 -2
- data/webpage/rdoc/Rake/RubyBreakerTestTask.html +19 -5
- data/webpage/rdoc/RubyBreaker.html +35 -3
- data/webpage/rdoc/RubyBreaker/Breakable.html +1 -3
- data/webpage/rdoc/RubyBreaker/Broken.html +1 -3
- data/webpage/rdoc/RubyBreaker/Context.html +0 -2
- data/webpage/rdoc/RubyBreaker/Errors.html +0 -2
- data/webpage/rdoc/RubyBreaker/Errors/InternalError.html +0 -2
- data/webpage/rdoc/RubyBreaker/Errors/InvalidSubtypeCheck.html +0 -2
- data/webpage/rdoc/RubyBreaker/Errors/InvalidTypeConstruction.html +0 -2
- data/webpage/rdoc/RubyBreaker/Errors/SubtypeFailure.html +0 -2
- data/webpage/rdoc/RubyBreaker/Errors/TypeError.html +0 -2
- data/webpage/rdoc/RubyBreaker/Errors/UserError.html +2 -4
- data/webpage/rdoc/RubyBreaker/ObjectPosition.html +0 -2
- data/webpage/rdoc/RubyBreaker/Position.html +0 -2
- data/webpage/rdoc/RubyBreaker/RDocSupport.html +0 -2
- data/webpage/rdoc/RubyBreaker/RubyTypeUtils.html +0 -2
- data/webpage/rdoc/RubyBreaker/Runtime.html +39 -6
- data/webpage/rdoc/RubyBreaker/Runtime/Inspector.html +0 -2
- data/webpage/rdoc/RubyBreaker/Runtime/MethodInfo.html +0 -2
- data/webpage/rdoc/RubyBreaker/Runtime/Monitor.html +295 -28
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorInstaller.html +30 -43
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorSwitch.html +4 -6
- data/webpage/rdoc/RubyBreaker/Runtime/ObjectWrapper.html +0 -2
- data/webpage/rdoc/RubyBreaker/Runtime/Pluggable.html +78 -18
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSigParser.html +0 -2
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSigUnparser.html +0 -2
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSystem.html +172 -28
- data/webpage/rdoc/RubyBreaker/TypeComparer.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/AnyType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/BlockType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/DuckType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/FusionType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/MethodListType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/MethodType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/NilType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/NominalType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/OptionalType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/OrType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/SelfType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/Type.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeDefs/VarLengthType.html +0 -2
- data/webpage/rdoc/RubyBreaker/TypeUnparser.html +0 -2
- data/webpage/rdoc/RubyBreaker/Typing.html +0 -2
- data/webpage/rdoc/RubyBreaker/Util.html +80 -4
- data/webpage/rdoc/Test.html +0 -2
- data/webpage/rdoc/Test/Unit.html +0 -2
- data/webpage/rdoc/created.rid +12 -12
- data/webpage/rdoc/index.html +0 -2
- data/webpage/rdoc/js/search_index.js +1 -1
- data/webpage/rdoc/table_of_contents.html +53 -34
- data/webpage/tutorial.html +36 -7
- metadata +8 -11
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorUtils.html +0 -402
data/NEWS
CHANGED
data/README.md
CHANGED
@@ -19,14 +19,13 @@ and effectively.
|
|
19
19
|
Currently, RubyBreaker *cannot*
|
20
20
|
|
21
21
|
* Auto-document block arguments (inherent)
|
22
|
-
* Perform early dynamic type checks
|
23
22
|
* Support parametric polymorphic types
|
24
23
|
* Support RDoc or YARD output format
|
25
24
|
|
26
25
|
To contribute to the project, visit RubyBreaker's
|
27
26
|
[GitHub page](http://github.com/rockalizer/rubybreaker) and
|
28
27
|
[RubyGems page](http://rubygems.org/gems/rubybreaker). The web version of
|
29
|
-
this document can be found
|
28
|
+
this document and the tutorial can be found
|
30
29
|
[here](http://rockalizer.webfactional.com/projects/rubybreaker).
|
31
30
|
|
32
31
|
## Requirements
|
data/Rakefile
CHANGED
data/TODO
CHANGED
@@ -1,11 +1,3 @@
|
|
1
|
-
# Dynamic Type Checker
|
2
|
-
|
3
|
-
Type documentation generated by RubyBreaker can be more useful if the types
|
4
|
-
are actually enforced during runtime. In order to do this, each method has
|
5
|
-
to be dynamically instrumented (as "breakable" methods are) and inspected
|
6
|
-
for each argument type and return type. Although this is still a dynamic
|
7
|
-
type checking, it finds type errors earlier in the program execution.
|
8
|
-
|
9
1
|
# Additional Testing Frameworks
|
10
2
|
|
11
3
|
RubyBreaker supports the built-in testing framework and RSpec. There are
|
data/TUTORIAL.md
CHANGED
@@ -19,7 +19,7 @@ to use RubyBreaker in a Rakefile later.
|
|
19
19
|
|
20
20
|
The above command runs RubyBreaker in verbose mode (`-v`) and will display
|
21
21
|
the output on the screen (`-s`). Before RubyBreaker runs `prog.rb`, it will
|
22
|
-
import (`-l`) `lib.rb` and
|
22
|
+
import (`-l`) `lib.rb` and _break_ (`-b`) classes `A` and `B`.
|
23
23
|
Here is `lib.rb`:
|
24
24
|
|
25
25
|
class A
|
@@ -70,7 +70,7 @@ obtain precise and accurate type information.
|
|
70
70
|
### Using Ruby Unit Testing Framework
|
71
71
|
|
72
72
|
Instead of manually inserting the entry point indicator in the source
|
73
|
-
program, you can take advantage of Ruby
|
73
|
+
program, you can take advantage of Ruby’s built-in testing framework. This
|
74
74
|
is preferred to modifying the source program directly, especially for the
|
75
75
|
long term program maintainability. But no worries! This method is as simple
|
76
76
|
as the previous one.
|
@@ -85,7 +85,7 @@ as the previous one.
|
|
85
85
|
# ...tests!...
|
86
86
|
end
|
87
87
|
|
88
|
-
That
|
88
|
+
That’s it! The only requirements are to indicate to RubyBreaker which modules
|
89
89
|
and classes to "break" and to place `require rubybreaker` _after_
|
90
90
|
`require test/unit`.
|
91
91
|
|
@@ -124,7 +124,7 @@ RubyBreaker. The following code snippet describes how it can be done:
|
|
124
124
|
Note that `RubyBrakerTestTask` can simply replace your `TestTask` block in
|
125
125
|
Rakefile. In fact, the former is a subclass of the latter and includes all
|
126
126
|
features supported by the latter. The only additional options are
|
127
|
-
`rubybreaker_opts` which is RubyBreaker
|
127
|
+
`rubybreaker_opts` which is RubyBreaker command-line options and
|
128
128
|
`break` which specifies which modules and classes to monitor. Since
|
129
129
|
`Class1` and `Class2` are not _recognized_ by this Rakefile, you must use
|
130
130
|
string literals to specify modules and classes (and with full namespace).
|
@@ -184,7 +184,7 @@ precise return type.
|
|
184
184
|
|
185
185
|
### Duck Type
|
186
186
|
|
187
|
-
This type is inspired by the Ruby Language
|
187
|
+
This type is inspired by the Ruby Language’s duck typing, _"if it
|
188
188
|
walks like a duck and quacks like a duck, it must be a duck."_ Using this
|
189
189
|
type, an object can be represented simply by a list of method names. For
|
190
190
|
example `[walks, quacks]` is an object that has `walks` and `quacks`
|
@@ -231,7 +231,7 @@ suffices, `?` and `*`, respectively.
|
|
231
231
|
|
232
232
|
### Block Type
|
233
233
|
|
234
|
-
One of the Ruby
|
234
|
+
One of the Ruby’s prominent features is the block argument. It allows
|
235
235
|
the caller to pass in a piece of code to be executed inside the callee. This
|
236
236
|
code block can be executed by the Ruby construct, `yield`, or by directly
|
237
237
|
calling the `call` method of the block object. In RubyBreaker, this type can
|
@@ -263,7 +263,7 @@ code:
|
|
263
263
|
end
|
264
264
|
|
265
265
|
There is no way to document the type of `foo` without using a method list
|
266
|
-
type. Let
|
266
|
+
type. Let’s try to give a method type to `foo` without a method list. The
|
267
267
|
closest we can come up with would be `foo(fixnum or string) -> fixnum and
|
268
268
|
string`. But RubyBreaker does not have the "and" type in the type annotation
|
269
269
|
language because it gives me an headache! (By the way, it needs to be an
|
@@ -289,3 +289,29 @@ compatibility between the return types and "promote" the method type to a
|
|
289
289
|
method list type by spliting the type signature into two (or more in
|
290
290
|
subsequent "promotions").
|
291
291
|
|
292
|
+
### Early Dynamic Type Checking
|
293
|
+
|
294
|
+
RubyBreaker gives you an option to run the early dynamic type check. If a
|
295
|
+
method is documented and its module is specified to be _type checked_, then
|
296
|
+
RubyBreaker will verify if the argument types and the return type are
|
297
|
+
appropriate at runtime. To allow this feature in command-line, use `-c`
|
298
|
+
(checking) option:
|
299
|
+
|
300
|
+
rubybreaker -l lib.rb -c A prog.rb
|
301
|
+
|
302
|
+
Or use it in Rakefile:
|
303
|
+
|
304
|
+
require "rubybreaker/task"
|
305
|
+
...
|
306
|
+
desc "Run RubyBreaker"
|
307
|
+
Rake::RubyBreakerTestTask.new(:"rubybreaker") do |t|
|
308
|
+
t.libs << "lib"
|
309
|
+
t.test_files = ["test/foo/tc_foo1.rb"]
|
310
|
+
# ...Other test task options..
|
311
|
+
t.rubybreaker_opts << "-v" # run in verbose mode
|
312
|
+
t.break = ["Class1", "Class2", ...] # specify classes to monitor
|
313
|
+
t.check = ["Class3", ....] # specify classes to check
|
314
|
+
end
|
315
|
+
|
316
|
+
Alternatively, you can use `RubyBreaker.check()` to specify classes to type
|
317
|
+
check in the setup code of the test case.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.7
|
data/lib/rubybreaker.rb
CHANGED
@@ -25,10 +25,11 @@ module RubyBreaker
|
|
25
25
|
:append => false, # append to the input file (if there is)?
|
26
26
|
:stdout => false, # also display on the screen?
|
27
27
|
:verbose => false, # in RubyBreaker.verbose mode?
|
28
|
-
:save_output => true,
|
29
|
-
:break => [],
|
30
|
-
:
|
31
|
-
:
|
28
|
+
:save_output => true, # save output to a file?
|
29
|
+
:break => [], # modules/classes to break
|
30
|
+
:check => [], # modules/classes to check
|
31
|
+
:libs => [], # list of library files to import
|
32
|
+
:prog => nil, # program or test file
|
32
33
|
}
|
33
34
|
|
34
35
|
# This option parser may be used for the command-line mode or for the
|
@@ -43,6 +44,11 @@ module RubyBreaker
|
|
43
44
|
tokens.each {|t| OPTIONS[:break] << t}
|
44
45
|
end
|
45
46
|
|
47
|
+
opts.on("-c MODULES", "--check MODULES", "Specify modules/classes to check") do |s|
|
48
|
+
tokens = s.split(/[;,]/)
|
49
|
+
tokens.each {|t| OPTIONS[:check] << t}
|
50
|
+
end
|
51
|
+
|
46
52
|
opts.on("-l LIBRARIES", "--libs LIBRARIES", "Specify libraries to load") do |s|
|
47
53
|
tokens = s.split(":")
|
48
54
|
tokens.each {|t| OPTIONS[:libs] << t}
|
@@ -172,12 +178,14 @@ module RubyBreaker
|
|
172
178
|
task = self.task
|
173
179
|
OPTION_PARSER.parse(*task[:rubybreaker_opts])
|
174
180
|
Runtime.break(*task[:break])
|
181
|
+
Runtime.check(*task[:check])
|
175
182
|
task_name = task[:name]
|
176
183
|
RubyBreaker.verbose("Done reading task information")
|
177
184
|
io_file = self.io_file(task_name)
|
178
185
|
elsif OPTIONS[:prog] # running in shell mode
|
179
186
|
Runtime.break(*mods) # should not happen but for backward-compatibility
|
180
187
|
Runtime.break(*OPTIONS[:break])
|
188
|
+
Runtime.check(*OPTIONS[:check])
|
181
189
|
io_file = self.io_file(OPTIONS[:prog_file])
|
182
190
|
else
|
183
191
|
# Otherwise, assume there are no explicit IO files.
|
data/lib/rubybreaker/runtime.rb
CHANGED
@@ -10,20 +10,13 @@ require_relative "runtime/inspector"
|
|
10
10
|
|
11
11
|
module RubyBreaker
|
12
12
|
|
13
|
+
# This module contains things that are needed at runtime.
|
13
14
|
module Runtime
|
14
15
|
|
15
16
|
# This set keeps track of modules/classes that will be monitored.
|
16
17
|
# *DEPRECATED* : Use +breakable+ method instead.
|
17
18
|
BREAKABLES = Set.new
|
18
19
|
|
19
|
-
# This hash maps a module to a nested hash that maps a method name to a
|
20
|
-
# method type. This hash is shared between breakable modules/classes and
|
21
|
-
# non-breakable modules/classes.
|
22
|
-
TYPE_MAP = {} # module => {:meth_name => type}
|
23
|
-
|
24
|
-
# This hash maps a (breakable) module to a type monitor
|
25
|
-
MONITOR_MAP = {} # module => monitor
|
26
|
-
|
27
20
|
private
|
28
21
|
|
29
22
|
# Instruments the monitor to the specified modules/classes.
|
@@ -36,9 +29,10 @@ module RubyBreaker
|
|
36
29
|
when Array
|
37
30
|
self.install(monitor_type, *mod)
|
38
31
|
when Module, Class
|
39
|
-
|
32
|
+
# Install both instance and its eigen class
|
33
|
+
MonitorInstaller.install_monitor(monitor_type, mod)
|
40
34
|
eigen_class = self.eigen_class(mod)
|
41
|
-
MonitorInstaller.install_monitor(eigen_class)
|
35
|
+
MonitorInstaller.install_monitor(monitor_type, eigen_class)
|
42
36
|
when String, Symbol
|
43
37
|
begin
|
44
38
|
# Get the actual module and install it right now
|
@@ -56,17 +50,23 @@ module RubyBreaker
|
|
56
50
|
public
|
57
51
|
|
58
52
|
# This method instruments the specified modules/classes at the time of
|
59
|
-
# the call.
|
53
|
+
# the call so they are monitored for type documentation.
|
60
54
|
def self.break(*mods)
|
61
55
|
self.install(:break, *mods)
|
62
56
|
end
|
63
57
|
|
58
|
+
# This method instruments the specified modules/classes at the time of
|
59
|
+
# the call so that they are type checked during runtime.
|
60
|
+
def self.check(*mods)
|
61
|
+
self.install(:check, *mods)
|
62
|
+
end
|
63
|
+
|
64
64
|
# This method installs a monitor for each breakable module.
|
65
65
|
# *DEPRECATED*: Use +breakable()+ method instead.
|
66
66
|
def self.instrument()
|
67
67
|
BREAKABLES.each do |mod|
|
68
68
|
# Duplicate checks in place in these calls.
|
69
|
-
MonitorInstaller.install_monitor(mod)
|
69
|
+
MonitorInstaller.install_monitor(:break, mod)
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -94,6 +94,10 @@ module RubyBreaker
|
|
94
94
|
Runtime.break(*mods)
|
95
95
|
end
|
96
96
|
|
97
|
+
def self.check(*mods)
|
98
|
+
Runtime.check(*mods)
|
99
|
+
end
|
100
|
+
|
97
101
|
# *DEPRECATED*: Use +Runtime.breakable()+ or +RubyBreaker.run()+ method
|
98
102
|
# instead.
|
99
103
|
module Breakable
|
@@ -13,6 +13,15 @@ module RubyBreaker
|
|
13
13
|
|
14
14
|
module Runtime
|
15
15
|
|
16
|
+
# This hash maps a module to a nested hash that maps a method name to a
|
17
|
+
# method type. This hash is shared between breakable modules/classes and
|
18
|
+
# non-breakable modules/classes.
|
19
|
+
TYPE_MAP = {} # module => {:meth_name => type}
|
20
|
+
|
21
|
+
# This hash maps a (breakable) module to a type monitor
|
22
|
+
MONITOR_MAP = {} # module => monitor
|
23
|
+
|
24
|
+
# The default type system for RubyBreaker
|
16
25
|
DEFAULT_TYPE_SYSTEM = TypeSystem.new
|
17
26
|
|
18
27
|
# This class monitors method calls before and after. It simply reroutes
|
@@ -20,73 +29,12 @@ module RubyBreaker
|
|
20
29
|
# the actual work of gathering type information.
|
21
30
|
class Monitor
|
22
31
|
|
23
|
-
# attr_accessor :mod
|
24
32
|
attr_accessor :pluggable
|
25
33
|
|
26
|
-
public
|
27
|
-
|
28
|
-
def initialize(mod, pluggable)
|
29
|
-
# @mod = mod
|
30
|
-
@pluggable = pluggable
|
31
|
-
end
|
32
|
-
|
33
|
-
# Starts monitoring of a method; it wraps each argument so that they
|
34
|
-
# can gather type information in the callee.
|
35
|
-
def monitor_before_method(obj, meth_info)
|
36
|
-
@pluggable.before_method(obj, meth_info)
|
37
|
-
end
|
38
|
-
|
39
|
-
# This method is invoked after the actual method is invoked.
|
40
|
-
def monitor_after_method(obj, meth_info)
|
41
|
-
@pluggable.after_method(obj, meth_info)
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
# This class is a switch to turn on and off the type monitoring system.
|
47
|
-
# It is important to turn off the monitor once the process is inside the
|
48
|
-
# monitor; otherwise, it WILL fall into an infinite loop.
|
49
|
-
class MonitorSwitch
|
50
|
-
|
51
|
-
attr_accessor :switch
|
52
|
-
|
53
|
-
def initialize(); @switch = true end
|
54
|
-
|
55
|
-
def turn_on();
|
56
|
-
RubyBreaker.log("Switch turned on")
|
57
|
-
@switch = true;
|
58
|
-
end
|
59
|
-
|
60
|
-
def turn_off();
|
61
|
-
RubyBreaker.log("Switch turned off")
|
62
|
-
@switch = false;
|
63
|
-
end
|
64
|
-
|
65
|
-
def set_to(mode); @switch = mode; end
|
66
|
-
end
|
67
|
-
|
68
|
-
# TODO:For now, we use a global switch; but in future, a switch per
|
69
|
-
# object should be used for multi-process apps. However, there is still
|
70
|
-
# a concern for module tracking in which case, there isn't really a way
|
71
|
-
# to do this unless we track with the process or some unique id for that
|
72
|
-
# process.
|
73
|
-
GLOBAL_MONITOR_SWITCH = MonitorSwitch.new
|
74
|
-
|
75
|
-
# This context is used for keeping track of context in the user code.
|
76
|
-
# This will ignore the context within the RubyBreaker code, so it is
|
77
|
-
# easy to pinpoint program locations without distraction from the
|
78
|
-
# RubyBreaker code.
|
79
|
-
CONTEXT = Context.new(ObjectPosition.new(self,"main"))
|
80
|
-
|
81
|
-
# This module contains helper methods for monitoring objects and
|
82
|
-
# modules.
|
83
|
-
module MonitorUtils
|
84
|
-
|
85
|
-
public
|
86
|
-
|
87
34
|
# This will do the actual routing work for a particular "monitored"
|
88
35
|
# method call.
|
89
36
|
#
|
37
|
+
# route_type:: :break or :check
|
90
38
|
# obj:: is the object receiving the message; is never wrapped object
|
91
39
|
# meth_name:: is the original method name being called args:: is a
|
92
40
|
# list of arguments for the original method call blk:: is the block
|
@@ -96,7 +44,7 @@ module RubyBreaker
|
|
96
44
|
# NOTE: This method should not assume that obj is a monitored
|
97
45
|
# object. That is, no special method should be called to obj unless it
|
98
46
|
# checks first.
|
99
|
-
def self.route(obj,meth_name
|
47
|
+
def self.route(route_type, obj, meth_name, *args, &blk)
|
100
48
|
|
101
49
|
# remember the switch mode before turning it off
|
102
50
|
switch = GLOBAL_MONITOR_SWITCH.switch
|
@@ -138,9 +86,21 @@ module RubyBreaker
|
|
138
86
|
|
139
87
|
meth_info = MethodInfo.new(meth_name, args, blk, nil)
|
140
88
|
|
141
|
-
|
89
|
+
begin
|
90
|
+
case route_type
|
91
|
+
when :break
|
92
|
+
mm.break_before_method(obj, meth_info)
|
93
|
+
when :check
|
94
|
+
mm.check_before_method(obj, meth_info)
|
95
|
+
end
|
96
|
+
rescue Errors::TypeError => e
|
97
|
+
# Trap it, turn on the global monitor and then re-raise the
|
98
|
+
# exception
|
99
|
+
GLOBAL_MONITOR_SWITCH.turn_on()
|
100
|
+
raise e
|
101
|
+
end
|
142
102
|
|
143
|
-
RubyBreaker.log("
|
103
|
+
RubyBreaker.log("break_before_method ended")
|
144
104
|
|
145
105
|
# we are going to turn the switch back on
|
146
106
|
GLOBAL_MONITOR_SWITCH.turn_on()
|
@@ -153,7 +113,21 @@ module RubyBreaker
|
|
153
113
|
GLOBAL_MONITOR_SWITCH.turn_off()
|
154
114
|
|
155
115
|
meth_info.ret = retval
|
156
|
-
|
116
|
+
|
117
|
+
begin
|
118
|
+
case route_type
|
119
|
+
when :break
|
120
|
+
mm.break_after_method(obj, meth_info)
|
121
|
+
when :check
|
122
|
+
mm.check_after_method(obj, meth_info)
|
123
|
+
end
|
124
|
+
rescue Errors::TypeError => e
|
125
|
+
# Trap it, turn on the global monitor and then re-raise the
|
126
|
+
# exception
|
127
|
+
GLOBAL_MONITOR_SWITCH.turn_on()
|
128
|
+
raise e
|
129
|
+
end
|
130
|
+
|
157
131
|
retval = meth_info.ret # Return value may have been altered by the
|
158
132
|
# after_method monitoring code
|
159
133
|
|
@@ -177,36 +151,91 @@ module RubyBreaker
|
|
177
151
|
return meth_name[2..-1]
|
178
152
|
end
|
179
153
|
|
154
|
+
def initialize(pluggable)
|
155
|
+
@pluggable = pluggable
|
156
|
+
end
|
157
|
+
|
158
|
+
# This method is invoked before the original method is executed.
|
159
|
+
def check_before_method(obj, meth_info)
|
160
|
+
@pluggable.check_before_method(obj, meth_info)
|
161
|
+
end
|
162
|
+
|
163
|
+
# This method is invoked after the original method is executed.
|
164
|
+
def check_after_method(obj, meth_info)
|
165
|
+
@pluggable.check_after_method(obj, meth_info)
|
166
|
+
end
|
167
|
+
|
168
|
+
# This method is invoked before the original method is executed.
|
169
|
+
def break_before_method(obj, meth_info)
|
170
|
+
@pluggable.break_before_method(obj, meth_info)
|
171
|
+
end
|
172
|
+
|
173
|
+
# This method is invoked after the original method is executed.
|
174
|
+
def break_after_method(obj, meth_info)
|
175
|
+
@pluggable.break_after_method(obj, meth_info)
|
176
|
+
end
|
177
|
+
|
180
178
|
end
|
181
179
|
|
180
|
+
# This class is a switch to turn on and off the type monitoring system.
|
181
|
+
# It is important to turn off the monitor once the process is inside the
|
182
|
+
# monitor; otherwise, it WILL fall into an infinite loop.
|
183
|
+
class MonitorSwitch
|
184
|
+
|
185
|
+
attr_accessor :switch
|
186
|
+
|
187
|
+
def initialize(); @switch = true end
|
188
|
+
|
189
|
+
def turn_on();
|
190
|
+
RubyBreaker.log("Switch turned on")
|
191
|
+
@switch = true;
|
192
|
+
end
|
193
|
+
|
194
|
+
def turn_off();
|
195
|
+
RubyBreaker.log("Switch turned off")
|
196
|
+
@switch = false;
|
197
|
+
end
|
198
|
+
|
199
|
+
def set_to(mode); @switch = mode; end
|
200
|
+
end
|
201
|
+
|
202
|
+
# TODO:For now, we use a global switch; but in future, a switch per
|
203
|
+
# object should be used for multi-process apps. However, there is still
|
204
|
+
# a concern for module tracking in which case, there isn't really a way
|
205
|
+
# to do this unless we track with the process or some unique id for that
|
206
|
+
# process.
|
207
|
+
GLOBAL_MONITOR_SWITCH = MonitorSwitch.new
|
208
|
+
|
209
|
+
# This context is used for keeping track of context in the user code.
|
210
|
+
# This will ignore the context within the RubyBreaker code, so it is
|
211
|
+
# easy to pinpoint program locations without distraction from the
|
212
|
+
# RubyBreaker code.
|
213
|
+
CONTEXT = Context.new(ObjectPosition.new(self,"main"))
|
214
|
+
|
182
215
|
# This module installs a monitor in the object.
|
183
216
|
module MonitorInstaller
|
184
217
|
|
185
|
-
include MonitorUtils
|
186
|
-
|
187
218
|
# returns true if the receiver is a module or a class
|
188
|
-
def self.is_module?(
|
189
|
-
return
|
219
|
+
def self.is_module?(mod)
|
220
|
+
return mod.respond_to?(:class) && mod.kind_of?(Module)
|
190
221
|
end
|
191
222
|
|
192
223
|
# renames the method in essence; this method also "installs" the
|
193
224
|
# module monitor for the class
|
194
|
-
def self.
|
195
|
-
alt_meth_name =
|
196
|
-
|
225
|
+
def self.monkey_patch_meth(monitor_type, mod, meth_name)
|
226
|
+
alt_meth_name = Monitor.get_alt_meth_name(meth_name)
|
227
|
+
mod.module_eval("alias :\"#{alt_meth_name}\" :\"#{meth_name}\"")
|
197
228
|
RubyBreaker.log("Adding alternate method for #{meth_name}")
|
198
|
-
|
229
|
+
route_call = "RubyBreaker::Runtime::Monitor.route"
|
230
|
+
mod.module_eval <<-EOF
|
199
231
|
def #{meth_name}(*args, &blk)
|
200
|
-
|
201
|
-
"#{meth_name}",
|
202
|
-
*args,
|
203
|
-
&blk)
|
232
|
+
#{route_call}(:#{monitor_type}, self,"#{meth_name}",*args,&blk)
|
204
233
|
end
|
205
234
|
EOF
|
206
235
|
end
|
207
236
|
|
208
237
|
# Installs an module (class) monitor to the object.
|
209
|
-
def self.install_monitor(mod)
|
238
|
+
def self.install_monitor(monitor_type, mod)
|
210
239
|
|
211
240
|
RubyBreaker.log("Installing module monitor for #{mod}")
|
212
241
|
|
@@ -216,7 +245,7 @@ module RubyBreaker
|
|
216
245
|
return
|
217
246
|
end
|
218
247
|
|
219
|
-
MONITOR_MAP[mod] = Monitor.new(
|
248
|
+
MONITOR_MAP[mod] = Monitor.new(DEFAULT_TYPE_SYSTEM)
|
220
249
|
|
221
250
|
# Create the type map if it does not exist already. Remember, this
|
222
251
|
# map could have been made by typesig().
|
@@ -226,12 +255,15 @@ module RubyBreaker
|
|
226
255
|
# methods. Those are part of the owner's not this module.
|
227
256
|
meths = mod.instance_methods(false)
|
228
257
|
|
229
|
-
# See if any method is already
|
230
|
-
|
231
|
-
|
258
|
+
# See if any method is already documented (explicitly typesig'ed)
|
259
|
+
doc_mt_map = Inspector.inspect_all(mod)
|
260
|
+
doc_meths = doc_mt_map.keys
|
232
261
|
|
233
262
|
meths.each do |m|
|
234
|
-
|
263
|
+
# Documented method will not be monkey-patched for "breaking"
|
264
|
+
unless monitor_type == :break && doc_meths.include?(m)
|
265
|
+
self.monkey_patch_meth(monitor_type, mod, m)
|
266
|
+
end
|
235
267
|
end
|
236
268
|
|
237
269
|
end
|