diff options
Diffstat (limited to 'Zend/zend_execute.c')
-rw-r--r-- | Zend/zend_execute.c | 1801 |
1 files changed, 1330 insertions, 471 deletions
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 0a505f55e8..3f8b3d5bb2 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | Zend Engine | +----------------------------------------------------------------------+ - | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) | + | Copyright (c) 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 | @@ -56,6 +56,9 @@ # elif defined(__IBMC__) && ZEND_GCC_VERSION >= 4002 && defined(__powerpc64__) # define ZEND_VM_FP_GLOBAL_REG "r28" # define ZEND_VM_IP_GLOBAL_REG "r29" +# elif defined(__GNUC__) && ZEND_GCC_VERSION >= 4008 && defined(__aarch64__) +# define ZEND_VM_FP_GLOBAL_REG "x27" +# define ZEND_VM_IP_GLOBAL_REG "x28" # endif #endif @@ -63,21 +66,28 @@ # pragma GCC diagnostic ignored "-Wvolatile-register-var" register zend_execute_data* volatile execute_data __asm__(ZEND_VM_FP_GLOBAL_REG); # pragma GCC diagnostic warning "-Wvolatile-register-var" +#endif + +#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) # define EXECUTE_DATA_D void # define EXECUTE_DATA_C # 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 +#endif + +#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) +# define OPLINE_D void +# define OPLINE_C +# define OPLINE_DC +# define OPLINE_CC +#else # define OPLINE_D const zend_op* opline # define OPLINE_C opline # define OPLINE_DC , OPLINE_D @@ -132,7 +142,7 @@ 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 zval_ptr_dtor(zv) i_zval_ptr_dtor(zv) #define FREE_VAR_PTR_AND_EXTRACT_RESULT_IF_NECESSARY(free_op, result) do { \ zval *__container_to_free = (free_op); \ @@ -187,7 +197,6 @@ 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; } @@ -262,21 +271,35 @@ static zend_always_inline zval *_get_zval_ptr_var_deref(uint32_t var, zend_free_ return ret; } -static zend_never_inline ZEND_COLD void zval_undefined_cv(uint32_t var EXECUTE_DATA_DC) +static zend_never_inline ZEND_COLD zval* zval_undefined_cv(uint32_t var EXECUTE_DATA_DC) { if (EXPECTED(EG(exception) == NULL)) { zend_string *cv = CV_DEF_OF(EX_VAR_TO_NUM(var)); zend_error(E_NOTICE, "Undefined variable: %s", ZSTR_VAL(cv)); } + return &EG(uninitialized_zval); +} + +static zend_never_inline ZEND_COLD zval* ZEND_FASTCALL _zval_undefined_op1(EXECUTE_DATA_D) +{ + return zval_undefined_cv(EX(opline)->op1.var EXECUTE_DATA_CC); +} + +static zend_never_inline ZEND_COLD zval* ZEND_FASTCALL _zval_undefined_op2(EXECUTE_DATA_D) +{ + return zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); } +#define ZVAL_UNDEFINED_OP1() _zval_undefined_op1(EXECUTE_DATA_C) +#define ZVAL_UNDEFINED_OP2() _zval_undefined_op2(EXECUTE_DATA_C) + 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: case BP_VAR_UNSET: - zval_undefined_cv(var EXECUTE_DATA_CC); - /* break missing intentionally */ + ptr = zval_undefined_cv(var EXECUTE_DATA_CC); + break; case BP_VAR_IS: ptr = &EG(uninitialized_zval); break; @@ -290,25 +313,6 @@ static zend_never_inline ZEND_COLD zval *_get_zval_cv_lookup(zval *ptr, uint32_t return ptr; } -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_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_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_ptr_cv(uint32_t var, int type EXECUTE_DATA_DC) { zval *ret = EX_VAR(var); @@ -344,7 +348,7 @@ static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_R(uint32_t var EXECUTE_D zval *ret = EX_VAR(var); if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) { - return _get_zval_cv_lookup_BP_VAR_R(ret, var EXECUTE_DATA_CC); + return zval_undefined_cv(var EXECUTE_DATA_CC); } return ret; } @@ -354,7 +358,7 @@ static zend_always_inline zval *_get_zval_ptr_cv_deref_BP_VAR_R(uint32_t var EXE zval *ret = EX_VAR(var); if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) { - return _get_zval_cv_lookup_BP_VAR_R(ret, var EXECUTE_DATA_CC); + return zval_undefined_cv(var EXECUTE_DATA_CC); } ZVAL_DEREF(ret); return ret; @@ -365,19 +369,8 @@ static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_UNSET(uint32_t var EXECU zval *ret = EX_VAR(var); if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) { - return _get_zval_cv_lookup_BP_VAR_UNSET(ret, var EXECUTE_DATA_CC); - } - return ret; -} - -static zend_always_inline zval *_get_zval_ptr_cv_deref_BP_VAR_UNSET(uint32_t var EXECUTE_DATA_DC) -{ - zval *ret = EX_VAR(var); - - if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) { - return _get_zval_cv_lookup_BP_VAR_UNSET(ret, var EXECUTE_DATA_CC); + return zval_undefined_cv(var EXECUTE_DATA_CC); } - ZVAL_DEREF(ret); return ret; } @@ -388,54 +381,25 @@ static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_IS(uint32_t var EXECUTE_ return ret; } -static zend_always_inline zval *_get_zval_ptr_cv_deref_BP_VAR_IS(uint32_t var EXECUTE_DATA_DC) -{ - zval *ret = EX_VAR(var); - - ZVAL_DEREF(ret); - return ret; -} - static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_RW(uint32_t var EXECUTE_DATA_DC) { zval *ret = EX_VAR(var); if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) { - return _get_zval_cv_lookup_BP_VAR_RW(ret, var EXECUTE_DATA_CC); - } - return ret; -} - -static zend_always_inline zval *_get_zval_ptr_cv_deref_BP_VAR_RW(uint32_t var EXECUTE_DATA_DC) -{ - zval *ret = EX_VAR(var); - - if (UNEXPECTED(Z_TYPE_P(ret) == IS_UNDEF)) { - return _get_zval_cv_lookup_BP_VAR_RW(ret, var EXECUTE_DATA_CC); - } - ZVAL_DEREF(ret); - return ret; -} - -static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_W(uint32_t var EXECUTE_DATA_DC) -{ - zval *ret = EX_VAR(var); - - if (Z_TYPE_P(ret) == IS_UNDEF) { ZVAL_NULL(ret); + zval_undefined_cv(var EXECUTE_DATA_CC); + return ret; } return ret; } -static zend_always_inline zval *_get_zval_ptr_cv_deref_BP_VAR_W(uint32_t var EXECUTE_DATA_DC) +static zend_always_inline zval *_get_zval_ptr_cv_BP_VAR_W(uint32_t var EXECUTE_DATA_DC) { zval *ret = EX_VAR(var); if (Z_TYPE_P(ret) == IS_UNDEF) { ZVAL_NULL(ret); - return ret; } - ZVAL_DEREF(ret); return ret; } @@ -621,14 +585,16 @@ 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) +static zend_never_inline ZEND_COLD int zend_wrong_assign_to_variable_reference(zval *variable_ptr, zval *value_ptr 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); + /* Use IS_TMP_VAR instead of IS_VAR to avoid ISREF check */ + Z_TRY_ADDREF_P(value_ptr); + value_ptr = zend_assign_to_variable(variable_ptr, value_ptr, IS_TMP_VAR, EX_USES_STRICT_TYPES()); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), value_ptr); @@ -636,27 +602,63 @@ static zend_never_inline ZEND_COLD int zend_wrong_assign_to_variable_reference(z 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)); +static void zend_format_type(zend_type type, const char **part1, const char **part2) { + *part1 = ZEND_TYPE_ALLOW_NULL(type) ? "?" : ""; + if (ZEND_TYPE_IS_CLASS(type)) { + if (ZEND_TYPE_IS_CE(type)) { + *part2 = ZSTR_VAL(ZEND_TYPE_CE(type)->name); + } else { + *part2 = ZSTR_VAL(ZEND_TYPE_NAME(type)); + } + } else { + *part2 = zend_get_type_by_const(ZEND_TYPE_CODE(type)); } } +static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_prop_error(zend_property_info *prop, const char *type) { + const char *prop_type1, *prop_type2; + zend_format_type(prop->type, &prop_type1, &prop_type2); + zend_type_error( + "Cannot auto-initialize an %s inside property %s::$%s of type %s%s", + type, + ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), + prop_type1, prop_type2 + ); +} + +static zend_never_inline ZEND_COLD void zend_throw_auto_init_in_ref_error(zend_property_info *prop, const char *type) { + const char *prop_type1, *prop_type2; + zend_format_type(prop->type, &prop_type1, &prop_type2); + zend_type_error( + "Cannot auto-initialize an %s inside a reference held by property %s::$%s of type %s%s", + type, + ZSTR_VAL(prop->ce->name), zend_get_unmangled_property_name(prop->name), + prop_type1, prop_type2 + ); +} + +static zend_never_inline ZEND_COLD void zend_throw_access_uninit_prop_by_ref_error( + zend_property_info *prop) { + zend_throw_error(NULL, + "Cannot access uninitialized non-nullable property %s::$%s by reference", + ZSTR_VAL(prop->ce->name), + zend_get_unmangled_property_name(prop->name)); +} + +static zend_always_inline zend_property_info *i_zend_check_ref_stdClass_assignable(zend_reference *ref); + /* this should modify object only if it's empty */ -static zend_never_inline ZEND_COLD int ZEND_FASTCALL make_real_object(zval *object, zval *property OPLINE_DC EXECUTE_DATA_DC) +static zend_never_inline ZEND_COLD zval* ZEND_FASTCALL make_real_object(zval *object, zval *property OPLINE_DC EXECUTE_DATA_DC) { zend_object *obj; + zval *ref = NULL; + if (Z_ISREF_P(object)) { + ref = object; + object = Z_REFVAL_P(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 { + if (UNEXPECTED(Z_TYPE_P(object) > IS_FALSE && + (Z_TYPE_P(object) != IS_STRING || Z_STRLEN_P(object) != 0))) { 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); @@ -666,6 +668,11 @@ static zend_never_inline ZEND_COLD int ZEND_FASTCALL make_real_object(zval *obje || 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 if (opline->opcode == ZEND_FETCH_OBJ_W + || opline->opcode == ZEND_FETCH_OBJ_RW + || opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG + || opline->opcode == ZEND_ASSIGN_OBJ_REF) { + zend_error(E_WARNING, "Attempt to modify 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)); } @@ -674,8 +681,21 @@ static zend_never_inline ZEND_COLD int ZEND_FASTCALL make_real_object(zval *obje if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); } - return 0; + return NULL; + } + + if (ref) { + zend_property_info *error_prop = i_zend_check_ref_stdClass_assignable(Z_REF_P(ref)); + if (error_prop) { + zend_throw_auto_init_in_ref_error(error_prop, "stdClass"); + if (RETURN_VALUE_USED(opline)) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); + } + return NULL; + } } + + zval_ptr_dtor_nogc(object); object_init(object); Z_ADDREF_P(object); obj = Z_OBJ_P(object); @@ -686,29 +706,10 @@ static zend_never_inline ZEND_COLD int ZEND_FASTCALL make_real_object(zval *obje if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); } - return 0; + return NULL; } 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; + return object; } static ZEND_COLD void zend_verify_type_error_common( @@ -867,10 +868,7 @@ static zend_bool zend_verify_weak_scalar_type_hint(zend_uchar type_hint, zval *a zend_string *dest; /* on success "arg" is converted to IS_STRING */ - if (!zend_parse_arg_str_weak(arg, &dest)) { - return 0; - } - return 1; + return zend_parse_arg_str_weak(arg, &dest); } default: return 0; @@ -891,17 +889,141 @@ static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, z return zend_verify_weak_scalar_type_hint(type_hint, arg); } +ZEND_COLD zend_never_inline void zend_verify_property_type_error(zend_property_info *info, zval *property) +{ + const char *prop_type1, *prop_type2; + + /* we _may_ land here in case reading already errored and runtime cache thus has not been updated (i.e. it contains a valid but unrelated info) */ + if (EG(exception)) { + return; + } + + // TODO Switch to a more standard error message? + zend_format_type(info->type, &prop_type1, &prop_type2); + (void) prop_type1; + if (ZEND_TYPE_IS_CLASS(info->type)) { + zend_type_error("Typed property %s::$%s must be an instance of %s%s, %s used", + ZSTR_VAL(info->ce->name), + zend_get_unmangled_property_name(info->name), + prop_type2, + ZEND_TYPE_ALLOW_NULL(info->type) ? " or null" : "", + Z_TYPE_P(property) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(property)->name) : zend_get_type_by_const(Z_TYPE_P(property))); + } else { + zend_type_error("Typed property %s::$%s must be %s%s, %s used", + ZSTR_VAL(info->ce->name), + zend_get_unmangled_property_name(info->name), + prop_type2, + ZEND_TYPE_ALLOW_NULL(info->type) ? " or null" : "", + Z_TYPE_P(property) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(property)->name) : zend_get_type_by_const(Z_TYPE_P(property))); + } +} + +static zend_bool zend_resolve_class_type(zend_type *type, zend_class_entry *self_ce) { + zend_class_entry *ce; + zend_string *name = ZEND_TYPE_NAME(*type); + if (zend_string_equals_literal_ci(name, "self")) { + /* We need to explicitly check for this here, to avoid updating the type in the trait and + * later using the wrong "self" when the trait is used in a class. */ + if (UNEXPECTED((self_ce->ce_flags & ZEND_ACC_TRAIT) != 0)) { + zend_throw_error(NULL, "Cannot write a%s value to a 'self' typed static property of a trait", ZEND_TYPE_ALLOW_NULL(*type) ? " non-null" : ""); + return 0; + } + ce = self_ce; + } else if (zend_string_equals_literal_ci(name, "parent")) { + if (UNEXPECTED(!self_ce->parent)) { + zend_throw_error(NULL, "Cannot access parent:: when current class scope has no parent"); + return 0; + } + ce = self_ce->parent; + } else { + ce = zend_lookup_class(name); + if (UNEXPECTED(!ce)) { + return 0; + } + } + + zend_string_release(name); + *type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(*type)); + return 1; +} + + +static zend_always_inline zend_bool i_zend_check_property_type(zend_property_info *info, zval *property, zend_bool strict) +{ + ZEND_ASSERT(!Z_ISREF_P(property)); + if (ZEND_TYPE_IS_CLASS(info->type)) { + if (UNEXPECTED(Z_TYPE_P(property) != IS_OBJECT)) { + return Z_TYPE_P(property) == IS_NULL && ZEND_TYPE_ALLOW_NULL(info->type); + } + + if (UNEXPECTED(!ZEND_TYPE_IS_CE(info->type)) && UNEXPECTED(!zend_resolve_class_type(&info->type, info->ce))) { + return 0; + } + + return instanceof_function(Z_OBJCE_P(property), ZEND_TYPE_CE(info->type)); + } + + ZEND_ASSERT(ZEND_TYPE_CODE(info->type) != IS_CALLABLE); + if (EXPECTED(ZEND_TYPE_CODE(info->type) == Z_TYPE_P(property))) { + return 1; + } else if (EXPECTED(Z_TYPE_P(property) == IS_NULL)) { + return ZEND_TYPE_ALLOW_NULL(info->type); + } else if (ZEND_TYPE_CODE(info->type) == _IS_BOOL && EXPECTED(Z_TYPE_P(property) == IS_FALSE || Z_TYPE_P(property) == IS_TRUE)) { + return 1; + } else if (ZEND_TYPE_CODE(info->type) == IS_ITERABLE) { + return zend_is_iterable(property); + } else { + return zend_verify_scalar_type_hint(ZEND_TYPE_CODE(info->type), property, strict); + } +} + +static zend_bool zend_always_inline i_zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict) +{ + if (i_zend_check_property_type(info, property, strict)) { + return 1; + } + + zend_verify_property_type_error(info, property); + return 0; +} + +zend_bool zend_never_inline zend_verify_property_type(zend_property_info *info, zval *property, zend_bool strict) { + return i_zend_verify_property_type(info, property, strict); +} + +static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *info, zval *property_val, zval *value EXECUTE_DATA_DC) +{ + zval tmp; + + ZVAL_DEREF(value); + ZVAL_COPY(&tmp, value); + + if (UNEXPECTED(!i_zend_verify_property_type(info, &tmp, EX_USES_STRICT_TYPES()))) { + zval_ptr_dtor(&tmp); + return &EG(uninitialized_zval); + } + + return zend_assign_to_variable(property_val, &tmp, IS_TMP_VAR, EX_USES_STRICT_TYPES()); +} + + static zend_always_inline zend_bool zend_check_type( zend_type type, zval *arg, zend_class_entry **ce, void **cache_slot, zval *default_value, zend_class_entry *scope, zend_bool is_return_type) { + zend_reference *ref = NULL; + if (!ZEND_TYPE_IS_SET(type)) { return 1; } - ZVAL_DEREF(arg); + if (UNEXPECTED(Z_ISREF_P(arg))) { + ref = Z_REF_P(arg); + arg = Z_REFVAL_P(arg); + } + if (ZEND_TYPE_IS_CLASS(type)) { if (EXPECTED(*cache_slot)) { *ce = (zend_class_entry *) *cache_slot; @@ -932,6 +1054,8 @@ static zend_always_inline zend_bool zend_check_type( } else if (ZEND_TYPE_CODE(type) == _IS_BOOL && EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { return 1; + } else if (ref && ZEND_REF_HAS_TYPE_SOURCES(ref)) { + return 0; /* we cannot have conversions for typed refs */ } else { return zend_verify_scalar_type_hint(ZEND_TYPE_CODE(type), arg, is_return_type ? ZEND_RET_USES_STRICT_TYPES() : ZEND_ARG_USES_STRICT_TYPES()); @@ -963,6 +1087,41 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a return 1; } +static zend_always_inline int zend_verify_recv_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot) +{ + zend_arg_info *cur_arg_info = &zf->common.arg_info[arg_num-1]; + zend_class_entry *ce; + + ZEND_ASSERT(arg_num <= zf->common.num_args); + cur_arg_info = &zf->common.arg_info[arg_num-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; + } + + return 1; +} + +static zend_always_inline int zend_verify_variadic_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot) +{ + zend_arg_info *cur_arg_info; + zend_class_entry *ce; + + ZEND_ASSERT(arg_num > zf->common.num_args); + ZEND_ASSERT(zf->common.fn_flags & ZEND_ACC_VARIADIC); + cur_arg_info = &zf->common.arg_info[zf->common.num_args]; + + 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; + } + + return 1; +} + static zend_never_inline int zend_verify_internal_arg_types(zend_function *fbc, zend_execute_data *call) { uint32_t i; @@ -1123,14 +1282,6 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_illegal_offset(void) 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_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))) { @@ -1138,13 +1289,12 @@ static zend_never_inline void zend_assign_to_object_dim(zval *object, zval *dim, } } -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) +static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval *property, zval *value, binary_op_type binary_op OPLINE_DC EXECUTE_DATA_DC) { zval *z; zval rv, res; - if (Z_OBJ_HT_P(object)->read_dimension && - (z = Z_OBJ_HT_P(object)->read_dimension(object, property, BP_VAR_R, &rv)) != NULL) { + if ((z = Z_OBJ_HT_P(object)->read_dimension(object, property, BP_VAR_R, &rv)) != NULL) { if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { zval rv2; @@ -1155,23 +1305,50 @@ static zend_never_inline void zend_binary_assign_op_obj_dim(zval *object, zval * } ZVAL_COPY_VALUE(z, value); } - binary_op(&res, Z_ISREF_P(z) ? Z_REFVAL_P(z) : z, value); - Z_OBJ_HT_P(object)->write_dimension(object, property, &res); + if (binary_op(&res, z, value) == SUCCESS) { + Z_OBJ_HT_P(object)->write_dimension(object, property, &res); + } if (z == &rv) { zval_ptr_dtor(&rv); } - if (retval) { - ZVAL_COPY(retval, &res); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_COPY(EX_VAR(opline->result.var), &res); } zval_ptr_dtor(&res); } else { zend_use_object_as_array(); - if (retval) { - ZVAL_NULL(retval); + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_NULL(EX_VAR(opline->result.var)); } } } +static zend_never_inline void zend_binary_assign_op_typed_ref(zend_reference *ref, zval *value, binary_op_type binary_op EXECUTE_DATA_DC) +{ + zval z_copy; + + binary_op(&z_copy, &ref->val, value); + if (EXPECTED(zend_verify_ref_assignable_zval(ref, &z_copy, EX_USES_STRICT_TYPES()))) { + zval_ptr_dtor(&ref->val); + ZVAL_COPY_VALUE(&ref->val, &z_copy); + } else { + zval_ptr_dtor(&z_copy); + } +} + +static zend_never_inline void zend_binary_assign_op_typed_prop(zend_property_info *prop_info, zval *zptr, zval *value, binary_op_type binary_op EXECUTE_DATA_DC) +{ + zval z_copy; + + binary_op(&z_copy, zptr, value); + if (EXPECTED(zend_verify_property_type(prop_info, &z_copy, EX_USES_STRICT_TYPES()))) { + zval_ptr_dtor(zptr); + ZVAL_COPY_VALUE(zptr, &z_copy); + } else { + zval_ptr_dtor(&z_copy); + } +} + static zend_never_inline zend_long zend_check_string_offset(zval *dim, int type EXECUTE_DATA_DC) { zend_long offset; @@ -1188,7 +1365,7 @@ try_again: } break; case IS_UNDEF: - zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); + ZVAL_UNDEFINED_OP2(); case IS_DOUBLE: case IS_NULL: case IS_FALSE: @@ -1345,22 +1522,6 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_wrong_property_read(z 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", @@ -1433,95 +1594,257 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim, } } -static zend_never_inline void zend_post_incdec_overloaded_property(zval *object, zval *property, void **cache_slot, int inc OPLINE_DC EXECUTE_DATA_DC) +static zend_property_info *zend_get_prop_not_accepting_double(zend_reference *ref) { - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval rv, obj; - zval *z; - zval z_copy; - - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - 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(EX_VAR(opline->result.var)); - return; + zend_property_info *prop; + ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) { + if (ZEND_TYPE_CODE(prop->type) != IS_DOUBLE) { + return prop; } + } ZEND_REF_FOREACH_TYPE_SOURCES_END(); + return NULL; +} - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); - } +static ZEND_COLD void zend_throw_incdec_ref_error(zend_reference *ref, zend_bool inc) +{ + zend_property_info *error_prop = zend_get_prop_not_accepting_double(ref); + /* Currently there should be no way for a typed reference to accept both int and double. + * Generalize this and the related property code once this becomes possible. */ + ZEND_ASSERT(error_prop); + zend_type_error( + "Cannot %s a reference held by property %s::$%s of type %sint past its %simal value", + inc ? "increment" : "decrement", + ZSTR_VAL(error_prop->ce->name), + zend_get_unmangled_property_name(error_prop->name), + ZEND_TYPE_ALLOW_NULL(error_prop->type) ? "?" : "", + inc ? "max" : "min"); +} - ZVAL_COPY_DEREF(&z_copy, z); - ZVAL_COPY(EX_VAR(opline->result.var), &z_copy); - if (inc) { - increment_function(&z_copy); - } else { - decrement_function(&z_copy); - } - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot); - OBJ_RELEASE(Z_OBJ(obj)); - zval_ptr_dtor(&z_copy); - zval_ptr_dtor(z); +static ZEND_COLD void zend_throw_incdec_prop_error(zend_property_info *prop, zend_bool inc) { + const char *prop_type1, *prop_type2; + zend_format_type(prop->type, &prop_type1, &prop_type2); + zend_type_error("Cannot %s property %s::$%s of type %s%s past its %simal value", + inc ? "increment" : "decrement", + ZSTR_VAL(prop->ce->name), + zend_get_unmangled_property_name(prop->name), + prop_type1, prop_type2, + inc ? "max" : "min"); +} + +static void zend_incdec_typed_ref(zend_reference *ref, zval *copy, int inc EXECUTE_DATA_DC) +{ + zval tmp; + zval *var_ptr = &ref->val; + + if (!copy) { + copy = &tmp; + } + + ZVAL_COPY(copy, var_ptr); + + if (inc) { + increment_function(var_ptr); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); - ZVAL_NULL(EX_VAR(opline->result.var)); + decrement_function(var_ptr); + } + + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(copy) == IS_LONG) { + zend_throw_incdec_ref_error(ref, inc); + ZVAL_COPY_VALUE(var_ptr, copy); + } else if (UNEXPECTED(!zend_verify_ref_assignable_zval(ref, var_ptr, EX_USES_STRICT_TYPES()))) { + zval_ptr_dtor(var_ptr); + ZVAL_COPY_VALUE(var_ptr, copy); + ZVAL_UNDEF(copy); + } else if (copy == &tmp) { + zval_ptr_dtor(&tmp); } } -static zend_never_inline void zend_pre_incdec_overloaded_property(zval *object, zval *property, void **cache_slot, int inc OPLINE_DC EXECUTE_DATA_DC) +static void zend_incdec_typed_prop(zend_property_info *prop_info, zval *var_ptr, zval *copy, int inc EXECUTE_DATA_DC) { - zval rv; + zval tmp; - if (Z_OBJ_HT_P(object)->read_property && Z_OBJ_HT_P(object)->write_property) { - zval *z, obj; - zval z_copy; + if (!copy) { + copy = &tmp; + } - ZVAL_OBJ(&obj, Z_OBJ_P(object)); - Z_ADDREF(obj); - 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 (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } - return; - } + ZVAL_COPY(copy, var_ptr); - if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); + if (inc) { + increment_function(var_ptr); + } else { + decrement_function(var_ptr); + } - if (z == &rv) { - zval_ptr_dtor(&rv); + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_DOUBLE) && Z_TYPE_P(copy) == IS_LONG) { + zend_throw_incdec_prop_error(prop_info, inc); + ZVAL_COPY_VALUE(var_ptr, copy); + } else if (UNEXPECTED(!zend_verify_property_type(prop_info, var_ptr, EX_USES_STRICT_TYPES()))) { + zval_ptr_dtor(var_ptr); + ZVAL_COPY_VALUE(var_ptr, copy); + ZVAL_UNDEF(copy); + } else if (copy == &tmp) { + zval_ptr_dtor(&tmp); + } +} + +static void zend_pre_incdec_property_zval(zval *prop, zend_property_info *prop_info, int inc OPLINE_DC EXECUTE_DATA_DC) +{ + if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) { + if (inc) { + fast_long_increment_function(prop); + } else { + fast_long_decrement_function(prop); + } + if (UNEXPECTED(Z_TYPE_P(prop) != IS_LONG) && UNEXPECTED(prop_info)) { + zend_throw_incdec_prop_error(prop_info, inc); + if (inc) { + ZVAL_LONG(prop, ZEND_LONG_MAX); + } else { + ZVAL_LONG(prop, ZEND_LONG_MIN); } - ZVAL_COPY_VALUE(z, value); } - ZVAL_COPY_DEREF(&z_copy, z); + } else { + do { + if (Z_ISREF_P(prop)) { + zend_reference *ref = Z_REF_P(prop); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_incdec_typed_ref(ref, NULL, inc EXECUTE_DATA_CC); + break; + } + prop = Z_REFVAL_P(prop); + } + + if (UNEXPECTED(prop_info)) { + zend_incdec_typed_prop(prop_info, prop, NULL, inc EXECUTE_DATA_CC); + } else if (inc) { + increment_function(prop); + } else { + decrement_function(prop); + } + } while (0); + } + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_COPY(EX_VAR(opline->result.var), prop); + } +} + +static void zend_post_incdec_property_zval(zval *prop, zend_property_info *prop_info, int inc OPLINE_DC EXECUTE_DATA_DC) +{ + if (EXPECTED(Z_TYPE_P(prop) == IS_LONG)) { + ZVAL_LONG(EX_VAR(opline->result.var), Z_LVAL_P(prop)); if (inc) { - increment_function(&z_copy); + fast_long_increment_function(prop); } else { - decrement_function(&z_copy); + fast_long_decrement_function(prop); } - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_COPY(EX_VAR(opline->result.var), &z_copy); + if (UNEXPECTED(Z_TYPE_P(prop) != IS_LONG) && UNEXPECTED(prop_info)) { + zend_throw_incdec_prop_error(prop_info, inc); + if (inc) { + ZVAL_LONG(prop, ZEND_LONG_MAX); + } else { + ZVAL_LONG(prop, ZEND_LONG_MIN); + } + } + } else { + if (Z_ISREF_P(prop)) { + zend_reference *ref = Z_REF_P(prop); + if (ZEND_REF_HAS_TYPE_SOURCES(ref)) { + zend_incdec_typed_ref(ref, EX_VAR(opline->result.var), inc EXECUTE_DATA_CC); + return; + } + prop = Z_REFVAL_P(prop); + } + + if (UNEXPECTED(prop_info)) { + zend_incdec_typed_prop(prop_info, prop, EX_VAR(opline->result.var), inc EXECUTE_DATA_CC); + } else { + ZVAL_COPY(EX_VAR(opline->result.var), prop); + if (inc) { + increment_function(prop); + } else { + decrement_function(prop); + } } - Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot); + } +} + +static zend_never_inline void zend_post_incdec_overloaded_property(zval *object, zval *property, void **cache_slot, int inc OPLINE_DC EXECUTE_DATA_DC) +{ + zval rv, obj; + zval *z; + zval z_copy; + + ZVAL_OBJ(&obj, Z_OBJ_P(object)); + Z_ADDREF(obj); + 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_ptr_dtor(&z_copy); - zval_ptr_dtor(z); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + return; + } + + if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { + zval rv2; + zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); + if (z == &rv) { + zval_ptr_dtor(&rv); + } + ZVAL_COPY_VALUE(z, value); + } + + ZVAL_COPY_DEREF(&z_copy, z); + ZVAL_COPY(EX_VAR(opline->result.var), &z_copy); + if (inc) { + increment_function(&z_copy); } else { - zend_error(E_WARNING, "Attempt to increment/decrement property of non-object"); + decrement_function(&z_copy); + } + Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot); + OBJ_RELEASE(Z_OBJ(obj)); + zval_ptr_dtor(&z_copy); + zval_ptr_dtor(z); +} + +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; + zval *z, obj; + zval z_copy; + + ZVAL_OBJ(&obj, Z_OBJ_P(object)); + Z_ADDREF(obj); + 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 (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_NULL(EX_VAR(opline->result.var)); } + return; } + + if (UNEXPECTED(Z_TYPE_P(z) == IS_OBJECT) && Z_OBJ_HT_P(z)->get) { + zval rv2; + zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); + + if (z == &rv) { + zval_ptr_dtor(&rv); + } + ZVAL_COPY_VALUE(z, value); + } + ZVAL_COPY_DEREF(&z_copy, z); + if (inc) { + increment_function(&z_copy); + } else { + decrement_function(&z_copy); + } + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_COPY(EX_VAR(opline->result.var), &z_copy); + } + Z_OBJ_HT(obj)->write_property(&obj, property, &z_copy, cache_slot); + OBJ_RELEASE(Z_OBJ(obj)); + zval_ptr_dtor(&z_copy); + zval_ptr_dtor(z); } 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) @@ -1531,37 +1854,31 @@ static zend_never_inline void zend_assign_op_overloaded_property(zval *object, z ZVAL_OBJ(&obj, Z_OBJ_P(object)); Z_ADDREF(obj); - if (EXPECTED(Z_OBJ_HT(obj)->read_property)) { - 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 (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_UNDEF(EX_VAR(opline->result.var)); - } - return; + 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 (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_UNDEF(EX_VAR(opline->result.var)); } - if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { - zval rv2; - zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); + return; + } + if (Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get) { + zval rv2; + zval *value = Z_OBJ_HT_P(z)->get(z, &rv2); - if (z == &rv) { - zval_ptr_dtor(&rv); - } - ZVAL_COPY_VALUE(z, value); + if (z == &rv) { + zval_ptr_dtor(&rv); } - binary_op(&res, z, value); + ZVAL_COPY_VALUE(z, value); + } + if (binary_op(&res, z, value) == SUCCESS) { 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(z); - zval_ptr_dtor(&res); - } else { - zend_error(E_WARNING, "Attempt to assign property of non-object"); - if (UNEXPECTED(RETURN_VALUE_USED(opline))) { - ZVAL_NULL(EX_VAR(opline->result.var)); - } } + if (UNEXPECTED(RETURN_VALUE_USED(opline))) { + ZVAL_COPY(EX_VAR(opline->result.var), &res); + } + zval_ptr_dtor(z); + zval_ptr_dtor(&res); OBJ_RELEASE(Z_OBJ(obj)); } @@ -1669,16 +1986,6 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_use_new_element_for_s 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 = NULL; @@ -1760,7 +2067,7 @@ str_index: } else { switch (Z_TYPE_P(dim)) { case IS_UNDEF: - zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); + ZVAL_UNDEFINED_OP2(); /* break missing intentionally */ case IS_NULL: offset_key = ZSTR_EMPTY_ALLOC(); @@ -1812,7 +2119,7 @@ static zend_never_inline zval* ZEND_FASTCALL zend_fetch_dimension_address_inner_ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval *container, zval *dim, int dim_type, int type EXECUTE_DATA_DC) { - zval *retval; + zval *retval; if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { try_array: @@ -1835,9 +2142,25 @@ fetch_from_array: ZVAL_INDIRECT(result, retval); return; } else if (EXPECTED(Z_TYPE_P(container) == IS_REFERENCE)) { + zend_reference *ref = Z_REF_P(container); container = Z_REFVAL_P(container); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { goto try_array; + } else if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + if (type != BP_VAR_UNSET) { + if (ZEND_REF_HAS_TYPE_SOURCES(ref)) { + zend_property_info *error_prop = zend_check_ref_array_assignable(ref); + if (UNEXPECTED(error_prop != NULL)) { + zend_throw_auto_init_in_ref_error(error_prop, "array"); + ZVAL_ERROR(result); + return; + } + } + array_init(container); + goto fetch_from_array; + } else { + goto return_null; + } } } if (UNEXPECTED(Z_TYPE_P(container) == IS_STRING)) { @@ -1850,56 +2173,51 @@ fetch_from_array: ZVAL_ERROR(result); } else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { if (/*dim_type == IS_CV &&*/ dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { - zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); - dim = &EG(uninitialized_zval); + dim = ZVAL_UNDEFINED_OP2(); } - if (!Z_OBJ_HT_P(container)->read_dimension) { - 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 (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))) { - zend_class_entry *ce = Z_OBJCE_P(container); + if (UNEXPECTED(retval == &EG(uninitialized_zval))) { + zend_class_entry *ce = Z_OBJCE_P(container); - ZVAL_NULL(result); - zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); - } else if (EXPECTED(retval && Z_TYPE_P(retval) != IS_UNDEF)) { - if (!Z_ISREF_P(retval)) { - if (result != retval) { - ZVAL_COPY(result, retval); - retval = result; - } - if (Z_TYPE_P(retval) != IS_OBJECT) { - zend_class_entry *ce = Z_OBJCE_P(container); - zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); - } - } else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) { - ZVAL_UNREF(retval); - } + ZVAL_NULL(result); + zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); + } else if (EXPECTED(retval && Z_TYPE_P(retval) != IS_UNDEF)) { + if (!Z_ISREF_P(retval)) { if (result != retval) { - ZVAL_INDIRECT(result, retval); + ZVAL_COPY(result, retval); + retval = result; } - } else { - ZVAL_ERROR(result); + if (Z_TYPE_P(retval) != IS_OBJECT) { + zend_class_entry *ce = Z_OBJCE_P(container); + zend_error(E_NOTICE, "Indirect modification of overloaded element of %s has no effect", ZSTR_VAL(ce->name)); + } + } else if (UNEXPECTED(Z_REFCOUNT_P(retval) == 1)) { + ZVAL_UNREF(retval); } + if (result != retval) { + ZVAL_INDIRECT(result, retval); + } + } else { + ZVAL_ERROR(result); } } else { - if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { - zval_undefined_cv(EX(opline)->op1.var EXECUTE_DATA_CC); - } - if (/*dim_type == IS_CV &&*/ dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { - zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); - } if (EXPECTED(Z_TYPE_P(container) <= IS_FALSE)) { + if (type != BP_VAR_W && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { + ZVAL_UNDEFINED_OP1(); + } if (type != BP_VAR_UNSET) { array_init(container); goto fetch_from_array; } else { +return_null: /* for read-mode only */ + if (/*dim_type == IS_CV &&*/ dim && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { + ZVAL_UNDEFINED_OP2(); + } ZVAL_NULL(result); } } else if (EXPECTED(Z_ISERROR_P(container))) { @@ -1969,7 +2287,7 @@ try_string_offset: zend_error(E_WARNING, "Illegal string offset '%s'", Z_STRVAL_P(dim)); break; case IS_UNDEF: - zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); + ZVAL_UNDEFINED_OP2(); case IS_DOUBLE: case IS_NULL: case IS_FALSE: @@ -2010,35 +2328,29 @@ try_string_offset: } } else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { if (/*dim_type == IS_CV &&*/ UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { - zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); - dim = &EG(uninitialized_zval); + dim = ZVAL_UNDEFINED_OP2(); } - if (!Z_OBJ_HT_P(container)->read_dimension) { - 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); + 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_DEREF(result, retval); - } else if (UNEXPECTED(Z_ISREF_P(retval))) { - zend_unwrap_reference(result); - } - } else { - ZVAL_NULL(result); + ZEND_ASSERT(result != NULL); + if (retval) { + if (result != retval) { + ZVAL_COPY_DEREF(result, retval); + } else if (UNEXPECTED(Z_ISREF_P(retval))) { + zend_unwrap_reference(result); } + } else { + ZVAL_NULL(result); } } else { if (type != BP_VAR_IS && UNEXPECTED(Z_TYPE_P(container) == IS_UNDEF)) { - zval_undefined_cv(EX(opline)->op1.var EXECUTE_DATA_CC); + ZVAL_UNDEFINED_OP1(); } if (/*dim_type == IS_CV &&*/ UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) { - zval_undefined_cv(EX(opline)->op2.var EXECUTE_DATA_CC); + ZVAL_UNDEFINED_OP2(); } ZVAL_NULL(result); } @@ -2094,7 +2406,7 @@ str_idx: 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); + ZVAL_UNDEFINED_OP2(); goto str_idx; } else { zend_error(E_WARNING, "Illegal offset type in isset or empty"); @@ -2105,17 +2417,11 @@ str_idx: 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); + offset = ZVAL_UNDEFINED_OP2(); } 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; - } + return Z_OBJ_HT_P(container)->has_dimension(container, offset, 0); } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ zend_long lval; @@ -2150,17 +2456,11 @@ str_offset: 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); + offset = ZVAL_UNDEFINED_OP2(); } 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; - } + return !Z_OBJ_HT_P(container)->has_dimension(container, offset, 1); } else if (EXPECTED(Z_TYPE_P(container) == IS_STRING)) { /* string offsets */ zend_long lval; @@ -2192,20 +2492,219 @@ str_offset: } } -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) +static zend_never_inline uint32_t ZEND_FASTCALL zend_array_key_exists_fast(HashTable *ht, zval *key OPLINE_DC EXECUTE_DATA_DC) +{ + zend_string *str; + zend_ulong hval; + +try_again: + if (EXPECTED(Z_TYPE_P(key) == IS_STRING)) { + str = Z_STR_P(key); + if (ZEND_HANDLE_NUMERIC(str, hval)) { + goto num_key; + } +str_key: + return zend_hash_find_ind(ht, str) != NULL ? IS_TRUE : IS_FALSE; + } else if (EXPECTED(Z_TYPE_P(key) == IS_LONG)) { + hval = Z_LVAL_P(key); +num_key: + return zend_hash_index_find(ht, hval) != NULL ? IS_TRUE : IS_FALSE; + } else if (EXPECTED(Z_ISREF_P(key))) { + key = Z_REFVAL_P(key); + goto try_again; + } else if (Z_TYPE_P(key) <= IS_NULL) { + if (UNEXPECTED(Z_TYPE_P(key) == IS_UNDEF)) { + ZVAL_UNDEFINED_OP1(); + } + str = ZSTR_EMPTY_ALLOC(); + goto str_key; + } else { + zend_error(E_WARNING, "array_key_exists(): The first argument should be either a string or an integer"); + return IS_FALSE; + } +} + +static zend_never_inline uint32_t ZEND_FASTCALL zend_array_key_exists_slow(zval *subject, zval *key OPLINE_DC EXECUTE_DATA_DC) +{ + if (EXPECTED(Z_TYPE_P(subject) == IS_OBJECT)) { + HashTable *ht = zend_get_properties_for(subject, ZEND_PROP_PURPOSE_ARRAY_CAST); + uint32_t result = zend_array_key_exists_fast(ht, key OPLINE_CC EXECUTE_DATA_CC); + zend_release_properties(ht); + return result; + } else { + if (UNEXPECTED(Z_TYPE_P(key) == IS_UNDEF)) { + ZVAL_UNDEFINED_OP1(); + } + if (UNEXPECTED(Z_TYPE_INFO_P(subject) == IS_UNDEF)) { + ZVAL_UNDEFINED_OP2(); + } + zend_internal_type_error(EX_USES_STRICT_TYPES(), "array_key_exists() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(subject))); + return IS_NULL; + } +} + +static zend_always_inline zend_bool promotes_to_array(zval *val) { + return Z_TYPE_P(val) <= IS_FALSE + || (Z_ISREF_P(val) && Z_TYPE_P(Z_REFVAL_P(val)) <= IS_FALSE); +} + +static zend_always_inline zend_bool promotes_to_object(zval *val) { + ZVAL_DEREF(val); + return Z_TYPE_P(val) <= IS_FALSE + || (Z_TYPE_P(val) == IS_STRING && Z_STRLEN_P(val) == 0); +} + +static zend_always_inline zend_bool check_type_array_assignable(zend_type type) { + if (!type) { + return 1; + } + return ZEND_TYPE_IS_CODE(type) + && (ZEND_TYPE_CODE(type) == IS_ARRAY || ZEND_TYPE_CODE(type) == IS_ITERABLE); +} + +static zend_always_inline zend_bool check_type_stdClass_assignable(zend_type type) { + if (!type) { + return 1; + } + if (ZEND_TYPE_IS_CLASS(type)) { + if (ZEND_TYPE_IS_CE(type)) { + return ZEND_TYPE_CE(type) == zend_standard_class_def; + } else { + return zend_string_equals_literal_ci(ZEND_TYPE_NAME(type), "stdclass"); + } + } else { + return ZEND_TYPE_CODE(type) == IS_OBJECT; + } +} + +/* Checks whether an array can be assigned to the reference. Returns conflicting property if + * assignment is not possible, NULL otherwise. */ +static zend_always_inline zend_property_info *i_zend_check_ref_array_assignable(zend_reference *ref) { + zend_property_info *prop; + if (!ZEND_REF_HAS_TYPE_SOURCES(ref)) { + return NULL; + } + ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) { + if (!check_type_array_assignable(prop->type)) { + return prop; + } + } ZEND_REF_FOREACH_TYPE_SOURCES_END(); + return NULL; +} + +/* Checks whether an stdClass can be assigned to the reference. Returns conflicting property if + * assignment is not possible, NULL otherwise. */ +static zend_always_inline zend_property_info *i_zend_check_ref_stdClass_assignable(zend_reference *ref) { + zend_property_info *prop; + if (!ZEND_REF_HAS_TYPE_SOURCES(ref)) { + return NULL; + } + ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) { + if (!check_type_stdClass_assignable(prop->type)) { + return prop; + } + } ZEND_REF_FOREACH_TYPE_SOURCES_END(); + return NULL; +} + +ZEND_API zend_property_info* ZEND_FASTCALL zend_check_ref_array_assignable(zend_reference *ref) { + return i_zend_check_ref_array_assignable(ref); +} + +static zend_property_info *zend_object_fetch_property_type_info( + zend_object *obj, zval *slot) +{ + if (EXPECTED(!ZEND_CLASS_HAS_TYPE_HINTS(obj->ce))) { + return NULL; + } + + /* Not a declared property */ + if (UNEXPECTED(slot < obj->properties_table || + slot >= obj->properties_table + obj->ce->default_properties_count)) { + return NULL; + } + + return zend_get_typed_property_info_for_slot(obj, slot); +} + +static zend_never_inline zend_bool zend_handle_fetch_obj_flags( + zval *result, zval *ptr, zend_object *obj, zend_property_info *prop_info, uint32_t flags) { - if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { + switch (flags) { + case ZEND_FETCH_DIM_WRITE: + if (promotes_to_array(ptr)) { + if (!prop_info) { + prop_info = zend_object_fetch_property_type_info(obj, ptr); + if (!prop_info) { + break; + } + } + if (!check_type_array_assignable(prop_info->type)) { + zend_throw_auto_init_in_prop_error(prop_info, "array"); + if (result) ZVAL_ERROR(result); + return 0; + } + } + break; + case ZEND_FETCH_OBJ_WRITE: + if (promotes_to_object(ptr)) { + if (!prop_info) { + prop_info = zend_object_fetch_property_type_info(obj, ptr); + if (!prop_info) { + break; + } + } + if (!check_type_stdClass_assignable(prop_info->type)) { + zend_throw_auto_init_in_prop_error(prop_info, "stdClass"); + if (result) ZVAL_ERROR(result); + return 0; + } + } + break; + case ZEND_FETCH_REF: + if (Z_TYPE_P(ptr) != IS_REFERENCE) { + if (!prop_info) { + prop_info = zend_object_fetch_property_type_info(obj, ptr); + if (!prop_info) { + break; + } + } + if (Z_TYPE_P(ptr) == IS_UNDEF) { + if (!ZEND_TYPE_ALLOW_NULL(prop_info->type)) { + zend_throw_access_uninit_prop_by_ref_error(prop_info); + if (result) ZVAL_ERROR(result); + return 0; + } + ZVAL_NULL(ptr); + } + + ZVAL_NEW_REF(ptr, ptr); + ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(ptr), prop_info); + } + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + 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, uint32_t flags, zend_bool init_undef OPLINE_DC EXECUTE_DATA_DC) +{ + zval *ptr; + + if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { do { - if (Z_ISREF_P(container)) { + if (Z_ISREF_P(container) && Z_TYPE_P(Z_REFVAL_P(container)) == IS_OBJECT) { container = Z_REFVAL_P(container); - if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { - break; - } + break; } /* this should modify object only if it's empty */ - if (type == BP_VAR_UNSET || - UNEXPECTED(!make_real_object_rw(container, prop_ptr OPLINE_CC))) { + if (type == BP_VAR_UNSET) { + return; + } + + container = make_real_object(container, prop_ptr OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(!container)) { ZVAL_ERROR(result); return; } @@ -2215,12 +2714,17 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c EXPECTED(Z_OBJCE_P(container) == CACHED_PTR_EX(cache_slot))) { uintptr_t prop_offset = (uintptr_t)CACHED_PTR_EX(cache_slot + 1); zend_object *zobj = Z_OBJ_P(container); - zval *retval; 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); + ptr = OBJ_PROP(zobj, prop_offset); + if (EXPECTED(Z_TYPE_P(ptr) != IS_UNDEF)) { + ZVAL_INDIRECT(result, ptr); + if (flags) { + zend_property_info *prop_info = CACHED_PTR_EX(cache_slot + 2); + if (prop_info) { + zend_handle_fetch_obj_flags(result, ptr, NULL, prop_info, flags); + } + } return; } } else if (EXPECTED(zobj->properties != NULL)) { @@ -2230,36 +2734,387 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c } zobj->properties = zend_array_dup(zobj->properties); } - retval = zend_hash_find_ex(zobj->properties, Z_STR_P(prop_ptr), 1); - if (EXPECTED(retval)) { - ZVAL_INDIRECT(result, retval); + ptr = zend_hash_find_ex(zobj->properties, Z_STR_P(prop_ptr), 1); + if (EXPECTED(ptr)) { + ZVAL_INDIRECT(result, ptr); return; } } } - if (EXPECTED(Z_OBJ_HT_P(container)->get_property_ptr_ptr)) { - zval *ptr = Z_OBJ_HT_P(container)->get_property_ptr_ptr(container, prop_ptr, type, cache_slot); - if (NULL == ptr) { - if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) { -use_read_property: - ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result); - if (ptr != result) { - ZVAL_INDIRECT(result, ptr); - } else if (UNEXPECTED(Z_ISREF_P(ptr) && Z_REFCOUNT_P(ptr) == 1)) { - ZVAL_UNREF(ptr); + ptr = Z_OBJ_HT_P(container)->get_property_ptr_ptr(container, prop_ptr, type, cache_slot); + if (NULL == ptr) { + ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result); + if (ptr == result) { + if (UNEXPECTED(Z_ISREF_P(ptr) && Z_REFCOUNT_P(ptr) == 1)) { + ZVAL_UNREF(ptr); + } + return; + } + } + + ZVAL_INDIRECT(result, ptr); + if (flags) { + zend_property_info *prop_info; + + if (prop_op_type == IS_CONST) { + prop_info = CACHED_PTR_EX(cache_slot + 2); + if (prop_info) { + if (UNEXPECTED(!zend_handle_fetch_obj_flags(result, ptr, NULL, prop_info, flags))) { + return; } - } else { - zend_access_undefined_propery_in_overloaded_object(); - ZVAL_ERROR(result); } } else { - ZVAL_INDIRECT(result, ptr); + if (UNEXPECTED(!zend_handle_fetch_obj_flags(result, ptr, Z_OBJ_P(container), NULL, flags))) { + return; + } + } + } + if (init_undef && UNEXPECTED(Z_TYPE_P(ptr) == IS_UNDEF)) { + ZVAL_NULL(ptr); + } +} + +static zend_never_inline int zend_fetch_static_property_address_ex(zval **retval, zend_property_info **prop_info, uint32_t cache_slot, int fetch_type OPLINE_DC EXECUTE_DATA_DC) { + zend_free_op free_op1; + zend_string *name, *tmp_name; + zend_class_entry *ce; + zend_property_info *property_info; + + zend_uchar op1_type = opline->op1_type, op2_type = opline->op2_type; + + if (EXPECTED(op2_type == IS_CONST)) { + zval *class_name = RT_CONSTANT(opline, opline->op2); + + ZEND_ASSERT(op1_type != IS_CONST || CACHED_PTR(cache_slot) == NULL); + + if (EXPECTED((ce = CACHED_PTR(cache_slot)) == NULL)) { + ce = zend_fetch_class_by_name(Z_STR_P(class_name), Z_STR_P(class_name + 1), ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(ce == NULL)) { + FREE_UNFETCHED_OP(op1_type, opline->op1.var); + return FAILURE; + } + if (UNEXPECTED(op1_type != IS_CONST)) { + CACHE_PTR(cache_slot, ce); + } } - } else if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) { - goto use_read_property; } else { - zend_unsupported_property_reference(); - ZVAL_ERROR(result); + if (EXPECTED(op2_type == IS_UNUSED)) { + ce = zend_fetch_class(NULL, opline->op2.num); + if (UNEXPECTED(ce == NULL)) { + FREE_UNFETCHED_OP(op1_type, opline->op1.var); + return FAILURE; + } + } else { + ce = Z_CE_P(EX_VAR(opline->op2.var)); + } + if (EXPECTED(op1_type == IS_CONST) && EXPECTED(CACHED_PTR(cache_slot) == ce)) { + *retval = CACHED_PTR(cache_slot + sizeof(void *)); + *prop_info = CACHED_PTR(cache_slot + sizeof(void *) * 2); + return SUCCESS; + } + } + + if (EXPECTED(op1_type == IS_CONST)) { + name = Z_STR_P(RT_CONSTANT(opline, opline->op1)); + } else { + zval *varname = get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, BP_VAR_R); + if (EXPECTED(Z_TYPE_P(varname) == IS_STRING)) { + name = Z_STR_P(varname); + tmp_name = NULL; + } else { + if (op1_type == IS_CV && UNEXPECTED(Z_TYPE_P(varname) == IS_UNDEF)) { + zval_undefined_cv(opline->op1.var EXECUTE_DATA_CC); + } + name = zval_get_tmp_string(varname, &tmp_name); + } + } + + *retval = zend_std_get_static_property_with_info(ce, name, fetch_type, &property_info); + + if (UNEXPECTED(op1_type != IS_CONST)) { + zend_tmp_string_release(tmp_name); + + if (op1_type != IS_CV) { + zval_ptr_dtor_nogc(free_op1); + } + } + + if (UNEXPECTED(*retval == NULL)) { + return FAILURE; + } + + *prop_info = property_info; + + if (EXPECTED(op1_type == IS_CONST)) { + CACHE_POLYMORPHIC_PTR(cache_slot, ce, *retval); + CACHE_PTR(cache_slot + sizeof(void *) * 2, property_info); + } + + return SUCCESS; +} + + +static zend_always_inline int zend_fetch_static_property_address(zval **retval, zend_property_info **prop_info, uint32_t cache_slot, int fetch_type, int flags OPLINE_DC EXECUTE_DATA_DC) { + int success; + zend_property_info *property_info; + + if (opline->op1_type == IS_CONST && (opline->op2_type == IS_CONST || (opline->op2_type == IS_UNUSED && (opline->op2.num == ZEND_FETCH_CLASS_SELF || opline->op2.num == ZEND_FETCH_CLASS_PARENT))) && EXPECTED(CACHED_PTR(cache_slot) != NULL)) { + *retval = CACHED_PTR(cache_slot + sizeof(void *)); + property_info = CACHED_PTR(cache_slot + sizeof(void *) * 2); + + if ((fetch_type == BP_VAR_R || fetch_type == BP_VAR_RW) + && UNEXPECTED(Z_TYPE_P(*retval) == IS_UNDEF) && UNEXPECTED(property_info->type != 0)) { + zend_throw_error(NULL, "Typed static property %s::$%s must not be accessed before initialization", + ZSTR_VAL(property_info->ce->name), + zend_get_unmangled_property_name(property_info->name)); + return FAILURE; + } + } else { + success = zend_fetch_static_property_address_ex(retval, &property_info, cache_slot, fetch_type OPLINE_CC EXECUTE_DATA_CC); + if (UNEXPECTED(success != SUCCESS)) { + return FAILURE; + } + } + + if (flags && property_info->type) { + zend_handle_fetch_obj_flags(NULL, *retval, NULL, property_info, flags); + } + + if (prop_info) { + *prop_info = property_info; + } + + return SUCCESS; +} + +ZEND_API ZEND_COLD void zend_throw_ref_type_error_type(zend_property_info *prop1, zend_property_info *prop2, zval *zv) { + const char *prop1_type1, *prop1_type2, *prop2_type1, *prop2_type2; + zend_format_type(prop1->type, &prop1_type1, &prop1_type2); + zend_format_type(prop2->type, &prop2_type1, &prop2_type2); + zend_type_error("Reference with value of type %s held by property %s::$%s of type %s%s is not compatible with property %s::$%s of type %s%s", + Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)), + ZSTR_VAL(prop1->ce->name), + zend_get_unmangled_property_name(prop1->name), + prop1_type1, prop1_type2, + ZSTR_VAL(prop2->ce->name), + zend_get_unmangled_property_name(prop2->name), + prop2_type1, prop2_type2 + ); +} + +ZEND_API ZEND_COLD void zend_throw_ref_type_error_zval(zend_property_info *prop, zval *zv) { + const char *prop_type1, *prop_type2; + zend_format_type(prop->type, &prop_type1, &prop_type2); + zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s%s", + Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)), + ZSTR_VAL(prop->ce->name), + zend_get_unmangled_property_name(prop->name), + prop_type1, prop_type2 + ); +} + +ZEND_API ZEND_COLD void zend_throw_conflicting_coercion_error(zend_property_info *prop1, zend_property_info *prop2, zval *zv) { + const char *prop1_type1, *prop1_type2, *prop2_type1, *prop2_type2; + zend_format_type(prop1->type, &prop1_type1, &prop1_type2); + zend_format_type(prop2->type, &prop2_type1, &prop2_type2); + zend_type_error("Cannot assign %s to reference held by property %s::$%s of type %s%s and property %s::$%s of type %s%s, as this would result in an inconsistent type conversion", + Z_TYPE_P(zv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(zv)->name) : zend_get_type_by_const(Z_TYPE_P(zv)), + ZSTR_VAL(prop1->ce->name), + zend_get_unmangled_property_name(prop1->name), + prop1_type1, prop1_type2, + ZSTR_VAL(prop2->ce->name), + zend_get_unmangled_property_name(prop2->name), + prop2_type1, prop2_type2 + ); +} + +/* 1: valid, 0: invalid, -1: may be valid after type coercion */ +static zend_always_inline int i_zend_verify_type_assignable_zval( + zend_type *type_ptr, zend_class_entry *self_ce, zval *zv, zend_bool strict) { + zend_type type = *type_ptr; + zend_uchar type_code; + zend_uchar zv_type = Z_TYPE_P(zv); + + if (ZEND_TYPE_ALLOW_NULL(type) && zv_type == IS_NULL) { + return 1; + } + + if (ZEND_TYPE_IS_CLASS(type)) { + if (!ZEND_TYPE_IS_CE(type)) { + if (!zend_resolve_class_type(type_ptr, self_ce)) { + return 0; + } + type = *type_ptr; + } + return zv_type == IS_OBJECT && instanceof_function(Z_OBJCE_P(zv), ZEND_TYPE_CE(type)); + } + + type_code = ZEND_TYPE_CODE(type); + if (type_code == zv_type || + (type_code == _IS_BOOL && (zv_type == IS_FALSE || zv_type == IS_TRUE))) { + return 1; + } + + if (type_code == IS_ITERABLE) { + return zend_is_iterable(zv); + } + + /* SSTH Exception: IS_LONG may be accepted as IS_DOUBLE (converted) */ + if (strict) { + if (type_code == IS_DOUBLE && zv_type == IS_LONG) { + return -1; + } + return 0; + } + + /* No weak conversions for arrays and objects */ + if (type_code == IS_ARRAY || type_code == IS_OBJECT) { + return 0; + } + + /* NULL may be accepted only by nullable hints (this is already checked) */ + if (zv_type == IS_NULL) { + return 0; + } + + /* Coercion may be necessary, check separately */ + return -1; +} + +static zend_always_inline zend_bool i_zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, zend_bool strict) { + zend_property_info *prop; + + /* The value must satisfy each property type, and coerce to the same value for each property + * type. Right now, the latter rule means that *if* coercion is necessary, then all types + * must be the same (modulo nullability). To handle this, remember the first type we see and + * compare against it when coercion becomes necessary. */ + zend_property_info *seen_prop = NULL; + zend_uchar seen_type; + zend_bool needs_coercion = 0; + + ZEND_ASSERT(Z_TYPE_P(zv) != IS_REFERENCE); + ZEND_REF_FOREACH_TYPE_SOURCES(ref, prop) { + int result = i_zend_verify_type_assignable_zval(&prop->type, prop->ce, zv, strict); + if (result == 0) { + zend_throw_ref_type_error_zval(prop, zv); + return 0; + } + + if (result < 0) { + needs_coercion = 1; + } + + if (!seen_prop) { + seen_prop = prop; + seen_type = ZEND_TYPE_IS_CLASS(prop->type) ? IS_OBJECT : ZEND_TYPE_CODE(prop->type); + } else if (needs_coercion && seen_type != ZEND_TYPE_CODE(prop->type)) { + zend_throw_conflicting_coercion_error(seen_prop, prop, zv); + return 0; + } + } ZEND_REF_FOREACH_TYPE_SOURCES_END(); + + if (UNEXPECTED(needs_coercion && !zend_verify_weak_scalar_type_hint(seen_type, zv))) { + zend_throw_ref_type_error_zval(seen_prop, zv); + return 0; + } + + return 1; +} + +ZEND_API zend_bool ZEND_FASTCALL zend_verify_ref_assignable_zval(zend_reference *ref, zval *zv, zend_bool strict) +{ + return i_zend_verify_ref_assignable_zval(ref, zv, strict); +} + +ZEND_API zend_bool ZEND_FASTCALL zend_verify_prop_assignable_by_ref(zend_property_info *prop_info, zval *orig_val, zend_bool strict) { + zval *val = orig_val; + if (Z_ISREF_P(val) && ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(val))) { + int result; + + val = Z_REFVAL_P(val); + result = i_zend_verify_type_assignable_zval(&prop_info->type, prop_info->ce, val, strict); + if (result > 0) { + return 1; + } + + if (result < 0) { + zend_property_info *ref_prop = ZEND_REF_FIRST_SOURCE(Z_REF_P(orig_val)); + if (ZEND_TYPE_CODE(prop_info->type) != ZEND_TYPE_CODE(ref_prop->type)) { + /* Invalid due to conflicting coercion */ + zend_throw_ref_type_error_type(ref_prop, prop_info, val); + return 0; + } + if (zend_verify_weak_scalar_type_hint(ZEND_TYPE_CODE(prop_info->type), val)) { + return 1; + } + } + } else { + ZVAL_DEREF(val); + if (i_zend_check_property_type(prop_info, val, strict)) { + return 1; + } + } + + zend_verify_property_type_error(prop_info, val); + return 0; +} + +ZEND_API void ZEND_FASTCALL zend_ref_add_type_source(zend_property_info_source_list *source_list, zend_property_info *prop) +{ + zend_property_info_list *list; + if (source_list->ptr == NULL) { + source_list->ptr = prop; + return; + } + + list = ZEND_PROPERTY_INFO_SOURCE_TO_LIST(source_list->list); + if (!ZEND_PROPERTY_INFO_SOURCE_IS_LIST(source_list->list)) { + list = emalloc(sizeof(zend_property_info_list) + (4 - 1) * sizeof(zend_property_info *)); + list->ptr[0] = source_list->ptr; + list->num_allocated = 4; + list->num = 1; + } else if (list->num_allocated == list->num) { + list->num_allocated = list->num * 2; + list = erealloc(list, sizeof(zend_property_info_list) + (list->num_allocated - 1) * sizeof(zend_property_info *)); + } + + list->ptr[list->num++] = prop; + source_list->list = ZEND_PROPERTY_INFO_SOURCE_FROM_LIST(list); +} + +ZEND_API void ZEND_FASTCALL zend_ref_del_type_source(zend_property_info_source_list *source_list, zend_property_info *prop) +{ + zend_property_info_list *list = ZEND_PROPERTY_INFO_SOURCE_TO_LIST(source_list->list); + zend_property_info **ptr, **end; + + if (!ZEND_PROPERTY_INFO_SOURCE_IS_LIST(source_list->list)) { + ZEND_ASSERT(source_list->ptr == prop); + source_list->ptr = NULL; + return; + } + + if (list->num == 1) { + ZEND_ASSERT(*list->ptr == prop); + efree(list); + source_list->ptr = NULL; + return; + } + + /* Checking against end here to get a more graceful failure mode if we missed adding a type + * source at some point. */ + ptr = list->ptr; + end = ptr + list->num; + while (ptr < end && *ptr != prop) { + ptr++; + } + ZEND_ASSERT(*ptr == prop); + + /* Copy the last list element into the deleted slot. */ + *ptr = list->ptr[--list->num]; + + if (list->num >= 4 && list->num * 4 == list->num_allocated) { + list->num_allocated = list->num * 2; + source_list->list = ZEND_PROPERTY_INFO_SOURCE_FROM_LIST(erealloc(list, sizeof(zend_property_info_list) + (list->num_allocated - 1) * sizeof(zend_property_info *))); } } @@ -2298,6 +3153,10 @@ static zend_never_inline void zend_fetch_this_var(int type OPLINE_DC EXECUTE_DAT } } +static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_wrong_clone_call(zend_function *clone, zend_class_entry *scope) +{ + zend_throw_error(NULL, "Call to %s %s::__clone() from context '%s'", zend_visibility_string(clone->common.fn_flags), ZSTR_VAL(clone->common.scope->name), scope ? ZSTR_VAL(scope->name) : ""); +} #if ZEND_INTENSIVE_DEBUGGING @@ -2396,7 +3255,7 @@ ZEND_API void zend_free_compiled_variables(zend_execute_data *execute_data) /* { * +========================================+ * EG(current_execute_data) -> | zend_execute_data | * +----------------------------------------+ - * EX_CV_NUM(0) ---------> | VAR[0] = ARG[1] | + * EX_VAR_NUM(0) --------> | VAR[0] = ARG[1] | * | ... | * | VAR[op_array->num_args-1] = ARG[N] | * | ... | @@ -2409,6 +3268,15 @@ ZEND_API void zend_free_compiled_variables(zend_execute_data *execute_data) /* { * +----------------------------------------+ */ +/* zend_copy_extra_args is used when the actually passed number of arguments + * (EX_NUM_ARGS) is greater than what the function defined (op_array->num_args). + * + * The extra arguments will be copied into the call frame after all the compiled variables. + * + * If there are extra arguments copied, a flag "ZEND_CALL_FREE_EXTRA_ARGS" will be set + * on the zend_execute_data, and when the executor leaves the function, the + * args will be freed in zend_leave_helper. + */ static zend_never_inline void zend_copy_extra_args(EXECUTE_DATA_D) { zend_op_array *op_array = &EX(func)->op_array; @@ -2421,7 +3289,7 @@ static zend_never_inline void zend_copy_extra_args(EXECUTE_DATA_D) 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)) +#if defined(ZEND_VM_IP_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; @@ -2473,7 +3341,7 @@ static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, 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)) +#if defined(ZEND_VM_IP_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; @@ -2490,7 +3358,7 @@ static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, } } 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)) +#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) opline += num_args; #else EX(opline) += num_args; @@ -2503,45 +3371,26 @@ static zend_always_inline void i_init_func_execute_data(zend_op_array *op_array, EX_LOAD_RUN_TIME_CACHE(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)) +#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) EX(opline) = opline; #endif } /* }}} */ -static zend_never_inline void ZEND_FASTCALL init_func_run_time_cache(zend_op_array *op_array) /* {{{ */ -{ - ZEND_ASSERT(op_array->run_time_cache == NULL); - 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); -} -/* }}} */ - -static zend_always_inline zend_function* ZEND_FASTCALL init_func_run_time_cache_i(zval *zv) /* {{{ */ +static zend_always_inline void init_func_run_time_cache_i(zend_op_array *op_array) /* {{{ */ { - 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); + void **run_time_cache; - 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; - } + ZEND_ASSERT(RUN_TIME_CACHE(op_array) == NULL); + run_time_cache = zend_arena_alloc(&CG(arena), op_array->cache_size); + memset(run_time_cache, 0, op_array->cache_size); + ZEND_MAP_PTR_SET(op_array->run_time_cache, run_time_cache); } /* }}} */ -static zend_never_inline zend_function* init_func_run_time_cache_ex(zval *zv) /* {{{ */ +static zend_never_inline void ZEND_FASTCALL init_func_run_time_cache(zend_op_array *op_array) /* {{{ */ { - return init_func_run_time_cache_i(zv); + init_func_run_time_cache_i(op_array); } /* }}} */ @@ -2552,8 +3401,8 @@ ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function(zend_string *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); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache_i(&fbc->op_array); } return fbc; } @@ -2567,8 +3416,8 @@ ZEND_API zend_function * ZEND_FASTCALL zend_fetch_function_str(const char *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); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache_i(&fbc->op_array); } return fbc; } @@ -2585,9 +3434,15 @@ static zend_always_inline void i_init_code_execute_data(zend_execute_data *execu zend_attach_symbol_table(execute_data); - if (!op_array->run_time_cache) { - op_array->run_time_cache = emalloc(op_array->cache_size); - memset(op_array->run_time_cache, 0, op_array->cache_size); + if (!ZEND_MAP_PTR(op_array->run_time_cache)) { + void *ptr; + + ZEND_ASSERT(op_array->fn_flags & ZEND_ACC_HEAP_RT_CACHE); + ptr = emalloc(op_array->cache_size + sizeof(void*)); + ZEND_MAP_PTR_INIT(op_array->run_time_cache, ptr); + ptr = (char*)ptr + sizeof(void*); + ZEND_MAP_PTR_SET(op_array->run_time_cache, ptr); + memset(ptr, 0, op_array->cache_size); } EX_LOAD_RUN_TIME_CACHE(op_array); @@ -2599,21 +3454,27 @@ ZEND_API void zend_init_func_execute_data(zend_execute_data *ex, zend_op_array * { #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; +#endif +#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) const zend_op *orig_opline = opline; +#endif +#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) 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) { + if (!RUN_TIME_CACHE(op_array)) { init_func_run_time_cache(op_array); } 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)) +#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) EX(opline) = opline; opline = orig_opline; +#endif +#if defined(ZEND_VM_FP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID)) execute_data = orig_execute_data; #endif } @@ -2622,10 +3483,6 @@ ZEND_API void zend_init_func_execute_data(zend_execute_data *ex, zend_op_array * ZEND_API void zend_init_code_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value) /* {{{ */ { EX(prev_execute_data) = EG(current_execute_data); - if (!op_array->run_time_cache) { - op_array->run_time_cache = emalloc(op_array->cache_size); - memset(op_array->run_time_cache, 0, op_array->cache_size); - } i_init_code_execute_data(execute_data, op_array, return_value); } /* }}} */ @@ -2640,15 +3497,6 @@ 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(uint32_t arg_num, zend_function *func) /* {{{ */ -{ - if (EXPECTED(arg_num <= MAX_ARG_FLAG_NUM)) { - return QUICK_ARG_SHOULD_BE_SENT_BY_REF(func, arg_num); - } - return ARG_SHOULD_BE_SENT_BY_REF(func, arg_num); -} -/* }}} */ - static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call, uint32_t passed_args, uint32_t additional_args) /* {{{ */ { zend_execute_data *new_call; @@ -2673,7 +3521,7 @@ static zend_execute_data *zend_vm_stack_copy_call_frame(zend_execute_data *call, /* delete old call_frame from previous stack segment */ EG(vm_stack)->prev->top = (zval*)call; - /* delete previous stack segment if it becames empty */ + /* delete previous stack segment if it became empty */ if (UNEXPECTED(EG(vm_stack)->prev->top == ZEND_VM_STACK_ELEMENTS(EG(vm_stack)->prev))) { zend_vm_stack r = EG(vm_stack)->prev; @@ -2812,12 +3660,6 @@ static void cleanup_unfinished_calls(zend_execute_data *execute_data, uint32_t o zend_vm_stack_free_args(EX(call)); if (ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS) { - if (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR) { - GC_DELREF(Z_OBJ(call->This)); - if (GC_REFCOUNT(Z_OBJ(call->This)) == 1) { - zend_object_store_ctor_failed(Z_OBJ(call->This)); - } - } OBJ_RELEASE(Z_OBJ(call->This)); } if (call->func->common.fn_flags & ZEND_ACC_CLOSURE) { @@ -2866,6 +3708,12 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, if (kind == ZEND_LIVE_TMPVAR) { zval_ptr_dtor_nogc(var); + } else if (kind == ZEND_LIVE_NEW) { + zend_object *obj; + ZEND_ASSERT(Z_TYPE_P(var) == IS_OBJECT); + obj = Z_OBJ_P(var); + zend_object_store_ctor_failed(obj); + OBJ_RELEASE(obj); } else if (kind == ZEND_LIVE_LOOP) { if (Z_TYPE_P(var) != IS_ARRAY && Z_FE_ITER_P(var) != (uint32_t)-1) { zend_hash_iterator_del(Z_FE_ITER_P(var)); @@ -2967,7 +3815,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s return NULL; } } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } } else { @@ -2985,20 +3833,21 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_string(zend_s 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); + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { + init_func_run_time_cache(&fbc->op_array); } called_scope = NULL; } return zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC, - fbc, num_args, called_scope, NULL); + fbc, num_args, called_scope); } /* }}} */ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zval *function, uint32_t num_args) /* {{{ */ { zend_function *fbc; + void *object_or_called_scope; zend_class_entry *called_scope; zend_object *object; uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC; @@ -3006,6 +3855,7 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zval * if (EXPECTED(Z_OBJ_HANDLER_P(function, get_closure)) && EXPECTED(Z_OBJ_HANDLER_P(function, get_closure)(function, &called_scope, &fbc, &object) == SUCCESS)) { + object_or_called_scope = called_scope; if (fbc->common.fn_flags & ZEND_ACC_CLOSURE) { /* Delay closure destruction until its invocation */ GC_ADDREF(ZEND_CLOSURE_OBJECT(fbc)); @@ -3013,29 +3863,33 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_object(zval * if (fbc->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) { call_info |= ZEND_CALL_FAKE_CLOSURE; } + if (object) { + call_info |= ZEND_CALL_HAS_THIS; + object_or_called_scope = object; + } } else if (object) { - call_info |= ZEND_CALL_RELEASE_THIS; + call_info |= ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS; GC_ADDREF(object); /* For $this pointer */ + object_or_called_scope = object; } } else { zend_throw_error(NULL, "Function name must be a string"); return NULL; } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } return zend_vm_stack_push_call_frame(call_info, - fbc, num_args, called_scope, object); + fbc, num_args, object_or_called_scope); } /* }}} */ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_array *function, uint32_t num_args) /* {{{ */ { zend_function *fbc; - zend_class_entry *called_scope; - zend_object *object; + void *object_or_called_scope; uint32_t call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC; if (zend_hash_num_elements(function) == 2) { @@ -3062,8 +3916,8 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar } if (Z_TYPE_P(obj) == IS_STRING) { - object = NULL; - called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + zend_class_entry *called_scope = zend_fetch_class_by_name(Z_STR_P(obj), NULL, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION); + if (UNEXPECTED(called_scope == NULL)) { return NULL; } @@ -3085,9 +3939,9 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar return NULL; } } + object_or_called_scope = called_scope; } else { - called_scope = Z_OBJCE_P(obj); - object = Z_OBJ_P(obj); + zend_object *object = Z_OBJ_P(obj); fbc = Z_OBJ_HT_P(obj)->get_method(&object, Z_STR_P(method), NULL); if (UNEXPECTED(fbc == NULL)) { @@ -3098,10 +3952,11 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar } if ((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0) { - object = NULL; + object_or_called_scope = object->ce; } else { - call_info |= ZEND_CALL_RELEASE_THIS; + call_info |= ZEND_CALL_RELEASE_THIS | ZEND_CALL_HAS_THIS; GC_ADDREF(object); /* For $this pointer */ + object_or_called_scope = object; } } } else { @@ -3109,12 +3964,12 @@ static zend_never_inline zend_execute_data *zend_init_dynamic_call_array(zend_ar return NULL; } - if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { + if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!RUN_TIME_CACHE(&fbc->op_array))) { init_func_run_time_cache(&fbc->op_array); } return zend_vm_stack_push_call_frame(call_info, - fbc, num_args, called_scope, object); + fbc, num_args, object_or_called_scope); } /* }}} */ @@ -3460,29 +4315,29 @@ static zend_never_inline int ZEND_FASTCALL zend_quick_check_constant( OPLINE = opline; \ ZEND_VM_CONTINUE() # define ZEND_VM_SMART_BRANCH(_result, _check) do { \ - int __result; \ + if ((_check) && UNEXPECTED(EG(exception))) { \ + break; \ + } \ if (EXPECTED((opline+1)->opcode == ZEND_JMPZ)) { \ - __result = (_result); \ + if (_result) { \ + ZEND_VM_SET_NEXT_OPCODE(opline + 2); \ + } else { \ + ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \ + } \ } else if (EXPECTED((opline+1)->opcode == ZEND_JMPNZ)) { \ - __result = !(_result); \ + if (!(_result)) { \ + ZEND_VM_SET_NEXT_OPCODE(opline + 2); \ + } else { \ + ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \ + } \ } else { \ break; \ } \ - if ((_check) && UNEXPECTED(EG(exception))) { \ - ZVAL_UNDEF(EX_VAR(opline->result.var)); \ - HANDLE_EXCEPTION(); \ - } \ - if (__result) { \ - ZEND_VM_SET_NEXT_OPCODE(opline + 2); \ - } else { \ - ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \ - } \ ZEND_VM_CONTINUE(); \ } while (0) # define ZEND_VM_SMART_BRANCH_JMPZ(_result, _check) do { \ if ((_check) && UNEXPECTED(EG(exception))) { \ - ZVAL_UNDEF(EX_VAR(opline->result.var)); \ - HANDLE_EXCEPTION(); \ + break; \ } \ if (_result) { \ ZEND_VM_SET_NEXT_OPCODE(opline + 2); \ @@ -3493,8 +4348,7 @@ static zend_never_inline int ZEND_FASTCALL zend_quick_check_constant( } while (0) # define ZEND_VM_SMART_BRANCH_JMPNZ(_result, _check) do { \ if ((_check) && UNEXPECTED(EG(exception))) { \ - ZVAL_UNDEF(EX_VAR(opline->result.var)); \ - HANDLE_EXCEPTION(); \ + break; \ } \ if (!(_result)) { \ ZEND_VM_SET_NEXT_OPCODE(opline + 2); \ @@ -3503,12 +4357,32 @@ static zend_never_inline int ZEND_FASTCALL zend_quick_check_constant( } \ ZEND_VM_CONTINUE(); \ } while (0) +#define ZEND_VM_SMART_BRANCH_TRUE() do { \ + if (EXPECTED((opline+1)->opcode == ZEND_JMPNZ)) { \ + ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \ + ZEND_VM_CONTINUE(); \ + } else if (EXPECTED((opline+1)->opcode == ZEND_JMPZ)) { \ + ZEND_VM_SET_NEXT_OPCODE(opline + 2); \ + ZEND_VM_CONTINUE(); \ + } \ + } while (0) +#define ZEND_VM_SMART_BRANCH_FALSE() do { \ + if (EXPECTED((opline+1)->opcode == ZEND_JMPNZ)) { \ + ZEND_VM_SET_NEXT_OPCODE(opline + 2); \ + ZEND_VM_CONTINUE(); \ + } else if (EXPECTED((opline+1)->opcode == ZEND_JMPZ)) { \ + ZEND_VM_SET_OPCODE(OP_JMP_ADDR(opline + 1, (opline+1)->op2)); \ + ZEND_VM_CONTINUE(); \ + } \ + } while (0) #else # define ZEND_VM_REPEATABLE_OPCODE # define ZEND_VM_REPEAT_OPCODE(_opcode) # define ZEND_VM_SMART_BRANCH(_result, _check) # define ZEND_VM_SMART_BRANCH_JMPZ(_result, _check) # define ZEND_VM_SMART_BRANCH_JMPNZ(_result, _check) +# define ZEND_VM_SMART_BRANCH_TRUE() +# define ZEND_VM_SMART_BRANCH_FALSE() #endif #ifdef __GNUC__ @@ -3517,11 +4391,6 @@ static zend_never_inline int ZEND_FASTCALL zend_quick_check_constant( # define ZEND_VM_GUARD(name) #endif -#define GET_OP1_UNDEF_CV(ptr, type) \ - _get_zval_cv_lookup_ ## type(ptr, opline->op1.var EXECUTE_DATA_CC) -#define GET_OP2_UNDEF_CV(ptr, type) \ - _get_zval_cv_lookup_ ## type(ptr, opline->op2.var EXECUTE_DATA_CC) - #define UNDEF_RESULT() do { \ if (opline->result_type & (IS_VAR | IS_TMP_VAR)) { \ ZVAL_UNDEF(EX_VAR(opline->result.var)); \ @@ -3597,13 +4466,3 @@ ZEND_API int ZEND_FASTCALL zend_check_arg_type(zend_function *zf, uint32_t arg_n { return zend_verify_arg_type(zf, arg_num, arg, default_value, cache_slot); } - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * indent-tabs-mode: t - * End: - * vim600: sw=4 ts=4 fdm=marker - * vim<600: sw=4 ts=4 - */ |