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