nitg*: extern classes a polymorph in Nit, and unboxed only for extern methods
authorAlexis Laferrière <alexis.laf@xymus.net>
Tue, 22 Jul 2014 17:32:13 +0000 (13:32 -0400)
committerAlexis Laferrière <alexis.laf@xymus.net>
Wed, 10 Sep 2014 18:53:51 +0000 (14:53 -0400)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

src/abstract_compiler.nit
src/compiler_ffi.nit
src/global_compiler.nit
src/model/model.nit
src/rapid_type_analysis.nit
src/separate_compiler.nit
src/separate_erasure_compiler.nit

index 9d441cc..9e67eae 100644 (file)
@@ -1147,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
 
@@ -1284,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
@@ -1608,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
@@ -1639,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
@@ -1661,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
@@ -1902,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
@@ -2147,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
@@ -2174,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
@@ -2873,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)
index 9a83ba4..4c55f65 100644 (file)
@@ -167,6 +167,7 @@ redef class AMethPropdef
                end
 
                v.adapt_signature(mpropdef, arguments)
+               v.unbox_signature_extern(mpropdef, arguments)
 
                var arguments_for_c = new Array[String]
                for a in [0..arguments.length[ do
@@ -199,6 +200,7 @@ redef class AMethPropdef
                                v.add("ret_var = {externname}({arguments_for_c.join(", ")});")
                                v.add("{recv_var} = ret_var->value;")
                        end
+                       recv_var = v.box_extern(recv_var, return_mtype)
                        v.ret(recv_var)
                end
 
@@ -225,6 +227,7 @@ redef class AMethPropdef
                var recv_var = v.new_var(return_mtype)
 
                v.adapt_signature(mpropdef, arguments)
+               v.unbox_signature_extern(mpropdef, arguments)
 
                arguments.shift
 
@@ -252,6 +255,7 @@ redef class AMethPropdef
                        v.add("ret_var = {externname}({arguments_for_c.join(", ")});")
                        v.add("{recv_var} = ret_var->value;")
                end
+               recv_var = v.box_extern(recv_var, return_mtype)
                v.ret(recv_var)
 
                compile_ffi_support_to_c(v)
@@ -391,17 +395,14 @@ redef class MExplicitCall
                var mtype: MType = recv_mtype
                var recv_var = null
                if mproperty.is_init then
-                       if recv_mtype.mclass.kind == extern_kind then
-                               recv_var = nitni_visitor.new_var(mtype)
-                       else
-                               var recv_mtype = recv_mtype
-                               recv_var = nitni_visitor.init_instance(recv_mtype)
-                               nitni_visitor.add("{mtype.ctype} recv /* var self: {mtype} */;")
-                               nitni_visitor.add("recv = {recv_var};")
-                       end
+                       var recv_mtype = recv_mtype
+                       recv_var = nitni_visitor.init_instance(recv_mtype)
+                       nitni_visitor.add("{mtype.ctype} recv /* var self: {mtype} */;")
+                       nitni_visitor.add("recv = {recv_var};")
                else
                        mtype = mtype.anchor_to(v.compiler.mainmodule, recv_mtype)
                        recv_var = nitni_visitor.var_from_c("recv", mtype)
+                       recv_var = nitni_visitor.box_extern(recv_var, mtype)
                end
 
                vars.add(recv_var)
@@ -409,6 +410,7 @@ redef class MExplicitCall
                for p in msignature.mparameters do
                        var arg_mtype = p.mtype.anchor_to(v.compiler.mainmodule, recv_mtype)
                        var arg = nitni_visitor.var_from_c(p.name, arg_mtype)
+                       arg = nitni_visitor.box_extern(arg, arg_mtype)
                        vars.add(arg)
                end
 
@@ -423,6 +425,7 @@ redef class MExplicitCall
                        assert ret_var != null
                        return_mtype = return_mtype.anchor_to(v.compiler.mainmodule, recv_mtype)
                        ret_var = nitni_visitor.autobox(ret_var, return_mtype)
+                       ret_var = nitni_visitor.unbox_extern(ret_var, return_mtype)
                        nitni_visitor.ret_to_c(ret_var, return_mtype)
                end
                nitni_visitor.add("\}")
@@ -459,11 +462,13 @@ redef class MExplicitSuper
                var vars = new Array[RuntimeVariable]
 
                var recv_var = nitni_visitor.var_from_c("recv", mclass_type)
+               recv_var = nitni_visitor.box_extern(recv_var, mclass_type)
                vars.add(recv_var)
 
                for p in msignature.mparameters do
                        var arg_mtype = v.anchor(p.mtype)
                        var arg = nitni_visitor.var_from_c(p.name, arg_mtype)
+                       arg = nitni_visitor.box_extern(arg, arg_mtype)
                        vars.add(arg)
                end
 
@@ -473,6 +478,8 @@ redef class MExplicitSuper
                if return_mtype != null then
                        assert ret_var != null
                        return_mtype = v.anchor(return_mtype)
+                       ret_var = nitni_visitor.autobox(ret_var, return_mtype)
+                       ret_var = nitni_visitor.unbox_extern(ret_var, return_mtype)
                        nitni_visitor.ret_to_c(ret_var, return_mtype)
                end
                nitni_visitor.add("\}")
@@ -500,11 +507,14 @@ redef class MExplicitCast
                var nitni_visitor = v.compiler.new_visitor
                nitni_visitor.frame = v.frame
 
-               var full_internal_csignature = "int {v.compiler.mainmodule.name }___{from.mangled_cname}_is_a_{to.mangled_cname}({from.cname_blind} from)"
+               var full_internal_csignature = "int {v.compiler.mainmodule.name }___{from.mangled_cname}_is_a_{to.mangled_cname}({internal_call_context.name_mtype(from)} from)"
+
                nitni_visitor.add_decl("/* nitni check for {from} to {to} */")
                nitni_visitor.add_decl("{full_internal_csignature} \{")
 
-               var from_var = new RuntimeVariable("from->value", from, from)
+               #var from_var = new RuntimeVariable("from->value", from, from)
+               var from_var = nitni_visitor.var_from_c("from", from)
+               from_var = nitni_visitor.box_extern(from_var, from)
                var recv_var = nitni_visitor.type_test(from_var, to, "FFI isa")
                nitni_visitor.add("return {recv_var};")
 
@@ -531,11 +541,12 @@ redef class MExplicitCast
                nitni_visitor = v.compiler.new_visitor
                nitni_visitor.frame = v.frame
 
-               full_internal_csignature = "{to.cname_blind} {v.compiler.mainmodule.name }___{from.mangled_cname}_as_{to.mangled_cname}({from.cname_blind} from)"
+               full_internal_csignature = "{to.cname_blind} {v.compiler.mainmodule.name }___{from.mangled_cname}_as_{to.mangled_cname}({internal_call_context.name_mtype(from)} from)"
                nitni_visitor.add_decl("/* nitni cast for {from} to {to} */")
                nitni_visitor.add_decl("{full_internal_csignature} \{")
 
                from_var = nitni_visitor.var_from_c("from", from)
+               from_var = nitni_visitor.box_extern(from_var, from)
 
                ## test type
                var check = nitni_visitor.type_test(from_var, to, "FFI cast")
@@ -545,6 +556,7 @@ redef class MExplicitCast
 
                ## internal cast
                recv_var = nitni_visitor.autobox(from_var, to)
+               recv_var = nitni_visitor.unbox_extern(recv_var, to)
 
                nitni_visitor.ret_to_c(recv_var, to)
 
index 9a82243..2196071 100644 (file)
@@ -61,6 +61,9 @@ redef class ModelBuilder
                var compiler = new GlobalCompiler(mainmodule, self, runtime_type_analysis)
                compiler.compile_header
 
+               if mainmodule.model.get_mclasses_by_name("Pointer") != null then
+                       runtime_type_analysis.live_types.add(mainmodule.pointer_type)
+               end
                for t in runtime_type_analysis.live_types do
                        compiler.declare_runtimeclass(t)
                end
@@ -71,6 +74,9 @@ redef class ModelBuilder
                for t in runtime_type_analysis.live_types do
                        if t.ctype == "val*" then
                                compiler.generate_init_instance(t)
+                               if t.mclass.kind == extern_kind then
+                                       compiler.generate_box_instance(t)
+                               end
                        else
                                compiler.generate_box_instance(t)
                        end
@@ -116,7 +122,7 @@ class GlobalCompiler
                self.runtime_type_analysis = runtime_type_analysis
                self.live_primitive_types = new Array[MClassType]
                for t in runtime_type_analysis.live_types do
-                       if t.ctype != "val*" then
+                       if t.ctype != "val*" or t.mclass.name == "Pointer" then
                                self.live_primitive_types.add(t)
                        end
                end
@@ -193,11 +199,11 @@ class GlobalCompiler
                        v.add_decl("{mtype.arguments.first.ctype} values[1];")
                end
 
-               if mtype.ctype != "val*" then
+               if mtype.ctype_extern != "val*" then
                        # Is the Nit type is native then the struct is a box with two fields:
                        # * the `classid` to be polymorph
                        # * the `value` that contains the native value.
-                       v.add_decl("{mtype.ctype} value;")
+                       v.add_decl("{mtype.ctype_extern} value;")
                end
 
                # Collect all attributes and associate them a field in the structure.
@@ -252,7 +258,6 @@ class GlobalCompiler
        fun generate_box_instance(mtype: MClassType)
        do
                assert self.runtime_type_analysis.live_types.has(mtype)
-               assert mtype.ctype != "val*"
                var v = self.new_visitor
 
                self.header.add_decl("val* BOX_{mtype.c_name}({mtype.ctype});")
@@ -318,6 +323,34 @@ class GlobalCompilerVisitor
                end
        end
 
+       redef fun unbox_extern(value, mtype)
+       do
+               if mtype isa MClassType and mtype.mclass.kind == extern_kind and
+                  mtype.mclass.name != "NativeString" then
+                       var res = self.new_var_extern(mtype)
+                       self.add "{res} = ((struct {mtype.c_name}*){value})->value; /* unboxing {value.mtype} */"
+                       return res
+               else
+                       return value
+               end
+       end
+
+       redef fun box_extern(value, mtype)
+       do
+               if not mtype isa MClassType or mtype.mclass.kind != extern_kind or
+                       mtype.mclass.name == "NativeString" then return value
+
+               var valtype = value.mtype.as(MClassType)
+               var res = self.new_var(mtype)
+               if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
+                       self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
+                       self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);")
+                       return res
+               end
+               self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
+               return res
+       end
+
        # The runtime types that are acceptable for a given receiver.
        fun collect_types(recv: RuntimeVariable): Array[MClassType]
        do
@@ -507,6 +540,7 @@ class GlobalCompilerVisitor
        do
                var recv_type = get_recvtype(m, recvtype, args)
                var recv = get_recv(recv_type, args)
+               if m.is_extern then recv = unbox_extern(recv, recv_type)
                var new_args = args.to_a
                self.varargize(m, m.msignature.as(not null), new_args)
                new_args.first = recv
@@ -519,6 +553,7 @@ class GlobalCompilerVisitor
        do
                var recv_type = get_recvtype(m, recvtype, args)
                var recv = get_recv(recv_type, args)
+               if m.is_extern then recv = unbox_extern(recv, recv_type)
                var new_args = args.to_a
                new_args.first = recv
                return finalize_call(m, recv_type, new_args)
@@ -592,6 +627,19 @@ class GlobalCompilerVisitor
                end
        end
 
+       redef fun unbox_signature_extern(m, args)
+       do
+               var recv = args.first
+               for i in [0..m.msignature.arity[ do
+                       var t = m.msignature.mparameters[i].mtype
+                       if i == m.msignature.vararg_rank then
+                               t = args[i+1].mtype
+                       end
+                       t = self.resolve_for(t, recv)
+                       if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
+               end
+       end
+
        # FIXME: this is currently buggy since recv is not exact
        redef fun vararg_instance(mpropdef, recv, varargs, elttype)
        do
@@ -846,6 +894,15 @@ class GlobalCompilerVisitor
                                if not t.is_subtype(self.compiler.mainmodule, null, value2.mcasttype) then continue
                                s.add "({value1}->classid == {self.compiler.classid(t)} && ((struct {t.c_name}*){value1})->value == ((struct {t.c_name}*){value2})->value)"
                        end
+
+                       if self.compiler.mainmodule.model.get_mclasses_by_name("Pointer") != null then
+                               var pointer_type = self.compiler.mainmodule.pointer_type
+                               if value1.mcasttype.is_subtype(self.compiler.mainmodule, null, pointer_type) or
+                                       value2.mcasttype.is_subtype(self.compiler.mainmodule, null, pointer_type) then
+                                       s.add "(((struct {pointer_type.c_name}*){value1})->value == ((struct {pointer_type.c_name}*){value2})->value)"
+                               end
+                       end
+
                        if s.is_empty then
                                self.add("{res} = {value1} == {value2};")
                        else
index fe4eade..a0f8c43 100644 (file)
@@ -217,8 +217,7 @@ redef class MModule
        private var object_type_cache: nullable MClassType
 
        # The type `Pointer`, super class to all extern classes
-       fun pointer_type: MClassType
-       is cached do return self.get_primitive_class("Pointer").mclass_type
+       var pointer_type: MClassType = self.get_primitive_class("Pointer").mclass_type is lazy
 
        # The primitive type `Bool`
        fun bool_type: MClassType
index 6518860..0820839 100644 (file)
@@ -207,6 +207,7 @@ class RapidTypeAnalysis
                force_alive("Int")
                force_alive("Float")
                force_alive("Char")
+               force_alive("Pointer")
 
                while not todo.is_empty do
                        var mmethoddef = todo.shift
index 4dfbeb0..43d06c2 100644 (file)
@@ -191,7 +191,11 @@ class SeparateCompiler
                        self.header.add_decl("void* val;")
                        for c, v in self.box_kinds do
                                var t = c.mclass_type
-                               self.header.add_decl("{t.ctype} {t.ctypename};")
+
+                               # `Pointer` reuse the `val` field
+                               if t.mclass.name == "Pointer" then continue
+
+                               self.header.add_decl("{t.ctype_extern} {t.ctypename};")
                        end
                        self.header.add_decl("\} nitattribute_t; /* general C type representing a Nit attribute. */")
                end
@@ -213,9 +217,11 @@ class SeparateCompiler
 
        fun box_kind_of(mclass: MClass): Int
        do
-               if mclass.mclass_type.ctype == "val*" then
+               #var pointer_type = self.mainmodule.pointer_type
+               #if mclass.mclass_type.ctype == "val*" or mclass.mclass_type.is_subtype(self.mainmodule, mclass.mclass_type pointer_type) then
+               if mclass.mclass_type.ctype_extern == "val*" then
                        return 0
-               else if mclass.kind == extern_kind then
+               else if mclass.kind == extern_kind and mclass.name != "NativeString" then
                        return self.box_kinds[self.mainmodule.get_primitive_class("Pointer")]
                else
                        return self.box_kinds[mclass]
@@ -662,14 +668,13 @@ class SeparateCompiler
        do
                var mtype = mclass.intro.bound_mtype
                var c_name = mclass.c_name
-               var c_instance_name = mclass.c_instance_name
 
                var vft = self.method_tables[mclass]
                var attrs = self.attr_tables[mclass]
                var v = new_visitor
 
                var rta = runtime_type_analysis
-               var is_dead = rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray"
+               var is_dead = rta != null and not rta.live_classes.has(mclass) and mtype.ctype == "val*" and mclass.name != "NativeArray" and mclass.name != "Pointer"
 
                v.add_decl("/* runtime class {c_name} */")
 
@@ -698,23 +703,24 @@ class SeparateCompiler
                        v.add_decl("\};")
                end
 
-               if mtype.ctype != "val*" then
-                       if mtype.mclass.name == "Pointer" or mtype.mclass.kind != extern_kind then
-                               #Build instance struct
-                               self.header.add_decl("struct instance_{c_instance_name} \{")
-                               self.header.add_decl("const struct type *type;")
-                               self.header.add_decl("const struct class *class;")
-                               self.header.add_decl("{mtype.ctype} value;")
-                               self.header.add_decl("\};")
-                       end
+               if mtype.ctype != "val*" or mtype.mclass.name == "Pointer" then
+                       # Is a primitive type or the Pointer class, not any other extern class
+
+                       #Build instance struct
+                       self.header.add_decl("struct instance_{c_name} \{")
+                       self.header.add_decl("const struct type *type;")
+                       self.header.add_decl("const struct class *class;")
+                       self.header.add_decl("{mtype.ctype_extern} value;")
+                       self.header.add_decl("\};")
 
-                       if not rta.live_types.has(mtype) then return
+                       if not rta.live_types.has(mtype) and mtype.mclass.name != "Pointer" then return
 
                        #Build BOX
-                       self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype});")
+                       self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
                        v.add_decl("/* allocate {mtype} */")
-                       v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{")
-                       v.add("struct instance_{c_instance_name}*res = nit_alloc(sizeof(struct instance_{c_instance_name}));")
+                       v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype_extern} value) \{")
+                       v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));")
+                       v.compiler.undead_types.add(mtype)
                        v.require_declaration("type_{c_name}")
                        v.add("res->type = &type_{c_name};")
                        v.require_declaration("class_{c_name}")
