Merge: Adding a Windows build test
authorJean Privat <jean@pryen.org>
Fri, 23 Aug 2019 13:25:22 +0000 (09:25 -0400)
committerJean Privat <jean@pryen.org>
Fri, 23 Aug 2019 13:25:22 +0000 (09:25 -0400)
In parallel, a new Gitlab Runner tagged "windows" was added. This new test only builds the project.

Pull-Request: #2776
Reviewed-by: Jean Privat <jean@pryen.org>

lib/core/text/abstract_text.nit
lib/mongodb/mongodb.nit
src/compiler/abstract_compiler.nit
src/compiler/global_compiler.nit
src/compiler/separate_compiler.nit

index 0072ec8..1b4b383 100644 (file)
@@ -2029,25 +2029,76 @@ redef class Float
        # ~~~
        # assert 12.34.to_s       == "12.34"
        # assert (-0120.030).to_s == "-120.03"
+       # assert (-inf).to_s == "-inf"
+       # assert (nan).to_s == "nan"
        # ~~~
        #
        # see `to_precision` for a custom precision.
        redef fun to_s do
                var str = to_precision(3)
-               if is_inf != 0 or is_nan then return str
-               var len = str.length
+               return adapt_number_of_decimal(str, false)
+       end
+
+       # Return the representation of `self`, with scientific notation
+       #
+       # Adpat the number of decimals as needed from 1 to a maximum of 6
+       # ~~~
+       # assert 12.34.to_sci       == "1.234e+01"
+       # assert 123.45.to_sci.to_f.to_sci  == "1.2345e+02"
+       # assert 0.001234.to_sci  == "1.234e-03"
+       # assert (inf).to_sci == "inf"
+       # assert (nan).to_sci == "nan"
+       # ~~~
+       fun to_sci: String
+       do
+               var is_inf_or_nan = check_inf_or_nan
+               if is_inf_or_nan != null then return is_inf_or_nan
+               return adapt_number_of_decimal(return_from_specific_format("%e".to_cstring), true)
+       end
+
+       # Return the `string_number` with the adapted number of decimal (i.e the fonction remove the useless `0`)
+       # `is_expo` it's here to specifi if the given `string_number` is in scientific notation
+       private fun adapt_number_of_decimal(string_number: String, is_expo: Bool): String
+       do
+               # check if `self` does not need an adaptation of the decimal
+               if is_inf != 0 or is_nan then return string_number
+               var len = string_number.length
+               var expo_value = ""
+               var numeric_value = ""
                for i in [0..len-1] do
-                       var j = len-1-i
-                       var c = str.chars[j]
-                       if c == '0' then
-                               continue
-                       else if c == '.' then
-                               return str.substring( 0, j+2 )
-                       else
-                               return str.substring( 0, j+1 )
+                       var j = len - 1 - i
+                       var c = string_number.chars[j]
+                       if not is_expo then
+                               if c == '0' then
+                                       continue
+                               else if c == '.' then
+                                       numeric_value = string_number.substring( 0, j + 2)
+                                       break
+                               else
+                                       numeric_value = string_number.substring( 0, j + 1)
+                                       break
+                               end
+                       else if c == 'e' then
+                               expo_value = string_number.substring( j, len - 1 )
+                               is_expo = false
                        end
                end
-               return str
+               return numeric_value + expo_value
+       end
+
+       # Return a string representation of `self` in fonction if it is not a number or infinity.
+       # Return `null` if `self` is not a not a number or an infinity
+       private fun check_inf_or_nan: nullable String
+       do
+               if is_nan then return "nan"
+
+               var isinf = self.is_inf
+               if isinf == 1 then
+                       return "inf"
+               else if isinf == -1 then
+                       return  "-inf"
+               end
+               return null
        end
 
        # `String` representation of `self` with the given number of `decimals`
@@ -2062,19 +2113,9 @@ redef class Float
        # ~~~
        fun to_precision(decimals: Int): String
        do
-               if is_nan then return "nan"
-
-               var isinf = self.is_inf
-               if isinf == 1 then
-                       return "inf"
-               else if isinf == -1 then
-                       return  "-inf"
-               end
-
-               var size = to_precision_size(decimals)
-               var cstr = new CString(size + 1)
-               to_precision_fill(decimals, size + 1, cstr)
-               return cstr.to_s_unsafe(byte_length = size, copy = false)
+               var is_inf_or_nan = check_inf_or_nan
+               if is_inf_or_nan != null then return is_inf_or_nan
+               return return_from_specific_format("%.{decimals}f".to_cstring)
        end
 
        # Returns the hexadecimal (`String`) representation of `self` in exponential notation
