rubybreaker 0.0.1 → 0.0.2
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 +48 -0
- data/README.md +56 -20
- data/Rakefile +9 -34
- data/TODO +10 -10
- data/VERSION +1 -0
- data/bin/gen_stub_rubylib +35 -36
- data/bin/rubybreaker +1 -4
- data/lib/rubybreaker/debug.rb +8 -4
- data/lib/rubybreaker/rubylib/core.rb +738 -571
- data/lib/rubybreaker/runtime/inspector.rb +16 -7
- data/lib/rubybreaker/runtime/monitor.rb +14 -18
- data/lib/rubybreaker/runtime/object_wrapper.rb +9 -3
- data/lib/rubybreaker/runtime/overrides.rb +51 -8
- data/lib/rubybreaker/runtime/pluggable.rb +1 -3
- data/lib/rubybreaker/runtime/type_placeholder.rb +2 -6
- data/lib/rubybreaker/runtime/type_system.rb +53 -17
- data/lib/rubybreaker/runtime/typesig_parser.rb +1 -0
- data/lib/rubybreaker/runtime/util.rb +18 -0
- data/lib/rubybreaker/runtime.rb +42 -15
- data/lib/rubybreaker/type/type_comparer.rb +10 -10
- data/lib/rubybreaker/type/type_grammar.treetop +30 -21
- data/lib/rubybreaker/type/type_unparser.rb +2 -2
- data/lib/rubybreaker/typing/subtyping.rb +21 -21
- data/lib/rubybreaker/util.rb +11 -1
- data/lib/rubybreaker.rb +75 -54
- data/test/integrated/tc_class_methods.rb +35 -0
- data/test/integrated/tc_inherit_broken.rb +29 -0
- data/test/integrated/tc_method_missing.rb +1 -1
- data/test/runtime/tc_obj_wrapper.rb +104 -4
- data/test/ts_integrated.rb +2 -0
- data/test/type/tc_comparer.rb +96 -96
- data/test/type/tc_parser.rb +18 -0
- data/test/type/tc_unparser.rb +16 -0
- data/test/typing/tc_typing.rb +20 -20
- data/webpage/footer.html +1 -1
- data/webpage/header.html +7 -7
- data/webpage/index.html +65 -28
- data/webpage/rdoc/RubyBreaker/Breakable.html +280 -0
- data/webpage/rdoc/RubyBreaker/Broken/BrokenEigen.html +304 -0
- data/webpage/rdoc/RubyBreaker/Broken.html +308 -0
- data/webpage/rdoc/RubyBreaker/Context.html +421 -0
- data/webpage/rdoc/RubyBreaker/Debug.html +411 -0
- data/webpage/rdoc/RubyBreaker/Errors/InternalError.html +263 -0
- data/webpage/rdoc/RubyBreaker/Errors/InvalidSubtypeCheck.html +263 -0
- data/webpage/rdoc/RubyBreaker/Errors/InvalidTypeConstruction.html +214 -0
- data/webpage/rdoc/RubyBreaker/Errors/SubtypeFailure.html +212 -0
- data/webpage/rdoc/RubyBreaker/Errors/TypeError.html +212 -0
- data/webpage/rdoc/RubyBreaker/Errors/UserError.html +264 -0
- data/webpage/rdoc/RubyBreaker/Errors.html +209 -0
- data/webpage/rdoc/RubyBreaker/Kernel.html +259 -0
- data/webpage/rdoc/RubyBreaker/Main.html +560 -0
- data/webpage/rdoc/RubyBreaker/ObjectPosition.html +334 -0
- data/webpage/rdoc/RubyBreaker/Position.html +463 -0
- data/webpage/rdoc/RubyBreaker/RubyTypeUtils.html +308 -0
- data/webpage/rdoc/RubyBreaker/Runtime/Inspector.html +380 -0
- data/webpage/rdoc/RubyBreaker/Runtime/MethodInfo.html +324 -0
- data/webpage/rdoc/RubyBreaker/Runtime/Monitor.html +354 -0
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorInstaller.html +379 -0
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorSwitch.html +382 -0
- data/webpage/rdoc/RubyBreaker/Runtime/MonitorUtils.html +400 -0
- data/webpage/rdoc/RubyBreaker/Runtime/ObjectWrapper.html +411 -0
- data/webpage/rdoc/RubyBreaker/Runtime/Pluggable.html +305 -0
- data/webpage/rdoc/RubyBreaker/Runtime/TypePlaceholder.html +280 -0
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSigParser.html +283 -0
- data/webpage/rdoc/RubyBreaker/Runtime/TypeSystem.html +630 -0
- data/webpage/rdoc/RubyBreaker/Runtime.html +255 -0
- data/webpage/rdoc/RubyBreaker/TestCase.html +332 -0
- data/webpage/rdoc/RubyBreaker/TypeComparer.html +304 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/AnyType.html +260 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/BlockType.html +310 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/DuckType.html +320 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/FusionType.html +323 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/MethodListType.html +281 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/MethodType.html +282 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/NilType.html +260 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/NominalType.html +282 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/OptionalType.html +281 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/OrType.html +281 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/SelfType.html +329 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/Type.html +409 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs/VarLengthType.html +282 -0
- data/webpage/rdoc/RubyBreaker/TypeDefs.html +212 -0
- data/webpage/rdoc/RubyBreaker/TypeUnparser.html +273 -0
- data/webpage/rdoc/RubyBreaker/Typing.html +305 -0
- data/webpage/rdoc/RubyBreaker/Utilities.html +294 -0
- data/webpage/rdoc/RubyBreaker.html +337 -0
- data/webpage/rdoc/created.rid +26 -0
- data/webpage/rdoc/images/add.png +0 -0
- data/webpage/rdoc/images/brick.png +0 -0
- data/webpage/rdoc/images/brick_link.png +0 -0
- data/webpage/rdoc/images/bug.png +0 -0
- data/webpage/rdoc/images/bullet_black.png +0 -0
- data/webpage/rdoc/images/bullet_toggle_minus.png +0 -0
- data/webpage/rdoc/images/bullet_toggle_plus.png +0 -0
- data/webpage/rdoc/images/date.png +0 -0
- data/webpage/rdoc/images/delete.png +0 -0
- data/webpage/rdoc/images/find.png +0 -0
- data/webpage/rdoc/images/loadingAnimation.gif +0 -0
- data/webpage/rdoc/images/macFFBgHack.png +0 -0
- data/webpage/rdoc/images/package.png +0 -0
- data/webpage/rdoc/images/page_green.png +0 -0
- data/webpage/rdoc/images/page_white_text.png +0 -0
- data/webpage/rdoc/images/page_white_width.png +0 -0
- data/webpage/rdoc/images/plugin.png +0 -0
- data/webpage/rdoc/images/ruby.png +0 -0
- data/webpage/rdoc/images/tag_blue.png +0 -0
- data/webpage/rdoc/images/tag_green.png +0 -0
- data/webpage/rdoc/images/transparent.png +0 -0
- data/webpage/rdoc/images/wrench.png +0 -0
- data/webpage/rdoc/images/wrench_orange.png +0 -0
- data/webpage/rdoc/images/zoom.png +0 -0
- data/webpage/rdoc/index.html +165 -0
- data/webpage/rdoc/js/darkfish.js +153 -0
- data/webpage/rdoc/js/jquery.js +18 -0
- data/webpage/rdoc/js/navigation.js +142 -0
- data/webpage/rdoc/js/search.js +94 -0
- data/webpage/rdoc/js/search_index.js +1 -0
- data/webpage/rdoc/js/searcher.js +228 -0
- data/webpage/rdoc/rdoc.css +543 -0
- data/webpage/rdoc/table_of_contents.html +376 -0
- data/webpage/rubybreaker.css +31 -31
- metadata +93 -6
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# This file defines Inspector which finds the type placeholder for a
|
|
3
3
|
# module.
|
|
4
4
|
|
|
5
|
+
require_relative "util"
|
|
5
6
|
require_relative "monitor"
|
|
6
7
|
|
|
7
8
|
module RubyBreaker
|
|
@@ -18,16 +19,23 @@ module RubyBreaker
|
|
|
18
19
|
# it returns nil
|
|
19
20
|
def self.inspect_meth(mod, mname)
|
|
20
21
|
mname = mname.to_sym
|
|
21
|
-
if
|
|
22
|
+
if Breakable::TYPE_PLACEHOLDER_MAP.has_key?(mod)
|
|
22
23
|
placeholder = Breakable::TYPE_PLACEHOLDER_MAP[mod]
|
|
23
|
-
elsif
|
|
24
|
+
elsif Broken::TYPE_PLACEHOLDER_MAP.has_key?(mod)
|
|
24
25
|
placeholder = Broken::TYPE_PLACEHOLDER_MAP[mod]
|
|
25
26
|
else
|
|
26
27
|
# TODO
|
|
27
28
|
end
|
|
28
|
-
t = placeholder.
|
|
29
|
+
t = placeholder.meth_type_map[mname] if placeholder
|
|
29
30
|
return t
|
|
30
31
|
end
|
|
32
|
+
|
|
33
|
+
# This method inspects the module for the specified class method name.
|
|
34
|
+
# This is a shorthand for calling inspect_meth with the eigen class.
|
|
35
|
+
def self.inspect_class_meth(mod, mname)
|
|
36
|
+
eigen_class = Runtime.eigen_class(mod)
|
|
37
|
+
return self.inspect_meth(eigen_class, mname)
|
|
38
|
+
end
|
|
31
39
|
|
|
32
40
|
# Similar to inspect_meth but returns a hash of (mname, mtype) pairs.
|
|
33
41
|
def self.inspect_meths(mod, mnames)
|
|
@@ -43,10 +51,11 @@ module RubyBreaker
|
|
|
43
51
|
def self.inspect_all(mod)
|
|
44
52
|
mtypes = {}
|
|
45
53
|
mm = Breakable::TYPE_PLACEHOLDER_MAP[mod]
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
if mm
|
|
55
|
+
mm.meth_type_map.each_pair {|im,mtype|
|
|
56
|
+
mtypes[im] = mtype if mtype
|
|
57
|
+
}
|
|
58
|
+
end
|
|
50
59
|
return mtypes
|
|
51
60
|
end
|
|
52
61
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# functions for later use (after runtime).
|
|
6
6
|
|
|
7
7
|
dir = File.dirname(__FILE__)
|
|
8
|
+
require_relative "util"
|
|
8
9
|
require_relative "type_placeholder"
|
|
9
10
|
require_relative "../context"
|
|
10
11
|
require_relative "../debug"
|
|
@@ -129,21 +130,16 @@ module RubyBreaker
|
|
|
129
130
|
end
|
|
130
131
|
|
|
131
132
|
is_obj_mod = (obj.class == Class or obj.class == Module)
|
|
132
|
-
|
|
133
|
-
# from here, do more work for module monitoring
|
|
134
|
-
mod = obj.class
|
|
135
|
-
|
|
136
|
-
# TODO:
|
|
137
|
-
meta = false
|
|
133
|
+
mod = is_obj_mod ? Runtime.eigen_class(obj) : obj.class
|
|
138
134
|
|
|
139
135
|
# mm = get_module_monitor(mod) unless is_obj_mod
|
|
140
|
-
mm = Breakable::MONITOR_MAP[mod]
|
|
136
|
+
mm = Breakable::MONITOR_MAP[mod]
|
|
141
137
|
|
|
142
138
|
# There is something wrong if there isn't a module monitor
|
|
143
139
|
# associated with the call.
|
|
144
|
-
# raise Exception if mm == nil || !mm.
|
|
140
|
+
# raise Exception if mm == nil || !mm.meth_type_map.include?(meth_name)
|
|
145
141
|
|
|
146
|
-
meth_info = MethodInfo.new(
|
|
142
|
+
meth_info = MethodInfo.new(meth_name, args, blk, nil)
|
|
147
143
|
|
|
148
144
|
mm.monitor_before_method(obj, meth_info)
|
|
149
145
|
|
|
@@ -161,6 +157,8 @@ module RubyBreaker
|
|
|
161
157
|
|
|
162
158
|
meth_info.ret = retval
|
|
163
159
|
mm.monitor_after_method(obj, meth_info)
|
|
160
|
+
retval = meth_info.ret # Return value may have been altered by the
|
|
161
|
+
# after_method monitoring code
|
|
164
162
|
|
|
165
163
|
# things are done in this context. pop it off.
|
|
166
164
|
CONTEXT.pop()
|
|
@@ -196,7 +194,7 @@ module RubyBreaker
|
|
|
196
194
|
|
|
197
195
|
# renames the method in essence; this method also "installs" the
|
|
198
196
|
# module monitor for the class
|
|
199
|
-
def self.rename_meth(recv,meth_name)
|
|
197
|
+
def self.rename_meth(recv, meth_name)
|
|
200
198
|
alt_meth_name = MonitorUtils.get_alt_meth_name(meth_name)
|
|
201
199
|
recv.module_eval("alias :\"#{alt_meth_name}\" :\"#{meth_name}\"")
|
|
202
200
|
Debug.msg("Adding alternate method for #{meth_name}")
|
|
@@ -210,15 +208,13 @@ module RubyBreaker
|
|
|
210
208
|
EOF
|
|
211
209
|
end
|
|
212
210
|
|
|
213
|
-
# Installs an module (class) monitor to the object.
|
|
214
|
-
def self.install_module_monitor(mod
|
|
211
|
+
# Installs an module (class) monitor to the object.
|
|
212
|
+
def self.install_module_monitor(mod)
|
|
215
213
|
Debug.short_msg("Installing module monitor for #{mod}")
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
inst_meths = []
|
|
221
|
-
meths = mod.instance_methods(false)
|
|
214
|
+
Breakable::MONITOR_MAP[mod] = Monitor.new(mod, DEFAULT_TYPE_SYSTEM)
|
|
215
|
+
Breakable::TYPE_PLACEHOLDER_MAP[mod] = TypePlaceholder.new
|
|
216
|
+
meth_type_map = []
|
|
217
|
+
meths = mod.instance_methods(false)
|
|
222
218
|
meths.each do |m|
|
|
223
219
|
self.rename_meth(mod,m)
|
|
224
220
|
end
|
|
@@ -34,13 +34,17 @@ module RubyBreaker
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
#--
|
|
37
|
-
# The following code
|
|
38
|
-
#
|
|
39
|
-
#
|
|
37
|
+
# The following code generates the "serious problem" warning which is
|
|
38
|
+
# suppressed by the hack using $VERBOSE. This is ok. This meta
|
|
39
|
+
# programming code block re-defines BasicObject's methods to redirect
|
|
40
|
+
# to the actual object.
|
|
40
41
|
[:"!", :"!=", :"==", :"equal?", :"eql?", :"__id__", :"object_id",
|
|
41
42
|
:"send", :"__send__", :"instance_eval",
|
|
42
43
|
:"instance_exec"].each do |meth|
|
|
43
44
|
|
|
45
|
+
orig_verbose = $VERBOSE
|
|
46
|
+
$VERBOSE = nil
|
|
47
|
+
|
|
44
48
|
eval <<-EOS
|
|
45
49
|
|
|
46
50
|
def #{meth}(*args,&blk)
|
|
@@ -49,6 +53,8 @@ module RubyBreaker
|
|
|
49
53
|
|
|
50
54
|
EOS
|
|
51
55
|
|
|
56
|
+
$VERBOSE = orig_verbose
|
|
57
|
+
|
|
52
58
|
end
|
|
53
59
|
|
|
54
60
|
# Only behave differently if it's looking for +WRAPPED_INDICATOR+
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
#--
|
|
2
2
|
# This file contains methods that need to override the existing ones to
|
|
3
|
-
# accommodate the object wrapper.
|
|
3
|
+
# accommodate the object wrapper. The main issue arises when a wrapped
|
|
4
|
+
# object is compared against a non-wrapped object in either direction. Going
|
|
5
|
+
# from a wrapped object, this issue is resolved by overriding the comparison
|
|
6
|
+
# operators, but going from a non-wrapped object, the override has to
|
|
7
|
+
# happen on the non-wrapped side. This file is for those overrides.
|
|
8
|
+
#
|
|
9
|
+
# XXX: THIS FILE NEEDS A LOT OF WORK!!!
|
|
10
|
+
#
|
|
4
11
|
|
|
5
12
|
require_relative "object_wrapper"
|
|
6
13
|
|
|
@@ -10,25 +17,61 @@ module RubyBreaker
|
|
|
10
17
|
|
|
11
18
|
# This constant holds the string used internally by RubyBreaker to
|
|
12
19
|
# indicate overridden methods.
|
|
13
|
-
OVERRIDE_PREFIX = "
|
|
20
|
+
OVERRIDE_PREFIX = "__rubybreaker"
|
|
14
21
|
|
|
15
22
|
end
|
|
16
23
|
|
|
17
24
|
end
|
|
18
25
|
|
|
26
|
+
# TODO: More IO related stuff need to be overriden!
|
|
27
|
+
[Kernel, IO].each do |mod|
|
|
19
28
|
|
|
20
|
-
|
|
29
|
+
[:"puts", :"putc", :"printf", :print].each do |meth_name|
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
mod.module_eval <<-EOS
|
|
23
32
|
|
|
24
|
-
|
|
33
|
+
alias :"#{RubyBreaker::Runtime::OVERRIDE_PREFIX}_#{mod.object_id}_#{meth_name}" :"#{meth_name}"
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
EOS
|
|
36
|
+
|
|
37
|
+
mod.module_eval <<-EOS
|
|
38
|
+
|
|
39
|
+
def #{meth_name}(*args)
|
|
40
|
+
args = args.map do |arg|
|
|
41
|
+
if arg.respond_to?(RubyBreaker::Runtime::WRAPPED_INDICATOR)
|
|
42
|
+
arg.__rubybreaker_obj
|
|
43
|
+
else
|
|
44
|
+
arg
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
send(:"#{RubyBreaker::Runtime::OVERRIDE_PREFIX}_#{mod.object_id}_#{meth_name}",*args)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
EOS
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# TODO: add more modules here as necessary!
|
|
57
|
+
[Object, Numeric, String, Symbol, Enumerable, Array, Hash].each do |mod|
|
|
58
|
+
|
|
59
|
+
[:"==", :equal?, :eql?].each do |meth_name|
|
|
60
|
+
|
|
61
|
+
# Create a unique alias name for each module. (It causes some issue when
|
|
62
|
+
# not done this way.)
|
|
63
|
+
alias_name = "RubyBreaker::Runtime::OVERRIDE_PREFIX_#{mod.object_id}" +
|
|
64
|
+
"_#{meth_name}"
|
|
65
|
+
|
|
66
|
+
mod.module_eval <<-EOS
|
|
67
|
+
|
|
68
|
+
alias :"#{alias_name}" :"#{meth_name}"
|
|
69
|
+
|
|
70
|
+
def #{meth_name}(other)
|
|
28
71
|
if other.respond_to?(RubyBreaker::Runtime::WRAPPED_INDICATOR)
|
|
29
72
|
other = other.__rubybreaker_obj
|
|
30
73
|
end
|
|
31
|
-
return self.send(:"#{
|
|
74
|
+
return self.send(:"#{alias_name}",other)
|
|
32
75
|
end
|
|
33
76
|
|
|
34
77
|
EOS
|
|
@@ -11,14 +11,12 @@ module RubyBreaker
|
|
|
11
11
|
# by Pluggable and Monitor
|
|
12
12
|
class MethodInfo
|
|
13
13
|
|
|
14
|
-
attr_accessor :meta
|
|
15
14
|
attr_accessor :meth_name
|
|
16
15
|
attr_accessor :args
|
|
17
16
|
attr_accessor :blk
|
|
18
17
|
attr_accessor :ret
|
|
19
18
|
|
|
20
|
-
def initialize(
|
|
21
|
-
@meta = meta
|
|
19
|
+
def initialize(meth_name, args, blk, ret)
|
|
22
20
|
@meth_name = meth_name
|
|
23
21
|
@args = args
|
|
24
22
|
@blk = blk
|
|
@@ -10,14 +10,10 @@ module RubyBreaker
|
|
|
10
10
|
class TypePlaceholder
|
|
11
11
|
|
|
12
12
|
# This accessor sets/gets instance method map
|
|
13
|
-
attr_accessor :
|
|
14
|
-
|
|
15
|
-
# This accessor sets/gets module method map (XXX: not used)
|
|
16
|
-
attr_accessor :mod_meths
|
|
13
|
+
attr_accessor :meth_type_map # method name => method type
|
|
17
14
|
|
|
18
15
|
def initialize()
|
|
19
|
-
@
|
|
20
|
-
@mod_meths = {}
|
|
16
|
+
@meth_type_map = {}
|
|
21
17
|
end
|
|
22
18
|
|
|
23
19
|
end
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# this type system is to give a readable type signature to every method in
|
|
6
6
|
# designated modules and classes.
|
|
7
7
|
|
|
8
|
+
require_relative "util"
|
|
8
9
|
require_relative "object_wrapper"
|
|
9
10
|
require_relative "type_placeholder"
|
|
10
11
|
require_relative "../type"
|
|
@@ -104,26 +105,36 @@ module RubyBreaker
|
|
|
104
105
|
# the method type to a method list type.
|
|
105
106
|
#
|
|
106
107
|
# obj:: the receive of the method call
|
|
107
|
-
#
|
|
108
|
+
# meth_type_map:: a hash object that maps method names to method types
|
|
108
109
|
# meth_name:: the name of the method being invoked
|
|
109
110
|
# retval:: the return value of the original method call
|
|
110
111
|
# args:: the arguments
|
|
111
112
|
# blk:: the block argument
|
|
112
113
|
#
|
|
113
|
-
def lub(obj,
|
|
114
|
+
def lub(obj, meth_type_map, meth_name, retval, *args, &blk)
|
|
114
115
|
|
|
115
|
-
exist_meth_type =
|
|
116
|
+
exist_meth_type = meth_type_map[meth_name.to_sym]
|
|
116
117
|
|
|
118
|
+
# Again, find the arity
|
|
119
|
+
meth_obj = obj.method(MonitorUtils.get_alt_meth_name(meth_name))
|
|
120
|
+
arity = meth_obj.arity
|
|
121
|
+
|
|
117
122
|
# Construct the newly observed method type first
|
|
118
|
-
new_meth_type = MethodType.new(meth_name)
|
|
119
|
-
args.
|
|
123
|
+
new_meth_type = MethodType.new(meth_name,[])
|
|
124
|
+
args.each_with_index do |arg,idx|
|
|
120
125
|
if is_object_wrapped?(arg)
|
|
121
126
|
arg_type = arg.__rubybreaker_type
|
|
122
127
|
else
|
|
123
128
|
arg_type = NominalType.new(arg.class)
|
|
124
129
|
end
|
|
130
|
+
# Check if the last argument should be a variable length argument
|
|
131
|
+
if arity < 0 && (idx + 1 == arity.abs)
|
|
132
|
+
new_meth_type.arg_types << VarLengthType.new(arg_type)
|
|
133
|
+
break
|
|
134
|
+
end
|
|
125
135
|
new_meth_type.arg_types << arg_type
|
|
126
|
-
|
|
136
|
+
end
|
|
137
|
+
|
|
127
138
|
if (obj == retval)
|
|
128
139
|
# the return value is same as the message receiver. This means the
|
|
129
140
|
# return value has the self type.
|
|
@@ -133,7 +144,7 @@ module RubyBreaker
|
|
|
133
144
|
# Otherwise, construct a nominal type.
|
|
134
145
|
ret_type = NominalType.new(retval.class)
|
|
135
146
|
end
|
|
136
|
-
new_meth_type.ret_type
|
|
147
|
+
new_meth_type.ret_type = ret_type
|
|
137
148
|
|
|
138
149
|
resolved = false
|
|
139
150
|
if exist_meth_type.instance_of?(MethodListType)
|
|
@@ -147,7 +158,7 @@ module RubyBreaker
|
|
|
147
158
|
# Could not resolve the types, so promote the method type to a
|
|
148
159
|
# method list type
|
|
149
160
|
exist_meth_type = MethodListType.new([exist_meth_type])
|
|
150
|
-
|
|
161
|
+
meth_type_map[meth_name.to_sym] = exist_meth_type
|
|
151
162
|
end
|
|
152
163
|
end
|
|
153
164
|
if !resolved
|
|
@@ -161,8 +172,10 @@ module RubyBreaker
|
|
|
161
172
|
# each argument with the object wrapper.
|
|
162
173
|
def before_method(obj, meth_info)
|
|
163
174
|
|
|
164
|
-
|
|
165
|
-
|
|
175
|
+
is_obj_mod = (obj.class == Class or obj.class == Module)
|
|
176
|
+
mod = is_obj_mod ? Runtime.eigen_class(obj) : obj.class
|
|
177
|
+
|
|
178
|
+
meth_type_map = Breakable::TYPE_PLACEHOLDER_MAP[mod].meth_type_map
|
|
166
179
|
|
|
167
180
|
# Let's take things out of the MethodInfo object
|
|
168
181
|
meth_name = meth_info.meth_name
|
|
@@ -181,7 +194,7 @@ module RubyBreaker
|
|
|
181
194
|
|
|
182
195
|
Debug.msg("In module monitor_before #{meth_name}")
|
|
183
196
|
|
|
184
|
-
meth_type =
|
|
197
|
+
meth_type = meth_type_map[meth_name]
|
|
185
198
|
|
|
186
199
|
if meth_type
|
|
187
200
|
# This means the method type has been created previously.
|
|
@@ -193,10 +206,22 @@ module RubyBreaker
|
|
|
193
206
|
# No method type has been created for this method yet. Create a
|
|
194
207
|
# blank method type (where each argument type, block type, and
|
|
195
208
|
# return type are all nil).
|
|
196
|
-
|
|
197
|
-
|
|
209
|
+
#
|
|
210
|
+
# First, use the orignal method's arity to find out # of
|
|
211
|
+
# arguments.
|
|
212
|
+
meth_obj = obj.method(MonitorUtils.get_alt_meth_name(meth_name))
|
|
213
|
+
arity = meth_obj.arity
|
|
214
|
+
arg_types = [nil] * meth_obj.arity.abs
|
|
215
|
+
if blk
|
|
216
|
+
# Do the same for the block too if there is one
|
|
217
|
+
blk_arity = blk.arity
|
|
218
|
+
blk_arg_types = [nil] * blk_artiy.abs
|
|
219
|
+
blk_type = BlockType.new(blk_arg_types, nil, nil)
|
|
220
|
+
else
|
|
221
|
+
blk_type = nil
|
|
222
|
+
end
|
|
198
223
|
meth_type = MethodType.new(meth_name, arg_types, blk_type, nil)
|
|
199
|
-
|
|
224
|
+
meth_type_map[meth_name] = meth_type
|
|
200
225
|
end
|
|
201
226
|
|
|
202
227
|
meth_info.args = args
|
|
@@ -206,7 +231,10 @@ module RubyBreaker
|
|
|
206
231
|
# This method occurs after every "monitored" method call. It updates
|
|
207
232
|
# the type information.
|
|
208
233
|
def after_method(obj, meth_info)
|
|
209
|
-
|
|
234
|
+
|
|
235
|
+
is_obj_mod = (obj.class == Class or obj.class == Module)
|
|
236
|
+
mod = is_obj_mod ? Runtime.eigen_class(obj) : obj.class
|
|
237
|
+
|
|
210
238
|
# Take things out
|
|
211
239
|
meth_name = meth_info.meth_name
|
|
212
240
|
retval = meth_info.ret
|
|
@@ -215,10 +243,18 @@ module RubyBreaker
|
|
|
215
243
|
|
|
216
244
|
Debug.msg("In module monitor_after #{meth_name}")
|
|
217
245
|
|
|
218
|
-
|
|
246
|
+
meth_type_map = Breakable::TYPE_PLACEHOLDER_MAP[mod].meth_type_map
|
|
219
247
|
|
|
220
248
|
# Compute the least upper bound
|
|
221
|
-
lub(obj,
|
|
249
|
+
lub(obj, meth_type_map,meth_name,retval,*args,&blk)
|
|
250
|
+
|
|
251
|
+
if obj == retval
|
|
252
|
+
# It is possible that the method receiver is a wrapped object if
|
|
253
|
+
# it is an argument to a method in the current call stack. So this
|
|
254
|
+
# check is to return the wrapped object and not the stripped off
|
|
255
|
+
# version. (Remember, == is overridden for the wrapped object.)
|
|
256
|
+
meth_info.ret = obj
|
|
257
|
+
end
|
|
222
258
|
|
|
223
259
|
end
|
|
224
260
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#-
|
|
2
|
+
# This file contains utility functions that are useful for the Runtime
|
|
3
|
+
# Library.
|
|
4
|
+
|
|
5
|
+
module RubyBreaker
|
|
6
|
+
|
|
7
|
+
module Runtime
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
# This returns the eigen class of the given module.
|
|
12
|
+
def self.eigen_class(mod)
|
|
13
|
+
return mod.module_eval("class << self; self end")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
data/lib/rubybreaker/runtime.rb
CHANGED
|
@@ -10,6 +10,18 @@ require_relative "runtime/inspector"
|
|
|
10
10
|
|
|
11
11
|
module RubyBreaker
|
|
12
12
|
|
|
13
|
+
# Broken takes higher precedence than Breakable. Once a module is
|
|
14
|
+
# "declared" to be Broken, it cannot be Breakable.
|
|
15
|
+
#
|
|
16
|
+
# TODO: In future, there will be a hybrid of two to allow documenting of
|
|
17
|
+
# methods that are newly introduced in a broken class/module.
|
|
18
|
+
|
|
19
|
+
# This array lists modules/classes that will be monitored.
|
|
20
|
+
BREAKABLE = []
|
|
21
|
+
|
|
22
|
+
# This array lists "broken" classes--i.e., with type signatures
|
|
23
|
+
BROKEN = []
|
|
24
|
+
|
|
13
25
|
# This module should be included in classes or modules that you want to
|
|
14
26
|
# monitor during runtime. The concept is that once a Breakable module is
|
|
15
27
|
# monitored and its type documentation is generated, the module now becomes
|
|
@@ -21,10 +33,11 @@ module RubyBreaker
|
|
|
21
33
|
TYPE_PLACEHOLDER_MAP = {} # module => type_placeholder
|
|
22
34
|
MONITOR_MAP = {} # module => monitor
|
|
23
35
|
|
|
24
|
-
#
|
|
25
|
-
#
|
|
36
|
+
# Simply keep track of this module and its eigen class so they are
|
|
37
|
+
# monitored later on.
|
|
26
38
|
def self.included(mod)
|
|
27
39
|
BREAKABLE << mod
|
|
40
|
+
BREAKABLE << Runtime.eigen_class(mod)
|
|
28
41
|
end
|
|
29
42
|
|
|
30
43
|
end
|
|
@@ -37,8 +50,9 @@ module RubyBreaker
|
|
|
37
50
|
|
|
38
51
|
TYPE_PLACEHOLDER_MAP = {} # module => type_placeholder
|
|
39
52
|
|
|
40
|
-
|
|
41
|
-
#
|
|
53
|
+
#-
|
|
54
|
+
# This module will be "extended" to the eigen class of the class that
|
|
55
|
+
# includes Broken module. This allows the eigen class to call 'typesig'
|
|
42
56
|
# method to parse the type signature dynamically.
|
|
43
57
|
#
|
|
44
58
|
# Usage:
|
|
@@ -49,28 +63,32 @@ module RubyBreaker
|
|
|
49
63
|
# def foo(x) ... end
|
|
50
64
|
# end
|
|
51
65
|
#
|
|
52
|
-
module
|
|
66
|
+
module BrokenEigen
|
|
53
67
|
|
|
54
68
|
include TypeDefs
|
|
55
69
|
include Runtime
|
|
56
70
|
|
|
57
|
-
# This method can be used at the
|
|
71
|
+
# This method can be used at the eigen level of the target module to
|
|
58
72
|
# specify the type of a method.
|
|
59
73
|
def typesig(str)
|
|
74
|
+
|
|
75
|
+
# This MUST BE set for self type to work in type signatures.
|
|
76
|
+
TypeDefs::SelfType.set_self(self)
|
|
77
|
+
|
|
60
78
|
t = TypeSigParser.parse(str)
|
|
61
79
|
placeholder = TYPE_PLACEHOLDER_MAP[self]
|
|
62
80
|
if placeholder
|
|
63
|
-
meth_type = placeholder.
|
|
81
|
+
meth_type = placeholder.meth_type_map[t.meth_name]
|
|
64
82
|
if meth_type
|
|
65
83
|
# TODO: make a method list
|
|
66
84
|
if meth_type.instance_of?(MethodListType)
|
|
67
85
|
meth_type.types << t
|
|
68
86
|
else
|
|
69
87
|
# then upgrade it
|
|
70
|
-
placeholder.
|
|
88
|
+
placeholder.meth_type_map[t.meth_name] = MethodListType.new([meth_type, t])
|
|
71
89
|
end
|
|
72
90
|
else
|
|
73
|
-
placeholder.
|
|
91
|
+
placeholder.meth_type_map[t.meth_name] = t
|
|
74
92
|
end
|
|
75
93
|
end
|
|
76
94
|
return t
|
|
@@ -79,23 +97,32 @@ module RubyBreaker
|
|
|
79
97
|
end
|
|
80
98
|
|
|
81
99
|
# This method is triggered when Broken module is included. This just
|
|
82
|
-
# extends
|
|
83
|
-
# called from the
|
|
100
|
+
# extends BrokenEigen into the target module so "typesig" method can be
|
|
101
|
+
# called from the eigen level of the module. It also extends the eigen
|
|
102
|
+
# class of the target module so that "typesig" can work for class
|
|
103
|
+
# methods too.
|
|
84
104
|
def self.included(mod)
|
|
85
105
|
|
|
86
106
|
# Add to the list of broken modules
|
|
87
107
|
BROKEN << mod
|
|
88
108
|
|
|
89
|
-
# This MUST BE set for self type to work in type signatures
|
|
90
|
-
SelfType.set_self(mod)
|
|
91
|
-
|
|
92
109
|
# Create if there is no type placeholder for this module yet
|
|
93
110
|
placeholder = TYPE_PLACEHOLDER_MAP[mod]
|
|
94
111
|
if !placeholder
|
|
95
112
|
placeholder = TypePlaceholder.new()
|
|
96
113
|
TYPE_PLACEHOLDER_MAP[mod] = placeholder
|
|
97
114
|
end
|
|
98
|
-
mod.extend(
|
|
115
|
+
mod.extend(BrokenEigen)
|
|
116
|
+
|
|
117
|
+
# Support up to one eigen level to support class methods
|
|
118
|
+
eigen_class = Runtime.eigen_class(mod)
|
|
119
|
+
BROKEN << eigen_class
|
|
120
|
+
placeholder = TYPE_PLACEHOLDER_MAP[eigen_class]
|
|
121
|
+
if !placeholder
|
|
122
|
+
placeholder = TypePlaceholder.new()
|
|
123
|
+
TYPE_PLACEHOLDER_MAP[eigen_class] = placeholder
|
|
124
|
+
end
|
|
125
|
+
eigen_class.extend(BrokenEigen)
|
|
99
126
|
end
|
|
100
127
|
|
|
101
128
|
end
|
|
@@ -53,7 +53,7 @@ module RubyBreaker
|
|
|
53
53
|
return is_equal
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
# This method determines if the type exists in the given type list.
|
|
57
57
|
def self.type_in_types?(t, types)
|
|
58
58
|
exist = false
|
|
59
59
|
types.each do |t2|
|
|
@@ -67,8 +67,8 @@ module RubyBreaker
|
|
|
67
67
|
|
|
68
68
|
# This method compares two OR or MethodListType. The order of inner
|
|
69
69
|
# types do not matter.
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
#
|
|
71
|
+
# XXX: Should the order not matter really?
|
|
72
72
|
def self.or_compare(lhs,rhs)
|
|
73
73
|
is_equal = false
|
|
74
74
|
if lhs.class == rhs.class && lhs.types.size == rhs.types.size
|
|
@@ -83,7 +83,7 @@ module RubyBreaker
|
|
|
83
83
|
return is_equal
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
# This method compares a method list to another method list.
|
|
87
87
|
def self.meth_list_compare(lhs, rhs)
|
|
88
88
|
return self.or_compare(lhs, rhs)
|
|
89
89
|
end
|
|
@@ -99,8 +99,8 @@ module RubyBreaker
|
|
|
99
99
|
is_equal = false
|
|
100
100
|
elsif lhs.instance_of?(NominalType)
|
|
101
101
|
is_equal = (lhs.mod == rhs.mod)
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
elsif lhs.instance_of?(SelfType)
|
|
103
|
+
is_equal = rhs.instance_of?(SelfType)
|
|
104
104
|
elsif lhs.instance_of?(DuckType)
|
|
105
105
|
is_equal = duck_compare(lhs,rhs)
|
|
106
106
|
elsif lhs.instance_of?(FusionType)
|
|
@@ -120,11 +120,11 @@ module RubyBreaker
|
|
|
120
120
|
elsif lhs.instance_of?(OrType)
|
|
121
121
|
is_equal = self.or_compare(lhs,rhs)
|
|
122
122
|
elsif lhs.instance_of?(VarLengthType)
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
is_equal = rhs.instance_of?(VarLengthType) &&
|
|
124
|
+
self.compare(lhs.type, rhs.type)
|
|
125
125
|
elsif lhs.instance_of?(OptionalType)
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
is_equal = rhs.instance_of?(OptionalType) &&
|
|
127
|
+
self.compare(lhs.type, rhs.type)
|
|
128
128
|
else
|
|
129
129
|
is_equal = lhs.class == rhs.class
|
|
130
130
|
end
|