. The VM stacks for passing function arguments and syntaticaly nested calls were merged into a single stack. The stack size needed for op_array execution is calculated at compile time and preallocated at once. As result all the stack push operatins don't require checks for stack overflow any more.

. Generators implementation was improved using the new VM stack. Now it's a bit more clear and faster.
This commit is contained in:
Dmitry Stogov 2012-11-30 13:39:23 +04:00
parent 9f7e53fde8
commit 70f83f35d0
20 changed files with 1477 additions and 1307 deletions

5
NEWS
View file

@ -3,6 +3,11 @@ PHP NEWS
?? ??? 201?, PHP 5.5.0
- General improvements:
. The VM stacks for passing function arguments and syntaticaly nested calls
were merged into a single stack. The stack size needed for op_array
execution is calculated at compile time and preallocated at once. As result
all the stack push operatins don't require checks for stack overflow
any more. (Dmitry)
. Added support for generators. (Nikita Popov)
. Add simplified password hashing API
(https://wiki.php.net/rfc/password_hash). (Anthony Ferrara)

View file

@ -28,6 +28,27 @@ PHP 5.5 UPGRADE NOTES
zend_logo_guid() have been removed
- Removal of Logo GUIDs
- extensions can't override zend_execute() any more, they should override
zend_execute_ex() instead. The EG(current_execute_data) is already
initialized in zend_execute_ex(), so for compatibility extensions
may need to use EG(current_execute_data)->prev_execute_data instead.
- removed EG(arg_types_stack), EX(fbc), EX(called_scope), EX(current_object)
- added op_array->nested_calls. It's calculated at compile time.
- added EX(call_slots). It is an array to store information about syntaticaly
nested calls (e.g. foo(bar())). It's preallocated together with execute_data.
- added EX(call) - pointer to a current calling function. Actually an
element of EX(call_slots)
- opcodes INIT_METHOD_CALL, ZEND_INIT_STATIC_METHOD_CALL,
ZEND_INIT_FCALL_BY_NAME, ZEND_INIT_NS_FCALL_BY_NAME use result.num as
an index in EX(call_slots)
- opcode ZEND_NEW uses extended_vallue as an index in EX(call_slots)
- opcoes ZEND_DO_FCALL and ZEND_DO_FCALL_BY_NAME use op2.num as
an index in EX(call_slots)
- added op_array->used_stack. It's calculated at compile time and the
corresponding stack space is preallocated together with execute_data.
ZEND_SEND* and ZEND_DO_FCALL* don't need to check for stack overflow
anymore.
========================================
2. New Features
========================================

View file

@ -683,11 +683,11 @@ int zend_startup(zend_utility_functions *utility_functions, char **extensions TS
#if HAVE_DTRACE
/* build with dtrace support */
zend_compile_file = dtrace_compile_file;
zend_execute = dtrace_execute;
zend_execute_ex = dtrace_execute_ex;
zend_execute_internal = dtrace_execute_internal;
#else
zend_compile_file = compile_file;
zend_execute = execute;
zend_execute_ex = execute_ex;
zend_execute_internal = NULL;
#endif /* HAVE_SYS_SDT_H */
zend_compile_string = compile_string;

View file

@ -179,6 +179,8 @@ void zend_init_compiler_context(TSRMLS_D) /* {{{ */
CG(context).literals_size = 0;
CG(context).current_brk_cont = -1;
CG(context).backpatch_count = 0;
CG(context).nested_calls = 0;
CG(context).used_stack = 0;
CG(context).labels = NULL;
}
/* }}} */
@ -1950,6 +1952,9 @@ int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace
function_name->u.constant.value.str.val = lcname;
zend_stack_push(&CG(function_call_stack), (void *) &function, sizeof(zend_function *));
if (CG(context).nested_calls + 1 > CG(active_op_array)->nested_calls) {
CG(active_op_array)->nested_calls = CG(context).nested_calls + 1;
}
zend_do_extended_fcall_begin(TSRMLS_C);
return 0;
}
@ -1988,11 +1993,13 @@ void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC) /* {{{ */
GET_POLYMORPHIC_CACHE_SLOT(last_op->op2.constant);
}
last_op->opcode = ZEND_INIT_METHOD_CALL;
SET_UNUSED(last_op->result);
last_op->result_type = IS_UNUSED;
last_op->result.num = CG(context).nested_calls;
Z_LVAL(left_bracket->u.constant) = ZEND_INIT_FCALL_BY_NAME;
} else {
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
opline->opcode = ZEND_INIT_FCALL_BY_NAME;
opline->result.num = CG(context).nested_calls;
SET_UNUSED(opline->op1);
if (left_bracket->op_type == IS_CONST) {
opline->op2_type = IS_CONST;
@ -2004,6 +2011,9 @@ void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC) /* {{{ */
}
zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(zend_function *));
if (++CG(context).nested_calls > CG(active_op_array)->nested_calls) {
CG(active_op_array)->nested_calls = CG(context).nested_calls;
}
zend_do_extended_fcall_begin(TSRMLS_C);
}
/* }}} */
@ -2031,12 +2041,14 @@ void zend_do_begin_dynamic_function_call(znode *function_name, int ns_call TSRML
/* In run-time PHP will check for function with full name and
internal function with short name */
opline->opcode = ZEND_INIT_NS_FCALL_BY_NAME;
opline->result.num = CG(context).nested_calls;
SET_UNUSED(opline->op1);
opline->op2_type = IS_CONST;
opline->op2.constant = zend_add_ns_func_name_literal(CG(active_op_array), &function_name->u.constant TSRMLS_CC);
GET_CACHE_SLOT(opline->op2.constant);
} else {
opline->opcode = ZEND_INIT_FCALL_BY_NAME;
opline->result.num = CG(context).nested_calls;
SET_UNUSED(opline->op1);
if (function_name->op_type == IS_CONST) {
opline->op2_type = IS_CONST;
@ -2048,6 +2060,9 @@ void zend_do_begin_dynamic_function_call(znode *function_name, int ns_call TSRML
}
zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(zend_function *));
if (++CG(context).nested_calls > CG(active_op_array)->nested_calls) {
CG(active_op_array)->nested_calls = CG(context).nested_calls;
}
zend_do_extended_fcall_begin(TSRMLS_C);
}
/* }}} */
@ -2395,6 +2410,7 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na
opline->extended_value = class_node.EA ;
}
opline->opcode = ZEND_INIT_STATIC_METHOD_CALL;
opline->result.num = CG(context).nested_calls;
if (class_node.op_type == IS_CONST) {
opline->op1_type = IS_CONST;
opline->op1.constant =
@ -2416,6 +2432,9 @@ int zend_do_begin_class_member_function_call(znode *class_name, znode *method_na
}
zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(zend_function *));
if (++CG(context).nested_calls > CG(active_op_array)->nested_calls) {
CG(active_op_array)->nested_calls = CG(context).nested_calls;
}
zend_do_extended_fcall_begin(TSRMLS_C);
return 1; /* Dynamic */
}
@ -2436,21 +2455,29 @@ void zend_do_end_function_call(znode *function_name, znode *result, const znode
if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) {
opline->opcode = ZEND_DO_FCALL;
SET_NODE(opline->op1, function_name);
SET_UNUSED(opline->op2);
opline->op2.num = CG(context).nested_calls;
CALCULATE_LITERAL_HASH(opline->op1.constant);
GET_CACHE_SLOT(opline->op1.constant);
} else {
opline->opcode = ZEND_DO_FCALL_BY_NAME;
SET_UNUSED(opline->op1);
SET_UNUSED(opline->op2);
opline->op2.num = --CG(context).nested_calls;
}
}
opline->result.var = get_temporary_variable(CG(active_op_array));
opline->result_type = IS_VAR;
GET_NODE(result, opline->result) ;
SET_UNUSED(opline->op2);
GET_NODE(result, opline->result);
zend_stack_del_top(&CG(function_call_stack));
opline->extended_value = Z_LVAL(argument_list->u.constant);
if (CG(context).used_stack + 1 > CG(active_op_array)->used_stack) {
CG(active_op_array)->used_stack = CG(context).used_stack + 1;
}
CG(context).used_stack -= Z_LVAL(argument_list->u.constant);
}
/* }}} */
@ -2558,6 +2585,10 @@ void zend_do_pass_param(znode *param, zend_uchar op, int offset TSRMLS_DC) /* {{
SET_NODE(opline->op1, param);
opline->op2.opline_num = offset;
SET_UNUSED(opline->op2);
if (++CG(context).used_stack > CG(active_op_array)->used_stack) {
CG(active_op_array)->used_stack = CG(context).used_stack;
}
}
/* }}} */
@ -5547,12 +5578,16 @@ void zend_do_begin_new_object(znode *new_token, znode *class_type TSRMLS_DC) /*
new_token->u.op.opline_num = get_next_op_number(CG(active_op_array));
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
opline->opcode = ZEND_NEW;
opline->extended_value = CG(context).nested_calls;
opline->result_type = IS_VAR;
opline->result.var = get_temporary_variable(CG(active_op_array));
SET_NODE(opline->op1, class_type);
SET_UNUSED(opline->op2);
zend_stack_push(&CG(function_call_stack), (void *) &ptr, sizeof(unsigned char *));
if (++CG(context).nested_calls > CG(active_op_array)->nested_calls) {
CG(active_op_array)->nested_calls = CG(context).nested_calls;
}
}
/* }}} */
@ -5765,6 +5800,13 @@ void zend_do_shell_exec(znode *result, const znode *cmd TSRMLS_DC) /* {{{ */
opline->extended_value = 1;
SET_UNUSED(opline->op2);
GET_NODE(result, opline->result);
if (CG(context).nested_calls + 1 > CG(active_op_array)->nested_calls) {
CG(active_op_array)->nested_calls = CG(context).nested_calls + 1;
}
if (CG(context).used_stack + 2 > CG(active_op_array)->used_stack) {
CG(active_op_array)->used_stack = CG(context).used_stack + 2;
}
}
/* }}} */

