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")
+ var opt_no_main = 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_cc_path = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
# --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")
# --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
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)
do
gather_cc_paths
- var mainmodule = compiler.mainmodule
var compile_dir = compile_dir
# Generate the .h and .c files
compiler.files_to_copy.add "{cc_paths.first}/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
fun makefile_name(mainmodule: MModule): String do return "{mainmodule.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
var outname = outfile(mainmodule)
- var orig_dir=".." # FIXME only works if `compile_dir` is a subdirectory of cwd
+ var orig_dir = compile_dir.relpath(".")
var outpath = orig_dir.join_path(outname).simplify_path
var makename = makefile_name(mainmodule)
var makepath = "{compile_dir}/{makename}"
end
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)
# The main module of the program currently compiled
# Is assigned during the separate compilation
- var mainmodule: MModule writable
+ var mainmodule: MModule is writable
# The real main module of the program
var realmainmodule: MModule
- # The modeulbuilder used to know the model and the AST
- var modelbuilder: ModelBuilder protected writable
+ # 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
# 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 writable
+ var header: CodeWriter is writable
# Provide a declaration that can be requested (before or latter) by a visitor
fun provide_declaration(key: String, s: String)
# 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 <stdlib.h>")
self.header.add_decl("#include <stdio.h>")
self.header.add_decl("#include <string.h>")
var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
self.mainmodule.linearize_mclassdefs(cds)
for cd in cds do
+ if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
var n = self.modelbuilder.mclassdef2nclassdef[cd]
for npropdef in n.n_propdefs do
if npropdef isa AAttrPropdef then
var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
self.mainmodule.linearize_mclassdefs(cds)
for cd in cds do
+ if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
var n = self.modelbuilder.mclassdef2nclassdef[cd]
for npropdef in n.n_propdefs do
if npropdef isa AAttrPropdef then
var compiler: COMPILER
# The current visited AST node
- var current_node: nullable ANode writable = null
+ var current_node: nullable ANode = null is writable
# The current `Frame`
- var frame: nullable Frame writable
+ var frame: nullable Frame is writable
# Alias for self.compiler.mainmodule.object_type
fun object_type: MClassType do return self.compiler.mainmodule.object_type
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
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
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]
-
- args.add(rawargs.first) # recv
+ var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+ var res = new Array[RuntimeVariable]
+ res.add(recv)
- 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
+ if args.is_empty then return res
- 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
# 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
# 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
# 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
return name
end
+ # 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
+
private var escapemark_names = new HashMap[EscapeMark, String]
# Return a "const char*" variable associated to the classname of the dynamic type of an object
# 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
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
return res
end
+ # Generate an integer value
+ fun bool_instance(value: Bool): RuntimeVariable
+ do
+ var res = self.new_var(self.get_class("Bool").mclass_type)
+ if value then
+ self.add("{res} = 1;")
+ else
+ self.add("{res} = 0;")
+ end
+ return res
+ end
+
# Generate a string value
fun string_instance(string: String): RuntimeVariable
do
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
# Non cached version of `c_name`
protected fun build_c_name: String is abstract
- protected var c_name_cache: nullable String writable = null
+ protected var c_name_cache: nullable String = null is writable
# Implements a call of the runtime_function
# May inline the body or generate a C function call
var mtype: MType
# The current casted type of the variable (as known in Nit)
- var mcasttype: MType writable
+ var mcasttype: MType is writable
# If the variable exaclty a mcasttype?
# false (usual value) means that the variable is a mcasttype or a subtype.
- var is_exact: Bool writable = false
+ var is_exact: Bool = false is writable
init(name: String, mtype: MType, mcasttype: MType)
do
var arguments: Array[RuntimeVariable]
# The runtime_variable associated to the return (in a function)
- var returnvar: nullable RuntimeVariable writable = null
+ var returnvar: nullable RuntimeVariable = null is writable
# The label at the end of the property
- var returnlabel: nullable String writable = null
+ var returnlabel: nullable String = null is writable
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 protected writable
+ protected var c_name_cache: nullable String is protected writable
end
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 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
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
if modelbuilder.mpropdef2npropdef.has_key(self) then
var npropdef = modelbuilder.mpropdef2npropdef[self]
return npropdef.can_inline
- else if self.mproperty.name == "init" then
+ else if self.mproperty.is_root_init then
# Automatic free init is always inlined since it is empty or contains only attribtes assigments
return true
else
fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
do
var modelbuilder = v.compiler.modelbuilder
+ var val = constant_value
if modelbuilder.mpropdef2npropdef.has_key(self) then
var npropdef = modelbuilder.mpropdef2npropdef[self]
var oldnode = v.current_node
self.compile_parameter_check(v, arguments)
npropdef.compile_to_c(v, self, arguments)
v.current_node = oldnode
- else if self.mproperty.name == "init" then
+ else if self.mproperty.is_root_init then
var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
var oldnode = v.current_node
v.current_node = nclassdef
self.compile_parameter_check(v, arguments)
nclassdef.compile_to_c(v, self, arguments)
v.current_node = oldnode
+ else if val != null then
+ v.ret(v.value_instance(val))
else
abort
end
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
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
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
+ 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
- externname = nextern.text.substring(1, nextern.text.length-2)
if location.file != null then
var file = location.file.filename
v.add_extern(file)
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
+ 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
- externname = nextern.text.substring(1, nextern.text.length-2)
if location.file != null then
var file = location.file.filename
v.add_extern(file)
end
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
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
# 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
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)
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
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
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
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
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
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
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 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
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])
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])
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
return v.native_array_instance(elttype, l)
else if ctype == "val*" then
recv = v.init_instance(mtype)
- else if ctype == "void*" then
+ else if ctype == "char*" 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 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
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
# 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."
# We do not add other options, so process them now!
# 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
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