nitj: compile java main function stub
[nit.git] / src / compiler / java_compiler.nit
index 8bee357..dfdff67 100644 (file)
@@ -209,6 +209,7 @@ class JavaCompiler
        fun do_compilation do
                # compile java classes used to represents the runtime model of the program
                rt_model.compile_rtmodel(self)
+               compile_box_kinds
 
                # compile class structures
                compile_mclasses_to_java
@@ -216,10 +217,25 @@ class JavaCompiler
                # compile method structures
                compile_mmethods_to_java
 
-               # TODO compile main
-               modelbuilder.toolcontext.info("NOT YET IMPLEMENTED", 0)
+               # compile main
+               compile_main_function
+       end
+
+       # Prepare the boxes used to represent Java primitive types
+       fun compile_box_kinds do
+               # Collect all bas box class
+               # FIXME: this is not completely fine with a separate compilation scheme
+               for classname in ["Int", "Bool", "Byte", "Char", "Float"] do
+                       var classes = mainmodule.model.get_mclasses_by_name(classname)
+                       if classes == null then continue
+                       assert classes.length == 1 else print classes.join(", ")
+                       box_kinds.add(classes.first.mclass_type)
+               end
        end
 
+       # Types of boxes used to represent Java primitive types
+       var box_kinds = new Array[MClassType]
+
        # Generate a `RTClass` for each `MClass` found in model
        #
        # This is a global phase because we need to know all the program to build
@@ -244,6 +260,17 @@ class JavaCompiler
                        end
                end
        end
+
+       # Generate Java main that call Sys.main
+       fun compile_main_function do
+               var v = new_visitor("{mainmodule.jname}_Main.java")
+               v.add("public class {mainmodule.jname}_Main \{")
+               v.add("  public static void main(String[] args) \{")
+               modelbuilder.toolcontext.info("NOT YET IMPLEMENTED", 0)
+               # TODO compile call to Sys::main
+               v.add("  \}")
+               v.add("\}")
+       end
 end
 
 # The class visiting the AST
