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

Generated by: LCOV version 1.10

Generated at Wed, 16 Apr 2014 12:47:43 +0000 (12 hours ago)

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