# This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2014 Julien Pagès # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Implementation of the Nit virtual machine module vm intrude import interpreter::naive_interpreter import model_utils import perfect_hashing redef class ModelBuilder redef fun run_naive_interpreter(mainmodule: MModule, arguments: Array[String]) do var time0 = get_time self.toolcontext.info("*** NITVM STARTING ***", 1) var interpreter = new VirtualMachine(self, mainmodule, arguments) init_naive_interpreter(interpreter, mainmodule) var time1 = get_time self.toolcontext.info("*** NITVM STOPPING : {time1-time0} ***", 2) end end # A virtual machine based on the naive_interpreter class VirtualMachine super NaiveInterpreter # Perfect hashing and perfect numbering var ph: Perfecthashing = new Perfecthashing # Handles memory and garbage collection var memory_manager: MemoryManager = new MemoryManager # The unique instance of the `MInit` value var initialization_value: Instance init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String]) do super var init_type = new MInitType(mainmodule.model) initialization_value = new MutableInstance(init_type) end # Subtyping test for the virtual machine redef fun is_subtype(sub, sup: MType): Bool do var anchor = self.frame.arguments.first.mtype.as(MClassType) var sup_accept_null = false if sup isa MNullableType then sup_accept_null = true sup = sup.mtype else if sup isa MNullType then sup_accept_null = true end # Can `sub` provides null or not? # Thus we can match with `sup_accept_null` # Also discard the nullable marker if it exists if sub isa MNullableType then if not sup_accept_null then return false sub = sub.mtype else if sub isa MNullType then return sup_accept_null end # Now the case of direct null and nullable is over # An unfixed formal type can only accept itself if sup isa MParameterType or sup isa MVirtualType then return sub == sup end if sub isa MParameterType or sub isa MVirtualType then sub = sub.anchor_to(mainmodule, anchor) # Manage the second layer of null/nullable if sub isa MNullableType then if not sup_accept_null then return false sub = sub.mtype else if sub isa MNullType then return sup_accept_null end end assert sub isa MClassType # `sup` accepts only null if sup isa MNullType then return false assert sup isa MClassType # Create the sup vtable if not create if not sup.mclass.loaded then create_class(sup.mclass) # Sub can be discovered inside a Generic type during the subtyping test if not sub.mclass.loaded then create_class(sub.mclass) if anchor == null then anchor = sub if sup isa MGenericType then var sub2 = sub.supertype_to(mainmodule, anchor, sup.mclass) assert sub2.mclass == sup.mclass for i in [0..sup.mclass.arity[ do var sub_arg = sub2.arguments[i] var sup_arg = sup.arguments[i] var res = is_subtype(sub_arg, sup_arg) if res == false then return false end return true end var super_id = sup.mclass.vtable.id var mask = sub.mclass.vtable.mask return inter_is_subtype(super_id, mask, sub.mclass.vtable.internal_vtable) end # Subtyping test with perfect hashing private fun inter_is_subtype(id: Int, mask:Int, vtable: Pointer): Bool `{ // hv is the position in hashtable int hv = id & mask; // Follow the pointer to somewhere in the vtable long unsigned int *offset = (long unsigned int*)(((long int *)vtable)[-hv]); // If the pointed value is corresponding to the identifier, the test is true, otherwise false return *offset == id; `} # Redef init_instance to simulate the loading of a class redef fun init_instance(recv: Instance) do if not recv.mtype.as(MClassType).mclass.loaded then create_class(recv.mtype.as(MClassType).mclass) recv.vtable = recv.mtype.as(MClassType).mclass.vtable assert(recv isa MutableInstance) recv.internal_attributes = init_internal_attributes(initialization_value, recv.mtype.as(MClassType).mclass.all_mattributes(mainmodule, none_visibility).length) super end # Initialize the internal representation of an object (its attribute values) # `init_instance` is the initial value of attributes private fun init_internal_attributes(init_instance: Instance, size: Int): Pointer import Array[Instance].length, Array[Instance].[] `{ Instance* attributes = malloc(sizeof(Instance) * size); int i; for(i=0; i 1 then # Starting at self var ordering = self.dfs(v, res) return ordering else # There is no super-class, self is Object return superclasses end end # A kind of Depth-First-Search for superclasses ordering # `v` : the current executed instance of VirtualMachine # `res` : Result Array, ie current superclasses ordering private fun dfs(v: VirtualMachine, res: Array[MClass]): Array[MClass] do # Add this class at the beginning res.insert(self, 0) var direct_parents = self.in_hierarchy(v.mainmodule).direct_greaters.to_a if direct_parents.length > 1 then # Prefix represents the class which has the most properties # we try to choose it in first to reduce the number of potential recompilations var prefix = null var max = -1 for cl in direct_parents do # If we never have visited this class if not res.has(cl) then var properties_length = cl.all_mproperties(v.mainmodule, none_visibility).length if properties_length > max then max = properties_length prefix = cl end end end if prefix != null then # Add the prefix class ordering at the beginning of our sequence var prefix_res = new Array[MClass] prefix_res = prefix.dfs(v, prefix_res) # Then we recurse on other classes for cl in direct_parents do if cl != prefix then res = new Array[MClass] res = cl.dfs(v, res) for cl_res in res do if not prefix_res.has(cl_res) then prefix_res.push(cl_res) end end end res = prefix_res end res.push(self) else if direct_parents.length > 0 then res = direct_parents.first.dfs(v, res) end end if not res.has(self) then res.push(self) return res end # Update positions of self class in `parent` # `attributes_offset`: absolute offset of introduced attributes # `methods_offset`: absolute offset of introduced methods private fun update_positions(attributes_offsets: Int, methods_offset:Int, parent: MClass) do parent.positions_attributes[self] = attributes_offsets parent.positions_methods[self] = methods_offset end end redef class MAttribute # Represents the relative offset of this attribute in the runtime instance var offset: Int end # Redef MutableInstance to improve implementation of attributes in objects redef class MutableInstance # C-array to store pointers to attributes of this Object var internal_attributes: Pointer end # Is the type of the initial value inside attributes class MInitType super MType redef var model: Model protected init(model: Model) do self.model = model end redef fun to_s do return "InitType" redef fun as_nullable do return self redef fun need_anchor do return false redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do return self redef fun can_resolve_for(mtype, anchor, mmodule) do return true redef fun collect_mclassdefs(mmodule) do return new HashSet[MClassDef] redef fun collect_mclasses(mmodule) do return new HashSet[MClass] redef fun collect_mtypes(mmodule) do return new HashSet[MClassType] end # A VTable contains the virtual method table for the dispatch # and informations to perform subtyping tests class VTable # The mask to perform perfect hashing var mask: Int is noinit # Unique identifier given by perfect hashing var id: Int is noinit # Pointer to the c-allocated area, represents the virtual table var internal_vtable: Pointer is noinit # The short classname of this class var classname: String is noinit end redef class Instance var vtable: nullable VTable end # Handle memory, used for allocate virtual table and associated structures class MemoryManager # Allocate and fill a virtual table fun init_vtable(ids: Array[Int], nb_methods: Array[Int], nb_attributes: Array[Int], mask: Int): Pointer do # Allocate in C current virtual table var res = intern_init_vtable(ids, nb_methods, nb_attributes, mask) return res end # Construct virtual tables with a bi-dimensional layout private fun intern_init_vtable(ids: Array[Int], nb_methods: Array[Int], deltas: Array[Int], mask: Int): Pointer import Array[Int].length, Array[Int].[] `{ // Allocate and fill current virtual table int i; int total_size = 0; // total size of this virtual table int nb_classes = Array_of_Int_length(nb_methods); for(i = 0; i