nitj: implement static and polymorphic sends
[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 # Calls handling
350
351 # Compile a call within a callsite
352 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
353 var initializers = callsite.mpropdef.initializers
354 if not initializers.is_empty then
355 var recv = arguments.first
356
357 var i = 1
358 for p in initializers do
359 if p isa MMethod then
360 var args = [recv]
361 var msignature = p.intro.msignature
362 if msignature != null then
363 for x in msignature.mparameters do
364 args.add arguments[i]
365 i += 1
366 end
367 end
368 send(p, args)
369 else if p isa MAttribute then
370 info("NOT YET IMPLEMENTED {class_name}::compile_callsite for MAttribute `{p}`")
371 #self.write_attribute(p, recv, arguments[i])
372 i += 1
373 else abort
374 end
375 assert i == arguments.length
376
377 return send(callsite.mproperty, [recv])
378 end
379
380 return send(callsite.mproperty, arguments)
381 end
382
383 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
384 #
385 # This method is used to manage varargs in signatures and returns the real array
386 # of runtime variables to use in the call.
387 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable] do
388 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
389 var res = new Array[RuntimeVariable]
390 res.add(recv)
391
392 if msignature.arity == 0 then return res
393
394 if map == null then
395 assert args.length == msignature.arity
396 for ne in args do
397 res.add expr(ne, null)
398 end
399 return res
400 end
401
402 # Eval in order of arguments, not parameters
403 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
404 for ne in args do
405 exprs.add expr(ne, null)
406 end
407
408 # Fill `res` with the result of the evaluation according to the mapping
409 for i in [0..msignature.arity[ do
410 var param = msignature.mparameters[i]
411 var j = map.map.get_or_null(i)
412 if j == null then
413 # default value
414 res.add(null_instance)
415 continue
416 end
417 if param.is_vararg and map.vararg_decl > 0 then
418 var vararg = exprs.sub(j, map.vararg_decl)
419 var elttype = param.mtype
420 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
421 res.add(arg)
422 continue
423 end
424 res.add exprs[j]
425 end
426 return res
427 end
428
429 # Generate a static call on a method definition (no receiver needed).
430 fun static_call(mmethoddef: MMethodDef, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
431 var res: nullable RuntimeVariable
432 var ret = mmethoddef.msignature.as(not null).return_mtype
433 if ret == null then
434 res = null
435 else
436 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
437 res = self.new_var(ret)
438 end
439
440 # Autobox arguments
441 adapt_signature(mmethoddef, arguments)
442
443 var rt_name = mmethoddef.rt_name
444 if res == null then
445 add("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});")
446 return null
447 end
448 var ress = new_expr("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});", compiler.mainmodule.object_type)
449 assign(res, ress)
450 return res
451 end
452
453 # Generate a polymorphic send for `method` with `arguments`
454 fun send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
455 # Polymorphic send
456 return table_send(mmethod, arguments)
457 end
458
459
460 # Handle common special cases before doing the effective method invocation
461 # This methods handle the `==` and `!=` methods and the case of the null receiver.
462 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
463 # Client must not forget to close the } after them.
464 #
465 # The value returned is the result of the common special cases.
466 # If not null, client must compile it with the result of their own effective method invocation.
467 #
468 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
469 # is generated to cancel the effective method invocation that will follow
470 # TODO: find a better approach
471 private fun before_send(res: nullable RuntimeVariable, mmethod: MMethodDef, arguments: Array[RuntimeVariable]) do
472 var bool_type = compiler.mainmodule.bool_type
473 var recv = arguments.first
474 var consider_null = mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance"
475 if recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType then
476 add("if ({recv} == null || {recv}.is_null()) \{")
477 if mmethod.name == "==" or mmethod.name == "is_same_instance" then
478 if res == null then res = new_var(bool_type)
479 var arg = arguments[1]
480 if arg.mcasttype isa MNullableType then
481 add("{res} = ({arg} == null || {arg}.is_null());")
482 else if arg.mcasttype isa MNullType then
483 add("{res} = true; /* is null */")
484 else
485 add("{res} = false; /* {arg.inspect} cannot be null */")
486 end
487 else if mmethod.name == "!=" then
488 if res == null then res = new_var(bool_type)
489 # res = self.new_var(bool_type)
490 var arg = arguments[1]
491 if arg.mcasttype isa MNullableType then
492 add("{res} = ({arg} != null && !{arg}.is_null());")
493 else if arg.mcasttype isa MNullType then
494 add("{res} = false; /* is null */")
495 else
496 add("{res} = true; /* {arg.inspect} cannot be null */")
497 end
498 else
499 add_abort("Receiver is null")
500 ret(null_instance)
501 end
502 add("\} else \{")
503 else
504 add "\{"
505 add "/* recv ({recv}) cannot be null since it's a {recv.mcasttype}"
506 end
507 if consider_null then
508 var arg = arguments[1]
509 if arg.mcasttype isa MNullType then
510 if res == null then res = new_var(bool_type)
511 if mmethod.name == "!=" then
512 add("{res} = true; /* arg is null and recv is not */")
513 else # `==` and `is_same_instance`
514 add("{res} = false; /* arg is null but recv is not */")
515 end
516 add("\}") # closes the null case
517 add("if (false) \{") # what follow is useless, Javac will drop it
518 end
519 end
520 end
521
522 # Perform a method call through vft
523 private fun table_send(mmethod: TableCallable, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
524 var mdef: MMethodDef
525 var name: String
526 if mmethod isa MMethod then
527 mdef = mmethod.intro
528 name = mmethod.full_name
529 else if mmethod isa MMethodDef then
530 mdef = mmethod
531 name = mmethod.full_name
532 else
533 abort
534 end
535
536 var recv = arguments.first
537 var rect = mdef.mclassdef.bound_mtype
538 var msignature = mdef.msignature.as(not null)
539 msignature = msignature.resolve_for(rect, rect, compiler.mainmodule, true)
540 adapt_signature(mdef, arguments)
541
542 var res: nullable RuntimeVariable
543 var ret = msignature.return_mtype
544 if ret == null then
545 res = null
546 else
547 res = self.new_var(ret)
548 end
549
550 before_send(res, mdef, arguments)
551
552 add "/* concrete call to {mdef} */"
553 if res != null then
554 var ress = new_expr("{recv}.rtclass.vft.get(\"{name}\").exec(new RTVal[]\{{arguments.join(",")}\});", compiler.mainmodule.object_type)
555 assign(res, ress)
556 else
557 add("{recv}.rtclass.vft.get(\"{name}\").exec(new RTVal[]\{{arguments.join(",")}\});")
558 end
559
560 add("\}") # closes the null case
561
562 return res
563 end
564
565 # Code generation
566
567 # Add a line (will be suffixed by `\n`)
568 fun add(line: String) do file.lines.add("{line}\n")
569
570 # Add a new partial line (no `\n` suffix)
571 fun addn(line: String) do file.lines.add(line)
572
573 # Compile a statement (if any)
574 fun stmt(nexpr: nullable AExpr) do
575 if nexpr == null then return
576 var old = self.current_node
577 current_node = nexpr
578 nexpr.stmt(self)
579 current_node = old
580 end
581
582 # Compile an expression an return its result
583 # `mtype` is the expected return type, pass null if no specific type is expected.
584 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable do
585 var old = current_node
586 current_node = nexpr
587
588 var res = null
589 if nexpr.mtype != null then
590 res = nexpr.expr(self)
591 end
592
593 if mtype != null then
594 mtype = anchor(mtype)
595 res = autobox(res, mtype)
596 end
597
598 current_node = old
599 return res
600 end
601
602 # Correctly assign a left and a right value
603 # Boxing and unboxing is performed if required
604 fun assign(left, right: RuntimeVariable) do
605 add("{left} = {autobox(right, left.mtype)};")
606 end
607
608 # Return a new local RuntimeVariable initialized with the Java expression `jexpr`.
609 #
610 # `mtype` is used for the Java return variable initialization.
611 fun new_expr(jexpr: String, mtype: MType): RuntimeVariable do
612 var res = new_var(mtype)
613 add("{res} = {jexpr};")
614 return res
615 end
616
617 # Generate generic abort
618 #
619 # Used by aborts, asserts, casts, etc.
620 fun add_abort(message: String) do
621 add("System.err.print(\"Runtime error: {message}\");")
622 var node = current_node
623 if node != null then
624 add("System.err.print(\" ({node.location.short_location})\");")
625 end
626 add("System.err.println(\"\");")
627 add("System.exit(1);")
628 end
629
630 # Types handling
631
632 # Anchor a type to the main module and the current receiver
633 fun anchor(mtype: MType): MType do
634 if not mtype.need_anchor then return mtype
635 return mtype.anchor_to(compiler.mainmodule, frame.as(not null).receiver)
636 end
637
638 # Adapt the arguments of a method according to targetted `MMethodDef`
639 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) do
640 var msignature = m.msignature.as(not null).resolve_for(
641 m.mclassdef.bound_mtype,
642 m.mclassdef.bound_mtype,
643 m.mclassdef.mmodule, true)
644 args.first = autobox(args.first, compiler.mainmodule.object_type)
645 for i in [0..msignature.arity[ do
646 args[i+1] = autobox(args[i + 1], compiler.mainmodule.object_type)
647 end
648 end
649
650 # Box primitive `value` to `mtype`.
651 private fun box(value: RuntimeVariable, mtype: MType): RuntimeVariable do
652 if value.is_boxed then return value
653 var obj_type = compiler.mainmodule.object_type
654 if value.mtype isa MNullType then
655 return new_expr("new RTVal(null, null)", compiler.mainmodule.model.null_type)
656 end
657 var mbox = value.mtype.as(MClassType).mclass
658 return new_expr("new RTVal({mbox.rt_name}.get{mbox.rt_name}(), {value})", obj_type)
659 end
660
661 # Unbox primitive `value` to `mtype`.
662 private fun unbox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
663 if not value.is_boxed then return value
664 if not mtype.is_java_primitive then return value
665 if compiler.box_kinds.has(mtype) then
666 return new_expr("({mtype.java_type}){value}.value", mtype)
667 else
668 info "NOT YET IMPLEMENTED unbox for {value} ({mtype})"
669 abort
670 end
671 end
672
673 # Box or unbox primitive `value` to `mtype` if needed.
674 private fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
675 if mtype.is_java_primitive then return unbox(value, mtype)
676 return box(value, mtype)
677 end
678
679 # Can this `value` be a primitive Java value?
680 private fun can_be_primitive(value: RuntimeVariable): Bool do
681 var t = value.mcasttype.undecorate
682 if not t isa MClassType then return false
683 var k = t.mclass.kind
684 return k == interface_kind or t.is_java_primitive
685 end
686
687 # Native instances
688
689 # Generate an integer value
690 fun int_instance(value: Int): RuntimeVariable do
691 var t = compiler.mainmodule.int_type
692 return new RuntimeVariable(value.to_s, t, t)
693 end
694
695 # Generate a byte value
696 fun byte_instance(value: Byte): RuntimeVariable do
697 var t = compiler.mainmodule.byte_type
698 return new RuntimeVariable(value.to_s, t, t)
699 end
700
701 # Generate a char value
702 fun char_instance(value: Char): RuntimeVariable do
703 var t = compiler.mainmodule.char_type
704 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
705 end
706
707 # Generate a float value
708 #
709 # FIXME pass a Float, not a string
710 fun float_instance(value: String): RuntimeVariable do
711 var t = compiler.mainmodule.float_type
712 return new RuntimeVariable(value.to_s, t, t)
713 end
714
715 # Generate an integer value
716 fun bool_instance(value: Bool): RuntimeVariable do
717 var t = compiler.mainmodule.bool_type
718 return new RuntimeVariable(value.to_s, t, t)
719 end
720
721 # Generate the `null` value
722 fun null_instance: RuntimeVariable do
723 var t = compiler.mainmodule.model.null_type
724 return new RuntimeVariable("null", t, t)
725 end
726
727 # Get an instance of a array for a vararg
728 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable do
729 # TODO handle dynamic types
730 info("NOT YET IMPLEMENTED vararg_instance")
731 return null_instance
732 # TODO return array_instance(varargs, elttype)
733 end
734
735 # Utils
736
737 # Display a info message
738 fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
739 end
740
741 # A file containing Java code.
742 class JavaCodeFile
743
744 # File name
745 var filename: String
746
747 # Lines to write
748 var lines: List[String] = new List[String]
749 end
750
751 redef class MEntity
752 # A Java compatible name for `self`
753 private fun jname: String do return name.to_cmangle
754 end
755
756 # Handler for runtime classes generation
757 #
758 # We need 3 kinds of runtime structures:
759 # * `RTClass` to represent a global class
760 # * `RTMethod` to represent a method definition
761 # * `RTVal` to represent runtime variables
762 class JavaRuntimeModel
763
764 # Compile JavaRuntimeModel structures
765 fun compile_rtmodel(compiler: JavaCompiler) do
766 compile_rtclass(compiler)
767 compile_rtmethod(compiler)
768 compile_rtval(compiler)
769 end
770
771 # Compile the abstract runtime class structure
772 #
773 # Runtime classes have 3 attributes:
774 # * `class_name`: the class name as a String
775 # * `vft`: the virtual function table for the class (flattened)
776 # * `supers`: the super type table (used for type tests)
777 fun compile_rtclass(compiler: JavaCompiler) do
778 var v = compiler.new_visitor("RTClass.java")
779 v.add("import java.util.HashMap;")
780 v.add("public abstract class RTClass \{")
781 v.add(" public String class_name;")
782 v.add(" public HashMap<String, RTMethod> vft = new HashMap<>();")
783 v.add(" public HashMap<String, RTClass> supers = new HashMap<>();")
784 v.add(" protected RTClass() \{\}")
785 v.add("\}")
786 end
787
788 # Compile the abstract runtime method structure
789 #
790 # Method body is executed through the `exec` method:
791 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
792 # * `exec` always returns a RTVal (or null if the Nit return type is void)
793 fun compile_rtmethod(compiler: JavaCompiler) do
794 var v = compiler.new_visitor("RTMethod.java")
795 v.add("public abstract class RTMethod \{")
796 v.add(" protected RTMethod() \{\}")
797 v.add(" public abstract RTVal exec(RTVal[] args);")
798 v.add("\}")
799 end
800
801 # Compile the runtime value structure
802 #
803 # RTVal both represents object instances and primitives values:
804 # * object instances:
805 # * `rtclass` the class of the RTVal is instance of
806 # * `attrs` contains the attributes of the instance
807 # * primitive values:
808 # * `rtclass` represents the class of the primitive value Nit type
809 # * `value` contains the primitive value of the instance
810 # * null values:
811 # * they must have both `rtclass` and `value` as null
812 fun compile_rtval(compiler: JavaCompiler) do
813 var v = compiler.new_visitor("RTVal.java")
814 v.add("import java.util.HashMap;")
815 v.add("public class RTVal \{")
816 v.add(" public RTClass rtclass;")
817 v.add(" public HashMap<String, RTVal> attrs = new HashMap<>();")
818 v.add(" Object value;")
819 v.add(" public RTVal(RTClass rtclass) \{")
820 v.add(" this.rtclass = rtclass;")
821 v.add(" \}")
822 v.add(" public RTVal(RTClass rtclass, Object value) \{")
823 v.add(" this.rtclass = rtclass;")
824 v.add(" this.value = value;")
825 v.add(" \}")
826 v.add(" public boolean is_null() \{ return rtclass == null && value == null; \}")
827 v.add("\}")
828 end
829 end
830
831 # A runtime variable hold a runtime value in Java.
832 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
833 class RuntimeVariable
834
835 # The name of the variable in the Java code
836 var name: String
837
838 # The static type of the variable (as declard in Java)
839 var mtype: MType
840
841 # The current casted type of the variable (as known in Nit)
842 var mcasttype: MType is writable
843
844 # If the variable exaclty a mcasttype?
845 # false (usual value) means that the variable is a mcasttype or a subtype.
846 var is_exact: Bool = false is writable
847
848 # Is this variable declared as a RTVal or a Java primitive one?
849 var is_boxed = false
850
851 redef fun to_s do return name
852
853 redef fun inspect
854 do
855 var exact_str
856 if self.is_exact then
857 exact_str = " exact"
858 else
859 exact_str = ""
860 end
861 var type_str
862 if self.mtype == self.mcasttype then
863 type_str = "{mtype}{exact_str}"
864 else
865 type_str = "{mtype}({mcasttype}{exact_str})"
866 end
867 return "<{name}:{type_str}>"
868 end
869 end
870
871 # The static context of a visited property in a `JavaCompilerVisitor`
872 class JavaStaticFrame
873 # The associated visitor
874 var visitor: JavaCompilerVisitor
875
876 # The executed property.
877 # A Method in case of a call, an attribute in case of a default initialization.
878 var mpropdef: MPropDef
879
880 # The static type of the receiver
881 var receiver: MClassType
882
883 # Arguments of the method (the first is the receiver)
884 var arguments: Array[RuntimeVariable]
885
886 # The runtime_variable associated to the return (in a function)
887 var returnvar: nullable RuntimeVariable = null is writable
888
889 # The label at the end of the property
890 var returnlabel: nullable String = null is writable
891 end
892
893 redef class Location
894 # Return a shortened version of the location with `"{file}:{line_start}"`
895 fun short_location: String do
896 var file = self.file
897 if file == null then return "<no file>:{line_start}"
898 return "{file.filename.escape_to_c}:{line_start}"
899 end
900 end
901
902 redef class MType
903 # Return the Java type associated to a given Nit static type
904 fun java_type: String do return "RTVal"
905
906 # Is the associated Java type a primitive one?
907 #
908 # ENSURE `result == (java_type != "Object")`
909 var is_java_primitive: Bool is lazy do return java_type != "RTVal"
910 end
911
912 redef class MClassType
913
914 redef var java_type is lazy do
915 if mclass.name == "Int" then
916 return "int"
917 else if mclass.name == "Bool" then
918 return "boolean"
919 else if mclass.name == "Char" then
920 return "char"
921 else if mclass.name == "Float" then
922 return "double"
923 else if mclass.name == "Byte" then
924 return "byte"
925 else if mclass.name == "NativeString" then
926 return "String"
927 else if mclass.name == "NativeArray" then
928 return "Array"
929 end
930 return "RTVal"
931 end
932 end
933
934 redef class MClass
935
936 # Runtime name
937 private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}"
938
939 # Generate a Java RTClass for a Nit MClass
940 fun compile_to_java(v: JavaCompilerVisitor) do
941 v.add("public class {rt_name} extends RTClass \{")
942 v.add(" protected static RTClass instance;")
943 v.add(" private {rt_name}() \{")
944 v.add(" this.class_name = \"{name}\";")
945 compile_vft(v)
946 compile_type_table(v)
947 v.add(" \}")
948 v.add(" public static RTClass get{rt_name}() \{")
949 v.add(" if(instance == null) \{")
950 v.add(" instance = new {rt_name}();")
951 v.add(" \}")
952 v.add(" return instance;")
953 v.add(" \}")
954 v.add("\}")
955 end
956
957 # Compile the virtual function table for the mclass
958 private fun compile_vft(v: JavaCompilerVisitor) do
959 # TODO handle generics
960 if mclass_type.need_anchor then return
961 var mclassdefs = mclass_type.collect_mclassdefs(v.compiler.mainmodule).to_a
962 v.compiler.mainmodule.linearize_mclassdefs(mclassdefs)
963
964 var mainmodule = v.compiler.mainmodule
965 for mclassdef in mclassdefs.reversed do
966 for mprop in mclassdef.intro_mproperties do
967 var mpropdef = mprop.lookup_first_definition(mainmodule, intro.bound_mtype)
968 if not mpropdef isa MMethodDef then continue
969 var rt_name = mpropdef.rt_name
970 v.add("this.vft.put(\"{mprop.full_name}\", {rt_name}.get{rt_name}());")
971
972 # fill super next definitions
973 while mpropdef.has_supercall do
974 var prefix = mpropdef.full_name
975 mpropdef = mpropdef.lookup_next_definition(mainmodule, intro.bound_mtype)
976 rt_name = mpropdef.rt_name
977 v.add("this.vft.put(\"{prefix}\", {rt_name}.get{rt_name}());")
978 end
979 end
980 end
981 end
982
983 # Compile the type table for the MClass
984 fun compile_type_table(v: JavaCompilerVisitor) do
985 for pclass in in_hierarchy(v.compiler.mainmodule).greaters do
986 if pclass == self then
987 v.add("supers.put(\"{pclass.jname}\", this);")
988 else
989 v.add("supers.put(\"{pclass.jname}\", {pclass.rt_name}.get{pclass.rt_name}());")
990 end
991 end
992 end
993 end
994
995 # Used as a common type between MMethod and MMethodDef for `table_send`
996 private interface TableCallable
997 end
998
999 redef class MMethod
1000 super TableCallable
1001 end
1002
1003 redef class MMethodDef
1004 super TableCallable
1005
1006 # Runtime name
1007 private fun rt_name: String do
1008 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
1009 end
1010
1011 # Generate a Java RTMethod for `self`
1012 fun compile_to_java(v: JavaCompilerVisitor) do
1013 v.add("public class {rt_name} extends RTMethod \{")
1014 v.add(" protected static RTMethod instance;")
1015 v.add(" public static RTMethod get{rt_name}() \{")
1016 v.add(" if(instance == null) \{")
1017 v.add(" instance = new {rt_name}();")
1018 v.add(" \}")
1019 v.add(" return instance;")
1020 v.add(" \}")
1021 v.add(" @Override")
1022 v.add(" public RTVal exec(RTVal[] args) \{")
1023 compile_inside_to_java(v)
1024 v.add(" \}")
1025 v.add("\}")
1026 end
1027
1028 # Compile the body of this function
1029 fun compile_inside_to_java(v: JavaCompilerVisitor) do
1030
1031 var modelbuilder = v.compiler.modelbuilder
1032 var node = modelbuilder.mpropdef2node(self)
1033
1034 if is_abstract then
1035 # TODO compile abstract
1036 v.info("NOT YET IMPLEMENTED call to abstract method")
1037 v.add("return null;")
1038 return
1039 end
1040
1041 if node isa APropdef then
1042 node.compile_to_java(v, self)
1043 else if node isa AClassdef then
1044 # TODO compile attributes
1045 v.info("NOT YET IMPLEMENTED attribute handling")
1046 v.add("return null;")
1047 else
1048 abort
1049 end
1050 end
1051 end
1052
1053 redef class APropdef
1054
1055 # Compile that property definition to java code
1056 fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1057 v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
1058 end
1059 end
1060
1061 redef class AMethPropdef
1062 redef fun compile_to_java(v, mpropdef) do
1063 # TODO Call the implicit super-init
1064
1065 # Compile intern methods
1066 if mpropdef.is_intern then
1067 v.info("NOT YET IMPLEMENTED {class_name}::compile_intern")
1068 # TODO if compile_intern_to_java(v, mpropdef, arguments) then return
1069 v.add("return null;")
1070 return
1071 end
1072
1073 # Compile block if any
1074 var n_block = n_block
1075 if n_block != null then
1076 v.stmt(n_block)
1077 return
1078 end
1079 end
1080 end
1081
1082 redef class AExpr
1083 # Try to compile self as an expression
1084 # Do not call this method directly, use `v.expr` instead
1085 private fun expr(v: JavaCompilerVisitor): nullable RuntimeVariable do
1086 v.info("NOT YET IMPLEMENTED {class_name}::expr")
1087 return null
1088 end
1089
1090 # Try to compile self as a statement
1091 # Do not call this method directly, use `v.stmt` instead
1092 private fun stmt(v: JavaCompilerVisitor) do expr(v)
1093 end
1094
1095 redef class ABlockExpr
1096 redef fun stmt(v)
1097 do
1098 for e in self.n_expr do v.stmt(e)
1099 end
1100 redef fun expr(v)
1101 do
1102 var last = self.n_expr.last
1103 for e in self.n_expr do
1104 if e == last then break
1105 v.stmt(e)
1106 end
1107 return v.expr(last, null)
1108 end
1109 end
1110
1111 redef class ASendExpr
1112 redef fun expr(v) do
1113 var recv = v.expr(n_expr, null)
1114 var callsite = callsite.as(not null)
1115 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, raw_arguments)
1116 return v.compile_callsite(callsite, args)
1117 end
1118 end
1119
1120 redef class ASelfExpr
1121 redef fun expr(v) do return v.frame.as(not null).arguments.first
1122 end
1123
1124 redef class AImplicitSelfExpr
1125 redef fun expr(v) do return v.frame.as(not null).arguments.first
1126 end
1127
1128 redef class AVardeclExpr
1129 redef fun stmt(v) do
1130 var variable = self.variable.as(not null)
1131 var ne = self.n_expr
1132 var decl = v.variable(variable)
1133 if ne != null then
1134 var i = v.expr(ne, variable.declared_type)
1135 v.assign(decl, i)
1136 end
1137 end
1138 end
1139
1140 redef class AVarExpr
1141 redef fun expr(v) do
1142 return v.variable(self.variable.as(not null))
1143 end
1144 end
1145
1146 redef class AVarAssignExpr
1147 redef fun expr(v) do
1148 var variable = self.variable.as(not null)
1149 var i = v.expr(self.n_value, variable.declared_type)
1150 v.assign(v.variable(variable), i)
1151 return i
1152 end
1153 end
1154
1155 redef class AIntExpr
1156 redef fun expr(v) do return v.int_instance(self.value.as(not null))
1157 end
1158
1159 redef class AByteExpr
1160 redef fun expr(v) do return v.byte_instance(self.value.as(not null))
1161 end
1162
1163 redef class AFloatExpr
1164 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
1165 end
1166
1167 redef class ACharExpr
1168 redef fun expr(v) do return v.char_instance(self.value.as(not null))
1169 end
1170
1171 redef class ATrueExpr
1172 redef fun expr(v) do return v.bool_instance(true)
1173 end
1174
1175 redef class AFalseExpr
1176 redef fun expr(v) do return v.bool_instance(false)
1177 end
1178
1179 redef class ANullExpr
1180 redef fun expr(v) do return v.null_instance
1181 end
1182
1183 redef class AAbortExpr
1184 redef fun stmt(v) do v.add_abort("Aborted")
1185 end
1186
1187 redef class ADebugTypeExpr
1188 redef fun stmt(v) do end # do nothing
1189 end