abstract_compiler: Multi threaded and dynamic `do catch` mecanism
[nit.git] / src / compiler / abstract_compiler.nit
index 9aea519..72f1f5f 100644 (file)
@@ -31,6 +31,8 @@ redef class ToolContext
        var opt_output = new OptionString("Filename of the generated executable", "-o", "--output")
        # --dir
        var opt_dir = new OptionString("Output directory", "--dir")
+       # --run
+       var opt_run = new OptionBool("Execute the binary after the compilation", "--run")
        # --no-cc
        var opt_no_cc = new OptionBool("Do not invoke the C compiler", "--no-cc")
        # --no-main
@@ -75,7 +77,7 @@ redef class ToolContext
        redef init
        do
                super
-               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.option_context.add_option(self.opt_output, self.opt_dir, self.opt_run, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
                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_no_stacktrace)
@@ -199,6 +201,14 @@ class MakefileToolchain
                        sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'")
                end
 
+               if toolcontext.opt_run.value then
+                       var mainmodule = compiler.mainmodule
+                       var out = outfile(mainmodule)
+                       var cmd = ["."/out]
+                       cmd.append toolcontext.option_context.rest
+                       toolcontext.exec_and_check(cmd, "--run")
+               end
+
                time1 = get_time
                self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
        end
@@ -350,8 +360,12 @@ class MakefileToolchain
                var debug = toolcontext.opt_debug.value
 
                makefile.write """
-CC ?= ccache cc
-CXX ?= ccache c++
+ifeq ($(origin CC), default)
+        CC = ccache cc
+endif
+ifeq ($(origin CXX), default)
+        CXX = ccache c++
+endif
 CFLAGS ?= -g {{{if not debug then "-O2" else ""}}} -Wno-unused-value -Wno-switch -Wno-attributes -Wno-trigraphs
 CINCL =
 LDFLAGS ?=
@@ -743,9 +757,10 @@ abstract class AbstractCompiler
                self.header.add_decl """
 struct catch_stack_t {
        int cursor;
-       jmp_buf envs[100];
+       int currentSize;
+       jmp_buf *envs;
 };
-extern struct catch_stack_t catchStack;
+extern __thread struct catch_stack_t catchStack;
 """
        end
 
@@ -846,7 +861,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                v.add_decl("int glob_argc;")
                v.add_decl("char **glob_argv;")
                v.add_decl("val *glob_sys;")
-               v.add_decl("struct catch_stack_t catchStack;")
+               v.add_decl("__thread struct catch_stack_t catchStack = \{-1, 0, NULL\};")
 
                if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
                        for tag in count_type_test_tags do
@@ -947,7 +962,6 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                v.add "#endif"
 
                v.add("glob_argc = argc; glob_argv = argv;")
-               v.add("catchStack.cursor = -1;")
                v.add("initialize_gc_option();")
 
                v.add "initialize_nitni_global_refs();"
@@ -1825,16 +1839,25 @@ abstract class AbstractCompilerVisitor
        # used by aborts, asserts, casts, etc.
        fun add_abort(message: String)
        do
+               add_raw_throw
+               self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
+               add_raw_abort
+       end
+
+       # Generate a long jump if there is a catch block.
+       #
+       # This method should be called before the error messages and before a `add_raw_abort`.
+       fun add_raw_throw
+       do
                self.add("if(catchStack.cursor >= 0)\{")
                self.add("longjmp(catchStack.envs[catchStack.cursor], 1);")
                self.add("\}")
-               self.add("PRINT_ERROR(\"Runtime error: %s\", \"{message.escape_to_c}\");")
-               add_raw_abort
        end
 
        # Generate abort without a message.
        #
        # Used when one need a more complex message.
+       # Do not forget to call `add_raw_abort` before the display of a custom user message.
        fun add_raw_abort
        do
                var current_node = self.current_node
@@ -1854,6 +1877,7 @@ abstract class AbstractCompilerVisitor
        do
                var res = self.type_test(value, mtype, tag)
                self.add("if (unlikely(!{res})) \{")
+               self.add_raw_throw
                var cn = self.class_name_string(value)
                self.add("PRINT_ERROR(\"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});")
                self.add_raw_abort
@@ -2183,6 +2207,7 @@ redef class MMethodDef
                var node = modelbuilder.mpropdef2node(self)
 
                if is_abstract then
+                       v.add_raw_throw
                        var cn = v.class_name_string(arguments.first)
                        v.current_node = node
                        v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
@@ -2294,6 +2319,7 @@ redef class AMethPropdef
                end
 
                # We have a problem
+               v.add_raw_throw
                var cn = v.class_name_string(arguments.first)
                v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
                v.add_raw_abort
@@ -3468,6 +3494,14 @@ redef class ADoExpr
        redef fun stmt(v)
        do
                if self.n_catch != null then
+                       v.add("if(catchStack.currentSize == 0) \{")
+                       v.add("         catchStack.cursor = -1;")
+                       v.add("         catchStack.currentSize = 100;")
+                       v.add("         catchStack.envs = malloc(sizeof(jmp_buf)*100);")
+                       v.add("\} else if(catchStack.cursor == catchStack.currentSize - 1) \{")
+                       v.add("         catchStack.currentSize *= 2;")
+                       v.add("         catchStack.envs = realloc(catchStack.envs, sizeof(jmp_buf)*catchStack.currentSize);")
+                       v.add("\}")
                        v.add("catchStack.cursor += 1;")
                        v.add("if(!setjmp(catchStack.envs[catchStack.cursor]))\{")
                        v.stmt(self.n_block)
@@ -4182,6 +4216,10 @@ var model = new Model
 var modelbuilder = new ModelBuilder(model, toolcontext)
 
 var arguments = toolcontext.option_context.rest
+if toolcontext.opt_run.value then
+       # When --run, only the first is the program, the rest is the run arguments
+       arguments = [toolcontext.option_context.rest.shift]
+end
 if arguments.length > 1 and toolcontext.opt_output.value != null then
        print "Option Error: --output needs a single source file. Do you prefer --dir?"
        exit 1