abstract_compiler: add `AModule::collect_linker_libs` to be refined in compiler_ffi
[nit.git] / src / compiler_ffi.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2013 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 compiler_ffi
19
20 intrude import abstract_compiler
21 intrude import common_ffi
22 import nitni
23
24 redef class AModule
25 private var foreign_callbacks = new ForeignCallbackSet
26 var nitni_ccu: nullable CCompilationUnit = null
27
28 redef var uses_legacy_ni: Bool = false
29
30 redef fun finalize_ffi(v: AbstractCompilerVisitor, modelbuilder: ModelBuilder)
31 do
32 finalize_ffi_wrapper(v.compiler.modelbuilder.compile_dir, v.compiler.mainmodule)
33 for file in ffi_files do v.compiler.extern_bodies.add(file)
34 end
35
36 fun ensure_compile_nitni_base(v: AbstractCompilerVisitor)
37 do
38 if nitni_ccu != null then return
39
40 nitni_ccu = new CCompilationUnit
41 end
42
43 redef fun finalize_nitni(v: AbstractCompilerVisitor)
44 do
45 ensure_compile_nitni_base(v)
46
47 nitni_ccu.header_c_types.add("#include \"{mmodule.name}._ffi.h\"\n")
48
49 nitni_ccu.write_as_nitni(self, v.compiler.modelbuilder.compile_dir)
50
51 for file in nitni_ccu.files do
52 v.compiler.extern_bodies.add(new ExternCFile(file, c_compiler_options))
53 end
54 end
55
56 redef fun collect_linker_libs
57 do
58 var s = c_linker_options
59 if s.is_empty then return null
60 var res = new ArraySet[String]
61 res.add s
62 return res
63 end
64
65 var compiled_callbacks: Array[NitniCallback] = new Array[NitniCallback]
66
67 # Returns true if callbacks has yet to be generated and register it as being generated
68 fun check_callback_compilation(cb: NitniCallback): Bool
69 do
70 var compiled = compiled_callbacks.has(cb)
71 if not compiled then compiled_callbacks.add(cb)
72 return not compiled
73 end
74 end
75
76 redef class AExternPropdef
77 fun compile_ffi_support_to_c(v: AbstractCompilerVisitor)
78 do
79 var mmodule = mpropdef.mclassdef.mmodule
80 var amainmodule = v.compiler.modelbuilder.mmodule2nmodule[v.compiler.mainmodule]
81 var amodule = v.compiler.modelbuilder.mmodule2nmodule[mmodule]
82 var mclass_type = mpropdef.mclassdef.bound_mtype
83
84 # Declare as extern
85 var csignature = mpropdef.mproperty.build_csignature(mclass_type, mmodule, "___impl", long_signature, internal_call_context)
86 v.declare_once("{csignature};")
87
88 # FFI part
89 compile_ffi_method(amodule)
90
91 # nitni - Compile missing callbacks
92 amodule.ensure_compile_nitni_base(v)
93 var ccu = amodule.nitni_ccu.as(not null)
94
95 for mtype in foreign_callbacks.types do
96 if not mtype.is_cprimitive then
97 mtype.compile_extern_type(v, ccu)
98
99 # has callbacks already been compiled? (this may very well happen with global compilation)
100 if amodule.check_callback_compilation(mtype) then mtype.compile_extern_helper_functions(v, ccu)
101 end
102 end
103
104 # compile callbacks
105 for cb in foreign_callbacks.callbacks do if amainmodule.check_callback_compilation(cb) then
106 cb.compile_extern_callback(v, ccu)
107 end
108
109 for cb in foreign_callbacks.supers do if amainmodule.check_callback_compilation(cb) then
110 cb.compile_extern_callback(v, ccu)
111 end
112
113 for cb in foreign_callbacks.casts do if amainmodule.check_callback_compilation(cb) then
114 cb.compile_extern_callbacks(v, ccu)
115 end
116
117 # manage nitni callback set
118 amodule.foreign_callbacks.join(foreign_callbacks)
119 end
120 end
121
122 redef class AExternMethPropdef
123 redef fun compile_to_c(v, mpropdef, arguments)
124 do
125 var mmodule = mpropdef.mclassdef.mmodule
126 var amodule = v.compiler.modelbuilder.mmodule2nmodule[mmodule]
127
128 # if using the old native interface fallback on previous implementation
129 var nextern = self.n_extern
130 if nextern != null then
131 amodule.uses_legacy_ni = true
132 super
133 return
134 end
135
136 amodule.mmodule.uses_ffi = true
137
138 var mclass_type = mpropdef.mclassdef.bound_mtype
139
140 # Outgoing code in compiler
141 var externname = mpropdef.mproperty.build_cname(mpropdef.mclassdef.bound_mtype, mmodule, "___impl", long_signature)
142 var recv_var: nullable RuntimeVariable = null
143 var return_mtype = mpropdef.msignature.return_mtype
144 if return_mtype != null then
145 return_mtype = return_mtype.anchor_to(mmodule, mclass_type)
146 recv_var = v.new_var(return_mtype)
147 end
148
149 v.adapt_signature(mpropdef, arguments)
150
151 var arguments_for_c = new Array[String]
152 for a in [0..arguments.length[ do
153 var arg = arguments[a]
154 var param_mtype: MType
155 if a == 0 then
156 param_mtype = mpropdef.mclassdef.mclass.mclass_type
157 else param_mtype = mpropdef.msignature.mparameters[a-1].mtype
158
159 param_mtype = param_mtype.anchor_to(mmodule, mclass_type)
160
161 if param_mtype.is_cprimitive then
162 arguments_for_c.add(arg.name)
163 else
164 v.add("struct nitni_instance* var_for_c_{a};")
165 v.add("var_for_c_{a} = malloc(sizeof(struct nitni_instance));")
166 v.add("var_for_c_{a}->value = {arg.name};")
167 arguments_for_c.add("var_for_c_{a}")
168 end
169 end
170
171 if recv_var == null then
172 v.add("{externname}({arguments_for_c.join(", ")});")
173 else
174 assert return_mtype != null
175 if return_mtype.is_cprimitive then
176 v.add("{recv_var} = {externname}({arguments_for_c.join(", ")});")
177 else
178 v.add("struct nitni_instance* ret_var;")
179 v.add("ret_var = {externname}({arguments_for_c.join(", ")});")
180 v.add("{recv_var} = ret_var->value;")
181 end
182 v.ret(recv_var)
183 end
184
185 compile_ffi_support_to_c(v)
186 end
187 end
188
189 redef class AExternInitPropdef
190 redef fun compile_to_c(v, mpropdef, arguments)
191 do
192 var mmodule = mpropdef.mclassdef.mmodule
193 var amodule = v.compiler.modelbuilder.mmodule2nmodule[mmodule]
194
195 # if using the old native interface fallback on previous implementation
196 var nextern = self.n_extern
197 if nextern != null then
198 amodule.uses_legacy_ni = true
199 super
200 return
201 end
202
203 amodule.mmodule.uses_ffi = true
204
205 var mclass_type = mpropdef.mclassdef.bound_mtype
206
207 var externname = mpropdef.mproperty.build_cname(mpropdef.mclassdef.bound_mtype, mmodule, "___impl", long_signature)
208 var return_mtype = arguments.first.mtype
209 var recv_var = v.new_var(return_mtype)
210
211 v.adapt_signature(mpropdef, arguments)
212
213 arguments.shift
214
215 var arguments_for_c = new Array[String]
216 for a in [0..arguments.length[ do
217 var arg = arguments[a]
218 var param_mtype: MType
219 param_mtype = mpropdef.msignature.mparameters[a].mtype
220 param_mtype = param_mtype.anchor_to(mmodule, mclass_type)
221
222 if param_mtype.is_cprimitive then
223 arguments_for_c.add(arg.name)
224 else
225 v.add("struct nitni_instance* var_for_c_{a};")
226 v.add("var_for_c_{a} = malloc(sizeof(struct nitni_instance));")
227 v.add("var_for_c_{a}->value = {arg.name};")
228 arguments_for_c.add("var_for_c_{a}")
229 end
230 end
231
232 if return_mtype.is_cprimitive then
233 v.add("{recv_var} = {externname}({arguments_for_c.join(", ")});")
234 else
235 v.add("struct nitni_instance* ret_var;")
236 v.add("ret_var = {externname}({arguments_for_c.join(", ")});")
237 v.add("{recv_var} = ret_var->value;")
238 end
239 v.ret(recv_var)
240
241 compile_ffi_support_to_c(v)
242 end
243 end
244
245 redef class CCompilationUnit
246 fun write_as_nitni(amodule: AModule, compdir: String)
247 do
248 var base_name = "{amodule.mmodule.name}._nitni"
249
250 var h_file = "{base_name}.h"
251 write_header_to_file( amodule, "{compdir}/{h_file}", new Array[String],
252 "{amodule.cname.to_s.to_upper}_NITG_NITNI_H")
253
254 var c_file = "{base_name}.c"
255 write_body_to_file( amodule, "{compdir}/{c_file}", ["\"{h_file}\""] )
256
257 files.add( "{compdir}/{c_file}" )
258 end
259 end
260
261 redef class AbstractCompilerVisitor
262 # Create a `RuntimeVariable` for this C variable originating from C user code
263 private fun var_from_c(name: String, mtype: MType): RuntimeVariable
264 do
265 if mtype.is_cprimitive then
266 return new RuntimeVariable(name, mtype, mtype)
267 else
268 return new RuntimeVariable("{name}->value", mtype, mtype)
269 end
270 end
271
272 # Return a `RuntimeVarible` to C user code
273 private fun ret_to_c(src: RuntimeVariable, mtype: MType)
274 do
275 if mtype.is_cprimitive then
276 add("return {src};")
277 else
278 add("struct nitni_instance* ret_for_c;")
279 add("ret_for_c = malloc(sizeof(struct nitni_instance));")
280 add("ret_for_c->value = {src};")
281 add("return ret_for_c;")
282 end
283 end
284 end
285
286 redef class MType
287 fun compile_extern_type(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
288 do
289 assert not is_cprimitive
290
291 # define friendly type
292 ccu.header_c_types.add("#ifndef NIT_TYPE_{cname}\n")
293 ccu.header_c_types.add("#define NIT_TYPE_{cname} 1\n")
294 ccu.header_c_types.add("typedef struct nitni_instance *{cname};\n")
295 ccu.header_c_types.add("#endif\n")
296 end
297
298 fun compile_extern_helper_functions(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
299 do
300 # actually, we do not need to do anything when using the bohem garbage collector
301
302 # incr_ref
303 var nitni_visitor = v.compiler.new_visitor
304 ccu.header_decl.add("#define {mangled_cname}_incr_ref(from) while(0)\{\}\n")
305
306 # incr_ref
307 nitni_visitor = v.compiler.new_visitor
308 ccu.header_decl.add("#define {mangled_cname}_decr_ref(from) while(0)\{\}\n")
309 end
310 end
311
312 redef class MNullableType
313 redef fun compile_extern_helper_functions(v, ccu)
314 do
315 super
316
317 var base_cname = "null_{mtype.mangled_cname}"
318 var full_cname = "NIT_NULL___{base_cname}"
319
320 # In nitni files, declare internal function as extern
321 var full_friendly_csignature = "{cname_blind} {full_cname}()"
322 ccu.header_decl.add("extern {full_friendly_csignature};\n")
323
324 # In nitni files, #define friendly as extern
325 ccu.header_decl.add("#define {base_cname} {full_cname}\n")
326
327 # FIXME: This is ugly an broke the separate compilation principle
328 # The real function MUST be compiled only once, #define pragma only protect the compiler, not the loader
329 # However, I am not sure of the right approach here (eg. week refs are ugly)
330 if is_already_compiled then return
331 is_already_compiled = true
332
333 # Internally, implement internal function
334 var nitni_visitor = v.compiler.new_visitor
335 nitni_visitor.frame = v.frame
336 var full_internal_csignature = "{cname_blind} {full_cname}()"
337 nitni_visitor.add("{full_internal_csignature} \{")
338 nitni_visitor.add("struct nitni_instance* ret_for_c;")
339 nitni_visitor.add("ret_for_c = malloc(sizeof(struct nitni_instance));")
340 nitni_visitor.add("ret_for_c->value = NULL;")
341 nitni_visitor.add("return ret_for_c;")
342 nitni_visitor.add("\}")
343 end
344
345 private var is_already_compiled = false # FIXME to remove, show above
346 end
347
348 redef class MExplicitCall
349 fun compile_extern_callback(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
350 do
351 var mproperty = mproperty
352 assert mproperty isa MMethod
353
354 # In nitni files, declare internal function as extern
355 var full_friendly_csignature = mproperty.build_csignature(recv_mtype, v.compiler.mainmodule, null, long_signature, internal_call_context)
356 ccu.header_decl.add("extern {full_friendly_csignature};\n")
357
358 # Internally, implement internal function
359 var nitni_visitor = v.compiler.new_visitor
360 nitni_visitor.frame = v.frame
361 var msignature = mproperty.lookup_first_definition(v.compiler.mainmodule, recv_mtype).msignature
362 var csignature_blind = mproperty.build_csignature(recv_mtype, v.compiler.mainmodule, null, long_signature, internal_call_context)
363
364 nitni_visitor.add_decl("/* nitni callback for {mproperty.full_name} */")
365 nitni_visitor.add_decl("{csignature_blind} \{")
366
367 var vars = new Array[RuntimeVariable]
368 var mtype: MType = recv_mtype
369 var recv_var = null
370 if mproperty.is_init then
371 if recv_mtype.mclass.kind == extern_kind then
372 recv_var = nitni_visitor.new_var(mtype)
373 else
374 var recv_mtype = recv_mtype
375 recv_var = nitni_visitor.init_instance(recv_mtype)
376 nitni_visitor.add("{mtype.ctype} recv /* var self: {mtype} */;")
377 nitni_visitor.add("recv = {recv_var};")
378 end
379 else
380 mtype = mtype.anchor_to(v.compiler.mainmodule, recv_mtype)
381 recv_var = nitni_visitor.var_from_c("recv", mtype)
382 end
383
384 vars.add(recv_var)
385
386 for p in msignature.mparameters do
387 var arg_mtype = p.mtype.anchor_to(v.compiler.mainmodule, recv_mtype)
388 var arg = nitni_visitor.var_from_c(p.name, arg_mtype)
389 vars.add(arg)
390 end
391
392 var ret_var = nitni_visitor.send(mproperty, vars)
393
394 var return_mtype = msignature.return_mtype
395 if mproperty.is_init then
396 if recv_mtype.mclass.kind != extern_kind then ret_var = recv_var
397 return_mtype = recv_mtype
398 end
399 if return_mtype != null then
400 assert ret_var != null
401 return_mtype = return_mtype.anchor_to(v.compiler.mainmodule, recv_mtype)
402 ret_var = nitni_visitor.autobox(ret_var, return_mtype)
403 nitni_visitor.ret_to_c(ret_var, return_mtype)
404 end
405 nitni_visitor.add("\}")
406 end
407 end
408
409 redef class MExplicitSuper
410 fun compile_extern_callback(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
411 do
412 var mproperty = from.mproperty
413 assert mproperty isa MMethod
414 var mclass_type = from.mclassdef.mclass.mclass_type
415 var mmodule = from.mclassdef.mmodule
416
417 # In nitni files, declare internal function as extern
418 var internal_csignature = mproperty.build_csignature(mclass_type, v.compiler.mainmodule, "___super", long_signature, internal_call_context)
419 ccu.header_decl.add("extern {internal_csignature};\n")
420
421 # In nitni files, #define friendly as extern
422 var friendly_cname = mproperty.build_cname(mclass_type, v.compiler.mainmodule, "___super", short_signature)
423 var internal_cname = mproperty.build_cname(mclass_type, v.compiler.mainmodule, "___super", long_signature)
424 ccu.header_decl.add("#define {friendly_cname} {internal_cname}\n")
425
426 # Internally, implement internal function
427 var nitni_visitor = v.compiler.new_visitor
428 nitni_visitor.frame = v.frame
429 var msignature = mproperty.lookup_first_definition(v.compiler.mainmodule, mclass_type).msignature
430
431 var csignature_blind = mproperty.build_csignature(mclass_type, v.compiler.mainmodule, "___super", long_signature, internal_call_context)
432
433 nitni_visitor.add_decl("/* nitni callback to super for {mproperty.full_name} */")
434 nitni_visitor.add_decl("{csignature_blind} \{")
435
436 var vars = new Array[RuntimeVariable]
437
438 var recv_var = nitni_visitor.var_from_c("recv", mclass_type)
439 vars.add(recv_var)
440
441 for p in msignature.mparameters do
442 var arg_mtype = v.anchor(p.mtype)
443 var arg = nitni_visitor.var_from_c(p.name, arg_mtype)
444 vars.add(arg)
445 end
446
447 var ret_var = nitni_visitor.supercall(from.as(MMethodDef), mclass_type, vars)
448
449 var return_mtype = msignature.return_mtype
450 if return_mtype != null then
451 assert ret_var != null
452 return_mtype = v.anchor(return_mtype)
453 nitni_visitor.ret_to_c(ret_var, return_mtype)
454 end
455 nitni_visitor.add("\}")
456 end
457 end
458
459 redef class MExplicitCast
460 fun compile_extern_callbacks(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
461 do
462 var from = from
463 var to = to
464
465 #
466 ## check type
467 #
468
469 # In nitni files, declare internal function as extern
470 var full_friendly_csignature = "int {v.compiler.mainmodule.name }___{from.mangled_cname}_is_a_{to.mangled_cname}({from.cname_blind})"
471 ccu.header_decl.add("extern {full_friendly_csignature};\n")
472
473 # In nitni files, #define friendly as extern
474 ccu.header_decl.add("#define {check_cname} {v.compiler.mainmodule.name}___{check_cname}\n")
475
476 # Internally, implement internal function
477 var nitni_visitor = v.compiler.new_visitor
478 nitni_visitor.frame = v.frame
479
480 var full_internal_csignature = "int {v.compiler.mainmodule.name }___{from.mangled_cname}_is_a_{to.mangled_cname}({from.cname_blind} from)"
481 nitni_visitor.add_decl("/* nitni check for {from} to {to} */")
482 nitni_visitor.add_decl("{full_internal_csignature} \{")
483
484 var from_var = new RuntimeVariable("from->value", from, from)
485 var recv_var = nitni_visitor.type_test(from_var, to, "FFI isa")
486 nitni_visitor.add("return {recv_var};")
487
488 nitni_visitor.add("\}")
489
490 # special checks
491 if from == to.as_nullable then
492 # format A_is_null
493 ccu.header_decl.add("#define {from.mangled_cname}_is_null !{from.mangled_cname}_is_a_{to.mangled_cname}\n")
494 end
495
496 #
497 ## cast
498 #
499
500 # In nitni files, declare internal function as extern
501 full_friendly_csignature = "{to.cname_blind} {v.compiler.mainmodule.name }___{from.mangled_cname}_as_{to.mangled_cname}({from.cname_blind})"
502 ccu.header_decl.add("extern {full_friendly_csignature};\n")
503
504 # In nitni files, #define friendly as extern
505 ccu.header_decl.add("#define {cast_cname} {v.compiler.mainmodule.name}___{cast_cname}\n")
506
507 # Internally, implement internal function
508 nitni_visitor = v.compiler.new_visitor
509 nitni_visitor.frame = v.frame
510
511 full_internal_csignature = "{to.cname_blind} {v.compiler.mainmodule.name }___{from.mangled_cname}_as_{to.mangled_cname}({from.cname_blind} from)"
512 nitni_visitor.add_decl("/* nitni cast for {from} to {to} */")
513 nitni_visitor.add_decl("{full_internal_csignature} \{")
514
515 from_var = nitni_visitor.var_from_c("from", from)
516
517 ## test type
518 var check = nitni_visitor.type_test(from_var, to, "FFI cast")
519 nitni_visitor.add("if (!{check}) \{")
520 nitni_visitor.add_abort("FFI cast failed")
521 nitni_visitor.add("\}")
522
523 ## internal cast
524 recv_var = nitni_visitor.autobox(from_var, to)
525
526 nitni_visitor.ret_to_c(recv_var, to)
527
528 nitni_visitor.add("\}")
529
530 # special casts
531 if from.as_nullable == to then
532 # format A_as_nullable
533 ccu.header_decl.add("#define {from.mangled_cname}_as_nullable {from.mangled_cname}_as_{to.mangled_cname}\n")
534 end
535
536 if from == to.as_nullable then
537 # format A_as_nullable
538 ccu.header_decl.add("#define {to.mangled_cname}_as_not_nullable {from.mangled_cname}_as_{to.mangled_cname}\n")
539 end
540 end
541 end