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 # Light FFI support for the compiler
20 intrude import abstract_compiler
21 intrude import ffi
::light_ffi
24 # `CCompilationUnit` used for nitni signatures and code specific to the compiler
25 var nitni_ccu
: nullable CCompilationUnit = null
27 private fun nmodule
(v
: AbstractCompilerVisitor): nullable AModule
29 return v
.compiler
.modelbuilder
.mmodule2node
(self)
32 redef fun finalize_ffi
(compiler
: AbstractCompiler)
34 if not uses_ffi
then return
36 var v
= compiler
.new_visitor
38 if n
== null then return
39 n
.ensure_compile_ffi_wrapper
40 finalize_ffi_wrapper
(v
.compiler
.modelbuilder
.compile_dir
, v
.compiler
.mainmodule
)
41 for file
in ffi_files
do v
.compiler
.extern_bodies
.add
(file
)
43 ensure_compile_nitni_base
(v
)
45 nitni_ccu
.header_c_types
.add
("#include \"{c_name}._ffi
.h\
"\n")
46 nitni_ccu
.header_c_types
.add
"""
47 extern void nitni_global_ref_incr(void*);
48 extern void nitni_global_ref_decr(void*);
51 var cflags
= self.cflags
[""].join
(" ")
52 nitni_ccu
.write_as_nitni
(self, v
.compiler
.modelbuilder
.compile_dir
)
54 for file
in nitni_ccu
.files
do
55 var f
= new ExternCFile(file
, cflags
)
56 f
.pkgconfigs
.add_all pkgconfigs
57 v
.compiler
.extern_bodies
.add
(f
)
60 # reset FFI things so the next compilation job, if any, starts with a clean context
61 # FIXME clean and rationalize this
63 compiled_ffi_methods
.clear
68 private fun ensure_compile_nitni_base
(v
: AbstractCompilerVisitor)
70 if nitni_ccu
!= null then return
72 nitni_ccu
= new CCompilationUnit
75 redef fun collect_linker_libs
77 if not self.ldflags
.keys
.has
("") then return null
78 return self.ldflags
[""]
82 redef class AMethPropdef
83 private fun compile_ffi_support_to_c
(v
: AbstractCompilerVisitor)
85 var mmodule
= mpropdef
.mclassdef
.mmodule
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 # Should we compile the extern method `self`?
103 # Returns false when restricting to the light FFI on methods using callbacks.
104 fun accept_externmeth
: Bool do return true
106 redef fun compile_externmeth_to_c
(v
, mpropdef
, arguments
)
108 # if using the old native interface fallback on previous implementation
109 if n_extern_code_block
== null then return super
111 if not accept_externmeth
then return false
113 var mmodule
= mpropdef
.mclassdef
.mmodule
114 mmodule
.uses_ffi
= true
116 var mclass_type
= mpropdef
.mclassdef
.bound_mtype
118 # Outgoing code in compiler
119 var externname
= mpropdef
.mproperty
.build_cname
(mpropdef
.mclassdef
.bound_mtype
, mmodule
, "___impl", long_signature
)
120 var recv_var
: nullable RuntimeVariable = null
121 var return_mtype
= mpropdef
.msignature
.return_mtype
122 if return_mtype
!= null then
123 return_mtype
= return_mtype
.anchor_to
(mmodule
, mclass_type
)
124 recv_var
= v
.new_var
(return_mtype
)
127 v
.adapt_signature
(mpropdef
, arguments
)
128 v
.unbox_signature_extern
(mpropdef
, arguments
)
130 var arguments_for_c
= new Array[String]
131 for a
in [0..arguments
.length
[ do
132 var arg
= arguments
[a
]
133 var param_mtype
: MType
135 param_mtype
= mpropdef
.mclassdef
.mclass
.mclass_type
136 else param_mtype
= mpropdef
.msignature
.mparameters
[a-1
].mtype
138 param_mtype
= param_mtype
.anchor_to
(mmodule
, mclass_type
)
140 if param_mtype
.is_cprimitive
then
141 arguments_for_c
.add
(arg
.name
)
143 v
.add
("struct nitni_instance* var_for_c_{a};")
144 v
.add
("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));")
145 v
.add
("var_for_c_{a}->value = {arg.name};")
146 arguments_for_c
.add
("var_for_c_{a}")
150 if recv_var
== null then
151 v
.add
("{externname}({arguments_for_c.join(", ")});")
153 assert return_mtype
!= null
154 if return_mtype
.is_cprimitive
then
155 v
.add
("{recv_var} = {externname}({arguments_for_c.join(", ")});")
157 v
.add
("struct nitni_instance* ret_var;")
158 v
.add
("ret_var = {externname}({arguments_for_c.join(", ")});")
159 v
.add
("{recv_var} = ret_var->value;")
161 recv_var
= v
.box_extern
(recv_var
, return_mtype
)
165 compile_ffi_support_to_c
(v
)
169 redef fun compile_externinit_to_c
(v
, mpropdef
, arguments
)
171 # if using the old native interface fallback on previous implementation
172 if n_extern_code_block
== null then return super
174 if not accept_externmeth
then return false
176 var mmodule
= mpropdef
.mclassdef
.mmodule
177 mmodule
.uses_ffi
= true
179 var mclass_type
= mpropdef
.mclassdef
.bound_mtype
181 var externname
= mpropdef
.mproperty
.build_cname
(mpropdef
.mclassdef
.bound_mtype
, mmodule
, "___impl", long_signature
)
182 var return_mtype
= arguments
.first
.mtype
183 var recv_var
= v
.new_var
(return_mtype
)
185 v
.adapt_signature
(mpropdef
, arguments
)
186 v
.unbox_signature_extern
(mpropdef
, arguments
)
190 var arguments_for_c
= new Array[String]
191 for a
in [0..arguments
.length
[ do
192 var arg
= arguments
[a
]
193 var param_mtype
: MType
194 param_mtype
= mpropdef
.msignature
.mparameters
[a
].mtype
195 param_mtype
= param_mtype
.anchor_to
(mmodule
, mclass_type
)
197 if param_mtype
.is_cprimitive
then
198 arguments_for_c
.add
(arg
.name
)
200 v
.add
("struct nitni_instance* var_for_c_{a};")
201 v
.add
("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));")
202 v
.add
("var_for_c_{a}->value = {arg.name};")
203 arguments_for_c
.add
("var_for_c_{a}")
207 if return_mtype
.is_cprimitive
then
208 v
.add
("{recv_var} = {externname}({arguments_for_c.join(", ")});")
210 v
.add
("struct nitni_instance* ret_var;")
211 v
.add
("ret_var = {externname}({arguments_for_c.join(", ")});")
212 v
.add
("{recv_var} = ret_var->value;")
214 recv_var
= v
.box_extern
(recv_var
, return_mtype
)
217 compile_ffi_support_to_c
(v
)
222 redef class CCompilationUnit
223 # Compile a `_nitni` files, used to implement nitni features for the compiler
224 fun write_as_nitni
(mmodule
: MModule, compdir
: String)
226 var base_name
= "{mmodule.c_name}._nitni"
228 var h_file
= "{base_name}.h"
229 write_header_to_file
( mmodule
, "{compdir}/{h_file}", new Array[String],
230 "{mmodule.c_name.to_s.to_upper}_NITG_NITNI_H")
232 var c_file
= "{base_name}.c"
233 write_body_to_file
( mmodule
, "{compdir}/{c_file}", ["\"{h_file}\
""] )
235 files
.add
( "{compdir}/{c_file}" )
239 redef class AbstractCompiler
240 # Cache to avoid multiple compilation of NULL values
241 # see FIXME in `MNullableType#compile_extern_helper_functions`
242 private var compiled_null_types
= new Array[MNullableType]
245 redef class AbstractCompilerVisitor
246 # Create a `RuntimeVariable` for this C variable originating from C user code
247 private fun var_from_c
(name
: String, mtype
: MType): RuntimeVariable
249 if mtype
.is_cprimitive
then
250 return new RuntimeVariable(name
, mtype
, mtype
)
252 return new RuntimeVariable("{name}->value", mtype
, mtype
)
256 # Return a `RuntimeVarible` to C user code
257 private fun ret_to_c
(src
: RuntimeVariable, mtype
: MType)
259 if mtype
.is_cprimitive
then
262 add
("struct nitni_instance* ret_for_c;")
263 add
("ret_for_c = nit_alloc(sizeof(struct nitni_instance));")
264 add
("ret_for_c->value = {src};")
265 add
("return ret_for_c;")
271 private fun compile_extern_type
(v
: AbstractCompilerVisitor, ccu
: CCompilationUnit)
273 assert not is_cprimitive
275 # define friendly type
276 ccu
.header_c_types
.add
("#ifndef NIT_TYPE_{cname}\n")
277 ccu
.header_c_types
.add
("#define NIT_TYPE_{cname} 1\n")
278 ccu
.header_c_types
.add
("typedef struct nitni_instance *{cname};\n")
279 ccu
.header_c_types
.add
("#endif\n")