1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
29 # include "php_config.h"
37 #include "libffi_compat.h"
41 static ffi_type
*ffi_type_sint128
;
42 static ffi_type
*ffi_type_uint128
;
45 struct psi_ffi_context
{
50 struct psi_ffi_callback_info
{
51 struct psi_ffi_impl_info
*impl_info
;
52 struct psi_let_exp
*let_exp
;
58 struct psi_ffi_decl_info
{
60 struct psi_ffi_struct_info
*rv_array
;
64 struct psi_ffi_extvar_info
{
78 struct psi_ffi_impl_info
{
79 struct psi_context
*context
;
80 struct psi_call_frame
*frame
;
86 struct psi_ffi_struct_info
{
88 struct psi_plist
*eles
;
91 static inline ffi_type
*psi_ffi_token_type(token_t t
)
98 return &ffi_type_void
;
100 return &ffi_type_sint8
;
102 return &ffi_type_uint8
;
104 return &ffi_type_sint16
;
106 return &ffi_type_uint16
;
108 return &ffi_type_sint32
;
110 return &ffi_type_uint32
;
112 return &ffi_type_sint64
;
114 return &ffi_type_uint64
;
117 return ffi_type_sint128
;
119 return ffi_type_uint128
;
122 return &ffi_type_uchar
;
124 return &ffi_type_sint
;
126 return &ffi_type_float
;
128 return &ffi_type_double
;
129 #ifdef HAVE_LONG_DOUBLE
130 case PSI_T_LONG_DOUBLE
:
131 return &ffi_type_longdouble
;
135 return &ffi_type_pointer
;
138 static inline ffi_type
*psi_ffi_impl_type(token_t impl_type
)
142 return &ffi_type_sint8
;
144 return &ffi_type_sint64
;
146 return &ffi_type_pointer
;
149 return &ffi_type_double
;
150 EMPTY_SWITCH_DEFAULT_CASE();
155 static void psi_ffi_type_free(ffi_type
**typ_ptr
)
160 static inline ffi_abi
psi_ffi_abi(zend_string
*convention
) {
161 if (FFI_LAST_ABI
- 2 != FFI_FIRST_ABI
) {
162 #ifdef HAVE_FFI_STDCALL
163 if (zend_string_equals_literal(convention
, "stdcall")) {
167 #ifdef HAVE_FFI_FASTCALL
168 if (zend_string_equals_literal(convention
, "fastcall")) {
173 return FFI_DEFAULT_ABI
;
176 static void psi_ffi_handler(ffi_cif
*sig
, void *result
, void **args
, void *data
)
178 struct psi_impl
*impl
= data
;
179 struct psi_ffi_impl_info
*info
= impl
->info
;
181 psi_context_call(info
->context
, *(zend_execute_data
**)args
[0], *(zval
**)args
[1], impl
);
184 static void psi_ffi_callback(ffi_cif
*sig
, void *result
, void **args
, void *data
)
186 struct psi_ffi_callback_info
*cb_info
= data
;
187 struct psi_call_frame_callback cb_data
;
189 assert(cb_info
->impl_info
->frame
);
191 cb_data
.cb
= cb_info
->let_exp
;
192 cb_data
.argc
= sig
->nargs
;
194 cb_data
.rval
= result
;
196 psi_call_frame_do_callback(cb_info
->impl_info
->frame
, &cb_data
);
199 static bool psi_ffi_load()
202 ffi_type
*i128
, *u128
;
204 i128
= pecalloc(1, 3*sizeof(ffi_type
), 1);
205 i128
->type
= FFI_TYPE_STRUCT
;
207 i128
->elements
= (ffi_type
**) (i128
+ 1);
208 i128
->elements
[0] = &ffi_type_sint64
;
209 i128
->elements
[1] = &ffi_type_sint64
;
211 ffi_type_sint128
= i128
;
213 u128
= pecalloc(1, 3*sizeof(ffi_type
), 1);
214 u128
->type
= FFI_TYPE_STRUCT
;
216 u128
->elements
= (ffi_type
**) (u128
+ 1);
217 u128
->elements
[0] = &ffi_type_uint64
;
218 u128
->elements
[1] = &ffi_type_uint64
;
220 ffi_type_uint128
= u128
;
225 static void psi_ffi_free()
228 free(ffi_type_sint128
);
229 free(ffi_type_uint128
);
233 static bool psi_ffi_init(struct psi_context
*C
)
236 struct psi_ffi_context
*context
= pecalloc(1, sizeof(*context
), 1);
237 ffi_abi abi
= FFI_DEFAULT_ABI
;
239 #if HAVE_FFI_FASTCALL
243 context
->params
[0] = &ffi_type_pointer
;
244 context
->params
[1] = &ffi_type_pointer
;
245 rc
= ffi_prep_cif(&context
->signature
, abi
, 2, &ffi_type_void
,
253 C
->context
= context
;
257 static void psi_ffi_dtor(struct psi_context
*C
)
260 pefree(C
->context
, 1);
265 static bool psi_ffi_composite_init(struct psi_context
*C
,
266 struct psi_decl_arg
*darg
)
268 struct psi_ffi_struct_info
*info
;
270 if (darg
->engine
.type
) {
274 info
= pecalloc(1, sizeof(*info
), 1);
275 info
->eles
= psi_plist_init((psi_plist_dtor
) psi_ffi_type_free
);
277 psi_context_composite_type_elements(C
, darg
, &info
->eles
);
279 /* add terminating NULL; libffi structs do not have an element count */
281 void *null_ptr
= NULL
;
282 info
->eles
= psi_plist_add(info
->eles
, &null_ptr
);
285 info
->strct
.type
= FFI_TYPE_STRUCT
;
286 info
->strct
.alignment
= 0;
287 info
->strct
.size
= 0;
288 info
->strct
.elements
= (ffi_type
**) psi_plist_eles(info
->eles
);
290 darg
->engine
.info
= info
;
291 darg
->engine
.type
= &info
->strct
;
296 static void psi_ffi_composite_dtor(struct psi_context
*C
,
297 struct psi_decl_arg
*darg
)
299 struct psi_ffi_struct_info
*info
= darg
->engine
.info
;
302 darg
->engine
.info
= NULL
;
303 darg
->engine
.type
= NULL
;
305 psi_plist_free(info
->eles
);
310 static void psi_ffi_extvar_get(ffi_cif
*sig
, void *result
, void **args
, void *data
) {
311 struct psi_decl_extvar
*evar
= data
;
313 psi_decl_extvar_get(evar
, result
);
316 static void psi_ffi_extvar_set(ffi_cif
*sig
, void *result
, void **args
, void *data
) {
317 struct psi_decl_extvar
*evar
= data
;
319 psi_decl_extvar_set(evar
, args
[0]);
322 static bool psi_ffi_decl_init(struct psi_context
*, struct psi_decl
*);
324 static bool psi_ffi_extvar_init(struct psi_context
*C
,
325 struct psi_decl_extvar
*evar
)
327 struct psi_ffi_extvar_info
*info
= pecalloc(1, sizeof(*info
), 1);
332 psi_ffi_decl_init(C
, evar
->getter
);
333 psi_ffi_decl_init(C
, evar
->setter
);
335 rc
= ffi_prep_cif(&info
->get
.signature
, FFI_DEFAULT_ABI
, 0,
336 psi_context_decl_arg_call_type(C
, evar
->getter
->func
), NULL
);
340 rc
= psi_ffi_prep_closure(&info
->get
.closure
, &info
->get
.code
,
341 &info
->get
.signature
, psi_ffi_extvar_get
, evar
);
346 info
->set
.params
[0] = psi_context_decl_arg_call_type(C
, evar
->arg
);
347 rc
= ffi_prep_cif(&info
->set
.signature
, FFI_DEFAULT_ABI
, 1,
348 &ffi_type_void
, info
->set
.params
);
352 rc
= psi_ffi_prep_closure(&info
->set
.closure
, &info
->set
.code
,
353 &info
->set
.signature
, psi_ffi_extvar_set
, evar
);
358 evar
->getter
->sym
= info
->get
.code
;
359 evar
->setter
->sym
= info
->set
.code
;
364 static void psi_ffi_extvar_dtor(struct psi_context
*C
,
365 struct psi_decl_extvar
*evar
) {
367 pefree(evar
->info
, 1);
372 static bool psi_ffi_decl_init(struct psi_context
*C
, struct psi_decl
*decl
)
376 size_t i
, c
= psi_plist_count(decl
->args
);
377 struct psi_decl_arg
*arg
;
378 struct psi_ffi_decl_info
*info
= pecalloc(1,
379 sizeof(*info
) + 2 * c
* sizeof(void *), 1);
383 for (i
= 0; psi_plist_get(decl
->args
, i
, &arg
); ++i
) {
384 info
->params
[i
] = psi_context_decl_arg_call_type(C
, arg
);
386 info
->params
[c
] = NULL
;
388 rc
= ffi_prep_cif(&info
->signature
, psi_ffi_abi(decl
->abi
->convention
),
389 c
, psi_context_decl_arg_call_type(C
, decl
->func
), info
->params
);
401 static void psi_ffi_decl_dtor(struct psi_context
*C
,
402 struct psi_decl
*decl
)
405 pefree(decl
->info
, 1);
410 static bool psi_ffi_impl_init(struct psi_context
*C
,
411 struct psi_impl
*impl
, zif_handler
*zh
)
413 struct psi_ffi_context
*context
= C
->context
;
414 struct psi_ffi_impl_info
*info
= pecalloc(1, sizeof(*info
), 1);
420 rc
= psi_ffi_prep_closure(&info
->closure
, &info
->code
,
421 &context
->signature
, psi_ffi_handler
, impl
);
436 static void psi_ffi_impl_dtor(struct psi_context
*C
, struct psi_impl
*impl
)
438 struct psi_ffi_impl_info
*info
= impl
->info
;
442 psi_ffi_closure_free(info
->closure
);
449 static bool psi_ffi_cb_init(struct psi_context
*C
,
450 struct psi_let_exp
*exp
, struct psi_impl
*impl
)
452 struct psi_ffi_callback_info
*cb_info
;
453 struct psi_ffi_decl_info
*decl_info
;
456 assert(exp
->kind
== PSI_LET_CALLBACK
);
458 if (!psi_ffi_decl_init(C
, exp
->data
.callback
->decl
)) {
462 cb_info
= pecalloc(1, sizeof(*cb_info
), 1);
463 cb_info
->impl_info
= impl
->info
;
464 cb_info
->let_exp
= exp
;
466 decl_info
= exp
->data
.callback
->decl
->info
;
467 rc
= psi_ffi_prep_closure(&cb_info
->closure
, &cb_info
->code
,
468 &decl_info
->signature
, psi_ffi_callback
, cb_info
);
475 assert(!exp
->data
.callback
->decl
->sym
);
476 exp
->data
.callback
->info
= cb_info
;
477 exp
->data
.callback
->decl
->sym
= cb_info
->code
;
482 static void psi_ffi_cb_dtor(struct psi_context
*C
,
483 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
485 assert(let_exp
->kind
== PSI_LET_CALLBACK
);
487 psi_ffi_decl_dtor(C
, let_exp
->data
.callback
->decl
);
489 if (let_exp
->data
.callback
->info
) {
490 struct psi_ffi_callback_info
*info
= let_exp
->data
.callback
->info
;
493 psi_ffi_closure_free(info
->closure
);
496 let_exp
->data
.callback
->info
= NULL
;
500 static void psi_ffi_call(struct psi_call_frame
*frame
) {
501 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
502 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
503 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
504 struct psi_ffi_impl_info
*impl_info
;
505 struct psi_call_frame
*prev
;
508 impl_info
= impl
->info
;
509 prev
= impl_info
->frame
;
510 impl_info
->frame
= frame
;
512 ffi_call(&decl_info
->signature
, FFI_FN(decl
->sym
),
513 psi_call_frame_get_rpointer(frame
),
514 psi_call_frame_get_arg_pointers(frame
));
516 impl_info
->frame
= prev
;
520 static void psi_ffi_call_va(struct psi_call_frame
*frame
) {
522 struct psi_call_frame
*prev
;
523 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
524 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
525 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
526 struct psi_ffi_impl_info
*impl_info
;
527 size_t i
, va_count
, argc
;
528 ffi_type
**param_types
;
530 argc
= psi_plist_count(decl
->args
);
531 va_count
= psi_call_frame_num_var_args(frame
);
532 param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
533 memcpy(param_types
, decl_info
->params
, argc
* sizeof(ffi_type
*));
534 for (i
= 0; i
< va_count
; ++i
) {
535 struct psi_call_frame_argument
*frame_arg
;
537 frame_arg
= psi_call_frame_get_var_argument(frame
, i
);
538 param_types
[argc
+ i
] = psi_ffi_impl_type(frame_arg
->va_type
);
541 psi_ffi_prep_va(&decl_info
->signature
, &signature
, argc
, va_count
, param_types
);
544 impl_info
= impl
->info
;
545 prev
= impl_info
->frame
;
546 impl_info
->frame
= frame
;
548 ffi_call(&signature
, FFI_FN(decl
->sym
),
549 psi_call_frame_get_rpointer(frame
),
550 psi_call_frame_get_arg_pointers(frame
));
552 impl_info
->frame
= prev
;
558 static void *psi_ffi_typeof_impl(struct psi_context
*C
, token_t impl_type
)
560 return psi_ffi_impl_type(impl_type
);
563 static void *psi_ffi_typeof_decl(struct psi_context
*C
, token_t decl_type
)
565 return psi_ffi_token_type(decl_type
);
568 static void *psi_ffi_copyof_type(struct psi_context
*C
, void *orig_type
)
570 ffi_type
*type
= pemalloc(sizeof(*type
), 1);
572 *type
= *(ffi_type
*) orig_type
;
576 static void psi_ffi_layoutof_type(struct psi_context
*C
, void *orig_type
,
577 struct psi_layout
*l
)
579 ffi_type
*type
= orig_type
;
581 if (!type
->size
|| !type
->alignment
) {
583 ffi_prep_cif(&tmp
, FFI_DEFAULT_ABI
, 0, type
, NULL
);
586 l
->pos
= type
->alignment
;
590 static struct psi_context_ops ops
= {
596 psi_ffi_composite_init
,
597 psi_ffi_composite_dtor
,
611 psi_ffi_layoutof_type
,
614 struct psi_context_ops
*psi_libffi_ops(void)
619 #endif /* HAVE_LIBFFI */