fix(aot): 修复动态调用中的方法重写处理问题

- 添加了对父类参数类型调用重写方法的测试用例
- 增加了方法重写检测逻辑,避免对重写方法进行去虚拟化
- 新增 getOverrideMethodName 方法统一获取重写方法名称
- 新增 isFinalClass 方法判断类是否为 final
- 修正了 canDevirtualize 方法中关于最终类的判断逻辑
- 统一使用 getOverrideMethodName 获取完整方法名
- 优化了 SSA 稳定对象
pull/3/head
韩天峰 2 months ago
parent 979ea385ca
commit 8148ffce1e
  1. 34
      src/Php/CompilerBase.php
  2. 4
      src/Php/Context/FunctionContext.php
  3. 37
      tests/aot/dynamic_call/call-parent-return-override.phpt

@ -4702,7 +4702,10 @@ class CompilerBase extends \PhpAot\Core\Translator
$methodIsAbstract = $class && $funcName && $this->hasClass($class)
&& ($this->getMethodFlags($class, $funcName) & Modifiers::ABSTRACT);
if ($class and $funcName and !$magicMethod and !$methodIsAbstract) {
$methodIsOverridden = $class && $funcName && $this->isOverrideMethod(
$this->getOverrideMethodName($class, $funcName)
);
if ($class and $funcName and !$magicMethod and !$methodIsAbstract and !$methodIsOverridden) {
$methodPtr = $this->getMethodPtr($class, $funcName);
} else {
$methodPtr = $method;
@ -5418,6 +5421,15 @@ class CompilerBase extends \PhpAot\Core\Translator
return isset($this->classMethodOverride[$fullMethodNameLower]) and $this->classMethodOverride[$fullMethodNameLower];
}
protected function getOverrideMethodName(string $class, string $method): string
{
if (!$this->hasClass($class) && !$this->hasInterface($class)
&& !$this->isInternalClass($class) && !$this->isInternalInterface($class)) {
$class = $this->getNamespacedClassName($class);
}
return $class . '::' . $method;
}
protected function hasSubClasses(string $classNameLower): bool
{
return !empty($this->classSubClasses[$classNameLower]);
@ -5428,6 +5440,11 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->classDef && ($this->classDef->flags & Modifiers::FINAL) !== 0;
}
protected function isFinalClass(string $class): bool
{
return $this->hasClass($class) && ($this->getClass($class)->flags & Modifiers::FINAL) !== 0;
}
protected function getMethodFlags(string $class, string $method): int
{
if (!$this->hasClass($class)) {
@ -5462,7 +5479,7 @@ class CompilerBase extends \PhpAot\Core\Translator
* 2. $this->m() where m is final (can't be overridden)
* 3. $this->m() where m is private (not virtual)
* 4. $obj->m() where obj's class has no known subclasses
* 5. $obj->m() where obj is SSA-stable (single def, no escape)
* 5. $obj->m() where obj is SSA-stable and its class is final
*/
protected function canDevirtualize(string $object, string $class, string $method): bool
{
@ -5480,15 +5497,16 @@ class CompilerBase extends \PhpAot\Core\Translator
// Case 4: Typed object whose class has no known subclasses
if ($object !== 'this_' && $this->hasClass($class)) {
$classLower = strtolower($class);
if (!$this->hasSubClasses($classLower) && !$this->isInterface($class)) {
if (!$this->hasSubClasses($classLower) && !$this->isInterface($class) && !$this->isAbstractClass($class)) {
return true;
}
}
// Case 5: SSA-stable object — compile-time proven exact type
// Case 5: SSA stability proves the variable identity, not necessarily
// the runtime class. Only final classes are exact enough here.
if ($object !== 'this_' && isset($this->context->stableObjects[$object])) {
$stableClass = $this->context->stableObjects[$object];
if ($this->hasClass($stableClass) && !$this->isAbstractClass($stableClass)) {
if ($this->isFinalClass($stableClass)) {
return true;
}
}
@ -5520,11 +5538,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
if ($classDef) {
$fullMethodName = $classDef->getNamespacedName(false) . '::' . $method;
} else {
$fullMethodName = $object . '::' . $method;
}
$fullMethodName = $this->getOverrideMethodName($class, $method);
// 存在子类同名方法,尝试去虚化
if ($this->isOverrideMethod($fullMethodName)) {

@ -8,10 +8,12 @@
namespace PhpAot\Php\Context;
use PhpAot\Php\Analysis\SsaBuilder;
class FunctionContext
{
/** SSA builder for the current function. Built once per function, discarded with the context. */
public ?\PhpAot\Php\Analysis\SsaBuilder $ssaBuilder = null;
public ?SsaBuilder $ssaBuilder = null;
/** Map of SSA-stable object variable name => class name (SsaPropOptimizer). */
public array $stableObjects = [];

@ -0,0 +1,37 @@
--TEST--
call overridden method through parent parameter type
--FILE--
<?php
use native_types;
class Base
{
public function run(): string
{
return 'base';
}
}
class Impl extends Base
{
public function run(): string
{
return 'impl';
}
}
function run(Base $obj): string
{
$alias = $obj;
return $alias->run();
}
function main(): int
{
$r = run(new Impl());
echo "result: $r\n";
return $r === 'impl' ? 0 : 1;
}
?>
--EXPECT--
result: impl
Loading…
Cancel
Save