refactor(compiler): 重构编译器配置和代码逻辑

- 将命令行参数配置提取到独立的配置文件 compiler_options.php
- 添加新的调试选项包括 debug-line 和 force 参数
- 替换 var_dump 为 symfony/var-dumper 的 dump 函数
- 优化赋值表达式处理逻辑,改进 inAssignExpr 状态管理
- 修复返回语句中间接引用可能导致的内存安全问题
- 添加临时变量确保返回值的内存安全
- 重构属性访问解析方法,传入赋值状态参数
- 更新 composer 依赖,添加 symfony/var-dumper 支持
- 移除调试相关的 var_dump 输出语句
- 优化数组维度获取和属性访问的解析流程
pull/1/head
韩天峰 7 months ago
parent 3931d77ceb
commit c7c375019d
  1. 3
      composer.json
  2. 259
      composer.lock
  3. 5
      examples/to_array.php
  4. 56
      src/Php/CompilerBase.php
  5. 51
      src/Php/Translator.php
  6. 54
      src/config/compiler_options.php

@ -2,7 +2,8 @@
"require": {
"nikic/php-parser": "5.6.1",
"league/climate": "^3.10",
"marcj/topsort": "^2.0"
"marcj/topsort": "^2.0",
"symfony/var-dumper": "^8.0"
},
"require-dev": {
"phpunit/phpunit": "^10.4",

259
composer.lock generated

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d4e35915568beccdcd533360bf30a206",
"content-hash": "4982ed71f89f83b090b223c2a581f719",
"packages": [
{
"name": "league/climate",
@ -296,6 +296,178 @@
"source": "https://github.com/Seldaek/cli-prompt/tree/1.0.4"
},
"time": "2020-12-15T21:32:01+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": ">=7.2"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-12-23T08:48:59+00:00"
},
{
"name": "symfony/var-dumper",
"version": "v8.0.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "326e0406fc315eca57ef5740fa4a280b7a068c82"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/326e0406fc315eca57ef5740fa4a280b7a068c82",
"reference": "326e0406fc315eca57ef5740fa4a280b7a068c82",
"shasum": ""
},
"require": {
"php": ">=8.4",
"symfony/polyfill-mbstring": "^1.0"
},
"conflict": {
"symfony/console": "<7.4",
"symfony/error-handler": "<7.4"
},
"require-dev": {
"symfony/console": "^7.4|^8.0",
"symfony/http-kernel": "^7.4|^8.0",
"symfony/process": "^7.4|^8.0",
"symfony/uid": "^7.4|^8.0",
"twig/twig": "^3.12"
},
"bin": [
"Resources/bin/var-dump-server"
],
"type": "library",
"autoload": {
"files": [
"Resources/functions/dump.php"
],
"psr-4": {
"Symfony\\Component\\VarDumper\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides mechanisms for walking through any arbitrary PHP variable",
"homepage": "https://symfony.com",
"keywords": [
"debug",
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v8.0.4"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2026-01-01T23:07:29+00:00"
}
],
"packages-dev": [
@ -3764,91 +3936,6 @@
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.33.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493",
"reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": ">=7.2"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-12-23T08:48:59+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.33.0",

@ -0,0 +1,5 @@
<?php
$a = new ArrayObject();
$a['nanme'] = 1;
var_dump($a);

@ -22,6 +22,8 @@ use PhpParser\Modifiers;
use RuntimeException;
use stdClass;
use Symfony\Component\VarDumper\Dump;
class CompilerBase extends \PhpAot\Core\Translator
{
use AstNodeType;
@ -156,6 +158,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected array $beforeStmtLines = [];
protected array $afterStmtLines = [];
protected bool $inLoop = false;
/**
* 赋值表达式的左值,写操作,右值为读操作
* @var bool
*/
protected bool $inAssignExpr = false;
protected bool $stubFile = false;
protected bool $enableProfiler = false;
@ -563,7 +569,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$type = $expr->getType();
$this->writeLog('Line ' . $this->getLine($expr) . ': ' . $type);
if ($expr->getLine() === $this->debugLine) {
var_dump($expr);
dump($expr);
}
switch ($type) {
case 'Expr_Isset':
@ -571,14 +577,10 @@ class CompilerBase extends \PhpAot\Core\Translator
case 'Expr_Empty':
return $this->parseEmpty($expr);
case 'Expr_Assign':
$this->inAssignExpr = true;
$result = $this->parseAssign($expr);
$this->inAssignExpr = false;
return $result;
case 'Expr_AssignRef':
$this->inAssignExpr = true;
$result = $this->parseAssignRef($expr);
$this->inAssignExpr = false;
return $result;
case 'Expr_Print':
return $this->parsePrint($expr);
@ -653,7 +655,7 @@ class CompilerBase extends \PhpAot\Core\Translator
case self::EXPR_ARRAY_DIM_FETCH:
return $this->parseArrayDimFetch($expr);
case 'Expr_PropertyFetch':
return $this->parsePropertyFetch($expr);
return $this->parsePropertyFetch($expr, $this->inAssignExpr);
case 'Expr_BinaryOp_ShiftLeft':
return $this->parseBinaryOpShiftLeft($expr);
case 'Expr_BinaryOp_ShiftRight':
@ -760,7 +762,10 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($this->isPropertyFetch($left)) {
return $this->parseAssignPropertyArrayDim($left, $right);
}
$oriInAssignExpr = $this->inAssignExpr;
$this->inAssignExpr = true;
$array = $this->parseIdentifier($left->var);
$this->inAssignExpr = $oriInAssignExpr;
$code = '';
// 这是 PHP 的初始化+赋值写法,需要先创建数组
if (!$this->hasVar($array) and $this->isVarExpr($left->var)) {
@ -846,7 +851,9 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->indentLevel--;
return $code . '}';
}
$this->inAssignExpr = true;
$var = $this->parseIdentifier($left);
$this->inAssignExpr = false;
if ($var === 'this_') {
$this->fatalError($left, 'Cannot re-assign $this');
}
@ -871,6 +878,8 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->addLocalVar($var, $type);
}
return $var . ' = ' . $this->parseIdentifier($right->args[0]->value);
} else {
$type = $type === self::TYPE_VOID ? self::TYPE_VAR : $type;
}
}
@ -985,7 +994,19 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($this->getReturnType() === 'void') {
$this->resetReturnType($type);
}
return 'return ' . $this->convertExprType($expr, $this->getReturnType(), $type);
$exprCode = $this->convertExprType($expr, $this->getReturnType(), $type);
// return 如果使用了 Indirect 语句,可能会导致变量提前析构,出现悬空指针
// 将 Indirect 赋值给临时变量后,使用 Ctor::Copy 解除了 Indirect,保证内存安全
if (!$this->isVarExpr($v->expr)) {
$tmpVar = $this->genTmpVarName();
// 必须提前声明变量,否则在末尾声明并 return 可能会被 gcc 优化掉
$this->addLocalVar($tmpVar, $type);
$code = $tmpVar . ' = ' . $exprCode . ';' . PHP_EOL;
$code .= $this->getIndent() . 'return ' . $tmpVar;
} else {
$code = 'return ' . $exprCode;
}
return $code;
}
protected function parseBinaryOpMul(mixed $expr): string
@ -1037,9 +1058,6 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function detectExprType($expr): string
{
$exprType = $expr->getType();
if ($this->debugLine === $expr->getLine()) {
var_dump($exprType);
}
switch ($exprType) {
case 'Expr_Cast_Int':
case 'Scalar_Int':
@ -2461,24 +2479,26 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseAssignRef(Node\Expr\AssignRef $expr): string
{
if ($this->isVarExpr($expr->var)) {
$var = $this->parseIdentifier($expr->var);
if (!$this->hasVar($var)) {
$this->addLocalVar($var, self::TYPE_REF);
$this->inAssignExpr = true;
$left = $this->parseIdentifier($expr->var);
$this->inAssignExpr = false;
if (!$this->hasVar($left)) {
$this->addLocalVar($left, self::TYPE_REF);
} else {
$type = $this->getVarType($var);
$type = $this->getVarType($left);
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()';
return $left . ' = ' . $this->parseIdentifier($expr->expr) . '.toReference()';
} elseif ($expr->expr->getType() === self::EXPR_ARRAY_DIM_FETCH) {
return $var . ' = ' . $this->parseIdentifier($expr->expr);
return $left . ' = ' . $this->parseIdentifier($expr->expr);
} elseif ($this->isPropertyFetch($expr->expr)) {
$var = $this->parseIdentifier($expr->var);
$left = $this->parseIdentifier($expr->var);
$object = $this->convertToObject($expr->expr->var);
$prop = $this->identifierToStr($expr->expr->name);
return $var . ' = ' . $object . '.getPropertyReference(' . $prop . ')';
return $left . ' = ' . $object . '.getPropertyReference(' . $prop . ')';
}
}
abort($expr);

@ -29,58 +29,13 @@ class Translator extends Preprocessor
public function __construct(string $rootPath)
{
parent::__construct($rootPath);
$this->climate->arguments->add([
'optimize' => [
'prefix' => 'O',
'longPrefix' => 'optimize',
'description' => 'Set the optimization level of the gcc compiler to 0 by default',
'required' => false,
'castTo' => 'int',
'defaultValue' => 0,
],
'output' => [
'prefix' => 'o',
'longPrefix' => 'output',
'description' => 'Output file',
],
'help' => [
'prefix' => 'h',
'longPrefix' => 'help',
'description' => 'Show help',
'noValue' => true,
],
'profile' => [
'longPrefix' => 'profile',
'description' => 'Enable performance profiling',
'required' => false,
'noValue' => true,
],
'noLiteralStrings' => [
'longPrefix' => 'no-literal-strings',
'description' => 'Disable literal strings optimization',
'required' => false,
'noValue' => true,
],
'force' => [
'prefix' => 'f',
'longPrefix' => 'force',
'description' => 'Force compile even if cache exists',
'required' => false,
'noValue' => true,
],
'mode' => [
'longPrefix' => 'mode',
'prefix' => 'm',
'description' => 'Build mode, -m bin(binary) or -m ext(extension), default: bin',
'required' => false,
'defaultValue' => 'bin',
],
]);
$this->climate->arguments->add(require __DIR__ . '/../config/compiler_options.php');
$this->preprocessArgvAdvanced();
$this->climate->arguments->parse();
$this->optimizeLevel = $this->climate->arguments->get('optimize');
$this->buildMode = $this->climate->arguments->get('mode');
$this->debugLine = intval($this->climate->arguments->get('debug-line'));
// $this->noLiteralStrings = $this->climate->arguments->get('noLiteralStrings');
$this->noLiteralStrings = true;
$this->enableProfiler = $this->climate->arguments->defined('profile');

@ -0,0 +1,54 @@
<?php
return [
'optimize' => [
'prefix' => 'O',
'longPrefix' => 'optimize',
'description' => 'Set the optimization level of the gcc compiler to 0 by default',
'required' => false,
'castTo' => 'int',
'defaultValue' => 0,
],
'output' => [
'prefix' => 'o',
'longPrefix' => 'output',
'description' => 'Output file',
],
'help' => [
'prefix' => 'h',
'longPrefix' => 'help',
'description' => 'Show help',
'noValue' => true,
],
'profile' => [
'longPrefix' => 'profile',
'description' => 'Enable performance profiling',
'required' => false,
'noValue' => true,
],
'noLiteralStrings' => [
'longPrefix' => 'no-literal-strings',
'description' => 'Disable literal strings optimization',
'required' => false,
'noValue' => true,
],
'force' => [
'prefix' => 'f',
'longPrefix' => 'force',
'description' => 'Force compile even if cache exists',
'required' => false,
'noValue' => true,
],
'mode' => [
'longPrefix' => 'mode',
'prefix' => 'm',
'description' => 'Build mode, -m bin(binary) or -m ext(extension), default: bin',
'required' => false,
'defaultValue' => 'bin',
],
'debug-line' => [
'longPrefix' => 'debug-line',
'description' => 'Enable debug line',
'required' => false,
'defaultValue' => 0,
],
];
Loading…
Cancel
Save