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: 287 306 93.8 %
Date: 2014-08-04 Functions: 26 27 96.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Mon, 04 Aug 2014 15:49:00 +0000 (29 days ago)

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