Compile Nit code to Java code

3 runtime structures are used to represent Nit instance in Java generated code:

  • RTClass to represent a class, it's super-type table and its VFT
  • RTMethod to reprensent a compiled method definition
  • RTVal to reprensent a Nit instance, the null value or a native value

More details are given in the documentation of these 3 classes.

TODO Factorize with abstract_compiler

Introduced classes

class JavaCodeFile

nitc :: JavaCodeFile

A file containing Java code.
class JavaCompiler

nitc :: JavaCompiler

Compiler that translates Nit code to Java code
class JavaCompilerVisitor

nitc :: JavaCompilerVisitor

The class visiting the AST
class JavaRuntimeModel

nitc :: JavaRuntimeModel

Handler for runtime classes generation
class JavaStaticFrame

nitc :: JavaStaticFrame

The static context of a visited property in a JavaCompilerVisitor
class RuntimeVariable

nitc :: RuntimeVariable

A runtime variable hold a runtime value in Java.
private interface TableCallable

nitc :: TableCallable

Used as a common type between MMethod and MMethodDef for table_send

Redefined classes

redef class AAbortExpr

nitc :: java_compiler $ AAbortExpr

An abort statement
redef class AAndExpr

nitc :: java_compiler $ AAndExpr

A and expression
redef class AAsCastExpr

nitc :: java_compiler $ AAsCastExpr

A type cast. eg x.as(T)
redef class AAsNotnullExpr

nitc :: java_compiler $ AAsNotnullExpr

A as-not-null cast. eg x.as(not null)
redef class AAssertExpr

nitc :: java_compiler $ AAssertExpr

An assert statement
redef class AAttrAssignExpr

nitc :: java_compiler $ AAttrAssignExpr

The assignment of an attribute. eg x._a=y
redef class AAttrExpr

nitc :: java_compiler $ AAttrExpr

The read of an attribute. eg x._a
redef class AAttrPropdef

nitc :: java_compiler $ AAttrPropdef

A definition of an attribute
redef class AAttrReassignExpr

nitc :: java_compiler $ AAttrReassignExpr

A complex attribute assignment. eg x._a+=y
redef class ABlockExpr

nitc :: java_compiler $ ABlockExpr

A sequence of AExpr (usually statements)
redef class ACharExpr

nitc :: java_compiler $ ACharExpr

A character literal
redef abstract class AClassdef

nitc :: java_compiler $ AClassdef

A class definition
redef class ADebugTypeExpr

nitc :: java_compiler $ ADebugTypeExpr

A special expression to debug types
redef class ADoExpr

nitc :: java_compiler $ ADoExpr

A do statement
redef abstract class AEscapeExpr

nitc :: java_compiler $ AEscapeExpr

A break or a continue
redef abstract class AExpr

nitc :: java_compiler $ AExpr

Expression and statements
redef class AFalseExpr

nitc :: java_compiler $ AFalseExpr

A false boolean literal constant
redef class AFloatExpr

nitc :: java_compiler $ AFloatExpr

A float literal
redef class AIfExpr

nitc :: java_compiler $ AIfExpr

A if statement
redef class AImplicitSelfExpr

nitc :: java_compiler $ AImplicitSelfExpr

When there is no explicit receiver, self is implicit
redef class AImpliesExpr

nitc :: java_compiler $ AImpliesExpr

A implies expression
redef class AIntegerExpr

nitc :: java_compiler $ AIntegerExpr

An integer literal
redef class AIsaExpr

nitc :: java_compiler $ AIsaExpr

A type-ckeck expression. eg x isa T
redef class AIssetAttrExpr

nitc :: java_compiler $ AIssetAttrExpr

A is-set check of old-style attributes. eg isset x._a
redef class ALoopExpr

nitc :: java_compiler $ ALoopExpr

A loop statement
redef class AMethPropdef

nitc :: java_compiler $ AMethPropdef

A definition of all kind of method (including constructors)
redef class ANewExpr

nitc :: java_compiler $ ANewExpr

An explicit instantiation. eg new T
redef class ANotExpr

nitc :: java_compiler $ ANotExpr

A not expression
redef class ANullExpr

nitc :: java_compiler $ ANullExpr

A null literal constant
redef class AOrElseExpr

nitc :: java_compiler $ AOrElseExpr

A or else expression
redef class AOrExpr

nitc :: java_compiler $ AOrExpr

A or expression
redef class AParExpr

nitc :: java_compiler $ AParExpr

A simple parenthesis. eg (x)
redef abstract class APropdef

nitc :: java_compiler $ APropdef

The definition of a property
redef class AReturnExpr

nitc :: java_compiler $ AReturnExpr

A return statement. eg return x
redef class ASelfExpr

nitc :: java_compiler $ ASelfExpr

A read of self
redef abstract class ASendExpr

nitc :: java_compiler $ ASendExpr

A polymorphic invocation of a method
redef class ASuperExpr

nitc :: java_compiler $ ASuperExpr

A call to super. OR a call of a super-constructor
redef class ATrueExpr

nitc :: java_compiler $ ATrueExpr

A true boolean literal constant
redef class AVarAssignExpr

nitc :: java_compiler $ AVarAssignExpr

A local variable simple assignment access
redef class AVarExpr

nitc :: java_compiler $ AVarExpr

A local variable read access.
redef class AVardeclExpr

nitc :: java_compiler $ AVardeclExpr

A declaration of a local variable. eg var x: X = y
redef class AWhileExpr

nitc :: java_compiler $ AWhileExpr

A while statement
redef class Location

nitc :: java_compiler $ Location

A location inside a source file
redef class MClass

nitc :: java_compiler $ MClass

A named class
redef class MClassType

nitc :: java_compiler $ MClassType

A type based on a class.
redef abstract class MEntity

nitc :: java_compiler $ MEntity

A named and possibly documented entity in the model.
redef class MMethod

nitc :: java_compiler $ MMethod

A global method
redef class MMethodDef

nitc :: java_compiler $ MMethodDef

A local definition of a method
redef abstract class MType

nitc :: java_compiler $ MType

A global static type
redef class ModelBuilder

nitc :: java_compiler $ ModelBuilder

