xref: /PHP-5.3/Zend/zend_closures.c (revision 831fbcf3)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2013 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@zend.com>                             |
17    |          Marcus Boerger <helly@php.net>                              |
18    +----------------------------------------------------------------------+
19 */
20 
21 /* $Id$ */
22 
23 #include "zend.h"
24 #include "zend_API.h"
25 #include "zend_closures.h"
26 #include "zend_exceptions.h"
27 #include "zend_interfaces.h"
28 #include "zend_objects.h"
29 #include "zend_objects_API.h"
30 #include "zend_globals.h"
31 
32 #define ZEND_CLOSURE_PRINT_NAME "Closure object"
33 
34 #define ZEND_CLOSURE_PROPERTY_ERROR() \
35 	zend_error(E_RECOVERABLE_ERROR, "Closure object cannot have properties")
36 
37 typedef struct _zend_closure {
38 	zend_object    std;
39 	zend_function  func;
40 	HashTable     *debug_info;
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 
ZEND_METHOD(Closure,__invoke)47 ZEND_METHOD(Closure, __invoke) /* {{{ */
48 {
49 	zend_function *func = EG(current_execute_data)->function_state.function;
50 	zval ***arguments;
51 	zval *closure_result_ptr = NULL;
52 
53 	arguments = emalloc(sizeof(zval**) * ZEND_NUM_ARGS());
54 	if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), arguments) == FAILURE) {
55 		efree(arguments);
56 		zend_error(E_RECOVERABLE_ERROR, "Cannot get arguments for calling closure");
57 		RETVAL_FALSE;
58 	} else if (call_user_function_ex(CG(function_table), NULL, this_ptr, &closure_result_ptr, ZEND_NUM_ARGS(), arguments, 1, NULL TSRMLS_CC) == FAILURE) {
59 		RETVAL_FALSE;
60 	} else if (closure_result_ptr) {
61 		if (Z_ISREF_P(closure_result_ptr) && return_value_ptr) {
62 			if (return_value) {
63 				zval_ptr_dtor(&return_value);
64 			}
65 			*return_value_ptr = closure_result_ptr;
66 		} else {
67 			RETVAL_ZVAL(closure_result_ptr, 1, 1);
68 		}
69 	}
70 	efree(arguments);
71 
72 	/* destruct the function also, then - we have allocated it in get_method */
73 	efree(func->internal_function.function_name);
74 	efree(func);
75 }
76 /* }}} */
77 
zend_closure_get_constructor(zval * object TSRMLS_DC)78 static zend_function *zend_closure_get_constructor(zval *object TSRMLS_DC) /* {{{ */
79 {
80 	zend_error(E_RECOVERABLE_ERROR, "Instantiation of 'Closure' is not allowed");
81 	return NULL;
82 }
83 /* }}} */
84 
zend_closure_compare_objects(zval * o1,zval * o2 TSRMLS_DC)85 static int zend_closure_compare_objects(zval *o1, zval *o2 TSRMLS_DC) /* {{{ */
86 {
87 	return (Z_OBJ_HANDLE_P(o1) != Z_OBJ_HANDLE_P(o2));
88 }
89 /* }}} */
90 
zend_get_closure_invoke_method(zval * obj TSRMLS_DC)91 ZEND_API zend_function *zend_get_closure_invoke_method(zval *obj TSRMLS_DC) /* {{{ */
92 {
93 	zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
94 	zend_function *invoke = (zend_function*)emalloc(sizeof(zend_function));
95 
96 	invoke->common = closure->func.common;
97 	invoke->type = ZEND_INTERNAL_FUNCTION;
98 	invoke->internal_function.fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER;
99 	invoke->internal_function.handler = ZEND_MN(Closure___invoke);
100 	invoke->internal_function.module = 0;
101 	invoke->internal_function.scope = zend_ce_closure;
102 	invoke->internal_function.function_name = estrndup(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1);
103 	return invoke;
104 }
105 /* }}} */
106 
zend_get_closure_method_def(zval * obj TSRMLS_DC)107 ZEND_API const zend_function *zend_get_closure_method_def(zval *obj TSRMLS_DC) /* {{{ */
108 {
109 	zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
110 	return &closure->func;
111 }
112 /* }}} */
113 
zend_closure_get_method(zval ** object_ptr,char * method_name,int method_len TSRMLS_DC)114 static zend_function *zend_closure_get_method(zval **object_ptr, char *method_name, int method_len TSRMLS_DC) /* {{{ */
115 {
116 	char *lc_name;
117 	ALLOCA_FLAG(use_heap)
118 
119 	lc_name = do_alloca(method_len + 1, use_heap);
120 	zend_str_tolower_copy(lc_name, method_name, method_len);
121 	if ((method_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) &&
122 		memcmp(lc_name, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0
123 	) {
124 		free_alloca(lc_name, use_heap);
125 		return zend_get_closure_invoke_method(*object_ptr TSRMLS_CC);
126 	}
127 	free_alloca(lc_name, use_heap);
128 	return NULL;
129 }
130 /* }}} */
131 
zend_closure_read_property(zval * object,zval * member,int type TSRMLS_DC)132 static zval *zend_closure_read_property(zval *object, zval *member, int type TSRMLS_DC) /* {{{ */
133 {
134 	ZEND_CLOSURE_PROPERTY_ERROR();
135 	Z_ADDREF(EG(uninitialized_zval));
136 	return &EG(uninitialized_zval);
137 }
138 /* }}} */
139 
zend_closure_write_property(zval * object,zval * member,zval * value TSRMLS_DC)140 static void zend_closure_write_property(zval *object, zval *member, zval *value TSRMLS_DC) /* {{{ */
141 {
142 	ZEND_CLOSURE_PROPERTY_ERROR();
143 }
144 /* }}} */
145 
zend_closure_get_property_ptr_ptr(zval * object,zval * member TSRMLS_DC)146 static zval **zend_closure_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC) /* {{{ */
147 {
148 	ZEND_CLOSURE_PROPERTY_ERROR();
149 	return NULL;
150 }
151 /* }}} */
152 
zend_closure_has_property(zval * object,zval * member,int has_set_exists TSRMLS_DC)153 static int zend_closure_has_property(zval *object, zval *member, int has_set_exists TSRMLS_DC) /* {{{ */
154 {
155 	if (has_set_exists != 2) {
156 		ZEND_CLOSURE_PROPERTY_ERROR();
157 	}
158 	return 0;
159 }
160 /* }}} */
161 
zend_closure_unset_property(zval * object,zval * member TSRMLS_DC)162 static void zend_closure_unset_property(zval *object, zval *member TSRMLS_DC) /* {{{ */
163 {
164 	ZEND_CLOSURE_PROPERTY_ERROR();
165 }
166 /* }}} */
167 
zend_closure_free_storage(void * object TSRMLS_DC)168 static void zend_closure_free_storage(void *object TSRMLS_DC) /* {{{ */
169 {
170 	zend_closure *closure = (zend_closure *)object;
171 
172 	zend_object_std_dtor(&closure->std TSRMLS_CC);
173 
174 	if (closure->func.type == ZEND_USER_FUNCTION) {
175 		zend_execute_data *ex = EG(current_execute_data);
176 		while (ex) {
177 			if (ex->op_array == &closure->func.op_array) {
178 				zend_error(E_ERROR, "Cannot destroy active lambda function");
179 			}
180 			ex = ex->prev_execute_data;
181 		}
182 		destroy_op_array(&closure->func.op_array TSRMLS_CC);
183 	}
184 
185 	if (closure->debug_info != NULL) {
186 		zend_hash_destroy(closure->debug_info);
187 		efree(closure->debug_info);
188 	}
189 
190 	efree(closure);
191 }
192 /* }}} */
193 
zend_closure_new(zend_class_entry * class_type TSRMLS_DC)194 static zend_object_value zend_closure_new(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
195 {
196 	zend_closure *closure;
197 	zend_object_value object;
198 
199 	closure = emalloc(sizeof(zend_closure));
200 	memset(closure, 0, sizeof(zend_closure));
201 
202 	zend_object_std_init(&closure->std, class_type TSRMLS_CC);
203 
204 	object.handle = zend_objects_store_put(closure, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) zend_closure_free_storage, NULL TSRMLS_CC);
205 	object.handlers = &closure_handlers;
206 
207 	return object;
208 }
209 /* }}} */
210 
zend_closure_get_closure(zval * obj,zend_class_entry ** ce_ptr,zend_function ** fptr_ptr,zval ** zobj_ptr TSRMLS_DC)211 int zend_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) /* {{{ */
212 {
213 	zend_closure *closure;
214 
215 	if (Z_TYPE_P(obj) != IS_OBJECT) {
216 		return FAILURE;
217 	}
218 
219 	closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
220 	*fptr_ptr = &closure->func;
221 
222 	if (zobj_ptr) {
223 		*zobj_ptr = NULL;
224 	}
225 	*ce_ptr = NULL;
226 	return SUCCESS;
227 }
228 /* }}} */
229 
zend_closure_get_debug_info(zval * object,int * is_temp TSRMLS_DC)230 static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
231 {
232 	zend_closure *closure = (zend_closure *)zend_object_store_get_object(object TSRMLS_CC);
233 	zval *val;
234 	struct _zend_arg_info *arg_info = closure->func.common.arg_info;
235 
236 	*is_temp = 0;
237 
238 	if (closure->debug_info == NULL) {
239 		ALLOC_HASHTABLE(closure->debug_info);
240 		zend_hash_init(closure->debug_info, 1, NULL, ZVAL_PTR_DTOR, 0);
241 	}
242 	if (closure->debug_info->nApplyCount == 0) {
243 		if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
244 			HashTable *static_variables = closure->func.op_array.static_variables;
245 			MAKE_STD_ZVAL(val);
246 			array_init(val);
247 			zend_hash_copy(Z_ARRVAL_P(val), static_variables, (copy_ctor_func_t)zval_add_ref, NULL, sizeof(zval*));
248 			zend_symtable_update(closure->debug_info, "static", sizeof("static"), (void *) &val, sizeof(zval *), NULL);
249 		}
250 
251 		if (arg_info) {
252 			zend_uint i, required = closure->func.common.required_num_args;
253 
254 			MAKE_STD_ZVAL(val);
255 			array_init(val);
256 
257 			for (i = 0; i < closure->func.common.num_args; i++) {
258 				char *name, *info;
259 				int name_len, info_len;
260 				if (arg_info->name) {
261 					name_len = zend_spprintf(&name, 0, "%s$%s",
262 									arg_info->pass_by_reference ? "&" : "",
263 									arg_info->name);
264 				} else {
265 					name_len = zend_spprintf(&name, 0, "%s$param%d",
266 									arg_info->pass_by_reference ? "&" : "",
267 									i + 1);
268 				}
269 				info_len = zend_spprintf(&info, 0, "%s",
270 								i >= required ? "<optional>" : "<required>");
271 				add_assoc_stringl_ex(val, name, name_len + 1, info, info_len, 0);
272 				efree(name);
273 				arg_info++;
274 			}
275 			zend_symtable_update(closure->debug_info, "parameter", sizeof("parameter"), (void *) &val, sizeof(zval *), NULL);
276 		}
277 	}
278 
279 	return closure->debug_info;
280 }
281 /* }}} */
282 
zend_closure_get_properties(zval * obj TSRMLS_DC)283 static HashTable *zend_closure_get_properties(zval *obj TSRMLS_DC) /* {{{ */
284 {
285 	zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
286 
287 	if (GC_G(gc_active)) {
288 		return (closure->func.type == ZEND_USER_FUNCTION) ? closure->func.op_array.static_variables : NULL;
289 	}
290 
291 	return closure->std.properties;
292 }
293 /* }}} */
294 
295 /* {{{ proto Closure::__construct()
296    Private constructor preventing instantiation */
ZEND_METHOD(Closure,__construct)297 ZEND_METHOD(Closure, __construct)
298 {
299 	zend_error(E_RECOVERABLE_ERROR, "Instantiation of 'Closure' is not allowed");
300 }
301 /* }}} */
302 
303 static const zend_function_entry closure_functions[] = {
304 	ZEND_ME(Closure, __construct, NULL, ZEND_ACC_PRIVATE)
305 	{NULL, NULL, NULL}
306 };
307 
zend_register_closure_ce(TSRMLS_D)308 void zend_register_closure_ce(TSRMLS_D) /* {{{ */
309 {
310 	zend_class_entry ce;
311 
312 	INIT_CLASS_ENTRY(ce, "Closure", closure_functions);
313 	zend_ce_closure = zend_register_internal_class(&ce TSRMLS_CC);
314 	zend_ce_closure->ce_flags |= ZEND_ACC_FINAL_CLASS;
315 	zend_ce_closure->create_object = zend_closure_new;
316 	zend_ce_closure->serialize = zend_class_serialize_deny;
317 	zend_ce_closure->unserialize = zend_class_unserialize_deny;
318 
319 	memcpy(&closure_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
320 	closure_handlers.get_constructor = zend_closure_get_constructor;
321 	closure_handlers.get_method = zend_closure_get_method;
322 	closure_handlers.write_property = zend_closure_write_property;
323 	closure_handlers.read_property = zend_closure_read_property;
324 	closure_handlers.get_property_ptr_ptr = zend_closure_get_property_ptr_ptr;
325 	closure_handlers.has_property = zend_closure_has_property;
326 	closure_handlers.unset_property = zend_closure_unset_property;
327 	closure_handlers.compare_objects = zend_closure_compare_objects;
328 	closure_handlers.clone_obj = NULL;
329 	closure_handlers.get_debug_info = zend_closure_get_debug_info;
330 	closure_handlers.get_closure = zend_closure_get_closure;
331 	closure_handlers.get_properties = zend_closure_get_properties;
332 }
333 /* }}} */
334 
zval_copy_static_var(zval ** p TSRMLS_DC,int num_args,va_list args,zend_hash_key * key)335 static int zval_copy_static_var(zval **p TSRMLS_DC, int num_args, va_list args, zend_hash_key *key) /* {{{ */
336 {
337 	HashTable *target = va_arg(args, HashTable*);
338 	zend_bool is_ref;
339 	zval *tmp;
340 
341 	if (Z_TYPE_PP(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) {
342 		is_ref = Z_TYPE_PP(p) & IS_LEXICAL_REF;
343 
344 		if (!EG(active_symbol_table)) {
345 			zend_rebuild_symbol_table(TSRMLS_C);
346 		}
347 		if (zend_hash_quick_find(EG(active_symbol_table), key->arKey, key->nKeyLength, key->h, (void **) &p) == FAILURE) {
348 			if (is_ref) {
349 				ALLOC_INIT_ZVAL(tmp);
350 				Z_SET_ISREF_P(tmp);
351 				zend_hash_quick_add(EG(active_symbol_table), key->arKey, key->nKeyLength, key->h, &tmp, sizeof(zval*), (void**)&p);
352 			} else {
353 				tmp = EG(uninitialized_zval_ptr);
354 				zend_error(E_NOTICE,"Undefined variable: %s", key->arKey);
355 			}
356 		} else {
357 			if (is_ref) {
358 				SEPARATE_ZVAL_TO_MAKE_IS_REF(p);
359 				tmp = *p;
360 			} else if (Z_ISREF_PP(p)) {
361 				ALLOC_INIT_ZVAL(tmp);
362 				*tmp = **p;
363 				zval_copy_ctor(tmp);
364 				Z_SET_REFCOUNT_P(tmp, 0);
365 				Z_UNSET_ISREF_P(tmp);
366 			} else {
367 				tmp = *p;
368 			}
369 		}
370 	} else {
371 		tmp = *p;
372 	}
373 	if (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, &tmp, sizeof(zval*), NULL) == SUCCESS) {
374 		Z_ADDREF_P(tmp);
375 	}
376 	return ZEND_HASH_APPLY_KEEP;
377 }
378 /* }}} */
379 
zend_create_closure(zval * res,zend_function * func TSRMLS_DC)380 ZEND_API void zend_create_closure(zval *res, zend_function *func TSRMLS_DC) /* {{{ */
381 {
382 	zend_closure *closure;
383 
384 	object_init_ex(res, zend_ce_closure);
385 
386 	closure = (zend_closure *)zend_object_store_get_object(res TSRMLS_CC);
387 
388 	closure->func = *func;
389 	closure->func.common.prototype = NULL;
390 
391 	if (closure->func.type == ZEND_USER_FUNCTION) {
392 		if (closure->func.op_array.static_variables) {
393 			HashTable *static_variables = closure->func.op_array.static_variables;
394 
395 			ALLOC_HASHTABLE(closure->func.op_array.static_variables);
396 			zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
397 			zend_hash_apply_with_arguments(static_variables TSRMLS_CC, (apply_func_args_t)zval_copy_static_var, 1, closure->func.op_array.static_variables);
398 		}
399 		(*closure->func.op_array.refcount)++;
400 	}
401 
402 	closure->func.common.scope = NULL;
403 }
404 /* }}} */
405 
406 /*
407  * Local variables:
408  * tab-width: 4
409  * c-basic-offset: 4
410  * indent-tabs-mode: t
411  * End:
412  */
413