nitmetrics: refactor self usage metrics computation
[nit.git] / src / native_interface / frontier.nit
index 99a6e2f..0e741b9 100644 (file)
@@ -22,6 +22,105 @@ import metamodel
 import ni_tools
 import ni_metamodel
 
+import syntax # FIXME: to remove since it breaks modularity
+
+# Transitive variable through the frontier file
+# Represents a variable going from Nit to C or from C to Nit
+abstract class NiVariable
+       fun ni_from_name : String is abstract
+       fun ni_to_name : String is abstract
+       fun ni_type : MMType is abstract
+
+       # needs to be boxed or unboxed
+       # anything using the GC
+       fun needs_preparation : Bool
+       do
+               return ni_type.local_class.primitive_info == null or
+                       ni_type.local_class.primitive_info.tagged or
+                       ni_type.is_nullable
+       end
+
+       # prepare variable to callback to Nit
+       fun prepare_for_nit( fc : FunctionCompiler )
+       do
+               if needs_preparation then
+                       fc.decls.add( "val_t {ni_to_name};\n" )
+                       fc.exprs.add( "{ni_type.assign_from_friendly( ni_to_name, ni_from_name )};\n" )
+               end
+       end
+
+       fun prepare_for_c( fc : FunctionCompiler )
+       do
+               if needs_preparation then
+                       ni_type.compile_new_local_ref( ni_to_name, fc, not self isa ReturnVariable ) # TODO
+                       fc.exprs.add( "{ni_type.assign_to_friendly( ni_to_name, ni_from_name )};\n" )
+               end
+       end
+
+       # format of the variable to callback to Nit
+       fun as_arg_to_nit : String
+       do
+               if needs_preparation then
+                       return ni_to_name
+               else if ( ni_type.local_class.primitive_info != null or ni_type.local_class.global.is_extern ) and
+                       not ni_type.is_nullable then # int, float, point/void* ...
+                       return ni_type.boxtype(ni_from_name)
+               else
+                       return "{ni_from_name}->ref.val"
+               end
+       end
+
+       # format of the variable to call C implementation functions
+       fun as_arg_to_c : String
+       do
+               if needs_preparation then
+                       return ni_to_name
+               else
+                       return ni_type.unboxtype( ni_from_name )
+               end
+       end
+end
+
+redef class MMParam
+       super NiVariable
+
+       redef fun ni_from_name do return name.to_s
+       redef fun ni_to_name do return "trans___{name}"
+       redef fun ni_type do return mmtype
+end
+
+class ReceiverVariable
+       super NiVariable
+
+       redef fun ni_from_name do return "recv"
+       redef fun ni_to_name do return "trans_recv"
+
+       redef var ni_type : MMType
+       init ( t : MMType ) do ni_type = t
+end
+
+class ReturnVariable
+       super NiVariable
+
+       redef fun ni_from_name do return "orig_return"
+       redef fun ni_to_name do return "trans_return"
+
+       redef var ni_type : MMType
+       init ( t : MMType ) do ni_type = t
+
+       # used only by friendly callbacks to Nit
+       redef fun prepare_for_c( fc )
+       do
+               fc.decls.add( "val_t {ni_from_name};\n" )
+               ni_type.compile_new_local_ref( ni_to_name, fc, true )
+       end
+       redef fun prepare_for_nit( fc )
+       do
+               ni_type.compile_new_local_ref( ni_from_name, fc, false )
+               fc.decls.add( "val_t {ni_to_name};\n" )
+       end
+end
+
 redef class MMSrcModule
        fun compile_frontier( v : FrontierVisitor )
        do
@@ -43,8 +142,9 @@ redef class MMSrcModule
                        native_header = "{directory.path}/{name}_nit.h"
                end
                if native_header.file_exists then
-                       v.body.add( "#include \"{native_header.path_from_parent}\"\n" )
-                       v.header.add( "#include \"{native_header.path_from_parent}\"\n" )
+                       var path = "..".join_path(native_header).simplify_path
+                       v.body.add( "#include \"{path}\"\n" )
+                       v.header.add( "#include \"{path}\"\n" )
                end
 
                for local_class in local_classes do
