rubybreaker 0.0.1

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 (53) hide show
  1. data/AUTHORS +7 -0
  2. data/LICENSE +26 -0
  3. data/README.md +403 -0
  4. data/Rakefile +90 -0
  5. data/TODO +30 -0
  6. data/bin/gen_stub_rubylib +64 -0
  7. data/bin/rubybreaker +67 -0
  8. data/lib/rubybreaker/context.rb +122 -0
  9. data/lib/rubybreaker/debug.rb +48 -0
  10. data/lib/rubybreaker/error.rb +59 -0
  11. data/lib/rubybreaker/rubylib/core.rb +2316 -0
  12. data/lib/rubybreaker/rubylib.rb +3 -0
  13. data/lib/rubybreaker/runtime/inspector.rb +57 -0
  14. data/lib/rubybreaker/runtime/monitor.rb +235 -0
  15. data/lib/rubybreaker/runtime/object_wrapper.rb +77 -0
  16. data/lib/rubybreaker/runtime/overrides.rb +42 -0
  17. data/lib/rubybreaker/runtime/pluggable.rb +57 -0
  18. data/lib/rubybreaker/runtime/type_placeholder.rb +27 -0
  19. data/lib/rubybreaker/runtime/type_system.rb +228 -0
  20. data/lib/rubybreaker/runtime/typesig_parser.rb +45 -0
  21. data/lib/rubybreaker/runtime.rb +103 -0
  22. data/lib/rubybreaker/test/testcase.rb +39 -0
  23. data/lib/rubybreaker/test.rb +1 -0
  24. data/lib/rubybreaker/type/type.rb +241 -0
  25. data/lib/rubybreaker/type/type_comparer.rb +143 -0
  26. data/lib/rubybreaker/type/type_grammar.treetop +285 -0
  27. data/lib/rubybreaker/type/type_unparser.rb +142 -0
  28. data/lib/rubybreaker/type.rb +2 -0
  29. data/lib/rubybreaker/typing/rubytype.rb +47 -0
  30. data/lib/rubybreaker/typing/subtyping.rb +480 -0
  31. data/lib/rubybreaker/typing.rb +3 -0
  32. data/lib/rubybreaker/util.rb +31 -0
  33. data/lib/rubybreaker.rb +193 -0
  34. data/test/integrated/tc_method_missing.rb +30 -0
  35. data/test/integrated/tc_simple1.rb +77 -0
  36. data/test/runtime/tc_obj_wrapper.rb +73 -0
  37. data/test/runtime/tc_typesig_parser.rb +33 -0
  38. data/test/ts_integrated.rb +4 -0
  39. data/test/ts_runtime.rb +5 -0
  40. data/test/ts_type.rb +5 -0
  41. data/test/ts_typing.rb +4 -0
  42. data/test/type/tc_comparer.rb +211 -0
  43. data/test/type/tc_parser.rb +219 -0
  44. data/test/type/tc_unparser.rb +276 -0
  45. data/test/typing/tc_rubytype.rb +63 -0
  46. data/test/typing/tc_typing.rb +219 -0
  47. data/webpage/footer.html +5 -0
  48. data/webpage/generated_toc.js +319 -0
  49. data/webpage/header.html +14 -0
  50. data/webpage/images/logo.png +0 -0
  51. data/webpage/index.html +439 -0
  52. data/webpage/rubybreaker.css +53 -0
  53. metadata +119 -0
