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