@@ -722,10 +728,31 @@ class SeparateCompiler
                        v.add("res->value = value;")
                        v.add("return (val*)res;")
                        v.add("\}")
+
+                       if mtype.mclass.name != "Pointer" then return
+
+                       v = new_visitor
+                       self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
+                       v.add_decl("/* allocate {mtype} */")
+                       v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
+                       if is_dead then
+                               v.add_abort("{mclass} is DEAD")
+                       else
+                               var res = v.new_named_var(mtype, "self")
+                               res.is_exact = true
+                               v.add("{res} = nit_alloc(sizeof(struct instance_{mtype.c_name}));")
+                               v.add("{res}->type = type;")
+                               hardening_live_type(v, "type")
+                               v.require_declaration("class_{c_name}")
+                               v.add("{res}->class = &class_{c_name};")
+                               v.add("((struct instance_{mtype.c_name}*){res})->value = NULL;")
+                               v.add("return {res};")
+                       end
+                       v.add("\}")
                        return
                else if mclass.name == "NativeArray" then
                        #Build instance struct
-                       self.header.add_decl("struct instance_{c_instance_name} \{")
+                       self.header.add_decl("struct instance_{c_name} \{")
                        self.header.add_decl("const struct type *type;")
                        self.header.add_decl("const struct class *class;")
                        # NativeArrays are just a instance header followed by a length and an array of values
