X-Git-Url: http://nitlanguage.org diff --git a/src/compiler_ffi.nit b/src/compiler_ffi.nit index efb3fec..4c55f65 100644 --- a/src/compiler_ffi.nit +++ b/src/compiler_ffi.nit @@ -21,42 +21,71 @@ intrude import abstract_compiler intrude import common_ffi import nitni -redef class AModule +redef class MModule private var foreign_callbacks = new ForeignCallbackSet var nitni_ccu: nullable CCompilationUnit = null - redef var uses_legacy_ni: Bool = false - - redef fun finalize_ffi(v: AbstractCompilerVisitor, modelbuilder: ModelBuilder) + private fun nmodule(v: AbstractCompilerVisitor): nullable AModule do - finalize_ffi_wrapper(".nit_compile", v.compiler.mainmodule) - for file in ffi_files do v.compiler.extern_bodies.add(file) + var m2n = v.compiler.modelbuilder.mmodule2nmodule + return m2n.get_or_null(self) end - fun ensure_compile_nitni_base(v: AbstractCompilerVisitor) + redef fun finalize_ffi(compiler: AbstractCompiler) do - if nitni_ccu != null then return + if not uses_ffi then return - nitni_ccu = new CCompilationUnit - end + var v = compiler.new_visitor + var n = nmodule(v) + if n == null then return + n.ensure_compile_ffi_wrapper + finalize_ffi_wrapper(v.compiler.modelbuilder.compile_dir, v.compiler.mainmodule) + for file in ffi_files do v.compiler.extern_bodies.add(file) - redef fun finalize_nitni(v: AbstractCompilerVisitor) - do ensure_compile_nitni_base(v) - nitni_ccu.header_c_types.add("#include \"{mmodule.name}._ffi.h\"\n") + nitni_ccu.header_c_types.add("#include \"{name}._ffi.h\"\n") + nitni_ccu.header_c_types.add """ +extern void nitni_global_ref_incr(void*); +extern void nitni_global_ref_decr(void*); +""" - nitni_ccu.write_as_nitni(self, ".nit_compile") + nitni_ccu.write_as_nitni(self, v.compiler.modelbuilder.compile_dir) for file in nitni_ccu.files do v.compiler.extern_bodies.add(new ExternCFile(file, c_compiler_options)) end + + # reset FFI things so the next compilation job, if any, starts with a clean context + # FIXME clean and rationalize this + nitni_ccu = null + compiled_ffi_methods.clear + ffi_ccu = null + ffi_files.clear + compiled_callbacks.clear + #Do not reset `foreign_callbacks` and `ffi_callbacks` because they are computed in previous phases + end + + private fun ensure_compile_nitni_base(v: AbstractCompilerVisitor) + do + if nitni_ccu != null then return + + nitni_ccu = new CCompilationUnit end - var compiled_callbacks: Array[NitniCallback] = new Array[NitniCallback] + redef fun collect_linker_libs + do + var s = c_linker_options + if s.is_empty then return null + var res = new ArraySet[String] + res.add s + return res + end + + private var compiled_callbacks: Array[NitniCallback] = new Array[NitniCallback] # Returns true if callbacks has yet to be generated and register it as being generated - fun check_callback_compilation(cb: NitniCallback): Bool + private fun check_callback_compilation(cb: NitniCallback): Bool do var compiled = compiled_callbacks.has(cb) if not compiled then compiled_callbacks.add(cb) @@ -64,11 +93,12 @@ redef class AModule end end -redef class AExternPropdef - fun compile_ffi_support_to_c(v: AbstractCompilerVisitor) +redef class AMethPropdef + private fun compile_ffi_support_to_c(v: AbstractCompilerVisitor) do var mmodule = mpropdef.mclassdef.mmodule - var amainmodule = v.compiler.modelbuilder.mmodule2nmodule[v.compiler.mainmodule] + var mainmodule = v.compiler.mainmodule + var amainmodule = v.compiler.modelbuilder.mmodule2nmodule[mainmodule] var amodule = v.compiler.modelbuilder.mmodule2nmodule[mmodule] var mclass_type = mpropdef.mclassdef.bound_mtype @@ -77,54 +107,53 @@ redef class AExternPropdef v.declare_once("{csignature};") # FFI part - compile_ffi_method(amodule) + amodule.ensure_compile_ffi_wrapper + compile_ffi_method(mmodule) + + assert self isa AExternPropdef # nitni - Compile missing callbacks - amodule.ensure_compile_nitni_base(v) - var ccu = amodule.nitni_ccu.as(not null) + mmodule.ensure_compile_nitni_base(v) + var ccu = mmodule.nitni_ccu.as(not null) for mtype in foreign_callbacks.types do if not mtype.is_cprimitive then mtype.compile_extern_type(v, ccu) # has callbacks already been compiled? (this may very well happen with global compilation) - if amodule.check_callback_compilation(mtype) then mtype.compile_extern_helper_functions(v, ccu) + if mmodule.check_callback_compilation(mtype) then mtype.compile_extern_helper_functions(v, ccu) end end # compile callbacks - for cb in foreign_callbacks.callbacks do if amainmodule.check_callback_compilation(cb) then + for cb in foreign_callbacks.callbacks do if mainmodule.check_callback_compilation(cb) then cb.compile_extern_callback(v, ccu) end - for cb in foreign_callbacks.supers do if amainmodule.check_callback_compilation(cb) then + for cb in foreign_callbacks.supers do if mainmodule.check_callback_compilation(cb) then cb.compile_extern_callback(v, ccu) end - for cb in foreign_callbacks.casts do if amainmodule.check_callback_compilation(cb) then + for cb in foreign_callbacks.casts do if mainmodule.check_callback_compilation(cb) then cb.compile_extern_callbacks(v, ccu) end # manage nitni callback set - amodule.foreign_callbacks.join(foreign_callbacks) + mmodule.foreign_callbacks.join(foreign_callbacks) end -end -redef class AExternMethPropdef - redef fun compile_to_c(v, mpropdef, arguments) + redef fun compile_externmeth_to_c(v, mpropdef, arguments) do var mmodule = mpropdef.mclassdef.mmodule - var amodule = v.compiler.modelbuilder.mmodule2nmodule[mmodule] # if using the old native interface fallback on previous implementation var nextern = self.n_extern if nextern != null then - amodule.uses_legacy_ni = true super return end - amodule.mmodule.uses_ffi = true + mmodule.uses_ffi = true var mclass_type = mpropdef.mclassdef.bound_mtype @@ -138,6 +167,7 @@ redef class AExternMethPropdef 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 @@ -153,7 +183,7 @@ redef class AExternMethPropdef arguments_for_c.add(arg.name) else v.add("struct nitni_instance* var_for_c_{a};") - v.add("var_for_c_{a} = malloc(sizeof(struct nitni_instance));") + v.add("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));") v.add("var_for_c_{a}->value = {arg.name};") arguments_for_c.add("var_for_c_{a}") end @@ -170,28 +200,25 @@ redef class AExternMethPropdef 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 compile_ffi_support_to_c(v) end -end -redef class AExternInitPropdef - redef fun compile_to_c(v, mpropdef, arguments) + redef fun compile_externinit_to_c(v, mpropdef, arguments) do var mmodule = mpropdef.mclassdef.mmodule - var amodule = v.compiler.modelbuilder.mmodule2nmodule[mmodule] # if using the old native interface fallback on previous implementation var nextern = self.n_extern if nextern != null then - amodule.uses_legacy_ni = true super return end - amodule.mmodule.uses_ffi = true + mmodule.uses_ffi = true var mclass_type = mpropdef.mclassdef.bound_mtype @@ -200,6 +227,7 @@ redef class AExternInitPropdef var recv_var = v.new_var(return_mtype) v.adapt_signature(mpropdef, arguments) + v.unbox_signature_extern(mpropdef, arguments) arguments.shift @@ -214,7 +242,7 @@ redef class AExternInitPropdef arguments_for_c.add(arg.name) else v.add("struct nitni_instance* var_for_c_{a};") - v.add("var_for_c_{a} = malloc(sizeof(struct nitni_instance));") + v.add("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));") v.add("var_for_c_{a}->value = {arg.name};") arguments_for_c.add("var_for_c_{a}") end @@ -227,6 +255,7 @@ redef class AExternInitPropdef 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) @@ -234,21 +263,27 @@ redef class AExternInitPropdef end redef class CCompilationUnit - fun write_as_nitni(amodule: AModule, compdir: String) + fun write_as_nitni(mmodule: MModule, compdir: String) do - var base_name = "{amodule.mmodule.name}._nitni" + var base_name = "{mmodule.name}._nitni" var h_file = "{base_name}.h" - write_header_to_file( amodule, "{compdir}/{h_file}", new Array[String], - "{amodule.cname.to_s.to_upper}_NITG_NITNI_H") + write_header_to_file( mmodule, "{compdir}/{h_file}", new Array[String], + "{mmodule.cname.to_s.to_upper}_NITG_NITNI_H") var c_file = "{base_name}.c" - write_body_to_file( amodule, "{compdir}/{c_file}", ["\"{h_file}\""] ) + write_body_to_file( mmodule, "{compdir}/{c_file}", ["\"{h_file}\""] ) files.add( "{compdir}/{c_file}" ) end end +redef class AbstractCompiler + # Cache to avoid multiple compilation of NULL values + # see FIXME in `MNullableType#compile_extern_helper_functions` + private var compiled_null_types = new Array[MNullableType] +end + redef class AbstractCompilerVisitor # Create a `RuntimeVariable` for this C variable originating from C user code private fun var_from_c(name: String, mtype: MType): RuntimeVariable @@ -267,7 +302,7 @@ redef class AbstractCompilerVisitor add("return {src};") else add("struct nitni_instance* ret_for_c;") - add("ret_for_c = malloc(sizeof(struct nitni_instance));") + add("ret_for_c = nit_alloc(sizeof(struct nitni_instance));") add("ret_for_c->value = {src};") add("return ret_for_c;") end @@ -275,7 +310,7 @@ redef class AbstractCompilerVisitor end redef class MType - fun compile_extern_type(v: AbstractCompilerVisitor, ccu: CCompilationUnit) + private fun compile_extern_type(v: AbstractCompilerVisitor, ccu: CCompilationUnit) do assert not is_cprimitive @@ -286,17 +321,20 @@ redef class MType ccu.header_c_types.add("#endif\n") end - fun compile_extern_helper_functions(v: AbstractCompilerVisitor, ccu: CCompilationUnit) + private fun compile_extern_helper_functions(v: AbstractCompilerVisitor, ccu: CCompilationUnit) do # actually, we do not need to do anything when using the bohem garbage collector + var call_context = from_c_call_context # incr_ref - var nitni_visitor = v.compiler.new_visitor - ccu.header_decl.add("#define {mangled_cname}_incr_ref(from) while(0)\{\}\n") - - # incr_ref - nitni_visitor = v.compiler.new_visitor - ccu.header_decl.add("#define {mangled_cname}_decr_ref(from) while(0)\{\}\n") + ccu.header_decl.add "#ifndef {mangled_cname}_incr_ref\n" + ccu.header_decl.add " #define {mangled_cname}_incr_ref(from) nitni_global_ref_incr(({call_context.name_mtype(self)})(from))\n" + ccu.header_decl.add "#endif\n" + + # decr_ref + ccu.header_decl.add "#ifndef {mangled_cname}_decr_ref\n" + ccu.header_decl.add " #define {mangled_cname}_decr_ref(from) nitni_global_ref_decr(({call_context.name_mtype(self)})(from))\n" + ccu.header_decl.add "#endif\n" end end @@ -305,32 +343,37 @@ redef class MNullableType do super + var base_cname = "null_{mtype.mangled_cname}" + var full_cname = "NIT_NULL___{base_cname}" + # In nitni files, declare internal function as extern - var full_friendly_csignature = "{cname} {v.compiler.mainmodule.name}___null_{mtype.mangled_cname}()" + var full_friendly_csignature = "{cname_blind} {full_cname}()" ccu.header_decl.add("extern {full_friendly_csignature};\n") # In nitni files, #define friendly as extern - var base_cname = "null_{mtype.mangled_cname}" - ccu.header_decl.add("#define {base_cname} {v.compiler.mainmodule.name}___{base_cname}\n") + ccu.header_decl.add("#define {base_cname} {full_cname}\n") + + # FIXME: This is ugly an broke the separate compilation principle + # The real function MUST be compiled only once, #define pragma only protect the compiler, not the loader + # However, I am not sure of the right approach here (eg. week refs are ugly) + if v.compiler.compiled_null_types.has(self) then return + v.compiler.compiled_null_types.add self # Internally, implement internal function var nitni_visitor = v.compiler.new_visitor nitni_visitor.frame = v.frame - var full_internal_csignature = "{cname_blind} {v.compiler.mainmodule.name}___null_{mtype.mangled_cname}()" - nitni_visitor.add("#ifndef NIT_NULL_null_{mtype.mangled_cname}") - nitni_visitor.add("#define NIT_NULL_null_{mtype.mangled_cname}") + var full_internal_csignature = "{cname_blind} {full_cname}()" nitni_visitor.add("{full_internal_csignature} \{") nitni_visitor.add("struct nitni_instance* ret_for_c;") - nitni_visitor.add("ret_for_c = malloc(sizeof(struct nitni_instance));") + nitni_visitor.add("ret_for_c = nit_alloc(sizeof(struct nitni_instance));") nitni_visitor.add("ret_for_c->value = NULL;") nitni_visitor.add("return ret_for_c;") nitni_visitor.add("\}") - nitni_visitor.add("#endif") end end redef class MExplicitCall - fun compile_extern_callback(v: AbstractCompilerVisitor, ccu: CCompilationUnit) + private fun compile_extern_callback(v: AbstractCompilerVisitor, ccu: CCompilationUnit) do var mproperty = mproperty assert mproperty isa MMethod @@ -342,7 +385,7 @@ redef class MExplicitCall # Internally, implement internal function var nitni_visitor = v.compiler.new_visitor nitni_visitor.frame = v.frame - var msignature = mproperty.intro.msignature + var msignature = mproperty.lookup_first_definition(v.compiler.mainmodule, recv_mtype).msignature var csignature_blind = mproperty.build_csignature(recv_mtype, v.compiler.mainmodule, null, long_signature, internal_call_context) nitni_visitor.add_decl("/* nitni callback for {mproperty.full_name} */") @@ -352,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) @@ -370,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 @@ -383,6 +424,8 @@ redef class MExplicitCall if return_mtype != null then 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("\}") @@ -390,7 +433,7 @@ redef class MExplicitCall end redef class MExplicitSuper - fun compile_extern_callback(v: AbstractCompilerVisitor, ccu: CCompilationUnit) + private fun compile_extern_callback(v: AbstractCompilerVisitor, ccu: CCompilationUnit) do var mproperty = from.mproperty assert mproperty isa MMethod @@ -398,7 +441,7 @@ redef class MExplicitSuper var mmodule = from.mclassdef.mmodule # In nitni files, declare internal function as extern - var internal_csignature = mproperty.build_csignature(mclass_type, v.compiler.mainmodule, null, long_signature, from_c_call_context) + var internal_csignature = mproperty.build_csignature(mclass_type, v.compiler.mainmodule, "___super", long_signature, internal_call_context) ccu.header_decl.add("extern {internal_csignature};\n") # In nitni files, #define friendly as extern @@ -409,7 +452,7 @@ redef class MExplicitSuper # Internally, implement internal function var nitni_visitor = v.compiler.new_visitor nitni_visitor.frame = v.frame - var msignature = mproperty.intro.msignature + var msignature = mproperty.lookup_first_definition(v.compiler.mainmodule, mclass_type).msignature var csignature_blind = mproperty.build_csignature(mclass_type, v.compiler.mainmodule, "___super", long_signature, internal_call_context) @@ -419,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 @@ -433,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("\}") @@ -440,7 +487,7 @@ redef class MExplicitSuper end redef class MExplicitCast - fun compile_extern_callbacks(v: AbstractCompilerVisitor, ccu: CCompilationUnit) + private fun compile_extern_callbacks(v: AbstractCompilerVisitor, ccu: CCompilationUnit) do var from = from var to = to @@ -460,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};") @@ -473,7 +523,7 @@ redef class MExplicitCast # special checks if from == to.as_nullable then # format A_is_null - ccu.header_decl.add("#define {from.mangled_cname}_is_null {from.mangled_cname}_is_a_{to.mangled_cname}\n") + ccu.header_decl.add("#define {from.mangled_cname}_is_null !{from.mangled_cname}_is_a_{to.mangled_cname}\n") end # @@ -481,7 +531,7 @@ redef class MExplicitCast # # In nitni files, declare internal function as extern - full_friendly_csignature = "{to.cname} {v.compiler.mainmodule.name }___{from.mangled_cname}_as_{to.mangled_cname}({from.cname_blind})" + full_friendly_csignature = "{to.cname_blind} {v.compiler.mainmodule.name }___{from.mangled_cname}_as_{to.mangled_cname}({from.cname_blind})" ccu.header_decl.add("extern {full_friendly_csignature};\n") # In nitni files, #define friendly as extern @@ -491,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") @@ -505,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)