From: Jean Privat Date: Tue, 17 Jun 2014 02:17:34 +0000 (-0400) Subject: Merge: Compilation to JavaScript using the Emscripten SDK X-Git-Tag: v0.6.6~28 X-Git-Url: http://nitlanguage.org?hp=0543b5d0585a36af4e7a622cb38c03e01a6ed8b0 Merge: Compilation to JavaScript using the Emscripten SDK Published for review but still needs some doc and examples. Works with most small examples but not yet with the naive_interpreter (but it's not clear why). Usage: apt-get emscripten nitg -m emscripten examples/hello_world.nit nodejs hello_wold.js Pull-Request: #506 Reviewed-by: Jean Privat Reviewed-by: Lucas Bajolet Reviewed-by: Alexandre Terrasa --- diff --git a/examples/mnit_ballz/src/ballz_android.nit b/examples/mnit_ballz/src/ballz_android.nit index 18e1b84..823edd9 100644 --- a/examples/mnit_ballz/src/ballz_android.nit +++ b/examples/mnit_ballz/src/ballz_android.nit @@ -16,15 +16,12 @@ module ballz_android -import realtime import game_logic redef class App var screen: nullable Screen - var target_dt = 20000000 - redef fun run do sensors_support_enabled = true @@ -34,6 +31,7 @@ redef class App gyroscope.enabled = true light.enabled = true proximity.enabled = true + maximum_fps = 50 super end @@ -48,16 +46,8 @@ redef class App do var screen = self.screen if screen != null then - var clock = new Clock - screen.game.do_turn screen.do_frame(display) - - var dt = clock.lapse - if dt.sec == 0 and dt.nanosec < target_dt then - var sleep_t = target_dt - dt.nanosec - sys.nanosleep(0, sleep_t) - end end end diff --git a/examples/mnit_dino/src/dino.nit b/examples/mnit_dino/src/dino.nit index 23314f8..11efd91 100644 --- a/examples/mnit_dino/src/dino.nit +++ b/examples/mnit_dino/src/dino.nit @@ -21,7 +21,6 @@ module dino is end import mnit -import realtime import graphism import fancy_dino @@ -31,8 +30,6 @@ redef class App var cavemen_at_first_level = 6 var cavemen_incr = 4 - var target_dt = 12000000 - var game : nullable Game = null var score = new Container[Int](0) var imgs : nullable ImageSet = null @@ -42,6 +39,8 @@ redef class App do super + maximum_fps = 80 + var display = display assert display != null @@ -59,16 +58,8 @@ redef class App do var game = game if game != null then - var clock = new Clock - var turn = game.do_turn game.draw( display, imgs.as(not null), turn ) - - var dt = clock.lapse - if dt.sec == 0 and dt.nanosec < target_dt then - var sleep_t = target_dt - dt.nanosec - sys.nanosleep(0, sleep_t) - end else splash.draw( display, true ) end diff --git a/examples/mnit_moles/src/moles.nit b/examples/mnit_moles/src/moles.nit index a0d783a..08642d1 100644 --- a/examples/mnit_moles/src/moles.nit +++ b/examples/mnit_moles/src/moles.nit @@ -20,7 +20,6 @@ module moles import mnit -import realtime class Hole var game: Game @@ -231,12 +230,11 @@ redef class App var screen: nullable Screen = null - var target_dt = 20000000 - redef fun window_created do super + maximum_fps = 50 init_screen_and_game end @@ -246,16 +244,8 @@ redef class App do var screen = self.screen if screen != null then - var clock = new Clock - screen.game.do_turn screen.do_frame(display) - - var dt = clock.lapse - if dt.sec == 0 and dt.nanosec < target_dt then - var sleep_t = target_dt - dt.nanosec - sys.nanosleep(0, sleep_t) - end end end diff --git a/examples/shoot/src/shoot.nit b/examples/shoot/src/shoot.nit index f4001c8..48cadaa 100644 --- a/examples/shoot/src/shoot.nit +++ b/examples/shoot/src/shoot.nit @@ -380,9 +380,6 @@ redef class App if not self.scene.exists then quit = true end self.scene.draw_on_display(self) - - # Wait the next frame - sys.nanosleep(0, 16000000) end var paused: Bool = false diff --git a/lib/mnit/mnit.nit b/lib/mnit/mnit.nit index 3e313d4..53c4681 100644 --- a/lib/mnit/mnit.nit +++ b/lib/mnit/mnit.nit @@ -21,3 +21,4 @@ import mnit_app import opengles1 import assets import numbers +import mnit_fps diff --git a/lib/mnit/mnit_fps.nit b/lib/mnit/mnit_fps.nit new file mode 100644 index 0000000..cd30e83 --- /dev/null +++ b/lib/mnit/mnit_fps.nit @@ -0,0 +1,75 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# 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. + +# Frame-rate control for applications +module mnit_fps + +import mnit_app +private import realtime + +redef class App + # Limit the frame-rate to a given frequency + # This basically limits how much `frame_core` is called per second. + # Zero (or a negative value) means no limit. + # + # Applications can modify this value even during the main-loop. + var maximum_fps writable = 60 + + # Current frame-rate + # Updated each 5 seconds. + var current_fps = 0.0 + + redef fun full_frame + do + super + limit_fps + end + + # The clock for limit_fps + private var clock = new Clock + + # Number of frames since the last deadline + # Used tocompute `current_fps`. + private var frame_count = 0 + + # Deadline used to compute `current_fps` + private var frame_count_deadline = 0 + + # Check and sleep to maitain a frame-rate bellow `maximum_fps` + # Also periodically uptate `current_fps` + # Is automatically called at the end of `full_frame`. + fun limit_fps + do + var t = clock.total.sec + if t >= frame_count_deadline then + var cfps = frame_count_deadline.to_f / 5.0 + self.current_fps = cfps + frame_count = 0 + frame_count_deadline = t + 5 + end + frame_count += 1 + + var mfps = maximum_fps + if mfps <= 0 then return + var dt = clock.lapse + var target_dt = 1000000000 / mfps + var sec = dt.sec + var nanosec = dt.nanosec + if sec == 0 and nanosec < target_dt then + var sleep_t = target_dt - nanosec + sys.nanosleep(0, sleep_t) + dt = clock.lapse + end + end +end diff --git a/lib/perfect_hashing.nit b/lib/perfect_hashing.nit index 9ac59f3..a4b8417 100644 --- a/lib/perfect_hashing.nit +++ b/lib/perfect_hashing.nit @@ -35,7 +35,7 @@ class Perfecthashing do # By default, all identifiers are available interval = new List[Couple[nullable Int, nullable Int]] - interval.push(new Couple[nullable Int, nullable Int](0, null)) + interval.push(new Couple[nullable Int, nullable Int](1, null)) tempht = new Array[nullable Int] end diff --git a/src/modelize_class.nit b/src/modelize_class.nit index 4cd9475..09b7081 100644 --- a/src/modelize_class.nit +++ b/src/modelize_class.nit @@ -97,7 +97,12 @@ redef class ModelBuilder error(nvisibility, "Error: refinement changed the visibility from a {mclass.visibility} to a {mvisibility}") end nclassdef.mclass = mclass - nmodule.mclass2nclassdef[mclass] = nclassdef + if not nmodule.mclass2nclassdef.has_key(mclass) then + nmodule.mclass2nclassdef[mclass] = nclassdef + nclassdef.all_defs = [nclassdef] + else + nmodule.mclass2nclassdef[mclass].all_defs.add(nclassdef) + end end # Visit the AST and create the `MClassDef` objects @@ -107,7 +112,14 @@ redef class ModelBuilder var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object") var mclass = nclassdef.mclass if mclass == null then return # Skip error - #var mclassdef = nclassdef.mclassdef.as(not null) + + # In case of non-standard AClassdef, try to attach to an already existing mclassdef + var other_nclassdef = nmodule.mclass2nclassdef[mclass] + if other_nclassdef != nclassdef then + assert not nclassdef isa AStdClassdef + nclassdef.mclassdef = other_nclassdef.mclassdef + return + end var names = new Array[String] var bounds = new Array[MType] @@ -281,8 +293,7 @@ redef class ModelBuilder if errcount != toolcontext.error_count then return # Create the mclassdef hierarchy - for nclassdef in nmodule.n_classdefs do - var mclassdef = nclassdef.mclassdef.as(not null) + for mclassdef in mmodule.mclassdefs do mclassdef.add_in_hierarchy end @@ -505,6 +516,8 @@ redef class AClassdef var mclass: nullable MClass # The associated MClassDef once build by a `ModelBuilder` var mclassdef: nullable MClassDef + # All (self and other) definitions for the same mclassdef + var all_defs: nullable Array[AClassdef] end redef class AClasskind diff --git a/src/modelize_property.nit b/src/modelize_property.nit index e24541e..3563862 100644 --- a/src/modelize_property.nit +++ b/src/modelize_property.nit @@ -28,6 +28,7 @@ private class ModelizePropertyPhase redef fun process_nmodule(nmodule) do for nclassdef in nmodule.n_classdefs do + if nclassdef.all_defs == null then continue # skip non principal classdef toolcontext.modelbuilder.build_properties(nclassdef) end end @@ -52,14 +53,16 @@ redef class ModelBuilder build_properties(mclassdef2nclassdef[superclassdef]) end - for npropdef in nclassdef.n_propdefs do - npropdef.build_property(self, nclassdef) - end - for npropdef in nclassdef.n_propdefs do - npropdef.build_signature(self) - end - for npropdef in nclassdef.n_propdefs do - npropdef.check_signature(self) + for nclassdef2 in nclassdef.all_defs do + for npropdef in nclassdef2.n_propdefs do + npropdef.build_property(self, mclassdef) + end + for npropdef in nclassdef2.n_propdefs do + npropdef.build_signature(self) + end + for npropdef in nclassdef2.n_propdefs do + npropdef.check_signature(self) + end end process_default_constructors(nclassdef) end @@ -225,7 +228,9 @@ redef class AClassdef # The free init (implicitely constructed by the class if required) var mfree_init: nullable MMethodDef = null +end +redef class MClassDef # What is the `APropdef` associated to a `MProperty`? # Used to check multiple definition of a property. var mprop2npropdef: Map[MProperty, APropdef] = new HashMap[MProperty, APropdef] @@ -260,7 +265,7 @@ redef class APropdef # The associated propdef once build by a `ModelBuilder` var mpropdef: nullable MPROPDEF writable - private fun build_property(modelbuilder: ModelBuilder, nclassdef: AClassdef) is abstract + private fun build_property(modelbuilder: ModelBuilder, mclassdef: MClassDef) is abstract private fun build_signature(modelbuilder: ModelBuilder) is abstract private fun check_signature(modelbuilder: ModelBuilder) is abstract private fun new_property_visibility(modelbuilder: ModelBuilder, mclassdef: MClassDef, nvisibility: nullable AVisibility): MVisibility @@ -306,13 +311,13 @@ redef class APropdef end end - private fun check_redef_keyword(modelbuilder: ModelBuilder, nclassdef: AClassdef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool + private fun check_redef_keyword(modelbuilder: ModelBuilder, mclassdef: MClassDef, kwredef: nullable Token, need_redef: Bool, mprop: MProperty): Bool do - if nclassdef.mprop2npropdef.has_key(mprop) then - modelbuilder.error(self, "Error: A property {mprop} is already defined in class {nclassdef.mclassdef.mclass} at line {nclassdef.mprop2npropdef[mprop].location.line_start}.") + if mclassdef.mprop2npropdef.has_key(mprop) then + modelbuilder.error(self, "Error: A property {mprop} is already defined in class {mclassdef.mclass} at line {mclassdef.mprop2npropdef[mprop].location.line_start}.") return false end - if mprop isa MMethod and mprop.is_toplevel != (nclassdef isa ATopClassdef) then + if mprop isa MMethod and mprop.is_toplevel != (parent isa ATopClassdef) then if mprop.is_toplevel then modelbuilder.error(self, "Error: {mprop} is a top level method.") else @@ -323,12 +328,12 @@ redef class APropdef end if kwredef == null then if need_redef then - modelbuilder.error(self, "Redef error: {nclassdef.mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.") + modelbuilder.error(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.") return false end else if not need_redef then - modelbuilder.error(self, "Error: No property {nclassdef.mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.") + modelbuilder.error(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.") return false end end @@ -416,12 +421,11 @@ end redef class AMethPropdef redef type MPROPDEF: MMethodDef - redef fun build_property(modelbuilder, nclassdef) + redef fun build_property(modelbuilder, mclassdef) do var n_kwinit = n_kwinit var n_kwnew = n_kwnew var is_init = n_kwinit != null or n_kwnew != null - var mclassdef = nclassdef.mclassdef.as(not null) var name: String var amethodid = self.n_methid var name_node: ANode @@ -458,13 +462,13 @@ redef class AMethPropdef mprop = new MMethod(mclassdef, name, mvisibility) mprop.is_init = is_init mprop.is_new = n_kwnew != null - if nclassdef isa ATopClassdef then mprop.is_toplevel = true - if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mprop) then return + if parent isa ATopClassdef then mprop.is_toplevel = true + if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mprop) then return else - if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, not self isa AMainMethPropdef, mprop) then return check_redef_property_visibility(modelbuilder, self.n_visibility, mprop) end - nclassdef.mprop2npropdef[mprop] = self + mclassdef.mprop2npropdef[mprop] = self var mpropdef = new MMethodDef(mclassdef, mprop, self.location) @@ -624,9 +628,8 @@ redef class AAttrPropdef var mreadpropdef: nullable MMethodDef writable # The associated setter (write accessor) if any var mwritepropdef: nullable MMethodDef writable - redef fun build_property(modelbuilder, nclassdef) + redef fun build_property(modelbuilder, mclassdef) do - var mclassdef = nclassdef.mclassdef.as(not null) var mclass = mclassdef.mclass var name: String @@ -651,13 +654,13 @@ redef class AAttrPropdef if mprop == null then var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility) mprop = new MAttribute(mclassdef, name, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, false, mprop) then return else assert mprop isa MAttribute check_redef_property_visibility(modelbuilder, self.n_visibility, mprop) - if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, true, mprop) then return end - nclassdef.mprop2npropdef[mprop] = self + mclassdef.mprop2npropdef[mprop] = self var mpropdef = new MAttributeDef(mclassdef, mprop, self.location) self.mpropdef = mpropdef @@ -671,12 +674,12 @@ redef class AAttrPropdef if mreadprop == null then var mvisibility = new_property_visibility(modelbuilder, mclassdef, nreadable.n_visibility) mreadprop = new MMethod(mclassdef, readname, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, false, mreadprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, nreadable.n_kwredef, false, mreadprop) then return else - if not self.check_redef_keyword(modelbuilder, nclassdef, nreadable.n_kwredef, true, mreadprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, nreadable.n_kwredef, true, mreadprop) then return check_redef_property_visibility(modelbuilder, nreadable.n_visibility, mreadprop) end - nclassdef.mprop2npropdef[mreadprop] = self + mclassdef.mprop2npropdef[mreadprop] = self var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location) self.mreadpropdef = mreadpropdef @@ -691,12 +694,12 @@ redef class AAttrPropdef if mwriteprop == null then var mvisibility = new_property_visibility(modelbuilder, mclassdef, nwritable.n_visibility) mwriteprop = new MMethod(mclassdef, writename, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, false, mwriteprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, nwritable.n_kwredef, false, mwriteprop) then return else - if not self.check_redef_keyword(modelbuilder, nclassdef, nwritable.n_kwredef, true, mwriteprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, nwritable.n_kwredef, true, mwriteprop) then return check_redef_property_visibility(modelbuilder, nwritable.n_visibility, mwriteprop) end - nclassdef.mprop2npropdef[mwriteprop] = self + mclassdef.mprop2npropdef[mwriteprop] = self var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location) self.mwritepropdef = mwritepropdef @@ -717,12 +720,12 @@ redef class AAttrPropdef if mreadprop == null then var mvisibility = new_property_visibility(modelbuilder, mclassdef, self.n_visibility) mreadprop = new MMethod(mclassdef, readname, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, false, mreadprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, false, mreadprop) then return else - if not self.check_redef_keyword(modelbuilder, nclassdef, n_kwredef, true, mreadprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, n_kwredef, true, mreadprop) then return check_redef_property_visibility(modelbuilder, self.n_visibility, mreadprop) end - nclassdef.mprop2npropdef[mreadprop] = self + mclassdef.mprop2npropdef[mreadprop] = self var mreadpropdef = new MMethodDef(mclassdef, mreadprop, self.location) self.mreadpropdef = mreadpropdef @@ -742,14 +745,14 @@ redef class AAttrPropdef mvisibility = private_visibility end mwriteprop = new MMethod(mclassdef, writename, mvisibility) - if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, false, mwriteprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, false, mwriteprop) then return else - if not self.check_redef_keyword(modelbuilder, nclassdef, nwkwredef, true, mwriteprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, nwkwredef, true, mwriteprop) then return if nwritable != null then check_redef_property_visibility(modelbuilder, nwritable.n_visibility, mwriteprop) end end - nclassdef.mprop2npropdef[mwriteprop] = self + mclassdef.mprop2npropdef[mwriteprop] = self var mwritepropdef = new MMethodDef(mclassdef, mwriteprop, self.location) self.mwritepropdef = mwritepropdef @@ -934,9 +937,8 @@ end redef class ATypePropdef redef type MPROPDEF: MVirtualTypeDef - redef fun build_property(modelbuilder, nclassdef) + redef fun build_property(modelbuilder, mclassdef) do - var mclassdef = nclassdef.mclassdef.as(not null) var name = self.n_id.text var mprop = modelbuilder.try_get_mproperty_by_name(self.n_id, mclassdef, name) if mprop == null then @@ -946,13 +948,13 @@ redef class ATypePropdef modelbuilder.warning(n_id, "Warning: lowercase in the virtual type {name}") break end - if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, false, mprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, false, mprop) then return else - if not self.check_redef_keyword(modelbuilder, nclassdef, self.n_kwredef, true, mprop) then return + if not self.check_redef_keyword(modelbuilder, mclassdef, self.n_kwredef, true, mprop) then return assert mprop isa MVirtualTypeProp check_redef_property_visibility(modelbuilder, self.n_visibility, mprop) end - nclassdef.mprop2npropdef[mprop] = self + mclassdef.mprop2npropdef[mprop] = self var mpropdef = new MVirtualTypeDef(mclassdef, mprop, self.location) self.mpropdef = mpropdef diff --git a/src/nitvm.nit b/src/nitvm.nit new file mode 100644 index 0000000..9a62208 --- /dev/null +++ b/src/nitvm.nit @@ -0,0 +1,62 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2012 Jean Privat +# +# 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. + +# The Nit virtual machine launcher +module nitvm + +import vm + +# Create a tool context to handle options and paths +var toolcontext = new ToolContext +toolcontext.tooldescription = "Usage: nitvm [OPTION]... ...\nExecutes Nit programs with a virtual machine." +# Add an option "-o" to enable compatibility with the tests.sh script +var opt = new OptionString("compatibility (does nothing)", "-o") +toolcontext.option_context.add_option(opt) +var opt_mixins = new OptionArray("Additional modules to min-in", "-m") +toolcontext.option_context.add_option(opt_mixins) +# We do not add other options, so process them now! +toolcontext.process_options(args) + +# We need a model to collect stufs +var model = new Model + +# Add a model builder to parse files +var modelbuilder = new ModelBuilder(model, toolcontext.as(not null)) + +var arguments = toolcontext.option_context.rest +var progname = arguments.first + +# Here we load and process all modules passed on the command line +var mmodules = modelbuilder.parse([progname]) +mmodules.add_all modelbuilder.parse(opt_mixins.value) +modelbuilder.run_phases + +if toolcontext.opt_only_metamodel.value then exit(0) + +var mainmodule: nullable MModule + +# Here we launch the interpreter on the main module +if mmodules.length == 1 then + mainmodule = mmodules.first +else + mainmodule = new MModule(model, null, mmodules.first.name, mmodules.first.location) + mainmodule.set_imported_mmodules(mmodules) +end + +var self_mm = mainmodule.as(not null) +var self_args = arguments.as(not null) + +modelbuilder.run_naive_interpreter(self_mm, self_args) diff --git a/src/typing.nit b/src/typing.nit index edb1c89..5db0ada 100644 --- a/src/typing.nit +++ b/src/typing.nit @@ -51,6 +51,11 @@ private class TypeVisitor var selfvariable: Variable = new Variable("self") + # Is `self` use restricted? + # * no explicit `self` + # * method called on the implicit self must be top-level + var is_toplevel_context = false + init(modelbuilder: ModelBuilder, mmodule: MModule, mpropdef: nullable MPropDef) do self.modelbuilder = modelbuilder @@ -67,6 +72,11 @@ private class TypeVisitor var selfvariable = new Variable("self") self.selfvariable = selfvariable selfvariable.declared_type = mclass.mclass_type + + var mprop = mpropdef.mproperty + if mprop isa MMethod and mprop.is_toplevel then + is_toplevel_context = true + end end end @@ -241,6 +251,15 @@ private class TypeVisitor end assert mproperty isa MMethod + + if is_toplevel_context and recv_is_self and not mproperty.is_toplevel and name != "sys" and name != "exit" and name != "args" then + # FIXME named methods are here as a workaround + error(node, "Error: '{name}' is not a top-level method, thus need a receiver.") + end + if not recv_is_self and mproperty.is_toplevel then + error(node, "Error: cannot call '{name}', a top-level method, with a receiver.") + end + if mproperty.visibility == protected_visibility and not recv_is_self and self.mmodule.visibility_for(mproperty.intro_mclassdef.mmodule) < intrude_visibility and not modelbuilder.toolcontext.opt_ignore_visibility.value then self.modelbuilder.error(node, "Error: Method '{name}' is protected and can only acceded by self.") return null @@ -833,7 +852,7 @@ redef class AForExpr if objcla == null then return # check iterator method - var itdef = v.get_method(self, mtype, "iterator", true) + var itdef = v.get_method(self, mtype, "iterator", n_expr isa ASelfExpr) if itdef == null then v.error(self, "Type Error: 'for' expects a type providing 'iterator' method, got '{mtype}'.") return @@ -1199,6 +1218,9 @@ redef class ASelfExpr redef var its_variable: nullable Variable redef fun accept_typing(v) do + if v.is_toplevel_context and not self isa AImplicitSelfExpr then + v.error(self, "Error: self cannot be used in top-level method.") + end var variable = v.selfvariable self.its_variable = variable self.mtype = v.get_variable(self, variable) diff --git a/src/vm.nit b/src/vm.nit new file mode 100644 index 0000000..3c8f048 --- /dev/null +++ b/src/vm.nit @@ -0,0 +1,314 @@ +# 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 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 + + # 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) + super + + recv.vtable = recv.mtype.as(MClassType).mclass.vtable + end + + # Creates the runtime structures for this class + fun create_class(mclass: MClass) do mclass.make_vt(self) +end + +redef class MClass + # A reference to the virtual table of this class + var vtable: nullable VTable + + # True when the class is effectively loaded by the vm, false otherwise + var loaded: Bool = false + + # Allocates a VTable for this class and gives it an id + private fun make_vt(v: VirtualMachine) + do + if loaded then return + + # Superclasses contains all superclasses except self + var superclasses = new Array[MClass] + superclasses.add_all(ancestors) + superclasses.remove(self) + v.mainmodule.linearize_mclasses(superclasses) + + # Make_vt for super-classes + var ids = new Array[Int] + var nb_methods = new Array[Int] + + for parent in superclasses do + if parent.vtable == null then parent.make_vt(v) + + # Get the number of introduced methods for this class + var count = 0 + var min_visibility = public_visibility + for p in parent.intro_mproperties(min_visibility) do + if p isa MMethod then + count += 1 + end + end + + ids.push(parent.vtable.id) + nb_methods.push(count) + end + + # When all super-classes have their identifiers and vtables, allocate current one + allocate_vtable(v, ids, nb_methods) + loaded = true + # The virtual table now needs to be filled with pointer to methods + end + + # Allocate a single vtable + # ids : Array of superclasses identifiers + # nb_methods : Array which contain the number of methods for each class in ids + private fun allocate_vtable(v: VirtualMachine, ids: Array[Int], nb_methods: Array[Int]) + do + vtable = new VTable + var idc = new Array[Int] + + vtable.mask = v.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) + + var nb_methods_total = new Array[Int] + var count = 0 + var min_visibility = public_visibility + for p in intro_mproperties(min_visibility) do + if p isa MMethod then + count += 1 + end + end + nb_methods_total.add_all(nb_methods) + nb_methods_total.push(count) + + vtable.internal_vtable = v.memory_manager.init_vtable(ids_total, nb_methods_total, vtable.mask) + end +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 + + # Unique identifier given by perfect hashing + var id: Int + + # Pointer to the c-allocated area, represents the virtual table + var internal_vtable: Pointer + + # The short classname of this class + var classname: String + + init do end +end + +redef class Instance + + var vtable: nullable VTable + + init(mt: MType) + do + mtype = mt + + # An instance is associated to its class virtual table + if mt isa MClassType then + vtable = mt.mclass.vtable + end + end +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], mask: Int): Pointer + do + # Allocate in C current virtual table + var res = intern_init_vtable(ids, nb_methods, mask) + + return res + end + + # Construct virtual tables with a bi-dimensional layout + private fun intern_init_vtable(ids: Array[Int], nb_methods: 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 import end -interface Object +interface Object end enum Bool diff --git a/tests/sav/nitmetrics_args1.res b/tests/sav/nitmetrics_args1.res index 0c177f8..4540753 100644 --- a/tests/sav/nitmetrics_args1.res +++ b/tests/sav/nitmetrics_args1.res @@ -41,11 +41,11 @@ std: 0.0 sum: 7 mnbr: number of refinement in module - avg: 3.0 - max: base_simple3 (3) - min: base_simple3 (3) + avg: 0.0 + max: base_simple3 (0) + min: base_simple3 (0) std: 0.0 - sum: 3 + sum: 0 mnbcc: number of concrete class in module (intro + redef) avg: 4.0 max: base_simple3 (4) @@ -59,11 +59,11 @@ std: 0.0 sum: 0 mnbic: number of interface in module (intro + redef) - avg: 4.0 - max: base_simple3 (4) - min: base_simple3 (4) + avg: 1.0 + max: base_simple3 (1) + min: base_simple3 (1) std: 0.0 - sum: 4 + sum: 1 ## project base_empty_module `- group base_empty_module @@ -166,11 +166,11 @@ std: 3.0 sum: 8 mnbr: number of refinement in module - avg: 1.0 - max: base_simple3 (3) - min: base_empty_module (0) - std: 1.581 - sum: 3 + avg: 0.0 + max: base_simple3 (0) + min: base_simple3 (0) + std: 0.0 + sum: 0 mnbcc: number of concrete class in module (intro + redef) avg: 2.0 max: base_simple3 (4) @@ -184,11 +184,11 @@ std: 0.0 sum: 0 mnbic: number of interface in module (intro + redef) - avg: 2.0 - max: base_simple3 (4) + avg: 0.0 + max: base_simple3 (1) min: base_empty_module (0) - std: 2.0 - sum: 4 + std: 0.707 + sum: 1 # MClasses metrics @@ -421,48 +421,45 @@ Distribution of direct smallers <=0: sub-population=1 (33.33%); cumulated value=0 (0.0%) <=1: sub-population=2 (66.66%); cumulated value=2 (100.00%) ## Classdef hierarchy -Number of nodes: 11 -Number of edges: 47 (4.27 per node) -Number of direct edges: 9 (0.81 per node) +Number of nodes: 8 +Number of edges: 14 (1.75 per node) +Number of direct edges: 6 (0.75 per node) Distribution of greaters - population: 11 + population: 8 minimum value: 1 - maximum value: 5 - total value: 47 - average value: 4.27 + maximum value: 2 + total value: 14 + average value: 1.75 distribution: - <=1: sub-population=1 (9.09%); cumulated value=1 (2.12%) - <=4: sub-population=4 (36.36%); cumulated value=16 (34.04%) - <=8: sub-population=6 (54.54%); cumulated value=30 (63.82%) + <=1: sub-population=2 (25.00%); cumulated value=2 (14.28%) + <=2: sub-population=6 (75.00%); cumulated value=12 (85.71%) Distribution of direct greaters - population: 11 + population: 8 minimum value: 0 - maximum value: 3 - total value: 9 - average value: 0.81 + maximum value: 1 + total value: 6 + average value: 0.75 distribution: - <=0: sub-population=4 (36.36%); cumulated value=0 (0.0%) - <=1: sub-population=6 (54.54%); cumulated value=6 (66.66%) - <=4: sub-population=1 (9.09%); cumulated value=3 (33.33%) + <=0: sub-population=2 (25.00%); cumulated value=0 (0.0%) + <=1: sub-population=6 (75.00%); cumulated value=6 (100.00%) Distribution of smallers - population: 11 + population: 8 minimum value: 1 - maximum value: 10 - total value: 47 - average value: 4.27 + maximum value: 7 + total value: 14 + average value: 1.75 distribution: - <=1: sub-population=7 (63.63%); cumulated value=7 (14.89%) - <=16: sub-population=4 (36.36%); cumulated value=40 (85.10%) + <=1: sub-population=7 (87.50%); cumulated value=7 (50.00%) + <=8: sub-population=1 (12.50%); cumulated value=7 (50.00%) Distribution of direct smallers - population: 11 + population: 8 minimum value: 0 maximum value: 6 - total value: 9 - average value: 0.81 + total value: 6 + average value: 0.75 distribution: - <=0: sub-population=7 (63.63%); cumulated value=0 (0.0%) - <=1: sub-population=3 (27.27%); cumulated value=3 (33.33%) - <=8: sub-population=1 (9.09%); cumulated value=6 (66.66%) + <=0: sub-population=7 (87.50%); cumulated value=0 (0.0%) + <=8: sub-population=1 (12.50%); cumulated value=6 (100.00%) ## Class hierarchy Number of nodes: 8 Number of edges: 14 (1.75 per node) @@ -580,10 +577,10 @@ Number of classes: 8 Number of enum kind: 2 (25.00%) Number of class kind: 5 (62.50%) -Number of class definitions: 11 -Number of refined classes: 1 (12.50%) -Average number of class refinments by classes: 0.37 -Average number of class refinments by refined classes: 3.00 +Number of class definitions: 8 +Number of refined classes: 0 (0.0%) +Average number of class refinments by classes: 0.0 +Average number of class refinments by refined classes: na Number of properties: 20 Number of MAttribute: 3 (15.00%) @@ -614,7 +611,7 @@ Total number of self: 5 Total number of implicit self: 4 (80.00%) --- Construction of tables --- Number of runtime classes: 7 (excluding interfaces and abstract classes) -Average number of composing class definition by runtime class: 4.42 +Average number of composing class definition by runtime class: 1.85 Total size of tables (classes and instances): 35 (not including stuff like info for subtyping or call-next-method) Average size of table by runtime class: 5.00 Values never redefined: 35 (100.00%) @@ -632,6 +629,8 @@ Values never redefined: 35 (100.00%) blooming mclasses (threshold: 2.388) B: 3.0 C: 3.0 +generating out/nitmetrics_args1.write/project_hierarchy.dot +generating out/nitmetrics_args1.write/module_hierarchy.dot # Nullable metrics @@ -1075,8 +1074,6 @@ MMethodDef possibly invoked at runtime (by number of CallSites) base_simple3#Object#bar: 1 (4.54%) base_simple3#Object#foo: 1 (4.54%) base_simple3#C#init: 1 (4.54%) -generating out/nitmetrics_args1.write/project_hierarchy.dot -generating out/nitmetrics_args1.write/module_hierarchy.dot class_hierarchy.dot classdef_hierarchy.dot inheritance/ diff --git a/tests/sav/nitvm.res b/tests/sav/nitvm.res new file mode 100644 index 0000000..742b9e8 --- /dev/null +++ b/tests/sav/nitvm.res @@ -0,0 +1,3 @@ +Usage: nitvm [OPTION]... ... +Executes Nit programs with a virtual machine. +Use --help for help diff --git a/tests/sav/nitvm_args1.res b/tests/sav/nitvm_args1.res new file mode 100644 index 0000000..3b18e51 --- /dev/null +++ b/tests/sav/nitvm_args1.res @@ -0,0 +1 @@ +hello world diff --git a/tests/sav/nitvm_args2.res b/tests/sav/nitvm_args2.res new file mode 100644 index 0000000..f00c965 --- /dev/null +++ b/tests/sav/nitvm_args2.res @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/tests/sav/nitvm_args3.res b/tests/sav/nitvm_args3.res new file mode 100644 index 0000000..c76ebf3 --- /dev/null +++ b/tests/sav/nitvm_args3.res @@ -0,0 +1,3 @@ +MIX: Before +MIX: hello world +MIX: After diff --git a/tests/sav/nitx_args3.res b/tests/sav/nitx_args3.res index 264e277..e3174b3 100644 --- a/tests/sav/nitx_args3.res +++ b/tests/sav/nitx_args3.res @@ -25,15 +25,4 @@ class Sys base_simple3::Sys (lines 53-66) - - == refined classes - - redef interface Object - base_simple3::Object (lines 49-49) - - redef interface Object - base_simple3::Object (lines 50-50) - - redef interface Object - base_simple3::Object (lines 51-51)