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