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