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