1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Execute FFI code by creating and loading shared libraries
17 # Triggers the compilation of the library when needed.
18 module dynamic_loading_ffi
is ldflags
"-ldl"
20 import naive_interpreter
22 import on_demand_compiler
29 // C structure behind `CallArg`
30 typedef union nit_call_arg {
37 uint16_t value_UInt16;
39 uint32_t value_UInt32;
44 // Signature shared by all entry points
45 typedef int (*nit_foreign_lib_entry)(int, nit_call_arg*, nit_call_arg*);
48 # Single cell in the list of arguments sent to foreign code (and received)
50 # Each cell can hold all primitive types and pointers to Nit class instances.
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* `}
55 # Initialize an array of `CallArg` of `length
` elements
56 new (length: Int) `{ return calloc(length, sizeof(nit_call_arg)); `}
58 # Get the element at `index` after `self`
59 fun [](index
: Int): CallArg `{ return self + index; `}
61 # The `Int` held by this cell
62 fun int: Int `{ return self->value_Int; `}
64 # The `Int` held by this cell
65 fun int
=(value
: Int) `{ self->value_Int = value; `}
67 # The `Bool` held by this cell
68 fun bool: Bool `{ return self->value_Bool; `}
70 # The `Bool` held by this cell
71 fun bool
=(value
: Bool) `{ self->value_Bool = value; `}
73 # The `Char` held by this cell
74 fun char: Char `{ return self->value_Char; `}
76 # The `Char` held by this cell
77 fun char
=(value
: Char) `{ self->value_Char = value; `}
79 # The `Byte` held by this cell
80 fun byte: Byte `{ return self->value_Byte; `}
82 # The `Byte` held by this cell
83 fun byte
=(value
: Byte) `{ self->value_Byte = value; `}
85 # The `Int` held by this cell
86 fun int8: Int8 `{ return self->value_Int8; `}
88 # The `Int` held by this cell
89 fun int8
=(value
: Int8) `{ self->value_Int8 = value; `}
91 # The `Int` held by this cell
92 fun int16: Int16 `{ return self->value_Int16; `}
94 # The `Int` held by this cell
95 fun int16
=(value
: Int16) `{ self->value_Int16 = value; `}
97 # The `Int` held by this cell
98 fun uint16: UInt16 `{ return self->value_UInt16; `}
100 # The `Int` held by this cell
101 fun uint16
=(value
: UInt16) `{ self->value_UInt16 = value; `}
103 # The `Int` held by this cell
104 fun int32: Int32 `{ return self->value_Int32; `}
106 # The `Int` held by this cell
107 fun int32
=(value
: Int32) `{ self->value_Int32 = value; `}
109 # The `Int` held by this cell
110 fun uint32: UInt32 `{ return self->value_UInt32; `}
112 # The `Int` held by this cell
113 fun uint32
=(value
: UInt32) `{ self->value_UInt32 = value; `}
115 # The `Float` held by this cell
116 fun float: Float `{ return self->value_Float; `}
118 # The `Float` held by this cell
119 fun float
=(value
: Float) `{ self->value_Float = value; `}
121 # The `Pointer` held by this cell
122 fun pointer: Pointer `{ return self->value_Pointer; `}
124 # The `Pointer` held by this cell
125 fun pointer
=(value
: Pointer) `{ self->value_Pointer = value; `}
127 # The `Instance` held by this cell
128 fun instance: Instance `{ return (Instance)self->value_Pointer; `}
130 # The `Instance` held by this cell
131 fun instance
=(value
: Instance) `{ self->value_Pointer = value; `}
133 # The `NativeString` held by this cell
134 fun native_string: NativeString `{ return (char*)self->value_Pointer; `}
136 # Set the content of this cell according to `static_type`
138 # Opposite of `to_instance`.
139 fun from_static_type
(value
: Instance, static_type
: MType)
141 if static_type
.name
== "Int" then
142 assert value
isa PrimitiveInstance[Int]
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
== "NativeString" then
172 assert value
isa PrimitiveInstance[NativeString]
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
178 self.instance
= value
182 # Get the content of this cell as an `Instance` of the `static_type`
184 # Opposite of `from_static_type`.
185 fun to_instance
(static_type
: MType, v
: NaiveInterpreter): Instance
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
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
== "NativeString" then
211 var instance
= new PrimitiveInstance[NativeString](static_type
, self.native_string
)
212 v
.init_instance_primitive 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
225 # Handle to foreign code library
226 private extern class ForeignCodeLib
227 # Open and load the library at `path`
228 new dlopen
(path
: NativeString) `{
229 return dlopen(path, RTLD_LOCAL | RTLD_NOW);
232 # Find the `ForeignCodeEntry` at `symbol_name`
233 fun dlsym
(symbol_name
: NativeString): ForeignCodeEntry `{
234 return dlsym(self, symbol_name);
238 private fun dlerror
: NativeString `{ return dlerror(); `}
240 # Handle to an implementation function in a `ForeignCodeLib`
241 private extern class ForeignCodeEntry`{ nit_foreign_lib_entry `}
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);
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
253 redef fun call_extern
(v
, mpropdef
, args
, frame
)
255 # Fallback the default error if this method is not supported
256 if not supported_by_dynamic_ffi
then return super
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
264 var lib
= amodule
.foreign_code_lib
(v
)
265 if lib
== null then return v
.error_instance
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
275 foreign_entry_cache
= entry
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
283 var native_args
= new CallArg(args
.length
)
287 var native_arg
= native_args
[a
]
288 native_arg
.from_static_type
(arg
, mpropdef
.mclassdef
.mclass
.mclass_type
)
291 for param
in mpropdef
.msignature
.mparameters
do
293 var native_arg
= native_args
[a
]
294 native_arg
.from_static_type
(arg
, param
.mtype
)
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
)
303 v
.fatal
"FFI Error: Native code library reported an error"
308 var return_mtype
= mpropdef
.msignature
.return_mtype
309 if is_init
then return_mtype
= mpropdef
.mclassdef
.mclass
.mclass_type
312 if return_mtype
== null then
315 return_value
= native_return
.to_instance
(return_mtype
, v
)
327 private var foreign_code_lib_cache
: nullable ForeignCodeLib = null
329 # Handle to the external library with FFI code
330 private fun foreign_code_lib
(v
: NaiveInterpreter): nullable ForeignCodeLib
332 var lib
= foreign_code_lib_cache
333 if lib
!= null then return lib
335 var mmodule
= mmodule
336 assert mmodule
!= null
338 var foreign_code_lib_path
= v
.foreign_code_lib_path
(mmodule
)
341 compile_foreign_lib v
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}"
349 foreign_code_lib_cache
= lib