1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2012 Alexis Laferrière <alexis.laf@xymus.net>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Tools and utilities for implement FFI with different languages
23 import nitni
::nitni_utilities
25 redef class ToolContext
26 # Phase that assign a `FFILanguage` to all `AExternCodeBlock`
27 var ffi_language_assignation_phase
: Phase = new FFILanguageAssignationPhase(self, null)
30 # Phase that assign a `FFILanguage` to all `AExternCodeBlock`
32 # It will also report errors when using an unknown foreign langages.
33 class FFILanguageAssignationPhase
36 # All supported languages
37 var languages
= new Array[FFILanguage]
39 redef fun process_nmodule
(nmodule
)
41 for block
in nmodule
.n_extern_code_blocks
do
42 verify_foreign_code_on_node
( block
)
46 redef fun process_npropdef
(npropdef
)
48 if npropdef
isa AMethPropdef then
49 var code_block
= npropdef
.n_extern_code_block
50 if code_block
!= null then
51 verify_foreign_code_on_node
( code_block
)
56 redef fun process_nclassdef
(nclassdef
)
58 if nclassdef
isa AStdClassdef and nclassdef
.n_extern_code_block
!= null then
59 verify_foreign_code_on_node
( nclassdef
.n_extern_code_block
.as(not null) )
63 private fun verify_foreign_code_on_node
(n
: AExternCodeBlock)
67 var identified
= v
.identify_language
(n
)
69 if found
and identified
then
70 toolcontext
.error
(n
.location
, "FFI Error: two languages identified as possible handlers.")
77 if not found
then toolcontext
.error
(n
.location
, "FFI Error: unsupported language.")
82 # All FFI files linked to this module
83 var ffi_files
= new Array[ExternFile]
86 redef class AExternCodeBlock
87 # User entered name for the language of this block
88 fun language_name
: nullable String do
89 if n_in_language
== null then return null
90 return n_in_language
.n_string
.without_quotes
93 # `language_name`, in lower case
94 protected fun language_name_lowered
: nullable String do
95 if language_name
== null then return null
96 return language_name
.to_lower
99 # User entered foreign code in the block
100 fun code
: String do return n_extern_code_segment
.without_guard
102 # `FFILanguage` assigned to this block
103 var language
: nullable FFILanguage = null
106 # Visitor for a specific languages. Works kinda like a `Phase` and is executed
109 # `FFILanguageAssignationPhase` assigning `self` to `AExternCodeBlock`s
110 var ffi_language_assignation_phase
: FFILanguageAssignationPhase
114 ffi_language_assignation_phase
.languages
.add
(self)
117 # Is this `block` written in this language?
118 fun identify_language
(block
: AExternCodeBlock ): Bool is abstract
120 # Generate wrapper code for this module/header code block
121 fun compile_module_block
(block
: AExternCodeBlock, ecc
: CCompilationUnit, mmodule
: MModule) is abstract
123 # Generate wrapper code for this extern method
124 fun compile_extern_method
(block
: AExternCodeBlock, m
: AMethPropdef,
125 ecc
: CCompilationUnit, nmodule
: MModule) is abstract
127 # Generate wrapper code for this extern class
128 fun compile_extern_class
(block
: AExternCodeBlock, m
: AClassdef,
129 ecc
: CCompilationUnit, mmodule
: MModule) is abstract
131 # Get the foreign type of this extern class definition
132 fun get_ftype
(block
: AExternCodeBlock, m
: AClassdef): ForeignType is abstract
134 # Complete compilation of generated code
135 fun compile_to_files
(mmodule
: MModule, directory
: String) do end
139 # Returns the content of this node without both of the surrounding "
140 fun without_quotes
: String
142 assert text
.length
>= 2
143 return text
.substring
(1, text
.length-2
)
147 redef class TExternCodeSegment
148 # Returns the content of this node without the surrounding `{ and `}
149 fun without_guard
: String
151 assert text
.length
>= 4
152 return text
.substring
(2, text
.length-4
)
156 redef class CCompilationUnit
157 # Compile as `_ffi` files which contains the implementation of extern methods
158 fun write_as_impl
(mmodule
: MModule, compdir
: String)
160 var base_name
= "{mmodule.c_name}._ffi"
162 var h_file
= "{base_name}.h"
163 var guard
= "{mmodule.c_name.to_upper}_NIT_H"
164 write_header_to_file
(mmodule
, "{compdir}/{h_file}", new Array[String], guard
)
166 var c_file
= "{base_name}.c"
167 write_body_to_file
(mmodule
, "{compdir}/{c_file}", ["<stdlib.h>", "<stdio.h>", "\"{h_file}\
""])
169 files
.add
( "{compdir}/{c_file}" )
172 # Write the header part to `file` including all `includes` using the `guard`
173 fun write_header_to_file
(mmodule
: MModule, file
: String, includes
: Array[String], guard
: String)
175 var stream
= new FileWriter.open
( file
)
178 var module_info
= "/*\n\tExtern implementation of Nit module {mmodule.name}\n*/\n"
180 stream
.write
( module_info
)
182 stream
.write
( "#ifndef {guard}\n" )
183 stream
.write
( "#define {guard}\n\n" )
185 for incl
in includes
do stream
.write
( "#include {incl}\n" )
187 compile_header_core
( stream
)
189 # header file guard close
190 stream
.write
( "#endif\n" )
194 # Write the body part to `file` including all `includes`
195 fun write_body_to_file
(mmodule
: MModule, file
: String, includes
: Array[String])
197 var stream
= new FileWriter.open
(file
)
199 var module_info
= "/*\n\tExtern implementation of Nit module {mmodule.name}\n*/\n"
201 stream
.write
( module_info
)
202 for incl
in includes
do stream
.write
( "#include {incl}\n" )
204 compile_body_core
( stream
)
210 # Foreign equivalent types of extern classes
212 # C type of `self`, by default it is `void*`
213 fun ctype
: String do return "void*"