Skip to content

Instantly share code, notes, and snippets.

@dstogov
Created January 22, 2016 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dstogov/285024375d15cacf2a9b to your computer and use it in GitHub Desktop.
Save dstogov/285024375d15cacf2a9b to your computer and use it in GitHub Desktop.
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index fffcb9c..382a757 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -2426,59 +2426,12 @@ static zend_always_inline void i_init_execute_data(zend_execute_data *execute_da
ZEND_API zend_execute_data *zend_create_generator_execute_data(zend_execute_data *call, zend_op_array *op_array, zval *return_value) /* {{{ */
{
- /*
- * Normally the execute_data is allocated on the VM stack (because it does
- * not actually do any allocation and thus is faster). For generators
- * though this behavior would be suboptimal, because the (rather large)
- * structure would have to be copied back and forth every time execution is
- * suspended or resumed. That's why for generators the execution context
- * is allocated using a separate VM stack, thus allowing to save and
- * restore it simply by replacing a pointer.
- */
- zend_execute_data *execute_data;
- uint32_t num_args = ZEND_CALL_NUM_ARGS(call);
- size_t stack_size = (ZEND_CALL_FRAME_SLOT + MAX(op_array->last_var + op_array->T, num_args)) * sizeof(zval);
- uint32_t call_info;
-
- EG(vm_stack) = zend_vm_stack_new_page(
- EXPECTED(stack_size < ZEND_VM_STACK_FREE_PAGE_SIZE(1)) ?
- ZEND_VM_STACK_PAGE_SIZE(1) :
- ZEND_VM_STACK_PAGE_ALIGNED_SIZE(1, stack_size),
- NULL);
- EG(vm_stack_top) = EG(vm_stack)->top;
- EG(vm_stack_end) = EG(vm_stack)->end;
-
- call_info = ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED | (ZEND_CALL_INFO(call) & (ZEND_CALL_CLOSURE|ZEND_CALL_RELEASE_THIS));
- if (Z_OBJ(call->This)) {
- call_info |= ZEND_CALL_RELEASE_THIS;
- }
- execute_data = zend_vm_stack_push_call_frame(
- call_info,
- (zend_function*)op_array,
- num_args,
- call->called_scope,
- Z_OBJ(call->This));
- EX(prev_execute_data) = NULL;
- EX_NUM_ARGS() = num_args;
-
- /* copy arguments */
- if (num_args > 0) {
- zval *arg_src = ZEND_CALL_ARG(call, 1);
- zval *arg_dst = ZEND_CALL_ARG(execute_data, 1);
- zval *end = arg_src + num_args;
-
- do {
- ZVAL_COPY_VALUE(arg_dst, arg_src);
- arg_src++;
- arg_dst++;
- } while (arg_src != end);
- }
-
- EX(symbol_table) = NULL;
+ call->prev_execute_data = NULL;
+ call->symbol_table = NULL;
- i_init_func_execute_data(execute_data, op_array, return_value, 1);
+ i_init_func_execute_data(call, op_array, return_value, 1);
- return execute_data;
+ return call;
}
/* }}} */
diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h
index 00c651f..b869cc3 100644
--- a/Zend/zend_execute.h
+++ b/Zend/zend_execute.h
@@ -173,7 +173,10 @@ static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(ui
ZEND_ASSERT_VM_STACK_GLOBAL;
- if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
+ if (UNEXPECTED(func->common.fn_flags & ZEND_ACC_GENERATOR)) {
+ call = (zend_execute_data*)emalloc(used_stack);
+ ZEND_SET_CALL_INFO(call, call_info | ZEND_CALL_TOP_FUNCTION | ZEND_CALL_ALLOCATED);
+ } else if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) {
call = (zend_execute_data*)zend_vm_stack_extend(used_stack);
ZEND_SET_CALL_INFO(call, call_info | ZEND_CALL_ALLOCATED);
} else {
@@ -259,6 +262,13 @@ static zend_always_inline void zend_vm_stack_free_call_frame_ex(uint32_t call_in
ZEND_ASSERT_VM_STACK_GLOBAL;
if (UNEXPECTED(call_info & ZEND_CALL_ALLOCATED)) {
+ if (!call->func) {
+ /* Generator with unused return value */
+ efree(call);
+ return;
+ } else if (call->func->common.fn_flags & ZEND_ACC_GENERATOR) {
+ return;
+ }
zend_vm_stack p = EG(vm_stack);
zend_vm_stack prev = p->prev;
@@ -267,7 +277,6 @@ static zend_always_inline void zend_vm_stack_free_call_frame_ex(uint32_t call_in
EG(vm_stack_end) = prev->end;
EG(vm_stack) = prev;
efree(p);
-
} else {
EG(vm_stack_top) = (zval*)call;
}
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
index 4861e45..7d45059 100644
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -44,21 +44,7 @@ static void zend_generator_cleanup_unfinished_execution(zend_generator *generato
/* -1 required because we want the last run opcode, not the next to-be-run one. */
uint32_t op_num = execute_data->opline - execute_data->func->op_array.opcodes - 1;
- /* There may be calls to zend_vm_stack_free_call_frame(), which modifies the VM stack
- * globals, so need to load/restore those. */
- zend_vm_stack original_stack = EG(vm_stack);
- original_stack->top = EG(vm_stack_top);
- EG(vm_stack_top) = generator->stack->top;
- EG(vm_stack_end) = generator->stack->end;
- EG(vm_stack) = generator->stack;
-
zend_cleanup_unfinished_execution(execute_data, op_num, 0);
-
- generator->stack = EG(vm_stack);
- generator->stack->top = EG(vm_stack_top);
- EG(vm_stack_top) = original_stack->top;
- EG(vm_stack_end) = original_stack->end;
- EG(vm_stack) = original_stack;
}
}
/* }}} */
@@ -103,7 +89,7 @@ ZEND_API void zend_generator_close(zend_generator *generator, zend_bool finished
OBJ_RELEASE((zend_object *) EX(func)->common.prototype);
}
- efree(generator->stack);
+ efree(generator->execute_data);
generator->execute_data = NULL;
}
}
@@ -223,14 +209,10 @@ ZEND_API void zend_generator_create_zval(zend_execute_data *call, zend_op_array
{
zend_generator *generator;
zend_execute_data *current_execute_data;
- zend_execute_data *execute_data;
- zend_vm_stack current_stack = EG(vm_stack);
-
- current_stack->top = EG(vm_stack_top);
/* Create new execution context. We have to back up and restore EG(current_execute_data) here. */
current_execute_data = EG(current_execute_data);
- execute_data = zend_create_generator_execute_data(call, op_array, return_value);
+ zend_create_generator_execute_data(call, op_array, return_value);
EG(current_execute_data) = current_execute_data;
object_init_ex(return_value, zend_ce_generator);
@@ -241,15 +223,10 @@ ZEND_API void zend_generator_create_zval(zend_execute_data *call, zend_op_array
/* Save execution context in generator object. */
generator = (zend_generator *) Z_OBJ_P(return_value);
- generator->execute_data = execute_data;
- generator->stack = EG(vm_stack);
- generator->stack->top = EG(vm_stack_top);
- EG(vm_stack_top) = current_stack->top;
- EG(vm_stack_end) = current_stack->end;
- EG(vm_stack) = current_stack;
+ generator->execute_data = call;
/* EX(return_value) keeps pointer to zend_object (not a real zval) */
- execute_data->return_value = (zval*)generator;
+ call->return_value = (zval*)generator;
memset(&generator->execute_fake, 0, sizeof(zend_execute_data));
Z_OBJ(generator->execute_fake.This) = (zend_object *) generator;
@@ -657,15 +634,10 @@ try_again:
/* Backup executor globals */
zend_execute_data *original_execute_data = EG(current_execute_data);
zend_class_entry *original_scope = EG(scope);
- zend_vm_stack original_stack = EG(vm_stack);
- original_stack->top = EG(vm_stack_top);
/* Set executor globals */
EG(current_execute_data) = generator->execute_data;
EG(scope) = generator->execute_data->func->common.scope;
- EG(vm_stack_top) = generator->stack->top;
- EG(vm_stack_end) = generator->stack->end;
- EG(vm_stack) = generator->stack;
/* We want the backtrace to look as if the generator function was
* called from whatever method we are current running (e.g. next()).
@@ -684,18 +656,9 @@ try_again:
zend_execute_ex(generator->execute_data);
generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
- /* Unlink generator call_frame from the caller and backup vm_stack_top */
- if (EXPECTED(generator->execute_data)) {
- generator->stack = EG(vm_stack);
- generator->stack->top = EG(vm_stack_top);
- }
-
/* Restore executor globals */
EG(current_execute_data) = original_execute_data;
EG(scope) = original_scope;
- EG(vm_stack_top) = original_stack->top;
- EG(vm_stack_end) = original_stack->end;
- EG(vm_stack) = original_stack;
/* If an exception was thrown in the generator we have to internally
* rethrow it in the parent scope.
diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h
index 26ee646..2fc71f5 100644
--- a/Zend/zend_generators.h
+++ b/Zend/zend_generators.h
@@ -62,9 +62,6 @@ struct _zend_generator {
/* The suspended execution context. */
zend_execute_data *execute_data;
- /* The separate stack used by generator */
- zend_vm_stack stack;
-
/* Current value */
zval value;
/* Current key */
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 86ced99..c90c2bb 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -3681,6 +3681,7 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, ANY, ANY)
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE((zend_object*)fbc->op_array.prototype);
}
+ call->func = NULL;
zend_vm_stack_free_args(call);
}
} else {
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 080c799..6ba1c49 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -779,6 +779,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_HANDLER(ZEND_OPC
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_CLOSURE)) {
OBJ_RELEASE((zend_object*)fbc->op_array.prototype);
}
+ call->func = NULL;
zend_vm_stack_free_args(call);
}
} else {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment