fun do_compilation do
# compile java classes used to represents the runtime model of the program
rt_model.compile_rtmodel(self)
+ compile_box_kinds
# compile class structures
compile_mclasses_to_java
modelbuilder.toolcontext.info("NOT YET IMPLEMENTED", 0)
end
+ # Prepare the boxes used to represent Java primitive types
+ fun compile_box_kinds do
+ # Collect all bas box class
+ # FIXME: this is not completely fine with a separate compilation scheme
+ for classname in ["Int", "Bool", "Byte", "Char", "Float"] do
+ var classes = mainmodule.model.get_mclasses_by_name(classname)
+ if classes == null then continue
+ assert classes.length == 1 else print classes.join(", ")
+ box_kinds.add(classes.first.mclass_type)
+ end
+ end
+
+ # Types of boxes used to represent Java primitive types
+ var box_kinds = new Array[MClassType]
+
# Generate a `RTClass` for each `MClass` found in model
#
# This is a global phase because we need to know all the program to build
else
var name = get_name("var_{variable.name}")
var mtype = variable.declared_type.as(not null)
- # TODO mtype = self.anchor(mtype)
+ mtype = anchor(mtype)
var res = decl_var(name, mtype)
variables[variable] = res
return res
# Return a new uninitialized local RuntimeVariable with `name`
fun decl_var(name: String, mtype: MType): RuntimeVariable do
var res = new RuntimeVariable(name, mtype, mtype)
+ res.is_boxed = not mtype.is_java_primitive
add("{mtype.java_type} {name} /* : {mtype} */;")
return res
end
# Return a new uninitialized local RuntimeVariable
fun new_var(mtype: MType): RuntimeVariable do
- # TODO mtype = self.anchor(mtype)
+ mtype = anchor(mtype)
var name = self.get_name("var")
return decl_var(name, mtype)
end
return res
end
+ # Calls handling
+
+ # Compile a call within a callsite
+ fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
+ var initializers = callsite.mpropdef.initializers
+ if not initializers.is_empty then
+ var recv = arguments.first
+
+ var i = 1
+ for p in initializers do
+ if p isa MMethod then
+ var args = [recv]
+ var msignature = p.intro.msignature
+ if msignature != null then
+ for x in msignature.mparameters do
+ args.add arguments[i]
+ i += 1
+ end
+ end
+ send(p, args)
+ else if p isa MAttribute then
+ info("NOT YET IMPLEMENTED {class_name}::compile_callsite for MAttribute `{p}`")
+ #self.write_attribute(p, recv, arguments[i])
+ i += 1
+ else abort
+ end
+ assert i == arguments.length
+
+ return send(callsite.mproperty, [recv])
+ end
+
+ return send(callsite.mproperty, arguments)
+ end
+
+ # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
+ #
+ # This method is used to manage varargs in signatures and returns the real array
+ # of runtime variables to use in the call.
+ fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable] do
+ var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+ var res = new Array[RuntimeVariable]
+ res.add(recv)
+
+ if msignature.arity == 0 then return res
+
+ if map == null then
+ assert args.length == msignature.arity
+ for ne in args do
+ res.add expr(ne, null)
+ end
+ return res
+ end
+
+ # Eval in order of arguments, not parameters
+ var exprs = new Array[RuntimeVariable].with_capacity(args.length)
+ for ne in args do
+ exprs.add expr(ne, null)
+ end
+
+ # Fill `res` with the result of the evaluation according to the mapping
+ for i in [0..msignature.arity[ do
+ var param = msignature.mparameters[i]
+ var j = map.map.get_or_null(i)
+ if j == null then
+ # default value
+ res.add(null_instance)
+ continue
+ end
+ if param.is_vararg and map.vararg_decl > 0 then
+ var vararg = exprs.sub(j, map.vararg_decl)
+ var elttype = param.mtype
+ var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
+ res.add(arg)
+ continue
+ end
+ res.add exprs[j]
+ end
+ return res
+ end
+
+ # Generate a static call on a method definition (no receiver needed).
+ fun static_call(mmethoddef: MMethodDef, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
+ var res: nullable RuntimeVariable
+ var ret = mmethoddef.msignature.as(not null).return_mtype
+ if ret == null then
+ res = null
+ else
+ ret = ret.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
+ res = self.new_var(ret)
+ end
+
+ # Autobox arguments
+ adapt_signature(mmethoddef, arguments)
+
+ var rt_name = mmethoddef.rt_name
+ if res == null then
+ add("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});")
+ return null
+ end
+ var ress = new_expr("{rt_name}.get{rt_name}().exec(new RTVal[]\{{arguments.join(",")}\});", compiler.mainmodule.object_type)
+ assign(res, ress)
+ return res
+ end
+
+ # Generate a polymorphic send for `method` with `arguments`
+ fun send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
+ # Polymorphic send
+ return table_send(mmethod, arguments)
+ end
+
+
+ # Handle common special cases before doing the effective method invocation
+ # This methods handle the `==` and `!=` methods and the case of the null receiver.
+ # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
+ # Client must not forget to close the } after them.
+ #
+ # The value returned is the result of the common special cases.
+ # If not null, client must compile it with the result of their own effective method invocation.
+ #
+ # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
+ # is generated to cancel the effective method invocation that will follow
+ # TODO: find a better approach
+ private fun before_send(res: nullable RuntimeVariable, mmethod: MMethodDef, arguments: Array[RuntimeVariable]) do
+ var bool_type = compiler.mainmodule.bool_type
+ var recv = arguments.first
+ var consider_null = mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance"
+ if recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType then
+ add("if ({recv} == null || {recv}.is_null()) \{")
+ if mmethod.name == "==" or mmethod.name == "is_same_instance" then
+ if res == null then res = new_var(bool_type)
+ var arg = arguments[1]
+ if arg.mcasttype isa MNullableType then
+ add("{res} = ({arg} == null || {arg}.is_null());")
+ else if arg.mcasttype isa MNullType then
+ add("{res} = true; /* is null */")
+ else
+ add("{res} = false; /* {arg.inspect} cannot be null */")
+ end
+ else if mmethod.name == "!=" then
+ if res == null then res = new_var(bool_type)
+ # res = self.new_var(bool_type)
+ var arg = arguments[1]
+ if arg.mcasttype isa MNullableType then
+ add("{res} = ({arg} != null && !{arg}.is_null());")
+ else if arg.mcasttype isa MNullType then
+ add("{res} = false; /* is null */")
+ else
+ add("{res} = true; /* {arg.inspect} cannot be null */")
+ end
+ else
+ add_abort("Receiver is null")
+ ret(null_instance)
+ end
+ add("\} else \{")
+ else
+ add "\{"
+ add "/* recv ({recv}) cannot be null since it's a {recv.mcasttype}"
+ end
+ if consider_null then
+ var arg = arguments[1]
+ if arg.mcasttype isa MNullType then
+ if res == null then res = new_var(bool_type)
+ if mmethod.name == "!=" then
+ add("{res} = true; /* arg is null and recv is not */")
+ else # `==` and `is_same_instance`
+ add("{res} = false; /* arg is null but recv is not */")
+ end
+ add("\}") # closes the null case
+ add("if (false) \{") # what follow is useless, Javac will drop it
+ end
+ end
+ end
+
+ # Perform a method call through vft
+ private fun table_send(mmethod: TableCallable, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do
+ var mdef: MMethodDef
+ var name: String
+ if mmethod isa MMethod then
+ mdef = mmethod.intro
+ name = mmethod.full_name
+ else if mmethod isa MMethodDef then
+ mdef = mmethod
+ name = mmethod.full_name
+ else
+ abort
+ end
+
+ var recv = arguments.first
+ var rect = mdef.mclassdef.bound_mtype
+ var msignature = mdef.msignature.as(not null)
+ msignature = msignature.resolve_for(rect, rect, compiler.mainmodule, true)
+ adapt_signature(mdef, arguments)
+
+ var res: nullable RuntimeVariable
+ var ret = msignature.return_mtype
+ if ret == null then
+ res = null
+ else
+ res = self.new_var(ret)
+ end
+
+ before_send(res, mdef, arguments)
+
+ add "/* concrete call to {mdef} */"
+ if res != null then
+ var ress = new_expr("{recv}.rtclass.vft.get(\"{name}\").exec(new RTVal[]\{{arguments.join(",")}\});", compiler.mainmodule.object_type)
+ assign(res, ress)
+ else
+ add("{recv}.rtclass.vft.get(\"{name}\").exec(new RTVal[]\{{arguments.join(",")}\});")
+ end
+
+ add("\}") # closes the null case
+
+ return res
+ end
+
# Code generation
# Add a line (will be suffixed by `\n`)
if nexpr.mtype != null then
res = nexpr.expr(self)
end
- assert res != null
+
+ if mtype != null then
+ mtype = anchor(mtype)
+ res = autobox(res, mtype)
+ end
+
current_node = old
return res
end
# Correctly assign a left and a right value
# Boxing and unboxing is performed if required
fun assign(left, right: RuntimeVariable) do
- # TODO right = autobox(right, left.mtype)
- add("{left} = {right};")
+ add("{left} = {autobox(right, left.mtype)};")
end
# Return a new local RuntimeVariable initialized with the Java expression `jexpr`.
add("System.exit(1);")
end
+ # Types handling
+
+ # Anchor a type to the main module and the current receiver
+ fun anchor(mtype: MType): MType do
+ if not mtype.need_anchor then return mtype
+ return mtype.anchor_to(compiler.mainmodule, frame.as(not null).receiver)
+ end
+
+ # Adapt the arguments of a method according to targetted `MMethodDef`
+ fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) do
+ var msignature = m.msignature.as(not null).resolve_for(
+ m.mclassdef.bound_mtype,
+ m.mclassdef.bound_mtype,
+ m.mclassdef.mmodule, true)
+ args.first = autobox(args.first, compiler.mainmodule.object_type)
+ for i in [0..msignature.arity[ do
+ args[i+1] = autobox(args[i + 1], compiler.mainmodule.object_type)
+ end
+ end
+
+ # Box primitive `value` to `mtype`.
+ private fun box(value: RuntimeVariable, mtype: MType): RuntimeVariable do
+ if value.is_boxed then return value
+ var obj_type = compiler.mainmodule.object_type
+ if value.mtype isa MNullType then
+ return new_expr("new RTVal(null, null)", compiler.mainmodule.model.null_type)
+ end
+ var mbox = value.mtype.as(MClassType).mclass
+ return new_expr("new RTVal({mbox.rt_name}.get{mbox.rt_name}(), {value})", obj_type)
+ end
+
+ # Unbox primitive `value` to `mtype`.
+ private fun unbox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
+ if not value.is_boxed then return value
+ if not mtype.is_java_primitive then return value
+ if compiler.box_kinds.has(mtype) then
+ return new_expr("({mtype.java_type}){value}.value", mtype)
+ else
+ info "NOT YET IMPLEMENTED unbox for {value} ({mtype})"
+ abort
+ end
+ end
+
+ # Box or unbox primitive `value` to `mtype` if needed.
+ private fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable do
+ if mtype.is_java_primitive then return unbox(value, mtype)
+ return box(value, mtype)
+ end
+
+ # Can this `value` be a primitive Java value?
+ private fun can_be_primitive(value: RuntimeVariable): Bool do
+ var t = value.mcasttype.undecorate
+ if not t isa MClassType then return false
+ var k = t.mclass.kind
+ return k == interface_kind or t.is_java_primitive
+ end
+
+ # Native instances
+
+ # Generate an integer value
+ fun int_instance(value: Int): RuntimeVariable do
+ var t = compiler.mainmodule.int_type
+ return new RuntimeVariable(value.to_s, t, t)
+ end
+
+ # Generate a byte value
+ fun byte_instance(value: Byte): RuntimeVariable do
+ var t = compiler.mainmodule.byte_type
+ return new RuntimeVariable(value.to_s, t, t)
+ end
+
+ # Generate a char value
+ fun char_instance(value: Char): RuntimeVariable do
+ var t = compiler.mainmodule.char_type
+ return new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
+ end
+
+ # Generate a float value
+ #
+ # FIXME pass a Float, not a string
+ fun float_instance(value: String): RuntimeVariable do
+ var t = compiler.mainmodule.float_type
+ return new RuntimeVariable(value.to_s, t, t)
+ end
+
+ # Generate an integer value
+ fun bool_instance(value: Bool): RuntimeVariable do
+ var t = compiler.mainmodule.bool_type
+ return new RuntimeVariable(value.to_s, t, t)
+ end
+
+ # Generate the `null` value
+ fun null_instance: RuntimeVariable do
+ var t = compiler.mainmodule.model.null_type
+ return new RuntimeVariable("null", t, t)
+ end
+
+ # Get an instance of a array for a vararg
+ fun vararg_instance(mpropdef: MPropDef, recv: RuntimeVariable, varargs: Array[RuntimeVariable], elttype: MType): RuntimeVariable do
+ # TODO handle dynamic types
+ info("NOT YET IMPLEMENTED vararg_instance")
+ return null_instance
+ # TODO return array_instance(varargs, elttype)
+ end
+
+ # Utils
+
# Display a info message
fun info(str: String) do compiler.modelbuilder.toolcontext.info(str, 0)
end
end
end
+# Used as a common type between MMethod and MMethodDef for `table_send`
+private interface TableCallable
+end
+
+redef class MMethod
+ super TableCallable
+end
+
redef class MMethodDef
+ super TableCallable
# Runtime name
private fun rt_name: String do
end
end
+redef class ASendExpr
+ redef fun expr(v) do
+ var recv = v.expr(n_expr, null)
+ var callsite = callsite.as(not null)
+ var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, raw_arguments)
+ return v.compile_callsite(callsite, args)
+ end
+end
+
redef class ASelfExpr
redef fun expr(v) do return v.frame.as(not null).arguments.first
end
redef fun expr(v) do return v.frame.as(not null).arguments.first
end
+redef class AVardeclExpr
+ redef fun stmt(v) do
+ var variable = self.variable.as(not null)
+ var ne = self.n_expr
+ var decl = v.variable(variable)
+ if ne != null then
+ var i = v.expr(ne, variable.declared_type)
+ v.assign(decl, i)
+ end
+ end
+end
+
+redef class AVarExpr
+ redef fun expr(v) do
+ return v.variable(self.variable.as(not null))
+ end
+end
+
+redef class AVarAssignExpr
+ redef fun expr(v) do
+ var variable = self.variable.as(not null)
+ var i = v.expr(self.n_value, variable.declared_type)
+ v.assign(v.variable(variable), i)
+ return i
+ end
+end
+
+redef class AIntExpr
+ redef fun expr(v) do return v.int_instance(self.value.as(not null))
+end
+
+redef class AByteExpr
+ redef fun expr(v) do return v.byte_instance(self.value.as(not null))
+end
+
+redef class AFloatExpr
+ redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
+end
+
+redef class ACharExpr
+ redef fun expr(v) do return v.char_instance(self.value.as(not null))
+end
+
+redef class ATrueExpr
+ redef fun expr(v) do return v.bool_instance(true)
+end
+
+redef class AFalseExpr
+ redef fun expr(v) do return v.bool_instance(false)
+end
+
+redef class ANullExpr
+ redef fun expr(v) do return v.null_instance
+end
+
redef class AAbortExpr
redef fun stmt(v) do v.add_abort("Aborted")
end