@@ -2085,32 +2126,26 @@ redef class Float
        # ~~~
        fun to_hexa_exponential_notation: String
        do
-               var size = to_precision_size_hexa
+               return return_from_specific_format("%a".to_cstring)
+       end
+
+       # Return the representation of `self`, with the specific given c `format`.
+       private fun return_from_specific_format(format: CString): String
+       do
+               var size = to_precision_size_with_format(format)
                var cstr = new CString(size + 1)
-               to_precision_fill_hexa(size + 1, cstr)
+               to_precision_fill_with_format(format, size + 1, cstr)
                return cstr.to_s_unsafe(byte_length = size, copy = false)
        end
 
-       # Required string length to hold `self` with `nb` decimals
-       #
-       # The length does not include the terminating null byte.
-       private fun to_precision_size(nb: Int): Int `{
-               return snprintf(NULL, 0, "%.*f", (int)nb, self);
-       `}
-
-       # Fill `cstr` with `self` and `nb` decimals
-       private fun to_precision_fill(nb, size: Int, cstr: CString) `{
-               snprintf(cstr, size, "%.*f", (int)nb, self);
-       `}
-
-       # The lenght of `self` in exponential hexadecimal notation
-       private fun to_precision_size_hexa: Int`{
-               return snprintf(NULL, 0, "%a", self);
+       # The lenght of `self` in the specific given c `format`
+       private fun to_precision_size_with_format(format: CString): Int`{
+               return snprintf(NULL, 0, format, self);
        `}
 
-       # Fill `cstr` with `self` in exponential hexadecimal notation
-       private fun to_precision_fill_hexa(size: Int, cstr: CString) `{
-               snprintf(cstr, size, "%a", self);
+       # Fill `cstr` with `self` in the specific given c `format`
+       private fun to_precision_fill_with_format(format: CString, size: Int, cstr: CString) `{
+               snprintf(cstr, size, format, self);
        `}
 end
 
index 020e0ce..7db69e7 100644 (file)
@@ -640,11 +640,12 @@ class MongoCollection
        #
        # var res = col.aggregate("""[
        #       { "$match": { "status": "A" } },
-       #       { "$group": { "_id": "$cust_id", "total": { "$sum": "$amount" } } }
+       #       { "$group": { "_id": "$cust_id", "total": { "$sum": "$amount" } } },
+        #       { "$sort" : { "_id": 1 } }
        # ]""".parse_json.as(JsonArray))
        #
-       # assert res[0].to_json == """{"_id":"B212","total":200}"""
-       # assert res[1].to_json == """{"_id":"A123","total":750}"""
+        # assert res[0].to_json == """{"_id":"A123","total":750}"""
+       # assert res[1].to_json == """{"_id":"B212","total":200}"""
        # ~~~
        fun aggregate(pipeline: JsonArray): Array[JsonObject] do
                var q = new JsonObject
index 3fc2f40..a6fdb2c 100644 (file)
@@ -2056,7 +2056,26 @@ abstract class AbstractCompilerVisitor
 end
 
 # A C function associated to a Nit method
-# Because of customization, a given Nit method can be compiler more that once
+# 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
 
        type COMPILER: AbstractCompiler
@@ -2065,6 +2084,8 @@ abstract class AbstractRuntimeFunction
        # The associated Nit method
        var mmethoddef: MMethodDef
 
+        protected var return_mtype: nullable MType = null
+
        # The mangled c name of the runtime_function
        # Subclasses should redefine `build_c_name` instead
        fun c_name: String
@@ -2076,6 +2097,8 @@ abstract class AbstractRuntimeFunction
                return res
        end
 
+        fun c_ref: String do return "&{c_name}"
+
        # Non cached version of `c_name`
        protected fun build_c_name: String is abstract
 
@@ -2085,11 +2108,248 @@ abstract class AbstractRuntimeFunction
        # May inline the body or generate a C function call
        fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
 
-       # Generate the code for the `AbstractRuntimeFunction`
-       # Warning: compile more than once compilation makes CC unhappy
-       fun compile_to_c(compiler: COMPILER) is abstract
+        # Returns `true` if the associated `mmethoddef`'s return type isn't null,
+        # otherwise `false`.
+        fun has_return: Bool
+        do
+                return mmethoddef.msignature.return_mtype != 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.arguments.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.arguments
+                var comment = new FlatBuffer
+                var selfvar = v.frame.selfvar
+                var c_ret = "void"
+                if has_return then
+                        c_ret = "{return_mtype.ctype}"
+                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.arguments)
+        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
 
