compiler: Changed types of Char to uint32_t and NativeString to unsigned char*
[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("#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*);
50 """
51
52 var cflags = self.cflags[""].join(" ")
53 nitni_ccu.write_as_nitni(self, v.compiler.toolchain.compile_dir)
54
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)
59 end
60
61 # reset FFI things so the next compilation job, if any, starts with a clean context
62 # FIXME clean and rationalize this
63 nitni_ccu = null
64 compiled_ffi_methods.clear
65 ffi_ccu = null
66 ffi_files.clear
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 if not self.ldflags.keys.has("") then return null
79 return self.ldflags[""]
80 end
81 end
82
83 redef class AMethPropdef
84 private fun compile_ffi_support_to_c(v: AbstractCompilerVisitor)
85 do
86 var mmodule = mpropdef.mclassdef.mmodule
87 var amodule = v.compiler.modelbuilder.mmodule2node(mmodule)
88 var mclass_type = mpropdef.mclassdef.bound_mtype
89
90 # Declare as extern
91 var csignature = mpropdef.mproperty.build_csignature(mclass_type, mmodule, "___impl", long_signature, internal_call_context)
92 v.declare_once("{csignature};")
93
94 # FFI part
95 amodule.ensure_compile_ffi_wrapper
96 compile_ffi_method(mmodule)
97
98 # nitni - Compile missing callbacks
99 mmodule.ensure_compile_nitni_base(v)
100 end
101
102 # Should we compile the extern method `self`?
103 #
104 # Returns false when restricting to the light FFI on methods using callbacks.
105 fun accept_externmeth: Bool do return true
106
107 redef fun compile_externmeth_to_c(v, mpropdef, arguments)
108 do
109 # if using the old native interface fallback on previous implementation
110 if n_extern_code_block == null then return super
111
112 if not accept_externmeth then return false
113
114 var mmodule = mpropdef.mclassdef.mmodule
115 mmodule.uses_ffi = true
116
117 var mclass_type = mpropdef.mclassdef.bound_mtype
118
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)
126 end
127
128 v.adapt_signature(mpropdef, arguments)
129 v.unbox_signature_extern(mpropdef, arguments)
130
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
135 if a == 0 then
136 param_mtype = mpropdef.mclassdef.mclass.mclass_type
137 else param_mtype = mpropdef.msignature.mparameters[a-1].mtype
138
139 param_mtype = param_mtype.anchor_to(mmodule, mclass_type)
140
141 if param_mtype.is_cprimitive then
142 arguments_for_c.add(arg.name)
143 else
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}")
148 end
149 end
150
151 if recv_var == null then
152 v.add("{externname}({arguments_for_c.join(", ")});")
153 else
154 assert return_mtype != null
155 if return_mtype.is_cprimitive then
156 v.add("{recv_var} = {externname}({arguments_for_c.join(", ")});")
157 else
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;")
161 end
162 recv_var = v.box_extern(recv_var, return_mtype)
163 v.ret(recv_var)
164 end
165
166 compile_ffi_support_to_c(v)
167 return true
168 end
169
170 redef fun compile_externinit_to_c(v, mpropdef, arguments)
171 do
172 # if using the old native interface fallback on previous implementation
173 if n_extern_code_block == null then return super
174
175 if not accept_externmeth then return false
176
177 var mmodule = mpropdef.mclassdef.mmodule
178 mmodule.uses_ffi = true
179
180 var mclass_type = mpropdef.mclassdef.bound_mtype
181
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)
185
186 v.adapt_signature(mpropdef, arguments)
187 v.unbox_signature_extern(mpropdef, arguments)
188
189 arguments.shift
190
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)
197
198 if param_mtype.is_cprimitive then
199 arguments_for_c.add(arg.name)
200 else
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}")
205 end
206 end
207
208 if return_mtype.is_cprimitive then
209 v.add("{recv_var} = {externname}({arguments_for_c.join(", ")});")
210 else
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;")
214 end
215 recv_var = v.box_extern(recv_var, return_mtype)
216 v.ret(recv_var)
217
218 compile_ffi_support_to_c(v)
219 return true
220 end
221 end
222
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)
226 do
227 var base_name = "{mmodule.c_name}._nitni"
228
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")
232
233 var c_file = "{base_name}.c"
234 write_body_to_file( mmodule, "{compdir}/{c_file}", ["\"{h_file}\""] )
235
236 files.add( "{compdir}/{c_file}" )
237 end
238 end
239
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]
244 end
245
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
249 do
250 if mtype.is_cprimitive then
251 return new RuntimeVariable(name, mtype, mtype)
252 else
253 return new RuntimeVariable("{name}->value", mtype, mtype)
254 end
255 end
256
257 # Return a `RuntimeVarible` to C user code
258 private fun ret_to_c(src: RuntimeVariable, mtype: MType)
259 do
260 if mtype.is_cprimitive then
261 add("return {src};")
262 else
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;")
267 end
268 end
269 end
270
271 redef class MType
272 private fun compile_extern_type(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
273 do
274 assert not is_cprimitive
275
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")
281 end
282 end