PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - Zend - zend_generators.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 462 514 89.9 %
Date: 2015-05-30 Functions: 35 37 94.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :    +----------------------------------------------------------------------+
       3             :    | Zend Engine                                                          |
       4             :    +----------------------------------------------------------------------+
       5             :    | Copyright (c) 1998-2015 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             :    |          Bob Weinand <bobwei9@hotmail.com>                           |
      17             :    +----------------------------------------------------------------------+
      18             : */
      19             : 
      20             : /* $Id$ */
      21             : 
      22             : #include "zend.h"
      23             : #include "zend_API.h"
      24             : #include "zend_interfaces.h"
      25             : #include "zend_exceptions.h"
      26             : #include "zend_generators.h"
      27             : 
      28             : ZEND_API zend_class_entry *zend_ce_generator;
      29             : ZEND_API zend_class_entry *zend_ce_ClosedGeneratorException;
      30             : static zend_object_handlers zend_generator_handlers;
      31             : 
      32             : static zend_object *zend_generator_create(zend_class_entry *class_type);
      33             : 
      34         248 : static void zend_generator_cleanup_unfinished_execution(zend_generator *generator) /* {{{ */
      35             : {
      36         248 :         zend_execute_data *execute_data = generator->execute_data;
      37         248 :         zend_op_array *op_array = &execute_data->func->op_array;
      38             : 
      39         248 :         if (generator->send_target) {
      40          83 :                 if (Z_REFCOUNTED_P(generator->send_target)) Z_DELREF_P(generator->send_target);
      41          83 :                 generator->send_target = NULL;
      42             :         }
      43             : 
      44             :         /* Manually free loop variables, as execution couldn't reach their
      45             :          * SWITCH_FREE / FREE opcodes. */
      46             :         {
      47             :                 /* -1 required because we want the last run opcode, not the
      48             :                  * next to-be-run one. */
      49         248 :                 uint32_t op_num = execute_data->opline - op_array->opcodes - 1;
      50             : 
      51             :                 int i;
      52         333 :                 for (i = 0; i < op_array->last_brk_cont; ++i) {
      53          85 :                         zend_brk_cont_element *brk_cont = op_array->brk_cont_array + i;
      54             : 
      55          85 :                         if (brk_cont->start < 0) {
      56          83 :                                 continue;
      57           2 :                         } else if ((uint32_t)brk_cont->start > op_num) {
      58           0 :                                 break;
      59           2 :                         } else if (brk_cont->brk >= 0 && (uint32_t)brk_cont->brk > op_num) {
      60           1 :                                 zend_op *brk_opline = op_array->opcodes + brk_cont->brk;
      61             : 
      62           1 :                                 if (brk_opline->opcode == ZEND_FREE) {
      63           0 :                                         zval *var = EX_VAR(brk_opline->op1.var);
      64             :                                         zval_ptr_dtor_nogc(var);
      65           1 :                                 } else if (brk_opline->opcode == ZEND_FE_FREE) {
      66           1 :                                         zval *var = EX_VAR(brk_opline->op1.var);
      67           1 :                                         if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) {
      68           0 :                                                 zend_hash_iterator_del(Z_FE_ITER_P(var));
      69             :                                         }
      70             :                                         zval_ptr_dtor_nogc(var);
      71             :                                 }
      72             :                         }
      73             :                 }
      74             :         }
      75             : 
      76             :         /* If yield was used as a function argument there may be active
      77             :          * method calls those objects need to be freed */
      78         499 :         while (execute_data->call) {
      79           3 :                 if (ZEND_CALL_INFO(execute_data->call) & ZEND_CALL_RELEASE_THIS) {
      80           1 :                         OBJ_RELEASE(Z_OBJ(execute_data->call->This));
      81             :                 }
      82           3 :                 execute_data->call = execute_data->call->prev_execute_data;
      83             :         }
      84         248 : }
      85             : /* }}} */
      86             : 
      87      100538 : ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution) /* {{{ */
      88             : {
      89      201076 :         if (Z_TYPE(generator->value) != IS_UNDEF) {
      90         267 :                 zval_ptr_dtor(&generator->value);
      91         267 :                 ZVAL_UNDEF(&generator->value);
      92             :         }
      93             : 
      94      201076 :         if (Z_TYPE(generator->key) != IS_UNDEF) {
      95         267 :                 zval_ptr_dtor(&generator->key);
      96         267 :                 ZVAL_UNDEF(&generator->key);
      97             :         }
      98             : 
      99      201076 :         if (Z_TYPE(generator->values) != IS_UNDEF) {
     100           0 :                 zval_ptr_dtor(&generator->values);
     101           0 :                 ZVAL_UNDEF(&generator->values);
     102             :         }
     103             : 
     104      100538 :         if (generator->execute_data) {
     105       50385 :                 zend_execute_data *execute_data = generator->execute_data;
     106             : 
     107       50385 :                 if (execute_data->symbol_table) {
     108           2 :                         zend_clean_and_cache_symbol_table(execute_data->symbol_table);
     109             :                 }
     110             :                 /* always free the CV's, in the symtable are only not-free'd IS_INDIRECT's */
     111       50385 :                 zend_free_compiled_variables(execute_data);
     112             : 
     113       50385 :                 if (Z_OBJ(execute_data->This)) {
     114           5 :                         OBJ_RELEASE(Z_OBJ(execute_data->This));
     115             :                 }
     116             : 
     117             :                 /* A fatal error / die occurred during the generator execution. Trying to clean
     118             :                  * up the stack may not be safe in this case. */
     119       50385 :                 if (CG(unclean_shutdown)) {
     120           7 :                         generator->execute_data = NULL;
     121           7 :                         return;
     122             :                 }
     123             : 
     124       50378 :                 zend_vm_stack_free_extra_args(generator->execute_data);
     125             : 
     126             :                 /* Some cleanups are only necessary if the generator was closued
     127             :                  * before it could finish execution (reach a return statement). */
     128       50378 :                 if (!finished_execution) {
     129         248 :                         zend_generator_cleanup_unfinished_execution(generator);
     130             :                 }
     131             : 
     132             :                 /* Free closure object */
     133       50378 :                 if (EX_CALL_INFO() & ZEND_CALL_CLOSURE) {
     134          12 :                         OBJ_RELEASE((zend_object *) EX(func)->common.prototype);
     135             :                 }
     136             : 
     137       50378 :                 efree(generator->stack);
     138       50378 :                 generator->execute_data = NULL;
     139             :         }
     140             : }
     141             : /* }}} */
     142             : 
     143       50375 : static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
     144             : {
     145       50375 :         zend_generator *generator = (zend_generator*) object;
     146       50375 :         zend_execute_data *ex = generator->execute_data;
     147             :         uint32_t op_num, finally_op_num, finally_op_end;
     148             :         int i;
     149             : 
     150       50375 :         if (!ex || !(ex->func->op_array.fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
     151       50372 :                 return;
     152             :         }
     153             : 
     154             :         /* -1 required because we want the last run opcode, not the
     155             :          * next to-be-run one. */
     156           3 :         op_num = ex->opline - ex->func->op_array.opcodes - 1;
     157             : 
     158             :         /* Find next finally block */
     159           3 :         finally_op_num = 0;
     160           3 :         finally_op_end = 0;
     161           7 :         for (i = 0; i < ex->func->op_array.last_try_catch; i++) {
     162           4 :                 zend_try_catch_element *try_catch = &ex->func->op_array.try_catch_array[i];
     163             : 
     164           4 :                 if (op_num < try_catch->try_op) {
     165           0 :                         break;
     166             :                 }
     167             : 
     168           4 :                 if (op_num < try_catch->finally_op) {
     169           4 :                         finally_op_num = try_catch->finally_op;
     170           4 :                         finally_op_end = try_catch->finally_end;
     171             :                 }
     172             :         }
     173             : 
     174             :         /* If a finally block was found we jump directly to it and
     175             :          * resume the generator. */
     176           3 :         if (finally_op_num) {
     177           3 :                 zval *fast_call = ZEND_CALL_VAR(ex, ex->func->op_array.opcodes[finally_op_end].op1.var);
     178             : 
     179           3 :                 Z_OBJ_P(fast_call) = NULL;
     180           3 :                 fast_call->u2.lineno = (uint32_t)-1;
     181           3 :                 ex->opline = &ex->func->op_array.opcodes[finally_op_num];
     182           3 :                 generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
     183           3 :                 zend_generator_resume(generator);
     184             :         }
     185             : }
     186             : /* }}} */
     187             : 
     188             : static zend_generator *zend_generator_get_child(zend_generator_node *node, zend_generator *leaf);
     189             : 
     190       50387 : static void zend_generator_free_storage(zend_object *object) /* {{{ */
     191             : {
     192       50387 :         zend_generator *generator = (zend_generator*) object;
     193             : 
     194       50387 :         zend_generator_close(generator, 0);
     195             : 
     196      100774 :         if (!Z_ISUNDEF(generator->retval)) {
     197       50101 :                 zval_ptr_dtor(&generator->retval);
     198             :         }
     199             : 
     200       50387 :         if (generator->node.children > 4) {
     201          29 :                 zend_hash_destroy(&generator->node.child.ht);
     202             :         }
     203             : 
     204       50387 :         zend_object_std_dtor(&generator->std);
     205             : 
     206       50387 :         if (generator->iterator) {
     207           0 :                 zend_iterator_dtor(generator->iterator);
     208             :         }
     209             : 
     210       50387 :         if (generator->node.children == 0) {
     211         305 :                 zend_generator *root = generator->node.ptr.root, *next;
     212         885 :                 while (root != generator) {
     213         275 :                         next = zend_generator_get_child(&root->node, generator);
     214         275 :                         OBJ_RELEASE(&root->std);
     215         275 :                         root = next;
     216             :                 }
     217             :         }
     218       50387 : }
     219             : /* }}} */
     220             : 
     221       50387 : static zend_object *zend_generator_create(zend_class_entry *class_type) /* {{{ */
     222             : {
     223             :         zend_generator *generator;
     224             : 
     225       50387 :         generator = emalloc(sizeof(zend_generator));
     226       50387 :         memset(generator, 0, sizeof(zend_generator));
     227             : 
     228             :         /* The key will be incremented on first use, so it'll start at 0 */
     229       50387 :         generator->largest_used_integer_key = -1;
     230             : 
     231       50387 :         ZVAL_UNDEF(&generator->retval);
     232       50387 :         ZVAL_UNDEF(&generator->values);
     233             : 
     234             :         /* By default we have a tree of only one node */
     235       50387 :         generator->node.parent = NULL;
     236       50387 :         generator->node.children = 0;
     237       50387 :         generator->node.ptr.root = generator;
     238             : 
     239       50387 :         zend_object_std_init(&generator->std, class_type);
     240       50387 :         generator->std.handlers = &zend_generator_handlers;
     241             : 
     242       50387 :         return (zend_object*)generator;
     243             : }
     244             : /* }}} */
     245             : 
     246             : /* Requires globals EG(scope), EG(This) and EG(current_execute_data). */
     247       50385 : ZEND_API void zend_generator_create_zval(zend_execute_data *call, zend_op_array *op_array, zval *return_value) /* {{{ */
     248             : {
     249             :         zend_generator *generator;
     250             :         zend_execute_data *current_execute_data;
     251             :         zend_execute_data *execute_data;
     252       50385 :         zend_vm_stack current_stack = EG(vm_stack);
     253             : 
     254       50385 :         current_stack->top = EG(vm_stack_top);
     255             : 
     256             :         /* Create new execution context. We have to back up and restore
     257             :          * EG(current_execute_data) here. */
     258       50385 :         current_execute_data = EG(current_execute_data);
     259       50385 :         execute_data = zend_create_generator_execute_data(call, op_array, return_value);
     260       50385 :         EG(current_execute_data) = current_execute_data;
     261             : 
     262       50385 :         object_init_ex(return_value, zend_ce_generator);
     263             : 
     264       50385 :         if (Z_OBJ(call->This)) {
     265           5 :                 Z_ADDREF(call->This);
     266             :         }
     267             : 
     268             :         /* Save execution context in generator object. */
     269       50385 :         generator = (zend_generator *) Z_OBJ_P(return_value);
     270       50385 :         generator->execute_data = execute_data;
     271       50385 :         generator->stack = EG(vm_stack);
     272       50385 :         generator->stack->top = EG(vm_stack_top);
     273       50385 :         EG(vm_stack_top) = current_stack->top;
     274       50385 :         EG(vm_stack_end) = current_stack->end;
     275       50385 :         EG(vm_stack) = current_stack;
     276             : 
     277             :         /* EX(return_value) keeps pointer to zend_object (not a real zval) */
     278       50385 :         execute_data->return_value = (zval*)generator;
     279             : 
     280       50385 :         memset(&generator->execute_fake, 0, sizeof(zend_execute_data));
     281       50385 :         Z_OBJ(generator->execute_fake.This) = (zend_object *) generator;
     282       50385 : }
     283             : /* }}} */
     284             : 
     285           2 : static zend_function *zend_generator_get_constructor(zend_object *object) /* {{{ */
     286             : {
     287           2 :         zend_error(E_EXCEPTION | E_ERROR, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated");
     288             : 
     289           2 :         return NULL;
     290             : }
     291             : /* }}} */
     292             : 
     293        3529 : ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_data *ptr)
     294             : {
     295        3529 :         if (!ptr->func && ptr->prev_execute_data && Z_OBJ(ptr->This)) {
     296          13 :                 if (Z_OBJCE(ptr->This) == zend_ce_generator) {
     297           4 :                         zend_generator *generator = (zend_generator *) Z_OBJ(ptr->This);
     298           4 :                         zend_generator *root = (generator->node.children < 1 ? generator : generator->node.ptr.leaf)->node.ptr.root;
     299           4 :                         zend_execute_data *prev = ptr->prev_execute_data;
     300           4 :                         if (generator->node.parent != root) {
     301             :                                 do {
     302           2 :                                         generator->execute_data->prev_execute_data = prev;
     303           2 :                                         prev = generator->execute_data;
     304           2 :                                         generator = generator->node.parent;
     305           2 :                                 } while (generator->node.parent != root);
     306             :                         }
     307           4 :                         generator->execute_data->prev_execute_data = prev;
     308           4 :                         ptr = generator->execute_data;
     309             :                 }
     310             :         }
     311        3529 :         return ptr;
     312             : }
     313             : 
     314          16 : static void zend_generator_throw_exception(zend_generator *generator, zval *exception)
     315             : {
     316             :         /* Throw the exception in the context of the generator. Decrementing the opline
     317             :          * to pretend the exception happened during the YIELD opcode. */
     318          16 :         zend_execute_data *original_execute_data = EG(current_execute_data);
     319          16 :         EG(current_execute_data) = generator->execute_data;
     320          16 :         generator->execute_data->opline--;
     321          16 :         if (exception) {
     322          10 :                 zend_throw_exception_object(exception);
     323             :         } else {
     324           6 :                 zend_throw_exception_internal(NULL);
     325             :         }
     326          16 :         generator->execute_data->opline++;
     327          16 :         EG(current_execute_data) = original_execute_data;
     328          16 : }
     329             : 
     330       50303 : static zend_generator *zend_generator_get_child(zend_generator_node *node, zend_generator *leaf)
     331             : {
     332       50303 :         switch (node->children) {
     333             :                 case 0:
     334           0 :                         return NULL;
     335             :                 case 1:
     336           0 :                         return node->child.array[0].child;
     337             : 
     338             : #define ZEND_GEN_GET_CHILD(x) \
     339             :                         if (node->child.array[x].leaf == leaf) { \
     340             :                                 return node->child.array[x].child; \
     341             :                         }
     342             :                 case 4:
     343           0 :                         ZEND_GEN_GET_CHILD(3)
     344             :                 case 3:
     345          76 :                         ZEND_GEN_GET_CHILD(2)
     346             :                 case 2:
     347       50053 :                         ZEND_GEN_GET_CHILD(1)
     348           0 :                         ZEND_GEN_GET_CHILD(0)
     349             :                         ZEND_ASSERT(0); // we never should have no matching child
     350             :         }
     351             : 
     352         424 :         return zend_hash_index_find_ptr(&node->child.ht, (zend_ulong) leaf);
     353             : }
     354             : 
     355         124 : static zend_generator_node *zend_generator_search_multi_children_node(zend_generator_node *node)
     356             : {
     357         248 :         while (node->children == 1) {
     358           0 :                 node = &node->child.array[0].child->node;
     359             :         }
     360         124 :         return node->children > 1 ? node : NULL;
     361             : }
     362             : 
     363      100539 : static void zend_generator_add_single_child(zend_generator_node *node, zend_generator *child, zend_generator *leaf)
     364             : {
     365      100539 :         if (node->children < 4) {
     366      100260 :                 node->child.array[node->children].leaf = leaf;
     367      100260 :                 node->child.array[node->children].child = child;
     368         279 :         } else if (node->children > 4) {
     369         250 :                 zend_hash_index_add_ptr(&node->child.ht, (zend_ulong) leaf, child);
     370             :         } else {
     371             :                 struct {
     372             :                         zend_generator *leaf;
     373             :                         zend_generator *child;
     374             :                 } array[4];
     375             :                 int i;
     376             : 
     377          29 :                 memcpy(&array, &node->child.array, sizeof(array));
     378          29 :                 zend_hash_init(&node->child.ht, 5, sigh, NULL, 0);
     379         145 :                 for (i = 0; i < 4; i++) {
     380         116 :                         zend_hash_index_add_ptr(&node->child.ht, (zend_ulong) array[i].leaf, array[i].child);
     381             :                 }
     382          29 :                 zend_hash_index_add_ptr(&node->child.ht, (zend_ulong) leaf, child);
     383             :         }
     384             : 
     385      100539 :         node->children++;
     386      100539 : }
     387             : 
     388           0 : static void zend_generator_merge_child_nodes(zend_generator_node *dest, zend_generator_node *src, zend_generator *child)
     389             : {
     390           0 :         if (src->children <= 4) {
     391           0 :                 int i = src->children;
     392           0 :                 while (i--) {
     393           0 :                         zend_generator_add_single_child(dest, child, src->child.array[i].leaf);
     394             :                 }
     395             :         } else {
     396             :                 zend_ulong leaf;
     397           0 :                 ZEND_HASH_FOREACH_NUM_KEY(&src->child.ht, leaf) {
     398           0 :                         zend_generator_add_single_child(dest, child, (zend_generator *) leaf);
     399             :                 } ZEND_HASH_FOREACH_END();
     400             :         }
     401           0 : }
     402             : 
     403             : /* Make attention so that the root of each subtree of the Generators tree is referenced once per leaf */
     404       50148 : static void zend_generator_add_child(zend_generator *generator, zend_generator *child)
     405             : {
     406       50148 :         zend_generator *leaf = child->node.children ? child->node.ptr.leaf : child;
     407             :         zend_generator_node *multi_children_node;
     408       50148 :         zend_bool was_leaf = generator->node.children == 0;
     409             : 
     410       50148 :         if (was_leaf) {
     411       50082 :                 zend_generator *next = generator->node.parent;
     412       50082 :                 leaf->node.ptr.root = generator->node.ptr.root;
     413       50082 :                 ++GC_REFCOUNT(&generator->std); /* we need to increment the generator refcount here as it became integrated into the tree (no leaf), but we must not increment the refcount of the *whole* path in tree */
     414       50082 :                 generator->node.ptr.leaf = leaf;
     415             : 
     416      100318 :                 while (next) {
     417         154 :                         if (next->node.children > 1) {
     418         154 :                                 if (next->node.children > 4) {
     419         250 :                                         zend_generator *child = zend_hash_index_find_ptr(&next->node.child.ht, (zend_ulong) generator);
     420         125 :                                         zend_hash_index_del(&next->node.child.ht, (zend_ulong) generator);
     421         125 :                                         zend_hash_index_add_ptr(&next->node.child.ht, (zend_ulong) leaf, child);
     422             :                                 } else {
     423          29 :                                         switch (next->node.children) {
     424             : #define ZEND_GEN_UPDATE_CHILD(x) \
     425             :                                                         if (next->node.child.array[x].leaf == generator) { \
     426             :                                                                 next->node.child.array[x].leaf = leaf; \
     427             :                                                                 break; \
     428             :                                                         }
     429             :                                                 case 4:
     430           0 :                                                         ZEND_GEN_UPDATE_CHILD(3)
     431             :                                                 case 3:
     432          29 :                                                         ZEND_GEN_UPDATE_CHILD(2)
     433             :                                                 case 2:
     434          29 :                                                         ZEND_GEN_UPDATE_CHILD(1)
     435           0 :                                                         ZEND_GEN_UPDATE_CHILD(0)
     436             :                                                         ZEND_ASSERT(0); // we never should have no matching child
     437             :                                         }
     438             :                                 }
     439             :                         }
     440             : 
     441         154 :                         next->node.ptr.leaf = leaf;
     442         154 :                         next = next->node.parent;
     443             :                 }
     444             : 
     445       50082 :                 zend_generator_add_single_child(&generator->node, child, leaf);
     446          66 :         } else if (generator->node.children == 1) {
     447           0 :                 multi_children_node = zend_generator_search_multi_children_node(&generator->node);
     448           0 :                 if (multi_children_node) {
     449           0 :                         generator->node.children = 0;
     450           0 :                         zend_generator_merge_child_nodes(&generator->node, multi_children_node, generator->node.child.array[0].child);
     451             :                 }
     452             :         }
     453             : 
     454       50148 :         if (!was_leaf) {
     455          66 :                 multi_children_node = zend_generator_search_multi_children_node(&child->node);
     456             :         } else {
     457       50082 :                 multi_children_node = (zend_generator_node *) 0x1;
     458             :         }
     459             : 
     460             :         {
     461       50148 :                 zend_generator *parent = generator->node.parent, *cur = generator;
     462             : 
     463       50148 :                 if (multi_children_node > (zend_generator_node *) 0x1) {
     464           0 :                         zend_generator_merge_child_nodes(&generator->node, multi_children_node, child);
     465             :                 } else {
     466       50148 :                         zend_generator_add_single_child(&generator->node, child, leaf);
     467             :                 }
     468      100605 :                 while (parent) {
     469         309 :                         if (parent->node.children > 1) {
     470         309 :                                 if (multi_children_node == (zend_generator_node *) 0x1) {
     471          58 :                                         multi_children_node = zend_generator_search_multi_children_node(&child->node);
     472             :                                 }
     473         309 :                                 if (multi_children_node) {
     474           0 :                                         zend_generator_merge_child_nodes(&parent->node, multi_children_node, cur);
     475             :                                 } else {
     476         309 :                                         zend_generator_add_single_child(&parent->node, cur, leaf);
     477             :                                 }
     478             :                         }
     479         309 :                         cur = parent;
     480         309 :                         parent = parent->node.parent;
     481             :                 }
     482             :         }
     483       50148 : }
     484             : 
     485       50148 : void zend_generator_yield_from(zend_generator *this, zend_generator *from)
     486             : {
     487       50148 :         zend_generator_add_child(from, this);
     488             : 
     489       50148 :         this->node.parent = from;
     490       50148 :         zend_generator_get_current(this);
     491       50148 :         --GC_REFCOUNT(from);
     492       50148 : }
     493             : 
     494     7255508 : ZEND_API zend_generator *zend_generator_get_current(zend_generator *generator)
     495             : {
     496             :         zend_generator *leaf;
     497             :         zend_generator *root, *old_root;
     498             : 
     499     7255508 :         if (generator->node.parent == NULL) {
     500             :                 /* we're not in yield from mode */
     501     7104286 :                 return generator;
     502             :         }
     503             : 
     504      151222 :         leaf = generator->node.children ? generator->node.ptr.leaf : generator;
     505      151222 :         root = leaf->node.ptr.root;
     506             : 
     507      151222 :         if (root->execute_data && root->node.parent == NULL) {
     508             :                 /* generator still running */
     509      101128 :                 return root;
     510             :         }
     511             : 
     512             :         /* generator at the root had stopped */
     513       50094 :         if (root != generator) {
     514       50028 :                 old_root = root;
     515       50028 :                 root = zend_generator_get_child(&root->node, leaf);
     516             :         } else {
     517          66 :                 old_root = NULL;
     518             :         }
     519             : 
     520      100188 :         while (!root->execute_data && root != generator) {
     521           0 :                 OBJ_RELEASE(&old_root->std);
     522           0 :                 old_root = root;
     523             : 
     524           0 :                 root = zend_generator_get_child(&root->node, leaf);
     525             :         }
     526             : 
     527       50094 :         if (root->node.parent) {
     528       50093 :                 if (root->node.parent->execute_data == NULL) {
     529       50027 :                         if (EXPECTED(EG(exception) == NULL)) {
     530       50021 :                                 zend_op *yield_from = (zend_op *) root->execute_data->opline - 1;
     531             : 
     532       50021 :                                 if (yield_from->opcode == ZEND_YIELD_FROM && !(yield_from->result_type & EXT_TYPE_UNUSED)) {
     533      100042 :                                         if (Z_ISUNDEF(root->node.parent->retval)) {
     534             :                                                 /* Throw the exception in the context of the generator */
     535           2 :                                                 zend_execute_data *original_execute_data = EG(current_execute_data);
     536           2 :                                                 EG(current_execute_data) = root->execute_data;
     537             : 
     538           2 :                                                 if (root == generator) {
     539           2 :                                                         root->execute_data->prev_execute_data = original_execute_data;
     540             :                                                 } else {
     541           0 :                                                         root->execute_data->prev_execute_data = &generator->execute_fake;
     542           0 :                                                         generator->execute_fake.prev_execute_data = original_execute_data;
     543             :                                                 }
     544             : 
     545           2 :                                                 zend_throw_exception(zend_ce_ClosedGeneratorException, "Generator yielded from aborted, no return value available", 0);
     546             : 
     547           2 :                                                 EG(current_execute_data) = original_execute_data;
     548             :                                         } else {
     549       50019 :                                                 ZVAL_COPY(ZEND_CALL_VAR(root->execute_data, yield_from->result.var), &root->node.parent->retval);
     550             :                                         }
     551             :                                 }
     552             :                         }
     553             : 
     554       50027 :                         root->node.parent = NULL;
     555             :                 } else {
     556             :                         do {
     557         221 :                                 root = root->node.parent;
     558         221 :                                 ++GC_REFCOUNT(&root->std);
     559         221 :                         } while (root->node.parent);
     560             :                 }
     561             :         }
     562             : 
     563       50094 :         if (old_root) {
     564       50028 :                 OBJ_RELEASE(&old_root->std);
     565             :         }
     566             : 
     567       50094 :         return leaf->node.ptr.root = root;
     568             : }
     569             : 
     570           8 : static int zend_generator_get_next_delegated_value(zend_generator *generator) /* {{{ */
     571             : {
     572             :         zval *value;
     573          16 :         if (Z_TYPE(generator->values) == IS_ARRAY) {
     574           4 :                 HashTable *ht = Z_ARR(generator->values);
     575           4 :                 HashPosition pos = Z_FE_POS(generator->values);
     576             : 
     577             :                 Bucket *p;
     578             :                 do {
     579           4 :                         if (UNEXPECTED(pos >= ht->nNumUsed)) {
     580             :                                 /* Reached end of array */
     581           2 :                                 goto failure;
     582             :                         }
     583             : 
     584           2 :                         p = &ht->arData[pos];
     585           2 :                         value = &p->val;
     586           2 :                         if (Z_TYPE_P(value) == IS_INDIRECT) {
     587           0 :                                 value = Z_INDIRECT_P(value);
     588             :                         }
     589           2 :                         pos++;
     590           2 :                 } while (Z_ISUNDEF_P(value));
     591             : 
     592           2 :                 zval_ptr_dtor(&generator->value);
     593           2 :                 ZVAL_COPY(&generator->value, value);
     594             : 
     595           2 :                 zval_ptr_dtor(&generator->key);
     596           2 :                 if (p->key) {
     597           0 :                         ZVAL_STR_COPY(&generator->key, p->key);
     598             :                 } else {
     599           2 :                         ZVAL_LONG(&generator->key, p->h);
     600             :                 }
     601             : 
     602           2 :                 Z_FE_POS(generator->values) = pos;
     603             :         } else {
     604           4 :                 zend_object_iterator *iter = (zend_object_iterator *) Z_OBJ(generator->values);
     605             : 
     606           4 :                 if (iter->index++ > 0) {
     607           3 :                         iter->funcs->move_forward(iter);
     608           3 :                         if (UNEXPECTED(EG(exception) != NULL)) {
     609           0 :                                 goto failure;
     610             :                         }
     611             :                 }
     612             : 
     613           4 :                 if (iter->funcs->valid(iter) == FAILURE) {
     614             :                         /* reached end of iteration */
     615           1 :                         goto failure;
     616             :                 }
     617             : 
     618           3 :                 value = iter->funcs->get_current_data(iter);
     619           3 :                 if (UNEXPECTED(EG(exception) != NULL || !value)) {
     620             :                         goto failure;
     621             :                 }
     622             : 
     623           3 :                 zval_ptr_dtor(&generator->value);
     624           3 :                 ZVAL_COPY(&generator->value, value);
     625             : 
     626           3 :                 zval_ptr_dtor(&generator->key);
     627           3 :                 if (iter->funcs->get_current_key) {
     628           3 :                         iter->funcs->get_current_key(iter, &generator->key);
     629           3 :                         if (UNEXPECTED(EG(exception) != NULL)) {
     630           0 :                                 ZVAL_UNDEF(&generator->key);
     631           0 :                                 goto failure;
     632             :                         }
     633             :                 } else {
     634           0 :                         ZVAL_LONG(&generator->key, iter->index);
     635             :                 }
     636             :         }
     637           5 :         return SUCCESS;
     638             : 
     639             : failure:
     640           3 :         zval_ptr_dtor(&generator->values);
     641           3 :         ZVAL_UNDEF(&generator->values);
     642           3 :         return FAILURE;
     643             : }
     644             : /* }}} */
     645             : 
     646     2351829 : ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */
     647             : {
     648             :         zend_generator *generator;
     649             : 
     650             :         /* The generator is already closed, thus can't resume */
     651     2351829 :         if (!orig_generator->execute_data) {
     652           2 :                 return;
     653             :         }
     654             : 
     655     2351827 :         generator = zend_generator_get_current(orig_generator);
     656             : 
     657             : try_again:
     658     2452002 :         if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
     659           2 :                 zend_error(E_EXCEPTION | E_ERROR, "Cannot resume an already running generator");
     660           2 :                 return;
     661             :         }
     662             : 
     663     4904000 :         if (!Z_ISUNDEF(generator->values)) {
     664           8 :                 if (zend_generator_get_next_delegated_value(generator) == SUCCESS) {
     665           5 :                         return;
     666             :                 }
     667             :                 /* If there are no more deletegated values, resume the generator
     668             :                  * after the "yield from" expression. */
     669             :         }
     670             : 
     671     2502404 :         if ((orig_generator->flags & ZEND_GENERATOR_DO_INIT) && !Z_ISUNDEF(generator->value)) {
     672             :                 /* We must not advance Generator if we yield from a Generator being currently run */
     673          64 :                 return;
     674             :         }
     675             : 
     676             :         /* Drop the AT_FIRST_YIELD flag */
     677     2451931 :         orig_generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
     678             : 
     679             :         {
     680             :                 /* Backup executor globals */
     681     2451931 :                 zend_execute_data *original_execute_data = EG(current_execute_data);
     682     2451931 :                 zend_class_entry *original_scope = EG(scope);
     683     2451931 :                 zend_vm_stack original_stack = EG(vm_stack);
     684     2451931 :                 original_stack->top = EG(vm_stack_top);
     685             : 
     686             :                 /* Set executor globals */
     687     2451931 :                 EG(current_execute_data) = generator->execute_data;
     688     2451931 :                 EG(scope) = generator->execute_data->func->common.scope;
     689     2451931 :                 EG(vm_stack_top) = generator->stack->top;
     690     2451931 :                 EG(vm_stack_end) = generator->stack->end;
     691     2451931 :                 EG(vm_stack) = generator->stack;
     692             : 
     693             :                 /* We want the backtrace to look as if the generator function was
     694             :                  * called from whatever method we are current running (e.g. next()).
     695             :                  * So we have to link generator call frame with caller call frame. */
     696     2451931 :                 if (generator == orig_generator) {
     697     2351624 :                         generator->execute_data->prev_execute_data = original_execute_data;
     698             :                 } else {
     699             :                         /* We need some execute_data placeholder in stacktrace to be replaced
     700             :                          * by the real stack trace when needed */
     701      100307 :                         generator->execute_data->prev_execute_data = &orig_generator->execute_fake;
     702      100307 :                         orig_generator->execute_fake.prev_execute_data = original_execute_data;
     703             :                 }
     704             : 
     705             :                 /* Resume execution */
     706     2451931 :                 generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
     707     2451931 :                 zend_execute_ex(generator->execute_data);
     708     2451928 :                 generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
     709             : 
     710             :                 /* Unlink generator call_frame from the caller and backup vm_stack_top */
     711     2451928 :                 if (generator->execute_data) {
     712     2401798 :                         generator->stack = EG(vm_stack);
     713     2401798 :                         generator->stack->top = EG(vm_stack_top);
     714             :                 }
     715             : 
     716             :                 /* Restore executor globals */
     717     2451928 :                 EG(current_execute_data) = original_execute_data;
     718     2451928 :                 EG(scope) = original_scope;
     719     2451928 :                 EG(vm_stack_top) = original_stack->top;
     720     2451928 :                 EG(vm_stack_end) = original_stack->end;
     721     2451928 :                 EG(vm_stack) = original_stack;
     722             : 
     723             :                 /* If an exception was thrown in the generator we have to internally
     724             :                  * rethrow it in the parent scope.
     725             :                  * In case we did yield from, the Exception must be rethrown into
     726             :                  * its calling frame (see above in if (check_yield_from). */
     727     2451928 :                 if (UNEXPECTED(EG(exception) != NULL)) {
     728          27 :                         if (generator == orig_generator) {
     729          21 :                                 zend_generator_close(generator, 0);
     730          21 :                                 zend_throw_exception_internal(NULL);
     731             :                         } else {
     732           6 :                                 generator = zend_generator_get_current(orig_generator);
     733           6 :                                 zend_generator_throw_exception(generator, NULL);
     734           6 :                                 goto try_again;
     735             :                         }
     736             :                 }
     737             : 
     738             :                 /* yield from was used, try another resume. */
     739     2552223 :                 if ((generator != orig_generator && !Z_ISUNDEF(generator->retval)) || (generator->execute_data && (generator->execute_data->opline - 1)->opcode == ZEND_YIELD_FROM)) {
     740      100169 :                         generator = zend_generator_get_current(orig_generator);
     741      100169 :                         goto try_again;
     742             :                 }
     743             :         }
     744             : }
     745             : /* }}} */
     746             : 
     747     2352674 : static void zend_generator_ensure_initialized(zend_generator *generator) /* {{{ */
     748             : {
     749     4705280 :         if (generator->execute_data && Z_TYPE(generator->value) == IS_UNDEF && generator->node.parent == NULL) {
     750         339 :                 generator->flags |= ZEND_GENERATOR_DO_INIT;
     751         339 :                 zend_generator_resume(generator);
     752         336 :                 generator->flags &= ~ZEND_GENERATOR_DO_INIT;
     753         336 :                 generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD;
     754             :         }
     755     2352671 : }
     756             : /* }}} */
     757             : 
     758          67 : static void zend_generator_rewind(zend_generator *generator) /* {{{ */
     759             : {
     760          67 :         zend_generator_ensure_initialized(generator);
     761             : 
     762          65 :         if (!(generator->flags & ZEND_GENERATOR_AT_FIRST_YIELD)) {
     763           1 :                 zend_throw_exception(NULL, "Cannot rewind a generator that was already run", 0);
     764             :         }
     765          65 : }
     766             : /* }}} */
     767             : 
     768             : /* {{{ proto void Generator::rewind()
     769             :  * Rewind the generator */
     770          16 : ZEND_METHOD(Generator, rewind)
     771             : {
     772             :         zend_generator *generator;
     773             : 
     774          16 :         if (zend_parse_parameters_none() == FAILURE) {
     775           0 :                 return;
     776             :         }
     777             : 
     778          16 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     779             : 
     780          16 :         zend_generator_rewind(generator);
     781             : }
     782             : /* }}} */
     783             : 
     784             : /* {{{ proto bool Generator::valid()
     785             :  * Check whether the generator is valid */
     786         328 : ZEND_METHOD(Generator, valid)
     787             : {
     788             :         zend_generator *generator;
     789             : 
     790         328 :         if (zend_parse_parameters_none() == FAILURE) {
     791           0 :                 return;
     792             :         }
     793             : 
     794         328 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     795             : 
     796         328 :         zend_generator_ensure_initialized(generator);
     797             : 
     798         328 :         zend_generator_get_current(generator);
     799             : 
     800         656 :         RETURN_BOOL(Z_TYPE(generator->value) != IS_UNDEF || generator->node.parent != NULL);
     801             : }
     802             : /* }}} */
     803             : 
     804             : /* {{{ proto mixed Generator::current()
     805             :  * Get the current value */
     806         407 : ZEND_METHOD(Generator, current)
     807             : {
     808             :         zend_generator *generator, *root;
     809             : 
     810         407 :         if (zend_parse_parameters_none() == FAILURE) {
     811           0 :                 return;
     812             :         }
     813             : 
     814         407 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     815             : 
     816         407 :         zend_generator_ensure_initialized(generator);
     817             : 
     818         406 :         root = zend_generator_get_current(generator);
     819         812 :         if (Z_TYPE(root->value) != IS_UNDEF) {
     820        1206 :                 RETURN_ZVAL_FAST(&root->value);
     821             :         }
     822             : }
     823             : /* }}} */
     824             : 
     825             : /* {{{ proto mixed Generator::key()
     826             :  * Get the current key */
     827          20 : ZEND_METHOD(Generator, key)
     828             : {
     829             :         zend_generator *generator, *root;
     830             : 
     831          20 :         if (zend_parse_parameters_none() == FAILURE) {
     832           0 :                 return;
     833             :         }
     834             : 
     835          20 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     836             : 
     837          20 :         zend_generator_ensure_initialized(generator);
     838             : 
     839          20 :         root = zend_generator_get_current(generator);
     840          40 :         if (Z_TYPE(root->key) != IS_UNDEF) {
     841          60 :                 RETURN_ZVAL_FAST(&root->key);
     842             :         }
     843             : }
     844             : /* }}} */
     845             : 
     846             : /* {{{ proto void Generator::next()
     847             :  * Advances the generator */
     848         309 : ZEND_METHOD(Generator, next)
     849             : {
     850             :         zend_generator *generator;
     851             : 
     852         309 :         if (zend_parse_parameters_none() == FAILURE) {
     853           0 :                 return;
     854             :         }
     855             : 
     856         309 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     857             : 
     858         309 :         zend_generator_ensure_initialized(generator);
     859             : 
     860         309 :         zend_generator_resume(generator);
     861             : }
     862             : /* }}} */
     863             : 
     864             : /* {{{ proto mixed Generator::send(mixed $value)
     865             :  * Sends a value to the generator */
     866     2351041 : ZEND_METHOD(Generator, send)
     867             : {
     868             :         zval *value;
     869             :         zend_generator *generator, *root;
     870             : 
     871     2351041 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) {
     872           0 :                 return;
     873             :         }
     874             : 
     875     2351041 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     876             : 
     877     2351041 :         zend_generator_ensure_initialized(generator);
     878             : 
     879             :         /* The generator is already closed, thus can't send anything */
     880     2351041 :         if (!generator->execute_data) {
     881           1 :                 return;
     882             :         }
     883             : 
     884     2351040 :         root = zend_generator_get_current(generator);
     885             :         /* Put sent value in the target VAR slot, if it is used */
     886     2351040 :         if (root->send_target) {
     887     2351035 :                 if (Z_REFCOUNTED_P(root->send_target)) Z_DELREF_P(root->send_target);
     888     2351035 :                 ZVAL_COPY(root->send_target, value);
     889             :         }
     890             : 
     891     2351040 :         zend_generator_resume(generator);
     892             : 
     893     2351040 :         root = zend_generator_get_current(generator);
     894     4702080 :         if (Z_TYPE(root->value) != IS_UNDEF) {
     895     7053087 :                 RETURN_ZVAL_FAST(&root->value);
     896             :         }
     897             : }
     898             : /* }}} */
     899             : 
     900             : /* {{{ proto mixed Generator::throw(Exception $exception)
     901             :  * Throws an exception into the generator */
     902          11 : ZEND_METHOD(Generator, throw)
     903             : {
     904             :         zval *exception, exception_copy;
     905             :         zend_generator *generator;
     906             : 
     907          11 :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &exception) == FAILURE) {
     908           0 :                 return;
     909             :         }
     910             : 
     911          11 :         ZVAL_DUP(&exception_copy, exception);
     912             : 
     913          11 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     914             : 
     915          11 :         zend_generator_ensure_initialized(generator);
     916             : 
     917          11 :         if (generator->execute_data) {
     918          10 :                 zend_generator *root = zend_generator_get_current(generator);
     919             : 
     920          10 :                 zend_generator_throw_exception(root, &exception_copy);
     921             : 
     922          10 :                 zend_generator_resume(generator);
     923             : 
     924          10 :                 root = zend_generator_get_current(generator);
     925          20 :                 if (Z_TYPE(root->value) != IS_UNDEF) {
     926          15 :                         RETURN_ZVAL_FAST(&root->value);
     927             :                 }
     928             :         } else {
     929             :                 /* If the generator is already closed throw the exception in the
     930             :                  * current context */
     931           1 :                 zend_throw_exception_object(&exception_copy);
     932             :         }
     933             : }
     934             : /* }}} */
     935             : 
     936             : /* {{{ proto mixed Generator::getReturn()
     937             :  * Retrieves the return value of the generator */
     938          18 : ZEND_METHOD(Generator, getReturn)
     939             : {
     940             :         zend_generator *generator;
     941             : 
     942          18 :         if (zend_parse_parameters_none() == FAILURE) {
     943           0 :                 return;
     944             :         }
     945             : 
     946          18 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     947             : 
     948          18 :         zend_generator_ensure_initialized(generator);
     949          18 :         if (EG(exception)) {
     950           2 :                 return;
     951             :         }
     952             : 
     953          32 :         if (Z_ISUNDEF(generator->retval)) {
     954             :                 /* Generator hasn't returned yet -> error! */
     955           4 :                 zend_throw_exception(NULL,
     956             :                         "Cannot get return value of a generator that hasn't returned", 0);
     957           4 :                 return;
     958             :         }
     959             : 
     960          12 :         ZVAL_COPY(return_value, &generator->retval);
     961             : }
     962             : /* }}} */
     963             : 
     964             : /* {{{ proto void Generator::__wakeup()
     965             :  * Throws an Exception as generators can't be serialized */
     966           0 : ZEND_METHOD(Generator, __wakeup)
     967             : {
     968             :         /* Just specifying the zend_class_unserialize_deny handler is not enough,
     969             :          * because it is only invoked for C unserialization. For O the error has
     970             :          * to be thrown in __wakeup. */
     971             : 
     972           0 :         if (zend_parse_parameters_none() == FAILURE) {
     973           0 :                 return;
     974             :         }
     975             : 
     976           0 :         zend_throw_exception(NULL, "Unserialization of 'Generator' is not allowed", 0);
     977             : }
     978             : /* }}} */
     979             : 
     980             : /* get_iterator implementation */
     981             : 
     982          52 : static void zend_generator_iterator_dtor(zend_object_iterator *iterator) /* {{{ */
     983             : {
     984          52 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
     985          52 :         generator->iterator = NULL;
     986          52 :         zval_ptr_dtor(&iterator->data);
     987          52 :         zend_iterator_dtor(iterator);
     988          52 : }
     989             : /* }}} */
     990             : 
     991         172 : static int zend_generator_iterator_valid(zend_object_iterator *iterator) /* {{{ */
     992             : {
     993         172 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
     994             : 
     995         172 :         zend_generator_ensure_initialized(generator);
     996             : 
     997         172 :         zend_generator_get_current(generator);
     998             : 
     999         344 :         return Z_TYPE(generator->value) != IS_UNDEF || generator->node.parent != NULL ? SUCCESS : FAILURE;
    1000             : }
    1001             : /* }}} */
    1002             : 
    1003         131 : static zval *zend_generator_iterator_get_data(zend_object_iterator *iterator) /* {{{ */
    1004             : {
    1005         131 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data), *root;
    1006             : 
    1007         131 :         zend_generator_ensure_initialized(generator);
    1008             : 
    1009         131 :         root = zend_generator_get_current(generator);
    1010             : 
    1011         131 :         return &root->value;
    1012             : }
    1013             : /* }}} */
    1014             : 
    1015          42 : static void zend_generator_iterator_get_key(zend_object_iterator *iterator, zval *key) /* {{{ */
    1016             : {
    1017          42 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data), *root;
    1018             : 
    1019          42 :         zend_generator_ensure_initialized(generator);
    1020             : 
    1021          42 :         root = zend_generator_get_current(generator);
    1022             : 
    1023          84 :         if (Z_TYPE(root->key) != IS_UNDEF) {
    1024          84 :                 ZVAL_ZVAL(key, &root->key, 1, 0);
    1025             :         } else {
    1026           0 :                 ZVAL_NULL(key);
    1027             :         }
    1028          42 : }
    1029             : /* }}} */
    1030             : 
    1031         128 : static void zend_generator_iterator_move_forward(zend_object_iterator *iterator) /* {{{ */
    1032             : {
    1033         128 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
    1034             : 
    1035         128 :         zend_generator_ensure_initialized(generator);
    1036             : 
    1037         128 :         zend_generator_resume(generator);
    1038         128 : }
    1039             : /* }}} */
    1040             : 
    1041          51 : static void zend_generator_iterator_rewind(zend_object_iterator *iterator) /* {{{ */
    1042             : {
    1043          51 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
    1044             : 
    1045          51 :         zend_generator_rewind(generator);
    1046          51 : }
    1047             : /* }}} */
    1048             : 
    1049             : static zend_object_iterator_funcs zend_generator_iterator_functions = {
    1050             :         zend_generator_iterator_dtor,
    1051             :         zend_generator_iterator_valid,
    1052             :         zend_generator_iterator_get_data,
    1053             :         zend_generator_iterator_get_key,
    1054             :         zend_generator_iterator_move_forward,
    1055             :         zend_generator_iterator_rewind
    1056             : };
    1057             : 
    1058          54 : zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
    1059             : {
    1060             :         zend_object_iterator *iterator;
    1061          54 :         zend_generator *generator = (zend_generator*)Z_OBJ_P(object);
    1062             : 
    1063          54 :         if (!generator->execute_data) {
    1064           1 :                 zend_throw_exception(NULL, "Cannot traverse an already closed generator", 0);
    1065           1 :                 return NULL;
    1066             :         }
    1067             : 
    1068          53 :         if (by_ref && !(generator->execute_data->func->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
    1069           1 :                 zend_throw_exception(NULL, "You can only iterate a generator by-reference if it declared that it yields by-reference", 0);
    1070           1 :                 return NULL;
    1071             :         }
    1072             : 
    1073          52 :         iterator = generator->iterator = emalloc(sizeof(zend_object_iterator));
    1074             : 
    1075          52 :         zend_iterator_init(iterator);
    1076             : 
    1077          52 :         iterator->funcs = &zend_generator_iterator_functions;
    1078          52 :         ZVAL_COPY(&iterator->data, object);
    1079             : 
    1080          52 :         return iterator;
    1081             : }
    1082             : /* }}} */
    1083             : 
    1084             : ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0)
    1085             : ZEND_END_ARG_INFO()
    1086             : 
    1087             : ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1)
    1088             :         ZEND_ARG_INFO(0, value)
    1089             : ZEND_END_ARG_INFO()
    1090             : 
    1091             : ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_throw, 0, 0, 1)
    1092             :         ZEND_ARG_INFO(0, exception)
    1093             : ZEND_END_ARG_INFO()
    1094             : 
    1095             : static const zend_function_entry generator_functions[] = {
    1096             :         ZEND_ME(Generator, rewind,   arginfo_generator_void, ZEND_ACC_PUBLIC)
    1097             :         ZEND_ME(Generator, valid,    arginfo_generator_void, ZEND_ACC_PUBLIC)
    1098             :         ZEND_ME(Generator, current,  arginfo_generator_void, ZEND_ACC_PUBLIC)
    1099             :         ZEND_ME(Generator, key,      arginfo_generator_void, ZEND_ACC_PUBLIC)
    1100             :         ZEND_ME(Generator, next,     arginfo_generator_void, ZEND_ACC_PUBLIC)
    1101             :         ZEND_ME(Generator, send,     arginfo_generator_send, ZEND_ACC_PUBLIC)
    1102             :         ZEND_ME(Generator, throw,    arginfo_generator_throw, ZEND_ACC_PUBLIC)
    1103             :         ZEND_ME(Generator, getReturn,arginfo_generator_void, ZEND_ACC_PUBLIC)
    1104             :         ZEND_ME(Generator, __wakeup, arginfo_generator_void, ZEND_ACC_PUBLIC)
    1105             :         ZEND_FE_END
    1106             : };
    1107             : 
    1108       20952 : void zend_register_generator_ce(void) /* {{{ */
    1109             : {
    1110             :         zend_class_entry ce;
    1111             : 
    1112       20952 :         INIT_CLASS_ENTRY(ce, "Generator", generator_functions);
    1113       20952 :         zend_ce_generator = zend_register_internal_class(&ce);
    1114       20952 :         zend_ce_generator->ce_flags |= ZEND_ACC_FINAL;
    1115       20952 :         zend_ce_generator->create_object = zend_generator_create;
    1116       20952 :         zend_ce_generator->serialize = zend_class_serialize_deny;
    1117       20952 :         zend_ce_generator->unserialize = zend_class_unserialize_deny;
    1118             : 
    1119             :         /* get_iterator has to be assigned *after* implementing the inferface */
    1120       20952 :         zend_class_implements(zend_ce_generator, 1, zend_ce_iterator);
    1121       20952 :         zend_ce_generator->get_iterator = zend_generator_get_iterator;
    1122       20952 :         zend_ce_generator->iterator_funcs.funcs = &zend_generator_iterator_functions;
    1123             : 
    1124       20952 :         memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
    1125       20952 :         zend_generator_handlers.free_obj = zend_generator_free_storage;
    1126       20952 :         zend_generator_handlers.dtor_obj = zend_generator_dtor_storage;
    1127       20952 :         zend_generator_handlers.clone_obj = NULL;
    1128       20952 :         zend_generator_handlers.get_constructor = zend_generator_get_constructor;
    1129             : 
    1130       20952 :         INIT_CLASS_ENTRY(ce, "ClosedGeneratorException", NULL);
    1131       20952 :         zend_ce_ClosedGeneratorException = zend_register_internal_class_ex(&ce, zend_exception_get_default());
    1132       20952 : }
    1133             : /* }}} */
    1134             : 
    1135             : /*
    1136             :  * Local variables:
    1137             :  * tab-width: 4
    1138             :  * c-basic-offset: 4
    1139             :  * indent-tabs-mode: t
    1140             :  * End:
    1141             :  */

Generated by: LCOV version 1.10

Generated at Sat, 30 May 2015 05:52:43 +0000 (8 hours ago)

Copyright © 2005-2015 The PHP Group
All rights reserved.