diff options
Diffstat (limited to 'Zend/zend_generators.c')
| -rw-r--r-- | Zend/zend_generators.c | 102 |
1 files changed, 98 insertions, 4 deletions
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 646e46b676..5a3e84ccd1 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -93,6 +93,12 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished OBJ_RELEASE((zend_object *) EX(func)->common.prototype); } + /* Free GC buffer. GC for closed generators doesn't need an allocated buffer */ + if (generator->gc_buffer) { + efree(generator->gc_buffer); + generator->gc_buffer = NULL; + } + efree(generator->stack); generator->execute_data = NULL; } @@ -190,12 +196,99 @@ static void zend_generator_free_storage(zend_object *object) /* {{{ */ } /* }}} */ +static uint32_t calc_gc_buffer_size(zend_generator *generator) /* {{{ */ +{ + uint32_t size = 4; /* value, key, retval, values */ + if (generator->execute_data) { + zend_execute_data *execute_data = generator->execute_data; + zend_op_array *op_array = &EX(func)->op_array; + + /* Compiled variables */ + if (!execute_data->symbol_table) { + size += op_array->last_var; + } + /* Extra args */ + if (EX_CALL_INFO() & ZEND_CALL_FREE_EXTRA_ARGS) { + size += EX_NUM_ARGS() - op_array->num_args; + } + size += Z_OBJ(execute_data->This) != NULL; /* $this */ + size += (EX_CALL_INFO() & ZEND_CALL_CLOSURE) != 0; /* Closure object */ + + /* Yield from root references */ + if (generator->node.children == 0) { + zend_generator *root = generator->node.ptr.root; + while (root != generator) { + size++; + root = zend_generator_get_child(&root->node, generator); + } + } + } + return size; +} +/* }}} */ + static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* {{{ */ { zend_generator *generator = (zend_generator*) Z_OBJ_P(object); - *table = &generator->value; - *n = 3; - return NULL; + zend_execute_data *execute_data = generator->execute_data; + zend_op_array *op_array; + zval *gc_buffer; + uint32_t gc_buffer_size; + + if (!execute_data) { + /* If the generator has been closed, it can only hold on to three values: The value, key + * and retval. These three zvals are stored sequentially starting at &generator->value. */ + *table = &generator->value; + *n = 3; + return NULL; + } + + op_array = &EX(func)->op_array; + gc_buffer_size = calc_gc_buffer_size(generator); + if (generator->gc_buffer_size < gc_buffer_size) { + generator->gc_buffer = safe_erealloc(generator->gc_buffer, sizeof(zval), gc_buffer_size, 0); + generator->gc_buffer_size = gc_buffer_size; + } + + *n = gc_buffer_size; + *table = gc_buffer = generator->gc_buffer; + + ZVAL_COPY_VALUE(gc_buffer++, &generator->value); + ZVAL_COPY_VALUE(gc_buffer++, &generator->key); + ZVAL_COPY_VALUE(gc_buffer++, &generator->retval); + ZVAL_COPY_VALUE(gc_buffer++, &generator->values); + + if (!execute_data->symbol_table) { + uint32_t i, num_cvs = EX(func)->op_array.last_var; + for (i = 0; i < num_cvs; i++) { + ZVAL_COPY_VALUE(gc_buffer++, EX_VAR_NUM(i)); + } + } + + if (EX_CALL_INFO() & ZEND_CALL_FREE_EXTRA_ARGS) { + zval *zv = EX_VAR_NUM(op_array->last_var + op_array->T); + zval *end = zv + (EX_NUM_ARGS() - op_array->num_args); + while (zv != end) { + ZVAL_COPY_VALUE(gc_buffer++, zv++); + } + } + + if (Z_OBJ(execute_data->This)) { + ZVAL_OBJ(gc_buffer++, Z_OBJ(execute_data->This)); + } + if (EX_CALL_INFO() & ZEND_CALL_CLOSURE) { + ZVAL_OBJ(gc_buffer++, (zend_object *) EX(func)->common.prototype); + } + + if (generator->node.children == 0) { + zend_generator *child = generator, *root = generator->node.ptr.root; + while (root != child) { + ZVAL_OBJ(gc_buffer++, &child->std); + child = child->node.parent; + } + } + + return execute_data->symbol_table; } /* }}} */ @@ -293,6 +386,8 @@ ZEND_API zend_execute_data *zend_generator_check_placeholder_frame(zend_execute_ static void zend_generator_throw_exception(zend_generator *generator, zval *exception) { + zend_execute_data *original_execute_data = EG(current_execute_data); + /* if we don't stop an array/iterator yield from, the exception will only reach the generator after the values were all iterated over */ if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) { zval_ptr_dtor(&generator->values); @@ -301,7 +396,6 @@ static void zend_generator_throw_exception(zend_generator *generator, zval *exce /* Throw the exception in the context of the generator. Decrementing the opline * to pretend the exception happened during the YIELD opcode. */ - zend_execute_data *original_execute_data = EG(current_execute_data); EG(current_execute_data) = generator->execute_data; generator->execute_data->opline--; if (exception) { |
