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