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