nitj: implement instance initialization and ANewExprs
[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 # Nit instances
758
759 # Generate a alloc-instance + init-attributes
760 fun init_instance(mtype: MClassType): RuntimeVariable do
761 var recv = new_recv(mtype)
762 var mclass = mtype.mclass
763 add("{recv} = new RTVal({mclass.rt_name}.get{mclass.rt_name}());")
764 # TODO init attributes
765 return recv
766 end
767
768 # Utils
769
770 # Display a info message
771 fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
772 end
773
774 # A file containing Java code.
775 class JavaCodeFile
776
777 # File name
778 var filename: String
779
780 # Lines to write
781 var lines: List[String] = new List[String]
782 end
783
784 redef class MEntity
785 # A Java compatible name for `self`
786 private fun jname: String do return name.to_cmangle
787 end
788
789 # Handler for runtime classes generation
790 #
791 # We need 3 kinds of runtime structures:
792 # * `RTClass` to represent a global class
793 # * `RTMethod` to represent a method definition
794 # * `RTVal` to represent runtime variables
795 class JavaRuntimeModel
796
797 # Compile JavaRuntimeModel structures
798 fun compile_rtmodel(compiler: JavaCompiler) do
799 compile_rtclass(compiler)
800 compile_rtmethod(compiler)
801 compile_rtval(compiler)
802 end
803
804 # Compile the abstract runtime class structure
805 #
806 # Runtime classes have 3 attributes:
807 # * `class_name`: the class name as a String
808 # * `vft`: the virtual function table for the class (flattened)
809 # * `supers`: the super type table (used for type tests)
810 fun compile_rtclass(compiler: JavaCompiler) do
811 var v = compiler.new_visitor("RTClass.java")
812 v.add("import java.util.HashMap;")
813 v.add("public abstract class RTClass \{")
814 v.add(" public String class_name;")
815 v.add(" public HashMap<String, RTMethod> vft = new HashMap<>();")
816 v.add(" public HashMap<String, RTClass> supers = new HashMap<>();")
817 v.add(" protected RTClass() \{\}")
818 v.add("\}")
819 end
820
821 # Compile the abstract runtime method structure
822 #
823 # Method body is executed through the `exec` method:
824 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
825 # * `exec` always returns a RTVal (or null if the Nit return type is void)
826 fun compile_rtmethod(compiler: JavaCompiler) do
827 var v = compiler.new_visitor("RTMethod.java")
828 v.add("public abstract class RTMethod \{")
829 v.add(" protected RTMethod() \{\}")
830 v.add(" public abstract RTVal exec(RTVal[] args);")
831 v.add("\}")
832 end
833
834 # Compile the runtime value structure
835 #
836 # RTVal both represents object instances and primitives values:
837 # * object instances:
838 # * `rtclass` the class of the RTVal is instance of
839 # * `attrs` contains the attributes of the instance
840 # * primitive values:
841 # * `rtclass` represents the class of the primitive value Nit type
842 # * `value` contains the primitive value of the instance
843 # * null values:
844 # * they must have both `rtclass` and `value` as null
845 fun compile_rtval(compiler: JavaCompiler) do
846 var v = compiler.new_visitor("RTVal.java")
847 v.add("import java.util.HashMap;")
848 v.add("public class RTVal \{")
849 v.add(" public RTClass rtclass;")
850 v.add(" public HashMap<String, RTVal> attrs = new HashMap<>();")
851 v.add(" Object value;")
852 v.add(" public RTVal(RTClass rtclass) \{")
853 v.add(" this.rtclass = rtclass;")
854 v.add(" \}")
855 v.add(" public RTVal(RTClass rtclass, Object value) \{")
856 v.add(" this.rtclass = rtclass;")
857 v.add(" this.value = value;")
858 v.add(" \}")
859 v.add(" public boolean is_null() \{ return rtclass == null && value == null; \}")
860 v.add("\}")
861 end
862 end
863
864 # A runtime variable hold a runtime value in Java.
865 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
866 class RuntimeVariable
867
868 # The name of the variable in the Java code
869 var name: String
870
871 # The static type of the variable (as declard in Java)
872 var mtype: MType
873
874 # The current casted type of the variable (as known in Nit)
875 var mcasttype: MType is writable
876
877 # If the variable exaclty a mcasttype?
878 # false (usual value) means that the variable is a mcasttype or a subtype.
879 var is_exact: Bool = false is writable
880
881 # Is this variable declared as a RTVal or a Java primitive one?
882 var is_boxed = false
883
884 redef fun to_s do return name
885
886 redef fun inspect
887 do
888 var exact_str
889 if self.is_exact then
890 exact_str = " exact"
891 else
892 exact_str = ""
893 end
894 var type_str
895 if self.mtype == self.mcasttype then
896 type_str = "{mtype}{exact_str}"
897 else
898 type_str = "{mtype}({mcasttype}{exact_str})"
899 end
900 return "<{name}:{type_str}>"
901 end
902 end
903
904 # The static context of a visited property in a `JavaCompilerVisitor`
905 class JavaStaticFrame
906 # The associated visitor
907 var visitor: JavaCompilerVisitor
908
909 # The executed property.
910 # A Method in case of a call, an attribute in case of a default initialization.
911 var mpropdef: MPropDef
912
913 # The static type of the receiver
914 var receiver: MClassType
915
916 # Arguments of the method (the first is the receiver)
917 var arguments: Array[RuntimeVariable]
918
919 # The runtime_variable associated to the return (in a function)
920 var returnvar: nullable RuntimeVariable = null is writable
921
922 # The label at the end of the property
923 var returnlabel: nullable String = null is writable
924 end
925
926 redef class Location
927 # Return a shortened version of the location with `"{file}:{line_start}"`
928 fun short_location: String do
929 var file = self.file
930 if file == null then return "<no file>:{line_start}"
931 return "{file.filename.escape_to_c}:{line_start}"
932 end
933 end
934
935 redef class MType
936 # Return the Java type associated to a given Nit static type
937 fun java_type: String do return "RTVal"
938
939 # Is the associated Java type a primitive one?
940 #
941 # ENSURE `result == (java_type != "Object")`
942 var is_java_primitive: Bool is lazy do return java_type != "RTVal"
943 end
944
945 redef class MClassType
946
947 redef var java_type is lazy do
948 if mclass.name == "Int" then
949 return "int"
950 else if mclass.name == "Bool" then
951 return "boolean"
952 else if mclass.name == "Char" then
953 return "char"
954 else if mclass.name == "Float" then
955 return "double"
956 else if mclass.name == "Byte" then
957 return "byte"
958 else if mclass.name == "NativeString" then
959 return "String"
960 else if mclass.name == "NativeArray" then
961 return "Array"
962 end
963 return "RTVal"
964 end
965 end
966
967 redef class MClass
968
969 # Runtime name
970 private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}"
971
972 # Generate a Java RTClass for a Nit MClass
973 fun compile_to_java(v: JavaCompilerVisitor) do
974 v.add("public class {rt_name} extends RTClass \{")
975 v.add(" protected static RTClass instance;")
976 v.add(" private {rt_name}() \{")
977 v.add(" this.class_name = \"{name}\";")
978 compile_vft(v)
979 compile_type_table(v)
980 v.add(" \}")
981 v.add(" public static RTClass get{rt_name}() \{")
982 v.add(" if(instance == null) \{")
983 v.add(" instance = new {rt_name}();")
984 v.add(" \}")
985 v.add(" return instance;")
986 v.add(" \}")
987 v.add("\}")
988 end
989
990 # Compile the virtual function table for the mclass
991 private fun compile_vft(v: JavaCompilerVisitor) do
992 # TODO handle generics
993 if mclass_type.need_anchor then return
994 var mclassdefs = mclass_type.collect_mclassdefs(v.compiler.mainmodule).to_a
995 v.compiler.mainmodule.linearize_mclassdefs(mclassdefs)
996
997 var mainmodule = v.compiler.mainmodule
998 for mclassdef in mclassdefs.reversed do
999 for mprop in mclassdef.intro_mproperties do
1000 var mpropdef = mprop.lookup_first_definition(mainmodule, intro.bound_mtype)
1001 if not mpropdef isa MMethodDef then continue
1002 var rt_name = mpropdef.rt_name
1003 v.add("this.vft.put(\"{mprop.full_name}\", {rt_name}.get{rt_name}());")
1004
1005 # fill super next definitions
1006 while mpropdef.has_supercall do
1007 var prefix = mpropdef.full_name
1008 mpropdef = mpropdef.lookup_next_definition(mainmodule, intro.bound_mtype)
1009 rt_name = mpropdef.rt_name
1010 v.add("this.vft.put(\"{prefix}\", {rt_name}.get{rt_name}());")
1011 end
1012 end
1013 end
1014 end
1015
1016 # Compile the type table for the MClass
1017 fun compile_type_table(v: JavaCompilerVisitor) do
1018 for pclass in in_hierarchy(v.compiler.mainmodule).greaters do
1019 if pclass == self then
1020 v.add("supers.put(\"{pclass.jname}\", this);")
1021 else
1022 v.add("supers.put(\"{pclass.jname}\", {pclass.rt_name}.get{pclass.rt_name}());")
1023 end
1024 end
1025 end
1026 end
1027
1028 # Used as a common type between MMethod and MMethodDef for `table_send`
1029 private interface TableCallable
1030 end
1031
1032 redef class MMethod
1033 super TableCallable
1034 end
1035
1036 redef class MMethodDef
1037 super TableCallable
1038
1039 # Runtime name
1040 private fun rt_name: String do
1041 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
1042 end
1043
1044 # Generate a Java RTMethod for `self`
1045 fun compile_to_java(v: JavaCompilerVisitor) do
1046 v.add("public class {rt_name} extends RTMethod \{")
1047 v.add(" protected static RTMethod instance;")
1048 v.add(" public static RTMethod get{rt_name}() \{")
1049 v.add(" if(instance == null) \{")
1050 v.add(" instance = new {rt_name}();")
1051 v.add(" \}")
1052 v.add(" return instance;")
1053 v.add(" \}")
1054 v.add(" @Override")
1055 v.add(" public RTVal exec(RTVal[] args) \{")
1056 compile_inside_to_java(v)
1057 v.add(" \}")
1058 v.add("\}")
1059 end
1060
1061 # Compile the body of this function
1062 fun compile_inside_to_java(v: JavaCompilerVisitor) do
1063
1064 var modelbuilder = v.compiler.modelbuilder
1065 var node = modelbuilder.mpropdef2node(self)
1066
1067 if is_abstract then
1068 v.add_abort("Abstract method `{mproperty.name}` called on `\" + {selfvar}.rtclass.class_name +\"`")
1069 v.add("return null;")
1070 return
1071 end
1072
1073 if node isa APropdef then
1074 node.compile_to_java(v, self)
1075 else if node isa AClassdef then
1076 # TODO compile attributes
1077 v.info("NOT YET IMPLEMENTED attribute handling")
1078 else
1079 abort
1080 end
1081
1082 end
1083 end
1084
1085 redef class APropdef
1086
1087 # Compile that property definition to java code
1088 fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1089 v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
1090 v.add("return null;")
1091 end
1092 end
1093
1094 redef class AMethPropdef
1095 redef fun compile_to_java(v, mpropdef) do
1096 # TODO Call the implicit super-init
1097
1098 # Compile intern methods
1099 if mpropdef.is_intern then
1100 v.info("NOT YET IMPLEMENTED {class_name}::compile_intern")
1101 # TODO if compile_intern_to_java(v, mpropdef, arguments) then return
1102 v.ret(v.null_instance)
1103 return
1104 end
1105
1106 # Compile block if any
1107 var n_block = n_block
1108 if n_block != null then
1109 v.stmt(n_block)
1110 return
1111 end
1112 end
1113 end
1114
1115 redef class AExpr
1116 # Try to compile self as an expression
1117 # Do not call this method directly, use `v.expr` instead
1118 private fun expr(v: JavaCompilerVisitor): nullable RuntimeVariable do
1119 v.info("NOT YET IMPLEMENTED {class_name}::expr")
1120 return null
1121 end
1122
1123 # Try to compile self as a statement
1124 # Do not call this method directly, use `v.stmt` instead
1125 private fun stmt(v: JavaCompilerVisitor) do expr(v)
1126 end
1127
1128 redef class ABlockExpr
1129 redef fun stmt(v)
1130 do
1131 for e in self.n_expr do v.stmt(e)
1132 end
1133 redef fun expr(v)
1134 do
1135 var last = self.n_expr.last
1136 for e in self.n_expr do
1137 if e == last then break
1138 v.stmt(e)
1139 end
1140 return v.expr(last, null)
1141 end
1142 end
1143
1144 redef class ASendExpr
1145 redef fun expr(v) do
1146 var recv = v.expr(n_expr, null)
1147 var callsite = callsite.as(not null)
1148 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, raw_arguments)
1149 return v.compile_callsite(callsite, args)
1150 end
1151 end
1152
1153 redef class ANewExpr
1154 redef fun expr(v)
1155 do
1156 var mtype = self.recvtype
1157 assert mtype != null
1158
1159 if mtype.mclass.name == "NativeArray" then
1160 # TODO handle native arrays
1161 v.info("NOT YET IMPLEMENTED new NativeArray")
1162 end
1163
1164 var recv = v.init_instance(mtype)
1165
1166 var callsite = self.callsite
1167 if callsite == null then return recv
1168
1169 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
1170 var res2 = v.compile_callsite(callsite, args)
1171 if res2 != null then
1172 return res2
1173 end
1174 return recv
1175 end
1176 end
1177
1178 redef class ASelfExpr
1179 redef fun expr(v) do return v.frame.as(not null).arguments.first
1180 end
1181
1182 redef class AImplicitSelfExpr
1183 redef fun expr(v) do return v.frame.as(not null).arguments.first
1184 end
1185
1186 redef class AReturnExpr
1187 redef fun stmt(v) do
1188 var nexpr = self.n_expr
1189 var frame = v.frame
1190 assert frame != null
1191 if nexpr != null then
1192 v.ret(v.expr(nexpr, frame.returnvar.as(not null).mtype))
1193 else
1194 v.ret(v.null_instance)
1195 end
1196 end
1197 end
1198
1199 redef class AVardeclExpr
1200 redef fun stmt(v) do
1201 var variable = self.variable.as(not null)
1202 var ne = self.n_expr
1203 var decl = v.variable(variable)
1204 if ne != null then
1205 var i = v.expr(ne, variable.declared_type)
1206 v.assign(decl, i)
1207 end
1208 end
1209 end
1210
1211 redef class AVarExpr
1212 redef fun expr(v) do
1213 return v.variable(self.variable.as(not null))
1214 end
1215 end
1216
1217 redef class AVarAssignExpr
1218 redef fun expr(v) do
1219 var variable = self.variable.as(not null)
1220 var i = v.expr(self.n_value, variable.declared_type)
1221 v.assign(v.variable(variable), i)
1222 return i
1223 end
1224 end
1225
1226 redef class AIntExpr
1227 redef fun expr(v) do return v.int_instance(self.value.as(not null))
1228 end
1229
1230 redef class AByteExpr
1231 redef fun expr(v) do return v.byte_instance(self.value.as(not null))
1232 end
1233
1234 redef class AFloatExpr
1235 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
1236 end
1237
1238 redef class ACharExpr
1239 redef fun expr(v) do return v.char_instance(self.value.as(not null))
1240 end
1241
1242 redef class ATrueExpr
1243 redef fun expr(v) do return v.bool_instance(true)
1244 end
1245
1246 redef class AFalseExpr
1247 redef fun expr(v) do return v.bool_instance(false)
1248 end
1249
1250 redef class ANullExpr
1251 redef fun expr(v) do return v.null_instance
1252 end
1253
1254 redef class AAbortExpr
1255 redef fun stmt(v) do v.add_abort("Aborted")
1256 end
1257
1258 redef class ADebugTypeExpr
1259 redef fun stmt(v) do end # do nothing
1260 end