scope: refuse `&x` where x is a local variable
[nit.git] / src / vm / virtual_machine.nit
index 2cb21d7..c7e57c6 100644 (file)
@@ -105,16 +105,18 @@ class VirtualMachine super NaiveInterpreter
 
                assert sup isa MClassType
 
-               # `sub` and `sup` can be discovered inside a Generic type during the subtyping test
-               if not sup.mclass.loaded then load_class(sup.mclass)
+               # and `sup` can be discovered inside a Generic type during the subtyping test
                if not sub.mclass.loaded then load_class(sub.mclass)
 
+               # If the target of the test is not-loaded yet, the subtyping-test will be false
+               if not sup.mclass.abstract_loaded then return false
+
                # For now, always use perfect hashing for subtyping test
                var super_id = sup.mclass.vtable.id
                var mask = sub.mclass.vtable.mask
 
                var res = inter_is_subtype_ph(super_id, mask, sub.mclass.vtable.internal_vtable)
-               if res == false then return false
+               if not res then return false
                # sub and sup can be generic types, each argument of generics has to be tested
 
                if not sup isa MGenericType then return true
@@ -125,7 +127,7 @@ class VirtualMachine super NaiveInterpreter
                        var sub_arg = sub2.arguments[i]
                        var sup_arg = sup.arguments[i]
                        var res2 = is_subtype(sub_arg, sup_arg)
-                       if res2 == false then return false
+                       if not res2 then return false
                end
                return true
        end
@@ -192,15 +194,39 @@ class VirtualMachine super NaiveInterpreter
                return attributes;
        `}
 
-       # Load the class and create its runtime structures
+       # Load the class and create its runtime structures, this loading is explicit
        fun load_class(mclass: MClass)
        do
                if mclass.loaded then return
 
-               # Recursively load superclasses
-               for parent in mclass.in_hierarchy(mainmodule).direct_greaters do load_class(parent)
+               load_supers(mclass)
+
+               if mclass.abstract_loaded then
+                       mclass.allocate_vtable(self)
+               else
+                       mclass.make_vt(self, true)
+               end
+       end
 
-               mclass.make_vt(self)
+       # Recursively load superclasses.
+       private fun load_supers(mclass: MClass)
+       do
+               for parent in mclass.in_hierarchy(mainmodule).direct_greaters do
+                       load_class_indirect(parent)
+               end
+       end
+
+       # This method is called to handle an implicitly loaded class,
+       # i.e. a superclass of an explicitly loaded class
+       # A class loaded implicitly will not be fully allocated
+       fun load_class_indirect(mclass: MClass)
+       do
+               # It the class was already implicitly loaded
+               if mclass.abstract_loaded then return
+
+               load_supers(mclass)
+
+               mclass.make_vt(self, false)
        end
 
        # Execute `mproperty` for a `args` (where `args[0]` is the receiver).
@@ -386,6 +412,9 @@ redef class MClass
        # True when the class is effectively loaded by the vm, false otherwise
        var loaded: Bool = false
 
+       # Indicate this class was partially loaded (it only has its identifier allocated)
+       var abstract_loaded: Bool = false
+
        # Color for Cohen subtyping test : the absolute position of the id
        # of this class in virtual tables
        var color: Int
@@ -425,13 +454,14 @@ redef class MClass
        var mmethods = new Array[MMethod]
 
        # Allocates a VTable for this class and gives it an id
-       private fun make_vt(vm: VirtualMachine)
+       # * `vm` The currently executed VirtualMachine
+       # * `explicit` Indicate if this class was directly instantiated (i.e. not indirectly loaded)
+       private fun make_vt(vm: VirtualMachine, explicit: Bool)
        do
                # `ordering` contains the order of superclasses for virtual tables
                ordering = superclasses_ordering(vm)
                ordering.remove(self)
 
-               # Make_vt for super-classes
                var ids = new Array[Int]
                var nb_methods = new Array[Int]
                var nb_attributes = new Array[Int]
@@ -472,53 +502,69 @@ redef class MClass
                        offset_methods += 2 # Because each block starts with an id and the delta
                end
 
-               # Recopy the position tables of the prefix in `self`
-               for key, value in prefix.positions_methods do
-                       positions_methods[key] = value
-               end
+               # Update the positions of the class
+               update_positions(offset_attributes, offset_methods)
 
-               for key, value in prefix.positions_attributes do
-                       positions_attributes[key] = value
-               end
+               ordering.add(self)
 
-               # When all super-classes have their identifiers and vtables, allocate current one
-               allocate_vtable(vm, ids, nb_methods, nb_attributes, offset_attributes, offset_methods)
-               loaded = true
+               # Compute the identifier with Perfect Hashing
+               compute_identifier(vm, ids, offset_methods)
 
-               # The virtual table now needs to be filled with pointer to methods
-               ordering.add(self)
-               for cl in ordering do
-                       fill_vtable(vm, vtable.as(not null), cl)
+               # Update caches and offsets of methods and attributes for this class
+               # If the loading was explicit, the virtual table will be allocated and filled
+               set_offsets(vm, explicit)
+
+               if not explicit then
+                       # Just init the C-pointer to NULL to avoid errors
+                       vtable.internal_vtable = vm.memory_manager.null_ptr
                end
        end
 
-       # Allocate a single vtable
-       # * `ids : Array of superclasses identifiers
-       # * `nb_methods : Array which contain the number of introduced methods for each class in ids
-       # * `nb_attributes : Array which contain the number of introduced attributes for each class in ids
-       # * `offset_attributes : Offset from the beginning of the table of the group of attributes
+       # Allocate a unique identifier to the class with perfect hashing
+       # * `vm` The currently executed VirtualMachine
+       # * `ids` Array of superclasses identifiers
        # * `offset_methods : Offset from the beginning of the table of the group of methods
