nitg & lib: intro `Finalizable` to be called when an object is freed
[nit.git] / src / abstract_compiler.nit
index 32d8f1f..d83d574 100644 (file)
@@ -82,11 +82,11 @@ redef class ToolContext
                super
 
                var st = opt_stacktrace.value
-               if st == null or st == "none" or st == "libunwind" or st == "nitstack" then
+               if st == "none" or st == "libunwind" or st == "nitstack" then
                        # Fine, do nothing
-               else if st == "auto" then
-                       # Default just unset
-                       opt_stacktrace.value = null
+               else if st == "auto" or st == null then
+                       # Default is nitstack
+                       opt_stacktrace.value = "nitstack"
                else
                        print "Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`."
                        exit(1)
@@ -207,9 +207,8 @@ class MakefileToolchain
 
        fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
        do
-               if self.toolcontext.opt_stacktrace.value == "nitstack" then compiler.build_c_to_nit_bindings
-
                var platform = compiler.mainmodule.target_platform
+               if self.toolcontext.opt_stacktrace.value == "nitstack" and (platform == null or platform.supports_libunwind) then compiler.build_c_to_nit_bindings
                var cc_opt_with_libgc = "-DWITH_LIBGC"
                if platform != null and not platform.supports_libgc then cc_opt_with_libgc = ""
 
@@ -317,6 +316,7 @@ class MakefileToolchain
        fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
        do
                var mainmodule = compiler.mainmodule
+               var platform = compiler.mainmodule.target_platform
 
                var outname = outfile(mainmodule)
 
@@ -341,7 +341,7 @@ class MakefileToolchain
                makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch\nCINCL = {cc_includes}\nLDFLAGS ?= \nLDLIBS  ?= -lm -lgc {linker_options.join(" ")}\n\n")
 
                var ost = toolcontext.opt_stacktrace.value
-               if ost == "libunwind" or ost == "nitstack" then makefile.write("NEED_LIBUNWIND := YesPlease\n")
+               if (ost == "libunwind" or ost == "nitstack") and (platform == null or platform.supports_libunwind) then makefile.write("NEED_LIBUNWIND := YesPlease\n")
 
                # Dynamic adaptations
                # While `platform` enable complex toolchains, they are statically applied
@@ -578,7 +578,59 @@ abstract class AbstractCompiler
        protected fun compile_header_structs is abstract
 
        # Declaration of structures for nitni undelying the FFI
-       protected fun compile_nitni_structs is abstract
+       protected fun compile_nitni_structs
+       do
+               self.header.add_decl """
+/* Native reference to Nit objects */
+/* This structure is used to represent every Nit type in extern methods and custom C code. */
+struct nitni_ref {
+       struct nitni_ref *next,
+               *prev; /* adjacent global references in global list */
+       int count; /* number of time this global reference has been marked */
+};
+
+/* List of global references from C code to Nit objects */
+/* Instanciated empty at init of Nit system and filled explicitly by user in C code */
+struct nitni_global_ref_list_t {
+       struct nitni_ref *head, *tail;
+};
+extern struct nitni_global_ref_list_t *nitni_global_ref_list;
+
+/* Initializer of global reference list */
+extern void nitni_global_ref_list_init();
+
+/* Intern function to add a global reference to the list */
+extern void nitni_global_ref_add( struct nitni_ref *ref );
+
+/* Intern function to remove a global reference from the list */
+extern void nitni_global_ref_remove( struct nitni_ref *ref );
+
+/* Increase count on an existing global reference */
+extern void nitni_global_ref_incr( struct nitni_ref *ref );
+
+/* Decrease count on an existing global reference */
+extern void nitni_global_ref_decr( struct nitni_ref *ref );
+"""
+       end
+
+       fun compile_finalizer_function
+       do
+               var finalizable_type = mainmodule.finalizable_type
+               if finalizable_type == null then return
+
+               var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
+
+               if finalize_meth == null then
+                       modelbuilder.toolcontext.error(null, "The `Finalizable` class doesn't declare the `finalize` method.")
+                       return
+               end
+
+               var v = self.new_visitor
+               v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
+               var recv = v.new_expr("obj", finalizable_type)
+               v.send(finalize_meth, [recv])
+               v.add "\}"
+       end
 
        # Generate the main C function.
        # This function:
@@ -592,16 +644,9 @@ abstract class AbstractCompiler
                var ost = modelbuilder.toolcontext.opt_stacktrace.value
                var platform = mainmodule.target_platform
 
-               if ost == null then
-                       if platform != null and not platform.supports_libunwind then
-                               ost = "none"
-                       else
-                               ost = "nitstack"
-                       end
-                       modelbuilder.toolcontext.opt_stacktrace.value = ost
-               end
+               if platform != null and not platform.supports_libunwind then ost = "none"
 
