fix(compiler): 修复变量赋值和属性访问的类型检查问题

- 添加了对属性赋值操作的类型检测支持
- 实现了模运算符的类型安全处理
- 添加了布尔值转义函数 escapeBool
- 修改 parsePropertyFetch 方法以支持赋值操作标识
- 优化 foreach 循环中的变量检查逻辑
- 添加引用赋值的类型验证
- 将 writeFile 方法改为公有以便外部调用
- 添加 checkVar 方法进行变量类型安全检查
- 更新测试文件中的变量命名以避免冲突
- 修复对象链接测试中的属性访问问题
pull/1/head
韩天峰 7 months ago
parent 9499aac142
commit e824c5d7c0
  1. 6
      bin/gen_stub.php
  2. 42
      src/Php/CompilerBase.php
  3. 9
      src/Php/Translator.php
  4. 4
      tests/aot/assignment_operators.phpt
  5. 4
      tests/aot/complex_array_operations.phpt
  6. 4
      tests/aot/foreach.phpt
  7. 3
      tests/aot/object-link.phpt

@ -35,9 +35,9 @@ const ALL_PHP_VERSION_IDS = [
// file_put_contents() but with a success message printed after saving
function reportFilePutContents(string $filename, string $content): void {
if (file_put_contents($filename, $content)) {
echo "Saved $filename\n";
}
global $translator;
$translator->writeFile($filename, $content);
echo "Saved $filename\n";
}
/**

@ -853,6 +853,8 @@ class CompilerBase extends \PhpAot\Core\Translator
if (!$this->hasVar($var)) {
$this->addLocalVar($var, $type);
}
} elseif ($this->isPropertyFetch($left)) {
$var = $this->parsePropertyFetch($left, true);
}
return $var . ' = ' . $this->convertExprType($expr, $this->detectExprType($left), $this->detectExprType($right));
}
@ -935,6 +937,10 @@ class CompilerBase extends \PhpAot\Core\Translator
$leftExpr = $this->convertExprType($leftExpr, $leftType, self::TYPE_INT);
}
if ($op === '%' and !($leftType === self::TYPE_INT and $rightType === self::TYPE_INT)) {
return 'php::math::mod(' . $leftExpr . ', ' . $rightExpr . ')';
}
return '((' . $leftExpr . ') ' . $op . ' (' . $rightExpr . '))';
}
@ -1936,6 +1942,11 @@ class CompilerBase extends \PhpAot\Core\Translator
return addcslashes($str, "\\\"\n\r\t\v\f\0\x01..\x1f\x7f..\xff");
}
protected function escapeBool(bool $bool): string
{
return $bool ? 'true' : 'false';
}
protected function escapeVarName(string $name): string
{
if (in_array($name, Constants::CPP_RESERVED_NAMES)) {
@ -2047,7 +2058,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return implode(PHP_EOL . $this->getIndent(), $lines);
}
protected function parsePropertyFetch(Node\Expr\PropertyFetch $expr): string
protected function parsePropertyFetch(Node\Expr\PropertyFetch $expr, bool $assign = false): string
{
$object = $expr->var;
$property = $expr->name;
@ -2057,7 +2068,8 @@ class CompilerBase extends \PhpAot\Core\Translator
$id = self::PREFIX . $this->getPropertyOffset($property, $this->class, $this->namespace);
}
}
return $this->convertToObject($object) . '.getPropertyIndirect(' . $id . ')';
return $this->convertToObject($object) . '.getPropertyIndirect(' . $id . ', ' . $this->escapeBool($assign) . ')';
}
protected function parseAssignOpShiftRight(Node $node): string
@ -2101,9 +2113,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$code = 'for (auto iter = ' . $iteratorVar . '.begin(); iter != ' . $iteratorVar . '.end(); ++iter) {' . PHP_EOL;
$this->indentLevel++;
if ($node->keyVar) {
if (!$this->hasVar($keyVar)) {
$this->addLocalVar($keyVar, self::TYPE_VAR);
}
$this->checkVar($node, $keyVar);
$code .= $this->getIndent() . ' ' . $keyVar . ' = iter.key();' . PHP_EOL;
}
@ -2116,9 +2126,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$code .= $this->getIndent() . "$array.offsetSet($dim, iter.value());";
} else {
$valueVar = $this->parseIdentifier($node->valueVar);
if (!$this->hasVar($valueVar)) {
$this->addLocalVar($valueVar, self::TYPE_VAR);
}
$this->checkVar($node, $valueVar);
$code .= $this->getIndent() . ' ' . $valueVar . ' = iter.value();' . PHP_EOL;
}
@ -2394,6 +2402,11 @@ class CompilerBase extends \PhpAot\Core\Translator
$var = $this->parseIdentifier($expr->var);
if (!$this->hasVar($var)) {
$this->addLocalVar($var, self::TYPE_REF);
} else {
$type = $this->getVarType($var);
if ($type !== self::TYPE_REF) {
$this->fatalError($expr, 'Cannot assign reference to variable of type ' . $type);
}
}
if ($this->isVarExpr($expr->expr)) {
return $var . ' = ' . $this->parseIdentifier($expr->expr) . '.toReference()';
@ -2668,7 +2681,7 @@ class CompilerBase extends \PhpAot\Core\Translator
* @param string $content
* @return void
*/
protected function writeFile(string $file, string $content): void
public function writeFile(string $file, string $content): void
{
$dir = dirname($file);
if (!is_dir($dir)) {
@ -2743,6 +2756,17 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'continue;';
}
protected function checkVar(NodeAbstract $node, string $name): void
{
if (!$this->hasVar($name)) {
$this->addLocalVar($name, self::TYPE_VAR);
} else {
if ($this->getVarType($name) !== self::TYPE_VAR) {
$this->fatalError($node, 'Cannot assign value to variable of type ' . $this->getVarType($name));
}
}
}
protected function checkAccessible(ClassDef $classDef, MethodDef $methodDef): bool
{
// 在当前类中,允许调用所有方法

@ -6,6 +6,7 @@ use MJS\TopSort\Implementations\StringSort;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\NodeAbstract;
use PhpParser\NodeTraverser;
class Translator extends Preprocessor
@ -864,11 +865,15 @@ class Translator extends Preprocessor
$code .= $this->getIndent() . $tmpVar . '.exec("rewind");' . PHP_EOL;
$code .= $this->getIndent() . 'for (;' . $tmpVar . '.exec("valid"); ' . $tmpVar . '.exec("next")) {' . PHP_EOL;
$this->indentLevel++;
$valueVar = $this->parseIdentifier($node->valueVar);
$code .= $this->getIndent() . self::TYPE_VAR . ' ' . $valueVar . ' = ' . $tmpVar . '.exec("current");' . PHP_EOL;
$this->checkVar($node, $valueVar);
$code .= $this->getIndent() . ' ' . $valueVar . ' = ' . $tmpVar . '.exec("current");' . PHP_EOL;
if ($node->keyVar) {
$keyVar = $this->parseIdentifier($node->keyVar);
$code .= $this->getIndent() . self::TYPE_VAR . ' ' . $keyVar . ' = ' . $tmpVar . '.exec("key");' . PHP_EOL;
$this->checkVar($node, $keyVar);
$code .= $this->getIndent() . ' ' . $keyVar . ' = ' . $tmpVar . '.exec("key");' . PHP_EOL;
}
$code .= $this->parseStmts($node->stmts);
$code .= '}' . PHP_EOL;

@ -61,8 +61,8 @@ var_dump($z);
// Test assignment with function results
$numbers = [1, 2, 3, 4, 5];
$sum = 0;
foreach ($numbers as $num) {
$sum += $num;
foreach ($numbers as $_num) {
$sum += $_num;
}
var_dump($sum);

@ -115,8 +115,8 @@ function main() {
// Test with array references
$original = ['value' => 50];
$ref =& $original;
$ref['value'] += 25;
$xref =& $original;
$xref['value'] += 25;
var_dump($original['value']); // 75
echo "All complex array operation tests passed!\n";

@ -7,8 +7,8 @@ function main()
$v = 199;
$arr = range(0, 99);
$c = 0;
foreach ($arr as $v) {
$c += $v;
foreach ($arr as $_v) {
$c += $_v;
}
var_dump($c);
var_dump($v);

@ -6,10 +6,11 @@ function main()
{
$o = new stdClass();
$o->prop = ['dim2' => ['dim3' => 'value']];
var_dump($o->prop['dim2']['dim3']);
$o->prop['dim2']['dim3'] = 'hello';
var_dump($o->prop['dim2']['dim3']);
}
?>
--EXPECT--
string(5) "value"
string(5) "hello"

Loading…
Cancel
Save