X-Git-Url: http://nitlanguage.org diff --git a/src/ffi/ffi.nit b/src/ffi/ffi.nit index 311f06d..e58d32a 100644 --- a/src/ffi/ffi.nit +++ b/src/ffi/ffi.nit @@ -1,6 +1,6 @@ # This file is part of NIT ( http://www.nitlanguage.org ). # -# Copyright 2012-2013 Alexis Laferrière +# Copyright 2013 Alexis Laferrière # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,69 +14,86 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This module implements the FFI with different languages +# 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 + +import nitni + +intrude import ffi_base +import extern_classes +import header_dependency +import pkgconfig +import c_compiler_options import c +import cpp +import java +import extra_java_files +import objc +intrude import light_ffi -redef class MMSrcModule - redef fun compile_separate_module(cprogram: CProgram) +redef class MModule + # Complete the compilation of the FFI code + redef fun finalize_ffi_wrapper(compdir, mainmodule) do - super - - if is_extern_hybrid then - var visitor = new FFIVisitor( cprogram.program.tc, self ) - # TODO use cprogram to add generated files? + for language in ffi_callbacks.keys do + for callback in ffi_callbacks[language] do + language.compile_callback(callback, self, mainmodule, ffi_ccu.as(not null)) + end - # actually compile stub - accept_ffi_visitor( visitor ) + language.compile_to_files(self, compdir) + end - # write to file - if uses_ffi then - visitor.compile - end + # include dependancies FFI + for mod in header_dependencies do + if mod.uses_ffi then ffi_ccu.header_custom.add("#include \"{mod.c_name}._ffi.h\"\n") end + + super end end -redef class MMLocalClass - super FFIVisited -end -redef class FFIVisitor - fun compile +redef class VerifyNitniCallbacksPhase + redef fun process_npropdef(npropdef) do - var compdir = tc.compdir.as(not null) - var base_name = "{mmmodule.cname}._ffi" - var c_file = "{base_name}.c" - var h_file = "{base_name}.h" + super - # header comments - var module_info = "/*\n\tExtern implementation of Nit module {mmmodule.name}\n*/\n" + if not npropdef isa AMethPropdef then return - # header file guard - var guard = "{mmmodule.cname.to_s.to_upper}_NIT_H" + var code_block = npropdef.n_extern_code_block + if code_block == null then return - # .h - var stream = new OFStream.open( "{compdir}/{h_file}" ) - stream.write( module_info ) - stream.write( "#include <{mmmodule.name}._nitni.h>\n\n" ) - stream.write( "#ifndef {guard}\n" ) - stream.write( "#define {guard}\n\n" ) - compilation_unit.header_c_base.write_to_stream( stream ) - compilation_unit.header_custom.write_to_stream( stream ) - compilation_unit.header_c_types.write_to_stream( stream ) - compilation_unit.header_decl.write_to_stream( stream ) - stream.write( "#endif\n" ) - stream.close + var lang = code_block.language + assert lang != null - # .c - stream = new OFStream.open( "{compdir}/{c_file}" ) - stream.write( module_info ) - stream.write( "#include \"{h_file}\"\n" ) - compilation_unit.body_decl.write_to_stream( stream ) - compilation_unit.body_custom.write_to_stream( stream ) - compilation_unit.body_impl.write_to_stream( stream ) - stream.close + # Associate callbacks used by an extern method to its foreign language + for callback in npropdef.foreign_callbacks.all do + var map = npropdef.mpropdef.mclassdef.mmodule.ffi_callbacks + if not map.keys.has(lang) then map[lang] = new HashSet[NitniCallback] + map[lang].add(callback) + end end end