fix(compiler): 修复PHP编译器中的类型检查和表达式解析问题

- 移除不再使用的常量定义 EXPR_VARIABLE、EXPR_NEW、EXPR_ARRAY_DIM_FETCH、EXPR_PROPERTY_FETCH
- 为 parseExpr 方法添加返回类型声明 string
- 将 switch 语句中的常量引用改为直接字符串引用
- 在变量重新赋值时添加对象到数组的类型检查限制
- 禁止对象与数组之间的相互转换
- 修复变量类型检测中的常量引用问题
- 删除未使用的 leftExprType 变量
- 使用 isArrayDimFetch 方法替代类型比较
- 优化布尔取反表达式的解析逻辑
- 添加对 self 和 parent 关键字的支持
- 重构 unset 语句的类型判断逻辑
- 修复 foreach 循环中数组维度获取的判断方式
- 修正 Translator 类中的格式化和类型引用问题
pull/1/head
韩天峰 4 months ago
parent 8c4bfd6683
commit c94d7afdba
  1. 52
      src/Php/CompilerBase.php
  2. 6
      src/Php/Translator.php
  3. 24
      tests/aot/self-class/001.phpt

@ -78,13 +78,6 @@ class CompilerBase extends \PhpAot\Core\Translator
public const string CLASS_MAP = 'class_map';
public const string FUNC_MAP = 'func_map';
public const string PROP_MAP = 'property_map';
public const string EXPR_VARIABLE = 'Expr_Variable';
public const string EXPR_NEW = 'Expr_New';
public const string EXPR_ARRAY_DIM_FETCH = 'Expr_ArrayDimFetch';
public const string EXPR_PROPERTY_FETCH = 'Expr_PropertyFetch';
public const string NAMESPACE_SEPARATOR = '__';
public const string PREFIX = 'php_';
@ -321,7 +314,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->context->objects[$object] ?? 'stdClass';
}
public function parseExpr(NodeAbstract $expr)
public function parseExpr(NodeAbstract $expr): string
{
if ($expr->hasAttribute('replace')) {
return $expr->getAttribute('replace');
@ -416,9 +409,9 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->parseBinaryOpMinus($expr);
case 'Expr_Array':
return $this->parseArray($expr);
case self::EXPR_ARRAY_DIM_FETCH:
case 'Expr_ArrayDimFetch':
return $this->parseArrayDimFetch($expr, $this->context->inAssignExpr);
case self::EXPR_PROPERTY_FETCH:
case 'Expr_PropertyFetch':
return $this->parsePropertyFetch($expr, $this->context->inAssignExpr);
case 'Expr_NullsafePropertyFetch':
return $this->parseNullsafePropertyFetch($expr);
@ -477,7 +470,7 @@ class CompilerBase extends \PhpAot\Core\Translator
case 'Scalar_Int':
case 'Scalar_Float':
case 'Scalar_String':
case self::EXPR_VARIABLE:
case 'Expr_Variable':
return $this->parseIdentifier($expr);
case 'Scalar_MagicConst_File':
case 'Scalar_MagicConst_Dir':
@ -979,7 +972,7 @@ class CompilerBase extends \PhpAot\Core\Translator
{
$type = $expr->getType();
switch ($type) {
case self::EXPR_VARIABLE:
case 'Expr_Variable':
return $this->parseVariable($expr);
case 'Name_FullyQualified':
return '\\' . $expr->name;
@ -1306,6 +1299,9 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->fatalError($left, "Cannot re-assign typed object `\${$var}` from `{$leftClass}` to `{$rightClass}`");
}
} else {
if ($this->getVarType($var) === self::TYPE_ARRAY) {
$this->fatalError($left, "Cannot re-assign `\${$var}` from object to array");
}
// TODO 右值是一个类型对象,但左值是一个 var ,许可,但无法标记对象类型
}
} else {
@ -1344,6 +1340,8 @@ class CompilerBase extends \PhpAot\Core\Translator
// 禁止字符串与数组互相转换,其他类型如对象可以使用 __toString() 协议转为字符串,整数和浮点型也可以转为字符串
or ($this->getVarType($var) === self::TYPE_STR and $type === self::TYPE_ARRAY)
or ($this->getVarType($var) === self::TYPE_ARRAY and $type === self::TYPE_STR)
// 禁止对象转为数组
or ($this->getVarType($var) === self::TYPE_ARRAY and $type === self::TYPE_OBJECT)
) {
$this->fatalError($left, "Cannot re-assign variable `\${$var}` from " . $this->getVarType($var) . ' to ' . $type);
}
@ -1904,7 +1902,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return self::TYPE_OBJECT;
case 'Expr_Assign':
return $this->detectVarType($expr->var);
case self::EXPR_VARIABLE:
case 'Expr_Variable':
return $this->detectVarType($expr);
case 'Expr_ConstFetch':
return $this->detectConstType($expr);
@ -2145,7 +2143,6 @@ class CompilerBase extends \PhpAot\Core\Translator
{
$var = $this->parseIdentifier($node->var);
$expr = $this->parseIdentifier($node->expr);
$leftExprType = $node->var->getType();
if ($this->isVarExpr($node->var)) {
if (!$this->hasVar($var)) {
@ -2166,7 +2163,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $var . ' ' . $op . ' ' . $rightExprStr;
}
if ($leftExprType === self::EXPR_ARRAY_DIM_FETCH) {
if ($this->isArrayDimFetch($node->var)) {
/**
* $count[$r] -= 1;
* 需要转为下面语句:
@ -2882,9 +2879,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseBooleanNot(Expr\BooleanNot $expr): string
{
$expr = $this->parseExpr($expr->expr);
return '!(' . $expr . ')';
return '!(' . $this->parseExpr($expr->expr) . ')';
}
protected function parseWhile(Node\Stmt\While_ $v): string
@ -3092,8 +3087,14 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($this->isNameExpr($expr->class)) {
if ($className === 'static') {
$cePtr = Symbol::getCalledCe();
} else {
if ($className === 'self') {
$className = $this->getFullClassName();
} elseif ($className === 'parent') {
$className = $this->classDef->extends;
} else {
$className = $this->getNamespacedClassName($className);
}
if ($this->hasClass($className)) {
$classDef = $this->getClass($className);
if ($classDef->flags & Modifiers::ABSTRACT) {
@ -3370,23 +3371,22 @@ class CompilerBase extends \PhpAot\Core\Translator
$vars = $node->vars;
$lines = [];
foreach ($vars as $var) {
$type = $var->getType();
if ($type === self::EXPR_ARRAY_DIM_FETCH) {
if ($this->isArrayDimFetch($var)) {
$array = $this->parseIdentifier($var->var);
$dim = $this->parseIdentifier($var->dim);
$lines[] = $array . '.offsetUnset(' . $dim . ');';
} elseif ($type === 'Expr_PropertyFetch') {
} elseif ($this->isPropertyFetch($var)) {
$object = $this->parseIdentifier($var->var);
$propName = $this->identifierToStr($var->name, literal: true);
$lines[] = $object . '.unsetProperty(' . $propName . ');';
} elseif ($type === self::EXPR_VARIABLE) {
} elseif ($this->isVarExpr($var)) {
$name = $this->parseIdentifier($var);
if (!$this->hasVar($name)) {
$this->errorUndefinedVariable($var);
}
$lines[] = "{$name}.unset();";
} else {
$this->fatalError($var, "Unsupported unset type `{$type}`");
$this->fatalError($var, "Unsupported unset type `{$var->getType()}`");
}
}
@ -3472,7 +3472,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->fatalError($node, 'Foreach by reference only supports variable as value');
}
if ($node->valueVar->getType() == self::EXPR_ARRAY_DIM_FETCH) {
if ($this->isArrayDimFetch($node->valueVar)) {
$array = $this->parseIdentifier($node->valueVar->var);
if (!$this->hasVar($array) or $node->valueVar->dim === null) {
abort($node->valueVar);
@ -4057,10 +4057,6 @@ class CompilerBase extends \PhpAot\Core\Translator
}
return $id;
}
/*
* 对 static 的支持存在问题,静态编译时无法获得实际运行时的子类名,所以只能使用 self
* self 是在编译期确定的,而 static 是运行时确定的,但使用 AOT 编译为可执行文件后,运行时类的名称是无法确定的
*/
if ($id === 'self') {
$id = $this->getNamespacedClassName($this->class);
} elseif ($id === 'static') {

@ -368,14 +368,14 @@ class Translator extends Preprocessor
$code .= 'uint32_t ' . self::PREFIX . self::PROP_MAP . '[' . count($this->propMap) . '];' . PHP_EOL;
$code .= <<<'CODE'
zend_class_entry *php_get_class(int class_id, const php::String &class_name) {
zend_class_entry *php_get_class(int class_id, const php::Str &class_name) {
if (UNEXPECTED(php_class_map[class_id] == nullptr)) {
php_class_map[class_id] = php::getClassEntrySafe(class_name);
}
return php_class_map[class_id];
}
zend_function *php_get_func(int func_id, const php::String &func_name) {
zend_function *php_get_func(int func_id, const php::Str &func_name) {
if (UNEXPECTED(php_func_map[func_id] == nullptr)) {
php_func_map[func_id] = php::getFunction(func_name);
}
@ -751,7 +751,7 @@ CODE;
$code .= 'extern ' . $func->returnType . ' ' . self::PREFIX . $name . '(';
$list = [];
if ($func->method) {
$list[] = 'php::Object &this_';
$list[] = self::TYPE_OBJECT . ' &this_';
}
$argInfoList = $func->argInfoList;
if ($argInfoList) {

@ -0,0 +1,24 @@
--TEST--
self class
--FILE--
<?php
class EvaluatedValue
{
public $value;
public function __construct(string $value)
{
$this->value = $value;
}
public static function null(): EvaluatedValue
{
return new self('null');
}
}
function main() {
$obj = EvaluatedValue::null();
var_dump($obj->value);
}
?>
--EXPECT--
string(4) "null"
Loading…
Cancel
Save