--- embedaddon/php/Zend/zend_execute.c 2012/02/21 23:47:52 1.1 +++ embedaddon/php/Zend/zend_execute.c 2013/07/22 01:32:15 1.1.1.3 @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) | + | Copyright (c) 1998-2013 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | | that is bundled with this package in the file LICENSE, and is | @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: zend_execute.c,v 1.1 2012/02/21 23:47:52 misho Exp $ */ +/* $Id: zend_execute.c,v 1.1.1.3 2013/07/22 01:32:15 misho Exp $ */ #define ZEND_INTENSIVE_DEBUGGING 0 @@ -36,6 +36,7 @@ #include "zend_interfaces.h" #include "zend_closures.h" #include "zend_vm.h" +#include "zend_dtrace.h" /* Virtual current working directory support */ #include "tsrm_virtual_cwd.h" @@ -48,20 +49,20 @@ typedef int (*incdec_t)(zval *); -#define get_zval_ptr(node, Ts, should_free, type) _get_zval_ptr(node, Ts, should_free, type TSRMLS_CC) -#define get_zval_ptr_ptr(node, Ts, should_free, type) _get_zval_ptr_ptr(node, Ts, should_free, type TSRMLS_CC) -#define get_obj_zval_ptr(node, Ts, should_free, type) _get_obj_zval_ptr(node, Ts, should_free, type TSRMLS_CC) -#define get_obj_zval_ptr_ptr(node, Ts, should_free, type) _get_obj_zval_ptr_ptr(node, Ts, should_free, type TSRMLS_CC) +#define get_zval_ptr(op_type, node, Ts, should_free, type) _get_zval_ptr(op_type, node, Ts, should_free, type TSRMLS_CC) +#define get_zval_ptr_ptr(op_type, node, Ts, should_free, type) _get_zval_ptr_ptr(op_type, node, Ts, should_free, type TSRMLS_CC) +#define get_obj_zval_ptr(op_type, node, Ts, should_free, type) _get_obj_zval_ptr(op_type, node, Ts, should_free, type TSRMLS_CC) +#define get_obj_zval_ptr_ptr(op_type, node, Ts, should_free, type) _get_obj_zval_ptr_ptr(op_type, node, Ts, should_free, type TSRMLS_CC) /* Prototypes */ static void zend_extension_statement_handler(const zend_extension *extension, zend_op_array *op_array TSRMLS_DC); static void zend_extension_fcall_begin_handler(const zend_extension *extension, zend_op_array *op_array TSRMLS_DC); static void zend_extension_fcall_end_handler(const zend_extension *extension, zend_op_array *op_array TSRMLS_DC); -#define RETURN_VALUE_USED(opline) (!((opline)->result.u.EA.type & EXT_TYPE_UNUSED)) +#define RETURN_VALUE_USED(opline) (!((opline)->result_type & EXT_TYPE_UNUSED)) -#define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset)) #define T(offset) (*(temp_variable *)((char *) Ts + offset)) +#define CV(var) CVs[var] #define TEMP_VAR_STACK_LIMIT 2000 @@ -92,24 +93,32 @@ static zend_always_inline void zend_pzval_unlock_free_ } } +#undef zval_ptr_dtor +#define zval_ptr_dtor(pzv) i_zval_ptr_dtor(*(pzv) ZEND_FILE_LINE_CC) + #define PZVAL_UNLOCK(z, f) zend_pzval_unlock_func(z, f, 1 TSRMLS_CC) #define PZVAL_UNLOCK_EX(z, f, u) zend_pzval_unlock_func(z, f, u TSRMLS_CC) #define PZVAL_UNLOCK_FREE(z) zend_pzval_unlock_free_func(z TSRMLS_CC) #define PZVAL_LOCK(z) Z_ADDREF_P((z)) -#define RETURN_VALUE_UNUSED(pzn) (((pzn)->u.EA.type & EXT_TYPE_UNUSED)) -#define SELECTIVE_PZVAL_LOCK(pzv, pzn) if (!RETURN_VALUE_UNUSED(pzn)) { PZVAL_LOCK(pzv); } +#define SELECTIVE_PZVAL_LOCK(pzv, opline) if (RETURN_VALUE_USED(opline)) { PZVAL_LOCK(pzv); } -#define AI_USE_PTR(ai) \ - if ((ai).ptr_ptr) { \ - (ai).ptr = *((ai).ptr_ptr); \ - (ai).ptr_ptr = &((ai).ptr); \ - } else { \ - (ai).ptr = NULL; \ - } +#define EXTRACT_ZVAL_PTR(t) do { \ + temp_variable *__t = (t); \ + if (__t->var.ptr_ptr) { \ + __t->var.ptr = *__t->var.ptr_ptr; \ + __t->var.ptr_ptr = &__t->var.ptr; \ + if (!PZVAL_IS_REF(__t->var.ptr) && \ + Z_REFCOUNT_P(__t->var.ptr) > 2) { \ + SEPARATE_ZVAL(__t->var.ptr_ptr); \ + } \ + } \ + } while (0) -#define AI_SET_PTR(ai, val) \ - (ai).ptr = (val); \ - (ai).ptr_ptr = &((ai).ptr); +#define AI_SET_PTR(t, val) do { \ + temp_variable *__t = (t); \ + __t->var.ptr = (val); \ + __t->var.ptr_ptr = &__t->var.ptr; \ + } while (0) #define FREE_OP(should_free) \ if (should_free.var) { \ @@ -134,21 +143,12 @@ static zend_always_inline void zend_pzval_unlock_free_ #define IS_TMP_FREE(should_free) ((zend_uintptr_t)should_free.var & 1L) -#define INIT_PZVAL_COPY(z,v) \ - (z)->value = (v)->value; \ - Z_TYPE_P(z) = Z_TYPE_P(v); \ - Z_SET_REFCOUNT_P(z, 1); \ - Z_UNSET_ISREF_P(z); - #define MAKE_REAL_ZVAL_PTR(val) \ do { \ zval *_tmp; \ ALLOC_ZVAL(_tmp); \ - _tmp->value = (val)->value; \ - Z_TYPE_P(_tmp) = Z_TYPE_P(val); \ - Z_SET_REFCOUNT_P(_tmp, 1); \ - Z_UNSET_ISREF_P(_tmp); \ - val = _tmp; \ + INIT_PZVAL_COPY(_tmp, (val)); \ + (val) = _tmp; \ } while (0) /* End of zend_execute_locks.h */ @@ -172,51 +172,21 @@ ZEND_API zval** zend_get_compiled_variable_value(const return execute_data_ptr->CVs[var]; } -static zend_always_inline zval *_get_zval_ptr_tmp(const znode *node, const temp_variable *Ts, zend_free_op *should_free TSRMLS_DC) +static zend_always_inline zval *_get_zval_ptr_tmp(zend_uint var, const temp_variable *Ts, zend_free_op *should_free TSRMLS_DC) { - return should_free->var = &T(node->u.var).tmp_var; + return should_free->var = &T(var).tmp_var; } -static zval *_get_zval_ptr_var_string_offset(const znode *node, const temp_variable *Ts, zend_free_op *should_free TSRMLS_DC) +static zend_always_inline zval *_get_zval_ptr_var(zend_uint var, const temp_variable *Ts, zend_free_op *should_free TSRMLS_DC) { - temp_variable *T = &T(node->u.var); - zval *str = T->str_offset.str; - zval *ptr; + zval *ptr = T(var).var.ptr; - /* string offset */ - ALLOC_ZVAL(ptr); - T->str_offset.ptr = ptr; - should_free->var = ptr; - - if (T->str_offset.str->type != IS_STRING - || ((int)T->str_offset.offset < 0) - || (T->str_offset.str->value.str.len <= (int)T->str_offset.offset)) { - ptr->value.str.val = STR_EMPTY_ALLOC(); - ptr->value.str.len = 0; - } else { - ptr->value.str.val = estrndup(str->value.str.val + T->str_offset.offset, 1); - ptr->value.str.len = 1; - } - PZVAL_UNLOCK_FREE(str); - Z_SET_REFCOUNT_P(ptr, 1); - Z_SET_ISREF_P(ptr); - ptr->type = IS_STRING; + PZVAL_UNLOCK(ptr, should_free); return ptr; } -static zend_always_inline zval *_get_zval_ptr_var(const znode *node, const temp_variable *Ts, zend_free_op *should_free TSRMLS_DC) +static zend_never_inline zval **_get_zval_cv_lookup(zval ***ptr, zend_uint var, int type TSRMLS_DC) { - zval *ptr = T(node->u.var).var.ptr; - if (EXPECTED(ptr != NULL)) { - PZVAL_UNLOCK(ptr, should_free); - return ptr; - } else { - return _get_zval_ptr_var_string_offset(node, Ts, should_free TSRMLS_CC); - } -} - -static zval **_get_zval_cv_lookup(zval ***ptr, zend_uint var, int type TSRMLS_DC) -{ zend_compiled_variable *cv = &CV_DEF_OF(var); if (!EG(active_symbol_table) || @@ -246,30 +216,147 @@ static zval **_get_zval_cv_lookup(zval ***ptr, zend_ui return *ptr; } -static zend_always_inline zval *_get_zval_ptr_cv(const znode *node, const temp_variable *Ts, int type TSRMLS_DC) +static zend_never_inline zval **_get_zval_cv_lookup_BP_VAR_R(zval ***ptr, zend_uint var TSRMLS_DC) { - zval ***ptr = &CV_OF(node->u.var); + zend_compiled_variable *cv = &CV_DEF_OF(var); + if (!EG(active_symbol_table) || + zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void **)ptr)==FAILURE) { + zend_error(E_NOTICE, "Undefined variable: %s", cv->name); + return &EG(uninitialized_zval_ptr); + } + return *ptr; +} + +static zend_never_inline zval **_get_zval_cv_lookup_BP_VAR_UNSET(zval ***ptr, zend_uint var TSRMLS_DC) +{ + zend_compiled_variable *cv = &CV_DEF_OF(var); + + if (!EG(active_symbol_table) || + zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void **)ptr)==FAILURE) { + zend_error(E_NOTICE, "Undefined variable: %s", cv->name); + return &EG(uninitialized_zval_ptr); + } + return *ptr; +} + +static zend_never_inline zval **_get_zval_cv_lookup_BP_VAR_IS(zval ***ptr, zend_uint var TSRMLS_DC) +{ + zend_compiled_variable *cv = &CV_DEF_OF(var); + + if (!EG(active_symbol_table) || + zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void **)ptr)==FAILURE) { + return &EG(uninitialized_zval_ptr); + } + return *ptr; +} + +static zend_never_inline zval **_get_zval_cv_lookup_BP_VAR_RW(zval ***ptr, zend_uint var TSRMLS_DC) +{ + zend_compiled_variable *cv = &CV_DEF_OF(var); + + if (!EG(active_symbol_table)) { + Z_ADDREF(EG(uninitialized_zval)); + *ptr = (zval**)EG(current_execute_data)->CVs + (EG(active_op_array)->last_var + var); + **ptr = &EG(uninitialized_zval); + zend_error(E_NOTICE, "Undefined variable: %s", cv->name); + } else if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void **)ptr)==FAILURE) { + Z_ADDREF(EG(uninitialized_zval)); + zend_hash_quick_update(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, &EG(uninitialized_zval_ptr), sizeof(zval *), (void **)ptr); + zend_error(E_NOTICE, "Undefined variable: %s", cv->name); + } + return *ptr; +} + +static zend_never_inline zval **_get_zval_cv_lookup_BP_VAR_W(zval ***ptr, zend_uint var TSRMLS_DC) +{ + zend_compiled_variable *cv = &CV_DEF_OF(var); + + if (!EG(active_symbol_table)) { + Z_ADDREF(EG(uninitialized_zval)); + *ptr = (zval**)EG(current_execute_data)->CVs + (EG(active_op_array)->last_var + var); + **ptr = &EG(uninitialized_zval); + } else if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void **)ptr)==FAILURE) { + Z_ADDREF(EG(uninitialized_zval)); + zend_hash_quick_update(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, &EG(uninitialized_zval_ptr), sizeof(zval *), (void **)ptr); + } + return *ptr; +} + +static zend_always_inline zval *_get_zval_ptr_cv(zend_uint var, int type TSRMLS_DC) +{ + zval ***ptr = &CV_OF(var); + if (UNEXPECTED(*ptr == NULL)) { - return *_get_zval_cv_lookup(ptr, node->u.var, type TSRMLS_CC); + return *_get_zval_cv_lookup(ptr, var, type TSRMLS_CC); } return **ptr; } -static inline zval *_get_zval_ptr(znode *node, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) +static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_R(zval ***CVs, zend_uint var TSRMLS_DC) { + zval ***ptr = &CV(var); + + if (UNEXPECTED(*ptr == NULL)) { + return *_get_zval_cv_lookup_BP_VAR_R(ptr, var TSRMLS_CC); + } + return **ptr; +} + +static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_UNSET(zval ***CVs, zend_uint var TSRMLS_DC) +{ + zval ***ptr = &CV(var); + + if (UNEXPECTED(*ptr == NULL)) { + return *_get_zval_cv_lookup_BP_VAR_UNSET(ptr, var TSRMLS_CC); + } + return **ptr; +} + +static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_IS(zval ***CVs, zend_uint var TSRMLS_DC) +{ + zval ***ptr = &CV(var); + + if (UNEXPECTED(*ptr == NULL)) { + return *_get_zval_cv_lookup_BP_VAR_IS(ptr, var TSRMLS_CC); + } + return **ptr; +} + +static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_RW(zval ***CVs, zend_uint var TSRMLS_DC) +{ + zval ***ptr = &CV(var); + + if (UNEXPECTED(*ptr == NULL)) { + return *_get_zval_cv_lookup_BP_VAR_RW(ptr, var TSRMLS_CC); + } + return **ptr; +} + +static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_W(zval ***CVs, zend_uint var TSRMLS_DC) +{ + zval ***ptr = &CV(var); + + if (UNEXPECTED(*ptr == NULL)) { + return *_get_zval_cv_lookup_BP_VAR_W(ptr, var TSRMLS_CC); + } + return **ptr; +} + +static inline zval *_get_zval_ptr(int op_type, const znode_op *node, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) +{ /* should_free->is_var = 0; */ - switch (node->op_type) { + switch (op_type) { case IS_CONST: should_free->var = 0; - return &node->u.constant; + return node->zv; break; case IS_TMP_VAR: - should_free->var = TMP_FREE(&T(node->u.var).tmp_var); - return &T(node->u.var).tmp_var; + should_free->var = TMP_FREE(&T(node->var).tmp_var); + return &T(node->var).tmp_var; break; case IS_VAR: - return _get_zval_ptr_var(node, Ts, should_free TSRMLS_CC); + return _get_zval_ptr_var(node->var, Ts, should_free TSRMLS_CC); break; case IS_UNUSED: should_free->var = 0; @@ -277,43 +364,93 @@ static inline zval *_get_zval_ptr(znode *node, const t break; case IS_CV: should_free->var = 0; - return _get_zval_ptr_cv(node, Ts, type TSRMLS_CC); + return _get_zval_ptr_cv(node->var, type TSRMLS_CC); break; EMPTY_SWITCH_DEFAULT_CASE() } return NULL; } -static zend_always_inline zval **_get_zval_ptr_ptr_var(const znode *node, const temp_variable *Ts, zend_free_op *should_free TSRMLS_DC) +static zend_always_inline zval **_get_zval_ptr_ptr_var(zend_uint var, const temp_variable *Ts, zend_free_op *should_free TSRMLS_DC) { - zval** ptr_ptr = T(node->u.var).var.ptr_ptr; + zval** ptr_ptr = T(var).var.ptr_ptr; if (EXPECTED(ptr_ptr != NULL)) { PZVAL_UNLOCK(*ptr_ptr, should_free); } else { /* string offset */ - PZVAL_UNLOCK(T(node->u.var).str_offset.str, should_free); + PZVAL_UNLOCK(T(var).str_offset.str, should_free); } return ptr_ptr; } -static zend_always_inline zval **_get_zval_ptr_ptr_cv(const znode *node, const temp_variable *Ts, int type TSRMLS_DC) +static zend_always_inline zval **_get_zval_ptr_ptr_cv(zend_uint var, int type TSRMLS_DC) { - zval ***ptr = &CV_OF(node->u.var); + zval ***ptr = &CV_OF(var); if (UNEXPECTED(*ptr == NULL)) { - return _get_zval_cv_lookup(ptr, node->u.var, type TSRMLS_CC); + return _get_zval_cv_lookup(ptr, var, type TSRMLS_CC); } return *ptr; } -static inline zval **_get_zval_ptr_ptr(const znode *node, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) +static zend_always_inline zval **_get_zval_ptr_ptr_cv_BP_VAR_R(zval ***CVs, zend_uint var TSRMLS_DC) { - if (node->op_type == IS_CV) { + zval ***ptr = &CV(var); + + if (UNEXPECTED(*ptr == NULL)) { + return _get_zval_cv_lookup_BP_VAR_R(ptr, var TSRMLS_CC); + } + return *ptr; +} + +static zend_always_inline zval **_get_zval_ptr_ptr_cv_BP_VAR_UNSET(zval ***CVs, zend_uint var TSRMLS_DC) +{ + zval ***ptr = &CV(var); + + if (UNEXPECTED(*ptr == NULL)) { + return _get_zval_cv_lookup_BP_VAR_UNSET(ptr, var TSRMLS_CC); + } + return *ptr; +} + +static zend_always_inline zval **_get_zval_ptr_ptr_cv_BP_VAR_IS(zval ***CVs, zend_uint var TSRMLS_DC) +{ + zval ***ptr = &CV(var); + + if (UNEXPECTED(*ptr == NULL)) { + return _get_zval_cv_lookup_BP_VAR_IS(ptr, var TSRMLS_CC); + } + return *ptr; +} + +static zend_always_inline zval **_get_zval_ptr_ptr_cv_BP_VAR_RW(zval ***CVs, zend_uint var TSRMLS_DC) +{ + zval ***ptr = &CV(var); + + if (UNEXPECTED(*ptr == NULL)) { + return _get_zval_cv_lookup_BP_VAR_RW(ptr, var TSRMLS_CC); + } + return *ptr; +} + +static zend_always_inline zval **_get_zval_ptr_ptr_cv_BP_VAR_W(zval ***CVs, zend_uint var TSRMLS_DC) +{ + zval ***ptr = &CV(var); + + if (UNEXPECTED(*ptr == NULL)) { + return _get_zval_cv_lookup_BP_VAR_W(ptr, var TSRMLS_CC); + } + return *ptr; +} + +static inline zval **_get_zval_ptr_ptr(int op_type, const znode_op *node, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) +{ + if (op_type == IS_CV) { should_free->var = 0; - return _get_zval_ptr_ptr_cv(node, Ts, type TSRMLS_CC); - } else if (node->op_type == IS_VAR) { - return _get_zval_ptr_ptr_var(node, Ts, should_free TSRMLS_CC); + return _get_zval_ptr_ptr_cv(node->var, type TSRMLS_CC); + } else if (op_type == IS_VAR) { + return _get_zval_ptr_ptr_var(node->var, Ts, should_free TSRMLS_CC); } else { should_free->var = 0; return NULL; @@ -330,9 +467,9 @@ static zend_always_inline zval *_get_obj_zval_ptr_unus } } -static inline zval **_get_obj_zval_ptr_ptr(const znode *op, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) +static inline zval **_get_obj_zval_ptr_ptr(int op_type, const znode_op *op, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) { - if (op->op_type == IS_UNUSED) { + if (op_type == IS_UNUSED) { if (EXPECTED(EG(This) != NULL)) { /* this should actually never be modified, _ptr_ptr is modified only when the object is empty */ @@ -342,7 +479,7 @@ static inline zval **_get_obj_zval_ptr_ptr(const znode zend_error_noreturn(E_ERROR, "Using $this when not in object context"); } } - return get_zval_ptr_ptr(op, Ts, should_free, type); + return get_zval_ptr_ptr(op_type, op, Ts, should_free, type); } static zend_always_inline zval **_get_obj_zval_ptr_ptr_unused(TSRMLS_D) @@ -355,9 +492,9 @@ static zend_always_inline zval **_get_obj_zval_ptr_ptr } } -static inline zval *_get_obj_zval_ptr(znode *op, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) +static inline zval *_get_obj_zval_ptr(int op_type, znode_op *op, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) { - if (op->op_type == IS_UNUSED) { + if (op_type == IS_UNUSED) { if (EXPECTED(EG(This) != NULL)) { should_free->var = 0; return EG(This); @@ -365,30 +502,15 @@ static inline zval *_get_obj_zval_ptr(znode *op, const zend_error_noreturn(E_ERROR, "Using $this when not in object context"); } } - return get_zval_ptr(op, Ts, should_free, type); + return get_zval_ptr(op_type, op, Ts, should_free, type); } -static inline void zend_switch_free(temp_variable *T, int extended_value TSRMLS_DC) -{ - if (T->var.ptr) { - if (extended_value & ZEND_FE_RESET_VARIABLE) { /* foreach() free */ - Z_DELREF_P(T->var.ptr); - } - zval_ptr_dtor(&T->var.ptr); - } else if (!T->var.ptr_ptr) { - /* perform the equivalent of equivalent of a - * quick & silent get_zval_ptr, and FREE_OP - */ - PZVAL_UNLOCK_FREE(T->str_offset.str); - } -} - static void zend_assign_to_variable_reference(zval **variable_ptr_ptr, zval **value_ptr_ptr TSRMLS_DC) { zval *variable_ptr = *variable_ptr_ptr; zval *value_ptr = *value_ptr_ptr; - if (variable_ptr == EG(error_zval_ptr) || value_ptr==EG(error_zval_ptr)) { + if (variable_ptr == &EG(error_zval) || value_ptr == &EG(error_zval)) { variable_ptr_ptr = &EG(uninitialized_zval_ptr); } else if (variable_ptr != value_ptr) { if (!PZVAL_IS_REF(value_ptr)) { @@ -396,7 +518,7 @@ static void zend_assign_to_variable_reference(zval **v Z_DELREF_P(value_ptr); if (Z_REFCOUNT_P(value_ptr)>0) { ALLOC_ZVAL(*value_ptr_ptr); - **value_ptr_ptr = *value_ptr; + ZVAL_COPY_VALUE(*value_ptr_ptr, value_ptr); value_ptr = *value_ptr_ptr; zendi_zval_copy_ctor(*value_ptr); } @@ -411,12 +533,12 @@ static void zend_assign_to_variable_reference(zval **v } else if (!Z_ISREF_P(variable_ptr)) { if (variable_ptr_ptr == value_ptr_ptr) { SEPARATE_ZVAL(variable_ptr_ptr); - } else if (variable_ptr==EG(uninitialized_zval_ptr) + } else if (variable_ptr==&EG(uninitialized_zval) || Z_REFCOUNT_P(variable_ptr)>2) { /* we need to separate */ Z_SET_REFCOUNT_P(variable_ptr, Z_REFCOUNT_P(variable_ptr) - 2); ALLOC_ZVAL(*variable_ptr_ptr); - **variable_ptr_ptr = *variable_ptr; + ZVAL_COPY_VALUE(*variable_ptr_ptr, variable_ptr); zval_copy_ctor(*variable_ptr_ptr); *value_ptr_ptr = *variable_ptr_ptr; Z_SET_REFCOUNT_PP(variable_ptr_ptr, 2); @@ -432,15 +554,14 @@ static inline void make_real_object(zval **object_ptr || (Z_TYPE_PP(object_ptr) == IS_BOOL && Z_LVAL_PP(object_ptr) == 0) || (Z_TYPE_PP(object_ptr) == IS_STRING && Z_STRLEN_PP(object_ptr) == 0) ) { - zend_error(E_STRICT, "Creating default object from empty value"); - SEPARATE_ZVAL_IF_NOT_REF(object_ptr); zval_dtor(*object_ptr); object_init(*object_ptr); + zend_error(E_WARNING, "Creating default object from empty value"); } } -static inline char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, ulong fetch_type, const char **class_name, zend_class_entry **pce TSRMLS_DC) +ZEND_API char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, ulong fetch_type, const char **class_name, zend_class_entry **pce TSRMLS_DC) { *pce = zend_fetch_class(cur_arg_info->class_name, cur_arg_info->class_name_len, (fetch_type | ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD) TSRMLS_CC); @@ -452,12 +573,12 @@ static inline char * zend_verify_arg_class_kind(const } } -static inline int zend_verify_arg_error(const zend_function *zf, zend_uint arg_num, const zend_arg_info *cur_arg_info, const char *need_msg, const char *need_kind, const char *given_msg, char *given_kind TSRMLS_DC) +ZEND_API int zend_verify_arg_error(int error_type, const zend_function *zf, zend_uint arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind TSRMLS_DC) { zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; - char *fname = zf->common.function_name; + const char *fname = zf->common.function_name; char *fsep; - char *fclass; + const char *fclass; if (zf->common.scope) { fsep = "::"; @@ -468,9 +589,9 @@ static inline int zend_verify_arg_error(const zend_fun } if (ptr && ptr->op_array) { - zend_error(E_RECOVERABLE_ERROR, "Argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d and defined", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, ptr->op_array->filename, ptr->opline->lineno); + zend_error(error_type, "Argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d and defined", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, ptr->op_array->filename, ptr->opline->lineno); } else { - zend_error(E_RECOVERABLE_ERROR, "Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); + zend_error(error_type, "Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); } return 0; } @@ -493,40 +614,55 @@ static inline int zend_verify_arg_type(zend_function * if (!arg) { need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC); - return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, "none", "" TSRMLS_CC); + return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, "none", "" TSRMLS_CC); } if (Z_TYPE_P(arg) == IS_OBJECT) { need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC); if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) { - return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC); + return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC); } } else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) { need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC); - return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, zend_zval_type_name(arg), "" TSRMLS_CC); + return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "" TSRMLS_CC); } - } else if (cur_arg_info->array_type_hint) { - if (!arg) { - return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be an array", "", "none", "" TSRMLS_CC); + } else if (cur_arg_info->type_hint) { + switch(cur_arg_info->type_hint) { + case IS_ARRAY: + if (!arg) { + return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type array", "", "none", "" TSRMLS_CC); + } + + if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { + return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be of the type array", "", zend_zval_type_name(arg), "" TSRMLS_CC); + } + break; + + case IS_CALLABLE: + if (!arg) { + return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be callable", "", "none", "" TSRMLS_CC); + } + if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL TSRMLS_CC) && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { + return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "be callable", "", zend_zval_type_name(arg), "" TSRMLS_CC); + } + break; + + default: + zend_error(E_ERROR, "Unknown typehint"); } - if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null)) { - return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be an array", "", zend_zval_type_name(arg), "" TSRMLS_CC); - } } return 1; } -static inline void zend_assign_to_object(znode *result, zval **object_ptr, zval *property_name, znode *value_op, const temp_variable *Ts, int opcode TSRMLS_DC) - +static inline void zend_assign_to_object(zval **retval, zval **object_ptr, zval *property_name, int value_type, znode_op *value_op, const temp_variable *Ts, int opcode, const zend_literal *key TSRMLS_DC) { zval *object = *object_ptr; zend_free_op free_value; - zval *value = get_zval_ptr(value_op, Ts, &free_value, BP_VAR_R); - zval **retval = &T(result->u.var).var.ptr; + zval *value = get_zval_ptr(value_type, value_op, Ts, &free_value, BP_VAR_R); if (Z_TYPE_P(object) != IS_OBJECT) { - if (object == EG(error_zval_ptr)) { - if (!RETURN_VALUE_UNUSED(result)) { - *retval = EG(uninitialized_zval_ptr); + if (object == &EG(error_zval)) { + if (retval) { + *retval = &EG(uninitialized_zval); PZVAL_LOCK(*retval); } FREE_OP(free_value); @@ -538,7 +674,7 @@ static inline void zend_assign_to_object(znode *result SEPARATE_ZVAL_IF_NOT_REF(object_ptr); object = *object_ptr; Z_ADDREF_P(object); - zend_error(E_STRICT, "Creating default object from empty value"); + zend_error(E_WARNING, "Creating default object from empty value"); if (Z_REFCOUNT_P(object) == 1) { /* object was removed by error handler, nothing to assign to */ zval_ptr_dtor(&object); @@ -554,28 +690,28 @@ static inline void zend_assign_to_object(znode *result object_init(object); } else { zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (!RETURN_VALUE_UNUSED(result)) { - *retval = EG(uninitialized_zval_ptr); + if (retval) { + *retval = &EG(uninitialized_zval); PZVAL_LOCK(*retval); } FREE_OP(free_value); return; } } - + /* separate our value if necessary */ - if (value_op->op_type == IS_TMP_VAR) { + if (value_type == IS_TMP_VAR) { zval *orig_value = value; ALLOC_ZVAL(value); - *value = *orig_value; + ZVAL_COPY_VALUE(value, orig_value); Z_UNSET_ISREF_P(value); Z_SET_REFCOUNT_P(value, 0); - } else if (value_op->op_type == IS_CONST) { + } else if (value_type == IS_CONST) { zval *orig_value = value; ALLOC_ZVAL(value); - *value = *orig_value; + ZVAL_COPY_VALUE(value, orig_value); Z_UNSET_ISREF_P(value); Z_SET_REFCOUNT_P(value, 0); zval_copy_ctor(value); @@ -586,19 +722,19 @@ static inline void zend_assign_to_object(znode *result if (opcode == ZEND_ASSIGN_OBJ) { if (!Z_OBJ_HT_P(object)->write_property) { zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (!RETURN_VALUE_UNUSED(result)) { - *retval = EG(uninitialized_zval_ptr); - PZVAL_LOCK(*retval); + if (retval) { + *retval = &EG(uninitialized_zval); + PZVAL_LOCK(&EG(uninitialized_zval)); } - if (value_op->op_type == IS_TMP_VAR) { + if (value_type == IS_TMP_VAR) { FREE_ZVAL(value); - } else if (value_op->op_type == IS_CONST) { + } else if (value_type == IS_CONST) { zval_ptr_dtor(&value); } FREE_OP(free_value); return; } - Z_OBJ_HT_P(object)->write_property(object, property_name, value TSRMLS_CC); + Z_OBJ_HT_P(object)->write_property(object, property_name, value, key TSRMLS_CC); } else { /* Note: property_name in this case is really the array index! */ if (!Z_OBJ_HT_P(object)->write_dimension) { @@ -607,8 +743,8 @@ static inline void zend_assign_to_object(znode *result Z_OBJ_HT_P(object)->write_dimension(object, property_name, value TSRMLS_CC); } - if (!RETURN_VALUE_UNUSED(result) && !EG(exception)) { - AI_SET_PTR(T(result->u.var).var, value); + if (retval && !EG(exception)) { + *retval = value; PZVAL_LOCK(value); } zval_ptr_dtor(&value); @@ -625,17 +761,30 @@ static inline int zend_assign_to_string_offset(const t } if (T->str_offset.offset >= Z_STRLEN_P(T->str_offset.str)) { - Z_STRVAL_P(T->str_offset.str) = (char *) erealloc(Z_STRVAL_P(T->str_offset.str), T->str_offset.offset+1+1); + if (IS_INTERNED(Z_STRVAL_P(T->str_offset.str))) { + char *tmp = (char *) emalloc(T->str_offset.offset+1+1); + + memcpy(tmp, Z_STRVAL_P(T->str_offset.str), Z_STRLEN_P(T->str_offset.str)+1); + Z_STRVAL_P(T->str_offset.str) = tmp; + } else { + Z_STRVAL_P(T->str_offset.str) = (char *) erealloc(Z_STRVAL_P(T->str_offset.str), T->str_offset.offset+1+1); + } memset(Z_STRVAL_P(T->str_offset.str) + Z_STRLEN_P(T->str_offset.str), ' ', T->str_offset.offset - Z_STRLEN_P(T->str_offset.str)); Z_STRVAL_P(T->str_offset.str)[T->str_offset.offset+1] = 0; Z_STRLEN_P(T->str_offset.str) = T->str_offset.offset+1; + } else if (IS_INTERNED(Z_STRVAL_P(T->str_offset.str))) { + char *tmp = (char *) emalloc(Z_STRLEN_P(T->str_offset.str) + 1); + + memcpy(tmp, Z_STRVAL_P(T->str_offset.str), Z_STRLEN_P(T->str_offset.str) + 1); + Z_STRVAL_P(T->str_offset.str) = tmp; } if (Z_TYPE_P(value) != IS_STRING) { - zval tmp = *value; + zval tmp; + ZVAL_COPY_VALUE(&tmp, value); if (value_type != IS_TMP_VAR) { zval_copy_ctor(&tmp); } @@ -659,92 +808,139 @@ static inline int zend_assign_to_string_offset(const t return 1; } -static inline zval* zend_assign_to_variable(zval **variable_ptr_ptr, zval *value, int is_tmp_var TSRMLS_DC) + +static inline zval* zend_assign_tmp_to_variable(zval **variable_ptr_ptr, zval *value TSRMLS_DC) { zval *variable_ptr = *variable_ptr_ptr; zval garbage; - if (variable_ptr == EG(error_zval_ptr)) { - if (is_tmp_var) { - zval_dtor(value); + if (Z_TYPE_P(variable_ptr) == IS_OBJECT && + UNEXPECTED(Z_OBJ_HANDLER_P(variable_ptr, set) != NULL)) { + Z_OBJ_HANDLER_P(variable_ptr, set)(variable_ptr_ptr, value TSRMLS_CC); + return variable_ptr; + } + + if (UNEXPECTED(Z_REFCOUNT_P(variable_ptr) > 1) && + EXPECTED(!PZVAL_IS_REF(variable_ptr))) { + /* we need to split */ + Z_DELREF_P(variable_ptr); + GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr); + ALLOC_ZVAL(variable_ptr); + INIT_PZVAL_COPY(variable_ptr, value); + *variable_ptr_ptr = variable_ptr; + return variable_ptr; + } else { + if (EXPECTED(Z_TYPE_P(variable_ptr) <= IS_BOOL)) { + /* nothing to destroy */ + ZVAL_COPY_VALUE(variable_ptr, value); + } else { + ZVAL_COPY_VALUE(&garbage, variable_ptr); + ZVAL_COPY_VALUE(variable_ptr, value); + _zval_dtor_func(&garbage ZEND_FILE_LINE_CC); } - return EG(uninitialized_zval_ptr); + return variable_ptr; } +} - if (Z_TYPE_P(variable_ptr) == IS_OBJECT && Z_OBJ_HANDLER_P(variable_ptr, set)) { +static inline zval* zend_assign_const_to_variable(zval **variable_ptr_ptr, zval *value TSRMLS_DC) +{ + zval *variable_ptr = *variable_ptr_ptr; + zval garbage; + + if (Z_TYPE_P(variable_ptr) == IS_OBJECT && + UNEXPECTED(Z_OBJ_HANDLER_P(variable_ptr, set) != NULL)) { Z_OBJ_HANDLER_P(variable_ptr, set)(variable_ptr_ptr, value TSRMLS_CC); return variable_ptr; } - if (PZVAL_IS_REF(variable_ptr)) { - if (variable_ptr!=value) { - zend_uint refcount = Z_REFCOUNT_P(variable_ptr); - - garbage = *variable_ptr; - *variable_ptr = *value; - Z_SET_REFCOUNT_P(variable_ptr, refcount); - Z_SET_ISREF_P(variable_ptr); - if (!is_tmp_var) { - zendi_zval_copy_ctor(*variable_ptr); - } - zendi_zval_dtor(garbage); - return variable_ptr; + if (UNEXPECTED(Z_REFCOUNT_P(variable_ptr) > 1) && + EXPECTED(!PZVAL_IS_REF(variable_ptr))) { + /* we need to split */ + Z_DELREF_P(variable_ptr); + GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr); + ALLOC_ZVAL(variable_ptr); + INIT_PZVAL_COPY(variable_ptr, value); + zval_copy_ctor(variable_ptr); + *variable_ptr_ptr = variable_ptr; + return variable_ptr; + } else { + if (EXPECTED(Z_TYPE_P(variable_ptr) <= IS_BOOL)) { + /* nothing to destroy */ + ZVAL_COPY_VALUE(variable_ptr, value); + zendi_zval_copy_ctor(*variable_ptr); + } else { + ZVAL_COPY_VALUE(&garbage, variable_ptr); + ZVAL_COPY_VALUE(variable_ptr, value); + zendi_zval_copy_ctor(*variable_ptr); + _zval_dtor_func(&garbage ZEND_FILE_LINE_CC); } - } else { - if (Z_DELREF_P(variable_ptr)==0) { - if (!is_tmp_var) { - if (variable_ptr==value) { - Z_ADDREF_P(variable_ptr); - } else if (PZVAL_IS_REF(value)) { - garbage = *variable_ptr; - *variable_ptr = *value; - INIT_PZVAL(variable_ptr); - zval_copy_ctor(variable_ptr); - zendi_zval_dtor(garbage); - return variable_ptr; + return variable_ptr; + } +} + +static inline zval* zend_assign_to_variable(zval **variable_ptr_ptr, zval *value TSRMLS_DC) +{ + zval *variable_ptr = *variable_ptr_ptr; + zval garbage; + + if (Z_TYPE_P(variable_ptr) == IS_OBJECT && + UNEXPECTED(Z_OBJ_HANDLER_P(variable_ptr, set) != NULL)) { + Z_OBJ_HANDLER_P(variable_ptr, set)(variable_ptr_ptr, value TSRMLS_CC); + return variable_ptr; + } + + if (EXPECTED(!PZVAL_IS_REF(variable_ptr))) { + if (Z_REFCOUNT_P(variable_ptr)==1) { + if (UNEXPECTED(variable_ptr == value)) { + return variable_ptr; + } else if (EXPECTED(!PZVAL_IS_REF(value))) { + Z_ADDREF_P(value); + *variable_ptr_ptr = value; + if (EXPECTED(variable_ptr != &EG(uninitialized_zval))) { + GC_REMOVE_ZVAL_FROM_BUFFER(variable_ptr); + zval_dtor(variable_ptr); + efree(variable_ptr); } else { - Z_ADDREF_P(value); - *variable_ptr_ptr = value; - if (variable_ptr != &EG(uninitialized_zval)) { - GC_REMOVE_ZVAL_FROM_BUFFER(variable_ptr); - zval_dtor(variable_ptr); - efree(variable_ptr); - } - return value; + Z_DELREF_P(variable_ptr); } + return value; } else { - garbage = *variable_ptr; - *variable_ptr = *value; - INIT_PZVAL(variable_ptr); - zendi_zval_dtor(garbage); - return variable_ptr; + goto copy_value; } } else { /* we need to split */ - GC_ZVAL_CHECK_POSSIBLE_ROOT(*variable_ptr_ptr); - if (!is_tmp_var) { - if (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) { - ALLOC_ZVAL(variable_ptr); - *variable_ptr_ptr = variable_ptr; - *variable_ptr = *value; - Z_SET_REFCOUNT_P(variable_ptr, 1); - zval_copy_ctor(variable_ptr); - } else { - *variable_ptr_ptr = value; - Z_ADDREF_P(value); - } + Z_DELREF_P(variable_ptr); + GC_ZVAL_CHECK_POSSIBLE_ROOT(variable_ptr); + if (PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) > 0) { + ALLOC_ZVAL(variable_ptr); + *variable_ptr_ptr = variable_ptr; + INIT_PZVAL_COPY(variable_ptr, value); + zval_copy_ctor(variable_ptr); + return variable_ptr; } else { - ALLOC_ZVAL(*variable_ptr_ptr); - Z_SET_REFCOUNT_P(value, 1); - **variable_ptr_ptr = *value; + *variable_ptr_ptr = value; + Z_ADDREF_P(value); + Z_UNSET_ISREF_P(value); + return value; } } - Z_UNSET_ISREF_PP(variable_ptr_ptr); + } else { + if (EXPECTED(variable_ptr != value)) { +copy_value: + if (EXPECTED(Z_TYPE_P(variable_ptr) <= IS_BOOL)) { + /* nothing to destroy */ + ZVAL_COPY_VALUE(variable_ptr, value); + zendi_zval_copy_ctor(*variable_ptr); + } else { + ZVAL_COPY_VALUE(&garbage, variable_ptr); + ZVAL_COPY_VALUE(variable_ptr, value); + zendi_zval_copy_ctor(*variable_ptr); + _zval_dtor_func(&garbage ZEND_FILE_LINE_CC); + } + } + return variable_ptr; } - - return *variable_ptr_ptr; } - /* Utility Functions for Extensions */ static void zend_extension_statement_handler(const zend_extension *extension, zend_op_array *op_array TSRMLS_DC) { @@ -770,9 +966,9 @@ static void zend_extension_fcall_end_handler(const zen } -static inline HashTable *zend_get_target_symbol_table(const zend_op *opline, const temp_variable *Ts, int type, const zval *variable TSRMLS_DC) +static inline HashTable *zend_get_target_symbol_table(int fetch_type TSRMLS_DC) { - switch (opline->op2.u.EA.type) { + switch (fetch_type) { case ZEND_FETCH_LOCAL: if (!EG(active_symbol_table)) { zend_rebuild_symbol_table(TSRMLS_C); @@ -795,26 +991,37 @@ static inline HashTable *zend_get_target_symbol_table( return NULL; } -static inline zval **zend_fetch_dimension_address_inner(HashTable *ht, const zval *dim, int type TSRMLS_DC) +static inline zval **zend_fetch_dimension_address_inner(HashTable *ht, const zval *dim, int dim_type, int type TSRMLS_DC) { zval **retval; char *offset_key; int offset_key_length; - long index; + ulong hval; switch (dim->type) { case IS_NULL: offset_key = ""; offset_key_length = 0; + hval = zend_inline_hash_func("", 1); goto fetch_string_dim; case IS_STRING: - + offset_key = dim->value.str.val; offset_key_length = dim->value.str.len; - + + if (dim_type == IS_CONST) { + hval = Z_HASH_P(dim); + } else { + ZEND_HANDLE_NUMERIC_EX(offset_key, offset_key_length+1, hval, goto num_index); + if (IS_INTERNED(offset_key)) { + hval = INTERNED_HASH(offset_key); + } else { + hval = zend_hash_func(offset_key, offset_key_length+1); + } + } fetch_string_dim: - if (zend_symtable_find(ht, offset_key, offset_key_length+1, (void **) &retval) == FAILURE) { + if (zend_hash_quick_find(ht, offset_key, offset_key_length+1, hval, (void **) &retval) == FAILURE) { switch (type) { case BP_VAR_R: zend_error(E_NOTICE, "Undefined index: %s", offset_key); @@ -830,39 +1037,39 @@ fetch_string_dim: zval *new_zval = &EG(uninitialized_zval); Z_ADDREF_P(new_zval); - zend_symtable_update(ht, offset_key, offset_key_length+1, &new_zval, sizeof(zval *), (void **) &retval); + zend_hash_quick_update(ht, offset_key, offset_key_length+1, hval, &new_zval, sizeof(zval *), (void **) &retval); } break; } } break; case IS_DOUBLE: - index = zend_dval_to_lval(Z_DVAL_P(dim)); + hval = zend_dval_to_lval(Z_DVAL_P(dim)); goto num_index; case IS_RESOURCE: zend_error(E_STRICT, "Resource ID#%ld used as offset, casting to integer (%ld)", Z_LVAL_P(dim), Z_LVAL_P(dim)); /* Fall Through */ case IS_BOOL: case IS_LONG: - index = Z_LVAL_P(dim); + hval = Z_LVAL_P(dim); num_index: - if (zend_hash_index_find(ht, index, (void **) &retval) == FAILURE) { + if (zend_hash_index_find(ht, hval, (void **) &retval) == FAILURE) { switch (type) { case BP_VAR_R: - zend_error(E_NOTICE,"Undefined offset: %ld", index); + zend_error(E_NOTICE,"Undefined offset: %ld", hval); /* break missing intentionally */ case BP_VAR_UNSET: case BP_VAR_IS: retval = &EG(uninitialized_zval_ptr); break; case BP_VAR_RW: - zend_error(E_NOTICE,"Undefined offset: %ld", index); + zend_error(E_NOTICE,"Undefined offset: %ld", hval); /* break missing intentionally */ case BP_VAR_W: { zval *new_zval = &EG(uninitialized_zval); Z_ADDREF_P(new_zval); - zend_hash_index_update(ht, index, &new_zval, sizeof(zval *), (void **) &retval); + zend_hash_index_update(ht, hval, &new_zval, sizeof(zval *), (void **) &retval); } break; } @@ -877,7 +1084,7 @@ num_index: return retval; } -static void zend_fetch_dimension_address(temp_variable *result, zval **container_ptr, zval *dim, int dim_is_tmp_var, int type TSRMLS_DC) +static void zend_fetch_dimension_address(temp_variable *result, zval **container_ptr, zval *dim, int dim_type, int type TSRMLS_DC) { zval *container = *container_ptr; zval **retval; @@ -900,7 +1107,7 @@ fetch_from_array: Z_DELREF_P(new_zval); } } else { - retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, type TSRMLS_CC); + retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type TSRMLS_CC); } result->var.ptr_ptr = retval; PZVAL_LOCK(*retval); @@ -908,7 +1115,7 @@ fetch_from_array: break; case IS_NULL: - if (container == EG(error_zval_ptr)) { + if (container == &EG(error_zval)) { result->var.ptr_ptr = &EG(error_zval_ptr); PZVAL_LOCK(EG(error_zval_ptr)); } else if (type != BP_VAR_UNSET) { @@ -938,14 +1145,27 @@ convert_to_array: zend_error_noreturn(E_ERROR, "[] operator not supported for strings"); } + if (type != BP_VAR_UNSET) { + SEPARATE_ZVAL_IF_NOT_REF(container_ptr); + } + if (Z_TYPE_P(dim) != IS_LONG) { + switch(Z_TYPE_P(dim)) { /* case IS_LONG: */ case IS_STRING: + if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, -1)) { + break; + } + if (type != BP_VAR_UNSET) { + zend_error(E_WARNING, "Illegal string offset '%s'", dim->value.str.val); + } + + break; case IS_DOUBLE: case IS_NULL: case IS_BOOL: - /* do nothing */ + zend_error(E_NOTICE, "String offset cast occurred"); break; default: zend_error(E_WARNING, "Illegal offset type"); @@ -957,15 +1177,11 @@ convert_to_array: convert_to_long(&tmp); dim = &tmp; } - if (type != BP_VAR_UNSET) { - SEPARATE_ZVAL_IF_NOT_REF(container_ptr); - } container = *container_ptr; result->str_offset.str = container; PZVAL_LOCK(container); result->str_offset.offset = Z_LVAL_P(dim); - result->var.ptr_ptr = NULL; - result->var.ptr = NULL; + result->str_offset.ptr_ptr = NULL; return; } break; @@ -976,7 +1192,7 @@ convert_to_array: } else { zval *overloaded_result; - if (dim_is_tmp_var) { + if (dim_type == IS_TMP_VAR) { zval *orig = dim; MAKE_REAL_ZVAL_PTR(dim); ZVAL_NULL(orig); @@ -989,7 +1205,7 @@ convert_to_array: zval *tmp = overloaded_result; ALLOC_ZVAL(overloaded_result); - *overloaded_result = *tmp; + ZVAL_COPY_VALUE(overloaded_result, tmp); zval_copy_ctor(overloaded_result); Z_UNSET_ISREF_P(overloaded_result); Z_SET_REFCOUNT_P(overloaded_result, 0); @@ -1003,9 +1219,9 @@ convert_to_array: } else { retval = &EG(error_zval_ptr); } - AI_SET_PTR(result->var, *retval); + AI_SET_PTR(result, *retval); PZVAL_LOCK(*retval); - if (dim_is_tmp_var) { + if (dim_type == IS_TMP_VAR) { zval_ptr_dtor(&dim); } } @@ -1021,8 +1237,8 @@ convert_to_array: default: if (type == BP_VAR_UNSET) { zend_error(E_WARNING, "Cannot unset offset in a non-array variable"); - AI_SET_PTR(result->var, EG(uninitialized_zval_ptr)); - PZVAL_LOCK(EG(uninitialized_zval_ptr)); + AI_SET_PTR(result, &EG(uninitialized_zval)); + PZVAL_LOCK(&EG(uninitialized_zval)); } else { zend_error(E_WARNING, "Cannot use a scalar value as an array"); result->var.ptr_ptr = &EG(error_zval_ptr); @@ -1032,7 +1248,7 @@ convert_to_array: } } -static void zend_fetch_dimension_address_read(temp_variable *result, zval **container_ptr, zval *dim, int dim_is_tmp_var, int type TSRMLS_DC) +static void zend_fetch_dimension_address_read(temp_variable *result, zval **container_ptr, zval *dim, int dim_type, int type TSRMLS_DC) { zval *container = *container_ptr; zval **retval; @@ -1040,54 +1256,66 @@ static void zend_fetch_dimension_address_read(temp_var switch (Z_TYPE_P(container)) { case IS_ARRAY: - retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, type TSRMLS_CC); - if (result) { - AI_SET_PTR(result->var, *retval); - PZVAL_LOCK(*retval); - } + retval = zend_fetch_dimension_address_inner(Z_ARRVAL_P(container), dim, dim_type, type TSRMLS_CC); + AI_SET_PTR(result, *retval); + PZVAL_LOCK(*retval); return; - break; case IS_NULL: - if (result) { - AI_SET_PTR(result->var, EG(uninitialized_zval_ptr)); - PZVAL_LOCK(EG(uninitialized_zval_ptr)); - } + AI_SET_PTR(result, &EG(uninitialized_zval)); + PZVAL_LOCK(&EG(uninitialized_zval)); return; - break; case IS_STRING: { zval tmp; + zval *ptr; if (Z_TYPE_P(dim) != IS_LONG) { switch(Z_TYPE_P(dim)) { /* case IS_LONG: */ case IS_STRING: + if (IS_LONG == is_numeric_string(Z_STRVAL_P(dim), Z_STRLEN_P(dim), NULL, NULL, -1)) { + break; + } + if (type != BP_VAR_IS) { + zend_error(E_WARNING, "Illegal string offset '%s'", dim->value.str.val); + } + break; case IS_DOUBLE: case IS_NULL: case IS_BOOL: - /* do nothing */ + if (type != BP_VAR_IS) { + zend_error(E_NOTICE, "String offset cast occurred"); + } break; default: zend_error(E_WARNING, "Illegal offset type"); break; } - tmp = *dim; + ZVAL_COPY_VALUE(&tmp, dim); zval_copy_ctor(&tmp); convert_to_long(&tmp); dim = &tmp; } - if (result) { - if ((Z_LVAL_P(dim) < 0 || Z_STRLEN_P(container) <= Z_LVAL_P(dim)) && type != BP_VAR_IS) { + + ALLOC_ZVAL(ptr); + INIT_PZVAL(ptr); + Z_TYPE_P(ptr) = IS_STRING; + + if (Z_LVAL_P(dim) < 0 || Z_STRLEN_P(container) <= Z_LVAL_P(dim)) { + if (type != BP_VAR_IS) { zend_error(E_NOTICE, "Uninitialized string offset: %ld", Z_LVAL_P(dim)); } - result->str_offset.str = container; - PZVAL_LOCK(container); - result->str_offset.offset = Z_LVAL_P(dim); - result->var.ptr_ptr = NULL; - result->var.ptr = NULL; + Z_STRVAL_P(ptr) = STR_EMPTY_ALLOC(); + Z_STRLEN_P(ptr) = 0; + } else { + Z_STRVAL_P(ptr) = (char*)emalloc(2); + Z_STRVAL_P(ptr)[0] = Z_STRVAL_P(container)[Z_LVAL_P(dim)]; + Z_STRVAL_P(ptr)[1] = 0; + Z_STRLEN_P(ptr) = 1; } + AI_SET_PTR(result, ptr); return; } break; @@ -1098,7 +1326,7 @@ static void zend_fetch_dimension_address_read(temp_var } else { zval *overloaded_result; - if (dim_is_tmp_var) { + if (dim_type == IS_TMP_VAR) { zval *orig = dim; MAKE_REAL_ZVAL_PTR(dim); ZVAL_NULL(orig); @@ -1106,43 +1334,33 @@ static void zend_fetch_dimension_address_read(temp_var overloaded_result = Z_OBJ_HT_P(container)->read_dimension(container, dim, type TSRMLS_CC); if (overloaded_result) { - if (result) { - AI_SET_PTR(result->var, overloaded_result); - PZVAL_LOCK(overloaded_result); - } else if (Z_REFCOUNT_P(overloaded_result) == 0) { - /* Destroy unused result from offsetGet() magic method */ - Z_SET_REFCOUNT_P(overloaded_result, 1); - zval_ptr_dtor(&overloaded_result); - } + AI_SET_PTR(result, overloaded_result); + PZVAL_LOCK(overloaded_result); } else if (result) { - AI_SET_PTR(result->var, EG(uninitialized_zval_ptr)); - PZVAL_LOCK(EG(uninitialized_zval_ptr)); + AI_SET_PTR(result, &EG(uninitialized_zval)); + PZVAL_LOCK(&EG(uninitialized_zval)); } - if (dim_is_tmp_var) { + if (dim_type == IS_TMP_VAR) { zval_ptr_dtor(&dim); } } return; - break; default: - if (result) { - AI_SET_PTR(result->var, EG(uninitialized_zval_ptr)); - PZVAL_LOCK(EG(uninitialized_zval_ptr)); - } + AI_SET_PTR(result, &EG(uninitialized_zval)); + PZVAL_LOCK(&EG(uninitialized_zval)); return; - break; } } -static void zend_fetch_property_address(temp_variable *result, zval **container_ptr, zval *prop_ptr, int type TSRMLS_DC) +static void zend_fetch_property_address(temp_variable *result, zval **container_ptr, zval *prop_ptr, const zend_literal *key, int type TSRMLS_DC) { zval *container = *container_ptr;; if (Z_TYPE_P(container) != IS_OBJECT) { - if (container == EG(error_zval_ptr)) { + if (container == &EG(error_zval)) { result->var.ptr_ptr = &EG(error_zval_ptr); - PZVAL_LOCK(*result->var.ptr_ptr); + PZVAL_LOCK(EG(error_zval_ptr)); return; } @@ -1165,13 +1383,13 @@ static void zend_fetch_property_address(temp_variable } if (Z_OBJ_HT_P(container)->get_property_ptr_ptr) { - zval **ptr_ptr = Z_OBJ_HT_P(container)->get_property_ptr_ptr(container, prop_ptr TSRMLS_CC); + zval **ptr_ptr = Z_OBJ_HT_P(container)->get_property_ptr_ptr(container, prop_ptr, key TSRMLS_CC); if (NULL == ptr_ptr) { zval *ptr; if (Z_OBJ_HT_P(container)->read_property && - (ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type TSRMLS_CC)) != NULL) { - AI_SET_PTR(result->var, ptr); + (ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, key TSRMLS_CC)) != NULL) { + AI_SET_PTR(result, ptr); PZVAL_LOCK(ptr); } else { zend_error_noreturn(E_ERROR, "Cannot access undefined property for object with overloaded property access"); @@ -1181,9 +1399,9 @@ static void zend_fetch_property_address(temp_variable PZVAL_LOCK(*ptr_ptr); } } else if (Z_OBJ_HT_P(container)->read_property) { - zval *ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type TSRMLS_CC); + zval *ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, key TSRMLS_CC); - AI_SET_PTR(result->var, ptr); + AI_SET_PTR(result, ptr); PZVAL_LOCK(ptr); } else { zend_error(E_WARNING, "This object doesn't support property references"); @@ -1192,21 +1410,11 @@ static void zend_fetch_property_address(temp_variable } } -static inline zend_brk_cont_element* zend_brk_cont(const zval *nest_levels_zval, int array_offset, const zend_op_array *op_array, const temp_variable *Ts TSRMLS_DC) +static inline zend_brk_cont_element* zend_brk_cont(int nest_levels, int array_offset, const zend_op_array *op_array, const temp_variable *Ts TSRMLS_DC) { - zval tmp; - int nest_levels, original_nest_levels; + int original_nest_levels = nest_levels; zend_brk_cont_element *jmp_to; - if (nest_levels_zval->type != IS_LONG) { - tmp = *nest_levels_zval; - zval_copy_ctor(&tmp); - convert_to_long(&tmp); - nest_levels = tmp.value.lval; - } else { - nest_levels = nest_levels_zval->value.lval; - } - original_nest_levels = nest_levels; do { if (array_offset==-1) { zend_error_noreturn(E_ERROR, "Cannot break/continue %d level%s", original_nest_levels, (original_nest_levels == 1) ? "" : "s"); @@ -1217,13 +1425,13 @@ static inline zend_brk_cont_element* zend_brk_cont(con switch (brk_opline->opcode) { case ZEND_SWITCH_FREE: - if (brk_opline->op1.u.EA.type != EXT_TYPE_FREE_ON_RETURN) { - zend_switch_free(&T(brk_opline->op1.u.var), brk_opline->extended_value TSRMLS_CC); + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zval_ptr_dtor(&T(brk_opline->op1.var).var.ptr); } break; case ZEND_FREE: - if (brk_opline->op1.u.EA.type != EXT_TYPE_FREE_ON_RETURN) { - zendi_zval_dtor(T(brk_opline->op1.u.var).tmp_var); + if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN)) { + zendi_zval_dtor(T(brk_opline->op1.var).tmp_var); } break; } @@ -1269,35 +1477,47 @@ ZEND_API opcode_handler_t *zend_opcode_handlers; ZEND_API void execute_internal(zend_execute_data *execute_data_ptr, int return_value_used TSRMLS_DC) { - zval **return_value_ptr = &(*(temp_variable *)((char *) execute_data_ptr->Ts + execute_data_ptr->opline->result.u.var)).var.ptr; - ((zend_internal_function *) execute_data_ptr->function_state.function)->handler(execute_data_ptr->opline->extended_value, *return_value_ptr, execute_data_ptr->function_state.function->common.return_reference?return_value_ptr:NULL, execute_data_ptr->object, return_value_used TSRMLS_CC); + zval **return_value_ptr = &(*(temp_variable *)((char *) execute_data_ptr->Ts + execute_data_ptr->opline->result.var)).var.ptr; + ((zend_internal_function *) execute_data_ptr->function_state.function)->handler(execute_data_ptr->opline->extended_value, *return_value_ptr, (execute_data_ptr->function_state.function->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)?return_value_ptr:NULL, execute_data_ptr->object, return_value_used TSRMLS_CC); } #define ZEND_VM_NEXT_OPCODE() \ CHECK_SYMBOL_TABLES() \ - EX(opline)++; \ + ZEND_VM_INC_OPCODE(); \ ZEND_VM_CONTINUE() #define ZEND_VM_SET_OPCODE(new_op) \ CHECK_SYMBOL_TABLES() \ - EX(opline) = new_op + OPLINE = new_op #define ZEND_VM_JMP(new_op) \ - CHECK_SYMBOL_TABLES() \ if (EXPECTED(!EG(exception))) { \ - EX(opline) = new_op; \ + ZEND_VM_SET_OPCODE(new_op); \ + } else { \ + LOAD_OPLINE(); \ } \ ZEND_VM_CONTINUE() #define ZEND_VM_INC_OPCODE() \ - EX(opline)++ + OPLINE++ +#ifdef __GNUC__ +# define ZEND_VM_GUARD(name) __asm__("#" #name) +#else +# define ZEND_VM_GUARD(name) +#endif + #include "zend_vm_execute.h" ZEND_API int zend_set_user_opcode_handler(zend_uchar opcode, user_opcode_handler_t handler) { if (opcode != ZEND_USER_OPCODE) { - zend_user_opcodes[opcode] = ZEND_USER_OPCODE; + if (handler == NULL) { + /* restore the original handler */ + zend_user_opcodes[opcode] = opcode; + } else { + zend_user_opcodes[opcode] = ZEND_USER_OPCODE; + } zend_user_opcode_handlers[opcode] = handler; return SUCCESS; } @@ -1309,12 +1529,12 @@ ZEND_API user_opcode_handler_t zend_get_user_opcode_ha return zend_user_opcode_handlers[opcode]; } -ZEND_API zval *zend_get_zval_ptr(znode *node, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) { - return get_zval_ptr(node, Ts, should_free, type); +ZEND_API zval *zend_get_zval_ptr(int op_type, const znode_op *node, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) { + return get_zval_ptr(op_type, node, Ts, should_free, type); } -ZEND_API zval **zend_get_zval_ptr_ptr(const znode *node, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) { - return get_zval_ptr_ptr(node, Ts, should_free, type); +ZEND_API zval **zend_get_zval_ptr_ptr(int op_type, const znode_op *node, const temp_variable *Ts, zend_free_op *should_free, int type TSRMLS_DC) { + return get_zval_ptr_ptr(op_type, node, Ts, should_free, type); } /*