- Date:
. Fixed bug #75232 (print_r of DateTime creating side-effect). (Nikita)
+- FFI:
+ . Added FFI extension. (Dmitry)
+
- FPM:
. Implemented FR #72510 (systemd service should be hardened). (Craig Andrews)
--- /dev/null
+FFI
+Dmitry Stogov
\ No newline at end of file
--- /dev/null
+# FFI PHP extension (Foreign Function Interface)
+
+FFI PHP extension provides a simple way to call native functions, access native variables and create/access data structures defined in C language. The API of the extension is very simple and demonstrated by the following example and its output.
+
+```php
+<?php
+$libc = FFI::cdef("
+ int printf(const char *format, ...);
+ const char * getenv(const char *);
+ unsigned int time(unsigned int *);
+
+ typedef unsigned int time_t;
+ typedef unsigned int suseconds_t;
+
+ struct timeval {
+ time_t tv_sec;
+ suseconds_t tv_usec;
+ };
+
+ struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+ };
+
+ int gettimeofday(struct timeval *tv, struct timezone *tz);
+", "libc.so.6");
+
+$libc->printf("Hello World from %s!\n", "PHP");
+var_dump($libc->getenv("PATH"));
+var_dump($libc->time(null));
+
+$tv = $libc->new("struct timeval");
+$tz = $libc->new("struct timezone");
+$libc->gettimeofday(FFI::addr($tv), FFI::addr($tz));
+var_dump($tv->tv_sec, $tv->tv_usec, $tz);
+?>
+```
+
+```
+Hello World from PHP!
+string(135) "/usr/lib64/qt-3.3/bin:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/home/dmitry/.local/bin:/home/dmitry/bin"
+int(1523617815)
+int(1523617815)
+int(977765)
+object(FFI\CData:<struct>)#3 (2) {
+ ["tz_minuteswest"]=>
+ int(-180)
+ ["tz_dsttime"]=>
+ int(0)
+}
+```
+
+FFI::cdef() takes two arguments (both are optional). The first one is a collection of C declarations and the second is DSO library. All variables and functions defined by first arguments are bound to corresponding native symbols in DSO library and then may be accessed as FFI object methods and properties. C types of argument, return value and variables are automatically converted to/from PHP types (if possible). Otherwise, they are wrapped in a special CData proxy object and may be accessed by elements.
+
+In some cases (e.g. passing C structure by pointer) we may need to create a real C data structures. This is possible using FFF::new() method. It takes a C type definition and may reuse C types and tags defined by FFI::cdef().
+
+It's also possible to use FFI::new() as a static method to create arbitrary C data structures.
+
+``` php
+<?php
+$p = FFI::new("struct {int x,y;} [2]");
+$p[0]->x = 5;
+$p[1]->y = 10;
+var_dump($p);
+```
+
+```
+object(FFI\CData:<struct>[2])#1 (2) {
+ [0]=>
+ object(FFI\CData:<struct>)#2 (2) {
+ ["x"]=>
+ int(5)
+ ["y"]=>
+ int(0)
+ }
+ [1]=>
+ object(FFI\CData:<struct>)#3 (2) {
+ ["x"]=>
+ int(0)
+ ["y"]=>
+ int(10)
+ }
+}
+```
+
+### API Reference
+
+##### function FFI::cdef([string $cdef = "" [, string $lib = null]]): FFI
+
+##### Call Native Functions
+
+All functions defined in FFI::cdef() may be called as methods of the created FFI object.
+
+```php
+$libc = FFI::cdef("const char * getenv(const char *);", "libc.so.6");
+var_dump($libc->getenv("PATH"));
+```
+
+##### Read/Write Values of Native Variables
+
+All functions defined in FFI::cdef() may be accessed as properties of the created FFI object.
+
+```php
+$libc = FFI::cdef("extern int errno;", "libc.so.6");
+var_dump($libc->errno);
+```
+
+##### function FFI::type(string $type): FFI\CType
+
+This function creates and returns a **FFI\CType** object, representng type of the given C type declaration string.
+
+FFI::type() may be called statically and use only predefined types, or as a method of previously created FFI object. In last case the first argument may reuse all type and tag names defined in FFI::cdef().
+
+
+##### function FFI::typeof(FFI\CData $type): FFI\CType
+
+This function returns a **FFI\CType** object, representing the type of the given **FFI\CData** object.
+
+##### static function FFI::arrayType(FFI\CType $type, array $dims): FFI\CType
+
+Constructs a new C array type with elements of $type and dimensions specified by $dims.
+
+##### function FFI::new(mixed $type [, bool $own = true [, bool $persistent = false]]): FFI\CData
+
+This function may be used to create a native data structure. The first argument is a C type definition. It may be a **string** or **FFI\CType** object. The following example creates two dimensional array of integers.
+
+```php
+$p = FFI::new("int[2][2]");
+var_dump($p, FFI::sizeof($p));
+```
+
+FFI::new() may be called statically and use only predefined types, or as a method of previously created FFI object. In last case the first argument may reuse all type and tag names defined in FFI::cdef().
+
+By default **FFI::new()** creates "owned" native data structures, that live together with corresponding PHP object, reusing PHP reference-counting and GC. However, in some cases it may be necessary to manually control the life time of the data structure. In this case, the PHP ownership on the corresponding data, may be manually changed, using **false** as the second optianal argument. Later, not-owned CData should be manually deallocated using **FFI::free()**.
+
+Using the optional $persistent argument it's possible to allocate C objects in persistent memory, through malloc(), otherwise memory is allocated in PHP request heap, through emalloc().
+
+##### static function FFI::free(FFI\CData $cdata): void
+
+manually removes previously created "not-owned" data structure.
+
+##### Read/Write Elements of Native Arrays
+
+Elements of native array may be accessed in the same way as elements of PHP arrays. Of course, native arrays support only integer indexes. It's not possible to check element existence using isset() or empty() and remove element using unset(). Native arrays work fine with "foreach" statement.
+
+```php
+$p = FFI::new("int[2]");
+$p[0] = 1;
+$p[1] = 2;
+foreach ($p as $key => $val) {
+ echo "$key => $val\n";
+}
+```
+
+##### Read/Write Fields of Native "struct" or "union"
+
+Fields of native struct/union may be accessed in the same way as properties of PHP objects. It's not possible to check filed existence using isset() or empty(), remove them using unset(), and iterate using "foreach" statement.
+
+```php
+$pp = FFI::new("struct {int x,y;}[2]");
+foreach($pp as $n => &$p) {
+ $p->x = $p->y = $n;
+}
+var_dump($pp);
+```
+
+##### Pointer arithmetic
+
+CData pointer values may be incremented/decremented by a number. The result is a pointer of the same type moved on given offset.
+
+Two pointers to the same type may be subtracted and return difference (similar to C).
+
+##### static function FFI::sizeof(mixed $cdata_or_ctype): int
+
+returns size of C data type of the given **FFI\CData** or **FFI\CType**.
+
+##### static function FFI::alignof(mixed $cdata_or_ctype): int
+
+returns size of C data type of the given **FFI\CData** or **FFI\CType**.
+
+##### static function FFI::memcpy(FFI\CData $dst, mixed $src, int $size): void
+
+copies $size bytes from memory area $src to memory area $dst. $src may be any native data structure (**FFI\CData**) or PHP **string**.
+
+##### static function FFI::memcmp(mixed $src1, mixed $src2, int $size): int
+
+compares $size bytes from memory area $src1 and $dst2. $src1 and $src2 may be any native data structures (**FFI\CData**) or PHP **string**s.
+
+##### static function FFI::memset(FFI\CData $dst, int $c, int $size): void
+
+fills the $size bytes of the memory area pointed to by $dst with the constant byte $c
+
+##### static function FFI::string(FFI\CData $src [, int $size]): string
+
+creates a PHP string from $size bytes of memory area pointed by $src. If size is omitted, $src must be zero terminated array of C chars.
+
+##### function FFI::cast(mixed $type, FFI\CData $cdata): FFI\CData
+
+Casts given $cdata to another C type, specified by C declaration **string** or **FFI\CType** object.
+
+This function may be called statically and use only predefined types, or as a method of previously created FFI object. In last case the first argument may reuse all type and tag names defined in FFI::cdef().
+
+##### static function addr(FFI\CData $cdata): FFI\CData;
+
+Returns C pointer to the given C data structure. The pointer is not "owned" and won't be free. Anyway, this is a potentially unsafe operation, because the life-time of the returned pointer may be longer than life-time of the source object, and this may cause dangling pointer dereference (like in regular C).
+
+##### static function load(string $filename): FFI;
+
+Instead of embedding of a long C definition into PHP string, and creating FFI through FFI::cdef(), it's possible to separate it into a C header file. Note, that C preprocessor directives (e.g. #define or #ifdef) are not supported. And only a couple of special macros may be used especially for FFI.
+
+``` C
+#define FFI_LIB "libc.so.6"
+
+int printf(const char *format, ...);
+```
+
+Here, FFI_LIB specifies, that the given library should be loaded.
+
+``` php
+$ffi = FFI::load(__DIR__ . "/printf.h");
+$ffi->printf("Hello world!\n");
+
+```
+
+##### static function scope(string $name): FFI;
+
+FFI definition parsing and shared library loading may take significant time. It's not useful to do it on each HTTP request in WEB environment. However, it's possible to pre-load FFI definitions and libraries at php startup, and instantiate FFI objects when necessary. Header files may be extended with **FFI_SCOPE** define (default pre-loading scope is "C"). This name is going to be used as **FFI::scope()** argument. It's possible to pre-load few files into a single scope.
+
+``` C
+#define FFI_LIB "libc.so.6"
+#define FFI_SCOPE "libc"
+
+int printf(const char *format, ...);
+```
+
+These files are loaded through the same **FFI::load()** load function, executed from file loaded by **opcache.preload** php.ini directive.
+
+``` ini
+ffi.preload=/etc/php/ffi/printf.h
+```
+
+Finally, **FFI::scope()** instantiate an **FFI** object, that implements all C definition from the given scope.
+
+``` php
+$ffi = FFI::scope("libc");
+$ffi->printf("Hello world!\n");
+```
+
+##### Owned and Not-Owned CData
+
+FFI extension uses two kind of native C data structures. "Owned" pointers are created using **FFI::new([, true])**, **clone**ed. Owned data is deallocated together with last PHP variable, that reference it. This mechanism reuses PHP reference-counting and garbage-collector.
+
+Elements of C arrays and structures, as well as most data structures returned by C functions are "not-owned". They work just as regular C pointers. They may leak memory, if not freed manually using **FFI::free()**, or may become dangling pointers and lead to PHP crashes.
+
+The following example demonstrates the problem.
+
+```php
+$p1 = FFI::new("int[2][2]"); // $p1 is owned pointer
+$p2 = $p1[0]; // $p2 is not-owned part of $p1
+unset($p1); // $p1 is deallocated ($p2 became dangling pointer)
+var_dump($p2); // crash because dereferencing of dangling pointer
+```
+
+It's possible to change ownership, to avoid this crash, but this would require manual memory management and may lead to memory leaks
+
+```php
+$p1 = FFI::new("int[2][2]", false); // $p1 is not-owned pointer
+$p2 = $p1[0];
+unset($p1); // $p1 CData is keep alive (memory leak)
+var_dump($p2); // works fine, except of memory leak
+```
+
+##### PHP Callbacks
+
+It's possible to assign PHP function to native function variable (or pass it as a function argument). This seems to work, but this functionality is not supported on all libffi platforms, it is not efficient and leaks resources by the end of request.
+
+##### FFI API restriction
+
+With FFI users may do almost anything, like in C, and therefor may crash PHP in thousand ways. It's possible to completely disable or enable all FFI functions using ffi.enable=0/1 configuration directives, or limit FFI usage to preloaded scripts using ffi.enable=preload (this is the default setting). In case FFI is not completely disabled, it's also enabled for CLI scripts. Finally, the restriction affects only FFI functions their selves, but not the overloaded method of created FFI or CData objects.
+
+### Status
+
+In current state, access to FFI data structures is significantly (about 2 times) slower, than access to PHP arrays and objects. It make no sense to use them for speed, but may make sense to reduce memory consumption.
+
+FFI functionality may be included into PHP-8 core, to provide better interpretation performance and integrate with JIT, providing almost C performance (similar to LuaJIT)
+
+### Requirement
+
+- [libffi-3.*](http://sourceware.org/libffi/)
+
+### Install
+
+``` bash
+./configure ... --with-ffi
+make
+sudo make install
+```
+
+### Real Usage
+
+FFI extension was used to implement [PHP TensorFlow binding](https://github.com/dstogov/php-tensorflow)
--- /dev/null
+dnl config.m4 for extension FFI
+
+PHP_ARG_WITH(ffi, for FFI support,
+[ --with-ffi Include FFI support])
+
+if test "$PHP_FFI" != "no"; then
+ if test -r $PHP_FFI/include/ffi.h; then
+ FFI_INCDIR=$PHP_FFI/include
+ FFI_LIBDIR=$PHP_FFI
+ else
+ dnl First try to find pkg-config
+ if test -z "$PKG_CONFIG"; then
+ AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+ fi
+
+ dnl If pkg-config is installed, try using it
+ if test -x "$PKG_CONFIG" && "$PKG_CONFIG" --exists libffi; then
+ FFI_VER=`"$PKG_CONFIG" --modversion libffi`
+ FFI_INCDIR=`"$PKG_CONFIG" --variable=includedir libffi`
+ FFI_LIBDIR=`"$PKG_CONFIG" --variable=libdir libffi`
+ AC_MSG_CHECKING(for libffi)
+ AC_MSG_RESULT(found version $FFI_VER)
+ else
+ AC_MSG_CHECKING(for libffi in default path)
+ for i in /usr/local /usr; do
+ if test -r $i/include/ffi.h; then
+ FFI_DIR=$i
+ AC_MSG_RESULT(found in $i)
+ break
+ fi
+ done
+
+ if test -z "$FFI_DIR"; then
+ AC_MSG_RESULT(not found)
+ AC_MSG_ERROR(Please reinstall the libffi distribution)
+ fi
+
+ FFI_INCDIR="$FFI_DIR/include"
+ FFI_LIBDIR="$FFI_DIR/$PHP_LIBDIR"
+ fi
+ fi
+
+ AC_CHECK_TYPES(long double)
+
+ PHP_CHECK_LIBRARY(ffi, ffi_call,
+ [
+ PHP_ADD_INCLUDE($FFI_INCDIR)
+ PHP_ADD_LIBRARY_WITH_PATH(ffi, $FFI_LIBDIR, FFI_SHARED_LIBADD)
+ AC_DEFINE(HAVE_FFI,1,[ Have ffi support ])
+ ], [
+ AC_MSG_ERROR(FFI module requires libffi)
+ ], [
+ -L$FFI_LIBDIR
+ ])
+
+ PHP_NEW_EXTENSION(ffi, ffi.c ffi_parser.c, $ext_shared)
+ PHP_SUBST(FFI_SHARED_LIBADD)
+fi
--- /dev/null
+ARG_WITH('ffi', 'ffi support', 'no');
+
+if (PHP_FFI != 'no') {
+ if (CHECK_HEADER_ADD_INCLUDE("ffi.h", "CFLAGS_FFI", PHP_FFI+ ";" + PHP_PHP_BUILD + "\\include") &&
+ CHECK_LIB("libffi.lib", "ffi", PHP_FFI)) {
+ AC_DEFINE('HAVE_FFI', 1, 'ffi support enabled');
+
+ EXTENSION('ffi', 'ffi.c ffi_parser.c', null, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');
+ } else {
+ WARNING('ffi not enabled, headers or libraries not found');
+ }
+}
--- /dev/null
+<?php
+$ffi = FFI::cdef("
+
+ int printf(const char *format, ...);
+ const char * getenv(const char *);
+ unsigned int time(unsigned int *);
+
+ typedef unsigned int time_t;
+ typedef unsigned int suseconds_t;
+
+ struct timeval {
+ time_t tv_sec;
+ suseconds_t tv_usec;
+ };
+
+ struct timezone {
+ int tz_minuteswest;
+ int tz_dsttime;
+ };
+
+ int gettimeofday(struct timeval *tv, struct timezone *tz);
+ ", "libc.so.6");
+
+var_dump($ffi->printf("Hello World from %s!\n", "PHP"));
+var_dump($ffi->getenv("PATH"));
+var_dump($ffi->time(null));
+
+$tv = $ffi->new("struct timeval");
+$tz = $ffi->new("struct timezone");
+var_dump($ffi->gettimeofday(FFI::addr($tv), FFI::addr($tz)));
+var_dump($tv, $tz);
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2018 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ffi.h"
+#include "ext/standard/info.h"
+#include "php_scandir.h"
+#include "zend_exceptions.h"
+#include "zend_interfaces.h"
+#include "zend_closures.h"
+#include "main/SAPI.h"
+
+#include <ffi.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+ZEND_DECLARE_MODULE_GLOBALS(ffi)
+
+typedef enum _zend_ffi_tag_kind {
+ ZEND_FFI_TAG_ENUM,
+ ZEND_FFI_TAG_STRUCT,
+ ZEND_FFI_TAG_UNION
+} zend_ffi_tag_kind;
+
+static const char *zend_ffi_tag_kind_name[3] = {"enum", "struct", "union"};
+
+
+typedef struct _zend_ffi_tag {
+ zend_ffi_tag_kind kind;
+ zend_ffi_type *type;
+} zend_ffi_tag;
+
+typedef enum _zend_ffi_type_kind {
+ ZEND_FFI_TYPE_VOID,
+ ZEND_FFI_TYPE_FLOAT,
+ ZEND_FFI_TYPE_DOUBLE,
+#ifdef HAVE_LONG_DOUBLE
+ ZEND_FFI_TYPE_LONGDOUBLE,
+#endif
+ ZEND_FFI_TYPE_UINT8,
+ ZEND_FFI_TYPE_SINT8,
+ ZEND_FFI_TYPE_UINT16,
+ ZEND_FFI_TYPE_SINT16,
+ ZEND_FFI_TYPE_UINT32,
+ ZEND_FFI_TYPE_SINT32,
+ ZEND_FFI_TYPE_UINT64,
+ ZEND_FFI_TYPE_SINT64,
+ ZEND_FFI_TYPE_ENUM,
+ ZEND_FFI_TYPE_BOOL,
+ ZEND_FFI_TYPE_CHAR,
+ ZEND_FFI_TYPE_POINTER,
+ ZEND_FFI_TYPE_FUNC,
+ ZEND_FFI_TYPE_ARRAY,
+ ZEND_FFI_TYPE_STRUCT,
+} zend_ffi_type_kind;
+
+typedef enum _zend_ffi_flags {
+ ZEND_FFI_FLAG_CONST = (1 << 0),
+ ZEND_FFI_FLAG_OWNED = (1 << 1),
+ ZEND_FFI_FLAG_PERSISTENT = (1 << 2),
+} zend_ffi_flags;
+
+struct _zend_ffi_type {
+ zend_ffi_type_kind kind;
+ size_t size;
+ uint32_t align;
+ uint32_t attr;
+ union {
+ struct {
+ zend_ffi_type_kind kind;
+ } enumeration;
+ struct {
+ zend_ffi_type *type;
+ zend_long length;
+ } array;
+ struct {
+ zend_ffi_type *type;
+ } pointer;
+ struct {
+ HashTable fields;
+ } record;
+ struct {
+ zend_ffi_type *ret_type;
+ HashTable *args;
+ ffi_abi abi;
+ } func;
+ };
+};
+
+typedef struct _zend_ffi_field {
+ size_t offset;
+ zend_bool is_const;
+ zend_bool is_nested; /* part of nested anonymous struct */
+ uint8_t first_bit;
+ uint8_t bits;
+ zend_ffi_type *type;
+} zend_ffi_field;
+
+typedef enum _zend_ffi_symbol_kind {
+ ZEND_FFI_SYM_TYPE,
+ ZEND_FFI_SYM_CONST,
+ ZEND_FFI_SYM_VAR,
+ ZEND_FFI_SYM_FUNC
+} zend_ffi_symbol_kind;
+
+typedef struct _zend_ffi_symbol {
+ zend_ffi_symbol_kind kind;
+ zend_bool is_const;
+ zend_ffi_type *type;
+ union {
+ void *addr;
+ int64_t value;
+ };
+} zend_ffi_symbol;
+
+typedef struct _zend_ffi_scope {
+ HashTable *symbols;
+ HashTable *tags;
+} zend_ffi_scope;
+
+typedef struct _zend_ffi {
+ zend_object std;
+ DL_HANDLE lib;
+ HashTable *symbols;
+ HashTable *tags;
+ zend_bool persistent;
+} zend_ffi;
+
+#define ZEND_FFI_TYPE_OWNED (1<<0)
+
+#define ZEND_FFI_TYPE(t) \
+ ((zend_ffi_type*)(((uintptr_t)(t)) & ~ZEND_FFI_TYPE_OWNED))
+
+#define ZEND_FFI_TYPE_IS_OWNED(t) \
+ (((uintptr_t)(t)) & ZEND_FFI_TYPE_OWNED)
+
+#define ZEND_FFI_TYPE_MAKE_OWNED(t) \
+ ((zend_ffi_type*)(((uintptr_t)(t)) | ZEND_FFI_TYPE_OWNED))
+
+typedef struct _zend_ffi_cdata {
+ zend_object std;
+ zend_ffi_type *type;
+ void *ptr;
+ void *ptr_holder;
+ zend_ffi_flags flags;
+} zend_ffi_cdata;
+
+typedef struct _zend_ffi_ctype {
+ zend_object std;
+ zend_ffi_type *type;
+} zend_ffi_ctype;
+
+static zend_class_entry *zend_ffi_exception_ce;
+static zend_class_entry *zend_ffi_parser_exception_ce;
+static zend_class_entry *zend_ffi_ce;
+static zend_class_entry *zend_ffi_cdata_ce;
+static zend_class_entry *zend_ffi_ctype_ce;
+
+static zend_object_handlers zend_ffi_handlers;
+static zend_object_handlers zend_ffi_cdata_handlers;
+static zend_object_handlers zend_ffi_cdata_value_handlers;
+static zend_object_handlers zend_ffi_cdata_free_handlers;
+static zend_object_handlers zend_ffi_ctype_handlers;
+
+static zend_internal_function zend_ffi_new_fn;
+static zend_internal_function zend_ffi_cast_fn;
+static zend_internal_function zend_ffi_type_fn;
+
+/* forward declarations */
+static void _zend_ffi_type_dtor(zend_ffi_type *type);
+static void zend_ffi_finalize_type(zend_ffi_dcl *dcl);
+static int zend_ffi_is_same_type(zend_ffi_type *type1, zend_ffi_type *type2);
+static zend_ffi_type *zend_ffi_remember_type(zend_ffi_type *type);
+static char *zend_ffi_parse_directives(const char *filename, char *code_pos, char **scope_name, char **lib, zend_bool preload);
+static ZEND_FUNCTION(ffi_trampoline);
+
+#if FFI_CLOSURES
+static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value);
+#endif
+
+static zend_always_inline void zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */
+{
+ if (UNEXPECTED(ZEND_FFI_TYPE_IS_OWNED(type))) {
+ _zend_ffi_type_dtor(type);
+ return;
+ }
+}
+/* }}} */
+
+static zend_always_inline void zend_ffi_object_init(zend_object *object, zend_class_entry *ce) /* {{{ */
+{
+ GC_SET_REFCOUNT(object, 1);
+ GC_TYPE_INFO(object) = IS_OBJECT | ((GC_COLLECTABLE | IS_OBJ_DESTRUCTOR_CALLED) << GC_FLAGS_SHIFT);
+ object->ce = ce;
+ object->properties = NULL;
+ zend_objects_store_put(object);
+}
+/* }}} */
+
+static zend_object *zend_ffi_cdata_new(zend_class_entry *class_type) /* {{{ */
+{
+ zend_ffi_cdata *cdata;
+
+ cdata = emalloc(sizeof(zend_ffi_cdata));
+
+ zend_ffi_object_init(&cdata->std, class_type);
+ cdata->std.handlers = &zend_ffi_cdata_handlers;
+
+ cdata->type = NULL;
+ cdata->ptr = NULL;
+ cdata->flags = 0;
+
+ return &cdata->std;
+}
+/* }}} */
+
+static int zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type) /* {{{ */
+{
+ while (1) {
+ if (dst_type == src_type) {
+ return 1;
+ } else if (dst_type->kind == src_type->kind) {
+ if (dst_type->kind < ZEND_FFI_TYPE_POINTER) {
+ return 1;
+ } else if (dst_type->kind == ZEND_FFI_TYPE_POINTER) {
+ dst_type = ZEND_FFI_TYPE(dst_type->pointer.type);
+ src_type = ZEND_FFI_TYPE(src_type->pointer.type);
+ if (dst_type->kind == ZEND_FFI_TYPE_VOID ||
+ src_type->kind == ZEND_FFI_TYPE_VOID) {
+ return 1;
+ }
+ } else if (dst_type->kind == ZEND_FFI_TYPE_ARRAY &&
+ (dst_type->array.length == src_type->array.length ||
+ dst_type->array.length == 0)) {
+ dst_type = ZEND_FFI_TYPE(dst_type->array.type);
+ src_type = ZEND_FFI_TYPE(src_type->array.type);
+ } else {
+ break;
+ }
+ } else if (dst_type->kind == ZEND_FFI_TYPE_POINTER &&
+ src_type->kind == ZEND_FFI_TYPE_ARRAY) {
+ dst_type = ZEND_FFI_TYPE(dst_type->pointer.type);
+ src_type = ZEND_FFI_TYPE(src_type->array.type);
+ if (dst_type->kind == ZEND_FFI_TYPE_VOID) {
+ return 1;
+ }
+ } else {
+ break;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+static ffi_type *zend_ffi_make_fake_struct_type(zend_ffi_type *type) /* {{{ */
+{
+ ffi_type *t = emalloc(sizeof(ffi_type) + sizeof(ffi_type*) * (zend_hash_num_elements(&type->record.fields) + 1));
+ int i;
+ zend_ffi_field *field;
+
+ t->size = type->size;
+ t->alignment = type->align;
+ t->type = FFI_TYPE_STRUCT;
+ t->elements = (ffi_type**)(t + 1);
+ i = 0;
+ ZEND_HASH_FOREACH_PTR(&type->record.fields, field) {
+ switch (ZEND_FFI_TYPE(field->type)->kind) {
+ case ZEND_FFI_TYPE_FLOAT:
+ t->elements[i] = &ffi_type_float;
+ break;
+ case ZEND_FFI_TYPE_DOUBLE:
+ t->elements[i] = &ffi_type_double;
+ break;
+#ifndef PHP_WIN32
+ case ZEND_FFI_TYPE_LONGDOUBLE:
+ t->elements[i] = &ffi_type_longdouble;
+ break;
+#endif
+ case ZEND_FFI_TYPE_SINT8:
+ case ZEND_FFI_TYPE_UINT8:
+ case ZEND_FFI_TYPE_BOOL:
+ case ZEND_FFI_TYPE_CHAR:
+ t->elements[i] = &ffi_type_uint8;
+ break;
+ case ZEND_FFI_TYPE_SINT16:
+ case ZEND_FFI_TYPE_UINT16:
+ t->elements[i] = &ffi_type_uint16;
+ break;
+ case ZEND_FFI_TYPE_SINT32:
+ case ZEND_FFI_TYPE_UINT32:
+ t->elements[i] = &ffi_type_uint32;
+ break;
+ case ZEND_FFI_TYPE_SINT64:
+ case ZEND_FFI_TYPE_UINT64:
+ t->elements[i] = &ffi_type_uint64;
+ break;
+ case ZEND_FFI_TYPE_POINTER:
+ t->elements[i] = &ffi_type_pointer;
+ break;
+ default:
+ efree(t);
+ zend_throw_error(zend_ffi_exception_ce, "Passing incompatible struct/union");
+ return NULL;
+ }
+ i++;
+ } ZEND_HASH_FOREACH_END();
+ t->elements[i] = NULL;
+ return t;
+}
+/* }}} */
+
+static ffi_type *zend_ffi_get_type(zend_ffi_type *type) /* {{{ */
+{
+ zend_ffi_type_kind kind = type->kind;
+
+again:
+ switch (kind) {
+ case ZEND_FFI_TYPE_FLOAT:
+ return &ffi_type_float;
+ case ZEND_FFI_TYPE_DOUBLE:
+ return &ffi_type_double;
+#ifndef PHP_WIN32
+ case ZEND_FFI_TYPE_LONGDOUBLE:
+ return &ffi_type_longdouble;
+#endif
+ case ZEND_FFI_TYPE_UINT8:
+ return &ffi_type_uint8;
+ case ZEND_FFI_TYPE_SINT8:
+ return &ffi_type_sint8;
+ case ZEND_FFI_TYPE_UINT16:
+ return &ffi_type_uint16;
+ case ZEND_FFI_TYPE_SINT16:
+ return &ffi_type_sint16;
+ case ZEND_FFI_TYPE_UINT32:
+ return &ffi_type_uint32;
+ case ZEND_FFI_TYPE_SINT32:
+ return &ffi_type_sint32;
+ case ZEND_FFI_TYPE_UINT64:
+ return &ffi_type_uint64;
+ case ZEND_FFI_TYPE_SINT64:
+ return &ffi_type_sint64;
+ case ZEND_FFI_TYPE_POINTER:
+ return &ffi_type_pointer;
+ case ZEND_FFI_TYPE_VOID:
+ return &ffi_type_void;
+ case ZEND_FFI_TYPE_BOOL:
+ return &ffi_type_uint8;
+ case ZEND_FFI_TYPE_CHAR:
+ return &ffi_type_sint8;
+ case ZEND_FFI_TYPE_ENUM:
+ kind = type->enumeration.kind;
+ goto again;
+ case ZEND_FFI_TYPE_STRUCT:
+ if (!(type->attr & ZEND_FFI_ATTR_UNION)) {
+ ffi_type *t = zend_ffi_make_fake_struct_type(type);
+ if (t) {
+ return t;
+ }
+ }
+ zend_throw_error(zend_ffi_exception_ce, "FFI return struct/union is not implemented");
+ break;
+ case ZEND_FFI_TYPE_ARRAY:
+ zend_throw_error(zend_ffi_exception_ce, "FFI return array is not implemented");
+ break;
+ default:
+ zend_throw_error(zend_ffi_exception_ce, "FFI internal error");
+ break;
+ }
+ return NULL;
+}
+/* }}} */
+
+static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */
+{
+ zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
+
+ zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
+ cdata->std.handlers =
+ (type->kind < ZEND_FFI_TYPE_POINTER) ?
+ &zend_ffi_cdata_value_handlers :
+ &zend_ffi_cdata_handlers;
+ cdata->type = type;
+ cdata->flags = flags;
+ cdata->ptr = ptr;
+ return cdata;
+}
+/* }}} */
+
+static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow_ptr(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */
+{
+ zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
+
+ zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
+ cdata->std.handlers = &zend_ffi_cdata_handlers;
+ cdata->type = type;
+ cdata->flags = flags;
+ cdata->ptr = (void*)&cdata->ptr_holder;
+ *(void**)cdata->ptr = *(void**)ptr;
+ return cdata;
+}
+/* }}} */
+
+static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow_ret(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */
+{
+ zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
+
+ zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
+ cdata->std.handlers =
+ (type->kind < ZEND_FFI_TYPE_POINTER) ?
+ &zend_ffi_cdata_value_handlers :
+ &zend_ffi_cdata_handlers;
+ cdata->type = type;
+ cdata->flags = flags;
+ if (type->kind == ZEND_FFI_TYPE_POINTER) {
+ cdata->ptr = (void*)&cdata->ptr_holder;
+ *(void**)cdata->ptr = *(void**)ptr;
+ } else if (type->kind == ZEND_FFI_TYPE_STRUCT) {
+ cdata->ptr = emalloc(type->size);
+ cdata->flags |= ZEND_FFI_FLAG_OWNED;
+ memcpy(cdata->ptr, ptr, type->size);
+ } else {
+ cdata->ptr = ptr;
+ }
+ return cdata;
+}
+/* }}} */
+
+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, zend_bool is_ret) /* {{{ */
+{
+ if (read_type == BP_VAR_R) {
+ zend_ffi_type_kind kind = type->kind;
+
+again:
+ switch (kind) {
+ case ZEND_FFI_TYPE_FLOAT:
+ ZVAL_DOUBLE(rv, *(float*)ptr);
+ return;
+ case ZEND_FFI_TYPE_DOUBLE:
+ ZVAL_DOUBLE(rv, *(double*)ptr);
+ return;
+#ifdef HAVE_LONG_DOUBLE
+ case ZEND_FFI_TYPE_LONGDOUBLE:
+ ZVAL_DOUBLE(rv, *(long double*)ptr);
+ return;
+#endif
+ case ZEND_FFI_TYPE_UINT8:
+ ZVAL_LONG(rv, *(uint8_t*)ptr);
+ return;
+ case ZEND_FFI_TYPE_SINT8:
+ ZVAL_LONG(rv, *(int8_t*)ptr);
+ return;
+ case ZEND_FFI_TYPE_UINT16:
+ ZVAL_LONG(rv, *(uint16_t*)ptr);
+ return;
+ case ZEND_FFI_TYPE_SINT16:
+ ZVAL_LONG(rv, *(int16_t*)ptr);
+ return;
+ case ZEND_FFI_TYPE_UINT32:
+ ZVAL_LONG(rv, *(uint32_t*)ptr);
+ return;
+ case ZEND_FFI_TYPE_SINT32:
+ ZVAL_LONG(rv, *(int32_t*)ptr);
+ return;
+ case ZEND_FFI_TYPE_UINT64:
+ ZVAL_LONG(rv, *(uint64_t*)ptr);
+ return;
+ case ZEND_FFI_TYPE_SINT64:
+ ZVAL_LONG(rv, *(int64_t*)ptr);
+ return;
+ case ZEND_FFI_TYPE_BOOL:
+ ZVAL_BOOL(rv, *(uint8_t*)ptr);
+ return;
+ case ZEND_FFI_TYPE_CHAR:
+ ZVAL_INTERNED_STR(rv, ZSTR_CHAR(*(unsigned char*)ptr));
+ return;
+ case ZEND_FFI_TYPE_ENUM:
+ kind = type->enumeration.kind;
+ goto again;
+ case ZEND_FFI_TYPE_POINTER:
+ if (*(void**)ptr == NULL) {
+ ZVAL_NULL(rv);
+ return;
+ } else if ((type->attr & ZEND_FFI_ATTR_CONST) && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) {
+ ZVAL_STRING(rv, *(char**)ptr);
+ return;
+ }
+ if (!cdata) {
+ if (is_ret) {
+ cdata = zend_ffi_cdata_to_zval_slow_ret(ptr, type, flags);
+ } else {
+ cdata = zend_ffi_cdata_to_zval_slow_ptr(ptr, type, flags);
+ }
+ } else {
+ GC_ADDREF(&cdata->std);
+ }
+ ZVAL_OBJ(rv, &cdata->std);
+ return;
+ default:
+ break;
+ }
+ }
+
+ if (!cdata) {
+ if (is_ret) {
+ cdata = zend_ffi_cdata_to_zval_slow_ret(ptr, type, flags);
+ } else {
+ cdata = zend_ffi_cdata_to_zval_slow(ptr, type, flags);
+ }
+ } else {
+ GC_ADDREF(&cdata->std);
+ }
+ ZVAL_OBJ(rv, &cdata->std);
+}
+/* }}} */
+
+static void zend_ffi_bit_field_to_zval(void *ptr, zend_ffi_field *field, zval *rv) /* {{{ */
+{
+ uint64_t *p1 = (uint64_t *)((char*)ptr + (field->first_bit / 64) * 8);
+ uint64_t *p2 = (uint64_t *)((char*)ptr + ((field->first_bit + field->bits - 1) / 64) * 8);
+ uint64_t pos = field->first_bit % 64;
+ uint64_t shift = 64 - (field->bits % 64);
+ uint64_t val;
+
+ if (p1 == p2) {
+ if (field->bits == 64) {
+ val = *p1;
+ shift = 0;
+ } else {
+ val = *p1 << (shift - pos);
+ }
+ } else {
+ val = (*p1 >> pos) | (*p2 << (64 - pos));
+ }
+ if (ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_CHAR
+ || ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT8
+ || ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT16
+ || ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT32
+ || ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT64) {
+ val = (int64_t)val >> shift;
+ } else {
+ val = val >> shift;
+ }
+ ZVAL_LONG(rv, val);
+}
+/* }}} */
+
+static int zend_ffi_zval_to_bit_field(void *ptr, zend_ffi_field *field, zval *value) /* {{{ */
+{
+ uint64_t *p1 = (uint64_t *)((char*)ptr + (field->first_bit / 64) * 8);
+ uint64_t *p2 = (uint64_t *)((char*)ptr + ((field->first_bit + field->bits - 1) / (8 * 8)) * 8);
+ uint64_t pos = field->first_bit % 64;
+ uint64_t mask;
+ uint64_t val = zval_get_long(value);
+
+ if (p1 == p2) {
+ if (field->bits == 64) {
+ *p1 = val;
+ } else {
+ mask = ((1ULL << field->bits) - 1ULL) << pos;
+ *p1 = (*p1 & ~mask) | ((val << pos) & mask);
+ }
+ } else {
+ mask = ((1ULL << (64 - pos)) - 1ULL) << pos;
+ *p1 = (*p1 & ~mask) | ((val << pos) & mask);
+ mask = (1ULL << pos) - 1ULL;
+ *p2 = (*p2 & ~mask) | ((val >> (64 - pos)) & mask);
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+static zend_always_inline int zend_ffi_zval_to_cdata(void *ptr, zend_ffi_type *type, zval *value) /* {{{ */
+{
+ zend_long lval;
+ double dval;
+ zend_string *tmp_str;
+ zend_string *str;
+ zend_ffi_type_kind kind = type->kind;
+
+again:
+ switch (kind) {
+ case ZEND_FFI_TYPE_FLOAT:
+ dval = zval_get_double(value);
+ *(float*)ptr = dval;
+ break;
+ case ZEND_FFI_TYPE_DOUBLE:
+ dval = zval_get_double(value);
+ *(double*)ptr = dval;
+ break;
+#ifdef HAVE_LONG_DOUBLE
+ case ZEND_FFI_TYPE_LONGDOUBLE:
+ dval = zval_get_double(value);
+ *(long double*)ptr = dval;
+ break;
+#endif
+ case ZEND_FFI_TYPE_UINT8:
+ lval = zval_get_long(value);
+ *(uint8_t*)ptr = lval;
+ break;
+ case ZEND_FFI_TYPE_SINT8:
+ lval = zval_get_long(value);
+ *(int8_t*)ptr = lval;
+ break;
+ case ZEND_FFI_TYPE_UINT16:
+ lval = zval_get_long(value);
+ *(uint16_t*)ptr = lval;
+ break;
+ case ZEND_FFI_TYPE_SINT16:
+ lval = zval_get_long(value);
+ *(int16_t*)ptr = lval;
+ break;
+ case ZEND_FFI_TYPE_UINT32:
+ lval = zval_get_long(value);
+ *(uint32_t*)ptr = lval;
+ break;
+ case ZEND_FFI_TYPE_SINT32:
+ lval = zval_get_long(value);
+ *(int32_t*)ptr = lval;
+ break;
+ case ZEND_FFI_TYPE_UINT64:
+ lval = zval_get_long(value);
+ *(uint64_t*)ptr = lval;
+ break;
+ case ZEND_FFI_TYPE_SINT64:
+ lval = zval_get_long(value);
+ *(int64_t*)ptr = lval;
+ break;
+ case ZEND_FFI_TYPE_BOOL:
+ *(uint8_t*)ptr = zend_is_true(value);
+ break;
+ case ZEND_FFI_TYPE_CHAR:
+ str = zval_get_tmp_string(value, &tmp_str);
+ if (ZSTR_LEN(str) == 1) {
+ *(char*)ptr = ZSTR_VAL(str)[0];
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign of incompatible C type");
+ return FAILURE;
+ }
+ zend_tmp_string_release(tmp_str);
+ break;
+ case ZEND_FFI_TYPE_ENUM:
+ kind = type->enumeration.kind;
+ goto again;
+ case ZEND_FFI_TYPE_POINTER:
+ if (Z_TYPE_P(value) == IS_NULL) {
+ *(void**)ptr = NULL;
+ break;
+ } else if (Z_TYPE_P(value) == IS_OBJECT && Z_OBJCE_P(value) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(value);
+
+ if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) {
+ if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
+ *(void**)ptr = *(void**)cdata->ptr;
+ } else {
+ if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign of owned C pointer");
+ return FAILURE;
+ }
+ *(void**)ptr = cdata->ptr;
+ }
+ return SUCCESS;
+ /* Allow transparent assignment of not-owned CData to compatible pointers */
+ } else if (ZEND_FFI_TYPE(cdata->type)->kind != ZEND_FFI_TYPE_POINTER
+ && zend_ffi_is_compatible_type(ZEND_FFI_TYPE(type->pointer.type), ZEND_FFI_TYPE(cdata->type))) {
+ if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign pointer to owned C data");
+ return FAILURE;
+ }
+ *(void**)ptr = cdata->ptr;
+ return SUCCESS;
+ }
+#if FFI_CLOSURES
+ } else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_FUNC) {
+ void *callback = zend_ffi_create_callback(ZEND_FFI_TYPE(type->pointer.type), value);
+
+ if (callback) {
+ *(void**)ptr = callback;
+ break;
+ } else {
+ return FAILURE;
+ }
+#endif
+ }
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign of incompatible C type");
+ return FAILURE;
+ case ZEND_FFI_TYPE_STRUCT:
+ case ZEND_FFI_TYPE_ARRAY:
+ default:
+ if (Z_TYPE_P(value) == IS_OBJECT && Z_OBJCE_P(value) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(value);
+ if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type)) &&
+ type->size == ZEND_FFI_TYPE(cdata->type)->size) {
+ memcpy(ptr, cdata->ptr, type->size);
+ return SUCCESS;
+ }
+ }
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign of incompatible C type");
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+#if FFI_CLOSURES
+typedef struct _zend_ffi_callback_data {
+ zend_fcall_info_cache fcc;
+ zend_ffi_type *type;
+ void *code;
+ void *callback;
+ ffi_cif cif;
+ uint32_t arg_count;
+ ffi_type *ret_type;
+ ffi_type *arg_types[0];
+} zend_ffi_callback_data;
+
+static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */
+{
+ zend_ffi_callback_data *callback_data = Z_PTR_P(zv);
+
+ ffi_closure_free(callback_data->callback);
+ if (callback_data->fcc.function_handler->common.fn_flags & ZEND_ACC_CLOSURE) {
+ OBJ_RELEASE(ZEND_CLOSURE_OBJECT(callback_data->fcc.function_handler));
+ }
+ efree(callback_data);
+}
+/* }}} */
+
+static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */
+{
+ zend_ffi_callback_data *callback_data = (zend_ffi_callback_data*)data;
+ zend_fcall_info fci;
+ zval retval;
+ ALLOCA_FLAG(use_heap)
+
+ fci.size = sizeof(zend_fcall_info);
+ ZVAL_UNDEF(&fci.function_name);
+ fci.retval = &retval;
+ fci.params = do_alloca(sizeof(zval) *callback_data->arg_count, use_heap);
+ fci.object = NULL;
+ fci.no_separation = 1;
+ fci.param_count = callback_data->arg_count;
+
+ if (callback_data->type->func.args) {
+ int n = 0;
+ zend_ffi_type *arg_type;
+
+ ZEND_HASH_FOREACH_PTR(callback_data->type->func.args, arg_type) {
+ arg_type = ZEND_FFI_TYPE(arg_type);
+ 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);
+ n++;
+ } ZEND_HASH_FOREACH_END();
+ }
+
+ ZVAL_UNDEF(&retval);
+ if (zend_call_function(&fci, &callback_data->fcc) != SUCCESS) {
+ zend_throw_error(zend_ffi_exception_ce, "Cannot call callback");
+ }
+
+ if (callback_data->arg_count) {
+ int n = 0;
+
+ for (n = 0; n < callback_data->arg_count; n++) {
+ zval_ptr_dtor(&fci.params[n]);
+ n++;
+ }
+ }
+ free_alloca(fci.params, use_heap);
+
+ zend_ffi_zval_to_cdata(ret, ZEND_FFI_TYPE(callback_data->type->func.ret_type), &retval);
+}
+/* }}} */
+
+static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ */
+{
+ zend_fcall_info_cache fcc;
+ char *error = NULL;
+ uint32_t arg_count;
+ void *code;
+ void *callback;
+ zend_ffi_callback_data *callback_data;
+
+ if (type->attr & ZEND_FFI_ATTR_VARIADIC) {
+ zend_throw_error(zend_ffi_exception_ce, "Variadic function closures are not supported");
+ return NULL;
+ }
+
+ if (!zend_is_callable_ex(value, NULL, 0, NULL, &fcc, &error)) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to assing an invalid callback, %s", error);
+ return NULL;
+ }
+
+ arg_count = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
+ if (arg_count < fcc.function_handler->common.required_num_args) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to assing an invalid callback, insufficient number of arguments");
+ return NULL;
+ }
+
+ callback = ffi_closure_alloc(sizeof(ffi_closure), &code);
+ if (!callback) {
+ zend_throw_error(zend_ffi_exception_ce, "Cannot allocate callback");
+ return NULL;
+ }
+
+ callback_data = emalloc(sizeof(zend_ffi_callback_data) + sizeof(ffi_type*) * arg_count);
+ memcpy(&callback_data->fcc, &fcc, sizeof(zend_fcall_info_cache));
+ callback_data->type = type;
+ callback_data->callback = callback;
+ callback_data->code = code;
+ callback_data->arg_count = arg_count;
+
+ if (type->func.args) {
+ int n = 0;
+ zend_ffi_type *arg_type;
+
+ ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) {
+ arg_type = ZEND_FFI_TYPE(arg_type);
+ callback_data->arg_types[n] = zend_ffi_get_type(arg_type);
+ if (!callback_data->arg_types[n]) {
+ efree(callback_data);
+ ffi_closure_free(callback);
+ return NULL;
+ }
+ n++;
+ } ZEND_HASH_FOREACH_END();
+ }
+ callback_data->ret_type = zend_ffi_get_type(type->func.ret_type);
+ if (!callback_data->ret_type) {
+ efree(callback_data);
+ ffi_closure_free(callback);
+ return NULL;
+ }
+
+ if (ffi_prep_cif(&callback_data->cif, type->func.abi, callback_data->arg_count, callback_data->ret_type, callback_data->arg_types) != FFI_OK) {
+ zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF");
+ efree(callback_data);
+ ffi_closure_free(callback);
+ return NULL;
+ }
+
+ if (ffi_prep_closure_loc(callback, &callback_data->cif, zend_ffi_callback_trampoline, callback_data, code) != FFI_OK) {
+ zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback");
+ efree(callback_data);
+ ffi_closure_free(callback);
+ return NULL;
+ }
+
+ if (!FFI_G(callbacks)) {
+ FFI_G(callbacks) = emalloc(sizeof(HashTable));
+ zend_hash_init(FFI_G(callbacks), 0, NULL, zend_ffi_callback_hash_dtor, 0);
+ }
+ zend_hash_next_index_insert_ptr(FFI_G(callbacks), callback_data);
+
+ if (fcc.function_handler->common.fn_flags & ZEND_ACC_CLOSURE) {
+ GC_ADDREF(ZEND_CLOSURE_OBJECT(fcc.function_handler));
+ }
+
+ return code;
+}
+/* }}} */
+#endif
+
+static zval* zend_ffi_cdata_get(zval *object, zval *rv) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+
+#if 0
+ if (UNEXPECTED(!cdata->ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return &EG(uninitialized_zval);
+ }
+#endif
+
+ zend_ffi_cdata_to_zval(cdata, cdata->ptr, type, BP_VAR_R, rv, 0, 0);
+ return rv;
+}
+/* }}} */
+
+static void zend_ffi_cdata_set(zval *object, zval *value) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+
+#if 0
+ if (UNEXPECTED(!cdata->ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return;
+ }
+#endif
+
+ zend_ffi_zval_to_cdata(cdata->ptr, type, value);
+}
+/* }}} */
+
+static int zend_ffi_cdata_cast_object(zval *readobj, zval *writeobj, int type) /* {{{ */
+{
+ return FAILURE;
+}
+/* }}} */
+
+static zval *zend_ffi_cdata_read_field(zval *object, zval *member, int read_type, void **cache_slot, zval *rv) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+ void *ptr = cdata->ptr;
+ zend_ffi_field *field;
+
+ if (cache_slot && *cache_slot == type) {
+ field = *(cache_slot + 1);
+ } else {
+ zend_string *tmp_field_name;
+ zend_string *field_name = zval_get_tmp_string(member, &tmp_field_name);
+
+ if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
+ if (type->kind == ZEND_FFI_TYPE_POINTER) {
+ /* transparently dereference the pointer */
+ if (UNEXPECTED(!ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ zend_tmp_string_release(tmp_field_name);
+ return &EG(uninitialized_zval);
+ }
+ ptr = (void*)(*(char**)ptr);
+ if (UNEXPECTED(!ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ zend_tmp_string_release(tmp_field_name);
+ return &EG(uninitialized_zval);
+ }
+ type = ZEND_FFI_TYPE(type->pointer.type);
+ }
+ if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to read field '%s' of non C struct/union", ZSTR_VAL(field_name));
+ zend_tmp_string_release(tmp_field_name);
+ return &EG(uninitialized_zval);
+ }
+ }
+
+ field = zend_hash_find_ptr(&type->record.fields, field_name);
+ if (UNEXPECTED(!field)) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to read undefined field '%s' of C struct/union", ZSTR_VAL(field_name));
+ zend_tmp_string_release(tmp_field_name);
+ return &EG(uninitialized_zval);
+ }
+
+ zend_tmp_string_release(tmp_field_name);
+
+ if (cache_slot) {
+ *cache_slot = type;
+ *(cache_slot + 1) = field;
+ }
+ }
+
+#if 0
+ if (UNEXPECTED(!ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return &EG(uninitialized_zval);
+ }
+#endif
+
+ if (EXPECTED(!field->bits)) {
+ zend_ffi_type *field_type = field->type;
+
+ if (ZEND_FFI_TYPE_IS_OWNED(field_type)) {
+ field_type = ZEND_FFI_TYPE(field_type);
+ if (!(field_type->attr & ZEND_FFI_ATTR_STORED)
+ && field_type->kind == ZEND_FFI_TYPE_POINTER) {
+ field->type = field_type = zend_ffi_remember_type(field_type);
+ }
+ }
+ ptr = (void*)(((char*)ptr) + field->offset);
+ 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);
+ } else {
+ zend_ffi_bit_field_to_zval(ptr, field, rv);
+ }
+
+ return rv;
+}
+/* }}} */
+
+static zval *zend_ffi_cdata_write_field(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+ void *ptr = cdata->ptr;
+ zend_ffi_field *field;
+
+ if (cache_slot && *cache_slot == type) {
+ field = *(cache_slot + 1);
+ } else {
+ zend_string *tmp_field_name;
+ zend_string *field_name = zval_get_tmp_string(member, &tmp_field_name);
+
+ if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
+ if (type->kind == ZEND_FFI_TYPE_POINTER) {
+ /* transparently dereference the pointer */
+ if (UNEXPECTED(!ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ zend_tmp_string_release(tmp_field_name);
+ return value;
+ }
+ ptr = (void*)(*(char**)ptr);
+ if (UNEXPECTED(!ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ zend_tmp_string_release(tmp_field_name);
+ return value;
+ }
+ type = ZEND_FFI_TYPE(type->pointer.type);
+ }
+ if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to assign field '%s' of non C struct/union", ZSTR_VAL(field_name));
+ zend_tmp_string_release(tmp_field_name);
+ return value;
+ }
+ }
+
+ field = zend_hash_find_ptr(&type->record.fields, field_name);
+ if (UNEXPECTED(!field)) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to assign undefined field '%s' of C struct/union", ZSTR_VAL(field_name));
+ zend_tmp_string_release(tmp_field_name);
+ return value;
+ }
+
+ zend_tmp_string_release(tmp_field_name);
+
+ if (cache_slot) {
+ *cache_slot = type;
+ *(cache_slot + 1) = field;
+ }
+ }
+
+#if 0
+ if (UNEXPECTED(!ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return value;
+ }
+#endif
+
+ if (UNEXPECTED(cdata->flags & ZEND_FFI_FLAG_CONST)) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only location");
+ return value;
+ } else if (UNEXPECTED(field->is_const)) {
+ zend_string *tmp_field_name;
+ zend_string *field_name = zval_get_tmp_string(member, &tmp_field_name);
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only field '%s'", ZSTR_VAL(field_name));
+ zend_tmp_string_release(tmp_field_name);
+ return value;
+ }
+
+ if (EXPECTED(!field->bits)) {
+ ptr = (void*)(((char*)ptr) + field->offset);
+ zend_ffi_zval_to_cdata(ptr, ZEND_FFI_TYPE(field->type), value);
+ } else {
+ zend_ffi_zval_to_bit_field(ptr, field, value);
+ }
+ return value;
+}
+/* }}} */
+
+static zval *zend_ffi_cdata_read_dim(zval *object, zval *offset, int read_type, zval *rv) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+ zend_long dim = zval_get_long(offset);
+ zend_ffi_type *dim_type;
+ void *ptr;
+ zend_ffi_flags is_const;
+
+ if (EXPECTED(type->kind == ZEND_FFI_TYPE_ARRAY)) {
+ if (UNEXPECTED((zend_ulong)(dim) >= (zend_ulong)type->array.length)
+ && (UNEXPECTED(dim < 0) || UNEXPECTED(type->array.length != 0))) {
+ zend_throw_error(zend_ffi_exception_ce, "C array index out of bounds");
+ return &EG(uninitialized_zval);
+ }
+
+ is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
+
+ dim_type = type->array.type;
+ if (ZEND_FFI_TYPE_IS_OWNED(dim_type)) {
+ dim_type = ZEND_FFI_TYPE(dim_type);
+ if (!(dim_type->attr & ZEND_FFI_ATTR_STORED)
+ && dim_type->kind == ZEND_FFI_TYPE_POINTER) {
+ type->array.type = dim_type = zend_ffi_remember_type(dim_type);
+ }
+ }
+#if 0
+ if (UNEXPECTED(!cdata->ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return &EG(uninitialized_zval);
+ }
+#endif
+ ptr = (void*)(((char*)cdata->ptr) + dim_type->size * dim);
+ } else if (EXPECTED(type->kind == ZEND_FFI_TYPE_POINTER)) {
+ is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
+ dim_type = type->pointer.type;
+ if (ZEND_FFI_TYPE_IS_OWNED(dim_type)) {
+ dim_type = ZEND_FFI_TYPE(dim_type);
+ if (!(dim_type->attr & ZEND_FFI_ATTR_STORED)
+ && dim_type->kind == ZEND_FFI_TYPE_POINTER) {
+ type->pointer.type = dim_type = zend_ffi_remember_type(dim_type);
+ }
+ }
+ if (UNEXPECTED(!cdata->ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return &EG(uninitialized_zval);
+ }
+ ptr = (void*)((*(char**)cdata->ptr) + dim_type->size * dim);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to read element of non C array");
+ return &EG(uninitialized_zval);
+ }
+
+ zend_ffi_cdata_to_zval(NULL, ptr, dim_type, read_type, rv, is_const, 0);
+ return rv;
+}
+/* }}} */
+
+static void zend_ffi_cdata_write_dim(zval *object, zval *offset, zval *value) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+ zend_long dim = zval_get_long(offset);
+ void *ptr;
+ zend_ffi_flags is_const;
+
+ if (EXPECTED(type->kind == ZEND_FFI_TYPE_ARRAY)) {
+ if (UNEXPECTED((zend_ulong)(dim) >= (zend_ulong)type->array.length)
+ && (UNEXPECTED(dim < 0) || UNEXPECTED(type->array.length != 0))) {
+ zend_throw_error(zend_ffi_exception_ce, "C array index out of bounds");
+ return;
+ }
+
+ is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
+ type = ZEND_FFI_TYPE(type->array.type);
+#if 0
+ if (UNEXPECTED(!cdata->ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return;
+ }
+#endif
+ ptr = (void*)(((char*)cdata->ptr) + type->size * dim);
+ } else if (EXPECTED(type->kind == ZEND_FFI_TYPE_POINTER)) {
+ is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
+ type = ZEND_FFI_TYPE(type->pointer.type);
+ if (UNEXPECTED(!cdata->ptr)) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return;
+ }
+ ptr = (void*)((*(char**)cdata->ptr) + type->size * dim);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to assign element of non C array");
+ return;
+ }
+
+ if (UNEXPECTED(is_const)) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only location");
+ return;
+ }
+
+ zend_ffi_zval_to_cdata(ptr, type, value);
+}
+/* }}} */
+
+#define MAX_TYPE_NAME_LEN 256
+
+typedef struct _zend_ffi_ctype_name_buf {
+ char *start;
+ char *end;
+ char buf[MAX_TYPE_NAME_LEN];
+} zend_ffi_ctype_name_buf;
+
+static int zend_ffi_ctype_name_prepend(zend_ffi_ctype_name_buf *buf, const char *str, size_t len) /* {{{ */
+{
+ buf->start -= len;
+ if (buf->start < buf->buf) {
+ return 0;
+ }
+ memcpy(buf->start, str, len);
+ return 1;
+}
+/* }}} */
+
+static int zend_ffi_ctype_name_append(zend_ffi_ctype_name_buf *buf, const char *str, size_t len) /* {{{ */
+{
+ if (buf->end + len > buf->buf + MAX_TYPE_NAME_LEN) {
+ return 0;
+ }
+ memcpy(buf->end, str, len);
+ buf->end += len;
+ return 1;
+}
+/* }}} */
+
+static int zend_ffi_ctype_name(zend_ffi_ctype_name_buf *buf, const zend_ffi_type *type) /* {{{ */
+{
+ const char *name = NULL;
+ int is_ptr = 0;
+
+ while (1) {
+ switch (type->kind) {
+ case ZEND_FFI_TYPE_VOID:
+ name = "void";
+ break;
+ case ZEND_FFI_TYPE_FLOAT:
+ name = "float";
+ break;
+ case ZEND_FFI_TYPE_DOUBLE:
+ name = "double";
+ break;
+#ifdef HAVE_LONG_DOUBLE
+ case ZEND_FFI_TYPE_LONGDOUBLE:
+ name = "long double";
+ break;
+#endif
+ case ZEND_FFI_TYPE_UINT8:
+ name = "uint8_t";
+ break;
+ case ZEND_FFI_TYPE_SINT8:
+ name = "int8_t";
+ break;
+ case ZEND_FFI_TYPE_UINT16:
+ name = "uint16_t";
+ break;
+ case ZEND_FFI_TYPE_SINT16:
+ name = "int16_t";
+ break;
+ case ZEND_FFI_TYPE_UINT32:
+ name = "uint32_t";
+ break;
+ case ZEND_FFI_TYPE_SINT32:
+ name = "int32_t";
+ break;
+ case ZEND_FFI_TYPE_UINT64:
+ name = "uint64_t";
+ break;
+ case ZEND_FFI_TYPE_SINT64:
+ name = "int64_t";
+ break;
+ case ZEND_FFI_TYPE_ENUM:
+ name = "<enum>";
+ break;
+ case ZEND_FFI_TYPE_BOOL:
+ name = "bool";
+ break;
+ case ZEND_FFI_TYPE_CHAR:
+ name = "char";
+ break;
+ case ZEND_FFI_TYPE_POINTER:
+ if (!zend_ffi_ctype_name_prepend(buf, "*", 1)) {
+ return 0;
+ }
+ is_ptr = 1;
+ type = ZEND_FFI_TYPE(type->pointer.type);
+ break;
+ case ZEND_FFI_TYPE_FUNC:
+ if (is_ptr) {
+ is_ptr = 0;
+ if (!zend_ffi_ctype_name_prepend(buf, "(", 1)
+ || !zend_ffi_ctype_name_append(buf, ")", 1)) {
+ return 0;
+ }
+ }
+ if (!zend_ffi_ctype_name_append(buf, "(", 1)
+ || !zend_ffi_ctype_name_append(buf, ")", 1)) {
+ return 0;
+ }
+ type = ZEND_FFI_TYPE(type->func.ret_type);
+ break;
+ case ZEND_FFI_TYPE_ARRAY:
+ if (is_ptr) {
+ is_ptr = 0;
+ if (!zend_ffi_ctype_name_prepend(buf, "(", 1)
+ || !zend_ffi_ctype_name_append(buf, ")", 1)) {
+ return 0;
+ }
+ }
+ if (!zend_ffi_ctype_name_append(buf, "[", 1)) {
+ return 0;
+ }
+ if (type->attr & ZEND_FFI_ATTR_VLA) {
+ if (!zend_ffi_ctype_name_append(buf, "*", 1)) {
+ return 0;
+ }
+ } else if (!(type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
+ char str[MAX_LENGTH_OF_LONG + 1];
+ char *s = zend_print_long_to_buf(str + sizeof(str) - 1, type->array.length);
+
+ if (!zend_ffi_ctype_name_append(buf, s, strlen(s))) {
+ return 0;
+ }
+ }
+ if (!zend_ffi_ctype_name_append(buf, "]", 1)) {
+ return 0;
+ }
+ type = ZEND_FFI_TYPE(type->array.type);
+ break;
+ case ZEND_FFI_TYPE_STRUCT:
+ if (type->attr & ZEND_FFI_ATTR_UNION) {
+ name = "<union>";
+ } else {
+ name = "<struct>";
+ }
+ break;
+ default:
+ ZEND_ASSERT(0);
+ }
+ if (name) {
+ break;
+ }
+ }
+
+// if (buf->start != buf->end && *buf->start != '[') {
+// if (!zend_ffi_ctype_name_prepend(buf, " ", 1)) {
+// return 0;
+// }
+// }
+ return zend_ffi_ctype_name_prepend(buf, name, strlen(name));
+}
+/* }}} */
+
+static zend_string *zend_ffi_get_class_name(zend_string *prefix, const zend_ffi_type *type) /* {{{ */
+{
+ zend_ffi_ctype_name_buf buf;
+
+ buf.start = buf.end = buf.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
+ if (!zend_ffi_ctype_name(&buf, type)) {
+ return zend_string_copy(prefix);
+ } else {
+ zend_string *name = zend_string_alloc(ZSTR_LEN(prefix) + 1 + buf.end - buf.start, 0);
+ memcpy(ZSTR_VAL(name), ZSTR_VAL(prefix), ZSTR_LEN(prefix));
+ ZSTR_VAL(name)[ZSTR_LEN(prefix)] = ':';
+ memcpy(ZSTR_VAL(name) + ZSTR_LEN(prefix) + 1, buf.start, buf.end - buf.start);
+ ZSTR_VAL(name)[ZSTR_LEN(name)] = 0;
+ return name;
+ }
+}
+/* }}} */
+
+static zend_string *zend_ffi_cdata_get_class_name(const zend_object *zobj) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)zobj;
+
+ return zend_ffi_get_class_name(zobj->ce->name, ZEND_FFI_TYPE(cdata->type));
+}
+/* }}} */
+
+static int zend_ffi_cdata_compare_objects(zval *o1, zval *o2) /* {{{ */
+{
+ if (Z_TYPE_P(o1) == IS_OBJECT && Z_OBJCE_P(o1) == zend_ffi_cdata_ce &&
+ Z_TYPE_P(o2) == IS_OBJECT && Z_OBJCE_P(o2) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata1 = (zend_ffi_cdata*)Z_OBJ_P(o1);
+ zend_ffi_cdata *cdata2 = (zend_ffi_cdata*)Z_OBJ_P(o2);
+ zend_ffi_type *type1 = ZEND_FFI_TYPE(cdata1->type);
+ zend_ffi_type *type2 = ZEND_FFI_TYPE(cdata2->type);
+
+ if (type1->kind == ZEND_FFI_TYPE_POINTER && type2->kind == ZEND_FFI_TYPE_POINTER) {
+ void *ptr1 = *(void**)cdata1->ptr;
+ void *ptr2 = *(void**)cdata2->ptr;
+
+ if (!ptr1 || !ptr2) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return 0;
+ }
+ return ptr1 == ptr2 ? 0 : (ptr1 < ptr2 ? -1 : 1);
+ }
+ }
+ zend_throw_error(zend_ffi_exception_ce, "Comparison of incompatible C types");
+ return 0;
+}
+/* }}} */
+
+static int zend_ffi_cdata_count_elements(zval *object, zend_long *count) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+
+ if (type->kind != ZEND_FFI_TYPE_ARRAY) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to count() on non C array");
+ return FAILURE;
+ } else {
+ *count = type->array.length;
+ return SUCCESS;
+ }
+}
+/* }}} */
+
+static zend_object* zend_ffi_add(zend_ffi_cdata *base_cdata, zend_ffi_type *base_type, zend_long offset) /* {{{ */
+{
+ char *ptr;
+ zend_ffi_type *ptr_type;
+ zend_ffi_cdata *cdata =
+ (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
+
+ if (base_type->kind == ZEND_FFI_TYPE_POINTER) {
+ if (ZEND_FFI_TYPE_IS_OWNED(base_cdata->type)) {
+ if (!(base_type->attr & ZEND_FFI_ATTR_STORED)) {
+ if (GC_REFCOUNT(&base_cdata->std) == 1) {
+ /* transfer type ownership */
+ base_cdata->type = base_type;
+ base_type = ZEND_FFI_TYPE_MAKE_OWNED(base_type);
+ } else {
+ base_cdata->type = base_type = zend_ffi_remember_type(base_type);
+ }
+ }
+ }
+ cdata->type = base_type;
+ ptr = (char*)(*(void**)base_cdata->ptr);
+ ptr_type = ZEND_FFI_TYPE(base_type)->pointer.type;
+ } else {
+ zend_ffi_type *new_type = emalloc(sizeof(zend_ffi_type));
+
+ new_type->kind = ZEND_FFI_TYPE_POINTER;
+ new_type->attr = 0;
+ new_type->size = sizeof(void*);
+ new_type->align = _Alignof(void*);
+
+ ptr_type = base_type->array.type;
+ if (ZEND_FFI_TYPE_IS_OWNED(ptr_type)) {
+ ptr_type = ZEND_FFI_TYPE(ptr_type);
+ if (!(ptr_type->attr & ZEND_FFI_ATTR_STORED)) {
+ if (GC_REFCOUNT(&base_cdata->std) == 1) {
+ /* transfer type ownership */
+ base_type->array.type = ptr_type;
+ ptr_type = ZEND_FFI_TYPE_MAKE_OWNED(ptr_type);
+ } else {
+ base_type->array.type = ptr_type = zend_ffi_remember_type(ptr_type);
+ }
+ }
+ }
+ new_type->pointer.type = ptr_type;
+
+ cdata->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
+ ptr = (char*)base_cdata->ptr;
+ }
+ cdata->ptr = &cdata->ptr_holder;
+ cdata->ptr_holder = ptr +
+ offset * ZEND_FFI_TYPE(ptr_type)->size;
+ cdata->flags = base_cdata->flags & ZEND_FFI_FLAG_CONST;
+ return &cdata->std;
+}
+/* }}} */
+
+static int zend_ffi_cdata_do_operation(zend_uchar opcode, zval *result, zval *op1, zval *op2) /* {{{ */
+{
+ zend_long offset;
+
+ ZVAL_DEREF(op1);
+ ZVAL_DEREF(op2);
+ if (Z_TYPE_P(op1) == IS_OBJECT && Z_OBJCE_P(op1) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata1 = (zend_ffi_cdata*)Z_OBJ_P(op1);
+ zend_ffi_type *type1 = ZEND_FFI_TYPE(cdata1->type);
+
+ if (type1->kind == ZEND_FFI_TYPE_POINTER || type1->kind == ZEND_FFI_TYPE_ARRAY) {
+ if (opcode == ZEND_ADD) {
+ offset = zval_get_long(op2);
+ ZVAL_OBJ(result, zend_ffi_add(cdata1, type1, offset));
+ if (result == op1) {
+ OBJ_RELEASE(&cdata1->std);
+ }
+ return SUCCESS;
+ } else if (opcode == ZEND_SUB) {
+ if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJCE_P(op2) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata2 = (zend_ffi_cdata*)Z_OBJ_P(op2);
+ zend_ffi_type *type2 = ZEND_FFI_TYPE(cdata2->type);
+
+ if (type2->kind == ZEND_FFI_TYPE_POINTER || type2->kind == ZEND_FFI_TYPE_ARRAY) {
+ zend_ffi_type *t1, *t2;
+ char *p1, *p2;
+
+ if (type1->kind == ZEND_FFI_TYPE_POINTER) {
+ t1 = ZEND_FFI_TYPE(type1->pointer.type);
+ p1 = (char*)(*(void**)cdata1->ptr);
+ } else {
+ t1 = ZEND_FFI_TYPE(type1->array.type);
+ p1 = cdata1->ptr;
+ }
+ if (type2->kind == ZEND_FFI_TYPE_POINTER) {
+ t2 = ZEND_FFI_TYPE(type2->pointer.type);
+ p2 = (char*)(*(void**)cdata2->ptr);
+ } else {
+ t2 = ZEND_FFI_TYPE(type2->array.type);
+ p2 = cdata2->ptr;
+ }
+ if (zend_ffi_is_same_type(t1, t2)) {
+ ZVAL_LONG(result,
+ (zend_long)(p1 - p2) / (zend_long)t1->size);
+ return SUCCESS;
+ }
+ }
+ }
+ offset = zval_get_long(op2);
+ ZVAL_OBJ(result, zend_ffi_add(cdata1, type1, -offset));
+ if (result == op1) {
+ OBJ_RELEASE(&cdata1->std);
+ }
+ return SUCCESS;
+ }
+ }
+ } else if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJCE_P(op2) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata2 = (zend_ffi_cdata*)Z_OBJ_P(op2);
+ zend_ffi_type *type2 = ZEND_FFI_TYPE(cdata2->type);
+
+ if (type2->kind == ZEND_FFI_TYPE_POINTER || type2->kind == ZEND_FFI_TYPE_ARRAY) {
+ if (opcode == ZEND_ADD) {
+ offset = zval_get_long(op1);
+ ZVAL_OBJ(result, zend_ffi_add(cdata2, type2, offset));
+ return SUCCESS;
+ }
+ }
+ }
+
+ return FAILURE;
+}
+/* }}} */
+
+typedef struct _zend_ffi_cdata_iterator {
+ zend_object_iterator it;
+ zend_long key;
+ zval value;
+ zend_bool by_ref;
+} zend_ffi_cdata_iterator;
+
+static void zend_ffi_cdata_it_dtor(zend_object_iterator *iter) /* {{{ */
+{
+ zval_ptr_dtor(&((zend_ffi_cdata_iterator*)iter)->value);
+ zval_ptr_dtor(&iter->data);
+}
+/* }}} */
+
+static int zend_ffi_cdata_it_valid(zend_object_iterator *it) /* {{{ */
+{
+ zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ(iter->it.data);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+
+ return (iter->key >= 0 && iter->key < type->array.length) ? SUCCESS : FAILURE;
+}
+/* }}} */
+
+static zval *zend_ffi_cdata_it_get_current_data(zend_object_iterator *it) /* {{{ */
+{
+ zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ(iter->it.data);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+ zend_ffi_type *dim_type;
+ void *ptr;
+
+ if (!cdata->ptr) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return &EG(uninitialized_zval);
+ }
+ dim_type = type->array.type;
+ if (ZEND_FFI_TYPE_IS_OWNED(dim_type)) {
+ dim_type = ZEND_FFI_TYPE(dim_type);
+ if (!(dim_type->attr & ZEND_FFI_ATTR_STORED)
+ && dim_type->kind == ZEND_FFI_TYPE_POINTER) {
+ type->array.type = dim_type = zend_ffi_remember_type(dim_type);
+ }
+ }
+ ptr = (void*)((char*)cdata->ptr + dim_type->size * iter->it.index);
+
+ zval_ptr_dtor(&iter->value);
+ 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);
+ return &iter->value;
+}
+/* }}} */
+
+static void zend_ffi_cdata_it_get_current_key(zend_object_iterator *it, zval *key) /* {{{ */
+{
+ zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
+ ZVAL_LONG(key, iter->key);
+}
+/* }}} */
+
+static void zend_ffi_cdata_it_move_forward(zend_object_iterator *it) /* {{{ */
+{
+ zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
+ iter->key++;
+}
+/* }}} */
+
+static void zend_ffi_cdata_it_rewind(zend_object_iterator *it) /* {{{ */
+{
+ zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
+ iter->key = 0;
+}
+/* }}} */
+
+static const zend_object_iterator_funcs zend_ffi_cdata_it_funcs = {
+ zend_ffi_cdata_it_dtor,
+ zend_ffi_cdata_it_valid,
+ zend_ffi_cdata_it_get_current_data,
+ zend_ffi_cdata_it_get_current_key,
+ zend_ffi_cdata_it_move_forward,
+ zend_ffi_cdata_it_rewind,
+ NULL
+};
+
+static zend_object_iterator *zend_ffi_cdata_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+ zend_ffi_cdata_iterator *iter;
+
+ if (type->kind != ZEND_FFI_TYPE_ARRAY) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to iterate on non C array");
+ return NULL;
+ }
+
+ iter = emalloc(sizeof(zend_ffi_cdata_iterator));
+
+ zend_iterator_init(&iter->it);
+
+ ZVAL_COPY(&iter->it.data, object);
+ iter->it.funcs = &zend_ffi_cdata_it_funcs;
+ iter->key = 0;
+ iter->by_ref = by_ref;
+ ZVAL_UNDEF(&iter->value);
+
+ return &iter->it;
+}
+/* }}} */
+
+static HashTable *zend_ffi_cdata_get_debug_info(zval *object, int *is_temp) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+ void *ptr = cdata->ptr;
+ HashTable *ht = NULL;
+ zend_string *key;
+ zend_ffi_field *f;
+ zend_long n;
+ zval tmp;
+
+ if (!cdata->ptr) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return NULL;
+ }
+
+ switch (type->kind) {
+ case ZEND_FFI_TYPE_BOOL:
+ case ZEND_FFI_TYPE_CHAR:
+ case ZEND_FFI_TYPE_ENUM:
+ case ZEND_FFI_TYPE_FLOAT:
+ case ZEND_FFI_TYPE_DOUBLE:
+#ifdef HAVE_LONG_DOUBLE
+ case ZEND_FFI_TYPE_LONGDOUBLE:
+#endif
+ case ZEND_FFI_TYPE_UINT8:
+ case ZEND_FFI_TYPE_SINT8:
+ case ZEND_FFI_TYPE_UINT16:
+ case ZEND_FFI_TYPE_SINT16:
+ case ZEND_FFI_TYPE_UINT32:
+ case ZEND_FFI_TYPE_SINT32:
+ case ZEND_FFI_TYPE_UINT64:
+ case ZEND_FFI_TYPE_SINT64:
+ zend_ffi_cdata_to_zval(cdata, ptr, type, BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0);
+ ht = zend_new_array(1);
+ zend_hash_str_add(ht, "cdata", sizeof("cdata")-1, &tmp);
+ *is_temp = 1;
+ return ht;
+ break;
+ case ZEND_FFI_TYPE_POINTER:
+ if (*(void**)ptr == NULL) {
+ ZVAL_NULL(&tmp);
+ ht = zend_new_array(1);
+ zend_hash_index_add_new(ht, 0, &tmp);
+ *is_temp = 1;
+ return ht;
+ } else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_VOID) {
+ ZVAL_LONG(&tmp, (uintptr_t)*(void**)ptr);
+ ht = zend_new_array(1);
+ zend_hash_index_add_new(ht, 0, &tmp);
+ *is_temp = 1;
+ return ht;
+ } else {
+ zend_ffi_cdata_to_zval(NULL, *(void**)ptr, ZEND_FFI_TYPE(type->pointer.type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0);
+ ht = zend_new_array(1);
+ zend_hash_index_add_new(ht, 0, &tmp);
+ *is_temp = 1;
+ return ht;
+ }
+ break;
+ case ZEND_FFI_TYPE_STRUCT:
+ ht = zend_new_array(zend_hash_num_elements(&type->record.fields));
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&type->record.fields, key, f) {
+ if (key) {
+ if (!f->bits) {
+ void *f_ptr = (void*)(((char*)ptr) + f->offset);
+ zend_ffi_cdata_to_zval(NULL, f_ptr, ZEND_FFI_TYPE(f->type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0);
+ zend_hash_add(ht, key, &tmp);
+ } else {
+ zend_ffi_bit_field_to_zval(ptr, f, &tmp);
+ zend_hash_add(ht, key, &tmp);
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ *is_temp = 1;
+ return ht;
+ case ZEND_FFI_TYPE_ARRAY:
+ ht = zend_new_array(type->array.length);
+ for (n = 0; n < type->array.length; n++) {
+ zend_ffi_cdata_to_zval(NULL, ptr, ZEND_FFI_TYPE(type->array.type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0);
+ zend_hash_index_add(ht, n, &tmp);
+ ptr = (void*)(((char*)ptr) + ZEND_FFI_TYPE(type->array.type)->size);
+ }
+ *is_temp = 1;
+ return ht;
+ case ZEND_FFI_TYPE_FUNC:
+ ht = zend_new_array(0);
+ // TODO: function name ???
+ *is_temp = 1;
+ return ht;
+ break;
+ default:
+ ZEND_ASSERT(0);
+ break;
+ }
+ return NULL;
+}
+/* }}} */
+
+static int zend_ffi_cdata_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(obj);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+ zend_function *func;
+
+ if (type->kind != ZEND_FFI_TYPE_POINTER) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to call non C function pointer");
+ return FAILURE;
+ }
+ type = ZEND_FFI_TYPE(type->pointer.type);
+ if (type->kind != ZEND_FFI_TYPE_FUNC) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to call non C function pointer");
+ return FAILURE;
+ }
+ if (!cdata->ptr) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return FAILURE;
+ }
+
+ if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
+ func = &EG(trampoline);
+ } else {
+ func = ecalloc(sizeof(zend_internal_function), 1);
+ }
+ func->type = ZEND_INTERNAL_FUNCTION;
+ func->common.arg_flags[0] = 0;
+ func->common.arg_flags[1] = 0;
+ func->common.arg_flags[2] = 0;
+ func->common.fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE;
+ func->common.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE);
+ func->common.num_args = func->common.required_num_args = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
+ func->internal_function.handler = ZEND_FN(ffi_trampoline);
+
+ if (func->common.num_args > MAX_ARG_FLAG_NUM) {
+ func->common.arg_info = emalloc(sizeof(zend_arg_info) * func->common.num_args);
+ memset(func->common.arg_info, 0, sizeof(zend_arg_info) * func->common.num_args);
+ }
+
+ func->internal_function.reserved[0] = type;
+ func->internal_function.reserved[1] = *(void**)cdata->ptr;
+
+ *ce_ptr = NULL;
+ *fptr_ptr= func;
+ *obj_ptr = NULL;
+
+ return SUCCESS;
+}
+/* }}} */
+
+static zend_object *zend_ffi_ctype_new(zend_class_entry *class_type) /* {{{ */
+{
+ zend_ffi_ctype *ctype;
+
+ ctype = emalloc(sizeof(zend_ffi_ctype));
+
+ zend_ffi_object_init(&ctype->std, class_type);
+ ctype->std.handlers = &zend_ffi_ctype_handlers;
+
+ ctype->type = NULL;
+
+ return &ctype->std;
+}
+/* }}} */
+
+static void zend_ffi_ctype_free_obj(zend_object *object) /* {{{ */
+{
+ zend_ffi_ctype *ctype = (zend_ffi_ctype*)object;
+
+ zend_ffi_type_dtor(ctype->type);
+}
+/* }}} */
+
+static int zend_ffi_is_same_type(zend_ffi_type *type1, zend_ffi_type *type2) /* {{{ */
+{
+ while (1) {
+ if (type1 == type2) {
+ return 1;
+ } else if (type1->kind == type2->kind) {
+ if (type1->kind < ZEND_FFI_TYPE_POINTER) {
+ return 1;
+ } else if (type1->kind == ZEND_FFI_TYPE_POINTER) {
+ type1 = ZEND_FFI_TYPE(type1->pointer.type);
+ type2 = ZEND_FFI_TYPE(type2->pointer.type);
+ if (type1->kind == ZEND_FFI_TYPE_VOID ||
+ type2->kind == ZEND_FFI_TYPE_VOID) {
+ return 1;
+ }
+ } else if (type1->kind == ZEND_FFI_TYPE_ARRAY &&
+ type1->array.length == type2->array.length) {
+ type1 = ZEND_FFI_TYPE(type1->array.type);
+ type2 = ZEND_FFI_TYPE(type2->array.type);
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+static zend_string *zend_ffi_ctype_get_class_name(const zend_object *zobj) /* {{{ */
+{
+ zend_ffi_ctype *ctype = (zend_ffi_ctype*)zobj;
+
+ return zend_ffi_get_class_name(zobj->ce->name, ZEND_FFI_TYPE(ctype->type));
+}
+/* }}} */
+
+static int zend_ffi_ctype_compare_objects(zval *o1, zval *o2) /* {{{ */
+{
+ if (Z_TYPE_P(o1) == IS_OBJECT && Z_OBJCE_P(o1) == zend_ffi_ctype_ce &&
+ Z_TYPE_P(o2) == IS_OBJECT && Z_OBJCE_P(o2) == zend_ffi_ctype_ce) {
+ zend_ffi_ctype *ctype1 = (zend_ffi_ctype*)Z_OBJ_P(o1);
+ zend_ffi_ctype *ctype2 = (zend_ffi_ctype*)Z_OBJ_P(o2);
+ zend_ffi_type *type1 = ZEND_FFI_TYPE(ctype1->type);
+ zend_ffi_type *type2 = ZEND_FFI_TYPE(ctype2->type);
+
+ if (zend_ffi_is_same_type(type1, type2)) {
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+ zend_throw_error(zend_ffi_exception_ce, "Comparison of incompatible C types");
+ return 0;
+}
+/* }}} */
+
+static HashTable *zend_ffi_ctype_get_debug_info(zval *object, int *is_temp) /* {{{ */
+{
+ return NULL;
+}
+/* }}} */
+
+static zend_object *zend_ffi_new(zend_class_entry *class_type) /* {{{ */
+{
+ zend_ffi *ffi;
+
+ ffi = emalloc(sizeof(zend_ffi));
+
+ zend_ffi_object_init(&ffi->std, class_type);
+ ffi->std.handlers = &zend_ffi_handlers;
+
+ ffi->lib = NULL;
+ ffi->symbols = NULL;
+ ffi->tags = NULL;
+ ffi->persistent = 0;
+
+ return &ffi->std;
+}
+/* }}} */
+
+static void _zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */
+{
+ type = ZEND_FFI_TYPE(type);
+
+ switch (type->kind) {
+ case ZEND_FFI_TYPE_STRUCT:
+ zend_hash_destroy(&type->record.fields);
+ break;
+ case ZEND_FFI_TYPE_POINTER:
+ zend_ffi_type_dtor(type->pointer.type);
+ break;
+ case ZEND_FFI_TYPE_ARRAY:
+ zend_ffi_type_dtor(type->array.type);
+ break;
+ case ZEND_FFI_TYPE_FUNC:
+ if (type->func.args) {
+ zend_hash_destroy(type->func.args);
+ pefree(type->func.args, type->attr & ZEND_FFI_ATTR_PERSISTENT);
+ }
+ zend_ffi_type_dtor(type->func.ret_type);
+ break;
+ default:
+ break;
+ }
+ pefree(type, type->attr & ZEND_FFI_ATTR_PERSISTENT);
+}
+/* }}} */
+
+static void zend_ffi_type_hash_dtor(zval *zv) /* {{{ */
+{
+ zend_ffi_type *type = Z_PTR_P(zv);
+ zend_ffi_type_dtor(type);
+}
+/* }}} */
+
+static void zend_ffi_field_hash_dtor(zval *zv) /* {{{ */
+{
+ zend_ffi_field *field = Z_PTR_P(zv);
+ zend_ffi_type_dtor(field->type);
+ efree(field);
+}
+/* }}} */
+
+static void zend_ffi_field_hash_persistent_dtor(zval *zv) /* {{{ */
+{
+ zend_ffi_field *field = Z_PTR_P(zv);
+ zend_ffi_type_dtor(field->type);
+ free(field);
+}
+/* }}} */
+
+static void zend_ffi_symbol_hash_dtor(zval *zv) /* {{{ */
+{
+ zend_ffi_symbol *sym = Z_PTR_P(zv);
+ zend_ffi_type_dtor(sym->type);
+ efree(sym);
+}
+/* }}} */
+
+static void zend_ffi_symbol_hash_persistent_dtor(zval *zv) /* {{{ */
+{
+ zend_ffi_symbol *sym = Z_PTR_P(zv);
+ zend_ffi_type_dtor(sym->type);
+ free(sym);
+}
+/* }}} */
+
+static void zend_ffi_tag_hash_dtor(zval *zv) /* {{{ */
+{
+ zend_ffi_tag *tag = Z_PTR_P(zv);
+ zend_ffi_type_dtor(tag->type);
+ efree(tag);
+}
+/* }}} */
+
+static void zend_ffi_tag_hash_persistent_dtor(zval *zv) /* {{{ */
+{
+ zend_ffi_tag *tag = Z_PTR_P(zv);
+ zend_ffi_type_dtor(tag->type);
+ free(tag);
+}
+/* }}} */
+
+static void zend_ffi_cdata_dtor(zend_ffi_cdata *cdata) /* {{{ */
+{
+ zend_ffi_type_dtor(cdata->type);
+ if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
+ if (cdata->ptr != (void*)&cdata->ptr_holder) {
+ pefree(cdata->ptr, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
+ } else {
+ pefree(cdata->ptr_holder, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
+ }
+ }
+}
+/* }}} */
+
+static void zend_ffi_scope_hash_dtor(zval *zv) /* {{{ */
+{
+ zend_ffi_scope *scope = Z_PTR_P(zv);
+ if (scope->symbols) {
+ zend_hash_destroy(scope->symbols);
+ free(scope->symbols);
+ }
+ if (scope->tags) {
+ zend_hash_destroy(scope->tags);
+ free(scope->tags);
+ }
+ free(scope);
+}
+/* }}} */
+
+static void zend_ffi_free_obj(zend_object *object) /* {{{ */
+{
+ zend_ffi *ffi = (zend_ffi*)object;
+
+ if (ffi->persistent) {
+ return;
+ }
+
+ if (ffi->lib) {
+ DL_UNLOAD(ffi->lib);
+ ffi->lib = NULL;
+ }
+
+ if (ffi->symbols) {
+ zend_hash_destroy(ffi->symbols);
+ efree(ffi->symbols);
+ }
+
+ if (ffi->tags) {
+ zend_hash_destroy(ffi->tags);
+ efree(ffi->tags);
+ }
+}
+/* }}} */
+
+static void zend_ffi_cdata_free_obj(zend_object *object) /* {{{ */
+{
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)object;
+
+ zend_ffi_cdata_dtor(cdata);
+}
+/* }}} */
+
+static zend_object *zend_ffi_cdata_clone_obj(zval *zobject) /* {{{ */
+{
+ zend_ffi_cdata *old_cdata = (zend_ffi_cdata*)Z_OBJ_P(zobject);
+ zend_ffi_type *type = ZEND_FFI_TYPE(old_cdata->type);
+ zend_ffi_cdata *new_cdata;
+
+ new_cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
+ if (type->kind < ZEND_FFI_TYPE_POINTER) {
+ new_cdata->std.handlers = &zend_ffi_cdata_value_handlers;
+ }
+ new_cdata->type = type;
+ new_cdata->ptr = emalloc(type->size);
+ memcpy(new_cdata->ptr, old_cdata->ptr, type->size);
+ new_cdata->flags |= ZEND_FFI_FLAG_OWNED;
+
+ return &new_cdata->std;
+}
+/* }}} */
+
+static zval *zend_ffi_read_var(zval *object, zval *member, int read_type, void **cache_slot, zval *rv) /* {{{ */
+{
+ zend_ffi *ffi = (zend_ffi*)Z_OBJ_P(object);
+ zend_string *tmp_var_name;
+ zend_string *var_name = zval_get_tmp_string(member, &tmp_var_name);
+ zend_ffi_symbol *sym = NULL;
+
+ if (ffi->symbols) {
+ sym = zend_hash_find_ptr(ffi->symbols, var_name);
+ if (sym && sym->kind != ZEND_FFI_SYM_VAR && sym->kind != ZEND_FFI_SYM_CONST) {
+ sym = NULL;
+ }
+ }
+ if (!sym) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to read undefined C variable '%s'", ZSTR_VAL(var_name));
+ zend_tmp_string_release(tmp_var_name);
+ return &EG(uninitialized_zval);
+ }
+
+ zend_tmp_string_release(tmp_var_name);
+
+ if (sym->kind == ZEND_FFI_SYM_VAR) {
+ zend_ffi_cdata_to_zval(NULL, sym->addr, ZEND_FFI_TYPE(sym->type), read_type, rv, (zend_ffi_flags)sym->is_const, 0);
+ } else {
+ ZVAL_LONG(rv, sym->value);
+ }
+
+ return rv;
+}
+/* }}} */
+
+static zval *zend_ffi_write_var(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
+{
+ zend_ffi *ffi = (zend_ffi*)Z_OBJ_P(object);
+ zend_string *tmp_var_name;
+ zend_string *var_name = zval_get_tmp_string(member, &tmp_var_name);
+ zend_ffi_symbol *sym = NULL;
+
+ if (ffi->symbols) {
+ sym = zend_hash_find_ptr(ffi->symbols, var_name);
+ if (sym && sym->kind != ZEND_FFI_SYM_VAR) {
+ sym = NULL;
+ }
+ }
+ if (!sym) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to assign undefined C variable '%s'", ZSTR_VAL(var_name));
+ zend_tmp_string_release(tmp_var_name);
+ return value;
+ }
+
+ zend_tmp_string_release(tmp_var_name);
+
+ if (sym->is_const) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only C variable '%s'", ZSTR_VAL(var_name));
+ return value;
+ }
+
+ zend_ffi_zval_to_cdata(sym->addr, ZEND_FFI_TYPE(sym->type), value);
+ return value;
+}
+/* }}} */
+
+static int zend_ffi_pass_arg(zval *arg, zend_ffi_type *type, ffi_type **pass_type, void **arg_values, uint32_t n) /* {{{ */
+{
+ zend_long lval;
+ double dval;
+ zend_string *str, *tmp_str;
+ zend_ffi_type_kind kind = type->kind;
+
+ ZVAL_DEREF(arg);
+
+again:
+ switch (kind) {
+ case ZEND_FFI_TYPE_FLOAT:
+ dval = zval_get_double(arg);
+ *pass_type = &ffi_type_float;
+ *(float*)arg_values[n] = (float)dval;
+ break;
+ case ZEND_FFI_TYPE_DOUBLE:
+ dval = zval_get_double(arg);
+ *pass_type = &ffi_type_double;
+ *(double*)arg_values[n] = dval;
+ break;
+#ifdef HAVE_LONG_DOUBLE
+ case ZEND_FFI_TYPE_LONGDOUBLE:
+ dval = zval_get_double(arg);
+ *pass_type = &ffi_type_double;
+ *(long double*)arg_values[n] = (long double)dval;
+ break;
+#endif
+ case ZEND_FFI_TYPE_UINT8:
+ lval = zval_get_long(arg);
+ *pass_type = &ffi_type_uint8;
+ *(uint8_t*)arg_values[n] = (uint8_t)lval;
+ break;
+ case ZEND_FFI_TYPE_SINT8:
+ lval = zval_get_long(arg);
+ *pass_type = &ffi_type_sint8;
+ *(int8_t*)arg_values[n] = (int8_t)lval;
+ break;
+ case ZEND_FFI_TYPE_UINT16:
+ lval = zval_get_long(arg);
+ *pass_type = &ffi_type_uint16;
+ *(uint16_t*)arg_values[n] = (uint16_t)lval;
+ break;
+ case ZEND_FFI_TYPE_SINT16:
+ lval = zval_get_long(arg);
+ *pass_type = &ffi_type_sint16;
+ *(int16_t*)arg_values[n] = (int16_t)lval;
+ break;
+ case ZEND_FFI_TYPE_UINT32:
+ lval = zval_get_long(arg);
+ *pass_type = &ffi_type_uint32;
+ *(uint32_t*)arg_values[n] = (uint32_t)lval;
+ break;
+ case ZEND_FFI_TYPE_SINT32:
+ lval = zval_get_long(arg);
+ *pass_type = &ffi_type_sint32;
+ *(int32_t*)arg_values[n] = (int32_t)lval;
+ break;
+ case ZEND_FFI_TYPE_UINT64:
+ lval = zval_get_long(arg);
+ *pass_type = &ffi_type_uint64;
+ *(uint64_t*)arg_values[n] = (uint64_t)lval;
+ break;
+ case ZEND_FFI_TYPE_SINT64:
+ lval = zval_get_long(arg);
+ *pass_type = &ffi_type_sint64;
+ *(int64_t*)arg_values[n] = (int64_t)lval;
+ break;
+ case ZEND_FFI_TYPE_POINTER:
+ *pass_type = &ffi_type_pointer;
+ if (Z_TYPE_P(arg) == IS_NULL) {
+ *(void**)arg_values[n] = NULL;
+ return SUCCESS;
+ } else if (Z_TYPE_P(arg) == IS_STRING
+ && ((ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR)
+ || (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_VOID))) {
+ *(void**)arg_values[n] = Z_STRVAL_P(arg);
+ return SUCCESS;
+ } else if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
+
+ if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) {
+ if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
+ if (!cdata->ptr) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return FAILURE;
+ }
+ *(void**)arg_values[n] = *(void**)cdata->ptr;
+ } else {
+ *(void**)arg_values[n] = cdata->ptr;
+ }
+ return SUCCESS;
+ }
+#if FFI_CLOSURES
+ } else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_FUNC) {
+ void *callback = zend_ffi_create_callback(ZEND_FFI_TYPE(type->pointer.type), arg);
+
+ if (callback) {
+ *(void**)arg_values[n] = callback;
+ break;
+ } else {
+ return FAILURE;
+ }
+#endif
+ }
+ zend_throw_error(zend_ffi_exception_ce, "Passing incompatible pointer");
+ return FAILURE;
+ case ZEND_FFI_TYPE_BOOL:
+ *pass_type = &ffi_type_uint8;
+ *(uint8_t*)arg_values[n] = zend_is_true(arg);
+ break;
+ case ZEND_FFI_TYPE_CHAR:
+ str = zval_get_tmp_string(arg, &tmp_str);
+ *pass_type = &ffi_type_sint8;
+ *(char*)arg_values[n] = ZSTR_VAL(str)[0];
+ if (ZSTR_LEN(str) != 1) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to pass incompatible C type");
+ }
+ zend_tmp_string_release(tmp_str);
+ break;
+ case ZEND_FFI_TYPE_ENUM:
+ kind = type->enumeration.kind;
+ goto again;
+ case ZEND_FFI_TYPE_STRUCT:
+ if (!(type->attr & ZEND_FFI_ATTR_UNION)
+ && Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
+
+ if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) {
+ /* Create a fake structure type */
+ ffi_type *t = zend_ffi_make_fake_struct_type(type);
+ if (!t) {
+ return FAILURE;
+ }
+ *pass_type = t;
+ arg_values[n] = cdata->ptr;
+ break;
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Passing incompatible struct/union");
+ return FAILURE;
+ }
+ }
+ zend_throw_error(zend_ffi_exception_ce, "FFI passing struct/union is not implemented");
+ return FAILURE;
+ case ZEND_FFI_TYPE_ARRAY:
+ zend_throw_error(zend_ffi_exception_ce, "FFI passing array is not implemented");
+ return FAILURE;
+ default:
+ zend_throw_error(zend_ffi_exception_ce, "FFI internal error");
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+static int zend_ffi_pass_var_arg(zval *arg, ffi_type **pass_type, void **arg_values, uint32_t n) /* {{{ */
+{
+ ZVAL_DEREF(arg);
+ switch (Z_TYPE_P(arg)) {
+ case IS_NULL:
+ *pass_type = &ffi_type_pointer;
+ *(void**)arg_values[n] = NULL;
+ break;
+ case IS_FALSE:
+ *pass_type = &ffi_type_uint8;
+ *(uint8_t*)arg_values[n] = 0;
+ break;
+ case IS_TRUE:
+ *pass_type = &ffi_type_uint8;
+ *(uint8_t*)arg_values[n] = 1;
+ break;
+ case IS_LONG:
+ if (sizeof(zend_long) == 4) {
+ *pass_type = &ffi_type_sint32;
+ *(int32_t*)arg_values[n] = Z_LVAL_P(arg);
+ } else {
+ *pass_type = &ffi_type_sint64;
+ *(int64_t*)arg_values[n] = Z_LVAL_P(arg);
+ }
+ break;
+ case IS_DOUBLE:
+ *pass_type = &ffi_type_double;
+ *(double*)arg_values[n] = Z_DVAL_P(arg);
+ break;
+ case IS_STRING:
+ *pass_type = &ffi_type_pointer;
+ *(char**)arg_values[n] = Z_STRVAL_P(arg);
+ break;
+ case IS_OBJECT:
+ if (Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
+ zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
+
+ return zend_ffi_pass_arg(arg, type, pass_type, arg_values, n);
+ }
+ /* break missing intentionally */
+ default:
+ zend_throw_error(zend_ffi_exception_ce, "FFI internal error");
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
+{
+ zend_ffi_type *type = EX(func)->internal_function.reserved[0];
+ void *addr = EX(func)->internal_function.reserved[1];
+ ffi_cif cif;
+ ffi_type *ret_type = NULL;
+ ffi_type **arg_types = NULL;
+ void **arg_values = NULL;
+ uint32_t n, arg_count;
+ ffi_arg ret;
+ zend_ffi_type *arg_type;
+ ALLOCA_FLAG(arg_types_use_heap = 0)
+ ALLOCA_FLAG(arg_values_use_heap = 0)
+
+ ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC);
+ arg_count = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
+ if (type->attr & ZEND_FFI_ATTR_VARIADIC) {
+ if (arg_count > EX_NUM_ARGS()) {
+ zend_throw_error(zend_ffi_exception_ce, "Incorrect number of arguments for C function '%s'", ZSTR_VAL(EX(func)->internal_function.function_name));
+ return;
+ }
+ if (EX_NUM_ARGS()) {
+ arg_types = do_alloca(
+ sizeof(ffi_type*) * EX_NUM_ARGS(), arg_types_use_heap);
+ arg_values = do_alloca(
+ (sizeof(void*) + FFI_SIZEOF_ARG) * EX_NUM_ARGS(), arg_values_use_heap);
+ n = 0;
+ if (type->func.args) {
+ ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) {
+ arg_type = ZEND_FFI_TYPE(arg_type);
+ arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (FFI_SIZEOF_ARG * n);
+ if (zend_ffi_pass_arg(EX_VAR_NUM(n), arg_type, &arg_types[n], arg_values, n) != SUCCESS) {
+ free_alloca(arg_types, arg_types_use_heap);
+ free_alloca(arg_values, arg_values_use_heap);
+ return;
+ }
+ n++;
+ } ZEND_HASH_FOREACH_END();
+ }
+ for (; n < EX_NUM_ARGS(); n++) {
+ arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (FFI_SIZEOF_ARG * n);
+ if (zend_ffi_pass_var_arg(EX_VAR_NUM(n), &arg_types[n], arg_values, n) != SUCCESS) {
+ free_alloca(arg_types, arg_types_use_heap);
+ free_alloca(arg_values, arg_values_use_heap);
+ return;
+ }
+ }
+ }
+ ret_type = zend_ffi_get_type(ZEND_FFI_TYPE(type->func.ret_type));
+ if (!ret_type) {
+ free_alloca(arg_types, arg_types_use_heap);
+ free_alloca(arg_values, arg_values_use_heap);
+ return;
+ }
+ if (ffi_prep_cif_var(&cif, type->func.abi, arg_count, EX_NUM_ARGS(), ret_type, arg_types) != FFI_OK) {
+ zend_throw_error(zend_ffi_exception_ce, "FFI internal error");
+ free_alloca(arg_types, arg_types_use_heap);
+ free_alloca(arg_values, arg_values_use_heap);
+ return;
+ }
+ } else {
+ if (arg_count != EX_NUM_ARGS()) {
+ zend_throw_error(zend_ffi_exception_ce, "Incorrect number of arguments for C function '%s'", ZSTR_VAL(EX(func)->internal_function.function_name));
+ return;
+ }
+ if (EX_NUM_ARGS()) {
+ arg_types = do_alloca(
+ (sizeof(ffi_type*) + sizeof(ffi_type)) * EX_NUM_ARGS(), arg_types_use_heap);
+ arg_values = do_alloca(
+ (sizeof(void*) + FFI_SIZEOF_ARG) * EX_NUM_ARGS(), arg_values_use_heap);
+ n = 0;
+ if (type->func.args) {
+ ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) {
+ arg_type = ZEND_FFI_TYPE(arg_type);
+ arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (FFI_SIZEOF_ARG * n);
+ if (zend_ffi_pass_arg(EX_VAR_NUM(n), arg_type, &arg_types[n], arg_values, n) != SUCCESS) {
+ free_alloca(arg_types, arg_types_use_heap);
+ free_alloca(arg_values, arg_values_use_heap);
+ return;
+ }
+ n++;
+ } ZEND_HASH_FOREACH_END();
+ }
+ }
+ ret_type = zend_ffi_get_type(ZEND_FFI_TYPE(type->func.ret_type));
+ if (!ret_type) {
+ free_alloca(arg_types, arg_types_use_heap);
+ free_alloca(arg_values, arg_values_use_heap);
+ return;
+ }
+ if (ffi_prep_cif(&cif, type->func.abi, arg_count, ret_type, arg_types) != FFI_OK) {
+ zend_throw_error(zend_ffi_exception_ce, "FFI internal error");
+ free_alloca(arg_types, arg_types_use_heap);
+ free_alloca(arg_values, arg_values_use_heap);
+ return;
+ }
+ }
+
+ ffi_call(&cif, addr, &ret, arg_values);
+
+ for (n = 0; n < arg_count; n++) {
+ if (arg_types[n]->type == FFI_TYPE_STRUCT) {
+ efree(arg_types[n]);
+ }
+ }
+ if (ret_type->type == FFI_TYPE_STRUCT) {
+ efree(ret_type);
+ }
+
+ if (EX_NUM_ARGS()) {
+ free_alloca(arg_types, arg_types_use_heap);
+ free_alloca(arg_values, arg_values_use_heap);
+ }
+
+ zend_ffi_cdata_to_zval(NULL, (void*)&ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1);
+
+ zend_string_release(EX(func)->common.function_name);
+ if (EX(func)->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
+ if (EX(func)->common.arg_info) {
+ efree(EX(func)->common.arg_info);
+ }
+ zend_free_trampoline(EX(func));
+ EX(func) = NULL;
+ }
+}
+/* }}} */
+
+static zend_function *zend_ffi_get_func(zend_object **obj, zend_string *name, const zval *key) /* {{{ */
+{
+ zend_ffi *ffi = (zend_ffi*)*obj;
+ zend_ffi_symbol *sym = NULL;
+ zend_function *func;
+ zend_ffi_type *type;
+
+ if (ZSTR_LEN(name) == sizeof("new") -1
+ && (ZSTR_VAL(name)[0] == 'n' || ZSTR_VAL(name)[0] == 'N')
+ && (ZSTR_VAL(name)[1] == 'e' || ZSTR_VAL(name)[1] == 'E')
+ && (ZSTR_VAL(name)[2] == 'w' || ZSTR_VAL(name)[2] == 'W')) {
+ return (zend_function*)&zend_ffi_new_fn;
+ } else if (ZSTR_LEN(name) == sizeof("cast") -1
+ && (ZSTR_VAL(name)[0] == 'c' || ZSTR_VAL(name)[0] == 'C')
+ && (ZSTR_VAL(name)[1] == 'a' || ZSTR_VAL(name)[1] == 'A')
+ && (ZSTR_VAL(name)[2] == 's' || ZSTR_VAL(name)[2] == 'S')
+ && (ZSTR_VAL(name)[3] == 't' || ZSTR_VAL(name)[3] == 'T')) {
+ return (zend_function*)&zend_ffi_cast_fn;
+ } else if (ZSTR_LEN(name) == sizeof("type") -1
+ && (ZSTR_VAL(name)[0] == 't' || ZSTR_VAL(name)[0] == 'T')
+ && (ZSTR_VAL(name)[1] == 'y' || ZSTR_VAL(name)[1] == 'Y')
+ && (ZSTR_VAL(name)[2] == 'p' || ZSTR_VAL(name)[2] == 'P')
+ && (ZSTR_VAL(name)[3] == 'e' || ZSTR_VAL(name)[3] == 'E')) {
+ return (zend_function*)&zend_ffi_type_fn;
+ }
+
+ if (ffi->symbols) {
+ sym = zend_hash_find_ptr(ffi->symbols, name);
+ if (sym && sym->kind != ZEND_FFI_SYM_FUNC) {
+ sym = NULL;
+ }
+ }
+ if (!sym) {
+ zend_throw_error(zend_ffi_exception_ce, "Attempt to call undefined C function '%s'", ZSTR_VAL(name));
+ return NULL;
+ }
+
+ type = ZEND_FFI_TYPE(sym->type);
+ ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC);
+
+ if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
+ func = &EG(trampoline);
+ } else {
+ func = ecalloc(sizeof(zend_internal_function), 1);
+ }
+ func->common.type = ZEND_INTERNAL_FUNCTION;
+ func->common.arg_flags[0] = 0;
+ func->common.arg_flags[1] = 0;
+ func->common.arg_flags[2] = 0;
+ func->common.fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE;
+ func->common.function_name = zend_string_copy(name);
+ func->common.num_args = func->common.required_num_args = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
+ func->internal_function.handler = ZEND_FN(ffi_trampoline);
+
+ if (func->common.num_args > MAX_ARG_FLAG_NUM) {
+ func->common.arg_info = emalloc(sizeof(zend_arg_info) * func->common.num_args);
+ memset(func->common.arg_info, 0, sizeof(zend_arg_info) * func->common.num_args);
+ }
+
+ func->internal_function.reserved[0] = type;
+ func->internal_function.reserved[1] = sym->addr;
+
+ return func;
+}
+/* }}} */
+
+static zend_never_inline int zend_ffi_disabled(void) /* {{{ */
+{
+ zend_throw_error(zend_ffi_exception_ce, "FFI API is restricted by \"ffi.enable\" configuration directive");
+ return 0;
+}
+/* }}} */
+
+static zend_always_inline int zend_ffi_validate_api_restriction(zend_execute_data *execute_data) /* {{{ */
+{
+ if (EXPECTED(FFI_G(restriction) > ZEND_FFI_ENABLED)) {
+ ZEND_ASSERT(FFI_G(restriction) == ZEND_FFI_PRELOAD);
+ if (FFI_G(is_cli)
+ || (execute_data->prev_execute_data
+ && (execute_data->prev_execute_data->func->common.fn_flags & ZEND_ACC_PRELOADED))
+ || (CG(compiler_options) & ZEND_COMPILE_PRELOAD)) {
+ return 1;
+ }
+ } else if (EXPECTED(FFI_G(restriction) == ZEND_FFI_ENABLED)) {
+ return 1;
+ }
+ return zend_ffi_disabled();
+}
+/* }}} */
+
+#define ZEND_FFI_VALIDATE_API_RESTRICTION() do { \
+ if (UNEXPECTED(!zend_ffi_validate_api_restriction(execute_data))) { \
+ return; \
+ } \
+ } while (0)
+
+ZEND_METHOD(FFI, cdef) /* {{{ */
+{
+ zend_string *code = NULL;
+ zend_string *lib = NULL;
+ zend_ffi *ffi = NULL;
+ DL_HANDLE handle = NULL;
+ void *addr;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(0, 2)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_STR(code)
+ Z_PARAM_STR(lib)
+ ZEND_PARSE_PARAMETERS_END();
+
+ if (lib) {
+ handle = DL_LOAD(ZSTR_VAL(lib));
+ if (!handle) {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s'", ZSTR_VAL(lib));
+ return;
+ }
+#ifdef RTLD_DEFAULT
+ } else if (1) {
+ // TODO: this might need to be disabled or protected ???
+ handle = RTLD_DEFAULT;
+#endif
+ }
+
+ FFI_G(symbols) = NULL;
+ FFI_G(tags) = NULL;
+
+ if (code) {
+ /* Parse C definitions */
+ FFI_G(default_type_attr) = ZEND_FFI_ATTR_STORED;
+
+ if (zend_ffi_parse_decl(ZSTR_VAL(code), ZSTR_LEN(code)) != SUCCESS) {
+ if (FFI_G(symbols)) {
+ zend_hash_destroy(FFI_G(symbols));
+ efree(FFI_G(symbols));
+ FFI_G(symbols) = NULL;
+ }
+ if (FFI_G(tags)) {
+ zend_hash_destroy(FFI_G(tags));
+ efree(FFI_G(tags));
+ FFI_G(tags) = NULL;
+ }
+ return;
+ }
+
+ if (FFI_G(symbols)) {
+ zend_string *name;
+ zend_ffi_symbol *sym;
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(symbols), name, sym) {
+ if (sym->kind == ZEND_FFI_SYM_VAR) {
+ addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name));
+ if (!addr) {
+ zend_throw_error(zend_ffi_exception_ce, "Failed resolving C variable '%s'", ZSTR_VAL(name));
+ }
+ sym->addr = addr;
+ } else if (sym->kind == ZEND_FFI_SYM_FUNC) {
+ addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name));
+ if (!addr) {
+ zend_throw_error(zend_ffi_exception_ce, "Failed resolving C function '%s'", ZSTR_VAL(name));
+ }
+ sym->addr = addr;
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+ }
+
+ ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
+ ffi->lib = handle;
+ ffi->symbols = FFI_G(symbols);
+ ffi->tags = FFI_G(tags);
+
+ FFI_G(symbols) = NULL;
+ FFI_G(tags) = NULL;
+
+ RETURN_OBJ(&ffi->std);
+}
+/* }}} */
+
+static int zend_ffi_same_types(zend_ffi_type *old, zend_ffi_type *type) /* {{{ */
+{
+ if (old == type) {
+ return 1;
+ }
+
+ if (old->kind != type->kind
+ || old->size != type->size
+ || old->align != type->align
+ || old->attr != type->attr) {
+ return 0;
+ }
+
+ switch (old->kind) {
+ case ZEND_FFI_TYPE_ENUM:
+ return old->enumeration.kind == type->enumeration.kind;
+ case ZEND_FFI_TYPE_ARRAY:
+ return old->array.length == type->array.length
+ && zend_ffi_same_types(ZEND_FFI_TYPE(old->array.type), ZEND_FFI_TYPE(type->array.type));
+ case ZEND_FFI_TYPE_POINTER:
+ return zend_ffi_same_types(ZEND_FFI_TYPE(old->pointer.type), ZEND_FFI_TYPE(type->pointer.type));
+ case ZEND_FFI_TYPE_STRUCT:
+ if (zend_hash_num_elements(&old->record.fields) != zend_hash_num_elements(&type->record.fields)) {
+ return 0;
+ } else {
+ zend_ffi_field *old_field, *field;
+ zend_string *key;
+ Bucket *b = type->record.fields.arData;
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&old->record.fields, key, old_field) {
+ while (Z_TYPE(b->val) == IS_UNDEF) {
+ b++;
+ }
+ if (key) {
+ if (!b->key
+ || !zend_string_equals(key, b->key)) {
+ return 0;
+ }
+ } else if (b->key) {
+ return 0;
+ }
+ field = Z_PTR(b->val);
+ if (old_field->offset != field->offset
+ || old_field->is_const != field->is_const
+ || old_field->is_nested != field->is_nested
+ || old_field->first_bit != field->first_bit
+ || old_field->bits != field->bits
+ || !zend_ffi_same_types(ZEND_FFI_TYPE(old_field->type), ZEND_FFI_TYPE(field->type))) {
+ return 0;
+ }
+ b++;
+ } ZEND_HASH_FOREACH_END();
+ }
+ break;
+ case ZEND_FFI_TYPE_FUNC:
+ if (old->func.abi != type->func.abi
+ || ((old->func.args ? zend_hash_num_elements(old->func.args) : 0) != (type->func.args ? zend_hash_num_elements(type->func.args) : 0))
+ || !zend_ffi_same_types(ZEND_FFI_TYPE(old->func.ret_type), ZEND_FFI_TYPE(type->func.ret_type))) {
+ return 0;
+ } else if (old->func.args) {
+ zend_ffi_type *arg_type;
+ Bucket *b = type->func.args->arData;
+
+ ZEND_HASH_FOREACH_PTR(old->func.args, arg_type) {
+ while (Z_TYPE(b->val) == IS_UNDEF) {
+ b++;
+ }
+ if (!zend_ffi_same_types(ZEND_FFI_TYPE(arg_type), ZEND_FFI_TYPE(Z_PTR(b->val)))) {
+ return 0;
+ }
+ b++;
+ } ZEND_HASH_FOREACH_END();
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 1;
+}
+/* }}} */
+
+static int zend_ffi_same_symbols(zend_ffi_symbol *old, zend_ffi_symbol *sym) /* {{{ */
+{
+ if (old->kind != sym->kind || old->is_const != sym->is_const) {
+ return 0;
+ }
+
+ if (old->kind == ZEND_FFI_SYM_CONST) {
+ if (old->value != sym->value) {
+ return 0;
+ }
+ }
+
+ return zend_ffi_same_types(ZEND_FFI_TYPE(old->type), ZEND_FFI_TYPE(sym->type));
+}
+/* }}} */
+
+static int zend_ffi_same_tags(zend_ffi_tag *old, zend_ffi_tag *tag) /* {{{ */
+{
+ if (old->kind != tag->kind) {
+ return 0;
+ }
+
+ return zend_ffi_same_types(ZEND_FFI_TYPE(old->type), ZEND_FFI_TYPE(tag->type));
+}
+/* }}} */
+
+static int zend_ffi_subst_old_type(zend_ffi_type **dcl, zend_ffi_type *old, zend_ffi_type *type) /* {{{ */
+{
+ zend_ffi_type *dcl_type;
+ zend_ffi_field *field;
+
+ if (ZEND_FFI_TYPE(*dcl) == type) {
+ *dcl = old;
+ return 1;
+ }
+ dcl_type = *dcl;
+ switch (dcl_type->kind) {
+ case ZEND_FFI_TYPE_POINTER:
+ return zend_ffi_subst_old_type(&dcl_type->pointer.type, old, type);
+ case ZEND_FFI_TYPE_ARRAY:
+ return zend_ffi_subst_old_type(&dcl_type->array.type, old, type);
+ case ZEND_FFI_TYPE_FUNC:
+ if (zend_ffi_subst_old_type(&dcl_type->func.ret_type, old, type)) {
+ return 1;
+ }
+ if (dcl_type->func.args) {
+ zval *zv;
+
+ ZEND_HASH_FOREACH_VAL(dcl_type->func.args, zv) {
+ if (zend_ffi_subst_old_type((zend_ffi_type**)&Z_PTR_P(zv), old, type)) {
+ return 1;
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+ break;
+ case ZEND_FFI_TYPE_STRUCT:
+ ZEND_HASH_FOREACH_PTR(&dcl_type->record.fields, field) {
+ if (zend_ffi_subst_old_type(&field->type, old, type)) {
+ return 1;
+ }
+ } ZEND_HASH_FOREACH_END();
+ break;
+ default:
+ break;
+ }
+ return 0;
+} /* }}} */
+
+static void zend_ffi_cleanup_type(zend_ffi_type *old, zend_ffi_type *type) /* {{{ */
+{
+ zend_ffi_symbol *sym;
+ zend_ffi_tag *tag;
+
+ if (FFI_G(symbols)) {
+ ZEND_HASH_FOREACH_PTR(FFI_G(symbols), sym) {
+ zend_ffi_subst_old_type(&sym->type, old, type);
+ } ZEND_HASH_FOREACH_END();
+ }
+ if (FFI_G(tags)) {
+ ZEND_HASH_FOREACH_PTR(FFI_G(tags), tag) {
+ zend_ffi_subst_old_type(&tag->type, old, type);
+ } ZEND_HASH_FOREACH_END();
+ }
+}
+/* }}} */
+
+static zend_ffi_type *zend_ffi_remember_type(zend_ffi_type *type) /* {{{ */
+{
+ if (!FFI_G(weak_types)) {
+ FFI_G(weak_types) = emalloc(sizeof(HashTable));
+ zend_hash_init(FFI_G(weak_types), 0, NULL, zend_ffi_type_hash_dtor, 0);
+ }
+ // TODO: avoid dups ???
+ type->attr |= ZEND_FFI_ATTR_STORED;
+ zend_hash_next_index_insert_ptr(FFI_G(weak_types), ZEND_FFI_TYPE_MAKE_OWNED(type));
+ return type;
+}
+/* }}} */
+
+ZEND_METHOD(FFI, load) /* {{{ */
+{
+ zend_string *fn;
+ struct stat buf;
+ int fd;
+ char *filename, *code, *code_pos, *scope_name, *lib;
+ size_t code_size, scope_name_len;
+ zend_ffi *ffi;
+ zend_bool preload = (CG(compiler_options) & ZEND_COMPILE_PRELOAD) != 0;
+ DL_HANDLE handle;
+ zend_ffi_scope *scope = NULL;
+ zend_string *name;
+ zend_ffi_symbol *sym;
+ zend_ffi_tag *tag;
+ void *addr;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_STR(fn)
+ ZEND_PARSE_PARAMETERS_END();
+
+ filename = ZSTR_VAL(fn);
+ if (stat(filename, &buf) != 0) {
+ if (preload) {
+ zend_error(E_WARNING, "FFI: failed pre-loading '%s', file doesn't exist", filename);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', file doesn't exist", filename);
+ }
+ return;
+ }
+
+ if ((buf.st_mode & S_IFMT) != S_IFREG) {
+ if (preload) {
+ zend_error(E_WARNING, "FFI: failed pre-loading '%s', not a regular file", filename);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', not a regular file", filename);
+ }
+ return;
+ }
+
+ code_size = buf.st_size;
+ code = emalloc(code_size + 1);
+ fd = open(filename, O_RDONLY, 0);
+ if (fd < 0 || read(fd, code, code_size) != code_size) {
+ if (preload) {
+ zend_error(E_WARNING, "FFI: Failed pre-loading '%s', cannot read_file", filename);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', cannot read_file", filename);
+ }
+ efree(code);
+ close(fd);
+ return;
+ }
+ close(fd);
+ code[code_size] = 0;
+
+ FFI_G(symbols) = NULL;
+ FFI_G(tags) = NULL;
+ FFI_G(persistent) = preload;
+ FFI_G(default_type_attr) = preload ?
+ ZEND_FFI_ATTR_STORED | ZEND_FFI_ATTR_PERSISTENT :
+ ZEND_FFI_ATTR_STORED;
+
+ scope_name = NULL;
+ lib = NULL;
+ code_pos = zend_ffi_parse_directives(filename, code, &scope_name, &lib, preload);
+ if (!code_pos) {
+ efree(code);
+ FFI_G(persistent) = 0;
+ return;
+ }
+ code_size -= code_pos - code;
+
+ if (zend_ffi_parse_decl(code_pos, code_size) != SUCCESS) {
+ if (preload) {
+ zend_error(E_WARNING, "FFI: failed pre-loading '%s'", filename);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s'", filename);
+ }
+ goto cleanup;
+ }
+
+ if (lib) {
+ handle = DL_LOAD(lib);
+ if (!handle) {
+ if (preload) {
+ zend_error(E_WARNING, "FFI: Failed pre-loading '%s'", lib);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s'", lib);
+ }
+ goto cleanup;
+ }
+#ifdef RTLD_DEFAULT
+ } else if (1) {
+ // TODO: this might need to be disabled or protected ???
+ handle = RTLD_DEFAULT;
+#endif
+ }
+
+ if (preload) {
+ if (!scope_name) {
+ scope_name = "C";
+ }
+ scope_name_len = strlen(scope_name);
+ if (FFI_G(scopes)) {
+ scope = zend_hash_str_find_ptr(FFI_G(scopes), scope_name, scope_name_len);
+ }
+ }
+
+ if (FFI_G(symbols)) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(symbols), name, sym) {
+ if (sym->kind == ZEND_FFI_SYM_VAR) {
+ addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name));
+ if (!addr) {
+ if (preload) {
+ zend_error(E_WARNING, "FFI: failed pre-loading '%s', cannot resolve C variable '%s'", filename, ZSTR_VAL(name));
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed resolving C variable '%s'", ZSTR_VAL(name));
+ }
+ if (lib) {
+ DL_UNLOAD(handle);
+ }
+ goto cleanup;
+ }
+ sym->addr = addr;
+ } else if (sym->kind == ZEND_FFI_SYM_FUNC) {
+ addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name));
+ if (!addr) {
+ if (preload) {
+ zend_error(E_WARNING, "failed pre-loading '%s', cannot resolve C function '%s'", filename, ZSTR_VAL(name));
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed resolving C function '%s'", ZSTR_VAL(name));
+ }
+ if (lib) {
+ DL_UNLOAD(handle);
+ }
+ goto cleanup;
+ }
+ sym->addr = addr;
+ }
+ if (scope && scope->symbols) {
+ zend_ffi_symbol *old_sym = zend_hash_find_ptr(scope->symbols, name);
+
+ if (old_sym) {
+ if (zend_ffi_same_symbols(old_sym, sym)) {
+ if (ZEND_FFI_TYPE_IS_OWNED(sym->type)
+ && ZEND_FFI_TYPE(old_sym->type) != ZEND_FFI_TYPE(sym->type)) {
+ zend_ffi_type *type = ZEND_FFI_TYPE(sym->type);
+ zend_ffi_cleanup_type(ZEND_FFI_TYPE(old_sym->type), ZEND_FFI_TYPE(type));
+ zend_ffi_type_dtor(type);
+ }
+ } else {
+ zend_error(E_WARNING, "FFI: failed pre-loading '%s', redefinition of '%s'", filename, ZSTR_VAL(name));
+ if (lib) {
+ DL_UNLOAD(handle);
+ }
+ goto cleanup;
+ }
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+
+ if (preload) {
+ if (scope && scope->tags && FFI_G(tags)) {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(tags), name, tag) {
+ zend_ffi_tag *old_tag = zend_hash_find_ptr(scope->tags, name);
+
+ if (old_tag) {
+ if (zend_ffi_same_tags(old_tag, tag)) {
+ if (ZEND_FFI_TYPE_IS_OWNED(tag->type)
+ && ZEND_FFI_TYPE(old_tag->type) != ZEND_FFI_TYPE(tag->type)) {
+ zend_ffi_type *type = ZEND_FFI_TYPE(tag->type);
+ zend_ffi_cleanup_type(ZEND_FFI_TYPE(old_tag->type), ZEND_FFI_TYPE(type));
+ zend_ffi_type_dtor(type);
+ }
+ } else {
+ zend_error(E_WARNING, "FFI: failed pre-loading '%s', redefinition of '%s %s'", filename, zend_ffi_tag_kind_name[tag->kind], ZSTR_VAL(name));
+ if (lib) {
+ DL_UNLOAD(handle);
+ }
+ goto cleanup;
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+
+ if (!scope) {
+ scope = malloc(sizeof(zend_ffi_scope));
+ scope->symbols = FFI_G(symbols);
+ scope->tags = FFI_G(tags);
+
+ if (!FFI_G(scopes)) {
+ FFI_G(scopes) = malloc(sizeof(HashTable));
+ zend_hash_init(FFI_G(scopes), 0, NULL, zend_ffi_scope_hash_dtor, 1);
+ }
+
+ zend_hash_str_add_ptr(FFI_G(scopes), scope_name, scope_name_len, scope);
+ } else {
+ if (FFI_G(symbols)) {
+ if (!scope->symbols) {
+ scope->symbols = FFI_G(symbols);
+ FFI_G(symbols) = NULL;
+ } else {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(symbols), name, sym) {
+ if (!zend_hash_add_ptr(scope->symbols, name, sym)) {
+ zend_ffi_type_dtor(sym->type);
+ free(sym);
+ }
+ } ZEND_HASH_FOREACH_END();
+ FFI_G(symbols)->pDestructor = NULL;
+ zend_hash_destroy(FFI_G(symbols));
+ }
+ }
+ if (FFI_G(tags)) {
+ if (!scope->tags) {
+ scope->tags = FFI_G(tags);
+ FFI_G(tags) = NULL;
+ } else {
+ ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(tags), name, tag) {
+ if (!zend_hash_add_ptr(scope->tags, name, tag)) {
+ zend_ffi_type_dtor(tag->type);
+ free(tag);
+ }
+ } ZEND_HASH_FOREACH_END();
+ FFI_G(tags)->pDestructor = NULL;
+ zend_hash_destroy(FFI_G(tags));
+ }
+ }
+ }
+
+ ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
+ ffi->symbols = scope->symbols;
+ ffi->tags = scope->tags;
+ ffi->persistent = 1;
+ } else {
+ ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
+ ffi->lib = handle;
+ ffi->symbols = FFI_G(symbols);
+ ffi->tags = FFI_G(tags);
+ }
+
+ efree(code);
+ FFI_G(symbols) = NULL;
+ FFI_G(tags) = NULL;
+
+ RETURN_OBJ(&ffi->std);
+
+cleanup:
+ efree(code);
+ if (FFI_G(symbols)) {
+ zend_hash_destroy(FFI_G(symbols));
+ pefree(FFI_G(symbols), preload);
+ FFI_G(symbols) = NULL;
+ }
+ if (FFI_G(tags)) {
+ zend_hash_destroy(FFI_G(tags));
+ pefree(FFI_G(tags), preload);
+ FFI_G(tags) = NULL;
+ }
+ FFI_G(persistent) = 0;
+}
+/* }}} */
+
+ZEND_METHOD(FFI, scope) /* {{{ */
+{
+ zend_string *scope_name;
+ zend_ffi_scope *scope = NULL;
+ zend_ffi *ffi;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_STR(scope_name)
+ ZEND_PARSE_PARAMETERS_END();
+
+ if (FFI_G(scopes)) {
+ scope = zend_hash_find_ptr(FFI_G(scopes), scope_name);
+ }
+
+ if (!scope) {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading scope '%s'", ZSTR_VAL(scope_name));
+ return;
+ }
+
+ ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
+
+ ffi->symbols = scope->symbols;
+ ffi->tags = scope->tags;
+ ffi->persistent = 1;
+
+ RETURN_OBJ(&ffi->std);
+}
+/* }}} */
+
+static void zend_ffi_cleanup_dcl(zend_ffi_dcl *dcl) /* {{{ */
+{
+ if (dcl) {
+ zend_ffi_type_dtor(dcl->type);
+ dcl->type = NULL;
+ }
+}
+/* }}} */
+
+static void zend_ffi_throw_parser_error(const char *format, ...) /* {{{ */
+{
+ va_list va;
+ char *message = NULL;
+
+ va_start(va, format);
+ zend_vspprintf(&message, 0, format, va);
+
+ if (EG(current_execute_data)) {
+ zend_throw_exception(zend_ffi_parser_exception_ce, message, 0);
+ } else {
+ zend_error(E_WARNING, "FFI Parser: %s", message);
+ }
+
+ efree(message);
+ va_end(va);
+}
+/* }}} */
+
+static int zend_ffi_validate_vla(zend_ffi_type *type) /* {{{ */
+{
+ if (!FFI_G(allow_vla) && (type->attr & ZEND_FFI_ATTR_VLA)) {
+ zend_ffi_throw_parser_error("'[*]' not allowed in other than function prototype scope at line %d", FFI_G(line));
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+static int zend_ffi_validate_incomplete_type(zend_ffi_type *type, zend_bool allow_ic) /* {{{ */
+{
+ if (type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG) {
+ if (FFI_G(tags)) {
+ zend_string *key;
+ zend_ffi_tag *tag;
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(tags), key, tag) {
+ if (ZEND_FFI_TYPE(tag->type) == type) {
+ if (type->kind == ZEND_FFI_TYPE_ENUM) {
+ zend_ffi_throw_parser_error("incomplete 'enum %s' at line %d", ZSTR_VAL(key), FFI_G(line));
+ } else if (type->attr & ZEND_FFI_ATTR_UNION) {
+ zend_ffi_throw_parser_error("incomplete 'union %s' at line %d", ZSTR_VAL(key), FFI_G(line));
+ } else {
+ zend_ffi_throw_parser_error("incomplete 'struct %s' at line %d", ZSTR_VAL(key), FFI_G(line));
+ }
+ return FAILURE;
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+ if (FFI_G(symbols)) {
+ zend_string *key;
+ zend_ffi_symbol *sym;
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(symbols), key, sym) {
+ if (type == ZEND_FFI_TYPE(sym->type)) {
+ zend_ffi_throw_parser_error("incomplete C type '%s' at line %d", ZSTR_VAL(key), FFI_G(line));
+ return FAILURE;
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+ zend_ffi_throw_parser_error("incomplete type at line %d", FFI_G(line));
+ return FAILURE;
+ } else if (!allow_ic && type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY) {
+ zend_ffi_throw_parser_error("'[]' not allowed at line %d", FFI_G(line));
+ return FAILURE;
+ } else if (!FFI_G(allow_vla) && (type->attr & ZEND_FFI_ATTR_VLA)) {
+ zend_ffi_throw_parser_error("'[*]' not allowed in other than function prototype scope at line %d", FFI_G(line));
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+static int zend_ffi_validate_type(zend_ffi_type *type, zend_bool allow_ic) /* {{{ */
+{
+ if (type->kind == ZEND_FFI_TYPE_VOID) {
+ zend_ffi_throw_parser_error("'void' type is not allowed at line %d", FFI_G(line));
+ return FAILURE;
+ }
+ return zend_ffi_validate_incomplete_type(type, allow_ic);
+}
+/* }}} */
+
+static int zend_ffi_validate_var_type(zend_ffi_type *type, zend_bool allow_ic) /* {{{ */
+{
+ if (type->kind == ZEND_FFI_TYPE_FUNC) {
+ zend_ffi_throw_parser_error("'function' type is not allowed at line %d", FFI_G(line));
+ return FAILURE;
+ }
+ return zend_ffi_validate_type(type, allow_ic);
+}
+/* }}} */
+
+void zend_ffi_validate_type_name(zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_finalize_type(dcl);
+ if (zend_ffi_validate_var_type(ZEND_FFI_TYPE(dcl->type), 0) != SUCCESS) {
+ zend_ffi_cleanup_dcl(dcl);
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+}
+/* }}} */
+
+static int zend_ffi_subst_type(zend_ffi_type **dcl, zend_ffi_type *type) /* {{{ */
+{
+ zend_ffi_type *dcl_type;
+ zend_ffi_field *field;
+
+ if (*dcl == type) {
+ *dcl = ZEND_FFI_TYPE_MAKE_OWNED(type);
+ return 1;
+ }
+ dcl_type = *dcl;
+ switch (dcl_type->kind) {
+ case ZEND_FFI_TYPE_POINTER:
+ return zend_ffi_subst_type(&dcl_type->pointer.type, type);
+ case ZEND_FFI_TYPE_ARRAY:
+ return zend_ffi_subst_type(&dcl_type->array.type, type);
+ case ZEND_FFI_TYPE_FUNC:
+ if (zend_ffi_subst_type(&dcl_type->func.ret_type, type)) {
+ return 1;
+ }
+ if (dcl_type->func.args) {
+ zval *zv;
+
+ ZEND_HASH_FOREACH_VAL(dcl_type->func.args, zv) {
+ if (zend_ffi_subst_type((zend_ffi_type**)&Z_PTR_P(zv), type)) {
+ return 1;
+ }
+ } ZEND_HASH_FOREACH_END();
+ }
+ break;
+ case ZEND_FFI_TYPE_STRUCT:
+ ZEND_HASH_FOREACH_PTR(&dcl_type->record.fields, field) {
+ if (zend_ffi_subst_type(&field->type, type)) {
+ return 1;
+ }
+ } ZEND_HASH_FOREACH_END();
+ break;
+ default:
+ break;
+ }
+ return 0;
+} /* }}} */
+
+static void zend_ffi_tags_cleanup(zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_tag *tag;
+ ZEND_HASH_FOREACH_PTR(FFI_G(tags), tag) {
+ if (ZEND_FFI_TYPE_IS_OWNED(tag->type)) {
+ zend_ffi_type *type = ZEND_FFI_TYPE(tag->type);
+ zend_ffi_subst_type(&dcl->type, type);
+ tag->type = type;
+ }
+ } ZEND_HASH_FOREACH_END();
+ zend_hash_destroy(FFI_G(tags));
+ efree(FFI_G(tags));
+}
+/* }}} */
+
+ZEND_METHOD(FFI, new) /* {{{ */
+{
+ zend_string *type_def = NULL;
+ zval *ztype;
+ zend_ffi_type *type, *type_ptr;
+ zend_ffi_cdata *cdata;
+ void *ptr;
+ zend_bool owned = 1;
+ zend_bool persistent = 0;
+ zend_bool is_const = 0;
+ zend_ffi_flags flags = ZEND_FFI_FLAG_OWNED;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(1, 3)
+ if (Z_TYPE_P(EX_VAR_NUM(0)) == IS_STRING) {
+ Z_PARAM_STR(type_def)
+ } else {
+ Z_PARAM_OBJECT_OF_CLASS(ztype, zend_ffi_ctype_ce)
+ }
+ Z_PARAM_OPTIONAL
+ Z_PARAM_BOOL(owned)
+ Z_PARAM_BOOL(persistent)
+ ZEND_PARSE_PARAMETERS_END();
+
+ if (!owned) {
+ flags &= ~ZEND_FFI_FLAG_OWNED;
+ }
+
+ if (persistent) {
+ flags |= ZEND_FFI_FLAG_PERSISTENT;
+ }
+
+ if (type_def) {
+ zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
+
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This));
+ FFI_G(symbols) = ffi->symbols;
+ FFI_G(tags) = ffi->tags;
+ } else {
+ FFI_G(symbols) = NULL;
+ FFI_G(tags) = NULL;
+ }
+
+ FFI_G(default_type_attr) = 0;
+
+ if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) != SUCCESS) {
+ zend_ffi_type_dtor(dcl.type);
+ if (Z_TYPE(EX(This)) != IS_OBJECT) {
+ if (FFI_G(tags)) {
+ zend_hash_destroy(FFI_G(tags));
+ efree(FFI_G(tags));
+ FFI_G(tags) = NULL;
+ }
+ if (FFI_G(symbols)) {
+ zend_hash_destroy(FFI_G(symbols));
+ efree(FFI_G(symbols));
+ FFI_G(symbols) = NULL;
+ }
+ }
+ return;
+ }
+
+ type = ZEND_FFI_TYPE(dcl.type);
+ if (dcl.attr & ZEND_FFI_ATTR_CONST) {
+ is_const = 1;
+ }
+
+ if (Z_TYPE(EX(This)) != IS_OBJECT) {
+ if (FFI_G(tags)) {
+ zend_ffi_tags_cleanup(&dcl);
+ }
+ if (FFI_G(symbols)) {
+ zend_hash_destroy(FFI_G(symbols));
+ efree(FFI_G(symbols));
+ FFI_G(symbols) = NULL;
+ }
+ }
+ FFI_G(symbols) = NULL;
+ FFI_G(tags) = NULL;
+
+ type_ptr = dcl.type;
+ } else {
+ zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(ztype);
+
+ type_ptr = type = ctype->type;
+ if (ZEND_FFI_TYPE_IS_OWNED(type)) {
+ type = ZEND_FFI_TYPE(type);
+ if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
+ if (GC_REFCOUNT(&ctype->std) == 1) {
+ /* transfer type ownership */
+ ctype->type = type;
+ } else {
+ ctype->type = type_ptr = type = zend_ffi_remember_type(type);
+ }
+ }
+ }
+ }
+
+ ptr = pemalloc(type->size, flags & ZEND_FFI_FLAG_PERSISTENT);
+ memset(ptr, 0, type->size);
+
+ cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
+ if (type->kind < ZEND_FFI_TYPE_POINTER) {
+ cdata->std.handlers = &zend_ffi_cdata_value_handlers;
+ }
+ cdata->type = type_ptr;
+ cdata->ptr = ptr;
+ cdata->flags = flags;
+ if (is_const) {
+ cdata->flags |= ZEND_FFI_FLAG_CONST;
+ }
+
+ RETURN_OBJ(&cdata->std);
+}
+/* }}} */
+
+ZEND_METHOD(FFI, free) /* {{{ */
+{
+ zval *zv;
+ zend_ffi_cdata *cdata;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_OBJECT_OF_CLASS_EX2(zv, zend_ffi_cdata_ce, 0, 1, 0);
+ ZEND_PARSE_PARAMETERS_END();
+
+ cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
+
+ if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
+ if (!cdata->ptr) {
+ zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
+ return;
+ }
+ if (cdata->ptr != (void*)&cdata->ptr_holder) {
+ pefree(*(void**)cdata->ptr, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
+ } else {
+ pefree(cdata->ptr_holder, (cdata->flags & ZEND_FFI_FLAG_PERSISTENT) || !is_zend_ptr(cdata->ptr_holder));
+ }
+ *(void**)cdata->ptr = NULL;
+ } else if (!(cdata->flags & ZEND_FFI_FLAG_OWNED)) {
+ pefree(cdata->ptr, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
+ cdata->ptr = NULL;
+ cdata->flags &= ~(ZEND_FFI_FLAG_OWNED|ZEND_FFI_FLAG_PERSISTENT);
+ cdata->std.handlers = &zend_ffi_cdata_free_handlers;
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "free() non a C pointer");
+ }
+}
+/* }}} */
+
+ZEND_METHOD(FFI, cast) /* {{{ */
+{
+ zend_string *type_def = NULL;
+ zval *ztype;
+ zend_ffi_type *old_type, *type, *type_ptr;
+ zend_ffi_cdata *old_cdata, *cdata;
+ zend_bool is_const = 0;
+ zval *zv, *arg;
+ void *ptr;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(2, 2)
+ if (Z_TYPE_P(EX_VAR_NUM(0)) == IS_STRING) {
+ Z_PARAM_STR(type_def)
+ } else {
+ Z_PARAM_OBJECT_OF_CLASS(ztype, zend_ffi_ctype_ce)
+ }
+ Z_PARAM_ZVAL(zv);
+ ZEND_PARSE_PARAMETERS_END();
+
+ arg = zv;
+ ZVAL_DEREF(zv);
+
+ if (type_def) {
+ zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
+
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This));
+ FFI_G(symbols) = ffi->symbols;
+ FFI_G(tags) = ffi->tags;
+ } else {
+ FFI_G(symbols) = NULL;
+ FFI_G(tags) = NULL;
+ }
+
+ FFI_G(default_type_attr) = 0;
+
+ if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) != SUCCESS) {
+ zend_ffi_type_dtor(dcl.type);
+ if (Z_TYPE(EX(This)) != IS_OBJECT) {
+ if (FFI_G(tags)) {
+ zend_hash_destroy(FFI_G(tags));
+ efree(FFI_G(tags));
+ FFI_G(tags) = NULL;
+ }
+ if (FFI_G(symbols)) {
+ zend_hash_destroy(FFI_G(symbols));
+ efree(FFI_G(symbols));
+ FFI_G(symbols) = NULL;
+ }
+ }
+ return;
+ }
+
+ type = ZEND_FFI_TYPE(dcl.type);
+ if (dcl.attr & ZEND_FFI_ATTR_CONST) {
+ is_const = 1;
+ }
+
+ if (Z_TYPE(EX(This)) != IS_OBJECT) {
+ if (FFI_G(tags)) {
+ zend_ffi_tags_cleanup(&dcl);
+ }
+ if (FFI_G(symbols)) {
+ zend_hash_destroy(FFI_G(symbols));
+ efree(FFI_G(symbols));
+ FFI_G(symbols) = NULL;
+ }
+ }
+ FFI_G(symbols) = NULL;
+ FFI_G(tags) = NULL;
+
+ type_ptr = dcl.type;
+ } else {
+ zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(ztype);
+
+ type_ptr = type = ctype->type;
+ if (ZEND_FFI_TYPE_IS_OWNED(type)) {
+ type = ZEND_FFI_TYPE(type);
+ if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
+ if (GC_REFCOUNT(&ctype->std) == 1) {
+ /* transfer type ownership */
+ ctype->type = type;
+ } else {
+ ctype->type = type_ptr = type = zend_ffi_remember_type(type);
+ }
+ }
+ }
+ }
+
+ if (Z_TYPE_P(zv) != IS_OBJECT || Z_OBJCE_P(zv) != zend_ffi_cdata_ce) {
+ if (type->kind < ZEND_FFI_TYPE_POINTER && Z_TYPE_P(zv) < IS_STRING) {
+ /* numeric conversion */
+ cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
+ cdata->std.handlers = &zend_ffi_cdata_value_handlers;
+ cdata->type = type_ptr;
+ cdata->ptr = emalloc(type->size);
+ zend_ffi_zval_to_cdata(cdata->ptr, type, zv);
+ cdata->flags = ZEND_FFI_FLAG_OWNED;
+ if (is_const) {
+ cdata->flags |= ZEND_FFI_FLAG_CONST;
+ }
+ RETURN_OBJ(&cdata->std);
+ } else if (type->kind == ZEND_FFI_TYPE_POINTER && Z_TYPE_P(zv) == IS_LONG) {
+ /* number to pointer conversion */
+ cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
+ cdata->type = type_ptr;
+ cdata->ptr = &cdata->ptr_holder;
+ cdata->ptr_holder = (void*)(intptr_t)Z_LVAL_P(zv);
+ if (is_const) {
+ cdata->flags |= ZEND_FFI_FLAG_CONST;
+ }
+ RETURN_OBJ(&cdata->std);
+ } else if (type->kind == ZEND_FFI_TYPE_POINTER && Z_TYPE_P(zv) == IS_NULL) {
+ /* null -> pointer */
+ cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
+ cdata->type = type_ptr;
+ cdata->ptr = &cdata->ptr_holder;
+ cdata->ptr_holder = NULL;
+ if (is_const) {
+ cdata->flags |= ZEND_FFI_FLAG_CONST;
+ }
+ RETURN_OBJ(&cdata->std);
+ } else {
+ zend_wrong_parameter_class_error(2, "FFI\\CData", zv);
+ }
+ }
+
+ old_cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
+ old_type = ZEND_FFI_TYPE(old_cdata->type);
+ ptr = old_cdata->ptr;
+
+ cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
+ if (type->kind < ZEND_FFI_TYPE_POINTER) {
+ cdata->std.handlers = &zend_ffi_cdata_value_handlers;
+ }
+ cdata->type = type_ptr;
+
+ if (old_type->kind == ZEND_FFI_TYPE_POINTER
+ && type->kind != ZEND_FFI_TYPE_POINTER
+ && ZEND_FFI_TYPE(old_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID) {
+ /* automatically dereference void* pointers ??? */
+ cdata->ptr = *(void**)ptr;
+ } else if (old_type->kind == ZEND_FFI_TYPE_ARRAY
+ && type->kind == ZEND_FFI_TYPE_POINTER) {
+ cdata->ptr = &cdata->ptr_holder;
+ cdata->ptr_holder = old_cdata->ptr;
+ } else if (type->size > old_type->size) {
+ zend_throw_error(zend_ffi_exception_ce, "attempt to cast to larger type");
+ return;
+ } else if (ptr != &old_cdata->ptr_holder) {
+ cdata->ptr = ptr;
+ } else {
+ cdata->ptr = &cdata->ptr_holder;
+ cdata->ptr_holder = old_cdata->ptr_holder;
+ }
+ if (is_const) {
+ cdata->flags |= ZEND_FFI_FLAG_CONST;
+ }
+
+ if (old_cdata->flags & ZEND_FFI_FLAG_OWNED) {
+ if (GC_REFCOUNT(&old_cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1) {
+ /* transfer ownership */
+ old_cdata->flags &= ~ZEND_FFI_FLAG_OWNED;
+ cdata->flags |= ZEND_FFI_FLAG_OWNED;
+ } else {
+ //???zend_throw_error(zend_ffi_exception_ce, "Attempt to cast owned C pointer");
+ }
+ }
+
+ RETURN_OBJ(&cdata->std);
+}
+/* }}} */
+
+ZEND_METHOD(FFI, type) /* {{{ */
+{
+ zend_ffi_ctype *ctype;
+ zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
+ zend_string *type_def;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_STR(type_def);
+ ZEND_PARSE_PARAMETERS_END();
+
+ if (Z_TYPE(EX(This)) == IS_OBJECT) {
+ zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This));
+ FFI_G(symbols) = ffi->symbols;
+ FFI_G(tags) = ffi->tags;
+ } else {
+ FFI_G(symbols) = NULL;
+ FFI_G(tags) = NULL;
+ }
+
+ FFI_G(default_type_attr) = 0;
+
+ if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) != SUCCESS) {
+ zend_ffi_type_dtor(dcl.type);
+ if (Z_TYPE(EX(This)) != IS_OBJECT) {
+ if (FFI_G(tags)) {
+ zend_hash_destroy(FFI_G(tags));
+ efree(FFI_G(tags));
+ FFI_G(tags) = NULL;
+ }
+ if (FFI_G(symbols)) {
+ zend_hash_destroy(FFI_G(symbols));
+ efree(FFI_G(symbols));
+ FFI_G(symbols) = NULL;
+ }
+ }
+ return;
+ }
+
+ if (Z_TYPE(EX(This)) != IS_OBJECT) {
+ if (FFI_G(tags)) {
+ zend_ffi_tags_cleanup(&dcl);
+ }
+ if (FFI_G(symbols)) {
+ zend_hash_destroy(FFI_G(symbols));
+ efree(FFI_G(symbols));
+ FFI_G(symbols) = NULL;
+ }
+ }
+ FFI_G(symbols) = NULL;
+ FFI_G(tags) = NULL;
+
+ ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
+ ctype->type = dcl.type;
+
+ RETURN_OBJ(&ctype->std);
+}
+/* }}} */
+
+ZEND_METHOD(FFI, typeof) /* {{{ */
+{
+ zval *zv, *arg;
+ zend_ffi_ctype *ctype;
+ zend_ffi_type *type;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_ZVAL(zv);
+ ZEND_PARSE_PARAMETERS_END();
+
+ arg = zv;
+ ZVAL_DEREF(zv);
+ if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
+
+ type = cdata->type;
+ if (ZEND_FFI_TYPE_IS_OWNED(type)) {
+ type = ZEND_FFI_TYPE(type);
+ if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
+ if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1) {
+ /* transfer type ownership */
+ cdata->type = type;
+ type = ZEND_FFI_TYPE_MAKE_OWNED(type);
+ } else {
+ cdata->type = type = zend_ffi_remember_type(type);
+ }
+ }
+ }
+ } else {
+ zend_wrong_parameter_class_error(1, "FFI\\CData", zv);
+ return;
+ }
+
+ ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
+ ctype->type = type;
+
+ RETURN_OBJ(&ctype->std);
+}
+/* }}} */
+
+ZEND_METHOD(FFI, arrayType) /* {{{ */
+{
+ zval *ztype;
+ zend_ffi_ctype *ctype;
+ zend_ffi_type *type;
+ HashTable *dims;
+ zval *val;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(2, 2)
+ Z_PARAM_OBJECT_OF_CLASS(ztype, zend_ffi_ctype_ce)
+ Z_PARAM_ARRAY_HT(dims)
+ ZEND_PARSE_PARAMETERS_END();
+
+ ctype = (zend_ffi_ctype*)Z_OBJ_P(ztype);
+ type = ZEND_FFI_TYPE(ctype->type);
+
+ if (type->kind == ZEND_FFI_TYPE_FUNC) {
+ zend_throw_error(zend_ffi_exception_ce, "array of functions is not allowed");
+ return;
+ } else if (type->kind == ZEND_FFI_TYPE_ARRAY && (type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
+ zend_throw_error(zend_ffi_exception_ce, "only the leftmost array can be undimensioned");
+ return;
+ } else if (type->kind == ZEND_FFI_TYPE_VOID) {
+ zend_throw_error(zend_ffi_exception_ce, "array of 'void' is not allowed");
+ return;
+ } else if (type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG) {
+ zend_throw_error(zend_ffi_exception_ce, "array of incomplete type is not allowed");
+ return;
+ }
+
+ if (ZEND_FFI_TYPE_IS_OWNED(ctype->type)) {
+ if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
+ if (GC_REFCOUNT(&ctype->std) == 1) {
+ /* transfer type ownership */
+ ctype->type = type;
+ type = ZEND_FFI_TYPE_MAKE_OWNED(type);
+ } else {
+ ctype->type = type = zend_ffi_remember_type(type);
+ }
+ }
+ }
+
+ ZEND_HASH_REVERSE_FOREACH_VAL(dims, val) {
+ zend_long n = zval_get_long(val);
+ zend_ffi_type *new_type;
+
+ if (n < 0) {
+ zend_throw_error(zend_ffi_exception_ce, "negative array index");
+ zend_ffi_type_dtor(type);
+ return;
+ } else if (ZEND_FFI_TYPE(type)->kind == ZEND_FFI_TYPE_ARRAY && (ZEND_FFI_TYPE(type)->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
+ zend_throw_error(zend_ffi_exception_ce, "only the leftmost array can be undimensioned");
+ zend_ffi_type_dtor(type);
+ return;
+ }
+
+ new_type = emalloc(sizeof(zend_ffi_type));
+ new_type->kind = ZEND_FFI_TYPE_ARRAY;
+ new_type->attr = 0;
+ new_type->size = n * ZEND_FFI_TYPE(type)->size;
+ new_type->align = ZEND_FFI_TYPE(type)->align;
+ new_type->array.type = type;
+ new_type->array.length = n;
+
+ if (n == 0) {
+ new_type->attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;
+ }
+
+ type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
+ } ZEND_HASH_FOREACH_END();
+
+ ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
+ ctype->type = type;
+
+ RETURN_OBJ(&ctype->std);
+}
+/* }}} */
+
+ZEND_METHOD(FFI, addr) /* {{{ */
+{
+ zend_ffi_type *type, *new_type;
+ zend_ffi_cdata *cdata, *new_cdata;
+ zval *zv, *arg;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_ZVAL(zv)
+ ZEND_PARSE_PARAMETERS_END();
+
+ arg = zv;
+ ZVAL_DEREF(zv);
+ if (Z_TYPE_P(zv) != IS_OBJECT || Z_OBJCE_P(zv) != zend_ffi_cdata_ce) {
+ zend_wrong_parameter_class_error(1, "FFI\\CData", zv);
+ }
+
+ cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
+ type = ZEND_FFI_TYPE(cdata->type);
+
+ new_type = emalloc(sizeof(zend_ffi_type));
+ new_type->kind = ZEND_FFI_TYPE_POINTER;
+ new_type->attr = 0;
+ new_type->size = sizeof(void*);
+ new_type->align = _Alignof(void*);
+ /* life-time (source must relive the resulting pointer) ??? */
+ new_type->pointer.type = type;
+
+ new_cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
+ new_cdata->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
+ new_cdata->ptr_holder = cdata->ptr;
+ new_cdata->ptr = &new_cdata->ptr_holder;
+
+ if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1) {
+ if (ZEND_FFI_TYPE_IS_OWNED(cdata->type)) {
+ /* transfer type ownership */
+ cdata->type = type;
+ new_type->pointer.type = ZEND_FFI_TYPE_MAKE_OWNED(type);
+ }
+ if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
+ /* transfer ownership */
+ cdata->flags &= ~ZEND_FFI_FLAG_OWNED;
+ new_cdata->flags |= ZEND_FFI_FLAG_OWNED;
+ }
+ }
+
+ RETURN_OBJ(&new_cdata->std);
+}
+/* }}} */
+
+ZEND_METHOD(FFI, sizeof) /* {{{ */
+{
+ zval *zv;
+ zend_ffi_type *type;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_ZVAL(zv);
+ ZEND_PARSE_PARAMETERS_END();
+
+ ZVAL_DEREF(zv);
+ if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
+ type = ZEND_FFI_TYPE(cdata->type);
+ } else if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_ctype_ce) {
+ zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(zv);
+ type = ZEND_FFI_TYPE(ctype->type);
+ } else {
+ zend_wrong_parameter_class_error(1, "FFI\\CData or FFI\\CType", zv);
+ return;
+ }
+
+ RETURN_LONG(type->size);
+}
+/* }}} */
+
+ZEND_METHOD(FFI, alignof) /* {{{ */
+{
+ zval *zv;
+ zend_ffi_type *type;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(1, 1)
+ Z_PARAM_ZVAL(zv);
+ ZEND_PARSE_PARAMETERS_END();
+
+ ZVAL_DEREF(zv);
+ if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_cdata_ce) {
+ zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
+ type = ZEND_FFI_TYPE(cdata->type);
+ } else if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_ctype_ce) {
+ zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(zv);
+ type = ZEND_FFI_TYPE(ctype->type);
+ } else {
+ zend_wrong_parameter_class_error(1, "FFI\\CData or FFI\\CType", zv);
+ return;
+ }
+
+ RETURN_LONG(type->align);
+}
+/* }}} */
+
+ZEND_METHOD(FFI, memcpy) /* {{{ */
+{
+ zval *zv1, *zv2;
+ zend_ffi_cdata *cdata1, *cdata2;
+ zend_ffi_type *type1, *type2;
+ void *ptr1, *ptr2;
+ zend_long size;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(3, 3)
+ Z_PARAM_OBJECT_OF_CLASS_EX2(zv1, zend_ffi_cdata_ce, 0, 1, 0);
+ Z_PARAM_ZVAL(zv2)
+ Z_PARAM_LONG(size)
+ ZEND_PARSE_PARAMETERS_END();
+
+ cdata1 = (zend_ffi_cdata*)Z_OBJ_P(zv1);
+ type1 = ZEND_FFI_TYPE(cdata1->type);
+ if (type1->kind == ZEND_FFI_TYPE_POINTER) {
+ ptr1 = *(void**)cdata1->ptr;
+ } else {
+ ptr1 = cdata1->ptr;
+ if (type1->kind != ZEND_FFI_TYPE_POINTER && size > type1->size) {
+ zend_throw_error(zend_ffi_exception_ce, "attempt to write over data boundary");
+ return;
+ }
+ }
+
+ ZVAL_DEREF(zv2);
+ if (Z_TYPE_P(zv2) == IS_STRING) {
+ ptr2 = Z_STRVAL_P(zv2);
+ if (size > Z_STRLEN_P(zv2)) {
+ zend_throw_error(zend_ffi_exception_ce, "attempt to read over string boundary");
+ return;
+ }
+ } else if (Z_TYPE_P(zv2) == IS_OBJECT && Z_OBJCE_P(zv2) == zend_ffi_cdata_ce) {
+ cdata2 = (zend_ffi_cdata*)Z_OBJ_P(zv2);
+ type2 = ZEND_FFI_TYPE(cdata2->type);
+ if (type2->kind == ZEND_FFI_TYPE_POINTER) {
+ ptr2 = *(void**)cdata2->ptr;
+ } else {
+ ptr2 = cdata2->ptr;
+ if (type2->kind != ZEND_FFI_TYPE_POINTER && size > type2->size) {
+ zend_throw_error(zend_ffi_exception_ce, "attempt to read over data boundary");
+ return;
+ }
+ }
+ } else {
+ zend_wrong_parameter_class_error(2, "FFI\\CData or string", zv2);
+ return;
+ }
+
+ memcpy(ptr1, ptr2, size);
+}
+/* }}} */
+
+ZEND_METHOD(FFI, memcmp) /* {{{ */
+{
+ zval *zv1, *zv2;
+ zend_ffi_cdata *cdata1, *cdata2;
+ zend_ffi_type *type1, *type2;
+ void *ptr1, *ptr2;
+ zend_long size;
+ int ret;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(3, 3)
+ Z_PARAM_ZVAL(zv1);
+ Z_PARAM_ZVAL(zv2);
+ Z_PARAM_LONG(size)
+ ZEND_PARSE_PARAMETERS_END();
+
+ ZVAL_DEREF(zv1);
+ if (Z_TYPE_P(zv1) == IS_STRING) {
+ ptr1 = Z_STRVAL_P(zv1);
+ if (size > Z_STRLEN_P(zv1)) {
+ zend_throw_error(zend_ffi_exception_ce, "attempt to read over string boundary");
+ return;
+ }
+ } else if (Z_TYPE_P(zv1) == IS_OBJECT && Z_OBJCE_P(zv1) == zend_ffi_cdata_ce) {
+ cdata1 = (zend_ffi_cdata*)Z_OBJ_P(zv1);
+ type1 = ZEND_FFI_TYPE(cdata1->type);
+ if (type1->kind == ZEND_FFI_TYPE_POINTER) {
+ ptr1 = *(void**)cdata1->ptr;
+ } else {
+ ptr1 = cdata1->ptr;
+ if (type1->kind != ZEND_FFI_TYPE_POINTER && size > type1->size) {
+ zend_throw_error(zend_ffi_exception_ce, "attempt to read over data boundary");
+ return;
+ }
+ }
+ } else {
+ zend_wrong_parameter_class_error(1, "FFI\\CData or string", zv1);
+ return;
+ }
+
+ ZVAL_DEREF(zv2);
+ if (Z_TYPE_P(zv2) == IS_STRING) {
+ ptr2 = Z_STRVAL_P(zv2);
+ if (size > Z_STRLEN_P(zv2)) {
+ zend_throw_error(zend_ffi_exception_ce, "attempt to read over string boundary");
+ return;
+ }
+ } else if (Z_TYPE_P(zv2) == IS_OBJECT && Z_OBJCE_P(zv2) == zend_ffi_cdata_ce) {
+ cdata2 = (zend_ffi_cdata*)Z_OBJ_P(zv2);
+ type2 = ZEND_FFI_TYPE(cdata2->type);
+ if (type2->kind == ZEND_FFI_TYPE_POINTER) {
+ ptr2 = *(void**)cdata2->ptr;
+ } else {
+ ptr2 = cdata2->ptr;
+ if (type2->kind != ZEND_FFI_TYPE_POINTER && size > type2->size) {
+ zend_throw_error(zend_ffi_exception_ce, "attempt to read over data boundary");
+ return;
+ }
+ }
+ } else {
+ zend_wrong_parameter_class_error(2, "FFI\\CData or string", zv2);
+ return;
+ }
+
+ ret = memcmp(ptr1, ptr2, size);
+ if (ret == 0) {
+ RETVAL_LONG(0);
+ } else if (ret < 0) {
+ RETVAL_LONG(-1);
+ } else {
+ RETVAL_LONG(1);
+ }
+}
+/* }}} */
+
+ZEND_METHOD(FFI, memset) /* {{{ */
+{
+ zval *zv;
+ zend_ffi_cdata *cdata;
+ zend_ffi_type *type;
+ void *ptr;
+ zend_long ch, size;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(3, 3)
+ Z_PARAM_OBJECT_OF_CLASS_EX2(zv, zend_ffi_cdata_ce, 0, 1, 0);
+ Z_PARAM_LONG(ch)
+ Z_PARAM_LONG(size)
+ ZEND_PARSE_PARAMETERS_END();
+
+ cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
+ type = ZEND_FFI_TYPE(cdata->type);
+ if (type->kind == ZEND_FFI_TYPE_POINTER) {
+ ptr = *(void**)cdata->ptr;
+ } else {
+ ptr = cdata->ptr;
+ if (type->kind != ZEND_FFI_TYPE_POINTER && size > type->size) {
+ zend_throw_error(zend_ffi_exception_ce, "attempt to write over data boundary");
+ return;
+ }
+ }
+
+ memset(ptr, ch, size);
+}
+/* }}} */
+
+ZEND_METHOD(FFI, string) /* {{{ */
+{
+ zval *zv;
+ zend_ffi_cdata *cdata;
+ zend_ffi_type *type;
+ void *ptr;
+ zend_long size = 0;
+
+ ZEND_FFI_VALIDATE_API_RESTRICTION();
+ ZEND_PARSE_PARAMETERS_START(1, 2)
+ Z_PARAM_OBJECT_OF_CLASS_EX2(zv, zend_ffi_cdata_ce, 0, 1, 0);
+ Z_PARAM_OPTIONAL
+ Z_PARAM_LONG(size)
+ ZEND_PARSE_PARAMETERS_END();
+
+ cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
+ type = ZEND_FFI_TYPE(cdata->type);
+ if (EX_NUM_ARGS() == 2) {
+ if (type->kind == ZEND_FFI_TYPE_POINTER) {
+ ptr = *(void**)cdata->ptr;
+ } else {
+ ptr = cdata->ptr;
+ if (type->kind != ZEND_FFI_TYPE_POINTER && size > type->size) {
+ zend_throw_error(zend_ffi_exception_ce, "attempt to read over data boundary");
+ return;
+ }
+ }
+ RETURN_STRINGL((char*)ptr, size);
+ } else {
+ if (type->kind == ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) {
+ ptr = *(void**)cdata->ptr;
+ } else if (type->kind == ZEND_FFI_TYPE_ARRAY && ZEND_FFI_TYPE(type->array.type)->kind == ZEND_FFI_TYPE_CHAR) {
+ ptr = cdata->ptr;
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "FFI\\Cdata is not a C string");
+ return;
+ }
+ RETURN_STRING((char*)ptr);
+ }
+}
+/* }}} */
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_cdef, 0, 0, 0)
+ ZEND_ARG_INFO(0, code)
+ ZEND_ARG_INFO(0, lib)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_load, 0, 0, 1)
+ ZEND_ARG_INFO(0, filename)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_scope, 0, 0, 1)
+ ZEND_ARG_INFO(0, scope_name)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_new, 0, 0, 1)
+ ZEND_ARG_INFO(0, type)
+ ZEND_ARG_INFO(0, owned)
+ ZEND_ARG_INFO(0, persistent)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_free, 0, 0, 1)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_cast, 0, 0, 2)
+ ZEND_ARG_INFO(0, type)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_type, 0, 0, 1)
+ ZEND_ARG_INFO(0, type)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_typeof, 0, 0, 1)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_array, 0, 0, 2)
+ ZEND_ARG_INFO(0, type)
+ ZEND_ARG_INFO(0, dims)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_addr, 0, 0, 1)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_sizeof, 0, 0, 1)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_alignof, 0, 0, 1)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_memcpy, 0, 0, 3)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, dst)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, src)
+ ZEND_ARG_INFO(0, size)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_memcmp, 0, 0, 3)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr1)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr2)
+ ZEND_ARG_INFO(0, size)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_memset, 0, 0, 3)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
+ ZEND_ARG_INFO(0, ch)
+ ZEND_ARG_INFO(0, size)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_func_string, 0, 0, 1)
+ ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
+ ZEND_ARG_INFO(0, size)
+ZEND_END_ARG_INFO()
+
+static const zend_function_entry zend_ffi_functions[] = {
+ ZEND_ME(FFI, cdef, arginfo_func_cdef, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, load, arginfo_func_load, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, scope, arginfo_func_scope, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, new, arginfo_func_new, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, free, arginfo_func_free, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, cast, arginfo_func_cast, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, type, arginfo_func_type, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, typeof, arginfo_func_typeof, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, arrayType, arginfo_func_array, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, addr, arginfo_func_addr, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, sizeof, arginfo_func_sizeof, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, alignof, arginfo_func_alignof, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, memcpy, arginfo_func_memcpy, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, memcmp, arginfo_func_memcmp, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, memset, arginfo_func_memset, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_ME(FFI, string, arginfo_func_string, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
+ ZEND_FE_END
+};
+
+static char *zend_ffi_parse_directives(const char *filename, char *code_pos, char **scope_name, char **lib, zend_bool preload) /* {{{ */
+{
+ char *p;
+
+ *scope_name = NULL;
+ *lib = NULL;
+ while (*code_pos == '#') {
+ if (strncmp(code_pos, "#define FFI_SCOPE \"", sizeof("#define FFI_SCOPE \"") - 1) == 0) {
+ if (*scope_name) {
+ if (preload) {
+ zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_SCOPE defined twice", filename);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_SCOPE defined twice", filename);
+ }
+ return NULL;
+ }
+ *scope_name = p = code_pos + sizeof("#define FFI_SCOPE \"") - 1;
+ while (1) {
+ if (*p == '\"') {
+ *p = 0;
+ p++;
+ break;
+ } else if (*p <= ' ') {
+ if (preload) {
+ zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
+ }
+ return NULL;
+ }
+ p++;
+ }
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+ while (*p == '\r' || *p == '\n') {
+ p++;
+ }
+ code_pos = p;
+ } else if (strncmp(code_pos, "#define FFI_LIB \"", sizeof("#define FFI_LIB \"") - 1) == 0) {
+ if (*lib) {
+ if (preload) {
+ zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_LIB defined twice", filename);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_LIB defined twice", filename);
+ }
+ return NULL;
+ }
+ *lib = p = code_pos + sizeof("#define FFI_LIB \"") - 1;
+ while (1) {
+ if (*p == '\"') {
+ *p = 0;
+ p++;
+ break;
+ } else if (*p <= ' ') {
+ if (preload) {
+ zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename);
+ } else {
+ zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename);
+ }
+ return NULL;
+ }
+ p++;
+ }
+ while (*p == ' ' || *p == '\t') {
+ p++;
+ }
+ while (*p == '\r' || *p == '\n') {
+ p++;
+ }
+ code_pos = p;
+ } else {
+ break;
+ }
+ }
+ return code_pos;
+}
+/* }}} */
+
+static ZEND_COLD zend_function *zend_fake_get_constructor(zend_object *object) /* {{{ */
+{
+ zend_throw_error(NULL, "Instantiation of '%s' is not allowed", ZSTR_VAL(object->ce->name));
+ return NULL;
+}
+/* }}} */
+
+static ZEND_COLD zend_never_inline void zend_bad_array_access(zend_class_entry *ce) /* {{{ */
+{
+ zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
+}
+/* }}} */
+
+static ZEND_COLD zval *zend_fake_read_dimension(zval *object, zval *offset, int type, zval *rv) /* {{{ */
+{
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_bad_array_access(ce);
+ return NULL;
+}
+/* }}} */
+
+static ZEND_COLD void zend_fake_write_dimension(zval *object, zval *offset, zval *value) /* {{{ */
+{
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_bad_array_access(ce);
+}
+/* }}} */
+
+static ZEND_COLD int zend_fake_has_dimension(zval *object, zval *offset, int check_empty) /* {{{ */
+{
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_bad_array_access(ce);
+ return 0;
+}
+/* }}} */
+
+static ZEND_COLD void zend_fake_unset_dimension(zval *object, zval *offset) /* {{{ */
+{
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_bad_array_access(ce);
+}
+/* }}} */
+
+static ZEND_COLD zend_never_inline void zend_bad_property_access(zend_class_entry *ce) /* {{{ */
+{
+ zend_throw_error(NULL, "Cannot access property of object of type %s", ZSTR_VAL(ce->name));
+}
+/* }}} */
+
+static ZEND_COLD zval *zend_fake_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */
+{
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_bad_property_access(ce);
+ return &EG(uninitialized_zval);
+}
+/* }}} */
+
+static ZEND_COLD zval *zend_fake_write_property(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
+{
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_bad_array_access(ce);
+ return value;
+}
+/* }}} */
+
+static ZEND_COLD int zend_fake_has_property(zval *object, zval *member, int has_set_exists, void **cache_slot) /* {{{ */
+{
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_bad_array_access(ce);
+ return 0;
+}
+/* }}} */
+
+static ZEND_COLD void zend_fake_unset_property(zval *object, zval *member, void **cache_slot) /* {{{ */
+{
+ zend_class_entry *ce = Z_OBJCE_P(object);
+ zend_bad_array_access(ce);
+}
+/* }}} */
+
+static zval *zend_fake_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) /* {{{ */
+{
+ return NULL;
+}
+/* }}} */
+
+static ZEND_COLD zend_function *zend_fake_get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key) /* {{{ */
+{
+ zend_class_entry *ce = (*obj_ptr)->ce;
+ zend_throw_error(NULL, "Object of type %s does not support method calls", ZSTR_VAL(ce->name));
+ return NULL;
+}
+/* }}} */
+
+static HashTable *zend_fake_get_properties(zval *object) /* {{{ */
+{
+ return (HashTable*)&zend_empty_array;
+}
+/* }}} */
+
+static HashTable *zend_fake_get_gc(zval *object, zval **table, int *n) /* {{{ */
+{
+ *table = NULL;
+ *n = 0;
+ return NULL;
+}
+/* }}} */
+
+static ZEND_COLD zend_never_inline void zend_ffi_use_after_free(void) /* {{{ */
+{
+ zend_throw_error(zend_ffi_exception_ce, "Use after free()");
+}
+/* }}} */
+
+static zend_object *zend_ffi_free_clone_obj(zval *zobject) /* {{{ */
+{
+ zend_ffi_use_after_free();
+ return NULL;
+}
+/* }}} */
+
+static ZEND_COLD zval *zend_ffi_free_read_dimension(zval *object, zval *offset, int type, zval *rv) /* {{{ */
+{
+ zend_ffi_use_after_free();
+ return NULL;
+}
+/* }}} */
+
+static ZEND_COLD void zend_ffi_free_write_dimension(zval *object, zval *offset, zval *value) /* {{{ */
+{
+ zend_ffi_use_after_free();
+}
+/* }}} */
+
+static ZEND_COLD int zend_ffi_free_has_dimension(zval *object, zval *offset, int check_empty) /* {{{ */
+{
+ zend_ffi_use_after_free();
+ return 0;
+}
+/* }}} */
+
+static ZEND_COLD void zend_ffi_free_unset_dimension(zval *object, zval *offset) /* {{{ */
+{
+ zend_ffi_use_after_free();
+}
+/* }}} */
+
+static ZEND_COLD zval *zend_ffi_free_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */
+{
+ zend_ffi_use_after_free();
+ return &EG(uninitialized_zval);
+}
+/* }}} */
+
+static ZEND_COLD zval *zend_ffi_free_write_property(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
+{
+ zend_ffi_use_after_free();
+ return value;
+}
+/* }}} */
+
+static ZEND_COLD int zend_ffi_free_has_property(zval *object, zval *member, int has_set_exists, void **cache_slot) /* {{{ */
+{
+ zend_ffi_use_after_free();
+ return 0;
+}
+/* }}} */
+
+static ZEND_COLD void zend_ffi_free_unset_property(zval *object, zval *member, void **cache_slot) /* {{{ */
+{
+ zend_ffi_use_after_free();
+}
+/* }}} */
+
+static zval* zend_ffi_free_get(zval *object, zval *rv) /* {{{ */
+{
+ zend_ffi_use_after_free();
+ return NULL;
+}
+/* }}} */
+
+static HashTable *zend_ffi_free_get_debug_info(zval *object, int *is_temp) /* {{{ */
+{
+ zend_ffi_use_after_free();
+ return NULL;
+}
+/* }}} */
+
+static ZEND_INI_MH(OnUpdateFFIEnable) /* {{{ */
+{
+ if (zend_string_equals_literal_ci(new_value, "preload")) {
+ FFI_G(restriction) = ZEND_FFI_PRELOAD;
+ } else {
+ FFI_G(restriction) = (zend_ffi_api_restriction)zend_ini_parse_bool(new_value);
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+static ZEND_INI_DISP(zend_ffi_enable_displayer_cb) /* {{{ */
+{
+ if (FFI_G(restriction) == ZEND_FFI_PRELOAD) {
+ ZEND_PUTS("preload");
+ } else if (FFI_G(restriction) == ZEND_FFI_ENABLED) {
+ ZEND_PUTS("On");
+ } else {
+ ZEND_PUTS("Off");
+ }
+}
+/* }}} */
+
+ZEND_INI_BEGIN()
+ ZEND_INI_ENTRY3_EX("ffi.enable", "preload", ZEND_INI_SYSTEM, OnUpdateFFIEnable, NULL, NULL, NULL, zend_ffi_enable_displayer_cb)
+ZEND_INI_END()
+
+/* {{{ ZEND_MINIT_FUNCTION
+ */
+ZEND_MINIT_FUNCTION(ffi)
+{
+ zend_class_entry ce;
+
+ REGISTER_INI_ENTRIES();
+
+ FFI_G(is_cli) = strcmp(sapi_module.name, "cli") == 0;
+
+ INIT_NS_CLASS_ENTRY(ce, "FFI", "Exception", NULL);
+ zend_ffi_exception_ce = zend_register_internal_class_ex(&ce, zend_ce_error);
+
+ INIT_NS_CLASS_ENTRY(ce, "FFI", "ParserException", NULL);
+ zend_ffi_parser_exception_ce = zend_register_internal_class_ex(&ce, zend_ffi_exception_ce);
+ zend_ffi_parser_exception_ce->ce_flags |= ZEND_ACC_FINAL;
+
+ INIT_CLASS_ENTRY(ce, "FFI", zend_ffi_functions);
+ zend_ffi_ce = zend_register_internal_class(&ce);
+ zend_ffi_ce->ce_flags |= ZEND_ACC_FINAL;
+ zend_ffi_ce->create_object = zend_ffi_new;
+ zend_ffi_ce->serialize = zend_class_serialize_deny;
+ zend_ffi_ce->unserialize = zend_class_unserialize_deny;
+
+ memcpy(&zend_ffi_new_fn, zend_hash_str_find_ptr(&zend_ffi_ce->function_table, "new", sizeof("new")-1), sizeof(zend_internal_function));
+ zend_ffi_new_fn.fn_flags &= ~ZEND_ACC_STATIC;
+ memcpy(&zend_ffi_cast_fn, zend_hash_str_find_ptr(&zend_ffi_ce->function_table, "cast", sizeof("cast")-1), sizeof(zend_internal_function));
+ zend_ffi_cast_fn.fn_flags &= ~ZEND_ACC_STATIC;
+ memcpy(&zend_ffi_type_fn, zend_hash_str_find_ptr(&zend_ffi_ce->function_table, "type", sizeof("type")-1), sizeof(zend_internal_function));
+ zend_ffi_type_fn.fn_flags &= ~ZEND_ACC_STATIC;
+
+ memcpy(&zend_ffi_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ zend_ffi_handlers.get_constructor = zend_fake_get_constructor;
+ zend_ffi_handlers.free_obj = zend_ffi_free_obj;
+ zend_ffi_handlers.clone_obj = NULL;
+ zend_ffi_handlers.read_property = zend_ffi_read_var;
+ zend_ffi_handlers.write_property = zend_ffi_write_var;
+ zend_ffi_handlers.read_dimension = zend_fake_read_dimension;
+ zend_ffi_handlers.write_dimension = zend_fake_write_dimension;
+ zend_ffi_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
+ zend_ffi_handlers.has_property = zend_fake_has_property;
+ zend_ffi_handlers.unset_property = zend_fake_unset_property;
+ zend_ffi_handlers.has_dimension = zend_fake_has_dimension;
+ zend_ffi_handlers.unset_dimension = zend_fake_unset_dimension;
+ zend_ffi_handlers.get_method = zend_ffi_get_func;
+ zend_ffi_handlers.compare_objects = NULL;
+ zend_ffi_handlers.cast_object = NULL;
+ zend_ffi_handlers.get_debug_info = NULL;
+ zend_ffi_handlers.get_closure = NULL;
+ zend_ffi_handlers.get_properties = zend_fake_get_properties;
+ zend_ffi_handlers.get_gc = zend_fake_get_gc;
+
+ INIT_NS_CLASS_ENTRY(ce, "FFI", "CData", NULL);
+ zend_ffi_cdata_ce = zend_register_internal_class(&ce);
+ zend_ffi_cdata_ce->ce_flags |= ZEND_ACC_FINAL;
+ zend_ffi_cdata_ce->create_object = zend_ffi_cdata_new;
+ zend_ffi_cdata_ce->get_iterator = zend_ffi_cdata_get_iterator;
+ zend_ffi_cdata_ce->serialize = zend_class_serialize_deny;
+ zend_ffi_cdata_ce->unserialize = zend_class_unserialize_deny;
+
+ memcpy(&zend_ffi_cdata_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ zend_ffi_cdata_handlers.get_constructor = zend_fake_get_constructor;
+ zend_ffi_cdata_handlers.free_obj = zend_ffi_cdata_free_obj;
+ zend_ffi_cdata_handlers.clone_obj = zend_ffi_cdata_clone_obj;
+ zend_ffi_cdata_handlers.read_property = zend_ffi_cdata_read_field;
+ zend_ffi_cdata_handlers.write_property = zend_ffi_cdata_write_field;
+ zend_ffi_cdata_handlers.read_dimension = zend_ffi_cdata_read_dim;
+ zend_ffi_cdata_handlers.write_dimension = zend_ffi_cdata_write_dim;
+ zend_ffi_cdata_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
+ zend_ffi_cdata_handlers.has_property = zend_fake_has_property;
+ zend_ffi_cdata_handlers.unset_property = zend_fake_unset_property;
+ zend_ffi_cdata_handlers.has_dimension = zend_fake_has_dimension;
+ zend_ffi_cdata_handlers.unset_dimension = zend_fake_unset_dimension;
+ zend_ffi_cdata_handlers.get_method = zend_fake_get_method;
+ zend_ffi_cdata_handlers.get_class_name = zend_ffi_cdata_get_class_name;
+ zend_ffi_cdata_handlers.do_operation = zend_ffi_cdata_do_operation;
+ zend_ffi_cdata_handlers.compare_objects = zend_ffi_cdata_compare_objects;
+ zend_ffi_cdata_handlers.cast_object = zend_ffi_cdata_cast_object;
+ zend_ffi_cdata_handlers.count_elements = zend_ffi_cdata_count_elements;
+ zend_ffi_cdata_handlers.get_debug_info = zend_ffi_cdata_get_debug_info;
+ zend_ffi_cdata_handlers.get_closure = zend_ffi_cdata_get_closure;
+ zend_ffi_cdata_handlers.get_properties = zend_fake_get_properties;
+ zend_ffi_cdata_handlers.get_gc = zend_fake_get_gc;
+
+ memcpy(&zend_ffi_cdata_value_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ zend_ffi_cdata_value_handlers.get_constructor = zend_fake_get_constructor;
+ zend_ffi_cdata_value_handlers.free_obj = zend_ffi_cdata_free_obj;
+ zend_ffi_cdata_value_handlers.clone_obj = zend_ffi_cdata_clone_obj;
+ zend_ffi_cdata_value_handlers.read_property = zend_fake_read_property;
+ zend_ffi_cdata_value_handlers.write_property = zend_fake_write_property;
+ zend_ffi_cdata_value_handlers.read_dimension = zend_fake_read_dimension;
+ zend_ffi_cdata_value_handlers.write_dimension = zend_fake_write_dimension;
+ zend_ffi_cdata_value_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
+ zend_ffi_cdata_value_handlers.get = zend_ffi_cdata_get;
+ zend_ffi_cdata_value_handlers.set = zend_ffi_cdata_set;
+ zend_ffi_cdata_value_handlers.has_property = zend_fake_has_property;
+ zend_ffi_cdata_value_handlers.unset_property = zend_fake_unset_property;
+ zend_ffi_cdata_value_handlers.has_dimension = zend_fake_has_dimension;
+ zend_ffi_cdata_value_handlers.unset_dimension = zend_fake_unset_dimension;
+ zend_ffi_cdata_value_handlers.get_method = zend_fake_get_method;
+ zend_ffi_cdata_value_handlers.get_class_name = zend_ffi_cdata_get_class_name;
+ zend_ffi_cdata_value_handlers.compare_objects = zend_ffi_cdata_compare_objects;
+ zend_ffi_cdata_value_handlers.cast_object = NULL;
+ zend_ffi_cdata_value_handlers.count_elements = NULL;
+ zend_ffi_cdata_value_handlers.get_debug_info = zend_ffi_cdata_get_debug_info;
+ zend_ffi_cdata_value_handlers.get_closure = NULL;
+ zend_ffi_cdata_value_handlers.get_properties = zend_fake_get_properties;
+ zend_ffi_cdata_value_handlers.get_gc = zend_fake_get_gc;
+
+ memcpy(&zend_ffi_cdata_free_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ zend_ffi_cdata_free_handlers.get_constructor = zend_fake_get_constructor;
+ zend_ffi_cdata_free_handlers.free_obj = zend_ffi_cdata_free_obj;
+ zend_ffi_cdata_free_handlers.clone_obj = zend_ffi_free_clone_obj;
+ zend_ffi_cdata_free_handlers.read_property = zend_ffi_free_read_property;
+ zend_ffi_cdata_free_handlers.write_property = zend_ffi_free_write_property;
+ zend_ffi_cdata_free_handlers.read_dimension = zend_ffi_free_read_dimension;
+ zend_ffi_cdata_free_handlers.write_dimension = zend_ffi_free_write_dimension;
+ zend_ffi_cdata_free_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
+ zend_ffi_cdata_free_handlers.get = zend_ffi_free_get;
+ zend_ffi_cdata_free_handlers.set = NULL;
+ zend_ffi_cdata_free_handlers.has_property = zend_ffi_free_has_property;
+ zend_ffi_cdata_free_handlers.unset_property = zend_ffi_free_unset_property;
+ zend_ffi_cdata_free_handlers.has_dimension = zend_ffi_free_has_dimension;
+ zend_ffi_cdata_free_handlers.unset_dimension = zend_ffi_free_unset_dimension;
+ zend_ffi_cdata_free_handlers.get_method = zend_fake_get_method;
+ zend_ffi_cdata_free_handlers.get_class_name = zend_ffi_cdata_get_class_name;
+ zend_ffi_cdata_free_handlers.compare_objects = zend_ffi_cdata_compare_objects;
+ zend_ffi_cdata_free_handlers.cast_object = NULL;
+ zend_ffi_cdata_free_handlers.count_elements = NULL;
+ zend_ffi_cdata_free_handlers.get_debug_info = zend_ffi_free_get_debug_info;
+ zend_ffi_cdata_free_handlers.get_closure = NULL;
+ zend_ffi_cdata_free_handlers.get_properties = zend_fake_get_properties;
+ zend_ffi_cdata_free_handlers.get_gc = zend_fake_get_gc;
+
+ INIT_NS_CLASS_ENTRY(ce, "FFI", "CType", NULL);
+ zend_ffi_ctype_ce = zend_register_internal_class(&ce);
+ zend_ffi_ctype_ce->ce_flags |= ZEND_ACC_FINAL;
+ zend_ffi_ctype_ce->create_object = zend_ffi_ctype_new;
+ zend_ffi_ctype_ce->serialize = zend_class_serialize_deny;
+ zend_ffi_ctype_ce->unserialize = zend_class_unserialize_deny;
+
+ memcpy(&zend_ffi_ctype_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ zend_ffi_ctype_handlers.get_constructor = zend_fake_get_constructor;
+ zend_ffi_ctype_handlers.free_obj = zend_ffi_ctype_free_obj;
+ zend_ffi_ctype_handlers.clone_obj = NULL;
+ zend_ffi_ctype_handlers.read_property = zend_fake_read_property;
+ zend_ffi_ctype_handlers.write_property = zend_fake_write_property;
+ zend_ffi_ctype_handlers.read_dimension = zend_fake_read_dimension;
+ zend_ffi_ctype_handlers.write_dimension = zend_fake_write_dimension;
+ zend_ffi_ctype_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
+ zend_ffi_ctype_handlers.has_property = zend_fake_has_property;
+ zend_ffi_ctype_handlers.unset_property = zend_fake_unset_property;
+ zend_ffi_ctype_handlers.has_dimension = zend_fake_has_dimension;
+ zend_ffi_ctype_handlers.unset_dimension = zend_fake_unset_dimension;
+ zend_ffi_ctype_handlers.get_method = zend_fake_get_method;
+ zend_ffi_ctype_handlers.get_class_name = zend_ffi_ctype_get_class_name;
+ zend_ffi_ctype_handlers.compare_objects = zend_ffi_ctype_compare_objects;
+ zend_ffi_ctype_handlers.cast_object = NULL;
+ zend_ffi_ctype_handlers.count_elements = NULL;
+ zend_ffi_ctype_handlers.get_debug_info = zend_ffi_ctype_get_debug_info;
+ zend_ffi_ctype_handlers.get_closure = NULL;
+ zend_ffi_ctype_handlers.get_properties = zend_fake_get_properties;
+ zend_ffi_ctype_handlers.get_gc = zend_fake_get_gc;
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ ZEND_RSHUTDOWN_FUNCTION
+ */
+ZEND_RSHUTDOWN_FUNCTION(ffi)
+{
+ if (FFI_G(callbacks)) {
+ zend_hash_destroy(FFI_G(callbacks));
+ efree(FFI_G(callbacks));
+ FFI_G(callbacks) = NULL;
+ }
+ if (FFI_G(weak_types)) {
+#if 0
+ fprintf(stderr, "WeakTypes: %d\n", zend_hash_num_elements(FFI_G(weak_types)));
+#endif
+ zend_hash_destroy(FFI_G(weak_types));
+ efree(FFI_G(weak_types));
+ FFI_G(weak_types) = NULL;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ ZEND_MINFO_FUNCTION
+ */
+ZEND_MINFO_FUNCTION(ffi)
+{
+ php_info_print_table_start();
+ php_info_print_table_header(2, "FFI support", "enabled");
+ php_info_print_table_end();
+}
+/* }}} */
+
+#define ZEND_FFI_VERSION "0.1.0"
+
+static const zend_ffi_type zend_ffi_type_void = {.kind=ZEND_FFI_TYPE_VOID, .size=1, .align=1};
+static const zend_ffi_type zend_ffi_type_char = {.kind=ZEND_FFI_TYPE_CHAR, .size=1, .align=_Alignof(char)};
+static const zend_ffi_type zend_ffi_type_bool = {.kind=ZEND_FFI_TYPE_BOOL, .size=1, .align=_Alignof(uint8_t)};
+static const zend_ffi_type zend_ffi_type_sint8 = {.kind=ZEND_FFI_TYPE_SINT8, .size=1, .align=_Alignof(int8_t)};
+static const zend_ffi_type zend_ffi_type_uint8 = {.kind=ZEND_FFI_TYPE_UINT8, .size=1, .align=_Alignof(uint8_t)};
+static const zend_ffi_type zend_ffi_type_sint16 = {.kind=ZEND_FFI_TYPE_SINT16, .size=2, .align=_Alignof(int16_t)};
+static const zend_ffi_type zend_ffi_type_uint16 = {.kind=ZEND_FFI_TYPE_UINT16, .size=2, .align=_Alignof(uint16_t)};
+static const zend_ffi_type zend_ffi_type_sint32 = {.kind=ZEND_FFI_TYPE_SINT32, .size=4, .align=_Alignof(int32_t)};
+static const zend_ffi_type zend_ffi_type_uint32 = {.kind=ZEND_FFI_TYPE_UINT32, .size=4, .align=_Alignof(uint32_t)};
+static const zend_ffi_type zend_ffi_type_sint64 = {.kind=ZEND_FFI_TYPE_SINT64, .size=8, .align=_Alignof(int64_t)};
+static const zend_ffi_type zend_ffi_type_uint64 = {.kind=ZEND_FFI_TYPE_UINT64, .size=8, .align=_Alignof(uint64_t)};
+static const zend_ffi_type zend_ffi_type_float = {.kind=ZEND_FFI_TYPE_FLOAT, .size=sizeof(float), .align=_Alignof(float)};
+static const zend_ffi_type zend_ffi_type_double = {.kind=ZEND_FFI_TYPE_DOUBLE, .size=sizeof(double), .align=_Alignof(double)};
+
+#ifdef HAVE_LONG_DOUBLE
+static const zend_ffi_type zend_ffi_type_long_double = {.kind=ZEND_FFI_TYPE_LONGDOUBLE, .size=sizeof(long double), .align=_Alignof(long double)};
+#endif
+
+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};
+
+const struct {
+ const char *name;
+ const zend_ffi_type *type;
+} zend_ffi_types[] = {
+ {"void", &zend_ffi_type_void},
+ {"char", &zend_ffi_type_char},
+ {"bool", &zend_ffi_type_bool},
+ {"int8_t", &zend_ffi_type_sint8},
+ {"uint8_t", &zend_ffi_type_uint8},
+ {"int16_t", &zend_ffi_type_sint16},
+ {"uint16_t", &zend_ffi_type_uint16},
+ {"int32_t", &zend_ffi_type_sint32},
+ {"uint32_t", &zend_ffi_type_uint32},
+ {"int64_t", &zend_ffi_type_sint64},
+ {"uint64_t", &zend_ffi_type_uint64},
+ {"float", &zend_ffi_type_float},
+ {"double", &zend_ffi_type_double},
+#ifdef HAVE_LONG_DOUBLE
+ {"long double", &zend_ffi_type_long_double},
+#endif
+#if SIZEOF_SIZE_T == 4
+ {"uintptr_t", &zend_ffi_type_uint32},
+ {"intptr_t", &zend_ffi_type_sint32},
+ {"size_t", &zend_ffi_type_uint32},
+ {"ssize_t", &zend_ffi_type_sint32},
+ {"ptrdiff_t", &zend_ffi_type_sint32},
+#else
+ {"uintptr_t", &zend_ffi_type_uint64},
+ {"intptr_t", &zend_ffi_type_sint64},
+ {"size_t", &zend_ffi_type_uint64},
+ {"ssize_t", &zend_ffi_type_sint64},
+ {"ptrdiff_t", &zend_ffi_type_sint64},
+#endif
+#if SIZEOF_OFF_T == 4
+ {"off_t", &zend_ffi_type_sint32},
+#else
+ {"off_t", &zend_ffi_type_sint64},
+#endif
+
+ {"va_list", &zend_ffi_type_ptr},
+ {"__builtin_va_list", &zend_ffi_type_ptr},
+ {"__gnuc_va_list", &zend_ffi_type_ptr},
+};
+
+/* {{{ ZEND_GINIT_FUNCTION
+ */
+static ZEND_GINIT_FUNCTION(ffi)
+{
+ size_t i;
+
+#if defined(COMPILE_DL_FFI) && defined(ZTS)
+ ZEND_TSRMLS_CACHE_UPDATE();
+#endif
+ memset(ffi_globals, 0, sizeof(*ffi_globals));
+ zend_hash_init(&ffi_globals->types, 0, NULL, NULL, 1);
+ for (i = 0; i < sizeof(zend_ffi_types)/sizeof(zend_ffi_types[0]); i++) {
+ 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);
+ }
+}
+/* }}} */
+
+/* {{{ ZEND_GINIT_FUNCTION
+ */
+static ZEND_GSHUTDOWN_FUNCTION(ffi)
+{
+ if (ffi_globals->scopes) {
+ zend_hash_destroy(ffi_globals->scopes);
+ free(ffi_globals->scopes);
+ }
+ zend_hash_destroy(&ffi_globals->types);
+}
+/* }}} */
+
+/* {{{ ffi_module_entry
+ */
+zend_module_entry ffi_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "FFI", /* Extension name */
+ NULL, /* zend_function_entry */
+ ZEND_MINIT(ffi), /* ZEND_MINIT - Module initialization */
+ NULL, /* ZEND_MSHUTDOWN - Module shutdown */
+ NULL, /* ZEND_RINIT - Request initialization */
+ ZEND_RSHUTDOWN(ffi), /* ZEND_RSHUTDOWN - Request shutdown */
+ ZEND_MINFO(ffi), /* ZEND_MINFO - Module info */
+ ZEND_FFI_VERSION, /* Version */
+ ZEND_MODULE_GLOBALS(ffi),
+ ZEND_GINIT(ffi),
+ ZEND_GSHUTDOWN(ffi),
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
+};
+/* }}} */
+
+#ifdef COMPILE_DL_FFI
+# ifdef ZTS
+ZEND_TSRMLS_CACHE_DEFINE()
+# endif
+ZEND_GET_MODULE(ffi)
+#endif
+
+/* parser callbacks */
+void zend_ffi_parser_error(const char *format, ...) /* {{{ */
+{
+ va_list va;
+ char *message = NULL;
+
+ va_start(va, format);
+ zend_vspprintf(&message, 0, format, va);
+
+ if (EG(current_execute_data)) {
+ zend_throw_exception(zend_ffi_parser_exception_ce, message, 0);
+ } else {
+ zend_error(E_WARNING, "FFI Parser: %s", message);
+ }
+
+ efree(message);
+ va_end(va);
+
+ LONGJMP(FFI_G(bailout), FAILURE);
+}
+/* }}} */
+
+static void zend_ffi_finalize_type(zend_ffi_dcl *dcl) /* {{{ */
+{
+ if (!dcl->type) {
+ switch (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) {
+ case ZEND_FFI_DCL_VOID:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_void;
+ break;
+ case ZEND_FFI_DCL_CHAR:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_char;
+ break;
+ case ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SIGNED:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_sint8;
+ break;
+ case ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_UNSIGNED:
+ case ZEND_FFI_DCL_BOOL:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_uint8;
+ break;
+ case ZEND_FFI_DCL_SHORT:
+ case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_SIGNED:
+ case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT:
+ case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_sint16;
+ break;
+ case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_UNSIGNED:
+ case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_uint16;
+ break;
+ case ZEND_FFI_DCL_INT:
+ case ZEND_FFI_DCL_SIGNED:
+ case ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_sint32;
+ break;
+ case ZEND_FFI_DCL_UNSIGNED:
+ case ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_uint32;
+ break;
+ case ZEND_FFI_DCL_LONG:
+ case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED:
+ case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_INT:
+ case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
+ if (sizeof(long) == 4) {
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_sint32;
+ } else {
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_sint64;
+ }
+ break;
+ case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED:
+ case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
+ if (sizeof(long) == 4) {
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_uint32;
+ } else {
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_uint64;
+ }
+ break;
+ case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG:
+ case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED:
+ case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_INT:
+ case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_sint64;
+ break;
+ case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED:
+ case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_uint64;
+ break;
+ case ZEND_FFI_DCL_FLOAT:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_float;
+ break;
+ case ZEND_FFI_DCL_DOUBLE:
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_double;
+ break;
+ case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_DOUBLE:
+#ifdef _WIN32
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_double;
+#else
+ dcl->type = (zend_ffi_type*)&zend_ffi_type_long_double;
+#endif
+ break;
+ case ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_COMPLEX:
+ case ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_COMPLEX:
+ case ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_COMPLEX:
+ zend_ffi_parser_error("unsupported type '_Complex' at line %d", FFI_G(line));
+ break;
+ default:
+ zend_ffi_parser_error("unsupported type specifier combination at line %d", FFI_G(line));
+ break;
+ }
+ dcl->flags &= ~ZEND_FFI_DCL_TYPE_SPECIFIERS;
+ dcl->flags |= ZEND_FFI_DCL_TYPEDEF_NAME;
+ }
+}
+/* }}} */
+
+int zend_ffi_is_typedef_name(const char *name, size_t name_len) /* {{{ */
+{
+ zend_ffi_symbol *sym;
+ zend_ffi_type *type;
+
+ if (FFI_G(symbols)) {
+ sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
+ if (sym) {
+ return (sym->kind == ZEND_FFI_SYM_TYPE);
+ }
+ }
+ type = zend_hash_str_find_ptr(&FFI_G(types), name, name_len);
+ if (type) {
+ return 1;
+ }
+ return 0;
+}
+/* }}} */
+
+void zend_ffi_resolve_typedef(const char *name, size_t name_len, zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_symbol *sym;
+ zend_ffi_type *type;
+
+ if (FFI_G(symbols)) {
+ sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
+ if (sym && sym->kind == ZEND_FFI_SYM_TYPE) {
+ dcl->type = ZEND_FFI_TYPE(sym->type);;
+ if (sym->is_const) {
+ dcl->attr |= ZEND_FFI_ATTR_CONST;
+ }
+ return;
+ }
+ }
+ type = zend_hash_str_find_ptr(&FFI_G(types), name, name_len);
+ if (type) {
+ dcl->type = type;
+ return;
+ }
+ zend_ffi_parser_error("undefined C type '%.*s' at line %d", name_len, name, FFI_G(line));
+}
+/* }}} */
+
+void zend_ffi_resolve_const(const char *name, size_t name_len, zend_ffi_val *val) /* {{{ */
+{
+ zend_ffi_symbol *sym;
+
+ if (FFI_G(symbols)) {
+ sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
+ if (sym && sym->kind == ZEND_FFI_SYM_CONST) {
+ val->i64 = sym->value;
+ switch (sym->type->kind) {
+ case ZEND_FFI_TYPE_SINT8:
+ case ZEND_FFI_TYPE_SINT16:
+ case ZEND_FFI_TYPE_SINT32:
+ val->kind = ZEND_FFI_VAL_INT32;
+ break;
+ case ZEND_FFI_TYPE_SINT64:
+ val->kind = ZEND_FFI_VAL_INT64;
+ break;
+ case ZEND_FFI_TYPE_UINT8:
+ case ZEND_FFI_TYPE_UINT16:
+ case ZEND_FFI_TYPE_UINT32:
+ val->kind = ZEND_FFI_VAL_UINT32;
+ break;
+ case ZEND_FFI_TYPE_UINT64:
+ val->kind = ZEND_FFI_VAL_UINT64;
+ break;
+ default:
+ ZEND_ASSERT(0);
+ }
+ return;
+ }
+ }
+ val->kind = ZEND_FFI_VAL_ERROR;
+}
+/* }}} */
+
+void zend_ffi_make_enum_type(zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
+ type->kind = ZEND_FFI_TYPE_ENUM;
+ type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_ENUM_ATTRS);
+ if (type->attr & ZEND_FFI_ATTR_PACKED) {
+ type->size = zend_ffi_type_uint8.size;
+ type->align = zend_ffi_type_uint8.align;
+ type->enumeration.kind = ZEND_FFI_TYPE_UINT8;
+ } else {
+ type->size = zend_ffi_type_uint32.size;
+ type->align = zend_ffi_type_uint32.align;
+ type->enumeration.kind = ZEND_FFI_TYPE_UINT32;
+ }
+ dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
+ dcl->attr &= ~ZEND_FFI_ENUM_ATTRS;
+}
+/* }}} */
+
+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) /* {{{ */
+{
+ zend_ffi_symbol *sym;
+ const zend_ffi_type *sym_type;
+ int64_t value;
+ zend_ffi_type *enum_type = ZEND_FFI_TYPE(enum_dcl->type);
+ zend_bool overflow = 0;
+ zend_bool is_signed =
+ (enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT8 ||
+ enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT16 ||
+ enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT32 ||
+ enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT64);
+
+ ZEND_ASSERT(enum_type && enum_type->kind == ZEND_FFI_TYPE_ENUM);
+ if (val->kind == ZEND_FFI_VAL_EMPTY) {
+ if (is_signed) {
+ if (*last == 0x7FFFFFFFFFFFFFFFLL) {
+ overflow = 1;
+ }
+ } else {
+ if ((*min != 0 || *max != 0)
+ && (uint64_t)*last == 0xFFFFFFFFFFFFFFFFULL) {
+ overflow = 1;
+ }
+ }
+ value = *last + 1;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ if (!is_signed && val->ch < 0) {
+ if ((uint64_t)*max > 0x7FFFFFFFFFFFFFFFULL) {
+ overflow = 1;
+ } else {
+ is_signed = 1;
+ }
+ }
+ value = val->ch;
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
+ if (!is_signed && val->i64 < 0) {
+ if ((uint64_t)*max > 0x7FFFFFFFFFFFFFFFULL) {
+ overflow = 1;
+ } else {
+ is_signed = 1;
+ }
+ }
+ value = val->i64;
+ } else if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
+ if (is_signed && val->u64 > 0x7FFFFFFFFFFFFFFFULL) {
+ overflow = 1;
+ }
+ value = val->u64;
+ } else {
+ zend_ffi_parser_error("enumerator value '%.*s' must be an integer at line %d", name_len, name, FFI_G(line));
+ return;
+ }
+
+ if (overflow) {
+ zend_ffi_parser_error("overflow in enumeration values '%.*s' at line %d", name_len, name, FFI_G(line));
+ return;
+ }
+
+ if (is_signed) {
+ *min = MIN(*min, value);
+ *max = MAX(*max, value);
+ if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
+ && *min >= -0x7FLL-1 && *max <= 0x7FLL) {
+ sym_type = &zend_ffi_type_sint8;
+ } else if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
+ && *min >= -0x7FFFLL-1 && *max <= 0x7FFFLL) {
+ sym_type = &zend_ffi_type_sint16;
+ } else if (*min >= -0x7FFFFFFFLL-1 && *max <= 0x7FFFFFFFLL) {
+ sym_type = &zend_ffi_type_sint32;
+ } else {
+ sym_type = &zend_ffi_type_sint64;
+ }
+ } else {
+ *min = MIN((uint64_t)*min, (uint64_t)value);
+ *max = MAX((uint64_t)*max, (uint64_t)value);
+ if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
+ && (uint64_t)*max <= 0xFFULL) {
+ sym_type = &zend_ffi_type_uint8;
+ } else if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
+ && (uint64_t)*max <= 0xFFFFULL) {
+ sym_type = &zend_ffi_type_uint16;
+ } else if ((uint64_t)*max <= 0xFFFFFFFFULL) {
+ sym_type = &zend_ffi_type_uint32;
+ } else {
+ sym_type = &zend_ffi_type_uint64;
+ }
+ }
+ enum_type->enumeration.kind = sym_type->kind;
+ enum_type->size = sym_type->size;
+ enum_type->align = sym_type->align;
+ *last = value;
+
+ if (!FFI_G(symbols)) {
+ FFI_G(symbols) = pemalloc(sizeof(HashTable), FFI_G(persistent));
+ 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));
+ }
+ sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
+ if (sym) {
+ zend_ffi_parser_error("redeclaration of '%.*s' at line %d", name_len, name, FFI_G(line));
+ } else {
+ sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
+ sym->kind = ZEND_FFI_SYM_CONST;
+ sym->type = (zend_ffi_type*)sym_type;
+ sym->value = value;
+ zend_hash_str_add_new_ptr(FFI_G(symbols), name, name_len, sym);
+ }
+}
+/* }}} */
+
+void zend_ffi_make_struct_type(zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
+ type->kind = ZEND_FFI_TYPE_STRUCT;
+ type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_STRUCT_ATTRS);
+ type->size = 0;
+ type->align = dcl->align > 1 ? dcl->align : 1;
+ if (dcl->flags & ZEND_FFI_DCL_UNION) {
+ type->attr |= ZEND_FFI_ATTR_UNION;
+ }
+ dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
+ 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));
+ dcl->attr &= ~ZEND_FFI_STRUCT_ATTRS;
+ dcl->align = 0;
+}
+/* }}} */
+
+static int zend_ffi_validate_prev_field_type(zend_ffi_type *struct_type) /* {{{ */
+{
+ if (zend_hash_num_elements(&struct_type->record.fields) > 0) {
+ zend_ffi_field *field = NULL;
+
+ ZEND_HASH_REVERSE_FOREACH_PTR(&struct_type->record.fields, field) {
+ break;
+ } ZEND_HASH_FOREACH_END();
+ if (ZEND_FFI_TYPE(field->type)->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY) {
+ zend_ffi_throw_parser_error("flexible array member not at end of struct at line %d", FFI_G(line));
+ return FAILURE;
+ }
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+static int zend_ffi_validate_field_type(zend_ffi_type *type, zend_ffi_type *struct_type) /* {{{ */
+{
+ if (type == struct_type) {
+ zend_ffi_throw_parser_error("struct/union can't contain an instance of itself at line %d", FFI_G(line));
+ return FAILURE;
+ } else if (zend_ffi_validate_var_type(type, 1) != SUCCESS) {
+ return FAILURE;
+ } else if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
+ if (type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY) {
+ zend_ffi_throw_parser_error("flexible array member in union at line %d", FFI_G(line));
+ return FAILURE;
+ }
+ }
+ return zend_ffi_validate_prev_field_type(struct_type);
+}
+/* }}} */
+
+void zend_ffi_add_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl) /* {{{ */
+{
+ zend_ffi_field *field;
+ zend_ffi_type *struct_type = ZEND_FFI_TYPE(struct_dcl->type);
+ zend_ffi_type *field_type;
+
+ ZEND_ASSERT(struct_type && struct_type->kind == ZEND_FFI_TYPE_STRUCT);
+ zend_ffi_finalize_type(field_dcl);
+ field_type = ZEND_FFI_TYPE(field_dcl->type);
+ if (zend_ffi_validate_field_type(field_type, struct_type) != SUCCESS) {
+ zend_ffi_cleanup_dcl(field_dcl);
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+
+ field = pemalloc(sizeof(zend_ffi_field), FFI_G(persistent));
+ if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
+ struct_type->align = MAX(struct_type->align, MAX(field_type->align, field_dcl->align));
+ }
+ if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
+ field->offset = 0;
+ struct_type->size = MAX(struct_type->size, field_type->size);
+ } else {
+ if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
+ uint32_t field_align = MAX(field_type->align, field_dcl->align);
+ struct_type->size = ((struct_type->size + (field_align - 1)) / field_align) * field_align;
+ }
+ field->offset = struct_type->size;
+ struct_type->size += field_type->size;
+ }
+ field->type = field_dcl->type;
+ field->is_const = (zend_bool)(field_dcl->attr & ZEND_FFI_ATTR_CONST);
+ field->is_nested = 0;
+ field->first_bit = 0;
+ field->bits = 0;
+ field_dcl->type = field_type; /* reset "owned" flag */
+
+ if (!zend_hash_str_add_ptr(&struct_type->record.fields, name, name_len, field)) {
+ zend_ffi_type_dtor(field->type);
+ pefree(field, FFI_G(persistent));
+ zend_ffi_parser_error("duplicate field name '%.*s' at line %d", name_len, name, FFI_G(line));
+ }
+}
+/* }}} */
+
+void zend_ffi_add_anonymous_field(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl) /* {{{ */
+{
+ zend_ffi_type *struct_type = ZEND_FFI_TYPE(struct_dcl->type);
+ zend_ffi_type *field_type;
+ zend_ffi_field *field;
+ zend_string *key;
+
+ ZEND_ASSERT(struct_type && struct_type->kind == ZEND_FFI_TYPE_STRUCT);
+ zend_ffi_finalize_type(field_dcl);
+ field_type = ZEND_FFI_TYPE(field_dcl->type);
+ if (field_type->kind != ZEND_FFI_TYPE_STRUCT) {
+ zend_ffi_cleanup_dcl(field_dcl);
+ zend_ffi_parser_error("declaration does not declare anything at line %d", FFI_G(line));
+ return;
+ }
+
+ if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
+ struct_type->align = MAX(struct_type->align, MAX(field_type->align, field_dcl->align));
+ }
+ if (!(struct_type->attr & ZEND_FFI_ATTR_UNION)) {
+ if (zend_ffi_validate_prev_field_type(struct_type) != SUCCESS) {
+ zend_ffi_cleanup_dcl(field_dcl);
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+ if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
+ uint32_t field_align = MAX(field_type->align, field_dcl->align);
+ struct_type->size = ((struct_type->size + (field_align - 1)) / field_align) * field_align;
+ }
+ }
+
+ ZEND_HASH_FOREACH_STR_KEY_PTR(&field_type->record.fields, key, field) {
+ zend_ffi_field *new_field = pemalloc(sizeof(zend_ffi_field), FFI_G(persistent));
+
+ if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
+ new_field->offset = field->offset;
+ } else {
+ new_field->offset = struct_type->size + field->offset;
+ }
+ new_field->type = field->type;
+ new_field->is_const = field->is_const;
+ new_field->is_nested = 1;
+ new_field->first_bit = field->first_bit;
+ new_field->bits = field->bits;
+ field->type = ZEND_FFI_TYPE(field->type); /* reset "owned" flag */
+
+ if (key) {
+ if (!zend_hash_add_ptr(&struct_type->record.fields, key, new_field)) {
+ zend_ffi_type_dtor(new_field->type);
+ pefree(new_field, FFI_G(persistent));
+ zend_ffi_parser_error("duplicate field name '%s' at line %d", ZSTR_VAL(key), FFI_G(line));
+ return;
+ }
+ } else {
+ zend_hash_next_index_insert_ptr(&struct_type->record.fields, field);
+ }
+ } ZEND_HASH_FOREACH_END();
+
+ if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
+ struct_type->size = MAX(struct_type->size, field_type->size);
+ } else {
+ struct_type->size += field_type->size;
+ }
+
+ zend_ffi_type_dtor(field_dcl->type);
+ field_dcl->type = NULL;
+}
+/* }}} */
+
+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) /* {{{ */
+{
+ zend_ffi_type *struct_type = ZEND_FFI_TYPE(struct_dcl->type);
+ zend_ffi_type *field_type;
+ zend_ffi_field *field;
+
+ ZEND_ASSERT(struct_type && struct_type->kind == ZEND_FFI_TYPE_STRUCT);
+ zend_ffi_finalize_type(field_dcl);
+ field_type = ZEND_FFI_TYPE(field_dcl->type);
+ if (zend_ffi_validate_field_type(field_type, struct_type) != SUCCESS) {
+ zend_ffi_cleanup_dcl(field_dcl);
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+
+ if (field_type->kind < ZEND_FFI_TYPE_UINT8 || field_type->kind > ZEND_FFI_TYPE_BOOL) {
+ zend_ffi_cleanup_dcl(field_dcl);
+ 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));
+ }
+
+ if (bits->kind == ZEND_FFI_VAL_INT32 || ZEND_FFI_VAL_INT64) {
+ if (bits->i64 < 0) {
+ zend_ffi_cleanup_dcl(field_dcl);
+ 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));
+ } else if (bits->i64 == 0) {
+ zend_ffi_cleanup_dcl(field_dcl);
+ if (name) {
+ 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));
+ }
+ return;
+ } else if (bits->i64 > field_type->size * 8) {
+ zend_ffi_cleanup_dcl(field_dcl);
+ 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));
+ }
+ } else if (ZEND_FFI_VAL_UINT32 || ZEND_FFI_VAL_UINT64) {
+ if (bits->u64 == 0) {
+ zend_ffi_cleanup_dcl(field_dcl);
+ if (name) {
+ 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));
+ }
+ return;
+ } else if (bits->u64 > field_type->size * 8) {
+ zend_ffi_cleanup_dcl(field_dcl);
+ 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));
+ }
+ } else {
+ zend_ffi_cleanup_dcl(field_dcl);
+ 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));
+ }
+
+ field = pemalloc(sizeof(zend_ffi_field), FFI_G(persistent));
+ if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED)) {
+ struct_type->align = MAX(struct_type->align, sizeof(uint32_t));
+ }
+ if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
+ field->offset = 0;
+ field->first_bit = 0;
+ field->bits = bits->u64;
+ if (struct_type->attr & ZEND_FFI_ATTR_PACKED) {
+ struct_type->size = MAX(struct_type->size, (bits->u64 + 7) / 8);
+ } else {
+ struct_type->size = MAX(struct_type->size, ((bits->u64 + 31) / 32) * 4);
+ }
+ } else {
+ zend_ffi_field *prev_field = NULL;
+
+ if (zend_hash_num_elements(&struct_type->record.fields) > 0) {
+ ZEND_HASH_REVERSE_FOREACH_PTR(&struct_type->record.fields, prev_field) {
+ break;
+ } ZEND_HASH_FOREACH_END();
+ }
+ if (prev_field && prev_field->bits) {
+ field->offset = prev_field->offset;
+ field->first_bit = prev_field->first_bit + prev_field->bits;
+ field->bits = bits->u64;
+ } else {
+ field->offset = struct_type->size;
+ field->first_bit = 0;
+ field->bits = bits->u64;
+ }
+ if (struct_type->attr & ZEND_FFI_ATTR_PACKED) {
+ struct_type->size = field->offset + ((field->first_bit + field->bits) + 7) / 8;
+ } else {
+ struct_type->size = field->offset + (((field->first_bit + field->bits) + 31) / 32) * 4;
+ }
+ }
+ field->type = field_dcl->type;
+ field->is_const = (zend_bool)(field_dcl->attr & ZEND_FFI_ATTR_CONST);
+ field->is_nested = 0;
+ field_dcl->type = field_type; /* reset "owned" flag */
+
+ if (name) {
+ if (!zend_hash_str_add_ptr(&struct_type->record.fields, name, name_len, field)) {
+ zend_ffi_type_dtor(field->type);
+ pefree(field, FFI_G(persistent));
+ zend_ffi_parser_error("duplicate field name '%.*s' at line %d", name_len, name, FFI_G(line));
+ }
+ } else {
+ zend_hash_next_index_insert_ptr(&struct_type->record.fields, field);
+ }
+}
+/* }}} */
+
+void zend_ffi_adjust_struct_size(zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_type *struct_type = ZEND_FFI_TYPE(dcl->type);
+
+ ZEND_ASSERT(struct_type->kind == ZEND_FFI_TYPE_STRUCT);
+ if (dcl->align > struct_type->align) {
+ struct_type->align = dcl->align;
+ }
+ if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED)) {
+ struct_type->size = ((struct_type->size + (struct_type->align - 1)) / struct_type->align) * struct_type->align;
+ }
+ dcl->align = 0;
+}
+/* }}} */
+
+void zend_ffi_make_pointer_type(zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
+ type->kind = ZEND_FFI_TYPE_POINTER;
+ type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_POINTER_ATTRS);
+ type->size = sizeof(void*);
+ type->align = _Alignof(void*);
+ zend_ffi_finalize_type(dcl);
+ if (zend_ffi_validate_vla(ZEND_FFI_TYPE(dcl->type)) != SUCCESS) {
+ zend_ffi_cleanup_dcl(dcl);
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+ type->pointer.type = dcl->type;
+ dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
+ dcl->flags &= ~ZEND_FFI_DCL_TYPE_QUALIFIERS;
+ dcl->attr &= ~ZEND_FFI_POINTER_ATTRS;
+ dcl->align = 0;
+}
+/* }}} */
+
+static int zend_ffi_validate_array_element_type(zend_ffi_type *type) /* {{{ */
+{
+ if (type->kind == ZEND_FFI_TYPE_FUNC) {
+ zend_ffi_throw_parser_error("array of functions is not allowed at line %d", FFI_G(line));
+ return FAILURE;
+ } else if (type->kind == ZEND_FFI_TYPE_ARRAY && (type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
+ zend_ffi_throw_parser_error("only the leftmost array can be undimensioned at line %d", FFI_G(line));
+ return FAILURE;
+ }
+ return zend_ffi_validate_type(type, 1);
+}
+/* }}} */
+
+void zend_ffi_make_array_type(zend_ffi_dcl *dcl, zend_ffi_val *len) /* {{{ */
+{
+ int length = 0;
+ zend_ffi_type *element_type;
+ zend_ffi_type *type;
+
+ zend_ffi_finalize_type(dcl);
+ element_type = ZEND_FFI_TYPE(dcl->type);
+
+ if (len->kind == ZEND_FFI_VAL_EMPTY) {
+ length = 0;
+ } else if (len->kind == ZEND_FFI_VAL_UINT32 || len->kind == ZEND_FFI_VAL_UINT64) {
+ length = len->u64;
+ } else if (len->kind == ZEND_FFI_VAL_INT32 || len->kind == ZEND_FFI_VAL_INT64) {
+ length = len->i64;
+ } else if (len->kind == ZEND_FFI_VAL_CHAR) {
+ length = len->ch;
+ } else {
+ zend_ffi_cleanup_dcl(dcl);
+ zend_ffi_parser_error("unsupported array index type at line %d", FFI_G(line));
+ return;
+ }
+ if (length < 0) {
+ zend_ffi_cleanup_dcl(dcl);
+ zend_ffi_parser_error("negative array index at line %d", FFI_G(line));
+ return;
+ }
+
+ if (zend_ffi_validate_array_element_type(element_type) != SUCCESS) {
+ zend_ffi_cleanup_dcl(dcl);
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+
+ type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
+ type->kind = ZEND_FFI_TYPE_ARRAY;
+ type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_ARRAY_ATTRS);
+ type->size = length * element_type->size;
+ type->align = element_type->align;
+ type->array.type = dcl->type;
+ type->array.length = length;
+ dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
+ dcl->flags &= ~ZEND_FFI_DCL_TYPE_QUALIFIERS;
+ dcl->attr &= ~ZEND_FFI_ARRAY_ATTRS;
+ dcl->align = 0;
+}
+/* }}} */
+
+static int zend_ffi_validate_func_ret_type(zend_ffi_type *type) /* {{{ */
+{
+ if (type->kind == ZEND_FFI_TYPE_FUNC) {
+ zend_ffi_throw_parser_error("function returning function is not allowed at line %d", FFI_G(line));
+ return FAILURE;
+ } else if (type->kind == ZEND_FFI_TYPE_ARRAY) {
+ zend_ffi_throw_parser_error("function returning array is not allowed at line %d", FFI_G(line));
+ return FAILURE;
+ }
+ return zend_ffi_validate_incomplete_type(type, 0);
+}
+/* }}} */
+
+void zend_ffi_make_func_type(zend_ffi_dcl *dcl, HashTable *args) /* {{{ */
+{
+ zend_ffi_type *type;
+ zend_ffi_type *ret_type;
+
+ zend_ffi_finalize_type(dcl);
+ ret_type = ZEND_FFI_TYPE(dcl->type);
+
+ if (args) {
+ int no_args = 0;
+ zend_ffi_type *arg_type;
+
+ ZEND_HASH_FOREACH_PTR(args, arg_type) {
+ arg_type = ZEND_FFI_TYPE(arg_type);
+ if (arg_type->kind == ZEND_FFI_TYPE_VOID) {
+ if (zend_hash_num_elements(args) != 1) {
+ zend_ffi_cleanup_dcl(dcl);
+ zend_hash_destroy(args);
+ pefree(args, FFI_G(persistent));
+ zend_ffi_parser_error("'void' type is not allowed at line %d", FFI_G(line));
+ return;
+ } else {
+ no_args = 1;
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ if (no_args) {
+ zend_hash_destroy(args);
+ pefree(args, FFI_G(persistent));
+ args = NULL;
+ }
+ }
+
+ if (zend_ffi_validate_func_ret_type(ret_type) != SUCCESS) {
+ zend_ffi_cleanup_dcl(dcl);
+ if (args) {
+ zend_hash_destroy(args);
+ pefree(args, FFI_G(persistent));
+ }
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+
+ type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
+ type->kind = ZEND_FFI_TYPE_FUNC;
+ type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_FUNC_ATTRS);
+ type->size = sizeof(void*);
+ type->align = 1;
+ type->func.ret_type = dcl->type;
+ switch (dcl->abi) {
+ case ZEND_FFI_ABI_DEFAULT:
+ case ZEND_FFI_ABI_CDECL:
+ type->func.abi = FFI_DEFAULT_ABI;
+ break;
+#ifndef _WIN64
+ case ZEND_FFI_ABI_FASTCALL:
+ type->func.abi = FFI_FASTCALL;
+ break;
+ case ZEND_FFI_ABI_THISCALL:
+ type->func.abi = FFI_THISCALL;
+ break;
+ case ZEND_FFI_ABI_STDCALL:
+ type->func.abi = FFI_STDCALL;
+ break;
+#endif
+#if 0
+ case ZEND_FFI_ABI_PASCAL:
+ type->func.abi = FFI_PASCAL;
+ break;
+#endif
+#if 0
+ case ZEND_FFI_ABI_REGISTER:
+ type->func.abi = FFI_REGISTER;
+ break;
+#endif
+#ifdef X86_WIN32
+ case ZEND_FFI_ABI_MS:
+ type->func.abi = FFI_MS_CDECL;
+ break;
+#endif
+#ifndef _WIN32
+ case ZEND_FFI_ABI_SYSV:
+ type->func.abi = FFI_SYSV;
+ break;
+#endif
+ default:
+ type->func.abi = FFI_DEFAULT_ABI;
+ zend_ffi_parser_error("unsupported calling convention line %d", FFI_G(line));
+ break;
+ }
+ type->func.args = args;
+ dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
+ dcl->attr &= ~ZEND_FFI_FUNC_ATTRS;
+ dcl->align = 0;
+ dcl->abi = 0;
+}
+/* }}} */
+
+void zend_ffi_add_arg(HashTable **args, const char *name, size_t name_len, zend_ffi_dcl *arg_dcl) /* {{{ */
+{
+ zend_ffi_type *type;
+
+ if (!*args) {
+ *args = pemalloc(sizeof(HashTable), FFI_G(persistent));
+ zend_hash_init(*args, 0, NULL, zend_ffi_type_hash_dtor, FFI_G(persistent));
+ }
+ zend_ffi_finalize_type(arg_dcl);
+ type = ZEND_FFI_TYPE(arg_dcl->type);
+ if (type->kind == ZEND_FFI_TYPE_ARRAY) {
+ if (ZEND_FFI_TYPE_IS_OWNED(arg_dcl->type)) {
+ type->kind = ZEND_FFI_TYPE_POINTER;
+ type->size = sizeof(void*);
+ } else {
+ zend_ffi_type *new_type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
+ new_type->kind = ZEND_FFI_TYPE_POINTER;
+ new_type->attr = FFI_G(default_type_attr) | (type->attr & ZEND_FFI_POINTER_ATTRS);
+ new_type->size = sizeof(void*);
+ new_type->align = _Alignof(void*);
+ new_type->pointer.type = ZEND_FFI_TYPE(type->array.type);
+ arg_dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
+ }
+ } else if (type->kind == ZEND_FFI_TYPE_FUNC) {
+ zend_ffi_type *new_type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
+ new_type->kind = ZEND_FFI_TYPE_POINTER;
+ new_type->attr = FFI_G(default_type_attr);
+ new_type->size = sizeof(void*);
+ new_type->align = _Alignof(void*);
+ new_type->pointer.type = arg_dcl->type;
+ arg_dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
+ }
+ if (zend_ffi_validate_incomplete_type(type, 1) != SUCCESS) {
+ zend_ffi_cleanup_dcl(arg_dcl);
+ zend_hash_destroy(*args);
+ pefree(*args, FFI_G(persistent));
+ *args = NULL;
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+ zend_hash_next_index_insert_ptr(*args, (void*)arg_dcl->type);
+}
+/* }}} */
+
+void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_symbol *sym;
+
+ if (!FFI_G(symbols)) {
+ FFI_G(symbols) = pemalloc(sizeof(HashTable), FFI_G(persistent));
+ 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));
+ }
+ sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
+ if (sym) {
+ zend_ffi_parser_error("redeclaration of '%.*s' at line %d", name_len, name, FFI_G(line));
+ } else {
+ zend_ffi_finalize_type(dcl);
+ if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_TYPEDEF) {
+ if (zend_ffi_validate_vla(ZEND_FFI_TYPE(dcl->type)) != SUCCESS) {
+ zend_ffi_cleanup_dcl(dcl);
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+ if (dcl->align && dcl->align > ZEND_FFI_TYPE(dcl->type)->align) {
+ if (ZEND_FFI_TYPE_IS_OWNED(dcl->type)) {
+ ZEND_FFI_TYPE(dcl->type)->align = dcl->align;
+ } else {
+ zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
+
+ memcpy(type, ZEND_FFI_TYPE(dcl->type), sizeof(zend_ffi_type));
+ type->attr |= FFI_G(default_type_attr);
+ type->align = dcl->align;
+ dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
+ }
+ }
+ sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
+ sym->kind = ZEND_FFI_SYM_TYPE;
+ sym->type = dcl->type;
+ sym->is_const = (zend_bool)(dcl->attr & ZEND_FFI_ATTR_CONST);
+ dcl->type = ZEND_FFI_TYPE(dcl->type); /* reset "owned" flag */
+ zend_hash_str_add_new_ptr(FFI_G(symbols), name, name_len, sym);
+ } else {
+ zend_ffi_type *type;
+
+ type = ZEND_FFI_TYPE(dcl->type);
+ if (zend_ffi_validate_type(type, 0) != SUCCESS) {
+ zend_ffi_cleanup_dcl(dcl);
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+ if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == 0 ||
+ (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_EXTERN) {
+ sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
+ sym->kind = (type->kind == ZEND_FFI_TYPE_FUNC) ? ZEND_FFI_SYM_FUNC : ZEND_FFI_SYM_VAR;
+ sym->type = dcl->type;
+ sym->is_const = (zend_bool)(dcl->attr & ZEND_FFI_ATTR_CONST);
+ dcl->type = type; /* reset "owned" flag */
+ zend_hash_str_add_new_ptr(FFI_G(symbols), name, name_len, sym);
+ } else {
+ /* useless declarartion */
+ zend_ffi_type_dtor(dcl->type);
+ }
+ }
+ }
+}
+/* }}} */
+
+void zend_ffi_declare_tag(const char *name, size_t name_len, zend_ffi_dcl *dcl, zend_bool incomplete) /* {{{ */
+{
+ zend_ffi_tag *tag;
+
+ if (!FFI_G(tags)) {
+ FFI_G(tags) = pemalloc(sizeof(HashTable), FFI_G(persistent));
+ 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));
+ }
+ tag = zend_hash_str_find_ptr(FFI_G(tags), name, name_len);
+ if (tag) {
+ zend_ffi_type *type = ZEND_FFI_TYPE(tag->type);
+
+ if (dcl->flags & ZEND_FFI_DCL_STRUCT) {
+ if (tag->kind != ZEND_FFI_TAG_STRUCT) {
+ zend_ffi_parser_error("'%.*s' defined as wrong kind of tag at line %d", name_len, name, FFI_G(line));
+ return;
+ } else if (!incomplete && !(type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
+ zend_ffi_parser_error("redefinition of 'struct %.*s' at line %d", name_len, name, FFI_G(line));
+ return;
+ }
+ } else if (dcl->flags & ZEND_FFI_DCL_UNION) {
+ if (tag->kind != ZEND_FFI_TAG_UNION) {
+ zend_ffi_parser_error("'%.*s' defined as wrong kind of tag at line %d", name_len, name, FFI_G(line));
+ return;
+ } else if (!incomplete && !(type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
+ zend_ffi_parser_error("redefinition of 'union %.*s' at line %d", name_len, name, FFI_G(line));
+ return;
+ }
+ } else if (dcl->flags & ZEND_FFI_DCL_ENUM) {
+ if (tag->kind != ZEND_FFI_TAG_ENUM) {
+ zend_ffi_parser_error("'%.*s' defined as wrong kind of tag at line %d", name_len, name, FFI_G(line));
+ return;
+ } else if (!incomplete && !(type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
+ zend_ffi_parser_error("redefinition of 'enum %.*s' at line %d", name_len, name, FFI_G(line));
+ return;
+ }
+ } else {
+ ZEND_ASSERT(0);
+ return;
+ }
+ dcl->type = type;
+ if (!incomplete) {
+ type->attr &= ~ZEND_FFI_ATTR_INCOMPLETE_TAG;
+ }
+ } else {
+ zend_ffi_tag *tag = pemalloc(sizeof(zend_ffi_tag), FFI_G(persistent));
+
+ if (dcl->flags & ZEND_FFI_DCL_STRUCT) {
+ tag->kind = ZEND_FFI_TAG_STRUCT;
+ zend_ffi_make_struct_type(dcl);
+ } else if (dcl->flags & ZEND_FFI_DCL_UNION) {
+ tag->kind = ZEND_FFI_TAG_UNION;
+ zend_ffi_make_struct_type(dcl);
+ } else if (dcl->flags & ZEND_FFI_DCL_ENUM) {
+ tag->kind = ZEND_FFI_TAG_ENUM;
+ zend_ffi_make_enum_type(dcl);
+ } else {
+ ZEND_ASSERT(0);
+ }
+ tag->type = ZEND_FFI_TYPE_MAKE_OWNED(dcl->type);
+ dcl->type = ZEND_FFI_TYPE(dcl->type);
+ if (incomplete) {
+ dcl->type->attr |= ZEND_FFI_ATTR_INCOMPLETE_TAG;
+ }
+ zend_hash_str_add_new_ptr(FFI_G(tags), name, name_len, tag);
+ }
+}
+/* }}} */
+
+void zend_ffi_set_abi(zend_ffi_dcl *dcl, uint16_t abi) /* {{{ */
+{
+ if (dcl->abi != ZEND_FFI_ABI_DEFAULT) {
+ zend_ffi_parser_error("multiple calling convention specifiers at line %d", FFI_G(line));
+ } else {
+ dcl->abi = abi;
+ }
+}
+/* }}} */
+
+#ifndef __BIGGEST_ALIGNMENT__
+/* XXX need something better, perhaps with regard to SIMD, etc. */
+# define __BIGGEST_ALIGNMENT__ sizeof(size_t)
+#endif
+
+void zend_ffi_add_attribute(zend_ffi_dcl *dcl, const char *name, size_t name_len) /* {{{ */
+{
+ if (name_len == sizeof("cdecl")-1 && memcmp(name, "cdecl", sizeof("cdecl")-1) == 0) {
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_CDECL);
+ } else if (name_len == sizeof("fastcall")-1 && memcmp(name, "fastcall", sizeof("fastcall")-1) == 0) {
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_FASTCALL);
+ } else if (name_len == sizeof("thiscall")-1 && memcmp(name, "thiscall", sizeof("thiscall")-1) == 0) {
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_THISCALL);
+ } else if (name_len == sizeof("stdcall")-1 && memcmp(name, "stdcall", sizeof("stdcall")-1) == 0) {
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_STDCALL);
+ } else if (name_len == sizeof("ms_abi")-1 && memcmp(name, "ms_abi", sizeof("ms_abi")-1) == 0) {
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_MS);
+ } else if (name_len == sizeof("sysv_abi")-1 && memcmp(name, "sysv_abi", sizeof("sysv_abi")-1) == 0) {
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_SYSV);
+ } else if (name_len == sizeof("aligned")-1 && memcmp(name, "aligned", sizeof("aligned")-1) == 0) {
+ dcl->align = __BIGGEST_ALIGNMENT__;
+ } else if (name_len == sizeof("packed")-1 && memcmp(name, "packed", sizeof("packed")-1) == 0) {
+ dcl->attr |= ZEND_FFI_ATTR_PACKED;
+ } else if (name_len == sizeof("ms_struct")-1 && memcmp(name, "ms_struct", sizeof("ms_struct")-1) == 0) {
+ dcl->attr |= ZEND_FFI_ATTR_MS_STRUCT;
+ } else if (name_len == sizeof("gcc_struct")-1 && memcmp(name, "gcc_struct", sizeof("gcc_struct")-1) == 0) {
+ dcl->attr |= ZEND_FFI_ATTR_GCC_STRUCT;
+ } else if (name_len == sizeof("const")-1 && memcmp(name, "const", sizeof("const")-1) == 0) {
+ /* ignore */
+ } else if (name_len == sizeof("malloc")-1 && memcmp(name, "malloc", sizeof("malloc")-1) == 0) {
+ /* ignore */
+ } else if (name_len == sizeof("deprecated")-1 && memcmp(name, "deprecated", sizeof("deprecated")-1) == 0) {
+ /* ignore */
+ } else {
+ zend_ffi_parser_error("unsupported attribute '%.*s' at line %d", name_len, name, FFI_G(line));
+ }
+}
+/* }}} */
+
+void zend_ffi_add_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, int n, zend_ffi_val *val) /* {{{ */
+{
+ if (n == 0 && name_len == sizeof("regparam")-1 && memcmp(name, "regparam", sizeof("regparam")-1) == 0) {
+ 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) && val->i64 == 3) {
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_REGISTER);
+ } else {
+ zend_ffi_parser_error("incorrect 'regparam' value at line %d", FFI_G(line));
+ }
+ } else if (n == 0 && name_len == sizeof("aligned")-1 && memcmp(name, "aligned", sizeof("aligned")-1) == 0) {
+ 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)
+ && val->i64 > 0 && val->i64 <= 0x80000000 && (val->i64 & (val->i64 - 1)) == 0) {
+ dcl->align = val->i64;
+ } else {
+ zend_ffi_parser_error("incorrect 'alignemnt' value at line %d", FFI_G(line));
+ }
+ } else if (name_len == sizeof("format")-1 && memcmp(name, "format", sizeof("format")-1) == 0) {
+ /* ignore */
+ } else if (name_len == sizeof("deprecated")-1 && memcmp(name, "deprecated", sizeof("deprecated")-1) == 0) {
+ /* ignore */
+ } else {
+ zend_ffi_parser_error("unsupported attribute '%.*s' at line %d", name_len, name, FFI_G(line));
+ }
+}
+/* }}} */
+
+void zend_ffi_add_msvc_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, zend_ffi_val *val) /* {{{ */
+{
+ if (name_len == sizeof("align")-1 && memcmp(name, "align", sizeof("align")-1) == 0) {
+ 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)
+ && val->i64 > 0 && val->i64 <= 0x80000000 && (val->i64 & (val->i64 - 1)) == 0) {
+ dcl->align = val->i64;
+ } else {
+ zend_ffi_parser_error("incorrect 'alignemnt' value at line %d", FFI_G(line));
+ }
+ } else {
+ /* ignore */
+ }
+}
+/* }}} */
+
+static int zend_ffi_nested_type(zend_ffi_type *type, zend_ffi_type *nested_type) /* {{{ */
+{
+ nested_type = ZEND_FFI_TYPE(nested_type);
+ switch (nested_type->kind) {
+ case ZEND_FFI_TYPE_POINTER:
+ /* "char" is used as a terminator of nested declaration */
+ if (nested_type->pointer.type == &zend_ffi_type_char) {
+ nested_type->pointer.type = type;
+ return zend_ffi_validate_vla(ZEND_FFI_TYPE(type));
+ } else {
+ return zend_ffi_nested_type(type, nested_type->pointer.type);
+ }
+ break;
+ case ZEND_FFI_TYPE_ARRAY:
+ /* "char" is used as a terminator of nested declaration */
+ if (nested_type->array.type == &zend_ffi_type_char) {
+ nested_type->array.type = type;
+ if (zend_ffi_validate_array_element_type(ZEND_FFI_TYPE(type)) != SUCCESS) {
+ return FAILURE;
+ }
+ } else {
+ if (zend_ffi_nested_type(type, nested_type->array.type) != SUCCESS) {
+ return FAILURE;
+ }
+ }
+ nested_type->size = nested_type->array.length * ZEND_FFI_TYPE(nested_type->array.type)->size;
+ nested_type->align = ZEND_FFI_TYPE(nested_type->array.type)->align;
+ return SUCCESS;
+ break;
+ case ZEND_FFI_TYPE_FUNC:
+ /* "char" is used as a terminator of nested declaration */
+ if (nested_type->func.ret_type == &zend_ffi_type_char) {
+ nested_type->func.ret_type = type;
+ return zend_ffi_validate_func_ret_type(ZEND_FFI_TYPE(type));
+ } else {
+ return zend_ffi_nested_type(type, nested_type->func.ret_type);
+ }
+ break;
+ default:
+ ZEND_ASSERT(0);
+ }
+}
+/* }}} */
+
+void zend_ffi_nested_declaration(zend_ffi_dcl *dcl, zend_ffi_dcl *nested_dcl) /* {{{ */
+{
+ /* "char" is used as a terminator of nested declaration */
+ zend_ffi_finalize_type(dcl);
+ if (!nested_dcl->type || nested_dcl->type == &zend_ffi_type_char) {
+ nested_dcl->type = dcl->type;
+ } else {
+ if (zend_ffi_nested_type(dcl->type, nested_dcl->type) != SUCCESS) {
+ zend_ffi_cleanup_dcl(nested_dcl);
+ LONGJMP(FFI_G(bailout), FAILURE);
+ }
+ }
+ dcl->type = nested_dcl->type;
+}
+/* }}} */
+
+void zend_ffi_align_as_type(zend_ffi_dcl *dcl, zend_ffi_dcl *align_dcl) /* {{{ */
+{
+ zend_ffi_finalize_type(align_dcl);
+ dcl->align = MAX(align_dcl->align, ZEND_FFI_TYPE(align_dcl->type)->align);
+}
+/* }}} */
+
+void zend_ffi_align_as_val(zend_ffi_dcl *dcl, zend_ffi_val *align_val) /* {{{ */
+{
+ switch (align_val->kind) {
+ case ZEND_FFI_VAL_INT32:
+ case ZEND_FFI_VAL_UINT32:
+ dcl->align = zend_ffi_type_uint32.align;
+ break;
+ case ZEND_FFI_VAL_INT64:
+ case ZEND_FFI_VAL_UINT64:
+ dcl->align = zend_ffi_type_uint64.align;
+ break;
+ case ZEND_FFI_VAL_FLOAT:
+ dcl->align = zend_ffi_type_float.align;
+ break;
+ case ZEND_FFI_VAL_DOUBLE:
+ dcl->align = zend_ffi_type_double.align;
+ break;
+#ifdef HAVE_LONG_DOUBLE
+ case ZEND_FFI_VAL_LONG_DOUBLE:
+ dcl->align = zend_ffi_type_long_double.align;
+ break;
+#endif
+ case ZEND_FFI_VAL_CHAR:
+ case ZEND_FFI_VAL_STRING:
+ dcl->align = zend_ffi_type_char.align;
+ break;
+ default:
+ break;
+ }
+}
+/* }}} */
+
+#define zend_ffi_expr_bool(val) do { \
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = !!val->u64; \
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = !!val->i64; \
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = !!val->d; \
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = !!val->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+} while (0)
+
+#define zend_ffi_expr_math(val, op2, OP) do { \
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->kind = MAX(val->kind, op2->kind); \
+ val->u64 = val->u64 OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32) { \
+ val->u64 = val->u64 OP op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->u64 = val->u64 OP op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->kind = op2->kind; \
+ val->d = (zend_ffi_double)val->u64 OP op2->d; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->u64 = val->u64 OP op2->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32) { \
+ val->i64 = val->i64 OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->i64 = val->i64 OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->kind = MAX(val->kind, op2->kind); \
+ val->i64 = val->i64 OP op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->kind = op2->kind; \
+ val->d = (zend_ffi_double)val->i64 OP op2->d; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->i64 = val->i64 OP op2->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->d = val->d OP (zend_ffi_double)op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32 ||op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->d = val->d OP (zend_ffi_double)op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->kind = MAX(val->kind, op2->kind); \
+ val->d = val->d OP op2->d; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->d = val->d OP (zend_ffi_double)op2->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->kind = op2->kind; \
+ val->u64 = val->ch OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->kind = ZEND_FFI_VAL_INT64; \
+ val->i64 = val->ch OP op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->kind = op2->kind; \
+ val->d = (zend_ffi_double)val->ch OP op2->d; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->ch = val->ch OP op2->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+} while (0)
+
+#define zend_ffi_expr_int_math(val, op2, OP) do { \
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->kind = MAX(val->kind, op2->kind); \
+ val->u64 = val->u64 OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32) { \
+ val->u64 = val->u64 OP op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->u64 = val->u64 OP op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->u64 = val->u64 OP (uint64_t)op2->d; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->u64 = val->u64 OP op2->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32) { \
+ val->i64 = val->i64 OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->i64 = val->i64 OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->kind = MAX(val->kind, op2->kind); \
+ val->i64 = val->i64 OP op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->u64 = val->u64 OP (int64_t)op2->d; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->i64 = val->i64 OP op2->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->kind = op2->kind; \
+ val->u64 = (uint64_t)val->d OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->kind = op2->kind; \
+ val->i64 = (int64_t)val->d OP op2->i64; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->kind = op2->kind; \
+ val->u64 = (uint64_t)val->ch OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->kind = op2->kind; \
+ val->i64 = (int64_t)val->ch OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->ch = val->ch OP op2->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+} while (0)
+
+#define zend_ffi_expr_cmp(val, op2, OP) do { \
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->u64 OP op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->u64 OP op2->u64; /*signed/unsigned */ \
+ } else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = (zend_ffi_double)val->u64 OP op2->d; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->u64 OP op2->d; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->i64 OP op2->i64; /* signed/unsigned */ \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->i64 OP op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = (zend_ffi_double)val->i64 OP op2->d; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->i64 OP op2->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->d OP (zend_ffi_double)op2->u64; \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32 ||op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->d OP (zend_ffi_double)op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->d OP op2->d; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->d OP (zend_ffi_double)op2->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) { \
+ if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->ch OP op2->i64; /* signed/unsigned */ \
+ } else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->ch OP op2->i64; \
+ } else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = (zend_ffi_double)val->ch OP op2->d; \
+ } else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
+ val->kind = ZEND_FFI_VAL_INT32; \
+ val->i64 = val->ch OP op2->ch; \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+ } else { \
+ val->kind = ZEND_FFI_VAL_ERROR; \
+ } \
+} while (0)
+
+void zend_ffi_expr_conditional(zend_ffi_val *val, zend_ffi_val *op2, zend_ffi_val *op3) /* {{{ */
+{
+ zend_ffi_expr_bool(val);
+ if (val->kind == ZEND_FFI_VAL_INT32) {
+ if (val->i64) {
+ *val = *op2;
+ } else {
+ *val = *op3;
+ }
+ }
+}
+/* }}} */
+
+void zend_ffi_expr_bool_or(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_bool(val);
+ zend_ffi_expr_bool(op2);
+ if (val->kind == ZEND_FFI_VAL_INT32 && op2->kind == ZEND_FFI_VAL_INT32) {
+ val->i64 = val->i64 || op2->i64;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+}
+/* }}} */
+
+void zend_ffi_expr_bool_and(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_bool(val);
+ zend_ffi_expr_bool(op2);
+ if (val->kind == ZEND_FFI_VAL_INT32 && op2->kind == ZEND_FFI_VAL_INT32) {
+ val->i64 = val->i64 && op2->i64;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+}
+/* }}} */
+
+void zend_ffi_expr_bw_or(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_int_math(val, op2, |);
+}
+/* }}} */
+
+void zend_ffi_expr_bw_xor(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_int_math(val, op2, ^);
+}
+/* }}} */
+
+void zend_ffi_expr_bw_and(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_int_math(val, op2, &);
+}
+/* }}} */
+
+void zend_ffi_expr_is_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_cmp(val, op2, ==);
+}
+/* }}} */
+
+void zend_ffi_expr_is_not_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_cmp(val, op2, !=);
+}
+/* }}} */
+
+void zend_ffi_expr_is_less(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_cmp(val, op2, <);
+}
+/* }}} */
+
+void zend_ffi_expr_is_greater(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_cmp(val, op2, >);
+}
+/* }}} */
+
+void zend_ffi_expr_is_less_or_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_cmp(val, op2, <=);
+}
+/* }}} */
+
+void zend_ffi_expr_is_greater_or_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_cmp(val, op2, >=);
+}
+/* }}} */
+
+void zend_ffi_expr_shift_left(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_int_math(val, op2, <<);
+}
+/* }}} */
+
+void zend_ffi_expr_shift_right(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_int_math(val, op2, >>);
+}
+/* }}} */
+
+void zend_ffi_expr_add(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_math(val, op2, +);
+}
+/* }}} */
+
+void zend_ffi_expr_sub(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_math(val, op2, -);
+}
+/* }}} */
+
+void zend_ffi_expr_mul(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_math(val, op2, *);
+}
+/* }}} */
+
+void zend_ffi_expr_div(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_math(val, op2, /);
+}
+/* }}} */
+
+void zend_ffi_expr_mod(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
+{
+ zend_ffi_expr_int_math(val, op2, %); // ???
+}
+/* }}} */
+
+void zend_ffi_expr_cast(zend_ffi_val *val, zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_finalize_type(dcl);
+ switch (dcl->type->kind) {
+ case ZEND_FFI_TYPE_FLOAT:
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
+ val->kind = ZEND_FFI_VAL_FLOAT;
+ val->d = val->u64;
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
+ val->kind = ZEND_FFI_VAL_FLOAT;
+ val->d = val->i64;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_FLOAT;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->kind = ZEND_FFI_VAL_FLOAT;
+ val->d = val->ch;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ break;
+ case ZEND_FFI_TYPE_DOUBLE:
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
+ val->kind = ZEND_FFI_VAL_DOUBLE;
+ val->d = val->u64;
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
+ val->kind = ZEND_FFI_VAL_DOUBLE;
+ val->d = val->i64;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_DOUBLE;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->kind = ZEND_FFI_VAL_DOUBLE;
+ val->d = val->ch;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ break;
+#ifdef HAVE_LONG_DOUBLE
+ case ZEND_FFI_TYPE_LONGDOUBLE:
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
+ val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
+ val->d = val->u64;
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
+ val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
+ val->d = val->i64;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
+ val->d = val->ch;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ break;
+#endif
+ case ZEND_FFI_TYPE_UINT8:
+ case ZEND_FFI_TYPE_UINT16:
+ case ZEND_FFI_TYPE_UINT32:
+ case ZEND_FFI_TYPE_BOOL:
+ 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) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = val->d;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = val->ch;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ break;
+ case ZEND_FFI_TYPE_SINT8:
+ case ZEND_FFI_TYPE_SINT16:
+ case ZEND_FFI_TYPE_SINT32:
+ 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) {
+ val->kind = ZEND_FFI_VAL_INT32;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_INT32;
+ val->i64 = val->d;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->kind = ZEND_FFI_VAL_INT32;
+ val->i64 = val->ch;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ break;
+ case ZEND_FFI_TYPE_UINT64:
+ 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) {
+ val->kind = ZEND_FFI_VAL_UINT64;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_UINT64;
+ val->u64 = val->d;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->kind = ZEND_FFI_VAL_UINT64;
+ val->u64 = val->ch;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ break;
+ case ZEND_FFI_TYPE_SINT64:
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
+ val->kind = ZEND_FFI_VAL_CHAR;
+ val->ch = val->u64;
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
+ val->kind = ZEND_FFI_VAL_CHAR;
+ val->ch = val->i64;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_CHAR;
+ val->ch = val->d;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ break;
+ case ZEND_FFI_TYPE_CHAR:
+ 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) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = val->d;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = val->ch;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ break;
+ default:
+ val->kind = ZEND_FFI_VAL_ERROR;
+ break;
+ }
+}
+/* }}} */
+
+void zend_ffi_expr_plus(zend_ffi_val *val) /* {{{ */
+{
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+}
+/* }}} */
+
+void zend_ffi_expr_neg(zend_ffi_val *val) /* {{{ */
+{
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
+ val->u64 = -val->u64;
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
+ val->i64 = -val->i64;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->d = -val->d;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->ch = -val->ch;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+}
+/* }}} */
+
+void zend_ffi_expr_bw_not(zend_ffi_val *val) /* {{{ */
+{
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
+ val->u64 = ~val->u64;
+ } else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
+ val->i64 = ~val->i64;
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->ch = ~val->ch;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+}
+/* }}} */
+
+void zend_ffi_expr_bool_not(zend_ffi_val *val) /* {{{ */
+{
+ zend_ffi_expr_bool(val);
+ if (val->kind == ZEND_FFI_VAL_INT32) {
+ val->i64 = !val->i64;
+ }
+}
+/* }}} */
+
+void zend_ffi_expr_sizeof_val(zend_ffi_val *val) /* {{{ */
+{
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT32) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_uint32.size;
+ } else if (val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT64) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_uint64.size;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_float.size;
+ } else if (val->kind == ZEND_FFI_VAL_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_double.size;
+ } else if (val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+#ifdef _WIN32
+ val->u64 = zend_ffi_type_double.size;
+#else
+ val->u64 = zend_ffi_type_long_double.size;
+#endif
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_char.size;
+ } else if (val->kind == ZEND_FFI_VAL_STRING) {
+ if (memchr(val->str, '\\', val->len)) {
+ // TODO: support for escape sequences ???
+ val->kind = ZEND_FFI_VAL_ERROR;
+ } else {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = val->len + 1;
+ }
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+}
+/* }}} */
+
+void zend_ffi_expr_sizeof_type(zend_ffi_val *val, zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_finalize_type(dcl);
+ val->kind = (dcl->type->size > 0xffffffff) ? ZEND_FFI_VAL_UINT64 : ZEND_FFI_VAL_UINT32;
+ val->u64 = dcl->type->size;
+}
+/* }}} */
+
+void zend_ffi_expr_alignof_val(zend_ffi_val *val) /* {{{ */
+{
+ if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT32) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_uint32.align;
+ } else if (val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT64) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_uint64.align;
+ } else if (val->kind == ZEND_FFI_VAL_FLOAT) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_float.align;
+ } else if (val->kind == ZEND_FFI_VAL_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_double.align;
+#ifdef HAVE_LONG_DOUBLE
+ } else if (val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_long_double.align;
+#endif
+ } else if (val->kind == ZEND_FFI_VAL_CHAR) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = zend_ffi_type_char.size;
+ } else if (val->kind == ZEND_FFI_VAL_STRING) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = _Alignof(char*);
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+}
+/* }}} */
+
+void zend_ffi_expr_alignof_type(zend_ffi_val *val, zend_ffi_dcl *dcl) /* {{{ */
+{
+ zend_ffi_finalize_type(dcl);
+ val->kind = ZEND_FFI_VAL_UINT32;
+ val->u64 = dcl->type->align;
+}
+/* }}} */
+
+void zend_ffi_val_number(zend_ffi_val *val, int base, const char *str, size_t str_len) /* {{{ */
+{
+ int u = 0;
+ int l = 0;
+
+ if (str[str_len-1] == 'u' || str[str_len-1] == 'U') {
+ u = 1;
+ if (str[str_len-2] == 'l' || str[str_len-2] == 'L') {
+ l = 1;
+ if (str[str_len-3] == 'l' || str[str_len-3] == 'L') {
+ l = 2;
+ }
+ }
+ } else if (str[str_len-1] == 'l' || str[str_len-1] == 'L') {
+ l = 1;
+ if (str[str_len-2] == 'l' || str[str_len-2] == 'L') {
+ l = 2;
+ if (str[str_len-3] == 'u' || str[str_len-3] == 'U') {
+ u = 1;
+ }
+ } else if (str[str_len-2] == 'u' || str[str_len-2] == 'U') {
+ u = 1;
+ }
+ }
+ if (u) {
+ val->u64 = strtoull(str, NULL, base);
+ if (l == 0) {
+ val->kind = ZEND_FFI_VAL_UINT32;
+ } else if (l == 1) {
+ val->kind = (sizeof(long) == 4) ? ZEND_FFI_VAL_UINT32 : ZEND_FFI_VAL_UINT64;
+ } else if (l == 2) {
+ val->kind = ZEND_FFI_VAL_UINT64;
+ }
+ } else {
+ val->i64 = strtoll(str, NULL, base);
+ if (l == 0) {
+ val->kind = ZEND_FFI_VAL_INT32;
+ } else if (l == 1) {
+ val->kind = (sizeof(long) == 4) ? ZEND_FFI_VAL_INT32 : ZEND_FFI_VAL_INT64;
+ } else if (l == 2) {
+ val->kind = ZEND_FFI_VAL_INT64;
+ }
+ }
+}
+/* }}} */
+
+void zend_ffi_val_float_number(zend_ffi_val *val, const char *str, size_t str_len) /* {{{ */
+{
+ val->d = strtold(str, NULL);
+ if (str[str_len-1] == 'f' || str[str_len-1] == 'F') {
+ val->kind = ZEND_FFI_VAL_FLOAT;
+ } else if (str[str_len-1] == 'l' || str[str_len-1] == 'L') {
+ val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
+ } else {
+ val->kind = ZEND_FFI_VAL_DOUBLE;
+ }
+}
+/* }}} */
+
+void zend_ffi_val_string(zend_ffi_val *val, const char *str, size_t str_len) /* {{{ */
+{
+ if (str[0] != '\"') {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ } else {
+ val->kind = ZEND_FFI_VAL_STRING;
+ val->str = str + 1;
+ val->len = str_len - 2;
+ }
+}
+/* }}} */
+
+void zend_ffi_val_character(zend_ffi_val *val, const char *str, size_t str_len) /* {{{ */
+{
+ int n;
+
+ if (str[0] != '\'') {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ } else {
+ val->kind = ZEND_FFI_VAL_CHAR;
+ if (str_len == 3) {
+ val->ch = str[1];
+ } else if (str[1] == '\\') {
+ if (str[2] == 'a') {
+ } else if (str[2] == 'b' && str_len == 4) {
+ val->ch = '\b';
+ } else if (str[2] == 'f' && str_len == 4) {
+ val->ch = '\f';
+ } else if (str[2] == 'n' && str_len == 4) {
+ val->ch = '\n';
+ } else if (str[2] == 'r' && str_len == 4) {
+ val->ch = '\r';
+ } else if (str[2] == 't' && str_len == 4) {
+ val->ch = '\t';
+ } else if (str[2] == 'v' && str_len == 4) {
+ val->ch = '\v';
+ } else if (str[2] >= '0' || str[2] <= '7') {
+ n = str[2] - '0';
+ if (str[3] >= '0' || str[3] <= '7') {
+ n = n * 8 + (str[3] - '0');
+ if ((str[4] >= '0' || str[4] <= '7') && str_len == 6) {
+ n = n * 8 + (str[4] - '0');
+ } else if (str_len != 5) {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ } else if (str_len != 4) {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ if (n <= 0xff) {
+ val->ch = n;
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ } else if (str[2] == 'x') {
+ if (str[3] >= '0' || str[3] <= '7') {
+ n = str[3] - '0';
+ } else if (str[3] >= 'A' || str[3] <= 'F') {
+ n = str[3] - 'A';
+ } else if (str[3] >= 'a' || str[3] <= 'f') {
+ n = str[3] - 'a';
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ if ((str[4] >= '0' || str[4] <= '7') && str_len == 6) {
+ n = n * 16 + (str[4] - '0');
+ } else if ((str[4] >= 'A' || str[4] <= 'F') && str_len == 6) {
+ n = n * 16 + (str[4] - 'A');
+ } else if ((str[4] >= 'a' || str[4] <= 'f') && str_len == 6) {
+ n = n * 16 + (str[4] - 'a');
+ } else if (str_len != 5) {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ val->ch = n;
+ } else if (str_len == 4) {
+ val->ch = str[2];
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ } else {
+ val->kind = ZEND_FFI_VAL_ERROR;
+ }
+ }
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2018 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+%start declarations
+%sub-start type_name
+%case-sensetive true
+%global-vars false
+%output "ffi_parser.c"
+%language "c"
+%indent "\t"
+
+%{
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2018 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ffi.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define yy_buf FFI_G(buf)
+#define yy_end FFI_G(end)
+#define yy_pos FFI_G(pos)
+#define yy_text FFI_G(text)
+#define yy_line FFI_G(line)
+
+/* forward declarations */
+static void yy_error(const char *msg);
+static void yy_error_sym(const char *msg, int sym);
+
+%}
+
+declarations:
+ (
+ {zend_ffi_dcl common_dcl = ZEND_FFI_ATTR_INIT;}
+ declaration_specifiers(&common_dcl)
+ (
+ {const char *name;}
+ {size_t name_len;}
+ {zend_ffi_dcl dcl;}
+ {dcl = common_dcl;}
+ declarator(&dcl, &name, &name_len)
+ attributes(&dcl)?
+ initializer?
+ {zend_ffi_declare(name, name_len, &dcl);}
+ ( ","
+ {dcl = common_dcl;}
+ declarator(&dcl, &name, &name_len)
+ attributes(&dcl)?
+ initializer?
+ {zend_ffi_declare(name, name_len, &dcl);}
+ )*
+ )?
+ ";"
+ )*
+;
+
+declaration_specifiers(zend_ffi_dcl *dcl):
+ ( ?{sym != YY_ID || zend_ffi_is_typedef_name((const char*)yy_text, yy_pos - yy_text)}
+ ( {if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);}
+ "typedef"
+ {dcl->flags |= ZEND_FFI_DCL_TYPEDEF;}
+ | {if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);}
+ "extern"
+ {dcl->flags |= ZEND_FFI_DCL_EXTERN;}
+ | {if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);}
+ "static"
+ {dcl->flags |= ZEND_FFI_DCL_STATIC;}
+ | {if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);}
+ "auto"
+ {dcl->flags |= ZEND_FFI_DCL_AUTO;}
+ | {if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);}
+ "register"
+ {dcl->flags |= ZEND_FFI_DCL_REGISTER;}
+// | "_Thread_local" // TODO: not-implemented ???
+ | ("inline"|"__inline"|"__inline__")
+ {dcl->flags |= ZEND_FFI_DCL_INLINE;}
+ | "_Noreturn"
+ {dcl->flags |= ZEND_FFI_DCL_NO_RETURN;}
+ | "__cdecl"
+ {zend_ffi_set_abi(dcl, ZEND_FFI_ABI_CDECL);}
+ | "__stdcall"
+ {zend_ffi_set_abi(dcl, ZEND_FFI_ABI_STDCALL);}
+ | "__fastcall"
+ {zend_ffi_set_abi(dcl, ZEND_FFI_ABI_FASTCALL);}
+ | "__thiscall"
+ {zend_ffi_set_abi(dcl, ZEND_FFI_ABI_THISCALL);}
+ | "_Alignas"
+ "("
+ ( &type_name
+ {zend_ffi_dcl align_dcl = ZEND_FFI_ATTR_INIT;}
+ type_name(&align_dcl)
+ {zend_ffi_align_as_type(dcl, &align_dcl);}
+ | {zend_ffi_val align_val;}
+ constant_expression(&align_val)
+ {zend_ffi_align_as_val(dcl, &align_val);}
+ )
+ ")"
+ | attributes(dcl)
+ | type_qualifier(dcl)
+ | type_specifier(dcl)
+ )
+ )+
+;
+
+specifier_qualifier_list(zend_ffi_dcl *dcl):
+ ( ?{sym != YY_ID || zend_ffi_is_typedef_name((const char*)yy_text, yy_pos - yy_text)}
+ ( type_specifier(dcl)
+ | type_qualifier(dcl)
+ | attributes(dcl)
+ )
+ )+
+;
+
+type_qualifier_list(zend_ffi_dcl *dcl):
+ ( type_qualifier(dcl)
+ | attributes(dcl)
+ )+
+;
+
+type_qualifier(zend_ffi_dcl *dcl):
+ ("const"|"__const"|"__const__")
+ {dcl->flags |= ZEND_FFI_DCL_CONST;}
+ {dcl->attr |= ZEND_FFI_ATTR_CONST;}
+ | ("restrict"|"__restict"|"__restrict__")
+ {dcl->flags |= ZEND_FFI_DCL_RESTRICT;}
+ | ("volatile"|"__volatile"|"__volatile__")
+ {dcl->flags |= ZEND_FFI_DCL_VOLATILE;}
+ | "_Atomic"
+ {dcl->flags |= ZEND_FFI_DCL_ATOMIC;}
+;
+
+type_specifier(zend_ffi_dcl *dcl):
+ {const char *name;}
+ {size_t name_len;}
+ ( {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);}
+ "void"
+ {dcl->flags |= ZEND_FFI_DCL_VOID;}
+ | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED))) yy_error_sym("unexpected", sym);}
+ "char"
+ {dcl->flags |= ZEND_FFI_DCL_CHAR;}
+ | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);}
+ "short"
+ {dcl->flags |= ZEND_FFI_DCL_SHORT;}
+ | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG))) yy_error_sym("unexpected", sym);}
+ "int"
+ {dcl->flags |= ZEND_FFI_DCL_INT;}
+ | {
+ if (dcl->flags & ZEND_FFI_DCL_LONG) {
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);
+ dcl->flags |= ZEND_FFI_DCL_LONG_LONG;
+ } else {
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);
+ dcl->flags |= ZEND_FFI_DCL_LONG;
+ }
+ }
+ "long"
+ | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);}
+ "float"
+ {dcl->flags |= ZEND_FFI_DCL_FLOAT;}
+ | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);}
+ "double"
+ {dcl->flags |= ZEND_FFI_DCL_DOUBLE;}
+ | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);}
+ "signed"
+ {dcl->flags |= ZEND_FFI_DCL_SIGNED;}
+ | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);}
+ "unsigned"
+ {dcl->flags |= ZEND_FFI_DCL_UNSIGNED;}
+ | {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);}
+ "_Bool"
+ {dcl->flags |= ZEND_FFI_DCL_BOOL;}
+ | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_LONG))) yy_error_sym("Unexpected '%s'", sym);}
+ ("_Complex"|"complex"|"__complex"|"__complex__")
+ {dcl->flags |= ZEND_FFI_DCL_COMPLEX;}
+// | "_Atomic" "(" type_name ")" // TODO: not-implemented ???
+ | {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);}
+ struct_or_union_specifier(dcl)
+ | {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);}
+ enum_specifier(dcl)
+ | {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);}
+ {/*redeclaration of '%.*s' ??? */}
+ ID(&name, &name_len)
+ {dcl->flags |= ZEND_FFI_DCL_TYPEDEF_NAME;}
+ {zend_ffi_resolve_typedef(name, name_len, dcl);}
+ )
+;
+
+struct_or_union_specifier(zend_ffi_dcl *dcl):
+ ( "struct"
+ {dcl->flags |= ZEND_FFI_DCL_STRUCT;}
+ | "union"
+ {dcl->flags |= ZEND_FFI_DCL_UNION;}
+ )
+ attributes(dcl)?
+ ( {const char *name;}
+ {size_t name_len;}
+ ID(&name, &name_len)
+ {zend_ffi_declare_tag(name, name_len, dcl, 1);}
+ ( struct_contents(dcl)
+ {zend_ffi_declare_tag(name, name_len, dcl, 0);}
+ )?
+ | {zend_ffi_make_struct_type(dcl);}
+ struct_contents(dcl)
+ )
+;
+
+struct_contents(zend_ffi_dcl *dcl):
+ "{"
+ ( struct_declaration(dcl)
+ ( ";"
+ struct_declaration(dcl)
+ )*
+ (";")?
+ )?
+ "}"
+ attributes(dcl)?+
+ {zend_ffi_adjust_struct_size(dcl);}
+;
+
+
+struct_declaration(zend_ffi_dcl *struct_dcl):
+ {zend_ffi_dcl common_field_dcl = ZEND_FFI_ATTR_INIT;}
+ specifier_qualifier_list(&common_field_dcl)
+ ( /* empty */
+ {zend_ffi_add_anonymous_field(struct_dcl, &common_field_dcl);}
+ | struct_declarator(struct_dcl, &common_field_dcl)
+ ( ","
+ {zend_ffi_dcl field_dcl = common_field_dcl;}
+ attributes(&field_dcl)?
+ struct_declarator(struct_dcl, &field_dcl)
+ )*
+ )
+;
+
+struct_declarator(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl):
+ {const char *name = NULL;}
+ {size_t name_len = 0;}
+ {zend_ffi_val bits;}
+ ( declarator(field_dcl, &name, &name_len)
+ ( ":"
+ constant_expression(&bits)
+ attributes(field_dcl)?
+ {zend_ffi_add_bit_field(struct_dcl, name, name_len, field_dcl, &bits);}
+ | /*empty */
+ attributes(field_dcl)?
+ {zend_ffi_add_field(struct_dcl, name, name_len, field_dcl);}
+ )
+ | ":"
+ constant_expression(&bits)
+ {zend_ffi_add_bit_field(struct_dcl, NULL, 0, field_dcl, &bits);}
+ )
+;
+
+enum_specifier(zend_ffi_dcl *dcl):
+ "enum"
+ {dcl->flags |= ZEND_FFI_DCL_ENUM;}
+ attributes(dcl)?
+ ( {const char *name;}
+ {size_t name_len;}
+ ID(&name, &name_len)
+ ( {zend_ffi_declare_tag(name, name_len, dcl, 0);}
+ "{"
+ enumerator_list(dcl)
+ "}"
+ attributes(dcl)?+
+ | {zend_ffi_declare_tag(name, name_len, dcl, 1);}
+ )
+ | "{"
+ {zend_ffi_make_enum_type(dcl);}
+ enumerator_list(dcl)
+ "}"
+ attributes(dcl)?+
+ )
+;
+
+enumerator_list(zend_ffi_dcl *enum_dcl):
+ {int64_t min = 0, max = 0, last = -1;}
+ enumerator(enum_dcl, &min, &max, &last)
+ ( ","
+ enumerator(enum_dcl, &min, &max, &last)
+ )*
+ ","?
+;
+
+enumerator(zend_ffi_dcl *enum_dcl, int64_t *min, int64_t *max, int64_t *last):
+ {const char *name;}
+ {size_t name_len;}
+ {zend_ffi_val val = {.kind = ZEND_FFI_VAL_EMPTY};}
+ ID(&name, &name_len)
+ ( "="
+ constant_expression(&val)
+ )?
+ {zend_ffi_add_enum_val(enum_dcl, name, name_len, &val, min, max, last);}
+;
+
+declarator(zend_ffi_dcl *dcl, const char **name, size_t *name_len):
+ /* "char" is used as a terminator of nested declaration */
+ {zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};}
+ {zend_bool nested = 0;}
+ pointer(dcl)?
+ ( ID(name, name_len)
+ | "("
+ attributes(&nested_dcl)?
+ declarator(&nested_dcl, name, name_len)
+ ")"
+ {nested = 1;}
+ )
+ array_or_function_declarators(dcl)?
+ {if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);}
+;
+
+abstract_declarator(zend_ffi_dcl *dcl, const char **name, size_t *name_len):
+ /* "char" is used as a terminator of nested declaration */
+ {zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};}
+ {zend_bool nested = 0;}
+ pointer(dcl)?
+ ( &nested_abstract_declarator
+ nested_abstract_declarator(&nested_dcl, name, name_len)
+ {nested = 1;}
+ | ID(name, name_len)
+ | /* empty */
+ )
+ array_or_function_declarators(dcl)?
+ {if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);}
+;
+
+nested_abstract_declarator(zend_ffi_dcl *dcl, const char **name, size_t *name_len):
+ {zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};}
+ {zend_bool nested = 0;}
+ "("
+ attributes(&nested_dcl)?
+ ( pointer(dcl)
+ ( &nested_abstract_declarator
+ nested_abstract_declarator(&nested_dcl, name, name_len)
+ {nested = 1;}
+ | ID(name, name_len)
+ | /* empty */
+ )
+ array_or_function_declarators(dcl)?
+ | ( &nested_abstract_declarator
+ nested_abstract_declarator(&nested_dcl, name, name_len)
+ array_or_function_declarators(dcl)?
+ {nested = 1;}
+ | ID(name, name_len)
+ array_or_function_declarators(dcl)?
+ | array_or_function_declarators(dcl)
+ )
+ )
+ ")"
+ {if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);}
+;
+
+pointer(zend_ffi_dcl *dcl):
+ ( "*"
+ {zend_ffi_make_pointer_type(dcl);}
+ type_qualifier_list(dcl)?
+ )+
+;
+
+array_or_function_declarators(zend_ffi_dcl *dcl):
+ {zend_ffi_dcl dummy = ZEND_FFI_ATTR_INIT;}
+ {zend_ffi_val len = {.kind = ZEND_FFI_VAL_EMPTY};}
+ {HashTable *args = NULL;}
+ {uint32_t attr = 0;}
+ ( "["
+ ( "static"
+ type_qualifier_list(&dummy)?
+ assignment_expression(&len)
+ | type_qualifier_list(&dummy)
+ ( "static" assignment_expression(&len)
+ | /* empty */
+ {attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;}
+ | "*"
+ {attr |= ZEND_FFI_ATTR_VLA;}
+ | assignment_expression(&len)
+ )
+ | ( /* empty */
+ {attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;}
+ | "*"
+ {attr |= ZEND_FFI_ATTR_VLA;}
+ | assignment_expression(&len)
+ )
+ )
+ "]"
+ array_or_function_declarators(dcl)?
+ {dcl->attr |= attr;}
+ {zend_ffi_make_array_type(dcl, &len);}
+ | "("
+ (
+ parameter_declaration(&args)
+ ( ","
+ parameter_declaration(&args)
+ )*
+ (
+ ","
+ "..."
+ {attr |= ZEND_FFI_ATTR_VARIADIC;}
+ )?
+ | "..."
+ {attr |= ZEND_FFI_ATTR_VARIADIC;}
+ )?
+ ")"
+ array_or_function_declarators(dcl)?
+ {dcl->attr |= attr;}
+ {zend_ffi_make_func_type(dcl, args);}
+// | "(" (ID ("," ID)*)? ")" // TODO: ANSI function not-implemented ???
+ )
+;
+
+parameter_declaration(HashTable **args):
+ {const char *name = NULL;}
+ {size_t name_len = 0;}
+ {zend_bool old_allow_vla = FFI_G(allow_vla);}
+ {FFI_G(allow_vla) = 1;}
+ {zend_ffi_dcl param_dcl = ZEND_FFI_ATTR_INIT;}
+ specifier_qualifier_list(¶m_dcl)
+ abstract_declarator(¶m_dcl, &name, &name_len)
+ /*attributes(¶m_dcl)? conflict ???*/
+ {zend_ffi_add_arg(args, name, name_len, ¶m_dcl);}
+ {FFI_G(allow_vla) = old_allow_vla;}
+;
+
+type_name(zend_ffi_dcl *dcl):
+ {const char *name = NULL;}
+ {size_t name_len = 0;}
+ specifier_qualifier_list(dcl)
+ abstract_declarator(dcl, &name, &name_len)
+;
+
+attributes(zend_ffi_dcl *dcl):
+ {const char *name;}
+ {size_t name_len;}
+ {zend_ffi_val val;}
+ (
+ ("__attribute"|"__attribute__")
+ "("
+ "("
+ attrib(dcl)
+ ( ","
+ attrib(dcl)
+ )*
+ ")"
+ ")"
+ | "__declspec"
+ "("
+ ( ID(&name, &name_len)
+ (
+ "("
+ assignment_expression(&val)
+ {zend_ffi_add_msvc_attribute_value(dcl, name, name_len, &val);}
+ ")"
+ )?
+ )+
+ ")"
+ )++
+;
+
+attrib(zend_ffi_dcl *dcl):
+ {const char *name;}
+ {size_t name_len;}
+ {int n;}
+ {zend_ffi_val val;}
+ ( ID(&name, &name_len)
+ ( /* empty */
+ {zend_ffi_add_attribute(dcl, name, name_len);}
+ | "("
+ assignment_expression(&val)
+ {zend_ffi_add_attribute_value(dcl, name, name_len, 0, &val);}
+ {n = 0;}
+ ( ","
+ assignment_expression(&val)
+ {zend_ffi_add_attribute_value(dcl, name, name_len, ++n, &val);}
+ )*
+ ")"
+ )
+ )?
+;
+
+initializer:
+ {zend_ffi_val dummy;}
+ "="
+ ( assignment_expression(&dummy)
+ | "{" designation? initializer ( "," designation? initializer)* ","? "}"
+ )
+;
+
+designation:
+ {const char *name;}
+ {size_t name_len;}
+ {zend_ffi_val dummy;}
+ ( "[" constant_expression(&dummy) "]"
+ | "." ID(&name, &name_len)
+ )+
+ "="
+;
+
+expr_list:
+ {zend_ffi_val dummy;}
+ assignment_expression(&dummy)
+ ( ","
+ assignment_expression(&dummy)
+ )*
+;
+
+expression(zend_ffi_val *val):
+ assignment_expression(val)
+ ( ","
+ assignment_expression(val)
+ )*
+;
+
+assignment_expression(zend_ffi_val *val):
+// ( unary_expression
+// ("="|"*="|"/="|"%="|"+="|"-="|"<<="|">>="|"&="|"^="|"|=")
+// )* // TODO: not-implemented ???
+ conditional_expression(val)
+;
+
+constant_expression(zend_ffi_val *val):
+ conditional_expression(val)
+;
+
+conditional_expression(zend_ffi_val *val):
+ {zend_ffi_val op2, op3;}
+ logical_or_expression(val)
+ ( "?"
+ expression(&op2)
+ ":"
+ conditional_expression(&op3)
+ {zend_ffi_expr_conditional(val, &op2, &op3);}
+ )?
+;
+
+logical_or_expression(zend_ffi_val *val):
+ {zend_ffi_val op2;}
+ logical_and_expression(val)
+ ( "||"
+ logical_and_expression(&op2)
+ {zend_ffi_expr_bool_or(val, &op2);}
+ )*
+;
+
+logical_and_expression(zend_ffi_val *val):
+ {zend_ffi_val op2;}
+ inclusive_or_expression(val)
+ ( "&&"
+ inclusive_or_expression(&op2)
+ {zend_ffi_expr_bool_and(val, &op2);}
+ )*
+;
+
+inclusive_or_expression(zend_ffi_val *val):
+ {zend_ffi_val op2;}
+ exclusive_or_expression(val)
+ ( "|"
+ exclusive_or_expression(&op2)
+ {zend_ffi_expr_bw_or(val, &op2);}
+ )*
+;
+
+exclusive_or_expression(zend_ffi_val *val):
+ {zend_ffi_val op2;}
+ and_expression(val)
+ ( "^"
+ and_expression(&op2)
+ {zend_ffi_expr_bw_xor(val, &op2);}
+ )*
+;
+
+and_expression(zend_ffi_val *val):
+ {zend_ffi_val op2;}
+ equality_expression(val)
+ ( "&"
+ equality_expression(&op2)
+ {zend_ffi_expr_bw_and(val, &op2);}
+ )*
+;
+
+equality_expression(zend_ffi_val *val):
+ {zend_ffi_val op2;}
+ relational_expression(val)
+ ( "=="
+ relational_expression(&op2)
+ {zend_ffi_expr_is_equal(val, &op2);}
+ | "!="
+ relational_expression(&op2)
+ {zend_ffi_expr_is_not_equal(val, &op2);}
+ )*
+;
+
+relational_expression(zend_ffi_val *val):
+ {zend_ffi_val op2;}
+ shift_expression(val)
+ ( "<"
+ shift_expression(&op2)
+ {zend_ffi_expr_is_less(val, &op2);}
+ | ">"
+ shift_expression(&op2)
+ {zend_ffi_expr_is_greater(val, &op2);}
+ | "<="
+ shift_expression(&op2)
+ {zend_ffi_expr_is_less_or_equal(val, &op2);}
+ | ">="
+ shift_expression(&op2)
+ {zend_ffi_expr_is_greater_or_equal(val, &op2);}
+ )*
+;
+
+shift_expression(zend_ffi_val *val):
+ {zend_ffi_val op2;}
+ additive_expression(val)
+ ( "<<"
+ additive_expression(&op2)
+ {zend_ffi_expr_shift_left(val, &op2);}
+ | ">>"
+ additive_expression(&op2)
+ {zend_ffi_expr_shift_right(val, &op2);}
+ )*
+;
+
+additive_expression(zend_ffi_val *val):
+ {zend_ffi_val op2;}
+ multiplicative_expression(val)
+ ( "+"
+ multiplicative_expression(&op2)
+ {zend_ffi_expr_add(val, &op2);}
+ | "-"
+ multiplicative_expression(&op2)
+ {zend_ffi_expr_sub(val, &op2);}
+ )*
+;
+
+multiplicative_expression(zend_ffi_val *val):
+ {zend_ffi_val op2;}
+ cast_expression(val)
+ ( "*"
+ cast_expression(&op2)
+ {zend_ffi_expr_mul(val, &op2);}
+ | "/"
+ cast_expression(&op2)
+ {zend_ffi_expr_div(val, &op2);}
+ | "%"
+ cast_expression(&op2)
+ {zend_ffi_expr_mod(val, &op2);}
+ )*
+;
+
+cast_expression(zend_ffi_val *val):
+ {int do_cast = 0;}
+ {zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;}
+ ( &( "(" type_name ")" )
+ "("
+ type_name(&dcl)
+ ")"
+ {do_cast = 1;}
+ )?
+ unary_expression(val)
+ {if (do_cast) zend_ffi_expr_cast(val, &dcl);}
+;
+
+unary_expression(zend_ffi_val *val):
+ {const char *name;}
+ {size_t name_len;}
+ {zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;}
+ ( ID(&name, &name_len)
+ {zend_ffi_resolve_const(name, name_len, val);}
+ (
+ ( "["
+ expr_list
+ "]"
+ | "("
+ expr_list?
+ ")"
+ | "."
+ ID(&name, &name_len)
+ | "->"
+ ID(&name, &name_len)
+ | "++"
+ | "--"
+ )
+ {zend_ffi_val_error(val);}
+ )*
+ | OCTNUMBER(val)
+ | DECNUMBER(val)
+ | HEXNUMBER(val)
+ | FLOATNUMBER(val)
+ | STRING(val)
+ | CHARACTER(val)
+ | "("
+ expression(val)
+ ")"
+ | "++"
+ unary_expression(val)
+ {zend_ffi_val_error(val);}
+ | "--"
+ unary_expression(val)
+ {zend_ffi_val_error(val);}
+ | "&"
+ cast_expression(val)
+ {zend_ffi_val_error(val);}
+ | "*"
+ cast_expression(val)
+ {zend_ffi_val_error(val);}
+ | "+"
+ cast_expression(val)
+ {zend_ffi_expr_plus(val);}
+ | "-"
+ cast_expression(val)
+ {zend_ffi_expr_neg(val);}
+ | "~"
+ cast_expression(val)
+ {zend_ffi_expr_bw_not(val);}
+ | "!"
+ cast_expression(val)
+ {zend_ffi_expr_bool_not(val);}
+ | "sizeof"
+ ( &( "(" type_name ")" )
+ "("
+ type_name(&dcl)
+ ")"
+ {zend_ffi_expr_sizeof_type(val, &dcl);}
+ | unary_expression(val)
+ {zend_ffi_expr_sizeof_val(val);}
+ )
+ | "_Alignof"
+ "("
+ type_name(&dcl)
+ ")"
+ {zend_ffi_expr_alignof_type(val, &dcl);}
+ | ("__alignof"|"__alignof__")
+ ( &( "(" type_name ")" )
+ "("
+ type_name(&dcl)
+ ")"
+ {zend_ffi_expr_alignof_type(val, &dcl);}
+ | unary_expression(val)
+ {zend_ffi_expr_alignof_val(val);}
+ )
+ )
+;
+
+ID(const char **name, size_t *name_len):
+ /[A-Za-z_][A-Za-z_0-9]*/
+ {*name = (const char*)yy_text; *name_len = yy_pos - yy_text;}
+;
+
+OCTNUMBER(zend_ffi_val *val):
+ /0[0-7]*([Uu](L|l|LL|l)?|[Ll][Uu]?|(LL|ll)[Uu])?/
+ {zend_ffi_val_number(val, 8, (const char*)yy_text, yy_pos - yy_text);}
+;
+
+DECNUMBER(zend_ffi_val *val):
+ /[1-9][0-9]*([Uu](L|l|LL|l)?|[Ll][Uu]?|(LL|ll)[Uu])?/
+ {zend_ffi_val_number(val, 10, (const char*)yy_text, yy_pos - yy_text);}
+;
+
+HEXNUMBER(zend_ffi_val *val):
+ /0[xX][0-9A-Fa-f][0-9A-Fa-f]*([Uu](L|l|LL|l)?|[Ll][Uu]?|(LL|ll)[Uu])?/
+ {zend_ffi_val_number(val, 16, (const char*)yy_text + 2, yy_pos - yy_text - 2);}
+;
+
+FLOATNUMBER(zend_ffi_val *val):
+ /([0-9]*\.[0-9]+([Ee][\+\-]?[0-9]+)?|[0-9]+\.([Ee][\+\-]?[0-9]+)?|[0-9]+[Ee][\+\-]?[0-9]+)[flFL]?/
+ {zend_ffi_val_float_number(val, (const char*)yy_text, yy_pos - yy_text);}
+;
+
+STRING(zend_ffi_val *val):
+ /(u8|u|U|L)?"([^"\\]|\\.)*"/
+ {zend_ffi_val_string(val, (const char*)yy_text, yy_pos - yy_text);}
+;
+
+CHARACTER(zend_ffi_val *val):
+ /[LuU]?'([^'\\]|\\.)*'/
+ {zend_ffi_val_character(val, (const char*)yy_text, yy_pos - yy_text);}
+;
+
+EOL: /\r\n|\r|\n/;
+WS: /[ \t\f\v]+/;
+ONE_LINE_COMMENT: /(\/\/|#)[^\r\n]*(\r\n|\r|\n)/;
+COMMENT: /\/\*([^\*]|\*+[^\*\/])*\*+\//;
+
+SKIP: ( EOL | WS | ONE_LINE_COMMENT | COMMENT )*;
+
+%%
+int zend_ffi_parse_decl(const char *str, size_t len) {
+ if (SETJMP(FFI_G(bailout))==0) {
+ FFI_G(allow_vla) = 0;
+ yy_buf = (unsigned char*)str;
+ yy_end = yy_buf + len;
+ parse();
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
+}
+
+int zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl) {
+ int sym;
+
+ if (SETJMP(FFI_G(bailout))==0) {
+ FFI_G(allow_vla) = 0;
+ yy_pos = yy_text = yy_buf = (unsigned char*)str;
+ yy_end = yy_buf + len;
+ yy_line = 1;
+ sym = parse_type_name(get_sym(), dcl);
+ if (sym != YY_EOF) {
+ yy_error_sym("<EOF> expected, got", sym);
+ }
+ zend_ffi_validate_type_name(dcl);
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ };
+}
+
+static void yy_error(const char *msg) {
+ zend_ffi_parser_error("%s at line %d", msg, yy_line);
+}
+
+static void yy_error_sym(const char *msg, int sym) {
+ zend_ffi_parser_error("%s '%s' at line %d", msg, sym_name[sym], yy_line);
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2018 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ffi.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define yy_buf FFI_G(buf)
+#define yy_end FFI_G(end)
+#define yy_pos FFI_G(pos)
+#define yy_text FFI_G(text)
+#define yy_line FFI_G(line)
+
+/* forward declarations */
+static void yy_error(const char *msg);
+static void yy_error_sym(const char *msg, int sym);
+
+#define YYPOS cpos
+#define YYEND cend
+
+#define YY_EOF 0
+#define YY__COMMA 1
+#define YY__SEMICOLON 2
+#define YY_TYPEDEF 3
+#define YY_EXTERN 4
+#define YY_STATIC 5
+#define YY_AUTO 6
+#define YY_REGISTER 7
+#define YY_INLINE 8
+#define YY___INLINE 9
+#define YY___INLINE__ 10
+#define YY__NORETURN 11
+#define YY___CDECL 12
+#define YY___STDCALL 13
+#define YY___FASTCALL 14
+#define YY___THISCALL 15
+#define YY__ALIGNAS 16
+#define YY__LPAREN 17
+#define YY__RPAREN 18
+#define YY_CONST 19
+#define YY___CONST 20
+#define YY___CONST__ 21
+#define YY_RESTRICT 22
+#define YY___RESTICT 23
+#define YY___RESTRICT__ 24
+#define YY_VOLATILE 25
+#define YY___VOLATILE 26
+#define YY___VOLATILE__ 27
+#define YY__ATOMIC 28
+#define YY_VOID 29
+#define YY_CHAR 30
+#define YY_SHORT 31
+#define YY_INT 32
+#define YY_LONG 33
+#define YY_FLOAT 34
+#define YY_DOUBLE 35
+#define YY_SIGNED 36
+#define YY_UNSIGNED 37
+#define YY__BOOL 38
+#define YY__COMPLEX 39
+#define YY_COMPLEX 40
+#define YY___COMPLEX 41
+#define YY___COMPLEX__ 42
+#define YY_STRUCT 43
+#define YY_UNION 44
+#define YY__LBRACE 45
+#define YY__RBRACE 46
+#define YY__COLON 47
+#define YY_ENUM 48
+#define YY__EQUAL 49
+#define YY__STAR 50
+#define YY__LBRACK 51
+#define YY__RBRACK 52
+#define YY__POINT_POINT_POINT 53
+#define YY___ATTRIBUTE 54
+#define YY___ATTRIBUTE__ 55
+#define YY___DECLSPEC 56
+#define YY__POINT 57
+#define YY__QUERY 58
+#define YY__BAR_BAR 59
+#define YY__AND_AND 60
+#define YY__BAR 61
+#define YY__UPARROW 62
+#define YY__AND 63
+#define YY__EQUAL_EQUAL 64
+#define YY__BANG_EQUAL 65
+#define YY__LESS 66
+#define YY__GREATER 67
+#define YY__LESS_EQUAL 68
+#define YY__GREATER_EQUAL 69
+#define YY__LESS_LESS 70
+#define YY__GREATER_GREATER 71
+#define YY__PLUS 72
+#define YY__MINUS 73
+#define YY__SLASH 74
+#define YY__PERCENT 75
+#define YY__MINUS_GREATER 76
+#define YY__PLUS_PLUS 77
+#define YY__MINUS_MINUS 78
+#define YY__TILDE 79
+#define YY__BANG 80
+#define YY_SIZEOF 81
+#define YY__ALIGNOF 82
+#define YY___ALIGNOF 83
+#define YY___ALIGNOF__ 84
+#define YY_ID 85
+#define YY_OCTNUMBER 86
+#define YY_DECNUMBER 87
+#define YY_HEXNUMBER 88
+#define YY_FLOATNUMBER 89
+#define YY_STRING 90
+#define YY_CHARACTER 91
+#define YY_EOL 92
+#define YY_WS 93
+#define YY_ONE_LINE_COMMENT 94
+#define YY_COMMENT 95
+
+static const char * sym_name[] = {
+ "<EOF>",
+ ",",
+ ";",
+ "typedef",
+ "extern",
+ "static",
+ "auto",
+ "register",
+ "inline",
+ "__inline",
+ "__inline__",
+ "_Noreturn",
+ "__cdecl",
+ "__stdcall",
+ "__fastcall",
+ "__thiscall",
+ "_Alignas",
+ "(",
+ ")",
+ "const",
+ "__const",
+ "__const__",
+ "restrict",
+ "__restict",
+ "__restrict__",
+ "volatile",
+ "__volatile",
+ "__volatile__",
+ "_Atomic",
+ "void",
+ "char",
+ "short",
+ "int",
+ "long",
+ "float",
+ "double",
+ "signed",
+ "unsigned",
+ "_Bool",
+ "_Complex",
+ "complex",
+ "__complex",
+ "__complex__",
+ "struct",
+ "union",
+ "{",
+ "}",
+ ":",
+ "enum",
+ "=",
+ "*",
+ "[",
+ "]",
+ "...",
+ "__attribute",
+ "__attribute__",
+ "__declspec",
+ ".",
+ "?",
+ "||",
+ "&&",
+ "|",
+ "^",
+ "&",
+ "==",
+ "!=",
+ "<",
+ ">",
+ "<=",
+ ">=",
+ "<<",
+ ">>",
+ "+",
+ "-",
+ "/",
+ "%",
+ "->",
+ "++",
+ "--",
+ "~",
+ "!",
+ "sizeof",
+ "_Alignof",
+ "__alignof",
+ "__alignof__",
+ "<ID>",
+ "<OCTNUMBER>",
+ "<DECNUMBER>",
+ "<HEXNUMBER>",
+ "<FLOATNUMBER>",
+ "<STRING>",
+ "<CHARACTER>",
+ "<EOL>",
+ "<WS>",
+ "<ONE_LINE_COMMENT>",
+ "<COMMENT>",
+ NULL
+};
+
+#define YY_IN_SET(sym, set, bitset) \
+ (bitset[sym>>3] & (1 << (sym & 0x7)))
+
+static int skip_EOL(int sym);
+static int skip_WS(int sym);
+static int skip_ONE_LINE_COMMENT(int sym);
+static int skip_COMMENT(int sym);
+static int get_sym(void);
+static int check_specifier_qualifier_list(int sym);
+static int check_type_qualifier_list(int sym);
+static int check_type_qualifier(int sym);
+static int check_type_specifier(int sym);
+static int check_struct_or_union_specifier(int sym);
+static int check_struct_contents(int sym);
+static int check_struct_declaration(int sym);
+static int check_struct_declarator(int sym);
+static int check_enum_specifier(int sym);
+static int check_enumerator_list(int sym);
+static int check_enumerator(int sym);
+static int check_declarator(int sym);
+static int check_abstract_declarator(int sym);
+static int check_nested_abstract_declarator(int sym);
+static int check_pointer(int sym);
+static int check_array_or_function_declarators(int sym);
+static int check_parameter_declaration(int sym);
+static int check_type_name(int sym);
+static int check_attributes(int sym);
+static int check_attrib(int sym);
+static int check_expr_list(int sym);
+static int check_expression(int sym);
+static int check_assignment_expression(int sym);
+static int check_constant_expression(int sym);
+static int check_conditional_expression(int sym);
+static int check_logical_or_expression(int sym);
+static int check_logical_and_expression(int sym);
+static int check_inclusive_or_expression(int sym);
+static int check_exclusive_or_expression(int sym);
+static int check_and_expression(int sym);
+static int check_equality_expression(int sym);
+static int check_relational_expression(int sym);
+static int check_shift_expression(int sym);
+static int check_additive_expression(int sym);
+static int check_multiplicative_expression(int sym);
+static int check_cast_expression(int sym);
+static int check_unary_expression(int sym);
+static int check_ID(int sym);
+static int check_OCTNUMBER(int sym);
+static int check_DECNUMBER(int sym);
+static int check_HEXNUMBER(int sym);
+static int check_FLOATNUMBER(int sym);
+static int check_STRING(int sym);
+static int check_CHARACTER(int sym);
+static int parse_declarations(int sym);
+static int parse_declaration_specifiers(int sym, zend_ffi_dcl *dcl);
+static int parse_specifier_qualifier_list(int sym, zend_ffi_dcl *dcl);
+static int parse_type_qualifier_list(int sym, zend_ffi_dcl *dcl);
+static int parse_type_qualifier(int sym, zend_ffi_dcl *dcl);
+static int parse_type_specifier(int sym, zend_ffi_dcl *dcl);
+static int parse_struct_or_union_specifier(int sym, zend_ffi_dcl *dcl);
+static int parse_struct_contents(int sym, zend_ffi_dcl *dcl);
+static int parse_struct_declaration(int sym, zend_ffi_dcl *struct_dcl);
+static int parse_struct_declarator(int sym, zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl);
+static int parse_enum_specifier(int sym, zend_ffi_dcl *dcl);
+static int parse_enumerator_list(int sym, zend_ffi_dcl *enum_dcl);
+static int parse_enumerator(int sym, zend_ffi_dcl *enum_dcl, int64_t *min, int64_t *max, int64_t *last);
+static int parse_declarator(int sym, zend_ffi_dcl *dcl, const char **name, size_t *name_len);
+static int parse_abstract_declarator(int sym, zend_ffi_dcl *dcl, const char **name, size_t *name_len);
+static int parse_nested_abstract_declarator(int sym, zend_ffi_dcl *dcl, const char **name, size_t *name_len);
+static int parse_pointer(int sym, zend_ffi_dcl *dcl);
+static int parse_array_or_function_declarators(int sym, zend_ffi_dcl *dcl);
+static int parse_parameter_declaration(int sym, HashTable **args);
+static int parse_type_name(int sym, zend_ffi_dcl *dcl);
+static int parse_attributes(int sym, zend_ffi_dcl *dcl);
+static int parse_attrib(int sym, zend_ffi_dcl *dcl);
+static int parse_initializer(int sym);
+static int parse_designation(int sym);
+static int parse_expr_list(int sym);
+static int parse_expression(int sym, zend_ffi_val *val);
+static int parse_assignment_expression(int sym, zend_ffi_val *val);
+static int parse_constant_expression(int sym, zend_ffi_val *val);
+static int parse_conditional_expression(int sym, zend_ffi_val *val);
+static int parse_logical_or_expression(int sym, zend_ffi_val *val);
+static int parse_logical_and_expression(int sym, zend_ffi_val *val);
+static int parse_inclusive_or_expression(int sym, zend_ffi_val *val);
+static int parse_exclusive_or_expression(int sym, zend_ffi_val *val);
+static int parse_and_expression(int sym, zend_ffi_val *val);
+static int parse_equality_expression(int sym, zend_ffi_val *val);
+static int parse_relational_expression(int sym, zend_ffi_val *val);
+static int parse_shift_expression(int sym, zend_ffi_val *val);
+static int parse_additive_expression(int sym, zend_ffi_val *val);
+static int parse_multiplicative_expression(int sym, zend_ffi_val *val);
+static int parse_cast_expression(int sym, zend_ffi_val *val);
+static int parse_unary_expression(int sym, zend_ffi_val *val);
+static int parse_ID(int sym, const char **name, size_t *name_len);
+static int parse_OCTNUMBER(int sym, zend_ffi_val *val);
+static int parse_DECNUMBER(int sym, zend_ffi_val *val);
+static int parse_HEXNUMBER(int sym, zend_ffi_val *val);
+static int parse_FLOATNUMBER(int sym, zend_ffi_val *val);
+static int parse_STRING(int sym, zend_ffi_val *val);
+static int parse_CHARACTER(int sym, zend_ffi_val *val);
+static int synpred_1(int sym);
+static int synpred_2(int sym);
+static int synpred_3(int sym);
+static int synpred_4(int sym);
+static int synpred_5(int sym);
+static int synpred_6(int sym);
+static int synpred_7(int sym);
+
+static int get_skip_sym(void) {
+ int ch;
+ int ret;
+ int accept = -1;
+ const unsigned char *accept_pos;
+ const unsigned char *cpos = yy_pos;
+ const unsigned char *cend = yy_end;
+
+_yy_state_start:
+ yy_text = YYPOS;
+ ch = *YYPOS;
+ switch (ch) {
+ case 't':
+ ch = *++YYPOS;
+ if (ch != 'y') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'p') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'd') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'f') goto _yy_tunnel_15;
+ ret = YY_TYPEDEF;
+ goto _yy_state_366;
+ case 'e':
+ ch = *++YYPOS;
+ if (ch == 'x') {
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'r') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ret = YY_EXTERN;
+ goto _yy_state_366;
+ } else if (ch == 'n') {
+ ch = *++YYPOS;
+ if (ch != 'u') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'm') goto _yy_tunnel_15;
+ ret = YY_ENUM;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case 's':
+ ch = *++YYPOS;
+ if (ch == 't') {
+ ch = *++YYPOS;
+ if (ch == 'a') {
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ret = YY_STATIC;
+ goto _yy_state_366;
+ } else if (ch == 'r') {
+ ch = *++YYPOS;
+ if (ch != 'u') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ret = YY_STRUCT;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ } else if (ch == 'h') {
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'r') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ret = YY_SHORT;
+ goto _yy_state_366;
+ } else if (ch == 'i') {
+ ch = *++YYPOS;
+ if (ch == 'g') {
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'd') goto _yy_tunnel_15;
+ ret = YY_SIGNED;
+ goto _yy_state_366;
+ } else if (ch == 'z') {
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'f') goto _yy_tunnel_15;
+ ret = YY_SIZEOF;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case 'a':
+ ch = *++YYPOS;
+ if (ch != 'u') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ret = YY_AUTO;
+ goto _yy_state_366;
+ case 'r':
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch == 'g') {
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 's') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'r') goto _yy_tunnel_15;
+ ret = YY_REGISTER;
+ goto _yy_state_366;
+ } else if (ch == 's') {
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'r') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ret = YY_RESTRICT;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case 'i':
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch == 'l') {
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ret = YY_INLINE;
+ goto _yy_state_366;
+ } else if (ch == 't') {
+ ret = YY_INT;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case '_':
+ ch = *++YYPOS;
+ switch (ch) {
+ case '_':
+ ch = *++YYPOS;
+ switch (ch) {
+ case 'i':
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != '_') {ret = YY___INLINE; goto _yy_tunnel_366;}
+ ch = *++YYPOS;
+ if (ch != '_') goto _yy_tunnel_15;
+ ret = YY___INLINE__;
+ goto _yy_state_366;
+ case 'c':
+ ch = *++YYPOS;
+ if (ch == 'd') {
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ret = YY___CDECL;
+ goto _yy_state_366;
+ } else if (ch == 'o') {
+ ch = *++YYPOS;
+ if (ch == 'm') {
+ ch = *++YYPOS;
+ if (ch != 'p') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'x') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != '_') {ret = YY___COMPLEX; goto _yy_tunnel_366;}
+ ch = *++YYPOS;
+ if (ch != '_') goto _yy_tunnel_15;
+ ret = YY___COMPLEX__;
+ goto _yy_state_366;
+ } else if (ch == 'n') {
+ ch = *++YYPOS;
+ if (ch != 's') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != '_') {ret = YY___CONST; goto _yy_tunnel_366;}
+ ch = *++YYPOS;
+ if (ch != '_') goto _yy_tunnel_15;
+ ret = YY___CONST__;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case 's':
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'd') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'a') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ret = YY___STDCALL;
+ goto _yy_state_366;
+ case 'f':
+ ch = *++YYPOS;
+ if (ch != 'a') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 's') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'a') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ret = YY___FASTCALL;
+ goto _yy_state_366;
+ case 't':
+ ch = *++YYPOS;
+ if (ch != 'h') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 's') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'a') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ret = YY___THISCALL;
+ goto _yy_state_366;
+ case 'a':
+ ch = *++YYPOS;
+ if (ch == 't') {
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'r') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'b') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'u') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != '_') {ret = YY___ATTRIBUTE; goto _yy_tunnel_366;}
+ ch = *++YYPOS;
+ if (ch != '_') goto _yy_tunnel_15;
+ ret = YY___ATTRIBUTE__;
+ goto _yy_state_366;
+ } else if (ch == 'l') {
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'g') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'f') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != '_') {ret = YY___ALIGNOF; goto _yy_tunnel_366;}
+ ch = *++YYPOS;
+ if (ch != '_') goto _yy_tunnel_15;
+ ret = YY___ALIGNOF__;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case 'd':
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 's') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'p') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ret = YY___DECLSPEC;
+ goto _yy_state_366;
+ case 'r':
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 's') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch == 'i') {
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ret = YY___RESTICT;
+ goto _yy_state_366;
+ } else if (ch == 'r') {
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != '_') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != '_') goto _yy_tunnel_15;
+ ret = YY___RESTRICT__;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case 'v':
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'a') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != '_') {ret = YY___VOLATILE; goto _yy_tunnel_366;}
+ ch = *++YYPOS;
+ if (ch != '_') goto _yy_tunnel_15;
+ ret = YY___VOLATILE__;
+ goto _yy_state_366;
+ default:
+ goto _yy_tunnel_15;
+ }
+ case 'N':
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'r') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'u') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'r') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ret = YY__NORETURN;
+ goto _yy_state_366;
+ case 'A':
+ ch = *++YYPOS;
+ if (ch == 'l') {
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'g') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch == 'a') {
+ ch = *++YYPOS;
+ if (ch != 's') goto _yy_tunnel_15;
+ ret = YY__ALIGNAS;
+ goto _yy_state_366;
+ } else if (ch == 'o') {
+ ch = *++YYPOS;
+ if (ch != 'f') goto _yy_tunnel_15;
+ ret = YY__ALIGNOF;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ } else if (ch == 't') {
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'm') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'c') goto _yy_tunnel_15;
+ ret = YY__ATOMIC;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case 'B':
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ret = YY__BOOL;
+ goto _yy_state_366;
+ case 'C':
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'm') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'p') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'x') goto _yy_tunnel_15;
+ ret = YY__COMPLEX;
+ goto _yy_state_366;
+ default:
+ goto _yy_tunnel_15;
+ }
+ case '(':
+ YYPOS++;
+ ret = YY__LPAREN;
+ goto _yy_fin;
+ case 'v':
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch == 'i') {
+ ch = *++YYPOS;
+ if (ch != 'd') goto _yy_tunnel_15;
+ ret = YY_VOID;
+ goto _yy_state_366;
+ } else if (ch == 'l') {
+ ch = *++YYPOS;
+ if (ch != 'a') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ret = YY_VOLATILE;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case 'c':
+ ch = *++YYPOS;
+ if (ch == 'h') {
+ ch = *++YYPOS;
+ if (ch != 'a') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'r') goto _yy_tunnel_15;
+ ret = YY_CHAR;
+ goto _yy_state_366;
+ } else if (ch == 'o') {
+ ch = *++YYPOS;
+ if (ch == 'm') {
+ ch = *++YYPOS;
+ if (ch != 'p') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'x') goto _yy_tunnel_15;
+ ret = YY_COMPLEX;
+ goto _yy_state_366;
+ } else if (ch == 'n') {
+ ch = *++YYPOS;
+ if (ch != 's') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ret = YY_CONST;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case 'l':
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'g') goto _yy_tunnel_15;
+ ret = YY_LONG;
+ goto _yy_state_366;
+ case 'f':
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'a') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 't') goto _yy_tunnel_15;
+ ret = YY_FLOAT;
+ goto _yy_state_366;
+ case 'd':
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'u') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'b') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'l') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ret = YY_DOUBLE;
+ goto _yy_state_366;
+ case 'u':
+ ch = *++YYPOS;
+ if (ch == 'n') {
+ ch = *++YYPOS;
+ if (ch == 's') {
+ ch = *++YYPOS;
+ if (ch != 'i') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'g') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'e') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'd') goto _yy_tunnel_15;
+ ret = YY_UNSIGNED;
+ goto _yy_state_366;
+ } else if (ch == 'i') {
+ ch = *++YYPOS;
+ if (ch != 'o') goto _yy_tunnel_15;
+ ch = *++YYPOS;
+ if (ch != 'n') goto _yy_tunnel_15;
+ ret = YY_UNION;
+ goto _yy_state_366;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ } else if (ch == '8') {
+ ch = *++YYPOS;
+ if (ch != '"') goto _yy_tunnel_15;
+ goto _yy_state_27;
+ } else if (ch == '"') {
+ goto _yy_state_27;
+ } else if (ch == '\'') {
+ goto _yy_state_28;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case 'b':
+ case 'g':
+ case 'h':
+ case 'j':
+ case 'k':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ goto _yy_state_15;
+ case 'L':
+ case 'U':
+ ch = *++YYPOS;
+ if (ch == '"') {
+ goto _yy_state_27;
+ } else if (ch == '\'') {
+ goto _yy_state_28;
+ } else {
+ goto _yy_tunnel_15;
+ }
+ case ')':
+ YYPOS++;
+ ret = YY__RPAREN;
+ goto _yy_fin;
+ case '[':
+ YYPOS++;
+ ret = YY__LBRACK;
+ goto _yy_fin;
+ case ',':
+ YYPOS++;
+ ret = YY__COMMA;
+ goto _yy_fin;
+ case ']':
+ YYPOS++;
+ ret = YY__RBRACK;
+ goto _yy_fin;
+ case '.':
+ ch = *++YYPOS;
+ accept = YY__POINT;
+ accept_pos = yy_pos;
+ if ((ch >= '0' && ch <= '9')) {
+ goto _yy_state_73;
+ } else if (ch == '.') {
+ ch = *++YYPOS;
+ if (ch == '.') {
+ YYPOS++;
+ ret = YY__POINT_POINT_POINT;
+ goto _yy_fin;
+ } else {
+ goto _yy_state_error;
+ }
+ } else {
+ ret = YY__POINT;
+ goto _yy_fin;
+ }
+ case '-':
+ ch = *++YYPOS;
+ if (ch == '>') {
+ YYPOS++;
+ ret = YY__MINUS_GREATER;
+ goto _yy_fin;
+ } else if (ch == '-') {
+ YYPOS++;
+ ret = YY__MINUS_MINUS;
+ goto _yy_fin;
+ } else {
+ ret = YY__MINUS;
+ goto _yy_fin;
+ }
+ case '+':
+ ch = *++YYPOS;
+ if (ch == '+') {
+ YYPOS++;
+ ret = YY__PLUS_PLUS;
+ goto _yy_fin;
+ } else {
+ ret = YY__PLUS;
+ goto _yy_fin;
+ }
+ case '0':
+ ch = *++YYPOS;
+ if (ch != 'X' && ch != 'x') goto _yy_tunnel_78;
+ ch = *++YYPOS;
+ if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) {
+ goto _yy_state_155;
+ } else {
+ goto _yy_state_error;
+ }
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ goto _yy_state_26;
+ case '"':
+ goto _yy_state_27;
+ case '\'':
+ goto _yy_state_28;
+ case '&':
+ ch = *++YYPOS;
+ if (ch == '&') {
+ YYPOS++;
+ ret = YY__AND_AND;
+ goto _yy_fin;
+ } else {
+ ret = YY__AND;
+ goto _yy_fin;
+ }
+ case '*':
+ YYPOS++;
+ ret = YY__STAR;
+ goto _yy_fin;
+ case '~':
+ YYPOS++;
+ ret = YY__TILDE;
+ goto _yy_fin;
+ case '!':
+ ch = *++YYPOS;
+ if (ch == '=') {
+ YYPOS++;
+ ret = YY__BANG_EQUAL;
+ goto _yy_fin;
+ } else {
+ ret = YY__BANG;
+ goto _yy_fin;
+ }
+ case '/':
+ ch = *++YYPOS;
+ accept = YY__SLASH;
+ accept_pos = yy_pos;
+ if (ch == '*') {
+ goto _yy_state_99;
+ } else if (ch == '/') {
+ goto _yy_state_48;
+ } else {
+ ret = YY__SLASH;
+ goto _yy_fin;
+ }
+ case '%':
+ YYPOS++;
+ ret = YY__PERCENT;
+ goto _yy_fin;
+ case '<':
+ ch = *++YYPOS;
+ if (ch == '<') {
+ YYPOS++;
+ ret = YY__LESS_LESS;
+ goto _yy_fin;
+ } else if (ch == '=') {
+ YYPOS++;
+ ret = YY__LESS_EQUAL;
+ goto _yy_fin;
+ } else {
+ ret = YY__LESS;
+ goto _yy_fin;
+ }
+ case '>':
+ ch = *++YYPOS;
+ if (ch == '>') {
+ YYPOS++;
+ ret = YY__GREATER_GREATER;
+ goto _yy_fin;
+ } else if (ch == '=') {
+ YYPOS++;
+ ret = YY__GREATER_EQUAL;
+ goto _yy_fin;
+ } else {
+ ret = YY__GREATER;
+ goto _yy_fin;
+ }
+ case '=':
+ ch = *++YYPOS;
+ if (ch == '=') {
+ YYPOS++;
+ ret = YY__EQUAL_EQUAL;
+ goto _yy_fin;
+ } else {
+ ret = YY__EQUAL;
+ goto _yy_fin;
+ }
+ case '^':
+ YYPOS++;
+ ret = YY__UPARROW;
+ goto _yy_fin;
+ case '|':
+ ch = *++YYPOS;
+ if (ch == '|') {
+ YYPOS++;
+ ret = YY__BAR_BAR;
+ goto _yy_fin;
+ } else {
+ ret = YY__BAR;
+ goto _yy_fin;
+ }
+ case '?':
+ YYPOS++;
+ ret = YY__QUERY;
+ goto _yy_fin;
+ case ':':
+ YYPOS++;
+ ret = YY__COLON;
+ goto _yy_fin;
+ case '{':
+ YYPOS++;
+ ret = YY__LBRACE;
+ goto _yy_fin;
+ case ';':
+ YYPOS++;
+ ret = YY__SEMICOLON;
+ goto _yy_fin;
+ case '}':
+ YYPOS++;
+ ret = YY__RBRACE;
+ goto _yy_fin;
+ case '\r':
+ ch = *++YYPOS;
+ if (ch == '\n') {
+ yy_line++;
+ YYPOS++;
+ ret = YY_EOL;
+ goto _yy_fin;
+ } else {
+ ret = YY_EOL;
+ goto _yy_fin;
+ }
+ case '\n':
+ yy_line++;
+ YYPOS++;
+ ret = YY_EOL;
+ goto _yy_fin;
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\v':
+ goto _yy_state_47;
+ case '#':
+ goto _yy_state_48;
+ case '\0':
+ if (ch == 0 && YYPOS < YYEND) goto _yy_state_error;
+ YYPOS++;
+ ret = YY_EOF;
+ goto _yy_fin;
+ default:
+ goto _yy_state_error;
+ }
+_yy_state_15:
+ ch = *++YYPOS;
+_yy_tunnel_15:
+ if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || ch == '_' || (ch >= 'a' && ch <= 'z')) {
+ goto _yy_state_15;
+ } else {
+ ret = YY_ID;
+ goto _yy_fin;
+ }
+_yy_state_26:
+ ch = *++YYPOS;
+ accept = YY_DECNUMBER;
+ accept_pos = yy_pos;
+ switch (ch) {
+ case 'U':
+ case 'u':
+ ch = *++YYPOS;
+ if (ch == 'L') {
+ ch = *++YYPOS;
+ if (ch == 'L') {
+ YYPOS++;
+ ret = YY_DECNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_DECNUMBER;
+ goto _yy_fin;
+ }
+ } else if (ch == 'l') {
+ YYPOS++;
+ ret = YY_DECNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_DECNUMBER;
+ goto _yy_fin;
+ }
+ case 'L':
+ ch = *++YYPOS;
+ accept = YY_DECNUMBER;
+ accept_pos = yy_pos;
+ if (ch == 'L') {
+ goto _yy_state_162;
+ } else if (ch == 'U' || ch == 'u') {
+ YYPOS++;
+ ret = YY_DECNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_DECNUMBER;
+ goto _yy_fin;
+ }
+ case 'l':
+ ch = *++YYPOS;
+ accept = YY_DECNUMBER;
+ accept_pos = yy_pos;
+ if (ch == 'U' || ch == 'u') {
+ YYPOS++;
+ ret = YY_DECNUMBER;
+ goto _yy_fin;
+ } else if (ch == 'l') {
+ goto _yy_state_162;
+ } else {
+ ret = YY_DECNUMBER;
+ goto _yy_fin;
+ }
+ case 'E':
+ case 'e':
+ goto _yy_state_85;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ goto _yy_state_26;
+ case '.':
+ goto _yy_state_73;
+ default:
+ ret = YY_DECNUMBER;
+ goto _yy_fin;
+ }
+_yy_state_27:
+ ch = *++YYPOS;
+ if (ch == '\\') {
+ ch = *++YYPOS;
+ if (YYPOS < YYEND) {
+ if (ch == '\n') {
+ yy_line++;
+ }
+ goto _yy_state_27;
+ } else {
+ goto _yy_state_error;
+ }
+ } else if (ch == '"') {
+ YYPOS++;
+ ret = YY_STRING;
+ goto _yy_fin;
+ } else if (YYPOS < YYEND && (ch <= '!' || (ch >= '#' && ch <= '[') || ch >= ']')) {
+ if (ch == '\n') {
+ yy_line++;
+ }
+ goto _yy_state_27;
+ } else {
+ goto _yy_state_error;
+ }
+_yy_state_28:
+ ch = *++YYPOS;
+ if (ch == '\\') {
+ ch = *++YYPOS;
+ if (YYPOS < YYEND) {
+ if (ch == '\n') {
+ yy_line++;
+ }
+ goto _yy_state_28;
+ } else {
+ goto _yy_state_error;
+ }
+ } else if (ch == '\'') {
+ YYPOS++;
+ ret = YY_CHARACTER;
+ goto _yy_fin;
+ } else if (YYPOS < YYEND && (ch <= '&' || (ch >= '(' && ch <= '[') || ch >= ']')) {
+ if (ch == '\n') {
+ yy_line++;
+ }
+ goto _yy_state_28;
+ } else {
+ goto _yy_state_error;
+ }
+_yy_state_47:
+ ch = *++YYPOS;
+ if (ch == '\t' || ch == '\v' || ch == '\f' || ch == ' ') {
+ goto _yy_state_47;
+ } else {
+ ret = YY_WS;
+ goto _yy_fin;
+ }
+_yy_state_48:
+ ch = *++YYPOS;
+ if (ch == '\r') {
+ ch = *++YYPOS;
+ if (ch == '\n') {
+ yy_line++;
+ YYPOS++;
+ ret = YY_ONE_LINE_COMMENT;
+ goto _yy_fin;
+ } else {
+ ret = YY_ONE_LINE_COMMENT;
+ goto _yy_fin;
+ }
+ } else if (ch == '\n') {
+ yy_line++;
+ YYPOS++;
+ ret = YY_ONE_LINE_COMMENT;
+ goto _yy_fin;
+ } else if (YYPOS < YYEND && (ch <= '\t' || ch == '\v' || ch == '\f' || ch >= '\016')) {
+ goto _yy_state_48;
+ } else {
+ goto _yy_state_error;
+ }
+_yy_state_73:
+ ch = *++YYPOS;
+ accept = YY_FLOATNUMBER;
+ accept_pos = yy_pos;
+ if ((ch >= '0' && ch <= '9')) {
+ goto _yy_state_73;
+ } else if (ch == 'F' || ch == 'L' || ch == 'f' || ch == 'l') {
+ YYPOS++;
+ ret = YY_FLOATNUMBER;
+ goto _yy_fin;
+ } else if (ch == 'E' || ch == 'e') {
+ goto _yy_state_85;
+ } else {
+ ret = YY_FLOATNUMBER;
+ goto _yy_fin;
+ }
+_yy_state_78:
+ ch = *++YYPOS;
+_yy_tunnel_78:
+ accept = YY_OCTNUMBER;
+ accept_pos = yy_pos;
+ switch (ch) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ goto _yy_state_78;
+ case 'U':
+ case 'u':
+ ch = *++YYPOS;
+ if (ch == 'L') {
+ ch = *++YYPOS;
+ if (ch == 'L') {
+ YYPOS++;
+ ret = YY_OCTNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_OCTNUMBER;
+ goto _yy_fin;
+ }
+ } else if (ch == 'l') {
+ YYPOS++;
+ ret = YY_OCTNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_OCTNUMBER;
+ goto _yy_fin;
+ }
+ case 'L':
+ ch = *++YYPOS;
+ accept = YY_OCTNUMBER;
+ accept_pos = yy_pos;
+ if (ch == 'L') {
+ goto _yy_state_153;
+ } else if (ch == 'U' || ch == 'u') {
+ YYPOS++;
+ ret = YY_OCTNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_OCTNUMBER;
+ goto _yy_fin;
+ }
+ case 'l':
+ ch = *++YYPOS;
+ accept = YY_OCTNUMBER;
+ accept_pos = yy_pos;
+ if (ch == 'l') {
+ goto _yy_state_153;
+ } else if (ch == 'U' || ch == 'u') {
+ YYPOS++;
+ ret = YY_OCTNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_OCTNUMBER;
+ goto _yy_fin;
+ }
+ case '8':
+ case '9':
+ goto _yy_state_83;
+ case 'E':
+ case 'e':
+ goto _yy_state_85;
+ case '.':
+ goto _yy_state_73;
+ default:
+ ret = YY_OCTNUMBER;
+ goto _yy_fin;
+ }
+_yy_state_83:
+ ch = *++YYPOS;
+ if ((ch >= '0' && ch <= '9')) {
+ goto _yy_state_83;
+ } else if (ch == 'E' || ch == 'e') {
+ goto _yy_state_85;
+ } else if (ch == '.') {
+ goto _yy_state_73;
+ } else {
+ goto _yy_state_error;
+ }
+_yy_state_85:
+ ch = *++YYPOS;
+ if (ch == '+' || ch == '-') {
+ ch = *++YYPOS;
+ if ((ch >= '0' && ch <= '9')) {
+ goto _yy_state_158;
+ } else {
+ goto _yy_state_error;
+ }
+ } else if ((ch >= '0' && ch <= '9')) {
+ goto _yy_state_158;
+ } else {
+ goto _yy_state_error;
+ }
+_yy_state_99:
+ ch = *++YYPOS;
+_yy_tunnel_99:
+ if (ch == '*') {
+ ch = *++YYPOS;
+ if (ch != '/') goto _yy_tunnel_99;
+ YYPOS++;
+ ret = YY_COMMENT;
+ goto _yy_fin;
+ } else if (YYPOS < YYEND && (ch <= ')' || ch >= '+')) {
+ if (ch == '\n') {
+ yy_line++;
+ }
+ goto _yy_state_99;
+ } else {
+ goto _yy_state_error;
+ }
+_yy_state_153:
+ ch = *++YYPOS;
+ if (ch == 'U' || ch == 'u') {
+ YYPOS++;
+ ret = YY_OCTNUMBER;
+ goto _yy_fin;
+ } else {
+ goto _yy_state_error;
+ }
+_yy_state_155:
+ ch = *++YYPOS;
+ if (ch == 'U' || ch == 'u') {
+ ch = *++YYPOS;
+ if (ch == 'L') {
+ ch = *++YYPOS;
+ if (ch == 'L') {
+ YYPOS++;
+ ret = YY_HEXNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_HEXNUMBER;
+ goto _yy_fin;
+ }
+ } else if (ch == 'l') {
+ YYPOS++;
+ ret = YY_HEXNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_HEXNUMBER;
+ goto _yy_fin;
+ }
+ } else if (ch == 'L') {
+ ch = *++YYPOS;
+ accept = YY_HEXNUMBER;
+ accept_pos = yy_pos;
+ if (ch == 'L') {
+ goto _yy_state_258;
+ } else if (ch == 'U' || ch == 'u') {
+ YYPOS++;
+ ret = YY_HEXNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_HEXNUMBER;
+ goto _yy_fin;
+ }
+ } else if (ch == 'l') {
+ ch = *++YYPOS;
+ accept = YY_HEXNUMBER;
+ accept_pos = yy_pos;
+ if (ch == 'U' || ch == 'u') {
+ YYPOS++;
+ ret = YY_HEXNUMBER;
+ goto _yy_fin;
+ } else if (ch == 'l') {
+ goto _yy_state_258;
+ } else {
+ ret = YY_HEXNUMBER;
+ goto _yy_fin;
+ }
+ } else if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) {
+ goto _yy_state_155;
+ } else {
+ ret = YY_HEXNUMBER;
+ goto _yy_fin;
+ }
+_yy_state_158:
+ ch = *++YYPOS;
+ if ((ch >= '0' && ch <= '9')) {
+ goto _yy_state_158;
+ } else if (ch == 'F' || ch == 'L' || ch == 'f' || ch == 'l') {
+ YYPOS++;
+ ret = YY_FLOATNUMBER;
+ goto _yy_fin;
+ } else {
+ ret = YY_FLOATNUMBER;
+ goto _yy_fin;
+ }
+_yy_state_162:
+ ch = *++YYPOS;
+ if (ch == 'U' || ch == 'u') {
+ YYPOS++;
+ ret = YY_DECNUMBER;
+ goto _yy_fin;
+ } else {
+ goto _yy_state_error;
+ }
+_yy_state_258:
+ ch = *++YYPOS;
+ if (ch == 'U' || ch == 'u') {
+ YYPOS++;
+ ret = YY_HEXNUMBER;
+ goto _yy_fin;
+ } else {
+ goto _yy_state_error;
+ }
+_yy_state_366:
+ ch = *++YYPOS;
+_yy_tunnel_366:
+ if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || ch == '_' || (ch >= 'a' && ch <= 'z')) {
+ goto _yy_state_15;
+ } else {
+ goto _yy_fin;
+ }
+_yy_state_error:
+ if (accept >= 0) {
+ yy_pos = accept_pos;
+ return accept;
+ }
+ if (YYPOS >= YYEND) {
+ yy_error("unexpected <EOF>");
+ } else if (YYPOS == yy_text) {
+ yy_error("unexpected character 'escape_char(ch)'");
+ } else {
+ yy_error("unexpected sequence 'escape_string(yy_text, 1 + YYPOS - yy_text))'");
+ }
+ YYPOS++;
+ goto _yy_state_start;
+_yy_fin:
+ yy_pos = YYPOS;
+ return ret;
+}
+
+static int skip_EOL(int sym) {
+ if (sym != YY_EOL) {
+ yy_error_sym("<EOL> expected, got", sym);
+ }
+ sym = get_skip_sym();
+ return sym;
+}
+
+static int skip_WS(int sym) {
+ if (sym != YY_WS) {
+ yy_error_sym("<WS> expected, got", sym);
+ }
+ sym = get_skip_sym();
+ return sym;
+}
+
+static int skip_ONE_LINE_COMMENT(int sym) {
+ if (sym != YY_ONE_LINE_COMMENT) {
+ yy_error_sym("<ONE_LINE_COMMENT> expected, got", sym);
+ }
+ sym = get_skip_sym();
+ return sym;
+}
+
+static int skip_COMMENT(int sym) {
+ if (sym != YY_COMMENT) {
+ yy_error_sym("<COMMENT> expected, got", sym);
+ }
+ sym = get_skip_sym();
+ return sym;
+}
+
+static int get_sym(void) {
+ int sym;
+ sym = get_skip_sym();
+ while (sym == YY_EOL || sym == YY_WS || sym == YY_ONE_LINE_COMMENT || sym == YY_COMMENT) {
+ if (sym == YY_EOL) {
+ sym = skip_EOL(sym);
+ } else if (sym == YY_WS) {
+ sym = skip_WS(sym);
+ } else if (sym == YY_ONE_LINE_COMMENT) {
+ sym = skip_ONE_LINE_COMMENT(sym);
+ } else if (sym == YY_COMMENT) {
+ sym = skip_COMMENT(sym);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ }
+ return sym;
+}
+
+static int check_specifier_qualifier_list(int sym) {
+ do {
+ if (YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID), "\000\000\000\340\377\037\001\000\000\000\040\000")) {
+ sym = check_type_specifier(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (YY_IN_SET(sym, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC), "\000\000\370\037\000\000\000\000\000\000\000\000")) {
+ sym = check_type_qualifier(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ } while ((YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) && (sym != YY_ID || zend_ffi_is_typedef_name((const char*)yy_text, yy_pos - yy_text)));
+ return sym;
+}
+
+static int check_type_qualifier_list(int sym) {
+ do {
+ if (YY_IN_SET(sym, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC), "\000\000\370\037\000\000\000\000\000\000\000\000")) {
+ sym = check_type_qualifier(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ } while (YY_IN_SET(sym, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\037\000\000\300\001\000\000\000\000"));
+ return sym;
+}
+
+static int check_type_qualifier(int sym) {
+ if (sym == YY_CONST || sym == YY___CONST || sym == YY___CONST__) {
+ if (sym == YY_CONST) {
+ sym = get_sym();
+ } else if (sym == YY___CONST) {
+ sym = get_sym();
+ } else if (sym == YY___CONST__) {
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ } else if (sym == YY_RESTRICT || sym == YY___RESTICT || sym == YY___RESTRICT__) {
+ if (sym == YY_RESTRICT) {
+ sym = get_sym();
+ } else if (sym == YY___RESTICT) {
+ sym = get_sym();
+ } else if (sym == YY___RESTRICT__) {
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ } else if (sym == YY_VOLATILE || sym == YY___VOLATILE || sym == YY___VOLATILE__) {
+ if (sym == YY_VOLATILE) {
+ sym = get_sym();
+ } else if (sym == YY___VOLATILE) {
+ sym = get_sym();
+ } else if (sym == YY___VOLATILE__) {
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ } else if (sym == YY__ATOMIC) {
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_type_specifier(int sym) {
+ switch (sym) {
+ case YY_VOID:
+ sym = get_sym();
+ break;
+ case YY_CHAR:
+ sym = get_sym();
+ break;
+ case YY_SHORT:
+ sym = get_sym();
+ break;
+ case YY_INT:
+ sym = get_sym();
+ break;
+ case YY_LONG:
+ sym = get_sym();
+ break;
+ case YY_FLOAT:
+ sym = get_sym();
+ break;
+ case YY_DOUBLE:
+ sym = get_sym();
+ break;
+ case YY_SIGNED:
+ sym = get_sym();
+ break;
+ case YY_UNSIGNED:
+ sym = get_sym();
+ break;
+ case YY__BOOL:
+ sym = get_sym();
+ break;
+ case YY__COMPLEX:
+ case YY_COMPLEX:
+ case YY___COMPLEX:
+ case YY___COMPLEX__:
+ if (sym == YY__COMPLEX) {
+ sym = get_sym();
+ } else if (sym == YY_COMPLEX) {
+ sym = get_sym();
+ } else if (sym == YY___COMPLEX) {
+ sym = get_sym();
+ } else if (sym == YY___COMPLEX__) {
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ break;
+ case YY_STRUCT:
+ case YY_UNION:
+ sym = check_struct_or_union_specifier(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY_ENUM:
+ sym = check_enum_specifier(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY_ID:
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+ return sym;
+}
+
+static int check_struct_or_union_specifier(int sym) {
+ if (sym == YY_STRUCT) {
+ sym = get_sym();
+ } else if (sym == YY_UNION) {
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if (sym == YY_ID) {
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY__LBRACE) {
+ sym = check_struct_contents(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else if (sym == YY__LBRACE) {
+ sym = check_struct_contents(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_struct_contents(int sym) {
+ int sym2;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+ int alt2;
+ if (sym != YY__LBRACE) {
+ return -1;
+ }
+ sym = get_sym();
+ if (YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) {
+ sym = check_struct_declaration(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (1) {
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt2 = -2;
+ sym2 = sym;
+ if (sym2 == YY__SEMICOLON) {
+ sym2 = get_sym();
+ goto _yy_state_2_1;
+ } else if (sym2 == YY__RBRACE) {
+ alt2 = 6;
+ goto _yy_state_2;
+ } else {
+ return -1;
+ }
+_yy_state_2_1:
+ if (YY_IN_SET(sym2, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) {
+ alt2 = 3;
+ goto _yy_state_2;
+ } else if (sym2 == YY__RBRACE) {
+ alt2 = 5;
+ goto _yy_state_2;
+ } else {
+ return -1;
+ }
+_yy_state_2:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt2 != 3) {
+ break;
+ }
+ sym = get_sym();
+ sym = check_struct_declaration(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if (alt2 == 5) {
+ sym = get_sym();
+ }
+ }
+ if (sym != YY__RBRACE) {
+ return -1;
+ }
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_struct_declaration(int sym) {
+ sym = check_specifier_qualifier_list(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY__SEMICOLON || sym == YY__RBRACE) {
+ } else if (sym == YY__STAR || sym == YY_ID || sym == YY__LPAREN || sym == YY__COLON) {
+ sym = check_struct_declarator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ sym = check_struct_declarator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_struct_declarator(int sym) {
+ if (sym == YY__STAR || sym == YY_ID || sym == YY__LPAREN) {
+ sym = check_declarator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY__COLON) {
+ sym = get_sym();
+ sym = check_constant_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else if (YY_IN_SET(sym, (YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY__COMMA,YY__SEMICOLON,YY__RBRACE), "\006\000\000\000\000\100\300\001\000\000\000\000")) {
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else {
+ return -1;
+ }
+ } else if (sym == YY__COLON) {
+ sym = get_sym();
+ sym = check_constant_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_enum_specifier(int sym) {
+ if (sym != YY_ENUM) {
+ return -1;
+ }
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if (sym == YY_ID) {
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY__LBRACE) {
+ sym = get_sym();
+ sym = check_enumerator_list(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RBRACE) {
+ return -1;
+ }
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else if (YY_IN_SET(sym, (YY_TYPEDEF,YY_EXTERN,YY_STATIC,YY_AUTO,YY_REGISTER,YY_INLINE,YY___INLINE,YY___INLINE__,YY__NORETURN,YY___CDECL,YY___STDCALL,YY___FASTCALL,YY___THISCALL,YY__ALIGNAS,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY__STAR,YY__LPAREN,YY__SEMICOLON,YY__COLON,YY__LBRACK,YY__RBRACE,YY__COMMA,YY__RPAREN,YY_EOF), "\377\377\377\377\377\337\315\001\000\000\040\000")) {
+ } else {
+ return -1;
+ }
+ } else if (sym == YY__LBRACE) {
+ sym = get_sym();
+ sym = check_enumerator_list(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RBRACE) {
+ return -1;
+ }
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_enumerator_list(int sym) {
+ int sym2;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+ int alt243;
+ sym = check_enumerator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (1) {
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt243 = -2;
+ sym2 = sym;
+ if (sym2 == YY__COMMA) {
+ sym2 = get_sym();
+ goto _yy_state_243_1;
+ } else if (sym2 == YY__RBRACE) {
+ alt243 = -1;
+ goto _yy_state_243;
+ } else {
+ return -1;
+ }
+_yy_state_243_1:
+ if (sym2 == YY_ID) {
+ alt243 = 244;
+ goto _yy_state_243;
+ } else if (sym2 == YY__RBRACE) {
+ alt243 = 246;
+ goto _yy_state_243;
+ } else {
+ return -1;
+ }
+_yy_state_243:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt243 != 244) {
+ break;
+ }
+ sym = get_sym();
+ sym = check_enumerator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if (alt243 == 246) {
+ sym = get_sym();
+ }
+ return sym;
+}
+
+static int check_enumerator(int sym) {
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY__EQUAL) {
+ sym = get_sym();
+ sym = check_constant_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_declarator(int sym) {
+ if (sym == YY__STAR) {
+ sym = check_pointer(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if (sym == YY_ID) {
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY__LPAREN) {
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ sym = check_declarator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = check_array_or_function_declarators(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_abstract_declarator(int sym) {
+ if (sym == YY__STAR) {
+ sym = check_pointer(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if ((sym == YY__LPAREN) && synpred_2(sym)) {
+ sym = check_nested_abstract_declarator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY_ID) {
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (YY_IN_SET(sym, (YY__LBRACK,YY__LPAREN,YY__COMMA,YY__RPAREN,YY_EOF), "\003\000\006\000\000\000\010\000\000\000\000\000")) {
+ } else {
+ return -1;
+ }
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = check_array_or_function_declarators(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_nested_abstract_declarator(int sym) {
+ if (sym != YY__LPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = check_attributes(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if (sym == YY__STAR) {
+ sym = check_pointer(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if ((sym == YY__LPAREN) && synpred_3(sym)) {
+ sym = check_nested_abstract_declarator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY_ID) {
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY__LBRACK || sym == YY__LPAREN || sym == YY__RPAREN) {
+ } else {
+ return -1;
+ }
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = check_array_or_function_declarators(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else if (sym == YY__LPAREN || sym == YY_ID || sym == YY__LBRACK) {
+ if ((sym == YY__LPAREN) && synpred_4(sym)) {
+ sym = check_nested_abstract_declarator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = check_array_or_function_declarators(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else if (sym == YY_ID) {
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = check_array_or_function_declarators(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = check_array_or_function_declarators(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int check_pointer(int sym) {
+ if (sym != YY__STAR) {
+ return -1;
+ }
+ do {
+ sym = get_sym();
+ if (YY_IN_SET(sym, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\037\000\000\300\001\000\000\000\000")) {
+ sym = check_type_qualifier_list(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } while (sym == YY__STAR);
+ return sym;
+}
+
+static int check_array_or_function_declarators(int sym) {
+ int sym2;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+ int alt114;
+ int alt110;
+ int alt124;
+ if (sym == YY__LBRACK) {
+ sym = get_sym();
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt110 = -2;
+ sym2 = sym;
+ if (sym2 == YY_STATIC) {
+ alt110 = 111;
+ goto _yy_state_110;
+ } else if (YY_IN_SET(sym2, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\037\000\000\300\001\000\000\000\000")) {
+ alt110 = 114;
+ goto _yy_state_110;
+ } else if (sym2 == YY__STAR) {
+ sym2 = get_sym();
+ goto _yy_state_110_15;
+ } else if (YY_IN_SET(sym2, (YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER), "\000\000\002\000\000\000\000\200\000\343\377\017")) {
+ alt110 = 120;
+ goto _yy_state_110;
+ } else if (sym2 == YY__RBRACK) {
+ alt110 = 121;
+ goto _yy_state_110;
+ } else {
+ return -1;
+ }
+_yy_state_110_15:
+ if (sym2 == YY__RBRACK) {
+ alt110 = 119;
+ goto _yy_state_110;
+ } else if (YY_IN_SET(sym2, (YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__STAR), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ alt110 = 120;
+ goto _yy_state_110;
+ } else {
+ return -1;
+ }
+_yy_state_110:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt110 == 111) {
+ sym = get_sym();
+ if (YY_IN_SET(sym, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\037\000\000\300\001\000\000\000\000")) {
+ sym = check_type_qualifier_list(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (alt110 == 114) {
+ sym = check_type_qualifier_list(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt114 = -2;
+ sym2 = sym;
+ if (sym2 == YY_STATIC) {
+ alt114 = 115;
+ goto _yy_state_114;
+ } else if (sym2 == YY__STAR) {
+ sym2 = get_sym();
+ goto _yy_state_114_2;
+ } else if (YY_IN_SET(sym2, (YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER), "\000\000\002\000\000\000\000\200\000\343\377\017")) {
+ alt114 = 118;
+ goto _yy_state_114;
+ } else if (sym2 == YY__RBRACK) {
+ alt114 = 121;
+ goto _yy_state_114;
+ } else {
+ return -1;
+ }
+_yy_state_114_2:
+ if (sym2 == YY__RBRACK) {
+ alt114 = 117;
+ goto _yy_state_114;
+ } else if (YY_IN_SET(sym2, (YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__STAR), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ alt114 = 118;
+ goto _yy_state_114;
+ } else {
+ return -1;
+ }
+_yy_state_114:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt114 == 115) {
+ sym = get_sym();
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (alt114 == 121) {
+ } else if (alt114 == 117) {
+ sym = get_sym();
+ } else if (alt114 == 118) {
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ } else if (alt110 == 121 || alt110 == 119 || alt110 == 120) {
+ if (alt110 == 121) {
+ } else if (alt110 == 119) {
+ sym = get_sym();
+ } else if (alt110 == 120) {
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ if (sym != YY__RBRACK) {
+ return -1;
+ }
+ sym = get_sym();
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = check_array_or_function_declarators(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else if (sym == YY__LPAREN) {
+ sym = get_sym();
+ if (YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY__POINT_POINT_POINT), "\000\000\370\377\377\037\341\001\000\000\040\000")) {
+ if (YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) {
+ sym = check_parameter_declaration(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (1) {
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt124 = -2;
+ sym2 = sym;
+ if (sym2 == YY__COMMA) {
+ sym2 = get_sym();
+ goto _yy_state_124_1;
+ } else if (sym2 == YY__RPAREN) {
+ alt124 = 130;
+ goto _yy_state_124;
+ } else {
+ return -1;
+ }
+_yy_state_124_1:
+ if (YY_IN_SET(sym2, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) {
+ alt124 = 125;
+ goto _yy_state_124;
+ } else if (sym2 == YY__POINT_POINT_POINT) {
+ alt124 = 127;
+ goto _yy_state_124;
+ } else {
+ return -1;
+ }
+_yy_state_124:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt124 != 125) {
+ break;
+ }
+ sym = get_sym();
+ sym = check_parameter_declaration(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if (alt124 == 127) {
+ sym = get_sym();
+ if (sym != YY__POINT_POINT_POINT) {
+ return -1;
+ }
+ sym = get_sym();
+ }
+ } else if (sym == YY__POINT_POINT_POINT) {
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = check_array_or_function_declarators(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ } else {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_parameter_declaration(int sym) {
+ sym = check_specifier_qualifier_list(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ sym = check_abstract_declarator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_type_name(int sym) {
+ sym = check_specifier_qualifier_list(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ sym = check_abstract_declarator(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_attributes(int sym) {
+ do {
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__) {
+ if (sym == YY___ATTRIBUTE) {
+ sym = get_sym();
+ } else if (sym == YY___ATTRIBUTE__) {
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ if (sym != YY__LPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ if (sym != YY__LPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ sym = check_attrib(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ sym = check_attrib(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ } else if (sym == YY___DECLSPEC) {
+ sym = get_sym();
+ if (sym != YY__LPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ do {
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY__LPAREN) {
+ sym = get_sym();
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ }
+ } while (sym == YY_ID);
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ } while (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC);
+ return sym;
+}
+
+static int check_attrib(int sym) {
+ if (sym == YY_ID) {
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY__COMMA || sym == YY__RPAREN) {
+ } else if (sym == YY__LPAREN) {
+ sym = get_sym();
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_expr_list(int sym) {
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_expression(int sym) {
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ sym = check_assignment_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_assignment_expression(int sym) {
+ sym = check_conditional_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_constant_expression(int sym) {
+ sym = check_conditional_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_conditional_expression(int sym) {
+ sym = check_logical_or_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym == YY__QUERY) {
+ sym = get_sym();
+ sym = check_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__COLON) {
+ return -1;
+ }
+ sym = get_sym();
+ sym = check_conditional_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_logical_or_expression(int sym) {
+ sym = check_logical_and_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__BAR_BAR) {
+ sym = get_sym();
+ sym = check_logical_and_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_logical_and_expression(int sym) {
+ sym = check_inclusive_or_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__AND_AND) {
+ sym = get_sym();
+ sym = check_inclusive_or_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_inclusive_or_expression(int sym) {
+ sym = check_exclusive_or_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__BAR) {
+ sym = get_sym();
+ sym = check_exclusive_or_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_exclusive_or_expression(int sym) {
+ sym = check_and_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__UPARROW) {
+ sym = get_sym();
+ sym = check_and_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_and_expression(int sym) {
+ sym = check_equality_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__AND) {
+ sym = get_sym();
+ sym = check_equality_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_equality_expression(int sym) {
+ sym = check_relational_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__EQUAL_EQUAL || sym == YY__BANG_EQUAL) {
+ if (sym == YY__EQUAL_EQUAL) {
+ sym = get_sym();
+ sym = check_relational_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY__BANG_EQUAL) {
+ sym = get_sym();
+ sym = check_relational_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_relational_expression(int sym) {
+ sym = check_shift_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__LESS || sym == YY__GREATER || sym == YY__LESS_EQUAL || sym == YY__GREATER_EQUAL) {
+ if (sym == YY__LESS) {
+ sym = get_sym();
+ sym = check_shift_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY__GREATER) {
+ sym = get_sym();
+ sym = check_shift_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY__LESS_EQUAL) {
+ sym = get_sym();
+ sym = check_shift_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY__GREATER_EQUAL) {
+ sym = get_sym();
+ sym = check_shift_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_shift_expression(int sym) {
+ sym = check_additive_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__LESS_LESS || sym == YY__GREATER_GREATER) {
+ if (sym == YY__LESS_LESS) {
+ sym = get_sym();
+ sym = check_additive_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY__GREATER_GREATER) {
+ sym = get_sym();
+ sym = check_additive_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_additive_expression(int sym) {
+ sym = check_multiplicative_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__PLUS || sym == YY__MINUS) {
+ if (sym == YY__PLUS) {
+ sym = get_sym();
+ sym = check_multiplicative_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY__MINUS) {
+ sym = get_sym();
+ sym = check_multiplicative_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_multiplicative_expression(int sym) {
+ sym = check_cast_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (sym == YY__STAR || sym == YY__SLASH || sym == YY__PERCENT) {
+ if (sym == YY__STAR) {
+ sym = get_sym();
+ sym = check_cast_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY__SLASH) {
+ sym = get_sym();
+ sym = check_cast_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else if (sym == YY__PERCENT) {
+ sym = get_sym();
+ sym = check_cast_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ }
+ return sym;
+}
+
+static int check_cast_expression(int sym) {
+ if ((sym == YY__LPAREN) && synpred_5(sym)) {
+ sym = get_sym();
+ sym = check_type_name(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ }
+ sym = check_unary_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ return sym;
+}
+
+static int check_unary_expression(int sym) {
+ switch (sym) {
+ case YY_ID:
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ while (YY_IN_SET(sym, (YY__LBRACK,YY__LPAREN,YY__POINT,YY__MINUS_GREATER,YY__PLUS_PLUS,YY__MINUS_MINUS), "\000\000\002\000\000\000\010\002\000\160\000\000")) {
+ switch (sym) {
+ case YY__LBRACK:
+ sym = get_sym();
+ sym = check_expr_list(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RBRACK) {
+ return -1;
+ }
+ sym = get_sym();
+ break;
+ case YY__LPAREN:
+ sym = get_sym();
+ if (YY_IN_SET(sym, (YY__LPAREN,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__STAR,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ sym = check_expr_list(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ break;
+ case YY__POINT:
+ sym = get_sym();
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY__MINUS_GREATER:
+ sym = get_sym();
+ sym = check_ID(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY__PLUS_PLUS:
+ sym = get_sym();
+ break;
+ case YY__MINUS_MINUS:
+ sym = get_sym();
+ break;
+ default:
+ return -1;
+ }
+ }
+ break;
+ case YY_OCTNUMBER:
+ sym = check_OCTNUMBER(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY_DECNUMBER:
+ sym = check_DECNUMBER(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY_HEXNUMBER:
+ sym = check_HEXNUMBER(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY_FLOATNUMBER:
+ sym = check_FLOATNUMBER(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY_STRING:
+ sym = check_STRING(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY_CHARACTER:
+ sym = check_CHARACTER(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY__LPAREN:
+ sym = get_sym();
+ sym = check_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ break;
+ case YY__PLUS_PLUS:
+ sym = get_sym();
+ sym = check_unary_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY__MINUS_MINUS:
+ sym = get_sym();
+ sym = check_unary_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY__AND:
+ sym = get_sym();
+ sym = check_cast_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY__STAR:
+ sym = get_sym();
+ sym = check_cast_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY__PLUS:
+ sym = get_sym();
+ sym = check_cast_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY__MINUS:
+ sym = get_sym();
+ sym = check_cast_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY__TILDE:
+ sym = get_sym();
+ sym = check_cast_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY__BANG:
+ sym = get_sym();
+ sym = check_cast_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ break;
+ case YY_SIZEOF:
+ sym = get_sym();
+ if ((sym == YY__LPAREN) && synpred_6(sym)) {
+ sym = get_sym();
+ sym = check_type_name(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ } else if (YY_IN_SET(sym, (YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__STAR,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ sym = check_unary_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ break;
+ case YY__ALIGNOF:
+ sym = get_sym();
+ if (sym != YY__LPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ sym = check_type_name(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ break;
+ case YY___ALIGNOF:
+ case YY___ALIGNOF__:
+ if (sym == YY___ALIGNOF) {
+ sym = get_sym();
+ } else if (sym == YY___ALIGNOF__) {
+ sym = get_sym();
+ } else {
+ return -1;
+ }
+ if ((sym == YY__LPAREN) && synpred_7(sym)) {
+ sym = get_sym();
+ sym = check_type_name(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ } else if (YY_IN_SET(sym, (YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__STAR,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ sym = check_unary_expression(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+ return sym;
+}
+
+static int check_ID(int sym) {
+ if (sym != YY_ID) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int check_OCTNUMBER(int sym) {
+ if (sym != YY_OCTNUMBER) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int check_DECNUMBER(int sym) {
+ if (sym != YY_DECNUMBER) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int check_HEXNUMBER(int sym) {
+ if (sym != YY_HEXNUMBER) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int check_FLOATNUMBER(int sym) {
+ if (sym != YY_FLOATNUMBER) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int check_STRING(int sym) {
+ if (sym != YY_STRING) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int check_CHARACTER(int sym) {
+ if (sym != YY_CHARACTER) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int synpred_1(int sym) {
+ int ret;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ ret = check_type_name(sym) != -1;
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ return ret;
+}
+
+static int synpred_2(int sym) {
+ int ret;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ ret = check_nested_abstract_declarator(sym) != -1;
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ return ret;
+}
+
+static int synpred_3(int sym) {
+ int ret;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ ret = check_nested_abstract_declarator(sym) != -1;
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ return ret;
+}
+
+static int synpred_4(int sym) {
+ int ret;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ ret = check_nested_abstract_declarator(sym) != -1;
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ return ret;
+}
+
+static int _synpred_5(int sym) {
+ if (sym != YY__LPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ sym = check_type_name(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int synpred_5(int sym) {
+ int ret;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ ret = _synpred_5(sym) != -1;
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ return ret;
+}
+
+static int _synpred_6(int sym) {
+ if (sym != YY__LPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ sym = check_type_name(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int synpred_6(int sym) {
+ int ret;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ ret = _synpred_6(sym) != -1;
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ return ret;
+}
+
+static int _synpred_7(int sym) {
+ if (sym != YY__LPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ sym = check_type_name(sym);
+ if (sym == -1) {
+ return -1;
+ }
+ if (sym != YY__RPAREN) {
+ return -1;
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int synpred_7(int sym) {
+ int ret;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ ret = _synpred_7(sym) != -1;
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ return ret;
+}
+
+static int parse_declarations(int sym) {
+ while (YY_IN_SET(sym, (YY_TYPEDEF,YY_EXTERN,YY_STATIC,YY_AUTO,YY_REGISTER,YY_INLINE,YY___INLINE,YY___INLINE__,YY__NORETURN,YY___CDECL,YY___STDCALL,YY___FASTCALL,YY___THISCALL,YY__ALIGNAS,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID), "\370\377\371\377\377\037\301\001\000\000\040\000")) {
+ zend_ffi_dcl common_dcl = ZEND_FFI_ATTR_INIT;
+ sym = parse_declaration_specifiers(sym, &common_dcl);
+ if (sym == YY__STAR || sym == YY_ID || sym == YY__LPAREN) {
+ const char *name;
+ size_t name_len;
+ zend_ffi_dcl dcl;
+ dcl = common_dcl;
+ sym = parse_declarator(sym, &dcl, &name, &name_len);
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, &dcl);
+ }
+ if (sym == YY__EQUAL) {
+ sym = parse_initializer(sym);
+ }
+ zend_ffi_declare(name, name_len, &dcl);
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ dcl = common_dcl;
+ sym = parse_declarator(sym, &dcl, &name, &name_len);
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, &dcl);
+ }
+ if (sym == YY__EQUAL) {
+ sym = parse_initializer(sym);
+ }
+ zend_ffi_declare(name, name_len, &dcl);
+ }
+ }
+ if (sym != YY__SEMICOLON) {
+ yy_error_sym("';' expected, got", sym);
+ }
+ sym = get_sym();
+ }
+ return sym;
+}
+
+static int parse_declaration_specifiers(int sym, zend_ffi_dcl *dcl) {
+ do {
+ switch (sym) {
+ case YY_TYPEDEF:
+ if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_TYPEDEF;
+ break;
+ case YY_EXTERN:
+ if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_EXTERN;
+ break;
+ case YY_STATIC:
+ if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_STATIC;
+ break;
+ case YY_AUTO:
+ if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_AUTO;
+ break;
+ case YY_REGISTER:
+ if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_REGISTER;
+ break;
+ case YY_INLINE:
+ case YY___INLINE:
+ case YY___INLINE__:
+ if (sym == YY_INLINE) {
+ sym = get_sym();
+ } else if (sym == YY___INLINE) {
+ sym = get_sym();
+ } else if (sym == YY___INLINE__) {
+ sym = get_sym();
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ dcl->flags |= ZEND_FFI_DCL_INLINE;
+ break;
+ case YY__NORETURN:
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_NO_RETURN;
+ break;
+ case YY___CDECL:
+ sym = get_sym();
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_CDECL);
+ break;
+ case YY___STDCALL:
+ sym = get_sym();
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_STDCALL);
+ break;
+ case YY___FASTCALL:
+ sym = get_sym();
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_FASTCALL);
+ break;
+ case YY___THISCALL:
+ sym = get_sym();
+ zend_ffi_set_abi(dcl, ZEND_FFI_ABI_THISCALL);
+ break;
+ case YY__ALIGNAS:
+ sym = get_sym();
+ if (sym != YY__LPAREN) {
+ yy_error_sym("'(' expected, got", sym);
+ }
+ sym = get_sym();
+ if ((YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) && synpred_1(sym)) {
+ zend_ffi_dcl align_dcl = ZEND_FFI_ATTR_INIT;
+ sym = parse_type_name(sym, &align_dcl);
+ zend_ffi_align_as_type(dcl, &align_dcl);
+ } else if (YY_IN_SET(sym, (YY__LPAREN,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__STAR,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ zend_ffi_val align_val;
+ sym = parse_constant_expression(sym, &align_val);
+ zend_ffi_align_as_val(dcl, &align_val);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ break;
+ case YY___ATTRIBUTE:
+ case YY___ATTRIBUTE__:
+ case YY___DECLSPEC:
+ sym = parse_attributes(sym, dcl);
+ break;
+ case YY_CONST:
+ case YY___CONST:
+ case YY___CONST__:
+ case YY_RESTRICT:
+ case YY___RESTICT:
+ case YY___RESTRICT__:
+ case YY_VOLATILE:
+ case YY___VOLATILE:
+ case YY___VOLATILE__:
+ case YY__ATOMIC:
+ sym = parse_type_qualifier(sym, dcl);
+ break;
+ case YY_VOID:
+ case YY_CHAR:
+ case YY_SHORT:
+ case YY_INT:
+ case YY_LONG:
+ case YY_FLOAT:
+ case YY_DOUBLE:
+ case YY_SIGNED:
+ case YY_UNSIGNED:
+ case YY__BOOL:
+ case YY__COMPLEX:
+ case YY_COMPLEX:
+ case YY___COMPLEX:
+ case YY___COMPLEX__:
+ case YY_STRUCT:
+ case YY_UNION:
+ case YY_ENUM:
+ case YY_ID:
+ sym = parse_type_specifier(sym, dcl);
+ break;
+ default:
+ yy_error_sym("unexpected", sym);
+ }
+ } while ((YY_IN_SET(sym, (YY_TYPEDEF,YY_EXTERN,YY_STATIC,YY_AUTO,YY_REGISTER,YY_INLINE,YY___INLINE,YY___INLINE__,YY__NORETURN,YY___CDECL,YY___STDCALL,YY___FASTCALL,YY___THISCALL,YY__ALIGNAS,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID), "\370\377\371\377\377\037\301\001\000\000\040\000")) && (sym != YY_ID || zend_ffi_is_typedef_name((const char*)yy_text, yy_pos - yy_text)));
+ return sym;
+}
+
+static int parse_specifier_qualifier_list(int sym, zend_ffi_dcl *dcl) {
+ do {
+ if (YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID), "\000\000\000\340\377\037\001\000\000\000\040\000")) {
+ sym = parse_type_specifier(sym, dcl);
+ } else if (YY_IN_SET(sym, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC), "\000\000\370\037\000\000\000\000\000\000\000\000")) {
+ sym = parse_type_qualifier(sym, dcl);
+ } else if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, dcl);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ } while ((YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) && (sym != YY_ID || zend_ffi_is_typedef_name((const char*)yy_text, yy_pos - yy_text)));
+ return sym;
+}
+
+static int parse_type_qualifier_list(int sym, zend_ffi_dcl *dcl) {
+ do {
+ if (YY_IN_SET(sym, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC), "\000\000\370\037\000\000\000\000\000\000\000\000")) {
+ sym = parse_type_qualifier(sym, dcl);
+ } else if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, dcl);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ } while (YY_IN_SET(sym, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\037\000\000\300\001\000\000\000\000"));
+ return sym;
+}
+
+static int parse_type_qualifier(int sym, zend_ffi_dcl *dcl) {
+ if (sym == YY_CONST || sym == YY___CONST || sym == YY___CONST__) {
+ if (sym == YY_CONST) {
+ sym = get_sym();
+ } else if (sym == YY___CONST) {
+ sym = get_sym();
+ } else if (sym == YY___CONST__) {
+ sym = get_sym();
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ dcl->flags |= ZEND_FFI_DCL_CONST;
+ dcl->attr |= ZEND_FFI_ATTR_CONST;
+ } else if (sym == YY_RESTRICT || sym == YY___RESTICT || sym == YY___RESTRICT__) {
+ if (sym == YY_RESTRICT) {
+ sym = get_sym();
+ } else if (sym == YY___RESTICT) {
+ sym = get_sym();
+ } else if (sym == YY___RESTRICT__) {
+ sym = get_sym();
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ dcl->flags |= ZEND_FFI_DCL_RESTRICT;
+ } else if (sym == YY_VOLATILE || sym == YY___VOLATILE || sym == YY___VOLATILE__) {
+ if (sym == YY_VOLATILE) {
+ sym = get_sym();
+ } else if (sym == YY___VOLATILE) {
+ sym = get_sym();
+ } else if (sym == YY___VOLATILE__) {
+ sym = get_sym();
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ dcl->flags |= ZEND_FFI_DCL_VOLATILE;
+ } else if (sym == YY__ATOMIC) {
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_ATOMIC;
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ return sym;
+}
+
+static int parse_type_specifier(int sym, zend_ffi_dcl *dcl) {
+ const char *name;
+ size_t name_len;
+ switch (sym) {
+ case YY_VOID:
+ if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_VOID;
+ break;
+ case YY_CHAR:
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED))) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_CHAR;
+ break;
+ case YY_SHORT:
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_SHORT;
+ break;
+ case YY_INT:
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG))) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_INT;
+ break;
+ case YY_LONG:
+ if (dcl->flags & ZEND_FFI_DCL_LONG) {
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);
+ dcl->flags |= ZEND_FFI_DCL_LONG_LONG;
+ } else {
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);
+ dcl->flags |= ZEND_FFI_DCL_LONG;
+ }
+ sym = get_sym();
+ break;
+ case YY_FLOAT:
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_FLOAT;
+ break;
+ case YY_DOUBLE:
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_DOUBLE;
+ break;
+ case YY_SIGNED:
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_SIGNED;
+ break;
+ case YY_UNSIGNED:
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_INT))) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_UNSIGNED;
+ break;
+ case YY__BOOL:
+ if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_BOOL;
+ break;
+ case YY__COMPLEX:
+ case YY_COMPLEX:
+ case YY___COMPLEX:
+ case YY___COMPLEX__:
+ if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_LONG))) yy_error_sym("Unexpected '%s'", sym);
+ if (sym == YY__COMPLEX) {
+ sym = get_sym();
+ } else if (sym == YY_COMPLEX) {
+ sym = get_sym();
+ } else if (sym == YY___COMPLEX) {
+ sym = get_sym();
+ } else if (sym == YY___COMPLEX__) {
+ sym = get_sym();
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ dcl->flags |= ZEND_FFI_DCL_COMPLEX;
+ break;
+ case YY_STRUCT:
+ case YY_UNION:
+ if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);
+ sym = parse_struct_or_union_specifier(sym, dcl);
+ break;
+ case YY_ENUM:
+ if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);
+ sym = parse_enum_specifier(sym, dcl);
+ break;
+ case YY_ID:
+ if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);
+ /*redeclaration of '%.*s' ??? */
+ sym = parse_ID(sym, &name, &name_len);
+ dcl->flags |= ZEND_FFI_DCL_TYPEDEF_NAME;
+ zend_ffi_resolve_typedef(name, name_len, dcl);
+ break;
+ default:
+ yy_error_sym("unexpected", sym);
+ }
+ return sym;
+}
+
+static int parse_struct_or_union_specifier(int sym, zend_ffi_dcl *dcl) {
+ if (sym == YY_STRUCT) {
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_STRUCT;
+ } else if (sym == YY_UNION) {
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_UNION;
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, dcl);
+ }
+ if (sym == YY_ID) {
+ const char *name;
+ size_t name_len;
+ sym = parse_ID(sym, &name, &name_len);
+ zend_ffi_declare_tag(name, name_len, dcl, 1);
+ if (sym == YY__LBRACE) {
+ sym = parse_struct_contents(sym, dcl);
+ zend_ffi_declare_tag(name, name_len, dcl, 0);
+ }
+ } else if (sym == YY__LBRACE) {
+ zend_ffi_make_struct_type(dcl);
+ sym = parse_struct_contents(sym, dcl);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ return sym;
+}
+
+static int parse_struct_contents(int sym, zend_ffi_dcl *dcl) {
+ int sym2;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+ int alt2;
+ if (sym != YY__LBRACE) {
+ yy_error_sym("'{' expected, got", sym);
+ }
+ sym = get_sym();
+ if (YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) {
+ sym = parse_struct_declaration(sym, dcl);
+ while (1) {
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt2 = -2;
+ sym2 = sym;
+ if (sym2 == YY__SEMICOLON) {
+ sym2 = get_sym();
+ goto _yy_state_2_1;
+ } else if (sym2 == YY__RBRACE) {
+ alt2 = 6;
+ goto _yy_state_2;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_2_1:
+ if (YY_IN_SET(sym2, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) {
+ alt2 = 3;
+ goto _yy_state_2;
+ } else if (sym2 == YY__RBRACE) {
+ alt2 = 5;
+ goto _yy_state_2;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_2:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt2 != 3) {
+ break;
+ }
+ sym = get_sym();
+ sym = parse_struct_declaration(sym, dcl);
+ }
+ if (alt2 == 5) {
+ sym = get_sym();
+ }
+ }
+ if (sym != YY__RBRACE) {
+ yy_error_sym("'}' expected, got", sym);
+ }
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, dcl);
+ }
+ zend_ffi_adjust_struct_size(dcl);
+ return sym;
+}
+
+static int parse_struct_declaration(int sym, zend_ffi_dcl *struct_dcl) {
+ zend_ffi_dcl common_field_dcl = ZEND_FFI_ATTR_INIT;
+ sym = parse_specifier_qualifier_list(sym, &common_field_dcl);
+ if (sym == YY__SEMICOLON || sym == YY__RBRACE) {
+ zend_ffi_add_anonymous_field(struct_dcl, &common_field_dcl);
+ } else if (sym == YY__STAR || sym == YY_ID || sym == YY__LPAREN || sym == YY__COLON) {
+ sym = parse_struct_declarator(sym, struct_dcl, &common_field_dcl);
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ zend_ffi_dcl field_dcl = common_field_dcl;
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, &field_dcl);
+ }
+ sym = parse_struct_declarator(sym, struct_dcl, &field_dcl);
+ }
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ return sym;
+}
+
+static int parse_struct_declarator(int sym, zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl) {
+ const char *name = NULL;
+ size_t name_len = 0;
+ zend_ffi_val bits;
+ if (sym == YY__STAR || sym == YY_ID || sym == YY__LPAREN) {
+ sym = parse_declarator(sym, field_dcl, &name, &name_len);
+ if (sym == YY__COLON) {
+ sym = get_sym();
+ sym = parse_constant_expression(sym, &bits);
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, field_dcl);
+ }
+ zend_ffi_add_bit_field(struct_dcl, name, name_len, field_dcl, &bits);
+ } else if (YY_IN_SET(sym, (YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY__COMMA,YY__SEMICOLON,YY__RBRACE), "\006\000\000\000\000\100\300\001\000\000\000\000")) {
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, field_dcl);
+ }
+ zend_ffi_add_field(struct_dcl, name, name_len, field_dcl);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ } else if (sym == YY__COLON) {
+ sym = get_sym();
+ sym = parse_constant_expression(sym, &bits);
+ zend_ffi_add_bit_field(struct_dcl, NULL, 0, field_dcl, &bits);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ return sym;
+}
+
+static int parse_enum_specifier(int sym, zend_ffi_dcl *dcl) {
+ if (sym != YY_ENUM) {
+ yy_error_sym("'enum' expected, got", sym);
+ }
+ sym = get_sym();
+ dcl->flags |= ZEND_FFI_DCL_ENUM;
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, dcl);
+ }
+ if (sym == YY_ID) {
+ const char *name;
+ size_t name_len;
+ sym = parse_ID(sym, &name, &name_len);
+ if (sym == YY__LBRACE) {
+ zend_ffi_declare_tag(name, name_len, dcl, 0);
+ sym = get_sym();
+ sym = parse_enumerator_list(sym, dcl);
+ if (sym != YY__RBRACE) {
+ yy_error_sym("'}' expected, got", sym);
+ }
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, dcl);
+ }
+ } else if (YY_IN_SET(sym, (YY_TYPEDEF,YY_EXTERN,YY_STATIC,YY_AUTO,YY_REGISTER,YY_INLINE,YY___INLINE,YY___INLINE__,YY__NORETURN,YY___CDECL,YY___STDCALL,YY___FASTCALL,YY___THISCALL,YY__ALIGNAS,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY__STAR,YY__LPAREN,YY__SEMICOLON,YY__COLON,YY__LBRACK,YY__RBRACE,YY__COMMA,YY__RPAREN,YY_EOF), "\377\377\377\377\377\337\315\001\000\000\040\000")) {
+ zend_ffi_declare_tag(name, name_len, dcl, 1);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ } else if (sym == YY__LBRACE) {
+ sym = get_sym();
+ zend_ffi_make_enum_type(dcl);
+ sym = parse_enumerator_list(sym, dcl);
+ if (sym != YY__RBRACE) {
+ yy_error_sym("'}' expected, got", sym);
+ }
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, dcl);
+ }
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ return sym;
+}
+
+static int parse_enumerator_list(int sym, zend_ffi_dcl *enum_dcl) {
+ int sym2;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+ int alt243;
+ int64_t min = 0, max = 0, last = -1;
+ sym = parse_enumerator(sym, enum_dcl, &min, &max, &last);
+ while (1) {
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt243 = -2;
+ sym2 = sym;
+ if (sym2 == YY__COMMA) {
+ sym2 = get_sym();
+ goto _yy_state_243_1;
+ } else if (sym2 == YY__RBRACE) {
+ alt243 = -1;
+ goto _yy_state_243;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_243_1:
+ if (sym2 == YY_ID) {
+ alt243 = 244;
+ goto _yy_state_243;
+ } else if (sym2 == YY__RBRACE) {
+ alt243 = 246;
+ goto _yy_state_243;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_243:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt243 != 244) {
+ break;
+ }
+ sym = get_sym();
+ sym = parse_enumerator(sym, enum_dcl, &min, &max, &last);
+ }
+ if (alt243 == 246) {
+ sym = get_sym();
+ }
+ return sym;
+}
+
+static int parse_enumerator(int sym, zend_ffi_dcl *enum_dcl, int64_t *min, int64_t *max, int64_t *last) {
+ const char *name;
+ size_t name_len;
+ zend_ffi_val val = {.kind = ZEND_FFI_VAL_EMPTY};
+ sym = parse_ID(sym, &name, &name_len);
+ if (sym == YY__EQUAL) {
+ sym = get_sym();
+ sym = parse_constant_expression(sym, &val);
+ }
+ zend_ffi_add_enum_val(enum_dcl, name, name_len, &val, min, max, last);
+ return sym;
+}
+
+static int parse_declarator(int sym, zend_ffi_dcl *dcl, const char **name, size_t *name_len) {
+ zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};
+ zend_bool nested = 0;
+ if (sym == YY__STAR) {
+ sym = parse_pointer(sym, dcl);
+ }
+ if (sym == YY_ID) {
+ sym = parse_ID(sym, name, name_len);
+ } else if (sym == YY__LPAREN) {
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, &nested_dcl);
+ }
+ sym = parse_declarator(sym, &nested_dcl, name, name_len);
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ nested = 1;
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = parse_array_or_function_declarators(sym, dcl);
+ }
+ if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);
+ return sym;
+}
+
+static int parse_abstract_declarator(int sym, zend_ffi_dcl *dcl, const char **name, size_t *name_len) {
+ zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};
+ zend_bool nested = 0;
+ if (sym == YY__STAR) {
+ sym = parse_pointer(sym, dcl);
+ }
+ if ((sym == YY__LPAREN) && synpred_2(sym)) {
+ sym = parse_nested_abstract_declarator(sym, &nested_dcl, name, name_len);
+ nested = 1;
+ } else if (sym == YY_ID) {
+ sym = parse_ID(sym, name, name_len);
+ } else if (YY_IN_SET(sym, (YY__LBRACK,YY__LPAREN,YY__COMMA,YY__RPAREN,YY_EOF), "\003\000\006\000\000\000\010\000\000\000\000\000")) {
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = parse_array_or_function_declarators(sym, dcl);
+ }
+ if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);
+ return sym;
+}
+
+static int parse_nested_abstract_declarator(int sym, zend_ffi_dcl *dcl, const char **name, size_t *name_len) {
+ zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};
+ zend_bool nested = 0;
+ if (sym != YY__LPAREN) {
+ yy_error_sym("'(' expected, got", sym);
+ }
+ sym = get_sym();
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC) {
+ sym = parse_attributes(sym, &nested_dcl);
+ }
+ if (sym == YY__STAR) {
+ sym = parse_pointer(sym, dcl);
+ if ((sym == YY__LPAREN) && synpred_3(sym)) {
+ sym = parse_nested_abstract_declarator(sym, &nested_dcl, name, name_len);
+ nested = 1;
+ } else if (sym == YY_ID) {
+ sym = parse_ID(sym, name, name_len);
+ } else if (sym == YY__LBRACK || sym == YY__LPAREN || sym == YY__RPAREN) {
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = parse_array_or_function_declarators(sym, dcl);
+ }
+ } else if (sym == YY__LPAREN || sym == YY_ID || sym == YY__LBRACK) {
+ if ((sym == YY__LPAREN) && synpred_4(sym)) {
+ sym = parse_nested_abstract_declarator(sym, &nested_dcl, name, name_len);
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = parse_array_or_function_declarators(sym, dcl);
+ }
+ nested = 1;
+ } else if (sym == YY_ID) {
+ sym = parse_ID(sym, name, name_len);
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = parse_array_or_function_declarators(sym, dcl);
+ }
+ } else if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = parse_array_or_function_declarators(sym, dcl);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);
+ return sym;
+}
+
+static int parse_pointer(int sym, zend_ffi_dcl *dcl) {
+ if (sym != YY__STAR) {
+ yy_error_sym("'*' expected, got", sym);
+ }
+ do {
+ sym = get_sym();
+ zend_ffi_make_pointer_type(dcl);
+ if (YY_IN_SET(sym, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\037\000\000\300\001\000\000\000\000")) {
+ sym = parse_type_qualifier_list(sym, dcl);
+ }
+ } while (sym == YY__STAR);
+ return sym;
+}
+
+static int parse_array_or_function_declarators(int sym, zend_ffi_dcl *dcl) {
+ int sym2;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+ int alt114;
+ int alt110;
+ int alt124;
+ zend_ffi_dcl dummy = ZEND_FFI_ATTR_INIT;
+ zend_ffi_val len = {.kind = ZEND_FFI_VAL_EMPTY};
+ HashTable *args = NULL;
+ uint32_t attr = 0;
+ if (sym == YY__LBRACK) {
+ sym = get_sym();
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt110 = -2;
+ sym2 = sym;
+ if (sym2 == YY_STATIC) {
+ alt110 = 111;
+ goto _yy_state_110;
+ } else if (YY_IN_SET(sym2, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\037\000\000\300\001\000\000\000\000")) {
+ alt110 = 114;
+ goto _yy_state_110;
+ } else if (sym2 == YY__STAR) {
+ sym2 = get_sym();
+ goto _yy_state_110_15;
+ } else if (YY_IN_SET(sym2, (YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER), "\000\000\002\000\000\000\000\200\000\343\377\017")) {
+ alt110 = 120;
+ goto _yy_state_110;
+ } else if (sym2 == YY__RBRACK) {
+ alt110 = 121;
+ goto _yy_state_110;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_110_15:
+ if (sym2 == YY__RBRACK) {
+ alt110 = 119;
+ goto _yy_state_110;
+ } else if (YY_IN_SET(sym2, (YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__STAR), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ alt110 = 120;
+ goto _yy_state_110;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_110:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt110 == 111) {
+ sym = get_sym();
+ if (YY_IN_SET(sym, (YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\037\000\000\300\001\000\000\000\000")) {
+ sym = parse_type_qualifier_list(sym, &dummy);
+ }
+ sym = parse_assignment_expression(sym, &len);
+ } else if (alt110 == 114) {
+ sym = parse_type_qualifier_list(sym, &dummy);
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt114 = -2;
+ sym2 = sym;
+ if (sym2 == YY_STATIC) {
+ alt114 = 115;
+ goto _yy_state_114;
+ } else if (sym2 == YY__STAR) {
+ sym2 = get_sym();
+ goto _yy_state_114_2;
+ } else if (YY_IN_SET(sym2, (YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER), "\000\000\002\000\000\000\000\200\000\343\377\017")) {
+ alt114 = 118;
+ goto _yy_state_114;
+ } else if (sym2 == YY__RBRACK) {
+ alt114 = 121;
+ goto _yy_state_114;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_114_2:
+ if (sym2 == YY__RBRACK) {
+ alt114 = 117;
+ goto _yy_state_114;
+ } else if (YY_IN_SET(sym2, (YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__STAR), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ alt114 = 118;
+ goto _yy_state_114;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_114:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt114 == 115) {
+ sym = get_sym();
+ sym = parse_assignment_expression(sym, &len);
+ } else if (alt114 == 121) {
+ attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;
+ } else if (alt114 == 117) {
+ sym = get_sym();
+ attr |= ZEND_FFI_ATTR_VLA;
+ } else if (alt114 == 118) {
+ sym = parse_assignment_expression(sym, &len);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ } else if (alt110 == 121 || alt110 == 119 || alt110 == 120) {
+ if (alt110 == 121) {
+ attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;
+ } else if (alt110 == 119) {
+ sym = get_sym();
+ attr |= ZEND_FFI_ATTR_VLA;
+ } else if (alt110 == 120) {
+ sym = parse_assignment_expression(sym, &len);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ if (sym != YY__RBRACK) {
+ yy_error_sym("']' expected, got", sym);
+ }
+ sym = get_sym();
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = parse_array_or_function_declarators(sym, dcl);
+ }
+ dcl->attr |= attr;
+ zend_ffi_make_array_type(dcl, &len);
+ } else if (sym == YY__LPAREN) {
+ sym = get_sym();
+ if (YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC,YY__POINT_POINT_POINT), "\000\000\370\377\377\037\341\001\000\000\040\000")) {
+ if (YY_IN_SET(sym, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) {
+ sym = parse_parameter_declaration(sym, &args);
+ while (1) {
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt124 = -2;
+ sym2 = sym;
+ if (sym2 == YY__COMMA) {
+ sym2 = get_sym();
+ goto _yy_state_124_1;
+ } else if (sym2 == YY__RPAREN) {
+ alt124 = 130;
+ goto _yy_state_124;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_124_1:
+ if (YY_IN_SET(sym2, (YY_VOID,YY_CHAR,YY_SHORT,YY_INT,YY_LONG,YY_FLOAT,YY_DOUBLE,YY_SIGNED,YY_UNSIGNED,YY__BOOL,YY__COMPLEX,YY_COMPLEX,YY___COMPLEX,YY___COMPLEX__,YY_STRUCT,YY_UNION,YY_ENUM,YY_ID,YY_CONST,YY___CONST,YY___CONST__,YY_RESTRICT,YY___RESTICT,YY___RESTRICT__,YY_VOLATILE,YY___VOLATILE,YY___VOLATILE__,YY__ATOMIC,YY___ATTRIBUTE,YY___ATTRIBUTE__,YY___DECLSPEC), "\000\000\370\377\377\037\301\001\000\000\040\000")) {
+ alt124 = 125;
+ goto _yy_state_124;
+ } else if (sym2 == YY__POINT_POINT_POINT) {
+ alt124 = 127;
+ goto _yy_state_124;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_124:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt124 != 125) {
+ break;
+ }
+ sym = get_sym();
+ sym = parse_parameter_declaration(sym, &args);
+ }
+ if (alt124 == 127) {
+ sym = get_sym();
+ if (sym != YY__POINT_POINT_POINT) {
+ yy_error_sym("'...' expected, got", sym);
+ }
+ sym = get_sym();
+ attr |= ZEND_FFI_ATTR_VARIADIC;
+ }
+ } else if (sym == YY__POINT_POINT_POINT) {
+ sym = get_sym();
+ attr |= ZEND_FFI_ATTR_VARIADIC;
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ }
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ if (sym == YY__LBRACK || sym == YY__LPAREN) {
+ sym = parse_array_or_function_declarators(sym, dcl);
+ }
+ dcl->attr |= attr;
+ zend_ffi_make_func_type(dcl, args);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ return sym;
+}
+
+static int parse_parameter_declaration(int sym, HashTable **args) {
+ const char *name = NULL;
+ size_t name_len = 0;
+ zend_bool old_allow_vla = FFI_G(allow_vla);
+ FFI_G(allow_vla) = 1;
+ zend_ffi_dcl param_dcl = ZEND_FFI_ATTR_INIT;
+ sym = parse_specifier_qualifier_list(sym, ¶m_dcl);
+ sym = parse_abstract_declarator(sym, ¶m_dcl, &name, &name_len);
+ zend_ffi_add_arg(args, name, name_len, ¶m_dcl);
+ FFI_G(allow_vla) = old_allow_vla;
+ return sym;
+}
+
+static int parse_type_name(int sym, zend_ffi_dcl *dcl) {
+ const char *name = NULL;
+ size_t name_len = 0;
+ sym = parse_specifier_qualifier_list(sym, dcl);
+ sym = parse_abstract_declarator(sym, dcl, &name, &name_len);
+ return sym;
+}
+
+static int parse_attributes(int sym, zend_ffi_dcl *dcl) {
+ const char *name;
+ size_t name_len;
+ zend_ffi_val val;
+ do {
+ if (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__) {
+ if (sym == YY___ATTRIBUTE) {
+ sym = get_sym();
+ } else if (sym == YY___ATTRIBUTE__) {
+ sym = get_sym();
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ if (sym != YY__LPAREN) {
+ yy_error_sym("'(' expected, got", sym);
+ }
+ sym = get_sym();
+ if (sym != YY__LPAREN) {
+ yy_error_sym("'(' expected, got", sym);
+ }
+ sym = get_sym();
+ sym = parse_attrib(sym, dcl);
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ sym = parse_attrib(sym, dcl);
+ }
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ } else if (sym == YY___DECLSPEC) {
+ sym = get_sym();
+ if (sym != YY__LPAREN) {
+ yy_error_sym("'(' expected, got", sym);
+ }
+ sym = get_sym();
+ do {
+ sym = parse_ID(sym, &name, &name_len);
+ if (sym == YY__LPAREN) {
+ sym = get_sym();
+ sym = parse_assignment_expression(sym, &val);
+ zend_ffi_add_msvc_attribute_value(dcl, name, name_len, &val);
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ }
+ } while (sym == YY_ID);
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ } while (sym == YY___ATTRIBUTE || sym == YY___ATTRIBUTE__ || sym == YY___DECLSPEC);
+ return sym;
+}
+
+static int parse_attrib(int sym, zend_ffi_dcl *dcl) {
+ const char *name;
+ size_t name_len;
+ int n;
+ zend_ffi_val val;
+ if (sym == YY_ID) {
+ sym = parse_ID(sym, &name, &name_len);
+ if (sym == YY__COMMA || sym == YY__RPAREN) {
+ zend_ffi_add_attribute(dcl, name, name_len);
+ } else if (sym == YY__LPAREN) {
+ sym = get_sym();
+ sym = parse_assignment_expression(sym, &val);
+ zend_ffi_add_attribute_value(dcl, name, name_len, 0, &val);
+ n = 0;
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ sym = parse_assignment_expression(sym, &val);
+ zend_ffi_add_attribute_value(dcl, name, name_len, ++n, &val);
+ }
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ }
+ return sym;
+}
+
+static int parse_initializer(int sym) {
+ int sym2;
+ const unsigned char *save_pos;
+ const unsigned char *save_text;
+ int save_line;
+ int alt336;
+ zend_ffi_val dummy;
+ if (sym != YY__EQUAL) {
+ yy_error_sym("'=' expected, got", sym);
+ }
+ sym = get_sym();
+ if (YY_IN_SET(sym, (YY__LPAREN,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__STAR,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ sym = parse_assignment_expression(sym, &dummy);
+ } else if (sym == YY__LBRACE) {
+ sym = get_sym();
+ if (sym == YY__LBRACK || sym == YY__POINT) {
+ sym = parse_designation(sym);
+ }
+ sym = parse_initializer(sym);
+ while (1) {
+ save_pos = yy_pos;
+ save_text = yy_text;
+ save_line = yy_line;
+ alt336 = -2;
+ sym2 = sym;
+ if (sym2 == YY__COMMA) {
+ sym2 = get_sym();
+ goto _yy_state_336_1;
+ } else if (sym2 == YY__RBRACE) {
+ alt336 = 341;
+ goto _yy_state_336;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_336_1:
+ if (sym2 == YY__LBRACK || sym2 == YY__POINT || sym2 == YY__EQUAL) {
+ alt336 = 337;
+ goto _yy_state_336;
+ } else if (sym2 == YY__RBRACE) {
+ alt336 = 340;
+ goto _yy_state_336;
+ } else {
+ yy_error_sym("unexpected", sym2);
+ }
+_yy_state_336:
+ yy_pos = save_pos;
+ yy_text = save_text;
+ yy_line = save_line;
+ if (alt336 != 337) {
+ break;
+ }
+ sym = get_sym();
+ if (sym == YY__LBRACK || sym == YY__POINT) {
+ sym = parse_designation(sym);
+ }
+ sym = parse_initializer(sym);
+ }
+ if (alt336 == 340) {
+ sym = get_sym();
+ }
+ if (sym != YY__RBRACE) {
+ yy_error_sym("'}' expected, got", sym);
+ }
+ sym = get_sym();
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ return sym;
+}
+
+static int parse_designation(int sym) {
+ const char *name;
+ size_t name_len;
+ zend_ffi_val dummy;
+ do {
+ if (sym == YY__LBRACK) {
+ sym = get_sym();
+ sym = parse_constant_expression(sym, &dummy);
+ if (sym != YY__RBRACK) {
+ yy_error_sym("']' expected, got", sym);
+ }
+ sym = get_sym();
+ } else if (sym == YY__POINT) {
+ sym = get_sym();
+ sym = parse_ID(sym, &name, &name_len);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ } while (sym == YY__LBRACK || sym == YY__POINT);
+ if (sym != YY__EQUAL) {
+ yy_error_sym("'=' expected, got", sym);
+ }
+ sym = get_sym();
+ return sym;
+}
+
+static int parse_expr_list(int sym) {
+ zend_ffi_val dummy;
+ sym = parse_assignment_expression(sym, &dummy);
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ sym = parse_assignment_expression(sym, &dummy);
+ }
+ return sym;
+}
+
+static int parse_expression(int sym, zend_ffi_val *val) {
+ sym = parse_assignment_expression(sym, val);
+ while (sym == YY__COMMA) {
+ sym = get_sym();
+ sym = parse_assignment_expression(sym, val);
+ }
+ return sym;
+}
+
+static int parse_assignment_expression(int sym, zend_ffi_val *val) {
+ sym = parse_conditional_expression(sym, val);
+ return sym;
+}
+
+static int parse_constant_expression(int sym, zend_ffi_val *val) {
+ sym = parse_conditional_expression(sym, val);
+ return sym;
+}
+
+static int parse_conditional_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2, op3;
+ sym = parse_logical_or_expression(sym, val);
+ if (sym == YY__QUERY) {
+ sym = get_sym();
+ sym = parse_expression(sym, &op2);
+ if (sym != YY__COLON) {
+ yy_error_sym("':' expected, got", sym);
+ }
+ sym = get_sym();
+ sym = parse_conditional_expression(sym, &op3);
+ zend_ffi_expr_conditional(val, &op2, &op3);
+ }
+ return sym;
+}
+
+static int parse_logical_or_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2;
+ sym = parse_logical_and_expression(sym, val);
+ while (sym == YY__BAR_BAR) {
+ sym = get_sym();
+ sym = parse_logical_and_expression(sym, &op2);
+ zend_ffi_expr_bool_or(val, &op2);
+ }
+ return sym;
+}
+
+static int parse_logical_and_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2;
+ sym = parse_inclusive_or_expression(sym, val);
+ while (sym == YY__AND_AND) {
+ sym = get_sym();
+ sym = parse_inclusive_or_expression(sym, &op2);
+ zend_ffi_expr_bool_and(val, &op2);
+ }
+ return sym;
+}
+
+static int parse_inclusive_or_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2;
+ sym = parse_exclusive_or_expression(sym, val);
+ while (sym == YY__BAR) {
+ sym = get_sym();
+ sym = parse_exclusive_or_expression(sym, &op2);
+ zend_ffi_expr_bw_or(val, &op2);
+ }
+ return sym;
+}
+
+static int parse_exclusive_or_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2;
+ sym = parse_and_expression(sym, val);
+ while (sym == YY__UPARROW) {
+ sym = get_sym();
+ sym = parse_and_expression(sym, &op2);
+ zend_ffi_expr_bw_xor(val, &op2);
+ }
+ return sym;
+}
+
+static int parse_and_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2;
+ sym = parse_equality_expression(sym, val);
+ while (sym == YY__AND) {
+ sym = get_sym();
+ sym = parse_equality_expression(sym, &op2);
+ zend_ffi_expr_bw_and(val, &op2);
+ }
+ return sym;
+}
+
+static int parse_equality_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2;
+ sym = parse_relational_expression(sym, val);
+ while (sym == YY__EQUAL_EQUAL || sym == YY__BANG_EQUAL) {
+ if (sym == YY__EQUAL_EQUAL) {
+ sym = get_sym();
+ sym = parse_relational_expression(sym, &op2);
+ zend_ffi_expr_is_equal(val, &op2);
+ } else if (sym == YY__BANG_EQUAL) {
+ sym = get_sym();
+ sym = parse_relational_expression(sym, &op2);
+ zend_ffi_expr_is_not_equal(val, &op2);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ }
+ return sym;
+}
+
+static int parse_relational_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2;
+ sym = parse_shift_expression(sym, val);
+ while (sym == YY__LESS || sym == YY__GREATER || sym == YY__LESS_EQUAL || sym == YY__GREATER_EQUAL) {
+ if (sym == YY__LESS) {
+ sym = get_sym();
+ sym = parse_shift_expression(sym, &op2);
+ zend_ffi_expr_is_less(val, &op2);
+ } else if (sym == YY__GREATER) {
+ sym = get_sym();
+ sym = parse_shift_expression(sym, &op2);
+ zend_ffi_expr_is_greater(val, &op2);
+ } else if (sym == YY__LESS_EQUAL) {
+ sym = get_sym();
+ sym = parse_shift_expression(sym, &op2);
+ zend_ffi_expr_is_less_or_equal(val, &op2);
+ } else if (sym == YY__GREATER_EQUAL) {
+ sym = get_sym();
+ sym = parse_shift_expression(sym, &op2);
+ zend_ffi_expr_is_greater_or_equal(val, &op2);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ }
+ return sym;
+}
+
+static int parse_shift_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2;
+ sym = parse_additive_expression(sym, val);
+ while (sym == YY__LESS_LESS || sym == YY__GREATER_GREATER) {
+ if (sym == YY__LESS_LESS) {
+ sym = get_sym();
+ sym = parse_additive_expression(sym, &op2);
+ zend_ffi_expr_shift_left(val, &op2);
+ } else if (sym == YY__GREATER_GREATER) {
+ sym = get_sym();
+ sym = parse_additive_expression(sym, &op2);
+ zend_ffi_expr_shift_right(val, &op2);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ }
+ return sym;
+}
+
+static int parse_additive_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2;
+ sym = parse_multiplicative_expression(sym, val);
+ while (sym == YY__PLUS || sym == YY__MINUS) {
+ if (sym == YY__PLUS) {
+ sym = get_sym();
+ sym = parse_multiplicative_expression(sym, &op2);
+ zend_ffi_expr_add(val, &op2);
+ } else if (sym == YY__MINUS) {
+ sym = get_sym();
+ sym = parse_multiplicative_expression(sym, &op2);
+ zend_ffi_expr_sub(val, &op2);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ }
+ return sym;
+}
+
+static int parse_multiplicative_expression(int sym, zend_ffi_val *val) {
+ zend_ffi_val op2;
+ sym = parse_cast_expression(sym, val);
+ while (sym == YY__STAR || sym == YY__SLASH || sym == YY__PERCENT) {
+ if (sym == YY__STAR) {
+ sym = get_sym();
+ sym = parse_cast_expression(sym, &op2);
+ zend_ffi_expr_mul(val, &op2);
+ } else if (sym == YY__SLASH) {
+ sym = get_sym();
+ sym = parse_cast_expression(sym, &op2);
+ zend_ffi_expr_div(val, &op2);
+ } else if (sym == YY__PERCENT) {
+ sym = get_sym();
+ sym = parse_cast_expression(sym, &op2);
+ zend_ffi_expr_mod(val, &op2);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ }
+ return sym;
+}
+
+static int parse_cast_expression(int sym, zend_ffi_val *val) {
+ int do_cast = 0;
+ zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
+ if ((sym == YY__LPAREN) && synpred_5(sym)) {
+ sym = get_sym();
+ sym = parse_type_name(sym, &dcl);
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ do_cast = 1;
+ }
+ sym = parse_unary_expression(sym, val);
+ if (do_cast) zend_ffi_expr_cast(val, &dcl);
+ return sym;
+}
+
+static int parse_unary_expression(int sym, zend_ffi_val *val) {
+ const char *name;
+ size_t name_len;
+ zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
+ switch (sym) {
+ case YY_ID:
+ sym = parse_ID(sym, &name, &name_len);
+ zend_ffi_resolve_const(name, name_len, val);
+ while (YY_IN_SET(sym, (YY__LBRACK,YY__LPAREN,YY__POINT,YY__MINUS_GREATER,YY__PLUS_PLUS,YY__MINUS_MINUS), "\000\000\002\000\000\000\010\002\000\160\000\000")) {
+ switch (sym) {
+ case YY__LBRACK:
+ sym = get_sym();
+ sym = parse_expr_list(sym);
+ if (sym != YY__RBRACK) {
+ yy_error_sym("']' expected, got", sym);
+ }
+ sym = get_sym();
+ break;
+ case YY__LPAREN:
+ sym = get_sym();
+ if (YY_IN_SET(sym, (YY__LPAREN,YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__STAR,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ sym = parse_expr_list(sym);
+ }
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ break;
+ case YY__POINT:
+ sym = get_sym();
+ sym = parse_ID(sym, &name, &name_len);
+ break;
+ case YY__MINUS_GREATER:
+ sym = get_sym();
+ sym = parse_ID(sym, &name, &name_len);
+ break;
+ case YY__PLUS_PLUS:
+ sym = get_sym();
+ break;
+ case YY__MINUS_MINUS:
+ sym = get_sym();
+ break;
+ default:
+ yy_error_sym("unexpected", sym);
+ }
+ zend_ffi_val_error(val);
+ }
+ break;
+ case YY_OCTNUMBER:
+ sym = parse_OCTNUMBER(sym, val);
+ break;
+ case YY_DECNUMBER:
+ sym = parse_DECNUMBER(sym, val);
+ break;
+ case YY_HEXNUMBER:
+ sym = parse_HEXNUMBER(sym, val);
+ break;
+ case YY_FLOATNUMBER:
+ sym = parse_FLOATNUMBER(sym, val);
+ break;
+ case YY_STRING:
+ sym = parse_STRING(sym, val);
+ break;
+ case YY_CHARACTER:
+ sym = parse_CHARACTER(sym, val);
+ break;
+ case YY__LPAREN:
+ sym = get_sym();
+ sym = parse_expression(sym, val);
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ break;
+ case YY__PLUS_PLUS:
+ sym = get_sym();
+ sym = parse_unary_expression(sym, val);
+ zend_ffi_val_error(val);
+ break;
+ case YY__MINUS_MINUS:
+ sym = get_sym();
+ sym = parse_unary_expression(sym, val);
+ zend_ffi_val_error(val);
+ break;
+ case YY__AND:
+ sym = get_sym();
+ sym = parse_cast_expression(sym, val);
+ zend_ffi_val_error(val);
+ break;
+ case YY__STAR:
+ sym = get_sym();
+ sym = parse_cast_expression(sym, val);
+ zend_ffi_val_error(val);
+ break;
+ case YY__PLUS:
+ sym = get_sym();
+ sym = parse_cast_expression(sym, val);
+ zend_ffi_expr_plus(val);
+ break;
+ case YY__MINUS:
+ sym = get_sym();
+ sym = parse_cast_expression(sym, val);
+ zend_ffi_expr_neg(val);
+ break;
+ case YY__TILDE:
+ sym = get_sym();
+ sym = parse_cast_expression(sym, val);
+ zend_ffi_expr_bw_not(val);
+ break;
+ case YY__BANG:
+ sym = get_sym();
+ sym = parse_cast_expression(sym, val);
+ zend_ffi_expr_bool_not(val);
+ break;
+ case YY_SIZEOF:
+ sym = get_sym();
+ if ((sym == YY__LPAREN) && synpred_6(sym)) {
+ sym = get_sym();
+ sym = parse_type_name(sym, &dcl);
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ zend_ffi_expr_sizeof_type(val, &dcl);
+ } else if (YY_IN_SET(sym, (YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__STAR,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ sym = parse_unary_expression(sym, val);
+ zend_ffi_expr_sizeof_val(val);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ break;
+ case YY__ALIGNOF:
+ sym = get_sym();
+ if (sym != YY__LPAREN) {
+ yy_error_sym("'(' expected, got", sym);
+ }
+ sym = get_sym();
+ sym = parse_type_name(sym, &dcl);
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ zend_ffi_expr_alignof_type(val, &dcl);
+ break;
+ case YY___ALIGNOF:
+ case YY___ALIGNOF__:
+ if (sym == YY___ALIGNOF) {
+ sym = get_sym();
+ } else if (sym == YY___ALIGNOF__) {
+ sym = get_sym();
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ if ((sym == YY__LPAREN) && synpred_7(sym)) {
+ sym = get_sym();
+ sym = parse_type_name(sym, &dcl);
+ if (sym != YY__RPAREN) {
+ yy_error_sym("')' expected, got", sym);
+ }
+ sym = get_sym();
+ zend_ffi_expr_alignof_type(val, &dcl);
+ } else if (YY_IN_SET(sym, (YY_ID,YY_OCTNUMBER,YY_DECNUMBER,YY_HEXNUMBER,YY_FLOATNUMBER,YY_STRING,YY_CHARACTER,YY__LPAREN,YY__PLUS_PLUS,YY__MINUS_MINUS,YY__AND,YY__STAR,YY__PLUS,YY__MINUS,YY__TILDE,YY__BANG,YY_SIZEOF,YY__ALIGNOF,YY___ALIGNOF,YY___ALIGNOF__), "\000\000\002\000\000\000\004\200\000\343\377\017")) {
+ sym = parse_unary_expression(sym, val);
+ zend_ffi_expr_alignof_val(val);
+ } else {
+ yy_error_sym("unexpected", sym);
+ }
+ break;
+ default:
+ yy_error_sym("unexpected", sym);
+ }
+ return sym;
+}
+
+static int parse_ID(int sym, const char **name, size_t *name_len) {
+ if (sym != YY_ID) {
+ yy_error_sym("<ID> expected, got", sym);
+ }
+ *name = (const char*)yy_text; *name_len = yy_pos - yy_text;
+ sym = get_sym();
+ return sym;
+}
+
+static int parse_OCTNUMBER(int sym, zend_ffi_val *val) {
+ if (sym != YY_OCTNUMBER) {
+ yy_error_sym("<OCTNUMBER> expected, got", sym);
+ }
+ zend_ffi_val_number(val, 8, (const char*)yy_text, yy_pos - yy_text);
+ sym = get_sym();
+ return sym;
+}
+
+static int parse_DECNUMBER(int sym, zend_ffi_val *val) {
+ if (sym != YY_DECNUMBER) {
+ yy_error_sym("<DECNUMBER> expected, got", sym);
+ }
+ zend_ffi_val_number(val, 10, (const char*)yy_text, yy_pos - yy_text);
+ sym = get_sym();
+ return sym;
+}
+
+static int parse_HEXNUMBER(int sym, zend_ffi_val *val) {
+ if (sym != YY_HEXNUMBER) {
+ yy_error_sym("<HEXNUMBER> expected, got", sym);
+ }
+ zend_ffi_val_number(val, 16, (const char*)yy_text + 2, yy_pos - yy_text - 2);
+ sym = get_sym();
+ return sym;
+}
+
+static int parse_FLOATNUMBER(int sym, zend_ffi_val *val) {
+ if (sym != YY_FLOATNUMBER) {
+ yy_error_sym("<FLOATNUMBER> expected, got", sym);
+ }
+ zend_ffi_val_float_number(val, (const char*)yy_text, yy_pos - yy_text);
+ sym = get_sym();
+ return sym;
+}
+
+static int parse_STRING(int sym, zend_ffi_val *val) {
+ if (sym != YY_STRING) {
+ yy_error_sym("<STRING> expected, got", sym);
+ }
+ zend_ffi_val_string(val, (const char*)yy_text, yy_pos - yy_text);
+ sym = get_sym();
+ return sym;
+}
+
+static int parse_CHARACTER(int sym, zend_ffi_val *val) {
+ if (sym != YY_CHARACTER) {
+ yy_error_sym("<CHARACTER> expected, got", sym);
+ }
+ zend_ffi_val_character(val, (const char*)yy_text, yy_pos - yy_text);
+ sym = get_sym();
+ return sym;
+}
+
+static void parse(void) {
+ int sym;
+
+ yy_pos = yy_text = yy_buf;
+ yy_line = 1;
+ sym = parse_declarations(get_sym());
+ if (sym != YY_EOF) {
+ yy_error_sym("<EOF> expected, got", sym);
+ }
+}
+
+int zend_ffi_parse_decl(const char *str, size_t len) {
+ if (SETJMP(FFI_G(bailout))==0) {
+ FFI_G(allow_vla) = 0;
+ yy_buf = (unsigned char*)str;
+ yy_end = yy_buf + len;
+ parse();
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ }
+}
+
+int zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl) {
+ int sym;
+
+ if (SETJMP(FFI_G(bailout))==0) {
+ FFI_G(allow_vla) = 0;
+ yy_pos = yy_text = yy_buf = (unsigned char*)str;
+ yy_end = yy_buf + len;
+ yy_line = 1;
+ sym = parse_type_name(get_sym(), dcl);
+ if (sym != YY_EOF) {
+ yy_error_sym("<EOF> expected, got", sym);
+ }
+ zend_ffi_validate_type_name(dcl);
+ return SUCCESS;
+ } else {
+ return FAILURE;
+ };
+}
+
+static void yy_error(const char *msg) {
+ zend_ffi_parser_error("%s at line %d", msg, yy_line);
+}
+
+static void yy_error_sym(const char *msg, int sym) {
+ zend_ffi_parser_error("%s '%s' at line %d", msg, sym_name[sym], yy_line);
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 7 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2018 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+
+#ifndef PHP_FFI_H
+#define PHP_FFI_H
+
+extern zend_module_entry ffi_module_entry;
+#define phpext_ffi_ptr &ffi_module_entry
+
+typedef enum _zend_ffi_api_restriction {
+ ZEND_FFI_DISABLED = 0, /* completely disabled */
+ ZEND_FFI_ENABLED = 1, /* enabled everywhere */
+ ZEND_FFI_PRELOAD = 2, /* enabled only in preloaded scripts and CLI */
+} zend_ffi_api_restriction;
+
+typedef struct _zend_ffi_type zend_ffi_type;
+
+ZEND_BEGIN_MODULE_GLOBALS(ffi)
+ zend_ffi_api_restriction restriction;
+ zend_bool is_cli;
+
+ /* predefined ffi_types */
+ HashTable types;
+
+ /* preloading */
+ HashTable *scopes; /* list of preloaded scopes */
+
+ /* callbacks */
+ HashTable *callbacks;
+
+ /* weak type references */
+ HashTable *weak_types;
+
+ /* ffi_parser */
+ JMP_BUF bailout;
+ unsigned const char *buf;
+ unsigned const char *end;
+ unsigned const char *pos;
+ unsigned const char *text;
+ int line;
+ HashTable *symbols;
+ HashTable *tags;
+ zend_bool allow_vla;
+ zend_bool persistent;
+ uint32_t default_type_attr;
+ZEND_END_MODULE_GLOBALS(ffi)
+
+ZEND_EXTERN_MODULE_GLOBALS(ffi)
+
+#ifdef PHP_WIN32
+# define PHP_FFI_API __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+# define PHP_FFI_API __attribute__ ((visibility("default")))
+#else
+# define PHP_FFI_API
+#endif
+
+#define FFI_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(ffi, v)
+
+#define ZEND_FFI_DCL_VOID (1<<0)
+#define ZEND_FFI_DCL_CHAR (1<<1)
+#define ZEND_FFI_DCL_SHORT (1<<2)
+#define ZEND_FFI_DCL_INT (1<<3)
+#define ZEND_FFI_DCL_LONG (1<<4)
+#define ZEND_FFI_DCL_LONG_LONG (1<<5)
+#define ZEND_FFI_DCL_FLOAT (1<<6)
+#define ZEND_FFI_DCL_DOUBLE (1<<7)
+#define ZEND_FFI_DCL_SIGNED (1<<8)
+#define ZEND_FFI_DCL_UNSIGNED (1<<9)
+#define ZEND_FFI_DCL_BOOL (1<<10)
+#define ZEND_FFI_DCL_COMPLEX (1<<11)
+
+#define ZEND_FFI_DCL_STRUCT (1<<12)
+#define ZEND_FFI_DCL_UNION (1<<13)
+#define ZEND_FFI_DCL_ENUM (1<<14)
+#define ZEND_FFI_DCL_TYPEDEF_NAME (1<<15)
+
+#define ZEND_FFI_DCL_TYPE_SPECIFIERS \
+ (ZEND_FFI_DCL_VOID|ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT \
+ |ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG \
+ |ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_SIGNED \
+ |ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_BOOL|ZEND_FFI_DCL_COMPLEX \
+ |ZEND_FFI_DCL_STRUCT|ZEND_FFI_DCL_UNION|ZEND_FFI_DCL_ENUM \
+ |ZEND_FFI_DCL_TYPEDEF_NAME)
+
+#define ZEND_FFI_DCL_TYPEDEF (1<<16)
+#define ZEND_FFI_DCL_EXTERN (1<<17)
+#define ZEND_FFI_DCL_STATIC (1<<18)
+#define ZEND_FFI_DCL_AUTO (1<<19)
+#define ZEND_FFI_DCL_REGISTER (1<<20)
+
+#define ZEND_FFI_DCL_STORAGE_CLASS \
+ (ZEND_FFI_DCL_TYPEDEF|ZEND_FFI_DCL_EXTERN|ZEND_FFI_DCL_STATIC \
+ |ZEND_FFI_DCL_AUTO|ZEND_FFI_DCL_REGISTER)
+
+#define ZEND_FFI_DCL_CONST (1<<21)
+#define ZEND_FFI_DCL_RESTRICT (1<<22)
+#define ZEND_FFI_DCL_VOLATILE (1<<23)
+#define ZEND_FFI_DCL_ATOMIC (1<<24)
+
+#define ZEND_FFI_DCL_TYPE_QUALIFIERS \
+ (ZEND_FFI_DCL_CONST|ZEND_FFI_DCL_RESTRICT|ZEND_FFI_DCL_VOLATILE \
+ |ZEND_FFI_DCL_ATOMIC)
+
+#define ZEND_FFI_DCL_INLINE (1<<25)
+#define ZEND_FFI_DCL_NO_RETURN (1<<26)
+
+#define ZEND_FFI_ABI_DEFAULT 0
+
+#define ZEND_FFI_ABI_CDECL 1 // FFI_DEFAULT_ABI
+#define ZEND_FFI_ABI_FASTCALL 2 // FFI_FASTCALL
+#define ZEND_FFI_ABI_THISCALL 3 // FFI_THISCALL
+#define ZEND_FFI_ABI_STDCALL 4 // FFI_STDCALL
+#define ZEND_FFI_ABI_PASCAL 5 // FFI_PASCAL
+#define ZEND_FFI_ABI_REGISTER 6 // FFI_REGISTER
+#define ZEND_FFI_ABI_MS 7 // FFI_MS_CDECL
+#define ZEND_FFI_ABI_SYSV 8 // FFI_SYSV
+
+#define ZEND_FFI_ATTR_CONST (1<<0)
+#define ZEND_FFI_ATTR_INCOMPLETE_TAG (1<<1)
+#define ZEND_FFI_ATTR_VARIADIC (1<<2)
+#define ZEND_FFI_ATTR_INCOMPLETE_ARRAY (1<<3)
+#define ZEND_FFI_ATTR_VLA (1<<4)
+#define ZEND_FFI_ATTR_UNION (1<<5)
+#define ZEND_FFI_ATTR_PACKED (1<<6)
+#define ZEND_FFI_ATTR_MS_STRUCT (1<<7)
+#define ZEND_FFI_ATTR_GCC_STRUCT (1<<8)
+
+#define ZEND_FFI_ATTR_PERSISTENT (1<<9)
+#define ZEND_FFI_ATTR_STORED (1<<10)
+
+#define ZEND_FFI_STRUCT_ATTRS \
+ (ZEND_FFI_ATTR_UNION|ZEND_FFI_ATTR_PACKED|ZEND_FFI_ATTR_MS_STRUCT \
+ |ZEND_FFI_ATTR_GCC_STRUCT)
+
+#define ZEND_FFI_ENUM_ATTRS \
+ (ZEND_FFI_ATTR_PACKED)
+
+#define ZEND_FFI_ARRAY_ATTRS \
+ (ZEND_FFI_ATTR_CONST|ZEND_FFI_ATTR_VLA|ZEND_FFI_ATTR_INCOMPLETE_ARRAY)
+
+#define ZEND_FFI_FUNC_ATTRS \
+ (ZEND_FFI_ATTR_VARIADIC)
+
+#define ZEND_FFI_POINTER_ATTRS \
+ (ZEND_FFI_ATTR_CONST)
+
+typedef struct _zend_ffi_dcl {
+ uint32_t flags;
+ uint32_t align;
+ uint16_t attr;
+ uint16_t abi;
+ zend_ffi_type *type;
+} zend_ffi_dcl;
+
+#define ZEND_FFI_ATTR_INIT {0, 0, 0, 0, NULL}
+
+typedef enum _zend_ffi_val_kind {
+ ZEND_FFI_VAL_EMPTY,
+ ZEND_FFI_VAL_ERROR,
+ ZEND_FFI_VAL_INT32,
+ ZEND_FFI_VAL_INT64,
+ ZEND_FFI_VAL_UINT32,
+ ZEND_FFI_VAL_UINT64,
+ ZEND_FFI_VAL_FLOAT,
+ ZEND_FFI_VAL_DOUBLE,
+ ZEND_FFI_VAL_LONG_DOUBLE,
+ ZEND_FFI_VAL_CHAR,
+ ZEND_FFI_VAL_STRING,
+} zend_ffi_val_kind;
+
+#ifdef HAVE_LONG_DOUBLE
+typedef long double zend_ffi_double;
+#else
+typedef double zend_ffi_double;
+#endif
+
+typedef struct _zend_ffi_val {
+ zend_ffi_val_kind kind;
+ union {
+ uint64_t u64;
+ int64_t i64;
+ zend_ffi_double d;
+ char ch;
+ struct {
+ const char *str;
+ size_t len;
+ };
+ };
+} zend_ffi_val;
+
+int zend_ffi_parse_decl(const char *str, size_t len);
+int zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl);
+
+/* parser callbacks */
+void ZEND_NORETURN zend_ffi_parser_error(const char *msg, ...);
+int zend_ffi_is_typedef_name(const char *name, size_t name_len);
+void zend_ffi_resolve_typedef(const char *name, size_t name_len, zend_ffi_dcl *dcl);
+void zend_ffi_resolve_const(const char *name, size_t name_len, zend_ffi_val *val);
+void zend_ffi_declare_tag(const char *name, size_t name_len, zend_ffi_dcl *dcl, zend_bool incomplete);
+void zend_ffi_make_enum_type(zend_ffi_dcl *dcl);
+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);
+void zend_ffi_make_struct_type(zend_ffi_dcl *dcl);
+void zend_ffi_add_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl);
+void zend_ffi_add_anonymous_field(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl);
+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);
+void zend_ffi_adjust_struct_size(zend_ffi_dcl *dcl);
+void zend_ffi_make_pointer_type(zend_ffi_dcl *dcl);
+void zend_ffi_make_array_type(zend_ffi_dcl *dcl, zend_ffi_val *len);
+void zend_ffi_make_func_type(zend_ffi_dcl *dcl, HashTable *args);
+void zend_ffi_add_arg(HashTable **args, const char *name, size_t name_len, zend_ffi_dcl *arg_dcl);
+void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl);
+void zend_ffi_add_attribute(zend_ffi_dcl *dcl, const char *name, size_t name_len);
+void zend_ffi_add_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, int n, zend_ffi_val *val);
+void zend_ffi_add_msvc_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, zend_ffi_val *val);
+void zend_ffi_set_abi(zend_ffi_dcl *dcl, uint16_t abi);
+void zend_ffi_nested_declaration(zend_ffi_dcl *dcl, zend_ffi_dcl *nested_dcl);
+void zend_ffi_align_as_type(zend_ffi_dcl *dcl, zend_ffi_dcl *align_dcl);
+void zend_ffi_align_as_val(zend_ffi_dcl *dcl, zend_ffi_val *align_val);
+void zend_ffi_validate_type_name(zend_ffi_dcl *dcl);
+
+void zend_ffi_expr_conditional(zend_ffi_val *val, zend_ffi_val *op2, zend_ffi_val *op3);
+void zend_ffi_expr_bool_or(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_bool_and(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_bw_or(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_bw_xor(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_bw_and(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_is_equal(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_is_not_equal(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_is_less(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_is_greater(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_is_less_or_equal(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_is_greater_or_equal(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_shift_left(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_shift_right(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_add(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_sub(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_mul(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_div(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_mod(zend_ffi_val *val, zend_ffi_val *op2);
+void zend_ffi_expr_cast(zend_ffi_val *val, zend_ffi_dcl *dcl);
+void zend_ffi_expr_plus(zend_ffi_val *val);
+void zend_ffi_expr_neg(zend_ffi_val *val);
+void zend_ffi_expr_bw_not(zend_ffi_val *val);
+void zend_ffi_expr_bool_not(zend_ffi_val *val);
+void zend_ffi_expr_sizeof_val(zend_ffi_val *val);
+void zend_ffi_expr_sizeof_type(zend_ffi_val *val, zend_ffi_dcl *dcl);
+void zend_ffi_expr_alignof_val(zend_ffi_val *val);
+void zend_ffi_expr_alignof_type(zend_ffi_val *val, zend_ffi_dcl *dcl);
+
+static zend_always_inline void zend_ffi_val_error(zend_ffi_val *val) /* {{{ */
+{
+ val->kind = ZEND_FFI_VAL_ERROR;
+}
+/* }}} */
+
+void zend_ffi_val_number(zend_ffi_val *val, int base, const char *str, size_t str_len);
+void zend_ffi_val_float_number(zend_ffi_val *val, const char *str, size_t str_len);
+void zend_ffi_val_string(zend_ffi_val *val, const char *str, size_t str_len);
+void zend_ffi_val_character(zend_ffi_val *val, const char *str, size_t str_len);
+
+#endif /* PHP_FFI_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
--- /dev/null
+--TEST--
+FFI 001: Check if FFI is loaded
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+echo 'The extension "FFI" is available';
+?>
+--EXPECT--
+The extension "FFI" is available
--- /dev/null
+--TEST--
+FFI 002: Check C declaration parser
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+echo "Empty declaration\n";
+$ffi = FFI::cdef(<<<EOF
+EOF
+);
+echo " ok\n";
+
+echo "Various declarations\n";
+$ffi = FFI::cdef(<<<EOF
+ /* allowed storage classes */
+ typedef int type1;
+// extern int var2;
+ static int var3;
+ auto int var4;
+ register int var5;
+
+ /* allowed types */
+ typedef void type6;
+ typedef char type7; /* sint8_t or uint8_t */
+ typedef signed char type8; /* sint8_t */
+ typedef unsigned char type9; /* uint8_t */
+ typedef short type10; /* sint16_t */
+ typedef signed short type11; /* sint16_t */
+ typedef short int type12; /* sint16_t */
+ typedef signed short int type13; /* sint16_t */
+ typedef unsigned short type14; /* uint16_t */
+ typedef unsigned short int type15; /* uint16_t */
+ typedef int type16; /* sint32_t */
+ typedef signed type17; /* sint32_t */
+ typedef signed int type18; /* sint32_t */
+ typedef unsigned type19; /* uint32_t */
+ typedef unsigned int type20; /* uint32_t */
+ typedef long type21; /* sint32_t or sint64_t */
+ typedef signed long type22; /* sint32_t or sint64_t */
+ typedef long int type23; /* sint32_t or sint64_t */
+ typedef signed long int type24; /* sint32_t or sint64_t */
+ typedef unsigned long type25; /* uint32_t or uint64_t */
+ typedef unsigned long int type25_2; /* uint32_t or uint64_t */
+ typedef long long type26; /* sint64_t */
+ typedef signed long long type27; /* sint64_t */
+ typedef long long int type28; /* sint64_t */
+ typedef signed long long int type29; /* sint64_t */
+ typedef unsigned long long type30; /* uint64_t */
+ typedef unsigned long long int type31; /* uint64_t */
+ typedef float type32;
+ typedef double type33;
+ typedef long double type34;
+ typedef _Bool type35;
+// typedef float _Complex type36;
+// typedef double _Complex type36_2;
+// typedef long double _Complex type36_3;
+
+ /* struct and union */
+ struct tag1;
+ union tag2;
+ typedef struct tag1 {int x; int y;} type37;
+ typedef union tag2 {int x; int y;} type38;
+ typedef struct {int x, y; int z;} type39;
+ typedef struct {unsigned int x:8, y:8;} type40;
+ typedef struct {unsigned int x:8, :8, y:8;} type41;
+
+ /* enum */
+ enum tag3;
+ typedef enum tag3 {A,B,C} type42;
+ typedef enum {D,E=10,F,} type43;
+
+ /* type qualifiers */
+ typedef void* type46;
+ typedef const void* type47;
+ typedef restrict void* type48;
+ typedef volatile void* type49;
+ typedef _Atomic void* type50;
+ typedef const volatile void* type51;
+
+ /* function specifiers */
+ static void f1();
+ static inline void f2();
+ static _Noreturn void f3();
+
+ /* align specifier */
+ typedef double _Alignas(char) type52;
+ typedef double _Alignas(1) type53;
+
+ /* pointers */
+ typedef void * type54;
+ typedef void ** type55;
+ typedef const void * const volatile * const type56;
+
+ /* arrays */
+ typedef char type57[];
+ typedef char type58[const];
+ typedef char type59[const volatile];
+ typedef char type60[10];
+ typedef char type61[const 10];
+ typedef char type62[static 10];
+ typedef char type63[static const volatile 10];
+ typedef char type64[const volatile static 10];
+ typedef char type65[];
+ typedef char type66[const volatile];
+ typedef char type67[10][10];
+
+ /* functions */
+ static void f4();
+ static void f5(void);
+ static void f6(int x);
+ static void f7(int x, int y);
+ static void f8(int x, int y, ...);
+ static void f9(int, int);
+ static void f9(int, int, ...);
+ static void f10(...);
+ static void f11(const char *name);
+ static void f12(const char *);
+ static void f13(const int a[5]);
+ static void f14(const int[5]);
+
+ /* nested */
+ typedef int *type69[4];
+ typedef int (*type70)[4];
+ typedef int (*type71[3])(int *x, int *y);
+ typedef int (*type72(int (*)(long), int))(int, ...);
+EOF
+);
+echo " ok\n";
+?>
+--EXPECT--
+Empty declaration
+ ok
+Various declarations
+ ok
--- /dev/null
+--TEST--
+FFI 003: Forward tag/typedef declarations
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$ffi = FFI::cdef(<<<EOF
+struct _a;
+struct _a {int x;};
+
+struct _b {int x;};
+struct _b;
+
+typedef struct _c c;
+struct _c {int x;};
+
+struct _d {int x;};
+typedef struct _d d;
+
+struct _e;
+
+struct _f;
+typedef struct _f f;
+EOF
+);
+var_dump($ffi->new("struct _a"));
+var_dump($ffi->new("struct _b"));
+var_dump($ffi->new("c"));
+var_dump($ffi->new("d"));
+try {
+ var_dump($ffi->new("struct _e"));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ var_dump($ffi->new("f"));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+echo "ok\n";
+?>
+--EXPECTF--
+object(FFI\CData:<struct>)#%d (1) {
+ ["x"]=>
+ int(0)
+}
+object(FFI\CData:<struct>)#%d (1) {
+ ["x"]=>
+ int(0)
+}
+object(FFI\CData:<struct>)#%d (1) {
+ ["x"]=>
+ int(0)
+}
+object(FFI\CData:<struct>)#%d (1) {
+ ["x"]=>
+ int(0)
+}
+FFI\ParserException: incomplete 'struct _e' at line 1
+FFI\ParserException: incomplete 'struct _f' at line 1
+ok
--- /dev/null
+--TEST--
+FFI 004: Enum declarations
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$ffi = FFI::cdef(<<<EOF
+enum _a;
+enum _a {e1};
+
+enum _b {e2};
+enum _b;
+
+typedef enum _c c;
+enum _c {e3};
+
+enum _d {e4};
+typedef enum _d d;
+
+enum _e;
+
+enum _f;
+typedef enum _f f;
+
+enum _g {
+ _c1,
+ _c2,
+ _c3 = 1,
+ _c4,
+};
+EOF
+);
+var_dump($ffi->new("enum _a"));
+var_dump($ffi->new("enum _b"));
+var_dump($ffi->new("c"));
+var_dump($ffi->new("d"));
+var_dump($ffi->new("int[_c2]"));
+var_dump($ffi->new("int[_c3]"));
+var_dump($ffi->new("int[_c4]"));
+try {
+ var_dump($ffi->new("enum _e"));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ var_dump($ffi->new("f"));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+echo "ok\n";
+?>
+--EXPECTF--
+object(FFI\CData:<enum>)#%d (1) {
+ ["cdata"]=>
+ int(0)
+}
+object(FFI\CData:<enum>)#%d (1) {
+ ["cdata"]=>
+ int(0)
+}
+object(FFI\CData:<enum>)#%d (1) {
+ ["cdata"]=>
+ int(0)
+}
+object(FFI\CData:<enum>)#%d (1) {
+ ["cdata"]=>
+ int(0)
+}
+object(FFI\CData:int32_t[1])#%d (1) {
+ [0]=>
+ int(0)
+}
+object(FFI\CData:int32_t[1])#%d (1) {
+ [0]=>
+ int(0)
+}
+object(FFI\CData:int32_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+}
+FFI\ParserException: incomplete 'enum _e' at line 1
+FFI\ParserException: incomplete 'enum _f' at line 1
+ok
--- /dev/null
+--TEST--
+FFI 005: Array assignment
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$m = FFI::new("int[2][2]");
+$v = FFI::new("int[2]");
+$v[1] = 42;
+$m[1] = $v;
+var_dump($m);
+?>
+--EXPECTF--
+object(FFI\CData:int32_t[2][2])#%d (2) {
+ [0]=>
+ object(FFI\CData:int32_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+ }
+ [1]=>
+ object(FFI\CData:int32_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(42)
+ }
+}
--- /dev/null
+--TEST--
+FFI 006: Pointer assignment
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$v = FFI::new("int*[2]");
+$v[1] = FFI::new("int[1]", false);
+$v[1][0] = 42;
+var_dump($v);
+FFI::free($v[1]);
+var_dump($v);
+?>
+--EXPECTF--
+object(FFI\CData:int32_t*[2])#%d (2) {
+ [0]=>
+ NULL
+ [1]=>
+ object(FFI\CData:int32_t*)#%d (1) {
+ [0]=>
+ int(42)
+ }
+}
+object(FFI\CData:int32_t*[2])#%d (2) {
+ [0]=>
+ NULL
+ [1]=>
+ NULL
+}
--- /dev/null
+--TEST--
+FFI 007: Pointer comparison
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$v = FFI::new("int*[3]");
+$v[0] = FFI::new("int[1]", false);
+$v[1] = FFI::new("int[1]", false);
+$v[2] = $v[1];
+$v[1][0] = 42;
+var_dump($v[0] == $v[1]);
+var_dump($v[1] == $v[2]);
+FFI::free($v[0]);
+FFI::free($v[1]);
+?>
+--EXPECT--
+bool(false)
+bool(true)
--- /dev/null
+--TEST--
+FFI 008: Array iteration
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$a = FFI::new("int[3]");
+$a[1] = 10;
+$a[2] = 20;
+var_dump(count($a));
+foreach ($a as $key => $val) {
+ echo "$key => $val\n";
+}
+
+$a = FFI::new("struct {int x,y;}");
+try {
+ var_dump(@count($a));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+
+try {
+ foreach ($a as $key => $val) {
+ echo "$key => $val\n";
+ }
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+--EXPECT--
+int(3)
+0 => 0
+1 => 10
+2 => 20
+FFI\Exception: Attempt to count() on non C array
+FFI\Exception: Attempt to iterate on non C array
--- /dev/null
+--TEST--
+FFI 009: memcpy(), memcmp(), memset() and sizeof()
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$a = FFI::new("int[3]");
+$a[1] = 10;
+$a[2] = 20;
+$b = FFI::new("int[4]");
+var_dump(FFI::memcmp($b, $a, FFI::sizeof($a)));
+FFI::memcpy($b, $a, FFI::sizeof($a));
+var_dump($b);
+var_dump(FFI::memcmp($b, $a, FFI::sizeof($a)));
+FFI::memset($a, -1, FFI::sizeof($a));
+var_dump($a);
+?>
+--EXPECTF--
+int(-1)
+object(FFI\CData:int32_t[4])#%d (4) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(10)
+ [2]=>
+ int(20)
+ [3]=>
+ int(0)
+}
+int(0)
+object(FFI\CData:int32_t[3])#%d (3) {
+ [0]=>
+ int(-1)
+ [1]=>
+ int(-1)
+ [2]=>
+ int(-1)
+}
--- /dev/null
+--TEST--
+FFI 010: string()
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$a = FFI::new("int[3]");
+FFI::memset($a, ord("a"), FFI::sizeof($a));
+var_dump(FFI::string($a, FFI::sizeof($a)));
+?>
+--EXPECT--
+string(12) "aaaaaaaaaaaa"
\ No newline at end of file
--- /dev/null
+--TEST--
+FFI 011: cast()
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$a = FFI::new("uint8_t[4]");
+$a[0] = 255;
+$a[1] = 255;
+var_dump(FFI::cast("uint16_t[2]", $a));
+?>
+--EXPECTF--
+object(FFI\CData:uint16_t[2])#%d (2) {
+ [0]=>
+ int(65535)
+ [1]=>
+ int(0)
+}
--- /dev/null
+--TEST--
+FFI 012: serialization
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+try {
+ var_dump(serialize(FFI::new("int[2]")));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+--EXPECT--
+Exception: Serialization of 'FFI\CData' is not allowed
--- /dev/null
+--TEST--
+FFI 013: Declaration priorities and constrains
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$a = FFI::new("int[1][2][3]");
+var_dump(count($a));
+var_dump(count($a[0]));
+var_dump(count($a[0][0]));
+
+try {
+ var_dump(FFI::new("void"));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+
+try {
+ var_dump(FFI::new("void[1]"));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("static int foo(int)[5];");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("static int foo[5](int);");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("static int foo(int)(int);");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("typedef int foo[2][];");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("typedef int foo[][2];");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+--EXPECT--
+int(1)
+int(2)
+int(3)
+FFI\ParserException: 'void' type is not allowed at line 1
+FFI\ParserException: 'void' type is not allowed at line 1
+FFI\ParserException: function returning array is not allowed at line 1
+FFI\ParserException: array of functions is not allowed at line 1
+FFI\ParserException: function returning function is not allowed at line 1
+FFI\ParserException: only the leftmost array can be undimensioned at line 1
+ok
--- /dev/null
+--TEST--
+FFI 014: Size of nested types
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+var_dump(FFI::sizeof(FFI::new("uint32_t[2]")));
+var_dump(FFI::sizeof(FFI::new("uint32_t([2])")));
+var_dump(FFI::sizeof(FFI::new("uint32_t([2])[2]")));
+?>
+ok
+--EXPECT--
+int(8)
+int(8)
+int(16)
+ok
\ No newline at end of file
--- /dev/null
+--TEST--
+FFI 015: Incomplete type usage
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+try {
+ FFI::cdef("struct DIR; static struct DIR dir;");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct DIR; static struct DIR *ptr;");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct DIR; typedef struct DIR DIR; static DIR dir;");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct DIR; typedef struct DIR DIR; static DIR *ptr;");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct DIR; static struct DIR foo();");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct DIR; static struct DIR* foo();");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct DIR; static void foo(struct DIR);");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct DIR; static void foo(struct DIR*);");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+ok
+--EXPECT--
+FFI\ParserException: incomplete 'struct DIR' at line 1
+ok
+FFI\ParserException: incomplete 'struct DIR' at line 1
+ok
+FFI\ParserException: incomplete 'struct DIR' at line 1
+ok
+FFI\ParserException: incomplete 'struct DIR' at line 1
+ok
+ok
--- /dev/null
+--TEST--
+FFI 016: Structure constraints
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+try {
+ FFI::cdef("struct X {void x();};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct X {struct X x;};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct X {struct X *ptr;};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+ok
+--EXPECT--
+FFI\ParserException: 'function' type is not allowed at line 1
+FFI\ParserException: struct/union can't contain an instance of itself at line 1
+ok
+ok
--- /dev/null
+--TEST--
+FFI 017: Structure constraints & tags cleanup
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+try {
+ var_dump(FFI::new("struct X {void x();}"));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ var_dump(FFI::new("struct X {struct X x;}"));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ var_dump(FFI::new("struct X {struct X *ptr;}"));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+ok
+--EXPECTF--
+FFI\ParserException: 'function' type is not allowed at line 1
+FFI\ParserException: struct/union can't contain an instance of itself at line 1
+object(FFI\CData:<struct>)#%d (1) {
+ ["ptr"]=>
+ NULL
+}
+ok
--- /dev/null
+--TEST--
+FFI 018: Indirectly recursive structure
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+try {
+ FFI::cdef("struct X {struct X x[2];};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct X {struct X *ptr[2];};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+ok
+--EXPECT--
+FFI\ParserException: incomplete 'struct X' at line 1
+ok
+ok
--- /dev/null
+--TEST--
+FFI 019: Parameter type adjustment
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+try {
+ FFI::cdef("static int foo(int[]);");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("static int foo(int bar(int));");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+ok
+--EXPECT--
+ok
+ok
+ok
--- /dev/null
+--TEST--
+FFI 020: read-only
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+try {
+ $p = FFI::new("struct {int x; const int y;}");
+ $p->x = 1;
+ $p->y = 1;
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ $p = FFI::new("struct {const int x; int y;}");
+ $p->y = 1;
+ $p->x = 1;
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ $p = FFI::new("const struct {int x; int y;}");
+ $p->x = 1;
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ $p = FFI::new("const int[10]");
+ $p[1] = 1;
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ $p = FFI::new("const int * [1]");
+ $p[0] = null;
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ $p = FFI::new("int * const [1]");
+ $p[0] = null;
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ $f = FFI::cdef("typedef int * const t[1];");
+ $p = $f->new("t");
+ $p[0] = null;
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+ok
+--EXPECT--
+FFI\Exception: Attempt to assign read-only field 'y'
+FFI\Exception: Attempt to assign read-only field 'x'
+FFI\Exception: Attempt to assign read-only location
+FFI\Exception: Attempt to assign read-only location
+ok
+FFI\Exception: Attempt to assign read-only location
+FFI\Exception: Attempt to assign read-only location
+ok
--- /dev/null
+--TEST--
+FFI 021: packed enums
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+function test($size, $type) {
+ if (FFI::sizeof(FFI::new($type)) !== $size) {
+ echo "FAIL: sizeof($type) != $size\n";
+ }
+}
+
+test(4, "enum {a1, b1}");
+test(1, "enum __attribute__((packed)) {a2, b2}");
+
+test(4, "enum {a3=0, b3=0x80000000}");
+test(8, "enum {a4=-1, b4=0x80000000}");
+test(8, "enum {a5=0x80000000, b5=-1}");
+test(4, "enum {a6=-1, b6=0x7fffffff}");
+test(8, "enum {a7=-1, b7=0x7fffffff, c7}");
+
+test(1, "enum __attribute__((packed)) {a8=0, b8=0xff}");
+test(2, "enum __attribute__((packed)) {a9=0, b9=0x100}");
+
+test(1, "enum __attribute__((packed)) {a10=-1, b10=0x7f}");
+test(2, "enum __attribute__((packed)) {a11=-1, b11=0x80}");
+test(1, "enum __attribute__((packed)) {a12=0x7f, b12=-0x80}");
+test(2, "enum __attribute__((packed)) {a13=0x7f, b13=-0x81}");
+
+test(2, "enum __attribute__((packed)) {a8=0, b8=0xffff}");
+test(4, "enum __attribute__((packed)) {a9=0, b9=0x10000}");
+
+test(2, "enum __attribute__((packed)) {a10=-1, b10=0x7f00}");
+test(4, "enum __attribute__((packed)) {a11=-1, b11=0x8000}");
+test(2, "enum __attribute__((packed)) {a12=0x7f00, b12=-0x8000}");
+test(4, "enum __attribute__((packed)) {a13=0x7f00, b13=-0x8001}");
+
+test(4, "enum __attribute__((packed)) {a8=0, b8=0xffffffff}");
+test(8, "enum __attribute__((packed)) {a9=0, b9=0x100000000}");
+
+test(4, "enum __attribute__((packed)) {a10=-1, b10=0x7f000000}");
+test(8, "enum __attribute__((packed)) {a11=-1, b11=0x80000000}");
+test(4, "enum __attribute__((packed)) {a12=0x7f000000, b12=-0x80000000}");
+test(8, "enum __attribute__((packed)) {a13=0x7f000000, b13=-0x80000001}");
+
+test(1, "enum __attribute__((packed)) {a14=-0x80}");
+test(2, "enum __attribute__((packed)) {a14=-0x81}");
+test(2, "enum __attribute__((packed)) {a14=-0x8000}");
+test(4, "enum __attribute__((packed)) {a14=-0x8001}");
+test(4, "enum __attribute__((packed)) {a14=-0x80000000}");
+test(8, "enum __attribute__((packed)) {a14=-0x80000001}");
+?>
+ok
+--EXPECT--
+ok
--- /dev/null
+--TEST--
+FFI 022: structure/union alignment
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+function test_size($size, $type) {
+ if (FFI::sizeof(FFI::new($type)) !== $size) {
+ echo "FAIL: sizeof($type) != $size\n";
+ }
+}
+
+function test_align($align, $type) {
+ if (FFI::alignof(FFI::new($type)) !== $align) {
+ echo "FAIL: alignof($type) != $align\n";
+ }
+}
+
+test_size(8, "struct {uint32_t a; uint32_t b;}");
+test_size(3, "struct {char a; uint8_t b; uint8_t c;}");
+test_size(8, "struct {char a; uint32_t b;}");
+test_size(8, "struct {uint32_t a; char b;}");
+test_size(5, "struct __attribute__((packed)) {char a; uint32_t b;}");
+test_size(5, "struct __attribute__((packed)) {uint32_t a; char b;}");
+
+test_size(16, "struct {uint32_t a; uint32_t b;}[2]");
+test_size(6, "struct {char a; uint8_t b; uint8_t c;}[2]");
+test_size(16, "struct {char a; uint32_t b;}[2]");
+test_size(16, "struct {uint32_t a; char b;}[2]");
+test_size(10, "struct __attribute__((packed)) {char a; uint32_t b;}[2]");
+test_size(10, "struct __attribute__((packed)) {uint32_t a; char b;}[2]");
+
+test_align(4, "union {uint32_t a; uint32_t b;}");
+test_align(1, "union {char a; uint8_t b; uint8_t c;}");
+test_align(4, "union {char a; uint32_t b;}");
+test_align(4, "union {uint32_t a; char b;}");
+test_align(1, "union __attribute__((packed)) {char a; uint32_t b;}");
+test_align(1, "union __attribute__((packed)) {uint32_t a; char b;}");
+
+test_size(8, "struct {char a __attribute__((packed)); uint32_t b;}");
+test_size(5, "struct {char a; uint32_t b __attribute__((packed));}");
+test_size(5, "struct {uint32_t a __attribute__((packed)); char b;}");
+test_size(8, "struct {uint32_t a; char b __attribute__((packed));}");
+
+test_align(4, "struct {char a __attribute__((packed)); uint32_t b;}");
+test_align(1, "struct {char a; uint32_t b __attribute__((packed));}");
+test_align(1, "struct {uint32_t a __attribute__((packed)); char b;}");
+test_align(4, "struct {uint32_t a; char b __attribute__((packed));}");
+
+test_size(16, "struct __attribute__((aligned(16))) {char a; uint32_t b;}");
+test_align(16, "struct __attribute__((aligned(16))) {char a; uint32_t b;}");
+
+test_size(16, "struct {char a; uint32_t b;} __attribute__((aligned(16)))");
+test_align(16, "struct {char a; uint32_t b;} __attribute__((aligned(16)))");
+
+test_size(8, "struct {char a; uint32_t b __attribute__((aligned(1)));}");
+test_align(4, "struct {char a; uint32_t b __attribute__((aligned(1)));}");
+test_size(32, "struct {char a; uint32_t b __attribute__((aligned(16)));}");
+test_align(16, "struct {char a; uint32_t b __attribute__((aligned(16)));}");
+
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ test_size(32, "struct {char a; uint32_t b __attribute__((aligned));}");
+ test_align(16, "struct {char a; uint32_t b __attribute__((aligned));}");
+}
+
+test_size(16, "struct __declspec(align(16)) {char a; uint32_t b;}");
+test_align(16, "struct __declspec(align(16)) {char a; uint32_t b;}");
+?>
+ok
+--EXPECT--
+ok
--- /dev/null
+--TEST--
+FFI 023: GCC struct extensions
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+ var_dump(FFI::sizeof(FFI::new("struct {}")));
+ var_dump(FFI::sizeof(FFI::new("struct {int a}")));
+ var_dump(FFI::sizeof(FFI::new("struct {int a; int b}")));
+?>
+ok
+--EXPECT--
+int(0)
+int(4)
+int(8)
+ok
--- /dev/null
+--TEST--
+FFI 024: anonymous struct/union
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+ $p = FFI::new("
+ struct {
+ int a;
+ struct {
+ int b;
+ int c;
+ };
+ union {
+ int d;
+ uint32_t e;
+ };
+ int f;
+ }");
+ var_dump(FFI::sizeof($p));
+ $p->a = 1;
+ $p->b = 2;
+ $p->c = 3;
+ $p->d = 4;
+ $p->f = 5;
+ var_dump($p);
+?>
+--EXPECTF--
+int(20)
+object(FFI\CData:<struct>)#%d (6) {
+ ["a"]=>
+ int(1)
+ ["b"]=>
+ int(2)
+ ["c"]=>
+ int(3)
+ ["d"]=>
+ int(4)
+ ["e"]=>
+ int(4)
+ ["f"]=>
+ int(5)
+}
--- /dev/null
+--TEST--
+FFI 025: direct work with primitive types
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+ $x = FFI::new("int");
+ $x = 5;
+ var_dump($x);
+ $x += 2;
+ var_dump($x);
+ echo "$x\n\n";
+ unset($x);
+
+ $x = FFI::new("char");
+ $x = 'a';
+ var_dump($x);
+ $x++;
+ var_dump($x);
+ echo "$x\n\n";
+ unset($x);
+?>
+--EXPECTF--
+object(FFI\CData:int32_t)#%d (1) {
+ ["cdata"]=>
+ int(5)
+}
+object(FFI\CData:int32_t)#%d (1) {
+ ["cdata"]=>
+ int(7)
+}
+7
+
+object(FFI\CData:char)#%d (1) {
+ ["cdata"]=>
+ string(1) "a"
+}
+object(FFI\CData:char)#%d (1) {
+ ["cdata"]=>
+ string(1) "b"
+}
+b
--- /dev/null
+--TEST--
+FFI 026: Array iteration by reference
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$a = FFI::new("int[3]");
+$a[1] = 10;
+$a[2] = 20;
+var_dump($a);
+foreach ($a as &$val) {
+ $val += 5;
+}
+var_dump($a);
+?>
+--EXPECTF--
+object(FFI\CData:int32_t[3])#%d (3) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(10)
+ [2]=>
+ int(20)
+}
+object(FFI\CData:int32_t[3])#%d (3) {
+ [0]=>
+ int(5)
+ [1]=>
+ int(15)
+ [2]=>
+ int(25)
+}
--- /dev/null
+--TEST--
+FFI 027: Incomplete and variable length arrays
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+try {
+ $p = FFI::new("int[*]");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("static int (*foo)[*];");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("typedef int foo[*];");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("static void foo(int[*][*]);");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ var_dump(FFI::sizeof(FFI::new("int[0]")));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ var_dump(FFI::sizeof(FFI::new("int[]")));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ var_dump(FFI::sizeof(FFI::cast("int[]", FFI::new("int[2]"))));
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct _x {int a; int b[];};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ $f = FFI::cdef("typedef int(*foo)[];");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ $f = FFI::cdef("typedef int foo[][2];");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ $f = FFI::cdef("typedef int foo[];");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ $f = FFI::cdef("static int foo(int[]);");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+--EXPECT--
+FFI\ParserException: '[*]' not allowed in other than function prototype scope at line 1
+FFI\ParserException: '[*]' not allowed in other than function prototype scope at line 1
+FFI\ParserException: '[*]' not allowed in other than function prototype scope at line 1
+ok
+int(0)
+FFI\ParserException: '[]' not allowed at line 1
+FFI\ParserException: '[]' not allowed at line 1
+ok
+ok
+ok
+ok
+ok
--- /dev/null
+--TEST--
+FFI 028: Incomplete arrays inside structure
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+try {
+ FFI::cdef("struct _x {int a; int b[0];};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct _x {int a; int b[];};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct _x {int a[0]; int b;};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct _x {int a[]; int b;};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("struct _x { struct {int a; int b[];}; int c;};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+try {
+ FFI::cdef("union _x {int a; int b[];};");
+ echo "ok\n";
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+--EXPECT--
+ok
+ok
+ok
+FFI\ParserException: flexible array member not at end of struct at line 1
+FFI\ParserException: flexible array member not at end of struct at line 1
+FFI\ParserException: flexible array member in union at line 1
--- /dev/null
+--TEST--
+FFI 029: _Alignas
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$ffi = FFI::cdef("
+ typedef char t1;
+ typedef char _Alignas(int) t2;
+");
+var_dump(FFI::sizeof($ffi->new("struct {char a; t1 b;}")));
+var_dump(FFI::sizeof($ffi->new("struct {char a; t2 b;}")));
+?>
+--EXPECT--
+int(2)
+int(8)
--- /dev/null
+--TEST--
+FFI 030: bool type
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+var_dump(FFI::sizeof(FFI::new("bool[2]")));
+$p = FFI::new("bool[2]");
+var_dump($p);
+$p[1] = true;
+var_dump($p[0]);
+var_dump($p[1]);
+?>
+--EXPECTF--
+int(2)
+object(FFI\CData:bool[2])#%d (2) {
+ [0]=>
+ bool(false)
+ [1]=>
+ bool(false)
+}
+bool(false)
+bool(true)
--- /dev/null
+--TEST--
+FFI 031: bit-fields packing
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+function test_size($expected_size, $type) {
+ try {
+ $size = FFI::sizeof(FFI::new($type));
+ if ($size !== $expected_size) {
+ echo "FAIL: sizeof($type) != $expected_size ($size)\n";
+ }
+ } catch (Throwable $e) {
+ echo $type . "=>" . get_class($e) . ": " . $e->getMessage()."\n";
+ }
+}
+
+test_size( 4, "struct {int a:2; int b:2;}");
+test_size( 1, "struct __attribute__((packed)) {int a:2; int b:2;}");
+test_size( 8, "struct {int a:2; unsigned long long :60; int b:2;}");
+test_size( 9, "struct __attribute__((packed)) {int a:2; unsigned long long :64; int b:2;}");
+test_size( 4, "union {int a:2; int b:8;}");
+test_size( 1, "union __attribute__((packed)) {int a:2; int b:8;}");
+?>
+ok
+--EXPECT--
+ok
--- /dev/null
+--TEST--
+FFI 032: bit-fields access
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$p = FFI::new("
+ union {
+ struct __attribute__((packed)) {
+ int a:2;
+ unsigned long long b:64;
+ int c:3;
+ unsigned int d:3;
+ } s;
+ uint8_t i[9];
+}");
+var_dump(FFI::sizeof($p));
+for ($i = -5; $i < 9; $i++) {
+ $p->s->c = $i;
+ $p->s->d = $i;
+ echo "$i => 3-bit int {$p->s->c}, 3-bit uint {$p->s->d}\n";
+}
+$p->s->a = 0;
+$p->s->c = 0;
+$p->s->d = 0;
+$p->s->b = 0x7fffffff;
+echo "0x";
+for ($i = 9; $i > 0;) {
+ printf("%02x", $p->i[--$i]);
+}
+echo "\n";
+?>
+ok
+--EXPECT--
+int(9)
+-5 => 3-bit int 3, 3-bit uint 3
+-4 => 3-bit int -4, 3-bit uint 4
+-3 => 3-bit int -3, 3-bit uint 5
+-2 => 3-bit int -2, 3-bit uint 6
+-1 => 3-bit int -1, 3-bit uint 7
+0 => 3-bit int 0, 3-bit uint 0
+1 => 3-bit int 1, 3-bit uint 1
+2 => 3-bit int 2, 3-bit uint 2
+3 => 3-bit int 3, 3-bit uint 3
+4 => 3-bit int -4, 3-bit uint 4
+5 => 3-bit int -3, 3-bit uint 5
+6 => 3-bit int -2, 3-bit uint 6
+7 => 3-bit int -1, 3-bit uint 7
+8 => 3-bit int 0, 3-bit uint 0
+0x0000000001fffffffc
+ok
--- /dev/null
+--TEST--
+FFI 033: FFI::new(), FFI::free(), FFI::type(), FFI::typeof(), FFI::arrayType()
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$p1 = FFI::new("uint8_t[2]");
+$p2 = FFI::new("uint16_t[2]", true, true);
+var_dump($p1, $p2);
+
+$t1 = FFI::typeof($p1);
+var_dump($t1);
+
+$p4 = FFI::new($t1);
+var_dump($p4);
+
+$t2 = FFI::type("uint16_t[2]");
+var_dump($t2);
+$p4 = FFI::new($t2);
+var_dump($p4);
+
+$t2 = FFI::type("uint32_t");
+var_dump($t2);
+$t3 = FFI::arrayType($t2, [2, 2]);
+var_dump($t3);
+?>
+--EXPECTF--
+object(FFI\CData:uint8_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+}
+object(FFI\CData:uint16_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+}
+object(FFI\CType:uint8_t[2])#%d (0) {
+}
+object(FFI\CData:uint8_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+}
+object(FFI\CType:uint16_t[2])#%d (0) {
+}
+object(FFI\CData:uint16_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+}
+object(FFI\CType:uint32_t)#%d (0) {
+}
+object(FFI\CType:uint32_t[2][2])#%d (0) {
+}
--- /dev/null
+--TEST--
+FFI 034: FFI::typeof(), FFI::sizeof(), FFI::alignof()
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$p1 = FFI::new("uint8_t[2]");
+$p2 = FFI::new("uint16_t[2]");
+$p3 = FFI::new("uint32_t[2]");
+var_dump(FFI::sizeof($p1), FFI::sizeof($p2), FFI::sizeof($p3));
+var_dump(FFI::alignof($p1), FFI::alignof($p2), FFI::alignof($p3));
+var_dump(FFI::sizeof(FFI::typeof($p1)), FFI::sizeof(FFI::typeof($p2)), FFI::sizeof(FFI::typeof($p3)));
+var_dump(FFI::alignof(FFI::typeof($p1)), FFI::alignof(FFI::typeof($p2)), FFI::alignof(FFI::typeof($p3)));
+?>
+--EXPECT--
+int(2)
+int(4)
+int(8)
+int(1)
+int(2)
+int(4)
+int(2)
+int(4)
+int(8)
+int(1)
+int(2)
+int(4)
--- /dev/null
+--TEST--
+FFI 035: FFI::new() not-owned
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$p = FFI::new("uint16_t[2]", false);
+var_dump($p);
+
+FFI::free($p);
+try {
+ var_dump($p);
+} catch (Throwable $e) {
+ echo get_class($e) . ": " . $e->getMessage()."\n";
+}
+?>
+--EXPECTF--
+object(FFI\CData:uint16_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+}
+object(FFI\CData:uint16_t[2])#%d (0) {
+}
+FFI\Exception: Use after free()
--- /dev/null
+--TEST--
+FFI 036: Type memory management
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$type = FFI::type("int*");
+
+function foo($ptr) {
+ global $type;
+ //$buf = FFI::new("int*[1]"); /* this loses type and crash */
+ $buf = FFI::new(FFI::arrayType($type, [1]));
+ $buf[0] = $ptr;
+ //...
+ return $buf[0];
+}
+
+$int = FFI::new("int");
+$int = 42;
+var_dump(foo(FFI::addr($int)));
+?>
+--EXPECTF--
+object(FFI\CData:int32_t*)#%d (1) {
+ [0]=>
+ int(42)
+}
--- /dev/null
+--TEST--
+FFI 037: Type memory management
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+function foo($ptr) {
+ $buf = FFI::new("int*[1]");
+ $buf[0] = $ptr;
+ //...
+ return $buf[0];
+}
+
+$int = FFI::new("int");
+$int = 42;
+var_dump(foo(FFI::addr($int)));
+?>
+--EXPECTF--
+object(FFI\CData:int32_t*)#%d (1) {
+ [0]=>
+ int(42)
+}
--- /dev/null
+--TEST--
+FFI 038: Casting array to pointer
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$a = FFI::new("int[10]");
+for ($i = 0; $i < 10; $i++) {
+ $a[$i] = $i;
+}
+$p = FFI::cast("int*", $a);
+var_dump($p[0]);
+var_dump($p[2]);
+vaR_dump($p)
+?>
+--EXPECTF--
+int(0)
+int(2)
+object(FFI\CData:int%d_t*)#%d (1) {
+ [0]=>
+ int(0)
+}
--- /dev/null
+--TEST--
+FFI 039: Pointer arithmetic
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$a = FFI::new("int[10]");
+for ($i = 0; $i < 10; $i++) {
+ $a[$i] = $i;
+}
+$p = $a + 0;
+var_dump($p[0]);
+$p += 7;
+var_dump($p[0]);
+$q = $p - 3;
+var_dump($q[0]);
+$q = 1 + $q;
+$p++;
+var_dump($p, $q);
+var_dump($p - $q);
+var_dump($q - $p);
+var_dump($q - $a);
+?>
+--EXPECTF--
+int(0)
+int(7)
+int(4)
+object(FFI\CData:int32_t*)#%d (1) {
+ [0]=>
+ int(8)
+}
+object(FFI\CData:int32_t*)#%d (1) {
+ [0]=>
+ int(5)
+}
+int(3)
+int(-3)
+int(5)
--- /dev/null
+--TEST--
+FFI 040: Support for scalar types
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$x = FFI::new("int");
+$x = 5;
+var_dump($x);
+var_dump(FFI::typeof($x));
+var_dump(FFI::cast("int8_t[4]", $x));
+$p = FFI::addr($x);
+var_dump($p);
+$p[0] += 2;
+var_dump($x);
+var_dump(FFI::sizeof($x));
+var_dump(FFI::alignof($x));
+FFI::memset($x, ord("a"), 4);
+var_dump(FFI::string($x, 4));
+
+echo "\n";
+
+$y = FFI::new("int[2]");
+$y[0] = 6;
+var_dump($y[0]);
+var_dump(FFI::typeof($y[0]));
+var_dump(FFI::cast("int8_t[4]", $y[0]));
+$p = FFI::addr($y[0]);
+var_dump($p);
+$p[0] += 2;
+var_dump($y[0]);
+var_dump(FFI::sizeof($y[0]));
+var_dump(FFI::alignof($y[0]));
+FFI::memset($y[0], ord("b"), 4);
+var_dump(FFI::string($y[0], 4));
+
+echo "\n";
+
+var_dump(FFI::memcmp($x, $y[0], 4));
+FFI::memcpy($x, $y[0], 4);
+var_dump(FFI::memcmp($x, $y[0], 4));
+?>
+--EXPECTF--
+object(FFI\CData:int32_t)#%d (1) {
+ ["cdata"]=>
+ int(5)
+}
+object(FFI\CType:int32_t)#%d (0) {
+}
+object(FFI\CData:int8_t[4])#%d (4) {
+ [0]=>
+ int(5)
+ [1]=>
+ int(0)
+ [2]=>
+ int(0)
+ [3]=>
+ int(0)
+}
+object(FFI\CData:int32_t*)#%d (1) {
+ [0]=>
+ int(5)
+}
+object(FFI\CData:int32_t)#%d (1) {
+ ["cdata"]=>
+ int(7)
+}
+int(4)
+int(4)
+string(4) "aaaa"
+
+int(6)
+object(FFI\CType:int32_t)#%d (0) {
+}
+object(FFI\CData:int8_t[4])#%d (4) {
+ [0]=>
+ int(6)
+ [1]=>
+ int(0)
+ [2]=>
+ int(0)
+ [3]=>
+ int(0)
+}
+object(FFI\CData:int32_t*)#%d (1) {
+ [0]=>
+ int(6)
+}
+int(8)
+int(4)
+int(4)
+string(4) "bbbb"
+
+int(-1)
+int(0)
--- /dev/null
+--TEST--
+FFI 041: Type memory management
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+function test_new_ownership($str, $transfer) {
+ if ($transfer) {
+ return FFI::new(FFI::type($str));
+ } else {
+ $type = FFI::type($str);
+ return FFI::new($type);
+ }
+}
+var_dump(test_new_ownership("int", 1));
+var_dump(test_new_ownership("int[2]", 1));
+var_dump(test_new_ownership("int", 0));
+var_dump(test_new_ownership("int[2]", 0));
+
+function test_type_ownership($str, $transfer) {
+ if ($transfer) {
+ return FFI::typeof(FFI::new($str));
+ } else {
+ $data = FFI::new($str);
+ return FFI::typeof($data);
+ }
+}
+var_dump(test_type_ownership("int", 1));
+var_dump(test_type_ownership("int[2]", 1));
+var_dump(test_type_ownership("int", 0));
+var_dump(test_type_ownership("int[2]", 0));
+
+function test_cast_ownership($str, $transfer) {
+ if ($transfer) {
+ return FFI::cast(FFI::type($str), FFI::new($str));
+ } else {
+ $type = FFI::type($str);
+ return FFI::cast($type, FFI::new($str));
+ }
+}
+var_dump(test_cast_ownership("int", 1));
+var_dump(test_cast_ownership("int[2]", 1));
+var_dump(test_cast_ownership("int", 0));
+var_dump(test_cast_ownership("int[2]", 0));
+
+function test_array_ownership($str, $transfer) {
+ if ($transfer) {
+ return FFI::arrayType(FFI::type($str), [2]);
+ } else {
+ $type = FFI::type($str);
+ return FFI::arrayType($type, [2]);
+ }
+}
+var_dump(test_array_ownership("int", 1));
+var_dump(test_array_ownership("int[2]", 1));
+var_dump(test_array_ownership("int", 0));
+var_dump(test_array_ownership("int[2]", 0));
+?>
+--EXPECTF--
+object(FFI\CData:int32_t)#%d (1) {
+ ["cdata"]=>
+ int(0)
+}
+object(FFI\CData:int32_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+}
+object(FFI\CData:int32_t)#%d (1) {
+ ["cdata"]=>
+ int(0)
+}
+object(FFI\CData:int32_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+}
+object(FFI\CType:int32_t)#%d (0) {
+}
+object(FFI\CType:int32_t[2])#%d (0) {
+}
+object(FFI\CType:int32_t)#%d (0) {
+}
+object(FFI\CType:int32_t[2])#%d (0) {
+}
+object(FFI\CData:int32_t)#%d (1) {
+ ["cdata"]=>
+ int(0)
+}
+object(FFI\CData:int32_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+}
+object(FFI\CData:int32_t)#%d (1) {
+ ["cdata"]=>
+ int(0)
+}
+object(FFI\CData:int32_t[2])#%d (2) {
+ [0]=>
+ int(0)
+ [1]=>
+ int(0)
+}
+object(FFI\CType:int32_t[2])#%s (0) {
+}
+object(FFI\CType:int32_t[2][2])#%d (0) {
+}
+object(FFI\CType:int32_t[2])#%d (0) {
+}
+object(FFI\CType:int32_t[2][2])#%d (0) {
+}
--- /dev/null
+--TEST--
+FFI 100: PHP symbols
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+<?php
+try {
+ FFI::cdef("extern void *zend_printf;");
+} catch (Throwable $e) {
+ die('skip PHP symbols not available');
+}
+?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$zend = FFI::cdef("
+ const char *get_zend_version(void);
+ //char *get_zend_version(void);
+ extern size_t (*zend_printf)(const char *format, ...);
+
+ unsigned long __attribute__((fastcall)) zend_hash_func(const char *str, size_t len);
+
+ void __attribute__((fastcall)) zend_str_tolower(char *str, size_t length);
+
+");
+var_dump(trim(explode("\n",$zend->get_zend_version())[0]));
+//var_dump(trim(FFI::string($zend->get_zend_version())));
+var_dump($zend->zend_printf);
+var_dump(($zend->zend_printf)("Hello %s!\n", "World"));
+
+var_dump($zend->zend_hash_func("file", strlen("file")));
+
+$str = $zend->new("char[16]");
+FFI::memcpy($str, "Hello World!", strlen("Hello World!"));
+$zend->zend_str_tolower($str, strlen("Hello World!"));
+var_dump(FFI::string($str));
+
+?>
+--EXPECTF--
+string(%d) "Zend Engine %s"
+object(FFI\CData:uint%d_t(*)())#%d (1) {
+ [0]=>
+ object(FFI\CData:uint%d_t())#%d (0) {
+ }
+}
+Hello World!
+int(13)
+int(%i)
+string(12) "hello world!"
--- /dev/null
+--TEST--
+FFI 200: PHP callbacks
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+<?php
+try {
+ FFI::cdef("void* zend_write;");
+} catch (Throwable $e) {
+ die('skip PHP symbols not available');
+}
+?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$zend = FFI::cdef("
+ typedef int (*zend_write_func_t)(const char *str, size_t str_length);
+ extern zend_write_func_t zend_write;
+");
+
+echo "Hello World!\n";
+
+$orig_zend_write = clone $zend->zend_write;
+$zend->zend_write = function($str, $len) {
+ global $orig_zend_write;
+ $orig_zend_write("{\n\t", 3);
+ $ret = $orig_zend_write($str, $len);
+ $orig_zend_write("}\n", 2);
+ return $ret;
+};
+echo "Hello World!\n";
+$zend->zend_write = $orig_zend_write;
+
+echo "Hello World!\n";
+?>
+--EXPECT--
+Hello World!
+{
+ Hello World!
+}
+Hello World!
--- /dev/null
+#define FFI_SCOPE "TEST_300_WIN32"
+#define FFI_LIB "PHP_DLL_NAME"
+
+size_t php_printf(const char *format, ...);
--- /dev/null
+--TEST--
+FFI 300: FFI preloading on Windows
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+<?php if (!extension_loaded('Zend OPcache')) die('skip Zend OPcache extension not available'); ?>
+<?php if (substr(PHP_OS, 0, 3) != 'WIN') die('skip for Windows only'); ?>
+--INI--
+ffi.enable=1
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+opcache.preload={PWD}/preload.inc
+--FILE--
+<?php
+$ffi = FFI::scope("TEST_300_WIN32");
+$ffi->php_printf("Hello World from %s!\n", "PHP");
+?>
+--CLEAN--
+<?php
+ $fn = __DIR__ . "/300-win32.h";
+ unlink($fn);
+?>
+--EXPECT--
+Hello World from PHP!
--- /dev/null
+#define FFI_SCOPE "TEST_300"\r
+\r
+int printf(const char *format, ...);\r
--- /dev/null
+--TEST--
+FFI 300: FFI preloading
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+<?php if (!extension_loaded('Zend OPcache')) die('skip Zend OPcache extension not available'); ?>
+<?php if (substr(PHP_OS, 0, 3) == 'WIN') die('skip not for Windows'); ?>
+--INI--
+ffi.enable=1
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+opcache.preload={PWD}/preload.inc
+--FILE--
+<?php
+$ffi = FFI::scope("TEST_300");
+$ffi->printf("Hello World from %s!\n", "PHP");
+?>
+--EXPECT--
+Hello World from PHP!
--- /dev/null
+--TEST--
+FFI 301: FFI loading on Windows
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+<?php if (substr(PHP_OS, 0, 3) != 'WIN') die('skip for Windows only'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$fn = __DIR__ . "/300-win32.h";
+$cont = str_replace(
+ "PHP_DLL_NAME",
+ "php" . PHP_MAJOR_VERSION . (PHP_ZTS ? "ts" : "") . (PHP_DEBUG ? "_debug" : "") . ".dll",
+ file_get_contents("$fn.in")
+ );
+file_put_contents($fn, $cont);
+
+$ffi = FFI::load($fn);
+$ffi->php_printf("Hello World from %s!\n", "PHP");
+?>
+--CLEAN--
+<?php
+ $fn = __DIR__ . "/300-win32.h";
+ unlink($fn);
+?>
+--EXPECT--
+Hello World from PHP!
--- /dev/null
+--TEST--
+FFI 301: FFI loading
+--SKIPIF--
+<?php require_once('skipif.inc'); ?>
+<?php if (substr(PHP_OS, 0, 3) == 'WIN') die('skip not for Windows'); ?>
+--INI--
+ffi.enable=1
+--FILE--
+<?php
+$ffi = FFI::load(__DIR__ . "/300.h");
+$ffi->printf("Hello World from %s!\n", "PHP");
+?>
+--EXPECT--
+Hello World from PHP!
--- /dev/null
+--TEST--\r
+FFI Double linked lists\r
+--SKIPIF--\r
+<?php require_once('skipif.inc'); ?>\r
+--INI--\r
+ffi.enable=1\r
+--FILE--\r
+<?php\r
+class DList {\r
+ private static $ffi = null;\r
+ private $root;\r
+\r
+ function __construct() {\r
+ if (is_null(self::$ffi)) {\r
+ self::$ffi =\r
+ FFI::cdef("\r
+ typedef struct _dlist dlist;\r
+ struct _dlist {\r
+ int data;\r
+ dlist *prev;\r
+ dlist *next;\r
+ };\r
+ ");\r
+ }\r
+ $node = FFI::addr(self::$ffi->new("dlist", false));\r
+ $node->data = 0;\r
+ $node->next = $node;\r
+ $node->prev = $node;\r
+ $this->root = $node;\r
+ }\r
+\r
+ function __destruct() {\r
+ $root = $this->root;\r
+ $node = $root->next;\r
+ while ($node != $root) {\r
+ $prev = $node;\r
+ $node = $node->next;\r
+ FFI::free($prev);\r
+ }\r
+ FFI::free($root);\r
+ }\r
+\r
+ function add(int $data) {\r
+ $node = FFI::addr(self::$ffi->new("dlist", false));\r
+ $node->data = $data;\r
+ $node->next = $this->root;\r
+ $node->prev = $this->root->prev;\r
+ $this->root->prev->next = $node;\r
+ $this->root->prev = $node;\r
+ }\r
+\r
+ function del(int $data) {\r
+ $root = $this->root;\r
+ $node = $root->next;\r
+ while ($node != $root) {\r
+ if ($node->data == $data) {\r
+ $node->prev->next = $node->next;\r
+ $node->next->prev = $node->prev;\r
+ FFI::free($node);\r
+ break;\r
+ }\r
+ $node = $node->next;\r
+ }\r
+ }\r
+\r
+ function print() {\r
+ echo "[";\r
+ $first = true;\r
+ $root = $this->root;\r
+ $node = $root->next;\r
+ while ($node != $root) {\r
+ if (!$first) {\r
+ echo ", ";\r
+ } else {\r
+ $first = false;\r
+ }\r
+ echo $node->data;\r
+ $node = $node->next;\r
+ }\r
+ echo "]\n";\r
+ }\r
+}\r
+\r
+$dlist = new Dlist;\r
+$dlist->add(1);\r
+$dlist->add(3);\r
+$dlist->add(5);\r
+$dlist->print();\r
+$dlist->del(3);\r
+$dlist->print();\r
+echo "OK\n";\r
+--EXPECT--\r
+[1, 3, 5]\r
+[1, 5]\r
+OK\r
--- /dev/null
+<?php
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ $fn = __DIR__ . "/300-win32.h";
+ $cont = str_replace(
+ "PHP_DLL_NAME",
+ "php" . PHP_MAJOR_VERSION . (PHP_ZTS ? "ts" : "") . (PHP_DEBUG ? "_debug" : "") . ".dll",
+ file_get_contents("$fn.in")
+ );
+ file_put_contents($fn, $cont);
+ $ffi = FFI::load($fn);
+ /* Test should cleanup this. */
+} else {
+ FFI::load(__DIR__ . "/300.h");
+}
--- /dev/null
+<?php
+if (!extension_loaded('FFI')) die('skip FFI extension not available');
+?>
; SSL stream context option.
;openssl.capath=
+[ffi]
+; FFI API restriction. Possibe values:
+; "preload" - enabled in CLI scripts and preloaded files (default)
+; "false" - always disabled
+; "true" - always enabled
+;ffi.enable=preload
+
; Local Variables:
; tab-width: 4
; End:
; SSL stream context option.
;openssl.capath=
+[ffi]
+; FFI API restriction. Possibe values:
+; "preload" - enabled in CLI scripts and preloaded files (default)
+; "false" - always disabled
+; "true" - always enabled
+;ffi.enable=preload
+
; Local Variables:
; tab-width: 4
; End: