feat(aot): 添加对引用赋值和列表赋值的支持

- 实现了引用赋值功能,支持变量间的引用传递
- 添加了列表赋值解析逻辑,支持数组解构赋值操作
- 新增 isAssignExpr 方法用于判断赋值表达式类型
- 重构了赋值相关的编译逻辑,提高代码可读性
- 添加了多个测试用例验证引用和列表赋值功能
- 修复了动态调用中的参数展开问题
pull/1/head v0.0.5
韩天峰 5 months ago
parent 95e7b5f6e4
commit 36cf7dbf16
  1. 5
      src/Php/AstNodeType.php
  2. 112
      src/Php/CompilerBase.php
  3. 91
      tests/aot/class_extends.phpt
  4. 27
      tests/aot/dynamic_call/vargs.phpt
  5. 109
      tests/aot/ref/001.phpt
  6. 43
      tests/aot/ref/003.phpt
  7. 39
      tests/aot/ref/004.phpt

@ -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

@ -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

@ -0,0 +1,91 @@
--TEST--
class extends
--FILE--
<?php
class Foo extends ArrayObject {
}
class A extends Foo
{
public function __construct()
{
echo "A::__construct()\n";
}
}
class B extends A
{
public function __construct()
{
echo "B::__construct()\n";
parent::__construct();
}
public function foo()
{
$this->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"

@ -0,0 +1,27 @@
--TEST--
named args
--FILE--
<?php
function main()
{
$array = [1, 3, 5];
$push = [12, 33, 99];
array_push($array, ...$push);
var_dump($array);
}
?>
--EXPECT--
array(6) {
[0]=>
int(1)
[1]=>
int(3)
[2]=>
int(5)
[3]=>
int(12)
[4]=>
int(33)
[5]=>
int(99)
}

@ -1,9 +1,6 @@
--TEST--
ref 001
--SKIPIF--
<?php
exit("skip: not supported in AOT");
?>
--FILE--
<?php
@ -97,3 +94,109 @@ function main()
}
?>
--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

@ -0,0 +1,43 @@
--TEST--
ref 003
--FILE--
<?php
function main()
{
$a = [1, 2, 3];
$c = $d = $e = &$a;
$e[] = 5;
var_dump($c);
var_dump($d);
var_dump($e);
}
?>
--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)
}

@ -0,0 +1,39 @@
--TEST--
ref 004
--FILE--
<?php
function main()
{
$a = [1, 2, 3];
$b = [4, 5];
$a[] = &$b;
var_dump($a);
$a[3][] = 10;
var_dump($b);
}
?>
--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)
}
Loading…
Cancel
Save