nitj: implement boxing for Java primitives
[nit.git] / src / compiler / java_compiler.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # Compile Nit code to Java code
16 #
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
21 #
22 # More details are given in the documentation of these 3 classes.
23 #
24 # TODO Factorize with `abstract_compiler`
25 module java_compiler
26
27 import rapid_type_analysis
28 import frontend
29
30 redef class ToolContext
31
32 # Where to output the generated binary
33 var opt_output = new OptionString("Output file", "-o", "--output")
34
35 # Where to output tmp files
36 var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
37
38 redef init do
39 super
40 option_context.add_option(opt_output, opt_compile_dir)
41 end
42 end
43
44 redef class ModelBuilder
45
46 # Start the Java compiler
47 fun run_java_compiler(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis) do
48 var time0 = get_time
49 toolcontext.info("*** GENERATING JAVA ***", 1)
50
51 var compiler = new JavaCompiler(mainmodule, self, runtime_type_analysis)
52 compiler.do_compilation
53
54 var time1 = get_time
55 toolcontext.info("*** END GENERATING JAVA: {time1-time0} ***", 2)
56 write_and_make(compiler)
57 end
58
59 # Write Java code and compile it into an executable jar
60 fun write_and_make(compiler: JavaCompiler) do
61 var time0 = get_time
62 toolcontext.info("*** WRITING JAVA ***", 1)
63
64 compiler.compile_dir.mkdir
65
66 var jfiles = write_java_files(compiler)
67
68 var time1 = get_time
69 toolcontext.info("*** END WRITING JAVA: {time1-time0} ***", 2)
70
71 time0 = time1
72 toolcontext.info("*** COMPILING JAVA ***", 1)
73
74 build_with_make(compiler, jfiles)
75 write_shell_script(compiler)
76
77 time1 = get_time
78 toolcontext.info("*** END COMPILING JAVA: {time1-time0} ***", 2)
79 end
80
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)
87 file.close
88 jfiles.add(f.filename)
89 end
90 return jfiles
91 end
92
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)
100 var res
101 if toolcontext.verbose_level >= 3 then
102 res = sys.system("make -B -C {compile_dir} -f {outname}.mk 2>&1")
103 else
104 res = sys.system("make -B -C {compile_dir} -f {outname}.mk 2>&1 > /dev/null")
105 end
106 if res != 0 then toolcontext.error(null, "make failed! Error code: {res}.")
107 end
108
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")
114
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)
120
121 makefile.write("JC = javac\n")
122 makefile.write("JAR = jar\n\n")
123
124 makefile.write("all: {outpath}.jar\n\n")
125
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")
128
129 makefile.write("{compiler.mainmodule.jname}_Main.class:\n")
130 makefile.write("\t$(JC) {jfiles.join(" ")}\n\n")
131
132 makefile.write("clean:\n")
133 makefile.write("\trm {ofiles.join(" ")} 2>/dev/null\n\n")
134
135 makefile.close
136 toolcontext.info("Generated makefile: {makename}", 2)
137 end
138
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")
146 maniffile.close
147 end
148
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")
155 shfile.close
156 sys.system("chmod +x {outname}")
157 end
158 end
159
160 # Compiler that translates Nit code to Java code
161 class JavaCompiler
162 # The main module of the program currently compiled
163 var mainmodule: MModule
164
165 # Modelbuilder used to know the model and the AST
166 var modelbuilder: ModelBuilder
167
168 # The result of the RTA (used to know live types and methods)
169 var runtime_type_analysis: RapidTypeAnalysis
170
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"
175 return dir
176 end
177
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
182 return name
183 end
184
185 # The list of all associated files
186 # Used to generate .java files
187 var files: Array[JavaCodeFile] = new Array[JavaCodeFile]
188
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)
193 files.add(file)
194 return file
195 end
196
197 # Kind of visitor to use
198 type VISITOR: JavaCompilerVisitor
199
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))
203 end
204
205 # RuntimeModel representation
206 private var rt_model: JavaRuntimeModel is lazy do return new JavaRuntimeModel
207
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)
212 compile_box_kinds
213
214 # compile class structures
215 compile_mclasses_to_java
216
217 # compile method structures
218 compile_mmethods_to_java
219
220 # TODO compile main
221 modelbuilder.toolcontext.info("NOT YET IMPLEMENTED", 0)
222 end
223
224 # Prepare the boxes used to represent Java primitive types
225 fun compile_box_kinds do
226 # Collect all bas box class
227 # FIXME: this is not completely fine with a separate compilation scheme
228 for classname in ["Int", "Bool", "Byte", "Char", "Float"] do
229 var classes = mainmodule.model.get_mclasses_by_name(classname)
230 if classes == null then continue
231 assert classes.length == 1 else print classes.join(", ")
232 box_kinds.add(classes.first.mclass_type)
233 end
234 end
235
236 # Types of boxes used to represent Java primitive types
237 var box_kinds = new Array[MClassType]
238
239 # Generate a `RTClass` for each `MClass` found in model
240 #
241 # This is a global phase because we need to know all the program to build
242 # attributes, fill vft and type table.
243 fun compile_mclasses_to_java do
244 for mclass in mainmodule.model.mclasses do
245 mclass.compile_to_java(new_visitor("{mclass.rt_name}.java"))
246 end
247 end
248
249 # Generate a `RTMethod` for each `MMethodDef` found in model
250 #
251 # This is a separate phase.
252 fun compile_mmethods_to_java do
253 for mmodule in mainmodule.in_importation.greaters do
254 for mclassdef in mmodule.mclassdefs do
255 for mdef in mclassdef.mpropdefs do
256 if mdef isa MMethodDef then
257 mdef.compile_to_java(new_visitor("{mdef.rt_name}.java"))
258 end
259 end
260 end
261 end
262 end
263 end
264
265 # The class visiting the AST
266 #
267 # A visitor is attached to one JavaCodeFile it writes into.
268 class JavaCompilerVisitor
269 super Visitor
270
271 # JavaCompiler used with this visitor
272 type COMPILER: JavaCompiler
273
274 # The associated compiler
275 var compiler: JavaCompiler
276
277 # The file to write generated code into
278 var file: JavaCodeFile
279
280 # Names handling
281
282 private var names = new HashSet[String]
283 private var last: Int = 0
284
285 # Return a new name based on `s` and unique in the visitor
286 fun get_name(s: String): String do
287 if not self.names.has(s) then
288 self.names.add(s)
289 return s
290 end
291 var i = self.last + 1
292 loop
293 var s2 = s + i.to_s
294 if not self.names.has(s2) then
295 self.last = i
296 self.names.add(s2)
297 return s2
298 end
299 i = i + 1
300 end
301 end
302
303 # Variables handling
304
305 # Registered variables
306 protected var variables = new HashMap[Variable, RuntimeVariable]
307
308 # Return the local RuntimeVariable associated to a Nit local variable
309 fun variable(variable: Variable): RuntimeVariable do
310 if variables.has_key(variable) then
311 return variables[variable]
312 else
313 var name = get_name("var_{variable.name}")
314 var mtype = variable.declared_type.as(not null)
315 mtype = anchor(mtype)
316 var res = decl_var(name, mtype)
317 variables[variable] = res
318 return res
319 end
320 end
321
322 # Return a new uninitialized local RuntimeVariable with `name`
323 fun decl_var(name: String, mtype: MType): RuntimeVariable do
324 var res = new RuntimeVariable(name, mtype, mtype)
325 res.is_boxed = not mtype.is_java_primitive
326 add("{mtype.java_type} {name} /* : {mtype} */;")
327 return res
328 end
329
330 # Return a new uninitialized local RuntimeVariable
331 fun new_var(mtype: MType): RuntimeVariable do
332 mtype = anchor(mtype)
333 var name = self.get_name("var")
334 return decl_var(name, mtype)
335 end
336
337 # Calls handling
338
339 # The current `JavaStaticFrame`
340 var frame: nullable JavaStaticFrame = null is writable
341
342 # Return a new local RuntimeVariable initialized from `args[0]`
343 fun new_recv(mtype: MType): RuntimeVariable do
344 var res = new_var(mtype)
345 add("{res} = args[0];")
346 return res
347 end
348
349 # Code generation
350
351 # Add a line (will be suffixed by `\n`)
352 fun add(line: String) do file.lines.add("{line}\n")
353
354 # Add a new partial line (no `\n` suffix)
355 fun addn(line: String) do file.lines.add(line)
356
357 # Compile a statement (if any)
358 fun stmt(nexpr: nullable AExpr) do
359 if nexpr == null then return
360 var old = self.current_node
361 current_node = nexpr
362 nexpr.stmt(self)
363 current_node = old
364 end
365
366 # Compile an expression an return its result
367 # `mtype` is the expected return type, pass null if no specific type is expected.
368 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable do
369 var old = current_node
370 current_node = nexpr
371
372 var res = null
373 if nexpr.mtype != null then
374 res = nexpr.expr(self)
375 end
376
377 if mtype != null then
378 mtype = anchor(mtype)
379 res = autobox(res, mtype)
380 end
381
382 current_node = old
383 return res
384 end
385
386 # Correctly assign a left and a right value
387 # Boxing and unboxing is performed if required
388 fun assign(left, right: RuntimeVariable) do
389 add("{left} = {autobox(right, left.mtype)};")
390 end
391
392 # Return a new local RuntimeVariable initialized with the Java expression `jexpr`.
393 #
394 # `mtype` is used for the Java return variable initialization.
395 fun new_expr(jexpr: String, mtype: MType): RuntimeVariable do
396 var res = new_var(mtype)
397 add("{res} = {jexpr};")
398 return res
399 end
400
401 # Generate generic abort
402 #
403 # Used by aborts, asserts, casts, etc.
404 fun add_abort(message: String) do
405 add("System.err.print(\"Runtime error: {message}\");")
406 var node = current_node
407 if node != null then
408 add("System.err.print(\" ({node.location.short_location})\");")
409 end
410 add("System.err.println(\"\");")
411 add("System.exit(1);")
412 end
413
414 # Types handling
415
416 # Anchor a type to the main module and the current receiver
417 fun anchor(mtype: MType): MType do
418 if not mtype.need_anchor then return mtype
419 return mtype.anchor_to(compiler.mainmodule, frame.as(not null).receiver)
420 end
421
422 # Adapt the arguments of a method according to targetted `MMethodDef`
423 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) do
424 var msignature = m.msignature.as(not null).resolve_for(
425 m.mclassdef.bound_mtype,
426 m.mclassdef.bound_mtype,
427 m.mclassdef.mmodule, true)
428 args.first = autobox(args.first, compiler.mainmodule.object_type)
429 for i in [0..msignature.arity[ do
430 args[i+1] = autobox(args[i + 1], compiler.mainmodule.object_type)
431 end
432 end
433
434 # Box primitive `value` to `mtype`.
435 private fun box(value: RuntimeVariable, mtype: MType): RuntimeVariable do
436 if value.is_boxed then return value
437 var obj_type = compiler.mainmodule.object_type
438 if value.mtype isa MNullType then
439 return new_expr("new RTVal(null, null)", compiler.mainmodule.model.null_type)
440 end
441 var mbox = value.mtype.as(MClassType).mclass
442 return new_expr("new RTVal({mbox.rt_name}.get{mbox.rt_name}(), {value})", obj_type)
443 end
444
445 # Unbox primitive `value` to `mtype`.
446 private fun unbox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
447 if not value.is_boxed then return value
448 if not mtype.is_java_primitive then return value
449 if compiler.box_kinds.has(mtype) then
450 return new_expr("({mtype.java_type}){value}.value", mtype)
451 else
452 info "NOT YET IMPLEMENTED unbox for {value} ({mtype})"
453 abort
454 end
455 end
456
457 # Box or unbox primitive `value` to `mtype` if needed.
458 private fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
459 if mtype.is_java_primitive then return unbox(value, mtype)
460 return box(value, mtype)
461 end
462
463 # Can this `value` be a primitive Java value?
464 private fun can_be_primitive(value: RuntimeVariable): Bool do
465 var t = value.mcasttype.undecorate
466 if not t isa MClassType then return false
467 var k = t.mclass.kind
468 return k == interface_kind or t.is_java_primitive
469 end
470
471 # Native instances
472
473 # Generate an integer value
474 fun int_instance(value: Int): RuntimeVariable do
475 var t = compiler.mainmodule.int_type
476 return new RuntimeVariable(value.to_s, t, t)
477 end
478
479 # Generate a byte value
480 fun byte_instance(value: Byte): RuntimeVariable do
481 var t = compiler.mainmodule.byte_type
482 return new RuntimeVariable(value.to_s, t, t)
483 end
484
485 # Generate a char value
486 fun char_instance(value: Char): RuntimeVariable do
487 var t = compiler.mainmodule.char_type
488 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
489 end
490
491 # Generate a float value
492 #
493 # FIXME pass a Float, not a string
494 fun float_instance(value: String): RuntimeVariable do
495 var t = compiler.mainmodule.float_type
496 return new RuntimeVariable(value.to_s, t, t)
497 end
498
499 # Generate an integer value
500 fun bool_instance(value: Bool): RuntimeVariable do
501 var t = compiler.mainmodule.bool_type
502 return new RuntimeVariable(value.to_s, t, t)
503 end
504
505 # Generate the `null` value
506 fun null_instance: RuntimeVariable do
507 var t = compiler.mainmodule.model.null_type
508 return new RuntimeVariable("null", t, t)
509 end
510
511 # Utils
512
513 # Display a info message
514 fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
515 end
516
517 # A file containing Java code.
518 class JavaCodeFile
519
520 # File name
521 var filename: String
522
523 # Lines to write
524 var lines: List[String] = new List[String]
525 end
526
527 redef class MEntity
528 # A Java compatible name for `self`
529 private fun jname: String do return name.to_cmangle
530 end
531
532 # Handler for runtime classes generation
533 #
534 # We need 3 kinds of runtime structures:
535 # * `RTClass` to represent a global class
536 # * `RTMethod` to represent a method definition
537 # * `RTVal` to represent runtime variables
538 class JavaRuntimeModel
539
540 # Compile JavaRuntimeModel structures
541 fun compile_rtmodel(compiler: JavaCompiler) do
542 compile_rtclass(compiler)
543 compile_rtmethod(compiler)
544 compile_rtval(compiler)
545 end
546
547 # Compile the abstract runtime class structure
548 #
549 # Runtime classes have 3 attributes:
550 # * `class_name`: the class name as a String
551 # * `vft`: the virtual function table for the class (flattened)
552 # * `supers`: the super type table (used for type tests)
553 fun compile_rtclass(compiler: JavaCompiler) do
554 var v = compiler.new_visitor("RTClass.java")
555 v.add("import java.util.HashMap;")
556 v.add("public abstract class RTClass \{")
557 v.add(" public String class_name;")
558 v.add(" public HashMap<String, RTMethod> vft = new HashMap<>();")
559 v.add(" public HashMap<String, RTClass> supers = new HashMap<>();")
560 v.add(" protected RTClass() \{\}")
561 v.add("\}")
562 end
563
564 # Compile the abstract runtime method structure
565 #
566 # Method body is executed through the `exec` method:
567 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
568 # * `exec` always returns a RTVal (or null if the Nit return type is void)
569 fun compile_rtmethod(compiler: JavaCompiler) do
570 var v = compiler.new_visitor("RTMethod.java")
571 v.add("public abstract class RTMethod \{")
572 v.add(" protected RTMethod() \{\}")
573 v.add(" public abstract RTVal exec(RTVal[] args);")
574 v.add("\}")
575 end
576
577 # Compile the runtime value structure
578 #
579 # RTVal both represents object instances and primitives values:
580 # * object instances:
581 # * `rtclass` the class of the RTVal is instance of
582 # * `attrs` contains the attributes of the instance
583 # * primitive values:
584 # * `rtclass` represents the class of the primitive value Nit type
585 # * `value` contains the primitive value of the instance
586 # * null values:
587 # * they must have both `rtclass` and `value` as null
588 fun compile_rtval(compiler: JavaCompiler) do
589 var v = compiler.new_visitor("RTVal.java")
590 v.add("import java.util.HashMap;")
591 v.add("public class RTVal \{")
592 v.add(" public RTClass rtclass;")
593 v.add(" public HashMap<String, RTVal> attrs = new HashMap<>();")
594 v.add(" Object value;")
595 v.add(" public RTVal(RTClass rtclass) \{")
596 v.add(" this.rtclass = rtclass;")
597 v.add(" \}")
598 v.add(" public RTVal(RTClass rtclass, Object value) \{")
599 v.add(" this.rtclass = rtclass;")
600 v.add(" this.value = value;")
601 v.add(" \}")
602 v.add(" public boolean is_null() \{ return rtclass == null && value == null; \}")
603 v.add("\}")
604 end
605 end
606
607 # A runtime variable hold a runtime value in Java.
608 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
609 class RuntimeVariable
610
611 # The name of the variable in the Java code
612 var name: String
613
614 # The static type of the variable (as declard in Java)
615 var mtype: MType
616
617 # The current casted type of the variable (as known in Nit)
618 var mcasttype: MType is writable
619
620 # If the variable exaclty a mcasttype?
621 # false (usual value) means that the variable is a mcasttype or a subtype.
622 var is_exact: Bool = false is writable
623
624 # Is this variable declared as a RTVal or a Java primitive one?
625 var is_boxed = false
626
627 redef fun to_s do return name
628
629 redef fun inspect
630 do
631 var exact_str
632 if self.is_exact then
633 exact_str = " exact"
634 else
635 exact_str = ""
636 end
637 var type_str
638 if self.mtype == self.mcasttype then
639 type_str = "{mtype}{exact_str}"
640 else
641 type_str = "{mtype}({mcasttype}{exact_str})"
642 end
643 return "<{name}:{type_str}>"
644 end
645 end
646
647 # The static context of a visited property in a `JavaCompilerVisitor`
648 class JavaStaticFrame
649 # The associated visitor
650 var visitor: JavaCompilerVisitor
651
652 # The executed property.
653 # A Method in case of a call, an attribute in case of a default initialization.
654 var mpropdef: MPropDef
655
656 # The static type of the receiver
657 var receiver: MClassType
658
659 # Arguments of the method (the first is the receiver)
660 var arguments: Array[RuntimeVariable]
661
662 # The runtime_variable associated to the return (in a function)
663 var returnvar: nullable RuntimeVariable = null is writable
664
665 # The label at the end of the property
666 var returnlabel: nullable String = null is writable
667 end
668
669 redef class Location
670 # Return a shortened version of the location with `"{file}:{line_start}"`
671 fun short_location: String do
672 var file = self.file
673 if file == null then return "<no file>:{line_start}"
674 return "{file.filename.escape_to_c}:{line_start}"
675 end
676 end
677
678 redef class MType
679 # Return the Java type associated to a given Nit static type
680 fun java_type: String do return "RTVal"
681
682 # Is the associated Java type a primitive one?
683 #
684 # ENSURE `result == (java_type != "Object")`
685 var is_java_primitive: Bool is lazy do return java_type != "RTVal"
686 end
687
688 redef class MClassType
689
690 redef var java_type is lazy do
691 if mclass.name == "Int" then
692 return "int"
693 else if mclass.name == "Bool" then
694 return "boolean"
695 else if mclass.name == "Char" then
696 return "char"
697 else if mclass.name == "Float" then
698 return "double"
699 else if mclass.name == "Byte" then
700 return "byte"
701 else if mclass.name == "NativeString" then
702 return "String"
703 else if mclass.name == "NativeArray" then
704 return "Array"
705 end
706 return "RTVal"
707 end
708 end
709
710 redef class MClass
711
712 # Runtime name
713 private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}"
714
715 # Generate a Java RTClass for a Nit MClass
716 fun compile_to_java(v: JavaCompilerVisitor) do
717 v.add("public class {rt_name} extends RTClass \{")
718 v.add(" protected static RTClass instance;")
719 v.add(" private {rt_name}() \{")
720 v.add(" this.class_name = \"{name}\";")
721 compile_vft(v)
722 compile_type_table(v)
723 v.add(" \}")
724 v.add(" public static RTClass get{rt_name}() \{")
725 v.add(" if(instance == null) \{")
726 v.add(" instance = new {rt_name}();")
727 v.add(" \}")
728 v.add(" return instance;")
729 v.add(" \}")
730 v.add("\}")
731 end
732
733 # Compile the virtual function table for the mclass
734 private fun compile_vft(v: JavaCompilerVisitor) do
735 # TODO handle generics
736 if mclass_type.need_anchor then return
737 var mclassdefs = mclass_type.collect_mclassdefs(v.compiler.mainmodule).to_a
738 v.compiler.mainmodule.linearize_mclassdefs(mclassdefs)
739
740 var mainmodule = v.compiler.mainmodule
741 for mclassdef in mclassdefs.reversed do
742 for mprop in mclassdef.intro_mproperties do
743 var mpropdef = mprop.lookup_first_definition(mainmodule, intro.bound_mtype)
744 if not mpropdef isa MMethodDef then continue
745 var rt_name = mpropdef.rt_name
746 v.add("this.vft.put(\"{mprop.full_name}\", {rt_name}.get{rt_name}());")
747
748 # fill super next definitions
749 while mpropdef.has_supercall do
750 var prefix = mpropdef.full_name
751 mpropdef = mpropdef.lookup_next_definition(mainmodule, intro.bound_mtype)
752 rt_name = mpropdef.rt_name
753 v.add("this.vft.put(\"{prefix}\", {rt_name}.get{rt_name}());")
754 end
755 end
756 end
757 end
758
759 # Compile the type table for the MClass
760 fun compile_type_table(v: JavaCompilerVisitor) do
761 for pclass in in_hierarchy(v.compiler.mainmodule).greaters do
762 if pclass == self then
763 v.add("supers.put(\"{pclass.jname}\", this);")
764 else
765 v.add("supers.put(\"{pclass.jname}\", {pclass.rt_name}.get{pclass.rt_name}());")
766 end
767 end
768 end
769 end
770
771 redef class MMethodDef
772
773 # Runtime name
774 private fun rt_name: String do
775 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
776 end
777
778 # Generate a Java RTMethod for `self`
779 fun compile_to_java(v: JavaCompilerVisitor) do
780 v.add("public class {rt_name} extends RTMethod \{")
781 v.add(" protected static RTMethod instance;")
782 v.add(" public static RTMethod get{rt_name}() \{")
783 v.add(" if(instance == null) \{")
784 v.add(" instance = new {rt_name}();")
785 v.add(" \}")
786 v.add(" return instance;")
787 v.add(" \}")
788 v.add(" @Override")
789 v.add(" public RTVal exec(RTVal[] args) \{")
790 compile_inside_to_java(v)
791 v.add(" \}")
792 v.add("\}")
793 end
794
795 # Compile the body of this function
796 fun compile_inside_to_java(v: JavaCompilerVisitor) do
797
798 var modelbuilder = v.compiler.modelbuilder
799 var node = modelbuilder.mpropdef2node(self)
800
801 if is_abstract then
802 # TODO compile abstract
803 v.info("NOT YET IMPLEMENTED call to abstract method")
804 v.add("return null;")
805 return
806 end
807
808 if node isa APropdef then
809 node.compile_to_java(v, self)
810 else if node isa AClassdef then
811 # TODO compile attributes
812 v.info("NOT YET IMPLEMENTED attribute handling")
813 v.add("return null;")
814 else
815 abort
816 end
817 end
818 end
819
820 redef class APropdef
821
822 # Compile that property definition to java code
823 fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
824 v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
825 end
826 end
827
828 redef class AMethPropdef
829 redef fun compile_to_java(v, mpropdef) do
830 # TODO Call the implicit super-init
831
832 # Compile intern methods
833 if mpropdef.is_intern then
834 v.info("NOT YET IMPLEMENTED {class_name}::compile_intern")
835 # TODO if compile_intern_to_java(v, mpropdef, arguments) then return
836 v.add("return null;")
837 return
838 end
839
840 # Compile block if any
841 var n_block = n_block
842 if n_block != null then
843 v.stmt(n_block)
844 return
845 end
846 end
847 end
848
849 redef class AExpr
850 # Try to compile self as an expression
851 # Do not call this method directly, use `v.expr` instead
852 private fun expr(v: JavaCompilerVisitor): nullable RuntimeVariable do
853 v.info("NOT YET IMPLEMENTED {class_name}::expr")
854 return null
855 end
856
857 # Try to compile self as a statement
858 # Do not call this method directly, use `v.stmt` instead
859 private fun stmt(v: JavaCompilerVisitor) do expr(v)
860 end
861
862 redef class ABlockExpr
863 redef fun stmt(v)
864 do
865 for e in self.n_expr do v.stmt(e)
866 end
867 redef fun expr(v)
868 do
869 var last = self.n_expr.last
870 for e in self.n_expr do
871 if e == last then break
872 v.stmt(e)
873 end
874 return v.expr(last, null)
875 end
876 end
877
878 redef class ASelfExpr
879 redef fun expr(v) do return v.frame.as(not null).arguments.first
880 end
881
882 redef class AImplicitSelfExpr
883 redef fun expr(v) do return v.frame.as(not null).arguments.first
884 end
885
886 redef class AVardeclExpr
887 redef fun stmt(v) do
888 var variable = self.variable.as(not null)
889 var ne = self.n_expr
890 var decl = v.variable(variable)
891 if ne != null then
892 var i = v.expr(ne, variable.declared_type)
893 v.assign(decl, i)
894 end
895 end
896 end
897
898 redef class AVarExpr
899 redef fun expr(v) do
900 return v.variable(self.variable.as(not null))
901 end
902 end
903
904 redef class AVarAssignExpr
905 redef fun expr(v) do
906 var variable = self.variable.as(not null)
907 var i = v.expr(self.n_value, variable.declared_type)
908 v.assign(v.variable(variable), i)
909 return i
910 end
911 end
912
913 redef class AIntExpr
914 redef fun expr(v) do return v.int_instance(self.value.as(not null))
915 end
916
917 redef class AByteExpr
918 redef fun expr(v) do return v.byte_instance(self.value.as(not null))
919 end
920
921 redef class AFloatExpr
922 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
923 end
924
925 redef class ACharExpr
926 redef fun expr(v) do return v.char_instance(self.value.as(not null))
927 end
928
929 redef class ATrueExpr
930 redef fun expr(v) do return v.bool_instance(true)
931 end
932
933 redef class AFalseExpr
934 redef fun expr(v) do return v.bool_instance(false)
935 end
936
937 redef class ANullExpr
938 redef fun expr(v) do return v.null_instance
939 end
940
941 redef class AAbortExpr
942 redef fun stmt(v) do v.add_abort("Aborted")
943 end
944
945 redef class ADebugTypeExpr
946 redef fun stmt(v) do end # do nothing
947 end