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: 268 287 93.4 %
Date: 2015-03-01 Functions: 26 27 96.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Sun, 01 Mar 2015 23:22:21 +0000 (2 days ago)

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