@@ -261,6 +288,302 @@ 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)
+                       mtype = 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)
+               res.is_boxed = not mtype.is_java_primitive
+               add("{mtype.java_type} {name} /* : {mtype} */;")
+               return res
+       end
+
+       # Return a new uninitialized local RuntimeVariable
+       fun new_var(mtype: MType): RuntimeVariable do
+               mtype = 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
+
+       # Calls handling
+
+       # Compile a call within a callsite
+       fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
+               var initializers = callsite.mpropdef.initializers
+               if not initializers.is_empty then
+                       var recv = arguments.first
+
+                       var i = 1
+                       for p in initializers do
+                               if p isa MMethod then
+                                       var args = [recv]
+                                       var msignature = p.intro.msignature
+                                       if msignature != null then
+                                               for x in msignature.mparameters do
+                                                       args.add arguments[i]
+                                                       i += 1
+                                               end
+                                       end
+                                       send(p, args)
+                               else if p isa MAttribute then
+                                       info("NOT YET IMPLEMENTED {class_name}::compile_callsite for MAttribute `{p}`")
+                                       #self.write_attribute(p, recv, arguments[i])
+                                       i += 1
+                               else abort
+                       end
+                       assert i == arguments.length
+
+                       return send(callsite.mproperty, [recv])
+               end
+
+               return send(callsite.mproperty, arguments)
+       end
+
+       # 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, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable] do
+               var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+               var res = new Array[RuntimeVariable]
+               res.add(recv)
+
+               if msignature.arity == 0 then return res
+
+               if map == null then
+                       assert args.length == msignature.arity
+                       for ne in args do
+                               res.add expr(ne, null)
+                       end
+                       return res
+               end
+
+               # Eval in order of arguments, not parameters
+               var exprs = new Array[RuntimeVariable].with_capacity(args.length)
+               for ne in args do
+                       exprs.add expr(ne, null)
+               end
+
+               # Fill `res` with the result of the evaluation according to the mapping
+               for i in [0..msignature.arity[ do
+                       var param = msignature.mparameters[i]
+                       var j = map.map.get_or_null(i)
+                       if j == null then
+                               # default value
+                               res.add(null_instance)
+                               continue
+                       end
+                       if param.is_vararg and map.vararg_decl > 0 then
+                               var vararg = exprs.sub(j, map.vararg_decl)
+                               var elttype = param.mtype
+                               var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
+                               res.add(arg)
+                               continue
+                       end
+                       res.add exprs[j]
+               end
+               return res
+       end
+
+       #  Generate a static call on a method definition (no receiver needed).
+       fun static_call(mmethoddef: MMethodDef, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
+               var res: nullable RuntimeVariable
+               var ret = mmethoddef.msignature.as(not null).return_mtype
+               if ret == null then
+                       res = null
+               else
+                       ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
+                       res = self.new_var(ret)
+               end
+
+               # Autobox arguments
+               adapt_signature(mmethoddef, arguments)
+
+               var rt_name = mmethoddef.rt_name
+               if res == null then
+                       add("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});")
+                       return null
+               end
+               var ress = new_expr("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});", compiler.mainmodule.object_type)
+               assign(res, ress)
+               return res
+       end
+
+       #  Generate a polymorphic send for `method` with `arguments`
+       fun send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
+               # Shortcut calls on primitives
+               if arguments.first.mcasttype.is_java_primitive then
+                       return monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
+               end
+               # Polymorphic send
+               return table_send(mmethod, arguments)
+       end
+
+
+       # Handle common special cases before doing the effective method invocation
+       # This methods handle the `==` and `!=` methods and the case of the null receiver.
+       # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
+       # Client must not forget to close the } after them.
+       #
+       # The value returned is the result of the common special cases.
+       # If not null, client must compile it with the result of their own effective method invocation.
+       #
+       # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
+       # is generated to cancel the effective method invocation that will follow
+       # TODO: find a better approach
+       private fun before_send(res: nullable RuntimeVariable, mmethod: MMethodDef, arguments: Array[RuntimeVariable]) do
+               var bool_type = compiler.mainmodule.bool_type
+               var recv = arguments.first
+               var consider_null = mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance"
+               if recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType then
+                       add("if ({recv} == null || {recv}.is_null()) \{")
+                       if mmethod.name == "==" or mmethod.name == "is_same_instance" then
+                               if res == null then res = new_var(bool_type)
+                               var arg = arguments[1]
+                               if arg.mcasttype isa MNullableType then
+                                       add("{res} = ({arg} == null || {arg}.is_null());")
+                               else if arg.mcasttype isa MNullType then
+                                       add("{res} = true; /* is null */")
+                               else
+                                       add("{res} = false; /* {arg.inspect} cannot be null */")
+                               end
+                       else if mmethod.name == "!=" then
+                               if res == null then res = new_var(bool_type)
+                               # res = self.new_var(bool_type)
+                               var arg = arguments[1]
+                               if arg.mcasttype isa MNullableType then
+                                       add("{res} = ({arg} != null && !{arg}.is_null());")
+                               else if arg.mcasttype isa MNullType then
+                                       add("{res} = false; /* is null */")
+                               else
+                                       add("{res} = true; /* {arg.inspect} cannot be null */")
+                               end
+                       else
+                               add_abort("Receiver is null")
+                               ret(null_instance)
+                       end
+                       add("\} else \{")
+               else
+                       add "\{"
+                       add "/* recv ({recv}) cannot be null since it's a {recv.mcasttype}"
+               end
+               if consider_null then
+                       var arg = arguments[1]
+                       if arg.mcasttype isa MNullType then
+                               if res == null then res = new_var(bool_type)
+                               if mmethod.name == "!=" then
+                                       add("{res} = true; /* arg is null and recv is not */")
+                               else # `==` and `is_same_instance`
+                                       add("{res} = false; /* arg is null but recv is not */")
+                               end
+                               add("\}") # closes the null case
+                               add("if (false) \{") # what follow is useless, Javac will drop it
+                       end
+               end
+       end
+
+       # Perform a method call through vft
+       private fun table_send(mmethod: TableCallable, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
+               var mdef: MMethodDef
+               var name: String
+               if mmethod isa MMethod then
+                       mdef = mmethod.intro
+                       name = mmethod.full_name
+               else if mmethod isa MMethodDef then
+                       mdef = mmethod
+                       name = mmethod.full_name
+               else
+                       abort
+               end
+
+               var recv = arguments.first
+               var rect = mdef.mclassdef.bound_mtype
+               var msignature = mdef.msignature.as(not null)
+               msignature = msignature.resolve_for(rect, rect, compiler.mainmodule, true)
+               adapt_signature(mdef, arguments)
+
+               var res: nullable RuntimeVariable
+               var ret = msignature.return_mtype
+               if ret == null then
+                       res = null
+               else
+                       res = self.new_var(ret)
+               end
+
+               before_send(res, mdef, arguments)
+
+               add "/* concrete call to {mdef} */"
+               if res != null then
+                       var ress = new_expr("{recv}.rtclass.vft.get(\"{name}\").exec(new RTVal[]\{{arguments.join(",")}\});", compiler.mainmodule.object_type)
+                       assign(res, ress)
+               else
+                       add("{recv}.rtclass.vft.get(\"{name}\").exec(new RTVal[]\{{arguments.join(",")}\});")
+               end
+
+               add("\}") # closes the null case
+
+               return res
+       end
+
+       # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
+       fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable do
+               assert t isa MClassType
+               var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
+               return self.static_call(propdef, args)
+       end
+
        # Code generation
 
        # Add a line (will be suffixed by `\n`)
@@ -268,6 +591,339 @@ 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
+
+               if mtype != null then
+                       mtype = anchor(mtype)
+                       res = autobox(res, mtype)
+               end
+
+               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
+               add("{left} = {autobox(right, left.mtype)};")
+       end
+
+       # Generate a return with `value`
+       fun ret(value: RuntimeVariable) do
+               var frame = self.frame
+               assert frame != null
+               var returnvar = frame.returnvar
+               if returnvar != null then
+                       assign(returnvar, value)
+               end
+               self.add("break {frame.returnlabel.as(not null)};")
+       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
+
+       # Types handling
+
+       # Anchor a type to the main module and the current receiver
+       fun anchor(mtype: MType): MType do
+               if not mtype.need_anchor then return mtype
+               return mtype.anchor_to(compiler.mainmodule, frame.as(not null).receiver)
+       end
+
+       # Adapt the arguments of a method according to targetted `MMethodDef`
+       fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) do
+               var msignature = m.msignature.as(not null).resolve_for(
+                       m.mclassdef.bound_mtype,
+                       m.mclassdef.bound_mtype,
+                       m.mclassdef.mmodule, true)
+               args.first = autobox(args.first, compiler.mainmodule.object_type)
+               for i in [0..msignature.arity[ do
+                       args[i+1] = autobox(args[i + 1], compiler.mainmodule.object_type)
+               end
+       end
+
+       # Box primitive `value` to `mtype`.
+       private fun box(value: RuntimeVariable, mtype: MType): RuntimeVariable do
+               if value.is_boxed then return value
+               var obj_type = compiler.mainmodule.object_type
+               if value.mtype isa MNullType then
+                       return new_expr("new RTVal(null, null)", compiler.mainmodule.model.null_type)
+               end
+               var mbox = value.mtype.as(MClassType).mclass
+               return new_expr("new RTVal({mbox.rt_name}.get{mbox.rt_name}(), {value})", obj_type)
+       end
+
+       # Unbox primitive `value` to `mtype`.
+       private fun unbox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
+               if not value.is_boxed then return value
+               if not mtype.is_java_primitive then return value
+               if compiler.box_kinds.has(mtype) then
+                       return new_expr("({mtype.java_type}){value}.value", mtype)
+               else
+                       info "NOT YET IMPLEMENTED unbox for {value} ({mtype})"
+                       abort
+               end
+       end
+
+       # Box or unbox primitive `value` to `mtype` if needed.
+       private fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
+               if mtype.is_java_primitive then return unbox(value, mtype)
+               return box(value, mtype)
+       end
+
+       # Can this `value` be a primitive Java value?
+       private fun can_be_primitive(value: RuntimeVariable): Bool do
+               var t = value.mcasttype.undecorate
+               if not t isa MClassType then return false
+               var k = t.mclass.kind
+               return k == interface_kind or t.is_java_primitive
+       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
+
+       # Get an instance of a array for a vararg
+       fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable do
+               # TODO handle dynamic types
+               info("NOT YET IMPLEMENTED vararg_instance")
+               return null_instance
+               # TODO return array_instance(varargs, elttype)
+       end
+
+       # Nit instances
+
+       # Generate a alloc-instance + init-attributes
+       fun init_instance(mtype: MClassType): RuntimeVariable do
+               var rt_name = mtype.mclass.rt_name
+               var res = new_expr("new RTVal({rt_name}.get{rt_name}())", mtype)
+               generate_init_attr(self, res, mtype)
+               return res
+       end
+
+       # Generate code that initialize the attributes on a new instance
+       fun generate_init_attr(v: JavaCompilerVisitor, recv: RuntimeVariable, mtype: MClassType) do
+               var cds = mtype.collect_mclassdefs(v.compiler.mainmodule).to_a
+               v.compiler.mainmodule.linearize_mclassdefs(cds)
+               for cd in cds do
+                       for npropdef in v.compiler.modelbuilder.collect_attr_propdef(cd) do
+                               npropdef.init_expr(v, recv)
+                       end
+               end
+       end
+
+       #  Generate a Nit "is" for two runtime_variables
+       fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable do
+               var res = new_var(compiler.mainmodule.bool_type)
+               if value2.mtype.is_java_primitive and not value1.mtype.is_java_primitive then
+                       var tmp = value1
+                       value1 = value2
+                       value2 = tmp
+               end
+               if value1.mtype.is_java_primitive then
+                       if value2.mtype == value1.mtype then
+                               add("{res} = {value1} == {value2}; /* == with two primitives */")
+                       else if value2.mtype.is_java_primitive then
+                               add("{res} = true; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
+                       # else if value1.mtype.is_tagged then
+                               # add("{res} = ({value2} != NULL) && ({autobox(value2, value1.mtype)} == {value1});")
+                       else
+                               var rt_name = value1.mtype.as(MClassType).mclass.rt_name
+                               add("{res} = ({value2} != null) && ({value2}.rtclass == {rt_name}.get{rt_name}());")
+                               add("if ({res}) \{")
+                               add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
+                               add("\}")
+                       end
+                       return res
+               end
+               var maybe_null = true
+               var test = new Array[String]
+               var t1 = value1.mcasttype
+               if t1 isa MNullableType then
+                       test.add("{value1} != null && !{value1}.is_null()")
+                       t1 = t1.mtype
+               else
+                       maybe_null = false
+               end
+               var t2 = value2.mcasttype
+               if t2 isa MNullableType then
+                       test.add("{value2} != null && !{value2}.is_null()")
+                       t2 = t2.mtype
+               else
+                       maybe_null = false
+               end
+
+               var incompatible = false
+               var primitive
+               if t1.is_java_primitive then
+                       primitive = t1
+                       if t1 == t2 then
+                               # No need to compare class
+                       else if t2.is_java_primitive then
+                               incompatible = true
+                       else if can_be_primitive(value2) then
+                               if t1.is_java_primitive then
+                                       self.add("{res} = {value1} == {value2}; /* t1 is primitive and t2 can be */")
+                                       return res
+                               end
+                               # if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
+                                       # test.add("(!{extract_tag(value2)})")
+                               # end
+                               test.add("{value1}.rtclass == {value2}.rtclass")
+                       else
+                               incompatible = true
+                       end
+               else if t2.is_java_primitive then
+                       primitive = t2
+                       if can_be_primitive(value1) then
+                               if t2.is_java_primitive then
+                                       self.add("{res} = {value1} == {value2}; /* t2 is primitive and t1 can be */")
+                                       return res
+                               end
+                               test.add("{value1}.rtclass == {value2}.rtclass")
+                       else
+                               incompatible = true
+                       end
+               else
+                       primitive = null
+               end
+
+               if incompatible then
+                       if maybe_null then
+                               self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
+                               return res
+                       else
+                               self.add("{res} = false; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
+                               return res
+                       end
+               end
+               if primitive != null then
+                       if primitive.is_java_primitive then
+                               self.add("{res} = {value1} == {value2};")
+                               return res
+                       end
+                       test.add("({value1}.value == {value2}.value")
+               else if can_be_primitive(value1) and can_be_primitive(value2) then
+                       test.add("{value1}.rtclass == {value2}.rtclass")
+                       var s = new Array[String]
+                       for b in compiler.box_kinds do
+                               var rt_name = b.mclass.rt_name
+                               s.add "({value1}.rtclass == {rt_name}.get{rt_name}()) && ({value1}.value.equals({value2}.value))"
+                               if b.mclass.name == "Float" then
+                                       s.add "({value1}.rtclass == RTClass_kernel_Float.getRTClass_kernel_Float() && {value1}.rtclass == {value2}.rtclass && Math.abs((double)({value1}.value)) == 0.0 && Math.abs((double)({value2}.value)) == 0.0)"
+                               end
+                       end
+                       if s.is_empty then
+                               self.add("{res} = {value1} == {value2}; /* both can be primitive */")
+                               return res
+                       end
+                       test.add("({s.join(" || ")})")
+               else
+                       self.add("{res} = {value1} == {value2}; /* no primitives */")
+                       return res
+               end
+               self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
+               return res
+       end
+
+       # Attributes
+
+       # Generate a polymorphic attribute read
+       fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable do
+               # TODO check_recv_notnull(recv)
+               # TODO compile_check(v)
+               # What is the declared type of the attribute?
+               var ret = a.intro.static_mtype.as(not null)
+               var intromclassdef = a.intro.mclassdef
+               ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
+
+               return new_expr("{recv}.attrs.get(\"{a.jname}\")", ret)
+       end
+
+       # Generate a polymorphic attribute write
+       fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) do
+               # TODO check_recv_notnull(recv)
+               add "{recv}.attrs.put(\"{a.jname}\", {autobox(value, compiler.mainmodule.object_type)});"
+       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 +1016,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 +1130,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,9 +1141,55 @@ 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
+
+# Used as a common type between MMethod and MMethodDef for `table_send`
+private interface TableCallable
+end
+
+redef class MMethod
+       super TableCallable
 end
 
 redef class MMethodDef
+       super TableCallable
 
        # Runtime name
        private fun rt_name: String do
@@ -403,9 +1208,617 @@ 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
+                       v.add_abort("Abstract method `{mproperty.name}` called on `\"  + {selfvar}.rtclass.class_name +\"`")
+                       v.add("return null;")
+                       return
+               end
+
+               if node isa APropdef then
+                       node.compile_to_java(v, self)
+               else if node isa AClassdef then
+                       node.compile_to_java(v, self)
+               else
+                       abort
+               end
+       end
+end
+
+redef class AClassdef
+       private fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
+               if mpropdef == self.mfree_init then
+                       assert mpropdef.mproperty.is_root_init
+                       if not mpropdef.is_intro then
+                               # TODO v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
+                       end
+               else
+                       abort
+               end
+               v.add("return null;")
+       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")
+               v.add("return null;")
+       end
+end
+
+redef class AMethPropdef
+       redef fun compile_to_java(v, mpropdef) do
+               # TODO Call the implicit super-init
+
+               var recv = mpropdef.mclassdef.bound_mtype
+               var arguments = new Array[RuntimeVariable]
+               var frame = new JavaStaticFrame(v, mpropdef, recv, arguments)
+               v.frame = frame
+
+               var selfvar = v.decl_var("self", recv)
+               arguments.add(selfvar)
+               var boxed = v.new_expr("args[0];", v.compiler.mainmodule.object_type)
+               v.add "{selfvar} = {v.unbox(boxed, recv)};"
+
+               var msignature = mpropdef.msignature
+               var ret = null
+               if msignature != null then
+                       ret = msignature.return_mtype
+                       if ret != null then frame.returnvar = v.new_var(ret)
+               end
+               frame.returnlabel = v.get_name("RET_LABEL")
+
+               if not mpropdef.is_intern and msignature != null then
+                       var i = 0
+                       for mparam in msignature.mparameters do
+                               var variable = n_signature.as(not null).n_params[i].variable
+                               if variable == null then continue
+                               var argvar = v.variable(variable)
+                               boxed = v.new_expr("args[{i + 1}];", v.compiler.mainmodule.object_type)
+                               v.add "{argvar} = {v.unbox(boxed, mparam.mtype)};"
+                               arguments.add(argvar)
+                               i += 1
+                       end
+               end
+
+               v.add("{frame.returnlabel.as(not null)}: \{")
+               compile_inside_to_java(v, mpropdef)
+               v.add("\}")
+
+               if ret != null then
+                       if ret.is_java_primitive then
+                               boxed = v.box(frame.returnvar.as(not null), v.compiler.mainmodule.object_type)
+                               v.add("return {boxed};")
+                       else
+                               v.add("return {frame.returnvar.as(not null)};")
+                       end
+               else
+                       v.add("return null;")
+               end
+               v.frame = null
+       end
+
+       # Compile the inside of the method body
+       private fun compile_inside_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
+               # Compile intern methods
+               if mpropdef.is_intern then
+                       if compile_intern_to_java(v, mpropdef, arguments) then return
+                       v.info("NOT YET IMPLEMENTED compile_intern for {mpropdef}")
+                       v.ret(v.null_instance)
+                       return
+               end
+
+               # Compile block if any
+               var n_block = n_block
+               if n_block != null then
+                       v.stmt(n_block)
+                       return
+               end
+       end
+
+       # Compile an intern method using Java primitives
+       fun compile_intern_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do
+               var pname = mpropdef.mproperty.name
+               var cname = mpropdef.mclassdef.mclass.name
+               var ret = mpropdef.msignature.as(not null).return_mtype
+               if cname == "Int" then
+                       if pname == "output" then
+                               v.add("System.out.println({arguments[0]});")
+                               v.ret(v.null_instance)
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(arguments.first)
+                               return true
+                       else if pname == "+" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "-" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "%" then
+                               v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "lshift" then
+                               v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "rshift" then
+                               v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               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 true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(byte){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "ascii" then
+                               v.ret(v.new_expr("(char){arguments[0]}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "Char" then
+                       if pname == "output" then
+                               v.add("System.out.print({arguments[0]});")
+                               v.ret(v.null_instance)
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "successor" then
+                               v.ret(v.new_expr("(char)({arguments[0]} + {arguments[1]})", ret.as(not null)))
+                               return true
+                       else if pname == "predecessor" then
+                               v.ret(v.new_expr("(char)({arguments[0]} - {arguments[1]})", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               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 true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "ascii" then
+                               v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "Byte" then
+                       if pname == "output" then
+                               v.add("System.out.println({arguments[0]});")
+                               v.ret(v.null_instance)
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "+" then
+                               v.ret(v.new_expr("(byte)({arguments[0]} + {arguments[1]})", ret.as(not null)))
+                               return true
+                       else if pname == "-" then
+                               v.ret(v.new_expr("(byte)({arguments[0]} - {arguments[1]})", ret.as(not null)))
+                               return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("(byte)(-{arguments[0]})", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("(byte)({arguments[0]} * {arguments[1]})", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("(byte)({arguments[0]} / {arguments[1]})", ret.as(not null)))
+                               return true
+                       else if pname == "%" then
+                               v.ret(v.new_expr("(byte)({arguments[0]} % {arguments[1]})", ret.as(not null)))
+                               return true
+                       else if pname == "lshift" then
+                               v.ret(v.new_expr("(byte)({arguments[0]} << {arguments[1]})", ret.as(not null)))
+                               return true
+                       else if pname == "rshift" then
+                               v.ret(v.new_expr("(byte)({arguments[0]} >> {arguments[1]})", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               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 true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_f" then
+                               v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "ascii" then
+                               v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
+                               return true
+                       end
+               else if cname == "Bool" then
+                       if pname == "output" then
+                               v.add("System.out.println({arguments[0]});")
+                               v.ret(v.null_instance)
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("{arguments[0]}?1:0", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               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 true
+                       end
+               else if cname == "Float" then
+                       if pname == "output" then
+                               v.add "if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
+                               v.add "System.out.println(\"inf\");"
+                               v.add "\} else if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
+                               v.add "System.out.println(\"-inf\");"
+                               v.add "\} else \{"
+                               var df = v.get_name("df")
+                               v.add "java.text.DecimalFormat {df} = new java.text.DecimalFormat(\"0.000000\");"
+                               v.add "System.out.println({df}.format({arguments[0]}));"
+                               v.add "\}"
+                               v.ret(v.null_instance)
+                               return true
+                       else if pname == "object_id" then
+                               v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "+" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "-" then
+                               v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary -" then
+                               v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
+                       else if pname == "succ" then
+                               v.ret(v.new_expr("{arguments[0]} + 1", ret.as(not null)))
+                               return true
+                       else if pname == "prec" then
+                               v.ret(v.new_expr("{arguments[0]} - 1", ret.as(not null)))
+                               return true
+                       else if pname == "*" then
+                               v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "/" then
+                               v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "==" then
+                               v.ret(v.equal_test(arguments[0], arguments[1]))
+                               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 true
+                       else if pname == "<" then
+                               v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">" then
+                               v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "<=" then
+                               v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == ">=" then
+                               v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_i" then
+                               v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
+                               return true
+                       else if pname == "to_b" then
+                               v.ret(v.new_expr("(byte){arguments[0]}", ret.as(not null)))
+                               return true
+                       end
+               end
+               if pname == "exit" then
+                       v.add("System.exit({arguments[1]});")
+                       v.ret(v.null_instance)
+                       return true
+               else if pname == "sys" then
+                       # TODO singleton
+                       var main_type = v.compiler.mainmodule.sys_type.as(not null)
+                       var sys = main_type.mclass
+                       v.ret(v.new_expr("new RTVal({sys.rt_name}.get{sys.rt_name}())", main_type))
+                       return true
+               else if pname == "object_id" then
+                       v.ret(v.new_expr("{arguments[0]}.hashCode()", ret.as(not null)))
+                       return true
+               else if pname == "is_same_type" then
+                       v.ret(v.is_same_type_test(arguments[0], arguments[1]))
+                       return true
+               else if pname == "is_same_instance" then
+                       v.ret(v.equal_test(arguments[0], arguments[1]))
+                       return true
+               else if pname == "output_class_name" then
+                       v.add("System.out.println({arguments[0]}.rtclass.class_name);")
+                       v.ret(v.null_instance)
+                       return true
+               end
+               return false
+       end
+end
+
+redef class AAttrPropdef
+       redef fun compile_to_java(v, mpropdef, arguments) do
+               v.current_node = self
+               if mpropdef == mreadpropdef then
+                       compile_getter(v, mpropdef, arguments)
+               else if mpropdef == mwritepropdef then
+                       compile_setter(v, mpropdef, arguments)
+               else
+                       abort
+               end
+               v.current_node = null
+       end
+
+       # Compile the setter method
+       private fun compile_setter(v: JavaCompilerVisitor, mpropdef: MPropDef, arguments: Array[RuntimeVariable]) do
+               var mtype = v.compiler.mainmodule.object_type
+               var recv = arguments.first
+               var val = v.new_expr("args[1]", mtype)
+               v.write_attribute(self.mpropdef.as(not null).mproperty, recv, val)
+               v.ret v.null_instance
+       end
+
+       # Compile the getter method
+       private fun compile_getter(v: JavaCompilerVisitor, mpropdef: MPropDef, arguments: Array[RuntimeVariable]) do
+               var recv = arguments.first
+               v.ret v.read_attribute(self.mpropdef.as(not null).mproperty, recv)
+       end
+
+       private fun init_expr(v: JavaCompilerVisitor, recv: RuntimeVariable) do
+               if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
+       end
+
+       # Evaluate, store and return the default value of the attribute
+       private fun evaluate_expr(v: JavaCompilerVisitor, recv: RuntimeVariable): RuntimeVariable do
+               var old = v.frame
+               var frame = new JavaStaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
+               v.frame = frame
+
+               var value
+               var mtype = self.mtype
+               assert mtype != null
+
+               var nexpr = self.n_expr
+               var nblock = self.n_block
+               if nexpr != null then
+                       value = v.expr(nexpr, mtype)
+               else if nblock != null then
+                       value = v.new_var(mtype)
+                       frame.returnvar = value
+                       frame.returnlabel = v.get_name("RET_LABEL")
+                       v.add("\{")
+                       v.stmt(nblock)
+                       v.add("{frame.returnlabel.as(not null)}:(void)0;")
+                       v.add("\}")
+               else
+                       abort
+               end
+
+               v.write_attribute(self.mpropdef.as(not null).mproperty, recv, value)
+               v.frame = old
+               return value
+       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 ASendExpr
+       redef fun expr(v) do
+               var recv = v.expr(n_expr, null)
+               var callsite = callsite.as(not null)
+               var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, raw_arguments)
+               return v.compile_callsite(callsite, args)
+       end
+end
+
+redef class ANewExpr
+       redef fun expr(v)
+       do
+               var mtype = self.recvtype
+               assert mtype != null
+
+               if mtype.mclass.name == "NativeArray" then
+                       # TODO handle native arrays
+                       v.info("NOT YET IMPLEMENTED new NativeArray")
+               end
+
+               var recv = v.init_instance(mtype)
+
+               var callsite = self.callsite
+               if callsite == null then return recv
+
+               var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
+               var res2 = v.compile_callsite(callsite, args)
+               if res2 != null then
+                       return res2
+               end
+               return recv
+       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 AReturnExpr
+       redef fun stmt(v) do
+               var nexpr = self.n_expr
+               var frame = v.frame
+               assert frame != null
+               if nexpr != null then
+                       v.ret(v.expr(nexpr, frame.returnvar.as(not null).mtype))
+               else
+                       v.ret(v.null_instance)
+               end
+       end
+end
+
+redef class AVardeclExpr
+       redef fun stmt(v) do
+               var variable = self.variable.as(not null)
+               var ne = self.n_expr
+               var decl = v.variable(variable)
+               if ne != null then
+                       var i = v.expr(ne, variable.declared_type)
+                       v.assign(decl, i)
+               end
+       end
+end
+
+redef class AVarExpr
+       redef fun expr(v) do
+               return v.variable(self.variable.as(not null))
+       end
+end
+
+redef class AVarAssignExpr
+       redef fun expr(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)
+               return i
+       end
+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