A model builder knows how to load nit source files and build the associated model
redef class ToolContext

nitc :: java_compiler $ ToolContext

Global context for tools

All class definitions

redef class AAbortExpr

nitc :: java_compiler $ AAbortExpr

An abort statement
redef class AAndExpr

nitc :: java_compiler $ AAndExpr

A and expression
redef class AAsCastExpr

nitc :: java_compiler $ AAsCastExpr

A type cast. eg x.as(T)
redef class AAsNotnullExpr

nitc :: java_compiler $ AAsNotnullExpr

A as-not-null cast. eg x.as(not null)
redef class AAssertExpr

nitc :: java_compiler $ AAssertExpr

An assert statement
redef class AAttrAssignExpr

nitc :: java_compiler $ AAttrAssignExpr

The assignment of an attribute. eg x._a=y
redef class AAttrExpr

nitc :: java_compiler $ AAttrExpr

The read of an attribute. eg x._a
redef class AAttrPropdef

nitc :: java_compiler $ AAttrPropdef

A definition of an attribute
redef class AAttrReassignExpr

nitc :: java_compiler $ AAttrReassignExpr

A complex attribute assignment. eg x._a+=y
redef class ABlockExpr

nitc :: java_compiler $ ABlockExpr

A sequence of AExpr (usually statements)
redef class ACharExpr

nitc :: java_compiler $ ACharExpr

A character literal
redef abstract class AClassdef

nitc :: java_compiler $ AClassdef

A class definition
redef class ADebugTypeExpr

nitc :: java_compiler $ ADebugTypeExpr

A special expression to debug types
redef class ADoExpr

nitc :: java_compiler $ ADoExpr

A do statement
redef abstract class AEscapeExpr

nitc :: java_compiler $ AEscapeExpr

A break or a continue
redef abstract class AExpr

nitc :: java_compiler $ AExpr

Expression and statements
redef class AFalseExpr

nitc :: java_compiler $ AFalseExpr

A false boolean literal constant
redef class AFloatExpr

nitc :: java_compiler $ AFloatExpr

A float literal
redef class AIfExpr

nitc :: java_compiler $ AIfExpr

A if statement
redef class AImplicitSelfExpr

nitc :: java_compiler $ AImplicitSelfExpr

When there is no explicit receiver, self is implicit
redef class AImpliesExpr

nitc :: java_compiler $ AImpliesExpr

A implies expression
redef class AIntegerExpr

nitc :: java_compiler $ AIntegerExpr

An integer literal
redef class AIsaExpr

nitc :: java_compiler $ AIsaExpr

A type-ckeck expression. eg x isa T
redef class AIssetAttrExpr

nitc :: java_compiler $ AIssetAttrExpr

A is-set check of old-style attributes. eg isset x._a
redef class ALoopExpr

nitc :: java_compiler $ ALoopExpr

A loop statement
redef class AMethPropdef

nitc :: java_compiler $ AMethPropdef

A definition of all kind of method (including constructors)
redef class ANewExpr

nitc :: java_compiler $ ANewExpr

An explicit instantiation. eg new T
redef class ANotExpr

nitc :: java_compiler $ ANotExpr

A not expression
redef class ANullExpr

nitc :: java_compiler $ ANullExpr

A null literal constant
redef class AOrElseExpr

nitc :: java_compiler $ AOrElseExpr

A or else expression
redef class AOrExpr

nitc :: java_compiler $ AOrExpr

A or expression
redef class AParExpr

nitc :: java_compiler $ AParExpr

A simple parenthesis. eg (x)
redef abstract class APropdef

nitc :: java_compiler $ APropdef

The definition of a property
redef class AReturnExpr

nitc :: java_compiler $ AReturnExpr

A return statement. eg return x
redef class ASelfExpr

nitc :: java_compiler $ ASelfExpr

A read of self
redef abstract class ASendExpr

nitc :: java_compiler $ ASendExpr

A polymorphic invocation of a method
redef class ASuperExpr

nitc :: java_compiler $ ASuperExpr

A call to super. OR a call of a super-constructor
redef class ATrueExpr

nitc :: java_compiler $ ATrueExpr

A true boolean literal constant
redef class AVarAssignExpr

nitc :: java_compiler $ AVarAssignExpr

A local variable simple assignment access
redef class AVarExpr

nitc :: java_compiler $ AVarExpr

A local variable read access.
redef class AVardeclExpr

nitc :: java_compiler $ AVardeclExpr

A declaration of a local variable. eg var x: X = y
redef class AWhileExpr

nitc :: java_compiler $ AWhileExpr

A while statement
class JavaCodeFile

nitc $ JavaCodeFile

A file containing Java code.
class JavaCompiler

nitc $ JavaCompiler

Compiler that translates Nit code to Java code
class JavaCompilerVisitor

nitc $ JavaCompilerVisitor

The class visiting the AST
class JavaRuntimeModel

nitc $ JavaRuntimeModel

Handler for runtime classes generation
class JavaStaticFrame

nitc $ JavaStaticFrame

The static context of a visited property in a JavaCompilerVisitor
redef class Location

nitc :: java_compiler $ Location

A location inside a source file
redef class MClass

nitc :: java_compiler $ MClass

A named class
redef class MClassType

nitc :: java_compiler $ MClassType

A type based on a class.
redef abstract class MEntity

nitc :: java_compiler $ MEntity

A named and possibly documented entity in the model.
redef class MMethod

nitc :: java_compiler $ MMethod

A global method
redef class MMethodDef

nitc :: java_compiler $ MMethodDef

A local definition of a method
redef abstract class MType

nitc :: java_compiler $ MType

A global static type
redef class ModelBuilder

nitc :: java_compiler $ ModelBuilder

A model builder knows how to load nit source files and build the associated model
class RuntimeVariable

nitc $ RuntimeVariable

A runtime variable hold a runtime value in Java.
private interface TableCallable

nitc $ TableCallable

Used as a common type between MMethod and MMethodDef for table_send
redef class ToolContext

nitc :: java_compiler $ ToolContext

