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