zend_generators.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  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: Nikita Popov <nikic@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. /* $Id$ */
  19. #include "zend.h"
  20. #include "zend_API.h"
  21. #include "zend_interfaces.h"
  22. #include "zend_exceptions.h"
  23. #include "zend_generators.h"
  24. ZEND_API zend_class_entry *zend_ce_generator;
  25. static zend_object_handlers zend_generator_handlers;
  26. static zend_object_value zend_generator_create(zend_class_entry *class_type TSRMLS_DC);
  27. static void zend_generator_cleanup_unfinished_execution(zend_generator *generator TSRMLS_DC) /* {{{ */
  28. {
  29. zend_execute_data *execute_data = generator->execute_data;
  30. zend_op_array *op_array = execute_data->op_array;
  31. if (generator->send_target) {
  32. Z_DELREF_PP(generator->send_target);
  33. generator->send_target = NULL;
  34. }
  35. /* Manually free loop variables, as execution couldn't reach their
  36. * SWITCH_FREE / FREE opcodes. */
  37. {
  38. /* -1 required because we want the last run opcode, not the
  39. * next to-be-run one. */
  40. zend_uint op_num = execute_data->opline - op_array->opcodes - 1;
  41. int i;
  42. for (i = 0; i < op_array->last_brk_cont; ++i) {
  43. zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i;
  44. if (brk_cont->start < 0) {
  45. continue;
  46. } else if (brk_cont->start > op_num) {
  47. break;
  48. } else if (brk_cont->brk > op_num) {
  49. zend_op *brk_opline = op_array->opcodes + brk_cont->brk;
  50. switch (brk_opline->opcode) {
  51. case ZEND_SWITCH_FREE:
  52. {
  53. temp_variable *var = EX_TMP_VAR(execute_data, brk_opline->op1.var);
  54. zval_ptr_dtor(&var->var.ptr);
  55. }
  56. break;
  57. case ZEND_FREE:
  58. {
  59. temp_variable *var = EX_TMP_VAR(execute_data, brk_opline->op1.var);
  60. zval_dtor(&var->tmp_var);
  61. }
  62. break;
  63. }
  64. }
  65. }
  66. }
  67. /* Clear any backed up stack arguments */
  68. {
  69. void **ptr = generator->stack->top - 1;
  70. void **end = zend_vm_stack_frame_base(execute_data);
  71. for (; ptr >= end; --ptr) {
  72. zval_ptr_dtor((zval **) ptr);
  73. }
  74. }
  75. /* If yield was used as a function argument there may be active
  76. * method calls those objects need to be freed */
  77. while (execute_data->call >= execute_data->call_slots) {
  78. if (execute_data->call->object) {
  79. zval_ptr_dtor(&execute_data->call->object);
  80. }
  81. execute_data->call--;
  82. }
  83. }
  84. /* }}} */
  85. ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC) /* {{{ */
  86. {
  87. if (generator->value) {
  88. zval_ptr_dtor(&generator->value);
  89. generator->value = NULL;
  90. }
  91. if (generator->key) {
  92. zval_ptr_dtor(&generator->key);
  93. generator->key = NULL;
  94. }
  95. if (generator->execute_data) {
  96. zend_execute_data *execute_data = generator->execute_data;
  97. zend_op_array *op_array = execute_data->op_array;
  98. if (!execute_data->symbol_table) {
  99. zend_free_compiled_variables(execute_data TSRMLS_CC);
  100. } else {
  101. zend_clean_and_cache_symbol_table(execute_data->symbol_table TSRMLS_CC);
  102. }
  103. if (execute_data->current_this) {
  104. zval_ptr_dtor(&execute_data->current_this);
  105. }
  106. /* A fatal error / die occurred during the generator execution. Trying to clean
  107. * up the stack may not be safe in this case. */
  108. if (CG(unclean_shutdown)) {
  109. generator->execute_data = NULL;
  110. return;
  111. }
  112. /* We have added an additional stack frame in prev_execute_data, so we
  113. * have to free it. It also contains the arguments passed to the
  114. * generator (for func_get_args) so those have to be freed too. */
  115. {
  116. zend_execute_data *prev_execute_data = execute_data->prev_execute_data;
  117. void **arguments = prev_execute_data->function_state.arguments;
  118. if (arguments) {
  119. int arguments_count = (int) (zend_uintptr_t) *arguments;
  120. zval **arguments_start = (zval **) (arguments - arguments_count);
  121. int i;
  122. for (i = 0; i < arguments_count; ++i) {
  123. zval_ptr_dtor(arguments_start + i);
  124. }
  125. }
  126. }
  127. /* Some cleanups are only necessary if the generator was closued
  128. * before it could finish execution (reach a return statement). */
  129. if (!finished_execution) {
  130. zend_generator_cleanup_unfinished_execution(generator TSRMLS_CC);
  131. }
  132. /* Free a clone of closure */
  133. if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
  134. destroy_op_array(op_array TSRMLS_CC);
  135. efree(op_array);
  136. }
  137. efree(generator->stack);
  138. generator->execute_data = NULL;
  139. }
  140. }
  141. /* }}} */
  142. static void zend_generator_dtor_storage(zend_generator *generator, zend_object_handle handle TSRMLS_DC) /* {{{ */
  143. {
  144. zend_execute_data *ex = generator->execute_data;
  145. zend_uint op_num, finally_op_num;
  146. int i;
  147. if (!ex || !ex->op_array->has_finally_block) {
  148. return;
  149. }
  150. /* -1 required because we want the last run opcode, not the
  151. * next to-be-run one. */
  152. op_num = ex->opline - ex->op_array->opcodes - 1;
  153. /* Find next finally block */
  154. finally_op_num = 0;
  155. for (i = 0; i < ex->op_array->last_try_catch; i++) {
  156. zend_try_catch_element *try_catch = &ex->op_array->try_catch_array[i];
  157. if (op_num < try_catch->try_op) {
  158. break;
  159. }
  160. if (op_num < try_catch->finally_op) {
  161. finally_op_num = try_catch->finally_op;
  162. }
  163. }
  164. /* If a finally block was found we jump directly to it and
  165. * resume the generator. */
  166. if (finally_op_num) {
  167. ex->opline = &ex->op_array->opcodes[finally_op_num];
  168. ex->fast_ret = NULL;
  169. ex->delayed_exception = EG(exception);
  170. EG(exception) = NULL;
  171. generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
  172. zend_generator_resume(generator TSRMLS_CC);
  173. }
  174. }
  175. /* }}} */
  176. static void zend_generator_free_storage(zend_generator *generator TSRMLS_DC) /* {{{ */
  177. {
  178. zend_generator_close(generator, 0 TSRMLS_CC);
  179. zend_object_std_dtor(&generator->std TSRMLS_CC);
  180. efree(generator);
  181. }
  182. /* }}} */
  183. static zend_object_value zend_generator_create(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
  184. {
  185. zend_generator *generator;
  186. zend_object_value object;
  187. generator = emalloc(sizeof(zend_generator));
  188. memset(generator, 0, sizeof(zend_generator));
  189. /* The key will be incremented on first use, so it'll start at 0 */
  190. generator->largest_used_integer_key = -1;
  191. zend_object_std_init(&generator->std, class_type TSRMLS_CC);
  192. object.handle = zend_objects_store_put(generator,
  193. (zend_objects_store_dtor_t) zend_generator_dtor_storage,
  194. (zend_objects_free_object_storage_t) zend_generator_free_storage,
  195. NULL TSRMLS_CC
  196. );
  197. object.handlers = &zend_generator_handlers;
  198. return object;
  199. }
  200. /* }}} */
  201. static void copy_closure_static_var(zval **var TSRMLS_DC, int num_args, va_list args, zend_hash_key *key) /* {{{ */
  202. {
  203. HashTable *target = va_arg(args, HashTable *);
  204. SEPARATE_ZVAL_TO_MAKE_IS_REF(var);
  205. Z_ADDREF_PP(var);
  206. zend_hash_quick_update(target, key->arKey, key->nKeyLength, key->h, var, sizeof(zval *), NULL);
  207. }
  208. /* }}} */
  209. /* Requires globals EG(scope), EG(current_scope), EG(This),
  210. * EG(active_symbol_table) and EG(current_execute_data). */
  211. ZEND_API zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  212. {
  213. zval *return_value;
  214. zend_generator *generator;
  215. zend_execute_data *current_execute_data;
  216. zend_op **opline_ptr;
  217. HashTable *current_symbol_table;
  218. zend_execute_data *execute_data;
  219. zend_vm_stack current_stack = EG(argument_stack);
  220. /* Create a clone of closure, because it may be destroyed */
  221. if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
  222. zend_op_array *op_array_copy = (zend_op_array*)emalloc(sizeof(zend_op_array));
  223. *op_array_copy = *op_array;
  224. (*op_array->refcount)++;
  225. op_array->run_time_cache = NULL;
  226. if (op_array->static_variables) {
  227. ALLOC_HASHTABLE(op_array_copy->static_variables);
  228. zend_hash_init(
  229. op_array_copy->static_variables,
  230. zend_hash_num_elements(op_array->static_variables),
  231. NULL, ZVAL_PTR_DTOR, 0
  232. );
  233. zend_hash_apply_with_arguments(
  234. op_array->static_variables TSRMLS_CC,
  235. (apply_func_args_t) copy_closure_static_var,
  236. 1, op_array_copy->static_variables
  237. );
  238. }
  239. op_array = op_array_copy;
  240. }
  241. /* Create new execution context. We have to back up and restore
  242. * EG(current_execute_data), EG(opline_ptr) and EG(active_symbol_table)
  243. * here because the function modifies or uses them */
  244. current_execute_data = EG(current_execute_data);
  245. opline_ptr = EG(opline_ptr);
  246. current_symbol_table = EG(active_symbol_table);
  247. EG(active_symbol_table) = NULL;
  248. execute_data = zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC);
  249. EG(active_symbol_table) = current_symbol_table;
  250. EG(current_execute_data) = current_execute_data;
  251. EG(opline_ptr) = opline_ptr;
  252. ALLOC_INIT_ZVAL(return_value);
  253. object_init_ex(return_value, zend_ce_generator);
  254. if (EG(This)) {
  255. Z_ADDREF_P(EG(This));
  256. }
  257. /* Back up executor globals. */
  258. execute_data->current_scope = EG(scope);
  259. execute_data->current_called_scope = EG(called_scope);
  260. execute_data->symbol_table = EG(active_symbol_table);
  261. execute_data->current_this = EG(This);
  262. /* Save execution context in generator object. */
  263. generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC);
  264. generator->execute_data = execute_data;
  265. generator->stack = EG(argument_stack);
  266. EG(argument_stack) = current_stack;
  267. return return_value;
  268. }
  269. /* }}} */
  270. static zend_function *zend_generator_get_constructor(zval *object TSRMLS_DC) /* {{{ */
  271. {
  272. zend_error(E_RECOVERABLE_ERROR, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated");
  273. return NULL;
  274. }
  275. /* }}} */
  276. ZEND_API void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */
  277. {
  278. /* The generator is already closed, thus can't resume */
  279. if (!generator->execute_data) {
  280. return;
  281. }
  282. if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
  283. zend_error(E_ERROR, "Cannot resume an already running generator");
  284. }
  285. /* Drop the AT_FIRST_YIELD flag */
  286. generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
  287. {
  288. /* Backup executor globals */
  289. zval **original_return_value_ptr_ptr = EG(return_value_ptr_ptr);
  290. zend_execute_data *original_execute_data = EG(current_execute_data);
  291. zend_op **original_opline_ptr = EG(opline_ptr);
  292. zend_op_array *original_active_op_array = EG(active_op_array);
  293. HashTable *original_active_symbol_table = EG(active_symbol_table);
  294. zval *original_This = EG(This);
  295. zend_class_entry *original_scope = EG(scope);
  296. zend_class_entry *original_called_scope = EG(called_scope);
  297. zend_vm_stack original_stack = EG(argument_stack);
  298. /* We (mis)use the return_value_ptr_ptr to provide the generator object
  299. * to the executor, so YIELD will be able to set the yielded value */
  300. EG(return_value_ptr_ptr) = (zval **) generator;
  301. /* Set executor globals */
  302. EG(current_execute_data) = generator->execute_data;
  303. EG(opline_ptr) = &generator->execute_data->opline;
  304. EG(active_op_array) = generator->execute_data->op_array;
  305. EG(active_symbol_table) = generator->execute_data->symbol_table;
  306. EG(This) = generator->execute_data->current_this;
  307. EG(scope) = generator->execute_data->current_scope;
  308. EG(called_scope) = generator->execute_data->current_called_scope;
  309. EG(argument_stack) = generator->stack;
  310. /* We want the backtrace to look as if the generator function was
  311. * called from whatever method we are current running (e.g. next()).
  312. * The first prev_execute_data contains an additional stack frame,
  313. * which makes the generator function show up in the backtrace and
  314. * makes the arguments available to func_get_args(). So we have to
  315. * set the prev_execute_data of that prev_execute_data :) */
  316. generator->execute_data->prev_execute_data->prev_execute_data = original_execute_data;
  317. /* Resume execution */
  318. generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
  319. zend_execute_ex(generator->execute_data TSRMLS_CC);
  320. generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
  321. /* Restore executor globals */
  322. EG(return_value_ptr_ptr) = original_return_value_ptr_ptr;
  323. EG(current_execute_data) = original_execute_data;
  324. EG(opline_ptr) = original_opline_ptr;
  325. EG(active_op_array) = original_active_op_array;
  326. EG(active_symbol_table) = original_active_symbol_table;
  327. EG(This) = original_This;
  328. EG(scope) = original_scope;
  329. EG(called_scope) = original_called_scope;
  330. EG(argument_stack) = original_stack;
  331. /* If an exception was thrown in the generator we have to internally
  332. * rethrow it in the parent scope. */
  333. if (UNEXPECTED(EG(exception) != NULL)) {
  334. zend_throw_exception_internal(NULL TSRMLS_CC);
  335. }
  336. }
  337. }
  338. /* }}} */
  339. static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_DC) /* {{{ */
  340. {
  341. if (generator->execute_data && !generator->value) {
  342. zend_generator_resume(generator TSRMLS_CC);
  343. generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD;
  344. }
  345. }
  346. /* }}} */
  347. static void zend_generator_rewind(zend_generator *generator TSRMLS_DC) /* {{{ */
  348. {
  349. zend_generator_ensure_initialized(generator TSRMLS_CC);
  350. if (!(generator->flags & ZEND_GENERATOR_AT_FIRST_YIELD)) {
  351. zend_throw_exception(NULL, "Cannot rewind a generator that was already run", 0 TSRMLS_CC);
  352. }
  353. }
  354. /* }}} */
  355. /* {{{ proto void Generator::rewind()
  356. * Rewind the generator */
  357. ZEND_METHOD(Generator, rewind)
  358. {
  359. zend_generator *generator;
  360. if (zend_parse_parameters_none() == FAILURE) {
  361. return;
  362. }
  363. generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
  364. zend_generator_rewind(generator TSRMLS_CC);
  365. }
  366. /* }}} */
  367. /* {{{ proto bool Generator::valid()
  368. * Check whether the generator is valid */
  369. ZEND_METHOD(Generator, valid)
  370. {
  371. zend_generator *generator;
  372. if (zend_parse_parameters_none() == FAILURE) {
  373. return;
  374. }
  375. generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
  376. zend_generator_ensure_initialized(generator TSRMLS_CC);
  377. RETURN_BOOL(generator->value != NULL);
  378. }
  379. /* }}} */
  380. /* {{{ proto mixed Generator::current()
  381. * Get the current value */
  382. ZEND_METHOD(Generator, current)
  383. {
  384. zend_generator *generator;
  385. if (zend_parse_parameters_none() == FAILURE) {
  386. return;
  387. }
  388. generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
  389. zend_generator_ensure_initialized(generator TSRMLS_CC);
  390. if (generator->value) {
  391. RETURN_ZVAL_FAST(generator->value);
  392. }
  393. }
  394. /* }}} */
  395. /* {{{ proto mixed Generator::key()
  396. * Get the current key */
  397. ZEND_METHOD(Generator, key)
  398. {
  399. zend_generator *generator;
  400. if (zend_parse_parameters_none() == FAILURE) {
  401. return;
  402. }
  403. generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
  404. zend_generator_ensure_initialized(generator TSRMLS_CC);
  405. if (generator->key) {
  406. RETURN_ZVAL_FAST(generator->key);
  407. }
  408. }
  409. /* }}} */
  410. /* {{{ proto void Generator::next()
  411. * Advances the generator */
  412. ZEND_METHOD(Generator, next)
  413. {
  414. zend_generator *generator;
  415. if (zend_parse_parameters_none() == FAILURE) {
  416. return;
  417. }
  418. generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
  419. zend_generator_ensure_initialized(generator TSRMLS_CC);
  420. zend_generator_resume(generator TSRMLS_CC);
  421. }
  422. /* }}} */
  423. /* {{{ proto mixed Generator::send(mixed $value)
  424. * Sends a value to the generator */
  425. ZEND_METHOD(Generator, send)
  426. {
  427. zval *value;
  428. zend_generator *generator;
  429. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  430. return;
  431. }
  432. generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
  433. zend_generator_ensure_initialized(generator TSRMLS_CC);
  434. /* The generator is already closed, thus can't send anything */
  435. if (!generator->execute_data) {
  436. return;
  437. }
  438. /* Put sent value in the target VAR slot, if it is used */
  439. if (generator->send_target) {
  440. Z_DELREF_PP(generator->send_target);
  441. Z_ADDREF_P(value);
  442. *generator->send_target = value;
  443. }
  444. zend_generator_resume(generator TSRMLS_CC);
  445. if (generator->value) {
  446. RETURN_ZVAL_FAST(generator->value);
  447. }
  448. }
  449. /* }}} */
  450. /* {{{ proto mixed Generator::throw(Exception $exception)
  451. * Throws an exception into the generator */
  452. ZEND_METHOD(Generator, throw)
  453. {
  454. zval *exception, *exception_copy;
  455. zend_generator *generator;
  456. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &exception) == FAILURE) {
  457. return;
  458. }
  459. ALLOC_ZVAL(exception_copy);
  460. MAKE_COPY_ZVAL(&exception, exception_copy);
  461. generator = (zend_generator *) zend_object_store_get_object(getThis() TSRMLS_CC);
  462. zend_generator_ensure_initialized(generator TSRMLS_CC);
  463. if (generator->execute_data) {
  464. /* Throw the exception in the context of the generator */
  465. zend_execute_data *current_execute_data = EG(current_execute_data);
  466. EG(current_execute_data) = generator->execute_data;
  467. zend_throw_exception_object(exception_copy TSRMLS_CC);
  468. EG(current_execute_data) = current_execute_data;
  469. zend_generator_resume(generator TSRMLS_CC);
  470. if (generator->value) {
  471. RETURN_ZVAL_FAST(generator->value);
  472. }
  473. } else {
  474. /* If the generator is already closed throw the exception in the
  475. * current context */
  476. zend_throw_exception_object(exception_copy TSRMLS_CC);
  477. }
  478. }
  479. /* }}} */
  480. /* {{{ proto void Generator::__wakeup()
  481. * Throws an Exception as generators can't be serialized */
  482. ZEND_METHOD(Generator, __wakeup)
  483. {
  484. /* Just specifying the zend_class_unserialize_deny handler is not enough,
  485. * because it is only invoked for C unserialization. For O the error has
  486. * to be thrown in __wakeup. */
  487. if (zend_parse_parameters_none() == FAILURE) {
  488. return;
  489. }
  490. zend_throw_exception(NULL, "Unserialization of 'Generator' is not allowed", 0 TSRMLS_CC);
  491. }
  492. /* }}} */
  493. /* get_iterator implementation */
  494. static void zend_generator_iterator_dtor(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
  495. {
  496. zend_generator_iterator *iter = (zend_generator_iterator *) iterator;
  497. zend_objects_store_del_ref_by_handle(iter->handle TSRMLS_CC);
  498. }
  499. /* }}} */
  500. static int zend_generator_iterator_valid(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
  501. {
  502. zend_generator *generator = (zend_generator *) iterator->data;
  503. zend_generator_ensure_initialized(generator TSRMLS_CC);
  504. return generator->value != NULL ? SUCCESS : FAILURE;
  505. }
  506. /* }}} */
  507. static void zend_generator_iterator_get_data(zend_object_iterator *iterator, zval ***data TSRMLS_DC) /* {{{ */
  508. {
  509. zend_generator *generator = (zend_generator *) iterator->data;
  510. zend_generator_ensure_initialized(generator TSRMLS_CC);
  511. if (generator->value) {
  512. *data = &generator->value;
  513. } else {
  514. *data = NULL;
  515. }
  516. }
  517. /* }}} */
  518. static void zend_generator_iterator_get_key(zend_object_iterator *iterator, zval *key TSRMLS_DC) /* {{{ */
  519. {
  520. zend_generator *generator = (zend_generator *) iterator->data;
  521. zend_generator_ensure_initialized(generator TSRMLS_CC);
  522. if (generator->key) {
  523. ZVAL_ZVAL(key, generator->key, 1, 0);
  524. } else {
  525. ZVAL_NULL(key);
  526. }
  527. }
  528. /* }}} */
  529. static void zend_generator_iterator_move_forward(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
  530. {
  531. zend_generator *generator = (zend_generator *) iterator->data;
  532. zend_generator_ensure_initialized(generator TSRMLS_CC);
  533. zend_generator_resume(generator TSRMLS_CC);
  534. }
  535. /* }}} */
  536. static void zend_generator_iterator_rewind(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
  537. {
  538. zend_generator *generator = (zend_generator *) iterator->data;
  539. zend_generator_rewind(generator TSRMLS_CC);
  540. }
  541. /* }}} */
  542. static zend_object_iterator_funcs zend_generator_iterator_functions = {
  543. zend_generator_iterator_dtor,
  544. zend_generator_iterator_valid,
  545. zend_generator_iterator_get_data,
  546. zend_generator_iterator_get_key,
  547. zend_generator_iterator_move_forward,
  548. zend_generator_iterator_rewind
  549. };
  550. zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */
  551. {
  552. zend_generator_iterator *iterator;
  553. zend_generator *generator;
  554. generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
  555. if (!generator->execute_data) {
  556. zend_throw_exception(NULL, "Cannot traverse an already closed generator", 0 TSRMLS_CC);
  557. return NULL;
  558. }
  559. if (by_ref && !(generator->execute_data->op_array->fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
  560. zend_throw_exception(NULL, "You can only iterate a generator by-reference if it declared that it yields by-reference", 0 TSRMLS_CC);
  561. return NULL;
  562. }
  563. iterator = &generator->iterator;
  564. iterator->intern.funcs = &zend_generator_iterator_functions;
  565. iterator->intern.data = (void *) generator;
  566. /* We have to keep a reference to the generator object zval around,
  567. * otherwise the generator may be destroyed during iteration. */
  568. iterator->handle = Z_OBJ_HANDLE_P(object);
  569. zend_objects_store_add_ref_by_handle(iterator->handle TSRMLS_CC);
  570. return (zend_object_iterator *) iterator;
  571. }
  572. /* }}} */
  573. ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0)
  574. ZEND_END_ARG_INFO()
  575. ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1)
  576. ZEND_ARG_INFO(0, value)
  577. ZEND_END_ARG_INFO()
  578. ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_throw, 0, 0, 1)
  579. ZEND_ARG_INFO(0, exception)
  580. ZEND_END_ARG_INFO()
  581. static const zend_function_entry generator_functions[] = {
  582. ZEND_ME(Generator, rewind, arginfo_generator_void, ZEND_ACC_PUBLIC)
  583. ZEND_ME(Generator, valid, arginfo_generator_void, ZEND_ACC_PUBLIC)
  584. ZEND_ME(Generator, current, arginfo_generator_void, ZEND_ACC_PUBLIC)
  585. ZEND_ME(Generator, key, arginfo_generator_void, ZEND_ACC_PUBLIC)
  586. ZEND_ME(Generator, next, arginfo_generator_void, ZEND_ACC_PUBLIC)
  587. ZEND_ME(Generator, send, arginfo_generator_send, ZEND_ACC_PUBLIC)
  588. ZEND_ME(Generator, throw, arginfo_generator_throw, ZEND_ACC_PUBLIC)
  589. ZEND_ME(Generator, __wakeup, arginfo_generator_void, ZEND_ACC_PUBLIC)
  590. ZEND_FE_END
  591. };
  592. void zend_register_generator_ce(TSRMLS_D) /* {{{ */
  593. {
  594. zend_class_entry ce;
  595. INIT_CLASS_ENTRY(ce, "Generator", generator_functions);
  596. zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC);
  597. zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS;
  598. zend_ce_generator->create_object = zend_generator_create;
  599. zend_ce_generator->serialize = zend_class_serialize_deny;
  600. zend_ce_generator->unserialize = zend_class_unserialize_deny;
  601. /* get_iterator has to be assigned *after* implementing the inferface */
  602. zend_class_implements(zend_ce_generator TSRMLS_CC, 1, zend_ce_iterator);
  603. zend_ce_generator->get_iterator = zend_generator_get_iterator;
  604. zend_ce_generator->iterator_funcs.funcs = &zend_generator_iterator_functions;
  605. memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
  606. zend_generator_handlers.get_constructor = zend_generator_get_constructor;
  607. zend_generator_handlers.clone_obj = NULL;
  608. }
  609. /* }}} */
  610. /*
  611. * Local variables:
  612. * tab-width: 4
  613. * c-basic-offset: 4
  614. * indent-tabs-mode: t
  615. * End:
  616. */