1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013-2015 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 # FFI support for the compilers
20 intrude import abstract_compiler
21 intrude import ffi
::light_ffi
24 var nitni_ccu
: nullable CCompilationUnit = null
26 private fun nmodule
(v
: AbstractCompilerVisitor): nullable AModule
28 return v
.compiler
.modelbuilder
.mmodule2node
(self)
31 redef fun finalize_ffi
(compiler
: AbstractCompiler)
33 if not uses_ffi
then return
35 var v
= compiler
.new_visitor
37 if n
== null then return
38 n
.ensure_compile_ffi_wrapper
39 finalize_ffi_wrapper
(v
.compiler
.modelbuilder
.compile_dir
, v
.compiler
.mainmodule
)
40 for file
in ffi_files
do v
.compiler
.extern_bodies
.add
(file
)
42 ensure_compile_nitni_base
(v
)
44 nitni_ccu
.header_c_types
.add
("#include \"{c_name}._ffi
.h\
"\n")
45 nitni_ccu
.header_c_types
.add
"""
46 extern void nitni_global_ref_incr(void*);
47 extern void nitni_global_ref_decr(void*);
50 var cflags
= self.cflags
[""].join
(" ")
51 nitni_ccu
.write_as_nitni
(self, v
.compiler
.modelbuilder
.compile_dir
)
53 for file
in nitni_ccu
.files
do
54 var f
= new ExternCFile(file
, cflags
)
55 f
.pkgconfigs
.add_all pkgconfigs
56 v
.compiler
.extern_bodies
.add
(f
)
59 # reset FFI things so the next compilation job, if any, starts with a clean context
60 # FIXME clean and rationalize this
62 compiled_ffi_methods
.clear
67 private fun ensure_compile_nitni_base
(v
: AbstractCompilerVisitor)
69 if nitni_ccu
!= null then return
71 nitni_ccu
= new CCompilationUnit
74 redef fun collect_linker_libs
76 if not self.ldflags
.keys
.has
("") then return null
77 return self.ldflags
[""]
81 redef class AMethPropdef
82 private fun compile_ffi_support_to_c
(v
: AbstractCompilerVisitor)
84 var mmodule
= mpropdef
.mclassdef
.mmodule
85 var mainmodule
= v
.compiler
.mainmodule
86 var amodule
= v
.compiler
.modelbuilder
.mmodule2node
(mmodule
)
87 var mclass_type
= mpropdef
.mclassdef
.bound_mtype
90 var csignature
= mpropdef
.mproperty
.build_csignature
(mclass_type
, mmodule
, "___impl", long_signature
, internal_call_context
)
91 v
.declare_once
("{csignature};")
94 amodule
.ensure_compile_ffi_wrapper
95 compile_ffi_method
(mmodule
)
97 # nitni - Compile missing callbacks
98 mmodule
.ensure_compile_nitni_base
(v
)
101 redef fun compile_externmeth_to_c
(v
, mpropdef
, arguments
)
103 # if using the old native interface fallback on previous implementation
104 if n_extern_code_block
== null then return super
106 var mmodule
= mpropdef
.mclassdef
.mmodule
107 mmodule
.uses_ffi
= true
109 var mclass_type
= mpropdef
.mclassdef
.bound_mtype
111 # Outgoing code in compiler
112 var externname
= mpropdef
.mproperty
.build_cname
(mpropdef
.mclassdef
.bound_mtype
, mmodule
, "___impl", long_signature
)
113 var recv_var
: nullable RuntimeVariable = null
114 var return_mtype
= mpropdef
.msignature
.return_mtype
115 if return_mtype
!= null then
116 return_mtype
= return_mtype
.anchor_to
(mmodule
, mclass_type
)
117 recv_var
= v
.new_var
(return_mtype
)
120 v
.adapt_signature
(mpropdef
, arguments
)
121 v
.unbox_signature_extern
(mpropdef
, arguments
)
123 var arguments_for_c
= new Array[String]
124 for a
in [0..arguments
.length
[ do
125 var arg
= arguments
[a
]
126 var param_mtype
: MType
128 param_mtype
= mpropdef
.mclassdef
.mclass
.mclass_type
129 else param_mtype
= mpropdef
.msignature
.mparameters
[a-1
].mtype
131 param_mtype
= param_mtype
.anchor_to
(mmodule
, mclass_type
)
133 if param_mtype
.is_cprimitive
then
134 arguments_for_c
.add
(arg
.name
)
136 v
.add
("struct nitni_instance* var_for_c_{a};")
137 v
.add
("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));")
138 v
.add
("var_for_c_{a}->value = {arg.name};")
139 arguments_for_c
.add
("var_for_c_{a}")
143 if recv_var
== null then
144 v
.add
("{externname}({arguments_for_c.join(", ")});")
146 assert return_mtype
!= null
147 if return_mtype
.is_cprimitive
then
148 v
.add
("{recv_var} = {externname}({arguments_for_c.join(", ")});")
150 v
.add
("struct nitni_instance* ret_var;")
151 v
.add
("ret_var = {externname}({arguments_for_c.join(", ")});")
152 v
.add
("{recv_var} = ret_var->value;")
154 recv_var
= v
.box_extern
(recv_var
, return_mtype
)
158 compile_ffi_support_to_c
(v
)
162 redef fun compile_externinit_to_c
(v
, mpropdef
, arguments
)
164 # if using the old native interface fallback on previous implementation
165 if n_extern_code_block
== null then return super
167 var mmodule
= mpropdef
.mclassdef
.mmodule
168 mmodule
.uses_ffi
= true
170 var mclass_type
= mpropdef
.mclassdef
.bound_mtype
172 var externname
= mpropdef
.mproperty
.build_cname
(mpropdef
.mclassdef
.bound_mtype
, mmodule
, "___impl", long_signature
)
173 var return_mtype
= arguments
.first
.mtype
174 var recv_var
= v
.new_var
(return_mtype
)
176 v
.adapt_signature
(mpropdef
, arguments
)
177 v
.unbox_signature_extern
(mpropdef
, arguments
)
181 var arguments_for_c
= new Array[String]
182 for a
in [0..arguments
.length
[ do
183 var arg
= arguments
[a
]
184 var param_mtype
: MType
185 param_mtype
= mpropdef
.msignature
.mparameters
[a
].mtype
186 param_mtype
= param_mtype
.anchor_to
(mmodule
, mclass_type
)
188 if param_mtype
.is_cprimitive
then
189 arguments_for_c
.add
(arg
.name
)
191 v
.add
("struct nitni_instance* var_for_c_{a};")
192 v
.add
("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));")
193 v
.add
("var_for_c_{a}->value = {arg.name};")
194 arguments_for_c
.add
("var_for_c_{a}")
198 if return_mtype
.is_cprimitive
then
199 v
.add
("{recv_var} = {externname}({arguments_for_c.join(", ")});")
201 v
.add
("struct nitni_instance* ret_var;")
202 v
.add
("ret_var = {externname}({arguments_for_c.join(", ")});")
203 v
.add
("{recv_var} = ret_var->value;")
205 recv_var
= v
.box_extern
(recv_var
, return_mtype
)
208 compile_ffi_support_to_c
(v
)
213 redef class CCompilationUnit
214 fun write_as_nitni
(mmodule
: MModule, compdir
: String)
216 var base_name
= "{mmodule.c_name}._nitni"
218 var h_file
= "{base_name}.h"
219 write_header_to_file
( mmodule
, "{compdir}/{h_file}", new Array[String],
220 "{mmodule.c_name.to_s.to_upper}_NITG_NITNI_H")
222 var c_file
= "{base_name}.c"
223 write_body_to_file
( mmodule
, "{compdir}/{c_file}", ["\"{h_file}\
""] )
225 files
.add
( "{compdir}/{c_file}" )
229 redef class AbstractCompiler
230 # Cache to avoid multiple compilation of NULL values
231 # see FIXME in `MNullableType#compile_extern_helper_functions`
232 private var compiled_null_types
= new Array[MNullableType]
235 redef class AbstractCompilerVisitor
236 # Create a `RuntimeVariable` for this C variable originating from C user code
237 private fun var_from_c
(name
: String, mtype
: MType): RuntimeVariable
239 if mtype
.is_cprimitive
then
240 return new RuntimeVariable(name
, mtype
, mtype
)
242 return new RuntimeVariable("{name}->value", mtype
, mtype
)
246 # Return a `RuntimeVarible` to C user code
247 private fun ret_to_c
(src
: RuntimeVariable, mtype
: MType)
249 if mtype
.is_cprimitive
then
252 add
("struct nitni_instance* ret_for_c;")
253 add
("ret_for_c = nit_alloc(sizeof(struct nitni_instance));")
254 add
("ret_for_c->value = {src};")
255 add
("return ret_for_c;")
261 private fun compile_extern_type
(v
: AbstractCompilerVisitor, ccu
: CCompilationUnit)
263 assert not is_cprimitive
265 # define friendly type
266 ccu
.header_c_types
.add
("#ifndef NIT_TYPE_{cname}\n")
267 ccu
.header_c_types
.add
("#define NIT_TYPE_{cname} 1\n")
268 ccu
.header_c_types
.add
("typedef struct nitni_instance *{cname};\n")
269 ccu
.header_c_types
.add
("#endif\n")