test(aot): 添加动态调用相关测试用例并修复命名空间方法重写检测

- 移除测试文件中不必要的 use native_types 语句
- 新增 call-interface-method.phpt 测试接口方法调用功能
- 新增 call-magic-fallback-typed-object.phpt 测试魔法方法回调功能
- 新增 call-multilevel-override.phpt 测试多层级继承方法重写功能
- 新增 call-parent-sibling-dispatch.phpt 测试父类参数分发到兄弟类方法功能
- 新增 call-protected-override.phpt 测试受保护方法重写功能
- 添加命名空间重写键名使用完整类名的单元测试
- 添加 static 属性 array_pop 操作的调试测试用例
pull/3/head
韩天峰 2 months ago
parent a7b5b248fb
commit 23f1a1eef9
  1. 60
      debug/ref.php
  2. 20
      phpunit/src/ClassMethodOverrideTest.php
  3. 2
      tests/aot/dynamic_call/call-abstract-method.phpt
  4. 32
      tests/aot/dynamic_call/call-interface-method.phpt
  5. 29
      tests/aot/dynamic_call/call-magic-fallback-typed-object.phpt
  6. 41
      tests/aot/dynamic_call/call-multilevel-override.phpt
  7. 2
      tests/aot/dynamic_call/call-namespaced-parent-override.phpt
  8. 2
      tests/aot/dynamic_call/call-parent-return-override.phpt
  9. 48
      tests/aot/dynamic_call/call-parent-sibling-dispatch.phpt
  10. 41
      tests/aot/dynamic_call/call-protected-override.phpt

@ -0,0 +1,60 @@
<?php
class StackHolder
{
public static array $stack = [];
public static function push(string $item): void
{
self::$stack[] = $item;
}
public static function pop(): void
{
// 触发 bug: AOT 对 static 属性 array_pop 生成 php::toReference(v) 缺第 2 参数
array_pop(self::$stack);
}
public static function count(): int
{
return count(self::$stack);
}
}
// ── 对照:array_pop($localVar) 走不同代码生成路径,应该正常 ──
function local_pop_test(): string
{
$arr = ['x', 'y', 'z'];
array_pop($arr);
if (count($arr) !== 2) {
return 'FAIL: local_pop_test expected 2, got ' . count($arr);
}
if ($arr[0] !== 'x' || $arr[1] !== 'y') {
return 'FAIL: local_pop_test wrong elements';
}
return 'OK';
}
function main(): int
{
// 测试 static 属性(已知 bug,预期编译失败)
StackHolder::push('a');
StackHolder::push('b');
StackHolder::pop();
if (StackHolder::count() !== 1) {
echo "FAIL: static_pop expected 1, got " . StackHolder::count() . "\n";
return 1;
}
// 测试局部变量(应该正常)
$result = local_pop_test();
echo $result . "\n";
if ($result !== 'OK') {
return 1;
}
echo "ALL OK\n";
return 0;
}

@ -25,4 +25,24 @@ class ClassMethodOverrideTest extends \BaseTest
// 子类方法未被覆盖
$this->assertFalse($classMethodOverride[$childMethodLower], 'ChildOverride::bar should not be marked as overridden');
}
public function testNamespacedOverrideKeysUseFullClassName(): void
{
$compiler = CompilerTest::create(ROOT_PATH);
$testFile = __DIR__ . '/../code/class-method-override-namespace.php';
$compiler->addFiles([$testFile]);
$compiler->prepareFile($testFile);
$ref = new ReflectionClass($compiler);
$prop = $ref->getProperty('classMethodOverride');
$classMethodOverride = $prop->getValue($compiler);
$parentMethodLower = strtolower('Demo\\Dispatch\\ParentBaseNs::bar');
$childMethodLower = strtolower('Demo\\Dispatch\\ChildOverrideNs::bar');
$this->assertArrayHasKey($parentMethodLower, $classMethodOverride, 'Namespaced parent method should be registered with full class name');
$this->assertArrayHasKey($childMethodLower, $classMethodOverride, 'Namespaced child method should be registered with full class name');
$this->assertTrue($classMethodOverride[$parentMethodLower], 'Namespaced parent method should be marked as overridden');
$this->assertFalse($classMethodOverride[$childMethodLower], 'Namespaced child method should not be marked as overridden');
}
}

