X-Git-Url: http://nitlanguage.org diff --git a/src/ffi/ffi.nit b/src/ffi/ffi.nit index cccb7a0..e58d32a 100644 --- a/src/ffi/ffi.nit +++ b/src/ffi/ffi.nit @@ -14,9 +14,28 @@ # See the License for the specific language governing permissions and # limitations under the License. -# FFI concers common between the compilers and the interpreter. -# Offers services to compile modules using foreign code. Mainly allows -# to wrap foreign code in Nit methods. +# Full FFI support, independent of the compiler +# +# The full FFI support all the features of the light FFI and more: +# +# * More foreign languages: **C++, Java and Objective-C**. +# * **Callbacks** to Nit from foreign codes. +# The callbacks are declared in Nit using the `import` annotation on extern methods. +# They are then generated on demand for the target foreign language. +# * **Static Nit types** in C for precise typing and static typing errors in C. +# * **Propagating public code blocks** at the module level (C Header). +# This allows to use extern classes in foreign code in other modules +# without having to import the related headers. +# This is optional in C as it is easy to find the correct importation. +# However it is important in Java and other complex FFIs. +# * **Reference pinning** of Nit objects from foreign code. +# This ensure that objects referenced from foreign code are not liberated by the GC. +# * FFI **annotations**: +# * `cflags`, `ldflags` and `cppflags` pass arguments to the C and C++ +# compilers and linker. +# * `pkgconfig` calls the `pkg-config` program to get the arguments +# to pass to the C copiler and linker. +# * `extra_java_files` adds Java source file to the compilation chain. module ffi import modelbuilder @@ -33,19 +52,11 @@ import cpp import java import extra_java_files import objc +intrude import light_ffi redef class MModule - # Does this module uses the FFI? - var uses_ffi: Bool = false - - # C compilation unit for the FFI files - private var ffi_ccu: nullable CCompilationUnit = null - - # Foreign language used in this AModule - private var present_languages = new HashSet[FFILanguage] - # Complete the compilation of the FFI code - fun finalize_ffi_wrapper(compdir: String, mainmodule: MModule) + redef fun finalize_ffi_wrapper(compdir, mainmodule) do for language in ffi_callbacks.keys do for callback in ffi_callbacks[language] do @@ -60,80 +71,10 @@ redef class MModule if mod.uses_ffi then ffi_ccu.header_custom.add("#include \"{mod.c_name}._ffi.h\"\n") end - ffi_ccu.write_as_impl(self, compdir) - for filename in ffi_ccu.files do - var f = new ExternCFile(filename, cflags) - f.pkgconfigs.add_all pkgconfigs - ffi_files.add(f) - end - end - - # Avoid the compile a ffi propdef more than once - # See `AMethPropdef::compile_ffi_method` - # FIXME find a better way - private var compiled_ffi_methods = new HashSet[AMethPropdef] -end - -redef class AModule - - # Ensures all of the general foreign code of the module has been analyzed. - # Manages header blocks, extern class types and foreign dependancies between modules - fun ensure_compile_ffi_wrapper - do - var mmodule = mmodule - if mmodule == null or mmodule.ffi_ccu != null then return - - # ready extern code compiler - var ffi_ccu = new CCompilationUnit - mmodule.ffi_ccu = ffi_ccu - - # generate code - for block in n_extern_code_blocks do - var language = block.language - assert language != null - mmodule.present_languages.add(language) - language.compile_module_block(block, ffi_ccu, mmodule) - end - - ffi_ccu.header_c_base.add( "#include \"{mmodule.c_name}._nitni.h\"\n" ) - - ffi_ccu.body_decl.add("#ifdef ANDROID\n") - ffi_ccu.body_decl.add(" #include \n") - ffi_ccu.body_decl.add(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)\n") - ffi_ccu.body_decl.add("#else\n") - ffi_ccu.body_decl.add(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)\n") - ffi_ccu.body_decl.add("#endif\n") - - for nclassdef in n_classdefs do - # Does it declares an extern type? - if nclassdef isa AStdClassdef and nclassdef.n_extern_code_block != null then - mmodule.uses_ffi = true - var language = nclassdef.n_extern_code_block.language - assert language != null - mmodule.present_languages.add(language) - nclassdef.n_extern_code_block.language.compile_extern_class( - nclassdef.n_extern_code_block.as(not null), nclassdef, ffi_ccu, mmodule) - end - end + super end end -redef class AMethPropdef - # Compile the necessary wrapper around this extern method or constructor - fun compile_ffi_method(mmodule: MModule) - do - assert n_extern_code_block != null - - if mmodule.compiled_ffi_methods.has(self) then return - mmodule.compiled_ffi_methods.add self - - var language = n_extern_code_block.language - assert language != null - mmodule.present_languages.add(language) - n_extern_code_block.language.compile_extern_method( - n_extern_code_block.as(not null), self, mmodule.ffi_ccu.as(not null), mmodule) - end -end redef class VerifyNitniCallbacksPhase redef fun process_npropdef(npropdef)