-       private fun allocate_vtable(v: VirtualMachine, ids: Array[Int], nb_methods: Array[Int], nb_attributes: Array[Int],
-                       offset_attributes: Int, offset_methods: Int)
+       private fun compute_identifier(vm: VirtualMachine, ids: Array[Int], offset_methods: Int)
        do
                vtable = new VTable
                var idc = new Array[Int]
 
-               vtable.mask = v.ph.pnand(ids, 1, idc) - 1
+               # Give an identifier to the class and put it inside the virtual table
+               vtable.mask = vm.ph.pnand(ids, 1, idc) - 1
                vtable.id = idc[0]
                vtable.classname = name
 
-               # Add current id to Array of super-ids
-               var ids_total = new Array[Int]
-               ids_total.add_all(ids)
-               ids_total.push(vtable.id)
+               # Set the color for subtyping tests in SST of this class
+               color = offset_methods - 2
 
-               var nb_methods_total = new Array[Int]
-               var nb_attributes_total = new Array[Int]
+               # Indicate the class has its identifier computed
+               abstract_loaded = true
+       end
+
+       # Update the positions of this class
+       # * `offset_attributes` The offset of the block of attributes of this class
+       # * `offset_methods` The offset of the block of methods of this class
+       private fun update_positions(offset_attributes: Int, offset_methods: Int)
+       do
+               # Recopy the position tables of the prefix in `self`
+               for key, value in prefix.positions_methods do
+                       positions_methods[key] = value
+               end
 
-               var self_methods = 0
-               var nb_introduced_attributes = 0
+               for key, value in prefix.positions_attributes do
+                       positions_attributes[key] = value
+               end
+
+               # Save the offsets of self class
+               position_attributes = offset_attributes
+               position_methods = offset_methods
+       end
 
+       # Set the offsets for the properties introduced by `self` class
+       # * `vm` The currently executed VirtualMachine
+       # * `explicit` Indicate if this class was explicitly loaded
+       private fun set_offsets(vm: VirtualMachine, explicit: Bool)
+       do
                # Fixing offsets for self attributes and methods
                var relative_offset_attr = 0
                var relative_offset_meth = 0
@@ -530,17 +576,13 @@ redef class MClass
                        for p in classdef.intro_mproperties do
                                # Collect properties and fixing offsets
                                if p isa MMethod then
-                                       self_methods += 1
                                        p.offset = relative_offset_meth