@@ -738,9 +765,9 @@ class SeparateCompiler
                        v.add_decl("/* allocate {mtype} */")
                        v.add_decl("{mtype.ctype} NEW_{c_name}(int length, const struct type* type) \{")
                        var res = v.get_name("self")
-                       v.add_decl("struct instance_{c_instance_name} *{res};")
+                       v.add_decl("struct instance_{c_name} *{res};")
                        var mtype_elt = mtype.arguments.first
-                       v.add("{res} = nit_alloc(sizeof(struct instance_{c_instance_name}) + length*sizeof({mtype_elt.ctype}));")
+                       v.add("{res} = nit_alloc(sizeof(struct instance_{c_name}) + length*sizeof({mtype_elt.ctype}));")
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
                        v.require_declaration("class_{c_name}")
@@ -749,6 +776,30 @@ class SeparateCompiler
                        v.add("return (val*){res};")
                        v.add("\}")
                        return
+               else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
+                       # Is an extern class (other than Pointer and NativeString)
+                       # Pointer is caught in a previous `if`, and NativeString is internal
+
+                       var pointer_type = mainmodule.pointer_type
+
+                       self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
+                       v.add_decl("/* allocate {mtype} */")
+                       v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
+                       if is_dead then
+                               v.add_abort("{mclass} is DEAD")
+                       else
+                               var res = v.new_named_var(mtype, "self")
+                               res.is_exact = true
+                               v.add("{res} = nit_alloc(sizeof(struct instance_{pointer_type.c_name}));")
+                               v.add("{res}->type = type;")
+                               hardening_live_type(v, "type")
+                               v.require_declaration("class_{c_name}")
+                               v.add("{res}->class = &class_{c_name};")
+                               v.add("((struct instance_{pointer_type.c_name}*){res})->value = NULL;")
+                               v.add("return {res};")
+                       end
+                       v.add("\}")
+                       return
                end
 
                #Build NEW
@@ -909,6 +960,22 @@ class SeparateCompilerVisitor
                end
        end
 
+       redef fun unbox_signature_extern(m, args)
+       do
+               var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
+               var recv = args.first
+               if not m.mproperty.is_init and m.is_extern then
+                       args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type)
+               end
+               for i in [0..msignature.arity[ do
+                       var t = msignature.mparameters[i].mtype
+                       if i == msignature.vararg_rank then
+                               t = args[i+1].mtype
+                       end
+                       if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t)
+               end
+       end
+
        redef fun autobox(value, mtype)
        do
                if value.mtype == mtype then
@@ -916,9 +983,12 @@ class SeparateCompilerVisitor
                else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then
                        return value
                else if value.mtype.ctype == "val*" then
-                       return self.new_expr("((struct instance_{mtype.c_instance_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
+                       return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
                else if mtype.ctype == "val*" then
                        var valtype = value.mtype.as(MClassType)
+                       if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
+                               valtype = compiler.mainmodule.pointer_type
+                       end
                        var res = self.new_var(mtype)
                        if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(valtype) then
                                self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
@@ -941,6 +1011,42 @@ class SeparateCompilerVisitor
                end
        end
 
+       redef fun unbox_extern(value, mtype)
+       do
+               if mtype isa MClassType and mtype.mclass.kind == extern_kind and
+                  mtype.mclass.name != "NativeString" then
+                       var pointer_type = compiler.mainmodule.pointer_type
+                       var res = self.new_var_extern(mtype)
+                       self.add "{res} = ((struct instance_{pointer_type.c_name}*){value})->value; /* unboxing {value.mtype} */"
+                       return res
+               else
+                       return value
+               end
+       end
+
+       redef fun box_extern(value, mtype)
+       do
+               if mtype isa MClassType and mtype.mclass.kind == extern_kind and
+                  mtype.mclass.name != "NativeString" then
+                       var valtype = compiler.mainmodule.pointer_type
+                       var res = self.new_var(mtype)
+                       if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
+                               self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
+                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);")
+                               return res
+                       end
+                       self.require_declaration("BOX_{valtype.c_name}")
+                       self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
+                       self.require_declaration("type_{mtype.c_name}")
+                       self.add("{res}->type = &type_{mtype.c_name};")
+                       self.require_declaration("class_{mtype.c_name}")
+                       self.add("{res}->class = &class_{mtype.c_name};")
+                       return res
+               else
+                       return value
+               end
+       end
+
        # Return a C expression returning the runtime type structure of the value
        # The point of the method is to works also with primitives types.
        fun type_info(value: RuntimeVariable): String
@@ -1307,7 +1413,7 @@ class SeparateCompilerVisitor
                                # The attribute is primitive, thus we store it in a box
                                # The trick is to create the box the first time then resuse the box
                                self.add("if ({attr} != NULL) \{")
-                               self.add("((struct instance_{mtype.c_instance_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
+                               self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
                                self.add("\} else \{")
                                value = self.autobox(value, self.object_type.as_nullable)
                                self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
@@ -1481,7 +1587,8 @@ class SeparateCompilerVisitor
                self.add_decl("const char* {res};")
                if value.mtype.ctype == "val*" then
                        self.add "{res} = {value} == NULL ? \"null\" : {value}->type->name;"
-               else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind then
+               else if value.mtype isa MClassType and value.mtype.as(MClassType).mclass.kind == extern_kind and
+                       value.mtype.as(MClassType).name != "NativeString" then
                        self.add "{res} = \"{value.mtype.as(MClassType).mclass}\";"
                else
                        self.require_declaration("type_{value.mtype.c_name}")
@@ -1564,12 +1671,12 @@ class SeparateCompilerVisitor
                        end
                end
                if primitive != null then
-                       test.add("((struct instance_{primitive.c_instance_name}*){value1})->value == ((struct instance_{primitive.c_instance_name}*){value2})->value")
+                       test.add("((struct instance_{primitive.c_name}*){value1})->value == ((struct instance_{primitive.c_name}*){value2})->value")
                else if can_be_primitive(value1) and can_be_primitive(value2) then
                        test.add("{value1}->class == {value2}->class")
                        var s = new Array[String]
                        for t, v in self.compiler.box_kinds do
-                               s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_instance_name}*){value1})->value == ((struct instance_{t.c_instance_name}*){value2})->value)"
+                               s.add "({value1}->class->box_kind == {v} && ((struct instance_{t.c_name}*){value1})->value == ((struct instance_{t.c_name}*){value2})->value)"
                        end
                        test.add("({s.join(" || ")})")
                else
