1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Compile Nit code to Java code
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
22 # More details are given in the documentation of these 3 classes.
24 # TODO Factorize with `abstract_compiler`
27 import rapid_type_analysis
30 redef class ToolContext
32 # Where to output the generated binary
33 var opt_output
= new OptionString("Output file", "-o", "--output")
35 # Where to output tmp files
36 var opt_compile_dir
= new OptionString("Directory used to generate temporary files", "--compile-dir")
40 option_context
.add_option
(opt_output
, opt_compile_dir
)
44 redef class ModelBuilder
46 # Start the Java compiler
47 fun run_java_compiler
(mainmodule
: MModule, runtime_type_analysis
: RapidTypeAnalysis) do
49 toolcontext
.info
("*** GENERATING JAVA ***", 1)
51 var compiler
= new JavaCompiler(mainmodule
, self, runtime_type_analysis
)
52 compiler
.do_compilation
55 toolcontext
.info
("*** END GENERATING JAVA: {time1-time0} ***", 2)
56 write_and_make
(compiler
)
59 # Write Java code and compile it into an executable jar
60 fun write_and_make
(compiler
: JavaCompiler) do
62 toolcontext
.info
("*** WRITING JAVA ***", 1)
64 compiler
.compile_dir
.mkdir
66 var jfiles
= write_java_files
(compiler
)
69 toolcontext
.info
("*** END WRITING JAVA: {time1-time0} ***", 2)
72 toolcontext
.info
("*** COMPILING JAVA ***", 1)
74 build_with_make
(compiler
, jfiles
)
75 write_shell_script
(compiler
)
78 toolcontext
.info
("*** END COMPILING JAVA: {time1-time0} ***", 2)
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
)
88 jfiles
.add
(f
.filename
)
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)
101 if toolcontext
.verbose_level
>= 3 then
102 res
= sys
.system
("make -B -C {compile_dir} -f {outname}.mk 2>&1")
104 res
= sys
.system
("make -B -C {compile_dir} -f {outname}.mk 2>&1 > /dev/null")
106 if res
!= 0 then toolcontext
.error
(null, "make failed! Error code: {res}.")
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")
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
)
121 makefile
.write
("JC = javac\n")
122 makefile
.write
("JAR = jar\n\n")
124 makefile
.write
("all: {outpath}.jar\n\n")
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")
129 makefile
.write
("{compiler.mainmodule.jname}_Main.class:\n")
130 makefile
.write
("\t$(JC) {jfiles.join(" ")}\n\n")
132 makefile
.write
("clean:\n")
133 makefile
.write
("\trm {ofiles.join(" ")} 2>/dev/null\n\n")
136 toolcontext
.info
("Generated makefile: {makename}", 2)
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")
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")
156 sys
.system
("chmod +x {outname}")
160 # Compiler that translates Nit code to Java code
162 # The main module of the program currently compiled
163 var mainmodule
: MModule
165 # Modelbuilder used to know the model and the AST
166 var modelbuilder
: ModelBuilder
168 # The result of the RTA (used to know live types and methods)
169 var runtime_type_analysis
: RapidTypeAnalysis
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"
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
185 # The list of all associated files
186 # Used to generate .java files
187 var files
: Array[JavaCodeFile] = new Array[JavaCodeFile]
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
)
197 # Kind of visitor to use
198 type VISITOR: JavaCompilerVisitor
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
))
205 # RuntimeModel representation
206 private var rt_model
: JavaRuntimeModel is lazy
do return new JavaRuntimeModel
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)
214 # compile class structures
215 compile_mclasses_to_java
217 # compile method structures
218 compile_mmethods_to_java
221 modelbuilder
.toolcontext
.info
("NOT YET IMPLEMENTED", 0)
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
)
236 # Types of boxes used to represent Java primitive types
237 var box_kinds
= new Array[MClassType]
239 # Generate a `RTClass` for each `MClass` found in model
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"))
249 # Generate a `RTMethod` for each `MMethodDef` found in model
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"))
265 # The class visiting the AST
267 # A visitor is attached to one JavaCodeFile it writes into.
268 class JavaCompilerVisitor
271 # JavaCompiler used with this visitor
272 type COMPILER: JavaCompiler
274 # The associated compiler
275 var compiler
: JavaCompiler
277 # The file to write generated code into
278 var file
: JavaCodeFile
282 private var names
= new HashSet[String]
283 private var last
: Int = 0
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
291 var i
= self.last
+ 1
294 if not self.names
.has
(s2
) then
305 # Registered variables
306 protected var variables
= new HashMap[Variable, RuntimeVariable]
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
]
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
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} */;")
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
)
339 # The current `JavaStaticFrame`
340 var frame
: nullable JavaStaticFrame = null is writable
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];")
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
358 for p
in initializers
do
359 if p
isa MMethod then
361 var msignature
= p
.intro
.msignature
362 if msignature
!= null then
363 for x
in msignature
.mparameters
do
364 args
.add arguments
[i
]
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])
375 assert i
== arguments
.length
377 return send
(callsite
.mproperty
, [recv
])
380 return send
(callsite
.mproperty
, arguments
)
383 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
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]
392 if msignature
.arity
== 0 then return res
395 assert args
.length
== msignature
.arity
397 res
.add expr
(ne
, null)
402 # Eval in order of arguments, not parameters
403 var exprs
= new Array[RuntimeVariable].with_capacity
(args
.length
)
405 exprs
.add expr
(ne
, null)
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
)
414 res
.add
(null_instance
)
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
)
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
436 ret
= ret
.resolve_for
(mmethoddef
.mclassdef
.bound_mtype
, mmethoddef
.mclassdef
.bound_mtype
, mmethoddef
.mclassdef
.mmodule
, true)
437 res
= self.new_var
(ret
)
441 adapt_signature
(mmethoddef
, arguments
)
443 var rt_name
= mmethoddef
.rt_name
445 add
("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});")
448 var ress
= new_expr
("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});", compiler
.mainmodule
.object_type
)
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
)
460 return table_send
(mmethod
, arguments
)
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.
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.
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 */")
489 add
("{res} = false; /* {arg.inspect} cannot be null */")
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 */")
500 add
("{res} = true; /* {arg.inspect} cannot be null */")
503 add_abort
("Receiver is null")
509 add
"/* recv ({recv}) cannot be null since it's a {recv.mcasttype}"
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 */")
520 add
("\}") # closes the null case
521 add
("if (false) \{") # what follow is useless, Javac will drop it
526 # Perform a method call through vft
527 private fun table_send
(mmethod
: TableCallable, arguments
: Array[RuntimeVariable]): nullable RuntimeVariable do
530 if mmethod
isa MMethod then
532 name
= mmethod
.full_name
533 else if mmethod
isa MMethodDef then
535 name
= mmethod
.full_name
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
)
546 var res
: nullable RuntimeVariable
547 var ret
= msignature
.return_mtype
551 res
= self.new_var
(ret
)
554 before_send
(res
, mdef
, arguments
)
556 add
"/* concrete call to {mdef} */"
558 var ress
= new_expr
("{recv}.rtclass.vft.get(\"{name}\
").exec(new RTVal[]\{{arguments.join(",")}\});", compiler
.mainmodule
.object_type
)
561 add
("{recv}.rtclass.vft.get(\"{name}\
").exec(new RTVal[]\{{arguments.join(",")}\});")
564 add
("\}") # closes the null case
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
)
578 # Add a line (will be suffixed by `\n`)
579 fun add
(line
: String) do file
.lines
.add
("{line}\n")
581 # Add a new partial line (no `\n` suffix)
582 fun addn
(line
: String) do file
.lines
.add
(line
)
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
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
600 if nexpr
.mtype
!= null then
601 res
= nexpr
.expr
(self)
604 if mtype
!= null then
605 mtype
= anchor
(mtype
)
606 res
= autobox
(res
, mtype
)
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)};")
619 # Generate a return with `value`
620 fun ret
(value
: RuntimeVariable) do
621 var frame
= self.frame
623 var returnvar
= frame
.returnvar
624 if returnvar
!= null then
625 assign
(returnvar
, value
)
627 self.add
("break {frame.returnlabel.as(not null)};")
630 # Return a new local RuntimeVariable initialized with the Java expression `jexpr`.
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};")
639 # Generate generic abort
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
646 add
("System.err.print(\" ({node.location.short_location})\
");")
648 add
("System.err.println(\"\
");")
649 add
("System.exit(1);")
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
)
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
)
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
)
679 var mbox
= value
.mtype
.as(MClassType).mclass
680 return new_expr
("new RTVal({mbox.rt_name}.get{mbox.rt_name}(), {value})", obj_type
)
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
)
690 info
"NOT YET IMPLEMENTED unbox for {value} ({mtype})"
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
)
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
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
)
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
)
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
)
729 # Generate a float value
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
)
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
)
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
)
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")
754 # TODO return array_instance(varargs, elttype)
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
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
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});")
784 var rt_name
= value1
.mtype
.as(MClassType).mclass
.rt_name
785 add
("{res} = ({value2} != null) && ({value2}.rtclass == {rt_name}.get{rt_name}());")
787 add
("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
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()")
801 var t2
= value2
.mcasttype
802 if t2
isa MNullableType then
803 test
.add
("{value2} != null && !{value2}.is_null()")
809 var incompatible
= false
811 if t1
.is_java_primitive
then
814 # No need to compare class
815 else if t2
.is_java_primitive
then
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 */")
822 # if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
823 # test.add("(!{extract_tag(value2)})")
825 test
.add
("{value1}.rtclass == {value2}.rtclass")
829 else if t2
.is_java_primitive
then
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 */")
836 test
.add
("{value1}.rtclass == {value2}.rtclass")
846 self.add
("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
849 self.add
("{res} = false; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
853 if primitive
!= null then
854 if primitive
.is_java_primitive
then
855 self.add
("{res} = {value1} == {value2};")
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)"
870 self.add
("{res} = {value1} == {value2}; /* both can be primitive */")
873 test
.add
("({s.join(" || ")})")
875 self.add
("{res} = {value1} == {value2}; /* no primitives */")
878 self.add
("{res} = {value1} == {value2} || ({test.join(" && ")});")
884 # Display a info message
885 fun info
(str
: String) do compiler
.modelbuilder
.toolcontext
.info
(str
, 0)
888 # A file containing Java code.
895 var lines
: List[String] = new List[String]
899 # A Java compatible name for `self`
900 private fun jname
: String do return name
.to_cmangle
903 # Handler for runtime classes generation
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
911 # Compile JavaRuntimeModel structures
912 fun compile_rtmodel
(compiler
: JavaCompiler) do
913 compile_rtclass
(compiler
)
914 compile_rtmethod
(compiler
)
915 compile_rtval
(compiler
)
918 # Compile the abstract runtime class structure
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() \{\}")
935 # Compile the abstract runtime method structure
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);")
948 # Compile the runtime value structure
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
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;")
969 v
.add
(" public RTVal(RTClass rtclass, Object value) \{")
970 v
.add
(" this.rtclass = rtclass;")
971 v
.add
(" this.value = value;")
973 v
.add
(" public boolean is_null() \{ return rtclass == null && value == null; \}")
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
982 # The name of the variable in the Java code
985 # The static type of the variable (as declard in Java)
988 # The current casted type of the variable (as known in Nit)
989 var mcasttype
: MType is writable
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
995 # Is this variable declared as a RTVal or a Java primitive one?
998 redef fun to_s
do return name
1003 if self.is_exact
then
1004 exact_str
= " exact"
1009 if self.mtype
== self.mcasttype
then
1010 type_str
= "{mtype}{exact_str}"
1012 type_str
= "{mtype}({mcasttype}{exact_str})"
1014 return "<{name}:{type_str}>"
1018 # The static context of a visited property in a `JavaCompilerVisitor`
1019 class JavaStaticFrame
1020 # The associated visitor
1021 var visitor
: JavaCompilerVisitor
1023 # The executed property.
1024 # A Method in case of a call, an attribute in case of a default initialization.
1025 var mpropdef
: MPropDef
1027 # The static type of the receiver
1028 var receiver
: MClassType
1030 # Arguments of the method (the first is the receiver)
1031 var arguments
: Array[RuntimeVariable]
1033 # The runtime_variable associated to the return (in a function)
1034 var returnvar
: nullable RuntimeVariable = null is writable
1036 # The label at the end of the property
1037 var returnlabel
: nullable String = null is writable
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}"
1050 # Return the Java type associated to a given Nit static type
1051 fun java_type
: String do return "RTVal"
1053 # Is the associated Java type a primitive one?
1055 # ENSURE `result == (java_type != "Object")`
1056 var is_java_primitive
: Bool is lazy
do return java_type
!= "RTVal"
1059 redef class MClassType
1061 redef var java_type
is lazy
do
1062 if mclass
.name
== "Int" then
1064 else if mclass
.name
== "Bool" then
1066 else if mclass
.name
== "Char" then
1068 else if mclass
.name
== "Float" then
1070 else if mclass
.name
== "Byte" then
1072 else if mclass
.name
== "NativeString" then
1074 else if mclass
.name
== "NativeArray" then
1084 private fun rt_name
: String do return "RTClass_{intro.mmodule.jname}_{jname}"
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}\
";")
1093 compile_type_table
(v
)
1095 v
.add
(" public static RTClass get{rt_name}() \{")
1096 v
.add
(" if(instance == null) \{")
1097 v
.add
(" instance = new {rt_name}();")
1099 v
.add
(" return instance;")
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
)
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}());")
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}());")
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);")
1136 v
.add
("supers.put(\"{pclass.jname}\
", {pclass.rt_name}.get{pclass.rt_name}());")
1142 # Used as a common type between MMethod and MMethodDef for `table_send`
1143 private interface TableCallable
1150 redef class MMethodDef
1154 private fun rt_name
: String do
1155 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
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}();")
1166 v
.add
(" return instance;")
1169 v
.add
(" public RTVal exec(RTVal[] args) \{")
1170 compile_inside_to_java
(v
)
1175 # Compile the body of this function
1176 fun compile_inside_to_java
(v
: JavaCompilerVisitor) do
1178 var modelbuilder
= v
.compiler
.modelbuilder
1179 var node
= modelbuilder
.mpropdef2node
(self)
1182 v
.add_abort
("Abstract method `{mproperty.name}` called on `\" + {selfvar}.rtclass
.class_name
+\
"`")
1183 v
.add
("return null;")
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")
1199 redef class APropdef
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;")
1208 redef class AMethPropdef
1209 redef fun compile_to_java
(v
, mpropdef
) do
1210 # TODO Call the implicit super-init
1212 var recv
= mpropdef
.mclassdef
.bound_mtype
1213 var arguments
= new Array[RuntimeVariable]
1214 var frame
= new JavaStaticFrame(v
, mpropdef
, recv
, arguments
)
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)};"
1222 var msignature
= mpropdef
.msignature
1224 if msignature
!= null then
1225 ret
= msignature
.return_mtype
1226 if ret
!= null then frame
.returnvar
= v
.new_var
(ret
)
1228 frame
.returnlabel
= v
.get_name
("RET_LABEL")
1230 if not mpropdef
.is_intern
and msignature
!= null then
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
)
1243 v
.add
("{frame.returnlabel.as(not null)}: \{")
1244 compile_inside_to_java
(v
, mpropdef
)
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};")
1252 v
.add
("return {frame.returnvar.as(not null)};")
1255 v
.add
("return null;")
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
)
1270 # Compile block if any
1271 var n_block
= n_block
1272 if n_block
!= null then
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
)
1288 else if pname
== "object_id" then
1289 v
.ret
(arguments
.first
)
1291 else if pname
== "+" then
1292 v
.ret
(v
.new_expr
("{arguments[0]} + {arguments[1]}", ret
.as(not null)))
1294 else if pname
== "-" then
1295 v
.ret
(v
.new_expr
("{arguments[0]} - {arguments[1]}", ret
.as(not null)))
1297 else if pname
== "unary -" then
1298 v
.ret
(v
.new_expr
("-{arguments[0]}", ret
.as(not null)))
1300 else if pname
== "unary +" then
1303 else if pname
== "*" then
1304 v
.ret
(v
.new_expr
("{arguments[0]} * {arguments[1]}", ret
.as(not null)))
1306 else if pname
== "/" then
1307 v
.ret
(v
.new_expr
("{arguments[0]} / {arguments[1]}", ret
.as(not null)))
1309 else if pname
== "%" then
1310 v
.ret
(v
.new_expr
("{arguments[0]} % {arguments[1]}", ret
.as(not null)))
1312 else if pname
== "lshift" then
1313 v
.ret
(v
.new_expr
("{arguments[0]} << {arguments[1]}", ret
.as(not null)))
1315 else if pname
== "rshift" then
1316 v
.ret
(v
.new_expr
("{arguments[0]} >> {arguments[1]}", ret
.as(not null)))
1318 else if pname
== "==" then
1319 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
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)))
1325 else if pname
== "<" then
1326 v
.ret
(v
.new_expr
("{arguments[0]} < {arguments[1]}", ret
.as(not null)))
1328 else if pname
== ">" then
1329 v
.ret
(v
.new_expr
("{arguments[0]} > {arguments[1]}", ret
.as(not null)))
1331 else if pname
== "<=" then
1332 v
.ret
(v
.new_expr
("{arguments[0]} <= {arguments[1]}", ret
.as(not null)))
1334 else if pname
== ">=" then
1335 v
.ret
(v
.new_expr
("{arguments[0]} >= {arguments[1]}", ret
.as(not null)))
1337 else if pname
== "to_f" then
1338 v
.ret
(v
.new_expr
("(double){arguments[0]}", ret
.as(not null)))
1340 else if pname
== "to_b" then
1341 v
.ret
(v
.new_expr
("(byte){arguments[0]}", ret
.as(not null)))
1343 else if pname
== "ascii" then
1344 v
.ret
(v
.new_expr
("(char){arguments[0]}", ret
.as(not null)))
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
)
1352 else if pname
== "object_id" then
1353 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1355 else if pname
== "successor" then
1356 v
.ret
(v
.new_expr
("(char)({arguments[0]} + {arguments[1]})", ret
.as(not null)))
1358 else if pname
== "predecessor" then
1359 v
.ret
(v
.new_expr
("(char)({arguments[0]} - {arguments[1]})", ret
.as(not null)))
1361 else if pname
== "==" then
1362 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
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)))
1368 else if pname
== "<" then
1369 v
.ret
(v
.new_expr
("{arguments[0]} < {arguments[1]}", ret
.as(not null)))
1371 else if pname
== ">" then
1372 v
.ret
(v
.new_expr
("{arguments[0]} > {arguments[1]}", ret
.as(not null)))
1374 else if pname
== "<=" then
1375 v
.ret
(v
.new_expr
("{arguments[0]} <= {arguments[1]}", ret
.as(not null)))
1377 else if pname
== ">=" then
1378 v
.ret
(v
.new_expr
("{arguments[0]} >= {arguments[1]}", ret
.as(not null)))
1380 else if pname
== "to_i" then
1381 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1383 else if pname
== "ascii" then
1384 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
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
)
1392 else if pname
== "object_id" then
1393 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1395 else if pname
== "+" then
1396 v
.ret
(v
.new_expr
("(byte)({arguments[0]} + {arguments[1]})", ret
.as(not null)))
1398 else if pname
== "-" then
1399 v
.ret
(v
.new_expr
("(byte)({arguments[0]} - {arguments[1]})", ret
.as(not null)))
1401 else if pname
== "unary -" then
1402 v
.ret
(v
.new_expr
("(byte)(-{arguments[0]})", ret
.as(not null)))
1404 else if pname
== "unary +" then
1407 else if pname
== "*" then
1408 v
.ret
(v
.new_expr
("(byte)({arguments[0]} * {arguments[1]})", ret
.as(not null)))
1410 else if pname
== "/" then
1411 v
.ret
(v
.new_expr
("(byte)({arguments[0]} / {arguments[1]})", ret
.as(not null)))
1413 else if pname
== "%" then
1414 v
.ret
(v
.new_expr
("(byte)({arguments[0]} % {arguments[1]})", ret
.as(not null)))
1416 else if pname
== "lshift" then
1417 v
.ret
(v
.new_expr
("(byte)({arguments[0]} << {arguments[1]})", ret
.as(not null)))
1419 else if pname
== "rshift" then
1420 v
.ret
(v
.new_expr
("(byte)({arguments[0]} >> {arguments[1]})", ret
.as(not null)))
1422 else if pname
== "==" then
1423 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
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)))
1429 else if pname
== "<" then
1430 v
.ret
(v
.new_expr
("{arguments[0]} < {arguments[1]}", ret
.as(not null)))
1432 else if pname
== ">" then
1433 v
.ret
(v
.new_expr
("{arguments[0]} > {arguments[1]}", ret
.as(not null)))
1435 else if pname
== "<=" then
1436 v
.ret
(v
.new_expr
("{arguments[0]} <= {arguments[1]}", ret
.as(not null)))
1438 else if pname
== ">=" then
1439 v
.ret
(v
.new_expr
("{arguments[0]} >= {arguments[1]}", ret
.as(not null)))
1441 else if pname
== "to_i" then
1442 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1444 else if pname
== "to_f" then
1445 v
.ret
(v
.new_expr
("(double){arguments[0]}", ret
.as(not null)))
1447 else if pname
== "ascii" then
1448 v
.ret
(v
.new_expr
("{arguments[0]}", ret
.as(not null)))
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
)
1456 else if pname
== "object_id" then
1457 v
.ret
(v
.new_expr
("{arguments[0]}?1:0", ret
.as(not null)))
1459 else if pname
== "==" then
1460 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
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)))
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\
");"
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]}));"
1478 v
.ret
(v
.null_instance
)
1480 else if pname
== "object_id" then
1481 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1483 else if pname
== "+" then
1484 v
.ret
(v
.new_expr
("{arguments[0]} + {arguments[1]}", ret
.as(not null)))
1486 else if pname
== "-" then
1487 v
.ret
(v
.new_expr
("{arguments[0]} - {arguments[1]}", ret
.as(not null)))
1489 else if pname
== "unary -" then
1490 v
.ret
(v
.new_expr
("-{arguments[0]}", ret
.as(not null)))
1492 else if pname
== "unary +" then
1495 else if pname
== "succ" then
1496 v
.ret
(v
.new_expr
("{arguments[0]} + 1", ret
.as(not null)))
1498 else if pname
== "prec" then
1499 v
.ret
(v
.new_expr
("{arguments[0]} - 1", ret
.as(not null)))
1501 else if pname
== "*" then
1502 v
.ret
(v
.new_expr
("{arguments[0]} * {arguments[1]}", ret
.as(not null)))
1504 else if pname
== "/" then
1505 v
.ret
(v
.new_expr
("{arguments[0]} / {arguments[1]}", ret
.as(not null)))
1507 else if pname
== "==" then
1508 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
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)))
1514 else if pname
== "<" then
1515 v
.ret
(v
.new_expr
("{arguments[0]} < {arguments[1]}", ret
.as(not null)))
1517 else if pname
== ">" then
1518 v
.ret
(v
.new_expr
("{arguments[0]} > {arguments[1]}", ret
.as(not null)))
1520 else if pname
== "<=" then
1521 v
.ret
(v
.new_expr
("{arguments[0]} <= {arguments[1]}", ret
.as(not null)))
1523 else if pname
== ">=" then
1524 v
.ret
(v
.new_expr
("{arguments[0]} >= {arguments[1]}", ret
.as(not null)))
1526 else if pname
== "to_i" then
1527 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1529 else if pname
== "to_b" then
1530 v
.ret
(v
.new_expr
("(byte){arguments[0]}", ret
.as(not null)))
1534 if pname
== "exit" then
1535 v
.add
("System.exit({arguments[1]});")
1536 v
.ret
(v
.null_instance
)
1538 else if pname
== "sys" then
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
))
1544 else if pname
== "object_id" then
1545 v
.ret
(v
.new_expr
("{arguments[0]}.hashCode()", ret
.as(not null)))
1547 else if pname
== "is_same_type" then
1548 v
.ret
(v
.is_same_type_test
(arguments
[0], arguments
[1]))
1550 else if pname
== "is_same_instance" then
1551 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
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
)
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")
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
)
1575 redef class ABlockExpr
1578 for e
in self.n_expr
do v
.stmt
(e
)
1582 var last
= self.n_expr
.last
1583 for e
in self.n_expr
do
1584 if e
== last
then break
1587 return v
.expr
(last
, null)
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
)
1600 redef class ANewExpr
1603 var mtype
= self.recvtype
1604 assert mtype
!= null
1606 if mtype
.mclass
.name
== "NativeArray" then
1607 # TODO handle native arrays
1608 v
.info
("NOT YET IMPLEMENTED new NativeArray")
1611 var recv
= v
.init_instance
(mtype
)
1613 var callsite
= self.callsite
1614 if callsite
== null then return recv
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
1625 redef class ASelfExpr
1626 redef fun expr
(v
) do return v
.frame
.as(not null).arguments
.first
1629 redef class AImplicitSelfExpr
1630 redef fun expr
(v
) do return v
.frame
.as(not null).arguments
.first
1633 redef class AReturnExpr
1634 redef fun stmt
(v
) do
1635 var nexpr
= self.n_expr
1637 assert frame
!= null
1638 if nexpr
!= null then
1639 v
.ret
(v
.expr
(nexpr
, frame
.returnvar
.as(not null).mtype
))
1641 v
.ret
(v
.null_instance
)
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
)
1652 var i
= v
.expr
(ne
, variable
.declared_type
)
1658 redef class AVarExpr
1659 redef fun expr
(v
) do
1660 return v
.variable
(self.variable
.as(not null))
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
)
1673 redef class AIntExpr
1674 redef fun expr
(v
) do return v
.int_instance
(self.value
.as(not null))
1677 redef class AByteExpr
1678 redef fun expr
(v
) do return v
.byte_instance
(self.value
.as(not null))
1681 redef class AFloatExpr
1682 redef fun expr
(v
) do return v
.float_instance
("{self.n_float.text}") # FIXME use value, not n_float
1685 redef class ACharExpr
1686 redef fun expr
(v
) do return v
.char_instance
(self.value
.as(not null))
1689 redef class ATrueExpr
1690 redef fun expr
(v
) do return v
.bool_instance
(true)
1693 redef class AFalseExpr
1694 redef fun expr
(v
) do return v
.bool_instance
(false)
1697 redef class ANullExpr
1698 redef fun expr
(v
) do return v
.null_instance
1701 redef class AAbortExpr
1702 redef fun stmt
(v
) do v
.add_abort
("Aborted")
1705 redef class ADebugTypeExpr
1706 redef fun stmt
(v
) do end # do nothing