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