nitj: implement instances initialization
[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 rt_name = mtype.mclass.rt_name
762 var res = new_expr("new RTVal({rt_name}.get{rt_name}())", mtype)
763 generate_init_attr(self, res, mtype)
764 return res
765 end
766
767 # Generate code that initialize the attributes on a new instance
768 fun generate_init_attr(v: JavaCompilerVisitor, recv: RuntimeVariable, mtype: MClassType) do
769 var cds = mtype.collect_mclassdefs(v.compiler.mainmodule).to_a
770 v.compiler.mainmodule.linearize_mclassdefs(cds)
771 for cd in cds do
772 for npropdef in v.compiler.modelbuilder.collect_attr_propdef(cd) do
773 npropdef.init_expr(v, recv)
774 end
775 end
776 end
777
778 # Generate a Nit "is" for two runtime_variables
779 fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable do
780 var res = new_var(compiler.mainmodule.bool_type)
781 if value2.mtype.is_java_primitive and not value1.mtype.is_java_primitive then
782 var tmp = value1
783 value1 = value2
784 value2 = tmp
785 end
786 if value1.mtype.is_java_primitive then
787 if value2.mtype == value1.mtype then
788 add("{res} = {value1} == {value2}; /* == with two primitives */")
789 else if value2.mtype.is_java_primitive then
790 add("{res} = true; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
791 # else if value1.mtype.is_tagged then
792 # add("{res} = ({value2} != NULL) && ({autobox(value2, value1.mtype)} == {value1});")
793 else
794 var rt_name = value1.mtype.as(MClassType).mclass.rt_name
795 add("{res} = ({value2} != null) && ({value2}.rtclass == {rt_name}.get{rt_name}());")
796 add("if ({res}) \{")
797 add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
798 add("\}")
799 end
800 return res
801 end
802 var maybe_null = true
803 var test = new Array[String]
804 var t1 = value1.mcasttype
805 if t1 isa MNullableType then
806 test.add("{value1} != null && !{value1}.is_null()")
807 t1 = t1.mtype
808 else
809 maybe_null = false
810 end
811 var t2 = value2.mcasttype
812 if t2 isa MNullableType then
813 test.add("{value2} != null && !{value2}.is_null()")
814 t2 = t2.mtype
815 else
816 maybe_null = false
817 end
818
819 var incompatible = false
820 var primitive
821 if t1.is_java_primitive then
822 primitive = t1
823 if t1 == t2 then
824 # No need to compare class
825 else if t2.is_java_primitive then
826 incompatible = true
827 else if can_be_primitive(value2) then
828 if t1.is_java_primitive then
829 self.add("{res} = {value1} == {value2}; /* t1 is primitive and t2 can be */")
830 return res
831 end
832 # if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
833 # test.add("(!{extract_tag(value2)})")
834 # end
835 test.add("{value1}.rtclass == {value2}.rtclass")
836 else
837 incompatible = true
838 end
839 else if t2.is_java_primitive then
840 primitive = t2
841 if can_be_primitive(value1) then
842 if t2.is_java_primitive then
843 self.add("{res} = {value1} == {value2}; /* t2 is primitive and t1 can be */")
844 return res
845 end
846 test.add("{value1}.rtclass == {value2}.rtclass")
847 else
848 incompatible = true
849 end
850 else
851 primitive = null
852 end
853
854 if incompatible then
855 if maybe_null then
856 self.add("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
857 return res
858 else
859 self.add("{res} = false; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
860 return res
861 end
862 end
863 if primitive != null then
864 if primitive.is_java_primitive then
865 self.add("{res} = {value1} == {value2};")
866 return res
867 end
868 test.add("({value1}.value == {value2}.value")
869 else if can_be_primitive(value1) and can_be_primitive(value2) then
870 test.add("{value1}.rtclass == {value2}.rtclass")
871 var s = new Array[String]
872 for b in compiler.box_kinds do
873 var rt_name = b.mclass.rt_name
874 s.add "({value1}.rtclass == {rt_name}.get{rt_name}()) && ({value1}.value.equals({value2}.value))"
875 if b.mclass.name == "Float" then
876 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)"
877 end
878 end
879 if s.is_empty then
880 self.add("{res} = {value1} == {value2}; /* both can be primitive */")
881 return res
882 end
883 test.add("({s.join(" || ")})")
884 else
885 self.add("{res} = {value1} == {value2}; /* no primitives */")
886 return res
887 end
888 self.add("{res} = {value1} == {value2} || ({test.join(" && ")});")
889 return res
890 end
891
892 # Attributes
893
894 # Generate a polymorphic attribute read
895 fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable do
896 # TODO check_recv_notnull(recv)
897 # TODO compile_check(v)
898 # What is the declared type of the attribute?
899 var ret = a.intro.static_mtype.as(not null)
900 var intromclassdef = a.intro.mclassdef
901 ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
902
903 return new_expr("{recv}.attrs.get(\"{a.jname}\")", ret)
904 end
905
906 # Generate a polymorphic attribute write
907 fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) do
908 # TODO check_recv_notnull(recv)
909 add "{recv}.attrs.put(\"{a.jname}\", {autobox(value, compiler.mainmodule.object_type)});"
910 end
911
912 # Utils
913
914 # Display a info message
915 fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
916 end
917
918 # A file containing Java code.
919 class JavaCodeFile
920
921 # File name
922 var filename: String
923
924 # Lines to write
925 var lines: List[String] = new List[String]
926 end
927
928 redef class MEntity
929 # A Java compatible name for `self`
930 private fun jname: String do return name.to_cmangle
931 end
932
933 # Handler for runtime classes generation
934 #
935 # We need 3 kinds of runtime structures:
936 # * `RTClass` to represent a global class
937 # * `RTMethod` to represent a method definition
938 # * `RTVal` to represent runtime variables
939 class JavaRuntimeModel
940
941 # Compile JavaRuntimeModel structures
942 fun compile_rtmodel(compiler: JavaCompiler) do
943 compile_rtclass(compiler)
944 compile_rtmethod(compiler)
945 compile_rtval(compiler)
946 end
947
948 # Compile the abstract runtime class structure
949 #
950 # Runtime classes have 3 attributes:
951 # * `class_name`: the class name as a String
952 # * `vft`: the virtual function table for the class (flattened)
953 # * `supers`: the super type table (used for type tests)
954 fun compile_rtclass(compiler: JavaCompiler) do
955 var v = compiler.new_visitor("RTClass.java")
956 v.add("import java.util.HashMap;")
957 v.add("public abstract class RTClass \{")
958 v.add(" public String class_name;")
959 v.add(" public HashMap<String, RTMethod> vft = new HashMap<>();")
960 v.add(" public HashMap<String, RTClass> supers = new HashMap<>();")
961 v.add(" protected RTClass() \{\}")
962 v.add("\}")
963 end
964
965 # Compile the abstract runtime method structure
966 #
967 # Method body is executed through the `exec` method:
968 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
969 # * `exec` always returns a RTVal (or null if the Nit return type is void)
970 fun compile_rtmethod(compiler: JavaCompiler) do
971 var v = compiler.new_visitor("RTMethod.java")
972 v.add("public abstract class RTMethod \{")
973 v.add(" protected RTMethod() \{\}")
974 v.add(" public abstract RTVal exec(RTVal[] args);")
975 v.add("\}")
976 end
977
978 # Compile the runtime value structure
979 #
980 # RTVal both represents object instances and primitives values:
981 # * object instances:
982 # * `rtclass` the class of the RTVal is instance of
983 # * `attrs` contains the attributes of the instance
984 # * primitive values:
985 # * `rtclass` represents the class of the primitive value Nit type
986 # * `value` contains the primitive value of the instance
987 # * null values:
988 # * they must have both `rtclass` and `value` as null
989 fun compile_rtval(compiler: JavaCompiler) do
990 var v = compiler.new_visitor("RTVal.java")
991 v.add("import java.util.HashMap;")
992 v.add("public class RTVal \{")
993 v.add(" public RTClass rtclass;")
994 v.add(" public HashMap<String, RTVal> attrs = new HashMap<>();")
995 v.add(" Object value;")
996 v.add(" public RTVal(RTClass rtclass) \{")
997 v.add(" this.rtclass = rtclass;")
998 v.add(" \}")
999 v.add(" public RTVal(RTClass rtclass, Object value) \{")
1000 v.add(" this.rtclass = rtclass;")
1001 v.add(" this.value = value;")
1002 v.add(" \}")
1003 v.add(" public boolean is_null() \{ return rtclass == null && value == null; \}")
1004 v.add("\}")
1005 end
1006 end
1007
1008 # A runtime variable hold a runtime value in Java.
1009 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1010 class RuntimeVariable
1011
1012 # The name of the variable in the Java code
1013 var name: String
1014
1015 # The static type of the variable (as declard in Java)
1016 var mtype: MType
1017
1018 # The current casted type of the variable (as known in Nit)
1019 var mcasttype: MType is writable
1020
1021 # If the variable exaclty a mcasttype?
1022 # false (usual value) means that the variable is a mcasttype or a subtype.
1023 var is_exact: Bool = false is writable
1024
1025 # Is this variable declared as a RTVal or a Java primitive one?
1026 var is_boxed = false
1027
1028 redef fun to_s do return name
1029
1030 redef fun inspect
1031 do
1032 var exact_str
1033 if self.is_exact then
1034 exact_str = " exact"
1035 else
1036 exact_str = ""
1037 end
1038 var type_str
1039 if self.mtype == self.mcasttype then
1040 type_str = "{mtype}{exact_str}"
1041 else
1042 type_str = "{mtype}({mcasttype}{exact_str})"
1043 end
1044 return "<{name}:{type_str}>"
1045 end
1046 end
1047
1048 # The static context of a visited property in a `JavaCompilerVisitor`
1049 class JavaStaticFrame
1050 # The associated visitor
1051 var visitor: JavaCompilerVisitor
1052
1053 # The executed property.
1054 # A Method in case of a call, an attribute in case of a default initialization.
1055 var mpropdef: MPropDef
1056
1057 # The static type of the receiver
1058 var receiver: MClassType
1059
1060 # Arguments of the method (the first is the receiver)
1061 var arguments: Array[RuntimeVariable]
1062
1063 # The runtime_variable associated to the return (in a function)
1064 var returnvar: nullable RuntimeVariable = null is writable
1065
1066 # The label at the end of the property
1067 var returnlabel: nullable String = null is writable
1068 end
1069
1070 redef class Location
1071 # Return a shortened version of the location with `"{file}:{line_start}"`
1072 fun short_location: String do
1073 var file = self.file
1074 if file == null then return "<no file>:{line_start}"
1075 return "{file.filename.escape_to_c}:{line_start}"
1076 end
1077 end
1078
1079 redef class MType
1080 # Return the Java type associated to a given Nit static type
1081 fun java_type: String do return "RTVal"
1082
1083 # Is the associated Java type a primitive one?
1084 #
1085 # ENSURE `result == (java_type != "Object")`
1086 var is_java_primitive: Bool is lazy do return java_type != "RTVal"
1087 end
1088
1089 redef class MClassType
1090
1091 redef var java_type is lazy do
1092 if mclass.name == "Int" then
1093 return "int"
1094 else if mclass.name == "Bool" then
1095 return "boolean"
1096 else if mclass.name == "Char" then
1097 return "char"
1098 else if mclass.name == "Float" then
1099 return "double"
1100 else if mclass.name == "Byte" then
1101 return "byte"
1102 else if mclass.name == "NativeString" then
1103 return "String"
1104 else if mclass.name == "NativeArray" then
1105 return "Array"
1106 end
1107 return "RTVal"
1108 end
1109 end
1110
1111 redef class MClass
1112
1113 # Runtime name
1114 private fun rt_name: String do return "RTClass_{intro.mmodule.jname}_{jname}"
1115
1116 # Generate a Java RTClass for a Nit MClass
1117 fun compile_to_java(v: JavaCompilerVisitor) do
1118 v.add("public class {rt_name} extends RTClass \{")
1119 v.add(" protected static RTClass instance;")
1120 v.add(" private {rt_name}() \{")
1121 v.add(" this.class_name = \"{name}\";")
1122 compile_vft(v)
1123 compile_type_table(v)
1124 v.add(" \}")
1125 v.add(" public static RTClass get{rt_name}() \{")
1126 v.add(" if(instance == null) \{")
1127 v.add(" instance = new {rt_name}();")
1128 v.add(" \}")
1129 v.add(" return instance;")
1130 v.add(" \}")
1131 v.add("\}")
1132 end
1133
1134 # Compile the virtual function table for the mclass
1135 private fun compile_vft(v: JavaCompilerVisitor) do
1136 # TODO handle generics
1137 if mclass_type.need_anchor then return
1138 var mclassdefs = mclass_type.collect_mclassdefs(v.compiler.mainmodule).to_a
1139 v.compiler.mainmodule.linearize_mclassdefs(mclassdefs)
1140
1141 var mainmodule = v.compiler.mainmodule
1142 for mclassdef in mclassdefs.reversed do
1143 for mprop in mclassdef.intro_mproperties do
1144 var mpropdef = mprop.lookup_first_definition(mainmodule, intro.bound_mtype)
1145 if not mpropdef isa MMethodDef then continue
1146 var rt_name = mpropdef.rt_name
1147 v.add("this.vft.put(\"{mprop.full_name}\", {rt_name}.get{rt_name}());")
1148
1149 # fill super next definitions
1150 while mpropdef.has_supercall do
1151 var prefix = mpropdef.full_name
1152 mpropdef = mpropdef.lookup_next_definition(mainmodule, intro.bound_mtype)
1153 rt_name = mpropdef.rt_name
1154 v.add("this.vft.put(\"{prefix}\", {rt_name}.get{rt_name}());")
1155 end
1156 end
1157 end
1158 end
1159
1160 # Compile the type table for the MClass
1161 fun compile_type_table(v: JavaCompilerVisitor) do
1162 for pclass in in_hierarchy(v.compiler.mainmodule).greaters do
1163 if pclass == self then
1164 v.add("supers.put(\"{pclass.jname}\", this);")
1165 else
1166 v.add("supers.put(\"{pclass.jname}\", {pclass.rt_name}.get{pclass.rt_name}());")
1167 end
1168 end
1169 end
1170 end
1171
1172 # Used as a common type between MMethod and MMethodDef for `table_send`
1173 private interface TableCallable
1174 end
1175
1176 redef class MMethod
1177 super TableCallable
1178 end
1179
1180 redef class MMethodDef
1181 super TableCallable
1182
1183 # Runtime name
1184 private fun rt_name: String do
1185 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
1186 end
1187
1188 # Generate a Java RTMethod for `self`
1189 fun compile_to_java(v: JavaCompilerVisitor) do
1190 v.add("public class {rt_name} extends RTMethod \{")
1191 v.add(" protected static RTMethod instance;")
1192 v.add(" public static RTMethod get{rt_name}() \{")
1193 v.add(" if(instance == null) \{")
1194 v.add(" instance = new {rt_name}();")
1195 v.add(" \}")
1196 v.add(" return instance;")
1197 v.add(" \}")
1198 v.add(" @Override")
1199 v.add(" public RTVal exec(RTVal[] args) \{")
1200 compile_inside_to_java(v)
1201 v.add(" \}")
1202 v.add("\}")
1203 end
1204
1205 # Compile the body of this function
1206 fun compile_inside_to_java(v: JavaCompilerVisitor) do
1207
1208 var modelbuilder = v.compiler.modelbuilder
1209 var node = modelbuilder.mpropdef2node(self)
1210
1211 if is_abstract then
1212 v.add_abort("Abstract method `{mproperty.name}` called on `\" + {selfvar}.rtclass.class_name +\"`")
1213 v.add("return null;")
1214 return
1215 end
1216
1217 if node isa APropdef then
1218 node.compile_to_java(v, self)
1219 else if node isa AClassdef then
1220 node.compile_to_java(v, self)
1221 else
1222 abort
1223 end
1224 end
1225 end
1226
1227 redef class AClassdef
1228 private fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1229 if mpropdef == self.mfree_init then
1230 assert mpropdef.mproperty.is_root_init
1231 if not mpropdef.is_intro then
1232 # TODO v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
1233 end
1234 else
1235 abort
1236 end
1237 v.add("return null;")
1238 end
1239 end
1240
1241 redef class APropdef
1242
1243 # Compile that property definition to java code
1244 fun compile_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1245 v.info("NOT YET IMPLEMENTED {class_name}::compile_to_java")
1246 v.add("return null;")
1247 end
1248 end
1249
1250 redef class AMethPropdef
1251 redef fun compile_to_java(v, mpropdef) do
1252 # TODO Call the implicit super-init
1253
1254 var recv = mpropdef.mclassdef.bound_mtype
1255 var arguments = new Array[RuntimeVariable]
1256 var frame = new JavaStaticFrame(v, mpropdef, recv, arguments)
1257 v.frame = frame
1258
1259 var selfvar = v.decl_var("self", recv)
1260 arguments.add(selfvar)
1261 var boxed = v.new_expr("args[0];", v.compiler.mainmodule.object_type)
1262 v.add "{selfvar} = {v.unbox(boxed, recv)};"
1263
1264 var msignature = mpropdef.msignature
1265 var ret = null
1266 if msignature != null then
1267 ret = msignature.return_mtype
1268 if ret != null then frame.returnvar = v.new_var(ret)
1269 end
1270 frame.returnlabel = v.get_name("RET_LABEL")
1271
1272 if not mpropdef.is_intern and msignature != null then
1273 var i = 0
1274 for mparam in msignature.mparameters do
1275 var variable = n_signature.as(not null).n_params[i].variable
1276 if variable == null then continue
1277 var argvar = v.variable(variable)
1278 boxed = v.new_expr("args[{i + 1}];", v.compiler.mainmodule.object_type)
1279 v.add "{argvar} = {v.unbox(boxed, mparam.mtype)};"
1280 arguments.add(argvar)
1281 i += 1
1282 end
1283 end
1284
1285 v.add("{frame.returnlabel.as(not null)}: \{")
1286 compile_inside_to_java(v, mpropdef)
1287 v.add("\}")
1288
1289 if ret != null then
1290 if ret.is_java_primitive then
1291 boxed = v.box(frame.returnvar.as(not null), v.compiler.mainmodule.object_type)
1292 v.add("return {boxed};")
1293 else
1294 v.add("return {frame.returnvar.as(not null)};")
1295 end
1296 else
1297 v.add("return null;")
1298 end
1299 v.frame = null
1300 end
1301
1302 # Compile the inside of the method body
1303 private fun compile_inside_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef) do
1304 # Compile intern methods
1305 if mpropdef.is_intern then
1306 if compile_intern_to_java(v, mpropdef, arguments) then return
1307 v.info("NOT YET IMPLEMENTED compile_intern for {mpropdef}")
1308 v.ret(v.null_instance)
1309 return
1310 end
1311
1312 # Compile block if any
1313 var n_block = n_block
1314 if n_block != null then
1315 v.stmt(n_block)
1316 return
1317 end
1318 end
1319
1320 # Compile an intern method using Java primitives
1321 fun compile_intern_to_java(v: JavaCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do
1322 var pname = mpropdef.mproperty.name
1323 var cname = mpropdef.mclassdef.mclass.name
1324 var ret = mpropdef.msignature.as(not null).return_mtype
1325 if cname == "Int" then
1326 if pname == "output" then
1327 v.add("System.out.println({arguments[0]});")
1328 v.ret(v.null_instance)
1329 return true
1330 else if pname == "object_id" then
1331 v.ret(arguments.first)
1332 return true
1333 else if pname == "+" then
1334 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1335 return true
1336 else if pname == "-" then
1337 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1338 return true
1339 else if pname == "unary -" then
1340 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1341 return true
1342 else if pname == "unary +" then
1343 v.ret(arguments[0])
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 == "lshift" then
1355 v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null)))
1356 return true
1357 else if pname == "rshift" then
1358 v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null)))
1359 return true
1360 else if pname == "==" then
1361 v.ret(v.equal_test(arguments[0], arguments[1]))
1362 return true
1363 else if pname == "!=" then
1364 var res = v.equal_test(arguments[0], arguments[1])
1365 v.ret(v.new_expr("!{res}", ret.as(not null)))
1366 return true
1367 else if pname == "<" then
1368 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1369 return true
1370 else if pname == ">" then
1371 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1372 return true
1373 else if pname == "<=" then
1374 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1375 return true
1376 else if pname == ">=" then
1377 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1378 return true
1379 else if pname == "to_f" then
1380 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1381 return true
1382 else if pname == "to_b" then
1383 v.ret(v.new_expr("(byte){arguments[0]}", ret.as(not null)))
1384 return true
1385 else if pname == "ascii" then
1386 v.ret(v.new_expr("(char){arguments[0]}", ret.as(not null)))
1387 return true
1388 end
1389 else if cname == "Char" then
1390 if pname == "output" then
1391 v.add("System.out.print({arguments[0]});")
1392 v.ret(v.null_instance)
1393 return true
1394 else if pname == "object_id" then
1395 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1396 return true
1397 else if pname == "successor" then
1398 v.ret(v.new_expr("(char)({arguments[0]} + {arguments[1]})", ret.as(not null)))
1399 return true
1400 else if pname == "predecessor" then
1401 v.ret(v.new_expr("(char)({arguments[0]} - {arguments[1]})", ret.as(not null)))
1402 return true
1403 else if pname == "==" then
1404 v.ret(v.equal_test(arguments[0], arguments[1]))
1405 return true
1406 else if pname == "!=" then
1407 var res = v.equal_test(arguments[0], arguments[1])
1408 v.ret(v.new_expr("!{res}", ret.as(not null)))
1409 return true
1410 else if pname == "<" then
1411 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1412 return true
1413 else if pname == ">" then
1414 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1415 return true
1416 else if pname == "<=" then
1417 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1418 return true
1419 else if pname == ">=" then
1420 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1421 return true
1422 else if pname == "to_i" then
1423 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1424 return true
1425 else if pname == "ascii" then
1426 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1427 return true
1428 end
1429 else if cname == "Byte" then
1430 if pname == "output" then
1431 v.add("System.out.println({arguments[0]});")
1432 v.ret(v.null_instance)
1433 return true
1434 else if pname == "object_id" then
1435 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1436 return true
1437 else if pname == "+" then
1438 v.ret(v.new_expr("(byte)({arguments[0]} + {arguments[1]})", ret.as(not null)))
1439 return true
1440 else if pname == "-" then
1441 v.ret(v.new_expr("(byte)({arguments[0]} - {arguments[1]})", ret.as(not null)))
1442 return true
1443 else if pname == "unary -" then
1444 v.ret(v.new_expr("(byte)(-{arguments[0]})", ret.as(not null)))
1445 return true
1446 else if pname == "unary +" then
1447 v.ret(arguments[0])
1448 return true
1449 else if pname == "*" then
1450 v.ret(v.new_expr("(byte)({arguments[0]} * {arguments[1]})", ret.as(not null)))
1451 return true
1452 else if pname == "/" then
1453 v.ret(v.new_expr("(byte)({arguments[0]} / {arguments[1]})", ret.as(not null)))
1454 return true
1455 else if pname == "%" then
1456 v.ret(v.new_expr("(byte)({arguments[0]} % {arguments[1]})", ret.as(not null)))
1457 return true
1458 else if pname == "lshift" then
1459 v.ret(v.new_expr("(byte)({arguments[0]} << {arguments[1]})", ret.as(not null)))
1460 return true
1461 else if pname == "rshift" then
1462 v.ret(v.new_expr("(byte)({arguments[0]} >> {arguments[1]})", ret.as(not null)))
1463 return true
1464 else if pname == "==" then
1465 v.ret(v.equal_test(arguments[0], arguments[1]))
1466 return true
1467 else if pname == "!=" then
1468 var res = v.equal_test(arguments[0], arguments[1])
1469 v.ret(v.new_expr("!{res}", ret.as(not null)))
1470 return true
1471 else if pname == "<" then
1472 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1473 return true
1474 else if pname == ">" then
1475 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1476 return true
1477 else if pname == "<=" then
1478 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1479 return true
1480 else if pname == ">=" then
1481 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1482 return true
1483 else if pname == "to_i" then
1484 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1485 return true
1486 else if pname == "to_f" then
1487 v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null)))
1488 return true
1489 else if pname == "ascii" then
1490 v.ret(v.new_expr("{arguments[0]}", ret.as(not null)))
1491 return true
1492 end
1493 else if cname == "Bool" then
1494 if pname == "output" then
1495 v.add("System.out.println({arguments[0]});")
1496 v.ret(v.null_instance)
1497 return true
1498 else if pname == "object_id" then
1499 v.ret(v.new_expr("{arguments[0]}?1:0", ret.as(not null)))
1500 return true
1501 else if pname == "==" then
1502 v.ret(v.equal_test(arguments[0], arguments[1]))
1503 return true
1504 else if pname == "!=" then
1505 var res = v.equal_test(arguments[0], arguments[1])
1506 v.ret(v.new_expr("!{res}", ret.as(not null)))
1507 return true
1508 end
1509 else if cname == "Float" then
1510 if pname == "output" then
1511 v.add "if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
1512 v.add "System.out.println(\"inf\");"
1513 v.add "\} else if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
1514 v.add "System.out.println(\"-inf\");"
1515 v.add "\} else \{"
1516 var df = v.get_name("df")
1517 v.add "java.text.DecimalFormat {df} = new java.text.DecimalFormat(\"0.000000\");"
1518 v.add "System.out.println({df}.format({arguments[0]}));"
1519 v.add "\}"
1520 v.ret(v.null_instance)
1521 return true
1522 else if pname == "object_id" then
1523 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1524 return true
1525 else if pname == "+" then
1526 v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
1527 return true
1528 else if pname == "-" then
1529 v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
1530 return true
1531 else if pname == "unary -" then
1532 v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
1533 return true
1534 else if pname == "unary +" then
1535 v.ret(arguments[0])
1536 return true
1537 else if pname == "succ" then
1538 v.ret(v.new_expr("{arguments[0]} + 1", ret.as(not null)))
1539 return true
1540 else if pname == "prec" then
1541 v.ret(v.new_expr("{arguments[0]} - 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 == "/" then
1547 v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null)))
1548 return true
1549 else if pname == "==" then
1550 v.ret(v.equal_test(arguments[0], arguments[1]))
1551 return true
1552 else if pname == "!=" then
1553 var res = v.equal_test(arguments[0], arguments[1])
1554 v.ret(v.new_expr("!{res}", ret.as(not null)))
1555 return true
1556 else if pname == "<" then
1557 v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
1558 return true
1559 else if pname == ">" then
1560 v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null)))
1561 return true
1562 else if pname == "<=" then
1563 v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null)))
1564 return true
1565 else if pname == ">=" then
1566 v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null)))
1567 return true
1568 else if pname == "to_i" then
1569 v.ret(v.new_expr("(int){arguments[0]}", ret.as(not null)))
1570 return true
1571 else if pname == "to_b" then
1572 v.ret(v.new_expr("(byte){arguments[0]}", ret.as(not null)))
1573 return true
1574 end
1575 end
1576 if pname == "exit" then
1577 v.add("System.exit({arguments[1]});")
1578 v.ret(v.null_instance)
1579 return true
1580 else if pname == "sys" then
1581 # TODO singleton
1582 var main_type = v.compiler.mainmodule.sys_type.as(not null)
1583 var sys = main_type.mclass
1584 v.ret(v.new_expr("new RTVal({sys.rt_name}.get{sys.rt_name}())", main_type))
1585 return true
1586 else if pname == "object_id" then
1587 v.ret(v.new_expr("{arguments[0]}.hashCode()", ret.as(not null)))
1588 return true
1589 else if pname == "is_same_type" then
1590 v.ret(v.is_same_type_test(arguments[0], arguments[1]))
1591 return true
1592 else if pname == "is_same_instance" then
1593 v.ret(v.equal_test(arguments[0], arguments[1]))
1594 return true
1595 else if pname == "output_class_name" then
1596 v.add("System.out.println({arguments[0]}.rtclass.class_name);")
1597 v.ret(v.null_instance)
1598 return true
1599 end
1600 return false
1601 end
1602 end
1603
1604 redef class AAttrPropdef
1605 redef fun compile_to_java(v, mpropdef, arguments) do
1606 v.current_node = self
1607 if mpropdef == mreadpropdef then
1608 compile_getter(v, mpropdef, arguments)
1609 else if mpropdef == mwritepropdef then
1610 compile_setter(v, mpropdef, arguments)
1611 else
1612 abort
1613 end
1614 v.current_node = null
1615 end
1616
1617 # Compile the setter method
1618 private fun compile_setter(v: JavaCompilerVisitor, mpropdef: MPropDef, arguments: Array[RuntimeVariable]) do
1619 var mtype = v.compiler.mainmodule.object_type
1620 var recv = arguments.first
1621 var val = v.new_expr("args[1]", mtype)
1622 v.write_attribute(self.mpropdef.as(not null).mproperty, recv, val)
1623 v.ret v.null_instance
1624 end
1625
1626 # Compile the getter method
1627 private fun compile_getter(v: JavaCompilerVisitor, mpropdef: MPropDef, arguments: Array[RuntimeVariable]) do
1628 var recv = arguments.first
1629 v.ret v.read_attribute(self.mpropdef.as(not null).mproperty, recv)
1630 end
1631
1632 private fun init_expr(v: JavaCompilerVisitor, recv: RuntimeVariable) do
1633 if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
1634 end
1635
1636 # Evaluate, store and return the default value of the attribute
1637 private fun evaluate_expr(v: JavaCompilerVisitor, recv: RuntimeVariable): RuntimeVariable do
1638 var old = v.frame
1639 var frame = new JavaStaticFrame(v, self.mreadpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
1640 v.frame = frame
1641
1642 var value
1643 var mtype = self.mtype
1644 assert mtype != null
1645
1646 var nexpr = self.n_expr
1647 var nblock = self.n_block
1648 if nexpr != null then
1649 value = v.expr(nexpr, mtype)
1650 else if nblock != null then
1651 value = v.new_var(mtype)
1652 frame.returnvar = value
1653 frame.returnlabel = v.get_name("RET_LABEL")
1654 v.add("\{")
1655 v.stmt(nblock)
1656 v.add("{frame.returnlabel.as(not null)}:(void)0;")
1657 v.add("\}")
1658 else
1659 abort
1660 end
1661
1662 v.write_attribute(self.mpropdef.as(not null).mproperty, recv, value)
1663 v.frame = old
1664 return value
1665 end
1666 end
1667
1668 redef class AExpr
1669 # Try to compile self as an expression
1670 # Do not call this method directly, use `v.expr` instead
1671 private fun expr(v: JavaCompilerVisitor): nullable RuntimeVariable do
1672 v.info("NOT YET IMPLEMENTED {class_name}::expr")
1673 return null
1674 end
1675
1676 # Try to compile self as a statement
1677 # Do not call this method directly, use `v.stmt` instead
1678 private fun stmt(v: JavaCompilerVisitor) do expr(v)
1679 end
1680
1681 redef class ABlockExpr
1682 redef fun stmt(v)
1683 do
1684 for e in self.n_expr do v.stmt(e)
1685 end
1686 redef fun expr(v)
1687 do
1688 var last = self.n_expr.last
1689 for e in self.n_expr do
1690 if e == last then break
1691 v.stmt(e)
1692 end
1693 return v.expr(last, null)
1694 end
1695 end
1696
1697 redef class ASendExpr
1698 redef fun expr(v) do
1699 var recv = v.expr(n_expr, null)
1700 var callsite = callsite.as(not null)
1701 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, raw_arguments)
1702 return v.compile_callsite(callsite, args)
1703 end
1704 end
1705
1706 redef class ANewExpr
1707 redef fun expr(v)
1708 do
1709 var mtype = self.recvtype
1710 assert mtype != null
1711
1712 if mtype.mclass.name == "NativeArray" then
1713 # TODO handle native arrays
1714 v.info("NOT YET IMPLEMENTED new NativeArray")
1715 end
1716
1717 var recv = v.init_instance(mtype)
1718
1719 var callsite = self.callsite
1720 if callsite == null then return recv
1721
1722 var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
1723 var res2 = v.compile_callsite(callsite, args)
1724 if res2 != null then
1725 return res2
1726 end
1727 return recv
1728 end
1729 end
1730
1731 redef class ASelfExpr
1732 redef fun expr(v) do return v.frame.as(not null).arguments.first
1733 end
1734
1735 redef class AImplicitSelfExpr
1736 redef fun expr(v) do return v.frame.as(not null).arguments.first
1737 end
1738
1739 redef class AReturnExpr
1740 redef fun stmt(v) do
1741 var nexpr = self.n_expr
1742 var frame = v.frame
1743 assert frame != null
1744 if nexpr != null then
1745 v.ret(v.expr(nexpr, frame.returnvar.as(not null).mtype))
1746 else
1747 v.ret(v.null_instance)
1748 end
1749 end
1750 end
1751
1752 redef class AVardeclExpr
1753 redef fun stmt(v) do
1754 var variable = self.variable.as(not null)
1755 var ne = self.n_expr
1756 var decl = v.variable(variable)
1757 if ne != null then
1758 var i = v.expr(ne, variable.declared_type)
1759 v.assign(decl, i)
1760 end
1761 end
1762 end
1763
1764 redef class AVarExpr
1765 redef fun expr(v) do
1766 return v.variable(self.variable.as(not null))
1767 end
1768 end
1769
1770 redef class AVarAssignExpr
1771 redef fun expr(v) do
1772 var variable = self.variable.as(not null)
1773 var i = v.expr(self.n_value, variable.declared_type)
1774 v.assign(v.variable(variable), i)
1775 return i
1776 end
1777 end
1778
1779 redef class AIntExpr
1780 redef fun expr(v) do return v.int_instance(self.value.as(not null))
1781 end
1782
1783 redef class AByteExpr
1784 redef fun expr(v) do return v.byte_instance(self.value.as(not null))
1785 end
1786
1787 redef class AFloatExpr
1788 redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
1789 end
1790
1791 redef class ACharExpr
1792 redef fun expr(v) do return v.char_instance(self.value.as(not null))
1793 end
1794
1795 redef class ATrueExpr
1796 redef fun expr(v) do return v.bool_instance(true)
1797 end
1798
1799 redef class AFalseExpr
1800 redef fun expr(v) do return v.bool_instance(false)
1801 end
1802
1803 redef class ANullExpr
1804 redef fun expr(v) do return v.null_instance
1805 end
1806
1807 redef class AAbortExpr
1808 redef fun stmt(v) do v.add_abort("Aborted")
1809 end
1810
1811 redef class ADebugTypeExpr
1812 redef fun stmt(v) do end # do nothing
1813 end