+# Base class for all thunk-like function. A thunk is a function whose purpose
+# is to call another function. Like a class wrapper or decorator, a thunk is used
+# to computer things (conversions, log, etc) before or after a function call. It's
+# an intermediary between the caller and the callee.
+#
+# The most basic use of a thunk is to unbox its argument before invoking the real callee.
+# Virtual functions are a use case of thunk function:
+#
+# ~~~~nitish
+# redef class Object
+#       fun toto(x: Int): Int do return 1 + x
+# end
+# redef class Int
+#       redef fun toto(x) do return x + self
+# end
+#
+# ```generated C code
+# long Object__toto(val* self, long x) { return 1 + x }
+# long Int__toto(long self, long x) { return x + self }
+# long THUNK_Int__toto(val* self, long x) {
+#       long self2 = (long)(self)>>2    // Unboxing from Object to Int
+#       return Int_toto(self2, x)
+# }
+#
+# ```
+# ~~~~
+#
+# A thunk has its OWN SIGNATURE and is considered like any other `AbstractRuntimeFunction`.
+# Thus, one might need to be careful when overriding parent's method. Since most usage of
+# thunks in Nit is to unbox and box things between a caller and a callee, the default
+# implementation is doing just that. In other words, a thunk takes a signature and a method
+# and tries to cast its signature to the underlying method's signature.
+#
+# A virtual function has the same signature as its `mmethoddef` field, except for
+# its receiver is of type `Object`.
+# In the same vibe, a call reference has all of its argument boxed as `Object`.
+abstract class ThunkFunction
+        super AbstractRuntimeFunction
+
+        # Determines if the callsite should be polymorphic or static.
+        var polymorph_call_flag = false is writable
+
+        # The type expected by the callee. Used to resolve `mmethoddef`'s formal
+        # parameters and virtual type. This type must NOT need anchor.
+        fun target_recv: MType is abstract
+
+        redef fun body_to_c(v)
+        do
+                assert not target_recv.need_anchor
+                var frame = v.frame
+                assert frame != null
+                var selfvar = frame.selfvar
+                var arguments = frame.arguments
+                var arguments2 = new Array[RuntimeVariable]
+                arguments2.push(v.autobox(selfvar, target_recv))
+                var resolved_sig = msignature.resolve_for(target_recv, target_recv.as(MClassType), v.mmodule, true)
+                for i in [0..resolved_sig.arity[ do
+                var param = resolved_sig.mparameters[i]
+                        var mtype = param.mtype
+                        if param.is_vararg then
+                               mtype = v.mmodule.array_type(mtype)
+                       end
+                        var temp = v.autobox(arguments[i+1], mtype)
+                        arguments2.push(temp)
+                end
+                v.add("/* {mmethoddef}, {recv_mtype.ctype} */")
+                var subret: nullable RuntimeVariable = null
+                if polymorph_call_flag then
+                        subret = v.send(mmethoddef.mproperty, arguments2)
+                else
+                        subret = v.call(mmethoddef, arguments2[0].mcasttype.as(MClassType), arguments2)
+                end
+                if has_return then
+                        assert subret != null
+                        var subret2 = v.autobox(subret, return_mtype.as(not null))
+                        v.assign(frame.returnvar.as(not null), subret2)
+                end
+
+        end
+
+end
+
+
 # A runtime variable hold a runtime value in C.
 # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions.
 #
@@ -2164,6 +2424,14 @@ class StaticFrame
 
        # The array comprehension currently filled, if any
        private var comprehension: nullable RuntimeVariable = null
+
+        # Returns the first argument (the receiver) of a frame.
+        # REQUIRE: arguments.length >= 1
+        fun selfvar: RuntimeVariable
+        do
+                assert arguments.length >= 1
+                return arguments.first
+        end
 end
 
 redef class MType
index 718cb5d..ce61657 100644 (file)
@@ -926,7 +926,7 @@ class GlobalCompilerVisitor
        end
 end
 
-# A runtime function customized on a specific monomrph receiver type
+# A runtime function customized on a specific monomorph receiver type
 private class CustomizedRuntimeFunction
        super AbstractRuntimeFunction
 
@@ -971,74 +971,51 @@ private class CustomizedRuntimeFunction
                end
        end
 
-       # compile the code customized for the reciever
-       redef fun compile_to_c(compiler)
-       do
-               var recv = self.recv
-               var mmethoddef = self.mmethoddef
-               if not recv.is_subtype(compiler.mainmodule, null, mmethoddef.mclassdef.bound_mtype) then
-                       print("problem: why do we compile {self} for {recv}?")
-                       abort
-               end
-
-               var v = compiler.new_visitor
-               var selfvar = new RuntimeVariable("self", recv, recv)
-               if compiler.runtime_type_analysis.live_types.has(recv) then
-                       selfvar.is_exact = true
-               end
-               var arguments = new Array[RuntimeVariable]
-               var frame = new StaticFrame(v, mmethoddef, recv, arguments)
-               v.frame = frame
-
-               var sig = new FlatBuffer
-               var comment = new FlatBuffer
-               var ret = mmethoddef.msignature.return_mtype
-               if ret != null then
-                       ret = v.resolve_for(ret, selfvar)
-                       sig.append("{ret.ctype} ")
-               else
-                       sig.append("void ")
-               end
-               sig.append(self.c_name)
-               sig.append("({recv.ctype} {selfvar}")
-               comment.append("(self: {recv}")
-               arguments.add(selfvar)
-               for i in [0..mmethoddef.msignature.arity[ do
-                       var mp = mmethoddef.msignature.mparameters[i]
-                       var mtype = mp.mtype
-                       if mp.is_vararg then
-                               mtype = v.mmodule.array_type(mtype)
-                       end
-                       mtype = v.resolve_for(mtype, selfvar)
-                       comment.append(", {mtype}")
-                       sig.append(", {mtype.ctype} p{i}")
-                       var argvar = new RuntimeVariable("p{i}", mtype, mtype)
-                       arguments.add(argvar)
-               end
-               sig.append(")")
-               comment.append(")")
-               if ret != null then
-                       comment.append(": {ret}")
-               end
-               compiler.header.add_decl("{sig};")
+        redef fun recv_mtype
+        do
+                return recv
+        end
 
-               v.add_decl("/* method {self} for {comment} */")
-               v.add_decl("{sig} \{")
-               #v.add("printf(\"method {self} for {comment}\\n\");")
-               if ret != null then
-                       frame.returnvar = v.new_var(ret)
-               end
-               frame.returnlabel = v.get_name("RET_LABEL")
+        redef var return_mtype
 
-               mmethoddef.compile_inside_to_c(v, arguments)
-
-               v.add("{frame.returnlabel.as(not null)}:;")
-               if ret != null then
-                       v.add("return {frame.returnvar.as(not null)};")
+        redef fun resolve_receiver(v)
+        do
+                var selfvar = new RuntimeVariable("self", recv, recv)
+               if v.compiler.runtime_type_analysis.live_types.has(recv) then
+                       selfvar.is_exact = true
                end
-               v.add("\}")
-               if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
-       end
+                return selfvar
+        end
+
+        redef fun resolve_return_mtype(v)
+        do
+                var selfvar = v.frame.selfvar
+                if has_return then
+                        var ret = msignature.return_mtype.as(not null)
+                        return_mtype = v.resolve_for(ret, selfvar)
+                end
+        end
+        redef fun resolve_ith_parameter(v, i)
+        do
+                var selfvar = v.frame.selfvar
+                var mp = msignature.mparameters[i]
+                var mtype = mp.mtype
+                if mp.is_vararg then
+                        mtype = v.mmodule.array_type(mtype)
+                end
+                mtype = v.resolve_for(mtype, selfvar)
+                return new RuntimeVariable("p{i}", mtype, mtype)
+        end
+
+        redef fun declare_signature(v, sig)
+        do
+                v.compiler.header.add_decl("{sig};")
+        end
+
+        redef fun end_compile_to_c(v)
+        do
+               if not self.c_name.has_substring("VIRTUAL", 0) then v.compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
+        end
 
        redef fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
index 15b6f05..f9d1444 100644 (file)
@@ -2197,10 +2197,7 @@ redef class MMethodDef
                                self.virtual_runtime_function_cache = res
                                return res
                        end
-
-                       res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}")
-                       self.virtual_runtime_function_cache = res
-                       res.is_thunk = true
+                        res = new SeparateThunkFunction(self, recv, msignature, "VIRTUAL_{c_name}", mclassdef.bound_mtype)
                end
                return res
        end
@@ -2237,11 +2234,23 @@ class SeparateRuntimeFunction
        # The name on the compiled method
        redef var build_c_name: String
 
-       # Statically call the original body instead
-       var is_thunk = false
-
        redef fun to_s do return self.mmethoddef.to_s
 
+        redef fun msignature
+        do
+                return called_signature
+        end
+
+        redef fun recv_mtype
+        do
+                return called_recv
+        end
+
+        redef fun return_mtype
+        do
+                return called_signature.return_mtype
+        end
+
        # The C return type (something or `void`)
        var c_ret: String is lazy do
                var ret = called_signature.return_mtype
@@ -2271,69 +2280,33 @@ class SeparateRuntimeFunction
        # The C type for the function pointer.
        var c_funptrtype: String is lazy do return "{c_ret}(*){c_sig}"
 
-       redef fun compile_to_c(compiler)
-       do
-               var mmethoddef = self.mmethoddef
-
-               var sig = "{c_ret} {c_name}{c_sig}"
-               compiler.provide_declaration(self.c_name, "{sig};")
-
-               var rta = compiler.as(SeparateCompiler).runtime_type_analysis
-
-               var recv = self.mmethoddef.mclassdef.bound_mtype
-               var v = compiler.new_visitor
-               var selfvar = new RuntimeVariable("self", called_recv, recv)
-               var arguments = new Array[RuntimeVariable]
-               var frame = new StaticFrame(v, mmethoddef, recv, arguments)
-               v.frame = frame
+        redef fun declare_signature(v, sig)
+        do
+                v.compiler.provide_declaration(c_name, "{sig};")
+        end
 
-               var msignature = called_signature
-               var ret = called_signature.return_mtype
-
-               var comment = new FlatBuffer
-               comment.append("({selfvar}: {selfvar.mtype}")
-               arguments.add(selfvar)
-               for i in [0..msignature.arity[ do
-                       var mp = msignature.mparameters[i]
-                       var mtype = mp.mtype
-                       if mp.is_vararg then
-                               mtype = v.mmodule.array_type(mtype)
-                       end
-                       comment.append(", {mtype}")
-                       var argvar = new RuntimeVariable("p{i}", mtype, mtype)
-                       arguments.add(argvar)
-               end
-               comment.append(")")
-               if ret != null then
-                       comment.append(": {ret}")
-               end
+        redef fun body_to_c(v)
+        do
+                var rta = v.compiler.as(SeparateCompiler).runtime_type_analysis
+                if rta != null and not rta.live_mmodules.has(mmethoddef.mclassdef.mmodule) then
+                       v.add_abort("FATAL: Dead method executed.")
+                else
+                        super
+                end
+        end
 
-               v.add_decl("/* method {self} for {comment} */")
-               v.add_decl("{sig} \{")
-               if ret != null then
-                       frame.returnvar = v.new_var(ret)
-               end
-               frame.returnlabel = v.get_name("RET_LABEL")
 
-               if is_thunk then
-                       var subret = v.call(mmethoddef, recv, arguments)
-                       if ret != null then
-                               assert subret != null
-                               v.assign(frame.returnvar.as(not null), subret)
-                       end
-               else if rta != null and not rta.live_mmodules.has(mmethoddef.mclassdef.mmodule) then
-                       v.add_abort("FATAL: Dead method executed.")
-               else
-                       mmethoddef.compile_inside_to_c(v, arguments)
-               end
+        redef fun end_compile_to_c(v)
+        do
+                var compiler = v.compiler
+                compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
+        end
 
-               v.add("{frame.returnlabel.as(not null)}:;")
-               if ret != null then
-                       v.add("return {frame.returnvar.as(not null)};")
-               end
-               v.add("\}")
-               compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
-       end
+        redef fun build_frame(v, arguments)
+        do
+                var recv = mmethoddef.mclassdef.bound_mtype
+                return new StaticFrame(v, mmethoddef, recv, arguments)
+        end
 
        # Compile the trampolines used to implement late-binding.
        #
@@ -2382,6 +2355,12 @@ class SeparateRuntimeFunction
        end
 end
 
+class SeparateThunkFunction
+        super ThunkFunction
+        super SeparateRuntimeFunction
+        redef var target_recv
+end
+
 redef class MType
        # Are values of `self` tagged?
        # If false, it means that the type is not primitive, or is boxed.