auto_super_init: handle the case of constructors redefinition (instead of infinitivel...
[nit.git] / src / abstract_compiler.nit
index 1258dfd..979832a 100644 (file)
@@ -27,8 +27,12 @@ import c_tools
 redef class ToolContext
        # --output
        var opt_output: OptionString = new OptionString("Output file", "-o", "--output")
+       # --dir
+       var opt_dir: OptionString = new OptionString("Output directory", "--dir")
        # --no-cc
        var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
+       # --no-main
+       var opt_no_main: OptionBool = new OptionBool("Do not generate main entry point", "--no-main")
        # --cc-paths
        var opt_cc_path: OptionArray = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
        # --make-flags
@@ -41,48 +45,71 @@ redef class ToolContext
        var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
        # --no-check-covariance
        var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
-       # --no-check-initialization
-       var opt_no_check_initialization: OptionBool = new OptionBool("Disable isset tests at the end of constructors (dangerous)", "--no-check-initialization")
+       # --no-check-attr-isset
+       var opt_no_check_attr_isset: OptionBool = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
        # --no-check-assert
        var opt_no_check_assert: OptionBool = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
        # --no-check-autocast
        var opt_no_check_autocast: OptionBool = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
-       # --no-check-other
-       var opt_no_check_other: OptionBool = new OptionBool("Disable implicit tests: unset attribute, null receiver (dangerous)", "--no-check-other")
+       # --no-check-null
+       var opt_no_check_null: OptionBool = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
+       # --no-check-all
+       var opt_no_check_all: OptionBool = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
        # --typing-test-metrics
        var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
        # --invocation-metrics
        var opt_invocation_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
-       # --no-stacktrace
-       var opt_no_stacktrace: OptionBool = new OptionBool("Disables libunwind and generation of C stack traces (can be problematic when compiling to targets such as Android or NaCl)", "--no-stacktrace")
-       # --stack-trace-C-to-Nit-name-binding
-       var opt_stacktrace: OptionBool = new OptionBool("Enables the use of gperf to bind C to Nit function names when encountering a Stack trace at runtime", "--nit-stacktrace")
+       # --isset-checks-metrics
+       var opt_isset_checks_metrics: OptionBool = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
+       # --stacktrace
+       var opt_stacktrace: OptionString = new OptionString("Control the generation of stack traces", "--stacktrace")
        # --no-gcc-directives
        var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
+       # --release
+       var opt_release = new OptionBool("Compile in release mode and finalize application", "--release")
 
        redef init
        do
                super
-               self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range)
-               self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_initialization, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_other)
-               self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics)
+               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range)
+               self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
+               self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
                self.option_context.add_option(self.opt_stacktrace)
-               self.option_context.add_option(self.opt_no_stacktrace)
                self.option_context.add_option(self.opt_no_gcc_directive)
+               self.option_context.add_option(self.opt_release)
        end
-end
 
-redef class ModelBuilder
-       redef init(model, toolcontext)
+       redef fun process_options(args)
        do
-               if toolcontext.opt_no_stacktrace.value and toolcontext.opt_stacktrace.value then
-                       print "Cannot use --nit-stacktrace when --no-stacktrace is activated"
+               super
+
+               var st = opt_stacktrace.value
+               if st == "none" or st == "libunwind" or st == "nitstack" then
+                       # Fine, do nothing
+               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)
                end
 
-               super
+               if opt_output.value != null and opt_dir.value != null then
+                       print "Error: cannot use both --dir and --output"
+                       exit(1)
+               end
+
+               if opt_no_check_all.value then
+                       opt_no_check_covariance.value = true
+                       opt_no_check_attr_isset.value = true
+                       opt_no_check_assert.value = true
+                       opt_no_check_autocast.value = true
+                       opt_no_check_null.value = true
+               end
        end
+end
 
+redef class ModelBuilder
        # The compilation directory
        var compile_dir: String
 
@@ -124,22 +151,19 @@ class MakefileToolchain
        # The list is initially set with :
        #   * the toolcontext --cc-path option
        #   * the NIT_CC_PATH environment variable