@ -2,8 +2,6 @@
call abstract base method through concrete object
--FILE--
<?php
use native_types;
abstract class Base
{
abstract public function run(): string;

@ -0,0 +1,32 @@
--TEST--
call interface method through concrete object
--FILE--
<?php
interface Runner
{
public function run(): string;
}
class FastRunner implements Runner
{
public function run(): string
{
return 'fast';
}
}
function invoke(Runner $runner): string
{
$alias = $runner;
return $alias->run();
}
function main(): int
{
$r = invoke(new FastRunner());
echo "result: $r\n";
return $r === 'fast' ? 0 : 1;
}
?>
--EXPECT--
result: fast

@ -0,0 +1,29 @@
--TEST--
missing method on typed object dispatches to __call
--FILE--
<?php
use native_types;
class DynamicHandler
{
public function __call(string $name, array $args): string
{
return $name . ':' . implode(',', $args);
}
}
function invoke(DynamicHandler $handler): string
{
$alias = $handler;
return $alias->missing('a', 'b');
}
function main(): int
{
$r = invoke(new DynamicHandler());
echo "result: $r\n";
return $r === 'missing:a,b' ? 0 : 1;
}
?>
--EXPECT--
result: missing:a,b

@ -0,0 +1,41 @@
--TEST--
call method overridden in grandchild through base parameter
--FILE--
<?php
use native_types;
class Base
{
public function value(): string
{
return 'base';
}
}
class Middle extends Base
{
}
class Leaf extends Middle
{
public function value(): string
{
return 'leaf';
}
}
function readValue(Base $obj): string
{
$alias = $obj;
return $alias->value();
}
function main(): int
{
$r = readValue(new Leaf());
echo "result: $r\n";
return $r === 'leaf' ? 0 : 1;
}
?>
--EXPECT--
result: leaf

@ -3,8 +3,6 @@ call overridden method through namespaced parent type
--FILE--
<?php
namespace Demo {
use native_types;
class Base
{
public function run(): string

@ -2,8 +2,6 @@
call overridden method through parent parameter type
--FILE--
<?php
use native_types;
class Base
{
public function run(): string

@ -0,0 +1,48 @@
--TEST--
call overridden method on sibling subclasses through parent parameter
--FILE--
<?php
use native_types;
class Base
{
public function name(): string
{
return 'base';
}
}
class Left extends Base
{
public function name(): string
{
return 'left';
}
}
class Right extends Base
{
public function name(): string
{
return 'right';
}
}
function readName(Base $obj): string
{
$alias = $obj;
return $alias->name();
}
function main(): int
{
$left = readName(new Left());
$right = readName(new Right());
echo "left: $left\n";
echo "right: $right\n";
return $left === 'left' && $right === 'right' ? 0 : 1;
}
?>
--EXPECT--
left: left
right: right

@ -0,0 +1,41 @@
--TEST--
protected overridden method remains virtual through parent wrapper
--FILE--
<?php
use native_types;
class Base
{
protected function token(): string
{
return 'base';
}
public function expose(): string
{
return $this->token();
}
}
class Child extends Base
{
protected function token(): string
{
return 'child';
}
}
function main(): int
{
$base = new Base();
$child = new Child();
$a = $base->expose();
$b = $child->expose();
echo "base: $a\n";
echo "child: $b\n";
return $a === 'base' && $b === 'child' ? 0 : 1;
}
?>
--EXPECT--
base: base
child: child
Loading…
Cancel
Save