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: 530 586 90.4 %
Date: 2016-08-24 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          32 : static void zend_restore_call_stack(zend_generator *generator) /* {{{ */
      35             : {
      36          32 :         zend_execute_data *call, *new_call, *prev_call = NULL;
      37             : 
      38          32 :         call = generator->frozen_call_stack;
      39             :         do {
      40         178 :                 new_call = zend_vm_stack_push_call_frame(
      41          37 :                         (ZEND_CALL_INFO(call) & ~ZEND_CALL_ALLOCATED),
      42             :                         call->func,
      43             :                         ZEND_CALL_NUM_ARGS(call),
      44          37 :                         (Z_TYPE(call->This) == IS_UNDEF) ?
      45          30 :                                 (zend_class_entry*)Z_OBJ(call->This) : NULL,
      46          37 :                         (Z_TYPE(call->This) != IS_UNDEF) ?
      47             :                                 Z_OBJ(call->This) : NULL);
      48          37 :                 memcpy(((zval*)new_call) + ZEND_CALL_FRAME_SLOT, ((zval*)call) + ZEND_CALL_FRAME_SLOT, ZEND_CALL_NUM_ARGS(call) * sizeof(zval));
      49          37 :                 new_call->prev_execute_data = prev_call;
      50          37 :                 prev_call = new_call;
      51             : 
      52          37 :                 call = call->prev_execute_data;
      53          37 :         } while (call);
      54          32 :         generator->execute_data->call = prev_call;
      55          32 :         efree(generator->frozen_call_stack);
      56          32 :         generator->frozen_call_stack = NULL;
      57          32 : }
      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         296 : static void zend_generator_cleanup_unfinished_execution(
     100             :                 zend_generator *generator, uint32_t catch_op_num) /* {{{ */
     101             : {
     102         296 :         zend_execute_data *execute_data = generator->execute_data;
     103             : 
     104         296 :         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         296 :                 uint32_t op_num = execute_data->opline - execute_data->func->op_array.opcodes - 1;
     107             : 
     108         296 :                 if (UNEXPECTED(generator->frozen_call_stack)) {
     109           7 :                         zend_restore_call_stack(generator);
     110             :                 }
     111         296 :                 zend_cleanup_unfinished_execution(execute_data, op_num, 0);
     112             :         }
     113         296 : }
     114             : /* }}} */
     115             : 
     116      100651 : ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution) /* {{{ */
     117             : {
     118      100651 :         if (EXPECTED(generator->execute_data)) {
     119       50455 :                 zend_execute_data *execute_data = generator->execute_data;
     120             : 
     121       50455 :                 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       50455 :                 zend_free_compiled_variables(execute_data);
     126             : 
     127       50455 :                 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       50455 :                 if (UNEXPECTED(CG(unclean_shutdown))) {
     134           6 :                         generator->execute_data = NULL;
     135           6 :                         return;
     136             :                 }
     137             : 
     138       50449 :                 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       50449 :                 if (UNEXPECTED(!finished_execution)) {
     143         283 :                         zend_generator_cleanup_unfinished_execution(generator, 0);
     144             :                 }
     145             : 
     146             :                 /* Free closure object */
     147       50449 :                 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       50449 :                 if (generator->gc_buffer) {
     153           4 :                         efree(generator->gc_buffer);
     154           4 :                         generator->gc_buffer = NULL;
     155             :                 }
     156             : 
     157       50449 :                 efree(generator->execute_data);
     158       50449 :                 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       50448 : static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
     166             : {
     167       50448 :         zend_generator *generator = (zend_generator*) object;
     168       50448 :         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      100896 :         if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) {
     174           5 :                 zval_ptr_dtor(&generator->values);
     175           5 :                 ZVAL_UNDEF(&generator->values);
     176             :         }
     177             : 
     178       50448 :         if (EXPECTED(generator->node.children == 0)) {
     179         350 :                 zend_generator *root = generator->node.ptr.root, *next;
     180         982 :                 while (UNEXPECTED(root != generator)) {
     181         282 :                         next = zend_generator_get_child(&root->node, generator);
     182         282 :                         OBJ_RELEASE(&root->std);
     183         282 :                         root = next;
     184             :                 }
     185         350 :                 generator->node.parent = NULL;
     186             :         }
     187             : 
     188       50448 :         if (EXPECTED(!ex) || EXPECTED(!(ex->func->op_array.fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK))) {
     189       50435 :                 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       50457 : static void zend_generator_free_storage(zend_object *object) /* {{{ */
     232             : {
     233       50457 :         zend_generator *generator = (zend_generator*) object;
     234             : 
     235       50457 :         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       50457 :         zval_ptr_dtor(&generator->value);
     239       50457 :         zval_ptr_dtor(&generator->key);
     240             : 
     241      100914 :         if (EXPECTED(!Z_ISUNDEF(generator->retval))) {
     242       50121 :                 zval_ptr_dtor(&generator->retval);
     243             :         }
     244             : 
     245       50457 :         if (UNEXPECTED(generator->node.children > 4)) {
     246          29 :                 zend_hash_destroy(&generator->node.child.ht);
     247             :         }
     248             : 
     249       50457 :         zend_object_std_dtor(&generator->std);
     250             : 
     251       50457 :         if (generator->iterator) {
     252           0 :                 zend_iterator_dtor(generator->iterator);
     253             :         }
     254       50457 : }
     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       50457 : static zend_object *zend_generator_create(zend_class_entry *class_type) /* {{{ */
     358             : {
     359             :         zend_generator *generator;
     360             : 
     361       50457 :         generator = emalloc(sizeof(zend_generator));
     362       50457 :         memset(generator, 0, sizeof(zend_generator));
     363             : 
     364             :         /* The key will be incremented on first use, so it'll start at 0 */
     365       50457 :         generator->largest_used_integer_key = -1;
     366             : 
     367       50457 :         ZVAL_UNDEF(&generator->retval);
     368       50457 :         ZVAL_UNDEF(&generator->values);
     369             : 
     370             :         /* By default we have a tree of only one node */
     371       50457 :         generator->node.parent = NULL;
     372       50457 :         generator->node.children = 0;
     373       50457 :         generator->node.ptr.root = generator;
     374             : 
     375       50457 :         zend_object_std_init(&generator->std, class_type);
     376       50457 :         generator->std.handlers = &zend_generator_handlers;
     377             : 
     378       50457 :         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        3967 : ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_data *ptr)
     391             : {
     392        4146 :         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        3967 :         return ptr;
     409             : }
     410             : 
     411          21 : static void zend_generator_throw_exception(zend_generator *generator, zval *exception)
     412             : {
     413          21 :         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          42 :         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          21 :         EG(current_execute_data) = generator->execute_data;
     424          21 :         generator->execute_data->opline--;
     425          21 :         if (exception) {
     426          12 :                 zend_throw_exception_object(exception);
     427             :         } else {
     428           9 :                 zend_throw_exception_internal(NULL);
     429             :         }
     430          21 :         generator->execute_data->opline++;
     431          21 :         EG(current_execute_data) = original_execute_data;
     432          21 : }
     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       50105 : ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator, zend_generator *leaf)
     599             : {
     600       50105 :         zend_generator *old_root, *root = leaf->node.ptr.root;
     601             : 
     602             :         /* generator at the root had stopped */
     603       50105 :         if (root != generator) {
     604       50038 :                 old_root = root;
     605       50038 :                 root = zend_generator_get_child(&root->node, leaf);
     606             :         } else {
     607          67 :                 old_root = NULL;
     608             :         }
     609             : 
     610      100210 :         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       50105 :         if (root->node.parent) {
     618       50104 :                 if (root->node.parent->execute_data == NULL) {
     619       50037 :                         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             : 
     639           2 :                                                 if (!((old_root ? old_root : generator)->flags & ZEND_GENERATOR_CURRENTLY_RUNNING)) {
     640           2 :                                                         leaf->node.ptr.root = root;
     641           2 :                                                         root->node.parent = NULL;
     642           2 :                                                         if (old_root) {
     643           2 :                                                                 OBJ_RELEASE(&old_root->std);
     644             :                                                         }
     645           2 :                                                         zend_generator_resume(leaf);
     646           2 :                                                         return leaf->node.ptr.root; /* this may be updated during zend_generator_resume! */
     647             :                                                 }
     648             :                                         } else {
     649       50026 :                                                 zval_ptr_dtor(&root->value);
     650       50026 :                                                 ZVAL_COPY(&root->value, &root->node.parent->value);
     651       50026 :                                                 ZVAL_COPY(ZEND_CALL_VAR(root->execute_data, yield_from->result.var), &root->node.parent->retval);
     652             :                                         }
     653             :                                 }
     654             :                         }
     655             : 
     656       50035 :                         root->node.parent = NULL;
     657             :                 } else {
     658             :                         do {
     659         222 :                                 root = root->node.parent;
     660         222 :                                 ++GC_REFCOUNT(&root->std);
     661         222 :                         } while (root->node.parent);
     662             :                 }
     663             :         }
     664             : 
     665       50103 :         if (old_root) {
     666       50036 :                 OBJ_RELEASE(&old_root->std);
     667             :         }
     668             : 
     669       50103 :         return leaf->node.ptr.root = root;
     670             : }
     671             : 
     672          25 : static int zend_generator_get_next_delegated_value(zend_generator *generator) /* {{{ */
     673             : {
     674             :         zval *value;
     675          50 :         if (Z_TYPE(generator->values) == IS_ARRAY) {
     676          18 :                 HashTable *ht = Z_ARR(generator->values);
     677          18 :                 HashPosition pos = Z_FE_POS(generator->values);
     678             : 
     679             :                 Bucket *p;
     680             :                 do {
     681          18 :                         if (UNEXPECTED(pos >= ht->nNumUsed)) {
     682             :                                 /* Reached end of array */
     683           3 :                                 goto failure;
     684             :                         }
     685             : 
     686          15 :                         p = &ht->arData[pos];
     687          15 :                         value = &p->val;
     688          15 :                         if (Z_TYPE_P(value) == IS_INDIRECT) {
     689           0 :                                 value = Z_INDIRECT_P(value);
     690             :                         }
     691          15 :                         pos++;
     692          15 :                 } while (Z_ISUNDEF_P(value));
     693             : 
     694          15 :                 zval_ptr_dtor(&generator->value);
     695          15 :                 ZVAL_COPY(&generator->value, value);
     696             : 
     697          15 :                 zval_ptr_dtor(&generator->key);
     698          15 :                 if (p->key) {
     699           0 :                         ZVAL_STR_COPY(&generator->key, p->key);
     700             :                 } else {
     701          15 :                         ZVAL_LONG(&generator->key, p->h);
     702             :                 }
     703             : 
     704          15 :                 Z_FE_POS(generator->values) = pos;
     705             :         } else {
     706           7 :                 zend_object_iterator *iter = (zend_object_iterator *) Z_OBJ(generator->values);
     707             : 
     708           7 :                 if (iter->index++ > 0) {
     709           4 :                         iter->funcs->move_forward(iter);
     710           4 :                         if (UNEXPECTED(EG(exception) != NULL)) {
     711           0 :                                 goto exception;
     712             :                         }
     713             :                 }
     714             : 
     715           7 :                 if (iter->funcs->valid(iter) == FAILURE) {
     716             :                         /* reached end of iteration */
     717           2 :                         goto failure;
     718             :                 }
     719             : 
     720           5 :                 value = iter->funcs->get_current_data(iter);
     721           5 :                 if (UNEXPECTED(EG(exception) != NULL)) {
     722           1 :                         goto exception;
     723           4 :                 } else if (UNEXPECTED(!value)) {
     724           0 :                         goto failure;
     725             :                 }
     726             : 
     727           4 :                 zval_ptr_dtor(&generator->value);
     728           4 :                 ZVAL_COPY(&generator->value, value);
     729             : 
     730           4 :                 zval_ptr_dtor(&generator->key);
     731           4 :                 if (iter->funcs->get_current_key) {
     732           4 :                         iter->funcs->get_current_key(iter, &generator->key);
     733           4 :                         if (UNEXPECTED(EG(exception) != NULL)) {
     734           0 :                                 ZVAL_UNDEF(&generator->key);
     735           0 :                                 goto exception;
     736             :                         }
     737             :                 } else {
     738           0 :                         ZVAL_LONG(&generator->key, iter->index);
     739             :                 }
     740             :         }
     741          19 :         return SUCCESS;
     742             : 
     743             : exception: {
     744           1 :                 zend_execute_data *ex = EG(current_execute_data);
     745           1 :                 EG(current_execute_data) = generator->execute_data;
     746           1 :                 zend_throw_exception_internal(NULL);
     747           1 :                 EG(current_execute_data) = ex;
     748             :         }
     749             : 
     750             : failure:
     751           6 :         zval_ptr_dtor(&generator->values);
     752           6 :         ZVAL_UNDEF(&generator->values);
     753           6 :         return FAILURE;
     754             : }
     755             : /* }}} */
     756             : 
     757     2351110 : ZEND_API void zend_generator_resume(zend_generator *orig_generator) /* {{{ */
     758             : {
     759             :         zend_generator *generator;
     760             : 
     761             :         /* The generator is already closed, thus can't resume */
     762     2351110 :         if (UNEXPECTED(!orig_generator->execute_data)) {
     763           5 :                 return;
     764             :         }
     765             : 
     766     2351105 :         generator = zend_generator_get_current(orig_generator);
     767             : 
     768             : try_again:
     769     2451314 :         if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
     770           2 :                 zend_throw_error(NULL, "Cannot resume an already running generator");
     771           2 :                 return;
     772             :         }
     773             : 
     774     2501781 :         if (UNEXPECTED((orig_generator->flags & ZEND_GENERATOR_DO_INIT) != 0 && !Z_ISUNDEF(generator->value))) {
     775             :                 /* We must not advance Generator if we yield from a Generator being currently run */
     776          65 :                 return;
     777             :         }
     778             : 
     779     4902494 :         if (UNEXPECTED(!Z_ISUNDEF(generator->values))) {
     780          25 :                 if (EXPECTED(zend_generator_get_next_delegated_value(generator) == SUCCESS)) {
     781          19 :                         return;
     782             :                 }
     783             :                 /* If there are no more deletegated values, resume the generator
     784             :                  * after the "yield from" expression. */
     785             :         }
     786             : 
     787             :         /* Drop the AT_FIRST_YIELD flag */
     788     2451228 :         orig_generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
     789             : 
     790             :         {
     791             :                 /* Backup executor globals */
     792     2451228 :                 zend_execute_data *original_execute_data = EG(current_execute_data);
     793             : 
     794             :                 /* Set executor globals */
     795     2451228 :                 EG(current_execute_data) = generator->execute_data;
     796             : 
     797             :                 /* We want the backtrace to look as if the generator function was
     798             :                  * called from whatever method we are current running (e.g. next()).
     799             :                  * So we have to link generator call frame with caller call frame. */
     800     2451228 :                 if (generator == orig_generator) {
     801     2350898 :                         generator->execute_data->prev_execute_data = original_execute_data;
     802             :                 } else {
     803             :                         /* We need some execute_data placeholder in stacktrace to be replaced
     804             :                          * by the real stack trace when needed */
     805      100330 :                         generator->execute_data->prev_execute_data = &orig_generator->execute_fake;
     806      100330 :                         orig_generator->execute_fake.prev_execute_data = original_execute_data;
     807             :                 }
     808             : 
     809     2451228 :                 if (UNEXPECTED(generator->frozen_call_stack)) {
     810             :                         /* Restore frozen call-stack */
     811          25 :                         zend_restore_call_stack(generator);
     812             :                 }
     813             : 
     814             :                 /* Resume execution */
     815     2451228 :                 generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
     816     2451228 :                 zend_execute_ex(generator->execute_data);
     817     2451225 :                 generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
     818             : 
     819     2451225 :                 generator->frozen_call_stack = NULL;
     820     4852284 :                 if (EXPECTED(generator->execute_data) &&
     821     2401059 :                     UNEXPECTED(generator->execute_data->call)) {
     822             :                         /* Frize call-stack */
     823          32 :                         generator->frozen_call_stack = zend_freeze_call_stack(generator->execute_data);
     824             :                 }
     825             : 
     826             :                 /* Restore executor globals */
     827     2451225 :                 EG(current_execute_data) = original_execute_data;
     828             : 
     829             :                 /* If an exception was thrown in the generator we have to internally
     830             :                  * rethrow it in the parent scope.
     831             :                  * In case we did yield from, the Exception must be rethrown into
     832             :                  * its calling frame (see above in if (check_yield_from). */
     833     2451225 :                 if (UNEXPECTED(EG(exception) != NULL)) {
     834          37 :                         if (generator == orig_generator) {
     835          28 :                                 zend_generator_close(generator, 0);
     836          28 :                                 zend_throw_exception_internal(NULL);
     837             :                         } else {
     838           9 :                                 generator = zend_generator_get_current(orig_generator);
     839           9 :                                 zend_generator_throw_exception(generator, NULL);
     840           9 :                                 goto try_again;
     841             :                         }
     842             :                 }
     843             : 
     844             :                 /* yield from was used, try another resume. */
     845     2551537 :                 if (UNEXPECTED((generator != orig_generator && !Z_ISUNDEF(generator->retval)) || (generator->execute_data && (generator->execute_data->opline - 1)->opcode == ZEND_YIELD_FROM))) {
     846      100200 :                         generator = zend_generator_get_current(orig_generator);
     847      100200 :                         goto try_again;
     848             :                 }
     849             :         }
     850             : }
     851             : /* }}} */
     852             : 
     853     2351988 : static inline void zend_generator_ensure_initialized(zend_generator *generator) /* {{{ */
     854             : {
     855     4703976 :         if (UNEXPECTED(Z_TYPE(generator->value) == IS_UNDEF) && EXPECTED(generator->execute_data) && EXPECTED(generator->node.parent == NULL)) {
     856         381 :                 generator->flags |= ZEND_GENERATOR_DO_INIT;
     857         381 :                 zend_generator_resume(generator);
     858         378 :                 generator->flags &= ~ZEND_GENERATOR_DO_INIT;
     859         378 :                 generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD;
     860             :         }
     861     2351985 : }
     862             : /* }}} */
     863             : 
     864          82 : static inline void zend_generator_rewind(zend_generator *generator) /* {{{ */
     865             : {
     866          82 :         zend_generator_ensure_initialized(generator);
     867             : 
     868          80 :         if (!(generator->flags & ZEND_GENERATOR_AT_FIRST_YIELD)) {
     869           1 :                 zend_throw_exception(NULL, "Cannot rewind a generator that was already run", 0);
     870             :         }
     871          80 : }
     872             : /* }}} */
     873             : 
     874             : /* {{{ proto void Generator::rewind()
     875             :  * Rewind the generator */
     876          24 : ZEND_METHOD(Generator, rewind)
     877             : {
     878             :         zend_generator *generator;
     879             : 
     880          24 :         if (zend_parse_parameters_none() == FAILURE) {
     881           0 :                 return;
     882             :         }
     883             : 
     884          48 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     885             : 
     886          24 :         zend_generator_rewind(generator);
     887             : }
     888             : /* }}} */
     889             : 
     890             : /* {{{ proto bool Generator::valid()
     891             :  * Check whether the generator is valid */
     892         341 : ZEND_METHOD(Generator, valid)
     893             : {
     894             :         zend_generator *generator;
     895             : 
     896         341 :         if (zend_parse_parameters_none() == FAILURE) {
     897           0 :                 return;
     898             :         }
     899             : 
     900         682 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     901             : 
     902         341 :         zend_generator_ensure_initialized(generator);
     903             : 
     904             :         zend_generator_get_current(generator);
     905             : 
     906         341 :         RETURN_BOOL(EXPECTED(generator->execute_data != NULL));
     907             : }
     908             : /* }}} */
     909             : 
     910             : /* {{{ proto mixed Generator::current()
     911             :  * Get the current value */
     912         431 : ZEND_METHOD(Generator, current)
     913             : {
     914             :         zend_generator *generator, *root;
     915             : 
     916         431 :         if (zend_parse_parameters_none() == FAILURE) {
     917           0 :                 return;
     918             :         }
     919             : 
     920         862 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     921             : 
     922         431 :         zend_generator_ensure_initialized(generator);
     923             : 
     924         430 :         root = zend_generator_get_current(generator);
     925         851 :         if (EXPECTED(generator->execute_data != NULL && Z_TYPE(root->value) != IS_UNDEF)) {
     926         421 :                 zval *value = &root->value;
     927             : 
     928         421 :                 ZVAL_DEREF(value);
     929         421 :                 ZVAL_COPY(return_value, value);
     930             :         }
     931             : }
     932             : /* }}} */
     933             : 
     934             : /* {{{ proto mixed Generator::key()
     935             :  * Get the current key */
     936          20 : ZEND_METHOD(Generator, key)
     937             : {
     938             :         zend_generator *generator, *root;
     939             : 
     940          20 :         if (zend_parse_parameters_none() == FAILURE) {
     941           0 :                 return;
     942             :         }
     943             : 
     944          40 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     945             : 
     946          20 :         zend_generator_ensure_initialized(generator);
     947             : 
     948          20 :         root = zend_generator_get_current(generator);
     949          40 :         if (EXPECTED(generator->execute_data != NULL && Z_TYPE(root->key) != IS_UNDEF)) {
     950          20 :                 zval *key = &root->key;
     951             : 
     952          20 :                 ZVAL_DEREF(key);
     953          20 :                 ZVAL_COPY(return_value, key);
     954             :         }
     955             : }
     956             : /* }}} */
     957             : 
     958             : /* {{{ proto void Generator::next()
     959             :  * Advances the generator */
     960         324 : ZEND_METHOD(Generator, next)
     961             : {
     962             :         zend_generator *generator;
     963             : 
     964         324 :         if (zend_parse_parameters_none() == FAILURE) {
     965           0 :                 return;
     966             :         }
     967             : 
     968         648 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     969             : 
     970         324 :         zend_generator_ensure_initialized(generator);
     971             : 
     972         324 :         zend_generator_resume(generator);
     973             : }
     974             : /* }}} */
     975             : 
     976             : /* {{{ proto mixed Generator::send(mixed value)
     977             :  * Sends a value to the generator */
     978     2350242 : ZEND_METHOD(Generator, send)
     979             : {
     980             :         zval *value;
     981             :         zend_generator *generator, *root;
     982             : 
     983             : #ifndef FAST_ZPP
     984             :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &value) == FAILURE) {
     985             :                 return;
     986             :         }
     987             : #else
     988     2350242 :         ZEND_PARSE_PARAMETERS_START(1, 1)
     989     2350242 :                 Z_PARAM_ZVAL(value)
     990     2350242 :         ZEND_PARSE_PARAMETERS_END();
     991             : #endif
     992             : 
     993     4700484 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     994             : 
     995     2350242 :         zend_generator_ensure_initialized(generator);
     996             : 
     997             :         /* The generator is already closed, thus can't send anything */
     998     2350242 :         if (UNEXPECTED(!generator->execute_data)) {
     999           1 :                 return;
    1000             :         }
    1001             : 
    1002     2350241 :         root = zend_generator_get_current(generator);
    1003             :         /* Put sent value in the target VAR slot, if it is used */
    1004     2350241 :         if (root->send_target) {
    1005     2350235 :                 ZVAL_COPY(root->send_target, value);
    1006             :         }
    1007             : 
    1008     2350241 :         zend_generator_resume(generator);
    1009             : 
    1010     2350241 :         root = zend_generator_get_current(generator);
    1011     2350241 :         if (EXPECTED(generator->execute_data)) {
    1012     2350229 :                 zval *value = &root->value;
    1013             : 
    1014     2350229 :                 ZVAL_DEREF(value);
    1015     2350229 :                 ZVAL_COPY(return_value, value);
    1016             :         }
    1017             : }
    1018             : /* }}} */
    1019             : 
    1020             : /* {{{ proto mixed Generator::throw(Exception exception)
    1021             :  * Throws an exception into the generator */
    1022          13 : ZEND_METHOD(Generator, throw)
    1023             : {
    1024             :         zval *exception, exception_copy;
    1025             :         zend_generator *generator;
    1026             : 
    1027             : #ifndef FAST_ZPP
    1028             :         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &exception) == FAILURE) {
    1029             :                 return;
    1030             :         }
    1031             : #else
    1032          13 :         ZEND_PARSE_PARAMETERS_START(1, 1)
    1033          13 :                 Z_PARAM_ZVAL(exception)
    1034          13 :         ZEND_PARSE_PARAMETERS_END();
    1035             : #endif
    1036             : 
    1037          13 :         ZVAL_DUP(&exception_copy, exception);
    1038             : 
    1039          26 :         generator = (zend_generator *) Z_OBJ_P(getThis());
    1040             : 
    1041          13 :         zend_generator_ensure_initialized(generator);
    1042             : 
    1043          13 :         if (generator->execute_data) {
    1044          12 :                 zend_generator *root = zend_generator_get_current(generator);
    1045             : 
    1046          12 :                 zend_generator_throw_exception(root, &exception_copy);
    1047             : 
    1048          12 :                 zend_generator_resume(generator);
    1049             : 
    1050          12 :                 root = zend_generator_get_current(generator);
    1051          12 :                 if (generator->execute_data) {
    1052           5 :                         zval *value = &root->value;
    1053             : 
    1054           5 :                         ZVAL_DEREF(value);
    1055           5 :                         ZVAL_COPY(return_value, value);
    1056             :                 }
    1057             :         } else {
    1058             :                 /* If the generator is already closed throw the exception in the
    1059             :                  * current context */
    1060           1 :                 zend_throw_exception_object(&exception_copy);
    1061             :         }
    1062             : }
    1063             : /* }}} */
    1064             : 
    1065             : /* {{{ proto mixed Generator::getReturn()
    1066             :  * Retrieves the return value of the generator */
    1067          20 : ZEND_METHOD(Generator, getReturn)
    1068             : {
    1069             :         zend_generator *generator;
    1070             : 
    1071          20 :         if (zend_parse_parameters_none() == FAILURE) {
    1072           0 :                 return;
    1073             :         }
    1074             : 
    1075          40 :         generator = (zend_generator *) Z_OBJ_P(getThis());
    1076             : 
    1077          20 :         zend_generator_ensure_initialized(generator);
    1078          20 :         if (UNEXPECTED(EG(exception))) {
    1079           2 :                 return;
    1080             :         }
    1081             : 
    1082          36 :         if (Z_ISUNDEF(generator->retval)) {
    1083             :                 /* Generator hasn't returned yet -> error! */
    1084           4 :                 zend_throw_exception(NULL,
    1085             :                         "Cannot get return value of a generator that hasn't returned", 0);
    1086           4 :                 return;
    1087             :         }
    1088             : 
    1089          14 :         ZVAL_COPY(return_value, &generator->retval);
    1090             : }
    1091             : /* }}} */
    1092             : 
    1093             : /* {{{ proto void Generator::__wakeup()
    1094             :  * Throws an Exception as generators can't be serialized */
    1095           0 : ZEND_METHOD(Generator, __wakeup)
    1096             : {
    1097             :         /* Just specifying the zend_class_unserialize_deny handler is not enough,
    1098             :          * because it is only invoked for C unserialization. For O the error has
    1099             :          * to be thrown in __wakeup. */
    1100             : 
    1101           0 :         if (zend_parse_parameters_none() == FAILURE) {
    1102           0 :                 return;
    1103             :         }
    1104             : 
    1105           0 :         zend_throw_exception(NULL, "Unserialization of 'Generator' is not allowed", 0);
    1106             : }
    1107             : /* }}} */
    1108             : 
    1109             : /* get_iterator implementation */
    1110             : 
    1111          59 : static void zend_generator_iterator_dtor(zend_object_iterator *iterator) /* {{{ */
    1112             : {
    1113          59 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
    1114          59 :         generator->iterator = NULL;
    1115          59 :         zval_ptr_dtor(&iterator->data);
    1116          59 :         zend_iterator_dtor(iterator);
    1117          59 : }
    1118             : /* }}} */
    1119             : 
    1120         190 : static int zend_generator_iterator_valid(zend_object_iterator *iterator) /* {{{ */
    1121             : {
    1122         190 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
    1123             : 
    1124         190 :         zend_generator_ensure_initialized(generator);
    1125             : 
    1126             :         zend_generator_get_current(generator);
    1127             : 
    1128         190 :         return generator->execute_data ? SUCCESS : FAILURE;
    1129             : }
    1130             : /* }}} */
    1131             : 
    1132         143 : static zval *zend_generator_iterator_get_data(zend_object_iterator *iterator) /* {{{ */
    1133             : {
    1134         143 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data), *root;
    1135             : 
    1136         143 :         zend_generator_ensure_initialized(generator);
    1137             : 
    1138         143 :         root = zend_generator_get_current(generator);
    1139             : 
    1140         143 :         return &root->value;
    1141             : }
    1142             : /* }}} */
    1143             : 
    1144          45 : static void zend_generator_iterator_get_key(zend_object_iterator *iterator, zval *key) /* {{{ */
    1145             : {
    1146          45 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data), *root;
    1147             : 
    1148          45 :         zend_generator_ensure_initialized(generator);
    1149             : 
    1150          45 :         root = zend_generator_get_current(generator);
    1151             : 
    1152          90 :         if (EXPECTED(Z_TYPE(root->key) != IS_UNDEF)) {
    1153          45 :                 zval *zv = &root->key;
    1154             : 
    1155          45 :                 ZVAL_DEREF(zv);
    1156          45 :                 ZVAL_COPY(key, zv);
    1157             :         } else {
    1158           0 :                 ZVAL_NULL(key);
    1159             :         }
    1160          45 : }
    1161             : /* }}} */
    1162             : 
    1163         137 : static void zend_generator_iterator_move_forward(zend_object_iterator *iterator) /* {{{ */
    1164             : {
    1165         137 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
    1166             : 
    1167         137 :         zend_generator_ensure_initialized(generator);
    1168             : 
    1169         137 :         zend_generator_resume(generator);
    1170         137 : }
    1171             : /* }}} */
    1172             : 
    1173          58 : static void zend_generator_iterator_rewind(zend_object_iterator *iterator) /* {{{ */
    1174             : {
    1175          58 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
    1176             : 
    1177          58 :         zend_generator_rewind(generator);
    1178          58 : }
    1179             : /* }}} */
    1180             : 
    1181             : static zend_object_iterator_funcs zend_generator_iterator_functions = {
    1182             :         zend_generator_iterator_dtor,
    1183             :         zend_generator_iterator_valid,
    1184             :         zend_generator_iterator_get_data,
    1185             :         zend_generator_iterator_get_key,
    1186             :         zend_generator_iterator_move_forward,
    1187             :         zend_generator_iterator_rewind,
    1188             :         NULL
    1189             : };
    1190             : 
    1191          62 : zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
    1192             : {
    1193             :         zend_object_iterator *iterator;
    1194          62 :         zend_generator *generator = (zend_generator*)Z_OBJ_P(object);
    1195             : 
    1196          62 :         if (!generator->execute_data) {
    1197           2 :                 zend_throw_exception(NULL, "Cannot traverse an already closed generator", 0);
    1198           2 :                 return NULL;
    1199             :         }
    1200             : 
    1201          60 :         if (UNEXPECTED(by_ref) && !(generator->execute_data->func->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
    1202           1 :                 zend_throw_exception(NULL, "You can only iterate a generator by-reference if it declared that it yields by-reference", 0);
    1203           1 :                 return NULL;
    1204             :         }
    1205             : 
    1206          59 :         iterator = generator->iterator = emalloc(sizeof(zend_object_iterator));
    1207             : 
    1208          59 :         zend_iterator_init(iterator);
    1209             : 
    1210          59 :         iterator->funcs = &zend_generator_iterator_functions;
    1211          59 :         ZVAL_COPY(&iterator->data, object);
    1212             : 
    1213          59 :         return iterator;
    1214             : }
    1215             : /* }}} */
    1216             : 
    1217             : ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0)
    1218             : ZEND_END_ARG_INFO()
    1219             : 
    1220             : ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1)
    1221             :         ZEND_ARG_INFO(0, value)
    1222             : ZEND_END_ARG_INFO()
    1223             : 
    1224             : ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_throw, 0, 0, 1)
    1225             :         ZEND_ARG_INFO(0, exception)
    1226             : ZEND_END_ARG_INFO()
    1227             : 
    1228             : static const zend_function_entry generator_functions[] = {
    1229             :         ZEND_ME(Generator, rewind,   arginfo_generator_void, ZEND_ACC_PUBLIC)
    1230             :         ZEND_ME(Generator, valid,    arginfo_generator_void, ZEND_ACC_PUBLIC)
    1231             :         ZEND_ME(Generator, current,  arginfo_generator_void, ZEND_ACC_PUBLIC)
    1232             :         ZEND_ME(Generator, key,      arginfo_generator_void, ZEND_ACC_PUBLIC)
    1233             :         ZEND_ME(Generator, next,     arginfo_generator_void, ZEND_ACC_PUBLIC)
    1234             :         ZEND_ME(Generator, send,     arginfo_generator_send, ZEND_ACC_PUBLIC)
    1235             :         ZEND_ME(Generator, throw,    arginfo_generator_throw, ZEND_ACC_PUBLIC)
    1236             :         ZEND_ME(Generator, getReturn,arginfo_generator_void, ZEND_ACC_PUBLIC)
    1237             :         ZEND_ME(Generator, __wakeup, arginfo_generator_void, ZEND_ACC_PUBLIC)
    1238             :         ZEND_FE_END
    1239             : };
    1240             : 
    1241       23504 : void zend_register_generator_ce(void) /* {{{ */
    1242             : {
    1243             :         zend_class_entry ce;
    1244             : 
    1245       23504 :         INIT_CLASS_ENTRY(ce, "Generator", generator_functions);
    1246       23504 :         zend_ce_generator = zend_register_internal_class(&ce);
    1247       23504 :         zend_ce_generator->ce_flags |= ZEND_ACC_FINAL;
    1248       23504 :         zend_ce_generator->create_object = zend_generator_create;
    1249       23504 :         zend_ce_generator->serialize = zend_class_serialize_deny;
    1250       23504 :         zend_ce_generator->unserialize = zend_class_unserialize_deny;
    1251             : 
    1252             :         /* get_iterator has to be assigned *after* implementing the inferface */
    1253       23504 :         zend_class_implements(zend_ce_generator, 1, zend_ce_iterator);
    1254       23504 :         zend_ce_generator->get_iterator = zend_generator_get_iterator;
    1255       23504 :         zend_ce_generator->iterator_funcs.funcs = &zend_generator_iterator_functions;
    1256             : 
    1257       23504 :         memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
    1258       23504 :         zend_generator_handlers.free_obj = zend_generator_free_storage;
    1259       23504 :         zend_generator_handlers.dtor_obj = zend_generator_dtor_storage;
    1260       23504 :         zend_generator_handlers.get_gc = zend_generator_get_gc;
    1261       23504 :         zend_generator_handlers.clone_obj = NULL;
    1262       23504 :         zend_generator_handlers.get_constructor = zend_generator_get_constructor;
    1263             : 
    1264       23504 :         INIT_CLASS_ENTRY(ce, "ClosedGeneratorException", NULL);
    1265       23504 :         zend_ce_ClosedGeneratorException = zend_register_internal_class_ex(&ce, zend_ce_exception);
    1266       23504 : }
    1267             : /* }}} */
    1268             : 
    1269             : /*
    1270             :  * Local variables:
    1271             :  * tab-width: 4
    1272             :  * c-basic-offset: 4
    1273             :  * indent-tabs-mode: t
    1274             :  * End:
    1275             :  */

Generated by: LCOV version 1.10

Generated at Wed, 24 Aug 2016 12:20:16 +0000 (33 hours ago)

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