Created
May 28, 2014 22:05
-
-
Save dstogov/002f84c5200dcff2811f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/Zend/zend.c b/Zend/zend.c | |
index 297fe26..b995966 100644 | |
--- a/Zend/zend.c | |
+++ b/Zend/zend.c | |
@@ -385,13 +385,15 @@ 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 (!Z_IMMUTABLE_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 (!Z_IMMUTABLE_P(expr)) { | |
+ Z_ARRVAL_P(expr)->u.v.nApplyCount--; | |
+ } | |
break; | |
case IS_OBJECT: | |
{ | |
diff --git a/Zend/zend.h b/Zend/zend.h | |
index 1968d76..1a4d301 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 23c38b8..efa4d20 100644 | |
--- a/Zend/zend_compile.c | |
+++ b/Zend/zend_compile.c | |
@@ -334,6 +334,17 @@ void zend_del_literal(zend_op_array *op_array, int n) /* {{{ */ | |
} | |
/* }}} */ | |
+void zend_del_literal_ptr(zend_op_array *op_array, int n) /* {{{ */ | |
+{ | |
+ zval_ptr_dtor(&CONSTANT_EX(op_array, n)); | |
+ if (n + 1 == op_array->last_literal) { | |
+ op_array->last_literal--; | |
+ } else { | |
+ ZVAL_UNDEF(&CONSTANT_EX(op_array, n)); | |
+ } | |
+} | |
+/* }}} */ | |
+ | |
/* Common part of zend_add_literal and zend_append_individual_literal */ | |
static inline void zend_insert_literal(zend_op_array *op_array, zval *zv, int literal_position TSRMLS_DC) /* {{{ */ | |
{ | |
@@ -5922,8 +5933,127 @@ 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; | |
+ | |
+ /* 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_IMMUTABLE_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_ptr(CG(active_op_array), opline->op2.constant); | |
+ } | |
+ zend_del_literal_ptr(CG(active_op_array), opline->op1.constant); | |
+ opline--; | |
+ } | |
+ if (opline->op2_type == IS_CONST) { | |
+ zend_del_literal_ptr(CG(active_op_array), opline->op2.constant); | |
+ } | |
+ if (opline->op1_type == IS_CONST) { | |
+ zend_del_literal_ptr(CG(active_op_array), opline->op1.constant); | |
+ } | |
+ CG(active_op_array)->last = array_node->u.op.opline_num; | |
+ | |
+ Z_TYPE_FLAGS(array) = IS_TYPE_IMMUTABLE; | |
+ | |
+ result->op_type = IS_CONST; | |
+ ZVAL_COPY_VALUE(&result->u.constant, &array); | |
+ } else { | |
+ GET_NODE(result, init_opline->result); | |
+ } | |
} | |
/* }}} */ | |
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c | |
index e0653f9..c45ac9b 100644 | |
--- a/Zend/zend_execute.c | |
+++ b/Zend/zend_execute.c | |
@@ -1119,7 +1119,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_execute_API.c b/Zend/zend_execute_API.c | |
index f0f856d..93851de 100644 | |
--- a/Zend/zend_execute_API.c | |
+++ b/Zend/zend_execute_API.c | |
@@ -479,6 +479,17 @@ ZEND_API void _zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) /* {{{ */ | |
} | |
/* }}} */ | |
+ZEND_API void _zval_immutable_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) /* {{{ */ | |
+{ | |
+ TSRMLS_FETCH(); | |
+ if (Z_IMMUTABLE_P(zval_ptr)) { | |
+ _zval_dtor_func(Z_COUNTED_P(zval_ptr) ZEND_FILE_LINE_RELAY_CC); | |
+ } else { | |
+ i_zval_ptr_dtor(zval_ptr ZEND_FILE_LINE_RELAY_CC TSRMLS_CC); | |
+ } | |
+} | |
+/* }}} */ | |
+ | |
ZEND_API void _zval_internal_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC) /* {{{ */ | |
{ | |
if (Z_REFCOUNTED_P(zval_ptr)) { | |
diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c | |
index 5bf3e42..563da43 100644 | |
--- a/Zend/zend_hash.c | |
+++ b/Zend/zend_hash.c | |
@@ -1172,7 +1172,7 @@ ZEND_API void zend_array_dup(HashTable *target, HashTable *source) | |
target->nTableMask = source->nTableMask; | |
target->nTableSize = source->nTableSize; | |
- target->pDestructor = source->pDestructor; | |
+ target->pDestructor = ZVAL_PTR_DTOR; | |
target->nInternalPointer = INVALID_IDX; | |
target->u.flags = (source->u.flags & ~HASH_FLAG_PERSISTENT); | |
diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c | |
index 37875dc..3755eb1 100644 | |
--- a/Zend/zend_opcode.c | |
+++ b/Zend/zend_opcode.c | |
@@ -350,7 +350,9 @@ ZEND_API void destroy_op_array(zend_op_array *op_array TSRMLS_DC) | |
if (literal) { | |
end = literal + op_array->last_literal; | |
while (literal < end) { | |
- zval_dtor(literal); | |
+ if (Z_REFCOUNTED_P(literal) || Z_OPT_IMMUTABLE_P(literal)) { | |
+ _zval_dtor_func(Z_COUNTED_P(literal) ZEND_FILE_LINE_CC); | |
+ } | |
literal++; | |
} | |
efree(op_array->literals); | |
diff --git a/Zend/zend_types.h b/Zend/zend_types.h | |
index 1503c5a..232fcba 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.c b/Zend/zend_variables.c | |
index 79bd00d..eacec85 100644 | |
--- a/Zend/zend_variables.c | |
+++ b/Zend/zend_variables.c | |
@@ -297,6 +297,12 @@ ZEND_API void _zval_ptr_dtor_wrapper(zval *zval_ptr) | |
} | |
+ZEND_API void _zval_immutable_ptr_dtor_wrapper(zval *zval_ptr) | |
+{ | |
+ zval_immutable_ptr_dtor(zval_ptr); | |
+} | |
+ | |
+ | |
ZEND_API void _zval_internal_ptr_dtor_wrapper(zval *zval_ptr) | |
{ | |
zval_internal_ptr_dtor(zval_ptr); | |
diff --git a/Zend/zend_variables.h b/Zend/zend_variables.h | |
index b85ef43..e6c91e0 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); | |
@@ -66,6 +66,7 @@ ZEND_API int zval_copy_static_var(zval *p TSRMLS_DC, int num_args, va_list args, | |
ZEND_API int zend_print_variable(zval *var TSRMLS_DC); | |
ZEND_API void _zval_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC); | |
+ZEND_API void _zval_immutable_ptr_dtor(zval *zval_ptr ZEND_FILE_LINE_DC); | |
ZEND_API void _zval_internal_dtor_for_ptr(zval *zvalue ZEND_FILE_LINE_DC); | |
ZEND_API void _zval_internal_dtor(zval *zvalue ZEND_FILE_LINE_DC); | |
ZEND_API void _zval_internal_ptr_dtor(zval *zvalue ZEND_FILE_LINE_DC); | |
@@ -74,6 +75,7 @@ ZEND_API void _zval_dtor_wrapper(zval *zvalue); | |
#define zval_opt_copy_ctor(zvalue) _zval_opt_copy_ctor((zvalue) ZEND_FILE_LINE_CC) | |
#define zval_dtor(zvalue) _zval_dtor((zvalue) ZEND_FILE_LINE_CC) | |
#define zval_ptr_dtor(zval_ptr) _zval_ptr_dtor((zval_ptr) ZEND_FILE_LINE_CC) | |
+#define zval_immutable_ptr_dtor(zval_ptr) _zval_immutable_ptr_dtor((zval_ptr) ZEND_FILE_LINE_CC) | |
#define zval_internal_dtor(zvalue) _zval_internal_dtor((zvalue) ZEND_FILE_LINE_CC) | |
#define zval_internal_ptr_dtor(zvalue) _zval_internal_ptr_dtor((zvalue) ZEND_FILE_LINE_CC) | |
#define zval_dtor_wrapper _zval_dtor_wrapper | |
@@ -81,15 +83,18 @@ ZEND_API void _zval_dtor_wrapper(zval *zvalue); | |
#if ZEND_DEBUG | |
ZEND_API void _zval_copy_ctor_wrapper(zval *zvalue); | |
ZEND_API void _zval_ptr_dtor_wrapper(zval *zval_ptr); | |
+ZEND_API void _zval_immutable_ptr_dtor_wrapper(zval *zval_ptr); | |
ZEND_API void _zval_internal_dtor_wrapper(zval *zvalue); | |
ZEND_API void _zval_internal_ptr_dtor_wrapper(zval *zvalue); | |
#define zval_copy_ctor_wrapper _zval_copy_ctor_wrapper | |
#define zval_ptr_dtor_wrapper _zval_ptr_dtor_wrapper | |
+#define zval_immutable_ptr_dtor_wrapper _zval_immutable_ptr_dtor_wrapper | |
#define zval_internal_dtor_wrapper _zval_internal_dtor_wrapper | |
#define zval_internal_ptr_dtor_wrapper _zval_internal_ptr_dtor_wrapper | |
#else | |
#define zval_copy_ctor_wrapper _zval_copy_ctor_func | |
#define zval_ptr_dtor_wrapper _zval_ptr_dtor | |
+#define zval_immutable_ptr_dtor_wrapper _zval_immutable_ptr_dtor | |
#define zval_internal_dtor_wrapper _zval_internal_dtor | |
#define zval_internal_ptr_dtor_wrapper _zval_internal_ptr_dtor | |
#endif | |
@@ -101,6 +106,7 @@ END_EXTERN_C() | |
#define ZVAL_DESTRUCTOR zval_dtor_wrapper | |
#define ZVAL_PTR_DTOR zval_ptr_dtor_wrapper | |
+#define ZVAL_IMMUTABLE_PTR_DTOR zval_immutable_ptr_dtor_wrapper | |
#define ZVAL_INTERNAL_DTOR zval_internal_dtor_wrapper | |
#define ZVAL_INTERNAL_PTR_DTOR zval_internal_ptr_dtor_wrapper | |
#define ZVAL_COPY_CTOR zval_copy_ctor_wrapper | |
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h | |
index cba6e77..2988632 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 might be passed without copying ??? */ | |
+ /* some internal functions might try to modify it !!! */ | |
+ if (!Z_OPT_IMMUTABLE_P(top)) { | |
+ zval_opt_copy_ctor(top); | |
+ } | |
} | |
ZEND_VM_NEXT_OPCODE(); | |
} | |
@@ -3095,6 +3099,10 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY) | |
} | |
if (Z_ISREF_P(varptr)) { | |
+ // TODO: Try to avoid copying ??? | |
+ 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 +3110,9 @@ ZEND_VM_HANDLER(67, ZEND_SEND_REF, VAR|CV, ANY) | |
ZVAL_COPY_VALUE(top, varptr); | |
SEPARATE_ZVAL_TO_MAKE_IS_REF(top); | |
} else { | |
+ 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 +3137,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)); | |
+ varptr = Z_REFVAL_P(varptr); | |
+ if (Z_IMMUTABLE_P(varptr)) { | |
+ ZVAL_COPY_VALUE(top, varptr); | |
+ } else { | |
+ ZVAL_DUP(top, varptr); | |
+ } | |
FREE_OP1(); | |
} else { | |
ZVAL_COPY_VALUE(top, varptr); | |
@@ -3157,6 +3173,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 +3200,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 +4225,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 +4246,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 +4271,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 +4286,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 +4302,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 +4312,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 2e7184e..92bb0a2 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 might be passed without copying ??? */ | |
+ /* some internal functions might try to modify it !!! */ | |
+ 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 might be passed without copying ??? */ | |
+ /* some internal functions might try to modify it !!! */ | |
+ 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); | |
} | |
} | |
@@ -12941,6 +12988,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 ??? | |
+ 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 +12999,9 @@ 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 { | |
+ 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 +13026,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)); | |
+ varptr = Z_REFVAL_P(varptr); | |
+ if (Z_IMMUTABLE_P(varptr)) { | |
+ ZVAL_COPY_VALUE(top, varptr); | |
+ } else { | |
+ ZVAL_DUP(top, varptr); | |
+ } | |
zval_ptr_dtor_nogc(free_op1.var); | |
} else { | |
ZVAL_COPY_VALUE(top, varptr); | |
@@ -13271,7 +13330,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 +13351,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 +13376,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 +13391,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 +13407,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 +13417,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); | |
} | |
} | |
@@ -30000,6 +30068,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 ??? | |
+ 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 +30079,9 @@ 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 { | |
+ 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 +30105,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)); | |
+ varptr = Z_REFVAL_P(varptr); | |
+ if (Z_IMMUTABLE_P(varptr)) { | |
+ ZVAL_COPY_VALUE(top, varptr); | |
+ } else { | |
+ ZVAL_DUP(top, varptr); | |
+ } | |
} else { | |
ZVAL_COPY_VALUE(top, varptr); | |
@@ -30317,7 +30397,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 +30418,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 +30443,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 +30458,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 +30474,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 +30484,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 7760741..1340807 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 && !Z_IMMUTABLE_P(data)) { | |
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 && !Z_IMMUTABLE_P(data)) { | |
ZEND_HASH_DEC_APPLY_COUNT(tmp_ht); | |
} | |
} ZEND_HASH_FOREACH_END(); | |
diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c | |
index aad6243..2fd1ecf 100644 | |
--- a/ext/opcache/Optimizer/compact_literals.c | |
+++ b/ext/opcache/Optimizer/compact_literals.c | |
@@ -286,7 +286,7 @@ static void optimizer_compact_literals(zend_op_array *op_array TSRMLS_DC) | |
for (i = 0; i < op_array->last_literal; i++) { | |
if (!info[i].flags) { | |
/* unsed literal */ | |
- zval_dtor(&op_array->literals[i]); | |
+ literal_dtor(&op_array->literals[i]); | |
continue; | |
} | |
switch (Z_TYPE(op_array->literals[i])) { | |
diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c | |
index 7244aaf..5246425 100644 | |
--- a/ext/opcache/Optimizer/zend_optimizer.c | |
+++ b/ext/opcache/Optimizer/zend_optimizer.c | |
@@ -110,6 +110,10 @@ 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) { | |
+ Z_TYPE_FLAGS_P(zv) = IS_TYPE_IMMUTABLE; | |
+ Z_ARRVAL_P(zv)->pDestructor = ZVAL_IMMUTABLE_PTR_DTOR; | |
+ } | |
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); | |
@@ -130,8 +134,11 @@ int zend_optimizer_add_literal(zend_op_array *op_array, zval *zv TSRMLS_DC) | |
} while (0) | |
# define literal_dtor(zv) do { \ | |
- zval_dtor(zv); \ | |
- ZVAL_NULL(zv); \ | |
+ zval *__zv = zv; \ | |
+ if (Z_REFCOUNTED_P(__zv) || Z_OPT_IMMUTABLE_P(__zv)) { \ | |
+ _zval_dtor_func(Z_COUNTED_P(__zv) ZEND_FILE_LINE_CC); \ | |
+ } \ | |
+ ZVAL_NULL(__zv); \ | |
} while (0) | |
#define COPY_NODE(target, src) do { \ | |
diff --git a/ext/standard/array.c b/ext/standard/array.c | |
index 045b80c..befbf61 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 (!Z_IMMUTABLE_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 (!Z_IMMUTABLE_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 (!Z_IMMUTABLE_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 (!Z_IMMUTABLE_P(entry)) { | |
+ Z_ARRVAL_P(entry)->u.v.nApplyCount--; | |
+ } | |
} | |
} | |
/* }}} */ | |
@@ -2205,11 +2212,14 @@ PHPAPI int php_array_merge(HashTable *dest, HashTable *src, int recursive TSRMLS | |
if ((dest_entry = zend_hash_find(dest, string_key)) != NULL) { | |
zval *src_zval = src_entry; | |
zval *dest_zval = dest_entry; | |
+ zval orig_dest_zval; | |
HashTable *thash; | |
zval tmp; | |
+ int ret; | |
ZVAL_DEREF(src_zval); | |
ZVAL_DEREF(dest_zval); | |
+ ZVAL_COPY_VALUE(&orig_dest_zval, dest_zval); | |
thash = Z_TYPE_P(dest_zval) == IS_ARRAY ? Z_ARRVAL_P(dest_zval) : NULL; | |
if ((thash && thash->u.v.nApplyCount > 1) || (src_entry == dest_entry && Z_ISREF_P(dest_entry) && (Z_REFCOUNT_P(dest_entry) % 2))) { | |
php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected"); | |
@@ -2241,18 +2251,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 && !Z_IMMUTABLE(orig_dest_zval)) { | |
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 && !Z_IMMUTABLE(orig_dest_zval)) { | |
thash->u.v.nApplyCount--; | |
} | |
+ if (!ret) { | |
+ return 0; | |
+ } | |
} else { | |
if (Z_REFCOUNTED_P(src_entry)) { | |
Z_ADDREF_P(src_entry); | |
@@ -2264,7 +2272,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 +2305,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 +2347,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 (!Z_IMMUTABLE_P(dest_zval)) { | |
+ Z_ARRVAL_P(dest_zval)->u.v.nApplyCount++; | |
+ } | |
+ if (!Z_IMMUTABLE_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 (!Z_IMMUTABLE_P(dest_zval)) { | |
Z_ARRVAL_P(dest_zval)->u.v.nApplyCount--; | |
+ } | |
+ if (!Z_IMMUTABLE_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 +3868,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 20e4023..ecf1010 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 (!Z_IMMUTABLE_P(zdata)) { | |
+ 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 (!Z_IMMUTABLE_P(zdata)) { | |
+ 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 ba8a729..fd17cb2 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 (!Z_IMMUTABLE_P(struc) && ++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 (!Z_IMMUTABLE_P(struc)) { | |
+ --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 (!Z_IMMUTABLE_P(struc) && 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 (!Z_IMMUTABLE_P(struc)) { | |
+ 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 (!Z_IMMUTABLE_P(struc) && 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 (!Z_IMMUTABLE_P(struc)) { | |
+ 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 && !Z_IMMUTABLE_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 && !Z_IMMUTABLE_P(data)) { | |
Z_ARRVAL_P(data)->u.v.nApplyCount--; | |
} | |
} | |
diff --git a/ext/wddx/wddx.c b/ext/wddx/wddx.c | |
index dc7ea83..38818e7 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 (!Z_IMMUTABLE_P(var)) { | |
+ ht->u.v.nApplyCount++; | |
+ } | |
php_wddx_serialize_array(packet, var); | |
- ht->u.v.nApplyCount--; | |
+ if (!Z_IMMUTABLE_P(var)) { | |
+ 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(); | |
+ } | |
} | |
} | |
/* }}} */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment