diff options
Diffstat (limited to 'ext/opcache')
-rw-r--r-- | ext/opcache/Optimizer/compact_literals.c | 112 | ||||
-rw-r--r-- | ext/opcache/Optimizer/dce.c | 3 | ||||
-rw-r--r-- | ext/opcache/Optimizer/escape_analysis.c | 6 | ||||
-rw-r--r-- | ext/opcache/Optimizer/sccp.c | 11 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_dfg.c | 9 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_dump.c | 14 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_inference.c | 266 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_optimizer.c | 68 | ||||
-rw-r--r-- | ext/opcache/Optimizer/zend_ssa.c | 16 | ||||
-rw-r--r-- | ext/opcache/tests/opt/prop_types.phpt | 115 | ||||
-rw-r--r-- | ext/opcache/zend_accelerator_util_funcs.c | 18 | ||||
-rw-r--r-- | ext/opcache/zend_file_cache.c | 44 | ||||
-rw-r--r-- | ext/opcache/zend_persist.c | 34 | ||||
-rw-r--r-- | ext/opcache/zend_persist_calc.c | 20 |
14 files changed, 631 insertions, 105 deletions
diff --git a/ext/opcache/Optimizer/compact_literals.c b/ext/opcache/Optimizer/compact_literals.c index 9e3a0ab9a1..397458365f 100644 --- a/ext/opcache/Optimizer/compact_literals.c +++ b/ext/opcache/Optimizer/compact_literals.c @@ -108,7 +108,7 @@ static uint32_t add_static_slot(HashTable *hash, ret = Z_LVAL_P(pos); } else { ret = *cache_size; - *cache_size += 2 * sizeof(void *); + *cache_size += (kind == LITERAL_STATIC_PROPERTY ? 3 : 2) * sizeof(void *); ZVAL_LONG(&tmp, ret); zend_hash_add(hash, key, &tmp); } @@ -183,6 +183,8 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } LITERAL_INFO(opline->op2.constant, LITERAL_CLASS_CONST, 1); break; + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_FETCH_STATIC_PROP_R: case ZEND_FETCH_STATIC_PROP_W: case ZEND_FETCH_STATIC_PROP_RW: @@ -191,6 +193,11 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_FETCH_STATIC_PROP_FUNC_ARG: case ZEND_UNSET_STATIC_PROP: case ZEND_ISSET_ISEMPTY_STATIC_PROP: + case ZEND_PRE_INC_STATIC_PROP: + case ZEND_PRE_DEC_STATIC_PROP: + case ZEND_POST_INC_STATIC_PROP: + case ZEND_POST_DEC_STATIC_PROP: +literals_handle_static_prop: if (opline->op2_type == IS_CONST) { LITERAL_INFO(opline->op2.constant, LITERAL_CLASS, 2); } @@ -210,6 +217,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx } break; case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_RW: @@ -238,6 +246,9 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_ASSIGN_BW_OR: case ZEND_ASSIGN_BW_AND: case ZEND_ASSIGN_BW_XOR: + if (opline->extended_value == ZEND_ASSIGN_STATIC_PROP) { + goto literals_handle_static_prop; + } if (opline->op2_type == IS_CONST) { if (opline->extended_value == ZEND_ASSIGN_OBJ) { LITERAL_INFO(opline->op2.constant, LITERAL_PROPERTY, 1); @@ -547,24 +558,48 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_ASSIGN_BW_OR: case ZEND_ASSIGN_BW_AND: case ZEND_ASSIGN_BW_XOR: - if (opline->extended_value != ZEND_ASSIGN_OBJ) { - break; + if (opline->extended_value == ZEND_ASSIGN_STATIC_PROP) { + if (opline->op1_type == IS_CONST) { + // op1 static property + if (opline->op2_type == IS_CONST) { + (opline+1)->extended_value = add_static_slot(&hash, op_array, + opline->op2.constant, + opline->op1.constant, + LITERAL_STATIC_PROPERTY, + &cache_size); + } else { + (opline+1)->extended_value = cache_size; + cache_size += 3 * sizeof(void *); + } + } else if (opline->op2_type == IS_CONST) { + // op2 class + if (class_slot[opline->op2.constant] >= 0) { + (opline+1)->extended_value = class_slot[opline->op2.constant]; + } else { + (opline+1)->extended_value = cache_size; + class_slot[opline->op2.constant] = cache_size; + cache_size += sizeof(void *); + } + } } - if (opline->op2_type == IS_CONST) { - // op2 property - if (opline->op1_type == IS_UNUSED && - property_slot[opline->op2.constant] >= 0) { - (opline+1)->extended_value = property_slot[opline->op2.constant]; - } else { - (opline+1)->extended_value = cache_size; - cache_size += 2 * sizeof(void *); - if (opline->op1_type == IS_UNUSED) { - property_slot[opline->op2.constant] = (opline+1)->extended_value; + if (opline->extended_value == ZEND_ASSIGN_OBJ) { + if (opline->op2_type == IS_CONST) { + // op2 property + if (opline->op1_type == IS_UNUSED && + property_slot[opline->op2.constant] >= 0) { + (opline+1)->extended_value = property_slot[opline->op2.constant]; + } else { + (opline+1)->extended_value = cache_size; + cache_size += 3 * sizeof(void *); + if (opline->op1_type == IS_UNUSED) { + property_slot[opline->op2.constant] = (opline+1)->extended_value; + } } } } break; case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_RW: @@ -580,12 +615,12 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx // op2 property if (opline->op1_type == IS_UNUSED && property_slot[opline->op2.constant] >= 0) { - opline->extended_value = property_slot[opline->op2.constant]; + opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); } else { - opline->extended_value = cache_size; - cache_size += 2 * sizeof(void *); + opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); + cache_size += 3 * sizeof(void *); if (opline->op1_type == IS_UNUSED) { - property_slot[opline->op2.constant] = opline->extended_value; + property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS; } } } @@ -598,7 +633,7 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx opline->extended_value = property_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY); } else { opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY); - cache_size += 2 * sizeof(void *); + cache_size += 3 * sizeof(void *); if (opline->op1_type == IS_UNUSED) { property_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY; } @@ -689,6 +724,8 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx cache_size += 2 * sizeof(void *); } break; + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_FETCH_STATIC_PROP_R: case ZEND_FETCH_STATIC_PROP_W: case ZEND_FETCH_STATIC_PROP_RW: @@ -696,30 +733,11 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx case ZEND_FETCH_STATIC_PROP_UNSET: case ZEND_FETCH_STATIC_PROP_FUNC_ARG: case ZEND_UNSET_STATIC_PROP: - if (opline->op1_type == IS_CONST) { - // op1 static property - if (opline->op2_type == IS_CONST) { - opline->extended_value = add_static_slot(&hash, op_array, - opline->op2.constant, - opline->op1.constant, - LITERAL_STATIC_PROPERTY, - &cache_size); - } else { - opline->extended_value = cache_size; - cache_size += 2 * sizeof(void *); - } - } else if (opline->op2_type == IS_CONST) { - // op2 class - if (class_slot[opline->op2.constant] >= 0) { - opline->extended_value = class_slot[opline->op2.constant]; - } else { - opline->extended_value = cache_size; - cache_size += sizeof(void *); - class_slot[opline->op2.constant] = opline->extended_value; - } - } - break; case ZEND_ISSET_ISEMPTY_STATIC_PROP: + case ZEND_PRE_INC_STATIC_PROP: + case ZEND_PRE_DEC_STATIC_PROP: + case ZEND_POST_INC_STATIC_PROP: + case ZEND_POST_DEC_STATIC_PROP: if (opline->op1_type == IS_CONST) { // op1 static property if (opline->op2_type == IS_CONST) { @@ -727,19 +745,19 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx opline->op2.constant, opline->op1.constant, LITERAL_STATIC_PROPERTY, - &cache_size) | (opline->extended_value & ZEND_ISEMPTY); + &cache_size) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); } else { - opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY); - cache_size += 2 * sizeof(void *); + opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); + cache_size += 3 * sizeof(void *); } } else if (opline->op2_type == IS_CONST) { // op2 class if (class_slot[opline->op2.constant] >= 0) { - opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_ISEMPTY); + opline->extended_value = class_slot[opline->op2.constant] | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); } else { - opline->extended_value = cache_size | (opline->extended_value & ZEND_ISEMPTY); + opline->extended_value = cache_size | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); + class_slot[opline->op2.constant] = cache_size; cache_size += sizeof(void *); - class_slot[opline->op2.constant] = opline->extended_value & ~ZEND_ISEMPTY; } } break; diff --git a/ext/opcache/Optimizer/dce.c b/ext/opcache/Optimizer/dce.c index 89898393a7..95755d559f 100644 --- a/ext/opcache/Optimizer/dce.c +++ b/ext/opcache/Optimizer/dce.c @@ -330,6 +330,9 @@ static zend_bool try_remove_var_def(context *ctx, int free_var, int use_chain, z case ZEND_ASSIGN_REF: case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: case ZEND_ASSIGN_MUL: diff --git a/ext/opcache/Optimizer/escape_analysis.c b/ext/opcache/Optimizer/escape_analysis.c index f88de6202a..a92d773783 100644 --- a/ext/opcache/Optimizer/escape_analysis.c +++ b/ext/opcache/Optimizer/escape_analysis.c @@ -217,6 +217,7 @@ static int is_allocation_def(zend_op_array *op_array, zend_ssa *ssa, int def, in break; case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: if (OP1_INFO() & (MAY_BE_UNDEF | MAY_BE_NULL | MAY_BE_FALSE)) { /* implicit object/array allocation */ return 1; @@ -259,6 +260,7 @@ static int is_local_def(zend_op_array *op_array, zend_ssa *ssa, int def, int var return 1; case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: return 1; case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: @@ -328,6 +330,7 @@ static int is_escape_use(zend_op_array *op_array, zend_ssa *ssa, int use, int va /* break missing intentionally */ case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: break; case ZEND_PRE_INC_OBJ: case ZEND_PRE_DEC_OBJ: @@ -508,7 +511,8 @@ int zend_ssa_escape_analysis(const zend_script *script, zend_op_array *op_array, if (opline->opcode == ZEND_OP_DATA && ((opline-1)->opcode == ZEND_ASSIGN_DIM || - (opline-1)->opcode == ZEND_ASSIGN_OBJ) && + (opline-1)->opcode == ZEND_ASSIGN_OBJ || + (opline-1)->opcode == ZEND_ASSIGN_OBJ_REF) && op->op1_use == i && (op-1)->op1_use >= 0) { enclosing_root = ees[(op-1)->op1_use]; diff --git a/ext/opcache/Optimizer/sccp.c b/ext/opcache/Optimizer/sccp.c index 08e1584923..69a3e81c6b 100644 --- a/ext/opcache/Optimizer/sccp.c +++ b/ext/opcache/Optimizer/sccp.c @@ -203,6 +203,7 @@ static zend_bool can_replace_op1( case ZEND_ASSIGN_REF: case ZEND_ASSIGN_DIM: case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: case ZEND_ASSIGN_MUL: @@ -251,6 +252,9 @@ static zend_bool can_replace_op1( case ZEND_VERIFY_RETURN_TYPE: // TODO: This would require a non-local change ??? return 0; + case ZEND_OP_DATA: + return (opline - 1)->opcode != ZEND_ASSIGN_OBJ_REF && + (opline - 1)->opcode != ZEND_ASSIGN_STATIC_PROP_REF; default: if (ssa_op->op1_def != -1) { ZEND_ASSERT(0); @@ -1400,7 +1404,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o if (op2) { SKIP_IF_TOP(op2); } - if (!opline->extended_value) { + if (opline->extended_value == 0) { if (zend_optimizer_eval_binary_op(&zv, zend_compound_assign_to_binary_op(opline->opcode), op1, op2) == SUCCESS) { SET_RESULT(op1, &zv); SET_RESULT(result, &zv); @@ -1494,6 +1498,11 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o zval_ptr_dtor_nogc(&zv); } } + } else if (opline->extended_value == ZEND_ASSIGN_STATIC_PROP) { + SET_RESULT_BOT(result); + break; + } else { + ZEND_ASSERT(0 && "Invalid compound assignment kind"); } SET_RESULT_BOT(result); SET_RESULT_BOT(op1); diff --git a/ext/opcache/Optimizer/zend_dfg.c b/ext/opcache/Optimizer/zend_dfg.c index 4430f193a8..66dd15da00 100644 --- a/ext/opcache/Optimizer/zend_dfg.c +++ b/ext/opcache/Optimizer/zend_dfg.c @@ -52,8 +52,14 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg if (next < end && next->opcode == ZEND_OP_DATA) { if (next->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { var_num = EX_VAR_TO_NUM(next->op1.var); - if (!DFG_ISSET(def, set_size, j, var_num)) { + if (opline->opcode == ZEND_ASSIGN_OBJ_REF + || opline->opcode == ZEND_ASSIGN_STATIC_PROP_REF) { DFG_SET(use, set_size, j, var_num); + DFG_SET(def, set_size, j, var_num); + } else { + if (!DFG_ISSET(def, set_size, j, var_num)) { + DFG_SET(use, set_size, j, var_num); + } } } if (next->op2_type & (IS_CV|IS_VAR|IS_TMP_VAR)) { @@ -92,6 +98,7 @@ int zend_build_dfg(const zend_op_array *op_array, const zend_cfg *cfg, zend_dfg case ZEND_UNSET_CV: case ZEND_ASSIGN: case ZEND_ASSIGN_REF: + case ZEND_ASSIGN_OBJ_REF: case ZEND_BIND_GLOBAL: case ZEND_BIND_STATIC: case ZEND_SEND_VAR_EX: diff --git a/ext/opcache/Optimizer/zend_dump.c b/ext/opcache/Optimizer/zend_dump.c index 90107785b7..d5f6c4564b 100644 --- a/ext/opcache/Optimizer/zend_dump.c +++ b/ext/opcache/Optimizer/zend_dump.c @@ -451,6 +451,8 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * fprintf(stderr, " (dim)"); } else if (opline->extended_value == ZEND_ASSIGN_OBJ) { fprintf(stderr, " (obj)"); + } else if (opline->extended_value == ZEND_ASSIGN_STATIC_PROP) { + fprintf(stderr, " (static prop)"); } } else if (ZEND_VM_EXT_TYPE == (flags & ZEND_VM_EXT_MASK)) { switch (opline->extended_value) { @@ -554,7 +556,7 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * } else if (ZEND_VM_EXT_SRC == (flags & ZEND_VM_EXT_MASK)) { if (opline->extended_value == ZEND_RETURNS_VALUE) { fprintf(stderr, " (value)"); - } else if (opline->extended_value == ZEND_RETURNS_FUNCTION) { + } else if (opline->extended_value & ZEND_RETURNS_FUNCTION) { fprintf(stderr, " (function)"); } } else { @@ -585,6 +587,16 @@ static void zend_dump_op(const zend_op_array *op_array, const zend_basic_block * fprintf(stderr, " (ref)"); } } + if ((ZEND_VM_EXT_DIM_OBJ_WRITE|ZEND_VM_EXT_FETCH_REF) & flags) { + uint32_t obj_flags = opline->extended_value & ZEND_FETCH_OBJ_FLAGS; + if (obj_flags == ZEND_FETCH_REF) { + fprintf(stderr, " (ref)"); + } else if (obj_flags == ZEND_FETCH_DIM_WRITE) { + fprintf(stderr, " (dim write)"); + } else if (obj_flags == ZEND_FETCH_OBJ_WRITE) { + fprintf(stderr, " (obj write)"); + } + } } if (opline->op1_type == IS_CONST) { diff --git a/ext/opcache/Optimizer/zend_inference.c b/ext/opcache/Optimizer/zend_inference.c index 9f1dfdba20..980f3c5fea 100644 --- a/ext/opcache/Optimizer/zend_inference.c +++ b/ext/opcache/Optimizer/zend_inference.c @@ -2240,6 +2240,24 @@ static inline zend_class_entry *get_class_entry(const zend_script *script, zend_ return NULL; } +static uint32_t zend_convert_type_code_to_may_be(zend_uchar type_code) { + switch (type_code) { + case IS_VOID: + return MAY_BE_NULL; + case IS_CALLABLE: + return MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + case IS_ITERABLE: + return MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + case IS_ARRAY: + return MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; + case _IS_BOOL: + return MAY_BE_TRUE|MAY_BE_FALSE; + default: + ZEND_ASSERT(type_code < IS_REFERENCE); + return 1 << type_code; + } +} + static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *arg_info, zend_class_entry **pce) { uint32_t tmp = 0; @@ -2252,22 +2270,7 @@ static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *ar *pce = get_class_entry(script, lcname); zend_string_release_ex(lcname, 0); } else if (ZEND_TYPE_IS_CODE(arg_info->type)) { - zend_uchar type_hint = ZEND_TYPE_CODE(arg_info->type); - - if (type_hint == IS_VOID) { - tmp |= MAY_BE_NULL; - } else if (type_hint == IS_CALLABLE) { - tmp |= MAY_BE_STRING|MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - } else if (type_hint == IS_ITERABLE) { - tmp |= MAY_BE_OBJECT|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - } else if (type_hint == IS_ARRAY) { - tmp |= MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; - } else if (type_hint == _IS_BOOL) { - tmp |= MAY_BE_TRUE|MAY_BE_FALSE; - } else { - ZEND_ASSERT(type_hint < IS_REFERENCE); - tmp |= 1 << type_hint; - } + tmp |= zend_convert_type_code_to_may_be(ZEND_TYPE_CODE(arg_info->type)); } else { tmp |= MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY|MAY_BE_ARRAY_OF_REF; } @@ -2277,6 +2280,123 @@ static uint32_t zend_fetch_arg_info(const zend_script *script, zend_arg_info *ar return tmp; } +static zend_property_info *lookup_prop_info(zend_class_entry *ce, zend_string *name, zend_class_entry *scope) { + zend_property_info *prop_info; + + /* If the class is linked, reuse the precise runtime logic. */ + if (ce->ce_flags & ZEND_ACC_LINKED) { + zend_class_entry *prev_scope = EG(fake_scope); + EG(fake_scope) = scope; + prop_info = zend_get_property_info(ce, name, 1); + EG(fake_scope) = prev_scope; + if (prop_info && prop_info != ZEND_WRONG_PROPERTY_INFO) { + return prop_info; + } + return NULL; + } + + /* Otherwise, handle only some safe cases */ + prop_info = zend_hash_find_ptr(&ce->properties_info, name); + if (prop_info && + ((prop_info->ce == scope) || + (!scope && (prop_info->flags & ZEND_ACC_PUBLIC))) + ) { + return prop_info; + } + return NULL; +} + +static zend_property_info *zend_fetch_prop_info(const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline, int i) +{ + zend_property_info *prop_info = NULL; + if (opline->op2_type == IS_CONST) { + zend_class_entry *ce = NULL; + + if (opline->op1_type == IS_UNUSED) { + ce = op_array->scope; + } else if (ssa->ops[i].op1_use >= 0) { + ce = ssa->var_info[ssa->ops[i].op1_use].ce; + } + if (ce) { + prop_info = lookup_prop_info(ce, + Z_STR_P(CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants)), + op_array->scope); + if (prop_info && (prop_info->flags & ZEND_ACC_STATIC)) { + prop_info = NULL; + } + } + } + return prop_info; +} + +static zend_property_info *zend_fetch_static_prop_info(const zend_script *script, const zend_op_array *op_array, zend_ssa *ssa, zend_op *opline) +{ + zend_property_info *prop_info = NULL; + if (opline->op1_type == IS_CONST) { + zend_class_entry *ce = NULL; + if (opline->op2_type == IS_UNUSED) { + int fetch_type = opline->op2.num & ZEND_FETCH_CLASS_MASK; + switch (fetch_type) { + case ZEND_FETCH_CLASS_SELF: + case ZEND_FETCH_CLASS_STATIC: + /* We enforce that static property types cannot change during inheritance, so + * handling static the same way as self here is legal. */ + ce = op_array->scope; + break; + case ZEND_FETCH_CLASS_PARENT: + if (op_array->scope) { + ce = op_array->scope->parent; + } + break; + } + } else if (opline->op2_type == IS_CONST) { + zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->op2, ssa->rt_constants); + ce = get_class_entry(script, Z_STR_P(zv + 1)); + } + + if (ce) { + zval *zv = CRT_CONSTANT_EX(op_array, opline, opline->op1, ssa->rt_constants); + prop_info = lookup_prop_info(ce, Z_STR_P(zv), op_array->scope); + if (prop_info && !(prop_info->flags & ZEND_ACC_STATIC)) { + prop_info = NULL; + } + } + } + return prop_info; +} + +static uint32_t zend_fetch_prop_type(const zend_script *script, zend_property_info *prop_info, zend_class_entry **pce) +{ + if (prop_info && ZEND_TYPE_IS_SET(prop_info->type)) { + uint32_t type = ZEND_TYPE_IS_CLASS(prop_info->type) + ? MAY_BE_OBJECT + : zend_convert_type_code_to_may_be(ZEND_TYPE_CODE(prop_info->type)); + + if (ZEND_TYPE_ALLOW_NULL(prop_info->type)) { + type |= MAY_BE_NULL; + } + if (type & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT|MAY_BE_RESOURCE)) { + type |= MAY_BE_RC1 | MAY_BE_RCN; + } + if (pce) { + if (ZEND_TYPE_IS_CE(prop_info->type)) { + *pce = ZEND_TYPE_CE(prop_info->type); + } else if (ZEND_TYPE_IS_NAME(prop_info->type)) { + zend_string *lcname = zend_string_tolower(ZEND_TYPE_NAME(prop_info->type)); + *pce = get_class_entry(script, lcname); + zend_string_release(lcname); + } else { + *pce = NULL; + } + } + return type; + } + if (pce) { + *pce = NULL; + } + return MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF | MAY_BE_RC1 | MAY_BE_RCN; +} + static int zend_update_type_info(const zend_op_array *op_array, zend_ssa *ssa, const zend_script *script, @@ -2474,13 +2594,14 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_ASSIGN_BW_OR: case ZEND_ASSIGN_BW_AND: case ZEND_ASSIGN_BW_XOR: - case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_CONCAT: { + zend_property_info *prop_info = NULL; orig = 0; tmp = 0; if (opline->extended_value == ZEND_ASSIGN_OBJ) { - tmp |= MAY_BE_REF; + prop_info = zend_fetch_prop_info(op_array, ssa, opline, i); orig = t1; - t1 = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; + t1 = zend_fetch_prop_type(script, prop_info, &ce); t2 = OP1_DATA_INFO(); } else if (opline->extended_value == ZEND_ASSIGN_DIM) { if (t1 & MAY_BE_ARRAY_OF_REF) { @@ -2489,6 +2610,10 @@ static int zend_update_type_info(const zend_op_array *op_array, orig = t1; t1 = zend_array_element_type(t1, 1, 0); t2 = OP1_DATA_INFO(); + } else if (opline->extended_value == ZEND_ASSIGN_STATIC_PROP) { + prop_info = zend_fetch_static_prop_info(script, op_array, ssa, opline); + t1 = zend_fetch_prop_type(script, prop_info, &ce); + t2 = OP1_DATA_INFO(); } else { if (t1 & MAY_BE_REF) { tmp |= MAY_BE_REF; @@ -2524,10 +2649,13 @@ static int zend_update_type_info(const zend_op_array *op_array, UPDATE_SSA_TYPE(orig, ssa_ops[i].op1_def); COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def); } + } else if (opline->extended_value == ZEND_ASSIGN_STATIC_PROP) { + /* Nothing to do */ } else { UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def); } if (ssa_ops[i].result_def >= 0) { + ce = NULL; if (opline->extended_value == ZEND_ASSIGN_DIM) { if (opline->op2_type == IS_UNUSED) { /* When appending to an array and the LONG_MAX key is already used @@ -2549,10 +2677,25 @@ static int zend_update_type_info(const zend_op_array *op_array, * anything else results in a null return value. */ tmp |= MAY_BE_NULL; } + + /* The return value must also satisfy the property type */ + if (prop_info) { + tmp &= zend_fetch_prop_type(script, prop_info, NULL); + } + } else if (opline->extended_value == ZEND_ASSIGN_STATIC_PROP) { + /* The return value must also satisfy the property type */ + if (prop_info) { + tmp &= zend_fetch_prop_type(script, prop_info, NULL); + } } + tmp &= ~MAY_BE_REF; UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + if (ce) { + UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def); + } } break; + } case ZEND_PRE_INC: case ZEND_PRE_DEC: tmp = 0; @@ -2727,9 +2870,13 @@ static int zend_update_type_info(const zend_op_array *op_array, COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def); } if (ssa_ops[i].result_def >= 0) { - // TODO: ??? - tmp = MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; + // TODO: If there is no __set we might do better + tmp = zend_fetch_prop_type(script, + zend_fetch_prop_info(op_array, ssa, opline, i), &ce); UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + if (ce) { + UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def); + } } if ((opline+1)->op1_type == IS_CV) { opline++; @@ -2827,6 +2974,42 @@ static int zend_update_type_info(const zend_op_array *op_array, UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); } break; + case ZEND_ASSIGN_OBJ_REF: + if (opline->op1_type == IS_CV) { + tmp = t1; + if (t1 & (MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE)) { + tmp &= ~(MAY_BE_UNDEF|MAY_BE_NULL|MAY_BE_FALSE); + tmp |= MAY_BE_OBJECT | MAY_BE_RC1 | MAY_BE_RCN; + } + if (tmp & MAY_BE_OBJECT) { + tmp |= MAY_BE_RC1 | MAY_BE_RCN; + } + UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def); + COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def); + } + + t2 = OP1_DATA_INFO(); + if ((opline+1)->op1_type == IS_VAR && (opline->extended_value & ZEND_RETURNS_FUNCTION)) { + tmp = (MAY_BE_REF | MAY_BE_RCN | MAY_BE_RC1 | t2) & ~MAY_BE_UNDEF; + } else { + tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_ERROR|MAY_BE_RC1|MAY_BE_RCN); + } + if (t2 & MAY_BE_UNDEF) { + tmp |= MAY_BE_NULL; + } + if (ssa_ops[i].result_def >= 0) { + UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + } + if ((opline+1)->op1_type == IS_CV) { + opline++; + i++; + tmp = (MAY_BE_REF | t2) & ~(MAY_BE_UNDEF|MAY_BE_RC1|MAY_BE_RCN); + if (t2 & MAY_BE_UNDEF) { + tmp |= MAY_BE_NULL; + } + UPDATE_SSA_TYPE(tmp, ssa_ops[i].op1_def); + } + break; case ZEND_BIND_GLOBAL: tmp = MAY_BE_REF | MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; @@ -3245,6 +3428,7 @@ static int zend_update_type_info(const zend_op_array *op_array, case ZEND_FETCH_OBJ_RW: case ZEND_FETCH_OBJ_FUNC_ARG: case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: case ZEND_PRE_INC_OBJ: case ZEND_PRE_DEC_OBJ: case ZEND_POST_INC_OBJ: @@ -3342,14 +3526,31 @@ static int zend_update_type_info(const zend_op_array *op_array, COPY_SSA_OBJ_TYPE(ssa_ops[i].op1_use, ssa_ops[i].op1_def); } if (ssa_ops[i].result_def >= 0) { - tmp = MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF; - if (opline->result_type == IS_TMP_VAR) { - /* can't be REF because of ZVAL_COPY_DEREF() usage */ - tmp |= MAY_BE_RC1 | MAY_BE_RCN; - } else { - tmp |= MAY_BE_REF | MAY_BE_RC1 | MAY_BE_RCN | MAY_BE_ERROR; + tmp = zend_fetch_prop_type(script, + zend_fetch_prop_info(op_array, ssa, opline, i), &ce); + if (opline->result_type != IS_TMP_VAR) { + tmp |= MAY_BE_REF | MAY_BE_ERROR; } UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + if (ce) { + UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def); + } + } + break; + case ZEND_FETCH_STATIC_PROP_R: + case ZEND_FETCH_STATIC_PROP_IS: + case ZEND_FETCH_STATIC_PROP_RW: + case ZEND_FETCH_STATIC_PROP_W: + case ZEND_FETCH_STATIC_PROP_UNSET: + case ZEND_FETCH_STATIC_PROP_FUNC_ARG: + tmp = zend_fetch_prop_type(script, + zend_fetch_static_prop_info(script, op_array, ssa, opline), &ce); + if (opline->result_type != IS_TMP_VAR) { + tmp |= MAY_BE_REF | MAY_BE_ERROR; + } + UPDATE_SSA_TYPE(tmp, ssa_ops[i].result_def); + if (ce) { + UPDATE_SSA_OBJ_TYPE(ce, 1, ssa_ops[i].result_def); } break; case ZEND_DO_FCALL: @@ -4182,7 +4383,7 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa return 1; } } - } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { + } else if (opline->op1_type & (IS_TMP_VAR|IS_VAR)) { if (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)) { switch (opline->opcode) { case ZEND_CASE: @@ -4206,9 +4407,9 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa return 1; } } - } + } - if (opline->op2_type == IS_CV) { + if (opline->op2_type == IS_CV) { if (t2 & MAY_BE_UNDEF) { switch (opline->opcode) { case ZEND_ASSIGN_REF: @@ -4228,7 +4429,7 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa return 1; } } - } + } switch (opline->opcode) { case ZEND_NOP: @@ -4390,6 +4591,9 @@ int zend_may_throw(const zend_op *opline, zend_op_array *op_array, zend_ssa *ssa return (t1 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)) || (t2 & (MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_OBJECT)); case ZEND_ASSIGN: + if (t1 & MAY_BE_REF) { + return 1; + } case ZEND_UNSET_VAR: return (t1 & (MAY_BE_OBJECT|MAY_BE_RESOURCE|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_ARRAY_OF_RESOURCE|MAY_BE_ARRAY_OF_ARRAY)); case ZEND_ASSIGN_DIM: diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index d13b8a000e..42cd5eef8f 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -256,6 +256,14 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array, zval *val) { switch (opline->opcode) { + case ZEND_OP_DATA: + switch ((opline-1)->opcode) { + case ZEND_ASSIGN_OBJ_REF: + case ZEND_ASSIGN_STATIC_PROP_REF: + return 0; + } + opline->op1.constant = zend_optimizer_add_literal(op_array, val); + break; case ZEND_FREE: case ZEND_CHECK_VAR: MAKE_NOP(opline); @@ -312,6 +320,23 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array, } zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); break; + case ZEND_ASSIGN_ADD: + case ZEND_ASSIGN_SUB: + case ZEND_ASSIGN_MUL: + case ZEND_ASSIGN_DIV: + case ZEND_ASSIGN_MOD: + case ZEND_ASSIGN_SL: + case ZEND_ASSIGN_SR: + case ZEND_ASSIGN_CONCAT: + case ZEND_ASSIGN_BW_OR: + case ZEND_ASSIGN_BW_AND: + case ZEND_ASSIGN_BW_XOR: + case ZEND_ASSIGN_POW: + if (opline->extended_value != ZEND_ASSIGN_STATIC_PROP) { + break; + } + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_FETCH_STATIC_PROP_R: case ZEND_FETCH_STATIC_PROP_W: case ZEND_FETCH_STATIC_PROP_RW: @@ -319,21 +344,17 @@ int zend_optimizer_update_op1_const(zend_op_array *op_array, case ZEND_FETCH_STATIC_PROP_UNSET: case ZEND_FETCH_STATIC_PROP_FUNC_ARG: case ZEND_UNSET_STATIC_PROP: - TO_STRING_NOWARN(val); - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - if (opline->op2_type == IS_CONST && opline->extended_value + sizeof(void*) == op_array->cache_size) { - op_array->cache_size += sizeof(void *); - } else { - opline->extended_value = alloc_cache_slots(op_array, 2); - } - break; case ZEND_ISSET_ISEMPTY_STATIC_PROP: + case ZEND_PRE_INC_STATIC_PROP: + case ZEND_PRE_DEC_STATIC_PROP: + case ZEND_POST_INC_STATIC_PROP: + case ZEND_POST_DEC_STATIC_PROP: TO_STRING_NOWARN(val); opline->op1.constant = zend_optimizer_add_literal(op_array, val); - if (opline->op2_type == IS_CONST && (opline->extended_value & ~ZEND_ISEMPTY) + sizeof(void*) == op_array->cache_size) { + if (opline->op2_type == IS_CONST && (opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*) == op_array->cache_size) { op_array->cache_size += sizeof(void *); } else { - opline->extended_value = alloc_cache_slots(op_array, 2) | (opline->extended_value & ZEND_ISEMPTY); + opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_FETCH_OBJ_FLAGS); } break; case ZEND_SEND_VAR: @@ -405,6 +426,8 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); opline->result.num = alloc_cache_slots(op_array, 1); break; + case ZEND_ASSIGN_STATIC_PROP: + case ZEND_ASSIGN_STATIC_PROP_REF: case ZEND_FETCH_STATIC_PROP_R: case ZEND_FETCH_STATIC_PROP_W: case ZEND_FETCH_STATIC_PROP_RW: @@ -412,21 +435,17 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, case ZEND_FETCH_STATIC_PROP_UNSET: case ZEND_FETCH_STATIC_PROP_FUNC_ARG: case ZEND_UNSET_STATIC_PROP: + case ZEND_PRE_INC_STATIC_PROP: + case ZEND_PRE_DEC_STATIC_PROP: + case ZEND_POST_INC_STATIC_PROP: + case ZEND_POST_DEC_STATIC_PROP: +handle_static_prop: REQUIRES_STRING(val); drop_leading_backslash(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); if (opline->op1_type != IS_CONST) { - opline->extended_value = alloc_cache_slots(op_array, 1); - } - break; - case ZEND_ISSET_ISEMPTY_STATIC_PROP: - REQUIRES_STRING(val); - drop_leading_backslash(val); - opline->op2.constant = zend_optimizer_add_literal(op_array, val); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - if (opline->op1_type != IS_CONST) { - opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & ZEND_ISEMPTY); + opline->extended_value = alloc_cache_slots(op_array, 1) | (opline->extended_value & (ZEND_RETURNS_FUNCTION|ZEND_ISEMPTY|ZEND_FETCH_OBJ_FLAGS)); } break; case ZEND_INIT_FCALL: @@ -477,6 +496,7 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, } break; case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_REF: case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_RW: @@ -490,12 +510,12 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, case ZEND_POST_DEC_OBJ: TO_STRING_NOWARN(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); - opline->extended_value = alloc_cache_slots(op_array, 2); + opline->extended_value = alloc_cache_slots(op_array, 3); break; case ZEND_ISSET_ISEMPTY_PROP_OBJ: TO_STRING_NOWARN(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); - opline->extended_value = alloc_cache_slots(op_array, 2) | (opline->extended_value & ZEND_ISEMPTY); + opline->extended_value = alloc_cache_slots(op_array, 3) | (opline->extended_value & ZEND_ISEMPTY); break; case ZEND_ASSIGN_ADD: case ZEND_ASSIGN_SUB: @@ -512,7 +532,9 @@ int zend_optimizer_update_op2_const(zend_op_array *op_array, if (opline->extended_value == ZEND_ASSIGN_OBJ) { TO_STRING_NOWARN(val); opline->op2.constant = zend_optimizer_add_literal(op_array, val); - (opline+1)->extended_value = alloc_cache_slots(op_array, 2); + (opline+1)->extended_value = alloc_cache_slots(op_array, 3); + } else if (opline->extended_value == ZEND_ASSIGN_STATIC_PROP) { + goto handle_static_prop; } else if (opline->extended_value == ZEND_ASSIGN_DIM) { if (Z_TYPE_P(val) == IS_STRING) { zend_ulong index; diff --git a/ext/opcache/Optimizer/zend_ssa.c b/ext/opcache/Optimizer/zend_ssa.c index ea387bb95f..ad34e7f643 100644 --- a/ext/opcache/Optimizer/zend_ssa.c +++ b/ext/opcache/Optimizer/zend_ssa.c @@ -642,6 +642,22 @@ static int zend_ssa_rename(const zend_op_array *op_array, uint32_t build_flags, //NEW_SSA_VAR(next->op1.var) } break; + case ZEND_ASSIGN_OBJ_REF: + if (opline->op1_type == IS_CV) { + ssa_ops[k].op1_def = ssa_vars_count; + var[EX_VAR_TO_NUM(opline->op1.var)] = ssa_vars_count; + ssa_vars_count++; + //NEW_SSA_VAR(opline->op1.var) + } + /* break missing intentionally */ + case ZEND_ASSIGN_STATIC_PROP_REF: + if (next->op1_type == IS_CV) { + ssa_ops[k + 1].op1_def = ssa_vars_count; + var[EX_VAR_TO_NUM(next->op1.var)] = ssa_vars_count; + ssa_vars_count++; + //NEW_SSA_VAR(next->op1.var) + } + break; case ZEND_PRE_INC_OBJ: case ZEND_PRE_DEC_OBJ: case ZEND_POST_INC_OBJ: diff --git a/ext/opcache/tests/opt/prop_types.phpt b/ext/opcache/tests/opt/prop_types.phpt new file mode 100644 index 0000000000..7962b1b893 --- /dev/null +++ b/ext/opcache/tests/opt/prop_types.phpt @@ -0,0 +1,115 @@ +--TEST-- +Property types in inference +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.optimization_level=-1 +opcache.opt_debug_level=0x200000 +--SKIPIF-- +<?php require_once('skipif.inc'); ?> +--FILE-- +<?php + +class Test { + public bool $public; + protected int $protected; + private float $private; + + public function inTest() { + var_dump($this->public, $this->protected, $this->private); + } + + public function inTestWithTest2(Test2 $test2) { + var_dump($test2->public, $test2->protected, $test2->private); + } +} + +class Test2 extends Test { + private array $private; + + public function inTest2() { + var_dump($this->public, $this->protected, $this->private); + } +} + +function noScope(Test $test) { + var_dump($test->public, $test->protected, $test->private); +} + +?> +--EXPECTF-- +$_main: ; (lines=1, args=0, vars=0, tmps=0, ssa_vars=0, no_loops) + ; (before dfa pass) + ; %s + ; return [long] RANGE[1..1] +BB0: start exit lines=[0-0] + ; level=0 + RETURN int(1) + +noScope: ; (lines=10, args=1, vars=1, tmps=1, ssa_vars=5, no_loops) + ; (before dfa pass) + ; %s + ; return [null] RANGE[0..0] + ; #0.CV0($test) NOVAL [undef] +BB0: start exit lines=[0-9] + ; level=0 + #1.CV0($test) [object (instanceof Test)] = RECV 1 + INIT_FCALL 3 128 string("var_dump") + #2.T1 [bool] = FETCH_OBJ_R #1.CV0($test) [object (instanceof Test)] string("public") + SEND_VAL #2.T1 [bool] 1 + #3.T1 [any] = FETCH_OBJ_R #1.CV0($test) [object (instanceof Test)] string("protected") + SEND_VAL #3.T1 [any] 2 + #4.T1 [any] = FETCH_OBJ_R #1.CV0($test) [object (instanceof Test)] string("private") + SEND_VAL #4.T1 [any] 3 + DO_ICALL + RETURN null + +Test::inTest: ; (lines=9, args=0, vars=0, tmps=1, ssa_vars=3, no_loops) + ; (before dfa pass) + ; %s + ; return [null] RANGE[0..0] +BB0: start exit lines=[0-8] + ; level=0 + INIT_FCALL 3 128 string("var_dump") + #0.T0 [bool] = FETCH_OBJ_R THIS string("public") + SEND_VAL #0.T0 [bool] 1 + #1.T0 [long] = FETCH_OBJ_R THIS string("protected") + SEND_VAL #1.T0 [long] 2 + #2.T0 [double] = FETCH_OBJ_R THIS string("private") + SEND_VAL #2.T0 [double] 3 + DO_ICALL + RETURN null + +Test::inTestWithTest2: ; (lines=10, args=1, vars=1, tmps=1, ssa_vars=5, no_loops) + ; (before dfa pass) + ; %s + ; return [null] RANGE[0..0] + ; #0.CV0($test2) NOVAL [undef] +BB0: start exit lines=[0-9] + ; level=0 + #1.CV0($test2) [object (instanceof Test2)] = RECV 1 + INIT_FCALL 3 128 string("var_dump") + #2.T1 [bool] = FETCH_OBJ_R #1.CV0($test2) [object (instanceof Test2)] string("public") + SEND_VAL #2.T1 [bool] 1 + #3.T1 [long] = FETCH_OBJ_R #1.CV0($test2) [object (instanceof Test2)] string("protected") + SEND_VAL #3.T1 [long] 2 + #4.T1 [double] = FETCH_OBJ_R #1.CV0($test2) [object (instanceof Test2)] string("private") + SEND_VAL #4.T1 [double] 3 + DO_ICALL + RETURN null + +Test2::inTest2: ; (lines=9, args=0, vars=0, tmps=1, ssa_vars=3, no_loops) + ; (before dfa pass) + ; %s + ; return [null] RANGE[0..0] +BB0: start exit lines=[0-8] + ; level=0 + INIT_FCALL 3 128 string("var_dump") + #0.T0 [bool] = FETCH_OBJ_R THIS string("public") + SEND_VAL #0.T0 [bool] 1 + #1.T0 [long] = FETCH_OBJ_R THIS string("protected") + SEND_VAL #1.T0 [long] 2 + #2.T0 [array of [any, ref]] = FETCH_OBJ_R THIS string("private") + SEND_VAL #2.T0 [array of [any, ref]] 3 + DO_ICALL + RETURN null diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index d22cc7ffd7..7b04d0d2df 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -232,6 +232,14 @@ static void zend_hash_clone_prop_info(HashTable *ht) if (IN_ARENA(prop_info->ce)) { prop_info->ce = ARENA_REALLOC(prop_info->ce); } + + if (ZEND_TYPE_IS_CE(prop_info->type)) { + zend_class_entry *ce = ZEND_TYPE_CE(prop_info->type); + if (IN_ARENA(ce)) { + ce = ARENA_REALLOC(ce); + prop_info->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop_info->type)); + } + } } } } @@ -304,6 +312,16 @@ static void zend_class_copy_ctor(zend_class_entry **pce) /* constants table */ zend_hash_clone_constants(&ce->constants_table); + if (ce->properties_info_table) { + int i; + ce->properties_info_table = ARENA_REALLOC(ce->properties_info_table); + for (i = 0; i < ce->default_properties_count; i++) { + if (IN_ARENA(ce->properties_info_table[i])) { + ce->properties_info_table[i] = ARENA_REALLOC(ce->properties_info_table[i]); + } + } + } + if (ce->num_interfaces) { zend_class_name *interface_names; diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 86cb8a2e25..92a77ab792 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -577,6 +577,17 @@ static void zend_file_cache_serialize_prop_info(zval *zv, SERIALIZE_STR(prop->doc_comment); } } + if (prop->type) { + if (ZEND_TYPE_IS_NAME(prop->type)) { + zend_string *name = ZEND_TYPE_NAME(prop->type); + SERIALIZE_STR(name); + prop->type = ZEND_TYPE_ENCODE_CLASS(name, ZEND_TYPE_ALLOW_NULL(prop->type)); + } else if (ZEND_TYPE_IS_CE(prop->type)) { + zend_class_entry *ce = ZEND_TYPE_CE(prop->type); + SERIALIZE_PTR(ce); + prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type)); + } + } } } @@ -660,6 +671,19 @@ static void zend_file_cache_serialize_class(zval *zv, SERIALIZE_STR(ce->info.user.doc_comment); zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info); + if (ce->properties_info_table) { + uint32_t i; + zend_property_info **table; + + SERIALIZE_PTR(ce->properties_info_table); + table = ce->properties_info_table; + UNSERIALIZE_PTR(table); + + for (i = 0; i < ce->default_properties_count; i++) { + SERIALIZE_PTR(table[i]); + } + } + if (ce->num_interfaces) { uint32_t i; zend_class_name *interface_names; @@ -1246,6 +1270,17 @@ static void zend_file_cache_unserialize_prop_info(zval *zv, UNSERIALIZE_STR(prop->doc_comment); } } + if (prop->type) { + if (ZEND_TYPE_IS_NAME(prop->type)) { + zend_string *name = ZEND_TYPE_NAME(prop->type); + UNSERIALIZE_STR(name); + prop->type = ZEND_TYPE_ENCODE_CLASS(name, ZEND_TYPE_ALLOW_NULL(prop->type)); + } else if (ZEND_TYPE_IS_CE(prop->type)) { + zend_class_entry *ce = ZEND_TYPE_CE(prop->type); + UNSERIALIZE_PTR(ce); + prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type)); + } + } } } @@ -1325,6 +1360,15 @@ static void zend_file_cache_unserialize_class(zval *zv, zend_file_cache_unserialize_hash(&ce->properties_info, script, buf, zend_file_cache_unserialize_prop_info, NULL); + if (ce->properties_info_table) { + uint32_t i; + UNSERIALIZE_PTR(ce->properties_info_table); + + for (i = 0; i < ce->default_properties_count; i++) { + UNSERIALIZE_PTR(ce->properties_info_table[i]); + } + } + if (ce->num_interfaces) { uint32_t i; diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index eba9dd23e4..af9244bbf5 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -746,6 +746,16 @@ static void zend_persist_property_info(zval *zv) prop->doc_comment = NULL; } } + + if (ZEND_TYPE_IS_NAME(prop->type)) { + zend_string *class_name = ZEND_TYPE_NAME(prop->type); + zend_accel_store_interned_string(class_name); + prop->type = ZEND_TYPE_ENCODE_CLASS(class_name, ZEND_TYPE_ALLOW_NULL(prop->type)); + } else if (ZEND_TYPE_IS_CE(prop->type)) { + zend_class_entry *ce = ZEND_TYPE_CE(prop->type); + ce = zend_shared_alloc_get_xlat_entry(ce); + prop->type = ZEND_TYPE_ENCODE_CE(ce, ZEND_TYPE_ALLOW_NULL(prop->type)); + } } static void zend_persist_class_constant(zval *zv) @@ -786,6 +796,16 @@ static void zend_persist_class_constant(zval *zv) } } +static zend_bool has_unresolved_property_types(zend_class_entry *ce) { + zend_property_info *prop; + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + if (ZEND_TYPE_IS_NAME(prop->type)) { + return 1; + } + } ZEND_HASH_FOREACH_END(); + return 0; +} + static void zend_persist_class_entry(zval *zv) { zend_class_entry *ce = Z_PTR_P(zv); @@ -793,6 +813,7 @@ static void zend_persist_class_entry(zval *zv) if (ce->type == ZEND_USER_CLASS) { if ((ce->ce_flags & ZEND_ACC_LINKED) && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) + && !has_unresolved_property_types(ce) && !ZCG(current_persistent_script)->corrupted) { ZCG(is_immutable_class) = 1; ce = Z_PTR_P(zv) = zend_shared_memdup_put(ce, sizeof(zend_class_entry)); @@ -855,6 +876,19 @@ static void zend_persist_class_entry(zval *zv) zend_hash_persist(&ce->properties_info, zend_persist_property_info); HT_FLAGS(&ce->properties_info) &= (HASH_FLAG_UNINITIALIZED | HASH_FLAG_STATIC_KEYS); + if (ce->properties_info_table) { + int i; + + size_t size = sizeof(zend_property_info *) * ce->default_properties_count; + memcpy(ZCG(arena_mem), ce->properties_info_table, size); + ce->properties_info_table = ZCG(arena_mem); + ZCG(arena_mem) = (void*)((char*)ZCG(arena_mem) + ZEND_ALIGNED_SIZE(size)); + + for (i = 0; i < ce->default_properties_count; i++) { + ce->properties_info_table[i] = zend_shared_alloc_get_xlat_entry(ce->properties_info_table[i]); + } + } + if (ce->num_interfaces && !(ce->ce_flags & ZEND_ACC_LINKED)) { uint32_t i = 0; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 5fd48a224e..a17d7c4627 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -306,6 +306,11 @@ static void zend_persist_property_info_calc(zval *zv) zend_shared_alloc_register_xlat_entry(prop, prop); ADD_SIZE_EX(sizeof(zend_property_info)); ADD_INTERNED_STRING(prop->name); + if (ZEND_TYPE_IS_NAME(prop->type)) { + zend_string *class_name = ZEND_TYPE_NAME(prop->type); + ADD_INTERNED_STRING(class_name); + prop->type = ZEND_TYPE_ENCODE_CLASS(class_name, ZEND_TYPE_ALLOW_NULL(prop->type)); + } if (ZCG(accel_directives).save_comments && prop->doc_comment) { ADD_STRING(prop->doc_comment); } @@ -326,6 +331,16 @@ static void zend_persist_class_constant_calc(zval *zv) } } +static zend_bool has_unresolved_property_types(zend_class_entry *ce) { + zend_property_info *prop; + ZEND_HASH_FOREACH_PTR(&ce->properties_info, prop) { + if (ZEND_TYPE_IS_NAME(prop->type)) { + return 1; + } + } ZEND_HASH_FOREACH_END(); + return 0; +} + static void zend_persist_class_entry_calc(zval *zv) { zend_class_entry *ce = Z_PTR_P(zv); @@ -334,6 +349,7 @@ static void zend_persist_class_entry_calc(zval *zv) ZCG(is_immutable_class) = (ce->ce_flags & ZEND_ACC_LINKED) && (ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED) && + !has_unresolved_property_types(ce) && !ZCG(current_persistent_script)->corrupted; ADD_SIZE_EX(sizeof(zend_class_entry)); @@ -371,6 +387,10 @@ static void zend_persist_class_entry_calc(zval *zv) zend_hash_persist_calc(&ce->properties_info, zend_persist_property_info_calc); + if (ce->properties_info_table) { + ADD_ARENA_SIZE(sizeof(zend_property_info *) * ce->default_properties_count); + } + if (ce->num_interfaces) { uint32_t i; |