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
.toolchain
.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
("#include <stdint.h>\n")
47 nitni_ccu
.header_c_types
.add
"""
48 extern void nitni_global_ref_incr(void*);
49 extern void nitni_global_ref_decr(void*);
52 var cflags
= self.cflags
[""].join
(" ")
53 nitni_ccu
.write_as_nitni
(self, v
.compiler
.toolchain
.compile_dir
)
55 for file
in nitni_ccu
.files
do
56 var f
= new ExternCFile(file
, cflags
)
57 f
.pkgconfigs
.add_all pkgconfigs
58 v
.compiler
.extern_bodies
.add
(f
)
61 # reset FFI things so the next compilation job, if any, starts with a clean context
62 # FIXME clean and rationalize this
64 compiled_ffi_methods
.clear
69 private fun ensure_compile_nitni_base
(v
: AbstractCompilerVisitor)
71 if nitni_ccu
!= null then return
73 nitni_ccu
= new CCompilationUnit
76 redef fun collect_linker_libs
78 if not self.ldflags
.keys
.has
("") then return null
79 return self.ldflags
[""]
83 redef class AMethPropdef
84 private fun compile_ffi_support_to_c
(v
: AbstractCompilerVisitor)
86 var mmodule
= mpropdef
.mclassdef
.mmodule
87 var amodule
= v
.compiler
.modelbuilder
.mmodule2node
(mmodule
)
88 var mclass_type
= mpropdef
.mclassdef
.bound_mtype
91 var csignature
= mpropdef
.mproperty
.build_csignature
(mclass_type
, mmodule
, "___impl", long_signature
, internal_call_context
)
92 v
.declare_once
("{csignature};")
95 amodule
.ensure_compile_ffi_wrapper
96 compile_ffi_method
(mmodule
)
98 # nitni - Compile missing callbacks
99 mmodule
.ensure_compile_nitni_base
(v
)
102 # Should we compile the extern method `self`?
104 # Returns false when restricting to the light FFI on methods using callbacks.
105 fun accept_externmeth
: Bool do return true
107 redef fun compile_externmeth_to_c
(v
, mpropdef
, arguments
)
109 # if using the old native interface fallback on previous implementation
110 if n_extern_code_block
== null then return super
112 if not accept_externmeth
then return false
114 var mmodule
= mpropdef
.mclassdef
.mmodule
115 mmodule
.uses_ffi
= true
117 var mclass_type
= mpropdef
.mclassdef
.bound_mtype
119 # Outgoing code in compiler
120 var externname
= mpropdef
.mproperty
.build_cname
(mpropdef
.mclassdef
.bound_mtype
, mmodule
, "___impl", long_signature
)
121 var recv_var
: nullable RuntimeVariable = null
122 var return_mtype
= mpropdef
.msignature
.return_mtype
123 if return_mtype
!= null then
124 return_mtype
= return_mtype
.anchor_to
(mmodule
, mclass_type
)
125 recv_var
= v
.new_var
(return_mtype
)
128 v
.adapt_signature
(mpropdef
, arguments
)
129 v
.unbox_signature_extern
(mpropdef
, arguments
)
131 var arguments_for_c
= new Array[String]
132 for a
in [0..arguments
.length
[ do
133 var arg
= arguments
[a
]
134 var param_mtype
: MType
136 param_mtype
= mpropdef
.mclassdef
.mclass
.mclass_type
137 else param_mtype
= mpropdef
.msignature
.mparameters
[a-1
].mtype
139 param_mtype
= param_mtype
.anchor_to
(mmodule
, mclass_type
)
141 if param_mtype
.is_cprimitive
then
142 arguments_for_c
.add
(arg
.name
)
144 v
.add
("struct nitni_instance* var_for_c_{a};")
145 v
.add
("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));")
146 v
.add
("var_for_c_{a}->value = {arg.name};")
147 arguments_for_c
.add
("var_for_c_{a}")
151 if recv_var
== null then
152 v
.add
("{externname}({arguments_for_c.join(", ")});")
154 assert return_mtype
!= null
155 if return_mtype
.is_cprimitive
then
156 v
.add
("{recv_var} = {externname}({arguments_for_c.join(", ")});")
158 v
.add
("struct nitni_instance* ret_var;")
159 v
.add
("ret_var = {externname}({arguments_for_c.join(", ")});")
160 v
.add
("{recv_var} = ret_var->value;")
162 recv_var
= v
.box_extern
(recv_var
, return_mtype
)
166 compile_ffi_support_to_c
(v
)
170 redef fun compile_externinit_to_c
(v
, mpropdef
, arguments
)
172 # if using the old native interface fallback on previous implementation
173 if n_extern_code_block
== null then return super
175 if not accept_externmeth
then return false
177 var mmodule
= mpropdef
.mclassdef
.mmodule
178 mmodule
.uses_ffi
= true
180 var mclass_type
= mpropdef
.mclassdef
.bound_mtype
182 var externname
= mpropdef
.mproperty
.build_cname
(mpropdef
.mclassdef
.bound_mtype
, mmodule
, "___impl", long_signature
)
183 var return_mtype
= arguments
.first
.mtype
184 var recv_var
= v
.new_var
(return_mtype
)
186 v
.adapt_signature
(mpropdef
, arguments
)
187 v
.unbox_signature_extern
(mpropdef
, arguments
)
191 var arguments_for_c
= new Array[String]
192 for a
in [0..arguments
.length
[ do
193 var arg
= arguments
[a
]
194 var param_mtype
: MType
195 param_mtype
= mpropdef
.msignature
.mparameters
[a
].mtype
196 param_mtype
= param_mtype
.anchor_to
(mmodule
, mclass_type
)
198 if param_mtype
.is_cprimitive
then
199 arguments_for_c
.add
(arg
.name
)
201 v
.add
("struct nitni_instance* var_for_c_{a};")
202 v
.add
("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));")
203 v
.add
("var_for_c_{a}->value = {arg.name};")
204 arguments_for_c
.add
("var_for_c_{a}")
208 if return_mtype
.is_cprimitive
then
209 v
.add
("{recv_var} = {externname}({arguments_for_c.join(", ")});")
211 v
.add
("struct nitni_instance* ret_var;")
212 v
.add
("ret_var = {externname}({arguments_for_c.join(", ")});")
213 v
.add
("{recv_var} = ret_var->value;")
215 recv_var
= v
.box_extern
(recv_var
, return_mtype
)
218 compile_ffi_support_to_c
(v
)
223 redef class CCompilationUnit
224 # Compile a `_nitni` files, used to implement nitni features for the compiler
225 fun write_as_nitni
(mmodule
: MModule, compdir
: String)
227 var base_name
= "{mmodule.c_name}._nitni"
229 var h_file
= "{base_name}.h"
230 write_header_to_file
( mmodule
, "{compdir}/{h_file}", new Array[String],
231 "{mmodule.c_name.to_s.to_upper}_NITG_NITNI_H")
233 var c_file
= "{base_name}.c"
234 write_body_to_file
( mmodule
, "{compdir}/{c_file}", ["\"{h_file}\
""] )
236 files
.add
( "{compdir}/{c_file}" )
240 redef class AbstractCompiler
241 # Cache to avoid multiple compilation of NULL values
242 # see FIXME in `MNullableType#compile_extern_helper_functions`
243 private var compiled_null_types
= new Array[MNullableType]
246 redef class AbstractCompilerVisitor
247 # Create a `RuntimeVariable` for this C variable originating from C user code
248 private fun var_from_c
(name
: String, mtype
: MType): RuntimeVariable
250 if mtype
.is_cprimitive
then
251 return new RuntimeVariable(name
, mtype
, mtype
)
253 return new RuntimeVariable("{name}->value", mtype
, mtype
)
257 # Return a `RuntimeVarible` to C user code
258 private fun ret_to_c
(src
: RuntimeVariable, mtype
: MType)
260 if mtype
.is_cprimitive
then
263 add
("struct nitni_instance* ret_for_c;")
264 add
("ret_for_c = nit_alloc(sizeof(struct nitni_instance));")
265 add
("ret_for_c->value = {src};")
266 add
("return ret_for_c;")
272 private fun compile_extern_type
(v
: AbstractCompilerVisitor, ccu
: CCompilationUnit)
274 assert not is_cprimitive
276 # define friendly type
277 ccu
.header_c_types
.add
("#ifndef NIT_TYPE_{cname}\n")
278 ccu
.header_c_types
.add
("#define NIT_TYPE_{cname} 1\n")
279 ccu
.header_c_types
.add
("typedef struct nitni_instance *{cname};\n")
280 ccu
.header_c_types
.add
("#endif\n")