nitj: compile native instances of Int, Byte, Float, Char and Bool
[nit.git] / src / compiler / java_compiler.nit
index 8bee357..7a7bf1e 100644 (file)
@@ -261,6 +261,74 @@ class JavaCompilerVisitor
        # 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`)
@@ -268,6 +336,104 @@ class JavaCompilerVisitor
 
        # 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.
@@ -360,6 +526,109 @@ class JavaRuntimeModel
        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
@@ -371,8 +640,8 @@ redef class MClass
                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) \{")
@@ -382,6 +651,43 @@ redef class MClass
                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
@@ -403,9 +709,134 @@ 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