From b3b616cf7ee0702aca2f6fdb022dc7e28e015a22 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 29 May 2014 18:21:56 +0400 Subject: [PATCH] Introduced immutable arrays. They don't need to be copyed and may be used directly from SHM. --- Zend/zend.c | 14 +- Zend/zend.h | 27 ++-- Zend/zend_compile.c | 162 +++++++++++++++++++- Zend/zend_compile.h | 1 + Zend/zend_constants.c | 4 +- Zend/zend_execute.c | 4 +- Zend/zend_hash.c | 2 +- Zend/zend_hash.h | 9 +- Zend/zend_types.h | 7 + Zend/zend_variables.h | 8 +- Zend/zend_vm_def.h | 82 ++++++++-- Zend/zend_vm_execute.h | 175 ++++++++++++++++++---- ext/json/json.c | 4 +- ext/opcache/Optimizer/zend_optimizer.c | 3 + ext/opcache/ZendAccelerator.c | 1 - ext/opcache/zend_accelerator_util_funcs.c | 4 + ext/opcache/zend_persist.c | 39 ++++- ext/standard/array.c | 59 +++++--- ext/standard/http.c | 8 +- ext/standard/var.c | 22 ++- ext/wddx/wddx.c | 34 +++-- 21 files changed, 549 insertions(+), 120 deletions(-) diff --git a/Zend/zend.c b/Zend/zend.c index 297fe26267..841fc38565 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -326,14 +326,17 @@ ZEND_API void zend_print_flat_zval_r(zval *expr TSRMLS_DC) /* {{{ */ switch (Z_TYPE_P(expr)) { case IS_ARRAY: ZEND_PUTS("Array ("); - if (++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) { + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr)) && + ++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) { ZEND_PUTS(" *RECURSION*"); Z_ARRVAL_P(expr)->u.v.nApplyCount--; return; } print_flat_hash(Z_ARRVAL_P(expr) TSRMLS_CC); ZEND_PUTS(")"); - Z_ARRVAL_P(expr)->u.v.nApplyCount--; + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr))) { + Z_ARRVAL_P(expr)->u.v.nApplyCount--; + } break; case IS_OBJECT: { @@ -385,13 +388,16 @@ ZEND_API void zend_print_zval_r_ex(zend_write_func_t write_func, zval *expr, int switch (Z_TYPE_P(expr)) { case IS_ARRAY: ZEND_PUTS_EX("Array\n"); - if (++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) { + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr)) && + ++Z_ARRVAL_P(expr)->u.v.nApplyCount>1) { ZEND_PUTS_EX(" *RECURSION*"); Z_ARRVAL_P(expr)->u.v.nApplyCount--; return; } print_hash(write_func, Z_ARRVAL_P(expr), indent, 0 TSRMLS_CC); - Z_ARRVAL_P(expr)->u.v.nApplyCount--; + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(expr))) { + Z_ARRVAL_P(expr)->u.v.nApplyCount--; + } break; case IS_OBJECT: { diff --git a/Zend/zend.h b/Zend/zend.h index 1968d76c22..1a4d301f45 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -737,17 +737,22 @@ END_EXTERN_C() zval_copy_ctor_func(_zv); \ } \ } \ + } else if (Z_IMMUTABLE_P(_zv)) { \ + zval_copy_ctor_func(_zv); \ } \ } while (0) #define SEPARATE_ZVAL_IF_NOT_REF(zv) do { \ zval *_zv = (zv); \ - if (!Z_ISREF_P(_zv) && \ - Z_COPYABLE_P(_zv) && \ - Z_REFCOUNT_P(_zv) > 1) { \ - Z_DELREF_P(_zv); \ - zval_copy_ctor_func(_zv); \ - } \ + if (!Z_ISREF_P(_zv)) { \ + if (Z_COPYABLE_P(_zv) && \ + Z_REFCOUNT_P(_zv) > 1) { \ + Z_DELREF_P(_zv); \ + zval_copy_ctor_func(_zv); \ + } else if (Z_IMMUTABLE_P(_zv)) { \ + zval_copy_ctor_func(_zv); \ + } \ + } \ } while (0) #define SEPARATE_ZVAL_IF_REF(zv) do { \ @@ -765,14 +770,12 @@ END_EXTERN_C() #define SEPARATE_ZVAL_TO_MAKE_IS_REF(zv) do { \ zval *__zv = (zv); \ if (!Z_ISREF_P(__zv)) { \ - if (!Z_COPYABLE_P(__zv) || \ - Z_REFCOUNT_P(__zv) == 1) { \ - ZVAL_NEW_REF(__zv, __zv); \ - } else { \ + if (Z_COPYABLE_P(__zv) && \ + Z_REFCOUNT_P(__zv) > 1) { \ Z_DELREF_P(__zv); \ - ZVAL_NEW_REF(__zv, __zv); \ - zval_copy_ctor_func(Z_REFVAL_P(__zv)); \ + zval_copy_ctor_func(__zv); \ } \ + ZVAL_NEW_REF(__zv, __zv); \ } \ } while (0) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 23c38b889e..e1a4da0f73 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5922,8 +5922,128 @@ void zend_do_add_array_element(znode *result, znode *expr, znode *offset, zend_b void zend_do_end_array(znode *result, const znode *array_node TSRMLS_DC) /* {{{ */ { + int next_op_num = get_next_op_number(CG(active_op_array)); zend_op *init_opline = &CG(active_op_array)->opcodes[array_node->u.op.opline_num]; - GET_NODE(result, init_opline->result); + zend_op *opline; + int i; + int constant_array = 0; + zval array; + zend_constant *c; + + /* check if constructed array consists only from constants */ + if ((init_opline->op1_type & (IS_UNUSED | IS_CONST)) && + (init_opline->op2_type & (IS_UNUSED | IS_CONST))) { + if (next_op_num == array_node->u.op.opline_num + 1) { + constant_array = 1; + } else if ((init_opline->extended_value >> ZEND_ARRAY_SIZE_SHIFT) == next_op_num - array_node->u.op.opline_num) { + opline = init_opline + 1; + i = next_op_num - array_node->u.op.opline_num - 1; + while (i > 0) { + if (opline->opcode != ZEND_ADD_ARRAY_ELEMENT || + opline->op1_type != IS_CONST || + !(opline->op2_type & (IS_UNUSED | IS_CONST))) { + break; + } + opline++; + i--; + } + if (i == 0) { + constant_array = 1; + } + } + } + + if (constant_array) { + /* try to construct constant array */ + zend_uint size; + long num; + zend_string *str; + + if (init_opline->op1_type != IS_UNUSED) { + size = init_opline->extended_value >> ZEND_ARRAY_SIZE_SHIFT; + } else { + size = 0; + } + ZVAL_NEW_ARR(&array); + zend_hash_init(Z_ARRVAL(array), size, NULL, ZVAL_PTR_DTOR, 0); + + if (init_opline->op1_type != IS_UNUSED) { + /* Explicitly initialize array as not-packed if flag is set */ + if (init_opline->extended_value & ZEND_ARRAY_NOT_PACKED) { + zend_hash_real_init(Z_ARRVAL(array), 0); + } + + opline = init_opline; + i = next_op_num - array_node->u.op.opline_num; + while (i > 0 && constant_array) { + if (opline->op2_type == IS_CONST) { + switch (Z_TYPE(CONSTANT(opline->op2.constant))) { + case IS_LONG: + num = Z_LVAL(CONSTANT(opline->op2.constant)); +num_index: + zend_hash_index_update(Z_ARRVAL(array), num, &CONSTANT(opline->op1.constant)); + if (Z_REFCOUNTED(CONSTANT(opline->op1.constant))) Z_ADDREF(CONSTANT(opline->op1.constant)); + break; + case IS_STRING: + str = Z_STR(CONSTANT(opline->op2.constant)); +str_index: + zend_hash_update(Z_ARRVAL(array), str, &CONSTANT(opline->op1.constant)); + if (Z_REFCOUNTED(CONSTANT(opline->op1.constant))) Z_ADDREF(CONSTANT(opline->op1.constant)); + break; + case IS_DOUBLE: + num = zend_dval_to_lval(Z_DVAL(CONSTANT(opline->op2.constant))); + goto num_index; + case IS_FALSE: + num = 0; + goto num_index; + case IS_TRUE: + num = 1; + goto num_index; + case IS_NULL: + str = STR_EMPTY_ALLOC(); + goto str_index; + default: + constant_array = 0; + break; + } + } else { + zend_hash_next_index_insert(Z_ARRVAL(array), &CONSTANT(opline->op1.constant)); + if (Z_REFCOUNTED(CONSTANT(opline->op1.constant))) Z_ADDREF(CONSTANT(opline->op1.constant)); + } + opline++; + i--; + } + if (!constant_array) { + zval_dtor(&array); + } + } + } + + if (constant_array) { + /* remove run-time array construction and use constant array instead */ + opline = &CG(active_op_array)->opcodes[next_op_num-1]; + while (opline != init_opline) { + if (opline->op2_type == IS_CONST) { + zend_del_literal(CG(active_op_array), opline->op2.constant); + } + zend_del_literal(CG(active_op_array), opline->op1.constant); + opline--; + } + if (opline->op2_type == IS_CONST) { + zend_del_literal(CG(active_op_array), opline->op2.constant); + } + if (opline->op1_type == IS_CONST) { + zend_del_literal(CG(active_op_array), opline->op1.constant); + } + CG(active_op_array)->last = array_node->u.op.opline_num; + + zend_make_immutable_array(&array TSRMLS_CC); + + result->op_type = IS_CONST; + ZVAL_COPY_VALUE(&result->u.constant, &array); + } else { + GET_NODE(result, init_opline->result); + } } /* }}} */ @@ -7368,6 +7488,43 @@ void zend_do_end_compilation(TSRMLS_D) /* {{{ */ } /* }}} */ +ZEND_API void zend_make_immutable_array(zval *zv TSRMLS_DC) /* {{{ */ +{ + zend_constant *c; + + if (Z_IMMUTABLE_P(zv)) { + return; + } + + Z_TYPE_FLAGS_P(zv) = IS_TYPE_IMMUTABLE; + Z_ARRVAL_P(zv)->u.flags &= ~HASH_FLAG_APPLY_PROTECTION; + + /* store as an anounimus constant */ + c = emalloc(sizeof(zend_constant)); + ZVAL_COPY_VALUE(&c->value, zv); + c->flags = 0; + c->name = NULL; + c->module_number = PHP_USER_CONSTANT; + zend_hash_next_index_insert_ptr(EG(zend_constants), c); +} +/* }}} */ + +void zend_make_immutable_array_r(zval *zv TSRMLS_DC) /* {{{ */ +{ + zval *el; + + if (Z_IMMUTABLE_P(zv)) { + return; + } + zend_make_immutable_array(zv TSRMLS_CC); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), el) { + if (Z_TYPE_P(el) == IS_ARRAY) { + zend_make_immutable_array_r(el TSRMLS_CC); + } + } ZEND_HASH_FOREACH_END(); +} +/* }}} */ + void zend_do_constant_expression(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ */ { if (ast->kind == ZEND_CONST) { @@ -7376,6 +7533,9 @@ void zend_do_constant_expression(znode *result, zend_ast *ast TSRMLS_DC) /* {{{ } else if (zend_ast_is_ct_constant(ast)) { zend_ast_evaluate(&result->u.constant, ast, NULL TSRMLS_CC); zend_ast_destroy(ast); + if (Z_TYPE(result->u.constant) == IS_ARRAY) { + zend_make_immutable_array_r(&result->u.constant TSRMLS_CC); + } } else { ZVAL_NEW_AST(&result->u.constant, ast); } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 1ff45ff6ce..5b5397844f 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -471,6 +471,7 @@ typedef int (*unary_op_type)(zval *, zval * TSRMLS_DC); typedef int (*binary_op_type)(zval *, zval *, zval * TSRMLS_DC); ZEND_API unary_op_type get_unary_op(int opcode); ZEND_API binary_op_type get_binary_op(int opcode); +ZEND_API void zend_make_immutable_array(zval *zv TSRMLS_DC); void zend_do_while_cond(znode *expr, znode *close_bracket_token TSRMLS_DC); void zend_do_while_end(const znode *while_token, const znode *close_bracket_token TSRMLS_DC); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 4f2cedb9af..4930d1fdd4 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -32,7 +32,9 @@ void free_zend_constant(zval *zv) zend_constant *c = Z_PTR_P(zv); if (!(c->flags & CONST_PERSISTENT)) { - zval_dtor(&c->value); + if (Z_REFCOUNTED(c->value) || Z_IMMUTABLE(c->value)) { + _zval_dtor_func(Z_COUNTED(c->value) ZEND_FILE_LINE_CC); + } } else { zval_internal_dtor(&c->value); } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index ae66364aa9..37539d6ffe 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1120,7 +1120,9 @@ static zend_always_inline void zend_fetch_dimension_address(zval *result, zval * ZVAL_DEREF(container); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { - if (container == container_ptr) { + if (Z_IMMUTABLE_P(container)) { + zval_copy_ctor(container); + } else if (container == container_ptr) { SEPARATE_ZVAL(container); } fetch_from_array: diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 5bf3e42b4e..08b4cb5e4e 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -1174,7 +1174,7 @@ ZEND_API void zend_array_dup(HashTable *target, HashTable *source) target->nTableSize = source->nTableSize; target->pDestructor = source->pDestructor; target->nInternalPointer = INVALID_IDX; - target->u.flags = (source->u.flags & ~HASH_FLAG_PERSISTENT); + target->u.flags = (source->u.flags & ~HASH_FLAG_PERSISTENT) | HASH_FLAG_APPLY_PROTECTION; target_idx = 0; if (target->nTableMask) { diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index c8f1e1c176..8660f8b6b7 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -669,10 +669,13 @@ static inline void *zend_hash_get_current_data_ptr_ex(HashTable *ht, HashPositio _key = _p->key; \ _val = _z; +#define ZEND_HASH_APPLY_PROTECTION(ht) \ + ((ht)->u.flags & HASH_FLAG_APPLY_PROTECTION) + #define ZEND_HASH_APPLY_SHIFT 8 -#define ZEND_HASH_GET_APPLY_COUNT(ht) (ht->u.flags >> ZEND_HASH_APPLY_SHIFT) -#define ZEND_HASH_INC_APPLY_COUNT(ht) (ht->u.flags += (1 << ZEND_HASH_APPLY_SHIFT)) -#define ZEND_HASH_DEC_APPLY_COUNT(ht) (ht->u.flags -= (1 << ZEND_HASH_APPLY_SHIFT)) +#define ZEND_HASH_GET_APPLY_COUNT(ht) ((ht)->u.flags >> ZEND_HASH_APPLY_SHIFT) +#define ZEND_HASH_INC_APPLY_COUNT(ht) ((ht)->u.flags += (1 << ZEND_HASH_APPLY_SHIFT)) +#define ZEND_HASH_DEC_APPLY_COUNT(ht) ((ht)->u.flags -= (1 << ZEND_HASH_APPLY_SHIFT)) #endif /* ZEND_HASH_H */ diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 1503c5a41a..232fcbadc1 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -282,6 +282,7 @@ static inline zend_uchar zval_get_type(const zval* pz) { #define IS_TYPE_REFCOUNTED (1<<1) #define IS_TYPE_COLLECTABLE (1<<2) #define IS_TYPE_COPYABLE (1<<3) +#define IS_TYPE_IMMUTABLE (1<<4) /* extended types */ #define IS_INTERNED_STRING_EX IS_STRING @@ -349,6 +350,9 @@ static inline zend_uchar zval_get_type(const zval* pz) { #define Z_COPYABLE(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_COPYABLE) != 0) #define Z_COPYABLE_P(zval_p) Z_COPYABLE(*(zval_p)) +#define Z_IMMUTABLE(zval) ((Z_TYPE_FLAGS(zval) & IS_TYPE_IMMUTABLE) != 0) +#define Z_IMMUTABLE_P(zval_p) Z_IMMUTABLE(*(zval_p)) + /* the following Z_OPT_* macros make better code when Z_TYPE_INFO accessed before */ #define Z_OPT_TYPE(zval) (Z_TYPE_INFO(zval) & 0xff) #define Z_OPT_TYPE_P(zval_p) Z_OPT_TYPE(*(zval_p)) @@ -365,6 +369,9 @@ static inline zend_uchar zval_get_type(const zval* pz) { #define Z_OPT_COPYABLE(zval) ((Z_TYPE_INFO(zval) & (IS_TYPE_COPYABLE << Z_TYPE_FLAGS_SHIFT)) != 0) #define Z_OPT_COPYABLE_P(zval_p) Z_OPT_COPYABLE(*(zval_p)) +#define Z_OPT_IMMUTABLE(zval) ((Z_TYPE_INFO(zval) & (IS_TYPE_IMMUTABLE << Z_TYPE_FLAGS_SHIFT)) != 0) +#define Z_OPT_IMMUTABLE_P(zval_p) Z_OPT_IMMUTABLE(*(zval_p)) + #define Z_ISREF(zval) (Z_TYPE(zval) == IS_REFERENCE) #define Z_ISREF_P(zval_p) Z_ISREF(*(zval_p)) diff --git a/Zend/zend_variables.h b/Zend/zend_variables.h index b85ef435ca..282725a325 100644 --- a/Zend/zend_variables.h +++ b/Zend/zend_variables.h @@ -42,8 +42,8 @@ ZEND_API void _zval_copy_ctor_func(zval *zvalue ZEND_FILE_LINE_DC); static zend_always_inline void _zval_copy_ctor(zval *zvalue ZEND_FILE_LINE_DC) { - if (Z_REFCOUNTED_P(zvalue)) { - if (Z_COPYABLE_P(zvalue)) { + if (Z_REFCOUNTED_P(zvalue) || Z_IMMUTABLE_P(zvalue)) { + if (Z_COPYABLE_P(zvalue) || Z_IMMUTABLE_P(zvalue)) { _zval_copy_ctor_func(zvalue ZEND_FILE_LINE_RELAY_CC); } else { Z_ADDREF_P(zvalue); @@ -53,8 +53,8 @@ static zend_always_inline void _zval_copy_ctor(zval *zvalue ZEND_FILE_LINE_DC) static zend_always_inline void _zval_opt_copy_ctor(zval *zvalue ZEND_FILE_LINE_DC) { - if (Z_OPT_REFCOUNTED_P(zvalue)) { - if (Z_OPT_COPYABLE_P(zvalue)) { + if (Z_OPT_REFCOUNTED_P(zvalue) || Z_OPT_IMMUTABLE_P(zvalue)) { + if (Z_OPT_COPYABLE_P(zvalue) || Z_OPT_IMMUTABLE_P(zvalue)) { _zval_copy_ctor_func(zvalue ZEND_FILE_LINE_RELAY_CC); } else { Z_ADDREF_P(zvalue); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index c27570c9df..0b5dde1599 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3003,7 +3003,11 @@ ZEND_VM_HANDLER(65, ZEND_SEND_VAL, CONST|TMP, ANY) top = zend_vm_stack_top_inc(TSRMLS_C); ZVAL_COPY_VALUE(top, value); if (OP1_TYPE == IS_CONST) { - zval_opt_copy_ctor(top); + /* Immutable arrays may be passed without copying ??? */ + /* some internal functions may try to modify them !!! */ + if (!Z_OPT_IMMUTABLE_P(top)) { + zval_opt_copy_ctor(top); + } } ZEND_VM_NEXT_OPCODE(); } @@ -3017,7 +3021,12 @@ ZEND_VM_HELPER(zend_send_by_var_helper, VAR|CV, ANY) varptr = GET_OP1_ZVAL_PTR(BP_VAR_R); top = zend_vm_stack_top_inc(TSRMLS_C); if (Z_ISREF_P(varptr)) { - ZVAL_DUP(top, Z_REFVAL_P(varptr)); + ZVAL_COPY_VALUE(top, Z_REFVAL_P(varptr)); + /* Immutable arrays may be passed without copying ??? */ + /* some internal functions may try to modify them !!! */ + if (!Z_OPT_IMMUTABLE_P(top)) { + zval_opt_copy_ctor(top); + } FREE_OP1(); } else { ZVAL_COPY_VALUE(top, varptr); @@ -3056,6 +3065,10 @@ ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR|CV, ANY) if (!Z_ISREF_P(varptr)) { ZVAL_NEW_REF(varptr, varptr); } + // TODO: Try to avoid copying of immutable arrays ??? + if (Z_OPT_IMMUTABLE_P(Z_REFVAL_P(varptr))) { + zval_opt_copy_ctor(Z_REFVAL_P(varptr)); + } if (OP1_TYPE == IS_CV) { Z_ADDREF_P(varptr); } @@ -3067,8 +3080,8 @@ ZEND_VM_HANDLER(106, ZEND_SEND_VAR_NO_REF, VAR|CV, ANY) zend_error(E_STRICT, "Only variables should be passed by reference"); } top = zend_vm_stack_top_inc(TSRMLS_C); - ZVAL_COPY_VALUE(top, varptr); - zval_opt_copy_ctor(top); + // TODO: Try to avoid copying of immutable arrays ??? + ZVAL_DUP(top, varptr); FREE_OP1_IF_VAR(); } CHECK_EXCEPTION(); @@ -3095,6 +3108,10 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY) } if (Z_ISREF_P(varptr)) { + // TODO: Try to avoid copying of immutable arrays ??? + if (Z_OPT_IMMUTABLE_P(Z_REFVAL_P(varptr))) { + zval_opt_copy_ctor(Z_REFVAL_P(varptr)); + } Z_ADDREF_P(varptr); ZVAL_COPY_VALUE(top, varptr); } else if (OP1_TYPE == IS_VAR && @@ -3102,6 +3119,10 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY) ZVAL_COPY_VALUE(top, varptr); SEPARATE_ZVAL_TO_MAKE_IS_REF(top); } else { + // TODO: Try to avoid copying of immutable arrays ??? + if (Z_OPT_IMMUTABLE_P(varptr)) { + zval_opt_copy_ctor(varptr); + } SEPARATE_ZVAL_TO_MAKE_IS_REF(varptr); Z_ADDREF_P(varptr); ZVAL_COPY_VALUE(top, varptr); @@ -3126,7 +3147,12 @@ ZEND_VM_HANDLER(66, ZEND_SEND_VAR, VAR|CV, ANY) varptr = GET_OP1_ZVAL_PTR(BP_VAR_R); top = zend_vm_stack_top_inc(TSRMLS_C); if (Z_ISREF_P(varptr)) { - ZVAL_DUP(top, Z_REFVAL_P(varptr)); + ZVAL_COPY_VALUE(top, Z_REFVAL_P(varptr)); + /* Immutable arrays may be passed without copying ??? */ + /* some internal functions may try to modify them !!! */ + if (!Z_OPT_IMMUTABLE_P(top)) { + zval_opt_copy_ctor(top); + } FREE_OP1(); } else { ZVAL_COPY_VALUE(top, varptr); @@ -3157,6 +3183,23 @@ ZEND_VM_C_LABEL(send_again): ZEND_VM_STACK_GROW_IF_NEEDED(zend_hash_num_elements(ht)); + if (OP1_TYPE != IS_CONST && OP1_TYPE != IS_TMP_VAR && Z_IMMUTABLE_P(args)) { + int i; + int separate = 0; + + /* check if any of arguments are going to be passed by reference */ + for (i = 0; i < zend_hash_num_elements(ht); i++) { + if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num + i)) { + separate = 1; + break; + } + } + if (separate) { + zval_copy_ctor(args); + ht = Z_ARRVAL_P(args); + } + } + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) { if (name) { zend_error(E_RECOVERABLE_ERROR, "Cannot unpack array with string keys"); @@ -3167,9 +3210,13 @@ ZEND_VM_C_LABEL(send_again): top = zend_vm_stack_top_inc(TSRMLS_C); if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) { - SEPARATE_ZVAL_TO_MAKE_IS_REF(arg); - Z_ADDREF_P(arg); - ZVAL_COPY_VALUE(top, arg); + if (!Z_IMMUTABLE_P(args)) { + SEPARATE_ZVAL_TO_MAKE_IS_REF(arg); + Z_ADDREF_P(arg); + ZVAL_COPY_VALUE(top, arg); + } else { + ZVAL_DUP(top, arg); + } } else if (Z_ISREF_P(arg)) { ZVAL_DUP(top, Z_REFVAL_P(arg)); } else { @@ -4188,7 +4235,7 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY) { USE_OPLINE zend_free_op free_op1; - zval *array_ptr, *array_ref, iterator; + zval *array_ptr, *array_ref, iterator, tmp; HashTable *fe_ht; zend_object_iterator *iter = NULL; zend_class_entry *ce = NULL; @@ -4209,6 +4256,8 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY) array_ref = array_ptr; array_ptr = Z_REFVAL_P(array_ptr); } + } else if (Z_IMMUTABLE_P(array_ptr)) { + zval_copy_ctor(array_ptr); } if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { @@ -4232,8 +4281,6 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY) array_ptr = array_ref = GET_OP1_ZVAL_PTR(BP_VAR_R); ZVAL_DEREF(array_ptr); if (IS_OP1_TMP_FREE()) { /* IS_TMP_VAR */ - zval tmp; - ZVAL_COPY_VALUE(&tmp, array_ptr); array_ptr = &tmp; if (Z_TYPE_P(array_ptr) == IS_OBJECT) { @@ -4249,6 +4296,14 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY) Z_ADDREF_P(array_ref); } } + } else if (Z_IMMUTABLE_P(array_ref)) { + if (OP1_TYPE == IS_CV) { + zval_copy_ctor(array_ref); + Z_ADDREF_P(array_ref); + } else { + ZVAL_DUP(&tmp, array_ref); + array_ptr = array_ref = &tmp; + } } else if (Z_REFCOUNTED_P(array_ref)) { if (OP1_TYPE == IS_CONST || (OP1_TYPE == IS_CV && @@ -4257,8 +4312,6 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY) (OP1_TYPE == IS_VAR && !Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) > 2)) { - zval tmp; - if (OP1_TYPE == IS_VAR) { Z_DELREF_P(array_ref); } @@ -4269,6 +4322,9 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET, CONST|TMP|VAR|CV, ANY) ZVAL_UNREF(array_ref); array_ptr = array_ref; } + if (Z_IMMUTABLE_P(array_ptr)) { + zval_copy_ctor(array_ptr); + } Z_ADDREF_P(array_ref); } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 4534be8639..a5b678c9d2 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -754,6 +754,23 @@ send_again: ZEND_VM_STACK_GROW_IF_NEEDED(zend_hash_num_elements(ht)); + if (opline->op1_type != IS_CONST && opline->op1_type != IS_TMP_VAR && Z_IMMUTABLE_P(args)) { + int i; + int separate = 0; + + /* check if any of arguments are going to be passed by reference */ + for (i = 0; i < zend_hash_num_elements(ht); i++) { + if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num + i)) { + separate = 1; + break; + } + } + if (separate) { + zval_copy_ctor(args); + ht = Z_ARRVAL_P(args); + } + } + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) { if (name) { zend_error(E_RECOVERABLE_ERROR, "Cannot unpack array with string keys"); @@ -764,9 +781,13 @@ send_again: top = zend_vm_stack_top_inc(TSRMLS_C); if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->fbc, arg_num)) { - SEPARATE_ZVAL_TO_MAKE_IS_REF(arg); - Z_ADDREF_P(arg); - ZVAL_COPY_VALUE(top, arg); + if (!Z_IMMUTABLE_P(args)) { + SEPARATE_ZVAL_TO_MAKE_IS_REF(arg); + Z_ADDREF_P(arg); + ZVAL_COPY_VALUE(top, arg); + } else { + ZVAL_DUP(top, arg); + } } else if (Z_ISREF_P(arg)) { ZVAL_DUP(top, Z_REFVAL_P(arg)); } else { @@ -2700,7 +2721,11 @@ static int ZEND_FASTCALL ZEND_SEND_VAL_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A top = zend_vm_stack_top_inc(TSRMLS_C); ZVAL_COPY_VALUE(top, value); if (IS_CONST == IS_CONST) { - zval_opt_copy_ctor(top); + /* Immutable arrays may be passed without copying ??? */ + /* some internal functions may try to modify them !!! */ + if (!Z_OPT_IMMUTABLE_P(top)) { + zval_opt_copy_ctor(top); + } } ZEND_VM_NEXT_OPCODE(); } @@ -2981,7 +3006,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A { USE_OPLINE - zval *array_ptr, *array_ref, iterator; + zval *array_ptr, *array_ref, iterator, tmp; HashTable *fe_ht; zend_object_iterator *iter = NULL; zend_class_entry *ce = NULL; @@ -3002,6 +3027,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A array_ref = array_ptr; array_ptr = Z_REFVAL_P(array_ptr); } + } else if (Z_IMMUTABLE_P(array_ptr)) { + zval_copy_ctor(array_ptr); } if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { @@ -3025,8 +3052,6 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A array_ptr = array_ref = opline->op1.zv; ZVAL_DEREF(array_ptr); if (0) { /* IS_TMP_VAR */ - zval tmp; - ZVAL_COPY_VALUE(&tmp, array_ptr); array_ptr = &tmp; if (Z_TYPE_P(array_ptr) == IS_OBJECT) { @@ -3042,6 +3067,14 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A Z_ADDREF_P(array_ref); } } + } else if (Z_IMMUTABLE_P(array_ref)) { + if (IS_CONST == IS_CV) { + zval_copy_ctor(array_ref); + Z_ADDREF_P(array_ref); + } else { + ZVAL_DUP(&tmp, array_ref); + array_ptr = array_ref = &tmp; + } } else if (Z_REFCOUNTED_P(array_ref)) { if (IS_CONST == IS_CONST || (IS_CONST == IS_CV && @@ -3050,8 +3083,6 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A (IS_CONST == IS_VAR && !Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) > 2)) { - zval tmp; - if (IS_CONST == IS_VAR) { Z_DELREF_P(array_ref); } @@ -3062,6 +3093,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_A ZVAL_UNREF(array_ref); array_ptr = array_ref; } + if (Z_IMMUTABLE_P(array_ptr)) { + zval_copy_ctor(array_ptr); + } Z_ADDREF_P(array_ref); } } @@ -7807,7 +7841,11 @@ static int ZEND_FASTCALL ZEND_SEND_VAL_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG top = zend_vm_stack_top_inc(TSRMLS_C); ZVAL_COPY_VALUE(top, value); if (IS_TMP_VAR == IS_CONST) { - zval_opt_copy_ctor(top); + /* Immutable arrays may be passed without copying ??? */ + /* some internal functions may try to modify them !!! */ + if (!Z_OPT_IMMUTABLE_P(top)) { + zval_opt_copy_ctor(top); + } } ZEND_VM_NEXT_OPCODE(); } @@ -8089,7 +8127,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG { USE_OPLINE zend_free_op free_op1; - zval *array_ptr, *array_ref, iterator; + zval *array_ptr, *array_ref, iterator, tmp; HashTable *fe_ht; zend_object_iterator *iter = NULL; zend_class_entry *ce = NULL; @@ -8110,6 +8148,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG array_ref = array_ptr; array_ptr = Z_REFVAL_P(array_ptr); } + } else if (Z_IMMUTABLE_P(array_ptr)) { + zval_copy_ctor(array_ptr); } if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { @@ -8133,8 +8173,6 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG array_ptr = array_ref = _get_zval_ptr_tmp(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); ZVAL_DEREF(array_ptr); if (1) { /* IS_TMP_VAR */ - zval tmp; - ZVAL_COPY_VALUE(&tmp, array_ptr); array_ptr = &tmp; if (Z_TYPE_P(array_ptr) == IS_OBJECT) { @@ -8150,6 +8188,14 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG Z_ADDREF_P(array_ref); } } + } else if (Z_IMMUTABLE_P(array_ref)) { + if (IS_TMP_VAR == IS_CV) { + zval_copy_ctor(array_ref); + Z_ADDREF_P(array_ref); + } else { + ZVAL_DUP(&tmp, array_ref); + array_ptr = array_ref = &tmp; + } } else if (Z_REFCOUNTED_P(array_ref)) { if (IS_TMP_VAR == IS_CONST || (IS_TMP_VAR == IS_CV && @@ -8158,8 +8204,6 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG (IS_TMP_VAR == IS_VAR && !Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) > 2)) { - zval tmp; - if (IS_TMP_VAR == IS_VAR) { Z_DELREF_P(array_ref); } @@ -8170,6 +8214,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARG ZVAL_UNREF(array_ref); array_ptr = array_ref; } + if (Z_IMMUTABLE_P(array_ptr)) { + zval_copy_ctor(array_ptr); + } Z_ADDREF_P(array_ref); } } @@ -12863,7 +12910,12 @@ static int ZEND_FASTCALL zend_send_by_var_helper_SPEC_VAR(ZEND_OPCODE_HANDLER_AR varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); top = zend_vm_stack_top_inc(TSRMLS_C); if (Z_ISREF_P(varptr)) { - ZVAL_DUP(top, Z_REFVAL_P(varptr)); + ZVAL_COPY_VALUE(top, Z_REFVAL_P(varptr)); + /* Immutable arrays may be passed without copying ??? */ + /* some internal functions may try to modify them !!! */ + if (!Z_OPT_IMMUTABLE_P(top)) { + zval_opt_copy_ctor(top); + } zval_ptr_dtor_nogc(free_op1.var); } else { ZVAL_COPY_VALUE(top, varptr); @@ -12902,6 +12954,10 @@ static int ZEND_FASTCALL ZEND_SEND_VAR_NO_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HAND if (!Z_ISREF_P(varptr)) { ZVAL_NEW_REF(varptr, varptr); } + // TODO: Try to avoid copying of immutable arrays ??? + if (Z_OPT_IMMUTABLE_P(Z_REFVAL_P(varptr))) { + zval_opt_copy_ctor(Z_REFVAL_P(varptr)); + } if (IS_VAR == IS_CV) { Z_ADDREF_P(varptr); } @@ -12913,8 +12969,8 @@ static int ZEND_FASTCALL ZEND_SEND_VAR_NO_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HAND zend_error(E_STRICT, "Only variables should be passed by reference"); } top = zend_vm_stack_top_inc(TSRMLS_C); - ZVAL_COPY_VALUE(top, varptr); - zval_opt_copy_ctor(top); + // TODO: Try to avoid copying of immutable arrays ??? + ZVAL_DUP(top, varptr); zval_ptr_dtor_nogc(free_op1.var); } CHECK_EXCEPTION(); @@ -12941,6 +12997,10 @@ static int ZEND_FASTCALL ZEND_SEND_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG } if (Z_ISREF_P(varptr)) { + // TODO: Try to avoid copying of immutable arrays ??? + if (Z_OPT_IMMUTABLE_P(Z_REFVAL_P(varptr))) { + zval_opt_copy_ctor(Z_REFVAL_P(varptr)); + } Z_ADDREF_P(varptr); ZVAL_COPY_VALUE(top, varptr); } else if (IS_VAR == IS_VAR && @@ -12948,6 +13008,10 @@ static int ZEND_FASTCALL ZEND_SEND_REF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG ZVAL_COPY_VALUE(top, varptr); SEPARATE_ZVAL_TO_MAKE_IS_REF(top); } else { + // TODO: Try to avoid copying of immutable arrays ??? + if (Z_OPT_IMMUTABLE_P(varptr)) { + zval_opt_copy_ctor(varptr); + } SEPARATE_ZVAL_TO_MAKE_IS_REF(varptr); Z_ADDREF_P(varptr); ZVAL_COPY_VALUE(top, varptr); @@ -12972,7 +13036,12 @@ static int ZEND_FASTCALL ZEND_SEND_VAR_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG varptr = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); top = zend_vm_stack_top_inc(TSRMLS_C); if (Z_ISREF_P(varptr)) { - ZVAL_DUP(top, Z_REFVAL_P(varptr)); + ZVAL_COPY_VALUE(top, Z_REFVAL_P(varptr)); + /* Immutable arrays may be passed without copying ??? */ + /* some internal functions may try to modify them !!! */ + if (!Z_OPT_IMMUTABLE_P(top)) { + zval_opt_copy_ctor(top); + } zval_ptr_dtor_nogc(free_op1.var); } else { ZVAL_COPY_VALUE(top, varptr); @@ -13271,7 +13340,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG { USE_OPLINE zend_free_op free_op1; - zval *array_ptr, *array_ref, iterator; + zval *array_ptr, *array_ref, iterator, tmp; HashTable *fe_ht; zend_object_iterator *iter = NULL; zend_class_entry *ce = NULL; @@ -13292,6 +13361,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG array_ref = array_ptr; array_ptr = Z_REFVAL_P(array_ptr); } + } else if (Z_IMMUTABLE_P(array_ptr)) { + zval_copy_ctor(array_ptr); } if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { @@ -13315,8 +13386,6 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG array_ptr = array_ref = _get_zval_ptr_var(opline->op1.var, execute_data, &free_op1 TSRMLS_CC); ZVAL_DEREF(array_ptr); if (0) { /* IS_TMP_VAR */ - zval tmp; - ZVAL_COPY_VALUE(&tmp, array_ptr); array_ptr = &tmp; if (Z_TYPE_P(array_ptr) == IS_OBJECT) { @@ -13332,6 +13401,14 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG Z_ADDREF_P(array_ref); } } + } else if (Z_IMMUTABLE_P(array_ref)) { + if (IS_VAR == IS_CV) { + zval_copy_ctor(array_ref); + Z_ADDREF_P(array_ref); + } else { + ZVAL_DUP(&tmp, array_ref); + array_ptr = array_ref = &tmp; + } } else if (Z_REFCOUNTED_P(array_ref)) { if (IS_VAR == IS_CONST || (IS_VAR == IS_CV && @@ -13340,8 +13417,6 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG (IS_VAR == IS_VAR && !Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) > 2)) { - zval tmp; - if (IS_VAR == IS_VAR) { Z_DELREF_P(array_ref); } @@ -13352,6 +13427,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARG ZVAL_UNREF(array_ref); array_ptr = array_ref; } + if (Z_IMMUTABLE_P(array_ptr)) { + zval_copy_ctor(array_ptr); + } Z_ADDREF_P(array_ref); } } @@ -29922,7 +30000,12 @@ static int ZEND_FASTCALL zend_send_by_var_helper_SPEC_CV(ZEND_OPCODE_HANDLER_ARG varptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); top = zend_vm_stack_top_inc(TSRMLS_C); if (Z_ISREF_P(varptr)) { - ZVAL_DUP(top, Z_REFVAL_P(varptr)); + ZVAL_COPY_VALUE(top, Z_REFVAL_P(varptr)); + /* Immutable arrays may be passed without copying ??? */ + /* some internal functions may try to modify them !!! */ + if (!Z_OPT_IMMUTABLE_P(top)) { + zval_opt_copy_ctor(top); + } } else { ZVAL_COPY_VALUE(top, varptr); @@ -29961,6 +30044,10 @@ static int ZEND_FASTCALL ZEND_SEND_VAR_NO_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDL if (!Z_ISREF_P(varptr)) { ZVAL_NEW_REF(varptr, varptr); } + // TODO: Try to avoid copying of immutable arrays ??? + if (Z_OPT_IMMUTABLE_P(Z_REFVAL_P(varptr))) { + zval_opt_copy_ctor(Z_REFVAL_P(varptr)); + } if (IS_CV == IS_CV) { Z_ADDREF_P(varptr); } @@ -29972,8 +30059,8 @@ static int ZEND_FASTCALL ZEND_SEND_VAR_NO_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDL zend_error(E_STRICT, "Only variables should be passed by reference"); } top = zend_vm_stack_top_inc(TSRMLS_C); - ZVAL_COPY_VALUE(top, varptr); - zval_opt_copy_ctor(top); + // TODO: Try to avoid copying of immutable arrays ??? + ZVAL_DUP(top, varptr); } CHECK_EXCEPTION(); @@ -30000,6 +30087,10 @@ static int ZEND_FASTCALL ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS } if (Z_ISREF_P(varptr)) { + // TODO: Try to avoid copying of immutable arrays ??? + if (Z_OPT_IMMUTABLE_P(Z_REFVAL_P(varptr))) { + zval_opt_copy_ctor(Z_REFVAL_P(varptr)); + } Z_ADDREF_P(varptr); ZVAL_COPY_VALUE(top, varptr); } else if (IS_CV == IS_VAR && @@ -30007,6 +30098,10 @@ static int ZEND_FASTCALL ZEND_SEND_REF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS ZVAL_COPY_VALUE(top, varptr); SEPARATE_ZVAL_TO_MAKE_IS_REF(top); } else { + // TODO: Try to avoid copying of immutable arrays ??? + if (Z_OPT_IMMUTABLE_P(varptr)) { + zval_opt_copy_ctor(varptr); + } SEPARATE_ZVAL_TO_MAKE_IS_REF(varptr); Z_ADDREF_P(varptr); ZVAL_COPY_VALUE(top, varptr); @@ -30030,7 +30125,12 @@ static int ZEND_FASTCALL ZEND_SEND_VAR_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS varptr = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); top = zend_vm_stack_top_inc(TSRMLS_C); if (Z_ISREF_P(varptr)) { - ZVAL_DUP(top, Z_REFVAL_P(varptr)); + ZVAL_COPY_VALUE(top, Z_REFVAL_P(varptr)); + /* Immutable arrays may be passed without copying ??? */ + /* some internal functions may try to modify them !!! */ + if (!Z_OPT_IMMUTABLE_P(top)) { + zval_opt_copy_ctor(top); + } } else { ZVAL_COPY_VALUE(top, varptr); @@ -30317,7 +30417,7 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS { USE_OPLINE - zval *array_ptr, *array_ref, iterator; + zval *array_ptr, *array_ref, iterator, tmp; HashTable *fe_ht; zend_object_iterator *iter = NULL; zend_class_entry *ce = NULL; @@ -30338,6 +30438,8 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS array_ref = array_ptr; array_ptr = Z_REFVAL_P(array_ptr); } + } else if (Z_IMMUTABLE_P(array_ptr)) { + zval_copy_ctor(array_ptr); } if (Z_REFCOUNTED_P(array_ref)) Z_ADDREF_P(array_ref); } else if (Z_TYPE_P(array_ptr) == IS_OBJECT) { @@ -30361,8 +30463,6 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS array_ptr = array_ref = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC); ZVAL_DEREF(array_ptr); if (0) { /* IS_TMP_VAR */ - zval tmp; - ZVAL_COPY_VALUE(&tmp, array_ptr); array_ptr = &tmp; if (Z_TYPE_P(array_ptr) == IS_OBJECT) { @@ -30378,6 +30478,14 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS Z_ADDREF_P(array_ref); } } + } else if (Z_IMMUTABLE_P(array_ref)) { + if (IS_CV == IS_CV) { + zval_copy_ctor(array_ref); + Z_ADDREF_P(array_ref); + } else { + ZVAL_DUP(&tmp, array_ref); + array_ptr = array_ref = &tmp; + } } else if (Z_REFCOUNTED_P(array_ref)) { if (IS_CV == IS_CONST || (IS_CV == IS_CV && @@ -30386,8 +30494,6 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS (IS_CV == IS_VAR && !Z_ISREF_P(array_ref) && Z_REFCOUNT_P(array_ref) > 2)) { - zval tmp; - if (IS_CV == IS_VAR) { Z_DELREF_P(array_ref); } @@ -30398,6 +30504,9 @@ static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS ZVAL_UNREF(array_ref); array_ptr = array_ref; } + if (Z_IMMUTABLE_P(array_ptr)) { + zval_copy_ctor(array_ptr); + } Z_ADDREF_P(array_ref); } } diff --git a/ext/json/json.c b/ext/json/json.c index 7760741125..e7e07ce2c1 100644 --- a/ext/json/json.c +++ b/ext/json/json.c @@ -258,7 +258,7 @@ static void json_encode_array(smart_str *buf, zval *val, int options TSRMLS_DC) ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) { ZVAL_DEREF(data); tmp_ht = HASH_OF(data); - if (tmp_ht) { + if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) { ZEND_HASH_INC_APPLY_COUNT(tmp_ht); } @@ -318,7 +318,7 @@ static void json_encode_array(smart_str *buf, zval *val, int options TSRMLS_DC) } } - if (tmp_ht) { + if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) { ZEND_HASH_DEC_APPLY_COUNT(tmp_ht); } } ZEND_HASH_FOREACH_END(); diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 7244aafcc0..0c68cc14d5 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -110,6 +110,9 @@ int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv TSRMLS_DC) int i = op_array->last_literal; op_array->last_literal++; op_array->literals = (zval*)erealloc(op_array->literals, op_array->last_literal * sizeof(zval)); + if (Z_TYPE_P(zv) == IS_ARRAY) { + zend_make_immutable_array(zv TSRMLS_CC); + } ZVAL_COPY_VALUE(&op_array->literals[i], zv); Z_CACHE_SLOT(op_array->literals[i]) = -1; //??? Z_SET_REFCOUNT(op_array->literals[i].constant, 2); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index d89fa73d4c..70dbc2cb08 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -2333,7 +2333,6 @@ static int accel_clean_non_persistent_constant(zval *zv TSRMLS_DC) if (c->flags & CONST_PERSISTENT) { return ZEND_HASH_APPLY_STOP; } else { - STR_RELEASE(c->name); return ZEND_HASH_APPLY_REMOVE; } } diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 1fe1d4c83f..9def0183a4 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -263,6 +263,10 @@ static inline void zend_clone_zval(zval *src, int bind TSRMLS_DC) { void *ptr; + if (Z_IMMUTABLE_P(src)) { + return; + } + #if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO switch (Z_TYPE_P(src)) { #else diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 1f5ee3af48..ec1859b2d5 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -101,6 +101,36 @@ static void zend_hash_persist(HashTable *ht, zend_persist_func_t pPersistElement } } +static void zend_hash_persist_immutable(HashTable *ht, zend_persist_func_t pPersistElement TSRMLS_DC) +{ + uint idx; + Bucket *p; + + if (!ht->nTableMask) { + ht->arHash = (zend_uint*)&uninitialized_bucket; + return; + } + if (ht->u.flags & HASH_FLAG_PACKED) { + ht->arData = zend_accel_memdup(ht->arData, sizeof(Bucket) * ht->nTableSize); + ht->arHash = (zend_uint*)&uninitialized_bucket; + } else { + ht->arData = zend_accel_memdup(ht->arData, (sizeof(Bucket) + sizeof(zend_uint)) * ht->nTableSize); + ht->arHash = (zend_uint*)(ht->arData + ht->nTableSize); + } + for (idx = 0; idx < ht->nNumUsed; idx++) { + p = ht->arData + idx; + if (Z_TYPE(p->val) == IS_UNDEF) continue; + + /* persist bucket and key */ + if (p->key) { + zend_accel_store_interned_string(p->key); + } + + /* persist the data itself */ + pPersistElement(&p->val TSRMLS_CC); + } +} + #if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO static zend_ast *zend_persist_ast(zend_ast *ast TSRMLS_DC) { @@ -147,8 +177,13 @@ static void zend_persist_zval(zval *z TSRMLS_DC) if (new_ptr) { Z_ARR_P(z) = new_ptr; } else { - zend_accel_store(Z_ARR_P(z), sizeof(zend_array)); - zend_hash_persist(Z_ARRVAL_P(z), zend_persist_zval TSRMLS_CC); + if (Z_IMMUTABLE_P(z)) { + Z_ARR_P(z) = zend_accel_memdup(Z_ARR_P(z), sizeof(zend_array)); + zend_hash_persist_immutable(Z_ARRVAL_P(z), zend_persist_zval TSRMLS_CC); + } else { + zend_accel_store(Z_ARR_P(z), sizeof(zend_array)); + zend_hash_persist(Z_ARRVAL_P(z), zend_persist_zval TSRMLS_CC); + } } break; #if ZEND_EXTENSION_API_NO > PHP_5_5_X_API_NO diff --git a/ext/standard/array.c b/ext/standard/array.c index 045b80c28b..1398d45b7e 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -266,10 +266,14 @@ PHPAPI int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */ cnt = zend_hash_num_elements(Z_ARRVAL_P(array)); if (mode == COUNT_RECURSIVE) { ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), element) { - Z_ARRVAL_P(array)->u.v.nApplyCount++; + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) { + Z_ARRVAL_P(array)->u.v.nApplyCount++; + } ZVAL_DEREF(element); cnt += php_count_recursive(element, COUNT_RECURSIVE TSRMLS_CC); - Z_ARRVAL_P(array)->u.v.nApplyCount--; + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(array))) { + Z_ARRVAL_P(array)->u.v.nApplyCount--; + } } ZEND_HASH_FOREACH_END(); } } @@ -1435,12 +1439,15 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu return; } - Z_ARRVAL_P(entry)->u.v.nApplyCount++; - + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) { + Z_ARRVAL_P(entry)->u.v.nApplyCount++; + } ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL_P(entry), value_ptr) { php_compact_var(eg_active_symbol_table, return_value, value_ptr TSRMLS_CC); } ZEND_HASH_FOREACH_END(); - Z_ARRVAL_P(entry)->u.v.nApplyCount--; + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(entry))) { + Z_ARRVAL_P(entry)->u.v.nApplyCount--; + } } } /* }}} */ @@ -2207,6 +2214,7 @@ PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS zval *dest_zval = dest_entry; HashTable *thash; zval tmp; + int ret; ZVAL_DEREF(src_zval); ZVAL_DEREF(dest_zval); @@ -2241,18 +2249,16 @@ PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS src_zval = &tmp; } if (Z_TYPE_P(src_zval) == IS_ARRAY) { - if (thash) { + if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) { thash->u.v.nApplyCount++; } - if (!php_array_merge(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval), 1 TSRMLS_CC)) { - if (thash) { - thash->u.v.nApplyCount--; - } - return 0; - } - if (thash) { + ret = php_array_merge(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval), 1 TSRMLS_CC); + if (thash && ZEND_HASH_APPLY_PROTECTION(thash)) { thash->u.v.nApplyCount--; } + if (!ret) { + return 0; + } } else { if (Z_REFCOUNTED_P(src_entry)) { Z_ADDREF_P(src_entry); @@ -2264,7 +2270,7 @@ PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS if (Z_REFCOUNTED_P(src_entry)) { Z_ADDREF_P(src_entry); } - zend_hash_update(dest, string_key, src_entry); + zend_hash_add_new(dest, string_key, src_entry); } } else { if (Z_REFCOUNTED_P(src_entry)) { @@ -2297,6 +2303,7 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC zval *src_entry, *dest_entry, *src_zval, *dest_zval; zend_string *string_key; ulong num_key; + int ret; ZEND_HASH_FOREACH_KEY_VAL(src, num_key, string_key, src_entry) { src_zval = src_entry; @@ -2338,17 +2345,26 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src TSRMLS_DC return 0; } SEPARATE_ZVAL(dest_zval); - Z_ARRVAL_P(dest_zval)->u.v.nApplyCount++; - Z_ARRVAL_P(src_zval)->u.v.nApplyCount++; - - if (!php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval) TSRMLS_CC)) { + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) { + Z_ARRVAL_P(dest_zval)->u.v.nApplyCount++; + } + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) { + Z_ARRVAL_P(src_zval)->u.v.nApplyCount++; + } + + ret = php_array_replace_recursive(Z_ARRVAL_P(dest_zval), Z_ARRVAL_P(src_zval) TSRMLS_CC); + + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(dest_zval))) { Z_ARRVAL_P(dest_zval)->u.v.nApplyCount--; + } + if (ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(src_zval))) { Z_ARRVAL_P(src_zval)->u.v.nApplyCount--; + } + + if (!ret) { return 0; } - Z_ARRVAL_P(dest_zval)->u.v.nApplyCount--; - Z_ARRVAL_P(src_zval)->u.v.nApplyCount--; } ZEND_HASH_FOREACH_END(); return 1; @@ -3850,6 +3866,9 @@ PHP_FUNCTION(array_multisort) ZVAL_DEREF(arg); if (Z_TYPE_P(arg) == IS_ARRAY) { + if (Z_IMMUTABLE_P(arg)) { + zval_copy_ctor(arg); + } /* We see the next array, so we update the sort flags of * the previous array and reset the sort flags. */ if (i > 0) { diff --git a/ext/standard/http.c b/ext/standard/http.c index 20e4023fc7..34b8e79389 100644 --- a/ext/standard/http.c +++ b/ext/standard/http.c @@ -135,9 +135,13 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, *(p++) = 'B'; *p = '\0'; } - ht->u.v.nApplyCount++; + if (ZEND_HASH_APPLY_PROTECTION(ht)) { + ht->u.v.nApplyCount++; + } php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, newprefix, newprefix_len, "%5D", 3, (Z_TYPE_P(zdata) == IS_OBJECT ? zdata : NULL), arg_sep, enc_type TSRMLS_CC); - ht->u.v.nApplyCount--; + if (ZEND_HASH_APPLY_PROTECTION(ht)) { + ht->u.v.nApplyCount--; + } efree(newprefix); } else if (Z_TYPE_P(zdata) == IS_NULL || Z_TYPE_P(zdata) == IS_RESOURCE) { /* Skip these types */ diff --git a/ext/standard/var.c b/ext/standard/var.c index ba8a72973d..6abb70c047 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -132,7 +132,7 @@ again: break; case IS_ARRAY: myht = Z_ARRVAL_P(struc); - if (++myht->u.v.nApplyCount > 1) { + if (ZEND_HASH_APPLY_PROTECTION(myht) && ++myht->u.v.nApplyCount > 1) { PUTS("*RECURSION*\n"); --myht->u.v.nApplyCount; return; @@ -143,7 +143,9 @@ again: ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, val) { php_array_element_dump(val, num, key, level TSRMLS_CC); } ZEND_HASH_FOREACH_END(); - --myht->u.v.nApplyCount; + if (ZEND_HASH_APPLY_PROTECTION(myht)) { + --myht->u.v.nApplyCount; + } if (is_temp) { zend_hash_destroy(myht); efree(myht); @@ -301,7 +303,7 @@ again: break; case IS_ARRAY: myht = Z_ARRVAL_P(struc); - if (myht->u.v.nApplyCount++ > 1) { + if (ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 1) { myht->u.v.nApplyCount--; PUTS("*RECURSION*\n"); return; @@ -310,7 +312,9 @@ again: ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) { zval_array_element_dump(val, index, key, level TSRMLS_CC); } ZEND_HASH_FOREACH_END(); - myht->u.v.nApplyCount--; + if (ZEND_HASH_APPLY_PROTECTION(myht)) { + myht->u.v.nApplyCount--; + } if (is_temp) { zend_hash_destroy(myht); efree(myht); @@ -491,7 +495,7 @@ again: break; case IS_ARRAY: myht = Z_ARRVAL_P(struc); - if (myht->u.v.nApplyCount++ > 0) { + if (ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 0) { myht->u.v.nApplyCount--; smart_str_appendl(buf, "NULL", 4); zend_error(E_WARNING, "var_export does not handle circular references"); @@ -505,7 +509,9 @@ again: ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) { php_array_element_export(val, index, key, level, buf TSRMLS_CC); } ZEND_HASH_FOREACH_END(); - myht->u.v.nApplyCount--; + if (ZEND_HASH_APPLY_PROTECTION(myht)) { + myht->u.v.nApplyCount--; + } if (level > 1) { buffer_append_spaces(buf, level - 1); } @@ -943,11 +949,11 @@ again: ) { smart_str_appendl(buf, "N;", 2); } else { - if (Z_TYPE_P(data) == IS_ARRAY) { + if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) { Z_ARRVAL_P(data)->u.v.nApplyCount++; } php_var_serialize_intern(buf, data, var_hash TSRMLS_CC); - if (Z_TYPE_P(data) == IS_ARRAY) { + if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) { Z_ARRVAL_P(data)->u.v.nApplyCount--; } } diff --git a/ext/wddx/wddx.c b/ext/wddx/wddx.c index dc7ea834b6..a87f97fdcf 100644 --- a/ext/wddx/wddx.c +++ b/ext/wddx/wddx.c @@ -632,9 +632,13 @@ void php_wddx_serialize_var(wddx_packet *packet, zval *var, zend_string *name TS php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "WDDX doesn't support circular references"); return; } - ht->u.v.nApplyCount++; + if (ZEND_HASH_APPLY_PROTECTION(ht)) { + ht->u.v.nApplyCount++; + } php_wddx_serialize_array(packet, var); - ht->u.v.nApplyCount--; + if (ZEND_HASH_APPLY_PROTECTION(ht)) { + ht->u.v.nApplyCount--; + } break; case IS_OBJECT: @@ -683,18 +687,24 @@ static void php_wddx_add_var(wddx_packet *packet, zval *name_var) return; } - ZEND_HASH_FOREACH_VAL(target_hash, val) { - if (is_array) { - target_hash->u.v.nApplyCount++; - } + if (Z_IMMUTABLE_P(name_var)) { + ZEND_HASH_FOREACH_VAL(target_hash, val) { + php_wddx_add_var(packet, val); + } ZEND_HASH_FOREACH_END(); + } else { + ZEND_HASH_FOREACH_VAL(target_hash, val) { + if (is_array) { + target_hash->u.v.nApplyCount++; + } - ZVAL_DEREF(val); - php_wddx_add_var(packet, val); + ZVAL_DEREF(val); + php_wddx_add_var(packet, val); - if (is_array) { - target_hash->u.v.nApplyCount--; - } - } ZEND_HASH_FOREACH_END(); + if (is_array) { + target_hash->u.v.nApplyCount--; + } + } ZEND_HASH_FOREACH_END(); + } } } /* }}} */ -- 2.50.1