diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index 3f54db9f09f..823277694b4 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -792,6 +792,42 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_ } } +static bool zend_optimizer_ignore_class(zval *ce_zv, zend_string *filename) +{ + zend_class_entry *ce = Z_PTR_P(ce_zv); + + if (ce->ce_flags & ZEND_ACC_PRELOADED) { + Bucket *ce_bucket = (Bucket*)((uintptr_t)ce_zv - XtOffsetOf(Bucket, val)); + size_t offset = ce_bucket - EG(class_table)->arData; + if (offset < EG(persistent_classes_count)) { + return false; + } + } + return ce->type == ZEND_USER_CLASS + && (!ce->info.user.filename || ce->info.user.filename != filename); +} + +static bool zend_optimizer_ignore_function(zval *fbc_zv, zend_string *filename) +{ + zend_function *fbc = Z_PTR_P(fbc_zv); + + if (fbc->type == ZEND_INTERNAL_FUNCTION) { + return false; + } else if (fbc->type == ZEND_USER_FUNCTION) { + if (fbc->op_array.fn_flags & ZEND_ACC_PRELOADED) { + Bucket *fbc_bucket = (Bucket*)((uintptr_t)fbc_zv - XtOffsetOf(Bucket, val)); + size_t offset = fbc_bucket - EG(function_table)->arData; + if (offset < EG(persistent_functions_count)) { + return false; + } + } + return !fbc->op_array.filename || fbc->op_array.filename != filename; + } else { + ZEND_ASSERT(fbc->type == ZEND_EVAL_CODE); + return true; + } +} + zend_class_entry *zend_optimizer_get_class_entry( const zend_script *script, const zend_op_array *op_array, zend_string *lcname) { zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL; @@ -799,11 +835,9 @@ zend_class_entry *zend_optimizer_get_class_entry( return ce; } - ce = zend_hash_find_ptr(CG(class_table), lcname); - if (ce - && (ce->type == ZEND_INTERNAL_CLASS - || (op_array && ce->info.user.filename == op_array->filename))) { - return ce; + zval *ce_zv = zend_hash_find(CG(class_table), lcname); + if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array ? op_array->filename : NULL)) { + return Z_PTR_P(ce_zv); } if (op_array && op_array->scope && zend_string_equals_ci(op_array->scope->name, lcname)) { @@ -844,15 +878,9 @@ const zend_class_constant *zend_fetch_class_const_info( if (script) { ce = zend_optimizer_get_class_entry(script, op_array, Z_STR_P(op1 + 1)); } else { - zend_class_entry *tmp = zend_hash_find_ptr(EG(class_table), Z_STR_P(op1 + 1)); - if (tmp != NULL) { - if (tmp->type == ZEND_INTERNAL_CLASS) { - ce = tmp; - } else if (tmp->type == ZEND_USER_CLASS - && tmp->info.user.filename - && tmp->info.user.filename == op_array->filename) { - ce = tmp; - } + zval *ce_zv = zend_hash_find(EG(class_table), Z_STR_P(op1 + 1)); + if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array->filename)) { + ce = Z_PTR_P(ce_zv); } } } @@ -897,15 +925,12 @@ zend_function *zend_optimizer_get_called_func( { zend_string *function_name = Z_STR_P(CRT_CONSTANT(opline->op2)); zend_function *func; + zval *func_zv; if (script && (func = zend_hash_find_ptr(&script->function_table, function_name)) != NULL) { return func; - } else if ((func = zend_hash_find_ptr(EG(function_table), function_name)) != NULL) { - if (func->type == ZEND_INTERNAL_FUNCTION) { - return func; - } else if (func->type == ZEND_USER_FUNCTION && - func->op_array.filename && - func->op_array.filename == op_array->filename) { - return func; + } else if ((func_zv = zend_hash_find(EG(function_table), function_name)) != NULL) { + if (!zend_optimizer_ignore_function(func_zv, op_array->filename)) { + return Z_PTR_P(func_zv); } } break; @@ -915,15 +940,12 @@ zend_function *zend_optimizer_get_called_func( if (opline->op2_type == IS_CONST && Z_TYPE_P(CRT_CONSTANT(opline->op2)) == IS_STRING) { zval *function_name = CRT_CONSTANT(opline->op2) + 1; zend_function *func; + zval *func_zv; if (script && (func = zend_hash_find_ptr(&script->function_table, Z_STR_P(function_name)))) { return func; - } else if ((func = zend_hash_find_ptr(EG(function_table), Z_STR_P(function_name))) != NULL) { - if (func->type == ZEND_INTERNAL_FUNCTION) { - return func; - } else if (func->type == ZEND_USER_FUNCTION && - func->op_array.filename && - func->op_array.filename == op_array->filename) { - return func; + } else if ((func_zv = zend_hash_find(EG(function_table), Z_STR_P(function_name))) != NULL) { + if (!zend_optimizer_ignore_function(func_zv, op_array->filename)) { + return Z_PTR_P(func_zv); } } } diff --git a/ext/opcache/tests/gh15021.phpt b/ext/opcache/tests/gh15021.phpt new file mode 100644 index 00000000000..81bf48cde22 --- /dev/null +++ b/ext/opcache/tests/gh15021.phpt @@ -0,0 +1,31 @@ +--TEST-- +GH-15021: Optimizer only relies on preloaded top-level symbols +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.preload={PWD}/gh15021_preload.inc +--EXTENSIONS-- +opcache +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) diff --git a/ext/opcache/tests/gh15021_a.inc b/ext/opcache/tests/gh15021_a.inc new file mode 100644 index 00000000000..08a4e3aa448 --- /dev/null +++ b/ext/opcache/tests/gh15021_a.inc @@ -0,0 +1,7 @@ + +--FILE-- + +--EXPECTF-- +$_main: + ; (lines=1, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; $PRELOAD$:0-0 +0000 RETURN null + +foo: + ; (lines=1, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spreload_optimizer.inc:3-5 +0000 RETURN int(42) + +$_main: + ; (lines=1, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spreload_optimizer.inc:1-6 +0000 RETURN int(1) + +$_main: + ; (lines=2, args=0, vars=0, tmps=%d) + ; (after optimizer) + ; %spreload_optimizer.php:1-4 +0000 ECHO string("42") +0001 RETURN int(1) +42 diff --git a/ext/zend_test/tests/observer_preload.phpt b/ext/zend_test/tests/observer_preload.phpt index a58ec032d44..042c9e088e5 100644 --- a/ext/zend_test/tests/observer_preload.phpt +++ b/ext/zend_test/tests/observer_preload.phpt @@ -10,7 +10,8 @@ if (PHP_OS_FAMILY == 'Windows') die('skip Preloading is not supported on Windows --INI-- opcache.enable=1 opcache.enable_cli=1 -opcache.optimization_level=-1 +; Disable inlining pass +opcache.optimization_level=0x7ffe3fff opcache.preload={PWD}/observer_preload.inc opcache.file_cache= opcache.file_cache_only=0