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);
233 context
->params
[0] = &ffi_type_pointer
;
234 context
->params
[1] = &ffi_type_pointer
;
235 rc
= ffi_prep_cif(&context
->signature
, FFI_DEFAULT_ABI
, 2, &ffi_type_void
,
243 C
->context
= context
;
247 static void psi_ffi_dtor(struct psi_context
*C
)
250 pefree(C
->context
, 1);
255 static bool psi_ffi_composite_init(struct psi_context
*C
,
256 struct psi_decl_arg
*darg
)
258 struct psi_ffi_struct_info
*info
;
260 if (darg
->engine
.type
) {
264 info
= pecalloc(1, sizeof(*info
), 1);
265 info
->eles
= psi_plist_init((psi_plist_dtor
) psi_ffi_type_free
);
267 psi_context_composite_type_elements(C
, darg
, &info
->eles
);
269 /* add terminating NULL; libffi structs do not have an element count */
271 void *null_ptr
= NULL
;
272 info
->eles
= psi_plist_add(info
->eles
, &null_ptr
);
275 info
->strct
.type
= FFI_TYPE_STRUCT
;
276 info
->strct
.alignment
= 0;
277 info
->strct
.size
= 0;
278 info
->strct
.elements
= (ffi_type
**) psi_plist_eles(info
->eles
);
280 darg
->engine
.info
= info
;
281 darg
->engine
.type
= &info
->strct
;
286 static void psi_ffi_composite_dtor(struct psi_context
*C
,
287 struct psi_decl_arg
*darg
)
289 struct psi_ffi_struct_info
*info
= darg
->engine
.info
;
292 darg
->engine
.info
= NULL
;
293 darg
->engine
.type
= NULL
;
295 struct psi_plist
*args
= NULL
;
296 struct psi_decl_type
*dtype
= psi_decl_type_get_real(darg
->type
);
298 if (dtype
->type
== PSI_T_STRUCT
) {
299 args
= dtype
->real
.strct
->args
;
300 } else if (dtype
->type
== PSI_T_UNION
) {
301 args
= dtype
->real
.unn
->args
;
305 struct psi_decl_arg
*tmp
;
307 while (psi_plist_get(args
, i
++, &tmp
)) {
308 psi_ffi_composite_dtor(C
, tmp
);
311 psi_plist_free(info
->eles
);
316 static void psi_ffi_extvar_get(ffi_cif
*sig
, void *result
, void **args
, void *data
) {
317 struct psi_decl_extvar
*evar
= data
;
319 psi_decl_extvar_get(evar
, result
);
322 static void psi_ffi_extvar_set(ffi_cif
*sig
, void *result
, void **args
, void *data
) {
323 struct psi_decl_extvar
*evar
= data
;
325 psi_decl_extvar_set(evar
, args
[0]);
328 static bool psi_ffi_decl_init(struct psi_context
*, struct psi_decl
*);
330 static bool psi_ffi_extvar_init(struct psi_context
*C
,
331 struct psi_decl_extvar
*evar
)
333 struct psi_ffi_extvar_info
*info
= pecalloc(1, sizeof(*info
), 1);
338 psi_ffi_decl_init(C
, evar
->getter
);
339 psi_ffi_decl_init(C
, evar
->setter
);
341 rc
= ffi_prep_cif(&info
->get
.signature
, FFI_DEFAULT_ABI
, 0,
342 psi_context_decl_arg_call_type(C
, evar
->getter
->func
), NULL
);
346 rc
= psi_ffi_prep_closure(&info
->get
.closure
, &info
->get
.code
,
347 &info
->get
.signature
, psi_ffi_extvar_get
, evar
);
352 info
->set
.params
[0] = psi_context_decl_arg_call_type(C
, evar
->arg
);
353 rc
= ffi_prep_cif(&info
->set
.signature
, FFI_DEFAULT_ABI
, 1,
354 &ffi_type_void
, info
->set
.params
);
358 rc
= psi_ffi_prep_closure(&info
->set
.closure
, &info
->set
.code
,
359 &info
->set
.signature
, psi_ffi_extvar_set
, evar
);
364 evar
->getter
->sym
= info
->get
.code
;
365 evar
->setter
->sym
= info
->set
.code
;
370 static void psi_ffi_extvar_dtor(struct psi_context
*C
,
371 struct psi_decl_extvar
*evar
) {
373 pefree(evar
->info
, 1);
378 static bool psi_ffi_decl_init(struct psi_context
*C
, struct psi_decl
*decl
)
382 size_t i
, c
= psi_plist_count(decl
->args
);
383 struct psi_decl_arg
*arg
;
384 struct psi_ffi_decl_info
*info
= pecalloc(1,
385 sizeof(*info
) + 2 * c
* sizeof(void *), 1);
389 for (i
= 0; psi_plist_get(decl
->args
, i
, &arg
); ++i
) {
390 info
->params
[i
] = psi_context_decl_arg_call_type(C
, arg
);
392 info
->params
[c
] = NULL
;
394 rc
= ffi_prep_cif(&info
->signature
, psi_ffi_abi(decl
->abi
->convention
),
395 c
, psi_context_decl_arg_call_type(C
, decl
->func
), info
->params
);
407 static void psi_ffi_decl_dtor(struct psi_context
*C
,
408 struct psi_decl
*decl
)
411 pefree(decl
->info
, 1);
416 static bool psi_ffi_impl_init(struct psi_context
*C
,
417 struct psi_impl
*impl
, zif_handler
*zh
)
419 struct psi_ffi_context
*context
= C
->context
;
420 struct psi_ffi_impl_info
*info
= pecalloc(1, sizeof(*info
), 1);
426 rc
= psi_ffi_prep_closure(&info
->closure
, &info
->code
,
427 &context
->signature
, psi_ffi_handler
, impl
);
442 static void psi_ffi_impl_dtor(struct psi_context
*C
, struct psi_impl
*impl
)
444 struct psi_ffi_impl_info
*info
= impl
->info
;
448 psi_ffi_closure_free(info
->closure
);
455 static bool psi_ffi_cb_init(struct psi_context
*C
,
456 struct psi_let_exp
*exp
, struct psi_impl
*impl
)
458 struct psi_ffi_callback_info
*cb_info
;
459 struct psi_ffi_decl_info
*decl_info
;
462 assert(exp
->kind
== PSI_LET_CALLBACK
);
464 if (!psi_ffi_decl_init(C
, exp
->data
.callback
->decl
)) {
468 cb_info
= pecalloc(1, sizeof(*cb_info
), 1);
469 cb_info
->impl_info
= impl
->info
;
470 cb_info
->let_exp
= exp
;
472 decl_info
= exp
->data
.callback
->decl
->info
;
473 rc
= psi_ffi_prep_closure(&cb_info
->closure
, &cb_info
->code
,
474 &decl_info
->signature
, psi_ffi_callback
, cb_info
);
481 assert(!exp
->data
.callback
->decl
->sym
);
482 exp
->data
.callback
->info
= cb_info
;
483 exp
->data
.callback
->decl
->sym
= cb_info
->code
;
488 static void psi_ffi_cb_dtor(struct psi_context
*C
,
489 struct psi_let_exp
*let_exp
, struct psi_impl
*impl
)
491 assert(let_exp
->kind
== PSI_LET_CALLBACK
);
493 psi_ffi_decl_dtor(C
, let_exp
->data
.callback
->decl
);
495 if (let_exp
->data
.callback
->info
) {
496 struct psi_ffi_callback_info
*info
= let_exp
->data
.callback
->info
;
499 psi_ffi_closure_free(info
->closure
);
502 let_exp
->data
.callback
->info
= NULL
;
506 static void psi_ffi_call(struct psi_call_frame
*frame
) {
507 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
508 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
509 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
510 struct psi_ffi_impl_info
*impl_info
;
511 struct psi_call_frame
*prev
;
514 impl_info
= impl
->info
;
515 prev
= impl_info
->frame
;
516 impl_info
->frame
= frame
;
518 ffi_call(&decl_info
->signature
, FFI_FN(decl
->sym
),
519 psi_call_frame_get_rpointer(frame
),
520 psi_call_frame_get_arg_pointers(frame
));
522 impl_info
->frame
= prev
;
526 static void psi_ffi_call_va(struct psi_call_frame
*frame
) {
528 struct psi_call_frame
*prev
;
529 struct psi_decl
*decl
= psi_call_frame_get_decl(frame
);
530 struct psi_impl
*impl
= psi_call_frame_get_impl(frame
);
531 struct psi_ffi_decl_info
*decl_info
= decl
->info
;
532 struct psi_ffi_impl_info
*impl_info
;
533 size_t i
, va_count
, argc
;
534 ffi_type
**param_types
;
536 argc
= psi_plist_count(decl
->args
);
537 va_count
= psi_call_frame_num_var_args(frame
);
538 param_types
= ecalloc(argc
+ va_count
+ 1, sizeof(ffi_type
*));
539 memcpy(param_types
, decl_info
->params
, argc
* sizeof(ffi_type
*));
540 for (i
= 0; i
< va_count
; ++i
) {
541 struct psi_call_frame_argument
*frame_arg
;
543 frame_arg
= psi_call_frame_get_var_argument(frame
, i
);
544 param_types
[argc
+ i
] = psi_ffi_impl_type(frame_arg
->va_type
);
547 psi_ffi_prep_va(&decl_info
->signature
, &signature
, argc
, va_count
, param_types
);
550 impl_info
= impl
->info
;
551 prev
= impl_info
->frame
;
552 impl_info
->frame
= frame
;
554 ffi_call(&signature
, FFI_FN(decl
->sym
),
555 psi_call_frame_get_rpointer(frame
),
556 psi_call_frame_get_arg_pointers(frame
));
558 impl_info
->frame
= prev
;
564 static void *psi_ffi_typeof_impl(struct psi_context
*C
, token_t impl_type
)
566 return psi_ffi_impl_type(impl_type
);
569 static void *psi_ffi_typeof_decl(struct psi_context
*C
, token_t decl_type
)
571 return psi_ffi_token_type(decl_type
);
574 static void *psi_ffi_copyof_type(struct psi_context
*C
, void *orig_type
)
576 ffi_type
*type
= pemalloc(sizeof(*type
), 1);
578 *type
= *(ffi_type
*) orig_type
;
582 static void psi_ffi_layoutof_type(struct psi_context
*C
, void *orig_type
,
583 struct psi_layout
*l
)
585 ffi_type
*type
= orig_type
;
587 if (!type
->size
|| !type
->alignment
) {
589 ffi_prep_cif(&tmp
, FFI_DEFAULT_ABI
, 0, type
, NULL
);
592 l
->pos
= type
->alignment
;
596 static struct psi_context_ops ops
= {
602 psi_ffi_composite_init
,
603 psi_ffi_composite_dtor
,
617 psi_ffi_layoutof_type
,
620 struct psi_context_ops
*psi_libffi_ops(void)
625 #endif /* HAVE_LIBFFI */