xref: /PHP-8.2/ext/ffi/ffi.c (revision 97267215)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Author: Dmitry Stogov <dmitry@zend.com>                              |
14    +----------------------------------------------------------------------+
15 */
16 
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
20 
21 #include "php.h"
22 #include "php_ffi.h"
23 #include "ext/standard/info.h"
24 #include "php_scandir.h"
25 #include "zend_exceptions.h"
26 #include "zend_closures.h"
27 #include "zend_weakrefs.h"
28 #include "main/SAPI.h"
29 #include "zend_observer.h"
30 
31 #include <ffi.h>
32 
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 
37 #ifdef HAVE_LIBDL
38 #ifdef PHP_WIN32
39 #include "win32/param.h"
40 #include "win32/winutil.h"
41 #define GET_DL_ERROR()  php_win_err()
42 #else
43 #include <sys/param.h>
44 #define GET_DL_ERROR()  DL_ERROR()
45 #endif
46 #endif
47 
48 #ifdef HAVE_GLOB
49 #ifdef PHP_WIN32
50 #include "win32/glob.h"
51 #else
52 #include <glob.h>
53 #endif
54 #endif
55 
56 #ifndef __BIGGEST_ALIGNMENT__
57 /* XXX need something better, perhaps with regard to SIMD, etc. */
58 # define __BIGGEST_ALIGNMENT__ sizeof(size_t)
59 #endif
60 
61 ZEND_DECLARE_MODULE_GLOBALS(ffi)
62 
63 typedef enum _zend_ffi_tag_kind {
64 	ZEND_FFI_TAG_ENUM,
65 	ZEND_FFI_TAG_STRUCT,
66 	ZEND_FFI_TAG_UNION
67 } zend_ffi_tag_kind;
68 
69 static const char *zend_ffi_tag_kind_name[3] = {"enum", "struct", "union"};
70 
71 
72 typedef struct _zend_ffi_tag {
73 	zend_ffi_tag_kind      kind;
74 	zend_ffi_type         *type;
75 } zend_ffi_tag;
76 
77 typedef enum _zend_ffi_type_kind {
78 	ZEND_FFI_TYPE_VOID,
79 	ZEND_FFI_TYPE_FLOAT,
80 	ZEND_FFI_TYPE_DOUBLE,
81 #ifdef HAVE_LONG_DOUBLE
82 	ZEND_FFI_TYPE_LONGDOUBLE,
83 #endif
84 	ZEND_FFI_TYPE_UINT8,
85 	ZEND_FFI_TYPE_SINT8,
86 	ZEND_FFI_TYPE_UINT16,
87 	ZEND_FFI_TYPE_SINT16,
88 	ZEND_FFI_TYPE_UINT32,
89 	ZEND_FFI_TYPE_SINT32,
90 	ZEND_FFI_TYPE_UINT64,
91 	ZEND_FFI_TYPE_SINT64,
92 	ZEND_FFI_TYPE_ENUM,
93 	ZEND_FFI_TYPE_BOOL,
94 	ZEND_FFI_TYPE_CHAR,
95 	ZEND_FFI_TYPE_POINTER,
96 	ZEND_FFI_TYPE_FUNC,
97 	ZEND_FFI_TYPE_ARRAY,
98 	ZEND_FFI_TYPE_STRUCT,
99 } zend_ffi_type_kind;
100 
101 #include "ffi_arginfo.h"
102 
103 typedef enum _zend_ffi_flags {
104 	ZEND_FFI_FLAG_CONST      = (1 << 0),
105 	ZEND_FFI_FLAG_OWNED      = (1 << 1),
106 	ZEND_FFI_FLAG_PERSISTENT = (1 << 2),
107 } zend_ffi_flags;
108 
109 struct _zend_ffi_type {
110 	zend_ffi_type_kind     kind;
111 	size_t                 size;
112 	uint32_t               align;
113 	uint32_t               attr;
114 	union {
115 		struct {
116 			zend_string        *tag_name;
117 			zend_ffi_type_kind  kind;
118 		} enumeration;
119 		struct {
120 			zend_ffi_type *type;
121 			zend_long      length;
122 		} array;
123 		struct {
124 			zend_ffi_type *type;
125 		} pointer;
126 		struct {
127 			zend_string   *tag_name;
128 			HashTable      fields;
129 		} record;
130 		struct {
131 			zend_ffi_type *ret_type;
132 			HashTable     *args;
133 			ffi_abi        abi;
134 		} func;
135 	};
136 };
137 
138 typedef struct _zend_ffi_field {
139 	size_t                 offset;
140 	bool              is_const;
141 	bool              is_nested; /* part of nested anonymous struct */
142 	uint8_t                first_bit;
143 	uint8_t                bits;
144 	zend_ffi_type         *type;
145 } zend_ffi_field;
146 
147 typedef enum _zend_ffi_symbol_kind {
148 	ZEND_FFI_SYM_TYPE,
149 	ZEND_FFI_SYM_CONST,
150 	ZEND_FFI_SYM_VAR,
151 	ZEND_FFI_SYM_FUNC
152 } zend_ffi_symbol_kind;
153 
154 typedef struct _zend_ffi_symbol {
155 	zend_ffi_symbol_kind   kind;
156 	bool              is_const;
157 	zend_ffi_type         *type;
158 	union {
159 		void *addr;
160 		int64_t value;
161 	};
162 } zend_ffi_symbol;
163 
164 typedef struct _zend_ffi_scope {
165 	HashTable             *symbols;
166 	HashTable             *tags;
167 } zend_ffi_scope;
168 
169 typedef struct _zend_ffi {
170 	zend_object            std;
171 	DL_HANDLE              lib;
172 	HashTable             *symbols;
173 	HashTable             *tags;
174 	bool              persistent;
175 } zend_ffi;
176 
177 #define ZEND_FFI_TYPE_OWNED        (1<<0)
178 
179 #define ZEND_FFI_TYPE(t) \
180 	((zend_ffi_type*)(((uintptr_t)(t)) & ~ZEND_FFI_TYPE_OWNED))
181 
182 #define ZEND_FFI_TYPE_IS_OWNED(t) \
183 	(((uintptr_t)(t)) & ZEND_FFI_TYPE_OWNED)
184 
185 #define ZEND_FFI_TYPE_MAKE_OWNED(t) \
186 	((zend_ffi_type*)(((uintptr_t)(t)) | ZEND_FFI_TYPE_OWNED))
187 
188 #define ZEND_FFI_SIZEOF_ARG \
189 	MAX(FFI_SIZEOF_ARG, sizeof(double))
190 
191 typedef struct _zend_ffi_cdata {
192 	zend_object            std;
193 	zend_ffi_type         *type;
194 	void                  *ptr;
195 	void                  *ptr_holder;
196 	zend_ffi_flags         flags;
197 } zend_ffi_cdata;
198 
199 typedef struct _zend_ffi_ctype {
200 	zend_object            std;
201 	zend_ffi_type         *type;
202 } zend_ffi_ctype;
203 
204 static zend_class_entry *zend_ffi_exception_ce;
205 static zend_class_entry *zend_ffi_parser_exception_ce;
206 static zend_class_entry *zend_ffi_ce;
207 static zend_class_entry *zend_ffi_cdata_ce;
208 static zend_class_entry *zend_ffi_ctype_ce;
209 
210 static zend_object_handlers zend_ffi_handlers;
211 static zend_object_handlers zend_ffi_cdata_handlers;
212 static zend_object_handlers zend_ffi_cdata_value_handlers;
213 static zend_object_handlers zend_ffi_cdata_free_handlers;
214 static zend_object_handlers zend_ffi_ctype_handlers;
215 
216 static zend_internal_function zend_ffi_new_fn;
217 static zend_internal_function zend_ffi_cast_fn;
218 static zend_internal_function zend_ffi_type_fn;
219 
220 /* forward declarations */
221 static void _zend_ffi_type_dtor(zend_ffi_type *type);
222 static void zend_ffi_finalize_type(zend_ffi_dcl *dcl);
223 static bool zend_ffi_is_same_type(zend_ffi_type *type1, zend_ffi_type *type2);
224 static zend_ffi_type *zend_ffi_remember_type(zend_ffi_type *type);
225 static char *zend_ffi_parse_directives(const char *filename, char *code_pos, char **scope_name, char **lib, bool preload);
226 static ZEND_FUNCTION(ffi_trampoline);
227 static ZEND_COLD void zend_ffi_return_unsupported(zend_ffi_type *type);
228 static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type);
229 static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type);
230 static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type);
231 
232 #if FFI_CLOSURES
233 static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value);
234 #endif
235 
zend_ffi_type_dtor(zend_ffi_type * type)236 static zend_always_inline void zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */
237 {
238 	if (UNEXPECTED(ZEND_FFI_TYPE_IS_OWNED(type))) {
239 		_zend_ffi_type_dtor(type);
240 		return;
241 	}
242 }
243 /* }}} */
244 
zend_ffi_object_init(zend_object * object,zend_class_entry * ce)245 static zend_always_inline void zend_ffi_object_init(zend_object *object, zend_class_entry *ce) /* {{{ */
246 {
247 	GC_SET_REFCOUNT(object, 1);
248 	GC_TYPE_INFO(object) = GC_OBJECT | (IS_OBJ_DESTRUCTOR_CALLED << GC_FLAGS_SHIFT);
249 	object->ce = ce;
250 	object->handlers = ce->default_object_handlers;
251 	object->properties = NULL;
252 	zend_objects_store_put(object);
253 }
254 /* }}} */
255 
zend_ffi_cdata_new(zend_class_entry * class_type)256 static zend_object *zend_ffi_cdata_new(zend_class_entry *class_type) /* {{{ */
257 {
258 	zend_ffi_cdata *cdata;
259 
260 	cdata = emalloc(sizeof(zend_ffi_cdata));
261 
262 	zend_ffi_object_init(&cdata->std, class_type);
263 
264 	cdata->type = NULL;
265 	cdata->ptr = NULL;
266 	cdata->flags = 0;
267 
268 	return &cdata->std;
269 }
270 /* }}} */
271 
zend_ffi_func_ptr_are_compatible(zend_ffi_type * dst_type,zend_ffi_type * src_type)272 static bool zend_ffi_func_ptr_are_compatible(zend_ffi_type *dst_type, zend_ffi_type *src_type) /* {{{ */
273 {
274 	uint32_t dst_argc, src_argc, i;
275 	zend_ffi_type *dst_arg, *src_arg;
276 
277 	ZEND_ASSERT(dst_type->kind == ZEND_FFI_TYPE_FUNC);
278 	ZEND_ASSERT(src_type->kind == ZEND_FFI_TYPE_FUNC);
279 
280 	/* Ensure calling convention matches */
281 	if (dst_type->func.abi != src_type->func.abi) {
282 		return 0;
283 	}
284 
285 	/* Ensure variadic attr matches */
286 	if ((dst_type->attr & ZEND_FFI_ATTR_VARIADIC) != (src_type->attr & ZEND_FFI_ATTR_VARIADIC)) {
287 		return 0;
288 	}
289 
290 	/* Ensure same arg count */
291 	dst_argc = dst_type->func.args ? zend_hash_num_elements(dst_type->func.args) : 0;
292 	src_argc = src_type->func.args ? zend_hash_num_elements(src_type->func.args) : 0;
293 	if (dst_argc != src_argc) {
294 		return 0;
295 	}
296 
297 	/* Ensure compatible ret_type */
298 	if (!zend_ffi_is_compatible_type(dst_type->func.ret_type, src_type->func.ret_type)) {
299 		return 0;
300 	}
301 
302 	/* Ensure compatible args */
303 	for (i = 0; i < dst_argc; i++) {
304 		dst_arg = zend_hash_index_find_ptr(dst_type->func.args, i);
305 		src_arg = zend_hash_index_find_ptr(src_type->func.args, i);
306 		if (!zend_ffi_is_compatible_type(ZEND_FFI_TYPE(dst_arg), ZEND_FFI_TYPE(src_arg))) {
307 			return 0;
308 		}
309 	}
310 
311 	return 1;
312 }
313 /* }}} */
314 
zend_ffi_is_compatible_type(zend_ffi_type * dst_type,zend_ffi_type * src_type)315 static bool zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type) /* {{{ */
316 {
317 	while (1) {
318 		if (dst_type == src_type) {
319 			return 1;
320 		} else if (dst_type->kind == src_type->kind) {
321 			if (dst_type->kind < ZEND_FFI_TYPE_POINTER) {
322 				return 1;
323 			} else if (dst_type->kind == ZEND_FFI_TYPE_POINTER) {
324 				dst_type = ZEND_FFI_TYPE(dst_type->pointer.type);
325 				src_type = ZEND_FFI_TYPE(src_type->pointer.type);
326 				if (dst_type->kind == ZEND_FFI_TYPE_VOID ||
327 				    src_type->kind == ZEND_FFI_TYPE_VOID) {
328 				    return 1;
329 				} else if (dst_type->kind == ZEND_FFI_TYPE_FUNC &&
330 				           src_type->kind == ZEND_FFI_TYPE_FUNC) {
331 				    return zend_ffi_func_ptr_are_compatible(dst_type, src_type);
332 				}
333 			} else if (dst_type->kind == ZEND_FFI_TYPE_ARRAY &&
334 			           (dst_type->array.length == src_type->array.length ||
335 			            dst_type->array.length == 0)) {
336 				dst_type = ZEND_FFI_TYPE(dst_type->array.type);
337 				src_type = ZEND_FFI_TYPE(src_type->array.type);
338 			} else {
339 				break;
340 			}
341 		} else if (dst_type->kind == ZEND_FFI_TYPE_POINTER &&
342 		           src_type->kind == ZEND_FFI_TYPE_ARRAY) {
343 			dst_type = ZEND_FFI_TYPE(dst_type->pointer.type);
344 			src_type = ZEND_FFI_TYPE(src_type->array.type);
345 			if (dst_type->kind == ZEND_FFI_TYPE_VOID) {
346 			    return 1;
347 			}
348 		} else {
349 			break;
350 		}
351 	}
352 	return 0;
353 }
354 /* }}} */
355 
zend_ffi_face_struct_add_fields(ffi_type * t,zend_ffi_type * type,int * i,size_t size)356 static ffi_type* zend_ffi_face_struct_add_fields(ffi_type* t, zend_ffi_type *type, int *i, size_t size)
357 {
358 	zend_ffi_field *field;
359 
360 	ZEND_HASH_MAP_FOREACH_PTR(&type->record.fields, field) {
361 		switch (ZEND_FFI_TYPE(field->type)->kind) {
362 			case ZEND_FFI_TYPE_FLOAT:
363 				t->elements[(*i)++] = &ffi_type_float;
364 				break;
365 			case ZEND_FFI_TYPE_DOUBLE:
366 				t->elements[(*i)++] = &ffi_type_double;
367 				break;
368 #ifdef HAVE_LONG_DOUBLE
369 			case ZEND_FFI_TYPE_LONGDOUBLE:
370 				t->elements[(*i)++] = &ffi_type_longdouble;
371 				break;
372 #endif
373 			case ZEND_FFI_TYPE_SINT8:
374 			case ZEND_FFI_TYPE_UINT8:
375 			case ZEND_FFI_TYPE_BOOL:
376 			case ZEND_FFI_TYPE_CHAR:
377 				t->elements[(*i)++] = &ffi_type_uint8;
378 				break;
379 			case ZEND_FFI_TYPE_SINT16:
380 			case ZEND_FFI_TYPE_UINT16:
381 				t->elements[(*i)++] = &ffi_type_uint16;
382 				break;
383 			case ZEND_FFI_TYPE_SINT32:
384 			case ZEND_FFI_TYPE_UINT32:
385 				t->elements[(*i)++] = &ffi_type_uint32;
386 				break;
387 			case ZEND_FFI_TYPE_SINT64:
388 			case ZEND_FFI_TYPE_UINT64:
389 				t->elements[(*i)++] = &ffi_type_uint64;
390 				break;
391 			case ZEND_FFI_TYPE_POINTER:
392 				t->elements[(*i)++] = &ffi_type_pointer;
393 				break;
394 			case ZEND_FFI_TYPE_STRUCT: {
395 				zend_ffi_type *field_type = ZEND_FFI_TYPE(field->type);
396 				/* for unions we use only the first field */
397 				uint32_t num_fields = !(field_type->attr & ZEND_FFI_ATTR_UNION) ?
398 					zend_hash_num_elements(&field_type->record.fields) : 1;
399 
400 				if (num_fields > 1) {
401 					size += sizeof(ffi_type*) * (num_fields - 1);
402 					t = erealloc(t, size);
403 					t->elements = (ffi_type**)(t + 1);
404 				}
405 				t = zend_ffi_face_struct_add_fields(t, field_type, i, size);
406 				break;
407 			}
408 			default:
409 				t->elements[(*i)++] = &ffi_type_void;
410 				break;
411 		}
412 		if (type->attr & ZEND_FFI_ATTR_UNION) {
413 			/* for unions we use only the first field */
414 			break;
415 		}
416 	} ZEND_HASH_FOREACH_END();
417 	return t;
418 }
419 
zend_ffi_make_fake_struct_type(zend_ffi_type * type)420 static ffi_type *zend_ffi_make_fake_struct_type(zend_ffi_type *type) /* {{{ */
421 {
422 	/* for unions we use only the first field */
423 	uint32_t num_fields = !(type->attr & ZEND_FFI_ATTR_UNION) ?
424 		zend_hash_num_elements(&type->record.fields) : 1;
425 	size_t size = sizeof(ffi_type) + sizeof(ffi_type*) * (num_fields + 1);
426 	ffi_type *t = emalloc(size);
427 	int i;
428 
429 	t->size = type->size;
430 	t->alignment = type->align;
431 	t->type = FFI_TYPE_STRUCT;
432 	t->elements = (ffi_type**)(t + 1);
433 	i = 0;
434 	t = zend_ffi_face_struct_add_fields(t, type, &i, size);
435 	t->elements[i] = NULL;
436 	return t;
437 }
438 /* }}} */
439 
zend_ffi_get_type(zend_ffi_type * type)440 static ffi_type *zend_ffi_get_type(zend_ffi_type *type) /* {{{ */
441 {
442 	zend_ffi_type_kind kind = type->kind;
443 
444 again:
445 	switch (kind) {
446 		case ZEND_FFI_TYPE_FLOAT:
447 			return &ffi_type_float;
448 		case ZEND_FFI_TYPE_DOUBLE:
449 			return &ffi_type_double;
450 #ifdef HAVE_LONG_DOUBLE
451 		case ZEND_FFI_TYPE_LONGDOUBLE:
452 			return &ffi_type_longdouble;
453 #endif
454 		case ZEND_FFI_TYPE_UINT8:
455 			return &ffi_type_uint8;
456 		case ZEND_FFI_TYPE_SINT8:
457 			return &ffi_type_sint8;
458 		case ZEND_FFI_TYPE_UINT16:
459 			return &ffi_type_uint16;
460 		case ZEND_FFI_TYPE_SINT16:
461 			return &ffi_type_sint16;
462 		case ZEND_FFI_TYPE_UINT32:
463 			return &ffi_type_uint32;
464 		case ZEND_FFI_TYPE_SINT32:
465 			return &ffi_type_sint32;
466 		case ZEND_FFI_TYPE_UINT64:
467 			return &ffi_type_uint64;
468 		case ZEND_FFI_TYPE_SINT64:
469 			return &ffi_type_sint64;
470 		case ZEND_FFI_TYPE_POINTER:
471 			return &ffi_type_pointer;
472 		case ZEND_FFI_TYPE_VOID:
473 			return &ffi_type_void;
474 		case ZEND_FFI_TYPE_BOOL:
475 			return &ffi_type_uint8;
476 		case ZEND_FFI_TYPE_CHAR:
477 			return &ffi_type_sint8;
478 		case ZEND_FFI_TYPE_ENUM:
479 			kind = type->enumeration.kind;
480 			goto again;
481 		case ZEND_FFI_TYPE_STRUCT:
482 			return zend_ffi_make_fake_struct_type(type);
483 		default:
484 			break;
485 	}
486 	return NULL;
487 }
488 /* }}} */
489 
zend_ffi_cdata_to_zval_slow(void * ptr,zend_ffi_type * type,zend_ffi_flags flags)490 static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */
491 {
492 	zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
493 
494 	zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
495 	cdata->std.handlers =
496 		(type->kind < ZEND_FFI_TYPE_POINTER) ?
497 		&zend_ffi_cdata_value_handlers :
498 		&zend_ffi_cdata_handlers;
499 	cdata->type = type;
500 	cdata->flags = flags;
501 	cdata->ptr = ptr;
502 	return cdata;
503 }
504 /* }}} */
505 
zend_ffi_cdata_to_zval_slow_ptr(void * ptr,zend_ffi_type * type,zend_ffi_flags flags)506 static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow_ptr(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */
507 {
508 	zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
509 
510 	zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
511 	cdata->type = type;
512 	cdata->flags = flags;
513 	cdata->ptr = (void*)&cdata->ptr_holder;
514 	*(void**)cdata->ptr = *(void**)ptr;
515 	return cdata;
516 }
517 /* }}} */
518 
zend_ffi_cdata_to_zval_slow_ret(void * ptr,zend_ffi_type * type,zend_ffi_flags flags)519 static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow_ret(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */
520 {
521 	zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
522 
523 	zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
524 	cdata->std.handlers =
525 		(type->kind < ZEND_FFI_TYPE_POINTER) ?
526 		&zend_ffi_cdata_value_handlers :
527 		&zend_ffi_cdata_handlers;
528 	cdata->type = type;
529 	cdata->flags = flags;
530 	if (type->kind == ZEND_FFI_TYPE_POINTER) {
531 		cdata->ptr = (void*)&cdata->ptr_holder;
532 		*(void**)cdata->ptr = *(void**)ptr;
533 	} else if (type->kind == ZEND_FFI_TYPE_STRUCT) {
534 		cdata->ptr = emalloc(type->size);
535 		cdata->flags |= ZEND_FFI_FLAG_OWNED;
536 		memcpy(cdata->ptr, ptr, type->size);
537 	} else {
538 		cdata->ptr = ptr;
539 	}
540 	return cdata;
541 }
542 /* }}} */
543 
zend_ffi_cdata_to_zval(zend_ffi_cdata * cdata,void * ptr,zend_ffi_type * type,int read_type,zval * rv,zend_ffi_flags flags,bool is_ret,bool debug_union)544 static zend_always_inline void zend_ffi_cdata_to_zval(zend_ffi_cdata *cdata, void *ptr, zend_ffi_type *type, int read_type, zval *rv, zend_ffi_flags flags, bool is_ret, bool debug_union) /* {{{ */
545 {
546 	if (read_type == BP_VAR_R) {
547 		zend_ffi_type_kind kind = type->kind;
548 
549 again:
550 	    switch (kind) {
551 			case ZEND_FFI_TYPE_FLOAT:
552 				ZVAL_DOUBLE(rv, *(float*)ptr);
553 				return;
554 			case ZEND_FFI_TYPE_DOUBLE:
555 				ZVAL_DOUBLE(rv, *(double*)ptr);
556 				return;
557 #ifdef HAVE_LONG_DOUBLE
558 			case ZEND_FFI_TYPE_LONGDOUBLE:
559 				ZVAL_DOUBLE(rv, *(long double*)ptr);
560 				return;
561 #endif
562 			case ZEND_FFI_TYPE_UINT8:
563 				ZVAL_LONG(rv, *(uint8_t*)ptr);
564 				return;
565 			case ZEND_FFI_TYPE_SINT8:
566 				ZVAL_LONG(rv, *(int8_t*)ptr);
567 				return;
568 			case ZEND_FFI_TYPE_UINT16:
569 				ZVAL_LONG(rv, *(uint16_t*)ptr);
570 				return;
571 			case ZEND_FFI_TYPE_SINT16:
572 				ZVAL_LONG(rv, *(int16_t*)ptr);
573 				return;
574 			case ZEND_FFI_TYPE_UINT32:
575 				ZVAL_LONG(rv, *(uint32_t*)ptr);
576 				return;
577 			case ZEND_FFI_TYPE_SINT32:
578 				ZVAL_LONG(rv, *(int32_t*)ptr);
579 				return;
580 			case ZEND_FFI_TYPE_UINT64:
581 				ZVAL_LONG(rv, *(uint64_t*)ptr);
582 				return;
583 			case ZEND_FFI_TYPE_SINT64:
584 				ZVAL_LONG(rv, *(int64_t*)ptr);
585 				return;
586 			case ZEND_FFI_TYPE_BOOL:
587 				ZVAL_BOOL(rv, *(uint8_t*)ptr);
588 				return;
589 			case ZEND_FFI_TYPE_CHAR:
590 				ZVAL_CHAR(rv, *(char*)ptr);
591 				return;
592 			case ZEND_FFI_TYPE_ENUM:
593 				kind = type->enumeration.kind;
594 				goto again;
595 			case ZEND_FFI_TYPE_POINTER:
596 				if (*(void**)ptr == NULL) {
597 					ZVAL_NULL(rv);
598 					return;
599 				} else if (debug_union) {
600 					ZVAL_STR(rv, zend_strpprintf(0, "%p", *(void**)ptr));
601 					return;
602 				} else if ((type->attr & ZEND_FFI_ATTR_CONST) && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) {
603 					ZVAL_STRING(rv, *(char**)ptr);
604 					return;
605 				}
606 				if (!cdata) {
607 					if (is_ret) {
608 						cdata = zend_ffi_cdata_to_zval_slow_ret(ptr, type, flags);
609 					} else {
610 						cdata = zend_ffi_cdata_to_zval_slow_ptr(ptr, type, flags);
611 					}
612 				} else {
613 					GC_ADDREF(&cdata->std);
614 				}
615 				ZVAL_OBJ(rv, &cdata->std);
616 				return;
617 			default:
618 				break;
619 		}
620 	}
621 
622 	if (!cdata) {
623 		if (is_ret) {
624 			cdata = zend_ffi_cdata_to_zval_slow_ret(ptr, type, flags);
625 		} else {
626 			cdata = zend_ffi_cdata_to_zval_slow(ptr, type, flags);
627 		}
628 	} else {
629 		GC_ADDREF(&cdata->std);
630 	}
631 	ZVAL_OBJ(rv, &cdata->std);
632 }
633 /* }}} */
634 
zend_ffi_bit_field_read(void * ptr,zend_ffi_field * field)635 static uint64_t zend_ffi_bit_field_read(void *ptr, zend_ffi_field *field) /* {{{ */
636 {
637 	size_t bit = field->first_bit;
638 	size_t last_bit = bit + field->bits - 1;
639 	uint8_t *p = (uint8_t *) ptr + bit / 8;
640 	uint8_t *last_p = (uint8_t *) ptr + last_bit / 8;
641 	size_t pos = bit % 8;
642 	size_t insert_pos = 0;
643 	uint8_t mask;
644 	uint64_t val = 0;
645 
646 	/* Bitfield fits into a single byte */
647 	if (p == last_p) {
648 		mask = (1U << field->bits) - 1U;
649 		return (*p >> pos) & mask;
650 	}
651 
652 	/* Read partial prefix byte */
653 	if (pos != 0) {
654 		size_t num_bits = 8 - pos;
655 		mask = (1U << num_bits) - 1U;
656 		val = (*p++ >> pos) & mask;
657 		insert_pos += num_bits;
658 	}
659 
660 	/* Read full bytes */
661 	while (p < last_p) {
662 		val |= (uint64_t) *p++ << insert_pos;
663 		insert_pos += 8;
664 	}
665 
666 	/* Read partial suffix byte */
667 	if (p == last_p) {
668 		size_t num_bits = last_bit % 8 + 1;
669 		mask = (1U << num_bits) - 1U;
670 		val |= (uint64_t) (*p & mask) << insert_pos;
671 	}
672 
673 	return val;
674 }
675 /* }}} */
676 
zend_ffi_bit_field_to_zval(void * ptr,zend_ffi_field * field,zval * rv)677 static void zend_ffi_bit_field_to_zval(void *ptr, zend_ffi_field *field, zval *rv) /* {{{ */
678 {
679 	uint64_t val = zend_ffi_bit_field_read(ptr, field);
680 	if (ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_CHAR
681 	 || ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT8
682 	 || ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT16
683 	 || ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT32
684 	 || ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT64) {
685 		/* Sign extend */
686 		uint64_t shift = 64 - (field->bits % 64);
687 		if (shift != 0) {
688 			val = (int64_t)(val << shift) >> shift;
689 		}
690 	}
691 	ZVAL_LONG(rv, val);
692 }
693 /* }}} */
694 
zend_ffi_zval_to_bit_field(void * ptr,zend_ffi_field * field,zval * value)695 static void zend_ffi_zval_to_bit_field(void *ptr, zend_ffi_field *field, zval *value) /* {{{ */
696 {
697 	uint64_t val = zval_get_long(value);
698 	size_t bit = field->first_bit;
699 	size_t last_bit = bit + field->bits - 1;
700 	uint8_t *p = (uint8_t *) ptr + bit / 8;
701 	uint8_t *last_p = (uint8_t *) ptr + last_bit / 8;
702 	size_t pos = bit % 8;
703 	uint8_t mask;
704 
705 	/* Bitfield fits into a single byte */
706 	if (p == last_p) {
707 		mask = ((1U << field->bits) - 1U) << pos;
708 		*p = (*p & ~mask) | ((val << pos) & mask);
709 		return;
710 	}
711 
712 	/* Write partial prefix byte */
713 	if (pos != 0) {
714 		size_t num_bits = 8 - pos;
715 		mask = ((1U << num_bits) - 1U) << pos;
716 		*p = (*p & ~mask) | ((val << pos) & mask);
717 		p++;
718 		val >>= num_bits;
719 	}
720 
721 	/* Write full bytes */
722 	while (p < last_p) {
723 		*p++ = val;
724 		val >>= 8;
725 	}
726 
727 	/* Write partial suffix byte */
728 	if (p == last_p) {
729 		size_t num_bits = last_bit % 8 + 1;
730 		mask = (1U << num_bits) - 1U;
731 		*p = (*p & ~mask) | (val & mask);
732 	}
733 }
734 /* }}} */
735 
zend_ffi_zval_to_cdata(void * ptr,zend_ffi_type * type,zval * value)736 static zend_always_inline zend_result zend_ffi_zval_to_cdata(void *ptr, zend_ffi_type *type, zval *value) /* {{{ */
737 {
738 	zend_long lval;
739 	double dval;
740 	zend_string *tmp_str;
741 	zend_string *str;
742 	zend_ffi_type_kind kind = type->kind;
743 
744 	/* Pointer type has special handling of CData */
745 	if (kind != ZEND_FFI_TYPE_POINTER && Z_TYPE_P(value) == IS_OBJECT && Z_OBJCE_P(value) == zend_ffi_cdata_ce) {
746 		zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(value);
747 		if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type)) &&
748 			type->size == ZEND_FFI_TYPE(cdata->type)->size) {
749 			memcpy(ptr, cdata->ptr, type->size);
750 			return SUCCESS;
751 		}
752 	}
753 
754 again:
755 	switch (kind) {
756 		case ZEND_FFI_TYPE_FLOAT:
757 			dval = zval_get_double(value);
758 			*(float*)ptr = dval;
759 			break;
760 		case ZEND_FFI_TYPE_DOUBLE:
761 			dval = zval_get_double(value);
762 			*(double*)ptr = dval;
763 			break;
764 #ifdef HAVE_LONG_DOUBLE
765 		case ZEND_FFI_TYPE_LONGDOUBLE:
766 			dval = zval_get_double(value);
767 			*(long double*)ptr = dval;
768 			break;
769 #endif
770 		case ZEND_FFI_TYPE_UINT8:
771 			lval = zval_get_long(value);
772 			*(uint8_t*)ptr = lval;
773 			break;
774 		case ZEND_FFI_TYPE_SINT8:
775 			lval = zval_get_long(value);
776 			*(int8_t*)ptr = lval;
777 			break;
778 		case ZEND_FFI_TYPE_UINT16:
779 			lval = zval_get_long(value);
780 			*(uint16_t*)ptr = lval;
781 			break;
782 		case ZEND_FFI_TYPE_SINT16:
783 			lval = zval_get_long(value);
784 			*(int16_t*)ptr = lval;
785 			break;
786 		case ZEND_FFI_TYPE_UINT32:
787 			lval = zval_get_long(value);
788 			*(uint32_t*)ptr = lval;
789 			break;
790 		case ZEND_FFI_TYPE_SINT32:
791 			lval = zval_get_long(value);
792 			*(int32_t*)ptr = lval;
793 			break;
794 		case ZEND_FFI_TYPE_UINT64:
795 			lval = zval_get_long(value);
796 			*(uint64_t*)ptr = lval;
797 			break;
798 		case ZEND_FFI_TYPE_SINT64:
799 			lval = zval_get_long(value);
800 			*(int64_t*)ptr = lval;
801 			break;
802 		case ZEND_FFI_TYPE_BOOL:
803 			*(uint8_t*)ptr = zend_is_true(value);
804 			break;
805 		case ZEND_FFI_TYPE_CHAR:
806 			str = zval_get_tmp_string(value, &tmp_str);
807 			if (ZSTR_LEN(str) == 1) {
808 				*(char*)ptr = ZSTR_VAL(str)[0];
809 			} else {
810 				zend_ffi_assign_incompatible(value, type);
811 				return FAILURE;
812 			}
813 			zend_tmp_string_release(tmp_str);
814 			break;
815 		case ZEND_FFI_TYPE_ENUM:
816 			kind = type->enumeration.kind;
817 			goto again;
818 		case ZEND_FFI_TYPE_POINTER:
819 			if (Z_TYPE_P(value) == IS_NULL) {
820 				*(void**)ptr = NULL;
821 				break;
822 			} else if (Z_TYPE_P(value) == IS_OBJECT && Z_OBJCE_P(value) == zend_ffi_cdata_ce) {
823 				zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(value);
824 
825 				if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) {
826 					if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
827 						*(void**)ptr = *(void**)cdata->ptr;
828 					} else {
829 						if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
830 							zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign of owned C pointer");
831 							return FAILURE;
832 						}
833 						*(void**)ptr = cdata->ptr;
834 					}
835 					return SUCCESS;
836 				/* Allow transparent assignment of not-owned CData to compatible pointers */
837 				} else if (ZEND_FFI_TYPE(cdata->type)->kind != ZEND_FFI_TYPE_POINTER
838 				 && zend_ffi_is_compatible_type(ZEND_FFI_TYPE(type->pointer.type), ZEND_FFI_TYPE(cdata->type))) {
839 					if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
840 						zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign pointer to owned C data");
841 						return FAILURE;
842 					}
843 					*(void**)ptr = cdata->ptr;
844 					return SUCCESS;
845 				}
846 #if FFI_CLOSURES
847 			} else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_FUNC) {
848 				void *callback = zend_ffi_create_callback(ZEND_FFI_TYPE(type->pointer.type), value);
849 
850 				if (callback) {
851 					*(void**)ptr = callback;
852 					break;
853 				} else {
854 					return FAILURE;
855 				}
856 #endif
857 			}
858 			zend_ffi_assign_incompatible(value, type);
859 			return FAILURE;
860 		case ZEND_FFI_TYPE_STRUCT:
861 		case ZEND_FFI_TYPE_ARRAY:
862 		default:
863 			/* Incompatible types, because otherwise the CData check at the entry point would've succeeded. */
864 			zend_ffi_assign_incompatible(value, type);
865 			return FAILURE;
866 	}
867 	return SUCCESS;
868 }
869 /* }}} */
870 
871 #if defined(ZEND_WIN32) && (defined(HAVE_FFI_FASTCALL) || defined(HAVE_FFI_STDCALL) || defined(HAVE_FFI_VECTORCALL_PARTIAL))
zend_ffi_arg_size(zend_ffi_type * type)872 static size_t zend_ffi_arg_size(zend_ffi_type *type) /* {{{ */
873 {
874 	zend_ffi_type *arg_type;
875 	size_t arg_size = 0;
876 
877 	ZEND_HASH_PACKED_FOREACH_PTR(type->func.args, arg_type) {
878 		arg_size += MAX(ZEND_FFI_TYPE(arg_type)->size, sizeof(size_t));
879 	} ZEND_HASH_FOREACH_END();
880 	return arg_size;
881 }
882 /* }}} */
883 #endif
884 
zend_ffi_mangled_func_name(zend_string * name,zend_ffi_type * type)885 static zend_always_inline zend_string *zend_ffi_mangled_func_name(zend_string *name, zend_ffi_type *type) /* {{{ */
886 {
887 #ifdef ZEND_WIN32
888 	switch (type->func.abi) {
889 # ifdef HAVE_FFI_FASTCALL
890 		case FFI_FASTCALL:
891 			return strpprintf(0, "@%s@%zu", ZSTR_VAL(name), zend_ffi_arg_size(type));
892 # endif
893 # ifdef HAVE_FFI_STDCALL
894 		case FFI_STDCALL:
895 			return strpprintf(0, "_%s@%zu", ZSTR_VAL(name), zend_ffi_arg_size(type));
896 # endif
897 # ifdef HAVE_FFI_VECTORCALL_PARTIAL
898 		case FFI_VECTORCALL_PARTIAL:
899 			return strpprintf(0, "%s@@%zu", ZSTR_VAL(name), zend_ffi_arg_size(type));
900 # endif
901 	}
902 #endif
903 	return zend_string_copy(name);
904 }
905 /* }}} */
906 
907 #if FFI_CLOSURES
908 typedef struct _zend_ffi_callback_data {
909 	zend_fcall_info_cache  fcc;
910 	zend_ffi_type         *type;
911 	void                  *code;
912 	void                  *callback;
913 	ffi_cif                cif;
914 	uint32_t               arg_count;
915 	ffi_type              *ret_type;
916 	ffi_type              *arg_types[0] ZEND_ELEMENT_COUNT(arg_count);
917 } zend_ffi_callback_data;
918 
zend_ffi_callback_hash_dtor(zval * zv)919 static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */
920 {
921 	zend_ffi_callback_data *callback_data = Z_PTR_P(zv);
922 
923 	ffi_closure_free(callback_data->callback);
924 	if (callback_data->fcc.function_handler->common.fn_flags & ZEND_ACC_CLOSURE) {
925 		OBJ_RELEASE(ZEND_CLOSURE_OBJECT(callback_data->fcc.function_handler));
926 	}
927 	for (int i = 0; i < callback_data->arg_count; ++i) {
928 		if (callback_data->arg_types[i]->type == FFI_TYPE_STRUCT) {
929 			efree(callback_data->arg_types[i]);
930 		}
931 	}
932 	if (callback_data->ret_type->type == FFI_TYPE_STRUCT) {
933 		efree(callback_data->ret_type);
934 	}
935 	efree(callback_data);
936 }
937 /* }}} */
938 
zend_ffi_callback_trampoline(ffi_cif * cif,void * ret,void ** args,void * data)939 static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */
940 {
941 	zend_ffi_callback_data *callback_data = (zend_ffi_callback_data*)data;
942 	zend_fcall_info fci;
943 	zend_ffi_type *ret_type;
944 	zval retval;
945 	ALLOCA_FLAG(use_heap)
946 
947 	fci.size = sizeof(zend_fcall_info);
948 	ZVAL_UNDEF(&fci.function_name);
949 	fci.retval = &retval;
950 	fci.params = do_alloca(sizeof(zval) *callback_data->arg_count, use_heap);
951 	fci.object = NULL;
952 	fci.param_count = callback_data->arg_count;
953 	fci.named_params = NULL;
954 
955 	if (callback_data->type->func.args) {
956 		int n = 0;
957 		zend_ffi_type *arg_type;
958 
959 		ZEND_HASH_PACKED_FOREACH_PTR(callback_data->type->func.args, arg_type) {
960 			arg_type = ZEND_FFI_TYPE(arg_type);
961 			zend_ffi_cdata_to_zval(NULL, args[n], arg_type, BP_VAR_R, &fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), 0, 0);
962 			n++;
963 		} ZEND_HASH_FOREACH_END();
964 	}
965 
966 	ZVAL_UNDEF(&retval);
967 	if (zend_call_function(&fci, &callback_data->fcc) != SUCCESS) {
968 		zend_throw_error(zend_ffi_exception_ce, "Cannot call callback");
969 	}
970 
971 	if (callback_data->arg_count) {
972 		int n = 0;
973 
974 		for (n = 0; n < callback_data->arg_count; n++) {
975 			zval_ptr_dtor(&fci.params[n]);
976 		}
977 	}
978 	free_alloca(fci.params, use_heap);
979 
980 	if (EG(exception)) {
981 		zend_error_noreturn(E_ERROR, "Throwing from FFI callbacks is not allowed");
982 	}
983 
984 	ret_type = ZEND_FFI_TYPE(callback_data->type->func.ret_type);
985 	if (ret_type->kind != ZEND_FFI_TYPE_VOID) {
986 		zend_ffi_zval_to_cdata(ret, ret_type, &retval);
987 	}
988 
989 	zval_ptr_dtor(&retval);
990 }
991 /* }}} */
992 
zend_ffi_create_callback(zend_ffi_type * type,zval * value)993 static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ */
994 {
995 	zend_fcall_info_cache fcc;
996 	char *error = NULL;
997 	uint32_t arg_count;
998 	void *code;
999 	void *callback;
1000 	zend_ffi_callback_data *callback_data;
1001 
1002 	if (type->attr & ZEND_FFI_ATTR_VARIADIC) {
1003 		zend_throw_error(zend_ffi_exception_ce, "Variadic function closures are not supported");
1004 		return NULL;
1005 	}
1006 
1007 	if (!zend_is_callable_ex(value, NULL, 0, NULL, &fcc, &error)) {
1008 		zend_throw_error(zend_ffi_exception_ce, "Attempt to assign an invalid callback, %s", error);
1009 		return NULL;
1010 	}
1011 
1012 	arg_count = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
1013 	if (arg_count < fcc.function_handler->common.required_num_args) {
1014 		zend_throw_error(zend_ffi_exception_ce, "Attempt to assign an invalid callback, insufficient number of arguments");
1015 		return NULL;
1016 	}
1017 
1018 	callback = ffi_closure_alloc(sizeof(ffi_closure), &code);
1019 	if (!callback) {
1020 		zend_throw_error(zend_ffi_exception_ce, "Cannot allocate callback");
1021 		return NULL;
1022 	}
1023 
1024 	callback_data = emalloc(sizeof(zend_ffi_callback_data) + sizeof(ffi_type*) * arg_count);
1025 	memcpy(&callback_data->fcc, &fcc, sizeof(zend_fcall_info_cache));
1026 	callback_data->type = type;
1027 	callback_data->callback = callback;
1028 	callback_data->code = code;
1029 	callback_data->arg_count = arg_count;
1030 
1031 	if (type->func.args) {
1032 		int n = 0;
1033 		zend_ffi_type *arg_type;
1034 
1035 		ZEND_HASH_PACKED_FOREACH_PTR(type->func.args, arg_type) {
1036 			arg_type = ZEND_FFI_TYPE(arg_type);
1037 			callback_data->arg_types[n] = zend_ffi_get_type(arg_type);
1038 			if (!callback_data->arg_types[n]) {
1039 				zend_ffi_pass_unsupported(arg_type);
1040 				for (int i = 0; i < n; ++i) {
1041 					if (callback_data->arg_types[i]->type == FFI_TYPE_STRUCT) {
1042 						efree(callback_data->arg_types[i]);
1043 					}
1044 				}
1045 				efree(callback_data);
1046 				ffi_closure_free(callback);
1047 				return NULL;
1048 			}
1049 			n++;
1050 		} ZEND_HASH_FOREACH_END();
1051 	}
1052 	callback_data->ret_type = zend_ffi_get_type(ZEND_FFI_TYPE(type->func.ret_type));
1053 	if (!callback_data->ret_type) {
1054 		zend_ffi_return_unsupported(type->func.ret_type);
1055 		for (int i = 0; i < callback_data->arg_count; ++i) {
1056 			if (callback_data->arg_types[i]->type == FFI_TYPE_STRUCT) {
1057 				efree(callback_data->arg_types[i]);
1058 			}
1059 		}
1060 		efree(callback_data);
1061 		ffi_closure_free(callback);
1062 		return NULL;
1063 	}
1064 
1065 	if (ffi_prep_cif(&callback_data->cif, type->func.abi, callback_data->arg_count, callback_data->ret_type, callback_data->arg_types) != FFI_OK) {
1066 		zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF");
1067 		goto free_on_failure;
1068 	}
1069 
1070 	if (ffi_prep_closure_loc(callback, &callback_data->cif, zend_ffi_callback_trampoline, callback_data, code) != FFI_OK) {
1071 		zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback");
1072 free_on_failure: ;
1073 		for (int i = 0; i < callback_data->arg_count; ++i) {
1074 			if (callback_data->arg_types[i]->type == FFI_TYPE_STRUCT) {
1075 				efree(callback_data->arg_types[i]);
1076 			}
1077 		}
1078 		if (callback_data->ret_type->type == FFI_TYPE_STRUCT) {
1079 			efree(callback_data->ret_type);
1080 		}
1081 		efree(callback_data);
1082 		ffi_closure_free(callback);
1083 		return NULL;
1084 	}
1085 
1086 	if (!FFI_G(callbacks)) {
1087 		FFI_G(callbacks) = emalloc(sizeof(HashTable));
1088 		zend_hash_init(FFI_G(callbacks), 0, NULL, zend_ffi_callback_hash_dtor, 0);
1089 	}
1090 	zend_hash_next_index_insert_ptr(FFI_G(callbacks), callback_data);
1091 
1092 	if (fcc.function_handler->common.fn_flags & ZEND_ACC_CLOSURE) {
1093 		GC_ADDREF(ZEND_CLOSURE_OBJECT(fcc.function_handler));
1094 	}
1095 
1096 	return code;
1097 }
1098 /* }}} */
1099 #endif
1100 
zend_ffi_cdata_get(zend_object * obj,zend_string * member,int read_type,void ** cache_slot,zval * rv)1101 static zval *zend_ffi_cdata_get(zend_object *obj, zend_string *member, int read_type, void **cache_slot, zval *rv) /* {{{ */
1102 {
1103 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)obj;
1104 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
1105 
1106 #if 0
1107 	if (UNEXPECTED(!cdata->ptr)) {
1108 		zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1109 		return &EG(uninitialized_zval);
1110 	}
1111 #endif
1112 
1113 	if (UNEXPECTED(!zend_string_equals_literal(member, "cdata"))) {
1114 		zend_throw_error(zend_ffi_exception_ce, "Only 'cdata' property may be read");
1115 		return &EG(uninitialized_zval);
1116 	}
1117 
1118 	zend_ffi_cdata_to_zval(cdata, cdata->ptr, type, BP_VAR_R, rv, 0, 0, 0);
1119 	return rv;
1120 }
1121 /* }}} */
1122 
zend_ffi_cdata_set(zend_object * obj,zend_string * member,zval * value,void ** cache_slot)1123 static zval *zend_ffi_cdata_set(zend_object *obj, zend_string *member, zval *value, void **cache_slot) /* {{{ */
1124 {
1125 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)obj;
1126 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
1127 
1128 #if 0
1129 	if (UNEXPECTED(!cdata->ptr)) {
1130 		zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1131 		return &EG(uninitialized_zval);
1132 	}
1133 #endif
1134 
1135 	if (UNEXPECTED(!zend_string_equals_literal(member, "cdata"))) {
1136 		zend_throw_error(zend_ffi_exception_ce, "Only 'cdata' property may be set");
1137 		return &EG(uninitialized_zval);
1138 	}
1139 
1140 	zend_ffi_zval_to_cdata(cdata->ptr, type, value);
1141 
1142 	return value;
1143 }
1144 /* }}} */
1145 
zend_ffi_cdata_cast_object(zend_object * readobj,zval * writeobj,int type)1146 static zend_result zend_ffi_cdata_cast_object(zend_object *readobj, zval *writeobj, int type) /* {{{ */
1147 {
1148 	if (type == IS_STRING) {
1149 		zend_ffi_cdata *cdata = (zend_ffi_cdata*)readobj;
1150 		zend_ffi_type  *ctype = ZEND_FFI_TYPE(cdata->type);
1151 		void           *ptr = cdata->ptr;
1152 		zend_ffi_type_kind kind = ctype->kind;
1153 
1154 again:
1155 	    switch (kind) {
1156 			case ZEND_FFI_TYPE_FLOAT:
1157 				ZVAL_DOUBLE(writeobj, *(float*)ptr);
1158 				break;
1159 			case ZEND_FFI_TYPE_DOUBLE:
1160 				ZVAL_DOUBLE(writeobj, *(double*)ptr);
1161 				break;
1162 #ifdef HAVE_LONG_DOUBLE
1163 			case ZEND_FFI_TYPE_LONGDOUBLE:
1164 				ZVAL_DOUBLE(writeobj, *(long double*)ptr);
1165 				break;
1166 #endif
1167 			case ZEND_FFI_TYPE_UINT8:
1168 				ZVAL_LONG(writeobj, *(uint8_t*)ptr);
1169 				break;
1170 			case ZEND_FFI_TYPE_SINT8:
1171 				ZVAL_LONG(writeobj, *(int8_t*)ptr);
1172 				break;
1173 			case ZEND_FFI_TYPE_UINT16:
1174 				ZVAL_LONG(writeobj, *(uint16_t*)ptr);
1175 				break;
1176 			case ZEND_FFI_TYPE_SINT16:
1177 				ZVAL_LONG(writeobj, *(int16_t*)ptr);
1178 				break;
1179 			case ZEND_FFI_TYPE_UINT32:
1180 				ZVAL_LONG(writeobj, *(uint32_t*)ptr);
1181 				break;
1182 			case ZEND_FFI_TYPE_SINT32:
1183 				ZVAL_LONG(writeobj, *(int32_t*)ptr);
1184 				break;
1185 			case ZEND_FFI_TYPE_UINT64:
1186 				ZVAL_LONG(writeobj, *(uint64_t*)ptr);
1187 				break;
1188 			case ZEND_FFI_TYPE_SINT64:
1189 				ZVAL_LONG(writeobj, *(int64_t*)ptr);
1190 				break;
1191 			case ZEND_FFI_TYPE_BOOL:
1192 				ZVAL_BOOL(writeobj, *(uint8_t*)ptr);
1193 				break;
1194 			case ZEND_FFI_TYPE_CHAR:
1195 				ZVAL_CHAR(writeobj, *(char*)ptr);
1196 				return SUCCESS;
1197 			case ZEND_FFI_TYPE_ENUM:
1198 				kind = ctype->enumeration.kind;
1199 				goto again;
1200 			case ZEND_FFI_TYPE_POINTER:
1201 				if (*(void**)ptr == NULL) {
1202 					ZVAL_NULL(writeobj);
1203 					break;
1204 				} else if ((ctype->attr & ZEND_FFI_ATTR_CONST) && ZEND_FFI_TYPE(ctype->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) {
1205 					ZVAL_STRING(writeobj, *(char**)ptr);
1206 					return SUCCESS;
1207 				}
1208 				return FAILURE;
1209 			default:
1210 				return FAILURE;
1211 		}
1212 		convert_to_string(writeobj);
1213 		return SUCCESS;
1214 	} else if (type == _IS_BOOL) {
1215 		ZVAL_TRUE(writeobj);
1216 		return SUCCESS;
1217 	}
1218 
1219 	return FAILURE;
1220 }
1221 /* }}} */
1222 
zend_ffi_cdata_read_field(zend_object * obj,zend_string * field_name,int read_type,void ** cache_slot,zval * rv)1223 static zval *zend_ffi_cdata_read_field(zend_object *obj, zend_string *field_name, int read_type, void **cache_slot, zval *rv) /* {{{ */
1224 {
1225 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)obj;
1226 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
1227 	void           *ptr = cdata->ptr;
1228 	zend_ffi_field *field;
1229 
1230 	if (cache_slot && *cache_slot == type) {
1231 		field = *(cache_slot + 1);
1232 	} else {
1233 		if (type->kind == ZEND_FFI_TYPE_POINTER) {
1234 			type = ZEND_FFI_TYPE(type->pointer.type);
1235 		}
1236 		if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
1237 			if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
1238 				zend_throw_error(zend_ffi_exception_ce, "Attempt to read field '%s' of non C struct/union", ZSTR_VAL(field_name));
1239 				return &EG(uninitialized_zval);
1240 			}
1241 		}
1242 
1243 		field = zend_hash_find_ptr(&type->record.fields, field_name);
1244 		if (UNEXPECTED(!field)) {
1245 			zend_throw_error(zend_ffi_exception_ce, "Attempt to read undefined field '%s' of C struct/union", ZSTR_VAL(field_name));
1246 			return &EG(uninitialized_zval);
1247 		}
1248 
1249 		if (cache_slot) {
1250 			*cache_slot = type;
1251 			*(cache_slot + 1) = field;
1252 		}
1253 	}
1254 
1255 	if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
1256 		/* transparently dereference the pointer */
1257 		if (UNEXPECTED(!ptr)) {
1258 			zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1259 			return &EG(uninitialized_zval);
1260 		}
1261 		ptr = (void*)(*(char**)ptr);
1262 		if (UNEXPECTED(!ptr)) {
1263 			zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1264 			return &EG(uninitialized_zval);
1265 		}
1266 		type = ZEND_FFI_TYPE(type->pointer.type);
1267 	}
1268 
1269 #if 0
1270 	if (UNEXPECTED(!ptr)) {
1271 		zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1272 		return &EG(uninitialized_zval);
1273 	}
1274 #endif
1275 
1276 	if (EXPECTED(!field->bits)) {
1277 		zend_ffi_type *field_type = field->type;
1278 
1279 		if (ZEND_FFI_TYPE_IS_OWNED(field_type)) {
1280 			field_type = ZEND_FFI_TYPE(field_type);
1281 			if (!(field_type->attr & ZEND_FFI_ATTR_STORED)
1282 			 && field_type->kind == ZEND_FFI_TYPE_POINTER) {
1283 				field->type = field_type = zend_ffi_remember_type(field_type);
1284 			}
1285 		}
1286 		ptr = (void*)(((char*)ptr) + field->offset);
1287 		zend_ffi_cdata_to_zval(NULL, ptr, field_type, read_type, rv, (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)field->is_const, 0, 0);
1288 	} else {
1289 		zend_ffi_bit_field_to_zval(ptr, field, rv);
1290 	}
1291 
1292 	return rv;
1293 }
1294 /* }}} */
1295 
zend_ffi_cdata_write_field(zend_object * obj,zend_string * field_name,zval * value,void ** cache_slot)1296 static zval *zend_ffi_cdata_write_field(zend_object *obj, zend_string *field_name, zval *value, void **cache_slot) /* {{{ */
1297 {
1298 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)obj;
1299 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
1300 	void           *ptr = cdata->ptr;
1301 	zend_ffi_field *field;
1302 
1303 	if (cache_slot && *cache_slot == type) {
1304 		field = *(cache_slot + 1);
1305 	} else {
1306 		if (UNEXPECTED(type == NULL)) {
1307 			zend_throw_error(zend_ffi_exception_ce, "Attempt to assign field '%s' to uninitialized FFI\\CData object", ZSTR_VAL(field_name));
1308 			return value;
1309 		}
1310 		if (type->kind == ZEND_FFI_TYPE_POINTER) {
1311 			type = ZEND_FFI_TYPE(type->pointer.type);
1312 		}
1313 		if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
1314 			if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
1315 				zend_throw_error(zend_ffi_exception_ce, "Attempt to assign field '%s' of non C struct/union", ZSTR_VAL(field_name));
1316 				return value;
1317 			}
1318 		}
1319 
1320 		field = zend_hash_find_ptr(&type->record.fields, field_name);
1321 		if (UNEXPECTED(!field)) {
1322 			zend_throw_error(zend_ffi_exception_ce, "Attempt to assign undefined field '%s' of C struct/union", ZSTR_VAL(field_name));
1323 			return value;
1324 		}
1325 
1326 		if (cache_slot) {
1327 			*cache_slot = type;
1328 			*(cache_slot + 1) = field;
1329 		}
1330 	}
1331 
1332 	if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
1333 		/* transparently dereference the pointer */
1334 		if (UNEXPECTED(!ptr)) {
1335 			zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1336 			return value;
1337 		}
1338 		ptr = (void*)(*(char**)ptr);
1339 		if (UNEXPECTED(!ptr)) {
1340 			zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1341 			return value;
1342 		}
1343 	}
1344 
1345 #if 0
1346 	if (UNEXPECTED(!ptr)) {
1347 		zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1348 		return value;
1349 	}
1350 #endif
1351 
1352 	if (UNEXPECTED(cdata->flags & ZEND_FFI_FLAG_CONST)) {
1353 		zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only location");
1354 		return value;
1355 	} else if (UNEXPECTED(field->is_const)) {
1356 		zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only field '%s'", ZSTR_VAL(field_name));
1357 		return value;
1358 	}
1359 
1360 	if (EXPECTED(!field->bits)) {
1361 		ptr = (void*)(((char*)ptr) + field->offset);
1362 		zend_ffi_zval_to_cdata(ptr, ZEND_FFI_TYPE(field->type), value);
1363 	} else {
1364 		zend_ffi_zval_to_bit_field(ptr, field, value);
1365 	}
1366 	return value;
1367 }
1368 /* }}} */
1369 
zend_ffi_cdata_read_dim(zend_object * obj,zval * offset,int read_type,zval * rv)1370 static zval *zend_ffi_cdata_read_dim(zend_object *obj, zval *offset, int read_type, zval *rv) /* {{{ */
1371 {
1372 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)obj;
1373 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
1374 	zend_long       dim = zval_get_long(offset);
1375 	zend_ffi_type  *dim_type;
1376 	void           *ptr;
1377 	zend_ffi_flags  is_const;
1378 
1379 	if (EXPECTED(type->kind == ZEND_FFI_TYPE_ARRAY)) {
1380 		if (UNEXPECTED((zend_ulong)(dim) >= (zend_ulong)type->array.length)
1381 		 && (UNEXPECTED(dim < 0) || UNEXPECTED(type->array.length != 0))) {
1382 			zend_throw_error(zend_ffi_exception_ce, "C array index out of bounds");
1383 			return &EG(uninitialized_zval);
1384 		}
1385 
1386 		is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
1387 
1388 		dim_type = type->array.type;
1389 		if (ZEND_FFI_TYPE_IS_OWNED(dim_type)) {
1390 			dim_type = ZEND_FFI_TYPE(dim_type);
1391 			if (!(dim_type->attr & ZEND_FFI_ATTR_STORED)
1392 			 && dim_type->kind == ZEND_FFI_TYPE_POINTER) {
1393 				type->array.type = dim_type = zend_ffi_remember_type(dim_type);
1394 			}
1395 		}
1396 #if 0
1397 		if (UNEXPECTED(!cdata->ptr)) {
1398 			zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1399 			return &EG(uninitialized_zval);
1400 		}
1401 #endif
1402 		ptr = (void*)(((char*)cdata->ptr) + dim_type->size * dim);
1403 	} else if (EXPECTED(type->kind == ZEND_FFI_TYPE_POINTER)) {
1404 		is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
1405 		dim_type = type->pointer.type;
1406 		if (ZEND_FFI_TYPE_IS_OWNED(dim_type)) {
1407 			dim_type = ZEND_FFI_TYPE(dim_type);
1408 			if (!(dim_type->attr & ZEND_FFI_ATTR_STORED)
1409 			 && dim_type->kind == ZEND_FFI_TYPE_POINTER) {
1410 				type->pointer.type = dim_type = zend_ffi_remember_type(dim_type);
1411 			}
1412 		}
1413 		if (UNEXPECTED(!cdata->ptr)) {
1414 			zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1415 			return &EG(uninitialized_zval);
1416 		}
1417 		ptr = (void*)((*(char**)cdata->ptr) + dim_type->size * dim);
1418 	} else {
1419 		zend_throw_error(zend_ffi_exception_ce, "Attempt to read element of non C array");
1420 		return &EG(uninitialized_zval);
1421 	}
1422 
1423 	zend_ffi_cdata_to_zval(NULL, ptr, dim_type, read_type, rv, is_const, 0, 0);
1424 	return rv;
1425 }
1426 /* }}} */
1427 
zend_ffi_cdata_write_dim(zend_object * obj,zval * offset,zval * value)1428 static void zend_ffi_cdata_write_dim(zend_object *obj, zval *offset, zval *value) /* {{{ */
1429 {
1430 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)obj;
1431 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
1432 	zend_long       dim;
1433 	void           *ptr;
1434 	zend_ffi_flags  is_const;
1435 
1436 	if (offset == NULL) {
1437 		zend_throw_error(zend_ffi_exception_ce, "Cannot add next element to object of type FFI\\CData");
1438 		return;
1439 	}
1440 
1441 	dim = zval_get_long(offset);
1442 	if (EXPECTED(type->kind == ZEND_FFI_TYPE_ARRAY)) {
1443 		if (UNEXPECTED((zend_ulong)(dim) >= (zend_ulong)type->array.length)
1444 		 && (UNEXPECTED(dim < 0) || UNEXPECTED(type->array.length != 0))) {
1445 			zend_throw_error(zend_ffi_exception_ce, "C array index out of bounds");
1446 			return;
1447 		}
1448 
1449 		is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
1450 		type = ZEND_FFI_TYPE(type->array.type);
1451 #if 0
1452 		if (UNEXPECTED(!cdata->ptr)) {
1453 			zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1454 			return;
1455 		}
1456 #endif
1457 		ptr = (void*)(((char*)cdata->ptr) + type->size * dim);
1458 	} else if (EXPECTED(type->kind == ZEND_FFI_TYPE_POINTER)) {
1459 		is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
1460 		type = ZEND_FFI_TYPE(type->pointer.type);
1461 		if (UNEXPECTED(!cdata->ptr)) {
1462 			zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1463 			return;
1464 		}
1465 		ptr = (void*)((*(char**)cdata->ptr) + type->size * dim);
1466 	} else {
1467 		zend_throw_error(zend_ffi_exception_ce, "Attempt to assign element of non C array");
1468 		return;
1469 	}
1470 
1471 	if (UNEXPECTED(is_const)) {
1472 		zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only location");
1473 		return;
1474 	}
1475 
1476 	zend_ffi_zval_to_cdata(ptr, type, value);
1477 }
1478 /* }}} */
1479 
1480 #define MAX_TYPE_NAME_LEN 256
1481 
1482 typedef struct _zend_ffi_ctype_name_buf {
1483 	char *start;
1484 	char *end;
1485 	char buf[MAX_TYPE_NAME_LEN];
1486 } zend_ffi_ctype_name_buf;
1487 
zend_ffi_ctype_name_prepend(zend_ffi_ctype_name_buf * buf,const char * str,size_t len)1488 static bool zend_ffi_ctype_name_prepend(zend_ffi_ctype_name_buf *buf, const char *str, size_t len) /* {{{ */
1489 {
1490 	buf->start -= len;
1491 	if (buf->start < buf->buf) {
1492 		return 0;
1493 	}
1494 	memcpy(buf->start, str, len);
1495 	return 1;
1496 }
1497 /* }}} */
1498 
zend_ffi_ctype_name_append(zend_ffi_ctype_name_buf * buf,const char * str,size_t len)1499 static bool zend_ffi_ctype_name_append(zend_ffi_ctype_name_buf *buf, const char *str, size_t len) /* {{{ */
1500 {
1501 	if (buf->end + len > buf->buf + MAX_TYPE_NAME_LEN) {
1502 		return 0;
1503 	}
1504 	buf->end = zend_mempcpy(buf->end, str, len);
1505 	return 1;
1506 }
1507 /* }}} */
1508 
zend_ffi_ctype_name(zend_ffi_ctype_name_buf * buf,const zend_ffi_type * type)1509 static bool zend_ffi_ctype_name(zend_ffi_ctype_name_buf *buf, const zend_ffi_type *type) /* {{{ */
1510 {
1511 	const char *name = NULL;
1512 	bool is_ptr = 0;
1513 
1514 	while (1) {
1515 		switch (type->kind) {
1516 			case ZEND_FFI_TYPE_VOID:
1517 				name = "void";
1518 				break;
1519 			case ZEND_FFI_TYPE_FLOAT:
1520 				name = "float";
1521 				break;
1522 			case ZEND_FFI_TYPE_DOUBLE:
1523 				name = "double";
1524 				break;
1525 #ifdef HAVE_LONG_DOUBLE
1526 			case ZEND_FFI_TYPE_LONGDOUBLE:
1527 				name = "long double";
1528 				break;
1529 #endif
1530 			case ZEND_FFI_TYPE_UINT8:
1531 				name = "uint8_t";
1532 				break;
1533 			case ZEND_FFI_TYPE_SINT8:
1534 				name = "int8_t";
1535 				break;
1536 			case ZEND_FFI_TYPE_UINT16:
1537 				name = "uint16_t";
1538 				break;
1539 			case ZEND_FFI_TYPE_SINT16:
1540 				name = "int16_t";
1541 				break;
1542 			case ZEND_FFI_TYPE_UINT32:
1543 				name = "uint32_t";
1544 				break;
1545 			case ZEND_FFI_TYPE_SINT32:
1546 				name = "int32_t";
1547 				break;
1548 			case ZEND_FFI_TYPE_UINT64:
1549 				name = "uint64_t";
1550 				break;
1551 			case ZEND_FFI_TYPE_SINT64:
1552 				name = "int64_t";
1553 				break;
1554 			case ZEND_FFI_TYPE_ENUM:
1555 				if (type->enumeration.tag_name) {
1556 					zend_ffi_ctype_name_prepend(buf, ZSTR_VAL(type->enumeration.tag_name), ZSTR_LEN(type->enumeration.tag_name));
1557 				} else {
1558 					zend_ffi_ctype_name_prepend(buf, "<anonymous>", sizeof("<anonymous>")-1);
1559 				}
1560 				name = "enum ";
1561 				break;
1562 			case ZEND_FFI_TYPE_BOOL:
1563 				name = "bool";
1564 				break;
1565 			case ZEND_FFI_TYPE_CHAR:
1566 				name = "char";
1567 				break;
1568 			case ZEND_FFI_TYPE_POINTER:
1569 				if (!zend_ffi_ctype_name_prepend(buf, "*", 1)) {
1570 					return 0;
1571 				}
1572 				is_ptr = 1;
1573 				type = ZEND_FFI_TYPE(type->pointer.type);
1574 				break;
1575 			case ZEND_FFI_TYPE_FUNC:
1576 				if (is_ptr) {
1577 					is_ptr = 0;
1578 					if (!zend_ffi_ctype_name_prepend(buf, "(", 1)
1579 					 || !zend_ffi_ctype_name_append(buf, ")", 1)) {
1580 						return 0;
1581 					}
1582 				}
1583 				if (!zend_ffi_ctype_name_append(buf, "(", 1)
1584 				 || !zend_ffi_ctype_name_append(buf, ")", 1)) {
1585 					return 0;
1586 				}
1587 				type = ZEND_FFI_TYPE(type->func.ret_type);
1588 				break;
1589 			case ZEND_FFI_TYPE_ARRAY:
1590 				if (is_ptr) {
1591 					is_ptr = 0;
1592 					if (!zend_ffi_ctype_name_prepend(buf, "(", 1)
1593 					 || !zend_ffi_ctype_name_append(buf, ")", 1)) {
1594 						return 0;
1595 					}
1596 				}
1597 				if (!zend_ffi_ctype_name_append(buf, "[", 1)) {
1598 					return 0;
1599 				}
1600 				if (type->attr & ZEND_FFI_ATTR_VLA) {
1601 					if (!zend_ffi_ctype_name_append(buf, "*", 1)) {
1602 						return 0;
1603 					}
1604 				} else if (!(type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
1605 					char str[MAX_LENGTH_OF_LONG + 1];
1606 					char *s = zend_print_long_to_buf(str + sizeof(str) - 1, type->array.length);
1607 
1608 					if (!zend_ffi_ctype_name_append(buf, s, strlen(s))) {
1609 						return 0;
1610 					}
1611 				}
1612 				if (!zend_ffi_ctype_name_append(buf, "]", 1)) {
1613 					return 0;
1614 				}
1615 				type = ZEND_FFI_TYPE(type->array.type);
1616 				break;
1617 			case ZEND_FFI_TYPE_STRUCT:
1618 				if (type->attr & ZEND_FFI_ATTR_UNION) {
1619 					if (type->record.tag_name) {
1620 						zend_ffi_ctype_name_prepend(buf, ZSTR_VAL(type->record.tag_name), ZSTR_LEN(type->record.tag_name));
1621 					} else {
1622 						zend_ffi_ctype_name_prepend(buf, "<anonymous>", sizeof("<anonymous>")-1);
1623 					}
1624 					name = "union ";
1625 				} else {
1626 					if (type->record.tag_name) {
1627 						zend_ffi_ctype_name_prepend(buf, ZSTR_VAL(type->record.tag_name), ZSTR_LEN(type->record.tag_name));
1628 					} else {
1629 						zend_ffi_ctype_name_prepend(buf, "<anonymous>", sizeof("<anonymous>")-1);
1630 					}
1631 					name = "struct ";
1632 				}
1633 				break;
1634 			default:
1635 				ZEND_UNREACHABLE();
1636 		}
1637 		if (name) {
1638 			break;
1639 		}
1640 	}
1641 
1642 //	if (buf->start != buf->end && *buf->start != '[') {
1643 //		if (!zend_ffi_ctype_name_prepend(buf, " ", 1)) {
1644 //			return 0;
1645 //		}
1646 //	}
1647 	return zend_ffi_ctype_name_prepend(buf, name, strlen(name));
1648 }
1649 /* }}} */
1650 
zend_ffi_return_unsupported(zend_ffi_type * type)1651 static ZEND_COLD void zend_ffi_return_unsupported(zend_ffi_type *type) /* {{{ */
1652 {
1653 	type = ZEND_FFI_TYPE(type);
1654 	if (type->kind == ZEND_FFI_TYPE_STRUCT) {
1655 		zend_throw_error(zend_ffi_exception_ce, "FFI return struct/union is not implemented");
1656 	} else if (type->kind == ZEND_FFI_TYPE_ARRAY) {
1657 		zend_throw_error(zend_ffi_exception_ce, "FFI return array is not implemented");
1658 	} else {
1659 		zend_throw_error(zend_ffi_exception_ce, "FFI internal error. Unsupported return type");
1660 	}
1661 }
1662 /* }}} */
1663 
zend_ffi_pass_unsupported(zend_ffi_type * type)1664 static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type) /* {{{ */
1665 {
1666 	type = ZEND_FFI_TYPE(type);
1667 	if (type->kind == ZEND_FFI_TYPE_STRUCT) {
1668 		zend_throw_error(zend_ffi_exception_ce, "FFI passing struct/union is not implemented");
1669 	} else if (type->kind == ZEND_FFI_TYPE_ARRAY) {
1670 		zend_throw_error(zend_ffi_exception_ce, "FFI passing array is not implemented");
1671 	} else {
1672 		zend_throw_error(zend_ffi_exception_ce, "FFI internal error. Unsupported parameter type");
1673 	}
1674 }
1675 /* }}} */
1676 
zend_ffi_pass_incompatible(zval * arg,zend_ffi_type * type,uint32_t n,zend_execute_data * execute_data)1677 static ZEND_COLD void zend_ffi_pass_incompatible(zval *arg, zend_ffi_type *type, uint32_t n, zend_execute_data *execute_data) /* {{{ */
1678 {
1679 	zend_ffi_ctype_name_buf buf1, buf2;
1680 
1681 	buf1.start = buf1.end = buf1.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
1682 	if (!zend_ffi_ctype_name(&buf1, type)) {
1683 		zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name));
1684 	} else {
1685 		*buf1.end = 0;
1686 		if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
1687 			zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
1688 
1689 			type = ZEND_FFI_TYPE(cdata->type);
1690 			buf2.start = buf2.end = buf2.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
1691 			if (!zend_ffi_ctype_name(&buf2, type)) {
1692 				zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s', expecting '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name), buf1.start);
1693 			} else {
1694 				*buf2.end = 0;
1695 				zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s', expecting '%s', found '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name), buf1.start, buf2.start);
1696 			}
1697 		} else {
1698 			zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s', expecting '%s', found PHP '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name), buf1.start, zend_zval_value_name(arg));
1699 		}
1700 	}
1701 }
1702 /* }}} */
1703 
zend_ffi_assign_incompatible(zval * arg,zend_ffi_type * type)1704 static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type) /* {{{ */
1705 {
1706 	zend_ffi_ctype_name_buf buf1, buf2;
1707 
1708 	buf1.start = buf1.end = buf1.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
1709 	if (!zend_ffi_ctype_name(&buf1, type)) {
1710 		zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning");
1711 	} else {
1712 		*buf1.end = 0;
1713 		if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
1714 			zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
1715 
1716 			type = ZEND_FFI_TYPE(cdata->type);
1717 			buf2.start = buf2.end = buf2.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
1718 			if (!zend_ffi_ctype_name(&buf2, type)) {
1719 				zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning to type '%s'", buf1.start);
1720 			} else {
1721 				*buf2.end = 0;
1722 				zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning to type '%s' from type '%s'", buf1.start, buf2.start);
1723 			}
1724 		} else {
1725 			zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning to type '%s' from PHP '%s'", buf1.start, zend_zval_value_name(arg));
1726 		}
1727 	}
1728 }
1729 /* }}} */
1730 
zend_ffi_get_class_name(zend_string * prefix,const zend_ffi_type * type)1731 static zend_string *zend_ffi_get_class_name(zend_string *prefix, const zend_ffi_type *type) /* {{{ */
1732 {
1733 	zend_ffi_ctype_name_buf buf;
1734 
1735 	buf.start = buf.end = buf.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
1736 	if (!zend_ffi_ctype_name(&buf, type)) {
1737 		return zend_string_copy(prefix);
1738 	} else {
1739 		return zend_string_concat3(
1740 			ZSTR_VAL(prefix), ZSTR_LEN(prefix), ":", 1, buf.start, buf.end - buf.start);
1741 	}
1742 }
1743 /* }}} */
1744 
zend_ffi_cdata_get_class_name(const zend_object * zobj)1745 static zend_string *zend_ffi_cdata_get_class_name(const zend_object *zobj) /* {{{ */
1746 {
1747 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)zobj;
1748 
1749 	return zend_ffi_get_class_name(zobj->ce->name, ZEND_FFI_TYPE(cdata->type));
1750 }
1751 /* }}} */
1752 
zend_ffi_cdata_compare_objects(zval * o1,zval * o2)1753 static int zend_ffi_cdata_compare_objects(zval *o1, zval *o2) /* {{{ */
1754 {
1755 	if (Z_TYPE_P(o1) == IS_OBJECT && Z_OBJCE_P(o1) == zend_ffi_cdata_ce &&
1756 	    Z_TYPE_P(o2) == IS_OBJECT && Z_OBJCE_P(o2) == zend_ffi_cdata_ce) {
1757 		zend_ffi_cdata *cdata1 = (zend_ffi_cdata*)Z_OBJ_P(o1);
1758 		zend_ffi_cdata *cdata2 = (zend_ffi_cdata*)Z_OBJ_P(o2);
1759 		zend_ffi_type *type1 = ZEND_FFI_TYPE(cdata1->type);
1760 		zend_ffi_type *type2 = ZEND_FFI_TYPE(cdata2->type);
1761 
1762 		if (type1->kind == ZEND_FFI_TYPE_POINTER && type2->kind == ZEND_FFI_TYPE_POINTER) {
1763 			void *ptr1 = *(void**)cdata1->ptr;
1764 			void *ptr2 = *(void**)cdata2->ptr;
1765 
1766 			if (!ptr1 || !ptr2) {
1767 				zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1768 				return 0;
1769 			}
1770 			return ptr1 == ptr2 ? 0 : (ptr1 < ptr2 ? -1 : 1);
1771 		}
1772 	}
1773 	zend_throw_error(zend_ffi_exception_ce, "Comparison of incompatible C types");
1774 	return 0;
1775 }
1776 /* }}} */
1777 
zend_ffi_cdata_count_elements(zend_object * obj,zend_long * count)1778 static zend_result zend_ffi_cdata_count_elements(zend_object *obj, zend_long *count) /* {{{ */
1779 {
1780 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)obj;
1781 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
1782 
1783 	if (type->kind != ZEND_FFI_TYPE_ARRAY) {
1784 		zend_throw_error(zend_ffi_exception_ce, "Attempt to count() on non C array");
1785 		return FAILURE;
1786 	} else {
1787 		*count = type->array.length;
1788 		return SUCCESS;
1789 	}
1790 }
1791 /* }}} */
1792 
zend_ffi_add(zend_ffi_cdata * base_cdata,zend_ffi_type * base_type,zend_long offset)1793 static zend_object* zend_ffi_add(zend_ffi_cdata *base_cdata, zend_ffi_type *base_type, zend_long offset) /* {{{ */
1794 {
1795 	char *ptr;
1796 	zend_ffi_type *ptr_type;
1797 	zend_ffi_cdata *cdata =
1798 		(zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
1799 
1800 	if (base_type->kind == ZEND_FFI_TYPE_POINTER) {
1801 		if (ZEND_FFI_TYPE_IS_OWNED(base_cdata->type)) {
1802 			if (!(base_type->attr & ZEND_FFI_ATTR_STORED)) {
1803 				if (GC_REFCOUNT(&base_cdata->std) == 1) {
1804 					/* transfer type ownership */
1805 					base_cdata->type = base_type;
1806 					base_type = ZEND_FFI_TYPE_MAKE_OWNED(base_type);
1807 				} else {
1808 					base_cdata->type = base_type = zend_ffi_remember_type(base_type);
1809 				}
1810 			}
1811 		}
1812 		cdata->type = base_type;
1813 		ptr = (char*)(*(void**)base_cdata->ptr);
1814 		ptr_type = ZEND_FFI_TYPE(base_type)->pointer.type;
1815 	} else {
1816 		zend_ffi_type *new_type = emalloc(sizeof(zend_ffi_type));
1817 
1818 		new_type->kind = ZEND_FFI_TYPE_POINTER;
1819 		new_type->attr = 0;
1820 		new_type->size = sizeof(void*);
1821 		new_type->align = _Alignof(void*);
1822 
1823 		ptr_type = base_type->array.type;
1824 		if (ZEND_FFI_TYPE_IS_OWNED(ptr_type)) {
1825 			ptr_type = ZEND_FFI_TYPE(ptr_type);
1826 			if (!(ptr_type->attr & ZEND_FFI_ATTR_STORED)) {
1827 				if (GC_REFCOUNT(&base_cdata->std) == 1) {
1828 					/* transfer type ownership */
1829 					base_type->array.type = ptr_type;
1830 					ptr_type = ZEND_FFI_TYPE_MAKE_OWNED(ptr_type);
1831 				} else {
1832 					base_type->array.type = ptr_type = zend_ffi_remember_type(ptr_type);
1833 				}
1834 			}
1835 		}
1836 		new_type->pointer.type = ptr_type;
1837 
1838 		cdata->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
1839 		ptr = (char*)base_cdata->ptr;
1840 	}
1841 	cdata->ptr = &cdata->ptr_holder;
1842 	cdata->ptr_holder = ptr +
1843 		(ptrdiff_t) (offset * ZEND_FFI_TYPE(ptr_type)->size);
1844 	cdata->flags = base_cdata->flags & ZEND_FFI_FLAG_CONST;
1845 	return &cdata->std;
1846 }
1847 /* }}} */
1848 
zend_ffi_cdata_do_operation(uint8_t opcode,zval * result,zval * op1,zval * op2)1849 static zend_result zend_ffi_cdata_do_operation(uint8_t opcode, zval *result, zval *op1, zval *op2) /* {{{ */
1850 {
1851 	zend_long offset;
1852 
1853 	ZVAL_DEREF(op1);
1854 	ZVAL_DEREF(op2);
1855 	if (Z_TYPE_P(op1) == IS_OBJECT && Z_OBJCE_P(op1) == zend_ffi_cdata_ce) {
1856 		zend_ffi_cdata *cdata1 = (zend_ffi_cdata*)Z_OBJ_P(op1);
1857 		zend_ffi_type *type1 = ZEND_FFI_TYPE(cdata1->type);
1858 
1859 		if (type1->kind == ZEND_FFI_TYPE_POINTER || type1->kind == ZEND_FFI_TYPE_ARRAY) {
1860 			if (opcode == ZEND_ADD) {
1861 				offset = zval_get_long(op2);
1862 				ZVAL_OBJ(result, zend_ffi_add(cdata1, type1, offset));
1863 				if (result == op1) {
1864 					OBJ_RELEASE(&cdata1->std);
1865 				}
1866 				return SUCCESS;
1867 			} else if (opcode == ZEND_SUB) {
1868 				if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJCE_P(op2) == zend_ffi_cdata_ce) {
1869 					zend_ffi_cdata *cdata2 = (zend_ffi_cdata*)Z_OBJ_P(op2);
1870 					zend_ffi_type *type2 = ZEND_FFI_TYPE(cdata2->type);
1871 
1872 					if (type2->kind == ZEND_FFI_TYPE_POINTER || type2->kind == ZEND_FFI_TYPE_ARRAY) {
1873 						zend_ffi_type *t1, *t2;
1874 						char *p1, *p2;
1875 
1876 						if (type1->kind == ZEND_FFI_TYPE_POINTER) {
1877 							t1 = ZEND_FFI_TYPE(type1->pointer.type);
1878 							p1 = (char*)(*(void**)cdata1->ptr);
1879 						} else {
1880 							t1 = ZEND_FFI_TYPE(type1->array.type);
1881 							p1 = cdata1->ptr;
1882 						}
1883 						if (type2->kind == ZEND_FFI_TYPE_POINTER) {
1884 							t2 = ZEND_FFI_TYPE(type2->pointer.type);
1885 							p2 = (char*)(*(void**)cdata2->ptr);
1886 						} else {
1887 							t2 = ZEND_FFI_TYPE(type2->array.type);
1888 							p2 = cdata2->ptr;
1889 						}
1890 						if (zend_ffi_is_same_type(t1, t2)) {
1891 							ZVAL_LONG(result,
1892 								(zend_long)(p1 - p2) / (zend_long)t1->size);
1893 							return SUCCESS;
1894 						}
1895 					}
1896 				}
1897 				offset = zval_get_long(op2);
1898 				ZVAL_OBJ(result, zend_ffi_add(cdata1, type1, -offset));
1899 				if (result == op1) {
1900 					OBJ_RELEASE(&cdata1->std);
1901 				}
1902 				return SUCCESS;
1903 			}
1904 		}
1905 	} else if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJCE_P(op2) == zend_ffi_cdata_ce) {
1906 		zend_ffi_cdata *cdata2 = (zend_ffi_cdata*)Z_OBJ_P(op2);
1907 		zend_ffi_type *type2 = ZEND_FFI_TYPE(cdata2->type);
1908 
1909 		if (type2->kind == ZEND_FFI_TYPE_POINTER || type2->kind == ZEND_FFI_TYPE_ARRAY) {
1910 			if (opcode == ZEND_ADD) {
1911 				offset = zval_get_long(op1);
1912 				ZVAL_OBJ(result, zend_ffi_add(cdata2, type2, offset));
1913 				return SUCCESS;
1914 			}
1915 		}
1916 	}
1917 
1918 	return FAILURE;
1919 }
1920 /* }}} */
1921 
1922 typedef struct _zend_ffi_cdata_iterator {
1923 	zend_object_iterator it;
1924 	zend_long key;
1925 	zval value;
1926 	bool by_ref;
1927 } zend_ffi_cdata_iterator;
1928 
zend_ffi_cdata_it_dtor(zend_object_iterator * iter)1929 static void zend_ffi_cdata_it_dtor(zend_object_iterator *iter) /* {{{ */
1930 {
1931 	zval_ptr_dtor(&((zend_ffi_cdata_iterator*)iter)->value);
1932 	zval_ptr_dtor(&iter->data);
1933 }
1934 /* }}} */
1935 
zend_ffi_cdata_it_valid(zend_object_iterator * it)1936 static zend_result zend_ffi_cdata_it_valid(zend_object_iterator *it) /* {{{ */
1937 {
1938 	zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
1939 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ(iter->it.data);
1940 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
1941 
1942 	return (iter->key >= 0 && iter->key < type->array.length) ? SUCCESS : FAILURE;
1943 }
1944 /* }}} */
1945 
zend_ffi_cdata_it_get_current_data(zend_object_iterator * it)1946 static zval *zend_ffi_cdata_it_get_current_data(zend_object_iterator *it) /* {{{ */
1947 {
1948 	zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
1949 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ(iter->it.data);
1950 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
1951 	zend_ffi_type  *dim_type;
1952 	void *ptr;
1953 
1954 	if (!cdata->ptr) {
1955 		zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
1956 		return &EG(uninitialized_zval);
1957 	}
1958 	dim_type = type->array.type;
1959 	if (ZEND_FFI_TYPE_IS_OWNED(dim_type)) {
1960 		dim_type = ZEND_FFI_TYPE(dim_type);
1961 		if (!(dim_type->attr & ZEND_FFI_ATTR_STORED)
1962 		 && dim_type->kind == ZEND_FFI_TYPE_POINTER) {
1963 			type->array.type = dim_type = zend_ffi_remember_type(dim_type);
1964 		}
1965 	}
1966 	ptr = (void*)((char*)cdata->ptr + dim_type->size * iter->it.index);
1967 
1968 	zval_ptr_dtor(&iter->value);
1969 	zend_ffi_cdata_to_zval(NULL, ptr, dim_type, iter->by_ref ? BP_VAR_RW : BP_VAR_R, &iter->value, (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST), 0, 0);
1970 	return &iter->value;
1971 }
1972 /* }}} */
1973 
zend_ffi_cdata_it_get_current_key(zend_object_iterator * it,zval * key)1974 static void zend_ffi_cdata_it_get_current_key(zend_object_iterator *it, zval *key) /* {{{ */
1975 {
1976 	zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
1977 	ZVAL_LONG(key, iter->key);
1978 }
1979 /* }}} */
1980 
zend_ffi_cdata_it_move_forward(zend_object_iterator * it)1981 static void zend_ffi_cdata_it_move_forward(zend_object_iterator *it) /* {{{ */
1982 {
1983 	zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
1984 	iter->key++;
1985 }
1986 /* }}} */
1987 
zend_ffi_cdata_it_rewind(zend_object_iterator * it)1988 static void zend_ffi_cdata_it_rewind(zend_object_iterator *it) /* {{{ */
1989 {
1990 	zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
1991 	iter->key = 0;
1992 }
1993 /* }}} */
1994 
1995 static const zend_object_iterator_funcs zend_ffi_cdata_it_funcs = {
1996 	zend_ffi_cdata_it_dtor,
1997 	zend_ffi_cdata_it_valid,
1998 	zend_ffi_cdata_it_get_current_data,
1999 	zend_ffi_cdata_it_get_current_key,
2000 	zend_ffi_cdata_it_move_forward,
2001 	zend_ffi_cdata_it_rewind,
2002 	NULL,
2003 	NULL, /* get_gc */
2004 };
2005 
zend_ffi_cdata_get_iterator(zend_class_entry * ce,zval * object,int by_ref)2006 static zend_object_iterator *zend_ffi_cdata_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
2007 {
2008 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
2009 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
2010 	zend_ffi_cdata_iterator *iter;
2011 
2012 	if (type->kind != ZEND_FFI_TYPE_ARRAY) {
2013 		zend_throw_error(zend_ffi_exception_ce, "Attempt to iterate on non C array");
2014 		return NULL;
2015 	}
2016 
2017 	iter = emalloc(sizeof(zend_ffi_cdata_iterator));
2018 
2019 	zend_iterator_init(&iter->it);
2020 
2021 	Z_ADDREF_P(object);
2022 	ZVAL_OBJ(&iter->it.data, Z_OBJ_P(object));
2023 	iter->it.funcs = &zend_ffi_cdata_it_funcs;
2024 	iter->key = 0;
2025 	iter->by_ref = by_ref;
2026 	ZVAL_UNDEF(&iter->value);
2027 
2028 	return &iter->it;
2029 }
2030 /* }}} */
2031 
zend_ffi_cdata_get_debug_info(zend_object * obj,int * is_temp)2032 static HashTable *zend_ffi_cdata_get_debug_info(zend_object *obj, int *is_temp) /* {{{ */
2033 {
2034 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)obj;
2035 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
2036 	void           *ptr = cdata->ptr;
2037 	HashTable      *ht = NULL;
2038 	zend_string    *key;
2039 	zend_ffi_field *f;
2040 	zend_long       n;
2041 	zval            tmp;
2042 
2043 	if (!cdata->ptr) {
2044 		zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
2045 		return NULL;
2046 	}
2047 
2048 	switch (type->kind) {
2049 		case ZEND_FFI_TYPE_VOID:
2050 			return NULL;
2051 		case ZEND_FFI_TYPE_BOOL:
2052 		case ZEND_FFI_TYPE_CHAR:
2053 		case ZEND_FFI_TYPE_ENUM:
2054 		case ZEND_FFI_TYPE_FLOAT:
2055 		case ZEND_FFI_TYPE_DOUBLE:
2056 #ifdef HAVE_LONG_DOUBLE
2057 		case ZEND_FFI_TYPE_LONGDOUBLE:
2058 #endif
2059 		case ZEND_FFI_TYPE_UINT8:
2060 		case ZEND_FFI_TYPE_SINT8:
2061 		case ZEND_FFI_TYPE_UINT16:
2062 		case ZEND_FFI_TYPE_SINT16:
2063 		case ZEND_FFI_TYPE_UINT32:
2064 		case ZEND_FFI_TYPE_SINT32:
2065 		case ZEND_FFI_TYPE_UINT64:
2066 		case ZEND_FFI_TYPE_SINT64:
2067 			zend_ffi_cdata_to_zval(cdata, ptr, type, BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, 0);
2068 			ht = zend_new_array(1);
2069 			zend_hash_str_add(ht, "cdata", sizeof("cdata")-1, &tmp);
2070 			*is_temp = 1;
2071 			return ht;
2072 			break;
2073 		case ZEND_FFI_TYPE_POINTER:
2074 			if (*(void**)ptr == NULL) {
2075 				ZVAL_NULL(&tmp);
2076 				ht = zend_new_array(1);
2077 				zend_hash_index_add_new(ht, 0, &tmp);
2078 				*is_temp = 1;
2079 				return ht;
2080 			} else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_VOID) {
2081 				ZVAL_LONG(&tmp, (uintptr_t)*(void**)ptr);
2082 				ht = zend_new_array(1);
2083 				zend_hash_index_add_new(ht, 0, &tmp);
2084 				*is_temp = 1;
2085 				return ht;
2086 			} else {
2087 				zend_ffi_cdata_to_zval(NULL, *(void**)ptr, ZEND_FFI_TYPE(type->pointer.type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, 0);
2088 				ht = zend_new_array(1);
2089 				zend_hash_index_add_new(ht, 0, &tmp);
2090 				*is_temp = 1;
2091 				return ht;
2092 			}
2093 			break;
2094 		case ZEND_FFI_TYPE_STRUCT:
2095 			ht = zend_new_array(zend_hash_num_elements(&type->record.fields));
2096 			ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&type->record.fields, key, f) {
2097 				if (key) {
2098 					if (!f->bits) {
2099 						void *f_ptr = (void*)(((char*)ptr) + f->offset);
2100 						zend_ffi_cdata_to_zval(NULL, f_ptr, ZEND_FFI_TYPE(f->type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, type->attr & ZEND_FFI_ATTR_UNION);
2101 						zend_hash_add(ht, key, &tmp);
2102 					} else {
2103 						zend_ffi_bit_field_to_zval(ptr, f, &tmp);
2104 						zend_hash_add(ht, key, &tmp);
2105 					}
2106 				}
2107 			} ZEND_HASH_FOREACH_END();
2108 			*is_temp = 1;
2109 			return ht;
2110 		case ZEND_FFI_TYPE_ARRAY:
2111 			ht = zend_new_array(type->array.length);
2112 			for (n = 0; n < type->array.length; n++) {
2113 				zend_ffi_cdata_to_zval(NULL, ptr, ZEND_FFI_TYPE(type->array.type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, 0);
2114 				zend_hash_index_add(ht, n, &tmp);
2115 				ptr = (void*)(((char*)ptr) + ZEND_FFI_TYPE(type->array.type)->size);
2116 			}
2117 			*is_temp = 1;
2118 			return ht;
2119 		case ZEND_FFI_TYPE_FUNC:
2120 			ht = zend_new_array(0);
2121 			// TODO: function name ???
2122 			*is_temp = 1;
2123 			return ht;
2124 			break;
2125 		default:
2126 			ZEND_UNREACHABLE();
2127 			break;
2128 	}
2129 	return NULL;
2130 }
2131 /* }}} */
2132 
zend_ffi_cdata_get_closure(zend_object * obj,zend_class_entry ** ce_ptr,zend_function ** fptr_ptr,zend_object ** obj_ptr,bool check_only)2133 static zend_result zend_ffi_cdata_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */
2134 {
2135 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)obj;
2136 	zend_ffi_type  *type = ZEND_FFI_TYPE(cdata->type);
2137 	zend_function  *func;
2138 
2139 	if (type->kind != ZEND_FFI_TYPE_POINTER) {
2140 		if (!check_only) {
2141 			zend_throw_error(zend_ffi_exception_ce, "Attempt to call non C function pointer");
2142 		}
2143 		return FAILURE;
2144 	}
2145 	type = ZEND_FFI_TYPE(type->pointer.type);
2146 	if (type->kind != ZEND_FFI_TYPE_FUNC) {
2147 		if (!check_only) {
2148 			zend_throw_error(zend_ffi_exception_ce, "Attempt to call non C function pointer");
2149 		}
2150 		return FAILURE;
2151 	}
2152 	if (!cdata->ptr) {
2153 		if (!check_only) {
2154 			zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
2155 		}
2156 		return FAILURE;
2157 	}
2158 
2159 	if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
2160 		func = &EG(trampoline);
2161 	} else {
2162 		func = ecalloc(sizeof(zend_internal_function), 1);
2163 	}
2164 	func->type = ZEND_INTERNAL_FUNCTION;
2165 	func->common.arg_flags[0] = 0;
2166 	func->common.arg_flags[1] = 0;
2167 	func->common.arg_flags[2] = 0;
2168 	func->common.fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE;
2169 	func->common.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE);
2170 	/* set to 0 to avoid arg_info[] allocation, because all values are passed by value anyway */
2171 	func->common.num_args = 0;
2172 	func->common.required_num_args = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
2173 	func->common.scope = NULL;
2174 	func->common.prototype = NULL;
2175 	func->common.arg_info = NULL;
2176 	func->internal_function.handler = ZEND_FN(ffi_trampoline);
2177 	func->internal_function.module = NULL;
2178 
2179 	func->internal_function.reserved[0] = type;
2180 	func->internal_function.reserved[1] = *(void**)cdata->ptr;
2181 
2182 	*ce_ptr = NULL;
2183 	*fptr_ptr= func;
2184 	*obj_ptr = NULL;
2185 
2186 	return SUCCESS;
2187 }
2188 /* }}} */
2189 
zend_ffi_ctype_new(zend_class_entry * class_type)2190 static zend_object *zend_ffi_ctype_new(zend_class_entry *class_type) /* {{{ */
2191 {
2192 	zend_ffi_ctype *ctype;
2193 
2194 	ctype = emalloc(sizeof(zend_ffi_ctype));
2195 
2196 	zend_ffi_object_init(&ctype->std, class_type);
2197 
2198 	ctype->type = NULL;
2199 
2200 	return &ctype->std;
2201 }
2202 /* }}} */
2203 
zend_ffi_ctype_free_obj(zend_object * object)2204 static void zend_ffi_ctype_free_obj(zend_object *object) /* {{{ */
2205 {
2206 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)object;
2207 
2208 	zend_ffi_type_dtor(ctype->type);
2209 
2210     if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) {
2211         zend_weakrefs_notify(object);
2212     }
2213 }
2214 /* }}} */
2215 
zend_ffi_is_same_type(zend_ffi_type * type1,zend_ffi_type * type2)2216 static bool zend_ffi_is_same_type(zend_ffi_type *type1, zend_ffi_type *type2) /* {{{ */
2217 {
2218 	while (1) {
2219 		if (type1 == type2) {
2220 			return 1;
2221 		} else if (type1->kind == type2->kind) {
2222 			if (type1->kind < ZEND_FFI_TYPE_POINTER) {
2223 				return 1;
2224 			} else if (type1->kind == ZEND_FFI_TYPE_POINTER) {
2225 				type1 = ZEND_FFI_TYPE(type1->pointer.type);
2226 				type2 = ZEND_FFI_TYPE(type2->pointer.type);
2227 				if (type1->kind == ZEND_FFI_TYPE_VOID ||
2228 				    type2->kind == ZEND_FFI_TYPE_VOID) {
2229 				    return 1;
2230 				}
2231 			} else if (type1->kind == ZEND_FFI_TYPE_ARRAY &&
2232 			           type1->array.length == type2->array.length) {
2233 				type1 = ZEND_FFI_TYPE(type1->array.type);
2234 				type2 = ZEND_FFI_TYPE(type2->array.type);
2235 			} else {
2236 				break;
2237 			}
2238 		} else {
2239 			break;
2240 		}
2241 	}
2242 	return 0;
2243 }
2244 /* }}} */
2245 
zend_ffi_ctype_get_class_name(const zend_object * zobj)2246 static zend_string *zend_ffi_ctype_get_class_name(const zend_object *zobj) /* {{{ */
2247 {
2248 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)zobj;
2249 
2250 	return zend_ffi_get_class_name(zobj->ce->name, ZEND_FFI_TYPE(ctype->type));
2251 }
2252 /* }}} */
2253 
zend_ffi_ctype_compare_objects(zval * o1,zval * o2)2254 static int zend_ffi_ctype_compare_objects(zval *o1, zval *o2) /* {{{ */
2255 {
2256 	if (Z_TYPE_P(o1) == IS_OBJECT && Z_OBJCE_P(o1) == zend_ffi_ctype_ce &&
2257 	    Z_TYPE_P(o2) == IS_OBJECT && Z_OBJCE_P(o2) == zend_ffi_ctype_ce) {
2258 		zend_ffi_ctype *ctype1 = (zend_ffi_ctype*)Z_OBJ_P(o1);
2259 		zend_ffi_ctype *ctype2 = (zend_ffi_ctype*)Z_OBJ_P(o2);
2260 		zend_ffi_type *type1 = ZEND_FFI_TYPE(ctype1->type);
2261 		zend_ffi_type *type2 = ZEND_FFI_TYPE(ctype2->type);
2262 
2263 		if (zend_ffi_is_same_type(type1, type2)) {
2264 			return 0;
2265 		} else {
2266 			return 1;
2267 		}
2268 	}
2269 	zend_throw_error(zend_ffi_exception_ce, "Comparison of incompatible C types");
2270 	return 0;
2271 }
2272 /* }}} */
2273 
zend_ffi_ctype_get_debug_info(zend_object * obj,int * is_temp)2274 static HashTable *zend_ffi_ctype_get_debug_info(zend_object *obj, int *is_temp) /* {{{ */
2275 {
2276 	return NULL;
2277 }
2278 /* }}} */
2279 
zend_ffi_new(zend_class_entry * class_type)2280 static zend_object *zend_ffi_new(zend_class_entry *class_type) /* {{{ */
2281 {
2282 	zend_ffi *ffi;
2283 
2284 	ffi = emalloc(sizeof(zend_ffi));
2285 
2286 	zend_ffi_object_init(&ffi->std, class_type);
2287 
2288 	ffi->lib = NULL;
2289 	ffi->symbols = NULL;
2290 	ffi->tags = NULL;
2291 	ffi->persistent = 0;
2292 
2293 	return &ffi->std;
2294 }
2295 /* }}} */
2296 
_zend_ffi_type_dtor(zend_ffi_type * type)2297 static void _zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */
2298 {
2299 	type = ZEND_FFI_TYPE(type);
2300 
2301 	switch (type->kind) {
2302 		case ZEND_FFI_TYPE_ENUM:
2303 			if (type->enumeration.tag_name) {
2304 				zend_string_release(type->enumeration.tag_name);
2305 			}
2306 			break;
2307 		case ZEND_FFI_TYPE_STRUCT:
2308 			if (type->record.tag_name) {
2309 				zend_string_release(type->record.tag_name);
2310 			}
2311 			zend_hash_destroy(&type->record.fields);
2312 			break;
2313 		case ZEND_FFI_TYPE_POINTER:
2314 			zend_ffi_type_dtor(type->pointer.type);
2315 			break;
2316 		case ZEND_FFI_TYPE_ARRAY:
2317 			zend_ffi_type_dtor(type->array.type);
2318 			break;
2319 		case ZEND_FFI_TYPE_FUNC:
2320 			if (type->func.args) {
2321 				zend_hash_destroy(type->func.args);
2322 				pefree(type->func.args, type->attr & ZEND_FFI_ATTR_PERSISTENT);
2323 			}
2324 			zend_ffi_type_dtor(type->func.ret_type);
2325 			break;
2326 		default:
2327 			break;
2328 	}
2329 	pefree(type, type->attr & ZEND_FFI_ATTR_PERSISTENT);
2330 }
2331 /* }}} */
2332 
zend_ffi_type_hash_dtor(zval * zv)2333 static void zend_ffi_type_hash_dtor(zval *zv) /* {{{ */
2334 {
2335 	zend_ffi_type *type = Z_PTR_P(zv);
2336 	zend_ffi_type_dtor(type);
2337 }
2338 /* }}} */
2339 
zend_ffi_field_hash_dtor(zval * zv)2340 static void zend_ffi_field_hash_dtor(zval *zv) /* {{{ */
2341 {
2342 	zend_ffi_field *field = Z_PTR_P(zv);
2343 	zend_ffi_type_dtor(field->type);
2344 	efree(field);
2345 }
2346 /* }}} */
2347 
zend_ffi_field_hash_persistent_dtor(zval * zv)2348 static void zend_ffi_field_hash_persistent_dtor(zval *zv) /* {{{ */
2349 {
2350 	zend_ffi_field *field = Z_PTR_P(zv);
2351 	zend_ffi_type_dtor(field->type);
2352 	free(field);
2353 }
2354 /* }}} */
2355 
zend_ffi_symbol_hash_dtor(zval * zv)2356 static void zend_ffi_symbol_hash_dtor(zval *zv) /* {{{ */
2357 {
2358 	zend_ffi_symbol *sym = Z_PTR_P(zv);
2359 	zend_ffi_type_dtor(sym->type);
2360 	efree(sym);
2361 }
2362 /* }}} */
2363 
zend_ffi_symbol_hash_persistent_dtor(zval * zv)2364 static void zend_ffi_symbol_hash_persistent_dtor(zval *zv) /* {{{ */
2365 {
2366 	zend_ffi_symbol *sym = Z_PTR_P(zv);
2367 	zend_ffi_type_dtor(sym->type);
2368 	free(sym);
2369 }
2370 /* }}} */
2371 
zend_ffi_tag_hash_dtor(zval * zv)2372 static void zend_ffi_tag_hash_dtor(zval *zv) /* {{{ */
2373 {
2374 	zend_ffi_tag *tag = Z_PTR_P(zv);
2375 	zend_ffi_type_dtor(tag->type);
2376 	efree(tag);
2377 }
2378 /* }}} */
2379 
zend_ffi_tag_hash_persistent_dtor(zval * zv)2380 static void zend_ffi_tag_hash_persistent_dtor(zval *zv) /* {{{ */
2381 {
2382 	zend_ffi_tag *tag = Z_PTR_P(zv);
2383 	zend_ffi_type_dtor(tag->type);
2384 	free(tag);
2385 }
2386 /* }}} */
2387 
zend_ffi_cdata_dtor(zend_ffi_cdata * cdata)2388 static void zend_ffi_cdata_dtor(zend_ffi_cdata *cdata) /* {{{ */
2389 {
2390 	zend_ffi_type_dtor(cdata->type);
2391 	if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
2392 		if (cdata->ptr != (void*)&cdata->ptr_holder) {
2393 			pefree(cdata->ptr, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
2394 		} else {
2395 			pefree(cdata->ptr_holder, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
2396 		}
2397 	}
2398 }
2399 /* }}} */
2400 
zend_ffi_scope_hash_dtor(zval * zv)2401 static void zend_ffi_scope_hash_dtor(zval *zv) /* {{{ */
2402 {
2403 	zend_ffi_scope *scope = Z_PTR_P(zv);
2404 	if (scope->symbols) {
2405 		zend_hash_destroy(scope->symbols);
2406 		free(scope->symbols);
2407 	}
2408 	if (scope->tags) {
2409 		zend_hash_destroy(scope->tags);
2410 		free(scope->tags);
2411 	}
2412 	free(scope);
2413 }
2414 /* }}} */
2415 
zend_ffi_free_obj(zend_object * object)2416 static void zend_ffi_free_obj(zend_object *object) /* {{{ */
2417 {
2418 	zend_ffi *ffi = (zend_ffi*)object;
2419 
2420 	if (ffi->persistent) {
2421 		return;
2422 	}
2423 
2424 	if (ffi->lib) {
2425 		DL_UNLOAD(ffi->lib);
2426 		ffi->lib = NULL;
2427 	}
2428 
2429 	if (ffi->symbols) {
2430 		zend_hash_destroy(ffi->symbols);
2431 		efree(ffi->symbols);
2432 	}
2433 
2434 	if (ffi->tags) {
2435 		zend_hash_destroy(ffi->tags);
2436 		efree(ffi->tags);
2437 	}
2438 
2439     if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) {
2440         zend_weakrefs_notify(object);
2441     }
2442 }
2443 /* }}} */
2444 
zend_ffi_cdata_free_obj(zend_object * object)2445 static void zend_ffi_cdata_free_obj(zend_object *object) /* {{{ */
2446 {
2447 	zend_ffi_cdata *cdata = (zend_ffi_cdata*)object;
2448 
2449 	zend_ffi_cdata_dtor(cdata);
2450 
2451     if (UNEXPECTED(GC_FLAGS(object) & IS_OBJ_WEAKLY_REFERENCED)) {
2452         zend_weakrefs_notify(object);
2453     }
2454 }
2455 /* }}} */
2456 
zend_ffi_cdata_clone_obj(zend_object * obj)2457 static zend_object *zend_ffi_cdata_clone_obj(zend_object *obj) /* {{{ */
2458 {
2459 	zend_ffi_cdata *old_cdata = (zend_ffi_cdata*)obj;
2460 	zend_ffi_type *type = ZEND_FFI_TYPE(old_cdata->type);
2461 	zend_ffi_cdata *new_cdata;
2462 
2463 	new_cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
2464 	if (type->kind < ZEND_FFI_TYPE_POINTER) {
2465 		new_cdata->std.handlers = &zend_ffi_cdata_value_handlers;
2466 	}
2467 	new_cdata->type = type;
2468 	new_cdata->ptr = emalloc(type->size);
2469 	memcpy(new_cdata->ptr, old_cdata->ptr, type->size);
2470 	new_cdata->flags |= ZEND_FFI_FLAG_OWNED;
2471 
2472 	return &new_cdata->std;
2473 }
2474 /* }}} */
2475 
zend_ffi_read_var(zend_object * obj,zend_string * var_name,int read_type,void ** cache_slot,zval * rv)2476 static zval *zend_ffi_read_var(zend_object *obj, zend_string *var_name, int read_type, void **cache_slot, zval *rv) /* {{{ */
2477 {
2478 	zend_ffi        *ffi = (zend_ffi*)obj;
2479 	zend_ffi_symbol *sym = NULL;
2480 
2481 	if (ffi->symbols) {
2482 		sym = zend_hash_find_ptr(ffi->symbols, var_name);
2483 		if (sym && sym->kind != ZEND_FFI_SYM_VAR && sym->kind != ZEND_FFI_SYM_CONST && sym->kind != ZEND_FFI_SYM_FUNC) {
2484 			sym = NULL;
2485 		}
2486 	}
2487 	if (!sym) {
2488 		zend_throw_error(zend_ffi_exception_ce, "Attempt to read undefined C variable '%s'", ZSTR_VAL(var_name));
2489 		return &EG(uninitialized_zval);
2490 	}
2491 
2492 	if (sym->kind == ZEND_FFI_SYM_VAR) {
2493 		zend_ffi_cdata_to_zval(NULL, sym->addr, ZEND_FFI_TYPE(sym->type), read_type, rv, (zend_ffi_flags)sym->is_const, 0, 0);
2494 	} else if (sym->kind == ZEND_FFI_SYM_FUNC) {
2495 		zend_ffi_cdata *cdata;
2496 		zend_ffi_type *new_type = emalloc(sizeof(zend_ffi_type));
2497 
2498 		new_type->kind = ZEND_FFI_TYPE_POINTER;
2499 		new_type->attr = 0;
2500 		new_type->size = sizeof(void*);
2501 		new_type->align = _Alignof(void*);
2502 		new_type->pointer.type = ZEND_FFI_TYPE(sym->type);
2503 
2504 		cdata = emalloc(sizeof(zend_ffi_cdata));
2505 		zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
2506 		cdata->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
2507 		cdata->flags = ZEND_FFI_FLAG_CONST;
2508 		cdata->ptr_holder = sym->addr;
2509 		cdata->ptr = &cdata->ptr_holder;
2510 		ZVAL_OBJ(rv, &cdata->std);
2511 	} else {
2512 		ZVAL_LONG(rv, sym->value);
2513 	}
2514 
2515 	return rv;
2516 }
2517 /* }}} */
2518 
zend_ffi_write_var(zend_object * obj,zend_string * var_name,zval * value,void ** cache_slot)2519 static zval *zend_ffi_write_var(zend_object *obj, zend_string *var_name, zval *value, void **cache_slot) /* {{{ */
2520 {
2521 	zend_ffi        *ffi = (zend_ffi*)obj;
2522 	zend_ffi_symbol *sym = NULL;
2523 
2524 	if (ffi->symbols) {
2525 		sym = zend_hash_find_ptr(ffi->symbols, var_name);
2526 		if (sym && sym->kind != ZEND_FFI_SYM_VAR) {
2527 			sym = NULL;
2528 		}
2529 	}
2530 	if (!sym) {
2531 		zend_throw_error(zend_ffi_exception_ce, "Attempt to assign undefined C variable '%s'", ZSTR_VAL(var_name));
2532 		return value;
2533 	}
2534 
2535 	if (sym->is_const) {
2536 		zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only C variable '%s'", ZSTR_VAL(var_name));
2537 		return value;
2538 	}
2539 
2540 	zend_ffi_zval_to_cdata(sym->addr, ZEND_FFI_TYPE(sym->type), value);
2541 	return value;
2542 }
2543 /* }}} */
2544 
zend_ffi_pass_arg(zval * arg,zend_ffi_type * type,ffi_type ** pass_type,void ** arg_values,uint32_t n,zend_execute_data * execute_data)2545 static zend_result zend_ffi_pass_arg(zval *arg, zend_ffi_type *type, ffi_type **pass_type, void **arg_values, uint32_t n, zend_execute_data *execute_data) /* {{{ */
2546 {
2547 	zend_long lval;
2548 	double dval;
2549 	zend_string *str, *tmp_str;
2550 	zend_ffi_type_kind kind = type->kind;
2551 
2552 	ZVAL_DEREF(arg);
2553 
2554 again:
2555 	switch (kind) {
2556 		case ZEND_FFI_TYPE_FLOAT:
2557 			dval = zval_get_double(arg);
2558 			*pass_type = &ffi_type_float;
2559 			*(float*)arg_values[n] = (float)dval;
2560 			break;
2561 		case ZEND_FFI_TYPE_DOUBLE:
2562 			dval = zval_get_double(arg);
2563 			*pass_type = &ffi_type_double;
2564 			*(double*)arg_values[n] = dval;
2565 			break;
2566 #ifdef HAVE_LONG_DOUBLE
2567 		case ZEND_FFI_TYPE_LONGDOUBLE:
2568 			dval = zval_get_double(arg);
2569 			*pass_type = &ffi_type_double;
2570 			*(long double*)arg_values[n] = (long double)dval;
2571 			break;
2572 #endif
2573 		case ZEND_FFI_TYPE_UINT8:
2574 			lval = zval_get_long(arg);
2575 			*pass_type = &ffi_type_uint8;
2576 			*(uint8_t*)arg_values[n] = (uint8_t)lval;
2577 			break;
2578 		case ZEND_FFI_TYPE_SINT8:
2579 			lval = zval_get_long(arg);
2580 			*pass_type = &ffi_type_sint8;
2581 			*(int8_t*)arg_values[n] = (int8_t)lval;
2582 			break;
2583 		case ZEND_FFI_TYPE_UINT16:
2584 			lval = zval_get_long(arg);
2585 			*pass_type = &ffi_type_uint16;
2586 			*(uint16_t*)arg_values[n] = (uint16_t)lval;
2587 			break;
2588 		case ZEND_FFI_TYPE_SINT16:
2589 			lval = zval_get_long(arg);
2590 			*pass_type = &ffi_type_sint16;
2591 			*(int16_t*)arg_values[n] = (int16_t)lval;
2592 			break;
2593 		case ZEND_FFI_TYPE_UINT32:
2594 			lval = zval_get_long(arg);
2595 			*pass_type = &ffi_type_uint32;
2596 			*(uint32_t*)arg_values[n] = (uint32_t)lval;
2597 			break;
2598 		case ZEND_FFI_TYPE_SINT32:
2599 			lval = zval_get_long(arg);
2600 			*pass_type = &ffi_type_sint32;
2601 			*(int32_t*)arg_values[n] = (int32_t)lval;
2602 			break;
2603 		case ZEND_FFI_TYPE_UINT64:
2604 			lval = zval_get_long(arg);
2605 			*pass_type = &ffi_type_uint64;
2606 			*(uint64_t*)arg_values[n] = (uint64_t)lval;
2607 			break;
2608 		case ZEND_FFI_TYPE_SINT64:
2609 			lval = zval_get_long(arg);
2610 			*pass_type = &ffi_type_sint64;
2611 			*(int64_t*)arg_values[n] = (int64_t)lval;
2612 			break;
2613 		case ZEND_FFI_TYPE_POINTER:
2614 			*pass_type = &ffi_type_pointer;
2615 			if (Z_TYPE_P(arg) == IS_NULL) {
2616 				*(void**)arg_values[n] = NULL;
2617 				return SUCCESS;
2618 			} else if (Z_TYPE_P(arg) == IS_STRING
2619 			        && ((ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR)
2620 			         || (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_VOID))) {
2621 				*(void**)arg_values[n] = Z_STRVAL_P(arg);
2622 				return SUCCESS;
2623 			} else if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
2624 				zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
2625 
2626 				if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) {
2627 					if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
2628 						if (!cdata->ptr) {
2629 							zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
2630 							return FAILURE;
2631 						}
2632 						*(void**)arg_values[n] = *(void**)cdata->ptr;
2633 					} else {
2634 						*(void**)arg_values[n] = cdata->ptr;
2635 					}
2636 					return SUCCESS;
2637 				}
2638 #if FFI_CLOSURES
2639 			} else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_FUNC) {
2640 				void *callback = zend_ffi_create_callback(ZEND_FFI_TYPE(type->pointer.type), arg);
2641 
2642 				if (callback) {
2643 					*(void**)arg_values[n] = callback;
2644 					break;
2645 				} else {
2646 					return FAILURE;
2647 				}
2648 #endif
2649 			}
2650 			zend_ffi_pass_incompatible(arg, type, n, execute_data);
2651 			return FAILURE;
2652 		case ZEND_FFI_TYPE_BOOL:
2653 			*pass_type = &ffi_type_uint8;
2654 			*(uint8_t*)arg_values[n] = zend_is_true(arg);
2655 			break;
2656 		case ZEND_FFI_TYPE_CHAR:
2657 			str = zval_get_tmp_string(arg, &tmp_str);
2658 			*pass_type = &ffi_type_sint8;
2659 			*(char*)arg_values[n] = ZSTR_VAL(str)[0];
2660 			if (ZSTR_LEN(str) != 1) {
2661 				zend_ffi_pass_incompatible(arg, type, n, execute_data);
2662 			}
2663 			zend_tmp_string_release(tmp_str);
2664 			break;
2665 		case ZEND_FFI_TYPE_ENUM:
2666 			kind = type->enumeration.kind;
2667 			goto again;
2668 		case ZEND_FFI_TYPE_STRUCT:
2669 			if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
2670 				zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
2671 
2672 				if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) {
2673 					*pass_type = zend_ffi_make_fake_struct_type(type);
2674 					arg_values[n] = cdata->ptr;
2675 					break;
2676 				}
2677 			}
2678 			zend_ffi_pass_incompatible(arg, type, n, execute_data);
2679 			return FAILURE;
2680 		default:
2681 			zend_ffi_pass_unsupported(type);
2682 			return FAILURE;
2683 	}
2684 	return SUCCESS;
2685 }
2686 /* }}} */
2687 
zend_ffi_pass_var_arg(zval * arg,ffi_type ** pass_type,void ** arg_values,uint32_t n,zend_execute_data * execute_data)2688 static zend_result zend_ffi_pass_var_arg(zval *arg, ffi_type **pass_type, void **arg_values, uint32_t n, zend_execute_data *execute_data) /* {{{ */
2689 {
2690 	ZVAL_DEREF(arg);
2691 	switch (Z_TYPE_P(arg)) {
2692 		case IS_NULL:
2693 			*pass_type = &ffi_type_pointer;
2694 			*(void**)arg_values[n] = NULL;
2695 			break;
2696 		case IS_FALSE:
2697 			*pass_type = &ffi_type_uint8;
2698 			*(uint8_t*)arg_values[n] = 0;
2699 			break;
2700 		case IS_TRUE:
2701 			*pass_type = &ffi_type_uint8;
2702 			*(uint8_t*)arg_values[n] = 1;
2703 			break;
2704 		case IS_LONG:
2705 			if (sizeof(zend_long) == 4) {
2706 				*pass_type = &ffi_type_sint32;
2707 				*(int32_t*)arg_values[n] = Z_LVAL_P(arg);
2708 			} else {
2709 				*pass_type = &ffi_type_sint64;
2710 				*(int64_t*)arg_values[n] = Z_LVAL_P(arg);
2711 			}
2712 			break;
2713 		case IS_DOUBLE:
2714 			*pass_type = &ffi_type_double;
2715 			*(double*)arg_values[n] = Z_DVAL_P(arg);
2716 			break;
2717 		case IS_STRING:
2718 			*pass_type = &ffi_type_pointer;
2719 			*(char**)arg_values[n] = Z_STRVAL_P(arg);
2720 			break;
2721 		case IS_OBJECT:
2722 			if (Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
2723 				zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
2724 				zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
2725 
2726 				return zend_ffi_pass_arg(arg, type, pass_type, arg_values, n, execute_data);
2727 			}
2728 			ZEND_FALLTHROUGH;
2729 		default:
2730 			zend_throw_error(zend_ffi_exception_ce, "Unsupported argument type");
2731 			return FAILURE;
2732 	}
2733 	return SUCCESS;
2734 }
2735 /* }}} */
2736 
ZEND_FUNCTION(ffi_trampoline)2737 static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
2738 {
2739 	zend_ffi_type *type = EX(func)->internal_function.reserved[0];
2740 	void *addr = EX(func)->internal_function.reserved[1];
2741 	ffi_cif cif;
2742 	ffi_type *ret_type = NULL;
2743 	ffi_type **arg_types = NULL;
2744 	void **arg_values = NULL;
2745 	uint32_t n, arg_count;
2746 	void *ret;
2747 	zend_ffi_type *arg_type;
2748 	ALLOCA_FLAG(arg_types_use_heap = 0)
2749 	ALLOCA_FLAG(arg_values_use_heap = 0)
2750 	ALLOCA_FLAG(ret_use_heap = 0)
2751 
2752 	ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC);
2753 	arg_count = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
2754 	if (type->attr & ZEND_FFI_ATTR_VARIADIC) {
2755 		if (arg_count > EX_NUM_ARGS()) {
2756 			zend_throw_error(zend_ffi_exception_ce, "Incorrect number of arguments for C function '%s', expecting at least %d parameter%s", ZSTR_VAL(EX(func)->internal_function.function_name), arg_count, (arg_count != 1) ? "s" : "");
2757 			goto exit;
2758 		}
2759 		if (EX_NUM_ARGS()) {
2760 			arg_types = do_alloca(
2761 				sizeof(ffi_type*) * EX_NUM_ARGS(), arg_types_use_heap);
2762 			arg_values = do_alloca(
2763 				(sizeof(void*) + ZEND_FFI_SIZEOF_ARG) * EX_NUM_ARGS(), arg_values_use_heap);
2764 			n = 0;
2765 			if (type->func.args) {
2766 				ZEND_HASH_PACKED_FOREACH_PTR(type->func.args, arg_type) {
2767 					arg_type = ZEND_FFI_TYPE(arg_type);
2768 					arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (ZEND_FFI_SIZEOF_ARG * n);
2769 					if (zend_ffi_pass_arg(EX_VAR_NUM(n), arg_type, &arg_types[n], arg_values, n, execute_data) == FAILURE) {
2770 						free_alloca(arg_types, arg_types_use_heap);
2771 						free_alloca(arg_values, arg_values_use_heap);
2772 						goto exit;
2773 					}
2774 					n++;
2775 				} ZEND_HASH_FOREACH_END();
2776 			}
2777 			for (; n < EX_NUM_ARGS(); n++) {
2778 				arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (ZEND_FFI_SIZEOF_ARG * n);
2779 				if (zend_ffi_pass_var_arg(EX_VAR_NUM(n), &arg_types[n], arg_values, n, execute_data) == FAILURE) {
2780 					free_alloca(arg_types, arg_types_use_heap);
2781 					free_alloca(arg_values, arg_values_use_heap);
2782 					goto exit;
2783 				}
2784 			}
2785 		}
2786 		ret_type = zend_ffi_get_type(ZEND_FFI_TYPE(type->func.ret_type));
2787 		if (!ret_type) {
2788 			zend_ffi_return_unsupported(type->func.ret_type);
2789 			free_alloca(arg_types, arg_types_use_heap);
2790 			free_alloca(arg_values, arg_values_use_heap);
2791 			goto exit;
2792 		}
2793 		if (ffi_prep_cif_var(&cif, type->func.abi, arg_count, EX_NUM_ARGS(), ret_type, arg_types) != FFI_OK) {
2794 			zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF");
2795 			free_alloca(arg_types, arg_types_use_heap);
2796 			free_alloca(arg_values, arg_values_use_heap);
2797 			goto exit;
2798 		}
2799 	} else {
2800 		if (arg_count != EX_NUM_ARGS()) {
2801 			zend_throw_error(zend_ffi_exception_ce, "Incorrect number of arguments for C function '%s', expecting exactly %d parameter%s", ZSTR_VAL(EX(func)->internal_function.function_name), arg_count, (arg_count != 1) ? "s" : "");
2802 			goto exit;
2803 		}
2804 		if (EX_NUM_ARGS()) {
2805 			arg_types = do_alloca(
2806 				(sizeof(ffi_type*) + sizeof(ffi_type)) * EX_NUM_ARGS(), arg_types_use_heap);
2807 			arg_values = do_alloca(
2808 				(sizeof(void*) + ZEND_FFI_SIZEOF_ARG) * EX_NUM_ARGS(), arg_values_use_heap);
2809 			n = 0;
2810 			if (type->func.args) {
2811 				ZEND_HASH_PACKED_FOREACH_PTR(type->func.args, arg_type) {
2812 					arg_type = ZEND_FFI_TYPE(arg_type);
2813 					arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (ZEND_FFI_SIZEOF_ARG * n);
2814 					if (zend_ffi_pass_arg(EX_VAR_NUM(n), arg_type, &arg_types[n], arg_values, n, execute_data) == FAILURE) {
2815 						free_alloca(arg_types, arg_types_use_heap);
2816 						free_alloca(arg_values, arg_values_use_heap);
2817 						goto exit;
2818 					}
2819 					n++;
2820 				} ZEND_HASH_FOREACH_END();
2821 			}
2822 		}
2823 		ret_type = zend_ffi_get_type(ZEND_FFI_TYPE(type->func.ret_type));
2824 		if (!ret_type) {
2825 			zend_ffi_return_unsupported(type->func.ret_type);
2826 			free_alloca(arg_types, arg_types_use_heap);
2827 			free_alloca(arg_values, arg_values_use_heap);
2828 			goto exit;
2829 		}
2830 		if (ffi_prep_cif(&cif, type->func.abi, arg_count, ret_type, arg_types) != FFI_OK) {
2831 			zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF");
2832 			free_alloca(arg_types, arg_types_use_heap);
2833 			free_alloca(arg_values, arg_values_use_heap);
2834 			goto exit;
2835 		}
2836 	}
2837 
2838 	ret = do_alloca(MAX(ret_type->size, sizeof(ffi_arg)), ret_use_heap);
2839 	ffi_call(&cif, addr, ret, arg_values);
2840 
2841 	for (n = 0; n < arg_count; n++) {
2842 		if (arg_types[n]->type == FFI_TYPE_STRUCT) {
2843 			efree(arg_types[n]);
2844 		}
2845 	}
2846 	if (ret_type->type == FFI_TYPE_STRUCT) {
2847 		efree(ret_type);
2848 	}
2849 
2850 	if (EX_NUM_ARGS()) {
2851 		free_alloca(arg_types, arg_types_use_heap);
2852 		free_alloca(arg_values, arg_values_use_heap);
2853 	}
2854 
2855 	if (ZEND_FFI_TYPE(type->func.ret_type)->kind != ZEND_FFI_TYPE_VOID) {
2856 		zend_ffi_cdata_to_zval(NULL, ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1, 0);
2857 	} else {
2858 		ZVAL_NULL(return_value);
2859 	}
2860 	free_alloca(ret, ret_use_heap);
2861 
2862 exit:
2863 	zend_string_release(EX(func)->common.function_name);
2864 	if (EX(func)->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
2865 		zend_free_trampoline(EX(func));
2866 		EX(func) = NULL;
2867 	}
2868 }
2869 /* }}} */
2870 
zend_ffi_get_func(zend_object ** obj,zend_string * name,const zval * key)2871 static zend_function *zend_ffi_get_func(zend_object **obj, zend_string *name, const zval *key) /* {{{ */
2872 {
2873 	zend_ffi        *ffi = (zend_ffi*)*obj;
2874 	zend_ffi_symbol *sym = NULL;
2875 	zend_function   *func;
2876 	zend_ffi_type   *type;
2877 
2878 	if (ZSTR_LEN(name) == sizeof("new") -1
2879 	 && (ZSTR_VAL(name)[0] == 'n' || ZSTR_VAL(name)[0] == 'N')
2880 	 && (ZSTR_VAL(name)[1] == 'e' || ZSTR_VAL(name)[1] == 'E')
2881 	 && (ZSTR_VAL(name)[2] == 'w' || ZSTR_VAL(name)[2] == 'W')) {
2882 		return (zend_function*)&zend_ffi_new_fn;
2883 	} else if (ZSTR_LEN(name) == sizeof("cast") -1
2884 	 && (ZSTR_VAL(name)[0] == 'c' || ZSTR_VAL(name)[0] == 'C')
2885 	 && (ZSTR_VAL(name)[1] == 'a' || ZSTR_VAL(name)[1] == 'A')
2886 	 && (ZSTR_VAL(name)[2] == 's' || ZSTR_VAL(name)[2] == 'S')
2887 	 && (ZSTR_VAL(name)[3] == 't' || ZSTR_VAL(name)[3] == 'T')) {
2888 		return (zend_function*)&zend_ffi_cast_fn;
2889 	} else if (ZSTR_LEN(name) == sizeof("type") -1
2890 	 && (ZSTR_VAL(name)[0] == 't' || ZSTR_VAL(name)[0] == 'T')
2891 	 && (ZSTR_VAL(name)[1] == 'y' || ZSTR_VAL(name)[1] == 'Y')
2892 	 && (ZSTR_VAL(name)[2] == 'p' || ZSTR_VAL(name)[2] == 'P')
2893 	 && (ZSTR_VAL(name)[3] == 'e' || ZSTR_VAL(name)[3] == 'E')) {
2894 		return (zend_function*)&zend_ffi_type_fn;
2895 	}
2896 
2897 	if (ffi->symbols) {
2898 		sym = zend_hash_find_ptr(ffi->symbols, name);
2899 		if (sym && sym->kind != ZEND_FFI_SYM_FUNC) {
2900 			sym = NULL;
2901 		}
2902 	}
2903 	if (!sym) {
2904 		zend_throw_error(zend_ffi_exception_ce, "Attempt to call undefined C function '%s'", ZSTR_VAL(name));
2905 		return NULL;
2906 	}
2907 
2908 	type = ZEND_FFI_TYPE(sym->type);
2909 	ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC);
2910 
2911 	if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
2912 		func = &EG(trampoline);
2913 	} else {
2914 		func = ecalloc(sizeof(zend_internal_function), 1);
2915 	}
2916 	func->common.type = ZEND_INTERNAL_FUNCTION;
2917 	func->common.arg_flags[0] = 0;
2918 	func->common.arg_flags[1] = 0;
2919 	func->common.arg_flags[2] = 0;
2920 	func->common.fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE;
2921 	func->common.function_name = zend_string_copy(name);
2922 	/* set to 0 to avoid arg_info[] allocation, because all values are passed by value anyway */
2923 	func->common.num_args = 0;
2924 	func->common.required_num_args = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
2925 	func->common.scope = NULL;
2926 	func->common.prototype = NULL;
2927 	func->common.arg_info = NULL;
2928 	func->internal_function.handler = ZEND_FN(ffi_trampoline);
2929 	func->internal_function.module = NULL;
2930 
2931 	func->internal_function.reserved[0] = type;
2932 	func->internal_function.reserved[1] = sym->addr;
2933 
2934 	return func;
2935 }
2936 /* }}} */
2937 
zend_ffi_disabled(void)2938 static zend_never_inline int zend_ffi_disabled(void) /* {{{ */
2939 {
2940 	zend_throw_error(zend_ffi_exception_ce, "FFI API is restricted by \"ffi.enable\" configuration directive");
2941 	return 0;
2942 }
2943 /* }}} */
2944 
zend_ffi_validate_api_restriction(zend_execute_data * execute_data)2945 static zend_always_inline bool zend_ffi_validate_api_restriction(zend_execute_data *execute_data) /* {{{ */
2946 {
2947 	if (EXPECTED(FFI_G(restriction) > ZEND_FFI_ENABLED)) {
2948 		ZEND_ASSERT(FFI_G(restriction) == ZEND_FFI_PRELOAD);
2949 		if (FFI_G(is_cli)
2950 		 || (execute_data->prev_execute_data
2951 		  && (execute_data->prev_execute_data->func->common.fn_flags & ZEND_ACC_PRELOADED))
2952 		 || (CG(compiler_options) & ZEND_COMPILE_PRELOAD)) {
2953 			return 1;
2954 		}
2955 	} else if (EXPECTED(FFI_G(restriction) == ZEND_FFI_ENABLED)) {
2956 		return 1;
2957 	}
2958 	return zend_ffi_disabled();
2959 }
2960 /* }}} */
2961 
2962 #define ZEND_FFI_VALIDATE_API_RESTRICTION() do { \
2963 		if (UNEXPECTED(!zend_ffi_validate_api_restriction(execute_data))) { \
2964 			RETURN_THROWS(); \
2965 		} \
2966 	} while (0)
2967 
ZEND_METHOD(FFI,cdef)2968 ZEND_METHOD(FFI, cdef) /* {{{ */
2969 {
2970 	zend_string *code = NULL;
2971 	zend_string *lib = NULL;
2972 	zend_ffi *ffi = NULL;
2973 	DL_HANDLE handle = NULL;
2974 	char *err;
2975 	void *addr;
2976 
2977 	ZEND_FFI_VALIDATE_API_RESTRICTION();
2978 	ZEND_PARSE_PARAMETERS_START(0, 2)
2979 		Z_PARAM_OPTIONAL
2980 		Z_PARAM_STR(code)
2981 		Z_PARAM_STR_OR_NULL(lib)
2982 	ZEND_PARSE_PARAMETERS_END();
2983 
2984 	if (lib) {
2985 		handle = DL_LOAD(ZSTR_VAL(lib));
2986 		if (!handle) {
2987 			err = GET_DL_ERROR();
2988 #ifdef PHP_WIN32
2989 			if (err && err[0]) {
2990 				zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s' (%s)", ZSTR_VAL(lib), err);
2991 				php_win32_error_msg_free(err);
2992 			} else {
2993 				zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s' (Unknown reason)", ZSTR_VAL(lib));
2994 			}
2995 #else
2996 			zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s' (%s)", ZSTR_VAL(lib), err);
2997 			GET_DL_ERROR(); /* free the buffer storing the error */
2998 #endif
2999 			RETURN_THROWS();
3000 		}
3001 
3002 #ifdef RTLD_DEFAULT
3003 	} else if (1) {
3004 		// TODO: this might need to be disabled or protected ???
3005 		handle = RTLD_DEFAULT;
3006 #endif
3007 	}
3008 
3009 	FFI_G(symbols) = NULL;
3010 	FFI_G(tags) = NULL;
3011 
3012 	if (code && ZSTR_LEN(code)) {
3013 		/* Parse C definitions */
3014 		FFI_G(default_type_attr) = ZEND_FFI_ATTR_STORED;
3015 
3016 		if (zend_ffi_parse_decl(ZSTR_VAL(code), ZSTR_LEN(code)) == FAILURE) {
3017 			if (FFI_G(symbols)) {
3018 				zend_hash_destroy(FFI_G(symbols));
3019 				efree(FFI_G(symbols));
3020 				FFI_G(symbols) = NULL;
3021 			}
3022 			if (FFI_G(tags)) {
3023 				zend_hash_destroy(FFI_G(tags));
3024 				efree(FFI_G(tags));
3025 				FFI_G(tags) = NULL;
3026 			}
3027 			RETURN_THROWS();
3028 		}
3029 
3030 		if (FFI_G(symbols)) {
3031 			zend_string *name;
3032 			zend_ffi_symbol *sym;
3033 
3034 			ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(FFI_G(symbols), name, sym) {
3035 				if (sym->kind == ZEND_FFI_SYM_VAR) {
3036 					addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name));
3037 					if (!addr) {
3038 						zend_throw_error(zend_ffi_exception_ce, "Failed resolving C variable '%s'", ZSTR_VAL(name));
3039 						RETURN_THROWS();
3040 					}
3041 					sym->addr = addr;
3042 				} else if (sym->kind == ZEND_FFI_SYM_FUNC) {
3043 					zend_string *mangled_name = zend_ffi_mangled_func_name(name, ZEND_FFI_TYPE(sym->type));
3044 
3045 					addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(mangled_name));
3046 					zend_string_release(mangled_name);
3047 					if (!addr) {
3048 						zend_throw_error(zend_ffi_exception_ce, "Failed resolving C function '%s'", ZSTR_VAL(name));
3049 						RETURN_THROWS();
3050 					}
3051 					sym->addr = addr;
3052 				}
3053 			} ZEND_HASH_FOREACH_END();
3054 		}
3055 	}
3056 
3057 	ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
3058 	ffi->lib = handle;
3059 	ffi->symbols = FFI_G(symbols);
3060 	ffi->tags = FFI_G(tags);
3061 
3062 	FFI_G(symbols) = NULL;
3063 	FFI_G(tags) = NULL;
3064 
3065 	RETURN_OBJ(&ffi->std);
3066 }
3067 /* }}} */
3068 
zend_ffi_same_types(zend_ffi_type * old,zend_ffi_type * type)3069 static bool zend_ffi_same_types(zend_ffi_type *old, zend_ffi_type *type) /* {{{ */
3070 {
3071 	if (old == type) {
3072 		return 1;
3073 	}
3074 
3075 	if (old->kind != type->kind
3076 	 || old->size != type->size
3077 	 || old->align != type->align
3078 	 || old->attr != type->attr) {
3079 		return 0;
3080 	}
3081 
3082 	switch (old->kind) {
3083 		case ZEND_FFI_TYPE_ENUM:
3084 			return old->enumeration.kind == type->enumeration.kind;
3085 		case ZEND_FFI_TYPE_ARRAY:
3086 			return old->array.length == type->array.length
3087 			 &&	zend_ffi_same_types(ZEND_FFI_TYPE(old->array.type), ZEND_FFI_TYPE(type->array.type));
3088 		case ZEND_FFI_TYPE_POINTER:
3089 			return zend_ffi_same_types(ZEND_FFI_TYPE(old->pointer.type), ZEND_FFI_TYPE(type->pointer.type));
3090 		case ZEND_FFI_TYPE_STRUCT:
3091 			if (zend_hash_num_elements(&old->record.fields) != zend_hash_num_elements(&type->record.fields)) {
3092 				return 0;
3093 			} else {
3094 				zend_ffi_field *old_field, *field;
3095 				zend_string *key;
3096 				Bucket *b = type->record.fields.arData;
3097 
3098 				ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&old->record.fields, key, old_field) {
3099 					while (Z_TYPE(b->val) == IS_UNDEF) {
3100 						b++;
3101 					}
3102 					if (key) {
3103 						if (!b->key
3104 						 || !zend_string_equals(key, b->key)) {
3105 							return 0;
3106 						}
3107 					} else if (b->key) {
3108 						return 0;
3109 					}
3110 					field = Z_PTR(b->val);
3111 					if (old_field->offset != field->offset
3112 					 || old_field->is_const != field->is_const
3113 					 || old_field->is_nested != field->is_nested
3114 					 || old_field->first_bit != field->first_bit
3115 					 || old_field->bits != field->bits
3116 					 || !zend_ffi_same_types(ZEND_FFI_TYPE(old_field->type), ZEND_FFI_TYPE(field->type))) {
3117 						return 0;
3118 					}
3119 					b++;
3120 				} ZEND_HASH_FOREACH_END();
3121 			}
3122 			break;
3123 		case ZEND_FFI_TYPE_FUNC:
3124 			if (old->func.abi != type->func.abi
3125 			 || ((old->func.args ? zend_hash_num_elements(old->func.args) : 0) != (type->func.args ? zend_hash_num_elements(type->func.args) : 0))
3126 			 || !zend_ffi_same_types(ZEND_FFI_TYPE(old->func.ret_type), ZEND_FFI_TYPE(type->func.ret_type))) {
3127 				return 0;
3128 			} else if (old->func.args) {
3129 				zend_ffi_type *arg_type;
3130 				zval *zv = type->func.args->arPacked;
3131 
3132 				ZEND_HASH_PACKED_FOREACH_PTR(old->func.args, arg_type) {
3133 					while (Z_TYPE_P(zv) == IS_UNDEF) {
3134 						zv++;
3135 					}
3136 					if (!zend_ffi_same_types(ZEND_FFI_TYPE(arg_type), ZEND_FFI_TYPE(Z_PTR_P(zv)))) {
3137 						return 0;
3138 					}
3139 					zv++;
3140 				} ZEND_HASH_FOREACH_END();
3141 			}
3142 			break;
3143 		default:
3144 			break;
3145 	}
3146 
3147 	return 1;
3148 }
3149 /* }}} */
3150 
zend_ffi_same_symbols(zend_ffi_symbol * old,zend_ffi_symbol * sym)3151 static bool zend_ffi_same_symbols(zend_ffi_symbol *old, zend_ffi_symbol *sym) /* {{{ */
3152 {
3153 	if (old->kind != sym->kind || old->is_const != sym->is_const) {
3154 		return 0;
3155 	}
3156 
3157 	if (old->kind == ZEND_FFI_SYM_CONST) {
3158 		if (old->value != sym->value) {
3159 			return 0;
3160 		}
3161 	}
3162 
3163 	return zend_ffi_same_types(ZEND_FFI_TYPE(old->type), ZEND_FFI_TYPE(sym->type));
3164 }
3165 /* }}} */
3166 
zend_ffi_same_tags(zend_ffi_tag * old,zend_ffi_tag * tag)3167 static bool zend_ffi_same_tags(zend_ffi_tag *old, zend_ffi_tag *tag) /* {{{ */
3168 {
3169 	if (old->kind != tag->kind) {
3170 		return 0;
3171 	}
3172 
3173 	return zend_ffi_same_types(ZEND_FFI_TYPE(old->type), ZEND_FFI_TYPE(tag->type));
3174 }
3175 /* }}} */
3176 
zend_ffi_subst_old_type(zend_ffi_type ** dcl,zend_ffi_type * old,zend_ffi_type * type)3177 static bool zend_ffi_subst_old_type(zend_ffi_type **dcl, zend_ffi_type *old, zend_ffi_type *type) /* {{{ */
3178 {
3179 	zend_ffi_type *dcl_type;
3180 	zend_ffi_field *field;
3181 
3182 	if (ZEND_FFI_TYPE(*dcl) == type) {
3183 		*dcl = old;
3184 		return 1;
3185 	}
3186 	dcl_type = *dcl;
3187 	switch (dcl_type->kind) {
3188 		case ZEND_FFI_TYPE_POINTER:
3189 			return zend_ffi_subst_old_type(&dcl_type->pointer.type, old, type);
3190 		case ZEND_FFI_TYPE_ARRAY:
3191 			return zend_ffi_subst_old_type(&dcl_type->array.type, old, type);
3192 		case ZEND_FFI_TYPE_FUNC:
3193 			if (zend_ffi_subst_old_type(&dcl_type->func.ret_type, old, type)) {
3194 				return 1;
3195 			}
3196 			if (dcl_type->func.args) {
3197 				zval *zv;
3198 
3199 				ZEND_HASH_PACKED_FOREACH_VAL(dcl_type->func.args, zv) {
3200 					if (zend_ffi_subst_old_type((zend_ffi_type**)&Z_PTR_P(zv), old, type)) {
3201 						return 1;
3202 					}
3203 				} ZEND_HASH_FOREACH_END();
3204 			}
3205 			break;
3206 		case ZEND_FFI_TYPE_STRUCT:
3207 			ZEND_HASH_MAP_FOREACH_PTR(&dcl_type->record.fields, field) {
3208 				if (zend_ffi_subst_old_type(&field->type, old, type)) {
3209 					return 1;
3210 				}
3211 			} ZEND_HASH_FOREACH_END();
3212 			break;
3213 		default:
3214 			break;
3215 	}
3216 	return 0;
3217 } /* }}} */
3218 
zend_ffi_cleanup_type(zend_ffi_type * old,zend_ffi_type * type)3219 static void zend_ffi_cleanup_type(zend_ffi_type *old, zend_ffi_type *type) /* {{{ */
3220 {
3221 	zend_ffi_symbol *sym;
3222 	zend_ffi_tag *tag;
3223 
3224 	if (FFI_G(symbols)) {
3225 		ZEND_HASH_MAP_FOREACH_PTR(FFI_G(symbols), sym) {
3226 			zend_ffi_subst_old_type(&sym->type, old, type);
3227 		} ZEND_HASH_FOREACH_END();
3228 	}
3229 	if (FFI_G(tags)) {
3230 		ZEND_HASH_MAP_FOREACH_PTR(FFI_G(tags), tag) {
3231 			zend_ffi_subst_old_type(&tag->type, old, type);
3232 		} ZEND_HASH_FOREACH_END();
3233 	}
3234 }
3235 /* }}} */
3236 
zend_ffi_remember_type(zend_ffi_type * type)3237 static zend_ffi_type *zend_ffi_remember_type(zend_ffi_type *type) /* {{{ */
3238 {
3239 	if (!FFI_G(weak_types)) {
3240 		FFI_G(weak_types) = emalloc(sizeof(HashTable));
3241 		zend_hash_init(FFI_G(weak_types), 0, NULL, zend_ffi_type_hash_dtor, 0);
3242 	}
3243 	// TODO: avoid dups ???
3244 	type->attr |= ZEND_FFI_ATTR_STORED;
3245 	zend_hash_next_index_insert_ptr(FFI_G(weak_types), ZEND_FFI_TYPE_MAKE_OWNED(type));
3246 	return type;
3247 }
3248 /* }}} */
3249 
zend_ffi_load(const char * filename,bool preload)3250 static zend_ffi *zend_ffi_load(const char *filename, bool preload) /* {{{ */
3251 {
3252 	struct stat buf;
3253 	int fd;
3254 	char *code, *code_pos, *scope_name, *lib, *err;
3255 	size_t code_size, scope_name_len;
3256 	zend_ffi *ffi;
3257 	DL_HANDLE handle = NULL;
3258 	zend_ffi_scope *scope = NULL;
3259 	zend_string *name;
3260 	zend_ffi_symbol *sym;
3261 	zend_ffi_tag *tag;
3262 	void *addr;
3263 
3264 	if (stat(filename, &buf) != 0) {
3265 		if (preload) {
3266 			zend_error(E_WARNING, "FFI: failed pre-loading '%s', file doesn't exist", filename);
3267 		} else {
3268 			zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', file doesn't exist", filename);
3269 		}
3270 		return NULL;
3271 	}
3272 
3273 	if ((buf.st_mode & S_IFMT) != S_IFREG) {
3274 		if (preload) {
3275 			zend_error(E_WARNING, "FFI: failed pre-loading '%s', not a regular file", filename);
3276 		} else {
3277 			zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', not a regular file", filename);
3278 		}
3279 		return NULL;
3280 	}
3281 
3282 	code_size = buf.st_size;
3283 	code = emalloc(code_size + 1);
3284 	fd = open(filename, O_RDONLY, 0);
3285 	if (fd < 0 || read(fd, code, code_size) != code_size) {
3286 		if (preload) {
3287 			zend_error(E_WARNING, "FFI: Failed pre-loading '%s', cannot read_file", filename);
3288 		} else {
3289 			zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', cannot read_file", filename);
3290 		}
3291 		efree(code);
3292 		close(fd);
3293 		return NULL;
3294 	}
3295 	close(fd);
3296 	code[code_size] = 0;
3297 
3298 	FFI_G(symbols) = NULL;
3299 	FFI_G(tags) = NULL;
3300 	FFI_G(persistent) = preload;
3301 	FFI_G(default_type_attr) = preload ?
3302 		ZEND_FFI_ATTR_STORED | ZEND_FFI_ATTR_PERSISTENT :
3303 		ZEND_FFI_ATTR_STORED;
3304 
3305 	scope_name = NULL;
3306 	scope_name_len = 0;
3307 	lib = NULL;
3308 	code_pos = zend_ffi_parse_directives(filename, code, &scope_name, &lib, preload);
3309 	if (!code_pos) {
3310 		efree(code);
3311 		FFI_G(persistent) = 0;
3312 		return NULL;
3313 	}
3314 	code_size -= code_pos - code;
3315 
3316 	if (zend_ffi_parse_decl(code_pos, code_size) == FAILURE) {
3317 		if (preload) {
3318 			zend_error(E_WARNING, "FFI: failed pre-loading '%s'", filename);
3319 		} else {
3320 			zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s'", filename);
3321 		}
3322 		goto cleanup;
3323 	}
3324 
3325 	if (lib) {
3326 		handle = DL_LOAD(lib);
3327 		if (!handle) {
3328 			if (preload) {
3329 				zend_error(E_WARNING, "FFI: Failed pre-loading '%s'", lib);
3330 			} else {
3331 				err = GET_DL_ERROR();
3332 #ifdef PHP_WIN32
3333 				if (err && err[0]) {
3334 					zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s' (%s)", lib, err);
3335 					php_win32_error_msg_free(err);
3336 				} else {
3337 					zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s' (Unknown reason)", lib);
3338 				}
3339 #else
3340 				zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s' (%s)", lib, err);
3341 				GET_DL_ERROR(); /* free the buffer storing the error */
3342 #endif
3343 			}
3344 			goto cleanup;
3345 		}
3346 #ifdef RTLD_DEFAULT
3347 	} else if (1) {
3348 		// TODO: this might need to be disabled or protected ???
3349 		handle = RTLD_DEFAULT;
3350 #endif
3351 	}
3352 
3353 	if (preload) {
3354 		if (!scope_name) {
3355 			scope_name = "C";
3356 		}
3357 		scope_name_len = strlen(scope_name);
3358 		if (FFI_G(scopes)) {
3359 			scope = zend_hash_str_find_ptr(FFI_G(scopes), scope_name, scope_name_len);
3360 		}
3361 	}
3362 
3363 	if (FFI_G(symbols)) {
3364 		ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(FFI_G(symbols), name, sym) {
3365 			if (sym->kind == ZEND_FFI_SYM_VAR) {
3366 				addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name));
3367 				if (!addr) {
3368 					if (preload) {
3369 						zend_error(E_WARNING, "FFI: failed pre-loading '%s', cannot resolve C variable '%s'", filename, ZSTR_VAL(name));
3370 					} else {
3371 						zend_throw_error(zend_ffi_exception_ce, "Failed resolving C variable '%s'", ZSTR_VAL(name));
3372 					}
3373 					if (lib) {
3374 						DL_UNLOAD(handle);
3375 					}
3376 					goto cleanup;
3377 				}
3378 				sym->addr = addr;
3379 			} else if (sym->kind == ZEND_FFI_SYM_FUNC) {
3380 				zend_string *mangled_name = zend_ffi_mangled_func_name(name, ZEND_FFI_TYPE(sym->type));
3381 
3382 				addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(mangled_name));
3383 				zend_string_release(mangled_name);
3384 				if (!addr) {
3385 					if (preload) {
3386 						zend_error(E_WARNING, "failed pre-loading '%s', cannot resolve C function '%s'", filename, ZSTR_VAL(name));
3387 					} else {
3388 						zend_throw_error(zend_ffi_exception_ce, "Failed resolving C function '%s'", ZSTR_VAL(name));
3389 					}
3390 					if (lib) {
3391 						DL_UNLOAD(handle);
3392 					}
3393 					goto cleanup;
3394 				}
3395 				sym->addr = addr;
3396 			}
3397 			if (scope && scope->symbols) {
3398 				zend_ffi_symbol *old_sym = zend_hash_find_ptr(scope->symbols, name);
3399 
3400 				if (old_sym) {
3401 					if (zend_ffi_same_symbols(old_sym, sym)) {
3402 						if (ZEND_FFI_TYPE_IS_OWNED(sym->type)
3403 						 && ZEND_FFI_TYPE(old_sym->type) != ZEND_FFI_TYPE(sym->type)) {
3404 							zend_ffi_type *type = ZEND_FFI_TYPE(sym->type);
3405 							zend_ffi_cleanup_type(ZEND_FFI_TYPE(old_sym->type), ZEND_FFI_TYPE(type));
3406 							zend_ffi_type_dtor(type);
3407 						}
3408 					} else {
3409 						zend_error(E_WARNING, "FFI: failed pre-loading '%s', redefinition of '%s'", filename, ZSTR_VAL(name));
3410 						if (lib) {
3411 							DL_UNLOAD(handle);
3412 						}
3413 						goto cleanup;
3414 					}
3415 				}
3416 			}
3417 		} ZEND_HASH_FOREACH_END();
3418 	}
3419 
3420 	if (preload) {
3421 		if (scope && scope->tags && FFI_G(tags)) {
3422 			ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(FFI_G(tags), name, tag) {
3423 				zend_ffi_tag *old_tag = zend_hash_find_ptr(scope->tags, name);
3424 
3425 				if (old_tag) {
3426 					if (zend_ffi_same_tags(old_tag, tag)) {
3427 						if (ZEND_FFI_TYPE_IS_OWNED(tag->type)
3428 						 && ZEND_FFI_TYPE(old_tag->type) != ZEND_FFI_TYPE(tag->type)) {
3429 							zend_ffi_type *type = ZEND_FFI_TYPE(tag->type);
3430 							zend_ffi_cleanup_type(ZEND_FFI_TYPE(old_tag->type), ZEND_FFI_TYPE(type));
3431 							zend_ffi_type_dtor(type);
3432 						}
3433 					} else {
3434 						zend_error(E_WARNING, "FFI: failed pre-loading '%s', redefinition of '%s %s'", filename, zend_ffi_tag_kind_name[tag->kind], ZSTR_VAL(name));
3435 						if (lib) {
3436 							DL_UNLOAD(handle);
3437 						}
3438 						goto cleanup;
3439 					}
3440 				}
3441 			} ZEND_HASH_FOREACH_END();
3442 		}
3443 
3444 		if (!scope) {
3445 			scope = malloc(sizeof(zend_ffi_scope));
3446 			scope->symbols = FFI_G(symbols);
3447 			scope->tags = FFI_G(tags);
3448 
3449 			if (!FFI_G(scopes)) {
3450 				FFI_G(scopes) = malloc(sizeof(HashTable));
3451 				zend_hash_init(FFI_G(scopes), 0, NULL, zend_ffi_scope_hash_dtor, 1);
3452 			}
3453 
3454 			zend_hash_str_add_ptr(FFI_G(scopes), scope_name, scope_name_len, scope);
3455 		} else {
3456 			if (FFI_G(symbols)) {
3457 				if (!scope->symbols) {
3458 					scope->symbols = FFI_G(symbols);
3459 					FFI_G(symbols) = NULL;
3460 				} else {
3461 					ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(FFI_G(symbols), name, sym) {
3462 						if (!zend_hash_add_ptr(scope->symbols, name, sym)) {
3463 							zend_ffi_type_dtor(sym->type);
3464 							free(sym);
3465 						}
3466 					} ZEND_HASH_FOREACH_END();
3467 					FFI_G(symbols)->pDestructor = NULL;
3468 					zend_hash_destroy(FFI_G(symbols));
3469 				}
3470 			}
3471 			if (FFI_G(tags)) {
3472 				if (!scope->tags) {
3473 					scope->tags = FFI_G(tags);
3474 					FFI_G(tags) = NULL;
3475 				} else {
3476 					ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(FFI_G(tags), name, tag) {
3477 						if (!zend_hash_add_ptr(scope->tags, name, tag)) {
3478 							zend_ffi_type_dtor(tag->type);
3479 							free(tag);
3480 						}
3481 					} ZEND_HASH_FOREACH_END();
3482 					FFI_G(tags)->pDestructor = NULL;
3483 					zend_hash_destroy(FFI_G(tags));
3484 				}
3485 			}
3486 		}
3487 
3488 		if (EG(objects_store).object_buckets) {
3489 			ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
3490 		} else {
3491 			ffi = ecalloc(1, sizeof(zend_ffi));
3492 		}
3493 		ffi->symbols = scope->symbols;
3494 		ffi->tags = scope->tags;
3495 		ffi->persistent = 1;
3496 	} else {
3497 		ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
3498 		ffi->lib = handle;
3499 		ffi->symbols = FFI_G(symbols);
3500 		ffi->tags = FFI_G(tags);
3501 	}
3502 
3503 	efree(code);
3504 	FFI_G(symbols) = NULL;
3505 	FFI_G(tags) = NULL;
3506 	FFI_G(persistent) = 0;
3507 
3508 	return ffi;
3509 
3510 cleanup:
3511 	efree(code);
3512 	if (FFI_G(symbols)) {
3513 		zend_hash_destroy(FFI_G(symbols));
3514 		pefree(FFI_G(symbols), preload);
3515 		FFI_G(symbols) = NULL;
3516 	}
3517 	if (FFI_G(tags)) {
3518 		zend_hash_destroy(FFI_G(tags));
3519 		pefree(FFI_G(tags), preload);
3520 		FFI_G(tags) = NULL;
3521 	}
3522 	FFI_G(persistent) = 0;
3523 	return NULL;
3524 }
3525 /* }}} */
3526 
ZEND_METHOD(FFI,load)3527 ZEND_METHOD(FFI, load) /* {{{ */
3528 {
3529 	zend_string *fn;
3530 	zend_ffi *ffi;
3531 
3532 	ZEND_FFI_VALIDATE_API_RESTRICTION();
3533 	ZEND_PARSE_PARAMETERS_START(1, 1)
3534 		Z_PARAM_STR(fn)
3535 	ZEND_PARSE_PARAMETERS_END();
3536 
3537 	if (CG(compiler_options) & ZEND_COMPILE_PRELOAD_IN_CHILD) {
3538 		zend_throw_error(zend_ffi_exception_ce, "FFI::load() doesn't work in conjunction with \"opcache.preload_user\". Use \"ffi.preload\" instead.");
3539 		RETURN_THROWS();
3540 	}
3541 
3542 	ffi = zend_ffi_load(ZSTR_VAL(fn), (CG(compiler_options) & ZEND_COMPILE_PRELOAD) != 0);
3543 
3544 	if (ffi) {
3545 		RETURN_OBJ(&ffi->std);
3546 	}
3547 }
3548 /* }}} */
3549 
ZEND_METHOD(FFI,scope)3550 ZEND_METHOD(FFI, scope) /* {{{ */
3551 {
3552 	zend_string *scope_name;
3553 	zend_ffi_scope *scope = NULL;
3554 	zend_ffi *ffi;
3555 
3556 	ZEND_FFI_VALIDATE_API_RESTRICTION();
3557 	ZEND_PARSE_PARAMETERS_START(1, 1)
3558 		Z_PARAM_STR(scope_name)
3559 	ZEND_PARSE_PARAMETERS_END();
3560 
3561 	if (FFI_G(scopes)) {
3562 		scope = zend_hash_find_ptr(FFI_G(scopes), scope_name);
3563 	}
3564 
3565 	if (!scope) {
3566 		zend_throw_error(zend_ffi_exception_ce, "Failed loading scope '%s'", ZSTR_VAL(scope_name));
3567 		RETURN_THROWS();
3568 	}
3569 
3570 	ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
3571 
3572 	ffi->symbols = scope->symbols;
3573 	ffi->tags = scope->tags;
3574 	ffi->persistent = 1;
3575 
3576 	RETURN_OBJ(&ffi->std);
3577 }
3578 /* }}} */
3579 
zend_ffi_cleanup_dcl(zend_ffi_dcl * dcl)3580 static void zend_ffi_cleanup_dcl(zend_ffi_dcl *dcl) /* {{{ */
3581 {
3582 	if (dcl) {
3583 		zend_ffi_type_dtor(dcl->type);
3584 		dcl->type = NULL;
3585 	}
3586 }
3587 /* }}} */
3588 
zend_ffi_throw_parser_error(const char * format,...)3589 static void zend_ffi_throw_parser_error(const char *format, ...) /* {{{ */
3590 {
3591 	va_list va;
3592 	char *message = NULL;
3593 
3594 	va_start(va, format);
3595 	zend_vspprintf(&message, 0, format, va);
3596 
3597 	if (EG(current_execute_data)) {
3598 		zend_throw_exception(zend_ffi_parser_exception_ce, message, 0);
3599 	} else {
3600 		zend_error(E_WARNING, "FFI Parser: %s", message);
3601 	}
3602 
3603 	efree(message);
3604 	va_end(va);
3605 }
3606 /* }}} */
3607 
zend_ffi_validate_vla(zend_ffi_type * type)3608 static zend_result zend_ffi_validate_vla(zend_ffi_type *type) /* {{{ */
3609 {
3610 	if (!FFI_G(allow_vla) && (type->attr & ZEND_FFI_ATTR_VLA)) {
3611 		zend_ffi_throw_parser_error("\"[*]\" is not allowed in other than function prototype scope at line %d", FFI_G(line));
3612 		return FAILURE;
3613 	}
3614 	return SUCCESS;
3615 }
3616 /* }}} */
3617 
zend_ffi_validate_incomplete_type(zend_ffi_type * type,bool allow_incomplete_tag,bool allow_incomplete_array)3618 static zend_result zend_ffi_validate_incomplete_type(zend_ffi_type *type, bool allow_incomplete_tag, bool allow_incomplete_array) /* {{{ */
3619 {
3620 	if (!allow_incomplete_tag && (type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
3621 		if (FFI_G(tags)) {
3622 			zend_string *key;
3623 			zend_ffi_tag *tag;
3624 
3625 			ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(FFI_G(tags), key, tag) {
3626 				if (ZEND_FFI_TYPE(tag->type) == type) {
3627 					if (type->kind == ZEND_FFI_TYPE_ENUM) {
3628 						zend_ffi_throw_parser_error("Incomplete enum \"%s\" at line %d", ZSTR_VAL(key), FFI_G(line));
3629 					} else if (type->attr & ZEND_FFI_ATTR_UNION) {
3630 						zend_ffi_throw_parser_error("Incomplete union \"%s\" at line %d", ZSTR_VAL(key), FFI_G(line));
3631 					} else {
3632 						zend_ffi_throw_parser_error("Incomplete struct \"%s\" at line %d", ZSTR_VAL(key), FFI_G(line));
3633 					}
3634 					return FAILURE;
3635 				}
3636 			} ZEND_HASH_FOREACH_END();
3637 		}
3638 		if (FFI_G(symbols)) {
3639 			zend_string *key;
3640 			zend_ffi_symbol *sym;
3641 
3642 			ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(FFI_G(symbols), key, sym) {
3643 				if (type == ZEND_FFI_TYPE(sym->type)) {
3644 					zend_ffi_throw_parser_error("Incomplete C type %s at line %d", ZSTR_VAL(key), FFI_G(line));
3645 					return FAILURE;
3646 				}
3647 			} ZEND_HASH_FOREACH_END();
3648 		}
3649 		zend_ffi_throw_parser_error("Incomplete type at line %d", FFI_G(line));
3650 		return FAILURE;
3651 	} else if (!allow_incomplete_array && (type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
3652 		zend_ffi_throw_parser_error("\"[]\" is not allowed at line %d", FFI_G(line));
3653 		return FAILURE;
3654 	} else if (!FFI_G(allow_vla) && (type->attr & ZEND_FFI_ATTR_VLA)) {
3655 		zend_ffi_throw_parser_error("\"[*]\" is not allowed in other than function prototype scope at line %d", FFI_G(line));
3656 		return FAILURE;
3657 	}
3658 	return SUCCESS;
3659 }
3660 /* }}} */
3661 
zend_ffi_validate_type(zend_ffi_type * type,bool allow_incomplete_tag,bool allow_incomplete_array)3662 static zend_result zend_ffi_validate_type(zend_ffi_type *type, bool allow_incomplete_tag, bool allow_incomplete_array) /* {{{ */
3663 {
3664 	if (type->kind == ZEND_FFI_TYPE_VOID) {
3665 		zend_ffi_throw_parser_error("void type is not allowed at line %d", FFI_G(line));
3666 		return FAILURE;
3667 	}
3668 	return zend_ffi_validate_incomplete_type(type, allow_incomplete_tag, allow_incomplete_array);
3669 }
3670 /* }}} */
3671 
zend_ffi_validate_var_type(zend_ffi_type * type,bool allow_incomplete_array)3672 static zend_result zend_ffi_validate_var_type(zend_ffi_type *type, bool allow_incomplete_array) /* {{{ */
3673 {
3674 	if (type->kind == ZEND_FFI_TYPE_FUNC) {
3675 		zend_ffi_throw_parser_error("function type is not allowed at line %d", FFI_G(line));
3676 		return FAILURE;
3677 	}
3678 	return zend_ffi_validate_type(type, 0, allow_incomplete_array);
3679 }
3680 /* }}} */
3681 
zend_ffi_validate_type_name(zend_ffi_dcl * dcl)3682 void zend_ffi_validate_type_name(zend_ffi_dcl *dcl) /* {{{ */
3683 {
3684 	zend_ffi_finalize_type(dcl);
3685 	if (zend_ffi_validate_var_type(ZEND_FFI_TYPE(dcl->type), 0) == FAILURE) {
3686 		zend_ffi_cleanup_dcl(dcl);
3687 		LONGJMP(FFI_G(bailout), FAILURE);
3688 	}
3689 }
3690 /* }}} */
3691 
zend_ffi_subst_type(zend_ffi_type ** dcl,zend_ffi_type * type)3692 static bool zend_ffi_subst_type(zend_ffi_type **dcl, zend_ffi_type *type) /* {{{ */
3693 {
3694 	zend_ffi_type *dcl_type;
3695 	zend_ffi_field *field;
3696 
3697 	if (*dcl == type) {
3698 		*dcl = ZEND_FFI_TYPE_MAKE_OWNED(type);
3699 		return 1;
3700 	}
3701 	dcl_type = *dcl;
3702 	switch (dcl_type->kind) {
3703 		case ZEND_FFI_TYPE_POINTER:
3704 			return zend_ffi_subst_type(&dcl_type->pointer.type, type);
3705 		case ZEND_FFI_TYPE_ARRAY:
3706 			return zend_ffi_subst_type(&dcl_type->array.type, type);
3707 		case ZEND_FFI_TYPE_FUNC:
3708 			if (zend_ffi_subst_type(&dcl_type->func.ret_type, type)) {
3709 				return 1;
3710 			}
3711 			if (dcl_type->func.args) {
3712 				zval *zv;
3713 
3714 				ZEND_HASH_PACKED_FOREACH_VAL(dcl_type->func.args, zv) {
3715 					if (zend_ffi_subst_type((zend_ffi_type**)&Z_PTR_P(zv), type)) {
3716 						return 1;
3717 					}
3718 				} ZEND_HASH_FOREACH_END();
3719 			}
3720 			break;
3721 		case ZEND_FFI_TYPE_STRUCT:
3722 			ZEND_HASH_MAP_FOREACH_PTR(&dcl_type->record.fields, field) {
3723 				if (zend_ffi_subst_type(&field->type, type)) {
3724 					return 1;
3725 				}
3726 			} ZEND_HASH_FOREACH_END();
3727 			break;
3728 		default:
3729 			break;
3730 	}
3731 	return 0;
3732 } /* }}} */
3733 
zend_ffi_tags_cleanup(zend_ffi_dcl * dcl)3734 static void zend_ffi_tags_cleanup(zend_ffi_dcl *dcl) /* {{{ */
3735 {
3736 	zend_ffi_tag *tag;
3737 	ZEND_HASH_MAP_FOREACH_PTR(FFI_G(tags), tag) {
3738 		if (ZEND_FFI_TYPE_IS_OWNED(tag->type)) {
3739 			zend_ffi_type *type = ZEND_FFI_TYPE(tag->type);
3740 			zend_ffi_subst_type(&dcl->type, type);
3741 			tag->type = type;
3742 		}
3743 	} ZEND_HASH_FOREACH_END();
3744 	zend_hash_destroy(FFI_G(tags));
3745 	efree(FFI_G(tags));
3746 }
3747 /* }}} */
3748 
ZEND_METHOD(FFI,new)3749 ZEND_METHOD(FFI, new) /* {{{ */
3750 {
3751 	zend_string *type_def = NULL;
3752 	zend_object *type_obj = NULL;
3753 	zend_ffi_type *type, *type_ptr;
3754 	zend_ffi_cdata *cdata;
3755 	void *ptr;
3756 	bool owned = 1;
3757 	bool persistent = 0;
3758 	bool is_const = 0;
3759 	bool is_static_call = Z_TYPE(EX(This)) != IS_OBJECT;
3760 	zend_ffi_flags flags = ZEND_FFI_FLAG_OWNED;
3761 
3762 	ZEND_FFI_VALIDATE_API_RESTRICTION();
3763 	ZEND_PARSE_PARAMETERS_START(1, 3)
3764 		Z_PARAM_OBJ_OF_CLASS_OR_STR(type_obj, zend_ffi_ctype_ce, type_def)
3765 		Z_PARAM_OPTIONAL
3766 		Z_PARAM_BOOL(owned)
3767 		Z_PARAM_BOOL(persistent)
3768 	ZEND_PARSE_PARAMETERS_END();
3769 
3770 	if (is_static_call) {
3771 		zend_error(E_DEPRECATED, "Calling FFI::new() statically is deprecated");
3772 		if (EG(exception)) {
3773 			RETURN_THROWS();
3774 		}
3775 	}
3776 
3777 	if (!owned) {
3778 		flags &= ~ZEND_FFI_FLAG_OWNED;
3779 	}
3780 
3781 	if (persistent) {
3782 		flags |= ZEND_FFI_FLAG_PERSISTENT;
3783 	}
3784 
3785 	if (type_def) {
3786 		zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
3787 
3788 		if (!is_static_call) {
3789 			zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This));
3790 			FFI_G(symbols) = ffi->symbols;
3791 			FFI_G(tags) = ffi->tags;
3792 		} else {
3793 			FFI_G(symbols) = NULL;
3794 			FFI_G(tags) = NULL;
3795 		}
3796 		bool clean_symbols = FFI_G(symbols) == NULL;
3797 		bool clean_tags = FFI_G(tags) == NULL;
3798 
3799 		FFI_G(default_type_attr) = 0;
3800 
3801 		if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) {
3802 			zend_ffi_type_dtor(dcl.type);
3803 			if (clean_tags && FFI_G(tags)) {
3804 				zend_hash_destroy(FFI_G(tags));
3805 				efree(FFI_G(tags));
3806 				FFI_G(tags) = NULL;
3807 			}
3808 			if (clean_symbols && FFI_G(symbols)) {
3809 				zend_hash_destroy(FFI_G(symbols));
3810 				efree(FFI_G(symbols));
3811 				FFI_G(symbols) = NULL;
3812 			}
3813 			return;
3814 		}
3815 
3816 		type = ZEND_FFI_TYPE(dcl.type);
3817 		if (dcl.attr & ZEND_FFI_ATTR_CONST) {
3818 			is_const = 1;
3819 		}
3820 
3821 		if (clean_tags && FFI_G(tags)) {
3822 			zend_ffi_tags_cleanup(&dcl);
3823 		}
3824 		if (clean_symbols && FFI_G(symbols)) {
3825 			zend_hash_destroy(FFI_G(symbols));
3826 			efree(FFI_G(symbols));
3827 			FFI_G(symbols) = NULL;
3828 		}
3829 		FFI_G(symbols) = NULL;
3830 		FFI_G(tags) = NULL;
3831 
3832 		type_ptr = dcl.type;
3833 	} else {
3834 		zend_ffi_ctype *ctype = (zend_ffi_ctype*) type_obj;
3835 
3836 		type_ptr = type = ctype->type;
3837 		if (ZEND_FFI_TYPE_IS_OWNED(type)) {
3838 			type = ZEND_FFI_TYPE(type);
3839 			if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
3840 				if (GC_REFCOUNT(&ctype->std) == 1) {
3841 					/* transfer type ownership */
3842 					ctype->type = type;
3843 				} else {
3844 					ctype->type = type_ptr = type = zend_ffi_remember_type(type);
3845 				}
3846 			}
3847 		}
3848 	}
3849 
3850 	if (type->size == 0) {
3851 		zend_throw_error(zend_ffi_exception_ce, "Cannot instantiate FFI\\CData of zero size");
3852 		zend_ffi_type_dtor(type_ptr);
3853 		return;
3854 	}
3855 
3856 	ptr = pemalloc(type->size, flags & ZEND_FFI_FLAG_PERSISTENT);
3857 	memset(ptr, 0, type->size);
3858 
3859 	cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
3860 	if (type->kind < ZEND_FFI_TYPE_POINTER) {
3861 		cdata->std.handlers = &zend_ffi_cdata_value_handlers;
3862 	}
3863 	cdata->type = type_ptr;
3864 	cdata->ptr = ptr;
3865 	cdata->flags = flags;
3866 	if (is_const) {
3867 		cdata->flags |= ZEND_FFI_FLAG_CONST;
3868 	}
3869 
3870 	RETURN_OBJ(&cdata->std);
3871 }
3872 /* }}} */
3873 
ZEND_METHOD(FFI,free)3874 ZEND_METHOD(FFI, free) /* {{{ */
3875 {
3876 	zval *zv;
3877 	zend_ffi_cdata *cdata;
3878 
3879 	ZEND_FFI_VALIDATE_API_RESTRICTION();
3880 	ZEND_PARSE_PARAMETERS_START(1, 1)
3881 		Z_PARAM_OBJECT_OF_CLASS_EX(zv, zend_ffi_cdata_ce, 0, 1);
3882 	ZEND_PARSE_PARAMETERS_END();
3883 
3884 	cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
3885 
3886 	if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
3887 		if (!cdata->ptr) {
3888 			zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
3889 			RETURN_THROWS();
3890 		}
3891 		if (cdata->ptr != (void*)&cdata->ptr_holder) {
3892 			pefree(*(void**)cdata->ptr, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
3893 		} else {
3894 			pefree(cdata->ptr_holder, (cdata->flags & ZEND_FFI_FLAG_PERSISTENT) || !is_zend_ptr(cdata->ptr_holder));
3895 		}
3896 		*(void**)cdata->ptr = NULL;
3897 	} else if (!(cdata->flags & ZEND_FFI_FLAG_OWNED)) {
3898 		pefree(cdata->ptr, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
3899 		cdata->ptr = NULL;
3900 		cdata->flags &= ~(ZEND_FFI_FLAG_OWNED|ZEND_FFI_FLAG_PERSISTENT);
3901 		cdata->std.handlers = &zend_ffi_cdata_free_handlers;
3902 	} else {
3903 		zend_throw_error(zend_ffi_exception_ce, "free() non a C pointer");
3904 	}
3905 }
3906 /* }}} */
3907 
ZEND_METHOD(FFI,cast)3908 ZEND_METHOD(FFI, cast) /* {{{ */
3909 {
3910 	zend_string *type_def = NULL;
3911 	zend_object *ztype = NULL;
3912 	zend_ffi_type *old_type, *type, *type_ptr;
3913 	zend_ffi_cdata *old_cdata, *cdata;
3914 	bool is_const = 0;
3915 	bool is_static_call = Z_TYPE(EX(This)) != IS_OBJECT;
3916 	zval *zv, *arg;
3917 	void *ptr;
3918 
3919 	ZEND_FFI_VALIDATE_API_RESTRICTION();
3920 	ZEND_PARSE_PARAMETERS_START(2, 2)
3921 		Z_PARAM_OBJ_OF_CLASS_OR_STR(ztype, zend_ffi_ctype_ce, type_def)
3922 		Z_PARAM_ZVAL(zv)
3923 	ZEND_PARSE_PARAMETERS_END();
3924 
3925 	if (is_static_call) {
3926 		zend_error(E_DEPRECATED, "Calling FFI::cast() statically is deprecated");
3927 		if (EG(exception)) {
3928 			RETURN_THROWS();
3929 		}
3930 	}
3931 
3932 	arg = zv;
3933 	ZVAL_DEREF(zv);
3934 
3935 	if (type_def) {
3936 		zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
3937 
3938 		if (!is_static_call) {
3939 			zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This));
3940 			FFI_G(symbols) = ffi->symbols;
3941 			FFI_G(tags) = ffi->tags;
3942 		} else {
3943 			FFI_G(symbols) = NULL;
3944 			FFI_G(tags) = NULL;
3945 		}
3946 		bool clean_symbols = FFI_G(symbols) == NULL;
3947 		bool clean_tags = FFI_G(tags) == NULL;
3948 
3949 		FFI_G(default_type_attr) = 0;
3950 
3951 		if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) {
3952 			zend_ffi_type_dtor(dcl.type);
3953 			if (clean_tags && FFI_G(tags)) {
3954 				zend_hash_destroy(FFI_G(tags));
3955 				efree(FFI_G(tags));
3956 				FFI_G(tags) = NULL;
3957 			}
3958 			if (clean_symbols && FFI_G(symbols)) {
3959 				zend_hash_destroy(FFI_G(symbols));
3960 				efree(FFI_G(symbols));
3961 				FFI_G(symbols) = NULL;
3962 			}
3963 			return;
3964 		}
3965 
3966 		type = ZEND_FFI_TYPE(dcl.type);
3967 		if (dcl.attr & ZEND_FFI_ATTR_CONST) {
3968 			is_const = 1;
3969 		}
3970 
3971 		if (clean_tags && FFI_G(tags)) {
3972 			zend_ffi_tags_cleanup(&dcl);
3973 		}
3974 		if (clean_symbols && FFI_G(symbols)) {
3975 			zend_hash_destroy(FFI_G(symbols));
3976 			efree(FFI_G(symbols));
3977 			FFI_G(symbols) = NULL;
3978 		}
3979 		FFI_G(symbols) = NULL;
3980 		FFI_G(tags) = NULL;
3981 
3982 		type_ptr = dcl.type;
3983 	} else {
3984 		zend_ffi_ctype *ctype = (zend_ffi_ctype*) ztype;
3985 
3986 		type_ptr = type = ctype->type;
3987 		if (ZEND_FFI_TYPE_IS_OWNED(type)) {
3988 			type = ZEND_FFI_TYPE(type);
3989 			if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
3990 				if (GC_REFCOUNT(&ctype->std) == 1) {
3991 					/* transfer type ownership */
3992 					ctype->type = type;
3993 				} else {
3994 					ctype->type = type_ptr = type = zend_ffi_remember_type(type);
3995 				}
3996 			}
3997 		}
3998 	}
3999 
4000 	if (Z_TYPE_P(zv) != IS_OBJECT || Z_OBJCE_P(zv) != zend_ffi_cdata_ce) {
4001 		if (type->kind < ZEND_FFI_TYPE_POINTER && Z_TYPE_P(zv) < IS_STRING) {
4002 			/* numeric conversion */
4003 			cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
4004 			cdata->std.handlers = &zend_ffi_cdata_value_handlers;
4005 			cdata->type = type_ptr;
4006 			cdata->ptr = emalloc(type->size);
4007 			zend_ffi_zval_to_cdata(cdata->ptr, type, zv);
4008 			cdata->flags = ZEND_FFI_FLAG_OWNED;
4009 			if (is_const) {
4010 				cdata->flags |= ZEND_FFI_FLAG_CONST;
4011 			}
4012 			RETURN_OBJ(&cdata->std);
4013 		} else if (type->kind == ZEND_FFI_TYPE_POINTER && Z_TYPE_P(zv) == IS_LONG) {
4014 			/* number to pointer conversion */
4015 			cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
4016 			cdata->type = type_ptr;
4017 			cdata->ptr = &cdata->ptr_holder;
4018 			cdata->ptr_holder = (void*)(intptr_t)Z_LVAL_P(zv);
4019 			if (is_const) {
4020 				cdata->flags |= ZEND_FFI_FLAG_CONST;
4021 			}
4022 			RETURN_OBJ(&cdata->std);
4023 		} else if (type->kind == ZEND_FFI_TYPE_POINTER && Z_TYPE_P(zv) == IS_NULL) {
4024 			/* null -> pointer */
4025 			cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
4026 			cdata->type = type_ptr;
4027 			cdata->ptr = &cdata->ptr_holder;
4028 			cdata->ptr_holder = NULL;
4029 			if (is_const) {
4030 				cdata->flags |= ZEND_FFI_FLAG_CONST;
4031 			}
4032 			RETURN_OBJ(&cdata->std);
4033 		} else {
4034 			zend_wrong_parameter_class_error(2, "FFI\\CData", zv);
4035 			RETURN_THROWS();
4036 		}
4037 	}
4038 
4039 	old_cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
4040 	old_type = ZEND_FFI_TYPE(old_cdata->type);
4041 	ptr = old_cdata->ptr;
4042 
4043 	cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
4044 	if (type->kind < ZEND_FFI_TYPE_POINTER) {
4045 		cdata->std.handlers = &zend_ffi_cdata_value_handlers;
4046 	}
4047 	cdata->type = type_ptr;
4048 
4049 	if (old_type->kind == ZEND_FFI_TYPE_POINTER
4050 	 && type->kind != ZEND_FFI_TYPE_POINTER
4051 	 && ZEND_FFI_TYPE(old_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID) {
4052 		/* automatically dereference void* pointers ??? */
4053 		cdata->ptr = *(void**)ptr;
4054 	} else if (old_type->kind == ZEND_FFI_TYPE_ARRAY
4055 	 && type->kind == ZEND_FFI_TYPE_POINTER
4056 	 && zend_ffi_is_compatible_type(ZEND_FFI_TYPE(old_type->array.type), ZEND_FFI_TYPE(type->pointer.type))) {		cdata->ptr = &cdata->ptr_holder;
4057  		cdata->ptr = &cdata->ptr_holder;
4058  		cdata->ptr_holder = old_cdata->ptr;
4059 	} else if (old_type->kind == ZEND_FFI_TYPE_POINTER
4060 	 && type->kind == ZEND_FFI_TYPE_ARRAY
4061 	 && zend_ffi_is_compatible_type(ZEND_FFI_TYPE(old_type->pointer.type), ZEND_FFI_TYPE(type->array.type))) {
4062 		cdata->ptr = old_cdata->ptr_holder;
4063 	} else if (type->size > old_type->size) {
4064 		zend_object_release(&cdata->std);
4065 		zend_throw_error(zend_ffi_exception_ce, "attempt to cast to larger type");
4066 		RETURN_THROWS();
4067 	} else if (ptr != &old_cdata->ptr_holder) {
4068 		cdata->ptr = ptr;
4069 	} else {
4070 		cdata->ptr = &cdata->ptr_holder;
4071 		cdata->ptr_holder = old_cdata->ptr_holder;
4072 	}
4073 	if (is_const) {
4074 		cdata->flags |= ZEND_FFI_FLAG_CONST;
4075 	}
4076 
4077 	if (old_cdata->flags & ZEND_FFI_FLAG_OWNED) {
4078 		if (GC_REFCOUNT(&old_cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1) {
4079 			/* transfer ownership */
4080 			old_cdata->flags &= ~ZEND_FFI_FLAG_OWNED;
4081 			cdata->flags |= ZEND_FFI_FLAG_OWNED;
4082 		} else {
4083 			//???zend_throw_error(zend_ffi_exception_ce, "Attempt to cast owned C pointer");
4084 		}
4085 	}
4086 
4087 	RETURN_OBJ(&cdata->std);
4088 }
4089 /* }}} */
4090 
ZEND_METHOD(FFI,type)4091 ZEND_METHOD(FFI, type) /* {{{ */
4092 {
4093 	zend_ffi_ctype *ctype;
4094 	zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
4095 	zend_string *type_def;
4096 	bool is_static_call = Z_TYPE(EX(This)) != IS_OBJECT;
4097 
4098 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4099 	ZEND_PARSE_PARAMETERS_START(1, 1)
4100 		Z_PARAM_STR(type_def);
4101 	ZEND_PARSE_PARAMETERS_END();
4102 
4103 	if (is_static_call) {
4104 		zend_error(E_DEPRECATED, "Calling FFI::type() statically is deprecated");
4105 		if (EG(exception)) {
4106 			RETURN_THROWS();
4107 		}
4108 	}
4109 
4110 	if (!is_static_call) {
4111 		zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This));
4112 		FFI_G(symbols) = ffi->symbols;
4113 		FFI_G(tags) = ffi->tags;
4114 	} else {
4115 		FFI_G(symbols) = NULL;
4116 		FFI_G(tags) = NULL;
4117 	}
4118 	bool clean_symbols = FFI_G(symbols) == NULL;
4119 	bool clean_tags = FFI_G(tags) == NULL;
4120 
4121 	FFI_G(default_type_attr) = 0;
4122 
4123 	if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) == FAILURE) {
4124 		zend_ffi_type_dtor(dcl.type);
4125 		if (clean_tags && FFI_G(tags)) {
4126 			zend_hash_destroy(FFI_G(tags));
4127 			efree(FFI_G(tags));
4128 			FFI_G(tags) = NULL;
4129 		}
4130 		if (clean_symbols && FFI_G(symbols)) {
4131 			zend_hash_destroy(FFI_G(symbols));
4132 			efree(FFI_G(symbols));
4133 			FFI_G(symbols) = NULL;
4134 		}
4135 		return;
4136 	}
4137 
4138 	if (clean_tags && FFI_G(tags)) {
4139 		zend_ffi_tags_cleanup(&dcl);
4140 	}
4141 	if (clean_symbols && FFI_G(symbols)) {
4142 		zend_hash_destroy(FFI_G(symbols));
4143 		efree(FFI_G(symbols));
4144 		FFI_G(symbols) = NULL;
4145 	}
4146 	FFI_G(symbols) = NULL;
4147 	FFI_G(tags) = NULL;
4148 
4149 	ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
4150 	ctype->type = dcl.type;
4151 
4152 	RETURN_OBJ(&ctype->std);
4153 }
4154 /* }}} */
4155 
ZEND_METHOD(FFI,typeof)4156 ZEND_METHOD(FFI, typeof) /* {{{ */
4157 {
4158 	zval *zv, *arg;
4159 	zend_ffi_ctype *ctype;
4160 	zend_ffi_type *type;
4161 
4162 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4163 	ZEND_PARSE_PARAMETERS_START(1, 1)
4164 		Z_PARAM_ZVAL(zv);
4165 	ZEND_PARSE_PARAMETERS_END();
4166 
4167 	arg = zv;
4168 	ZVAL_DEREF(zv);
4169 	if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_cdata_ce) {
4170 		zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
4171 
4172 		type = cdata->type;
4173 		if (ZEND_FFI_TYPE_IS_OWNED(type)) {
4174 			type = ZEND_FFI_TYPE(type);
4175 			if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
4176 				if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1) {
4177 					/* transfer type ownership */
4178 					cdata->type = type;
4179 					type = ZEND_FFI_TYPE_MAKE_OWNED(type);
4180 				} else {
4181 					cdata->type = type = zend_ffi_remember_type(type);
4182 				}
4183 			}
4184 		}
4185 	} else {
4186 		zend_wrong_parameter_class_error(1, "FFI\\CData", zv);
4187 		RETURN_THROWS();
4188 	}
4189 
4190 	ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
4191 	ctype->type = type;
4192 
4193 	RETURN_OBJ(&ctype->std);
4194 }
4195 /* }}} */
4196 
ZEND_METHOD(FFI,arrayType)4197 ZEND_METHOD(FFI, arrayType) /* {{{ */
4198 {
4199 	zval *ztype;
4200 	zend_ffi_ctype *ctype;
4201 	zend_ffi_type *type;
4202 	HashTable *dims;
4203 	zval *val;
4204 
4205 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4206 	ZEND_PARSE_PARAMETERS_START(2, 2)
4207 		Z_PARAM_OBJECT_OF_CLASS(ztype, zend_ffi_ctype_ce)
4208 		Z_PARAM_ARRAY_HT(dims)
4209 	ZEND_PARSE_PARAMETERS_END();
4210 
4211 	ctype = (zend_ffi_ctype*)Z_OBJ_P(ztype);
4212 	type = ZEND_FFI_TYPE(ctype->type);
4213 
4214 	if (type->kind == ZEND_FFI_TYPE_FUNC) {
4215 		zend_throw_error(zend_ffi_exception_ce, "Array of functions is not allowed");
4216 		RETURN_THROWS();
4217 	} else if (type->kind == ZEND_FFI_TYPE_ARRAY && (type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
4218 		zend_throw_error(zend_ffi_exception_ce, "Only the leftmost array can be undimensioned");
4219 		RETURN_THROWS();
4220 	} else if (type->kind == ZEND_FFI_TYPE_VOID) {
4221 		zend_throw_error(zend_ffi_exception_ce, "Array of void type is not allowed");
4222 		RETURN_THROWS();
4223 	} else if (type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG) {
4224 		zend_throw_error(zend_ffi_exception_ce, "Array of incomplete type is not allowed");
4225 		RETURN_THROWS();
4226 	}
4227 
4228 	if (ZEND_FFI_TYPE_IS_OWNED(ctype->type)) {
4229 		if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
4230 			if (GC_REFCOUNT(&ctype->std) == 1) {
4231 				/* transfer type ownership */
4232 				ctype->type = type;
4233 				type = ZEND_FFI_TYPE_MAKE_OWNED(type);
4234 			} else {
4235 				ctype->type = type = zend_ffi_remember_type(type);
4236 			}
4237 		}
4238 	}
4239 
4240 	ZEND_HASH_REVERSE_FOREACH_VAL(dims, val) {
4241 		zend_long n = zval_get_long(val);
4242 		zend_ffi_type *new_type;
4243 
4244 		if (n < 0) {
4245 			zend_throw_error(zend_ffi_exception_ce, "negative array index");
4246 			zend_ffi_type_dtor(type);
4247 			RETURN_THROWS();
4248 		} else if (ZEND_FFI_TYPE(type)->kind == ZEND_FFI_TYPE_ARRAY && (ZEND_FFI_TYPE(type)->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
4249 			zend_throw_error(zend_ffi_exception_ce, "only the leftmost array can be undimensioned");
4250 			zend_ffi_type_dtor(type);
4251 			RETURN_THROWS();
4252 		}
4253 
4254 		new_type = emalloc(sizeof(zend_ffi_type));
4255 		new_type->kind = ZEND_FFI_TYPE_ARRAY;
4256 		new_type->attr = 0;
4257 		new_type->size = n * ZEND_FFI_TYPE(type)->size;
4258 		new_type->align = ZEND_FFI_TYPE(type)->align;
4259 		new_type->array.type = type;
4260 		new_type->array.length = n;
4261 
4262 		if (n == 0) {
4263 			new_type->attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;
4264 		}
4265 
4266 		type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
4267 	} ZEND_HASH_FOREACH_END();
4268 
4269 	ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
4270 	ctype->type = type;
4271 
4272 	RETURN_OBJ(&ctype->std);
4273 }
4274 /* }}} */
4275 
ZEND_METHOD(FFI,addr)4276 ZEND_METHOD(FFI, addr) /* {{{ */
4277 {
4278 	zend_ffi_type *type, *new_type;
4279 	zend_ffi_cdata *cdata, *new_cdata;
4280 	zval *zv, *arg;
4281 
4282 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4283 	ZEND_PARSE_PARAMETERS_START(1, 1)
4284 		Z_PARAM_ZVAL(zv)
4285 	ZEND_PARSE_PARAMETERS_END();
4286 
4287 	arg = zv;
4288 	ZVAL_DEREF(zv);
4289 	if (Z_TYPE_P(zv) != IS_OBJECT || Z_OBJCE_P(zv) != zend_ffi_cdata_ce) {
4290 		zend_wrong_parameter_class_error(1, "FFI\\CData", zv);
4291 		RETURN_THROWS();
4292 	}
4293 
4294 	cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
4295 	type = ZEND_FFI_TYPE(cdata->type);
4296 
4297 	if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1 && type->kind == ZEND_FFI_TYPE_POINTER
4298 	 && cdata->ptr == &cdata->ptr_holder) {
4299 		zend_throw_error(zend_ffi_exception_ce, "FFI::addr() cannot create a reference to a temporary pointer");
4300 		RETURN_THROWS();
4301 	}
4302 
4303 	new_type = emalloc(sizeof(zend_ffi_type));
4304 	new_type->kind = ZEND_FFI_TYPE_POINTER;
4305 	new_type->attr = 0;
4306 	new_type->size = sizeof(void*);
4307 	new_type->align = _Alignof(void*);
4308 	/* life-time (source must relive the resulting pointer) ??? */
4309 	new_type->pointer.type = type;
4310 
4311 	new_cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
4312 	new_cdata->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
4313 	new_cdata->ptr_holder = cdata->ptr;
4314 	new_cdata->ptr = &new_cdata->ptr_holder;
4315 
4316 	if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1) {
4317 		if (ZEND_FFI_TYPE_IS_OWNED(cdata->type)) {
4318 			/* transfer type ownership */
4319 			cdata->type = type;
4320 			new_type->pointer.type = ZEND_FFI_TYPE_MAKE_OWNED(type);
4321 		}
4322 		if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
4323 			/* transfer ownership */
4324 			cdata->flags &= ~ZEND_FFI_FLAG_OWNED;
4325 			new_cdata->flags |= ZEND_FFI_FLAG_OWNED;
4326 		}
4327 	}
4328 
4329 	RETURN_OBJ(&new_cdata->std);
4330 }
4331 /* }}} */
4332 
ZEND_METHOD(FFI,sizeof)4333 ZEND_METHOD(FFI, sizeof) /* {{{ */
4334 {
4335 	zval *zv;
4336 	zend_ffi_type *type;
4337 
4338 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4339 	ZEND_PARSE_PARAMETERS_START(1, 1)
4340 		Z_PARAM_ZVAL(zv);
4341 	ZEND_PARSE_PARAMETERS_END();
4342 
4343 	ZVAL_DEREF(zv);
4344 	if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_cdata_ce) {
4345 		zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
4346 		type = ZEND_FFI_TYPE(cdata->type);
4347 	} else if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_ctype_ce) {
4348 		zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(zv);
4349 		type = ZEND_FFI_TYPE(ctype->type);
4350 	} else {
4351 		zend_wrong_parameter_class_error(1, "FFI\\CData or FFI\\CType", zv);
4352 		RETURN_THROWS();
4353 	}
4354 
4355 	RETURN_LONG(type->size);
4356 }
4357 /* }}} */
4358 
ZEND_METHOD(FFI,alignof)4359 ZEND_METHOD(FFI, alignof) /* {{{ */
4360 {
4361 	zval *zv;
4362 	zend_ffi_type *type;
4363 
4364 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4365 	ZEND_PARSE_PARAMETERS_START(1, 1)
4366 		Z_PARAM_ZVAL(zv);
4367 	ZEND_PARSE_PARAMETERS_END();
4368 
4369 	ZVAL_DEREF(zv);
4370 	if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_cdata_ce) {
4371 		zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
4372 		type = ZEND_FFI_TYPE(cdata->type);
4373 	} else if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_ctype_ce) {
4374 		zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(zv);
4375 		type = ZEND_FFI_TYPE(ctype->type);
4376 	} else {
4377 		zend_wrong_parameter_class_error(1, "FFI\\CData or FFI\\CType", zv);
4378 		RETURN_THROWS();
4379 	}
4380 
4381 	RETURN_LONG(type->align);
4382 }
4383 /* }}} */
4384 
ZEND_METHOD(FFI,memcpy)4385 ZEND_METHOD(FFI, memcpy) /* {{{ */
4386 {
4387 	zval *zv1, *zv2;
4388 	zend_ffi_cdata *cdata1, *cdata2;
4389 	zend_ffi_type *type1, *type2;
4390 	void *ptr1, *ptr2;
4391 	zend_long size;
4392 
4393 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4394 	ZEND_PARSE_PARAMETERS_START(3, 3)
4395 		Z_PARAM_OBJECT_OF_CLASS_EX(zv1, zend_ffi_cdata_ce, 0, 1);
4396 		Z_PARAM_ZVAL(zv2)
4397 		Z_PARAM_LONG(size)
4398 	ZEND_PARSE_PARAMETERS_END();
4399 
4400 	cdata1 = (zend_ffi_cdata*)Z_OBJ_P(zv1);
4401 	type1 = ZEND_FFI_TYPE(cdata1->type);
4402 	if (type1->kind == ZEND_FFI_TYPE_POINTER) {
4403 		ptr1 = *(void**)cdata1->ptr;
4404 	} else {
4405 		ptr1 = cdata1->ptr;
4406 		if (type1->kind != ZEND_FFI_TYPE_POINTER && size > type1->size) {
4407 			zend_throw_error(zend_ffi_exception_ce, "Attempt to write over data boundary");
4408 			RETURN_THROWS();
4409 		}
4410 	}
4411 
4412 	ZVAL_DEREF(zv2);
4413 	if (Z_TYPE_P(zv2) == IS_STRING) {
4414 		ptr2 = Z_STRVAL_P(zv2);
4415 		if (size > Z_STRLEN_P(zv2)) {
4416 			zend_throw_error(zend_ffi_exception_ce, "Attempt to read over string boundary");
4417 			RETURN_THROWS();
4418 		}
4419 	} else if (Z_TYPE_P(zv2) == IS_OBJECT && Z_OBJCE_P(zv2) == zend_ffi_cdata_ce) {
4420 		cdata2 = (zend_ffi_cdata*)Z_OBJ_P(zv2);
4421 		type2 = ZEND_FFI_TYPE(cdata2->type);
4422 		if (type2->kind == ZEND_FFI_TYPE_POINTER) {
4423 			ptr2 = *(void**)cdata2->ptr;
4424 		} else {
4425 			ptr2 = cdata2->ptr;
4426 			if (type2->kind != ZEND_FFI_TYPE_POINTER && size > type2->size) {
4427 				zend_throw_error(zend_ffi_exception_ce, "Attempt to read over data boundary");
4428 				RETURN_THROWS();
4429 			}
4430 		}
4431 	} else {
4432 		zend_wrong_parameter_class_error(2, "FFI\\CData or string", zv2);
4433 		RETURN_THROWS();
4434 	}
4435 
4436 	memcpy(ptr1, ptr2, size);
4437 }
4438 /* }}} */
4439 
ZEND_METHOD(FFI,memcmp)4440 ZEND_METHOD(FFI, memcmp) /* {{{ */
4441 {
4442 	zval *zv1, *zv2;
4443 	zend_ffi_cdata *cdata1, *cdata2;
4444 	zend_ffi_type *type1, *type2;
4445 	void *ptr1, *ptr2;
4446 	zend_long size;
4447 	int ret;
4448 
4449 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4450 	ZEND_PARSE_PARAMETERS_START(3, 3)
4451 		Z_PARAM_ZVAL(zv1);
4452 		Z_PARAM_ZVAL(zv2);
4453 		Z_PARAM_LONG(size)
4454 	ZEND_PARSE_PARAMETERS_END();
4455 
4456 	ZVAL_DEREF(zv1);
4457 	if (Z_TYPE_P(zv1) == IS_STRING) {
4458 		ptr1 = Z_STRVAL_P(zv1);
4459 		if (size > Z_STRLEN_P(zv1)) {
4460 			zend_throw_error(zend_ffi_exception_ce, "attempt to read over string boundary");
4461 			RETURN_THROWS();
4462 		}
4463 	} else if (Z_TYPE_P(zv1) == IS_OBJECT && Z_OBJCE_P(zv1) == zend_ffi_cdata_ce) {
4464 		cdata1 = (zend_ffi_cdata*)Z_OBJ_P(zv1);
4465 		type1 = ZEND_FFI_TYPE(cdata1->type);
4466 		if (type1->kind == ZEND_FFI_TYPE_POINTER) {
4467 			ptr1 = *(void**)cdata1->ptr;
4468 		} else {
4469 			ptr1 = cdata1->ptr;
4470 			if (type1->kind != ZEND_FFI_TYPE_POINTER && size > type1->size) {
4471 				zend_throw_error(zend_ffi_exception_ce, "attempt to read over data boundary");
4472 				RETURN_THROWS();
4473 			}
4474 		}
4475 	} else {
4476 		zend_wrong_parameter_class_error(1, "FFI\\CData or string", zv1);
4477 		RETURN_THROWS();
4478 	}
4479 
4480 	ZVAL_DEREF(zv2);
4481 	if (Z_TYPE_P(zv2) == IS_STRING) {
4482 		ptr2 = Z_STRVAL_P(zv2);
4483 		if (size > Z_STRLEN_P(zv2)) {
4484 			zend_throw_error(zend_ffi_exception_ce, "Attempt to read over string boundary");
4485 			RETURN_THROWS();
4486 		}
4487 	} else if (Z_TYPE_P(zv2) == IS_OBJECT && Z_OBJCE_P(zv2) == zend_ffi_cdata_ce) {
4488 		cdata2 = (zend_ffi_cdata*)Z_OBJ_P(zv2);
4489 		type2 = ZEND_FFI_TYPE(cdata2->type);
4490 		if (type2->kind == ZEND_FFI_TYPE_POINTER) {
4491 			ptr2 = *(void**)cdata2->ptr;
4492 		} else {
4493 			ptr2 = cdata2->ptr;
4494 			if (type2->kind != ZEND_FFI_TYPE_POINTER && size > type2->size) {
4495 				zend_throw_error(zend_ffi_exception_ce, "Attempt to read over data boundary");
4496 				RETURN_THROWS();
4497 			}
4498 		}
4499 	} else {
4500 		zend_wrong_parameter_class_error(2, "FFI\\CData or string", zv2);
4501 		RETURN_THROWS();
4502 	}
4503 
4504 	ret = memcmp(ptr1, ptr2, size);
4505 	if (ret == 0) {
4506 		RETVAL_LONG(0);
4507 	} else if (ret < 0) {
4508 		RETVAL_LONG(-1);
4509 	} else {
4510 		RETVAL_LONG(1);
4511 	}
4512 }
4513 /* }}} */
4514 
ZEND_METHOD(FFI,memset)4515 ZEND_METHOD(FFI, memset) /* {{{ */
4516 {
4517 	zval *zv;
4518 	zend_ffi_cdata *cdata;
4519 	zend_ffi_type *type;
4520 	void *ptr;
4521 	zend_long ch, size;
4522 
4523 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4524 	ZEND_PARSE_PARAMETERS_START(3, 3)
4525 		Z_PARAM_OBJECT_OF_CLASS_EX(zv, zend_ffi_cdata_ce, 0, 1);
4526 		Z_PARAM_LONG(ch)
4527 		Z_PARAM_LONG(size)
4528 	ZEND_PARSE_PARAMETERS_END();
4529 
4530 	cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
4531 	type = ZEND_FFI_TYPE(cdata->type);
4532 	if (type->kind == ZEND_FFI_TYPE_POINTER) {
4533 		ptr = *(void**)cdata->ptr;
4534 	} else {
4535 		ptr = cdata->ptr;
4536 		if (type->kind != ZEND_FFI_TYPE_POINTER && size > type->size) {
4537 			zend_throw_error(zend_ffi_exception_ce, "attempt to write over data boundary");
4538 			RETURN_THROWS();
4539 		}
4540 	}
4541 
4542 	memset(ptr, ch, size);
4543 }
4544 /* }}} */
4545 
ZEND_METHOD(FFI,string)4546 ZEND_METHOD(FFI, string) /* {{{ */
4547 {
4548 	zval *zv;
4549 	zend_ffi_cdata *cdata;
4550 	zend_ffi_type *type;
4551 	void *ptr;
4552 	zend_long size;
4553 	bool size_is_null = 1;
4554 
4555 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4556 	ZEND_PARSE_PARAMETERS_START(1, 2)
4557 		Z_PARAM_OBJECT_OF_CLASS_EX(zv, zend_ffi_cdata_ce, 0, 1);
4558 		Z_PARAM_OPTIONAL
4559 		Z_PARAM_LONG_OR_NULL(size, size_is_null)
4560 	ZEND_PARSE_PARAMETERS_END();
4561 
4562 	cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
4563 	type = ZEND_FFI_TYPE(cdata->type);
4564 	if (!size_is_null) {
4565 		if (type->kind == ZEND_FFI_TYPE_POINTER) {
4566 			ptr = *(void**)cdata->ptr;
4567 		} else {
4568 			ptr = cdata->ptr;
4569 			if (type->kind != ZEND_FFI_TYPE_POINTER && size > type->size) {
4570 				zend_throw_error(zend_ffi_exception_ce, "attempt to read over data boundary");
4571 				RETURN_THROWS();
4572 			}
4573 		}
4574 		RETURN_STRINGL((char*)ptr, size);
4575 	} else {
4576 		if (type->kind == ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) {
4577 			ptr = *(void**)cdata->ptr;
4578 		} else if (type->kind == ZEND_FFI_TYPE_ARRAY && ZEND_FFI_TYPE(type->array.type)->kind == ZEND_FFI_TYPE_CHAR) {
4579 			ptr = cdata->ptr;
4580 		} else {
4581 			zend_throw_error(zend_ffi_exception_ce, "FFI\\Cdata is not a C string");
4582 			RETURN_THROWS();
4583 		}
4584 		RETURN_STRING((char*)ptr);
4585 	}
4586 }
4587 /* }}} */
4588 
ZEND_METHOD(FFI,isNull)4589 ZEND_METHOD(FFI, isNull) /* {{{ */
4590 {
4591 	zval *zv;
4592 	zend_ffi_cdata *cdata;
4593 	zend_ffi_type *type;
4594 
4595 	ZEND_FFI_VALIDATE_API_RESTRICTION();
4596 	ZEND_PARSE_PARAMETERS_START(1, 1)
4597 		Z_PARAM_ZVAL(zv);
4598 	ZEND_PARSE_PARAMETERS_END();
4599 
4600 	ZVAL_DEREF(zv);
4601 	if (Z_TYPE_P(zv) != IS_OBJECT || Z_OBJCE_P(zv) != zend_ffi_cdata_ce) {
4602 		zend_wrong_parameter_class_error(1, "FFI\\CData", zv);
4603 		RETURN_THROWS();
4604 	}
4605 
4606 	cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
4607 	type = ZEND_FFI_TYPE(cdata->type);
4608 
4609 	if (type->kind != ZEND_FFI_TYPE_POINTER){
4610 		zend_throw_error(zend_ffi_exception_ce, "FFI\\Cdata is not a pointer");
4611 		RETURN_THROWS();
4612 	}
4613 
4614 	RETURN_BOOL(*(void**)cdata->ptr == NULL);
4615 }
4616 /* }}} */
4617 
4618 
ZEND_METHOD(FFI_CType,getName)4619 ZEND_METHOD(FFI_CType, getName) /* {{{ */
4620 {
4621 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4622 	if (zend_parse_parameters_none() == FAILURE) {
4623 		RETURN_THROWS();
4624 	}
4625 
4626 	zend_ffi_ctype_name_buf buf;
4627 
4628 	buf.start = buf.end = buf.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
4629 	if (!zend_ffi_ctype_name(&buf, ZEND_FFI_TYPE(ctype->type))) {
4630 		RETURN_STR_COPY(Z_OBJ_P(ZEND_THIS)->ce->name);
4631 	} else {
4632 		size_t len = buf.end - buf.start;
4633 		zend_string *res = zend_string_init(buf.start, len, 0);
4634 		RETURN_STR(res);
4635 	}
4636 }
4637 /* }}} */
4638 
ZEND_METHOD(FFI_CType,getKind)4639 ZEND_METHOD(FFI_CType, getKind) /* {{{ */
4640 {
4641 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4642 	zend_ffi_type *type;
4643 
4644 	if (zend_parse_parameters_none() == FAILURE) {
4645 		RETURN_THROWS();
4646 	}
4647 
4648 	type = ZEND_FFI_TYPE(ctype->type);
4649 	RETURN_LONG(type->kind);
4650 }
4651 /* }}} */
4652 
ZEND_METHOD(FFI_CType,getSize)4653 ZEND_METHOD(FFI_CType, getSize) /* {{{ */
4654 {
4655 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4656 	zend_ffi_type *type;
4657 
4658 	if (zend_parse_parameters_none() == FAILURE) {
4659 		RETURN_THROWS();
4660 	}
4661 
4662 	type = ZEND_FFI_TYPE(ctype->type);
4663 	RETURN_LONG(type->size);
4664 }
4665 /* }}} */
4666 
ZEND_METHOD(FFI_CType,getAlignment)4667 ZEND_METHOD(FFI_CType, getAlignment) /* {{{ */
4668 {
4669 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4670 	zend_ffi_type *type;
4671 
4672 	if (zend_parse_parameters_none() == FAILURE) {
4673 		RETURN_THROWS();
4674 	}
4675 
4676 	type = ZEND_FFI_TYPE(ctype->type);
4677 	RETURN_LONG(type->align);
4678 }
4679 /* }}} */
4680 
ZEND_METHOD(FFI_CType,getAttributes)4681 ZEND_METHOD(FFI_CType, getAttributes) /* {{{ */
4682 {
4683 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4684 	zend_ffi_type *type;
4685 
4686 	if (zend_parse_parameters_none() == FAILURE) {
4687 		RETURN_THROWS();
4688 	}
4689 
4690 	type = ZEND_FFI_TYPE(ctype->type);
4691 	RETURN_LONG(type->attr);
4692 }
4693 /* }}} */
4694 
ZEND_METHOD(FFI_CType,getEnumKind)4695 ZEND_METHOD(FFI_CType, getEnumKind) /* {{{ */
4696 {
4697 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4698 	zend_ffi_type *type;
4699 
4700 	if (zend_parse_parameters_none() == FAILURE) {
4701 		RETURN_THROWS();
4702 	}
4703 
4704 	type = ZEND_FFI_TYPE(ctype->type);
4705 	if (type->kind != ZEND_FFI_TYPE_ENUM) {
4706 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not an enumeration");
4707 		RETURN_THROWS();
4708 	}
4709 	RETURN_LONG(type->enumeration.kind);
4710 }
4711 /* }}} */
4712 
ZEND_METHOD(FFI_CType,getArrayElementType)4713 ZEND_METHOD(FFI_CType, getArrayElementType) /* {{{ */
4714 {
4715 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4716 	zend_ffi_type *type;
4717 	zend_ffi_ctype *ret;
4718 
4719 	if (zend_parse_parameters_none() == FAILURE) {
4720 		RETURN_THROWS();
4721 	}
4722 
4723 	type = ZEND_FFI_TYPE(ctype->type);
4724 	if (type->kind != ZEND_FFI_TYPE_ARRAY) {
4725 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not an array");
4726 		RETURN_THROWS();
4727 	}
4728 
4729 	ret = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
4730 	ret->type = ZEND_FFI_TYPE(type->array.type);
4731 	RETURN_OBJ(&ret->std);
4732 }
4733 /* }}} */
4734 
ZEND_METHOD(FFI_CType,getArrayLength)4735 ZEND_METHOD(FFI_CType, getArrayLength) /* {{{ */
4736 {
4737 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4738 	zend_ffi_type *type;
4739 
4740 	if (zend_parse_parameters_none() == FAILURE) {
4741 		RETURN_THROWS();
4742 	}
4743 
4744 	type = ZEND_FFI_TYPE(ctype->type);
4745 	if (type->kind != ZEND_FFI_TYPE_ARRAY) {
4746 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not an array");
4747 		RETURN_THROWS();
4748 	}
4749 	RETURN_LONG(type->array.length);
4750 }
4751 /* }}} */
4752 
ZEND_METHOD(FFI_CType,getPointerType)4753 ZEND_METHOD(FFI_CType, getPointerType) /* {{{ */
4754 {
4755 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4756 	zend_ffi_ctype *ret;
4757 	zend_ffi_type *type;
4758 
4759 	if (zend_parse_parameters_none() == FAILURE) {
4760 		RETURN_THROWS();
4761 	}
4762 
4763 	type = ZEND_FFI_TYPE(ctype->type);
4764 	if (type->kind != ZEND_FFI_TYPE_POINTER) {
4765 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not a pointer");
4766 		RETURN_THROWS();
4767 	}
4768 
4769 	ret = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
4770 	ret->type = ZEND_FFI_TYPE(type->pointer.type);
4771 	RETURN_OBJ(&ret->std);
4772 }
4773 /* }}} */
4774 
ZEND_METHOD(FFI_CType,getStructFieldNames)4775 ZEND_METHOD(FFI_CType, getStructFieldNames) /* {{{ */
4776 {
4777 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4778 	zend_ffi_type *type;
4779 	HashTable *ht;
4780 	zend_string* name;
4781 	zval zv;
4782 
4783 	if (zend_parse_parameters_none() == FAILURE) {
4784 		RETURN_THROWS();
4785 	}
4786 
4787 	type = ZEND_FFI_TYPE(ctype->type);
4788 	if (type->kind != ZEND_FFI_TYPE_STRUCT) {
4789 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not a structure");
4790 		RETURN_THROWS();
4791 	}
4792 
4793 	ht = zend_new_array(zend_hash_num_elements(&type->record.fields));
4794 	RETVAL_ARR(ht);
4795 	ZEND_HASH_MAP_FOREACH_STR_KEY(&type->record.fields, name) {
4796 		ZVAL_STR_COPY(&zv, name);
4797 		zend_hash_next_index_insert_new(ht, &zv);
4798 	} ZEND_HASH_FOREACH_END();
4799 }
4800 /* }}} */
4801 
ZEND_METHOD(FFI_CType,getStructFieldOffset)4802 ZEND_METHOD(FFI_CType, getStructFieldOffset) /* {{{ */
4803 {
4804 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4805 	zend_ffi_type *type;
4806 	zend_string *name;
4807 	zend_ffi_field *ptr;
4808 
4809 	ZEND_PARSE_PARAMETERS_START(1, 1)
4810 		Z_PARAM_STR(name)
4811 	ZEND_PARSE_PARAMETERS_END();
4812 
4813 	type = ZEND_FFI_TYPE(ctype->type);
4814 	if (type->kind != ZEND_FFI_TYPE_STRUCT) {
4815 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not a structure");
4816 		RETURN_THROWS();
4817 	}
4818 
4819 	ptr = zend_hash_find_ptr(&type->record.fields, name);
4820 	if (!ptr) {
4821 		zend_throw_error(zend_ffi_exception_ce, "Wrong field name");
4822 		RETURN_THROWS();
4823 	}
4824 	RETURN_LONG(ptr->offset);
4825 }
4826 /* }}} */
4827 
ZEND_METHOD(FFI_CType,getStructFieldType)4828 ZEND_METHOD(FFI_CType, getStructFieldType) /* {{{ */
4829 {
4830 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4831 	zend_ffi_type *type;
4832 	zend_string *name;
4833 	zend_ffi_field *ptr;
4834 	zend_ffi_ctype *ret;
4835 
4836 	ZEND_PARSE_PARAMETERS_START(1, 1)
4837 		Z_PARAM_STR(name)
4838 	ZEND_PARSE_PARAMETERS_END();
4839 
4840 	type = ZEND_FFI_TYPE(ctype->type);
4841 	if (type->kind != ZEND_FFI_TYPE_STRUCT) {
4842 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not a structure");
4843 		RETURN_THROWS();
4844 	}
4845 
4846 	ptr = zend_hash_find_ptr(&type->record.fields, name);
4847 	if (!ptr) {
4848 		zend_throw_error(zend_ffi_exception_ce, "Wrong field name");
4849 		RETURN_THROWS();
4850 	}
4851 
4852 	ret = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
4853 	ret->type = ZEND_FFI_TYPE(ptr->type);
4854 	RETURN_OBJ(&ret->std);
4855 }
4856 /* }}} */
4857 
ZEND_METHOD(FFI_CType,getFuncABI)4858 ZEND_METHOD(FFI_CType, getFuncABI) /* {{{ */
4859 {
4860 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4861 	zend_ffi_type *type;
4862 
4863 	if (zend_parse_parameters_none() == FAILURE) {
4864 		RETURN_THROWS();
4865 	}
4866 
4867 	type = ZEND_FFI_TYPE(ctype->type);
4868 	if (type->kind != ZEND_FFI_TYPE_FUNC) {
4869 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not a function");
4870 		RETURN_THROWS();
4871 	}
4872 	RETURN_LONG(type->func.abi);
4873 }
4874 /* }}} */
4875 
ZEND_METHOD(FFI_CType,getFuncReturnType)4876 ZEND_METHOD(FFI_CType, getFuncReturnType) /* {{{ */
4877 {
4878 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4879 	zend_ffi_ctype *ret;
4880 	zend_ffi_type *type;
4881 
4882 	if (zend_parse_parameters_none() == FAILURE) {
4883 		RETURN_THROWS();
4884 	}
4885 
4886 	type = ZEND_FFI_TYPE(ctype->type);
4887 	if (type->kind != ZEND_FFI_TYPE_FUNC) {
4888 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not a function");
4889 		RETURN_THROWS();
4890 	}
4891 
4892 	ret = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
4893 	ret->type = ZEND_FFI_TYPE(type->func.ret_type);
4894 	RETURN_OBJ(&ret->std);
4895 }
4896 /* }}} */
4897 
ZEND_METHOD(FFI_CType,getFuncParameterCount)4898 ZEND_METHOD(FFI_CType, getFuncParameterCount) /* {{{ */
4899 {
4900 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4901 	zend_ffi_type *type;
4902 
4903 	if (zend_parse_parameters_none() == FAILURE) {
4904 		RETURN_THROWS();
4905 	}
4906 
4907 	type = ZEND_FFI_TYPE(ctype->type);
4908 	if (type->kind != ZEND_FFI_TYPE_FUNC) {
4909 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not a function");
4910 		RETURN_THROWS();
4911 	}
4912 	RETURN_LONG(type->func.args ? zend_hash_num_elements(type->func.args) : 0);
4913 }
4914 /* }}} */
4915 
ZEND_METHOD(FFI_CType,getFuncParameterType)4916 ZEND_METHOD(FFI_CType, getFuncParameterType) /* {{{ */
4917 {
4918 	zend_ffi_ctype *ctype = (zend_ffi_ctype*)(Z_OBJ_P(ZEND_THIS));
4919 	zend_ffi_type *type, *ptr;
4920 	zend_long n;
4921 	zend_ffi_ctype *ret;
4922 
4923 	ZEND_PARSE_PARAMETERS_START(1, 1)
4924 		Z_PARAM_LONG(n)
4925 	ZEND_PARSE_PARAMETERS_END();
4926 
4927 	type = ZEND_FFI_TYPE(ctype->type);
4928 	if (type->kind != ZEND_FFI_TYPE_FUNC) {
4929 		zend_throw_error(zend_ffi_exception_ce, "FFI\\CType is not a function");
4930 		RETURN_THROWS();
4931 	}
4932 
4933 	if (!type->func.args) {
4934 		zend_throw_error(zend_ffi_exception_ce, "Wrong argument number");
4935 		RETURN_THROWS();
4936 	}
4937 
4938 	ptr = zend_hash_index_find_ptr(type->func.args, n);
4939 	if (!ptr) {
4940 		zend_throw_error(zend_ffi_exception_ce, "Wrong argument number");
4941 		RETURN_THROWS();
4942 	}
4943 
4944 	ret = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
4945 	ret->type = ZEND_FFI_TYPE(ptr);
4946 	RETURN_OBJ(&ret->std);
4947 }
4948 /* }}} */
4949 
zend_ffi_parse_directives(const char * filename,char * code_pos,char ** scope_name,char ** lib,bool preload)4950 static char *zend_ffi_parse_directives(const char *filename, char *code_pos, char **scope_name, char **lib, bool preload) /* {{{ */
4951 {
4952 	char *p;
4953 
4954 	*scope_name = NULL;
4955 	*lib = NULL;
4956 	while (*code_pos == '#') {
4957 		if (strncmp(code_pos, "#define FFI_SCOPE", sizeof("#define FFI_SCOPE") - 1) == 0
4958 		 && (code_pos[sizeof("#define FFI_SCOPE") - 1] == ' '
4959 		  || code_pos[sizeof("#define FFI_SCOPE") - 1] == '\t')) {
4960 			p = code_pos + sizeof("#define FFI_SCOPE");
4961 			while (*p == ' ' || *p == '\t') {
4962 				p++;
4963 			}
4964 			if (*p != '"') {
4965 				if (preload) {
4966 					zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
4967 				} else {
4968 					zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
4969 				}
4970 				return NULL;
4971 			}
4972 			p++;
4973 			if (*scope_name) {
4974 				if (preload) {
4975 					zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_SCOPE defined twice", filename);
4976 				} else {
4977 					zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_SCOPE defined twice", filename);
4978 				}
4979 				return NULL;
4980 			}
4981 			*scope_name = p;
4982 			while (1) {
4983 				if (*p == '\"') {
4984 					*p = 0;
4985 					p++;
4986 					break;
4987 				} else if (*p <= ' ') {
4988 					if (preload) {
4989 						zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
4990 					} else {
4991 						zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
4992 					}
4993 					return NULL;
4994 				}
4995 				p++;
4996 			}
4997 			while (*p == ' ' || *p == '\t') {
4998 				p++;
4999 			}
5000 			while (*p == '\r' || *p == '\n') {
5001 				p++;
5002 			}
5003 			code_pos = p;
5004 		} else if (strncmp(code_pos, "#define FFI_LIB", sizeof("#define FFI_LIB") - 1) == 0
5005 		 && (code_pos[sizeof("#define FFI_LIB") - 1] == ' '
5006 		  || code_pos[sizeof("#define FFI_LIB") - 1] == '\t')) {
5007 			p = code_pos + sizeof("#define FFI_LIB");
5008 			while (*p == ' ' || *p == '\t') {
5009 				p++;
5010 			}
5011 			if (*p != '"') {
5012 				if (preload) {
5013 					zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename);
5014 				} else {
5015 					zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename);
5016 				}
5017 				return NULL;
5018 			}
5019 			p++;
5020 			if (*lib) {
5021 				if (preload) {
5022 					zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_LIB defined twice", filename);
5023 				} else {
5024 					zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_LIB defined twice", filename);
5025 				}
5026 				return NULL;
5027 			}
5028 			*lib = p;
5029 			while (1) {
5030 				if (*p == '\"') {
5031 					*p = 0;
5032 					p++;
5033 					break;
5034 				} else if (*p <= ' ') {
5035 					if (preload) {
5036 						zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename);
5037 					} else {
5038 						zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename);
5039 					}
5040 					return NULL;
5041 				}
5042 				p++;
5043 			}
5044 			while (*p == ' ' || *p == '\t') {
5045 				p++;
5046 			}
5047 			while (*p == '\r' || *p == '\n') {
5048 				p++;
5049 			}
5050 			code_pos = p;
5051 		} else {
5052 			break;
5053 		}
5054 	}
5055 	return code_pos;
5056 }
5057 /* }}} */
5058 
zend_fake_get_constructor(zend_object * object)5059 static ZEND_COLD zend_function *zend_fake_get_constructor(zend_object *object) /* {{{ */
5060 {
5061 	zend_throw_error(NULL, "Instantiation of %s is not allowed", ZSTR_VAL(object->ce->name));
5062 	return NULL;
5063 }
5064 /* }}} */
5065 
zend_bad_array_access(zend_class_entry * ce)5066 static ZEND_COLD zend_never_inline void zend_bad_array_access(zend_class_entry *ce) /* {{{ */
5067 {
5068 	zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
5069 }
5070 /* }}} */
5071 
zend_fake_read_dimension(zend_object * obj,zval * offset,int type,zval * rv)5072 static ZEND_COLD zval *zend_fake_read_dimension(zend_object *obj, zval *offset, int type, zval *rv) /* {{{ */
5073 {
5074 	zend_bad_array_access(obj->ce);
5075 	return NULL;
5076 }
5077 /* }}} */
5078 
zend_fake_write_dimension(zend_object * obj,zval * offset,zval * value)5079 static ZEND_COLD void zend_fake_write_dimension(zend_object *obj, zval *offset, zval *value) /* {{{ */
5080 {
5081 	zend_bad_array_access(obj->ce);
5082 }
5083 /* }}} */
5084 
zend_fake_has_dimension(zend_object * obj,zval * offset,int check_empty)5085 static ZEND_COLD int zend_fake_has_dimension(zend_object *obj, zval *offset, int check_empty) /* {{{ */
5086 {
5087 	zend_bad_array_access(obj->ce);
5088 	return 0;
5089 }
5090 /* }}} */
5091 
zend_fake_unset_dimension(zend_object * obj,zval * offset)5092 static ZEND_COLD void zend_fake_unset_dimension(zend_object *obj, zval *offset) /* {{{ */
5093 {
5094 	zend_bad_array_access(obj->ce);
5095 }
5096 /* }}} */
5097 
zend_bad_property_access(zend_class_entry * ce)5098 static ZEND_COLD zend_never_inline void zend_bad_property_access(zend_class_entry *ce) /* {{{ */
5099 {
5100 	zend_throw_error(NULL, "Cannot access property of object of type %s", ZSTR_VAL(ce->name));
5101 }
5102 /* }}} */
5103 
zend_fake_read_property(zend_object * obj,zend_string * member,int type,void ** cache_slot,zval * rv)5104 static ZEND_COLD zval *zend_fake_read_property(zend_object *obj, zend_string *member, int type, void **cache_slot, zval *rv) /* {{{ */
5105 {
5106 	zend_bad_property_access(obj->ce);
5107 	return &EG(uninitialized_zval);
5108 }
5109 /* }}} */
5110 
zend_fake_write_property(zend_object * obj,zend_string * member,zval * value,void ** cache_slot)5111 static ZEND_COLD zval *zend_fake_write_property(zend_object *obj, zend_string *member, zval *value, void **cache_slot) /* {{{ */
5112 {
5113 	zend_bad_array_access(obj->ce);
5114 	return value;
5115 }
5116 /* }}} */
5117 
zend_fake_has_property(zend_object * obj,zend_string * member,int has_set_exists,void ** cache_slot)5118 static ZEND_COLD int zend_fake_has_property(zend_object *obj, zend_string *member, int has_set_exists, void **cache_slot) /* {{{ */
5119 {
5120 	zend_bad_array_access(obj->ce);
5121 	return 0;
5122 }
5123 /* }}} */
5124 
zend_fake_unset_property(zend_object * obj,zend_string * member,void ** cache_slot)5125 static ZEND_COLD void zend_fake_unset_property(zend_object *obj, zend_string *member, void **cache_slot) /* {{{ */
5126 {
5127 	zend_bad_array_access(obj->ce);
5128 }
5129 /* }}} */
5130 
zend_fake_get_property_ptr_ptr(zend_object * obj,zend_string * member,int type,void ** cache_slot)5131 static zval *zend_fake_get_property_ptr_ptr(zend_object *obj, zend_string *member, int type, void **cache_slot) /* {{{ */
5132 {
5133 	return NULL;
5134 }
5135 /* }}} */
5136 
zend_fake_get_method(zend_object ** obj_ptr,zend_string * method_name,const zval * key)5137 static ZEND_COLD zend_function *zend_fake_get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key) /* {{{ */
5138 {
5139 	zend_class_entry *ce = (*obj_ptr)->ce;
5140 	zend_throw_error(NULL, "Object of type %s does not support method calls", ZSTR_VAL(ce->name));
5141 	return NULL;
5142 }
5143 /* }}} */
5144 
zend_fake_get_properties(zend_object * obj)5145 static HashTable *zend_fake_get_properties(zend_object *obj) /* {{{ */
5146 {
5147 	return (HashTable*)&zend_empty_array;
5148 }
5149 /* }}} */
5150 
zend_fake_get_gc(zend_object * ob,zval ** table,int * n)5151 static HashTable *zend_fake_get_gc(zend_object *ob, zval **table, int *n) /* {{{ */
5152 {
5153 	*table = NULL;
5154 	*n = 0;
5155 	return NULL;
5156 }
5157 /* }}} */
5158 
zend_fake_cast_object(zend_object * obj,zval * result,int type)5159 static zend_result zend_fake_cast_object(zend_object *obj, zval *result, int type)
5160 {
5161 	switch (type) {
5162 		case _IS_BOOL:
5163 			ZVAL_TRUE(result);
5164 			return SUCCESS;
5165 		default:
5166 			return FAILURE;
5167 	}
5168 }
5169 
zend_ffi_use_after_free(void)5170 static ZEND_COLD zend_never_inline void zend_ffi_use_after_free(void) /* {{{ */
5171 {
5172 	zend_throw_error(zend_ffi_exception_ce, "Use after free()");
5173 }
5174 /* }}} */
5175 
zend_ffi_free_clone_obj(zend_object * obj)5176 static zend_object *zend_ffi_free_clone_obj(zend_object *obj) /* {{{ */
5177 {
5178 	zend_ffi_use_after_free();
5179 	return NULL;
5180 }
5181 /* }}} */
5182 
zend_ffi_free_read_dimension(zend_object * obj,zval * offset,int type,zval * rv)5183 static ZEND_COLD zval *zend_ffi_free_read_dimension(zend_object *obj, zval *offset, int type, zval *rv) /* {{{ */
5184 {
5185 	zend_ffi_use_after_free();
5186 	return NULL;
5187 }
5188 /* }}} */
5189 
zend_ffi_free_write_dimension(zend_object * obj,zval * offset,zval * value)5190 static ZEND_COLD void zend_ffi_free_write_dimension(zend_object *obj, zval *offset, zval *value) /* {{{ */
5191 {
5192 	zend_ffi_use_after_free();
5193 }
5194 /* }}} */
5195 
zend_ffi_free_has_dimension(zend_object * obj,zval * offset,int check_empty)5196 static ZEND_COLD int zend_ffi_free_has_dimension(zend_object *obj, zval *offset, int check_empty) /* {{{ */
5197 {
5198 	zend_ffi_use_after_free();
5199 	return 0;
5200 }
5201 /* }}} */
5202 
zend_ffi_free_unset_dimension(zend_object * obj,zval * offset)5203 static ZEND_COLD void zend_ffi_free_unset_dimension(zend_object *obj, zval *offset) /* {{{ */
5204 {
5205 	zend_ffi_use_after_free();
5206 }
5207 /* }}} */
5208 
zend_ffi_free_read_property(zend_object * obj,zend_string * member,int type,void ** cache_slot,zval * rv)5209 static ZEND_COLD zval *zend_ffi_free_read_property(zend_object *obj, zend_string *member, int type, void **cache_slot, zval *rv) /* {{{ */
5210 {
5211 	zend_ffi_use_after_free();
5212 	return &EG(uninitialized_zval);
5213 }
5214 /* }}} */
5215 
zend_ffi_free_write_property(zend_object * obj,zend_string * member,zval * value,void ** cache_slot)5216 static ZEND_COLD zval *zend_ffi_free_write_property(zend_object *obj, zend_string *member, zval *value, void **cache_slot) /* {{{ */
5217 {
5218 	zend_ffi_use_after_free();
5219 	return value;
5220 }
5221 /* }}} */
5222 
zend_ffi_free_has_property(zend_object * obj,zend_string * member,int has_set_exists,void ** cache_slot)5223 static ZEND_COLD int zend_ffi_free_has_property(zend_object *obj, zend_string *member, int has_set_exists, void **cache_slot) /* {{{ */
5224 {
5225 	zend_ffi_use_after_free();
5226 	return 0;
5227 }
5228 /* }}} */
5229 
zend_ffi_free_unset_property(zend_object * obj,zend_string * member,void ** cache_slot)5230 static ZEND_COLD void zend_ffi_free_unset_property(zend_object *obj, zend_string *member, void **cache_slot) /* {{{ */
5231 {
5232 	zend_ffi_use_after_free();
5233 }
5234 /* }}} */
5235 
zend_ffi_free_get_debug_info(zend_object * obj,int * is_temp)5236 static HashTable *zend_ffi_free_get_debug_info(zend_object *obj, int *is_temp) /* {{{ */
5237 {
5238 	zend_ffi_use_after_free();
5239 	return NULL;
5240 }
5241 /* }}} */
5242 
ZEND_INI_MH(OnUpdateFFIEnable)5243 static ZEND_INI_MH(OnUpdateFFIEnable) /* {{{ */
5244 {
5245 	if (zend_string_equals_literal_ci(new_value, "preload")) {
5246 		FFI_G(restriction) = ZEND_FFI_PRELOAD;
5247 	} else {
5248 		FFI_G(restriction) = (zend_ffi_api_restriction)zend_ini_parse_bool(new_value);
5249 	}
5250 	return SUCCESS;
5251 }
5252 /* }}} */
5253 
ZEND_INI_DISP(zend_ffi_enable_displayer_cb)5254 static ZEND_INI_DISP(zend_ffi_enable_displayer_cb) /* {{{ */
5255 {
5256 	if (FFI_G(restriction) == ZEND_FFI_PRELOAD) {
5257 		ZEND_PUTS("preload");
5258 	} else if (FFI_G(restriction) == ZEND_FFI_ENABLED) {
5259 		ZEND_PUTS("On");
5260 	} else {
5261 		ZEND_PUTS("Off");
5262 	}
5263 }
5264 /* }}} */
5265 
5266 ZEND_INI_BEGIN()
5267 	ZEND_INI_ENTRY_EX("ffi.enable", "preload", ZEND_INI_SYSTEM, OnUpdateFFIEnable, zend_ffi_enable_displayer_cb)
5268 	STD_ZEND_INI_ENTRY("ffi.preload", NULL, ZEND_INI_SYSTEM, OnUpdateString, preload, zend_ffi_globals, ffi_globals)
ZEND_INI_END()5269 ZEND_INI_END()
5270 
5271 static zend_result zend_ffi_preload_glob(const char *filename) /* {{{ */
5272 {
5273 #ifdef HAVE_GLOB
5274 	glob_t globbuf;
5275 	int    ret;
5276 	unsigned int i;
5277 
5278 	memset(&globbuf, 0, sizeof(glob_t));
5279 
5280 	ret = glob(filename, 0, NULL, &globbuf);
5281 #ifdef GLOB_NOMATCH
5282 	if (ret == GLOB_NOMATCH || !globbuf.gl_pathc) {
5283 #else
5284 	if (!globbuf.gl_pathc) {
5285 #endif
5286 		/* pass */
5287 	} else {
5288 		for(i=0 ; i<globbuf.gl_pathc; i++) {
5289 			zend_ffi *ffi = zend_ffi_load(globbuf.gl_pathv[i], 1);
5290 			if (!ffi) {
5291 				globfree(&globbuf);
5292 				return FAILURE;
5293 			}
5294 			efree(ffi);
5295 		}
5296 		globfree(&globbuf);
5297 	}
5298 #else
5299 	zend_ffi *ffi = zend_ffi_load(filename, 1);
5300 	if (!ffi) {
5301 		return FAILURE;
5302 	}
5303 	efree(ffi);
5304 #endif
5305 
5306 	return SUCCESS;
5307 }
5308 /* }}} */
5309 
5310 static zend_result zend_ffi_preload(char *preload) /* {{{ */
5311 {
5312 	zend_ffi *ffi;
5313 	char *s = NULL, *e, *filename;
5314 	bool is_glob = 0;
5315 
5316 	e = preload;
5317 	while (*e) {
5318 		switch (*e) {
5319 			case ZEND_PATHS_SEPARATOR:
5320 				if (s) {
5321 					filename = estrndup(s, e-s);
5322 					s = NULL;
5323 					if (!is_glob) {
5324 						ffi = zend_ffi_load(filename, 1);
5325 						efree(filename);
5326 						if (!ffi) {
5327 							return FAILURE;
5328 						}
5329 						efree(ffi);
5330 					} else {
5331 						zend_result ret = zend_ffi_preload_glob(filename);
5332 
5333 						efree(filename);
5334 						if (ret == FAILURE) {
5335 							return FAILURE;
5336 						}
5337 						is_glob = 0;
5338 					}
5339 				}
5340 				break;
5341 			case '*':
5342 			case '?':
5343 			case '[':
5344 				is_glob = 1;
5345 				break;
5346 			default:
5347 				if (!s) {
5348 					s = e;
5349 				}
5350 				break;
5351 		}
5352 		e++;
5353 	}
5354 	if (s) {
5355 		filename = estrndup(s, e-s);
5356 		if (!is_glob) {
5357 			ffi = zend_ffi_load(filename, 1);
5358 			efree(filename);
5359 			if (!ffi) {
5360 				return FAILURE;
5361 			}
5362 			efree(ffi);
5363 		} else {
5364 			zend_result ret = zend_ffi_preload_glob(filename);
5365 			efree(filename);
5366 			if (ret == FAILURE) {
5367 				return FAILURE;
5368 			}
5369 		}
5370 	}
5371 
5372 	return SUCCESS;
5373 }
5374 /* }}} */
5375 
5376 /* The startup code for observers adds a temporary to each function for internal use.
5377  * The "new", "cast", and "type" functions in FFI are both static and non-static.
5378  * Only the static versions are in the function table and the non-static versions are not.
5379  * This means the non-static versions will be skipped by the observers startup code.
5380  * This function fixes that by incrementing the temporary count for the non-static versions.
5381  */
5382 static zend_result (*prev_zend_post_startup_cb)(void);
5383 static zend_result ffi_fixup_temporaries(void) {
5384 	if (ZEND_OBSERVER_ENABLED) {
5385 		++zend_ffi_new_fn.T;
5386 		++zend_ffi_cast_fn.T;
5387 		++zend_ffi_type_fn.T;
5388 	}
5389 	if (prev_zend_post_startup_cb) {
5390 		return prev_zend_post_startup_cb();
5391 	}
5392 	return SUCCESS;
5393 }
5394 
5395 /* {{{ ZEND_MINIT_FUNCTION */
5396 ZEND_MINIT_FUNCTION(ffi)
5397 {
5398 	REGISTER_INI_ENTRIES();
5399 
5400 	FFI_G(is_cli) = strcmp(sapi_module.name, "cli") == 0;
5401 
5402 	zend_ffi_exception_ce = register_class_FFI_Exception(zend_ce_error);
5403 
5404 	zend_ffi_parser_exception_ce = register_class_FFI_ParserException(zend_ffi_exception_ce);
5405 
5406 	zend_ffi_ce = register_class_FFI();
5407 	zend_ffi_ce->create_object = zend_ffi_new;
5408 	zend_ffi_ce->default_object_handlers = &zend_ffi_handlers;
5409 
5410 	memcpy(&zend_ffi_new_fn, zend_hash_str_find_ptr(&zend_ffi_ce->function_table, "new", sizeof("new")-1), sizeof(zend_internal_function));
5411 	zend_ffi_new_fn.fn_flags &= ~ZEND_ACC_STATIC;
5412 	memcpy(&zend_ffi_cast_fn, zend_hash_str_find_ptr(&zend_ffi_ce->function_table, "cast", sizeof("cast")-1), sizeof(zend_internal_function));
5413 	zend_ffi_cast_fn.fn_flags &= ~ZEND_ACC_STATIC;
5414 	memcpy(&zend_ffi_type_fn, zend_hash_str_find_ptr(&zend_ffi_ce->function_table, "type", sizeof("type")-1), sizeof(zend_internal_function));
5415 	zend_ffi_type_fn.fn_flags &= ~ZEND_ACC_STATIC;
5416 
5417 	prev_zend_post_startup_cb = zend_post_startup_cb;
5418 	zend_post_startup_cb = ffi_fixup_temporaries;
5419 
5420 	memcpy(&zend_ffi_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
5421 	zend_ffi_handlers.get_constructor      = zend_fake_get_constructor;
5422 	zend_ffi_handlers.free_obj             = zend_ffi_free_obj;
5423 	zend_ffi_handlers.clone_obj            = NULL;
5424 	zend_ffi_handlers.read_property        = zend_ffi_read_var;
5425 	zend_ffi_handlers.write_property       = zend_ffi_write_var;
5426 	zend_ffi_handlers.read_dimension       = zend_fake_read_dimension;
5427 	zend_ffi_handlers.write_dimension      = zend_fake_write_dimension;
5428 	zend_ffi_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
5429 	zend_ffi_handlers.has_property         = zend_fake_has_property;
5430 	zend_ffi_handlers.unset_property       = zend_fake_unset_property;
5431 	zend_ffi_handlers.has_dimension        = zend_fake_has_dimension;
5432 	zend_ffi_handlers.unset_dimension      = zend_fake_unset_dimension;
5433 	zend_ffi_handlers.get_method           = zend_ffi_get_func;
5434 	zend_ffi_handlers.compare              = NULL;
5435 	zend_ffi_handlers.cast_object          = zend_fake_cast_object;
5436 	zend_ffi_handlers.get_debug_info       = NULL;
5437 	zend_ffi_handlers.get_closure          = NULL;
5438 	zend_ffi_handlers.get_properties       = zend_fake_get_properties;
5439 	zend_ffi_handlers.get_gc               = zend_fake_get_gc;
5440 
5441 	zend_ffi_cdata_ce = register_class_FFI_CData();
5442 	zend_ffi_cdata_ce->create_object = zend_ffi_cdata_new;
5443 	zend_ffi_cdata_ce->default_object_handlers = &zend_ffi_cdata_handlers;
5444 	zend_ffi_cdata_ce->get_iterator = zend_ffi_cdata_get_iterator;
5445 
5446 	memcpy(&zend_ffi_cdata_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
5447 	zend_ffi_cdata_handlers.get_constructor      = zend_fake_get_constructor;
5448 	zend_ffi_cdata_handlers.free_obj             = zend_ffi_cdata_free_obj;
5449 	zend_ffi_cdata_handlers.clone_obj            = zend_ffi_cdata_clone_obj;
5450 	zend_ffi_cdata_handlers.read_property        = zend_ffi_cdata_read_field;
5451 	zend_ffi_cdata_handlers.write_property       = zend_ffi_cdata_write_field;
5452 	zend_ffi_cdata_handlers.read_dimension       = zend_ffi_cdata_read_dim;
5453 	zend_ffi_cdata_handlers.write_dimension      = zend_ffi_cdata_write_dim;
5454 	zend_ffi_cdata_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
5455 	zend_ffi_cdata_handlers.has_property         = zend_fake_has_property;
5456 	zend_ffi_cdata_handlers.unset_property       = zend_fake_unset_property;
5457 	zend_ffi_cdata_handlers.has_dimension        = zend_fake_has_dimension;
5458 	zend_ffi_cdata_handlers.unset_dimension      = zend_fake_unset_dimension;
5459 	zend_ffi_cdata_handlers.get_method           = zend_fake_get_method;
5460 	zend_ffi_cdata_handlers.get_class_name       = zend_ffi_cdata_get_class_name;
5461 	zend_ffi_cdata_handlers.do_operation         = zend_ffi_cdata_do_operation;
5462 	zend_ffi_cdata_handlers.compare              = zend_ffi_cdata_compare_objects;
5463 	zend_ffi_cdata_handlers.cast_object          = zend_ffi_cdata_cast_object;
5464 	zend_ffi_cdata_handlers.count_elements       = zend_ffi_cdata_count_elements;
5465 	zend_ffi_cdata_handlers.get_debug_info       = zend_ffi_cdata_get_debug_info;
5466 	zend_ffi_cdata_handlers.get_closure          = zend_ffi_cdata_get_closure;
5467 	zend_ffi_cdata_handlers.get_properties       = zend_fake_get_properties;
5468 	zend_ffi_cdata_handlers.get_gc               = zend_fake_get_gc;
5469 
5470 	memcpy(&zend_ffi_cdata_value_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
5471 	zend_ffi_cdata_value_handlers.get_constructor      = zend_fake_get_constructor;
5472 	zend_ffi_cdata_value_handlers.free_obj             = zend_ffi_cdata_free_obj;
5473 	zend_ffi_cdata_value_handlers.clone_obj            = zend_ffi_cdata_clone_obj;
5474 	zend_ffi_cdata_value_handlers.read_property        = zend_ffi_cdata_get;
5475 	zend_ffi_cdata_value_handlers.write_property       = zend_ffi_cdata_set;
5476 	zend_ffi_cdata_value_handlers.read_dimension       = zend_fake_read_dimension;
5477 	zend_ffi_cdata_value_handlers.write_dimension      = zend_fake_write_dimension;
5478 	zend_ffi_cdata_value_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
5479 	zend_ffi_cdata_value_handlers.has_property         = zend_fake_has_property;
5480 	zend_ffi_cdata_value_handlers.unset_property       = zend_fake_unset_property;
5481 	zend_ffi_cdata_value_handlers.has_dimension        = zend_fake_has_dimension;
5482 	zend_ffi_cdata_value_handlers.unset_dimension      = zend_fake_unset_dimension;
5483 	zend_ffi_cdata_value_handlers.get_method           = zend_fake_get_method;
5484 	zend_ffi_cdata_value_handlers.get_class_name       = zend_ffi_cdata_get_class_name;
5485 	zend_ffi_cdata_value_handlers.compare              = zend_ffi_cdata_compare_objects;
5486 	zend_ffi_cdata_value_handlers.cast_object          = zend_ffi_cdata_cast_object;
5487 	zend_ffi_cdata_value_handlers.count_elements       = NULL;
5488 	zend_ffi_cdata_value_handlers.get_debug_info       = zend_ffi_cdata_get_debug_info;
5489 	zend_ffi_cdata_value_handlers.get_closure          = NULL;
5490 	zend_ffi_cdata_value_handlers.get_properties       = zend_fake_get_properties;
5491 	zend_ffi_cdata_value_handlers.get_gc               = zend_fake_get_gc;
5492 
5493 	memcpy(&zend_ffi_cdata_free_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
5494 	zend_ffi_cdata_free_handlers.get_constructor      = zend_fake_get_constructor;
5495 	zend_ffi_cdata_free_handlers.free_obj             = zend_ffi_cdata_free_obj;
5496 	zend_ffi_cdata_free_handlers.clone_obj            = zend_ffi_free_clone_obj;
5497 	zend_ffi_cdata_free_handlers.read_property        = zend_ffi_free_read_property;
5498 	zend_ffi_cdata_free_handlers.write_property       = zend_ffi_free_write_property;
5499 	zend_ffi_cdata_free_handlers.read_dimension       = zend_ffi_free_read_dimension;
5500 	zend_ffi_cdata_free_handlers.write_dimension      = zend_ffi_free_write_dimension;
5501 	zend_ffi_cdata_free_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
5502 	zend_ffi_cdata_free_handlers.has_property         = zend_ffi_free_has_property;
5503 	zend_ffi_cdata_free_handlers.unset_property       = zend_ffi_free_unset_property;
5504 	zend_ffi_cdata_free_handlers.has_dimension        = zend_ffi_free_has_dimension;
5505 	zend_ffi_cdata_free_handlers.unset_dimension      = zend_ffi_free_unset_dimension;
5506 	zend_ffi_cdata_free_handlers.get_method           = zend_fake_get_method;
5507 	zend_ffi_cdata_free_handlers.get_class_name       = zend_ffi_cdata_get_class_name;
5508 	zend_ffi_cdata_free_handlers.compare              = zend_ffi_cdata_compare_objects;
5509 	zend_ffi_cdata_free_handlers.cast_object          = zend_fake_cast_object;
5510 	zend_ffi_cdata_free_handlers.count_elements       = NULL;
5511 	zend_ffi_cdata_free_handlers.get_debug_info       = zend_ffi_free_get_debug_info;
5512 	zend_ffi_cdata_free_handlers.get_closure          = NULL;
5513 	zend_ffi_cdata_free_handlers.get_properties       = zend_fake_get_properties;
5514 	zend_ffi_cdata_free_handlers.get_gc               = zend_fake_get_gc;
5515 
5516 	zend_ffi_ctype_ce = register_class_FFI_CType();
5517 	zend_ffi_ctype_ce->create_object = zend_ffi_ctype_new;
5518 	zend_ffi_ctype_ce->default_object_handlers = &zend_ffi_ctype_handlers;
5519 
5520 	memcpy(&zend_ffi_ctype_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
5521 	zend_ffi_ctype_handlers.get_constructor      = zend_fake_get_constructor;
5522 	zend_ffi_ctype_handlers.free_obj             = zend_ffi_ctype_free_obj;
5523 	zend_ffi_ctype_handlers.clone_obj            = NULL;
5524 	zend_ffi_ctype_handlers.read_property        = zend_fake_read_property;
5525 	zend_ffi_ctype_handlers.write_property       = zend_fake_write_property;
5526 	zend_ffi_ctype_handlers.read_dimension       = zend_fake_read_dimension;
5527 	zend_ffi_ctype_handlers.write_dimension      = zend_fake_write_dimension;
5528 	zend_ffi_ctype_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
5529 	zend_ffi_ctype_handlers.has_property         = zend_fake_has_property;
5530 	zend_ffi_ctype_handlers.unset_property       = zend_fake_unset_property;
5531 	zend_ffi_ctype_handlers.has_dimension        = zend_fake_has_dimension;
5532 	zend_ffi_ctype_handlers.unset_dimension      = zend_fake_unset_dimension;
5533 	//zend_ffi_ctype_handlers.get_method           = zend_fake_get_method;
5534 	zend_ffi_ctype_handlers.get_class_name       = zend_ffi_ctype_get_class_name;
5535 	zend_ffi_ctype_handlers.compare              = zend_ffi_ctype_compare_objects;
5536 	zend_ffi_ctype_handlers.cast_object          = zend_fake_cast_object;
5537 	zend_ffi_ctype_handlers.count_elements       = NULL;
5538 	zend_ffi_ctype_handlers.get_debug_info       = zend_ffi_ctype_get_debug_info;
5539 	zend_ffi_ctype_handlers.get_closure          = NULL;
5540 	zend_ffi_ctype_handlers.get_properties       = zend_fake_get_properties;
5541 	zend_ffi_ctype_handlers.get_gc               = zend_fake_get_gc;
5542 
5543 	if (FFI_G(preload)) {
5544 		return zend_ffi_preload(FFI_G(preload));
5545 	}
5546 
5547 	return SUCCESS;
5548 }
5549 /* }}} */
5550 
5551 /* {{{ ZEND_RSHUTDOWN_FUNCTION */
5552 ZEND_RSHUTDOWN_FUNCTION(ffi)
5553 {
5554 	if (FFI_G(callbacks)) {
5555 		zend_hash_destroy(FFI_G(callbacks));
5556 		efree(FFI_G(callbacks));
5557 		FFI_G(callbacks) = NULL;
5558 	}
5559 	if (FFI_G(weak_types)) {
5560 #if 0
5561 		fprintf(stderr, "WeakTypes: %d\n", zend_hash_num_elements(FFI_G(weak_types)));
5562 #endif
5563 		zend_hash_destroy(FFI_G(weak_types));
5564 		efree(FFI_G(weak_types));
5565 		FFI_G(weak_types) = NULL;
5566 	}
5567 	return SUCCESS;
5568 }
5569 /* }}} */
5570 
5571 /* {{{ ZEND_MINFO_FUNCTION */
5572 ZEND_MINFO_FUNCTION(ffi)
5573 {
5574 	php_info_print_table_start();
5575 	php_info_print_table_row(2, "FFI support", "enabled");
5576 	php_info_print_table_end();
5577 
5578 	DISPLAY_INI_ENTRIES();
5579 }
5580 /* }}} */
5581 
5582 static const zend_ffi_type zend_ffi_type_void = {.kind=ZEND_FFI_TYPE_VOID, .size=1, .align=1};
5583 static const zend_ffi_type zend_ffi_type_char = {.kind=ZEND_FFI_TYPE_CHAR, .size=1, .align=_Alignof(char)};
5584 static const zend_ffi_type zend_ffi_type_bool = {.kind=ZEND_FFI_TYPE_BOOL, .size=1, .align=_Alignof(uint8_t)};
5585 static const zend_ffi_type zend_ffi_type_sint8 = {.kind=ZEND_FFI_TYPE_SINT8, .size=1, .align=_Alignof(int8_t)};
5586 static const zend_ffi_type zend_ffi_type_uint8 = {.kind=ZEND_FFI_TYPE_UINT8, .size=1, .align=_Alignof(uint8_t)};
5587 static const zend_ffi_type zend_ffi_type_sint16 = {.kind=ZEND_FFI_TYPE_SINT16, .size=2, .align=_Alignof(int16_t)};
5588 static const zend_ffi_type zend_ffi_type_uint16 = {.kind=ZEND_FFI_TYPE_UINT16, .size=2, .align=_Alignof(uint16_t)};
5589 static const zend_ffi_type zend_ffi_type_sint32 = {.kind=ZEND_FFI_TYPE_SINT32, .size=4, .align=_Alignof(int32_t)};
5590 static const zend_ffi_type zend_ffi_type_uint32 = {.kind=ZEND_FFI_TYPE_UINT32, .size=4, .align=_Alignof(uint32_t)};
5591 static const zend_ffi_type zend_ffi_type_sint64 = {.kind=ZEND_FFI_TYPE_SINT64, .size=8, .align=_Alignof(int64_t)};
5592 static const zend_ffi_type zend_ffi_type_uint64 = {.kind=ZEND_FFI_TYPE_UINT64, .size=8, .align=_Alignof(uint64_t)};
5593 static const zend_ffi_type zend_ffi_type_float = {.kind=ZEND_FFI_TYPE_FLOAT, .size=sizeof(float), .align=_Alignof(float)};
5594 static const zend_ffi_type zend_ffi_type_double = {.kind=ZEND_FFI_TYPE_DOUBLE, .size=sizeof(double), .align=_Alignof(double)};
5595 
5596 #ifdef HAVE_LONG_DOUBLE
5597 static const zend_ffi_type zend_ffi_type_long_double = {.kind=ZEND_FFI_TYPE_LONGDOUBLE, .size=sizeof(long double), .align=_Alignof(long double)};
5598 #endif
5599 
5600 static const zend_ffi_type zend_ffi_type_ptr = {.kind=ZEND_FFI_TYPE_POINTER, .size=sizeof(void*), .align=_Alignof(void*), .pointer.type = (zend_ffi_type*)&zend_ffi_type_void};
5601 
5602 static const struct {
5603 	const char *name;
5604 	const zend_ffi_type *type;
5605 } zend_ffi_types[] = {
5606 	{"void",        &zend_ffi_type_void},
5607 	{"char",        &zend_ffi_type_char},
5608 	{"bool",        &zend_ffi_type_bool},
5609 	{"int8_t",      &zend_ffi_type_sint8},
5610 	{"uint8_t",     &zend_ffi_type_uint8},
5611 	{"int16_t",     &zend_ffi_type_sint16},
5612 	{"uint16_t",    &zend_ffi_type_uint16},
5613 	{"int32_t",     &zend_ffi_type_sint32},
5614 	{"uint32_t",    &zend_ffi_type_uint32},
5615 	{"int64_t",     &zend_ffi_type_sint64},
5616 	{"uint64_t",    &zend_ffi_type_uint64},
5617 	{"float",       &zend_ffi_type_float},
5618 	{"double",      &zend_ffi_type_double},
5619 #ifdef HAVE_LONG_DOUBLE
5620 	{"long double", &zend_ffi_type_long_double},
5621 #endif
5622 #if SIZEOF_SIZE_T == 4
5623 	{"uintptr_t",  &zend_ffi_type_uint32},
5624 	{"intptr_t",   &zend_ffi_type_sint32},
5625 	{"size_t",     &zend_ffi_type_uint32},
5626 	{"ssize_t",    &zend_ffi_type_sint32},
5627 	{"ptrdiff_t",  &zend_ffi_type_sint32},
5628 #else
5629 	{"uintptr_t",  &zend_ffi_type_uint64},
5630 	{"intptr_t",   &zend_ffi_type_sint64},
5631 	{"size_t",     &zend_ffi_type_uint64},
5632 	{"ssize_t",    &zend_ffi_type_sint64},
5633 	{"ptrdiff_t",  &zend_ffi_type_sint64},
5634 #endif
5635 #if SIZEOF_OFF_T == 4
5636 	{"off_t",      &zend_ffi_type_sint32},
5637 #else
5638 	{"off_t",      &zend_ffi_type_sint64},
5639 #endif
5640 
5641 	{"va_list",           &zend_ffi_type_ptr},
5642 	{"__builtin_va_list", &zend_ffi_type_ptr},
5643 	{"__gnuc_va_list",    &zend_ffi_type_ptr},
5644 };
5645 
5646 /* {{{ ZEND_GINIT_FUNCTION */
5647 static ZEND_GINIT_FUNCTION(ffi)
5648 {
5649 	size_t i;
5650 
5651 #if defined(COMPILE_DL_FFI) && defined(ZTS)
5652 	ZEND_TSRMLS_CACHE_UPDATE();
5653 #endif
5654 	memset(ffi_globals, 0, sizeof(*ffi_globals));
5655 	zend_hash_init(&ffi_globals->types, 0, NULL, NULL, 1);
5656 	for (i = 0; i < sizeof(zend_ffi_types)/sizeof(zend_ffi_types[0]); i++) {
5657 		zend_hash_str_add_new_ptr(&ffi_globals->types, zend_ffi_types[i].name, strlen(zend_ffi_types[i].name), (void*)zend_ffi_types[i].type);
5658 	}
5659 }
5660 /* }}} */
5661 
5662 /* {{{ ZEND_GINIT_FUNCTION */
5663 static ZEND_GSHUTDOWN_FUNCTION(ffi)
5664 {
5665 	if (ffi_globals->scopes) {
5666 		zend_hash_destroy(ffi_globals->scopes);
5667 		free(ffi_globals->scopes);
5668 	}
5669 	zend_hash_destroy(&ffi_globals->types);
5670 }
5671 /* }}} */
5672 
5673 /* {{{ ffi_module_entry */
5674 zend_module_entry ffi_module_entry = {
5675 	STANDARD_MODULE_HEADER,
5676 	"FFI",					/* Extension name */
5677 	NULL,					/* zend_function_entry */
5678 	ZEND_MINIT(ffi),		/* ZEND_MINIT - Module initialization */
5679 	NULL,					/* ZEND_MSHUTDOWN - Module shutdown */
5680 	NULL,					/* ZEND_RINIT - Request initialization */
5681 	ZEND_RSHUTDOWN(ffi),	/* ZEND_RSHUTDOWN - Request shutdown */
5682 	ZEND_MINFO(ffi),		/* ZEND_MINFO - Module info */
5683 	PHP_VERSION,			/* Version */
5684 	ZEND_MODULE_GLOBALS(ffi),
5685 	ZEND_GINIT(ffi),
5686 	ZEND_GSHUTDOWN(ffi),
5687 	NULL,
5688 	STANDARD_MODULE_PROPERTIES_EX
5689 };
5690 /* }}} */
5691 
5692 #ifdef COMPILE_DL_FFI
5693 # ifdef ZTS
5694 ZEND_TSRMLS_CACHE_DEFINE()
5695 # endif
5696 ZEND_GET_MODULE(ffi)
5697 #endif
5698 
5699 /* parser callbacks */
5700 void zend_ffi_parser_error(const char *format, ...) /* {{{ */
5701 {
5702 	va_list va;
5703 	char *message = NULL;
5704 
5705 	va_start(va, format);
5706 	zend_vspprintf(&message, 0, format, va);
5707 
5708 	if (EG(current_execute_data)) {
5709 		zend_throw_exception(zend_ffi_parser_exception_ce, message, 0);
5710 	} else {
5711 		zend_error(E_WARNING, "FFI Parser: %s", message);
5712 	}
5713 
5714 	efree(message);
5715 	va_end(va);
5716 
5717 	LONGJMP(FFI_G(bailout), FAILURE);
5718 }
5719 /* }}} */
5720 
5721 static void zend_ffi_finalize_type(zend_ffi_dcl *dcl) /* {{{ */
5722 {
5723 	if (!dcl->type) {
5724 		switch (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) {
5725 			case ZEND_FFI_DCL_VOID:
5726 				dcl->type = (zend_ffi_type*)&zend_ffi_type_void;
5727 				break;
5728 			case ZEND_FFI_DCL_CHAR:
5729 				dcl->type = (zend_ffi_type*)&zend_ffi_type_char;
5730 				break;
5731 			case ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SIGNED:
5732 				dcl->type = (zend_ffi_type*)&zend_ffi_type_sint8;
5733 				break;
5734 			case ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_UNSIGNED:
5735 			case ZEND_FFI_DCL_BOOL:
5736 				dcl->type = (zend_ffi_type*)&zend_ffi_type_uint8;
5737 				break;
5738 			case ZEND_FFI_DCL_SHORT:
5739 			case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_SIGNED:
5740 			case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT:
5741 			case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
5742 				dcl->type = (zend_ffi_type*)&zend_ffi_type_sint16;
5743 				break;
5744 			case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_UNSIGNED:
5745 			case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
5746 				dcl->type = (zend_ffi_type*)&zend_ffi_type_uint16;
5747 				break;
5748 			case ZEND_FFI_DCL_INT:
5749 			case ZEND_FFI_DCL_SIGNED:
5750 			case ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
5751 				dcl->type = (zend_ffi_type*)&zend_ffi_type_sint32;
5752 				break;
5753 			case ZEND_FFI_DCL_UNSIGNED:
5754 			case ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
5755 				dcl->type = (zend_ffi_type*)&zend_ffi_type_uint32;
5756 				break;
5757 			case ZEND_FFI_DCL_LONG:
5758 			case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED:
5759 			case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_INT:
5760 			case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
5761 				if (sizeof(long) == 4) {
5762 					dcl->type = (zend_ffi_type*)&zend_ffi_type_sint32;
5763 				} else {
5764 					dcl->type = (zend_ffi_type*)&zend_ffi_type_sint64;
5765 				}
5766 				break;
5767 			case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED:
5768 			case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
5769 				if (sizeof(long) == 4) {
5770 					dcl->type = (zend_ffi_type*)&zend_ffi_type_uint32;
5771 				} else {
5772 					dcl->type = (zend_ffi_type*)&zend_ffi_type_uint64;
5773 				}
5774 				break;
5775 			case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG:
5776 			case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED:
5777 			case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_INT:
5778 			case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
5779 				dcl->type = (zend_ffi_type*)&zend_ffi_type_sint64;
5780 				break;
5781 			case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED:
5782 			case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
5783 				dcl->type = (zend_ffi_type*)&zend_ffi_type_uint64;
5784 				break;
5785 			case ZEND_FFI_DCL_FLOAT:
5786 				dcl->type = (zend_ffi_type*)&zend_ffi_type_float;
5787 				break;
5788 			case ZEND_FFI_DCL_DOUBLE:
5789 				dcl->type = (zend_ffi_type*)&zend_ffi_type_double;
5790 				break;
5791 			case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_DOUBLE:
5792 #ifdef _WIN32
5793 				dcl->type = (zend_ffi_type*)&zend_ffi_type_double;
5794 #else
5795 				dcl->type = (zend_ffi_type*)&zend_ffi_type_long_double;
5796 #endif
5797 				break;
5798 			case ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_COMPLEX:
5799 			case ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_COMPLEX:
5800 			case ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_COMPLEX:
5801 				zend_ffi_parser_error("Unsupported type _Complex at line %d", FFI_G(line));
5802 				break;
5803 			default:
5804 				zend_ffi_parser_error("Unsupported type specifier combination at line %d", FFI_G(line));
5805 				break;
5806 		}
5807 		dcl->flags &= ~ZEND_FFI_DCL_TYPE_SPECIFIERS;
5808 		dcl->flags |= ZEND_FFI_DCL_TYPEDEF_NAME;
5809 	}
5810 }
5811 /* }}} */
5812 
5813 bool zend_ffi_is_typedef_name(const char *name, size_t name_len) /* {{{ */
5814 {
5815 	zend_ffi_symbol *sym;
5816 	zend_ffi_type *type;
5817 
5818 	if (FFI_G(symbols)) {
5819 		sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
5820 		if (sym) {
5821 			return (sym->kind == ZEND_FFI_SYM_TYPE);
5822 		}
5823 	}
5824 	type = zend_hash_str_find_ptr(&FFI_G(types), name, name_len);
5825 	if (type) {
5826 		return 1;
5827 	}
5828 	return 0;
5829 }
5830 /* }}} */
5831 
5832 void zend_ffi_resolve_typedef(const char *name, size_t name_len, zend_ffi_dcl *dcl) /* {{{ */
5833 {
5834 	zend_ffi_symbol *sym;
5835 	zend_ffi_type *type;
5836 
5837 	if (FFI_G(symbols)) {
5838 		sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
5839 		if (sym && sym->kind == ZEND_FFI_SYM_TYPE) {
5840 			dcl->type = ZEND_FFI_TYPE(sym->type);
5841 			if (sym->is_const) {
5842 				dcl->attr |= ZEND_FFI_ATTR_CONST;
5843 			}
5844 			return;
5845 		}
5846 	}
5847 	type = zend_hash_str_find_ptr(&FFI_G(types), name, name_len);
5848 	if (type) {
5849 		dcl->type = type;
5850 		return;
5851 	}
5852 	zend_ffi_parser_error("Undefined C type \"%.*s\" at line %d", name_len, name, FFI_G(line));
5853 }
5854 /* }}} */
5855 
5856 void zend_ffi_resolve_const(const char *name, size_t name_len, zend_ffi_val *val) /* {{{ */
5857 {
5858 	zend_ffi_symbol *sym;
5859 
5860 	if (UNEXPECTED(FFI_G(attribute_parsing))) {
5861 		val->kind = ZEND_FFI_VAL_NAME;
5862 		val->str = name;
5863 		val->len = name_len;
5864 		return;
5865 	} else if (FFI_G(symbols)) {
5866 		sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
5867 		if (sym && sym->kind == ZEND_FFI_SYM_CONST) {
5868 			val->i64 = sym->value;
5869 			switch (sym->type->kind) {
5870 				case ZEND_FFI_TYPE_SINT8:
5871 				case ZEND_FFI_TYPE_SINT16:
5872 				case ZEND_FFI_TYPE_SINT32:
5873 					val->kind = ZEND_FFI_VAL_INT32;
5874 					break;
5875 				case ZEND_FFI_TYPE_SINT64:
5876 					val->kind = ZEND_FFI_VAL_INT64;
5877 					break;
5878 				case ZEND_FFI_TYPE_UINT8:
5879 				case ZEND_FFI_TYPE_UINT16:
5880 				case ZEND_FFI_TYPE_UINT32:
5881 					val->kind = ZEND_FFI_VAL_UINT32;
5882 					break;
5883 				case ZEND_FFI_TYPE_UINT64:
5884 					val->kind = ZEND_FFI_VAL_UINT64;
5885 					break;
5886 				default:
5887 					ZEND_UNREACHABLE();
5888 			}
5889 			return;
5890 		}
5891 	}
5892 	val->kind = ZEND_FFI_VAL_ERROR;
5893 }
5894 /* }}} */
5895 
5896 void zend_ffi_make_enum_type(zend_ffi_dcl *dcl) /* {{{ */
5897 {
5898 	zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
5899 	type->kind = ZEND_FFI_TYPE_ENUM;
5900 	type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_ENUM_ATTRS);
5901 	type->enumeration.tag_name = NULL;
5902 	if (type->attr & ZEND_FFI_ATTR_PACKED) {
5903 		type->size = zend_ffi_type_uint8.size;
5904 		type->align = zend_ffi_type_uint8.align;
5905 		type->enumeration.kind = ZEND_FFI_TYPE_UINT8;
5906 	} else {
5907 		type->size = zend_ffi_type_uint32.size;
5908 		type->align = zend_ffi_type_uint32.align;
5909 		type->enumeration.kind = ZEND_FFI_TYPE_UINT32;
5910 	}
5911 	dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
5912 	dcl->attr &= ~ZEND_FFI_ENUM_ATTRS;
5913 }
5914 /* }}} */
5915 
5916 void zend_ffi_add_enum_val(zend_ffi_dcl *enum_dcl, const char *name, size_t name_len, zend_ffi_val *val, int64_t *min, int64_t *max, int64_t *last) /* {{{ */
5917 {
5918 	zend_ffi_symbol *sym;
5919 	const zend_ffi_type *sym_type;
5920 	int64_t value;
5921 	zend_ffi_type *enum_type = ZEND_FFI_TYPE(enum_dcl->type);
5922 	bool overflow = 0;
5923 	bool is_signed =
5924 		(enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT8 ||
5925 		 enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT16 ||
5926 		 enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT32 ||
5927 		 enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT64);
5928 
5929 	ZEND_ASSERT(enum_type && enum_type->kind == ZEND_FFI_TYPE_ENUM);
5930 	if (val->kind == ZEND_FFI_VAL_EMPTY) {
5931 		if (is_signed) {
5932 			if (*last == 0x7FFFFFFFFFFFFFFFLL) {
5933 				overflow = 1;
5934 			}
5935 		} else {
5936 			if ((*min != 0 || *max != 0)
5937 			 && (uint64_t)*last == 0xFFFFFFFFFFFFFFFFULL) {
5938 				overflow = 1;
5939 			}
5940 		}
5941 		value = *last + 1;
5942 	} else if (val->kind == ZEND_FFI_VAL_CHAR) {
5943 		if (!is_signed && val->ch < 0) {
5944 			if ((uint64_t)*max > 0x7FFFFFFFFFFFFFFFULL) {
5945 				overflow = 1;
5946 			} else {
5947 				is_signed = 1;
5948 			}
5949 		}
5950 		value = val->ch;
5951 	} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
5952 		if (!is_signed && val->i64 < 0) {
5953 			if ((uint64_t)*max > 0x7FFFFFFFFFFFFFFFULL) {
5954 				overflow = 1;
5955 			} else {
5956 				is_signed = 1;
5957 			}
5958 		}
5959 		value = val->i64;
5960 	} else if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
5961 		if (is_signed && val->u64 > 0x7FFFFFFFFFFFFFFFULL) {
5962 			overflow = 1;
5963 		}
5964 		value = val->u64;
5965 	} else {
5966 		zend_ffi_parser_error("Enumerator value \"%.*s\" must be an integer at line %d", name_len, name, FFI_G(line));
5967 		return;
5968 	}
5969 
5970 	if (overflow) {
5971 		zend_ffi_parser_error("Overflow in enumeration values \"%.*s\" at line %d", name_len, name, FFI_G(line));
5972 		return;
5973 	}
5974 
5975 	if (is_signed) {
5976 		*min = MIN(*min, value);
5977 		*max = MAX(*max, value);
5978 		if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
5979 		 && *min >= -0x7FLL-1 && *max <= 0x7FLL) {
5980 			sym_type = &zend_ffi_type_sint8;
5981 		} else if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
5982 		 && *min >= -0x7FFFLL-1 && *max <= 0x7FFFLL) {
5983 			sym_type = &zend_ffi_type_sint16;
5984 		} else if (*min >= -0x7FFFFFFFLL-1 && *max <= 0x7FFFFFFFLL) {
5985 			sym_type = &zend_ffi_type_sint32;
5986 		} else {
5987 			sym_type = &zend_ffi_type_sint64;
5988 		}
5989 	} else {
5990 		*min = MIN((uint64_t)*min, (uint64_t)value);
5991 		*max = MAX((uint64_t)*max, (uint64_t)value);
5992 		if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
5993 		 && (uint64_t)*max <= 0xFFULL) {
5994 			sym_type = &zend_ffi_type_uint8;
5995 		} else if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
5996 		 && (uint64_t)*max <= 0xFFFFULL) {
5997 			sym_type = &zend_ffi_type_uint16;
5998 		} else if ((uint64_t)*max <= 0xFFFFFFFFULL) {
5999 			sym_type = &zend_ffi_type_uint32;
6000 		} else {
6001 			sym_type = &zend_ffi_type_uint64;
6002 		}
6003 	}
6004 	enum_type->enumeration.kind = sym_type->kind;
6005 	enum_type->size = sym_type->size;
6006 	enum_type->align = sym_type->align;
6007 	*last = value;
6008 
6009 	if (!FFI_G(symbols)) {
6010 		FFI_G(symbols) = pemalloc(sizeof(HashTable), FFI_G(persistent));
6011 		zend_hash_init(FFI_G(symbols), 0, NULL, FFI_G(persistent) ? zend_ffi_symbol_hash_persistent_dtor : zend_ffi_symbol_hash_dtor, FFI_G(persistent));
6012 	}
6013 	sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
6014 	if (sym) {
6015 		zend_ffi_parser_error("Redeclaration of \"%.*s\" at line %d", name_len, name, FFI_G(line));
6016 	} else {
6017 		sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
6018 		sym->kind  = ZEND_FFI_SYM_CONST;
6019 		sym->type  = (zend_ffi_type*)sym_type;
6020 		sym->value = value;
6021 		zend_hash_str_add_new_ptr(FFI_G(symbols), name, name_len, sym);
6022 	}
6023 }
6024 /* }}} */
6025 
6026 void zend_ffi_make_struct_type(zend_ffi_dcl *dcl) /* {{{ */
6027 {
6028 	zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
6029 	type->kind = ZEND_FFI_TYPE_STRUCT;
6030 	type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_STRUCT_ATTRS);
6031 	type->size = 0;
6032 	type->align = dcl->align > 1 ? dcl->align : 1;
6033 	if (dcl->flags & ZEND_FFI_DCL_UNION) {
6034 		type->attr |= ZEND_FFI_ATTR_UNION;
6035 	}
6036 	dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
6037 	type->record.tag_name = NULL;
6038 	zend_hash_init(&type->record.fields, 0, NULL, FFI_G(persistent) ? zend_ffi_field_hash_persistent_dtor :zend_ffi_field_hash_dtor, FFI_G(persistent));
6039 	dcl->attr &= ~ZEND_FFI_STRUCT_ATTRS;
6040 	dcl->align = 0;
6041 }
6042 /* }}} */
6043 
6044 static zend_result zend_ffi_validate_prev_field_type(zend_ffi_type *struct_type) /* {{{ */
6045 {
6046 	if (zend_hash_num_elements(&struct_type->record.fields) > 0) {
6047 		zend_ffi_field *field = NULL;
6048 
6049 		ZEND_HASH_MAP_REVERSE_FOREACH_PTR(&struct_type->record.fields, field) {
6050 			break;
6051 		} ZEND_HASH_FOREACH_END();
6052 		if (ZEND_FFI_TYPE(field->type)->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY) {
6053 			zend_ffi_throw_parser_error("Flexible array member not at end of struct at line %d", FFI_G(line));
6054 			return FAILURE;
6055 		}
6056 	}
6057 	return SUCCESS;
6058 }
6059 /* }}} */
6060 
6061 static zend_result zend_ffi_validate_field_type(zend_ffi_type *type, zend_ffi_type *struct_type) /* {{{ */
6062 {
6063 	if (type == struct_type) {
6064 		zend_ffi_throw_parser_error("Struct/union can't contain an instance of itself at line %d", FFI_G(line));
6065 		return FAILURE;
6066 	} else if (zend_ffi_validate_var_type(type, 1) == FAILURE) {
6067 		return FAILURE;
6068 	} else if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
6069 		if (type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY) {
6070 			zend_ffi_throw_parser_error("Flexible array member in union at line %d", FFI_G(line));
6071 			return FAILURE;
6072 		}
6073 	}
6074 	return zend_ffi_validate_prev_field_type(struct_type);
6075 }
6076 /* }}} */
6077 
6078 void zend_ffi_add_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl) /* {{{ */
6079 {
6080 	zend_ffi_field *field;
6081 	zend_ffi_type *struct_type = ZEND_FFI_TYPE(struct_dcl->type);
6082 	zend_ffi_type *field_type;
6083 
6084 	ZEND_ASSERT(struct_type && struct_type->kind == ZEND_FFI_TYPE_STRUCT);
6085 	zend_ffi_finalize_type(field_dcl);
6086 	field_type = ZEND_FFI_TYPE(field_dcl->type);
6087 	if (zend_ffi_validate_field_type(field_type, struct_type) == FAILURE) {
6088 		zend_ffi_cleanup_dcl(field_dcl);
6089 		LONGJMP(FFI_G(bailout), FAILURE);
6090 	}
6091 
6092 	field = pemalloc(sizeof(zend_ffi_field), FFI_G(persistent));
6093 	if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
6094 		struct_type->align = MAX(struct_type->align, MAX(field_type->align, field_dcl->align));
6095 	}
6096 	if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
6097 		field->offset = 0;
6098 		struct_type->size = MAX(struct_type->size, field_type->size);
6099 	} else {
6100 		if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
6101 			uint32_t field_align = MAX(field_type->align, field_dcl->align);
6102 			struct_type->size = ((struct_type->size + (field_align - 1)) / field_align) * field_align;
6103 		}
6104 		field->offset = struct_type->size;
6105 		struct_type->size += field_type->size;
6106 	}
6107 	field->type = field_dcl->type;
6108 	field->is_const = (bool)(field_dcl->attr & ZEND_FFI_ATTR_CONST);
6109 	field->is_nested = 0;
6110 	field->first_bit = 0;
6111 	field->bits = 0;
6112 	field_dcl->type = field_type; /* reset "owned" flag */
6113 
6114 	if (!zend_hash_str_add_ptr(&struct_type->record.fields, name, name_len, field)) {
6115 		zend_ffi_type_dtor(field->type);
6116 		pefree(field, FFI_G(persistent));
6117 		zend_ffi_parser_error("Duplicate field name \"%.*s\" at line %d", name_len, name, FFI_G(line));
6118 	}
6119 }
6120 /* }}} */
6121 
6122 void zend_ffi_add_anonymous_field(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl) /* {{{ */
6123 {
6124 	zend_ffi_type *struct_type = ZEND_FFI_TYPE(struct_dcl->type);
6125 	zend_ffi_type *field_type;
6126 	zend_ffi_field *field;
6127 	zend_string *key;
6128 
6129 	ZEND_ASSERT(struct_type && struct_type->kind == ZEND_FFI_TYPE_STRUCT);
6130 	zend_ffi_finalize_type(field_dcl);
6131 	field_type = ZEND_FFI_TYPE(field_dcl->type);
6132 	if (field_type->kind != ZEND_FFI_TYPE_STRUCT) {
6133 		zend_ffi_cleanup_dcl(field_dcl);
6134 		zend_ffi_parser_error("Declaration does not declare anything at line %d", FFI_G(line));
6135 		return;
6136 	}
6137 
6138 	if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
6139 		struct_type->align = MAX(struct_type->align, MAX(field_type->align, field_dcl->align));
6140 	}
6141 	if (!(struct_type->attr & ZEND_FFI_ATTR_UNION)) {
6142 		if (zend_ffi_validate_prev_field_type(struct_type) == FAILURE) {
6143 			zend_ffi_cleanup_dcl(field_dcl);
6144 			LONGJMP(FFI_G(bailout), FAILURE);
6145 		}
6146 		if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
6147 			uint32_t field_align = MAX(field_type->align, field_dcl->align);
6148 			struct_type->size = ((struct_type->size + (field_align - 1)) / field_align) * field_align;
6149 		}
6150 	}
6151 
6152 	ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&field_type->record.fields, key, field) {
6153 		zend_ffi_field *new_field = pemalloc(sizeof(zend_ffi_field), FFI_G(persistent));
6154 
6155 		if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
6156 			new_field->offset = field->offset;
6157 		} else {
6158 			new_field->offset = struct_type->size + field->offset;
6159 		}
6160 		new_field->type = field->type;
6161 		new_field->is_const = field->is_const;
6162 		new_field->is_nested = 1;
6163 		new_field->first_bit = field->first_bit;
6164 		new_field->bits = field->bits;
6165 		field->type = ZEND_FFI_TYPE(field->type); /* reset "owned" flag */
6166 
6167 		if (key) {
6168 			if (!zend_hash_add_ptr(&struct_type->record.fields, key, new_field)) {
6169 				zend_ffi_type_dtor(new_field->type);
6170 				pefree(new_field, FFI_G(persistent));
6171 				zend_ffi_parser_error("Duplicate field name \"%s\" at line %d", ZSTR_VAL(key), FFI_G(line));
6172 				return;
6173 			}
6174 		} else {
6175 			zend_hash_next_index_insert_ptr(&struct_type->record.fields, field);
6176 		}
6177 	} ZEND_HASH_FOREACH_END();
6178 
6179 	if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
6180 		struct_type->size = MAX(struct_type->size, field_type->size);
6181 	} else {
6182 		struct_type->size += field_type->size;
6183 	}
6184 
6185 	zend_ffi_type_dtor(field_dcl->type);
6186 	field_dcl->type = NULL;
6187 }
6188 /* }}} */
6189 
6190 void zend_ffi_add_bit_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl, zend_ffi_val *bits) /* {{{ */
6191 {
6192 	zend_ffi_type *struct_type = ZEND_FFI_TYPE(struct_dcl->type);
6193 	zend_ffi_type *field_type;
6194 	zend_ffi_field *field;
6195 
6196 	ZEND_ASSERT(struct_type && struct_type->kind == ZEND_FFI_TYPE_STRUCT);
6197 	zend_ffi_finalize_type(field_dcl);
6198 	field_type = ZEND_FFI_TYPE(field_dcl->type);
6199 	if (zend_ffi_validate_field_type(field_type, struct_type) == FAILURE) {
6200 		zend_ffi_cleanup_dcl(field_dcl);
6201 		LONGJMP(FFI_G(bailout), FAILURE);
6202 	}
6203 
6204 	if (field_type->kind < ZEND_FFI_TYPE_UINT8 || field_type->kind > ZEND_FFI_TYPE_BOOL) {
6205 		zend_ffi_cleanup_dcl(field_dcl);
6206 		zend_ffi_parser_error("Wrong type of bit field \"%.*s\" at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
6207 	}
6208 
6209 	if (bits->kind == ZEND_FFI_VAL_INT32 || bits->kind == ZEND_FFI_VAL_INT64) {
6210 		if (bits->i64 < 0) {
6211 			zend_ffi_cleanup_dcl(field_dcl);
6212 			zend_ffi_parser_error("Negative width in bit-field \"%.*s\" at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
6213 		} else if (bits->i64 == 0) {
6214 			zend_ffi_cleanup_dcl(field_dcl);
6215 			if (name) {
6216 				zend_ffi_parser_error("Zero width in bit-field \"%.*s\" at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
6217 			}
6218 			return;
6219 		} else if (bits->i64 > field_type->size * 8) {
6220 			zend_ffi_cleanup_dcl(field_dcl);
6221 			zend_ffi_parser_error("Width of \"%.*s\" exceeds its type at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
6222 		}
6223 	} else if (bits->kind == ZEND_FFI_VAL_UINT32 || bits->kind == ZEND_FFI_VAL_UINT64) {
6224 		if (bits->u64 == 0) {
6225 			zend_ffi_cleanup_dcl(field_dcl);
6226 			if (name) {
6227 				zend_ffi_parser_error("Zero width in bit-field \"%.*s\" at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
6228 			}
6229 			return;
6230 		} else if (bits->u64 > field_type->size * 8) {
6231 			zend_ffi_cleanup_dcl(field_dcl);
6232 			zend_ffi_parser_error("Width of \"%.*s\" exceeds its type at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
6233 		}
6234 	} else {
6235 		zend_ffi_cleanup_dcl(field_dcl);
6236 		zend_ffi_parser_error("Bit field \"%.*s\" width not an integer constant at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
6237 	}
6238 
6239 	field = pemalloc(sizeof(zend_ffi_field), FFI_G(persistent));
6240 	if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED)) {
6241 		struct_type->align = MAX(struct_type->align, sizeof(uint32_t));
6242 	}
6243 	if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
6244 		field->offset = 0;
6245 		field->first_bit = 0;
6246 		field->bits = bits->u64;
6247 		if (struct_type->attr & ZEND_FFI_ATTR_PACKED) {
6248 			struct_type->size = MAX(struct_type->size, (bits->u64 + 7) / 8);
6249 		} else {
6250 			struct_type->size = MAX(struct_type->size, ((bits->u64 + 31) / 32) * 4);
6251 		}
6252 	} else {
6253 		zend_ffi_field *prev_field = NULL;
6254 
6255 		if (zend_hash_num_elements(&struct_type->record.fields) > 0) {
6256 			ZEND_HASH_MAP_REVERSE_FOREACH_PTR(&struct_type->record.fields, prev_field) {
6257 				break;
6258 			} ZEND_HASH_FOREACH_END();
6259 		}
6260 		if (prev_field && prev_field->bits) {
6261 			field->offset = prev_field->offset;
6262 			field->first_bit = prev_field->first_bit + prev_field->bits;
6263 			field->bits = bits->u64;
6264 		} else {
6265 			field->offset = struct_type->size;
6266 			field->first_bit = 0;
6267 			field->bits = bits->u64;
6268 		}
6269 		if (struct_type->attr & ZEND_FFI_ATTR_PACKED) {
6270 			struct_type->size = field->offset + ((field->first_bit + field->bits) + 7) / 8;
6271 		} else {
6272 			struct_type->size = field->offset + (((field->first_bit + field->bits) + 31) / 32) * 4;
6273 		}
6274 	}
6275 	field->type = field_dcl->type;
6276 	field->is_const = (bool)(field_dcl->attr & ZEND_FFI_ATTR_CONST);
6277 	field->is_nested = 0;
6278 	field_dcl->type = field_type; /* reset "owned" flag */
6279 
6280 	if (name) {
6281 		if (!zend_hash_str_add_ptr(&struct_type->record.fields, name, name_len, field)) {
6282 			zend_ffi_type_dtor(field->type);
6283 			pefree(field, FFI_G(persistent));
6284 			zend_ffi_parser_error("Duplicate field name \"%.*s\" at line %d", name_len, name, FFI_G(line));
6285 		}
6286 	} else {
6287 		zend_hash_next_index_insert_ptr(&struct_type->record.fields, field);
6288 	}
6289 }
6290 /* }}} */
6291 
6292 void zend_ffi_adjust_struct_size(zend_ffi_dcl *dcl) /* {{{ */
6293 {
6294 	zend_ffi_type *struct_type = ZEND_FFI_TYPE(dcl->type);
6295 
6296 	ZEND_ASSERT(struct_type->kind == ZEND_FFI_TYPE_STRUCT);
6297 	if (dcl->align > struct_type->align) {
6298 		struct_type->align = dcl->align;
6299 	}
6300 	if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED)) {
6301 		struct_type->size = ((struct_type->size + (struct_type->align - 1)) / struct_type->align) * struct_type->align;
6302 	}
6303 	dcl->align = 0;
6304 }
6305 /* }}} */
6306 
6307 void zend_ffi_make_pointer_type(zend_ffi_dcl *dcl) /* {{{ */
6308 {
6309 	zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
6310 	type->kind = ZEND_FFI_TYPE_POINTER;
6311 	type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_POINTER_ATTRS);
6312 	type->size = sizeof(void*);
6313 	type->align = _Alignof(void*);
6314 	zend_ffi_finalize_type(dcl);
6315 	if (zend_ffi_validate_vla(ZEND_FFI_TYPE(dcl->type)) == FAILURE) {
6316 		zend_ffi_cleanup_dcl(dcl);
6317 		LONGJMP(FFI_G(bailout), FAILURE);
6318 	}
6319 	type->pointer.type = dcl->type;
6320 	dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
6321 	dcl->flags &= ~ZEND_FFI_DCL_TYPE_QUALIFIERS;
6322 	dcl->attr &= ~ZEND_FFI_POINTER_ATTRS;
6323 	dcl->align = 0;
6324 }
6325 /* }}} */
6326 
6327 static zend_result zend_ffi_validate_array_element_type(zend_ffi_type *type) /* {{{ */
6328 {
6329 	if (type->kind == ZEND_FFI_TYPE_FUNC) {
6330 		zend_ffi_throw_parser_error("Array of functions is not allowed at line %d", FFI_G(line));
6331 		return FAILURE;
6332 	} else if (type->kind == ZEND_FFI_TYPE_ARRAY && (type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
6333 		zend_ffi_throw_parser_error("Only the leftmost array can be undimensioned at line %d", FFI_G(line));
6334 		return FAILURE;
6335 	}
6336 	return zend_ffi_validate_type(type, 0, 1);
6337 }
6338 /* }}} */
6339 
6340 void zend_ffi_make_array_type(zend_ffi_dcl *dcl, zend_ffi_val *len) /* {{{ */
6341 {
6342 	int length = 0;
6343 	zend_ffi_type *element_type;
6344 	zend_ffi_type *type;
6345 
6346 	zend_ffi_finalize_type(dcl);
6347 	element_type = ZEND_FFI_TYPE(dcl->type);
6348 
6349 	if (len->kind == ZEND_FFI_VAL_EMPTY) {
6350 		length = 0;
6351 	} else if (len->kind == ZEND_FFI_VAL_UINT32 || len->kind == ZEND_FFI_VAL_UINT64) {
6352 		length = len->u64;
6353 	} else if (len->kind == ZEND_FFI_VAL_INT32 || len->kind == ZEND_FFI_VAL_INT64) {
6354 		length = len->i64;
6355 	} else if (len->kind == ZEND_FFI_VAL_CHAR) {
6356 		length = len->ch;
6357 	} else {
6358 		zend_ffi_cleanup_dcl(dcl);
6359 		zend_ffi_parser_error("Unsupported array index type at line %d", FFI_G(line));
6360 		return;
6361 	}
6362 	if (length < 0) {
6363 		zend_ffi_cleanup_dcl(dcl);
6364 		zend_ffi_parser_error("Negative array index at line %d", FFI_G(line));
6365 		return;
6366 	}
6367 
6368 	if (zend_ffi_validate_array_element_type(element_type) == FAILURE) {
6369 		zend_ffi_cleanup_dcl(dcl);
6370 		LONGJMP(FFI_G(bailout), FAILURE);
6371 	}
6372 
6373 	type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
6374 	type->kind = ZEND_FFI_TYPE_ARRAY;
6375 	type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_ARRAY_ATTRS);
6376 	type->size = length * element_type->size;
6377 	type->align = element_type->align;
6378 	type->array.type = dcl->type;
6379 	type->array.length = length;
6380 	dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
6381 	dcl->flags &= ~ZEND_FFI_DCL_TYPE_QUALIFIERS;
6382 	dcl->attr &= ~ZEND_FFI_ARRAY_ATTRS;
6383 	dcl->align = 0;
6384 }
6385 /* }}} */
6386 
6387 static zend_result zend_ffi_validate_func_ret_type(zend_ffi_type *type) /* {{{ */
6388 {
6389 	if (type->kind == ZEND_FFI_TYPE_FUNC) {
6390 		zend_ffi_throw_parser_error("Function returning function is not allowed at line %d", FFI_G(line));
6391 		return FAILURE;
6392 	 } else if (type->kind == ZEND_FFI_TYPE_ARRAY) {
6393 		zend_ffi_throw_parser_error("Function returning array is not allowed at line %d", FFI_G(line));
6394 		return FAILURE;
6395 	}
6396 	return zend_ffi_validate_incomplete_type(type, 1, 0);
6397 }
6398 /* }}} */
6399 
6400 void zend_ffi_make_func_type(zend_ffi_dcl *dcl, HashTable *args, zend_ffi_dcl *nested_dcl) /* {{{ */
6401 {
6402 	zend_ffi_type *type;
6403 	zend_ffi_type *ret_type;
6404 
6405 	zend_ffi_finalize_type(dcl);
6406 	ret_type = ZEND_FFI_TYPE(dcl->type);
6407 
6408 	if (args) {
6409 		int no_args = 0;
6410 		zend_ffi_type *arg_type;
6411 
6412 		ZEND_HASH_PACKED_FOREACH_PTR(args, arg_type) {
6413 			arg_type = ZEND_FFI_TYPE(arg_type);
6414 			if (arg_type->kind == ZEND_FFI_TYPE_VOID) {
6415 				if (zend_hash_num_elements(args) != 1) {
6416 					zend_ffi_cleanup_dcl(nested_dcl);
6417 					zend_ffi_cleanup_dcl(dcl);
6418 					zend_hash_destroy(args);
6419 					pefree(args, FFI_G(persistent));
6420 					zend_ffi_parser_error("void type is not allowed at line %d", FFI_G(line));
6421 					return;
6422 				} else {
6423 					no_args = 1;
6424 				}
6425 			}
6426 		} ZEND_HASH_FOREACH_END();
6427 		if (no_args) {
6428 			zend_hash_destroy(args);
6429 			pefree(args, FFI_G(persistent));
6430 			args = NULL;
6431 		}
6432 	}
6433 
6434 #ifdef HAVE_FFI_VECTORCALL_PARTIAL
6435 	if (dcl->abi == ZEND_FFI_ABI_VECTORCALL && args) {
6436 		zend_ulong i;
6437 		zend_ffi_type *arg_type;
6438 
6439 		ZEND_HASH_PACKED_FOREACH_KEY_PTR(args, i, arg_type) {
6440 			arg_type = ZEND_FFI_TYPE(arg_type);
6441 # ifdef _WIN64
6442 			if (i >= 4 && i <= 5 && (arg_type->kind == ZEND_FFI_TYPE_FLOAT || arg_type->kind == ZEND_FFI_TYPE_DOUBLE)) {
6443 # else
6444 			if (i < 6 && (arg_type->kind == ZEND_FFI_TYPE_FLOAT || arg_type->kind == ZEND_FFI_TYPE_DOUBLE)) {
6445 # endif
6446 				zend_ffi_cleanup_dcl(nested_dcl);
6447 				zend_ffi_cleanup_dcl(dcl);
6448 				zend_hash_destroy(args);
6449 				pefree(args, FFI_G(persistent));
6450 				zend_ffi_parser_error("Type float/double is not allowed at position " ZEND_ULONG_FMT " with __vectorcall at line %d", i+1, FFI_G(line));
6451 				return;
6452 			}
6453 		} ZEND_HASH_FOREACH_END();
6454 	}
6455 #endif
6456 
6457 	if (zend_ffi_validate_func_ret_type(ret_type) == FAILURE) {
6458 		zend_ffi_cleanup_dcl(nested_dcl);
6459 		zend_ffi_cleanup_dcl(dcl);
6460 		if (args) {
6461 			zend_hash_destroy(args);
6462 			pefree(args, FFI_G(persistent));
6463 		}
6464 		LONGJMP(FFI_G(bailout), FAILURE);
6465 	}
6466 
6467 	type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
6468 	type->kind = ZEND_FFI_TYPE_FUNC;
6469 	type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_FUNC_ATTRS);
6470 	type->size = sizeof(void*);
6471 	type->align = 1;
6472 	type->func.ret_type = dcl->type;
6473 	switch (dcl->abi) {
6474 		case ZEND_FFI_ABI_DEFAULT:
6475 		case ZEND_FFI_ABI_CDECL:
6476 			type->func.abi = FFI_DEFAULT_ABI;
6477 			break;
6478 #ifdef HAVE_FFI_FASTCALL
6479 		case ZEND_FFI_ABI_FASTCALL:
6480 			type->func.abi = FFI_FASTCALL;
6481 			break;
6482 #endif
6483 #ifdef HAVE_FFI_THISCALL
6484 		case ZEND_FFI_ABI_THISCALL:
6485 			type->func.abi = FFI_THISCALL;
6486 			break;
6487 #endif
6488 #ifdef HAVE_FFI_STDCALL
6489 		case ZEND_FFI_ABI_STDCALL:
6490 			type->func.abi = FFI_STDCALL;
6491 			break;
6492 #endif
6493 #ifdef HAVE_FFI_PASCAL
6494 		case ZEND_FFI_ABI_PASCAL:
6495 			type->func.abi = FFI_PASCAL;
6496 			break;
6497 #endif
6498 #ifdef HAVE_FFI_REGISTER
6499 		case ZEND_FFI_ABI_REGISTER:
6500 			type->func.abi = FFI_REGISTER;
6501 			break;
6502 #endif
6503 #ifdef HAVE_FFI_MS_CDECL
6504 		case ZEND_FFI_ABI_MS:
6505 			type->func.abi = FFI_MS_CDECL;
6506 			break;
6507 #endif
6508 #ifdef HAVE_FFI_SYSV
6509 		case ZEND_FFI_ABI_SYSV:
6510 			type->func.abi = FFI_SYSV;
6511 			break;
6512 #endif
6513 #ifdef HAVE_FFI_VECTORCALL_PARTIAL
6514 		case ZEND_FFI_ABI_VECTORCALL:
6515 			type->func.abi = FFI_VECTORCALL_PARTIAL;
6516 			break;
6517 #endif
6518 		default:
6519 			type->func.abi = FFI_DEFAULT_ABI;
6520 			zend_ffi_cleanup_dcl(nested_dcl);
6521 			if (args) {
6522 				zend_hash_destroy(args);
6523 				pefree(args, FFI_G(persistent));
6524 			}
6525 			type->func.args = NULL;
6526 			_zend_ffi_type_dtor(type);
6527 			zend_ffi_parser_error("Unsupported calling convention line %d", FFI_G(line));
6528 			break;
6529 	}
6530 	type->func.args = args;
6531 	dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
6532 	dcl->attr &= ~ZEND_FFI_FUNC_ATTRS;
6533 	dcl->align = 0;
6534 	dcl->abi = 0;
6535 }
6536 /* }}} */
6537 
6538 void zend_ffi_add_arg(HashTable **args, const char *name, size_t name_len, zend_ffi_dcl *arg_dcl) /* {{{ */
6539 {
6540 	zend_ffi_type *type;
6541 
6542 	if (!*args) {
6543 		*args = pemalloc(sizeof(HashTable), FFI_G(persistent));
6544 		zend_hash_init(*args, 0, NULL, zend_ffi_type_hash_dtor, FFI_G(persistent));
6545 	}
6546 	zend_ffi_finalize_type(arg_dcl);
6547 	type = ZEND_FFI_TYPE(arg_dcl->type);
6548 	if (type->kind == ZEND_FFI_TYPE_ARRAY) {
6549 		if (ZEND_FFI_TYPE_IS_OWNED(arg_dcl->type)) {
6550 			type->kind = ZEND_FFI_TYPE_POINTER;
6551 			type->size = sizeof(void*);
6552 		} else {
6553 			zend_ffi_type *new_type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
6554 			new_type->kind = ZEND_FFI_TYPE_POINTER;
6555 			new_type->attr = FFI_G(default_type_attr) | (type->attr & ZEND_FFI_POINTER_ATTRS);
6556 			new_type->size = sizeof(void*);
6557 			new_type->align = _Alignof(void*);
6558 			new_type->pointer.type = ZEND_FFI_TYPE(type->array.type);
6559 			arg_dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
6560 		}
6561 	} else if (type->kind == ZEND_FFI_TYPE_FUNC) {
6562 		zend_ffi_type *new_type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
6563 		new_type->kind = ZEND_FFI_TYPE_POINTER;
6564 		new_type->attr = FFI_G(default_type_attr);
6565 		new_type->size = sizeof(void*);
6566 		new_type->align = _Alignof(void*);
6567 		new_type->pointer.type = arg_dcl->type;
6568 		arg_dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
6569 	}
6570 	if (zend_ffi_validate_incomplete_type(type, 1, 1) == FAILURE) {
6571 		zend_ffi_cleanup_dcl(arg_dcl);
6572 		zend_hash_destroy(*args);
6573 		pefree(*args, FFI_G(persistent));
6574 		*args = NULL;
6575 		LONGJMP(FFI_G(bailout), FAILURE);
6576 	}
6577 	zend_hash_next_index_insert_ptr(*args, (void*)arg_dcl->type);
6578 }
6579 /* }}} */
6580 
6581 void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl) /* {{{ */
6582 {
6583 	zend_ffi_symbol *sym;
6584 
6585 	if (!FFI_G(symbols)) {
6586 		FFI_G(symbols) = pemalloc(sizeof(HashTable), FFI_G(persistent));
6587 		zend_hash_init(FFI_G(symbols), 0, NULL, FFI_G(persistent) ? zend_ffi_symbol_hash_persistent_dtor : zend_ffi_symbol_hash_dtor, FFI_G(persistent));
6588 	}
6589 	zend_ffi_finalize_type(dcl);
6590 	sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
6591 	if (sym) {
6592 		if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_TYPEDEF
6593 		 && sym->kind == ZEND_FFI_SYM_TYPE
6594 		 && zend_ffi_is_same_type(ZEND_FFI_TYPE(sym->type), ZEND_FFI_TYPE(dcl->type))
6595 		 && sym->is_const == (bool)(dcl->attr & ZEND_FFI_ATTR_CONST)) {
6596 			/* allowed redeclaration */
6597 			zend_ffi_type_dtor(dcl->type);
6598 			return;
6599 		} else if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == 0
6600 		 || (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_EXTERN) {
6601 			zend_ffi_type *type = ZEND_FFI_TYPE(dcl->type);
6602 
6603 			if (type->kind == ZEND_FFI_TYPE_FUNC) {
6604 				if (sym->kind == ZEND_FFI_SYM_FUNC
6605 				 && zend_ffi_same_types(ZEND_FFI_TYPE(sym->type), type)) {
6606 					/* allowed redeclaration */
6607 					zend_ffi_type_dtor(dcl->type);
6608 					return;
6609 				}
6610 			} else {
6611 				if (sym->kind == ZEND_FFI_SYM_VAR
6612 				 && zend_ffi_is_same_type(ZEND_FFI_TYPE(sym->type), type)
6613 				 && sym->is_const == (bool)(dcl->attr & ZEND_FFI_ATTR_CONST)) {
6614 					/* allowed redeclaration */
6615 					zend_ffi_type_dtor(dcl->type);
6616 					return;
6617 				}
6618 			}
6619 		}
6620 		zend_ffi_parser_error("Redeclaration of \"%.*s\" at line %d", name_len, name, FFI_G(line));
6621 	} else {
6622 		if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_TYPEDEF) {
6623 			if (zend_ffi_validate_vla(ZEND_FFI_TYPE(dcl->type)) == FAILURE) {
6624 				zend_ffi_cleanup_dcl(dcl);
6625 				LONGJMP(FFI_G(bailout), FAILURE);
6626 			}
6627 			if (dcl->align && dcl->align > ZEND_FFI_TYPE(dcl->type)->align) {
6628 				if (ZEND_FFI_TYPE_IS_OWNED(dcl->type)) {
6629 					ZEND_FFI_TYPE(dcl->type)->align = dcl->align;
6630 				} else {
6631 					zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
6632 
6633 					memcpy(type, ZEND_FFI_TYPE(dcl->type), sizeof(zend_ffi_type));
6634 					type->attr |= FFI_G(default_type_attr);
6635 					type->align = dcl->align;
6636 					dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
6637 				}
6638 			}
6639 			sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
6640 			sym->kind = ZEND_FFI_SYM_TYPE;
6641 			sym->type = dcl->type;
6642 			sym->is_const = (bool)(dcl->attr & ZEND_FFI_ATTR_CONST);
6643 			dcl->type = ZEND_FFI_TYPE(dcl->type); /* reset "owned" flag */
6644 			zend_hash_str_add_new_ptr(FFI_G(symbols), name, name_len, sym);
6645 		} else {
6646 			zend_ffi_type *type;
6647 
6648 			type = ZEND_FFI_TYPE(dcl->type);
6649 			if (zend_ffi_validate_type(type, (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_EXTERN, 1) == FAILURE) {
6650 				zend_ffi_cleanup_dcl(dcl);
6651 				LONGJMP(FFI_G(bailout), FAILURE);
6652 			}
6653 			if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == 0 ||
6654 			    (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_EXTERN) {
6655 				sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
6656 				sym->kind = (type->kind == ZEND_FFI_TYPE_FUNC) ? ZEND_FFI_SYM_FUNC : ZEND_FFI_SYM_VAR;
6657 				sym->type = dcl->type;
6658 				sym->is_const = (bool)(dcl->attr & ZEND_FFI_ATTR_CONST);
6659 				dcl->type = type; /* reset "owned" flag */
6660 				zend_hash_str_add_new_ptr(FFI_G(symbols), name, name_len, sym);
6661 			} else {
6662 				/* useless declaration */
6663 				zend_ffi_type_dtor(dcl->type);
6664 			}
6665 		}
6666 	}
6667 }
6668 /* }}} */
6669 
6670 void zend_ffi_declare_tag(const char *name, size_t name_len, zend_ffi_dcl *dcl, bool incomplete) /* {{{ */
6671 {
6672 	zend_ffi_tag *tag;
6673 	zend_ffi_type *type;
6674 
6675 	if (!FFI_G(tags)) {
6676 		FFI_G(tags) = pemalloc(sizeof(HashTable), FFI_G(persistent));
6677 		zend_hash_init(FFI_G(tags), 0, NULL, FFI_G(persistent) ? zend_ffi_tag_hash_persistent_dtor : zend_ffi_tag_hash_dtor, FFI_G(persistent));
6678 	}
6679 	tag = zend_hash_str_find_ptr(FFI_G(tags), name, name_len);
6680 	if (tag) {
6681 		zend_ffi_type *type = ZEND_FFI_TYPE(tag->type);
6682 
6683 		if (dcl->flags & ZEND_FFI_DCL_STRUCT) {
6684 			if (tag->kind != ZEND_FFI_TAG_STRUCT) {
6685 				zend_ffi_parser_error("\"%.*s\" defined as wrong kind of tag at line %d", name_len, name, FFI_G(line));
6686 				return;
6687 			} else if (!incomplete && !(type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
6688 				zend_ffi_parser_error("Redefinition of \"struct %.*s\" at line %d", name_len, name, FFI_G(line));
6689 				return;
6690 			}
6691 		} else if (dcl->flags & ZEND_FFI_DCL_UNION) {
6692 			if (tag->kind != ZEND_FFI_TAG_UNION) {
6693 				zend_ffi_parser_error("\"%.*s\" defined as wrong kind of tag at line %d", name_len, name, FFI_G(line));
6694 				return;
6695 			} else if (!incomplete && !(type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
6696 				zend_ffi_parser_error("Redefinition of \"union %.*s\" at line %d", name_len, name, FFI_G(line));
6697 				return;
6698 			}
6699 		} else if (dcl->flags & ZEND_FFI_DCL_ENUM) {
6700 			if (tag->kind != ZEND_FFI_TAG_ENUM) {
6701 				zend_ffi_parser_error("\"%.*s\" defined as wrong kind of tag at line %d", name_len, name, FFI_G(line));
6702 				return;
6703 			} else if (!incomplete && !(type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
6704 				zend_ffi_parser_error("Redefinition of \"enum %.*s\" at line %d", name_len, name, FFI_G(line));
6705 				return;
6706 			}
6707 		} else {
6708 			ZEND_UNREACHABLE();
6709 			return;
6710 		}
6711 		dcl->type = type;
6712 		if (!incomplete) {
6713 			type->attr &= ~ZEND_FFI_ATTR_INCOMPLETE_TAG;
6714 		}
6715 	} else {
6716 		zend_ffi_tag *tag = pemalloc(sizeof(zend_ffi_tag), FFI_G(persistent));
6717 		zend_string *tag_name = zend_string_init(name, name_len, FFI_G(persistent));
6718 
6719 		if (dcl->flags & ZEND_FFI_DCL_STRUCT) {
6720 			tag->kind = ZEND_FFI_TAG_STRUCT;
6721 			zend_ffi_make_struct_type(dcl);
6722 			type = ZEND_FFI_TYPE(dcl->type);
6723 			type->record.tag_name = zend_string_copy(tag_name);
6724 		} else if (dcl->flags & ZEND_FFI_DCL_UNION) {
6725 			tag->kind = ZEND_FFI_TAG_UNION;
6726 			zend_ffi_make_struct_type(dcl);
6727 			type = ZEND_FFI_TYPE(dcl->type);
6728 			type->record.tag_name = zend_string_copy(tag_name);
6729 		} else if (dcl->flags & ZEND_FFI_DCL_ENUM) {
6730 			tag->kind = ZEND_FFI_TAG_ENUM;
6731 			zend_ffi_make_enum_type(dcl);
6732 			type = ZEND_FFI_TYPE(dcl->type);
6733 			type->enumeration.tag_name = zend_string_copy(tag_name);
6734 		} else {
6735 			ZEND_UNREACHABLE();
6736 		}
6737 		tag->type = ZEND_FFI_TYPE_MAKE_OWNED(dcl->type);
6738 		dcl->type = ZEND_FFI_TYPE(dcl->type);
6739 		if (incomplete) {
6740 			dcl->type->attr |= ZEND_FFI_ATTR_INCOMPLETE_TAG;
6741 		}
6742 		zend_hash_add_new_ptr(FFI_G(tags), tag_name, tag);
6743 		zend_string_release(tag_name);
6744 	}
6745 }
6746 /* }}} */
6747 
6748 void zend_ffi_set_abi(zend_ffi_dcl *dcl, uint16_t abi) /* {{{ */
6749 {
6750 	if (dcl->abi != ZEND_FFI_ABI_DEFAULT) {
6751 		zend_ffi_parser_error("Multiple calling convention specifiers at line %d", FFI_G(line));
6752 	} else {
6753 		dcl->abi = abi;
6754 	}
6755 }
6756 /* }}} */
6757 
6758 #define SIMPLE_ATTRIBUTES(_) \
6759 	_(cdecl) \
6760 	_(fastcall) \
6761 	_(thiscall) \
6762 	_(stdcall) \
6763 	_(ms_abi) \
6764 	_(sysv_abi) \
6765 	_(vectorcall) \
6766 	_(aligned) \
6767 	_(packed) \
6768 	_(ms_struct) \
6769 	_(gcc_struct) \
6770 	_(const) \
6771 	_(malloc) \
6772 	_(deprecated) \
6773 	_(nothrow) \
6774 	_(leaf) \
6775 	_(pure) \
6776 	_(noreturn) \
6777 	_(warn_unused_result)
6778 
6779 #define ATTR_ID(name)   attr_ ## name,
6780 #define ATTR_NAME(name) {sizeof(#name)-1, #name},
6781 
6782 void zend_ffi_add_attribute(zend_ffi_dcl *dcl, const char *name, size_t name_len) /* {{{ */
6783 {
6784 	enum {
6785 		SIMPLE_ATTRIBUTES(ATTR_ID)
6786 		attr_unsupported
6787 	};
6788 	static const struct {
6789 		size_t len;
6790 		const char * const name;
6791 	} names[] = {
6792 		SIMPLE_ATTRIBUTES(ATTR_NAME)
6793 		{0, NULL}
6794 	};
6795 	int id;
6796 
6797 	if (name_len > 4
6798 	 && name[0] == '_'
6799 	 && name[1] == '_'
6800 	 && name[name_len-2] == '_'
6801 	 && name[name_len-1] == '_') {
6802 		name += 2;
6803 		name_len -= 4;
6804 	}
6805 	for (id = 0; names[id].len != 0; id++) {
6806 		if (name_len == names[id].len) {
6807 			if (memcmp(name, names[id].name, name_len) == 0) {
6808 				break;
6809 			}
6810 		}
6811 	}
6812 	switch (id) {
6813 		case attr_cdecl:
6814 			zend_ffi_set_abi(dcl, ZEND_FFI_ABI_CDECL);
6815 			break;
6816 		case attr_fastcall:
6817 			zend_ffi_set_abi(dcl, ZEND_FFI_ABI_FASTCALL);
6818 			break;
6819 		case attr_thiscall:
6820 			zend_ffi_set_abi(dcl, ZEND_FFI_ABI_THISCALL);
6821 			break;
6822 		case attr_stdcall:
6823 			zend_ffi_set_abi(dcl, ZEND_FFI_ABI_STDCALL);
6824 			break;
6825 		case attr_ms_abi:
6826 			zend_ffi_set_abi(dcl, ZEND_FFI_ABI_MS);
6827 			break;
6828 		case attr_sysv_abi:
6829 			zend_ffi_set_abi(dcl, ZEND_FFI_ABI_SYSV);
6830 			break;
6831 		case attr_vectorcall:
6832 			zend_ffi_set_abi(dcl, ZEND_FFI_ABI_VECTORCALL);
6833 			break;
6834 		case attr_aligned:
6835 			dcl->align = __BIGGEST_ALIGNMENT__;
6836 			break;
6837 		case attr_packed:
6838 			dcl->attr |= ZEND_FFI_ATTR_PACKED;
6839 			break;
6840 		case attr_ms_struct:
6841 			dcl->attr |= ZEND_FFI_ATTR_MS_STRUCT;
6842 			break;
6843 		case attr_gcc_struct:
6844 			dcl->attr |= ZEND_FFI_ATTR_GCC_STRUCT;
6845 			break;
6846 		case attr_unsupported:
6847 			zend_ffi_parser_error("Unsupported attribute \"%.*s\" at line %d", name_len, name, FFI_G(line));
6848 			break;
6849 		default:
6850 			/* ignore */
6851 			break;
6852 	}
6853 }
6854 /* }}} */
6855 
6856 #define VALUE_ATTRIBUTES(_) \
6857 	_(regparam) \
6858 	_(aligned) \
6859 	_(mode) \
6860 	_(nonnull) \
6861 	_(alloc_size) \
6862 	_(format) \
6863 	_(deprecated)
6864 
6865 void zend_ffi_add_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, int n, zend_ffi_val *val) /* {{{ */
6866 {
6867 	enum {
6868 		VALUE_ATTRIBUTES(ATTR_ID)
6869 		attr_unsupported
6870 	};
6871 	static const struct {
6872 		size_t len;
6873 		const char * const name;
6874 	} names[] = {
6875 		VALUE_ATTRIBUTES(ATTR_NAME)
6876 		{0, NULL}
6877 	};
6878 	int id;
6879 
6880 	if (name_len > 4
6881 	 && name[0] == '_'
6882 	 && name[1] == '_'
6883 	 && name[name_len-2] == '_'
6884 	 && name[name_len-1] == '_') {
6885 		name += 2;
6886 		name_len -= 4;
6887 	}
6888 	for (id = 0; names[id].len != 0; id++) {
6889 		if (name_len == names[id].len) {
6890 			if (memcmp(name, names[id].name, name_len) == 0) {
6891 				break;
6892 			}
6893 		}
6894 	}
6895 	switch (id) {
6896 		case attr_regparam:
6897 			if (n == 0
6898 			 && (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT64 || val->kind == ZEND_FFI_VAL_UINT64)
6899 			 && val->i64 == 3) {
6900 				zend_ffi_set_abi(dcl, ZEND_FFI_ABI_REGISTER);
6901 			} else {
6902 				zend_ffi_parser_error("Incorrect \"regparam\" value at line %d", FFI_G(line));
6903 			}
6904 			break;
6905 		case attr_aligned:
6906 			if (n == 0
6907 			 && (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT64 || val->kind == ZEND_FFI_VAL_UINT64)
6908 			 && val->i64 > 0 && val->i64 <= 0x80000000 && (val->i64 & (val->i64 - 1)) == 0) {
6909 				dcl->align = val->i64;
6910 			} else {
6911 				zend_ffi_parser_error("Incorrect \"alignment\" value at line %d", FFI_G(line));
6912 			}
6913 			break;
6914 		case attr_mode:
6915 			if (n == 0
6916 			 && (val->kind == ZEND_FFI_VAL_NAME)) {
6917 				const char *str = val->str;
6918 				size_t len = val->len;
6919 				if (len > 4
6920 				 && str[0] == '_'
6921 				 && str[1] == '_'
6922 				 && str[len-2] == '_'
6923 				 && str[len-1] == '_') {
6924 					str += 2;
6925 					len -= 4;
6926 				}
6927 				// TODO: Add support for vector type 'VnXX' ???
6928 				if (len == 2) {
6929 					if (str[1] == 'I') {
6930 						if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED))) {
6931 							/* inappropriate type */
6932 						} else if (str[0] == 'Q') {
6933 							dcl->flags &= ~(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG);
6934 							dcl->flags |= ZEND_FFI_DCL_CHAR;
6935 							break;
6936 						} else if (str[0] == 'H') {
6937 							dcl->flags &= ~(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG);
6938 							dcl->flags |= ZEND_FFI_DCL_SHORT;
6939 							break;
6940 						} else if (str[0] == 'S') {
6941 							dcl->flags &= ~(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG);
6942 							dcl->flags |= ZEND_FFI_DCL_INT;
6943 							break;
6944 						} else if (str[0] == 'D') {
6945 							dcl->flags &= ~(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG);
6946 							if (sizeof(long) == 8) {
6947 								dcl->flags |= ZEND_FFI_DCL_LONG;
6948 							} else {
6949 								dcl->flags |= ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG;
6950 							}
6951 							break;
6952 						}
6953 					} else if (str[1] == 'F') {
6954 						if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE))) {
6955 							/* inappropriate type */
6956 						} else if (str[0] == 'S') {
6957 							dcl->flags &= ~(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE);
6958 							dcl->flags |= ZEND_FFI_DCL_FLOAT;
6959 							break;
6960 						} else if (str[0] == 'D') {
6961 							dcl->flags &= ~(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE);
6962 							dcl->flags |= ZEND_FFI_DCL_DOUBLE;
6963 							break;
6964 						}
6965 					}
6966 				}
6967 			}
6968 			zend_ffi_parser_error("Unsupported \"mode\" value at line %d", FFI_G(line));
6969 			// TODO: ???
6970 		case attr_unsupported:
6971 			zend_ffi_parser_error("Unsupported attribute \"%.*s\" at line %d", name_len, name, FFI_G(line));
6972 			break;
6973 		default:
6974 			/* ignore */
6975 			break;
6976 	}
6977 }
6978 /* }}} */
6979 
6980 void zend_ffi_add_msvc_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, zend_ffi_val *val) /* {{{ */
6981 {
6982 	if (name_len == sizeof("align")-1 && memcmp(name, "align", sizeof("align")-1) == 0) {
6983 		if ((val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT64 || val->kind == ZEND_FFI_VAL_UINT64)
6984 		 && val->i64 > 0 && val->i64 <= 0x80000000 && (val->i64 & (val->i64 - 1)) == 0) {
6985 			dcl->align = val->i64;
6986 		} else {
6987 			zend_ffi_parser_error("Incorrect \"alignment\" value at line %d", FFI_G(line));
6988 		}
6989 	} else {
6990 		/* ignore */
6991 	}
6992 }
6993 /* }}} */
6994 
6995 static zend_result zend_ffi_nested_type(zend_ffi_type *type, zend_ffi_type *nested_type) /* {{{ */
6996 {
6997 	nested_type = ZEND_FFI_TYPE(nested_type);
6998 	switch (nested_type->kind) {
6999 		case ZEND_FFI_TYPE_POINTER:
7000 			/* "char" is used as a terminator of nested declaration */
7001 			if (nested_type->pointer.type == &zend_ffi_type_char) {
7002 				nested_type->pointer.type = type;
7003 				return zend_ffi_validate_vla(ZEND_FFI_TYPE(type));
7004 			} else {
7005 				return zend_ffi_nested_type(type, nested_type->pointer.type);
7006 			}
7007 			break;
7008 		case ZEND_FFI_TYPE_ARRAY:
7009 			/* "char" is used as a terminator of nested declaration */
7010 			if (nested_type->array.type == &zend_ffi_type_char) {
7011 				nested_type->array.type = type;
7012 				if (zend_ffi_validate_array_element_type(ZEND_FFI_TYPE(type)) == FAILURE) {
7013 					return FAILURE;
7014 				}
7015 			} else {
7016 				if (zend_ffi_nested_type(type, nested_type->array.type) != SUCCESS) {
7017 					return FAILURE;
7018 				}
7019 			}
7020 			nested_type->size = nested_type->array.length * ZEND_FFI_TYPE(nested_type->array.type)->size;
7021 			nested_type->align = ZEND_FFI_TYPE(nested_type->array.type)->align;
7022 			return SUCCESS;
7023 			break;
7024 		case ZEND_FFI_TYPE_FUNC:
7025 			/* "char" is used as a terminator of nested declaration */
7026 			if (nested_type->func.ret_type == &zend_ffi_type_char) {
7027 				nested_type->func.ret_type = type;
7028 				return zend_ffi_validate_func_ret_type(ZEND_FFI_TYPE(type));
7029 			} else {
7030 				return zend_ffi_nested_type(type, nested_type->func.ret_type);
7031 			}
7032 			break;
7033 		default:
7034 			ZEND_UNREACHABLE();
7035 	}
7036 }
7037 /* }}} */
7038 
7039 void zend_ffi_nested_declaration(zend_ffi_dcl *dcl, zend_ffi_dcl *nested_dcl) /* {{{ */
7040 {
7041 	/* "char" is used as a terminator of nested declaration */
7042 	zend_ffi_finalize_type(dcl);
7043 	if (!nested_dcl->type || nested_dcl->type == &zend_ffi_type_char) {
7044 		nested_dcl->type = dcl->type;
7045 	} else {
7046 		if (zend_ffi_nested_type(dcl->type, nested_dcl->type) == FAILURE) {
7047 			zend_ffi_cleanup_dcl(nested_dcl);
7048 			LONGJMP(FFI_G(bailout), FAILURE);
7049 		}
7050 	}
7051 	dcl->type = nested_dcl->type;
7052 }
7053 /* }}} */
7054 
7055 void zend_ffi_align_as_type(zend_ffi_dcl *dcl, zend_ffi_dcl *align_dcl) /* {{{ */
7056 {
7057 	zend_ffi_finalize_type(align_dcl);
7058 	dcl->align = MAX(align_dcl->align, ZEND_FFI_TYPE(align_dcl->type)->align);
7059 }
7060 /* }}} */
7061 
7062 void zend_ffi_align_as_val(zend_ffi_dcl *dcl, zend_ffi_val *align_val) /* {{{ */
7063 {
7064 	switch (align_val->kind) {
7065 		case ZEND_FFI_VAL_INT32:
7066 		case ZEND_FFI_VAL_UINT32:
7067 			dcl->align = zend_ffi_type_uint32.align;
7068 			break;
7069 		case ZEND_FFI_VAL_INT64:
7070 		case ZEND_FFI_VAL_UINT64:
7071 			dcl->align = zend_ffi_type_uint64.align;
7072 			break;
7073 		case ZEND_FFI_VAL_FLOAT:
7074 			dcl->align = zend_ffi_type_float.align;
7075 			break;
7076 		case ZEND_FFI_VAL_DOUBLE:
7077 			dcl->align = zend_ffi_type_double.align;
7078 			break;
7079 #ifdef HAVE_LONG_DOUBLE
7080 		case ZEND_FFI_VAL_LONG_DOUBLE:
7081 			dcl->align = zend_ffi_type_long_double.align;
7082 			break;
7083 #endif
7084 		case ZEND_FFI_VAL_CHAR:
7085 		case ZEND_FFI_VAL_STRING:
7086 			dcl->align = zend_ffi_type_char.align;
7087 			break;
7088 		default:
7089 			break;
7090 	}
7091 }
7092 /* }}} */
7093 
7094 #define zend_ffi_expr_bool(val) do { \
7095 	if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
7096 		val->kind = ZEND_FFI_VAL_INT32; \
7097 		val->i64 = !!val->u64; \
7098 	} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
7099 		val->kind = ZEND_FFI_VAL_INT32; \
7100 		val->i64 = !!val->i64; \
7101 	} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7102 		val->kind = ZEND_FFI_VAL_INT32; \
7103 		val->i64 = !!val->d; \
7104 	} else if (val->kind == ZEND_FFI_VAL_CHAR) { \
7105 		val->kind = ZEND_FFI_VAL_INT32; \
7106 		val->i64 = !!val->ch; \
7107 	} else { \
7108 		val->kind = ZEND_FFI_VAL_ERROR; \
7109 	} \
7110 } while (0)
7111 
7112 #define zend_ffi_expr_math(val, op2, OP) do { \
7113 	if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
7114 		if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
7115 			val->kind = MAX(val->kind, op2->kind); \
7116 			val->u64 = val->u64 OP op2->u64; \
7117 		} else if (op2->kind == ZEND_FFI_VAL_INT32) { \
7118 			val->u64 = val->u64 OP op2->i64; \
7119 		} else if (op2->kind == ZEND_FFI_VAL_INT64) { \
7120 			val->u64 = val->u64 OP op2->i64; \
7121 		} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7122 			val->kind = op2->kind; \
7123 			val->d = (zend_ffi_double)val->u64 OP op2->d; \
7124 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7125 			val->u64 = val->u64 OP op2->ch; \
7126 		} else { \
7127 			val->kind = ZEND_FFI_VAL_ERROR; \
7128 		} \
7129 	} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
7130 		if (op2->kind == ZEND_FFI_VAL_UINT32) { \
7131 			val->i64 = val->i64 OP op2->u64; \
7132 		} else if (op2->kind == ZEND_FFI_VAL_UINT64) { \
7133 			val->i64 = val->i64 OP op2->u64; \
7134 		} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
7135 			val->kind = MAX(val->kind, op2->kind); \
7136 			val->i64 = val->i64 OP op2->i64; \
7137 		} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7138 			val->kind = op2->kind; \
7139 			val->d = (zend_ffi_double)val->i64 OP op2->d; \
7140 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7141 			val->i64 = val->i64 OP op2->ch; \
7142 		} else { \
7143 			val->kind = ZEND_FFI_VAL_ERROR; \
7144 		} \
7145 	} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7146 		if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
7147 			val->d = val->d OP (zend_ffi_double)op2->u64; \
7148 		} else if (op2->kind == ZEND_FFI_VAL_INT32 ||op2->kind == ZEND_FFI_VAL_INT64) { \
7149 			val->d = val->d OP (zend_ffi_double)op2->i64; \
7150 		} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7151 			val->kind = MAX(val->kind, op2->kind); \
7152 			val->d = val->d OP op2->d; \
7153 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7154 			val->d = val->d OP (zend_ffi_double)op2->ch; \
7155 		} else { \
7156 			val->kind = ZEND_FFI_VAL_ERROR; \
7157 		} \
7158 	} else if (val->kind == ZEND_FFI_VAL_CHAR) { \
7159 		if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
7160 			val->kind = op2->kind; \
7161 			val->u64 = val->ch OP op2->u64; \
7162 		} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
7163 			val->kind = ZEND_FFI_VAL_INT64; \
7164 			val->i64 = val->ch OP op2->i64; \
7165 		} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7166 			val->kind = op2->kind; \
7167 			val->d = (zend_ffi_double)val->ch OP op2->d; \
7168 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7169 			val->ch = val->ch OP op2->ch; \
7170 		} else { \
7171 			val->kind = ZEND_FFI_VAL_ERROR; \
7172 		} \
7173 	} else { \
7174 		val->kind = ZEND_FFI_VAL_ERROR; \
7175 	} \
7176 } while (0)
7177 
7178 #define zend_ffi_expr_int_math(val, op2, OP) do { \
7179 	if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
7180 		if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
7181 			val->kind = MAX(val->kind, op2->kind); \
7182 			val->u64 = val->u64 OP op2->u64; \
7183 		} else if (op2->kind == ZEND_FFI_VAL_INT32) { \
7184 			val->u64 = val->u64 OP op2->i64; \
7185 		} else if (op2->kind == ZEND_FFI_VAL_INT64) { \
7186 			val->u64 = val->u64 OP op2->i64; \
7187 		} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7188 			val->u64 = val->u64 OP (uint64_t)op2->d; \
7189 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7190 			val->u64 = val->u64 OP op2->ch; \
7191 		} else { \
7192 			val->kind = ZEND_FFI_VAL_ERROR; \
7193 		} \
7194 	} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
7195 		if (op2->kind == ZEND_FFI_VAL_UINT32) { \
7196 			val->i64 = val->i64 OP op2->u64; \
7197 		} else if (op2->kind == ZEND_FFI_VAL_UINT64) { \
7198 			val->i64 = val->i64 OP op2->u64; \
7199 		} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
7200 			val->kind = MAX(val->kind, op2->kind); \
7201 			val->i64 = val->i64 OP op2->i64; \
7202 		} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7203 			val->u64 = val->u64 OP (int64_t)op2->d; \
7204 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7205 			val->i64 = val->i64 OP op2->ch; \
7206 		} else { \
7207 			val->kind = ZEND_FFI_VAL_ERROR; \
7208 		} \
7209 	} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7210 		if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
7211 			val->kind = op2->kind; \
7212 			val->u64 = (uint64_t)val->d OP op2->u64; \
7213 		} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
7214 			val->kind = op2->kind; \
7215 			val->i64 = (int64_t)val->d OP op2->i64; \
7216 		} else { \
7217 			val->kind = ZEND_FFI_VAL_ERROR; \
7218 		} \
7219 	} else if (val->kind == ZEND_FFI_VAL_CHAR) { \
7220 		if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
7221 			val->kind = op2->kind; \
7222 			val->u64 = (uint64_t)val->ch OP op2->u64; \
7223 		} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
7224 			val->kind = op2->kind; \
7225 			val->i64 = (int64_t)val->ch OP op2->u64; \
7226 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7227 			val->ch = val->ch OP op2->ch; \
7228 		} else { \
7229 			val->kind = ZEND_FFI_VAL_ERROR; \
7230 		} \
7231 	} else { \
7232 		val->kind = ZEND_FFI_VAL_ERROR; \
7233 	} \
7234 } while (0)
7235 
7236 #define zend_ffi_expr_cmp(val, op2, OP) do { \
7237 	if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
7238 		if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
7239 			val->kind = ZEND_FFI_VAL_INT32; \
7240 			val->i64 = val->u64 OP op2->u64; \
7241 		} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
7242 			val->kind = ZEND_FFI_VAL_INT32; \
7243 			val->i64 = val->u64 OP op2->u64; /*signed/unsigned */ \
7244 		} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7245 			val->kind = ZEND_FFI_VAL_INT32; \
7246 			val->i64 = (zend_ffi_double)val->u64 OP op2->d; \
7247 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7248 			val->kind = ZEND_FFI_VAL_INT32; \
7249 			val->i64 = val->u64 OP op2->d; \
7250 		} else { \
7251 			val->kind = ZEND_FFI_VAL_ERROR; \
7252 		} \
7253 	} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
7254 		if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
7255 			val->kind = ZEND_FFI_VAL_INT32; \
7256 			val->i64 = val->i64 OP op2->i64; /* signed/unsigned */ \
7257 		} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
7258 			val->kind = ZEND_FFI_VAL_INT32; \
7259 			val->i64 = val->i64 OP op2->i64; \
7260 		} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7261 			val->kind = ZEND_FFI_VAL_INT32; \
7262 			val->i64 = (zend_ffi_double)val->i64 OP op2->d; \
7263 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7264 			val->kind = ZEND_FFI_VAL_INT32; \
7265 			val->i64 = val->i64 OP op2->ch; \
7266 		} else { \
7267 			val->kind = ZEND_FFI_VAL_ERROR; \
7268 		} \
7269 	} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7270 		if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
7271 			val->kind = ZEND_FFI_VAL_INT32; \
7272 			val->i64 = val->d OP (zend_ffi_double)op2->u64; \
7273 		} else if (op2->kind == ZEND_FFI_VAL_INT32 ||op2->kind == ZEND_FFI_VAL_INT64) { \
7274 			val->kind = ZEND_FFI_VAL_INT32; \
7275 			val->i64 = val->d OP (zend_ffi_double)op2->i64; \
7276 		} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7277 			val->kind = ZEND_FFI_VAL_INT32; \
7278 			val->i64 = val->d OP op2->d; \
7279 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7280 			val->kind = ZEND_FFI_VAL_INT32; \
7281 			val->i64 = val->d OP (zend_ffi_double)op2->ch; \
7282 		} else { \
7283 			val->kind = ZEND_FFI_VAL_ERROR; \
7284 		} \
7285 	} else if (val->kind == ZEND_FFI_VAL_CHAR) { \
7286 		if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
7287 			val->kind = ZEND_FFI_VAL_INT32; \
7288 			val->i64 = val->ch OP op2->i64; /* signed/unsigned */ \
7289 		} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
7290 			val->kind = ZEND_FFI_VAL_INT32; \
7291 			val->i64 = val->ch OP op2->i64; \
7292 		} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
7293 			val->kind = ZEND_FFI_VAL_INT32; \
7294 			val->i64 = (zend_ffi_double)val->ch OP op2->d; \
7295 		} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
7296 			val->kind = ZEND_FFI_VAL_INT32; \
7297 			val->i64 = val->ch OP op2->ch; \
7298 		} else { \
7299 			val->kind = ZEND_FFI_VAL_ERROR; \
7300 		} \
7301 	} else { \
7302 		val->kind = ZEND_FFI_VAL_ERROR; \
7303 	} \
7304 } while (0)
7305 
7306 void zend_ffi_expr_conditional(zend_ffi_val *val, zend_ffi_val *op2, zend_ffi_val *op3) /* {{{ */
7307 {
7308 	zend_ffi_expr_bool(val);
7309 	if (val->kind == ZEND_FFI_VAL_INT32) {
7310 		if (val->i64) {
7311 			*val = *op2;
7312 		} else {
7313 			*val = *op3;
7314 		}
7315 	}
7316 }
7317 /* }}} */
7318 
7319 void zend_ffi_expr_bool_or(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7320 {
7321 	zend_ffi_expr_bool(val);
7322 	zend_ffi_expr_bool(op2);
7323 	if (val->kind == ZEND_FFI_VAL_INT32 && op2->kind == ZEND_FFI_VAL_INT32) {
7324 		val->i64 = val->i64 || op2->i64;
7325 	} else {
7326 		val->kind = ZEND_FFI_VAL_ERROR;
7327 	}
7328 }
7329 /* }}} */
7330 
7331 void zend_ffi_expr_bool_and(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7332 {
7333 	zend_ffi_expr_bool(val);
7334 	zend_ffi_expr_bool(op2);
7335 	if (val->kind == ZEND_FFI_VAL_INT32 && op2->kind == ZEND_FFI_VAL_INT32) {
7336 		val->i64 = val->i64 && op2->i64;
7337 	} else {
7338 		val->kind = ZEND_FFI_VAL_ERROR;
7339 	}
7340 }
7341 /* }}} */
7342 
7343 void zend_ffi_expr_bw_or(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7344 {
7345 	zend_ffi_expr_int_math(val, op2, |);
7346 }
7347 /* }}} */
7348 
7349 void zend_ffi_expr_bw_xor(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7350 {
7351 	zend_ffi_expr_int_math(val, op2, ^);
7352 }
7353 /* }}} */
7354 
7355 void zend_ffi_expr_bw_and(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7356 {
7357 	zend_ffi_expr_int_math(val, op2, &);
7358 }
7359 /* }}} */
7360 
7361 void zend_ffi_expr_is_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7362 {
7363 	zend_ffi_expr_cmp(val, op2, ==);
7364 }
7365 /* }}} */
7366 
7367 void zend_ffi_expr_is_not_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7368 {
7369 	zend_ffi_expr_cmp(val, op2, !=);
7370 }
7371 /* }}} */
7372 
7373 void zend_ffi_expr_is_less(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7374 {
7375 	zend_ffi_expr_cmp(val, op2, <);
7376 }
7377 /* }}} */
7378 
7379 void zend_ffi_expr_is_greater(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7380 {
7381 	zend_ffi_expr_cmp(val, op2, >);
7382 }
7383 /* }}} */
7384 
7385 void zend_ffi_expr_is_less_or_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7386 {
7387 	zend_ffi_expr_cmp(val, op2, <=);
7388 }
7389 /* }}} */
7390 
7391 void zend_ffi_expr_is_greater_or_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7392 {
7393 	zend_ffi_expr_cmp(val, op2, >=);
7394 }
7395 /* }}} */
7396 
7397 void zend_ffi_expr_shift_left(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7398 {
7399 	zend_ffi_expr_int_math(val, op2, <<);
7400 }
7401 /* }}} */
7402 
7403 void zend_ffi_expr_shift_right(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7404 {
7405 	zend_ffi_expr_int_math(val, op2, >>);
7406 }
7407 /* }}} */
7408 
7409 void zend_ffi_expr_add(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7410 {
7411 	zend_ffi_expr_math(val, op2, +);
7412 }
7413 /* }}} */
7414 
7415 void zend_ffi_expr_sub(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7416 {
7417 	zend_ffi_expr_math(val, op2, -);
7418 }
7419 /* }}} */
7420 
7421 void zend_ffi_expr_mul(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7422 {
7423 	zend_ffi_expr_math(val, op2, *);
7424 }
7425 /* }}} */
7426 
7427 void zend_ffi_expr_div(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7428 {
7429 	zend_ffi_expr_math(val, op2, /);
7430 }
7431 /* }}} */
7432 
7433 void zend_ffi_expr_mod(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
7434 {
7435 	zend_ffi_expr_int_math(val, op2, %); // ???
7436 }
7437 /* }}} */
7438 
7439 void zend_ffi_expr_cast(zend_ffi_val *val, zend_ffi_dcl *dcl) /* {{{ */
7440 {
7441 	zend_ffi_finalize_type(dcl);
7442 	switch (ZEND_FFI_TYPE(dcl->type)->kind) {
7443 		case ZEND_FFI_TYPE_FLOAT:
7444 			if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
7445 				val->kind = ZEND_FFI_VAL_FLOAT;
7446 				val->d = val->u64;
7447 			} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7448 				val->kind = ZEND_FFI_VAL_FLOAT;
7449 				val->d = val->i64;
7450 			} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7451 				val->kind = ZEND_FFI_VAL_FLOAT;
7452 			} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7453 				val->kind = ZEND_FFI_VAL_FLOAT;
7454 				val->d = val->ch;
7455 			} else {
7456 				val->kind = ZEND_FFI_VAL_ERROR;
7457 			}
7458 			break;
7459 		case ZEND_FFI_TYPE_DOUBLE:
7460 			if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
7461 				val->kind = ZEND_FFI_VAL_DOUBLE;
7462 				val->d = val->u64;
7463 			} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7464 				val->kind = ZEND_FFI_VAL_DOUBLE;
7465 				val->d = val->i64;
7466 			} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7467 				val->kind = ZEND_FFI_VAL_DOUBLE;
7468 			} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7469 				val->kind = ZEND_FFI_VAL_DOUBLE;
7470 				val->d = val->ch;
7471 			} else {
7472 				val->kind = ZEND_FFI_VAL_ERROR;
7473 			}
7474 			break;
7475 #ifdef HAVE_LONG_DOUBLE
7476 		case ZEND_FFI_TYPE_LONGDOUBLE:
7477 			if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
7478 				val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
7479 				val->d = val->u64;
7480 			} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7481 				val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
7482 				val->d = val->i64;
7483 			} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7484 				val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
7485 			} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7486 				val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
7487 				val->d = val->ch;
7488 			} else {
7489 				val->kind = ZEND_FFI_VAL_ERROR;
7490 			}
7491 			break;
7492 #endif
7493 		case ZEND_FFI_TYPE_UINT8:
7494 		case ZEND_FFI_TYPE_UINT16:
7495 		case ZEND_FFI_TYPE_UINT32:
7496 		case ZEND_FFI_TYPE_BOOL:
7497 			if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7498 				val->kind = ZEND_FFI_VAL_UINT32;
7499 			} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7500 				val->kind = ZEND_FFI_VAL_UINT32;
7501 				val->u64 = val->d;
7502 			} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7503 				val->kind = ZEND_FFI_VAL_UINT32;
7504 				val->u64 = val->ch;
7505 			} else {
7506 				val->kind = ZEND_FFI_VAL_ERROR;
7507 			}
7508 			break;
7509 		case ZEND_FFI_TYPE_SINT8:
7510 		case ZEND_FFI_TYPE_SINT16:
7511 		case ZEND_FFI_TYPE_SINT32:
7512 			if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7513 				val->kind = ZEND_FFI_VAL_INT32;
7514 			} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7515 				val->kind = ZEND_FFI_VAL_INT32;
7516 				val->i64 = val->d;
7517 			} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7518 				val->kind = ZEND_FFI_VAL_INT32;
7519 				val->i64 = val->ch;
7520 			} else {
7521 				val->kind = ZEND_FFI_VAL_ERROR;
7522 			}
7523 			break;
7524 		case ZEND_FFI_TYPE_UINT64:
7525 			if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7526 				val->kind = ZEND_FFI_VAL_UINT64;
7527 			} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7528 				val->kind = ZEND_FFI_VAL_UINT64;
7529 				val->u64 = val->d;
7530 			} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7531 				val->kind = ZEND_FFI_VAL_UINT64;
7532 				val->u64 = val->ch;
7533 			} else {
7534 				val->kind = ZEND_FFI_VAL_ERROR;
7535 			}
7536 			break;
7537 		case ZEND_FFI_TYPE_SINT64:
7538 			if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
7539 				val->kind = ZEND_FFI_VAL_CHAR;
7540 				val->ch = val->u64;
7541 			} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7542 				val->kind = ZEND_FFI_VAL_CHAR;
7543 				val->ch = val->i64;
7544 			} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7545 				val->kind = ZEND_FFI_VAL_CHAR;
7546 				val->ch = val->d;
7547 			} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7548 			} else {
7549 				val->kind = ZEND_FFI_VAL_ERROR;
7550 			}
7551 			break;
7552 		case ZEND_FFI_TYPE_CHAR:
7553 			if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7554 				val->kind = ZEND_FFI_VAL_UINT32;
7555 			} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7556 				val->kind = ZEND_FFI_VAL_UINT32;
7557 				val->u64 = val->d;
7558 			} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7559 				val->kind = ZEND_FFI_VAL_UINT32;
7560 				val->u64 = val->ch;
7561 			} else {
7562 				val->kind = ZEND_FFI_VAL_ERROR;
7563 			}
7564 			break;
7565 		default:
7566 			val->kind = ZEND_FFI_VAL_ERROR;
7567 			break;
7568 	}
7569 	zend_ffi_type_dtor(dcl->type);
7570 }
7571 /* }}} */
7572 
7573 void zend_ffi_expr_plus(zend_ffi_val *val) /* {{{ */
7574 {
7575 	if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
7576 	} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7577 	} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7578 	} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7579 	} else {
7580 		val->kind = ZEND_FFI_VAL_ERROR;
7581 	}
7582 }
7583 /* }}} */
7584 
7585 void zend_ffi_expr_neg(zend_ffi_val *val) /* {{{ */
7586 {
7587 	if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
7588 		val->u64 = -val->u64;
7589 	} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7590 		val->i64 = -val->i64;
7591 	} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7592 		val->d = -val->d;
7593 	} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7594 		val->ch = -val->ch;
7595 	} else {
7596 		val->kind = ZEND_FFI_VAL_ERROR;
7597 	}
7598 }
7599 /* }}} */
7600 
7601 void zend_ffi_expr_bw_not(zend_ffi_val *val) /* {{{ */
7602 {
7603 	if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
7604 		val->u64 = ~val->u64;
7605 	} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
7606 		val->i64 = ~val->i64;
7607 	} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7608 		val->ch = ~val->ch;
7609 	} else {
7610 		val->kind = ZEND_FFI_VAL_ERROR;
7611 	}
7612 }
7613 /* }}} */
7614 
7615 void zend_ffi_expr_bool_not(zend_ffi_val *val) /* {{{ */
7616 {
7617 	zend_ffi_expr_bool(val);
7618 	if (val->kind == ZEND_FFI_VAL_INT32) {
7619 		val->i64 = !val->i64;
7620 	}
7621 }
7622 /* }}} */
7623 
7624 void zend_ffi_expr_sizeof_val(zend_ffi_val *val) /* {{{ */
7625 {
7626 	if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT32) {
7627 		val->kind = ZEND_FFI_VAL_UINT32;
7628 		val->u64 = zend_ffi_type_uint32.size;
7629 	} else if (val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT64) {
7630 		val->kind = ZEND_FFI_VAL_UINT32;
7631 		val->u64 = zend_ffi_type_uint64.size;
7632 	} else if (val->kind == ZEND_FFI_VAL_FLOAT) {
7633 		val->kind = ZEND_FFI_VAL_UINT32;
7634 		val->u64 = zend_ffi_type_float.size;
7635 	} else if (val->kind == ZEND_FFI_VAL_DOUBLE) {
7636 		val->kind = ZEND_FFI_VAL_UINT32;
7637 		val->u64 = zend_ffi_type_double.size;
7638 	} else if (val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7639 		val->kind = ZEND_FFI_VAL_UINT32;
7640 #ifdef _WIN32
7641 		val->u64 = zend_ffi_type_double.size;
7642 #else
7643 		val->u64 = zend_ffi_type_long_double.size;
7644 #endif
7645 	} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7646 		val->kind = ZEND_FFI_VAL_UINT32;
7647 		val->u64 = zend_ffi_type_char.size;
7648 	} else if (val->kind == ZEND_FFI_VAL_STRING) {
7649 		if (memchr(val->str, '\\', val->len)) {
7650 			// TODO: support for escape sequences ???
7651 			val->kind = ZEND_FFI_VAL_ERROR;
7652 		} else {
7653 			val->kind = ZEND_FFI_VAL_UINT32;
7654 			val->u64 = val->len + 1;
7655 		}
7656 	} else {
7657 		val->kind = ZEND_FFI_VAL_ERROR;
7658 	}
7659 }
7660 /* }}} */
7661 
7662 void zend_ffi_expr_sizeof_type(zend_ffi_val *val, zend_ffi_dcl *dcl) /* {{{ */
7663 {
7664 	zend_ffi_type *type;
7665 
7666 	zend_ffi_finalize_type(dcl);
7667 	type = ZEND_FFI_TYPE(dcl->type);
7668 	val->kind = (type->size > 0xffffffff) ? ZEND_FFI_VAL_UINT64 : ZEND_FFI_VAL_UINT32;
7669 	val->u64 = type->size;
7670 	zend_ffi_type_dtor(dcl->type);
7671 }
7672 /* }}} */
7673 
7674 void zend_ffi_expr_alignof_val(zend_ffi_val *val) /* {{{ */
7675 {
7676 	if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT32) {
7677 		val->kind = ZEND_FFI_VAL_UINT32;
7678 		val->u64 = zend_ffi_type_uint32.align;
7679 	} else if (val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT64) {
7680 		val->kind = ZEND_FFI_VAL_UINT32;
7681 		val->u64 = zend_ffi_type_uint64.align;
7682 	} else if (val->kind == ZEND_FFI_VAL_FLOAT) {
7683 		val->kind = ZEND_FFI_VAL_UINT32;
7684 		val->u64 = zend_ffi_type_float.align;
7685 	} else if (val->kind == ZEND_FFI_VAL_DOUBLE) {
7686 		val->kind = ZEND_FFI_VAL_UINT32;
7687 		val->u64 = zend_ffi_type_double.align;
7688 #ifdef HAVE_LONG_DOUBLE
7689 	} else if (val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
7690 		val->kind = ZEND_FFI_VAL_UINT32;
7691 		val->u64 = zend_ffi_type_long_double.align;
7692 #endif
7693 	} else if (val->kind == ZEND_FFI_VAL_CHAR) {
7694 		val->kind = ZEND_FFI_VAL_UINT32;
7695 		val->u64 = zend_ffi_type_char.size;
7696 	} else if (val->kind == ZEND_FFI_VAL_STRING) {
7697 		val->kind = ZEND_FFI_VAL_UINT32;
7698 		val->u64 = _Alignof(char*);
7699 	} else {
7700 		val->kind = ZEND_FFI_VAL_ERROR;
7701 	}
7702 }
7703 /* }}} */
7704 
7705 void zend_ffi_expr_alignof_type(zend_ffi_val *val, zend_ffi_dcl *dcl) /* {{{ */
7706 {
7707 	zend_ffi_finalize_type(dcl);
7708 	val->kind = ZEND_FFI_VAL_UINT32;
7709 	val->u64 = ZEND_FFI_TYPE(dcl->type)->align;
7710 	zend_ffi_type_dtor(dcl->type);
7711 }
7712 /* }}} */
7713 
7714 void zend_ffi_val_number(zend_ffi_val *val, int base, const char *str, size_t str_len) /* {{{ */
7715 {
7716 	int u = 0;
7717 	int l = 0;
7718 
7719 	if (str[str_len-1] == 'u' || str[str_len-1] == 'U') {
7720 		u = 1;
7721 		if (str[str_len-2] == 'l' || str[str_len-2] == 'L') {
7722 			l = 1;
7723 			if (str[str_len-3] == 'l' || str[str_len-3] == 'L') {
7724 				l = 2;
7725 			}
7726 		}
7727 	} else if (str[str_len-1] == 'l' || str[str_len-1] == 'L') {
7728 		l = 1;
7729 		if (str[str_len-2] == 'l' || str[str_len-2] == 'L') {
7730 			l = 2;
7731 			if (str[str_len-3] == 'u' || str[str_len-3] == 'U') {
7732 				u = 1;
7733 			}
7734 		} else if (str[str_len-2] == 'u' || str[str_len-2] == 'U') {
7735 			u = 1;
7736 		}
7737 	}
7738 	if (u) {
7739 		val->u64 = strtoull(str, NULL, base);
7740 		if (l == 0) {
7741 			val->kind = ZEND_FFI_VAL_UINT32;
7742 		} else if (l == 1) {
7743 			val->kind = (sizeof(long) == 4) ? ZEND_FFI_VAL_UINT32 : ZEND_FFI_VAL_UINT64;
7744 		} else if (l == 2) {
7745 			val->kind = ZEND_FFI_VAL_UINT64;
7746 		}
7747 	} else {
7748 		val->i64 = strtoll(str, NULL, base);
7749 		if (l == 0) {
7750 			val->kind = ZEND_FFI_VAL_INT32;
7751 		} else if (l == 1) {
7752 			val->kind = (sizeof(long) == 4) ? ZEND_FFI_VAL_INT32 : ZEND_FFI_VAL_INT64;
7753 		} else if (l == 2) {
7754 			val->kind = ZEND_FFI_VAL_INT64;
7755 		}
7756 	}
7757 }
7758 /* }}} */
7759 
7760 void zend_ffi_val_float_number(zend_ffi_val *val, const char *str, size_t str_len) /* {{{ */
7761 {
7762 	val->d = strtold(str, NULL);
7763 	if (str[str_len-1] == 'f' || str[str_len-1] == 'F') {
7764 		val->kind = ZEND_FFI_VAL_FLOAT;
7765 	} else if (str[str_len-1] == 'l' || str[str_len-1] == 'L') {
7766 		val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
7767 	} else {
7768 		val->kind = ZEND_FFI_VAL_DOUBLE;
7769 	}
7770 }
7771 /* }}} */
7772 
7773 void zend_ffi_val_string(zend_ffi_val *val, const char *str, size_t str_len) /* {{{ */
7774 {
7775 	if (str[0] != '\"') {
7776 		val->kind = ZEND_FFI_VAL_ERROR;
7777 	} else {
7778 		val->kind = ZEND_FFI_VAL_STRING;
7779 		val->str = str + 1;
7780 		val->len = str_len - 2;
7781 	}
7782 }
7783 /* }}} */
7784 
7785 void zend_ffi_val_character(zend_ffi_val *val, const char *str, size_t str_len) /* {{{ */
7786 {
7787 	int n;
7788 
7789 	if (str[0] != '\'') {
7790 		val->kind = ZEND_FFI_VAL_ERROR;
7791 	} else {
7792 		val->kind = ZEND_FFI_VAL_CHAR;
7793 		if (str_len == 3) {
7794 			val->ch = str[1];
7795 		} else if (str[1] == '\\') {
7796 			if (str[2] == 'a') {
7797 			} else if (str[2] == 'b' && str_len == 4) {
7798 				val->ch = '\b';
7799 			} else if (str[2] == 'f' && str_len == 4) {
7800 				val->ch = '\f';
7801 			} else if (str[2] == 'n' && str_len == 4) {
7802 				val->ch = '\n';
7803 			} else if (str[2] == 'r' && str_len == 4) {
7804 				val->ch = '\r';
7805 			} else if (str[2] == 't' && str_len == 4) {
7806 				val->ch = '\t';
7807 			} else if (str[2] == 'v' && str_len == 4) {
7808 				val->ch = '\v';
7809 			} else if (str[2] >= '0' && str[2] <= '7') {
7810 				n = str[2] - '0';
7811 				if (str[3] >= '0' && str[3] <= '7') {
7812 					n = n * 8 + (str[3] - '0');
7813 					if ((str[4] >= '0' && str[4] <= '7') && str_len == 6) {
7814 						n = n * 8 + (str[4] - '0');
7815 					} else if (str_len != 5) {
7816 						val->kind = ZEND_FFI_VAL_ERROR;
7817 					}
7818 				} else if (str_len != 4) {
7819 					val->kind = ZEND_FFI_VAL_ERROR;
7820 				}
7821 				if (n <= 0xff) {
7822 					val->ch = n;
7823 				} else {
7824 					val->kind = ZEND_FFI_VAL_ERROR;
7825 				}
7826 			} else if (str[2] == 'x') {
7827 				if (str[3] >= '0' && str[3] <= '9') {
7828 					n = str[3] - '0';
7829 				} else if (str[3] >= 'A' && str[3] <= 'F') {
7830 					n = str[3] - 'A';
7831 				} else if (str[3] >= 'a' && str[3] <= 'f') {
7832 					n = str[3] - 'a';
7833 				} else {
7834 					val->kind = ZEND_FFI_VAL_ERROR;
7835 					return;
7836 				}
7837 				if ((str[4] >= '0' && str[4] <= '9') && str_len == 6) {
7838 					n = n * 16 + (str[4] - '0');
7839 				} else if ((str[4] >= 'A' && str[4] <= 'F') && str_len == 6) {
7840 					n = n * 16 + (str[4] - 'A');
7841 				} else if ((str[4] >= 'a' && str[4] <= 'f') && str_len == 6) {
7842 					n = n * 16 + (str[4] - 'a');
7843 				} else if (str_len != 5) {
7844 					val->kind = ZEND_FFI_VAL_ERROR;
7845 					return;
7846 				}
7847 				val->ch = n;
7848 			} else if (str_len == 4) {
7849 				val->ch = str[2];
7850 			} else {
7851 				val->kind = ZEND_FFI_VAL_ERROR;
7852 			}
7853 		} else {
7854 			val->kind = ZEND_FFI_VAL_ERROR;
7855 		}
7856 	}
7857 }
7858 /* }}} */
7859