rubybreaker 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS +5 -0
- data/NOTES +9 -0
- data/README.md +100 -183
- data/Rakefile +21 -6
- data/VERSION +1 -1
- data/bin/rubybreaker +34 -50
- data/lib/rubybreaker/debug/debug.rb +6 -0
- data/lib/rubybreaker/doc/rdoc.rb +37 -0
- data/lib/rubybreaker/doc.rb +3 -0
- data/lib/rubybreaker/runtime/inspector.rb +6 -28
- data/lib/rubybreaker/runtime/monitor.rb +21 -17
- data/lib/rubybreaker/runtime/object_wrapper.rb +7 -2
- data/lib/rubybreaker/runtime/type_system.rb +3 -5
- data/lib/rubybreaker/runtime/typesig_unparser.rb +1 -4
- data/lib/rubybreaker/runtime.rb +64 -105
- data/lib/rubybreaker/task.rb +97 -0
- data/lib/rubybreaker/test/rspec.rb +1 -1
- data/lib/rubybreaker/test/testcase.rb +13 -28
- data/lib/rubybreaker/type/type.rb +1 -1
- data/lib/rubybreaker/typing/subtyping.rb +10 -5
- data/lib/rubybreaker/util.rb +0 -1
- data/lib/rubybreaker.rb +163 -115
- data/test/integrated/tc_both_broken_breakable.rb +5 -4
- data/test/integrated/tc_class_methods.rb +4 -3
- data/test/integrated/tc_inherit_broken.rb +4 -3
- data/test/integrated/tc_method_missing.rb +4 -4
- data/test/integrated/tc_namespace.rb +4 -2
- data/test/integrated/tc_simple1.rb +4 -3
- data/test/runtime/tc_obj_wrapper.rb +25 -6
- data/test/runtime/tc_typesig_parser.rb +0 -1
- data/test/testtask/sample.rb +10 -0
- data/test/testtask/tc_testtask.rb +25 -0
- data/test/ts_rspec.rb +21 -15
- data/test/typing/tc_typing.rb +2 -3
- data/webpage/index.html +105 -193
- data/webpage/rdoc/Kernel.html +286 -0
- data/webpage/rdoc/Object.html +17 -11
- data/webpage/rdoc/Rake/RubyBreakerTestTask.html +374 -0
- data/webpage/rdoc/Rake.html +212 -0
- data/webpage/rdoc/RubyBreaker/Breakable.html +24 -40
- data/webpage/rdoc/RubyBreaker/Broken.html +21 -69
- data/webpage/rdoc/RubyBreaker/Context.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/InternalError.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/InvalidSubtypeCheck.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/InvalidTypeConstruction.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/SubtypeFailure.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/TypeError.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors/UserError.html +16 -10
- data/webpage/rdoc/RubyBreaker/Errors.html +16 -10
- data/webpage/rdoc/RubyBreaker/ObjectPosition.html +16 -10
- data/webpage/rdoc/RubyBreaker/Position.html +16 -10
- data/webpage/rdoc/RubyBreaker/{TestCase.html → RDocSupport.html} +81 -82
- data/webpage/rdoc/RubyBreaker/RubyTypeUtils.html +16 -10
- data/webpage/rdoc/RubyBreaker/Runtime/Inspector.html +25 -44
- data/webpage/rdoc/RubyBreaker/Runtime/MethodInfo.html +16 -10
- data/webpage/rdoc/RubyBreaker/Runtime/Monitor.html +19 -13
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorInstaller.html +37 -25
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorSwitch.html +20 -14
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorUtils.html +21 -15
- data/webpage/rdoc/RubyBreaker/Runtime/ObjectWrapper.html +23 -12
- data/webpage/rdoc/RubyBreaker/Runtime/Pluggable.html +16 -10
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSigParser.html +16 -10
- data/webpage/rdoc/RubyBreaker/Runtime/{TypesigUnparser.html → TypeSigUnparser.html} +19 -16
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSystem.html +18 -14
- data/webpage/rdoc/RubyBreaker/Runtime.html +145 -11
- data/webpage/rdoc/RubyBreaker/TypeComparer.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/AnyType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/BlockType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/DuckType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/FusionType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/MethodListType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/MethodType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/NilType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/NominalType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/OptionalType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/OrType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs/SelfType.html +17 -11
- data/webpage/rdoc/RubyBreaker/TypeDefs/Type.html +17 -11
- data/webpage/rdoc/RubyBreaker/TypeDefs/VarLengthType.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeDefs.html +16 -10
- data/webpage/rdoc/RubyBreaker/TypeUnparser.html +16 -10
- data/webpage/rdoc/RubyBreaker/Typing.html +17 -11
- data/webpage/rdoc/RubyBreaker/Util.html +16 -10
- data/webpage/rdoc/RubyBreaker.html +167 -34
- data/webpage/rdoc/{RubyBreaker/Runtime/TypePlaceholder.html → Test/Unit/TestCase.html} +68 -39
- data/webpage/rdoc/Test/Unit.html +211 -0
- data/webpage/rdoc/Test.html +211 -0
- data/webpage/rdoc/created.rid +18 -17
- data/webpage/rdoc/index.html +16 -10
- data/webpage/rdoc/js/search_index.js +1 -1
- data/webpage/rdoc/table_of_contents.html +61 -48
- metadata +21 -12
- data/lib/rubybreaker/rubylib/core.rb +0 -2483
- data/lib/rubybreaker/rubylib.rb +0 -3
- data/lib/rubybreaker/runtime/type_placeholder.rb +0 -23
- data/webpage/rdoc/RubyBreaker/Broken/BrokenEigen.html +0 -305
- data/webpage/rdoc/RubyBreaker/Main.html +0 -458
@@ -1,6 +1,6 @@
|
|
1
1
|
#--
|
2
2
|
# This file defines the type inspector which fetches the type information
|
3
|
-
# gathered or documented in a
|
3
|
+
# gathered or documented in a class.
|
4
4
|
|
5
5
|
require_relative "util"
|
6
6
|
require_relative "monitor"
|
@@ -9,26 +9,14 @@ module RubyBreaker
|
|
9
9
|
|
10
10
|
module Runtime
|
11
11
|
|
12
|
-
# This module inspects
|
13
|
-
# module to fetch the type information for each method.
|
12
|
+
# This module inspects type information gathered so far.
|
14
13
|
module Inspector
|
15
14
|
|
16
15
|
# This method inspects the module for the type of the specified
|
17
|
-
# method.
|
18
|
-
# method, by looking at, first, the placeholder for the Breakable
|
19
|
-
# side of the module, and then, the placeholder for the Broken side of
|
20
|
-
# the module. If no method exists or if there is no type information
|
21
|
-
# for the method, it returns nil.
|
16
|
+
# method.
|
22
17
|
def self.inspect_meth(mod, mname)
|
23
18
|
mname = mname.to_sym
|
24
|
-
if
|
25
|
-
placeholder = Breakable::TYPE_PLACEHOLDER_MAP[mod]
|
26
|
-
end
|
27
|
-
t = placeholder.meth_type_map[mname] if placeholder
|
28
|
-
if !t && Broken::TYPE_PLACEHOLDER_MAP.has_key?(mod)
|
29
|
-
placeholder = Broken::TYPE_PLACEHOLDER_MAP[mod]
|
30
|
-
t = placeholder.meth_type_map[mname] if placeholder
|
31
|
-
end
|
19
|
+
t = TYPE_MAP[mod][mname] if TYPE_MAP.has_key?(mod)
|
32
20
|
return t
|
33
21
|
end
|
34
22
|
|
@@ -52,18 +40,8 @@ module RubyBreaker
|
|
52
40
|
# containing (method name, method type) pairs.
|
53
41
|
def self.inspect_all(mod)
|
54
42
|
mtypes = {}
|
55
|
-
mm =
|
56
|
-
if mm
|
57
|
-
mm.meth_type_map.each_pair {|im,mtype|
|
58
|
-
mtypes[im] = mtype if mtype
|
59
|
-
}
|
60
|
-
end
|
61
|
-
mm = Broken::TYPE_PLACEHOLDER_MAP[mod]
|
62
|
-
if mm
|
63
|
-
mm.meth_type_map.each_pair {|im,mtype|
|
64
|
-
mtypes[im] = mtype if mtype
|
65
|
-
}
|
66
|
-
end
|
43
|
+
mm = TYPE_MAP[mod]
|
44
|
+
mm.each_pair {|im,mtype| mtypes[im] = mtype if mtype } if mm
|
67
45
|
return mtypes
|
68
46
|
end
|
69
47
|
|
@@ -1,14 +1,12 @@
|
|
1
1
|
#--
|
2
2
|
# This file contains the core of the runtime framework which injects the
|
3
|
-
# monitoring code into
|
3
|
+
# monitoring code into breakable classes/modules and actually monitors the
|
4
4
|
# instances of those classes/modules at runtime. It also provides some utility
|
5
5
|
# functions for later use (after runtime).
|
6
6
|
|
7
7
|
dir = File.dirname(__FILE__)
|
8
8
|
require_relative "util"
|
9
9
|
require_relative "../debug"
|
10
|
-
require_relative "type_placeholder"
|
11
|
-
require_relative "pluggable"
|
12
10
|
require_relative "type_system"
|
13
11
|
|
14
12
|
module RubyBreaker
|
@@ -132,11 +130,11 @@ module RubyBreaker
|
|
132
130
|
mod = is_obj_mod ? Runtime.eigen_class(obj) : obj.class
|
133
131
|
|
134
132
|
# mm = get_module_monitor(mod) unless is_obj_mod
|
135
|
-
mm =
|
133
|
+
mm = MONITOR_MAP[mod]
|
136
134
|
|
137
135
|
# There is something wrong if there isn't a module monitor
|
138
136
|
# associated with the call.
|
139
|
-
# raise Exception if mm == nil || !mm.
|
137
|
+
# raise Exception if mm == nil || !mm.include?(meth_name)
|
140
138
|
|
141
139
|
meth_info = MethodInfo.new(meth_name, args, blk, nil)
|
142
140
|
|
@@ -212,24 +210,30 @@ module RubyBreaker
|
|
212
210
|
|
213
211
|
RubyBreaker.log("Installing module monitor for #{mod}")
|
214
212
|
|
215
|
-
|
216
|
-
|
213
|
+
# Do not re-install monitor if already done so.
|
214
|
+
if MONITOR_MAP[mod]
|
215
|
+
RubyBreaker.log("Skip #{mod} as it has a monitor installed.")
|
216
|
+
return
|
217
|
+
end
|
218
|
+
|
219
|
+
MONITOR_MAP[mod] = Monitor.new(mod, DEFAULT_TYPE_SYSTEM)
|
217
220
|
|
218
|
-
|
221
|
+
# Create the type map if it does not exist already. Remember, this
|
222
|
+
# map could have been made by typesig().
|
223
|
+
TYPE_MAP[mod] = {} unless TYPE_MAP[mod]
|
224
|
+
|
225
|
+
# Get the list of instance methods but do not include inherited
|
226
|
+
# methods. Those are part of the owner's not this module.
|
219
227
|
meths = mod.instance_methods(false)
|
220
228
|
|
221
|
-
#
|
222
|
-
|
223
|
-
|
224
|
-
broken_meths = broken_meth_type_map.keys
|
229
|
+
# See if any method is already broken (explicitly typesig'ed)
|
230
|
+
broken_mt_map = Inspector.inspect_all(mod)
|
231
|
+
broken_meths = broken_mt_map.keys
|
225
232
|
|
226
233
|
meths.each do |m|
|
227
|
-
|
228
|
-
# Breakable (if the module is declared to be Breakable).
|
229
|
-
unless broken_meths.include?(m)
|
230
|
-
self.rename_meth(mod,m)
|
231
|
-
end
|
234
|
+
self.rename_meth(mod,m) unless broken_meths.include?(m)
|
232
235
|
end
|
236
|
+
|
233
237
|
end
|
234
238
|
|
235
239
|
end
|
@@ -70,11 +70,16 @@ module RubyBreaker
|
|
70
70
|
def method_missing(mname,*args,&blk)
|
71
71
|
::RubyBreaker.log("Method_missing for #{mname}")
|
72
72
|
if GLOBAL_MONITOR_SWITCH.switch
|
73
|
+
# Must handle send method specially (do not track them)
|
74
|
+
if [:"__send__", :send].include?(mname)
|
75
|
+
mname = args[0]
|
76
|
+
args = args[1..-1]
|
77
|
+
end
|
73
78
|
@__rubybreaker_type.add_meth(mname)
|
74
|
-
retval = @__rubybreaker_obj.send(mname
|
79
|
+
retval = @__rubybreaker_obj.send(mname, *args, &blk)
|
75
80
|
retval = ObjectWrapper.new(retval)
|
76
81
|
else
|
77
|
-
retval = @__rubybreaker_obj.send(mname
|
82
|
+
retval = @__rubybreaker_obj.send(mname, *args, &blk)
|
78
83
|
end
|
79
84
|
return retval
|
80
85
|
end
|
@@ -7,10 +7,10 @@
|
|
7
7
|
|
8
8
|
require_relative "util"
|
9
9
|
require_relative "object_wrapper"
|
10
|
-
require_relative "type_placeholder"
|
11
10
|
require_relative "../type"
|
12
11
|
require_relative "../typing"
|
13
12
|
require_relative "../debug"
|
13
|
+
require_relative "pluggable"
|
14
14
|
|
15
15
|
module RubyBreaker
|
16
16
|
|
@@ -176,7 +176,7 @@ module RubyBreaker
|
|
176
176
|
is_obj_mod = (obj.class == Class or obj.class == Module)
|
177
177
|
mod = is_obj_mod ? Runtime.eigen_class(obj) : obj.class
|
178
178
|
|
179
|
-
meth_type_map =
|
179
|
+
meth_type_map = TYPE_MAP[mod]
|
180
180
|
|
181
181
|
# Let's take things out of the MethodInfo object
|
182
182
|
meth_name = meth_info.meth_name
|
@@ -244,10 +244,8 @@ module RubyBreaker
|
|
244
244
|
|
245
245
|
RubyBreaker.log("In module monitor_after #{meth_name}")
|
246
246
|
|
247
|
-
meth_type_map = Breakable::TYPE_PLACEHOLDER_MAP[mod].meth_type_map
|
248
|
-
|
249
247
|
# Compute the least upper bound
|
250
|
-
lub(obj,
|
248
|
+
lub(obj, TYPE_MAP[mod],meth_name,retval,*args,&blk)
|
251
249
|
|
252
250
|
if obj == retval
|
253
251
|
# It is possible that the method receiver is a wrapped object if
|
@@ -6,7 +6,7 @@ module RubyBreaker
|
|
6
6
|
module Runtime
|
7
7
|
|
8
8
|
# This module handles unparsing type signatures.
|
9
|
-
module
|
9
|
+
module TypeSigUnparser
|
10
10
|
|
11
11
|
include TypeDefs
|
12
12
|
|
@@ -51,9 +51,6 @@ module RubyBreaker
|
|
51
51
|
|
52
52
|
pp.text("#{keyword} #{mod.to_s}", 80)
|
53
53
|
pp.nest(2) do
|
54
|
-
pp.breakable("")
|
55
|
-
pp.text("include RubyBreaker::Broken", 80)
|
56
|
-
|
57
54
|
# See if there is any class method to show
|
58
55
|
eigen = Runtime.eigen_class(mod)
|
59
56
|
if !DOCUMENTED.include?(eigen)
|
data/lib/rubybreaker/runtime.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
#--
|
2
|
-
# This file
|
3
|
-
#
|
4
|
-
|
5
|
-
|
2
|
+
# This file provides two methods breakable() and broken() that declares a
|
3
|
+
# module/class to be monitored
|
4
|
+
require "set"
|
6
5
|
require_relative "runtime/overrides"
|
7
6
|
require_relative "runtime/typesig_parser"
|
8
7
|
require_relative "runtime/typesig_unparser"
|
@@ -11,122 +10,82 @@ require_relative "runtime/inspector"
|
|
11
10
|
|
12
11
|
module RubyBreaker
|
13
12
|
|
14
|
-
|
15
|
-
# "declared" to be Broken, it cannot be Breakable.
|
16
|
-
#
|
17
|
-
# TODO: In future, there will be a hybrid of two to allow documenting of
|
18
|
-
# methods that are newly introduced in a broken class/module.
|
13
|
+
module Runtime
|
19
14
|
|
20
|
-
|
21
|
-
|
15
|
+
# This set keeps track of modules/classes that will be monitored.
|
16
|
+
# *DEPRECATED* : Use +breakable+ method instead.
|
17
|
+
BREAKABLES = Set.new
|
22
18
|
|
23
|
-
|
24
|
-
|
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}
|
25
23
|
|
26
|
-
|
27
|
-
#
|
28
|
-
# monitored and its type documentation is generated, the module now becomes
|
29
|
-
# a Broken module. The actual implementation is a simple trigger that
|
30
|
-
# queues the target module into the list of modules to monitor. The queued
|
31
|
-
# modules are then modified to be monitored dynamically.
|
32
|
-
module Breakable
|
24
|
+
# This hash maps a (breakable) module to a type monitor
|
25
|
+
MONITOR_MAP = {} # module => monitor
|
33
26
|
|
34
|
-
|
35
|
-
|
27
|
+
# This set lists modules/classes that are actually instrumented with a
|
28
|
+
# monitor.
|
29
|
+
INSTALLED = Set.new
|
36
30
|
|
37
|
-
#
|
38
|
-
#
|
39
|
-
def self.
|
40
|
-
|
41
|
-
|
31
|
+
# This method installs a monitor for each breakable module.
|
32
|
+
# *DEPRECATED*: Use +breakable()+ method instead.
|
33
|
+
def self.instrument()
|
34
|
+
BREAKABLES.each do |mod|
|
35
|
+
# Duplicate checks in place in these calls.
|
36
|
+
MonitorInstaller.install_module_monitor(mod)
|
37
|
+
INSTALLED << mod
|
38
|
+
end
|
42
39
|
end
|
43
40
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
# typesig("foo(fixnum) -> fixnum")
|
64
|
-
# def foo(x) ... end
|
65
|
-
# end
|
66
|
-
#
|
67
|
-
module BrokenEigen
|
68
|
-
|
69
|
-
include TypeDefs
|
70
|
-
include Runtime
|
71
|
-
|
72
|
-
# This method can be used at the eigen level of the target module to
|
73
|
-
# specify the type of a method.
|
74
|
-
def typesig(str)
|
75
|
-
|
76
|
-
# This MUST BE set for self type to work in type signatures.
|
77
|
-
TypeDefs::SelfType.set_self(self)
|
78
|
-
|
79
|
-
t = TypeSigParser.parse(str)
|
80
|
-
placeholder = TYPE_PLACEHOLDER_MAP[self]
|
81
|
-
if placeholder
|
82
|
-
meth_type = placeholder.meth_type_map[t.meth_name]
|
83
|
-
if meth_type
|
84
|
-
# TODO: make a method list
|
85
|
-
if meth_type.instance_of?(MethodListType)
|
86
|
-
meth_type.types << t
|
87
|
-
else
|
88
|
-
# then upgrade it
|
89
|
-
placeholder.meth_type_map[t.meth_name] =
|
90
|
-
MethodListType.new([meth_type, t])
|
91
|
-
end
|
92
|
-
else
|
93
|
-
placeholder.meth_type_map[t.meth_name] = t
|
41
|
+
# This method modifies specified modules/classes at the very moment
|
42
|
+
# (instead of registering them for later).
|
43
|
+
def self.breakable(*mods)
|
44
|
+
mods.each do |mod|
|
45
|
+
case mod
|
46
|
+
when Array
|
47
|
+
self.breakable(*mod)
|
48
|
+
when Module, Class
|
49
|
+
MonitorInstaller.install_module_monitor(mod)
|
50
|
+
eigen_class = self.eigen_class(mod)
|
51
|
+
MonitorInstaller.install_module_monitor(eigen_class)
|
52
|
+
INSTALLED << mod << eigen_class
|
53
|
+
when String, Symbol
|
54
|
+
begin
|
55
|
+
# Get the actual module and install it right now
|
56
|
+
mod = eval("#{mod}", TOPLEVEL_BINDING)
|
57
|
+
self.breakable(mod) if mod
|
58
|
+
rescue NameError => e
|
59
|
+
RubyBreaker.error("#{mod} cannot be found.")
|
94
60
|
end
|
61
|
+
else
|
62
|
+
RubyBreaker.error("You must specify a module/class or its name.")
|
95
63
|
end
|
96
|
-
return t
|
97
64
|
end
|
98
|
-
|
99
65
|
end
|
100
|
-
|
101
|
-
# This method is triggered when Broken module is included. This just
|
102
|
-
# extends BrokenEigen into the target module so "typesig" method can be
|
103
|
-
# called from the eigen level of the module. It also extends the eigen
|
104
|
-
# class of the target module so that "typesig" can work for class
|
105
|
-
# methods too.
|
106
|
-
def self.included(mod)
|
66
|
+
end
|
107
67
|
|
108
|
-
|
109
|
-
|
68
|
+
# *DEPRECATED*: Use +RubyBreaker.run()+ to indicate the point of entry.
|
69
|
+
def self.monitor()
|
70
|
+
end
|
110
71
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
TYPE_PLACEHOLDER_MAP[mod] = placeholder
|
116
|
-
end
|
117
|
-
mod.extend(BrokenEigen)
|
72
|
+
# This method just redirects to Runtime's method.
|
73
|
+
def self.breakable(*mods)
|
74
|
+
Runtime.breakable(*mods)
|
75
|
+
end
|
118
76
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
placeholder = TypePlaceholder.new()
|
125
|
-
TYPE_PLACEHOLDER_MAP[eigen_class] = placeholder
|
126
|
-
end
|
127
|
-
eigen_class.extend(BrokenEigen)
|
77
|
+
# *DEPRECATED*: Use +Runtime.breakable()+ or +RubyBreaker.run()+ method
|
78
|
+
# instead.
|
79
|
+
module Breakable
|
80
|
+
def self.included(mod)
|
81
|
+
Runtime::BREAKABLES << mod << Runtime.eigen_class(mod)
|
128
82
|
end
|
129
|
-
|
130
83
|
end
|
131
84
|
|
85
|
+
# *DEPRECATED*: It has no effect.
|
86
|
+
module Broken
|
87
|
+
def self.included(mod)
|
88
|
+
# Runtime.broken(mod)
|
89
|
+
end
|
90
|
+
end
|
132
91
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
require "optparse"
|
3
|
+
require "rake/testtask"
|
4
|
+
require "yaml"
|
5
|
+
require "tempfile"
|
6
|
+
|
7
|
+
module Rake
|
8
|
+
|
9
|
+
# This class can be used as a replacement for Rake::TestTask. It is a
|
10
|
+
# subclass of Rake::TestTask and maintains additional information for
|
11
|
+
# running RubyBreaker as a Rake test task.
|
12
|
+
#
|
13
|
+
# For example, the following shows how to run RubyBreaker in a test task:
|
14
|
+
#
|
15
|
+
# desc "Run testtask test"
|
16
|
+
# Rake::RubyBreakerTestTask.new(:"testtask_test") do |t|
|
17
|
+
# t.libs << "lib"
|
18
|
+
# t.test_files = ["test/testtask/tc_testtask.rb"]
|
19
|
+
# t.breakable = ["SampleClassA"]
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
class RubyBreakerTestTask < Rake::TestTask
|
23
|
+
|
24
|
+
# List of Breakable modules/classes
|
25
|
+
attr_accessor :breakable
|
26
|
+
|
27
|
+
# RubyBreaker options
|
28
|
+
attr_accessor :rubybreaker_opts
|
29
|
+
|
30
|
+
# This overrides the testtask's constructor. In addition to the original
|
31
|
+
# behavior, it keeps track of RubyBreaker options and store them in a
|
32
|
+
# yaml file.
|
33
|
+
def initialize(taskname="", *args, &blk)
|
34
|
+
|
35
|
+
# Initialize extra instance variables
|
36
|
+
@rubybreaker_opts = []
|
37
|
+
@breakable = nil
|
38
|
+
|
39
|
+
# Call the original constructor first
|
40
|
+
super(taskname, *args, &blk)
|
41
|
+
|
42
|
+
# Parse the RubyBreaker options
|
43
|
+
case @rubybreaker_opts
|
44
|
+
when Array
|
45
|
+
opts = @rubybreaker_opts
|
46
|
+
when String
|
47
|
+
opts = @rubybreaker_opts.split(" ").select {|v| v != ""}
|
48
|
+
else
|
49
|
+
opts = []
|
50
|
+
end
|
51
|
+
|
52
|
+
# Construct the task configuration hash
|
53
|
+
config = {
|
54
|
+
name: taskname,
|
55
|
+
rubybreaker_opts: opts,
|
56
|
+
breakable: [], # Set doesn't work well with YAML; just use an array
|
57
|
+
test_files: @test_files,
|
58
|
+
}
|
59
|
+
|
60
|
+
# This allows a bulk declaration of Breakable modules/classes
|
61
|
+
@breakable.each { |b| config[:breakable] << b } if @breakable
|
62
|
+
|
63
|
+
# This code segment is a clever way to store yaml data in a ruby file
|
64
|
+
# that reads its own yaml data after __END__ when loaded.
|
65
|
+
code_data = <<-EOS
|
66
|
+
require "yaml"
|
67
|
+
f = File.new(__FILE__, "r")
|
68
|
+
while !(f.readline.match("^__END__.*$"))
|
69
|
+
# do nothing
|
70
|
+
end
|
71
|
+
data = f.read
|
72
|
+
$__rubybreaker_task = YAML.load(data)
|
73
|
+
__END__
|
74
|
+
#{YAML.dump(config)}
|
75
|
+
EOS
|
76
|
+
|
77
|
+
tmp_path = ""
|
78
|
+
# Tests are run different processes, so we must export this
|
79
|
+
# information to an external yaml file.
|
80
|
+
f = Tempfile.new(["#{taskname}",".rb"])
|
81
|
+
tmp_path = f.path
|
82
|
+
f.write(code_data)
|
83
|
+
f.close()
|
84
|
+
|
85
|
+
# Inject the -r option to load this yaml file
|
86
|
+
if @ruby_opts && @ruby_opts.empty?
|
87
|
+
@ruby_opts << "-r" << tmp_path
|
88
|
+
else
|
89
|
+
@ruby_opts = ["-r", tmp_path]
|
90
|
+
end
|
91
|
+
|
92
|
+
return self
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -1,38 +1,23 @@
|
|
1
1
|
#--
|
2
|
-
# This file
|
3
|
-
#
|
4
|
-
# (supposedly :) ).
|
2
|
+
# This file overrides the test case behavior to work with RubyBreaker behind
|
3
|
+
# the scene without requiring the user to modify the code.
|
5
4
|
|
6
|
-
|
5
|
+
# Do this only if Test::Unit is defined
|
6
|
+
if defined?(Test) && defined?(Test::Unit)
|
7
7
|
|
8
|
-
# This
|
9
|
-
|
10
|
-
module TestCase
|
8
|
+
# This class is patched to run RubyBreaker along with the test cases.
|
9
|
+
class Test::Unit::TestCase
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
end
|
11
|
+
# Save the original constructor method.
|
12
|
+
alias :__rubybreaker_initialize :initialize
|
15
13
|
|
16
|
-
|
17
|
-
|
14
|
+
# This method overrides the original constructor to run RubyBreaker before
|
15
|
+
# calling the original constructor.
|
16
|
+
def initialize(*args, &blk)
|
17
|
+
RubyBreaker.run()
|
18
|
+
return send(:__rubybreaker_initialize, *args, &blk)
|
18
19
|
end
|
19
20
|
|
20
|
-
def self.included(mod)
|
21
|
-
|
22
|
-
# hack to insert RubyBreaker's own setup and teardown methods
|
23
|
-
mod.module_eval <<-EOS
|
24
|
-
|
25
|
-
alias :__run :run
|
26
|
-
|
27
|
-
def run(*args,&blk)
|
28
|
-
RubyBreaker::TestCase.__rubybreaker_setup()
|
29
|
-
__run(*args,&blk)
|
30
|
-
RubyBreaker::TestCase.__rubybreaker_teardown()
|
31
|
-
end
|
32
|
-
|
33
|
-
EOS
|
34
|
-
|
35
|
-
end
|
36
21
|
end
|
37
22
|
end
|
38
23
|
|
@@ -96,7 +96,7 @@ module RubyBreaker
|
|
96
96
|
class SelfType < NominalType
|
97
97
|
|
98
98
|
# This is a setter method for class variable mod.
|
99
|
-
# NOTE: It is set every time
|
99
|
+
# NOTE: It is set every time typesig() is called
|
100
100
|
def self.set_self(mod)
|
101
101
|
@@mod = mod
|
102
102
|
end
|
@@ -9,7 +9,7 @@
|
|
9
9
|
# any constraint graph to resolve subtype relations. This is the main
|
10
10
|
# difference between Rubydust and RubyBreaker.
|
11
11
|
#
|
12
|
-
# If a module is not
|
12
|
+
# If a module is not broken but is breakable (i.e., monitored), then it is
|
13
13
|
# treated as non-Broken. At the end of the execution, the module will be
|
14
14
|
# Broken--i.e., its type information is now revealed. If the user wishes to
|
15
15
|
# use the result of the analysis, this type information will be documented
|
@@ -57,6 +57,11 @@ module RubyBreaker
|
|
57
57
|
|
58
58
|
private
|
59
59
|
|
60
|
+
# This method determins if the module/class has its corresponding
|
61
|
+
def self.has_type_map?(mod)
|
62
|
+
return Runtime::TYPE_MAP[mod] != nil
|
63
|
+
end
|
64
|
+
|
60
65
|
# Thie method checks if the module has all the methods specified in
|
61
66
|
# meths array
|
62
67
|
def self.module_has_methods?(mod, meths)
|
@@ -295,11 +300,11 @@ module RubyBreaker
|
|
295
300
|
#
|
296
301
|
def self.fusion_subtype_rel?(lhs,rhs)
|
297
302
|
return false unless lhs.kind_of?(FusionType)
|
298
|
-
if lhs.mod
|
303
|
+
if self.has_type_map?(lhs.mod)
|
299
304
|
if rhs.instance_of?(NominalType) # don't include self type
|
300
305
|
if RubyTypeUtils.subclass_rel?(lhs.mod, rhs.mod)
|
301
306
|
is_subtype = true
|
302
|
-
elsif rhs.mod
|
307
|
+
elsif self.has_type_map?(rhs.mod)
|
303
308
|
# then do a type check for each method
|
304
309
|
lhs_meths = Inspect.inspect_all(lhs.mod)
|
305
310
|
rhs_meths = Inspect.inspect_all(rhs.mod)
|
@@ -311,7 +316,7 @@ module RubyBreaker
|
|
311
316
|
elsif rhs.instance_of?(FusionType)
|
312
317
|
if RubyTypeUtils.subclass_rel?(lhs.mod, rhs.mod)
|
313
318
|
is_subtype = true
|
314
|
-
elsif rhs.mod
|
319
|
+
elsif self.has_type_map?(rhs.mod)
|
315
320
|
# then do a type check for each method
|
316
321
|
lhs_meths = Inspect.inspect_all(lhs.mod)
|
317
322
|
rhs_meths = Inspect.inspect_meths(rhs.mod, lhs.meths.keys)
|
@@ -377,7 +382,7 @@ module RubyBreaker
|
|
377
382
|
# If RHS is not broken, sorry no subtype relationship
|
378
383
|
if RubyTypeUtils.subclass_rel?(lhs.mod, rhs.mod)
|
379
384
|
is_subtype = true
|
380
|
-
elsif
|
385
|
+
elsif self.has_type_map?(lhs.mod) && self.has_type_map?(rhs.mod)
|
381
386
|
is_subtype = true
|
382
387
|
lhs_methods = lhs.mod.instance_methods
|
383
388
|
rhs.meth_names.each {|m|
|