rubybreaker 0.0.1 → 0.0.2

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