View file

@ -59,6 +59,8 @@ typedef struct _zend_compiler_context {
int literals_size;
int current_brk_cont;
int backpatch_count;
int nested_calls;
int used_stack;
HashTable *labels;
} zend_compiler_context;
@ -279,6 +281,9 @@ struct _zend_op_array {
zend_uint T;
zend_uint nested_calls;
zend_uint used_stack;
zend_brk_cont_element *brk_cont_array;
int last_brk_cont;
@ -369,11 +374,17 @@ typedef struct _list_llist_element {
union _temp_variable;
typedef struct _call_slot {
zend_function *fbc;
zval *object;
zend_class_entry *called_scope;
zend_bool is_ctor_call;
zend_bool is_ctor_result_used;
} call_slot;
struct _zend_execute_data {
struct _zend_op *opline;
zend_function_state function_state;
zend_function *fbc; /* Function Being Called */
zend_class_entry *called_scope;
zend_op_array *op_array;
zval *object;
union _temp_variable *Ts;
@ -386,8 +397,9 @@ struct _zend_execute_data {
zend_class_entry *current_scope;
zend_class_entry *current_called_scope;
zval *current_this;
zval *current_object;
struct _zend_op *fast_ret; /* used by FAST_CALL/FAST_RET (finally keyword) */
call_slot *call_slots;
call_slot *call;
};
#define EX(element) execute_data.element

View file

@ -1570,25 +1570,6 @@ void zend_free_compiled_variables(zval ***CVs, int num) /* {{{ */
}
/* }}} */
void** zend_copy_arguments(void **arguments_end) /* {{{ */
{
int arguments_count = (int) (zend_uintptr_t) *arguments_end;
size_t arguments_size = (arguments_count + 1) * sizeof(void **);
void **arguments_start = arguments_end - arguments_count;
void **copied_arguments_start = emalloc(arguments_size);
void **copied_arguments_end = copied_arguments_start + arguments_count;
int i;
memcpy(copied_arguments_start, arguments_start, arguments_size);
for (i = 0; i < arguments_count; i++) {
Z_ADDREF_P((zval *) arguments_start[i]);
}
return copied_arguments_end;
}
/* }}} */
/*
* Local variables:
* tab-width: 4

View file

@ -50,14 +50,14 @@ typedef union _temp_variable {
BEGIN_EXTERN_C()
struct _zend_fcall_info;
ZEND_API extern void (*zend_execute)(zend_op_array *op_array TSRMLS_DC);
ZEND_API extern void (*zend_execute_ex)(zend_execute_data *execute_data TSRMLS_DC);
ZEND_API extern void (*zend_execute_internal)(zend_execute_data *execute_data_ptr, struct _zend_fcall_info *fci, int return_value_used TSRMLS_DC);
void init_executor(TSRMLS_D);
void shutdown_executor(TSRMLS_D);
void shutdown_destructors(TSRMLS_D);
zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_array, zend_bool nested TSRMLS_DC);
ZEND_API void execute(zend_op_array *op_array TSRMLS_DC);
ZEND_API void zend_execute(zend_op_array *op_array TSRMLS_DC);
ZEND_API void execute_ex(zend_execute_data *execute_data TSRMLS_DC);
ZEND_API void execute_internal(zend_execute_data *execute_data_ptr, struct _zend_fcall_info *fci, int return_value_used TSRMLS_DC);
ZEND_API int zend_is_true(zval *op);
@ -221,12 +221,6 @@ static zend_always_inline void **zend_vm_stack_top(TSRMLS_D)
}
static zend_always_inline void zend_vm_stack_push(void *ptr TSRMLS_DC)
{
ZEND_VM_STACK_GROW_IF_NEEDED(1);
*(EG(argument_stack)->top++) = ptr;
}
static zend_always_inline void zend_vm_stack_push_nocheck(void *ptr TSRMLS_DC)
{
*(EG(argument_stack)->top++) = ptr;
}
@ -235,11 +229,6 @@ static zend_always_inline void *zend_vm_stack_pop(TSRMLS_D)
{
void *el = *(--EG(argument_stack)->top);
if (UNEXPECTED(EG(argument_stack)->top == ZEND_VM_STACK_ELEMETS(EG(argument_stack)))) {
zend_vm_stack p = EG(argument_stack);
EG(argument_stack) = p->prev;
efree(p);
}
return el;
}
@ -272,6 +261,12 @@ static zend_always_inline void *zend_vm_stack_alloc(size_t size TSRMLS_DC)
return ret;
}
static zend_always_inline void** zend_vm_stack_frame_base(zend_execute_data *ex)
{
return (void**)((char*)ex->call_slots +
ZEND_MM_ALIGNED_SIZE(sizeof(call_slot)) * ex->op_array->nested_calls);
}
static zend_always_inline void zend_vm_stack_free_int(void *ptr TSRMLS_DC)
{
if (UNEXPECTED(ZEND_VM_STACK_ELEMETS(EG(argument_stack)) == (void**)ptr)) {
@ -302,35 +297,6 @@ static zend_always_inline void zend_vm_stack_free(void *ptr TSRMLS_DC)
}
}
static zend_always_inline void** zend_vm_stack_push_args(int count TSRMLS_DC)
{
if (UNEXPECTED(EG(argument_stack)->top - ZEND_VM_STACK_ELEMETS(EG(argument_stack)) < count) ||
UNEXPECTED(EG(argument_stack)->top == EG(argument_stack)->end)) {
zend_vm_stack p = EG(argument_stack);
zend_vm_stack_extend(count + 1 TSRMLS_CC);
EG(argument_stack)->top += count;
*(EG(argument_stack)->top) = (void*)(zend_uintptr_t)count;
while (count-- > 0) {
void *data = *(--p->top);
if (UNEXPECTED(p->top == ZEND_VM_STACK_ELEMETS(p))) {
zend_vm_stack r = p;
EG(argument_stack)->prev = p->prev;
p = p->prev;
efree(r);
}
*(ZEND_VM_STACK_ELEMETS(EG(argument_stack)) + count) = data;
}
return EG(argument_stack)->top++;
}
*(EG(argument_stack)->top) = (void*)(zend_uintptr_t)count;
return EG(argument_stack)->top++;
}
static zend_always_inline void zend_vm_stack_clear_multiple(TSRMLS_D)
{
void **p = EG(argument_stack)->top - 1;
@ -344,9 +310,19 @@ static zend_always_inline void zend_vm_stack_clear_multiple(TSRMLS_D)
zend_vm_stack_free_int(p TSRMLS_CC);
}
static zend_always_inline zval** zend_vm_stack_get_arg(int requested_arg TSRMLS_DC)
static zend_always_inline int zend_vm_stack_get_args_count_ex(zend_execute_data *ex)
{
void **p = EG(current_execute_data)->prev_execute_data->function_state.arguments;
if (ex) {
void **p = ex->function_state.arguments;
return (int)(zend_uintptr_t) *p;
} else {
return 0;
}
}
static zend_always_inline zval** zend_vm_stack_get_arg_ex(zend_execute_data *ex, int requested_arg)
{
void **p = ex->function_state.arguments;
int arg_count = (int)(zend_uintptr_t) *p;
if (UNEXPECTED(requested_arg > arg_count)) {
@ -355,25 +331,14 @@ static zend_always_inline zval** zend_vm_stack_get_arg(int requested_arg TSRMLS_
return (zval**)p - arg_count + requested_arg - 1;
}
static zend_always_inline void zend_arg_types_stack_2_pop(zend_ptr_stack *stack, zval **object, zend_function **fbc)
static zend_always_inline int zend_vm_stack_get_args_count(TSRMLS_D)
{
void *a, *b;
zend_ptr_stack_2_pop(stack, &a, &b);
*object = (zval *) a;
*fbc = (zend_function *) b;
return zend_vm_stack_get_args_count_ex(EG(current_execute_data)->prev_execute_data);
}
static zend_always_inline void zend_arg_types_stack_3_pop(zend_ptr_stack *stack, zend_class_entry **called_scope, zval **object, zend_function **fbc)
static zend_always_inline zval** zend_vm_stack_get_arg(int requested_arg TSRMLS_DC)
{
void *a, *b, *c;
zend_ptr_stack_3_pop(stack, &a, &b, &c);
*called_scope = (zend_class_entry *) a;
*object = (zval *) b;
*fbc = (zend_function *) c;
return zend_vm_stack_get_arg_ex(EG(current_execute_data)->prev_execute_data, requested_arg);
}
void execute_new_code(TSRMLS_D);
@ -436,7 +401,6 @@ ZEND_API int zend_do_fcall(ZEND_OPCODE_HANDLER_ARGS);
void zend_clean_and_cache_symbol_table(HashTable *symbol_table TSRMLS_DC);
void zend_free_compiled_variables(zval ***CVs, int num);
void **zend_copy_arguments(void **arguments_end);
#define CACHED_PTR(num) \
EG(active_op_array)->run_time_cache[(num)]

View file

@ -38,7 +38,7 @@
#include <sys/time.h>
#endif
ZEND_API void (*zend_execute)(zend_op_array *op_array TSRMLS_DC);
ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data TSRMLS_DC);
ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data_ptr, zend_fcall_info *fci, int return_value_used TSRMLS_DC);
/* true globals */
@ -137,7 +137,6 @@ void init_executor(TSRMLS_D) /* {{{ */
INIT_ZVAL(EG(error_zval));
EG(uninitialized_zval_ptr)=&EG(uninitialized_zval);
EG(error_zval_ptr)=&EG(error_zval);
zend_ptr_stack_init(&EG(arg_types_stack));
/* destroys stack frame, therefore makes core dumps worthless */
#if 0&&ZEND_DEBUG
original_sigsegv_handler = signal(SIGSEGV, zend_handle_sigsegv);
@ -293,10 +292,10 @@ void shutdown_executor(TSRMLS_D) /* {{{ */
} zend_end_try();
zend_try {
zend_vm_stack_destroy(TSRMLS_C);
zend_objects_store_free_object_storage(&EG(objects_store) TSRMLS_CC);
zend_vm_stack_destroy(TSRMLS_C);
/* Destroy all op arrays */
if (EG(full_tables_cleanup)) {
zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC);
@ -324,7 +323,6 @@ void shutdown_executor(TSRMLS_D) /* {{{ */
zend_hash_destroy(&EG(included_files));
zend_ptr_stack_destroy(&EG(arg_types_stack));
zend_stack_destroy(&EG(user_error_handlers_error_reporting));
zend_ptr_stack_destroy(&EG(user_error_handlers));
zend_ptr_stack_destroy(&EG(user_exception_handlers));
@ -862,7 +860,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS
!ARG_MAY_BE_SENT_BY_REF(EX(function_state).function, i + 1)) {
if (i || UNEXPECTED(ZEND_VM_STACK_ELEMETS(EG(argument_stack)) == (EG(argument_stack)->top))) {
/* hack to clean up the stack */
zend_vm_stack_push_nocheck((void *) (zend_uintptr_t)i TSRMLS_CC);
zend_vm_stack_push((void *) (zend_uintptr_t)i TSRMLS_CC);
zend_vm_stack_clear_multiple(TSRMLS_C);
}
@ -899,11 +897,11 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache TS
*param = **(fci->params[i]);
INIT_PZVAL(param);
}
zend_vm_stack_push_nocheck(param TSRMLS_CC);
zend_vm_stack_push(param TSRMLS_CC);
}
EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C);
zend_vm_stack_push_nocheck((void*)(zend_uintptr_t)fci->param_count TSRMLS_CC);
zend_vm_stack_push((void*)(zend_uintptr_t)fci->param_count TSRMLS_CC);
current_scope = EG(scope);
EG(scope) = calling_scope;