@@ -90,8 +190,8 @@ redef class MMSrcMethod
                for cast in explicit_casts do
                        v.casts.add( cast )
 
-                       v.types.add( cast.from )
-                       v.types.add( cast.to )
+                       v.types.add( cast.from.direct_type )
+                       v.types.add( cast.to.direct_type )
                end
 
                # adds super
@@ -123,47 +223,34 @@ redef class MMSrcMethod
                var fc = new FunctionCompiler( friendly_super_csignature )
 
                # params
-               var params = new Array[String]
+               var params = new Array[NiVariable]
+               params.add( signature.recv_ni_variable )
+               params.add_all( signature.params )
 
-               # receiver
-               var name_for_sub = "recv___nit"
-               fc.decls.add( "val_t {name_for_sub};\n" )
-               fc.exprs.add( "{signature.recv.assign_from_friendly( name_for_sub, "recv" )};\n" )
-               params.add( name_for_sub )
+               # prepare transition
+               for p in params do p.prepare_for_nit( fc )
 
-               # other params
-               for p in signature.params do
-                       name_for_sub = "{p.name}___nit"
-                       fc.decls.add( "val_t {name_for_sub};\n" )
-                       fc.exprs.add( "{p.mmtype.assign_from_friendly( name_for_sub, p.name.to_s )};\n" )
-                       params.add( name_for_sub )
-               end
+               # extract strings
+               var args = new Array[String]
+               for p in params do args.add( p.as_arg_to_nit )
 
                # hook to generated C
-               var return_type : nullable MMType = null
-
-               if signature.return_type != null then
-                       return_type = signature.return_type
-               end
-
+               var rnv = signature.return_ni_variable
                var s = new Buffer
-               if return_type != null then
-                       fc.decls.add( "{return_type.friendly_extern_name} return___nitni;\n" )
-                       fc.decls.add( "val_t return___nit;\n" )
-                       s.append( "return___nit = " )
+               if rnv != null then
+                       rnv.prepare_for_c( fc )
+                       s.append( "{rnv.ni_from_name} = " )
                end
 
-               s.append( "{super_meth_call}( recv___nit )" )
-
-               s.append( "( {params.join( ", " )} );\n" )
+               s.append( "{super_meth_call}( {signature.recv_ni_variable.as_arg_to_nit} )" )
+               s.append( "( {args.join( ", " )} );\n" )
 
                fc.exprs.add( s.to_s )
 
-               # verify and return
-               if return_type != null
-               then
-                       fc.exprs.add( "{return_type.assign_to_friendly( "return___nitni", "return___nit" )};\n" )
-                       fc.exprs.add( "return return___nitni;\n" )
+               # return
+               if rnv != null then
+                       fc.exprs.add( "{rnv.ni_type.assign_to_friendly( rnv.ni_to_name, rnv.ni_from_name )};\n" )
+                       fc.exprs.add( "return {rnv.ni_to_name};\n" )
                end
 
                v.body.append( fc.to_writer )
@@ -174,6 +261,14 @@ redef class MMSrcMethod
        # It handles variables conversions and verification
        fun compile_out_to_frontier( v : FrontierVisitor )
        do
+               # a simple out method can be optimized
+               # To qualify as simple this method must:
+               #       - have no explicit imports (including super and casts)
+               #       - return nothing or return a primitive to C
+               var is_simple = explicit_imports.is_empty and not need_super and
+                       explicit_casts.is_empty and (signature.return_type == null or
+                       signature.return_type.local_class.primitive_info != null )
+
                # header
                v.header.add( "\n/* out/indirect function for {full_name} */\n" )
                v.header.add( "{out_csignature};\n" ) # incoming types boxed
@@ -183,46 +278,46 @@ redef class MMSrcMethod
                var fc = new FunctionCompiler( out_csignature )
 
                # params
-               var params = new List[String]
+               var params = new List[NiVariable]
+               if not is_init then params.add( signature.recv_ni_variable )
+               params.add_all( signature.params )
 