-                                       p.absolute_offset = offset_methods + relative_offset_meth
                                        relative_offset_meth += 1
 
                                        intro_mmethods.add(p)
                                end
                                if p isa MAttribute then
-                                       nb_introduced_attributes += 1
                                        p.offset = relative_offset_attr
-                                       p.absolute_offset = offset_attributes + relative_offset_attr
                                        relative_offset_attr += 1
 
                                        intro_mattributes.add(p)
@@ -552,22 +594,33 @@ redef class MClass
                mattributes.add_all(intro_mattributes)
                mmethods.add_all(intro_mmethods)
 
-               nb_methods_total.add_all(nb_methods)
-               nb_methods_total.push(self_methods)
-
-               nb_attributes_total.add_all(nb_attributes)
-               nb_attributes_total.push(nb_introduced_attributes)
+               if explicit then allocate_vtable(vm)
+       end
 
-               # Set the color for subtyping test of this class
-               color = offset_methods - 2
+       # Allocate a single vtable
+       # * `vm` The currently executed VirtualMachine
+       private fun allocate_vtable(vm: VirtualMachine)
+       do
+               var ids = new Array[Int]
+               var nb_methods_total = new Array[Int]
+               var nb_attributes_total = new Array[Int]
 
-               # Save the offsets of self class
-               position_attributes = offset_attributes
-               position_methods = offset_methods
+               for cl in ordering do
+                       ids.add(cl.vtable.id)
+                       nb_methods_total.add(cl.intro_mmethods.length)
+                       nb_attributes_total.add(cl.intro_mattributes.length)
+               end
 
-               # Since we have the number of attributes for each class, calculate the delta
+               # Calculate the delta to prepare object structure
                var deltas = calculate_delta(nb_attributes_total)
-               vtable.internal_vtable = v.memory_manager.init_vtable(ids_total, nb_methods_total, deltas, vtable.mask)
+               vtable.internal_vtable = vm.memory_manager.init_vtable(ids, nb_methods_total, deltas, vtable.mask)
+
+               # The virtual table now needs to be filled with pointer to methods
+               for cl in ordering do
+                       fill_vtable(vm, vtable.as(not null), cl)
+               end
+
+               loaded = true
        end
 
        # Fill the vtable with local methods for `self` class
@@ -707,6 +760,7 @@ redef class MClass
 
                                var super_id = current_class.vtable.id
                                var mask = sub.vtable.mask
+                               vm.load_class(sub)
 
                                if vm.inter_is_subtype_ph(super_id, mask, sub.vtable.internal_vtable) then
                                        if not sub.positions_methods.has_key(current_class) then
@@ -746,6 +800,7 @@ redef class MClass
 
                                var super_id = current_class.vtable.id
                                var mask = sub.vtable.mask
+                               vm.load_class(sub)
 
                                if vm.inter_is_subtype_ph(super_id, mask, sub.vtable.internal_vtable) then
                                        if not sub.positions_methods.has_key(current_class) then
@@ -802,21 +857,21 @@ redef class MClass
        end
 end
 
+redef class MProperty
+       # Relative offset of this in the runtime instance
+       # (beginning of the block of its introducing class for attributes or methods)
+       var offset: Int
+end
+
 redef class MAttribute
        # Relative offset of this attribute in the runtime instance
        # (beginning of the block of its introducing class)
-       var offset: Int
-
-       # Absolute offset of this attribute in the runtime instance (beginning of the attribute table)
-       var absolute_offset: Int
+       redef var offset: Int
 end
 
 redef class MMethod
        # Relative offset of this method in the virtual table (from the beginning of the block)
-       var offset: Int
-
-       # Absolute offset of this method in the virtual table (from the beginning of the vtable)
-       var absolute_offset: Int
+       redef var offset: Int
 end
 
 # Redef MutableInstance to improve implementation of attributes in objects
@@ -955,4 +1010,9 @@ class MemoryManager
                        MMethodDef_incr_ref(method);
                }
        `}
+
+       # Return a NULL pointer, used to initialize virtual tables
+       private fun null_ptr: Pointer `{
+               return NULL;
+       `}
 end