nitg: intro the nitni module
authorAlexis Laferrière <alexis.laf@xymus.net>
Fri, 15 Nov 2013 21:07:07 +0000 (16:07 -0500)
committerJean Privat <jean@pryen.org>
Mon, 17 Feb 2014 19:08:38 +0000 (14:08 -0500)
Offers services defining the tailored API used by the FFI.

Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

src/nitni/nitni.nit [new file with mode: 0644]
src/nitni/nitni_base.nit [new file with mode: 0644]
src/nitni/nitni_callbacks.nit [new file with mode: 0644]
src/nitni/nitni_utilities.nit [new file with mode: 0644]

diff --git a/src/nitni/nitni.nit b/src/nitni/nitni.nit
new file mode 100644 (file)
index 0000000..5778e5a
--- /dev/null
@@ -0,0 +1,25 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Native interface related services (used underneath the FFI)
+#
+# All nitni properties may not be visible to the user but they are
+# shared between engines
+module nitni
+
+import nitni_base
+import nitni_callbacks
+import nitni_utilities
diff --git a/src/nitni/nitni_base.nit b/src/nitni/nitni_base.nit
new file mode 100644 (file)
index 0000000..69f64f3
--- /dev/null
@@ -0,0 +1,149 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Native interface related services (used underneath the FFI)
+#
+# All nitni properties may not be visible to the user but they are
+# shared between engines
+module nitni_base
+
+import parser
+import modelbuilder # builder only for externcalls
+
+redef class MMethod
+       # Short name of this method in C (without the class name)
+       fun short_cname: String do
+               var nit_name = name
+
+               if nit_name == "+" then return "_plus"
+               if nit_name == "-" then return "_minus"
+               if nit_name == "*" then return "_star"
+               if nit_name == "/" then return "_slash"
+               if nit_name == "%" then return "_percent"
+               if nit_name == "[]" then return "_index"
+               if nit_name == "[]=" then return "_index_assign"
+               if nit_name == "==" then return "_equal"
+               if nit_name == "<" then return "_less"
+               if nit_name == ">" then return "_geater"
+               if nit_name == "<=" then return "_less_or_equal"
+               if nit_name == ">=" then return "_greater_or_equal"
+               if nit_name == "!=" then return "_not_equal"
+               if nit_name == "<<" then return "_left"
+               if nit_name == ">>" then return "_right"
+               if nit_name == "<=>" then return "_starship"
+
+               if nit_name.last == '=' then return "{nit_name.substring(0, nit_name.length-1)}__assign"
+               return nit_name
+       end
+end
+
+redef class AModule
+       # Mangled name of this module in C
+       fun cname: String do return mmodule.name
+end
+
+redef class MMethodDef
+       # Name of the function to callback this method from C,
+       # also used in other functions names used for this method.
+       fun cname: String do return "{mclassdef.mclass.name}_{mproperty.short_cname}"
+end
+
+redef class MType
+       # Representation of this type in pure C on the FFI extern side
+       #   Object -> Object
+       #   Pointer -> void*
+       fun cname: String is abstract
+
+       # Representation of this type in C for the internal of the system
+       # Hides extern types.
+       fun cname_blind: String is abstract
+
+       # Representation of this type in mangled C
+       #   Object -> Object
+       #   Pointer -> Pointer
+       fun mangled_cname: String is abstract
+
+       # Does this types has a primitive reprensentation
+       #   type Object is_primitive? false
+       #   type Pointer is_primitive? true
+       fun is_cprimitive: Bool is abstract
+end
+
+redef class MClassType
+       redef fun cname
+       do
+               var name = mclass.name
+               if name == "Bool" then return "int"
+               if name == "Char" then return "char"
+               if name == "Float" then return "double"
+               if name == "Int" then return "int"
+               if name == "NativeString" then return "char*"
+               if mclass.kind == extern_kind then
+                       var ctype = mclass.ctype
+                       assert ctype != null
+                       return ctype
+               end
+               return mangled_cname
+       end
+
+       redef fun cname_blind do
+               var name = mclass.name
+               if name == "Bool" then return "int"
+               if name == "Char" then return "char"
+               if name == "Float" then return "double"
+               if name == "Int" then return "int"
+               if name == "NativeString" then return "char*"
+               if mclass.kind == extern_kind then return "void*"
+               return "struct nitni_instance *"
+       end
+
+       redef fun mangled_cname do return mclass.name
+
+       redef fun is_cprimitive do return mclass.kind == extern_kind or
+                       (once ["Bool", "Char", "Float", "Int", "NativeString"]).has(mclass.name)
+end
+
+redef class MNullableType
+       redef fun cname do return mangled_cname
+       redef fun cname_blind do return "struct nitni_instance *"
+       redef fun mangled_cname do return "nullable_{mtype.mangled_cname}"
+       redef fun is_cprimitive do return false
+end
+
+redef class MVirtualType
+       redef fun mangled_cname: String do return to_s
+end
+
+redef class MGenericType
+       redef fun cname do return mangled_cname
+       redef fun mangled_cname
+       do
+               var base = super
+
+               var params = new Array[String]
+               for arg in arguments do params.add(arg.mangled_cname)
+
+               return "{base}_of_{params.join("_")}"
+       end
+end
+
+redef class MClass
+       fun ctype: nullable String
+       do
+               assert kind == extern_kind
+               return "void*"
+       end
+end
diff --git a/src/nitni/nitni_callbacks.nit b/src/nitni/nitni_callbacks.nit
new file mode 100644 (file)
index 0000000..5cf389f
--- /dev/null
@@ -0,0 +1,423 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012-2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# nitni services related to callbacks (used underneath the FFI)
+module nitni_callbacks
+
+import modelbuilder
+intrude import rapid_type_analysis
+
+import nitni_base
+
+redef class ToolContext
+       var verify_nitni_callback_phase: Phase = new VerifyNitniCallbacksPhase(self, [typing_phase])
+end
+
+# * checks for the validity of callbacks
+# * store the callbacks on each method
+class VerifyNitniCallbacksPhase
+       super Phase
+
+       redef fun process_npropdef(npropdef)
+       do
+               if not npropdef isa AExternPropdef then return
+
+               npropdef.verify_nitni_callbacks(toolcontext)
+       end
+end
+
+# Provides a better API but mainly the same content as AExternCalls
+class ForeignCallbackSet
+       # set of imported functions, cached to avoid repetitions
+       var callbacks: Set[ MExplicitCall ] = new HashSet[ MExplicitCall ]
+
+       # set of imported functions, cached to avoid repetitions
+       var supers: Set[ MExplicitSuper ] = new HashSet[ MExplicitSuper ]
+
+       # set of relevant types, cached to avoid repetitions
+       var types: Set[ MType ] = new HashSet[ MType ]
+
+       # set of imported casts and as, cached to avoid repetitions
+       var casts: Set[ MExplicitCast ] = new HashSet[ MExplicitCast ]
+
+       # Utility function, must be called only when all other maps are filled
+       private var all_cached: nullable Set[NitniCallback] = null
+       fun all: Set[NitniCallback]
+       do
+               var cached = all_cached
+               if cached != null then return cached
+
+               var set = new HashSet[NitniCallback]
+               set.add_all(callbacks)
+               set.add_all(supers)
+               set.add_all(types)
+               set.add_all(casts)
+               
+               self.all_cached = set
+               return set
+       end
+
+       # Integrate content from the `other` set into this one
+       fun join(other: ForeignCallbackSet)
+       do
+               callbacks.add_all( other.callbacks )
+               supers.add_all( other.supers )
+               types.add_all( other.types )
+               casts.add_all( other.casts )
+       end
+end
+
+redef class AExternPropdef
+       private var foreign_callbacks_cache: nullable ForeignCallbackSet = null
+
+       # All foreign callbacks from this method
+       fun foreign_callbacks: ForeignCallbackSet
+       do
+               var fcs = foreign_callbacks_cache
+               assert fcs != null else print "Error: attempting to access nitni callbacks before phase 'verify_nitni_callback_phase'."
+               return fcs
+       end
+
+       # Verifiy the validity of the explicit callbacks to Nit
+       # also fills the set returned by foreign_callbacks
+       fun verify_nitni_callbacks(toolcontext: ToolContext)
+       do
+               if foreign_callbacks_cache != null then return
+
+               var fcs = new ForeignCallbackSet
+
+               var mmodule = mpropdef.mclassdef.mmodule
+
+               # receiver
+               var recv_type = mpropdef.mclassdef.bound_mtype
+               fcs.types.add(recv_type)
+
+               # return type
+               var rmt = mpropdef.msignature.return_mtype
+               if rmt != null then
+                       if rmt isa MParameterType or rmt isa MVirtualType then
+                               var mclass_type = mpropdef.mclassdef.bound_mtype
+                               rmt = rmt.anchor_to(mmodule, mclass_type)
+                       end
+                       var mtype = rmt.resolve_for(recv_type, recv_type, mmodule, true)
+                       fcs.types.add(mtype)
+               end
+
+               # params
+               for p in mpropdef.msignature.mparameters do
+                       var mtype = p.mtype.resolve_for(recv_type, recv_type, mmodule, true)
+                       if mtype isa MParameterType or mtype isa MVirtualType then
+                               var mclass_type = mpropdef.mclassdef.bound_mtype
+                               mtype = mtype.anchor_to(mmodule, mclass_type)
+                       end
+                       fcs.types.add( mtype )
+               end
+
+               # explicit callbacks
+               if n_extern_calls != null then
+                       for ec in n_extern_calls.n_extern_calls do
+                               ec.verify_and_collect(self, fcs, toolcontext)
+                       end
+               end
+
+               # store result
+               foreign_callbacks_cache = fcs
+       end
+
+       redef fun accept_rapid_type_visitor(v)
+       do
+               for cb in foreign_callbacks.callbacks do v.add_send(cb.recv_mtype, cb.mproperty.as(MMethod))
+               for cast in foreign_callbacks.casts do v.add_cast_type(cast.to)
+               for sup in foreign_callbacks.supers do
+                       v.analysis.add_super_send(sup.from.mclassdef.mclass.mclass_type, sup.from.as(MMethodDef))
+               end
+               for t in foreign_callbacks.types do if t isa MClassType then v.add_type t
+       end
+end
+
+# Classification for all nitni callbacks
+interface NitniCallback
+end
+
+redef class MType
+       super NitniCallback
+end
+
+# A prossible call from C, declared explictly after the `import` keyword
+class MExplicitCall
+       super NitniCallback
+
+       # Previously resolved mtype
+       var recv_mtype: MClassType
+       var mproperty: MProperty
+       var from_mmodule: MModule
+
+       fun fill_type_for( callback_set: ForeignCallbackSet, from: MModule )
+       do
+               var first = mproperty.lookup_first_definition( from, recv_mtype )
+               var mclassdef = first.mclassdef
+               var bound_mtype = mclassdef.bound_mtype
+
+               # receiver / constructor return
+               recv_mtype = recv_mtype.resolve_for(bound_mtype, bound_mtype, from, true)
+               recv_mtype = recv_mtype.anchor_to(from, bound_mtype)
+               callback_set.types.add( recv_mtype )
+
+               if first isa MMethodDef then
+                       var rmt = first.msignature.return_mtype
+                       if rmt != null then
+                               rmt = rmt.resolve_for(bound_mtype, bound_mtype, from, true)
+                               rmt = rmt.anchor_to(from, bound_mtype)
+                               callback_set.types.add( rmt )
+                       end
+
+                       for p in first.msignature.mparameters do
+                               var param_mtype = p.mtype.resolve_for(recv_mtype, recv_mtype, from, true)
+                               param_mtype = param_mtype.resolve_for(bound_mtype, bound_mtype, from, true)
+                               param_mtype = param_mtype.anchor_to(from, bound_mtype)
+                               callback_set.types.add( param_mtype )
+                       end
+               end
+       end
+
+       # Signature of this call in C as seen by user
+       fun csignature: String
+       do
+               var mproperty = self.mproperty
+               if mproperty isa MMethod then
+                       var signature = mproperty.intro.msignature
+                       assert signature != null
+
+                       var creturn_type
+                       if mproperty.is_init then
+                               creturn_type = recv_mtype.cname
+                       else if signature.return_mtype != null then
+                               var ret_mtype = signature.return_mtype
+                               ret_mtype = ret_mtype.resolve_for(recv_mtype, recv_mtype, from_mmodule, true)
+                               creturn_type = ret_mtype.cname
+                       else
+                               creturn_type = "void"
+                       end
+
+                       var cname
+                       if mproperty.is_init then
+                               if mproperty.name == "init" or mproperty.name == "new" then
+                                       cname = "new_{recv_mtype.mangled_cname}"
+                               else
+                                       cname = "new_{recv_mtype.mangled_cname}_{mproperty.short_cname}"
+                               end
+                       else
+                               cname = "{recv_mtype.mangled_cname}_{mproperty.short_cname}"
+                       end
+
+                       var cparams = new List[String]
+                       if not mproperty.is_init then
+                               cparams.add( "{recv_mtype.cname} self" )
+                       end
+                       for p in signature.mparameters do
+                               var param_mtype = p.mtype.resolve_for(recv_mtype, recv_mtype, from_mmodule, true)
+                               cparams.add( "{param_mtype.cname} {p.name}" )
+                       end
+
+                       return "{creturn_type} {cname}( {cparams.join(", ")} )"
+               else
+                       print "Type of callback from foreign code not yet supported."
+                       abort
+               end
+       end
+
+       redef fun hash do return recv_mtype.hash + 1024 * mproperty.hash
+       redef fun ==(o) do return o isa MExplicitCall and recv_mtype == o.recv_mtype and mproperty == o.mproperty
+end
+
+class MExplicitSuper
+       super NitniCallback
+
+       var from: MPropDef
+
+       redef fun hash do return from.hash
+       redef fun ==(o) do return o isa MExplicitSuper and from == o.from
+end
+
+class MExplicitCast
+       super NitniCallback
+
+       var from: MType
+       var to: MType
+
+       fun check_cname: String do return "{from.mangled_cname}_is_a_{to.mangled_cname}"
+
+       fun cast_cname: String do return "{from.mangled_cname}_as_{to.mangled_cname}"
+
+       redef fun hash do return from.hash + 1024 * to.hash
+       redef fun ==(o) do return o isa MExplicitCast and from == o.from and to == o.to
+end
+
+redef class AExternCall
+       # Verify this explicit declaration of call from C and collect all relevant callbacks
+       fun verify_and_collect(npropdef: AExternPropdef, callback_set: ForeignCallbackSet,
+               toolcontext: ToolContext) is abstract
+end
+
+redef class ALocalPropExternCall
+       redef fun verify_and_collect(npropdef, callback_set, toolcontext)
+       do
+               var mmodule = npropdef.mpropdef.mclassdef.mmodule
+               var mclass_type = npropdef.mpropdef.mclassdef.bound_mtype
+               var m_name = n_methid.collect_text
+               var method = toolcontext.modelbuilder.try_get_mproperty_by_name2( self,
+                       mmodule, mclass_type, m_name )
+
+               if method == null then
+                       toolcontext.error(location, "Local method {m_name} not found.")
+                       return
+               end
+
+               var explicit_call = new MExplicitCall(mclass_type, method, mmodule)
+               callback_set.callbacks.add(explicit_call)
+
+               explicit_call.fill_type_for(callback_set, mmodule)
+       end
+end
+
+redef class AFullPropExternCall
+       redef fun verify_and_collect(npropdef, callback_set, toolcontext)
+       do
+               var mmodule = npropdef.mpropdef.mclassdef.mmodule
+               var mclassdef = npropdef.mpropdef.mclassdef
+               var nclassdef = toolcontext.modelbuilder.mclassdef2nclassdef[mclassdef]
+               var mclass_type = mclassdef.bound_mtype
+               var mtype = toolcontext.modelbuilder.resolve_mtype(nclassdef, n_type)
+
+               if mtype == null then return
+
+               if mtype isa MParameterType or mtype isa MVirtualType then
+                       mtype = mtype.anchor_to(mmodule, mclass_type)
+               end
+
+               if mtype isa MNullableType then
+                       toolcontext.error(location, "Type {n_type.collect_text} is nullable and thus cannot be the receiver." )
+                       return
+               end
+
+               var m_name = n_methid.collect_text
+               var method = toolcontext.modelbuilder.try_get_mproperty_by_name2( self,
+                       mmodule, mtype, m_name )
+
+               if method == null then
+                       toolcontext.error(location, "Method {m_name} not found in {n_type.collect_text}." )
+                       return
+               end
+
+               var explicit_call = new MExplicitCall(mtype.as(MClassType), method, mmodule)
+               callback_set.callbacks.add(explicit_call)
+               explicit_call.fill_type_for(callback_set, mmodule)
+       end
+end
+
+redef class AInitPropExternCall
+       redef fun verify_and_collect(npropdef, callback_set, toolcontext)
+       do
+               var mmodule = npropdef.mpropdef.mclassdef.mmodule
+               var mclassdef = npropdef.mpropdef.mclassdef
+               var nclassdef = toolcontext.modelbuilder.mclassdef2nclassdef[mclassdef]
+               var mtype = toolcontext.modelbuilder.resolve_mtype(nclassdef, n_type)
+               if mtype == null then return
+
+               if not mtype isa MClassType then
+                       toolcontext.error(location, "Type {n_type.collect_text} is not a class and thus cannot be used to instanciate a new instance." )
+                       return
+               end
+
+               var meth_name = "init"
+               var meth = toolcontext.modelbuilder.try_get_mproperty_by_name2( self,
+                       mmodule, mtype, meth_name )
+
+               if meth == null then
+                       toolcontext.error(location, "Method {meth_name} not found in {n_type.collect_text}." )
+                       return
+               end
+
+               var explicit_call = new MExplicitCall(mtype, meth, mmodule)
+               callback_set.callbacks.add(explicit_call)
+               explicit_call.fill_type_for(callback_set, mmodule)
+       end
+end
+
+redef class ASuperExternCall
+       redef fun verify_and_collect(npropdef, callback_set, toolcontext)
+       do
+               callback_set.supers.add( new MExplicitSuper( npropdef.mpropdef.as(not null) ) )
+               callback_set.types.add( npropdef.mpropdef.mclassdef.mclass.mclass_type )
+       end
+end
+
+redef class ACastExternCall
+       fun from_mtype: MType is abstract
+       fun to_mtype: MType is abstract
+
+       redef fun verify_and_collect(npropdef, callback_set, toolcontext)
+       do
+               var from = from_mtype
+               var to = to_mtype
+
+               callback_set.types.add(from)
+               callback_set.types.add(to)
+
+               callback_set.casts.add(new MExplicitCast(from, to))
+       end
+end
+
+redef class ACastAsExternCall
+       redef fun from_mtype do return n_from_type.mtype.as(not null)
+       redef fun to_mtype do return n_to_type.mtype.as(not null)
+
+       redef fun verify_and_collect(npropdef, callback_set, toolcontext)
+       do
+               var parent_aclassdef = npropdef.parent.as(AClassdef)
+               toolcontext.modelbuilder.resolve_mtype_unchecked(parent_aclassdef, n_from_type, true)
+               toolcontext.modelbuilder.resolve_mtype_unchecked(parent_aclassdef, n_to_type, true)
+               super
+       end
+end
+
+redef class AAsNullableExternCall
+       redef fun from_mtype do return n_type.mtype.as(not null)
+       redef fun to_mtype do return n_type.mtype.as_nullable
+
+       redef fun verify_and_collect(npropdef, callback_set, toolcontext)
+       do
+               var parent_aclassdef = npropdef.parent.as(AClassdef)
+               toolcontext.modelbuilder.resolve_mtype_unchecked(parent_aclassdef, n_type, true)
+               super
+       end
+end
+
+redef class AAsNotNullableExternCall
+       redef fun from_mtype do return n_type.mtype.as_nullable
+       redef fun to_mtype do
+               var mtype = n_type.mtype.as(not null)
+               if mtype isa MNullableType then return mtype.mtype
+               return mtype
+       end
+
+       redef fun verify_and_collect(npropdef, callback_set, toolcontext)
+       do
+               var parent_aclassdef = npropdef.parent.as(AClassdef)
+               toolcontext.modelbuilder.resolve_mtype_unchecked(parent_aclassdef, n_type, true)
+               super
+       end
+end
diff --git a/src/nitni/nitni_utilities.nit b/src/nitni/nitni_utilities.nit
new file mode 100644 (file)
index 0000000..80c6691
--- /dev/null
@@ -0,0 +1,150 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Support utilities to use nitni and the FFI
+import nitni_base
+
+redef class MMethod
+       # Build a C function name for the FFI implementation (uses friendly naming).
+       # * On a specific static receiver mype `recv_mtype` 
+       # * In referene to the module `from_module` (used for type resolving and as a possible prefix)
+       # * Has a possible `suffix` to the method name (may be "__super", "__impl", null, etc.)
+       # * With a specified length indicating whether it uses the sort name or the long name with
+       #   the module name as prefix
+       fun build_cname(recv_mtype: MClassType, from_mmodule: MModule, suffix: nullable String, length: SignatureLength): String
+       do
+               var cname
+               if self.is_init then
+                       if self.name == "init" or self.name == "new" then
+                               cname = "new_{recv_mtype.mangled_cname}"
+                       else
+                               cname = "new_{recv_mtype.mangled_cname}_{self.short_cname}"
+                       end
+               else
+                       cname = "{recv_mtype.mangled_cname}_{self.short_cname}"
+               end
+
+               if suffix != null then cname = "{cname}{suffix}"
+
+               if length.long then cname = "{from_mmodule.name}___{cname}"
+
+               return cname
+       end
+
+       # Build a C function signature for the FFI implementation (uses friendly naming).
+       # * On a specific static receiver mype `recv_mtype` 
+       # * In referene to the module `from_module` (used for type resolving and as a possible prefix)
+       # * Has a possible `suffix` to the method name (may be "__super", "__impl", null, etc.)
+       # * With a specified length indicating whether it uses the sort name or the long name with
+       #   the module name as prefix
+       # * The `call_context` identifying which types and casts to use (see `CallContext` and its instances)
+       fun build_csignature(recv_mtype: MClassType, from_mmodule: MModule, suffix: nullable String, length: SignatureLength, call_context: CallContext): String
+       do
+               var signature = self.intro.msignature
+               assert signature != null
+
+               var creturn_type
+               if self.is_init then
+                       creturn_type = call_context.name_mtype(recv_mtype)
+               else if signature.return_mtype != null then
+                       var ret_mtype = signature.return_mtype
+                       ret_mtype = ret_mtype.resolve_for(recv_mtype, recv_mtype, from_mmodule, true)
+                       creturn_type = call_context.name_mtype(ret_mtype)
+               else
+                       creturn_type = "void"
+               end
+
+               var cname = build_cname(recv_mtype, from_mmodule, suffix, length)
+
+               var cparams = new List[String]
+               if not self.is_init then
+                       cparams.add( "{call_context.name_mtype(recv_mtype)} recv" )
+               end
+               for p in signature.mparameters do
+                       var param_mtype = p.mtype.resolve_for(recv_mtype, recv_mtype, from_mmodule, true)
+                       cparams.add( "{call_context.name_mtype(param_mtype)} {p.name}" )
+               end
+
+               return "{creturn_type} {cname}( {cparams.join(", ")} )"
+       end
+
+       # Build a C function call for the FFI implementation (uses friendly naming).
+       # * On a specific static receiver mype `recv_mtype` 
+       # * In referene to the module `from_module` (used for type resolving and as a possible prefix)
+       # * Has a possible `suffix` to the method name (may be "__super", "__impl", null, etc.)
+       # * With a specified length indicating whether it uses the sort name or the long name with
+       #   the module name as prefix
+       # * The `call_context` identifying which types and casts to use (see `CallContext` and its instances)
+       # * Possible suffix to the parameters `param_suffix`
+       fun build_ccall(recv_mtype: MClassType, from_mmodule: MModule, suffix: nullable String, length: SignatureLength, call_context: CallContext, param_suffix: nullable String): String
+       do
+               if param_suffix == null then param_suffix = ""
+
+               var signature = self.intro.msignature
+               assert signature != null
+
+               var return_mtype = null
+               if self.is_init then
+                       return_mtype = recv_mtype
+               else if signature.return_mtype != null then
+                       return_mtype = signature.return_mtype
+               end
+
+               var cname = build_cname(recv_mtype, from_mmodule, suffix, length)
+
+               var cparams = new List[String]
+               if not self.is_init then
+                       cparams.add(call_context.cast_to(recv_mtype, "recv{param_suffix}"))
+               end
+
+               for p in signature.mparameters do
+                       cparams.add(call_context.cast_to(p.mtype, "{p.name}{param_suffix}"))
+               end
+
+               var joined_cparams = cparams.join(", ")
+               var ccall = "{cname}({joined_cparams})"
+               if return_mtype != null then
+                       return "return {call_context.cast_from(return_mtype, ccall)};"
+               else
+                       return "{ccall};"
+               end
+       end
+end
+
+# Describes the context of the code to be generated by `build_ccall` and `build_csignature`
+class CallContext
+       # Which C name to use for type `mtype`
+       fun name_mtype(mtype: MType): String do return mtype.cname_blind
+
+       # How to cast a returned C variable named `name` of type `mtype`
+       fun cast_from(mtype: MType, name: String): String do return name
+
+       # How to cast a C argument named `name` of type `mtype`
+       fun cast_to(mtype: MType, name: String): String do return name
+end
+
+redef class Object
+       # Call context to use
+       protected fun internal_call_context: CallContext do return new CallContext
+       protected fun long_signature: SignatureLength do return once new SignatureLength(true)
+       protected fun short_signature: SignatureLength do return once new SignatureLength(false)
+end
+
+# Length of the signature of a C function (long version hase the module name as prefix)
+class SignatureLength
+       private var long: Bool
+       private init(long: Bool) do self.long = long
+end