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