1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Compile Nit code to Java code
17 # 3 runtime structures are used to represent Nit instance in Java generated code:
18 # * `RTClass` to represent a class, it's super-type table and its VFT
19 # * `RTMethod` to reprensent a compiled method definition
20 # * `RTVal` to reprensent a Nit instance, the null value or a native value
22 # More details are given in the documentation of these 3 classes.
24 # TODO Factorize with `abstract_compiler`
27 import rapid_type_analysis
30 redef class ToolContext
32 # Where to output the generated binary
33 var opt_output
= new OptionString("Output file", "-o", "--output")
35 # Where to output tmp files
36 var opt_compile_dir
= new OptionString("Directory used to generate temporary files", "--compile-dir")
40 option_context
.add_option
(opt_output
, opt_compile_dir
)
44 redef class ModelBuilder
46 # Start the Java compiler
47 fun run_java_compiler
(mainmodule
: MModule, runtime_type_analysis
: RapidTypeAnalysis) do
49 toolcontext
.info
("*** GENERATING JAVA ***", 1)
51 var compiler
= new JavaCompiler(mainmodule
, self, runtime_type_analysis
)
52 compiler
.do_compilation
55 toolcontext
.info
("*** END GENERATING JAVA: {time1-time0} ***", 2)
56 write_and_make
(compiler
)
59 # Write Java code and compile it into an executable jar
60 fun write_and_make
(compiler
: JavaCompiler) do
62 toolcontext
.info
("*** WRITING JAVA ***", 1)
64 compiler
.compile_dir
.mkdir
66 var jfiles
= write_java_files
(compiler
)
69 toolcontext
.info
("*** END WRITING JAVA: {time1-time0} ***", 2)
72 toolcontext
.info
("*** COMPILING JAVA ***", 1)
74 build_with_make
(compiler
, jfiles
)
75 write_shell_script
(compiler
)
78 toolcontext
.info
("*** END COMPILING JAVA: {time1-time0} ***", 2)
81 # Write files managed by `compiler` into concrete files
82 fun write_java_files
(compiler
: JavaCompiler): Array[String] do
83 var jfiles
= new Array[String]
84 for f
in compiler
.files
do
85 var file
= new FileWriter.open
("{compiler.compile_dir}/{f.filename}")
86 for line
in f
.lines
do file
.write
(line
)
88 jfiles
.add
(f
.filename
)
93 # Compile Java generated files using `make`
94 fun build_with_make
(compiler
: JavaCompiler, jfiles
: Array[String]) do
95 write_manifest
(compiler
)
96 write_makefile
(compiler
, jfiles
)
97 var compile_dir
= compiler
.compile_dir
98 var outname
= compiler
.outname
.to_path
.filename
99 toolcontext
.info
("make -N -C {compile_dir} -f {outname}.mk", 2)
101 if toolcontext
.verbose_level
>= 3 then
102 res
= sys
.system
("make -B -C {compile_dir} -f {outname}.mk 2>&1")
104 res
= sys
.system
("make -B -C {compile_dir} -f {outname}.mk 2>&1 > /dev/null")
106 if res
!= 0 then toolcontext
.error
(null, "make failed! Error code: {res}.")
109 # Write the Makefile used to compile Java generated files into an executable jar
110 fun write_makefile
(compiler
: JavaCompiler, jfiles
: Array[String]) do
111 # list class files from jfiles
112 var ofiles
= new List[String]
113 for f
in jfiles
do ofiles
.add
(f
.strip_extension
(".java") + ".class")
115 var compile_dir
= compiler
.compile_dir
116 var outname
= compiler
.outname
.to_path
.filename
117 var outpath
= (sys
.getcwd
/ compiler
.outname
).simplify_path
118 var makename
= "{compile_dir}/{outname}.mk"
119 var makefile
= new FileWriter.open
(makename
)
121 makefile
.write
("JC = javac\n")
122 makefile
.write
("JAR = jar\n\n")
124 makefile
.write
("all: {outpath}.jar\n\n")
126 makefile
.write
("{outpath}.jar: {compiler.mainmodule.jname}_Main.class\n")
127 makefile
.write
("\t$(JAR) cfm {outpath}.jar {outname}.mf {ofiles.join(" ")}\n\n")
129 makefile
.write
("{compiler.mainmodule.jname}_Main.class:\n")
130 makefile
.write
("\t$(JC) {jfiles.join(" ")}\n\n")
132 makefile
.write
("clean:\n")
133 makefile
.write
("\trm {ofiles.join(" ")} 2>/dev/null\n\n")
136 toolcontext
.info
("Generated makefile: {makename}", 2)
139 # Write the Java manifest file
140 private fun write_manifest
(compiler
: JavaCompiler) do
141 var compile_dir
= compiler
.compile_dir
142 var outname
= compiler
.outname
.to_path
.filename
143 var maniffile
= new FileWriter.open
("{compile_dir}/{outname}.mf")
144 maniffile
.write
("Manifest-Version: 1.0\n")
145 maniffile
.write
("Main-Class: {compiler.mainmodule.jname}_Main\n")
149 # Write a simple bash script that runs the jar like it was a binary generated by nitc
150 private fun write_shell_script
(compiler
: JavaCompiler) do
151 var outname
= compiler
.outname
152 var shfile
= new FileWriter.open
(outname
)
153 shfile
.write
("#!/bin/bash\n")
154 shfile
.write
("java -jar {outname}.jar \"$@\
"\n")
156 sys
.system
("chmod +x {outname}")
160 # Compiler that translates Nit code to Java code
162 # The main module of the program currently compiled
163 var mainmodule
: MModule
165 # Modelbuilder used to know the model and the AST
166 var modelbuilder
: ModelBuilder
168 # The result of the RTA (used to know live types and methods)
169 var runtime_type_analysis
: RapidTypeAnalysis
171 # Where to generate tmp files
172 var compile_dir
: String is lazy
do
173 var dir
= modelbuilder
.toolcontext
.opt_compile_dir
.value
174 if dir
== null then dir
= "nitj_compile"
178 # Name of the generated executable
179 var outname
: String is lazy
do
180 var name
= modelbuilder
.toolcontext
.opt_output
.value
181 if name
== null then name
= mainmodule
.jname
185 # The list of all associated files
186 # Used to generate .java files
187 var files
: Array[JavaCodeFile] = new Array[JavaCodeFile]
189 # Force the creation of a new file
190 # The point is to avoid contamination between must-be-compiled-separately files
191 fun new_file
(name
: String): JavaCodeFile do
192 var file
= new JavaCodeFile(name
)
197 # Kind of visitor to use
198 type VISITOR: JavaCompilerVisitor
200 # Initialize a visitor specific for the compiler engine
201 fun new_visitor
(filename
: String): VISITOR do
202 return new JavaCompilerVisitor(self, new_file
(filename
))
205 # RuntimeModel representation
206 private var rt_model
: JavaRuntimeModel is lazy
do return new JavaRuntimeModel
208 # Compile Nit code to Java
209 fun do_compilation
do
210 # compile java classes used to represents the runtime model of the program
211 rt_model
.compile_rtmodel
(self)
213 # compile class structures
214 compile_mclasses_to_java
216 # TODO compile methods
217 modelbuilder
.toolcontext
.info
("NOT YET IMPLEMENTED", 0)
220 # Generate a `RTClass` for each `MClass` found in model
222 # This is a global phase because we need to know all the program to build
223 # attributes, fill vft and type table.
224 fun compile_mclasses_to_java
do
225 for mclass
in mainmodule
.model
.mclasses
do
226 mclass
.compile_to_java
(new_visitor
("{mclass.rt_name}.java"))
231 # The class visiting the AST
233 # A visitor is attached to one JavaCodeFile it writes into.
234 class JavaCompilerVisitor
237 # JavaCompiler used with this visitor
238 type COMPILER: JavaCompiler
240 # The associated compiler
241 var compiler
: JavaCompiler
243 # The file to write generated code into
244 var file
: JavaCodeFile
248 # Add a line (will be suffixed by `\n`)
249 fun add
(line
: String) do file
.lines
.add
("{line}\n")
251 # Add a new partial line (no `\n` suffix)
252 fun addn
(line
: String) do file
.lines
.add
(line
)
255 # A file containing Java code.
262 var lines
: List[String] = new List[String]
266 # A Java compatible name for `self`
267 private fun jname
: String do return name
.to_cmangle
270 # Handler for runtime classes generation
272 # We need 3 kinds of runtime structures:
273 # * `RTClass` to represent a global class
274 # * `RTMethod` to represent a method definition
275 # * `RTVal` to represent runtime variables
276 class JavaRuntimeModel
278 # Compile JavaRuntimeModel structures
279 fun compile_rtmodel
(compiler
: JavaCompiler) do
280 compile_rtclass
(compiler
)
281 compile_rtmethod
(compiler
)
282 compile_rtval
(compiler
)
285 # Compile the abstract runtime class structure
287 # Runtime classes have 3 attributes:
288 # * `class_name`: the class name as a String
289 # * `vft`: the virtual function table for the class (flattened)
290 # * `supers`: the super type table (used for type tests)
291 fun compile_rtclass
(compiler
: JavaCompiler) do
292 var v
= compiler
.new_visitor
("RTClass.java")
293 v
.add
("import java.util.HashMap;")
294 v
.add
("public abstract class RTClass \{")
295 v
.add
(" public String class_name;")
296 v
.add
(" public HashMap<String, RTMethod> vft = new HashMap<>();")
297 v
.add
(" public HashMap<String, RTClass> supers = new HashMap<>();")
298 v
.add
(" protected RTClass() \{\}")
302 # Compile the abstract runtime method structure
304 # Method body is executed through the `exec` method:
305 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
306 # * `exec` always returns a RTVal (or null if the Nit return type is void)
307 fun compile_rtmethod
(compiler
: JavaCompiler) do
308 var v
= compiler
.new_visitor
("RTMethod.java")
309 v
.add
("public abstract class RTMethod \{")
310 v
.add
(" protected RTMethod() \{\}")
311 v
.add
(" public abstract RTVal exec(RTVal[] args);")
315 # Compile the runtime value structure
317 # RTVal both represents object instances and primitives values:
318 # * object instances:
319 # * `rtclass` the class of the RTVal is instance of
320 # * `attrs` contains the attributes of the instance
321 # * primitive values:
322 # * `rtclass` represents the class of the primitive value Nit type
323 # * `value` contains the primitive value of the instance
325 # * they must have both `rtclass` and `value` as null
326 fun compile_rtval
(compiler
: JavaCompiler) do
327 var v
= compiler
.new_visitor
("RTVal.java")
328 v
.add
("import java.util.HashMap;")
329 v
.add
("public class RTVal \{")
330 v
.add
(" public RTClass rtclass;")
331 v
.add
(" public HashMap<String, RTVal> attrs = new HashMap<>();")
332 v
.add
(" Object value;")
333 v
.add
(" public RTVal(RTClass rtclass) \{")
334 v
.add
(" this.rtclass = rtclass;")
336 v
.add
(" public RTVal(RTClass rtclass, Object value) \{")
337 v
.add
(" this.rtclass = rtclass;")
338 v
.add
(" this.value = value;")
340 v
.add
(" public boolean is_null() \{ return rtclass == null && value == null; \}")
348 private fun rt_name
: String do return "RTClass_{intro.mmodule.jname}_{jname}"
350 # Generate a Java RTClass for a Nit MClass
351 fun compile_to_java
(v
: JavaCompilerVisitor) do
352 v
.add
("public class {rt_name} extends RTClass \{")
353 v
.add
(" protected static RTClass instance;")
354 v
.add
(" private {rt_name}() \{")
355 v
.add
(" this.class_name = \"{name}\
";")
356 # TODO compile_vft(v)
357 # TODO compile_type_table(v)
359 v
.add
(" public static RTClass get{rt_name}() \{")
360 v
.add
(" if(instance == null) \{")
361 v
.add
(" instance = new {rt_name}();")
363 v
.add
(" return instance;")
369 redef class MMethodDef
372 private fun rt_name
: String do
373 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"