X-Git-Url: http://nitlanguage.org diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index 4ea22d5..224de8b 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -28,15 +28,15 @@ import counter # Add compiling options redef class ToolContext # --output - var opt_output = new OptionString("Output file", "-o", "--output") + var opt_output = new OptionString("Filename of the generated executable", "-o", "--output") # --dir var opt_dir = new OptionString("Output directory", "--dir") # --no-cc - var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc") + var opt_no_cc = new OptionBool("Do not invoke the C compiler", "--no-cc") # --no-main var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main") # --make-flags - var opt_make_flags = new OptionString("Additional options to make", "--make-flags") + var opt_make_flags = new OptionString("Additional options to the `make` command", "--make-flags") # --max-c-lines var opt_max_c_lines = new OptionInt("Maximum number of lines in generated C files. Use 0 for unlimited", 10000, "--max-c-lines") # --group-c-files @@ -50,7 +50,7 @@ redef class ToolContext # --no-check-attr-isset var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset") # --no-check-assert - var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert") + var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit `assert` and `as` (dangerous)", "--no-check-assert") # --no-check-autocast var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast") # --no-check-null @@ -63,12 +63,14 @@ redef class ToolContext var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics") # --isset-checks-metrics var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics") - # --stacktrace - var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace") + # --no-stacktrace + var opt_no_stacktrace = new OptionBool("Disable the generation of stack traces", "--no-stacktrace") # --no-gcc-directives - var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive") + var opt_no_gcc_directive = new OptionArray("Disable advanced gcc directives for optimization", "--no-gcc-directive") # --release var opt_release = new OptionBool("Compile in release mode and finalize application", "--release") + # -g + var opt_debug = new OptionBool("Compile in debug mode (no C-side optimization)", "-g", "--debug") redef init do @@ -76,10 +78,11 @@ redef class ToolContext 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_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) self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files) + self.option_context.add_option(self.opt_debug) opt_no_main.hidden = true end @@ -88,17 +91,6 @@ redef class ToolContext do 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 "Option Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`." - exit(1) - end - if opt_output.value != null and opt_dir.value != null then print "Option Error: cannot use both --dir and --output" exit(1) @@ -115,15 +107,12 @@ redef class ToolContext end redef class ModelBuilder - # The compilation directory - var compile_dir: String - # Simple indirection to `Toolchain::write_and_make` protected fun write_and_make(compiler: AbstractCompiler) do var platform = compiler.target_platform var toolchain = platform.toolchain(toolcontext, compiler) - compile_dir = toolchain.compile_dir + compiler.toolchain = toolchain toolchain.write_and_make end end @@ -145,14 +134,21 @@ class Toolchain # Compiler of the target program var compiler: AbstractCompiler - # Directory where to generate all C files - fun compile_dir: String + # Directory where to generate all files + # + # The option `--compile_dir` change this directory. + fun root_compile_dir: String do var compile_dir = toolcontext.opt_compile_dir.value - if compile_dir == null then compile_dir = ".nit_compile" + if compile_dir == null then compile_dir = "nit_compile" return compile_dir end + # Directory where to generate all C files + # + # By default it is `root_compile_dir` but some platform may require that it is a subdirectory. + fun compile_dir: String do return root_compile_dir + # Write all C files and compile them fun write_and_make is abstract end @@ -163,14 +159,21 @@ class MakefileToolchain redef fun write_and_make do + var debug = toolcontext.opt_debug.value var compile_dir = compile_dir + # Remove the compilation directory unless explicitly set + var auto_remove = toolcontext.opt_compile_dir.value == null + # If debug flag is set, do not remove sources + if debug then auto_remove = false + # Generate the .h and .c files # A single C file regroups many compiled rumtime functions # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files var time0 = get_time self.toolcontext.info("*** WRITING C ***", 1) + root_compile_dir.mkdir compile_dir.mkdir var cfiles = new Array[String] @@ -192,6 +195,10 @@ class MakefileToolchain compile_c_code(compile_dir) + if auto_remove then + sys.system("rm -r -- '{root_compile_dir.escape_to_sh}/'") + end + time1 = get_time self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2) end @@ -219,7 +226,7 @@ class MakefileToolchain # Copy original .[ch] files to compile_dir for src in compiler.files_to_copy do - var basename = src.basename("") + var basename = src.basename var dst = "{compile_dir}/{basename}" src.file_copy_to dst end @@ -327,7 +334,7 @@ class MakefileToolchain var outpath = real_outpath.escape_to_mk if outpath != real_outpath then # If the name is crazy and need escaping, we will do an indirection - # 1. generate the binary in the .nit_compile dir under an escaped name + # 1. generate the binary in the nit_compile dir under an escaped name # 2. copy the binary at the right place in the `all` goal. outpath = mainmodule.c_name end @@ -340,28 +347,82 @@ class MakefileToolchain var libs = m.collect_linker_libs if libs != null then linker_options.add_all(libs) end - - makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n") - - var ost = toolcontext.opt_stacktrace.value - if (ost == "libunwind" or ost == "nitstack") and platform.supports_libunwind then makefile.write("NEED_LIBUNWIND := YesPlease\n") + var debug = toolcontext.opt_debug.value + + makefile.write """ +CC = ccache cc +CXX = ccache c++ +CFLAGS = -g {{{if not debug then "-O2" else ""}}} -Wno-unused-value -Wno-switch -Wno-attributes -Wno-trigraphs +CINCL = +LDFLAGS ?= +LDLIBS ?= -lm {{{linker_options.join(" ")}}} +\n""" + + makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n" + if platform.supports_libunwind then + if toolcontext.opt_no_stacktrace.value then + makefile.write "NO_STACKTRACE=True" + else + makefile.write "NO_STACKTRACE= # Set to `True` to enable" + end + end # 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 + makefile.write "\n\n" # 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") + if platform.supports_libunwind then + makefile.write """ +ifneq ($(NO_STACKTRACE), True) + # Check and include lib-unwind in a portable way + ifneq ($(uname_S),Darwin) + # already included on macosx, but need to get the correct flags in other supported platforms. + ifeq ($(shell pkg-config --exists 'libunwind'; echo $$?), 0) + LDLIBS += `pkg-config --libs libunwind` + CFLAGS += `pkg-config --cflags libunwind` + else + $(warning "[_] stack-traces disabled. Please install libunwind-dev.") + CFLAGS += -D NO_STACKTRACE + endif + endif +else + # Stacktraces disabled + CFLAGS += -D NO_STACKTRACE +endif + +""" + else + makefile.write("CFLAGS += -D NO_STACKTRACE\n\n") + end + + makefile.write """ +# Special configuration for Darwin +ifeq ($(uname_S),Darwin) + # Remove POSIX flag -lrt + LDLIBS := $(filter-out -lrt,$(LDLIBS)) +endif + +# Special configuration for Windows under mingw64 +ifeq ($(uname_S),MINGW64_NT-10.0) + # Use the pcreposix regex library + LDLIBS += -lpcreposix + + # Remove POSIX flag -lrt + LDLIBS := $(filter-out -lrt,$(LDLIBS)) + + # Silence warnings when storing Int, Char and Bool as pointer address + CFLAGS += -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast +endif + +""" makefile.write("all: {outpath}\n") if outpath != real_outpath then @@ -418,7 +479,7 @@ endif # Compile each required extern body into a specific .o for f in compiler.extern_bodies do var o = f.makefile_rule_name - var ff = f.filename.basename("") + var ff = f.filename.basename makefile.write("{o}: {ff}\n") makefile.write("\t{f.makefile_rule_content}\n\n") dep_rules.add(f.makefile_rule_name) @@ -470,6 +531,8 @@ endif var res if self.toolcontext.verbose_level >= 3 then res = sys.system("{command} 2>&1") + else if is_windows then + res = sys.system("{command} 2>&1 >nul") else res = sys.system("{command} 2>&1 >/dev/null") end @@ -496,6 +559,11 @@ abstract class AbstractCompiler # The modelbuilder used to know the model and the AST var modelbuilder: ModelBuilder is protected writable + # The associated toolchain + # + # Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required. + var toolchain: Toolchain is noinit + # Is hardening asked? (see --hardening) fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value @@ -530,7 +598,7 @@ abstract class AbstractCompiler # The list of all associated files # Used to generate .c files - var files = new List[CodeFile] + var files = new Array[CodeFile] # Initialize a visitor specific for a compiler engine fun new_visitor: VISITOR is abstract @@ -559,7 +627,7 @@ abstract class AbstractCompiler # Binds the generated C function names to Nit function names fun build_c_to_nit_bindings do - var compile_dir = modelbuilder.compile_dir + var compile_dir = toolchain.compile_dir var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c") stream.write("#include \n") @@ -604,18 +672,44 @@ abstract class AbstractCompiler self.header.add_decl("#include ") self.header.add_decl("#include ") self.header.add_decl("#include ") + # longjmp ! + self.header.add_decl("#include \n") self.header.add_decl("#include \n") self.header.add_decl("#include \n") + self.header.add_decl("#include \n") + self.header.add_decl("#ifdef __linux__") + self.header.add_decl(" #include ") + self.header.add_decl("#endif") + self.header.add_decl("#include \n") self.header.add_decl("#include \"gc_chooser.h\"") + self.header.add_decl("#ifdef __APPLE__") + self.header.add_decl(" #include ") + self.header.add_decl(" #include ") + self.header.add_decl(" #include ") + self.header.add_decl(" #define be32toh(x) OSSwapBigToHostInt32(x)") + self.header.add_decl("#endif") + self.header.add_decl("#ifdef _WIN32") + self.header.add_decl(" #define be32toh(val) _byteswap_ulong(val)") + self.header.add_decl("#endif") + self.header.add_decl("#ifdef __pnacl__") + self.header.add_decl(" #define be16toh(val) (((val) >> 8) | ((val) << 8))") + self.header.add_decl(" #define be32toh(val) ((be16toh((val) << 16) | (be16toh((val) >> 16))))") + self.header.add_decl("#endif") self.header.add_decl("#ifdef ANDROID") + self.header.add_decl(" #ifndef be32toh") + self.header.add_decl(" #define be32toh(val) betoh32(val)") + self.header.add_decl(" #endif") self.header.add_decl(" #include ") self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)") + self.header.add_decl("#elif TARGET_OS_IPHONE") + self.header.add_decl(" #define PRINT_ERROR(...) syslog(LOG_ERR, __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 + compile_catch_stack var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value if gccd_disable.has("noreturn") or gccd_disable.has("all") then @@ -644,6 +738,17 @@ abstract class AbstractCompiler self.header.add_decl("extern val *glob_sys;") end + # Stack stocking environment for longjumps + protected fun compile_catch_stack do + self.header.add_decl """ +struct catch_stack_t { + int cursor; + jmp_buf envs[100]; +}; +extern struct catch_stack_t catchStack; +""" + end + # Declaration of structures for live Nit types protected fun compile_header_structs is abstract @@ -702,6 +807,20 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); v.add "\}" end + # Hook to add specif piece of code before the the main C function. + # + # Is called by `compile_main_function` + fun compile_before_main(v: VISITOR) + do + end + + # Hook to add specif piece of code at the begin on the main C function. + # + # Is called by `compile_main_function` + fun compile_begin_main(v: VISITOR) + do + end + # Generate the main C function. # # This function: @@ -718,13 +837,16 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value if platform.supports_libunwind then + v.add_decl("#ifndef NO_STACKTRACE") v.add_decl("#define UNW_LOCAL_ONLY") v.add_decl("#include ") v.add_decl("#include \"c_functions_hash.h\"") + v.add_decl("#endif") end 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;") if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then for tag in count_type_test_tags do @@ -755,6 +877,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); v.add_decl("static void show_backtrace(void) \{") if platform.supports_libunwind then + v.add_decl("#ifndef NO_STACKTRACE") v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");") v.add_decl("unw_cursor_t cursor;") v.add_decl("if(opt==NULL)\{") @@ -778,15 +901,22 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");") v.add_decl("free(procname);") v.add_decl("\}") + v.add_decl("#endif /* NO_STACKTRACE */") end v.add_decl("\}") v.add_decl("void sig_handler(int signo)\{") + v.add_decl "#ifdef _WIN32" + v.add_decl "PRINT_ERROR(\"Caught signal : %s\\n\", signo);" + v.add_decl "#else" v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));") + v.add_decl "#endif" v.add_decl("show_backtrace();") # rethrows v.add_decl("signal(signo, SIG_DFL);") + v.add_decl "#ifndef _WIN32" v.add_decl("kill(getpid(), signo);") + v.add_decl "#endif" v.add_decl("\}") v.add_decl("void fatal_exit(int status) \{") @@ -794,21 +924,30 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); v.add_decl("exit(status);") v.add_decl("\}") + compile_before_main(v) + 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 + compile_begin_main(v) + + v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)" v.add("signal(SIGABRT, sig_handler);") v.add("signal(SIGFPE, sig_handler);") v.add("signal(SIGILL, sig_handler);") v.add("signal(SIGINT, sig_handler);") v.add("signal(SIGTERM, sig_handler);") v.add("signal(SIGSEGV, sig_handler);") - v.add("signal(SIGPIPE, sig_handler);") + v.add "#endif" + v.add "#ifndef _WIN32" + v.add("signal(SIGPIPE, SIG_IGN);") + 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();" @@ -1031,25 +1170,41 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) { fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self) end -# A file unit (may be more than one file if -# A file unit aim to be autonomous and is made or one or more `CodeWriter`s +# C code file generated from the `writers` and the `required_declarations` +# +# A file unit aims to be autonomous and is made or one or more `CodeWriter`s. class CodeFile + # Basename of the file, will be appended by `.h` and `.c` var name: String + + # `CodeWriter` used in sequence to fill the top of the body, then the bottom var writers = new Array[CodeWriter] + + # Required declarations keys + # + # See: `provide_declaration` var required_declarations = new HashSet[String] end -# Where to store generated lines +# Store generated lines +# +# Instances are added to `file.writers` at construction. class CodeWriter + # Parent `CodeFile` var file: CodeFile - var lines: List[String] = new List[String] - var decl_lines: List[String] = new List[String] - # Add a line in the main part of the generated C + # Main lines of code (written at the bottom of the body) + var lines = new Array[String] + + # Lines of code for declarations (written at the top of the body) + var decl_lines = new Array[String] + + # Add a line in the main lines of code (to `lines`) fun add(s: String) do self.lines.add(s) - # Add a line in the - # (used for local or global declaration) + # Add a declaration line (to `decl_lines`) + # + # Used for local and global declaration. fun add_decl(s: String) do self.decl_lines.add(s) init @@ -1094,6 +1249,7 @@ abstract class AbstractCompilerVisitor fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do + if callsite.is_broken then return null var initializers = callsite.mpropdef.initializers if not initializers.is_empty then var recv = arguments.first @@ -1122,9 +1278,7 @@ abstract class AbstractCompilerVisitor 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 + fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]): Bool do return false # Return an element of a native array. # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays @@ -1134,6 +1288,16 @@ abstract class AbstractCompilerVisitor # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract + # Allocate `size` bytes with the low_level `nit_alloc` C function + # + # This method can be redefined to inject statistic or tracing code. + # + # `tag` if any, is used to mark the class of the allocated object. + fun nit_alloc(size: String, tag: nullable String): String + do + return "nit_alloc({size})" + end + # Evaluate `args` as expressions in the call of `mpropdef` on `recv`. # This method is used to manage varargs in signatures and returns the real array # of runtime variables to use in the call. @@ -1168,8 +1332,8 @@ abstract class AbstractCompilerVisitor res.add(null_instance) continue end - if param.is_vararg and map.vararg_decl > 0 then - var vararg = exprs.sub(j, map.vararg_decl) + if param.is_vararg and args[i].vararg_decl > 0 then + var vararg = exprs.sub(j, args[i].vararg_decl) var elttype = param.mtype var arg = self.vararg_instance(mpropdef, recv, vararg, elttype) res.add(arg) @@ -1203,17 +1367,21 @@ abstract class AbstractCompilerVisitor do mtype = self.anchor(mtype) var valmtype = value.mcasttype + + # Do nothing if useless autocast if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then return value end + # Just as_not_null if the target is not nullable. + # + # eg `nullable PreciseType` adapted to `Object` gives precisetype. if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then - var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype) - return res - else - var res = new RuntimeVariable(value.name, valmtype, mtype) - return res + mtype = valmtype.mtype end + + var res = new RuntimeVariable(value.name, value.mtype, mtype) + return res end # Generate a super call from a method definition @@ -1281,13 +1449,18 @@ abstract class AbstractCompilerVisitor # Checks + # Can value be null? (according to current knowledge) + fun maybenull(value: RuntimeVariable): Bool + do + return value.mcasttype isa MNullableType or value.mcasttype isa MNullType + end + # Add a check and an abort for a null receiver if needed fun check_recv_notnull(recv: RuntimeVariable) do 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 + if maybenull(recv) then self.add("if (unlikely({recv} == NULL)) \{") self.add_abort("Receiver is null") self.add("\}") @@ -1339,7 +1512,7 @@ abstract class AbstractCompilerVisitor end # Return a "const char*" variable associated to the classname of the dynamic type of an object - # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program + # NOTE: we do not return a `RuntimeVariable` "CString" as the class may not exist in the module/program fun class_name_string(value: RuntimeVariable): String is abstract # Variables handling @@ -1446,12 +1619,64 @@ abstract class AbstractCompilerVisitor return res end + # Generate a byte value + fun byte_instance(value: Byte): RuntimeVariable + do + var t = mmodule.byte_type + var res = new RuntimeVariable("((unsigned char){value.to_s})", t, t) + return res + end + + # Generate an int8 value + fun int8_instance(value: Int8): RuntimeVariable + do + var t = mmodule.int8_type + var res = new RuntimeVariable("INT8_C({value.to_s})", t, t) + return res + end + + # Generate an int16 value + fun int16_instance(value: Int16): RuntimeVariable + do + var t = mmodule.int16_type + var res = new RuntimeVariable("INT16_C({value.to_s})", t, t) + return res + end + + # Generate a uint16 value + fun uint16_instance(value: UInt16): RuntimeVariable + do + var t = mmodule.uint16_type + var res = new RuntimeVariable("UINT16_C({value.to_s})", t, t) + return res + end + + # Generate an int32 value + fun int32_instance(value: Int32): RuntimeVariable + do + var t = mmodule.int32_type + var res = new RuntimeVariable("INT32_C({value.to_s})", t, t) + return res + end + + # Generate a uint32 value + fun uint32_instance(value: UInt32): RuntimeVariable + do + var t = mmodule.uint32_type + var res = new RuntimeVariable("UINT32_C({value.to_s})", t, t) + return res + end + # Generate a char value fun char_instance(value: Char): RuntimeVariable do var t = mmodule.char_type - var res = new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t) - return res + + if value.code_point < 128 then + return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t) + else + return new RuntimeVariable("{value.code_point}", t, t) + end end # Generate a float value @@ -1480,6 +1705,18 @@ abstract class AbstractCompilerVisitor return res end + # Generates a CString instance fully escaped in C-style \xHH fashion + fun c_string_instance(ns: CString, len: Int): RuntimeVariable do + var mtype = mmodule.c_string_type + var nat = new_var(mtype) + var byte_esc = new Buffer.with_cap(len * 4) + for i in [0 .. len[ do + byte_esc.append("\\x{ns[i].to_s.substring_from(2)}") + end + self.add("{nat} = \"{byte_esc}\";") + return nat + end + # Generate a string value fun string_instance(string: String): RuntimeVariable do @@ -1490,11 +1727,12 @@ abstract class AbstractCompilerVisitor self.add("if (likely({name}!=NULL)) \{") self.add("{res} = {name};") self.add("\} else \{") - var native_mtype = mmodule.native_string_type + var native_mtype = mmodule.c_string_type var nat = self.new_var(native_mtype) self.add("{nat} = \"{string.escape_to_c}\";") - var length = self.int_instance(string.length) - self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};") + var byte_length = self.int_instance(string.byte_length) + var unilen = self.int_instance(string.length) + self.add("{res} = {self.send(self.get_property("to_s_unsafe", native_mtype), [nat, byte_length, unilen, value_instance(false), value_instance(false)]).as(not null)};") self.add("{name} = {res};") self.add("\}") return res @@ -1550,16 +1788,16 @@ abstract class AbstractCompilerVisitor # This is used for the legacy FFI fun add_extern(mmodule: MModule) do - var file = mmodule.location.file.filename + var file = mmodule.filepath file = file.strip_extension(".nit") var tryfile = file + ".nit.h" if tryfile.file_exists then - self.declare_once("#include \"{tryfile.basename("")}\"") + self.declare_once("#include \"{tryfile.basename}\"") self.compiler.files_to_copy.add(tryfile) end tryfile = file + "_nit.h" if tryfile.file_exists then - self.declare_once("#include \"{tryfile.basename("")}\"") + self.declare_once("#include \"{tryfile.basename}\"") self.compiler.files_to_copy.add(tryfile) end @@ -1570,7 +1808,7 @@ abstract class AbstractCompilerVisitor tryfile = file + "_nit.c" if not tryfile.file_exists then return end - var f = new ExternCFile(tryfile.basename(""), "") + var f = new ExternCFile(tryfile.basename, "") self.compiler.extern_bodies.add(f) self.compiler.files_to_copy.add(tryfile) end @@ -1587,6 +1825,9 @@ abstract class AbstractCompilerVisitor # used by aborts, asserts, casts, etc. fun add_abort(message: String) 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 @@ -1626,10 +1867,11 @@ abstract class AbstractCompilerVisitor fun stmt(nexpr: nullable AExpr) do if nexpr == null then return - if nexpr.mtype == null and not nexpr.is_typed then + if nexpr.is_broken then # Untyped expression. - # Might mean dead code - # So just return + # Might mean dead code or invalid code + # so aborts + add_abort("FATAL: bad statement executed.") return end @@ -1651,16 +1893,27 @@ abstract class AbstractCompilerVisitor # `mtype` is the expected return type, pass null if no specific type is expected. fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable do - if nexpr.mtype == null then + var old = self.current_node + self.current_node = nexpr + + var res = null + if nexpr.mtype != null then + res = nexpr.expr(self) + end + + if res == null then # Untyped expression. - # Might mean dead code - # so return a placebo result + # Might mean dead code or invalid code. + # so aborts + add_abort("FATAL: bad expression executed.") + # and return a placebo result to please the C compiler if mtype == null then mtype = compiler.mainmodule.object_type - return new_var(mtype) + res = new_var(mtype) + + self.current_node = old + return res end - var old = self.current_node - self.current_node = nexpr - var res = nexpr.expr(self).as(not null) + if mtype != null then mtype = self.anchor(mtype) res = self.autobox(res, mtype) @@ -1826,10 +2079,22 @@ redef class MClassType else if mclass.name == "Bool" then return "short int" else if mclass.name == "Char" then - return "char" + return "uint32_t" else if mclass.name == "Float" then return "double" - else if mclass.name == "NativeString" then + else if mclass.name == "Int8" then + return "int8_t" + else if mclass.name == "Byte" then + return "unsigned char" + else if mclass.name == "Int16" then + return "int16_t" + else if mclass.name == "UInt16" then + return "uint16_t" + else if mclass.name == "Int32" then + return "int32_t" + else if mclass.name == "UInt32" then + return "uint32_t" + else if mclass.name == "CString" then return "char*" else if mclass.name == "NativeArray" then return "val*" @@ -1859,7 +2124,19 @@ redef class MClassType return "c" else if mclass.name == "Float" then return "d" - else if mclass.name == "NativeString" then + else if mclass.name == "Int8" then + return "i8" + else if mclass.name == "Byte" then + return "b" + else if mclass.name == "Int16" then + return "i16" + else if mclass.name == "UInt16" then + return "u16" + else if mclass.name == "Int32" then + return "i32" + else if mclass.name == "UInt32" then + return "u32" + else if mclass.name == "CString" then return "str" else if mclass.name == "NativeArray" then #return "{self.arguments.first.ctype}*" @@ -1879,6 +2156,7 @@ redef class MMethodDef fun can_inline(v: VISITOR): Bool do if is_abstract then return true + if constant_value != null then return true var modelbuilder = v.compiler.modelbuilder var node = modelbuilder.mpropdef2node(self) if node isa APropdef then @@ -1886,6 +2164,8 @@ redef class MMethodDef else if node isa AClassdef then # Automatic free init is always inlined since it is empty or contains only attribtes assigments return true + else if node == null then + return true else abort end @@ -1931,20 +2211,23 @@ redef class MMethodDef do if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return + var msignature = self.msignature.as(not null) + for i in [0..msignature.arity[ do + var mp = msignature.mparameters[i] # skip test for vararg since the array is instantiated with the correct polymorphic type - if msignature.vararg_rank == i then continue + if mp.is_vararg then continue # skip if the cast is not required var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype if not origmtype.need_anchor then continue # get the parameter type - var mtype = self.msignature.mparameters[i].mtype + var mtype = mp.mtype # generate the cast # note that v decides if and how to implements the cast - v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */") + v.add("/* Covariant cast for argument {i} ({mp.name}) {arguments[i+1].inspect} isa {mtype} */") v.add_cast(arguments[i+1], mtype, "covariance") end end @@ -1986,7 +2269,8 @@ redef class AMethPropdef # Try special compilation if mpropdef.is_intern then if compile_intern_to_c(v, mpropdef, arguments) then return - else if mpropdef.is_extern then + end + if mpropdef.is_extern then if mpropdef.mproperty.is_init then if compile_externinit_to_c(v, mpropdef, arguments) then return else @@ -2061,12 +2345,6 @@ redef class AMethPropdef else if pname == "%" then v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) return true - else if pname == "lshift" then - v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) - return true - else if pname == "rshift" then - v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) - return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) return true @@ -2086,18 +2364,45 @@ redef class AMethPropdef else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) return true + else if pname == "to_i8" then + v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i16" then + v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u16" then + v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i32" then + v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u32" then + v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null))) + return true else if pname == "to_f" then v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) return true - else if pname == "ascii" then - v.ret(v.new_expr("{arguments[0]}", ret.as(not null))) + else if pname == "to_b" then + v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + return true + else if pname == "code_point" then + v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "&" then + v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null))) + return true + else if pname == "|" then + v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null))) + return true + else if pname == ">>" then + v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) + return true + else if pname == "<<" then + v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) return true end else if cname == "Char" then - if pname == "output" then - v.add("printf(\"%c\", {arguments.first});") - return true - else if pname == "object_id" then + if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) return true else if pname == "successor" then @@ -2128,8 +2433,89 @@ redef class AMethPropdef else if pname == "to_i" then v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null))) return true + else if pname == "code_point" then + v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) + return true + end + else if cname == "Byte" then + if pname == "output" then + v.add("printf(\"%x\\n\", {arguments.first});") + return true + else if pname == "object_id" then + v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) + return true + else if pname == "+" then + v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) + return true + else if pname == "-" then + v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary -" then + v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) + return true + else if pname == "unary +" then + v.ret(arguments[0]) + return true + else if pname == "*" then + v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) + return true + else if pname == "/" then + v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) + return true + else if pname == "%" then + v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) + return true + else if pname == "==" then + v.ret(v.equal_test(arguments[0], arguments[1])) + return true + else if pname == "!=" then + var res = v.equal_test(arguments[0], arguments[1]) + v.ret(v.new_expr("!{res}", ret.as(not null))) + return true + else if pname == "<" then + v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) + return true + else if pname == ">" then + v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) + return true + else if pname == "<=" then + v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) + return true + else if pname == ">=" then + v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) + return true + else if pname == ">>" then + v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) + return true + else if pname == "<<" then + v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) + return true + else if pname == "&" then + v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null))) + return true + else if pname == "to_i" then + v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_f" then + v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i8" then + v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i16" then + v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u16" then + v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i32" then + v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u32" then + v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null))) + return true else if pname == "ascii" then - v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null))) return true end else if cname == "Bool" then @@ -2200,13 +2586,31 @@ redef class AMethPropdef else if pname == "to_i" then v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) return true + else if pname == "to_b" then + v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i8" then + v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i16" then + v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u16" then + v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i32" then + v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u32" then + v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null))) + return true end - else if cname == "NativeString" then + else if cname == "CString" then if pname == "[]" then - v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null))) + v.ret(v.new_expr("(unsigned char)((int){arguments[0]}[{arguments[1]}])", ret.as(not null))) return true else if pname == "[]=" then - v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};") + v.add("{arguments[0]}[{arguments[1]}]=(unsigned char){arguments[2]};") return true else if pname == "copy_to" then v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});") @@ -2217,44 +2621,486 @@ redef class AMethPropdef else if pname == "fast_cstring" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) return true + else if pname == "==" then + v.ret(v.equal_test(arguments[0], arguments[1])) + return true + else if pname == "!=" then + var res = v.equal_test(arguments[0], arguments[1]) + v.ret(v.new_expr("!{res}", ret.as(not null))) + return true else if pname == "new" then - v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) + var alloc = v.nit_alloc(arguments[1].to_s, "CString") + v.ret(v.new_expr("(char*){alloc}", ret.as(not null))) + return true + else if pname == "fetch_4_chars" then + v.ret(v.new_expr("*((uint32_t*)({arguments[0]} + {arguments[1]}))", ret.as(not null))) + return true + else if pname == "fetch_4_hchars" then + v.ret(v.new_expr("(uint32_t)be32toh(*((uint32_t*)({arguments[0]} + {arguments[1]})))", ret.as(not null))) return true end else if cname == "NativeArray" then - v.native_array_def(pname, ret, arguments) - return true - end - if pname == "exit" then - v.add("exit({arguments[1]});") - return true - else if pname == "sys" then - v.ret(v.new_expr("glob_sys", ret.as(not null))) - return true - else if pname == "calloc_string" then - v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) - return true - else if pname == "calloc_array" then - v.calloc_array(ret.as(not null), arguments) - return true - else if pname == "object_id" then - v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return true - else if pname == "is_same_type" then - v.ret(v.is_same_type_test(arguments[0], arguments[1])) - return true - else if pname == "is_same_instance" then - v.ret(v.equal_test(arguments[0], arguments[1])) - return true - else if pname == "output_class_name" then - var nat = v.class_name_string(arguments.first) - v.add("printf(\"%s\\n\", {nat});") - return true - else if pname == "native_class_name" then - var nat = v.class_name_string(arguments.first) - v.ret(v.new_expr("(char*){nat}", ret.as(not null))) - return true - else if pname == "force_garbage_collection" then + return v.native_array_def(pname, ret, arguments) + else if cname == "Int8" then + if pname == "output" then + v.add("printf(\"%\"PRIi8 \"\\n\", {arguments.first});") + return true + else if pname == "object_id" then + v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) + return true + else if pname == "+" then + v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) + return true + else if pname == "-" then + v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary -" then + v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) + return true + else if pname == "unary +" then + v.ret(arguments[0]) + return true + else if pname == "*" then + v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) + return true + else if pname == "/" then + v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) + return true + else if pname == "%" then + v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) + return true + else if pname == "<<" then + v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) + return true + else if pname == ">>" then + v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) + return true + else if pname == "==" then + v.ret(v.equal_test(arguments[0], arguments[1])) + return true + else if pname == "!=" then + var res = v.equal_test(arguments[0], arguments[1]) + v.ret(v.new_expr("!{res}", ret.as(not null))) + return true + else if pname == "<" then + v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) + return true + else if pname == ">" then + v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) + return true + else if pname == "<=" then + v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) + return true + else if pname == ">=" then + v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) + return true + else if pname == "to_i" then + v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_b" then + v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i16" then + v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u16" then + v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i32" then + v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u32" then + v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_f" then + v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) + return true + else if pname == "&" then + v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null))) + return true + else if pname == "|" then + v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null))) + return true + else if pname == "^" then + v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary ~" then + v.ret(v.new_expr("~{arguments[0]}", ret.as(not null))) + return true + end + else if cname == "Int16" then + if pname == "output" then + v.add("printf(\"%\"PRIi16 \"\\n\", {arguments.first});") + return true + else if pname == "object_id" then + v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) + return true + else if pname == "+" then + v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) + return true + else if pname == "-" then + v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary -" then + v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) + return true + else if pname == "unary +" then + v.ret(arguments[0]) + return true + else if pname == "*" then + v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) + return true + else if pname == "/" then + v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) + return true + else if pname == "%" then + v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) + return true + else if pname == "<<" then + v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) + return true + else if pname == ">>" then + v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) + return true + else if pname == "==" then + v.ret(v.equal_test(arguments[0], arguments[1])) + return true + else if pname == "!=" then + var res = v.equal_test(arguments[0], arguments[1]) + v.ret(v.new_expr("!{res}", ret.as(not null))) + return true + else if pname == "<" then + v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) + return true + else if pname == ">" then + v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) + return true + else if pname == "<=" then + v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) + return true + else if pname == ">=" then + v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) + return true + else if pname == "to_i" then + v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_b" then + v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i8" then + v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u16" then + v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i32" then + v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u32" then + v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_f" then + v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) + return true + else if pname == "&" then + v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null))) + return true + else if pname == "|" then + v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null))) + return true + else if pname == "^" then + v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary ~" then + v.ret(v.new_expr("~{arguments[0]}", ret.as(not null))) + return true + end + else if cname == "UInt16" then + if pname == "output" then + v.add("printf(\"%\"PRIu16 \"\\n\", {arguments.first});") + return true + else if pname == "object_id" then + v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) + return true + else if pname == "+" then + v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) + return true + else if pname == "-" then + v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary -" then + v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) + return true + else if pname == "unary +" then + v.ret(arguments[0]) + return true + else if pname == "*" then + v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) + return true + else if pname == "/" then + v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) + return true + else if pname == "%" then + v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) + return true + else if pname == "<<" then + v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) + return true + else if pname == ">>" then + v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) + return true + else if pname == "==" then + v.ret(v.equal_test(arguments[0], arguments[1])) + return true + else if pname == "!=" then + var res = v.equal_test(arguments[0], arguments[1]) + v.ret(v.new_expr("!{res}", ret.as(not null))) + return true + else if pname == "<" then + v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) + return true + else if pname == ">" then + v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) + return true + else if pname == "<=" then + v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) + return true + else if pname == ">=" then + v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) + return true + else if pname == "to_i" then + v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_b" then + v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i8" then + v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i16" then + v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i32" then + v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u32" then + v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_f" then + v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) + return true + else if pname == "&" then + v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null))) + return true + else if pname == "|" then + v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null))) + return true + else if pname == "^" then + v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary ~" then + v.ret(v.new_expr("~{arguments[0]}", ret.as(not null))) + return true + end + else if cname == "Int32" then + if pname == "output" then + v.add("printf(\"%\"PRIi32 \"\\n\", {arguments.first});") + return true + else if pname == "object_id" then + v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) + return true + else if pname == "+" then + v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) + return true + else if pname == "-" then + v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary -" then + v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) + return true + else if pname == "unary +" then + v.ret(arguments[0]) + return true + else if pname == "*" then + v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) + return true + else if pname == "/" then + v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) + return true + else if pname == "%" then + v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) + return true + else if pname == "<<" then + v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) + return true + else if pname == ">>" then + v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) + return true + else if pname == "==" then + v.ret(v.equal_test(arguments[0], arguments[1])) + return true + else if pname == "!=" then + var res = v.equal_test(arguments[0], arguments[1]) + v.ret(v.new_expr("!{res}", ret.as(not null))) + return true + else if pname == "<" then + v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) + return true + else if pname == ">" then + v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) + return true + else if pname == "<=" then + v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) + return true + else if pname == ">=" then + v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) + return true + else if pname == "to_i" then + v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_b" then + v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i8" then + v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i16" then + v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u16" then + v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u32" then + v.ret(v.new_expr("(uint32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_f" then + v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) + return true + else if pname == "&" then + v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null))) + return true + else if pname == "|" then + v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null))) + return true + else if pname == "^" then + v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary ~" then + v.ret(v.new_expr("~{arguments[0]}", ret.as(not null))) + return true + end + else if cname == "UInt32" then + if pname == "output" then + v.add("printf(\"%\"PRIu32 \"\\n\", {arguments.first});") + return true + else if pname == "object_id" then + v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) + return true + else if pname == "+" then + v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) + return true + else if pname == "-" then + v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary -" then + v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) + return true + else if pname == "unary +" then + v.ret(arguments[0]) + return true + else if pname == "*" then + v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) + return true + else if pname == "/" then + v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) + return true + else if pname == "%" then + v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) + return true + else if pname == "<<" then + v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) + return true + else if pname == ">>" then + v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) + return true + else if pname == "==" then + v.ret(v.equal_test(arguments[0], arguments[1])) + return true + else if pname == "!=" then + var res = v.equal_test(arguments[0], arguments[1]) + v.ret(v.new_expr("!{res}", ret.as(not null))) + return true + else if pname == "<" then + v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) + return true + else if pname == ">" then + v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) + return true + else if pname == "<=" then + v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) + return true + else if pname == ">=" then + v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) + return true + else if pname == "to_i" then + v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_b" then + v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i8" then + v.ret(v.new_expr("(int8_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i16" then + v.ret(v.new_expr("(int16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_u16" then + v.ret(v.new_expr("(uint16_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_i32" then + v.ret(v.new_expr("(int32_t){arguments[0]}", ret.as(not null))) + return true + else if pname == "to_f" then + v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) + return true + else if pname == "&" then + v.ret(v.new_expr("{arguments[0]} & {arguments[1]}", ret.as(not null))) + return true + else if pname == "|" then + v.ret(v.new_expr("{arguments[0]} | {arguments[1]}", ret.as(not null))) + return true + else if pname == "^" then + v.ret(v.new_expr("{arguments[0]} ^ {arguments[1]}", ret.as(not null))) + return true + else if pname == "unary ~" then + v.ret(v.new_expr("~{arguments[0]}", ret.as(not null))) + return true + end + end + if pname == "exit" then + v.add("exit((int){arguments[1]});") + return true + else if pname == "sys" then + v.ret(v.new_expr("glob_sys", ret.as(not null))) + return true + else if pname == "object_id" then + v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) + return true + else if pname == "is_same_type" then + v.ret(v.is_same_type_test(arguments[0], arguments[1])) + return true + else if pname == "is_same_instance" then + v.ret(v.equal_test(arguments[0], arguments[1])) + return true + else if pname == "output_class_name" then + var nat = v.class_name_string(arguments.first) + v.add("printf(\"%s\\n\", {nat});") + return true + else if pname == "native_class_name" then + var nat = v.class_name_string(arguments.first) + v.ret(v.new_expr("(char*){nat}", ret.as(not null))) + return true + else if pname == "force_garbage_collection" then v.add("nit_gcollect();") return true else if pname == "native_argc" then @@ -2337,7 +3183,7 @@ redef class AAttrPropdef var res if is_lazy then var set - var ret = self.mpropdef.static_mtype + var ret = self.mtype var useiset = not ret.is_c_primitive and not ret isa MNullableType var guard = self.mlazypropdef.mproperty if useiset then @@ -2363,9 +3209,20 @@ redef class AAttrPropdef v.assign(v.frame.returnvar.as(not null), res) else if mpropdef == mwritepropdef then assert arguments.length == 2 - v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1]) + var recv = arguments.first + var arg = arguments[1] + if is_optional and v.maybenull(arg) then + var value = v.new_var(self.mpropdef.static_mtype.as(not null)) + v.add("if ({arg} == NULL) \{") + v.assign(value, evaluate_expr(v, recv)) + v.add("\} else \{") + v.assign(value, arg) + v.add("\}") + arg = value + end + v.write_attribute(self.mpropdef.mproperty, arguments.first, arg) if is_lazy then - var ret = self.mpropdef.static_mtype + var ret = self.mtype var useiset = not ret.is_c_primitive and not ret isa MNullableType if not useiset then v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true)) @@ -2378,7 +3235,7 @@ redef class AAttrPropdef fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable) do - if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv) + if has_value and not is_lazy and not is_optional and not n_expr isa ANullExpr then evaluate_expr(v, recv) end # Evaluate, store and return the default value of the attribute @@ -2387,11 +3244,11 @@ redef class AAttrPropdef var oldnode = v.current_node v.current_node = self var old_frame = v.frame - var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv]) + var frame = new StaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv]) v.frame = frame var value - var mtype = self.mpropdef.static_mtype + var mtype = self.mtype assert mtype != null var nexpr = self.n_expr @@ -2438,8 +3295,7 @@ end redef class AClassdef private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) do - if mpropdef == self.mfree_init then - assert mpropdef.mproperty.is_root_init + if mpropdef.mproperty.is_root_init then assert arguments.length == 1 if not mpropdef.is_intro then v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) @@ -2607,8 +3463,19 @@ end redef class ADoExpr redef fun stmt(v) do - v.stmt(self.n_block) - v.add_escape_label(break_mark) + if self.n_catch != null then + v.add("catchStack.cursor += 1;") + v.add("if(!setjmp(catchStack.envs[catchStack.cursor]))\{") + v.stmt(self.n_block) + v.add("catchStack.cursor -= 1;") + v.add("\}else \{") + v.add("catchStack.cursor -= 1;") + v.stmt(self.n_catch) + v.add("\}") + else + v.stmt(self.n_block) + end + v.add_escape_label(break_mark) end end @@ -2639,53 +3506,68 @@ end redef class AForExpr redef fun stmt(v) do - var cl = v.expr(self.n_expr, null) - var it_meth = self.method_iterator - assert it_meth != null - var it = v.compile_callsite(it_meth, [cl]) - assert it != null + for g in n_groups do + var cl = v.expr(g.n_expr, null) + var it_meth = g.method_iterator + assert it_meth != null + var it = v.compile_callsite(it_meth, [cl]) + assert it != null + g.it = it + end v.add("for(;;) \{") - var isok_meth = self.method_is_ok - assert isok_meth != null - var ok = v.compile_callsite(isok_meth, [it]) - assert ok != null - v.add("if(!{ok}) break;") - if self.variables.length == 1 then - var item_meth = self.method_item - assert item_meth != null - var i = v.compile_callsite(item_meth, [it]) - assert i != null - v.assign(v.variable(variables.first), i) - else if self.variables.length == 2 then - var key_meth = self.method_key - assert key_meth != null - var i = v.compile_callsite(key_meth, [it]) - assert i != null - v.assign(v.variable(variables[0]), i) - var item_meth = self.method_item - assert item_meth != null - i = v.compile_callsite(item_meth, [it]) - assert i != null - v.assign(v.variable(variables[1]), i) - else - abort + for g in n_groups do + var it = g.it + var isok_meth = g.method_is_ok + assert isok_meth != null + var ok = v.compile_callsite(isok_meth, [it]) + assert ok != null + v.add("if(!{ok}) break;") + if g.variables.length == 1 then + var item_meth = g.method_item + assert item_meth != null + var i = v.compile_callsite(item_meth, [it]) + assert i != null + v.assign(v.variable(g.variables.first), i) + else if g.variables.length == 2 then + var key_meth = g.method_key + assert key_meth != null + var i = v.compile_callsite(key_meth, [it]) + assert i != null + v.assign(v.variable(g.variables[0]), i) + var item_meth = g.method_item + assert item_meth != null + i = v.compile_callsite(item_meth, [it]) + assert i != null + v.assign(v.variable(g.variables[1]), i) + else + abort + end end v.stmt(self.n_block) v.add_escape_label(continue_mark) - var next_meth = self.method_next - assert next_meth != null - v.compile_callsite(next_meth, [it]) + for g in n_groups do + var next_meth = g.method_next + assert next_meth != null + v.compile_callsite(next_meth, [g.it]) + end v.add("\}") v.add_escape_label(break_mark) - var method_finish = self.method_finish - if method_finish != null then - # TODO: Find a way to call this also in long escape (e.g. return) - v.compile_callsite(method_finish, [it]) + for g in n_groups do + var method_finish = g.method_finish + if method_finish != null then + # TODO: Find a way to call this also in long escape (e.g. return) + v.compile_callsite(method_finish, [g.it]) + end end end end +redef class AForGroup + # C variable representing the iterator + private var it: RuntimeVariable is noinit +end + redef class AAssertExpr redef fun stmt(v) do @@ -2762,6 +3644,9 @@ redef class AOrElseExpr do var res = v.new_var(self.mtype.as(not null)) var i1 = v.expr(self.n_expr, null) + + if not v.maybenull(i1) then return i1 + v.add("if ({i1}!=NULL) \{") v.assign(res, i1) v.add("\} else \{") @@ -2772,8 +3657,18 @@ redef class AOrElseExpr end end -redef class AIntExpr - redef fun expr(v) do return v.int_instance(self.value.as(not null)) +redef class AIntegerExpr + redef fun expr(v) do + if value isa Int then return v.int_instance(value.as(Int)) + if value isa Byte then return v.byte_instance(value.as(Byte)) + if value isa Int8 then return v.int8_instance(value.as(Int8)) + if value isa Int16 then return v.int16_instance(value.as(Int16)) + if value isa UInt16 then return v.uint16_instance(value.as(UInt16)) + if value isa Int32 then return v.int32_instance(value.as(Int32)) + if value isa UInt32 then return v.uint32_instance(value.as(UInt32)) + # Should never happen + abort + end end redef class AFloatExpr @@ -2781,7 +3676,11 @@ redef class AFloatExpr end redef class ACharExpr - redef fun expr(v) do return v.char_instance(self.value.as(not null)) + redef fun expr(v) do + if is_ascii then return v.byte_instance(value.as(not null).ascii) + if is_code_point then return v.int_instance(value.as(not null).code_point) + return v.char_instance(self.value.as(not null)) + end end redef class AArrayExpr @@ -2802,14 +3701,75 @@ redef class AArrayExpr end end +redef class AugmentedStringFormExpr + # Factorize the making of a `Regex` object from a literal prefixed string + protected fun make_re(v: AbstractCompilerVisitor, rs: RuntimeVariable): nullable RuntimeVariable do + var re = to_re + assert re != null + var res = v.compile_callsite(re, [rs]) + if res == null then + print "Cannot call property `to_re` on {self}" + abort + end + for i in suffix.chars do + if i == 'i' then + var ign = ignore_case + assert ign != null + v.compile_callsite(ign, [res, v.bool_instance(true)]) + continue + end + if i == 'm' then + var nl = newline + assert nl != null + v.compile_callsite(nl, [res, v.bool_instance(true)]) + continue + end + if i == 'b' then + var ext = extended + assert ext != null + v.compile_callsite(ext, [res, v.bool_instance(false)]) + continue + end + # Should not happen, this needs to be updated + # along with the addition of new suffixes + abort + end + return res + end +end + redef class AStringFormExpr - redef fun expr(v) do return v.string_instance(self.value.as(not null)) + redef fun expr(v) do return v.string_instance(value) +end + +redef class AStringExpr + redef fun expr(v) do + var s = v.string_instance(value) + if is_string then return s + if is_bytestring then + var ns = v.c_string_instance(bytes.items, bytes.length) + var ln = v.int_instance(bytes.length) + var cs = to_bytes_with_copy + assert cs != null + var res = v.compile_callsite(cs, [ns, ln]) + assert res != null + s = res + else if is_re then + var res = make_re(v, s) + assert res != null + s = res + else + print "Unimplemented prefix or suffix for {self}" + abort + end + return s + end end redef class ASuperstringExpr redef fun expr(v) do - var type_string = mtype.as(not null) + var type_string = v.mmodule.string_type # Collect elements of the superstring var array = new Array[AExpr] @@ -2861,12 +3821,16 @@ redef class ASuperstringExpr v.native_array_set(a, i, e) end - # Fast join the native string to get the result + # Fast join the C string to get the result var res = v.send(v.get_property("native_to_s", a.mtype), [a]) + assert res != null + + if is_re then res = make_re(v, res) # We finish to work with the native array, # so store it so that it can be reused v.add("{varonce} = {a};") + return res end end @@ -2911,7 +3875,9 @@ redef class AIsaExpr redef fun expr(v) do var i = v.expr(self.n_expr, null) - return v.type_test(i, self.cast_type.as(not null), "isa") + var cast_type = self.cast_type + if cast_type == null then return null # no-no on broken node + return v.type_test(i, cast_type, "isa") end end @@ -2932,7 +3898,7 @@ 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.is_c_primitive then return i + if not v.maybenull(i) then return i v.add("if (unlikely({i} == NULL)) \{") v.add_abort("Cast failed") @@ -2971,6 +3937,7 @@ redef class ASendExpr do var recv = v.expr(self.n_expr, null) var callsite = self.callsite.as(not null) + if callsite.is_broken then return null var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments) return v.compile_callsite(callsite, args) end @@ -2981,6 +3948,7 @@ redef class ASendReassignFormExpr do var recv = v.expr(self.n_expr, null) var callsite = self.callsite.as(not null) + if callsite.is_broken then return var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments) var value = v.expr(self.n_value, null) @@ -2999,17 +3967,19 @@ end redef class ASuperExpr redef fun expr(v) do - var recv = v.frame.arguments.first + var frame = v.frame.as(not null) + var recv = frame.arguments.first var callsite = self.callsite if callsite != null then + if callsite.is_broken then return null var args if self.n_args.n_exprs.is_empty then # Add automatic arguments for the super init call args = [recv] for i in [0..callsite.msignature.arity[ do - args.add(v.frame.arguments[i+1]) + args.add(frame.arguments[i+1]) end else args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs) @@ -3024,7 +3994,7 @@ redef class ASuperExpr var args if self.n_args.n_exprs.is_empty then - args = v.frame.arguments + args = frame.arguments else args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs) end @@ -3048,10 +4018,35 @@ redef class ANewExpr return v.native_array_instance(elttype, l) end - var recv = v.init_instance_or_extern(mtype) - var callsite = self.callsite - if callsite == null then return recv + if callsite == null then return v.init_instance_or_extern(mtype) + if callsite.is_broken then return null + + var recv + # new factories are badly implemented. + # They assume a stub temporary receiver exists. + # This temporary receiver is required because it + # currently holds the method and the formal types. + # + # However, this object could be reused if the formal types are the same. + # Therefore, the following code will `once` it in these case + if callsite.mproperty.is_new and not mtype.need_anchor then + var name = v.get_name("varoncenew") + var guard = v.get_name(name + "_guard") + v.add_decl("static {mtype.ctype} {name};") + v.add_decl("static int {guard};") + recv = v.new_var(mtype) + v.add("if (likely({guard})) \{") + v.add("{recv} = {name};") + v.add("\} else \{") + var i = v.init_instance_or_extern(mtype) + v.add("{recv} = {i};") + v.add("{name} = {recv};") + v.add("{guard} = 1;") + v.add("\}") + else + recv = v.init_instance_or_extern(mtype) + end var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs) var res2 = v.compile_callsite(callsite, args) @@ -3191,7 +4186,8 @@ end # Here we load an process all modules passed on the command line var mmodules = modelbuilder.parse(arguments) -if mmodules.is_empty then return +if mmodules.is_empty then toolcontext.quit + modelbuilder.run_phases for mmodule in mmodules do