Skip to content

Instantly share code, notes, and snippets.

@dstogov
Created May 28, 2014 22:05
Show Gist options
  • Save dstogov/002f84c5200dcff2811f to your computer and use it in GitHub Desktop.
Save dstogov/002f84c5200dcff2811f to your computer and use it in GitHub Desktop.
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