diff options
Diffstat (limited to 'Zend/zend_execute.c')
| -rw-r--r-- | Zend/zend_execute.c | 1457 |
1 files changed, 996 insertions, 461 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index ad438f6dea..0a505f55e8 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -12,14 +12,12 @@ | obtain it through the world-wide-web, please send a note to | | license@zend.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Authors: Andi Gutmans <andi@zend.com> | - | Zeev Suraski <zeev@zend.com> | - | Dmitry Stogov <dmitry@zend.com> | + | Authors: Andi Gutmans <andi@php.net> | + | Zeev Suraski <zeev@php.net> | + | Dmitry Stogov <dmitry@php.net> | +----------------------------------------------------------------------+ */ -/* $Id$ */ - #define ZEND_INTENSIVE_DEBUGGING 0 #include <stdio.h> @@ -70,12 +68,20 @@ # define EXECUTE_DATA_DC # define EXECUTE_DATA_CC # define NO_EXECUTE_DATA_CC +# define OPLINE_D void +# define OPLINE_C +# define OPLINE_DC +# define OPLINE_CC #else # define EXECUTE_DATA_D zend_execute_data* execute_data # define EXECUTE_DATA_C execute_data # define EXECUTE_DATA_DC , EXECUTE_DATA_D # define EXECUTE_DATA_CC , EXECUTE_DATA_C # define NO_EXECUTE_DATA_CC , NULL +# define OPLINE_D const zend_op* opline +# define OPLINE_C opline +# define OPLINE_DC , OPLINE_D +# define OPLINE_CC , OPLINE_C #endif #if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) @@ -93,15 +99,15 @@ typedef int (ZEND_FASTCALL *incdec_t)(zval *); -#define get_zval_ptr(op_type, node, should_free, type) _get_zval_ptr(op_type, node, should_free, type EXECUTE_DATA_CC) -#define get_zval_ptr_deref(op_type, node, should_free, type) _get_zval_ptr_deref(op_type, node, should_free, type EXECUTE_DATA_CC) -#define get_zval_ptr_r(op_type, node, should_free) _get_zval_ptr_r(op_type, node, should_free EXECUTE_DATA_CC) -#define get_zval_ptr_r_deref(op_type, node, should_free) _get_zval_ptr_r_deref(op_type, node, should_free EXECUTE_DATA_CC) -#define get_zval_ptr_undef(op_type, node, should_free, type) _get_zval_ptr_undef(op_type, node, should_free, type EXECUTE_DATA_CC) +#define get_zval_ptr(op_type, node, should_free, type) _get_zval_ptr(op_type, node, should_free, type EXECUTE_DATA_CC OPLINE_CC) +#define get_zval_ptr_deref(op_type, node, should_free, type) _get_zval_ptr_deref(op_type, node, should_free, type EXECUTE_DATA_CC OPLINE_CC) +#define get_zval_ptr_undef(op_type, node, should_free, type) _get_zval_ptr_undef(op_type, node, should_free, type EXECUTE_DATA_CC OPLINE_CC) +#define get_op_data_zval_ptr_r(op_type, node, should_free) _get_op_data_zval_ptr_r(op_type, node, should_free EXECUTE_DATA_CC OPLINE_CC) +#define get_op_data_zval_ptr_deref_r(op_type, node, should_free) _get_op_data_zval_ptr_deref_r(op_type, node, should_free EXECUTE_DATA_CC OPLINE_CC) #define get_zval_ptr_ptr(op_type, node, should_free, type) _get_zval_ptr_ptr(op_type, node, should_free, type EXECUTE_DATA_CC) #define get_zval_ptr_ptr_undef(op_type, node, should_free, type) _get_zval_ptr_ptr(op_type, node, should_free, type EXECUTE_DATA_CC) -#define get_obj_zval_ptr(op_type, node, should_free, type) _get_obj_zval_ptr(op_type, node, should_free, type EXECUTE_DATA_CC) -#define get_obj_zval_ptr_undef(op_type, node, should_free, type) _get_obj_zval_ptr_undef(op_type, node, should_free, type EXECUTE_DATA_CC) +#define get_obj_zval_ptr(op_type, node, should_free, type) _get_obj_zval_ptr(op_type, node, should_free, type EXECUTE_DATA_CC OPLINE_CC) +#define get_obj_zval_ptr_undef(op_type, node, should_free, type) _get_obj_zval_ptr_undef(op_type, node, should_free, type EXECUTE_DATA_CC OPLINE_CC) #define get_obj_zval_ptr_ptr(op_type, node, should_free, type) _get_obj_zval_ptr_ptr(op_type, node, should_free, type EXECUTE_DATA_CC) #define RETURN_VALUE_USED(opline) ((opline)->result_type != IS_UNUSED) @@ -128,14 +134,19 @@ ZEND_API const zend_internal_function zend_pass_function = { #undef zval_ptr_dtor #define zval_ptr_dtor(zv) i_zval_ptr_dtor(zv ZEND_FILE_LINE_CC) -#define READY_TO_DESTROY(zv) \ - (UNEXPECTED(zv) && Z_REFCOUNTED_P(zv) && Z_REFCOUNT_P(zv) == 1) - -#define EXTRACT_ZVAL_PTR(zv) do { \ - zval *__zv = (zv); \ - if (EXPECTED(Z_TYPE_P(__zv) == IS_INDIRECT)) { \ - ZVAL_COPY(__zv, Z_INDIRECT_P(__zv)); \ - } \ +#define FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(free_op, result) do { \ + zval *__container_to_free = (free_op); \ + if (UNEXPECTED(__container_to_free) \ + && EXPECTED(Z_REFCOUNTED_P(__container_to_free))) { \ + zend_refcounted *__ref = Z_COUNTED_P(__container_to_free); \ + if (UNEXPECTED(!GC_DELREF(__ref))) { \ + zval *__zv = (result); \ + if (EXPECTED(Z_TYPE_P(__zv) == IS_INDIRECT)) { \ + ZVAL_COPY(__zv, Z_INDIRECT_P(__zv)); \ + } \ + rc_dtor_func(__ref); \ + } \ + } \ } while (0) #define FREE_OP(should_free) \ @@ -159,12 +170,9 @@ ZEND_API const zend_internal_function zend_pass_function = { #define ZEND_VM_STACK_PAGE_SIZE (ZEND_VM_STACK_PAGE_SLOTS * sizeof(zval)) -#define ZEND_VM_STACK_FREE_PAGE_SIZE \ - ((ZEND_VM_STACK_PAGE_SLOTS - ZEND_VM_STACK_HEADER_SLOTS) * sizeof(zval)) - -#define ZEND_VM_STACK_PAGE_ALIGNED_SIZE(size) \ +#define ZEND_VM_STACK_PAGE_ALIGNED_SIZE(size, page_size) \ (((size) + ZEND_VM_STACK_HEADER_SLOTS * sizeof(zval) \ - + (ZEND_VM_STACK_PAGE_SIZE - 1)) & ~(ZEND_VM_STACK_PAGE_SIZE - 1)) + + ((page_size) - 1)) & ~((page_size) - 1)) static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend_vm_stack prev) { zend_vm_stack page = (zend_vm_stack)emalloc(size); @@ -177,12 +185,23 @@ static zend_always_inline zend_vm_stack zend_vm_stack_new_page(size_t size, zend ZEND_API void zend_vm_stack_init(void) { + EG(vm_stack_page_size) = ZEND_VM_STACK_PAGE_SIZE; EG(vm_stack) = zend_vm_stack_new_page(ZEND_VM_STACK_PAGE_SIZE, NULL); EG(vm_stack)->top++; EG(vm_stack_top) = EG(vm_stack)->top; EG(vm_stack_end) = EG(vm_stack)->end; } +ZEND_API void zend_vm_stack_init_ex(size_t page_size) +{ + /* page_size must be a power of 2 */ + ZEND_ASSERT(page_size > 0 && (page_size & (page_size - 1)) == 0); + EG(vm_stack_page_size) = page_size; + EG(vm_stack) = zend_vm_stack_new_page(page_size, NULL); + EG(vm_stack_top) = EG(vm_stack)->top; + EG(vm_stack_end) = EG(vm_stack)->end; +} + ZEND_API void zend_vm_stack_destroy(void) { zend_vm_stack stack = EG(vm_stack); @@ -202,8 +221,8 @@ ZEND_API void* zend_vm_stack_extend(size_t size) stack = EG(vm_stack); stack->top = EG(vm_stack_top); EG(vm_stack) = stack = zend_vm_stack_new_page( - EXPECTED(size < ZEND_VM_STACK_FREE_PAGE_SIZE) ? - ZEND_VM_STACK_PAGE_SIZE : ZEND_VM_STACK_PAGE_ALIGNED_SIZE(size), + EXPECTED(size < EG(vm_stack_page_size) - (ZEND_VM_STACK_HEADER_SLOTS * sizeof(zval))) ? + EG(vm_stack_page_size) : ZEND_VM_STACK_PAGE_ALIGNED_SIZE(size, EG(vm_stack_page_size)), stack); ptr = stack->top; EG(vm_stack_top) = (void*)(((char*)ptr) + size); @@ -251,7 +270,7 @@ static zend_never_inline ZEND_COLD void zval_undefined_cv(uint32_t var EXECUTE_D } } -static zend_never_inline zval *_get_zval_cv_lookup(zval *ptr, uint32_t var, int type EXECUTE_DATA_DC) +static zend_never_inline ZEND_COLD zval *_get_zval_cv_lookup(zval *ptr, uint32_t var, int type EXECUTE_DATA_DC) { switch (type) { case BP_VAR_R: @@ -271,52 +290,50 @@ static zend_never_inline zval *_get_zval_cv_lookup(zval *ptr, uint32_t var, int return ptr; } -static zend_always_inline zval *_get_zval_cv_lookup_BP_VAR_R(zval *ptr, uint32_t var EXECUTE_DATA_DC) +static zend_never_inline ZEND_COLD zval *_get_zval_cv_lookup_BP_VAR_R(zval *ptr, uint32_t var EXECUTE_DATA_DC) { zval_undefined_cv(var EXECUTE_DATA_CC); return &EG(uninitialized_zval); } -static zend_always_inline zval *_get_zval_cv_lookup_BP_VAR_UNSET(zval *ptr, uint32_t var EXECUTE_DATA_DC) +static zend_never_inline ZEND_COLD zval *_get_zval_cv_lookup_BP_VAR_UNSET(zval *ptr, uint32_t var EXECUTE_DATA_DC) { zval_undefined_cv(var EXECUTE_DATA_CC); return &EG(uninitialized_zval); } -static zend_always_inline zval *_get_zval_cv_lookup_BP_VAR_RW(zval *ptr, uint32_t var EXECUTE_DATA_DC) +static zend_never_inline ZEND_COLD zval *_get_zval_cv_lookup_BP_VAR_RW(zval *ptr, uint32_t var EXECUTE_DATA_DC) { ZVAL_NULL(ptr); zval_undefined_cv(var EXECUTE_DATA_CC); return ptr; } -static zend_always_inline zval *_get_zval_cv_lookup_BP_VAR_W(zval *ptr, uint32_t var EXECUTE_DATA_DC) -{ - ZVAL_NULL(ptr); - return ptr; -} - static zend_always_inline zval *_get_zval_ptr_cv(uint32_t var, int type EXECUTE_DATA_DC) { zval *ret = EX_VAR(var); if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) { - return _get_zval_cv_lookup(ret, var, type EXECUTE_DATA_CC); + if (type == BP_VAR_W) { + ZVAL_NULL(ret); + } else { + return _get_zval_cv_lookup(ret, var, type EXECUTE_DATA_CC); + } } return ret; } -static zend_always_inline zval *_get_zval_ptr_cv_undef(uint32_t var EXECUTE_DATA_DC) -{ - return EX_VAR(var); -} - static zend_always_inline zval *_get_zval_ptr_cv_deref(uint32_t var, int type EXECUTE_DATA_DC) { zval *ret = EX_VAR(var); if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) { - return _get_zval_cv_lookup(ret, var, type EXECUTE_DATA_CC); + if (type == BP_VAR_W) { + ZVAL_NULL(ret); + return ret; + } else { + return _get_zval_cv_lookup(ret, var, type EXECUTE_DATA_CC); + } } ZVAL_DEREF(ret); return ret; @@ -405,38 +422,24 @@ static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_W(uint32_t var EXECUTE_D zval *ret = EX_VAR(var); if (Z_TYPE_P(ret) == IS_UNDEF) { - return _get_zval_cv_lookup_BP_VAR_W(ret, var EXECUTE_DATA_CC); + ZVAL_NULL(ret); } return ret; } -static zend_always_inline zval *_get_zval_ptr_cv_undef_BP_VAR_W(uint32_t var EXECUTE_DATA_DC) -{ - return EX_VAR(var); -} - -static zend_always_inline zval *_get_zval_ptr_cv_undef_BP_VAR_RW(uint32_t var EXECUTE_DATA_DC) -{ - return EX_VAR(var); -} - -static zend_always_inline zval *_get_zval_ptr_cv_undef_BP_VAR_UNSET(uint32_t var EXECUTE_DATA_DC) -{ - return EX_VAR(var); -} - static zend_always_inline zval *_get_zval_ptr_cv_deref_BP_VAR_W(uint32_t var EXECUTE_DATA_DC) { zval *ret = EX_VAR(var); if (Z_TYPE_P(ret) == IS_UNDEF) { - return _get_zval_cv_lookup_BP_VAR_W(ret, var EXECUTE_DATA_CC); + ZVAL_NULL(ret); + return ret; } ZVAL_DEREF(ret); return ret; } -static zend_always_inline zval *_get_zval_ptr(int op_type, znode_op node, zend_free_op *should_free, int type EXECUTE_DATA_DC) +static zend_always_inline zval *_get_zval_ptr(int op_type, znode_op node, zend_free_op *should_free, int type EXECUTE_DATA_DC OPLINE_DC) { if (op_type & (IS_TMP_VAR|IS_VAR)) { if (op_type == IS_TMP_VAR) { @@ -448,7 +451,7 @@ static zend_always_inline zval *_get_zval_ptr(int op_type, znode_op node, zend_f } else { *should_free = NULL; if (op_type == IS_CONST) { - return EX_CONSTANT(node); + return RT_CONSTANT(opline, node); } else if (op_type == IS_CV) { return _get_zval_ptr_cv(node.var, type EXECUTE_DATA_CC); } else { @@ -457,7 +460,7 @@ static zend_always_inline zval *_get_zval_ptr(int op_type, znode_op node, zend_f } } -static zend_always_inline zval *_get_zval_ptr_r(int op_type, znode_op node, zend_free_op *should_free EXECUTE_DATA_DC) +static zend_always_inline zval *_get_op_data_zval_ptr_r(int op_type, znode_op node, zend_free_op *should_free EXECUTE_DATA_DC OPLINE_DC) { if (op_type & (IS_TMP_VAR|IS_VAR)) { if (op_type == IS_TMP_VAR) { @@ -469,7 +472,7 @@ static zend_always_inline zval *_get_zval_ptr_r(int op_type, znode_op node, zend } else { *should_free = NULL; if (op_type == IS_CONST) { - return EX_CONSTANT(node); + return RT_CONSTANT(opline + 1, node); } else if (op_type == IS_CV) { return _get_zval_ptr_cv_BP_VAR_R(node.var EXECUTE_DATA_CC); } else { @@ -478,7 +481,7 @@ static zend_always_inline zval *_get_zval_ptr_r(int op_type, znode_op node, zend } } -static zend_always_inline zval *_get_zval_ptr_deref(int op_type, znode_op node, zend_free_op *should_free, int type EXECUTE_DATA_DC) +static zend_always_inline zval *_get_zval_ptr_deref(int op_type, znode_op node, zend_free_op *should_free, int type EXECUTE_DATA_DC OPLINE_DC) { if (op_type & (IS_TMP_VAR|IS_VAR)) { if (op_type == IS_TMP_VAR) { @@ -490,7 +493,7 @@ static zend_always_inline zval *_get_zval_ptr_deref(int op_type, znode_op node, } else { *should_free = NULL; if (op_type == IS_CONST) { - return EX_CONSTANT(node); + return RT_CONSTANT(opline, node); } else if (op_type == IS_CV) { return _get_zval_ptr_cv_deref(node.var, type EXECUTE_DATA_CC); } else { @@ -499,7 +502,7 @@ static zend_always_inline zval *_get_zval_ptr_deref(int op_type, znode_op node, } } -static zend_always_inline zval *_get_zval_ptr_r_deref(int op_type, znode_op node, zend_free_op *should_free EXECUTE_DATA_DC) +static zend_always_inline zval *_get_op_data_zval_ptr_deref_r(int op_type, znode_op node, zend_free_op *should_free EXECUTE_DATA_DC OPLINE_DC) { if (op_type & (IS_TMP_VAR|IS_VAR)) { if (op_type == IS_TMP_VAR) { @@ -511,7 +514,7 @@ static zend_always_inline zval *_get_zval_ptr_r_deref(int op_type, znode_op node } else { *should_free = NULL; if (op_type == IS_CONST) { - return EX_CONSTANT(node); + return RT_CONSTANT(opline + 1, node); } else if (op_type == IS_CV) { return _get_zval_ptr_cv_deref_BP_VAR_R(node.var EXECUTE_DATA_CC); } else { @@ -520,7 +523,7 @@ static zend_always_inline zval *_get_zval_ptr_r_deref(int op_type, znode_op node } } -static zend_always_inline zval *_get_zval_ptr_undef(int op_type, znode_op node, zend_free_op *should_free, int type EXECUTE_DATA_DC) +static zend_always_inline zval *_get_zval_ptr_undef(int op_type, znode_op node, zend_free_op *should_free, int type EXECUTE_DATA_DC OPLINE_DC) { if (op_type & (IS_TMP_VAR|IS_VAR)) { if (op_type == IS_TMP_VAR) { @@ -532,9 +535,9 @@ static zend_always_inline zval *_get_zval_ptr_undef(int op_type, znode_op node, } else { *should_free = NULL; if (op_type == IS_CONST) { - return EX_CONSTANT(node); + return RT_CONSTANT(opline, node); } else if (op_type == IS_CV) { - return _get_zval_ptr_cv_undef(node.var EXECUTE_DATA_CC); + return EX_VAR(node.var); } else { return NULL; } @@ -565,12 +568,7 @@ static inline zval *_get_zval_ptr_ptr(int op_type, znode_op node, zend_free_op * } } -static zend_always_inline zval *_get_obj_zval_ptr_unused(EXECUTE_DATA_D) -{ - return &EX(This); -} - -static inline zval *_get_obj_zval_ptr(int op_type, znode_op op, zend_free_op *should_free, int type EXECUTE_DATA_DC) +static inline zval *_get_obj_zval_ptr(int op_type, znode_op op, zend_free_op *should_free, int type EXECUTE_DATA_DC OPLINE_DC) { if (op_type == IS_UNUSED) { *should_free = NULL; @@ -579,7 +577,7 @@ static inline zval *_get_obj_zval_ptr(int op_type, znode_op op, zend_free_op *sh return get_zval_ptr(op_type, op, should_free, type); } -static inline zval *_get_obj_zval_ptr_undef(int op_type, znode_op op, zend_free_op *should_free, int type EXECUTE_DATA_DC) +static inline zval *_get_obj_zval_ptr_undef(int op_type, znode_op op, zend_free_op *should_free, int type EXECUTE_DATA_DC OPLINE_DC) { if (op_type == IS_UNUSED) { *should_free = NULL; @@ -608,13 +606,13 @@ static inline void zend_assign_to_variable_reference(zval *variable_ptr, zval *v } ref = Z_REF_P(value_ptr); - GC_REFCOUNT(ref)++; + GC_ADDREF(ref); if (Z_REFCOUNTED_P(variable_ptr)) { zend_refcounted *garbage = Z_COUNTED_P(variable_ptr); - if (--GC_REFCOUNT(garbage) == 0) { + if (GC_DELREF(garbage) == 0) { ZVAL_REF(variable_ptr, ref); - zval_dtor_func(garbage); + rc_dtor_func(garbage); return; } else { gc_check_possible_root(garbage); @@ -623,20 +621,93 @@ static inline void zend_assign_to_variable_reference(zval *variable_ptr, zval *v ZVAL_REF(variable_ptr, ref); } +static zend_never_inline ZEND_COLD int zend_wrong_assign_to_variable_reference(zval *variable_ptr, zval *value_ptr, zend_uchar value_type OPLINE_DC EXECUTE_DATA_DC) +{ + zend_error(E_NOTICE, "Only variables should be assigned by reference"); + if (UNEXPECTED(EG(exception) != NULL)) { + return 0; + } + + value_ptr = zend_assign_to_variable(variable_ptr, value_ptr, value_type); + + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_COPY(EX_VAR(opline->result.var), value_ptr); + } + return 1; +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_wrong_property_assignment(zval *property OPLINE_DC EXECUTE_DATA_DC) +{ + zend_string *tmp_property_name; + zend_string *property_name = zval_get_tmp_string(property, &tmp_property_name); + zend_error(E_WARNING, "Attempt to assign property '%s' of non-object", ZSTR_VAL(property_name)); + zend_tmp_string_release(tmp_property_name); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } +} + /* this should modify object only if it's empty */ -static inline int make_real_object(zval *object) +static zend_never_inline ZEND_COLD int ZEND_FASTCALL make_real_object(zval *object, zval *property OPLINE_DC EXECUTE_DATA_DC) { - if (UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { - if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE)) { - /* nothing to destroy */ - } else if (EXPECTED((Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) { - zval_ptr_dtor_nogc(object); - } else { - return 0; + zend_object *obj; + + if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE)) { + /* nothing to destroy */ + } else if (EXPECTED((Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) { + zval_ptr_dtor_nogc(object); + } else { + if (opline->op1_type != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) { + zend_string *tmp_property_name; + zend_string *property_name = zval_get_tmp_string(property, &tmp_property_name); + + if (opline->opcode == ZEND_PRE_INC_OBJ + || opline->opcode == ZEND_PRE_DEC_OBJ + || opline->opcode == ZEND_POST_INC_OBJ + || opline->opcode == ZEND_POST_DEC_OBJ) { + zend_error(E_WARNING, "Attempt to increment/decrement property '%s' of non-object", ZSTR_VAL(property_name)); + } else { + zend_error(E_WARNING, "Attempt to assign property '%s' of non-object", ZSTR_VAL(property_name)); + } + zend_tmp_string_release(tmp_property_name); + } + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); } - object_init(object); - zend_error(E_WARNING, "Creating default object from empty value"); + return 0; } + object_init(object); + Z_ADDREF_P(object); + obj = Z_OBJ_P(object); + zend_error(E_WARNING, "Creating default object from empty value"); + if (GC_REFCOUNT(obj) == 1) { + /* the enclosing container was deleted, obj is unreferenced */ + OBJ_RELEASE(obj); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); + } + return 0; + } + Z_DELREF_P(object); + return 1; +} + +static zend_never_inline ZEND_COLD int ZEND_FASTCALL make_real_object_rw(zval *object, zval *property OPLINE_DC) +{ + if (EXPECTED(Z_TYPE_P(object) <= IS_FALSE)) { + /* nothing to destroy */ + } else if (EXPECTED((Z_TYPE_P(object) == IS_STRING && Z_STRLEN_P(object) == 0))) { + zval_ptr_dtor_nogc(object); + } else { + if (opline->op1_type != IS_VAR || EXPECTED(!Z_ISERROR_P(object))) { + zend_string *tmp_property_name; + zend_string *property_name = zval_get_tmp_string(property, &tmp_property_name); + zend_error(E_WARNING, "Attempt to modify property '%s' of non-object", ZSTR_VAL(property_name)); + zend_tmp_string_release(tmp_property_name); + } + return 0; + } + object_init(object); return 1; } @@ -744,7 +815,7 @@ static ZEND_COLD void zend_verify_arg_error( static int is_null_constant(zend_class_entry *scope, zval *default_value) { - if (Z_CONSTANT_P(default_value)) { + if (Z_TYPE_P(default_value) == IS_CONSTANT_AST) { zval constant; ZVAL_COPY(&constant, default_value); @@ -754,7 +825,7 @@ static int is_null_constant(zend_class_entry *scope, zval *default_value) if (Z_TYPE(constant) == IS_NULL) { return 1; } - zval_ptr_dtor(&constant); + zval_ptr_dtor_nogc(&constant); } return 0; } @@ -883,6 +954,7 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a return 1; } + ce = NULL; if (UNEXPECTED(!zend_check_type(cur_arg_info->type, arg, &ce, cache_slot, default_value, zf->common.scope, 0))) { zend_verify_arg_error(zf, cur_arg_info, arg_num, ce, arg); return 0; @@ -1017,7 +1089,7 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval * } } -static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **cache_slot) +static ZEND_COLD int zend_verify_missing_return_type(const zend_function *zf, void **cache_slot) { zend_arg_info *ret_info = zf->common.arg_info - 1; @@ -1039,14 +1111,31 @@ static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **c return 1; } -static zend_never_inline void zend_assign_to_object_dim(zval *object, zval *dim, zval *value) +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_object_as_array(void) +{ + zend_throw_error(NULL, "Cannot use object as array"); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_illegal_offset(void) +{ + zend_error(E_WARNING, "Illegal offset type"); +} + +static zend_never_inline void zend_assign_to_object_dim(zval *object, zval *dim, zval *value OPLINE_DC EXECUTE_DATA_DC) { if (UNEXPECTED(!Z_OBJ_HT_P(object)->write_dimension)) { - zend_throw_error(NULL, "Cannot use object as array"); + zend_use_object_as_array(); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } return; } Z_OBJ_HT_P(object)->write_dimension(object, dim, value); + + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_COPY(EX_VAR(opline->result.var), value); + } } static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *property, zval *value, zval *retval, binary_op_type binary_op EXECUTE_DATA_DC) @@ -1076,7 +1165,7 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval * } zval_ptr_dtor(&res); } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); + zend_use_object_as_array(); if (retval) { ZVAL_NULL(retval); } @@ -1110,11 +1199,11 @@ try_again: dim = Z_REFVAL_P(dim); goto try_again; default: - zend_error(E_WARNING, "Illegal offset type"); + zend_illegal_offset(); break; } - offset = _zval_get_long_func(dim); + offset = zval_get_long_func(dim); } else { offset = Z_LVAL_P(dim); } @@ -1129,6 +1218,10 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(EXECUTE_DATA_D) const zend_op *end; uint32_t var; + if (UNEXPECTED(EG(exception) != NULL)) { + return; + } + switch (opline->opcode) { case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: @@ -1148,6 +1241,7 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(EXECUTE_DATA_D) case ZEND_FETCH_DIM_RW: case ZEND_FETCH_DIM_FUNC_ARG: case ZEND_FETCH_DIM_UNSET: + case ZEND_FETCH_LIST_W: /* TODO: Encode the "reason" into opline->extended_value??? */ var = opline->result.var; opline++; @@ -1190,6 +1284,7 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(EXECUTE_DATA_D) case ZEND_FETCH_DIM_RW: case ZEND_FETCH_DIM_FUNC_ARG: case ZEND_FETCH_DIM_UNSET: + case ZEND_FETCH_LIST_W: case ZEND_ASSIGN_DIM: msg = "Cannot use string offset as an array"; break; @@ -1219,6 +1314,7 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(EXECUTE_DATA_D) break; case ZEND_SEND_REF: case ZEND_SEND_VAR_EX: + case ZEND_SEND_FUNC_ARG: msg = "Only variables can be passed by reference"; break; case ZEND_FE_RESET_RW: @@ -1238,12 +1334,43 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(EXECUTE_DATA_D) EMPTY_SWITCH_DEFAULT_CASE(); } ZEND_ASSERT(msg != NULL); - zend_throw_error(NULL, msg); + zend_throw_error(NULL, "%s", msg); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_wrong_property_read(zval *property) +{ + zend_string *tmp_property_name; + zend_string *property_name = zval_get_tmp_string(property, &tmp_property_name); + zend_error(E_NOTICE, "Trying to get property '%s' of non-object", ZSTR_VAL(property_name)); + zend_tmp_string_release(tmp_property_name); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_wrong_property_unset(zval *property) +{ + zend_string *tmp_property_name; + zend_string *property_name = zval_get_tmp_string(property, &tmp_property_name); + zend_error(E_NOTICE, "Trying to unset property '%s' of non-object", ZSTR_VAL(property_name)); + zend_tmp_string_release(tmp_property_name); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_wrong_property_check(zval *property) +{ + zend_string *tmp_property_name; + zend_string *property_name = zval_get_tmp_string(property, &tmp_property_name); + zend_error(E_NOTICE, "Trying to check property '%s' of non-object", ZSTR_VAL(property_name)); + zend_tmp_string_release(tmp_property_name); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_deprecated_function(const zend_function *fbc) +{ + zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated", + fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "", + fbc->common.scope ? "::" : "", + ZSTR_VAL(fbc->common.function_name)); } -static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value, zval *result EXECUTE_DATA_DC) +static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, zval *value OPLINE_DC EXECUTE_DATA_DC) { - zend_string *old_str; zend_uchar c; size_t string_len; zend_long offset; @@ -1252,19 +1379,19 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, if (offset < -(zend_long)Z_STRLEN_P(str)) { /* Error on negative offset */ zend_error(E_WARNING, "Illegal string offset: " ZEND_LONG_FMT, offset); - if (result) { - ZVAL_NULL(result); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); } return; } if (Z_TYPE_P(value) != IS_STRING) { /* Convert to string, just the time to pick the 1st byte */ - zend_string *tmp = zval_get_string(value); + zend_string *tmp = zval_get_string_func(value); string_len = ZSTR_LEN(tmp); c = (zend_uchar)ZSTR_VAL(tmp)[0]; - zend_string_release(tmp); + zend_string_release_ex(tmp, 0); } else { string_len = Z_STRLEN_P(value); c = (zend_uchar)Z_STRVAL_P(value)[0]; @@ -1273,8 +1400,8 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, if (string_len == 0) { /* Error on empty input string */ zend_error(E_WARNING, "Cannot assign an empty string to a string offset"); - if (result) { - ZVAL_NULL(result); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); } return; } @@ -1286,29 +1413,27 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, if ((size_t)offset >= Z_STRLEN_P(str)) { /* Extend string if needed */ zend_long old_len = Z_STRLEN_P(str); - Z_STR_P(str) = zend_string_extend(Z_STR_P(str), offset + 1, 0); - Z_TYPE_INFO_P(str) = IS_STRING_EX; + ZVAL_NEW_STR(str, zend_string_extend(Z_STR_P(str), offset + 1, 0)); memset(Z_STRVAL_P(str) + old_len, ' ', offset - old_len); Z_STRVAL_P(str)[offset+1] = 0; } else if (!Z_REFCOUNTED_P(str)) { - old_str = Z_STR_P(str); - Z_STR_P(str) = zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0); - Z_TYPE_INFO_P(str) = IS_STRING_EX; - zend_string_release(old_str); + ZVAL_NEW_STR(str, zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0)); + } else if (Z_REFCOUNT_P(str) > 1) { + Z_DELREF_P(str); + ZVAL_NEW_STR(str, zend_string_init(Z_STRVAL_P(str), Z_STRLEN_P(str), 0)); } else { - SEPARATE_STRING(str); zend_string_forget_hash_val(Z_STR_P(str)); } Z_STRVAL_P(str)[offset] = c; - if (result) { + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { /* Return the new character */ - ZVAL_INTERNED_STR(result, ZSTR_CHAR(c)); + ZVAL_INTERNED_STR(EX_VAR(opline->result.var), ZSTR_CHAR(c)); } } -static zend_never_inline void zend_post_incdec_overloaded_property(zval *object, zval *property, void **cache_slot, int inc, zval *result) +static zend_never_inline void zend_post_incdec_overloaded_property(zval *object, zval *property, void **cache_slot, int inc OPLINE_DC EXECUTE_DATA_DC) { if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { zval rv, obj; @@ -1320,7 +1445,7 @@ static zend_never_inline void zend_post_incdec_overloaded_property(zval *object, z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv); if (UNEXPECTED(EG(exception))) { OBJ_RELEASE(Z_OBJ(obj)); - ZVAL_UNDEF(result); + ZVAL_UNDEF(EX_VAR(opline->result.var)); return; } @@ -1333,12 +1458,8 @@ static zend_never_inline void zend_post_incdec_overloaded_property(zval *object, ZVAL_COPY_VALUE(z, value); } - if (UNEXPECTED(Z_TYPE_P(z) == IS_REFERENCE)) { - ZVAL_COPY(result, Z_REFVAL_P(z)); - } else { - ZVAL_COPY(result, z); - } - ZVAL_DUP(&z_copy, result); + ZVAL_COPY_DEREF(&z_copy, z); + ZVAL_COPY(EX_VAR(opline->result.var), &z_copy); if (inc) { increment_function(&z_copy); } else { @@ -1350,24 +1471,25 @@ static zend_never_inline void zend_post_incdec_overloaded_property(zval *object, zval_ptr_dtor(z); } else { zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(result); + ZVAL_NULL(EX_VAR(opline->result.var)); } } -static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, zval *property, void **cache_slot, int inc, zval *result) +static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, zval *property, void **cache_slot, int inc OPLINE_DC EXECUTE_DATA_DC) { zval rv; if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, *zptr, obj; + zval *z, obj; + zval z_copy; ZVAL_OBJ(&obj, Z_OBJ_P(object)); Z_ADDREF(obj); - zptr = z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv); + z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv); if (UNEXPECTED(EG(exception))) { OBJ_RELEASE(Z_OBJ(obj)); - if (result) { - ZVAL_UNDEF(result); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); } return; } @@ -1381,32 +1503,31 @@ static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, } ZVAL_COPY_VALUE(z, value); } - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); + ZVAL_COPY_DEREF(&z_copy, z); if (inc) { - increment_function(z); + increment_function(&z_copy); } else { - decrement_function(z); + decrement_function(&z_copy); } - if (UNEXPECTED(result)) { - ZVAL_COPY(result, z); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_COPY(EX_VAR(opline->result.var), &z_copy); } - Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot); + Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot); OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(zptr); + zval_ptr_dtor(&z_copy); + zval_ptr_dtor(z); } else { zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - if (UNEXPECTED(result)) { - ZVAL_NULL(result); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); } } } -static zend_never_inline void zend_assign_op_overloaded_property(zval *object, zval *property, void **cache_slot, zval *value, binary_op_type binary_op, zval *result) +static zend_never_inline void zend_assign_op_overloaded_property(zval *object, zval *property, void **cache_slot, zval *value, binary_op_type binary_op OPLINE_DC EXECUTE_DATA_DC) { zval *z; - zval rv, obj; - zval *zptr; + zval rv, obj, res; ZVAL_OBJ(&obj, Z_OBJ_P(object)); Z_ADDREF(obj); @@ -1414,8 +1535,8 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z z = Z_OBJ_HT(obj)->read_property(&obj, property, BP_VAR_R, cache_slot, &rv); if (UNEXPECTED(EG(exception))) { OBJ_RELEASE(Z_OBJ(obj)); - if (result) { - ZVAL_UNDEF(result); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); } return; } @@ -1428,19 +1549,17 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z } ZVAL_COPY_VALUE(z, value); } - zptr = z; - ZVAL_DEREF(z); - SEPARATE_ZVAL_NOREF(z); - binary_op(z, z, value); - Z_OBJ_HT(obj)->write_property(&obj, property, z, cache_slot); - if (UNEXPECTED(result)) { - ZVAL_COPY(result, z); + binary_op(&res, z, value); + Z_OBJ_HT(obj)->write_property(&obj, property, &res, cache_slot); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_COPY(EX_VAR(opline->result.var), &res); } - zval_ptr_dtor(zptr); + zval_ptr_dtor(z); + zval_ptr_dtor(&res); } else { zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(result)) { - ZVAL_NULL(result); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); } } OBJ_RELEASE(Z_OBJ(obj)); @@ -1475,11 +1594,10 @@ static zend_always_inline HashTable *zend_get_target_symbol_table(int fetch_type { HashTable *ht; - if (EXPECTED(fetch_type == ZEND_FETCH_GLOBAL_LOCK) || - EXPECTED(fetch_type == ZEND_FETCH_GLOBAL)) { + if (EXPECTED(fetch_type & (ZEND_FETCH_GLOBAL_LOCK | ZEND_FETCH_GLOBAL))) { ht = &EG(symbol_table); } else { - ZEND_ASSERT(fetch_type == ZEND_FETCH_LOCAL); + ZEND_ASSERT(fetch_type & ZEND_FETCH_LOCAL); if (!(EX_CALL_INFO() & ZEND_CALL_HAS_SYMBOL_TABLE)) { zend_rebuild_symbol_table(); } @@ -1488,9 +1606,82 @@ static zend_always_inline HashTable *zend_get_target_symbol_table(int fetch_type return ht; } +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_offset(zend_long lval) +{ + zend_error(E_NOTICE, "Undefined offset: " ZEND_LONG_FMT, lval); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_index(const zend_string *offset) +{ + zend_error(E_NOTICE, "Undefined index: %s", ZSTR_VAL(offset)); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_undefined_method(const zend_class_entry *ce, const zend_string *method) +{ + zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(method)); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_invalid_method_call(zval *object, zval *function_name) +{ + zend_throw_error(NULL, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_non_static_method_call(const zend_function *fbc) +{ + if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { + zend_error(E_DEPRECATED, + "Non-static method %s::%s() should not be called statically", + ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); + } else { + zend_throw_error( + zend_ce_error, + "Non-static method %s::%s() cannot be called statically", + ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); + } +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_param_must_be_ref(const zend_function *func, uint32_t arg_num) +{ + zend_error(E_WARNING, "Parameter %d to %s%s%s() expected to be a reference, value given", + arg_num, + func->common.scope ? ZSTR_VAL(func->common.scope->name) : "", + func->common.scope ? "::" : "", + ZSTR_VAL(func->common.function_name)); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_scalar_as_array(void) +{ + zend_error(E_WARNING, "Cannot use a scalar value as an array"); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_cannot_add_element(void) +{ + zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied"); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_resource_as_offset(const zval *dim) +{ + zend_error(E_NOTICE, "Resource ID#%d used as offset, casting to integer (%d)", Z_RES_HANDLE_P(dim), Z_RES_HANDLE_P(dim)); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_new_element_for_string(void) +{ + zend_throw_error(NULL, "[] operator not supported for strings"); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_access_undefined_propery_in_overloaded_object(void) +{ + zend_throw_error(NULL, "Cannot access undefined property for object with overloaded property access"); +} + +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_unsupported_property_reference(void) +{ + zend_error(E_WARNING, "This object doesn't support property references"); +} + static zend_always_inline zval *zend_fetch_dimension_address_inner(HashTable *ht, const zval *dim, int dim_type, int type EXECUTE_DATA_DC) { - zval *retval; + zval *retval = NULL; zend_string *offset_key; zend_ulong hval; @@ -1503,14 +1694,14 @@ num_index: num_undef: switch (type) { case BP_VAR_R: - zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval); + zend_undefined_offset(hval); /* break missing intentionally */ case BP_VAR_UNSET: case BP_VAR_IS: retval = &EG(uninitialized_zval); break; case BP_VAR_RW: - zend_error(E_NOTICE,"Undefined offset: " ZEND_LONG_FMT, hval); + zend_undefined_offset(hval); retval = zend_hash_index_update(ht, hval, &EG(uninitialized_zval)); break; case BP_VAR_W: @@ -1525,7 +1716,7 @@ num_undef: } } str_index: - retval = zend_hash_find(ht, offset_key); + retval = zend_hash_find_ex(ht, offset_key, dim_type == IS_CONST); if (retval) { /* support for $GLOBALS[...] */ if (UNEXPECTED(Z_TYPE_P(retval) == IS_INDIRECT)) { @@ -1533,14 +1724,14 @@ str_index: if (UNEXPECTED(Z_TYPE_P(retval) == IS_UNDEF)) { switch (type) { case BP_VAR_R: - zend_error(E_NOTICE, "Undefined index: %s", ZSTR_VAL(offset_key)); + zend_undefined_index(offset_key); /* break missing intentionally */ case BP_VAR_UNSET: case BP_VAR_IS: retval = &EG(uninitialized_zval); break; case BP_VAR_RW: - zend_error(E_NOTICE,"Undefined index: %s", ZSTR_VAL(offset_key)); + zend_undefined_index(offset_key); /* break missing intentionally */ case BP_VAR_W: ZVAL_NULL(retval); @@ -1551,14 +1742,14 @@ str_index: } else { switch (type) { case BP_VAR_R: - zend_error(E_NOTICE, "Undefined index: %s", ZSTR_VAL(offset_key)); + zend_undefined_index(offset_key); /* break missing intentionally */ case BP_VAR_UNSET: case BP_VAR_IS: retval = &EG(uninitialized_zval); break; case BP_VAR_RW: - zend_error(E_NOTICE,"Undefined index: %s", ZSTR_VAL(offset_key)); + zend_undefined_index(offset_key); retval = zend_hash_update(ht, offset_key, &EG(uninitialized_zval)); break; case BP_VAR_W: @@ -1578,7 +1769,7 @@ str_index: hval = zend_dval_to_lval(Z_DVAL_P(dim)); goto num_index; case IS_RESOURCE: - zend_error(E_NOTICE, "Resource ID#%d used as offset, casting to integer (%d)", Z_RES_HANDLE_P(dim), Z_RES_HANDLE_P(dim)); + zend_use_resource_as_offset(dim); hval = Z_RES_HANDLE_P(dim); goto num_index; case IS_FALSE: @@ -1591,7 +1782,7 @@ str_index: dim = Z_REFVAL_P(dim); goto try_again; default: - zend_error(E_WARNING, "Illegal offset type"); + zend_illegal_offset(); retval = (type == BP_VAR_W || type == BP_VAR_RW) ? NULL : &EG(uninitialized_zval); } @@ -1630,7 +1821,7 @@ fetch_from_array: if (dim == NULL) { retval = zend_hash_next_index_insert(Z_ARRVAL_P(container), &EG(uninitialized_zval)); if (UNEXPECTED(retval == NULL)) { - zend_error(E_WARNING, "Cannot add element to the array as the next element is already occupied"); + zend_cannot_add_element(); ZVAL_ERROR(result); return; } @@ -1651,12 +1842,10 @@ fetch_from_array: } if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { if (dim == NULL) { - zend_throw_error(NULL, "[] operator not supported for strings"); + zend_use_new_element_for_string(); } else { zend_check_string_offset(dim, type EXECUTE_DATA_CC); - if (EXPECTED(EG(exception) == NULL)) { - zend_wrong_string_offset(EXECUTE_DATA_C); - } + zend_wrong_string_offset(EXECUTE_DATA_C); } ZVAL_ERROR(result); } else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { @@ -1665,9 +1854,12 @@ fetch_from_array: dim = &EG(uninitialized_zval); } if (!Z_OBJ_HT_P(container)->read_dimension) { - zend_throw_error(NULL, "Cannot use object as array"); + zend_use_object_as_array(); ZVAL_ERROR(result); } else { + if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + dim++; + } retval = Z_OBJ_HT_P(container)->read_dimension(container, dim, type, result); if (UNEXPECTED(retval == &EG(uninitialized_zval))) { @@ -1704,8 +1896,7 @@ fetch_from_array: } if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { if (type != BP_VAR_UNSET) { - ZVAL_NEW_ARR(container); - zend_hash_init(Z_ARRVAL_P(container), 8, NULL, ZVAL_PTR_DTOR, 0); + array_init(container); goto fetch_from_array; } else { /* for read-mode only */ @@ -1718,25 +1909,28 @@ fetch_from_array: zend_error(E_WARNING, "Cannot unset offset in a non-array variable"); ZVAL_NULL(result); } else { - zend_error(E_WARNING, "Cannot use a scalar value as an array"); + zend_use_scalar_as_array(); ZVAL_ERROR(result); } } } } -static zend_never_inline void zend_fetch_dimension_address_W(zval *result, zval *container_ptr, zval *dim, int dim_type EXECUTE_DATA_DC) +static zend_never_inline void ZEND_FASTCALL zend_fetch_dimension_address_W(zval *container_ptr, zval *dim, int dim_type OPLINE_DC EXECUTE_DATA_DC) { + zval *result = EX_VAR(opline->result.var); zend_fetch_dimension_address(result, container_ptr, dim, dim_type, BP_VAR_W EXECUTE_DATA_CC); } -static zend_never_inline void zend_fetch_dimension_address_RW(zval *result, zval *container_ptr, zval *dim, int dim_type EXECUTE_DATA_DC) +static zend_never_inline void ZEND_FASTCALL zend_fetch_dimension_address_RW(zval *container_ptr, zval *dim, int dim_type OPLINE_DC EXECUTE_DATA_DC) { + zval *result = EX_VAR(opline->result.var); zend_fetch_dimension_address(result, container_ptr, dim, dim_type, BP_VAR_RW EXECUTE_DATA_CC); } -static zend_never_inline void zend_fetch_dimension_address_UNSET(zval *result, zval *container_ptr, zval *dim, int dim_type EXECUTE_DATA_DC) +static zend_never_inline void ZEND_FASTCALL zend_fetch_dimension_address_UNSET(zval *container_ptr, zval *dim, int dim_type OPLINE_DC EXECUTE_DATA_DC) { + zval *result = EX_VAR(opline->result.var); zend_fetch_dimension_address(result, container_ptr, dim, dim_type, BP_VAR_UNSET EXECUTE_DATA_CC); } @@ -1748,7 +1942,7 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { try_array: retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type EXECUTE_DATA_CC); - ZVAL_COPY(result, retval); + ZVAL_COPY_DEREF(result, retval); return; } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) { container = Z_REFVAL_P(container); @@ -1788,11 +1982,11 @@ try_string_offset: dim = Z_REFVAL_P(dim); goto try_string_offset; default: - zend_error(E_WARNING, "Illegal offset type"); + zend_illegal_offset(); break; } - offset = _zval_get_long_func(dim); + offset = zval_get_long_func(dim); } else { offset = Z_LVAL_P(dim); } @@ -1820,15 +2014,20 @@ try_string_offset: dim = &EG(uninitialized_zval); } if (!Z_OBJ_HT_P(container)->read_dimension) { - zend_throw_error(NULL, "Cannot use object as array"); + zend_use_object_as_array(); ZVAL_NULL(result); } else { + if (dim_type == IS_CONST && Z_EXTRA_P(dim) == ZEND_EXTRA_VALUE) { + dim++; + } retval = Z_OBJ_HT_P(container)->read_dimension(container, dim, type, result); ZEND_ASSERT(result != NULL); if (retval) { if (result != retval) { - ZVAL_COPY(result, retval); + ZVAL_COPY_DEREF(result, retval); + } else if (UNEXPECTED(Z_ISREF_P(retval))) { + zend_unwrap_reference(result); } } else { ZVAL_NULL(result); @@ -1845,36 +2044,155 @@ try_string_offset: } } -static zend_never_inline void zend_fetch_dimension_address_read_R(zval *result, zval *container, zval *dim, int dim_type EXECUTE_DATA_DC) +static zend_never_inline void ZEND_FASTCALL zend_fetch_dimension_address_read_R(zval *container, zval *dim, int dim_type OPLINE_DC EXECUTE_DATA_DC) { + zval *result = EX_VAR(opline->result.var); zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_R, 1, 0 EXECUTE_DATA_CC); } -static zend_never_inline void zend_fetch_dimension_address_read_R_slow(zval *result, zval *container, zval *dim EXECUTE_DATA_DC) +static zend_never_inline void zend_fetch_dimension_address_read_R_slow(zval *container, zval *dim OPLINE_DC EXECUTE_DATA_DC) { + zval *result = EX_VAR(opline->result.var); zend_fetch_dimension_address_read(result, container, dim, IS_CV, BP_VAR_R, 1, 1 EXECUTE_DATA_CC); } -static zend_never_inline void zend_fetch_dimension_address_read_IS(zval *result, zval *container, zval *dim, int dim_type EXECUTE_DATA_DC) +static zend_never_inline void ZEND_FASTCALL zend_fetch_dimension_address_read_IS(zval *container, zval *dim, int dim_type OPLINE_DC EXECUTE_DATA_DC) { + zval *result = EX_VAR(opline->result.var); zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1, 0 EXECUTE_DATA_CC); } -static zend_never_inline void zend_fetch_dimension_address_read_LIST(zval *result, zval *container, zval *dim EXECUTE_DATA_DC) +static zend_never_inline void ZEND_FASTCALL zend_fetch_dimension_address_LIST_r(zval *container, zval *dim, int dim_type OPLINE_DC EXECUTE_DATA_DC) { - zend_fetch_dimension_address_read(result, container, dim, IS_TMP_VAR, BP_VAR_R, 0, 0 EXECUTE_DATA_CC); + zval *result = EX_VAR(opline->result.var); + zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_R, 0, 0 EXECUTE_DATA_CC); } ZEND_API void zend_fetch_dimension_const(zval *result, zval *container, zval *dim, int type) { - if (type == BP_VAR_IS) { - zend_fetch_dimension_address_read_IS(result, container, dim, IS_CONST NO_EXECUTE_DATA_CC); + zend_fetch_dimension_address_read(result, container, dim, IS_TMP_VAR, type, 1, 0 NO_EXECUTE_DATA_CC); +} + +static zend_never_inline zval* ZEND_FASTCALL zend_find_array_dim_slow(HashTable *ht, zval *offset EXECUTE_DATA_DC) +{ + zend_ulong hval; + + if (Z_TYPE_P(offset) == IS_DOUBLE) { + hval = zend_dval_to_lval(Z_DVAL_P(offset)); +num_idx: + return zend_hash_index_find(ht, hval); + } else if (Z_TYPE_P(offset) == IS_NULL) { +str_idx: + return zend_hash_find_ex_ind(ht, ZSTR_EMPTY_ALLOC(), 1); + } else if (Z_TYPE_P(offset) == IS_FALSE) { + hval = 0; + goto num_idx; + } else if (Z_TYPE_P(offset) == IS_TRUE) { + hval = 1; + goto num_idx; + } else if (Z_TYPE_P(offset) == IS_RESOURCE) { + hval = Z_RES_HANDLE_P(offset); + goto num_idx; + } else if (/*OP2_TYPE == IS_CV &&*/ Z_TYPE_P(offset) == IS_UNDEF) { + zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); + goto str_idx; } else { - zend_fetch_dimension_address_read_R(result, container, dim, IS_CONST NO_EXECUTE_DATA_CC); + zend_error(E_WARNING, "Illegal offset type in isset or empty"); + return NULL; } } -static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type) +static zend_never_inline int ZEND_FASTCALL zend_isset_dim_slow(zval *container, zval *offset EXECUTE_DATA_DC) +{ + if (/*OP2_TYPE == IS_CV &&*/ UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); + offset = &EG(uninitialized_zval); + } + + if (/*OP1_TYPE != IS_CONST &&*/ EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { + return Z_OBJ_HT_P(container)->has_dimension(container, offset, 0); + } else { + zend_use_object_as_array(); + return 0; + } + } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ + zend_long lval; + + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +str_offset: + if (UNEXPECTED(lval < 0)) { /* Handle negative offset */ + lval += (zend_long)Z_STRLEN_P(container); + } + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + return 1; + } else { + return 0; + } + } else { + /*if (OP2_TYPE & (IS_CV|IS_VAR)) {*/ + ZVAL_DEREF(offset); + /*}*/ + if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ + || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ + && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { + lval = zval_get_long(offset); + goto str_offset; + } + return 0; + } + } else { + return 0; + } +} + +static zend_never_inline int ZEND_FASTCALL zend_isempty_dim_slow(zval *container, zval *offset EXECUTE_DATA_DC) +{ + if (/*OP2_TYPE == IS_CV &&*/ UNEXPECTED(Z_TYPE_P(offset) == IS_UNDEF)) { + zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); + offset = &EG(uninitialized_zval); + } + + if (/*OP1_TYPE != IS_CONST &&*/ EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { + if (EXPECTED(Z_OBJ_HT_P(container)->has_dimension)) { + return !Z_OBJ_HT_P(container)->has_dimension(container, offset, 1); + } else { + zend_use_object_as_array(); + return 1; + } + } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ + zend_long lval; + + if (EXPECTED(Z_TYPE_P(offset) == IS_LONG)) { + lval = Z_LVAL_P(offset); +str_offset: + if (UNEXPECTED(lval < 0)) { /* Handle negative offset */ + lval += (zend_long)Z_STRLEN_P(container); + } + if (EXPECTED(lval >= 0) && (size_t)lval < Z_STRLEN_P(container)) { + return (Z_STRVAL_P(container)[lval] == '0'); + } else { + return 1; + } + } else { + /*if (OP2_TYPE & (IS_CV|IS_VAR)) {*/ + ZVAL_DEREF(offset); + /*}*/ + if (Z_TYPE_P(offset) < IS_STRING /* simple scalar types */ + || (Z_TYPE_P(offset) == IS_STRING /* or numeric string */ + && IS_LONG == is_numeric_string(Z_STRVAL_P(offset), Z_STRLEN_P(offset), NULL, NULL, 0))) { + lval = zval_get_long(offset); + goto str_offset; + } + return 1; + } + } else { + return 1; + } +} + +static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type OPLINE_DC) { if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { do { @@ -1886,17 +2204,8 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c } /* this should modify object only if it's empty */ - if (type != BP_VAR_UNSET && - EXPECTED(Z_TYPE_P(container) <= IS_FALSE || - (Z_TYPE_P(container) == IS_STRING && Z_STRLEN_P(container)==0))) { - zval_ptr_dtor_nogc(container); - object_init(container); - } else { - if (container_op_type != IS_VAR || EXPECTED(!Z_ISERROR_P(container))) { - zend_string *property_name = zval_get_string(prop_ptr); - zend_error(E_WARNING, "Attempt to modify property '%s' of non-object", ZSTR_VAL(property_name)); - zend_string_release(property_name); - } + if (type == BP_VAR_UNSET || + UNEXPECTED(!make_real_object_rw(container, prop_ptr OPLINE_CC))) { ZVAL_ERROR(result); return; } @@ -1904,11 +2213,11 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c } if (prop_op_type == IS_CONST && EXPECTED(Z_OBJCE_P(container) == CACHED_PTR_EX(cache_slot))) { - uint32_t prop_offset = (uint32_t)(intptr_t)CACHED_PTR_EX(cache_slot + 1); + uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(container); zval *retval; - if (EXPECTED(prop_offset != (uint32_t)ZEND_DYNAMIC_PROPERTY_OFFSET)) { + if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { retval = OBJ_PROP(zobj, prop_offset); if (EXPECTED(Z_TYPE_P(retval) != IS_UNDEF)) { ZVAL_INDIRECT(result, retval); @@ -1917,11 +2226,11 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c } else if (EXPECTED(zobj->properties != NULL)) { if (UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) { if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) { - GC_REFCOUNT(zobj->properties)--; + GC_DELREF(zobj->properties); } zobj->properties = zend_array_dup(zobj->properties); } - retval = zend_hash_find(zobj->properties, Z_STR_P(prop_ptr)); + retval = zend_hash_find_ex(zobj->properties, Z_STR_P(prop_ptr), 1); if (EXPECTED(retval)) { ZVAL_INDIRECT(result, retval); return; @@ -1940,7 +2249,7 @@ use_read_property: ZVAL_UNREF(ptr); } } else { - zend_throw_error(NULL, "Cannot access undefined property for object with overloaded property access"); + zend_access_undefined_propery_in_overloaded_object(); ZVAL_ERROR(result); } } else { @@ -1949,101 +2258,47 @@ use_read_property: } else if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) { goto use_read_property; } else { - zend_error(E_WARNING, "This object doesn't support property references"); + zend_unsupported_property_reference(); ZVAL_ERROR(result); } } -static zend_always_inline zval* zend_fetch_static_property_address(zval *varname, zend_uchar varname_type, znode_op op2, zend_uchar op2_type, int type EXECUTE_DATA_DC) +static zend_never_inline void zend_fetch_this_var(int type OPLINE_DC EXECUTE_DATA_DC) { - zval *retval; - zend_string *name; - zend_class_entry *ce; - - if (varname_type == IS_CONST) { - name = Z_STR_P(varname); - } else if (EXPECTED(Z_TYPE_P(varname) == IS_STRING)) { - name = Z_STR_P(varname); - zend_string_addref(name); - } else { - if (varname_type == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { - zval_undefined_cv(EX(opline)->op1.var EXECUTE_DATA_CC); - } - name = zval_get_string(varname); - } - - if (op2_type == IS_CONST) { - if (varname_type == IS_CONST && EXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(varname))) != NULL)) { - retval = CACHED_PTR(Z_CACHE_SLOT_P(varname) + sizeof(void*)); - - /* check if static properties were destoyed */ - if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) { - if (type != BP_VAR_IS) { - zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name)); - } - return NULL; - } - - return retval; - } else { - zval *class_name = EX_CONSTANT(op2); + zval *result = EX_VAR(opline->result.var); - if (UNEXPECTED((ce = CACHED_PTR(Z_CACHE_SLOT_P(class_name))) == NULL)) { - ce = zend_fetch_class_by_name(Z_STR_P(class_name), class_name + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); - if (UNEXPECTED(ce == NULL)) { - if (varname_type != IS_CONST) { - zend_string_release(name); - } - return NULL; - } - CACHE_PTR(Z_CACHE_SLOT_P(class_name), ce); - } - } - } else { - if (op2_type == IS_UNUSED) { - ce = zend_fetch_class(NULL, op2.num); - if (UNEXPECTED(ce == NULL)) { - if (varname_type != IS_CONST) { - zend_string_release(name); - } - return NULL; + switch (type) { + case BP_VAR_R: + if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) { + ZVAL_OBJ(result, Z_OBJ(EX(This))); + Z_ADDREF_P(result); + } else { + ZVAL_NULL(result); + zend_error(E_NOTICE,"Undefined variable: this"); } - } else { - ce = Z_CE_P(EX_VAR(op2.var)); - } - if (varname_type == IS_CONST && - EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(varname)) == ce)) { - retval = CACHED_PTR(Z_CACHE_SLOT_P(varname) + sizeof(void*)); - - /* check if static properties were destoyed */ - if (UNEXPECTED(CE_STATIC_MEMBERS(ce) == NULL)) { - if (type != BP_VAR_IS) { - zend_throw_error(NULL, "Access to undeclared static property: %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(name)); - } - return NULL; + break; + case BP_VAR_IS: + if (EXPECTED(Z_TYPE(EX(This)) == IS_OBJECT)) { + ZVAL_OBJ(result, Z_OBJ(EX(This))); + Z_ADDREF_P(result); + } else { + ZVAL_NULL(result); } - - return retval; - } - } - - retval = zend_std_get_static_property(ce, name, type == BP_VAR_IS); - - if (varname_type != IS_CONST) { - zend_string_release(name); - } - - if (UNEXPECTED(retval == NULL)) { - return NULL; - } - - if (varname_type == IS_CONST) { - CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(varname), ce, retval); + break; + case BP_VAR_RW: + case BP_VAR_W: + ZVAL_UNDEF(result); + zend_throw_error(NULL, "Cannot re-assign $this"); + break; + case BP_VAR_UNSET: + ZVAL_UNDEF(result); + zend_throw_error(NULL, "Cannot unset $this"); + break; + EMPTY_SWITCH_DEFAULT_CASE() } - - return retval; } + #if ZEND_INTENSIVE_DEBUGGING #define CHECK_SYMBOL_TABLES() \ @@ -2099,23 +2354,24 @@ ZEND_API void zend_clean_and_cache_symbol_table(zend_array *symbol_table) /* {{{ static zend_always_inline void i_free_compiled_variables(zend_execute_data *execute_data) /* {{{ */ { zval *cv = EX_VAR_NUM(0); - zval *end = cv + EX(func)->op_array.last_var; - while (EXPECTED(cv != end)) { + int count = EX(func)->op_array.last_var; + while (EXPECTED(count != 0)) { if (Z_REFCOUNTED_P(cv)) { zend_refcounted *r = Z_COUNTED_P(cv); - if (!--GC_REFCOUNT(r)) { + if (!GC_DELREF(r)) { ZVAL_NULL(cv); - zval_dtor_func(r); + rc_dtor_func(r); } else { gc_check_possible_root(r); } } cv++; - } + count--; + } } /* }}} */ -void zend_free_compiled_variables(zend_execute_data *execute_data) /* {{{ */ +ZEND_API void zend_free_compiled_variables(zend_execute_data *execute_data) /* {{{ */ { i_free_compiled_variables(execute_data); } @@ -2153,12 +2409,75 @@ void zend_free_compiled_variables(zend_execute_data *execute_data) /* {{{ */ * +----------------------------------------+ */ -static zend_always_inline void i_init_func_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value) /* {{{ */ +static zend_never_inline void zend_copy_extra_args(EXECUTE_DATA_D) +{ + zend_op_array *op_array = &EX(func)->op_array; + uint32_t first_extra_arg = op_array->num_args; + uint32_t num_args = EX_NUM_ARGS(); + zval *src; + size_t delta; + uint32_t count; + uint32_t type_flags = 0; + + if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) { + /* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */ +#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) + opline += first_extra_arg; +#else + EX(opline) += first_extra_arg; +#endif + + } + + /* move extra args into separate array after all CV and TMP vars */ + src = EX_VAR_NUM(num_args - 1); + delta = op_array->last_var + op_array->T - first_extra_arg; + count = num_args - first_extra_arg; + if (EXPECTED(delta != 0)) { + delta *= sizeof(zval); + do { + type_flags |= Z_TYPE_INFO_P(src); + ZVAL_COPY_VALUE((zval*)(((char*)src) + delta), src); + ZVAL_UNDEF(src); + src--; + } while (--count); + if (Z_TYPE_INFO_REFCOUNTED(type_flags)) { + ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_FREE_EXTRA_ARGS); + } + } else { + do { + if (Z_REFCOUNTED_P(src)) { + ZEND_ADD_CALL_FLAG(execute_data, ZEND_CALL_FREE_EXTRA_ARGS); + break; + } + src--; + } while (--count); + } +} + +static zend_always_inline void zend_init_cvs(uint32_t first, uint32_t last EXECUTE_DATA_DC) +{ + if (EXPECTED(first < last)) { + uint32_t count = last - first; + zval *var = EX_VAR_NUM(first); + + do { + ZVAL_UNDEF(var); + var++; + } while (--count); + } +} + +static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, zval *return_value, zend_bool may_be_trampoline EXECUTE_DATA_DC) /* {{{ */ { uint32_t first_extra_arg, num_args; ZEND_ASSERT(EX(func) == (zend_function*)op_array); +#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) + opline = op_array->opcodes; +#else EX(opline) = op_array->opcodes; +#endif EX(call) = NULL; EX(return_value) = return_value; @@ -2166,55 +2485,27 @@ static zend_always_inline void i_init_func_execute_data(zend_execute_data *execu first_extra_arg = op_array->num_args; num_args = EX_NUM_ARGS(); if (UNEXPECTED(num_args > first_extra_arg)) { - if (EXPECTED(!(op_array->fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))) { - zval *end, *src, *dst; - uint32_t type_flags = 0; - - if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) { - /* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */ - EX(opline) += first_extra_arg; - } - - /* move extra args into separate array after all CV and TMP vars */ - end = EX_VAR_NUM(first_extra_arg - 1); - src = end + (num_args - first_extra_arg); - dst = src + (op_array->last_var + op_array->T - first_extra_arg); - if (EXPECTED(src != dst)) { - do { - type_flags |= Z_TYPE_INFO_P(src); - ZVAL_COPY_VALUE(dst, src); - ZVAL_UNDEF(src); - src--; - dst--; - } while (src != end); - } else { - do { - type_flags |= Z_TYPE_INFO_P(src); - src--; - } while (src != end); - } - ZEND_ADD_CALL_FLAG(execute_data, ((type_flags >> Z_TYPE_FLAGS_SHIFT) & IS_TYPE_REFCOUNTED)); + if (!may_be_trampoline || EXPECTED(!(op_array->fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE))) { + zend_copy_extra_args(EXECUTE_DATA_C); } } else if (EXPECTED((op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0)) { /* Skip useless ZEND_RECV and ZEND_RECV_INIT opcodes */ +#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) + opline += num_args; +#else EX(opline) += num_args; +#endif } /* Initialize CV variables (skip arguments) */ - if (EXPECTED((int)num_args < op_array->last_var)) { - zval *var = EX_VAR_NUM(num_args); - zval *end = EX_VAR_NUM(op_array->last_var); - - do { - ZVAL_UNDEF(var); - var++; - } while (var != end); - } + zend_init_cvs(num_args, op_array->last_var EXECUTE_DATA_CC); EX_LOAD_RUN_TIME_CACHE(op_array); - EX_LOAD_LITERALS(op_array); EG(current_execute_data) = execute_data; +#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) + EX(opline) = opline; +#endif } /* }}} */ @@ -2226,6 +2517,64 @@ static zend_never_inline void ZEND_FASTCALL init_func_run_time_cache(zend_op_arr } /* }}} */ +static zend_always_inline zend_function* ZEND_FASTCALL init_func_run_time_cache_i(zval *zv) /* {{{ */ +{ + zend_op_array *op_array = Z_PTR_P(zv); + + ZEND_ASSERT(op_array->run_time_cache == NULL); + if (op_array->fn_flags & ZEND_ACC_IMMUTABLE) { + zend_op_array *new_op_array = zend_arena_alloc(&CG(arena), sizeof(zend_op_array) + op_array->cache_size); + + Z_PTR_P(zv) = new_op_array; + memcpy(new_op_array, op_array, sizeof(zend_op_array)); + new_op_array->fn_flags &= ~ZEND_ACC_IMMUTABLE; + new_op_array->run_time_cache = (void**)(new_op_array + 1); + memset(new_op_array->run_time_cache, 0, new_op_array->cache_size); + return (zend_function*)new_op_array; + } else { + op_array->run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size); + memset(op_array->run_time_cache, 0, op_array->cache_size); + return (zend_function*)op_array; + } +} +/* }}} */ + +static zend_never_inline zend_function* init_func_run_time_cache_ex(zval *zv) /* {{{ */ +{ + return init_func_run_time_cache_i(zv); +} +/* }}} */ + +ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *name) /* {{{ */ +{ + zval *zv = zend_hash_find(EG(function_table), name); + + if (EXPECTED(zv != NULL)) { + zend_function *fbc = Z_FUNC_P(zv); + + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + fbc = (zend_function*)init_func_run_time_cache_i(zv); + } + return fbc; + } + return NULL; +} /* }}} */ + +ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function_str(const char *name, size_t len) /* {{{ */ +{ + zval *zv = zend_hash_str_find(EG(function_table), name, len); + + if (EXPECTED(zv != NULL)) { + zend_function *fbc = Z_FUNC_P(zv); + + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + fbc = (zend_function*)init_func_run_time_cache_i(zv); + } + return fbc; + } + return NULL; +} /* }}} */ + static zend_always_inline void i_init_code_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value) /* {{{ */ { ZEND_ASSERT(EX(func) == (zend_function*)op_array); @@ -2241,20 +2590,32 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu memset(op_array->run_time_cache, 0, op_array->cache_size); } EX_LOAD_RUN_TIME_CACHE(op_array); - EX_LOAD_LITERALS(op_array); EG(current_execute_data) = execute_data; } /* }}} */ -ZEND_API void zend_init_func_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value) /* {{{ */ +ZEND_API void zend_init_func_execute_data(zend_execute_data *ex, zend_op_array *op_array, zval *return_value) /* {{{ */ { +#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) + zend_execute_data *orig_execute_data = execute_data; + const zend_op *orig_opline = opline; + execute_data = ex; +#else + zend_execute_data *execute_data = ex; +#endif + EX(prev_execute_data) = EG(current_execute_data); if (!op_array->run_time_cache) { - op_array->run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size); - memset(op_array->run_time_cache, 0, op_array->cache_size); + init_func_run_time_cache(op_array); } - i_init_func_execute_data(execute_data, op_array, return_value); + i_init_func_execute_data(op_array, return_value, 1 EXECUTE_DATA_CC); + +#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) + EX(opline) = opline; + opline = orig_opline; + execute_data = orig_execute_data; +#endif } /* }}} */ @@ -2279,14 +2640,12 @@ ZEND_API void zend_init_execute_data(zend_execute_data *execute_data, zend_op_ar } /* }}} */ -static zend_always_inline zend_bool zend_is_by_ref_func_arg_fetch(const zend_op *opline, zend_execute_data *call) /* {{{ */ +static zend_always_inline zend_bool zend_is_by_ref_func_arg_fetch(uint32_t arg_num, zend_function *func) /* {{{ */ { - uint32_t arg_num = opline->extended_value & ZEND_FETCH_ARG_MASK; - if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) { - return QUICK_ARG_SHOULD_BE_SENT_BY_REF(call->func, arg_num); + return QUICK_ARG_SHOULD_BE_SENT_BY_REF(func, arg_num); } - return ARG_SHOULD_BE_SENT_BY_REF(call->func, arg_num); + return ARG_SHOULD_BE_SENT_BY_REF(func, arg_num); } /* }}} */ @@ -2399,6 +2758,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o case ZEND_SEND_VAL_EX: case ZEND_SEND_VAR: case ZEND_SEND_VAR_EX: + case ZEND_SEND_FUNC_ARG: case ZEND_SEND_REF: case ZEND_SEND_VAR_NO_REF: case ZEND_SEND_VAR_NO_REF_EX: @@ -2453,7 +2813,7 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { if (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR) { - GC_REFCOUNT(Z_OBJ(call->This))--; + GC_DELREF(Z_OBJ(call->This)); if (GC_REFCOUNT(Z_OBJ(call->This)) == 1) { zend_object_store_ctor_failed(Z_OBJ(call->This)); } @@ -2461,9 +2821,9 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o OBJ_RELEASE(Z_OBJ(call->This)); } if (call->func->common.fn_flags & ZEND_ACC_CLOSURE) { - zend_object_release((zend_object *) call->func->common.prototype); + zend_object_release(ZEND_CLOSURE_OBJECT(call->func)); } else if (call->func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { - zend_string_release(call->func->common.function_name); + zend_string_release_ex(call->func->common.function_name, 0); zend_free_trampoline(call->func); } @@ -2475,6 +2835,20 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o } /* }}} */ +static const zend_live_range *find_live_range(const zend_op_array *op_array, uint32_t op_num, uint32_t var_num) /* {{{ */ +{ + int i; + for (i = 0; i < op_array->last_live_range; i++) { + const zend_live_range *range = &op_array->live_range[i]; + if (op_num >= range->start && op_num < range->end + && var_num == (range->var & ~ZEND_LIVE_MASK)) { + return range; + } + } + return NULL; +} +/* }}} */ + static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */ { int i; @@ -2506,11 +2880,11 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, last--; } if (last->opcode == ZEND_ROPE_INIT) { - zend_string_release(*rope); + zend_string_release_ex(*rope, 0); } else { int j = last->extended_value; do { - zend_string_release(rope[j]); + zend_string_release_ex(rope[j], 0); } while (j--); } } else if (kind == ZEND_LIVE_SILENCE) { @@ -2525,7 +2899,7 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, } /* }}} */ -void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) { +ZEND_API void zend_cleanup_unfinished_execution(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) { cleanup_unfinished_calls(execute_data, op_num); cleanup_live_vars(execute_data, op_num, catch_op_num); } @@ -2564,7 +2938,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s called_scope = zend_fetch_class_by_name(lcname, NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); if (UNEXPECTED(called_scope == NULL)) { - zend_string_release(lcname); + zend_string_release_ex(lcname, 0); return NULL; } @@ -2577,32 +2951,25 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s } if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), ZSTR_VAL(mname)); + zend_undefined_method(called_scope, mname); } - zend_string_release(lcname); - zend_string_release(mname); + zend_string_release_ex(lcname, 0); + zend_string_release_ex(mname, 0); return NULL; } - zend_string_release(lcname); - zend_string_release(mname); + zend_string_release_ex(lcname, 0); + zend_string_release_ex(mname, 0); if (UNEXPECTED(!(fbc->common.fn_flags & ZEND_ACC_STATIC))) { - if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { - zend_error(E_DEPRECATED, - "Non-static method %s::%s() should not be called statically", - ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); - if (UNEXPECTED(EG(exception) != NULL)) { - return NULL; - } - } else { - zend_throw_error( - zend_ce_error, - "Non-static method %s::%s() cannot be called statically", - ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); + zend_non_static_method_call(fbc); + if (UNEXPECTED(EG(exception) != NULL)) { return NULL; } } + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + init_func_run_time_cache(&fbc->op_array); + } } else { if (ZSTR_VAL(function)[0] == '\\') { lcname = zend_string_alloc(ZSTR_LEN(function) - 1, 0); @@ -2612,19 +2979,18 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s } if (UNEXPECTED((func = zend_hash_find(EG(function_table), lcname)) == NULL)) { zend_throw_error(NULL, "Call to undefined function %s()", ZSTR_VAL(function)); - zend_string_release(lcname); + zend_string_release_ex(lcname, 0); return NULL; } - zend_string_release(lcname); + zend_string_release_ex(lcname, 0); fbc = Z_FUNC_P(func); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + fbc = init_func_run_time_cache_ex(func); + } called_scope = NULL; } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { - init_func_run_time_cache(&fbc->op_array); - } - return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC, fbc, num_args, called_scope, NULL); } @@ -2642,15 +3008,14 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zval * if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) { /* Delay closure destruction until its invocation */ - ZEND_ASSERT(GC_TYPE((zend_object*)fbc->common.prototype) == IS_OBJECT); - GC_REFCOUNT((zend_object*)fbc->common.prototype)++; + GC_ADDREF(ZEND_CLOSURE_OBJECT(fbc)); call_info |= ZEND_CALL_CLOSURE; if (fbc->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) { call_info |= ZEND_CALL_FAKE_CLOSURE; } } else if (object) { call_info |= ZEND_CALL_RELEASE_THIS; - GC_REFCOUNT(object)++; /* For $this pointer */ + GC_ADDREF(object); /* For $this pointer */ } } else { zend_throw_error(NULL, "Function name must be a string"); @@ -2710,23 +3075,13 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar } if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(called_scope->name), Z_STRVAL_P(method)); + zend_undefined_method(called_scope, Z_STR_P(method)); } return NULL; } if (!(fbc->common.fn_flags & ZEND_ACC_STATIC)) { - if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { - zend_error(E_DEPRECATED, - "Non-static method %s::%s() should not be called statically", - ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); - if (UNEXPECTED(EG(exception) != NULL)) { - return NULL; - } - } else { - zend_throw_error( - zend_ce_error, - "Non-static method %s::%s() cannot be called statically", - ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name)); + zend_non_static_method_call(fbc); + if (UNEXPECTED(EG(exception) != NULL)) { return NULL; } } @@ -2737,7 +3092,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { - zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(object->ce->name), Z_STRVAL_P(method)); + zend_undefined_method(object->ce, Z_STR_P(method)); } return NULL; } @@ -2746,7 +3101,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar object = NULL; } else { call_info |= ZEND_CALL_RELEASE_THIS; - GC_REFCOUNT(object)++; /* For $this pointer */ + GC_ADDREF(object); /* For $this pointer */ } } } else { @@ -2772,76 +3127,81 @@ static zend_never_inline zend_op_array* ZEND_FASTCALL zend_include_or_eval(zval ZVAL_UNDEF(&tmp_inc_filename); if (Z_TYPE_P(inc_filename) != IS_STRING) { - ZVAL_STR(&tmp_inc_filename, zval_get_string(inc_filename)); + ZVAL_STR(&tmp_inc_filename, zval_get_string_func(inc_filename)); inc_filename = &tmp_inc_filename; } - if (type != ZEND_EVAL && strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename)) { - if (type == ZEND_INCLUDE_ONCE || type == ZEND_INCLUDE) { - zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename)); - } else { - zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename)); - } - } else { - switch (type) { - case ZEND_INCLUDE_ONCE: - case ZEND_REQUIRE_ONCE: { - zend_file_handle file_handle; - zend_string *resolved_path; - - resolved_path = zend_resolve_path(Z_STRVAL_P(inc_filename), (int)Z_STRLEN_P(inc_filename)); - if (resolved_path) { - if (zend_hash_exists(&EG(included_files), resolved_path)) { - goto already_compiled; - } - } else { - resolved_path = zend_string_copy(Z_STR_P(inc_filename)); + switch (type) { + case ZEND_INCLUDE_ONCE: + case ZEND_REQUIRE_ONCE: { + zend_file_handle file_handle; + zend_string *resolved_path; + + resolved_path = zend_resolve_path(Z_STRVAL_P(inc_filename), Z_STRLEN_P(inc_filename)); + if (EXPECTED(resolved_path)) { + if (zend_hash_exists(&EG(included_files), resolved_path)) { + goto already_compiled; } + } else if (UNEXPECTED(strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename))) { + zend_message_dispatcher( + (type == ZEND_INCLUDE_ONCE) ? + ZMSG_FAILED_INCLUDE_FOPEN : ZMSG_FAILED_REQUIRE_FOPEN, + Z_STRVAL_P(inc_filename)); + break; + } else { + resolved_path = zend_string_copy(Z_STR_P(inc_filename)); + } - if (SUCCESS == zend_stream_open(ZSTR_VAL(resolved_path), &file_handle)) { + if (SUCCESS == zend_stream_open(ZSTR_VAL(resolved_path), &file_handle)) { - if (!file_handle.opened_path) { - file_handle.opened_path = zend_string_copy(resolved_path); - } + if (!file_handle.opened_path) { + file_handle.opened_path = zend_string_copy(resolved_path); + } - if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path)) { - zend_op_array *op_array = zend_compile_file(&file_handle, (type==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE)); - zend_destroy_file_handle(&file_handle); - zend_string_release(resolved_path); - if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) { - zend_string_release(Z_STR(tmp_inc_filename)); - } - return op_array; - } else { - zend_file_handle_dtor(&file_handle); -already_compiled: - new_op_array = ZEND_FAKE_OP_ARRAY; + if (zend_hash_add_empty_element(&EG(included_files), file_handle.opened_path)) { + zend_op_array *op_array = zend_compile_file(&file_handle, (type==ZEND_INCLUDE_ONCE?ZEND_INCLUDE:ZEND_REQUIRE)); + zend_destroy_file_handle(&file_handle); + zend_string_release_ex(resolved_path, 0); + if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) { + zval_ptr_dtor_str(&tmp_inc_filename); } + return op_array; } else { - if (type == ZEND_INCLUDE_ONCE) { - zend_message_dispatcher(ZMSG_FAILED_INCLUDE_FOPEN, Z_STRVAL_P(inc_filename)); - } else { - zend_message_dispatcher(ZMSG_FAILED_REQUIRE_FOPEN, Z_STRVAL_P(inc_filename)); - } + zend_file_handle_dtor(&file_handle); +already_compiled: + new_op_array = ZEND_FAKE_OP_ARRAY; } - zend_string_release(resolved_path); - } - break; - case ZEND_INCLUDE: - case ZEND_REQUIRE: - new_op_array = compile_filename(type, inc_filename); - break; - case ZEND_EVAL: { - char *eval_desc = zend_make_compiled_string_description("eval()'d code"); - new_op_array = zend_compile_string(inc_filename, eval_desc); - efree(eval_desc); + } else { + zend_message_dispatcher( + (type == ZEND_INCLUDE_ONCE) ? + ZMSG_FAILED_INCLUDE_FOPEN : ZMSG_FAILED_REQUIRE_FOPEN, + Z_STRVAL_P(inc_filename)); } + zend_string_release_ex(resolved_path, 0); + } + break; + case ZEND_INCLUDE: + case ZEND_REQUIRE: + if (UNEXPECTED(strlen(Z_STRVAL_P(inc_filename)) != Z_STRLEN_P(inc_filename))) { + zend_message_dispatcher( + (type == ZEND_INCLUDE) ? + ZMSG_FAILED_INCLUDE_FOPEN : ZMSG_FAILED_REQUIRE_FOPEN, + Z_STRVAL_P(inc_filename)); break; - EMPTY_SWITCH_DEFAULT_CASE() - } + } + new_op_array = compile_filename(type, inc_filename); + break; + case ZEND_EVAL: { + char *eval_desc = zend_make_compiled_string_description("eval()'d code"); + new_op_array = zend_compile_string(inc_filename, eval_desc); + efree(eval_desc); + } + break; + EMPTY_SWITCH_DEFAULT_CASE() } + if (Z_TYPE(tmp_inc_filename) != IS_UNDEF) { - zend_string_release(Z_STR(tmp_inc_filename)); + zval_ptr_dtor_str(&tmp_inc_filename); } return new_op_array; } @@ -2856,7 +3216,7 @@ ZEND_API int ZEND_FASTCALL zend_do_fcall_overloaded(zend_execute_data *call, zva if (UNEXPECTED(Z_TYPE(call->This) != IS_OBJECT)) { zend_vm_stack_free_args(call); if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { - zend_string_release(fbc->common.function_name); + zend_string_release_ex(fbc->common.function_name, 0); } efree(fbc); zend_vm_stack_free_call_frame(call); @@ -2876,7 +3236,7 @@ ZEND_API int ZEND_FASTCALL zend_do_fcall_overloaded(zend_execute_data *call, zva zend_vm_stack_free_args(call); if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) { - zend_string_release(fbc->common.function_name); + zend_string_release_ex(fbc->common.function_name, 0); } efree(fbc); @@ -2884,6 +3244,168 @@ ZEND_API int ZEND_FASTCALL zend_do_fcall_overloaded(zend_execute_data *call, zva } /* }}} */ +static zend_never_inline zend_bool ZEND_FASTCALL zend_fe_reset_iterator(zval *array_ptr, int by_ref OPLINE_DC EXECUTE_DATA_DC) /* {{{ */ +{ + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, by_ref); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + if (iter) { + OBJ_RELEASE(&iter->std); + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ZSTR_VAL(ce->name)); + } + ZVAL_UNDEF(EX_VAR(opline->result.var)); + return 1; + } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + return 1; + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + return 1; + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + return is_empty; +} +/* }}} */ + +static zend_always_inline int _zend_quick_get_constant( + const zval *key, uint32_t flags, int check_defined_only OPLINE_DC EXECUTE_DATA_DC) /* {{{ */ +{ + zval *zv; + const zval *orig_key = key; + zend_constant *c = NULL; + + zv = zend_hash_find_ex(EG(zend_constants), Z_STR_P(key), 1); + if (zv) { + c = (zend_constant*)Z_PTR_P(zv); + } else { + key++; + zv = zend_hash_find_ex(EG(zend_constants), Z_STR_P(key), 1); + if (zv && (ZEND_CONSTANT_FLAGS((zend_constant*)Z_PTR_P(zv)) & CONST_CS) == 0) { + c = (zend_constant*)Z_PTR_P(zv); + } else { + if ((flags & (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) == (IS_CONSTANT_IN_NAMESPACE|IS_CONSTANT_UNQUALIFIED)) { + key++; + zv = zend_hash_find_ex(EG(zend_constants), Z_STR_P(key), 1); + if (zv) { + c = (zend_constant*)Z_PTR_P(zv); + } else { + key++; + zv = zend_hash_find_ex(EG(zend_constants), Z_STR_P(key), 1); + if (zv && (ZEND_CONSTANT_FLAGS((zend_constant*)Z_PTR_P(zv)) & CONST_CS) == 0) { + c = (zend_constant*)Z_PTR_P(zv); + } + } + } + } + } + + if (!c) { + if (!check_defined_only) { + if ((opline->op1.num & IS_CONSTANT_UNQUALIFIED) != 0) { + char *actual = (char *)zend_memrchr(Z_STRVAL_P(RT_CONSTANT(opline, opline->op2)), '\\', Z_STRLEN_P(RT_CONSTANT(opline, opline->op2))); + if (!actual) { + ZVAL_STR_COPY(EX_VAR(opline->result.var), Z_STR_P(RT_CONSTANT(opline, opline->op2))); + } else { + actual++; + ZVAL_STRINGL(EX_VAR(opline->result.var), + actual, Z_STRLEN_P(RT_CONSTANT(opline, opline->op2)) - (actual - Z_STRVAL_P(RT_CONSTANT(opline, opline->op2)))); + } + /* non-qualified constant - allow text substitution */ + zend_error(E_WARNING, "Use of undefined constant %s - assumed '%s' (this will throw an Error in a future version of PHP)", + Z_STRVAL_P(EX_VAR(opline->result.var)), Z_STRVAL_P(EX_VAR(opline->result.var))); + } else { + zend_throw_error(NULL, "Undefined constant '%s'", Z_STRVAL_P(RT_CONSTANT(opline, opline->op2))); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + } + return FAILURE; + } + + if (!check_defined_only) { + ZVAL_COPY_OR_DUP(EX_VAR(opline->result.var), &c->value); + if (!(ZEND_CONSTANT_FLAGS(c) & (CONST_CS|CONST_CT_SUBST))) { + const char *ns_sep; + size_t shortname_offset; + size_t shortname_len; + zend_bool is_deprecated; + + if (flags & IS_CONSTANT_UNQUALIFIED) { + const zval *access_key; + + if (!(flags & IS_CONSTANT_IN_NAMESPACE)) { + access_key = orig_key - 1; + } else { + if (key < orig_key + 2) { + goto check_short_name; + } else { + access_key = orig_key + 2; + } + } + is_deprecated = !zend_string_equals(c->name, Z_STR_P(access_key)); + } else { +check_short_name: + ns_sep = zend_memrchr(ZSTR_VAL(c->name), '\\', ZSTR_LEN(c->name)); + ZEND_ASSERT(ns_sep); + /* Namespaces are always case-insensitive. Only compare shortname. */ + shortname_offset = ns_sep - ZSTR_VAL(c->name) + 1; + shortname_len = ZSTR_LEN(c->name) - shortname_offset; + + is_deprecated = memcmp(ZSTR_VAL(c->name) + shortname_offset, Z_STRVAL_P(orig_key - 1) + shortname_offset, shortname_len) != 0; + } + + if (is_deprecated) { + zend_error(E_DEPRECATED, + "Case-insensitive constants are deprecated. " + "The correct casing for this constant is \"%s\"", + ZSTR_VAL(c->name)); + return SUCCESS; + } + } + } + + CACHE_PTR(opline->extended_value, c); + return SUCCESS; +} +/* }}} */ + +static zend_never_inline void ZEND_FASTCALL zend_quick_get_constant( + const zval *key, uint32_t flags OPLINE_DC EXECUTE_DATA_DC) /* {{{ */ +{ + _zend_quick_get_constant(key, flags, 0 OPLINE_CC EXECUTE_DATA_CC); +} /* }}} */ + +static zend_never_inline int ZEND_FASTCALL zend_quick_check_constant( + const zval *key OPLINE_DC EXECUTE_DATA_DC) /* {{{ */ +{ + return _zend_quick_get_constant(key, 0, 1 OPLINE_CC EXECUTE_DATA_CC); +} /* }}} */ + +#ifdef ZEND_VM_TRACE_HANDLERS +# include "zend_vm_trace_handlers.h" +#elif defined(ZEND_VM_TRACE_MAP) +# include "zend_vm_trace_map.h" +#endif + #define ZEND_VM_NEXT_OPCODE_EX(check_exception, skip) \ CHECK_SYMBOL_TABLES() \ if (check_exception) { \ @@ -2911,14 +3433,17 @@ ZEND_API int ZEND_FASTCALL zend_do_fcall_overloaded(zend_execute_data *call, zva #define ZEND_VM_SET_RELATIVE_OPCODE(opline, offset) \ ZEND_VM_SET_OPCODE(ZEND_OFFSET_TO_OPLINE(opline, offset)) -#define ZEND_VM_JMP(new_op) do { \ - if (UNEXPECTED(EG(exception))) { \ +#define ZEND_VM_JMP_EX(new_op, check_exception) do { \ + if (check_exception && UNEXPECTED(EG(exception))) { \ HANDLE_EXCEPTION(); \ } \ ZEND_VM_SET_OPCODE(new_op); \ ZEND_VM_CONTINUE(); \ } while (0) +#define ZEND_VM_JMP(new_op) \ + ZEND_VM_JMP_EX(new_op, 1) + #define ZEND_VM_INC_OPCODE() \ OPLINE++ @@ -3003,8 +3528,18 @@ ZEND_API int ZEND_FASTCALL zend_do_fcall_overloaded(zend_execute_data *call, zva } \ } while (0) +#if ZEND_GCC_VERSION >= 4000 && !defined(__clang__) +# pragma GCC push_options +# pragma GCC optimize("no-gcse") +# pragma GCC optimize("no-ivopts") +#endif + #include "zend_vm_execute.h" +#if ZEND_GCC_VERSION >= 4000 && !defined(__clang__) +# pragma GCC pop_options +#endif + ZEND_API int zend_set_user_opcode_handler(zend_uchar opcode, user_opcode_handler_t handler) { if (opcode != ZEND_USER_OPCODE) { @@ -3025,13 +3560,13 @@ ZEND_API user_opcode_handler_t zend_get_user_opcode_handler(zend_uchar opcode) return zend_user_opcode_handlers[opcode]; } -ZEND_API zval *zend_get_zval_ptr(int op_type, const znode_op *node, const zend_execute_data *execute_data, zend_free_op *should_free, int type) +ZEND_API zval *zend_get_zval_ptr(const zend_op *opline, int op_type, const znode_op *node, const zend_execute_data *execute_data, zend_free_op *should_free, int type) { zval *ret; switch (op_type) { case IS_CONST: - ret = EX_CONSTANT(*node); + ret = RT_CONSTANT(opline, *node); *should_free = NULL; break; case IS_TMP_VAR: |
