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

Generated by: LCOV version 1.10

Generated at Wed, 20 Jul 2016 02:56:13 +0000 (3 days ago)

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