zend_closures.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Zend Engine |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1998-2016 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. /* $Id$ */
  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. #define ZEND_CLOSURE_PRINT_NAME "Closure object"
  30. #define ZEND_CLOSURE_PROPERTY_ERROR() \
  31. zend_error(E_RECOVERABLE_ERROR, "Closure object cannot have properties")
  32. typedef struct _zend_closure {
  33. zend_object std;
  34. zend_function func;
  35. zval *this_ptr;
  36. HashTable *debug_info;
  37. } zend_closure;
  38. /* non-static since it needs to be referenced */
  39. ZEND_API zend_class_entry *zend_ce_closure;
  40. static zend_object_handlers closure_handlers;
  41. ZEND_METHOD(Closure, __invoke) /* {{{ */
  42. {
  43. zend_function *func = EG(current_execute_data)->function_state.function;
  44. zval ***arguments;
  45. zval *closure_result_ptr = NULL;
  46. arguments = emalloc(sizeof(zval**) * ZEND_NUM_ARGS());
  47. if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), arguments) == FAILURE) {
  48. efree(arguments);
  49. zend_error(E_RECOVERABLE_ERROR, "Cannot get arguments for calling closure");
  50. RETVAL_FALSE;
  51. } else if (call_user_function_ex(CG(function_table), NULL, this_ptr, &closure_result_ptr, ZEND_NUM_ARGS(), arguments, 1, NULL TSRMLS_CC) == FAILURE) {
  52. RETVAL_FALSE;
  53. } else if (closure_result_ptr) {
  54. zval_ptr_dtor(&return_value);
  55. *return_value_ptr = closure_result_ptr;
  56. }
  57. efree(arguments);
  58. /* destruct the function also, then - we have allocated it in get_method */
  59. efree((char*)func->internal_function.function_name);
  60. efree(func);
  61. }
  62. /* }}} */
  63. /* {{{ proto Closure Closure::bind(Closure $old, object $to [, mixed $scope = "static" ] )
  64. Create a closure from another one and bind to another object and scope */
  65. ZEND_METHOD(Closure, bind)
  66. {
  67. zval *newthis, *zclosure, *scope_arg = NULL;
  68. zend_closure *closure;
  69. zend_class_entry *ce, **ce_p;
  70. if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) {
  71. RETURN_NULL();
  72. }
  73. closure = (zend_closure *)zend_object_store_get_object(zclosure TSRMLS_CC);
  74. if ((newthis != NULL) && (closure->func.common.fn_flags & ZEND_ACC_STATIC)) {
  75. zend_error(E_WARNING, "Cannot bind an instance to a static closure");
  76. }
  77. if (newthis == NULL && !(closure->func.common.fn_flags & ZEND_ACC_STATIC)
  78. && closure->func.common.scope && closure->func.type == ZEND_INTERNAL_FUNCTION) {
  79. zend_error(E_WARNING, "Cannot unbind $this of internal method");
  80. return;
  81. }
  82. if (scope_arg != NULL) { /* scope argument was given */
  83. if (IS_ZEND_STD_OBJECT(*scope_arg)) {
  84. ce = Z_OBJCE_P(scope_arg);
  85. } else if (Z_TYPE_P(scope_arg) == IS_NULL) {
  86. ce = NULL;
  87. } else {
  88. char *class_name;
  89. int class_name_len;
  90. zval tmp_zval;
  91. INIT_ZVAL(tmp_zval);
  92. if (Z_TYPE_P(scope_arg) == IS_STRING) {
  93. class_name = Z_STRVAL_P(scope_arg);
  94. class_name_len = Z_STRLEN_P(scope_arg);
  95. } else {
  96. tmp_zval = *scope_arg;
  97. zval_copy_ctor(&tmp_zval);
  98. convert_to_string(&tmp_zval);
  99. class_name = Z_STRVAL(tmp_zval);
  100. class_name_len = Z_STRLEN(tmp_zval);
  101. }
  102. if ((class_name_len == sizeof("static") - 1) &&
  103. (memcmp("static", class_name, sizeof("static") - 1) == 0)) {
  104. ce = closure->func.common.scope;
  105. }
  106. else if (zend_lookup_class_ex(class_name, class_name_len, NULL, 1, &ce_p TSRMLS_CC) == FAILURE) {
  107. zend_error(E_WARNING, "Class '%s' not found", class_name);
  108. zval_dtor(&tmp_zval);
  109. RETURN_NULL();
  110. } else {
  111. ce = *ce_p;
  112. }
  113. zval_dtor(&tmp_zval);
  114. }
  115. } else { /* scope argument not given; do not change the scope by default */
  116. ce = closure->func.common.scope;
  117. }
  118. /* verify that we aren't binding internal function to a wrong scope */
  119. if (closure->func.type == ZEND_INTERNAL_FUNCTION && closure->func.common.scope != NULL) {
  120. if (ce && !instanceof_function(ce, closure->func.common.scope TSRMLS_CC)) {
  121. zend_error(E_WARNING, "Cannot bind function %s::%s to scope class %s", closure->func.common.scope->name, closure->func.common.function_name, ce->name);
  122. return;
  123. }
  124. if (ce && newthis && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0 &&
  125. !instanceof_function(Z_OBJCE_P(newthis), closure->func.common.scope TSRMLS_CC)) {
  126. zend_error(E_WARNING, "Cannot bind internal method %s::%s() to object of class %s", closure->func.common.scope->name, closure->func.common.function_name, Z_OBJCE_P(newthis)->name);
  127. return;
  128. }
  129. }
  130. zend_create_closure(return_value, &closure->func, ce, newthis TSRMLS_CC);
  131. }
  132. /* }}} */
  133. static zend_function *zend_closure_get_constructor(zval *object TSRMLS_DC) /* {{{ */
  134. {
  135. zend_error(E_RECOVERABLE_ERROR, "Instantiation of 'Closure' is not allowed");
  136. return NULL;
  137. }
  138. /* }}} */
  139. static int zend_closure_compare_objects(zval *o1, zval *o2 TSRMLS_DC) /* {{{ */
  140. {
  141. return (Z_OBJ_HANDLE_P(o1) != Z_OBJ_HANDLE_P(o2));
  142. }
  143. /* }}} */
  144. ZEND_API zend_function *zend_get_closure_invoke_method(zval *obj TSRMLS_DC) /* {{{ */
  145. {
  146. zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
  147. zend_function *invoke = (zend_function*)emalloc(sizeof(zend_function));
  148. const zend_uint keep_flags = ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_VARIADIC;
  149. invoke->common = closure->func.common;
  150. invoke->type = ZEND_INTERNAL_FUNCTION;
  151. invoke->internal_function.fn_flags =
  152. ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags);
  153. invoke->internal_function.handler = ZEND_MN(Closure___invoke);
  154. invoke->internal_function.module = 0;
  155. invoke->internal_function.scope = zend_ce_closure;
  156. invoke->internal_function.function_name = estrndup(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1);
  157. return invoke;
  158. }
  159. /* }}} */
  160. ZEND_API const zend_function *zend_get_closure_method_def(zval *obj TSRMLS_DC) /* {{{ */
  161. {
  162. zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
  163. return &closure->func;
  164. }
  165. /* }}} */
  166. ZEND_API zval* zend_get_closure_this_ptr(zval *obj TSRMLS_DC) /* {{{ */
  167. {
  168. zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
  169. return closure->this_ptr;
  170. }
  171. /* }}} */
  172. static zend_function *zend_closure_get_method(zval **object_ptr, char *method_name, int method_len, const zend_literal *key TSRMLS_DC) /* {{{ */
  173. {
  174. char *lc_name;
  175. ALLOCA_FLAG(use_heap)
  176. lc_name = do_alloca(method_len + 1, use_heap);
  177. zend_str_tolower_copy(lc_name, method_name, method_len);
  178. if ((method_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) &&
  179. memcmp(lc_name, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0
  180. ) {
  181. free_alloca(lc_name, use_heap);
  182. return zend_get_closure_invoke_method(*object_ptr TSRMLS_CC);
  183. }
  184. free_alloca(lc_name, use_heap);
  185. return std_object_handlers.get_method(object_ptr, method_name, method_len, key TSRMLS_CC);
  186. }
  187. /* }}} */
  188. static zval *zend_closure_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC) /* {{{ */
  189. {
  190. ZEND_CLOSURE_PROPERTY_ERROR();
  191. Z_ADDREF(EG(uninitialized_zval));
  192. return &EG(uninitialized_zval);
  193. }
  194. /* }}} */
  195. static void zend_closure_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC) /* {{{ */
  196. {
  197. ZEND_CLOSURE_PROPERTY_ERROR();
  198. }
  199. /* }}} */
  200. static zval **zend_closure_get_property_ptr_ptr(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC) /* {{{ */
  201. {
  202. ZEND_CLOSURE_PROPERTY_ERROR();
  203. return NULL;
  204. }
  205. /* }}} */
  206. static int zend_closure_has_property(zval *object, zval *member, int has_set_exists, const zend_literal *key TSRMLS_DC) /* {{{ */
  207. {
  208. if (has_set_exists != 2) {
  209. ZEND_CLOSURE_PROPERTY_ERROR();
  210. }
  211. return 0;
  212. }
  213. /* }}} */
  214. static void zend_closure_unset_property(zval *object, zval *member, const zend_literal *key TSRMLS_DC) /* {{{ */
  215. {
  216. ZEND_CLOSURE_PROPERTY_ERROR();
  217. }
  218. /* }}} */
  219. static void zend_closure_free_storage(void *object TSRMLS_DC) /* {{{ */
  220. {
  221. zend_closure *closure = (zend_closure *)object;
  222. zend_object_std_dtor(&closure->std TSRMLS_CC);
  223. if (closure->func.type == ZEND_USER_FUNCTION) {
  224. zend_execute_data *ex = EG(current_execute_data);
  225. while (ex) {
  226. if (ex->op_array == &closure->func.op_array) {
  227. zend_error(E_ERROR, "Cannot destroy active lambda function");
  228. }
  229. ex = ex->prev_execute_data;
  230. }
  231. destroy_op_array(&closure->func.op_array TSRMLS_CC);
  232. }
  233. if (closure->debug_info != NULL) {
  234. zend_hash_destroy(closure->debug_info);
  235. efree(closure->debug_info);
  236. }
  237. if (closure->this_ptr) {
  238. zval_ptr_dtor(&closure->this_ptr);
  239. }
  240. efree(closure);
  241. }
  242. /* }}} */
  243. static zend_object_value zend_closure_new(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
  244. {
  245. zend_closure *closure;
  246. zend_object_value object;
  247. closure = emalloc(sizeof(zend_closure));
  248. memset(closure, 0, sizeof(zend_closure));
  249. zend_object_std_init(&closure->std, class_type TSRMLS_CC);
  250. 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);
  251. object.handlers = &closure_handlers;
  252. return object;
  253. }
  254. /* }}} */
  255. static zend_object_value zend_closure_clone(zval *zobject TSRMLS_DC) /* {{{ */
  256. {
  257. zend_closure *closure = (zend_closure *)zend_object_store_get_object(zobject TSRMLS_CC);
  258. zval result;
  259. zend_create_closure(&result, &closure->func, closure->func.common.scope, closure->this_ptr TSRMLS_CC);
  260. return Z_OBJVAL(result);
  261. }
  262. /* }}} */
  263. int zend_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) /* {{{ */
  264. {
  265. zend_closure *closure;
  266. if (Z_TYPE_P(obj) != IS_OBJECT) {
  267. return FAILURE;
  268. }
  269. closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
  270. *fptr_ptr = &closure->func;
  271. if (closure->this_ptr) {
  272. if (zobj_ptr) {
  273. *zobj_ptr = closure->this_ptr;
  274. }
  275. *ce_ptr = Z_OBJCE_P(closure->this_ptr);
  276. } else {
  277. if (zobj_ptr) {
  278. *zobj_ptr = NULL;
  279. }
  280. *ce_ptr = closure->func.common.scope;
  281. }
  282. return SUCCESS;
  283. }
  284. /* }}} */
  285. static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
  286. {
  287. zend_closure *closure = (zend_closure *)zend_object_store_get_object(object TSRMLS_CC);
  288. zval *val;
  289. struct _zend_arg_info *arg_info = closure->func.common.arg_info;
  290. *is_temp = 0;
  291. if (closure->debug_info == NULL) {
  292. ALLOC_HASHTABLE(closure->debug_info);
  293. zend_hash_init(closure->debug_info, 1, NULL, ZVAL_PTR_DTOR, 0);
  294. }
  295. if (closure->debug_info->nApplyCount == 0) {
  296. if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
  297. HashTable *static_variables = closure->func.op_array.static_variables;
  298. MAKE_STD_ZVAL(val);
  299. array_init(val);
  300. zend_hash_copy(Z_ARRVAL_P(val), static_variables, (copy_ctor_func_t)zval_add_ref, NULL, sizeof(zval*));
  301. zend_hash_update(closure->debug_info, "static", sizeof("static"), (void *) &val, sizeof(zval *), NULL);
  302. }
  303. if (closure->this_ptr) {
  304. Z_ADDREF_P(closure->this_ptr);
  305. zend_symtable_update(closure->debug_info, "this", sizeof("this"), (void *) &closure->this_ptr, sizeof(zval *), NULL);
  306. }
  307. if (arg_info) {
  308. zend_uint i, required = closure->func.common.required_num_args;
  309. MAKE_STD_ZVAL(val);
  310. array_init(val);
  311. for (i = 0; i < closure->func.common.num_args; i++) {
  312. char *name, *info;
  313. int name_len, info_len;
  314. if (arg_info->name) {
  315. name_len = zend_spprintf(&name, 0, "%s$%s",
  316. arg_info->pass_by_reference ? "&" : "",
  317. arg_info->name);
  318. } else {
  319. name_len = zend_spprintf(&name, 0, "%s$param%d",
  320. arg_info->pass_by_reference ? "&" : "",
  321. i + 1);
  322. }
  323. info_len = zend_spprintf(&info, 0, "%s",
  324. i >= required ? "<optional>" : "<required>");
  325. add_assoc_stringl_ex(val, name, name_len + 1, info, info_len, 0);
  326. efree(name);
  327. arg_info++;
  328. }
  329. zend_hash_update(closure->debug_info, "parameter", sizeof("parameter"), (void *) &val, sizeof(zval *), NULL);
  330. }
  331. }
  332. return closure->debug_info;
  333. }
  334. /* }}} */
  335. static HashTable *zend_closure_get_gc(zval *obj, zval ***table, int *n TSRMLS_DC) /* {{{ */
  336. {
  337. zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
  338. *table = closure->this_ptr ? &closure->this_ptr : NULL;
  339. *n = closure->this_ptr ? 1 : 0;
  340. return (closure->func.type == ZEND_USER_FUNCTION) ?
  341. closure->func.op_array.static_variables : NULL;
  342. }
  343. /* }}} */
  344. /* {{{ proto Closure::__construct()
  345. Private constructor preventing instantiation */
  346. ZEND_METHOD(Closure, __construct)
  347. {
  348. zend_error(E_RECOVERABLE_ERROR, "Instantiation of 'Closure' is not allowed");
  349. }
  350. /* }}} */
  351. ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bindto, 0, 0, 1)
  352. ZEND_ARG_INFO(0, newthis)
  353. ZEND_ARG_INFO(0, newscope)
  354. ZEND_END_ARG_INFO()
  355. ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bind, 0, 0, 2)
  356. ZEND_ARG_INFO(0, closure)
  357. ZEND_ARG_INFO(0, newthis)
  358. ZEND_ARG_INFO(0, newscope)
  359. ZEND_END_ARG_INFO()
  360. static const zend_function_entry closure_functions[] = {
  361. ZEND_ME(Closure, __construct, NULL, ZEND_ACC_PRIVATE)
  362. ZEND_ME(Closure, bind, arginfo_closure_bind, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
  363. ZEND_MALIAS(Closure, bindTo, bind, arginfo_closure_bindto, ZEND_ACC_PUBLIC)
  364. {NULL, NULL, NULL}
  365. };
  366. void zend_register_closure_ce(TSRMLS_D) /* {{{ */
  367. {
  368. zend_class_entry ce;
  369. INIT_CLASS_ENTRY(ce, "Closure", closure_functions);
  370. zend_ce_closure = zend_register_internal_class(&ce TSRMLS_CC);
  371. zend_ce_closure->ce_flags |= ZEND_ACC_FINAL_CLASS;
  372. zend_ce_closure->create_object = zend_closure_new;
  373. zend_ce_closure->serialize = zend_class_serialize_deny;
  374. zend_ce_closure->unserialize = zend_class_unserialize_deny;
  375. memcpy(&closure_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
  376. closure_handlers.get_constructor = zend_closure_get_constructor;
  377. closure_handlers.get_method = zend_closure_get_method;
  378. closure_handlers.write_property = zend_closure_write_property;
  379. closure_handlers.read_property = zend_closure_read_property;
  380. closure_handlers.get_property_ptr_ptr = zend_closure_get_property_ptr_ptr;
  381. closure_handlers.has_property = zend_closure_has_property;
  382. closure_handlers.unset_property = zend_closure_unset_property;
  383. closure_handlers.compare_objects = zend_closure_compare_objects;
  384. closure_handlers.clone_obj = zend_closure_clone;
  385. closure_handlers.get_debug_info = zend_closure_get_debug_info;
  386. closure_handlers.get_closure = zend_closure_get_closure;
  387. closure_handlers.get_gc = zend_closure_get_gc;
  388. }
  389. /* }}} */
  390. ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zval *this_ptr TSRMLS_DC) /* {{{ */
  391. {
  392. zend_closure *closure;
  393. object_init_ex(res, zend_ce_closure);
  394. closure = (zend_closure *)zend_object_store_get_object(res TSRMLS_CC);
  395. closure->func = *func;
  396. closure->func.common.prototype = NULL;
  397. closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
  398. if ((scope == NULL) && (this_ptr != NULL)) {
  399. /* use dummy scope if we're binding an object without specifying a scope */
  400. /* maybe it would be better to create one for this purpose */
  401. scope = zend_ce_closure;
  402. }
  403. if (closure->func.type == ZEND_USER_FUNCTION) {
  404. if (closure->func.op_array.static_variables) {
  405. HashTable *static_variables = closure->func.op_array.static_variables;
  406. ALLOC_HASHTABLE(closure->func.op_array.static_variables);
  407. zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
  408. zend_hash_apply_with_arguments(static_variables TSRMLS_CC, (apply_func_args_t)zval_copy_static_var, 1, closure->func.op_array.static_variables);
  409. }
  410. closure->func.op_array.run_time_cache = NULL;
  411. (*closure->func.op_array.refcount)++;
  412. } else {
  413. if (!func->common.scope) {
  414. /* if it's a free function, we won't set scope & this since they're meaningless */
  415. this_ptr = NULL;
  416. scope = NULL;
  417. }
  418. }
  419. closure->this_ptr = NULL;
  420. /* Invariants:
  421. * If the closure is unscoped, it has no bound object.
  422. * The the closure is scoped, it's either static or it's bound */
  423. closure->func.common.scope = scope;
  424. if (scope) {
  425. closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
  426. if (this_ptr && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
  427. closure->this_ptr = this_ptr;
  428. Z_ADDREF_P(this_ptr);
  429. } else {
  430. closure->func.common.fn_flags |= ZEND_ACC_STATIC;
  431. }
  432. }
  433. }
  434. /* }}} */
  435. /*
  436. * Local variables:
  437. * tab-width: 4
  438. * c-basic-offset: 4
  439. * indent-tabs-mode: t
  440. * End:
  441. */