nitj: compile java main function stub
[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 # compile main
221 compile_main_function
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
264 # Generate Java main that call Sys.main
265 fun compile_main_function do
266 var v = new_visitor("{mainmodule.jname}_Main.java")
267 v.add("public class {mainmodule.jname}_Main \{")
268 v.add(" public static void main(String[] args) \{")
269 modelbuilder.toolcontext.info("NOT YET IMPLEMENTED", 0)
270 # TODO compile call to Sys::main
271 v.add(" \}")
272 v.add("\}")
273 end
274 end
275
276 # The class visiting the AST
277 #
278 # A visitor is attached to one JavaCodeFile it writes into.
279 class JavaCompilerVisitor
280 super Visitor
281
282 # JavaCompiler used with this visitor
283 type COMPILER: JavaCompiler
284
285 # The associated compiler
286 var compiler: JavaCompiler
287
288 # The file to write generated code into
289 var file: JavaCodeFile
290
291 # Names handling
292
293 private var names = new HashSet[String]
294 private var last: Int = 0
295
296 # Return a new name based on `s` and unique in the visitor
297 fun get_name(s: String): String do
298 if not self.names.has(s) then
299 self.names.add(s)
300 return s
301 end
302 var i = self.last + 1
303 loop
304 var s2 = s + i.to_s
305 if not self.names.has(s2) then
306 self.last = i
307 self.names.add(s2)
308 return s2
309 end
310 i = i + 1
311 end
312 end
313
314 # Variables handling
315
316 # Registered variables
317 protected var variables = new HashMap[Variable, RuntimeVariable]
318
319 # Return the local RuntimeVariable associated to a Nit local variable
320 fun variable(variable: Variable): RuntimeVariable do
321 if variables.has_key(variable) then
322 return variables[variable]
323 else
324 var name = get_name("var_{variable.name}")
325 var mtype = variable.declared_type.as(not null)
326 mtype = anchor(mtype)
327 var res = decl_var(name, mtype)
328 variables[variable] = res
329 return res
330 end
331 end
332
333 # Return a new uninitialized local RuntimeVariable with `name`
334 fun decl_var(name: String, mtype: MType): RuntimeVariable do
335 var res = new RuntimeVariable(name, mtype, mtype)
336 res.is_boxed = not mtype.is_java_primitive
337 add("{mtype.java_type} {name} /* : {mtype} */;")
338 return res
339 end
340
341 # Return a new uninitialized local RuntimeVariable
342 fun new_var(mtype: MType): RuntimeVariable do
343 mtype = anchor(mtype)
344 var name = self.get_name("var")
345 return decl_var(name, mtype)
346 end
347
348 # Calls handling
349
350 # The current `JavaStaticFrame`
351 var frame: nullable JavaStaticFrame = null is writable
352
353 # Return a new local RuntimeVariable initialized from `args[0]`
354 fun new_recv(mtype: MType): RuntimeVariable do
355 var res = new_var(mtype)
356 add("{res} = args[0];")
357 return res
358 end
359
360 # Calls handling
361
362 # Compile a call within a callsite
363 fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
364 var initializers = callsite.mpropdef.initializers
365 if not initializers.is_empty then
366 var recv = arguments.first
367
368 var i = 1
369 for p in initializers do
370 if p isa MMethod then
371 var args = [recv]
372 var msignature = p.intro.msignature
373 if msignature != null then
374 for x in msignature.mparameters do
375 args.add arguments[i]
376 i += 1
377 end
378 end
379 send(p, args)
380 else if p isa MAttribute then
381 info("NOT YET IMPLEMENTED {class_name}::compile_callsite for MAttribute `{p}`")
382 #self.write_attribute(p, recv, arguments[i])
383 i += 1
384 else abort
385 end
386 assert i == arguments.length
387
388 return send(callsite.mproperty, [recv])
389 end
390
391 return send(callsite.mproperty, arguments)
392 end
393
394 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
395 #
396 # This method is used to manage varargs in signatures and returns the real array
397 # of runtime variables to use in the call.
398 fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable] do
399 var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
400 var res = new Array[RuntimeVariable]
401 res.add(recv)
402
403 if msignature.arity == 0 then return res
404
405 if map == null then
406 assert args.length == msignature.arity
407 for ne in args do
408 res.add expr(ne, null)
409 end
410 return res
411 end
412
413 # Eval in order of arguments, not parameters
414 var exprs = new Array[RuntimeVariable].with_capacity(args.length)
415 for ne in args do
416 exprs.add expr(ne, null)
417 end
418
419 # Fill `res` with the result of the evaluation according to the mapping
420 for i in [0..msignature.arity[ do
421 var param = msignature.mparameters[i]
422 var j = map.map.get_or_null(i)
423 if j == null then
424 # default value
425 res.add(null_instance)
426 continue
427 end
428 if param.is_vararg and map.vararg_decl > 0 then
429 var vararg = exprs.sub(j, map.vararg_decl)
430 var elttype = param.mtype
431 var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
432 res.add(arg)
433 continue
434 end
435 res.add exprs[j]
436 end
437 return res
438 end
439
440 # Generate a static call on a method definition (no receiver needed).
441 fun static_call(mmethoddef: MMethodDef, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
442 var res: nullable RuntimeVariable
443 var ret = mmethoddef.msignature.as(not null).return_mtype
444 if ret == null then
445 res = null
446 else
447 ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
448 res = self.new_var(ret)
449 end
450
451 # Autobox arguments
452 adapt_signature(mmethoddef, arguments)
453
454 var rt_name = mmethoddef.rt_name
455 if res == null then
456 add("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});")
457 return null
458 end
459 var ress = new_expr("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});", compiler.mainmodule.object_type)
460 assign(res, ress)
461 return res
462 end
463
464 # Generate a polymorphic send for `method` with `arguments`
465 fun send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
466 # Shortcut calls on primitives
467 if arguments.first.mcasttype.is_java_primitive then
468 return monomorphic_send(mmethod, arguments.first.mcasttype, arguments)
469 end
470 # Polymorphic send
471 return table_send(mmethod, arguments)
472 end
473
474
475 # Handle common special cases before doing the effective method invocation
476 # This methods handle the `==` and `!=` methods and the case of the null receiver.
477 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
478 # Client must not forget to close the } after them.
479 #
480 # The value returned is the result of the common special cases.
481 # If not null, client must compile it with the result of their own effective method invocation.
482 #
483 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
484 # is generated to cancel the effective method invocation that will follow
485 # TODO: find a better approach
486 private fun before_send(res: nullable RuntimeVariable, mmethod: MMethodDef, arguments: Array[RuntimeVariable]) do
487 var bool_type = compiler.mainmodule.bool_type
488 var recv = arguments.first
489 var consider_null = mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance"
490 if recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType then
491 add("if ({recv} == null || {recv}.is_null()) \{")
492 if mmethod.name == "==" or mmethod.name == "is_same_instance" then
493 if res == null then res = 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} = true; /* is null */")
499 else
500 add("{res} = false; /* {arg.inspect} cannot be null */")
501 end
502 else if mmethod.name == "!=" then
503 if res == null then res = new_var(bool_type)
504 # res = self.new_var(bool_type)
505 var arg = arguments[1]
506 if arg.mcasttype isa MNullableType then
507 add("{res} = ({arg} != null && !{arg}.is_null());")
508 else if arg.mcasttype isa MNullType then
509 add("{res} = false; /* is null */")
510 else
511 add("{res} = true; /* {arg.inspect} cannot be null */")
512 end
513 else
514 add_abort("Receiver is null")
515 ret(null_instance)
516 end
517 add("\} else \{")
518 else
519 add "\{"
520 add "/* recv ({recv}) cannot be null since it's a {recv.mcasttype}"
521 end
522 if consider_null then
523 var arg = arguments[1]
524 if arg.mcasttype isa MNullType then
525 if res == null then res = new_var(bool_type)
526 if mmethod.name == "!=" then
527 add("{res} = true; /* arg is null and recv is not */")
528 else # `==` and `is_same_instance`
529 add("{res} = false; /* arg is null but recv is not */")
530 end
531 add("\}") # closes the null case
532 add("if (false) \{") # what follow is useless, Javac will drop it
533 end
534 end
535 end
536
537 # Perform a method call through vft
538 private fun table_send(mmethod: TableCallable, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
539 var mdef: MMethodDef
540 var name: String
541 if mmethod isa MMethod then
542 mdef = mmethod.intro
543 name = mmethod.full_name
544 else if mmethod isa MMethodDef then
545 mdef = mmethod
546 name = mmethod.full_name
547 else
548 abort
549 end
550
551 var recv = arguments.first
552 var rect = mdef.mclassdef.bound_mtype
553 var msignature = mdef.msignature.as(not null)
554 msignature = msignature.resolve_for(rect, rect, compiler.mainmodule, true)
555 adapt_signature(mdef, arguments)
556
557 var res: nullable RuntimeVariable
558 var ret = msignature.return_mtype
559 if ret == null then
560 res = null
561 else
562 res = self.new_var(ret)
563 end
564
565 before_send(res, mdef, arguments)
566
567 add "/* concrete call to {mdef} */"
568 if res != null then
569 var ress = new_expr("{recv}.rtclass.vft.get(\"{name}\").exec(new RTVal[]\{{arguments.join(",")}\});", compiler.mainmodule.object_type)
570 assign(res, ress)
571 else
572 add("{recv}.rtclass.vft.get(\"{name}\").exec(new RTVal[]\{{arguments.join(",")}\});")
573 end
574
575 add("\}") # closes the null case
576
577 return res
578 end
579
580 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
581 fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable do
582 assert t isa MClassType
583 var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
584 return self.static_call(propdef, args)
585 end
586
587 # Code generation
588
589 # Add a line (will be suffixed by `\n`)
590 fun add(line: String) do file.lines.add("{line}\n")
591
592 # Add a new partial line (no `\n` suffix)
593 fun addn(line: String) do file.lines.add(line)
594
595 # Compile a statement (if any)
596 fun stmt(nexpr: nullable AExpr) do
597 if nexpr == null then return
598 var old = self.current_node
599 current_node = nexpr
600 nexpr.stmt(self)
601 current_node = old
602 end
603
604 # Compile an expression an return its result
605 # `mtype` is the expected return type, pass null if no specific type is expected.
606 fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable do
607 var old = current_node
608 current_node = nexpr
609
610 var res = null
611 if nexpr.mtype != null then
612 res = nexpr.expr(self)
613 end
614
615 if mtype != null then
616 mtype = anchor(mtype)
617 res = autobox(res, mtype)
618 end
619
620 current_node = old
621 return res
622 end
623
624 # Correctly assign a left and a right value
625 # Boxing and unboxing is performed if required
626 fun assign(left, right: RuntimeVariable) do
627 add("{left} = {autobox(right, left.mtype)};")
628 end
629
630 # Generate a return with `value`
631 fun ret(value: RuntimeVariable) do
632 var frame = self.frame
633 assert frame != null
634 var returnvar = frame.returnvar
635 if returnvar != null then
636 assign(returnvar, value)
637 end
638 self.add("break {frame.returnlabel.as(not null)};")
639 end
640
641 # Return a new local RuntimeVariable initialized with the Java expression `jexpr`.
642 #
643 # `mtype` is used for the Java return variable initialization.
644 fun new_expr(jexpr: String, mtype: MType): RuntimeVariable do
645 var res = new_var(mtype)
646 add("{res} = {jexpr};")
647 return res
648 end
649
650 # Generate generic abort
651 #
652 # Used by aborts, asserts, casts, etc.
653 fun add_abort(message: String) do
654 add("System.err.print(\"Runtime error: {message}\");")
655 var node = current_node
656 if node != null then
657 add("System.err.print(\" ({node.location.short_location})\");")
658 end
659 add("System.err.println(\"\");")
660 add("System.exit(1);")
661 end
662
663 # Types handling
664
665 # Anchor a type to the main module and the current receiver
666 fun anchor(mtype: MType): MType do
667 if not mtype.need_anchor then return mtype
668 return mtype.anchor_to(compiler.mainmodule, frame.as(not null).receiver)
669 end
670
671 # Adapt the arguments of a method according to targetted `MMethodDef`
672 fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) do
673 var msignature = m.msignature.as(not null).resolve_for(
674 m.mclassdef.bound_mtype,
675 m.mclassdef.bound_mtype,
676 m.mclassdef.mmodule, true)
677 args.first = autobox(args.first, compiler.mainmodule.object_type)
678 for i in [0..msignature.arity[ do
679 args[i+1] = autobox(args[i + 1], compiler.mainmodule.object_type)
680 end
681 end
682
683 # Box primitive `value` to `mtype`.
684 private fun box(value: RuntimeVariable, mtype: MType): RuntimeVariable do
685 if value.is_boxed then return value
686 var obj_type = compiler.mainmodule.object_type
687 if value.mtype isa MNullType then
688 return new_expr("new RTVal(null, null)", compiler.mainmodule.model.null_type)
689 end
690 var mbox = value.mtype.as(MClassType).mclass
691 return new_expr("new RTVal({mbox.rt_name}.get{mbox.rt_name}(), {value})", obj_type)
692 end
693
694 # Unbox primitive `value` to `mtype`.
695 private fun unbox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
696 if not value.is_boxed then return value
697 if not mtype.is_java_primitive then return value
698 if compiler.box_kinds.has(mtype) then
699 return new_expr("({mtype.java_type}){value}.value", mtype)
700 else
701 info "NOT YET IMPLEMENTED unbox for {value} ({mtype})"
702 abort
703 end
704 end
705
706 # Box or unbox primitive `value` to `mtype` if needed.
707 private fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
708 if mtype.is_java_primitive then return unbox(value, mtype)
709 return box(value, mtype)
710 end
711
712 # Can this `value` be a primitive Java value?
713 private fun can_be_primitive(value: RuntimeVariable): Bool do
714 var t = value.mcasttype.undecorate
715 if not t isa MClassType then return false
716 var k = t.mclass.kind
717 return k == interface_kind or t.is_java_primitive
718 end
719
720 # Native instances
721
722 # Generate an integer value
723 fun int_instance(value: Int): RuntimeVariable do
724 var t = compiler.mainmodule.int_type
725 return new RuntimeVariable(value.to_s, t, t)
726 end
727
728 # Generate a byte value
729 fun byte_instance(value: Byte): RuntimeVariable do
730 var t = compiler.mainmodule.byte_type
731 return new RuntimeVariable(value.to_s, t, t)
732 end
733
734 # Generate a char value
735 fun char_instance(value: Char): RuntimeVariable do
736 var t = compiler.mainmodule.char_type
737 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
738 end
739
740 # Generate a float value
741 #
742 # FIXME pass a Float, not a string
743 fun float_instance(value: String): RuntimeVariable do
744 var t = compiler.mainmodule.float_type
745 return new RuntimeVariable(value.to_s, t, t)
746 end
747
748 # Generate an integer value
749 fun bool_instance(value: Bool): RuntimeVariable do
750 var t = compiler.mainmodule.bool_type
751 return new RuntimeVariable(value.to_s, t, t)
752 end
753
754 # Generate the `null` value
755 fun null_instance: RuntimeVariable do
756 var t = compiler.mainmodule.model.null_type
757 return new RuntimeVariable("null", t, t)
758 end
759
760 # Get an instance of a array for a vararg
761 fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable do
762 # TODO handle dynamic types
763 info("NOT YET IMPLEMENTED vararg_instance")
764 return null_instance
765 # TODO return array_instance(varargs, elttype)
766 end
767
768 # Nit instances
769
770 # Generate a alloc-instance + init-attributes
771 fun init_instance(mtype: MClassType): RuntimeVariable do
772 var rt_name = mtype.mclass.rt_name
773 var res = new_expr("new RTVal({rt_name}.get{rt_name}())", mtype)
774 generate_init_attr(self, res, mtype)
775 return res
776 end
777
778 # Generate code that initialize the attributes on a new instance
779 fun generate_init_attr(v: JavaCompilerVisitor, recv: RuntimeVariable, mtype: MClassType) do
780 var cds = mtype.collect_mclassdefs(v.compiler.mainmodule).to_a
781 v.compiler.mainmodule.linearize_mclassdefs(cds)
782 for cd in cds do
783 for npropdef in v.compiler.modelbuilder.collect_attr_propdef(cd) do
784 npropdef.init_expr(v, recv)
785 end
786 end
787 end
788
789 # Generate a Nit "is" for two runtime_variables
790 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable do
791 var res = new_var(compiler.mainmodule.bool_type)
792 if value2.mtype.is_java_primitive and not value1.mtype.is_java_primitive then
793 var tmp = value1
794 value1 = value2
795 value2 = tmp
796 end
797 if value1.mtype.is_java_primitive then
798 if value2.mtype == value1.mtype then
799 add("{res} = {value1} == {value2}; /* == with two primitives */")
800 else if value2.mtype.is_java_primitive then
801 add("{res} = true; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
802 # else if value1.mtype.is_tagged then
803 # add("{res} = ({value2} != NULL) && ({autobox(value2, value1.mtype)} == {value1});")
804 else
805 var rt_name = value1.mtype.as(MClassType).mclass.rt_name
806 add("{res} = ({value2} != null) && ({value2}.rtclass == {rt_name}.get{rt_name}());")
807 add("if ({res}) \{")
808 add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
809 add("\}")
810 end
811 return res
812 end
813 var maybe_null = true
814 var test = new Array[String]
815 var t1 = value1.mcasttype
816 if t1 isa MNullableType then
817 test.add("{value1} != null && !{value1}.is_null()")
818 t1 = t1.mtype
819 else
820 maybe_null = false
821 end
822 var t2 = value2.mcasttype
823 if t2 isa MNullableType then
824 test.add("{value2} != null && !{value2}.is_null()")
825 t2 = t2.mtype
826 else
827 maybe_null = false
828 end
829
830 var incompatible = false
831 var primitive
832 if t1.is_java_primitive then
833 primitive = t1
834 if t1 == t2 then
835 # No need to compare class
836 else if t2.is_java_primitive then
837 incompatible = true
838 else if can_be_primitive(value2) then
839 if t1.is_java_primitive then
840 self.add("{res} = {value1} == {value2}; /* t1 is primitive and t2 can be */")
841 return res
842 end
843 # if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
844 # test.add("(!{extract_tag(value2)})")
845 # end
846 test.add("{value1}.rtclass == {value2}.rtclass")
847 else
848 incompatible = true
849 end
850 else if t2.is_java_primitive then
851 primitive = t2
852 if can_be_primitive(value1) then
853 if t2.is_java_primitive then
854 self.add("{res} = {value1} == {value2}; /* t2 is primitive and t1 can be */")
855 return res
856 end
857 test.add("{value1}.rtclass == {value2}.rtclass")
858 else
859 incompatible = true
860 end
861 else
862 primitive = null
863 end
864
865 if incompatible then
866 if maybe_null then
867 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
868 return res
869 else
870 self.add("{res} = false; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
871 return res
872 end
873 end
874 if primitive != null then
875 if primitive.is_java_primitive then
876 self.add("{res} = {value1} == {value2};")
877 return res
878 end
879 test.add("({value1}.value == {value2}.value")
880 else if can_be_primitive(value1) and can_be_primitive(value2) then
881 test.add("{value1}.rtclass == {value2}.rtclass")
882 var s = new Array[String]
883 for b in compiler.box_kinds do
884 var rt_name = b.mclass.rt_name
885 s.add "({value1}.rtclass == {rt_name}.get{rt_name}()) && ({value1}.value.equals({value2}.value))"
886 if b.mclass.name == "Float" then
887 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)"
888 end
889 end
890 if s.is_empty then
891 self.add("{res} = {value1} == {value2}; /* both can be primitive */")
892 return res
893 end
894 test.add("({s.join(" || ")})")
895 else
896 self.add("{res} = {value1} == {value2}; /* no primitives */")
897 return res
898 end
899 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
900 return res
901 end
902
903 # Attributes
904
905 # Generate a polymorphic attribute read
906 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable do
907 # TODO check_recv_notnull(recv)
908 # TODO compile_check(v)
909 # What is the declared type of the attribute?
910 var ret = a.intro.static_mtype.as(not null)
911 var intromclassdef = a.intro.mclassdef
912 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
913
914 return new_expr("{recv}.attrs.get(\"{a.jname}\")", ret)
915 end
916
917 # Generate a polymorphic attribute write
918 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) do
919 # TODO check_recv_notnull(recv)
920 add "{recv}.attrs.put(\"{a.jname}\", {autobox(value, compiler.mainmodule.object_type)});"
921 end
922
923 # Utils
924
925 # Display a info message
926 fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
927 end
928
929 # A file containing Java code.
930 class JavaCodeFile
931
932 # File name
933 var filename: String
934
935 # Lines to write
936 var lines: List[String] = new List[String]
937 end
938
939 redef class MEntity
940 # A Java compatible name for `self`
941 private fun jname: String do return name.to_cmangle
942 end
943
944 # Handler for runtime classes generation
945 #
946 # We need 3 kinds of runtime structures:
947 # * `RTClass` to represent a global class
948 # * `RTMethod` to represent a method definition
949 # * `RTVal` to represent runtime variables
950 class JavaRuntimeModel
951
952 # Compile JavaRuntimeModel structures
953 fun compile_rtmodel(compiler: JavaCompiler) do
954 compile_rtclass(compiler)
955 compile_rtmethod(compiler)
956 compile_rtval(compiler)
957 end
958
959 # Compile the abstract runtime class structure
960 #
961 # Runtime classes have 3 attributes:
962 # * `class_name`: the class name as a String
963 # * `vft`: the virtual function table for the class (flattened)
964 # * `supers`: the super type table (used for type tests)
965 fun compile_rtclass(compiler: JavaCompiler) do
966 var v = compiler.new_visitor("RTClass.java")
967 v.add("import java.util.HashMap;")
968 v.add("public abstract class RTClass \{")
969 v.add(" public String class_name;")
970 v.add(" public HashMap<String, RTMethod> vft = new HashMap<>();")
971 v.add(" public HashMap<String, RTClass> supers = new HashMap<>();")
972 v.add(" protected RTClass() \{\}")
973 v.add("\}")
974 end
975
976 # Compile the abstract runtime method structure
977 #
978 # Method body is executed through the `exec` method:
979 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
980 # * `exec` always returns a RTVal (or null if the Nit return type is void)
981 fun compile_rtmethod(compiler: JavaCompiler) do
982 var v = compiler.new_visitor("RTMethod.java")
983 v.add("public abstract class RTMethod \{")
984 v.add(" protected RTMethod() \{\}")
985 v.add(" public abstract RTVal exec(RTVal[] args);")
986 v.add("\}")
987 end
988
989 # Compile the runtime value structure
990 #
991 # RTVal both represents object instances and primitives values:
992 # * object instances:
993 # * `rtclass` the class of the RTVal is instance of
994 # * `attrs` contains the attributes of the instance
995 # * primitive values:
996 # * `rtclass` represents the class of the primitive value Nit type
997 # * `value` contains the primitive value of the instance
998 # * null values:
999 # * they must have both `rtclass` and `value` as null
1000 fun compile_rtval(compiler: JavaCompiler) do
1001 var v = compiler.new_visitor("RTVal.java")
1002 v.add("import java.util.HashMap;")
1003 v.add("public class RTVal \{")
1004 v.add(" public RTClass rtclass;")
1005 v.add(" public HashMap<String, RTVal> attrs = new HashMap<>();")
1006 v.add(" Object value;")
1007 v.add(" public RTVal(RTClass rtclass) \{")
1008 v.add(" this.rtclass = rtclass;")
1009 v.add(" \}")
1010 v.add(" public RTVal(RTClass rtclass, Object value) \{")
1011 v.add(" this.rtclass = rtclass;")
1012 v.add(" this.value = value;")
1013 v.add(" \}")
1014 v.add(" public boolean is_null() \{ return rtclass == null && value == null; \}")
1015 v.add("\}")
1016 end
1017 end
1018
1019 # A runtime variable hold a runtime value in Java.
1020 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1021 class RuntimeVariable
1022
1023 # The name of the variable in the Java code
1024 var name: String
1025
1026 # The static type of the variable (as declard in Java)
1027 var mtype: MType
1028
1029 # The current casted type of the variable (as known in Nit)
1030 var mcasttype: MType is writable
1031
1032 # If the variable exaclty a mcasttype?
1033 # false (usual value) means that the variable is a mcasttype or a subtype.
1034 var is_exact: Bool = false is writable
1035
1036 # Is this variable declared as a RTVal or a Java primitive one?
1037 var is_boxed = false
1038
1039 redef fun to_s do return name
1040
1041 redef fun inspect
1042 do
1043 var exact_str
1044 if self.is_exact then
1045 exact_str = " exact"
1046 else
1047 exact_str = ""
1048 end
1049 var type_str
1050 if self.mtype == self.mcasttype then
1051 type_str = "{mtype}{exact_str}"
1052 else
1053 type_str = "{mtype}({mcasttype}{exact_str})"
1054 end
1055 return "<{name}:{type_str}>"
1056 end
1057 end
1058
1059 # The static context of a visited property in a `JavaCompilerVisitor`
1060 class JavaStaticFrame
1061 # The associated visitor
1062 var visitor: JavaCompilerVisitor
1063
1064 # The executed property.
1065 # A Method in case of a call, an attribute in case of a default initialization.
1066 var mpropdef: MPropDef
1067
1068 # The static type of the receiver
1069 var receiver: MClassType
1070
1071 # Arguments of the method (the first is the receiver)
1072 var arguments: Array[RuntimeVariable]
1073
1074 # The runtime_variable associated to the return (in a function)
1075 var returnvar: nullable RuntimeVariable = null is writable
1076
1077 # The label at the end of the property
1078 var returnlabel: nullable String = null is writable
1079 end
1080
1081 redef class Location
1082 # Return a shortened version of the location with `"{file}:{line_start}"`
1083 fun short_location: String do
1084 var file = self.file
1085 if file == null then return "<no file>:{line_start}"
1086 return "{file.filename.escape_to_c}:{line_start}"
1087 end
1088 end
1089
1090 redef class MType
1091 # Return the Java type associated to a given Nit static type
1092 fun java_type: String do return "RTVal"
1093
1094 # Is the associated Java type a primitive one?
1095 #
1096 # ENSURE `result == (java_type != "Object")`
1097 var is_java_primitive: Bool is lazy do return java_type != "RTVal"
1098 end
1099
1100 redef class MClassType
1101
1102 redef var java_type is lazy do
1103 if mclass.name == "Int" then
1104 return "int"
1105 else if mclass.name == "Bool" then
1106 return "boolean"
1107 else if mclass.name == "Char" then
1108 return "char"
1109 else if mclass.name == "Float" then
1110 return "double"
1111 else if mclass.name == "Byte" then
1112 return "byte"
1113 else if mclass.name == "NativeString" then
1114 return "String"
1115 else if mclass.name == "NativeArray" then
1116 return "Array"
1117 end
1118 return "RTVal"
1119 end
1120 end
1121
1122 redef class MClass
1123
1124 # Runtime name
1125 private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}"
1126
1127 # Generate a Java RTClass for a Nit MClass
1128 fun compile_to_java(v: JavaCompilerVisitor) do
1129 v.add("public class {rt_name} extends RTClass \{")
1130 v.add(" protected static RTClass instance;")
1131 v.add(" private {rt_name}() \{")
1132 v.add(" this.class_name = \"{name}\";")
1133 compile_vft(v)
1134 compile_type_table(v)
1135 v.add(" \}")
1136 v.add(" public static RTClass get{rt_name}() \{")
1137 v.add(" if(instance == null) \{")
1138 v.add(" instance = new {rt_name}();")
1139 v.add(" \}")
1140 v.add(" return instance;")
1141 v.add(" \}")
1142 v.add("\}")
1143 end
1144
1145 # Compile the virtual function table for the mclass
1146 private fun compile_vft(v: JavaCompilerVisitor) do
1147 # TODO handle generics
1148 if mclass_type.need_anchor then return
1149 var mclassdefs = mclass_type.collect_mclassdefs(v.compiler.mainmodule).to_a
1150 v.compiler.mainmodule.linearize_mclassdefs(mclassdefs)
1151
1152 var mainmodule = v.compiler.mainmodule
1153 for mclassdef in mclassdefs.reversed do
1154 for mprop in mclassdef.intro_mproperties do
1155 var mpropdef = mprop.lookup_first_definition(mainmodule, intro.bound_mtype)
1156 if not mpropdef isa MMethodDef then continue
1157 var rt_name = mpropdef.rt_name
1158 v.add("this.vft.put(\"{mprop.full_name}\", {rt_name}.get{rt_name}());")
1159
1160 # fill super next definitions
1161 while mpropdef.has_supercall do
1162 var prefix = mpropdef.full_name
1163 mpropdef = mpropdef.lookup_next_definition(mainmodule, intro.bound_mtype)
1164 rt_name = mpropdef.rt_name
1165 v.add("this.vft.put(\"{prefix}\", {rt_name}.get{rt_name}());")
1166 end
1167 end
1168 end
1169 end
1170
1171 # Compile the type table for the MClass
1172 fun compile_type_table(v: JavaCompilerVisitor) do
1173 for pclass in in_hierarchy(v.compiler.mainmodule).greaters do
1174 if pclass == self then
1175 v.add("supers.put(\"{pclass.jname}\", this);")
1176 else
1177 v.add("supers.put(\"{pclass.jname}\", {pclass.rt_name}.get{pclass.rt_name}());")
1178 end
1179 end
1180 end
1181 end
1182
1183 # Used as a common type between MMethod and MMethodDef for `table_send`
1184 private interface TableCallable
1185 end
1186
1187 redef class MMethod
1188 super TableCallable
1189 end
1190
1191 redef class MMethodDef
1192 super TableCallable
1193
1194 # Runtime name
1195 private fun rt_name: String do
1196 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
1197 end
1198
1199 # Generate a Java RTMethod for `self`
1200 fun compile_to_java(v: JavaCompilerVisitor) do
1201 v.add("public class {rt_name} extends RTMethod \{")
1202 v.add(" protected static RTMethod instance;")
1203 v.add(" public static RTMethod get{rt_name}() \{")
1204 v.add(" if(instance == null) \{")
1205 v.add(" instance = new {rt_name}();")
1206 v.add(" \}")
1207 v.add(" return instance;")
1208 v.add(" \}")
1209 v.add(" @Override")
1210 v.add(" public RTVal exec(RTVal[] args) \{")
1211 compile_inside_to_java(v)
1212 v.add(" \}")
1213 v.add("\}")
1214 end
1215
1216 # Compile the body of this function
1217 fun compile_inside_to_java(v: JavaCompilerVisitor) do
1218
1219 var modelbuilder = v.compiler.modelbuilder
1220 var node = modelbuilder.mpropdef2node(self)
1221
1222 if is_abstract then
1223 v.add_abort("Abstract method `{mproperty.name}` called on `\" + {selfvar}.rtclass.class_name +\"`")
1224 v.add("return null;")
1225 return
1226 end
1227
1228 if node isa APropdef then
1229 node.compile_to_java(v, self)
1230 else if node isa AClassdef then
1231 node.compile_to_java(v, self)
1232 else
1233 abort
1234 end
1235 end
1236 end
1237
1238 redef class AClassdef
1239 private fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1240 if mpropdef == self.mfree_init then
1241 assert mpropdef.mproperty.is_root_init
1242 if not mpropdef.is_intro then
1243 # TODO v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1244 end
1245 else
1246 abort
1247 end
1248 v.add("return null;")
1249 end
1250 end
1251
1252 redef class APropdef
1253
1254 # Compile that property definition to java code
1255 fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1256 v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
1257 v.add("return null;")
1258 end
1259 end
1260
1261 redef class AMethPropdef
1262 redef fun compile_to_java(v, mpropdef) do
1263 # TODO Call the implicit super-init
1264
1265 var recv = mpropdef.mclassdef.bound_mtype
1266 var arguments = new Array[RuntimeVariable]
1267 var frame = new JavaStaticFrame(v, mpropdef, recv, arguments)
1268 v.frame = frame
1269
1270 var selfvar = v.decl_var("self", recv)
1271 arguments.add(selfvar)
1272 var boxed = v.new_expr("args[0];", v.compiler.mainmodule.object_type)
1273 v.add "{selfvar} = {v.unbox(boxed, recv)};"
1274
1275 var msignature = mpropdef.msignature
1276 var ret = null
1277 if msignature != null then
1278 ret = msignature.return_mtype
1279 if ret != null then frame.returnvar = v.new_var(ret)
1280 end
1281 frame.returnlabel = v.get_name("RET_LABEL")
1282
1283 if not mpropdef.is_intern and msignature != null then
1284 var i = 0
1285 for mparam in msignature.mparameters do
1286 var variable = n_signature.as(not null).n_params[i].variable
1287 if variable == null then continue
1288 var argvar = v.variable(variable)
1289 boxed = v.new_expr("args[{i + 1}];", v.compiler.mainmodule.object_type)
1290 v.add "{argvar} = {v.unbox(boxed, mparam.mtype)};"
1291 arguments.add(argvar)
1292 i += 1
1293 end
1294 end
1295
1296 v.add("{frame.returnlabel.as(not null)}: \{")
1297 compile_inside_to_java(v, mpropdef)
1298 v.add("\}")
1299
1300 if ret != null then
1301 if ret.is_java_primitive then
1302 boxed = v.box(frame.returnvar.as(not null), v.compiler.mainmodule.object_type)
1303 v.add("return {boxed};")
1304 else
1305 v.add("return {frame.returnvar.as(not null)};")
1306 end
1307 else
1308 v.add("return null;")
1309 end
1310 v.frame = null
1311 end
1312
1313 # Compile the inside of the method body
1314 private fun compile_inside_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1315 # Compile intern methods
1316 if mpropdef.is_intern then
1317 if compile_intern_to_java(v, mpropdef, arguments) then return
1318 v.info("NOT YET IMPLEMENTED compile_intern for {mpropdef}")
1319 v.ret(v.null_instance)
1320 return
1321 end
1322
1323 # Compile block if any
1324 var n_block = n_block
1325 if n_block != null then
1326 v.stmt(n_block)
1327 return
1328 end
1329 end
1330
1331 # Compile an intern method using Java primitives
1332 fun compile_intern_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do
1333 var pname = mpropdef.mproperty.name
1334 var cname = mpropdef.mclassdef.mclass.name
1335 var ret = mpropdef.msignature.as(not null).return_mtype
1336 if cname == "Int" then
1337 if pname == "output" then
1338 v.add("System.out.println({arguments[0]});")
1339 v.ret(v.null_instance)
1340 return true
1341 else if pname == "object_id" then
1342 v.ret(arguments.first)
1343 return true
1344 else if pname == "+" then
1345 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1346 return true
1347 else if pname == "-" then
1348 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1349 return true
1350 else if pname == "unary -" then
1351 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1352 return true
1353 else if pname == "unary +" then
1354 v.ret(arguments[0])
1355 return true
1356 else if pname == "*" then
1357 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1358 return true
1359 else if pname == "/" then
1360 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1361 return true
1362 else if pname == "%" then
1363 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1364 return true
1365 else if pname == "lshift" then
1366 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1367 return true
1368 else if pname == "rshift" then
1369 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1370 return true
1371 else if pname == "==" then
1372 v.ret(v.equal_test(arguments[0], arguments[1]))
1373 return true
1374 else if pname == "!=" then
1375 var res = v.equal_test(arguments[0], arguments[1])
1376 v.ret(v.new_expr("!{res}", ret.as(not null)))
1377 return true
1378 else if pname == "<" then
1379 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1380 return true
1381 else if pname == ">" then
1382 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1383 return true
1384 else if pname == "<=" then
1385 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1386 return true
1387 else if pname == ">=" then
1388 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1389 return true
1390 else if pname == "to_f" then
1391 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1392 return true
1393 else if pname == "to_b" then
1394 v.ret(v.new_expr("(byte){arguments[0]}", ret.as(not null)))
1395 return true
1396 else if pname == "ascii" then
1397 v.ret(v.new_expr("(char){arguments[0]}", ret.as(not null)))
1398 return true
1399 end
1400 else if cname == "Char" then
1401 if pname == "output" then
1402 v.add("System.out.print({arguments[0]});")
1403 v.ret(v.null_instance)
1404 return true
1405 else if pname == "object_id" then
1406 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1407 return true
1408 else if pname == "successor" then
1409 v.ret(v.new_expr("(char)({arguments[0]} + {arguments[1]})", ret.as(not null)))
1410 return true
1411 else if pname == "predecessor" then
1412 v.ret(v.new_expr("(char)({arguments[0]} - {arguments[1]})", ret.as(not null)))
1413 return true
1414 else if pname == "==" then
1415 v.ret(v.equal_test(arguments[0], arguments[1]))
1416 return true
1417 else if pname == "!=" then
1418 var res = v.equal_test(arguments[0], arguments[1])
1419 v.ret(v.new_expr("!{res}", ret.as(not null)))
1420 return true
1421 else if pname == "<" then
1422 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1423 return true
1424 else if pname == ">" then
1425 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1426 return true
1427 else if pname == "<=" then
1428 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1429 return true
1430 else if pname == ">=" then
1431 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1432 return true
1433 else if pname == "to_i" then
1434 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1435 return true
1436 else if pname == "ascii" then
1437 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1438 return true
1439 end
1440 else if cname == "Byte" then
1441 if pname == "output" then
1442 v.add("System.out.println({arguments[0]});")
1443 v.ret(v.null_instance)
1444 return true
1445 else if pname == "object_id" then
1446 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1447 return true
1448 else if pname == "+" then
1449 v.ret(v.new_expr("(byte)({arguments[0]} + {arguments[1]})", ret.as(not null)))
1450 return true
1451 else if pname == "-" then
1452 v.ret(v.new_expr("(byte)({arguments[0]} - {arguments[1]})", ret.as(not null)))
1453 return true
1454 else if pname == "unary -" then
1455 v.ret(v.new_expr("(byte)(-{arguments[0]})", ret.as(not null)))
1456 return true
1457 else if pname == "unary +" then
1458 v.ret(arguments[0])
1459 return true
1460 else if pname == "*" then
1461 v.ret(v.new_expr("(byte)({arguments[0]} * {arguments[1]})", ret.as(not null)))
1462 return true
1463 else if pname == "/" then
1464 v.ret(v.new_expr("(byte)({arguments[0]} / {arguments[1]})", ret.as(not null)))
1465 return true
1466 else if pname == "%" then
1467 v.ret(v.new_expr("(byte)({arguments[0]} % {arguments[1]})", ret.as(not null)))
1468 return true
1469 else if pname == "lshift" then
1470 v.ret(v.new_expr("(byte)({arguments[0]} << {arguments[1]})", ret.as(not null)))
1471 return true
1472 else if pname == "rshift" then
1473 v.ret(v.new_expr("(byte)({arguments[0]} >> {arguments[1]})", ret.as(not null)))
1474 return true
1475 else if pname == "==" then
1476 v.ret(v.equal_test(arguments[0], arguments[1]))
1477 return true
1478 else if pname == "!=" then
1479 var res = v.equal_test(arguments[0], arguments[1])
1480 v.ret(v.new_expr("!{res}", ret.as(not null)))
1481 return true
1482 else if pname == "<" then
1483 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1484 return true
1485 else if pname == ">" then
1486 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1487 return true
1488 else if pname == "<=" then
1489 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1490 return true
1491 else if pname == ">=" then
1492 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1493 return true
1494 else if pname == "to_i" then
1495 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1496 return true
1497 else if pname == "to_f" then
1498 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1499 return true
1500 else if pname == "ascii" then
1501 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1502 return true
1503 end
1504 else if cname == "Bool" then
1505 if pname == "output" then
1506 v.add("System.out.println({arguments[0]});")
1507 v.ret(v.null_instance)
1508 return true
1509 else if pname == "object_id" then
1510 v.ret(v.new_expr("{arguments[0]}?1:0", ret.as(not null)))
1511 return true
1512 else if pname == "==" then
1513 v.ret(v.equal_test(arguments[0], arguments[1]))
1514 return true
1515 else if pname == "!=" then
1516 var res = v.equal_test(arguments[0], arguments[1])
1517 v.ret(v.new_expr("!{res}", ret.as(not null)))
1518 return true
1519 end
1520 else if cname == "Float" then
1521 if pname == "output" then
1522 v.add "if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
1523 v.add "System.out.println(\"inf\");"
1524 v.add "\} else if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
1525 v.add "System.out.println(\"-inf\");"
1526 v.add "\} else \{"
1527 var df = v.get_name("df")
1528 v.add "java.text.DecimalFormat {df} = new java.text.DecimalFormat(\"0.000000\");"
1529 v.add "System.out.println({df}.format({arguments[0]}));"
1530 v.add "\}"
1531 v.ret(v.null_instance)
1532 return true
1533 else if pname == "object_id" then
1534 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1535 return true
1536 else if pname == "+" then
1537 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1538 return true
1539 else if pname == "-" then
1540 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1541 return true
1542 else if pname == "unary -" then
1543 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1544 return true
1545 else if pname == "unary +" then
1546 v.ret(arguments[0])
1547 return true
1548 else if pname == "succ" then
1549 v.ret(v.new_expr("{arguments[0]} + 1", ret.as(not null)))
1550 return true
1551 else if pname == "prec" then
1552 v.ret(v.new_expr("{arguments[0]} - 1", ret.as(not null)))
1553 return true
1554 else if pname == "*" then
1555 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1556 return true
1557 else if pname == "/" then
1558 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1559 return true
1560 else if pname == "==" then
1561 v.ret(v.equal_test(arguments[0], arguments[1]))
1562 return true
1563 else if pname == "!=" then
1564 var res = v.equal_test(arguments[0], arguments[1])
1565 v.ret(v.new_expr("!{res}", 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 == ">=" then
1577 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1578 return true
1579 else if pname == "to_i" then
1580 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1581 return true
1582 else if pname == "to_b" then
1583 v.ret(v.new_expr("(byte){arguments[0]}", ret.as(not null)))
1584 return true
1585 end
1586 end
1587 if pname == "exit" then
1588 v.add("System.exit({arguments[1]});")
1589 v.ret(v.null_instance)
1590 return true
1591 else if pname == "sys" then
1592 # TODO singleton
1593 var main_type = v.compiler.mainmodule.sys_type.as(not null)
1594 var sys = main_type.mclass
1595 v.ret(v.new_expr("new RTVal({sys.rt_name}.get{sys.rt_name}())", main_type))
1596 return true
1597 else if pname == "object_id" then
1598 v.ret(v.new_expr("{arguments[0]}.hashCode()", ret.as(not null)))
1599 return true
1600 else if pname == "is_same_type" then
1601 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1602 return true
1603 else if pname == "is_same_instance" then
1604 v.ret(v.equal_test(arguments[0], arguments[1]))
1605 return true
1606 else if pname == "output_class_name" then
1607 v.add("System.out.println({arguments[0]}.rtclass.class_name);")
1608 v.ret(v.null_instance)
1609 return true
1610 end
1611 return false
1612 end
1613 end
1614
1615 redef class AAttrPropdef
1616 redef fun compile_to_java(v, mpropdef, arguments) do
1617 v.current_node = self
1618 if mpropdef == mreadpropdef then
1619 compile_getter(v, mpropdef, arguments)
1620 else if mpropdef == mwritepropdef then
1621 compile_setter(v, mpropdef, arguments)
1622 else
1623 abort
1624 end
1625 v.current_node = null
1626 end
1627
1628 # Compile the setter method
1629 private fun compile_setter(v: JavaCompilerVisitor, mpropdef: MPropDef, arguments: Array[RuntimeVariable]) do
1630 var mtype = v.compiler.mainmodule.object_type
1631 var recv = arguments.first
1632 var val = v.new_expr("args[1]", mtype)
1633 v.write_attribute(self.mpropdef.as(not null).mproperty, recv, val)
1634 v.ret v.null_instance
1635 end
1636
1637 # Compile the getter method
1638 private fun compile_getter(v: JavaCompilerVisitor, mpropdef: MPropDef, arguments: Array[RuntimeVariable]) do
1639 var recv = arguments.first
1640 v.ret v.read_attribute(self.mpropdef.as(not null).mproperty, recv)
1641 end
1642
1643 private fun init_expr(v: JavaCompilerVisitor, recv: RuntimeVariable) do
1644 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
1645 end
1646
1647 # Evaluate, store and return the default value of the attribute
1648 private fun evaluate_expr(v: JavaCompilerVisitor, recv: RuntimeVariable): RuntimeVariable do
1649 var old = v.frame
1650 var frame = new JavaStaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
1651 v.frame = frame
1652
1653 var value
1654 var mtype = self.mtype
1655 assert mtype != null
1656
1657 var nexpr = self.n_expr
1658 var nblock = self.n_block
1659 if nexpr != null then
1660 value = v.expr(nexpr, mtype)
1661 else if nblock != null then
1662 value = v.new_var(mtype)
1663 frame.returnvar = value
1664 frame.returnlabel = v.get_name("RET_LABEL")
1665 v.add("\{")
1666 v.stmt(nblock)
1667 v.add("{frame.returnlabel.as(not null)}:(void)0;")
1668 v.add("\}")
1669 else
1670 abort
1671 end
1672
1673 v.write_attribute(self.mpropdef.as(not null).mproperty, recv, value)
1674 v.frame = old
1675 return value
1676 end
1677 end
1678
1679 redef class AExpr
1680 # Try to compile self as an expression
1681 # Do not call this method directly, use `v.expr` instead
1682 private fun expr(v: JavaCompilerVisitor): nullable RuntimeVariable do
1683 v.info("NOT YET IMPLEMENTED {class_name}::expr")
1684 return null
1685 end
1686
1687 # Try to compile self as a statement
1688 # Do not call this method directly, use `v.stmt` instead
1689 private fun stmt(v: JavaCompilerVisitor) do expr(v)
1690 end
1691
1692 redef class ABlockExpr
1693 redef fun stmt(v)
1694 do
1695 for e in self.n_expr do v.stmt(e)
1696 end
1697 redef fun expr(v)
1698 do
1699 var last = self.n_expr.last
1700 for e in self.n_expr do
1701 if e == last then break
1702 v.stmt(e)
1703 end
1704 return v.expr(last, null)
1705 end
1706 end
1707
1708 redef class ASendExpr
1709 redef fun expr(v) do
1710 var recv = v.expr(n_expr, null)
1711 var callsite = callsite.as(not null)
1712 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, raw_arguments)
1713 return v.compile_callsite(callsite, args)
1714 end
1715 end
1716
1717 redef class ANewExpr
1718 redef fun expr(v)
1719 do
1720 var mtype = self.recvtype
1721 assert mtype != null
1722
1723 if mtype.mclass.name == "NativeArray" then
1724 # TODO handle native arrays
1725 v.info("NOT YET IMPLEMENTED new NativeArray")
1726 end
1727
1728 var recv = v.init_instance(mtype)
1729
1730 var callsite = self.callsite
1731 if callsite == null then return recv
1732
1733 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
1734 var res2 = v.compile_callsite(callsite, args)
1735 if res2 != null then
1736 return res2
1737 end
1738 return recv
1739 end
1740 end
1741
1742 redef class ASelfExpr
1743 redef fun expr(v) do return v.frame.as(not null).arguments.first
1744 end
1745
1746 redef class AImplicitSelfExpr
1747 redef fun expr(v) do return v.frame.as(not null).arguments.first
1748 end
1749
1750 redef class AReturnExpr
1751 redef fun stmt(v) do
1752 var nexpr = self.n_expr
1753 var frame = v.frame
1754 assert frame != null
1755 if nexpr != null then
1756 v.ret(v.expr(nexpr, frame.returnvar.as(not null).mtype))
1757 else
1758 v.ret(v.null_instance)
1759 end
1760 end
1761 end
1762
1763 redef class AVardeclExpr
1764 redef fun stmt(v) do
1765 var variable = self.variable.as(not null)
1766 var ne = self.n_expr
1767 var decl = v.variable(variable)
1768 if ne != null then
1769 var i = v.expr(ne, variable.declared_type)
1770 v.assign(decl, i)
1771 end
1772 end
1773 end
1774
1775 redef class AVarExpr
1776 redef fun expr(v) do
1777 return v.variable(self.variable.as(not null))
1778 end
1779 end
1780
1781 redef class AVarAssignExpr
1782 redef fun expr(v) do
1783 var variable = self.variable.as(not null)
1784 var i = v.expr(self.n_value, variable.declared_type)
1785 v.assign(v.variable(variable), i)
1786 return i
1787 end
1788 end
1789
1790 redef class AIntExpr
1791 redef fun expr(v) do return v.int_instance(self.value.as(not null))
1792 end
1793
1794 redef class AByteExpr
1795 redef fun expr(v) do return v.byte_instance(self.value.as(not null))
1796 end
1797
1798 redef class AFloatExpr
1799 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
1800 end
1801
1802 redef class ACharExpr
1803 redef fun expr(v) do return v.char_instance(self.value.as(not null))
1804 end
1805
1806 redef class ATrueExpr
1807 redef fun expr(v) do return v.bool_instance(true)
1808 end
1809
1810 redef class AFalseExpr
1811 redef fun expr(v) do return v.bool_instance(false)
1812 end
1813
1814 redef class ANullExpr
1815 redef fun expr(v) do return v.null_instance
1816 end
1817
1818 redef class AAbortExpr
1819 redef fun stmt(v) do v.add_abort("Aborted")
1820 end
1821
1822 redef class ADebugTypeExpr
1823 redef fun stmt(v) do end # do nothing
1824 end