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,228 @@
|
|
1
|
+
#--
|
2
|
+
# This file contains the default type system for RubyBreaker. It uses
|
3
|
+
# subtyping defined in ../typing/subtyping.rb. It is not a constraint-based
|
4
|
+
# type system and does not check type errors statically. The main purpose of
|
5
|
+
# this type system is to give a readable type signature to every method in
|
6
|
+
# designated modules and classes.
|
7
|
+
|
8
|
+
require_relative "object_wrapper"
|
9
|
+
require_relative "type_placeholder"
|
10
|
+
require_relative "../type"
|
11
|
+
require_relative "../typing"
|
12
|
+
|
13
|
+
module RubyBreaker
|
14
|
+
|
15
|
+
module Runtime
|
16
|
+
|
17
|
+
# This is the default type system for RubyBreaker. It can be overridden
|
18
|
+
# by a user specified type system. See +pluggable.rb+ for how this can
|
19
|
+
# be done.
|
20
|
+
class TypeSystem
|
21
|
+
|
22
|
+
include Pluggable # not needed but for the sake of documentation...
|
23
|
+
include TypeDefs
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
# Check if the object is wrapped by a monitor
|
28
|
+
def is_object_wrapped?(obj)
|
29
|
+
return obj.respond_to?(WRAPPED_INDICATOR)
|
30
|
+
end
|
31
|
+
|
32
|
+
# This method is a helper for computing the least upper bound. It
|
33
|
+
# handles the case where existing method type is a method type (and
|
34
|
+
# not a method list type). If there is no compatibility of the two
|
35
|
+
# types, then it returns false.
|
36
|
+
def lub_helper(exist_meth_type, new_meth_type)
|
37
|
+
|
38
|
+
# most restrictive for the given test cases.
|
39
|
+
arg_types = []
|
40
|
+
|
41
|
+
exist_meth_type.arg_types.each_with_index do |exist_arg_type, i|
|
42
|
+
arg_type = nil
|
43
|
+
new_arg_type = new_meth_type.arg_types[i]
|
44
|
+
if !exist_arg_type
|
45
|
+
# nil means there hasn't been any type observed
|
46
|
+
arg_type = new_arg_type
|
47
|
+
elsif new_arg_type.subtype_of?(exist_arg_type)
|
48
|
+
arg_type = exist_arg_type
|
49
|
+
elsif !exist_arg_type.subtype_of?(new_arg_type)
|
50
|
+
# No subtype relation between them, so OR them
|
51
|
+
arg_type = OrType.new([new_arg_type, exist_arg_type])
|
52
|
+
end
|
53
|
+
arg_types << arg_type
|
54
|
+
end
|
55
|
+
|
56
|
+
new_ret_type = new_meth_type.ret_type
|
57
|
+
exist_ret_type = exist_meth_type.ret_type
|
58
|
+
|
59
|
+
if !exist_ret_type
|
60
|
+
ret_type = new_ret_type
|
61
|
+
resolved = true
|
62
|
+
elsif exist_ret_type.subtype_of?(new_ret_type)
|
63
|
+
ret_type = exist_ret_type
|
64
|
+
resolved = true
|
65
|
+
elsif new_ret_type.subtype_of?(exist_ret_type)
|
66
|
+
ret_type = new_ret_type
|
67
|
+
resolved = true
|
68
|
+
else
|
69
|
+
resolved = false
|
70
|
+
end
|
71
|
+
|
72
|
+
if resolved
|
73
|
+
exist_meth_type.arg_types = arg_types
|
74
|
+
exist_meth_type.ret_type = ret_type
|
75
|
+
end
|
76
|
+
|
77
|
+
return resolved
|
78
|
+
end
|
79
|
+
|
80
|
+
# This method computes the least upper bound of the existing method
|
81
|
+
# type and newly observed argument/block/return types. There are a few
|
82
|
+
# cases to consider:
|
83
|
+
#
|
84
|
+
# If the existing type is a method list type, the new observed type
|
85
|
+
# will be either "consolidated" into one of the method types in the
|
86
|
+
# list or added to the list.
|
87
|
+
#
|
88
|
+
# If there is no compatibility between the existing method type and
|
89
|
+
# the observed type, then the method type will be promoted to a method
|
90
|
+
# list type. And the newly observed type will be added to the list.
|
91
|
+
#
|
92
|
+
# For each method type,
|
93
|
+
#
|
94
|
+
# It basically consolidates the existing type information for the
|
95
|
+
# invoked method and the observed type.
|
96
|
+
#
|
97
|
+
# For arguments, we look for most general type that can handle all
|
98
|
+
# types we have seen. This means we find the super type of all types
|
99
|
+
# we have seen (excluding unknown types).
|
100
|
+
#
|
101
|
+
# For return, we look for most specific type that can handle both
|
102
|
+
# types. Therefore, if two types have no subtype relation, we AND
|
103
|
+
# them. But we do not allow AND types in the return type. We must turn
|
104
|
+
# the method type to a method list type.
|
105
|
+
#
|
106
|
+
# obj:: the receive of the method call
|
107
|
+
# inst_meths:: a hash object that maps method names to method types
|
108
|
+
# meth_name:: the name of the method being invoked
|
109
|
+
# retval:: the return value of the original method call
|
110
|
+
# args:: the arguments
|
111
|
+
# blk:: the block argument
|
112
|
+
#
|
113
|
+
def lub(obj, inst_meths, meth_name, retval, *args, &blk)
|
114
|
+
|
115
|
+
exist_meth_type = inst_meths[meth_name.to_sym]
|
116
|
+
|
117
|
+
# Construct the newly observed method type first
|
118
|
+
new_meth_type = MethodType.new(meth_name)
|
119
|
+
args.each {|arg|
|
120
|
+
if is_object_wrapped?(arg)
|
121
|
+
arg_type = arg.__rubybreaker_type
|
122
|
+
else
|
123
|
+
arg_type = NominalType.new(arg.class)
|
124
|
+
end
|
125
|
+
new_meth_type.arg_types << arg_type
|
126
|
+
}
|
127
|
+
if (obj == retval)
|
128
|
+
# the return value is same as the message receiver. This means the
|
129
|
+
# return value has the self type.
|
130
|
+
SelfType.set_self(obj.class)
|
131
|
+
ret_type = SelfType.new()
|
132
|
+
else
|
133
|
+
# Otherwise, construct a nominal type.
|
134
|
+
ret_type = NominalType.new(retval.class)
|
135
|
+
end
|
136
|
+
new_meth_type.ret_type = ret_type
|
137
|
+
|
138
|
+
resolved = false
|
139
|
+
if exist_meth_type.instance_of?(MethodListType)
|
140
|
+
exist_meth_type.types.each {|meth_type|
|
141
|
+
resolved = lub_helper(meth_type, new_meth_type)
|
142
|
+
break if resolved
|
143
|
+
}
|
144
|
+
else
|
145
|
+
resolved = lub_helper(exist_meth_type, new_meth_type)
|
146
|
+
if !resolved
|
147
|
+
# Could not resolve the types, so promote the method type to a
|
148
|
+
# method list type
|
149
|
+
exist_meth_type = MethodListType.new([exist_meth_type])
|
150
|
+
inst_meths[meth_name.to_sym] = exist_meth_type
|
151
|
+
end
|
152
|
+
end
|
153
|
+
if !resolved
|
154
|
+
exist_meth_type.types << new_meth_type
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
public
|
159
|
+
|
160
|
+
# This method occurs before every "monitored" method call. It wraps
|
161
|
+
# each argument with the object wrapper.
|
162
|
+
def before_method(obj, meth_info)
|
163
|
+
|
164
|
+
mod = obj.class
|
165
|
+
inst_meths = Breakable::TYPE_PLACEHOLDER_MAP[mod].inst_meths
|
166
|
+
|
167
|
+
# Let's take things out of the MethodInfo object
|
168
|
+
meth_name = meth_info.meth_name
|
169
|
+
args = meth_info.args
|
170
|
+
blk = meth_info.blk
|
171
|
+
ret = meth_info.ret
|
172
|
+
|
173
|
+
args = args.map do |arg|
|
174
|
+
if arg.kind_of?(TrueClass) || arg.kind_of?(FalseClass)
|
175
|
+
# XXX: would overrides resolve this issue?
|
176
|
+
arg
|
177
|
+
else
|
178
|
+
ObjectWrapper.new(arg)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
Debug.msg("In module monitor_before #{meth_name}")
|
183
|
+
|
184
|
+
meth_type = inst_meths[meth_name]
|
185
|
+
|
186
|
+
if meth_type
|
187
|
+
# This means the method type has been created previously.
|
188
|
+
unless (blk == nil && meth_type.blk_type == nil) &&
|
189
|
+
(!blk || blk.arity == meth_type.blk_type.arg_types.length)
|
190
|
+
raise Errors::TypeError("Block usage is inconsistent")
|
191
|
+
end
|
192
|
+
else
|
193
|
+
# No method type has been created for this method yet. Create a
|
194
|
+
# blank method type (where each argument type, block type, and
|
195
|
+
# 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
|
198
|
+
meth_type = MethodType.new(meth_name, arg_types, blk_type, nil)
|
199
|
+
inst_meths[meth_name] = meth_type
|
200
|
+
end
|
201
|
+
|
202
|
+
meth_info.args = args
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
# This method occurs after every "monitored" method call. It updates
|
207
|
+
# the type information.
|
208
|
+
def after_method(obj, meth_info)
|
209
|
+
mod = obj.class
|
210
|
+
# Take things out
|
211
|
+
meth_name = meth_info.meth_name
|
212
|
+
retval = meth_info.ret
|
213
|
+
args = meth_info.args
|
214
|
+
blk = meth_info.blk
|
215
|
+
|
216
|
+
Debug.msg("In module monitor_after #{meth_name}")
|
217
|
+
|
218
|
+
inst_meths = Breakable::TYPE_PLACEHOLDER_MAP[mod].inst_meths
|
219
|
+
|
220
|
+
# Compute the least upper bound
|
221
|
+
lub(obj, inst_meths,meth_name,retval,*args,&blk)
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#--
|
2
|
+
# This program parses a type signature given by the Ruby programmer in the
|
3
|
+
# RubyBreaker type format.
|
4
|
+
|
5
|
+
require "treetop"
|
6
|
+
require_relative "../type"
|
7
|
+
|
8
|
+
module RubyBreaker
|
9
|
+
|
10
|
+
module Runtime
|
11
|
+
|
12
|
+
module TypeSigParser
|
13
|
+
|
14
|
+
Treetop.load "#{File.dirname(__FILE__)}/../type/type_grammar"
|
15
|
+
PARSER = TypeGrammarParser.new
|
16
|
+
|
17
|
+
public
|
18
|
+
|
19
|
+
# This is a simple redirecting method for parsing type signature. The
|
20
|
+
# only special thing about this method is that, if there are multiple
|
21
|
+
# lines in the signature, it will look at each line and construct a
|
22
|
+
# MethodListType to represent the intersection type.
|
23
|
+
def self.parse(str)
|
24
|
+
meth_types = []
|
25
|
+
|
26
|
+
# Get caller information and set the global location
|
27
|
+
my_caller = caller[1]
|
28
|
+
if my_caller
|
29
|
+
file,line,junk = my_caller.split(":")
|
30
|
+
Position.set(file,line,-1)
|
31
|
+
end
|
32
|
+
|
33
|
+
return PARSER.parse(str).value
|
34
|
+
|
35
|
+
rescue => e
|
36
|
+
|
37
|
+
puts e
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#--
|
2
|
+
# This file defines Breakable and Broken module. Breakable module makes the
|
3
|
+
# hosting module (and class) subject to type monitoring. Broken module makes
|
4
|
+
# type information of the hosting module accessible.
|
5
|
+
|
6
|
+
require_relative "runtime/overrides"
|
7
|
+
require_relative "runtime/typesig_parser"
|
8
|
+
require_relative "runtime/monitor"
|
9
|
+
require_relative "runtime/inspector"
|
10
|
+
|
11
|
+
module RubyBreaker
|
12
|
+
|
13
|
+
# This module should be included in classes or modules that you want to
|
14
|
+
# monitor during runtime. The concept is that once a Breakable module is
|
15
|
+
# monitored and its type documentation is generated, the module now becomes
|
16
|
+
# a Broken module. The actual implementation is a simple trigger that
|
17
|
+
# queues the target module into the list of modules to monitor. The queued
|
18
|
+
# modules are then modified to be monitored dynamically.
|
19
|
+
module Breakable
|
20
|
+
|
21
|
+
TYPE_PLACEHOLDER_MAP = {} # module => type_placeholder
|
22
|
+
MONITOR_MAP = {} # module => monitor
|
23
|
+
|
24
|
+
# when this module is included, simply keep track of this module so we
|
25
|
+
# can start monitoring
|
26
|
+
def self.included(mod)
|
27
|
+
BREAKABLE << mod
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
# This module is included for "broken" classes.
|
33
|
+
module Broken
|
34
|
+
|
35
|
+
include TypeDefs
|
36
|
+
include Runtime
|
37
|
+
|
38
|
+
TYPE_PLACEHOLDER_MAP = {} # module => type_placeholder
|
39
|
+
|
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'
|
42
|
+
# method to parse the type signature dynamically.
|
43
|
+
#
|
44
|
+
# Usage:
|
45
|
+
# Class A
|
46
|
+
# include RubyBreaker::Broken
|
47
|
+
#
|
48
|
+
# typesig("foo(fixnum) -> fixnum")
|
49
|
+
# def foo(x) ... end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
module BrokenMeta
|
53
|
+
|
54
|
+
include TypeDefs
|
55
|
+
include Runtime
|
56
|
+
|
57
|
+
# This method can be used at the meta level of the target module to
|
58
|
+
# specify the type of a method.
|
59
|
+
def typesig(str)
|
60
|
+
t = TypeSigParser.parse(str)
|
61
|
+
placeholder = TYPE_PLACEHOLDER_MAP[self]
|
62
|
+
if placeholder
|
63
|
+
meth_type = placeholder.inst_meths[t.meth_name]
|
64
|
+
if meth_type
|
65
|
+
# TODO: make a method list
|
66
|
+
if meth_type.instance_of?(MethodListType)
|
67
|
+
meth_type.types << t
|
68
|
+
else
|
69
|
+
# then upgrade it
|
70
|
+
placeholder.inst_meths[t.meth_name] = MethodListType.new([meth_type, t])
|
71
|
+
end
|
72
|
+
else
|
73
|
+
placeholder.inst_meths[t.meth_name] = t
|
74
|
+
end
|
75
|
+
end
|
76
|
+
return t
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
# 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.
|
84
|
+
def self.included(mod)
|
85
|
+
|
86
|
+
# Add to the list of broken modules
|
87
|
+
BROKEN << mod
|
88
|
+
|
89
|
+
# This MUST BE set for self type to work in type signatures
|
90
|
+
SelfType.set_self(mod)
|
91
|
+
|
92
|
+
# Create if there is no type placeholder for this module yet
|
93
|
+
placeholder = TYPE_PLACEHOLDER_MAP[mod]
|
94
|
+
if !placeholder
|
95
|
+
placeholder = TypePlaceholder.new()
|
96
|
+
TYPE_PLACEHOLDER_MAP[mod] = placeholder
|
97
|
+
end
|
98
|
+
mod.extend(BrokenMeta)
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#--
|
2
|
+
# This file mimics the Ruby testing framework. This is provided so that
|
3
|
+
# RubyBreaker can be used seamlessly with the traditional Ruby framework
|
4
|
+
# (supposedly :) ).
|
5
|
+
|
6
|
+
module RubyBreaker
|
7
|
+
|
8
|
+
# This module overrides the normal behavior of Ruby Stdlib's TestCase
|
9
|
+
# class.
|
10
|
+
module TestCase
|
11
|
+
|
12
|
+
def self.setup()
|
13
|
+
Main.setup()
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.teardown()
|
17
|
+
# Main.output()
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.included(mod)
|
21
|
+
|
22
|
+
# hack to insert RubyBreaker's own setup and teardown methods
|
23
|
+
mod.module_eval <<-EOS
|
24
|
+
|
25
|
+
alias :__run :run
|
26
|
+
|
27
|
+
def run(*args,&blk)
|
28
|
+
# RubyBreaker::Utilities.rb_print("Running " + args[0].to_s)
|
29
|
+
RubyBreaker::TestCase.setup()
|
30
|
+
__run(*args,&blk)
|
31
|
+
RubyBreaker::TestCase.teardown()
|
32
|
+
end
|
33
|
+
|
34
|
+
EOS
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative "test/testcase"
|
@@ -0,0 +1,241 @@
|
|
1
|
+
#--
|
2
|
+
# This file contains the type structure used in RubyBreaker. Here we list
|
3
|
+
# types that are used (allowed) in RubyBreaker:
|
4
|
+
#
|
5
|
+
# AnyType represents basically any type in Ruby
|
6
|
+
# NilType represents a nil object
|
7
|
+
# SelfType represents "self"--the current object
|
8
|
+
# NominalType represents an object
|
9
|
+
# DuckType represents a partial object (a collection of methods)
|
10
|
+
# FusionType represents an object along with a set of methods
|
11
|
+
# MethodType represents a method
|
12
|
+
# BlockType represents a block
|
13
|
+
# OrType represents one of the many objects (i.e, OR)
|
14
|
+
# OptionalType represents an optional argument type
|
15
|
+
# VarLengthType represents a variable length argument type
|
16
|
+
# MethodListType represents one or more methods
|
17
|
+
#
|
18
|
+
|
19
|
+
require_relative "../util"
|
20
|
+
require_relative "../context.rb"
|
21
|
+
|
22
|
+
module RubyBreaker
|
23
|
+
|
24
|
+
# This module contains all RubyBreaker type definitions. This module has
|
25
|
+
# to be included to the module if you want to work with the type
|
26
|
+
# definitions that RubyBreaker provides (unless you want to add TypeDefs
|
27
|
+
# namespace in the module).
|
28
|
+
module TypeDefs
|
29
|
+
|
30
|
+
# This class is a catch-all. The constructor of this class must be
|
31
|
+
# called from children via super() in order to assign the position field
|
32
|
+
# variable.
|
33
|
+
#
|
34
|
+
# The optional arguments are args[0] = file name args[1] = line number
|
35
|
+
#
|
36
|
+
# args[2] = column position
|
37
|
+
#
|
38
|
+
# or
|
39
|
+
#
|
40
|
+
# args[0] = a Position, ObjectPosition, or Context object
|
41
|
+
#
|
42
|
+
class Type
|
43
|
+
|
44
|
+
# Speficies the context of the type.
|
45
|
+
attr_accessor :ctx
|
46
|
+
|
47
|
+
def initialize(*args)
|
48
|
+
case args[0]
|
49
|
+
when Context
|
50
|
+
@ctx = args[0]
|
51
|
+
when Position
|
52
|
+
@ctx = Context.new(args[0])
|
53
|
+
when ObjectPosition
|
54
|
+
@ctx = Context.new(args[0])
|
55
|
+
else
|
56
|
+
file = args[0]
|
57
|
+
line = args[1]
|
58
|
+
col = args[2]
|
59
|
+
pos = Position.new(file,line,col)
|
60
|
+
@ctx = Context.new(pos)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# This type can represent any object
|
66
|
+
class AnyType < Type
|
67
|
+
def initialize(*args)
|
68
|
+
super(*args)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# This type represents a nil
|
73
|
+
class NilType < Type
|
74
|
+
def initialize(*args)
|
75
|
+
super(*args)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# This class represents a concrete object like a Numeric or String. It
|
80
|
+
# stores the actual module in the instance variable @mod.
|
81
|
+
class NominalType < Type
|
82
|
+
|
83
|
+
# This accessor points to the actual module
|
84
|
+
attr_accessor :mod
|
85
|
+
|
86
|
+
def initialize(mod=nil,*args)
|
87
|
+
super(*args)
|
88
|
+
@mod = mod
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# This type represents the self type. Note that this is a subclass of
|
93
|
+
# Nominal Type. It works just like nominal type except that it also points
|
94
|
+
# to the current object! See subtyping.rb for more detail on how this
|
95
|
+
# would impact typing.
|
96
|
+
class SelfType < NominalType
|
97
|
+
|
98
|
+
# This is a setter method for class variable mod.
|
99
|
+
# NOTE: It is set every time Broken module is included.
|
100
|
+
def self.set_self(mod)
|
101
|
+
@@mod = mod
|
102
|
+
end
|
103
|
+
|
104
|
+
# This is a getter method for class variable mod.
|
105
|
+
def self.get_self(mod)
|
106
|
+
@@mod = mod
|
107
|
+
end
|
108
|
+
|
109
|
+
def initialize(*args)
|
110
|
+
# NOTE: @@mod is not required in general, but for typing it is a must.
|
111
|
+
super(@@mod, *args)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# This class represents any object with certain methods
|
116
|
+
# Usage: [m1,m2,...] where m1...mn are method names
|
117
|
+
class DuckType < Type
|
118
|
+
|
119
|
+
# This accessor sets/gets method names in the duck type.
|
120
|
+
attr_accessor :meth_names
|
121
|
+
|
122
|
+
def initialize(meth_names=[],*args)
|
123
|
+
super(*args)
|
124
|
+
@meth_names = meth_names.map!{|n| n.to_sym}
|
125
|
+
end
|
126
|
+
def add_meth(meth_name)
|
127
|
+
@meth_names << meth_name.to_sym if !@meth_names.include?(meth_name)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# This class represents any object that has certain methods whose types
|
132
|
+
# are same as the given nominal type's counterparts.
|
133
|
+
# Usage: nominal_type[m1,m2,...]
|
134
|
+
class FusionType < DuckType
|
135
|
+
|
136
|
+
# This accessor sets/gets the nominal type to which the method names
|
137
|
+
# are bound.
|
138
|
+
attr_accessor :nom_type
|
139
|
+
|
140
|
+
def initialize(nom_type,meth_names=[],*args)
|
141
|
+
super(meth_names,*args)
|
142
|
+
@nom_type = nom_type
|
143
|
+
end
|
144
|
+
|
145
|
+
# This method gets the actual module of the nominal type for this
|
146
|
+
# fusion type. This is a shorthand for t1.nom_type.mod().
|
147
|
+
def mod()
|
148
|
+
return @nom_type.mod
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# This class represents a block (in a method). It has zero or more argument
|
153
|
+
# types, nested block type (optional), and a return type.
|
154
|
+
class BlockType < Type
|
155
|
+
|
156
|
+
# This accessor sets/gets the argument types for this block type.
|
157
|
+
attr_accessor :arg_types
|
158
|
+
|
159
|
+
# This accessor sets/gets the block type for this block type.
|
160
|
+
attr_accessor :blk_type
|
161
|
+
|
162
|
+
# This accessor sets/gets the return type for this block type.
|
163
|
+
attr_accessor :ret_type
|
164
|
+
|
165
|
+
def initialize(arg_types=[],blk_type=nil,ret_type=nil,*args)
|
166
|
+
super(*args)
|
167
|
+
@arg_types = arg_types
|
168
|
+
@blk_type = blk_type
|
169
|
+
@ret_type = ret_type
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# This class represents a method and is essentially same as block type
|
174
|
+
# except the method name.
|
175
|
+
class MethodType < BlockType
|
176
|
+
|
177
|
+
# This accessor sets/gets the method name for this method type.
|
178
|
+
attr_accessor :meth_name
|
179
|
+
|
180
|
+
def initialize(meth_name,arg_types=[],blk_type=nil,ret_type=nil,*args)
|
181
|
+
super(arg_types,blk_type,ret_type,*args)
|
182
|
+
@meth_name = meth_name.to_sym
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# This class respresents an optional argument type
|
187
|
+
class OptionalType < Type
|
188
|
+
|
189
|
+
# This accessor sets/gets the inner type of this optional type.
|
190
|
+
attr_accessor :type
|
191
|
+
|
192
|
+
def initialize(type,*args)
|
193
|
+
super(*args)
|
194
|
+
@type = type
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# This class represents a variable-length argument type
|
199
|
+
class VarLengthType < Type
|
200
|
+
|
201
|
+
# This accessor sets/gets the inner type of this variable length
|
202
|
+
# argument type.
|
203
|
+
attr_accessor :type
|
204
|
+
|
205
|
+
def initialize(type,*args)
|
206
|
+
super(*args)
|
207
|
+
@type = type
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# This class represents one of many types
|
212
|
+
class OrType < Type
|
213
|
+
|
214
|
+
# This accessor sets/gets the inner types of this "or" type.
|
215
|
+
attr_accessor :types
|
216
|
+
|
217
|
+
def initialize(types=[],*args)
|
218
|
+
super(*args)
|
219
|
+
@types = types
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# This class represents multiple method types.
|
224
|
+
class MethodListType < Type
|
225
|
+
|
226
|
+
# This accessor sets/gets the method types.
|
227
|
+
attr_accessor :types
|
228
|
+
|
229
|
+
def initialize(types=[],*args)
|
230
|
+
super(*args)
|
231
|
+
@types = types
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
|
237
|
+
# Include the module right away
|
238
|
+
include TypeDefs
|
239
|
+
|
240
|
+
end
|
241
|
+
|