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.
Files changed (122) hide show
  1. data/NEWS +48 -0
  2. data/README.md +56 -20
  3. data/Rakefile +9 -34
  4. data/TODO +10 -10
  5. data/VERSION +1 -0
  6. data/bin/gen_stub_rubylib +35 -36
  7. data/bin/rubybreaker +1 -4
  8. data/lib/rubybreaker/debug.rb +8 -4
  9. data/lib/rubybreaker/rubylib/core.rb +738 -571
  10. data/lib/rubybreaker/runtime/inspector.rb +16 -7
  11. data/lib/rubybreaker/runtime/monitor.rb +14 -18
  12. data/lib/rubybreaker/runtime/object_wrapper.rb +9 -3
  13. data/lib/rubybreaker/runtime/overrides.rb +51 -8
  14. data/lib/rubybreaker/runtime/pluggable.rb +1 -3
  15. data/lib/rubybreaker/runtime/type_placeholder.rb +2 -6
  16. data/lib/rubybreaker/runtime/type_system.rb +53 -17
  17. data/lib/rubybreaker/runtime/typesig_parser.rb +1 -0
  18. data/lib/rubybreaker/runtime/util.rb +18 -0
  19. data/lib/rubybreaker/runtime.rb +42 -15
  20. data/lib/rubybreaker/type/type_comparer.rb +10 -10
  21. data/lib/rubybreaker/type/type_grammar.treetop +30 -21
  22. data/lib/rubybreaker/type/type_unparser.rb +2 -2
  23. data/lib/rubybreaker/typing/subtyping.rb +21 -21
  24. data/lib/rubybreaker/util.rb +11 -1
  25. data/lib/rubybreaker.rb +75 -54
  26. data/test/integrated/tc_class_methods.rb +35 -0
  27. data/test/integrated/tc_inherit_broken.rb +29 -0
  28. data/test/integrated/tc_method_missing.rb +1 -1
  29. data/test/runtime/tc_obj_wrapper.rb +104 -4
  30. data/test/ts_integrated.rb +2 -0
  31. data/test/type/tc_comparer.rb +96 -96
  32. data/test/type/tc_parser.rb +18 -0
  33. data/test/type/tc_unparser.rb +16 -0
  34. data/test/typing/tc_typing.rb +20 -20
  35. data/webpage/footer.html +1 -1
  36. data/webpage/header.html +7 -7
  37. data/webpage/index.html +65 -28
  38. data/webpage/rdoc/RubyBreaker/Breakable.html +280 -0
  39. data/webpage/rdoc/RubyBreaker/Broken/BrokenEigen.html +304 -0
  40. data/webpage/rdoc/RubyBreaker/Broken.html +308 -0
  41. data/webpage/rdoc/RubyBreaker/Context.html +421 -0
  42. data/webpage/rdoc/RubyBreaker/Debug.html +411 -0
  43. data/webpage/rdoc/RubyBreaker/Errors/InternalError.html +263 -0
  44. data/webpage/rdoc/RubyBreaker/Errors/InvalidSubtypeCheck.html +263 -0
  45. data/webpage/rdoc/RubyBreaker/Errors/InvalidTypeConstruction.html +214 -0
  46. data/webpage/rdoc/RubyBreaker/Errors/SubtypeFailure.html +212 -0
  47. data/webpage/rdoc/RubyBreaker/Errors/TypeError.html +212 -0
  48. data/webpage/rdoc/RubyBreaker/Errors/UserError.html +264 -0
  49. data/webpage/rdoc/RubyBreaker/Errors.html +209 -0
  50. data/webpage/rdoc/RubyBreaker/Kernel.html +259 -0
  51. data/webpage/rdoc/RubyBreaker/Main.html +560 -0
  52. data/webpage/rdoc/RubyBreaker/ObjectPosition.html +334 -0
  53. data/webpage/rdoc/RubyBreaker/Position.html +463 -0
  54. data/webpage/rdoc/RubyBreaker/RubyTypeUtils.html +308 -0
  55. data/webpage/rdoc/RubyBreaker/Runtime/Inspector.html +380 -0
  56. data/webpage/rdoc/RubyBreaker/Runtime/MethodInfo.html +324 -0
  57. data/webpage/rdoc/RubyBreaker/Runtime/Monitor.html +354 -0
  58. data/webpage/rdoc/RubyBreaker/Runtime/MonitorInstaller.html +379 -0
  59. data/webpage/rdoc/RubyBreaker/Runtime/MonitorSwitch.html +382 -0
  60. data/webpage/rdoc/RubyBreaker/Runtime/MonitorUtils.html +400 -0
  61. data/webpage/rdoc/RubyBreaker/Runtime/ObjectWrapper.html +411 -0
  62. data/webpage/rdoc/RubyBreaker/Runtime/Pluggable.html +305 -0
  63. data/webpage/rdoc/RubyBreaker/Runtime/TypePlaceholder.html +280 -0
  64. data/webpage/rdoc/RubyBreaker/Runtime/TypeSigParser.html +283 -0
  65. data/webpage/rdoc/RubyBreaker/Runtime/TypeSystem.html +630 -0
  66. data/webpage/rdoc/RubyBreaker/Runtime.html +255 -0
  67. data/webpage/rdoc/RubyBreaker/TestCase.html +332 -0
  68. data/webpage/rdoc/RubyBreaker/TypeComparer.html +304 -0
  69. data/webpage/rdoc/RubyBreaker/TypeDefs/AnyType.html +260 -0
  70. data/webpage/rdoc/RubyBreaker/TypeDefs/BlockType.html +310 -0
  71. data/webpage/rdoc/RubyBreaker/TypeDefs/DuckType.html +320 -0
  72. data/webpage/rdoc/RubyBreaker/TypeDefs/FusionType.html +323 -0
  73. data/webpage/rdoc/RubyBreaker/TypeDefs/MethodListType.html +281 -0
  74. data/webpage/rdoc/RubyBreaker/TypeDefs/MethodType.html +282 -0
  75. data/webpage/rdoc/RubyBreaker/TypeDefs/NilType.html +260 -0
  76. data/webpage/rdoc/RubyBreaker/TypeDefs/NominalType.html +282 -0
  77. data/webpage/rdoc/RubyBreaker/TypeDefs/OptionalType.html +281 -0
  78. data/webpage/rdoc/RubyBreaker/TypeDefs/OrType.html +281 -0
  79. data/webpage/rdoc/RubyBreaker/TypeDefs/SelfType.html +329 -0
  80. data/webpage/rdoc/RubyBreaker/TypeDefs/Type.html +409 -0
  81. data/webpage/rdoc/RubyBreaker/TypeDefs/VarLengthType.html +282 -0
  82. data/webpage/rdoc/RubyBreaker/TypeDefs.html +212 -0
  83. data/webpage/rdoc/RubyBreaker/TypeUnparser.html +273 -0
  84. data/webpage/rdoc/RubyBreaker/Typing.html +305 -0
  85. data/webpage/rdoc/RubyBreaker/Utilities.html +294 -0
  86. data/webpage/rdoc/RubyBreaker.html +337 -0
  87. data/webpage/rdoc/created.rid +26 -0
  88. data/webpage/rdoc/images/add.png +0 -0
  89. data/webpage/rdoc/images/brick.png +0 -0
  90. data/webpage/rdoc/images/brick_link.png +0 -0
  91. data/webpage/rdoc/images/bug.png +0 -0
  92. data/webpage/rdoc/images/bullet_black.png +0 -0
  93. data/webpage/rdoc/images/bullet_toggle_minus.png +0 -0
  94. data/webpage/rdoc/images/bullet_toggle_plus.png +0 -0
  95. data/webpage/rdoc/images/date.png +0 -0
  96. data/webpage/rdoc/images/delete.png +0 -0
  97. data/webpage/rdoc/images/find.png +0 -0
  98. data/webpage/rdoc/images/loadingAnimation.gif +0 -0
  99. data/webpage/rdoc/images/macFFBgHack.png +0 -0
  100. data/webpage/rdoc/images/package.png +0 -0
  101. data/webpage/rdoc/images/page_green.png +0 -0
  102. data/webpage/rdoc/images/page_white_text.png +0 -0
  103. data/webpage/rdoc/images/page_white_width.png +0 -0
  104. data/webpage/rdoc/images/plugin.png +0 -0
  105. data/webpage/rdoc/images/ruby.png +0 -0
  106. data/webpage/rdoc/images/tag_blue.png +0 -0
  107. data/webpage/rdoc/images/tag_green.png +0 -0
  108. data/webpage/rdoc/images/transparent.png +0 -0
  109. data/webpage/rdoc/images/wrench.png +0 -0
  110. data/webpage/rdoc/images/wrench_orange.png +0 -0
  111. data/webpage/rdoc/images/zoom.png +0 -0
  112. data/webpage/rdoc/index.html +165 -0
  113. data/webpage/rdoc/js/darkfish.js +153 -0
  114. data/webpage/rdoc/js/jquery.js +18 -0
  115. data/webpage/rdoc/js/navigation.js +142 -0
  116. data/webpage/rdoc/js/search.js +94 -0
  117. data/webpage/rdoc/js/search_index.js +1 -0
  118. data/webpage/rdoc/js/searcher.js +228 -0
  119. data/webpage/rdoc/rdoc.css +543 -0
  120. data/webpage/rdoc/table_of_contents.html +376 -0
  121. data/webpage/rubybreaker.css +31 -31
  122. 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 mod.included_modules.include?(Breakable)
