53a99fa9be20c5638636163d12b12dbb006e1b66
[nit.git] / src / vm.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Julien Pagès <julien.pages@lirmm.fr>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Implementation of the Nit virtual machine
18 module vm
19
20 import interpreter::naive_interpreter
21 import model_utils
22 import perfect_hashing
23
24 redef class ModelBuilder
25 redef fun run_naive_interpreter(mainmodule: MModule, arguments: Array[String])
26 do
27 var time0 = get_time
28 self.toolcontext.info("*** NITVM STARTING ***", 1)
29
30 var interpreter = new VirtualMachine(self, mainmodule, arguments)
31 interpreter.start(mainmodule)
32
33 var time1 = get_time
34 self.toolcontext.info("*** NITVM STOPPING : {time1-time0} ***", 2)
35 end
36 end
37
38 # A virtual machine based on the naive_interpreter
39 class VirtualMachine super NaiveInterpreter
40
41 # Perfect hashing and perfect numbering
42 var ph: Perfecthashing = new Perfecthashing
43
44 # Handles memory allocated in C
45 var memory_manager: MemoryManager = new MemoryManager
46
47 # The unique instance of the `MInit` value
48 var initialization_value: Instance is noinit
49
50 init
51 do
52 var init_type = new MInitType(mainmodule.model)
53 initialization_value = new MutableInstance(init_type)
54 super
55 end
56
57 # Runtime subtyping test
58 redef fun is_subtype(sub, sup: MType): Bool
59 do
60 if sub == sup then return true
61
62 var anchor = self.frame.arguments.first.mtype.as(MClassType)
63
64 # `sub` or `sup` are formal or virtual types, resolve them to concrete types
65 if sub isa MParameterType or sub isa MVirtualType then
66 sub = sub.resolve_for(anchor.mclass.mclass_type, anchor, mainmodule, false)
67 end
68 if sup isa MParameterType or sup isa MVirtualType then
69 sup = sup.resolve_for(anchor.mclass.mclass_type, anchor, mainmodule, false)
70 end
71
72 var sup_accept_null = false
73 if sup isa MNullableType then
74 sup_accept_null = true
75 sup = sup.mtype
76 else if sup isa MNullType then
77 sup_accept_null = true
78 end
79
80 # Can `sub` provides null or not?
81 # Thus we can match with `sup_accept_null`
82 # Also discard the nullable marker if it exists
83 if sub isa MNullableType then
84 if not sup_accept_null then return false
85 sub = sub.mtype
86 else if sub isa MNullType then
87 return sup_accept_null
88 end
89 # Now the case of direct null and nullable is over
90
91 if sub isa MParameterType or sub isa MVirtualType then
92 sub = sub.anchor_to(mainmodule, anchor)
93 # Manage the second layer of null/nullable
94 if sub isa MNullableType then
95 if not sup_accept_null then return false
96 sub = sub.mtype
97 else if sub isa MNullType then
98 return sup_accept_null
99 end
100 end
101
102 assert sub isa MClassType
103
104 # `sup` accepts only null
105 if sup isa MNullType then return false
106
107 assert sup isa MClassType
108
109 # `sub` and `sup` can be discovered inside a Generic type during the subtyping test
110 if not sup.mclass.loaded then create_class(sup.mclass)
111 if not sub.mclass.loaded then create_class(sub.mclass)
112
113 # For now, always use perfect hashing for subtyping test
114 var super_id = sup.mclass.vtable.id
115 var mask = sub.mclass.vtable.mask
116
117 var res = inter_is_subtype_ph(super_id, mask, sub.mclass.vtable.internal_vtable)
118 if res == false then return false
119 # sub and sup can be generic types, each argument of generics has to be tested
120
121 if not sup isa MGenericType then return true
122 var sub2 = sub.supertype_to(mainmodule, anchor, sup.mclass)
123
124 # Test each argument of a generic by recursive calls
125 for i in [0..sup.mclass.arity[ do
126 var sub_arg = sub2.arguments[i]
127 var sup_arg = sup.arguments[i]
128 var res2 = is_subtype(sub_arg, sup_arg)
129 if res2 == false then return false
130 end
131 return true
132 end
133
134 # Subtyping test with perfect hashing
135 private fun inter_is_subtype_ph(id: Int, mask:Int, vtable: Pointer): Bool `{
136 // hv is the position in hashtable
137 int hv = id & mask;
138
139 // Follow the pointer to somewhere in the vtable
140 long unsigned int *offset = (long unsigned int*)(((long int *)vtable)[-hv]);
141
142 // If the pointed value is corresponding to the identifier, the test is true, otherwise false
143 return *offset == id;
144 `}
145
146 # Subtyping test with Cohen test (direct access)
147 private fun inter_is_subtype_sst(id: Int, position: Int, vtable: Pointer): Bool `{
148 // Direct access to the position given in parameter
149 int tableid = (long unsigned int)((long int *)vtable)[position];
150
151 return id == tableid;
152 `}
153
154 # Redef init_instance to simulate the loading of a class
155 redef fun init_instance(recv: Instance)
156 do
157 if not recv.mtype.as(MClassType).mclass.loaded then create_class(recv.mtype.as(MClassType).mclass)
158
159 recv.vtable = recv.mtype.as(MClassType).mclass.vtable
160
161 assert recv isa MutableInstance
162
163 recv.internal_attributes = init_internal_attributes(initialization_value, recv.mtype.as(MClassType).mclass.all_mattributes(mainmodule, none_visibility).length)
164 super
165 end
166
167 # Associate a `PrimitiveInstance` to its `VTable`
168 redef fun init_instance_primitive(recv: Instance)
169 do
170 if not recv.mtype.as(MClassType).mclass.loaded then create_class(recv.mtype.as(MClassType).mclass)
171
172 recv.vtable = recv.mtype.as(MClassType).mclass.vtable
173 end
174
175 # Create a virtual table for this `MClass` if not already done
176 redef fun get_primitive_class(name: String): MClass
177 do
178 var mclass = super
179
180 if not mclass.loaded then create_class(mclass)
181
182 return mclass
183 end
184
185 # Initialize the internal representation of an object (its attribute values)
186 # `init_instance` is the initial value of attributes
187 private fun init_internal_attributes(init_instance: Instance, size: Int): Pointer
188 import Array[Instance].length, Array[Instance].[] `{
189
190 Instance* attributes = malloc(sizeof(Instance) * size);
191
192 int i;
193 for(i=0; i<size; i++)
194 attributes[i] = init_instance;
195
196 Instance_incr_ref(init_instance);
197 return attributes;
198 `}
199
200 # Creates the runtime structures for this class
201 fun create_class(mclass: MClass) do mclass.make_vt(self)
202
203 # Execute `mproperty` for a `args` (where `args[0]` is the receiver).
204 redef fun send(mproperty: MMethod, args: Array[Instance]): nullable Instance
205 do
206 var recv = args.first
207 var mtype = recv.mtype
208 var ret = send_commons(mproperty, args, mtype)
209 if ret != null then return ret
210
211 var propdef = method_dispatch(mproperty, recv.vtable.as(not null), recv)
212
213 return self.call(propdef, args)
214 end
215
216 # Method dispatch, for a given global method `mproperty`
217 # returns the most specific local method in the class corresponding to `vtable`
218 private fun method_dispatch(mproperty: MMethod, vtable: VTable, recv: Instance): MMethodDef
219 do
220 if mproperty.intro_mclassdef.mclass.positions_methods[recv.mtype.as(MClassType).mclass] != -1 then
221 return method_dispatch_sst(vtable.internal_vtable, mproperty.absolute_offset)
222 else
223 return method_dispatch_ph(vtable.internal_vtable, vtable.mask,
224 mproperty.intro_mclassdef.mclass.vtable.id, mproperty.offset)
225 end
226 end
227
228 # Execute a method dispatch with perfect hashing
229 private fun method_dispatch_ph(vtable: Pointer, mask: Int, id: Int, offset: Int): MMethodDef `{
230 // Perfect hashing position
231 int hv = mask & id;
232 long unsigned int *pointer = (long unsigned int*)(((long int *)vtable)[-hv]);
233
234 // pointer+2 is the position where methods are
235 // Add the offset of property and get the method implementation
236 MMethodDef propdef = (MMethodDef)*(pointer + 2 + offset);
237
238 return propdef;
239 `}
240
241 # Execute a method dispatch with direct access and return the appropriate `MMethodDef`
242 # `vtable` : Pointer to the internal pointer of the class
243 # `absolute_offset` : Absolute offset from the beginning of the virtual table
244 private fun method_dispatch_sst(vtable: Pointer, absolute_offset: Int): MMethodDef `{
245 // pointer+2 is the position where methods are
246 // Add the offset of property and get the method implementation
247 MMethodDef propdef = (MMethodDef)((long int *)vtable)[absolute_offset];
248
249 return propdef;
250 `}
251
252 # Return the value of the attribute `mproperty` for the object `recv`
253 redef fun read_attribute(mproperty: MAttribute, recv: Instance): Instance
254 do
255 assert recv isa MutableInstance
256
257 var i: Instance
258
259 if mproperty.intro_mclassdef.mclass.positions_attributes[recv.mtype.as(MClassType).mclass] != -1 then
260 # if this attribute class has an unique position for this receiver, then use direct access
261 i = read_attribute_sst(recv.internal_attributes, mproperty.absolute_offset)
262 else
263 # Otherwise, read the attribute value with perfect hashing
264 var id = mproperty.intro_mclassdef.mclass.vtable.id
265
266 i = read_attribute_ph(recv.internal_attributes, recv.vtable.internal_vtable,
267 recv.vtable.mask, id, mproperty.offset)
268 end
269
270 # If we get a `MInit` value, throw an error
271 if i == initialization_value then
272 fatal("Uninitialized attribute {mproperty.name}")
273 abort
274 end
275
276 return i
277 end
278
279 # Return the attribute value in `instance` with a sequence of perfect_hashing
280 # * `instance` is the attributes array of the receiver
281 # * `vtable` is the pointer to the virtual table of the class (of the receiver)
282 # * `mask` is the perfect hashing mask of the class
283 # * `id` is the identifier of the class
284 # * `offset` is the relative offset of this attribute
285 private fun read_attribute_ph(instance: Pointer, vtable: Pointer, mask: Int, id: Int, offset: Int): Instance `{
286 // Perfect hashing position
287 int hv = mask & id;
288 long unsigned int *pointer = (long unsigned int*)(((long int *)vtable)[-hv]);
289
290 // pointer+1 is the position where the delta of the class is
291 int absolute_offset = *(pointer + 1);
292
293 Instance res = ((Instance *)instance)[absolute_offset + offset];
294
295 return res;
296 `}
297
298 # Return the attribute value in `instance` with a direct access (SST)
299 # * `instance` is the attributes array of the receiver
300 # * `offset` is the absolute offset of this attribute
301 private fun read_attribute_sst(instance: Pointer, offset: Int): Instance `{
302 /* We can make a direct access to the attribute value
303 because this attribute is always at the same position
304 for the class of this receiver */
305 Instance res = ((Instance *)instance)[offset];
306
307 return res;
308 `}
309
310 # Replace in `recv` the value of the attribute `mproperty` by `value`
311 redef fun write_attribute(mproperty: MAttribute, recv: Instance, value: Instance)
312 do
313 assert recv isa MutableInstance
314
315 # Replace the old value of mproperty in recv
316 if mproperty.intro_mclassdef.mclass.positions_attributes[recv.mtype.as(MClassType).mclass] != -1 then
317 # if this attribute class has an unique position for this receiver, then use direct access
318 write_attribute_sst(recv.internal_attributes, mproperty.absolute_offset, value)
319 else
320 # Otherwise, use perfect hashing to replace the old value
321 var id = mproperty.intro_mclassdef.mclass.vtable.id
322
323 write_attribute_ph(recv.internal_attributes, recv.vtable.internal_vtable,
324 recv.vtable.mask, id, mproperty.offset, value)
325 end
326 end
327
328 # Replace the value of an attribute in an instance
329 # * `instance` is the attributes array of the receiver
330 # * `vtable` is the pointer to the virtual table of the class (of the receiver)
331 # * `mask` is the perfect hashing mask of the class
332 # * `id` is the identifier of the class
333 # * `offset` is the relative offset of this attribute
334 # * `value` is the new value for this attribute
335 private fun write_attribute_ph(instance: Pointer, vtable: Pointer, mask: Int, id: Int, offset: Int, value: Instance) `{
336 // Perfect hashing position
337 int hv = mask & id;
338 long unsigned int *pointer = (long unsigned int*)(((long int *)vtable)[-hv]);
339
340 // pointer+1 is the position where the delta of the class is
341 int absolute_offset = *(pointer + 1);
342
343 ((Instance *)instance)[absolute_offset + offset] = value;
344 Instance_incr_ref(value);
345 `}
346
347 # Replace the value of an attribute in an instance with direct access
348 # * `instance` is the attributes array of the receiver
349 # * `offset` is the absolute offset of this attribute
350 # * `value` is the new value for this attribute
351 private fun write_attribute_sst(instance: Pointer, offset: Int, value: Instance) `{
352 // Direct access to the position with the absolute offset
353 ((Instance *)instance)[offset] = value;
354 Instance_incr_ref(value);
355 `}
356
357 # Is the attribute `mproperty` initialized in the instance `recv`?
358 redef fun isset_attribute(mproperty: MAttribute, recv: Instance): Bool
359 do
360 assert recv isa MutableInstance
361
362 # Read the attribute value with internal perfect hashing read
363 # because we do not want to throw an error if the value is `initialization_value`
364 var id = mproperty.intro_mclassdef.mclass.vtable.id
365
366 var i = read_attribute_ph(recv.internal_attributes, recv.vtable.internal_vtable,
367 recv.vtable.mask, id, mproperty.offset)
368
369 return i != initialization_value
370 end
371 end
372
373 redef class MClass
374 # A reference to the virtual table of this class
375 var vtable: nullable VTable
376
377 # True when the class is effectively loaded by the vm, false otherwise
378 var loaded: Bool = false
379
380 # Color for Cohen subtyping test : the absolute position of the id
381 # of this class in virtual tables
382 var color: Int
383
384 # For each loaded subclass, keep the position of the group of attributes
385 # introduced by self class in the object
386 var positions_attributes: HashMap[MClass, Int] = new HashMap[MClass, Int]
387
388 # For each loaded subclass, keep the position of the group of methods
389 # introduced by self class in the vtable
390 var positions_methods: HashMap[MClass, Int] = new HashMap[MClass, Int]
391
392 # Allocates a VTable for this class and gives it an id
393 private fun make_vt(v: VirtualMachine)
394 do
395 if loaded then return
396
397 # `superclasses` contains the order of superclasses for virtual tables
398 var superclasses = superclasses_ordering(v)
399 superclasses.remove(self)
400
401 # Make_vt for super-classes
402 var ids = new Array[Int]
403 var nb_methods = new Array[Int]
404 var nb_attributes = new Array[Int]
405
406 # Absolute offset of attribute from the beginning of the attributes table
407 var offset_attributes = 0
408
409 # Absolute offset of method from the beginning of the methods table,
410 # is initialize to 3 because the first position is empty in the virtual table
411 # and the second and third are respectively class id and delta
412 var offset_methods = 3
413
414 # The previous element in `superclasses`
415 var previous_parent: nullable MClass = null
416 if superclasses.length > 0 then previous_parent = superclasses[0]
417 for parent in superclasses do
418 if not parent.loaded then parent.make_vt(v)
419
420 # Get the number of introduced methods and attributes for this class
421 var methods = 0
422 var attributes = 0
423
424 for p in parent.intro_mproperties(none_visibility) do
425 if p isa MMethod then methods += 1
426 if p isa MAttribute then attributes += 1
427 end
428
429 ids.push(parent.vtable.id)
430 nb_methods.push(methods)
431 nb_attributes.push(attributes)
432
433 # Update `positions_attributes` and `positions_methods` in `parent`.
434 # If the position is invariant for this parent, store this position
435 # else store a special value (-1)
436 var pos_attr = -1
437 var pos_meth = -1
438
439 if previous_parent.as(not null).positions_attributes[parent] == offset_attributes then pos_attr = offset_attributes
440 if previous_parent.as(not null).positions_methods[parent] == offset_methods then pos_meth = offset_methods
441
442 parent.update_positions(pos_attr, pos_meth, self)
443
444 offset_attributes += attributes
445 offset_methods += methods
446 offset_methods += 2 # Because each block starts with an id and the delta
447 end
448
449 # When all super-classes have their identifiers and vtables, allocate current one
450 allocate_vtable(v, ids, nb_methods, nb_attributes, offset_attributes, offset_methods)
451 loaded = true
452
453 # Set the absolute position of the identifier of this class in the virtual table
454 color = offset_methods - 2
455
456 # The virtual table now needs to be filled with pointer to methods
457 superclasses.add(self)
458 for cl in superclasses do
459 fill_vtable(v, vtable.as(not null), cl)
460 end
461 end
462
463 # Allocate a single vtable
464 # * `ids : Array of superclasses identifiers
465 # * `nb_methods : Array which contain the number of introduced methods for each class in ids
466 # * `nb_attributes : Array which contain the number of introduced attributes for each class in ids
467 # * `offset_attributes : Offset from the beginning of the table of the group of attributes
468 # * `offset_methods : Offset from the beginning of the table of the group of methods
469 private fun allocate_vtable(v: VirtualMachine, ids: Array[Int], nb_methods: Array[Int], nb_attributes: Array[Int],
470 offset_attributes: Int, offset_methods: Int)
471 do
472 vtable = new VTable
473 var idc = new Array[Int]
474
475 vtable.mask = v.ph.pnand(ids, 1, idc) - 1
476 vtable.id = idc[0]
477 vtable.classname = name
478
479 # Add current id to Array of super-ids
480 var ids_total = new Array[Int]
481 ids_total.add_all(ids)
482 ids_total.push(vtable.id)
483
484 var nb_methods_total = new Array[Int]
485 var nb_attributes_total = new Array[Int]
486
487 var self_methods = 0
488 var nb_introduced_attributes = 0
489
490 # Fixing offsets for self attributes and methods
491 var relative_offset_attr = 0
492 var relative_offset_meth = 0
493 for p in intro_mproperties(none_visibility) do
494 if p isa MMethod then
495 self_methods += 1
496 p.offset = relative_offset_meth
497 p.absolute_offset = offset_methods + relative_offset_meth
498 relative_offset_meth += 1
499 end
500 if p isa MAttribute then
501 nb_introduced_attributes += 1
502 p.offset = relative_offset_attr
503 p.absolute_offset = offset_attributes + relative_offset_attr
504 relative_offset_attr += 1
505 end
506 end
507
508 nb_methods_total.add_all(nb_methods)
509 nb_methods_total.push(self_methods)
510
511 nb_attributes_total.add_all(nb_attributes)
512 nb_attributes_total.push(nb_introduced_attributes)
513
514 # Save the offsets of self class
515 update_positions(offset_attributes, offset_methods, self)
516
517 # Since we have the number of attributes for each class, calculate the delta
518 var deltas = calculate_delta(nb_attributes_total)
519 vtable.internal_vtable = v.memory_manager.init_vtable(ids_total, nb_methods_total, deltas, vtable.mask)
520 end
521
522 # Fill the vtable with methods of `self` class
523 # * `v` : Current instance of the VirtualMachine
524 # * `table` : the table of self class, will be filled with its methods
525 private fun fill_vtable(v:VirtualMachine, table: VTable, cl: MClass)
526 do
527 var methods = new Array[MMethodDef]
528 for m in cl.intro_mproperties(none_visibility) do
529 if m isa MMethod then
530 # `propdef` is the most specific implementation for this MMethod
531 var propdef = m.lookup_first_definition(v.mainmodule, self.intro.bound_mtype)
532 methods.push(propdef)
533 end
534 end
535
536 # Call a method in C to put propdefs of self methods in the vtables
537 v.memory_manager.put_methods(vtable.internal_vtable, vtable.mask, cl.vtable.id, methods)
538 end
539
540 # Computes delta for each class
541 # A delta represents the offset for this group of attributes in the object
542 # *`nb_attributes` : number of attributes for each class (classes are linearized from Object to current)
543 # * return deltas for each class
544 private fun calculate_delta(nb_attributes: Array[Int]): Array[Int]
545 do
546 var deltas = new Array[Int]
547
548 var total = 0
549 for nb in nb_attributes do
550 deltas.push(total)
551 total += nb
552 end
553
554 return deltas
555 end
556
557 # Order superclasses of self
558 # Return the order of superclasses in runtime structures of this class
559 private fun superclasses_ordering(v: VirtualMachine): Array[MClass]
560 do
561 var superclasses = new Array[MClass]
562
563 # Add all superclasses of `self`
564 superclasses.add_all(self.in_hierarchy(v.mainmodule).greaters)
565
566 var res = new Array[MClass]
567 if superclasses.length > 1 then
568 # Starting at self
569 var ordering = self.dfs(v, res)
570
571 return ordering
572 else
573 # There is no super-class, self is Object
574 return superclasses
575 end
576 end
577
578 # A kind of Depth-First-Search for superclasses ordering
579 # *`v` : the current executed instance of VirtualMachine
580 # * `res` : Result Array, ie current superclasses ordering
581 private fun dfs(v: VirtualMachine, res: Array[MClass]): Array[MClass]
582 do
583 # Add this class at the beginning
584 res.insert(self, 0)
585
586 var direct_parents = self.in_hierarchy(v.mainmodule).direct_greaters.to_a
587
588 if direct_parents.length > 1 then
589 # Prefix represents the class which has the most properties
590 # we try to choose it in first to reduce the number of potential recompilations
591 var prefix = null
592 var max = -1
593 for cl in direct_parents do
594 # If we never have visited this class
595 if not res.has(cl) then
596 var properties_length = cl.all_mproperties(v.mainmodule, none_visibility).length
597 if properties_length > max then
598 max = properties_length
599 prefix = cl
600 end
601 end
602 end
603
604 if prefix != null then
605 # Add the prefix class ordering at the beginning of our sequence
606 var prefix_res = new Array[MClass]
607 prefix_res = prefix.dfs(v, prefix_res)
608
609 # Then we recurse on other classes
610 for cl in direct_parents do
611 if cl != prefix then
612 res = new Array[MClass]
613 res = cl.dfs(v, res)
614
615 for cl_res in res do
616 if not prefix_res.has(cl_res) then prefix_res.push(cl_res)
617 end
618 end
619 end
620 res = prefix_res
621 end
622
623 res.push(self)
624 else
625 if direct_parents.length > 0 then
626 res = direct_parents.first.dfs(v, res)
627 end
628 end
629
630 if not res.has(self) then res.push(self)
631
632 return res
633 end
634
635 # Update positions of the class `cl`
636 # * `attributes_offset`: absolute offset of introduced attributes
637 # * `methods_offset`: absolute offset of introduced methods
638 private fun update_positions(attributes_offsets: Int, methods_offset:Int, cl: MClass)
639 do
640 positions_attributes[cl] = attributes_offsets
641 positions_methods[cl] = methods_offset
642 end
643 end
644
645 redef class MAttribute
646 # Relative offset of this attribute in the runtime instance
647 # (beginning of the block of its introducing class)
648 var offset: Int
649
650 # Absolute offset of this attribute in the runtime instance (beginning of the attribute table)
651 var absolute_offset: Int
652 end
653
654 redef class MMethod
655 # Relative offset of this method in the virtual table (from the beginning of the block)
656 var offset: Int
657
658 # Absolute offset of this method in the virtual table (from the beginning of the vtable)
659 var absolute_offset: Int
660 end
661
662 # Redef MutableInstance to improve implementation of attributes in objects
663 redef class MutableInstance
664
665 # C-array to store pointers to attributes of this Object
666 var internal_attributes: Pointer
667 end
668
669 # Redef to associate an `Instance` to its `VTable`
670 redef class Instance
671
672 # Associate a runtime instance to its virtual table which contains methods, types etc.
673 var vtable: nullable VTable
674 end
675
676 # Is the type of the initial value inside attributes
677 class MInitType
678 super MType
679
680 redef var model: Model
681
682 redef fun to_s do return "InitType"
683 redef fun as_nullable do return self
684 redef fun need_anchor do return false
685 redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self
686 redef fun can_resolve_for(mtype, anchor, mmodule) do return true
687
688 redef fun collect_mclassdefs(mmodule) do return new HashSet[MClassDef]
689
690 redef fun collect_mclasses(mmodule) do return new HashSet[MClass]
691
692 redef fun collect_mtypes(mmodule) do return new HashSet[MClassType]
693 end
694
695 # A VTable contains the virtual method table for the dispatch
696 # and informations to perform subtyping tests
697 class VTable
698 # The mask to perform perfect hashing
699 var mask: Int is noinit
700
701 # Unique identifier given by perfect hashing
702 var id: Int is noinit
703
704 # Pointer to the c-allocated area, represents the virtual table
705 var internal_vtable: Pointer is noinit
706
707 # The short classname of this class
708 var classname: String is noinit
709 end
710
711 # Handle memory, used for allocate virtual table and associated structures
712 class MemoryManager
713
714 # Allocate and fill a virtual table
715 fun init_vtable(ids: Array[Int], nb_methods: Array[Int], nb_attributes: Array[Int], mask: Int): Pointer
716 do
717 # Allocate in C current virtual table
718 var res = intern_init_vtable(ids, nb_methods, nb_attributes, mask)
719
720 return res
721 end
722
723 # Construct virtual tables with a bi-dimensional layout
724 private fun intern_init_vtable(ids: Array[Int], nb_methods: Array[Int], deltas: Array[Int], mask: Int): Pointer
725 import Array[Int].length, Array[Int].[] `{
726
727 // Allocate and fill current virtual table
728 int i;
729 int total_size = 0; // total size of this virtual table
730 int nb_classes = Array_of_Int_length(nb_methods);
731 for(i = 0; i<nb_classes; i++) {
732 /* - One for each method of this class
733 * - One for the delta (offset of this group of attributes in objects)
734 * - One for the id
735 */
736 total_size += Array_of_Int__index(nb_methods, i);
737 total_size += 2;
738 }
739
740 // Add the size of the perfect hashtable (mask +1)
741 // Add one because we start to fill the vtable at position 1 (0 is the init position)
742 total_size += mask+2;
743 long unsigned int* vtable = malloc(sizeof(long unsigned int)*total_size);
744
745 // Initialisation to the first position of the virtual table (ie : Object)
746 long unsigned int *init = vtable + mask + 2;
747 for(i=0; i<total_size; i++)
748 vtable[i] = (long unsigned int)init;
749
750 // Set the virtual table to its position 0
751 // ie: after the hashtable
752 vtable = vtable + mask + 1;
753
754 int current_size = 1;
755 for(i = 0; i < nb_classes; i++) {
756 /*
757 vtable[hv] contains a pointer to the group of introduced methods
758 For each superclasse we have in virtual table :
759 (id | delta | introduced methods)
760 */
761 int hv = mask & Array_of_Int__index(ids, i);
762
763 vtable[current_size] = Array_of_Int__index(ids, i);
764 vtable[current_size + 1] = Array_of_Int__index(deltas, i);
765 vtable[-hv] = (long unsigned int)&(vtable[current_size]);
766
767 current_size += 2;
768 current_size += Array_of_Int__index(nb_methods, i);
769 }
770
771 return vtable;
772 `}
773
774 # Put implementation of methods of a class in `vtable`
775 # * `vtable` : Pointer to the C-virtual table
776 # * `mask` : perfect-hashing mask of the class corresponding to the vtable
777 # * `id` : id of the target class
778 # * `methods` : array of MMethodDef of the target class
779 fun put_methods(vtable: Pointer, mask: Int, id: Int, methods: Array[MMethodDef])
780 import Array[MMethodDef].length, Array[MMethodDef].[] `{
781
782 // Get the area to fill with methods by a sequence of perfect hashing
783 int hv = mask & id;
784 long unsigned int *pointer = (long unsigned int*)(((long unsigned int *)vtable)[-hv]);
785
786 // pointer+2 is the beginning of the area for methods implementation
787 int length = Array_of_MMethodDef_length(methods);
788 long unsigned int *area = (pointer + 2);
789 int i;
790
791 for(i=0; i<length; i++)
792 {
793 MMethodDef method = Array_of_MMethodDef__index(methods, i);
794 area[i] = (long unsigned int)method;
795 MMethodDef_incr_ref(method);
796 }
797 `}
798 end