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: 264 281 94.0 %
Date: 2014-12-13 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 *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->func->op_array;
      36             : 
      37          15 :         if (generator->send_target) {
      38           3 :                 if (Z_REFCOUNTED_P(generator->send_target)) Z_DELREF_P(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 :                 uint32_t 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 ((uint32_t)brk_cont->start > op_num) {
      56           0 :                                 break;
      57           1 :                         } else if (brk_cont->brk >= 0 && (uint32_t)brk_cont->brk > op_num) {
      58           1 :                                 zend_op *brk_opline = op_array->opcodes + brk_cont->brk;
      59             : 
      60           1 :                                 if (brk_opline->opcode == ZEND_FREE) {
      61           1 :                                         zval *var = EX_VAR(brk_opline->op1.var);
      62             :                                         zval_ptr_dtor_nogc(var);
      63             :                                 }
      64             :                         }
      65             :                 }
      66             :         }
      67             : 
      68             :         /* If yield was used as a function argument there may be active
      69             :          * method calls those objects need to be freed */
      70          32 :         while (execute_data->call) {
      71           2 :                 if (Z_OBJ(execute_data->call->This)) {
      72           1 :                         OBJ_RELEASE(Z_OBJ(execute_data->call->This));
      73             :                 }
      74           2 :                 execute_data->call = execute_data->call->prev_execute_data;
      75             :         }
      76          15 : }
      77             : /* }}} */
      78             : 
      79         145 : ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished_execution TSRMLS_DC) /* {{{ */
      80             : {
      81         290 :         if (Z_TYPE(generator->value) != IS_UNDEF) {
      82          71 :                 zval_ptr_dtor(&generator->value);
      83          71 :                 ZVAL_UNDEF(&generator->value);
      84             :         }
      85             : 
      86         290 :         if (Z_TYPE(generator->key) != IS_UNDEF) {
      87          71 :                 zval_ptr_dtor(&generator->key);
      88          71 :                 ZVAL_UNDEF(&generator->key);
      89             :         }
      90             : 
      91         145 :         if (generator->execute_data) {
      92          83 :                 zend_execute_data *execute_data = generator->execute_data;
      93          83 :                 zend_op_array *op_array = &execute_data->func->op_array;
      94             : 
      95          83 :                 if (!execute_data->symbol_table) {
      96          83 :                         zend_free_compiled_variables(execute_data TSRMLS_CC);
      97             :                 } else {
      98           0 :                         zend_clean_and_cache_symbol_table(execute_data->symbol_table TSRMLS_CC);
      99             :                 }
     100             : 
     101          83 :                 if (Z_OBJ(execute_data->This)) {
     102           3 :                         OBJ_RELEASE(Z_OBJ(execute_data->This));
     103             :                 }
     104             : 
     105             :                 /* A fatal error / die occurred during the generator execution. Trying to clean
     106             :                  * up the stack may not be safe in this case. */
     107          83 :                 if (CG(unclean_shutdown)) {
     108          10 :                         generator->execute_data = NULL;
     109          10 :                         return;
     110             :                 }
     111             : 
     112          73 :                 zend_vm_stack_free_extra_args(generator->execute_data TSRMLS_CC);
     113             : 
     114             :                 /* Some cleanups are only necessary if the generator was closued
     115             :                  * before it could finish execution (reach a return statement). */
     116          73 :                 if (!finished_execution) {
     117          15 :                         zend_generator_cleanup_unfinished_execution(generator TSRMLS_CC);
     118             :                 }
     119             : 
     120             :                 /* Free a clone of closure */
     121          73 :                 if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
     122           7 :                         destroy_op_array(op_array TSRMLS_CC);
     123           7 :                         efree_size(op_array, sizeof(zend_op_array));
     124             :                 }
     125             : 
     126          73 :                 efree(generator->stack);
     127          73 :                 generator->execute_data = NULL;
     128             :         }
     129             : }
     130             : /* }}} */
     131             : 
     132          76 : static void zend_generator_dtor_storage(zend_object *object TSRMLS_DC) /* {{{ */
     133             : {
     134          76 :         zend_generator *generator = (zend_generator*) object;
     135          76 :         zend_execute_data *ex = generator->execute_data;
     136             :         uint32_t op_num, finally_op_num, finally_op_end;
     137             :         int i;
     138             : 
     139          76 :         if (!ex || !(ex->func->op_array.fn_flags & ZEND_ACC_HAS_FINALLY_BLOCK)) {
     140          73 :                 return;
     141             :         }
     142             : 
     143             :         /* -1 required because we want the last run opcode, not the
     144             :          * next to-be-run one. */
     145           3 :         op_num = ex->opline - ex->func->op_array.opcodes - 1;
     146             : 
     147             :         /* Find next finally block */
     148           3 :         finally_op_num = 0;
     149           3 :         finally_op_end = 0;
     150           7 :         for (i = 0; i < ex->func->op_array.last_try_catch; i++) {
     151           4 :                 zend_try_catch_element *try_catch = &ex->func->op_array.try_catch_array[i];
     152             : 
     153           4 :                 if (op_num < try_catch->try_op) {
     154           0 :                         break;
     155             :                 }
     156             : 
     157           4 :                 if (op_num < try_catch->finally_op) {
     158           4 :                         finally_op_num = try_catch->finally_op;
     159           4 :                         finally_op_end = try_catch->finally_end;
     160             :                 }
     161             :         }
     162             : 
     163             :         /* If a finally block was found we jump directly to it and
     164             :          * resume the generator. */
     165           3 :         if (finally_op_num) {
     166           3 :                 zval *fast_call = ZEND_CALL_VAR(ex, ex->func->op_array.opcodes[finally_op_end].op1.var);
     167             : 
     168           3 :                 Z_OBJ_P(fast_call) = NULL;
     169           3 :                 fast_call->u2.lineno = (uint32_t)-1;
     170           3 :                 ex->opline = &ex->func->op_array.opcodes[finally_op_num];
     171           3 :                 generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
     172           3 :                 zend_generator_resume(generator TSRMLS_CC);
     173             :         }
     174             : }
     175             : /* }}} */
     176             : 
     177          87 : static void zend_generator_free_storage(zend_object *object TSRMLS_DC) /* {{{ */
     178             : {
     179          87 :         zend_generator *generator = (zend_generator*) object;
     180             : 
     181          87 :         zend_generator_close(generator, 0 TSRMLS_CC);
     182             : 
     183          87 :         zend_object_std_dtor(&generator->std TSRMLS_CC);
     184             : 
     185          87 :         if (generator->iterator) {
     186           0 :                 zend_iterator_dtor(generator->iterator TSRMLS_CC);
     187             :         }
     188          87 : }
     189             : /* }}} */
     190             : 
     191          87 : static zend_object *zend_generator_create(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
     192             : {
     193             :         zend_generator *generator;
     194             : 
     195          87 :         generator = emalloc(sizeof(zend_generator));
     196          87 :         memset(generator, 0, sizeof(zend_generator));
     197             : 
     198             :         /* The key will be incremented on first use, so it'll start at 0 */
     199          87 :         generator->largest_used_integer_key = -1;
     200             : 
     201          87 :         zend_object_std_init(&generator->std, class_type TSRMLS_CC);
     202          87 :         generator->std.handlers = &zend_generator_handlers;
     203             : 
     204          87 :         return (zend_object*)generator;
     205             : }
     206             : /* }}} */
     207             : 
     208           5 : static int copy_closure_static_var(zval *var TSRMLS_DC, int num_args, va_list args, zend_hash_key *key) /* {{{ */
     209             : {
     210           5 :         HashTable *target = va_arg(args, HashTable *);
     211             : 
     212          10 :         ZVAL_MAKE_REF(var);
     213             :         Z_ADDREF_P(var);
     214           5 :         zend_hash_update(target, key->key, var);
     215           5 :         return 0;
     216             : }
     217             : /* }}} */
     218             : 
     219             : /* Requires globals EG(scope), EG(This) and EG(current_execute_data). */
     220          83 : ZEND_API void zend_generator_create_zval(zend_execute_data *call, zend_op_array *op_array, zval *return_value TSRMLS_DC) /* {{{ */
     221             : {
     222             :         zend_generator *generator;
     223             :         zend_execute_data *current_execute_data;
     224             :         zend_execute_data *execute_data;
     225          83 :         zend_vm_stack current_stack = EG(vm_stack);
     226             : 
     227          83 :         current_stack->top = EG(vm_stack_top);
     228             :         /* Create a clone of closure, because it may be destroyed */
     229          83 :         if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
     230           7 :                 zend_op_array *op_array_copy = (zend_op_array*)emalloc(sizeof(zend_op_array));
     231           7 :                 *op_array_copy = *op_array;
     232             : 
     233           7 :                 (*op_array->refcount)++;
     234           7 :                 op_array->run_time_cache = NULL;
     235           7 :                 if (op_array->static_variables) {
     236           5 :                         ALLOC_HASHTABLE(op_array_copy->static_variables);
     237           5 :                         zend_hash_init(
     238             :                                 op_array_copy->static_variables, 
     239             :                                 zend_hash_num_elements(op_array->static_variables),
     240             :                                 NULL, ZVAL_PTR_DTOR, 0
     241             :                         );
     242           5 :                         zend_hash_apply_with_arguments(
     243             :                                 op_array->static_variables TSRMLS_CC,
     244             :                                 copy_closure_static_var, 1, 
     245             :                                 op_array_copy->static_variables
     246             :                         );
     247             :                 }
     248             : 
     249           7 :                 op_array = op_array_copy;
     250             :         }
     251             :         
     252             :         /* Create new execution context. We have to back up and restore
     253             :          * EG(current_execute_data) here. */
     254          83 :         current_execute_data = EG(current_execute_data);
     255          83 :         execute_data = zend_create_generator_execute_data(call, op_array, return_value TSRMLS_CC);
     256          83 :         EG(current_execute_data) = current_execute_data;
     257             : 
     258          83 :         object_init_ex(return_value, zend_ce_generator);
     259             : 
     260          83 :         if (Z_OBJ(call->This)) {
     261           3 :                 Z_ADDREF(call->This);
     262             :         }
     263             : 
     264             :         /* Save execution context in generator object. */
     265          83 :         generator = (zend_generator *) Z_OBJ_P(return_value);
     266          83 :         execute_data->prev_execute_data = NULL;
     267          83 :         generator->execute_data = execute_data;
     268          83 :         generator->stack = EG(vm_stack);
     269          83 :         generator->stack->top = EG(vm_stack_top);
     270          83 :         EG(vm_stack_top) = current_stack->top;
     271          83 :         EG(vm_stack_end) = current_stack->end;
     272          83 :         EG(vm_stack) = current_stack;
     273             : 
     274             :         /* EX(return_value) keeps pointer to zend_object (not a real zval) */
     275          83 :         execute_data->return_value = (zval*)generator;
     276          83 : }
     277             : /* }}} */
     278             : 
     279           2 : static zend_function *zend_generator_get_constructor(zend_object *object TSRMLS_DC) /* {{{ */
     280             : {
     281           2 :         zend_error(E_RECOVERABLE_ERROR, "The \"Generator\" class is reserved for internal use and cannot be manually instantiated");
     282             : 
     283           0 :         return NULL;
     284             : }
     285             : /* }}} */
     286             : 
     287         225 : ZEND_API void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */
     288             : {
     289             :         /* The generator is already closed, thus can't resume */
     290         225 :         if (!generator->execute_data) {
     291           1 :                 return;
     292             :         }
     293             : 
     294         224 :         if (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING) {
     295           1 :                 zend_error(E_ERROR, "Cannot resume an already running generator");
     296             :         }
     297             : 
     298             :         /* Drop the AT_FIRST_YIELD flag */
     299         223 :         generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
     300             : 
     301             :         {
     302             :                 /* Backup executor globals */
     303         223 :                 zend_execute_data *original_execute_data = EG(current_execute_data);
     304         223 :                 zend_class_entry *original_scope = EG(scope);
     305         223 :                 zend_vm_stack original_stack = EG(vm_stack);
     306             : 
     307         223 :                 original_stack->top = EG(vm_stack_top);
     308             :                 /* Set executor globals */
     309         223 :                 EG(current_execute_data) = generator->execute_data;
     310         223 :                 EG(scope) = generator->execute_data->func->common.scope;
     311         223 :                 EG(vm_stack_top) = generator->stack->top;
     312         223 :                 EG(vm_stack_end) = generator->stack->end;
     313         223 :                 EG(vm_stack) = generator->stack;
     314             : 
     315             :                 /* We want the backtrace to look as if the generator function was
     316             :                  * called from whatever method we are current running (e.g. next()).
     317             :                  * So we have to link generator call frame with caller call frames */
     318             : 
     319         223 :                 generator->execute_data->prev_execute_data = original_execute_data;
     320             : 
     321             :                 /* Resume execution */
     322         223 :                 generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
     323         223 :                 zend_execute_ex(generator->execute_data TSRMLS_CC);
     324         216 :                 generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
     325             : 
     326             :                 /* Unlink generator call_frame from the caller */
     327         216 :                 if (generator->execute_data) {
     328         158 :                         generator->execute_data->prev_execute_data = NULL;
     329             :                 }
     330             : 
     331             :                 /* Restore executor globals */
     332         216 :                 EG(current_execute_data) = original_execute_data;
     333         216 :                 EG(scope) = original_scope;
     334         216 :                 EG(vm_stack_top) = original_stack->top;
     335         216 :                 EG(vm_stack_end) = original_stack->end;
     336         216 :                 EG(vm_stack) = original_stack;
     337             : 
     338             :                 /* If an exception was thrown in the generator we have to internally
     339             :                  * rethrow it in the parent scope. */
     340         216 :                 if (UNEXPECTED(EG(exception) != NULL)) {
     341           8 :                         zend_throw_exception_internal(NULL TSRMLS_CC);
     342             :                 }
     343             :         }
     344             : }
     345             : /* }}} */
     346             : 
     347         544 : static void zend_generator_ensure_initialized(zend_generator *generator TSRMLS_DC) /* {{{ */
     348             : {
     349        1049 :         if (generator->execute_data && Z_TYPE(generator->value) == IS_UNDEF) {
     350          79 :                 zend_generator_resume(generator TSRMLS_CC);
     351          75 :                 generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD;
     352             :         }
     353         540 : }
     354             : /* }}} */
     355             : 
     356          54 : static void zend_generator_rewind(zend_generator *generator TSRMLS_DC) /* {{{ */
     357             : {
     358          54 :         zend_generator_ensure_initialized(generator TSRMLS_CC);
     359             : 
     360          51 :         if (!(generator->flags & ZEND_GENERATOR_AT_FIRST_YIELD)) {
     361           1 :                 zend_throw_exception(NULL, "Cannot rewind a generator that was already run", 0 TSRMLS_CC);
     362             :         }
     363          51 : }
     364             : /* }}} */
     365             : 
     366             : /* {{{ proto void Generator::rewind()
     367             :  * Rewind the generator */
     368          16 : ZEND_METHOD(Generator, rewind)
     369             : {
     370             :         zend_generator *generator;
     371             : 
     372          16 :         if (zend_parse_parameters_none() == FAILURE) {
     373           0 :                 return;
     374             :         }
     375             : 
     376          16 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     377             : 
     378          16 :         zend_generator_rewind(generator TSRMLS_CC);
     379             : }
     380             : /* }}} */
     381             : 
     382             : /* {{{ proto bool Generator::valid()
     383             :  * Check whether the generator is valid */
     384          26 : ZEND_METHOD(Generator, valid)
     385             : {
     386             :         zend_generator *generator;
     387             : 
     388          26 :         if (zend_parse_parameters_none() == FAILURE) {
     389           0 :                 return;
     390             :         }
     391             : 
     392          26 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     393             : 
     394          26 :         zend_generator_ensure_initialized(generator TSRMLS_CC);
     395             : 
     396          52 :         RETURN_BOOL(Z_TYPE(generator->value) != IS_UNDEF);
     397             : }
     398             : /* }}} */
     399             : 
     400             : /* {{{ proto mixed Generator::current()
     401             :  * Get the current value */
     402          37 : ZEND_METHOD(Generator, current)
     403             : {
     404             :         zend_generator *generator;
     405             : 
     406          37 :         if (zend_parse_parameters_none() == FAILURE) {
     407           0 :                 return;
     408             :         }
     409             : 
     410          37 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     411             : 
     412          37 :         zend_generator_ensure_initialized(generator TSRMLS_CC);
     413             : 
     414          72 :         if (Z_TYPE(generator->value) != IS_UNDEF) {
     415         102 :                 RETURN_ZVAL_FAST(&generator->value);
     416             :         }
     417             : }
     418             : /* }}} */
     419             : 
     420             : /* {{{ proto mixed Generator::key()
     421             :  * Get the current key */
     422          16 : ZEND_METHOD(Generator, key)
     423             : {
     424             :         zend_generator *generator;
     425             : 
     426          16 :         if (zend_parse_parameters_none() == FAILURE) {
     427           0 :                 return;
     428             :         }
     429             : 
     430          16 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     431             : 
     432          16 :         zend_generator_ensure_initialized(generator TSRMLS_CC);
     433             : 
     434          32 :         if (Z_TYPE(generator->key) != IS_UNDEF) {
     435          48 :                 RETURN_ZVAL_FAST(&generator->key);
     436             :         }
     437             : }
     438             : /* }}} */
     439             : 
     440             : /* {{{ proto void Generator::next()
     441             :  * Advances the generator */
     442          15 : ZEND_METHOD(Generator, next)
     443             : {
     444             :         zend_generator *generator;
     445             : 
     446          15 :         if (zend_parse_parameters_none() == FAILURE) {
     447           0 :                 return;
     448             :         }
     449             : 
     450          15 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     451             : 
     452          15 :         zend_generator_ensure_initialized(generator TSRMLS_CC);
     453             : 
     454          15 :         zend_generator_resume(generator TSRMLS_CC);
     455             : }
     456             : /* }}} */
     457             : 
     458             : /* {{{ proto mixed Generator::send(mixed $value)
     459             :  * Sends a value to the generator */
     460          29 : ZEND_METHOD(Generator, send)
     461             : {
     462             :         zval *value;
     463             :         zend_generator *generator;
     464             : 
     465          29 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
     466           0 :                 return;
     467             :         }
     468             : 
     469          29 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     470             : 
     471          29 :         zend_generator_ensure_initialized(generator TSRMLS_CC); 
     472             : 
     473             :         /* The generator is already closed, thus can't send anything */
     474          29 :         if (!generator->execute_data) {
     475           1 :                 return;
     476             :         }
     477             : 
     478             :         /* Put sent value in the target VAR slot, if it is used */
     479          28 :         if (generator->send_target) {
     480          23 :                 if (Z_REFCOUNTED_P(generator->send_target)) Z_DELREF_P(generator->send_target);
     481          23 :                 ZVAL_COPY(generator->send_target, value);
     482             :         }
     483             : 
     484          28 :         zend_generator_resume(generator TSRMLS_CC);
     485             : 
     486          54 :         if (Z_TYPE(generator->value) != IS_UNDEF) {
     487          60 :                 RETURN_ZVAL_FAST(&generator->value);
     488             :         }
     489             : }
     490             : /* }}} */
     491             : 
     492             : /* {{{ proto mixed Generator::throw(Exception $exception)
     493             :  * Throws an exception into the generator */
     494           5 : ZEND_METHOD(Generator, throw)
     495             : {
     496             :         zval *exception, exception_copy;
     497             :         zend_generator *generator;
     498             : 
     499           5 :         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &exception) == FAILURE) {
     500           0 :                 return;
     501             :         }
     502             : 
     503           5 :         ZVAL_DUP(&exception_copy, exception);
     504             : 
     505           5 :         generator = (zend_generator *) Z_OBJ_P(getThis());
     506             : 
     507           5 :         zend_generator_ensure_initialized(generator TSRMLS_CC); 
     508             : 
     509           5 :         if (generator->execute_data) {
     510             :                 /* Throw the exception in the context of the generator */
     511           4 :                 zend_execute_data *current_execute_data = EG(current_execute_data);
     512           4 :                 EG(current_execute_data) = generator->execute_data;
     513             : 
     514           4 :                 zend_throw_exception_object(&exception_copy TSRMLS_CC);
     515             : 
     516           3 :                 EG(current_execute_data) = current_execute_data;
     517             : 
     518           3 :                 zend_generator_resume(generator TSRMLS_CC);
     519             : 
     520           6 :                 if (Z_TYPE(generator->value) != IS_UNDEF) {
     521           3 :                         RETURN_ZVAL_FAST(&generator->value);
     522             :                 }
     523             :         } else {
     524             :                 /* If the generator is already closed throw the exception in the
     525             :                  * current context */
     526           1 :                 zend_throw_exception_object(&exception_copy TSRMLS_CC);
     527             :         }
     528             : }
     529             : /* }}} */
     530             : 
     531             : /* {{{ proto void Generator::__wakeup()
     532             :  * Throws an Exception as generators can't be serialized */
     533           0 : ZEND_METHOD(Generator, __wakeup)
     534             : {
     535             :         /* Just specifying the zend_class_unserialize_deny handler is not enough,
     536             :          * because it is only invoked for C unserialization. For O the error has
     537             :          * to be thrown in __wakeup. */
     538             : 
     539           0 :         if (zend_parse_parameters_none() == FAILURE) {
     540           0 :                 return;
     541             :         }
     542             : 
     543           0 :         zend_throw_exception(NULL, "Unserialization of 'Generator' is not allowed", 0 TSRMLS_CC);
     544             : }
     545             : /* }}} */
     546             : 
     547             : /* get_iterator implementation */
     548             : 
     549          38 : static void zend_generator_iterator_dtor(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
     550             : {
     551          38 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
     552          38 :         generator->iterator = NULL;
     553          38 :         zval_ptr_dtor(&iterator->data);
     554          38 :         zend_iterator_dtor(iterator TSRMLS_CC);
     555          38 : }
     556             : /* }}} */
     557             : 
     558         129 : static int zend_generator_iterator_valid(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
     559             : {
     560         129 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
     561             : 
     562         129 :         zend_generator_ensure_initialized(generator TSRMLS_CC);
     563             : 
     564         258 :         return Z_TYPE(generator->value) != IS_UNDEF ? SUCCESS : FAILURE;
     565             : }
     566             : /* }}} */
     567             : 
     568          98 : static zval *zend_generator_iterator_get_data(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
     569             : {
     570          98 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
     571             : 
     572          98 :         zend_generator_ensure_initialized(generator TSRMLS_CC);
     573             : 
     574          98 :         return &generator->value;
     575             : }
     576             : /* }}} */
     577             : 
     578          38 : static void zend_generator_iterator_get_key(zend_object_iterator *iterator, zval *key TSRMLS_DC) /* {{{ */
     579             : {
     580          38 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
     581             : 
     582          38 :         zend_generator_ensure_initialized(generator TSRMLS_CC);
     583             : 
     584          76 :         if (Z_TYPE(generator->key) != IS_UNDEF) {
     585          76 :                 ZVAL_ZVAL(key, &generator->key, 1, 0);
     586             :         } else {
     587           0 :                 ZVAL_NULL(key);
     588             :         }
     589          38 : }
     590             : /* }}} */
     591             : 
     592          97 : static void zend_generator_iterator_move_forward(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
     593             : {
     594          97 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
     595             : 
     596          97 :         zend_generator_ensure_initialized(generator TSRMLS_CC);
     597             : 
     598          97 :         zend_generator_resume(generator TSRMLS_CC);
     599          96 : }
     600             : /* }}} */
     601             : 
     602          38 : static void zend_generator_iterator_rewind(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
     603             : {
     604          38 :         zend_generator *generator = (zend_generator*)Z_OBJ(iterator->data);
     605             : 
     606          38 :         zend_generator_rewind(generator TSRMLS_CC);
     607          37 : }
     608             : /* }}} */
     609             : 
     610             : static zend_object_iterator_funcs zend_generator_iterator_functions = {
     611             :         zend_generator_iterator_dtor,
     612             :         zend_generator_iterator_valid,
     613             :         zend_generator_iterator_get_data,
     614             :         zend_generator_iterator_get_key,
     615             :         zend_generator_iterator_move_forward,
     616             :         zend_generator_iterator_rewind
     617             : };
     618             : 
     619          40 : zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */
     620             : {
     621             :         zend_object_iterator *iterator;
     622          40 :         zend_generator *generator = (zend_generator*)Z_OBJ_P(object);
     623             : 
     624          40 :         if (!generator->execute_data) {
     625           1 :                 zend_throw_exception(NULL, "Cannot traverse an already closed generator", 0 TSRMLS_CC);
     626           1 :                 return NULL;
     627             :         }
     628             : 
     629          39 :         if (by_ref && !(generator->execute_data->func->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE)) {
     630           1 :                 zend_throw_exception(NULL, "You can only iterate a generator by-reference if it declared that it yields by-reference", 0 TSRMLS_CC);
     631           1 :                 return NULL;
     632             :         }
     633             : 
     634          38 :         iterator = generator->iterator = emalloc(sizeof(zend_object_iterator));
     635             :         
     636          38 :         zend_iterator_init(iterator TSRMLS_CC);
     637             : 
     638          38 :         iterator->funcs = &zend_generator_iterator_functions;
     639          38 :         ZVAL_COPY(&iterator->data, object);
     640             : 
     641          38 :         return iterator;
     642             : }
     643             : /* }}} */
     644             : 
     645             : ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0)
     646             : ZEND_END_ARG_INFO()
     647             : 
     648             : ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1)
     649             :         ZEND_ARG_INFO(0, value)
     650             : ZEND_END_ARG_INFO()
     651             : 
     652             : ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_throw, 0, 0, 1)
     653             :         ZEND_ARG_INFO(0, exception)
     654             : ZEND_END_ARG_INFO()
     655             : 
     656             : static const zend_function_entry generator_functions[] = {
     657             :         ZEND_ME(Generator, rewind,   arginfo_generator_void, ZEND_ACC_PUBLIC)
     658             :         ZEND_ME(Generator, valid,    arginfo_generator_void, ZEND_ACC_PUBLIC)
     659             :         ZEND_ME(Generator, current,  arginfo_generator_void, ZEND_ACC_PUBLIC)
     660             :         ZEND_ME(Generator, key,      arginfo_generator_void, ZEND_ACC_PUBLIC)
     661             :         ZEND_ME(Generator, next,     arginfo_generator_void, ZEND_ACC_PUBLIC)
     662             :         ZEND_ME(Generator, send,     arginfo_generator_send, ZEND_ACC_PUBLIC)
     663             :         ZEND_ME(Generator, throw,    arginfo_generator_throw, ZEND_ACC_PUBLIC)
     664             :         ZEND_ME(Generator, __wakeup, arginfo_generator_void, ZEND_ACC_PUBLIC)
     665             :         ZEND_FE_END
     666             : };
     667             : 
     668       20622 : void zend_register_generator_ce(TSRMLS_D) /* {{{ */
     669             : {
     670             :         zend_class_entry ce;
     671             : 
     672       20622 :         INIT_CLASS_ENTRY(ce, "Generator", generator_functions);
     673       20622 :         zend_ce_generator = zend_register_internal_class(&ce TSRMLS_CC);
     674       20622 :         zend_ce_generator->ce_flags |= ZEND_ACC_FINAL_CLASS;
     675       20622 :         zend_ce_generator->create_object = zend_generator_create;
     676       20622 :         zend_ce_generator->serialize = zend_class_serialize_deny;
     677       20622 :         zend_ce_generator->unserialize = zend_class_unserialize_deny;
     678             : 
     679             :         /* get_iterator has to be assigned *after* implementing the inferface */
     680       20622 :         zend_class_implements(zend_ce_generator TSRMLS_CC, 1, zend_ce_iterator);
     681       20622 :         zend_ce_generator->get_iterator = zend_generator_get_iterator;
     682       20622 :         zend_ce_generator->iterator_funcs.funcs = &zend_generator_iterator_functions;
     683             : 
     684       20622 :         memcpy(&zend_generator_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
     685       20622 :         zend_generator_handlers.free_obj = zend_generator_free_storage;
     686       20622 :         zend_generator_handlers.dtor_obj = zend_generator_dtor_storage;
     687       20622 :         zend_generator_handlers.clone_obj = NULL;
     688       20622 :         zend_generator_handlers.get_constructor = zend_generator_get_constructor;
     689       20622 : }
     690             : /* }}} */
     691             : 
     692             : /*
     693             :  * Local variables:
     694             :  * tab-width: 4
     695             :  * c-basic-offset: 4
     696             :  * indent-tabs-mode: t
     697             :  * End:
     698             :  */

Generated by: LCOV version 1.10

Generated at Sat, 13 Dec 2014 06:16:08 +0000 (6 days ago)

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