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.
- data/AUTHORS +7 -0
- data/LICENSE +26 -0
- data/README.md +403 -0
- data/Rakefile +90 -0
- data/TODO +30 -0
- data/bin/gen_stub_rubylib +64 -0
- data/bin/rubybreaker +67 -0
- data/lib/rubybreaker/context.rb +122 -0
- data/lib/rubybreaker/debug.rb +48 -0
- data/lib/rubybreaker/error.rb +59 -0
- data/lib/rubybreaker/rubylib/core.rb +2316 -0
- data/lib/rubybreaker/rubylib.rb +3 -0
- data/lib/rubybreaker/runtime/inspector.rb +57 -0
- data/lib/rubybreaker/runtime/monitor.rb +235 -0
- data/lib/rubybreaker/runtime/object_wrapper.rb +77 -0
- data/lib/rubybreaker/runtime/overrides.rb +42 -0
- data/lib/rubybreaker/runtime/pluggable.rb +57 -0
- data/lib/rubybreaker/runtime/type_placeholder.rb +27 -0
- data/lib/rubybreaker/runtime/type_system.rb +228 -0
- data/lib/rubybreaker/runtime/typesig_parser.rb +45 -0
- data/lib/rubybreaker/runtime.rb +103 -0
- data/lib/rubybreaker/test/testcase.rb +39 -0
- data/lib/rubybreaker/test.rb +1 -0
- data/lib/rubybreaker/type/type.rb +241 -0
- data/lib/rubybreaker/type/type_comparer.rb +143 -0
- data/lib/rubybreaker/type/type_grammar.treetop +285 -0
- data/lib/rubybreaker/type/type_unparser.rb +142 -0
- data/lib/rubybreaker/type.rb +2 -0
- data/lib/rubybreaker/typing/rubytype.rb +47 -0
- data/lib/rubybreaker/typing/subtyping.rb +480 -0
- data/lib/rubybreaker/typing.rb +3 -0
- data/lib/rubybreaker/util.rb +31 -0
- data/lib/rubybreaker.rb +193 -0
- data/test/integrated/tc_method_missing.rb +30 -0
- data/test/integrated/tc_simple1.rb +77 -0
- data/test/runtime/tc_obj_wrapper.rb +73 -0
- data/test/runtime/tc_typesig_parser.rb +33 -0
- data/test/ts_integrated.rb +4 -0
- data/test/ts_runtime.rb +5 -0
- data/test/ts_type.rb +5 -0
- data/test/ts_typing.rb +4 -0
- data/test/type/tc_comparer.rb +211 -0
- data/test/type/tc_parser.rb +219 -0
- data/test/type/tc_unparser.rb +276 -0
- data/test/typing/tc_rubytype.rb +63 -0
- data/test/typing/tc_typing.rb +219 -0
- data/webpage/footer.html +5 -0
- data/webpage/generated_toc.js +319 -0
- data/webpage/header.html +14 -0
- data/webpage/images/logo.png +0 -0
- data/webpage/index.html +439 -0
- data/webpage/rubybreaker.css +53 -0
- metadata +119 -0
@@ -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
|