nitc/ffi: adds full FFI support for C to nitc
authorAlexis Laferrière <alexis.laf@xymus.net>
Wed, 6 Feb 2013 13:07:04 +0000 (08:07 -0500)
committerAlexis Laferrière <alexis.laf@xymus.net>
Wed, 6 Feb 2013 23:09:30 +0000 (18:09 -0500)
This commit adds all required features to use the nested C code, add it
to the compilation script and invoke it.

Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

src/compiling/compiling.nit
src/ffi/c.nit [new file with mode: 0644]
src/ffi/ffi.nit [new file with mode: 0644]
src/ffi/ffi_base.nit [new file with mode: 0644]
src/native_interface/frontier.nit
src/native_interface/native_interface.nit
src/nitc.nit
src/separate_options.nit

index d92473b..429505b 100644 (file)
@@ -115,6 +115,12 @@ redef class MMModule
                                native_body = native_name + "_nit.c"
                                if native_body.file_exists then cprogram.files.add(native_body)
                        end
+                       if uses_ffi then
+                               var ffi_header_name = "{cname}._ffi.h"
+                               v.add_decl("#include \"{ffi_header_name}\"")
+                               var ffi_body_name = "{cname}._ffi.c"
+                               cprogram.files.add( "{cprogram.compdir}/{ffi_body_name}" )
+                       end
                end
 
                declare_class_tables_to_c(v)
diff --git a/src/ffi/c.nit b/src/ffi/c.nit
new file mode 100644 (file)
index 0000000..329d9a3
--- /dev/null
@@ -0,0 +1,61 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012-2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Handles C inline code
+# Wraps all  code in a thin wrapper.
+module c
+
+import ffi_base
+
+redef class ExternCode
+       fun is_c : Bool do return language == null or
+               language_lowered == "c" or language_lowered.has_prefix( "c " )
+
+       fun is_c_body : Bool do return language == null or
+               language_lowered == "c" or language_lowered ==  "c body"
+
+       fun is_c_header : Bool do return language_lowered == "c header"
+
+       redef fun accept_ffi_visitor( v )
+       do
+               if is_c_header then
+                       v.compilation_unit.header_custom.add( code )
+               else if is_c_body then
+                       v.compilation_unit.body_custom.add( code )
+               end
+       end
+end
+
+redef class MMMethod
+       redef fun accept_ffi_visitor( v )
+       do
+               if extern_implementation.is_c then
+                       var fc = new CFunction( impl_csignature )
+                       fc.decls.add( extern_implementation.location.as_line_pragma )
+                       fc.exprs.add( extern_implementation.code )
+                       v.compilation_unit.add_exported_function( fc )
+               else
+                       super
+               end
+       end
+end
+
+redef class Location
+       fun as_line_pragma : String
+       do
+               return "#line {line_start} \"{file.filename}\"\n"
+       end
+end
diff --git a/src/ffi/ffi.nit b/src/ffi/ffi.nit
new file mode 100644 (file)
index 0000000..311f06d
--- /dev/null
@@ -0,0 +1,82 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012-2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This module implements the FFI with different languages
+module ffi
+
+import c
+
+redef class MMSrcModule
+       redef fun compile_separate_module(cprogram: CProgram)
+       do
+               super
+
+               if is_extern_hybrid then
+                       var visitor = new FFIVisitor( cprogram.program.tc, self )
+                       # TODO use cprogram to add generated files?
+
+                       # actually compile stub
+                       accept_ffi_visitor( visitor )
+
+                       # write to file
+                       if uses_ffi then
+                               visitor.compile
+                       end
+               end
+       end
+end
+
+redef class MMLocalClass
+       super FFIVisited
+end
+
+redef class FFIVisitor
+       fun compile
+       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
+       end
+end
diff --git a/src/ffi/ffi_base.nit b/src/ffi/ffi_base.nit
new file mode 100644 (file)
index 0000000..7b24be8
--- /dev/null
@@ -0,0 +1,126 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012-2013 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module ffi_base
+
+import native_interface
+import syntax # TODO move extern_implementation to MM
+import c_tools
+
+interface FFIVisited
+       fun accept_ffi_visitor( v : FFIVisitor ) do end
+end
+
+redef class ExternCode
+       super FFIVisited
+
+       # 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 )
+       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
+               end
+       end
+
+       var language_lowered : nullable String = null
+
+       redef init ( language, code, loc )
+       do
+               super
+               if language != null then language_lowered = language.to_lower
+       end
+end
+
+redef class MMMethod
+       super FFIVisited
+
+       # 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 )
+       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
+                       end
+               end
+       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 ]
+
+       # set of imported functions, cached to avoid repetitions
+       var supers : Set[ MMExplicitImport ] = new HashSet[ MMExplicitImport ]
+
+       # set of relevant types, cached to avoid repetitions
+       var types : Set[ MMType ] = new HashSet[ MMType ]
+
+       # set of imported casts and as, cached to avoid repetitions
+       var casts : Set[ MMImportedCast ] = new HashSet[ MMImportedCast ]
+end
+
+redef class MMLocalClass
+       super FFIVisited
+
+       fun c_type : nullable String do return null
+end
+
+redef class MMModule
+       super FFIVisited
+
+       redef fun accept_ffi_visitor( v )
+       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
+
+                       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
+
+               end
+
+               for block in extern_code_blocks do block.accept_ffi_visitor( v )
+       end
+end
+
+class FFIVisitor
+       var tc : ToolContext
+
+       # module being visited
+       var mmmodule : MMModule
+
+       var compilation_unit = new CCompilationUnit
+end
+
index 56e1bcf..b49782d 100644 (file)
@@ -146,6 +146,9 @@ redef class MMSrcModule
                        v.body.add( "#include \"{path}\"\n" )
                        v.header.add( "#include \"{path}\"\n" )
                end
+               if uses_ffi then
+                       v.header.add( "#include <{cname}._ffi.h>\n" )
+               end
 
                for local_class in local_classes do
                        ### extern methods
@@ -159,6 +162,8 @@ redef class MMSrcModule
                        end
                end
 
+               v.compile_cached
+
                v.header.add( "#endif\n" )
        end
 end
@@ -374,6 +379,7 @@ class FrontierVisitor
 
        var cprogram : CProgram
 
+       # compiles cached types and callbacks
        fun compile_cached
        do
                # types
index 1e29b3e..80a0fd0 100644 (file)
@@ -27,8 +27,6 @@ redef class MMSrcModule
                        var visitor = new FrontierVisitor( self, cprogram )
                        compile_frontier( visitor )
 
-                       visitor.compile_cached
-
                        var base_path = "{cprogram.compdir}/{name}"
                        visitor.write_to_files( base_path )
                end
index 803f637..6115520 100644 (file)
@@ -25,6 +25,7 @@ import compiling
 import syntax
 import native_interface
 import separate_options
+import ffi
 
 # The main class of the nitcompiler program
 class NitCompiler
index bdc336c..2096cc3 100644 (file)
@@ -22,6 +22,7 @@ import compiling
 
 # only to order correctly redefs of compile_separate_module
 import native_interface
+import ffi
 
 redef class ToolContext
        # all ops precised in .ops files