22
+ if Breakable::TYPE_PLACEHOLDER_MAP.has_key?(mod)
22
23
  placeholder = Breakable::TYPE_PLACEHOLDER_MAP[mod]
23
- elsif mod.included_modules.include?(Broken)
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.inst_meths[mname] if 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
- # mm = MonitorUtils.get_module_monitor(mod)
47
- mm.inst_meths.each_pair {|im,mtype|
48
- mtypes[im] = mtype if mtype
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] if !is_obj_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.inst_meths.include?(meth_name)
140
+ # raise Exception if mm == nil || !mm.meth_type_map.include?(meth_name)
145
141
 
146
- meth_info = MethodInfo.new(meta, meth_name, args, blk, nil)
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,infer=false)
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
- if infer
217
- Breakable::MONITOR_MAP[mod] = Monitor.new(mod, DEFAULT_TYPE_SYSTEM)
218
- Breakable::TYPE_PLACEHOLDER_MAP[mod] = TypePlaceholder.new
219
- end
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 will generate "serious problem" warning. But it's
38
- # ok. This meta programming code re-defines BasicObject's methods to
39
- # redirect to the actual object.
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 = "__rubybreaker_"
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
- class Numeric
29
+ [:"puts", :"putc", :"printf", :print].each do |meth_name|
21
30
 
