rubybreaker 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|