# A C function associated to a Nit method
# This is the base class for all runtime representation of a nit method.
# It implements the Template Design Pattern whose steps are :
# 1. create the receiver `RuntimeVariable` (selfvar)
# 2. create a `StaticFrame`
# 3. resolve the return type.
# 4. write the function signature
# 5. Declare the function signature (for C header files)
# 6. writer the function body
# 7. post-compiler hook (optional)
# Each step is called in `AbstractRuntimeFunction::compile_to_c`. A default
# body is provided foreach step except for compilation hooks. Subclasses may
# redefine any of the above mentioned steps. However, it's not recommanded
# to override `compile_to_c` since there's an ordering of the compilation steps.
# Any information between steps must be saved in the visitor. Currently most
# of the future runtime info are stored in the `StaticFrame` of the visitor,
# e.g. the receiver (selfvar), the arguments, etc.
#
# Moreover, any subclass may redefine : the receiver type, the return type and
# the signature. This allow for a better customization for the final implementation.
# Because of customization, a given Nit method can be compile more that once.
abstract class AbstractRuntimeFunction
# The associated Nit method
var mmethoddef: MMethodDef
protected var : nullable MType = null
# The mangled c name of the runtime_function
# Subclasses should redefine `build_c_name` instead
fun c_name: String
do
var res = self.c_name_cache
if res != null then return res
res = self.build_c_name
self.c_name_cache = res
return res
end
fun : String do return "&{c_name}"
# Non cached version of `c_name`
protected fun build_c_name: String is abstract
protected var : nullable String = null is writable
# Implements a call of the runtime_function
# May inline the body or generate a C function call
fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
# Returns `true` if the associated `mmethoddef`'s return type isn't null,
# otherwise `false`.
fun has_return: Bool
do
return mmethoddef.msignature. != null
end
# The current msignature to use when compiling : `signature_to_c` and `body_to_c`.
# This method is useful since most concrete implementation doesn't use the
# mmethoddef's signature. By providing a definition in the abstract class,
# subclasses can use any msignature.
fun msignature: MSignature
do
return mmethoddef.msignature.as(not null)
end
# The current receiver type to compile : `signature_to_c` and `body_to_c`.
# See `msignature` method for more information.
protected fun recv_mtype: MType
do
return mmethoddef.mclassdef.bound_mtype
end
# Prepare the `self` runtime variable to be used by the rest of
# compilation steps.
# Step 1
protected fun resolve_receiver(v: VISITOR): RuntimeVariable
do
var casttype = mmethoddef.mclassdef.bound_mtype
return new RuntimeVariable("self", recv_mtype, casttype)
end
# Builds the static frame for current runtime method
# Step 2
protected fun build_frame(v: VISITOR, arguments: Array[RuntimeVariable]): StaticFrame
do
return new StaticFrame(v, mmethoddef, recv_mtype.as(MClassType), arguments)
end
# Step 3 : Returns the return type used by the runtime function.
protected fun resolve_return_mtype(v: VISITOR)
do
return_mtype = msignature.return_mtype
end
# Fills the argument array inside v.frame.arguments, calling `resolve_ith_parameter`
# for each parameter.
private fun fill_parameters(v: VISITOR)
do
assert v.frame != null
for i in [0..msignature.arity[ do
var arg = resolve_ith_parameter(v, i)
v.frame..add(arg)
end
end
# Step 4 : Creates `RuntimeVariable` for each method argument.
protected fun resolve_ith_parameter(v: VISITOR, i: Int): RuntimeVariable
do
var mp = msignature.mparameters[i]
var mtype = mp.mtype
if mp.is_vararg then
mtype = v.mmodule.array_type(mtype)
end
return new RuntimeVariable("p{i}", mtype, mtype)
end
# Generate the code for the signature with an open curly brace
#
# Returns the generated signature without a semicolon and curly brace,
# e.g `RES f(T0 p0, T1 p1, ..., TN pN)`
# Step 5
protected fun signature_to_c(v: VISITOR): String
do
assert v.frame != null
var arguments = v.frame.
var comment = new FlatBuffer
var selfvar = v.frame.
var c_ret = "void"
if has_return then
c_ret = "{return_mtype.}"
end
var sig = new FlatBuffer
sig.append("{c_ret} {c_name}({recv_mtype.ctype} self")
comment.append("({selfvar}: {selfvar.mtype}")
for i in [0..arguments.length-1[ do
# Skip the receiver
var arg = arguments[i+1]
comment.append(", {arg.mtype}")
sig.append(", {arg.mtype.ctype} p{i}")
end
sig.append(")")
comment.append(")")
if has_return then
comment.append(": {return_mtype.as(not null)}")
end
v.add_decl("/* method {self} for {comment} */")
v.add_decl("{sig} \{")
return sig.to_s
end
# How the concrete compiler will declare the method, e.g inside a global header file,
# extern signature, etc.
# Step 6
protected fun declare_signature(v: VISITOR, signature: String) is abstract
# Generate the code for the body without return statement at the end and
# no curly brace.
# Step 7
protected fun body_to_c(v: VISITOR)
do
mmethoddef.compile_inside_to_c(v, v.frame.)
end
# Hook called at the end of `compile_to_c` function. This function
# is useful if you need to register any function compiled to c.
# Step 8 (optional).
protected fun end_compile_to_c(v: VISITOR)
do
# Nothing to do by default
end
# Generate the code
fun compile_to_c(compiler: COMPILER)
do
var v = compiler.new_visitor
var selfvar = resolve_receiver(v)
var arguments = [selfvar]
var frame = build_frame(v, arguments)
v.frame = frame
resolve_return_mtype(v)
fill_parameters(v)
# WARNING: the signature must be declared before creating
# any returnlabel and returnvar (`StaticFrame`). Otherwise,
# you could end up with variable outside the function.
var sig = signature_to_c(v)
declare_signature(v, sig)
frame.returnlabel = v.get_name("RET_LABEL")
if has_return then
var ret_mtype = return_mtype
assert ret_mtype != null
frame.returnvar = v.new_var(ret_mtype)
end
body_to_c(v)
v.add("{frame.returnlabel.as(not null)}:;")
if has_return then
v.add("return {frame.returnvar.as(not null)};")
end
v.add "\}"
end_compile_to_c(v)
end
end
src/compiler/abstract_compiler.nit:2055,1--2264,3