22
- [:"==", :equal?, :eql?].each do |m|
31
+ mod.module_eval <<-EOS
23
32
 
24
- eval <<-EOS
33
+ alias :"#{RubyBreaker::Runtime::OVERRIDE_PREFIX}_#{mod.object_id}_#{meth_name}" :"#{meth_name}"
25
34
 
26
- alias :"#{RubyBreaker::Runtime::OVERRIDE_PREFIX}#{m}" :"#{m}"
27
- def #{m}(other)
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(:"#{RubyBreaker::Runtime::OVERRIDE_PREFIX}#{m}", other)
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(meta, meth_name, args, blk, ret)
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 :inst_meths # method name => method type
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
- @inst_meths = {}
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
- # inst_meths:: a hash object that maps method names to method types
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, inst_meths, meth_name, retval, *args, &blk)
114
+ def lub(obj, meth_type_map, meth_name, retval, *args, &blk)
114
115
 
115
- exist_meth_type = inst_meths[meth_name.to_sym]
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.each {|arg|
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 = 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
- inst_meths[meth_name.to_sym] = exist_meth_type
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
- mod = obj.class
165
- inst_meths = Breakable::TYPE_PLACEHOLDER_MAP[mod].inst_meths
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 = inst_meths[meth_name]
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
- arg_types = args.map {|arg| nil }
197
- blk_type = blk ? BlockType.new(Array.new(blk.arity), nil, nil) : nil
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
- inst_meths[meth_name] = meth_type
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
- mod = obj.class
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
- inst_meths = Breakable::TYPE_PLACEHOLDER_MAP[mod].inst_meths
246
+ meth_type_map = Breakable::TYPE_PLACEHOLDER_MAP[mod].meth_type_map
219
247
 
220
248
  # Compute the least upper bound
221
- lub(obj, inst_meths,meth_name,retval,*args,&blk)
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
 
@@ -21,6 +21,7 @@ module RubyBreaker
21
21
  # lines in the signature, it will look at each line and construct a
22
22
  # MethodListType to represent the intersection type.
23
23
  def self.parse(str)
24
+
24
25
  meth_types = []
25
26
 
26
27
  # Get caller information and set the global location
@@ -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
@@ -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
- # when this module is included, simply keep track of this module so we
25
- # can start monitoring
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
- # This module will be "extended" to the meta class of the class that
41
- # includes Broken module. This allows the meta class to call 'typesig'
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 BrokenMeta
66
+ module BrokenEigen
53
67
 
54
68
  include TypeDefs
55
69
  include Runtime
56
70
 
57
- # This method can be used at the meta level of the target module to
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.inst_meths[t.meth_name]
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.inst_meths[t.meth_name] = MethodListType.new([meth_type, t])
88
+ placeholder.meth_type_map[t.meth_name] = MethodListType.new([meth_type, t])
71
89
  end
72
90
  else
73
- placeholder.inst_meths[t.meth_name] = t
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 BrokenMeta into the target module so "typesig" method can be
83
- # called from the meta level of the module.
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(BrokenMeta)
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
- # This method determines if the type exists in the given type list.
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
- # XXX: Should the order not matter really?
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
- # This method compares a method list to another method list.
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
- elsif lhs.instance_of?(SelfType)
103
- is_equal = rhs.instance_of?(SelfType)
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
- is_equal = rhs.instance_of?(VarLengthType) &&
124
- self.compare(lhs.type, rhs.type)
123
+ is_equal = rhs.instance_of?(VarLengthType) &&
124
+ self.compare(lhs.type, rhs.type)
125
125
  elsif lhs.instance_of?(OptionalType)
126
- is_equal = rhs.instance_of?(OptionalType) &&
127
- self.compare(lhs.type, rhs.type)
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