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: 472 520 90.8 %
Date: 2016-02-09 Functions: 35 37 94.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Tue, 09 Feb 2016 10:48:37 +0000 (3 days ago)

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