nitj: compile intern methods bodies
[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 # Utils
883
884 # Display a info message
885 fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
886 end
887
888 # A file containing Java code.
889 class JavaCodeFile
890
891 # File name
892 var filename: String
893
894 # Lines to write
895 var lines: List[String] = new List[String]
896 end
897
898 redef class MEntity
899 # A Java compatible name for `self`
900 private fun jname: String do return name.to_cmangle
901 end
902
903 # Handler for runtime classes generation
904 #
905 # We need 3 kinds of runtime structures:
906 # * `RTClass` to represent a global class
907 # * `RTMethod` to represent a method definition
908 # * `RTVal` to represent runtime variables
909 class JavaRuntimeModel
910
911 # Compile JavaRuntimeModel structures
912 fun compile_rtmodel(compiler: JavaCompiler) do
913 compile_rtclass(compiler)
914 compile_rtmethod(compiler)
915 compile_rtval(compiler)
916 end
917
918 # Compile the abstract runtime class structure
919 #
920 # Runtime classes have 3 attributes:
921 # * `class_name`: the class name as a String
922 # * `vft`: the virtual function table for the class (flattened)
923 # * `supers`: the super type table (used for type tests)
924 fun compile_rtclass(compiler: JavaCompiler) do
925 var v = compiler.new_visitor("RTClass.java")
926 v.add("import java.util.HashMap;")
927 v.add("public abstract class RTClass \{")
928 v.add(" public String class_name;")
929 v.add(" public HashMap<String, RTMethod> vft = new HashMap<>();")
930 v.add(" public HashMap<String, RTClass> supers = new HashMap<>();")
931 v.add(" protected RTClass() \{\}")
932 v.add("\}")
933 end
934
935 # Compile the abstract runtime method structure
936 #
937 # Method body is executed through the `exec` method:
938 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
939 # * `exec` always returns a RTVal (or null if the Nit return type is void)
940 fun compile_rtmethod(compiler: JavaCompiler) do
941 var v = compiler.new_visitor("RTMethod.java")
942 v.add("public abstract class RTMethod \{")
943 v.add(" protected RTMethod() \{\}")
944 v.add(" public abstract RTVal exec(RTVal[] args);")
945 v.add("\}")
946 end
947
948 # Compile the runtime value structure
949 #
950 # RTVal both represents object instances and primitives values:
951 # * object instances:
952 # * `rtclass` the class of the RTVal is instance of
953 # * `attrs` contains the attributes of the instance
954 # * primitive values:
955 # * `rtclass` represents the class of the primitive value Nit type
956 # * `value` contains the primitive value of the instance
957 # * null values:
958 # * they must have both `rtclass` and `value` as null
959 fun compile_rtval(compiler: JavaCompiler) do
960 var v = compiler.new_visitor("RTVal.java")
961 v.add("import java.util.HashMap;")
962 v.add("public class RTVal \{")
963 v.add(" public RTClass rtclass;")
964 v.add(" public HashMap<String, RTVal> attrs = new HashMap<>();")
965 v.add(" Object value;")
966 v.add(" public RTVal(RTClass rtclass) \{")
967 v.add(" this.rtclass = rtclass;")
968 v.add(" \}")
969 v.add(" public RTVal(RTClass rtclass, Object value) \{")
970 v.add(" this.rtclass = rtclass;")
971 v.add(" this.value = value;")
972 v.add(" \}")
973 v.add(" public boolean is_null() \{ return rtclass == null && value == null; \}")
974 v.add("\}")
975 end
976 end
977
978 # A runtime variable hold a runtime value in Java.
979 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
980 class RuntimeVariable
981
982 # The name of the variable in the Java code
983 var name: String
984
985 # The static type of the variable (as declard in Java)
986 var mtype: MType
987
988 # The current casted type of the variable (as known in Nit)
989 var mcasttype: MType is writable
990
991 # If the variable exaclty a mcasttype?
992 # false (usual value) means that the variable is a mcasttype or a subtype.
993 var is_exact: Bool = false is writable
994
995 # Is this variable declared as a RTVal or a Java primitive one?
996 var is_boxed = false
997
998 redef fun to_s do return name
999
1000 redef fun inspect
1001 do
1002 var exact_str
1003 if self.is_exact then
1004 exact_str = " exact"
1005 else
1006 exact_str = ""
1007 end
1008 var type_str
1009 if self.mtype == self.mcasttype then
1010 type_str = "{mtype}{exact_str}"
1011 else
1012 type_str = "{mtype}({mcasttype}{exact_str})"
1013 end
1014 return "<{name}:{type_str}>"
1015 end
1016 end
1017
1018 # The static context of a visited property in a `JavaCompilerVisitor`
1019 class JavaStaticFrame
1020 # The associated visitor
1021 var visitor: JavaCompilerVisitor
1022
1023 # The executed property.
1024 # A Method in case of a call, an attribute in case of a default initialization.
1025 var mpropdef: MPropDef
1026
1027 # The static type of the receiver
1028 var receiver: MClassType
1029
1030 # Arguments of the method (the first is the receiver)
1031 var arguments: Array[RuntimeVariable]
1032
1033 # The runtime_variable associated to the return (in a function)
1034 var returnvar: nullable RuntimeVariable = null is writable
1035
1036 # The label at the end of the property
1037 var returnlabel: nullable String = null is writable
1038 end
1039
1040 redef class Location
1041 # Return a shortened version of the location with `"{file}:{line_start}"`
1042 fun short_location: String do
1043 var file = self.file
1044 if file == null then return "<no file>:{line_start}"
1045 return "{file.filename.escape_to_c}:{line_start}"
1046 end
1047 end
1048
1049 redef class MType
1050 # Return the Java type associated to a given Nit static type
1051 fun java_type: String do return "RTVal"
1052
1053 # Is the associated Java type a primitive one?
1054 #
1055 # ENSURE `result == (java_type != "Object")`
1056 var is_java_primitive: Bool is lazy do return java_type != "RTVal"
1057 end
1058
1059 redef class MClassType
1060
1061 redef var java_type is lazy do
1062 if mclass.name == "Int" then
1063 return "int"
1064 else if mclass.name == "Bool" then
1065 return "boolean"
1066 else if mclass.name == "Char" then
1067 return "char"
1068 else if mclass.name == "Float" then
1069 return "double"
1070 else if mclass.name == "Byte" then
1071 return "byte"
1072 else if mclass.name == "NativeString" then
1073 return "String"
1074 else if mclass.name == "NativeArray" then
1075 return "Array"
1076 end
1077 return "RTVal"
1078 end
1079 end
1080
1081 redef class MClass
1082
1083 # Runtime name
1084 private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}"
1085
1086 # Generate a Java RTClass for a Nit MClass
1087 fun compile_to_java(v: JavaCompilerVisitor) do
1088 v.add("public class {rt_name} extends RTClass \{")
1089 v.add(" protected static RTClass instance;")
1090 v.add(" private {rt_name}() \{")
1091 v.add(" this.class_name = \"{name}\";")
1092 compile_vft(v)
1093 compile_type_table(v)
1094 v.add(" \}")
1095 v.add(" public static RTClass get{rt_name}() \{")
1096 v.add(" if(instance == null) \{")
1097 v.add(" instance = new {rt_name}();")
1098 v.add(" \}")
1099 v.add(" return instance;")
1100 v.add(" \}")
1101 v.add("\}")
1102 end
1103
1104 # Compile the virtual function table for the mclass
1105 private fun compile_vft(v: JavaCompilerVisitor) do
1106 # TODO handle generics
1107 if mclass_type.need_anchor then return
1108 var mclassdefs = mclass_type.collect_mclassdefs(v.compiler.mainmodule).to_a
1109 v.compiler.mainmodule.linearize_mclassdefs(mclassdefs)
1110
1111 var mainmodule = v.compiler.mainmodule
1112 for mclassdef in mclassdefs.reversed do
1113 for mprop in mclassdef.intro_mproperties do
1114 var mpropdef = mprop.lookup_first_definition(mainmodule, intro.bound_mtype)
1115 if not mpropdef isa MMethodDef then continue
1116 var rt_name = mpropdef.rt_name
1117 v.add("this.vft.put(\"{mprop.full_name}\", {rt_name}.get{rt_name}());")
1118
1119 # fill super next definitions
1120 while mpropdef.has_supercall do
1121 var prefix = mpropdef.full_name
1122 mpropdef = mpropdef.lookup_next_definition(mainmodule, intro.bound_mtype)
1123 rt_name = mpropdef.rt_name
1124 v.add("this.vft.put(\"{prefix}\", {rt_name}.get{rt_name}());")
1125 end
1126 end
1127 end
1128 end
1129
1130 # Compile the type table for the MClass
1131 fun compile_type_table(v: JavaCompilerVisitor) do
1132 for pclass in in_hierarchy(v.compiler.mainmodule).greaters do
1133 if pclass == self then
1134 v.add("supers.put(\"{pclass.jname}\", this);")
1135 else
1136 v.add("supers.put(\"{pclass.jname}\", {pclass.rt_name}.get{pclass.rt_name}());")
1137 end
1138 end
1139 end
1140 end
1141
1142 # Used as a common type between MMethod and MMethodDef for `table_send`
1143 private interface TableCallable
1144 end
1145
1146 redef class MMethod
1147 super TableCallable
1148 end
1149
1150 redef class MMethodDef
1151 super TableCallable
1152
1153 # Runtime name
1154 private fun rt_name: String do
1155 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
1156 end
1157
1158 # Generate a Java RTMethod for `self`
1159 fun compile_to_java(v: JavaCompilerVisitor) do
1160 v.add("public class {rt_name} extends RTMethod \{")
1161 v.add(" protected static RTMethod instance;")
1162 v.add(" public static RTMethod get{rt_name}() \{")
1163 v.add(" if(instance == null) \{")
1164 v.add(" instance = new {rt_name}();")
1165 v.add(" \}")
1166 v.add(" return instance;")
1167 v.add(" \}")
1168 v.add(" @Override")
1169 v.add(" public RTVal exec(RTVal[] args) \{")
1170 compile_inside_to_java(v)
1171 v.add(" \}")
1172 v.add("\}")
1173 end
1174
1175 # Compile the body of this function
1176 fun compile_inside_to_java(v: JavaCompilerVisitor) do
1177
1178 var modelbuilder = v.compiler.modelbuilder
1179 var node = modelbuilder.mpropdef2node(self)
1180
1181 if is_abstract then
1182 v.add_abort("Abstract method `{mproperty.name}` called on `\" + {selfvar}.rtclass.class_name +\"`")
1183 v.add("return null;")
1184 return
1185 end
1186
1187 if node isa APropdef then
1188 node.compile_to_java(v, self)
1189 else if node isa AClassdef then
1190 # TODO compile attributes
1191 v.info("NOT YET IMPLEMENTED attribute handling")
1192 else
1193 abort
1194 end
1195
1196 end
1197 end
1198
1199 redef class APropdef
1200
1201 # Compile that property definition to java code
1202 fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1203 v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
1204 v.add("return null;")
1205 end
1206 end
1207
1208 redef class AMethPropdef
1209 redef fun compile_to_java(v, mpropdef) do
1210 # TODO Call the implicit super-init
1211
1212 var recv = mpropdef.mclassdef.bound_mtype
1213 var arguments = new Array[RuntimeVariable]
1214 var frame = new JavaStaticFrame(v, mpropdef, recv, arguments)
1215 v.frame = frame
1216
1217 var selfvar = v.decl_var("self", recv)
1218 arguments.add(selfvar)
1219 var boxed = v.new_expr("args[0];", v.compiler.mainmodule.object_type)
1220 v.add "{selfvar} = {v.unbox(boxed, recv)};"
1221
1222 var msignature = mpropdef.msignature
1223 var ret = null
1224 if msignature != null then
1225 ret = msignature.return_mtype
1226 if ret != null then frame.returnvar = v.new_var(ret)
1227 end
1228 frame.returnlabel = v.get_name("RET_LABEL")
1229
1230 if not mpropdef.is_intern and msignature != null then
1231 var i = 0
1232 for mparam in msignature.mparameters do
1233 var variable = n_signature.as(not null).n_params[i].variable
1234 if variable == null then continue
1235 var argvar = v.variable(variable)
1236 boxed = v.new_expr("args[{i + 1}];", v.compiler.mainmodule.object_type)
1237 v.add "{argvar} = {v.unbox(boxed, mparam.mtype)};"
1238 arguments.add(argvar)
1239 i += 1
1240 end
1241 end
1242
1243 v.add("{frame.returnlabel.as(not null)}: \{")
1244 compile_inside_to_java(v, mpropdef)
1245 v.add("\}")
1246
1247 if ret != null then
1248 if ret.is_java_primitive then
1249 boxed = v.box(frame.returnvar.as(not null), v.compiler.mainmodule.object_type)
1250 v.add("return {boxed};")
1251 else
1252 v.add("return {frame.returnvar.as(not null)};")
1253 end
1254 else
1255 v.add("return null;")
1256 end
1257 v.frame = null
1258 end
1259
1260 # Compile the inside of the method body
1261 private fun compile_inside_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1262 # Compile intern methods
1263 if mpropdef.is_intern then
1264 if compile_intern_to_java(v, mpropdef, arguments) then return
1265 v.info("NOT YET IMPLEMENTED compile_intern for {mpropdef}")
1266 v.ret(v.null_instance)
1267 return
1268 end
1269
1270 # Compile block if any
1271 var n_block = n_block
1272 if n_block != null then
1273 v.stmt(n_block)
1274 return
1275 end
1276 end
1277
1278 # Compile an intern method using Java primitives
1279 fun compile_intern_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do
1280 var pname = mpropdef.mproperty.name
1281 var cname = mpropdef.mclassdef.mclass.name
1282 var ret = mpropdef.msignature.as(not null).return_mtype
1283 if cname == "Int" then
1284 if pname == "output" then
1285 v.add("System.out.println({arguments[0]});")
1286 v.ret(v.null_instance)
1287 return true
1288 else if pname == "object_id" then
1289 v.ret(arguments.first)
1290 return true
1291 else if pname == "+" then
1292 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1293 return true
1294 else if pname == "-" then
1295 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1296 return true
1297 else if pname == "unary -" then
1298 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1299 return true
1300 else if pname == "unary +" then
1301 v.ret(arguments[0])
1302 return true
1303 else if pname == "*" then
1304 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1305 return true
1306 else if pname == "/" then
1307 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1308 return true
1309 else if pname == "%" then
1310 v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null)))
1311 return true
1312 else if pname == "lshift" then
1313 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1314 return true
1315 else if pname == "rshift" then
1316 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1317 return true
1318 else if pname == "==" then
1319 v.ret(v.equal_test(arguments[0], arguments[1]))
1320 return true
1321 else if pname == "!=" then
1322 var res = v.equal_test(arguments[0], arguments[1])
1323 v.ret(v.new_expr("!{res}", ret.as(not null)))
1324 return true
1325 else if pname == "<" then
1326 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1327 return true
1328 else if pname == ">" then
1329 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1330 return true
1331 else if pname == "<=" then
1332 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1333 return true
1334 else if pname == ">=" then
1335 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1336 return true
1337 else if pname == "to_f" then
1338 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1339 return true
1340 else if pname == "to_b" then
1341 v.ret(v.new_expr("(byte){arguments[0]}", ret.as(not null)))
1342 return true
1343 else if pname == "ascii" then
1344 v.ret(v.new_expr("(char){arguments[0]}", ret.as(not null)))
1345 return true
1346 end
1347 else if cname == "Char" then
1348 if pname == "output" then
1349 v.add("System.out.print({arguments[0]});")
1350 v.ret(v.null_instance)
1351 return true
1352 else if pname == "object_id" then
1353 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1354 return true
1355 else if pname == "successor" then
1356 v.ret(v.new_expr("(char)({arguments[0]} + {arguments[1]})", ret.as(not null)))
1357 return true
1358 else if pname == "predecessor" then
1359 v.ret(v.new_expr("(char)({arguments[0]} - {arguments[1]})", ret.as(not null)))
1360 return true
1361 else if pname == "==" then
1362 v.ret(v.equal_test(arguments[0], arguments[1]))
1363 return true
1364 else if pname == "!=" then
1365 var res = v.equal_test(arguments[0], arguments[1])
1366 v.ret(v.new_expr("!{res}", ret.as(not null)))
1367 return true
1368 else if pname == "<" then
1369 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1370 return true
1371 else if pname == ">" then
1372 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1373 return true
1374 else if pname == "<=" then
1375 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1376 return true
1377 else if pname == ">=" then
1378 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1379 return true
1380 else if pname == "to_i" then
1381 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1382 return true
1383 else if pname == "ascii" then
1384 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1385 return true
1386 end
1387 else if cname == "Byte" then
1388 if pname == "output" then
1389 v.add("System.out.println({arguments[0]});")
1390 v.ret(v.null_instance)
1391 return true
1392 else if pname == "object_id" then
1393 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1394 return true
1395 else if pname == "+" then
1396 v.ret(v.new_expr("(byte)({arguments[0]} + {arguments[1]})", ret.as(not null)))
1397 return true
1398 else if pname == "-" then
1399 v.ret(v.new_expr("(byte)({arguments[0]} - {arguments[1]})", ret.as(not null)))
1400 return true
1401 else if pname == "unary -" then
1402 v.ret(v.new_expr("(byte)(-{arguments[0]})", ret.as(not null)))
1403 return true
1404 else if pname == "unary +" then
1405 v.ret(arguments[0])
1406 return true
1407 else if pname == "*" then
1408 v.ret(v.new_expr("(byte)({arguments[0]} * {arguments[1]})", ret.as(not null)))
1409 return true
1410 else if pname == "/" then
1411 v.ret(v.new_expr("(byte)({arguments[0]} / {arguments[1]})", ret.as(not null)))
1412 return true
1413 else if pname == "%" then
1414 v.ret(v.new_expr("(byte)({arguments[0]} % {arguments[1]})", ret.as(not null)))
1415 return true
1416 else if pname == "lshift" then
1417 v.ret(v.new_expr("(byte)({arguments[0]} << {arguments[1]})", ret.as(not null)))
1418 return true
1419 else if pname == "rshift" then
1420 v.ret(v.new_expr("(byte)({arguments[0]} >> {arguments[1]})", ret.as(not null)))
1421 return true
1422 else if pname == "==" then
1423 v.ret(v.equal_test(arguments[0], arguments[1]))
1424 return true
1425 else if pname == "!=" then
1426 var res = v.equal_test(arguments[0], arguments[1])
1427 v.ret(v.new_expr("!{res}", 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 == ">" then
1433 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1434 return true
1435 else if pname == "<=" then
1436 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1437 return true
1438 else if pname == ">=" then
1439 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1440 return true
1441 else if pname == "to_i" then
1442 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1443 return true
1444 else if pname == "to_f" then
1445 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1446 return true
1447 else if pname == "ascii" then
1448 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1449 return true
1450 end
1451 else if cname == "Bool" then
1452 if pname == "output" then
1453 v.add("System.out.println({arguments[0]});")
1454 v.ret(v.null_instance)
1455 return true
1456 else if pname == "object_id" then
1457 v.ret(v.new_expr("{arguments[0]}?1:0", ret.as(not null)))
1458 return true
1459 else if pname == "==" then
1460 v.ret(v.equal_test(arguments[0], arguments[1]))
1461 return true
1462 else if pname == "!=" then
1463 var res = v.equal_test(arguments[0], arguments[1])
1464 v.ret(v.new_expr("!{res}", ret.as(not null)))
1465 return true
1466 end
1467 else if cname == "Float" then
1468 if pname == "output" then
1469 v.add "if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
1470 v.add "System.out.println(\"inf\");"
1471 v.add "\} else if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
1472 v.add "System.out.println(\"-inf\");"
1473 v.add "\} else \{"
1474 var df = v.get_name("df")
1475 v.add "java.text.DecimalFormat {df} = new java.text.DecimalFormat(\"0.000000\");"
1476 v.add "System.out.println({df}.format({arguments[0]}));"
1477 v.add "\}"
1478 v.ret(v.null_instance)
1479 return true
1480 else if pname == "object_id" then
1481 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1482 return true
1483 else if pname == "+" then
1484 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1485 return true
1486 else if pname == "-" then
1487 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1488 return true
1489 else if pname == "unary -" then
1490 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1491 return true
1492 else if pname == "unary +" then
1493 v.ret(arguments[0])
1494 return true
1495 else if pname == "succ" then
1496 v.ret(v.new_expr("{arguments[0]} + 1", ret.as(not null)))
1497 return true
1498 else if pname == "prec" then
1499 v.ret(v.new_expr("{arguments[0]} - 1", ret.as(not null)))
1500 return true
1501 else if pname == "*" then
1502 v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
1503 return true
1504 else if pname == "/" then
1505 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1506 return true
1507 else if pname == "==" then
1508 v.ret(v.equal_test(arguments[0], arguments[1]))
1509 return true
1510 else if pname == "!=" then
1511 var res = v.equal_test(arguments[0], arguments[1])
1512 v.ret(v.new_expr("!{res}", ret.as(not null)))
1513 return true
1514 else if pname == "<" then
1515 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1516 return true
1517 else if pname == ">" then
1518 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1519 return true
1520 else if pname == "<=" then
1521 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1522 return true
1523 else if pname == ">=" then
1524 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1525 return true
1526 else if pname == "to_i" then
1527 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1528 return true
1529 else if pname == "to_b" then
1530 v.ret(v.new_expr("(byte){arguments[0]}", ret.as(not null)))
1531 return true
1532 end
1533 end
1534 if pname == "exit" then
1535 v.add("System.exit({arguments[1]});")
1536 v.ret(v.null_instance)
1537 return true
1538 else if pname == "sys" then
1539 # TODO singleton
1540 var main_type = v.compiler.mainmodule.sys_type.as(not null)
1541 var sys = main_type.mclass
1542 v.ret(v.new_expr("new RTVal({sys.rt_name}.get{sys.rt_name}())", main_type))
1543 return true
1544 else if pname == "object_id" then
1545 v.ret(v.new_expr("{arguments[0]}.hashCode()", ret.as(not null)))
1546 return true
1547 else if pname == "is_same_type" then
1548 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1549 return true
1550 else if pname == "is_same_instance" then
1551 v.ret(v.equal_test(arguments[0], arguments[1]))
1552 return true
1553 else if pname == "output_class_name" then
1554 v.add("System.out.println({arguments[0]}.rtclass.class_name);")
1555 v.ret(v.null_instance)
1556 return true
1557 end
1558 return false
1559 end
1560 end
1561
1562 redef class AExpr
1563 # Try to compile self as an expression
1564 # Do not call this method directly, use `v.expr` instead
1565 private fun expr(v: JavaCompilerVisitor): nullable RuntimeVariable do
1566 v.info("NOT YET IMPLEMENTED {class_name}::expr")
1567 return null
1568 end
1569
1570 # Try to compile self as a statement
1571 # Do not call this method directly, use `v.stmt` instead
1572 private fun stmt(v: JavaCompilerVisitor) do expr(v)
1573 end
1574
1575 redef class ABlockExpr
1576 redef fun stmt(v)
1577 do
1578 for e in self.n_expr do v.stmt(e)
1579 end
1580 redef fun expr(v)
1581 do
1582 var last = self.n_expr.last
1583 for e in self.n_expr do
1584 if e == last then break
1585 v.stmt(e)
1586 end
1587 return v.expr(last, null)
1588 end
1589 end
1590
1591 redef class ASendExpr
1592 redef fun expr(v) do
1593 var recv = v.expr(n_expr, null)
1594 var callsite = callsite.as(not null)
1595 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, raw_arguments)
1596 return v.compile_callsite(callsite, args)
1597 end
1598 end
1599
1600 redef class ANewExpr
1601 redef fun expr(v)
1602 do
1603 var mtype = self.recvtype
1604 assert mtype != null
1605
1606 if mtype.mclass.name == "NativeArray" then
1607 # TODO handle native arrays
1608 v.info("NOT YET IMPLEMENTED new NativeArray")
1609 end
1610
1611 var recv = v.init_instance(mtype)
1612
1613 var callsite = self.callsite
1614 if callsite == null then return recv
1615
1616 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
1617 var res2 = v.compile_callsite(callsite, args)
1618 if res2 != null then
1619 return res2
1620 end
1621 return recv
1622 end
1623 end
1624
1625 redef class ASelfExpr
1626 redef fun expr(v) do return v.frame.as(not null).arguments.first
1627 end
1628
1629 redef class AImplicitSelfExpr
1630 redef fun expr(v) do return v.frame.as(not null).arguments.first
1631 end
1632
1633 redef class AReturnExpr
1634 redef fun stmt(v) do
1635 var nexpr = self.n_expr
1636 var frame = v.frame
1637 assert frame != null
1638 if nexpr != null then
1639 v.ret(v.expr(nexpr, frame.returnvar.as(not null).mtype))
1640 else
1641 v.ret(v.null_instance)
1642 end
1643 end
1644 end
1645
1646 redef class AVardeclExpr
1647 redef fun stmt(v) do
1648 var variable = self.variable.as(not null)
1649 var ne = self.n_expr
1650 var decl = v.variable(variable)
1651 if ne != null then
1652 var i = v.expr(ne, variable.declared_type)
1653 v.assign(decl, i)
1654 end
1655 end
1656 end
1657
1658 redef class AVarExpr
1659 redef fun expr(v) do
1660 return v.variable(self.variable.as(not null))
1661 end
1662 end
1663
1664 redef class AVarAssignExpr
1665 redef fun expr(v) do
1666 var variable = self.variable.as(not null)
1667 var i = v.expr(self.n_value, variable.declared_type)
1668 v.assign(v.variable(variable), i)
1669 return i
1670 end
1671 end
1672
1673 redef class AIntExpr
1674 redef fun expr(v) do return v.int_instance(self.value.as(not null))
1675 end
1676
1677 redef class AByteExpr
1678 redef fun expr(v) do return v.byte_instance(self.value.as(not null))
1679 end
1680
1681 redef class AFloatExpr
1682 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
1683 end
1684
1685 redef class ACharExpr
1686 redef fun expr(v) do return v.char_instance(self.value.as(not null))
1687 end
1688
1689 redef class ATrueExpr
1690 redef fun expr(v) do return v.bool_instance(true)
1691 end
1692
1693 redef class AFalseExpr
1694 redef fun expr(v) do return v.bool_instance(false)
1695 end
1696
1697 redef class ANullExpr
1698 redef fun expr(v) do return v.null_instance
1699 end
1700
1701 redef class AAbortExpr
1702 redef fun stmt(v) do v.add_abort("Aborted")
1703 end
1704
1705 redef class ADebugTypeExpr
1706 redef fun stmt(v) do end # do nothing
1707 end