From 1050edaef8e4c24bd194aa1cbfb14f2edca9162b Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 26 Dec 2021 10:11:59 +0100 Subject: [PATCH] Extract special function evaluation from pass1 Pass1 handles a number of special functions that can be evaluated under some circumstances. Move the core logic into a separate helper, as I believe that SCCP should reuse this. --- Zend/Optimizer/pass1.c | 97 +++--------------------- Zend/Optimizer/zend_optimizer.c | 63 +++++++++++++++ Zend/Optimizer/zend_optimizer_internal.h | 2 + 3 files changed, 74 insertions(+), 88 deletions(-) diff --git a/Zend/Optimizer/pass1.c b/Zend/Optimizer/pass1.c index 4477a0270a3..4a8f4d4f8ec 100644 --- a/Zend/Optimizer/pass1.c +++ b/Zend/Optimizer/pass1.c @@ -27,7 +27,6 @@ * - pre-evaluate constant function calls */ -#include "php.h" #include "Optimizer/zend_optimizer.h" #include "Optimizer/zend_optimizer_internal.h" #include "zend_API.h" @@ -252,94 +251,16 @@ void zend_optimizer_pass1(zend_op_array *op_array, zend_optimizer_ctx *ctx) } } - /* pre-evaluate constant functions: - constant(x) - function_exists(x) - is_callable(x) - extension_loaded(x) - */ - if (!send2_opline && - Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING) { - if (zend_string_equals_literal(Z_STR(ZEND_OP2_LITERAL(init_opline)), "function_exists") || - zend_string_equals_literal(Z_STR(ZEND_OP2_LITERAL(init_opline)), "is_callable")) { - zend_internal_function *func; - zend_string *lc_name = zend_string_tolower( - Z_STR(ZEND_OP1_LITERAL(send1_opline))); - - if ((func = zend_hash_find_ptr(EG(function_table), lc_name)) != NULL - && func->type == ZEND_INTERNAL_FUNCTION - && func->module->type == MODULE_PERSISTENT -#ifdef ZEND_WIN32 - && func->module->handle == NULL -#endif - ) { - ZVAL_TRUE(&result); - literal_dtor(&ZEND_OP2_LITERAL(init_opline)); - MAKE_NOP(init_opline); - literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); - MAKE_NOP(send1_opline); - replace_by_const_or_qm_assign(op_array, opline, &result); - } - zend_string_release_ex(lc_name, 0); - break; - } else if (zend_string_equals_literal(Z_STR(ZEND_OP2_LITERAL(init_opline)), "extension_loaded")) { - zend_string *lc_name = zend_string_tolower( - Z_STR(ZEND_OP1_LITERAL(send1_opline))); - zend_module_entry *m = zend_hash_find_ptr(&module_registry, - lc_name); - - zend_string_release_ex(lc_name, 0); - if (!m) { - if (PG(enable_dl)) { - break; - } else { - ZVAL_FALSE(&result); - } - } else { - if (m->type == MODULE_PERSISTENT -#ifdef ZEND_WIN32 - && m->handle == NULL -#endif - ) { - ZVAL_TRUE(&result); - } else { - break; - } - } - - literal_dtor(&ZEND_OP2_LITERAL(init_opline)); - MAKE_NOP(init_opline); - literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); - MAKE_NOP(send1_opline); - replace_by_const_or_qm_assign(op_array, opline, &result); - break; - } else if (zend_string_equals_literal(Z_STR(ZEND_OP2_LITERAL(init_opline)), "constant")) { - if (zend_optimizer_get_persistent_constant(Z_STR(ZEND_OP1_LITERAL(send1_opline)), &result, 1)) { - literal_dtor(&ZEND_OP2_LITERAL(init_opline)); - MAKE_NOP(init_opline); - literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); - MAKE_NOP(send1_opline); - replace_by_const_or_qm_assign(op_array, opline, &result); - } - break; - /* dirname(IS_CONST/IS_STRING) -> IS_CONST/IS_STRING */ - } else if (zend_string_equals_literal(Z_STR(ZEND_OP2_LITERAL(init_opline)), "dirname") && - IS_ABSOLUTE_PATH(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)))) { - zend_string *dirname = zend_string_init(Z_STRVAL(ZEND_OP1_LITERAL(send1_opline)), Z_STRLEN(ZEND_OP1_LITERAL(send1_opline)), 0); - ZSTR_LEN(dirname) = zend_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname)); - if (IS_ABSOLUTE_PATH(ZSTR_VAL(dirname), ZSTR_LEN(dirname))) { - ZVAL_STR(&result, dirname); - literal_dtor(&ZEND_OP2_LITERAL(init_opline)); - MAKE_NOP(init_opline); - literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); - MAKE_NOP(send1_opline); - replace_by_const_or_qm_assign(op_array, opline, &result); - } else { - zend_string_release_ex(dirname, 0); - } - break; - } + if (!send2_opline && Z_TYPE(ZEND_OP1_LITERAL(send1_opline)) == IS_STRING && + zend_optimizer_eval_special_func_call(&result, Z_STR(ZEND_OP2_LITERAL(init_opline)), Z_STR(ZEND_OP1_LITERAL(send1_opline))) == SUCCESS) { + literal_dtor(&ZEND_OP2_LITERAL(init_opline)); + MAKE_NOP(init_opline); + literal_dtor(&ZEND_OP1_LITERAL(send1_opline)); + MAKE_NOP(send1_opline); + replace_by_const_or_qm_assign(op_array, opline, &result); + break; } + /* don't collect constants after any other function call */ collect_constants = 0; break; diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index f8aee5148cb..fd04a8bffb1 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -30,6 +30,7 @@ #include "zend_call_graph.h" #include "zend_inference.h" #include "zend_dump.h" +#include "php.h" #ifndef ZEND_OPTIMIZER_MAX_REGISTERED_PASSES # define ZEND_OPTIMIZER_MAX_REGISTERED_PASSES 32 @@ -121,6 +122,68 @@ zend_result zend_optimizer_eval_strlen(zval *result, zval *op1) /* {{{ */ } /* }}} */ +zend_result zend_optimizer_eval_special_func_call( + zval *result, zend_string *name, zend_string *arg) { + if (zend_string_equals_literal(name, "function_exists") || + zend_string_equals_literal(name, "is_callable")) { + zend_string *lc_name = zend_string_tolower(arg); + zend_internal_function *func = zend_hash_find_ptr(EG(function_table), lc_name); + zend_string_release_ex(lc_name, 0); + + if (func && func->type == ZEND_INTERNAL_FUNCTION + && func->module->type == MODULE_PERSISTENT +#ifdef ZEND_WIN32 + && func->module->handle == NULL +#endif + ) { + ZVAL_TRUE(result); + return SUCCESS; + } + return FAILURE; + } + if (zend_string_equals_literal(name, "extension_loaded")) { + zend_string *lc_name = zend_string_tolower(arg); + zend_module_entry *m = zend_hash_find_ptr(&module_registry, lc_name); + zend_string_release_ex(lc_name, 0); + + if (!m) { + if (PG(enable_dl)) { + return FAILURE; + } + ZVAL_FALSE(result); + return SUCCESS; + } + + if (m->type == MODULE_PERSISTENT +#ifdef ZEND_WIN32 + && m->handle == NULL +#endif + ) { + ZVAL_TRUE(result); + return SUCCESS; + } + return FAILURE; + } + if (zend_string_equals_literal(name, "constant")) { + return zend_optimizer_get_persistent_constant(arg, result, 1) ? SUCCESS : FAILURE; + } + if (zend_string_equals_literal(name, "dirname")) { + if (!IS_ABSOLUTE_PATH(ZSTR_VAL(arg), ZSTR_LEN(arg))) { + return FAILURE; + } + + zend_string *dirname = zend_string_init(ZSTR_VAL(arg), ZSTR_LEN(arg), 0); + ZSTR_LEN(dirname) = zend_dirname(ZSTR_VAL(dirname), ZSTR_LEN(dirname)); + if (IS_ABSOLUTE_PATH(ZSTR_VAL(dirname), ZSTR_LEN(dirname))) { + ZVAL_STR(result, dirname); + return SUCCESS; + } + zend_string_release_ex(dirname, 0); + return FAILURE; + } + return FAILURE; +} + bool zend_optimizer_get_collected_constant(HashTable *constants, zval *name, zval* value) { zval *val; diff --git a/Zend/Optimizer/zend_optimizer_internal.h b/Zend/Optimizer/zend_optimizer_internal.h index 0116e150600..25d021fa98c 100644 --- a/Zend/Optimizer/zend_optimizer_internal.h +++ b/Zend/Optimizer/zend_optimizer_internal.h @@ -84,6 +84,8 @@ zend_result zend_optimizer_eval_binary_op(zval *result, zend_uchar opcode, zval zend_result zend_optimizer_eval_unary_op(zval *result, zend_uchar opcode, zval *op1); zend_result zend_optimizer_eval_cast(zval *result, uint32_t type, zval *op1); zend_result zend_optimizer_eval_strlen(zval *result, zval *op1); +zend_result zend_optimizer_eval_special_func_call( + zval *result, zend_string *name, zend_string *arg); bool zend_optimizer_update_op1_const(zend_op_array *op_array, zend_op *opline, zval *val);