Global context for tools
package_diagram nitc::java_compiler java_compiler nitc::rapid_type_analysis rapid_type_analysis nitc::java_compiler->nitc::rapid_type_analysis nitc::transform transform nitc::java_compiler->nitc::transform nitc\>frontend\> frontend nitc::java_compiler->nitc\>frontend\> nitc\>semantize\> semantize nitc::rapid_type_analysis->nitc\>semantize\> nitc::explain_assert_api explain_assert_api nitc::rapid_type_analysis->nitc::explain_assert_api csv csv nitc::rapid_type_analysis->csv nitc::transform->nitc\>semantize\> nitc::astbuilder astbuilder nitc::transform->nitc::astbuilder nitc\>frontend\>->nitc\>semantize\> nitc\>modelize\> modelize nitc\>frontend\>->nitc\>modelize\> gen_nit gen_nit nitc\>frontend\>->gen_nit nitc nitc nitc\>frontend\>->nitc nitc\>parser\> parser nitc\>frontend\>->nitc\>parser\> ...nitc\>semantize\> ... ...nitc\>semantize\>->nitc\>semantize\> ...nitc::explain_assert_api ... ...nitc::explain_assert_api->nitc::explain_assert_api ...csv ... ...csv->csv ...nitc::astbuilder ... ...nitc::astbuilder->nitc::astbuilder ...nitc\>modelize\> ... ...nitc\>modelize\>->nitc\>modelize\> ...gen_nit ... ...gen_nit->gen_nit ...nitc ... ...nitc->nitc ...nitc\>parser\> ... ...nitc\>parser\>->nitc\>parser\> nitc::nitj nitj nitc::nitj->nitc::java_compiler a_star-m a_star-m a_star-m->nitc::nitj a_star-m... ... a_star-m...->a_star-m

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module actors_injection_phase

nitc :: actors_injection_phase

Injects model for the classes annotated with "is actor" so
module annotation

nitc :: annotation

Management and utilities on annotations
module array

core :: array

This module introduces the standard array structure.
module astbuilder

nitc :: astbuilder

Instantiation and transformation of semantic nodes in the AST of expressions and statements
module auto_super_init

nitc :: auto_super_init

Computing of super-constructors that must be implicitly called at the begin of constructors.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module check_annotation

nitc :: check_annotation

Check that annotation present in the AST are either primitive or user-declared
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module console

console :: console

Defines some ANSI Terminal Control Escape Sequences.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module csv

csv :: csv

CSV document handling.
module deriving

nitc :: deriving

Injection of automatic method definitions for standard methods, based on the attributes of the classes
module digraph

graph :: digraph

Implementation of directed graphs, also called digraphs.
module div_by_zero

nitc :: div_by_zero

Detection of divisions by zero in obvious cases
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module explain_assert_api

nitc :: explain_assert_api

Explain failed assert to the console (service declaration only)
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module flow

nitc :: flow

Intraprocedural static flow.
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module glsl_validation

nitc :: glsl_validation

Check shader code within Nit modules using the tool glslangValidator
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module i18n_phase

nitc :: i18n_phase

Basic support of internationalization through the generation of id-to-string tables
module ini

ini :: ini

Read and write INI configuration files
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module lexer

nitc :: lexer

Lexer and its tokens.
module lexer_work

nitc :: lexer_work

Internal algorithm and data structures for the Nit lexer
module list

core :: list

This module handle double linked lists
module literal

nitc :: literal

Parsing of literal values in the abstract syntax tree.
module loader

nitc :: loader

Loading of Nit source files
module local_var_init

nitc :: local_var_init

Verify that local variables are initialized before their usage
module location

nitc :: location

Nit source-file and locations in source-file
module math

core :: math

Mathematical operations
module mdoc

nitc :: mdoc

Documentation of model entities
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module mmodule

nitc :: mmodule

modules and module hierarchies in the metamodel
module mmodule_data

nitc :: mmodule_data

Define and retrieve data in modules
module model

nitc :: model

Classes, types and properties
module model_base

nitc :: model_base

The abstract concept of model and related common things
module modelbuilder_base

nitc :: modelbuilder_base

Load nit source files and build the associated model
module modelize

nitc :: modelize

Create a model from nit source files
module modelize_class

nitc :: modelize_class

Analysis and verification of class definitions to instantiate model element
module modelize_property

nitc :: modelize_property

Analysis and verification of property definitions to instantiate model element
module more_collections

more_collections :: more_collections

Highly specific, but useful, collections-related classes.
module mpackage

nitc :: mpackage

Modelisation of a Nit package
module native

core :: native

Native structures for text and bytes
module nitpm_shared

nitc :: nitpm_shared

Services related to the Nit package manager
module no_warning

nitc :: no_warning

Fill toolcontext information about blacklisting of warnings.
module numeric

core :: numeric

Advanced services for Numeric types
module opts

opts :: opts

Management of options on the command line
module ordered_tree

ordered_tree :: ordered_tree

Manipulation and presentation of ordered trees.
module parallelization_phase

nitc :: parallelization_phase

Phase generating threads for functions annotated with threaded annotation
module parse_annotations

nitc :: parse_annotations

Simple annotation parsing
module parser

nitc :: parser

Parser.
module parser_nodes

nitc :: parser_nodes

AST nodes of the Nit language
module parser_prod

nitc :: parser_prod

Production AST nodes full definition.
module parser_util

nitc :: parser_util

Utils and tools related to parsers and AST
module parser_work

nitc :: parser_work

Internal algorithm and data structures for the Nit parser
module phase

nitc :: phase

Phases of the processing of nit programs
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module regex_phase

nitc :: regex_phase

Check for error in regular expressions from string literals
module ropes

core :: ropes

Tree-based representation of a String.
module scope

nitc :: scope

Identification and scoping of local variables and labels.
module semantize

nitc :: semantize

Process bodies of methods in regard with the model.
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module serialization_model_phase

nitc :: serialization_model_phase

Phase generating methods (model-only) to serialize Nit objects
module simple_misc_analysis

nitc :: simple_misc_analysis

Simple vavious processing on a AST
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module tables

nitc :: tables

Module that interfaces the parsing tables.
module template

template :: template

Basic template system
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module toolcontext

nitc :: toolcontext

Common command-line tool infrastructure than handle options and error messages
module typing

