2008-07-14 09:49:03 +00:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Zend Engine |
|
|
|
|
+----------------------------------------------------------------------+
|
2019-01-30 09:23:29 +00:00
|
|
|
| Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
|
2008-07-14 09:49:03 +00:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| This source file is subject to version 2.00 of the Zend license, |
|
2013-02-19 04:56:02 +00:00
|
|
|
| that is bundled with this package in the file LICENSE, and is |
|
2008-07-14 09:49:03 +00:00
|
|
|
| available through the world-wide-web at the following url: |
|
|
|
|
| http://www.zend.com/license/2_00.txt. |
|
|
|
|
| If you did not receive a copy of the Zend license and are unable to |
|
|
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
|
|
| license@zend.com so we can mail you a copy immediately. |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Authors: Christian Seiler <chris_se@gmx.net> |
|
2018-11-01 15:20:07 +00:00
|
|
|
| Dmitry Stogov <dmitry@php.net> |
|
2009-01-01 16:22:44 +00:00
|
|
|
| Marcus Boerger <helly@php.net> |
|
2008-07-14 09:49:03 +00:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "zend.h"
|
|
|
|
#include "zend_API.h"
|
|
|
|
#include "zend_closures.h"
|
2009-01-01 16:22:44 +00:00
|
|
|
#include "zend_exceptions.h"
|
2008-12-22 14:11:49 +00:00
|
|
|
#include "zend_interfaces.h"
|
2008-07-14 09:49:03 +00:00
|
|
|
#include "zend_objects.h"
|
|
|
|
#include "zend_objects_API.h"
|
|
|
|
#include "zend_globals.h"
|
2019-10-15 12:30:23 +00:00
|
|
|
#include "zend_closures_arginfo.h"
|
2008-07-14 09:49:03 +00:00
|
|
|
|
|
|
|
typedef struct _zend_closure {
|
2015-05-05 19:06:34 +00:00
|
|
|
zend_object std;
|
|
|
|
zend_function func;
|
|
|
|
zval this_ptr;
|
|
|
|
zend_class_entry *called_scope;
|
2017-06-08 13:52:39 +00:00
|
|
|
zif_handler orig_internal_handler;
|
2008-07-14 09:49:03 +00:00
|
|
|
} zend_closure;
|
|
|
|
|
2008-08-11 08:49:00 +00:00
|
|
|
/* non-static since it needs to be referenced */
|
|
|
|
ZEND_API zend_class_entry *zend_ce_closure;
|
2008-07-14 09:49:03 +00:00
|
|
|
static zend_object_handlers closure_handlers;
|
|
|
|
|
|
|
|
ZEND_METHOD(Closure, __invoke) /* {{{ */
|
|
|
|
{
|
2014-10-03 15:32:46 +00:00
|
|
|
zend_function *func = EX(func);
|
2020-04-06 10:46:52 +00:00
|
|
|
zval *args;
|
|
|
|
uint32_t num_args;
|
|
|
|
HashTable *named_args;
|
2008-07-14 09:49:03 +00:00
|
|
|
|
2020-04-06 10:46:52 +00:00
|
|
|
ZEND_PARSE_PARAMETERS_START(0, -1)
|
|
|
|
Z_PARAM_VARIADIC_WITH_NAMED(args, num_args, named_args)
|
|
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
|
|
|
|
if (call_user_function_named(CG(function_table), NULL, ZEND_THIS, return_value, num_args, args, named_args) == FAILURE) {
|
2008-07-26 13:14:04 +00:00
|
|
|
RETVAL_FALSE;
|
2008-07-14 09:49:03 +00:00
|
|
|
}
|
|
|
|
|
2008-07-26 13:14:04 +00:00
|
|
|
/* destruct the function also, then - we have allocated it in get_method */
|
2018-05-28 13:27:12 +00:00
|
|
|
zend_string_release_ex(func->internal_function.function_name, 0);
|
2008-07-26 13:14:04 +00:00
|
|
|
efree(func);
|
2015-02-21 14:44:51 +00:00
|
|
|
#if ZEND_DEBUG
|
|
|
|
execute_data->func = NULL;
|
|
|
|
#endif
|
2008-07-26 13:14:04 +00:00
|
|
|
}
|
2008-07-14 12:18:23 +00:00
|
|
|
/* }}} */
|
|
|
|
|
2021-01-15 11:30:54 +00:00
|
|
|
static bool zend_valid_closure_binding(
|
2015-10-10 11:39:26 +00:00
|
|
|
zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */
|
|
|
|
{
|
|
|
|
zend_function *func = &closure->func;
|
2021-01-15 11:30:54 +00:00
|
|
|
bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
|
2015-10-10 11:39:26 +00:00
|
|
|
if (newthis) {
|
|
|
|
if (func->common.fn_flags & ZEND_ACC_STATIC) {
|
|
|
|
zend_error(E_WARNING, "Cannot bind an instance to a static closure");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-29 21:48:07 +00:00
|
|
|
if (is_fake_closure && func->common.scope &&
|
2015-10-10 11:39:26 +00:00
|
|
|
!instanceof_function(Z_OBJCE_P(newthis), func->common.scope)) {
|
|
|
|
/* Binding incompatible $this to an internal method is not supported. */
|
2016-03-29 21:48:07 +00:00
|
|
|
zend_error(E_WARNING, "Cannot bind method %s::%s() to object of class %s",
|
2015-10-10 11:39:26 +00:00
|
|
|
ZSTR_VAL(func->common.scope->name),
|
|
|
|
ZSTR_VAL(func->common.function_name),
|
|
|
|
ZSTR_VAL(Z_OBJCE_P(newthis)->name));
|
|
|
|
return 0;
|
|
|
|
}
|
2018-09-29 18:58:17 +00:00
|
|
|
} else if (is_fake_closure && func->common.scope
|
|
|
|
&& !(func->common.fn_flags & ZEND_ACC_STATIC)) {
|
2019-01-29 14:16:51 +00:00
|
|
|
zend_error(E_WARNING, "Cannot unbind $this of method");
|
|
|
|
return 0;
|
2019-08-16 10:55:28 +00:00
|
|
|
} else if (!is_fake_closure && !Z_ISUNDEF(closure->this_ptr)
|
|
|
|
&& (func->common.fn_flags & ZEND_ACC_USES_THIS)) {
|
2019-10-28 12:24:07 +00:00
|
|
|
zend_error(E_WARNING, "Cannot unbind $this of closure using $this");
|
|
|
|
return 0;
|
2015-10-10 11:39:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (scope && scope != func->common.scope && scope->type == ZEND_INTERNAL_CLASS) {
|
|
|
|
/* rebinding to internal class is not allowed */
|
|
|
|
zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s",
|
|
|
|
ZSTR_VAL(scope->name));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-29 21:48:07 +00:00
|
|
|
if (is_fake_closure && scope != func->common.scope) {
|
2020-11-12 14:37:51 +00:00
|
|
|
if (func->common.scope == NULL) {
|
|
|
|
zend_error(E_WARNING, "Cannot rebind scope of closure created from function");
|
|
|
|
} else {
|
|
|
|
zend_error(E_WARNING, "Cannot rebind scope of closure created from method");
|
|
|
|
}
|
2015-10-10 11:39:26 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 13:32:55 +00:00
|
|
|
/* {{{ Call closure, binding to a given object with its class as the scope */
|
2015-01-03 09:22:58 +00:00
|
|
|
ZEND_METHOD(Closure, call)
|
2014-07-29 01:36:17 +00:00
|
|
|
{
|
2018-11-13 23:44:25 +00:00
|
|
|
zval *newthis, closure_result;
|
2014-07-29 01:36:17 +00:00
|
|
|
zend_closure *closure;
|
|
|
|
zend_fcall_info fci;
|
|
|
|
zend_fcall_info_cache fci_cache;
|
2014-08-17 22:47:47 +00:00
|
|
|
zend_function my_function;
|
2015-04-28 04:43:43 +00:00
|
|
|
zend_object *newobj;
|
2021-01-02 19:07:45 +00:00
|
|
|
zend_class_entry *newclass;
|
2014-07-29 01:36:17 +00:00
|
|
|
|
2018-01-11 18:23:52 +00:00
|
|
|
fci.param_count = 0;
|
|
|
|
fci.params = NULL;
|
|
|
|
|
2020-04-06 10:46:52 +00:00
|
|
|
ZEND_PARSE_PARAMETERS_START(1, -1)
|
|
|
|
Z_PARAM_OBJECT(newthis)
|
|
|
|
Z_PARAM_VARIADIC_WITH_NAMED(fci.params, fci.param_count, fci.named_params)
|
|
|
|
ZEND_PARSE_PARAMETERS_END();
|
2015-01-03 09:22:58 +00:00
|
|
|
|
2018-11-15 16:54:19 +00:00
|
|
|
closure = (zend_closure *) Z_OBJ_P(ZEND_THIS);
|
2015-01-03 09:22:58 +00:00
|
|
|
|
2015-04-28 04:43:43 +00:00
|
|
|
newobj = Z_OBJ_P(newthis);
|
2021-01-02 19:07:45 +00:00
|
|
|
newclass = newobj->ce;
|
2015-04-28 04:43:43 +00:00
|
|
|
|
2021-01-02 19:07:45 +00:00
|
|
|
if (!zend_valid_closure_binding(closure, newthis, newclass)) {
|
2015-04-28 04:43:43 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-11 18:23:52 +00:00
|
|
|
if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) {
|
2015-09-01 16:04:00 +00:00
|
|
|
zval new_closure;
|
2021-01-02 19:07:45 +00:00
|
|
|
zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis);
|
2015-09-01 16:04:00 +00:00
|
|
|
closure = (zend_closure *) Z_OBJ(new_closure);
|
|
|
|
fci_cache.function_handler = &closure->func;
|
|
|
|
} else {
|
2018-01-11 18:23:52 +00:00
|
|
|
memcpy(&my_function, &closure->func, closure->func.type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
|
|
|
|
my_function.common.fn_flags &= ~ZEND_ACC_CLOSURE;
|
2015-10-06 20:48:10 +00:00
|
|
|
/* use scope of passed object */
|
2021-01-02 19:07:45 +00:00
|
|
|
my_function.common.scope = newclass;
|
2015-10-06 20:48:15 +00:00
|
|
|
fci_cache.function_handler = &my_function;
|
2015-09-01 16:04:00 +00:00
|
|
|
|
|
|
|
/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
|
2018-10-02 06:10:04 +00:00
|
|
|
if (ZEND_USER_CODE(my_function.type)
|
2021-01-02 19:07:45 +00:00
|
|
|
&& (closure->func.common.scope != newclass
|
2018-10-02 06:10:04 +00:00
|
|
|
|| (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) {
|
2018-10-17 12:52:50 +00:00
|
|
|
void *ptr;
|
|
|
|
|
2018-10-02 06:10:04 +00:00
|
|
|
my_function.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE;
|
2018-10-17 12:52:50 +00:00
|
|
|
ptr = emalloc(sizeof(void*) + my_function.op_array.cache_size);
|
|
|
|
ZEND_MAP_PTR_INIT(my_function.op_array.run_time_cache, ptr);
|
|
|
|
ptr = (char*)ptr + sizeof(void*);
|
|
|
|
ZEND_MAP_PTR_SET(my_function.op_array.run_time_cache, ptr);
|
|
|
|
memset(ptr, 0, my_function.op_array.cache_size);
|
2015-09-01 16:04:00 +00:00
|
|
|
}
|
2015-06-21 14:39:28 +00:00
|
|
|
}
|
|
|
|
|
2021-01-02 19:07:45 +00:00
|
|
|
fci_cache.called_scope = newclass;
|
2018-01-11 18:23:52 +00:00
|
|
|
fci_cache.object = fci.object = newobj;
|
|
|
|
|
|
|
|
fci.size = sizeof(fci);
|
2018-11-13 23:44:25 +00:00
|
|
|
ZVAL_OBJ(&fci.function_name, &closure->std);
|
2018-01-11 18:23:52 +00:00
|
|
|
fci.retval = &closure_result;
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(closure_result) != IS_UNDEF) {
|
2016-09-30 20:08:08 +00:00
|
|
|
if (Z_ISREF(closure_result)) {
|
|
|
|
zend_unwrap_reference(&closure_result);
|
|
|
|
}
|
2014-08-17 19:20:23 +00:00
|
|
|
ZVAL_COPY_VALUE(return_value, &closure_result);
|
2014-07-29 01:36:17 +00:00
|
|
|
}
|
2015-06-21 14:39:28 +00:00
|
|
|
|
2015-09-01 16:04:00 +00:00
|
|
|
if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) {
|
|
|
|
/* copied upon generator creation */
|
2017-10-26 22:28:58 +00:00
|
|
|
GC_DELREF(&closure->std);
|
2019-09-02 11:05:28 +00:00
|
|
|
} else if (ZEND_USER_CODE(my_function.type)
|
2020-06-07 00:38:13 +00:00
|
|
|
&& (fci_cache.function_handler->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE)) {
|
2018-10-17 12:52:50 +00:00
|
|
|
efree(ZEND_MAP_PTR(my_function.op_array.run_time_cache));
|
2015-06-21 14:39:28 +00:00
|
|
|
}
|
2014-07-29 01:36:17 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-09-21 12:45:31 +00:00
|
|
|
static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, zend_object *scope_obj, zend_string *scope_str)
|
2010-04-19 19:45:03 +00:00
|
|
|
{
|
2015-05-05 19:06:34 +00:00
|
|
|
zend_class_entry *ce, *called_scope;
|
2020-09-19 17:04:35 +00:00
|
|
|
zend_closure *closure = (zend_closure *) Z_OBJ_P(zclosure);
|
2010-04-19 19:45:03 +00:00
|
|
|
|
2020-09-21 12:45:31 +00:00
|
|
|
if (scope_obj) {
|
|
|
|
ce = scope_obj->ce;
|
|
|
|
} else if (scope_str) {
|
|
|
|
if (zend_string_equals(scope_str, ZSTR_KNOWN(ZEND_STR_STATIC))) {
|
|
|
|
ce = closure->func.common.scope;
|
|
|
|
} else if ((ce = zend_lookup_class(scope_str)) == NULL) {
|
|
|
|
zend_error(E_WARNING, "Class \"%s\" not found", ZSTR_VAL(scope_str));
|
|
|
|
RETURN_NULL();
|
2011-09-07 06:46:27 +00:00
|
|
|
}
|
2020-09-21 12:45:31 +00:00
|
|
|
} else {
|
|
|
|
ce = NULL;
|
2011-09-07 06:46:27 +00:00
|
|
|
}
|
2010-04-19 19:45:03 +00:00
|
|
|
|
2015-10-10 11:39:26 +00:00
|
|
|
if (!zend_valid_closure_binding(closure, newthis, ce)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-05 19:06:34 +00:00
|
|
|
if (newthis) {
|
|
|
|
called_scope = Z_OBJCE_P(newthis);
|
|
|
|
} else {
|
|
|
|
called_scope = ce;
|
|
|
|
}
|
|
|
|
|
2015-10-06 20:48:15 +00:00
|
|
|
zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
|
2010-04-19 19:45:03 +00:00
|
|
|
}
|
2020-09-19 17:04:35 +00:00
|
|
|
|
|
|
|
/* {{{ Create a closure from another one and bind to another object and scope */
|
|
|
|
ZEND_METHOD(Closure, bind)
|
|
|
|
{
|
2020-09-21 12:45:31 +00:00
|
|
|
zval *zclosure, *newthis;
|
|
|
|
zend_object *scope_obj = NULL;
|
|
|
|
zend_string *scope_str = ZSTR_KNOWN(ZEND_STR_STATIC);
|
|
|
|
|
|
|
|
ZEND_PARSE_PARAMETERS_START(2, 3)
|
|
|
|
Z_PARAM_OBJECT_OF_CLASS(zclosure, zend_ce_closure)
|
|
|
|
Z_PARAM_OBJECT_OR_NULL(newthis)
|
|
|
|
Z_PARAM_OPTIONAL
|
|
|
|
Z_PARAM_OBJ_OR_STR_OR_NULL(scope_obj, scope_str)
|
|
|
|
ZEND_PARSE_PARAMETERS_END();
|
2020-09-19 17:04:35 +00:00
|
|
|
|
2020-09-21 12:45:31 +00:00
|
|
|
do_closure_bind(return_value, zclosure, newthis, scope_obj, scope_str);
|
2020-09-19 17:04:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* {{{ Create a closure from another one and bind to another object and scope */
|
|
|
|
ZEND_METHOD(Closure, bindTo)
|
|
|
|
{
|
2020-09-21 12:45:31 +00:00
|
|
|
zval *newthis;
|
|
|
|
zend_object *scope_obj = NULL;
|
|
|
|
zend_string *scope_str = ZSTR_KNOWN(ZEND_STR_STATIC);
|
|
|
|
|
|
|
|
ZEND_PARSE_PARAMETERS_START(1, 2)
|
|
|
|
Z_PARAM_OBJECT_OR_NULL(newthis)
|
|
|
|
Z_PARAM_OPTIONAL
|
|
|
|
Z_PARAM_OBJ_OR_STR_OR_NULL(scope_obj, scope_str)
|
|
|
|
ZEND_PARSE_PARAMETERS_END();
|
2020-09-19 17:04:35 +00:00
|
|
|
|
2020-09-21 12:45:31 +00:00
|
|
|
do_closure_bind(return_value, getThis(), newthis, scope_obj, scope_str);
|
2020-09-19 17:04:35 +00:00
|
|
|
}
|
2010-04-19 19:45:03 +00:00
|
|
|
|
2017-06-08 13:52:39 +00:00
|
|
|
static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
|
2016-05-15 16:39:47 +00:00
|
|
|
zend_fcall_info fci;
|
|
|
|
zend_fcall_info_cache fcc;
|
2016-07-05 13:43:27 +00:00
|
|
|
zval params[2];
|
2016-05-15 16:39:47 +00:00
|
|
|
|
|
|
|
memset(&fci, 0, sizeof(zend_fcall_info));
|
2017-12-13 05:39:28 +00:00
|
|
|
memset(&fcc, 0, sizeof(zend_fcall_info_cache));
|
2016-05-15 16:39:47 +00:00
|
|
|
|
|
|
|
fci.size = sizeof(zend_fcall_info);
|
|
|
|
fci.retval = return_value;
|
|
|
|
|
2018-08-22 07:43:51 +00:00
|
|
|
fcc.function_handler = (EX(func)->internal_function.fn_flags & ZEND_ACC_STATIC) ?
|
|
|
|
EX(func)->internal_function.scope->__callstatic : EX(func)->internal_function.scope->__call;
|
2020-04-06 10:46:52 +00:00
|
|
|
fci.named_params = NULL;
|
2016-07-05 13:43:27 +00:00
|
|
|
fci.params = params;
|
2016-05-15 16:39:47 +00:00
|
|
|
fci.param_count = 2;
|
|
|
|
ZVAL_STR(&fci.params[0], EX(func)->common.function_name);
|
2017-10-24 14:27:31 +00:00
|
|
|
if (ZEND_NUM_ARGS()) {
|
|
|
|
array_init_size(&fci.params[1], ZEND_NUM_ARGS());
|
|
|
|
zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]);
|
|
|
|
} else {
|
|
|
|
ZVAL_EMPTY_ARRAY(&fci.params[1]);
|
|
|
|
}
|
2016-05-15 16:39:47 +00:00
|
|
|
|
2018-11-15 16:54:19 +00:00
|
|
|
fcc.object = fci.object = Z_OBJ_P(ZEND_THIS);
|
2016-05-15 16:39:47 +00:00
|
|
|
|
|
|
|
zend_call_function(&fci, &fcc);
|
|
|
|
|
|
|
|
zval_ptr_dtor(&fci.params[1]);
|
|
|
|
}
|
2016-07-05 07:31:46 +00:00
|
|
|
/* }}} */
|
2016-05-15 16:39:47 +00:00
|
|
|
|
2020-08-28 13:41:27 +00:00
|
|
|
static zend_result zend_create_closure_from_callable(zval *return_value, zval *callable, char **error) /* {{{ */ {
|
2016-05-15 16:39:47 +00:00
|
|
|
zend_fcall_info_cache fcc;
|
|
|
|
zend_function *mptr;
|
|
|
|
zval instance;
|
2016-07-05 12:53:20 +00:00
|
|
|
zend_internal_function call;
|
2016-05-15 16:39:47 +00:00
|
|
|
|
|
|
|
if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, error)) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
mptr = fcc.function_handler;
|
|
|
|
if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
|
2019-10-29 14:05:59 +00:00
|
|
|
/* For Closure::fromCallable([$closure, "__invoke"]) return $closure. */
|
|
|
|
if (fcc.object && fcc.object->ce == zend_ce_closure
|
|
|
|
&& zend_string_equals_literal(mptr->common.function_name, "__invoke")) {
|
2020-06-17 10:34:04 +00:00
|
|
|
RETVAL_OBJ_COPY(fcc.object);
|
2019-10-29 14:05:59 +00:00
|
|
|
zend_free_trampoline(mptr);
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
2016-05-15 16:39:47 +00:00
|
|
|
|
2019-03-11 12:39:34 +00:00
|
|
|
if (!mptr->common.scope) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
if (mptr->common.fn_flags & ZEND_ACC_STATIC) {
|
|
|
|
if (!mptr->common.scope->__callstatic) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!mptr->common.scope->__call) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-29 14:05:59 +00:00
|
|
|
memset(&call, 0, sizeof(zend_internal_function));
|
2016-05-15 16:39:47 +00:00
|
|
|
call.type = ZEND_INTERNAL_FUNCTION;
|
2018-08-22 07:43:51 +00:00
|
|
|
call.fn_flags = mptr->common.fn_flags & ZEND_ACC_STATIC;
|
2016-05-15 16:39:47 +00:00
|
|
|
call.handler = zend_closure_call_magic;
|
|
|
|
call.function_name = mptr->common.function_name;
|
|
|
|
call.scope = mptr->common.scope;
|
|
|
|
|
|
|
|
zend_free_trampoline(mptr);
|
|
|
|
mptr = (zend_function *) &call;
|
|
|
|
}
|
|
|
|
|
2016-07-05 07:31:46 +00:00
|
|
|
if (fcc.object) {
|
|
|
|
ZVAL_OBJ(&instance, fcc.object);
|
2016-07-05 13:43:27 +00:00
|
|
|
zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, &instance);
|
2016-07-05 07:31:46 +00:00
|
|
|
} else {
|
2016-07-05 13:43:27 +00:00
|
|
|
zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, NULL);
|
2016-07-05 07:31:46 +00:00
|
|
|
}
|
2016-05-15 16:39:47 +00:00
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
2016-07-05 07:31:46 +00:00
|
|
|
/* }}} */
|
2016-05-15 16:39:47 +00:00
|
|
|
|
2020-07-01 13:32:55 +00:00
|
|
|
/* {{{ Create a closure from a callable using the current scope. */
|
2016-05-15 16:39:47 +00:00
|
|
|
ZEND_METHOD(Closure, fromCallable)
|
|
|
|
{
|
|
|
|
zval *callable;
|
|
|
|
char *error = NULL;
|
|
|
|
|
2021-02-24 16:13:11 +00:00
|
|
|
ZEND_PARSE_PARAMETERS_START(1, 1)
|
|
|
|
Z_PARAM_ZVAL(callable)
|
|
|
|
ZEND_PARSE_PARAMETERS_END();
|
2016-05-15 16:39:47 +00:00
|
|
|
|
|
|
|
if (Z_TYPE_P(callable) == IS_OBJECT && instanceof_function(Z_OBJCE_P(callable), zend_ce_closure)) {
|
2016-07-05 07:31:46 +00:00
|
|
|
/* It's already a closure */
|
2020-01-20 09:34:17 +00:00
|
|
|
RETURN_COPY(callable);
|
2016-05-15 16:39:47 +00:00
|
|
|
}
|
|
|
|
|
2020-08-14 08:22:42 +00:00
|
|
|
if (zend_create_closure_from_callable(return_value, callable, &error) == FAILURE) {
|
2016-05-15 16:39:47 +00:00
|
|
|
if (error) {
|
2019-01-07 11:28:51 +00:00
|
|
|
zend_type_error("Failed to create closure from callable: %s", error);
|
2016-05-15 16:39:47 +00:00
|
|
|
efree(error);
|
|
|
|
} else {
|
2019-01-07 11:28:51 +00:00
|
|
|
zend_type_error("Failed to create closure from callable");
|
2016-05-15 16:39:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2015-08-19 11:40:56 +00:00
|
|
|
static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */
|
2008-07-14 09:49:03 +00:00
|
|
|
{
|
2020-05-26 12:10:57 +00:00
|
|
|
zend_throw_error(NULL, "Instantiation of class Closure is not allowed");
|
2008-07-14 09:49:03 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-08-28 13:41:27 +00:00
|
|
|
/* int return due to Object Handler API */
|
2019-10-07 14:57:49 +00:00
|
|
|
static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */
|
2008-07-14 09:49:03 +00:00
|
|
|
{
|
2019-10-07 14:57:49 +00:00
|
|
|
ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2);
|
2021-07-08 19:52:01 +00:00
|
|
|
|
|
|
|
zend_closure *lhs = (zend_closure*) Z_OBJ_P(o1);
|
|
|
|
zend_closure *rhs = (zend_closure*) Z_OBJ_P(o2);
|
|
|
|
|
|
|
|
if (!((lhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE) && (rhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE))) {
|
|
|
|
return ZEND_UNCOMPARABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Z_TYPE(lhs->this_ptr) != Z_TYPE(rhs->this_ptr)) {
|
|
|
|
return ZEND_UNCOMPARABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Z_TYPE(lhs->this_ptr) == IS_OBJECT && Z_OBJ(lhs->this_ptr) != Z_OBJ(rhs->this_ptr)) {
|
|
|
|
return ZEND_UNCOMPARABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lhs->called_scope != rhs->called_scope) {
|
|
|
|
return ZEND_UNCOMPARABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lhs->func.type != rhs->func.type) {
|
|
|
|
return ZEND_UNCOMPARABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lhs->func.common.scope != rhs->func.common.scope) {
|
|
|
|
return ZEND_UNCOMPARABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!zend_string_equals(lhs->func.common.function_name, rhs->func.common.function_name)) {
|
|
|
|
return ZEND_UNCOMPARABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2008-07-14 09:49:03 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {{{ */
|
2008-08-11 08:49:00 +00:00
|
|
|
{
|
2014-03-27 22:11:22 +00:00
|
|
|
zend_closure *closure = (zend_closure *)object;
|
2008-08-11 08:49:00 +00:00
|
|
|
zend_function *invoke = (zend_function*)emalloc(sizeof(zend_function));
|
2015-05-29 09:11:02 +00:00
|
|
|
const uint32_t keep_flags =
|
|
|
|
ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_VARIADIC | ZEND_ACC_HAS_RETURN_TYPE;
|
2008-08-11 08:49:00 +00:00
|
|
|
|
|
|
|
invoke->common = closure->func.common;
|
2015-04-23 14:52:05 +00:00
|
|
|
/* We return ZEND_INTERNAL_FUNCTION, but arg_info representation is the
|
|
|
|
* same as for ZEND_USER_FUNCTION (uses zend_string* instead of char*).
|
|
|
|
* This is not a problem, because ZEND_ACC_HAS_TYPE_HINTS is never set,
|
2015-06-16 08:24:35 +00:00
|
|
|
* and we won't check arguments on internal function. We also set
|
|
|
|
* ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */
|
2008-08-11 08:49:00 +00:00
|
|
|
invoke->type = ZEND_INTERNAL_FUNCTION;
|
2015-10-06 20:59:36 +00:00
|
|
|
invoke->internal_function.fn_flags =
|
|
|
|
ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags);
|
2015-06-16 10:29:17 +00:00
|
|
|
if (closure->func.type != ZEND_INTERNAL_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
|
2015-10-06 20:59:36 +00:00
|
|
|
invoke->internal_function.fn_flags |=
|
|
|
|
ZEND_ACC_USER_ARG_INFO;
|
2015-06-16 10:29:17 +00:00
|
|
|
}
|
2008-08-11 08:49:00 +00:00
|
|
|
invoke->internal_function.handler = ZEND_MN(Closure___invoke);
|
|
|
|
invoke->internal_function.module = 0;
|
|
|
|
invoke->internal_function.scope = zend_ce_closure;
|
2017-03-04 09:39:13 +00:00
|
|
|
invoke->internal_function.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE);
|
2008-08-11 08:49:00 +00:00
|
|
|
return invoke;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-24 08:46:37 +00:00
|
|
|
ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj) /* {{{ */
|
2009-01-03 12:25:59 +00:00
|
|
|
{
|
2020-07-24 08:46:37 +00:00
|
|
|
zend_closure *closure = (zend_closure *) obj;
|
2009-01-03 12:25:59 +00:00
|
|
|
return &closure->func;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
ZEND_API zval* zend_get_closure_this_ptr(zval *obj) /* {{{ */
|
2010-04-19 19:45:03 +00:00
|
|
|
{
|
2014-02-10 06:04:30 +00:00
|
|
|
zend_closure *closure = (zend_closure *)Z_OBJ_P(obj);
|
|
|
|
return &closure->this_ptr;
|
2010-04-19 19:45:03 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
static zend_function *zend_closure_get_method(zend_object **object, zend_string *method, const zval *key) /* {{{ */
|
2008-07-14 09:49:03 +00:00
|
|
|
{
|
2016-03-30 16:31:10 +00:00
|
|
|
if (zend_string_equals_literal_ci(method, ZEND_INVOKE_FUNC_NAME)) {
|
2014-12-13 22:06:14 +00:00
|
|
|
return zend_get_closure_invoke_method(*object);
|
2008-07-14 09:49:03 +00:00
|
|
|
}
|
2016-03-30 16:31:10 +00:00
|
|
|
|
2018-05-31 08:57:22 +00:00
|
|
|
return zend_std_get_method(object, method, key);
|
2008-07-14 09:49:03 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
static void zend_closure_free_storage(zend_object *object) /* {{{ */
|
2008-07-14 09:49:03 +00:00
|
|
|
{
|
|
|
|
zend_closure *closure = (zend_closure *)object;
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
zend_object_std_dtor(&closure->std);
|
2008-07-14 09:49:03 +00:00
|
|
|
|
|
|
|
if (closure->func.type == ZEND_USER_FUNCTION) {
|
Reference dynamic functions through dynamic_defs
Currently, dynamically declared functions and closures are inserted
into the function table under a runtime definition key, and then later
possibly renamed. When opcache is not used and a file containing a
closure is repeatedly included, this leads to a very large memory leak,
as the no longer needed closure declarations will never be freed
(https://bugs.php.net/bug.php?id=76982).
With this patch, dynamic functions are instead stored in a
dynamic_func_defs member on the op_array, which opcodes reference
by index. When the parent op_array is destroyed, the dynamic_func_defs
it contains are also destroyed (unless they are stilled used elsewhere,
e.g. because they have been bound, or are used by a live closure). This
resolves the fundamental part of the leak, though doesn't completely
fix it yet due to some arena allocations.
The main non-obvious change here is to static variable handling:
We can't destroy static_variables_ptr in destroy_op_array, as e.g.
that would clear the static variables in a dynamic function when
the op_array containing it is destroyed. Static variable destruction
is separated out for this reason (we already do static variable
destruction separately for normal functions, so we only need to
handle main scripts).
Closes GH-5595.
2020-05-18 13:46:06 +00:00
|
|
|
/* We don't own the static variables of fake closures. */
|
|
|
|
if (!(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
|
|
|
|
zend_destroy_static_vars(&closure->func.op_array);
|
2021-02-17 13:45:35 +00:00
|
|
|
}
|
2014-12-13 22:06:14 +00:00
|
|
|
destroy_op_array(&closure->func.op_array);
|
2021-04-15 03:40:34 +00:00
|
|
|
} else if (closure->orig_internal_handler == zend_closure_call_magic) {
|
|
|
|
zend_string_release(closure->func.common.function_name);
|
2008-07-14 09:49:03 +00:00
|
|
|
}
|
|
|
|
|
2014-02-10 06:04:30 +00:00
|
|
|
if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
|
2010-04-19 19:45:03 +00:00
|
|
|
zval_ptr_dtor(&closure->this_ptr);
|
|
|
|
}
|
2008-07-14 09:49:03 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
static zend_object *zend_closure_new(zend_class_entry *class_type) /* {{{ */
|
2008-07-14 09:49:03 +00:00
|
|
|
{
|
|
|
|
zend_closure *closure;
|
|
|
|
|
|
|
|
closure = emalloc(sizeof(zend_closure));
|
|
|
|
memset(closure, 0, sizeof(zend_closure));
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
zend_object_std_init(&closure->std, class_type);
|
2014-02-10 06:04:30 +00:00
|
|
|
closure->std.handlers = &closure_handlers;
|
2008-07-22 07:44:41 +00:00
|
|
|
|
2014-02-10 06:04:30 +00:00
|
|
|
return (zend_object*)closure;
|
2008-07-14 09:49:03 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2019-01-31 15:47:58 +00:00
|
|
|
static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */
|
2010-04-19 19:45:03 +00:00
|
|
|
{
|
2019-01-31 15:47:58 +00:00
|
|
|
zend_closure *closure = (zend_closure *)zobject;
|
2010-04-19 19:45:03 +00:00
|
|
|
zval result;
|
|
|
|
|
2015-10-06 20:48:15 +00:00
|
|
|
zend_create_closure(&result, &closure->func,
|
|
|
|
closure->func.common.scope, closure->called_scope, &closure->this_ptr);
|
2014-02-10 06:04:30 +00:00
|
|
|
return Z_OBJ(result);
|
2010-04-19 19:45:03 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2021-01-15 11:30:54 +00:00
|
|
|
int zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */
|
2008-08-14 21:36:56 +00:00
|
|
|
{
|
2021-05-20 06:14:56 +00:00
|
|
|
zend_closure *closure = (zend_closure*)obj;
|
|
|
|
|
2008-08-14 21:36:56 +00:00
|
|
|
*fptr_ptr = &closure->func;
|
2015-05-05 19:06:34 +00:00
|
|
|
*ce_ptr = closure->called_scope;
|
2008-08-14 21:36:56 +00:00
|
|
|
|
2016-04-16 13:15:44 +00:00
|
|
|
if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
|
|
|
|
*obj_ptr = Z_OBJ(closure->this_ptr);
|
|
|
|
} else {
|
|
|
|
*obj_ptr = NULL;
|
2008-08-14 21:36:56 +00:00
|
|
|
}
|
2016-04-16 13:15:44 +00:00
|
|
|
|
2008-08-14 21:36:56 +00:00
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-08-28 13:41:27 +00:00
|
|
|
/* *is_temp is int due to Object Handler API */
|
2019-01-31 15:47:58 +00:00
|
|
|
static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) /* {{{ */
|
2009-01-01 16:22:44 +00:00
|
|
|
{
|
2019-01-31 15:47:58 +00:00
|
|
|
zend_closure *closure = (zend_closure *)object;
|
2014-02-10 06:04:30 +00:00
|
|
|
zval val;
|
2009-01-01 16:22:44 +00:00
|
|
|
struct _zend_arg_info *arg_info = closure->func.common.arg_info;
|
2015-04-16 08:01:55 +00:00
|
|
|
HashTable *debug_info;
|
2021-01-15 11:30:54 +00:00
|
|
|
bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
|
2015-04-16 08:01:55 +00:00
|
|
|
|
|
|
|
*is_temp = 1;
|
2009-01-01 16:22:44 +00:00
|
|
|
|
2017-09-19 23:25:56 +00:00
|
|
|
debug_info = zend_new_array(8);
|
2009-08-10 15:18:13 +00:00
|
|
|
|
2015-04-16 08:01:55 +00:00
|
|
|
if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
|
2020-07-07 08:11:34 +00:00
|
|
|
zval *var;
|
2021-05-24 05:26:56 +00:00
|
|
|
zend_string *key;
|
|
|
|
HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
|
|
|
|
|
|
|
|
array_init(&val);
|
|
|
|
|
|
|
|
ZEND_HASH_FOREACH_STR_KEY_VAL(static_variables, key, var) {
|
|
|
|
zval copy;
|
|
|
|
|
2020-07-07 08:11:34 +00:00
|
|
|
if (Z_TYPE_P(var) == IS_CONSTANT_AST) {
|
2021-05-24 05:26:56 +00:00
|
|
|
ZVAL_STRING(©, "<constant ast>");
|
|
|
|
} else {
|
|
|
|
if (Z_ISREF_P(var) && Z_REFCOUNT_P(var) == 1) {
|
|
|
|
var = Z_REFVAL_P(var);
|
|
|
|
}
|
|
|
|
ZVAL_COPY(©, var);
|
2020-07-07 08:11:34 +00:00
|
|
|
}
|
2021-05-24 05:26:56 +00:00
|
|
|
|
|
|
|
zend_hash_add_new(Z_ARRVAL(val), key, ©);
|
2020-07-07 08:11:34 +00:00
|
|
|
} ZEND_HASH_FOREACH_END();
|
2021-05-24 05:26:56 +00:00
|
|
|
|
|
|
|
if (zend_hash_num_elements(Z_ARRVAL(val))) {
|
|
|
|
zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_STATIC), &val);
|
|
|
|
} else {
|
|
|
|
zval_ptr_dtor(&val);
|
|
|
|
}
|
2009-01-01 16:22:44 +00:00
|
|
|
}
|
|
|
|
|
2015-04-16 08:01:55 +00:00
|
|
|
if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
|
|
|
|
Z_ADDREF(closure->this_ptr);
|
2017-03-04 09:39:13 +00:00
|
|
|
zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_THIS), &closure->this_ptr);
|
2015-04-16 08:01:55 +00:00
|
|
|
}
|
2010-04-19 19:45:03 +00:00
|
|
|
|
2015-04-16 08:01:55 +00:00
|
|
|
if (arg_info &&
|
|
|
|
(closure->func.common.num_args ||
|
|
|
|
(closure->func.common.fn_flags & ZEND_ACC_VARIADIC))) {
|
|
|
|
uint32_t i, num_args, required = closure->func.common.required_num_args;
|
2009-08-10 15:18:13 +00:00
|
|
|
|
2015-04-16 08:01:55 +00:00
|
|
|
array_init(&val);
|
2009-08-10 15:18:13 +00:00
|
|
|
|
2015-04-16 08:01:55 +00:00
|
|
|
num_args = closure->func.common.num_args;
|
|
|
|
if (closure->func.common.fn_flags & ZEND_ACC_VARIADIC) {
|
|
|
|
num_args++;
|
|
|
|
}
|
|
|
|
for (i = 0; i < num_args; i++) {
|
|
|
|
zend_string *name;
|
|
|
|
zval info;
|
2021-07-09 08:22:14 +00:00
|
|
|
ZEND_ASSERT(arg_info->name && "Argument should have name");
|
|
|
|
if (zstr_args) {
|
|
|
|
name = zend_strpprintf(0, "%s$%s",
|
|
|
|
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
|
|
|
|
ZSTR_VAL(arg_info->name));
|
2015-04-16 08:01:55 +00:00
|
|
|
} else {
|
2021-07-09 08:22:14 +00:00
|
|
|
name = zend_strpprintf(0, "%s$%s",
|
2019-09-20 15:01:19 +00:00
|
|
|
ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
|
2021-07-09 08:22:14 +00:00
|
|
|
((zend_internal_arg_info*)arg_info)->name);
|
2009-01-01 16:22:44 +00:00
|
|
|
}
|
2015-04-16 08:01:55 +00:00
|
|
|
ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "<optional>" : "<required>"));
|
|
|
|
zend_hash_update(Z_ARRVAL(val), name, &info);
|
2018-05-28 13:27:12 +00:00
|
|
|
zend_string_release_ex(name, 0);
|
2015-04-16 08:01:55 +00:00
|
|
|
arg_info++;
|
2009-01-01 16:22:44 +00:00
|
|
|
}
|
2015-04-16 08:01:55 +00:00
|
|
|
zend_hash_str_update(debug_info, "parameter", sizeof("parameter")-1, &val);
|
2009-01-01 16:22:44 +00:00
|
|
|
}
|
2009-08-10 15:18:13 +00:00
|
|
|
|
2015-04-16 08:01:55 +00:00
|
|
|
return debug_info;
|
2009-01-01 16:22:44 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2019-01-31 15:47:58 +00:00
|
|
|
static HashTable *zend_closure_get_gc(zend_object *obj, zval **table, int *n) /* {{{ */
|
2011-11-02 06:31:33 +00:00
|
|
|
{
|
2019-01-31 15:47:58 +00:00
|
|
|
zend_closure *closure = (zend_closure *)obj;
|
2011-11-02 06:31:33 +00:00
|
|
|
|
2014-02-10 06:04:30 +00:00
|
|
|
*table = Z_TYPE(closure->this_ptr) != IS_NULL ? &closure->this_ptr : NULL;
|
|
|
|
*n = Z_TYPE(closure->this_ptr) != IS_NULL ? 1 : 0;
|
2021-02-20 10:16:37 +00:00
|
|
|
/* Fake closures don't own the static variables they reference. */
|
|
|
|
return (closure->func.type == ZEND_USER_FUNCTION
|
|
|
|
&& !(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) ?
|
2018-10-17 12:52:50 +00:00
|
|
|
ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr) : NULL;
|
2011-11-02 06:31:33 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2020-07-01 13:32:55 +00:00
|
|
|
/* {{{ Private constructor preventing instantiation */
|
2015-08-19 11:40:56 +00:00
|
|
|
ZEND_COLD ZEND_METHOD(Closure, __construct)
|
2009-01-03 19:29:55 +00:00
|
|
|
{
|
2020-05-26 12:10:57 +00:00
|
|
|
zend_throw_error(NULL, "Instantiation of class Closure is not allowed");
|
2009-01-03 19:29:55 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2014-12-13 22:06:14 +00:00
|
|
|
void zend_register_closure_ce(void) /* {{{ */
|
2008-07-14 09:49:03 +00:00
|
|
|
{
|
2021-02-12 22:33:09 +00:00
|
|
|
zend_ce_closure = register_class_Closure();
|
2008-07-14 09:49:03 +00:00
|
|
|
zend_ce_closure->create_object = zend_closure_new;
|
2008-12-22 14:11:49 +00:00
|
|
|
zend_ce_closure->serialize = zend_class_serialize_deny;
|
|
|
|
zend_ce_closure->unserialize = zend_class_unserialize_deny;
|
2008-07-14 09:49:03 +00:00
|
|
|
|
2018-05-31 08:57:22 +00:00
|
|
|
memcpy(&closure_handlers, &std_object_handlers, sizeof(zend_object_handlers));
|
2014-02-10 06:04:30 +00:00
|
|
|
closure_handlers.free_obj = zend_closure_free_storage;
|
2008-07-14 09:49:03 +00:00
|
|
|
closure_handlers.get_constructor = zend_closure_get_constructor;
|
|
|
|
closure_handlers.get_method = zend_closure_get_method;
|
2019-10-07 14:57:49 +00:00
|
|
|
closure_handlers.compare = zend_closure_compare;
|
2010-04-19 19:45:03 +00:00
|
|
|
closure_handlers.clone_obj = zend_closure_clone;
|
2009-01-01 16:22:44 +00:00
|
|
|
closure_handlers.get_debug_info = zend_closure_get_debug_info;
|
2008-08-14 21:36:56 +00:00
|
|
|
closure_handlers.get_closure = zend_closure_get_closure;
|
2011-11-02 06:31:33 +00:00
|
|
|
closure_handlers.get_gc = zend_closure_get_gc;
|
2008-07-14 09:49:03 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2017-06-08 13:52:39 +00:00
|
|
|
static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */
|
2015-10-08 21:45:02 +00:00
|
|
|
{
|
2018-01-11 19:15:45 +00:00
|
|
|
zend_closure *closure = (zend_closure*)ZEND_CLOSURE_OBJECT(EX(func));
|
2015-10-08 21:45:02 +00:00
|
|
|
closure->orig_internal_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
|
|
|
|
OBJ_RELEASE((zend_object*)closure);
|
2015-10-08 22:09:22 +00:00
|
|
|
EX(func) = NULL;
|
2015-10-08 21:45:02 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2021-02-17 13:45:35 +00:00
|
|
|
static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake) /* {{{ */
|
2008-07-14 09:49:03 +00:00
|
|
|
{
|
|
|
|
zend_closure *closure;
|
|
|
|
|
|
|
|
object_init_ex(res, zend_ce_closure);
|
|
|
|
|
2015-10-06 20:59:36 +00:00
|
|
|
closure = (zend_closure *)Z_OBJ_P(res);
|
2008-07-14 09:49:03 +00:00
|
|
|
|
2015-10-06 20:48:15 +00:00
|
|
|
if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) {
|
|
|
|
/* use dummy scope if we're binding an object without specifying a scope */
|
|
|
|
/* maybe it would be better to create one for this purpose */
|
|
|
|
scope = zend_ce_closure;
|
|
|
|
}
|
|
|
|
|
2015-10-08 21:45:02 +00:00
|
|
|
if (func->type == ZEND_USER_FUNCTION) {
|
|
|
|
memcpy(&closure->func, func, sizeof(zend_op_array));
|
|
|
|
closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
|
2018-10-17 12:52:50 +00:00
|
|
|
closure->func.common.fn_flags &= ~ZEND_ACC_IMMUTABLE;
|
|
|
|
|
2021-02-17 13:45:35 +00:00
|
|
|
/* For fake closures, we want to reuse the static variables of the original function. */
|
|
|
|
if (!is_fake) {
|
|
|
|
if (closure->func.op_array.static_variables) {
|
|
|
|
closure->func.op_array.static_variables =
|
|
|
|
zend_array_dup(closure->func.op_array.static_variables);
|
|
|
|
}
|
|
|
|
ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr,
|
|
|
|
&closure->func.op_array.static_variables);
|
2008-07-14 09:49:03 +00:00
|
|
|
}
|
2018-01-15 11:13:55 +00:00
|
|
|
|
|
|
|
/* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */
|
2018-10-17 12:52:50 +00:00
|
|
|
if (!ZEND_MAP_PTR_GET(closure->func.op_array.run_time_cache)
|
2018-01-15 11:13:55 +00:00
|
|
|
|| func->common.scope != scope
|
2018-10-02 06:10:04 +00:00
|
|
|
|| (func->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE)
|
2018-01-15 11:13:55 +00:00
|
|
|
) {
|
2018-10-17 12:52:50 +00:00
|
|
|
void *ptr;
|
|
|
|
|
|
|
|
if (!ZEND_MAP_PTR_GET(func->op_array.run_time_cache)
|
|
|
|
&& (func->common.fn_flags & ZEND_ACC_CLOSURE)
|
|
|
|
&& (func->common.scope == scope ||
|
|
|
|
!(func->common.fn_flags & ZEND_ACC_IMMUTABLE))) {
|
2018-01-15 11:13:55 +00:00
|
|
|
/* If a real closure is used for the first time, we create a shared runtime cache
|
|
|
|
* and remember which scope it is for. */
|
2018-10-17 12:52:50 +00:00
|
|
|
if (func->common.scope != scope) {
|
|
|
|
func->common.scope = scope;
|
|
|
|
}
|
|
|
|
closure->func.op_array.fn_flags &= ~ZEND_ACC_HEAP_RT_CACHE;
|
|
|
|
ptr = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
|
|
|
|
ZEND_MAP_PTR_SET(func->op_array.run_time_cache, ptr);
|
|
|
|
ZEND_MAP_PTR_SET(closure->func.op_array.run_time_cache, ptr);
|
2018-01-15 11:13:55 +00:00
|
|
|
} else {
|
|
|
|
/* Otherwise, we use a non-shared runtime cache */
|
2018-10-02 06:10:04 +00:00
|
|
|
closure->func.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE;
|
2018-10-17 12:52:50 +00:00
|
|
|
ptr = emalloc(sizeof(void*) + func->op_array.cache_size);
|
|
|
|
ZEND_MAP_PTR_INIT(closure->func.op_array.run_time_cache, ptr);
|
|
|
|
ptr = (char*)ptr + sizeof(void*);
|
|
|
|
ZEND_MAP_PTR_SET(closure->func.op_array.run_time_cache, ptr);
|
2018-01-15 11:13:55 +00:00
|
|
|
}
|
2018-10-17 12:52:50 +00:00
|
|
|
memset(ptr, 0, func->op_array.cache_size);
|
2015-06-21 14:39:28 +00:00
|
|
|
}
|
2020-03-02 10:07:57 +00:00
|
|
|
zend_string_addref(closure->func.op_array.function_name);
|
2015-02-20 11:59:30 +00:00
|
|
|
if (closure->func.op_array.refcount) {
|
|
|
|
(*closure->func.op_array.refcount)++;
|
|
|
|
}
|
2015-10-06 20:59:36 +00:00
|
|
|
} else {
|
2015-10-08 21:45:02 +00:00
|
|
|
memcpy(&closure->func, func, sizeof(zend_internal_function));
|
|
|
|
closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
|
|
|
|
/* wrap internal function handler to avoid memory leak */
|
2015-10-08 23:37:51 +00:00
|
|
|
if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) {
|
|
|
|
/* avoid infinity recursion, by taking handler from nested closure */
|
|
|
|
zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func));
|
|
|
|
ZEND_ASSERT(nested->std.ce == zend_ce_closure);
|
|
|
|
closure->orig_internal_handler = nested->orig_internal_handler;
|
|
|
|
} else {
|
|
|
|
closure->orig_internal_handler = closure->func.internal_function.handler;
|
|
|
|
}
|
2015-10-08 21:45:02 +00:00
|
|
|
closure->func.internal_function.handler = zend_closure_internal_handler;
|
2015-10-10 11:39:26 +00:00
|
|
|
if (!func->common.scope) {
|
2015-10-06 20:48:15 +00:00
|
|
|
/* if it's a free function, we won't set scope & this since they're meaningless */
|
|
|
|
this_ptr = NULL;
|
|
|
|
scope = NULL;
|
|
|
|
}
|
|
|
|
}
|
2008-07-14 09:49:03 +00:00
|
|
|
|
2014-06-12 01:07:33 +00:00
|
|
|
ZVAL_UNDEF(&closure->this_ptr);
|
2015-05-06 15:29:05 +00:00
|
|
|
/* Invariant:
|
|
|
|
* If the closure is unscoped or static, it has no bound object. */
|
2010-04-19 19:45:03 +00:00
|
|
|
closure->func.common.scope = scope;
|
2015-05-05 19:06:34 +00:00
|
|
|
closure->called_scope = called_scope;
|
2010-04-19 19:45:03 +00:00
|
|
|
if (scope) {
|
|
|
|
closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
|
2014-02-28 12:06:38 +00:00
|
|
|
if (this_ptr && Z_TYPE_P(this_ptr) == IS_OBJECT && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
|
2020-06-17 10:34:04 +00:00
|
|
|
ZVAL_OBJ_COPY(&closure->this_ptr, Z_OBJ_P(this_ptr));
|
2010-04-19 19:45:03 +00:00
|
|
|
}
|
|
|
|
}
|
2008-07-14 09:49:03 +00:00
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2021-02-17 13:45:35 +00:00
|
|
|
ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr)
|
|
|
|
{
|
|
|
|
zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ false);
|
|
|
|
}
|
|
|
|
|
2015-10-09 16:41:15 +00:00
|
|
|
ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
|
|
|
|
{
|
|
|
|
zend_closure *closure;
|
|
|
|
|
2021-02-17 13:45:35 +00:00
|
|
|
zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true);
|
2015-10-09 16:41:15 +00:00
|
|
|
|
|
|
|
closure = (zend_closure *)Z_OBJ_P(res);
|
|
|
|
closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2021-05-20 06:14:56 +00:00
|
|
|
void zend_closure_from_frame(zval *return_value, zend_execute_data *call) { /* {{{ */
|
|
|
|
zval instance;
|
|
|
|
zend_internal_function trampoline;
|
|
|
|
zend_function *mptr = call->func;
|
|
|
|
|
|
|
|
if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) {
|
|
|
|
RETURN_OBJ(ZEND_CLOSURE_OBJECT(mptr));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
|
|
|
|
if ((ZEND_CALL_INFO(call) & ZEND_CALL_HAS_THIS) &&
|
|
|
|
(Z_OBJCE(call->This) == zend_ce_closure)
|
|
|
|
&& zend_string_equals_literal(mptr->common.function_name, "__invoke")) {
|
|
|
|
zend_free_trampoline(mptr);
|
|
|
|
RETURN_OBJ_COPY(Z_OBJ(call->This));
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&trampoline, 0, sizeof(zend_internal_function));
|
|
|
|
trampoline.type = ZEND_INTERNAL_FUNCTION;
|
|
|
|
trampoline.fn_flags = mptr->common.fn_flags & ZEND_ACC_STATIC;
|
|
|
|
trampoline.handler = zend_closure_call_magic;
|
|
|
|
trampoline.function_name = mptr->common.function_name;
|
|
|
|
trampoline.scope = mptr->common.scope;
|
|
|
|
|
|
|
|
zend_free_trampoline(mptr);
|
|
|
|
mptr = (zend_function *) &trampoline;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_THIS) {
|
|
|
|
ZVAL_OBJ(&instance, Z_OBJ(call->This));
|
|
|
|
|
|
|
|
zend_create_fake_closure(return_value, mptr, mptr->common.scope, Z_OBJCE(instance), &instance);
|
|
|
|
} else {
|
|
|
|
zend_create_fake_closure(return_value, mptr, mptr->common.scope, Z_CE(call->This), NULL);
|
|
|
|
}
|
|
|
|
} /* }}} */
|
|
|
|
|
2015-12-29 10:16:08 +00:00
|
|
|
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */
|
|
|
|
{
|
|
|
|
zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
|
2018-10-17 12:52:50 +00:00
|
|
|
HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
|
2015-12-29 10:16:08 +00:00
|
|
|
zend_hash_update(static_variables, var_name, var);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
2018-08-20 13:10:09 +00:00
|
|
|
void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {{{ */
|
|
|
|
{
|
|
|
|
zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
|
2018-10-17 12:52:50 +00:00
|
|
|
HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
|
2018-08-20 13:10:09 +00:00
|
|
|
zval *var = (zval*)((char*)static_variables->arData + offset);
|
|
|
|
zval_ptr_dtor(var);
|
|
|
|
ZVAL_COPY_VALUE(var, val);
|
|
|
|
}
|
|
|
|
/* }}} */
|