/src/php-src/Zend/zend_closures.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Zend Engine | |
4 | | +----------------------------------------------------------------------+ |
5 | | | Copyright © Zend Technologies Ltd., a subsidiary company of | |
6 | | | Perforce Software, Inc., and Contributors. | |
7 | | +----------------------------------------------------------------------+ |
8 | | | This source file is subject to the Modified BSD License that is | |
9 | | | bundled with this package in the file LICENSE, and is available | |
10 | | | through the World Wide Web at <https://www.php.net/license/>. | |
11 | | | | |
12 | | | SPDX-License-Identifier: BSD-3-Clause | |
13 | | +----------------------------------------------------------------------+ |
14 | | | Authors: Christian Seiler <chris_se@gmx.net> | |
15 | | | Dmitry Stogov <dmitry@php.net> | |
16 | | | Marcus Boerger <helly@php.net> | |
17 | | +----------------------------------------------------------------------+ |
18 | | */ |
19 | | |
20 | | #include "zend.h" |
21 | | #include "zend_API.h" |
22 | | #include "zend_closures.h" |
23 | | #include "zend_exceptions.h" |
24 | | #include "zend_interfaces.h" |
25 | | #include "zend_objects.h" |
26 | | #include "zend_objects_API.h" |
27 | | #include "zend_globals.h" |
28 | | #include "zend_closures_arginfo.h" |
29 | | |
30 | | /* Closure is a PFA */ |
31 | 5.04k | #define ZEND_PARTIAL OBJ_EXTRA_FLAG_PRIV_1 |
32 | | /* Closure is a PFA of a Closure. Rebinding the PFA requires rebinding the inner Closure. */ |
33 | 2.77k | #define ZEND_PARTIAL_OF_CLOSURE OBJ_EXTRA_FLAG_PRIV_2 |
34 | | |
35 | | typedef struct _zend_closure { |
36 | | zend_object std; |
37 | | zend_function func; |
38 | | zend_object *this_ptr; |
39 | | zend_class_entry *called_scope; |
40 | | zif_handler orig_internal_handler; |
41 | | } zend_closure; |
42 | | |
43 | | /* non-static since it needs to be referenced */ |
44 | | ZEND_API zend_class_entry *zend_ce_closure; |
45 | | static zend_object_handlers closure_handlers; |
46 | | |
47 | | static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only); |
48 | | static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr, bool is_fake, uint32_t flags); |
49 | | |
50 | | static inline uint32_t zend_closure_flags(const zend_closure *closure) |
51 | 1.96k | { |
52 | 1.96k | return closure->std.extra_flags & (ZEND_PARTIAL|ZEND_PARTIAL_OF_CLOSURE); |
53 | 1.96k | } |
54 | | |
55 | | static inline bool zend_closure_is_fake(const zend_closure *closure) |
56 | 649 | { |
57 | 649 | return closure->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE; |
58 | 649 | } |
59 | | |
60 | | ZEND_METHOD(Closure, __invoke) /* {{{ */ |
61 | 315 | { |
62 | 315 | zend_function *func = EX(func); |
63 | 315 | zval *args; |
64 | 315 | uint32_t num_args; |
65 | 315 | HashTable *named_args; |
66 | | |
67 | 945 | ZEND_PARSE_PARAMETERS_START(0, -1) |
68 | 945 | Z_PARAM_VARIADIC_WITH_NAMED(args, num_args, named_args) |
69 | 945 | ZEND_PARSE_PARAMETERS_END(); |
70 | | |
71 | 315 | zend_fcall_info_cache fcc = { |
72 | 315 | .closure = Z_OBJ_P(ZEND_THIS), |
73 | 315 | }; |
74 | 315 | zend_closure_get_closure(Z_OBJ_P(ZEND_THIS), &fcc.calling_scope, &fcc.function_handler, &fcc.object, false); |
75 | 315 | fcc.called_scope = fcc.calling_scope; |
76 | 315 | zend_call_known_fcc(&fcc, return_value, num_args, args, named_args); |
77 | | |
78 | | /* destruct the function also, then - we have allocated it in get_method */ |
79 | 315 | zend_string_release_ex(func->internal_function.function_name, 0); |
80 | 315 | efree(func); |
81 | | |
82 | | /* Set the func pointer to NULL. Prior to PHP 8.3, this was only done for debug builds, |
83 | | * because debug builds check certain properties after the call and needed to know this |
84 | | * had been freed. |
85 | | * However, extensions can proxy zend_execute_internal, and it's a bit surprising to have |
86 | | * an invalid func pointer sitting on there, so this was changed in PHP 8.3. |
87 | | */ |
88 | 315 | execute_data->func = NULL; |
89 | 315 | } |
90 | | /* }}} */ |
91 | | |
92 | | static bool zend_valid_closure_binding( |
93 | | zend_closure *closure, zend_object *new_this, zend_class_entry *scope) /* {{{ */ |
94 | 1.90k | { |
95 | 1.90k | zend_function *func = &closure->func; |
96 | | // TODO: rename variable |
97 | 1.90k | bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0 |
98 | 1.74k | || (closure->std.extra_flags & ZEND_PARTIAL); |
99 | 1.90k | if (new_this) { |
100 | 1.60k | if (func->common.fn_flags & ZEND_ACC_STATIC) { |
101 | 76 | zend_error(E_WARNING, "Cannot bind an instance to a static closure, this will be an error in PHP 9"); |
102 | 76 | return false; |
103 | 76 | } |
104 | | |
105 | 1.53k | if (is_fake_closure && func->common.scope && |
106 | 193 | !instanceof_function(new_this->ce, func->common.scope)) { |
107 | | /* Binding incompatible $this to an internal method is not supported. */ |
108 | 24 | zend_error(E_WARNING, "Cannot bind method %s::%s() to object of class %s, this will be an error in PHP 9", |
109 | 24 | ZSTR_VAL(func->common.scope->name), |
110 | 24 | ZSTR_VAL(func->common.function_name), |
111 | 24 | ZSTR_VAL(new_this->ce->name)); |
112 | 24 | return false; |
113 | 24 | } |
114 | 1.53k | } else if (is_fake_closure && func->common.scope |
115 | 26 | && !(func->common.fn_flags & ZEND_ACC_STATIC)) { |
116 | 16 | zend_error(E_WARNING, "Cannot unbind $this of method, this will be an error in PHP 9"); |
117 | 16 | return false; |
118 | 284 | } else if (!is_fake_closure && closure->this_ptr |
119 | 40 | && (func->common.fn_flags & ZEND_ACC_USES_THIS)) { |
120 | 35 | zend_error(E_WARNING, "Cannot unbind $this of closure using $this, this will be an error in PHP 9"); |
121 | 35 | return false; |
122 | 35 | } |
123 | | |
124 | 1.75k | if (scope && scope != func->common.scope && scope->type == ZEND_INTERNAL_CLASS) { |
125 | | /* rebinding to internal class is not allowed */ |
126 | 412 | zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s, this will be an error in PHP 9", |
127 | 412 | ZSTR_VAL(scope->name)); |
128 | 412 | return false; |
129 | 412 | } |
130 | | |
131 | 1.34k | if (is_fake_closure && scope != func->common.scope) { |
132 | 16 | if (func->common.scope == NULL) { |
133 | 4 | zend_error(E_WARNING, "Cannot rebind scope of closure created from function, this will be an error in PHP 9"); |
134 | 12 | } else { |
135 | 12 | zend_error(E_WARNING, "Cannot rebind scope of closure created from method, this will be an error in PHP 9"); |
136 | 12 | } |
137 | 16 | return false; |
138 | 16 | } |
139 | | |
140 | 1.32k | return true; |
141 | 1.34k | } |
142 | | /* }}} */ |
143 | | |
144 | | /* {{{ Call closure, binding to a given object with its class as the scope */ |
145 | | ZEND_METHOD(Closure, call) |
146 | 1.12k | { |
147 | 1.12k | zval closure_result; |
148 | 1.12k | zend_closure *closure; |
149 | 1.12k | zend_fcall_info fci; |
150 | 1.12k | zend_fcall_info_cache fci_cache; |
151 | 1.12k | zend_object *new_this; |
152 | 1.12k | zend_class_entry *newclass; |
153 | | |
154 | 1.12k | fci.param_count = 0; |
155 | 1.12k | fci.params = NULL; |
156 | | |
157 | 3.38k | ZEND_PARSE_PARAMETERS_START(1, -1) |
158 | 4.50k | Z_PARAM_OBJ(new_this) |
159 | 1.12k | Z_PARAM_VARIADIC_WITH_NAMED(fci.params, fci.param_count, fci.named_params) |
160 | 1.12k | ZEND_PARSE_PARAMETERS_END(); |
161 | | |
162 | 1.12k | closure = (zend_closure *) Z_OBJ_P(ZEND_THIS); |
163 | | |
164 | 1.12k | newclass = new_this->ce; |
165 | | |
166 | 1.12k | if (!zend_valid_closure_binding(closure, new_this, newclass)) { |
167 | 422 | return; |
168 | 422 | } |
169 | | |
170 | 700 | fci_cache.called_scope = newclass; |
171 | 700 | fci_cache.object = fci.object = new_this; |
172 | | |
173 | 700 | fci.size = sizeof(fci); |
174 | 700 | fci.consumed_args = 0; |
175 | 700 | ZVAL_OBJ(&fci.function_name, &closure->std); |
176 | 700 | ZVAL_UNDEF(&closure_result); |
177 | 700 | fci.retval = &closure_result; |
178 | | |
179 | 700 | if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) { |
180 | 7 | zval new_closure; |
181 | 7 | zend_create_closure_ex(&new_closure, &closure->func, newclass, |
182 | 7 | closure->called_scope, new_this, |
183 | 7 | zend_closure_is_fake(closure), zend_closure_flags(closure)); |
184 | 7 | closure = (zend_closure *) Z_OBJ(new_closure); |
185 | 7 | fci_cache.function_handler = &closure->func; |
186 | | |
187 | 7 | zend_call_function(&fci, &fci_cache); |
188 | | |
189 | | /* copied upon generator creation */ |
190 | 7 | GC_DELREF(&closure->std); |
191 | 693 | } else { |
192 | 693 | zend_closure *fake_closure; |
193 | 693 | zend_function *my_function; |
194 | | |
195 | 693 | fake_closure = emalloc(sizeof(zend_closure)); |
196 | 693 | memset(&fake_closure->std, 0, sizeof(fake_closure->std)); |
197 | 693 | fake_closure->std.gc.refcount = 1; |
198 | 693 | fake_closure->std.gc.u.type_info = GC_NULL; |
199 | 693 | fake_closure->std.extra_flags = zend_closure_flags(closure); |
200 | 693 | fake_closure->this_ptr = NULL; |
201 | 693 | fake_closure->called_scope = NULL; |
202 | 693 | my_function = &fake_closure->func; |
203 | 693 | if (ZEND_USER_CODE(closure->func.type)) { |
204 | 686 | memcpy(my_function, &closure->func, sizeof(zend_op_array)); |
205 | 686 | } else { |
206 | 7 | memcpy(my_function, &closure->func, sizeof(zend_internal_function)); |
207 | 7 | } |
208 | | /* use scope of passed object */ |
209 | 693 | my_function->common.scope = newclass; |
210 | 693 | if (closure->func.type == ZEND_INTERNAL_FUNCTION) { |
211 | 7 | my_function->internal_function.handler = closure->orig_internal_handler; |
212 | 7 | } |
213 | 693 | fci_cache.function_handler = my_function; |
214 | | |
215 | | /* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */ |
216 | 693 | if (ZEND_USER_CODE(my_function->type) |
217 | 686 | && (closure->func.common.scope != newclass |
218 | 686 | || (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) { |
219 | 686 | void *ptr; |
220 | | |
221 | 686 | my_function->op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE; |
222 | 686 | ptr = emalloc(my_function->op_array.cache_size); |
223 | 686 | ZEND_MAP_PTR_INIT(my_function->op_array.run_time_cache, ptr); |
224 | 686 | memset(ptr, 0, my_function->op_array.cache_size); |
225 | 686 | } |
226 | | |
227 | 693 | zend_call_function(&fci, &fci_cache); |
228 | | |
229 | 693 | if (ZEND_USER_CODE(my_function->type)) { |
230 | 686 | if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE) { |
231 | 686 | efree(ZEND_MAP_PTR(my_function->op_array.run_time_cache)); |
232 | 686 | } |
233 | 686 | } |
234 | 693 | efree_size(fake_closure, sizeof(zend_closure)); |
235 | 693 | } |
236 | | |
237 | 700 | if (Z_TYPE(closure_result) != IS_UNDEF) { |
238 | 699 | if (Z_ISREF(closure_result)) { |
239 | 7 | zend_unwrap_reference(&closure_result); |
240 | 7 | } |
241 | 699 | ZVAL_COPY_VALUE(return_value, &closure_result); |
242 | 699 | } |
243 | 700 | } |
244 | | /* }}} */ |
245 | | |
246 | | static zend_result do_closure_bind(zval *return_value, zval *zclosure, zend_object *new_this, zend_object *scope_obj, zend_string *scope_str) |
247 | 796 | { |
248 | 796 | zend_class_entry *ce, *called_scope; |
249 | 796 | zend_closure *closure = (zend_closure *) Z_OBJ_P(zclosure); |
250 | | |
251 | 796 | if (scope_obj) { |
252 | 34 | ce = scope_obj->ce; |
253 | 762 | } else if (scope_str) { |
254 | 662 | if (zend_string_equals(scope_str, ZSTR_KNOWN(ZEND_STR_STATIC))) { |
255 | 380 | ce = closure->func.common.scope; |
256 | 380 | } else if ((ce = zend_lookup_class(scope_str)) == NULL) { |
257 | 12 | zend_error(E_WARNING, "Class \"%s\" not found", ZSTR_VAL(scope_str)); |
258 | 12 | RETVAL_NULL(); |
259 | 12 | return FAILURE; |
260 | 12 | } |
261 | 662 | } else { |
262 | 100 | ce = NULL; |
263 | 100 | } |
264 | | |
265 | 784 | if (!zend_valid_closure_binding(closure, new_this, ce)) { |
266 | 157 | return FAILURE; |
267 | 157 | } |
268 | | |
269 | 627 | if (new_this) { |
270 | 378 | called_scope = new_this->ce; |
271 | 378 | } else { |
272 | 249 | called_scope = ce; |
273 | 249 | } |
274 | | |
275 | 627 | zend_create_closure_ex(return_value, &closure->func, ce, called_scope, new_this, |
276 | 627 | zend_closure_is_fake(closure), zend_closure_flags(closure)); |
277 | | |
278 | 627 | if (zend_closure_flags(closure) & ZEND_PARTIAL_OF_CLOSURE) { |
279 | | /* Re-bind the inner closure */ |
280 | | |
281 | 23 | zend_closure *new_closure = (zend_closure*)Z_OBJ_P(return_value); |
282 | 23 | HashTable *static_variables = ZEND_MAP_PTR_GET(new_closure->func.op_array.static_variables_ptr); |
283 | 23 | ZEND_ASSERT(static_variables->nNumOfElements > 0); |
284 | 23 | zval *inner = &static_variables->arData[0].val; |
285 | 23 | ZEND_ASSERT(Z_TYPE_P(inner) == IS_OBJECT && Z_OBJCE_P(inner) == zend_ce_closure); |
286 | | |
287 | 23 | zval new_inner; |
288 | 23 | if (do_closure_bind(&new_inner, inner, new_this, scope_obj, scope_str) != SUCCESS) { |
289 | | /* Should not happen, as we have already validated arguments and the |
290 | | * inner closure should have the same constraints. */ |
291 | 0 | ZEND_UNREACHABLE(); |
292 | 0 | zval_ptr_dtor(return_value); |
293 | 0 | ZVAL_NULL(return_value); |
294 | 0 | return FAILURE; |
295 | 0 | } |
296 | | |
297 | 23 | zend_object *garbage = Z_OBJ_P(inner); |
298 | 23 | ZVAL_COPY_VALUE(inner, &new_inner); |
299 | 23 | zend_object_release(garbage); |
300 | 23 | } |
301 | | |
302 | 627 | return SUCCESS; |
303 | 627 | } |
304 | | |
305 | | /* {{{ Create a closure from another one and bind to another object and scope */ |
306 | | ZEND_METHOD(Closure, bind) |
307 | 91 | { |
308 | 91 | zval *zclosure; |
309 | 91 | zend_object *new_this; |
310 | 91 | zend_object *scope_obj = NULL; |
311 | 91 | zend_string *scope_str = ZSTR_KNOWN(ZEND_STR_STATIC); |
312 | | |
313 | 273 | ZEND_PARSE_PARAMETERS_START(2, 3) |
314 | 364 | Z_PARAM_OBJECT_OF_CLASS(zclosure, zend_ce_closure) |
315 | 455 | Z_PARAM_OBJ_OR_NULL(new_this) |
316 | 91 | Z_PARAM_OPTIONAL |
317 | 440 | Z_PARAM_OBJ_OR_STR_OR_NULL(scope_obj, scope_str) |
318 | 440 | ZEND_PARSE_PARAMETERS_END(); |
319 | | |
320 | 91 | do_closure_bind(return_value, zclosure, new_this, scope_obj, scope_str); |
321 | 91 | } |
322 | | |
323 | | /* {{{ Create a closure from another one and bind to another object and scope */ |
324 | | ZEND_METHOD(Closure, bindTo) |
325 | 688 | { |
326 | 688 | zend_object *new_this; |
327 | 688 | zend_object *scope_obj = NULL; |
328 | 688 | zend_string *scope_str = ZSTR_KNOWN(ZEND_STR_STATIC); |
329 | | |
330 | 2.06k | ZEND_PARSE_PARAMETERS_START(1, 2) |
331 | 2.75k | Z_PARAM_OBJ_OR_NULL(new_this) |
332 | 687 | Z_PARAM_OPTIONAL |
333 | 2.46k | Z_PARAM_OBJ_OR_STR_OR_NULL(scope_obj, scope_str) |
334 | 2.46k | ZEND_PARSE_PARAMETERS_END(); |
335 | | |
336 | 682 | do_closure_bind(return_value, ZEND_THIS, new_this, scope_obj, scope_str); |
337 | 682 | } |
338 | | |
339 | | static void zend_copy_parameters_array(const uint32_t param_count, HashTable *argument_array) /* {{{ */ |
340 | 215 | { |
341 | 215 | zval *param_ptr = ZEND_CALL_ARG(EG(current_execute_data), 1); |
342 | | |
343 | 215 | ZEND_ASSERT(param_count <= ZEND_CALL_NUM_ARGS(EG(current_execute_data))); |
344 | | |
345 | 723 | for (uint32_t i = 0; i < param_count; i++) { |
346 | 508 | Z_TRY_ADDREF_P(param_ptr); |
347 | 508 | zend_hash_next_index_insert_new(argument_array, param_ptr); |
348 | 508 | param_ptr++; |
349 | 508 | } |
350 | 215 | } |
351 | | |
352 | 371 | static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ { |
353 | 371 | zend_fcall_info fci; |
354 | 371 | zend_fcall_info_cache fcc; |
355 | 371 | zval params[2]; |
356 | | |
357 | 371 | memset(&fci, 0, sizeof(zend_fcall_info)); |
358 | 371 | memset(&fcc, 0, sizeof(zend_fcall_info_cache)); |
359 | | |
360 | 371 | fci.size = sizeof(zend_fcall_info); |
361 | 371 | fci.retval = return_value; |
362 | | |
363 | 371 | fcc.function_handler = (EX(func)->internal_function.fn_flags & ZEND_ACC_STATIC) ? |
364 | 307 | EX(func)->internal_function.scope->__callstatic : EX(func)->internal_function.scope->__call; |
365 | 371 | fci.named_params = NULL; |
366 | 371 | fci.params = params; |
367 | 371 | fci.param_count = 2; |
368 | 371 | ZVAL_STR(&fci.params[0], EX(func)->common.function_name); |
369 | 371 | if (EX_CALL_INFO() & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS) { |
370 | 40 | zend_string *name; |
371 | 40 | zval *named_param_zval; |
372 | 40 | array_init_size(&fci.params[1], ZEND_NUM_ARGS() + zend_hash_num_elements(EX(extra_named_params))); |
373 | | /* Avoid conversion from packed to mixed later. */ |
374 | 40 | zend_hash_real_init_mixed(Z_ARRVAL(fci.params[1])); |
375 | 40 | zend_copy_parameters_array(ZEND_NUM_ARGS(), Z_ARRVAL(fci.params[1])); |
376 | 240 | ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, named_param_zval) { |
377 | 240 | Z_TRY_ADDREF_P(named_param_zval); |
378 | 240 | zend_hash_add_new(Z_ARRVAL(fci.params[1]), name, named_param_zval); |
379 | 240 | } ZEND_HASH_FOREACH_END(); |
380 | 331 | } else if (ZEND_NUM_ARGS()) { |
381 | 175 | array_init_size(&fci.params[1], ZEND_NUM_ARGS()); |
382 | 175 | zend_copy_parameters_array(ZEND_NUM_ARGS(), Z_ARRVAL(fci.params[1])); |
383 | 175 | } else { |
384 | 156 | ZVAL_EMPTY_ARRAY(&fci.params[1]); |
385 | 156 | } |
386 | | |
387 | 371 | fcc.object = fci.object = Z_OBJ_P(ZEND_THIS); |
388 | 371 | fcc.called_scope = zend_get_called_scope(EG(current_execute_data)); |
389 | | |
390 | 371 | zend_call_function(&fci, &fcc); |
391 | 371 | zend_return_unwrap_ref(EG(current_execute_data), return_value); |
392 | 371 | zval_ptr_dtor(&fci.params[1]); |
393 | 371 | } |
394 | | /* }}} */ |
395 | | |
396 | 772 | static zend_result zend_create_closure_from_callable(zval *return_value, zval *callable, char **error) /* {{{ */ { |
397 | 772 | zend_fcall_info_cache fcc; |
398 | 772 | zend_function *mptr; |
399 | 772 | zend_internal_function call; |
400 | | |
401 | 772 | if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, error)) { |
402 | 103 | return FAILURE; |
403 | 103 | } |
404 | | |
405 | 669 | mptr = fcc.function_handler; |
406 | 669 | if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { |
407 | | /* For Closure::fromCallable([$closure, "__invoke"]) return $closure. */ |
408 | 168 | if (fcc.object && fcc.object->ce == zend_ce_closure |
409 | 44 | && zend_string_equals(mptr->common.function_name, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) { |
410 | 44 | RETVAL_OBJ_COPY(fcc.object); |
411 | 44 | zend_free_trampoline(mptr); |
412 | 44 | return SUCCESS; |
413 | 44 | } |
414 | | |
415 | 124 | if (!mptr->common.scope) { |
416 | 0 | return FAILURE; |
417 | 0 | } |
418 | 124 | if (mptr->common.fn_flags & ZEND_ACC_STATIC) { |
419 | 6 | if (!mptr->common.scope->__callstatic) { |
420 | 0 | return FAILURE; |
421 | 0 | } |
422 | 118 | } else { |
423 | 118 | if (!mptr->common.scope->__call) { |
424 | 0 | return FAILURE; |
425 | 0 | } |
426 | 118 | } |
427 | | |
428 | 124 | memset(&call, 0, sizeof(zend_internal_function)); |
429 | 124 | call.type = ZEND_INTERNAL_FUNCTION; |
430 | 124 | call.fn_flags = mptr->common.fn_flags & (ZEND_ACC_STATIC | ZEND_ACC_DEPRECATED); |
431 | 124 | call.handler = zend_closure_call_magic; |
432 | 124 | call.function_name = mptr->common.function_name; |
433 | 124 | call.scope = mptr->common.scope; |
434 | 124 | call.doc_comment = NULL; |
435 | 124 | call.attributes = mptr->common.attributes; |
436 | | |
437 | 124 | zend_free_trampoline(mptr); |
438 | 124 | mptr = (zend_function *) &call; |
439 | 124 | } |
440 | | |
441 | 625 | zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, fcc.object); |
442 | | |
443 | 625 | if (&mptr->internal_function == &call) { |
444 | 124 | zend_string_release(mptr->common.function_name); |
445 | 124 | } |
446 | | |
447 | 625 | return SUCCESS; |
448 | 669 | } |
449 | | /* }}} */ |
450 | | |
451 | | /* {{{ Create a closure from a callable using the current scope. */ |
452 | | ZEND_METHOD(Closure, fromCallable) |
453 | 780 | { |
454 | 780 | zval *callable; |
455 | 780 | char *error = NULL; |
456 | | |
457 | 2.34k | ZEND_PARSE_PARAMETERS_START(1, 1) |
458 | 3.12k | Z_PARAM_ZVAL(callable) |
459 | 3.12k | ZEND_PARSE_PARAMETERS_END(); |
460 | | |
461 | 780 | if (Z_TYPE_P(callable) == IS_OBJECT && instanceof_function(Z_OBJCE_P(callable), zend_ce_closure)) { |
462 | | /* It's already a closure */ |
463 | 8 | RETURN_COPY(callable); |
464 | 8 | } |
465 | | |
466 | 772 | if (zend_create_closure_from_callable(return_value, callable, &error) == FAILURE) { |
467 | 103 | if (error) { |
468 | 103 | zend_type_error("Failed to create closure from callable: %s", error); |
469 | 103 | efree(error); |
470 | 103 | } else { |
471 | 0 | zend_type_error("Failed to create closure from callable"); |
472 | 0 | } |
473 | 103 | } |
474 | 772 | } |
475 | | /* }}} */ |
476 | | |
477 | | ZEND_METHOD(Closure, getCurrent) |
478 | 144 | { |
479 | 144 | ZEND_PARSE_PARAMETERS_NONE(); |
480 | | |
481 | 144 | zend_execute_data *prev_ex = EX(prev_execute_data); |
482 | | |
483 | 144 | if (!prev_ex |
484 | 144 | || !prev_ex->func |
485 | 144 | || (prev_ex->func->common.fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE)) != ZEND_ACC_CLOSURE) { |
486 | 12 | zend_throw_error(NULL, "Current function is not a closure"); |
487 | 12 | RETURN_THROWS(); |
488 | 12 | } |
489 | | |
490 | 132 | zend_object *obj = ZEND_CLOSURE_OBJECT(prev_ex->func); |
491 | 132 | RETURN_OBJ_COPY(obj); |
492 | 132 | } |
493 | | |
494 | | static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */ |
495 | 11 | { |
496 | 11 | zend_throw_error(NULL, "Instantiation of class Closure is not allowed"); |
497 | 11 | return NULL; |
498 | 11 | } |
499 | | /* }}} */ |
500 | | |
501 | | /* int return due to Object Handler API */ |
502 | | static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */ |
503 | 211 | { |
504 | 211 | ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2); |
505 | | |
506 | 193 | zend_closure *lhs = (zend_closure*) Z_OBJ_P(o1); |
507 | 193 | zend_closure *rhs = (zend_closure*) Z_OBJ_P(o2); |
508 | | |
509 | 193 | if (!((lhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE) && (rhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE))) { |
510 | 4 | return ZEND_UNCOMPARABLE; |
511 | 4 | } |
512 | | |
513 | 189 | if (lhs->this_ptr != rhs->this_ptr) { |
514 | 42 | return ZEND_UNCOMPARABLE; |
515 | 42 | } |
516 | | |
517 | 147 | if (lhs->called_scope != rhs->called_scope) { |
518 | 10 | return ZEND_UNCOMPARABLE; |
519 | 10 | } |
520 | | |
521 | 137 | if (lhs->func.type != rhs->func.type) { |
522 | 2 | return ZEND_UNCOMPARABLE; |
523 | 2 | } |
524 | | |
525 | 135 | if (lhs->func.common.scope != rhs->func.common.scope) { |
526 | 0 | return ZEND_UNCOMPARABLE; |
527 | 0 | } |
528 | | |
529 | 135 | if (!zend_string_equals(lhs->func.common.function_name, rhs->func.common.function_name)) { |
530 | 49 | return ZEND_UNCOMPARABLE; |
531 | 49 | } |
532 | | |
533 | 86 | return 0; |
534 | 135 | } |
535 | | /* }}} */ |
536 | | |
537 | | ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {{{ */ |
538 | 584 | { |
539 | 584 | zend_closure *closure = (zend_closure *)object; |
540 | 584 | zend_function *invoke = (zend_function*)emalloc(sizeof(zend_function)); |
541 | 584 | const uint32_t keep_flags = |
542 | 584 | ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_VARIADIC | ZEND_ACC_HAS_RETURN_TYPE | ZEND_ACC_DEPRECATED; |
543 | | |
544 | 584 | invoke->common = closure->func.common; |
545 | | /* We return ZEND_INTERNAL_FUNCTION, but arg_info representation is the |
546 | | * same as for ZEND_USER_FUNCTION (uses zend_string* instead of char*). |
547 | | * This is not a problem, because ZEND_ACC_HAS_TYPE_HINTS is never set, |
548 | | * and we won't check arguments on internal function. We also set |
549 | | * ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */ |
550 | 584 | invoke->type = ZEND_INTERNAL_FUNCTION; |
551 | 584 | invoke->internal_function.fn_flags = |
552 | 584 | ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | ZEND_ACC_NEVER_CACHE | (closure->func.common.fn_flags & keep_flags); |
553 | 584 | if (closure->func.type != ZEND_INTERNAL_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { |
554 | 487 | invoke->internal_function.fn_flags |= |
555 | 487 | ZEND_ACC_USER_ARG_INFO; |
556 | 487 | } |
557 | 584 | invoke->internal_function.handler = ZEND_MN(Closure___invoke); |
558 | 584 | invoke->internal_function.doc_comment = NULL; |
559 | 584 | invoke->internal_function.module = 0; |
560 | 584 | invoke->internal_function.scope = zend_ce_closure; |
561 | 584 | invoke->internal_function.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE); |
562 | 584 | return invoke; |
563 | 584 | } |
564 | | /* }}} */ |
565 | | |
566 | | ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj) /* {{{ */ |
567 | 1.28k | { |
568 | 1.28k | zend_closure *closure = (zend_closure *) obj; |
569 | 1.28k | return &closure->func; |
570 | 1.28k | } |
571 | | /* }}} */ |
572 | | |
573 | | ZEND_API zend_object* zend_get_closure_this_ptr(zval *obj) /* {{{ */ |
574 | 13 | { |
575 | 13 | zend_closure *closure = (zend_closure *)Z_OBJ_P(obj); |
576 | 13 | return closure->this_ptr; |
577 | 13 | } |
578 | | /* }}} */ |
579 | | |
580 | | static zend_function *zend_closure_get_method(zend_object **object, zend_string *method, const zval *key) /* {{{ */ |
581 | 2.05k | { |
582 | 2.05k | if (zend_string_equals_literal_ci(method, ZEND_INVOKE_FUNC_NAME)) { |
583 | 546 | return zend_get_closure_invoke_method(*object); |
584 | 546 | } |
585 | | |
586 | 1.50k | return zend_std_get_method(object, method, key); |
587 | 2.05k | } |
588 | | /* }}} */ |
589 | | |
590 | | static void zend_closure_free_storage(zend_object *object) /* {{{ */ |
591 | 17.4k | { |
592 | 17.4k | zend_closure *closure = (zend_closure *)object; |
593 | | |
594 | 17.4k | zend_object_std_dtor(&closure->std); |
595 | | |
596 | 17.4k | if (closure->func.type == ZEND_USER_FUNCTION) { |
597 | | /* We don't own the static variables of fake closures. */ |
598 | 16.5k | if (!(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) { |
599 | 15.6k | zend_destroy_static_vars(&closure->func.op_array); |
600 | 15.6k | } |
601 | 16.5k | destroy_op_array(&closure->func.op_array); |
602 | 16.5k | } else if (closure->func.type == ZEND_INTERNAL_FUNCTION) { |
603 | 882 | zend_string_release(closure->func.common.function_name); |
604 | 882 | } |
605 | | |
606 | 17.4k | if (closure->this_ptr) { |
607 | 3.66k | OBJ_RELEASE(closure->this_ptr); |
608 | 3.66k | } |
609 | 17.4k | } |
610 | | /* }}} */ |
611 | | |
612 | | static zend_object *zend_closure_new(zend_class_entry *class_type) /* {{{ */ |
613 | 17.4k | { |
614 | 17.4k | zend_closure *closure; |
615 | | |
616 | 17.4k | closure = emalloc(sizeof(zend_closure)); |
617 | 17.4k | memset(closure, 0, sizeof(zend_closure)); |
618 | | |
619 | 17.4k | zend_object_std_init(&closure->std, class_type); |
620 | | |
621 | 17.4k | return (zend_object*)closure; |
622 | 17.4k | } |
623 | | /* }}} */ |
624 | | |
625 | | static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */ |
626 | 15 | { |
627 | 15 | zend_closure *closure = (zend_closure *)zobject; |
628 | 15 | zval result; |
629 | | |
630 | 15 | zend_create_closure_ex(&result, &closure->func, |
631 | 15 | closure->func.common.scope, closure->called_scope, |
632 | 15 | closure->this_ptr, |
633 | 15 | zend_closure_is_fake(closure), zend_closure_flags(closure)); |
634 | 15 | return Z_OBJ(result); |
635 | 15 | } |
636 | | /* }}} */ |
637 | | |
638 | | static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */ |
639 | 21.5k | { |
640 | 21.5k | zend_closure *closure = (zend_closure*)obj; |
641 | | |
642 | 21.5k | *fptr_ptr = &closure->func; |
643 | 21.5k | *ce_ptr = closure->called_scope; |
644 | 21.5k | *obj_ptr = closure->this_ptr; |
645 | | |
646 | 21.5k | return SUCCESS; |
647 | 21.5k | } |
648 | | /* }}} */ |
649 | | |
650 | | /* *is_temp is int due to Object Handler API */ |
651 | | static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) /* {{{ */ |
652 | 1.15k | { |
653 | 1.15k | zend_closure *closure = (zend_closure *)object; |
654 | 1.15k | zval val; |
655 | 1.15k | struct _zend_arg_info *arg_info = closure->func.common.arg_info; |
656 | 1.15k | HashTable *debug_info; |
657 | | |
658 | 1.15k | *is_temp = 1; |
659 | | |
660 | 1.15k | debug_info = zend_new_array(8); |
661 | | |
662 | 1.15k | if (closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE) { |
663 | 198 | if (closure->func.common.scope) { |
664 | 52 | zend_string *class_name = closure->func.common.scope->name; |
665 | 52 | zend_string *func_name = closure->func.common.function_name; |
666 | 52 | zend_string *combined = zend_string_concat3( |
667 | 52 | ZSTR_VAL(class_name), ZSTR_LEN(class_name), |
668 | 52 | "::", strlen("::"), |
669 | 52 | ZSTR_VAL(func_name), ZSTR_LEN(func_name) |
670 | 52 | ); |
671 | 52 | ZVAL_STR(&val, combined); |
672 | 146 | } else { |
673 | 146 | ZVAL_STR_COPY(&val, closure->func.common.function_name); |
674 | 146 | } |
675 | 198 | zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_FUNCTION), &val); |
676 | 954 | } else { |
677 | 954 | ZVAL_STR_COPY(&val, closure->func.common.function_name); |
678 | 954 | zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_NAME), &val); |
679 | | |
680 | 954 | ZVAL_STR_COPY(&val, closure->func.op_array.filename); |
681 | 954 | zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_FILE), &val); |
682 | | |
683 | 954 | ZVAL_LONG(&val, closure->func.op_array.line_start); |
684 | 954 | zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_LINE), &val); |
685 | 954 | } |
686 | | |
687 | 1.15k | if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) { |
688 | 175 | zval *var; |
689 | 175 | zend_string *key; |
690 | 175 | HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); |
691 | | |
692 | 175 | array_init(&val); |
693 | | |
694 | 781 | ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(static_variables, key, var) { |
695 | 781 | zval copy; |
696 | | |
697 | 781 | if (Z_ISREF_P(var) && Z_REFCOUNT_P(var) == 1) { |
698 | 40 | var = Z_REFVAL_P(var); |
699 | 40 | } |
700 | 781 | ZVAL_COPY(©, var); |
701 | | |
702 | 781 | zend_hash_add_new(Z_ARRVAL(val), key, ©); |
703 | 781 | } ZEND_HASH_FOREACH_END(); |
704 | | |
705 | 175 | if (zend_hash_num_elements(Z_ARRVAL(val))) { |
706 | 162 | zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_STATIC), &val); |
707 | 162 | } else { |
708 | 13 | zval_ptr_dtor(&val); |
709 | 13 | } |
710 | 175 | } |
711 | | |
712 | 1.15k | if (closure->this_ptr) { |
713 | 647 | zval tmp; |
714 | 647 | ZVAL_OBJ_COPY(&tmp, closure->this_ptr); |
715 | 647 | zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_THIS), &tmp); |
716 | 647 | } |
717 | | |
718 | 1.15k | if (arg_info && |
719 | 212 | (closure->func.common.num_args || |
720 | 205 | (closure->func.common.fn_flags & ZEND_ACC_VARIADIC))) { |
721 | 205 | uint32_t i, num_args, required = closure->func.common.required_num_args; |
722 | | |
723 | 205 | array_init(&val); |
724 | | |
725 | 205 | num_args = closure->func.common.num_args; |
726 | 205 | if (closure->func.common.fn_flags & ZEND_ACC_VARIADIC) { |
727 | 42 | num_args++; |
728 | 42 | } |
729 | 463 | for (i = 0; i < num_args; i++) { |
730 | 258 | zend_string *name; |
731 | 258 | zval info; |
732 | 258 | ZEND_ASSERT(arg_info->name && "Argument should have name"); |
733 | 258 | name = zend_strpprintf(0, "%s$%s", |
734 | 258 | ZEND_ARG_SEND_MODE(arg_info) ? "&" : "", |
735 | 258 | ZSTR_VAL(arg_info->name)); |
736 | 258 | ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "<optional>" : "<required>")); |
737 | 258 | zend_hash_update(Z_ARRVAL(val), name, &info); |
738 | 258 | zend_string_release_ex(name, 0); |
739 | 258 | arg_info++; |
740 | 258 | } |
741 | 205 | zend_hash_str_update(debug_info, "parameter", sizeof("parameter")-1, &val); |
742 | 205 | } |
743 | | |
744 | 1.15k | return debug_info; |
745 | 1.15k | } |
746 | | /* }}} */ |
747 | | |
748 | | static HashTable *zend_closure_get_gc(zend_object *obj, zval **table, int *n) /* {{{ */ |
749 | 17.6k | { |
750 | 17.6k | zend_closure *closure = (zend_closure *)obj; |
751 | | |
752 | 17.6k | if (closure->this_ptr) { |
753 | 5.98k | zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create(); |
754 | 5.98k | zend_get_gc_buffer_add_obj(gc_buffer, closure->this_ptr); |
755 | 5.98k | zend_get_gc_buffer_use(gc_buffer, table, n); |
756 | 11.7k | } else { |
757 | 11.7k | *table = NULL; |
758 | 11.7k | *n = 0; |
759 | 11.7k | } |
760 | | |
761 | | /* Fake closures don't own the static variables they reference. */ |
762 | 17.6k | return (closure->func.type == ZEND_USER_FUNCTION |
763 | 17.3k | && !(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) ? |
764 | 17.6k | ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr) : NULL; |
765 | 17.6k | } |
766 | | /* }}} */ |
767 | | |
768 | | /* {{{ Private constructor preventing instantiation */ |
769 | | ZEND_COLD ZEND_METHOD(Closure, __construct) |
770 | 1 | { |
771 | 1 | zend_throw_error(NULL, "Instantiation of class Closure is not allowed"); |
772 | 1 | } |
773 | | /* }}} */ |
774 | | |
775 | | void zend_register_closure_ce(void) /* {{{ */ |
776 | 16 | { |
777 | 16 | zend_ce_closure = register_class_Closure(); |
778 | 16 | zend_ce_closure->create_object = zend_closure_new; |
779 | 16 | zend_ce_closure->default_object_handlers = &closure_handlers; |
780 | | |
781 | 16 | memcpy(&closure_handlers, &std_object_handlers, sizeof(zend_object_handlers)); |
782 | 16 | closure_handlers.free_obj = zend_closure_free_storage; |
783 | 16 | closure_handlers.get_constructor = zend_closure_get_constructor; |
784 | 16 | closure_handlers.get_method = zend_closure_get_method; |
785 | 16 | closure_handlers.compare = zend_closure_compare; |
786 | 16 | closure_handlers.clone_obj = zend_closure_clone; |
787 | 16 | closure_handlers.get_debug_info = zend_closure_get_debug_info; |
788 | 16 | closure_handlers.get_closure = zend_closure_get_closure; |
789 | 16 | closure_handlers.get_gc = zend_closure_get_gc; |
790 | 16 | } |
791 | | /* }}} */ |
792 | | |
793 | | static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */ |
794 | 572 | { |
795 | 572 | zend_closure *closure = (zend_closure*)ZEND_CLOSURE_OBJECT(EX(func)); |
796 | 572 | closure->orig_internal_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU); |
797 | 572 | ZEND_ASSERT(!(closure->func.common.fn_flags2 & ZEND_ACC2_FORBID_DYN_CALLS) || EG(exception)); |
798 | | // Assign to EX(this) so that it is released after observer checks etc. |
799 | 572 | ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_RELEASE_THIS); |
800 | 572 | Z_OBJ(EX(This)) = &closure->std; |
801 | 572 | } |
802 | | /* }}} */ |
803 | | |
804 | | static void zend_create_closure_ex( |
805 | | zval *res, zend_function *func, |
806 | | zend_class_entry *scope, zend_class_entry *called_scope, |
807 | | zend_object *this_ptr, bool is_fake, uint32_t flags) /* {{{ */ |
808 | 17.4k | { |
809 | 17.4k | zend_closure *closure; |
810 | 17.4k | void *ptr; |
811 | | |
812 | 17.4k | object_init_ex(res, zend_ce_closure); |
813 | | |
814 | 17.4k | closure = (zend_closure *)Z_OBJ_P(res); |
815 | 17.4k | closure->std.extra_flags = flags; |
816 | | |
817 | 17.4k | if ((scope == NULL) && this_ptr) { |
818 | | /* use dummy scope if we're binding an object without specifying a scope */ |
819 | | /* maybe it would be better to create one for this purpose */ |
820 | 156 | scope = zend_ce_closure; |
821 | 156 | } |
822 | | |
823 | 17.4k | if (func->type == ZEND_USER_FUNCTION) { |
824 | 16.5k | memcpy(&closure->func, func, sizeof(zend_op_array)); |
825 | 16.5k | closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; |
826 | 16.5k | closure->func.common.fn_flags &= ~ZEND_ACC_IMMUTABLE; |
827 | | |
828 | 16.5k | zend_string_addref(closure->func.op_array.function_name); |
829 | 16.5k | if (closure->func.op_array.refcount) { |
830 | 1.72k | (*closure->func.op_array.refcount)++; |
831 | 1.72k | } |
832 | | |
833 | | /* For fake closures, we want to reuse the static variables of the original function. */ |
834 | 16.5k | HashTable *ht = ZEND_MAP_PTR_GET(func->op_array.static_variables_ptr); |
835 | 16.5k | if (!is_fake) { |
836 | 15.6k | if (!ht) { |
837 | 15.5k | ht = closure->func.op_array.static_variables; |
838 | 15.5k | } |
839 | 15.6k | ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, |
840 | 15.6k | ht ? zend_array_dup(ht) : NULL); |
841 | 15.6k | } else if (func->op_array.static_variables) { |
842 | 165 | if (!ht) { |
843 | 79 | ht = zend_array_dup(func->op_array.static_variables); |
844 | 79 | ZEND_MAP_PTR_SET(func->op_array.static_variables_ptr, ht); |
845 | 79 | } |
846 | 165 | ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, ht); |
847 | 165 | } |
848 | | |
849 | | /* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */ |
850 | 16.5k | ptr = ZEND_MAP_PTR_GET(func->op_array.run_time_cache); |
851 | 16.5k | if (!ptr |
852 | 3.62k | || func->common.scope != scope |
853 | 3.28k | || (func->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE) |
854 | 16.5k | ) { |
855 | 13.3k | if (!ptr |
856 | 12.8k | && (func->common.fn_flags & ZEND_ACC_CLOSURE) |
857 | 12.6k | && (func->common.scope == scope || |
858 | 9.87k | !(func->common.fn_flags & ZEND_ACC_IMMUTABLE))) { |
859 | | /* If a real closure is used for the first time, we create a shared runtime cache |
860 | | * and remember which scope it is for. */ |
861 | 9.87k | if (func->common.scope != scope) { |
862 | 63 | func->common.scope = scope; |
863 | 63 | } |
864 | 9.87k | ptr = zend_arena_alloc(&CG(arena), func->op_array.cache_size); |
865 | 9.87k | ZEND_MAP_PTR_SET(func->op_array.run_time_cache, ptr); |
866 | 9.87k | closure->func.op_array.fn_flags &= ~ZEND_ACC_HEAP_RT_CACHE; |
867 | 9.87k | } else { |
868 | | /* Otherwise, we use a non-shared runtime cache */ |
869 | 3.51k | ptr = emalloc(func->op_array.cache_size); |
870 | 3.51k | closure->func.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE; |
871 | 3.51k | } |
872 | 13.3k | memset(ptr, 0, func->op_array.cache_size); |
873 | 13.3k | } |
874 | 16.5k | ZEND_MAP_PTR_INIT(closure->func.op_array.run_time_cache, ptr); |
875 | 16.5k | } else { |
876 | 882 | memcpy(&closure->func, func, sizeof(zend_internal_function)); |
877 | 882 | closure->func.common.fn_flags |= ZEND_ACC_CLOSURE; |
878 | | /* wrap internal function handler to avoid memory leak */ |
879 | 882 | if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) { |
880 | | /* avoid infinity recursion, by taking handler from nested closure */ |
881 | 101 | zend_closure *nested = ZEND_CONTAINER_OF(func, zend_closure, func); |
882 | 101 | ZEND_ASSERT(nested->std.ce == zend_ce_closure); |
883 | 101 | closure->orig_internal_handler = nested->orig_internal_handler; |
884 | 781 | } else { |
885 | 781 | closure->orig_internal_handler = closure->func.internal_function.handler; |
886 | 781 | } |
887 | 882 | closure->func.internal_function.handler = zend_closure_internal_handler; |
888 | 882 | zend_string_addref(closure->func.op_array.function_name); |
889 | 882 | if (!func->common.scope) { |
890 | | /* if it's a free function, we won't set scope & this since they're meaningless */ |
891 | 412 | this_ptr = NULL; |
892 | 412 | scope = NULL; |
893 | 412 | } |
894 | 882 | } |
895 | | |
896 | 17.4k | closure->this_ptr = NULL; |
897 | | /* Invariant: |
898 | | * If the closure is unscoped or static, it has no bound object. */ |
899 | 17.4k | closure->func.common.scope = scope; |
900 | 17.4k | closure->called_scope = called_scope; |
901 | 17.4k | if (scope) { |
902 | 4.70k | closure->func.common.fn_flags |= ZEND_ACC_PUBLIC; |
903 | 4.70k | if (this_ptr && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) { |
904 | 3.66k | closure->this_ptr = this_ptr; |
905 | 3.66k | GC_ADDREF(this_ptr); |
906 | 3.66k | } |
907 | 4.70k | } |
908 | 17.4k | } |
909 | | /* }}} */ |
910 | | |
911 | | ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr) |
912 | 13.7k | { |
913 | 13.7k | zend_create_closure_ex(res, func, scope, called_scope, this_ptr, |
914 | 13.7k | /* is_fake */ (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0, |
915 | 13.7k | /* flags */ 0); |
916 | 13.7k | } |
917 | | |
918 | | ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr) /* {{{ */ |
919 | 1.65k | { |
920 | 1.65k | zend_closure *closure; |
921 | | |
922 | 1.65k | zend_create_closure_ex(res, func, scope, called_scope, this_ptr, |
923 | 1.65k | /* is_fake */ true, /* flags */ 0); |
924 | | |
925 | 1.65k | closure = (zend_closure *)Z_OBJ_P(res); |
926 | 1.65k | closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE; |
927 | 1.65k | if (!closure->this_ptr) { |
928 | 1.10k | GC_ADD_FLAGS(&closure->std, GC_NOT_COLLECTABLE); |
929 | 1.10k | } |
930 | 1.65k | } |
931 | | /* }}} */ |
932 | | |
933 | | ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr, bool partial_of_closure) |
934 | 1.33k | { |
935 | 1.33k | uint32_t flags = ZEND_PARTIAL; |
936 | 1.33k | if (partial_of_closure) { |
937 | 174 | flags |= ZEND_PARTIAL_OF_CLOSURE; |
938 | 174 | } |
939 | 1.33k | zend_create_closure_ex(res, func, scope, called_scope, this_ptr, |
940 | 1.33k | /* is_fake */ false, flags); |
941 | 1.33k | } |
942 | | |
943 | 776 | void zend_closure_from_frame(zval *return_value, const zend_execute_data *call) { /* {{{ */ |
944 | 776 | zend_internal_function trampoline; |
945 | 776 | zend_function *mptr = call->func; |
946 | | |
947 | 776 | if (ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE) { |
948 | 8 | RETURN_OBJ(ZEND_CLOSURE_OBJECT(mptr)); |
949 | 8 | } |
950 | | |
951 | 768 | if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { |
952 | 209 | if ((ZEND_CALL_INFO(call) & ZEND_CALL_HAS_THIS) && |
953 | 141 | (Z_OBJCE(call->This) == zend_ce_closure) |
954 | 10 | && zend_string_equals(mptr->common.function_name, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) { |
955 | 10 | zend_free_trampoline(mptr); |
956 | 10 | RETURN_OBJ_COPY(Z_OBJ(call->This)); |
957 | 10 | } |
958 | | |
959 | 199 | memset(&trampoline, 0, sizeof(zend_internal_function)); |
960 | 199 | trampoline.type = ZEND_INTERNAL_FUNCTION; |
961 | 199 | trampoline.fn_flags = mptr->common.fn_flags & (ZEND_ACC_STATIC | ZEND_ACC_VARIADIC | ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_DEPRECATED); |
962 | 199 | trampoline.handler = zend_closure_call_magic; |
963 | 199 | trampoline.function_name = mptr->common.function_name; |
964 | 199 | trampoline.scope = mptr->common.scope; |
965 | 199 | trampoline.doc_comment = NULL; |
966 | 199 | trampoline.arg_info = mptr->common.arg_info; |
967 | 199 | trampoline.attributes = mptr->common.attributes; |
968 | | |
969 | 199 | zend_free_trampoline(mptr); |
970 | 199 | mptr = (zend_function *) &trampoline; |
971 | 199 | } |
972 | | |
973 | 758 | if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_THIS) { |
974 | 237 | zend_create_fake_closure(return_value, mptr, mptr->common.scope, Z_OBJCE(call->This), Z_OBJ(call->This)); |
975 | 521 | } else { |
976 | 521 | zend_create_fake_closure(return_value, mptr, mptr->common.scope, Z_CE(call->This), NULL); |
977 | 521 | } |
978 | | |
979 | 758 | if (&mptr->internal_function == &trampoline) { |
980 | 199 | zend_string_release(mptr->common.function_name); |
981 | 199 | } |
982 | 758 | } /* }}} */ |
983 | | |
984 | | void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */ |
985 | 0 | { |
986 | 0 | zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv); |
987 | 0 | HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); |
988 | 0 | zend_hash_update(static_variables, var_name, var); |
989 | 0 | } |
990 | | /* }}} */ |
991 | | |
992 | | void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {{{ */ |
993 | 6.98k | { |
994 | 6.98k | zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv); |
995 | 6.98k | HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr); |
996 | 6.98k | zval *var = (zval*)((char*)static_variables->arData + offset); |
997 | 6.98k | zval_ptr_dtor(var); |
998 | 6.98k | ZVAL_COPY_VALUE(var, val); |
999 | 6.98k | } |
1000 | | /* }}} */ |