rubybreaker 0.0.4 → 0.0.5
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 +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|
|