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