-               if platform != null and platform.no_main then modelbuilder.toolcontext.opt_no_main.value = true
+               var no_main = (platform != null and platform.no_main) or modelbuilder.toolcontext.opt_no_main.value
 
                if ost == "nitstack" or ost == "libunwind" then
                        v.add_decl("#define UNW_LOCAL_ONLY")
@@ -679,7 +724,7 @@ abstract class AbstractCompiler
                v.add_decl("exit(signo);")
                v.add_decl("\}")
 
-               if modelbuilder.toolcontext.opt_no_main.value then
+               if no_main then
                        v.add_decl("int nit_main(int argc, char** argv) \{")
                else
                        v.add_decl("int main(int argc, char** argv) \{")
@@ -695,6 +740,9 @@ abstract class AbstractCompiler
 
                v.add("glob_argc = argc; glob_argv = argv;")
                v.add("initialize_gc_option();")
+
+               v.add "initialize_nitni_global_refs();"
+
                var main_type = mainmodule.sys_type
                if main_type != null then
                        var mainmodule = v.compiler.mainmodule
@@ -704,7 +752,8 @@ abstract class AbstractCompiler
                        if main_init != null then
                                v.send(main_init, [glob_sys])
                        end
-                       var main_method = mainmodule.try_get_primitive_method("main", main_type.mclass)
+                       var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
+                               mainmodule.try_get_primitive_method("main", main_type.mclass)
                        if main_method != null then
                                v.send(main_method, [glob_sys])
                        end
@@ -754,6 +803,67 @@ abstract class AbstractCompiler
                v.add("\}")
        end
 
+       # Copile all C functions related to the [incr|decr]_ref features of the FFI
+       fun compile_nitni_global_ref_functions
+       do
+               var v = self.new_visitor
+               v.add """
+struct nitni_global_ref_list_t *nitni_global_ref_list;
+void initialize_nitni_global_refs() {
+       nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
+       nitni_global_ref_list->head = NULL;
+       nitni_global_ref_list->tail = NULL;
+}
+
+void nitni_global_ref_add( struct nitni_ref *ref ) {
+       if ( nitni_global_ref_list->head == NULL ) {
+               nitni_global_ref_list->head = ref;
+               ref->prev = NULL;
+       } else {
+               nitni_global_ref_list->tail->next = ref;
+               ref->prev = nitni_global_ref_list->tail;
+       }
+       nitni_global_ref_list->tail = ref;
+
+       ref->next = NULL;
+}
+
+void nitni_global_ref_remove( struct nitni_ref *ref ) {
+       if ( ref->prev == NULL ) {
+               nitni_global_ref_list->head = ref->next;
+       } else {
+               ref->prev->next = ref->next;
+       }
+
+       if ( ref->next == NULL ) {
+               nitni_global_ref_list->tail = ref->prev;
+       } else {
+               ref->next->prev = ref->prev;
+       }
+}
+
+extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
+       if ( ref->count == 0 ) /* not registered */
+       {
+               /* add to list */
+               nitni_global_ref_add( ref );
+       }
+
+       ref->count ++;
+}
+
+extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
+       if ( ref->count == 1 ) /* was last reference */
+       {
+               /* remove from list */
+               nitni_global_ref_remove( ref );
+       }
+
+       ref->count --;
+}
+"""
+       end
+
        # List of additional files required to compile (FFI)
        var extern_bodies = new Array[ExternFile]
 
@@ -1168,6 +1278,17 @@ abstract class AbstractCompilerVisitor
        # Generate a alloc-instance + init-attributes
        fun init_instance(mtype: MClassType): RuntimeVariable is abstract
 
+       # Set a GC finalizer on `recv`, only if `recv` isa Finalizable
+       fun set_finalizer(recv: RuntimeVariable)
+       do
+               var mtype = recv.mtype
+               var finalizable_type = compiler.mainmodule.finalizable_type
+               if finalizable_type != null and not mtype.need_anchor and
+                  mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
+                       add "gc_register_finalizer({recv});"
+               end
+       end
+
        # Generate an integer value
        fun int_instance(value: Int): RuntimeVariable
        do
@@ -2031,18 +2152,55 @@ end
 redef class AAttrPropdef
        redef fun compile_to_c(v, mpropdef, arguments)
        do