-       #   * some heuristics including the NIT_DIR environment variable and the progname of the process
+       #   * `toolcontext.nit_dir`
        # Path can be added (or removed) by the client
        var cc_paths = new Array[String]
 
        protected fun gather_cc_paths
        do
                # Look for the the Nit clib path
-               var path_env = "NIT_DIR".environ
-               if not path_env.is_empty then
+               var path_env = toolcontext.nit_dir
+               if path_env != null then
                        var libname = "{path_env}/clib"
                        if libname.file_exists then cc_paths.add(libname)
                end
 
-               var libname = "{sys.program_name.dirname}/../clib"
-               if libname.file_exists then cc_paths.add(libname.simplify_path)
-
                if cc_paths.is_empty then
                        toolcontext.error(null, "Cannot determine the nit clib path. define envvar NIT_DIR.")
                end
@@ -193,10 +217,13 @@ class MakefileToolchain
 
        fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
        do
-               if self.toolcontext.opt_stacktrace.value 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 = ""
 
                # Add gc_choser.h to aditionnal bodies
-               var gc_chooser = new ExternCFile("gc_chooser.c", "-DWITH_LIBGC")
+               var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
                compiler.extern_bodies.add(gc_chooser)
                compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.c"
                compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.h"
@@ -281,18 +308,31 @@ class MakefileToolchain
                self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
        end
 
+       fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk"
+
+       fun default_outname(mainmodule: MModule): String do return mainmodule.name
+
+       # Combine options and platform informations to get the final path of the outfile
+       fun outfile(mainmodule: MModule): String
+       do
+               var res = self.toolcontext.opt_output.value
+               if res != null then return res
+               res = default_outname(mainmodule)
+               var dir = self.toolcontext.opt_dir.value
+               if dir != null then return dir.join_path(res)
+               return res
+       end
+
        fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
        do
                var mainmodule = compiler.mainmodule
+               var platform = compiler.mainmodule.target_platform
 
-               var outname = self.toolcontext.opt_output.value
-               if outname == null then
-                       outname = "{mainmodule.name}"
-               end
+               var outname = outfile(mainmodule)
 
                var orig_dir=".." # FIXME only works if `compile_dir` is a subdirectory of cwd
                var outpath = orig_dir.join_path(outname).simplify_path
-               var makename = "{mainmodule.name}.mk"
+               var makename = makefile_name(mainmodule)
                var makepath = "{compile_dir}/{makename}"
                var makefile = new OFStream.open(makepath)
 
@@ -308,9 +348,28 @@ class MakefileToolchain
                        if libs != null then linker_options.add_all(libs)
                end
 
-               if not toolcontext.opt_no_stacktrace.value then linker_options.add("-lunwind")
+               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") 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
+               # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
+
+               # Check and adapt the targeted system
+               makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
+               makefile.write("ifeq ($(uname_S),Darwin)\n")
+               # remove -lunwind since it is already included on macosx
+               makefile.write("\tNEED_LIBUNWIND :=\n")
+               makefile.write("endif\n\n")
+
+               # Check and adapt for the compiler used
+               # clang need an additionnal `-Qunused-arguments`
+               makefile.write("clang_check := $(shell sh -c '$(CC) -v 2>&1 | grep -q clang; echo $$?')\nifeq ($(clang_check), 0)\n\tCFLAGS += -Qunused-arguments\nendif\n")
+
+               makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
 
-               makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nCINCL = {cc_includes}\nLDFLAGS ?= \nLDLIBS  ?= -lm -lgc {linker_options.join(" ")}\n\n")
                makefile.write("all: {outpath}\n\n")
 
                var ofiles = new Array[String]
@@ -323,6 +382,8 @@ class MakefileToolchain
                        dep_rules.add(o)
                end
 
+               var java_files = new Array[ExternFile]
+
                # Compile each required extern body into a specific .o
                for f in compiler.extern_bodies do
                        var o = f.makefile_rule_name
@@ -330,7 +391,21 @@ class MakefileToolchain
                        makefile.write("{o}: {ff}\n")
                        makefile.write("\t{f.makefile_rule_content}\n\n")
                        dep_rules.add(f.makefile_rule_name)
