nitj: shortcut calls on primitive instances
[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 # Return a new local RuntimeVariable initialized with the Java expression `jexpr`.
620 #
621 # `mtype` is used for the Java return variable initialization.
622 fun new_expr(jexpr: String, mtype: MType): RuntimeVariable do
623 var res = new_var(mtype)
624 add("{res} = {jexpr};")
625 return res
626 end
627
628 # Generate generic abort
629 #
630 # Used by aborts, asserts, casts, etc.
631 fun add_abort(message: String) do
632 add("System.err.print(\"Runtime error: {message}\");")
633 var node = current_node
634 if node != null then
635 add("System.err.print(\" ({node.location.short_location})\");")
636 end
637 add("System.err.println(\"\");")
638 add("System.exit(1);")
639 end
640
641 # Types handling
642
643 # Anchor a type to the main module and the current receiver
644 fun anchor(mtype: MType): MType do
645 if not mtype.need_anchor then return mtype
646 return mtype.anchor_to(compiler.mainmodule, frame.as(not null).receiver)
647 end
648
649 # Adapt the arguments of a method according to targetted `MMethodDef`
650 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) do
651 var msignature = m.msignature.as(not null).resolve_for(
652 m.mclassdef.bound_mtype,
653 m.mclassdef.bound_mtype,
654 m.mclassdef.mmodule, true)
655 args.first = autobox(args.first, compiler.mainmodule.object_type)
656 for i in [0..msignature.arity[ do
657 args[i+1] = autobox(args[i + 1], compiler.mainmodule.object_type)
658 end
659 end
660
661 # Box primitive `value` to `mtype`.
662 private fun box(value: RuntimeVariable, mtype: MType): RuntimeVariable do
663 if value.is_boxed then return value
664 var obj_type = compiler.mainmodule.object_type
665 if value.mtype isa MNullType then
666 return new_expr("new RTVal(null, null)", compiler.mainmodule.model.null_type)
667 end
668 var mbox = value.mtype.as(MClassType).mclass
669 return new_expr("new RTVal({mbox.rt_name}.get{mbox.rt_name}(), {value})", obj_type)
670 end
671
672 # Unbox primitive `value` to `mtype`.
673 private fun unbox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
674 if not value.is_boxed then return value
675 if not mtype.is_java_primitive then return value
676 if compiler.box_kinds.has(mtype) then
677 return new_expr("({mtype.java_type}){value}.value", mtype)
678 else
679 info "NOT YET IMPLEMENTED unbox for {value} ({mtype})"
680 abort
681 end
682 end
683
684 # Box or unbox primitive `value` to `mtype` if needed.
685 private fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
686 if mtype.is_java_primitive then return unbox(value, mtype)
687 return box(value, mtype)
688 end
689
690 # Can this `value` be a primitive Java value?
691 private fun can_be_primitive(value: RuntimeVariable): Bool do
692 var t = value.mcasttype.undecorate
693 if not t isa MClassType then return false
694 var k = t.mclass.kind
695 return k == interface_kind or t.is_java_primitive
696 end
697
698 # Native instances
699
700 # Generate an integer value
701 fun int_instance(value: Int): RuntimeVariable do
702 var t = compiler.mainmodule.int_type
703 return new RuntimeVariable(value.to_s, t, t)
704 end
705
706 # Generate a byte value
707 fun byte_instance(value: Byte): RuntimeVariable do
708 var t = compiler.mainmodule.byte_type
709 return new RuntimeVariable(value.to_s, t, t)
710 end
711
712 # Generate a char value
713 fun char_instance(value: Char): RuntimeVariable do
714 var t = compiler.mainmodule.char_type
715 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
716 end
717
718 # Generate a float value
719 #
720 # FIXME pass a Float, not a string
721 fun float_instance(value: String): RuntimeVariable do
722 var t = compiler.mainmodule.float_type
723 return new RuntimeVariable(value.to_s, t, t)
724 end
725
726 # Generate an integer value
727 fun bool_instance(value: Bool): RuntimeVariable do
728 var t = compiler.mainmodule.bool_type
729 return new RuntimeVariable(value.to_s, t, t)
730 end
731
732 # Generate the `null` value
733 fun null_instance: RuntimeVariable do
734 var t = compiler.mainmodule.model.null_type
735 return new RuntimeVariable("null", t, t)
736 end
737
738 # Get an instance of a array for a vararg
739 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable do
740 # TODO handle dynamic types
741 info("NOT YET IMPLEMENTED vararg_instance")
742 return null_instance
743 # TODO return array_instance(varargs, elttype)
744 end
745
746 # Utils
747
748 # Display a info message
749 fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
750 end
751
752 # A file containing Java code.
753 class JavaCodeFile
754
755 # File name
756 var filename: String
757
758 # Lines to write
759 var lines: List[String] = new List[String]
760 end
761
762 redef class MEntity
763 # A Java compatible name for `self`
764 private fun jname: String do return name.to_cmangle
765 end
766
767 # Handler for runtime classes generation
768 #
769 # We need 3 kinds of runtime structures:
770 # * `RTClass` to represent a global class
771 # * `RTMethod` to represent a method definition
772 # * `RTVal` to represent runtime variables
773 class JavaRuntimeModel
774
775 # Compile JavaRuntimeModel structures
776 fun compile_rtmodel(compiler: JavaCompiler) do
777 compile_rtclass(compiler)
778 compile_rtmethod(compiler)
779 compile_rtval(compiler)
780 end
781
782 # Compile the abstract runtime class structure
783 #
784 # Runtime classes have 3 attributes:
785 # * `class_name`: the class name as a String
786 # * `vft`: the virtual function table for the class (flattened)
787 # * `supers`: the super type table (used for type tests)
788 fun compile_rtclass(compiler: JavaCompiler) do
789 var v = compiler.new_visitor("RTClass.java")
790 v.add("import java.util.HashMap;")
791 v.add("public abstract class RTClass \{")
792 v.add(" public String class_name;")
793 v.add(" public HashMap<String, RTMethod> vft = new HashMap<>();")
794 v.add(" public HashMap<String, RTClass> supers = new HashMap<>();")
795 v.add(" protected RTClass() \{\}")
796 v.add("\}")
797 end
798
799 # Compile the abstract runtime method structure
800 #
801 # Method body is executed through the `exec` method:
802 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
803 # * `exec` always returns a RTVal (or null if the Nit return type is void)
804 fun compile_rtmethod(compiler: JavaCompiler) do
805 var v = compiler.new_visitor("RTMethod.java")
806 v.add("public abstract class RTMethod \{")
807 v.add(" protected RTMethod() \{\}")
808 v.add(" public abstract RTVal exec(RTVal[] args);")
809 v.add("\}")
810 end
811
812 # Compile the runtime value structure
813 #
814 # RTVal both represents object instances and primitives values:
815 # * object instances:
816 # * `rtclass` the class of the RTVal is instance of
817 # * `attrs` contains the attributes of the instance
818 # * primitive values:
819 # * `rtclass` represents the class of the primitive value Nit type
820 # * `value` contains the primitive value of the instance
821 # * null values:
822 # * they must have both `rtclass` and `value` as null
823 fun compile_rtval(compiler: JavaCompiler) do
824 var v = compiler.new_visitor("RTVal.java")
825 v.add("import java.util.HashMap;")
826 v.add("public class RTVal \{")
827 v.add(" public RTClass rtclass;")
828 v.add(" public HashMap<String, RTVal> attrs = new HashMap<>();")
829 v.add(" Object value;")
830 v.add(" public RTVal(RTClass rtclass) \{")
831 v.add(" this.rtclass = rtclass;")
832 v.add(" \}")
833 v.add(" public RTVal(RTClass rtclass, Object value) \{")
834 v.add(" this.rtclass = rtclass;")
835 v.add(" this.value = value;")
836 v.add(" \}")
837 v.add(" public boolean is_null() \{ return rtclass == null && value == null; \}")
838 v.add("\}")
839 end
840 end
841
842 # A runtime variable hold a runtime value in Java.
843 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
844 class RuntimeVariable
845
846 # The name of the variable in the Java code
847 var name: String
848
849 # The static type of the variable (as declard in Java)
850 var mtype: MType
851
852 # The current casted type of the variable (as known in Nit)
853 var mcasttype: MType is writable
854
855 # If the variable exaclty a mcasttype?
856 # false (usual value) means that the variable is a mcasttype or a subtype.
857 var is_exact: Bool = false is writable
858
859 # Is this variable declared as a RTVal or a Java primitive one?
860 var is_boxed = false
861
862 redef fun to_s do return name
863
864 redef fun inspect
865 do
866 var exact_str
867 if self.is_exact then
868 exact_str = " exact"
869 else
870 exact_str = ""
871 end
872 var type_str
873 if self.mtype == self.mcasttype then
874 type_str = "{mtype}{exact_str}"
875 else
876 type_str = "{mtype}({mcasttype}{exact_str})"
877 end
878 return "<{name}:{type_str}>"
879 end
880 end
881
882 # The static context of a visited property in a `JavaCompilerVisitor`
883 class JavaStaticFrame
884 # The associated visitor
885 var visitor: JavaCompilerVisitor
886
887 # The executed property.
888 # A Method in case of a call, an attribute in case of a default initialization.
889 var mpropdef: MPropDef
890
891 # The static type of the receiver
892 var receiver: MClassType
893
894 # Arguments of the method (the first is the receiver)
895 var arguments: Array[RuntimeVariable]
896
897 # The runtime_variable associated to the return (in a function)
898 var returnvar: nullable RuntimeVariable = null is writable
899
900 # The label at the end of the property
901 var returnlabel: nullable String = null is writable
902 end
903
904 redef class Location
905 # Return a shortened version of the location with `"{file}:{line_start}"`
906 fun short_location: String do
907 var file = self.file
908 if file == null then return "<no file>:{line_start}"
909 return "{file.filename.escape_to_c}:{line_start}"
910 end
911 end
912
913 redef class MType
914 # Return the Java type associated to a given Nit static type
915 fun java_type: String do return "RTVal"
916
917 # Is the associated Java type a primitive one?
918 #
919 # ENSURE `result == (java_type != "Object")`
920 var is_java_primitive: Bool is lazy do return java_type != "RTVal"
921 end
922
923 redef class MClassType
924
925 redef var java_type is lazy do
926 if mclass.name == "Int" then
927 return "int"
928 else if mclass.name == "Bool" then
929 return "boolean"
930 else if mclass.name == "Char" then
931 return "char"
932 else if mclass.name == "Float" then
933 return "double"
934 else if mclass.name == "Byte" then
935 return "byte"
936 else if mclass.name == "NativeString" then
937 return "String"
938 else if mclass.name == "NativeArray" then
939 return "Array"
940 end
941 return "RTVal"
942 end
943 end
944
945 redef class MClass
946
947 # Runtime name
948 private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}"
949
950 # Generate a Java RTClass for a Nit MClass
951 fun compile_to_java(v: JavaCompilerVisitor) do
952 v.add("public class {rt_name} extends RTClass \{")
953 v.add(" protected static RTClass instance;")
954 v.add(" private {rt_name}() \{")
955 v.add(" this.class_name = \"{name}\";")
956 compile_vft(v)
957 compile_type_table(v)
958 v.add(" \}")
959 v.add(" public static RTClass get{rt_name}() \{")
960 v.add(" if(instance == null) \{")
961 v.add(" instance = new {rt_name}();")
962 v.add(" \}")
963 v.add(" return instance;")
964 v.add(" \}")
965 v.add("\}")
966 end
967
968 # Compile the virtual function table for the mclass
969 private fun compile_vft(v: JavaCompilerVisitor) do
970 # TODO handle generics
971 if mclass_type.need_anchor then return
972 var mclassdefs = mclass_type.collect_mclassdefs(v.compiler.mainmodule).to_a
973 v.compiler.mainmodule.linearize_mclassdefs(mclassdefs)
974
975 var mainmodule = v.compiler.mainmodule
976 for mclassdef in mclassdefs.reversed do
977 for mprop in mclassdef.intro_mproperties do
978 var mpropdef = mprop.lookup_first_definition(mainmodule, intro.bound_mtype)
979 if not mpropdef isa MMethodDef then continue
980 var rt_name = mpropdef.rt_name
981 v.add("this.vft.put(\"{mprop.full_name}\", {rt_name}.get{rt_name}());")
982
983 # fill super next definitions
984 while mpropdef.has_supercall do
985 var prefix = mpropdef.full_name
986 mpropdef = mpropdef.lookup_next_definition(mainmodule, intro.bound_mtype)
987 rt_name = mpropdef.rt_name
988 v.add("this.vft.put(\"{prefix}\", {rt_name}.get{rt_name}());")
989 end
990 end
991 end
992 end
993
994 # Compile the type table for the MClass
995 fun compile_type_table(v: JavaCompilerVisitor) do
996 for pclass in in_hierarchy(v.compiler.mainmodule).greaters do
997 if pclass == self then
998 v.add("supers.put(\"{pclass.jname}\", this);")
999 else
1000 v.add("supers.put(\"{pclass.jname}\", {pclass.rt_name}.get{pclass.rt_name}());")
1001 end
1002 end
1003 end
1004 end
1005
1006 # Used as a common type between MMethod and MMethodDef for `table_send`
1007 private interface TableCallable
1008 end
1009
1010 redef class MMethod
1011 super TableCallable
1012 end
1013
1014 redef class MMethodDef
1015 super TableCallable
1016
1017 # Runtime name
1018 private fun rt_name: String do
1019 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
1020 end
1021
1022 # Generate a Java RTMethod for `self`
1023 fun compile_to_java(v: JavaCompilerVisitor) do
1024 v.add("public class {rt_name} extends RTMethod \{")
1025 v.add(" protected static RTMethod instance;")
1026 v.add(" public static RTMethod get{rt_name}() \{")
1027 v.add(" if(instance == null) \{")
1028 v.add(" instance = new {rt_name}();")
1029 v.add(" \}")
1030 v.add(" return instance;")
1031 v.add(" \}")
1032 v.add(" @Override")
1033 v.add(" public RTVal exec(RTVal[] args) \{")
1034 compile_inside_to_java(v)
1035 v.add(" \}")
1036 v.add("\}")
1037 end
1038
1039 # Compile the body of this function
1040 fun compile_inside_to_java(v: JavaCompilerVisitor) do
1041
1042 var modelbuilder = v.compiler.modelbuilder
1043 var node = modelbuilder.mpropdef2node(self)
1044
1045 if is_abstract then
1046 # TODO compile abstract
1047 v.info("NOT YET IMPLEMENTED call to abstract method")
1048 v.add("return null;")
1049 return
1050 end
1051
1052 if node isa APropdef then
1053 node.compile_to_java(v, self)
1054 else if node isa AClassdef then
1055 # TODO compile attributes
1056 v.info("NOT YET IMPLEMENTED attribute handling")
1057 v.add("return null;")
1058 else
1059 abort
1060 end
1061 end
1062 end
1063
1064 redef class APropdef
1065
1066 # Compile that property definition to java code
1067 fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1068 v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
1069 end
1070 end
1071
1072 redef class AMethPropdef
1073 redef fun compile_to_java(v, mpropdef) do
1074 # TODO Call the implicit super-init
1075
1076 # Compile intern methods
1077 if mpropdef.is_intern then
1078 v.info("NOT YET IMPLEMENTED {class_name}::compile_intern")
1079 # TODO if compile_intern_to_java(v, mpropdef, arguments) then return
1080 v.add("return null;")
1081 return
1082 end
1083
1084 # Compile block if any
1085 var n_block = n_block
1086 if n_block != null then
1087 v.stmt(n_block)
1088 return
1089 end
1090 end
1091 end
1092
1093 redef class AExpr
1094 # Try to compile self as an expression
1095 # Do not call this method directly, use `v.expr` instead
1096 private fun expr(v: JavaCompilerVisitor): nullable RuntimeVariable do
1097 v.info("NOT YET IMPLEMENTED {class_name}::expr")
1098 return null
1099 end
1100
1101 # Try to compile self as a statement
1102 # Do not call this method directly, use `v.stmt` instead
1103 private fun stmt(v: JavaCompilerVisitor) do expr(v)
1104 end
1105
1106 redef class ABlockExpr
1107 redef fun stmt(v)
1108 do
1109 for e in self.n_expr do v.stmt(e)
1110 end
1111 redef fun expr(v)
1112 do
1113 var last = self.n_expr.last
1114 for e in self.n_expr do
1115 if e == last then break
1116 v.stmt(e)
1117 end
1118 return v.expr(last, null)
1119 end
1120 end
1121
1122 redef class ASendExpr
1123 redef fun expr(v) do
1124 var recv = v.expr(n_expr, null)
1125 var callsite = callsite.as(not null)
1126 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, raw_arguments)
1127 return v.compile_callsite(callsite, args)
1128 end
1129 end
1130
1131 redef class ASelfExpr
1132 redef fun expr(v) do return v.frame.as(not null).arguments.first
1133 end
1134
1135 redef class AImplicitSelfExpr
1136 redef fun expr(v) do return v.frame.as(not null).arguments.first
1137 end
1138
1139 redef class AVardeclExpr
1140 redef fun stmt(v) do
1141 var variable = self.variable.as(not null)
1142 var ne = self.n_expr
1143 var decl = v.variable(variable)
1144 if ne != null then
1145 var i = v.expr(ne, variable.declared_type)
1146 v.assign(decl, i)
1147 end
1148 end
1149 end
1150
1151 redef class AVarExpr
1152 redef fun expr(v) do
1153 return v.variable(self.variable.as(not null))
1154 end
1155 end
1156
1157 redef class AVarAssignExpr
1158 redef fun expr(v) do
1159 var variable = self.variable.as(not null)
1160 var i = v.expr(self.n_value, variable.declared_type)
1161 v.assign(v.variable(variable), i)
1162 return i
1163 end
1164 end
1165
1166 redef class AIntExpr
1167 redef fun expr(v) do return v.int_instance(self.value.as(not null))
1168 end
1169
1170 redef class AByteExpr
1171 redef fun expr(v) do return v.byte_instance(self.value.as(not null))
1172 end
1173
1174 redef class AFloatExpr
1175 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
1176 end
1177
1178 redef class ACharExpr
1179 redef fun expr(v) do return v.char_instance(self.value.as(not null))
1180 end
1181
1182 redef class ATrueExpr
1183 redef fun expr(v) do return v.bool_instance(true)
1184 end
1185
1186 redef class AFalseExpr
1187 redef fun expr(v) do return v.bool_instance(false)
1188 end
1189
1190 redef class ANullExpr
1191 redef fun expr(v) do return v.null_instance
1192 end
1193
1194 redef class AAbortExpr
1195 redef fun stmt(v) do v.add_abort("Aborted")
1196 end
1197
1198 redef class ADebugTypeExpr
1199 redef fun stmt(v) do end # do nothing
1200 end