@@ -1635,7 +1742,7 @@ class SeparateCompilerVisitor
        do
                var elttype = arguments.first.mtype
                var nclass = self.get_class("NativeArray")
-               var recv = "((struct instance_{nclass.c_instance_name}*){arguments[0]})->values"
+               var recv = "((struct instance_{nclass.c_name}*){arguments[0]})->values"
                if pname == "[]" then
                        self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null)))
                        return
@@ -1643,10 +1750,10 @@ class SeparateCompilerVisitor
                        self.add("{recv}[{arguments[1]}]={arguments[2]};")
                        return
                else if pname == "length" then
-                       self.ret(self.new_expr("((struct instance_{nclass.c_instance_name}*){arguments[0]})->length", ret_type.as(not null)))
+                       self.ret(self.new_expr("((struct instance_{nclass.c_name}*){arguments[0]})->length", ret_type.as(not null)))
                        return
                else if pname == "copy_to" then
-                       var recv1 = "((struct instance_{nclass.c_instance_name}*){arguments[1]})->values"
+                       var recv1 = "((struct instance_{nclass.c_name}*){arguments[1]})->values"
                        self.add("memmove({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
                        return
                end
@@ -1851,23 +1958,6 @@ end
 
 redef class MType
        fun const_color: String do return "COLOR_{c_name}"
-
-       # C name of the instance type to use
-       fun c_instance_name: String do return c_name
-end
-
-redef class MClassType
-       redef fun c_instance_name do return mclass.c_instance_name
-end
-
-redef class MClass
-       # Extern classes use the C instance of kernel::Pointer
-       fun c_instance_name: String
-       do
-               if kind == extern_kind then
-                       return "kernel__Pointer"
-               else return c_name
-       end
 end
 
 interface PropertyLayoutElement end
@@ -1881,3 +1971,8 @@ redef class MPropDef
        super PropertyLayoutElement
        fun const_color: String do return "COLOR_{c_name}"
 end
+
+redef class AExternInitPropdef
+       # The semi-global compilation does not support inlining calls to extern news
+       redef fun can_inline do return false
+end
index dcb9d6c..af3a43c 100644 (file)
@@ -219,7 +219,6 @@ class SeparateErasureCompiler
        do
                var mtype = mclass.intro.bound_mtype
                var c_name = mclass.c_name
-               var c_instance_name = mclass.c_instance_name
 
                var vft = self.method_tables[mclass]
                var attrs = self.attr_tables[mclass]
@@ -290,25 +289,42 @@ class SeparateErasureCompiler
                v.add_decl("\}")
                v.add_decl("\};")
 
-               if mtype.ctype != "val*" then
-                       if mtype.mclass.name == "Pointer" or mtype.mclass.kind != extern_kind then
-                               #Build instance struct
-                               self.header.add_decl("struct instance_{c_instance_name} \{")
-                               self.header.add_decl("const struct class *class;")
-                               self.header.add_decl("{mtype.ctype} value;")
-                               self.header.add_decl("\};")
-                       end
+               if mtype.ctype != "val*" or mtype.mclass.name == "Pointer" then
+                       #Build instance struct
+                       self.header.add_decl("struct instance_{c_name} \{")
+                       self.header.add_decl("const struct class *class;")
+                       self.header.add_decl("{mtype.ctype} value;")
+                       self.header.add_decl("\};")
 
                        #Build BOX
                        self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype});")
                        v.add_decl("/* allocate {mtype} */")
                        v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{")
-                       v.add("struct instance_{c_instance_name}*res = nit_alloc(sizeof(struct instance_{c_instance_name}));")
+                       v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));")
                        v.require_declaration("class_{c_name}")
                        v.add("res->class = &class_{c_name};")
                        v.add("res->value = value;")
                        v.add("return (val*)res;")
                        v.add("\}")
