From 36cf7dbf16b0688264c0c349925a1709474cd728 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 10 Apr 2026 15:04:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(aot):=20=E6=B7=BB=E5=8A=A0=E5=AF=B9?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E8=B5=8B=E5=80=BC=E5=92=8C=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E8=B5=8B=E5=80=BC=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了引用赋值功能,支持变量间的引用传递 - 添加了列表赋值解析逻辑,支持数组解构赋值操作 - 新增 isAssignExpr 方法用于判断赋值表达式类型 - 重构了赋值相关的编译逻辑,提高代码可读性 - 添加了多个测试用例验证引用和列表赋值功能 - 修复了动态调用中的参数展开问题 --- src/Php/AstNodeType.php | 5 ++ src/Php/CompilerBase.php | 112 ++++++++++++++++-------------- tests/aot/class_extends.phpt | 91 ++++++++++++++++++++++++ tests/aot/dynamic_call/vargs.phpt | 27 +++++++ tests/aot/ref/001.phpt | 109 ++++++++++++++++++++++++++++- tests/aot/ref/003.phpt | 43 ++++++++++++ tests/aot/ref/004.phpt | 39 +++++++++++ 7 files changed, 372 insertions(+), 54 deletions(-) create mode 100644 tests/aot/class_extends.phpt create mode 100644 tests/aot/dynamic_call/vargs.phpt create mode 100644 tests/aot/ref/003.phpt create mode 100644 tests/aot/ref/004.phpt diff --git a/src/Php/AstNodeType.php b/src/Php/AstNodeType.php index 46f9b2a1..e77de8ed 100644 --- a/src/Php/AstNodeType.php +++ b/src/Php/AstNodeType.php @@ -100,6 +100,11 @@ trait AstNodeType return $expr instanceof Expr\AssignOp or $expr instanceof Expr\Assign; } + protected function isAssignExpr(NodeAbstract $expr): bool + { + return $expr instanceof Expr\Assign; + } + protected function isCallExpr(NodeAbstract $expr): bool { return $expr instanceof Expr\FuncCall diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index f447b905..d6925c06 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -1159,7 +1159,7 @@ class CompilerBase extends \PhpAot\Core\Translator { $chain[] = $left; $next = $right; - while ($next->getType() === 'Expr_Assign') { + while ($this->isAssignExpr($next)) { $var = $next->var; $chain[] = $var; $next = $next->expr; @@ -1196,42 +1196,46 @@ class CompilerBase extends \PhpAot\Core\Translator { $left = $v->var; $right = $v->expr; - - if ($right->getType() === 'Expr_Assign') { + if ($this->isAssignExpr($right)) { return $this->parseRightAssociativeAssign($left, $right); } return $this->parseAssignFinally($left, $right); } - protected function parseAssignFinally(Expr $left, Expr $right): string + protected function parseAssignToList(Expr $left, Expr $right): string { - if ($left instanceof Expr\List_) { - $items = $left->items; - $code = '{'; - $this->indentLevel++; - $tmpVar = $this->genTmpVarName(); - $this->addLocalVar($tmpVar, self::TYPE_VAR); - $code .= $this->getIndent() . $tmpVar . ' = ' . $this->parseExpr($right) . '; '; - foreach ($items as $k => $item) { - if (!$item) { - continue; - } - if ($item instanceof Expr\ArrayItem) { - $oriInAssignExpr = $this->context->inAssignExpr; - $this->context->inAssignExpr = true; - $var = $this->parseIdentifier($item->value); - $this->context->inAssignExpr = $oriInAssignExpr; - if ($this->isVarExpr($item->value) and !$this->hasVar($var)) { - $this->addLocalVar($var, self::TYPE_VAR); - } - $code .= "{$var} = {$tmpVar}.item({$k}); "; - } else { - abort($item); + $items = $left->items; + $code = '{'; + $this->indentLevel++; + $tmpVar = $this->genTmpVarName(); + $this->addLocalVar($tmpVar, self::TYPE_VAR); + $code .= $this->getIndent() . $tmpVar . ' = ' . $this->parseExpr($right) . '; '; + foreach ($items as $k => $item) { + if (!$item) { + continue; + } + if ($item instanceof Expr\ArrayItem) { + $oriInAssignExpr = $this->context->inAssignExpr; + $this->context->inAssignExpr = true; + $var = $this->parseIdentifier($item->value); + $this->context->inAssignExpr = $oriInAssignExpr; + if ($this->isVarExpr($item->value) and !$this->hasVar($var)) { + $this->addLocalVar($var, self::TYPE_VAR); } + $code .= "{$var} = {$tmpVar}.item({$k}); "; + } else { + abort($item); } - $this->indentLevel--; + } + $this->indentLevel--; - return $code . '}'; + return $code . '}'; + } + + protected function parseAssignFinally(Expr $left, Expr $right): string + { + if ($left instanceof Expr\List_) { + return $this->parseAssignToList($left, $right); } $oriInAssignExpr = $this->context->inAssignExpr; @@ -3786,38 +3790,44 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseAssignRef(Expr\AssignRef $expr): string { - if (!$this->isVarExpr($expr->var)) { - $this->fatalError($expr, 'Cannot assign reference to non-variable'); - } - $this->context->inAssignExpr = true; $left = $this->parseIdentifier($expr->var); $this->context->inAssignExpr = false; - if (!$this->hasVar($left)) { - $this->addLocalVar($left, self::TYPE_REF); - } else { - $type = $this->getVarType($left); - if ($type !== self::TYPE_REF) { - $this->fatalError($expr, 'Cannot assign reference to variable of type ' . $type); + + if ($this->isVarExpr($expr->var)) { + if (!$this->hasVar($left)) { + $this->addLocalVar($left, self::TYPE_REF); + } else { + $type = $this->getVarType($left); + if ($type !== self::TYPE_REF) { + $this->fatalError($expr, 'Cannot assign reference to variable of type ' . $type); + } } } + + $tmpVar = $this->addTmpVar(self::TYPE_REF); + $rightExpr = ''; + if ($this->isVarExpr($expr->expr)) { - return $left . ' = ' . $this->parseIdentifier($expr->expr) . '.toReference()'; - } - if ($expr->expr->getType() === self::EXPR_ARRAY_DIM_FETCH) { - return $left . ' = ' . $this->parseIdentifier($expr->expr); - } - if ($this->isPropertyFetch($expr->expr)) { - $left = $this->parseIdentifier($expr->var); + $rightExpr = $tmpVar . ' = ' . $this->parseIdentifier($expr->expr) . '.toReference()'; + } elseif ($this->isPropertyFetch($expr->expr)) { + $left = $this->parseIdentifier($expr->var); $object = $this->parseExpr($expr->expr->var); - $prop = $this->identifierToStr($expr->expr->name); - - return $left . ' = ' . $object . '.attrRef(' . $prop . ')'; + $prop = $this->identifierToStr($expr->expr->name); + $rightExpr = $tmpVar . ' = ' . $object . '.attrRef(' . $prop . ')'; + } elseif ($this->isArrayDimFetch($expr->expr)) { + $left = $this->parseIdentifier($expr->var); + $array = $this->parseIdentifier($expr->expr->var); + if ($expr->expr->dim == null) { + $this->fatalError($expr, 'Cannot assign reference to array dim fetch without dim'); + } + $rightExpr = $tmpVar . ' = ' . $array . '.itemRef(' . $this->parseIdentifier($expr->expr->dim) . ')'; + } else { + $this->fatalError($expr, 'Cannot assign reference to ' . $this->parseIdentifier($expr->expr)); } - $tmpVar = $this->addTmpVar(self::TYPE_VAR); - $this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($expr->expr) . ';'; - return $left . ' = ' . $tmpVar . '.toReference()'; + $this->context->beforeStmtLines[] = $rightExpr . ';'; + return $left . ' = &' . $tmpVar; } protected function parseMethodCall(Expr\MethodCall $expr): string diff --git a/tests/aot/class_extends.phpt b/tests/aot/class_extends.phpt new file mode 100644 index 00000000..e36ca32c --- /dev/null +++ b/tests/aot/class_extends.phpt @@ -0,0 +1,91 @@ +--TEST-- +class extends +--FILE-- +bar2(); + } + + function bar2() + { + var_dump(__METHOD__); + } +} + +class C extends B { + public function __construct() + { + echo "C::__construct()\n"; + parent::__construct(); + } +} + +function foo_test($a, $b, $c) { + return 1; +} + +function foo2(): void +{ + var_dump(__FUNCTION__); + return; + var_dump(__FUNCTION__); +} + +function main() +{ + var_dump(foo_test(1, 2, 3)); + $o = new B; + $o->foo(); + + $c = new C; + $c->offsetSet(0, 1); + $c->offsetSet(1, 2); + var_dump($c); + var_dump($c->foo()); + + foo2(); +} +?> +--EXPECT-- +int(1) +B::__construct() +A::__construct() +string(7) "B::bar2" +C::__construct() +B::__construct() +A::__construct() +object(C)#2 (1) { + ["storage":"ArrayObject":private]=> + array(2) { + [0]=> + int(1) + [1]=> + int(2) + } +} +string(7) "B::bar2" +NULL +string(4) "foo2" + diff --git a/tests/aot/dynamic_call/vargs.phpt b/tests/aot/dynamic_call/vargs.phpt new file mode 100644 index 00000000..9b82d729 --- /dev/null +++ b/tests/aot/dynamic_call/vargs.phpt @@ -0,0 +1,27 @@ +--TEST-- +named args +--FILE-- + +--EXPECT-- +array(6) { + [0]=> + int(1) + [1]=> + int(3) + [2]=> + int(5) + [3]=> + int(12) + [4]=> + int(33) + [5]=> + int(99) +} \ No newline at end of file diff --git a/tests/aot/ref/001.phpt b/tests/aot/ref/001.phpt index dc53eaa0..687105b4 100644 --- a/tests/aot/ref/001.phpt +++ b/tests/aot/ref/001.phpt @@ -1,9 +1,6 @@ --TEST-- ref 001 --SKIPIF-- - --FILE-- --EXPECT-- +生成的树结构: +Array +( + [0] => Array + ( + [id] => 1 + [pid] => 0 + [name] => 中国 + [children] => Array + ( + [0] => Array + ( + [id] => 2 + [pid] => 1 + [name] => 广东省 + [children] => Array + ( + [0] => Array + ( + [id] => 4 + [pid] => 2 + [name] => 广州市 + [children] => Array + ( + ) + + ) + + [1] => Array + ( + [id] => 5 + [pid] => 2 + [name] => 深圳市 + [children] => Array + ( + ) + + ) + + ) + + ) + + [1] => Array + ( + [id] => 3 + [pid] => 1 + [name] => 浙江省 + [children] => Array + ( + [0] => Array + ( + [id] => 6 + [pid] => 3 + [name] => 杭州市 + [children] => Array + ( + ) + + ) + + ) + + ) + + ) + + ) + + [1] => Array + ( + [id] => 7 + [pid] => 0 + [name] => 美国 + [children] => Array + ( + [0] => Array + ( + [id] => 8 + [pid] => 7 + [name] => 加州 + [children] => Array + ( + [0] => Array + ( + [id] => 9 + [pid] => 8 + [name] => 旧金山 + [children] => Array + ( + ) + + ) + + ) + + ) + + ) + + ) + +) + +开始断言测试... +DONE \ No newline at end of file diff --git a/tests/aot/ref/003.phpt b/tests/aot/ref/003.phpt new file mode 100644 index 00000000..c0bb7903 --- /dev/null +++ b/tests/aot/ref/003.phpt @@ -0,0 +1,43 @@ +--TEST-- +ref 003 +--FILE-- + +--EXPECT-- +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) +} +array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + int(5) +} \ No newline at end of file diff --git a/tests/aot/ref/004.phpt b/tests/aot/ref/004.phpt new file mode 100644 index 00000000..37f6896c --- /dev/null +++ b/tests/aot/ref/004.phpt @@ -0,0 +1,39 @@ +--TEST-- +ref 004 +--FILE-- + +--EXPECT-- +array(4) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + [3]=> + &array(2) { + [0]=> + int(4) + [1]=> + int(5) + } +} +array(3) { + [0]=> + int(4) + [1]=> + int(5) + [2]=> + int(10) +} \ No newline at end of file