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 *******************************************************************************/
26 #include "php_psi_stdinc.h"
33 #include "libffi_compat.h"
36 static ffi_type
*ffi_type_sint128
;
37 static ffi_type
*ffi_type_uint128
;
40 struct psi_ffi_context
{
45 struct psi_ffi_callback_info
{
46 struct psi_ffi_impl_info
*impl_info
;
47 struct psi_let_exp
*let_exp
;
53 struct psi_ffi_decl_info
{
55 struct psi_ffi_struct_info
*rv_array
;
59 struct psi_ffi_extvar_info
{
73 struct psi_ffi_impl_info
{
74 struct psi_context
*context
;
75 struct psi_call_frame
*frame
;
81 struct psi_ffi_struct_info
{
83 struct psi_plist
*eles
;
86 static inline ffi_type
*psi_ffi_token_type(token_t t
)
93 return &ffi_type_void
;
95 return &ffi_type_sint8
;
97 return &ffi_type_uint8
;
99 return &ffi_type_sint16
;
101 return &ffi_type_uint16
;
103 return &ffi_type_sint32
;
105 return &ffi_type_uint32
;
107 return &ffi_type_sint64
;
109 return &ffi_type_uint64
;
112 return ffi_type_sint128
;
114 return ffi_type_uint128
;
117 return &ffi_type_uchar
;
119 return &ffi_type_sint
;
121 return &ffi_type_float
;
123 return &ffi_type_double
;
124 #ifdef HAVE_LONG_DOUBLE
125 case PSI_T_LONG_DOUBLE
:
126 return &ffi_type_longdouble
;
130 return &ffi_type_pointer
;
133 static inline ffi_type
*psi_ffi_impl_type(token_t impl_type
)
137 return &ffi_type_sint8
;
139 return &ffi_type_sint64
;
141 return &ffi_type_pointer
;
144 return &ffi_type_double
;
145 EMPTY_SWITCH_DEFAULT_CASE();
150 static void psi_ffi_type_free(ffi_type
**typ_ptr
)
155 static inline ffi_abi
psi_ffi_abi(zend_string
*convention
) {
156 if (FFI_LAST_ABI
- 2 != FFI_FIRST_ABI
) {
157 #ifdef HAVE_FFI_STDCALL
158 if (zend_string_equals_literal(convention
, "stdcall")) {
162 #ifdef HAVE_FFI_FASTCALL
163 if (zend_string_equals_literal(convention
, "fastcall")) {
168 return FFI_DEFAULT_ABI
;
171 static void psi_ffi_handler(ffi_cif
*sig
, void *result
, void **args
, void *data
)
173 struct psi_impl
*impl
= data
;
174 struct psi_ffi_impl_info
*info
= impl
->info
;
176 psi_context_call(info
->context
, *(zend_execute_data
**)args
[0], *(zval
**)args
[1], impl
);
179 static void psi_ffi_callback(ffi_cif
*sig
, void *result
, void **args
, void *data
)
181 struct psi_ffi_callback_info
*cb_info
= data
;
182 struct psi_call_frame_callback cb_data
;
184 assert(cb_info
->impl_info
->frame
);
186 cb_data
.cb
= cb_info
->let_exp
;
187 cb_data
.argc
= sig
->nargs
;
189 cb_data
.rval
= result
;
191 psi_call_frame_do_callback(cb_info
->impl_info
->frame
, &cb_data
);
194 static bool psi_ffi_load()
197 ffi_type
*i128
, *u128
;
199 i128
= pecalloc(1, 3*sizeof(ffi_type
), 1);
200 i128
->type
= FFI_TYPE_STRUCT
;
202 i128
->elements
= (ffi_type
**) (i128
+ 1);
203 i128
->elements
[0] = &ffi_type_sint64
;
204 i128
->elements
[1] = &ffi_type_sint64
;
206 ffi_type_sint128
= i128
;
208 u128
= pecalloc(1, 3*sizeof(ffi_type
), 1);
209 u128
->type
= FFI_TYPE_STRUCT
;
211 u128
->elements
= (ffi_type
**) (u128
+ 1);
212 u128
->elements
[0] = &ffi_type_uint64
;
213 u128
->elements
[1] = &ffi_type_uint64
;
215 ffi_type_uint128
= u128
;
220 static void psi_ffi_free()
223 free(ffi_type_sint128
);
224 free(ffi_type_uint128
);
228 static bool psi_ffi_init(struct psi_context
*C
)
231 struct psi_ffi_context
*context
= pecalloc(1, sizeof(*context
), 1);
232 ffi_abi abi
= FFI_DEFAULT_ABI
;
234 #if HAVE_FFI_FASTCALL
238 context
->params
[0] = &ffi_type_pointer
;
239 context
->params
[1] = &ffi_type_pointer
;
240 rc
= ffi_prep_cif(&context
->signature
, abi
, 2, &ffi_type_void
,
248 C
->context
= context
;
252 static void psi_ffi_dtor(struct psi_context
*C
)
255 pefree(C
->context
, 1);
260 static bool psi_ffi_composite_init(struct psi_context
*C
,
261 struct psi_decl_arg
*darg
)
263 struct psi_ffi_struct_info
*info
;
265 if (darg
->engine
.type
) {
269 info
= pecalloc(1, sizeof(*info
), 1);
270 info
->eles
= psi_plist_init((psi_plist_dtor
) psi_ffi_type_free
);
272 psi_context_composite_type_elements(C
, darg
, &info
->eles
);
274 /* add terminating NULL; libffi structs do not have an element count */
276 void *null_ptr
= NULL
;
277 info
->eles
= psi_plist_add(info
->eles
, &null_ptr
);
280 info
->strct
.type
= FFI_TYPE_STRUCT
;
281 info
->strct
.alignment
= 0;
282 info
->strct
.size
= 0;
283 info
->strct
.elements
= (ffi_type
**) psi_plist_eles(info
->eles
);
285 darg
->engine
.info
= info
;
286 darg
->engine
.type
= &info
->strct
;
291 static void psi_ffi_composite_dtor(struct psi_context
*C
,
292 struct psi_decl_arg
*darg
)
294 struct psi_ffi_struct_info
*info
= darg
->engine
.info
;
297 darg
->engine
.info
= NULL
;
298 darg
->engine
.type
= NULL
;
300 psi_plist_free(info
->eles
);
305 static void psi_ffi_extvar_get(ffi_cif
*sig
, void *result
, void **args
, void *data
) {
306 struct psi_decl_extvar
*evar
= data
;
308 psi_decl_extvar_get(evar
, result
);
311 static void psi_ffi_extvar_set(ffi_cif
*sig
, void *result
, void **args
, void *data
) {
312 struct psi_decl_extvar
*evar
= data
;
314 psi_decl_extvar_set(evar
, args
[0]);
317 static bool psi_ffi_decl_init(struct psi_context
*, struct psi_decl
*);
319 static bool psi_ffi_extvar_init(struct psi_context
*C
,
320 struct psi_decl_extvar
*evar
)
322 struct psi_ffi_extvar_info
*info
= pecalloc(1, sizeof(*info
), 1);
327 psi_ffi_decl_init(C
, evar
->getter
);
328 psi_ffi_decl_init(C
, evar
->setter
);
330 rc
= ffi_prep_cif(&info
->get
.signature
, FFI_DEFAULT_ABI
, 0,
331 psi_context_decl_arg_call_type(C
, evar
->getter
->func
), NULL
);
335 rc
= psi_ffi_prep_closure(&info
->get
.closure
, &info
->get
.code
,
336 &info
->get
.signature
, psi_ffi_extvar_get
, evar
);
341 info
->set
.params
[0] = psi_context_decl_arg_call_type(C
, evar
->arg
);
342 rc
= ffi_prep_cif(&info
->set
.signature
, FFI_DEFAULT_ABI
, 1,
343 &ffi_type_void
, info
->set
.params
);
347 rc
= psi_ffi_prep_closure(&info
->set
.closure
, &info
->set
.code
,
348 &info
->set
.signature
, psi_ffi_extvar_set
, evar
);
353 evar
->getter
->sym
= info
->get
.code
;
354 evar
->setter
->sym
= info
->set
.code
;
359 static void psi_ffi_extvar_dtor(struct psi_context
*C
,
360 struct psi_decl_extvar
*evar
) {
362 pefree(evar
->info
, 1);
367 static bool psi_ffi_decl_init(struct psi_context
*C
, struct psi_decl
*decl
)
371 size_t i
, c
= psi_plist_count(decl
->args
);
372 struct psi_decl_arg
*arg
;
373 struct psi_ffi_decl_info
*info
= pecalloc(1,
374 sizeof(*info
) + 2 * c
* sizeof(void *), 1);
378 for (i
= 0; psi_plist_get(decl
->args
, i
, &arg
); ++i
) {
379 info
->params
[i
] = psi_context_decl_arg_call_type(C
, arg
);
381 info
->params
[c
] = NULL
;
383 rc
= ffi_prep_cif(&info
->signature
, psi_ffi_abi(decl
->abi
->convention
),
384 c
, psi_context_decl_arg_call_type(C
, decl
->func
), info
->params
);
396 static void psi_ffi_decl_dtor(struct psi_context
*C
,
397 struct psi_decl
*decl
)
400 pefree(decl
->info
, 1);
405 static bool psi_ffi_impl_init(struct psi_context
*C
,
406 struct psi_impl
*impl
, zif_handler
*zh
)
408 struct psi_ffi_context
*context
= C
->context
;
409 struct psi_ffi_impl_info
*info
= pecalloc(1, sizeof(*info
), 1);
415 rc
= psi_ffi_prep_closure(&info
->closure
, &info
->code
,
416 &context
->signature
, psi_ffi_handler
, impl
);
431 static void psi_ffi_impl_dtor(struct psi_context
*C
, struct psi_impl
*impl
)
433 struct psi_ffi_impl_info
*info
= impl
->info
;
437 psi_ffi_closure_free(info
->closure
);
444 static bool psi_ffi_cb_init(struct psi_context
*C
,
445 struct psi_let_exp
*exp
, struct psi_impl
*impl
)
447 struct psi_ffi_callback_info
*cb_info
;
448 struct psi_ffi_decl_info
*decl_info
;
451 assert(exp
->kind
== PSI_LET_CALLBACK
);
453 if (!psi_ffi_decl_init(C
, exp
->data
.callback
->decl
)) {
457 cb_info
= pecalloc(1, sizeof(*cb_info
), 1);
458 cb_info
->impl_info
= impl
->info
;
459 cb_info
->let_exp
= exp
;
461 decl_info
= exp
->data
.callback
->decl
->info
;
462 rc
= psi_ffi_prep_closure(&cb_info
->closure
, &cb_info
->code
,
463 &decl_info
->signature
, psi_ffi_callback
, cb_info
);
470 assert(!exp
->data
.callback
->decl
->sym
);
471 exp
->data
.callback
->info
= cb_info
;
472 exp
->data
.callback
->decl
->sym
= cb_info
->code
;
477 static void psi_ffi_cb_dtor(struct psi_context
*C
,
478 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
480 assert(let_exp
->kind
== PSI_LET_CALLBACK
);
482 psi_ffi_decl_dtor(C
, let_exp
->data
.callback
->decl
);
484 if (let_exp
->data
.callback
->info
) {
485 struct psi_ffi_callback_info
*info
= let_exp
->data
.callback
->info
;
488 psi_ffi_closure_free(info
->closure
);
491 let_exp
->data
.callback
->info
= NULL
;
495 static void psi_ffi_call(struct psi_call_frame
*frame
) {
496 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
497 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
498 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
499 struct psi_ffi_impl_info
*impl_info
;
500 struct psi_call_frame
*prev
;
503 impl_info
= impl
->info
;
504 prev
= impl_info
->frame
;
505 impl_info
->frame
= frame
;
507 ffi_call(&decl_info
->signature
, FFI_FN(decl
->sym
),
508 psi_call_frame_get_rpointer(frame
),
509 psi_call_frame_get_arg_pointers(frame
));
511 impl_info
->frame
= prev
;
515 static void psi_ffi_call_va(struct psi_call_frame
*frame
) {
517 struct psi_call_frame
*prev
;
518 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
519 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
520 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
521 struct psi_ffi_impl_info
*impl_info
;
522 size_t i
, va_count
, argc
;
523 ffi_type
**param_types
;
525 argc
= psi_plist_count(decl
->args
);
526 va_count
= psi_call_frame_num_var_args(frame
);
527 param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
528 memcpy(param_types
, decl_info
->params
, argc
* sizeof(ffi_type
*));
529 for (i
= 0; i
< va_count
; ++i
) {
530 struct psi_call_frame_argument
*frame_arg
;
532 frame_arg
= psi_call_frame_get_var_argument(frame
, i
);
533 param_types
[argc
+ i
] = psi_ffi_impl_type(frame_arg
->va_type
);
536 psi_ffi_prep_va(&decl_info
->signature
, &signature
, argc
, va_count
, param_types
);
539 impl_info
= impl
->info
;
540 prev
= impl_info
->frame
;
541 impl_info
->frame
= frame
;
543 ffi_call(&signature
, FFI_FN(decl
->sym
),
544 psi_call_frame_get_rpointer(frame
),
545 psi_call_frame_get_arg_pointers(frame
));
547 impl_info
->frame
= prev
;
553 static void *psi_ffi_typeof_impl(struct psi_context
*C
, token_t impl_type
)
555 return psi_ffi_impl_type(impl_type
);
558 static void *psi_ffi_typeof_decl(struct psi_context
*C
, token_t decl_type
)
560 return psi_ffi_token_type(decl_type
);
563 static void *psi_ffi_copyof_type(struct psi_context
*C
, void *orig_type
)
565 ffi_type
*type
= pemalloc(sizeof(*type
), 1);
567 *type
= *(ffi_type
*) orig_type
;
571 static void psi_ffi_layoutof_type(struct psi_context
*C
, void *orig_type
,
572 struct psi_layout
*l
)
574 ffi_type
*type
= orig_type
;
576 if (!type
->size
|| !type
->alignment
) {
578 ffi_prep_cif(&tmp
, FFI_DEFAULT_ABI
, 0, type
, NULL
);
581 l
->pos
= type
->alignment
;
585 static struct psi_context_ops ops
= {
591 psi_ffi_composite_init
,
592 psi_ffi_composite_dtor
,
606 psi_ffi_layoutof_type
,
609 struct psi_context_ops
*psi_libffi_ops(void)
614 #endif /* HAVE_LIBFFI */