+
+                       if mtype.mclass.name != "Pointer" then return
+
+                       v = new_visitor
+                       self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}();")
+                       v.add_decl("/* allocate {mtype} */")
+                       v.add_decl("{mtype.ctype} NEW_{c_name}() \{")
+                       if is_dead then
+                               v.add_abort("{mclass} is DEAD")
+                       else
+                               var res = v.new_named_var(mtype, "self")
+                               res.is_exact = true
+                               v.add("{res} = nit_alloc(sizeof(struct instance_{mtype.c_name}));")
+                               v.require_declaration("class_{c_name}")
+                               v.add("{res}->class = &class_{c_name};")
+                               v.add("((struct instance_{mtype.c_name}*){res})->value = NULL;")
+                               v.add("return {res};")
+                       end
+                       v.add("\}")
                        return
                else if mclass.name == "NativeArray" then
                        #Build instance struct
@@ -332,6 +348,26 @@ class SeparateErasureCompiler
                        v.add("return (val*){res};")
                        v.add("\}")
                        return
+               else if mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
+                       var pointer_type = mainmodule.pointer_type
+
+                       self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}();")
+                       v.add_decl("/* allocate {mtype} */")
+                       v.add_decl("{mtype.ctype} NEW_{c_name}() \{")
+                       if is_dead then
+                               v.add_abort("{mclass} is DEAD")
+                       else
+                               var res = v.new_named_var(mtype, "self")
+                               res.is_exact = true
+                               v.add("{res} = nit_alloc(sizeof(struct instance_{pointer_type.c_name}));")
+                               #v.add("{res}->type = type;")
+                               v.require_declaration("class_{c_name}")
+                               v.add("{res}->class = &class_{c_name};")
+                               v.add("((struct instance_{pointer_type.c_name}*){res})->value = NULL;")
+                               v.add("return {res};")
+                       end
+                       v.add("\}")
+                       return
                end
 
                #Build NEW