nitc :: typing

Intraprocedural resolution of static types and OO-services
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module version

nitc :: version

This file was generated by git-gen-version.sh

Parents

module frontend

nitc :: frontend

Collect and orchestration of main frontend phases
module rapid_type_analysis

nitc :: rapid_type_analysis

Rapid type analysis on the AST
module transform

nitc :: transform

Thansformations that simplify the AST of expressions

Children

module nitj

nitc :: nitj

Compile Nit into Java code runnable on the Java Virtual Machine.

Descendants

module a_star-m

a_star-m

# Compile Nit code to Java code
#
# 3 runtime structures are used to represent Nit instance in Java generated code:
# * `RTClass` to represent a class, it's super-type table and its VFT
# * `RTMethod` to reprensent a compiled method definition
# * `RTVal` to reprensent a Nit instance, the null value or a native value
#
# More details are given in the documentation of these 3 classes.
#
# TODO Factorize with `abstract_compiler`
module java_compiler

import rapid_type_analysis
import transform
import frontend

redef class ToolContext

	# Where to output the generated binary
	var opt_output = new OptionString("Output file", "-o", "--output")

	# Where to output tmp files
	var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")

	# Compile using ant instead of make (faster, but no error display)
	var opt_ant = new OptionBool("Batch with ant (faster, but no error display)", "--ant")

	redef init do
		super
		option_context.add_option(opt_output, opt_compile_dir, opt_ant)
	end
end

redef class ModelBuilder

	# Start the Java compiler
	fun run_java_compiler(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis) do
		var time0 = get_time
		toolcontext.info("*** GENERATING JAVA ***", 1)

		var compiler = new JavaCompiler(mainmodule, self, runtime_type_analysis)
		compiler.do_compilation

		var time1 = get_time
		toolcontext.info("*** END GENERATING JAVA: {time1-time0} ***", 2)
		write_and_make(compiler)
	end

	# Write Java code and compile it into an executable jar
	fun write_and_make(compiler: JavaCompiler) do
		var time0 = get_time
		toolcontext.info("*** WRITING JAVA ***", 1)

		compiler.compile_dir.mkdir

		var jfiles = write_java_files(compiler)

		var time1 = get_time
		toolcontext.info("*** END WRITING JAVA: {time1-time0} ***", 2)

		time0 = time1
		toolcontext.info("*** COMPILING JAVA ***", 1)

		if toolcontext.opt_ant.value then
			build_with_ant(compiler, jfiles)
		else
			build_with_make(compiler, jfiles)
		end
		write_shell_script(compiler)

		time1 = get_time
		toolcontext.info("*** END COMPILING JAVA: {time1-time0} ***", 2)
	end

	# Write files managed by `compiler` into concrete files
	fun write_java_files(compiler: JavaCompiler): Array[String] do
		var jfiles = new Array[String]
		for f in compiler.files do
			var filepath = "{compiler.compile_dir}/{f.filename}"
			var file = cache_file(filepath)
			for line in f.lines do file.write(line)
			close_cache(filepath, file)
			jfiles.add(f.filename)
		end
		return jfiles
	end

	# Cache a file as `{filepath}.tmp` and replace the original if different
	private fun cache_file(filepath: String): FileWriter do
		if toolcontext.opt_ant.value and filepath.file_exists then
			return new FileWriter.open("{filepath}.tmp")
		else
			return new FileWriter.open(filepath)
		end
	end

	# Close the writer and move tmp file to original if modified
	private fun close_cache(filepath: String, file: FileWriter) do
		file.close
		if "{filepath}.tmp".file_exists then
			sys.system("if ! diff {filepath}.tmp {filepath} > /dev/null; then mv {filepath}.tmp {filepath}; else rm {filepath}.tmp; fi")
		end
	end

	# Compile Java generated files using `make`
	fun build_with_make(compiler: JavaCompiler, jfiles: Array[String]) do
		write_manifest(compiler)
		write_makefile(compiler, jfiles)
		var compile_dir = compiler.compile_dir
		var outname = compiler.outname.to_path.filename
		toolcontext.info("make -N -C {compile_dir} -f {outname}.mk", 2)
		var res
		if toolcontext.verbose_level >= 3 then
			res = sys.system("make -B -C {compile_dir} -f {outname}.mk 2>&1")
		else
			res = sys.system("make -B -C {compile_dir} -f {outname}.mk 2>&1 > /dev/null")
		end
		if res != 0 then toolcontext.error(null, "make failed! Error code: {res}.")
	end

	# Compile Java sources using `ant`
	fun build_with_ant(compiler: JavaCompiler, jfiles: Array[String]) do
		compile_antfile(compiler, jfiles)
		var outname = compiler.outname.to_path.filename
		var antpath = "{compiler.compile_dir}/{outname}.xml"
		self.toolcontext.info("ant jar -f {antpath}", 2)
		var res
		if self.toolcontext.verbose_level >= 3 then
			res = sys.system("ant jar -f {antpath} 2>&1")
		else
			res = sys.system("ant jar -f {antpath} 2>&1 > /dev/null")
		end
		if res != 0 then
			toolcontext.error(null, "ant compile failed! Error code: {res}.")
		end
	end

	# Write the Makefile used to compile Java generated files into an executable jar
	fun write_makefile(compiler: JavaCompiler, jfiles: Array[String]) do
		# list class files from jfiles
		var ofiles = new List[String]
		for f in jfiles do ofiles.add(f.strip_extension(".java") + ".class")

		var compile_dir = compiler.compile_dir
		var outname = compiler.outname.to_path.filename
		var outpath = (sys.getcwd / compiler.outname).simplify_path
		var makename = "{compile_dir}/{outname}.mk"
		var makefile = new FileWriter.open(makename)

		makefile.write("JC = javac\n")
		makefile.write("JAR = jar\n\n")

		makefile.write("all: {outpath}.jar\n\n")

		makefile.write("{outpath}.jar: {compiler.mainmodule.jname}_Main.class\n")
		makefile.write("\t$(JAR) cfm {outpath}.jar {outname}.mf {ofiles.join(" ")}\n\n")

		makefile.write("{compiler.mainmodule.jname}_Main.class:\n")
		makefile.write("\t$(JC) {jfiles.join(" ")}\n\n")

		makefile.write("clean:\n")
		makefile.write("\trm {ofiles.join(" ")} 2>/dev/null\n\n")

		makefile.close
		toolcontext.info("Generated makefile: {makename}", 2)
	end

	# The Ant `build.xml` script used to compile build the final jar
	fun compile_antfile(compiler: JavaCompiler, jfiles: Array[String]) do
		var compile_dir = compiler.compile_dir
		var outname = compiler.outname.to_path.filename
		var outpath = (sys.getcwd / compiler.outname).simplify_path
		var antname = "{compile_dir}/{outname}.xml"
		var antfile = new FileWriter.open(antname)
		var jname = compiler.mainmodule.jname
		antfile.write("<project>")
		antfile.write(" <target name=\"compile\">")
		antfile.write("  <mkdir dir=\"classes\"/>")
		antfile.write("  <javac includes=\"{compiler.mainmodule.jname}_Main.java {jfiles.join(" ")}\" srcdir=\".\" destdir=\"classes\"/>")
		antfile.write(" </target>")
		antfile.write(" <target name=\"jar\" depends=\"compile\">")
		antfile.write("  <jar destfile=\"{outpath}.jar\" basedir=\"classes\">")
		antfile.write("   <manifest>")
		antfile.write("    <attribute name=\"Main-Class\" value=\"{jname}_Main\"/>")
		antfile.write("   </manifest>")
		antfile.write("  </jar>")
		antfile.write(" </target>")
		antfile.write("</project>")
		antfile.close
		toolcontext.info("Generated antfile: {antname}", 2)
	end

	# Write the Java manifest file
	private fun write_manifest(compiler: JavaCompiler) do
		var compile_dir = compiler.compile_dir
		var outname = compiler.outname.to_path.filename
		var maniffile = new FileWriter.open("{compile_dir}/{outname}.mf")
		maniffile.write("Manifest-Version: 1.0\n")
		maniffile.write("Main-Class: {compiler.mainmodule.jname}_Main\n")
		maniffile.close
	end

	# Write a simple bash script that runs the jar like it was a binary generated by nitc
	private fun write_shell_script(compiler: JavaCompiler) do
		var outname = compiler.outname
		var shfile = new FileWriter.open(outname)
		shfile.write("#!/bin/bash\n")
		shfile.write("java -jar {outname}.jar \"$@\"\n")
		shfile.close
		sys.system("chmod +x {outname}")
	end
