rubybreaker 0.0.1

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