From 99d818f1e901d4eb411a6458a7800df071d8284a Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 7 Nov 2015 11:48:21 +0100 Subject: [PATCH] Don't propagate into ops that error on non-string To properly support this make update_opN_const fallible -- they are not always called through replace_with_const. --- .../static_prop_on_int_expr_class.phpt | 11 +++ ext/opcache/Optimizer/block_pass.c | 24 +++-- ext/opcache/Optimizer/zend_optimizer.c | 92 ++++++++++--------- .../Optimizer/zend_optimizer_internal.h | 12 +-- 4 files changed, 82 insertions(+), 57 deletions(-) create mode 100644 Zend/tests/varSyntax/static_prop_on_int_expr_class.phpt diff --git a/Zend/tests/varSyntax/static_prop_on_int_expr_class.phpt b/Zend/tests/varSyntax/static_prop_on_int_expr_class.phpt new file mode 100644 index 0000000000..c7c44d5694 --- /dev/null +++ b/Zend/tests/varSyntax/static_prop_on_int_expr_class.phpt @@ -0,0 +1,11 @@ +--TEST-- +Static property access on constexpr class evaluating to integer +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: Class name must be a valid object or a string in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/ext/opcache/Optimizer/block_pass.c b/ext/opcache/Optimizer/block_pass.c index b7e1682866..210078e7a9 100644 --- a/ext/opcache/Optimizer/block_pass.c +++ b/ext/opcache/Optimizer/block_pass.c @@ -643,13 +643,15 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, (opline->opcode != ZEND_FE_RESET_R || opline->opcode != ZEND_FE_RESET_RW) && opline->opcode != ZEND_FREE ) { - zend_op *src = VAR_SOURCE(opline->op1); + znode_op op1 = opline->op1; + zend_op *src = VAR_SOURCE(op1); zval c = ZEND_OP1_LITERAL(src); - VAR_UNSET(opline->op1); zval_copy_ctor(&c); - zend_optimizer_update_op1_const(op_array, opline, &c); - literal_dtor(&ZEND_OP1_LITERAL(src)); - MAKE_NOP(src); + if (zend_optimizer_update_op1_const(op_array, opline, &c)) { + VAR_SOURCE(op1) = NULL; + literal_dtor(&ZEND_OP1_LITERAL(src)); + MAKE_NOP(src); + } } /* T = QM_ASSIGN(C), F(T) => NOP, F(C) */ @@ -657,13 +659,15 @@ static void zend_optimize_block(zend_code_block *block, zend_op_array *op_array, VAR_SOURCE(opline->op2) && VAR_SOURCE(opline->op2)->opcode == ZEND_QM_ASSIGN && ZEND_OP1_TYPE(VAR_SOURCE(opline->op2)) == IS_CONST) { - zend_op *src = VAR_SOURCE(opline->op2); + znode_op op2 = opline->op2; + zend_op *src = VAR_SOURCE(op2); zval c = ZEND_OP1_LITERAL(src); - VAR_UNSET(opline->op2); zval_copy_ctor(&c); - zend_optimizer_update_op2_const(op_array, opline, &c); - literal_dtor(&ZEND_OP1_LITERAL(src)); - MAKE_NOP(src); + if (zend_optimizer_update_op2_const(op_array, opline, &c)) { + VAR_SOURCE(op2) = NULL; + literal_dtor(&ZEND_OP1_LITERAL(src)); + MAKE_NOP(src); + } } /* T = CAST(X, String), ECHO(T) => NOP, ECHO(X) */ diff --git a/ext/opcache/Optimizer/zend_optimizer.c b/ext/opcache/Optimizer/zend_optimizer.c index 153de18e46..9e13b6497e 100644 --- a/ext/opcache/Optimizer/zend_optimizer.c +++ b/ext/opcache/Optimizer/zend_optimizer.c @@ -132,56 +132,60 @@ static inline void drop_leading_backslash(zval *val) { } } -void zend_optimizer_update_op1_const(zend_op_array *op_array, - zend_op *opline, - zval *val) +int zend_optimizer_update_op1_const(zend_op_array *op_array, + zend_op *opline, + zval *val) { - if (opline->opcode == ZEND_FREE) { - MAKE_NOP(opline); - zval_dtor(val); - } else { - ZEND_OP1_TYPE(opline) = IS_CONST; - if (Z_TYPE_P(val) == IS_STRING) { - switch (opline->opcode) { - case ZEND_INIT_STATIC_METHOD_CALL: - case ZEND_CATCH: - case ZEND_FETCH_CONSTANT: - case ZEND_DEFINED: - case ZEND_NEW: - drop_leading_backslash(val); - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline))); - Z_CACHE_SLOT(op_array->literals[opline->op1.constant]) = op_array->cache_size; - op_array->cache_size += sizeof(void*); - zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); - break; - default: - opline->op1.constant = zend_optimizer_add_literal(op_array, val); - zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline))); - break; - } - } else { - if (opline->opcode == ZEND_CONCAT || - opline->opcode == ZEND_FAST_CONCAT) { - convert_to_string(val); + switch (opline->opcode) { + case ZEND_FREE: + MAKE_NOP(opline); + zval_dtor(val); + break; + case ZEND_INIT_STATIC_METHOD_CALL: + case ZEND_CATCH: + case ZEND_FETCH_CONSTANT: + case ZEND_DEFINED: + case ZEND_NEW: + if (Z_TYPE_P(val) != IS_STRING) { + zval_dtor(val); + return 0; } + ZEND_OP1_TYPE(opline) = IS_CONST; + drop_leading_backslash(val); opline->op1.constant = zend_optimizer_add_literal(op_array, val); - } + zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline))); + Z_CACHE_SLOT(op_array->literals[opline->op1.constant]) = op_array->cache_size; + op_array->cache_size += sizeof(void*); + zend_optimizer_add_literal_string(op_array, zend_string_tolower(Z_STR_P(val))); + break; + case ZEND_CONCAT: + case ZEND_FAST_CONCAT: + convert_to_string(val); + /* break missing intentionally */ + default: + ZEND_OP1_TYPE(opline) = IS_CONST; + opline->op1.constant = zend_optimizer_add_literal(op_array, val); + if (Z_TYPE_P(val) == IS_STRING) { + zend_string_hash_val(Z_STR(ZEND_OP1_LITERAL(opline))); + } + break; } + + return 1; } -void zend_optimizer_update_op2_const(zend_op_array *op_array, - zend_op *opline, - zval *val) +int zend_optimizer_update_op2_const(zend_op_array *op_array, + zend_op *opline, + zval *val) { - ZEND_OP2_TYPE(opline) = IS_CONST; if (opline->opcode == ZEND_INIT_FCALL) { + ZEND_OP2_TYPE(opline) = IS_CONST; zend_str_tolower(Z_STRVAL_P(val), Z_STRLEN_P(val)); opline->op2.constant = zend_optimizer_add_literal(op_array, val); zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline))); Z_CACHE_SLOT(op_array->literals[opline->op2.constant]) = op_array->cache_size; op_array->cache_size += sizeof(void*); - return; + return 1; } switch (opline->opcode) { @@ -206,11 +210,17 @@ void zend_optimizer_update_op2_const(zend_op_array *op_array, case ZEND_ADD_INTERFACE: case ZEND_ADD_TRAIT: case ZEND_INSTANCEOF: + if (Z_TYPE_P(val) != IS_STRING) { + zval_dtor(val); + return 0; + } + /* break missing intentionally */ case ZEND_INIT_DYNAMIC_CALL: drop_leading_backslash(val); break; } + ZEND_OP2_TYPE(opline) = IS_CONST; opline->op2.constant = zend_optimizer_add_literal(op_array, val); if (Z_TYPE_P(val) == IS_STRING) { zend_string_hash_val(Z_STR(ZEND_OP2_LITERAL(opline))); @@ -322,6 +332,8 @@ check_numeric: break; } } + + return 1; } int zend_optimizer_replace_by_const(zend_op_array *op_array, @@ -429,8 +441,7 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, default: break; } - zend_optimizer_update_op1_const(op_array, opline, val); - break; + return zend_optimizer_update_op1_const(op_array, opline, val); } if (ZEND_OP2_TYPE(opline) == type && @@ -442,8 +453,7 @@ int zend_optimizer_replace_by_const(zend_op_array *op_array, default: break; } - zend_optimizer_update_op2_const(op_array, opline, val); - break; + return zend_optimizer_update_op2_const(op_array, opline, val); } opline++; } diff --git a/ext/opcache/Optimizer/zend_optimizer_internal.h b/ext/opcache/Optimizer/zend_optimizer_internal.h index ca452670d7..90df3cfadc 100644 --- a/ext/opcache/Optimizer/zend_optimizer_internal.h +++ b/ext/opcache/Optimizer/zend_optimizer_internal.h @@ -118,12 +118,12 @@ int zend_optimizer_get_persistent_constant(zend_string *name, zval *result, int void zend_optimizer_collect_constant(zend_optimizer_ctx *ctx, zval *name, zval* value); int zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value); int zend_optimizer_lookup_cv(zend_op_array *op_array, zend_string* name); -void zend_optimizer_update_op1_const(zend_op_array *op_array, - zend_op *opline, - zval *val); -void zend_optimizer_update_op2_const(zend_op_array *op_array, - zend_op *opline, - zval *val); +int zend_optimizer_update_op1_const(zend_op_array *op_array, + zend_op *opline, + zval *val); +int zend_optimizer_update_op2_const(zend_op_array *op_array, + zend_op *opline, + zval *val); int zend_optimizer_replace_by_const(zend_op_array *op_array, zend_op *opline, zend_uchar type, -- 2.40.0