View file

@ -28,7 +28,7 @@
/* The first number is the engine version and the rest is the date.
* This way engine 2/3 API no. is always greater than engine 1 API no..
*/
#define ZEND_EXTENSION_API_NO 220121113
#define ZEND_EXTENSION_API_NO 220121128
typedef struct _zend_extension_version_info {
int zend_extension_api_no;

View file

@ -32,41 +32,7 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio
if (generator->execute_data) {
zend_execute_data *execute_data = generator->execute_data;
zend_op_array *op_array = execute_data->op_array;
if (!finished_execution) {
if (op_array->has_finally_block) {
/* -1 required because we want the last run opcode, not the
* next to-be-run one. */
zend_uint op_num = execute_data->opline - op_array->opcodes - 1;
zend_uint finally_op_num = 0;
/* Find next finally block */
int i;
for (i = 0; i < op_array->last_try_catch; i++) {
zend_try_catch_element *try_catch = &op_array->try_catch_array[i];
if (op_num < try_catch->try_op) {
break;
}
if (op_num < try_catch->finally_op) {
finally_op_num = try_catch->finally_op;
}
}
/* If a finally block was found we jump directly to it and
* resume the generator. Furthermore we abort this close call
* because the generator will already be closed somewhere in
* the resume. */
if (finally_op_num) {
execute_data->opline = &op_array->opcodes[finally_op_num];
execute_data->fast_ret = NULL;
generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
zend_generator_resume(generator TSRMLS_CC);
return;
}
}
}
void **stack_frame;
if (!execute_data->symbol_table) {
zend_free_compiled_variables(execute_data->CVs, op_array->last_var);
@ -78,10 +44,6 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio
zval_ptr_dtor(&execute_data->current_this);
}
if (execute_data->object) {
zval_ptr_dtor(&execute_data->object);
}
/* If the generator is closed before it can finish execution (reach
* a return statement) we have to free loop variables manually, as
* we don't know whether the SWITCH_FREE / FREE opcodes have run */
@ -120,31 +82,19 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio
}
/* Clear any backed up stack arguments */
if (generator->backed_up_stack) {
zval **zvals = (zval **) generator->backed_up_stack;
size_t zval_num = generator->backed_up_stack_size / sizeof(zval *);
int i;
for (i = 0; i < zval_num; i++) {
zval_ptr_dtor(&zvals[i]);
if (generator->stack != EG(argument_stack)) {
stack_frame = zend_vm_stack_frame_base(execute_data);
while (generator->stack->top != stack_frame) {
zval_ptr_dtor((zval**)stack_frame);
stack_frame++;
}
efree(generator->backed_up_stack);
}
if (generator->backed_up_arg_types_stack) {
/* The arg types stack contains three elements per call: fbc, object
* and called_scope. Here we traverse the stack from top to bottom
* and dtor the object. */
int i = generator->backed_up_arg_types_stack_count / 3;
while (i--) {
zval *object = (zval *) generator->backed_up_arg_types_stack[3*i + 1];
if (object) {
zval_ptr_dtor(&object);
}
while (execute_data->call >= execute_data->call_slots) {
if (execute_data->call->object) {
zval_ptr_dtor(&execute_data->call->object);
}
efree(generator->backed_up_arg_types_stack);
execute_data->call--;
}
/* We have added an additional stack frame in prev_execute_data, so we
@ -162,11 +112,7 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio
for (i = 0; i < arguments_count; ++i) {
zval_ptr_dtor(arguments_start + i);
}
efree(arguments_start);
}
efree(prev_execute_data);
}
/* Free a clone of closure */
@ -175,7 +121,11 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio
efree(op_array);
}
efree(execute_data);
efree(generator->stack);
if (generator->stack == EG(argument_stack)) {
/* abnormal exit for running generator */
EG(argument_stack) = NULL;
}
generator->execute_data = NULL;
}
@ -210,26 +160,38 @@ static void zend_generator_clone_storage(zend_generator *orig, zend_generator **
zend_execute_data *execute_data = orig->execute_data;
zend_op_array *op_array = execute_data->op_array;
HashTable *symbol_table = execute_data->symbol_table;
zend_execute_data *current_execute_data;
zend_op **opline_ptr;
HashTable *current_symbol_table;
zend_vm_stack current_stack;
zval *current_this;
void **stack_frame, **orig_stack_frame;
/* Alloc separate execution context, as well as separate sections for
* compiled variables and temporary variables */
size_t execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data));
size_t CVs_size = ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (symbol_table ? 1 : 2));
size_t Ts_size = ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T;
size_t total_size = execute_data_size + CVs_size + Ts_size;
/* Create new execution context. We have to back up and restore
* EG(current_execute_data), EG(opline_ptr), EG(active_symbol_table)
* and EG(This) here because the function modifies or uses them */
current_execute_data = EG(current_execute_data);
EG(current_execute_data) = execute_data->prev_execute_data;
opline_ptr = EG(opline_ptr);
current_symbol_table = EG(active_symbol_table);
EG(active_symbol_table) = execute_data->symbol_table;
current_this = EG(This);
EG(This) = NULL;
current_stack = EG(argument_stack);
clone->execute_data = zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC);
clone->stack = EG(argument_stack);
EG(argument_stack) = current_stack;
EG(This) = current_this;
EG(active_symbol_table) = current_symbol_table;
EG(current_execute_data) = current_execute_data;
EG(opline_ptr) = opline_ptr;
clone->execute_data = emalloc(total_size);
/* Copy the zend_execute_data struct */
memcpy(clone->execute_data, execute_data, execute_data_size);
/* Set the pointers to the memory segments for the compiled and
* temporary variables (which are located after the execute_data) */
clone->execute_data->CVs = (zval ***) ((char *) clone->execute_data + execute_data_size);
clone->execute_data->Ts = (temp_variable *) ((char *) clone->execute_data->CVs + CVs_size);
/* Zero out the compiled variables section */
memset(clone->execute_data->CVs, 0, sizeof(zval **) * op_array->last_var);
/* copy */
clone->execute_data->opline = execute_data->opline;
clone->execute_data->function_state = execute_data->function_state;
clone->execute_data->current_scope = execute_data->current_scope;
clone->execute_data->current_called_scope = execute_data->current_called_scope;
clone->execute_data->fast_ret = execute_data->fast_ret;
if (!symbol_table) {
int i;
@ -238,7 +200,7 @@ static void zend_generator_clone_storage(zend_generator *orig, zend_generator **
for (i = 0; i < op_array->last_var; i++) {
if (execute_data->CVs[i]) {
clone->execute_data->CVs[i] = (zval **) clone->execute_data->CVs + op_array->last_var + i;
*clone->execute_data->CVs[i] = (zval *) orig->execute_data->CVs[op_array->last_var + i];
*clone->execute_data->CVs[i] = (zval *) execute_data->CVs[op_array->last_var + i];
Z_ADDREF_PP(clone->execute_data->CVs[i]);
}
}
@ -259,8 +221,39 @@ static void zend_generator_clone_storage(zend_generator *orig, zend_generator **
}
}
/* Copy nested-calls stack */
if (execute_data->call) {
clone->execute_data->call = clone->execute_data->call_slots +
(execute_data->call - execute_data->call_slots);
} else {
clone->execute_data->call = NULL;
}
memcpy(clone->execute_data->call_slots, execute_data->call_slots, sizeof(call_slot) * op_array->nested_calls);
if (clone->execute_data->call >= clone->execute_data->call_slots) {
call_slot *call = clone->execute_data->call;
while (call >= clone->execute_data->call_slots) {
if (call->object) {
Z_ADDREF_P(call->object);
}
call--;
}
}
/* Copy the temporary variables */
memcpy(clone->execute_data->Ts, orig->execute_data->Ts, Ts_size);
memcpy(clone->execute_data->Ts, execute_data->Ts, sizeof(temp_variable) * op_array->T);
/* Copy arguments passed on stack */
stack_frame = zend_vm_stack_frame_base(clone->execute_data);
orig_stack_frame = zend_vm_stack_frame_base(execute_data);
clone->stack->top = stack_frame + (orig->stack->top - orig_stack_frame);
if (clone->stack->top != stack_frame) {
memcpy(stack_frame, orig_stack_frame, sizeof(zval*) * (orig->stack->top - orig_stack_frame));
while (clone->stack->top != stack_frame) {
Z_ADDREF_PP((zval**)stack_frame);
stack_frame++;
}
}
/* Add references to loop variables */
{
@ -286,42 +279,6 @@ static void zend_generator_clone_storage(zend_generator *orig, zend_generator **
}
}
if (orig->backed_up_stack) {
/* Copy backed up stack */
clone->backed_up_stack = emalloc(orig->backed_up_stack_size);
memcpy(clone->backed_up_stack, orig->backed_up_stack, orig->backed_up_stack_size);
/* Add refs to stack variables */
{
zval **zvals = (zval **) orig->backed_up_stack;
size_t zval_num = orig->backed_up_stack_size / sizeof(zval *);
int i;
for (i = 0; i < zval_num; i++) {
Z_ADDREF_P(zvals[i]);
}
}
}
if (orig->backed_up_arg_types_stack) {
size_t stack_size = orig->backed_up_arg_types_stack_count * sizeof(void *);
clone->backed_up_arg_types_stack = emalloc(stack_size);
memcpy(clone->backed_up_arg_types_stack, orig->backed_up_arg_types_stack, stack_size);
/* We have to add refs to the objects in the arg types stack (the
* object is always the second element of a three-pack. */
{
int i, stack_frames = clone->backed_up_arg_types_stack_count / 3;
for (i = 0; i < stack_frames; i++) {
zval *object = (zval *) clone->backed_up_arg_types_stack[3*i + 1];
if (object) {
Z_ADDREF_P(object);
}
}
}
}
/* Update the send_target to use the temporary variable with the same
* offset as the original generator, but in our temporary variable
* memory segment. */
@ -334,24 +291,14 @@ static void zend_generator_clone_storage(zend_generator *orig, zend_generator **
}
if (execute_data->current_this) {
clone->execute_data->current_this = execute_data->current_this;
Z_ADDREF_P(execute_data->current_this);
}
if (execute_data->object) {
clone->execute_data->object = execute_data->object;
Z_ADDREF_P(execute_data->object);
}
/* Prev execute data contains an additional stack frame (for proper
* backtraces) which has to be copied. */
clone->execute_data->prev_execute_data = emalloc(sizeof(zend_execute_data));
memcpy(clone->execute_data->prev_execute_data, execute_data->prev_execute_data, sizeof(zend_execute_data));
/* It also contains the arguments passed to the generator, which also
* have to be copied */
if (execute_data->prev_execute_data->function_state.arguments) {
clone->execute_data->prev_execute_data->function_state.arguments
= zend_copy_arguments(execute_data->prev_execute_data->function_state.arguments);
}
}
/* The value and key are known not to be references, so simply add refs */
@ -399,7 +346,9 @@ zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC) /* {{{ */
zend_generator *generator;
zend_execute_data *current_execute_data;
zend_op **opline_ptr;
HashTable *current_symbol_table;
zend_execute_data *execute_data;
zend_vm_stack current_stack = EG(argument_stack);
/* Create a clone of closure, because it may be destroyed */
if (op_array->fn_flags & ZEND_ACC_CLOSURE) {
@ -410,11 +359,14 @@ zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC) /* {{{ */
}
/* Create new execution context. We have to back up and restore
* EG(current_execute_data) and EG(opline_ptr) here because the function
* modifies it. */
* EG(current_execute_data), EG(opline_ptr) and EG(active_symbol_table)
* here because the function modifies or uses them */
current_execute_data = EG(current_execute_data);
opline_ptr = EG(opline_ptr);
current_symbol_table = EG(active_symbol_table);
EG(active_symbol_table) = NULL;
execute_data = zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC);
EG(active_symbol_table) = current_symbol_table;
EG(current_execute_data) = current_execute_data;
EG(opline_ptr) = opline_ptr;
@ -434,18 +386,8 @@ zval *zend_generator_create_zval(zend_op_array *op_array TSRMLS_DC) /* {{{ */
/* Save execution context in generator object. */
generator = (zend_generator *) zend_object_store_get_object(return_value TSRMLS_CC);
generator->execute_data = execute_data;
/* We have to add another stack frame so the generator function shows
* up in backtraces and func_get_all() can access the function
* arguments. */
execute_data->prev_execute_data = emalloc(sizeof(zend_execute_data));
memset(execute_data->prev_execute_data, 0, sizeof(zend_execute_data));
execute_data->prev_execute_data->function_state.function = (zend_function *) op_array;
if (EG(current_execute_data)) {
execute_data->prev_execute_data->function_state.arguments = zend_copy_arguments(EG(current_execute_data)->function_state.arguments);
} else {
execute_data->prev_execute_data->function_state.arguments = NULL;
}
generator->stack = EG(argument_stack);
EG(argument_stack) = current_stack;
return return_value;
}
@ -487,28 +429,7 @@ void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */
zval *original_This = EG(This);
zend_class_entry *original_scope = EG(scope);
zend_class_entry *original_called_scope = EG(called_scope);
int original_arg_types_stack_count = EG(arg_types_stack).top;
/* Remember the current stack position so we can back up pushed args */
generator->original_stack_top = zend_vm_stack_top(TSRMLS_C);
/* If there is a backed up stack copy it to the VM stack */
if (generator->backed_up_stack) {
void *stack = zend_vm_stack_alloc(generator->backed_up_stack_size TSRMLS_CC);
memcpy(stack, generator->backed_up_stack, generator->backed_up_stack_size);
efree(generator->backed_up_stack);
generator->backed_up_stack = NULL;
}
if (generator->backed_up_arg_types_stack) {
zend_ptr_stack_push_from_memory(
&EG(arg_types_stack),
generator->backed_up_arg_types_stack_count,
generator->backed_up_arg_types_stack
);
efree(generator->backed_up_arg_types_stack);
generator->backed_up_arg_types_stack = NULL;
}
zend_vm_stack original_stack = EG(argument_stack);
/* We (mis)use the return_value_ptr_ptr to provide the generator object
* to the executor, so YIELD will be able to set the yielded value */
@ -522,6 +443,7 @@ void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */
EG(This) = generator->execute_data->current_this;
EG(scope) = generator->execute_data->current_scope;
EG(called_scope) = generator->execute_data->current_called_scope;
EG(argument_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()).
@ -533,7 +455,7 @@ void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */
/* Resume execution */
generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
execute_ex(generator->execute_data TSRMLS_CC);
zend_execute_ex(generator->execute_data TSRMLS_CC);
generator->flags &= ~ZEND_GENERATOR_CURRENTLY_RUNNING;
/* Restore executor globals */
@ -545,27 +467,7 @@ void zend_generator_resume(zend_generator *generator TSRMLS_DC) /* {{{ */
EG(This) = original_This;
EG(scope) = original_scope;
EG(called_scope) = original_called_scope;
/* The stack top before and after the execution differ, i.e. there are
* arguments pushed to the stack. */
if (generator->original_stack_top != zend_vm_stack_top(TSRMLS_C)) {
generator->backed_up_stack_size = (zend_vm_stack_top(TSRMLS_C) - generator->original_stack_top) * sizeof(void *);
generator->backed_up_stack = emalloc(generator->backed_up_stack_size);
memcpy(generator->backed_up_stack, generator->original_stack_top, generator->backed_up_stack_size);
zend_vm_stack_free(generator->original_stack_top TSRMLS_CC);
}
if (original_arg_types_stack_count != EG(arg_types_stack).top) {
generator->backed_up_arg_types_stack_count =
EG(arg_types_stack).top - original_arg_types_stack_count;
generator->backed_up_arg_types_stack = emalloc(generator->backed_up_arg_types_stack_count * sizeof(void *));
zend_ptr_stack_pop_into_memory(
&EG(arg_types_stack),
generator->backed_up_arg_types_stack_count,
generator->backed_up_arg_types_stack
);
}
EG(argument_stack) = original_stack;
/* If an exception was thrown in the generator we have to internally
* rethrow it in the parent scope. */
@ -741,21 +643,11 @@ ZEND_METHOD(Generator, __wakeup)
/* get_iterator implementation */
typedef struct _zend_generator_iterator {
zend_object_iterator intern;
/* The generator object zval has to be stored, because the iterator is
* holding a ref to it, which has to be dtored. */
zval *object;
} zend_generator_iterator;
static void zend_generator_iterator_dtor(zend_object_iterator *iterator TSRMLS_DC) /* {{{ */
{
zval *object = ((zend_generator_iterator *) iterator)->object;
zval_ptr_dtor(&object);
efree(iterator);
}
/* }}} */
@ -854,7 +746,7 @@ zend_object_iterator *zend_generator_get_iterator(zend_class_entry *ce, zval *ob
return NULL;
}
iterator = emalloc(sizeof(zend_generator_iterator));
iterator = &generator->iterator;
iterator->intern.funcs = &zend_generator_iterator_functions;
iterator->intern.data = (void *) generator;