-                       ofiles.add(o)
+
+                       if f.compiles_to_o_file then ofiles.add(o)
+                       if f.add_to_jar then java_files.add(f)
+               end
+               
+               if not java_files.is_empty then
+                       var jar_file = "{outpath}.jar"
+
+                       var class_files_array = new Array[String]
+                       for f in java_files do class_files_array.add(f.makefile_rule_name)
+                       var class_files = class_files_array.join(" ")
+
+                       makefile.write("{jar_file}: {class_files}\n")
+                       makefile.write("\tjar cf {jar_file} {class_files}\n\n")
+                       dep_rules.add jar_file
                end
 
                # Link edition
@@ -343,7 +418,7 @@ class MakefileToolchain
 
        fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
        do
-               var makename = "{compiler.mainmodule.name}.mk" # FIXME duplicated from write_makefile
+               var makename = makefile_name(compiler.mainmodule)
 
                var makeflags = self.toolcontext.opt_make_flags.value
                if makeflags == null then makeflags = ""
@@ -426,30 +501,40 @@ abstract class AbstractCompiler
        do
                var compile_dir = modelbuilder.compile_dir
 
-               var stream = new OFStream.open("{compile_dir}/C_fun_names")
-               stream.write("%\{\n#include \"c_functions_hash.h\"\n%\}\n")
-               stream.write("%define lookup-function-name get_nit_name\n")
-               stream.write("struct C_Nit_Names;\n")
-               stream.write("%%\n")
-               stream.write("####\n")
+               var stream = new OFStream.open("{compile_dir}/c_functions_hash.c")
+               stream.write("#include <string.h>\n")
+               stream.write("#include <stdlib.h>\n")
+               stream.write("#include \"c_functions_hash.h\"\n")
+               stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
+               stream.write("const char* get_nit_name(register const char* procproc, register unsigned int len)\{\n")
+               stream.write("char* procname = malloc(len+1);")
+               stream.write("memcpy(procname, procproc, len);")
+               stream.write("procname[len] = '\\0';")
+               stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
                for i in names.keys do
+                       stream.write("\{\"")
                        stream.write(i)
-                       stream.write(",\t\"")
+                       stream.write("\",\"")
                        stream.write(names[i])
-                       stream.write("\"\n")
-               end
-               stream.write("####\n")
-               stream.write("%%\n")
+                       stream.write("\"\},\n")
+               end
+               stream.write("\};\n")
+               stream.write("int i;")
+               stream.write("for(i = 0; i < {names.length}; i++)\{")
+               stream.write("if(strcmp(procname,map[i].name) == 0)\{")
+               stream.write("free(procname);")
+               stream.write("return map[i].nit_name;")
+               stream.write("\}")
+               stream.write("\}")
+               stream.write("free(procname);")
+               stream.write("return NULL;")
+               stream.write("\}\n")
                stream.close
 
                stream = new OFStream.open("{compile_dir}/c_functions_hash.h")
-               stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
-               stream.write("const struct C_Nit_Names* get_nit_name(register const char *str, register unsigned int len);\n")
+               stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
                stream.close
 
-               var x = new Process("gperf","{compile_dir}/C_fun_names","-t","-7","--output-file={compile_dir}/c_functions_hash.c","-C")
-               x.wait
-
                extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
        end
 
@@ -462,6 +547,12 @@ abstract class AbstractCompiler
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
                self.header.add_decl("#include \"gc_chooser.h\"")
