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
31 redef class ToolContext
33 # Where to output the generated binary
34 var opt_output
= new OptionString("Output file", "-o", "--output")
36 # Where to output tmp files
37 var opt_compile_dir
= new OptionString("Directory used to generate temporary files", "--compile-dir")
41 option_context
.add_option
(opt_output
, opt_compile_dir
)
45 redef class ModelBuilder
47 # Start the Java compiler
48 fun run_java_compiler
(mainmodule
: MModule, runtime_type_analysis
: RapidTypeAnalysis) do
50 toolcontext
.info
("*** GENERATING JAVA ***", 1)
52 var compiler
= new JavaCompiler(mainmodule
, self, runtime_type_analysis
)
53 compiler
.do_compilation
56 toolcontext
.info
("*** END GENERATING JAVA: {time1-time0} ***", 2)
57 write_and_make
(compiler
)
60 # Write Java code and compile it into an executable jar
61 fun write_and_make
(compiler
: JavaCompiler) do
63 toolcontext
.info
("*** WRITING JAVA ***", 1)
65 compiler
.compile_dir
.mkdir
67 var jfiles
= write_java_files
(compiler
)
70 toolcontext
.info
("*** END WRITING JAVA: {time1-time0} ***", 2)
73 toolcontext
.info
("*** COMPILING JAVA ***", 1)
75 build_with_make
(compiler
, jfiles
)
76 write_shell_script
(compiler
)
79 toolcontext
.info
("*** END COMPILING JAVA: {time1-time0} ***", 2)
82 # Write files managed by `compiler` into concrete files
83 fun write_java_files
(compiler
: JavaCompiler): Array[String] do
84 var jfiles
= new Array[String]
85 for f
in compiler
.files
do
86 var file
= new FileWriter.open
("{compiler.compile_dir}/{f.filename}")
87 for line
in f
.lines
do file
.write
(line
)
89 jfiles
.add
(f
.filename
)
94 # Compile Java generated files using `make`
95 fun build_with_make
(compiler
: JavaCompiler, jfiles
: Array[String]) do
96 write_manifest
(compiler
)
97 write_makefile
(compiler
, jfiles
)
98 var compile_dir
= compiler
.compile_dir
99 var outname
= compiler
.outname
.to_path
.filename
100 toolcontext
.info
("make -N -C {compile_dir} -f {outname}.mk", 2)
102 if toolcontext
.verbose_level
>= 3 then
103 res
= sys
.system
("make -B -C {compile_dir} -f {outname}.mk 2>&1")
105 res
= sys
.system
("make -B -C {compile_dir} -f {outname}.mk 2>&1 > /dev/null")
107 if res
!= 0 then toolcontext
.error
(null, "make failed! Error code: {res}.")
110 # Write the Makefile used to compile Java generated files into an executable jar
111 fun write_makefile
(compiler
: JavaCompiler, jfiles
: Array[String]) do
112 # list class files from jfiles
113 var ofiles
= new List[String]
114 for f
in jfiles
do ofiles
.add
(f
.strip_extension
(".java") + ".class")
116 var compile_dir
= compiler
.compile_dir
117 var outname
= compiler
.outname
.to_path
.filename
118 var outpath
= (sys
.getcwd
/ compiler
.outname
).simplify_path
119 var makename
= "{compile_dir}/{outname}.mk"
120 var makefile
= new FileWriter.open
(makename
)
122 makefile
.write
("JC = javac\n")
123 makefile
.write
("JAR = jar\n\n")
125 makefile
.write
("all: {outpath}.jar\n\n")
127 makefile
.write
("{outpath}.jar: {compiler.mainmodule.jname}_Main.class\n")
128 makefile
.write
("\t$(JAR) cfm {outpath}.jar {outname}.mf {ofiles.join(" ")}\n\n")
130 makefile
.write
("{compiler.mainmodule.jname}_Main.class:\n")
131 makefile
.write
("\t$(JC) {jfiles.join(" ")}\n\n")
133 makefile
.write
("clean:\n")
134 makefile
.write
("\trm {ofiles.join(" ")} 2>/dev/null\n\n")
137 toolcontext
.info
("Generated makefile: {makename}", 2)
140 # Write the Java manifest file
141 private fun write_manifest
(compiler
: JavaCompiler) do
142 var compile_dir
= compiler
.compile_dir
143 var outname
= compiler
.outname
.to_path
.filename
144 var maniffile
= new FileWriter.open
("{compile_dir}/{outname}.mf")
145 maniffile
.write
("Manifest-Version: 1.0\n")
146 maniffile
.write
("Main-Class: {compiler.mainmodule.jname}_Main\n")
150 # Write a simple bash script that runs the jar like it was a binary generated by nitc
151 private fun write_shell_script
(compiler
: JavaCompiler) do
152 var outname
= compiler
.outname
153 var shfile
= new FileWriter.open
(outname
)
154 shfile
.write
("#!/bin/bash\n")
155 shfile
.write
("java -jar {outname}.jar \"$@\
"\n")
157 sys
.system
("chmod +x {outname}")
161 # Compiler that translates Nit code to Java code
163 # The main module of the program currently compiled
164 var mainmodule
: MModule
166 # Modelbuilder used to know the model and the AST
167 var modelbuilder
: ModelBuilder
169 # The result of the RTA (used to know live types and methods)
170 var runtime_type_analysis
: RapidTypeAnalysis
172 # Where to generate tmp files
173 var compile_dir
: String is lazy
do
174 var dir
= modelbuilder
.toolcontext
.opt_compile_dir
.value
175 if dir
== null then dir
= "nitj_compile"
179 # Name of the generated executable
180 var outname
: String is lazy
do
181 var name
= modelbuilder
.toolcontext
.opt_output
.value
182 if name
== null then name
= mainmodule
.jname
186 # The list of all associated files
187 # Used to generate .java files
188 var files
: Array[JavaCodeFile] = new Array[JavaCodeFile]
190 # Force the creation of a new file
191 # The point is to avoid contamination between must-be-compiled-separately files
192 fun new_file
(name
: String): JavaCodeFile do
193 var file
= new JavaCodeFile(name
)
198 # Kind of visitor to use
199 type VISITOR: JavaCompilerVisitor
201 # Initialize a visitor specific for the compiler engine
202 fun new_visitor
(filename
: String): VISITOR do
203 return new JavaCompilerVisitor(self, new_file
(filename
))
206 # RuntimeModel representation
207 private var rt_model
: JavaRuntimeModel is lazy
do return new JavaRuntimeModel
209 # Compile Nit code to Java
210 fun do_compilation
do
211 # compile java classes used to represents the runtime model of the program
212 rt_model
.compile_rtmodel
(self)
215 # compile class structures
216 compile_mclasses_to_java
218 # compile method structures
219 compile_mmethods_to_java
222 compile_main_function
225 # Prepare the boxes used to represent Java primitive types
226 fun compile_box_kinds
do
227 # Collect all bas box class
228 # FIXME: this is not completely fine with a separate compilation scheme
229 for classname
in ["Int", "Bool", "Byte", "Char", "Float"] do
230 var classes
= mainmodule
.model
.get_mclasses_by_name
(classname
)
231 if classes
== null then continue
232 assert classes
.length
== 1 else print classes
.join
(", ")
233 box_kinds
.add
(classes
.first
.mclass_type
)
237 # Types of boxes used to represent Java primitive types
238 var box_kinds
= new Array[MClassType]
240 # Generate a `RTClass` for each `MClass` found in model
242 # This is a global phase because we need to know all the program to build
243 # attributes, fill vft and type table.
244 fun compile_mclasses_to_java
do
245 for mclass
in mainmodule
.model
.mclasses
do
246 mclass
.compile_to_java
(new_visitor
("{mclass.rt_name}.java"))
250 # Generate a `RTMethod` for each `MMethodDef` found in model
252 # This is a separate phase.
253 fun compile_mmethods_to_java
do
254 for mmodule
in mainmodule
.in_importation
.greaters
do
255 for mclassdef
in mmodule
.mclassdefs
do
256 for mdef
in mclassdef
.mpropdefs
do
257 if mdef
isa MMethodDef then
258 mdef
.compile_to_java
(new_visitor
("{mdef.rt_name}.java"))
265 # Generate Java main that call Sys.main
266 fun compile_main_function
do
267 var v
= new_visitor
("{mainmodule.jname}_Main.java")
268 v
.add
("public class {mainmodule.jname}_Main \{")
269 v
.add
(" public static void main(String[] args) \{")
271 var main_type
= mainmodule
.sys_type
272 if main_type
!= null then
273 var mainmodule
= v
.compiler
.mainmodule
274 var glob_sys
= v
.init_instance
(main_type
)
275 var main_init
= mainmodule
.try_get_primitive_method
("init", main_type
.mclass
)
276 if main_init
!= null then
277 v
.send
(main_init
, [glob_sys
])
279 var main_method
= mainmodule
.try_get_primitive_method
("run", main_type
.mclass
) or else
280 mainmodule
.try_get_primitive_method
("main", main_type
.mclass
)
281 if main_method
!= null then
282 v
.send
(main_method
, [glob_sys
])
290 # The class visiting the AST
292 # A visitor is attached to one JavaCodeFile it writes into.
293 class JavaCompilerVisitor
296 # JavaCompiler used with this visitor
297 type COMPILER: JavaCompiler
299 # The associated compiler
300 var compiler
: JavaCompiler
302 # The file to write generated code into
303 var file
: JavaCodeFile
307 private var names
= new HashSet[String]
308 private var last
: Int = 0
310 # Return a new name based on `s` and unique in the visitor
311 fun get_name
(s
: String): String do
312 if not self.names
.has
(s
) then
316 var i
= self.last
+ 1
319 if not self.names
.has
(s2
) then
328 # Return an unique and stable identifier associated with an escapemark
329 fun escapemark_name
(e
: nullable EscapeMark): String do
331 var frame
= self.frame
333 if frame
.escapemark_names
.has_key
(e
) then return frame
.escapemark_names
[e
]
335 if name
== null then name
= "label"
336 name
= get_name
(name
)
337 frame
.escapemark_names
[e
] = name
341 # Insert a C label for associated with an escapemark
342 fun add_escape_label
(e
: nullable EscapeMark) do
343 if e
== null then return
344 if e
.escapes
.is_empty
then return
345 add
("BREAK_{escapemark_name(e)}: ")
350 # Registered variables
351 protected var variables
= new HashMap[Variable, RuntimeVariable]
353 # Return the local RuntimeVariable associated to a Nit local variable
354 fun variable
(variable
: Variable): RuntimeVariable do
355 if variables
.has_key
(variable
) then
356 return variables
[variable
]
358 var name
= get_name
("var_{variable.name}")
359 var mtype
= variable
.declared_type
.as(not null)
360 mtype
= anchor
(mtype
)
361 var res
= decl_var
(name
, mtype
)
362 variables
[variable
] = res
367 # Return a new uninitialized local RuntimeVariable with `name`
368 fun decl_var
(name
: String, mtype
: MType): RuntimeVariable do
369 var res
= new RuntimeVariable(name
, mtype
, mtype
)
370 res
.is_boxed
= not mtype
.is_java_primitive
371 add
("{mtype.java_type} {name} /* : {mtype} */;")
375 # Return a new uninitialized local RuntimeVariable
376 fun new_var
(mtype
: MType): RuntimeVariable do
377 mtype
= anchor
(mtype
)
378 var name
= self.get_name
("var")
379 return decl_var
(name
, mtype
)
384 # The current `JavaStaticFrame`
385 var frame
: nullable JavaStaticFrame = null is writable
387 # Return a new local RuntimeVariable initialized from `args[0]`
388 fun new_recv
(mtype
: MType): RuntimeVariable do
389 var res
= new_var
(mtype
)
390 add
("{res} = args[0];")
396 # Compile a call within a callsite
397 fun compile_callsite
(callsite
: CallSite, arguments
: Array[RuntimeVariable]): nullable RuntimeVariable do
398 var initializers
= callsite
.mpropdef
.initializers
399 if not initializers
.is_empty
then
400 var recv
= arguments
.first
403 for p
in initializers
do
404 if p
isa MMethod then
406 var msignature
= p
.intro
.msignature
407 if msignature
!= null then
408 for x
in msignature
.mparameters
do
409 args
.add arguments
[i
]
414 else if p
isa MAttribute then
415 info
("NOT YET IMPLEMENTED {class_name}::compile_callsite for MAttribute `{p}`")
416 #self.write_attribute(p, recv, arguments[i])
420 assert i
== arguments
.length
422 return send
(callsite
.mproperty
, [recv
])
425 return send
(callsite
.mproperty
, arguments
)
428 # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
430 # This method is used to manage varargs in signatures and returns the real array
431 # of runtime variables to use in the call.
432 fun varargize
(mpropdef
: MMethodDef, map
: nullable SignatureMap, recv
: RuntimeVariable, args
: SequenceRead[AExpr]): Array[RuntimeVariable] do
433 var msignature
= mpropdef
.new_msignature
or else mpropdef
.msignature
.as(not null)
434 var res
= new Array[RuntimeVariable]
437 if msignature
.arity
== 0 then return res
440 assert args
.length
== msignature
.arity
442 res
.add expr
(ne
, null)
447 # Eval in order of arguments, not parameters
448 var exprs
= new Array[RuntimeVariable].with_capacity
(args
.length
)
450 exprs
.add expr
(ne
, null)
453 # Fill `res` with the result of the evaluation according to the mapping
454 for i
in [0..msignature
.arity
[ do
455 var param
= msignature
.mparameters
[i
]
456 var j
= map
.map
.get_or_null
(i
)
459 res
.add
(null_instance
)
462 if param
.is_vararg
and map
.vararg_decl
> 0 then
463 var vararg
= exprs
.sub
(j
, map
.vararg_decl
)
464 var elttype
= param
.mtype
465 var arg
= self.vararg_instance
(mpropdef
, recv
, vararg
, elttype
)
474 # Generate a static call on a method definition (no receiver needed).
475 fun static_call
(mmethoddef
: MMethodDef, arguments
: Array[RuntimeVariable]): nullable RuntimeVariable do
476 var res
: nullable RuntimeVariable
477 var ret
= mmethoddef
.msignature
.as(not null).return_mtype
481 ret
= ret
.resolve_for
(mmethoddef
.mclassdef
.bound_mtype
, mmethoddef
.mclassdef
.bound_mtype
, mmethoddef
.mclassdef
.mmodule
, true)
482 res
= self.new_var
(ret
)
486 adapt_signature
(mmethoddef
, arguments
)
488 var rt_name
= mmethoddef
.rt_name
490 add
("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});")
493 var ress
= new_expr
("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});", compiler
.mainmodule
.object_type
)
498 # Generate a polymorphic send for `method` with `arguments`
499 fun send
(mmethod
: MMethod, arguments
: Array[RuntimeVariable]): nullable RuntimeVariable do
500 # Shortcut calls on primitives
501 if arguments
.first
.mcasttype
.is_java_primitive
then
502 return monomorphic_send
(mmethod
, arguments
.first
.mcasttype
, arguments
)
505 return table_send
(mmethod
, arguments
)
509 # Handle common special cases before doing the effective method invocation
510 # This methods handle the `==` and `!=` methods and the case of the null receiver.
511 # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
512 # Client must not forget to close the } after them.
514 # The value returned is the result of the common special cases.
515 # If not null, client must compile it with the result of their own effective method invocation.
517 # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
518 # is generated to cancel the effective method invocation that will follow
519 # TODO: find a better approach
520 private fun before_send
(res
: nullable RuntimeVariable, mmethod
: MMethodDef, arguments
: Array[RuntimeVariable]) do
521 var bool_type
= compiler
.mainmodule
.bool_type
522 var recv
= arguments
.first
523 var consider_null
= mmethod
.name
== "==" or mmethod
.name
== "!=" or mmethod
.name
== "is_same_instance"
524 if recv
.mcasttype
isa MNullableType or recv
.mcasttype
isa MNullType then
525 add
("if ({recv} == null || {recv}.is_null()) \{")
526 if mmethod
.name
== "==" or mmethod
.name
== "is_same_instance" then
527 if res
== null then res
= new_var
(bool_type
)
528 var arg
= arguments
[1]
529 if arg
.mcasttype
isa MNullableType then
530 add
("{res} = ({arg} == null || {arg}.is_null());")
531 else if arg
.mcasttype
isa MNullType then
532 add
("{res} = true; /* is null */")
534 add
("{res} = false; /* {arg.inspect} cannot be null */")
536 else if mmethod
.name
== "!=" then
537 if res
== null then res
= new_var
(bool_type
)
538 # res = self.new_var(bool_type)
539 var arg
= arguments
[1]
540 if arg
.mcasttype
isa MNullableType then
541 add
("{res} = ({arg} != null && !{arg}.is_null());")
542 else if arg
.mcasttype
isa MNullType then
543 add
("{res} = false; /* is null */")
545 add
("{res} = true; /* {arg.inspect} cannot be null */")
548 add_abort
("Receiver is null")
554 add
"/* recv ({recv}) cannot be null since it's a {recv.mcasttype}"
556 if consider_null
then
557 var arg
= arguments
[1]
558 if arg
.mcasttype
isa MNullType then
559 if res
== null then res
= new_var
(bool_type
)
560 if mmethod
.name
== "!=" then
561 add
("{res} = true; /* arg is null and recv is not */")
562 else # `==` and `is_same_instance`
563 add
("{res} = false; /* arg is null but recv is not */")
565 add
("\}") # closes the null case
566 add
("if (false) \{") # what follow is useless, Javac will drop it
571 # Perform a method call through vft
572 private fun table_send
(mmethod
: TableCallable, arguments
: Array[RuntimeVariable]): nullable RuntimeVariable do
575 if mmethod
isa MMethod then
577 name
= mmethod
.full_name
578 else if mmethod
isa MMethodDef then
580 name
= mmethod
.full_name
585 var recv
= arguments
.first
586 var rect
= mdef
.mclassdef
.bound_mtype
587 var msignature
= mdef
.msignature
.as(not null)
588 msignature
= msignature
.resolve_for
(rect
, rect
, compiler
.mainmodule
, true)
589 adapt_signature
(mdef
, arguments
)
591 var res
: nullable RuntimeVariable
592 var ret
= msignature
.return_mtype
596 res
= self.new_var
(ret
)
599 before_send
(res
, mdef
, arguments
)
601 add
"/* concrete call to {mdef} */"
603 var ress
= new_expr
("{recv}.rtclass.vft.get(\"{name}\
").exec(new RTVal[]\{{arguments.join(",")}\});", compiler
.mainmodule
.object_type
)
606 add
("{recv}.rtclass.vft.get(\"{name}\
").exec(new RTVal[]\{{arguments.join(",")}\});")
609 add
("\}") # closes the null case
614 # Generate a super call from a method definition
615 fun supercall
(m
: MMethodDef, recvtype
: MClassType, args
: Array[RuntimeVariable]): nullable RuntimeVariable do
616 return table_send
(m
, args
)
619 # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args`
620 fun monomorphic_send
(m
: MMethod, t
: MType, args
: Array[RuntimeVariable]): nullable RuntimeVariable do
621 assert t
isa MClassType
622 var propdef
= m
.lookup_first_definition
(self.compiler
.mainmodule
, t
)
623 return self.static_call
(propdef
, args
)
628 # Add a line (will be suffixed by `\n`)
629 fun add
(line
: String) do file
.lines
.add
("{line}\n")
631 # Add a new partial line (no `\n` suffix)
632 fun addn
(line
: String) do file
.lines
.add
(line
)
634 # Compile a statement (if any)
635 fun stmt
(nexpr
: nullable AExpr) do
636 if nexpr
== null then return
637 var old
= self.current_node
643 # Compile an expression an return its result
644 # `mtype` is the expected return type, pass null if no specific type is expected.
645 fun expr
(nexpr
: AExpr, mtype
: nullable MType): RuntimeVariable do
646 var old
= current_node
650 if nexpr
.mtype
!= null then
651 res
= nexpr
.expr
(self)
654 if mtype
!= null then
655 mtype
= anchor
(mtype
)
656 res
= autobox
(res
, mtype
)
663 # Alias for `self.expr(nexpr, self.bool_type)`
664 fun expr_bool
(nexpr
: AExpr): RuntimeVariable do
665 return expr
(nexpr
, compiler
.mainmodule
.bool_type
)
668 # Correctly assign a left and a right value
669 # Boxing and unboxing is performed if required
670 fun assign
(left
, right
: RuntimeVariable) do
671 add
("{left} = {autobox(right, left.mtype)};")
674 # Generate a return with `value`
675 fun ret
(value
: RuntimeVariable) do
676 var frame
= self.frame
678 var returnvar
= frame
.returnvar
679 if returnvar
!= null then
680 assign
(returnvar
, value
)
682 self.add
("break {frame.returnlabel.as(not null)};")
685 # Return a new local RuntimeVariable initialized with the Java expression `jexpr`.
687 # `mtype` is used for the Java return variable initialization.
688 fun new_expr
(jexpr
: String, mtype
: MType): RuntimeVariable do
689 var res
= new_var
(mtype
)
690 add
("{res} = {jexpr};")
694 # Generate generic abort
696 # Used by aborts, asserts, casts, etc.
697 fun add_abort
(message
: String) do
698 add
("System.err.print(\"Runtime error
: {message}\
");")
699 var node
= current_node
701 add
("System.err.print(\" ({node.location.short_location})\
");")
703 add
("System.err.println(\"\
");")
704 add
("System.exit(1);")
709 # Anchor a type to the main module and the current receiver
710 fun anchor
(mtype
: MType): MType do
711 if not mtype
.need_anchor
then return mtype
712 return mtype
.anchor_to
(compiler
.mainmodule
, frame
.as(not null).receiver
)
715 # Adapt the arguments of a method according to targetted `MMethodDef`
716 fun adapt_signature
(m
: MMethodDef, args
: Array[RuntimeVariable]) do
717 var msignature
= m
.msignature
.as(not null).resolve_for
(
718 m
.mclassdef
.bound_mtype
,
719 m
.mclassdef
.bound_mtype
,
720 m
.mclassdef
.mmodule
, true)
721 args
.first
= autobox
(args
.first
, compiler
.mainmodule
.object_type
)
722 for i
in [0..msignature
.arity
[ do
723 args
[i
+1] = autobox
(args
[i
+ 1], compiler
.mainmodule
.object_type
)
727 # Box primitive `value` to `mtype`.
728 private fun box
(value
: RuntimeVariable, mtype
: MType): RuntimeVariable do
729 if value
.is_boxed
then return value
730 var obj_type
= compiler
.mainmodule
.object_type
731 if value
.mtype
isa MNullType then
732 return new_expr
("new RTVal(null, null)", compiler
.mainmodule
.model
.null_type
)
734 var mbox
= value
.mtype
.as(MClassType).mclass
735 return new_expr
("new RTVal({mbox.rt_name}.get{mbox.rt_name}(), {value})", obj_type
)
738 # Unbox primitive `value` to `mtype`.
739 private fun unbox
(value
: RuntimeVariable, mtype
: MType): RuntimeVariable do
740 if not value
.is_boxed
then return value
741 if not mtype
.is_java_primitive
then return value
742 if compiler
.box_kinds
.has
(mtype
) then
743 return new_expr
("({mtype.java_type}){value}.value", mtype
)
745 info
"NOT YET IMPLEMENTED unbox for {value} ({mtype})"
750 # Box or unbox primitive `value` to `mtype` if needed.
751 private fun autobox
(value
: RuntimeVariable, mtype
: MType): RuntimeVariable do
752 if mtype
.is_java_primitive
then return unbox
(value
, mtype
)
753 return box
(value
, mtype
)
756 # Can this `value` be a primitive Java value?
757 private fun can_be_primitive
(value
: RuntimeVariable): Bool do
758 var t
= value
.mcasttype
.undecorate
759 if not t
isa MClassType then return false
760 var k
= t
.mclass
.kind
761 return k
== interface_kind
or t
.is_java_primitive
764 # Generate a polymorphic subtype test
765 fun type_test
(value
: RuntimeVariable, mtype
: MType): RuntimeVariable do
766 add
("/* {value.inspect} isa {mtype} */")
767 var res
= self.new_var
(compiler
.mainmodule
.bool_type
)
769 # check color is in table
770 var maybenull
= (value
.mcasttype
isa MNullableType or value
.mcasttype
isa MNullType)
772 add
("if({value} == null || {value}.is_null()) \{")
773 add
("{res} = true && {mtype isa MNullableType};")
776 if mtype
isa MNullableType then mtype
= mtype
.mtype
777 var mclass
= mtype
.as(MClassType).mclass
778 add
("{res} = {value}.rtclass.supers.get(\"{mclass.jname}\
") == {mclass.rt_name}.get{mclass.rt_name}();")
785 # Generate the code required to dynamically check if 2 objects share the same runtime type
786 fun is_same_type_test
(value1
, value2
: RuntimeVariable): RuntimeVariable do
787 var res
= self.new_var
(compiler
.mainmodule
.bool_type
)
788 add
("{res} = {value1}.rtclass == {value2}.rtclass;")
794 # Generate an integer value
795 fun int_instance
(value
: Int): RuntimeVariable do
796 var t
= compiler
.mainmodule
.int_type
797 return new RuntimeVariable(value
.to_s
, t
, t
)
800 # Generate a byte value
801 fun byte_instance
(value
: Byte): RuntimeVariable do
802 var t
= compiler
.mainmodule
.byte_type
803 return new RuntimeVariable(value
.to_s
, t
, t
)
806 # Generate a char value
807 fun char_instance
(value
: Char): RuntimeVariable do
808 var t
= compiler
.mainmodule
.char_type
809 return new RuntimeVariable("'{value.to_s.escape_to_c}'", t
, t
)
812 # Generate a float value
814 # FIXME pass a Float, not a string
815 fun float_instance
(value
: String): RuntimeVariable do
816 var t
= compiler
.mainmodule
.float_type
817 return new RuntimeVariable(value
.to_s
, t
, t
)
820 # Generate an integer value
821 fun bool_instance
(value
: Bool): RuntimeVariable do
822 var t
= compiler
.mainmodule
.bool_type
823 return new RuntimeVariable(value
.to_s
, t
, t
)
826 # Generate the `null` value
827 fun null_instance
: RuntimeVariable do
828 var t
= compiler
.mainmodule
.model
.null_type
829 return new RuntimeVariable("null", t
, t
)
832 # Get an instance of a array for a vararg
833 fun vararg_instance
(mpropdef
: MPropDef, recv
: RuntimeVariable, varargs
: Array[RuntimeVariable], elttype
: MType): RuntimeVariable do
834 # TODO handle dynamic types
835 info
("NOT YET IMPLEMENTED vararg_instance")
837 # TODO return array_instance(varargs, elttype)
842 # Generate a alloc-instance + init-attributes
843 fun init_instance
(mtype
: MClassType): RuntimeVariable do
844 var rt_name
= mtype
.mclass
.rt_name
845 var res
= new_expr
("new RTVal({rt_name}.get{rt_name}())", mtype
)
846 generate_init_attr
(self, res
, mtype
)
850 # Generate code that initialize the attributes on a new instance
851 fun generate_init_attr
(v
: JavaCompilerVisitor, recv
: RuntimeVariable, mtype
: MClassType) do
852 var cds
= mtype
.collect_mclassdefs
(v
.compiler
.mainmodule
).to_a
853 v
.compiler
.mainmodule
.linearize_mclassdefs
(cds
)
855 for npropdef
in v
.compiler
.modelbuilder
.collect_attr_propdef
(cd
) do
856 npropdef
.init_expr
(v
, recv
)
861 # Generate a Nit "is" for two runtime_variables
862 fun equal_test
(value1
, value2
: RuntimeVariable): RuntimeVariable do
863 var res
= new_var
(compiler
.mainmodule
.bool_type
)
864 if value2
.mtype
.is_java_primitive
and not value1
.mtype
.is_java_primitive
then
869 if value1
.mtype
.is_java_primitive
then
870 if value2
.mtype
== value1
.mtype
then
871 add
("{res} = {value1} == {value2}; /* == with two primitives */")
872 else if value2
.mtype
.is_java_primitive
then
873 add
("{res} = true; /* incompatible types {value1.mtype} vs. {value2.mtype}*/")
874 # else if value1.mtype.is_tagged then
875 # add("{res} = ({value2} != NULL) && ({autobox(value2, value1.mtype)} == {value1});")
877 var rt_name
= value1
.mtype
.as(MClassType).mclass
.rt_name
878 add
("{res} = ({value2} != null) && ({value2}.rtclass == {rt_name}.get{rt_name}());")
880 add
("{res} = ({self.autobox(value2, value1.mtype)} == {value1});")
885 var maybe_null
= true
886 var test
= new Array[String]
887 var t1
= value1
.mcasttype
888 if t1
isa MNullableType then
889 test
.add
("{value1} != null && !{value1}.is_null()")
894 var t2
= value2
.mcasttype
895 if t2
isa MNullableType then
896 test
.add
("{value2} != null && !{value2}.is_null()")
902 var incompatible
= false
904 if t1
.is_java_primitive
then
907 # No need to compare class
908 else if t2
.is_java_primitive
then
910 else if can_be_primitive
(value2
) then
911 if t1
.is_java_primitive
then
912 self.add
("{res} = {value1} == {value2}; /* t1 is primitive and t2 can be */")
915 # if not compiler.modelbuilder.toolcontext.opt_no_tag_primitives.value then
916 # test.add("(!{extract_tag(value2)})")
918 test
.add
("{value1}.rtclass == {value2}.rtclass")
922 else if t2
.is_java_primitive
then
924 if can_be_primitive
(value1
) then
925 if t2
.is_java_primitive
then
926 self.add
("{res} = {value1} == {value2}; /* t2 is primitive and t1 can be */")
929 test
.add
("{value1}.rtclass == {value2}.rtclass")
939 self.add
("{res} = {value1} == {value2}; /* incompatible types {t1} vs. {t2}; but may be NULL*/")
942 self.add
("{res} = false; /* incompatible types {t1} vs. {t2}; cannot be NULL */")
946 if primitive
!= null then
947 if primitive
.is_java_primitive
then
948 self.add
("{res} = {value1} == {value2};")
951 test
.add
("({value1}.value == {value2}.value")
952 else if can_be_primitive
(value1
) and can_be_primitive
(value2
) then
953 test
.add
("{value1}.rtclass == {value2}.rtclass")
954 var s
= new Array[String]
955 for b
in compiler
.box_kinds
do
956 var rt_name
= b
.mclass
.rt_name
957 s
.add
"({value1}.rtclass == {rt_name}.get{rt_name}()) && ({value1}.value.equals({value2}.value))"
958 if b
.mclass
.name
== "Float" then
959 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)"
963 self.add
("{res} = {value1} == {value2}; /* both can be primitive */")
966 test
.add
("({s.join(" || ")})")
968 self.add
("{res} = {value1} == {value2}; /* no primitives */")
971 self.add
("{res} = {value1} == {value2} || ({test.join(" && ")});")
977 # Generate a polymorphic attribute is_set test
978 fun isset_attribute
(a
: MAttribute, recv
: RuntimeVariable): RuntimeVariable do
979 # TODO self.check_recv_notnull(recv)
980 var res
= new_var
(compiler
.mainmodule
.bool_type
)
982 # What is the declared type of the attribute?
983 var mtype
= a
.intro
.static_mtype
.as(not null)
984 var intromclassdef
= a
.intro
.mclassdef
985 mtype
= mtype
.resolve_for
(intromclassdef
.bound_mtype
, intromclassdef
.bound_mtype
, intromclassdef
.mmodule
, true)
987 if mtype
isa MNullableType then
988 add
("{res} = true; /* easy isset: {a} on {recv.inspect} */")
991 add
("{res} = {recv}.attrs.get(\"{a.jname}\
") != null; /* {a} on {recv.inspect} */")
995 # Generate a polymorphic attribute read
996 fun read_attribute
(a
: MAttribute, recv
: RuntimeVariable): RuntimeVariable do
997 # TODO check_recv_notnull(recv)
998 # TODO compile_check(v)
999 # What is the declared type of the attribute?
1000 var ret
= a
.intro
.static_mtype
.as(not null)
1001 var intromclassdef
= a
.intro
.mclassdef
1002 ret
= ret
.resolve_for
(intromclassdef
.bound_mtype
, intromclassdef
.bound_mtype
, intromclassdef
.mmodule
, true)
1004 # Check for Uninitialized attribute
1005 if not ret
isa MNullableType then check_attribute
(a
, recv
)
1007 return new_expr
("{recv}.attrs.get(\"{a.jname}\
")", ret
)
1010 # Generate a polymorphic attribute write
1011 fun write_attribute
(a
: MAttribute, recv
: RuntimeVariable, value
: RuntimeVariable) do
1012 # TODO check_recv_notnull(recv)
1013 add
"{recv}.attrs.put(\"{a.jname}\
", {autobox(value, compiler.mainmodule.object_type)});"
1016 # Check uninitialized attribute
1017 fun check_attribute
(a
: MAttribute, recv
: RuntimeVariable) do
1018 add
"if({recv}.attrs.get(\"{a.jname}\
") == null) \{"
1019 add_abort
"Uninitialized attribute {a.name}"
1025 # Display a info message
1026 fun info
(str
: String) do compiler
.modelbuilder
.toolcontext
.info
(str
, 0)
1029 # A file containing Java code.
1033 var filename
: String
1036 var lines
: List[String] = new List[String]
1040 # A Java compatible name for `self`
1041 private fun jname
: String do return name
.to_cmangle
1044 # Handler for runtime classes generation
1046 # We need 3 kinds of runtime structures:
1047 # * `RTClass` to represent a global class
1048 # * `RTMethod` to represent a method definition
1049 # * `RTVal` to represent runtime variables
1050 class JavaRuntimeModel
1052 # Compile JavaRuntimeModel structures
1053 fun compile_rtmodel
(compiler
: JavaCompiler) do
1054 compile_rtclass
(compiler
)
1055 compile_rtmethod
(compiler
)
1056 compile_rtval
(compiler
)
1059 # Compile the abstract runtime class structure
1061 # Runtime classes have 3 attributes:
1062 # * `class_name`: the class name as a String
1063 # * `vft`: the virtual function table for the class (flattened)
1064 # * `supers`: the super type table (used for type tests)
1065 fun compile_rtclass
(compiler
: JavaCompiler) do
1066 var v
= compiler
.new_visitor
("RTClass.java")
1067 v
.add
("import java.util.HashMap;")
1068 v
.add
("public abstract class RTClass \{")
1069 v
.add
(" public String class_name;")
1070 v
.add
(" public HashMap<String, RTMethod> vft = new HashMap<>();")
1071 v
.add
(" public HashMap<String, RTClass> supers = new HashMap<>();")
1072 v
.add
(" protected RTClass() \{\}")
1076 # Compile the abstract runtime method structure
1078 # Method body is executed through the `exec` method:
1079 # * `exec` always take an array of RTVal as arg, the first one must be the receiver
1080 # * `exec` always returns a RTVal (or null if the Nit return type is void)
1081 fun compile_rtmethod
(compiler
: JavaCompiler) do
1082 var v
= compiler
.new_visitor
("RTMethod.java")
1083 v
.add
("public abstract class RTMethod \{")
1084 v
.add
(" protected RTMethod() \{\}")
1085 v
.add
(" public abstract RTVal exec(RTVal[] args);")
1089 # Compile the runtime value structure
1091 # RTVal both represents object instances and primitives values:
1092 # * object instances:
1093 # * `rtclass` the class of the RTVal is instance of
1094 # * `attrs` contains the attributes of the instance
1095 # * primitive values:
1096 # * `rtclass` represents the class of the primitive value Nit type
1097 # * `value` contains the primitive value of the instance
1099 # * they must have both `rtclass` and `value` as null
1100 fun compile_rtval
(compiler
: JavaCompiler) do
1101 var v
= compiler
.new_visitor
("RTVal.java")
1102 v
.add
("import java.util.HashMap;")
1103 v
.add
("public class RTVal \{")
1104 v
.add
(" public RTClass rtclass;")
1105 v
.add
(" public HashMap<String, RTVal> attrs = new HashMap<>();")
1106 v
.add
(" Object value;")
1107 v
.add
(" public RTVal(RTClass rtclass) \{")
1108 v
.add
(" this.rtclass = rtclass;")
1110 v
.add
(" public RTVal(RTClass rtclass, Object value) \{")
1111 v
.add
(" this.rtclass = rtclass;")
1112 v
.add
(" this.value = value;")
1114 v
.add
(" public boolean is_null() \{ return rtclass == null && value == null; \}")
1119 # A runtime variable hold a runtime value in Java.
1120 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
1121 class RuntimeVariable
1123 # The name of the variable in the Java code
1126 # The static type of the variable (as declard in Java)
1129 # The current casted type of the variable (as known in Nit)
1130 var mcasttype
: MType is writable
1132 # If the variable exaclty a mcasttype?
1133 # false (usual value) means that the variable is a mcasttype or a subtype.
1134 var is_exact
: Bool = false is writable
1136 # Is this variable declared as a RTVal or a Java primitive one?
1137 var is_boxed
= false
1139 redef fun to_s
do return name
1144 if self.is_exact
then
1145 exact_str
= " exact"
1150 if self.mtype
== self.mcasttype
then
1151 type_str
= "{mtype}{exact_str}"
1153 type_str
= "{mtype}({mcasttype}{exact_str})"
1155 return "<{name}:{type_str}>"
1159 # The static context of a visited property in a `JavaCompilerVisitor`
1160 class JavaStaticFrame
1161 # The associated visitor
1162 var visitor
: JavaCompilerVisitor
1164 # The executed property.
1165 # A Method in case of a call, an attribute in case of a default initialization.
1166 var mpropdef
: MPropDef
1168 # The static type of the receiver
1169 var receiver
: MClassType
1171 # Arguments of the method (the first is the receiver)
1172 var arguments
: Array[RuntimeVariable]
1174 # The runtime_variable associated to the return (in a function)
1175 var returnvar
: nullable RuntimeVariable = null is writable
1177 # The label at the end of the property
1178 var returnlabel
: nullable String = null is writable
1180 # Labels associated to a each escapemarks.
1181 # Because of inlinings, escape-marks must be associated to their context (the frame)
1182 private var escapemark_names
= new HashMap[EscapeMark, String]
1185 redef class Location
1186 # Return a shortened version of the location with `"{file}:{line_start}"`
1187 fun short_location
: String do
1188 var file
= self.file
1189 if file
== null then return "<no file>:{line_start}"
1190 return "{file.filename.escape_to_c}:{line_start}"
1195 # Return the Java type associated to a given Nit static type
1196 fun java_type
: String do return "RTVal"
1198 # Is the associated Java type a primitive one?
1200 # ENSURE `result == (java_type != "Object")`
1201 var is_java_primitive
: Bool is lazy
do return java_type
!= "RTVal"
1204 redef class MClassType
1206 redef var java_type
is lazy
do
1207 if mclass
.name
== "Int" then
1209 else if mclass
.name
== "Bool" then
1211 else if mclass
.name
== "Char" then
1213 else if mclass
.name
== "Float" then
1215 else if mclass
.name
== "Byte" then
1217 else if mclass
.name
== "NativeString" then
1219 else if mclass
.name
== "NativeArray" then
1229 private fun rt_name
: String do return "RTClass_{intro.mmodule.jname}_{jname}"
1231 # Generate a Java RTClass for a Nit MClass
1232 fun compile_to_java
(v
: JavaCompilerVisitor) do
1233 v
.add
("public class {rt_name} extends RTClass \{")
1234 v
.add
(" protected static RTClass instance;")
1235 v
.add
(" private {rt_name}() \{")
1236 v
.add
(" this.class_name = \"{name}\
";")
1238 compile_type_table
(v
)
1240 v
.add
(" public static RTClass get{rt_name}() \{")
1241 v
.add
(" if(instance == null) \{")
1242 v
.add
(" instance = new {rt_name}();")
1244 v
.add
(" return instance;")
1249 # Compile the virtual function table for the mclass
1250 private fun compile_vft
(v
: JavaCompilerVisitor) do
1251 # TODO handle generics
1252 if mclass_type
.need_anchor
then return
1253 var mclassdefs
= mclass_type
.collect_mclassdefs
(v
.compiler
.mainmodule
).to_a
1254 v
.compiler
.mainmodule
.linearize_mclassdefs
(mclassdefs
)
1256 var mainmodule
= v
.compiler
.mainmodule
1257 for mclassdef
in mclassdefs
.reversed
do
1258 for mprop
in mclassdef
.intro_mproperties
do
1259 var mpropdef
= mprop
.lookup_first_definition
(mainmodule
, intro
.bound_mtype
)
1260 if not mpropdef
isa MMethodDef then continue
1261 var rt_name
= mpropdef
.rt_name
1262 v
.add
("this.vft.put(\"{mprop.full_name}\
", {rt_name}.get{rt_name}());")
1264 # fill super next definitions
1265 while mpropdef
.has_supercall
do
1266 var prefix
= mpropdef
.full_name
1267 mpropdef
= mpropdef
.lookup_next_definition
(mainmodule
, intro
.bound_mtype
)
1268 rt_name
= mpropdef
.rt_name
1269 v
.add
("this.vft.put(\"{prefix}\
", {rt_name}.get{rt_name}());")
1275 # Compile the type table for the MClass
1276 fun compile_type_table
(v
: JavaCompilerVisitor) do
1277 for pclass
in in_hierarchy
(v
.compiler
.mainmodule
).greaters
do
1278 if pclass
== self then
1279 v
.add
("supers.put(\"{pclass.jname}\
", this);")
1281 v
.add
("supers.put(\"{pclass.jname}\
", {pclass.rt_name}.get{pclass.rt_name}());")
1287 # Used as a common type between MMethod and MMethodDef for `table_send`
1288 private interface TableCallable
1295 redef class MMethodDef
1299 private fun rt_name
: String do
1300 return "RTMethod_{mclassdef.mmodule.jname}_{mclassdef.mclass.jname}_{mproperty.jname}"
1303 # Generate a Java RTMethod for `self`
1304 fun compile_to_java
(v
: JavaCompilerVisitor) do
1305 v
.add
("public class {rt_name} extends RTMethod \{")
1306 v
.add
(" protected static RTMethod instance;")
1307 v
.add
(" public static RTMethod get{rt_name}() \{")
1308 v
.add
(" if(instance == null) \{")
1309 v
.add
(" instance = new {rt_name}();")
1311 v
.add
(" return instance;")
1314 v
.add
(" public RTVal exec(RTVal[] args) \{")
1315 compile_inside_to_java
(v
)
1320 # Compile the body of this function
1321 fun compile_inside_to_java
(v
: JavaCompilerVisitor) do
1323 var modelbuilder
= v
.compiler
.modelbuilder
1324 var node
= modelbuilder
.mpropdef2node
(self)
1327 v
.add_abort
("Abstract method `{mproperty.name}` called on `\" + {selfvar}.rtclass
.class_name
+\
"`")
1328 v
.add
("return null;")
1332 if node
isa APropdef then
1333 node
.compile_to_java
(v
, self)
1334 else if node
isa AClassdef then
1335 node
.compile_to_java
(v
, self)
1342 redef class AClassdef
1343 private fun compile_to_java
(v
: JavaCompilerVisitor, mpropdef
: MMethodDef) do
1344 if mpropdef
== self.mfree_init
then
1345 var recv
= mpropdef
.mclassdef
.bound_mtype
1346 var arguments
= new Array[RuntimeVariable]
1347 var frame
= new JavaStaticFrame(v
, mpropdef
, recv
, arguments
)
1350 var selfvar
= v
.decl_var
("self", recv
)
1351 arguments
.add
(selfvar
)
1352 var boxed
= v
.new_expr
("args[0];", v
.compiler
.mainmodule
.object_type
)
1353 v
.add
"{selfvar} = {v.unbox(boxed, recv)};"
1355 var msignature
= mpropdef
.msignature
1357 if msignature
!= null then
1358 ret
= msignature
.return_mtype
1359 if ret
!= null then frame
.returnvar
= v
.new_var
(ret
)
1361 frame
.returnlabel
= v
.get_name
("RET_LABEL")
1363 assert mpropdef
.mproperty
.is_root_init
1364 if not mpropdef
.is_intro
then
1365 v
.supercall
(mpropdef
, arguments
.first
.mtype
.as(MClassType), arguments
)
1370 v
.add
("return null;")
1374 redef class APropdef
1376 # Compile that property definition to java code
1377 fun compile_to_java
(v
: JavaCompilerVisitor, mpropdef
: MMethodDef) do
1378 v
.info
("NOT YET IMPLEMENTED {class_name}::compile_to_java")
1379 v
.add
("return null;")
1383 redef class AMethPropdef
1384 redef fun compile_to_java
(v
, mpropdef
) do
1385 var recv
= mpropdef
.mclassdef
.bound_mtype
1386 var arguments
= new Array[RuntimeVariable]
1387 var frame
= new JavaStaticFrame(v
, mpropdef
, recv
, arguments
)
1390 var selfvar
= v
.decl_var
("self", recv
)
1391 arguments
.add
(selfvar
)
1392 var boxed
= v
.new_expr
("args[0];", v
.compiler
.mainmodule
.object_type
)
1393 v
.add
"{selfvar} = {v.unbox(boxed, recv)};"
1395 var msignature
= mpropdef
.msignature
1397 if msignature
!= null then
1398 ret
= msignature
.return_mtype
1399 if ret
!= null then frame
.returnvar
= v
.new_var
(ret
)
1401 frame
.returnlabel
= v
.get_name
("RET_LABEL")
1403 if not mpropdef
.is_intern
and msignature
!= null then
1405 for mparam
in msignature
.mparameters
do
1406 var variable
= n_signature
.as(not null).n_params
[i
].variable
1407 if variable
== null then continue
1408 var argvar
= v
.variable
(variable
)
1409 boxed
= v
.new_expr
("args[{i + 1}];", v
.compiler
.mainmodule
.object_type
)
1410 v
.add
"{argvar} = {v.unbox(boxed, mparam.mtype)};"
1411 arguments
.add
(argvar
)
1416 v
.add
("{frame.returnlabel.as(not null)}: \{")
1418 # Call the implicit super-init
1419 var auto_super_inits
= self.auto_super_inits
1420 if auto_super_inits
!= null then
1421 var args
= [arguments
.first
]
1422 for auto_super_init
in auto_super_inits
do
1423 assert auto_super_init
.mproperty
!= mpropdef
.mproperty
1425 for i
in [0..auto_super_init
.msignature
.arity
+1[ do
1426 args
.add
(arguments
[i
])
1428 assert auto_super_init
.mproperty
!= mpropdef
.mproperty
1429 v
.compile_callsite
(auto_super_init
, args
)
1432 if auto_super_call
then
1433 v
.supercall
(mpropdef
, arguments
.first
.mtype
.as(MClassType), arguments
)
1436 compile_inside_to_java
(v
, mpropdef
)
1440 if ret
.is_java_primitive
then
1441 boxed
= v
.box
(frame
.returnvar
.as(not null), v
.compiler
.mainmodule
.object_type
)
1442 v
.add
("return {boxed};")
1444 v
.add
("return {frame.returnvar.as(not null)};")
1447 v
.add
("return null;")
1452 # Compile the inside of the method body
1453 private fun compile_inside_to_java
(v
: JavaCompilerVisitor, mpropdef
: MMethodDef) do
1454 # Compile intern methods
1455 if mpropdef
.is_intern
then
1456 if compile_intern_to_java
(v
, mpropdef
, arguments
) then return
1457 v
.info
("NOT YET IMPLEMENTED compile_intern for {mpropdef}")
1458 v
.ret
(v
.null_instance
)
1462 # Compile block if any
1463 var n_block
= n_block
1464 if n_block
!= null then
1470 # Compile an intern method using Java primitives
1471 fun compile_intern_to_java
(v
: JavaCompilerVisitor, mpropdef
: MMethodDef, arguments
: Array[RuntimeVariable]): Bool do
1472 var pname
= mpropdef
.mproperty
.name
1473 var cname
= mpropdef
.mclassdef
.mclass
.name
1474 var ret
= mpropdef
.msignature
.as(not null).return_mtype
1475 if cname
== "Int" then
1476 if pname
== "output" then
1477 v
.add
("System.out.println({arguments[0]});")
1478 v
.ret
(v
.null_instance
)
1480 else if pname
== "object_id" then
1481 v
.ret
(arguments
.first
)
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
== "*" then
1496 v
.ret
(v
.new_expr
("{arguments[0]} * {arguments[1]}", ret
.as(not null)))
1498 else if pname
== "/" then
1499 v
.ret
(v
.new_expr
("{arguments[0]} / {arguments[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
== "lshift" then
1505 v
.ret
(v
.new_expr
("{arguments[0]} << {arguments[1]}", ret
.as(not null)))
1507 else if pname
== "rshift" then
1508 v
.ret
(v
.new_expr
("{arguments[0]} >> {arguments[1]}", ret
.as(not null)))
1510 else if pname
== "==" then
1511 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
1513 else if pname
== "!=" then
1514 var res
= v
.equal_test
(arguments
[0], arguments
[1])
1515 v
.ret
(v
.new_expr
("!{res}", 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
== ">=" then
1527 v
.ret
(v
.new_expr
("{arguments[0]} >= {arguments[1]}", ret
.as(not null)))
1529 else if pname
== "to_f" then
1530 v
.ret
(v
.new_expr
("(double){arguments[0]}", ret
.as(not null)))
1532 else if pname
== "to_b" then
1533 v
.ret
(v
.new_expr
("(byte){arguments[0]}", ret
.as(not null)))
1535 else if pname
== "ascii" then
1536 v
.ret
(v
.new_expr
("(char){arguments[0]}", ret
.as(not null)))
1539 else if cname
== "Char" then
1540 if pname
== "output" then
1541 v
.add
("System.out.print({arguments[0]});")
1542 v
.ret
(v
.null_instance
)
1544 else if pname
== "object_id" then
1545 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1547 else if pname
== "successor" then
1548 v
.ret
(v
.new_expr
("(char)({arguments[0]} + {arguments[1]})", ret
.as(not null)))
1550 else if pname
== "predecessor" then
1551 v
.ret
(v
.new_expr
("(char)({arguments[0]} - {arguments[1]})", ret
.as(not null)))
1553 else if pname
== "==" then
1554 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
1556 else if pname
== "!=" then
1557 var res
= v
.equal_test
(arguments
[0], arguments
[1])
1558 v
.ret
(v
.new_expr
("!{res}", ret
.as(not null)))
1560 else if pname
== "<" then
1561 v
.ret
(v
.new_expr
("{arguments[0]} < {arguments[1]}", ret
.as(not null)))
1563 else if pname
== ">" then
1564 v
.ret
(v
.new_expr
("{arguments[0]} > {arguments[1]}", ret
.as(not null)))
1566 else if pname
== "<=" then
1567 v
.ret
(v
.new_expr
("{arguments[0]} <= {arguments[1]}", ret
.as(not null)))
1569 else if pname
== ">=" then
1570 v
.ret
(v
.new_expr
("{arguments[0]} >= {arguments[1]}", ret
.as(not null)))
1572 else if pname
== "to_i" then
1573 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1575 else if pname
== "ascii" then
1576 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1579 else if cname
== "Byte" then
1580 if pname
== "output" then
1581 v
.add
("System.out.println({arguments[0]});")
1582 v
.ret
(v
.null_instance
)
1584 else if pname
== "object_id" then
1585 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1587 else if pname
== "+" then
1588 v
.ret
(v
.new_expr
("(byte)({arguments[0]} + {arguments[1]})", ret
.as(not null)))
1590 else if pname
== "-" then
1591 v
.ret
(v
.new_expr
("(byte)({arguments[0]} - {arguments[1]})", ret
.as(not null)))
1593 else if pname
== "unary -" then
1594 v
.ret
(v
.new_expr
("(byte)(-{arguments[0]})", ret
.as(not null)))
1596 else if pname
== "unary +" then
1599 else if pname
== "*" then
1600 v
.ret
(v
.new_expr
("(byte)({arguments[0]} * {arguments[1]})", ret
.as(not null)))
1602 else if pname
== "/" then
1603 v
.ret
(v
.new_expr
("(byte)({arguments[0]} / {arguments[1]})", ret
.as(not null)))
1605 else if pname
== "%" then
1606 v
.ret
(v
.new_expr
("(byte)({arguments[0]} % {arguments[1]})", ret
.as(not null)))
1608 else if pname
== "lshift" then
1609 v
.ret
(v
.new_expr
("(byte)({arguments[0]} << {arguments[1]})", ret
.as(not null)))
1611 else if pname
== "rshift" then
1612 v
.ret
(v
.new_expr
("(byte)({arguments[0]} >> {arguments[1]})", ret
.as(not null)))
1614 else if pname
== "==" then
1615 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
1617 else if pname
== "!=" then
1618 var res
= v
.equal_test
(arguments
[0], arguments
[1])
1619 v
.ret
(v
.new_expr
("!{res}", ret
.as(not null)))
1621 else if pname
== "<" then
1622 v
.ret
(v
.new_expr
("{arguments[0]} < {arguments[1]}", ret
.as(not null)))
1624 else if pname
== ">" then
1625 v
.ret
(v
.new_expr
("{arguments[0]} > {arguments[1]}", ret
.as(not null)))
1627 else if pname
== "<=" then
1628 v
.ret
(v
.new_expr
("{arguments[0]} <= {arguments[1]}", ret
.as(not null)))
1630 else if pname
== ">=" then
1631 v
.ret
(v
.new_expr
("{arguments[0]} >= {arguments[1]}", ret
.as(not null)))
1633 else if pname
== "to_i" then
1634 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1636 else if pname
== "to_f" then
1637 v
.ret
(v
.new_expr
("(double){arguments[0]}", ret
.as(not null)))
1639 else if pname
== "ascii" then
1640 v
.ret
(v
.new_expr
("{arguments[0]}", ret
.as(not null)))
1643 else if cname
== "Bool" then
1644 if pname
== "output" then
1645 v
.add
("System.out.println({arguments[0]});")
1646 v
.ret
(v
.null_instance
)
1648 else if pname
== "object_id" then
1649 v
.ret
(v
.new_expr
("{arguments[0]}?1:0", ret
.as(not null)))
1651 else if pname
== "==" then
1652 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
1654 else if pname
== "!=" then
1655 var res
= v
.equal_test
(arguments
[0], arguments
[1])
1656 v
.ret
(v
.new_expr
("!{res}", ret
.as(not null)))
1659 else if cname
== "Float" then
1660 if pname
== "output" then
1661 v
.add
"if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
1662 v
.add
"System.out.println(\"inf\
");"
1663 v
.add
"\} else if({arguments[0]} == Double.POSITIVE_INFINITY) \{"
1664 v
.add
"System.out.println(\"-inf\
");"
1666 var df
= v
.get_name
("df")
1667 v
.add
"java.text.DecimalFormat {df} = new java.text.DecimalFormat(\"0.000000\
");"
1668 v
.add
"System.out.println({df}.format({arguments[0]}));"
1670 v
.ret
(v
.null_instance
)
1672 else if pname
== "object_id" then
1673 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1675 else if pname
== "+" then
1676 v
.ret
(v
.new_expr
("{arguments[0]} + {arguments[1]}", ret
.as(not null)))
1678 else if pname
== "-" then
1679 v
.ret
(v
.new_expr
("{arguments[0]} - {arguments[1]}", ret
.as(not null)))
1681 else if pname
== "unary -" then
1682 v
.ret
(v
.new_expr
("-{arguments[0]}", ret
.as(not null)))
1684 else if pname
== "unary +" then
1687 else if pname
== "succ" then
1688 v
.ret
(v
.new_expr
("{arguments[0]} + 1", ret
.as(not null)))
1690 else if pname
== "prec" then
1691 v
.ret
(v
.new_expr
("{arguments[0]} - 1", ret
.as(not null)))
1693 else if pname
== "*" then
1694 v
.ret
(v
.new_expr
("{arguments[0]} * {arguments[1]}", ret
.as(not null)))
1696 else if pname
== "/" then
1697 v
.ret
(v
.new_expr
("{arguments[0]} / {arguments[1]}", ret
.as(not null)))
1699 else if pname
== "==" then
1700 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
1702 else if pname
== "!=" then
1703 var res
= v
.equal_test
(arguments
[0], arguments
[1])
1704 v
.ret
(v
.new_expr
("!{res}", ret
.as(not null)))
1706 else if pname
== "<" then
1707 v
.ret
(v
.new_expr
("{arguments[0]} < {arguments[1]}", ret
.as(not null)))
1709 else if pname
== ">" then
1710 v
.ret
(v
.new_expr
("{arguments[0]} > {arguments[1]}", ret
.as(not null)))
1712 else if pname
== "<=" then
1713 v
.ret
(v
.new_expr
("{arguments[0]} <= {arguments[1]}", ret
.as(not null)))
1715 else if pname
== ">=" then
1716 v
.ret
(v
.new_expr
("{arguments[0]} >= {arguments[1]}", ret
.as(not null)))
1718 else if pname
== "to_i" then
1719 v
.ret
(v
.new_expr
("(int){arguments[0]}", ret
.as(not null)))
1721 else if pname
== "to_b" then
1722 v
.ret
(v
.new_expr
("(byte){arguments[0]}", ret
.as(not null)))
1726 if pname
== "exit" then
1727 v
.add
("System.exit({arguments[1]});")
1728 v
.ret
(v
.null_instance
)
1730 else if pname
== "sys" then
1732 var main_type
= v
.compiler
.mainmodule
.sys_type
.as(not null)
1733 var sys
= main_type
.mclass
1734 v
.ret
(v
.new_expr
("new RTVal({sys.rt_name}.get{sys.rt_name}())", main_type
))
1736 else if pname
== "object_id" then
1737 v
.ret
(v
.new_expr
("{arguments[0]}.hashCode()", ret
.as(not null)))
1739 else if pname
== "is_same_type" then
1740 v
.ret
(v
.is_same_type_test
(arguments
[0], arguments
[1]))
1742 else if pname
== "is_same_instance" then
1743 v
.ret
(v
.equal_test
(arguments
[0], arguments
[1]))
1745 else if pname
== "output_class_name" then
1746 v
.add
("System.out.println({arguments[0]}.rtclass.class_name);")
1747 v
.ret
(v
.null_instance
)
1754 redef class AAttrPropdef
1755 redef fun compile_to_java
(v
, mpropdef
, arguments
) do
1756 v
.current_node
= self
1757 if mpropdef
== mreadpropdef
then
1758 compile_getter
(v
, mpropdef
, arguments
)
1759 else if mpropdef
== mwritepropdef
then
1760 compile_setter
(v
, mpropdef
, arguments
)
1764 v
.current_node
= null
1767 # Compile the setter method
1768 private fun compile_setter
(v
: JavaCompilerVisitor, mpropdef
: MPropDef, arguments
: Array[RuntimeVariable]) do
1769 var mtype
= v
.compiler
.mainmodule
.object_type
1770 var recv
= arguments
.first
1771 var val
= v
.new_expr
("args[1]", mtype
)
1772 v
.write_attribute
(self.mpropdef
.as(not null).mproperty
, recv
, val
)
1773 v
.ret v
.null_instance
1776 # Compile the getter method
1777 private fun compile_getter
(v
: JavaCompilerVisitor, mpropdef
: MPropDef, arguments
: Array[RuntimeVariable]) do
1778 var recv
= arguments
.first
1779 v
.ret v
.read_attribute
(self.mpropdef
.as(not null).mproperty
, recv
)
1782 private fun init_expr
(v
: JavaCompilerVisitor, recv
: RuntimeVariable) do
1783 if has_value
and not is_lazy
and not n_expr
isa ANullExpr then evaluate_expr
(v
, recv
)
1786 # Evaluate, store and return the default value of the attribute
1787 private fun evaluate_expr
(v
: JavaCompilerVisitor, recv
: RuntimeVariable): RuntimeVariable do
1789 var frame
= new JavaStaticFrame(v
, self.mreadpropdef
.as(not null), recv
.mcasttype
.undecorate
.as(MClassType), [recv
])
1793 var mtype
= self.mtype
1794 assert mtype
!= null
1796 var nexpr
= self.n_expr
1797 var nblock
= self.n_block
1798 if nexpr
!= null then
1799 value
= v
.expr
(nexpr
, mtype
)
1800 else if nblock
!= null then
1801 value
= v
.new_var
(mtype
)
1802 frame
.returnvar
= value
1803 frame
.returnlabel
= v
.get_name
("RET_LABEL")
1804 v
.add
("{frame.returnlabel.as(not null)}: \{")
1811 v
.write_attribute
(self.mpropdef
.as(not null).mproperty
, recv
, value
)
1818 # Try to compile self as an expression
1819 # Do not call this method directly, use `v.expr` instead
1820 private fun expr
(v
: JavaCompilerVisitor): nullable RuntimeVariable do
1821 v
.info
("NOT YET IMPLEMENTED {class_name}::expr")
1825 # Try to compile self as a statement
1826 # Do not call this method directly, use `v.stmt` instead
1827 private fun stmt
(v
: JavaCompilerVisitor) do expr
(v
)
1830 redef class ABlockExpr
1833 for e
in self.n_expr
do v
.stmt
(e
)
1837 var last
= self.n_expr
.last
1838 for e
in self.n_expr
do
1839 if e
== last
then break
1842 return v
.expr
(last
, null)
1846 redef class ASendExpr
1847 redef fun expr
(v
) do
1848 var recv
= v
.expr
(n_expr
, null)
1849 var callsite
= callsite
.as(not null)
1850 var args
= v
.varargize
(callsite
.mpropdef
, callsite
.signaturemap
, recv
, raw_arguments
)
1851 return v
.compile_callsite
(callsite
, args
)
1855 redef class ANewExpr
1858 var mtype
= self.recvtype
1859 assert mtype
!= null
1861 if mtype
.mclass
.name
== "NativeArray" then
1862 # TODO handle native arrays
1863 v
.info
("NOT YET IMPLEMENTED new NativeArray")
1866 var recv
= v
.init_instance
(mtype
)
1868 var callsite
= self.callsite
1869 if callsite
== null then return recv
1871 var args
= v
.varargize
(callsite
.mpropdef
, callsite
.signaturemap
, recv
, self.n_args
.n_exprs
)
1872 var res2
= v
.compile_callsite
(callsite
, args
)
1873 if res2
!= null then
1880 redef class ASuperExpr
1884 assert frame
!= null
1885 var recv
= frame
.arguments
.first
1887 var callsite
= self.callsite
1888 if callsite
!= null then
1891 if self.n_args
.n_exprs
.is_empty
then
1892 # Add automatic arguments for the super init call
1894 for i
in [0..callsite
.msignature
.arity
[ do
1895 args
.add
(frame
.arguments
[i
+1])
1898 args
= v
.varargize
(callsite
.mpropdef
, callsite
.signaturemap
, recv
, self.n_args
.n_exprs
)
1902 var res
= v
.compile_callsite
(callsite
, args
)
1906 var mpropdef
= self.mpropdef
.as(not null)
1909 if self.n_args
.n_exprs
.is_empty
then
1910 args
= frame
.arguments
1912 args
= v
.varargize
(mpropdef
, signaturemap
, recv
, self.n_args
.n_exprs
)
1915 # Standard call-next-method
1916 return v
.supercall
(mpropdef
, recv
.mtype
.as(MClassType), args
)
1920 redef class ASelfExpr
1921 redef fun expr
(v
) do return v
.frame
.as(not null).arguments
.first
1924 redef class AImplicitSelfExpr
1925 redef fun expr
(v
) do return v
.frame
.as(not null).arguments
.first
1928 redef class AAttrExpr
1929 redef fun expr
(v
) do
1930 var recv
= v
.expr
(self.n_expr
, null)
1931 var mproperty
= self.mproperty
.as(not null)
1932 return v
.read_attribute
(mproperty
, recv
)
1936 redef class AAttrAssignExpr
1937 redef fun expr
(v
) do
1938 var recv
= v
.expr
(self.n_expr
, null)
1939 var i
= v
.expr
(self.n_value
, null)
1940 var mproperty
= self.mproperty
.as(not null)
1941 v
.write_attribute
(mproperty
, recv
, i
)
1946 redef class AAttrReassignExpr
1947 redef fun stmt
(v
) do
1948 var recv
= v
.expr
(self.n_expr
, null)
1949 var value
= v
.expr
(self.n_value
, null)
1950 var mproperty
= self.mproperty
.as(not null)
1951 var attr
= v
.read_attribute
(mproperty
, recv
)
1952 var res
= v
.compile_callsite
(self.reassign_callsite
.as(not null), [attr
, value
])
1954 v
.write_attribute
(mproperty
, recv
, res
)
1958 redef class AIssetAttrExpr
1959 redef fun expr
(v
) do
1960 var recv
= v
.expr
(self.n_expr
, null)
1961 var mproperty
= self.mproperty
.as(not null)
1962 return v
.isset_attribute
(mproperty
, recv
)
1966 redef class AReturnExpr
1967 redef fun stmt
(v
) do
1968 var nexpr
= self.n_expr
1970 assert frame
!= null
1971 if nexpr
!= null then
1972 v
.ret
(v
.expr
(nexpr
, frame
.returnvar
.as(not null).mtype
))
1974 v
.ret
(v
.null_instance
)
1980 redef fun stmt
(v
) do
1981 var cond
= v
.expr_bool
(self.n_expr
)
1982 v
.add
("if ({cond})\{")
1989 redef fun expr
(v
) do
1990 var res
= v
.new_var
(self.mtype
.as(not null))
1991 var cond
= v
.expr_bool
(self.n_expr
)
1992 v
.add
("if ({cond})\{")
1993 v
.assign
(res
, v
.expr
(self.n_then
.as(not null), null))
1995 v
.assign
(res
, v
.expr
(self.n_else
.as(not null), null))
2004 v
.add_escape_label
(break_mark
)
2006 v
.stmt
(self.n_block
)
2011 redef class AWhileExpr
2014 v
.add_escape_label
(break_mark
)
2015 v
.add_escape_label
(continue_mark
)
2017 var cond
= v
.expr_bool
(self.n_expr
)
2018 v
.add
("if (!{cond}) break;")
2019 v
.stmt
(self.n_block
)
2024 redef class ALoopExpr
2027 v
.add_escape_label
(break_mark
)
2028 v
.add_escape_label
(continue_mark
)
2030 v
.stmt
(self.n_block
)
2035 redef class AEscapeExpr
2036 redef fun stmt
(v
) do v
.add
("break BREAK_{v.escapemark_name(escapemark)};")
2039 redef class AVardeclExpr
2040 redef fun stmt
(v
) do
2041 var variable
= self.variable
.as(not null)
2042 var ne
= self.n_expr
2043 var decl
= v
.variable
(variable
)
2045 var i
= v
.expr
(ne
, variable
.declared_type
)
2051 redef class AVarExpr
2052 redef fun expr
(v
) do
2053 return v
.variable
(self.variable
.as(not null))
2057 redef class AVarAssignExpr
2058 redef fun expr
(v
) do
2059 var variable
= self.variable
.as(not null)
2060 var i
= v
.expr
(self.n_value
, variable
.declared_type
)
2061 v
.assign
(v
.variable
(variable
), i
)
2067 redef class ANotExpr
2068 redef fun expr
(v
) do
2069 var cond
= v
.expr_bool
(self.n_expr
)
2070 return v
.new_expr
("!{cond}", self.mtype
.as(not null))
2074 redef class AIntExpr
2075 redef fun expr
(v
) do return v
.int_instance
(self.value
.as(not null))
2078 redef class AByteExpr
2079 redef fun expr
(v
) do return v
.byte_instance
(self.value
.as(not null))
2082 redef class AFloatExpr
2083 redef fun expr
(v
) do return v
.float_instance
("{self.n_float.text}") # FIXME use value, not n_float
2086 redef class ACharExpr
2087 redef fun expr
(v
) do return v
.char_instance
(self.value
.as(not null))
2090 redef class ATrueExpr
2091 redef fun expr
(v
) do return v
.bool_instance
(true)
2094 redef class AFalseExpr
2095 redef fun expr
(v
) do return v
.bool_instance
(false)
2098 redef class ANullExpr
2099 redef fun expr
(v
) do return v
.null_instance
2102 redef class AIsaExpr
2105 var i
= v
.expr
(self.n_expr
, null)
2106 var cast_type
= self.cast_type
2107 if cast_type
== null then return null # no-no on broken node
2108 return v
.type_test
(i
, cast_type
)
2112 redef class AParExpr
2113 redef fun expr
(v
) do return v
.expr
(self.n_expr
, null)
2116 redef class AAbortExpr
2117 redef fun stmt
(v
) do v
.add_abort
("Aborted")
2120 redef class ADebugTypeExpr
2121 redef fun stmt
(v
) do end # do nothing