end

# Compiler that translates Nit code to Java code
class JavaCompiler
	# The main module of the program currently compiled
	var mainmodule: MModule

	# Modelbuilder used to know the model and the AST
	var modelbuilder: ModelBuilder

	# The result of the RTA (used to know live types and methods)
	var runtime_type_analysis: RapidTypeAnalysis

	# Where to generate tmp files
	var compile_dir: String is lazy do
		var dir = modelbuilder.toolcontext.opt_compile_dir.value
		if dir == null then dir = "nitj_compile"
		return dir
	end

	# Name of the generated executable
	var outname: String is lazy do
		var name = modelbuilder.toolcontext.opt_output.value
		if name == null then name = mainmodule.jname
		return name
	end

	# The list of all associated files
	# Used to generate .java files
	var files: Array[JavaCodeFile] = new Array[JavaCodeFile]

	# Force the creation of a new file
	# The point is to avoid contamination between must-be-compiled-separately files
	fun new_file(name: String): JavaCodeFile do
		var file = new JavaCodeFile(name)
		files.add(file)
		return file
	end

	# Kind of visitor to use
	type VISITOR: JavaCompilerVisitor

	# Initialize a visitor specific for the compiler engine
	fun new_visitor(filename: String): VISITOR do
		return new JavaCompilerVisitor(self, new_file(filename))
	end

	# RuntimeModel representation
	private var rt_model: JavaRuntimeModel is lazy do return new JavaRuntimeModel

	# Compile Nit code to Java
	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

		# compile method structures
		compile_mmethods_to_java

		# 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
	# attributes, fill vft and type table.
	fun compile_mclasses_to_java do
		for mclass in mainmodule.model.mclasses do
			mclass.compile_to_java(new_visitor("{mclass.rt_name}.java"))
		end
	end

	# Generate a `RTMethod` for each `MMethodDef` found in model
	#
	# This is a separate phase.
	fun compile_mmethods_to_java do
		for mmodule in mainmodule.in_importation.greaters do
			for mclassdef in mmodule.mclassdefs do
				for mdef in mclassdef.mpropdefs do
					if mdef isa MMethodDef then
						mdef.compile_to_java(new_visitor("{mdef.rt_name}.java"))
					end
				end
			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) \{")

		var main_type = mainmodule.sys_type
		if main_type != null then
			var mainmodule = v.compiler.mainmodule
			var glob_sys = v.init_instance(main_type)
			var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
			if main_init != null then
				v.send(main_init, [glob_sys])
			end
			var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
				mainmodule.try_get_primitive_method("main", main_type.mclass)
			if main_method != null then
				v.send(main_method, [glob_sys])
			end
		end
		v.add("  \}")
		v.add("\}")
	end
end

# The class visiting the AST
#
# A visitor is attached to one JavaCodeFile it writes into.
class JavaCompilerVisitor
	super Visitor

	# JavaCompiler used with this visitor
	type COMPILER: JavaCompiler

	# The associated compiler
	var compiler: JavaCompiler

	# 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

	# Return an unique and stable identifier associated with an escapemark
	fun escapemark_name(e: nullable EscapeMark): String do
		assert e != null
		var frame = self.frame
		assert frame != null
		if frame.escapemark_names.has_key(e) then return frame.escapemark_names[e]
		var name = e.name
		if name == null then name = "label"
		name = get_name(name)
		frame.escapemark_names[e] = name
		return name
	end

	# Insert a C label for associated with an escapemark
	fun add_escape_label(e: nullable EscapeMark) do
		if e == null then return
		if e.escapes.is_empty then return
		add("BREAK_{escapemark_name(e)}: ")
	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.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 args[i].vararg_decl > 0 then
				var vararg = exprs.sub(j, args[i].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 super call from a method definition
	fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable do
		return table_send(m, args)
	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`)
	fun add(line: String) do file.lines.add("{line}\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
		if nexpr.mtype == null and not nexpr.is_typed then
			# Untyped expression.
			# Might mean dead code or invalid code
			# so aborts
			add_abort("FATAL: bad statement executed.")
			return
		end

		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 res == null then
			# Untyped expression.
			# Might mean dead code or invalid code.
			# so aborts
			add_abort("FATAL: bad expression executed.")
			# and return a placebo result to please the C compiler
			if mtype == null then mtype = compiler.mainmodule.object_type
			res = null_instance

			self.current_node = old
			return res
		end

		if mtype != null then
			mtype = anchor(mtype)
			res = autobox(res, mtype)
		end

		current_node = old
		return res
	end

	# Alias for `self.expr(nexpr, self.bool_type)`
	fun expr_bool(nexpr: AExpr): RuntimeVariable do
		return expr(nexpr, compiler.mainmodule.bool_type)
	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}\");")
		add_raw_abort
	end

	# Abort without displaying the cause.
	#
	# Used to customizable errors.
	private fun add_raw_abort do
		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

	# Add a dynamic cast
	fun add_cast(value: RuntimeVariable, mtype: MType) do
		var res = type_test(value, mtype)
		add("if (!{res}) \{")
		add("System.err.print(\"Runtime error: Cast failed. Expected `{mtype.to_s.escape_to_c}`, got `\" + {value}.rtclass.class_name + \"`\");")
		add_raw_abort
		add("\}")
	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

	#  Generate a polymorphic subtype test
	fun type_test(value: RuntimeVariable, mtype: MType): RuntimeVariable do
		add("/* {value.inspect} isa {mtype} */")
		var res = self.new_var(compiler.mainmodule.bool_type)

		# check color is in table
		var maybenull = (value.mcasttype isa MNullableType or value.mcasttype isa MNullType)
		if maybenull then
			add("if({value} == null || {value}.is_null()) \{")
			add("{res} = true && {mtype isa MNullableType};")
			add("\} else \{")
		end
		if mtype isa MNullableType then mtype = mtype.mtype
		var mclass = mtype.as(MClassType).mclass
		add("{res} = {value}.rtclass.supers.get(\"{mclass.jname}\") == {mclass.rt_name}.get{mclass.rt_name}();")
		if maybenull then
			add("\}")
		end
		return res
	end

	# Generate the code required to dynamically check if 2 objects share the same runtime type
	fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable do
		var res = self.new_var(compiler.mainmodule.bool_type)
		add("{res} = {value1}.rtclass == {value2}.rtclass;")
		return res
	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 is_set test
	fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable do
		# TODO self.check_recv_notnull(recv)
		var res = new_var(compiler.mainmodule.bool_type)

		# What is the declared type of the attribute?
		var mtype = a.intro.static_mtype.as(not null)
		var intromclassdef = a.intro.mclassdef
		mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)

		if mtype isa MNullableType then
			add("{res} = true; /* easy isset: {a} on {recv.inspect} */")
			return res
		end
		add("{res} = {recv}.attrs.get(\"{a.jname}\") != null; /* {a} on {recv.inspect} */")
		return res
	end

	# 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)

		# Check for Uninitialized attribute
		if not ret isa MNullableType then check_attribute(a, recv)

		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

	# Check uninitialized attribute
	fun check_attribute(a: MAttribute, recv: RuntimeVariable) do
		add "if({recv}.attrs.get(\"{a.jname}\") == null) \{"
		add_abort "Uninitialized attribute {a.name}"
		add "\}"
	end

	# Utils

	# Display a info message
	fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
