import c_tools
private import annotation
import mixin
+import counter
# Add compiling options
redef class ToolContext
# Default is nitstack
opt_stacktrace.value = "nitstack"
else
- print "Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`."
+ print "Option Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`."
exit(1)
end
if opt_output.value != null and opt_dir.value != null then
- print "Error: cannot use both --dir and --output"
+ print "Option Error: cannot use both --dir and --output"
exit(1)
end
protected fun write_and_make(compiler: AbstractCompiler)
do
var platform = compiler.target_platform
- var toolchain = platform.toolchain(toolcontext)
+ var toolchain = platform.toolchain(toolcontext, compiler)
compile_dir = toolchain.compile_dir
- toolchain.write_and_make compiler
+ 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
+ # Compiler of the target program
+ var compiler: AbstractCompiler
+
+ # Directory where to generate all C files
fun compile_dir: String
do
var compile_dir = toolcontext.opt_compile_dir.value
return compile_dir
end
- fun write_and_make(compiler: AbstractCompiler) is abstract
+ # 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
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)
time0 = time1
self.toolcontext.info("*** COMPILING C ***", 1)
- compile_c_code(compiler, compile_dir)
+ compile_c_code(compile_dir)
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
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
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
# 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 FileWriter.open(makepath)
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
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
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
# 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
- var vararg_rank = msignature.vararg_rank
- var vararg_len = args.length - msignature.arity
- if vararg_len < 0 then vararg_len = 0
+ if map == null then
+ assert args.length == msignature.arity
+ for ne in args do
+ res.add self.expr(ne, null)
+ end
+ return res
+ end
+
+ # 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
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
+ # So just return
+ return
+ end
var narray = nexpr.comprehension
if narray != null then
# `mtype` is the expected return type, pass null if no specific type is expected.
fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
do
+ if nexpr.mtype == null then
+ # Untyped expression.
+ # Might mean dead code
+ # so return a placebo result
+ if mtype == null then mtype = compiler.mainmodule.object_type
+ return new_var(mtype)
+ end
var old = self.current_node
self.current_node = nexpr
var res = nexpr.expr(self).as(not null)
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 == "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
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
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])
+ var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
v.frame = frame
var value
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
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
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)
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
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 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}")
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
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