X-Git-Url: http://nitlanguage.org diff --git a/src/ffi/ffi.nit b/src/ffi/ffi.nit index aa85bbd..93acf66 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,65 +14,147 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This module implements the FFI with different languages +# 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. 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 + +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] -redef class MMSrcModule - redef fun compile_separate_module(cprogram: CProgram) + # Complete the compilation of the FFI code + fun finalize_ffi_wrapper(compdir: String, mainmodule: MModule) do - super + 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 + + language.compile_to_files(self, compdir) + end - if is_extern_hybrid then - var visitor = new FFIVisitor( cprogram.program.tc, self ) - # TODO use cprogram to add generated files? + # 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 + + var cflags = self.cflags[""].join(" ") + + 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 - # actually compile stub - accept_ffi_visitor( visitor ) + # 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 - # write to file - if uses_ffi then - visitor.compile + # 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 end end -redef class FFIVisitor - fun compile +redef class AMethPropdef + # Compile the necessary wrapper around this extern method or constructor + fun compile_ffi_method(mmodule: MModule) 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" - - # header comments - var module_info = "/*\n\tExtern implementation of Nit module {mmmodule.name}\n*/\n" - - # header file guard - var guard = "{mmmodule.cname.to_s.to_upper}_NIT_H" - - # .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 - - # .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 + 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) + do + super + + if not npropdef isa AMethPropdef then return + + var code_block = npropdef.n_extern_code_block + if code_block == null then return + + var lang = code_block.language + assert lang != null + + # 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