From 9f3eab44dfc99ac032dbfc84418925ee3adcd175 Mon Sep 17 00:00:00 2001 From: =?utf8?q?M=C3=A1rcio=20Almada?= Date: Sun, 17 Apr 2016 04:27:15 -0400 Subject: [PATCH] allow null coalescing (??) on constant expressions --- Zend/tests/constant_expressions_coalesce.phpt | 45 +++++++++++++++++++ Zend/zend_ast.c | 39 +++++++++++++++- Zend/zend_compile.c | 2 +- Zend/zend_compile.h | 2 + Zend/zend_execute.c | 6 +++ Zend/zend_execute.h | 1 + 6 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/constant_expressions_coalesce.phpt diff --git a/Zend/tests/constant_expressions_coalesce.phpt b/Zend/tests/constant_expressions_coalesce.phpt new file mode 100644 index 0000000000..aa40bd68e2 --- /dev/null +++ b/Zend/tests/constant_expressions_coalesce.phpt @@ -0,0 +1,45 @@ +--TEST-- +Constant expressions with null coalescing operator ?? +--FILE-- + [[]]]; + +const T_1 = null ?? A[1]['undefined']['index'] ?? 1; +const T_2 = null ?? A['undefined']['index'] ?? 2; +const T_3 = null ?? A[1][0][2] ?? 3; +const T_4 = A[1][0][2] ?? 4; + +var_dump(T_1); +var_dump(T_2); +var_dump(T_3); +var_dump(T_4); + +var_dump((function(){ static $var = null ?? A[1]['undefined']['index'] ?? 1; return $var; })()); +var_dump((function(){ static $var = null ?? A['undefined']['index'] ?? 2; return $var; })()); +var_dump((function(){ static $var = null ?? A[1][0][2] ?? 3; return $var; })()); +var_dump((function(){ static $var = A[1][0][2] ?? 4; return $var; })()); + +var_dump((new class { public $var = null ?? A[1]['undefined']['index'] ?? 1; })->var); +var_dump((new class { public $var = null ?? A['undefined']['index'] ?? 2; })->var); +var_dump((new class { public $var = null ?? A[1][0][2] ?? 3; })->var); +var_dump((new class { public $var = A[1][0][2] ?? 4; })->var); + +const D = [][] ?? 1; + +?> +--EXPECTF-- +int(1) +int(2) +int(3) +int(4) +int(1) +int(2) +int(3) +int(4) +int(1) +int(2) +int(3) +int(4) + +Fatal error: Cannot use [] for reading in %s.php on line 25 diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index cfcd636269..4d3678f0b6 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -337,6 +337,30 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc zval_dtor(&op1); } break; + case ZEND_AST_COALESCE: + if (ast->child[0]->kind == ZEND_AST_DIM) { + ast->child[0]->attr = ZEND_DIM_IS; + } + + if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) { + ret = FAILURE; + break; + } + if (Z_TYPE(op1) > IS_NULL) { + *result = op1; + } else { + if (ast->child[1]->kind == ZEND_AST_DIM) { + ast->child[1]->attr = ZEND_DIM_IS; + } + + if (UNEXPECTED(zend_ast_evaluate(result, ast->child[1], scope) != SUCCESS)) { + zval_dtor(&op1); + ret = FAILURE; + break; + } + zval_dtor(&op1); + } + break; case ZEND_AST_UNARY_PLUS: if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[0], scope) != SUCCESS)) { ret = FAILURE; @@ -385,6 +409,14 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc } break; case ZEND_AST_DIM: + if (ast->child[1] == NULL) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use [] for reading"); + } + + if (ast->attr == ZEND_DIM_IS && ast->child[0]->kind == ZEND_AST_DIM) { + ast->child[0]->attr = ZEND_DIM_IS; + } + if (UNEXPECTED(zend_ast_evaluate(&op1, ast->child[0], scope) != SUCCESS)) { ret = FAILURE; } else if (UNEXPECTED(zend_ast_evaluate(&op2, ast->child[1], scope) != SUCCESS)) { @@ -393,7 +425,12 @@ ZEND_API int zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *sc } else { zval tmp; - zend_fetch_dimension_by_zval(&tmp, &op1, &op2); + if (ast->attr == ZEND_DIM_IS) { + zend_fetch_dimension_by_zval_is(&tmp, &op1, &op2, IS_CONST); + } else { + zend_fetch_dimension_by_zval(&tmp, &op1, &op2); + } + if (UNEXPECTED(Z_ISREF(tmp))) { ZVAL_DUP(result, Z_REFVAL(tmp)); } else { diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 89fd0e520e..18836d6418 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6833,7 +6833,7 @@ zend_bool zend_is_allowed_in_const_expr(zend_ast_kind kind) /* {{{ */ || kind == ZEND_AST_CONDITIONAL || kind == ZEND_AST_DIM || kind == ZEND_AST_ARRAY || kind == ZEND_AST_ARRAY_ELEM || kind == ZEND_AST_CONST || kind == ZEND_AST_CLASS_CONST - || kind == ZEND_AST_MAGIC_CONST; + || kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE; } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 1bf654eb92..9e30f55bdc 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -889,6 +889,8 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name); #define ZEND_SEND_BY_REF 1 #define ZEND_SEND_PREFER_REF 2 +#define ZEND_DIM_IS 1 + static zend_always_inline int zend_check_arg_send_type(const zend_function *zf, uint32_t arg_num, uint32_t mask) { arg_num--; diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 8253fd5d85..324cf82e2b 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1912,6 +1912,12 @@ ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval * zend_fetch_dimension_address_read_R(result, container, dim, IS_TMP_VAR); } +ZEND_API void zend_fetch_dimension_by_zval_is(zval *result, zval *container, zval *dim, int dim_type) +{ + zend_fetch_dimension_address_read(result, container, dim, dim_type, BP_VAR_IS, 1); +} + + static zend_always_inline void zend_fetch_property_address(zval *result, zval *container, uint32_t container_op_type, zval *prop_ptr, uint32_t prop_op_type, void **cache_slot, int type) { if (container_op_type != IS_UNUSED && UNEXPECTED(Z_TYPE_P(container) != IS_OBJECT)) { diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 83336061e4..c47667b8f0 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -296,6 +296,7 @@ ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, con void zend_verify_abstract_class(zend_class_entry *ce); ZEND_API void zend_fetch_dimension_by_zval(zval *result, zval *container, zval *dim); +ZEND_API void zend_fetch_dimension_by_zval_is(zval *result, zval *container, zval *dim, int dim_type); ZEND_API zval* zend_get_compiled_variable_value(const zend_execute_data *execute_data_ptr, uint32_t var); -- 2.40.0