nitj: implement AParExpr
[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 transform
29 import frontend
30
31 redef class ToolContext
32
33 # Where to output the generated binary
34 var opt_output = new OptionString("Output file", "-o", "--output")
35
36 # Where to output tmp files
37 var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
38
39 redef init do
40 super
41 option_context.add_option(opt_output, opt_compile_dir)
42 end
43 end
44
45 redef class ModelBuilder
46
47 # Start the Java compiler
48 fun run_java_compiler(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis) do
49 var time0 = get_time
50 toolcontext.info("*** GENERATING JAVA ***", 1)
51
52 var compiler = new JavaCompiler(mainmodule, self, runtime_type_analysis)
53 compiler.do_compilation
54
55 var time1 = get_time
56 toolcontext.info("*** END GENERATING JAVA: {time1-time0} ***", 2)
57 write_and_make(compiler)
58 end
59
60 # Write Java code and compile it into an executable jar
61 fun write_and_make(compiler: JavaCompiler) do
62 var time0 = get_time
63 toolcontext.info("*** WRITING JAVA ***", 1)
64
65 compiler.compile_dir.mkdir
66
67 var jfiles = write_java_files(compiler)
68
69 var time1 = get_time
70 toolcontext.info("*** END WRITING JAVA: {time1-time0} ***", 2)
71
72 time0 = time1
73 toolcontext.info("*** COMPILING JAVA ***", 1)
74
75 build_with_make(compiler, jfiles)
76 write_shell_script(compiler)
77
78 time1 = get_time
79 toolcontext.info("*** END COMPILING JAVA: {time1-time0} ***", 2)
80 end
81
82 # Write files managed by `compiler` into concrete files
83 fun write_java_files(compiler: JavaCompiler): Array[String] do
84 var jfiles = new Array[String]
85 for f in compiler.files do
86 var file = new FileWriter.open("{compiler.compile_dir}/{f.filename}")
87 for line in f.lines do file.write(line)
88 file.close
89 jfiles.add(f.filename)
90 end
91 return jfiles
92 end
93
94 # Compile Java generated files using `make`
95 fun build_with_make(compiler: JavaCompiler, jfiles: Array[String]) do
96 write_manifest(compiler)
97 write_makefile(compiler, jfiles)
98 var compile_dir = compiler.compile_dir
99 var outname = compiler.outname.to_path.filename
100 toolcontext.info("make -N -C {compile_dir} -f {outname}.mk", 2)
101 var res
102 if toolcontext.verbose_level >= 3 then
103 res = sys.system("make -B -C {compile_dir} -f {outname}.mk 2>&1")
104 else
105 res = sys.system("make -B -C {compile_dir} -f {outname}.mk 2>&1 > /dev/null")
106 end
107 if res != 0 then toolcontext.error(null, "make failed! Error code: {res}.")
108 end
109
110 # Write the Makefile used to compile Java generated files into an executable jar
111 fun write_makefile(compiler: JavaCompiler, jfiles: Array[String]) do
112 # list class files from jfiles
113 var ofiles = new List[String]
114 for f in jfiles do ofiles.add(f.strip_extension(".java") + ".class")
115
116 var compile_dir = compiler.compile_dir
117 var outname = compiler.outname.to_path.filename
118 var outpath = (sys.getcwd / compiler.outname).simplify_path
119 var makename = "{compile_dir}/{outname}.mk"
120 var makefile = new FileWriter.open(makename)
121
122 makefile.write("JC = javac\n")
123 makefile.write("JAR = jar\n\n")
124
125 makefile.write("all: {outpath}.jar\n\n")
126
127 makefile.write("{outpath}.jar: {compiler.mainmodule.jname}_Main.class\n")
128 makefile.write("\t$(JAR) cfm {outpath}.jar {outname}.mf {ofiles.join(" ")}\n\n")
129
130 makefile.write("{compiler.mainmodule.jname}_Main.class:\n")
131 makefile.write("\t$(JC) {jfiles.join(" ")}\n\n")
132
133 makefile.write("clean:\n")
134 makefile.write("\trm {ofiles.join(" ")} 2>/dev/null\n\n")
135
136 makefile.close
137 toolcontext.info("Generated makefile: {makename}", 2)
138 end
139
140 # Write the Java manifest file
141 private fun write_manifest(compiler: JavaCompiler) do
142 var compile_dir = compiler.compile_dir
143 var outname = compiler.outname.to_path.filename
144 var maniffile = new FileWriter.open("{compile_dir}/{outname}.mf")
145 maniffile.write("Manifest-Version: 1.0\n")
146 maniffile.write("Main-Class: {compiler.mainmodule.jname}_Main\n")
147 maniffile.close
148 end
149
150 # Write a simple bash script that runs the jar like it was a binary generated by nitc
151 private fun write_shell_script(compiler: JavaCompiler) do
152 var outname = compiler.outname
153 var shfile = new FileWriter.open(outname)
154 shfile.write("#!/bin/bash\n")
155 shfile.write("java -jar {outname}.jar \"$@\"\n")
156 shfile.close
157 sys.system("chmod +x {outname}")
158 end
159 end
160
161 # Compiler that translates Nit code to Java code
162 class JavaCompiler
163 # The main module of the program currently compiled
164 var mainmodule: MModule
165
166 # Modelbuilder used to know the model and the AST
167 var modelbuilder: ModelBuilder
168
169 # The result of the RTA (used to know live types and methods)
170 var runtime_type_analysis: RapidTypeAnalysis
171
172 # Where to generate tmp files
173 var compile_dir: String is lazy do
174 var dir = modelbuilder.toolcontext.opt_compile_dir.value
175 if dir == null then dir = "nitj_compile"
176 return dir
177 end
178
179 # Name of the generated executable
180 var outname: String is lazy do
181 var name = modelbuilder.toolcontext.opt_output.value
182 if name == null then name = mainmodule.jname
183 return name
184 end
185
186 # The list of all associated files
187 # Used to generate .java files
188 var files: Array[JavaCodeFile] = new Array[JavaCodeFile]
189
190 # Force the creation of a new file
191 # The point is to avoid contamination between must-be-compiled-separately files
192 fun new_file(name: String): JavaCodeFile do
193 var file = new JavaCodeFile(name)
194 files.add(file)
195 return file
196 end
197
198 # Kind of visitor to use
199 type VISITOR: JavaCompilerVisitor
200
201 # Initialize a visitor specific for the compiler engine
202 fun new_visitor(filename: String): VISITOR do
203 return new JavaCompilerVisitor(self, new_file(filename))
204 end
205
206 # RuntimeModel representation
207 private var rt_model: JavaRuntimeModel is lazy do return new JavaRuntimeModel
208
209 # Compile Nit code to Java
210 fun do_compilation do
211 # compile java classes used to represents the runtime model of the program
212 rt_model.compile_rtmodel(self)
213 compile_box_kinds
214
215 # compile class structures
216 compile_mclasses_to_java
217
218 # compile method structures
219 compile_mmethods_to_java
220
221 # compile main
222 compile_main_function
223 end
224
225 # Prepare the boxes used to represent Java primitive types
226 fun compile_box_kinds do
227 # Collect all bas box class
228 # FIXME: this is not completely fine with a separate compilation scheme
229 for classname in ["Int", "Bool", "Byte", "Char", "Float"] do
230 var classes = mainmodule.model.get_mclasses_by_name(classname)
231 if classes == null then continue
232 assert classes.length == 1 else print classes.join(", ")
233 box_kinds.add(classes.first.mclass_type)
234 end
235 end
236
237 # Types of boxes used to represent Java primitive types
238 var box_kinds = new Array[MClassType]
239
240 # Generate a `RTClass` for each `MClass` found in model
241 #
242 # This is a global phase because we need to know all the program to build
243 # attributes, fill vft and type table.
244 fun compile_mclasses_to_java do
245 for mclass in mainmodule.model.mclasses do
246 mclass.compile_to_java(new_visitor("{mclass.rt_name}.java"))
247 end
248 end
249
250 # Generate a `RTMethod` for each `MMethodDef` found in model
251 #
252 # This is a separate phase.
253 fun compile_mmethods_to_java do
254 for mmodule in mainmodule.in_importation.greaters do
255 for mclassdef in mmodule.mclassdefs do
256 for mdef in mclassdef.mpropdefs do
257 if mdef isa MMethodDef then
258 mdef.compile_to_java(new_visitor("{mdef.rt_name}.java"))
259 end
260 end
261 end
262 end
263 end
264
265 # Generate Java main that call Sys.main
266 fun compile_main_function do
267 var v = new_visitor("{mainmodule.jname}_Main.java")
268 v.add("public class {mainmodule.jname}_Main \{")
269 v.add(" public static void main(String[] args) \{")
270
271 var main_type = mainmodule.sys_type
272 if main_type != null then
273 var mainmodule = v.compiler.mainmodule
274 var glob_sys = v.init_instance(main_type)
275 var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass)
276 if main_init != null then
277 v.send(main_init, [glob_sys])
278 end
279 var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
280 mainmodule.try_get_primitive_method("main", main_type.mclass)
281 if main_method != null then
282 v.send(main_method, [glob_sys])
283 end
284 end
285 v.add(" \}")
286 v.add("\}")
287 end
288 end
289
290 # The class visiting the AST
291 #
292 # A visitor is attached to one JavaCodeFile it writes into.
293 class JavaCompilerVisitor
294 super Visitor
295
296 # JavaCompiler used with this visitor
297 type COMPILER: JavaCompiler
298
299 # The associated compiler
300 var compiler: JavaCompiler
301
302 # The file to write generated code into
303 var file: JavaCodeFile
304
305 # Names handling
306
307 private var names = new HashSet[String]
308 private var last: Int = 0
309
310 # Return a new name based on `s` and unique in the visitor
311 fun get_name(s: String): String do
312 if not self.names.has(s) then
313 self.names.add(s)
314 return s
315 end
316 var i = self.last + 1
317 loop
318 var s2 = s + i.to_s
319 if not self.names.has(s2) then
320 self.last = i
321 self.names.add(s2)
322 return s2
323 end
324 i = i + 1
325 end
326 end
327
328 # Variables handling
329
330 # Registered variables
331 protected var variables = new HashMap[Variable, RuntimeVariable]
332
333 # Return the local RuntimeVariable associated to a Nit local variable
334 fun variable(variable: Variable): RuntimeVariable do
335 if variables.has_key(variable) then
336 return variables[variable]
337 else
338 var name = get_name("var_{variable.name}")
339 var mtype = variable.declared_type.as(not null)
340 mtype = anchor(mtype)
341 var res = decl_var(name, mtype)
342 variables[variable] = res
343 return res
344 end
345 end
346
347 # Return a new uninitialized local RuntimeVariable with `name`
348 fun decl_var(name: String, mtype: MType): RuntimeVariable do
349 var res = new RuntimeVariable(name, mtype, mtype)
350 res.is_boxed = not mtype.is_java_primitive
351 add("{mtype.java_type} {name} /* : {mtype} */;")
352 return res
353 end
354
355 # Return a new uninitialized local RuntimeVariable
356 fun new_var(mtype: MType): RuntimeVariable do
357 mtype = anchor(mtype)
358 var name = self.get_name("var")
359 return decl_var(name, mtype)
360 end
361
362 # Calls handling
363
364 # The current `JavaStaticFrame`
365 var frame: nullable JavaStaticFrame = null is writable
366
367 # Return a new local RuntimeVariable initialized from `args[0]`
368 fun new_recv(mtype: MType): RuntimeVariable do
369 var res = new_var(mtype)
370 add("{res} = args[0];")
371 return res
372 end
373
374 # Calls handling
375
376 # Compile a call within a callsite
377 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
378 var initializers = callsite.mpropdef.initializers
379 if not initializers.is_empty then
380 var recv = arguments.first
381
382 var i = 1
383 for p in initializers do
384 if p isa MMethod then
385 var args = [recv]
386 var msignature = p.intro.msignature
387 if msignature != null then
388 for x in msignature.mparameters do
389 args.add arguments[i]
390 i += 1
391 end
392 end
393 send(p, args)
394 else if p isa MAttribute then
395 info("NOT YET IMPLEMENTED {class_name}::compile_callsite for MAttribute `{p}`")
396 #self.write_attribute(p, recv, arguments[i])
397 i += 1
398 else abort
399 end
400 assert i == arguments.length
401
402 return send(callsite.mproperty, [recv])
403 end
404
405 return send(callsite.mproperty, arguments)
406 end
407
408 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
409 #
410 # This method is used to manage varargs in signatures and returns the real array
411 # of runtime variables to use in the call.
412 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable] do
413 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
414 var res = new Array[RuntimeVariable]
415 res.add(recv)
416
417 if msignature.arity == 0 then return res
418
419 if map == null then
420 assert args.length == msignature.arity
421 for ne in args do
422 res.add expr(ne, null)
423 end
424 return res
425 end
426
427 # Eval in order of arguments, not parameters
428 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
429 for ne in args do
430 exprs.add expr(ne, null)
431 end
432
433 # Fill `res` with the result of the evaluation according to the mapping
434 for i in [0..msignature.arity[ do
435 var param = msignature.mparameters[i]
436 var j = map.map.get_or_null(i)
437 if j == null then
438 # default value
439 res.add(null_instance)
440 continue
441 end
442 if param.is_vararg and map.vararg_decl > 0 then
443 var vararg = exprs.sub(j, map.vararg_decl)
444 var elttype = param.mtype
445 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
446 res.add(arg)
447 continue
448 end
449 res.add exprs[j]
450 end
451 return res
452 end
453
454 # Generate a static call on a method definition (no receiver needed).
455 fun static_call(mmethoddef: MMethodDef, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
456 var res: nullable RuntimeVariable
457 var ret = mmethoddef.msignature.as(not null).return_mtype
458 if ret == null then
459 res = null
460 else
461 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
462 res = self.new_var(ret)
463 end
464
465 # Autobox arguments
466 adapt_signature(mmethoddef, arguments)
467
468 var rt_name = mmethoddef.rt_name
469 if res == null then
470 add("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});")
471 return null
472 end
473 var ress = new_expr("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});", compiler.mainmodule.object_type)
474 assign(res, ress)
475 return res
476 end
477
478 # Generate a polymorphic send for `method` with `arguments`
479 fun send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
480 # Shortcut calls on primitives
481 if arguments.first.mcasttype.is_java_primitive then
482 return monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
483 end
484 # Polymorphic send
485 return table_send(mmethod, arguments)
486 end
487
488
489 # Handle common special cases before doing the effective method invocation
490 # This methods handle the `==` and `!=` methods and the case of the null receiver.
491 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
492 # Client must not forget to close the } after them.
493 #
494 # The value returned is the result of the common special cases.
495 # If not null, client must compile it with the result of their own effective method invocation.
496 #
497 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
498 # is generated to cancel the effective method invocation that will follow
499 # TODO: find a better approach
500 private fun before_send(res: nullable RuntimeVariable, mmethod: MMethodDef, arguments: Array[RuntimeVariable]) do
501 var bool_type = compiler.mainmodule.bool_type
502 var recv = arguments.first
503 var consider_null = mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance"
504 if recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType then
505 add("if ({recv} == null || {recv}.is_null()) \{")
506 if mmethod.name == "==" or mmethod.name == "is_same_instance" then
507 if res == null then res = new_var(bool_type)
508 var arg = arguments[1]
509 if arg.mcasttype isa MNullableType then
510 add("{res} = ({arg} == null || {arg}.is_null());")
511 else if arg.mcasttype isa MNullType then
512 add("{res} = true; /* is null */")
513 else
514 add("{res} = false; /* {arg.inspect} cannot be null */")
515 end
516 else if mmethod.name == "!=" then
517 if res == null then res = new_var(bool_type)
518 # res = self.new_var(bool_type)
519 var arg = arguments[1]
520 if arg.mcasttype isa MNullableType then
521 add("{res} = ({arg} != null && !{arg}.is_null());")
522 else if arg.mcasttype isa MNullType then
523 add("{res} = false; /* is null */")
524 else
525 add("{res} = true; /* {arg.inspect} cannot be null */")
526 end
527 else
528 add_abort("Receiver is null")
529 ret(null_instance)
530 end
531 add("\} else \{")
532 else
533 add "\{"
534 add "/* recv ({recv}) cannot be null since it's a {recv.mcasttype}"
535 end
536 if consider_null then
537 var arg = arguments[1]
538 if arg.mcasttype isa MNullType then
539 if res == null then res = new_var(bool_type)
540 if mmethod.name == "!=" then
541 add("{res} = true; /* arg is null and recv is not */")
542 else # `==` and `is_same_instance`
543 add("{res} = false; /* arg is null but recv is not */")
544 end
545 add("\}") # closes the null case
546 add("if (false) \{") # what follow is useless, Javac will drop it
547 end
548 end
549 end
550
551 # Perform a method call through vft
552 private fun table_send(mmethod: TableCallable, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
553 var mdef: MMethodDef
554 var name: String
555 if mmethod isa MMethod then
556 mdef = mmethod.intro
557 name = mmethod.full_name
558 else if mmethod isa MMethodDef then
559 mdef = mmethod
560 name = mmethod.full_name
561 else
562 abort
563 end
564
565 var recv = arguments.first
566 var rect = mdef.mclassdef.bound_mtype
567 var msignature = mdef.msignature.as(not null)
568 msignature = msignature.resolve_for(rect, rect, compiler.mainmodule, true)
569 adapt_signature(mdef, arguments)
570
571 var res: nullable RuntimeVariable
572 var ret = msignature.return_mtype
573 if ret == null then
574 res = null
575 else
576 res = self.new_var(ret)
577 end
578
579 before_send(res, mdef, arguments)
580
581 add "/* concrete call to {mdef} */"
582 if res != null then
583 var ress = new_expr("{recv}.rtclass.vft.get(\"{name}\").exec(new RTVal[]\{{arguments.join(",")}\});", compiler.mainmodule.object_type)
584 assign(res, ress)
585 else
586 add("{recv}.rtclass.vft.get(\"{name}\").exec(new RTVal[]\{{arguments.join(",")}\});")
587 end
588
589 add("\}") # closes the null case
590
591 return res
592 end
593
594 # Generate a super call from a method definition
595 fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable do
596 return table_send(m, args)
597 end
598
599 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
600 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable do
601 assert t isa MClassType
602 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
603 return self.static_call(propdef, args)
604 end
605
606 # Code generation
607
608 # Add a line (will be suffixed by `\n`)
609 fun add(line: String) do file.lines.add("{line}\n")
610
611 # Add a new partial line (no `\n` suffix)
612 fun addn(line: String) do file.lines.add(line)
613
614 # Compile a statement (if any)
615 fun stmt(nexpr: nullable AExpr) do
616 if nexpr == null then return
617 var old = self.current_node
618 current_node = nexpr
619 nexpr.stmt(self)
620 current_node = old
621 end
622
623 # Compile an expression an return its result
624 # `mtype` is the expected return type, pass null if no specific type is expected.
625 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable do
626 var old = current_node
627 current_node = nexpr
628
629 var res = null
630 if nexpr.mtype != null then
631 res = nexpr.expr(self)
632 end
633
634 if mtype != null then
635 mtype = anchor(mtype)
636 res = autobox(res, mtype)
637 end
638
639 current_node = old
640 return res
641 end
642
643 # Correctly assign a left and a right value
644 # Boxing and unboxing is performed if required
645 fun assign(left, right: RuntimeVariable) do
646 add("{left} = {autobox(right, left.mtype)};")
647 end
648
649 # Generate a return with `value`
650 fun ret(value: RuntimeVariable) do
651 var frame = self.frame
652 assert frame != null
653 var returnvar = frame.returnvar
654 if returnvar != null then
655 assign(returnvar, value)
656 end
657 self.add("break {frame.returnlabel.as(not null)};")
658 end
659
660 # Return a new local RuntimeVariable initialized with the Java expression `jexpr`.
661 #
662 # `mtype` is used for the Java return variable initialization.
663 fun new_expr(jexpr: String, mtype: MType): RuntimeVariable do
664 var res = new_var(mtype)
665 add("{res} = {jexpr};")
666 return res
667 end
668
669 # Generate generic abort
670 #
671 # Used by aborts, asserts, casts, etc.
672 fun add_abort(message: String) do
673 add("System.err.print(\"Runtime error: {message}\");")
674 var node = current_node
675 if node != null then
676 add("System.err.print(\" ({node.location.short_location})\");")
677 end
678 add("System.err.println(\"\");")
679 add("System.exit(1);")
680 end
681
682 # Types handling
683
684 # Anchor a type to the main module and the current receiver
685 fun anchor(mtype: MType): MType do
686 if not mtype.need_anchor then return mtype
687 return mtype.anchor_to(compiler.mainmodule, frame.as(not null).receiver)
688 end
689
690 # Adapt the arguments of a method according to targetted `MMethodDef`
691 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) do
692 var msignature = m.msignature.as(not null).resolve_for(
693 m.mclassdef.bound_mtype,
694 m.mclassdef.bound_mtype,
695 m.mclassdef.mmodule, true)
696 args.first = autobox(args.first, compiler.mainmodule.object_type)
697 for i in [0..msignature.arity[ do
698 args[i+1] = autobox(args[i + 1], compiler.mainmodule.object_type)
699 end
700 end
701
702 # Box primitive `value` to `mtype`.
703 private fun box(value: RuntimeVariable, mtype: MType): RuntimeVariable do
704 if value.is_boxed then return value
705 var obj_type = compiler.mainmodule.object_type
706 if value.mtype isa MNullType then
707 return new_expr("new RTVal(null, null)", compiler.mainmodule.model.null_type)
708 end
709 var mbox = value.mtype.as(MClassType).mclass
710 return new_expr("new RTVal({mbox.rt_name}.get{mbox.rt_name}(), {value})", obj_type)
711 end
712
713 # Unbox primitive `value` to `mtype`.
714 private fun unbox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
715 if not value.is_boxed then return value
716 if not mtype.is_java_primitive then return value
717 if compiler.box_kinds.has(mtype) then
718 return new_expr("({mtype.java_type}){value}.value", mtype)
719 else
720 info "NOT YET IMPLEMENTED unbox for {value} ({mtype})"
721 abort
722 end
723 end
724
725 # Box or unbox primitive `value` to `mtype` if needed.
726 private fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
727 if mtype.is_java_primitive then return unbox(value, mtype)
728 return box(value, mtype)
729 end
730
731 # Can this `value` be a primitive Java value?
732 private fun can_be_primitive(value: RuntimeVariable): Bool do
733 var t = value.mcasttype.undecorate
734 if not t isa MClassType then return false
735 var k = t.mclass.kind
736 return k == interface_kind or t.is_java_primitive
737 end
738
739 # Native instances
740
741 # Generate an integer value
742 fun int_instance(value: Int): RuntimeVariable do
743 var t = compiler.mainmodule.int_type
744 return new RuntimeVariable(value.to_s, t, t)
745 end
746
747 # Generate a byte value
748 fun byte_instance(value: Byte): RuntimeVariable do
749 var t = compiler.mainmodule.byte_type
750 return new RuntimeVariable(value.to_s, t, t)
751 end
752
753 # Generate a char value
754 fun char_instance(value: Char): RuntimeVariable do
755 var t = compiler.mainmodule.char_type
756 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
757 end
758
759 # Generate a float value
760 #
761 # FIXME pass a Float, not a string
762 fun float_instance(value: String): RuntimeVariable do
763 var t = compiler.mainmodule.float_type
764 return new RuntimeVariable(value.to_s, t, t)
765 end
766
767 # Generate an integer value
768 fun bool_instance(value: Bool): RuntimeVariable do
769 var t = compiler.mainmodule.bool_type
770 return new RuntimeVariable(value.to_s, t, t)
771 end
772
773 # Generate the `null` value
774 fun null_instance: RuntimeVariable do
775 var t = compiler.mainmodule.model.null_type
776 return new RuntimeVariable("null", t, t)
777 end
778
779 # Get an instance of a array for a vararg
780 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable do
781 # TODO handle dynamic types
782 info("NOT YET IMPLEMENTED vararg_instance")
783 return null_instance
784 # TODO return array_instance(varargs, elttype)
785 end
786
787 # Nit instances
788
789 # Generate a alloc-instance + init-attributes
790 fun init_instance(mtype: MClassType): RuntimeVariable do
791 var rt_name = mtype.mclass.rt_name
792 var res = new_expr("new RTVal({rt_name}.get{rt_name}())", mtype)
793 generate_init_attr(self, res, mtype)
794 return res
795 end
796
797 # Generate code that initialize the attributes on a new instance
798 fun generate_init_attr(v: JavaCompilerVisitor, recv: RuntimeVariable, mtype: MClassType) do
799 var cds = mtype.collect_mclassdefs(v.compiler.mainmodule).to_a
800 v.compiler.mainmodule.linearize_mclassdefs(cds)
801 for cd in cds do
802 for npropdef in v.compiler.modelbuilder.collect_attr_propdef(cd) do
803 npropdef.init_expr(v, recv)
804 end
805 end
806 end
807
808 # Generate a Nit "is" for two runtime_variables
809 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable do
810 var res = new_var(compiler.mainmodule.bool_type)
811 if value2.mtype.is_java_primitive and not value1.mtype.is_java_primitive then
812 var tmp = value1
813 value1 = value2
814 value2 = tmp
815 end
816 if value1.mtype.is_java_primitive then
817 if value2.mtype == value1.mtype then
818 add("{res} = {value1} == {value2}; /* == with two primitives */")
819 else if value2.mtype.is_java_primitive then
820 add("{res} = true; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
821 # else if value1.mtype.is_tagged then
822 # add("{res} = ({value2} != NULL) && ({autobox(value2, value1.mtype)} == {value1});")
823 else
824 var rt_name = value1.mtype.as(MClassType).mclass.rt_name
825 add("{res} = ({value2} != null) && ({value2}.rtclass == {rt_name}.get{rt_name}());")
826 add("if ({res}) \{")
827 add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
828 add("\}")
829 end
830 return res
831 end
832 var maybe_null = true
833 var test = new Array[String]
834 var t1 = value1.mcasttype
835 if t1 isa MNullableType then
836 test.add("{value1} != null && !{value1}.is_null()")
837 t1 = t1.mtype
838 else
839 maybe_null = false
840 end
841 var t2 = value2.mcasttype
842 if t2 isa MNullableType then
843 test.add("{value2} != null && !{value2}.is_null()")
844 t2 = t2.mtype
845 else
846 maybe_null = false
847 end
848
849 var incompatible = false
850 var primitive
851 if t1.is_java_primitive then
852 primitive = t1
853 if t1 == t2 then
854 # No need to compare class
855 else if t2.is_java_primitive then
856 incompatible = true
857 else if can_be_primitive(value2) then
858 if t1.is_java_primitive then
859 self.add("{res} = {value1} == {value2}; /* t1 is primitive and t2 can be */")
860 return res
861 end
862 # if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
863 # test.add("(!{extract_tag(value2)})")
864 # end
865 test.add("{value1}.rtclass == {value2}.rtclass")
866 else
867 incompatible = true
868 end
869 else if t2.is_java_primitive then
870 primitive = t2
871 if can_be_primitive(value1) then
872 if t2.is_java_primitive then
873 self.add("{res} = {value1} == {value2}; /* t2 is primitive and t1 can be */")
874 return res
875 end
876 test.add("{value1}.rtclass == {value2}.rtclass")
877 else
878 incompatible = true
879 end
880 else
881 primitive = null
882 end
883
884 if incompatible then
885 if maybe_null then
886 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
887 return res
888 else
889 self.add("{res} = false; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
890 return res
891 end
892 end
893 if primitive != null then
894 if primitive.is_java_primitive then
895 self.add("{res} = {value1} == {value2};")
896 return res
897 end
898 test.add("({value1}.value == {value2}.value")
899 else if can_be_primitive(value1) and can_be_primitive(value2) then
900 test.add("{value1}.rtclass == {value2}.rtclass")
901 var s = new Array[String]
902 for b in compiler.box_kinds do
903 var rt_name = b.mclass.rt_name
904 s.add "({value1}.rtclass == {rt_name}.get{rt_name}()) && ({value1}.value.equals({value2}.value))"
905 if b.mclass.name == "Float" then
906 s.add "({value1}.rtclass == RTClass_kernel_Float.getRTClass_kernel_Float() && {value1}.rtclass == {value2}.rtclass && Math.abs((double)({value1}.value)) == 0.0 && Math.abs((double)({value2}.value)) == 0.0)"
907 end
908 end
909 if s.is_empty then
910 self.add("{res} = {value1} == {value2}; /* both can be primitive */")
911 return res
912 end
913 test.add("({s.join(" || ")})")
914 else
915 self.add("{res} = {value1} == {value2}; /* no primitives */")
916 return res
917 end
918 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
919 return res
920 end
921
922 # Attributes
923
924 # Generate a polymorphic attribute is_set test
925 fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable do
926 # TODO self.check_recv_notnull(recv)
927 var res = new_var(compiler.mainmodule.bool_type)
928
929 # What is the declared type of the attribute?
930 var mtype = a.intro.static_mtype.as(not null)
931 var intromclassdef = a.intro.mclassdef
932 mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
933
934 if mtype isa MNullableType then
935 add("{res} = true; /* easy isset: {a} on {recv.inspect} */")
936 return res
937 end
938 add("{res} = {recv}.attrs.get(\"{a.jname}\") != null; /* {a} on {recv.inspect} */")
939 return res
940 end
941
942 # Generate a polymorphic attribute read
943 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable do
944 # TODO check_recv_notnull(recv)
945 # TODO compile_check(v)
946 # What is the declared type of the attribute?
947 var ret = a.intro.static_mtype.as(not null)
948 var intromclassdef = a.intro.mclassdef
949 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
950
951 # Check for Uninitialized attribute
952 if not ret isa MNullableType then check_attribute(a, recv)
953
954 return new_expr("{recv}.attrs.get(\"{a.jname}\")", ret)
955 end
956
957 # Generate a polymorphic attribute write
958 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) do
959 # TODO check_recv_notnull(recv)
960 add "{recv}.attrs.put(\"{a.jname}\", {autobox(value, compiler.mainmodule.object_type)});"
961 end
962
963 # Check uninitialized attribute
964 fun check_attribute(a: MAttribute, recv: RuntimeVariable) do
965 add "if({recv}.attrs.get(\"{a.jname}\") == null) \{"
966 add_abort "Uninitialized attribute {a.name}"
967 add "\}"
968 end
969
970 # Utils
971
972 # Display a info message
973 fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
974 end
975
976 # A file containing Java code.
977 class JavaCodeFile
978
979 # File name
980 var filename: String
981
982 # Lines to write
983 var lines: List[String] = new List[String]
984 end
985
986 redef class MEntity
987 # A Java compatible name for `self`
988 private fun jname: String do return name.to_cmangle
989 end
990
991 # Handler for runtime classes generation
992 #
993 # We need 3 kinds of runtime structures:
994 # * `RTClass` to represent a global class
995 # * `RTMethod` to represent a method definition
996 # * `RTVal` to represent runtime variables
997 class JavaRuntimeModel
998
999 # Compile JavaRuntimeModel structures
1000 fun compile_rtmodel(compiler: JavaCompiler) do
1001 compile_rtclass(compiler)
1002 compile_rtmethod(compiler)
1003 compile_rtval(compiler)
1004 end
1005
1006 # Compile the abstract runtime class structure
1007 #
1008 # Runtime classes have 3 attributes:
1009 # * `class_name`: the class name as a String
1010 # * `vft`: the virtual function table for the class (flattened)
1011 # * `supers`: the super type table (used for type tests)
1012 fun compile_rtclass(compiler: JavaCompiler) do
1013 var v = compiler.new_visitor("RTClass.java")
1014 v.add("import java.util.HashMap;")
1015 v.add("public abstract class RTClass \{")
1016 v.add(" public String class_name;")
1017 v.add(" public HashMap<String, RTMethod> vft = new HashMap<>();")
1018 v.add(" public HashMap<String, RTClass> supers = new HashMap<>();")
1019 v.add(" protected RTClass() \{\}")
1020 v.add("\}")
1021 end
1022
1023 # Compile the abstract runtime method structure
1024 #
1025 # Method body is executed through the `exec` method:
1026 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
1027 # * `exec` always returns a RTVal (or null if the Nit return type is void)
1028 fun compile_rtmethod(compiler: JavaCompiler) do
1029 var v = compiler.new_visitor("RTMethod.java")
1030 v.add("public abstract class RTMethod \{")
1031 v.add(" protected RTMethod() \{\}")
1032 v.add(" public abstract RTVal exec(RTVal[] args);")
1033 v.add("\}")
1034 end
1035
1036 # Compile the runtime value structure
1037 #
1038 # RTVal both represents object instances and primitives values:
1039 # * object instances:
1040 # * `rtclass` the class of the RTVal is instance of
1041 # * `attrs` contains the attributes of the instance
1042 # * primitive values:
1043 # * `rtclass` represents the class of the primitive value Nit type
1044 # * `value` contains the primitive value of the instance
1045 # * null values:
1046 # * they must have both `rtclass` and `value` as null
1047 fun compile_rtval(compiler: JavaCompiler) do
1048 var v = compiler.new_visitor("RTVal.java")
1049 v.add("import java.util.HashMap;")
1050 v.add("public class RTVal \{")
1051 v.add(" public RTClass rtclass;")
1052 v.add(" public HashMap<String, RTVal> attrs = new HashMap<>();")
1053 v.add(" Object value;")
1054 v.add(" public RTVal(RTClass rtclass) \{")
1055 v.add(" this.rtclass = rtclass;")
1056 v.add(" \}")
1057 v.add(" public RTVal(RTClass rtclass, Object value) \{")
1058 v.add(" this.rtclass = rtclass;")
1059 v.add(" this.value = value;")
1060 v.add(" \}")
1061 v.add(" public boolean is_null() \{ return rtclass == null && value == null; \}")
1062 v.add("\}")
1063 end
1064 end
1065
1066 # A runtime variable hold a runtime value in Java.
1067 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1068 class RuntimeVariable
1069
1070 # The name of the variable in the Java code
1071 var name: String
1072
1073 # The static type of the variable (as declard in Java)
1074 var mtype: MType
1075
1076 # The current casted type of the variable (as known in Nit)
1077 var mcasttype: MType is writable
1078
1079 # If the variable exaclty a mcasttype?
1080 # false (usual value) means that the variable is a mcasttype or a subtype.
1081 var is_exact: Bool = false is writable
1082
1083 # Is this variable declared as a RTVal or a Java primitive one?
1084 var is_boxed = false
1085
1086 redef fun to_s do return name
1087
1088 redef fun inspect
1089 do
1090 var exact_str
1091 if self.is_exact then
1092 exact_str = " exact"
1093 else
1094 exact_str = ""
1095 end
1096 var type_str
1097 if self.mtype == self.mcasttype then
1098 type_str = "{mtype}{exact_str}"
1099 else
1100 type_str = "{mtype}({mcasttype}{exact_str})"
1101 end
1102 return "<{name}:{type_str}>"
1103 end
1104 end
1105
1106 # The static context of a visited property in a `JavaCompilerVisitor`
1107 class JavaStaticFrame
1108 # The associated visitor
1109 var visitor: JavaCompilerVisitor
1110
1111 # The executed property.
1112 # A Method in case of a call, an attribute in case of a default initialization.
1113 var mpropdef: MPropDef
1114
1115 # The static type of the receiver
1116 var receiver: MClassType
1117
1118 # Arguments of the method (the first is the receiver)
1119 var arguments: Array[RuntimeVariable]
1120
1121 # The runtime_variable associated to the return (in a function)
1122 var returnvar: nullable RuntimeVariable = null is writable
1123
1124 # The label at the end of the property
1125 var returnlabel: nullable String = null is writable
1126 end
1127
1128 redef class Location
1129 # Return a shortened version of the location with `"{file}:{line_start}"`
1130 fun short_location: String do
1131 var file = self.file
1132 if file == null then return "<no file>:{line_start}"
1133 return "{file.filename.escape_to_c}:{line_start}"
1134 end
1135 end
1136
1137 redef class MType
1138 # Return the Java type associated to a given Nit static type
1139 fun java_type: String do return "RTVal"
1140
1141 # Is the associated Java type a primitive one?
1142 #
1143 # ENSURE `result == (java_type != "Object")`
1144 var is_java_primitive: Bool is lazy do return java_type != "RTVal"
1145 end
1146
1147 redef class MClassType
1148
1149 redef var java_type is lazy do
1150 if mclass.name == "Int" then
1151 return "int"
1152 else if mclass.name == "Bool" then
1153 return "boolean"
1154 else if mclass.name == "Char" then
1155 return "char"
1156 else if mclass.name == "Float" then
1157 return "double"
1158 else if mclass.name == "Byte" then
1159 return "byte"
1160 else if mclass.name == "NativeString" then
1161 return "String"
1162 else if mclass.name == "NativeArray" then
1163 return "Array"
1164 end
1165 return "RTVal"
1166 end
1167 end
1168
1169 redef class MClass
1170
1171 # Runtime name
1172 private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}"
1173
1174 # Generate a Java RTClass for a Nit MClass
1175 fun compile_to_java(v: JavaCompilerVisitor) do
1176 v.add("public class {rt_name} extends RTClass \{")
1177 v.add(" protected static RTClass instance;")
1178 v.add(" private {rt_name}() \{")
1179 v.add(" this.class_name = \"{name}\";")
1180 compile_vft(v)
1181 compile_type_table(v)
1182 v.add(" \}")
1183 v.add(" public static RTClass get{rt_name}() \{")
1184 v.add(" if(instance == null) \{")
1185 v.add(" instance = new {rt_name}();")
1186 v.add(" \}")
1187 v.add(" return instance;")
1188 v.add(" \}")
1189 v.add("\}")
1190 end
1191
1192 # Compile the virtual function table for the mclass
1193 private fun compile_vft(v: JavaCompilerVisitor) do
1194 # TODO handle generics
1195 if mclass_type.need_anchor then return
1196 var mclassdefs = mclass_type.collect_mclassdefs(v.compiler.mainmodule).to_a
1197 v.compiler.mainmodule.linearize_mclassdefs(mclassdefs)
1198
1199 var mainmodule = v.compiler.mainmodule
1200 for mclassdef in mclassdefs.reversed do
1201 for mprop in mclassdef.intro_mproperties do
1202 var mpropdef = mprop.lookup_first_definition(mainmodule, intro.bound_mtype)
1203 if not mpropdef isa MMethodDef then continue
1204 var rt_name = mpropdef.rt_name
1205 v.add("this.vft.put(\"{mprop.full_name}\", {rt_name}.get{rt_name}());")
1206
1207 # fill super next definitions
1208 while mpropdef.has_supercall do
1209 var prefix = mpropdef.full_name
1210 mpropdef = mpropdef.lookup_next_definition(mainmodule, intro.bound_mtype)
1211 rt_name = mpropdef.rt_name
1212 v.add("this.vft.put(\"{prefix}\", {rt_name}.get{rt_name}());")
1213 end
1214 end
1215 end
1216 end
1217
1218 # Compile the type table for the MClass
1219 fun compile_type_table(v: JavaCompilerVisitor) do
1220 for pclass in in_hierarchy(v.compiler.mainmodule).greaters do
1221 if pclass == self then
1222 v.add("supers.put(\"{pclass.jname}\", this);")
1223 else
1224 v.add("supers.put(\"{pclass.jname}\", {pclass.rt_name}.get{pclass.rt_name}());")
1225 end
1226 end
1227 end
1228 end
1229
1230 # Used as a common type between MMethod and MMethodDef for `table_send`
1231 private interface TableCallable
1232 end
1233
1234 redef class MMethod
1235 super TableCallable
1236 end
1237
1238 redef class MMethodDef
1239 super TableCallable
1240
1241 # Runtime name
1242 private fun rt_name: String do
1243 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
1244 end
1245
1246 # Generate a Java RTMethod for `self`
1247 fun compile_to_java(v: JavaCompilerVisitor) do
1248 v.add("public class {rt_name} extends RTMethod \{")
1249 v.add(" protected static RTMethod instance;")
1250 v.add(" public static RTMethod get{rt_name}() \{")
1251 v.add(" if(instance == null) \{")
1252 v.add(" instance = new {rt_name}();")
1253 v.add(" \}")
1254 v.add(" return instance;")
1255 v.add(" \}")
1256 v.add(" @Override")
1257 v.add(" public RTVal exec(RTVal[] args) \{")
1258 compile_inside_to_java(v)
1259 v.add(" \}")
1260 v.add("\}")
1261 end
1262
1263 # Compile the body of this function
1264 fun compile_inside_to_java(v: JavaCompilerVisitor) do
1265
1266 var modelbuilder = v.compiler.modelbuilder
1267 var node = modelbuilder.mpropdef2node(self)
1268
1269 if is_abstract then
1270 v.add_abort("Abstract method `{mproperty.name}` called on `\" + {selfvar}.rtclass.class_name +\"`")
1271 v.add("return null;")
1272 return
1273 end
1274
1275 if node isa APropdef then
1276 node.compile_to_java(v, self)
1277 else if node isa AClassdef then
1278 node.compile_to_java(v, self)
1279 else
1280 abort
1281 end
1282 end
1283 end
1284
1285 redef class AClassdef
1286 private fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1287 if mpropdef == self.mfree_init then
1288 var recv = mpropdef.mclassdef.bound_mtype
1289 var arguments = new Array[RuntimeVariable]
1290 var frame = new JavaStaticFrame(v, mpropdef, recv, arguments)
1291 v.frame = frame
1292
1293 var selfvar = v.decl_var("self", recv)
1294 arguments.add(selfvar)
1295 var boxed = v.new_expr("args[0];", v.compiler.mainmodule.object_type)
1296 v.add "{selfvar} = {v.unbox(boxed, recv)};"
1297
1298 var msignature = mpropdef.msignature
1299 var ret = null
1300 if msignature != null then
1301 ret = msignature.return_mtype
1302 if ret != null then frame.returnvar = v.new_var(ret)
1303 end
1304 frame.returnlabel = v.get_name("RET_LABEL")
1305
1306 assert mpropdef.mproperty.is_root_init
1307 if not mpropdef.is_intro then
1308 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1309 end
1310 else
1311 abort
1312 end
1313 v.add("return null;")
1314 end
1315 end
1316
1317 redef class APropdef
1318
1319 # Compile that property definition to java code
1320 fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1321 v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
1322 v.add("return null;")
1323 end
1324 end
1325
1326 redef class AMethPropdef
1327 redef fun compile_to_java(v, mpropdef) do
1328 var recv = mpropdef.mclassdef.bound_mtype
1329 var arguments = new Array[RuntimeVariable]
1330 var frame = new JavaStaticFrame(v, mpropdef, recv, arguments)
1331 v.frame = frame
1332
1333 var selfvar = v.decl_var("self", recv)
1334 arguments.add(selfvar)
1335 var boxed = v.new_expr("args[0];", v.compiler.mainmodule.object_type)
1336 v.add "{selfvar} = {v.unbox(boxed, recv)};"
1337
1338 var msignature = mpropdef.msignature
1339 var ret = null
1340 if msignature != null then
1341 ret = msignature.return_mtype
1342 if ret != null then frame.returnvar = v.new_var(ret)
1343 end
1344 frame.returnlabel = v.get_name("RET_LABEL")
1345
1346 if not mpropdef.is_intern and msignature != null then
1347 var i = 0
1348 for mparam in msignature.mparameters do
1349 var variable = n_signature.as(not null).n_params[i].variable
1350 if variable == null then continue
1351 var argvar = v.variable(variable)
1352 boxed = v.new_expr("args[{i + 1}];", v.compiler.mainmodule.object_type)
1353 v.add "{argvar} = {v.unbox(boxed, mparam.mtype)};"
1354 arguments.add(argvar)
1355 i += 1
1356 end
1357 end
1358
1359 v.add("{frame.returnlabel.as(not null)}: \{")
1360
1361 # Call the implicit super-init
1362 var auto_super_inits = self.auto_super_inits
1363 if auto_super_inits != null then
1364 var args = [arguments.first]
1365 for auto_super_init in auto_super_inits do
1366 assert auto_super_init.mproperty != mpropdef.mproperty
1367 args.clear
1368 for i in [0..auto_super_init.msignature.arity+1[ do
1369 args.add(arguments[i])
1370 end
1371 assert auto_super_init.mproperty != mpropdef.mproperty
1372 v.compile_callsite(auto_super_init, args)
1373 end
1374 end
1375 if auto_super_call then
1376 v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1377 end
1378
1379 compile_inside_to_java(v, mpropdef)
1380 v.add("\}")
1381
1382 if ret != null then
1383 if ret.is_java_primitive then
1384 boxed = v.box(frame.returnvar.as(not null), v.compiler.mainmodule.object_type)
1385 v.add("return {boxed};")
1386 else
1387 v.add("return {frame.returnvar.as(not null)};")
1388 end
1389 else
1390 v.add("return null;")
1391 end
1392 v.frame = null
1393 end
1394
1395 # Compile the inside of the method body
1396 private fun compile_inside_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1397 # Compile intern methods
1398 if mpropdef.is_intern then
1399 if compile_intern_to_java(v, mpropdef, arguments) then return
1400 v.info("NOT YET IMPLEMENTED compile_intern for {mpropdef}")
1401 v.ret(v.null_instance)
1402 return
1403 end
1404
1405 # Compile block if any
1406 var n_block = n_block
1407 if n_block != null then
1408 v.stmt(n_block)
1409 return
1410 end
1411 end
1412
1413 # Compile an intern method using Java primitives
1414 fun compile_intern_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do
1415 var pname = mpropdef.mproperty.name
1416 var cname = mpropdef.mclassdef.mclass.name
1417 var ret = mpropdef.msignature.as(not null).return_mtype
1418 if cname == "Int" then
1419 if pname == "output" then
1420 v.add("System.out.println({arguments[0]});")
1421 v.ret(v.null_instance)
1422 return true
1423 else if pname == "object_id" then
1424 v.ret(arguments.first)
1425 return true
1426 else if pname == "+" then
1427 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1428 return true
1429 else if pname == "-" then
1430 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1431 return true
1432 else if pname == "unary -" then
1433 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1434 return true
1435 else if pname == "unary +" then
1436 v.ret(arguments[0])
1437 return true
1438 else if pname == "*" then
1439 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1440 return true
1441 else if pname == "/" then
1442 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1443 return true
1444 else if pname == "%" then
1445 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1446 return true
1447 else if pname == "lshift" then
1448 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1449 return true
1450 else if pname == "rshift" then
1451 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1452 return true
1453 else if pname == "==" then
1454 v.ret(v.equal_test(arguments[0], arguments[1]))
1455 return true
1456 else if pname == "!=" then
1457 var res = v.equal_test(arguments[0], arguments[1])
1458 v.ret(v.new_expr("!{res}", ret.as(not null)))
1459 return true
1460 else if pname == "<" then
1461 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1462 return true
1463 else if pname == ">" then
1464 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1465 return true
1466 else if pname == "<=" then
1467 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1468 return true
1469 else if pname == ">=" then
1470 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1471 return true
1472 else if pname == "to_f" then
1473 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1474 return true
1475 else if pname == "to_b" then
1476 v.ret(v.new_expr("(byte){arguments[0]}", ret.as(not null)))
1477 return true
1478 else if pname == "ascii" then
1479 v.ret(v.new_expr("(char){arguments[0]}", ret.as(not null)))
1480 return true
1481 end
1482 else if cname == "Char" then
1483 if pname == "output" then
1484 v.add("System.out.print({arguments[0]});")
1485 v.ret(v.null_instance)
1486 return true
1487 else if pname == "object_id" then
1488 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1489 return true
1490 else if pname == "successor" then
1491 v.ret(v.new_expr("(char)({arguments[0]} + {arguments[1]})", ret.as(not null)))
1492 return true
1493 else if pname == "predecessor" then
1494 v.ret(v.new_expr("(char)({arguments[0]} - {arguments[1]})", ret.as(not null)))
1495 return true
1496 else if pname == "==" then
1497 v.ret(v.equal_test(arguments[0], arguments[1]))
1498 return true
1499 else if pname == "!=" then
1500 var res = v.equal_test(arguments[0], arguments[1])
1501 v.ret(v.new_expr("!{res}", ret.as(not null)))
1502 return true
1503 else if pname == "<" then
1504 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1505 return true
1506 else if pname == ">" then
1507 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1508 return true
1509 else if pname == "<=" then
1510 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1511 return true
1512 else if pname == ">=" then
1513 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1514 return true
1515 else if pname == "to_i" then
1516 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1517 return true
1518 else if pname == "ascii" then
1519 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1520 return true
1521 end
1522 else if cname == "Byte" then
1523 if pname == "output" then
1524 v.add("System.out.println({arguments[0]});")
1525 v.ret(v.null_instance)
1526 return true
1527 else if pname == "object_id" then
1528 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1529 return true
1530 else if pname == "+" then
1531 v.ret(v.new_expr("(byte)({arguments[0]} + {arguments[1]})", ret.as(not null)))
1532 return true
1533 else if pname == "-" then
1534 v.ret(v.new_expr("(byte)({arguments[0]} - {arguments[1]})", ret.as(not null)))
1535 return true
1536 else if pname == "unary -" then
1537 v.ret(v.new_expr("(byte)(-{arguments[0]})", ret.as(not null)))
1538 return true
1539 else if pname == "unary +" then
1540 v.ret(arguments[0])
1541 return true
1542 else if pname == "*" then
1543 v.ret(v.new_expr("(byte)({arguments[0]} * {arguments[1]})", ret.as(not null)))
1544 return true
1545 else if pname == "/" then
1546 v.ret(v.new_expr("(byte)({arguments[0]} / {arguments[1]})", ret.as(not null)))
1547 return true
1548 else if pname == "%" then
1549 v.ret(v.new_expr("(byte)({arguments[0]} % {arguments[1]})", ret.as(not null)))
1550 return true
1551 else if pname == "lshift" then
1552 v.ret(v.new_expr("(byte)({arguments[0]} << {arguments[1]})", ret.as(not null)))
1553 return true
1554 else if pname == "rshift" then
1555 v.ret(v.new_expr("(byte)({arguments[0]} >> {arguments[1]})", ret.as(not null)))
1556 return true
1557 else if pname == "==" then
1558 v.ret(v.equal_test(arguments[0], arguments[1]))
1559 return true
1560 else if pname == "!=" then
1561 var res = v.equal_test(arguments[0], arguments[1])
1562 v.ret(v.new_expr("!{res}", ret.as(not null)))
1563 return true
1564 else if pname == "<" then
1565 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1566 return true
1567 else if pname == ">" then
1568 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1569 return true
1570 else if pname == "<=" then
1571 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1572 return true
1573 else if pname == ">=" then
1574 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1575 return true
1576 else if pname == "to_i" then
1577 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1578 return true
1579 else if pname == "to_f" then
1580 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1581 return true
1582 else if pname == "ascii" then
1583 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1584 return true
1585 end
1586 else if cname == "Bool" then
1587 if pname == "output" then
1588 v.add("System.out.println({arguments[0]});")
1589 v.ret(v.null_instance)
1590 return true
1591 else if pname == "object_id" then
1592 v.ret(v.new_expr("{arguments[0]}?1:0", ret.as(not null)))
1593 return true
1594 else if pname == "==" then
1595 v.ret(v.equal_test(arguments[0], arguments[1]))
1596 return true
1597 else if pname == "!=" then
1598 var res = v.equal_test(arguments[0], arguments[1])
1599 v.ret(v.new_expr("!{res}", ret.as(not null)))
1600 return true
1601 end
1602 else if cname == "Float" then
1603 if pname == "output" then
1604 v.add "if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
1605 v.add "System.out.println(\"inf\");"
1606 v.add "\} else if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
1607 v.add "System.out.println(\"-inf\");"
1608 v.add "\} else \{"
1609 var df = v.get_name("df")
1610 v.add "java.text.DecimalFormat {df} = new java.text.DecimalFormat(\"0.000000\");"
1611 v.add "System.out.println({df}.format({arguments[0]}));"
1612 v.add "\}"
1613 v.ret(v.null_instance)
1614 return true
1615 else if pname == "object_id" then
1616 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1617 return true
1618 else if pname == "+" then
1619 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1620 return true
1621 else if pname == "-" then
1622 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1623 return true
1624 else if pname == "unary -" then
1625 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1626 return true
1627 else if pname == "unary +" then
1628 v.ret(arguments[0])
1629 return true
1630 else if pname == "succ" then
1631 v.ret(v.new_expr("{arguments[0]} + 1", ret.as(not null)))
1632 return true
1633 else if pname == "prec" then
1634 v.ret(v.new_expr("{arguments[0]} - 1", ret.as(not null)))
1635 return true
1636 else if pname == "*" then
1637 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1638 return true
1639 else if pname == "/" then
1640 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1641 return true
1642 else if pname == "==" then
1643 v.ret(v.equal_test(arguments[0], arguments[1]))
1644 return true
1645 else if pname == "!=" then
1646 var res = v.equal_test(arguments[0], arguments[1])
1647 v.ret(v.new_expr("!{res}", ret.as(not null)))
1648 return true
1649 else if pname == "<" then
1650 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1651 return true
1652 else if pname == ">" then
1653 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1654 return true
1655 else if pname == "<=" then
1656 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1657 return true
1658 else if pname == ">=" then
1659 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1660 return true
1661 else if pname == "to_i" then
1662 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1663 return true
1664 else if pname == "to_b" then
1665 v.ret(v.new_expr("(byte){arguments[0]}", ret.as(not null)))
1666 return true
1667 end
1668 end
1669 if pname == "exit" then
1670 v.add("System.exit({arguments[1]});")
1671 v.ret(v.null_instance)
1672 return true
1673 else if pname == "sys" then
1674 # TODO singleton
1675 var main_type = v.compiler.mainmodule.sys_type.as(not null)
1676 var sys = main_type.mclass
1677 v.ret(v.new_expr("new RTVal({sys.rt_name}.get{sys.rt_name}())", main_type))
1678 return true
1679 else if pname == "object_id" then
1680 v.ret(v.new_expr("{arguments[0]}.hashCode()", ret.as(not null)))
1681 return true
1682 else if pname == "is_same_type" then
1683 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1684 return true
1685 else if pname == "is_same_instance" then
1686 v.ret(v.equal_test(arguments[0], arguments[1]))
1687 return true
1688 else if pname == "output_class_name" then
1689 v.add("System.out.println({arguments[0]}.rtclass.class_name);")
1690 v.ret(v.null_instance)
1691 return true
1692 end
1693 return false
1694 end
1695 end
1696
1697 redef class AAttrPropdef
1698 redef fun compile_to_java(v, mpropdef, arguments) do
1699 v.current_node = self
1700 if mpropdef == mreadpropdef then
1701 compile_getter(v, mpropdef, arguments)
1702 else if mpropdef == mwritepropdef then
1703 compile_setter(v, mpropdef, arguments)
1704 else
1705 abort
1706 end
1707 v.current_node = null
1708 end
1709
1710 # Compile the setter method
1711 private fun compile_setter(v: JavaCompilerVisitor, mpropdef: MPropDef, arguments: Array[RuntimeVariable]) do
1712 var mtype = v.compiler.mainmodule.object_type
1713 var recv = arguments.first
1714 var val = v.new_expr("args[1]", mtype)
1715 v.write_attribute(self.mpropdef.as(not null).mproperty, recv, val)
1716 v.ret v.null_instance
1717 end
1718
1719 # Compile the getter method
1720 private fun compile_getter(v: JavaCompilerVisitor, mpropdef: MPropDef, arguments: Array[RuntimeVariable]) do
1721 var recv = arguments.first
1722 v.ret v.read_attribute(self.mpropdef.as(not null).mproperty, recv)
1723 end
1724
1725 private fun init_expr(v: JavaCompilerVisitor, recv: RuntimeVariable) do
1726 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
1727 end
1728
1729 # Evaluate, store and return the default value of the attribute
1730 private fun evaluate_expr(v: JavaCompilerVisitor, recv: RuntimeVariable): RuntimeVariable do
1731 var old = v.frame
1732 var frame = new JavaStaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
1733 v.frame = frame
1734
1735 var value
1736 var mtype = self.mtype
1737 assert mtype != null
1738
1739 var nexpr = self.n_expr
1740 var nblock = self.n_block
1741 if nexpr != null then
1742 value = v.expr(nexpr, mtype)
1743 else if nblock != null then
1744 value = v.new_var(mtype)
1745 frame.returnvar = value
1746 frame.returnlabel = v.get_name("RET_LABEL")
1747 v.add("{frame.returnlabel.as(not null)}: \{")
1748 v.stmt(nblock)
1749 v.add("\}")
1750 else
1751 abort
1752 end
1753
1754 v.write_attribute(self.mpropdef.as(not null).mproperty, recv, value)
1755 v.frame = old
1756 return value
1757 end
1758 end
1759
1760 redef class AExpr
1761 # Try to compile self as an expression
1762 # Do not call this method directly, use `v.expr` instead
1763 private fun expr(v: JavaCompilerVisitor): nullable RuntimeVariable do
1764 v.info("NOT YET IMPLEMENTED {class_name}::expr")
1765 return null
1766 end
1767
1768 # Try to compile self as a statement
1769 # Do not call this method directly, use `v.stmt` instead
1770 private fun stmt(v: JavaCompilerVisitor) do expr(v)
1771 end
1772
1773 redef class ABlockExpr
1774 redef fun stmt(v)
1775 do
1776 for e in self.n_expr do v.stmt(e)
1777 end
1778 redef fun expr(v)
1779 do
1780 var last = self.n_expr.last
1781 for e in self.n_expr do
1782 if e == last then break
1783 v.stmt(e)
1784 end
1785 return v.expr(last, null)
1786 end
1787 end
1788
1789 redef class ASendExpr
1790 redef fun expr(v) do
1791 var recv = v.expr(n_expr, null)
1792 var callsite = callsite.as(not null)
1793 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, raw_arguments)
1794 return v.compile_callsite(callsite, args)
1795 end
1796 end
1797
1798 redef class ANewExpr
1799 redef fun expr(v)
1800 do
1801 var mtype = self.recvtype
1802 assert mtype != null
1803
1804 if mtype.mclass.name == "NativeArray" then
1805 # TODO handle native arrays
1806 v.info("NOT YET IMPLEMENTED new NativeArray")
1807 end
1808
1809 var recv = v.init_instance(mtype)
1810
1811 var callsite = self.callsite
1812 if callsite == null then return recv
1813
1814 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
1815 var res2 = v.compile_callsite(callsite, args)
1816 if res2 != null then
1817 return res2
1818 end
1819 return recv
1820 end
1821 end
1822
1823 redef class ASuperExpr
1824 redef fun expr(v)
1825 do
1826 var frame = v.frame
1827 assert frame != null
1828 var recv = frame.arguments.first
1829
1830 var callsite = self.callsite
1831 if callsite != null then
1832 var args
1833
1834 if self.n_args.n_exprs.is_empty then
1835 # Add automatic arguments for the super init call
1836 args = [recv]
1837 for i in [0..callsite.msignature.arity[ do
1838 args.add(frame.arguments[i+1])
1839 end
1840 else
1841 args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
1842 end
1843
1844 # Super init call
1845 var res = v.compile_callsite(callsite, args)
1846 return res
1847 end
1848
1849 var mpropdef = self.mpropdef.as(not null)
1850
1851 var args
1852 if self.n_args.n_exprs.is_empty then
1853 args = frame.arguments
1854 else
1855 args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
1856 end
1857
1858 # Standard call-next-method
1859 return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
1860 end
1861 end
1862
1863 redef class ASelfExpr
1864 redef fun expr(v) do return v.frame.as(not null).arguments.first
1865 end
1866
1867 redef class AImplicitSelfExpr
1868 redef fun expr(v) do return v.frame.as(not null).arguments.first
1869 end
1870
1871 redef class AAttrExpr
1872 redef fun expr(v) do
1873 var recv = v.expr(self.n_expr, null)
1874 var mproperty = self.mproperty.as(not null)
1875 return v.read_attribute(mproperty, recv)
1876 end
1877 end
1878
1879 redef class AAttrAssignExpr
1880 redef fun expr(v) do
1881 var recv = v.expr(self.n_expr, null)
1882 var i = v.expr(self.n_value, null)
1883 var mproperty = self.mproperty.as(not null)
1884 v.write_attribute(mproperty, recv, i)
1885 return i
1886 end
1887 end
1888
1889 redef class AAttrReassignExpr
1890 redef fun stmt(v) do
1891 var recv = v.expr(self.n_expr, null)
1892 var value = v.expr(self.n_value, null)
1893 var mproperty = self.mproperty.as(not null)
1894 var attr = v.read_attribute(mproperty, recv)
1895 var res = v.compile_callsite(self.reassign_callsite.as(not null), [attr, value])
1896 assert res != null
1897 v.write_attribute(mproperty, recv, res)
1898 end
1899 end
1900
1901 redef class AIssetAttrExpr
1902 redef fun expr(v) do
1903 var recv = v.expr(self.n_expr, null)
1904 var mproperty = self.mproperty.as(not null)
1905 return v.isset_attribute(mproperty, recv)
1906 end
1907 end
1908
1909 redef class AReturnExpr
1910 redef fun stmt(v) do
1911 var nexpr = self.n_expr
1912 var frame = v.frame
1913 assert frame != null
1914 if nexpr != null then
1915 v.ret(v.expr(nexpr, frame.returnvar.as(not null).mtype))
1916 else
1917 v.ret(v.null_instance)
1918 end
1919 end
1920 end
1921
1922 redef class AVardeclExpr
1923 redef fun stmt(v) do
1924 var variable = self.variable.as(not null)
1925 var ne = self.n_expr
1926 var decl = v.variable(variable)
1927 if ne != null then
1928 var i = v.expr(ne, variable.declared_type)
1929 v.assign(decl, i)
1930 end
1931 end
1932 end
1933
1934 redef class AVarExpr
1935 redef fun expr(v) do
1936 return v.variable(self.variable.as(not null))
1937 end
1938 end
1939
1940 redef class AVarAssignExpr
1941 redef fun expr(v) do
1942 var variable = self.variable.as(not null)
1943 var i = v.expr(self.n_value, variable.declared_type)
1944 v.assign(v.variable(variable), i)
1945 return i
1946 end
1947 end
1948
1949 redef class AIntExpr
1950 redef fun expr(v) do return v.int_instance(self.value.as(not null))
1951 end
1952
1953 redef class AByteExpr
1954 redef fun expr(v) do return v.byte_instance(self.value.as(not null))
1955 end
1956
1957 redef class AFloatExpr
1958 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
1959 end
1960
1961 redef class ACharExpr
1962 redef fun expr(v) do return v.char_instance(self.value.as(not null))
1963 end
1964
1965 redef class ATrueExpr
1966 redef fun expr(v) do return v.bool_instance(true)
1967 end
1968
1969 redef class AFalseExpr
1970 redef fun expr(v) do return v.bool_instance(false)
1971 end
1972
1973 redef class ANullExpr
1974 redef fun expr(v) do return v.null_instance
1975 end
1976
1977 redef class AParExpr
1978 redef fun expr(v) do return v.expr(self.n_expr, null)
1979 end
1980
1981 redef class AAbortExpr
1982 redef fun stmt(v) do v.add_abort("Aborted")
1983 end
1984
1985 redef class ADebugTypeExpr
1986 redef fun stmt(v) do end # do nothing
1987 end