@@ -579,6 +615,40 @@ class SeparateErasureCompilerVisitor
                return res
        end
 
+       redef fun unbox_extern(value, mtype)
+       do
+               if mtype isa MClassType and mtype.mclass.kind == extern_kind and
+                  mtype.mclass.name != "NativeString" then
+                       var pointer_type = compiler.mainmodule.pointer_type
+                       var res = self.new_var_extern(mtype)
+                       self.add "{res} = ((struct instance_{pointer_type.c_name}*){value})->value; /* unboxing {value.mtype} */"
+                       return res
+               else
+                       return value
+               end
+       end
+
+       redef fun box_extern(value, mtype)
+       do
+               if mtype isa MClassType and mtype.mclass.kind == extern_kind and
+                  mtype.mclass.name != "NativeString" then
+                       var valtype = compiler.mainmodule.pointer_type
+                       var res = self.new_var(mtype)
+                       if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
+                               self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
+                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);")
+                               return res
+                       end
+                       self.require_declaration("BOX_{valtype.c_name}")
+                       self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
+                       self.require_declaration("class_{mtype.c_name}")
+                       self.add("{res}->class = &class_{mtype.c_name};")
+                       return res
+               else
+                       return value
+               end
+       end
+
        redef fun class_name_string(value)
        do
                var res = self.get_name("var_class_name")