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