end

# A file containing Java code.
class JavaCodeFile

	# File name
	var filename: String

	# Lines to write
	var lines: List[String] = new List[String]
end

redef class MEntity
	# A Java compatible name for `self`
	private fun jname: String do return name.to_cmangle
end

# Handler for runtime classes generation
#
# We need 3 kinds of runtime structures:
# * `RTClass` to represent a global class
# * `RTMethod` to represent a method definition
# * `RTVal` to represent runtime variables
class JavaRuntimeModel

	# Compile JavaRuntimeModel structures
	fun compile_rtmodel(compiler: JavaCompiler) do
		compile_rtclass(compiler)
		compile_rtmethod(compiler)
		compile_rtval(compiler)
	end

	# Compile the abstract runtime class structure
	#
	# Runtime classes have 3 attributes:
	# * `class_name`: the class name as a String
	# * `vft`: the virtual function table for the class (flattened)
	# * `supers`: the super type table (used for type tests)
	fun compile_rtclass(compiler: JavaCompiler) do
		var v = compiler.new_visitor("RTClass.java")
		v.add("import java.util.HashMap;")
		v.add("public abstract class RTClass \{")
		v.add("  public String class_name;")
		v.add("  public HashMap<String, RTMethod> vft = new HashMap<>();")
		v.add("  public HashMap<String, RTClass> supers = new HashMap<>();")
		v.add("  protected RTClass() \{\}")
		v.add("\}")
	end

	# Compile the abstract runtime method structure
	#
	# Method body is executed through the `exec` method:
	# * `exec` always take an array of RTVal as arg, the first one must be the receiver
	# * `exec` always returns a RTVal (or null if the Nit return type is void)
	fun compile_rtmethod(compiler: JavaCompiler) do
		var v = compiler.new_visitor("RTMethod.java")
		v.add("public abstract class RTMethod \{")
		v.add("  protected RTMethod() \{\}")
		v.add("  public abstract RTVal exec(RTVal[] args);")
		v.add("\}")
	end

	# Compile the runtime value structure
	#
	# RTVal both represents object instances and primitives values:
	# * object instances:
	#   * `rtclass` the class of the RTVal is instance of
	#   * `attrs` contains the attributes of the instance
	# * primitive values:
	#   * `rtclass` represents the class of the primitive value Nit type
	#   * `value` contains the primitive value of the instance
	# * null values:
	#   * they must have both `rtclass` and `value` as null
	fun compile_rtval(compiler: JavaCompiler) do
		var v = compiler.new_visitor("RTVal.java")
		v.add("import java.util.HashMap;")
		v.add("public class RTVal \{")
		v.add("  public RTClass rtclass;")
		v.add("  public HashMap<String, RTVal> attrs = new HashMap<>();")
		v.add("  Object value;")
		v.add("  public RTVal(RTClass rtclass) \{")
		v.add("    this.rtclass = rtclass;")
		v.add("  \}")
		v.add("  public RTVal(RTClass rtclass, Object value) \{")
		v.add("    this.rtclass = rtclass;")
		v.add("    this.value = value;")
		v.add("  \}")
		v.add("  public boolean is_null() \{ return rtclass == null && value == null; \}")
		v.add("\}")
	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

	# Labels associated to a each escapemarks.
	# Because of inlinings, escape-marks must be associated to their context (the frame)
	private var escapemark_names = new HashMap[EscapeMark, String]
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 != "RTVal")`
	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 == "CString" then
			return "String"
		else if mclass.name == "NativeArray" then
			return "Array"
		end
		return "RTVal"
	end
end

redef class MClass

	# Runtime name
	private fun rt_name: String do return "RTClass_{intro_mmodule.jname}_{jname}"

	# Generate a Java RTClass for a Nit MClass
	fun compile_to_java(v: JavaCompilerVisitor) do
		v.add("public class {rt_name} extends RTClass \{")
		v.add("  protected static RTClass instance;")
		v.add("  private {rt_name}() \{")
		v.add("    this.class_name = \"{name}\";")
		compile_vft(v)
		compile_type_table(v)
		v.add("  \}")
		v.add("  public static RTClass get{rt_name}() \{")
		v.add("    if(instance == null) \{")
		v.add("      instance = new {rt_name}();")
		v.add("    \}")
		v.add("    return instance;")
		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
		return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
	end

	# Generate a Java RTMethod for `self`
	fun compile_to_java(v: JavaCompilerVisitor) do
		v.add("public class {rt_name} extends RTMethod \{")
		v.add("  protected static RTMethod instance;")
		v.add("  public static RTMethod get{rt_name}() \{")
		v.add("    if(instance == null) \{")
		v.add("      instance = new {rt_name}();")
		v.add("    \}")
		v.add("    return instance;")
		v.add("  \}")
		v.add("  @Override")
		v.add("  public RTVal exec(RTVal[] args) \{")
		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)

		var recv = mclassdef.bound_mtype
		var arguments = new Array[RuntimeVariable]
		var frame = new JavaStaticFrame(v, self, 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.autobox(boxed, recv)};"

		var msignature = self.msignature
		var ret = null
		if msignature != null then
			ret = msignature.return_mtype
			if ret != null then
				var retvar = v.decl_var("ret", ret)
				if ret.name == "Int" then v.add "{retvar} = 0;"
				if ret.name == "Float" then v.add "{retvar} = 0.0;"
				if ret.name == "Bool" then v.add "{retvar} = false;"
				if ret.name == "Char" then v.add "{retvar} = 0;"
				if ret.name == "Byte" then v.add "{retvar} = 0;"
				frame.returnvar = retvar
			end
		end
		frame.returnlabel = v.get_name("RET_LABEL")

		v.current_node = node
		if is_abstract then
			v.add_abort("Abstract method `{mproperty.name}` called on `\"  + {selfvar}.rtclass.class_name +\"`")
			v.add("return null;")
			return
		end
		v.current_node = null

		v.add("{frame.returnlabel.as(not null)}: \{")

		if node isa APropdef then
			node.compile_to_java(v, self, arguments)
		else if node isa AClassdef then
			node.compile_to_java(v, self, arguments)
		else
			abort
		end

		v.add("\}")
		if ret != null then
			v.add("return {v.autobox(frame.returnvar.as(not null), v.compiler.mainmodule.object_type)};")
		else
			v.add("return null;")
		end
	end
