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