mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
. 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:
parent
9f7e53fde8
commit
70f83f35d0
20 changed files with 1477 additions and 1307 deletions
5
NEWS
5
NEWS
|
@ -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)
|
||||
|
|
21
UPGRADING
21
UPGRADING
|
@ -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
|
||||
========================================
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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
|
@ -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%}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue