rubybreaker 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|