├── .gitignore ├── CREDITS ├── README.md ├── config.m4 ├── config.w32 ├── example.php ├── ffi.c ├── ffi.g ├── ffi_parser.c ├── php_ffi.h └── tests ├── 001.phpt ├── 002.phpt ├── 003.phpt ├── 004.phpt ├── 005.phpt ├── 006.phpt ├── 007.phpt ├── 008.phpt ├── 009.phpt ├── 010.phpt ├── 011.phpt ├── 012.phpt ├── 013.phpt ├── 014.phpt ├── 015.phpt ├── 016.phpt ├── 017.phpt ├── 018.phpt ├── 019.phpt ├── 020.phpt ├── 021.phpt ├── 022.phpt ├── 023.phpt ├── 024.phpt ├── 025.phpt ├── 026.phpt ├── 027.phpt ├── 028.phpt ├── 029.phpt ├── 030.phpt ├── 031.phpt ├── 032.phpt ├── 033.phpt ├── 034.phpt ├── 035.phpt ├── 036.phpt ├── 037.phpt ├── 038.phpt ├── 039.phpt ├── 040.phpt ├── 100.phpt ├── 200.phpt ├── 300-win32.h.in ├── 300-win32.phpt ├── 300.h ├── 300.phpt ├── 301-win32.phpt ├── 301.phpt ├── list.phpt ├── preload.inc └── skipif.inc /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | *.lo 3 | *.la 4 | .libs 5 | acinclude.m4 6 | aclocal.m4 7 | autom4te.cache 8 | build 9 | config.guess 10 | config.h 11 | config.h.in 12 | config.log 13 | config.nice 14 | config.status 15 | config.sub 16 | configure 17 | configure.ac 18 | configure.in 19 | include 20 | install-sh 21 | libtool 22 | ltmain.sh 23 | Makefile 24 | Makefile.fragments 25 | Makefile.global 26 | Makefile.objects 27 | missing 28 | mkinstalldirs 29 | modules 30 | run-tests.php 31 | tests/*.diff 32 | tests/*.out 33 | tests/*.php 34 | tests/*.exp 35 | tests/*.log 36 | tests/*.sh 37 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | FFI 2 | Dmitry Stogov -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FFI PHP extension (Foreign Function Interface) 2 | 3 | > This extension has been merged into php main source tree. The latest version is available as part of PHP sourcses at [php.net](https://www.php.net/downloads.php) or at [github mirror](https://github.com/php/php-src/tree/master/ext/ffi) ! 4 | 5 | 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. 6 | 7 | ```php 8 | printf("Hello World from %s!\n", "PHP"); 31 | var_dump($libc->getenv("PATH")); 32 | var_dump($libc->time(null)); 33 | 34 | $tv = $libc->new("struct timeval"); 35 | $tz = $libc->new("struct timezone"); 36 | $libc->gettimeofday(FFI::addr($tv), FFI::addr($tz)); 37 | var_dump($tv->tv_sec, $tv->tv_usec, $tz); 38 | ?> 39 | ``` 40 | 41 | ``` 42 | Hello World from PHP! 43 | 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" 44 | int(1523617815) 45 | int(1523617815) 46 | int(977765) 47 | object(FFI\CData:)#3 (2) { 48 | ["tz_minuteswest"]=> 49 | int(-180) 50 | ["tz_dsttime"]=> 51 | int(0) 52 | } 53 | ``` 54 | 55 | 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. 56 | 57 | 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(). 58 | 59 | It's also possible to use FFI::new() as a static method to create arbitrary C data structures. 60 | 61 | ``` php 62 | x = 5; 65 | $p[1]->y = 10; 66 | var_dump($p); 67 | ``` 68 | 69 | ``` 70 | object(FFI\CData:[2])#1 (2) { 71 | [0]=> 72 | object(FFI\CData:)#2 (2) { 73 | ["x"]=> 74 | int(5) 75 | ["y"]=> 76 | int(0) 77 | } 78 | [1]=> 79 | object(FFI\CData:)#3 (2) { 80 | ["x"]=> 81 | int(0) 82 | ["y"]=> 83 | int(10) 84 | } 85 | } 86 | ``` 87 | 88 | ### API Reference 89 | 90 | ##### function FFI::cdef([string $cdef = "" [, string $lib = null]]): FFI 91 | 92 | ##### Call Native Functions 93 | 94 | All functions defined in FFI::cdef() may be called as methods of the created FFI object. 95 | 96 | ```php 97 | $libc = FFI::cdef("const char * getenv(const char *);", "libc.so.6"); 98 | var_dump($libc->getenv("PATH")); 99 | ``` 100 | 101 | ##### Read/Write Values of Native Variables 102 | 103 | All functions defined in FFI::cdef() may be accessed as properties of the created FFI object. 104 | 105 | ```php 106 | $libc = FFI::cdef("extern int errno;", "libc.so.6"); 107 | var_dump($libc->errno); 108 | ``` 109 | 110 | ##### function FFI::type(string $type): FFI\CType 111 | 112 | This function creates and returns a **FFI\CType** object, representng type of the given C type declaration string. 113 | 114 | 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(). 115 | 116 | 117 | ##### function FFI::typeof(FFI\CData $type): FFI\CType 118 | 119 | This function returns a **FFI\CType** object, representing the type of the given **FFI\CData** object. 120 | 121 | ##### static function FFI::arrayType(FFI\CType $type, array $dims): FFI\CType 122 | 123 | Constructs a new C array type with elements of $type and dimensions specified by $dims. 124 | 125 | ##### function FFI::new(mixed $type [, bool $own = true [, bool $persistent = false]]): FFI\CData 126 | 127 | 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. 128 | 129 | ```php 130 | $p = FFI::new("int[2][2]"); 131 | var_dump($p, FFI::sizeof($p)); 132 | ``` 133 | 134 | 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(). 135 | 136 | 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()**. 137 | 138 | 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(). 139 | 140 | ##### static function FFI::free(FFI\CData $cdata): void 141 | 142 | manually removes previously created "not-owned" data structure. 143 | 144 | ##### Read/Write Elements of Native Arrays 145 | 146 | 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. 147 | 148 | ```php 149 | $p = FFI::new("int[2]"); 150 | $p[0] = 1; 151 | $p[1] = 2; 152 | foreach ($p as $key => $val) { 153 | echo "$key => $val\n"; 154 | } 155 | ``` 156 | 157 | ##### Read/Write Fields of Native "struct" or "union" 158 | 159 | 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. 160 | 161 | ```php 162 | $pp = FFI::new("struct {int x,y;}[2]"); 163 | foreach($pp as $n => &$p) { 164 | $p->x = $p->y = $n; 165 | } 166 | var_dump($pp); 167 | ``` 168 | 169 | ##### Pointer arithmetic 170 | 171 | CData pointer values may be incremented/decremented by a number. The result is a pointer of the same type moved on given offset. 172 | 173 | Two pointers to the same type may be subtracted and return difference (similar to C). 174 | 175 | ##### static function FFI::sizeof(mixed $cdata_or_ctype): int 176 | 177 | returns size of C data type of the given **FFI\CData** or **FFI\CType**. 178 | 179 | ##### static function FFI::alignof(mixed $cdata_or_ctype): int 180 | 181 | returns size of C data type of the given **FFI\CData** or **FFI\CType**. 182 | 183 | ##### static function FFI::memcpy(FFI\CData $dst, mixed $src, int $size): void 184 | 185 | copies $size bytes from memory area $src to memory area $dst. $src may be any native data structure (**FFI\CData**) or PHP **string**. 186 | 187 | ##### static function FFI::memcmp(mixed $src1, mixed $src2, int $size): int 188 | 189 | compares $size bytes from memory area $src1 and $dst2. $src1 and $src2 may be any native data structures (**FFI\CData**) or PHP **string**s. 190 | 191 | ##### static function FFI::memset(FFI\CData $dst, int $c, int $size): void 192 | 193 | fills the $size bytes of the memory area pointed to by $dst with the constant byte $c 194 | 195 | ##### static function FFI::string(FFI\CData $src [, int $size]): string 196 | 197 | 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. 198 | 199 | ##### function FFI::cast(mixed $type, FFI\CData $cdata): FFI\CData 200 | 201 | Casts given $cdata to another C type, specified by C declaration **string** or **FFI\CType** object. 202 | 203 | 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(). 204 | 205 | ##### static function addr(FFI\CData $cdata): FFI\CData; 206 | 207 | 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). 208 | 209 | ##### static function load(string $filename): FFI; 210 | 211 | 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. 212 | 213 | ``` C 214 | #define FFI_LIB "libc.so.6" 215 | 216 | int printf(const char *format, ...); 217 | ``` 218 | 219 | Here, FFI_LIB specifies, that the given library should be loaded. 220 | 221 | ``` php 222 | $ffi = FFI::load(__DIR__ . "/printf.h"); 223 | $ffi->printf("Hello world!\n"); 224 | 225 | ``` 226 | 227 | ##### static function scope(string $name): FFI; 228 | 229 | 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. 230 | 231 | ``` C 232 | #define FFI_LIB "libc.so.6" 233 | #define FFI_SCOPE "libc" 234 | 235 | int printf(const char *format, ...); 236 | ``` 237 | 238 | These files are loaded through the same **FFI::load()** load function, executed from file loaded by **opcache.preload** php.ini directive. 239 | 240 | ``` ini 241 | ffi.preload=/etc/php/ffi/printf.h 242 | ``` 243 | 244 | Finally, **FFI::scope()** instantiate an **FFI** object, that implements all C definition from the given scope. 245 | 246 | ``` php 247 | $ffi = FFI::scope("libc"); 248 | $ffi->printf("Hello world!\n"); 249 | ``` 250 | 251 | ##### Owned and Not-Owned CData 252 | 253 | 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. 254 | 255 | 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. 256 | 257 | The following example demonstrates the problem. 258 | 259 | ```php 260 | $p1 = FFI::new("int[2][2]"); // $p1 is owned pointer 261 | $p2 = $p1[0]; // $p2 is not-owned part of $p1 262 | unset($p1); // $p1 is deallocated ($p2 became dangling pointer) 263 | var_dump($p2); // crash because dereferencing of dangling pointer 264 | ``` 265 | 266 | It's possible to change ownership, to avoid this crash, but this would require manual memory management and may lead to memory leaks 267 | 268 | ```php 269 | $p1 = FFI::new("int[2][2]", false); // $p1 is not-owned pointer 270 | $p2 = $p1[0]; 271 | unset($p1); // $p1 CData is keep alive (memory leak) 272 | var_dump($p2); // works fine, except of memory leak 273 | ``` 274 | 275 | ##### PHP Callbacks 276 | 277 | 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. 278 | 279 | ##### FFI API restriction 280 | 281 | 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. 282 | 283 | ### Status 284 | 285 | 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. 286 | 287 | 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) 288 | 289 | ### Requirement 290 | 291 | - php-master (7.4) 292 | - [libffi-3.*](http://sourceware.org/libffi/) 293 | 294 | ### Install 295 | 296 | ``` bash 297 | phpize 298 | ./configure --with-ffi 299 | make 300 | sudo make install 301 | ``` 302 | 303 | ### Real Usage 304 | 305 | FFI extension was used to implement [PHP TensorFlow binding](https://github.com/dstogov/php-tensorflow) 306 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl config.m4 for extension FFI 2 | 3 | PHP_ARG_WITH(ffi, for FFI support, 4 | [ --with-ffi Include FFI support]) 5 | 6 | if test "$PHP_FFI" != "no"; then 7 | if test -r $PHP_FFI/include/ffi.h; then 8 | FFI_INCDIR=$PHP_FFI/include 9 | FFI_LIBDIR=$PHP_FFI 10 | else 11 | dnl First try to find pkg-config 12 | if test -z "$PKG_CONFIG"; then 13 | AC_PATH_PROG(PKG_CONFIG, pkg-config, no) 14 | fi 15 | 16 | dnl If pkg-config is installed, try using it 17 | if test -x "$PKG_CONFIG" && "$PKG_CONFIG" --exists libffi; then 18 | FFI_VER=`"$PKG_CONFIG" --modversion libffi` 19 | FFI_INCDIR=`"$PKG_CONFIG" --variable=includedir libffi` 20 | FFI_LIBDIR=`"$PKG_CONFIG" --variable=libdir libffi` 21 | AC_MSG_CHECKING(for libffi) 22 | AC_MSG_RESULT(found version $FFI_VER) 23 | else 24 | AC_MSG_CHECKING(for libffi in default path) 25 | for i in /usr/local /usr; do 26 | if test -r $i/include/ffi.h; then 27 | FFI_DIR=$i 28 | AC_MSG_RESULT(found in $i) 29 | break 30 | fi 31 | done 32 | 33 | if test -z "$FFI_DIR"; then 34 | AC_MSG_RESULT(not found) 35 | AC_MSG_ERROR(Please reinstall the libffi distribution) 36 | fi 37 | 38 | FFI_INCDIR="$FFI_DIR/include" 39 | FFI_LIBDIR="$FFI_DIR/$PHP_LIBDIR" 40 | fi 41 | fi 42 | 43 | AC_CHECK_TYPES(long double) 44 | 45 | PHP_CHECK_LIBRARY(ffi, ffi_call, 46 | [ 47 | PHP_ADD_INCLUDE($FFI_INCDIR) 48 | PHP_ADD_LIBRARY_WITH_PATH(ffi, $FFI_LIBDIR, FFI_SHARED_LIBADD) 49 | AC_DEFINE(HAVE_FFI,1,[ Have ffi support ]) 50 | ], [ 51 | AC_MSG_ERROR(FFI module requires libffi) 52 | ], [ 53 | -L$FFI_LIBDIR 54 | ]) 55 | 56 | PHP_NEW_EXTENSION(ffi, ffi.c ffi_parser.c, $ext_shared) 57 | PHP_SUBST(FFI_SHARED_LIBADD) 58 | fi 59 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | ARG_ENABLE('ffi', 'ffi support', 'no'); 2 | 3 | if (PHP_FFI != 'no') { 4 | if (CHECK_HEADER_ADD_INCLUDE("ffi.h", "CFLAGS_FFI", PHP_FFI+ ";" + PHP_PHP_BUILD + "\\include") && 5 | CHECK_LIB("libffi.lib", "ffi", PHP_FFI)) { 6 | AC_DEFINE('HAVE_FFI', 1, 'ffi support enabled'); 7 | 8 | EXTENSION('ffi', 'ffi.c ffi_parser.c', null, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1'); 9 | } else { 10 | WARNING('ffi not enabled, headers or libraries not found'); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /example.php: -------------------------------------------------------------------------------- 1 | printf("Hello World from %s!\n", "PHP")); 25 | var_dump($ffi->getenv("PATH")); 26 | var_dump($ffi->time(null)); 27 | 28 | $tv = $ffi->new("struct timeval"); 29 | $tz = $ffi->new("struct timezone"); 30 | var_dump($ffi->gettimeofday(FFI::addr($tv), FFI::addr($tz))); 31 | var_dump($tv, $tz); 32 | -------------------------------------------------------------------------------- /ffi.g: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2018 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Dmitry Stogov | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | %start declarations 20 | %sub-start type_name 21 | %case-sensetive true 22 | %global-vars false 23 | %output "ffi_parser.c" 24 | %language "c" 25 | %indent "\t" 26 | 27 | %{ 28 | /* 29 | +----------------------------------------------------------------------+ 30 | | PHP Version 7 | 31 | +----------------------------------------------------------------------+ 32 | | Copyright (c) 1997-2018 The PHP Group | 33 | +----------------------------------------------------------------------+ 34 | | This source file is subject to version 3.01 of the PHP license, | 35 | | that is bundled with this package in the file LICENSE, and is | 36 | | available through the world-wide-web at the following url: | 37 | | http://www.php.net/license/3_01.txt | 38 | | If you did not receive a copy of the PHP license and are unable to | 39 | | obtain it through the world-wide-web, please send a note to | 40 | | license@php.net so we can mail you a copy immediately. | 41 | +----------------------------------------------------------------------+ 42 | | Author: Dmitry Stogov | 43 | +----------------------------------------------------------------------+ 44 | */ 45 | 46 | #ifdef HAVE_CONFIG_H 47 | # include "config.h" 48 | #endif 49 | 50 | #include "php.h" 51 | #include "php_ffi.h" 52 | 53 | #include 54 | #include 55 | #include 56 | 57 | #define yy_buf FFI_G(buf) 58 | #define yy_end FFI_G(end) 59 | #define yy_pos FFI_G(pos) 60 | #define yy_text FFI_G(text) 61 | #define yy_line FFI_G(line) 62 | 63 | /* forward declarations */ 64 | static void yy_error(const char *msg); 65 | static void yy_error_sym(const char *msg, int sym); 66 | 67 | %} 68 | 69 | declarations: 70 | ( 71 | {zend_ffi_dcl common_dcl = ZEND_FFI_ATTR_INIT;} 72 | declaration_specifiers(&common_dcl) 73 | ( 74 | {const char *name;} 75 | {size_t name_len;} 76 | {zend_ffi_dcl dcl;} 77 | {dcl = common_dcl;} 78 | declarator(&dcl, &name, &name_len) 79 | attributes(&dcl)? 80 | initializer? 81 | {zend_ffi_declare(name, name_len, &dcl);} 82 | ( "," 83 | {dcl = common_dcl;} 84 | declarator(&dcl, &name, &name_len) 85 | attributes(&dcl)? 86 | initializer? 87 | {zend_ffi_declare(name, name_len, &dcl);} 88 | )* 89 | )? 90 | ";" 91 | )* 92 | ; 93 | 94 | declaration_specifiers(zend_ffi_dcl *dcl): 95 | ( ?{sym != YY_ID || zend_ffi_is_typedef_name((const char*)yy_text, yy_pos - yy_text)} 96 | ( {if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);} 97 | "typedef" 98 | {dcl->flags |= ZEND_FFI_DCL_TYPEDEF;} 99 | | {if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);} 100 | "extern" 101 | {dcl->flags |= ZEND_FFI_DCL_EXTERN;} 102 | | {if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);} 103 | "static" 104 | {dcl->flags |= ZEND_FFI_DCL_STATIC;} 105 | | {if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);} 106 | "auto" 107 | {dcl->flags |= ZEND_FFI_DCL_AUTO;} 108 | | {if (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) yy_error_sym("unexpected", sym);} 109 | "register" 110 | {dcl->flags |= ZEND_FFI_DCL_REGISTER;} 111 | // | "_Thread_local" // TODO: not-implemented ??? 112 | | ("inline"|"__inline"|"__inline__") 113 | {dcl->flags |= ZEND_FFI_DCL_INLINE;} 114 | | "_Noreturn" 115 | {dcl->flags |= ZEND_FFI_DCL_NO_RETURN;} 116 | | "__cdecl" 117 | {zend_ffi_set_abi(dcl, ZEND_FFI_ABI_CDECL);} 118 | | "__stdcall" 119 | {zend_ffi_set_abi(dcl, ZEND_FFI_ABI_STDCALL);} 120 | | "__fastcall" 121 | {zend_ffi_set_abi(dcl, ZEND_FFI_ABI_FASTCALL);} 122 | | "__thiscall" 123 | {zend_ffi_set_abi(dcl, ZEND_FFI_ABI_THISCALL);} 124 | | "_Alignas" 125 | "(" 126 | ( &type_name 127 | {zend_ffi_dcl align_dcl = ZEND_FFI_ATTR_INIT;} 128 | type_name(&align_dcl) 129 | {zend_ffi_align_as_type(dcl, &align_dcl);} 130 | | {zend_ffi_val align_val;} 131 | constant_expression(&align_val) 132 | {zend_ffi_align_as_val(dcl, &align_val);} 133 | ) 134 | ")" 135 | | attributes(dcl) 136 | | type_qualifier(dcl) 137 | | type_specifier(dcl) 138 | ) 139 | )+ 140 | ; 141 | 142 | specifier_qualifier_list(zend_ffi_dcl *dcl): 143 | ( ?{sym != YY_ID || zend_ffi_is_typedef_name((const char*)yy_text, yy_pos - yy_text)} 144 | ( type_specifier(dcl) 145 | | type_qualifier(dcl) 146 | | attributes(dcl) 147 | ) 148 | )+ 149 | ; 150 | 151 | type_qualifier_list(zend_ffi_dcl *dcl): 152 | ( type_qualifier(dcl) 153 | | attributes(dcl) 154 | )+ 155 | ; 156 | 157 | type_qualifier(zend_ffi_dcl *dcl): 158 | ("const"|"__const"|"__const__") 159 | {dcl->flags |= ZEND_FFI_DCL_CONST;} 160 | {dcl->attr |= ZEND_FFI_ATTR_CONST;} 161 | | ("restrict"|"__restict"|"__restrict__") 162 | {dcl->flags |= ZEND_FFI_DCL_RESTRICT;} 163 | | ("volatile"|"__volatile"|"__volatile__") 164 | {dcl->flags |= ZEND_FFI_DCL_VOLATILE;} 165 | | "_Atomic" 166 | {dcl->flags |= ZEND_FFI_DCL_ATOMIC;} 167 | ; 168 | 169 | type_specifier(zend_ffi_dcl *dcl): 170 | {const char *name;} 171 | {size_t name_len;} 172 | ( {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);} 173 | "void" 174 | {dcl->flags |= ZEND_FFI_DCL_VOID;} 175 | | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED))) yy_error_sym("unexpected", sym);} 176 | "char" 177 | {dcl->flags |= ZEND_FFI_DCL_CHAR;} 178 | | {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);} 179 | "short" 180 | {dcl->flags |= ZEND_FFI_DCL_SHORT;} 181 | | {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);} 182 | "int" 183 | {dcl->flags |= ZEND_FFI_DCL_INT;} 184 | | { 185 | if (dcl->flags & ZEND_FFI_DCL_LONG) { 186 | 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); 187 | dcl->flags |= ZEND_FFI_DCL_LONG_LONG; 188 | } else { 189 | 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); 190 | dcl->flags |= ZEND_FFI_DCL_LONG; 191 | } 192 | } 193 | "long" 194 | | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);} 195 | "float" 196 | {dcl->flags |= ZEND_FFI_DCL_FLOAT;} 197 | | {if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_COMPLEX))) yy_error_sym("unexpected", sym);} 198 | "double" 199 | {dcl->flags |= ZEND_FFI_DCL_DOUBLE;} 200 | | {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);} 201 | "signed" 202 | {dcl->flags |= ZEND_FFI_DCL_SIGNED;} 203 | | {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);} 204 | "unsigned" 205 | {dcl->flags |= ZEND_FFI_DCL_UNSIGNED;} 206 | | {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);} 207 | "_Bool" 208 | {dcl->flags |= ZEND_FFI_DCL_BOOL;} 209 | | {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);} 210 | ("_Complex"|"complex"|"__complex"|"__complex__") 211 | {dcl->flags |= ZEND_FFI_DCL_COMPLEX;} 212 | // | "_Atomic" "(" type_name ")" // TODO: not-implemented ??? 213 | | {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);} 214 | struct_or_union_specifier(dcl) 215 | | {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);} 216 | enum_specifier(dcl) 217 | | {if (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) yy_error_sym("unexpected", sym);} 218 | {/*redeclaration of '%.*s' ??? */} 219 | ID(&name, &name_len) 220 | {dcl->flags |= ZEND_FFI_DCL_TYPEDEF_NAME;} 221 | {zend_ffi_resolve_typedef(name, name_len, dcl);} 222 | ) 223 | ; 224 | 225 | struct_or_union_specifier(zend_ffi_dcl *dcl): 226 | ( "struct" 227 | {dcl->flags |= ZEND_FFI_DCL_STRUCT;} 228 | | "union" 229 | {dcl->flags |= ZEND_FFI_DCL_UNION;} 230 | ) 231 | attributes(dcl)? 232 | ( {const char *name;} 233 | {size_t name_len;} 234 | ID(&name, &name_len) 235 | {zend_ffi_declare_tag(name, name_len, dcl, 1);} 236 | ( struct_contents(dcl) 237 | {zend_ffi_declare_tag(name, name_len, dcl, 0);} 238 | )? 239 | | {zend_ffi_make_struct_type(dcl);} 240 | struct_contents(dcl) 241 | ) 242 | ; 243 | 244 | struct_contents(zend_ffi_dcl *dcl): 245 | "{" 246 | ( struct_declaration(dcl) 247 | ( ";" 248 | struct_declaration(dcl) 249 | )* 250 | (";")? 251 | )? 252 | "}" 253 | attributes(dcl)?+ 254 | {zend_ffi_adjust_struct_size(dcl);} 255 | ; 256 | 257 | 258 | struct_declaration(zend_ffi_dcl *struct_dcl): 259 | {zend_ffi_dcl common_field_dcl = ZEND_FFI_ATTR_INIT;} 260 | specifier_qualifier_list(&common_field_dcl) 261 | ( /* empty */ 262 | {zend_ffi_add_anonymous_field(struct_dcl, &common_field_dcl);} 263 | | struct_declarator(struct_dcl, &common_field_dcl) 264 | ( "," 265 | {zend_ffi_dcl field_dcl = common_field_dcl;} 266 | attributes(&field_dcl)? 267 | struct_declarator(struct_dcl, &field_dcl) 268 | )* 269 | ) 270 | ; 271 | 272 | struct_declarator(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl): 273 | {const char *name = NULL;} 274 | {size_t name_len = 0;} 275 | {zend_ffi_val bits;} 276 | ( declarator(field_dcl, &name, &name_len) 277 | ( ":" 278 | constant_expression(&bits) 279 | attributes(field_dcl)? 280 | {zend_ffi_add_bit_field(struct_dcl, name, name_len, field_dcl, &bits);} 281 | | /*empty */ 282 | attributes(field_dcl)? 283 | {zend_ffi_add_field(struct_dcl, name, name_len, field_dcl);} 284 | ) 285 | | ":" 286 | constant_expression(&bits) 287 | {zend_ffi_add_bit_field(struct_dcl, NULL, 0, field_dcl, &bits);} 288 | ) 289 | ; 290 | 291 | enum_specifier(zend_ffi_dcl *dcl): 292 | "enum" 293 | {dcl->flags |= ZEND_FFI_DCL_ENUM;} 294 | attributes(dcl)? 295 | ( {const char *name;} 296 | {size_t name_len;} 297 | ID(&name, &name_len) 298 | ( {zend_ffi_declare_tag(name, name_len, dcl, 0);} 299 | "{" 300 | enumerator_list(dcl) 301 | "}" 302 | attributes(dcl)?+ 303 | | {zend_ffi_declare_tag(name, name_len, dcl, 1);} 304 | ) 305 | | "{" 306 | {zend_ffi_make_enum_type(dcl);} 307 | enumerator_list(dcl) 308 | "}" 309 | attributes(dcl)?+ 310 | ) 311 | ; 312 | 313 | enumerator_list(zend_ffi_dcl *enum_dcl): 314 | {int64_t min = 0, max = 0, last = -1;} 315 | enumerator(enum_dcl, &min, &max, &last) 316 | ( "," 317 | enumerator(enum_dcl, &min, &max, &last) 318 | )* 319 | ","? 320 | ; 321 | 322 | enumerator(zend_ffi_dcl *enum_dcl, int64_t *min, int64_t *max, int64_t *last): 323 | {const char *name;} 324 | {size_t name_len;} 325 | {zend_ffi_val val = {.kind = ZEND_FFI_VAL_EMPTY};} 326 | ID(&name, &name_len) 327 | ( "=" 328 | constant_expression(&val) 329 | )? 330 | {zend_ffi_add_enum_val(enum_dcl, name, name_len, &val, min, max, last);} 331 | ; 332 | 333 | declarator(zend_ffi_dcl *dcl, const char **name, size_t *name_len): 334 | /* "char" is used as a terminator of nested declaration */ 335 | {zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};} 336 | {zend_bool nested = 0;} 337 | pointer(dcl)? 338 | ( ID(name, name_len) 339 | | "(" 340 | attributes(&nested_dcl)? 341 | declarator(&nested_dcl, name, name_len) 342 | ")" 343 | {nested = 1;} 344 | ) 345 | array_or_function_declarators(dcl)? 346 | {if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);} 347 | ; 348 | 349 | abstract_declarator(zend_ffi_dcl *dcl, const char **name, size_t *name_len): 350 | /* "char" is used as a terminator of nested declaration */ 351 | {zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};} 352 | {zend_bool nested = 0;} 353 | pointer(dcl)? 354 | ( &nested_abstract_declarator 355 | nested_abstract_declarator(&nested_dcl, name, name_len) 356 | {nested = 1;} 357 | | ID(name, name_len) 358 | | /* empty */ 359 | ) 360 | array_or_function_declarators(dcl)? 361 | {if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);} 362 | ; 363 | 364 | nested_abstract_declarator(zend_ffi_dcl *dcl, const char **name, size_t *name_len): 365 | {zend_ffi_dcl nested_dcl = {ZEND_FFI_DCL_CHAR, 0, 0, 0, NULL};} 366 | {zend_bool nested = 0;} 367 | "(" 368 | attributes(&nested_dcl)? 369 | ( pointer(dcl) 370 | ( &nested_abstract_declarator 371 | nested_abstract_declarator(&nested_dcl, name, name_len) 372 | {nested = 1;} 373 | | ID(name, name_len) 374 | | /* empty */ 375 | ) 376 | array_or_function_declarators(dcl)? 377 | | ( &nested_abstract_declarator 378 | nested_abstract_declarator(&nested_dcl, name, name_len) 379 | array_or_function_declarators(dcl)? 380 | {nested = 1;} 381 | | ID(name, name_len) 382 | array_or_function_declarators(dcl)? 383 | | array_or_function_declarators(dcl) 384 | ) 385 | ) 386 | ")" 387 | {if (nested) zend_ffi_nested_declaration(dcl, &nested_dcl);} 388 | ; 389 | 390 | pointer(zend_ffi_dcl *dcl): 391 | ( "*" 392 | {zend_ffi_make_pointer_type(dcl);} 393 | type_qualifier_list(dcl)? 394 | )+ 395 | ; 396 | 397 | array_or_function_declarators(zend_ffi_dcl *dcl): 398 | {zend_ffi_dcl dummy = ZEND_FFI_ATTR_INIT;} 399 | {zend_ffi_val len = {.kind = ZEND_FFI_VAL_EMPTY};} 400 | {HashTable *args = NULL;} 401 | {uint32_t attr = 0;} 402 | ( "[" 403 | ( "static" 404 | type_qualifier_list(&dummy)? 405 | assignment_expression(&len) 406 | | type_qualifier_list(&dummy) 407 | ( "static" assignment_expression(&len) 408 | | /* empty */ 409 | {attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;} 410 | | "*" 411 | {attr |= ZEND_FFI_ATTR_VLA;} 412 | | assignment_expression(&len) 413 | ) 414 | | ( /* empty */ 415 | {attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;} 416 | | "*" 417 | {attr |= ZEND_FFI_ATTR_VLA;} 418 | | assignment_expression(&len) 419 | ) 420 | ) 421 | "]" 422 | array_or_function_declarators(dcl)? 423 | {dcl->attr |= attr;} 424 | {zend_ffi_make_array_type(dcl, &len);} 425 | | "(" 426 | ( 427 | parameter_declaration(&args) 428 | ( "," 429 | parameter_declaration(&args) 430 | )* 431 | ( 432 | "," 433 | "..." 434 | {attr |= ZEND_FFI_ATTR_VARIADIC;} 435 | )? 436 | | "..." 437 | {attr |= ZEND_FFI_ATTR_VARIADIC;} 438 | )? 439 | ")" 440 | array_or_function_declarators(dcl)? 441 | {dcl->attr |= attr;} 442 | {zend_ffi_make_func_type(dcl, args);} 443 | // | "(" (ID ("," ID)*)? ")" // TODO: ANSI function not-implemented ??? 444 | ) 445 | ; 446 | 447 | parameter_declaration(HashTable **args): 448 | {const char *name = NULL;} 449 | {size_t name_len = 0;} 450 | {zend_bool old_allow_vla = FFI_G(allow_vla);} 451 | {FFI_G(allow_vla) = 1;} 452 | {zend_ffi_dcl param_dcl = ZEND_FFI_ATTR_INIT;} 453 | specifier_qualifier_list(¶m_dcl) 454 | abstract_declarator(¶m_dcl, &name, &name_len) 455 | /*attributes(¶m_dcl)? conflict ???*/ 456 | {zend_ffi_add_arg(args, name, name_len, ¶m_dcl);} 457 | {FFI_G(allow_vla) = old_allow_vla;} 458 | ; 459 | 460 | type_name(zend_ffi_dcl *dcl): 461 | {const char *name = NULL;} 462 | {size_t name_len = 0;} 463 | specifier_qualifier_list(dcl) 464 | abstract_declarator(dcl, &name, &name_len) 465 | ; 466 | 467 | attributes(zend_ffi_dcl *dcl): 468 | {const char *name;} 469 | {size_t name_len;} 470 | {zend_ffi_val val;} 471 | ( 472 | ("__attribute"|"__attribute__") 473 | "(" 474 | "(" 475 | attrib(dcl) 476 | ( "," 477 | attrib(dcl) 478 | )* 479 | ")" 480 | ")" 481 | | "__declspec" 482 | "(" 483 | ( ID(&name, &name_len) 484 | ( 485 | "(" 486 | assignment_expression(&val) 487 | {zend_ffi_add_msvc_attribute_value(dcl, name, name_len, &val);} 488 | ")" 489 | )? 490 | )+ 491 | ")" 492 | )++ 493 | ; 494 | 495 | attrib(zend_ffi_dcl *dcl): 496 | {const char *name;} 497 | {size_t name_len;} 498 | {int n;} 499 | {zend_ffi_val val;} 500 | ( ID(&name, &name_len) 501 | ( /* empty */ 502 | {zend_ffi_add_attribute(dcl, name, name_len);} 503 | | "(" 504 | assignment_expression(&val) 505 | {zend_ffi_add_attribute_value(dcl, name, name_len, 0, &val);} 506 | {n = 0;} 507 | ( "," 508 | assignment_expression(&val) 509 | {zend_ffi_add_attribute_value(dcl, name, name_len, ++n, &val);} 510 | )* 511 | ")" 512 | ) 513 | )? 514 | ; 515 | 516 | initializer: 517 | {zend_ffi_val dummy;} 518 | "=" 519 | ( assignment_expression(&dummy) 520 | | "{" designation? initializer ( "," designation? initializer)* ","? "}" 521 | ) 522 | ; 523 | 524 | designation: 525 | {const char *name;} 526 | {size_t name_len;} 527 | {zend_ffi_val dummy;} 528 | ( "[" constant_expression(&dummy) "]" 529 | | "." ID(&name, &name_len) 530 | )+ 531 | "=" 532 | ; 533 | 534 | expr_list: 535 | {zend_ffi_val dummy;} 536 | assignment_expression(&dummy) 537 | ( "," 538 | assignment_expression(&dummy) 539 | )* 540 | ; 541 | 542 | expression(zend_ffi_val *val): 543 | assignment_expression(val) 544 | ( "," 545 | assignment_expression(val) 546 | )* 547 | ; 548 | 549 | assignment_expression(zend_ffi_val *val): 550 | // ( unary_expression 551 | // ("="|"*="|"/="|"%="|"+="|"-="|"<<="|">>="|"&="|"^="|"|=") 552 | // )* // TODO: not-implemented ??? 553 | conditional_expression(val) 554 | ; 555 | 556 | constant_expression(zend_ffi_val *val): 557 | conditional_expression(val) 558 | ; 559 | 560 | conditional_expression(zend_ffi_val *val): 561 | {zend_ffi_val op2, op3;} 562 | logical_or_expression(val) 563 | ( "?" 564 | expression(&op2) 565 | ":" 566 | conditional_expression(&op3) 567 | {zend_ffi_expr_conditional(val, &op2, &op3);} 568 | )? 569 | ; 570 | 571 | logical_or_expression(zend_ffi_val *val): 572 | {zend_ffi_val op2;} 573 | logical_and_expression(val) 574 | ( "||" 575 | logical_and_expression(&op2) 576 | {zend_ffi_expr_bool_or(val, &op2);} 577 | )* 578 | ; 579 | 580 | logical_and_expression(zend_ffi_val *val): 581 | {zend_ffi_val op2;} 582 | inclusive_or_expression(val) 583 | ( "&&" 584 | inclusive_or_expression(&op2) 585 | {zend_ffi_expr_bool_and(val, &op2);} 586 | )* 587 | ; 588 | 589 | inclusive_or_expression(zend_ffi_val *val): 590 | {zend_ffi_val op2;} 591 | exclusive_or_expression(val) 592 | ( "|" 593 | exclusive_or_expression(&op2) 594 | {zend_ffi_expr_bw_or(val, &op2);} 595 | )* 596 | ; 597 | 598 | exclusive_or_expression(zend_ffi_val *val): 599 | {zend_ffi_val op2;} 600 | and_expression(val) 601 | ( "^" 602 | and_expression(&op2) 603 | {zend_ffi_expr_bw_xor(val, &op2);} 604 | )* 605 | ; 606 | 607 | and_expression(zend_ffi_val *val): 608 | {zend_ffi_val op2;} 609 | equality_expression(val) 610 | ( "&" 611 | equality_expression(&op2) 612 | {zend_ffi_expr_bw_and(val, &op2);} 613 | )* 614 | ; 615 | 616 | equality_expression(zend_ffi_val *val): 617 | {zend_ffi_val op2;} 618 | relational_expression(val) 619 | ( "==" 620 | relational_expression(&op2) 621 | {zend_ffi_expr_is_equal(val, &op2);} 622 | | "!=" 623 | relational_expression(&op2) 624 | {zend_ffi_expr_is_not_equal(val, &op2);} 625 | )* 626 | ; 627 | 628 | relational_expression(zend_ffi_val *val): 629 | {zend_ffi_val op2;} 630 | shift_expression(val) 631 | ( "<" 632 | shift_expression(&op2) 633 | {zend_ffi_expr_is_less(val, &op2);} 634 | | ">" 635 | shift_expression(&op2) 636 | {zend_ffi_expr_is_greater(val, &op2);} 637 | | "<=" 638 | shift_expression(&op2) 639 | {zend_ffi_expr_is_less_or_equal(val, &op2);} 640 | | ">=" 641 | shift_expression(&op2) 642 | {zend_ffi_expr_is_greater_or_equal(val, &op2);} 643 | )* 644 | ; 645 | 646 | shift_expression(zend_ffi_val *val): 647 | {zend_ffi_val op2;} 648 | additive_expression(val) 649 | ( "<<" 650 | additive_expression(&op2) 651 | {zend_ffi_expr_shift_left(val, &op2);} 652 | | ">>" 653 | additive_expression(&op2) 654 | {zend_ffi_expr_shift_right(val, &op2);} 655 | )* 656 | ; 657 | 658 | additive_expression(zend_ffi_val *val): 659 | {zend_ffi_val op2;} 660 | multiplicative_expression(val) 661 | ( "+" 662 | multiplicative_expression(&op2) 663 | {zend_ffi_expr_add(val, &op2);} 664 | | "-" 665 | multiplicative_expression(&op2) 666 | {zend_ffi_expr_sub(val, &op2);} 667 | )* 668 | ; 669 | 670 | multiplicative_expression(zend_ffi_val *val): 671 | {zend_ffi_val op2;} 672 | cast_expression(val) 673 | ( "*" 674 | cast_expression(&op2) 675 | {zend_ffi_expr_mul(val, &op2);} 676 | | "/" 677 | cast_expression(&op2) 678 | {zend_ffi_expr_div(val, &op2);} 679 | | "%" 680 | cast_expression(&op2) 681 | {zend_ffi_expr_mod(val, &op2);} 682 | )* 683 | ; 684 | 685 | cast_expression(zend_ffi_val *val): 686 | {int do_cast = 0;} 687 | {zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;} 688 | ( &( "(" type_name ")" ) 689 | "(" 690 | type_name(&dcl) 691 | ")" 692 | {do_cast = 1;} 693 | )? 694 | unary_expression(val) 695 | {if (do_cast) zend_ffi_expr_cast(val, &dcl);} 696 | ; 697 | 698 | unary_expression(zend_ffi_val *val): 699 | {const char *name;} 700 | {size_t name_len;} 701 | {zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;} 702 | ( ID(&name, &name_len) 703 | {zend_ffi_resolve_const(name, name_len, val);} 704 | ( 705 | ( "[" 706 | expr_list 707 | "]" 708 | | "(" 709 | expr_list? 710 | ")" 711 | | "." 712 | ID(&name, &name_len) 713 | | "->" 714 | ID(&name, &name_len) 715 | | "++" 716 | | "--" 717 | ) 718 | {zend_ffi_val_error(val);} 719 | )* 720 | | OCTNUMBER(val) 721 | | DECNUMBER(val) 722 | | HEXNUMBER(val) 723 | | FLOATNUMBER(val) 724 | | STRING(val) 725 | | CHARACTER(val) 726 | | "(" 727 | expression(val) 728 | ")" 729 | | "++" 730 | unary_expression(val) 731 | {zend_ffi_val_error(val);} 732 | | "--" 733 | unary_expression(val) 734 | {zend_ffi_val_error(val);} 735 | | "&" 736 | cast_expression(val) 737 | {zend_ffi_val_error(val);} 738 | | "*" 739 | cast_expression(val) 740 | {zend_ffi_val_error(val);} 741 | | "+" 742 | cast_expression(val) 743 | {zend_ffi_expr_plus(val);} 744 | | "-" 745 | cast_expression(val) 746 | {zend_ffi_expr_neg(val);} 747 | | "~" 748 | cast_expression(val) 749 | {zend_ffi_expr_bw_not(val);} 750 | | "!" 751 | cast_expression(val) 752 | {zend_ffi_expr_bool_not(val);} 753 | | "sizeof" 754 | ( &( "(" type_name ")" ) 755 | "(" 756 | type_name(&dcl) 757 | ")" 758 | {zend_ffi_expr_sizeof_type(val, &dcl);} 759 | | unary_expression(val) 760 | {zend_ffi_expr_sizeof_val(val);} 761 | ) 762 | | "_Alignof" 763 | "(" 764 | type_name(&dcl) 765 | ")" 766 | {zend_ffi_expr_alignof_type(val, &dcl);} 767 | | ("__alignof"|"__alignof__") 768 | ( &( "(" type_name ")" ) 769 | "(" 770 | type_name(&dcl) 771 | ")" 772 | {zend_ffi_expr_alignof_type(val, &dcl);} 773 | | unary_expression(val) 774 | {zend_ffi_expr_alignof_val(val);} 775 | ) 776 | ) 777 | ; 778 | 779 | ID(const char **name, size_t *name_len): 780 | /[A-Za-z_][A-Za-z_0-9]*/ 781 | {*name = (const char*)yy_text; *name_len = yy_pos - yy_text;} 782 | ; 783 | 784 | OCTNUMBER(zend_ffi_val *val): 785 | /0[0-7]*([Uu](L|l|LL|l)?|[Ll][Uu]?|(LL|ll)[Uu])?/ 786 | {zend_ffi_val_number(val, 8, (const char*)yy_text, yy_pos - yy_text);} 787 | ; 788 | 789 | DECNUMBER(zend_ffi_val *val): 790 | /[1-9][0-9]*([Uu](L|l|LL|l)?|[Ll][Uu]?|(LL|ll)[Uu])?/ 791 | {zend_ffi_val_number(val, 10, (const char*)yy_text, yy_pos - yy_text);} 792 | ; 793 | 794 | HEXNUMBER(zend_ffi_val *val): 795 | /0[xX][0-9A-Fa-f][0-9A-Fa-f]*([Uu](L|l|LL|l)?|[Ll][Uu]?|(LL|ll)[Uu])?/ 796 | {zend_ffi_val_number(val, 16, (const char*)yy_text + 2, yy_pos - yy_text - 2);} 797 | ; 798 | 799 | FLOATNUMBER(zend_ffi_val *val): 800 | /([0-9]*\.[0-9]+([Ee][\+\-]?[0-9]+)?|[0-9]+\.([Ee][\+\-]?[0-9]+)?|[0-9]+[Ee][\+\-]?[0-9]+)[flFL]?/ 801 | {zend_ffi_val_float_number(val, (const char*)yy_text, yy_pos - yy_text);} 802 | ; 803 | 804 | STRING(zend_ffi_val *val): 805 | /(u8|u|U|L)?"([^"\\]|\\.)*"/ 806 | {zend_ffi_val_string(val, (const char*)yy_text, yy_pos - yy_text);} 807 | ; 808 | 809 | CHARACTER(zend_ffi_val *val): 810 | /[LuU]?'([^'\\]|\\.)*'/ 811 | {zend_ffi_val_character(val, (const char*)yy_text, yy_pos - yy_text);} 812 | ; 813 | 814 | EOL: /\r\n|\r|\n/; 815 | WS: /[ \t\f\v]+/; 816 | ONE_LINE_COMMENT: /(\/\/|#)[^\r\n]*(\r\n|\r|\n)/; 817 | COMMENT: /\/\*([^\*]|\*+[^\*\/])*\*+\//; 818 | 819 | SKIP: ( EOL | WS | ONE_LINE_COMMENT | COMMENT )*; 820 | 821 | %% 822 | int zend_ffi_parse_decl(const char *str, size_t len) { 823 | if (SETJMP(FFI_G(bailout))==0) { 824 | FFI_G(allow_vla) = 0; 825 | yy_buf = (unsigned char*)str; 826 | yy_end = yy_buf + len; 827 | parse(); 828 | return SUCCESS; 829 | } else { 830 | return FAILURE; 831 | } 832 | } 833 | 834 | int zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl) { 835 | int sym; 836 | 837 | if (SETJMP(FFI_G(bailout))==0) { 838 | FFI_G(allow_vla) = 0; 839 | yy_pos = yy_text = yy_buf = (unsigned char*)str; 840 | yy_end = yy_buf + len; 841 | yy_line = 1; 842 | sym = parse_type_name(get_sym(), dcl); 843 | if (sym != YY_EOF) { 844 | yy_error_sym(" expected, got", sym); 845 | } 846 | zend_ffi_validate_type_name(dcl); 847 | return SUCCESS; 848 | } else { 849 | return FAILURE; 850 | }; 851 | } 852 | 853 | static void yy_error(const char *msg) { 854 | zend_ffi_parser_error("%s at line %d", msg, yy_line); 855 | } 856 | 857 | static void yy_error_sym(const char *msg, int sym) { 858 | zend_ffi_parser_error("%s '%s' at line %d", msg, sym_name[sym], yy_line); 859 | } 860 | 861 | /* 862 | * Local variables: 863 | * tab-width: 4 864 | * c-basic-offset: 4 865 | * End: 866 | * vim600: sw=4 ts=4 fdm=marker 867 | * vim<600: sw=4 ts=4 868 | */ 869 | -------------------------------------------------------------------------------- /php_ffi.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2018 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Dmitry Stogov | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifndef PHP_FFI_H 20 | #define PHP_FFI_H 21 | 22 | typedef enum _zend_ffi_api_restriction { 23 | ZEND_FFI_DISABLED = 0, /* completely disabled */ 24 | ZEND_FFI_ENABLED = 1, /* enabled everywhere */ 25 | ZEND_FFI_PRELOAD = 2, /* enabled only in preloaded scripts and CLI */ 26 | } zend_ffi_api_restriction; 27 | 28 | typedef struct _zend_ffi_type zend_ffi_type; 29 | 30 | ZEND_BEGIN_MODULE_GLOBALS(ffi) 31 | zend_ffi_api_restriction restriction; 32 | zend_bool is_cli; 33 | 34 | /* predefined ffi_types */ 35 | HashTable types; 36 | 37 | /* preloading */ 38 | HashTable *scopes; /* list of preloaded scopes */ 39 | 40 | /* callbacks */ 41 | HashTable *callbacks; 42 | 43 | /* weak type references */ 44 | HashTable *weak_types; 45 | 46 | /* ffi_parser */ 47 | JMP_BUF bailout; 48 | unsigned const char *buf; 49 | unsigned const char *end; 50 | unsigned const char *pos; 51 | unsigned const char *text; 52 | int line; 53 | HashTable *symbols; 54 | HashTable *tags; 55 | zend_bool allow_vla; 56 | zend_bool persistent; 57 | uint32_t default_type_attr; 58 | ZEND_END_MODULE_GLOBALS(ffi) 59 | 60 | ZEND_EXTERN_MODULE_GLOBALS(ffi) 61 | 62 | #ifdef PHP_WIN32 63 | # define PHP_FFI_API __declspec(dllexport) 64 | #elif defined(__GNUC__) && __GNUC__ >= 4 65 | # define PHP_FFI_API __attribute__ ((visibility("default"))) 66 | #else 67 | # define PHP_FFI_API 68 | #endif 69 | 70 | #define FFI_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(ffi, v) 71 | 72 | #define ZEND_FFI_DCL_VOID (1<<0) 73 | #define ZEND_FFI_DCL_CHAR (1<<1) 74 | #define ZEND_FFI_DCL_SHORT (1<<2) 75 | #define ZEND_FFI_DCL_INT (1<<3) 76 | #define ZEND_FFI_DCL_LONG (1<<4) 77 | #define ZEND_FFI_DCL_LONG_LONG (1<<5) 78 | #define ZEND_FFI_DCL_FLOAT (1<<6) 79 | #define ZEND_FFI_DCL_DOUBLE (1<<7) 80 | #define ZEND_FFI_DCL_SIGNED (1<<8) 81 | #define ZEND_FFI_DCL_UNSIGNED (1<<9) 82 | #define ZEND_FFI_DCL_BOOL (1<<10) 83 | #define ZEND_FFI_DCL_COMPLEX (1<<11) 84 | 85 | #define ZEND_FFI_DCL_STRUCT (1<<12) 86 | #define ZEND_FFI_DCL_UNION (1<<13) 87 | #define ZEND_FFI_DCL_ENUM (1<<14) 88 | #define ZEND_FFI_DCL_TYPEDEF_NAME (1<<15) 89 | 90 | #define ZEND_FFI_DCL_TYPE_SPECIFIERS \ 91 | (ZEND_FFI_DCL_VOID|ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT \ 92 | |ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG \ 93 | |ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_SIGNED \ 94 | |ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_BOOL|ZEND_FFI_DCL_COMPLEX \ 95 | |ZEND_FFI_DCL_STRUCT|ZEND_FFI_DCL_UNION|ZEND_FFI_DCL_ENUM \ 96 | |ZEND_FFI_DCL_TYPEDEF_NAME) 97 | 98 | #define ZEND_FFI_DCL_TYPEDEF (1<<16) 99 | #define ZEND_FFI_DCL_EXTERN (1<<17) 100 | #define ZEND_FFI_DCL_STATIC (1<<18) 101 | #define ZEND_FFI_DCL_AUTO (1<<19) 102 | #define ZEND_FFI_DCL_REGISTER (1<<20) 103 | 104 | #define ZEND_FFI_DCL_STORAGE_CLASS \ 105 | (ZEND_FFI_DCL_TYPEDEF|ZEND_FFI_DCL_EXTERN|ZEND_FFI_DCL_STATIC \ 106 | |ZEND_FFI_DCL_AUTO|ZEND_FFI_DCL_REGISTER) 107 | 108 | #define ZEND_FFI_DCL_CONST (1<<21) 109 | #define ZEND_FFI_DCL_RESTRICT (1<<22) 110 | #define ZEND_FFI_DCL_VOLATILE (1<<23) 111 | #define ZEND_FFI_DCL_ATOMIC (1<<24) 112 | 113 | #define ZEND_FFI_DCL_TYPE_QUALIFIERS \ 114 | (ZEND_FFI_DCL_CONST|ZEND_FFI_DCL_RESTRICT|ZEND_FFI_DCL_VOLATILE \ 115 | |ZEND_FFI_DCL_ATOMIC) 116 | 117 | #define ZEND_FFI_DCL_INLINE (1<<25) 118 | #define ZEND_FFI_DCL_NO_RETURN (1<<26) 119 | 120 | #define ZEND_FFI_ABI_DEFAULT 0 121 | 122 | #define ZEND_FFI_ABI_CDECL 1 // FFI_DEFAULT_ABI 123 | #define ZEND_FFI_ABI_FASTCALL 2 // FFI_FASTCALL 124 | #define ZEND_FFI_ABI_THISCALL 3 // FFI_THISCALL 125 | #define ZEND_FFI_ABI_STDCALL 4 // FFI_STDCALL 126 | #define ZEND_FFI_ABI_PASCAL 5 // FFI_PASCAL 127 | #define ZEND_FFI_ABI_REGISTER 6 // FFI_REGISTER 128 | #define ZEND_FFI_ABI_MS 7 // FFI_MS_CDECL 129 | #define ZEND_FFI_ABI_SYSV 8 // FFI_SYSV 130 | 131 | #define ZEND_FFI_ATTR_CONST (1<<0) 132 | #define ZEND_FFI_ATTR_INCOMPLETE_TAG (1<<1) 133 | #define ZEND_FFI_ATTR_VARIADIC (1<<2) 134 | #define ZEND_FFI_ATTR_INCOMPLETE_ARRAY (1<<3) 135 | #define ZEND_FFI_ATTR_VLA (1<<4) 136 | #define ZEND_FFI_ATTR_UNION (1<<5) 137 | #define ZEND_FFI_ATTR_PACKED (1<<6) 138 | #define ZEND_FFI_ATTR_MS_STRUCT (1<<7) 139 | #define ZEND_FFI_ATTR_GCC_STRUCT (1<<8) 140 | 141 | #define ZEND_FFI_ATTR_PERSISTENT (1<<9) 142 | #define ZEND_FFI_ATTR_STORED (1<<10) 143 | 144 | #define ZEND_FFI_STRUCT_ATTRS \ 145 | (ZEND_FFI_ATTR_UNION|ZEND_FFI_ATTR_PACKED|ZEND_FFI_ATTR_MS_STRUCT \ 146 | |ZEND_FFI_ATTR_GCC_STRUCT) 147 | 148 | #define ZEND_FFI_ENUM_ATTRS \ 149 | (ZEND_FFI_ATTR_PACKED) 150 | 151 | #define ZEND_FFI_ARRAY_ATTRS \ 152 | (ZEND_FFI_ATTR_CONST|ZEND_FFI_ATTR_VLA|ZEND_FFI_ATTR_INCOMPLETE_ARRAY) 153 | 154 | #define ZEND_FFI_FUNC_ATTRS \ 155 | (ZEND_FFI_ATTR_VARIADIC) 156 | 157 | #define ZEND_FFI_POINTER_ATTRS \ 158 | (ZEND_FFI_ATTR_CONST) 159 | 160 | typedef struct _zend_ffi_dcl { 161 | uint32_t flags; 162 | uint32_t align; 163 | uint16_t attr; 164 | uint16_t abi; 165 | zend_ffi_type *type; 166 | } zend_ffi_dcl; 167 | 168 | #define ZEND_FFI_ATTR_INIT {0, 0, 0, 0, NULL} 169 | 170 | typedef enum _zend_ffi_val_kind { 171 | ZEND_FFI_VAL_EMPTY, 172 | ZEND_FFI_VAL_ERROR, 173 | ZEND_FFI_VAL_INT32, 174 | ZEND_FFI_VAL_INT64, 175 | ZEND_FFI_VAL_UINT32, 176 | ZEND_FFI_VAL_UINT64, 177 | ZEND_FFI_VAL_FLOAT, 178 | ZEND_FFI_VAL_DOUBLE, 179 | ZEND_FFI_VAL_LONG_DOUBLE, 180 | ZEND_FFI_VAL_CHAR, 181 | ZEND_FFI_VAL_STRING, 182 | } zend_ffi_val_kind; 183 | 184 | #ifdef HAVE_LONG_DOUBLE 185 | typedef long double zend_ffi_double; 186 | #else 187 | typedef double zend_ffi_double; 188 | #endif 189 | 190 | typedef struct _zend_ffi_val { 191 | zend_ffi_val_kind kind; 192 | union { 193 | uint64_t u64; 194 | int64_t i64; 195 | zend_ffi_double d; 196 | char ch; 197 | struct { 198 | const char *str; 199 | size_t len; 200 | }; 201 | }; 202 | } zend_ffi_val; 203 | 204 | int zend_ffi_parse_decl(const char *str, size_t len); 205 | int zend_ffi_parse_type(const char *str, size_t len, zend_ffi_dcl *dcl); 206 | 207 | /* parser callbacks */ 208 | void ZEND_NORETURN zend_ffi_parser_error(const char *msg, ...); 209 | int zend_ffi_is_typedef_name(const char *name, size_t name_len); 210 | void zend_ffi_resolve_typedef(const char *name, size_t name_len, zend_ffi_dcl *dcl); 211 | void zend_ffi_resolve_const(const char *name, size_t name_len, zend_ffi_val *val); 212 | void zend_ffi_declare_tag(const char *name, size_t name_len, zend_ffi_dcl *dcl, zend_bool incomplete); 213 | void zend_ffi_make_enum_type(zend_ffi_dcl *dcl); 214 | 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); 215 | void zend_ffi_make_struct_type(zend_ffi_dcl *dcl); 216 | void zend_ffi_add_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl); 217 | void zend_ffi_add_anonymous_field(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl); 218 | 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); 219 | void zend_ffi_adjust_struct_size(zend_ffi_dcl *dcl); 220 | void zend_ffi_make_pointer_type(zend_ffi_dcl *dcl); 221 | void zend_ffi_make_array_type(zend_ffi_dcl *dcl, zend_ffi_val *len); 222 | void zend_ffi_make_func_type(zend_ffi_dcl *dcl, HashTable *args); 223 | void zend_ffi_add_arg(HashTable **args, const char *name, size_t name_len, zend_ffi_dcl *arg_dcl); 224 | void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl); 225 | void zend_ffi_add_attribute(zend_ffi_dcl *dcl, const char *name, size_t name_len); 226 | void zend_ffi_add_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, int n, zend_ffi_val *val); 227 | void zend_ffi_add_msvc_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, zend_ffi_val *val); 228 | void zend_ffi_set_abi(zend_ffi_dcl *dcl, uint16_t abi); 229 | void zend_ffi_nested_declaration(zend_ffi_dcl *dcl, zend_ffi_dcl *nested_dcl); 230 | void zend_ffi_align_as_type(zend_ffi_dcl *dcl, zend_ffi_dcl *align_dcl); 231 | void zend_ffi_align_as_val(zend_ffi_dcl *dcl, zend_ffi_val *align_val); 232 | void zend_ffi_validate_type_name(zend_ffi_dcl *dcl); 233 | 234 | void zend_ffi_expr_conditional(zend_ffi_val *val, zend_ffi_val *op2, zend_ffi_val *op3); 235 | void zend_ffi_expr_bool_or(zend_ffi_val *val, zend_ffi_val *op2); 236 | void zend_ffi_expr_bool_and(zend_ffi_val *val, zend_ffi_val *op2); 237 | void zend_ffi_expr_bw_or(zend_ffi_val *val, zend_ffi_val *op2); 238 | void zend_ffi_expr_bw_xor(zend_ffi_val *val, zend_ffi_val *op2); 239 | void zend_ffi_expr_bw_and(zend_ffi_val *val, zend_ffi_val *op2); 240 | void zend_ffi_expr_is_equal(zend_ffi_val *val, zend_ffi_val *op2); 241 | void zend_ffi_expr_is_not_equal(zend_ffi_val *val, zend_ffi_val *op2); 242 | void zend_ffi_expr_is_less(zend_ffi_val *val, zend_ffi_val *op2); 243 | void zend_ffi_expr_is_greater(zend_ffi_val *val, zend_ffi_val *op2); 244 | void zend_ffi_expr_is_less_or_equal(zend_ffi_val *val, zend_ffi_val *op2); 245 | void zend_ffi_expr_is_greater_or_equal(zend_ffi_val *val, zend_ffi_val *op2); 246 | void zend_ffi_expr_shift_left(zend_ffi_val *val, zend_ffi_val *op2); 247 | void zend_ffi_expr_shift_right(zend_ffi_val *val, zend_ffi_val *op2); 248 | void zend_ffi_expr_add(zend_ffi_val *val, zend_ffi_val *op2); 249 | void zend_ffi_expr_sub(zend_ffi_val *val, zend_ffi_val *op2); 250 | void zend_ffi_expr_mul(zend_ffi_val *val, zend_ffi_val *op2); 251 | void zend_ffi_expr_div(zend_ffi_val *val, zend_ffi_val *op2); 252 | void zend_ffi_expr_mod(zend_ffi_val *val, zend_ffi_val *op2); 253 | void zend_ffi_expr_cast(zend_ffi_val *val, zend_ffi_dcl *dcl); 254 | void zend_ffi_expr_plus(zend_ffi_val *val); 255 | void zend_ffi_expr_neg(zend_ffi_val *val); 256 | void zend_ffi_expr_bw_not(zend_ffi_val *val); 257 | void zend_ffi_expr_bool_not(zend_ffi_val *val); 258 | void zend_ffi_expr_sizeof_val(zend_ffi_val *val); 259 | void zend_ffi_expr_sizeof_type(zend_ffi_val *val, zend_ffi_dcl *dcl); 260 | void zend_ffi_expr_alignof_val(zend_ffi_val *val); 261 | void zend_ffi_expr_alignof_type(zend_ffi_val *val, zend_ffi_dcl *dcl); 262 | 263 | static zend_always_inline void zend_ffi_val_error(zend_ffi_val *val) /* {{{ */ 264 | { 265 | val->kind = ZEND_FFI_VAL_ERROR; 266 | } 267 | /* }}} */ 268 | 269 | void zend_ffi_val_number(zend_ffi_val *val, int base, const char *str, size_t str_len); 270 | void zend_ffi_val_float_number(zend_ffi_val *val, const char *str, size_t str_len); 271 | void zend_ffi_val_string(zend_ffi_val *val, const char *str, size_t str_len); 272 | void zend_ffi_val_character(zend_ffi_val *val, const char *str, size_t str_len); 273 | 274 | #endif /* PHP_FFI_H */ 275 | 276 | /* 277 | * Local variables: 278 | * tab-width: 4 279 | * c-basic-offset: 4 280 | * indent-tabs-mode: t 281 | * End: 282 | * vim600: sw=4 ts=4 fdm=marker 283 | * vim<600: sw=4 ts=4 284 | */ 285 | -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 001: Check if FFI is loaded 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 11 | --EXPECT-- 12 | The extension "FFI" is available 13 | -------------------------------------------------------------------------------- /tests/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 002: Check C declaration parser 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 132 | --EXPECT-- 133 | Empty declaration 134 | ok 135 | Various declarations 136 | ok 137 | -------------------------------------------------------------------------------- /tests/003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 003: Forward tag/typedef declarations 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | new("struct _a")); 29 | var_dump($ffi->new("struct _b")); 30 | var_dump($ffi->new("c")); 31 | var_dump($ffi->new("d")); 32 | try { 33 | var_dump($ffi->new("struct _e")); 34 | } catch (Throwable $e) { 35 | echo get_class($e) . ": " . $e->getMessage()."\n"; 36 | } 37 | try { 38 | var_dump($ffi->new("f")); 39 | } catch (Throwable $e) { 40 | echo get_class($e) . ": " . $e->getMessage()."\n"; 41 | } 42 | echo "ok\n"; 43 | ?> 44 | --EXPECTF-- 45 | object(FFI\CData:)#%d (1) { 46 | ["x"]=> 47 | int(0) 48 | } 49 | object(FFI\CData:)#%d (1) { 50 | ["x"]=> 51 | int(0) 52 | } 53 | object(FFI\CData:)#%d (1) { 54 | ["x"]=> 55 | int(0) 56 | } 57 | object(FFI\CData:)#%d (1) { 58 | ["x"]=> 59 | int(0) 60 | } 61 | FFI\ParserException: incomplete 'struct _e' at line 1 62 | FFI\ParserException: incomplete 'struct _f' at line 1 63 | ok 64 | -------------------------------------------------------------------------------- /tests/004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 004: Enum declarations 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | new("enum _a")); 36 | var_dump($ffi->new("enum _b")); 37 | var_dump($ffi->new("c")); 38 | var_dump($ffi->new("d")); 39 | var_dump($ffi->new("int[_c2]")); 40 | var_dump($ffi->new("int[_c3]")); 41 | var_dump($ffi->new("int[_c4]")); 42 | try { 43 | var_dump($ffi->new("enum _e")); 44 | } catch (Throwable $e) { 45 | echo get_class($e) . ": " . $e->getMessage()."\n"; 46 | } 47 | try { 48 | var_dump($ffi->new("f")); 49 | } catch (Throwable $e) { 50 | echo get_class($e) . ": " . $e->getMessage()."\n"; 51 | } 52 | echo "ok\n"; 53 | ?> 54 | --EXPECTF-- 55 | object(FFI\CData:)#%d (1) { 56 | ["cdata"]=> 57 | int(0) 58 | } 59 | object(FFI\CData:)#%d (1) { 60 | ["cdata"]=> 61 | int(0) 62 | } 63 | object(FFI\CData:)#%d (1) { 64 | ["cdata"]=> 65 | int(0) 66 | } 67 | object(FFI\CData:)#%d (1) { 68 | ["cdata"]=> 69 | int(0) 70 | } 71 | object(FFI\CData:int32_t[1])#%d (1) { 72 | [0]=> 73 | int(0) 74 | } 75 | object(FFI\CData:int32_t[1])#%d (1) { 76 | [0]=> 77 | int(0) 78 | } 79 | object(FFI\CData:int32_t[2])#%d (2) { 80 | [0]=> 81 | int(0) 82 | [1]=> 83 | int(0) 84 | } 85 | FFI\ParserException: incomplete 'enum _e' at line 1 86 | FFI\ParserException: incomplete 'enum _f' at line 1 87 | ok 88 | -------------------------------------------------------------------------------- /tests/005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 005: Array assignment 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 15 | --EXPECTF-- 16 | object(FFI\CData:int32_t[2][2])#%d (2) { 17 | [0]=> 18 | object(FFI\CData:int32_t[2])#%d (2) { 19 | [0]=> 20 | int(0) 21 | [1]=> 22 | int(0) 23 | } 24 | [1]=> 25 | object(FFI\CData:int32_t[2])#%d (2) { 26 | [0]=> 27 | int(0) 28 | [1]=> 29 | int(42) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/006.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 006: Pointer assignment 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 16 | --EXPECTF-- 17 | object(FFI\CData:int32_t*[2])#%d (2) { 18 | [0]=> 19 | NULL 20 | [1]=> 21 | object(FFI\CData:int32_t*)#%d (1) { 22 | [0]=> 23 | int(42) 24 | } 25 | } 26 | object(FFI\CData:int32_t*[2])#%d (2) { 27 | [0]=> 28 | NULL 29 | [1]=> 30 | NULL 31 | } 32 | -------------------------------------------------------------------------------- /tests/007.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 007: Pointer comparison 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 19 | --EXPECT-- 20 | bool(false) 21 | bool(true) 22 | -------------------------------------------------------------------------------- /tests/008.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 008: Array iteration 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | $val) { 14 | echo "$key => $val\n"; 15 | } 16 | 17 | $a = FFI::new("struct {int x,y;}"); 18 | try { 19 | var_dump(@count($a)); 20 | } catch (Throwable $e) { 21 | echo get_class($e) . ": " . $e->getMessage()."\n"; 22 | } 23 | 24 | try { 25 | foreach ($a as $key => $val) { 26 | echo "$key => $val\n"; 27 | } 28 | } catch (Throwable $e) { 29 | echo get_class($e) . ": " . $e->getMessage()."\n"; 30 | } 31 | ?> 32 | --EXPECT-- 33 | int(3) 34 | 0 => 0 35 | 1 => 10 36 | 2 => 20 37 | FFI\Exception: Attempt to count() on non C array 38 | FFI\Exception: Attempt to iterate on non C array 39 | -------------------------------------------------------------------------------- /tests/009.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 009: memcpy(), memcmp(), memset() and sizeof() 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 20 | --EXPECTF-- 21 | int(-1) 22 | object(FFI\CData:int32_t[4])#%d (4) { 23 | [0]=> 24 | int(0) 25 | [1]=> 26 | int(10) 27 | [2]=> 28 | int(20) 29 | [3]=> 30 | int(0) 31 | } 32 | int(0) 33 | object(FFI\CData:int32_t[3])#%d (3) { 34 | [0]=> 35 | int(-1) 36 | [1]=> 37 | int(-1) 38 | [2]=> 39 | int(-1) 40 | } 41 | -------------------------------------------------------------------------------- /tests/010.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 010: string() 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 13 | --EXPECT-- 14 | string(12) "aaaaaaaaaaaa" -------------------------------------------------------------------------------- /tests/011.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 011: cast() 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 14 | --EXPECTF-- 15 | object(FFI\CData:uint16_t[2])#%d (2) { 16 | [0]=> 17 | int(65535) 18 | [1]=> 19 | int(0) 20 | } 21 | -------------------------------------------------------------------------------- /tests/012.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 012: serialization 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | getMessage()."\n"; 13 | } 14 | ?> 15 | --EXPECT-- 16 | Exception: Serialization of 'FFI\CData' is not allowed 17 | -------------------------------------------------------------------------------- /tests/013.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 013: Declaration priorities and constrains 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | getMessage()."\n"; 18 | } 19 | 20 | try { 21 | var_dump(FFI::new("void[1]")); 22 | } catch (Throwable $e) { 23 | echo get_class($e) . ": " . $e->getMessage()."\n"; 24 | } 25 | try { 26 | FFI::cdef("static int foo(int)[5];"); 27 | echo "ok\n"; 28 | } catch (Throwable $e) { 29 | echo get_class($e) . ": " . $e->getMessage()."\n"; 30 | } 31 | try { 32 | FFI::cdef("static int foo[5](int);"); 33 | echo "ok\n"; 34 | } catch (Throwable $e) { 35 | echo get_class($e) . ": " . $e->getMessage()."\n"; 36 | } 37 | try { 38 | FFI::cdef("static int foo(int)(int);"); 39 | echo "ok\n"; 40 | } catch (Throwable $e) { 41 | echo get_class($e) . ": " . $e->getMessage()."\n"; 42 | } 43 | try { 44 | FFI::cdef("typedef int foo[2][];"); 45 | echo "ok\n"; 46 | } catch (Throwable $e) { 47 | echo get_class($e) . ": " . $e->getMessage()."\n"; 48 | } 49 | try { 50 | FFI::cdef("typedef int foo[][2];"); 51 | echo "ok\n"; 52 | } catch (Throwable $e) { 53 | echo get_class($e) . ": " . $e->getMessage()."\n"; 54 | } 55 | ?> 56 | --EXPECT-- 57 | int(1) 58 | int(2) 59 | int(3) 60 | FFI\ParserException: 'void' type is not allowed at line 1 61 | FFI\ParserException: 'void' type is not allowed at line 1 62 | FFI\ParserException: function returning array is not allowed at line 1 63 | FFI\ParserException: array of functions is not allowed at line 1 64 | FFI\ParserException: function returning function is not allowed at line 1 65 | FFI\ParserException: only the leftmost array can be undimensioned at line 1 66 | ok 67 | -------------------------------------------------------------------------------- /tests/014.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 014: Size of nested types 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 13 | ok 14 | --EXPECT-- 15 | int(8) 16 | int(8) 17 | int(16) 18 | ok -------------------------------------------------------------------------------- /tests/015.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 015: Incomplete type usage 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | getMessage()."\n"; 14 | } 15 | try { 16 | FFI::cdef("struct DIR; static struct DIR *ptr;"); 17 | echo "ok\n"; 18 | } catch (Throwable $e) { 19 | echo get_class($e) . ": " . $e->getMessage()."\n"; 20 | } 21 | try { 22 | FFI::cdef("struct DIR; typedef struct DIR DIR; static DIR dir;"); 23 | echo "ok\n"; 24 | } catch (Throwable $e) { 25 | echo get_class($e) . ": " . $e->getMessage()."\n"; 26 | } 27 | try { 28 | FFI::cdef("struct DIR; typedef struct DIR DIR; static DIR *ptr;"); 29 | echo "ok\n"; 30 | } catch (Throwable $e) { 31 | echo get_class($e) . ": " . $e->getMessage()."\n"; 32 | } 33 | try { 34 | FFI::cdef("struct DIR; static struct DIR foo();"); 35 | echo "ok\n"; 36 | } catch (Throwable $e) { 37 | echo get_class($e) . ": " . $e->getMessage()."\n"; 38 | } 39 | try { 40 | FFI::cdef("struct DIR; static struct DIR* foo();"); 41 | echo "ok\n"; 42 | } catch (Throwable $e) { 43 | echo get_class($e) . ": " . $e->getMessage()."\n"; 44 | } 45 | try { 46 | FFI::cdef("struct DIR; static void foo(struct DIR);"); 47 | echo "ok\n"; 48 | } catch (Throwable $e) { 49 | echo get_class($e) . ": " . $e->getMessage()."\n"; 50 | } 51 | try { 52 | FFI::cdef("struct DIR; static void foo(struct DIR*);"); 53 | echo "ok\n"; 54 | } catch (Throwable $e) { 55 | echo get_class($e) . ": " . $e->getMessage()."\n"; 56 | } 57 | ?> 58 | ok 59 | --EXPECT-- 60 | FFI\ParserException: incomplete 'struct DIR' at line 1 61 | ok 62 | FFI\ParserException: incomplete 'struct DIR' at line 1 63 | ok 64 | FFI\ParserException: incomplete 'struct DIR' at line 1 65 | ok 66 | FFI\ParserException: incomplete 'struct DIR' at line 1 67 | ok 68 | ok 69 | -------------------------------------------------------------------------------- /tests/016.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 016: Structure constraints 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | getMessage()."\n"; 14 | } 15 | try { 16 | FFI::cdef("struct X {struct X x;};"); 17 | echo "ok\n"; 18 | } catch (Throwable $e) { 19 | echo get_class($e) . ": " . $e->getMessage()."\n"; 20 | } 21 | try { 22 | FFI::cdef("struct X {struct X *ptr;};"); 23 | echo "ok\n"; 24 | } catch (Throwable $e) { 25 | echo get_class($e) . ": " . $e->getMessage()."\n"; 26 | } 27 | ?> 28 | ok 29 | --EXPECT-- 30 | FFI\ParserException: 'function' type is not allowed at line 1 31 | FFI\ParserException: struct/union can't contain an instance of itself at line 1 32 | ok 33 | ok 34 | -------------------------------------------------------------------------------- /tests/017.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 017: Structure constraints & tags cleanup 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | getMessage()."\n"; 13 | } 14 | try { 15 | var_dump(FFI::new("struct X {struct X x;}")); 16 | } catch (Throwable $e) { 17 | echo get_class($e) . ": " . $e->getMessage()."\n"; 18 | } 19 | try { 20 | var_dump(FFI::new("struct X {struct X *ptr;}")); 21 | } catch (Throwable $e) { 22 | echo get_class($e) . ": " . $e->getMessage()."\n"; 23 | } 24 | ?> 25 | ok 26 | --EXPECTF-- 27 | FFI\ParserException: 'function' type is not allowed at line 1 28 | FFI\ParserException: struct/union can't contain an instance of itself at line 1 29 | object(FFI\CData:)#%d (1) { 30 | ["ptr"]=> 31 | NULL 32 | } 33 | ok 34 | -------------------------------------------------------------------------------- /tests/018.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 018: Indirectly recursive structure 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | getMessage()."\n"; 14 | } 15 | try { 16 | FFI::cdef("struct X {struct X *ptr[2];};"); 17 | echo "ok\n"; 18 | } catch (Throwable $e) { 19 | echo get_class($e) . ": " . $e->getMessage()."\n"; 20 | } 21 | ?> 22 | ok 23 | --EXPECT-- 24 | FFI\ParserException: incomplete 'struct X' at line 1 25 | ok 26 | ok 27 | -------------------------------------------------------------------------------- /tests/019.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 019: Parameter type adjustment 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | getMessage()."\n"; 14 | } 15 | try { 16 | FFI::cdef("static int foo(int bar(int));"); 17 | echo "ok\n"; 18 | } catch (Throwable $e) { 19 | echo get_class($e) . ": " . $e->getMessage()."\n"; 20 | } 21 | ?> 22 | ok 23 | --EXPECT-- 24 | ok 25 | ok 26 | ok 27 | -------------------------------------------------------------------------------- /tests/020.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 020: read-only 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | x = 1; 12 | $p->y = 1; 13 | echo "ok\n"; 14 | } catch (Throwable $e) { 15 | echo get_class($e) . ": " . $e->getMessage()."\n"; 16 | } 17 | try { 18 | $p = FFI::new("struct {const int x; int y;}"); 19 | $p->y = 1; 20 | $p->x = 1; 21 | echo "ok\n"; 22 | } catch (Throwable $e) { 23 | echo get_class($e) . ": " . $e->getMessage()."\n"; 24 | } 25 | try { 26 | $p = FFI::new("const struct {int x; int y;}"); 27 | $p->x = 1; 28 | echo "ok\n"; 29 | } catch (Throwable $e) { 30 | echo get_class($e) . ": " . $e->getMessage()."\n"; 31 | } 32 | try { 33 | $p = FFI::new("const int[10]"); 34 | $p[1] = 1; 35 | echo "ok\n"; 36 | } catch (Throwable $e) { 37 | echo get_class($e) . ": " . $e->getMessage()."\n"; 38 | } 39 | try { 40 | $p = FFI::new("const int * [1]"); 41 | $p[0] = null; 42 | echo "ok\n"; 43 | } catch (Throwable $e) { 44 | echo get_class($e) . ": " . $e->getMessage()."\n"; 45 | } 46 | try { 47 | $p = FFI::new("int * const [1]"); 48 | $p[0] = null; 49 | echo "ok\n"; 50 | } catch (Throwable $e) { 51 | echo get_class($e) . ": " . $e->getMessage()."\n"; 52 | } 53 | try { 54 | $f = FFI::cdef("typedef int * const t[1];"); 55 | $p = $f->new("t"); 56 | $p[0] = null; 57 | echo "ok\n"; 58 | } catch (Throwable $e) { 59 | echo get_class($e) . ": " . $e->getMessage()."\n"; 60 | } 61 | ?> 62 | ok 63 | --EXPECT-- 64 | FFI\Exception: Attempt to assign read-only field 'y' 65 | FFI\Exception: Attempt to assign read-only field 'x' 66 | FFI\Exception: Attempt to assign read-only location 67 | FFI\Exception: Attempt to assign read-only location 68 | ok 69 | FFI\Exception: Attempt to assign read-only location 70 | FFI\Exception: Attempt to assign read-only location 71 | ok 72 | -------------------------------------------------------------------------------- /tests/021.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 021: packed enums 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 55 | ok 56 | --EXPECT-- 57 | ok 58 | -------------------------------------------------------------------------------- /tests/022.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 022: structure/union alignment 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 71 | ok 72 | --EXPECT-- 73 | ok 74 | -------------------------------------------------------------------------------- /tests/023.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 023: GCC struct extensions 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 13 | ok 14 | --EXPECT-- 15 | int(0) 16 | int(4) 17 | int(8) 18 | ok 19 | -------------------------------------------------------------------------------- /tests/024.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 024: anonymous struct/union 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | a = 1; 24 | $p->b = 2; 25 | $p->c = 3; 26 | $p->d = 4; 27 | $p->f = 5; 28 | var_dump($p); 29 | ?> 30 | --EXPECTF-- 31 | int(20) 32 | object(FFI\CData:)#%d (6) { 33 | ["a"]=> 34 | int(1) 35 | ["b"]=> 36 | int(2) 37 | ["c"]=> 38 | int(3) 39 | ["d"]=> 40 | int(4) 41 | ["e"]=> 42 | int(4) 43 | ["f"]=> 44 | int(5) 45 | } 46 | -------------------------------------------------------------------------------- /tests/025.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 025: direct work with primitive types 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 25 | --EXPECTF-- 26 | object(FFI\CData:int32_t)#%d (1) { 27 | ["cdata"]=> 28 | int(5) 29 | } 30 | object(FFI\CData:int32_t)#%d (1) { 31 | ["cdata"]=> 32 | int(7) 33 | } 34 | 7 35 | 36 | object(FFI\CData:char)#%d (1) { 37 | ["cdata"]=> 38 | string(1) "a" 39 | } 40 | object(FFI\CData:char)#%d (1) { 41 | ["cdata"]=> 42 | string(1) "b" 43 | } 44 | b 45 | -------------------------------------------------------------------------------- /tests/026.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 026: Array iteration by reference 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 18 | --EXPECTF-- 19 | object(FFI\CData:int32_t[3])#%d (3) { 20 | [0]=> 21 | int(0) 22 | [1]=> 23 | int(10) 24 | [2]=> 25 | int(20) 26 | } 27 | object(FFI\CData:int32_t[3])#%d (3) { 28 | [0]=> 29 | int(5) 30 | [1]=> 31 | int(15) 32 | [2]=> 33 | int(25) 34 | } 35 | -------------------------------------------------------------------------------- /tests/027.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 027: Incomplete and variable length arrays 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | getMessage()."\n"; 14 | } 15 | try { 16 | FFI::cdef("static int (*foo)[*];"); 17 | echo "ok\n"; 18 | } catch (Throwable $e) { 19 | echo get_class($e) . ": " . $e->getMessage()."\n"; 20 | } 21 | try { 22 | FFI::cdef("typedef int foo[*];"); 23 | echo "ok\n"; 24 | } catch (Throwable $e) { 25 | echo get_class($e) . ": " . $e->getMessage()."\n"; 26 | } 27 | try { 28 | FFI::cdef("static void foo(int[*][*]);"); 29 | echo "ok\n"; 30 | } catch (Throwable $e) { 31 | echo get_class($e) . ": " . $e->getMessage()."\n"; 32 | } 33 | try { 34 | var_dump(FFI::sizeof(FFI::new("int[0]"))); 35 | } catch (Throwable $e) { 36 | echo get_class($e) . ": " . $e->getMessage()."\n"; 37 | } 38 | try { 39 | var_dump(FFI::sizeof(FFI::new("int[]"))); 40 | } catch (Throwable $e) { 41 | echo get_class($e) . ": " . $e->getMessage()."\n"; 42 | } 43 | try { 44 | var_dump(FFI::sizeof(FFI::cast("int[]", FFI::new("int[2]")))); 45 | } catch (Throwable $e) { 46 | echo get_class($e) . ": " . $e->getMessage()."\n"; 47 | } 48 | try { 49 | FFI::cdef("struct _x {int a; int b[];};"); 50 | echo "ok\n"; 51 | } catch (Throwable $e) { 52 | echo get_class($e) . ": " . $e->getMessage()."\n"; 53 | } 54 | try { 55 | $f = FFI::cdef("typedef int(*foo)[];"); 56 | echo "ok\n"; 57 | } catch (Throwable $e) { 58 | echo get_class($e) . ": " . $e->getMessage()."\n"; 59 | } 60 | try { 61 | $f = FFI::cdef("typedef int foo[][2];"); 62 | echo "ok\n"; 63 | } catch (Throwable $e) { 64 | echo get_class($e) . ": " . $e->getMessage()."\n"; 65 | } 66 | try { 67 | $f = FFI::cdef("typedef int foo[];"); 68 | echo "ok\n"; 69 | } catch (Throwable $e) { 70 | echo get_class($e) . ": " . $e->getMessage()."\n"; 71 | } 72 | try { 73 | $f = FFI::cdef("static int foo(int[]);"); 74 | echo "ok\n"; 75 | } catch (Throwable $e) { 76 | echo get_class($e) . ": " . $e->getMessage()."\n"; 77 | } 78 | ?> 79 | --EXPECT-- 80 | FFI\ParserException: '[*]' not allowed in other than function prototype scope at line 1 81 | FFI\ParserException: '[*]' not allowed in other than function prototype scope at line 1 82 | FFI\ParserException: '[*]' not allowed in other than function prototype scope at line 1 83 | ok 84 | int(0) 85 | FFI\ParserException: '[]' not allowed at line 1 86 | FFI\ParserException: '[]' not allowed at line 1 87 | ok 88 | ok 89 | ok 90 | ok 91 | ok 92 | -------------------------------------------------------------------------------- /tests/028.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 028: Incomplete arrays inside structure 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | getMessage()."\n"; 14 | } 15 | try { 16 | FFI::cdef("struct _x {int a; int b[];};"); 17 | echo "ok\n"; 18 | } catch (Throwable $e) { 19 | echo get_class($e) . ": " . $e->getMessage()."\n"; 20 | } 21 | try { 22 | FFI::cdef("struct _x {int a[0]; int b;};"); 23 | echo "ok\n"; 24 | } catch (Throwable $e) { 25 | echo get_class($e) . ": " . $e->getMessage()."\n"; 26 | } 27 | try { 28 | FFI::cdef("struct _x {int a[]; int b;};"); 29 | echo "ok\n"; 30 | } catch (Throwable $e) { 31 | echo get_class($e) . ": " . $e->getMessage()."\n"; 32 | } 33 | try { 34 | FFI::cdef("struct _x { struct {int a; int b[];}; int c;};"); 35 | echo "ok\n"; 36 | } catch (Throwable $e) { 37 | echo get_class($e) . ": " . $e->getMessage()."\n"; 38 | } 39 | try { 40 | FFI::cdef("union _x {int a; int b[];};"); 41 | echo "ok\n"; 42 | } catch (Throwable $e) { 43 | echo get_class($e) . ": " . $e->getMessage()."\n"; 44 | } 45 | ?> 46 | --EXPECT-- 47 | ok 48 | ok 49 | ok 50 | FFI\ParserException: flexible array member not at end of struct at line 1 51 | FFI\ParserException: flexible array member not at end of struct at line 1 52 | FFI\ParserException: flexible array member in union at line 1 53 | -------------------------------------------------------------------------------- /tests/029.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 029: _Alignas 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | new("struct {char a; t1 b;}"))); 14 | var_dump(FFI::sizeof($ffi->new("struct {char a; t2 b;}"))); 15 | ?> 16 | --EXPECT-- 17 | int(2) 18 | int(8) 19 | -------------------------------------------------------------------------------- /tests/030.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 030: bool type 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 16 | --EXPECTF-- 17 | int(2) 18 | object(FFI\CData:bool[2])#%d (2) { 19 | [0]=> 20 | bool(false) 21 | [1]=> 22 | bool(false) 23 | } 24 | bool(false) 25 | bool(true) 26 | -------------------------------------------------------------------------------- /tests/031.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 031: bit-fields packing 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | " . get_class($e) . ": " . $e->getMessage()."\n"; 17 | } 18 | } 19 | 20 | test_size( 4, "struct {int a:2; int b:2;}"); 21 | test_size( 1, "struct __attribute__((packed)) {int a:2; int b:2;}"); 22 | test_size( 8, "struct {int a:2; unsigned long long :60; int b:2;}"); 23 | test_size( 9, "struct __attribute__((packed)) {int a:2; unsigned long long :64; int b:2;}"); 24 | test_size( 4, "union {int a:2; int b:8;}"); 25 | test_size( 1, "union __attribute__((packed)) {int a:2; int b:8;}"); 26 | ?> 27 | ok 28 | --EXPECT-- 29 | ok 30 | -------------------------------------------------------------------------------- /tests/032.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 032: bit-fields access 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | s->c = $i; 22 | $p->s->d = $i; 23 | echo "$i => 3-bit int {$p->s->c}, 3-bit uint {$p->s->d}\n"; 24 | } 25 | $p->s->a = 0; 26 | $p->s->c = 0; 27 | $p->s->d = 0; 28 | $p->s->b = 0x7fffffff; 29 | echo "0x"; 30 | for ($i = 9; $i > 0;) { 31 | printf("%02x", $p->i[--$i]); 32 | } 33 | echo "\n"; 34 | ?> 35 | ok 36 | --EXPECT-- 37 | int(9) 38 | -5 => 3-bit int 3, 3-bit uint 3 39 | -4 => 3-bit int -4, 3-bit uint 4 40 | -3 => 3-bit int -3, 3-bit uint 5 41 | -2 => 3-bit int -2, 3-bit uint 6 42 | -1 => 3-bit int -1, 3-bit uint 7 43 | 0 => 3-bit int 0, 3-bit uint 0 44 | 1 => 3-bit int 1, 3-bit uint 1 45 | 2 => 3-bit int 2, 3-bit uint 2 46 | 3 => 3-bit int 3, 3-bit uint 3 47 | 4 => 3-bit int -4, 3-bit uint 4 48 | 5 => 3-bit int -3, 3-bit uint 5 49 | 6 => 3-bit int -2, 3-bit uint 6 50 | 7 => 3-bit int -1, 3-bit uint 7 51 | 8 => 3-bit int 0, 3-bit uint 0 52 | 0x0000000001fffffffc 53 | ok 54 | -------------------------------------------------------------------------------- /tests/033.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 033: FFI::new(), FFI::free(), FFI::type(), FFI::typeof(), FFI::arrayType() 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 29 | --EXPECTF-- 30 | object(FFI\CData:uint8_t[2])#%d (2) { 31 | [0]=> 32 | int(0) 33 | [1]=> 34 | int(0) 35 | } 36 | object(FFI\CData:uint16_t[2])#%d (2) { 37 | [0]=> 38 | int(0) 39 | [1]=> 40 | int(0) 41 | } 42 | object(FFI\CType:uint8_t[2])#%d (0) { 43 | } 44 | object(FFI\CData:uint8_t[2])#%d (2) { 45 | [0]=> 46 | int(0) 47 | [1]=> 48 | int(0) 49 | } 50 | object(FFI\CType:uint16_t[2])#%d (0) { 51 | } 52 | object(FFI\CData:uint16_t[2])#%d (2) { 53 | [0]=> 54 | int(0) 55 | [1]=> 56 | int(0) 57 | } 58 | object(FFI\CType:uint32_t)#%d (0) { 59 | } 60 | object(FFI\CType:uint32_t[2][2])#%d (0) { 61 | } 62 | -------------------------------------------------------------------------------- /tests/034.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 034: FFI::typeof(), FFI::sizeof(), FFI::alignof() 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 17 | --EXPECT-- 18 | int(2) 19 | int(4) 20 | int(8) 21 | int(1) 22 | int(2) 23 | int(4) 24 | int(2) 25 | int(4) 26 | int(8) 27 | int(1) 28 | int(2) 29 | int(4) 30 | -------------------------------------------------------------------------------- /tests/035.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 035: FFI::new() not-owned 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | getMessage()."\n"; 17 | } 18 | ?> 19 | --EXPECTF-- 20 | object(FFI\CData:uint16_t[2])#%d (2) { 21 | [0]=> 22 | int(0) 23 | [1]=> 24 | int(0) 25 | } 26 | object(FFI\CData:uint16_t[2])#%d (0) { 27 | } 28 | FFI\Exception: Use after free() 29 | -------------------------------------------------------------------------------- /tests/036.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 036: Type memory management 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 24 | --EXPECTF-- 25 | object(FFI\CData:int32_t*)#%d (1) { 26 | [0]=> 27 | int(42) 28 | } 29 | -------------------------------------------------------------------------------- /tests/037.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 037: Type memory management 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 20 | --EXPECTF-- 21 | object(FFI\CData:int32_t*)#%d (1) { 22 | [0]=> 23 | int(42) 24 | } 25 | -------------------------------------------------------------------------------- /tests/038.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 038: Casting array to pointer 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 18 | --EXPECTF-- 19 | int(0) 20 | int(2) 21 | object(FFI\CData:int%d_t*)#%d (1) { 22 | [0]=> 23 | int(0) 24 | } 25 | -------------------------------------------------------------------------------- /tests/039.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 039: Pointer arithmetic 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 26 | --EXPECTF-- 27 | int(0) 28 | int(7) 29 | int(4) 30 | object(FFI\CData:int32_t*)#%d (1) { 31 | [0]=> 32 | int(8) 33 | } 34 | object(FFI\CData:int32_t*)#%d (1) { 35 | [0]=> 36 | int(5) 37 | } 38 | int(3) 39 | int(-3) 40 | int(5) 41 | -------------------------------------------------------------------------------- /tests/040.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 040: Support for scalar types 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | 45 | --EXPECTF-- 46 | object(FFI\CData:int32_t)#%d (1) { 47 | ["cdata"]=> 48 | int(5) 49 | } 50 | object(FFI\CType:int32_t)#%d (0) { 51 | } 52 | object(FFI\CData:int8_t[4])#%d (4) { 53 | [0]=> 54 | int(5) 55 | [1]=> 56 | int(0) 57 | [2]=> 58 | int(0) 59 | [3]=> 60 | int(0) 61 | } 62 | object(FFI\CData:int32_t*)#%d (1) { 63 | [0]=> 64 | int(5) 65 | } 66 | object(FFI\CData:int32_t)#%d (1) { 67 | ["cdata"]=> 68 | int(7) 69 | } 70 | int(4) 71 | int(4) 72 | string(4) "aaaa" 73 | 74 | int(6) 75 | object(FFI\CType:int32_t)#%d (0) { 76 | } 77 | object(FFI\CData:int8_t[4])#%d (4) { 78 | [0]=> 79 | int(6) 80 | [1]=> 81 | int(0) 82 | [2]=> 83 | int(0) 84 | [3]=> 85 | int(0) 86 | } 87 | object(FFI\CData:int32_t*)#%d (1) { 88 | [0]=> 89 | int(6) 90 | } 91 | int(8) 92 | int(4) 93 | int(4) 94 | string(4) "bbbb" 95 | 96 | int(-1) 97 | int(0) 98 | -------------------------------------------------------------------------------- /tests/100.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 100: PHP symbols 3 | --SKIPIF-- 4 | 5 | 12 | --INI-- 13 | ffi.enable=1 14 | --FILE-- 15 | get_zend_version())); 27 | //var_dump(trim(FFI::string($zend->get_zend_version()))); 28 | var_dump($zend->zend_printf); 29 | var_dump(($zend->zend_printf)("Hello %s!\n", "World")); 30 | 31 | var_dump($zend->zend_hash_func("file", strlen("file"))); 32 | 33 | $str = $zend->new("char[16]"); 34 | FFI::memcpy($str, "Hello World!", strlen("Hello World!")); 35 | $zend->zend_str_tolower($str, strlen("Hello World!")); 36 | var_dump(FFI::string($str)); 37 | 38 | ?> 39 | --EXPECTF-- 40 | string(%d) "Zend Engine %s" 41 | object(FFI\CData:uint%d_t(*)())#%d (1) { 42 | [0]=> 43 | object(FFI\CData:uint%d_t())#%d (0) { 44 | } 45 | } 46 | Hello World! 47 | int(13) 48 | int(%i) 49 | string(12) "hello world!" 50 | -------------------------------------------------------------------------------- /tests/200.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 200: PHP callbacks 3 | --SKIPIF-- 4 | 5 | 12 | --INI-- 13 | ffi.enable=1 14 | --FILE-- 15 | zend_write; 24 | $zend->zend_write = function($str, $len) { 25 | global $orig_zend_write; 26 | $orig_zend_write("{\n\t", 3); 27 | $ret = $orig_zend_write($str, $len); 28 | $orig_zend_write("}\n", 2); 29 | return $ret; 30 | }; 31 | echo "Hello World!\n"; 32 | $zend->zend_write = $orig_zend_write; 33 | 34 | echo "Hello World!\n"; 35 | ?> 36 | --EXPECT-- 37 | Hello World! 38 | { 39 | Hello World! 40 | } 41 | Hello World! 42 | -------------------------------------------------------------------------------- /tests/300-win32.h.in: -------------------------------------------------------------------------------- 1 | #define FFI_SCOPE "TEST_300_WIN32" 2 | #define FFI_LIB "PHP_DLL_NAME" 3 | 4 | size_t php_printf(const char *format, ...); 5 | -------------------------------------------------------------------------------- /tests/300-win32.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 300: FFI preloading on Windows 3 | --SKIPIF-- 4 | 5 | 6 | 7 | --INI-- 8 | ffi.enable=1 9 | opcache.enable=1 10 | opcache.enable_cli=1 11 | opcache.optimization_level=-1 12 | opcache.preload={PWD}/preload.inc 13 | --FILE-- 14 | php_printf("Hello World from %s!\n", "PHP"); 17 | ?> 18 | --CLEAN-- 19 | 23 | --EXPECT-- 24 | Hello World from PHP! 25 | -------------------------------------------------------------------------------- /tests/300.h: -------------------------------------------------------------------------------- 1 | #define FFI_SCOPE "TEST_300" 2 | 3 | int printf(const char *format, ...); 4 | -------------------------------------------------------------------------------- /tests/300.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 300: FFI preloading 3 | --SKIPIF-- 4 | 5 | 6 | 7 | --INI-- 8 | ffi.enable=1 9 | opcache.enable=1 10 | opcache.enable_cli=1 11 | opcache.optimization_level=-1 12 | opcache.preload={PWD}/preload.inc 13 | --FILE-- 14 | printf("Hello World from %s!\n", "PHP"); 17 | ?> 18 | --EXPECT-- 19 | Hello World from PHP! 20 | -------------------------------------------------------------------------------- /tests/301-win32.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 301: FFI loading on Windows 3 | --SKIPIF-- 4 | 5 | 6 | --INI-- 7 | ffi.enable=1 8 | --FILE-- 9 | php_printf("Hello World from %s!\n", "PHP"); 20 | ?> 21 | --CLEAN-- 22 | 26 | --EXPECT-- 27 | Hello World from PHP! 28 | -------------------------------------------------------------------------------- /tests/301.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI 301: FFI loading 3 | --SKIPIF-- 4 | 5 | 6 | --INI-- 7 | ffi.enable=1 8 | --FILE-- 9 | printf("Hello World from %s!\n", "PHP"); 12 | ?> 13 | --EXPECT-- 14 | Hello World from PHP! 15 | -------------------------------------------------------------------------------- /tests/list.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | FFI Double linked lists 3 | --SKIPIF-- 4 | 5 | --INI-- 6 | ffi.enable=1 7 | --FILE-- 8 | new("dlist", false)); 26 | $node->data = 0; 27 | $node->next = $node; 28 | $node->prev = $node; 29 | $this->root = $node; 30 | } 31 | 32 | function __destruct() { 33 | $root = $this->root; 34 | $node = $root->next; 35 | while ($node != $root) { 36 | $prev = $node; 37 | $node = $node->next; 38 | FFI::free($prev); 39 | } 40 | FFI::free($root); 41 | } 42 | 43 | function add(int $data) { 44 | $node = FFI::addr(self::$ffi->new("dlist", false)); 45 | $node->data = $data; 46 | $node->next = $this->root; 47 | $node->prev = $this->root->prev; 48 | $this->root->prev->next = $node; 49 | $this->root->prev = $node; 50 | } 51 | 52 | function del(int $data) { 53 | $root = $this->root; 54 | $node = $root->next; 55 | while ($node != $root) { 56 | if ($node->data == $data) { 57 | $node->prev->next = $node->next; 58 | $node->next->prev = $node->prev; 59 | FFI::free($node); 60 | break; 61 | } 62 | $node = $node->next; 63 | } 64 | } 65 | 66 | function print() { 67 | echo "["; 68 | $first = true; 69 | $root = $this->root; 70 | $node = $root->next; 71 | while ($node != $root) { 72 | if (!$first) { 73 | echo ", "; 74 | } else { 75 | $first = false; 76 | } 77 | echo $node->data; 78 | $node = $node->next; 79 | } 80 | echo "]\n"; 81 | } 82 | } 83 | 84 | $dlist = new Dlist; 85 | $dlist->add(1); 86 | $dlist->add(3); 87 | $dlist->add(5); 88 | $dlist->print(); 89 | $dlist->del(3); 90 | $dlist->print(); 91 | echo "OK\n"; 92 | --EXPECT-- 93 | [1, 3, 5] 94 | [1, 5] 95 | OK 96 | -------------------------------------------------------------------------------- /tests/preload.inc: -------------------------------------------------------------------------------- 1 | 4 | --------------------------------------------------------------------------------