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": { "require": {
"nikic/php-parser": "5.6.1", "nikic/php-parser": "5.6.1",
"league/climate": "^3.10", "league/climate": "^3.10",
"marcj/topsort": "^2.0" "marcj/topsort": "^2.0",
"symfony/var-dumper": "^8.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^10.4", "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", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "d4e35915568beccdcd533360bf30a206", "content-hash": "4982ed71f89f83b090b223c2a581f719",
"packages": [ "packages": [
{ {
"name": "league/climate", "name": "league/climate",
@ -296,6 +296,178 @@
"source": "https://github.com/Seldaek/cli-prompt/tree/1.0.4" "source": "https://github.com/Seldaek/cli-prompt/tree/1.0.4"
}, },
"time": "2020-12-15T21:32:01+00:00" "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": [ "packages-dev": [
@ -3764,91 +3936,6 @@
], ],
"time": "2024-09-09T11:45:10+00:00" "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", "name": "symfony/polyfill-php80",
"version": "v1.33.0", "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 RuntimeException;
use stdClass; use stdClass;
use Symfony\Component\VarDumper\Dump;
class CompilerBase extends \PhpAot\Core\Translator class CompilerBase extends \PhpAot\Core\Translator
{ {
use AstNodeType; use AstNodeType;
@ -156,6 +158,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected array $beforeStmtLines = []; protected array $beforeStmtLines = [];
protected array $afterStmtLines = []; protected array $afterStmtLines = [];
protected bool $inLoop = false; protected bool $inLoop = false;
/**
* 赋值表达式的左值,写操作,右值为读操作
* @var bool
*/
protected bool $inAssignExpr = false; protected bool $inAssignExpr = false;
protected bool $stubFile = false; protected bool $stubFile = false;
protected bool $enableProfiler = false; protected bool $enableProfiler = false;
@ -563,7 +569,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$type = $expr->getType(); $type = $expr->getType();
$this->writeLog('Line ' . $this->getLine($expr) . ': ' . $type); $this->writeLog('Line ' . $this->getLine($expr) . ': ' . $type);
if ($expr->getLine() === $this->debugLine) { if ($expr->getLine() === $this->debugLine) {
var_dump($expr); dump($expr);
} }
switch ($type) { switch ($type) {
case 'Expr_Isset': case 'Expr_Isset':
@ -571,14 +577,10 @@ class CompilerBase extends \PhpAot\Core\Translator
case 'Expr_Empty': case 'Expr_Empty':
return $this->parseEmpty($expr); return $this->parseEmpty($expr);
case 'Expr_Assign': case 'Expr_Assign':
$this->inAssignExpr = true;
$result = $this->parseAssign($expr); $result = $this->parseAssign($expr);
$this->inAssignExpr = false;
return $result; return $result;
case 'Expr_AssignRef': case 'Expr_AssignRef':
$this->inAssignExpr = true;
$result = $this->parseAssignRef($expr); $result = $this->parseAssignRef($expr);
$this->inAssignExpr = false;
return $result; return $result;
case 'Expr_Print': case 'Expr_Print':
return $this->parsePrint($expr); return $this->parsePrint($expr);
@ -653,7 +655,7 @@ class CompilerBase extends \PhpAot\Core\Translator
case self::EXPR_ARRAY_DIM_FETCH: case self::EXPR_ARRAY_DIM_FETCH:
return $this->parseArrayDimFetch($expr); return $this->parseArrayDimFetch($expr);
case 'Expr_PropertyFetch': case 'Expr_PropertyFetch':
return $this->parsePropertyFetch($expr); return $this->parsePropertyFetch($expr, $this->inAssignExpr);
case 'Expr_BinaryOp_ShiftLeft': case 'Expr_BinaryOp_ShiftLeft':
return $this->parseBinaryOpShiftLeft($expr); return $this->parseBinaryOpShiftLeft($expr);
case 'Expr_BinaryOp_ShiftRight': case 'Expr_BinaryOp_ShiftRight':
@ -760,7 +762,10 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($this->isPropertyFetch($left)) { if ($this->isPropertyFetch($left)) {
return $this->parseAssignPropertyArrayDim($left, $right); return $this->parseAssignPropertyArrayDim($left, $right);
} }
$oriInAssignExpr = $this->inAssignExpr;
$this->inAssignExpr = true;
$array = $this->parseIdentifier($left->var); $array = $this->parseIdentifier($left->var);
$this->inAssignExpr = $oriInAssignExpr;
$code = ''; $code = '';
// 这是 PHP 的初始化+赋值写法,需要先创建数组 // 这是 PHP 的初始化+赋值写法,需要先创建数组
if (!$this->hasVar($array) and $this->isVarExpr($left->var)) { if (!$this->hasVar($array) and $this->isVarExpr($left->var)) {
@ -846,7 +851,9 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->indentLevel--; $this->indentLevel--;
return $code . '}'; return $code . '}';
} }
$this->inAssignExpr = true;
$var = $this->parseIdentifier($left); $var = $this->parseIdentifier($left);
$this->inAssignExpr = false;
if ($var === 'this_') { if ($var === 'this_') {
$this->fatalError($left, 'Cannot re-assign $this'); $this->fatalError($left, 'Cannot re-assign $this');
} }
@ -871,6 +878,8 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->addLocalVar($var, $type); $this->addLocalVar($var, $type);
} }
return $var . ' = ' . $this->parseIdentifier($right->args[0]->value); 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') { if ($this->getReturnType() === 'void') {
$this->resetReturnType($type); $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 protected function parseBinaryOpMul(mixed $expr): string
@ -1037,9 +1058,6 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function detectExprType($expr): string protected function detectExprType($expr): string
{ {
$exprType = $expr->getType(); $exprType = $expr->getType();
if ($this->debugLine === $expr->getLine()) {
var_dump($exprType);
}
switch ($exprType) { switch ($exprType) {
case 'Expr_Cast_Int': case 'Expr_Cast_Int':
case 'Scalar_Int': case 'Scalar_Int':
@ -2461,24 +2479,26 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseAssignRef(Node\Expr\AssignRef $expr): string protected function parseAssignRef(Node\Expr\AssignRef $expr): string
{ {
if ($this->isVarExpr($expr->var)) { if ($this->isVarExpr($expr->var)) {
$var = $this->parseIdentifier($expr->var); $this->inAssignExpr = true;
if (!$this->hasVar($var)) { $left = $this->parseIdentifier($expr->var);
$this->addLocalVar($var, self::TYPE_REF); $this->inAssignExpr = false;
if (!$this->hasVar($left)) {
$this->addLocalVar($left, self::TYPE_REF);
} else { } else {
$type = $this->getVarType($var); $type = $this->getVarType($left);
if ($type !== self::TYPE_REF) { if ($type !== self::TYPE_REF) {
$this->fatalError($expr, 'Cannot assign reference to variable of type ' . $type); $this->fatalError($expr, 'Cannot assign reference to variable of type ' . $type);
} }
} }
if ($this->isVarExpr($expr->expr)) { 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) { } 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)) { } elseif ($this->isPropertyFetch($expr->expr)) {
$var = $this->parseIdentifier($expr->var); $left = $this->parseIdentifier($expr->var);
$object = $this->convertToObject($expr->expr->var); $object = $this->convertToObject($expr->expr->var);
$prop = $this->identifierToStr($expr->expr->name); $prop = $this->identifierToStr($expr->expr->name);
return $var . ' = ' . $object . '.getPropertyReference(' . $prop . ')'; return $left . ' = ' . $object . '.getPropertyReference(' . $prop . ')';
} }
} }
abort($expr); abort($expr);

@ -29,58 +29,13 @@ class Translator extends Preprocessor
public function __construct(string $rootPath) public function __construct(string $rootPath)
{ {
parent::__construct($rootPath); parent::__construct($rootPath);
$this->climate->arguments->add([ $this->climate->arguments->add(require __DIR__ . '/../config/compiler_options.php');
'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->preprocessArgvAdvanced(); $this->preprocessArgvAdvanced();
$this->climate->arguments->parse(); $this->climate->arguments->parse();
$this->optimizeLevel = $this->climate->arguments->get('optimize'); $this->optimizeLevel = $this->climate->arguments->get('optimize');
$this->buildMode = $this->climate->arguments->get('mode'); $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 = $this->climate->arguments->get('noLiteralStrings');
$this->noLiteralStrings = true; $this->noLiteralStrings = true;
$this->enableProfiler = $this->climate->arguments->defined('profile'); $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