+               self.header.add_decl("#ifdef ANDROID")
+               self.header.add_decl("  #include <android/log.h>")
+               self.header.add_decl("  #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
+               self.header.add_decl("#else")
+               self.header.add_decl("  #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
+               self.header.add_decl("#endif")
 
                compile_header_structs
                compile_nitni_structs
@@ -497,7 +588,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:
@@ -507,13 +650,20 @@ abstract class AbstractCompiler
        fun compile_main_function
        do
                var v = self.new_visitor
-               if modelbuilder.toolcontext.opt_stacktrace.value then
-                       v.add_decl("#include \"c_functions_hash.h\"")
-               end
                v.add_decl("#include <signal.h>")
-               if not modelbuilder.toolcontext.opt_no_stacktrace.value then
+               var ost = modelbuilder.toolcontext.opt_stacktrace.value
+               var platform = mainmodule.target_platform
+
+               if platform != null and not platform.supports_libunwind then ost = "none"
+
+               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")
                        v.add_decl("#include <libunwind.h>")
+                       if ost == "nitstack" then
+                               v.add_decl("#include \"c_functions_hash.h\"")
+                       end
                end
                v.add_decl("int glob_argc;")
                v.add_decl("char **glob_argv;")
@@ -539,13 +689,20 @@ abstract class AbstractCompiler
                        v.compiler.header.add_decl("extern long count_invoke_by_inline;")
                end
 
+               if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
+                       v.add_decl("long count_attr_reads = 0;")
+                       v.add_decl("long count_isset_checks = 0;")
+                       v.compiler.header.add_decl("extern long count_attr_reads;")
+                       v.compiler.header.add_decl("extern long count_isset_checks;")
+               end
+
                v.add_decl("void sig_handler(int signo)\{")
-               v.add_decl("printf(\"Caught signal : %s\\n\", strsignal(signo));")
+               v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
                v.add_decl("show_backtrace(signo);")
                v.add_decl("\}")
 
                v.add_decl("void show_backtrace (int signo) \{")
-               if not modelbuilder.toolcontext.opt_no_stacktrace.value then
+               if ost == "nitstack" or ost == "libunwind" then
                        v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
                        v.add_decl("unw_cursor_t cursor;")
                        v.add_decl("if(opt==NULL)\{")
@@ -554,30 +711,34 @@ abstract class AbstractCompiler
                        v.add_decl("char* procname = malloc(sizeof(char) * 100);")
                        v.add_decl("unw_getcontext(&uc);")
                        v.add_decl("unw_init_local(&cursor, &uc);")
-                       v.add_decl("printf(\"-------------------------------------------------\\n\");")
-                       v.add_decl("printf(\"--   Stack Trace   ------------------------------\\n\");")
-                       v.add_decl("printf(\"-------------------------------------------------\\n\");")
+                       v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
+                       v.add_decl("PRINT_ERROR(\"--   Stack Trace   ------------------------------\\n\");")
+                       v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
                        v.add_decl("while (unw_step(&cursor) > 0) \{")
                        v.add_decl("    unw_get_proc_name(&cursor, procname, 100, &ip);")
-                       if modelbuilder.toolcontext.opt_stacktrace.value then
-                       v.add_decl("    const C_Nit_Names* recv = get_nit_name(procname, strlen(procname));")
-                       v.add_decl("    if (recv != 0)\{")
-                       v.add_decl("            printf(\"` %s\\n\", recv->nit_name);")
+                       if ost == "nitstack" then
+                       v.add_decl("    const char* recv = get_nit_name(procname, strlen(procname));")
+                       v.add_decl("    if (recv != NULL)\{")
+                       v.add_decl("            PRINT_ERROR(\"` %s\\n\", recv);")
                        v.add_decl("    \}else\{")
-                       v.add_decl("            printf(\"` %s\\n\", procname);")
+                       v.add_decl("            PRINT_ERROR(\"` %s\\n\", procname);")
                        v.add_decl("    \}")
                        else
-                       v.add_decl("    printf(\"` %s \\n\",procname);")
+                       v.add_decl("    PRINT_ERROR(\"` %s \\n\",procname);")
                        end
                        v.add_decl("\}")
-                       v.add_decl("printf(\"-------------------------------------------------\\n\");")
+                       v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
                        v.add_decl("free(procname);")
                        v.add_decl("\}")
                end
                v.add_decl("exit(signo);")
                v.add_decl("\}")
 
-               v.add_decl("int main(int argc, char** argv) \{")
+               if no_main then
+                       v.add_decl("int nit_main(int argc, char** argv) \{")
+               else
+                       v.add_decl("int main(int argc, char** argv) \{")
+               end
 
                v.add("signal(SIGABRT, sig_handler);")
                v.add("signal(SIGFPE, sig_handler);")
@@ -589,6 +750,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
@@ -598,7 +762,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
@@ -638,10 +803,77 @@ abstract class AbstractCompiler
                        v.add("printf(\"direct:   %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
                        v.add("printf(\"inlined:  %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
                end
+
+               if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
+                       v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
+                       v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
+               end
+
                v.add("return 0;")
                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]
 
@@ -817,6 +1049,8 @@ abstract class AbstractCompilerVisitor
                return self.send(callsite.mproperty, args)
        end
 
+       fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
+
        fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) is abstract
 
        fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
@@ -950,7 +1184,7 @@ abstract class AbstractCompilerVisitor
        # Add a check and an abort for a null reciever if needed
        fun check_recv_notnull(recv: RuntimeVariable)
        do
-               if self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then return
+               if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
 
                var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
                if maybenull then
@@ -1054,6 +1288,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
@@ -1155,16 +1400,16 @@ abstract class AbstractCompilerVisitor
        # used by aborts, asserts, casts, etc.
        fun add_abort(message: String)
        do
-               self.add("fprintf(stderr, \"Runtime error: %s\", \"{message.escape_to_c}\");")
+               self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
                add_raw_abort
        end
 
        fun add_raw_abort
        do
                if self.current_node != null and self.current_node.location.file != null then
-                       self.add("fprintf(stderr, \" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
+                       self.add("PRINT_ERROR(\" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
                else
-                       self.add("fprintf(stderr, \"\\n\");")
+                       self.add("PRINT_ERROR(\"\\n\");")
                end
                self.add("show_backtrace(1);")
        end
@@ -1175,7 +1420,7 @@ abstract class AbstractCompilerVisitor
                var res = self.type_test(value, mtype, tag)
                self.add("if (unlikely(!{res})) \{")
                var cn = self.class_name_string(value)
-               self.add("fprintf(stderr, \"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
+               self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
                self.add_raw_abort
                self.add("\}")
        end
@@ -1496,6 +1741,7 @@ redef class MMethodDef
        # Can the body be inlined?
        fun can_inline(v: VISITOR): Bool
        do
+               if is_abstract then return true
                var modelbuilder = v.compiler.modelbuilder
                if modelbuilder.mpropdef2npropdef.has_key(self) then
                        var npropdef = modelbuilder.mpropdef2npropdef[self]
@@ -1561,33 +1807,59 @@ end
 redef class APropdef
        fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
        do
-               v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
+               v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
                debug("Not yet implemented")
        end
 
        fun can_inline: Bool do return true
 end
 
-redef class AConcreteMethPropdef
+redef class AMethPropdef
        redef fun compile_to_c(v, mpropdef, arguments)
        do
-               for i in [0..mpropdef.msignature.arity[ do
-                       var variable = self.n_signature.n_params[i].variable.as(not null)
-                       v.assign(v.variable(variable), arguments[i+1])
+               if mpropdef.is_abstract then
+                       var cn = v.class_name_string(arguments.first)
+                       v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
+                       v.add_raw_abort
+                       return
                end
+
                # Call the implicit super-init
                var auto_super_inits = self.auto_super_inits
                if auto_super_inits != null then
                        var args = [arguments.first]
                        for auto_super_init in auto_super_inits do
+                               assert auto_super_init.mproperty != mpropdef.mproperty
                                args.clear
                                for i in [0..auto_super_init.msignature.arity+1[ do
                                        args.add(arguments[i])
                                end
+                               assert auto_super_init.mproperty != mpropdef.mproperty
                                v.compile_callsite(auto_super_init, args)
                        end
                end
-               v.stmt(self.n_block)
+               if auto_super_call then
+                       v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
+               end
+
+               var n_block = n_block
+               if n_block != null then
+                       for i in [0..mpropdef.msignature.arity[ do
+                               var variable = self.n_signature.n_params[i].variable.as(not null)
+                               v.assign(v.variable(variable), arguments[i+1])
+                       end
+                       v.stmt(n_block)
+               else if mpropdef.is_intern then
+                       compile_intern_to_c(v, mpropdef, arguments)
+               else if mpropdef.is_extern then
+                       if mpropdef.mproperty.is_init then
+                               compile_externinit_to_c(v, mpropdef, arguments)
+                       else
+                               compile_externmeth_to_c(v, mpropdef, arguments)
+                       end
+               else
+                       abort
+               end
        end
 
        redef fun can_inline
@@ -1599,16 +1871,16 @@ redef class AConcreteMethPropdef
                if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true
                return false
        end
-end
 
-redef class AInternMethPropdef
-       redef fun compile_to_c(v, mpropdef, arguments)
+       fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
        do
                var pname = mpropdef.mproperty.name
                var cname = mpropdef.mclassdef.mclass.name
                var ret = mpropdef.msignature.return_mtype
                if ret != null then
                        ret = v.resolve_for(ret, arguments.first)
+               else if mpropdef.mproperty.is_new then
+                       ret = arguments.first.mcasttype
                end
                if pname != "==" and pname != "!=" then
                        v.adapt_signature(mpropdef, arguments)
@@ -1629,12 +1901,6 @@ redef class AInternMethPropdef
                        else if pname == "unary -" then
                                v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
                                return
-                       else if pname == "succ" then
-                               v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
-                               return
-                       else if pname == "prec" then
-                               v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
-                               return
                        else if pname == "*" then
                                v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
                                return
@@ -1683,10 +1949,10 @@ redef class AInternMethPropdef
                        else if pname == "object_id" then
                                v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
                                return
-                       else if pname == "+" then
+                       else if pname == "successor" then
                                v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
                                return
-                       else if pname == "-" then
+                       else if pname == "predecessor" then
                                v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
                                return
                        else if pname == "==" then
@@ -1696,12 +1962,6 @@ redef class AInternMethPropdef
                                var res = v.equal_test(arguments[0], arguments[1])
                                v.ret(v.new_expr("!{res}", ret.as(not null)))
                                return
-                       else if pname == "succ" then
-                               v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
-                               return
-                       else if pname == "prec" then
-                               v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
-                               return
                        else if pname == "<" then
                                v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
                                return
@@ -1795,11 +2055,14 @@ redef class AInternMethPropdef
                                v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
                                return
                        else if pname == "copy_to" then
-                               v.add("memcpy({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
+                               v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});")
                                return
                        else if pname == "atoi" then
                                v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
                                return
+                       else if pname == "init" then
+                               v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
+                               return
                        end
                else if cname == "NativeArray" then
                        v.native_array_def(pname, ret, arguments)
@@ -1844,18 +2107,16 @@ redef class AInternMethPropdef
                        v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null)))
                        return
                end
-               v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
+               v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
                debug("Not implemented {mpropdef}")
        end
-end
 
-redef class AExternMethPropdef
-       redef fun compile_to_c(v, mpropdef, arguments)
+       fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
        do
                var externname
                var nextern = self.n_extern
                if nextern == null then
-                       v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
+                       v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
                        v.add("show_backtrace(1);")
                        return
                end
@@ -1879,15 +2140,13 @@ redef class AExternMethPropdef
                        v.ret(res)
                end
        end
-end
 
-redef class AExternInitPropdef
-       redef fun compile_to_c(v, mpropdef, arguments)
+       fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
        do
                var externname
                var nextern = self.n_extern
                if nextern == null then
-                       v.add("printf(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
+                       v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");")
                        v.add("show_backtrace(1);")
                        return
                end
@@ -1910,18 +2169,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
@@ -1957,17 +2253,17 @@ redef class AClassdef
                if mpropdef == self.mfree_init then
                        var super_inits = self.super_inits
                        if super_inits != null then
-                               assert arguments.length == 1
+                               var args_of_super = arguments
+                               if arguments.length > 1 then args_of_super = [arguments.first]
                                for su in super_inits do
-                                       v.send(su, arguments)
+                                       v.send(su, args_of_super)
                                end
-                               return
                        end
                        var recv = arguments.first
                        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
@@ -1978,21 +2274,12 @@ redef class AClassdef
        end
 end
 
-redef class ADeferredMethPropdef
-       redef fun compile_to_c(v, mpropdef, arguments) do
-               var cn = v.class_name_string(arguments.first)
-               v.add("fprintf(stderr, \"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
-               v.add_raw_abort
-       end
-       redef fun can_inline do return true
-end
-
 redef class AExpr
        # Try to compile self as an expression
        # Do not call this method directly, use `v.expr` instead
        private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable
        do
-               v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
+               v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");")
                var mtype = self.mtype
                if mtype == null then
                        return null
@@ -2185,22 +2472,28 @@ redef class AForExpr
                # Shortcut on explicit range
                # Avoid the instantiation of the range and the iterator
                var nexpr = self.n_expr
-               if self.variables.length == 1 and nexpr isa AOrangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
+               if self.variables.length == 1 and nexpr isa ARangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
                        var from = v.expr(nexpr.n_expr, null)
                        var to = v.expr(nexpr.n_expr2, null)
                        var variable = v.variable(variables.first)
+                       var one = v.new_expr("1", v.get_class("Int").mclass_type)
 
                        v.assign(variable, from)
                        v.add("for(;;) \{ /* shortcut range */")
 
-                       var ok = v.send(v.get_property("<", variable.mtype), [variable, to])
+                       var ok
+                       if nexpr isa AOrangeExpr then
+                               ok = v.send(v.get_property("<", variable.mtype), [variable, to])
+                       else
+                               ok = v.send(v.get_property("<=", variable.mtype), [variable, to])
+                       end
                        assert ok != null
                        v.add("if(!{ok}) break;")
 
                        v.stmt(self.n_block)
 
                        v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
-                       var succ = v.send(v.get_property("succ", variable.mtype), [variable])
+                       var succ = v.send(v.get_property("successor", variable.mtype), [variable, one])
                        assert succ != null
                        v.assign(variable, succ)
                        v.add("\}")
@@ -2440,6 +2733,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("\}")
@@ -2477,7 +2772,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)
@@ -2489,7 +2784,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)
@@ -2518,7 +2813,7 @@ redef class ASuperExpr
                if callsite != null then
                        # Add additionnals arguments for the super init call
                        if args.length == 1 then
-                               for i in [0..callsite.mproperty.intro.msignature.arity[ do
+                               for i in [0..callsite.msignature.arity[ do
                                        args.add(v.frame.arguments[i+1])
                                end
                        end
@@ -2542,13 +2837,18 @@ redef class ANewExpr
                var mtype = self.mtype.as(MClassType)
                var recv
                var ctype = mtype.ctype
-               if ctype == "val*" then
+               if mtype.mclass.name == "NativeArray" then
+                       assert self.n_args.n_exprs.length == 1
+                       var l = v.expr(self.n_args.n_exprs.first, null)
+                       assert mtype isa MGenericType
+                       var elttype = mtype.arguments.first
+                       return v.native_array_instance(elttype, l)
+               else if ctype == "val*" then
                        recv = v.init_instance(mtype)
                else if ctype == "void*" then
                        recv = v.new_expr("NULL/*special!*/", mtype)
                else
-                       debug("cannot new {mtype}")
-                       abort
+                       recv = v.new_expr("({ctype})0/*special!*/", mtype)
                end
                var args = [recv]
                for a in self.n_args.n_exprs do
@@ -2635,6 +2935,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
@@ -2652,3 +2953,42 @@ redef class MModule
        # Note: can return null instead of an empty set
        fun collect_linker_libs: nullable Set[String] do return null
 end
+
+# Create a tool context to handle options and paths
+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."
+
+# 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
+# An a model builder to parse files
+var modelbuilder = new ModelBuilder(model, toolcontext)
+
+var arguments = toolcontext.option_context.rest
+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
+
+# Here we load an process all modules passed on the command line
+var mmodules = modelbuilder.parse(arguments)
+var mixins = modelbuilder.parse(opt_mixins.value)
+
+if mmodules.is_empty then return
+modelbuilder.run_phases
+
+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
+