com php-src: Merge branch 'master' into typed_properties: Zend/zend.c Zend/zend_API.c Zend/zend_API.h Zend/zend_ast.c Zend/zend_ast.h
Zend/zend_compile .c Zend/zend_compile.h Zend/zend_execute.c Zend /zend_execute.h Zend/zend_inheritance.c Zend/zend _language_parser.y
Zend/zend_object_handlers.c Zend/zend_opcode.c Zend/zend_vm_def.h Zend/zend _vm_execute.h Zend/zend_vm_gen.php Zend/zend_ vm_opcodes.c
Zend/zend_vm_opcodes.h ext/opcache/O ptimizer/compac
| From: | Bob Weinand | Date: | Sun, 06 Nov 2016 13:11:55 +0000 |
| Subject: | com php-src: Merge branch 'master' into typed_properties: Zend/zend.c Zend/zend_API.c Zend/zend_API.h Zend/zend_ast.c Zend/zend_ast.h Zend/zend_compile .c Zend/zend_compile.h Zend/zend_execute.c Zend /zend_execute.h Zend/zend_inheritance.c Zend/zend _language_parser.y Zend/zend_object_handlers.c Zend/zend_opcode.c Zend/zend_vm_def.h Zend/zend _vm_execute.h Zend/zend_vm_gen.php Zend/zend_ vm_opcodes.c Zend/zend_vm_opcodes.h ext/opcache/O ptimizer/compac |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-95491@lists.php.net to get a copy of this message | ||
Commit: 67ffff912f5387f70e3c7ca53d604259252b0137
Author: Bob Weinand <bobwei9@hotmail.com> Sun, 6 Nov 2016 14:11:55 +0100
Parents: 8eec27f9db01817677321f0fbd1aed6971fb386c c482ee6020aab78f4dfde84f79c533850e2ff93a
Branches: typed_ref_properties
Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=67ffff912f5387f70e3c7ca53d604259252b0137
Log:
Merge branch 'master' into typed_properties
Changed paths:
MM Zend/zend.c
MM Zend/zend_API.c
MM Zend/zend_API.h
MM Zend/zend_ast.c
MM Zend/zend_ast.h
MM Zend/zend_compile.c
MM Zend/zend_compile.h
MM Zend/zend_execute.c
MM Zend/zend_execute.h
MM Zend/zend_inheritance.c
MM Zend/zend_language_parser.y
MM Zend/zend_object_handlers.c
MM Zend/zend_opcode.c
MM Zend/zend_vm_def.h
MM Zend/zend_vm_execute.h
MM Zend/zend_vm_gen.php
MM Zend/zend_vm_opcodes.c
MM Zend/zend_vm_opcodes.h
MM ext/opcache/Optimizer/compact_literals.c
MM ext/opcache/Optimizer/zend_dump.c
MM ext/opcache/Optimizer/zend_optimizer.c
MM ext/opcache/zend_persist.c
MM ext/opcache/zend_persist_calc.c
MM ext/reflection/php_reflection.c
MM ext/reflection/tests/ReflectionType_001.phpt
MM ext/standard/var.c
MM ext/standard/var_unserializer.c
MM ext/standard/var_unserializer.re
diff --cc Zend/zend_compile.c index 70a65a4,14e9a9c..5d89538 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@@ -3024,8 -3051,21 +3060,21 @@@ void zend_compile_assign_ref(znode *res zend_ensure_writable_variable(target_ast); offset = zend_delayed_compile_begin(); - zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W); - zend_compile_var(&source_node, source_ast, BP_VAR_W); + zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W, 1); - zend_delayed_compile_var(&source_node, source_ast, BP_VAR_W, 1); ++ zend_compile_var(&source_node, source_ast, BP_VAR_W, 1); + + if ((target_ast->kind != ZEND_AST_VAR + || target_ast->child[0]->kind != ZEND_AST_ZVAL) + && source_node.op_type != IS_CV) { + /* Both LHS and RHS expressions may modify the same data structure, + * and the modification during RHS evaluation may dangle the pointer + * to the result of the LHS evaluation. + * Use MAKE_REF instruction to replace direct pointer with REFERENCE. + * See: Bug #71539 + */ + zend_emit_op(&source_node, ZEND_MAKE_REF, &source_node, NULL); + } + zend_delayed_compile_end(offset); if (source_node.op_type != IS_VAR && zend_is_call(source_ast)) { diff --cc Zend/zend_execute.c index b10f969,1bc67fb..9f1d572 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@@ -731,14 -800,53 +810,53 @@@ static zend_bool zend_verify_scalar_typ /* NULL may be accepted only by nullable hints (this is already checked) */ return 0; } - return zend_verify_weak_scalar_type_hint(type_hint, arg); + return zend_verify_weak_scalar_type_hint(type_hint, arg, ret); } + static zend_always_inline zend_bool zend_check_internal_type( + const zend_function *zf, const zend_internal_arg_info *arg_info, + zval *arg, zend_class_entry **ce, zend_bool is_return_type) + { + if (!arg_info->type_hint) { + return 1; + } + + ZVAL_DEREF(arg); + if (EXPECTED(arg_info->type_hint == Z_TYPE_P(arg))) { + if (arg_info->class_name) { + *ce = zend_verify_internal_arg_class_kind(arg_info); + return *ce && instanceof_function(Z_OBJCE_P(arg), *ce); + } + return 1; + } + + if (Z_TYPE_P(arg) == IS_NULL && arg_info->allow_null) { + return 1; + } + + if (arg_info->class_name) { + *ce = zend_verify_internal_arg_class_kind(arg_info); + return 0; + } else if (arg_info->type_hint == IS_CALLABLE) { + return zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL); + } else if (arg_info->type_hint == IS_ITERABLE) { + return zend_is_iterable(arg); + } else if (arg_info->type_hint == _IS_BOOL && + EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { + return 1; + } else if (is_return_type) { + /* Internal return types are always strict */ + return 0; + } else { + /* Internal parameter types honor strict_types */ - return zend_verify_scalar_type_hint(arg_info->type_hint, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))); ++ return zend_verify_scalar_type_hint(arg_info->type_hint, arg, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))); + } + } + static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg) { - zend_internal_arg_info *cur_arg_info; - char *need_msg, *class_name; - zend_class_entry *ce; + const zend_internal_arg_info *cur_arg_info; + zend_class_entry *ce = NULL; if (EXPECTED(arg_num <= zf->internal_function.num_args)) { cur_arg_info = &zf->internal_function.arg_info[arg_num-1]; @@@ -947,14 -906,44 +996,44 @@@ static zend_always_inline zend_bool zen return 0; } } + return 1; } - return 1; + + if (Z_TYPE_P(arg) == IS_NULL && (arg_info->allow_null || (default_value && is_null_constant(zf->common.scope, default_value)))) { + /* Null passed to nullable type */ + return 1; + } + + if (UNEXPECTED(arg_info->class_name)) { + /* This is always an error - we fetch the class name for the error message here */ + if (EXPECTED(*cache_slot)) { + *ce = (zend_class_entry *) *cache_slot; + } else { + *ce = zend_verify_arg_class_kind(arg_info); + if (*ce) { + *cache_slot = (void *) *ce; + } + } + return 0; + } else if (arg_info->type_hint == IS_CALLABLE) { + return zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL); + } else if (arg_info->type_hint == IS_ITERABLE) { + return zend_is_iterable(arg); + } else if (arg_info->type_hint == _IS_BOOL && + EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { + return 1; + } else { - return zend_verify_scalar_type_hint(arg_info->type_hint, arg, ++ return zend_verify_scalar_type_hint(arg_info->type_hint, arg, arg, + is_return_type ? ZEND_RET_USES_STRICT_TYPES() : ZEND_ARG_USES_STRICT_TYPES()); + } + + /* Special handling for IS_VOID is not necessary (for return types), + * because this case is already checked at compile-time. */ } - static zend_always_inline int zend_verify_missing_arg_type(zend_function *zf, uint32_t arg_num, void **cache_slot) + static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot) { zend_arg_info *cur_arg_info; - char *need_msg; zend_class_entry *ce; if (EXPECTED(arg_num <= zf->common.num_args)) { @@@ -2084,17 -1927,10 +2059,12 @@@ ZEND_API void zend_fetch_dimension_by_z } -static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type) +static zend_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 by_ref) { + zval *ptr; + if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { do { - if (container_op_type == IS_VAR && UNEXPECTED(Z_ISERROR_P(container))) { - ZVAL_ERROR(result); - return; - } - if (Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { @@@ -2159,12 -1980,13 +2131,13 @@@ return_indirect } } 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); + 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); + goto return_indirect; } else if (UNEXPECTED(Z_ISREF_P(ptr) && Z_REFCOUNT_P(ptr) == 1)) { ZVAL_UNREF(ptr); } @@@ -2173,15 -1995,10 +2146,10 @@@ ZVAL_ERROR(result); } } else { - ZVAL_INDIRECT(result, ptr); + goto return_indirect; } } else if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) { - zval *ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result); - if (ptr != result) { - goto return_indirect; - } else if (UNEXPECTED(Z_ISREF_P(ptr) && Z_REFCOUNT_P(ptr) == 1)) { - ZVAL_UNREF(ptr); - } + goto use_read_property; } else { zend_error(E_WARNING, "This object doesn't support property references"); ZVAL_ERROR(result); diff --cc Zend/zend_vm_execute.h index 7095bb9,4e5c0f9..3bd2b19 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@@ -3441,162 -3524,165 +3524,179 @@@ static ZEND_OPCODE_HANDLER_RET ZEND_FAS if (IS_CONST != IS_TMP_VAR) { Z_ADDREF_P(array_ptr); } - fe_ht = Z_OBJPROP_P(array_ptr); - pos = 0; - p = fe_ht->arData; - while (1) { - if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { - - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } - if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || - EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { - break; + if (Z_OBJ_P(array_ptr)->properties + && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) { + if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) { + GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--; } - pos++; - p++; - } - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); - - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); - } else { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); - zend_bool is_empty; - - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ZSTR_VAL(ce->name)); - } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } - - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - - HANDLE_EXCEPTION(); - } - } - - is_empty = iter->funcs->valid(iter) != SUCCESS; - - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); - } - } - } else { - zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - ZVAL_UNDEF(EX_VAR(opline->result.var)); - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } - } - - static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) - { - USE_OPLINE - - zval *array_ptr, *array_ref; - HashTable *fe_ht; - HashPosition pos = 0; - Bucket *p; - - SAVE_OPLINE(); - - if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { - array_ref = array_ptr = NULL; - if (Z_ISREF_P(array_ref)) { - array_ptr = Z_REFVAL_P(array_ref); - } - } else { - array_ref = array_ptr = EX_CONSTANT(opline->op1); - } - - if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { - if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { - if (array_ptr == array_ref) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - Z_ADDREF_P(array_ref); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); - } else { - array_ref = EX_VAR(opline->result.var); - ZVAL_NEW_REF(array_ref, array_ptr); - array_ptr = Z_REFVAL_P(array_ref); - } - if (IS_CONST == IS_CONST) { - zval_copy_ctor_func(array_ptr); - } else { - SEPARATE_ARRAY(array_ptr); - } - fe_ht = Z_ARRVAL_P(array_ptr); - p = fe_ht->arData; - while (1) { - if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { - - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || - EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) { - break; - } - pos++; - p++; - } - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); - - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); - } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { - if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(Z_OBJCE_P(array_ptr)))) { - fe_reset_rw_typed: - zend_throw_exception_ex( - zend_ce_type_error, 0, - "Typed properties exist in %s: foreach by reference is disallowed", - ZSTR_VAL(Z_OBJCE_P(array_ptr)->name)); - if (IS_CONST == IS_VAR) { - - } else { - - } - HANDLE_EXCEPTION(); - } - - if (!Z_OBJCE_P(array_ptr)->get_iterator) { - if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { - if (array_ptr == array_ref) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - Z_ADDREF_P(array_ref); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); - } else { - array_ptr = EX_VAR(opline->result.var); - ZVAL_COPY_VALUE(array_ptr, array_ref); + Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties); + } + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + p = fe_ht->arData; + while (1) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && + (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || + EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && + (UNEXPECTED(!p->key) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + break; + } + pos++; + p++; + } + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + + if (iter) { + OBJ_RELEASE(&iter->std); + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ZSTR_VAL(ce->name)); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); + } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } + } + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + } + + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) + { + USE_OPLINE + + zval *array_ptr, *array_ref; + HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; + + SAVE_OPLINE(); + + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + array_ref = array_ptr = NULL; + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); + } + } else { + array_ref = array_ptr = EX_CONSTANT(opline->op1); + } + + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ref = EX_VAR(opline->result.var); + ZVAL_NEW_REF(array_ref, array_ptr); + array_ptr = Z_REFVAL_P(array_ref); + } + if (IS_CONST == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); + } + fe_ht = Z_ARRVAL_P(array_ptr); + p = fe_ht->arData; + while (1) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && + (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || + EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) { + break; + } + pos++; + p++; + } + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { ++ if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(Z_OBJCE_P(array_ptr)))) { ++fe_reset_rw_typed: ++ zend_throw_exception_ex( ++ zend_ce_type_error, 0, ++ "Typed properties exist in %s: foreach by reference is disallowed", ++ ZSTR_VAL(Z_OBJCE_P(array_ptr)->name)); ++ if (IS_CONST == IS_VAR) { ++ ++ } else { ++ ++ } ++ HANDLE_EXCEPTION(); ++ } ++ + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + if (Z_OBJ_P(array_ptr)->properties + && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) { + if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) { + GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--; + } + Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties); } fe_ht = Z_OBJPROP_P(array_ptr); p = fe_ht->arData; diff --cc Zend/zend_vm_gen.php index 14718ed,d379599..0d21e33 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@@ -128,6 -127,6 +128,7 @@@ $vm_ext_decode = array "ISSET" => ZEND_VM_EXT_ISSET, "ARG_NUM" => ZEND_VM_EXT_ARG_NUM, "REF" => ZEND_VM_EXT_REF, ++ "FETCH_REF" => ZEND_VM_EXT_FETCH_REF, "SRC" => ZEND_VM_EXT_SRC, ); diff --cc ext/opcache/Optimizer/zend_optimizer.c index c370dc5,82a6daa..f603957 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@@ -189,8 -189,17 +189,17 @@@ int zend_optimizer_update_op1_const(zen case ZEND_FETCH_STATIC_PROP_FUNC_ARG: TO_STRING_NOWARN(val); opline->op1.constant = zend_optimizer_add_literal(op_array, val); - alloc_cache_slots_op1(op_array, opline, 2); + alloc_cache_slots_op1(op_array, opline, 3); break; + case ZEND_SEND_VAR: + opline->opcode = ZEND_SEND_VAL; + opline->op1.constant = zend_optimizer_add_literal(op_array, val); + break; + case ZEND_SEPARATE: + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + zval_ptr_dtor(val); + return 0; case ZEND_CONCAT: case ZEND_FAST_CONCAT: case ZEND_FETCH_R: diff --cc ext/reflection/php_reflection.c index 6fc340f,1169bb6..9ea6148 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@@ -1281,12 -1277,11 +1291,12 @@@ static void reflection_arg_type_factory reflection_object *intern; type_reference *reference; - reflection_instantiate(reflection_type_ptr, object); + reflection_instantiate(reflection_named_type_ptr, object); intern = Z_REFLECTION_P(object); reference = (type_reference*) emalloc(sizeof(type_reference)); - reference->arg_info = arg_info; - reference->fptr = fptr; + reference->type = REF_INFO_ARG; + reference->info.arg = arg_info; + reference->scope.fptr = fptr; intern->ptr = reference; intern->ref_type = REF_TYPE_TYPE; intern->ce = fptr->common.scope; @@@ -3044,51 -3000,23 +3053,42 @@@ ZEND_METHOD(reflection_type, isBuiltin } /* }}} */ - /* {{{ proto public string ReflectionType::__toString() - Return the text of the type hint */ - ZEND_METHOD(reflection_type, __toString) - { - reflection_object *intern; - type_reference *param; + /* {{{ reflection_type_name */ + static zend_string *reflection_type_name(type_reference *param) { - switch (param->arg_info->type_hint) { + zend_uchar type = IS_UNDEF; + - if (zend_parse_parameters_none() == FAILURE) { - return; - } - - GET_REFLECTION_OBJECT_PTR(param); - + switch (param->type) { + case REF_INFO_ARG: + type = param->info.arg->type_hint; + if (type == IS_OBJECT) { + zend_function *fptr = param->scope.fptr; + + if (fptr->type == ZEND_INTERNAL_FUNCTION && + !(fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { - RETURN_STRING(((zend_internal_arg_info*) param->info.arg)->class_name); ++ return zend_string_init(((zend_internal_arg_info*)param->info.arg)->class_name, strlen(((zend_internal_arg_info*)param->info.arg)->class_name), 0); + } else { - RETURN_STR_COPY(param->info.arg->class_name); ++ return zend_string_copy(param->info.arg->class_name); + } + } + break; + + case REF_INFO_PROP: + type = param->info.prop->type; + if (type == IS_OBJECT) { - RETURN_STR_COPY(param->info.prop->type_name); ++ return zend_string_copy(param->info.prop->type_name); + } + break; + } + + switch (type) { - case IS_ARRAY: RETURN_STRINGL("array", sizeof("array") - 1); - case IS_CALLABLE: RETURN_STRINGL("callable", sizeof("callable") - 1); - case IS_STRING: RETURN_STRINGL("string", sizeof("string") - 1); - case _IS_BOOL: RETURN_STRINGL("bool", sizeof("bool") - 1); - case IS_LONG: RETURN_STRINGL("int", sizeof("int") - 1); - case IS_DOUBLE: RETURN_STRINGL("float", sizeof("float") - 1); - case IS_VOID: RETURN_STRINGL("void", sizeof("void") - 1); + case IS_ARRAY: return zend_string_init("array", sizeof("array") - 1, 0); + case IS_CALLABLE: return zend_string_init("callable", sizeof("callable") - 1, 0); - case IS_OBJECT: - if (param->fptr->type == ZEND_INTERNAL_FUNCTION && - !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { - return zend_string_init(((zend_internal_arg_info*)param->arg_info)->class_name, strlen(((zend_internal_arg_info*)param->arg_info)->class_name), 0); - } - return zend_string_copy(param->arg_info->class_name); + case IS_STRING: return zend_string_init("string", sizeof("string") - 1, 0); + case _IS_BOOL: return zend_string_init("bool", sizeof("bool") - 1, 0); + case IS_LONG: return zend_string_init("int", sizeof("int") - 1, 0); + case IS_DOUBLE: return zend_string_init("float", sizeof("float") - 1, 0); + case IS_VOID: return zend_string_init("void", sizeof("void") - 1, 0); + case IS_ITERABLE: return zend_string_init("iterable", sizeof("iterable") - 1, 0); EMPTY_SWITCH_DEFAULT_CASE() } } diff --cc ext/reflection/tests/ReflectionType_001.phpt index 158ecb6,d949e18..342306b --- a/ext/reflection/tests/ReflectionType_001.phpt +++ b/ext/reflection/tests/ReflectionType_001.phpt @@@ -74,28 -74,7 +74,29 @@@ foreach ( } } +echo "\n*** property types\n"; + +class PropTypeTest { + public int $int; + public string $string; + public array $arr; + public callable $callable; + public stdClass $std; + public OtherThing $other; + public $mixed; +} + +$reflector = new ReflectionClass(PropTypeTest::class); + +foreach ($reflector->getProperties() as $name => $property) { + if ($property->hasType()) { + printf("public %s $%s;\n", + $property->getType(), $property->getName()); + } else printf("public $%s;\n", $property->getName()); +} + +echo "** property types\n"; + ?> --EXPECT-- *** functions ** Function 0 - Parameter 0 diff --cc ext/standard/var_unserializer.c index edb122a,4cf41e6..3fd97f0 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@@ -1,4 -1,4 +1,4 @@@ - /* Generated by re2c 0.13.5 */ -/* Generated by re2c 0.15.3 */ ++/* Generated by re2c 0.14.3 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@@ -23,6 -23,50 +23,51 @@@ #include "php.h" #include "ext/standard/php_var.h" #include "php_incomplete_class.h" + #include "zend_portability.h" + + struct php_unserialize_data { + void *first; + void *last; + void *first_dtor; + void *last_dtor; + HashTable *allowed_classes; ++ HashTable *refs; + }; + + PHPAPI php_unserialize_data_t php_var_unserialize_init() { + php_unserialize_data_t d; + /* fprintf(stderr, "UNSERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ + if (BG(serialize_lock) || !BG(unserialize).level) { + d = ecalloc(1, sizeof(struct php_unserialize_data)); + if (!BG(serialize_lock)) { + BG(unserialize).data = d; + BG(unserialize).level = 1; + } + } else { + d = BG(unserialize).data; + ++BG(unserialize).level; + } + return d; + } + + PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) { + /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ + if (BG(serialize_lock) || BG(unserialize).level == 1) { + var_destroy(&d); + efree(d); + } + if (!BG(serialize_lock) && !--BG(unserialize).level) { + BG(unserialize).data = NULL; + } + } + + PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d) { + return d->allowed_classes; + } + PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { + d->allowed_classes = classes; + } + /* {{{ reference-handling for unserializer: var_* */ #define VAR_ENTRIES_MAX 1024 @@@ -242,7 -284,7 +289,7 @@@ static inline int unserialize_allowed_c #define YYMARKER marker - #line 250 "ext/standard/var_unserializer.re" -#line 292 "ext/standard/var_unserializer.re" ++#line 297 "ext/standard/var_unserializer.re" @@@ -302,15 -344,16 +349,17 @@@ static inline size_t parse_uiv(const un return result; } - #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes - #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes + #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash + #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash + + static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER); -static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) +static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_class_entry *ce) { while (elements-- > 0) { - zval key, *data, d, *old_data; + zval key, *data, d, *old_data, tmp; zend_ulong idx; + zend_property_info *info = NULL; ZVAL_UNDEF(&key); @@@ -505,9 -509,16 +555,16 @@@ static inline int object_common2(UNSERI return 0; } + has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY + && zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1); + ht = Z_OBJPROP_P(rval); zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED)); - if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) { + if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJCE_P(rval))) { + if (has_wakeup) { + ZVAL_DEREF(rval); + GC_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED; + } return 0; } @@@ -559,7 -590,7 +636,7 @@@ static int php_var_unserialize_internal start = cursor; - #line 563 "ext/standard/var_unserializer.c" -#line 594 "ext/standard/var_unserializer.c" ++#line 640 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@@ -619,9 -649,9 +696,9 @@@ yy2 yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: - #line 929 "ext/standard/var_unserializer.re" -#line 960 "ext/standard/var_unserializer.re" ++#line 1006 "ext/standard/var_unserializer.re" { return 0; } - #line 625 "ext/standard/var_unserializer.c" -#line 655 "ext/standard/var_unserializer.c" ++#line 702 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@@ -664,13 -694,13 +741,13 @@@ yy13 goto yy3; yy14: ++YYCURSOR; - #line 923 "ext/standard/var_unserializer.re" -#line 954 "ext/standard/var_unserializer.re" ++#line 1000 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } - #line 674 "ext/standard/var_unserializer.c" -#line 704 "ext/standard/var_unserializer.c" ++#line 751 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@@ -700,7 -731,7 +778,7 @@@ yy20 yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; - #line 778 "ext/standard/var_unserializer.re" -#line 809 "ext/standard/var_unserializer.re" ++#line 855 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; zend_long elements; @@@ -845,7 -876,7 +923,7 @@@ return object_common2(UNSERIALIZE_PASSTHRU, elements); } - #line 849 "ext/standard/var_unserializer.c" -#line 880 "ext/standard/var_unserializer.c" ++#line 927 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@@ -870,14 -901,14 +948,14 @@@ yy27 yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; - #line 771 "ext/standard/var_unserializer.re" -#line 802 "ext/standard/var_unserializer.re" ++#line 848 "ext/standard/var_unserializer.re" { if (!var_hash) return 0; return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } - #line 881 "ext/standard/var_unserializer.c" -#line 912 "ext/standard/var_unserializer.c" ++#line 959 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@@ -898,7 -929,7 +976,7 @@@ yy34 yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; - #line 747 "ext/standard/var_unserializer.re" -#line 778 "ext/standard/var_unserializer.re" ++#line 824 "ext/standard/var_unserializer.re" { zend_long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@@ -922,7 -953,7 +1000,7 @@@ return finish_nested_data(UNSERIALIZE_PASSTHRU); } - #line 926 "ext/standard/var_unserializer.c" -#line 957 "ext/standard/var_unserializer.c" ++#line 1004 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@@ -943,7 -974,7 +1021,7 @@@ yy41 yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; - #line 713 "ext/standard/var_unserializer.re" -#line 744 "ext/standard/var_unserializer.re" ++#line 790 "ext/standard/var_unserializer.re" { size_t len, maxlen; zend_string *str; @@@ -977,7 -1008,7 +1055,7 @@@ ZVAL_STR(rval, str); return 1; } - #line 981 "ext/standard/var_unserializer.c" -#line 1012 "ext/standard/var_unserializer.c" ++#line 1059 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@@ -998,7 -1029,7 +1076,7 @@@ yy48 yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; - #line 681 "ext/standard/var_unserializer.re" -#line 712 "ext/standard/var_unserializer.re" ++#line 758 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@@ -1030,7 -1061,7 +1108,7 @@@ ZVAL_STRINGL(rval, str, len); return 1; } - #line 1034 "ext/standard/var_unserializer.c" -#line 1065 "ext/standard/var_unserializer.c" ++#line 1112 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@@ -1118,7 -1149,7 +1196,7 @@@ yy61 } yy63: ++YYCURSOR; - #line 672 "ext/standard/var_unserializer.re" -#line 703 "ext/standard/var_unserializer.re" ++#line 749 "ext/standard/var_unserializer.re" { #if SIZEOF_ZEND_LONG == 4 use_double: @@@ -1127,7 -1158,7 +1205,7 @@@ ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL)); return 1; } - #line 1131 "ext/standard/var_unserializer.c" -#line 1162 "ext/standard/var_unserializer.c" ++#line 1209 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@@ -1186,7 -1217,7 +1264,7 @@@ yy73 yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; - #line 656 "ext/standard/var_unserializer.re" -#line 687 "ext/standard/var_unserializer.re" ++#line 733 "ext/standard/var_unserializer.re" { *p = YYCURSOR; @@@ -1202,7 -1233,7 +1280,7 @@@ return 1; } - #line 1206 "ext/standard/var_unserializer.c" -#line 1237 "ext/standard/var_unserializer.c" ++#line 1284 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@@ -1229,7 -1260,7 +1307,7 @@@ yy79 if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; - #line 630 "ext/standard/var_unserializer.re" -#line 661 "ext/standard/var_unserializer.re" ++#line 707 "ext/standard/var_unserializer.re" { #if SIZEOF_ZEND_LONG == 4 int digits = YYCURSOR - start - 3; @@@ -1255,7 -1286,7 +1333,7 @@@ ZVAL_LONG(rval, parse_iv(start + 2)); return 1; } - #line 1259 "ext/standard/var_unserializer.c" -#line 1290 "ext/standard/var_unserializer.c" ++#line 1337 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@@ -1263,22 -1294,22 +1341,22 @@@ yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; - #line 624 "ext/standard/var_unserializer.re" -#line 655 "ext/standard/var_unserializer.re" ++#line 701 "ext/standard/var_unserializer.re" { *p = YYCURSOR; ZVAL_BOOL(rval, parse_iv(start + 2)); return 1; } - #line 1273 "ext/standard/var_unserializer.c" -#line 1304 "ext/standard/var_unserializer.c" ++#line 1351 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; - #line 618 "ext/standard/var_unserializer.re" -#line 649 "ext/standard/var_unserializer.re" ++#line 695 "ext/standard/var_unserializer.re" { *p = YYCURSOR; ZVAL_NULL(rval); return 1; } - #line 1282 "ext/standard/var_unserializer.c" -#line 1313 "ext/standard/var_unserializer.c" ++#line 1360 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@@ -1301,7 -1332,7 +1379,7 @@@ yy91 if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; - #line 593 "ext/standard/var_unserializer.re" -#line 624 "ext/standard/var_unserializer.re" ++#line 670 "ext/standard/var_unserializer.re" { zend_long id; @@@ -1326,7 -1357,7 +1404,7 @@@ return 1; } - #line 1330 "ext/standard/var_unserializer.c" -#line 1361 "ext/standard/var_unserializer.c" ++#line 1408 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@@ -1349,7 -1380,7 +1427,7 @@@ yy97 if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; - #line 567 "ext/standard/var_unserializer.re" -#line 598 "ext/standard/var_unserializer.re" ++#line 644 "ext/standard/var_unserializer.re" { zend_long id; @@@ -1375,9 -1406,9 +1453,9 @@@ return 1; } - #line 1379 "ext/standard/var_unserializer.c" -#line 1410 "ext/standard/var_unserializer.c" ++#line 1457 "ext/standard/var_unserializer.c" } - #line 931 "ext/standard/var_unserializer.re" -#line 962 "ext/standard/var_unserializer.re" ++#line 1008 "ext/standard/var_unserializer.re" return 0; diff --cc ext/standard/var_unserializer.re index d65ddb9,4b6266c..c0fcb5a --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@@ -21,6 -21,50 +21,51 @@@ #include "php.h" #include "ext/standard/php_var.h" #include "php_incomplete_class.h" + #include "zend_portability.h" + + struct php_unserialize_data { + void *first; + void *last; + void *first_dtor; + void *last_dtor; + HashTable *allowed_classes; ++ HashTable *refs; + }; + + PHPAPI php_unserialize_data_t php_var_unserialize_init() { + php_unserialize_data_t d; + /* fprintf(stderr, "UNSERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ + if (BG(serialize_lock) || !BG(unserialize).level) { + d = ecalloc(1, sizeof(struct php_unserialize_data)); + if (!BG(serialize_lock)) { + BG(unserialize).data = d; + BG(unserialize).level = 1; + } + } else { + d = BG(unserialize).data; + ++BG(unserialize).level; + } + return d; + } + + PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) { + /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ + if (BG(serialize_lock) || BG(unserialize).level == 1) { + var_destroy(&d); + efree(d); + } + if (!BG(serialize_lock) && !--BG(unserialize).level) { + BG(unserialize).data = NULL; + } + } + + PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d) { + return d->allowed_classes; + } + PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { + d->allowed_classes = classes; + } + /* {{{ reference-handling for unserializer: var_* */ #define VAR_ENTRIES_MAX 1024 @@@ -306,15 -348,16 +353,17 @@@ static inline size_t parse_uiv(const un return result; } - #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes - #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes + #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash + #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash + + static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER); -static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) +static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_class_entry *ce) { while (elements-- > 0) { - zval key, *data, d, *old_data; + zval key, *data, d, *old_data, tmp; zend_ulong idx; + zend_property_info *info = NULL; ZVAL_UNDEF(&key); @@@ -509,9 -513,16 +559,16 @@@ static inline int object_common2(UNSERI return 0; } + has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY + && zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1); + ht = Z_OBJPROP_P(rval); zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED)); - if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) { + if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJCE_P(rval))) { + if (has_wakeup) { + ZVAL_DEREF(rval); + GC_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED; + } return 0; }
diff --cc Zend/zend_compile.c index 70a65a4,14e9a9c..5d89538 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@@ -3024,8 -3051,21 +3060,21 @@@ void zend_compile_assign_ref(znode *res zend_ensure_writable_variable(target_ast); offset = zend_delayed_compile_begin(); - zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W); - zend_compile_var(&source_node, source_ast, BP_VAR_W); + zend_delayed_compile_var(&target_node, target_ast, BP_VAR_W, 1); - zend_delayed_compile_var(&source_node, source_ast, BP_VAR_W, 1); ++ zend_compile_var(&source_node, source_ast, BP_VAR_W, 1); + + if ((target_ast->kind != ZEND_AST_VAR + || target_ast->child[0]->kind != ZEND_AST_ZVAL) + && source_node.op_type != IS_CV) { + /* Both LHS and RHS expressions may modify the same data structure, + * and the modification during RHS evaluation may dangle the pointer + * to the result of the LHS evaluation. + * Use MAKE_REF instruction to replace direct pointer with REFERENCE. + * See: Bug #71539 + */ + zend_emit_op(&source_node, ZEND_MAKE_REF, &source_node, NULL); + } + zend_delayed_compile_end(offset); if (source_node.op_type != IS_VAR && zend_is_call(source_ast)) { diff --cc Zend/zend_execute.c index b10f969,1bc67fb..9f1d572 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@@ -731,14 -800,53 +810,53 @@@ static zend_bool zend_verify_scalar_typ /* NULL may be accepted only by nullable hints (this is already checked) */ return 0; } - return zend_verify_weak_scalar_type_hint(type_hint, arg); + return zend_verify_weak_scalar_type_hint(type_hint, arg, ret); } + static zend_always_inline zend_bool zend_check_internal_type( + const zend_function *zf, const zend_internal_arg_info *arg_info, + zval *arg, zend_class_entry **ce, zend_bool is_return_type) + { + if (!arg_info->type_hint) { + return 1; + } + + ZVAL_DEREF(arg); + if (EXPECTED(arg_info->type_hint == Z_TYPE_P(arg))) { + if (arg_info->class_name) { + *ce = zend_verify_internal_arg_class_kind(arg_info); + return *ce && instanceof_function(Z_OBJCE_P(arg), *ce); + } + return 1; + } + + if (Z_TYPE_P(arg) == IS_NULL && arg_info->allow_null) { + return 1; + } + + if (arg_info->class_name) { + *ce = zend_verify_internal_arg_class_kind(arg_info); + return 0; + } else if (arg_info->type_hint == IS_CALLABLE) { + return zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL); + } else if (arg_info->type_hint == IS_ITERABLE) { + return zend_is_iterable(arg); + } else if (arg_info->type_hint == _IS_BOOL && + EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { + return 1; + } else if (is_return_type) { + /* Internal return types are always strict */ + return 0; + } else { + /* Internal parameter types honor strict_types */ - return zend_verify_scalar_type_hint(arg_info->type_hint, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))); ++ return zend_verify_scalar_type_hint(arg_info->type_hint, arg, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))); + } + } + static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg) { - zend_internal_arg_info *cur_arg_info; - char *need_msg, *class_name; - zend_class_entry *ce; + const zend_internal_arg_info *cur_arg_info; + zend_class_entry *ce = NULL; if (EXPECTED(arg_num <= zf->internal_function.num_args)) { cur_arg_info = &zf->internal_function.arg_info[arg_num-1]; @@@ -947,14 -906,44 +996,44 @@@ static zend_always_inline zend_bool zen return 0; } } + return 1; } - return 1; + + if (Z_TYPE_P(arg) == IS_NULL && (arg_info->allow_null || (default_value && is_null_constant(zf->common.scope, default_value)))) { + /* Null passed to nullable type */ + return 1; + } + + if (UNEXPECTED(arg_info->class_name)) { + /* This is always an error - we fetch the class name for the error message here */ + if (EXPECTED(*cache_slot)) { + *ce = (zend_class_entry *) *cache_slot; + } else { + *ce = zend_verify_arg_class_kind(arg_info); + if (*ce) { + *cache_slot = (void *) *ce; + } + } + return 0; + } else if (arg_info->type_hint == IS_CALLABLE) { + return zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL); + } else if (arg_info->type_hint == IS_ITERABLE) { + return zend_is_iterable(arg); + } else if (arg_info->type_hint == _IS_BOOL && + EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { + return 1; + } else { - return zend_verify_scalar_type_hint(arg_info->type_hint, arg, ++ return zend_verify_scalar_type_hint(arg_info->type_hint, arg, arg, + is_return_type ? ZEND_RET_USES_STRICT_TYPES() : ZEND_ARG_USES_STRICT_TYPES()); + } + + /* Special handling for IS_VOID is not necessary (for return types), + * because this case is already checked at compile-time. */ } - static zend_always_inline int zend_verify_missing_arg_type(zend_function *zf, uint32_t arg_num, void **cache_slot) + static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot) { zend_arg_info *cur_arg_info; - char *need_msg; zend_class_entry *ce; if (EXPECTED(arg_num <= zf->common.num_args)) { @@@ -2084,17 -1927,10 +2059,12 @@@ ZEND_API void zend_fetch_dimension_by_z } -static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type) +static zend_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 by_ref) { + zval *ptr; + if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { do { - if (container_op_type == IS_VAR && UNEXPECTED(Z_ISERROR_P(container))) { - ZVAL_ERROR(result); - return; - } - if (Z_ISREF_P(container)) { container = Z_REFVAL_P(container); if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) { @@@ -2159,12 -1980,13 +2131,13 @@@ return_indirect } } 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); + 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); + goto return_indirect; } else if (UNEXPECTED(Z_ISREF_P(ptr) && Z_REFCOUNT_P(ptr) == 1)) { ZVAL_UNREF(ptr); } @@@ -2173,15 -1995,10 +2146,10 @@@ ZVAL_ERROR(result); } } else { - ZVAL_INDIRECT(result, ptr); + goto return_indirect; } } else if (EXPECTED(Z_OBJ_HT_P(container)->read_property)) { - zval *ptr = Z_OBJ_HT_P(container)->read_property(container, prop_ptr, type, cache_slot, result); - if (ptr != result) { - goto return_indirect; - } else if (UNEXPECTED(Z_ISREF_P(ptr) && Z_REFCOUNT_P(ptr) == 1)) { - ZVAL_UNREF(ptr); - } + goto use_read_property; } else { zend_error(E_WARNING, "This object doesn't support property references"); ZVAL_ERROR(result); diff --cc Zend/zend_vm_execute.h index 7095bb9,4e5c0f9..3bd2b19 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@@ -3441,162 -3524,165 +3524,179 @@@ static ZEND_OPCODE_HANDLER_RET ZEND_FAS if (IS_CONST != IS_TMP_VAR) { Z_ADDREF_P(array_ptr); } - fe_ht = Z_OBJPROP_P(array_ptr); - pos = 0; - p = fe_ht->arData; - while (1) { - if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { - - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } - if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || - EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && - (UNEXPECTED(!p->key) || - EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { - break; + if (Z_OBJ_P(array_ptr)->properties + && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) { + if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) { + GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--; } - pos++; - p++; - } - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); - - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); - } else { - zend_class_entry *ce = Z_OBJCE_P(array_ptr); - zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); - zend_bool is_empty; - - if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { - - if (!EG(exception)) { - zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ZSTR_VAL(ce->name)); - } - zend_throw_exception_internal(NULL); - HANDLE_EXCEPTION(); - } - - iter->index = 0; - if (iter->funcs->rewind) { - iter->funcs->rewind(iter); - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - - HANDLE_EXCEPTION(); - } - } - - is_empty = iter->funcs->valid(iter) != SUCCESS; - - if (UNEXPECTED(EG(exception) != NULL)) { - OBJ_RELEASE(&iter->std); - - HANDLE_EXCEPTION(); - } - iter->index = -1; /* will be set to 0 before using next handler */ - - ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - - if (is_empty) { - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } else { - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); - } - } - } else { - zend_error(E_WARNING, "Invalid argument supplied for foreach()"); - ZVAL_UNDEF(EX_VAR(opline->result.var)); - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } - } - - static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) - { - USE_OPLINE - - zval *array_ptr, *array_ref; - HashTable *fe_ht; - HashPosition pos = 0; - Bucket *p; - - SAVE_OPLINE(); - - if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { - array_ref = array_ptr = NULL; - if (Z_ISREF_P(array_ref)) { - array_ptr = Z_REFVAL_P(array_ref); - } - } else { - array_ref = array_ptr = EX_CONSTANT(opline->op1); - } - - if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { - if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { - if (array_ptr == array_ref) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - Z_ADDREF_P(array_ref); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); - } else { - array_ref = EX_VAR(opline->result.var); - ZVAL_NEW_REF(array_ref, array_ptr); - array_ptr = Z_REFVAL_P(array_ref); - } - if (IS_CONST == IS_CONST) { - zval_copy_ctor_func(array_ptr); - } else { - SEPARATE_ARRAY(array_ptr); - } - fe_ht = Z_ARRVAL_P(array_ptr); - p = fe_ht->arData; - while (1) { - if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { - - Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; - ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); - } - if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && - (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || - EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) { - break; - } - pos++; - p++; - } - Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); - - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); - } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { - if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(Z_OBJCE_P(array_ptr)))) { - fe_reset_rw_typed: - zend_throw_exception_ex( - zend_ce_type_error, 0, - "Typed properties exist in %s: foreach by reference is disallowed", - ZSTR_VAL(Z_OBJCE_P(array_ptr)->name)); - if (IS_CONST == IS_VAR) { - - } else { - - } - HANDLE_EXCEPTION(); - } - - if (!Z_OBJCE_P(array_ptr)->get_iterator) { - if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { - if (array_ptr == array_ref) { - ZVAL_NEW_REF(array_ref, array_ref); - array_ptr = Z_REFVAL_P(array_ref); - } - Z_ADDREF_P(array_ref); - ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); - } else { - array_ptr = EX_VAR(opline->result.var); - ZVAL_COPY_VALUE(array_ptr, array_ref); + Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties); + } + fe_ht = Z_OBJPROP_P(array_ptr); + pos = 0; + p = fe_ht->arData; + while (1) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + if ((EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && + (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || + EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) && + (UNEXPECTED(!p->key) || + EXPECTED(zend_check_property_access(Z_OBJ_P(array_ptr), p->key) == SUCCESS))) { + break; + } + pos++; + p++; + } + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } else { + zend_class_entry *ce = Z_OBJCE_P(array_ptr); + zend_object_iterator *iter = ce->get_iterator(ce, array_ptr, 0); + zend_bool is_empty; + + if (UNEXPECTED(!iter) || UNEXPECTED(EG(exception))) { + + if (iter) { + OBJ_RELEASE(&iter->std); + } + if (!EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Object of type %s did not create an Iterator", ZSTR_VAL(ce->name)); + } + zend_throw_exception_internal(NULL); + HANDLE_EXCEPTION(); + } + + iter->index = 0; + if (iter->funcs->rewind) { + iter->funcs->rewind(iter); + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + + HANDLE_EXCEPTION(); + } + } + + is_empty = iter->funcs->valid(iter) != SUCCESS; + + if (UNEXPECTED(EG(exception) != NULL)) { + OBJ_RELEASE(&iter->std); + + HANDLE_EXCEPTION(); + } + iter->index = -1; /* will be set to 0 before using next handler */ + + ZVAL_OBJ(EX_VAR(opline->result.var), &iter->std); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + if (is_empty) { + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } else { + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } + } + } else { + zend_error(E_WARNING, "Invalid argument supplied for foreach()"); + ZVAL_UNDEF(EX_VAR(opline->result.var)); + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + } + + static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) + { + USE_OPLINE + + zval *array_ptr, *array_ref; + HashTable *fe_ht; + HashPosition pos = 0; + Bucket *p; + + SAVE_OPLINE(); + + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + array_ref = array_ptr = NULL; + if (Z_ISREF_P(array_ref)) { + array_ptr = Z_REFVAL_P(array_ref); + } + } else { + array_ref = array_ptr = EX_CONSTANT(opline->op1); + } + + if (EXPECTED(Z_TYPE_P(array_ptr) == IS_ARRAY)) { + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ref = EX_VAR(opline->result.var); + ZVAL_NEW_REF(array_ref, array_ptr); + array_ptr = Z_REFVAL_P(array_ref); + } + if (IS_CONST == IS_CONST) { + zval_copy_ctor_func(array_ptr); + } else { + SEPARATE_ARRAY(array_ptr); + } + fe_ht = Z_ARRVAL_P(array_ptr); + p = fe_ht->arData; + while (1) { + if (UNEXPECTED(pos >= fe_ht->nNumUsed)) { + + Z_FE_ITER_P(EX_VAR(opline->result.var)) = (uint32_t)-1; + ZEND_VM_JMP(OP_JMP_ADDR(opline, opline->op2)); + } + if (EXPECTED(Z_TYPE(p->val) != IS_UNDEF) && + (EXPECTED(Z_TYPE(p->val) != IS_INDIRECT) || + EXPECTED(Z_TYPE_P(Z_INDIRECT(p->val)) != IS_UNDEF))) { + break; + } + pos++; + p++; + } + Z_FE_ITER_P(EX_VAR(opline->result.var)) = zend_hash_iterator_add(fe_ht, pos); + + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + } else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) { ++ if (UNEXPECTED(ZEND_CLASS_HAS_TYPE_HINTS(Z_OBJCE_P(array_ptr)))) { ++fe_reset_rw_typed: ++ zend_throw_exception_ex( ++ zend_ce_type_error, 0, ++ "Typed properties exist in %s: foreach by reference is disallowed", ++ ZSTR_VAL(Z_OBJCE_P(array_ptr)->name)); ++ if (IS_CONST == IS_VAR) { ++ ++ } else { ++ ++ } ++ HANDLE_EXCEPTION(); ++ } ++ + if (!Z_OBJCE_P(array_ptr)->get_iterator) { + if (IS_CONST == IS_VAR || IS_CONST == IS_CV) { + if (array_ptr == array_ref) { + ZVAL_NEW_REF(array_ref, array_ref); + array_ptr = Z_REFVAL_P(array_ref); + } + Z_ADDREF_P(array_ref); + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), array_ref); + } else { + array_ptr = EX_VAR(opline->result.var); + ZVAL_COPY_VALUE(array_ptr, array_ref); + } + if (Z_OBJ_P(array_ptr)->properties + && UNEXPECTED(GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties) > 1)) { + if (EXPECTED(!(GC_FLAGS(Z_OBJ_P(array_ptr)->properties) & IS_ARRAY_IMMUTABLE))) { + GC_REFCOUNT(Z_OBJ_P(array_ptr)->properties)--; + } + Z_OBJ_P(array_ptr)->properties = zend_array_dup(Z_OBJ_P(array_ptr)->properties); } fe_ht = Z_OBJPROP_P(array_ptr); p = fe_ht->arData; diff --cc Zend/zend_vm_gen.php index 14718ed,d379599..0d21e33 --- a/Zend/zend_vm_gen.php +++ b/Zend/zend_vm_gen.php @@@ -128,6 -127,6 +128,7 @@@ $vm_ext_decode = array "ISSET" => ZEND_VM_EXT_ISSET, "ARG_NUM" => ZEND_VM_EXT_ARG_NUM, "REF" => ZEND_VM_EXT_REF, ++ "FETCH_REF" => ZEND_VM_EXT_FETCH_REF, "SRC" => ZEND_VM_EXT_SRC, ); diff --cc ext/opcache/Optimizer/zend_optimizer.c index c370dc5,82a6daa..f603957 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@@ -189,8 -189,17 +189,17 @@@ int zend_optimizer_update_op1_const(zen case ZEND_FETCH_STATIC_PROP_FUNC_ARG: TO_STRING_NOWARN(val); opline->op1.constant = zend_optimizer_add_literal(op_array, val); - alloc_cache_slots_op1(op_array, opline, 2); + alloc_cache_slots_op1(op_array, opline, 3); break; + case ZEND_SEND_VAR: + opline->opcode = ZEND_SEND_VAL; + opline->op1.constant = zend_optimizer_add_literal(op_array, val); + break; + case ZEND_SEPARATE: + case ZEND_SEND_VAR_NO_REF: + case ZEND_SEND_VAR_NO_REF_EX: + zval_ptr_dtor(val); + return 0; case ZEND_CONCAT: case ZEND_FAST_CONCAT: case ZEND_FETCH_R: diff --cc ext/reflection/php_reflection.c index 6fc340f,1169bb6..9ea6148 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@@ -1281,12 -1277,11 +1291,12 @@@ static void reflection_arg_type_factory reflection_object *intern; type_reference *reference; - reflection_instantiate(reflection_type_ptr, object); + reflection_instantiate(reflection_named_type_ptr, object); intern = Z_REFLECTION_P(object); reference = (type_reference*) emalloc(sizeof(type_reference)); - reference->arg_info = arg_info; - reference->fptr = fptr; + reference->type = REF_INFO_ARG; + reference->info.arg = arg_info; + reference->scope.fptr = fptr; intern->ptr = reference; intern->ref_type = REF_TYPE_TYPE; intern->ce = fptr->common.scope; @@@ -3044,51 -3000,23 +3053,42 @@@ ZEND_METHOD(reflection_type, isBuiltin } /* }}} */ - /* {{{ proto public string ReflectionType::__toString() - Return the text of the type hint */ - ZEND_METHOD(reflection_type, __toString) - { - reflection_object *intern; - type_reference *param; + /* {{{ reflection_type_name */ + static zend_string *reflection_type_name(type_reference *param) { - switch (param->arg_info->type_hint) { + zend_uchar type = IS_UNDEF; + - if (zend_parse_parameters_none() == FAILURE) { - return; - } - - GET_REFLECTION_OBJECT_PTR(param); - + switch (param->type) { + case REF_INFO_ARG: + type = param->info.arg->type_hint; + if (type == IS_OBJECT) { + zend_function *fptr = param->scope.fptr; + + if (fptr->type == ZEND_INTERNAL_FUNCTION && + !(fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { - RETURN_STRING(((zend_internal_arg_info*) param->info.arg)->class_name); ++ return zend_string_init(((zend_internal_arg_info*)param->info.arg)->class_name, strlen(((zend_internal_arg_info*)param->info.arg)->class_name), 0); + } else { - RETURN_STR_COPY(param->info.arg->class_name); ++ return zend_string_copy(param->info.arg->class_name); + } + } + break; + + case REF_INFO_PROP: + type = param->info.prop->type; + if (type == IS_OBJECT) { - RETURN_STR_COPY(param->info.prop->type_name); ++ return zend_string_copy(param->info.prop->type_name); + } + break; + } + + switch (type) { - case IS_ARRAY: RETURN_STRINGL("array", sizeof("array") - 1); - case IS_CALLABLE: RETURN_STRINGL("callable", sizeof("callable") - 1); - case IS_STRING: RETURN_STRINGL("string", sizeof("string") - 1); - case _IS_BOOL: RETURN_STRINGL("bool", sizeof("bool") - 1); - case IS_LONG: RETURN_STRINGL("int", sizeof("int") - 1); - case IS_DOUBLE: RETURN_STRINGL("float", sizeof("float") - 1); - case IS_VOID: RETURN_STRINGL("void", sizeof("void") - 1); + case IS_ARRAY: return zend_string_init("array", sizeof("array") - 1, 0); + case IS_CALLABLE: return zend_string_init("callable", sizeof("callable") - 1, 0); - case IS_OBJECT: - if (param->fptr->type == ZEND_INTERNAL_FUNCTION && - !(param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO)) { - return zend_string_init(((zend_internal_arg_info*)param->arg_info)->class_name, strlen(((zend_internal_arg_info*)param->arg_info)->class_name), 0); - } - return zend_string_copy(param->arg_info->class_name); + case IS_STRING: return zend_string_init("string", sizeof("string") - 1, 0); + case _IS_BOOL: return zend_string_init("bool", sizeof("bool") - 1, 0); + case IS_LONG: return zend_string_init("int", sizeof("int") - 1, 0); + case IS_DOUBLE: return zend_string_init("float", sizeof("float") - 1, 0); + case IS_VOID: return zend_string_init("void", sizeof("void") - 1, 0); + case IS_ITERABLE: return zend_string_init("iterable", sizeof("iterable") - 1, 0); EMPTY_SWITCH_DEFAULT_CASE() } } diff --cc ext/reflection/tests/ReflectionType_001.phpt index 158ecb6,d949e18..342306b --- a/ext/reflection/tests/ReflectionType_001.phpt +++ b/ext/reflection/tests/ReflectionType_001.phpt @@@ -74,28 -74,7 +74,29 @@@ foreach ( } } +echo "\n*** property types\n"; + +class PropTypeTest { + public int $int; + public string $string; + public array $arr; + public callable $callable; + public stdClass $std; + public OtherThing $other; + public $mixed; +} + +$reflector = new ReflectionClass(PropTypeTest::class); + +foreach ($reflector->getProperties() as $name => $property) { + if ($property->hasType()) { + printf("public %s $%s;\n", + $property->getType(), $property->getName()); + } else printf("public $%s;\n", $property->getName()); +} + +echo "** property types\n"; + ?> --EXPECT-- *** functions ** Function 0 - Parameter 0 diff --cc ext/standard/var_unserializer.c index edb122a,4cf41e6..3fd97f0 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@@ -1,4 -1,4 +1,4 @@@ - /* Generated by re2c 0.13.5 */ -/* Generated by re2c 0.15.3 */ ++/* Generated by re2c 0.14.3 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@@ -23,6 -23,50 +23,51 @@@ #include "php.h" #include "ext/standard/php_var.h" #include "php_incomplete_class.h" + #include "zend_portability.h" + + struct php_unserialize_data { + void *first; + void *last; + void *first_dtor; + void *last_dtor; + HashTable *allowed_classes; ++ HashTable *refs; + }; + + PHPAPI php_unserialize_data_t php_var_unserialize_init() { + php_unserialize_data_t d; + /* fprintf(stderr, "UNSERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ + if (BG(serialize_lock) || !BG(unserialize).level) { + d = ecalloc(1, sizeof(struct php_unserialize_data)); + if (!BG(serialize_lock)) { + BG(unserialize).data = d; + BG(unserialize).level = 1; + } + } else { + d = BG(unserialize).data; + ++BG(unserialize).level; + } + return d; + } + + PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) { + /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ + if (BG(serialize_lock) || BG(unserialize).level == 1) { + var_destroy(&d); + efree(d); + } + if (!BG(serialize_lock) && !--BG(unserialize).level) { + BG(unserialize).data = NULL; + } + } + + PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d) { + return d->allowed_classes; + } + PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { + d->allowed_classes = classes; + } + /* {{{ reference-handling for unserializer: var_* */ #define VAR_ENTRIES_MAX 1024 @@@ -242,7 -284,7 +289,7 @@@ static inline int unserialize_allowed_c #define YYMARKER marker - #line 250 "ext/standard/var_unserializer.re" -#line 292 "ext/standard/var_unserializer.re" ++#line 297 "ext/standard/var_unserializer.re" @@@ -302,15 -344,16 +349,17 @@@ static inline size_t parse_uiv(const un return result; } - #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes - #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes + #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash + #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash + + static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER); -static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) +static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_class_entry *ce) { while (elements-- > 0) { - zval key, *data, d, *old_data; + zval key, *data, d, *old_data, tmp; zend_ulong idx; + zend_property_info *info = NULL; ZVAL_UNDEF(&key); @@@ -505,9 -509,16 +555,16 @@@ static inline int object_common2(UNSERI return 0; } + has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY + && zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1); + ht = Z_OBJPROP_P(rval); zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED)); - if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) { + if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJCE_P(rval))) { + if (has_wakeup) { + ZVAL_DEREF(rval); + GC_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED; + } return 0; } @@@ -559,7 -590,7 +636,7 @@@ static int php_var_unserialize_internal start = cursor; - #line 563 "ext/standard/var_unserializer.c" -#line 594 "ext/standard/var_unserializer.c" ++#line 640 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@@ -619,9 -649,9 +696,9 @@@ yy2 yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: - #line 929 "ext/standard/var_unserializer.re" -#line 960 "ext/standard/var_unserializer.re" ++#line 1006 "ext/standard/var_unserializer.re" { return 0; } - #line 625 "ext/standard/var_unserializer.c" -#line 655 "ext/standard/var_unserializer.c" ++#line 702 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@@ -664,13 -694,13 +741,13 @@@ yy13 goto yy3; yy14: ++YYCURSOR; - #line 923 "ext/standard/var_unserializer.re" -#line 954 "ext/standard/var_unserializer.re" ++#line 1000 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } - #line 674 "ext/standard/var_unserializer.c" -#line 704 "ext/standard/var_unserializer.c" ++#line 751 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@@ -700,7 -731,7 +778,7 @@@ yy20 yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; - #line 778 "ext/standard/var_unserializer.re" -#line 809 "ext/standard/var_unserializer.re" ++#line 855 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; zend_long elements; @@@ -845,7 -876,7 +923,7 @@@ return object_common2(UNSERIALIZE_PASSTHRU, elements); } - #line 849 "ext/standard/var_unserializer.c" -#line 880 "ext/standard/var_unserializer.c" ++#line 927 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@@ -870,14 -901,14 +948,14 @@@ yy27 yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; - #line 771 "ext/standard/var_unserializer.re" -#line 802 "ext/standard/var_unserializer.re" ++#line 848 "ext/standard/var_unserializer.re" { if (!var_hash) return 0; return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } - #line 881 "ext/standard/var_unserializer.c" -#line 912 "ext/standard/var_unserializer.c" ++#line 959 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@@ -898,7 -929,7 +976,7 @@@ yy34 yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; - #line 747 "ext/standard/var_unserializer.re" -#line 778 "ext/standard/var_unserializer.re" ++#line 824 "ext/standard/var_unserializer.re" { zend_long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@@ -922,7 -953,7 +1000,7 @@@ return finish_nested_data(UNSERIALIZE_PASSTHRU); } - #line 926 "ext/standard/var_unserializer.c" -#line 957 "ext/standard/var_unserializer.c" ++#line 1004 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@@ -943,7 -974,7 +1021,7 @@@ yy41 yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; - #line 713 "ext/standard/var_unserializer.re" -#line 744 "ext/standard/var_unserializer.re" ++#line 790 "ext/standard/var_unserializer.re" { size_t len, maxlen; zend_string *str; @@@ -977,7 -1008,7 +1055,7 @@@ ZVAL_STR(rval, str); return 1; } - #line 981 "ext/standard/var_unserializer.c" -#line 1012 "ext/standard/var_unserializer.c" ++#line 1059 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@@ -998,7 -1029,7 +1076,7 @@@ yy48 yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; - #line 681 "ext/standard/var_unserializer.re" -#line 712 "ext/standard/var_unserializer.re" ++#line 758 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@@ -1030,7 -1061,7 +1108,7 @@@ ZVAL_STRINGL(rval, str, len); return 1; } - #line 1034 "ext/standard/var_unserializer.c" -#line 1065 "ext/standard/var_unserializer.c" ++#line 1112 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@@ -1118,7 -1149,7 +1196,7 @@@ yy61 } yy63: ++YYCURSOR; - #line 672 "ext/standard/var_unserializer.re" -#line 703 "ext/standard/var_unserializer.re" ++#line 749 "ext/standard/var_unserializer.re" { #if SIZEOF_ZEND_LONG == 4 use_double: @@@ -1127,7 -1158,7 +1205,7 @@@ ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL)); return 1; } - #line 1131 "ext/standard/var_unserializer.c" -#line 1162 "ext/standard/var_unserializer.c" ++#line 1209 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@@ -1186,7 -1217,7 +1264,7 @@@ yy73 yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; - #line 656 "ext/standard/var_unserializer.re" -#line 687 "ext/standard/var_unserializer.re" ++#line 733 "ext/standard/var_unserializer.re" { *p = YYCURSOR; @@@ -1202,7 -1233,7 +1280,7 @@@ return 1; } - #line 1206 "ext/standard/var_unserializer.c" -#line 1237 "ext/standard/var_unserializer.c" ++#line 1284 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@@ -1229,7 -1260,7 +1307,7 @@@ yy79 if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; - #line 630 "ext/standard/var_unserializer.re" -#line 661 "ext/standard/var_unserializer.re" ++#line 707 "ext/standard/var_unserializer.re" { #if SIZEOF_ZEND_LONG == 4 int digits = YYCURSOR - start - 3; @@@ -1255,7 -1286,7 +1333,7 @@@ ZVAL_LONG(rval, parse_iv(start + 2)); return 1; } - #line 1259 "ext/standard/var_unserializer.c" -#line 1290 "ext/standard/var_unserializer.c" ++#line 1337 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@@ -1263,22 -1294,22 +1341,22 @@@ yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; - #line 624 "ext/standard/var_unserializer.re" -#line 655 "ext/standard/var_unserializer.re" ++#line 701 "ext/standard/var_unserializer.re" { *p = YYCURSOR; ZVAL_BOOL(rval, parse_iv(start + 2)); return 1; } - #line 1273 "ext/standard/var_unserializer.c" -#line 1304 "ext/standard/var_unserializer.c" ++#line 1351 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; - #line 618 "ext/standard/var_unserializer.re" -#line 649 "ext/standard/var_unserializer.re" ++#line 695 "ext/standard/var_unserializer.re" { *p = YYCURSOR; ZVAL_NULL(rval); return 1; } - #line 1282 "ext/standard/var_unserializer.c" -#line 1313 "ext/standard/var_unserializer.c" ++#line 1360 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@@ -1301,7 -1332,7 +1379,7 @@@ yy91 if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; - #line 593 "ext/standard/var_unserializer.re" -#line 624 "ext/standard/var_unserializer.re" ++#line 670 "ext/standard/var_unserializer.re" { zend_long id; @@@ -1326,7 -1357,7 +1404,7 @@@ return 1; } - #line 1330 "ext/standard/var_unserializer.c" -#line 1361 "ext/standard/var_unserializer.c" ++#line 1408 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@@ -1349,7 -1380,7 +1427,7 @@@ yy97 if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; - #line 567 "ext/standard/var_unserializer.re" -#line 598 "ext/standard/var_unserializer.re" ++#line 644 "ext/standard/var_unserializer.re" { zend_long id; @@@ -1375,9 -1406,9 +1453,9 @@@ return 1; } - #line 1379 "ext/standard/var_unserializer.c" -#line 1410 "ext/standard/var_unserializer.c" ++#line 1457 "ext/standard/var_unserializer.c" } - #line 931 "ext/standard/var_unserializer.re" -#line 962 "ext/standard/var_unserializer.re" ++#line 1008 "ext/standard/var_unserializer.re" return 0; diff --cc ext/standard/var_unserializer.re index d65ddb9,4b6266c..c0fcb5a --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@@ -21,6 -21,50 +21,51 @@@ #include "php.h" #include "ext/standard/php_var.h" #include "php_incomplete_class.h" + #include "zend_portability.h" + + struct php_unserialize_data { + void *first; + void *last; + void *first_dtor; + void *last_dtor; + HashTable *allowed_classes; ++ HashTable *refs; + }; + + PHPAPI php_unserialize_data_t php_var_unserialize_init() { + php_unserialize_data_t d; + /* fprintf(stderr, "UNSERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ + if (BG(serialize_lock) || !BG(unserialize).level) { + d = ecalloc(1, sizeof(struct php_unserialize_data)); + if (!BG(serialize_lock)) { + BG(unserialize).data = d; + BG(unserialize).level = 1; + } + } else { + d = BG(unserialize).data; + ++BG(unserialize).level; + } + return d; + } + + PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) { + /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ + if (BG(serialize_lock) || BG(unserialize).level == 1) { + var_destroy(&d); + efree(d); + } + if (!BG(serialize_lock) && !--BG(unserialize).level) { + BG(unserialize).data = NULL; + } + } + + PHPAPI HashTable *php_var_unserialize_get_allowed_classes(php_unserialize_data_t d) { + return d->allowed_classes; + } + PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { + d->allowed_classes = classes; + } + /* {{{ reference-handling for unserializer: var_* */ #define VAR_ENTRIES_MAX 1024 @@@ -306,15 -348,16 +353,17 @@@ static inline size_t parse_uiv(const un return result; } - #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes - #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes + #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash + #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash + + static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER); -static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) +static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_class_entry *ce) { while (elements-- > 0) { - zval key, *data, d, *old_data; + zval key, *data, d, *old_data, tmp; zend_ulong idx; + zend_property_info *info = NULL; ZVAL_UNDEF(&key); @@@ -509,9 -513,16 +559,16 @@@ static inline int object_common2(UNSERI return 0; } + has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY + && zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1); + ht = Z_OBJPROP_P(rval); zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED)); - if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) { + if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJCE_P(rval))) { + if (has_wakeup) { + ZVAL_DEREF(rval); + GC_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED; + } return 0; }