-               if not is_init then
-                       var name_for_impl = "recv___nitni"
-                       fc.decls.add( "{signature.recv.friendly_extern_name} {name_for_impl};\n" )
-                       fc.exprs.add( "{signature.recv.assign_to_friendly( name_for_impl, "recv" )};\n" )
-                       params.add( name_for_impl )
-               end
+               var args = new List[String]
 
-               for p in signature.params do
-                       var name_for_impl = "{p.name}___nitni"
-                       fc.decls.add( "{p.mmtype.friendly_extern_name} {name_for_impl};\n" )
-                       fc.exprs.add( "{p.mmtype.assign_to_friendly( name_for_impl, p.name.to_s )};\n" )
-                       params.add( name_for_impl )
+               for nv in params do
+                       if not is_simple or nv.ni_type.local_class.primitive_info != null then
+                               nv.prepare_for_c( fc )
+                               args.add( nv.as_arg_to_c )
+                       else
+                               args.add( "NULL" )
+                       end
                end
 
                # call to impl
-               var return_type : nullable MMType = null
-
-               if signature.return_type != null then
-                       return_type = signature.return_type
-               else if is_init then
-                       return_type = local_class.get_type
+               var rnv = signature.return_ni_variable
+               if rnv == null and is_init then
+                       rnv = new ReturnVariable( signature.recv )
                end
 
                var s = new Buffer
-               if return_type != null then
-                       fc.decls.add( "{return_type.friendly_extern_name} return___nitni;\n" )
-                       fc.decls.add( "val_t return___nit;\n" )
-                       s.append( "return___nitni = " )
+               if rnv != null then
+                       rnv.prepare_for_nit( fc )
+                       s.append( "{rnv.ni_from_name} = " )
                end
 
-               s.append( "{extern_name.as(not null)}( {params.join( ", " )} );\n" )
+               s.append( "{extern_name.as(not null)}( {args.join( ", " )} );\n" )
 
                fc.exprs.add( s.to_s )
 
+               if rnv != null then
+                       fc.exprs.add( "{rnv.ni_type.assign_from_friendly( rnv.ni_to_name, rnv.ni_from_name )};\n" )
+               end
+
+               fc.exprs.add( "nitni_local_ref_clean(  );\n" )
+
                # return
-               if return_type != null then
-                       fc.exprs.add( "{return_type.assign_from_friendly( "return___nit", "return___nitni" )};\n" )
-                       fc.exprs.add( "return return___nit;\n" )
+               if rnv != null then
+                       fc.exprs.add( "return {rnv.ni_to_name};\n" )
                end
 
                v.body.append( fc.to_writer )
@@ -242,18 +337,32 @@ redef class MMLocalClass
 end
 
 redef class MMSignature
+       var recv_ni_variable : ReceiverVariable
+       var return_ni_variable : nullable ReturnVariable
+       redef init( params, return_type, recv_type )
+       do
+               super
+
+               if return_type != null then
+                       return_ni_variable = new ReturnVariable( return_type )
+               else
+                       return_ni_variable = null
+               end
+               recv_ni_variable = new ReceiverVariable( recv_type )
+       end
+
        fun compile_frontier( v : FrontierVisitor )
        do
                # receiver
-               v.types.add( recv )
+               v.types.add( recv.direct_type )
 
                # params
-               for p in params do v.types.add( p.mmtype )
+               for p in params do v.types.add( p.mmtype.direct_type )
 
                # return
                var rt = return_type
                if rt != null then
-                       v.types.add( rt )
+                       v.types.add( rt.direct_type )
                end
        end
 end
@@ -310,21 +419,6 @@ class FrontierVisitor
        end
 end
 
-redef class String
-       # return path from one level deeper
-       # could be moved to stdlib.file
-       fun path_from_parent : String
-       do
-               if self[0] == '/' # is_absolute
-               then
-                       return self
-               else
-                       return "../{self}"
-               end
-
-       end
-end
-
 redef class MMImportedCast
        # Defines functions to cast types and verify the type of an object.
        fun compile_to_frontier( v : FrontierVisitor )
