# The file to write generated code into
var file: JavaCodeFile
+ # Names handling
+
+ private var names = new HashSet[String]
+ private var last: Int = 0
+
+ # Return a new name based on `s` and unique in the visitor
+ fun get_name(s: String): String do
+ if not self.names.has(s) then
+ self.names.add(s)
+ return s
+ end
+ var i = self.last + 1
+ loop
+ var s2 = s + i.to_s
+ if not self.names.has(s2) then
+ self.last = i
+ self.names.add(s2)
+ return s2
+ end
+ i = i + 1
+ end
+ end
+
+ # Variables handling
+
+ # Registered variables
+ protected var variables = new HashMap[Variable, RuntimeVariable]
+
+ # Return the local RuntimeVariable associated to a Nit local variable
+ fun variable(variable: Variable): RuntimeVariable do
+ if variables.has_key(variable) then
+ return variables[variable]
+ else
+ var name = get_name("var_{variable.name}")
+ var mtype = variable.declared_type.as(not null)
+ # TODO mtype = self.anchor(mtype)
+ var res = decl_var(name, mtype)
+ variables[variable] = res
+ return res
+ end
+ end
+
+ # Return a new uninitialized local RuntimeVariable with `name`
+ fun decl_var(name: String, mtype: MType): RuntimeVariable do
+ var res = new RuntimeVariable(name, mtype, mtype)
+ add("{mtype.java_type} {name} /* : {mtype} */;")
+ return res
+ end
+
+ # Return a new uninitialized local RuntimeVariable
+ fun new_var(mtype: MType): RuntimeVariable do
+ # TODO mtype = self.anchor(mtype)
+ var name = self.get_name("var")
+ return decl_var(name, mtype)
+ end
+
+ # Calls handling
+
+ # The current `JavaStaticFrame`
+ var frame: nullable JavaStaticFrame = null is writable
+
+ # Return a new local RuntimeVariable initialized from `args[0]`
+ fun new_recv(mtype: MType): RuntimeVariable do
+ var res = new_var(mtype)
+ add("{res} = args[0];")
+ return res
+ end
+
# Code generation
# Add a line (will be suffixed by `\n`)
# Add a new partial line (no `\n` suffix)
fun addn(line: String) do file.lines.add(line)
+
+ # Compile a statement (if any)
+ fun stmt(nexpr: nullable AExpr) do
+ if nexpr == null then return
+ var old = self.current_node
+ current_node = nexpr
+ nexpr.stmt(self)
+ current_node = old
+ end
+
+ # Compile an expression an return its result
+ # `mtype` is the expected return type, pass null if no specific type is expected.
+ fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable do
+ var old = current_node
+ current_node = nexpr
+
+ var res = null
+ if nexpr.mtype != null then
+ res = nexpr.expr(self)
+ end
+ assert res != null
+ current_node = old
+ return res
+ end
+
+ # Correctly assign a left and a right value
+ # Boxing and unboxing is performed if required
+ fun assign(left, right: RuntimeVariable) do
+ # TODO right = autobox(right, left.mtype)
+ add("{left} = {right};")
+ end
+
+ # Return a new local RuntimeVariable initialized with the Java expression `jexpr`.
+ #
+ # `mtype` is used for the Java return variable initialization.
+ fun new_expr(jexpr: String, mtype: MType): RuntimeVariable do
+ var res = new_var(mtype)
+ add("{res} = {jexpr};")
+ return res
+ end
+
+ # Generate generic abort
+ #
+ # Used by aborts, asserts, casts, etc.
+ fun add_abort(message: String) do
+ add("System.err.print(\"Runtime error: {message}\");")
+ var node = current_node
+ if node != null then
+ add("System.err.print(\" ({node.location.short_location})\");")
+ end
+ add("System.err.println(\"\");")
+ add("System.exit(1);")
+ end
+
+ # Native instances
+
+ # Generate an integer value
+ fun int_instance(value: Int): RuntimeVariable do
+ var t = compiler.mainmodule.int_type
+ return new RuntimeVariable(value.to_s, t, t)
+ end
+
+ # Generate a byte value
+ fun byte_instance(value: Byte): RuntimeVariable do
+ var t = compiler.mainmodule.byte_type
+ return new RuntimeVariable(value.to_s, t, t)
+ end
+
+ # Generate a char value
+ fun char_instance(value: Char): RuntimeVariable do
+ var t = compiler.mainmodule.char_type
+ return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
+ end
+
+ # Generate a float value
+ #
+ # FIXME pass a Float, not a string
+ fun float_instance(value: String): RuntimeVariable do
+ var t = compiler.mainmodule.float_type
+ return new RuntimeVariable(value.to_s, t, t)
+ end
+
+ # Generate an integer value
+ fun bool_instance(value: Bool): RuntimeVariable do
+ var t = compiler.mainmodule.bool_type
+ return new RuntimeVariable(value.to_s, t, t)
+ end
+
+ # Generate the `null` value
+ fun null_instance: RuntimeVariable do
+ var t = compiler.mainmodule.model.null_type
+ return new RuntimeVariable("null", t, t)
+ end
+
+ # Utils
+
+ # Display a info message
+ fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
end
# A file containing Java code.
end
end
+# A runtime variable hold a runtime value in Java.
+# Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
+class RuntimeVariable
+
+ # The name of the variable in the Java code
+ var name: String
+
+ # The static type of the variable (as declard in Java)
+ var mtype: MType
+
+ # The current casted type of the variable (as known in Nit)
+ 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 = false is writable
+
+ # Is this variable declared as a RTVal or a Java primitive one?
+ var is_boxed = false
+
+ redef fun to_s do return name
+
+ redef fun inspect
+ do
+ var exact_str
+ if self.is_exact then
+ exact_str = " exact"
+ else
+ exact_str = ""
+ end
+ var type_str
+ if self.mtype == self.mcasttype then
+ type_str = "{mtype}{exact_str}"
+ else
+ type_str = "{mtype}({mcasttype}{exact_str})"
+ end
+ return "<{name}:{type_str}>"
+ end
+end
+
+# The static context of a visited property in a `JavaCompilerVisitor`
+class JavaStaticFrame
+ # The associated visitor
+ var visitor: JavaCompilerVisitor
+
+ # The executed property.
+ # A Method in case of a call, an attribute in case of a default initialization.
+ var mpropdef: MPropDef
+
+ # The static type of the receiver
+ var receiver: MClassType
+
+ # Arguments of the method (the first is the receiver)
+ var arguments: Array[RuntimeVariable]
+
+ # The runtime_variable associated to the return (in a function)
+ var returnvar: nullable RuntimeVariable = null is writable
+
+ # The label at the end of the property
+ var returnlabel: nullable String = null is writable
+end
+
+redef class Location
+ # Return a shortened version of the location with `"{file}:{line_start}"`
+ fun short_location: String do
+ var file = self.file
+ if file == null then return "<no file>:{line_start}"
+ return "{file.filename.escape_to_c}:{line_start}"
+ end
+end
+
+redef class MType
+ # Return the Java type associated to a given Nit static type
+ fun java_type: String do return "RTVal"
+
+ # Is the associated Java type a primitive one?
+ #
+ # ENSURE `result == (java_type != "Object")`
+ var is_java_primitive: Bool is lazy do return java_type != "RTVal"
+end
+
+redef class MClassType
+
+ redef var java_type is lazy do
+ if mclass.name == "Int" then
+ return "int"
+ else if mclass.name == "Bool" then
+ return "boolean"
+ else if mclass.name == "Char" then
+ return "char"
+ else if mclass.name == "Float" then
+ return "double"
+ else if mclass.name == "Byte" then
+ return "byte"
+ else if mclass.name == "NativeString" then
+ return "String"
+ else if mclass.name == "NativeArray" then
+ return "Array"
+ end
+ return "RTVal"
+ end
+end
+
redef class MClass
# Runtime name
v.add(" protected static RTClass instance;")
v.add(" private {rt_name}() \{")
v.add(" this.class_name = \"{name}\";")
- # TODO compile_vft(v)
- # TODO compile_type_table(v)
+ compile_vft(v)
+ compile_type_table(v)
v.add(" \}")
v.add(" public static RTClass get{rt_name}() \{")
v.add(" if(instance == null) \{")
v.add(" \}")
v.add("\}")
end
+
+ # Compile the virtual function table for the mclass
+ private fun compile_vft(v: JavaCompilerVisitor) do
+ # TODO handle generics
+ if mclass_type.need_anchor then return
+ var mclassdefs = mclass_type.collect_mclassdefs(v.compiler.mainmodule).to_a
+ v.compiler.mainmodule.linearize_mclassdefs(mclassdefs)
+
+ var mainmodule = v.compiler.mainmodule
+ for mclassdef in mclassdefs.reversed do
+ for mprop in mclassdef.intro_mproperties do
+ var mpropdef = mprop.lookup_first_definition(mainmodule, intro.bound_mtype)
+ if not mpropdef isa MMethodDef then continue
+ var rt_name = mpropdef.rt_name
+ v.add("this.vft.put(\"{mprop.full_name}\", {rt_name}.get{rt_name}());")
+
+ # fill super next definitions
+ while mpropdef.has_supercall do
+ var prefix = mpropdef.full_name
+ mpropdef = mpropdef.lookup_next_definition(mainmodule, intro.bound_mtype)
+ rt_name = mpropdef.rt_name
+ v.add("this.vft.put(\"{prefix}\", {rt_name}.get{rt_name}());")
+ end
+ end
+ end
+ end
+
+ # Compile the type table for the MClass
+ fun compile_type_table(v: JavaCompilerVisitor) do
+ for pclass in in_hierarchy(v.compiler.mainmodule).greaters do
+ if pclass == self then
+ v.add("supers.put(\"{pclass.jname}\", this);")
+ else
+ v.add("supers.put(\"{pclass.jname}\", {pclass.rt_name}.get{pclass.rt_name}());")
+ end
+ end
+ end
end
redef class MMethodDef
v.add(" \}")
v.add(" @Override")
v.add(" public RTVal exec(RTVal[] args) \{")
- # TODO compile_inside_to_java(v)
- v.add(" return null;")
+ compile_inside_to_java(v)
v.add(" \}")
v.add("\}")
end
+
+ # Compile the body of this function
+ fun compile_inside_to_java(v: JavaCompilerVisitor) do
+
+ var modelbuilder = v.compiler.modelbuilder
+ var node = modelbuilder.mpropdef2node(self)
+
+ if is_abstract then
+ # TODO compile abstract
+ v.info("NOT YET IMPLEMENTED call to abstract method")
+ v.add("return null;")
+ return
+ end
+
+ if node isa APropdef then
+ node.compile_to_java(v, self)
+ else if node isa AClassdef then
+ # TODO compile attributes
+ v.info("NOT YET IMPLEMENTED attribute handling")
+ v.add("return null;")
+ else
+ abort
+ end
+ end
+end
+
+redef class APropdef
+
+ # Compile that property definition to java code
+ fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
+ v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
+ end
+end
+
+redef class AMethPropdef
+ redef fun compile_to_java(v, mpropdef) do
+ # TODO Call the implicit super-init
+
+ # Compile intern methods
+ if mpropdef.is_intern then
+ v.info("NOT YET IMPLEMENTED {class_name}::compile_intern")
+ # TODO if compile_intern_to_java(v, mpropdef, arguments) then return
+ v.add("return null;")
+ return
+ end
+
+ # Compile block if any
+ var n_block = n_block
+ if n_block != null then
+ v.stmt(n_block)
+ return
+ end
+ end
+end
+
+redef class AExpr
+ # Try to compile self as an expression
+ # Do not call this method directly, use `v.expr` instead
+ private fun expr(v: JavaCompilerVisitor): nullable RuntimeVariable do
+ v.info("NOT YET IMPLEMENTED {class_name}::expr")
+ return null
+ end
+
+ # Try to compile self as a statement
+ # Do not call this method directly, use `v.stmt` instead
+ private fun stmt(v: JavaCompilerVisitor) do expr(v)
+end
+
+redef class ABlockExpr
+ redef fun stmt(v)
+ do
+ for e in self.n_expr do v.stmt(e)
+ end
+ redef fun expr(v)
+ do
+ var last = self.n_expr.last
+ for e in self.n_expr do
+ if e == last then break
+ v.stmt(e)
+ end
+ return v.expr(last, null)
+ end
+end
+
+redef class ASelfExpr
+ redef fun expr(v) do return v.frame.as(not null).arguments.first
+end
+
+redef class AImplicitSelfExpr
+ redef fun expr(v) do return v.frame.as(not null).arguments.first
+end
+
+redef class AIntExpr
+ redef fun expr(v) do return v.int_instance(self.value.as(not null))
+end
+
+redef class AByteExpr
+ redef fun expr(v) do return v.byte_instance(self.value.as(not null))
+end
+
+redef class AFloatExpr
+ 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.char_instance(self.value.as(not null))
+end
+
+redef class ATrueExpr
+ redef fun expr(v) do return v.bool_instance(true)
+end
+
+redef class AFalseExpr
+ redef fun expr(v) do return v.bool_instance(false)
+end
+
+redef class ANullExpr
+ redef fun expr(v) do return v.null_instance
+end
+
+redef class AAbortExpr
+ redef fun stmt(v) do v.add_abort("Aborted")
+end
+
+redef class ADebugTypeExpr
+ redef fun stmt(v) do end # do nothing
end