View file

@ -25,25 +25,24 @@ BEGIN_EXTERN_C()
extern ZEND_API zend_class_entry *zend_ce_generator;
END_EXTERN_C()
typedef struct _zend_generator_iterator {
zend_object_iterator intern;
/* The generator object zval has to be stored, because the iterator is
* holding a ref to it, which has to be dtored. */
zval *object;
} zend_generator_iterator;
typedef struct _zend_generator {
zend_object std;
zend_generator_iterator iterator;
/* The suspended execution context. */
zend_execute_data *execute_data;
/* If the execution is suspended during a function call there may be
* arguments pushed to the stack, so it has to be backed up. */
void *backed_up_stack;
size_t backed_up_stack_size;
/* For method calls PHP also pushes various type information on a second
* stack, which also needs to be backed up. */
void **backed_up_arg_types_stack;
int backed_up_arg_types_stack_count;
/* The original stack top before resuming the generator. This is required
* for proper cleanup during exception handling. */
void **original_stack_top;
/* The separate stack used by generator */
zend_vm_stack stack;
/* Current value */
zval *value;

View file

@ -167,8 +167,6 @@ struct _zend_executor_globals {
zval error_zval;
zval *error_zval_ptr;
zend_ptr_stack arg_types_stack;
/* symbol table cache */
HashTable *symtable_cache[SYMTABLE_CACHE_SIZE];
HashTable **symtable_cache_limit;

View file

@ -33,7 +33,7 @@
#define ZEND_MODULE_INFO_FUNC_ARGS zend_module_entry *zend_module TSRMLS_DC
#define ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU zend_module TSRMLS_CC
#define ZEND_MODULE_API_NO 20121113
#define ZEND_MODULE_API_NO 20121128
#ifdef ZTS
#define USING_ZTS 1
#else

View file

@ -70,6 +70,9 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz
op_array->T = 0;
op_array->nested_calls = 0;
op_array->used_stack = 0;
op_array->function_name = NULL;
op_array->filename = zend_get_compiled_filename(TSRMLS_C);
op_array->doc_comment = NULL;
@ -541,6 +544,15 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num,
/* generate a FAST_CALL to finaly block */
start_op = get_next_op_number(op_array);
if (op_array->opcodes[op_num].opcode == ZEND_YIELD) {
/* Disable yield in finally block */
opline = get_next_op(op_array TSRMLS_CC);
opline->opcode = ZEND_GENERATOR_FLAG;
opline->extended_value = 1;
SET_UNUSED(opline->op1);
SET_UNUSED(opline->op2);
}
opline = get_next_op(op_array TSRMLS_CC);
opline->opcode = ZEND_FAST_CALL;
SET_UNUSED(opline->op1);
@ -567,6 +579,14 @@ static void zend_resolve_finally_call(zend_op_array *op_array, zend_uint op_num,
opline->op1.opline_num = op_array->try_catch_array[i].finally_op;
}
}
if (op_array->opcodes[op_num].opcode == ZEND_YIELD) {
/* Re-enable yield */
opline = get_next_op(op_array TSRMLS_CC);
opline->opcode = ZEND_GENERATOR_FLAG;
opline->extended_value = 0;
SET_UNUSED(opline->op1);
SET_UNUSED(opline->op2);
}
/* Finish the sequence with original opcode */
opline = get_next_op(op_array TSRMLS_CC);
@ -622,6 +642,7 @@ static void zend_resolve_finally_calls(zend_op_array *op_array TSRMLS_DC)
switch (opline->opcode) {
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
case ZEND_YIELD:
zend_resolve_finally_call(op_array, i, (zend_uint)-1 TSRMLS_CC);
break;
case ZEND_BRK:

View file

@ -110,23 +110,6 @@ ZEND_API int zend_ptr_stack_num_elements(zend_ptr_stack *stack)
{
return stack->top;
}
ZEND_API void zend_ptr_stack_push_from_memory(zend_ptr_stack *stack, int count, void **pointers)
{
ZEND_PTR_STACK_RESIZE_IF_NEEDED(stack, count);
memcpy(stack->top_element, pointers, count * sizeof(void *));
stack->top_element += count;
stack->top += count;
}
ZEND_API void zend_ptr_stack_pop_into_memory(zend_ptr_stack *stack, int count, void **pointers)
{
memcpy(pointers, stack->top_element - count, count * sizeof(void *));
stack->top_element -= count;
stack->top -= count;
}
/*
* Local variables:
* tab-width: 4

View file

@ -41,8 +41,6 @@ ZEND_API void zend_ptr_stack_destroy(zend_ptr_stack *stack);
ZEND_API void zend_ptr_stack_apply(zend_ptr_stack *stack, void (*func)(void *));
ZEND_API void zend_ptr_stack_clean(zend_ptr_stack *stack, void (*func)(void *), zend_bool free_elements);
ZEND_API int zend_ptr_stack_num_elements(zend_ptr_stack *stack);
ZEND_API void zend_ptr_stack_push_from_memory(zend_ptr_stack *stack, int count, void **pointers);
ZEND_API void zend_ptr_stack_pop_into_memory(zend_ptr_stack *stack, int count, void **pointers);
END_EXTERN_C()
#define ZEND_PTR_STACK_RESIZE_IF_NEEDED(stack, count) \

View file

@ -1161,7 +1161,7 @@ ZEND_VM_HANDLER(92, ZEND_FETCH_FUNC_ARG, CONST|TMP|VAR|CV, UNUSED|CONST|VAR)
USE_OPLINE
ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type,
ARG_SHOULD_BE_SENT_BY_REF(EX(fbc), (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R);
ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))?BP_VAR_W:BP_VAR_R);
}
ZEND_VM_HANDLER(95, ZEND_FETCH_UNSET, CONST|TMP|VAR|CV, UNUSED|CONST|VAR)
@ -1283,7 +1283,7 @@ ZEND_VM_HANDLER(93, ZEND_FETCH_DIM_FUNC_ARG, VAR|CV, CONST|TMP|VAR|UNUSED|CV)
SAVE_OPLINE();
if (ARG_SHOULD_BE_SENT_BY_REF(EX(fbc), (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
container = GET_OP1_ZVAL_PTR_PTR(BP_VAR_W);
if (OP1_TYPE == IS_VAR && UNEXPECTED(container == NULL)) {
zend_error_noreturn(E_ERROR, "Cannot use string offset as an array");
@ -1519,7 +1519,7 @@ ZEND_VM_HANDLER(94, ZEND_FETCH_OBJ_FUNC_ARG, VAR|UNUSED|CV, CONST|TMP|VAR|CV)
{
USE_OPLINE
if (ARG_SHOULD_BE_SENT_BY_REF(EX(fbc), (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, (opline->extended_value & ZEND_FETCH_ARG_MASK))) {
/* Behave like FETCH_OBJ_W */
zend_free_op free_op1, free_op2;
zval *property;
@ -1889,7 +1889,6 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
EX(function_state).function = (zend_function *) EX(op_array);
EX(function_state).arguments = NULL;
EX(object) = EX(current_object);
EG(opline_ptr) = &EX(opline);
EG(active_op_array) = EX(op_array);
@ -1925,8 +1924,8 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
EX(function_state).arguments = NULL;
if (EG(This)) {
if (UNEXPECTED(EG(exception) != NULL) && IS_CTOR_CALL(EX(called_scope))) {
if (IS_CTOR_USED(EX(called_scope))) {
if (UNEXPECTED(EG(exception) != NULL) && EX(call)->is_ctor_call) {
if (EX(call)->is_ctor_result_used) {
Z_DELREF_P(EG(This));
}
if (Z_REFCOUNT_P(EG(This)) == 1) {
@ -1939,8 +1938,7 @@ ZEND_VM_HELPER(zend_leave_helper, ANY, ANY)
EG(scope) = EX(current_scope);
EG(called_scope) = EX(current_called_scope);
EX(object) = EX(current_object);
EX(called_scope) = DECODE_CTOR(EX(called_scope));
EX(call)--;
zend_vm_stack_clear_multiple(TSRMLS_C);
@ -1966,6 +1964,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY)
zend_function *fbc = EX(function_state).function;
SAVE_OPLINE();
EX(object) = EX(call)->object;
if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
zend_error_noreturn(E_ERROR, "Cannot call abstract method %s::%s()", fbc->common.scope->name, fbc->common.function_name);
@ -2000,11 +1999,11 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY)
EX(current_called_scope) = EG(called_scope);
EG(This) = EX(object);
EG(scope) = (fbc->type == ZEND_USER_FUNCTION || !EX(object)) ? fbc->common.scope : NULL;
EG(called_scope) = EX(called_scope);
EG(called_scope) = EX(call)->called_scope;
}
zend_arg_types_stack_3_pop(&EG(arg_types_stack), &EX(called_scope), &EX(current_object), &EX(fbc));
EX(function_state).arguments = zend_vm_stack_push_args(opline->extended_value TSRMLS_CC);
EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C);
zend_vm_stack_push((void*)(zend_uintptr_t)opline->extended_value TSRMLS_CC);
LOAD_OPLINE();
if (fbc->type == ZEND_INTERNAL_FUNCTION) {
@ -2054,7 +2053,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY)
if (RETURN_VALUE_USED(opline)) {
EX_T(opline->result.var).var.ptr = zend_generator_create_zval(EG(active_op_array) TSRMLS_CC);
}
} else if (EXPECTED(zend_execute == execute)) {
} else if (EXPECTED(zend_execute_ex == execute_ex)) {
if (EXPECTED(EG(exception) == NULL)) {
ZEND_VM_ENTER();
}
@ -2100,8 +2099,8 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY)
if (should_change_scope) {
if (EG(This)) {
if (UNEXPECTED(EG(exception) != NULL) && IS_CTOR_CALL(EX(called_scope))) {
if (IS_CTOR_USED(EX(called_scope))) {
if (UNEXPECTED(EG(exception) != NULL) && EX(call)->is_ctor_call) {
if (EX(call)->is_ctor_result_used) {
Z_DELREF_P(EG(This));
}
if (Z_REFCOUNT_P(EG(This)) == 1) {
@ -2115,8 +2114,7 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY)
EG(called_scope) = EX(current_called_scope);
}
EX(object) = EX(current_object);
EX(called_scope) = DECODE_CTOR(EX(called_scope));
EX(call)--;
zend_vm_stack_clear_multiple(TSRMLS_C);
@ -2463,9 +2461,9 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, TMP|VAR|UNUSED|CV, CONST|TMP|VAR|CV)
char *function_name_strval;
int function_name_strlen;
zend_free_op free_op1, free_op2;
call_slot *call = EX(call_slots) + opline->result.num;
SAVE_OPLINE();
zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope));
function_name = GET_OP2_ZVAL_PTR(BP_VAR_R);
@ -2477,49 +2475,51 @@ ZEND_VM_HANDLER(112, ZEND_INIT_METHOD_CALL, TMP|VAR|UNUSED|CV, CONST|TMP|VAR|CV)
function_name_strval = Z_STRVAL_P(function_name);
function_name_strlen = Z_STRLEN_P(function_name);
EX(object) = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_R);
call->object = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_R);
if (EXPECTED(EX(object) != NULL) &&
EXPECTED(Z_TYPE_P(EX(object)) == IS_OBJECT)) {
EX(called_scope) = Z_OBJCE_P(EX(object));
if (EXPECTED(call->object != NULL) &&
EXPECTED(Z_TYPE_P(call->object) == IS_OBJECT)) {
call->called_scope = Z_OBJCE_P(call->object);
if (OP2_TYPE != IS_CONST ||
(EX(fbc) = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, EX(called_scope))) == NULL) {
zval *object = EX(object);
(call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope)) == NULL) {
zval *object = call->object;
if (UNEXPECTED(Z_OBJ_HT_P(EX(object))->get_method == NULL)) {
if (UNEXPECTED(Z_OBJ_HT_P(call->object)->get_method == NULL)) {
zend_error_noreturn(E_ERROR, "Object does not support method calls");
}
/* First, locate the function. */
EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), function_name_strval, function_name_strlen, ((OP2_TYPE == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC);
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(EX(object)), function_name_strval);
call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((OP2_TYPE == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC);
if (UNEXPECTED(call->fbc == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), function_name_strval);
}
if (OP2_TYPE == IS_CONST &&
EXPECTED(EX(fbc)->type <= ZEND_USER_FUNCTION) &&
EXPECTED((EX(fbc)->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) &&
EXPECTED(EX(object) == object)) {
CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, EX(called_scope), EX(fbc));
EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) &&
EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0) &&
EXPECTED(call->object == object)) {
CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, call->called_scope, call->fbc);
}
}
} else {
zend_error_noreturn(E_ERROR, "Call to a member function %s() on a non-object", function_name_strval);
}
if ((EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) != 0) {
EX(object) = NULL;
if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) {
call->object = NULL;
} else {
if (!PZVAL_IS_REF(EX(object))) {
Z_ADDREF_P(EX(object)); /* For $this pointer */
if (!PZVAL_IS_REF(call->object)) {
Z_ADDREF_P(call->object); /* For $this pointer */
} else {
zval *this_ptr;
ALLOC_ZVAL(this_ptr);
INIT_PZVAL_COPY(this_ptr, EX(object));
INIT_PZVAL_COPY(this_ptr, call->object);
zval_copy_ctor(this_ptr);
EX(object) = this_ptr;
call->object = this_ptr;
}
}
call->is_ctor_call = 0;
EX(call) = call;
FREE_OP2();
FREE_OP1_IF_VAR();
@ -2533,9 +2533,9 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMP|VAR|UNUS
USE_OPLINE
zval *function_name;
zend_class_entry *ce;
call_slot *call = EX(call_slots) + opline->result.num;
SAVE_OPLINE();
zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope));
if (OP1_TYPE == IS_CONST) {
/* no function found. try a static method in class */
@ -2549,24 +2549,24 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMP|VAR|UNUS
}
CACHE_PTR(opline->op1.literal->cache_slot, ce);
}
EX(called_scope) = ce;
call->called_scope = ce;
} else {
ce = EX_T(opline->op1.var).class_entry;
if (opline->extended_value == ZEND_FETCH_CLASS_PARENT || opline->extended_value == ZEND_FETCH_CLASS_SELF) {
EX(called_scope) = EG(called_scope);
call->called_scope = EG(called_scope);
} else {
EX(called_scope) = ce;
call->called_scope = ce;
}
}
if (OP1_TYPE == IS_CONST &&
OP2_TYPE == IS_CONST &&
CACHED_PTR(opline->op2.literal->cache_slot)) {
EX(fbc) = CACHED_PTR(opline->op2.literal->cache_slot);
call->fbc = CACHED_PTR(opline->op2.literal->cache_slot);
} else if (OP1_TYPE != IS_CONST &&
OP2_TYPE == IS_CONST &&
(EX(fbc) = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, ce))) {
(call->fbc = CACHED_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, ce))) {
/* do nothing */
} else if (OP2_TYPE != IS_UNUSED) {
char *function_name_strval = NULL;
@ -2589,20 +2589,20 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMP|VAR|UNUS
if (function_name_strval) {
if (ce->get_static_method) {
EX(fbc) = ce->get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC);
call->fbc = ce->get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC);
} else {
EX(fbc) = zend_std_get_static_method(ce, function_name_strval, function_name_strlen, ((OP2_TYPE == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC);
call->fbc = zend_std_get_static_method(ce, function_name_strval, function_name_strlen, ((OP2_TYPE == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC);
}
if (UNEXPECTED(EX(fbc) == NULL)) {
if (UNEXPECTED(call->fbc == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, function_name_strval);
}
if (OP2_TYPE == IS_CONST &&
EXPECTED(EX(fbc)->type <= ZEND_USER_FUNCTION) &&
EXPECTED((EX(fbc)->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) {
EXPECTED(call->fbc->type <= ZEND_USER_FUNCTION) &&
EXPECTED((call->fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_HANDLER|ZEND_ACC_NEVER_CACHE)) == 0)) {
if (OP1_TYPE == IS_CONST) {
CACHE_PTR(opline->op2.literal->cache_slot, EX(fbc));
CACHE_PTR(opline->op2.literal->cache_slot, call->fbc);
} else {
CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, ce, EX(fbc));
CACHE_POLYMORPHIC_PTR(opline->op2.literal->cache_slot, ce, call->fbc);
}
}
}
@ -2616,29 +2616,31 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, CONST|VAR, CONST|TMP|VAR|UNUS
if (EG(This) && Z_OBJCE_P(EG(This)) != ce->constructor->common.scope && (ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
zend_error_noreturn(E_ERROR, "Cannot call private %s::__construct()", ce->name);
}
EX(fbc) = ce->constructor;
call->fbc = ce->constructor;
}
if (EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) {
EX(object) = NULL;
if (call->fbc->common.fn_flags & ZEND_ACC_STATIC) {
call->object = NULL;
} else {
if (EG(This) &&
Z_OBJ_HT_P(EG(This))->get_class_entry &&
!instanceof_function(Z_OBJCE_P(EG(This)), ce TSRMLS_CC)) {
/* We are calling method of the other (incompatible) class,
but passing $this. This is done for compatibility with php-4. */
if (EX(fbc)->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
zend_error(E_STRICT, "Non-static method %s::%s() should not be called statically, assuming $this from incompatible context", EX(fbc)->common.scope->name, EX(fbc)->common.function_name);
if (call->fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) {
zend_error(E_STRICT, "Non-static method %s::%s() should not be called statically, assuming $this from incompatible context", call->fbc->common.scope->name, call->fbc->common.function_name);
} else {
/* An internal function assumes $this is present and won't check that. So PHP would crash by allowing the call. */
zend_error_noreturn(E_ERROR, "Non-static method %s::%s() cannot be called statically, assuming $this from incompatible context", EX(fbc)->common.scope->name, EX(fbc)->common.function_name);
zend_error_noreturn(E_ERROR, "Non-static method %s::%s() cannot be called statically, assuming $this from incompatible context", call->fbc->common.scope->name, call->fbc->common.function_name);
}
}
if ((EX(object) = EG(This))) {
Z_ADDREF_P(EX(object));
EX(called_scope) = Z_OBJCE_P(EX(object));
if ((call->object = EG(This))) {
Z_ADDREF_P(call->object);
call->called_scope = Z_OBJCE_P(call->object);
}
}
call->is_ctor_call = 0;
EX(call) = call;
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
@ -2648,19 +2650,21 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
{
USE_OPLINE
zval *function_name;
zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope));
call_slot *call = EX(call_slots) + opline->result.num;
if (OP2_TYPE == IS_CONST) {
function_name = (zval*)(opline->op2.literal+1);
if (CACHED_PTR(opline->op2.literal->cache_slot)) {
EX(fbc) = CACHED_PTR(opline->op2.literal->cache_slot);
} else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(function_name), Z_STRLEN_P(function_name)+1, Z_HASH_P(function_name), (void **) &EX(fbc)) == FAILURE)) {
call->fbc = CACHED_PTR(opline->op2.literal->cache_slot);
} else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(function_name), Z_STRLEN_P(function_name)+1, Z_HASH_P(function_name), (void **) &call->fbc) == FAILURE)) {
SAVE_OPLINE();
zend_error_noreturn(E_ERROR, "Call to undefined function %s()", Z_STRVAL_P(opline->op2.zv));
} else {
CACHE_PTR(opline->op2.literal->cache_slot, EX(fbc));
CACHE_PTR(opline->op2.literal->cache_slot, call->fbc);
}
EX(object) = NULL;
call->object = NULL;
call->is_ctor_call = 0;
EX(call) = call;
/*CHECK_EXCEPTION();*/
ZEND_VM_NEXT_OPCODE();
} else {
@ -2680,28 +2684,32 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
} else {
lcname = zend_str_tolower_dup(function_name_strval, function_name_strlen);
}
if (UNEXPECTED(zend_hash_find(EG(function_table), lcname, function_name_strlen+1, (void **) &EX(fbc)) == FAILURE)) {
if (UNEXPECTED(zend_hash_find(EG(function_table), lcname, function_name_strlen+1, (void **) &call->fbc) == FAILURE)) {
zend_error_noreturn(E_ERROR, "Call to undefined function %s()", function_name_strval);
}
efree(lcname);
FREE_OP2();
EX(object) = NULL;
call->object = NULL;
call->is_ctor_call = 0;
EX(call) = call;
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else if (OP2_TYPE != IS_CONST && OP2_TYPE != IS_TMP_VAR &&
EXPECTED(Z_TYPE_P(function_name) == IS_OBJECT) &&
Z_OBJ_HANDLER_P(function_name, get_closure) &&
Z_OBJ_HANDLER_P(function_name, get_closure)(function_name, &EX(called_scope), &EX(fbc), &EX(object) TSRMLS_CC) == SUCCESS) {
if (EX(object)) {
Z_ADDREF_P(EX(object));
Z_OBJ_HANDLER_P(function_name, get_closure)(function_name, &call->called_scope, &call->fbc, &call->object TSRMLS_CC) == SUCCESS) {
if (call->object) {
Z_ADDREF_P(call->object);
}
if (OP2_TYPE == IS_VAR && OP2_FREE &&
EX(fbc)->common.fn_flags & ZEND_ACC_CLOSURE) {
call->fbc->common.fn_flags & ZEND_ACC_CLOSURE) {
/* Delay closure destruction until its invocation */
EX(fbc)->common.prototype = (zend_function*)function_name;
call->fbc->common.prototype = (zend_function*)function_name;
} else {
FREE_OP2();
}
call->is_ctor_call = 0;
EX(call) = call;
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
} else if (OP2_TYPE != IS_CONST &&
@ -2732,41 +2740,43 @@ ZEND_VM_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST|TMP|VAR|CV)
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
}
EX(called_scope) = ce;
EX(object) = NULL;
call->called_scope = ce;
call->object = NULL;
if (ce->get_static_method) {
EX(fbc) = ce->get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method) TSRMLS_CC);
call->fbc = ce->get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method) TSRMLS_CC);
} else {
EX(fbc) = zend_std_get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
call->fbc = zend_std_get_static_method(ce, Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
}
} else {
EX(object) = *obj;
ce = EX(called_scope) = Z_OBJCE_PP(obj);
call->object = *obj;
ce = call->called_scope = Z_OBJCE_PP(obj);
EX(fbc) = Z_OBJ_HT_P(EX(object))->get_method(&EX(object), Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
if (UNEXPECTED(EX(fbc) == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(EX(object)), Z_STRVAL_PP(method));
call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, Z_STRVAL_PP(method), Z_STRLEN_PP(method), NULL TSRMLS_CC);
if (UNEXPECTED(call->fbc == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", Z_OBJ_CLASS_NAME_P(call->object), Z_STRVAL_PP(method));
}
if ((EX(fbc)->common.fn_flags & ZEND_ACC_STATIC) != 0) {
EX(object) = NULL;
if ((call->fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) {
call->object = NULL;
} else {
if (!PZVAL_IS_REF(EX(object))) {
Z_ADDREF_P(EX(object)); /* For $this pointer */
if (!PZVAL_IS_REF(call->object)) {
Z_ADDREF_P(call->object); /* For $this pointer */
} else {
zval *this_ptr;
ALLOC_ZVAL(this_ptr);
INIT_PZVAL_COPY(this_ptr, EX(object));
INIT_PZVAL_COPY(this_ptr, call->object);
zval_copy_ctor(this_ptr);
EX(object) = this_ptr;
call->object = this_ptr;
}
}
}
if (UNEXPECTED(EX(fbc) == NULL)) {
if (UNEXPECTED(call->fbc == NULL)) {
zend_error_noreturn(E_ERROR, "Call to undefined method %s::%s()", ce->name, Z_STRVAL_PP(method));
}
call->is_ctor_call = 0;
EX(call) = call;
FREE_OP2();
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
@ -2781,31 +2791,32 @@ ZEND_VM_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST)
{
USE_OPLINE
zend_literal *func_name;
zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope));
call_slot *call = EX(call_slots) + opline->result.num;
func_name = opline->op2.literal + 1;
if (CACHED_PTR(opline->op2.literal->cache_slot)) {
EX(fbc) = CACHED_PTR(opline->op2.literal->cache_slot);
} else if (zend_hash_quick_find(EG(function_table), Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant)+1, func_name->hash_value, (void **) &EX(fbc))==FAILURE) {
call->fbc = CACHED_PTR(opline->op2.literal->cache_slot);
} else if (zend_hash_quick_find(EG(function_table), Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant)+1, func_name->hash_value, (void **) &call->fbc)==FAILURE) {
func_name++;
if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant)+1, func_name->hash_value, (void **) &EX(fbc))==FAILURE)) {
if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL(func_name->constant), Z_STRLEN(func_name->constant)+1, func_name->hash_value, (void **) &call->fbc)==FAILURE)) {
SAVE_OPLINE();
zend_error_noreturn(E_ERROR, "Call to undefined function %s()", Z_STRVAL_P(opline->op2.zv));
} else {
CACHE_PTR(opline->op2.literal->cache_slot, EX(fbc));
CACHE_PTR(opline->op2.literal->cache_slot, call->fbc);
}
} else {
CACHE_PTR(opline->op2.literal->cache_slot, EX(fbc));
CACHE_PTR(opline->op2.literal->cache_slot, call->fbc);
}
EX(object) = NULL;
call->object = NULL;
call->is_ctor_call = 0;
EX(call) = call;
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(61, ZEND_DO_FCALL_BY_NAME, ANY, ANY)
{
EX(function_state).function = EX(fbc);
EX(function_state).function = EX(call)->fbc;
ZEND_VM_DISPATCH_TO_HELPER(zend_do_fcall_common_helper);
}
@ -2814,8 +2825,7 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, CONST, ANY)
USE_OPLINE
zend_free_op free_op1;
zval *fname = GET_OP1_ZVAL_PTR(BP_VAR_R);
zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), EX(called_scope));
call_slot *call = EX(call_slots) + opline->op2.num;
if (CACHED_PTR(opline->op1.literal->cache_slot)) {
EX(function_state).function = CACHED_PTR(opline->op1.literal->cache_slot);
@ -2825,7 +2835,10 @@ ZEND_VM_HANDLER(60, ZEND_DO_FCALL, CONST, ANY)
} else {
CACHE_PTR(opline->op1.literal->cache_slot, EX(function_state).function);
}
EX(object) = NULL;
call->fbc = EX(function_state).function;
call->object = NULL;
call->is_ctor_call = 0;
EX(call) = call;
FREE_OP1();
@ -3052,7 +3065,7 @@ ZEND_VM_HANDLER(65, ZEND_SEND_VAL, CONST|TMP, ANY)
SAVE_OPLINE();
if (opline->extended_value==ZEND_DO_FCALL_BY_NAME
&& ARG_MUST_BE_SENT_BY_REF(EX(fbc), opline->op2.opline_num)) {
&& ARG_MUST_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
zend_error_noreturn(E_ERROR, "Cannot pass parameter %d by reference", opline->op2.opline_num);
}
{
@ -3113,7 +3126,7 @@ ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR|CV, ANY)
if (!(opline->extended_value & ZEND_ARG_SEND_BY_REF)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_send_by_var_helper);
}
} else if (!ARG_SHOULD_BE_SENT_BY_REF(EX(fbc), opline->op2.opline_num)) {
} else if (!ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_send_by_var_helper);
}
@ -3139,7 +3152,7 @@ ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR|CV, ANY)
if ((opline->extended_value & ZEND_ARG_COMPILE_TIME_BOUND) ?
!(opline->extended_value & ZEND_ARG_SEND_SILENT) :
!ARG_MAY_BE_SENT_BY_REF(EX(fbc), opline->op2.opline_num)) {
!ARG_MAY_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
zend_error(E_STRICT, "Only variables should be passed by reference");
}
ALLOC_ZVAL(valptr);
@ -3175,7 +3188,7 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY)
ZEND_VM_NEXT_OPCODE();
}
if (EX(function_state).function->type == ZEND_INTERNAL_FUNCTION && !ARG_SHOULD_BE_SENT_BY_REF(EX(fbc), opline->op2.opline_num)) {
if (EX(function_state).function->type == ZEND_INTERNAL_FUNCTION && !ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
ZEND_VM_DISPATCH_TO_HELPER(zend_send_by_var_helper);
}
@ -3194,7 +3207,7 @@ ZEND_VM_HANDLER(66, ZEND_SEND_VAR, VAR|CV, ANY)
USE_OPLINE
if ((opline->extended_value == ZEND_DO_FCALL_BY_NAME)
&& ARG_SHOULD_BE_SENT_BY_REF(EX(fbc), opline->op2.opline_num)) {
&& ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, opline->op2.opline_num)) {
ZEND_VM_DISPATCH_TO_HANDLER(ZEND_SEND_REF);
}
SAVE_OPLINE();
@ -3400,17 +3413,20 @@ ZEND_VM_HANDLER(68, ZEND_NEW, ANY, ANY)
}
ZEND_VM_JMP(EX(op_array)->opcodes + opline->op2.opline_num);
} else {
call_slot *call = EX(call_slots) + opline->extended_value;
if (RETURN_VALUE_USED(opline)) {
PZVAL_LOCK(object_zval);
AI_SET_PTR(&EX_T(opline->result.var), object_zval);
}
zend_ptr_stack_3_push(&EG(arg_types_stack), EX(fbc), EX(object), ENCODE_CTOR(EX(called_scope), RETURN_VALUE_USED(opline)));
/* We are not handling overloaded classes right now */
EX(object) = object_zval;
EX(fbc) = constructor;
EX(called_scope) = EX_T(opline->op1.var).class_entry;
call->fbc = constructor;
call->object = object_zval;
call->called_scope = EX_T(opline->op1.var).class_entry;
call->is_ctor_call = 1;
call->is_ctor_result_used = RETURN_VALUE_USED(opline);
EX(call) = call;
CHECK_EXCEPTION();
ZEND_VM_NEXT_OPCODE();
@ -3829,8 +3845,6 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMP|VAR|CV, ANY)
EG(return_value_ptr_ptr) = NULL;
}
EX(current_object) = EX(object);
EX(function_state).function = (zend_function *) new_op_array;
EX(object) = NULL;
@ -3838,14 +3852,13 @@ ZEND_VM_HANDLER(73, ZEND_INCLUDE_OR_EVAL, CONST|TMP|VAR|CV, ANY)
zend_rebuild_symbol_table(TSRMLS_C);
}
if (EXPECTED(zend_execute == execute)) {
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
zend_execute(new_op_array TSRMLS_CC);
}
EX(function_state).function = (zend_function *) EX(op_array);
EX(object) = EX(current_object);
EG(opline_ptr) = &EX(opline);
EG(active_op_array) = EX(op_array);
@ -5022,19 +5035,7 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
/* Figure out where the next stack frame (which maybe contains pushed
* arguments that have to be dtor'ed) starts */
if (EX(op_array)->fn_flags & ZEND_ACC_GENERATOR) {
/* The generator object is stored in return_value_ptr_ptr */
zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr);
/* For generators the next stack frame is conveniently stored in the
* generator object. */
stack_frame = generator->original_stack_top;
} else {
/* In all other cases the next stack frame starts after the temporary
* variables section of the current execution context */
stack_frame = (void **) ((char *) EX_Ts() +
ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * EX(op_array)->T);
}
stack_frame = zend_vm_stack_frame_base(EXECUTE_DATA);
/* If the exception was thrown during a function call there might be
* arguments pushed to the stack that have to be dtor'ed. */
@ -5056,21 +5057,23 @@ ZEND_VM_HANDLER(149, ZEND_HANDLE_EXCEPTION, ANY, ANY)
}
}
while (EX(fbc)) {
EX(called_scope) = (zend_class_entry*)zend_ptr_stack_pop(&EG(arg_types_stack));
if (EX(object)) {
if (IS_CTOR_CALL(EX(called_scope))) {
if (IS_CTOR_USED(EX(called_scope))) {
Z_DELREF_P(EX(object));
}
if (Z_REFCOUNT_P(EX(object)) == 1) {
zend_object_store_ctor_failed(EX(object) TSRMLS_CC);
if (EX(call) >= EX(call_slots)) {
call_slot *call = EX(call);
do {
if (call->object) {
if (call->is_ctor_call) {
if (call->is_ctor_result_used) {
Z_DELREF_P(call->object);
}
if (Z_REFCOUNT_P(call->object) == 1) {
zend_object_store_ctor_failed(call->object TSRMLS_CC);
}
}
zval_ptr_dtor(&call->object);
}
zval_ptr_dtor(&EX(object));
}
EX(called_scope) = DECODE_CTOR(EX(called_scope));
zend_arg_types_stack_2_pop(&EG(arg_types_stack), &EX(object), &EX(fbc));
call--;
} while (call >= EX(call_slots));
EX(call) = NULL;
}
for (i=0; i<EX(op_array)->last_brk_cont; i++) {
@ -5240,6 +5243,19 @@ ZEND_VM_HANDLER(156, ZEND_SEPARATE, VAR, UNUSED)
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(159, ZEND_GENERATOR_FLAG, ANY, ANY)
{
USE_OPLINE
zend_generator *generator = (zend_generator *) EG(return_value_ptr_ptr);
if (opline->extended_value) {
generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
} else {
generator->flags &= ~ZEND_GENERATOR_FORCED_CLOSE;
}
ZEND_VM_NEXT_OPCODE();
}
ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED)
{
USE_OPLINE

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,49 @@
{%DEFINES%}
zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_array, zend_bool nested TSRMLS_DC) {
/*
* Stack Frame Layout (the whole stack frame is allocated at once)
* ==================
*
* +========================================+
* | zend_execute_data |<---+
* | EX(function_state).arguments |--+ |
* | ... | | |
* | ARGUMENT [1] | | |
* | ... | | |
* | ARGUMENT [ARGS_NUMBER] | | |
* | ARGS_NUMBER |<-+ |
* +========================================+ |
* |
* +========================================+ |
* EG(current_execute_data) -> | zend_execute_data | |
* | EX(prev_execute_data) |----+
* +----------------------------------------+
* EX(Ts) ---------> | EX(Ts)[0] |
* | ... |
* | EX(Tx)[op_arrat->T] |
* +----------------------------------------+
* EX(CVs) --------> | EX(CVs)[0] |--+
* | ... | |
* | EX(CVs)[op_array->last_var] | |
* +----------------------------------------+ |
* | Optional slot for CV[0] zval* |<-+
* | ... |
* | ... for CV [op_array->last_var] zval* |
* +----------------------------------------+
* EX(call_slots) -> | EX(call_slots)[0] |
* | ... |
* | EX(call_slots)[op_array->nested_calls] |
* +----------------------------------------+
* zend_vm_stack_frame_base -> | ARGUMENTS STACK [0] |
* | ... |
* zend_vm_stack_top --------> | ... |
* | ... |
* | ARGUMENTS STACK [op_array->used_stack] |
* +----------------------------------------+
*/
zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_array, zend_bool nested TSRMLS_DC)
{
zend_execute_data *execute_data;
/*
@ -15,7 +58,9 @@ zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_arra
size_t execute_data_size = ZEND_MM_ALIGNED_SIZE(sizeof(zend_execute_data));
size_t CVs_size = ZEND_MM_ALIGNED_SIZE(sizeof(zval **) * op_array->last_var * (EG(active_symbol_table) ? 1 : 2));
size_t Ts_size = ZEND_MM_ALIGNED_SIZE(sizeof(temp_variable)) * op_array->T;
size_t total_size = execute_data_size + CVs_size + Ts_size;
size_t call_slots_size = ZEND_MM_ALIGNED_SIZE(sizeof(call_slot)) * op_array->nested_calls;
size_t stack_size = ZEND_MM_ALIGNED_SIZE(sizeof(zval*)) * op_array->used_stack;
size_t total_size = execute_data_size + Ts_size + CVs_size + call_slots_size + stack_size;
/*
* Normally the execute_data is allocated on the VM stack (because it does
@ -27,23 +72,58 @@ zend_execute_data *zend_create_execute_data_from_op_array(zend_op_array *op_arra
* by replacing a pointer.
*/
if (op_array->fn_flags & ZEND_ACC_GENERATOR) {
execute_data = emalloc(total_size);
/* Prepend the regular stack frame with copy on prev_execute_data
* and passed arguments
*/
int args_count = zend_vm_stack_get_args_count_ex(EG(current_execute_data));
size_t args_size = ZEND_MM_ALIGNED_SIZE(sizeof(zval*)) * (args_count + 1);
total_size += args_size + execute_data_size;
EG(argument_stack) = zend_vm_stack_new_page((total_size + (sizeof(void*) - 1)) / sizeof(void*));
EG(argument_stack)->prev = NULL;
execute_data = (zend_execute_data*)((char*)ZEND_VM_STACK_ELEMETS(EG(argument_stack)) + args_size + execute_data_size);
/* copy prev_execute_data */
EX(prev_execute_data) = (zend_execute_data*)((char*)ZEND_VM_STACK_ELEMETS(EG(argument_stack)) + args_size);
memset(EX(prev_execute_data), 0, sizeof(zend_execute_data));
EX(prev_execute_data)->function_state.function = (zend_function*)op_array;
EX(prev_execute_data)->function_state.arguments = (void**)((char*)ZEND_VM_STACK_ELEMETS(EG(argument_stack)) + ZEND_MM_ALIGNED_SIZE(sizeof(zval*)) * args_count);
/* copy arguemnts */
*EX(prev_execute_data)->function_state.arguments = (void*)(zend_uintptr_t)args_count;
if (args_count > 0) {
zval **arg_src = (zval**)zend_vm_stack_get_arg_ex(EG(current_execute_data), 1);
zval **arg_dst = (zval**)zend_vm_stack_get_arg_ex(EX(prev_execute_data), 1);
int i;
for (i = 0; i < args_count; i++) {
arg_dst[i] = arg_src[i];
Z_ADDREF_P(arg_dst[i]);
}
}
} else {
execute_data = zend_vm_stack_alloc(total_size TSRMLS_CC);
EX(prev_execute_data) = EG(current_execute_data);
}
EX(CVs) = (zval ***) ((char *) execute_data + execute_data_size);
EX(Ts) = (temp_variable *) ((char *) execute_data + execute_data_size);
EX(CVs) = (zval ***) ((char *) EX(Ts) + Ts_size);
memset(EX(CVs), 0, sizeof(zval **) * op_array->last_var);
EX(Ts) = (temp_variable *) ((char *) EX(CVs) + CVs_size);
EX(call_slots) = (call_slot*)((char *) EX(CVs) + CVs_size);
EX(fbc) = NULL;
EX(called_scope) = NULL;
EX(object) = NULL;
EX(old_error_reporting) = NULL;
EX(op_array) = op_array;
EG(argument_stack)->top = zend_vm_stack_frame_base(execute_data);
EX(object) = NULL;
EX(current_this) = NULL;
EX(old_error_reporting) = NULL;
EX(symbol_table) = EG(active_symbol_table);
EX(prev_execute_data) = EG(current_execute_data);
EX(call) = NULL;
EG(current_execute_data) = execute_data;
EX(nested) = nested;
@ -103,12 +183,12 @@ ZEND_API void {%EXECUTOR_NAME%}_ex(zend_execute_data *execute_data TSRMLS_DC)
zend_error_noreturn(E_ERROR, "Arrived at end of main loop which shouldn't happen");
}
ZEND_API void {%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC)
ZEND_API void zend_{%EXECUTOR_NAME%}(zend_op_array *op_array TSRMLS_DC)
{
if (EG(exception)) {
return;
}
{%EXECUTOR_NAME%}_ex(zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC) TSRMLS_CC);
zend_{%EXECUTOR_NAME%}_ex(zend_create_execute_data_from_op_array(op_array, 0 TSRMLS_CC) TSRMLS_CC);
}
{%EXTERNAL_EXECUTOR%}

View file

@ -159,6 +159,7 @@
#define ZEND_SEPARATE 156
#define ZEND_QM_ASSIGN_VAR 157
#define ZEND_JMP_SET_VAR 158
#define ZEND_GENERATOR_FLAG 159
#define ZEND_YIELD 160
#define ZEND_GENERATOR_RETURN 161
#define ZEND_FAST_CALL 162