X-Git-Url: http://nitlanguage.org diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index 3f0e8a1..f0a66ac 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -23,6 +23,7 @@ import platform import c_tools private import annotation import mixin +import counter # Add compiling options redef class ToolContext @@ -62,8 +63,8 @@ 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") # --release @@ -75,7 +76,7 @@ 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) @@ -87,19 +88,8 @@ 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 "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 "Error: cannot use both --dir and --output" + print "Option Error: cannot use both --dir and --output" exit(1) end @@ -114,58 +104,78 @@ 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) - compile_dir = toolchain.compile_dir - toolchain.write_and_make compiler + var toolchain = platform.toolchain(toolcontext, compiler) + compiler.toolchain = toolchain + toolchain.write_and_make end end redef class Platform # The specific tool-chain associated to the platform - fun toolchain(toolcontext: ToolContext): Toolchain do return new MakefileToolchain(toolcontext) + fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain + do + return new MakefileToolchain(toolcontext, compiler) + end end +# Build toolchain for a specific target program, varies per `Platform` class Toolchain + + # Toolcontext var toolcontext: ToolContext - fun compile_dir: String + # Compiler of the target program + var compiler: AbstractCompiler + + # 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 - fun write_and_make(compiler: AbstractCompiler) is abstract + # 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 +# Default toolchain using a Makefile class MakefileToolchain super Toolchain - redef fun write_and_make(compiler) + redef fun write_and_make do var compile_dir = compile_dir + # Remove the compilation directory unless explicitly set + var auto_remove = toolcontext.opt_compile_dir.value == null + # 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] - write_files(compiler, compile_dir, cfiles) + write_files(compile_dir, cfiles) # Generate the Makefile - write_makefile(compiler, compile_dir, cfiles) + write_makefile(compile_dir, cfiles) var time1 = get_time self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2) @@ -177,16 +187,21 @@ class MakefileToolchain time0 = time1 self.toolcontext.info("*** COMPILING C ***", 1) - compile_c_code(compiler, compile_dir) + 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 - fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String]) + # Write all source files to the `compile_dir` + fun write_files(compile_dir: String, cfiles: Array[String]) do var platform = compiler.target_platform - if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings + if platform.supports_libunwind then compiler.build_c_to_nit_bindings var cc_opt_with_libgc = "-DWITH_LIBGC" if not platform.supports_libgc then cc_opt_with_libgc = "" @@ -205,14 +220,14 @@ 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 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" @@ -227,7 +242,7 @@ class MakefileToolchain for f in compiler.files do var i = 0 var count = 0 - 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 @@ -240,7 +255,7 @@ class MakefileToolchain 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 @@ -258,8 +273,8 @@ class MakefileToolchain var cfilename = "{f.name}.0.h" var cfilepath = "{compile_dir}/{cfilename}" - var hfile: nullable OFStream = null - hfile = new OFStream.open(cfilepath) + 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 @@ -280,17 +295,14 @@ class MakefileToolchain self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2) end - fun makefile_name(mainmodule: MModule): String do return "{mainmodule.c_name}.mk" + # Get the name of the Makefile to use + fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk" - fun default_outname(mainmodule: MModule): String + # Get the default name of the executable to produce + fun default_outname: 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 + var mainmodule = compiler.mainmodule.first_real_mmodule + return mainmodule.name end # Combine options and platform informations to get the final path of the outfile @@ -298,13 +310,14 @@ class MakefileToolchain do var res = self.toolcontext.opt_output.value if res != null then return res - res = default_outname(mainmodule) + res = default_outname var dir = self.toolcontext.opt_dir.value if dir != null then return dir.join_path(res) return res end - fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String]) + # Write the Makefile + fun write_makefile(compile_dir: String, cfiles: Array[String]) do var mainmodule = compiler.mainmodule var platform = compiler.target_platform @@ -315,13 +328,13 @@ 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 - var makename = makefile_name(mainmodule) + var makename = makefile_name var makepath = "{compile_dir}/{makename}" - var makefile = new OFStream.open(makepath) + var makefile = new FileWriter.open(makepath) var linker_options = new HashSet[String] for m in mainmodule.in_importation.greaters do @@ -331,25 +344,59 @@ class MakefileToolchain 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") + 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 + +""" makefile.write("all: {outpath}\n") if outpath != real_outpath then @@ -371,7 +418,7 @@ class MakefileToolchain if not compiler.linker_script.is_empty then var linker_script_path = "{compile_dir}/linker_script" ofiles.add "linker_script" - var f = new OFStream.open(linker_script_path) + var f = new FileWriter.open(linker_script_path) for l in compiler.linker_script do f.write l f.write "\n" @@ -406,7 +453,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) @@ -444,22 +491,25 @@ endif makepath.file_copy_to "{compile_dir}/Makefile" end - fun compile_c_code(compiler: AbstractCompiler, compile_dir: String) + # The C code is generated, compile it to an executable + fun compile_c_code(compile_dir: String) do - var makename = makefile_name(compiler.mainmodule) + var makename = makefile_name var makeflags = self.toolcontext.opt_make_flags.value if makeflags == null then makeflags = "" - self.toolcontext.info("make -B -C {compile_dir} -f {makename} -j 4 {makeflags}", 2) + + var command = "make -B -C {compile_dir} -f {makename} -j 4 {makeflags}" + self.toolcontext.info(command, 2) var res if self.toolcontext.verbose_level >= 3 then - res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1") + res = sys.system("{command} 2>&1") else - res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1 >/dev/null") + res = sys.system("{command} 2>&1 >/dev/null") end if res != 0 then - toolcontext.error(null, "make failed! Error code: {res}.") + toolcontext.error(null, "Compilation Error: `make` failed with error code: {res}. The command was `{command}`.") end end end @@ -481,6 +531,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 @@ -544,9 +599,9 @@ 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 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") @@ -576,7 +631,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 @@ -589,6 +644,9 @@ abstract class AbstractCompiler 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 \n") self.header.add_decl("#include \"gc_chooser.h\"") self.header.add_decl("#ifdef ANDROID") self.header.add_decl(" #include ") @@ -603,9 +661,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 @@ -674,7 +732,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass) if finalize_meth == null then - modelbuilder.toolcontext.error(null, "The `Finalizable` class doesn't declare the `finalize` method.") + modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.") return end @@ -696,19 +754,16 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); do var v = self.new_visitor v.add_decl("#include ") - var ost = modelbuilder.toolcontext.opt_stacktrace.value var platform = target_platform - if not platform.supports_libunwind then ost = "none" - var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value - if ost == "nitstack" or ost == "libunwind" then + if platform.supports_libunwind then + v.add_decl("#ifndef NO_STACKTRACE") v.add_decl("#define UNW_LOCAL_ONLY") v.add_decl("#include ") - if ost == "nitstack" then - v.add_decl("#include \"c_functions_hash.h\"") - end + 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;") @@ -741,13 +796,9 @@ 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) \{") - if ost == "nitstack" or ost == "libunwind" then + 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)\{") @@ -761,22 +812,31 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");") v.add_decl("while (unw_step(&cursor) > 0) \{") v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);") - if ost == "nitstack" then v.add_decl(" const char* recv = get_nit_name(procname, strlen(procname));") v.add_decl(" if (recv != NULL)\{") v.add_decl(" PRINT_ERROR(\"` %s\\n\", recv);") v.add_decl(" \}else\{") v.add_decl(" PRINT_ERROR(\"` %s\\n\", procname);") v.add_decl(" \}") - else - v.add_decl(" PRINT_ERROR(\"` %s \\n\",procname);") - end v.add_decl("\}") v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");") v.add_decl("free(procname);") v.add_decl("\}") + v.add_decl("#endif /* NO_STACKTRACE */") 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 @@ -791,7 +851,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); 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("signal(SIGPIPE, SIG_IGN);") v.add("glob_argc = argc; glob_argv = argv;") v.add("initialize_gc_option();") @@ -856,6 +916,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 @@ -1008,14 +1074,6 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) { end fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self) - - # Division facility - # Avoid division by zero by returning the string "n/a" - fun div(a,b:Int):String - do - if b == 0 then return "n/a" - return ((a*10000/b).to_f / 100.0).to_precision(2) - end end # A file unit (may be more than one file if @@ -1056,8 +1114,8 @@ abstract class AbstractCompilerVisitor # The current visited AST node var current_node: nullable ANode = null is writable - # The current `Frame` - var frame: nullable Frame = null 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 @@ -1072,9 +1130,6 @@ abstract class AbstractCompilerVisitor 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 @@ -1116,43 +1171,56 @@ abstract class AbstractCompilerVisitor fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract + # Return an element of a native array. + # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays + fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract + + # Store an element in a native array. + # 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 + # 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] + fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable] do var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) var res = new Array[RuntimeVariable] res.add(recv) - if args.is_empty then return res + if msignature.arity == 0 then return res + + if map == null then + assert args.length == msignature.arity + for ne in args do + res.add self.expr(ne, null) + end + return res + end - var vararg_rank = msignature.vararg_rank - var vararg_len = args.length - msignature.arity - if vararg_len < 0 then vararg_len = 0 + # Eval in order of arguments, not parameters + var exprs = new Array[RuntimeVariable].with_capacity(args.length) + for ne in args do + exprs.add self.expr(ne, null) + end + # Fill `res` with the result of the evaluation according to the mapping 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 param = msignature.mparameters[i] + var j = map.map.get_or_null(i) + if j == null then + # default value + res.add(null_instance) + continue + end + if param.is_vararg and map.vararg_decl > 0 then + var vararg = exprs.sub(j, map.vararg_decl) + var elttype = param.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) + continue end + res.add exprs[j] end return res end @@ -1381,6 +1449,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 @@ -1392,40 +1478,79 @@ 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 an integer value - fun bool_instance(value: Bool): RuntimeVariable + # 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 a char value + fun char_instance(value: Char): RuntimeVariable do - var res = self.new_var(self.get_class("Bool").mclass_type) - if value then - self.add("{res} = 1;") + var t = mmodule.char_type + + if value.ascii < 128 then + return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t) else - self.add("{res} = 0;") + return new RuntimeVariable("{value.ascii}", t, t) end + 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) + var length = self.int_instance(string.bytelen) self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};") self.add("{name} = {res};") self.add("\}") @@ -1486,12 +1611,12 @@ abstract class AbstractCompilerVisitor 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 @@ -1502,7 +1627,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 @@ -1525,12 +1650,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 @@ -1555,6 +1683,13 @@ abstract class AbstractCompilerVisitor fun stmt(nexpr: nullable AExpr) do if nexpr == null then return + if nexpr.mtype == null and not nexpr.is_typed then + # Untyped expression. + # Might mean dead code or invalid code + # so aborts + add_abort("FATAL: bad statement executed.") + return + end var narray = nexpr.comprehension if narray != null then @@ -1576,7 +1711,25 @@ abstract class AbstractCompilerVisitor do var old = self.current_node self.current_node = nexpr - var res = nexpr.expr(self).as(not null) + + var res = null + if nexpr.mtype != null then + res = nexpr.expr(self) + end + + if res == null then + # Untyped expression. + # 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 + res = new_var(mtype) + + self.current_node = old + return res + end + if mtype != null then mtype = self.anchor(mtype) res = self.autobox(res, mtype) @@ -1686,8 +1839,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 @@ -1727,20 +1880,26 @@ redef class MType # Short name of the `ctype` to use in unions fun ctypename: String do return "val" + + # 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 ctype: String - do + redef var ctype is lazy do if mclass.name == "Int" then return "long" 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 == "Byte" then + return "unsigned char" else if mclass.name == "NativeString" then return "char*" else if mclass.name == "NativeArray" then @@ -1750,6 +1909,8 @@ redef class MClassType 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 @@ -1769,6 +1930,8 @@ redef class MClassType return "c" else if mclass.name == "Float" then return "d" + else if mclass.name == "Byte" then + return "b" else if mclass.name == "NativeString" then return "str" else if mclass.name == "NativeArray" then @@ -1807,6 +1970,15 @@ redef class MMethodDef var modelbuilder = v.compiler.modelbuilder 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 = node @@ -1866,13 +2038,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 @@ -1957,6 +2122,9 @@ redef class AMethPropdef 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 @@ -1994,15 +2162,15 @@ redef class AMethPropdef else if pname == "to_f" then v.ret(v.new_expr("(double){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 == "ascii" then - v.ret(v.new_expr("{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 == "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 @@ -2034,7 +2202,70 @@ redef class AMethPropdef v.ret(v.new_expr("{arguments[0]}-'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("(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 == "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 + 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_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))) return true end else if cname == "Bool" then @@ -2068,6 +2299,9 @@ redef class AMethPropdef 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 == "succ" then v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null))) return true @@ -2102,13 +2336,16 @@ 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 end else if cname == "NativeString" 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]});") @@ -2116,6 +2353,9 @@ redef class AMethPropdef else if pname == "atoi" then v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null))) return true + else if pname == "fast_cstring" then + v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", 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))) return true @@ -2236,8 +2476,8 @@ redef class AAttrPropdef var res if is_lazy then var set - var ret = self.mpropdef.static_mtype - var useiset = ret.ctype == "val*" and not ret isa MNullableType + var ret = self.mtype + 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, recv) @@ -2252,7 +2492,7 @@ redef class AAttrPropdef 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("\}") @@ -2264,10 +2504,10 @@ redef class AAttrPropdef assert arguments.length == 2 v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1]) if is_lazy then - var ret = self.mpropdef.static_mtype - var useiset = ret.ctype == "val*" and not ret isa MNullableType + 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.new_expr("1", v.bool_type)) + v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true)) end end else @@ -2277,7 +2517,7 @@ redef class AAttrPropdef fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable) do - if has_value and not is_lazy then evaluate_expr(v, recv) + 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 @@ -2286,11 +2526,11 @@ 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.mcasttype.as_notnullable.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 @@ -2325,7 +2565,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) @@ -2437,6 +2677,13 @@ redef class ASelfExpr redef fun expr(v) do return v.frame.arguments.first end +redef class AImplicitSelfExpr + redef fun expr(v) do + if not is_sys then return super + return v.new_expr("glob_sys", mtype.as(not null)) + end +end + redef class AEscapeExpr redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};") end @@ -2664,16 +2911,21 @@ redef class AOrElseExpr end end -redef class AIntExpr - redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.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)) + # Should never happen + abort + end 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 @@ -2701,14 +2953,64 @@ end redef class ASuperstringExpr redef fun expr(v) do - var array = new Array[RuntimeVariable] + var type_string = mtype.as(not null) + + # Collect elements of the superstring + var array = new Array[AExpr] for ne in self.n_exprs do + # Drop literal empty string. + # They appears in things like "{a}" that is ["", a, ""] if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings - var i = v.expr(ne, null) - array.add(i) + array.add(ne) end - var a = v.array_instance(array, v.object_type) - var res = v.send(v.get_property("to_s", a.mtype), [a]) + + # Store the allocated native array in a static variable + # For reusing later + var varonce = v.get_name("varonce") + v.add("if (unlikely({varonce}==NULL)) \{") + + # The native array that will contains the elements to_s-ized. + # For fast concatenation. + var a = v.native_array_instance(type_string, v.int_instance(array.length)) + + v.add_decl("static {a.mtype.ctype} {varonce};") + + # Pre-fill the array with the literal string parts. + # So they do not need to be filled again when reused + for i in [0..array.length[ do + var ne = array[i] + if not ne isa AStringFormExpr then continue + var e = v.expr(ne, null) + v.native_array_set(a, i, e) + end + + v.add("\} else \{") + # Take the native-array from the store. + # The point is to prevent that some recursive execution use (and corrupt) the same native array + # WARNING: not thread safe! (FIXME?) + v.add("{a} = {varonce};") + v.add("{varonce} = NULL;") + v.add("\}") + + # Stringify the elements and put them in the native array + var to_s_method = v.get_property("to_s", v.object_type) + for i in [0..array.length[ do + var ne = array[i] + if ne isa AStringFormExpr then continue + var e = v.expr(ne, null) + # Skip the `to_s` if the element is already a String + if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then + e = v.send(to_s_method, [e]).as(not null) + end + v.native_array_set(a, i, e) + end + + # Fast join the native string to get the result + var res = v.send(v.get_property("native_to_s", a.mtype), [a]) + + # 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 @@ -2738,22 +3040,24 @@ redef class AOrangeExpr 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 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 @@ -2774,7 +3078,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") @@ -2796,7 +3100,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) @@ -2813,7 +3117,7 @@ redef class ASendExpr do var recv = v.expr(self.n_expr, null) var callsite = self.callsite.as(not null) - var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments) + var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments) return v.compile_callsite(callsite, args) end end @@ -2823,7 +3127,7 @@ redef class ASendReassignFormExpr do var recv = v.expr(self.n_expr, null) var callsite = self.callsite.as(not null) - var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments) + var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments) var value = v.expr(self.n_value, null) @@ -2845,26 +3149,33 @@ redef class ASuperExpr var callsite = self.callsite if callsite != null then - var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs) + var args - # Add additional arguments for the super init call - if args.length == 1 then + 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]) end + else + args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs) end + # Super init call var res = v.compile_callsite(callsite, args) 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 + + var args + if self.n_args.n_exprs.is_empty then args = v.frame.arguments + else + args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs) end - # stantard call-next-method + # Standard call-next-method return v.supercall(mpropdef, recv.mtype.as(MClassType), args) end end @@ -2874,24 +3185,21 @@ redef class ANewExpr do var mtype = self.recvtype assert mtype != null - var recv - var ctype = mtype.ctype + 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 == "char*" then - recv = v.new_expr("NULL/*special!*/", mtype) - else - recv = v.new_expr("({ctype})0/*special!*/", mtype) end - var callsite = self.callsite.as(not null) - var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs) + var recv = v.init_instance_or_extern(mtype) + + var callsite = self.callsite + if callsite == null then return recv + + var args = v.varargize(callsite.mpropdef, callsite.signaturemap, 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}") @@ -2943,6 +3251,20 @@ redef class AIssetAttrExpr end end +redef class AVarargExpr + redef fun expr(v) + do + return v.expr(self.n_expr, null) + end +end + +redef class ANamedargExpr + redef fun expr(v) + do + return v.expr(self.n_expr, null) + end +end + redef class ADebugTypeExpr redef fun stmt(v) do @@ -2990,7 +3312,7 @@ 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 @@ -3008,7 +3330,7 @@ var modelbuilder = new ModelBuilder(model, toolcontext) var arguments = toolcontext.option_context.rest if arguments.length > 1 and toolcontext.opt_output.value != null then - print "Error: --output needs a single source file. Do you prefer --dir?" + print "Option Error: --output needs a single source file. Do you prefer --dir?" exit 1 end