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