nitg*: extern classes a polymorph in Nit, and unboxed only for extern methods
[nit.git] / src / abstract_compiler.nit
index 979832a..9e67eae 100644 (file)
@@ -1044,9 +1044,27 @@ abstract class AbstractCompilerVisitor
                return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule)
        end
 
-       fun compile_callsite(callsite: CallSite, args: Array[RuntimeVariable]): nullable RuntimeVariable
+       fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
-               return self.send(callsite.mproperty, args)
+               var initializers = callsite.mpropdef.initializers
+               if not initializers.is_empty then
+                       var recv = arguments.first
+
+                       assert initializers.length == arguments.length - 1 else debug("expected {initializers.length}, got {arguments.length - 1}")
+                       var i = 1
+                       for p in initializers do
+                               if p isa MMethod then
+                                       self.send(p, [recv, arguments[i]])
+                               else if p isa MAttribute then
+                                       self.write_attribute(p, recv, arguments[i])
+                               else abort
+                               i += 1
+                       end
+
+                       return self.send(callsite.mproperty, [recv])
+               end
+
+               return self.send(callsite.mproperty, arguments)
        end
 
        fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable is abstract
@@ -1129,12 +1147,22 @@ abstract class AbstractCompilerVisitor
        # Generate a super call from a method definition
        fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract
 
+       # Adapt the arguments of a method according to targetted `MMethodDef`
        fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
 
+       # Unbox all the arguments of a method when implemented `extern` or `intern`
+       fun unbox_signature_extern(m: MMethodDef, args: Array[RuntimeVariable]) is abstract
+
        # Box or unbox a value to another type iff a C type conversion is needed
        # ENSURE: `result.mtype.ctype == mtype.ctype`
        fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
 
+       # Box extern classes to be used in the generated code
+       fun box_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
+
+       # Unbox extern classes to be used in extern code (legacy NI and FFI)
+       fun unbox_extern(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract
+
        #  Generate a polymorphic subtype test
        fun type_test(value: RuntimeVariable, mtype: MType, tag: String): RuntimeVariable is abstract
 
@@ -1266,6 +1294,16 @@ abstract class AbstractCompilerVisitor
                return res
        end
 
+       # The difference with `new_var` is the C static type of the local variable
+       fun new_var_extern(mtype: MType): RuntimeVariable
+       do
+               mtype = self.anchor(mtype)
+               var name = self.get_name("var")
+               var res = new RuntimeVariable(name, mtype, mtype)
+               self.add_decl("{mtype.ctype_extern} {name} /* : {mtype} for extern */;")
+               return res
+       end
+
        # Return a new uninitialized named runtime_variable
        fun new_named_var(mtype: MType, name: String): RuntimeVariable
        do
@@ -1590,6 +1628,10 @@ redef class MType
        # Return the C type associated to a given Nit static type
        fun ctype: String do return "val*"
 
+       # C type outside of the compiler code and in boxes
+       fun ctype_extern: String do return "val*"
+
+       # Short name of the `ctype` to use in unions
        fun ctypename: String do return "val"
 
        # Return the name of the C structure associated to a Nit live type
@@ -1621,13 +1663,20 @@ redef class MClassType
                        return "char*"
                else if mclass.name == "NativeArray" then
                        return "val*"
-               else if mclass.kind == extern_kind then
-                       return "void*"
                else
                        return "val*"
                end
        end
 
+       redef fun ctype_extern: String
+       do
+               if mclass.kind == extern_kind then
+                       return "void*"
+               else
+                       return ctype
+               end
+       end
+
        redef fun ctypename: String
        do
                if mclass.name == "Int" then
@@ -1643,8 +1692,6 @@ redef class MClassType
                else if mclass.name == "NativeArray" then
                        #return "{self.arguments.first.ctype}*"
                        return "val"
-               else if mclass.kind == extern_kind then
-                       return "ptr"
                else
                        return "val"
                end
@@ -1884,6 +1931,7 @@ redef class AMethPropdef
                end
                if pname != "==" and pname != "!=" then
                        v.adapt_signature(mpropdef, arguments)
+                       v.unbox_signature_extern(mpropdef, arguments)
                end
                if cname == "Int" then
                        if pname == "output" then
@@ -2129,14 +2177,16 @@ redef class AMethPropdef
                var ret = mpropdef.msignature.return_mtype
                if ret != null then
                        ret = v.resolve_for(ret, arguments.first)
-                       res = v.new_var(ret)
+                       res = v.new_var_extern(ret)
                end
                v.adapt_signature(mpropdef, arguments)
+               v.unbox_signature_extern(mpropdef, arguments)
 
                if res == null then
                        v.add("{externname}({arguments.join(", ")});")
                else
                        v.add("{res} = {externname}({arguments.join(", ")});")
+                       res = v.box_extern(res, ret.as(not null))
                        v.ret(res)
                end
        end
@@ -2156,12 +2206,14 @@ redef class AMethPropdef
                        v.add_extern(file)
                end
                v.adapt_signature(mpropdef, arguments)
+               v.unbox_signature_extern(mpropdef, arguments)
                var ret = arguments.first.mtype
-               var res = v.new_var(ret)
+               var res = v.new_var_extern(ret)
 
                arguments.shift
 
                v.add("{res} = {externname}({arguments.join(", ")});")
+               res = v.box_extern(res, ret)
                v.ret(res)
        end
 end
@@ -2251,6 +2303,15 @@ redef class AClassdef
        private fun compile_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable])
        do
                if mpropdef == self.mfree_init then
+                       if mpropdef.mproperty.is_root_init then
+                               assert self.super_inits == null
+                               assert arguments.length == 1
+                               if not mpropdef.is_intro then
+                                       v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments)
+                               end
+                               return
+                       end
+
                        var super_inits = self.super_inits
                        if super_inits != null then
                                var args_of_super = arguments
@@ -2259,6 +2320,7 @@ redef class AClassdef
                                        v.send(su, args_of_super)
                                end
                        end
+
                        var recv = arguments.first
                        var i = 1
                        # Collect undefined attributes
@@ -2845,7 +2907,7 @@ redef class ANewExpr
                        return v.native_array_instance(elttype, l)
                else if ctype == "val*" then
                        recv = v.init_instance(mtype)
-               else if ctype == "void*" then
+               else if ctype == "char*" then
                        recv = v.new_expr("NULL/*special!*/", mtype)
                else
                        recv = v.new_expr("({ctype})0/*special!*/", mtype)