diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2019-01-04 10:49:23 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2019-01-04 10:49:23 +0100 |
commit | 290adc41321a6fb0dfe71f1a8058a87ae4c6cab1 (patch) | |
tree | 579bd8e22a3251e007f5af47b6f8717e35e536a0 | |
parent | 03094c7af304f6fa48da641e20f91b6ec7f9876f (diff) | |
download | php-git-290adc41321a6fb0dfe71f1a8058a87ae4c6cab1.tar.gz |
Introduce separate ZEND_AST_CLASS_NAME AST node
Instead of representing this as a ZEND_AST_CLASS_CONST with a
"class" constant name.
Class constants and ::class are unrelated features that happen to
share syntax, so represent and handle them separately.
-rw-r--r-- | Zend/tests/class_name_of_var.phpt | 11 | ||||
-rw-r--r-- | Zend/zend_ast.c | 14 | ||||
-rw-r--r-- | Zend/zend_ast.h | 2 | ||||
-rw-r--r-- | Zend/zend_compile.c | 112 | ||||
-rw-r--r-- | Zend/zend_language_parser.y | 4 |
5 files changed, 85 insertions, 58 deletions
diff --git a/Zend/tests/class_name_of_var.phpt b/Zend/tests/class_name_of_var.phpt new file mode 100644 index 0000000000..29f19e9fac --- /dev/null +++ b/Zend/tests/class_name_of_var.phpt @@ -0,0 +1,11 @@ +--TEST-- +$var::class is not supported +--FILE-- +<?php + +$obj = new stdClass; +var_dump($obj::class); + +?> +--EXPECTF-- +Fatal error: Cannot use ::class with dynamic class name in %s on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 834696c2ca..b691dce0e0 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -102,6 +102,16 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, ze return (zend_ast *) ast; } +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class, zend_ast *name) { + zend_string *name_str = zend_ast_get_str(name); + if (zend_string_equals_literal_ci(name_str, "class")) { + zend_string_release(name_str); + return zend_ast_create(ZEND_AST_CLASS_NAME, class); + } else { + return zend_ast_create(ZEND_AST_CLASS_CONST, class, name); + } +} + ZEND_API zend_ast *zend_ast_create_decl( zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3 @@ -1606,6 +1616,10 @@ simple_list: smart_str_appends(str, "::"); zend_ast_export_name(str, ast->child[1], 0, indent); break; + case ZEND_AST_CLASS_NAME: + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + smart_str_appends(str, "::class"); + break; case ZEND_AST_ASSIGN: BINARY_OP(" = ", 90, 91, 90); case ZEND_AST_ASSIGN_REF: BINARY_OP(" =& ", 90, 91, 90); case ZEND_AST_ASSIGN_OP: diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index db1f51b378..78d879a3c6 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -87,6 +87,7 @@ enum _zend_ast_kind { ZEND_AST_POST_INC, ZEND_AST_POST_DEC, ZEND_AST_YIELD_FROM, + ZEND_AST_CLASS_NAME, ZEND_AST_GLOBAL, ZEND_AST_UNSET, @@ -200,6 +201,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_str(zend_string *str ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_zval_from_long(zend_long lval); ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_constant(zend_string *name, zend_ast_attr attr); +ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast *class, zend_ast *name); #if ZEND_AST_SPEC # define ZEND_AST_SPEC_CALL(name, ...) \ diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index af6a3f1332..a12de01990 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1366,21 +1366,12 @@ static void zend_ensure_valid_class_fetch_type(uint32_t fetch_type) /* {{{ */ } /* }}} */ -static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast, zend_ast *name_ast, zend_bool constant) /* {{{ */ +static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_ast *class_ast, zend_bool constant) /* {{{ */ { uint32_t fetch_type; - if (name_ast->kind != ZEND_AST_ZVAL) { - return 0; - } - - if (!zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "class")) { - return 0; - } - if (class_ast->kind != ZEND_AST_ZVAL) { - zend_error_noreturn(E_COMPILE_ERROR, - "Dynamic class names are not allowed in compile-time ::class fetch"); + zend_error_noreturn(E_COMPILE_ERROR, "Cannot use ::class with dynamic class name"); } fetch_type = zend_get_class_fetch_type(zend_ast_get_str(class_ast)); @@ -1390,10 +1381,9 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a case ZEND_FETCH_CLASS_SELF: if (CG(active_class_entry) && zend_is_scope_known()) { ZVAL_STR_COPY(zv, CG(active_class_entry)->name); - } else { - ZVAL_NULL(zv); + return 1; } - return 1; + return 0; case ZEND_FETCH_CLASS_STATIC: case ZEND_FETCH_CLASS_PARENT: if (constant) { @@ -1401,10 +1391,8 @@ static zend_bool zend_try_compile_const_expr_resolve_class_name(zval *zv, zend_a "%s::class cannot be used for compile-time class name resolution", fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent" ); - } else { - ZVAL_NULL(zv); } - return 1; + return 0; case ZEND_FETCH_CLASS_DEFAULT: ZVAL_STR(zv, zend_resolve_class_name_ast(class_ast)); return 1; @@ -7689,16 +7677,6 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */ znode class_node, const_node; zend_op *opline; - if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast, const_ast, 0)) { - if (Z_TYPE(result->u.constant) == IS_NULL) { - zend_op *opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL); - opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast)); - } else { - result->op_type = IS_CONST; - } - return; - } - zend_eval_const_expr(&ast->child[0]); zend_eval_const_expr(&ast->child[1]); @@ -7716,10 +7694,6 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */ } zend_string_release_ex(resolved_name, 0); } - if (const_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(const_ast), "class")) { - zend_error_noreturn(E_COMPILE_ERROR, - "Dynamic class names are not allowed in compile-time ::class fetch"); - } zend_compile_class_ref(&class_node, class_ast, ZEND_FETCH_CLASS_EXCEPTION); @@ -7733,6 +7707,21 @@ void zend_compile_class_const(znode *result, zend_ast *ast) /* {{{ */ } /* }}} */ +void zend_compile_class_name(znode *result, zend_ast *ast) /* {{{ */ +{ + zend_ast *class_ast = ast->child[0]; + zend_op *opline; + + if (zend_try_compile_const_expr_resolve_class_name(&result->u.constant, class_ast, 0)) { + result->op_type = IS_CONST; + return; + } + + opline = zend_emit_op_tmp(result, ZEND_FETCH_CLASS_NAME, NULL, NULL); + opline->op1.num = zend_get_class_fetch_type(zend_ast_get_str(class_ast)); +} +/* }}} */ + void zend_compile_resolve_class_name(znode *result, zend_ast *ast) /* {{{ */ { zend_ast *name_ast = ast->child[0]; @@ -7924,6 +7913,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_CLASS_NAME || kind == ZEND_AST_MAGIC_CONST || kind == ZEND_AST_COALESCE; } /* }}} */ @@ -7936,7 +7926,6 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ zend_string *class_name; zend_string *const_name = zend_ast_get_str(const_ast); zend_string *name; - zval result; int fetch_type; if (class_ast->kind != ZEND_AST_ZVAL) { @@ -7944,11 +7933,6 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ "Dynamic class names are not allowed in compile-time class constant references"); } - if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, const_ast, 1)) { - *ast_ptr = zend_ast_create_zval(&result); - return; - } - class_name = zend_ast_get_str(class_ast); fetch_type = zend_get_class_fetch_type(class_name); @@ -7973,6 +7957,19 @@ void zend_compile_const_expr_class_const(zend_ast **ast_ptr) /* {{{ */ } /* }}} */ +void zend_compile_const_expr_class_name(zend_ast **ast_ptr) /* {{{ */ +{ + zend_ast *class_ast = (*ast_ptr)->child[0]; + uint32_t fetch_type = zend_get_class_fetch_type(zend_ast_get_str(class_ast)); + + /* If we reach here, ::class should have either been constant evaluated or replaced + * by a AST_MAGIC_CONST, so only the error case is left here. */ + zend_error_noreturn(E_COMPILE_ERROR, + "%s::class cannot be used for compile-time class name resolution", + fetch_type == ZEND_FETCH_CLASS_STATIC ? "static" : "parent" + ); +} + void zend_compile_const_expr_const(zend_ast **ast_ptr) /* {{{ */ { zend_ast *ast = *ast_ptr; @@ -8024,6 +8021,9 @@ void zend_compile_const_expr(zend_ast **ast_ptr) /* {{{ */ case ZEND_AST_CLASS_CONST: zend_compile_const_expr_class_const(ast_ptr); break; + case ZEND_AST_CLASS_NAME: + zend_compile_const_expr_class_name(ast_ptr); + break; case ZEND_AST_CONST: zend_compile_const_expr_const(ast_ptr); break; @@ -8312,6 +8312,9 @@ void zend_compile_expr(znode *result, zend_ast *ast) /* {{{ */ case ZEND_AST_CLASS_CONST: zend_compile_class_const(result, ast); return; + case ZEND_AST_CLASS_NAME: + zend_compile_class_name(result, ast); + return; case ZEND_AST_ENCAPS_LIST: zend_compile_encaps_list(result, ast); return; @@ -8605,32 +8608,16 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ } case ZEND_AST_CLASS_CONST: { - zend_ast *class_ast = ast->child[0]; - zend_ast *name_ast = ast->child[1]; + zend_ast *class_ast; + zend_ast *name_ast; zend_string *resolved_name; - if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, name_ast, 0)) { - if (Z_TYPE(result) == IS_NULL) { - if (zend_get_class_fetch_type(zend_ast_get_str(class_ast)) == ZEND_FETCH_CLASS_SELF) { - zend_ast_destroy(ast); - *ast_ptr = zend_ast_create_ex(ZEND_AST_MAGIC_CONST, T_CLASS_C); - } - return; - } - break; - } - zend_eval_const_expr(&ast->child[0]); zend_eval_const_expr(&ast->child[1]); class_ast = ast->child[0]; name_ast = ast->child[1]; - if (name_ast->kind == ZEND_AST_ZVAL && zend_string_equals_literal_ci(zend_ast_get_str(name_ast), "class")) { - zend_error_noreturn(E_COMPILE_ERROR, - "Dynamic class names are not allowed in compile-time ::class fetch"); - } - if (class_ast->kind != ZEND_AST_ZVAL || name_ast->kind != ZEND_AST_ZVAL) { return; } @@ -8645,7 +8632,20 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */ zend_string_release_ex(resolved_name, 0); break; } - + case ZEND_AST_CLASS_NAME: + { + zend_ast *class_ast = ast->child[0]; + if (zend_try_compile_const_expr_resolve_class_name(&result, class_ast, 0)) { + break; + } + /* TODO We should not use AST_MAGIC_CONST for this, because the semantics are slightly + * different. */ + if (zend_get_class_fetch_type(zend_ast_get_str(class_ast)) == ZEND_FETCH_CLASS_SELF) { + zend_ast_destroy(ast); + *ast_ptr = zend_ast_create_ex(ZEND_AST_MAGIC_CONST, T_CLASS_C); + } + return; + } default: return; } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index cf6f066260..ec9ba82193 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -1095,9 +1095,9 @@ scalar: constant: name { $$ = zend_ast_create(ZEND_AST_CONST, $1); } | class_name T_PAAMAYIM_NEKUDOTAYIM identifier - { $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $3); } + { $$ = zend_ast_create_class_const_or_name($1, $3); } | variable_class_name T_PAAMAYIM_NEKUDOTAYIM identifier - { $$ = zend_ast_create(ZEND_AST_CLASS_CONST, $1, $3); } + { $$ = zend_ast_create_class_const_or_name($1, $3); } ; optional_expr: |