write_and_make(compiler)
end
- private fun write_and_make(compiler: GlobalCompiler)
+ protected fun write_and_make(compiler: GlobalCompiler)
do
var mainmodule = compiler.mainmodule
end
# Singleton that store the knowledge about the compilation process
-private class GlobalCompiler
+class GlobalCompiler
# The main module of the program
var mainmodule: MModule
#
# FIXME: should not be a vistor but just somewhere to store lines
# FIXME: should not have a global .h since it does not help recompilations
- var header: nullable GlobalCompilerVisitor = null
+ var header: nullable GlobalCompilerVisitor writable = null
# The list of all associated visitors
# Used to generate .c files
end
# Cache for classid (computed by declare_runtimeclass)
- private var classids: HashMap[MClassType, String] = new HashMap[MClassType, String]
+ protected var classids: HashMap[MClassType, String] = new HashMap[MClassType, String]
# Declare C structures and identifiers for a runtime class
fun declare_runtimeclass(v: GlobalCompilerVisitor, mtype: MClassType)
v.add_decl("struct {mtype.c_name} \{")
v.add_decl("int classid; /* must be {idname} */")
+ if mtype.mclass.name == "NativeArray" then
+ # NativeArrays are just a instance header followed by an array of values
+ v.add_decl("{mtype.arguments.first.ctype} values[1];")
+ end
+
if mtype.ctype != "val*" then
# Is the Nit type is native then the struct is a box with two fields:
# * the `classid` to be polymorph
assert mtype.ctype == "val*"
var v = new GlobalCompilerVisitor(self)
- self.header.add_decl("{mtype.ctype} NEW_{mtype.c_name}(void);")
+ var is_native_array = mtype.mclass.name == "NativeArray"
+
+ var sig
+ if is_native_array then
+ sig = "int length"
+ else
+ sig = "void"
+ end
+
+ self.header.add_decl("{mtype.ctype} NEW_{mtype.c_name}({sig});")
v.add_decl("/* allocate {mtype} */")
- v.add_decl("{mtype.ctype} NEW_{mtype.c_name}(void) \{")
+ v.add_decl("{mtype.ctype} NEW_{mtype.c_name}({sig}) \{")
var res = v.new_var(mtype)
res.is_exact = true
- v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}));")
+ if is_native_array then
+ var mtype_elt = mtype.arguments.first
+ v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}) + length*sizeof({mtype_elt.ctype}));")
+ else
+ v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}));")
+ end
v.add("{res}->classid = {self.classid(mtype)};")
for cd in mtype.collect_mclassdefs(self.mainmodule)
else if mclass.name == "NativeString" then
return "char*"
else if mclass.name == "NativeArray" then
- assert self isa MGenericType
- return "{self.arguments.first.ctype}*"
+ #return "{self.arguments.first.ctype}*"
+ return "val*"
else if mclass.kind == extern_kind then
return "void*"
else
end
end
+redef class MParameterType
+ redef fun c_name
+ do
+ var res = self.c_name_cache
+ if res != null then return res
+ res = "FT{self.rank}"
+ self.c_name_cache = res
+ return res
+ end
+end
+
redef class MNullableType
redef fun c_name
do
# A C function associated to a Nit method
# Because of customization, a given Nit method can be compiler more that once
-private abstract class AbstractRuntimeFunction
+abstract class AbstractRuntimeFunction
# The associated Nit method
var mmethoddef: MMethodDef
sig.append("void ")
end
sig.append(self.c_name)
- sig.append("({recv.ctype} self")
- comment.append("(self: {recv}")
+ sig.append("({recv.ctype} {selfvar}")
+ comment.append("(self: {selfvar}")
arguments.add(selfvar)
for i in [0..mmethoddef.msignature.arity[ do
var mtype = mmethoddef.msignature.mparameters[i].mtype
# Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
#
# The tricky point is that a single C variable can be associated to more than one RuntimeVariable because the static knowledge of the type of an expression can vary in the C code.
-private class RuntimeVariable
+class RuntimeVariable
# The name of the variable in the C code
var name: String
var mtype: MType
# The current casted type of the variable (as known in Nit)
- var mcasttype: MType
+ var mcasttype: MType writable
# If the variable exaclty a mcasttype?
# false (usual value) means that the variable is a mcasttype or a subtype.
- var is_exact: Bool = false
+ var is_exact: Bool writable = false
init(name: String, mtype: MType, mcasttype: MType)
do
# A visitor on the AST of property definition that generate the C code.
# Because of inlining, a visitor can visit more than one property.
-private class GlobalCompilerVisitor
+class GlobalCompilerVisitor
# The associated compiler
var compiler: GlobalCompiler
# Force to get the primitive property named `name' in the instance `recv' or abort
fun get_property(name: String, recv: MType): MMethod
do
- return self.compiler.mainmodule.force_get_primitive_method(name, recv)
+ return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv, self.compiler.mainmodule)
end
# The current Frame
- var frame: nullable Frame
+ var frame: nullable Frame writable
# Anchor a type to the main module and the current receiver
fun anchor(mtype: MType): MType
private var decl_lines: List[String] = new List[String]
# The current visited AST node
- var current_node: nullable AExpr = null
+ var current_node: nullable ANode = null
# Compile an expression an return its result
# `mtype` is the expected return type, pass null if no specific type is expected.
if value.mtype.ctype == mtype.ctype then
return value
else if value.mtype.ctype == "val*" then
- return self.new_expr("((struct {mtype.c_name}*){value})->value /* autounbox from {value.mtype} to {mtype} */", mtype)
+ return self.new_expr("((struct {mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
else if mtype.ctype == "val*" then
var valtype = value.mtype.as(MClassType)
var res = self.new_var(mtype)
return res
end
+ # Return a new uninitialized named runtime_variable
+ fun new_named_var(mtype: MType, name: String): RuntimeVariable
+ do
+ mtype = self.anchor(mtype)
+ var res = new RuntimeVariable(name, mtype, mtype)
+ self.add_decl("{mtype.ctype} {name} /* : {mtype} */;")
+ return res
+ end
+
# Return a new local runtime_variable initialized with the C expression `cexpr'.
fun new_expr(cexpr: String, mtype: MType): RuntimeVariable
do
return res
end
+ fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable])
+ do
+ var elttype = arguments.first.mtype
+ var recv = "((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->values"
+ if pname == "[]" then
+ self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
+ return
+ else if pname == "[]=" then
+ self.add("{recv}[{arguments[1]}]={arguments[2]};")
+ return
+ else if pname == "copy_to" then
+ var recv1 = "((struct {arguments[1].mcasttype.c_name}*){arguments[1]})->values"
+ self.add("memcpy({recv1},{recv},{arguments[2]}*sizeof({elttype.ctype}));")
+ return
+ end
+ end
+
+ fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable])
+ do
+ self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type))
+ end
+
# Generate a polymorphic send for the method `m' and the arguments `args'
fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable
do
else
args = args.to_a
end
- assert args.length == m.msignature.arity + 1 # because of self
+ if args.length != m.msignature.arity + 1 then # because of self
+ add("printf(\"NOT YET IMPLEMENTED: Invalid arity for {m}. {args.length} arguments given.\\n\"); exit(1);")
+ debug("NOT YET IMPLEMENTED: Invalid arity for {m}. {args.length} arguments given.")
+ return null
+ end
args.first = recv
var rm = new CustomizedRuntimeFunction(m, recvtype)
self.add("fprintf(stderr, \"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");")
end
+ # Generate a polymorphic attribute is_set test
+ fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable
+ do
+ var types = self.collect_types(recv)
+
+ var res = self.new_var(bool_type)
+
+ if types.is_empty then
+ self.add("/*BUG: no live types for {recv.inspect} . {a}*/")
+ return res
+ end
+ self.add("/* isset {a} on {recv.inspect} */")
+ self.add("switch({recv}->classid) \{")
+ var last = types.last
+ for t in types do
+ if not self.compiler.hardening and t == last then
+ self.add("default: /*{self.compiler.classid(t)}*/")
+ else
+ self.add("case {self.compiler.classid(t)}:")
+ end
+ var recv2 = self.autoadapt(recv, t)
+ var ta = a.intro.static_mtype.as(not null)
+ ta = self.resolve_for(ta, recv2)
+ var attr = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta)
+ if not ta isa MNullableType then
+ if ta.ctype == "val*" then
+ self.add("{res} = ({attr} != NULL);")
+ else
+ self.add("{res} = 1; /*NOTYET isset on primitive attributes*/")
+ end
+ end
+ self.add("break;")
+ end
+ if self.compiler.hardening then
+ self.add("default: /* Bug */")
+ self.bugtype(recv)
+ end
+ self.add("\}")
+
+ return res
+ end
+
# Generate a polymorphic attribute read
fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable
do
return res
end
+ # Generate the code required to dynamically check if 2 objects share the same runtime type
+ fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable
+ do
+ var res = self.new_var(bool_type)
+ if value2.mtype.ctype == "val*" then
+ if value1.mtype.ctype == "val*" then
+ self.add "{res} = {value1}->classid == {value2}->classid;"
+ else
+ self.add "{res} = {self.compiler.classid(value1.mtype.as(MClassType))} == {value2}->classid;"
+ end
+ else
+ if value1.mtype.ctype == "val*" then
+ self.add "{res} = {value1}->classid == {self.compiler.classid(value2.mtype.as(MClassType))};"
+ else if value1.mcasttype == value2.mcasttype then
+ self.add "{res} = 1;"
+ else
+ self.add "{res} = 0;"
+ end
+ end
+ return res
+ end
+
+ # Return a "const char*" variable associated to the classname of the dynamic type of an object
+ # NOTE: we do not return a RuntimeVariable "NativeString" as the class may not exist in the module/program
+ fun class_name_string(value: RuntimeVariable): String
+ do
+ var res = self.get_name("var_class_name")
+ self.add_decl("const char* {res};")
+ if value.mtype.ctype == "val*" then
+ self.add "{res} = class_names[{value}->classid];"
+ else
+ self.add "{res} = class_names[{self.compiler.classid(value.mtype.as(MClassType))}];"
+ end
+ return res
+ end
+
# Generate a Nit "is" for two runtime_variables
fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable
do
end
# Generate a check-init-instance
- # TODO: is an empty stub currently
fun check_init_instance(recv: RuntimeVariable)
do
+ var mtype = self.anchor(recv.mcasttype)
+ for cd in mtype.collect_mclassdefs(self.compiler.mainmodule)
+ do
+ var n = self.compiler.modelbuilder.mclassdef2nclassdef[cd]
+ for npropdef in n.n_propdefs do
+ if npropdef isa AAttrPropdef then
+ # Force read to check the initialization
+ self.read_attribute(npropdef.mpropdef.mproperty, recv)
+ end
+ end
+ end
end
# Generate an integer value
self.add("\{ /* {res} = array_instance Array[{elttype}] */")
var nat = self.new_var(self.get_class("NativeArray").get_mtype([elttype]))
nat.is_exact = true
- self.add("{nat} = GC_MALLOC({array.length} * sizeof({elttype.ctype}));")
+ self.add("{nat} = NEW_{nat.mtype.c_name}({array.length});")
for i in [0..array.length[ do
var r = self.autobox(array[i], elttype)
- self.add("{nat}[{i}] = {r};")
+ self.add("((struct {nat.mtype.c_name}*) {nat})->values[{i}] = {r};")
end
var length = self.int_instance(array.length)
self.send(self.get_property("with_native", arraytype), [res, nat, length])
end
# A frame correspond to a visited property in a GlobalCompilerVisitor
-private class Frame
+class Frame
# The associated visitor
var visitor: GlobalCompilerVisitor
var arguments: Array[RuntimeVariable]
# The runtime_variable associated to the return (in a function)
- var returnvar: nullable RuntimeVariable = null
+ var returnvar: nullable RuntimeVariable writable = null
# The label at the end of the property
- var returnlabel: nullable String = null
+ var returnlabel: nullable String writable = null
end
redef class MPropDef
redef class MMethodDef
# Can the body be inlined?
- private fun can_inline(v: GlobalCompilerVisitor): Bool
+ fun can_inline(v: GlobalCompilerVisitor): Bool
do
var modelbuilder = v.compiler.modelbuilder
if modelbuilder.mpropdef2npropdef.has_key(self) then
end
# Inline the body in another visitor
- private fun compile_inside_to_c(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
+ fun compile_inside_to_c(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
do
var modelbuilder = v.compiler.modelbuilder
if modelbuilder.mpropdef2npropdef.has_key(self) then
var npropdef = modelbuilder.mpropdef2npropdef[self]
+ var oldnode = v.current_node
+ v.current_node = npropdef
npropdef.compile_to_c(v, self, arguments)
+ v.current_node = oldnode
else if self.mproperty.name == "init" then
var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef]
+ var oldnode = v.current_node
+ v.current_node = nclassdef
nclassdef.compile_to_c(v, self, arguments)
+ v.current_node = oldnode
else
abort
end
end
redef class APropdef
- private fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
+ fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
do
v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
debug("Not yet implemented")
end
- private fun can_inline: Bool do return true
+ fun can_inline: Bool do return true
end
redef class AConcreteMethPropdef
end
end
end
-
v.stmt(self.n_block)
end
else if pname == "object_id" then
v.ret(arguments.first)
return
+ else if pname == "+" then
+ v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+ return
+ else if pname == "-" then
+ v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+ return
else if pname == "==" then
v.ret(v.equal_test(arguments[0], arguments[1]))
return
return
end
else if cname == "NativeArray" then
- var elttype = arguments.first.mtype
- if pname == "[]" then
- v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null)))
- return
- else if pname == "[]=" then
- v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};")
- return
- else if pname == "copy_to" then
- v.add("memcpy({arguments[1]},{arguments[0]},{arguments[2]}*sizeof({elttype.ctype}));")
- return
- end
+ v.native_array_def(pname, ret, arguments)
+ return
end
if pname == "exit" then
v.add("exit({arguments[1]});")
v.ret(v.new_expr("(char*)GC_MALLOC({arguments[1]})", ret.as(not null)))
return
else if pname == "calloc_array" then
- var elttype = arguments.first.mtype.supertype_to(v.compiler.mainmodule,arguments.first.mtype.as(MClassType),v.get_class("ArrayCapable")).as(MGenericType).arguments.first
- v.ret(v.new_expr("({elttype.ctype}*)GC_MALLOC({arguments[1]} * sizeof({elttype.ctype}))", ret.as(not null)))
+ v.calloc_array(ret.as(not null), arguments)
return
else if pname == "object_id" then
v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
return
else if pname == "is_same_type" then
- if arguments[0].mtype.ctype == "val*" then
- v.ret(v.new_expr("{arguments[0]}->classid == {arguments[1]}->classid", ret.as(not null)))
- else
- v.ret(v.new_expr("{v.compiler.classid(arguments[0].mtype.as(MClassType))} == {arguments[1]}->classid", ret.as(not null)))
- end
+ v.ret(v.is_same_type_test(arguments[0], arguments[1]))
return
else if pname == "output_class_name" then
- if arguments[0].mtype.ctype == "val*" then
- v.add("printf(\"%s\\n\", class_names[{arguments.first}->classid]);")
- else
- v.add("printf(\"%s\\n\", class_names[{v.compiler.classid(arguments.first.mtype.as(MClassType))}]);")
- end
+ var nat = v.class_name_string(arguments.first)
+ v.add("printf(\"%s\\n\", {nat});")
return
else if pname == "native_class_name" then
- if arguments[0].mtype.ctype == "val*" then
- v.ret(v.new_expr("(char*)(void*)class_names[{arguments.first}->classid]", ret.as(not null)))
- else
- v.ret(v.new_expr("(char*)(void*)class_names[{v.compiler.classid(arguments.first.mtype.as(MClassType))}]", ret.as(not null)))
- end
+ var nat = v.class_name_string(arguments.first)
+ v.ret(v.new_expr("(char*){nat}", ret.as(not null)))
return
end
- v.add("printf(\"NOT IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
+ v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");")
debug("Not implemented {mpropdef}")
end
end
ret = v.resolve_for(ret, arguments.first)
res = v.new_var(ret)
end
+ v.adapt_signature(mpropdef, arguments)
if res == null then
v.add("{externname}({arguments.join(", ")});")
var file = location.file.filename
v.compiler.add_extern(file)
end
+ v.adapt_signature(mpropdef, arguments)
var ret = arguments.first.mtype
var res = v.new_var(ret)
end
end
- private fun init_expr(v: GlobalCompilerVisitor, recv: RuntimeVariable)
+ fun init_expr(v: GlobalCompilerVisitor, recv: RuntimeVariable)
do
var nexpr = self.n_expr
if nexpr != null then
redef fun stmt(v)
do
var cl = v.expr(self.n_expr, null)
- var it = v.send(v.get_property("iterator", cl.mtype), [cl])
+ var it_meth = self.method_iterator
+ assert it_meth != null
+ var it = v.send(it_meth, [cl])
assert it != null
v.add("for(;;) \{")
- var ok = v.send(v.get_property("is_ok", it.mtype), [it])
+ var isok_meth = self.method_is_ok
+ assert isok_meth != null
+ var ok = v.send(isok_meth, [it])
assert ok != null
v.add("if(!{ok}) break;")
if self.variables.length == 1 then
- var i = v.send(v.get_property("item", it.mtype), [it])
+ var item_meth = self.method_item
+ assert item_meth != null
+ var i = v.send(item_meth, [it])
assert i != null
v.assign(v.variable(variables.first), i)
else if self.variables.length == 2 then
- var i = v.send(v.get_property("key", it.mtype), [it])
+ var key_meth = self.method_key
+ assert key_meth != null
+ var i = v.send(key_meth, [it])
assert i != null
v.assign(v.variable(variables[0]), i)
- i = v.send(v.get_property("item", it.mtype), [it])
+ var item_meth = self.method_item
+ assert item_meth != null
+ i = v.send(item_meth, [it])
assert i != null
v.assign(v.variable(variables[1]), i)
else
end
v.stmt(self.n_block)
v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
- v.send(v.get_property("next", it.mtype), [it])
+ var next_meth = self.method_next
+ assert next_meth != null
+ v.send(next_meth, [it])
v.add("\}")
v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
end
redef class AArrayExpr
redef fun expr(v)
do
- var mtype = self.mtype.as(MGenericType).arguments.first
+ var mtype = self.mtype.as(MClassType).arguments.first
var array = new Array[RuntimeVariable]
for nexpr in self.n_exprs.n_exprs do
var i = v.expr(nexpr, mtype)
# FIXME: we do not want an ugly static call!
var mpropdefs = mpropdef.mproperty.lookup_super_definitions(mpropdef.mclassdef.mmodule, mpropdef.mclassdef.bound_mtype)
if mpropdefs.length != 1 then
+ v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");")
debug("MPRODFEFS for super {mpropdef} for {recv}: {mpropdefs.join(", ")}")
end
mpropdef = mpropdefs.first
end
redef class AIssetAttrExpr
+ redef fun expr(v)
+ do
+ var recv = v.expr(self.n_expr, null)
+ var mproperty = self.mproperty.as(not null)
+ return v.isset_attribute(mproperty, recv)
+ end
end
redef class ADebugTypeExpr