Skip to content

Improve FCC API #9840

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -3856,6 +3856,7 @@ ZEND_API bool zend_is_callable_at_frame(
fcc->called_scope = NULL;
fcc->function_handler = NULL;
fcc->object = NULL;
fcc->closure = NULL;

again:
switch (Z_TYPE_P(callable)) {
Expand Down Expand Up @@ -3929,6 +3930,7 @@ ZEND_API bool zend_is_callable_at_frame(
case IS_OBJECT:
if (Z_OBJ_HANDLER_P(callable, get_closure) && Z_OBJ_HANDLER_P(callable, get_closure)(Z_OBJ_P(callable), &fcc->calling_scope, &fcc->function_handler, &fcc->object, 1) == SUCCESS) {
fcc->called_scope = fcc->calling_scope;
fcc->closure = Z_OBJ_P(callable);
if (fcc == &fcc_local) {
zend_release_fcall_info_cache(fcc);
}
Expand Down Expand Up @@ -4143,6 +4145,24 @@ ZEND_API zend_result zend_fcall_info_call(zend_fcall_info *fci, zend_fcall_info_
}
/* }}} */

ZEND_API void zend_get_callable_zval_from_fcc(const zend_fcall_info_cache *fcc, zval *callable)
{
if (fcc->closure) {
ZVAL_OBJ_COPY(callable, fcc->closure);
} else if (fcc->function_handler->common.scope) {
array_init(callable);
if (fcc->object) {
GC_ADDREF(fcc->object);
add_next_index_object(callable, fcc->object);
} else {
add_next_index_str(callable, zend_string_copy(fcc->calling_scope->name));
}
add_next_index_str(callable, zend_string_copy(fcc->function_handler->common.function_name));
} else {
ZVAL_STR_COPY(callable, fcc->function_handler->common.function_name);
}
}

ZEND_API const char *zend_get_module_version(const char *module_name) /* {{{ */
{
zend_string *lname;
Expand Down
61 changes: 60 additions & 1 deletion Zend/zend_API.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ typedef struct _zend_fcall_info_cache {
zend_function *function_handler;
zend_class_entry *calling_scope;
zend_class_entry *called_scope;
zend_object *object;
zend_object *object; /* Instance of object for method calls */
zend_object *closure; /* Closure reference, only if the callable *is* the object */
} zend_fcall_info_cache;

#define ZEND_NS_NAME(ns, name) ns "\\" name
Expand Down Expand Up @@ -327,6 +328,7 @@ typedef struct _zend_fcall_info_cache {
zend_class_backed_enum_table(ce)

#define ZEND_FCI_INITIALIZED(fci) ((fci).size != 0)
#define ZEND_FCC_INITIALIZED(fcc) ((fcc).function_handler != NULL)

ZEND_API int zend_next_free_module(void);

Expand Down Expand Up @@ -728,6 +730,57 @@ ZEND_API void zend_fcall_info_argn(zend_fcall_info *fci, uint32_t argc, ...);
*/
ZEND_API zend_result zend_fcall_info_call(zend_fcall_info *fci, zend_fcall_info_cache *fcc, zval *retval, zval *args);

/* Zend FCC API to store and handle PHP userland functions */
static zend_always_inline bool zend_fcc_equals(const zend_fcall_info_cache* a, const zend_fcall_info_cache* b)
{
return a->function_handler == b->function_handler
&& a->object == b->object
&& a->calling_scope == b->calling_scope
&& a->closure == b->closure
;
}

static zend_always_inline void zend_fcc_addref(zend_fcall_info_cache *fcc)
{
if (fcc->object) {
GC_ADDREF(fcc->object);
}
if (fcc->closure) {
GC_ADDREF(fcc->closure);
}
}

static zend_always_inline void zend_fcc_dup(/* restrict */ zend_fcall_info_cache *dest, const zend_fcall_info_cache *src)
{
memcpy(dest, src, sizeof(zend_fcall_info_cache));
zend_fcc_addref(dest);
}

static zend_always_inline void zend_fcc_dtor(zend_fcall_info_cache *fcc)
{
if (fcc->object) {
OBJ_RELEASE(fcc->object);
}
if (fcc->closure) {
OBJ_RELEASE(fcc->closure);
}
memcpy(fcc, &empty_fcall_info_cache, sizeof(zend_fcall_info_cache));
}

ZEND_API void zend_get_callable_zval_from_fcc(const zend_fcall_info_cache *fcc, zval *callable);

/* Moved out of zend_gc.h because zend_fcall_info_cache is an unknown type in that header */
static zend_always_inline void zend_get_gc_buffer_add_fcc(zend_get_gc_buffer *gc_buffer, zend_fcall_info_cache *fcc)
{
ZEND_ASSERT(ZEND_FCC_INITIALIZED(*fcc));
if (fcc->object) {
zend_get_gc_buffer_add_obj(gc_buffer, fcc->object);
}
if (fcc->closure) {
zend_get_gc_buffer_add_obj(gc_buffer, fcc->closure);
}
}

/* Can only return FAILURE if EG(active) is false during late engine shutdown.
* If the call or call setup throws, EG(exception) will be set and the retval
* will be UNDEF. Otherwise, the retval will be a non-UNDEF value. */
Expand All @@ -750,6 +803,12 @@ ZEND_API void zend_call_known_function(
zend_function *fn, zend_object *object, zend_class_entry *called_scope, zval *retval_ptr,
uint32_t param_count, zval *params, HashTable *named_params);

static zend_always_inline void zend_call_known_fcc(
zend_fcall_info_cache *fcc, zval *retval_ptr, uint32_t param_count, zval *params, HashTable *named_params)
{
zend_call_known_function(fcc->function_handler, fcc->object, fcc->called_scope, retval_ptr, param_count, params, named_params);
}

/* Call the provided zend_function instance method on an object. */
static zend_always_inline void zend_call_known_instance_method(
zend_function *fn, zend_object *object, zval *retval_ptr,
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ ZEND_API zend_class_entry *(*zend_autoload)(zend_string *name, zend_string *lc_n

/* true globals */
ZEND_API const zend_fcall_info empty_fcall_info = {0};
ZEND_API const zend_fcall_info_cache empty_fcall_info_cache = { NULL, NULL, NULL, NULL };
ZEND_API const zend_fcall_info_cache empty_fcall_info_cache = {0};

#ifdef ZEND_WIN32
ZEND_TLS HANDLE tq_timer = NULL;
Expand Down
64 changes: 26 additions & 38 deletions ext/libxml/libxml.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,7 @@ static PHP_GINIT_FUNCTION(libxml)
ZVAL_UNDEF(&libxml_globals->stream_context);
libxml_globals->error_buffer.s = NULL;
libxml_globals->error_list = NULL;
ZVAL_NULL(&libxml_globals->entity_loader.callback);
libxml_globals->entity_loader.fci.size = 0;
libxml_globals->entity_loader_disabled = 0;
libxml_globals->entity_loader_callback = empty_fcall_info_cache;
}

/* Channel libxml file io layer through the PHP streams subsystem.
Expand Down Expand Up @@ -573,13 +571,9 @@ static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
const char *resource = NULL;
zval *ctxzv, retval;
zval params[3];
int status;
zend_fcall_info *fci;

fci = &LIBXML(entity_loader).fci;

if (fci->size == 0) {
/* no custom user-land callback set up; delegate to original loader */
/* no custom user-land callback set up; delegate to original loader */
if (!ZEND_FCC_INITIALIZED(LIBXML(entity_loader_callback))) {
return _php_libxml_default_entity_loader(URL, ID, context);
}

Expand Down Expand Up @@ -611,24 +605,14 @@ static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,

#undef ADD_NULL_OR_STRING_KEY

fci->retval = &retval;
fci->params = params;
fci->param_count = sizeof(params)/sizeof(*params);
zend_call_known_fcc(&LIBXML(entity_loader_callback), &retval, 3, params, /* named_params */ NULL);

status = zend_call_function(fci, &LIBXML(entity_loader).fcc);
if (status != SUCCESS || Z_ISUNDEF(retval)) {
if (Z_ISUNDEF(retval)) {
php_libxml_ctx_error(context,
"Call to user entity loader callback '%s' has failed",
Z_STRVAL(fci->function_name));
ZSTR_VAL(LIBXML(entity_loader_callback).function_handler->common.function_name));
} else {
/*
retval_ptr = *fci->retval_ptr_ptr;
if (retval_ptr == NULL) {
php_libxml_ctx_error(context,
"Call to user entity loader callback '%s' has failed; "
"probably it has thrown an exception",
fci->function_name);
} else */ if (Z_TYPE(retval) == IS_STRING) {
if (Z_TYPE(retval) == IS_STRING) {
is_string:
resource = Z_STRVAL(retval);
} else if (Z_TYPE(retval) == IS_RESOURCE) {
Expand All @@ -638,7 +622,7 @@ static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
php_libxml_ctx_error(context,
"The user entity loader callback '%s' has returned a "
"resource, but it is not a stream",
Z_STRVAL(fci->function_name));
ZSTR_VAL(LIBXML(entity_loader_callback).function_handler->common.function_name));
} else {
/* TODO: allow storing the encoding in the stream context? */
xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
Expand Down Expand Up @@ -839,9 +823,9 @@ static PHP_RINIT_FUNCTION(libxml)

static PHP_RSHUTDOWN_FUNCTION(libxml)
{
LIBXML(entity_loader).fci.size = 0;
zval_ptr_dtor_nogc(&LIBXML(entity_loader).callback);
ZVAL_NULL(&LIBXML(entity_loader).callback);
if (ZEND_FCC_INITIALIZED(LIBXML(entity_loader_callback))) {
zend_fcc_dtor(&LIBXML(entity_loader_callback));
}

return SUCCESS;
}
Expand Down Expand Up @@ -1061,24 +1045,20 @@ PHP_FUNCTION(libxml_disable_entity_loader)
/* {{{ Changes the default external entity loader */
PHP_FUNCTION(libxml_set_external_entity_loader)
{
zval *callback;
zend_fcall_info fci;
zend_fcall_info_cache fcc;

ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_FUNC_OR_NULL_WITH_ZVAL(fci, fcc, callback)
Z_PARAM_FUNC_OR_NULL(fci, fcc)
ZEND_PARSE_PARAMETERS_END();

if (ZEND_FCI_INITIALIZED(fci)) { /* argument not null */
LIBXML(entity_loader).fci = fci;
LIBXML(entity_loader).fcc = fcc;
} else {
LIBXML(entity_loader).fci.size = 0;
/* Unset old callback if it's defined */
if (ZEND_FCC_INITIALIZED(LIBXML(entity_loader_callback))) {
zend_fcc_dtor(&LIBXML(entity_loader_callback));
}
if (!Z_ISNULL(LIBXML(entity_loader).callback)) {
zval_ptr_dtor_nogc(&LIBXML(entity_loader).callback);
if (ZEND_FCI_INITIALIZED(fci)) { /* argument not null */
zend_fcc_dup(&LIBXML(entity_loader_callback), &fcc);
}
ZVAL_COPY(&LIBXML(entity_loader).callback, callback);
RETURN_TRUE;
}
/* }}} */
Expand All @@ -1087,7 +1067,15 @@ PHP_FUNCTION(libxml_set_external_entity_loader)
PHP_FUNCTION(libxml_get_external_entity_loader)
{
ZEND_PARSE_PARAMETERS_NONE();
RETURN_COPY(&LIBXML(entity_loader).callback);

if (ZEND_FCC_INITIALIZED(LIBXML(entity_loader_callback))) {
zval tmp;
zend_get_callable_zval_from_fcc(&LIBXML(entity_loader_callback), &tmp);
RETVAL_COPY(&tmp);
zval_ptr_dtor(&tmp);
return;
}
RETURN_NULL();
}
/* }}} */

Expand Down
6 changes: 1 addition & 5 deletions ext/libxml/php_libxml.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ ZEND_BEGIN_MODULE_GLOBALS(libxml)
zval stream_context;
smart_str error_buffer;
zend_llist *error_list;
struct _php_libxml_entity_resolver {
zval callback;
zend_fcall_info fci;
zend_fcall_info_cache fcc;
} entity_loader;
zend_fcall_info_cache entity_loader_callback;
bool entity_loader_disabled;
ZEND_END_MODULE_GLOBALS(libxml)

Expand Down
50 changes: 15 additions & 35 deletions ext/spl/spl_iterators.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,6 @@ typedef struct _spl_recursive_it_iterator {
zend_object_iterator intern;
} spl_recursive_it_iterator;

typedef struct _spl_cbfilter_it_intern {
zend_fcall_info fci;
zend_fcall_info_cache fcc;
zend_object *object;
} _spl_cbfilter_it_intern;

typedef struct _spl_dual_it_object {
struct {
zval zobject;
Expand Down Expand Up @@ -143,7 +137,7 @@ typedef struct _spl_dual_it_object {
regex_mode mode;
int use_flags;
} regex;
_spl_cbfilter_it_intern *cbfilter;
zend_fcall_info_cache callback_filter;
} u;
zend_object std;
} spl_dual_it_object;
Expand Down Expand Up @@ -1441,16 +1435,11 @@ static spl_dual_it_object* spl_dual_it_construct(INTERNAL_FUNCTION_PARAMETERS, z
}
case DIT_CallbackFilterIterator:
case DIT_RecursiveCallbackFilterIterator: {
_spl_cbfilter_it_intern *cfi = emalloc(sizeof(*cfi));
cfi->fci.object = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Of", &zobject, ce_inner, &cfi->fci, &cfi->fcc) == FAILURE) {
efree(cfi);
zend_fcall_info fci;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Of", &zobject, ce_inner, &fci, &intern->u.callback_filter) == FAILURE) {
return NULL;
}
Z_TRY_ADDREF(cfi->fci.function_name);
cfi->object = cfi->fcc.object;
if (cfi->object) GC_ADDREF(cfi->object);
intern->u.cbfilter = cfi;
zend_fcc_addref(&intern->u.callback_filter);
break;
}
default:
Expand Down Expand Up @@ -1783,7 +1772,10 @@ PHP_METHOD(RecursiveCallbackFilterIterator, getChildren)

zend_call_method_with_0_params(Z_OBJ(intern->inner.zobject), intern->inner.ce, NULL, "getchildren", &retval);
if (!EG(exception) && Z_TYPE(retval) != IS_UNDEF) {
spl_instantiate_arg_ex2(Z_OBJCE_P(ZEND_THIS), return_value, &retval, &intern->u.cbfilter->fci.function_name);
zval callable;
zend_get_callable_zval_from_fcc(&intern->u.callback_filter, &callable);
spl_instantiate_arg_ex2(Z_OBJCE_P(ZEND_THIS), return_value, &retval, &callable);
zval_ptr_dtor(&callable);
}
zval_ptr_dtor(&retval);
} /* }}} */
Expand Down Expand Up @@ -1819,13 +1811,10 @@ PHP_METHOD(CallbackFilterIterator, accept)
ZVAL_COPY_VALUE(&params[1], &intern->current.key);
ZVAL_COPY_VALUE(&params[2], &intern->inner.zobject);

zend_fcall_info *fci = &intern->u.cbfilter->fci;
zend_fcall_info_cache *fcc = &intern->u.cbfilter->fcc;
fci->retval = return_value;
fci->param_count = 3;
fci->params = params;
zend_fcall_info_cache *fcc = &intern->u.callback_filter;

if (zend_call_function(fci, fcc) != SUCCESS || Z_ISUNDEF_P(return_value)) {
zend_call_known_fcc(fcc, return_value, 3, params, NULL);
if (Z_ISUNDEF_P(return_value)) {
RETURN_FALSE;
}
}
Expand Down Expand Up @@ -2129,14 +2118,8 @@ static void spl_dual_it_free_storage(zend_object *_object)
}

if (object->dit_type == DIT_CallbackFilterIterator || object->dit_type == DIT_RecursiveCallbackFilterIterator) {
if (object->u.cbfilter) {
_spl_cbfilter_it_intern *cbfilter = object->u.cbfilter;
object->u.cbfilter = NULL;
zval_ptr_dtor(&cbfilter->fci.function_name);
if (cbfilter->fci.object) {
OBJ_RELEASE(cbfilter->fci.object);
}
efree(cbfilter);
if (ZEND_FCC_INITIALIZED(object->u.callback_filter)) {
zend_fcc_dtor(&object->u.callback_filter);
}
}

Expand Down Expand Up @@ -2181,11 +2164,8 @@ static HashTable *spl_dual_it_get_gc(zend_object *obj, zval **table, int *n)
break;
case DIT_CallbackFilterIterator:
case DIT_RecursiveCallbackFilterIterator:
if (object->u.cbfilter) {
zend_get_gc_buffer_add_zval(gc_buffer, &object->u.cbfilter->fci.function_name);
if (object->u.cbfilter->fci.object) {
zend_get_gc_buffer_add_obj(gc_buffer, object->u.cbfilter->fci.object);
}
if (ZEND_FCC_INITIALIZED(object->u.callback_filter)) {
zend_get_gc_buffer_add_fcc(gc_buffer, &object->u.callback_filter);
}
break;
}
Expand Down
Loading