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