php-src/ext/ffi/ffi.c
Christoph M. Becker f547412cba Fix #79177: FFI doesn't handle well PHP exceptions within callback
We have to error on unhandled exceptions in FFI callbacks, to avoid
passing back undefined values.

This has been discussed and agreed upon in a previous PR[1].

[1] <https://github.com/php/php-src/pull/5120>

Closes GH-6366.
2020-10-28 13:34:56 +01:00

7484 lines
224 KiB
C

/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "php.h"
#include "php_ffi.h"
#include "ext/standard/info.h"
#include "php_scandir.h"
#include "zend_exceptions.h"
#include "zend_interfaces.h"
#include "zend_closures.h"
#include "main/SAPI.h"
#include <ffi.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_GLOB
#ifdef PHP_WIN32
#include "win32/glob.h"
#else
#include <glob.h>
#endif
#endif
#ifndef __BIGGEST_ALIGNMENT__
/* XXX need something better, perhaps with regard to SIMD, etc. */
# define __BIGGEST_ALIGNMENT__ sizeof(size_t)
#endif
ZEND_DECLARE_MODULE_GLOBALS(ffi)
typedef enum _zend_ffi_tag_kind {
ZEND_FFI_TAG_ENUM,
ZEND_FFI_TAG_STRUCT,
ZEND_FFI_TAG_UNION
} zend_ffi_tag_kind;
static const char *zend_ffi_tag_kind_name[3] = {"enum", "struct", "union"};
typedef struct _zend_ffi_tag {
zend_ffi_tag_kind kind;
zend_ffi_type *type;
} zend_ffi_tag;
typedef enum _zend_ffi_type_kind {
ZEND_FFI_TYPE_VOID,
ZEND_FFI_TYPE_FLOAT,
ZEND_FFI_TYPE_DOUBLE,
#ifdef HAVE_LONG_DOUBLE
ZEND_FFI_TYPE_LONGDOUBLE,
#endif
ZEND_FFI_TYPE_UINT8,
ZEND_FFI_TYPE_SINT8,
ZEND_FFI_TYPE_UINT16,
ZEND_FFI_TYPE_SINT16,
ZEND_FFI_TYPE_UINT32,
ZEND_FFI_TYPE_SINT32,
ZEND_FFI_TYPE_UINT64,
ZEND_FFI_TYPE_SINT64,
ZEND_FFI_TYPE_ENUM,
ZEND_FFI_TYPE_BOOL,
ZEND_FFI_TYPE_CHAR,
ZEND_FFI_TYPE_POINTER,
ZEND_FFI_TYPE_FUNC,
ZEND_FFI_TYPE_ARRAY,
ZEND_FFI_TYPE_STRUCT,
} zend_ffi_type_kind;
typedef enum _zend_ffi_flags {
ZEND_FFI_FLAG_CONST = (1 << 0),
ZEND_FFI_FLAG_OWNED = (1 << 1),
ZEND_FFI_FLAG_PERSISTENT = (1 << 2),
} zend_ffi_flags;
struct _zend_ffi_type {
zend_ffi_type_kind kind;
size_t size;
uint32_t align;
uint32_t attr;
union {
struct {
zend_string *tag_name;
zend_ffi_type_kind kind;
} enumeration;
struct {
zend_ffi_type *type;
zend_long length;
} array;
struct {
zend_ffi_type *type;
} pointer;
struct {
zend_string *tag_name;
HashTable fields;
} record;
struct {
zend_ffi_type *ret_type;
HashTable *args;
ffi_abi abi;
} func;
};
};
typedef struct _zend_ffi_field {
size_t offset;
zend_bool is_const;
zend_bool is_nested; /* part of nested anonymous struct */
uint8_t first_bit;
uint8_t bits;
zend_ffi_type *type;
} zend_ffi_field;
typedef enum _zend_ffi_symbol_kind {
ZEND_FFI_SYM_TYPE,
ZEND_FFI_SYM_CONST,
ZEND_FFI_SYM_VAR,
ZEND_FFI_SYM_FUNC
} zend_ffi_symbol_kind;
typedef struct _zend_ffi_symbol {
zend_ffi_symbol_kind kind;
zend_bool is_const;
zend_ffi_type *type;
union {
void *addr;
int64_t value;
};
} zend_ffi_symbol;
typedef struct _zend_ffi_scope {
HashTable *symbols;
HashTable *tags;
} zend_ffi_scope;
typedef struct _zend_ffi {
zend_object std;
DL_HANDLE lib;
HashTable *symbols;
HashTable *tags;
zend_bool persistent;
} zend_ffi;
#define ZEND_FFI_TYPE_OWNED (1<<0)
#define ZEND_FFI_TYPE(t) \
((zend_ffi_type*)(((uintptr_t)(t)) & ~ZEND_FFI_TYPE_OWNED))
#define ZEND_FFI_TYPE_IS_OWNED(t) \
(((uintptr_t)(t)) & ZEND_FFI_TYPE_OWNED)
#define ZEND_FFI_TYPE_MAKE_OWNED(t) \
((zend_ffi_type*)(((uintptr_t)(t)) | ZEND_FFI_TYPE_OWNED))
#define ZEND_FFI_SIZEOF_ARG \
MAX(FFI_SIZEOF_ARG, sizeof(double))
typedef struct _zend_ffi_cdata {
zend_object std;
zend_ffi_type *type;
void *ptr;
void *ptr_holder;
zend_ffi_flags flags;
} zend_ffi_cdata;
typedef struct _zend_ffi_ctype {
zend_object std;
zend_ffi_type *type;
} zend_ffi_ctype;
static zend_class_entry *zend_ffi_exception_ce;
static zend_class_entry *zend_ffi_parser_exception_ce;
static zend_class_entry *zend_ffi_ce;
static zend_class_entry *zend_ffi_cdata_ce;
static zend_class_entry *zend_ffi_ctype_ce;
static zend_object_handlers zend_ffi_handlers;
static zend_object_handlers zend_ffi_cdata_handlers;
static zend_object_handlers zend_ffi_cdata_value_handlers;
static zend_object_handlers zend_ffi_cdata_free_handlers;
static zend_object_handlers zend_ffi_ctype_handlers;
static zend_internal_function zend_ffi_new_fn;
static zend_internal_function zend_ffi_cast_fn;
static zend_internal_function zend_ffi_type_fn;
/* forward declarations */
static void _zend_ffi_type_dtor(zend_ffi_type *type);
static void zend_ffi_finalize_type(zend_ffi_dcl *dcl);
static int zend_ffi_is_same_type(zend_ffi_type *type1, zend_ffi_type *type2);
static zend_ffi_type *zend_ffi_remember_type(zend_ffi_type *type);
static char *zend_ffi_parse_directives(const char *filename, char *code_pos, char **scope_name, char **lib, zend_bool preload);
static ZEND_FUNCTION(ffi_trampoline);
static ZEND_COLD void zend_ffi_return_unsupported(zend_ffi_type *type);
static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type);
static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type);
#if FFI_CLOSURES
static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value);
#endif
static zend_always_inline void zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */
{
if (UNEXPECTED(ZEND_FFI_TYPE_IS_OWNED(type))) {
_zend_ffi_type_dtor(type);
return;
}
}
/* }}} */
static zend_always_inline void zend_ffi_object_init(zend_object *object, zend_class_entry *ce) /* {{{ */
{
GC_SET_REFCOUNT(object, 1);
GC_TYPE_INFO(object) = IS_OBJECT | ((GC_COLLECTABLE | IS_OBJ_DESTRUCTOR_CALLED) << GC_FLAGS_SHIFT);
object->ce = ce;
object->properties = NULL;
zend_objects_store_put(object);
}
/* }}} */
static zend_object *zend_ffi_cdata_new(zend_class_entry *class_type) /* {{{ */
{
zend_ffi_cdata *cdata;
cdata = emalloc(sizeof(zend_ffi_cdata));
zend_ffi_object_init(&cdata->std, class_type);
cdata->std.handlers = &zend_ffi_cdata_handlers;
cdata->type = NULL;
cdata->ptr = NULL;
cdata->flags = 0;
return &cdata->std;
}
/* }}} */
static int zend_ffi_is_compatible_type(zend_ffi_type *dst_type, zend_ffi_type *src_type) /* {{{ */
{
while (1) {
if (dst_type == src_type) {
return 1;
} else if (dst_type->kind == src_type->kind) {
if (dst_type->kind < ZEND_FFI_TYPE_POINTER) {
return 1;
} else if (dst_type->kind == ZEND_FFI_TYPE_POINTER) {
dst_type = ZEND_FFI_TYPE(dst_type->pointer.type);
src_type = ZEND_FFI_TYPE(src_type->pointer.type);
if (dst_type->kind == ZEND_FFI_TYPE_VOID ||
src_type->kind == ZEND_FFI_TYPE_VOID) {
return 1;
}
} else if (dst_type->kind == ZEND_FFI_TYPE_ARRAY &&
(dst_type->array.length == src_type->array.length ||
dst_type->array.length == 0)) {
dst_type = ZEND_FFI_TYPE(dst_type->array.type);
src_type = ZEND_FFI_TYPE(src_type->array.type);
} else {
break;
}
} else if (dst_type->kind == ZEND_FFI_TYPE_POINTER &&
src_type->kind == ZEND_FFI_TYPE_ARRAY) {
dst_type = ZEND_FFI_TYPE(dst_type->pointer.type);
src_type = ZEND_FFI_TYPE(src_type->array.type);
if (dst_type->kind == ZEND_FFI_TYPE_VOID) {
return 1;
}
} else {
break;
}
}
return 0;
}
/* }}} */
static ffi_type *zend_ffi_make_fake_struct_type(zend_ffi_type *type) /* {{{ */
{
ffi_type *t = emalloc(sizeof(ffi_type) + sizeof(ffi_type*) * (zend_hash_num_elements(&type->record.fields) + 1));
int i;
zend_ffi_field *field;
t->size = type->size;
t->alignment = type->align;
t->type = FFI_TYPE_STRUCT;
t->elements = (ffi_type**)(t + 1);
i = 0;
ZEND_HASH_FOREACH_PTR(&type->record.fields, field) {
switch (ZEND_FFI_TYPE(field->type)->kind) {
case ZEND_FFI_TYPE_FLOAT:
t->elements[i] = &ffi_type_float;
break;
case ZEND_FFI_TYPE_DOUBLE:
t->elements[i] = &ffi_type_double;
break;
#ifndef PHP_WIN32
case ZEND_FFI_TYPE_LONGDOUBLE:
t->elements[i] = &ffi_type_longdouble;
break;
#endif
case ZEND_FFI_TYPE_SINT8:
case ZEND_FFI_TYPE_UINT8:
case ZEND_FFI_TYPE_BOOL:
case ZEND_FFI_TYPE_CHAR:
t->elements[i] = &ffi_type_uint8;
break;
case ZEND_FFI_TYPE_SINT16:
case ZEND_FFI_TYPE_UINT16:
t->elements[i] = &ffi_type_uint16;
break;
case ZEND_FFI_TYPE_SINT32:
case ZEND_FFI_TYPE_UINT32:
t->elements[i] = &ffi_type_uint32;
break;
case ZEND_FFI_TYPE_SINT64:
case ZEND_FFI_TYPE_UINT64:
t->elements[i] = &ffi_type_uint64;
break;
case ZEND_FFI_TYPE_POINTER:
t->elements[i] = &ffi_type_pointer;
break;
default:
efree(t);
return NULL;
}
i++;
} ZEND_HASH_FOREACH_END();
t->elements[i] = NULL;
return t;
}
/* }}} */
static ffi_type *zend_ffi_get_type(zend_ffi_type *type) /* {{{ */
{
zend_ffi_type_kind kind = type->kind;
again:
switch (kind) {
case ZEND_FFI_TYPE_FLOAT:
return &ffi_type_float;
case ZEND_FFI_TYPE_DOUBLE:
return &ffi_type_double;
#ifndef PHP_WIN32
case ZEND_FFI_TYPE_LONGDOUBLE:
return &ffi_type_longdouble;
#endif
case ZEND_FFI_TYPE_UINT8:
return &ffi_type_uint8;
case ZEND_FFI_TYPE_SINT8:
return &ffi_type_sint8;
case ZEND_FFI_TYPE_UINT16:
return &ffi_type_uint16;
case ZEND_FFI_TYPE_SINT16:
return &ffi_type_sint16;
case ZEND_FFI_TYPE_UINT32:
return &ffi_type_uint32;
case ZEND_FFI_TYPE_SINT32:
return &ffi_type_sint32;
case ZEND_FFI_TYPE_UINT64:
return &ffi_type_uint64;
case ZEND_FFI_TYPE_SINT64:
return &ffi_type_sint64;
case ZEND_FFI_TYPE_POINTER:
return &ffi_type_pointer;
case ZEND_FFI_TYPE_VOID:
return &ffi_type_void;
case ZEND_FFI_TYPE_BOOL:
return &ffi_type_uint8;
case ZEND_FFI_TYPE_CHAR:
return &ffi_type_sint8;
case ZEND_FFI_TYPE_ENUM:
kind = type->enumeration.kind;
goto again;
case ZEND_FFI_TYPE_STRUCT:
if (!(type->attr & ZEND_FFI_ATTR_UNION)) {
ffi_type *t = zend_ffi_make_fake_struct_type(type);
return t;
}
break;
default:
break;
}
return NULL;
}
/* }}} */
static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */
{
zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
cdata->std.handlers =
(type->kind < ZEND_FFI_TYPE_POINTER) ?
&zend_ffi_cdata_value_handlers :
&zend_ffi_cdata_handlers;
cdata->type = type;
cdata->flags = flags;
cdata->ptr = ptr;
return cdata;
}
/* }}} */
static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow_ptr(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */
{
zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
cdata->std.handlers = &zend_ffi_cdata_handlers;
cdata->type = type;
cdata->flags = flags;
cdata->ptr = (void*)&cdata->ptr_holder;
*(void**)cdata->ptr = *(void**)ptr;
return cdata;
}
/* }}} */
static zend_never_inline zend_ffi_cdata *zend_ffi_cdata_to_zval_slow_ret(void *ptr, zend_ffi_type *type, zend_ffi_flags flags) /* {{{ */
{
zend_ffi_cdata *cdata = emalloc(sizeof(zend_ffi_cdata));
zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
cdata->std.handlers =
(type->kind < ZEND_FFI_TYPE_POINTER) ?
&zend_ffi_cdata_value_handlers :
&zend_ffi_cdata_handlers;
cdata->type = type;
cdata->flags = flags;
if (type->kind == ZEND_FFI_TYPE_POINTER) {
cdata->ptr = (void*)&cdata->ptr_holder;
*(void**)cdata->ptr = *(void**)ptr;
} else if (type->kind == ZEND_FFI_TYPE_STRUCT) {
cdata->ptr = emalloc(type->size);
cdata->flags |= ZEND_FFI_FLAG_OWNED;
memcpy(cdata->ptr, ptr, type->size);
} else {
cdata->ptr = ptr;
}
return cdata;
}
/* }}} */
static zend_always_inline void zend_ffi_cdata_to_zval(zend_ffi_cdata *cdata, void *ptr, zend_ffi_type *type, int read_type, zval *rv, zend_ffi_flags flags, zend_bool is_ret, zend_bool debug_union) /* {{{ */
{
if (read_type == BP_VAR_R) {
zend_ffi_type_kind kind = type->kind;
again:
switch (kind) {
case ZEND_FFI_TYPE_FLOAT:
ZVAL_DOUBLE(rv, *(float*)ptr);
return;
case ZEND_FFI_TYPE_DOUBLE:
ZVAL_DOUBLE(rv, *(double*)ptr);
return;
#ifdef HAVE_LONG_DOUBLE
case ZEND_FFI_TYPE_LONGDOUBLE:
ZVAL_DOUBLE(rv, *(long double*)ptr);
return;
#endif
case ZEND_FFI_TYPE_UINT8:
ZVAL_LONG(rv, *(uint8_t*)ptr);
return;
case ZEND_FFI_TYPE_SINT8:
ZVAL_LONG(rv, *(int8_t*)ptr);
return;
case ZEND_FFI_TYPE_UINT16:
ZVAL_LONG(rv, *(uint16_t*)ptr);
return;
case ZEND_FFI_TYPE_SINT16:
ZVAL_LONG(rv, *(int16_t*)ptr);
return;
case ZEND_FFI_TYPE_UINT32:
ZVAL_LONG(rv, *(uint32_t*)ptr);
return;
case ZEND_FFI_TYPE_SINT32:
ZVAL_LONG(rv, *(int32_t*)ptr);
return;
case ZEND_FFI_TYPE_UINT64:
ZVAL_LONG(rv, *(uint64_t*)ptr);
return;
case ZEND_FFI_TYPE_SINT64:
ZVAL_LONG(rv, *(int64_t*)ptr);
return;
case ZEND_FFI_TYPE_BOOL:
ZVAL_BOOL(rv, *(uint8_t*)ptr);
return;
case ZEND_FFI_TYPE_CHAR:
ZVAL_INTERNED_STR(rv, ZSTR_CHAR(*(unsigned char*)ptr));
return;
case ZEND_FFI_TYPE_ENUM:
kind = type->enumeration.kind;
goto again;
case ZEND_FFI_TYPE_POINTER:
if (*(void**)ptr == NULL) {
ZVAL_NULL(rv);
return;
} else if (debug_union) {
ZVAL_STR(rv, zend_strpprintf(0, "%p", *(void**)ptr));
return;
} else if ((type->attr & ZEND_FFI_ATTR_CONST) && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) {
ZVAL_STRING(rv, *(char**)ptr);
return;
}
if (!cdata) {
if (is_ret) {
cdata = zend_ffi_cdata_to_zval_slow_ret(ptr, type, flags);
} else {
cdata = zend_ffi_cdata_to_zval_slow_ptr(ptr, type, flags);
}
} else {
GC_ADDREF(&cdata->std);
}
ZVAL_OBJ(rv, &cdata->std);
return;
default:
break;
}
}
if (!cdata) {
if (is_ret) {
cdata = zend_ffi_cdata_to_zval_slow_ret(ptr, type, flags);
} else {
cdata = zend_ffi_cdata_to_zval_slow(ptr, type, flags);
}
} else {
GC_ADDREF(&cdata->std);
}
ZVAL_OBJ(rv, &cdata->std);
}
/* }}} */
static uint64_t zend_ffi_bit_field_read(void *ptr, zend_ffi_field *field) /* {{{ */
{
size_t bit = field->first_bit;
size_t last_bit = bit + field->bits - 1;
uint8_t *p = (uint8_t *) ptr + bit / 8;
uint8_t *last_p = (uint8_t *) ptr + last_bit / 8;
size_t pos = bit % 8;
size_t insert_pos = 0;
uint8_t mask;
uint64_t val = 0;
/* Bitfield fits into a single byte */
if (p == last_p) {
mask = (1U << field->bits) - 1U;
return (*p >> pos) & mask;
}
/* Read partial prefix byte */
if (pos != 0) {
size_t num_bits = 8 - pos;
mask = ((1U << num_bits) - 1U) << pos;
val = (*p++ >> pos) & mask;
insert_pos += num_bits;
}
/* Read full bytes */
while (p < last_p) {
val |= *p++ << insert_pos;
insert_pos += 8;
}
/* Read partial suffix byte */
if (p == last_p) {
size_t num_bits = last_bit % 8 + 1;
mask = (1U << num_bits) - 1U;
val |= (*p & mask) << insert_pos;
}
return val;
}
/* }}} */
static void zend_ffi_bit_field_to_zval(void *ptr, zend_ffi_field *field, zval *rv) /* {{{ */
{
uint64_t val = zend_ffi_bit_field_read(ptr, field);
if (ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_CHAR
|| ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT8
|| ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT16
|| ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT32
|| ZEND_FFI_TYPE(field->type)->kind == ZEND_FFI_TYPE_SINT64) {
/* Sign extend */
uint64_t shift = 64 - (field->bits % 64);
if (shift != 0) {
val = (int64_t)(val << shift) >> shift;
}
}
ZVAL_LONG(rv, val);
}
/* }}} */
static int zend_ffi_zval_to_bit_field(void *ptr, zend_ffi_field *field, zval *value) /* {{{ */
{
uint64_t val = zval_get_long(value);
size_t bit = field->first_bit;
size_t last_bit = bit + field->bits - 1;
uint8_t *p = (uint8_t *) ptr + bit / 8;
uint8_t *last_p = (uint8_t *) ptr + last_bit / 8;
size_t pos = bit % 8;
uint8_t mask;
/* Bitfield fits into a single byte */
if (p == last_p) {
mask = ((1U << field->bits) - 1U) << pos;
*p = (*p & ~mask) | ((val << pos) & mask);
return SUCCESS;
}
/* Write partial prefix byte */
if (pos != 0) {
size_t num_bits = 8 - pos;
mask = ((1U << num_bits) - 1U) << pos;
*p = (*p & ~mask) | ((val << pos) & mask);
p++;
val >>= num_bits;
}
/* Write full bytes */
while (p < last_p) {
*p++ = val;
val >>= 8;
}
/* Write partial suffix byte */
if (p == last_p) {
size_t num_bits = last_bit % 8 + 1;
mask = (1U << num_bits) - 1U;
*p = (*p & ~mask) | (val & mask);
}
return SUCCESS;
}
/* }}} */
static zend_always_inline int zend_ffi_zval_to_cdata(void *ptr, zend_ffi_type *type, zval *value) /* {{{ */
{
zend_long lval;
double dval;
zend_string *tmp_str;
zend_string *str;
zend_ffi_type_kind kind = type->kind;
again:
switch (kind) {
case ZEND_FFI_TYPE_FLOAT:
dval = zval_get_double(value);
*(float*)ptr = dval;
break;
case ZEND_FFI_TYPE_DOUBLE:
dval = zval_get_double(value);
*(double*)ptr = dval;
break;
#ifdef HAVE_LONG_DOUBLE
case ZEND_FFI_TYPE_LONGDOUBLE:
dval = zval_get_double(value);
*(long double*)ptr = dval;
break;
#endif
case ZEND_FFI_TYPE_UINT8:
lval = zval_get_long(value);
*(uint8_t*)ptr = lval;
break;
case ZEND_FFI_TYPE_SINT8:
lval = zval_get_long(value);
*(int8_t*)ptr = lval;
break;
case ZEND_FFI_TYPE_UINT16:
lval = zval_get_long(value);
*(uint16_t*)ptr = lval;
break;
case ZEND_FFI_TYPE_SINT16:
lval = zval_get_long(value);
*(int16_t*)ptr = lval;
break;
case ZEND_FFI_TYPE_UINT32:
lval = zval_get_long(value);
*(uint32_t*)ptr = lval;
break;
case ZEND_FFI_TYPE_SINT32:
lval = zval_get_long(value);
*(int32_t*)ptr = lval;
break;
case ZEND_FFI_TYPE_UINT64:
lval = zval_get_long(value);
*(uint64_t*)ptr = lval;
break;
case ZEND_FFI_TYPE_SINT64:
lval = zval_get_long(value);
*(int64_t*)ptr = lval;
break;
case ZEND_FFI_TYPE_BOOL:
*(uint8_t*)ptr = zend_is_true(value);
break;
case ZEND_FFI_TYPE_CHAR:
str = zval_get_tmp_string(value, &tmp_str);
if (ZSTR_LEN(str) == 1) {
*(char*)ptr = ZSTR_VAL(str)[0];
} else {
zend_ffi_assign_incompatible(value, type);
return FAILURE;
}
zend_tmp_string_release(tmp_str);
break;
case ZEND_FFI_TYPE_ENUM:
kind = type->enumeration.kind;
goto again;
case ZEND_FFI_TYPE_POINTER:
if (Z_TYPE_P(value) == IS_NULL) {
*(void**)ptr = NULL;
break;
} else if (Z_TYPE_P(value) == IS_OBJECT && Z_OBJCE_P(value) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(value);
if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) {
if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
*(void**)ptr = *(void**)cdata->ptr;
} else {
if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign of owned C pointer");
return FAILURE;
}
*(void**)ptr = cdata->ptr;
}
return SUCCESS;
/* Allow transparent assignment of not-owned CData to compatible pointers */
} else if (ZEND_FFI_TYPE(cdata->type)->kind != ZEND_FFI_TYPE_POINTER
&& zend_ffi_is_compatible_type(ZEND_FFI_TYPE(type->pointer.type), ZEND_FFI_TYPE(cdata->type))) {
if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign pointer to owned C data");
return FAILURE;
}
*(void**)ptr = cdata->ptr;
return SUCCESS;
}
#if FFI_CLOSURES
} else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_FUNC) {
void *callback = zend_ffi_create_callback(ZEND_FFI_TYPE(type->pointer.type), value);
if (callback) {
*(void**)ptr = callback;
break;
} else {
return FAILURE;
}
#endif
}
zend_ffi_assign_incompatible(value, type);
return FAILURE;
case ZEND_FFI_TYPE_STRUCT:
case ZEND_FFI_TYPE_ARRAY:
default:
if (Z_TYPE_P(value) == IS_OBJECT && Z_OBJCE_P(value) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(value);
if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type)) &&
type->size == ZEND_FFI_TYPE(cdata->type)->size) {
memcpy(ptr, cdata->ptr, type->size);
return SUCCESS;
}
}
zend_ffi_assign_incompatible(value, type);
return FAILURE;
}
return SUCCESS;
}
/* }}} */
#if defined(ZEND_WIN32) && (defined(HAVE_FFI_FASTCALL) || defined(HAVE_FFI_STDCALL) || defined(HAVE_FFI_VECTORCALL_PARTIAL))
static size_t zend_ffi_arg_size(zend_ffi_type *type) /* {{{ */
{
zend_ffi_type *arg_type;
size_t arg_size = 0;
ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) {
arg_size += MAX(ZEND_FFI_TYPE(arg_type)->size, sizeof(size_t));
} ZEND_HASH_FOREACH_END();
return arg_size;
}
/* }}} */
#endif
static zend_always_inline zend_string *zend_ffi_mangled_func_name(zend_string *name, zend_ffi_type *type) /* {{{ */
{
#ifdef ZEND_WIN32
switch (type->func.abi) {
# ifdef HAVE_FFI_FASTCALL
case FFI_FASTCALL:
return strpprintf(0, "@%s@%zu", ZSTR_VAL(name), zend_ffi_arg_size(type));
# endif
# ifdef HAVE_FFI_STDCALL
case FFI_STDCALL:
return strpprintf(0, "_%s@%zu", ZSTR_VAL(name), zend_ffi_arg_size(type));
# endif
# ifdef HAVE_FFI_VECTORCALL_PARTIAL
case FFI_VECTORCALL_PARTIAL:
return strpprintf(0, "%s@@%zu", ZSTR_VAL(name), zend_ffi_arg_size(type));
# endif
}
#endif
return zend_string_copy(name);
}
/* }}} */
#if FFI_CLOSURES
typedef struct _zend_ffi_callback_data {
zend_fcall_info_cache fcc;
zend_ffi_type *type;
void *code;
void *callback;
ffi_cif cif;
uint32_t arg_count;
ffi_type *ret_type;
ffi_type *arg_types[0];
} zend_ffi_callback_data;
static void zend_ffi_callback_hash_dtor(zval *zv) /* {{{ */
{
zend_ffi_callback_data *callback_data = Z_PTR_P(zv);
ffi_closure_free(callback_data->callback);
if (callback_data->fcc.function_handler->common.fn_flags & ZEND_ACC_CLOSURE) {
OBJ_RELEASE(ZEND_CLOSURE_OBJECT(callback_data->fcc.function_handler));
}
efree(callback_data);
}
/* }}} */
static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, void* data) /* {{{ */
{
zend_ffi_callback_data *callback_data = (zend_ffi_callback_data*)data;
zend_fcall_info fci;
zend_ffi_type *ret_type;
zval retval;
ALLOCA_FLAG(use_heap)
fci.size = sizeof(zend_fcall_info);
ZVAL_UNDEF(&fci.function_name);
fci.retval = &retval;
fci.params = do_alloca(sizeof(zval) *callback_data->arg_count, use_heap);
fci.object = NULL;
fci.no_separation = 1;
fci.param_count = callback_data->arg_count;
if (callback_data->type->func.args) {
int n = 0;
zend_ffi_type *arg_type;
ZEND_HASH_FOREACH_PTR(callback_data->type->func.args, arg_type) {
arg_type = ZEND_FFI_TYPE(arg_type);
zend_ffi_cdata_to_zval(NULL, args[n], arg_type, BP_VAR_R, &fci.params[n], (zend_ffi_flags)(arg_type->attr & ZEND_FFI_ATTR_CONST), 0, 0);
n++;
} ZEND_HASH_FOREACH_END();
}
ZVAL_UNDEF(&retval);
if (zend_call_function(&fci, &callback_data->fcc) != SUCCESS) {
zend_throw_error(zend_ffi_exception_ce, "Cannot call callback");
}
if (callback_data->arg_count) {
int n = 0;
for (n = 0; n < callback_data->arg_count; n++) {
zval_ptr_dtor(&fci.params[n]);
}
}
free_alloca(fci.params, use_heap);
if (EG(exception)) {
zend_error(E_ERROR, "Throwing from FFI callbacks is not allowed");
}
ret_type = ZEND_FFI_TYPE(callback_data->type->func.ret_type);
if (ret_type->kind != ZEND_FFI_TYPE_VOID) {
zend_ffi_zval_to_cdata(ret, ret_type, &retval);
}
}
/* }}} */
static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ */
{
zend_fcall_info_cache fcc;
char *error = NULL;
uint32_t arg_count;
void *code;
void *callback;
zend_ffi_callback_data *callback_data;
if (type->attr & ZEND_FFI_ATTR_VARIADIC) {
zend_throw_error(zend_ffi_exception_ce, "Variadic function closures are not supported");
return NULL;
}
if (!zend_is_callable_ex(value, NULL, 0, NULL, &fcc, &error)) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to assign an invalid callback, %s", error);
return NULL;
}
arg_count = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
if (arg_count < fcc.function_handler->common.required_num_args) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to assign an invalid callback, insufficient number of arguments");
return NULL;
}
callback = ffi_closure_alloc(sizeof(ffi_closure), &code);
if (!callback) {
zend_throw_error(zend_ffi_exception_ce, "Cannot allocate callback");
return NULL;
}
callback_data = emalloc(sizeof(zend_ffi_callback_data) + sizeof(ffi_type*) * arg_count);
memcpy(&callback_data->fcc, &fcc, sizeof(zend_fcall_info_cache));
callback_data->type = type;
callback_data->callback = callback;
callback_data->code = code;
callback_data->arg_count = arg_count;
if (type->func.args) {
int n = 0;
zend_ffi_type *arg_type;
ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) {
arg_type = ZEND_FFI_TYPE(arg_type);
callback_data->arg_types[n] = zend_ffi_get_type(arg_type);
if (!callback_data->arg_types[n]) {
zend_ffi_pass_unsupported(arg_type);
efree(callback_data);
ffi_closure_free(callback);
return NULL;
}
n++;
} ZEND_HASH_FOREACH_END();
}
callback_data->ret_type = zend_ffi_get_type(ZEND_FFI_TYPE(type->func.ret_type));
if (!callback_data->ret_type) {
zend_ffi_return_unsupported(type->func.ret_type);
efree(callback_data);
ffi_closure_free(callback);
return NULL;
}
if (ffi_prep_cif(&callback_data->cif, type->func.abi, callback_data->arg_count, callback_data->ret_type, callback_data->arg_types) != FFI_OK) {
zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF");
efree(callback_data);
ffi_closure_free(callback);
return NULL;
}
if (ffi_prep_closure_loc(callback, &callback_data->cif, zend_ffi_callback_trampoline, callback_data, code) != FFI_OK) {
zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback");
efree(callback_data);
ffi_closure_free(callback);
return NULL;
}
if (!FFI_G(callbacks)) {
FFI_G(callbacks) = emalloc(sizeof(HashTable));
zend_hash_init(FFI_G(callbacks), 0, NULL, zend_ffi_callback_hash_dtor, 0);
}
zend_hash_next_index_insert_ptr(FFI_G(callbacks), callback_data);
if (fcc.function_handler->common.fn_flags & ZEND_ACC_CLOSURE) {
GC_ADDREF(ZEND_CLOSURE_OBJECT(fcc.function_handler));
}
return code;
}
/* }}} */
#endif
static zval *zend_ffi_cdata_get(zval *object, zval *member, int read_type, void **cache_slot, zval *rv) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
#if 0
if (UNEXPECTED(!cdata->ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return &EG(uninitialized_zval);
}
#endif
if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)
|| UNEXPECTED(!zend_string_equals_literal(Z_STR_P(member), "cdata"))) {
zend_throw_error(zend_ffi_exception_ce, "only 'cdata' property may be read");
return &EG(uninitialized_zval);;
}
zend_ffi_cdata_to_zval(cdata, cdata->ptr, type, BP_VAR_R, rv, 0, 0, 0);
return rv;
}
/* }}} */
static zval *zend_ffi_cdata_set(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
#if 0
if (UNEXPECTED(!cdata->ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return &EG(uninitialized_zval);;
}
#endif
if (UNEXPECTED(Z_TYPE_P(member) != IS_STRING)
|| UNEXPECTED(!zend_string_equals_literal(Z_STR_P(member), "cdata"))) {
zend_throw_error(zend_ffi_exception_ce, "only 'cdata' property may be set");
return &EG(uninitialized_zval);;
}
zend_ffi_zval_to_cdata(cdata->ptr, type, value);
return value;
}
/* }}} */
static int zend_ffi_cdata_cast_object(zval *readobj, zval *writeobj, int type) /* {{{ */
{
if (type == IS_STRING) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(readobj);
zend_ffi_type *ctype = ZEND_FFI_TYPE(cdata->type);
void *ptr = cdata->ptr;
zend_ffi_type_kind kind = ctype->kind;
again:
switch (kind) {
case ZEND_FFI_TYPE_FLOAT:
ZVAL_DOUBLE(writeobj, *(float*)ptr);
break;
case ZEND_FFI_TYPE_DOUBLE:
ZVAL_DOUBLE(writeobj, *(double*)ptr);
break;
#ifdef HAVE_LONG_DOUBLE
case ZEND_FFI_TYPE_LONGDOUBLE:
ZVAL_DOUBLE(writeobj, *(long double*)ptr);
break;
#endif
case ZEND_FFI_TYPE_UINT8:
ZVAL_LONG(writeobj, *(uint8_t*)ptr);
break;
case ZEND_FFI_TYPE_SINT8:
ZVAL_LONG(writeobj, *(int8_t*)ptr);
break;
case ZEND_FFI_TYPE_UINT16:
ZVAL_LONG(writeobj, *(uint16_t*)ptr);
break;
case ZEND_FFI_TYPE_SINT16:
ZVAL_LONG(writeobj, *(int16_t*)ptr);
break;
case ZEND_FFI_TYPE_UINT32:
ZVAL_LONG(writeobj, *(uint32_t*)ptr);
break;
case ZEND_FFI_TYPE_SINT32:
ZVAL_LONG(writeobj, *(int32_t*)ptr);
break;
case ZEND_FFI_TYPE_UINT64:
ZVAL_LONG(writeobj, *(uint64_t*)ptr);
break;
case ZEND_FFI_TYPE_SINT64:
ZVAL_LONG(writeobj, *(int64_t*)ptr);
break;
case ZEND_FFI_TYPE_BOOL:
ZVAL_BOOL(writeobj, *(uint8_t*)ptr);
break;
case ZEND_FFI_TYPE_CHAR:
ZVAL_INTERNED_STR(writeobj, ZSTR_CHAR(*(unsigned char*)ptr));
return SUCCESS;
case ZEND_FFI_TYPE_ENUM:
kind = ctype->enumeration.kind;
goto again;
case ZEND_FFI_TYPE_POINTER:
if (*(void**)ptr == NULL) {
ZVAL_NULL(writeobj);
break;
} else if ((ctype->attr & ZEND_FFI_ATTR_CONST) && ZEND_FFI_TYPE(ctype->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) {
ZVAL_STRING(writeobj, *(char**)ptr);
return SUCCESS;
}
return FAILURE;
default:
return FAILURE;
}
convert_to_string(writeobj);
return SUCCESS;
}
return FAILURE;
}
/* }}} */
static zval *zend_ffi_cdata_read_field(zval *object, zval *member, int read_type, void **cache_slot, zval *rv) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
void *ptr = cdata->ptr;
zend_ffi_field *field;
if (cache_slot && *cache_slot == type) {
field = *(cache_slot + 1);
} else {
zend_string *tmp_field_name;
zend_string *field_name = zval_get_tmp_string(member, &tmp_field_name);
if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
if (type->kind == ZEND_FFI_TYPE_POINTER) {
/* transparently dereference the pointer */
if (UNEXPECTED(!ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
zend_tmp_string_release(tmp_field_name);
return &EG(uninitialized_zval);
}
ptr = (void*)(*(char**)ptr);
if (UNEXPECTED(!ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
zend_tmp_string_release(tmp_field_name);
return &EG(uninitialized_zval);
}
type = ZEND_FFI_TYPE(type->pointer.type);
}
if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to read field '%s' of non C struct/union", ZSTR_VAL(field_name));
zend_tmp_string_release(tmp_field_name);
return &EG(uninitialized_zval);
}
}
field = zend_hash_find_ptr(&type->record.fields, field_name);
if (UNEXPECTED(!field)) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to read undefined field '%s' of C struct/union", ZSTR_VAL(field_name));
zend_tmp_string_release(tmp_field_name);
return &EG(uninitialized_zval);
}
zend_tmp_string_release(tmp_field_name);
if (cache_slot) {
*cache_slot = type;
*(cache_slot + 1) = field;
}
}
#if 0
if (UNEXPECTED(!ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return &EG(uninitialized_zval);
}
#endif
if (EXPECTED(!field->bits)) {
zend_ffi_type *field_type = field->type;
if (ZEND_FFI_TYPE_IS_OWNED(field_type)) {
field_type = ZEND_FFI_TYPE(field_type);
if (!(field_type->attr & ZEND_FFI_ATTR_STORED)
&& field_type->kind == ZEND_FFI_TYPE_POINTER) {
field->type = field_type = zend_ffi_remember_type(field_type);
}
}
ptr = (void*)(((char*)ptr) + field->offset);
zend_ffi_cdata_to_zval(NULL, ptr, field_type, read_type, rv, (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)field->is_const, 0, 0);
} else {
zend_ffi_bit_field_to_zval(ptr, field, rv);
}
return rv;
}
/* }}} */
static zval *zend_ffi_cdata_write_field(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
void *ptr = cdata->ptr;
zend_ffi_field *field;
if (cache_slot && *cache_slot == type) {
field = *(cache_slot + 1);
} else {
zend_string *tmp_field_name;
zend_string *field_name = zval_get_tmp_string(member, &tmp_field_name);
if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
if (type->kind == ZEND_FFI_TYPE_POINTER) {
/* transparently dereference the pointer */
if (UNEXPECTED(!ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
zend_tmp_string_release(tmp_field_name);
return value;
}
ptr = (void*)(*(char**)ptr);
if (UNEXPECTED(!ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
zend_tmp_string_release(tmp_field_name);
return value;
}
type = ZEND_FFI_TYPE(type->pointer.type);
}
if (UNEXPECTED(type->kind != ZEND_FFI_TYPE_STRUCT)) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to assign field '%s' of non C struct/union", ZSTR_VAL(field_name));
zend_tmp_string_release(tmp_field_name);
return value;
}
}
field = zend_hash_find_ptr(&type->record.fields, field_name);
if (UNEXPECTED(!field)) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to assign undefined field '%s' of C struct/union", ZSTR_VAL(field_name));
zend_tmp_string_release(tmp_field_name);
return value;
}
zend_tmp_string_release(tmp_field_name);
if (cache_slot) {
*cache_slot = type;
*(cache_slot + 1) = field;
}
}
#if 0
if (UNEXPECTED(!ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return value;
}
#endif
if (UNEXPECTED(cdata->flags & ZEND_FFI_FLAG_CONST)) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only location");
return value;
} else if (UNEXPECTED(field->is_const)) {
zend_string *tmp_field_name;
zend_string *field_name = zval_get_tmp_string(member, &tmp_field_name);
zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only field '%s'", ZSTR_VAL(field_name));
zend_tmp_string_release(tmp_field_name);
return value;
}
if (EXPECTED(!field->bits)) {
ptr = (void*)(((char*)ptr) + field->offset);
zend_ffi_zval_to_cdata(ptr, ZEND_FFI_TYPE(field->type), value);
} else {
zend_ffi_zval_to_bit_field(ptr, field, value);
}
return value;
}
/* }}} */
static zval *zend_ffi_cdata_read_dim(zval *object, zval *offset, int read_type, zval *rv) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
zend_long dim = zval_get_long(offset);
zend_ffi_type *dim_type;
void *ptr;
zend_ffi_flags is_const;
if (EXPECTED(type->kind == ZEND_FFI_TYPE_ARRAY)) {
if (UNEXPECTED((zend_ulong)(dim) >= (zend_ulong)type->array.length)
&& (UNEXPECTED(dim < 0) || UNEXPECTED(type->array.length != 0))) {
zend_throw_error(zend_ffi_exception_ce, "C array index out of bounds");
return &EG(uninitialized_zval);
}
is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
dim_type = type->array.type;
if (ZEND_FFI_TYPE_IS_OWNED(dim_type)) {
dim_type = ZEND_FFI_TYPE(dim_type);
if (!(dim_type->attr & ZEND_FFI_ATTR_STORED)
&& dim_type->kind == ZEND_FFI_TYPE_POINTER) {
type->array.type = dim_type = zend_ffi_remember_type(dim_type);
}
}
#if 0
if (UNEXPECTED(!cdata->ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return &EG(uninitialized_zval);
}
#endif
ptr = (void*)(((char*)cdata->ptr) + dim_type->size * dim);
} else if (EXPECTED(type->kind == ZEND_FFI_TYPE_POINTER)) {
is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
dim_type = type->pointer.type;
if (ZEND_FFI_TYPE_IS_OWNED(dim_type)) {
dim_type = ZEND_FFI_TYPE(dim_type);
if (!(dim_type->attr & ZEND_FFI_ATTR_STORED)
&& dim_type->kind == ZEND_FFI_TYPE_POINTER) {
type->pointer.type = dim_type = zend_ffi_remember_type(dim_type);
}
}
if (UNEXPECTED(!cdata->ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return &EG(uninitialized_zval);
}
ptr = (void*)((*(char**)cdata->ptr) + dim_type->size * dim);
} else {
zend_throw_error(zend_ffi_exception_ce, "Attempt to read element of non C array");
return &EG(uninitialized_zval);
}
zend_ffi_cdata_to_zval(NULL, ptr, dim_type, read_type, rv, is_const, 0, 0);
return rv;
}
/* }}} */
static void zend_ffi_cdata_write_dim(zval *object, zval *offset, zval *value) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
zend_long dim;
void *ptr;
zend_ffi_flags is_const;
if (offset == NULL) {
zend_throw_error(zend_ffi_exception_ce, "Cannot add next element to object of type FFI\\CData");
return;
}
dim = zval_get_long(offset);
if (EXPECTED(type->kind == ZEND_FFI_TYPE_ARRAY)) {
if (UNEXPECTED((zend_ulong)(dim) >= (zend_ulong)type->array.length)
&& (UNEXPECTED(dim < 0) || UNEXPECTED(type->array.length != 0))) {
zend_throw_error(zend_ffi_exception_ce, "C array index out of bounds");
return;
}
is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
type = ZEND_FFI_TYPE(type->array.type);
#if 0
if (UNEXPECTED(!cdata->ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return;
}
#endif
ptr = (void*)(((char*)cdata->ptr) + type->size * dim);
} else if (EXPECTED(type->kind == ZEND_FFI_TYPE_POINTER)) {
is_const = (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST);
type = ZEND_FFI_TYPE(type->pointer.type);
if (UNEXPECTED(!cdata->ptr)) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return;
}
ptr = (void*)((*(char**)cdata->ptr) + type->size * dim);
} else {
zend_throw_error(zend_ffi_exception_ce, "Attempt to assign element of non C array");
return;
}
if (UNEXPECTED(is_const)) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only location");
return;
}
zend_ffi_zval_to_cdata(ptr, type, value);
}
/* }}} */
#define MAX_TYPE_NAME_LEN 256
typedef struct _zend_ffi_ctype_name_buf {
char *start;
char *end;
char buf[MAX_TYPE_NAME_LEN];
} zend_ffi_ctype_name_buf;
static int zend_ffi_ctype_name_prepend(zend_ffi_ctype_name_buf *buf, const char *str, size_t len) /* {{{ */
{
buf->start -= len;
if (buf->start < buf->buf) {
return 0;
}
memcpy(buf->start, str, len);
return 1;
}
/* }}} */
static int zend_ffi_ctype_name_append(zend_ffi_ctype_name_buf *buf, const char *str, size_t len) /* {{{ */
{
if (buf->end + len > buf->buf + MAX_TYPE_NAME_LEN) {
return 0;
}
memcpy(buf->end, str, len);
buf->end += len;
return 1;
}
/* }}} */
static int zend_ffi_ctype_name(zend_ffi_ctype_name_buf *buf, const zend_ffi_type *type) /* {{{ */
{
const char *name = NULL;
int is_ptr = 0;
while (1) {
switch (type->kind) {
case ZEND_FFI_TYPE_VOID:
name = "void";
break;
case ZEND_FFI_TYPE_FLOAT:
name = "float";
break;
case ZEND_FFI_TYPE_DOUBLE:
name = "double";
break;
#ifdef HAVE_LONG_DOUBLE
case ZEND_FFI_TYPE_LONGDOUBLE:
name = "long double";
break;
#endif
case ZEND_FFI_TYPE_UINT8:
name = "uint8_t";
break;
case ZEND_FFI_TYPE_SINT8:
name = "int8_t";
break;
case ZEND_FFI_TYPE_UINT16:
name = "uint16_t";
break;
case ZEND_FFI_TYPE_SINT16:
name = "int16_t";
break;
case ZEND_FFI_TYPE_UINT32:
name = "uint32_t";
break;
case ZEND_FFI_TYPE_SINT32:
name = "int32_t";
break;
case ZEND_FFI_TYPE_UINT64:
name = "uint64_t";
break;
case ZEND_FFI_TYPE_SINT64:
name = "int64_t";
break;
case ZEND_FFI_TYPE_ENUM:
if (type->enumeration.tag_name) {
zend_ffi_ctype_name_prepend(buf, ZSTR_VAL(type->enumeration.tag_name), ZSTR_LEN(type->enumeration.tag_name));
} else {
zend_ffi_ctype_name_prepend(buf, "<anonymous>", sizeof("<anonymous>")-1);
}
name = "enum ";
break;
case ZEND_FFI_TYPE_BOOL:
name = "bool";
break;
case ZEND_FFI_TYPE_CHAR:
name = "char";
break;
case ZEND_FFI_TYPE_POINTER:
if (!zend_ffi_ctype_name_prepend(buf, "*", 1)) {
return 0;
}
is_ptr = 1;
type = ZEND_FFI_TYPE(type->pointer.type);
break;
case ZEND_FFI_TYPE_FUNC:
if (is_ptr) {
is_ptr = 0;
if (!zend_ffi_ctype_name_prepend(buf, "(", 1)
|| !zend_ffi_ctype_name_append(buf, ")", 1)) {
return 0;
}
}
if (!zend_ffi_ctype_name_append(buf, "(", 1)
|| !zend_ffi_ctype_name_append(buf, ")", 1)) {
return 0;
}
type = ZEND_FFI_TYPE(type->func.ret_type);
break;
case ZEND_FFI_TYPE_ARRAY:
if (is_ptr) {
is_ptr = 0;
if (!zend_ffi_ctype_name_prepend(buf, "(", 1)
|| !zend_ffi_ctype_name_append(buf, ")", 1)) {
return 0;
}
}
if (!zend_ffi_ctype_name_append(buf, "[", 1)) {
return 0;
}
if (type->attr & ZEND_FFI_ATTR_VLA) {
if (!zend_ffi_ctype_name_append(buf, "*", 1)) {
return 0;
}
} else if (!(type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
char str[MAX_LENGTH_OF_LONG + 1];
char *s = zend_print_long_to_buf(str + sizeof(str) - 1, type->array.length);
if (!zend_ffi_ctype_name_append(buf, s, strlen(s))) {
return 0;
}
}
if (!zend_ffi_ctype_name_append(buf, "]", 1)) {
return 0;
}
type = ZEND_FFI_TYPE(type->array.type);
break;
case ZEND_FFI_TYPE_STRUCT:
if (type->attr & ZEND_FFI_ATTR_UNION) {
if (type->record.tag_name) {
zend_ffi_ctype_name_prepend(buf, ZSTR_VAL(type->record.tag_name), ZSTR_LEN(type->record.tag_name));
} else {
zend_ffi_ctype_name_prepend(buf, "<anonymous>", sizeof("<anonymous>")-1);
}
name = "union ";
} else {
if (type->record.tag_name) {
zend_ffi_ctype_name_prepend(buf, ZSTR_VAL(type->record.tag_name), ZSTR_LEN(type->record.tag_name));
} else {
zend_ffi_ctype_name_prepend(buf, "<anonymous>", sizeof("<anonymous>")-1);
}
name = "struct ";
}
break;
default:
ZEND_ASSERT(0);
}
if (name) {
break;
}
}
// if (buf->start != buf->end && *buf->start != '[') {
// if (!zend_ffi_ctype_name_prepend(buf, " ", 1)) {
// return 0;
// }
// }
return zend_ffi_ctype_name_prepend(buf, name, strlen(name));
}
/* }}} */
static ZEND_COLD void zend_ffi_return_unsupported(zend_ffi_type *type) /* {{{ */
{
type = ZEND_FFI_TYPE(type);
if (type->kind == ZEND_FFI_TYPE_STRUCT) {
zend_throw_error(zend_ffi_exception_ce, "FFI return struct/union is not implemented");
} else if (type->kind == ZEND_FFI_TYPE_ARRAY) {
zend_throw_error(zend_ffi_exception_ce, "FFI return array is not implemented");
} else {
zend_throw_error(zend_ffi_exception_ce, "FFI internal error. Unsupported return type");
}
}
/* }}} */
static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type) /* {{{ */
{
type = ZEND_FFI_TYPE(type);
if (type->kind == ZEND_FFI_TYPE_STRUCT) {
zend_throw_error(zend_ffi_exception_ce, "FFI passing struct/union is not implemented");
} else if (type->kind == ZEND_FFI_TYPE_ARRAY) {
zend_throw_error(zend_ffi_exception_ce, "FFI passing array is not implemented");
} else {
zend_throw_error(zend_ffi_exception_ce, "FFI internal error. Unsupported parameter type");
}
}
/* }}} */
static ZEND_COLD void zend_ffi_pass_incompatible(zval *arg, zend_ffi_type *type, uint32_t n, zend_execute_data *execute_data) /* {{{ */
{
zend_ffi_ctype_name_buf buf1, buf2;
buf1.start = buf1.end = buf1.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
if (!zend_ffi_ctype_name(&buf1, type)) {
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name));
} else {
*buf1.end = 0;
if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
type = ZEND_FFI_TYPE(cdata->type);
buf2.start = buf2.end = buf2.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
if (!zend_ffi_ctype_name(&buf2, type)) {
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s', expecting '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name), buf1.start);
} else {
*buf2.end = 0;
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s', expecting '%s', found '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name), buf1.start, buf2.start);
}
} else {
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s', expecting '%s', found PHP '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name), buf1.start, zend_get_type_by_const(Z_TYPE_P(arg)));
}
}
}
/* }}} */
static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type) /* {{{ */
{
zend_ffi_ctype_name_buf buf1, buf2;
buf1.start = buf1.end = buf1.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
if (!zend_ffi_ctype_name(&buf1, type)) {
zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning");
} else {
*buf1.end = 0;
if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
type = ZEND_FFI_TYPE(cdata->type);
buf2.start = buf2.end = buf2.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
if (!zend_ffi_ctype_name(&buf2, type)) {
zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning to type '%s'", buf1.start);
} else {
*buf2.end = 0;
zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning to type '%s' from type '%s'", buf1.start, buf2.start);
}
} else {
zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning to type '%s' from PHP '%s'", buf1.start, zend_get_type_by_const(Z_TYPE_P(arg)));
}
}
}
/* }}} */
static zend_string *zend_ffi_get_class_name(zend_string *prefix, const zend_ffi_type *type) /* {{{ */
{
zend_ffi_ctype_name_buf buf;
buf.start = buf.end = buf.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
if (!zend_ffi_ctype_name(&buf, type)) {
return zend_string_copy(prefix);
} else {
zend_string *name = zend_string_alloc(ZSTR_LEN(prefix) + 1 + buf.end - buf.start, 0);
memcpy(ZSTR_VAL(name), ZSTR_VAL(prefix), ZSTR_LEN(prefix));
ZSTR_VAL(name)[ZSTR_LEN(prefix)] = ':';
memcpy(ZSTR_VAL(name) + ZSTR_LEN(prefix) + 1, buf.start, buf.end - buf.start);
ZSTR_VAL(name)[ZSTR_LEN(name)] = 0;
return name;
}
}
/* }}} */
static zend_string *zend_ffi_cdata_get_class_name(const zend_object *zobj) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)zobj;
return zend_ffi_get_class_name(zobj->ce->name, ZEND_FFI_TYPE(cdata->type));
}
/* }}} */
static int zend_ffi_cdata_compare_objects(zval *o1, zval *o2) /* {{{ */
{
if (Z_TYPE_P(o1) == IS_OBJECT && Z_OBJCE_P(o1) == zend_ffi_cdata_ce &&
Z_TYPE_P(o2) == IS_OBJECT && Z_OBJCE_P(o2) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata1 = (zend_ffi_cdata*)Z_OBJ_P(o1);
zend_ffi_cdata *cdata2 = (zend_ffi_cdata*)Z_OBJ_P(o2);
zend_ffi_type *type1 = ZEND_FFI_TYPE(cdata1->type);
zend_ffi_type *type2 = ZEND_FFI_TYPE(cdata2->type);
if (type1->kind == ZEND_FFI_TYPE_POINTER && type2->kind == ZEND_FFI_TYPE_POINTER) {
void *ptr1 = *(void**)cdata1->ptr;
void *ptr2 = *(void**)cdata2->ptr;
if (!ptr1 || !ptr2) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return 0;
}
return ptr1 == ptr2 ? 0 : (ptr1 < ptr2 ? -1 : 1);
}
}
zend_throw_error(zend_ffi_exception_ce, "Comparison of incompatible C types");
return 0;
}
/* }}} */
static int zend_ffi_cdata_count_elements(zval *object, zend_long *count) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
if (type->kind != ZEND_FFI_TYPE_ARRAY) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to count() on non C array");
return FAILURE;
} else {
*count = type->array.length;
return SUCCESS;
}
}
/* }}} */
static zend_object* zend_ffi_add(zend_ffi_cdata *base_cdata, zend_ffi_type *base_type, zend_long offset) /* {{{ */
{
char *ptr;
zend_ffi_type *ptr_type;
zend_ffi_cdata *cdata =
(zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
if (base_type->kind == ZEND_FFI_TYPE_POINTER) {
if (ZEND_FFI_TYPE_IS_OWNED(base_cdata->type)) {
if (!(base_type->attr & ZEND_FFI_ATTR_STORED)) {
if (GC_REFCOUNT(&base_cdata->std) == 1) {
/* transfer type ownership */
base_cdata->type = base_type;
base_type = ZEND_FFI_TYPE_MAKE_OWNED(base_type);
} else {
base_cdata->type = base_type = zend_ffi_remember_type(base_type);
}
}
}
cdata->type = base_type;
ptr = (char*)(*(void**)base_cdata->ptr);
ptr_type = ZEND_FFI_TYPE(base_type)->pointer.type;
} else {
zend_ffi_type *new_type = emalloc(sizeof(zend_ffi_type));
new_type->kind = ZEND_FFI_TYPE_POINTER;
new_type->attr = 0;
new_type->size = sizeof(void*);
new_type->align = _Alignof(void*);
ptr_type = base_type->array.type;
if (ZEND_FFI_TYPE_IS_OWNED(ptr_type)) {
ptr_type = ZEND_FFI_TYPE(ptr_type);
if (!(ptr_type->attr & ZEND_FFI_ATTR_STORED)) {
if (GC_REFCOUNT(&base_cdata->std) == 1) {
/* transfer type ownership */
base_type->array.type = ptr_type;
ptr_type = ZEND_FFI_TYPE_MAKE_OWNED(ptr_type);
} else {
base_type->array.type = ptr_type = zend_ffi_remember_type(ptr_type);
}
}
}
new_type->pointer.type = ptr_type;
cdata->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
ptr = (char*)base_cdata->ptr;
}
cdata->ptr = &cdata->ptr_holder;
cdata->ptr_holder = ptr +
(ptrdiff_t) (offset * ZEND_FFI_TYPE(ptr_type)->size);
cdata->flags = base_cdata->flags & ZEND_FFI_FLAG_CONST;
return &cdata->std;
}
/* }}} */
static int zend_ffi_cdata_do_operation(zend_uchar opcode, zval *result, zval *op1, zval *op2) /* {{{ */
{
zend_long offset;
ZVAL_DEREF(op1);
ZVAL_DEREF(op2);
if (Z_TYPE_P(op1) == IS_OBJECT && Z_OBJCE_P(op1) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata1 = (zend_ffi_cdata*)Z_OBJ_P(op1);
zend_ffi_type *type1 = ZEND_FFI_TYPE(cdata1->type);
if (type1->kind == ZEND_FFI_TYPE_POINTER || type1->kind == ZEND_FFI_TYPE_ARRAY) {
if (opcode == ZEND_ADD) {
offset = zval_get_long(op2);
ZVAL_OBJ(result, zend_ffi_add(cdata1, type1, offset));
if (result == op1) {
OBJ_RELEASE(&cdata1->std);
}
return SUCCESS;
} else if (opcode == ZEND_SUB) {
if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJCE_P(op2) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata2 = (zend_ffi_cdata*)Z_OBJ_P(op2);
zend_ffi_type *type2 = ZEND_FFI_TYPE(cdata2->type);
if (type2->kind == ZEND_FFI_TYPE_POINTER || type2->kind == ZEND_FFI_TYPE_ARRAY) {
zend_ffi_type *t1, *t2;
char *p1, *p2;
if (type1->kind == ZEND_FFI_TYPE_POINTER) {
t1 = ZEND_FFI_TYPE(type1->pointer.type);
p1 = (char*)(*(void**)cdata1->ptr);
} else {
t1 = ZEND_FFI_TYPE(type1->array.type);
p1 = cdata1->ptr;
}
if (type2->kind == ZEND_FFI_TYPE_POINTER) {
t2 = ZEND_FFI_TYPE(type2->pointer.type);
p2 = (char*)(*(void**)cdata2->ptr);
} else {
t2 = ZEND_FFI_TYPE(type2->array.type);
p2 = cdata2->ptr;
}
if (zend_ffi_is_same_type(t1, t2)) {
ZVAL_LONG(result,
(zend_long)(p1 - p2) / (zend_long)t1->size);
return SUCCESS;
}
}
}
offset = zval_get_long(op2);
ZVAL_OBJ(result, zend_ffi_add(cdata1, type1, -offset));
if (result == op1) {
OBJ_RELEASE(&cdata1->std);
}
return SUCCESS;
}
}
} else if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJCE_P(op2) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata2 = (zend_ffi_cdata*)Z_OBJ_P(op2);
zend_ffi_type *type2 = ZEND_FFI_TYPE(cdata2->type);
if (type2->kind == ZEND_FFI_TYPE_POINTER || type2->kind == ZEND_FFI_TYPE_ARRAY) {
if (opcode == ZEND_ADD) {
offset = zval_get_long(op1);
ZVAL_OBJ(result, zend_ffi_add(cdata2, type2, offset));
return SUCCESS;
}
}
}
return FAILURE;
}
/* }}} */
typedef struct _zend_ffi_cdata_iterator {
zend_object_iterator it;
zend_long key;
zval value;
zend_bool by_ref;
} zend_ffi_cdata_iterator;
static void zend_ffi_cdata_it_dtor(zend_object_iterator *iter) /* {{{ */
{
zval_ptr_dtor(&((zend_ffi_cdata_iterator*)iter)->value);
zval_ptr_dtor(&iter->data);
}
/* }}} */
static int zend_ffi_cdata_it_valid(zend_object_iterator *it) /* {{{ */
{
zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ(iter->it.data);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
return (iter->key >= 0 && iter->key < type->array.length) ? SUCCESS : FAILURE;
}
/* }}} */
static zval *zend_ffi_cdata_it_get_current_data(zend_object_iterator *it) /* {{{ */
{
zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ(iter->it.data);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
zend_ffi_type *dim_type;
void *ptr;
if (!cdata->ptr) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return &EG(uninitialized_zval);
}
dim_type = type->array.type;
if (ZEND_FFI_TYPE_IS_OWNED(dim_type)) {
dim_type = ZEND_FFI_TYPE(dim_type);
if (!(dim_type->attr & ZEND_FFI_ATTR_STORED)
&& dim_type->kind == ZEND_FFI_TYPE_POINTER) {
type->array.type = dim_type = zend_ffi_remember_type(dim_type);
}
}
ptr = (void*)((char*)cdata->ptr + dim_type->size * iter->it.index);
zval_ptr_dtor(&iter->value);
zend_ffi_cdata_to_zval(NULL, ptr, dim_type, iter->by_ref ? BP_VAR_RW : BP_VAR_R, &iter->value, (cdata->flags & ZEND_FFI_FLAG_CONST) | (zend_ffi_flags)(type->attr & ZEND_FFI_ATTR_CONST), 0, 0);
return &iter->value;
}
/* }}} */
static void zend_ffi_cdata_it_get_current_key(zend_object_iterator *it, zval *key) /* {{{ */
{
zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
ZVAL_LONG(key, iter->key);
}
/* }}} */
static void zend_ffi_cdata_it_move_forward(zend_object_iterator *it) /* {{{ */
{
zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
iter->key++;
}
/* }}} */
static void zend_ffi_cdata_it_rewind(zend_object_iterator *it) /* {{{ */
{
zend_ffi_cdata_iterator *iter = (zend_ffi_cdata_iterator*)it;
iter->key = 0;
}
/* }}} */
static const zend_object_iterator_funcs zend_ffi_cdata_it_funcs = {
zend_ffi_cdata_it_dtor,
zend_ffi_cdata_it_valid,
zend_ffi_cdata_it_get_current_data,
zend_ffi_cdata_it_get_current_key,
zend_ffi_cdata_it_move_forward,
zend_ffi_cdata_it_rewind,
NULL
};
static zend_object_iterator *zend_ffi_cdata_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
zend_ffi_cdata_iterator *iter;
if (type->kind != ZEND_FFI_TYPE_ARRAY) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to iterate on non C array");
return NULL;
}
iter = emalloc(sizeof(zend_ffi_cdata_iterator));
zend_iterator_init(&iter->it);
Z_ADDREF_P(object);
ZVAL_OBJ(&iter->it.data, Z_OBJ_P(object));
iter->it.funcs = &zend_ffi_cdata_it_funcs;
iter->key = 0;
iter->by_ref = by_ref;
ZVAL_UNDEF(&iter->value);
return &iter->it;
}
/* }}} */
static HashTable *zend_ffi_cdata_get_debug_info(zval *object, int *is_temp) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(object);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
void *ptr = cdata->ptr;
HashTable *ht = NULL;
zend_string *key;
zend_ffi_field *f;
zend_long n;
zval tmp;
if (!cdata->ptr) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return NULL;
}
switch (type->kind) {
case ZEND_FFI_TYPE_BOOL:
case ZEND_FFI_TYPE_CHAR:
case ZEND_FFI_TYPE_ENUM:
case ZEND_FFI_TYPE_FLOAT:
case ZEND_FFI_TYPE_DOUBLE:
#ifdef HAVE_LONG_DOUBLE
case ZEND_FFI_TYPE_LONGDOUBLE:
#endif
case ZEND_FFI_TYPE_UINT8:
case ZEND_FFI_TYPE_SINT8:
case ZEND_FFI_TYPE_UINT16:
case ZEND_FFI_TYPE_SINT16:
case ZEND_FFI_TYPE_UINT32:
case ZEND_FFI_TYPE_SINT32:
case ZEND_FFI_TYPE_UINT64:
case ZEND_FFI_TYPE_SINT64:
zend_ffi_cdata_to_zval(cdata, ptr, type, BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, 0);
ht = zend_new_array(1);
zend_hash_str_add(ht, "cdata", sizeof("cdata")-1, &tmp);
*is_temp = 1;
return ht;
break;
case ZEND_FFI_TYPE_POINTER:
if (*(void**)ptr == NULL) {
ZVAL_NULL(&tmp);
ht = zend_new_array(1);
zend_hash_index_add_new(ht, 0, &tmp);
*is_temp = 1;
return ht;
} else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_VOID) {
ZVAL_LONG(&tmp, (uintptr_t)*(void**)ptr);
ht = zend_new_array(1);
zend_hash_index_add_new(ht, 0, &tmp);
*is_temp = 1;
return ht;
} else {
zend_ffi_cdata_to_zval(NULL, *(void**)ptr, ZEND_FFI_TYPE(type->pointer.type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, 0);
ht = zend_new_array(1);
zend_hash_index_add_new(ht, 0, &tmp);
*is_temp = 1;
return ht;
}
break;
case ZEND_FFI_TYPE_STRUCT:
ht = zend_new_array(zend_hash_num_elements(&type->record.fields));
ZEND_HASH_FOREACH_STR_KEY_PTR(&type->record.fields, key, f) {
if (key) {
if (!f->bits) {
void *f_ptr = (void*)(((char*)ptr) + f->offset);
zend_ffi_cdata_to_zval(NULL, f_ptr, ZEND_FFI_TYPE(f->type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, type->attr & ZEND_FFI_ATTR_UNION);
zend_hash_add(ht, key, &tmp);
} else {
zend_ffi_bit_field_to_zval(ptr, f, &tmp);
zend_hash_add(ht, key, &tmp);
}
}
} ZEND_HASH_FOREACH_END();
*is_temp = 1;
return ht;
case ZEND_FFI_TYPE_ARRAY:
ht = zend_new_array(type->array.length);
for (n = 0; n < type->array.length; n++) {
zend_ffi_cdata_to_zval(NULL, ptr, ZEND_FFI_TYPE(type->array.type), BP_VAR_R, &tmp, ZEND_FFI_FLAG_CONST, 0, 0);
zend_hash_index_add(ht, n, &tmp);
ptr = (void*)(((char*)ptr) + ZEND_FFI_TYPE(type->array.type)->size);
}
*is_temp = 1;
return ht;
case ZEND_FFI_TYPE_FUNC:
ht = zend_new_array(0);
// TODO: function name ???
*is_temp = 1;
return ht;
break;
default:
ZEND_ASSERT(0);
break;
}
return NULL;
}
/* }}} */
static int zend_ffi_cdata_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(obj);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
zend_function *func;
if (type->kind != ZEND_FFI_TYPE_POINTER) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to call non C function pointer");
return FAILURE;
}
type = ZEND_FFI_TYPE(type->pointer.type);
if (type->kind != ZEND_FFI_TYPE_FUNC) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to call non C function pointer");
return FAILURE;
}
if (!cdata->ptr) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return FAILURE;
}
if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
func = &EG(trampoline);
} else {
func = ecalloc(sizeof(zend_internal_function), 1);
}
func->type = ZEND_INTERNAL_FUNCTION;
func->common.arg_flags[0] = 0;
func->common.arg_flags[1] = 0;
func->common.arg_flags[2] = 0;
func->common.fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE;
func->common.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE);
/* set to 0 to avoid arg_info[] allocation, because all values are passed by value anyway */
func->common.num_args = 0;
func->common.required_num_args = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
func->common.scope = NULL;
func->common.prototype = NULL;
func->common.arg_info = NULL;
func->internal_function.handler = ZEND_FN(ffi_trampoline);
func->internal_function.module = NULL;
func->internal_function.reserved[0] = type;
func->internal_function.reserved[1] = *(void**)cdata->ptr;
*ce_ptr = NULL;
*fptr_ptr= func;
*obj_ptr = NULL;
return SUCCESS;
}
/* }}} */
static zend_object *zend_ffi_ctype_new(zend_class_entry *class_type) /* {{{ */
{
zend_ffi_ctype *ctype;
ctype = emalloc(sizeof(zend_ffi_ctype));
zend_ffi_object_init(&ctype->std, class_type);
ctype->std.handlers = &zend_ffi_ctype_handlers;
ctype->type = NULL;
return &ctype->std;
}
/* }}} */
static void zend_ffi_ctype_free_obj(zend_object *object) /* {{{ */
{
zend_ffi_ctype *ctype = (zend_ffi_ctype*)object;
zend_ffi_type_dtor(ctype->type);
}
/* }}} */
static int zend_ffi_is_same_type(zend_ffi_type *type1, zend_ffi_type *type2) /* {{{ */
{
while (1) {
if (type1 == type2) {
return 1;
} else if (type1->kind == type2->kind) {
if (type1->kind < ZEND_FFI_TYPE_POINTER) {
return 1;
} else if (type1->kind == ZEND_FFI_TYPE_POINTER) {
type1 = ZEND_FFI_TYPE(type1->pointer.type);
type2 = ZEND_FFI_TYPE(type2->pointer.type);
if (type1->kind == ZEND_FFI_TYPE_VOID ||
type2->kind == ZEND_FFI_TYPE_VOID) {
return 1;
}
} else if (type1->kind == ZEND_FFI_TYPE_ARRAY &&
type1->array.length == type2->array.length) {
type1 = ZEND_FFI_TYPE(type1->array.type);
type2 = ZEND_FFI_TYPE(type2->array.type);
} else {
break;
}
} else {
break;
}
}
return 0;
}
/* }}} */
static zend_string *zend_ffi_ctype_get_class_name(const zend_object *zobj) /* {{{ */
{
zend_ffi_ctype *ctype = (zend_ffi_ctype*)zobj;
return zend_ffi_get_class_name(zobj->ce->name, ZEND_FFI_TYPE(ctype->type));
}
/* }}} */
static int zend_ffi_ctype_compare_objects(zval *o1, zval *o2) /* {{{ */
{
if (Z_TYPE_P(o1) == IS_OBJECT && Z_OBJCE_P(o1) == zend_ffi_ctype_ce &&
Z_TYPE_P(o2) == IS_OBJECT && Z_OBJCE_P(o2) == zend_ffi_ctype_ce) {
zend_ffi_ctype *ctype1 = (zend_ffi_ctype*)Z_OBJ_P(o1);
zend_ffi_ctype *ctype2 = (zend_ffi_ctype*)Z_OBJ_P(o2);
zend_ffi_type *type1 = ZEND_FFI_TYPE(ctype1->type);
zend_ffi_type *type2 = ZEND_FFI_TYPE(ctype2->type);
if (zend_ffi_is_same_type(type1, type2)) {
return 0;
} else {
return 1;
}
}
zend_throw_error(zend_ffi_exception_ce, "Comparison of incompatible C types");
return 0;
}
/* }}} */
static HashTable *zend_ffi_ctype_get_debug_info(zval *object, int *is_temp) /* {{{ */
{
return NULL;
}
/* }}} */
static zend_object *zend_ffi_new(zend_class_entry *class_type) /* {{{ */
{
zend_ffi *ffi;
ffi = emalloc(sizeof(zend_ffi));
zend_ffi_object_init(&ffi->std, class_type);
ffi->std.handlers = &zend_ffi_handlers;
ffi->lib = NULL;
ffi->symbols = NULL;
ffi->tags = NULL;
ffi->persistent = 0;
return &ffi->std;
}
/* }}} */
static void _zend_ffi_type_dtor(zend_ffi_type *type) /* {{{ */
{
type = ZEND_FFI_TYPE(type);
switch (type->kind) {
case ZEND_FFI_TYPE_ENUM:
if (type->enumeration.tag_name) {
zend_string_release(type->enumeration.tag_name);
}
break;
case ZEND_FFI_TYPE_STRUCT:
if (type->record.tag_name) {
zend_string_release(type->record.tag_name);
}
zend_hash_destroy(&type->record.fields);
break;
case ZEND_FFI_TYPE_POINTER:
zend_ffi_type_dtor(type->pointer.type);
break;
case ZEND_FFI_TYPE_ARRAY:
zend_ffi_type_dtor(type->array.type);
break;
case ZEND_FFI_TYPE_FUNC:
if (type->func.args) {
zend_hash_destroy(type->func.args);
pefree(type->func.args, type->attr & ZEND_FFI_ATTR_PERSISTENT);
}
zend_ffi_type_dtor(type->func.ret_type);
break;
default:
break;
}
pefree(type, type->attr & ZEND_FFI_ATTR_PERSISTENT);
}
/* }}} */
static void zend_ffi_type_hash_dtor(zval *zv) /* {{{ */
{
zend_ffi_type *type = Z_PTR_P(zv);
zend_ffi_type_dtor(type);
}
/* }}} */
static void zend_ffi_field_hash_dtor(zval *zv) /* {{{ */
{
zend_ffi_field *field = Z_PTR_P(zv);
zend_ffi_type_dtor(field->type);
efree(field);
}
/* }}} */
static void zend_ffi_field_hash_persistent_dtor(zval *zv) /* {{{ */
{
zend_ffi_field *field = Z_PTR_P(zv);
zend_ffi_type_dtor(field->type);
free(field);
}
/* }}} */
static void zend_ffi_symbol_hash_dtor(zval *zv) /* {{{ */
{
zend_ffi_symbol *sym = Z_PTR_P(zv);
zend_ffi_type_dtor(sym->type);
efree(sym);
}
/* }}} */
static void zend_ffi_symbol_hash_persistent_dtor(zval *zv) /* {{{ */
{
zend_ffi_symbol *sym = Z_PTR_P(zv);
zend_ffi_type_dtor(sym->type);
free(sym);
}
/* }}} */
static void zend_ffi_tag_hash_dtor(zval *zv) /* {{{ */
{
zend_ffi_tag *tag = Z_PTR_P(zv);
zend_ffi_type_dtor(tag->type);
efree(tag);
}
/* }}} */
static void zend_ffi_tag_hash_persistent_dtor(zval *zv) /* {{{ */
{
zend_ffi_tag *tag = Z_PTR_P(zv);
zend_ffi_type_dtor(tag->type);
free(tag);
}
/* }}} */
static void zend_ffi_cdata_dtor(zend_ffi_cdata *cdata) /* {{{ */
{
zend_ffi_type_dtor(cdata->type);
if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
if (cdata->ptr != (void*)&cdata->ptr_holder) {
pefree(cdata->ptr, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
} else {
pefree(cdata->ptr_holder, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
}
}
}
/* }}} */
static void zend_ffi_scope_hash_dtor(zval *zv) /* {{{ */
{
zend_ffi_scope *scope = Z_PTR_P(zv);
if (scope->symbols) {
zend_hash_destroy(scope->symbols);
free(scope->symbols);
}
if (scope->tags) {
zend_hash_destroy(scope->tags);
free(scope->tags);
}
free(scope);
}
/* }}} */
static void zend_ffi_free_obj(zend_object *object) /* {{{ */
{
zend_ffi *ffi = (zend_ffi*)object;
if (ffi->persistent) {
return;
}
if (ffi->lib) {
DL_UNLOAD(ffi->lib);
ffi->lib = NULL;
}
if (ffi->symbols) {
zend_hash_destroy(ffi->symbols);
efree(ffi->symbols);
}
if (ffi->tags) {
zend_hash_destroy(ffi->tags);
efree(ffi->tags);
}
}
/* }}} */
static void zend_ffi_cdata_free_obj(zend_object *object) /* {{{ */
{
zend_ffi_cdata *cdata = (zend_ffi_cdata*)object;
zend_ffi_cdata_dtor(cdata);
}
/* }}} */
static zend_object *zend_ffi_cdata_clone_obj(zval *zobject) /* {{{ */
{
zend_ffi_cdata *old_cdata = (zend_ffi_cdata*)Z_OBJ_P(zobject);
zend_ffi_type *type = ZEND_FFI_TYPE(old_cdata->type);
zend_ffi_cdata *new_cdata;
new_cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
if (type->kind < ZEND_FFI_TYPE_POINTER) {
new_cdata->std.handlers = &zend_ffi_cdata_value_handlers;
}
new_cdata->type = type;
new_cdata->ptr = emalloc(type->size);
memcpy(new_cdata->ptr, old_cdata->ptr, type->size);
new_cdata->flags |= ZEND_FFI_FLAG_OWNED;
return &new_cdata->std;
}
/* }}} */
static zval *zend_ffi_read_var(zval *object, zval *member, int read_type, void **cache_slot, zval *rv) /* {{{ */
{
zend_ffi *ffi = (zend_ffi*)Z_OBJ_P(object);
zend_string *tmp_var_name;
zend_string *var_name = zval_get_tmp_string(member, &tmp_var_name);
zend_ffi_symbol *sym = NULL;
if (ffi->symbols) {
sym = zend_hash_find_ptr(ffi->symbols, var_name);
if (sym && sym->kind != ZEND_FFI_SYM_VAR && sym->kind != ZEND_FFI_SYM_CONST && sym->kind != ZEND_FFI_SYM_FUNC) {
sym = NULL;
}
}
if (!sym) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to read undefined C variable '%s'", ZSTR_VAL(var_name));
zend_tmp_string_release(tmp_var_name);
return &EG(uninitialized_zval);
}
zend_tmp_string_release(tmp_var_name);
if (sym->kind == ZEND_FFI_SYM_VAR) {
zend_ffi_cdata_to_zval(NULL, sym->addr, ZEND_FFI_TYPE(sym->type), read_type, rv, (zend_ffi_flags)sym->is_const, 0, 0);
} else if (sym->kind == ZEND_FFI_SYM_FUNC) {
zend_ffi_cdata *cdata;
zend_ffi_type *new_type = emalloc(sizeof(zend_ffi_type));
new_type->kind = ZEND_FFI_TYPE_POINTER;
new_type->attr = 0;
new_type->size = sizeof(void*);
new_type->align = _Alignof(void*);
new_type->pointer.type = ZEND_FFI_TYPE(sym->type);
cdata = emalloc(sizeof(zend_ffi_cdata));
zend_ffi_object_init(&cdata->std, zend_ffi_cdata_ce);
cdata->std.handlers = &zend_ffi_cdata_handlers;
cdata->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
cdata->flags = ZEND_FFI_FLAG_CONST;
cdata->ptr_holder = sym->addr;
cdata->ptr = &cdata->ptr_holder;
ZVAL_OBJ(rv, &cdata->std);
} else {
ZVAL_LONG(rv, sym->value);
}
return rv;
}
/* }}} */
static zval *zend_ffi_write_var(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
{
zend_ffi *ffi = (zend_ffi*)Z_OBJ_P(object);
zend_string *tmp_var_name;
zend_string *var_name = zval_get_tmp_string(member, &tmp_var_name);
zend_ffi_symbol *sym = NULL;
if (ffi->symbols) {
sym = zend_hash_find_ptr(ffi->symbols, var_name);
if (sym && sym->kind != ZEND_FFI_SYM_VAR) {
sym = NULL;
}
}
if (!sym) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to assign undefined C variable '%s'", ZSTR_VAL(var_name));
zend_tmp_string_release(tmp_var_name);
return value;
}
zend_tmp_string_release(tmp_var_name);
if (sym->is_const) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to assign read-only C variable '%s'", ZSTR_VAL(var_name));
return value;
}
zend_ffi_zval_to_cdata(sym->addr, ZEND_FFI_TYPE(sym->type), value);
return value;
}
/* }}} */
static int zend_ffi_pass_arg(zval *arg, zend_ffi_type *type, ffi_type **pass_type, void **arg_values, uint32_t n, zend_execute_data *execute_data) /* {{{ */
{
zend_long lval;
double dval;
zend_string *str, *tmp_str;
zend_ffi_type_kind kind = type->kind;
ZVAL_DEREF(arg);
again:
switch (kind) {
case ZEND_FFI_TYPE_FLOAT:
dval = zval_get_double(arg);
*pass_type = &ffi_type_float;
*(float*)arg_values[n] = (float)dval;
break;
case ZEND_FFI_TYPE_DOUBLE:
dval = zval_get_double(arg);
*pass_type = &ffi_type_double;
*(double*)arg_values[n] = dval;
break;
#ifdef HAVE_LONG_DOUBLE
case ZEND_FFI_TYPE_LONGDOUBLE:
dval = zval_get_double(arg);
*pass_type = &ffi_type_double;
*(long double*)arg_values[n] = (long double)dval;
break;
#endif
case ZEND_FFI_TYPE_UINT8:
lval = zval_get_long(arg);
*pass_type = &ffi_type_uint8;
*(uint8_t*)arg_values[n] = (uint8_t)lval;
break;
case ZEND_FFI_TYPE_SINT8:
lval = zval_get_long(arg);
*pass_type = &ffi_type_sint8;
*(int8_t*)arg_values[n] = (int8_t)lval;
break;
case ZEND_FFI_TYPE_UINT16:
lval = zval_get_long(arg);
*pass_type = &ffi_type_uint16;
*(uint16_t*)arg_values[n] = (uint16_t)lval;
break;
case ZEND_FFI_TYPE_SINT16:
lval = zval_get_long(arg);
*pass_type = &ffi_type_sint16;
*(int16_t*)arg_values[n] = (int16_t)lval;
break;
case ZEND_FFI_TYPE_UINT32:
lval = zval_get_long(arg);
*pass_type = &ffi_type_uint32;
*(uint32_t*)arg_values[n] = (uint32_t)lval;
break;
case ZEND_FFI_TYPE_SINT32:
lval = zval_get_long(arg);
*pass_type = &ffi_type_sint32;
*(int32_t*)arg_values[n] = (int32_t)lval;
break;
case ZEND_FFI_TYPE_UINT64:
lval = zval_get_long(arg);
*pass_type = &ffi_type_uint64;
*(uint64_t*)arg_values[n] = (uint64_t)lval;
break;
case ZEND_FFI_TYPE_SINT64:
lval = zval_get_long(arg);
*pass_type = &ffi_type_sint64;
*(int64_t*)arg_values[n] = (int64_t)lval;
break;
case ZEND_FFI_TYPE_POINTER:
*pass_type = &ffi_type_pointer;
if (Z_TYPE_P(arg) == IS_NULL) {
*(void**)arg_values[n] = NULL;
return SUCCESS;
} else if (Z_TYPE_P(arg) == IS_STRING
&& ((ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR)
|| (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_VOID))) {
*(void**)arg_values[n] = Z_STRVAL_P(arg);
return SUCCESS;
} else if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) {
if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
if (!cdata->ptr) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return FAILURE;
}
*(void**)arg_values[n] = *(void**)cdata->ptr;
} else {
*(void**)arg_values[n] = cdata->ptr;
}
return SUCCESS;
}
#if FFI_CLOSURES
} else if (ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_FUNC) {
void *callback = zend_ffi_create_callback(ZEND_FFI_TYPE(type->pointer.type), arg);
if (callback) {
*(void**)arg_values[n] = callback;
break;
} else {
return FAILURE;
}
#endif
}
zend_ffi_pass_incompatible(arg, type, n, execute_data);
return FAILURE;
case ZEND_FFI_TYPE_BOOL:
*pass_type = &ffi_type_uint8;
*(uint8_t*)arg_values[n] = zend_is_true(arg);
break;
case ZEND_FFI_TYPE_CHAR:
str = zval_get_tmp_string(arg, &tmp_str);
*pass_type = &ffi_type_sint8;
*(char*)arg_values[n] = ZSTR_VAL(str)[0];
if (ZSTR_LEN(str) != 1) {
zend_ffi_pass_incompatible(arg, type, n, execute_data);
}
zend_tmp_string_release(tmp_str);
break;
case ZEND_FFI_TYPE_ENUM:
kind = type->enumeration.kind;
goto again;
case ZEND_FFI_TYPE_STRUCT:
if (!(type->attr & ZEND_FFI_ATTR_UNION)
&& Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) {
/* Create a fake structure type */
ffi_type *t = zend_ffi_make_fake_struct_type(type);
if (t) {
*pass_type = t;
arg_values[n] = cdata->ptr;
break;
}
}
}
zend_ffi_pass_incompatible(arg, type, n, execute_data);
return FAILURE;
default:
zend_ffi_pass_unsupported(type);
return FAILURE;
}
return SUCCESS;
}
/* }}} */
static int zend_ffi_pass_var_arg(zval *arg, ffi_type **pass_type, void **arg_values, uint32_t n, zend_execute_data *execute_data) /* {{{ */
{
ZVAL_DEREF(arg);
switch (Z_TYPE_P(arg)) {
case IS_NULL:
*pass_type = &ffi_type_pointer;
*(void**)arg_values[n] = NULL;
break;
case IS_FALSE:
*pass_type = &ffi_type_uint8;
*(uint8_t*)arg_values[n] = 0;
break;
case IS_TRUE:
*pass_type = &ffi_type_uint8;
*(uint8_t*)arg_values[n] = 1;
break;
case IS_LONG:
if (sizeof(zend_long) == 4) {
*pass_type = &ffi_type_sint32;
*(int32_t*)arg_values[n] = Z_LVAL_P(arg);
} else {
*pass_type = &ffi_type_sint64;
*(int64_t*)arg_values[n] = Z_LVAL_P(arg);
}
break;
case IS_DOUBLE:
*pass_type = &ffi_type_double;
*(double*)arg_values[n] = Z_DVAL_P(arg);
break;
case IS_STRING:
*pass_type = &ffi_type_pointer;
*(char**)arg_values[n] = Z_STRVAL_P(arg);
break;
case IS_OBJECT:
if (Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
return zend_ffi_pass_arg(arg, type, pass_type, arg_values, n, execute_data);
}
/* break missing intentionally */
default:
zend_throw_error(zend_ffi_exception_ce, "Unsupported argument type");
return FAILURE;
}
return SUCCESS;
}
/* }}} */
static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
{
zend_ffi_type *type = EX(func)->internal_function.reserved[0];
void *addr = EX(func)->internal_function.reserved[1];
ffi_cif cif;
ffi_type *ret_type = NULL;
ffi_type **arg_types = NULL;
void **arg_values = NULL;
uint32_t n, arg_count;
void *ret;
zend_ffi_type *arg_type;
ALLOCA_FLAG(arg_types_use_heap = 0)
ALLOCA_FLAG(arg_values_use_heap = 0)
ALLOCA_FLAG(ret_use_heap = 0)
ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC);
arg_count = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
if (type->attr & ZEND_FFI_ATTR_VARIADIC) {
if (arg_count > EX_NUM_ARGS()) {
zend_throw_error(zend_ffi_exception_ce, "Incorrect number of arguments for C function '%s', expecting at least %d parameter%s", ZSTR_VAL(EX(func)->internal_function.function_name), arg_count, (arg_count != 1) ? "s" : "");
goto exit;
}
if (EX_NUM_ARGS()) {
arg_types = do_alloca(
sizeof(ffi_type*) * EX_NUM_ARGS(), arg_types_use_heap);
arg_values = do_alloca(
(sizeof(void*) + ZEND_FFI_SIZEOF_ARG) * EX_NUM_ARGS(), arg_values_use_heap);
n = 0;
if (type->func.args) {
ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) {
arg_type = ZEND_FFI_TYPE(arg_type);
arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (ZEND_FFI_SIZEOF_ARG * n);
if (zend_ffi_pass_arg(EX_VAR_NUM(n), arg_type, &arg_types[n], arg_values, n, execute_data) != SUCCESS) {
free_alloca(arg_types, arg_types_use_heap);
free_alloca(arg_values, arg_values_use_heap);
goto exit;
}
n++;
} ZEND_HASH_FOREACH_END();
}
for (; n < EX_NUM_ARGS(); n++) {
arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (ZEND_FFI_SIZEOF_ARG * n);
if (zend_ffi_pass_var_arg(EX_VAR_NUM(n), &arg_types[n], arg_values, n, execute_data) != SUCCESS) {
free_alloca(arg_types, arg_types_use_heap);
free_alloca(arg_values, arg_values_use_heap);
goto exit;
}
}
}
ret_type = zend_ffi_get_type(ZEND_FFI_TYPE(type->func.ret_type));
if (!ret_type) {
zend_ffi_return_unsupported(type->func.ret_type);
free_alloca(arg_types, arg_types_use_heap);
free_alloca(arg_values, arg_values_use_heap);
goto exit;
}
if (ffi_prep_cif_var(&cif, type->func.abi, arg_count, EX_NUM_ARGS(), ret_type, arg_types) != FFI_OK) {
zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF");
free_alloca(arg_types, arg_types_use_heap);
free_alloca(arg_values, arg_values_use_heap);
goto exit;
}
} else {
if (arg_count != EX_NUM_ARGS()) {
zend_throw_error(zend_ffi_exception_ce, "Incorrect number of arguments for C function '%s', expecting exactly %d parameter%s", ZSTR_VAL(EX(func)->internal_function.function_name), arg_count, (arg_count != 1) ? "s" : "");
goto exit;
}
if (EX_NUM_ARGS()) {
arg_types = do_alloca(
(sizeof(ffi_type*) + sizeof(ffi_type)) * EX_NUM_ARGS(), arg_types_use_heap);
arg_values = do_alloca(
(sizeof(void*) + ZEND_FFI_SIZEOF_ARG) * EX_NUM_ARGS(), arg_values_use_heap);
n = 0;
if (type->func.args) {
ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) {
arg_type = ZEND_FFI_TYPE(arg_type);
arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (ZEND_FFI_SIZEOF_ARG * n);
if (zend_ffi_pass_arg(EX_VAR_NUM(n), arg_type, &arg_types[n], arg_values, n, execute_data) != SUCCESS) {
free_alloca(arg_types, arg_types_use_heap);
free_alloca(arg_values, arg_values_use_heap);
goto exit;
}
n++;
} ZEND_HASH_FOREACH_END();
}
}
ret_type = zend_ffi_get_type(ZEND_FFI_TYPE(type->func.ret_type));
if (!ret_type) {
zend_ffi_return_unsupported(type->func.ret_type);
free_alloca(arg_types, arg_types_use_heap);
free_alloca(arg_values, arg_values_use_heap);
goto exit;
}
if (ffi_prep_cif(&cif, type->func.abi, arg_count, ret_type, arg_types) != FFI_OK) {
zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF");
free_alloca(arg_types, arg_types_use_heap);
free_alloca(arg_values, arg_values_use_heap);
goto exit;
}
}
ret = do_alloca(MAX(ret_type->size, sizeof(ffi_arg)), ret_use_heap);
ffi_call(&cif, addr, ret, arg_values);
for (n = 0; n < arg_count; n++) {
if (arg_types[n]->type == FFI_TYPE_STRUCT) {
efree(arg_types[n]);
}
}
if (ret_type->type == FFI_TYPE_STRUCT) {
efree(ret_type);
}
if (EX_NUM_ARGS()) {
free_alloca(arg_types, arg_types_use_heap);
free_alloca(arg_values, arg_values_use_heap);
}
zend_ffi_cdata_to_zval(NULL, ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1, 0);
free_alloca(ret, ret_use_heap);
exit:
zend_string_release(EX(func)->common.function_name);
if (EX(func)->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
zend_free_trampoline(EX(func));
EX(func) = NULL;
}
}
/* }}} */
static zend_function *zend_ffi_get_func(zend_object **obj, zend_string *name, const zval *key) /* {{{ */
{
zend_ffi *ffi = (zend_ffi*)*obj;
zend_ffi_symbol *sym = NULL;
zend_function *func;
zend_ffi_type *type;
if (ZSTR_LEN(name) == sizeof("new") -1
&& (ZSTR_VAL(name)[0] == 'n' || ZSTR_VAL(name)[0] == 'N')
&& (ZSTR_VAL(name)[1] == 'e' || ZSTR_VAL(name)[1] == 'E')
&& (ZSTR_VAL(name)[2] == 'w' || ZSTR_VAL(name)[2] == 'W')) {
return (zend_function*)&zend_ffi_new_fn;
} else if (ZSTR_LEN(name) == sizeof("cast") -1
&& (ZSTR_VAL(name)[0] == 'c' || ZSTR_VAL(name)[0] == 'C')
&& (ZSTR_VAL(name)[1] == 'a' || ZSTR_VAL(name)[1] == 'A')
&& (ZSTR_VAL(name)[2] == 's' || ZSTR_VAL(name)[2] == 'S')
&& (ZSTR_VAL(name)[3] == 't' || ZSTR_VAL(name)[3] == 'T')) {
return (zend_function*)&zend_ffi_cast_fn;
} else if (ZSTR_LEN(name) == sizeof("type") -1
&& (ZSTR_VAL(name)[0] == 't' || ZSTR_VAL(name)[0] == 'T')
&& (ZSTR_VAL(name)[1] == 'y' || ZSTR_VAL(name)[1] == 'Y')
&& (ZSTR_VAL(name)[2] == 'p' || ZSTR_VAL(name)[2] == 'P')
&& (ZSTR_VAL(name)[3] == 'e' || ZSTR_VAL(name)[3] == 'E')) {
return (zend_function*)&zend_ffi_type_fn;
}
if (ffi->symbols) {
sym = zend_hash_find_ptr(ffi->symbols, name);
if (sym && sym->kind != ZEND_FFI_SYM_FUNC) {
sym = NULL;
}
}
if (!sym) {
zend_throw_error(zend_ffi_exception_ce, "Attempt to call undefined C function '%s'", ZSTR_VAL(name));
return NULL;
}
type = ZEND_FFI_TYPE(sym->type);
ZEND_ASSERT(type->kind == ZEND_FFI_TYPE_FUNC);
if (EXPECTED(EG(trampoline).common.function_name == NULL)) {
func = &EG(trampoline);
} else {
func = ecalloc(sizeof(zend_internal_function), 1);
}
func->common.type = ZEND_INTERNAL_FUNCTION;
func->common.arg_flags[0] = 0;
func->common.arg_flags[1] = 0;
func->common.arg_flags[2] = 0;
func->common.fn_flags = ZEND_ACC_CALL_VIA_TRAMPOLINE;
func->common.function_name = zend_string_copy(name);
/* set to 0 to avoid arg_info[] allocation, because all values are passed by value anyway */
func->common.num_args = 0;
func->common.required_num_args = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
func->common.scope = NULL;
func->common.prototype = NULL;
func->common.arg_info = NULL;
func->internal_function.handler = ZEND_FN(ffi_trampoline);
func->internal_function.module = NULL;
func->internal_function.reserved[0] = type;
func->internal_function.reserved[1] = sym->addr;
return func;
}
/* }}} */
static zend_never_inline int zend_ffi_disabled(void) /* {{{ */
{
zend_throw_error(zend_ffi_exception_ce, "FFI API is restricted by \"ffi.enable\" configuration directive");
return 0;
}
/* }}} */
static zend_always_inline int zend_ffi_validate_api_restriction(zend_execute_data *execute_data) /* {{{ */
{
if (EXPECTED(FFI_G(restriction) > ZEND_FFI_ENABLED)) {
ZEND_ASSERT(FFI_G(restriction) == ZEND_FFI_PRELOAD);
if (FFI_G(is_cli)
|| (execute_data->prev_execute_data
&& (execute_data->prev_execute_data->func->common.fn_flags & ZEND_ACC_PRELOADED))
|| (CG(compiler_options) & ZEND_COMPILE_PRELOAD)) {
return 1;
}
} else if (EXPECTED(FFI_G(restriction) == ZEND_FFI_ENABLED)) {
return 1;
}
return zend_ffi_disabled();
}
/* }}} */
#define ZEND_FFI_VALIDATE_API_RESTRICTION() do { \
if (UNEXPECTED(!zend_ffi_validate_api_restriction(execute_data))) { \
return; \
} \
} while (0)
ZEND_METHOD(FFI, cdef) /* {{{ */
{
zend_string *code = NULL;
zend_string *lib = NULL;
zend_ffi *ffi = NULL;
DL_HANDLE handle = NULL;
void *addr;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(0, 2)
Z_PARAM_OPTIONAL
Z_PARAM_STR(code)
Z_PARAM_STR(lib)
ZEND_PARSE_PARAMETERS_END();
if (lib) {
handle = DL_LOAD(ZSTR_VAL(lib));
if (!handle) {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s'", ZSTR_VAL(lib));
return;
}
#ifdef RTLD_DEFAULT
} else if (1) {
// TODO: this might need to be disabled or protected ???
handle = RTLD_DEFAULT;
#endif
}
FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;
if (code) {
/* Parse C definitions */
FFI_G(default_type_attr) = ZEND_FFI_ATTR_STORED;
if (zend_ffi_parse_decl(ZSTR_VAL(code), ZSTR_LEN(code)) != SUCCESS) {
if (FFI_G(symbols)) {
zend_hash_destroy(FFI_G(symbols));
efree(FFI_G(symbols));
FFI_G(symbols) = NULL;
}
if (FFI_G(tags)) {
zend_hash_destroy(FFI_G(tags));
efree(FFI_G(tags));
FFI_G(tags) = NULL;
}
return;
}
if (FFI_G(symbols)) {
zend_string *name;
zend_ffi_symbol *sym;
ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(symbols), name, sym) {
if (sym->kind == ZEND_FFI_SYM_VAR) {
addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name));
if (!addr) {
zend_throw_error(zend_ffi_exception_ce, "Failed resolving C variable '%s'", ZSTR_VAL(name));
}
sym->addr = addr;
} else if (sym->kind == ZEND_FFI_SYM_FUNC) {
zend_string *mangled_name = zend_ffi_mangled_func_name(name, ZEND_FFI_TYPE(sym->type));
addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(mangled_name));
zend_string_release(mangled_name);
if (!addr) {
zend_throw_error(zend_ffi_exception_ce, "Failed resolving C function '%s'", ZSTR_VAL(name));
}
sym->addr = addr;
}
} ZEND_HASH_FOREACH_END();
}
}
ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
ffi->lib = handle;
ffi->symbols = FFI_G(symbols);
ffi->tags = FFI_G(tags);
FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;
RETURN_OBJ(&ffi->std);
}
/* }}} */
static int zend_ffi_same_types(zend_ffi_type *old, zend_ffi_type *type) /* {{{ */
{
if (old == type) {
return 1;
}
if (old->kind != type->kind
|| old->size != type->size
|| old->align != type->align
|| old->attr != type->attr) {
return 0;
}
switch (old->kind) {
case ZEND_FFI_TYPE_ENUM:
return old->enumeration.kind == type->enumeration.kind;
case ZEND_FFI_TYPE_ARRAY:
return old->array.length == type->array.length
&& zend_ffi_same_types(ZEND_FFI_TYPE(old->array.type), ZEND_FFI_TYPE(type->array.type));
case ZEND_FFI_TYPE_POINTER:
return zend_ffi_same_types(ZEND_FFI_TYPE(old->pointer.type), ZEND_FFI_TYPE(type->pointer.type));
case ZEND_FFI_TYPE_STRUCT:
if (zend_hash_num_elements(&old->record.fields) != zend_hash_num_elements(&type->record.fields)) {
return 0;
} else {
zend_ffi_field *old_field, *field;
zend_string *key;
Bucket *b = type->record.fields.arData;
ZEND_HASH_FOREACH_STR_KEY_PTR(&old->record.fields, key, old_field) {
while (Z_TYPE(b->val) == IS_UNDEF) {
b++;
}
if (key) {
if (!b->key
|| !zend_string_equals(key, b->key)) {
return 0;
}
} else if (b->key) {
return 0;
}
field = Z_PTR(b->val);
if (old_field->offset != field->offset
|| old_field->is_const != field->is_const
|| old_field->is_nested != field->is_nested
|| old_field->first_bit != field->first_bit
|| old_field->bits != field->bits
|| !zend_ffi_same_types(ZEND_FFI_TYPE(old_field->type), ZEND_FFI_TYPE(field->type))) {
return 0;
}
b++;
} ZEND_HASH_FOREACH_END();
}
break;
case ZEND_FFI_TYPE_FUNC:
if (old->func.abi != type->func.abi
|| ((old->func.args ? zend_hash_num_elements(old->func.args) : 0) != (type->func.args ? zend_hash_num_elements(type->func.args) : 0))
|| !zend_ffi_same_types(ZEND_FFI_TYPE(old->func.ret_type), ZEND_FFI_TYPE(type->func.ret_type))) {
return 0;
} else if (old->func.args) {
zend_ffi_type *arg_type;
Bucket *b = type->func.args->arData;
ZEND_HASH_FOREACH_PTR(old->func.args, arg_type) {
while (Z_TYPE(b->val) == IS_UNDEF) {
b++;
}
if (!zend_ffi_same_types(ZEND_FFI_TYPE(arg_type), ZEND_FFI_TYPE(Z_PTR(b->val)))) {
return 0;
}
b++;
} ZEND_HASH_FOREACH_END();
}
break;
default:
break;
}
return 1;
}
/* }}} */
static int zend_ffi_same_symbols(zend_ffi_symbol *old, zend_ffi_symbol *sym) /* {{{ */
{
if (old->kind != sym->kind || old->is_const != sym->is_const) {
return 0;
}
if (old->kind == ZEND_FFI_SYM_CONST) {
if (old->value != sym->value) {
return 0;
}
}
return zend_ffi_same_types(ZEND_FFI_TYPE(old->type), ZEND_FFI_TYPE(sym->type));
}
/* }}} */
static int zend_ffi_same_tags(zend_ffi_tag *old, zend_ffi_tag *tag) /* {{{ */
{
if (old->kind != tag->kind) {
return 0;
}
return zend_ffi_same_types(ZEND_FFI_TYPE(old->type), ZEND_FFI_TYPE(tag->type));
}
/* }}} */
static int zend_ffi_subst_old_type(zend_ffi_type **dcl, zend_ffi_type *old, zend_ffi_type *type) /* {{{ */
{
zend_ffi_type *dcl_type;
zend_ffi_field *field;
if (ZEND_FFI_TYPE(*dcl) == type) {
*dcl = old;
return 1;
}
dcl_type = *dcl;
switch (dcl_type->kind) {
case ZEND_FFI_TYPE_POINTER:
return zend_ffi_subst_old_type(&dcl_type->pointer.type, old, type);
case ZEND_FFI_TYPE_ARRAY:
return zend_ffi_subst_old_type(&dcl_type->array.type, old, type);
case ZEND_FFI_TYPE_FUNC:
if (zend_ffi_subst_old_type(&dcl_type->func.ret_type, old, type)) {
return 1;
}
if (dcl_type->func.args) {
zval *zv;
ZEND_HASH_FOREACH_VAL(dcl_type->func.args, zv) {
if (zend_ffi_subst_old_type((zend_ffi_type**)&Z_PTR_P(zv), old, type)) {
return 1;
}
} ZEND_HASH_FOREACH_END();
}
break;
case ZEND_FFI_TYPE_STRUCT:
ZEND_HASH_FOREACH_PTR(&dcl_type->record.fields, field) {
if (zend_ffi_subst_old_type(&field->type, old, type)) {
return 1;
}
} ZEND_HASH_FOREACH_END();
break;
default:
break;
}
return 0;
} /* }}} */
static void zend_ffi_cleanup_type(zend_ffi_type *old, zend_ffi_type *type) /* {{{ */
{
zend_ffi_symbol *sym;
zend_ffi_tag *tag;
if (FFI_G(symbols)) {
ZEND_HASH_FOREACH_PTR(FFI_G(symbols), sym) {
zend_ffi_subst_old_type(&sym->type, old, type);
} ZEND_HASH_FOREACH_END();
}
if (FFI_G(tags)) {
ZEND_HASH_FOREACH_PTR(FFI_G(tags), tag) {
zend_ffi_subst_old_type(&tag->type, old, type);
} ZEND_HASH_FOREACH_END();
}
}
/* }}} */
static zend_ffi_type *zend_ffi_remember_type(zend_ffi_type *type) /* {{{ */
{
if (!FFI_G(weak_types)) {
FFI_G(weak_types) = emalloc(sizeof(HashTable));
zend_hash_init(FFI_G(weak_types), 0, NULL, zend_ffi_type_hash_dtor, 0);
}
// TODO: avoid dups ???
type->attr |= ZEND_FFI_ATTR_STORED;
zend_hash_next_index_insert_ptr(FFI_G(weak_types), ZEND_FFI_TYPE_MAKE_OWNED(type));
return type;
}
/* }}} */
static zend_ffi *zend_ffi_load(const char *filename, zend_bool preload) /* {{{ */
{
struct stat buf;
int fd;
char *code, *code_pos, *scope_name, *lib;
size_t code_size, scope_name_len;
zend_ffi *ffi;
DL_HANDLE handle = NULL;
zend_ffi_scope *scope = NULL;
zend_string *name;
zend_ffi_symbol *sym;
zend_ffi_tag *tag;
void *addr;
if (stat(filename, &buf) != 0) {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', file doesn't exist", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', file doesn't exist", filename);
}
return NULL;
}
if ((buf.st_mode & S_IFMT) != S_IFREG) {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', not a regular file", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', not a regular file", filename);
}
return NULL;
}
code_size = buf.st_size;
code = emalloc(code_size + 1);
fd = open(filename, O_RDONLY, 0);
if (fd < 0 || read(fd, code, code_size) != code_size) {
if (preload) {
zend_error(E_WARNING, "FFI: Failed pre-loading '%s', cannot read_file", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', cannot read_file", filename);
}
efree(code);
close(fd);
return NULL;
}
close(fd);
code[code_size] = 0;
FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;
FFI_G(persistent) = preload;
FFI_G(default_type_attr) = preload ?
ZEND_FFI_ATTR_STORED | ZEND_FFI_ATTR_PERSISTENT :
ZEND_FFI_ATTR_STORED;
scope_name = NULL;
scope_name_len = 0;
lib = NULL;
code_pos = zend_ffi_parse_directives(filename, code, &scope_name, &lib, preload);
if (!code_pos) {
efree(code);
FFI_G(persistent) = 0;
return NULL;
}
code_size -= code_pos - code;
if (zend_ffi_parse_decl(code_pos, code_size) != SUCCESS) {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s'", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s'", filename);
}
goto cleanup;
}
if (lib) {
handle = DL_LOAD(lib);
if (!handle) {
if (preload) {
zend_error(E_WARNING, "FFI: Failed pre-loading '%s'", lib);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s'", lib);
}
goto cleanup;
}
#ifdef RTLD_DEFAULT
} else if (1) {
// TODO: this might need to be disabled or protected ???
handle = RTLD_DEFAULT;
#endif
}
if (preload) {
if (!scope_name) {
scope_name = "C";
}
scope_name_len = strlen(scope_name);
if (FFI_G(scopes)) {
scope = zend_hash_str_find_ptr(FFI_G(scopes), scope_name, scope_name_len);
}
}
if (FFI_G(symbols)) {
ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(symbols), name, sym) {
if (sym->kind == ZEND_FFI_SYM_VAR) {
addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(name));
if (!addr) {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', cannot resolve C variable '%s'", filename, ZSTR_VAL(name));
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed resolving C variable '%s'", ZSTR_VAL(name));
}
if (lib) {
DL_UNLOAD(handle);
}
goto cleanup;
}
sym->addr = addr;
} else if (sym->kind == ZEND_FFI_SYM_FUNC) {
zend_string *mangled_name = zend_ffi_mangled_func_name(name, ZEND_FFI_TYPE(sym->type));
addr = DL_FETCH_SYMBOL(handle, ZSTR_VAL(mangled_name));
zend_string_release(mangled_name);
if (!addr) {
if (preload) {
zend_error(E_WARNING, "failed pre-loading '%s', cannot resolve C function '%s'", filename, ZSTR_VAL(name));
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed resolving C function '%s'", ZSTR_VAL(name));
}
if (lib) {
DL_UNLOAD(handle);
}
goto cleanup;
}
sym->addr = addr;
}
if (scope && scope->symbols) {
zend_ffi_symbol *old_sym = zend_hash_find_ptr(scope->symbols, name);
if (old_sym) {
if (zend_ffi_same_symbols(old_sym, sym)) {
if (ZEND_FFI_TYPE_IS_OWNED(sym->type)
&& ZEND_FFI_TYPE(old_sym->type) != ZEND_FFI_TYPE(sym->type)) {
zend_ffi_type *type = ZEND_FFI_TYPE(sym->type);
zend_ffi_cleanup_type(ZEND_FFI_TYPE(old_sym->type), ZEND_FFI_TYPE(type));
zend_ffi_type_dtor(type);
}
} else {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', redefinition of '%s'", filename, ZSTR_VAL(name));
if (lib) {
DL_UNLOAD(handle);
}
goto cleanup;
}
}
}
} ZEND_HASH_FOREACH_END();
}
if (preload) {
if (scope && scope->tags && FFI_G(tags)) {
ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(tags), name, tag) {
zend_ffi_tag *old_tag = zend_hash_find_ptr(scope->tags, name);
if (old_tag) {
if (zend_ffi_same_tags(old_tag, tag)) {
if (ZEND_FFI_TYPE_IS_OWNED(tag->type)
&& ZEND_FFI_TYPE(old_tag->type) != ZEND_FFI_TYPE(tag->type)) {
zend_ffi_type *type = ZEND_FFI_TYPE(tag->type);
zend_ffi_cleanup_type(ZEND_FFI_TYPE(old_tag->type), ZEND_FFI_TYPE(type));
zend_ffi_type_dtor(type);
}
} else {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', redefinition of '%s %s'", filename, zend_ffi_tag_kind_name[tag->kind], ZSTR_VAL(name));
if (lib) {
DL_UNLOAD(handle);
}
goto cleanup;
}
}
} ZEND_HASH_FOREACH_END();
}
if (!scope) {
scope = malloc(sizeof(zend_ffi_scope));
scope->symbols = FFI_G(symbols);
scope->tags = FFI_G(tags);
if (!FFI_G(scopes)) {
FFI_G(scopes) = malloc(sizeof(HashTable));
zend_hash_init(FFI_G(scopes), 0, NULL, zend_ffi_scope_hash_dtor, 1);
}
zend_hash_str_add_ptr(FFI_G(scopes), scope_name, scope_name_len, scope);
} else {
if (FFI_G(symbols)) {
if (!scope->symbols) {
scope->symbols = FFI_G(symbols);
FFI_G(symbols) = NULL;
} else {
ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(symbols), name, sym) {
if (!zend_hash_add_ptr(scope->symbols, name, sym)) {
zend_ffi_type_dtor(sym->type);
free(sym);
}
} ZEND_HASH_FOREACH_END();
FFI_G(symbols)->pDestructor = NULL;
zend_hash_destroy(FFI_G(symbols));
}
}
if (FFI_G(tags)) {
if (!scope->tags) {
scope->tags = FFI_G(tags);
FFI_G(tags) = NULL;
} else {
ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(tags), name, tag) {
if (!zend_hash_add_ptr(scope->tags, name, tag)) {
zend_ffi_type_dtor(tag->type);
free(tag);
}
} ZEND_HASH_FOREACH_END();
FFI_G(tags)->pDestructor = NULL;
zend_hash_destroy(FFI_G(tags));
}
}
}
if (EG(objects_store).object_buckets) {
ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
} else {
ffi = ecalloc(1, sizeof(zend_ffi));
}
ffi->symbols = scope->symbols;
ffi->tags = scope->tags;
ffi->persistent = 1;
} else {
ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
ffi->lib = handle;
ffi->symbols = FFI_G(symbols);
ffi->tags = FFI_G(tags);
}
efree(code);
FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;
FFI_G(persistent) = 0;
return ffi;
cleanup:
efree(code);
if (FFI_G(symbols)) {
zend_hash_destroy(FFI_G(symbols));
pefree(FFI_G(symbols), preload);
FFI_G(symbols) = NULL;
}
if (FFI_G(tags)) {
zend_hash_destroy(FFI_G(tags));
pefree(FFI_G(tags), preload);
FFI_G(tags) = NULL;
}
FFI_G(persistent) = 0;
return NULL;
}
/* }}} */
ZEND_METHOD(FFI, load) /* {{{ */
{
zend_string *fn;
zend_ffi *ffi;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(fn)
ZEND_PARSE_PARAMETERS_END();
if (CG(compiler_options) & ZEND_COMPILE_PRELOAD_IN_CHILD) {
zend_throw_error(zend_ffi_exception_ce, "FFI::load() doesn't work in conjunction with \"opcache.preload_user\". Use \"ffi.preload\" instead.");
return;
}
ffi = zend_ffi_load(ZSTR_VAL(fn), (CG(compiler_options) & ZEND_COMPILE_PRELOAD) != 0);
if (ffi) {
RETURN_OBJ(&ffi->std);
}
}
/* }}} */
ZEND_METHOD(FFI, scope) /* {{{ */
{
zend_string *scope_name;
zend_ffi_scope *scope = NULL;
zend_ffi *ffi;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(scope_name)
ZEND_PARSE_PARAMETERS_END();
if (FFI_G(scopes)) {
scope = zend_hash_find_ptr(FFI_G(scopes), scope_name);
}
if (!scope) {
zend_throw_error(zend_ffi_exception_ce, "Failed loading scope '%s'", ZSTR_VAL(scope_name));
return;
}
ffi = (zend_ffi*)zend_ffi_new(zend_ffi_ce);
ffi->symbols = scope->symbols;
ffi->tags = scope->tags;
ffi->persistent = 1;
RETURN_OBJ(&ffi->std);
}
/* }}} */
static void zend_ffi_cleanup_dcl(zend_ffi_dcl *dcl) /* {{{ */
{
if (dcl) {
zend_ffi_type_dtor(dcl->type);
dcl->type = NULL;
}
}
/* }}} */
static void zend_ffi_throw_parser_error(const char *format, ...) /* {{{ */
{
va_list va;
char *message = NULL;
va_start(va, format);
zend_vspprintf(&message, 0, format, va);
if (EG(current_execute_data)) {
zend_throw_exception(zend_ffi_parser_exception_ce, message, 0);
} else {
zend_error(E_WARNING, "FFI Parser: %s", message);
}
efree(message);
va_end(va);
}
/* }}} */
static int zend_ffi_validate_vla(zend_ffi_type *type) /* {{{ */
{
if (!FFI_G(allow_vla) && (type->attr & ZEND_FFI_ATTR_VLA)) {
zend_ffi_throw_parser_error("'[*]' not allowed in other than function prototype scope at line %d", FFI_G(line));
return FAILURE;
}
return SUCCESS;
}
/* }}} */
static int zend_ffi_validate_incomplete_type(zend_ffi_type *type, zend_bool allow_incomplete_tag, zend_bool allow_incomplete_array) /* {{{ */
{
if (!allow_incomplete_tag && (type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
if (FFI_G(tags)) {
zend_string *key;
zend_ffi_tag *tag;
ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(tags), key, tag) {
if (ZEND_FFI_TYPE(tag->type) == type) {
if (type->kind == ZEND_FFI_TYPE_ENUM) {
zend_ffi_throw_parser_error("incomplete 'enum %s' at line %d", ZSTR_VAL(key), FFI_G(line));
} else if (type->attr & ZEND_FFI_ATTR_UNION) {
zend_ffi_throw_parser_error("incomplete 'union %s' at line %d", ZSTR_VAL(key), FFI_G(line));
} else {
zend_ffi_throw_parser_error("incomplete 'struct %s' at line %d", ZSTR_VAL(key), FFI_G(line));
}
return FAILURE;
}
} ZEND_HASH_FOREACH_END();
}
if (FFI_G(symbols)) {
zend_string *key;
zend_ffi_symbol *sym;
ZEND_HASH_FOREACH_STR_KEY_PTR(FFI_G(symbols), key, sym) {
if (type == ZEND_FFI_TYPE(sym->type)) {
zend_ffi_throw_parser_error("incomplete C type '%s' at line %d", ZSTR_VAL(key), FFI_G(line));
return FAILURE;
}
} ZEND_HASH_FOREACH_END();
}
zend_ffi_throw_parser_error("incomplete type at line %d", FFI_G(line));
return FAILURE;
} else if (!allow_incomplete_array && type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY) {
zend_ffi_throw_parser_error("'[]' not allowed at line %d", FFI_G(line));
return FAILURE;
} else if (!FFI_G(allow_vla) && (type->attr & ZEND_FFI_ATTR_VLA)) {
zend_ffi_throw_parser_error("'[*]' not allowed in other than function prototype scope at line %d", FFI_G(line));
return FAILURE;
}
return SUCCESS;
}
/* }}} */
static int zend_ffi_validate_type(zend_ffi_type *type, zend_bool allow_incomplete_tag, zend_bool allow_incomplete_array) /* {{{ */
{
if (type->kind == ZEND_FFI_TYPE_VOID) {
zend_ffi_throw_parser_error("'void' type is not allowed at line %d", FFI_G(line));
return FAILURE;
}
return zend_ffi_validate_incomplete_type(type, allow_incomplete_tag, allow_incomplete_array);
}
/* }}} */
static int zend_ffi_validate_var_type(zend_ffi_type *type, zend_bool allow_incomplete_array) /* {{{ */
{
if (type->kind == ZEND_FFI_TYPE_FUNC) {
zend_ffi_throw_parser_error("'function' type is not allowed at line %d", FFI_G(line));
return FAILURE;
}
return zend_ffi_validate_type(type, 0, allow_incomplete_array);
}
/* }}} */
void zend_ffi_validate_type_name(zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_finalize_type(dcl);
if (zend_ffi_validate_var_type(ZEND_FFI_TYPE(dcl->type), 0) != SUCCESS) {
zend_ffi_cleanup_dcl(dcl);
LONGJMP(FFI_G(bailout), FAILURE);
}
}
/* }}} */
static int zend_ffi_subst_type(zend_ffi_type **dcl, zend_ffi_type *type) /* {{{ */
{
zend_ffi_type *dcl_type;
zend_ffi_field *field;
if (*dcl == type) {
*dcl = ZEND_FFI_TYPE_MAKE_OWNED(type);
return 1;
}
dcl_type = *dcl;
switch (dcl_type->kind) {
case ZEND_FFI_TYPE_POINTER:
return zend_ffi_subst_type(&dcl_type->pointer.type, type);
case ZEND_FFI_TYPE_ARRAY:
return zend_ffi_subst_type(&dcl_type->array.type, type);
case ZEND_FFI_TYPE_FUNC:
if (zend_ffi_subst_type(&dcl_type->func.ret_type, type)) {
return 1;
}
if (dcl_type->func.args) {
zval *zv;
ZEND_HASH_FOREACH_VAL(dcl_type->func.args, zv) {
if (zend_ffi_subst_type((zend_ffi_type**)&Z_PTR_P(zv), type)) {
return 1;
}
} ZEND_HASH_FOREACH_END();
}
break;
case ZEND_FFI_TYPE_STRUCT:
ZEND_HASH_FOREACH_PTR(&dcl_type->record.fields, field) {
if (zend_ffi_subst_type(&field->type, type)) {
return 1;
}
} ZEND_HASH_FOREACH_END();
break;
default:
break;
}
return 0;
} /* }}} */
static void zend_ffi_tags_cleanup(zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_tag *tag;
ZEND_HASH_FOREACH_PTR(FFI_G(tags), tag) {
if (ZEND_FFI_TYPE_IS_OWNED(tag->type)) {
zend_ffi_type *type = ZEND_FFI_TYPE(tag->type);
zend_ffi_subst_type(&dcl->type, type);
tag->type = type;
}
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(FFI_G(tags));
efree(FFI_G(tags));
}
/* }}} */
ZEND_METHOD(FFI, new) /* {{{ */
{
zend_string *type_def = NULL;
zval *ztype = NULL;
zend_ffi_type *type, *type_ptr;
zend_ffi_cdata *cdata;
void *ptr;
zend_bool owned = 1;
zend_bool persistent = 0;
zend_bool is_const = 0;
zend_ffi_flags flags = ZEND_FFI_FLAG_OWNED;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 3)
if (Z_TYPE_P(EX_VAR_NUM(0)) == IS_STRING) {
Z_PARAM_STR(type_def)
} else {
Z_PARAM_OBJECT_OF_CLASS(ztype, zend_ffi_ctype_ce)
}
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(owned)
Z_PARAM_BOOL(persistent)
ZEND_PARSE_PARAMETERS_END();
if (!owned) {
flags &= ~ZEND_FFI_FLAG_OWNED;
}
if (persistent) {
flags |= ZEND_FFI_FLAG_PERSISTENT;
}
if (type_def) {
zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
if (Z_TYPE(EX(This)) == IS_OBJECT) {
zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This));
FFI_G(symbols) = ffi->symbols;
FFI_G(tags) = ffi->tags;
} else {
FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;
}
FFI_G(default_type_attr) = 0;
if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) != SUCCESS) {
zend_ffi_type_dtor(dcl.type);
if (Z_TYPE(EX(This)) != IS_OBJECT) {
if (FFI_G(tags)) {
zend_hash_destroy(FFI_G(tags));
efree(FFI_G(tags));
FFI_G(tags) = NULL;
}
if (FFI_G(symbols)) {
zend_hash_destroy(FFI_G(symbols));
efree(FFI_G(symbols));
FFI_G(symbols) = NULL;
}
}
return;
}
type = ZEND_FFI_TYPE(dcl.type);
if (dcl.attr & ZEND_FFI_ATTR_CONST) {
is_const = 1;
}
if (Z_TYPE(EX(This)) != IS_OBJECT) {
if (FFI_G(tags)) {
zend_ffi_tags_cleanup(&dcl);
}
if (FFI_G(symbols)) {
zend_hash_destroy(FFI_G(symbols));
efree(FFI_G(symbols));
FFI_G(symbols) = NULL;
}
}
FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;
type_ptr = dcl.type;
} else {
zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(ztype);
type_ptr = type = ctype->type;
if (ZEND_FFI_TYPE_IS_OWNED(type)) {
type = ZEND_FFI_TYPE(type);
if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
if (GC_REFCOUNT(&ctype->std) == 1) {
/* transfer type ownership */
ctype->type = type;
} else {
ctype->type = type_ptr = type = zend_ffi_remember_type(type);
}
}
}
}
if (type->size == 0) {
zend_throw_error(zend_ffi_exception_ce, "Cannot instantiate FFI\\CData of zero size");
zend_ffi_type_dtor(type_ptr);
return;
}
ptr = pemalloc(type->size, flags & ZEND_FFI_FLAG_PERSISTENT);
memset(ptr, 0, type->size);
cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
if (type->kind < ZEND_FFI_TYPE_POINTER) {
cdata->std.handlers = &zend_ffi_cdata_value_handlers;
}
cdata->type = type_ptr;
cdata->ptr = ptr;
cdata->flags = flags;
if (is_const) {
cdata->flags |= ZEND_FFI_FLAG_CONST;
}
RETURN_OBJ(&cdata->std);
}
/* }}} */
ZEND_METHOD(FFI, free) /* {{{ */
{
zval *zv;
zend_ffi_cdata *cdata;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_OBJECT_OF_CLASS_EX2(zv, zend_ffi_cdata_ce, 0, 1, 0);
ZEND_PARSE_PARAMETERS_END();
cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
if (ZEND_FFI_TYPE(cdata->type)->kind == ZEND_FFI_TYPE_POINTER) {
if (!cdata->ptr) {
zend_throw_error(zend_ffi_exception_ce, "NULL pointer dereference");
return;
}
if (cdata->ptr != (void*)&cdata->ptr_holder) {
pefree(*(void**)cdata->ptr, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
} else {
pefree(cdata->ptr_holder, (cdata->flags & ZEND_FFI_FLAG_PERSISTENT) || !is_zend_ptr(cdata->ptr_holder));
}
*(void**)cdata->ptr = NULL;
} else if (!(cdata->flags & ZEND_FFI_FLAG_OWNED)) {
pefree(cdata->ptr, cdata->flags & ZEND_FFI_FLAG_PERSISTENT);
cdata->ptr = NULL;
cdata->flags &= ~(ZEND_FFI_FLAG_OWNED|ZEND_FFI_FLAG_PERSISTENT);
cdata->std.handlers = &zend_ffi_cdata_free_handlers;
} else {
zend_throw_error(zend_ffi_exception_ce, "free() non a C pointer");
}
}
/* }}} */
ZEND_METHOD(FFI, cast) /* {{{ */
{
zend_string *type_def = NULL;
zval *ztype = NULL;
zend_ffi_type *old_type, *type, *type_ptr;
zend_ffi_cdata *old_cdata, *cdata;
zend_bool is_const = 0;
zval *zv, *arg;
void *ptr;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(2, 2)
if (Z_TYPE_P(EX_VAR_NUM(0)) == IS_STRING) {
Z_PARAM_STR(type_def)
} else {
Z_PARAM_OBJECT_OF_CLASS(ztype, zend_ffi_ctype_ce)
}
Z_PARAM_ZVAL(zv);
ZEND_PARSE_PARAMETERS_END();
arg = zv;
ZVAL_DEREF(zv);
if (type_def) {
zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
if (Z_TYPE(EX(This)) == IS_OBJECT) {
zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This));
FFI_G(symbols) = ffi->symbols;
FFI_G(tags) = ffi->tags;
} else {
FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;
}
FFI_G(default_type_attr) = 0;
if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) != SUCCESS) {
zend_ffi_type_dtor(dcl.type);
if (Z_TYPE(EX(This)) != IS_OBJECT) {
if (FFI_G(tags)) {
zend_hash_destroy(FFI_G(tags));
efree(FFI_G(tags));
FFI_G(tags) = NULL;
}
if (FFI_G(symbols)) {
zend_hash_destroy(FFI_G(symbols));
efree(FFI_G(symbols));
FFI_G(symbols) = NULL;
}
}
return;
}
type = ZEND_FFI_TYPE(dcl.type);
if (dcl.attr & ZEND_FFI_ATTR_CONST) {
is_const = 1;
}
if (Z_TYPE(EX(This)) != IS_OBJECT) {
if (FFI_G(tags)) {
zend_ffi_tags_cleanup(&dcl);
}
if (FFI_G(symbols)) {
zend_hash_destroy(FFI_G(symbols));
efree(FFI_G(symbols));
FFI_G(symbols) = NULL;
}
}
FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;
type_ptr = dcl.type;
} else {
zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(ztype);
type_ptr = type = ctype->type;
if (ZEND_FFI_TYPE_IS_OWNED(type)) {
type = ZEND_FFI_TYPE(type);
if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
if (GC_REFCOUNT(&ctype->std) == 1) {
/* transfer type ownership */
ctype->type = type;
} else {
ctype->type = type_ptr = type = zend_ffi_remember_type(type);
}
}
}
}
if (Z_TYPE_P(zv) != IS_OBJECT || Z_OBJCE_P(zv) != zend_ffi_cdata_ce) {
if (type->kind < ZEND_FFI_TYPE_POINTER && Z_TYPE_P(zv) < IS_STRING) {
/* numeric conversion */
cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
cdata->std.handlers = &zend_ffi_cdata_value_handlers;
cdata->type = type_ptr;
cdata->ptr = emalloc(type->size);
zend_ffi_zval_to_cdata(cdata->ptr, type, zv);
cdata->flags = ZEND_FFI_FLAG_OWNED;
if (is_const) {
cdata->flags |= ZEND_FFI_FLAG_CONST;
}
RETURN_OBJ(&cdata->std);
} else if (type->kind == ZEND_FFI_TYPE_POINTER && Z_TYPE_P(zv) == IS_LONG) {
/* number to pointer conversion */
cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
cdata->type = type_ptr;
cdata->ptr = &cdata->ptr_holder;
cdata->ptr_holder = (void*)(intptr_t)Z_LVAL_P(zv);
if (is_const) {
cdata->flags |= ZEND_FFI_FLAG_CONST;
}
RETURN_OBJ(&cdata->std);
} else if (type->kind == ZEND_FFI_TYPE_POINTER && Z_TYPE_P(zv) == IS_NULL) {
/* null -> pointer */
cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
cdata->type = type_ptr;
cdata->ptr = &cdata->ptr_holder;
cdata->ptr_holder = NULL;
if (is_const) {
cdata->flags |= ZEND_FFI_FLAG_CONST;
}
RETURN_OBJ(&cdata->std);
} else {
zend_wrong_parameter_class_error(2, "FFI\\CData", zv);
return;
}
}
old_cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
old_type = ZEND_FFI_TYPE(old_cdata->type);
ptr = old_cdata->ptr;
cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
if (type->kind < ZEND_FFI_TYPE_POINTER) {
cdata->std.handlers = &zend_ffi_cdata_value_handlers;
}
cdata->type = type_ptr;
if (old_type->kind == ZEND_FFI_TYPE_POINTER
&& type->kind != ZEND_FFI_TYPE_POINTER
&& ZEND_FFI_TYPE(old_type->pointer.type)->kind == ZEND_FFI_TYPE_VOID) {
/* automatically dereference void* pointers ??? */
cdata->ptr = *(void**)ptr;
} else if (old_type->kind == ZEND_FFI_TYPE_ARRAY
&& type->kind == ZEND_FFI_TYPE_POINTER) {
cdata->ptr = &cdata->ptr_holder;
cdata->ptr_holder = old_cdata->ptr;
} else if (type->size > old_type->size) {
zend_object_release(&cdata->std);
zend_throw_error(zend_ffi_exception_ce, "attempt to cast to larger type");
return;
} else if (ptr != &old_cdata->ptr_holder) {
cdata->ptr = ptr;
} else {
cdata->ptr = &cdata->ptr_holder;
cdata->ptr_holder = old_cdata->ptr_holder;
}
if (is_const) {
cdata->flags |= ZEND_FFI_FLAG_CONST;
}
if (old_cdata->flags & ZEND_FFI_FLAG_OWNED) {
if (GC_REFCOUNT(&old_cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1) {
/* transfer ownership */
old_cdata->flags &= ~ZEND_FFI_FLAG_OWNED;
cdata->flags |= ZEND_FFI_FLAG_OWNED;
} else {
//???zend_throw_error(zend_ffi_exception_ce, "Attempt to cast owned C pointer");
}
}
RETURN_OBJ(&cdata->std);
}
/* }}} */
ZEND_METHOD(FFI, type) /* {{{ */
{
zend_ffi_ctype *ctype;
zend_ffi_dcl dcl = ZEND_FFI_ATTR_INIT;
zend_string *type_def;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(type_def);
ZEND_PARSE_PARAMETERS_END();
if (Z_TYPE(EX(This)) == IS_OBJECT) {
zend_ffi *ffi = (zend_ffi*)Z_OBJ(EX(This));
FFI_G(symbols) = ffi->symbols;
FFI_G(tags) = ffi->tags;
} else {
FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;
}
FFI_G(default_type_attr) = 0;
if (zend_ffi_parse_type(ZSTR_VAL(type_def), ZSTR_LEN(type_def), &dcl) != SUCCESS) {
zend_ffi_type_dtor(dcl.type);
if (Z_TYPE(EX(This)) != IS_OBJECT) {
if (FFI_G(tags)) {
zend_hash_destroy(FFI_G(tags));
efree(FFI_G(tags));
FFI_G(tags) = NULL;
}
if (FFI_G(symbols)) {
zend_hash_destroy(FFI_G(symbols));
efree(FFI_G(symbols));
FFI_G(symbols) = NULL;
}
}
return;
}
if (Z_TYPE(EX(This)) != IS_OBJECT) {
if (FFI_G(tags)) {
zend_ffi_tags_cleanup(&dcl);
}
if (FFI_G(symbols)) {
zend_hash_destroy(FFI_G(symbols));
efree(FFI_G(symbols));
FFI_G(symbols) = NULL;
}
}
FFI_G(symbols) = NULL;
FFI_G(tags) = NULL;
ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
ctype->type = dcl.type;
RETURN_OBJ(&ctype->std);
}
/* }}} */
ZEND_METHOD(FFI, typeof) /* {{{ */
{
zval *zv, *arg;
zend_ffi_ctype *ctype;
zend_ffi_type *type;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ZVAL(zv);
ZEND_PARSE_PARAMETERS_END();
arg = zv;
ZVAL_DEREF(zv);
if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
type = cdata->type;
if (ZEND_FFI_TYPE_IS_OWNED(type)) {
type = ZEND_FFI_TYPE(type);
if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1) {
/* transfer type ownership */
cdata->type = type;
type = ZEND_FFI_TYPE_MAKE_OWNED(type);
} else {
cdata->type = type = zend_ffi_remember_type(type);
}
}
}
} else {
zend_wrong_parameter_class_error(1, "FFI\\CData", zv);
return;
}
ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
ctype->type = type;
RETURN_OBJ(&ctype->std);
}
/* }}} */
ZEND_METHOD(FFI, arrayType) /* {{{ */
{
zval *ztype;
zend_ffi_ctype *ctype;
zend_ffi_type *type;
HashTable *dims;
zval *val;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_OBJECT_OF_CLASS(ztype, zend_ffi_ctype_ce)
Z_PARAM_ARRAY_HT(dims)
ZEND_PARSE_PARAMETERS_END();
ctype = (zend_ffi_ctype*)Z_OBJ_P(ztype);
type = ZEND_FFI_TYPE(ctype->type);
if (type->kind == ZEND_FFI_TYPE_FUNC) {
zend_throw_error(zend_ffi_exception_ce, "array of functions is not allowed");
return;
} else if (type->kind == ZEND_FFI_TYPE_ARRAY && (type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
zend_throw_error(zend_ffi_exception_ce, "only the leftmost array can be undimensioned");
return;
} else if (type->kind == ZEND_FFI_TYPE_VOID) {
zend_throw_error(zend_ffi_exception_ce, "array of 'void' is not allowed");
return;
} else if (type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG) {
zend_throw_error(zend_ffi_exception_ce, "array of incomplete type is not allowed");
return;
}
if (ZEND_FFI_TYPE_IS_OWNED(ctype->type)) {
if (!(type->attr & ZEND_FFI_ATTR_STORED)) {
if (GC_REFCOUNT(&ctype->std) == 1) {
/* transfer type ownership */
ctype->type = type;
type = ZEND_FFI_TYPE_MAKE_OWNED(type);
} else {
ctype->type = type = zend_ffi_remember_type(type);
}
}
}
ZEND_HASH_REVERSE_FOREACH_VAL(dims, val) {
zend_long n = zval_get_long(val);
zend_ffi_type *new_type;
if (n < 0) {
zend_throw_error(zend_ffi_exception_ce, "negative array index");
zend_ffi_type_dtor(type);
return;
} else if (ZEND_FFI_TYPE(type)->kind == ZEND_FFI_TYPE_ARRAY && (ZEND_FFI_TYPE(type)->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
zend_throw_error(zend_ffi_exception_ce, "only the leftmost array can be undimensioned");
zend_ffi_type_dtor(type);
return;
}
new_type = emalloc(sizeof(zend_ffi_type));
new_type->kind = ZEND_FFI_TYPE_ARRAY;
new_type->attr = 0;
new_type->size = n * ZEND_FFI_TYPE(type)->size;
new_type->align = ZEND_FFI_TYPE(type)->align;
new_type->array.type = type;
new_type->array.length = n;
if (n == 0) {
new_type->attr |= ZEND_FFI_ATTR_INCOMPLETE_ARRAY;
}
type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
} ZEND_HASH_FOREACH_END();
ctype = (zend_ffi_ctype*)zend_ffi_ctype_new(zend_ffi_ctype_ce);
ctype->type = type;
RETURN_OBJ(&ctype->std);
}
/* }}} */
ZEND_METHOD(FFI, addr) /* {{{ */
{
zend_ffi_type *type, *new_type;
zend_ffi_cdata *cdata, *new_cdata;
zval *zv, *arg;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ZVAL(zv)
ZEND_PARSE_PARAMETERS_END();
arg = zv;
ZVAL_DEREF(zv);
if (Z_TYPE_P(zv) != IS_OBJECT || Z_OBJCE_P(zv) != zend_ffi_cdata_ce) {
zend_wrong_parameter_class_error(1, "FFI\\CData", zv);
return;
}
cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
type = ZEND_FFI_TYPE(cdata->type);
new_type = emalloc(sizeof(zend_ffi_type));
new_type->kind = ZEND_FFI_TYPE_POINTER;
new_type->attr = 0;
new_type->size = sizeof(void*);
new_type->align = _Alignof(void*);
/* life-time (source must relive the resulting pointer) ??? */
new_type->pointer.type = type;
new_cdata = (zend_ffi_cdata*)zend_ffi_cdata_new(zend_ffi_cdata_ce);
new_cdata->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
new_cdata->ptr_holder = cdata->ptr;
new_cdata->ptr = &new_cdata->ptr_holder;
if (GC_REFCOUNT(&cdata->std) == 1 && Z_REFCOUNT_P(arg) == 1) {
if (ZEND_FFI_TYPE_IS_OWNED(cdata->type)) {
/* transfer type ownership */
cdata->type = type;
new_type->pointer.type = ZEND_FFI_TYPE_MAKE_OWNED(type);
}
if (cdata->flags & ZEND_FFI_FLAG_OWNED) {
/* transfer ownership */
cdata->flags &= ~ZEND_FFI_FLAG_OWNED;
new_cdata->flags |= ZEND_FFI_FLAG_OWNED;
}
}
RETURN_OBJ(&new_cdata->std);
}
/* }}} */
ZEND_METHOD(FFI, sizeof) /* {{{ */
{
zval *zv;
zend_ffi_type *type;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ZVAL(zv);
ZEND_PARSE_PARAMETERS_END();
ZVAL_DEREF(zv);
if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
type = ZEND_FFI_TYPE(cdata->type);
} else if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_ctype_ce) {
zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(zv);
type = ZEND_FFI_TYPE(ctype->type);
} else {
zend_wrong_parameter_class_error(1, "FFI\\CData or FFI\\CType", zv);
return;
}
RETURN_LONG(type->size);
}
/* }}} */
ZEND_METHOD(FFI, alignof) /* {{{ */
{
zval *zv;
zend_ffi_type *type;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ZVAL(zv);
ZEND_PARSE_PARAMETERS_END();
ZVAL_DEREF(zv);
if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_cdata_ce) {
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
type = ZEND_FFI_TYPE(cdata->type);
} else if (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zend_ffi_ctype_ce) {
zend_ffi_ctype *ctype = (zend_ffi_ctype*)Z_OBJ_P(zv);
type = ZEND_FFI_TYPE(ctype->type);
} else {
zend_wrong_parameter_class_error(1, "FFI\\CData or FFI\\CType", zv);
return;
}
RETURN_LONG(type->align);
}
/* }}} */
ZEND_METHOD(FFI, memcpy) /* {{{ */
{
zval *zv1, *zv2;
zend_ffi_cdata *cdata1, *cdata2;
zend_ffi_type *type1, *type2;
void *ptr1, *ptr2;
zend_long size;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(3, 3)
Z_PARAM_OBJECT_OF_CLASS_EX2(zv1, zend_ffi_cdata_ce, 0, 1, 0);
Z_PARAM_ZVAL(zv2)
Z_PARAM_LONG(size)
ZEND_PARSE_PARAMETERS_END();
cdata1 = (zend_ffi_cdata*)Z_OBJ_P(zv1);
type1 = ZEND_FFI_TYPE(cdata1->type);
if (type1->kind == ZEND_FFI_TYPE_POINTER) {
ptr1 = *(void**)cdata1->ptr;
} else {
ptr1 = cdata1->ptr;
if (type1->kind != ZEND_FFI_TYPE_POINTER && size > type1->size) {
zend_throw_error(zend_ffi_exception_ce, "attempt to write over data boundary");
return;
}
}
ZVAL_DEREF(zv2);
if (Z_TYPE_P(zv2) == IS_STRING) {
ptr2 = Z_STRVAL_P(zv2);
if (size > Z_STRLEN_P(zv2)) {
zend_throw_error(zend_ffi_exception_ce, "attempt to read over string boundary");
return;
}
} else if (Z_TYPE_P(zv2) == IS_OBJECT && Z_OBJCE_P(zv2) == zend_ffi_cdata_ce) {
cdata2 = (zend_ffi_cdata*)Z_OBJ_P(zv2);
type2 = ZEND_FFI_TYPE(cdata2->type);
if (type2->kind == ZEND_FFI_TYPE_POINTER) {
ptr2 = *(void**)cdata2->ptr;
} else {
ptr2 = cdata2->ptr;
if (type2->kind != ZEND_FFI_TYPE_POINTER && size > type2->size) {
zend_throw_error(zend_ffi_exception_ce, "attempt to read over data boundary");
return;
}
}
} else {
zend_wrong_parameter_class_error(2, "FFI\\CData or string", zv2);
return;
}
memcpy(ptr1, ptr2, size);
}
/* }}} */
ZEND_METHOD(FFI, memcmp) /* {{{ */
{
zval *zv1, *zv2;
zend_ffi_cdata *cdata1, *cdata2;
zend_ffi_type *type1, *type2;
void *ptr1, *ptr2;
zend_long size;
int ret;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(3, 3)
Z_PARAM_ZVAL(zv1);
Z_PARAM_ZVAL(zv2);
Z_PARAM_LONG(size)
ZEND_PARSE_PARAMETERS_END();
ZVAL_DEREF(zv1);
if (Z_TYPE_P(zv1) == IS_STRING) {
ptr1 = Z_STRVAL_P(zv1);
if (size > Z_STRLEN_P(zv1)) {
zend_throw_error(zend_ffi_exception_ce, "attempt to read over string boundary");
return;
}
} else if (Z_TYPE_P(zv1) == IS_OBJECT && Z_OBJCE_P(zv1) == zend_ffi_cdata_ce) {
cdata1 = (zend_ffi_cdata*)Z_OBJ_P(zv1);
type1 = ZEND_FFI_TYPE(cdata1->type);
if (type1->kind == ZEND_FFI_TYPE_POINTER) {
ptr1 = *(void**)cdata1->ptr;
} else {
ptr1 = cdata1->ptr;
if (type1->kind != ZEND_FFI_TYPE_POINTER && size > type1->size) {
zend_throw_error(zend_ffi_exception_ce, "attempt to read over data boundary");
return;
}
}
} else {
zend_wrong_parameter_class_error(1, "FFI\\CData or string", zv1);
return;
}
ZVAL_DEREF(zv2);
if (Z_TYPE_P(zv2) == IS_STRING) {
ptr2 = Z_STRVAL_P(zv2);
if (size > Z_STRLEN_P(zv2)) {
zend_throw_error(zend_ffi_exception_ce, "attempt to read over string boundary");
return;
}
} else if (Z_TYPE_P(zv2) == IS_OBJECT && Z_OBJCE_P(zv2) == zend_ffi_cdata_ce) {
cdata2 = (zend_ffi_cdata*)Z_OBJ_P(zv2);
type2 = ZEND_FFI_TYPE(cdata2->type);
if (type2->kind == ZEND_FFI_TYPE_POINTER) {
ptr2 = *(void**)cdata2->ptr;
} else {
ptr2 = cdata2->ptr;
if (type2->kind != ZEND_FFI_TYPE_POINTER && size > type2->size) {
zend_throw_error(zend_ffi_exception_ce, "attempt to read over data boundary");
return;
}
}
} else {
zend_wrong_parameter_class_error(2, "FFI\\CData or string", zv2);
return;
}
ret = memcmp(ptr1, ptr2, size);
if (ret == 0) {
RETVAL_LONG(0);
} else if (ret < 0) {
RETVAL_LONG(-1);
} else {
RETVAL_LONG(1);
}
}
/* }}} */
ZEND_METHOD(FFI, memset) /* {{{ */
{
zval *zv;
zend_ffi_cdata *cdata;
zend_ffi_type *type;
void *ptr;
zend_long ch, size;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(3, 3)
Z_PARAM_OBJECT_OF_CLASS_EX2(zv, zend_ffi_cdata_ce, 0, 1, 0);
Z_PARAM_LONG(ch)
Z_PARAM_LONG(size)
ZEND_PARSE_PARAMETERS_END();
cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
type = ZEND_FFI_TYPE(cdata->type);
if (type->kind == ZEND_FFI_TYPE_POINTER) {
ptr = *(void**)cdata->ptr;
} else {
ptr = cdata->ptr;
if (type->kind != ZEND_FFI_TYPE_POINTER && size > type->size) {
zend_throw_error(zend_ffi_exception_ce, "attempt to write over data boundary");
return;
}
}
memset(ptr, ch, size);
}
/* }}} */
ZEND_METHOD(FFI, string) /* {{{ */
{
zval *zv;
zend_ffi_cdata *cdata;
zend_ffi_type *type;
void *ptr;
zend_long size = 0;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_OBJECT_OF_CLASS_EX2(zv, zend_ffi_cdata_ce, 0, 1, 0);
Z_PARAM_OPTIONAL
Z_PARAM_LONG(size)
ZEND_PARSE_PARAMETERS_END();
cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
type = ZEND_FFI_TYPE(cdata->type);
if (EX_NUM_ARGS() == 2) {
if (type->kind == ZEND_FFI_TYPE_POINTER) {
ptr = *(void**)cdata->ptr;
} else {
ptr = cdata->ptr;
if (type->kind != ZEND_FFI_TYPE_POINTER && size > type->size) {
zend_throw_error(zend_ffi_exception_ce, "attempt to read over data boundary");
return;
}
}
RETURN_STRINGL((char*)ptr, size);
} else {
if (type->kind == ZEND_FFI_TYPE_POINTER && ZEND_FFI_TYPE(type->pointer.type)->kind == ZEND_FFI_TYPE_CHAR) {
ptr = *(void**)cdata->ptr;
} else if (type->kind == ZEND_FFI_TYPE_ARRAY && ZEND_FFI_TYPE(type->array.type)->kind == ZEND_FFI_TYPE_CHAR) {
ptr = cdata->ptr;
} else {
zend_throw_error(zend_ffi_exception_ce, "FFI\\Cdata is not a C string");
return;
}
RETURN_STRING((char*)ptr);
}
}
/* }}} */
ZEND_METHOD(FFI, isNull) /* {{{ */
{
zval *zv;
zend_ffi_cdata *cdata;
zend_ffi_type *type;
ZEND_FFI_VALIDATE_API_RESTRICTION();
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_ZVAL(zv);
ZEND_PARSE_PARAMETERS_END();
ZVAL_DEREF(zv);
if (Z_TYPE_P(zv) != IS_OBJECT || Z_OBJCE_P(zv) != zend_ffi_cdata_ce) {
zend_wrong_parameter_class_error(1, "FFI\\CData", zv);
return;
}
cdata = (zend_ffi_cdata*)Z_OBJ_P(zv);
type = ZEND_FFI_TYPE(cdata->type);
if (type->kind != ZEND_FFI_TYPE_POINTER){
zend_throw_error(zend_ffi_exception_ce, "FFI\\Cdata is not a pointer");
return;
}
RETURN_BOOL(*(void**)cdata->ptr == NULL);
}
/* }}} */
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_cdef, 0, 0, 0)
ZEND_ARG_INFO(0, code)
ZEND_ARG_INFO(0, lib)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_load, 0, 0, 1)
ZEND_ARG_INFO(0, filename)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_scope, 0, 0, 1)
ZEND_ARG_INFO(0, scope_name)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_new, 0, 0, 1)
ZEND_ARG_INFO(0, type)
ZEND_ARG_INFO(0, owned)
ZEND_ARG_INFO(0, persistent)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_free, 0, 0, 1)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_cast, 0, 0, 2)
ZEND_ARG_INFO(0, type)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_type, 0, 0, 1)
ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_typeof, 0, 0, 1)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_array, 0, 0, 2)
ZEND_ARG_INFO(0, type)
ZEND_ARG_INFO(0, dims)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_addr, 0, 0, 1)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_sizeof, 0, 0, 1)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_alignof, 0, 0, 1)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_memcpy, 0, 0, 3)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, dst)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, src)
ZEND_ARG_INFO(0, size)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_memcmp, 0, 0, 3)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr1)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr2)
ZEND_ARG_INFO(0, size)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_memset, 0, 0, 3)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
ZEND_ARG_INFO(0, ch)
ZEND_ARG_INFO(0, size)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_string, 0, 0, 1)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
ZEND_ARG_INFO(0, size)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_func_isnull, 0, 0, 1)
ZEND_ARG_INFO(ZEND_SEND_PREFER_REF, ptr)
ZEND_END_ARG_INFO()
static const zend_function_entry zend_ffi_functions[] = {
ZEND_ME(FFI, cdef, arginfo_func_cdef, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, load, arginfo_func_load, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, scope, arginfo_func_scope, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, new, arginfo_func_new, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, free, arginfo_func_free, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, cast, arginfo_func_cast, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, type, arginfo_func_type, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, typeof, arginfo_func_typeof, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, arrayType, arginfo_func_array, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, addr, arginfo_func_addr, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, sizeof, arginfo_func_sizeof, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, alignof, arginfo_func_alignof, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, memcpy, arginfo_func_memcpy, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, memcmp, arginfo_func_memcmp, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, memset, arginfo_func_memset, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, string, arginfo_func_string, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(FFI, isNull, arginfo_func_isnull, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_FE_END
};
static char *zend_ffi_parse_directives(const char *filename, char *code_pos, char **scope_name, char **lib, zend_bool preload) /* {{{ */
{
char *p;
*scope_name = NULL;
*lib = NULL;
while (*code_pos == '#') {
if (strncmp(code_pos, "#define FFI_SCOPE", sizeof("#define FFI_SCOPE") - 1) == 0
&& (code_pos[sizeof("#define FFI_SCOPE") - 1] == ' '
|| code_pos[sizeof("#define FFI_SCOPE") - 1] == '\t')) {
p = code_pos + sizeof("#define FFI_SCOPE");
while (*p == ' ' || *p == '\t') {
p++;
}
if (*p != '"') {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
}
return NULL;
}
p++;
if (*scope_name) {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_SCOPE defined twice", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_SCOPE defined twice", filename);
}
return NULL;
}
*scope_name = p;
while (1) {
if (*p == '\"') {
*p = 0;
p++;
break;
} else if (*p <= ' ') {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_SCOPE define", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_SCOPE define", filename);
}
return NULL;
}
p++;
}
while (*p == ' ' || *p == '\t') {
p++;
}
while (*p == '\r' || *p == '\n') {
p++;
}
code_pos = p;
} else if (strncmp(code_pos, "#define FFI_LIB", sizeof("#define FFI_LIB") - 1) == 0
&& (code_pos[sizeof("#define FFI_LIB") - 1] == ' '
|| code_pos[sizeof("#define FFI_LIB") - 1] == '\t')) {
p = code_pos + sizeof("#define FFI_LIB");
while (*p == ' ' || *p == '\t') {
p++;
}
if (*p != '"') {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename);
}
return NULL;
}
p++;
if (*lib) {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', FFI_LIB defined twice", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', FFI_LIB defined twice", filename);
}
return NULL;
}
*lib = p;
while (1) {
if (*p == '\"') {
*p = 0;
p++;
break;
} else if (*p <= ' ') {
if (preload) {
zend_error(E_WARNING, "FFI: failed pre-loading '%s', bad FFI_LIB define", filename);
} else {
zend_throw_error(zend_ffi_exception_ce, "Failed loading '%s', bad FFI_LIB define", filename);
}
return NULL;
}
p++;
}
while (*p == ' ' || *p == '\t') {
p++;
}
while (*p == '\r' || *p == '\n') {
p++;
}
code_pos = p;
} else {
break;
}
}
return code_pos;
}
/* }}} */
static ZEND_COLD zend_function *zend_fake_get_constructor(zend_object *object) /* {{{ */
{
zend_throw_error(NULL, "Instantiation of '%s' is not allowed", ZSTR_VAL(object->ce->name));
return NULL;
}
/* }}} */
static ZEND_COLD zend_never_inline void zend_bad_array_access(zend_class_entry *ce) /* {{{ */
{
zend_throw_error(NULL, "Cannot use object of type %s as array", ZSTR_VAL(ce->name));
}
/* }}} */
static ZEND_COLD zval *zend_fake_read_dimension(zval *object, zval *offset, int type, zval *rv) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
zend_bad_array_access(ce);
return NULL;
}
/* }}} */
static ZEND_COLD void zend_fake_write_dimension(zval *object, zval *offset, zval *value) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
zend_bad_array_access(ce);
}
/* }}} */
static ZEND_COLD int zend_fake_has_dimension(zval *object, zval *offset, int check_empty) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
zend_bad_array_access(ce);
return 0;
}
/* }}} */
static ZEND_COLD void zend_fake_unset_dimension(zval *object, zval *offset) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
zend_bad_array_access(ce);
}
/* }}} */
static ZEND_COLD zend_never_inline void zend_bad_property_access(zend_class_entry *ce) /* {{{ */
{
zend_throw_error(NULL, "Cannot access property of object of type %s", ZSTR_VAL(ce->name));
}
/* }}} */
static ZEND_COLD zval *zend_fake_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
zend_bad_property_access(ce);
return &EG(uninitialized_zval);
}
/* }}} */
static ZEND_COLD zval *zend_fake_write_property(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
zend_bad_array_access(ce);
return value;
}
/* }}} */
static ZEND_COLD int zend_fake_has_property(zval *object, zval *member, int has_set_exists, void **cache_slot) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
zend_bad_array_access(ce);
return 0;
}
/* }}} */
static ZEND_COLD void zend_fake_unset_property(zval *object, zval *member, void **cache_slot) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(object);
zend_bad_array_access(ce);
}
/* }}} */
static zval *zend_fake_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) /* {{{ */
{
return NULL;
}
/* }}} */
static ZEND_COLD zend_function *zend_fake_get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key) /* {{{ */
{
zend_class_entry *ce = (*obj_ptr)->ce;
zend_throw_error(NULL, "Object of type %s does not support method calls", ZSTR_VAL(ce->name));
return NULL;
}
/* }}} */
static HashTable *zend_fake_get_properties(zval *object) /* {{{ */
{
return (HashTable*)&zend_empty_array;
}
/* }}} */
static HashTable *zend_fake_get_gc(zval *object, zval **table, int *n) /* {{{ */
{
*table = NULL;
*n = 0;
return NULL;
}
/* }}} */
static ZEND_COLD zend_never_inline void zend_ffi_use_after_free(void) /* {{{ */
{
zend_throw_error(zend_ffi_exception_ce, "Use after free()");
}
/* }}} */
static zend_object *zend_ffi_free_clone_obj(zval *zobject) /* {{{ */
{
zend_ffi_use_after_free();
return NULL;
}
/* }}} */
static ZEND_COLD zval *zend_ffi_free_read_dimension(zval *object, zval *offset, int type, zval *rv) /* {{{ */
{
zend_ffi_use_after_free();
return NULL;
}
/* }}} */
static ZEND_COLD void zend_ffi_free_write_dimension(zval *object, zval *offset, zval *value) /* {{{ */
{
zend_ffi_use_after_free();
}
/* }}} */
static ZEND_COLD int zend_ffi_free_has_dimension(zval *object, zval *offset, int check_empty) /* {{{ */
{
zend_ffi_use_after_free();
return 0;
}
/* }}} */
static ZEND_COLD void zend_ffi_free_unset_dimension(zval *object, zval *offset) /* {{{ */
{
zend_ffi_use_after_free();
}
/* }}} */
static ZEND_COLD zval *zend_ffi_free_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */
{
zend_ffi_use_after_free();
return &EG(uninitialized_zval);
}
/* }}} */
static ZEND_COLD zval *zend_ffi_free_write_property(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
{
zend_ffi_use_after_free();
return value;
}
/* }}} */
static ZEND_COLD int zend_ffi_free_has_property(zval *object, zval *member, int has_set_exists, void **cache_slot) /* {{{ */
{
zend_ffi_use_after_free();
return 0;
}
/* }}} */
static ZEND_COLD void zend_ffi_free_unset_property(zval *object, zval *member, void **cache_slot) /* {{{ */
{
zend_ffi_use_after_free();
}
/* }}} */
static zval* zend_ffi_free_get(zval *object, zval *rv) /* {{{ */
{
zend_ffi_use_after_free();
return NULL;
}
/* }}} */
static HashTable *zend_ffi_free_get_debug_info(zval *object, int *is_temp) /* {{{ */
{
zend_ffi_use_after_free();
return NULL;
}
/* }}} */
static ZEND_INI_MH(OnUpdateFFIEnable) /* {{{ */
{
if (zend_string_equals_literal_ci(new_value, "preload")) {
FFI_G(restriction) = ZEND_FFI_PRELOAD;
} else {
FFI_G(restriction) = (zend_ffi_api_restriction)zend_ini_parse_bool(new_value);
}
return SUCCESS;
}
/* }}} */
static ZEND_INI_DISP(zend_ffi_enable_displayer_cb) /* {{{ */
{
if (FFI_G(restriction) == ZEND_FFI_PRELOAD) {
ZEND_PUTS("preload");
} else if (FFI_G(restriction) == ZEND_FFI_ENABLED) {
ZEND_PUTS("On");
} else {
ZEND_PUTS("Off");
}
}
/* }}} */
ZEND_INI_BEGIN()
ZEND_INI_ENTRY3_EX("ffi.enable", "preload", ZEND_INI_SYSTEM, OnUpdateFFIEnable, NULL, NULL, NULL, zend_ffi_enable_displayer_cb)
STD_ZEND_INI_ENTRY("ffi.preload", NULL, ZEND_INI_SYSTEM, OnUpdateString, preload, zend_ffi_globals, ffi_globals)
ZEND_INI_END()
static int zend_ffi_preload_glob(const char *filename) /* {{{ */
{
#ifdef HAVE_GLOB
glob_t globbuf;
int ret;
unsigned int i;
memset(&globbuf, 0, sizeof(glob_t));
ret = glob(filename, 0, NULL, &globbuf);
#ifdef GLOB_NOMATCH
if (ret == GLOB_NOMATCH || !globbuf.gl_pathc) {
#else
if (!globbuf.gl_pathc) {
#endif
/* pass */
} else {
for(i=0 ; i<globbuf.gl_pathc; i++) {
zend_ffi *ffi = zend_ffi_load(globbuf.gl_pathv[i], 1);
if (!ffi) {
globfree(&globbuf);
return FAILURE;
}
efree(ffi);
}
globfree(&globbuf);
}
#else
zend_ffi *ffi = zend_ffi_load(filename, 1);
if (!ffi) {
return FAILURE;
}
efree(ffi);
#endif
return SUCCESS;
}
/* }}} */
static int zend_ffi_preload(char *preload) /* {{{ */
{
zend_ffi *ffi;
char *s = NULL, *e, *filename;
zend_bool is_glob = 0;
e = preload;
while (*e) {
switch (*e) {
case ZEND_PATHS_SEPARATOR:
if (s) {
filename = estrndup(s, e-s);
s = NULL;
if (!is_glob) {
ffi = zend_ffi_load(filename, 1);
efree(filename);
if (!ffi) {
return FAILURE;
}
efree(ffi);
} else {
int ret = zend_ffi_preload_glob(filename);
efree(filename);
if (ret != SUCCESS) {
return FAILURE;
}
is_glob = 0;
}
}
break;
case '*':
case '?':
case '[':
is_glob = 1;
break;
default:
if (!s) {
s = e;
}
break;
}
e++;
}
if (s) {
filename = estrndup(s, e-s);
if (!is_glob) {
ffi = zend_ffi_load(filename, 1);
efree(filename);
if (!ffi) {
return FAILURE;
}
efree(ffi);
} else {
int ret = zend_ffi_preload_glob(filename);
efree(filename);
if (ret != SUCCESS) {
return FAILURE;
}
}
}
return SUCCESS;
}
/* }}} */
/* {{{ ZEND_MINIT_FUNCTION
*/
ZEND_MINIT_FUNCTION(ffi)
{
zend_class_entry ce;
REGISTER_INI_ENTRIES();
FFI_G(is_cli) = strcmp(sapi_module.name, "cli") == 0;
INIT_NS_CLASS_ENTRY(ce, "FFI", "Exception", NULL);
zend_ffi_exception_ce = zend_register_internal_class_ex(&ce, zend_ce_error);
INIT_NS_CLASS_ENTRY(ce, "FFI", "ParserException", NULL);
zend_ffi_parser_exception_ce = zend_register_internal_class_ex(&ce, zend_ffi_exception_ce);
zend_ffi_parser_exception_ce->ce_flags |= ZEND_ACC_FINAL;
INIT_CLASS_ENTRY(ce, "FFI", zend_ffi_functions);
zend_ffi_ce = zend_register_internal_class(&ce);
zend_ffi_ce->ce_flags |= ZEND_ACC_FINAL;
zend_ffi_ce->create_object = zend_ffi_new;
zend_ffi_ce->serialize = zend_class_serialize_deny;
zend_ffi_ce->unserialize = zend_class_unserialize_deny;
memcpy(&zend_ffi_new_fn, zend_hash_str_find_ptr(&zend_ffi_ce->function_table, "new", sizeof("new")-1), sizeof(zend_internal_function));
zend_ffi_new_fn.fn_flags &= ~ZEND_ACC_STATIC;
memcpy(&zend_ffi_cast_fn, zend_hash_str_find_ptr(&zend_ffi_ce->function_table, "cast", sizeof("cast")-1), sizeof(zend_internal_function));
zend_ffi_cast_fn.fn_flags &= ~ZEND_ACC_STATIC;
memcpy(&zend_ffi_type_fn, zend_hash_str_find_ptr(&zend_ffi_ce->function_table, "type", sizeof("type")-1), sizeof(zend_internal_function));
zend_ffi_type_fn.fn_flags &= ~ZEND_ACC_STATIC;
memcpy(&zend_ffi_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
zend_ffi_handlers.get_constructor = zend_fake_get_constructor;
zend_ffi_handlers.free_obj = zend_ffi_free_obj;
zend_ffi_handlers.clone_obj = NULL;
zend_ffi_handlers.read_property = zend_ffi_read_var;
zend_ffi_handlers.write_property = zend_ffi_write_var;
zend_ffi_handlers.read_dimension = zend_fake_read_dimension;
zend_ffi_handlers.write_dimension = zend_fake_write_dimension;
zend_ffi_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
zend_ffi_handlers.has_property = zend_fake_has_property;
zend_ffi_handlers.unset_property = zend_fake_unset_property;
zend_ffi_handlers.has_dimension = zend_fake_has_dimension;
zend_ffi_handlers.unset_dimension = zend_fake_unset_dimension;
zend_ffi_handlers.get_method = zend_ffi_get_func;
zend_ffi_handlers.compare_objects = NULL;
zend_ffi_handlers.cast_object = NULL;
zend_ffi_handlers.get_debug_info = NULL;
zend_ffi_handlers.get_closure = NULL;
zend_ffi_handlers.get_properties = zend_fake_get_properties;
zend_ffi_handlers.get_gc = zend_fake_get_gc;
zend_declare_class_constant_long(zend_ffi_ce, "__BIGGEST_ALIGNMENT__", sizeof("__BIGGEST_ALIGNMENT__")-1, __BIGGEST_ALIGNMENT__);
INIT_NS_CLASS_ENTRY(ce, "FFI", "CData", NULL);
zend_ffi_cdata_ce = zend_register_internal_class(&ce);
zend_ffi_cdata_ce->ce_flags |= ZEND_ACC_FINAL;
zend_ffi_cdata_ce->create_object = zend_ffi_cdata_new;
zend_ffi_cdata_ce->get_iterator = zend_ffi_cdata_get_iterator;
zend_ffi_cdata_ce->serialize = zend_class_serialize_deny;
zend_ffi_cdata_ce->unserialize = zend_class_unserialize_deny;
memcpy(&zend_ffi_cdata_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
zend_ffi_cdata_handlers.get_constructor = zend_fake_get_constructor;
zend_ffi_cdata_handlers.free_obj = zend_ffi_cdata_free_obj;
zend_ffi_cdata_handlers.clone_obj = zend_ffi_cdata_clone_obj;
zend_ffi_cdata_handlers.read_property = zend_ffi_cdata_read_field;
zend_ffi_cdata_handlers.write_property = zend_ffi_cdata_write_field;
zend_ffi_cdata_handlers.read_dimension = zend_ffi_cdata_read_dim;
zend_ffi_cdata_handlers.write_dimension = zend_ffi_cdata_write_dim;
zend_ffi_cdata_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
zend_ffi_cdata_handlers.has_property = zend_fake_has_property;
zend_ffi_cdata_handlers.unset_property = zend_fake_unset_property;
zend_ffi_cdata_handlers.has_dimension = zend_fake_has_dimension;
zend_ffi_cdata_handlers.unset_dimension = zend_fake_unset_dimension;
zend_ffi_cdata_handlers.get_method = zend_fake_get_method;
zend_ffi_cdata_handlers.get_class_name = zend_ffi_cdata_get_class_name;
zend_ffi_cdata_handlers.do_operation = zend_ffi_cdata_do_operation;
zend_ffi_cdata_handlers.compare_objects = zend_ffi_cdata_compare_objects;
zend_ffi_cdata_handlers.cast_object = zend_ffi_cdata_cast_object;
zend_ffi_cdata_handlers.count_elements = zend_ffi_cdata_count_elements;
zend_ffi_cdata_handlers.get_debug_info = zend_ffi_cdata_get_debug_info;
zend_ffi_cdata_handlers.get_closure = zend_ffi_cdata_get_closure;
zend_ffi_cdata_handlers.get_properties = zend_fake_get_properties;
zend_ffi_cdata_handlers.get_gc = zend_fake_get_gc;
memcpy(&zend_ffi_cdata_value_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
zend_ffi_cdata_value_handlers.get_constructor = zend_fake_get_constructor;
zend_ffi_cdata_value_handlers.free_obj = zend_ffi_cdata_free_obj;
zend_ffi_cdata_value_handlers.clone_obj = zend_ffi_cdata_clone_obj;
zend_ffi_cdata_value_handlers.read_property = zend_ffi_cdata_get;
zend_ffi_cdata_value_handlers.write_property = zend_ffi_cdata_set;
zend_ffi_cdata_value_handlers.read_dimension = zend_fake_read_dimension;
zend_ffi_cdata_value_handlers.write_dimension = zend_fake_write_dimension;
zend_ffi_cdata_value_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
zend_ffi_cdata_value_handlers.has_property = zend_fake_has_property;
zend_ffi_cdata_value_handlers.unset_property = zend_fake_unset_property;
zend_ffi_cdata_value_handlers.has_dimension = zend_fake_has_dimension;
zend_ffi_cdata_value_handlers.unset_dimension = zend_fake_unset_dimension;
zend_ffi_cdata_value_handlers.get_method = zend_fake_get_method;
zend_ffi_cdata_value_handlers.get_class_name = zend_ffi_cdata_get_class_name;
zend_ffi_cdata_value_handlers.compare_objects = zend_ffi_cdata_compare_objects;
zend_ffi_cdata_value_handlers.cast_object = zend_ffi_cdata_cast_object;
zend_ffi_cdata_value_handlers.count_elements = NULL;
zend_ffi_cdata_value_handlers.get_debug_info = zend_ffi_cdata_get_debug_info;
zend_ffi_cdata_value_handlers.get_closure = NULL;
zend_ffi_cdata_value_handlers.get_properties = zend_fake_get_properties;
zend_ffi_cdata_value_handlers.get_gc = zend_fake_get_gc;
memcpy(&zend_ffi_cdata_free_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
zend_ffi_cdata_free_handlers.get_constructor = zend_fake_get_constructor;
zend_ffi_cdata_free_handlers.free_obj = zend_ffi_cdata_free_obj;
zend_ffi_cdata_free_handlers.clone_obj = zend_ffi_free_clone_obj;
zend_ffi_cdata_free_handlers.read_property = zend_ffi_free_read_property;
zend_ffi_cdata_free_handlers.write_property = zend_ffi_free_write_property;
zend_ffi_cdata_free_handlers.read_dimension = zend_ffi_free_read_dimension;
zend_ffi_cdata_free_handlers.write_dimension = zend_ffi_free_write_dimension;
zend_ffi_cdata_free_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
zend_ffi_cdata_free_handlers.get = zend_ffi_free_get;
zend_ffi_cdata_free_handlers.has_property = zend_ffi_free_has_property;
zend_ffi_cdata_free_handlers.unset_property = zend_ffi_free_unset_property;
zend_ffi_cdata_free_handlers.has_dimension = zend_ffi_free_has_dimension;
zend_ffi_cdata_free_handlers.unset_dimension = zend_ffi_free_unset_dimension;
zend_ffi_cdata_free_handlers.get_method = zend_fake_get_method;
zend_ffi_cdata_free_handlers.get_class_name = zend_ffi_cdata_get_class_name;
zend_ffi_cdata_free_handlers.compare_objects = zend_ffi_cdata_compare_objects;
zend_ffi_cdata_free_handlers.cast_object = NULL;
zend_ffi_cdata_free_handlers.count_elements = NULL;
zend_ffi_cdata_free_handlers.get_debug_info = zend_ffi_free_get_debug_info;
zend_ffi_cdata_free_handlers.get_closure = NULL;
zend_ffi_cdata_free_handlers.get_properties = zend_fake_get_properties;
zend_ffi_cdata_free_handlers.get_gc = zend_fake_get_gc;
INIT_NS_CLASS_ENTRY(ce, "FFI", "CType", NULL);
zend_ffi_ctype_ce = zend_register_internal_class(&ce);
zend_ffi_ctype_ce->ce_flags |= ZEND_ACC_FINAL;
zend_ffi_ctype_ce->create_object = zend_ffi_ctype_new;
zend_ffi_ctype_ce->serialize = zend_class_serialize_deny;
zend_ffi_ctype_ce->unserialize = zend_class_unserialize_deny;
memcpy(&zend_ffi_ctype_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
zend_ffi_ctype_handlers.get_constructor = zend_fake_get_constructor;
zend_ffi_ctype_handlers.free_obj = zend_ffi_ctype_free_obj;
zend_ffi_ctype_handlers.clone_obj = NULL;
zend_ffi_ctype_handlers.read_property = zend_fake_read_property;
zend_ffi_ctype_handlers.write_property = zend_fake_write_property;
zend_ffi_ctype_handlers.read_dimension = zend_fake_read_dimension;
zend_ffi_ctype_handlers.write_dimension = zend_fake_write_dimension;
zend_ffi_ctype_handlers.get_property_ptr_ptr = zend_fake_get_property_ptr_ptr;
zend_ffi_ctype_handlers.has_property = zend_fake_has_property;
zend_ffi_ctype_handlers.unset_property = zend_fake_unset_property;
zend_ffi_ctype_handlers.has_dimension = zend_fake_has_dimension;
zend_ffi_ctype_handlers.unset_dimension = zend_fake_unset_dimension;
zend_ffi_ctype_handlers.get_method = zend_fake_get_method;
zend_ffi_ctype_handlers.get_class_name = zend_ffi_ctype_get_class_name;
zend_ffi_ctype_handlers.compare_objects = zend_ffi_ctype_compare_objects;
zend_ffi_ctype_handlers.cast_object = NULL;
zend_ffi_ctype_handlers.count_elements = NULL;
zend_ffi_ctype_handlers.get_debug_info = zend_ffi_ctype_get_debug_info;
zend_ffi_ctype_handlers.get_closure = NULL;
zend_ffi_ctype_handlers.get_properties = zend_fake_get_properties;
zend_ffi_ctype_handlers.get_gc = zend_fake_get_gc;
if (FFI_G(preload)) {
if (zend_ffi_preload(FFI_G(preload)) != SUCCESS) {
return FAILURE;
}
}
return SUCCESS;
}
/* }}} */
/* {{{ ZEND_RSHUTDOWN_FUNCTION
*/
ZEND_RSHUTDOWN_FUNCTION(ffi)
{
if (FFI_G(callbacks)) {
zend_hash_destroy(FFI_G(callbacks));
efree(FFI_G(callbacks));
FFI_G(callbacks) = NULL;
}
if (FFI_G(weak_types)) {
#if 0
fprintf(stderr, "WeakTypes: %d\n", zend_hash_num_elements(FFI_G(weak_types)));
#endif
zend_hash_destroy(FFI_G(weak_types));
efree(FFI_G(weak_types));
FFI_G(weak_types) = NULL;
}
return SUCCESS;
}
/* }}} */
/* {{{ ZEND_MINFO_FUNCTION
*/
ZEND_MINFO_FUNCTION(ffi)
{
php_info_print_table_start();
php_info_print_table_header(2, "FFI support", "enabled");
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
}
/* }}} */
static const zend_ffi_type zend_ffi_type_void = {.kind=ZEND_FFI_TYPE_VOID, .size=1, .align=1};
static const zend_ffi_type zend_ffi_type_char = {.kind=ZEND_FFI_TYPE_CHAR, .size=1, .align=_Alignof(char)};
static const zend_ffi_type zend_ffi_type_bool = {.kind=ZEND_FFI_TYPE_BOOL, .size=1, .align=_Alignof(uint8_t)};
static const zend_ffi_type zend_ffi_type_sint8 = {.kind=ZEND_FFI_TYPE_SINT8, .size=1, .align=_Alignof(int8_t)};
static const zend_ffi_type zend_ffi_type_uint8 = {.kind=ZEND_FFI_TYPE_UINT8, .size=1, .align=_Alignof(uint8_t)};
static const zend_ffi_type zend_ffi_type_sint16 = {.kind=ZEND_FFI_TYPE_SINT16, .size=2, .align=_Alignof(int16_t)};
static const zend_ffi_type zend_ffi_type_uint16 = {.kind=ZEND_FFI_TYPE_UINT16, .size=2, .align=_Alignof(uint16_t)};
static const zend_ffi_type zend_ffi_type_sint32 = {.kind=ZEND_FFI_TYPE_SINT32, .size=4, .align=_Alignof(int32_t)};
static const zend_ffi_type zend_ffi_type_uint32 = {.kind=ZEND_FFI_TYPE_UINT32, .size=4, .align=_Alignof(uint32_t)};
static const zend_ffi_type zend_ffi_type_sint64 = {.kind=ZEND_FFI_TYPE_SINT64, .size=8, .align=_Alignof(int64_t)};
static const zend_ffi_type zend_ffi_type_uint64 = {.kind=ZEND_FFI_TYPE_UINT64, .size=8, .align=_Alignof(uint64_t)};
static const zend_ffi_type zend_ffi_type_float = {.kind=ZEND_FFI_TYPE_FLOAT, .size=sizeof(float), .align=_Alignof(float)};
static const zend_ffi_type zend_ffi_type_double = {.kind=ZEND_FFI_TYPE_DOUBLE, .size=sizeof(double), .align=_Alignof(double)};
#ifdef HAVE_LONG_DOUBLE
static const zend_ffi_type zend_ffi_type_long_double = {.kind=ZEND_FFI_TYPE_LONGDOUBLE, .size=sizeof(long double), .align=_Alignof(long double)};
#endif
static const zend_ffi_type zend_ffi_type_ptr = {.kind=ZEND_FFI_TYPE_POINTER, .size=sizeof(void*), .align=_Alignof(void*), .pointer.type = (zend_ffi_type*)&zend_ffi_type_void};
const struct {
const char *name;
const zend_ffi_type *type;
} zend_ffi_types[] = {
{"void", &zend_ffi_type_void},
{"char", &zend_ffi_type_char},
{"bool", &zend_ffi_type_bool},
{"int8_t", &zend_ffi_type_sint8},
{"uint8_t", &zend_ffi_type_uint8},
{"int16_t", &zend_ffi_type_sint16},
{"uint16_t", &zend_ffi_type_uint16},
{"int32_t", &zend_ffi_type_sint32},
{"uint32_t", &zend_ffi_type_uint32},
{"int64_t", &zend_ffi_type_sint64},
{"uint64_t", &zend_ffi_type_uint64},
{"float", &zend_ffi_type_float},
{"double", &zend_ffi_type_double},
#ifdef HAVE_LONG_DOUBLE
{"long double", &zend_ffi_type_long_double},
#endif
#if SIZEOF_SIZE_T == 4
{"uintptr_t", &zend_ffi_type_uint32},
{"intptr_t", &zend_ffi_type_sint32},
{"size_t", &zend_ffi_type_uint32},
{"ssize_t", &zend_ffi_type_sint32},
{"ptrdiff_t", &zend_ffi_type_sint32},
#else
{"uintptr_t", &zend_ffi_type_uint64},
{"intptr_t", &zend_ffi_type_sint64},
{"size_t", &zend_ffi_type_uint64},
{"ssize_t", &zend_ffi_type_sint64},
{"ptrdiff_t", &zend_ffi_type_sint64},
#endif
#if SIZEOF_OFF_T == 4
{"off_t", &zend_ffi_type_sint32},
#else
{"off_t", &zend_ffi_type_sint64},
#endif
{"va_list", &zend_ffi_type_ptr},
{"__builtin_va_list", &zend_ffi_type_ptr},
{"__gnuc_va_list", &zend_ffi_type_ptr},
};
/* {{{ ZEND_GINIT_FUNCTION
*/
static ZEND_GINIT_FUNCTION(ffi)
{
size_t i;
#if defined(COMPILE_DL_FFI) && defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
memset(ffi_globals, 0, sizeof(*ffi_globals));
zend_hash_init(&ffi_globals->types, 0, NULL, NULL, 1);
for (i = 0; i < sizeof(zend_ffi_types)/sizeof(zend_ffi_types[0]); i++) {
zend_hash_str_add_new_ptr(&ffi_globals->types, zend_ffi_types[i].name, strlen(zend_ffi_types[i].name), (void*)zend_ffi_types[i].type);
}
}
/* }}} */
/* {{{ ZEND_GINIT_FUNCTION
*/
static ZEND_GSHUTDOWN_FUNCTION(ffi)
{
if (ffi_globals->scopes) {
zend_hash_destroy(ffi_globals->scopes);
free(ffi_globals->scopes);
}
zend_hash_destroy(&ffi_globals->types);
}
/* }}} */
/* {{{ ffi_module_entry
*/
zend_module_entry ffi_module_entry = {
STANDARD_MODULE_HEADER,
"FFI", /* Extension name */
NULL, /* zend_function_entry */
ZEND_MINIT(ffi), /* ZEND_MINIT - Module initialization */
NULL, /* ZEND_MSHUTDOWN - Module shutdown */
NULL, /* ZEND_RINIT - Request initialization */
ZEND_RSHUTDOWN(ffi), /* ZEND_RSHUTDOWN - Request shutdown */
ZEND_MINFO(ffi), /* ZEND_MINFO - Module info */
PHP_VERSION, /* Version */
ZEND_MODULE_GLOBALS(ffi),
ZEND_GINIT(ffi),
ZEND_GSHUTDOWN(ffi),
NULL,
STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */
#ifdef COMPILE_DL_FFI
# ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
# endif
ZEND_GET_MODULE(ffi)
#endif
/* parser callbacks */
void zend_ffi_parser_error(const char *format, ...) /* {{{ */
{
va_list va;
char *message = NULL;
va_start(va, format);
zend_vspprintf(&message, 0, format, va);
if (EG(current_execute_data)) {
zend_throw_exception(zend_ffi_parser_exception_ce, message, 0);
} else {
zend_error(E_WARNING, "FFI Parser: %s", message);
}
efree(message);
va_end(va);
LONGJMP(FFI_G(bailout), FAILURE);
}
/* }}} */
static void zend_ffi_finalize_type(zend_ffi_dcl *dcl) /* {{{ */
{
if (!dcl->type) {
switch (dcl->flags & ZEND_FFI_DCL_TYPE_SPECIFIERS) {
case ZEND_FFI_DCL_VOID:
dcl->type = (zend_ffi_type*)&zend_ffi_type_void;
break;
case ZEND_FFI_DCL_CHAR:
dcl->type = (zend_ffi_type*)&zend_ffi_type_char;
break;
case ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SIGNED:
dcl->type = (zend_ffi_type*)&zend_ffi_type_sint8;
break;
case ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_UNSIGNED:
case ZEND_FFI_DCL_BOOL:
dcl->type = (zend_ffi_type*)&zend_ffi_type_uint8;
break;
case ZEND_FFI_DCL_SHORT:
case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_SIGNED:
case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT:
case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
dcl->type = (zend_ffi_type*)&zend_ffi_type_sint16;
break;
case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_UNSIGNED:
case ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
dcl->type = (zend_ffi_type*)&zend_ffi_type_uint16;
break;
case ZEND_FFI_DCL_INT:
case ZEND_FFI_DCL_SIGNED:
case ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
dcl->type = (zend_ffi_type*)&zend_ffi_type_sint32;
break;
case ZEND_FFI_DCL_UNSIGNED:
case ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
dcl->type = (zend_ffi_type*)&zend_ffi_type_uint32;
break;
case ZEND_FFI_DCL_LONG:
case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED:
case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_INT:
case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
if (sizeof(long) == 4) {
dcl->type = (zend_ffi_type*)&zend_ffi_type_sint32;
} else {
dcl->type = (zend_ffi_type*)&zend_ffi_type_sint64;
}
break;
case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED:
case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
if (sizeof(long) == 4) {
dcl->type = (zend_ffi_type*)&zend_ffi_type_uint32;
} else {
dcl->type = (zend_ffi_type*)&zend_ffi_type_uint64;
}
break;
case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG:
case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED:
case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_INT:
case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_INT:
dcl->type = (zend_ffi_type*)&zend_ffi_type_sint64;
break;
case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED:
case ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_UNSIGNED|ZEND_FFI_DCL_INT:
dcl->type = (zend_ffi_type*)&zend_ffi_type_uint64;
break;
case ZEND_FFI_DCL_FLOAT:
dcl->type = (zend_ffi_type*)&zend_ffi_type_float;
break;
case ZEND_FFI_DCL_DOUBLE:
dcl->type = (zend_ffi_type*)&zend_ffi_type_double;
break;
case ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_DOUBLE:
#ifdef _WIN32
dcl->type = (zend_ffi_type*)&zend_ffi_type_double;
#else
dcl->type = (zend_ffi_type*)&zend_ffi_type_long_double;
#endif
break;
case ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_COMPLEX:
case ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_COMPLEX:
case ZEND_FFI_DCL_DOUBLE|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_COMPLEX:
zend_ffi_parser_error("unsupported type '_Complex' at line %d", FFI_G(line));
break;
default:
zend_ffi_parser_error("unsupported type specifier combination at line %d", FFI_G(line));
break;
}
dcl->flags &= ~ZEND_FFI_DCL_TYPE_SPECIFIERS;
dcl->flags |= ZEND_FFI_DCL_TYPEDEF_NAME;
}
}
/* }}} */
int zend_ffi_is_typedef_name(const char *name, size_t name_len) /* {{{ */
{
zend_ffi_symbol *sym;
zend_ffi_type *type;
if (FFI_G(symbols)) {
sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
if (sym) {
return (sym->kind == ZEND_FFI_SYM_TYPE);
}
}
type = zend_hash_str_find_ptr(&FFI_G(types), name, name_len);
if (type) {
return 1;
}
return 0;
}
/* }}} */
void zend_ffi_resolve_typedef(const char *name, size_t name_len, zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_symbol *sym;
zend_ffi_type *type;
if (FFI_G(symbols)) {
sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
if (sym && sym->kind == ZEND_FFI_SYM_TYPE) {
dcl->type = ZEND_FFI_TYPE(sym->type);;
if (sym->is_const) {
dcl->attr |= ZEND_FFI_ATTR_CONST;
}
return;
}
}
type = zend_hash_str_find_ptr(&FFI_G(types), name, name_len);
if (type) {
dcl->type = type;
return;
}
zend_ffi_parser_error("undefined C type '%.*s' at line %d", name_len, name, FFI_G(line));
}
/* }}} */
void zend_ffi_resolve_const(const char *name, size_t name_len, zend_ffi_val *val) /* {{{ */
{
zend_ffi_symbol *sym;
if (UNEXPECTED(FFI_G(attribute_parsing))) {
val->kind = ZEND_FFI_VAL_NAME;
val->str = name;
val->len = name_len;
return;
} else if (FFI_G(symbols)) {
sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
if (sym && sym->kind == ZEND_FFI_SYM_CONST) {
val->i64 = sym->value;
switch (sym->type->kind) {
case ZEND_FFI_TYPE_SINT8:
case ZEND_FFI_TYPE_SINT16:
case ZEND_FFI_TYPE_SINT32:
val->kind = ZEND_FFI_VAL_INT32;
break;
case ZEND_FFI_TYPE_SINT64:
val->kind = ZEND_FFI_VAL_INT64;
break;
case ZEND_FFI_TYPE_UINT8:
case ZEND_FFI_TYPE_UINT16:
case ZEND_FFI_TYPE_UINT32:
val->kind = ZEND_FFI_VAL_UINT32;
break;
case ZEND_FFI_TYPE_UINT64:
val->kind = ZEND_FFI_VAL_UINT64;
break;
default:
ZEND_ASSERT(0);
}
return;
}
}
val->kind = ZEND_FFI_VAL_ERROR;
}
/* }}} */
void zend_ffi_make_enum_type(zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
type->kind = ZEND_FFI_TYPE_ENUM;
type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_ENUM_ATTRS);
type->enumeration.tag_name = NULL;
if (type->attr & ZEND_FFI_ATTR_PACKED) {
type->size = zend_ffi_type_uint8.size;
type->align = zend_ffi_type_uint8.align;
type->enumeration.kind = ZEND_FFI_TYPE_UINT8;
} else {
type->size = zend_ffi_type_uint32.size;
type->align = zend_ffi_type_uint32.align;
type->enumeration.kind = ZEND_FFI_TYPE_UINT32;
}
dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
dcl->attr &= ~ZEND_FFI_ENUM_ATTRS;
}
/* }}} */
void zend_ffi_add_enum_val(zend_ffi_dcl *enum_dcl, const char *name, size_t name_len, zend_ffi_val *val, int64_t *min, int64_t *max, int64_t *last) /* {{{ */
{
zend_ffi_symbol *sym;
const zend_ffi_type *sym_type;
int64_t value;
zend_ffi_type *enum_type = ZEND_FFI_TYPE(enum_dcl->type);
zend_bool overflow = 0;
zend_bool is_signed =
(enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT8 ||
enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT16 ||
enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT32 ||
enum_type->enumeration.kind == ZEND_FFI_TYPE_SINT64);
ZEND_ASSERT(enum_type && enum_type->kind == ZEND_FFI_TYPE_ENUM);
if (val->kind == ZEND_FFI_VAL_EMPTY) {
if (is_signed) {
if (*last == 0x7FFFFFFFFFFFFFFFLL) {
overflow = 1;
}
} else {
if ((*min != 0 || *max != 0)
&& (uint64_t)*last == 0xFFFFFFFFFFFFFFFFULL) {
overflow = 1;
}
}
value = *last + 1;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
if (!is_signed && val->ch < 0) {
if ((uint64_t)*max > 0x7FFFFFFFFFFFFFFFULL) {
overflow = 1;
} else {
is_signed = 1;
}
}
value = val->ch;
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
if (!is_signed && val->i64 < 0) {
if ((uint64_t)*max > 0x7FFFFFFFFFFFFFFFULL) {
overflow = 1;
} else {
is_signed = 1;
}
}
value = val->i64;
} else if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
if (is_signed && val->u64 > 0x7FFFFFFFFFFFFFFFULL) {
overflow = 1;
}
value = val->u64;
} else {
zend_ffi_parser_error("enumerator value '%.*s' must be an integer at line %d", name_len, name, FFI_G(line));
return;
}
if (overflow) {
zend_ffi_parser_error("overflow in enumeration values '%.*s' at line %d", name_len, name, FFI_G(line));
return;
}
if (is_signed) {
*min = MIN(*min, value);
*max = MAX(*max, value);
if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
&& *min >= -0x7FLL-1 && *max <= 0x7FLL) {
sym_type = &zend_ffi_type_sint8;
} else if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
&& *min >= -0x7FFFLL-1 && *max <= 0x7FFFLL) {
sym_type = &zend_ffi_type_sint16;
} else if (*min >= -0x7FFFFFFFLL-1 && *max <= 0x7FFFFFFFLL) {
sym_type = &zend_ffi_type_sint32;
} else {
sym_type = &zend_ffi_type_sint64;
}
} else {
*min = MIN((uint64_t)*min, (uint64_t)value);
*max = MAX((uint64_t)*max, (uint64_t)value);
if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
&& (uint64_t)*max <= 0xFFULL) {
sym_type = &zend_ffi_type_uint8;
} else if ((enum_type->attr & ZEND_FFI_ATTR_PACKED)
&& (uint64_t)*max <= 0xFFFFULL) {
sym_type = &zend_ffi_type_uint16;
} else if ((uint64_t)*max <= 0xFFFFFFFFULL) {
sym_type = &zend_ffi_type_uint32;
} else {
sym_type = &zend_ffi_type_uint64;
}
}
enum_type->enumeration.kind = sym_type->kind;
enum_type->size = sym_type->size;
enum_type->align = sym_type->align;
*last = value;
if (!FFI_G(symbols)) {
FFI_G(symbols) = pemalloc(sizeof(HashTable), FFI_G(persistent));
zend_hash_init(FFI_G(symbols), 0, NULL, FFI_G(persistent) ? zend_ffi_symbol_hash_persistent_dtor : zend_ffi_symbol_hash_dtor, FFI_G(persistent));
}
sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
if (sym) {
zend_ffi_parser_error("redeclaration of '%.*s' at line %d", name_len, name, FFI_G(line));
} else {
sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
sym->kind = ZEND_FFI_SYM_CONST;
sym->type = (zend_ffi_type*)sym_type;
sym->value = value;
zend_hash_str_add_new_ptr(FFI_G(symbols), name, name_len, sym);
}
}
/* }}} */
void zend_ffi_make_struct_type(zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
type->kind = ZEND_FFI_TYPE_STRUCT;
type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_STRUCT_ATTRS);
type->size = 0;
type->align = dcl->align > 1 ? dcl->align : 1;
if (dcl->flags & ZEND_FFI_DCL_UNION) {
type->attr |= ZEND_FFI_ATTR_UNION;
}
dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
type->record.tag_name = NULL;
zend_hash_init(&type->record.fields, 0, NULL, FFI_G(persistent) ? zend_ffi_field_hash_persistent_dtor :zend_ffi_field_hash_dtor, FFI_G(persistent));
dcl->attr &= ~ZEND_FFI_STRUCT_ATTRS;
dcl->align = 0;
}
/* }}} */
static int zend_ffi_validate_prev_field_type(zend_ffi_type *struct_type) /* {{{ */
{
if (zend_hash_num_elements(&struct_type->record.fields) > 0) {
zend_ffi_field *field = NULL;
ZEND_HASH_REVERSE_FOREACH_PTR(&struct_type->record.fields, field) {
break;
} ZEND_HASH_FOREACH_END();
if (ZEND_FFI_TYPE(field->type)->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY) {
zend_ffi_throw_parser_error("flexible array member not at end of struct at line %d", FFI_G(line));
return FAILURE;
}
}
return SUCCESS;
}
/* }}} */
static int zend_ffi_validate_field_type(zend_ffi_type *type, zend_ffi_type *struct_type) /* {{{ */
{
if (type == struct_type) {
zend_ffi_throw_parser_error("struct/union can't contain an instance of itself at line %d", FFI_G(line));
return FAILURE;
} else if (zend_ffi_validate_var_type(type, 1) != SUCCESS) {
return FAILURE;
} else if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
if (type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY) {
zend_ffi_throw_parser_error("flexible array member in union at line %d", FFI_G(line));
return FAILURE;
}
}
return zend_ffi_validate_prev_field_type(struct_type);
}
/* }}} */
void zend_ffi_add_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl) /* {{{ */
{
zend_ffi_field *field;
zend_ffi_type *struct_type = ZEND_FFI_TYPE(struct_dcl->type);
zend_ffi_type *field_type;
ZEND_ASSERT(struct_type && struct_type->kind == ZEND_FFI_TYPE_STRUCT);
zend_ffi_finalize_type(field_dcl);
field_type = ZEND_FFI_TYPE(field_dcl->type);
if (zend_ffi_validate_field_type(field_type, struct_type) != SUCCESS) {
zend_ffi_cleanup_dcl(field_dcl);
LONGJMP(FFI_G(bailout), FAILURE);
}
field = pemalloc(sizeof(zend_ffi_field), FFI_G(persistent));
if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
struct_type->align = MAX(struct_type->align, MAX(field_type->align, field_dcl->align));
}
if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
field->offset = 0;
struct_type->size = MAX(struct_type->size, field_type->size);
} else {
if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
uint32_t field_align = MAX(field_type->align, field_dcl->align);
struct_type->size = ((struct_type->size + (field_align - 1)) / field_align) * field_align;
}
field->offset = struct_type->size;
struct_type->size += field_type->size;
}
field->type = field_dcl->type;
field->is_const = (zend_bool)(field_dcl->attr & ZEND_FFI_ATTR_CONST);
field->is_nested = 0;
field->first_bit = 0;
field->bits = 0;
field_dcl->type = field_type; /* reset "owned" flag */
if (!zend_hash_str_add_ptr(&struct_type->record.fields, name, name_len, field)) {
zend_ffi_type_dtor(field->type);
pefree(field, FFI_G(persistent));
zend_ffi_parser_error("duplicate field name '%.*s' at line %d", name_len, name, FFI_G(line));
}
}
/* }}} */
void zend_ffi_add_anonymous_field(zend_ffi_dcl *struct_dcl, zend_ffi_dcl *field_dcl) /* {{{ */
{
zend_ffi_type *struct_type = ZEND_FFI_TYPE(struct_dcl->type);
zend_ffi_type *field_type;
zend_ffi_field *field;
zend_string *key;
ZEND_ASSERT(struct_type && struct_type->kind == ZEND_FFI_TYPE_STRUCT);
zend_ffi_finalize_type(field_dcl);
field_type = ZEND_FFI_TYPE(field_dcl->type);
if (field_type->kind != ZEND_FFI_TYPE_STRUCT) {
zend_ffi_cleanup_dcl(field_dcl);
zend_ffi_parser_error("declaration does not declare anything at line %d", FFI_G(line));
return;
}
if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
struct_type->align = MAX(struct_type->align, MAX(field_type->align, field_dcl->align));
}
if (!(struct_type->attr & ZEND_FFI_ATTR_UNION)) {
if (zend_ffi_validate_prev_field_type(struct_type) != SUCCESS) {
zend_ffi_cleanup_dcl(field_dcl);
LONGJMP(FFI_G(bailout), FAILURE);
}
if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED) && !(field_dcl->attr & ZEND_FFI_ATTR_PACKED)) {
uint32_t field_align = MAX(field_type->align, field_dcl->align);
struct_type->size = ((struct_type->size + (field_align - 1)) / field_align) * field_align;
}
}
ZEND_HASH_FOREACH_STR_KEY_PTR(&field_type->record.fields, key, field) {
zend_ffi_field *new_field = pemalloc(sizeof(zend_ffi_field), FFI_G(persistent));
if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
new_field->offset = field->offset;
} else {
new_field->offset = struct_type->size + field->offset;
}
new_field->type = field->type;
new_field->is_const = field->is_const;
new_field->is_nested = 1;
new_field->first_bit = field->first_bit;
new_field->bits = field->bits;
field->type = ZEND_FFI_TYPE(field->type); /* reset "owned" flag */
if (key) {
if (!zend_hash_add_ptr(&struct_type->record.fields, key, new_field)) {
zend_ffi_type_dtor(new_field->type);
pefree(new_field, FFI_G(persistent));
zend_ffi_parser_error("duplicate field name '%s' at line %d", ZSTR_VAL(key), FFI_G(line));
return;
}
} else {
zend_hash_next_index_insert_ptr(&struct_type->record.fields, field);
}
} ZEND_HASH_FOREACH_END();
if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
struct_type->size = MAX(struct_type->size, field_type->size);
} else {
struct_type->size += field_type->size;
}
zend_ffi_type_dtor(field_dcl->type);
field_dcl->type = NULL;
}
/* }}} */
void zend_ffi_add_bit_field(zend_ffi_dcl *struct_dcl, const char *name, size_t name_len, zend_ffi_dcl *field_dcl, zend_ffi_val *bits) /* {{{ */
{
zend_ffi_type *struct_type = ZEND_FFI_TYPE(struct_dcl->type);
zend_ffi_type *field_type;
zend_ffi_field *field;
ZEND_ASSERT(struct_type && struct_type->kind == ZEND_FFI_TYPE_STRUCT);
zend_ffi_finalize_type(field_dcl);
field_type = ZEND_FFI_TYPE(field_dcl->type);
if (zend_ffi_validate_field_type(field_type, struct_type) != SUCCESS) {
zend_ffi_cleanup_dcl(field_dcl);
LONGJMP(FFI_G(bailout), FAILURE);
}
if (field_type->kind < ZEND_FFI_TYPE_UINT8 || field_type->kind > ZEND_FFI_TYPE_BOOL) {
zend_ffi_cleanup_dcl(field_dcl);
zend_ffi_parser_error("wrong type of bit field '%.*s' at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
}
if (bits->kind == ZEND_FFI_VAL_INT32 || bits->kind == ZEND_FFI_VAL_INT64) {
if (bits->i64 < 0) {
zend_ffi_cleanup_dcl(field_dcl);
zend_ffi_parser_error("negative width in bit-field '%.*s' at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
} else if (bits->i64 == 0) {
zend_ffi_cleanup_dcl(field_dcl);
if (name) {
zend_ffi_parser_error("zero width in bit-field '%.*s' at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
}
return;
} else if (bits->i64 > field_type->size * 8) {
zend_ffi_cleanup_dcl(field_dcl);
zend_ffi_parser_error("width of '%.*s' exceeds its type at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
}
} else if (bits->kind == ZEND_FFI_VAL_UINT32 || bits->kind == ZEND_FFI_VAL_UINT64) {
if (bits->u64 == 0) {
zend_ffi_cleanup_dcl(field_dcl);
if (name) {
zend_ffi_parser_error("zero width in bit-field '%.*s' at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
}
return;
} else if (bits->u64 > field_type->size * 8) {
zend_ffi_cleanup_dcl(field_dcl);
zend_ffi_parser_error("width of '%.*s' exceeds its type at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
}
} else {
zend_ffi_cleanup_dcl(field_dcl);
zend_ffi_parser_error("bit field '%.*s' width not an integer constant at line %d", name ? name_len : sizeof("<anonymous>")-1, name ? name : "<anonymous>", FFI_G(line));
}
field = pemalloc(sizeof(zend_ffi_field), FFI_G(persistent));
if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED)) {
struct_type->align = MAX(struct_type->align, sizeof(uint32_t));
}
if (struct_type->attr & ZEND_FFI_ATTR_UNION) {
field->offset = 0;
field->first_bit = 0;
field->bits = bits->u64;
if (struct_type->attr & ZEND_FFI_ATTR_PACKED) {
struct_type->size = MAX(struct_type->size, (bits->u64 + 7) / 8);
} else {
struct_type->size = MAX(struct_type->size, ((bits->u64 + 31) / 32) * 4);
}
} else {
zend_ffi_field *prev_field = NULL;
if (zend_hash_num_elements(&struct_type->record.fields) > 0) {
ZEND_HASH_REVERSE_FOREACH_PTR(&struct_type->record.fields, prev_field) {
break;
} ZEND_HASH_FOREACH_END();
}
if (prev_field && prev_field->bits) {
field->offset = prev_field->offset;
field->first_bit = prev_field->first_bit + prev_field->bits;
field->bits = bits->u64;
} else {
field->offset = struct_type->size;
field->first_bit = 0;
field->bits = bits->u64;
}
if (struct_type->attr & ZEND_FFI_ATTR_PACKED) {
struct_type->size = field->offset + ((field->first_bit + field->bits) + 7) / 8;
} else {
struct_type->size = field->offset + (((field->first_bit + field->bits) + 31) / 32) * 4;
}
}
field->type = field_dcl->type;
field->is_const = (zend_bool)(field_dcl->attr & ZEND_FFI_ATTR_CONST);
field->is_nested = 0;
field_dcl->type = field_type; /* reset "owned" flag */
if (name) {
if (!zend_hash_str_add_ptr(&struct_type->record.fields, name, name_len, field)) {
zend_ffi_type_dtor(field->type);
pefree(field, FFI_G(persistent));
zend_ffi_parser_error("duplicate field name '%.*s' at line %d", name_len, name, FFI_G(line));
}
} else {
zend_hash_next_index_insert_ptr(&struct_type->record.fields, field);
}
}
/* }}} */
void zend_ffi_adjust_struct_size(zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_type *struct_type = ZEND_FFI_TYPE(dcl->type);
ZEND_ASSERT(struct_type->kind == ZEND_FFI_TYPE_STRUCT);
if (dcl->align > struct_type->align) {
struct_type->align = dcl->align;
}
if (!(struct_type->attr & ZEND_FFI_ATTR_PACKED)) {
struct_type->size = ((struct_type->size + (struct_type->align - 1)) / struct_type->align) * struct_type->align;
}
dcl->align = 0;
}
/* }}} */
void zend_ffi_make_pointer_type(zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
type->kind = ZEND_FFI_TYPE_POINTER;
type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_POINTER_ATTRS);
type->size = sizeof(void*);
type->align = _Alignof(void*);
zend_ffi_finalize_type(dcl);
if (zend_ffi_validate_vla(ZEND_FFI_TYPE(dcl->type)) != SUCCESS) {
zend_ffi_cleanup_dcl(dcl);
LONGJMP(FFI_G(bailout), FAILURE);
}
type->pointer.type = dcl->type;
dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
dcl->flags &= ~ZEND_FFI_DCL_TYPE_QUALIFIERS;
dcl->attr &= ~ZEND_FFI_POINTER_ATTRS;
dcl->align = 0;
}
/* }}} */
static int zend_ffi_validate_array_element_type(zend_ffi_type *type) /* {{{ */
{
if (type->kind == ZEND_FFI_TYPE_FUNC) {
zend_ffi_throw_parser_error("array of functions is not allowed at line %d", FFI_G(line));
return FAILURE;
} else if (type->kind == ZEND_FFI_TYPE_ARRAY && (type->attr & ZEND_FFI_ATTR_INCOMPLETE_ARRAY)) {
zend_ffi_throw_parser_error("only the leftmost array can be undimensioned at line %d", FFI_G(line));
return FAILURE;
}
return zend_ffi_validate_type(type, 0, 1);
}
/* }}} */
void zend_ffi_make_array_type(zend_ffi_dcl *dcl, zend_ffi_val *len) /* {{{ */
{
int length = 0;
zend_ffi_type *element_type;
zend_ffi_type *type;
zend_ffi_finalize_type(dcl);
element_type = ZEND_FFI_TYPE(dcl->type);
if (len->kind == ZEND_FFI_VAL_EMPTY) {
length = 0;
} else if (len->kind == ZEND_FFI_VAL_UINT32 || len->kind == ZEND_FFI_VAL_UINT64) {
length = len->u64;
} else if (len->kind == ZEND_FFI_VAL_INT32 || len->kind == ZEND_FFI_VAL_INT64) {
length = len->i64;
} else if (len->kind == ZEND_FFI_VAL_CHAR) {
length = len->ch;
} else {
zend_ffi_cleanup_dcl(dcl);
zend_ffi_parser_error("unsupported array index type at line %d", FFI_G(line));
return;
}
if (length < 0) {
zend_ffi_cleanup_dcl(dcl);
zend_ffi_parser_error("negative array index at line %d", FFI_G(line));
return;
}
if (zend_ffi_validate_array_element_type(element_type) != SUCCESS) {
zend_ffi_cleanup_dcl(dcl);
LONGJMP(FFI_G(bailout), FAILURE);
}
type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
type->kind = ZEND_FFI_TYPE_ARRAY;
type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_ARRAY_ATTRS);
type->size = length * element_type->size;
type->align = element_type->align;
type->array.type = dcl->type;
type->array.length = length;
dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
dcl->flags &= ~ZEND_FFI_DCL_TYPE_QUALIFIERS;
dcl->attr &= ~ZEND_FFI_ARRAY_ATTRS;
dcl->align = 0;
}
/* }}} */
static int zend_ffi_validate_func_ret_type(zend_ffi_type *type) /* {{{ */
{
if (type->kind == ZEND_FFI_TYPE_FUNC) {
zend_ffi_throw_parser_error("function returning function is not allowed at line %d", FFI_G(line));
return FAILURE;
} else if (type->kind == ZEND_FFI_TYPE_ARRAY) {
zend_ffi_throw_parser_error("function returning array is not allowed at line %d", FFI_G(line));
return FAILURE;
}
return zend_ffi_validate_incomplete_type(type, 1, 0);
}
/* }}} */
void zend_ffi_make_func_type(zend_ffi_dcl *dcl, HashTable *args, zend_ffi_dcl *nested_dcl) /* {{{ */
{
zend_ffi_type *type;
zend_ffi_type *ret_type;
zend_ffi_finalize_type(dcl);
ret_type = ZEND_FFI_TYPE(dcl->type);
if (args) {
int no_args = 0;
zend_ffi_type *arg_type;
ZEND_HASH_FOREACH_PTR(args, arg_type) {
arg_type = ZEND_FFI_TYPE(arg_type);
if (arg_type->kind == ZEND_FFI_TYPE_VOID) {
if (zend_hash_num_elements(args) != 1) {
zend_ffi_cleanup_dcl(nested_dcl);
zend_ffi_cleanup_dcl(dcl);
zend_hash_destroy(args);
pefree(args, FFI_G(persistent));
zend_ffi_parser_error("'void' type is not allowed at line %d", FFI_G(line));
return;
} else {
no_args = 1;
}
}
} ZEND_HASH_FOREACH_END();
if (no_args) {
zend_hash_destroy(args);
pefree(args, FFI_G(persistent));
args = NULL;
}
}
#ifdef HAVE_FFI_VECTORCALL_PARTIAL
if (dcl->abi == ZEND_FFI_ABI_VECTORCALL && args) {
zend_ulong i;
zend_ffi_type *arg_type;
ZEND_HASH_FOREACH_NUM_KEY_PTR(args, i, arg_type) {
arg_type = ZEND_FFI_TYPE(arg_type);
# ifdef _WIN64
if (i >= 4 && i <= 5 && (arg_type->kind == ZEND_FFI_TYPE_FLOAT || arg_type->kind == ZEND_FFI_TYPE_DOUBLE)) {
# else
if (i < 6 && (arg_type->kind == ZEND_FFI_TYPE_FLOAT || arg_type->kind == ZEND_FFI_TYPE_DOUBLE)) {
# endif
zend_ffi_cleanup_dcl(nested_dcl);
zend_ffi_cleanup_dcl(dcl);
zend_hash_destroy(args);
pefree(args, FFI_G(persistent));
zend_ffi_parser_error("'float'/'double' type not allowed at position " ZEND_ULONG_FMT " with __vectorcall at line %d", i+1, FFI_G(line));
return;
}
} ZEND_HASH_FOREACH_END();
}
#endif
if (zend_ffi_validate_func_ret_type(ret_type) != SUCCESS) {
zend_ffi_cleanup_dcl(nested_dcl);
zend_ffi_cleanup_dcl(dcl);
if (args) {
zend_hash_destroy(args);
pefree(args, FFI_G(persistent));
}
LONGJMP(FFI_G(bailout), FAILURE);
}
type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
type->kind = ZEND_FFI_TYPE_FUNC;
type->attr = FFI_G(default_type_attr) | (dcl->attr & ZEND_FFI_FUNC_ATTRS);
type->size = sizeof(void*);
type->align = 1;
type->func.ret_type = dcl->type;
switch (dcl->abi) {
case ZEND_FFI_ABI_DEFAULT:
case ZEND_FFI_ABI_CDECL:
type->func.abi = FFI_DEFAULT_ABI;
break;
#ifdef HAVE_FFI_FASTCALL
case ZEND_FFI_ABI_FASTCALL:
type->func.abi = FFI_FASTCALL;
break;
#endif
#ifdef HAVE_FFI_THISCALL
case ZEND_FFI_ABI_THISCALL:
type->func.abi = FFI_THISCALL;
break;
#endif
#ifdef HAVE_FFI_STDCALL
case ZEND_FFI_ABI_STDCALL:
type->func.abi = FFI_STDCALL;
break;
#endif
#ifdef HAVE_FFI_PASCAL
case ZEND_FFI_ABI_PASCAL:
type->func.abi = FFI_PASCAL;
break;
#endif
#ifdef HAVE_FFI_REGISTER
case ZEND_FFI_ABI_REGISTER:
type->func.abi = FFI_REGISTER;
break;
#endif
#ifdef HAVE_FFI_MS_CDECL
case ZEND_FFI_ABI_MS:
type->func.abi = FFI_MS_CDECL;
break;
#endif
#ifdef HAVE_FFI_SYSV
case ZEND_FFI_ABI_SYSV:
type->func.abi = FFI_SYSV;
break;
#endif
#ifdef HAVE_FFI_VECTORCALL_PARTIAL
case ZEND_FFI_ABI_VECTORCALL:
type->func.abi = FFI_VECTORCALL_PARTIAL;
break;
#endif
default:
type->func.abi = FFI_DEFAULT_ABI;
zend_ffi_cleanup_dcl(nested_dcl);
if (args) {
zend_hash_destroy(args);
pefree(args, FFI_G(persistent));
}
type->func.args = NULL;
_zend_ffi_type_dtor(type);
zend_ffi_parser_error("unsupported calling convention line %d", FFI_G(line));
break;
}
type->func.args = args;
dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
dcl->attr &= ~ZEND_FFI_FUNC_ATTRS;
dcl->align = 0;
dcl->abi = 0;
}
/* }}} */
void zend_ffi_add_arg(HashTable **args, const char *name, size_t name_len, zend_ffi_dcl *arg_dcl) /* {{{ */
{
zend_ffi_type *type;
if (!*args) {
*args = pemalloc(sizeof(HashTable), FFI_G(persistent));
zend_hash_init(*args, 0, NULL, zend_ffi_type_hash_dtor, FFI_G(persistent));
}
zend_ffi_finalize_type(arg_dcl);
type = ZEND_FFI_TYPE(arg_dcl->type);
if (type->kind == ZEND_FFI_TYPE_ARRAY) {
if (ZEND_FFI_TYPE_IS_OWNED(arg_dcl->type)) {
type->kind = ZEND_FFI_TYPE_POINTER;
type->size = sizeof(void*);
} else {
zend_ffi_type *new_type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
new_type->kind = ZEND_FFI_TYPE_POINTER;
new_type->attr = FFI_G(default_type_attr) | (type->attr & ZEND_FFI_POINTER_ATTRS);
new_type->size = sizeof(void*);
new_type->align = _Alignof(void*);
new_type->pointer.type = ZEND_FFI_TYPE(type->array.type);
arg_dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
}
} else if (type->kind == ZEND_FFI_TYPE_FUNC) {
zend_ffi_type *new_type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
new_type->kind = ZEND_FFI_TYPE_POINTER;
new_type->attr = FFI_G(default_type_attr);
new_type->size = sizeof(void*);
new_type->align = _Alignof(void*);
new_type->pointer.type = arg_dcl->type;
arg_dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(new_type);
}
if (zend_ffi_validate_incomplete_type(type, 1, 1) != SUCCESS) {
zend_ffi_cleanup_dcl(arg_dcl);
zend_hash_destroy(*args);
pefree(*args, FFI_G(persistent));
*args = NULL;
LONGJMP(FFI_G(bailout), FAILURE);
}
zend_hash_next_index_insert_ptr(*args, (void*)arg_dcl->type);
}
/* }}} */
void zend_ffi_declare(const char *name, size_t name_len, zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_symbol *sym;
if (!FFI_G(symbols)) {
FFI_G(symbols) = pemalloc(sizeof(HashTable), FFI_G(persistent));
zend_hash_init(FFI_G(symbols), 0, NULL, FFI_G(persistent) ? zend_ffi_symbol_hash_persistent_dtor : zend_ffi_symbol_hash_dtor, FFI_G(persistent));
}
zend_ffi_finalize_type(dcl);
sym = zend_hash_str_find_ptr(FFI_G(symbols), name, name_len);
if (sym) {
if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_TYPEDEF
&& sym->kind == ZEND_FFI_SYM_TYPE
&& zend_ffi_is_same_type(ZEND_FFI_TYPE(sym->type), ZEND_FFI_TYPE(dcl->type))
&& sym->is_const == (zend_bool)(dcl->attr & ZEND_FFI_ATTR_CONST)) {
/* allowed redeclaration */
zend_ffi_type_dtor(dcl->type);
return;
} else if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == 0
|| (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_EXTERN) {
zend_ffi_type *type = ZEND_FFI_TYPE(dcl->type);
if (type->kind == ZEND_FFI_TYPE_FUNC) {
if (sym->kind == ZEND_FFI_SYM_FUNC
&& zend_ffi_same_types(ZEND_FFI_TYPE(sym->type), type)) {
/* allowed redeclaration */
zend_ffi_type_dtor(dcl->type);
return;
}
} else {
if (sym->kind == ZEND_FFI_SYM_VAR
&& zend_ffi_is_same_type(ZEND_FFI_TYPE(sym->type), type)
&& sym->is_const == (zend_bool)(dcl->attr & ZEND_FFI_ATTR_CONST)) {
/* allowed redeclaration */
zend_ffi_type_dtor(dcl->type);
return;
}
}
}
zend_ffi_parser_error("redeclaration of '%.*s' at line %d", name_len, name, FFI_G(line));
} else {
if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_TYPEDEF) {
if (zend_ffi_validate_vla(ZEND_FFI_TYPE(dcl->type)) != SUCCESS) {
zend_ffi_cleanup_dcl(dcl);
LONGJMP(FFI_G(bailout), FAILURE);
}
if (dcl->align && dcl->align > ZEND_FFI_TYPE(dcl->type)->align) {
if (ZEND_FFI_TYPE_IS_OWNED(dcl->type)) {
ZEND_FFI_TYPE(dcl->type)->align = dcl->align;
} else {
zend_ffi_type *type = pemalloc(sizeof(zend_ffi_type), FFI_G(persistent));
memcpy(type, ZEND_FFI_TYPE(dcl->type), sizeof(zend_ffi_type));
type->attr |= FFI_G(default_type_attr);
type->align = dcl->align;
dcl->type = ZEND_FFI_TYPE_MAKE_OWNED(type);
}
}
sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
sym->kind = ZEND_FFI_SYM_TYPE;
sym->type = dcl->type;
sym->is_const = (zend_bool)(dcl->attr & ZEND_FFI_ATTR_CONST);
dcl->type = ZEND_FFI_TYPE(dcl->type); /* reset "owned" flag */
zend_hash_str_add_new_ptr(FFI_G(symbols), name, name_len, sym);
} else {
zend_ffi_type *type;
type = ZEND_FFI_TYPE(dcl->type);
if (zend_ffi_validate_type(type, (dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_EXTERN, 1) != SUCCESS) {
zend_ffi_cleanup_dcl(dcl);
LONGJMP(FFI_G(bailout), FAILURE);
}
if ((dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == 0 ||
(dcl->flags & ZEND_FFI_DCL_STORAGE_CLASS) == ZEND_FFI_DCL_EXTERN) {
sym = pemalloc(sizeof(zend_ffi_symbol), FFI_G(persistent));
sym->kind = (type->kind == ZEND_FFI_TYPE_FUNC) ? ZEND_FFI_SYM_FUNC : ZEND_FFI_SYM_VAR;
sym->type = dcl->type;
sym->is_const = (zend_bool)(dcl->attr & ZEND_FFI_ATTR_CONST);
dcl->type = type; /* reset "owned" flag */
zend_hash_str_add_new_ptr(FFI_G(symbols), name, name_len, sym);
} else {
/* useless declarartion */
zend_ffi_type_dtor(dcl->type);
}
}
}
}
/* }}} */
void zend_ffi_declare_tag(const char *name, size_t name_len, zend_ffi_dcl *dcl, zend_bool incomplete) /* {{{ */
{
zend_ffi_tag *tag;
zend_ffi_type *type;
if (!FFI_G(tags)) {
FFI_G(tags) = pemalloc(sizeof(HashTable), FFI_G(persistent));
zend_hash_init(FFI_G(tags), 0, NULL, FFI_G(persistent) ? zend_ffi_tag_hash_persistent_dtor : zend_ffi_tag_hash_dtor, FFI_G(persistent));
}
tag = zend_hash_str_find_ptr(FFI_G(tags), name, name_len);
if (tag) {
zend_ffi_type *type = ZEND_FFI_TYPE(tag->type);
if (dcl->flags & ZEND_FFI_DCL_STRUCT) {
if (tag->kind != ZEND_FFI_TAG_STRUCT) {
zend_ffi_parser_error("'%.*s' defined as wrong kind of tag at line %d", name_len, name, FFI_G(line));
return;
} else if (!incomplete && !(type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
zend_ffi_parser_error("redefinition of 'struct %.*s' at line %d", name_len, name, FFI_G(line));
return;
}
} else if (dcl->flags & ZEND_FFI_DCL_UNION) {
if (tag->kind != ZEND_FFI_TAG_UNION) {
zend_ffi_parser_error("'%.*s' defined as wrong kind of tag at line %d", name_len, name, FFI_G(line));
return;
} else if (!incomplete && !(type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
zend_ffi_parser_error("redefinition of 'union %.*s' at line %d", name_len, name, FFI_G(line));
return;
}
} else if (dcl->flags & ZEND_FFI_DCL_ENUM) {
if (tag->kind != ZEND_FFI_TAG_ENUM) {
zend_ffi_parser_error("'%.*s' defined as wrong kind of tag at line %d", name_len, name, FFI_G(line));
return;
} else if (!incomplete && !(type->attr & ZEND_FFI_ATTR_INCOMPLETE_TAG)) {
zend_ffi_parser_error("redefinition of 'enum %.*s' at line %d", name_len, name, FFI_G(line));
return;
}
} else {
ZEND_ASSERT(0);
return;
}
dcl->type = type;
if (!incomplete) {
type->attr &= ~ZEND_FFI_ATTR_INCOMPLETE_TAG;
}
} else {
zend_ffi_tag *tag = pemalloc(sizeof(zend_ffi_tag), FFI_G(persistent));
zend_string *tag_name = zend_string_init(name, name_len, FFI_G(persistent));
if (dcl->flags & ZEND_FFI_DCL_STRUCT) {
tag->kind = ZEND_FFI_TAG_STRUCT;
zend_ffi_make_struct_type(dcl);
type = ZEND_FFI_TYPE(dcl->type);
type->record.tag_name = zend_string_copy(tag_name);
} else if (dcl->flags & ZEND_FFI_DCL_UNION) {
tag->kind = ZEND_FFI_TAG_UNION;
zend_ffi_make_struct_type(dcl);
type = ZEND_FFI_TYPE(dcl->type);
type->record.tag_name = zend_string_copy(tag_name);
} else if (dcl->flags & ZEND_FFI_DCL_ENUM) {
tag->kind = ZEND_FFI_TAG_ENUM;
zend_ffi_make_enum_type(dcl);
type = ZEND_FFI_TYPE(dcl->type);
type->enumeration.tag_name = zend_string_copy(tag_name);
} else {
ZEND_ASSERT(0);
}
tag->type = ZEND_FFI_TYPE_MAKE_OWNED(dcl->type);
dcl->type = ZEND_FFI_TYPE(dcl->type);
if (incomplete) {
dcl->type->attr |= ZEND_FFI_ATTR_INCOMPLETE_TAG;
}
zend_hash_add_new_ptr(FFI_G(tags), tag_name, tag);
zend_string_release(tag_name);
}
}
/* }}} */
void zend_ffi_set_abi(zend_ffi_dcl *dcl, uint16_t abi) /* {{{ */
{
if (dcl->abi != ZEND_FFI_ABI_DEFAULT) {
zend_ffi_parser_error("multiple calling convention specifiers at line %d", FFI_G(line));
} else {
dcl->abi = abi;
}
}
/* }}} */
#define SIMPLE_ATTRIBUTES(_) \
_(cdecl) \
_(fastcall) \
_(thiscall) \
_(stdcall) \
_(ms_abi) \
_(sysv_abi) \
_(vectorcall) \
_(aligned) \
_(packed) \
_(ms_struct) \
_(gcc_struct) \
_(const) \
_(malloc) \
_(deprecated) \
_(nothrow) \
_(leaf) \
_(pure) \
_(noreturn) \
_(warn_unused_result)
#define ATTR_ID(name) attr_ ## name,
#define ATTR_NAME(name) {sizeof(#name)-1, #name},
void zend_ffi_add_attribute(zend_ffi_dcl *dcl, const char *name, size_t name_len) /* {{{ */
{
enum {
SIMPLE_ATTRIBUTES(ATTR_ID)
attr_unsupported
};
static const struct {
size_t len;
const char * const name;
} names[] = {
SIMPLE_ATTRIBUTES(ATTR_NAME)
{0, NULL}
};
int id;
if (name_len > 4
&& name[0] == '_'
&& name[1] == '_'
&& name[name_len-2] == '_'
&& name[name_len-1] == '_') {
name += 2;
name_len -= 4;
}
for (id = 0; names[id].len != 0; id++) {
if (name_len == names[id].len) {
if (memcmp(name, names[id].name, name_len) == 0) {
break;
}
}
}
switch (id) {
case attr_cdecl:
zend_ffi_set_abi(dcl, ZEND_FFI_ABI_CDECL);
break;
case attr_fastcall:
zend_ffi_set_abi(dcl, ZEND_FFI_ABI_FASTCALL);
break;
case attr_thiscall:
zend_ffi_set_abi(dcl, ZEND_FFI_ABI_THISCALL);
break;
case attr_stdcall:
zend_ffi_set_abi(dcl, ZEND_FFI_ABI_STDCALL);
break;
case attr_ms_abi:
zend_ffi_set_abi(dcl, ZEND_FFI_ABI_MS);
break;
case attr_sysv_abi:
zend_ffi_set_abi(dcl, ZEND_FFI_ABI_SYSV);
break;
case attr_vectorcall:
zend_ffi_set_abi(dcl, ZEND_FFI_ABI_VECTORCALL);
break;
case attr_aligned:
dcl->align = __BIGGEST_ALIGNMENT__;
break;
case attr_packed:
dcl->attr |= ZEND_FFI_ATTR_PACKED;
break;
case attr_ms_struct:
dcl->attr |= ZEND_FFI_ATTR_MS_STRUCT;
break;
case attr_gcc_struct:
dcl->attr |= ZEND_FFI_ATTR_GCC_STRUCT;
break;
case attr_unsupported:
zend_ffi_parser_error("unsupported attribute '%.*s' at line %d", name_len, name, FFI_G(line));
break;
default:
/* ignore */
break;
}
}
/* }}} */
#define VALUE_ATTRIBUTES(_) \
_(regparam) \
_(aligned) \
_(mode) \
_(nonnull) \
_(alloc_size) \
_(format) \
_(deprecated)
void zend_ffi_add_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, int n, zend_ffi_val *val) /* {{{ */
{
enum {
VALUE_ATTRIBUTES(ATTR_ID)
attr_unsupported
};
static const struct {
size_t len;
const char * const name;
} names[] = {
VALUE_ATTRIBUTES(ATTR_NAME)
{0, NULL}
};
int id;
if (name_len > 4
&& name[0] == '_'
&& name[1] == '_'
&& name[name_len-2] == '_'
&& name[name_len-1] == '_') {
name += 2;
name_len -= 4;
}
for (id = 0; names[id].len != 0; id++) {
if (name_len == names[id].len) {
if (memcmp(name, names[id].name, name_len) == 0) {
break;
}
}
}
switch (id) {
case attr_regparam:
if (n == 0
&& (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT64 || val->kind == ZEND_FFI_VAL_UINT64)
&& val->i64 == 3) {
zend_ffi_set_abi(dcl, ZEND_FFI_ABI_REGISTER);
} else {
zend_ffi_parser_error("incorrect 'regparam' value at line %d", FFI_G(line));
}
break;
case attr_aligned:
if (n == 0
&& (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT64 || val->kind == ZEND_FFI_VAL_UINT64)
&& val->i64 > 0 && val->i64 <= 0x80000000 && (val->i64 & (val->i64 - 1)) == 0) {
dcl->align = val->i64;
} else {
zend_ffi_parser_error("incorrect 'alignment' value at line %d", FFI_G(line));
}
break;
case attr_mode:
if (n == 0
&& (val->kind == ZEND_FFI_VAL_NAME)) {
const char *str = val->str;
size_t len = val->len;
if (len > 4
&& str[0] == '_'
&& str[1] == '_'
&& str[len-2] == '_'
&& str[len-1] == '_') {
str += 2;
len -= 4;
}
// TODO: Add support for vector type 'VnXX' ???
if (len == 2) {
if (str[1] == 'I') {
if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG|ZEND_FFI_DCL_SIGNED|ZEND_FFI_DCL_UNSIGNED))) {
/* inappropriate type */
} else if (str[0] == 'Q') {
dcl->flags &= ~(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG);
dcl->flags |= ZEND_FFI_DCL_CHAR;
break;
} else if (str[0] == 'H') {
dcl->flags &= ~(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG);
dcl->flags |= ZEND_FFI_DCL_SHORT;
break;
} else if (str[0] == 'S') {
dcl->flags &= ~(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG);
dcl->flags |= ZEND_FFI_DCL_INT;
break;
} else if (str[0] == 'D') {
dcl->flags &= ~(ZEND_FFI_DCL_CHAR|ZEND_FFI_DCL_SHORT|ZEND_FFI_DCL_INT|ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG);
if (sizeof(long) == 8) {
dcl->flags |= ZEND_FFI_DCL_LONG;
} else {
dcl->flags |= ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_LONG_LONG;
}
break;
}
} else if (str[1] == 'F') {
if (dcl->flags & (ZEND_FFI_DCL_TYPE_SPECIFIERS-(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE))) {
/* inappropriate type */
} else if (str[0] == 'S') {
dcl->flags &= ~(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE);
dcl->flags |= ZEND_FFI_DCL_FLOAT;
break;
} else if (str[0] == 'D') {
dcl->flags &= ~(ZEND_FFI_DCL_LONG|ZEND_FFI_DCL_FLOAT|ZEND_FFI_DCL_DOUBLE);
dcl->flags |= ZEND_FFI_DCL_DOUBLE;
break;
}
}
}
}
zend_ffi_parser_error("unsupported 'mode' value at line %d", FFI_G(line));
// TODO: ???
case attr_unsupported:
zend_ffi_parser_error("unsupported attribute '%.*s' at line %d", name_len, name, FFI_G(line));
break;
default:
/* ignore */
break;
}
}
/* }}} */
void zend_ffi_add_msvc_attribute_value(zend_ffi_dcl *dcl, const char *name, size_t name_len, zend_ffi_val *val) /* {{{ */
{
if (name_len == sizeof("align")-1 && memcmp(name, "align", sizeof("align")-1) == 0) {
if ((val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT64 || val->kind == ZEND_FFI_VAL_UINT64)
&& val->i64 > 0 && val->i64 <= 0x80000000 && (val->i64 & (val->i64 - 1)) == 0) {
dcl->align = val->i64;
} else {
zend_ffi_parser_error("incorrect 'alignment' value at line %d", FFI_G(line));
}
} else {
/* ignore */
}
}
/* }}} */
static int zend_ffi_nested_type(zend_ffi_type *type, zend_ffi_type *nested_type) /* {{{ */
{
nested_type = ZEND_FFI_TYPE(nested_type);
switch (nested_type->kind) {
case ZEND_FFI_TYPE_POINTER:
/* "char" is used as a terminator of nested declaration */
if (nested_type->pointer.type == &zend_ffi_type_char) {
nested_type->pointer.type = type;
return zend_ffi_validate_vla(ZEND_FFI_TYPE(type));
} else {
return zend_ffi_nested_type(type, nested_type->pointer.type);
}
break;
case ZEND_FFI_TYPE_ARRAY:
/* "char" is used as a terminator of nested declaration */
if (nested_type->array.type == &zend_ffi_type_char) {
nested_type->array.type = type;
if (zend_ffi_validate_array_element_type(ZEND_FFI_TYPE(type)) != SUCCESS) {
return FAILURE;
}
} else {
if (zend_ffi_nested_type(type, nested_type->array.type) != SUCCESS) {
return FAILURE;
}
}
nested_type->size = nested_type->array.length * ZEND_FFI_TYPE(nested_type->array.type)->size;
nested_type->align = ZEND_FFI_TYPE(nested_type->array.type)->align;
return SUCCESS;
break;
case ZEND_FFI_TYPE_FUNC:
/* "char" is used as a terminator of nested declaration */
if (nested_type->func.ret_type == &zend_ffi_type_char) {
nested_type->func.ret_type = type;
return zend_ffi_validate_func_ret_type(ZEND_FFI_TYPE(type));
} else {
return zend_ffi_nested_type(type, nested_type->func.ret_type);
}
break;
default:
ZEND_ASSERT(0);
}
}
/* }}} */
void zend_ffi_nested_declaration(zend_ffi_dcl *dcl, zend_ffi_dcl *nested_dcl) /* {{{ */
{
/* "char" is used as a terminator of nested declaration */
zend_ffi_finalize_type(dcl);
if (!nested_dcl->type || nested_dcl->type == &zend_ffi_type_char) {
nested_dcl->type = dcl->type;
} else {
if (zend_ffi_nested_type(dcl->type, nested_dcl->type) != SUCCESS) {
zend_ffi_cleanup_dcl(nested_dcl);
LONGJMP(FFI_G(bailout), FAILURE);
}
}
dcl->type = nested_dcl->type;
}
/* }}} */
void zend_ffi_align_as_type(zend_ffi_dcl *dcl, zend_ffi_dcl *align_dcl) /* {{{ */
{
zend_ffi_finalize_type(align_dcl);
dcl->align = MAX(align_dcl->align, ZEND_FFI_TYPE(align_dcl->type)->align);
}
/* }}} */
void zend_ffi_align_as_val(zend_ffi_dcl *dcl, zend_ffi_val *align_val) /* {{{ */
{
switch (align_val->kind) {
case ZEND_FFI_VAL_INT32:
case ZEND_FFI_VAL_UINT32:
dcl->align = zend_ffi_type_uint32.align;
break;
case ZEND_FFI_VAL_INT64:
case ZEND_FFI_VAL_UINT64:
dcl->align = zend_ffi_type_uint64.align;
break;
case ZEND_FFI_VAL_FLOAT:
dcl->align = zend_ffi_type_float.align;
break;
case ZEND_FFI_VAL_DOUBLE:
dcl->align = zend_ffi_type_double.align;
break;
#ifdef HAVE_LONG_DOUBLE
case ZEND_FFI_VAL_LONG_DOUBLE:
dcl->align = zend_ffi_type_long_double.align;
break;
#endif
case ZEND_FFI_VAL_CHAR:
case ZEND_FFI_VAL_STRING:
dcl->align = zend_ffi_type_char.align;
break;
default:
break;
}
}
/* }}} */
#define zend_ffi_expr_bool(val) do { \
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = !!val->u64; \
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = !!val->i64; \
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = !!val->d; \
} else if (val->kind == ZEND_FFI_VAL_CHAR) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = !!val->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} while (0)
#define zend_ffi_expr_math(val, op2, OP) do { \
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
val->kind = MAX(val->kind, op2->kind); \
val->u64 = val->u64 OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_INT32) { \
val->u64 = val->u64 OP op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_INT64) { \
val->u64 = val->u64 OP op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->kind = op2->kind; \
val->d = (zend_ffi_double)val->u64 OP op2->d; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->u64 = val->u64 OP op2->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
if (op2->kind == ZEND_FFI_VAL_UINT32) { \
val->i64 = val->i64 OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_UINT64) { \
val->i64 = val->i64 OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
val->kind = MAX(val->kind, op2->kind); \
val->i64 = val->i64 OP op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->kind = op2->kind; \
val->d = (zend_ffi_double)val->i64 OP op2->d; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->i64 = val->i64 OP op2->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
val->d = val->d OP (zend_ffi_double)op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_INT32 ||op2->kind == ZEND_FFI_VAL_INT64) { \
val->d = val->d OP (zend_ffi_double)op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->kind = MAX(val->kind, op2->kind); \
val->d = val->d OP op2->d; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->d = val->d OP (zend_ffi_double)op2->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else if (val->kind == ZEND_FFI_VAL_CHAR) { \
if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
val->kind = op2->kind; \
val->u64 = val->ch OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
val->kind = ZEND_FFI_VAL_INT64; \
val->i64 = val->ch OP op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->kind = op2->kind; \
val->d = (zend_ffi_double)val->ch OP op2->d; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->ch = val->ch OP op2->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} while (0)
#define zend_ffi_expr_int_math(val, op2, OP) do { \
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
val->kind = MAX(val->kind, op2->kind); \
val->u64 = val->u64 OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_INT32) { \
val->u64 = val->u64 OP op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_INT64) { \
val->u64 = val->u64 OP op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->u64 = val->u64 OP (uint64_t)op2->d; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->u64 = val->u64 OP op2->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
if (op2->kind == ZEND_FFI_VAL_UINT32) { \
val->i64 = val->i64 OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_UINT64) { \
val->i64 = val->i64 OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
val->kind = MAX(val->kind, op2->kind); \
val->i64 = val->i64 OP op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->u64 = val->u64 OP (int64_t)op2->d; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->i64 = val->i64 OP op2->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
val->kind = op2->kind; \
val->u64 = (uint64_t)val->d OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
val->kind = op2->kind; \
val->i64 = (int64_t)val->d OP op2->i64; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else if (val->kind == ZEND_FFI_VAL_CHAR) { \
if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
val->kind = op2->kind; \
val->u64 = (uint64_t)val->ch OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
val->kind = op2->kind; \
val->i64 = (int64_t)val->ch OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->ch = val->ch OP op2->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} while (0)
#define zend_ffi_expr_cmp(val, op2, OP) do { \
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) { \
if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->u64 OP op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->u64 OP op2->u64; /*signed/unsigned */ \
} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = (zend_ffi_double)val->u64 OP op2->d; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->u64 OP op2->d; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) { \
if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->i64 OP op2->i64; /* signed/unsigned */ \
} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->i64 OP op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = (zend_ffi_double)val->i64 OP op2->d; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->i64 OP op2->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->d OP (zend_ffi_double)op2->u64; \
} else if (op2->kind == ZEND_FFI_VAL_INT32 ||op2->kind == ZEND_FFI_VAL_INT64) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->d OP (zend_ffi_double)op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->d OP op2->d; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->d OP (zend_ffi_double)op2->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else if (val->kind == ZEND_FFI_VAL_CHAR) { \
if (op2->kind == ZEND_FFI_VAL_UINT32 || op2->kind == ZEND_FFI_VAL_UINT64) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->ch OP op2->i64; /* signed/unsigned */ \
} else if (op2->kind == ZEND_FFI_VAL_INT32 || op2->kind == ZEND_FFI_VAL_INT64) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->ch OP op2->i64; \
} else if (op2->kind == ZEND_FFI_VAL_FLOAT || op2->kind == ZEND_FFI_VAL_DOUBLE || op2->kind == ZEND_FFI_VAL_LONG_DOUBLE) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = (zend_ffi_double)val->ch OP op2->d; \
} else if (op2->kind == ZEND_FFI_VAL_CHAR) { \
val->kind = ZEND_FFI_VAL_INT32; \
val->i64 = val->ch OP op2->ch; \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} else { \
val->kind = ZEND_FFI_VAL_ERROR; \
} \
} while (0)
void zend_ffi_expr_conditional(zend_ffi_val *val, zend_ffi_val *op2, zend_ffi_val *op3) /* {{{ */
{
zend_ffi_expr_bool(val);
if (val->kind == ZEND_FFI_VAL_INT32) {
if (val->i64) {
*val = *op2;
} else {
*val = *op3;
}
}
}
/* }}} */
void zend_ffi_expr_bool_or(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_bool(val);
zend_ffi_expr_bool(op2);
if (val->kind == ZEND_FFI_VAL_INT32 && op2->kind == ZEND_FFI_VAL_INT32) {
val->i64 = val->i64 || op2->i64;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
}
/* }}} */
void zend_ffi_expr_bool_and(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_bool(val);
zend_ffi_expr_bool(op2);
if (val->kind == ZEND_FFI_VAL_INT32 && op2->kind == ZEND_FFI_VAL_INT32) {
val->i64 = val->i64 && op2->i64;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
}
/* }}} */
void zend_ffi_expr_bw_or(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_int_math(val, op2, |);
}
/* }}} */
void zend_ffi_expr_bw_xor(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_int_math(val, op2, ^);
}
/* }}} */
void zend_ffi_expr_bw_and(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_int_math(val, op2, &);
}
/* }}} */
void zend_ffi_expr_is_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_cmp(val, op2, ==);
}
/* }}} */
void zend_ffi_expr_is_not_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_cmp(val, op2, !=);
}
/* }}} */
void zend_ffi_expr_is_less(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_cmp(val, op2, <);
}
/* }}} */
void zend_ffi_expr_is_greater(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_cmp(val, op2, >);
}
/* }}} */
void zend_ffi_expr_is_less_or_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_cmp(val, op2, <=);
}
/* }}} */
void zend_ffi_expr_is_greater_or_equal(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_cmp(val, op2, >=);
}
/* }}} */
void zend_ffi_expr_shift_left(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_int_math(val, op2, <<);
}
/* }}} */
void zend_ffi_expr_shift_right(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_int_math(val, op2, >>);
}
/* }}} */
void zend_ffi_expr_add(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_math(val, op2, +);
}
/* }}} */
void zend_ffi_expr_sub(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_math(val, op2, -);
}
/* }}} */
void zend_ffi_expr_mul(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_math(val, op2, *);
}
/* }}} */
void zend_ffi_expr_div(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_math(val, op2, /);
}
/* }}} */
void zend_ffi_expr_mod(zend_ffi_val *val, zend_ffi_val *op2) /* {{{ */
{
zend_ffi_expr_int_math(val, op2, %); // ???
}
/* }}} */
void zend_ffi_expr_cast(zend_ffi_val *val, zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_finalize_type(dcl);
switch (ZEND_FFI_TYPE(dcl->type)->kind) {
case ZEND_FFI_TYPE_FLOAT:
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
val->kind = ZEND_FFI_VAL_FLOAT;
val->d = val->u64;
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
val->kind = ZEND_FFI_VAL_FLOAT;
val->d = val->i64;
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->kind = ZEND_FFI_VAL_FLOAT;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->kind = ZEND_FFI_VAL_FLOAT;
val->d = val->ch;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
break;
case ZEND_FFI_TYPE_DOUBLE:
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
val->kind = ZEND_FFI_VAL_DOUBLE;
val->d = val->u64;
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
val->kind = ZEND_FFI_VAL_DOUBLE;
val->d = val->i64;
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->kind = ZEND_FFI_VAL_DOUBLE;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->kind = ZEND_FFI_VAL_DOUBLE;
val->d = val->ch;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
break;
#ifdef HAVE_LONG_DOUBLE
case ZEND_FFI_TYPE_LONGDOUBLE:
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
val->d = val->u64;
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
val->d = val->i64;
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
val->d = val->ch;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
break;
#endif
case ZEND_FFI_TYPE_UINT8:
case ZEND_FFI_TYPE_UINT16:
case ZEND_FFI_TYPE_UINT32:
case ZEND_FFI_TYPE_BOOL:
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
val->kind = ZEND_FFI_VAL_UINT32;
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = val->d;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = val->ch;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
break;
case ZEND_FFI_TYPE_SINT8:
case ZEND_FFI_TYPE_SINT16:
case ZEND_FFI_TYPE_SINT32:
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
val->kind = ZEND_FFI_VAL_INT32;
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->kind = ZEND_FFI_VAL_INT32;
val->i64 = val->d;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->kind = ZEND_FFI_VAL_INT32;
val->i64 = val->ch;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
break;
case ZEND_FFI_TYPE_UINT64:
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
val->kind = ZEND_FFI_VAL_UINT64;
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->kind = ZEND_FFI_VAL_UINT64;
val->u64 = val->d;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->kind = ZEND_FFI_VAL_UINT64;
val->u64 = val->ch;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
break;
case ZEND_FFI_TYPE_SINT64:
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
val->kind = ZEND_FFI_VAL_CHAR;
val->ch = val->u64;
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
val->kind = ZEND_FFI_VAL_CHAR;
val->ch = val->i64;
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->kind = ZEND_FFI_VAL_CHAR;
val->ch = val->d;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
break;
case ZEND_FFI_TYPE_CHAR:
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
val->kind = ZEND_FFI_VAL_UINT32;
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = val->d;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = val->ch;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
break;
default:
val->kind = ZEND_FFI_VAL_ERROR;
break;
}
zend_ffi_type_dtor(dcl->type);
}
/* }}} */
void zend_ffi_expr_plus(zend_ffi_val *val) /* {{{ */
{
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
}
/* }}} */
void zend_ffi_expr_neg(zend_ffi_val *val) /* {{{ */
{
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
val->u64 = -val->u64;
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
val->i64 = -val->i64;
} else if (val->kind == ZEND_FFI_VAL_FLOAT || val->kind == ZEND_FFI_VAL_DOUBLE || val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->d = -val->d;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->ch = -val->ch;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
}
/* }}} */
void zend_ffi_expr_bw_not(zend_ffi_val *val) /* {{{ */
{
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_UINT64) {
val->u64 = ~val->u64;
} else if (val->kind == ZEND_FFI_VAL_INT32 || val->kind == ZEND_FFI_VAL_INT64) {
val->i64 = ~val->i64;
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->ch = ~val->ch;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
}
/* }}} */
void zend_ffi_expr_bool_not(zend_ffi_val *val) /* {{{ */
{
zend_ffi_expr_bool(val);
if (val->kind == ZEND_FFI_VAL_INT32) {
val->i64 = !val->i64;
}
}
/* }}} */
void zend_ffi_expr_sizeof_val(zend_ffi_val *val) /* {{{ */
{
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT32) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_uint32.size;
} else if (val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT64) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_uint64.size;
} else if (val->kind == ZEND_FFI_VAL_FLOAT) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_float.size;
} else if (val->kind == ZEND_FFI_VAL_DOUBLE) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_double.size;
} else if (val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->kind = ZEND_FFI_VAL_UINT32;
#ifdef _WIN32
val->u64 = zend_ffi_type_double.size;
#else
val->u64 = zend_ffi_type_long_double.size;
#endif
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_char.size;
} else if (val->kind == ZEND_FFI_VAL_STRING) {
if (memchr(val->str, '\\', val->len)) {
// TODO: support for escape sequences ???
val->kind = ZEND_FFI_VAL_ERROR;
} else {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = val->len + 1;
}
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
}
/* }}} */
void zend_ffi_expr_sizeof_type(zend_ffi_val *val, zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_type *type;
zend_ffi_finalize_type(dcl);
type = ZEND_FFI_TYPE(dcl->type);
val->kind = (type->size > 0xffffffff) ? ZEND_FFI_VAL_UINT64 : ZEND_FFI_VAL_UINT32;
val->u64 = type->size;
zend_ffi_type_dtor(dcl->type);
}
/* }}} */
void zend_ffi_expr_alignof_val(zend_ffi_val *val) /* {{{ */
{
if (val->kind == ZEND_FFI_VAL_UINT32 || val->kind == ZEND_FFI_VAL_INT32) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_uint32.align;
} else if (val->kind == ZEND_FFI_VAL_UINT64 || val->kind == ZEND_FFI_VAL_INT64) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_uint64.align;
} else if (val->kind == ZEND_FFI_VAL_FLOAT) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_float.align;
} else if (val->kind == ZEND_FFI_VAL_DOUBLE) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_double.align;
#ifdef HAVE_LONG_DOUBLE
} else if (val->kind == ZEND_FFI_VAL_LONG_DOUBLE) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_long_double.align;
#endif
} else if (val->kind == ZEND_FFI_VAL_CHAR) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = zend_ffi_type_char.size;
} else if (val->kind == ZEND_FFI_VAL_STRING) {
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = _Alignof(char*);
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
}
/* }}} */
void zend_ffi_expr_alignof_type(zend_ffi_val *val, zend_ffi_dcl *dcl) /* {{{ */
{
zend_ffi_finalize_type(dcl);
val->kind = ZEND_FFI_VAL_UINT32;
val->u64 = ZEND_FFI_TYPE(dcl->type)->align;
zend_ffi_type_dtor(dcl->type);
}
/* }}} */
void zend_ffi_val_number(zend_ffi_val *val, int base, const char *str, size_t str_len) /* {{{ */
{
int u = 0;
int l = 0;
if (str[str_len-1] == 'u' || str[str_len-1] == 'U') {
u = 1;
if (str[str_len-2] == 'l' || str[str_len-2] == 'L') {
l = 1;
if (str[str_len-3] == 'l' || str[str_len-3] == 'L') {
l = 2;
}
}
} else if (str[str_len-1] == 'l' || str[str_len-1] == 'L') {
l = 1;
if (str[str_len-2] == 'l' || str[str_len-2] == 'L') {
l = 2;
if (str[str_len-3] == 'u' || str[str_len-3] == 'U') {
u = 1;
}
} else if (str[str_len-2] == 'u' || str[str_len-2] == 'U') {
u = 1;
}
}
if (u) {
val->u64 = strtoull(str, NULL, base);
if (l == 0) {
val->kind = ZEND_FFI_VAL_UINT32;
} else if (l == 1) {
val->kind = (sizeof(long) == 4) ? ZEND_FFI_VAL_UINT32 : ZEND_FFI_VAL_UINT64;
} else if (l == 2) {
val->kind = ZEND_FFI_VAL_UINT64;
}
} else {
val->i64 = strtoll(str, NULL, base);
if (l == 0) {
val->kind = ZEND_FFI_VAL_INT32;
} else if (l == 1) {
val->kind = (sizeof(long) == 4) ? ZEND_FFI_VAL_INT32 : ZEND_FFI_VAL_INT64;
} else if (l == 2) {
val->kind = ZEND_FFI_VAL_INT64;
}
}
}
/* }}} */
void zend_ffi_val_float_number(zend_ffi_val *val, const char *str, size_t str_len) /* {{{ */
{
val->d = strtold(str, NULL);
if (str[str_len-1] == 'f' || str[str_len-1] == 'F') {
val->kind = ZEND_FFI_VAL_FLOAT;
} else if (str[str_len-1] == 'l' || str[str_len-1] == 'L') {
val->kind = ZEND_FFI_VAL_LONG_DOUBLE;
} else {
val->kind = ZEND_FFI_VAL_DOUBLE;
}
}
/* }}} */
void zend_ffi_val_string(zend_ffi_val *val, const char *str, size_t str_len) /* {{{ */
{
if (str[0] != '\"') {
val->kind = ZEND_FFI_VAL_ERROR;
} else {
val->kind = ZEND_FFI_VAL_STRING;
val->str = str + 1;
val->len = str_len - 2;
}
}
/* }}} */
void zend_ffi_val_character(zend_ffi_val *val, const char *str, size_t str_len) /* {{{ */
{
int n;
if (str[0] != '\'') {
val->kind = ZEND_FFI_VAL_ERROR;
} else {
val->kind = ZEND_FFI_VAL_CHAR;
if (str_len == 3) {
val->ch = str[1];
} else if (str[1] == '\\') {
if (str[2] == 'a') {
} else if (str[2] == 'b' && str_len == 4) {
val->ch = '\b';
} else if (str[2] == 'f' && str_len == 4) {
val->ch = '\f';
} else if (str[2] == 'n' && str_len == 4) {
val->ch = '\n';
} else if (str[2] == 'r' && str_len == 4) {
val->ch = '\r';
} else if (str[2] == 't' && str_len == 4) {
val->ch = '\t';
} else if (str[2] == 'v' && str_len == 4) {
val->ch = '\v';
} else if (str[2] >= '0' && str[2] <= '7') {
n = str[2] - '0';
if (str[3] >= '0' && str[3] <= '7') {
n = n * 8 + (str[3] - '0');
if ((str[4] >= '0' && str[4] <= '7') && str_len == 6) {
n = n * 8 + (str[4] - '0');
} else if (str_len != 5) {
val->kind = ZEND_FFI_VAL_ERROR;
}
} else if (str_len != 4) {
val->kind = ZEND_FFI_VAL_ERROR;
}
if (n <= 0xff) {
val->ch = n;
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
} else if (str[2] == 'x') {
if (str[3] >= '0' && str[3] <= '9') {
n = str[3] - '0';
} else if (str[3] >= 'A' && str[3] <= 'F') {
n = str[3] - 'A';
} else if (str[3] >= 'a' && str[3] <= 'f') {
n = str[3] - 'a';
} else {
val->kind = ZEND_FFI_VAL_ERROR;
return;
}
if ((str[4] >= '0' && str[4] <= '9') && str_len == 6) {
n = n * 16 + (str[4] - '0');
} else if ((str[4] >= 'A' && str[4] <= 'F') && str_len == 6) {
n = n * 16 + (str[4] - 'A');
} else if ((str[4] >= 'a' && str[4] <= 'f') && str_len == 6) {
n = n * 16 + (str[4] - 'a');
} else if (str_len != 5) {
val->kind = ZEND_FFI_VAL_ERROR;
return;
}
val->ch = n;
} else if (str_len == 4) {
val->ch = str[2];
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
} else {
val->kind = ZEND_FFI_VAL_ERROR;
}
}
}
/* }}} */