/
laser_module.rb
407 lines (349 loc) · 12 KB
/
laser_module.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
module Laser
module Analysis
# Laser representation of a module. Named LaserModule to avoid naming
# conflicts. It has lists of methods, instance variables, and so on.
class LaserModule < LaserObject
attr_reader :binding, :superclass
attr_accessor :path
cattr_accessor_with_default :all_modules, []
def initialize(klass = ClassRegistry['Module'], scope = Scope::GlobalScope,
full_path=(@name_set = :no; "#{klass.path}:Anonymous:#{object_id.to_s(16)}"))
super(klass, scope, full_path.split('::').last)
full_path = submodule_path(full_path) if scope && scope.parent
validate_module_path!(full_path) unless LaserSingletonClass === self
@name_set = :yes unless @name_set == :no
@path = full_path
@instance_methods = {}
@visibility_table = {}
@constant_table = {}
@scope = scope
@ivar_types = {}
@superclass ||= nil
initialize_protocol
@binding = Bindings::ConstantBinding.new(name, self)
initialize_scope
yield self if block_given?
LaserModule.all_modules << self
end
def <=>(other)
if self == other
return 0
elsif ancestors.include?(other)
return -1
elsif other.ancestors.include?(self)
return 1
else
return nil
end
end
# including Comparable is having issues due to override of include. Do it
# ourselves.
%w(< <= >= >).each do |op|
class_eval %Q{
def #{op}(other)
cmp = self <=> other
cmp && cmp #{op} 0
end
}
end
def as_type
Types::ClassObjectType.new(self)
end
def name_set?
@name_set == :yes
end
def set_name(new_path)
@path = new_path
ProtocolRegistry.add_class(self)
@name_set = :yes
end
# Returns the canonical path for a (soon-to-be-created) submodule of the given
# scope. This is computed before creating the module.
def submodule_path(new_mod_name)
scope = self.scope.parent
new_mod_full_path = scope.parent.nil? ? '' : scope.path
new_mod_full_path += '::' unless new_mod_full_path.empty?
new_mod_full_path += new_mod_name
end
def validate_module_path!(path)
path.split('::').each do |component|
if !component.empty? && component[0,1] !~ /[A-Z]/
raise ArgumentError.new("Path component #{component} in #{path}" +
' does not start with a capital letter, A-Z.')
end
end
end
def class_name
'Module'
end
# If this is a new, custom module, we can update the constant
# table and perform module initialization.
def initialize_scope
if @scope && !(@scope.parent.nil?)
@scope.parent.constants[name] = self.binding if @scope.parent
@scope.locals['self'] = Bindings::LocalVariableBinding.new('self', self)
end
end
# Initializes the protocol for this LaserClass.
def initialize_protocol
if ProtocolRegistry[path].any? && !TESTS_ACTIVATED
$stderr.puts "Warning: creating new instance of #{class_name} #{path}"
else
ProtocolRegistry.add_class(self)
end
end
def name
self.path.split('::').last
end
def add_instance_method!(method)
@instance_methods[method.name.to_sym] = method
@visibility_table[method.name.to_sym] = :public
method.owner = self
end
def instance_method(name)
lookup = name.to_sym
if @instance_methods.has_key?(lookup)
then @instance_methods[lookup]
else @superclass && @superclass.instance_method(lookup)
end
end
def method_defined?(name)
instance_method(name) && visibility_for(name) != :private
end
def public_instance_method(name)
result = instance_method(name)
visibility_for(name) == :public ? result : nil
end
def __all_instance_methods(include_super = true)
mine = @instance_methods.keys
if include_super && @superclass
then @superclass.instance_methods | mine
else mine
end
end
def __instance_methods_with_privacy(include_super, *allowed)
methods = __all_instance_methods(include_super)
table = visibility_table
methods.select { |name| allowed.include?(table[name.to_sym]) }
end
def instance_methods(include_super = true)
__instance_methods_with_privacy(include_super, :public, :protected)
end
def public_instance_methods(include_super = true)
__instance_methods_with_privacy(include_super, :public)
end
def protected_instance_methods(include_super = true)
__instance_methods_with_privacy(include_super, :protected)
end
def protected_method_defined?(name)
!!__instance_methods_with_privacy(include_super, :protected)
end
def private_instance_methods(include_super = true)
__instance_methods_with_privacy(include_super, :private)
end
def ivar_type(name)
@ivar_types[name] || (@superclass && @superclass.ivar_type(name)) || Types::NILCLASS
end
def set_ivar_type(name, type)
@ivar_types[name] = type
end
def instance_variable(name)
@instance_variables[name] || (@superclass && @superclass.instance_variable(name))
end
def instance_variables
if @superclass.nil?
then @instance_variables
else @instance_variables.merge(@superclass.instance_variables)
end
end
def add_instance_variable!(binding)
@instance_variables[binding.name] = binding
end
def visibility_for(method)
lookup = method.to_sym
return @visibility_table[lookup] ||
(@superclass && @superclass.visibility_for(lookup))
end
def visibility_table
if @superclass
then @superclass.visibility_table.merge(@visibility_table)
else @visibility_table
end
end
def set_visibility!(method, visibility)
@visibility_table[method] = visibility
end
def superclass=(new_superclass)
@superclass = new_superclass
end
def parent
@superclass
end
# The set of all superclasses (including the class itself)
def ancestors
if @superclass.nil?
then [self]
else [self] + @superclass.ancestors
end
end
def subset
[self]
end
def classes_including
@classes_including ||= []
end
def included_modules
ancestors.select { |mod| LaserModuleCopy === mod }
end
# Directly translated from MRI's C implementation in class.c:650
def include_module(mod)
if mod.klass == ClassRegistry['Class']
raise ArgumentError.new("Tried to include #{mod.name}, which should "+
" be a Module or Module subclass, not a " +
"#{mod.klass.name}.")
end
original_mod = mod
any_changes = false
current = self
while mod
superclass_seen = false
should_change = true
if mod == self
raise ArgumentError.new("Cyclic module inclusion: #{mod} mixed into #{self}")
end
ancestors.each do |parent|
case parent
when LaserModuleCopy
if parent == mod
current = parent unless superclass_seen
should_change = false
break
end
when LaserClass
superclass_seen = true
end
end
if should_change
new_super = (current.superclass = LaserModuleCopy.new(mod, current.ancestors[1]))
mod.classes_including << current
current = new_super
any_changes = true
end
mod = mod.superclass
end
unless any_changes
raise DoubleIncludeError.new("Included #{original_mod.path} into #{self.path}"+
" but it was already included.", nil)
end
end
def inspect
"#<LaserModule: #{path}>"
end
# simulation methods
def ===(other)
klass = (LaserObject === other ? other.klass : ClassRegistry[other.class.name])
klass.ancestors.include?(self)
end
def const_set(string, value)
@constant_table[string] = value
if LaserModule === value && !value.name_set?
if self == ClassRegistry['Object']
value.set_name(string)
else
value.set_name("#{@path}::#{string}")
end
end
end
def const_get(constant, inherit=true)
if inherit && superclass
@constant_table[constant] || superclass.const_get(constant, true)
elsif LaserClass === self
@constant_table[constant] or raise ArgumentError.new("Class #{@path} has no constant #{constant}")
else
(@constant_table[constant] || ClassRegistry['Object'].const_get(constant, false)) or
raise ArgumentError.new("Class #{@path} has no constant #{constant}")
end
end
# Fuck you, that's why
def const_defined?(constant, inherit=true)
!!const_get(constant, inherit)
rescue
false
end
def remove_const(sym)
@constant_table.delete(sym)
end
def define_method(name, proc)
str_name = name.to_s
name = name.to_sym
new_method = LaserMethod.new(str_name, proc)
new_method.owner = self
@instance_methods[name] = new_method
if Bootstrap::VISIBILITY_STACK.value.last == :module_function
__make_module_function__(name)
else
@visibility_table[name] = Bootstrap::VISIBILITY_STACK.value.last
end
new_method
end
def define_method_with_annotations(name, proc, opts={})
method = define_method(name, proc)
opts.each { |name, value| method.send("#{name}=", value) }
end
def undef_method(symbol)
sym = symbol.to_sym
@instance_methods[sym] = nil
@visibility_table[sym] = :undefined
end
def remove_method(symbol)
sym = symbol.to_sym
@instance_methods.delete(sym)
@visibility_table.delete(sym)
end
def alias_method(new, old)
newsym = new.to_sym
oldsym = old.to_sym
@instance_methods[newsym] = instance_method(oldsym)
@visibility_table[newsym] = visibility_for(oldsym)
end
def include(*mods)
mods.reverse.each { |mod| include_module(mod) }
end
def extend(*mods)
singleton_class.include(*mods)
end
def __visibility_modifier__(args, kind)
if args.empty?
Bootstrap::VISIBILITY_STACK.value[-1] = kind
else
args.each { |method| set_visibility!(method.to_sym, kind) }
end
self
end
def __make_module_function__(method_name)
set_visibility!(method_name, :private)
found_method = instance_method(method_name).dup
singleton_class.add_instance_method!(found_method)
singleton_class.set_visibility!(method_name, :public)
end
def public(*args)
__visibility_modifier__(args, :public)
end
def protected(*args)
__visibility_modifier__(args, :protected)
end
def private(*args)
__visibility_modifier__(args, :private)
end
def module_function(*args)
if args.any?
args.each do |method|
__make_module_function__(method.to_sym)
end
else
Bootstrap::VISIBILITY_STACK.value[-1] = :module_function
end
end
end
end
end