@@ -364,7 +458,7 @@ redef class MMImportedCast
                var temp_name = "temp"
 
                fc.decls.add( "val_t {temp_name};\n" )
-               fc.decls.add( "{to.friendly_extern_name} {out_name};\n" )
+               to.compile_new_local_ref( out_name, fc, true )
 
                fc.exprs.add( "{from.assign_from_friendly(temp_name, in_name)};\n" )
 
@@ -441,10 +535,10 @@ redef class MMType
        # Is to be nested within another function.
        fun compile_check_isa( fc : FunctionCompiler, name : String )
        do
-               fc.exprs.add( "if ( ! {compile_condition_isa( name )} )\{" )
+               fc.exprs.add( "if ( ! {compile_condition_isa( name )} )\{\n" )
                fc.exprs.add( "\tfprintf( stderr, \"Casting to {self} failed because value is not a {self}.\" );\n" )
                fc.exprs.add( "\tabort();\n" )
-               fc.exprs.add( "\}" )
+               fc.exprs.add( "\}\n" )
        end
 
        # Compiles an expression to verify if an object is of the given type.
@@ -466,31 +560,64 @@ redef class MMType
                        # defines struct
                        v.header_top.add( "#ifndef {guard}\n" )
                        v.header_top.add( "#define {guard}\n" )
-                       v.header_top.add( "typedef struct s_{name}\{\n" )
-                       v.header_top.add( "\tval_t v;\n" )
-                       v.header_top.add( "\} {name};\n" )
-                       v.header_top.add( "#endif\n\n" )
+                       v.header_top.add( "struct s_{name}\{\n" )
+                       v.header_top.add( "\t\tstruct nitni_ref ref; /* real ref struct, must be first */\n" )
+                       v.header_top.add( "\};\n" )
+                       v.header_top.add( "typedef struct s_{name} *{name};\n" )
 
                        # add null version, as a struct
                        if is_nullable then
-                               var null_getter = "null_{as_notnull.friendly_extern_name}"
-                               var null_getter_local = "{mmmodule.to_s}_{null_getter}"
+                               var local_null_getter = local_friendly_null_getter_from( mmmodule )
+
+                               v.header_top.add( "#ifndef {friendly_null_getter}\n" )
+                               v.header_top.add( "#define {friendly_null_getter} {local_null_getter}\n" )
+                               v.header_top.add( "#endif\n" )
+
+                               v.header_top.add( "{name} {local_null_getter}();\n" )
+
+                               var fc = new FunctionCompiler( "{name} {local_null_getter}()" )
+                               compile_new_local_ref( "n", fc, true )
+                               fc.exprs.add( "return n;\n" )
+                               v.body.append( fc.to_writer )
+                       end
 
-                               v.header.add( "{name} {null_getter_local}();\n" )
+                       # reference incr
+                       var incr_name = "{as_notnull.mangled_name}_incr_ref"
+                       v.header_top.add( "#define {incr_name}( x ) nitni_global_ref_incr( (struct nitni_ref*)(x) )\n" )
 
-                               v.header.add( "#ifndef {null_getter}\n" )
-                               v.header.add( "#define {null_getter} {null_getter_local}\n" )
-                               v.header.add( "#endif\n\n" )
+                       # reference decr
+                       var decr_name = "{as_notnull.mangled_name}_decr_ref"
+                       v.header_top.add( "#define {decr_name}( x ) nitni_global_ref_decr( (struct nitni_ref*)(x) )\n" )
 
-                               v.body.add( "{name} {null_getter_local}()\n" )
-                               v.body.add( "\{\n" )
-                               v.body.add( "\t{name} n;\n" )
-                               v.body.add( "\tn.v = NIT_NULL;\n" )
-                               v.body.add( "\treturn n;\n" )
-                               v.body.add( "\}\n\n" )
+                       v.header_top.add( "#endif\n" )
+               end
+       end
+
+       fun compile_new_local_ref( var_name : String, fc : FunctionCompiler, stack_it : Bool )
+       do
+               var type_name = friendly_extern_name
+
+               fc.decls.add( "{type_name} {var_name};\n" )
+               if uses_nitni_ref then
+                       fc.exprs.add( "{var_name} = malloc( sizeof( struct s_{type_name} ) );\n" )
+                       fc.exprs.add( "{var_name}->ref.val = NIT_NULL;\n" )
+                       fc.exprs.add( "{var_name}->ref.count = 0;\n" )
+                       if stack_it then
+                               fc.exprs.add( "nitni_local_ref_add( (struct nitni_ref *){var_name} );\n" )
                        end
                end
        end