-               if arguments.length == 1 then
-                       var res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
+               if mpropdef == mreadpropdef then
+                       assert arguments.length == 1
+                       var res
+                       if is_lazy then
+                               var nexpr = n_expr
+                               assert nexpr != null
+                               var set
+                               var ret = self.mpropdef.static_mtype
+                               var useiset = ret.ctype == "val*" and not ret isa MNullableType
+                               var guard = self.mlazypropdef.mproperty
+                               if useiset then
+                                       set = v.isset_attribute(self.mpropdef.mproperty, arguments.first)
+                               else
+                                       set = v.read_attribute(guard, arguments.first)
+                               end
+                               v.add("if(likely({set})) \{")
+                               res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
+                               v.add("\} else \{")
+                               var value = v.expr(nexpr, self.mpropdef.static_mtype)
+                               v.write_attribute(self.mpropdef.mproperty, arguments.first, value)
+                               v.assign(res, value)
+                               if not useiset then
+                                       var true_v = v.new_expr("1", v.bool_type)
+                                       v.write_attribute(guard, arguments.first, true_v)
+                               end
+                               v.add("\}")
+                       else
+                               res = v.read_attribute(self.mpropdef.mproperty, arguments.first)
+                       end
                        v.assign(v.frame.returnvar.as(not null), res)
-               else
+               else if mpropdef == mwritepropdef then
+                       assert arguments.length == 2
                        v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
+                       if is_lazy then
+                               var ret = self.mpropdef.static_mtype
+                               var useiset = ret.ctype == "val*" and not ret isa MNullableType
+                               if not useiset then
+                                       v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
+                               end
+                       end
+               else
+                       abort
                end
        end
 
        fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
        do
                var nexpr = self.n_expr
-               if nexpr != null then
+               if nexpr != null and not is_lazy then
                        var oldnode = v.current_node
                        v.current_node = self
                        var old_frame = v.frame
@@ -2088,7 +2246,7 @@ redef class AClassdef
                        var i = 1
                        # Collect undefined attributes
                        for npropdef in self.n_propdefs do
-                               if npropdef isa AAttrPropdef and npropdef.n_expr == null then
+                               if npropdef isa AAttrPropdef and npropdef.n_expr == null and not npropdef.noinit then
                                        v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i])
                                        i += 1
                                end
@@ -2552,6 +2710,8 @@ redef class AAsNotnullExpr
                var i = v.expr(self.n_expr, null)
                if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
 
+               if i.mtype.ctype != "val*" then return i
+
                v.add("if (unlikely({i} == NULL)) \{")
                v.add_abort("Cast failed")
                v.add("\}")
@@ -2589,7 +2749,7 @@ redef class ASendExpr
        do
                var recv = v.expr(self.n_expr, null)
                var args = [recv]
-               for a in self.raw_arguments.as(not null) do
+               for a in self.raw_arguments do
                        args.add(v.expr(a, null))
                end
                return v.compile_callsite(self.callsite.as(not null), args)
@@ -2601,7 +2761,7 @@ redef class ASendReassignFormExpr
        do
                var recv = v.expr(self.n_expr, null)
                var args = [recv]
-               for a in self.raw_arguments.as(not null) do
+               for a in self.raw_arguments do
                        args.add(v.expr(a, null))
                end
                var value = v.expr(self.n_value, null)
@@ -2752,6 +2912,7 @@ redef class MModule
                                properties.add_all(self.properties(parent))
                        end
                        for mclassdef in mclass.mclassdefs do
+                               if not self.in_importation <= mclassdef.mmodule then continue
                                for mprop in mclassdef.intro_mproperties do
                                        properties.add(mprop)
                                end
@@ -2776,7 +2937,7 @@ var toolcontext = new ToolContext
 var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
 toolcontext.option_context.add_option(opt_mixins)
 
-toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit\nCompiles Nit programs."
+toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
 
 # We do not add other options, so process them now!
 toolcontext.process_options(args)
@@ -2787,26 +2948,24 @@ var model = new Model
 var modelbuilder = new ModelBuilder(model, toolcontext)
 
 var arguments = toolcontext.option_context.rest
-if arguments.length > 1 then
-       print "Too much arguments: {arguments.join(" ")}"
-       print toolcontext.tooldescription
+if arguments.length > 1 and toolcontext.opt_output.value != null then
+       print "Error: --output needs a single source file. Do you prefer --dir?"
        exit 1
 end
-var progname = arguments.first
 
 # Here we load an process all modules passed on the command line
-var mmodules = modelbuilder.parse([progname])
-mmodules.add_all modelbuilder.parse(opt_mixins.value)
+var mmodules = modelbuilder.parse(arguments)
+var mixins = modelbuilder.parse(opt_mixins.value)
 
 if mmodules.is_empty then return
 modelbuilder.run_phases
 
-var mainmodule
-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)
+for mmodule in mmodules do
+       toolcontext.info("*** PROCESS {mmodule} ***", 1)
+       var ms = [mmodule]
+       if not mixins.is_empty then
+               ms.add_all mixins
+       end
+       toolcontext.run_global_phases(ms)
 end
 
-toolcontext.run_global_phases(mmodules)