xref: /PHP-8.0/Zend/zend_closures.c (revision d26965b2)
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 #define ZEND_CLOSURE_PRINT_NAME "Closure object"
32 
33 #define ZEND_CLOSURE_PROPERTY_ERROR() \
34 	zend_throw_error(NULL, "Closure object cannot have properties")
35 
36 typedef struct _zend_closure {
37 	zend_object       std;
38 	zend_function     func;
39 	zval              this_ptr;
40 	zend_class_entry *called_scope;
41 	zif_handler       orig_internal_handler;
42 } zend_closure;
43 
44 /* non-static since it needs to be referenced */
45 ZEND_API zend_class_entry *zend_ce_closure;
46 static zend_object_handlers closure_handlers;
47 
ZEND_METHOD(Closure,__invoke)48 ZEND_METHOD(Closure, __invoke) /* {{{ */
49 {
50 	zend_function *func = EX(func);
51 	zval *args;
52 	uint32_t num_args;
53 	HashTable *named_args;
54 
55 	ZEND_PARSE_PARAMETERS_START(0, -1)
56 		Z_PARAM_VARIADIC_WITH_NAMED(args, num_args, named_args)
57 	ZEND_PARSE_PARAMETERS_END();
58 
59 	if (call_user_function_named(CG(function_table), NULL, ZEND_THIS, return_value, num_args, args, named_args) == FAILURE) {
60 		RETVAL_FALSE;
61 	}
62 
63 	/* destruct the function also, then - we have allocated it in get_method */
64 	zend_string_release_ex(func->internal_function.function_name, 0);
65 	efree(func);
66 #if ZEND_DEBUG
67 	execute_data->func = NULL;
68 #endif
69 }
70 /* }}} */
71 
zend_valid_closure_binding(zend_closure * closure,zval * newthis,zend_class_entry * scope)72 static zend_bool zend_valid_closure_binding(
73 		zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */
74 {
75 	zend_function *func = &closure->func;
76 	zend_bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
77 	if (newthis) {
78 		if (func->common.fn_flags & ZEND_ACC_STATIC) {
79 			zend_error(E_WARNING, "Cannot bind an instance to a static closure");
80 			return 0;
81 		}
82 
83 		if (is_fake_closure && func->common.scope &&
84 				!instanceof_function(Z_OBJCE_P(newthis), func->common.scope)) {
85 			/* Binding incompatible $this to an internal method is not supported. */
86 			zend_error(E_WARNING, "Cannot bind method %s::%s() to object of class %s",
87 					ZSTR_VAL(func->common.scope->name),
88 					ZSTR_VAL(func->common.function_name),
89 					ZSTR_VAL(Z_OBJCE_P(newthis)->name));
90 			return 0;
91 		}
92 	} else if (is_fake_closure && func->common.scope
93 			&& !(func->common.fn_flags & ZEND_ACC_STATIC)) {
94 		zend_error(E_WARNING, "Cannot unbind $this of method");
95 		return 0;
96 	} else if (!is_fake_closure && !Z_ISUNDEF(closure->this_ptr)
97 			&& (func->common.fn_flags & ZEND_ACC_USES_THIS)) {
98 		zend_error(E_WARNING, "Cannot unbind $this of closure using $this");
99 		return 0;
100 	}
101 
102 	if (scope && scope != func->common.scope && scope->type == ZEND_INTERNAL_CLASS) {
103 		/* rebinding to internal class is not allowed */
104 		zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s",
105 				ZSTR_VAL(scope->name));
106 		return 0;
107 	}
108 
109 	if (is_fake_closure && scope != func->common.scope) {
110 		if (func->common.scope == NULL) {
111 			zend_error(E_WARNING, "Cannot rebind scope of closure created from function");
112 		} else {
113 			zend_error(E_WARNING, "Cannot rebind scope of closure created from method");
114 		}
115 		return 0;
116 	}
117 
118 	return 1;
119 }
120 /* }}} */
121 
122 /* {{{ Call closure, binding to a given object with its class as the scope */
ZEND_METHOD(Closure,call)123 ZEND_METHOD(Closure, call)
124 {
125 	zval *newthis, closure_result;
126 	zend_closure *closure;
127 	zend_fcall_info fci;
128 	zend_fcall_info_cache fci_cache;
129 	zend_function my_function;
130 	zend_object *newobj;
131 
132 	fci.param_count = 0;
133 	fci.params = NULL;
134 
135 	ZEND_PARSE_PARAMETERS_START(1, -1)
136 		Z_PARAM_OBJECT(newthis)
137 		Z_PARAM_VARIADIC_WITH_NAMED(fci.params, fci.param_count, fci.named_params)
138 	ZEND_PARSE_PARAMETERS_END();
139 
140 	closure = (zend_closure *) Z_OBJ_P(ZEND_THIS);
141 
142 	newobj = Z_OBJ_P(newthis);
143 
144 	if (!zend_valid_closure_binding(closure, newthis, Z_OBJCE_P(newthis))) {
145 		return;
146 	}
147 
148 	if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) {
149 		zval new_closure;
150 		zend_create_closure(&new_closure, &closure->func, Z_OBJCE_P(newthis), closure->called_scope, newthis);
151 		closure = (zend_closure *) Z_OBJ(new_closure);
152 		fci_cache.function_handler = &closure->func;
153 	} else {
154 		memcpy(&my_function, &closure->func, closure->func.type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
155 		my_function.common.fn_flags &= ~ZEND_ACC_CLOSURE;
156 		/* use scope of passed object */
157 		my_function.common.scope = Z_OBJCE_P(newthis);
158 		if (closure->func.type == ZEND_INTERNAL_FUNCTION) {
159 			my_function.internal_function.handler = closure->orig_internal_handler;
160 		}
161 		fci_cache.function_handler = &my_function;
162 
163 		/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
164 		if (ZEND_USER_CODE(my_function.type)
165 		 && (closure->func.common.scope != Z_OBJCE_P(newthis)
166 		  || (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) {
167 			void *ptr;
168 
169 			my_function.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE;
170 			ptr = emalloc(sizeof(void*) + my_function.op_array.cache_size);
171 			ZEND_MAP_PTR_INIT(my_function.op_array.run_time_cache, ptr);
172 			ptr = (char*)ptr + sizeof(void*);
173 			ZEND_MAP_PTR_SET(my_function.op_array.run_time_cache, ptr);
174 			memset(ptr, 0, my_function.op_array.cache_size);
175 		}
176 	}
177 
178 	fci_cache.called_scope = newobj->ce;
179 	fci_cache.object = fci.object = newobj;
180 
181 	fci.size = sizeof(fci);
182 	ZVAL_OBJ(&fci.function_name, &closure->std);
183 	fci.retval = &closure_result;
184 
185 	if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(closure_result) != IS_UNDEF) {
186 		if (Z_ISREF(closure_result)) {
187 			zend_unwrap_reference(&closure_result);
188 		}
189 		ZVAL_COPY_VALUE(return_value, &closure_result);
190 	}
191 
192 	if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) {
193 		/* copied upon generator creation */
194 		GC_DELREF(&closure->std);
195 	} else if (ZEND_USER_CODE(my_function.type)
196 	 && (fci_cache.function_handler->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE)) {
197 		efree(ZEND_MAP_PTR(my_function.op_array.run_time_cache));
198 	}
199 }
200 /* }}} */
201 
do_closure_bind(zval * return_value,zval * zclosure,zval * newthis,zend_object * scope_obj,zend_string * scope_str)202 static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, zend_object *scope_obj, zend_string *scope_str)
203 {
204 	zend_class_entry *ce, *called_scope;
205 	zend_closure *closure = (zend_closure *) Z_OBJ_P(zclosure);
206 
207 	if (scope_obj) {
208 		ce = scope_obj->ce;
209 	} else if (scope_str) {
210 		if (zend_string_equals(scope_str, ZSTR_KNOWN(ZEND_STR_STATIC))) {
211 			ce = closure->func.common.scope;
212 		} else if ((ce = zend_lookup_class(scope_str)) == NULL) {
213 			zend_error(E_WARNING, "Class \"%s\" not found", ZSTR_VAL(scope_str));
214 			RETURN_NULL();
215 		}
216 	} else {
217 		ce = NULL;
218 	}
219 
220 	if (!zend_valid_closure_binding(closure, newthis, ce)) {
221 		return;
222 	}
223 
224 	if (newthis) {
225 		called_scope = Z_OBJCE_P(newthis);
226 	} else {
227 		called_scope = ce;
228 	}
229 
230 	zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
231 }
232 
233 /* {{{ Create a closure from another one and bind to another object and scope */
ZEND_METHOD(Closure,bind)234 ZEND_METHOD(Closure, bind)
235 {
236 	zval *zclosure, *newthis;
237 	zend_object *scope_obj = NULL;
238 	zend_string *scope_str = ZSTR_KNOWN(ZEND_STR_STATIC);
239 
240 	ZEND_PARSE_PARAMETERS_START(2, 3)
241 		Z_PARAM_OBJECT_OF_CLASS(zclosure, zend_ce_closure)
242 		Z_PARAM_OBJECT_OR_NULL(newthis)
243 		Z_PARAM_OPTIONAL
244 		Z_PARAM_OBJ_OR_STR_OR_NULL(scope_obj, scope_str)
245 	ZEND_PARSE_PARAMETERS_END();
246 
247 	do_closure_bind(return_value, zclosure, newthis, scope_obj, scope_str);
248 }
249 
250 /* {{{ Create a closure from another one and bind to another object and scope */
ZEND_METHOD(Closure,bindTo)251 ZEND_METHOD(Closure, bindTo)
252 {
253 	zval *newthis;
254 	zend_object *scope_obj = NULL;
255 	zend_string *scope_str = ZSTR_KNOWN(ZEND_STR_STATIC);
256 
257 	ZEND_PARSE_PARAMETERS_START(1, 2)
258 		Z_PARAM_OBJECT_OR_NULL(newthis)
259 		Z_PARAM_OPTIONAL
260 		Z_PARAM_OBJ_OR_STR_OR_NULL(scope_obj, scope_str)
261 	ZEND_PARSE_PARAMETERS_END();
262 
263 	do_closure_bind(return_value, getThis(), newthis, scope_obj, scope_str);
264 }
265 
ZEND_NAMED_FUNCTION(zend_closure_call_magic)266 static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
267 	zend_fcall_info fci;
268 	zend_fcall_info_cache fcc;
269 	zval params[2];
270 
271 	memset(&fci, 0, sizeof(zend_fcall_info));
272 	memset(&fcc, 0, sizeof(zend_fcall_info_cache));
273 
274 	fci.size = sizeof(zend_fcall_info);
275 	fci.retval = return_value;
276 
277 	fcc.function_handler = (EX(func)->internal_function.fn_flags & ZEND_ACC_STATIC) ?
278 		EX(func)->internal_function.scope->__callstatic : EX(func)->internal_function.scope->__call;
279 	fci.named_params = NULL;
280 	fci.params = params;
281 	fci.param_count = 2;
282 	ZVAL_STR(&fci.params[0], EX(func)->common.function_name);
283 	if (ZEND_NUM_ARGS()) {
284 		array_init_size(&fci.params[1], ZEND_NUM_ARGS());
285 		zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]);
286 	} else {
287 		ZVAL_EMPTY_ARRAY(&fci.params[1]);
288 	}
289 
290 	fcc.object = fci.object = Z_OBJ_P(ZEND_THIS);
291 	fcc.called_scope = zend_get_called_scope(EG(current_execute_data));
292 
293 	zend_call_function(&fci, &fcc);
294 
295 	zval_ptr_dtor(&fci.params[1]);
296 }
297 /* }}} */
298 
zend_create_closure_from_callable(zval * return_value,zval * callable,char ** error)299 static zend_result zend_create_closure_from_callable(zval *return_value, zval *callable, char **error) /* {{{ */ {
300 	zend_fcall_info_cache fcc;
301 	zend_function *mptr;
302 	zval instance;
303 	zend_internal_function call;
304 
305 	if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, error)) {
306 		return FAILURE;
307 	}
308 
309 	mptr = fcc.function_handler;
310 	if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
311 		/* For Closure::fromCallable([$closure, "__invoke"]) return $closure. */
312 		if (fcc.object && fcc.object->ce == zend_ce_closure
313 				&& zend_string_equals_literal(mptr->common.function_name, "__invoke")) {
314 			RETVAL_OBJ_COPY(fcc.object);
315 			zend_free_trampoline(mptr);
316 			return SUCCESS;
317 		}
318 
319 		if (!mptr->common.scope) {
320 			return FAILURE;
321 		}
322 		if (mptr->common.fn_flags & ZEND_ACC_STATIC) {
323 			if (!mptr->common.scope->__callstatic) {
324 				return FAILURE;
325 			}
326 		} else {
327 			if (!mptr->common.scope->__call) {
328 				return FAILURE;
329 			}
330 		}
331 
332 		memset(&call, 0, sizeof(zend_internal_function));
333 		call.type = ZEND_INTERNAL_FUNCTION;
334 		call.fn_flags = mptr->common.fn_flags & ZEND_ACC_STATIC;
335 		call.handler = zend_closure_call_magic;
336 		call.function_name = mptr->common.function_name;
337 		call.scope = mptr->common.scope;
338 
339 		zend_free_trampoline(mptr);
340 		mptr = (zend_function *) &call;
341 	}
342 
343 	if (fcc.object) {
344 		ZVAL_OBJ(&instance, fcc.object);
345 		zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, &instance);
346 	} else {
347 		zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, NULL);
348 	}
349 
350 	return SUCCESS;
351 }
352 /* }}} */
353 
354 /* {{{ Create a closure from a callable using the current scope. */
ZEND_METHOD(Closure,fromCallable)355 ZEND_METHOD(Closure, fromCallable)
356 {
357 	zval *callable;
358 	char *error = NULL;
359 
360 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callable) == FAILURE) {
361 		RETURN_THROWS();
362 	}
363 
364 	if (Z_TYPE_P(callable) == IS_OBJECT && instanceof_function(Z_OBJCE_P(callable), zend_ce_closure)) {
365 		/* It's already a closure */
366 		RETURN_COPY(callable);
367 	}
368 
369 	if (zend_create_closure_from_callable(return_value, callable, &error) == FAILURE) {
370 		if (error) {
371 			zend_type_error("Failed to create closure from callable: %s", error);
372 			efree(error);
373 		} else {
374 			zend_type_error("Failed to create closure from callable");
375 		}
376 	}
377 }
378 /* }}} */
379 
zend_closure_get_constructor(zend_object * object)380 static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */
381 {
382 	zend_throw_error(NULL, "Instantiation of class Closure is not allowed");
383 	return NULL;
384 }
385 /* }}} */
386 
387 /* int return due to Object Handler API */
zend_closure_compare(zval * o1,zval * o2)388 static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */
389 {
390 	ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2);
391 	return Z_OBJ_P(o1) != Z_OBJ_P(o2);
392 }
393 /* }}} */
394 
zend_get_closure_invoke_method(zend_object * object)395 ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {{{ */
396 {
397 	zend_closure *closure = (zend_closure *)object;
398 	zend_function *invoke = (zend_function*)emalloc(sizeof(zend_function));
399 	const uint32_t keep_flags =
400 		ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_VARIADIC | ZEND_ACC_HAS_RETURN_TYPE;
401 
402 	invoke->common = closure->func.common;
403 	/* We return ZEND_INTERNAL_FUNCTION, but arg_info representation is the
404 	 * same as for ZEND_USER_FUNCTION (uses zend_string* instead of char*).
405 	 * This is not a problem, because ZEND_ACC_HAS_TYPE_HINTS is never set,
406 	 * and we won't check arguments on internal function. We also set
407 	 * ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */
408 	invoke->type = ZEND_INTERNAL_FUNCTION;
409 	invoke->internal_function.fn_flags =
410 		ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags);
411 	if (closure->func.type != ZEND_INTERNAL_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
412 		invoke->internal_function.fn_flags |=
413 			ZEND_ACC_USER_ARG_INFO;
414 	}
415 	invoke->internal_function.handler = ZEND_MN(Closure___invoke);
416 	invoke->internal_function.module = 0;
417 	invoke->internal_function.scope = zend_ce_closure;
418 	invoke->internal_function.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE);
419 	return invoke;
420 }
421 /* }}} */
422 
zend_get_closure_method_def(zend_object * obj)423 ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj) /* {{{ */
424 {
425 	zend_closure *closure = (zend_closure *) obj;
426 	return &closure->func;
427 }
428 /* }}} */
429 
zend_get_closure_this_ptr(zval * obj)430 ZEND_API zval* zend_get_closure_this_ptr(zval *obj) /* {{{ */
431 {
432 	zend_closure *closure = (zend_closure *)Z_OBJ_P(obj);
433 	return &closure->this_ptr;
434 }
435 /* }}} */
436 
zend_closure_get_method(zend_object ** object,zend_string * method,const zval * key)437 static zend_function *zend_closure_get_method(zend_object **object, zend_string *method, const zval *key) /* {{{ */
438 {
439 	if (zend_string_equals_literal_ci(method, ZEND_INVOKE_FUNC_NAME)) {
440 		return zend_get_closure_invoke_method(*object);
441 	}
442 
443 	return zend_std_get_method(object, method, key);
444 }
445 /* }}} */
446 
zend_closure_read_property(zend_object * object,zend_string * member,int type,void ** cache_slot,zval * rv)447 static ZEND_COLD zval *zend_closure_read_property(zend_object *object, zend_string *member, int type, void **cache_slot, zval *rv) /* {{{ */
448 {
449 	ZEND_CLOSURE_PROPERTY_ERROR();
450 	return &EG(uninitialized_zval);
451 }
452 /* }}} */
453 
zend_closure_write_property(zend_object * object,zend_string * member,zval * value,void ** cache_slot)454 static ZEND_COLD zval *zend_closure_write_property(zend_object *object, zend_string *member, zval *value, void **cache_slot) /* {{{ */
455 {
456 	ZEND_CLOSURE_PROPERTY_ERROR();
457 	return &EG(error_zval);
458 }
459 /* }}} */
460 
zend_closure_get_property_ptr_ptr(zend_object * object,zend_string * member,int type,void ** cache_slot)461 static ZEND_COLD zval *zend_closure_get_property_ptr_ptr(zend_object *object, zend_string *member, int type, void **cache_slot) /* {{{ */
462 {
463 	ZEND_CLOSURE_PROPERTY_ERROR();
464 	return NULL;
465 }
466 /* }}} */
467 
zend_closure_has_property(zend_object * object,zend_string * member,int has_set_exists,void ** cache_slot)468 static ZEND_COLD int zend_closure_has_property(zend_object *object, zend_string *member, int has_set_exists, void **cache_slot) /* {{{ */
469 {
470 	if (has_set_exists != ZEND_PROPERTY_EXISTS) {
471 		ZEND_CLOSURE_PROPERTY_ERROR();
472 	}
473 	return 0;
474 }
475 /* }}} */
476 
zend_closure_unset_property(zend_object * object,zend_string * member,void ** cache_slot)477 static ZEND_COLD void zend_closure_unset_property(zend_object *object, zend_string *member, void **cache_slot) /* {{{ */
478 {
479 	ZEND_CLOSURE_PROPERTY_ERROR();
480 }
481 /* }}} */
482 
zend_closure_free_storage(zend_object * object)483 static void zend_closure_free_storage(zend_object *object) /* {{{ */
484 {
485 	zend_closure *closure = (zend_closure *)object;
486 
487 	zend_object_std_dtor(&closure->std);
488 
489 	if (closure->func.type == ZEND_USER_FUNCTION) {
490 		destroy_op_array(&closure->func.op_array);
491 	} else if (closure->orig_internal_handler == zend_closure_call_magic) {
492 		zend_string_release(closure->func.common.function_name);
493 	}
494 
495 	if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
496 		zval_ptr_dtor(&closure->this_ptr);
497 	}
498 }
499 /* }}} */
500 
zend_closure_new(zend_class_entry * class_type)501 static zend_object *zend_closure_new(zend_class_entry *class_type) /* {{{ */
502 {
503 	zend_closure *closure;
504 
505 	closure = emalloc(sizeof(zend_closure));
506 	memset(closure, 0, sizeof(zend_closure));
507 
508 	zend_object_std_init(&closure->std, class_type);
509 	closure->std.handlers = &closure_handlers;
510 
511 	return (zend_object*)closure;
512 }
513 /* }}} */
514 
zend_closure_clone(zend_object * zobject)515 static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */
516 {
517 	zend_closure *closure = (zend_closure *)zobject;
518 	zval result;
519 
520 	zend_create_closure(&result, &closure->func,
521 		closure->func.common.scope, closure->called_scope, &closure->this_ptr);
522 	return Z_OBJ(result);
523 }
524 /* }}} */
525 
zend_closure_get_closure(zend_object * obj,zend_class_entry ** ce_ptr,zend_function ** fptr_ptr,zend_object ** obj_ptr,zend_bool check_only)526 int zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, zend_bool check_only) /* {{{ */
527 {
528 	zend_closure *closure = (zend_closure *)obj;
529 	*fptr_ptr = &closure->func;
530 	*ce_ptr = closure->called_scope;
531 
532 	if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
533 		*obj_ptr = Z_OBJ(closure->this_ptr);
534 	} else {
535 		*obj_ptr = NULL;
536 	}
537 
538 	return SUCCESS;
539 }
540 /* }}} */
541 
542 /* *is_temp is int due to Object Handler API */
zend_closure_get_debug_info(zend_object * object,int * is_temp)543 static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) /* {{{ */
544 {
545 	zend_closure *closure = (zend_closure *)object;
546 	zval val;
547 	struct _zend_arg_info *arg_info = closure->func.common.arg_info;
548 	HashTable *debug_info;
549 	zend_bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
550 
551 	*is_temp = 1;
552 
553 	debug_info = zend_new_array(8);
554 
555 	if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
556 		zval *var;
557 		zend_string *key;
558 		HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
559 
560 		array_init(&val);
561 
562 		ZEND_HASH_FOREACH_STR_KEY_VAL(static_variables, key, var) {
563 			zval copy;
564 
565 			if (Z_TYPE_P(var) == IS_CONSTANT_AST) {
566 				ZVAL_STRING(&copy, "<constant ast>");
567 			} else {
568 				if (Z_ISREF_P(var) && Z_REFCOUNT_P(var) == 1) {
569 					var = Z_REFVAL_P(var);
570 				}
571 				ZVAL_COPY(&copy, var);
572 			}
573 
574 			zend_hash_add_new(Z_ARRVAL(val), key, &copy);
575 		} ZEND_HASH_FOREACH_END();
576 
577 		if (zend_hash_num_elements(Z_ARRVAL(val))) {
578 			zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_STATIC), &val);
579 		} else {
580 			zval_ptr_dtor(&val);
581 		}
582 	}
583 
584 	if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
585 		Z_ADDREF(closure->this_ptr);
586 		zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_THIS), &closure->this_ptr);
587 	}
588 
589 	if (arg_info &&
590 		(closure->func.common.num_args ||
591 		 (closure->func.common.fn_flags & ZEND_ACC_VARIADIC))) {
592 		uint32_t i, num_args, required = closure->func.common.required_num_args;
593 
594 		array_init(&val);
595 
596 		num_args = closure->func.common.num_args;
597 		if (closure->func.common.fn_flags & ZEND_ACC_VARIADIC) {
598 			num_args++;
599 		}
600 		for (i = 0; i < num_args; i++) {
601 			zend_string *name;
602 			zval info;
603 			if (arg_info->name) {
604 				if (zstr_args) {
605 					name = zend_strpprintf(0, "%s$%s",
606 							ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
607 							ZSTR_VAL(arg_info->name));
608 				} else {
609 					name = zend_strpprintf(0, "%s$%s",
610 							ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
611 							((zend_internal_arg_info*)arg_info)->name);
612 				}
613 			} else {
614 				name = zend_strpprintf(0, "%s$param%d",
615 						ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
616 						i + 1);
617 			}
618 			ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "<optional>" : "<required>"));
619 			zend_hash_update(Z_ARRVAL(val), name, &info);
620 			zend_string_release_ex(name, 0);
621 			arg_info++;
622 		}
623 		zend_hash_str_update(debug_info, "parameter", sizeof("parameter")-1, &val);
624 	}
625 
626 	return debug_info;
627 }
628 /* }}} */
629 
zend_closure_get_gc(zend_object * obj,zval ** table,int * n)630 static HashTable *zend_closure_get_gc(zend_object *obj, zval **table, int *n) /* {{{ */
631 {
632 	zend_closure *closure = (zend_closure *)obj;
633 
634 	*table = Z_TYPE(closure->this_ptr) != IS_NULL ? &closure->this_ptr : NULL;
635 	*n = Z_TYPE(closure->this_ptr) != IS_NULL ? 1 : 0;
636 	return (closure->func.type == ZEND_USER_FUNCTION) ?
637 		ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr) : NULL;
638 }
639 /* }}} */
640 
641 /* {{{ Private constructor preventing instantiation */
ZEND_METHOD(Closure,__construct)642 ZEND_COLD ZEND_METHOD(Closure, __construct)
643 {
644 	zend_throw_error(NULL, "Instantiation of class Closure is not allowed");
645 }
646 /* }}} */
647 
zend_register_closure_ce(void)648 void zend_register_closure_ce(void) /* {{{ */
649 {
650 	zend_class_entry ce;
651 
652 	INIT_CLASS_ENTRY(ce, "Closure", class_Closure_methods);
653 	zend_ce_closure = zend_register_internal_class(&ce);
654 	zend_ce_closure->ce_flags |= ZEND_ACC_FINAL;
655 	zend_ce_closure->create_object = zend_closure_new;
656 	zend_ce_closure->serialize = zend_class_serialize_deny;
657 	zend_ce_closure->unserialize = zend_class_unserialize_deny;
658 
659 	memcpy(&closure_handlers, &std_object_handlers, sizeof(zend_object_handlers));
660 	closure_handlers.free_obj = zend_closure_free_storage;
661 	closure_handlers.get_constructor = zend_closure_get_constructor;
662 	closure_handlers.get_method = zend_closure_get_method;
663 	closure_handlers.write_property = zend_closure_write_property;
664 	closure_handlers.read_property = zend_closure_read_property;
665 	closure_handlers.get_property_ptr_ptr = zend_closure_get_property_ptr_ptr;
666 	closure_handlers.has_property = zend_closure_has_property;
667 	closure_handlers.unset_property = zend_closure_unset_property;
668 	closure_handlers.compare = zend_closure_compare;
669 	closure_handlers.clone_obj = zend_closure_clone;
670 	closure_handlers.get_debug_info = zend_closure_get_debug_info;
671 	closure_handlers.get_closure = zend_closure_get_closure;
672 	closure_handlers.get_gc = zend_closure_get_gc;
673 }
674 /* }}} */
675 
ZEND_NAMED_FUNCTION(zend_closure_internal_handler)676 static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */
677 {
678 	zend_closure *closure = (zend_closure*)ZEND_CLOSURE_OBJECT(EX(func));
679 	closure->orig_internal_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
680 	OBJ_RELEASE((zend_object*)closure);
681 	EX(func) = NULL;
682 }
683 /* }}} */
684 
zend_create_closure(zval * res,zend_function * func,zend_class_entry * scope,zend_class_entry * called_scope,zval * this_ptr)685 ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
686 {
687 	zend_closure *closure;
688 
689 	object_init_ex(res, zend_ce_closure);
690 
691 	closure = (zend_closure *)Z_OBJ_P(res);
692 
693 	if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) {
694 		/* use dummy scope if we're binding an object without specifying a scope */
695 		/* maybe it would be better to create one for this purpose */
696 		scope = zend_ce_closure;
697 	}
698 
699 	if (func->type == ZEND_USER_FUNCTION) {
700 		memcpy(&closure->func, func, sizeof(zend_op_array));
701 		closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
702 		closure->func.common.fn_flags &= ~ZEND_ACC_IMMUTABLE;
703 
704 		if (closure->func.op_array.static_variables) {
705 			closure->func.op_array.static_variables =
706 				zend_array_dup(closure->func.op_array.static_variables);
707 		}
708 		ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr,
709 			&closure->func.op_array.static_variables);
710 
711 		/* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */
712 		if (!ZEND_MAP_PTR_GET(closure->func.op_array.run_time_cache)
713 			|| func->common.scope != scope
714 			|| (func->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE)
715 		) {
716 			void *ptr;
717 
718 			if (!ZEND_MAP_PTR_GET(func->op_array.run_time_cache)
719 			 && (func->common.fn_flags & ZEND_ACC_CLOSURE)
720 			 && (func->common.scope == scope ||
721 			     !(func->common.fn_flags & ZEND_ACC_IMMUTABLE))) {
722 				/* If a real closure is used for the first time, we create a shared runtime cache
723 				 * and remember which scope it is for. */
724 				if (func->common.scope != scope) {
725 					func->common.scope = scope;
726 				}
727 				closure->func.op_array.fn_flags &= ~ZEND_ACC_HEAP_RT_CACHE;
728 				ptr = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
729 				ZEND_MAP_PTR_SET(func->op_array.run_time_cache, ptr);
730 				ZEND_MAP_PTR_SET(closure->func.op_array.run_time_cache, ptr);
731 			} else {
732 				/* Otherwise, we use a non-shared runtime cache */
733 				closure->func.op_array.fn_flags |= ZEND_ACC_HEAP_RT_CACHE;
734 				ptr = emalloc(sizeof(void*) + func->op_array.cache_size);
735 				ZEND_MAP_PTR_INIT(closure->func.op_array.run_time_cache, ptr);
736 				ptr = (char*)ptr + sizeof(void*);
737 				ZEND_MAP_PTR_SET(closure->func.op_array.run_time_cache, ptr);
738 			}
739 			memset(ptr, 0, func->op_array.cache_size);
740 		}
741 		zend_string_addref(closure->func.op_array.function_name);
742 		if (closure->func.op_array.refcount) {
743 			(*closure->func.op_array.refcount)++;
744 		}
745 	} else {
746 		memcpy(&closure->func, func, sizeof(zend_internal_function));
747 		closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
748 		/* wrap internal function handler to avoid memory leak */
749 		if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) {
750 			/* avoid infinity recursion, by taking handler from nested closure */
751 			zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func));
752 			ZEND_ASSERT(nested->std.ce == zend_ce_closure);
753 			closure->orig_internal_handler = nested->orig_internal_handler;
754 		} else {
755 			closure->orig_internal_handler = closure->func.internal_function.handler;
756 		}
757 		closure->func.internal_function.handler = zend_closure_internal_handler;
758 		if (!func->common.scope) {
759 			/* if it's a free function, we won't set scope & this since they're meaningless */
760 			this_ptr = NULL;
761 			scope = NULL;
762 		}
763 	}
764 
765 	ZVAL_UNDEF(&closure->this_ptr);
766 	/* Invariant:
767 	 * If the closure is unscoped or static, it has no bound object. */
768 	closure->func.common.scope = scope;
769 	closure->called_scope = called_scope;
770 	if (scope) {
771 		closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
772 		if (this_ptr && Z_TYPE_P(this_ptr) == IS_OBJECT && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
773 			ZVAL_OBJ_COPY(&closure->this_ptr, Z_OBJ_P(this_ptr));
774 		}
775 	}
776 }
777 /* }}} */
778 
zend_create_fake_closure(zval * res,zend_function * func,zend_class_entry * scope,zend_class_entry * called_scope,zval * this_ptr)779 ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
780 {
781 	zend_closure *closure;
782 
783 	zend_create_closure(res, func, scope, called_scope, this_ptr);
784 
785 	closure = (zend_closure *)Z_OBJ_P(res);
786 	closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE;
787 }
788 /* }}} */
789 
zend_closure_bind_var(zval * closure_zv,zend_string * var_name,zval * var)790 void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */
791 {
792 	zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
793 	HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
794 	zend_hash_update(static_variables, var_name, var);
795 }
796 /* }}} */
797 
zend_closure_bind_var_ex(zval * closure_zv,uint32_t offset,zval * val)798 void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {{{ */
799 {
800 	zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
801 	HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
802 	zval *var = (zval*)((char*)static_variables->arData + offset);
803 	zval_ptr_dtor(var);
804 	ZVAL_COPY_VALUE(var, val);
805 }
806 /* }}} */
807