abstract_compiler: attach compiler and toolchain
[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 # Light FFI support for the compiler
18 module light
19
20 intrude import abstract_compiler
21 intrude import ffi::light_ffi
22
23 redef class MModule
24 # `CCompilationUnit` used for nitni signatures and code specific to the compiler
25 var nitni_ccu: nullable CCompilationUnit = null
26
27 private fun nmodule(v: AbstractCompilerVisitor): nullable AModule
28 do
29 return v.compiler.modelbuilder.mmodule2node(self)
30 end
31
32 redef fun finalize_ffi(compiler: AbstractCompiler)
33 do
34 if not uses_ffi then return
35
36 var v = compiler.new_visitor
37 var n = nmodule(v)
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)
42
43 ensure_compile_nitni_base(v)
44
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*);
49 """
50
51 var cflags = self.cflags[""].join(" ")
52 nitni_ccu.write_as_nitni(self, v.compiler.toolchain.compile_dir)
53
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)
58 end
59
60 # reset FFI things so the next compilation job, if any, starts with a clean context
61 # FIXME clean and rationalize this
62 nitni_ccu = null
63 compiled_ffi_methods.clear
64 ffi_ccu = null
65 ffi_files.clear
66 end
67
68 private fun ensure_compile_nitni_base(v: AbstractCompilerVisitor)
69 do
70 if nitni_ccu != null then return
71
72 nitni_ccu = new CCompilationUnit
73 end
74
75 redef fun collect_linker_libs
76 do
77 if not self.ldflags.keys.has("") then return null
78 return self.ldflags[""]
79 end
80 end
81
82 redef class AMethPropdef
83 private fun compile_ffi_support_to_c(v: AbstractCompilerVisitor)
84 do
85 var mmodule = mpropdef.mclassdef.mmodule
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 # Should we compile the extern method `self`?
102 #
103 # Returns false when restricting to the light FFI on methods using callbacks.
104 fun accept_externmeth: Bool do return true
105
106 redef fun compile_externmeth_to_c(v, mpropdef, arguments)
107 do
108 # if using the old native interface fallback on previous implementation
109 if n_extern_code_block == null then return super
110
111 if not accept_externmeth then return false
112
113 var mmodule = mpropdef.mclassdef.mmodule
114 mmodule.uses_ffi = true
115
116 var mclass_type = mpropdef.mclassdef.bound_mtype
117
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)
125 end
126
127 v.adapt_signature(mpropdef, arguments)
128 v.unbox_signature_extern(mpropdef, arguments)
129
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
134 if a == 0 then
135 param_mtype = mpropdef.mclassdef.mclass.mclass_type
136 else param_mtype = mpropdef.msignature.mparameters[a-1].mtype
137
138 param_mtype = param_mtype.anchor_to(mmodule, mclass_type)
139
140 if param_mtype.is_cprimitive then
141 arguments_for_c.add(arg.name)
142 else
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}")
147 end
148 end
149
150 if recv_var == null then
151 v.add("{externname}({arguments_for_c.join(", ")});")
152 else
153 assert return_mtype != null
154 if return_mtype.is_cprimitive then
155 v.add("{recv_var} = {externname}({arguments_for_c.join(", ")});")
156 else
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;")
160 end
161 recv_var = v.box_extern(recv_var, return_mtype)
162 v.ret(recv_var)
163 end
164
165 compile_ffi_support_to_c(v)
166 return true
167 end
168
169 redef fun compile_externinit_to_c(v, mpropdef, arguments)
170 do
171 # if using the old native interface fallback on previous implementation
172 if n_extern_code_block == null then return super
173
174 if not accept_externmeth then return false
175
176 var mmodule = mpropdef.mclassdef.mmodule
177 mmodule.uses_ffi = true
178
179 var mclass_type = mpropdef.mclassdef.bound_mtype
180
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)
184
185 v.adapt_signature(mpropdef, arguments)
186 v.unbox_signature_extern(mpropdef, arguments)
187
188 arguments.shift
189
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)
196
197 if param_mtype.is_cprimitive then
198 arguments_for_c.add(arg.name)
199 else
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}")
204 end
205 end
206
207 if return_mtype.is_cprimitive then
208 v.add("{recv_var} = {externname}({arguments_for_c.join(", ")});")
209 else
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;")
213 end
214 recv_var = v.box_extern(recv_var, return_mtype)
215 v.ret(recv_var)
216
217 compile_ffi_support_to_c(v)
218 return true
219 end
220 end
221
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)
225 do
226 var base_name = "{mmodule.c_name}._nitni"
227
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")
231
232 var c_file = "{base_name}.c"
233 write_body_to_file( mmodule, "{compdir}/{c_file}", ["\"{h_file}\""] )
234
235 files.add( "{compdir}/{c_file}" )
236 end
237 end
238
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]
243 end
244
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
248 do
249 if mtype.is_cprimitive then
250 return new RuntimeVariable(name, mtype, mtype)
251 else
252 return new RuntimeVariable("{name}->value", mtype, mtype)
253 end
254 end
255
256 # Return a `RuntimeVarible` to C user code
257 private fun ret_to_c(src: RuntimeVariable, mtype: MType)
258 do
259 if mtype.is_cprimitive then
260 add("return {src};")
261 else
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;")
266 end
267 end
268 end
269
270 redef class MType
271 private fun compile_extern_type(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
272 do
273 assert not is_cprimitive
274
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")
280 end
281 end