end

redef class AClassdef
	private fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) do
		if mpropdef.mproperty.is_root_init then
			if not mpropdef.is_intro then
				v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
			end
		else
			abort
		end
	end
end

redef class APropdef

	# Compile that property definition to java code
	fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) do
		v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
	end
end

redef class AMethPropdef
	redef fun compile_to_java(v, mpropdef, arguments) do
		if mpropdef.msignature != null then
			var i = 0
			for mparam in mpropdef.msignature.as(not null).mparameters do
				var variable = n_signature.as(not null).n_params[i].variable
				if variable == null then continue
				var argvar = v.variable(variable)
				v.assign(argvar, v.new_expr("args[{i + 1}]", v.compiler.mainmodule.object_type))
				arguments.add(argvar)
				i += 1
			end
		end

		# Call the implicit super-init
		var auto_super_inits = self.auto_super_inits
		if auto_super_inits != null then
			var args = [arguments.first]
			for auto_super_init in auto_super_inits do
				assert auto_super_init.mproperty != mpropdef.mproperty
				args.clear
				for i in [0..auto_super_init.msignature.arity+1[ do
					args.add(arguments[i])
				end
				assert auto_super_init.mproperty != mpropdef.mproperty
				v.compile_callsite(auto_super_init, args)
			end
		end
		if auto_super_call then
			v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
		end

		compile_inside_to_java(v, mpropdef, arguments)
	end

	# Compile the inside of the method body
	private fun compile_inside_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) 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 == "<<" 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_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 == "<<" 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.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("{frame.returnlabel.as(not null)}: \{")
			v.stmt(nblock)
			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 ASuperExpr
	redef fun expr(v)
	do
		var frame = v.frame
		assert frame != null
		var recv = frame.arguments.first

		var callsite = self.callsite
		if callsite != null then
			var args

			if self.n_args.n_exprs.is_empty then
				# Add automatic arguments for the super init call
				args = [recv]
				for i in [0..callsite.msignature.arity[ do
					args.add(frame.arguments[i+1])
				end
			else
				args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
			end

			# Super init call
			var res = v.compile_callsite(callsite, args)
			return res
		end

		var mpropdef = self.mpropdef.as(not null)

		var args
		if self.n_args.n_exprs.is_empty then
			args = frame.arguments
		else
			args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
		end

		# Standard call-next-method
		return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
	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 AAttrExpr
	redef fun expr(v) do
		var recv = v.expr(self.n_expr, null)
		var mproperty = self.mproperty.as(not null)
		return v.read_attribute(mproperty, recv)
	end
end

redef class AAttrAssignExpr
	redef fun expr(v) do
		var recv = v.expr(self.n_expr, null)
		var i = v.expr(self.n_value, null)
		var mproperty = self.mproperty.as(not null)
		v.write_attribute(mproperty, recv, i)
		return i
	end
end

redef class AAttrReassignExpr
	redef fun stmt(v) do
		var recv = v.expr(self.n_expr, null)
		var value = v.expr(self.n_value, null)
		var mproperty = self.mproperty.as(not null)
		var attr = v.read_attribute(mproperty, recv)
		var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
		assert res != null
		v.write_attribute(mproperty, recv, res)
	end
end

redef class AIssetAttrExpr
	redef fun expr(v) do
		var recv = v.expr(self.n_expr, null)
		var mproperty = self.mproperty.as(not null)
		return v.isset_attribute(mproperty, recv)
	end
end

redef class 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 AIfExpr
	redef fun stmt(v) do
		var cond = v.expr_bool(self.n_expr)
		v.add("if ({cond})\{")
		v.stmt(self.n_then)
		v.add("\} else \{")
		v.stmt(self.n_else)
		v.add("\}")
	end

	redef fun expr(v) do
		var res = v.new_var(self.mtype.as(not null))
		var cond = v.expr_bool(self.n_expr)
		v.add("if ({cond})\{")
		v.assign(res, v.expr(self.n_then.as(not null), null))
		v.add("\} else \{")
		v.assign(res, v.expr(self.n_else.as(not null), null))
		v.add("\}")
		return res
	end
end

redef class ADoExpr
	redef fun stmt(v)
	do
		v.add_escape_label(break_mark)
		v.add "\{"
		v.stmt(self.n_block)
		v.add "\}"
	end
end

redef class AWhileExpr
	redef fun stmt(v)
	do
		v.add_escape_label(break_mark)
		v.add_escape_label(continue_mark)
		v.add("for(;;) \{")
		var cond = v.expr_bool(self.n_expr)
		v.add("if (!{cond}) break;")
		v.stmt(self.n_block)
		v.add("\}")
	end
end

redef class ALoopExpr
	redef fun stmt(v)
	do
		v.add_escape_label(break_mark)
		v.add_escape_label(continue_mark)
		v.add("for(;;) \{")
		v.stmt(self.n_block)
		v.add("\}")
	end
end

redef class AEscapeExpr
	redef fun stmt(v) do v.add("break BREAK_{v.escapemark_name(escapemark)};")
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 AAssertExpr
	redef fun stmt(v) do
		var cond = v.expr_bool(self.n_expr)
		v.add("if (!{cond}) \{")
		v.stmt(self.n_else)
		var nid = self.n_id
		if nid != null then
			v.add_abort("Assert '{nid.text}' failed")
		else
			v.add_abort("Assert failed")
		end
		v.add("\}")
	end
end

redef class AImpliesExpr
	redef fun expr(v) do
		var res = v.new_var(mtype.as(not null))
		var i1 = v.expr_bool(n_expr)
		v.add("if (!{i1}) \{")
		v.add("{res} = true;")
		v.add("\} else \{")
		var i2 = v.expr_bool(n_expr2)
		v.add("{res} = {i2};")
		v.add("\}")
		return res
	end
end

redef class AOrElseExpr
	redef fun expr(v)
	do
		var res = v.new_var(self.mtype.as(not null))
		var i1 = v.expr(self.n_expr, null)
		v.add("if ({i1} != null && !{i1}.is_null()) \{")
		v.assign(res, i1)
		v.add("\} else \{")
		var i2 = v.expr(self.n_expr2, null)
		v.assign(res, i2)
		v.add("\}")
		return res
	end
end

redef class AOrExpr
	redef fun expr(v) do
		var res = v.new_var(self.mtype.as(not null))
		var i1 = v.expr_bool(self.n_expr)
		v.add("if ({i1}) \{")
		v.add("{res} = true;")
		v.add("\} else \{")
		var i2 = v.expr_bool(self.n_expr2)
		v.add("{res} = {i2};")
		v.add("\}")
		return res
	end
end

redef class AAndExpr
	redef fun expr(v) do
		var res = v.new_var(self.mtype.as(not null))
		var i1 = v.expr_bool(self.n_expr)
		v.add("if (!{i1}) \{")
		v.add("{res} = false;")
		v.add("\} else \{")
		var i2 = v.expr_bool(self.n_expr2)
		v.add("{res} = {i2};")
		v.add("\}")
		return res
	end
end

redef class ANotExpr
	redef fun expr(v) do
		var cond = v.expr_bool(self.n_expr)
		return v.new_expr("!{cond}", self.mtype.as(not null))
	end
end

redef class AIntegerExpr
	redef fun expr(v) do
		if value isa Int then
			return v.int_instance(self.value.as(Int))
		else if value isa Byte then
			return v.byte_instance(self.value.as(Byte))
		else
			# Should not happen
			abort
		end
	end
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 AAsCastExpr
	redef fun expr(v)
	do
		var i = v.expr(n_expr, null)
		v.add_cast(i, mtype.as(not null))
		return i
	end
end

redef class AAsNotnullExpr
	redef fun expr(v) do
		var i = v.expr(n_expr, null)
		if i.mtype.is_java_primitive then return i

		v.add("if ({i} == null || {i}.is_null()) \{")
		v.add_abort("Cast failed")
		v.add("\}")
		return i
	end
end

redef class AIsaExpr
	redef fun expr(v)
	do
		var i = v.expr(self.n_expr, null)
		var cast_type = self.cast_type
		if cast_type == null then return null # no-no on broken node
		return v.type_test(i, cast_type)
	end
end

redef class AParExpr
	redef fun expr(v) do return v.expr(self.n_expr, null)
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
src/compiler/java_compiler.nit:15,1--2306,3