X-Git-Url: http://nitlanguage.org diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index 89254d4..1b51d22 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -21,47 +21,49 @@ import literal import semantize import platform import c_tools +private import annotation +import mixin # Add compiling options redef class ToolContext # --output - var opt_output: OptionString = new OptionString("Output file", "-o", "--output") + var opt_output = new OptionString("Output file", "-o", "--output") # --dir - var opt_dir: OptionString = new OptionString("Output directory", "--dir") + var opt_dir = new OptionString("Output directory", "--dir") # --no-cc - var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc") + var opt_no_cc = 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") + var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main") # --make-flags - var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags") + var opt_make_flags = new OptionString("Additional options to make", "--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 + var opt_group_c_files = new OptionBool("Group all generated code in the same series of files", "--group-c-files") # --compile-dir - var opt_compile_dir: OptionString = new OptionString("Directory used to generate temporary files", "--compile-dir") + var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir") # --hardening - var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening") - # --no-shortcut-range - var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range") + var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening") # --no-check-covariance - var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance") + var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance") # --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") + 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: OptionBool = 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: OptionBool = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast") + var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast") # --no-check-null - var opt_no_check_null: OptionBool = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null") + var opt_no_check_null = 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") + var opt_no_check_all = 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") + var opt_typing_test_metrics = 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") + 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: OptionBool = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--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: OptionString = new OptionString("Control the generation of stack traces", "--stacktrace") + var opt_stacktrace = 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 @@ -70,12 +72,15 @@ 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.opt_no_shortcut_range) + 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_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) + + opt_no_main.hidden = true end redef fun process_options(args) @@ -115,20 +120,16 @@ redef class ModelBuilder # Simple indirection to `Toolchain::write_and_make` protected fun write_and_make(compiler: AbstractCompiler) do - var platform = compiler.mainmodule.target_platform - var toolchain - if platform == null then - toolchain = new MakefileToolchain(toolcontext) - else - toolchain = platform.toolchain(toolcontext) - end + var platform = compiler.target_platform + var toolchain = platform.toolchain(toolcontext) compile_dir = toolchain.compile_dir toolchain.write_and_make compiler end end redef class Platform - fun toolchain(toolcontext: ToolContext): Toolchain is abstract + # The specific tool-chain associated to the platform + fun toolchain(toolcontext: ToolContext): Toolchain do return new MakefileToolchain(toolcontext) end class Toolchain @@ -146,41 +147,9 @@ end class MakefileToolchain super Toolchain - # The list of directories to search for included C headers (-I for C compilers) - # The list is initially set with : - # * the toolcontext --cc-path option - # * the NIT_CC_PATH environment variable - # * `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 = toolcontext.nit_dir - if path_env != null then - var libname = "{path_env}/clib" - if libname.file_exists then cc_paths.add(libname) - end - - if cc_paths.is_empty then - toolcontext.error(null, "Cannot determine the nit clib path. define envvar NIT_DIR.") - end - - # Add user defined cc_paths - cc_paths.append(toolcontext.opt_cc_path.value) - - path_env = "NIT_CC_PATH".environ - if not path_env.is_empty then - cc_paths.append(path_env.split_with(':')) - end - end redef fun write_and_make(compiler) do - gather_cc_paths - - var mainmodule = compiler.mainmodule var compile_dir = compile_dir # Generate the .h and .c files @@ -216,19 +185,20 @@ class MakefileToolchain fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String]) do - 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 platform = compiler.target_platform + if self.toolcontext.opt_stacktrace.value == "nitstack" and 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 = "" + if not platform.supports_libgc then cc_opt_with_libgc = "" # Add gc_choser.h to aditionnal bodies var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc) + if cc_opt_with_libgc != "" then gc_chooser.pkgconfigs.add "bdw-gc" 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" + var clib = toolcontext.nit_dir / "clib" + compiler.files_to_copy.add "{clib}/gc_chooser.c" + compiler.files_to_copy.add "{clib}/gc_chooser.h" # FFI - var m2m = toolcontext.modelbuilder.mmodule2nmodule for m in compiler.mainmodule.in_importation.greaters do compiler.finalize_ffi_for_module(m) end @@ -242,7 +212,7 @@ class MakefileToolchain var hfilename = compiler.header.file.name + ".h" var hfilepath = "{compile_dir}/{hfilename}" - var h = new OFStream.open(hfilepath) + var h = new FileWriter.open(hfilepath) for l in compiler.header.decl_lines do h.write l h.write "\n" @@ -253,42 +223,24 @@ class MakefileToolchain end h.close + var max_c_lines = toolcontext.opt_max_c_lines.value for f in compiler.files do var i = 0 - var hfile: nullable OFStream = null var count = 0 - var cfilename = "{f.name}.0.h" - var cfilepath = "{compile_dir}/{cfilename}" - hfile = new OFStream.open(cfilepath) - hfile.write "#include \"{hfilename}\"\n" - for key in f.required_declarations do - if not compiler.provided_declarations.has_key(key) then - var node = compiler.requirers_of_declarations.get_or_null(key) - if node != null then - node.debug "No provided declaration for {key}" - else - print "No provided declaration for {key}" - end - abort - end - hfile.write compiler.provided_declarations[key] - hfile.write "\n" - end - hfile.close - var file: nullable OFStream = null + var file: nullable FileWriter = null for vis in f.writers do if vis == compiler.header then continue var total_lines = vis.lines.length + vis.decl_lines.length if total_lines == 0 then continue count += total_lines - if file == null or count > 10000 then + if file == null or (count > max_c_lines and max_c_lines > 0) then i += 1 if file != null then file.close - cfilename = "{f.name}.{i}.c" - cfilepath = "{compile_dir}/{cfilename}" + var cfilename = "{f.name}.{i}.c" + var cfilepath = "{compile_dir}/{cfilename}" self.toolcontext.info("new C source files to compile: {cfilepath}", 3) cfiles.add(cfilename) - file = new OFStream.open(cfilepath) + file = new FileWriter.open(cfilepath) file.write "#include \"{f.name}.0.h\"\n" count = total_lines end @@ -301,15 +253,45 @@ class MakefileToolchain file.write "\n" end end - if file != null then file.close + if file == null then continue + file.close + + var cfilename = "{f.name}.0.h" + var cfilepath = "{compile_dir}/{cfilename}" + var hfile: nullable FileWriter = null + hfile = new FileWriter.open(cfilepath) + hfile.write "#include \"{hfilename}\"\n" + for key in f.required_declarations do + if not compiler.provided_declarations.has_key(key) then + var node = compiler.requirers_of_declarations.get_or_null(key) + if node != null then + node.debug "No provided declaration for {key}" + else + print "No provided declaration for {key}" + end + abort + end + hfile.write compiler.provided_declarations[key] + hfile.write "\n" + end + hfile.close end 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 makefile_name(mainmodule: MModule): String do return "{mainmodule.c_name}.mk" - fun default_outname(mainmodule: MModule): String do return mainmodule.name + fun default_outname(mainmodule: MModule): String + do + # Search a non fictive module + var res = mainmodule.name + while mainmodule.is_fictive do + mainmodule = mainmodule.in_importation.direct_greaters.first + res = mainmodule.name + end + return res + end # Combine options and platform informations to get the final path of the outfile fun outfile(mainmodule: MModule): String @@ -325,32 +307,32 @@ class MakefileToolchain fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String]) do var mainmodule = compiler.mainmodule - var platform = compiler.mainmodule.target_platform + var platform = compiler.target_platform 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 real_outpath = compile_dir.relpath(outname) + 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 + # 2. copy the binary at the right place in the `all` goal. + outpath = mainmodule.c_name + end var makename = makefile_name(mainmodule) var makepath = "{compile_dir}/{makename}" - var makefile = new OFStream.open(makepath) - - var cc_includes = "" - for p in cc_paths do - cc_includes += " -I \"" + p + "\"" - end + var makefile = new FileWriter.open(makepath) var linker_options = new HashSet[String] - var m2m = toolcontext.modelbuilder.mmodule2nmodule for m in mainmodule.in_importation.greaters do 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\nCINCL = {cc_includes}\nLDFLAGS ?= \nLDLIBS ?= -lm -lgc {linker_options.join(" ")}\n\n") + 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 == null or platform.supports_libunwind) then makefile.write("NEED_LIBUNWIND := YesPlease\n") + if (ost == "libunwind" or ost == "nitstack") and platform.supports_libunwind then makefile.write("NEED_LIBUNWIND := YesPlease\n") # Dynamic adaptations # While `platform` enable complex toolchains, they are statically applied @@ -369,7 +351,11 @@ class MakefileToolchain makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n") - makefile.write("all: {outpath}\n\n") + makefile.write("all: {outpath}\n") + if outpath != real_outpath then + makefile.write("\tcp -- {outpath.escape_to_sh} {real_outpath.escape_to_sh.replace("$","$$")}") + end + makefile.write("\n") var ofiles = new Array[String] var dep_rules = new Array[String] @@ -381,8 +367,42 @@ class MakefileToolchain dep_rules.add(o) end + # Generate linker script, if any + if not compiler.linker_script.is_empty then + var linker_script_path = "{compile_dir}/linker_script" + ofiles.add "linker_script" + var f = new FileWriter.open(linker_script_path) + for l in compiler.linker_script do + f.write l + f.write "\n" + end + f.close + end + var java_files = new Array[ExternFile] + var pkgconfigs = new Array[String] + for f in compiler.extern_bodies do + pkgconfigs.add_all f.pkgconfigs + end + # Protect pkg-config + if not pkgconfigs.is_empty then + makefile.write """ +# does pkg-config exists? +ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0) +$(error "Command `pkg-config` not found. Please install it") +endif +""" + for p in pkgconfigs do + makefile.write """ +# Check for library {{{p}}} +ifneq ($(shell pkg-config --exists '{{{p}}}'; echo $$?), 0) +$(error "pkg-config: package {{{p}}} is not found.") +endif +""" + end + end + # Compile each required extern body into a specific .o for f in compiler.extern_bodies do var o = f.makefile_rule_name @@ -408,11 +428,20 @@ class MakefileToolchain end # Link edition - makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS)\n\n") + var pkg = "" + if not pkgconfigs.is_empty then + pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`" + end + makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n") # Clean - makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n") + makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n") + if outpath != real_outpath then + makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n") + end makefile.close self.toolcontext.info("Generated makefile: {makepath}", 2) + + makepath.file_copy_to "{compile_dir}/Makefile" end fun compile_c_code(compiler: AbstractCompiler, compile_dir: String) @@ -447,25 +476,38 @@ abstract class AbstractCompiler var mainmodule: MModule is writable # The real main module of the program - var realmainmodule: MModule + var realmainmodule: MModule is noinit - # The modeulbuilder used to know the model and the AST + # The modelbuilder used to know the model and the AST var modelbuilder: ModelBuilder is protected writable # Is hardening asked? (see --hardening) fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value - init(mainmodule: MModule, modelbuilder: ModelBuilder) + # The targeted specific platform + var target_platform: Platform is noinit + + init do - self.mainmodule = mainmodule self.realmainmodule = mainmodule - self.modelbuilder = modelbuilder + target_platform = mainmodule.target_platform or else new Platform end + # Do the full code generation of the program `mainmodule` + # It is the main method usually called after the instantiation + fun do_compilation is abstract + # Force the creation of a new file # The point is to avoid contamination between must-be-compiled-separately files fun new_file(name: String): CodeFile do + if modelbuilder.toolcontext.opt_group_c_files.value then + if self.files.is_empty then + var f = new CodeFile(mainmodule.c_name) + self.files.add(f) + end + return self.files.first + end var f = new CodeFile(name) self.files.add(f) return f @@ -473,13 +515,17 @@ abstract class AbstractCompiler # The list of all associated files # Used to generate .c files - var files: List[CodeFile] = new List[CodeFile] + var files = new List[CodeFile] # Initialize a visitor specific for a compiler engine fun new_visitor: VISITOR is abstract # Where global declaration are stored (the main .h) - var header: CodeWriter is writable + var header: CodeWriter is writable, noinit + + # Additionnal linker script for `ld`. + # Mainly used to do specific link-time symbol resolution + var linker_script = new Array[String] # Provide a declaration that can be requested (before or latter) by a visitor fun provide_declaration(key: String, s: String) @@ -500,7 +546,7 @@ abstract class AbstractCompiler do var compile_dir = modelbuilder.compile_dir - var stream = new OFStream.open("{compile_dir}/c_functions_hash.c") + var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c") stream.write("#include \n") stream.write("#include \n") stream.write("#include \"c_functions_hash.h\"\n") @@ -512,9 +558,9 @@ abstract class AbstractCompiler stream.write("static const C_Nit_Names map[{names.length}] = \{\n") for i in names.keys do stream.write("\{\"") - stream.write(i) + stream.write(i.escape_to_c) stream.write("\",\"") - stream.write(names[i]) + stream.write(names[i].escape_to_c) stream.write("\"\},\n") end stream.write("\};\n") @@ -530,7 +576,7 @@ abstract class AbstractCompiler stream.write("\}\n") stream.close - stream = new OFStream.open("{compile_dir}/c_functions_hash.h") + stream = new FileWriter.open("{compile_dir}/c_functions_hash.h") stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n") stream.close @@ -540,11 +586,11 @@ abstract class AbstractCompiler # Compile C headers # This method call compile_header_strucs method that has to be refined fun compile_header do - var v = self.header - var toolctx = modelbuilder.toolcontext self.header.add_decl("#include ") self.header.add_decl("#include ") self.header.add_decl("#include ") + self.header.add_decl("#include \n") + self.header.add_decl("#include \n") self.header.add_decl("#include \"gc_chooser.h\"") self.header.add_decl("#ifdef ANDROID") self.header.add_decl(" #include ") @@ -559,9 +605,9 @@ abstract class AbstractCompiler var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value if gccd_disable.has("noreturn") or gccd_disable.has("all") then # Signal handler function prototype - self.header.add_decl("void show_backtrace(int);") + self.header.add_decl("void fatal_exit(int);") else - self.header.add_decl("void show_backtrace(int) __attribute__ ((noreturn));") + self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));") end if gccd_disable.has("likely") or gccd_disable.has("all") then @@ -653,11 +699,11 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); var v = self.new_visitor v.add_decl("#include ") var ost = modelbuilder.toolcontext.opt_stacktrace.value - var platform = mainmodule.target_platform + var platform = target_platform - if platform != null and not platform.supports_libunwind then ost = "none" + if not platform.supports_libunwind then ost = "none" - var no_main = (platform != null and platform.no_main) or modelbuilder.toolcontext.opt_no_main.value + var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value if ost == "nitstack" or ost == "libunwind" then v.add_decl("#define UNW_LOCAL_ONLY") @@ -697,12 +743,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); v.compiler.header.add_decl("extern long count_isset_checks;") end - v.add_decl("void sig_handler(int 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) \{") + v.add_decl("static void show_backtrace(void) \{") if ost == "nitstack" or ost == "libunwind" then v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");") v.add_decl("unw_cursor_t cursor;") @@ -732,7 +773,19 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); v.add_decl("free(procname);") v.add_decl("\}") end - v.add_decl("exit(signo);") + v.add_decl("\}") + + v.add_decl("void sig_handler(int signo)\{") + v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));") + v.add_decl("show_backtrace();") + # rethrows + v.add_decl("signal(signo, SIG_DFL);") + v.add_decl("kill(getpid(), signo);") + v.add_decl("\}") + + v.add_decl("void fatal_exit(int status) \{") + v.add_decl("show_backtrace();") + v.add_decl("exit(status);") v.add_decl("\}") if no_main then @@ -812,6 +865,12 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); v.add("return 0;") v.add("\}") + + for m in mainmodule.in_importation.greaters do + var f = "FILE_"+m.c_name + v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";" + provide_declaration(f, "extern const char {f}[];") + end end # Copile all C functions related to the [incr|decr]_ref features of the FFI @@ -890,11 +949,8 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) { var cds = mtype.collect_mclassdefs(self.mainmodule).to_a self.mainmodule.linearize_mclassdefs(cds) for cd in cds do - var n = self.modelbuilder.mclassdef2nclassdef[cd] - for npropdef in n.n_propdefs do - if npropdef isa AAttrPropdef then - npropdef.init_expr(v, recv) - end + for npropdef in modelbuilder.collect_attr_propdef(cd) do + npropdef.init_expr(v, recv) end end end @@ -905,11 +961,8 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) { var cds = mtype.collect_mclassdefs(self.mainmodule).to_a self.mainmodule.linearize_mclassdefs(cds) for cd in cds do - var n = self.modelbuilder.mclassdef2nclassdef[cd] - for npropdef in n.n_propdefs do - if npropdef isa AAttrPropdef then - npropdef.check_expr(v, recv) - end + for npropdef in modelbuilder.collect_attr_propdef(cd) do + npropdef.check_expr(v, recv) end end end @@ -1001,9 +1054,8 @@ class CodeWriter # (used for local or global declaration) fun add_decl(s: String) do self.decl_lines.add(s) - init(file: CodeFile) + init do - self.file = file file.writers.add(self) end end @@ -1019,8 +1071,8 @@ abstract class AbstractCompilerVisitor # The current visited AST node var current_node: nullable ANode = null is writable - # The current `Frame` - var frame: nullable Frame is writable + # The current `StaticFrame` + var frame: nullable StaticFrame = null is writable # Alias for self.compiler.mainmodule.object_type fun object_type: MClassType do return self.compiler.mainmodule.object_type @@ -1028,22 +1080,18 @@ abstract class AbstractCompilerVisitor # Alias for self.compiler.mainmodule.bool_type fun bool_type: MClassType do return self.compiler.mainmodule.bool_type - var writer: CodeWriter + var writer: CodeWriter is noinit - init(compiler: COMPILER) + init do - self.compiler = compiler self.writer = new CodeWriter(compiler.files.last) end - # Force to get the primitive class named `name` or abort - fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name) - # Force to get the primitive property named `name` in the instance `recv` or abort fun get_property(name: String, recv: MType): MMethod do assert recv isa MClassType - return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule) + return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule) end fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable @@ -1052,16 +1100,21 @@ abstract class AbstractCompilerVisitor if not initializers.is_empty then var recv = arguments.first - assert initializers.length == arguments.length - 1 else debug("expected {initializers.length}, got {arguments.length - 1}") var i = 1 for p in initializers do if p isa MMethod then - self.send(p, [recv, arguments[i]]) + var args = [recv] + for x in p.intro.msignature.mparameters do + args.add arguments[i] + i += 1 + end + self.send(p, args) else if p isa MAttribute then self.write_attribute(p, recv, arguments[i]) + i += 1 else abort - i += 1 end + assert i == arguments.length return self.send(callsite.mproperty, [recv]) end @@ -1075,39 +1128,45 @@ abstract class AbstractCompilerVisitor fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract - # Transform varargs, in raw arguments, into a single argument of type `Array` - # Note: this method modify the given `args` - # If there is no vararg, then `args` is not modified. - fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable]) + # 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. + fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable] do - var recv = args.first - var vararg_rank = msignature.vararg_rank - if vararg_rank >= 0 then - assert args.length >= msignature.arity + 1 # because of self - var rawargs = args - args = new Array[RuntimeVariable] + var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) + var res = new Array[RuntimeVariable] + res.add(recv) - args.add(rawargs.first) # recv + if args.is_empty then return res - for i in [0..vararg_rank[ do - args.add(rawargs[i+1]) - end - - var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity - var vararg = new Array[RuntimeVariable] - for i in [vararg_rank..vararg_lastrank] do - vararg.add(rawargs[i+1]) - end - - var elttype = msignature.mparameters[vararg_rank].mtype - args.add(self.vararg_instance(mpropdef, recv, vararg, elttype)) + var vararg_rank = msignature.vararg_rank + var vararg_len = args.length - msignature.arity + if vararg_len < 0 then vararg_len = 0 - for i in [vararg_lastrank+1..rawargs.length-1[ do - args.add(rawargs[i+1]) + for i in [0..msignature.arity[ do + if i == vararg_rank then + var ne = args[i] + if ne isa AVarargExpr then + var e = self.expr(ne.n_expr, null) + res.add(e) + continue + end + var vararg = new Array[RuntimeVariable] + for j in [vararg_rank..vararg_rank+vararg_len] do + var e = self.expr(args[j], null) + vararg.add(e) + end + var elttype = msignature.mparameters[vararg_rank].mtype + var arg = self.vararg_instance(mpropdef, recv, vararg, elttype) + res.add(arg) + else + var j = i + if i > vararg_rank then j += vararg_len + var e = self.expr(args[j], null) + res.add(e) end - rawargs.clear - rawargs.add_all(args) end + return res end # Type handling @@ -1149,12 +1208,22 @@ abstract class AbstractCompilerVisitor # Generate a super call from a method definition fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract + # Adapt the arguments of a method according to targetted `MMethodDef` fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract + # Unbox all the arguments of a method when implemented `extern` or `intern` + fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract + # Box or unbox a value to another type iff a C type conversion is needed # ENSURE: `result.mtype.ctype == mtype.ctype` fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract + # Box extern classes to be used in the generated code + fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract + + # Unbox extern classes to be used in extern code (legacy NI and FFI) + fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract + # Generate a polymorphic subtype test fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract @@ -1201,7 +1270,7 @@ abstract class AbstractCompilerVisitor # Checks - # Add a check and an abort for a null reciever if needed + # 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 @@ -1216,7 +1285,7 @@ abstract class AbstractCompilerVisitor # Names handling - private var names: HashSet[String] = new HashSet[String] + private var names = new HashSet[String] private var last: Int = 0 # Return a new name based on `s` and unique in the visitor @@ -1242,15 +1311,21 @@ abstract class AbstractCompilerVisitor fun escapemark_name(e: nullable EscapeMark): String do assert e != null - if escapemark_names.has_key(e) then return escapemark_names[e] + if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e] var name = e.name if name == null then name = "label" name = get_name(name) - escapemark_names[e] = name + frame.escapemark_names[e] = name return name end - private var escapemark_names = new HashMap[EscapeMark, String] + # Insert a C label for associated with an escapemark + fun add_escape_label(e: nullable EscapeMark) + do + if e == null then return + if e.escapes.is_empty then return + add("BREAK_{escapemark_name(e)}: (void)0;") + 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 @@ -1258,7 +1333,7 @@ abstract class AbstractCompilerVisitor # Variables handling - protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable] + protected var variables = new HashMap[Variable, RuntimeVariable] # Return the local runtime_variable associated to a Nit local variable fun variable(variable: Variable): RuntimeVariable @@ -1286,6 +1361,16 @@ abstract class AbstractCompilerVisitor return res end + # The difference with `new_var` is the C static type of the local variable + fun new_var_extern(mtype: MType): RuntimeVariable + do + mtype = self.anchor(mtype) + var name = self.get_name("var") + var res = new RuntimeVariable(name, mtype, mtype) + self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;") + return res + end + # Return a new uninitialized named runtime_variable fun new_named_var(mtype: MType, name: String): RuntimeVariable do @@ -1308,6 +1393,24 @@ abstract class AbstractCompilerVisitor # Generate a alloc-instance + init-attributes fun init_instance(mtype: MClassType): RuntimeVariable is abstract + # Allocate and init attributes of an instance of a standard or extern class + # + # Does not support universals and the pseudo-internal `NativeArray` class. + fun init_instance_or_extern(mtype: MClassType): RuntimeVariable + do + var recv + var ctype = mtype.ctype + assert mtype.mclass.name != "NativeArray" + if not mtype.is_c_primitive then + recv = init_instance(mtype) + else if ctype == "char*" then + recv = new_expr("NULL/*special!*/", mtype) + else + recv = new_expr("({ctype})0/*special!*/", mtype) + end + return recv + end + # Set a GC finalizer on `recv`, only if `recv` isa Finalizable fun set_finalizer(recv: RuntimeVariable) do @@ -1319,25 +1422,64 @@ abstract class AbstractCompilerVisitor end end + # The currently processed module + # + # alias for `compiler.mainmodule` + fun mmodule: MModule do return compiler.mainmodule + # Generate an integer value fun int_instance(value: Int): RuntimeVariable do - var res = self.new_var(self.get_class("Int").mclass_type) - self.add("{res} = {value};") + var t = mmodule.int_type + var res = new RuntimeVariable("{value.to_s}l", 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 + end + + # Generate a float value + # + # FIXME pass a Float, not a string + fun float_instance(value: String): RuntimeVariable + do + var t = mmodule.float_type + var res = new RuntimeVariable("{value}", t, t) + return res + end + + # Generate an integer value + fun bool_instance(value: Bool): RuntimeVariable + do + var s = if value then "1" else "0" + var res = new RuntimeVariable(s, bool_type, bool_type) + return res + end + + # Generate the `null` value + fun null_instance: RuntimeVariable + do + var t = compiler.mainmodule.model.null_type + var res = new RuntimeVariable("((val*)NULL)", t, t) return res end # Generate a string value fun string_instance(string: String): RuntimeVariable do - var mtype = self.get_class("String").mclass_type + var mtype = mmodule.string_type var name = self.get_name("varonce") self.add_decl("static {mtype.ctype} {name};") var res = self.new_var(mtype) - self.add("if ({name}) \{") + self.add("if (likely({name}!=NULL)) \{") self.add("{res} = {name};") self.add("\} else \{") - var native_mtype = self.get_class("NativeString").mclass_type + var native_mtype = mmodule.native_string_type var nat = self.new_var(native_mtype) self.add("{nat} = \"{string.escape_to_c}\";") var length = self.int_instance(string.length) @@ -1347,6 +1489,19 @@ abstract class AbstractCompilerVisitor return res end + fun value_instance(object: Object): RuntimeVariable + do + if object isa Int then + return int_instance(object) + else if object isa Bool then + return bool_instance(object) + else if object isa String then + return string_instance(object) + else + abort + end + end + # Generate an array value fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract @@ -1380,10 +1535,11 @@ abstract class AbstractCompilerVisitor self.require_declaration(s) end - # look for a needed .h and .c file for a given .nit source-file - # FIXME: bad API, parameter should be a `MModule`, not its source-file - fun add_extern(file: String) + # Look for a needed .h and .c file for a given module + # This is used for the legacy FFI + fun add_extern(mmodule: MModule) do + var file = mmodule.location.file.filename file = file.strip_extension(".nit") var tryfile = file + ".nit.h" if tryfile.file_exists then @@ -1426,12 +1582,15 @@ abstract class AbstractCompilerVisitor fun add_raw_abort do - if self.current_node != null and self.current_node.location.file != null then - self.add("PRINT_ERROR(\" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});") + if self.current_node != null and self.current_node.location.file != null and + self.current_node.location.file.mmodule != null then + var f = "FILE_{self.current_node.location.file.mmodule.c_name}" + self.require_declaration(f) + self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});") else self.add("PRINT_ERROR(\"\\n\");") end - self.add("show_backtrace(1);") + self.add("fatal_exit(1);") end # Add a dynamic cast @@ -1456,6 +1615,15 @@ abstract class AbstractCompilerVisitor fun stmt(nexpr: nullable AExpr) do if nexpr == null then return + + var narray = nexpr.comprehension + if narray != null then + var recv = frame.comprehension.as(not null) + var val = expr(nexpr, narray.element_mtype) + compile_callsite(narray.push_callsite.as(not null), [recv, val]) + return + end + var old = self.current_node self.current_node = nexpr nexpr.stmt(self) @@ -1552,11 +1720,8 @@ class RuntimeVariable # false (usual value) means that the variable is a mcasttype or a subtype. var is_exact: Bool = false is writable - init(name: String, mtype: MType, mcasttype: MType) + init do - self.name = name - self.mtype = mtype - self.mcasttype = mcasttype assert not mtype.need_anchor assert not mcasttype.need_anchor end @@ -1581,8 +1746,8 @@ class RuntimeVariable end end -# A frame correspond to a visited property in a `GlobalCompilerVisitor` -class Frame +# The static context of a visited property in a `AbstractCompilerVisitor` +class StaticFrame type VISITOR: AbstractCompilerVisitor @@ -1604,31 +1769,34 @@ class Frame # The label at the end of the property var returnlabel: nullable String = null is writable + + # Labels associated to a each escapemarks. + # Because of inlinings, escape-marks must be associated to their context (the frame) + private var escapemark_names = new HashMap[EscapeMark, String] + + # The array comprehension currently filled, if any + private var comprehension: nullable RuntimeVariable = null end redef class MType # Return the C type associated to a given Nit static type fun ctype: String do return "val*" + # C type outside of the compiler code and in boxes + fun ctype_extern: String do return "val*" + + # Short name of the `ctype` to use in unions fun ctypename: String do return "val" - # Return the name of the C structure associated to a Nit live type - fun c_name: String is abstract - protected var c_name_cache: nullable String is protected writable + # Is the associated C type a primitive one? + # + # ENSURE `result == (ctype != "val*")` + fun is_c_primitive: Bool do return false end redef class MClassType - redef fun c_name - do - var res = self.c_name_cache - if res != null then return res - res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}" - self.c_name_cache = res - return res - end - redef fun ctype: String - do + redef var ctype is lazy do if mclass.name == "Int" then return "long" else if mclass.name == "Bool" then @@ -1641,13 +1809,22 @@ redef class MClassType return "char*" else if mclass.name == "NativeArray" then return "val*" - else if mclass.kind == extern_kind then - return "void*" else return "val*" end end + redef var is_c_primitive is lazy do return ctype != "val*" + + redef fun ctype_extern: String + do + if mclass.kind == extern_kind then + return "void*" + else + return ctype + end + end + redef fun ctypename: String do if mclass.name == "Int" then @@ -1663,98 +1840,14 @@ redef class MClassType else if mclass.name == "NativeArray" then #return "{self.arguments.first.ctype}*" return "val" - else if mclass.kind == extern_kind then - return "ptr" else return "val" end end end -redef class MGenericType - redef fun c_name - do - var res = self.c_name_cache - if res != null then return res - res = super - for t in self.arguments do - res = res + t.c_name - end - self.c_name_cache = res - return res - end -end - -redef class MParameterType - redef fun c_name - do - var res = self.c_name_cache - if res != null then return res - res = "{self.mclass.c_name}_FT{self.rank}" - self.c_name_cache = res - return res - end -end - -redef class MVirtualType - redef fun c_name - do - var res = self.c_name_cache - if res != null then return res - res = "{self.mproperty.intro.mclassdef.mclass.c_name}_VT{self.mproperty.name}" - self.c_name_cache = res - return res - end -end - -redef class MNullableType - redef fun c_name - do - var res = self.c_name_cache - if res != null then return res - res = "nullable_{self.mtype.c_name}" - self.c_name_cache = res - return res - end -end - -redef class MClass - # Return the name of the C structure associated to a Nit class - fun c_name: String do - var res = self.c_name_cache - if res != null then return res - res = "{intro_mmodule.name.to_cmangle}__{name.to_cmangle}" - self.c_name_cache = res - return res - end - private var c_name_cache: nullable String -end - -redef class MProperty - fun c_name: String do - var res = self.c_name_cache - if res != null then return res - res = "{self.intro.c_name}" - self.c_name_cache = res - return res - end - private var c_name_cache: nullable String -end - redef class MPropDef type VISITOR: AbstractCompilerVisitor - - private var c_name_cache: nullable String - - # The mangled name associated to the property - fun c_name: String - do - var res = self.c_name_cache - if res != null then return res - res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}" - self.c_name_cache = res - return res - end end redef class MMethodDef @@ -1763,10 +1856,10 @@ redef class MMethodDef do if is_abstract then return true var modelbuilder = v.compiler.modelbuilder - if modelbuilder.mpropdef2npropdef.has_key(self) then - var npropdef = modelbuilder.mpropdef2npropdef[self] - return npropdef.can_inline - else if self.mproperty.name == "init" then + var node = modelbuilder.mpropdef2node(self) + if node isa APropdef then + return node.can_inline + else if node isa AClassdef then # Automatic free init is always inlined since it is empty or contains only attribtes assigments return true else @@ -1778,20 +1871,31 @@ redef class MMethodDef fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do var modelbuilder = v.compiler.modelbuilder - if modelbuilder.mpropdef2npropdef.has_key(self) then - var npropdef = modelbuilder.mpropdef2npropdef[self] + var val = constant_value + var node = modelbuilder.mpropdef2node(self) + + if is_abstract then + 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});") + v.add_raw_abort + return null + end + + if node isa APropdef then var oldnode = v.current_node - v.current_node = npropdef + v.current_node = node self.compile_parameter_check(v, arguments) - npropdef.compile_to_c(v, self, arguments) + node.compile_to_c(v, self, arguments) v.current_node = oldnode - else if self.mproperty.name == "init" then - var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef] + else if node isa AClassdef then var oldnode = v.current_node - v.current_node = nclassdef + v.current_node = node self.compile_parameter_check(v, arguments) - nclassdef.compile_to_c(v, self, arguments) + node.compile_to_c(v, self, arguments) v.current_node = oldnode + else if val != null then + v.ret(v.value_instance(val)) else abort end @@ -1837,13 +1941,6 @@ end redef class AMethPropdef redef fun compile_to_c(v, mpropdef, arguments) do - 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 @@ -1862,6 +1959,18 @@ redef class AMethPropdef v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) end + # Try special compilation + if mpropdef.is_intern then + if compile_intern_to_c(v, mpropdef, arguments) then return + else if mpropdef.is_extern then + if mpropdef.mproperty.is_init then + if compile_externinit_to_c(v, mpropdef, arguments) then return + else + if compile_externmeth_to_c(v, mpropdef, arguments) then return + end + end + + # Compile block if any var n_block = n_block if n_block != null then for i in [0..mpropdef.msignature.arity[ do @@ -1869,17 +1978,13 @@ redef class AMethPropdef 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 + return end + + # We have a problem + 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 end redef fun can_inline @@ -1892,326 +1997,330 @@ redef class AMethPropdef return false end - fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool 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) + v.unbox_signature_extern(mpropdef, arguments) end if cname == "Int" then if pname == "output" then v.add("printf(\"%ld\\n\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(arguments.first) - return + return true else if pname == "+" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "-" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "unary -" then v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) - return + return true else if pname == "*" then v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) - return + return true else if pname == "/" then v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) - return + return true else if pname == "%" then v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) - return + return true else if pname == "lshift" then v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) - return + return true else if pname == "rshift" then v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + 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 + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_f" then v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) - return + return true else if pname == "ascii" then v.ret(v.new_expr("{arguments[0]}", ret.as(not null))) - return + return true end else if cname == "Char" then if pname == "output" then v.add("printf(\"%c\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "successor" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "predecessor" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + 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 + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_i" then v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null))) - return + return true else if pname == "ascii" then v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) - return + return true end else if cname == "Bool" then if pname == "output" then v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + 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 + return true end else if cname == "Float" then if pname == "output" then v.add("printf(\"%f\\n\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(double){arguments.first}", ret.as(not null))) - return + return true else if pname == "+" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "-" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "unary -" then v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) - return + return true else if pname == "succ" then v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null))) - return + return true else if pname == "prec" then v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null))) - return + return true else if pname == "*" then v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) - return + return true else if pname == "/" then v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + 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 + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_i" then v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) - return + return true end else if cname == "NativeString" then if pname == "[]" then v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null))) - return + return true else if pname == "[]=" then v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};") - return + return true else if pname == "copy_to" then v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});") - return + return true else if pname == "atoi" then v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null))) - return - else if pname == "init" then + return true + else if pname == "new" then v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) - return + return true end else if cname == "NativeArray" then v.native_array_def(pname, ret, arguments) - return + return true end if pname == "exit" then v.add("exit({arguments[1]});") - return + return true else if pname == "sys" then v.ret(v.new_expr("glob_sys", ret.as(not null))) - return + return true else if pname == "calloc_string" then v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) - return + return true else if pname == "calloc_array" then v.calloc_array(ret.as(not null), arguments) - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "is_same_type" then v.ret(v.is_same_type_test(arguments[0], arguments[1])) - return + return true else if pname == "is_same_instance" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "output_class_name" then var nat = v.class_name_string(arguments.first) v.add("printf(\"%s\\n\", {nat});") - return + 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 + return true else if pname == "force_garbage_collection" then v.add("nit_gcollect();") - return + return true else if pname == "native_argc" then v.ret(v.new_expr("glob_argc", ret.as(not null))) - return + return true else if pname == "native_argv" then v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null))) - return + return true end - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");") - debug("Not implemented {mpropdef}") + return false end - fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + # Compile an extern method + # Return `true` if the compilation was successful, `false` if a fall-back is needed + fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var externname - var nextern = self.n_extern - if nextern == null then - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");") - v.add("show_backtrace(1);") - return - end - externname = nextern.text.substring(1, nextern.text.length-2) - if location.file != null then - var file = location.file.filename - v.add_extern(file) + var at = self.get_single_annotation("extern", v.compiler.modelbuilder) + if at != null and at.n_args.length == 1 then + externname = at.arg_as_string(v.compiler.modelbuilder) + if externname == null then return false + else + return false end + v.add_extern(mpropdef.mclassdef.mmodule) var res: nullable RuntimeVariable = null var ret = mpropdef.msignature.return_mtype if ret != null then ret = v.resolve_for(ret, arguments.first) - res = v.new_var(ret) + res = v.new_var_extern(ret) end v.adapt_signature(mpropdef, arguments) + v.unbox_signature_extern(mpropdef, arguments) if res == null then v.add("{externname}({arguments.join(", ")});") else v.add("{res} = {externname}({arguments.join(", ")});") + res = v.box_extern(res, ret.as(not null)) v.ret(res) end + return true end - fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + # Compile an extern factory + # Return `true` if the compilation was successful, `false` if a fall-back is needed + fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var externname - var nextern = self.n_extern - if nextern == null then - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");") - v.add("show_backtrace(1);") - return - end - externname = nextern.text.substring(1, nextern.text.length-2) - if location.file != null then - var file = location.file.filename - v.add_extern(file) + var at = self.get_single_annotation("extern", v.compiler.modelbuilder) + if at != null then + externname = at.arg_as_string(v.compiler.modelbuilder) + if externname == null then return false + else + return false end + v.add_extern(mpropdef.mclassdef.mmodule) v.adapt_signature(mpropdef, arguments) + v.unbox_signature_extern(mpropdef, arguments) var ret = arguments.first.mtype - var res = v.new_var(ret) + var res = v.new_var_extern(ret) arguments.shift v.add("{res} = {externname}({arguments.join(", ")});") + res = v.box_extern(res, ret) v.ret(res) + return true end end redef class AAttrPropdef + redef fun can_inline: Bool do return not is_lazy + redef fun compile_to_c(v, mpropdef, arguments) do if mpropdef == mreadpropdef then assert arguments.length == 1 + var recv = arguments.first 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 useiset = not ret.is_c_primitive and not ret isa MNullableType var guard = self.mlazypropdef.mproperty if useiset then - set = v.isset_attribute(self.mpropdef.mproperty, arguments.first) + set = v.isset_attribute(self.mpropdef.mproperty, recv) else - set = v.read_attribute(guard, arguments.first) + set = v.read_attribute(guard, recv) end v.add("if(likely({set})) \{") - res = v.read_attribute(self.mpropdef.mproperty, arguments.first) + res = v.read_attribute(self.mpropdef.mproperty, recv) v.add("\} else \{") - var value = v.expr(nexpr, self.mpropdef.static_mtype) - v.write_attribute(self.mpropdef.mproperty, arguments.first, value) + + var value = evaluate_expr(v, recv) + v.assign(res, value) if not useiset then - var true_v = v.new_expr("1", v.bool_type) + var true_v = v.bool_instance(true) v.write_attribute(guard, arguments.first, true_v) end v.add("\}") @@ -2224,9 +2333,9 @@ redef class AAttrPropdef 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 + 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.new_expr("1", v.bool_type)) + v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true)) end end else @@ -2236,18 +2345,44 @@ 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) + end + + # Evaluate, store and return the default value of the attribute + private fun evaluate_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable): RuntimeVariable + do + 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.as_notnullable.as(MClassType), [recv]) + v.frame = frame + + var value + var mtype = self.mpropdef.static_mtype + assert mtype != null + var nexpr = self.n_expr - if nexpr != null and not is_lazy then - var oldnode = v.current_node - v.current_node = self - var old_frame = v.frame - var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv]) - v.frame = frame - var value = v.expr(nexpr, self.mpropdef.static_mtype) - v.write_attribute(self.mpropdef.mproperty, recv, value) - v.frame = old_frame - v.current_node = oldnode + var nblock = self.n_block + if nexpr != null then + value = v.expr(nexpr, mtype) + else if nblock != null then + value = v.new_var(mtype) + frame.returnvar = value + frame.returnlabel = v.get_name("RET_LABEL") + v.add("\{") + v.stmt(nblock) + v.add("{frame.returnlabel.as(not null)}:(void)0;") + v.add("\}") + else + abort end + + v.write_attribute(self.mpropdef.mproperty, recv, value) + + v.frame = old_frame + v.current_node = oldnode + + return value end fun check_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable) @@ -2258,7 +2393,7 @@ redef class AAttrPropdef var oldnode = v.current_node v.current_node = self var old_frame = v.frame - var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv]) + var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv]) v.frame = frame # Force read to check the initialization v.read_attribute(self.mpropdef.mproperty, recv) @@ -2271,33 +2406,12 @@ redef class AClassdef private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) do if mpropdef == self.mfree_init then - if mpropdef.mproperty.is_root_init then - assert self.super_inits == null - assert arguments.length == 1 - if not mpropdef.is_intro then - v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) - end - return - end - - var super_inits = self.super_inits - if super_inits != null then - var args_of_super = arguments - if arguments.length > 1 then args_of_super = [arguments.first] - for su in super_inits do - v.send(su, args_of_super) - end - 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 and not npropdef.noinit then - v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i]) - i += 1 - end + assert mpropdef.mproperty.is_root_init + assert arguments.length == 1 + if not mpropdef.is_intro then + v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) end + return else abort end @@ -2324,8 +2438,7 @@ redef class AExpr # Do not call this method directly, use `v.stmt` instead private fun stmt(v: AbstractCompilerVisitor) do - var res = expr(v) - if res != null then v.add("{res};") + expr(v) end end @@ -2367,12 +2480,6 @@ redef class AVarExpr end redef class AVarAssignExpr - redef fun stmt(v) - do - var variable = self.variable.as(not null) - var i = v.expr(self.n_value, variable.declared_type) - v.assign(v.variable(variable), i) - end redef fun expr(v) do var variable = self.variable.as(not null) @@ -2398,11 +2505,7 @@ redef class ASelfExpr redef fun expr(v) do return v.frame.arguments.first end -redef class AContinueExpr - redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};") -end - -redef class ABreakExpr +redef class AEscapeExpr redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};") end @@ -2465,10 +2568,7 @@ redef class ADoExpr redef fun stmt(v) do v.stmt(self.n_block) - var escapemark = self.escapemark - if escapemark != null then - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") - end + v.add_escape_label(break_mark) end end @@ -2479,9 +2579,9 @@ redef class AWhileExpr var cond = v.expr_bool(self.n_expr) v.add("if (!{cond}) break;") v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(continue_mark) v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(break_mark) end end @@ -2490,47 +2590,15 @@ redef class ALoopExpr do v.add("for(;;) \{") v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(continue_mark) v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(break_mark) end end redef class AForExpr redef fun stmt(v) do - # 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 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 - 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("successor", variable.mtype), [variable, one]) - assert succ != null - v.assign(variable, succ) - v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") - return - end - var cl = v.expr(self.n_expr, null) var it_meth = self.method_iterator assert it_meth != null @@ -2563,12 +2631,18 @@ redef class AForExpr abort end v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(continue_mark) var next_meth = self.method_next assert next_meth != null v.compile_callsite(next_meth, [it]) v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") + 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]) + end end end @@ -2659,27 +2733,32 @@ redef class AOrElseExpr end redef class AIntExpr - redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null)) + redef fun expr(v) do return v.int_instance(self.value.as(not null)) end redef class AFloatExpr - redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float + redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float end redef class ACharExpr - redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null)) + redef fun expr(v) do return v.char_instance(self.value.as(not null)) end redef class AArrayExpr redef fun expr(v) do - var mtype = self.mtype.as(MClassType).arguments.first + var mtype = self.element_mtype.as(not null) var array = new Array[RuntimeVariable] - for nexpr in self.n_exprs.n_exprs do - var i = v.expr(nexpr, mtype) - array.add(i) + var res = v.array_instance(array, mtype) + + var old_comprehension = v.frame.comprehension + v.frame.comprehension = res + for nexpr in self.n_exprs do + v.stmt(nexpr) end - return v.array_instance(array, mtype) + v.frame.comprehension = old_comprehension + + return res end end @@ -2709,7 +2788,7 @@ redef class ACrangeExpr var i2 = v.expr(self.n_expr2, null) var mtype = self.mtype.as(MClassType) var res = v.init_instance(mtype) - var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) + v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) return res end end @@ -2721,21 +2800,21 @@ redef class AOrangeExpr var i2 = v.expr(self.n_expr2, null) var mtype = self.mtype.as(MClassType) var res = v.init_instance(mtype) - var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) + v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) return res end end redef class ATrueExpr - redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null)) + redef fun expr(v) do return v.bool_instance(true) end redef class AFalseExpr - redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null)) + redef fun expr(v) do return v.bool_instance(false) end redef class ANullExpr - redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null)) + redef fun expr(v) do return v.null_instance end redef class AIsaExpr @@ -2763,7 +2842,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.ctype != "val*" then return i + if i.mtype.is_c_primitive then return i v.add("if (unlikely({i} == NULL)) \{") v.add_abort("Cast failed") @@ -2785,7 +2864,7 @@ redef class AOnceExpr v.add_decl("static {mtype.ctype} {name};") v.add_decl("static int {guard};") var res = v.new_var(mtype) - v.add("if ({guard}) \{") + v.add("if (likely({guard})) \{") v.add("{res} = {name};") v.add("\} else \{") var i = v.expr(self.n_expr, mtype) @@ -2801,11 +2880,9 @@ redef class ASendExpr redef fun expr(v) do var recv = v.expr(self.n_expr, null) - var args = [recv] - for a in self.raw_arguments do - args.add(v.expr(a, null)) - end - return v.compile_callsite(self.callsite.as(not null), args) + var callsite = self.callsite.as(not null) + var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments) + return v.compile_callsite(callsite, args) end end @@ -2813,13 +2890,12 @@ redef class ASendReassignFormExpr redef fun stmt(v) do var recv = v.expr(self.n_expr, null) - var args = [recv] - for a in self.raw_arguments do - args.add(v.expr(a, null)) - end + var callsite = self.callsite.as(not null) + var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments) + var value = v.expr(self.n_value, null) - var left = v.compile_callsite(self.callsite.as(not null), args) + var left = v.compile_callsite(callsite, args) assert left != null var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value]) @@ -2834,14 +2910,12 @@ redef class ASuperExpr redef fun expr(v) do var recv = v.frame.arguments.first - var args = [recv] - for a in self.n_args.n_exprs do - args.add(v.expr(a, null)) - end var callsite = self.callsite if callsite != null then - # Add additionnals arguments for the super init call + var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs) + + # Add additional arguments for the super init call if args.length == 1 then for i in [0..callsite.msignature.arity[ do args.add(v.frame.arguments[i+1]) @@ -2852,39 +2926,36 @@ redef class ASuperExpr return res end + var mpropdef = self.mpropdef.as(not null) + var args = v.varargize(mpropdef, recv, self.n_args.n_exprs) if args.length == 1 then args = v.frame.arguments end # stantard call-next-method - return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args) + return v.supercall(mpropdef, recv.mtype.as(MClassType), args) end end redef class ANewExpr redef fun expr(v) do - var mtype = self.mtype.as(MClassType) - var recv - var ctype = mtype.ctype + var mtype = self.recvtype + assert mtype != null + 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 - recv = v.new_expr("({ctype})0/*special!*/", mtype) end - var args = [recv] - for a in self.n_args.n_exprs do - args.add(v.expr(a, null)) - end - var res2 = v.compile_callsite(self.callsite.as(not null), args) + + var recv = v.init_instance_or_extern(mtype) + + var callsite = self.callsite.as(not null) + var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs) + var res2 = v.compile_callsite(callsite, args) if res2 != null then #self.debug("got {res2} from {mproperty}. drop {recv}") return res2 @@ -2903,12 +2974,13 @@ redef class AAttrExpr end redef class AAttrAssignExpr - redef fun stmt(v) + redef fun expr(v) do var recv = v.expr(self.n_expr, null) var i = v.expr(self.n_value, null) var mproperty = self.mproperty.as(not null) v.write_attribute(mproperty, recv, i) + return i end end @@ -2981,16 +3053,13 @@ redef class MModule # Give requided addinional system libraries (as given to LD_LIBS) # Note: can return null instead of an empty set - fun collect_linker_libs: nullable Set[String] do return null + fun collect_linker_libs: nullable Array[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." +toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs." # We do not add other options, so process them now! toolcontext.process_options(args) @@ -3008,7 +3077,6 @@ 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 @@ -3016,8 +3084,5 @@ 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