345377aee387e2690c5af1009b4ec7987b02b7b6
[nit.git] / src / compiler / compiler_ffi / light.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013-2015 Alexis Laferrière <alexis.laf@xymus.net>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # FFI support for the compilers
18 module light
19
20 intrude import abstract_compiler
21 intrude import ffi::light_ffi
22
23 redef class MModule
24 var nitni_ccu: nullable CCompilationUnit = null
25
26 private fun nmodule(v: AbstractCompilerVisitor): nullable AModule
27 do
28 return v.compiler.modelbuilder.mmodule2node(self)
29 end
30
31 redef fun finalize_ffi(compiler: AbstractCompiler)
32 do
33 if not uses_ffi then return
34
35 var v = compiler.new_visitor
36 var n = nmodule(v)
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)
41
42 ensure_compile_nitni_base(v)
43
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*);
48 """
49
50 var cflags = self.cflags[""].join(" ")
51 nitni_ccu.write_as_nitni(self, v.compiler.modelbuilder.compile_dir)
52
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)
57 end
58
59 # reset FFI things so the next compilation job, if any, starts with a clean context
60 # FIXME clean and rationalize this
61 nitni_ccu = null
62 compiled_ffi_methods.clear
63 ffi_ccu = null
64 ffi_files.clear
65 end
66
67 private fun ensure_compile_nitni_base(v: AbstractCompilerVisitor)
68 do
69 if nitni_ccu != null then return
70
71 nitni_ccu = new CCompilationUnit
72 end
73
74 redef fun collect_linker_libs
75 do
76 if not self.ldflags.keys.has("") then return null
77 return self.ldflags[""]
78 end
79 end
80
81 redef class AMethPropdef
82 private fun compile_ffi_support_to_c(v: AbstractCompilerVisitor)
83 do
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
88
89 # Declare as extern
90 var csignature = mpropdef.mproperty.build_csignature(mclass_type, mmodule, "___impl", long_signature, internal_call_context)
91 v.declare_once("{csignature};")
92
93 # FFI part
94 amodule.ensure_compile_ffi_wrapper
95 compile_ffi_method(mmodule)
96
97 # nitni - Compile missing callbacks
98 mmodule.ensure_compile_nitni_base(v)
99 end
100
101 redef fun compile_externmeth_to_c(v, mpropdef, arguments)
102 do
103 # if using the old native interface fallback on previous implementation
104 if n_extern_code_block == null then return super
105
106 var mmodule = mpropdef.mclassdef.mmodule
107 mmodule.uses_ffi = true
108
109 var mclass_type = mpropdef.mclassdef.bound_mtype
110
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)
118 end
119
120 v.adapt_signature(mpropdef, arguments)
121 v.unbox_signature_extern(mpropdef, arguments)
122
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
127 if a == 0 then
128 param_mtype = mpropdef.mclassdef.mclass.mclass_type
129 else param_mtype = mpropdef.msignature.mparameters[a-1].mtype
130
131 param_mtype = param_mtype.anchor_to(mmodule, mclass_type)
132
133 if param_mtype.is_cprimitive then
134 arguments_for_c.add(arg.name)
135 else
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}")
140 end
141 end
142
143 if recv_var == null then
144 v.add("{externname}({arguments_for_c.join(", ")});")
145 else
146 assert return_mtype != null
147 if return_mtype.is_cprimitive then
148 v.add("{recv_var} = {externname}({arguments_for_c.join(", ")});")
149 else
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;")
153 end
154 recv_var = v.box_extern(recv_var, return_mtype)
155 v.ret(recv_var)
156 end
157
158 compile_ffi_support_to_c(v)
159 return true
160 end
161
162 redef fun compile_externinit_to_c(v, mpropdef, arguments)
163 do
164 # if using the old native interface fallback on previous implementation
165 if n_extern_code_block == null then return super
166
167 var mmodule = mpropdef.mclassdef.mmodule
168 mmodule.uses_ffi = true
169
170 var mclass_type = mpropdef.mclassdef.bound_mtype
171
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)
175
176 v.adapt_signature(mpropdef, arguments)
177 v.unbox_signature_extern(mpropdef, arguments)
178
179 arguments.shift
180
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)
187
188 if param_mtype.is_cprimitive then
189 arguments_for_c.add(arg.name)
190 else
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}")
195 end
196 end
197
198 if return_mtype.is_cprimitive then
199 v.add("{recv_var} = {externname}({arguments_for_c.join(", ")});")
200 else
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;")
204 end
205 recv_var = v.box_extern(recv_var, return_mtype)
206 v.ret(recv_var)
207
208 compile_ffi_support_to_c(v)
209 return true
210 end
211 end
212
213 redef class CCompilationUnit
214 fun write_as_nitni(mmodule: MModule, compdir: String)
215 do
216 var base_name = "{mmodule.c_name}._nitni"
217
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")
221
222 var c_file = "{base_name}.c"
223 write_body_to_file( mmodule, "{compdir}/{c_file}", ["\"{h_file}\""] )
224
225 files.add( "{compdir}/{c_file}" )
226 end
227 end
228
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]
233 end
234
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
238 do
239 if mtype.is_cprimitive then
240 return new RuntimeVariable(name, mtype, mtype)
241 else
242 return new RuntimeVariable("{name}->value", mtype, mtype)
243 end
244 end
245
246 # Return a `RuntimeVarible` to C user code
247 private fun ret_to_c(src: RuntimeVariable, mtype: MType)
248 do
249 if mtype.is_cprimitive then
250 add("return {src};")
251 else
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;")
256 end
257 end
258 end
259
260 redef class MType
261 private fun compile_extern_type(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
262 do
263 assert not is_cprimitive
264
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")
270 end
271 end