X-Git-Url: http://nitlanguage.org diff --git a/src/ffi/ffi_base.nit b/src/ffi/ffi_base.nit index 7b24be8..babab08 100644 --- a/src/ffi/ffi_base.nit +++ b/src/ffi/ffi_base.nit @@ -1,6 +1,6 @@ # This file is part of NIT ( http://www.nitlanguage.org ). # -# Copyright 2012-2013 Alexis Laferrière +# Copyright 2012 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,113 +14,192 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Tools and utilities for language targetted FFI implementations module ffi_base -import native_interface -import syntax # TODO move extern_implementation to MM import c_tools +import parser +import modelbuilder +import nitni -interface FFIVisited - fun accept_ffi_visitor( v : FFIVisitor ) do end +redef class ToolContext + var ffi_language_assignation_phase: Phase = new FFILanguageAssignationPhase(self, null) end -redef class ExternCode - super FFIVisited +class FFILanguageAssignationPhase + super Phase - # last resort for extern method - # assumes this is a code block in the root of a module - # should never be called if the extern method is implemented inline by a known language - redef fun accept_ffi_visitor( v ) + # All supported languages + var languages = new Array[FFILanguage] + + redef fun process_nmodule(nmodule) do - var language = self.language - if language != null then - v.tc.warning( null, "language \"{language}\" used to implement a code block in {v.mmmodule} is unknown." ) - else - v.tc.warning( null, "please specify a language to implement code blocks in {v.mmmodule}." ) # is ok with spec as of now, won't be raised + for block in nmodule.n_extern_code_blocks do + verify_foreign_code_on_node( block ) end end - var language_lowered : nullable String = null - - redef init ( language, code, loc ) + redef fun process_npropdef(npropdef) do - super - if language != null then language_lowered = language.to_lower + if npropdef isa AMethPropdef then + var code_block = npropdef.n_extern_code_block + if code_block != null then + verify_foreign_code_on_node( code_block ) + end + end end -end -redef class MMMethod - super FFIVisited + redef fun process_nclassdef(nclassdef) + do + if nclassdef isa AStdClassdef and nclassdef.n_extern_code_block != null then + verify_foreign_code_on_node( nclassdef.n_extern_code_block.as(not null) ) + end + end - # last resort for extern method - # should never be called if the extern method is implemented inline by a known language - redef fun accept_ffi_visitor( v ) + private fun verify_foreign_code_on_node(n: AExternCodeBlock) do - if extern_implementation != null then - var language = extern_implementation.language - if language != null then - v.tc.warning( null, "language \"{language}\" used to implement {full_name} is unknown." ) - else - v.tc.warning( null, "please specify a language to implement {full_name}." ) # is ok with spec as of now, won't be raised + var found = false + for v in languages do + var identified = v.identify_language(n) + if identified then + if found and identified then + toolcontext.error(n.location, "Two languages identified as possible handlers.") + end + n.language = v + found = true end end + + if not found then toolcontext.error(n.location, "Unsupported language.") end end -# set of extern imports in a language to be used by a module -# FIXME move to native_interface or MM? -class ExternImportSet - # set of imported functions, cached to avoid repetitions - var callbacks : Set[ MMExplicitImport ] = new HashSet[ MMExplicitImport ] +redef class MModule + var ffi_files = new Array[ExternFile] - # set of imported functions, cached to avoid repetitions - var supers : Set[ MMExplicitImport ] = new HashSet[ MMExplicitImport ] + # Callbacks used by this module, classified by language + var ffi_callbacks = new HashMap[FFILanguage, Set[NitniCallback]] +end - # set of relevant types, cached to avoid repetitions - var types : Set[ MMType ] = new HashSet[ MMType ] +redef class AExternCodeBlock + fun language_name: nullable String do + if n_in_language == null then return null + return n_in_language.n_string.without_quotes + end + fun language_name_lowered: nullable String do + if language_name == null then return null + return language_name.to_lower + end - # set of imported casts and as, cached to avoid repetitions - var casts : Set[ MMImportedCast ] = new HashSet[ MMImportedCast ] + fun code: String do return n_extern_code_segment.without_guard + + var language: nullable FFILanguage = null end -redef class MMLocalClass - super FFIVisited +# Visitor for a specific languages. Works kinda like a `Phase` and is executed +# by a `Phase`. +class FFILanguage + var ffi_language_assignation_phase: FFILanguageAssignationPhase + + init + do + ffi_language_assignation_phase.languages.add(self) + end + + # Is this `block` written in this language? + fun identify_language(block: AExternCodeBlock ): Bool is abstract + + # Generate wrapper code for this module/header code block + fun compile_module_block(block: AExternCodeBlock, ecc: CCompilationUnit, mmodule: MModule) is abstract - fun c_type : nullable String do return null + # Generate wrapper code for this extern method + fun compile_extern_method(block: AExternCodeBlock, m: AMethPropdef, + ecc: CCompilationUnit, nmodule: MModule) is abstract + + # Generate wrapper code for this extern class + fun compile_extern_class(block: AExternCodeBlock, m: AClassdef, + ecc: CCompilationUnit, mmodule: MModule) is abstract + + # Get the foreign type of this extern class definition + fun get_ftype(block: AExternCodeBlock, m: AClassdef): ForeignType is abstract + + # Generate the code to offer this callback if foreign code + fun compile_callback(callback: NitniCallback, mmodule: MModule, + mainmmodule: MModule, ecc: CCompilationUnit) is abstract + + # Complete compilation of generated code + fun compile_to_files(mmodule: MModule, directory: String) do end end -redef class MMModule - super FFIVisited +redef class TString + # Returns the content of this node without both of the surrounding " + fun without_quotes: String + do + assert text.length >= 2 + return text.substring(1, text.length-2) + end +end - redef fun accept_ffi_visitor( v ) +redef class TExternCodeSegment + # Returns the content of this node without the surrounding `{ and `} + fun without_guard: String do - for local_class in local_classes do - # if class is extern and defined here first - if local_class.global.intro == local_class and - local_class.global.is_extern then - local_class.accept_ffi_visitor( v ) - end + assert text.length >= 4 + return text.substring(2, text.length-4) + end +end - for prop in local_class.local_local_properties do - # if defined of redefined in this module and is extern - if prop.mmmodule == self and prop isa MMMethod and prop.is_extern and - prop.extern_implementation != null then - prop.accept_ffi_visitor( v ) - end - end +redef class CCompilationUnit + fun write_as_impl(mmodule: MModule, compdir: String) + do + var base_name = "{mmodule.c_name}._ffi" - end + var h_file = "{base_name}.h" + var guard = "{mmodule.c_name.to_upper}_NIT_H" + write_header_to_file(mmodule, "{compdir}/{h_file}", new Array[String], guard) + + var c_file = "{base_name}.c" + write_body_to_file(mmodule, "{compdir}/{c_file}", ["", "", "\"{h_file}\""]) - for block in extern_code_blocks do block.accept_ffi_visitor( v ) + files.add( "{compdir}/{c_file}" ) end -end -class FFIVisitor - var tc : ToolContext + fun write_header_to_file(mmodule: MModule, file: String, includes: Array[String], guard: String) + do + var stream = new OFStream.open( file ) + + # header comments + var module_info = "/*\n\tExtern implementation of Nit module {mmodule.name}\n*/\n" - # module being visited - var mmmodule : MMModule + stream.write( module_info ) - var compilation_unit = new CCompilationUnit + stream.write( "#ifndef {guard}\n" ) + stream.write( "#define {guard}\n\n" ) + + for incl in includes do stream.write( "#include {incl}\n" ) + + compile_header_core( stream ) + + # header file guard close + stream.write( "#endif\n" ) + stream.close + end + + fun write_body_to_file(mmodule: MModule, file: String, includes: Array[String]) + do + var stream = new OFStream.open(file) + + var module_info = "/*\n\tExtern implementation of Nit module {mmodule.name}\n*/\n" + + stream.write( module_info ) + for incl in includes do stream.write( "#include {incl}\n" ) + + compile_body_core( stream ) + + stream.close + end end +class ForeignType + fun ctype: String do return "void*" +end