rename `NativeString` to `CString`
[nit.git] / src / interpreter / dynamic_loading_ffi / dynamic_loading_ffi.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Execute FFI code by creating and loading shared libraries
16 #
17 # Triggers the compilation of the library when needed.
18 module dynamic_loading_ffi is ldflags "-ldl"
19
20 import naive_interpreter
21
22 import on_demand_compiler
23 import ffi
24
25 in "C Header" `{
26 #include <dlfcn.h>
27 #include <inttypes.h>
28
29 // C structure behind `CallArg`
30 typedef union nit_call_arg {
31 long value_Int;
32 int value_Bool;
33 uint32_t value_Char;
34 uint8_t value_Byte;
35 int8_t value_Int8;
36 int16_t value_Int16;
37 uint16_t value_UInt16;
38 int32_t value_Int32;
39 uint32_t value_UInt32;
40 double value_Float;
41 void* value_Pointer;
42 } nit_call_arg;
43
44 // Signature shared by all entry points
45 typedef int (*nit_foreign_lib_entry)(int, nit_call_arg*, nit_call_arg*);
46 `}
47
48 # Single cell in the list of arguments sent to foreign code (and received)
49 #
50 # Each cell can hold all primitive types and pointers to Nit class instances.
51 #
52 # Since this is a C pointer, it acts as both a single cell and an array.
53 private extern class CallArg `{ nit_call_arg* `}
54
55 # Initialize an array of `CallArg` of `length` elements
56 new (length: Int) `{ return calloc(length, sizeof(nit_call_arg)); `}
57
58 # Get the element at `index` after `self`
59 fun [](index: Int): CallArg `{ return self + index; `}
60
61 # The `Int` held by this cell
62 fun int: Int `{ return self->value_Int; `}
63
64 # The `Int` held by this cell
65 fun int=(value: Int) `{ self->value_Int = value; `}
66
67 # The `Bool` held by this cell
68 fun bool: Bool `{ return self->value_Bool; `}
69
70 # The `Bool` held by this cell
71 fun bool=(value: Bool) `{ self->value_Bool = value; `}
72
73 # The `Char` held by this cell
74 fun char: Char `{ return self->value_Char; `}
75
76 # The `Char` held by this cell
77 fun char=(value: Char) `{ self->value_Char = value; `}
78
79 # The `Byte` held by this cell
80 fun byte: Byte `{ return self->value_Byte; `}
81
82 # The `Byte` held by this cell
83 fun byte=(value: Byte) `{ self->value_Byte = value; `}
84
85 # The `Int` held by this cell
86 fun int8: Int8 `{ return self->value_Int8; `}
87
88 # The `Int` held by this cell
89 fun int8=(value: Int8) `{ self->value_Int8 = value; `}
90
91 # The `Int` held by this cell
92 fun int16: Int16 `{ return self->value_Int16; `}
93
94 # The `Int` held by this cell
95 fun int16=(value: Int16) `{ self->value_Int16 = value; `}
96
97 # The `Int` held by this cell
98 fun uint16: UInt16 `{ return self->value_UInt16; `}
99
100 # The `Int` held by this cell
101 fun uint16=(value: UInt16) `{ self->value_UInt16 = value; `}
102
103 # The `Int` held by this cell
104 fun int32: Int32 `{ return self->value_Int32; `}
105
106 # The `Int` held by this cell
107 fun int32=(value: Int32) `{ self->value_Int32 = value; `}
108
109 # The `Int` held by this cell
110 fun uint32: UInt32 `{ return self->value_UInt32; `}
111
112 # The `Int` held by this cell
113 fun uint32=(value: UInt32) `{ self->value_UInt32 = value; `}
114
115 # The `Float` held by this cell
116 fun float: Float `{ return self->value_Float; `}
117
118 # The `Float` held by this cell
119 fun float=(value: Float) `{ self->value_Float = value; `}
120
121 # The `Pointer` held by this cell
122 fun pointer: Pointer `{ return self->value_Pointer; `}
123
124 # The `Pointer` held by this cell
125 fun pointer=(value: Pointer) `{ self->value_Pointer = value; `}
126
127 # The `Instance` held by this cell
128 fun instance: Instance is light_ffi `{ return (Instance)self->value_Pointer; `}
129
130 # The `Instance` held by this cell
131 fun instance=(value: Instance) is light_ffi `{ self->value_Pointer = value; `}
132
133 # The `CString` held by this cell
134 fun native_string: CString `{ return (char*)self->value_Pointer; `}
135
136 # Set the content of this cell according to `static_type`
137 #
138 # Opposite of `to_instance`.
139 fun from_static_type(value: Instance, static_type: MType)
140 do
141 if static_type.name == "Int" then
142 assert value isa PrimitiveInstance[Int]
143 self.int = value.val
144 else if static_type.name == "Bool" then
145 assert value isa PrimitiveInstance[Bool]
146 self.bool = value.val
147 else if static_type.name == "Char" then
148 assert value isa PrimitiveInstance[Char]
149 self.char = value.val
150 else if static_type.name == "Byte" then
151 assert value isa PrimitiveInstance[Byte]
152 self.byte = value.val
153 else if static_type.name == "Int8" then
154 assert value isa PrimitiveInstance[Int8]
155 self.int8 = value.val
156 else if static_type.name == "Int16" then
157 assert value isa PrimitiveInstance[Int16]
158 self.int16 = value.val
159 else if static_type.name == "UInt16" then
160 assert value isa PrimitiveInstance[UInt16]
161 self.uint16 = value.val
162 else if static_type.name == "Int32" then
163 assert value isa PrimitiveInstance[Int32]
164 self.int32 = value.val
165 else if static_type.name == "UInt32" then
166 assert value isa PrimitiveInstance[UInt32]
167 self.uint32 = value.val
168 else if static_type.name == "Float" then
169 assert value isa PrimitiveInstance[Float]
170 self.float = value.val
171 else if static_type.name == "CString" then
172 assert value isa PrimitiveInstance[CString]
173 self.pointer = value.val
174 else if static_type isa MClassType and static_type.mclass.kind == extern_kind then
175 assert value isa PrimitiveInstance[Pointer] else print value.class_name
176 self.pointer = value.val
177 else
178 self.instance = value
179 end
180 end
181
182 # Get the content of this cell as an `Instance` of the `static_type`
183 #
184 # Opposite of `from_static_type`.
185 fun to_instance(static_type: MType, v: NaiveInterpreter): Instance
186 do
187 var name = static_type.name
188 if name == "Int" then
189 return v.int_instance(self.int)
190 else if name == "Bool" then
191 return if self.bool then
192 v.true_instance
193 else v.false_instance
194 else if name == "Char" then
195 return v.char_instance(self.char)
196 else if name == "Byte" then
197 return v.byte_instance(self.byte)
198 else if name == "Int8" then
199 return v.int8_instance(self.int8)
200 else if name == "Int16" then
201 return v.int16_instance(self.int16)
202 else if name == "UInt16" then
203 return v.uint16_instance(self.uint16)
204 else if name == "Int32" then
205 return v.int32_instance(self.int32)
206 else if name == "UInt32" then
207 return v.uint32_instance(self.uint32)
208 else if name == "Float" then
209 return v.float_instance(self.float)
210 else if name == "CString" then
211 var instance = new PrimitiveInstance[CString](static_type, self.native_string)
212 v.init_instance_primitive instance
213 return instance
214 else if static_type isa MClassType and static_type.mclass.kind == extern_kind then
215 # We tag it with the most precise known type
216 var instance = new PrimitiveInstance[Pointer](static_type, self.pointer)
217 v.init_instance_primitive instance
218 return instance
219 else
220 return self.instance
221 end
222 end
223 end
224
225 # Handle to foreign code library
226 private extern class ForeignCodeLib
227 # Open and load the library at `path`
228 new dlopen(path: CString) `{
229 return dlopen(path, RTLD_LOCAL | RTLD_NOW);
230 `}
231
232 # Find the `ForeignCodeEntry` at `symbol_name`
233 fun dlsym(symbol_name: CString): ForeignCodeEntry `{
234 return dlsym(self, symbol_name);
235 `}
236 end
237
238 private fun dlerror: CString `{ return dlerror(); `}
239
240 # Handle to an implementation function in a `ForeignCodeLib`
241 private extern class ForeignCodeEntry`{ nit_foreign_lib_entry `}
242
243 # Invoke the implementation function by passing `args` and receive the return value
244 fun call(argc: Int, args, ret: CallArg): Bool `{
245 return ((nit_foreign_lib_entry)self)(argc, args, ret);
246 `}
247 end
248
249 redef class AMethPropdef
250 # Handle to the entrypoint of this method in the foreign code library
251 private var foreign_entry_cache: nullable ForeignCodeEntry = null
252
253 redef fun call_extern(v, mpropdef, args, frame)
254 do
255 # Fallback the default error if this method is not supported
256 if not supported_by_dynamic_ffi then return super
257
258 var entry = foreign_entry_cache
259 if entry == null then
260 # Get handle to foreign code lib
261 var amodule = v.modelbuilder.mmodule2node(mpropdef.mclassdef.mmodule)
262 assert amodule != null
263
264 var lib = amodule.foreign_code_lib(v)
265 if lib == null then return v.error_instance
266
267 # Get handle to implementation function
268 entry = lib.dlsym(mpropdef.foreign_lib_entry_cname.to_cstring)
269 if entry.address_is_null then
270 print mpropdef.foreign_lib_entry_cname
271 v.fatal "FFI Error: Cannot find method {mpropdef.name} in foreign code library."
272 return v.error_instance
273 end
274
275 foreign_entry_cache = entry
276 end
277
278 # Prepare to send args to foreign code lib
279 var is_init = mpropdef.mproperty.is_init
280 if is_init then args.shift
281 var native_args_length = args.length
282
283 var native_args = new CallArg(args.length)
284 var a = 0
285 if not is_init then
286 var arg = args[a]
287 var native_arg = native_args[a]
288 native_arg.from_static_type(arg, mpropdef.mclassdef.mclass.mclass_type)
289 a += 1
290 end
291 for param in mpropdef.msignature.mparameters do
292 var arg = args[a]
293 var native_arg = native_args[a]
294 native_arg.from_static_type(arg, param.mtype)
295 a += 1
296 end
297
298 # Allocate memory for the return value
299 var native_return = new CallArg(1)
300 var error = entry.call(native_args_length, native_args, native_return)
301
302 if error then
303 v.fatal "FFI Error: Native code library reported an error"
304 return null
305 end
306
307 # Get the result
308 var return_mtype = mpropdef.msignature.return_mtype
309 if is_init then return_mtype = mpropdef.mclassdef.mclass.mclass_type
310
311 var return_value
312 if return_mtype == null then
313 return_value = null
314 else
315 return_value = native_return.to_instance(return_mtype, v)
316 end
317
318 native_args.free
319 native_return.free
320
321 return return_value
322 end
323 end
324
325 redef class AModule
326
327 private var foreign_code_lib_cache: nullable ForeignCodeLib = null
328
329 # Handle to the external library with FFI code
330 private fun foreign_code_lib(v: NaiveInterpreter): nullable ForeignCodeLib
331 do
332 var lib = foreign_code_lib_cache
333 if lib != null then return lib
334
335 var mmodule = mmodule
336 assert mmodule != null
337
338 var foreign_code_lib_path = v.foreign_code_lib_path(mmodule)
339
340 # Compile lib
341 compile_foreign_lib v
342
343 lib = new ForeignCodeLib.dlopen(foreign_code_lib_path.to_cstring)
344 if lib.address_is_null then
345 v.fatal "FFI Error: Cannot load foreign code library for {mmodule.name}: {dlerror.to_s}"
346 return null
347 end
348
349 foreign_code_lib_cache = lib
350 return lib
351 end
352 end