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