TypePHP 编译器 https://swoole.com/aot/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

242 lines
8.0 KiB

<?php
/**
* This file is part of Swoole-Compiler(AOT).
*
* @link https://www.swoole.com/
* @contact service@swoole.com
*/
namespace PhpAot\Php;
use PhpAot\Php\Exception\SyntaxError;
use PhpParser\Node;
use PhpParser\NodeAbstract;
use PhpParser\NodeFinder;
use PhpParser\NodeTraverser;
class Preprocessor extends CompilerBase
{
protected bool $enableCache = false;
public function sortFiles(array &$list): void
{
foreach ($this->symbolCallInFile as $k => $call) {
if (!isset($this->symbolDeclInFile[$call['name']])) {
unset($this->symbolCallInFile[$k]);
}
}
$sorter = new FileSorter($this->symbolDeclInFile, $this->symbolCallInFile);
$sortedFiles = $sorter->sort();
foreach ($list as $file) {
if (!$this->isStubFile($file) and !in_array($file, $sortedFiles)) {
$sortedFiles[] = $file;
}
}
$list = $sortedFiles;
}
public function getCppFile(string $file): string
{
$info = pathinfo($file);
return $this->buildDir . '/' . $this->removeCommonPrefix($this->buildDir, $info['dirname'] . '/' . $info['filename'] . '.cc');
}
public function getObjectFile(string $cppFile): string
{
$info = pathinfo($cppFile);
return $info['dirname'] . '/' . $info['filename'] . '.o';
}
public function hasCppFileCache(string $file): bool
{
if (!$this->enableCache or $this->climate->arguments->defined('force')) {
return false;
}
$cppFile = $this->getCppFile($file);
if (file_exists($cppFile) and filemtime($cppFile) > filemtime($file)) {
return true;
}
return false;
}
public function prepare(string $file): void
{
if ($this->hasCppFileCache($file)) {
$this->climate->darkGray('skip: ' . $file . ', cache exists');
return;
}
$phpCode = $this->loadFile($file);
$this->climate->info('prepare: ' . $this->file);
try {
$ast = $this->parser->parse($phpCode);
} catch (\PhpParser\Error $e) {
$this->climate->red("Fatal error: {$e->getMessage()} in {$this->file}");
throw new SyntaxError($e->getMessage(), $e->getCode());
}
$traverser = new NodeTraverser();
$traverser->addVisitor(new Visitor());
$stmts = $traverser->traverse($ast);
foreach ($stmts as $v) {
$type = $v->getType();
switch ($type) {
case 'Stmt_Namespace':
$this->prepareNamespaceDef($v);
break;
case 'Stmt_Enum':
case 'Stmt_Class':
case 'Stmt_Trait':
$this->prepareClass($v);
break;
case 'Stmt_Function':
$this->prepareFunction($v) . PHP_EOL;
break;
case 'Stmt_Declare':
case 'Stmt_Use':
case 'Stmt_Interface':
case 'Stmt_Nop':
break;
case 'Stmt_Const':
$this->parseConstDef($v);
break;
case 'Stmt_Expression':
$this->foundStrayCode($v);
// no break
default:
$this->fatalError($v, 'Unsupported statement: ' . $type);
}
}
$nodeFinder = new NodeFinder();
$functionCalls = $nodeFinder->findInstanceOf($ast, Node\Expr\FuncCall::class);
foreach ($functionCalls as $call) {
if ($call->name instanceof Node\Name) {
$name = $call->name->toString();
$this->symbolCallInFile[] = [
'name' => strtolower($name),
'file' => $this->file,
'line' => $call->getLine(),
];
}
}
}
protected function prepareNamespaceDef(Node\Stmt\Namespace_ $node): void
{
$this->resetNamespace();
$this->namespace = $node->name ? $this->parseIdentifier($node->name) : '';
foreach ($node->stmts as $v2) {
$type2 = $v2->getType();
switch ($type2) {
case 'Stmt_Class':
case 'Stmt_Enum':
$this->prepareClass($v2);
break;
case 'Stmt_Function':
$this->prepareFunction($v2) . PHP_EOL;
break;
case 'Stmt_Use':
case 'Stmt_Const':
case 'Stmt_Interface':
break;
case 'Stmt_Expression':
$this->foundStrayCode($v2);
// no break
default:
abort($v2);
}
}
$this->resetNamespace();
}
protected function prepareFunction(Node $v): void
{
$name = $this->getFunctionName($v);
if ($this->stubFile) {
$this->nativeFunctions[$name] = $this->parseFunctionDecl($v);
} else {
$this->symbolDeclInFile[strtolower($name)] = $this->file;
}
}
protected function getParentClass(NodeAbstract $extends): string
{
if ($extends instanceof Node\Name\FullyQualified) {
return $this->parseIdentifier($extends);
} else {
return $this->getNamespacedClassName($this->parseIdentifier($extends));
}
}
protected function prepareClass(Node\Stmt\Class_|Node\Stmt\Trait_|Node\Stmt\Enum_ $class): string
{
$this->class = $this->parseIdentifier($class->name);
$fullClassName = $this->getNamespacedClassName($this->class);
$fullClassNameLower = strtolower($fullClassName);
if (!empty($class->extends)) {
$this->parentClass = $this->getParentClass($class->extends);
$parentClassLower = strtolower($this->parentClass);
$this->symbolCallInFile[] = [
'name' => $parentClassLower,
'file' => $this->file,
'line' => $class->getLine(),
];
$this->classExtends[$fullClassNameLower] = $parentClassLower;
}
$this->symbolDeclInFile[$fullClassNameLower] = $this->file;
$code = '';
foreach ($class->stmts as $v) {
$type = $v->getType();
switch ($type) {
case 'Stmt_ClassConst':
case 'Stmt_Property':
case 'Stmt_Nop':
case 'Stmt_TraitUse':
case 'Stmt_EnumCase':
break;
case 'Stmt_ClassMethod':
$this->prepareMethod($v);
break;
case 'Stmt_Expression':
$this->foundStrayCode($v);
// no break
default:
abort($v);
}
}
$this->class = '';
$this->parentClass = '';
return $code;
}
protected function prepareMethod(Node\Stmt\ClassMethod $v): void
{
$this->prepareFunction($v) . PHP_EOL;
if ($this->isIdExpr($v->name) or $this->isNameExpr($v->name)) {
$fullClassName = $this->getNamespacedClassName($this->class);
$fullMethodName = $fullClassName . '::' . $v->name;
$this->classMethodOverride[strtolower($fullMethodName)] = false;
// 查找父类是否有同名方法,递归查找
$fullClassNameLower = strtolower($fullClassName);
while (isset($this->classExtends[$fullClassNameLower])) {
$parentClass = $this->classExtends[$fullClassNameLower];
$parentMethodLower = strtolower($parentClass . '::' . $v->name);
if (isset($this->classMethodOverride[$parentMethodLower])) {
$this->classMethodOverride[$parentMethodLower] = true;
}
$fullClassNameLower = strtolower($parentClass);
}
}
}
}