@@ -0,0 +1,3 @@
1
+
2
+ require_relative "rubylib/core"
3
+
@@ -0,0 +1,57 @@
1
+ #--
2
+ # This file defines Inspector which finds the type placeholder for a
3
+ # module.
4
+
5
+ require_relative "monitor"
6
+
7
+ module RubyBreaker
8
+
9
+ module Runtime
10
+
11
+ # This module inspects a Breakable module and retrieves type information
12
+ # if there is any.
13
+ module Inspector
14
+
15
+ # This method inspects the module for specified method name. It
16
+ # returns the method type or method list type for the given method. If
17
+ # no method exists or if there is no type information for the method,
18
+ # it returns nil
19
+ def self.inspect_meth(mod, mname)
20
+ mname = mname.to_sym
21
+ if mod.included_modules.include?(Breakable)
22
+ placeholder = Breakable::TYPE_PLACEHOLDER_MAP[mod]
23
+ elsif mod.included_modules.include?(Broken)
24
+ placeholder = Broken::TYPE_PLACEHOLDER_MAP[mod]
25
+ else
26
+ # TODO
27
+ end
28
+ t = placeholder.inst_meths[mname] if placeholder
29
+ return t
30
+ end
31
+
32
+ # Similar to inspect_meth but returns a hash of (mname, mtype) pairs.
33
+ def self.inspect_meths(mod, mnames)
34
+ mtype_hash = {}
35
+ mnames.each {|mname|
36
+ mtype_hash[mname] = self.inspect_meth(mod, mname)
37
+ }
38
+ return mtype_hash
39
+ end
40
+
41
+ # This method inspects the module for all methods. It returns a Hash
42
+ # containing (method name, method type) pairs.
43
+ def self.inspect_all(mod)
44
+ mtypes = {}
45
+ 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
+ }
50
+ return mtypes
51
+ end
52
+
53
+
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,235 @@
1
+ #--
2
+ # This file contains the core of the runtime framework which injects the
3
+ # monitoring code into Breakable classes/modules and actually monitors the
4
+ # instances of those classes/modules at runtime. It also provides some utility
5
+ # functions for later use (after runtime).
6
+
7
+ dir = File.dirname(__FILE__)
8
+ require_relative "type_placeholder"
9
+ require_relative "../context"
10
+ require_relative "../debug"
11
+ require_relative "pluggable"
12
+ require_relative "type_system"
13
+
14
+ module RubyBreaker
15
+
16
+ module Runtime
17
+
18
+ DEFAULT_TYPE_SYSTEM = TypeSystem.new
19
+
20
+ # This class monitors method calls before and after. It simply reroutes
21
+ # the responsibility to the appropriate pluggable type system which does
22
+ # the actual work of gathering type information.
23
+ class Monitor
24
+
25
+ # attr_accessor :mod
26
+ attr_accessor :pluggable
27
+
28
+ public
29
+
30
+ def initialize(mod, pluggable)
31
+ # @mod = mod
32
+ @pluggable = pluggable
33
+ end
34
+
35
+ # Starts monitoring of a method; it wraps each argument so that they
36
+ # can gather type information in the callee.
37
+ def monitor_before_method(obj, meth_info)
38
+ @pluggable.before_method(obj, meth_info)
39
+ end
40
+
41
+ # This method is invoked after the actual method is invoked.
42
+ def monitor_after_method(obj, meth_info)
43
+ @pluggable.after_method(obj, meth_info)
44
+ end
45
+
46
+ end
47
+
48
+ # This class is a switch to turn on and off the type monitoring system.
49
+ # It is important to turn off the monitor once the process is inside the
50
+ # monitor; otherwise, it WILL fall into an infinite loop.
51
+ class MonitorSwitch
52
+
53
+ attr_accessor :switch
54
+
55
+ def initialize(); @switch = true end
56
+
57
+ def turn_on();
58
+ Debug.msg("Switch turned on")
59
+ @switch = true;
60
+ end
61
+
62
+ def turn_off();
63
+ Debug.msg("Switch turned off")
64
+ @switch = false;
65
+ end
66
+
67
+ def set_to(mode); @switch = mode; end
68
+ end
69
+
70
+ # TODO:For now, we use a global switch; but in future, a switch per
71
+ # object should be used for multi-process apps. However, there is still
72
+ # a concern for module tracking in which case, there isn't really a way
73
+ # to do this unless we track with the process or some unique id for that
74
+ # process.
75
+ GLOBAL_MONITOR_SWITCH = MonitorSwitch.new
76
+
77
+ # This context is used for keeping track of context in the user code.
78
+ # This will ignore the context within the RubyBreaker code, so it is
79
+ # easy to pinpoint program locations without distraction from the
80
+ # RubyBreaker code.
81
+ CONTEXT = Context.new(ObjectPosition.new(self,"main"))
82
+
83
+ # This module contains helper methods for monitoring objects and
84
+ # modules.
85
+ module MonitorUtils
86
+
87
+ public
88
+
89
+ # This will do the actual routing work for a particular "monitored"
90
+ # method call.
91
+ #
92
+ # obj:: is the object receiving the message; is never wrapped object
93
+ # meth_name:: is the original method name being called args:: is a
94
+ # list of arguments for the original method call blk:: is the block
95
+ # argument for the original method call
96
+ #
97
+ #--
98
+ # NOTE: This method should not assume that obj is a monitored
99
+ # object. That is, no special method should be called to obj unless it
100
+ # checks first.
101
+ def self.route(obj,meth_name,*args,&blk)
102
+
103
+ # remember the switch mode before turning it off
104
+ switch = GLOBAL_MONITOR_SWITCH.switch
105
+
106
+ # turn off the monitor so we do not fall into an infinite loop
107
+ GLOBAL_MONITOR_SWITCH.turn_off()
108
+
109
+ # use symbol instead of string throughout this code
110
+ meth_name = :"#{meth_name}"
111
+
112
+ # first, get the context right
113
+ # notice the argument 2 to the caller!
114
+ #
115
+ # CONTEXT.push(obj, meth_name,
116
+ # Position.convert_caller_to_pos(caller(2)))
117
+ CONTEXT.push(Position.convert_caller_to_pos(caller(2)))
118
+
119
+ # this is what the renamed method
120
+ stub_meth_name = get_alt_meth_name(meth_name)
121
+
122
+ Debug.msg("Route to #{stub_meth_name}",CONTEXT)
123
+
124
+ # short-circuit if switch was off--i.e., no monitoring
125
+ if !switch
126
+ retval = obj.send(stub_meth_name.to_sym,*args,&blk)
127
+ CONTEXT.pop() # do not forget to pop the context before returning
128
+ return retval
129
+ end
130
+
131
+ 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
138
+
139
+ # mm = get_module_monitor(mod) unless is_obj_mod
140
+ mm = Breakable::MONITOR_MAP[mod] if !is_obj_mod
141
+
142
+ # There is something wrong if there isn't a module monitor
143
+ # associated with the call.
144
+ # raise Exception if mm == nil || !mm.inst_meths.include?(meth_name)
145
+
146
+ meth_info = MethodInfo.new(meta, meth_name, args, blk, nil)
147
+
148
+ mm.monitor_before_method(obj, meth_info)
149
+
150
+ Debug.msg("monitor_before_method ended")
151
+
152
+ # we are going to turn the switch back on
153
+ GLOBAL_MONITOR_SWITCH.turn_on()
154
+
155
+ # call the original method which was renamed
156
+ retval = obj.send(stub_meth_name.to_sym, *meth_info.args,
157
+ &meth_info.blk)
158
+
159
+ # turn it off
160
+ GLOBAL_MONITOR_SWITCH.turn_off()
161
+
162
+ meth_info.ret = retval
163
+ mm.monitor_after_method(obj, meth_info)
164
+
165
+ # things are done in this context. pop it off.
166
+ CONTEXT.pop()
167
+
168
+ # it is always the case that the switch was off when this particular
169
+ # call was made. (Otherwise, it would have quit somewhere above
170
+ GLOBAL_MONITOR_SWITCH.turn_on()
171
+
172
+ return retval # always return the return value
173
+ end
174
+
175
+ # This method returns the alternative (renamed) method name
176
+ def self.get_alt_meth_name(meth_name)
177
+ return "__#{meth_name}"
178
+ end
179
+
180
+ # This method returns the original method name
181
+ def self.get_orig_meth_name(meth_name)
182
+ return meth_name[2..-1]
183
+ end
184
+
185
+ end
186
+
187
+ # This module installs a monitor in the object.
188
+ module MonitorInstaller
189
+
190
+ include MonitorUtils
191
+
192
+ # returns true if the receiver is a module or a class
193
+ def self.is_module?(recv)
194
+ return recv.respond_to?(:class) && recv.kind_of?(Module)
195
+ end
196
+
197
+ # renames the method in essence; this method also "installs" the
198
+ # module monitor for the class
199
+ def self.rename_meth(recv,meth_name)
200
+ alt_meth_name = MonitorUtils.get_alt_meth_name(meth_name)
201
+ recv.module_eval("alias :\"#{alt_meth_name}\" :\"#{meth_name}\"")
202
+ Debug.msg("Adding alternate method for #{meth_name}")
203
+ recv.module_eval <<-EOF
204
+ def #{meth_name}(*args, &blk)
205
+ RubyBreaker::Runtime::MonitorUtils.route(self,
206
+ "#{meth_name}",
207
+ *args,
208
+ &blk)
209
+ end
210
+ EOF
211
+ end
212
+
213
+ # Installs an module (class) monitor to the object.
214
+ def self.install_module_monitor(mod,infer=false)
215
+ 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)
222
+ meths.each do |m|
223
+ self.rename_meth(mod,m)
224
+ end
225
+ Debug.feed_line()
226
+ end
227
+
228
+ def self.report(mod)
229
+ end
230
+
231
+ end
232
+
233
+ end
234
+ end
235
+
@@ -0,0 +1,77 @@
1
+ #--
2
+ # This program keeps track of every method call to a wrapped object.
3
+
4
+ module RubyBreaker
5
+
6
+ module Runtime
7
+
8
+ # This constant is used to determine if an object is a wrapped object.
9
+ WRAPPED_INDICATOR = :"__is_wrapped?__"
10
+
11
+ # This class represents the shell object that wraps around another
12
+ # object. Note that it is a subclass of BasicObject to keep it really
13
+ # concise. It also redirects the following methods (from BasicObject):
14
+ #
15
+ # !, !=, ==, equal?, eql?, __id__, object_id,
16
+ # send, __send__, instance_eval, instance_exec
17
+ #
18
+ class ObjectWrapper < BasicObject
19
+
20
+ def initialize(obj)
21
+ @__rubybreaker_obj = obj
22
+ nom_type = TypeDefs::NominalType.new(obj.class)
23
+ @__rubybreaker_type = TypeDefs::FusionType.new(nom_type,[])
24
+ end
25
+
26
+ # This method returns the original object.
27
+ def __rubybreaker_obj()
28
+ return @__rubybreaker_obj
29
+ end
30
+
31
+ # This method returns the type gathered so far for this object.
32
+ def __rubybreaker_type()
33
+ return @__rubybreaker_type
34
+ end
35
+
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.
40
+ [:"!", :"!=", :"==", :"equal?", :"eql?", :"__id__", :"object_id",
41
+ :"send", :"__send__", :"instance_eval",
42
+ :"instance_exec"].each do |meth|
43
+
44
+ eval <<-EOS
45
+
46
+ def #{meth}(*args,&blk)
47
+ self.method_missing(:"#{meth}", *args, &blk)
48
+ end
49
+
50
+ EOS
51
+
52
+ end
53
+
54
+ # Only behave differently if it's looking for +WRAPPED_INDICATOR+
55
+ # method
56
+ def respond_to?(mname)
57
+ return true if mname.to_sym == WRAPPED_INDICATOR
58
+ return @__rubybreaker_obj.respond_to?(mname)
59
+ end
60
+
61
+ # This method missing method redirects all other method calls.
62
+ def method_missing(mname,*args,&blk)
63
+ Debug.msg("Method_missing for #{mname}")
64
+ if GLOBAL_MONITOR_SWITCH.switch
65
+ @__rubybreaker_type.add_meth(mname)
66
+ retval = @__rubybreaker_obj.send(mname,*args,&blk)
67
+ retval = ObjectWrapper.new(retval)
68
+ else
69
+ retval = @__rubybreaker_obj.send(mname,*args,&blk)
70
+ end
71
+ return retval
72
+ end
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,42 @@
1
+ #--
2
+ # This file contains methods that need to override the existing ones to
3
+ # accommodate the object wrapper.
4
+
5
+ require_relative "object_wrapper"
6
+
7
+ module RubyBreaker
8
+
9
+ module Runtime
10
+
11
+ # This constant holds the string used internally by RubyBreaker to
12
+ # indicate overridden methods.
13
+ OVERRIDE_PREFIX = "__rubybreaker_"
14
+
15
+ end
16
+
17
+ end
18
+
19
+
20
+ class Numeric
21
+
22
+ [:"==", :equal?, :eql?].each do |m|
23
+
24
+ eval <<-EOS
25
+
26
+ alias :"#{RubyBreaker::Runtime::OVERRIDE_PREFIX}#{m}" :"#{m}"
27
+ def #{m}(other)
28
+ if other.respond_to?(RubyBreaker::Runtime::WRAPPED_INDICATOR)
29
+ other = other.__rubybreaker_obj
30
+ end
31
+ return self.send(:"#{RubyBreaker::Runtime::OVERRIDE_PREFIX}#{m}", other)
32
+ end
33
+
34
+ EOS
35
+
36
+ end
37
+
38
+ end
39
+
40
+
41
+
42
+
@@ -0,0 +1,57 @@
1
+ #--
2
+ # This file contains a module that explains what is eligible to be a
3
+ # pluggable type system (or anything really). (Think of AOP but for metod
4
+ # calls only.)
5
+
6
+ module RubyBreaker
7
+
8
+ module Runtime
9
+
10
+ # This class has information (and data) of the method being called. Used
11
+ # by Pluggable and Monitor
12
+ class MethodInfo
13
+
14
+ attr_accessor :meta
15
+ attr_accessor :meth_name
16
+ attr_accessor :args
17
+ attr_accessor :blk
18
+ attr_accessor :ret
19
+
20
+ def initialize(meta, meth_name, args, blk, ret)
21
+ @meta = meta
22
+ @meth_name = meth_name
23
+ @args = args
24
+ @blk = blk
25
+ @ret = ret
26
+ end
27
+
28
+ end
29
+
30
+ # Any Pluggable module can be "plugged" into the RubyBreaker monitoring
31
+ # system. For example, if you write your own type system for
32
+ # RubyBreaker, you can include this module to use it instead of the
33
+ # default type system that comes with RubyBreaker.
34
+ module Pluggable
35
+
36
+ # This method will be invoked right before the actual method is
37
+ # invoked.
38
+ #
39
+ # obj:: the receiver of the method call (message)
40
+ # method_info:: a MethodInfo object containing the method call
41
+ # information
42
+ def before_method_call(obj, meth_info)
43
+ end
44
+
45
+ # This method will be invoked right after the actual method is
46
+ # invoked.
47
+ #
48
+ # obj:: the receiver of the method call (message)
49
+ # method_info:: a MethodInfo object containing the method call
50
+ # information
51
+ def after_method_call(obj, meth_info)
52
+ end
53
+
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,27 @@
1
+ #--
2
+ # This file defines the placeholder for types which will be tracked by
3
+ # two (global) constant--one for Breakable and the other for Broken.
4
+
5
+ module RubyBreaker
6
+
7
+ module Runtime
8
+
9
+ # This class is a placeholder for method types
10
+ class TypePlaceholder
11
+
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
17
+
18
+ def initialize()
19
+ @inst_meths = {}
20
+ @mod_meths = {}
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end