+
+       # compiles a stub local reference for unused references
+       # allows to maintain static typing but avoids malloc and free
+       fun compile_stub_local_ref( var_name : String, fc : FunctionCompiler )
+       do
+               var type_name = friendly_extern_name
+               fc.decls.add( "{type_name} {var_name};\n" )
+               if uses_nitni_ref then
+                       fc.exprs.add( "{var_name} = ({type_name})NULL;\n" )
+               end
+       end
 end
 
 redef class MMExplicitImport
@@ -512,58 +639,41 @@ redef class MMExplicitImport
                var fc = new FunctionCompiler( method.frontier_csignature_from( v.mmmodule, local_class ) )
 
                # params
-               var params = new Array[String]
-
-               # if not init, add receiver
-               if not method.is_init then
-                       var name_for_sub = "recv___nit"
-                       fc.decls.add( "val_t {name_for_sub};\n" )
-                       fc.exprs.add( "{signature.recv.assign_from_friendly( name_for_sub, "recv" )};\n" )
-                       params.add( name_for_sub )
-               end
-
-               for p in signature.params do
-                       var name_for_sub = "{p.name}___nit"
-                       fc.decls.add( "val_t {name_for_sub};\n" )
-                       fc.exprs.add( "{p.mmtype.assign_from_friendly( name_for_sub, p.name.to_s )};\n" )
-                       params.add( name_for_sub )
-               end
+               var params = new Array[NiVariable]
+               if not method.is_init then params.add( signature.recv_ni_variable )
+               params.add_all( signature.params )
 
-               # call to nit
-               var return_type : nullable MMType = null
+               for nv in params do nv.prepare_for_nit( fc )
 
                # handles return of method or constructor
-               if method.signature.return_type != null then
-                       return_type = method.signature.return_type
-               else if method.is_init then
-                       return_type = method.local_class.get_type
+               var rnv = signature.return_ni_variable
+               if rnv == null and method.is_init then
+                       rnv = new ReturnVariable( signature.recv )
                end
-
                var s = new Buffer
-               if return_type != null then
-                       fc.decls.add( "{return_type.friendly_extern_name} result___nitni;\n" )
-                       fc.decls.add( "val_t result___nit;\n" )
-                       s.append( "result___nit = " )
+               if rnv != null then
+                       rnv.prepare_for_c( fc )
+                       s.append( "{rnv.ni_from_name} = " )
                end
 
                # hook to generated C code
                if method.is_init then
                        s.append( "NEW_{local_class}_{method.global.intro.cname}" )
                else
-                       s.append( "{method.global.meth_call}( recv___nit )" )
+                       s.append( "{method.global.meth_call}( {signature.recv_ni_variable.as_arg_to_nit} )" )
                end
 
-               s.append( "( {params.join( ", " )} );\n" )
+               var args = new Array[String]
+               for p in params do args.add( p.as_arg_to_nit )
+
+               s.append( "( {args.join( ", " )} );\n" )
 
                fc.exprs.add( s.to_s )
 
                # return
-               if return_type != null then
-                       var result_name_nitni = "result___nitni"
-                       var result_name_nit = "result___nit"
-
-                       fc.exprs.add( "{return_type.assign_to_friendly( result_name_nitni, result_name_nit )};\n" )
-                       fc.exprs.add( "return {result_name_nitni};\n" )
+               if rnv != null then
+                       fc.exprs.add( "{rnv.ni_type.assign_to_friendly( rnv.ni_to_name, rnv.ni_from_name )};\n" )
+                       fc.exprs.add( "return {rnv.ni_to_name};\n" )
                end
 
                v.body.append( fc.to_writer )