module abstract_compiler
import literal
-import typing
-import auto_super_init
+import semantize
import platform
import c_tools
+private import annotation
# Add compiling options
redef class ToolContext
if f.compiles_to_o_file then ofiles.add(o)
if f.add_to_jar then java_files.add(f)
end
-
+
if not java_files.is_empty then
var jar_file = "{outpath}.jar"
# 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
+ var modelbuilder: ModelBuilder is protected writable
# Is hardening asked? (see --hardening)
fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
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)
end
# Generate the main C function.
+ #
# This function:
- # * allocate the Sys object if it exists
- # * call init if is exists
- # * call main if it exists
+ #
+ # * allocate the Sys object if it exists
+ # * call init if is exists
+ # * call main if it exists
fun compile_main_function
do
var v = self.new_visitor
end
# Display stats about compilation process
+ #
# Metrics used:
- # * type tests against resolved types (`x isa Collection[Animal]`)
- # * type tests against unresolved types (`x isa Collection[E]`)
- # * type tests skipped
- # * type tests total
- # *
+ #
+ # * type tests against resolved types (`x isa Collection[Animal]`)
+ # * type tests against unresolved types (`x isa Collection[E]`)
+ # * type tests skipped
+ # * type tests total
fun display_stats
do
if self.modelbuilder.toolcontext.opt_typing_test_metrics.value 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
private var escapemark_names = new HashMap[EscapeMark, String]
# Return a "const char*" variable associated to the classname of the dynamic type of an object
- # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
+ # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program
fun class_name_string(value: RuntimeVariable): String is abstract
# Variables handling
var mtype = recv.mtype
var finalizable_type = compiler.mainmodule.finalizable_type
if finalizable_type != null and not mtype.need_anchor and
- mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
+ mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then
add "gc_register_finalizer({recv});"
end
end
# 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 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
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
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
+ return true
else if pname == "init" 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
+ var nextern = self.n_extern
+ if nextern == null then return false
+ externname = nextern.text.substring(1, nextern.text.length-2)
end
- externname = nextern.text.substring(1, nextern.text.length-2)
if location.file != null then
var file = location.file.filename
v.add_extern(file)
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
+ var nextern = self.n_extern
+ if nextern == null then return false
+ externname = nextern.text.substring(1, nextern.text.length-2)
end
- externname = nextern.text.substring(1, nextern.text.length-2)
if location.file != null then
var file = location.file.filename
v.add_extern(file)
v.add("{res} = {externname}({arguments.join(", ")});")
res = v.box_extern(res, ret)
v.ret(res)
+ return true
end
end
end
toolcontext.run_global_phases(ms)
end
-