大重构,新增 FunctionContext 类

pull/1/head
韩天峰 5 months ago
parent 3df8a36674
commit 9eb781464a
  1. 299
      src/Php/CompilerBase.php
  2. 8
      src/Php/Constants.php
  3. 45
      src/Php/Context/FunctionContext.php
  4. 41
      src/Php/Generator/ClosureGenerator.php
  5. 7
      src/Php/Translator.php

@ -9,6 +9,7 @@
namespace PhpAot\Php;
use League\CLImate\CLImate;
use PhpAot\Php\Context\FunctionContext;
use PhpAot\Php\Entity\ClassDef;
use PhpAot\Php\Entity\FunctionDef;
use PhpAot\Php\Entity\InterfaceDef;
@ -21,9 +22,9 @@ use PhpAot\Php\Generator\ClosureGenerator;
use PhpAot\Php\Generator\PlaceHolderGenerator;
use PhpAot\Php\Generator\PropertyPromotion;
use PhpAot\Php\Generator\Utils;
use PhpAot\Php\Generator\RefGenerator;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\Expr\CallLike;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\NullableType;
@ -34,7 +35,6 @@ use PhpParser\NodeAbstract;
use PhpParser\Parser;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;
use PhpParser\Node\Expr\CallLike;
class CompilerBase extends \PhpAot\Core\Translator
{
@ -84,10 +84,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected string $phpxDir = '~/workspace/projects/phpx';
protected string $lang = 'PHP';
protected string $cppCompiler = 'g++';
protected array $arguments = [];
protected array $literalStrings = [];
protected int $literalStringIndex = 0;
protected int $tmpVarIndex = 0;
protected int $anonClassIndex = 0;
protected int $classIndex = 0;
@ -201,6 +201,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected ?ClassDef $classDef = null;
protected ?MethodDef $methodDef = null;
protected ?InterfaceDef $interfaceDef = null;
protected FunctionContext $context;
protected array $superGlobalVars = [
'_GET' => self::TYPE_ARRAY,
'_POST' => self::TYPE_ARRAY,
@ -211,38 +212,12 @@ class CompilerBase extends \PhpAot\Core\Translator
'_REQUEST' => self::TYPE_ARRAY,
];
protected array $globalVars = [];
/**
* @var array<string, string>
*/
protected array $objects = [];
protected array $localVars = [];
protected array $staticVars = [];
/**
* @var array<string, string>
*/
protected array $objectWrappers = [];
/**
* @var array<string, string>
*/
protected array $ceWrappers = [];
protected bool $strictTypes = false;
protected string $rootPath;
protected string $buildDir;
protected int $debugLine = 0;
protected CLImate $climate;
protected array $beforeStmtLines = [];
protected array $afterStmtLines = [];
protected bool $inLoop = false;
protected bool $inClosure = false;
protected bool $defaultNativeType = false;
/**
* 赋值表达式的左值,写操作,右值为读操作.
*/
protected bool $inAssignExpr = false;
protected bool $stubFile = false;
protected bool $enableProfiler = false;
protected Parser $parser;
@ -304,7 +279,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return $this->objects[$object] ?? 'stdClass';
}
public function parseExpr(mixed $expr)
public function parseExpr(Node\Expr $expr)
{
$type = $expr->getType();
$this->writeLog('Line ' . $this->getLine($expr) . ': ' . $type);
@ -395,9 +370,9 @@ class CompilerBase extends \PhpAot\Core\Translator
case 'Expr_Array':
return $this->parseArray($expr);
case self::EXPR_ARRAY_DIM_FETCH:
return $this->parseArrayDimFetch($expr, $this->inAssignExpr);
return $this->parseArrayDimFetch($expr, $this->context->inAssignExpr);
case self::EXPR_PROPERTY_FETCH:
return $this->parsePropertyFetch($expr, $this->inAssignExpr);
return $this->parsePropertyFetch($expr, $this->context->inAssignExpr);
case 'Expr_NullsafePropertyFetch':
return $this->parseNullsafePropertyFetch($expr);
case 'Expr_NullsafeMethodCall':
@ -451,7 +426,7 @@ class CompilerBase extends \PhpAot\Core\Translator
case 'Expr_ArrowFunction':
return $this->parseArrowFunction($expr);
case 'Name_FullyQualified':
return $expr->name;
return $this->parseFullyQualifiedName($expr);
case 'Scalar_Int':
case 'Scalar_Float':
case 'Scalar_String':
@ -507,7 +482,7 @@ class CompilerBase extends \PhpAot\Core\Translator
public function genTmpVarName(): string
{
return 'tmp_var_' . $this->tmpVarIndex++;
return 'tmp_var_' . $this->context->tmpVarIndex++;
}
public function genAnonClassName(): string
@ -563,7 +538,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function getVarType(string $name): string
{
if ($this->hasLocalVar($name)) {
return $this->localVars[$name];
return $this->context->localVars[$name];
}
if ($this->hasLocalVar($name)) {
return $this->globalVars[$name];
@ -574,17 +549,9 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function resetFunction(): void
{
$this->localVars = [];
$this->staticVars = [];
$this->arguments = [];
$this->objects = [];
$this->objectWrappers = [];
$this->ceWrappers = [];
$this->tmpVarIndex = 0;
$this->inLoop = false;
$this->context = new FunctionContext();
$this->function = '';
$this->functionDef = null;
$this->inClosure = false;
}
protected function resetMethod(): void
@ -739,12 +706,12 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function getCeWrapper(string $className): string
{
if (isset($this->ceWrappers[$className])) {
return $this->ceWrappers[$className];
if (isset($this->context->ceWrappers[$className])) {
return $this->context->ceWrappers[$className];
}
$object = $this->addTmpVar(self::TYPE_OBJECT);
$this->beforeStmtLines[] = 'Z_PTR_P(' . $object . '.ptr()) = ' . $this->getClassEntryPtr($className) . ';';
$this->ceWrappers[$className] = $object;
$this->context->beforeStmtLines[] = 'Z_PTR_P(' . $object . '.ptr()) = ' . $this->getClassEntryPtr($className) . ';';
$this->context->ceWrappers[$className] = $object;
return $object;
}
@ -943,6 +910,7 @@ class CompilerBase extends \PhpAot\Core\Translator
case self::EXPR_VARIABLE:
return $this->parseVariable($expr);
case 'Name':
case 'Name_FullyQualified':
case 'VarLikeIdentifier':
case 'Identifier':
return $expr->name;
@ -1053,9 +1021,9 @@ class CompilerBase extends \PhpAot\Core\Translator
*/
protected function parseBeforeStmtLines(): string
{
if ($this->beforeStmtLines) {
$code = implode(PHP_EOL, $this->beforeStmtLines);
$this->beforeStmtLines = [];
if ($this->context->beforeStmtLines) {
$code = implode(PHP_EOL, $this->context->beforeStmtLines);
$this->context->beforeStmtLines = [];
return $code . PHP_EOL;
}
@ -1065,12 +1033,12 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseStmts(array $stmts): string
{
$lines = [];
$inLoopTop = $this->inLoop;
$inLoopTop = $this->context->inLoop;
$last = array_key_last($stmts);
foreach ($stmts as $i => $v) {
$class = $v->getType();
$this->beforeStmtLines = [];
$this->afterStmtLines = [];
$this->context->beforeStmtLines = [];
$this->context->afterStmtLines = [];
$result = '';
$this->writeLog('Line ' . $this->getLine($v) . ': ' . $class);
$lines[] = $this->genDebugInfo($v);
@ -1086,29 +1054,29 @@ class CompilerBase extends \PhpAot\Core\Translator
$result = $this->parseReturn($v);
break;
case 'Stmt_For':
$this->inLoop = true;
$this->context->inLoop = true;
$result = $this->parseFor($v);
$this->inLoop = $inLoopTop;
$this->context->inLoop = $inLoopTop;
break;
case 'Stmt_Foreach':
$this->inLoop = true;
$this->context->inLoop = true;
$result = $this->parseForeach($v);
$this->inLoop = $inLoopTop;
$this->context->inLoop = $inLoopTop;
break;
case 'Stmt_Switch':
$this->inLoop = true;
$this->context->inLoop = true;
$result = $this->parseSwitch($v);
$this->inLoop = $inLoopTop;
$this->context->inLoop = $inLoopTop;
break;
case 'Stmt_While':
$this->inLoop = true;
$this->context->inLoop = true;
$result = $this->parseWhile($v);
$this->inLoop = $inLoopTop;
$this->context->inLoop = $inLoopTop;
break;
case 'Stmt_Do':
$this->inLoop = true;
$this->context->inLoop = true;
$result = $this->parseDo($v);
$this->inLoop = $inLoopTop;
$this->context->inLoop = $inLoopTop;
break;
case 'Stmt_If':
$result = $this->parseIf($v);
@ -1151,14 +1119,14 @@ class CompilerBase extends \PhpAot\Core\Translator
default:
abort($v);
}
$lines = array_merge($lines, $this->beforeStmtLines);
$this->beforeStmtLines = [];
$lines = array_merge($lines, $this->context->beforeStmtLines);
$this->context->beforeStmtLines = [];
if ($result) {
$lines[] = $result;
}
if ($this->afterStmtLines) {
$lines = array_merge($lines, $this->afterStmtLines);
$this->afterStmtLines = [];
if ($this->context->afterStmtLines) {
$lines = array_merge($lines, $this->context->afterStmtLines);
$this->context->afterStmtLines = [];
}
}
@ -1175,10 +1143,10 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($this->isPropertyFetch($left)) {
return $this->parseAssignPropertyArrayDim($left, $right);
}
$oriInAssignExpr = $this->inAssignExpr;
$this->inAssignExpr = true;
$oriInAssignExpr = $this->context->inAssignExpr;
$this->context->inAssignExpr = true;
$array = $this->parseIdentifier($left->var);
$this->inAssignExpr = $oriInAssignExpr;
$this->context->inAssignExpr = $oriInAssignExpr;
$code = '';
// 这是 PHP 的初始化+赋值写法,需要先创建数组
if (!$this->hasVar($array) and $this->isVarExpr($left->var)) {
@ -1227,7 +1195,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return implode(";\n" . $this->getIndent(), $list);
}
protected function parseAssignStaticProperty($left, $right)
protected function parseAssignStaticProperty($left, $right): string
{
$value = $this->trimBrackets($this->parseExpr($right));
$native = $this->parseNativeStaticPropertyFetch($left);
@ -1264,10 +1232,10 @@ class CompilerBase extends \PhpAot\Core\Translator
continue;
}
if ($item instanceof Node\Expr\ArrayItem) {
$oriInAssignExpr = $this->inAssignExpr;
$this->inAssignExpr = true;
$oriInAssignExpr = $this->context->inAssignExpr;
$this->context->inAssignExpr = true;
$var = $this->parseIdentifier($item->value);
$this->inAssignExpr = $oriInAssignExpr;
$this->context->inAssignExpr = $oriInAssignExpr;
if ($this->isVarExpr($item->value) and !$this->hasVar($var)) {
$this->addLocalVar($var, self::TYPE_VAR);
}
@ -1281,10 +1249,10 @@ class CompilerBase extends \PhpAot\Core\Translator
return $code . '}';
}
$oriInAssignExpr = $this->inAssignExpr;
$this->inAssignExpr = true;
$oriInAssignExpr = $this->context->inAssignExpr;
$this->context->inAssignExpr = true;
$var = $this->parseIdentifier($left);
$this->inAssignExpr = $oriInAssignExpr;
$this->context->inAssignExpr = $oriInAssignExpr;
if ($var === 'this_') {
$this->fatalError($left, 'Cannot re-assign $this');
}
@ -1296,12 +1264,12 @@ class CompilerBase extends \PhpAot\Core\Translator
// 类型推断,获取对象的类名
if ($this->isNewExpr($right) and $this->isNameExpr($right->class)) {
$class = $this->parseIdentifier($right->class);
$this->objects[$var] = $this->getNamespacedClassName($class);
$this->context->objects[$var] = $this->getNamespacedClassName($class);
$type = self::TYPE_OBJECT;
} elseif ($this->isFuncCallExpr($right) and $this->isNameExpr($right->name)) {
$fn = $this->parseIdentifier($right->name);
if (count($right->args) === 2 and $fn === 'objval' and $this->isScalarString($right->args[1]->value)) {
$this->objects[$var] = $this->getNamespacedClassName($this->parseIdentifier($right->args[1]->value));
$this->context->objects[$var] = $this->getNamespacedClassName($this->parseIdentifier($right->args[1]->value));
$type = self::TYPE_OBJECT;
} elseif (count($right->args) === 1 and $fn === 'any') {
$type = self::TYPE_VAR;
@ -1356,6 +1324,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseEcho(mixed $v): string
{
$lines = [];
foreach ($v->exprs as $expr) {
if ($expr instanceof Node\Expr\Assign) {
$this->fatalError($expr, 'Cannot echo assign expression');
@ -1409,9 +1378,8 @@ class CompilerBase extends \PhpAot\Core\Translator
/**
* 尽可能转为数字,优先级 浮点 > 整数 > 字符串.
* @param mixed $expr
*/
protected function parseNumericIdentifier($expr)
protected function parseNumericIdentifier(NodeAbstract $expr): float|int|string
{
if ($expr->getType() === 'Scalar_String') {
if ($this->isFloatStr($expr->value)) {
@ -1462,7 +1430,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseReturn(Node\Stmt\Return_ $v): string
{
if ($v->expr === null) {
if ($this->functionDef->returnType === self::TYPE_VOID and !$this->inClosure) {
if ($this->functionDef->returnType === self::TYPE_VOID and !$this->context->inClosure) {
return 'return;';
} else {
return 'return ' . self::VALUE_NULL . ';';
@ -1473,7 +1441,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$expr = $this->parseExpr($v->expr);
// 匿名函数的返回值一定是 var
if (!$this->inClosure) {
if (!$this->context->inClosure) {
// 函数定义时没有声明返回值,但函数体中有返回值,修改为实际的返回值类型
if ($this->getReturnType() === 'void') {
$this->resetReturnType($v, $type);
@ -1495,7 +1463,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->addLocalVar($tmpVar, $returnType);
$code = $tmpVar . ' = ' . $exprCode . ';' . PHP_EOL;
// 解析表达式后可能会插入语句,因此需要在末尾添加 return 语句,而不是直接返回
$this->afterStmtLines[] = $this->getIndent() . 'return ' . $tmpVar . ';';
$this->context->afterStmtLines[] = $this->getIndent() . 'return ' . $tmpVar . ';';
} else {
$code = 'return ' . $exprCode . ';';
}
@ -1510,7 +1478,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function addLocalVar(string $name, string $type): void
{
$this->localVars[$name] = $type;
$this->context->localVars[$name] = $type;
}
protected function addTmpVar(string $type): string
@ -1522,13 +1490,13 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function addStaticVar(string $name, string $type): void
{
$this->staticVars[$name] = $type;
$this->context->staticVars[$name] = $type;
$this->addGlobalVar($this->getStaticVarName($name), $type);
}
protected function addArgument(string $name, string $type): void
{
$this->arguments[$name] = $type;
$this->context->arguments[$name] = $type;
$this->addLocalVar($name, $type);
}
@ -1562,7 +1530,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function hasLocalVar(string $name): bool
{
return isset($this->localVars[$name]);
return isset($this->context->localVars[$name]);
}
/**
@ -1805,13 +1773,13 @@ class CompilerBase extends \PhpAot\Core\Translator
case 'mixed':
return self::TYPE_VAR;
case 'self':
$this->objects[$var] = $this->classDef->getNamespacedName(false);
$this->context->objects[$var] = $this->classDef->getNamespacedName(false);
return self::TYPE_OBJECT;
case 'resource':
$this->fatalError($param, 'Cannot use `resource` as a parameter type.');
// no break
default:
$this->objects[$var] = $this->getNamespacedClassName($name);
$this->context->objects[$var] = $this->getNamespacedClassName($name);
return self::TYPE_OBJECT;
}
}
@ -1995,11 +1963,11 @@ class CompilerBase extends \PhpAot\Core\Translator
$binaryOp = $this->removeAssignOp($op);
if ($binaryOp === '.') {
$this->beforeStmtLines[] = "{$tmpVar} = php::concat(" .
$this->context->beforeStmtLines[] = "{$tmpVar} = php::concat(" .
$this->convertVarType($tmpVar, $var) . ', ' .
$this->convertExprType($expr, $type, $rightType) . ');';
} else {
$this->beforeStmtLines[] = "{$tmpVar} = " .
$this->context->beforeStmtLines[] = "{$tmpVar} = " .
$this->convertVarType($tmpVar, $var) . ' ' .
$binaryOp . ' ' .
$this->convertExprType($expr, $type, $rightType) . ';';
@ -2114,20 +2082,20 @@ class CompilerBase extends \PhpAot\Core\Translator
return $var . '.newItem()';
}
} else {
$oriInAssignExpr = $this->inAssignExpr;
$this->inAssignExpr = false;
$oriInAssignExpr = $this->context->inAssignExpr;
$this->context->inAssignExpr = false;
$dim = $this->parseIdentifier($node->dim);
$this->inAssignExpr = $oriInAssignExpr;
$this->context->inAssignExpr = $oriInAssignExpr;
return $var . '.item(' . $this->trimBrackets($dim) . ', ' . $this->escapeBool($write) . ')';
}
}
protected function parseArrayDimStore($array, $dim, $var): string
{
$oriInAssignExpr = $this->inAssignExpr;
$this->inAssignExpr = true;
$oriInAssignExpr = $this->context->inAssignExpr;
$this->context->inAssignExpr = true;
$id = $this->parseIdentifier($array);
$this->inAssignExpr = $oriInAssignExpr;
$this->context->inAssignExpr = $oriInAssignExpr;
return $id . '.offsetSet(' . $this->trimBrackets($dim) . ', ' . $this->trimBrackets($var) . ')';
}
@ -2201,14 +2169,13 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseFuncCall(Node\Expr\FuncCall $expr): string
{
$call = '';
if ($this->isVarExpr($expr->name)) {
$fn = $this->parseIdentifier($expr->name);
$placeHolder = $fn;
$name = '';
} elseif ($expr->name->getType() === 'Name' or $expr->name->getType() === 'Name_FullyQualified') {
$name = $this->parseIdentifier($expr->name);
if (in_array($name, $this->unsupportedFunctions)) {
if (in_array($name, Constants::UNSUPPORTED_FUNCTIONS)) {
$this->fatalError($expr, 'Unsupported function: `' . $name . '`');
}
$nativeFn = $this->findNativeFunction($name);
@ -2226,11 +2193,11 @@ class CompilerBase extends \PhpAot\Core\Translator
}
$placeHolder = $this->identifierToStr($expr->name);
$fn = $this->getFuncPtr($name);
$this->beforeStmtLines[] = '// Func Call: ' . $name . '()';
$this->context->beforeStmtLines[] = '// Func Call: ' . $name . '()';
} else {
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
$this->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($expr->name) . ';';
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($expr->name) . ';';
$placeHolder = $fn = $tmpVar;
$name = '';
}
@ -2294,10 +2261,10 @@ class CompilerBase extends \PhpAot\Core\Translator
$tmpVar = $this->addTmpVar(self::TYPE_ARRAY);
foreach ($argsSlice as $item) {
if ($item->unpack) {
$this->beforeStmtLines[] = $tmpVar . '.merge(' . $this->parseArg($item) . ');';
$this->context->beforeStmtLines[] = $tmpVar . '.merge(' . $this->parseArg($item) . ');';
break;
} else {
$this->beforeStmtLines[] = $tmpVar . '.append(' . $this->parseArg($item) . ');';
$this->context->beforeStmtLines[] = $tmpVar . '.append(' . $this->parseArg($item) . ');';
}
}
$argList[] = $tmpVar;
@ -2331,12 +2298,12 @@ class CompilerBase extends \PhpAot\Core\Translator
} else {
// 本地变量,且是原生类型,则转为普通变量
if ($this->hasLocalVar($name) and $this->isNativeType($this->getVarType($name))) {
$this->localVars[$name] = self::TYPE_VAR;
$this->context->localVars[$name] = self::TYPE_VAR;
}
// 需要引用类型的参数,使用临时变量作为引用,并替换掉实际的参数
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_REF);
$this->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();';
$this->context->beforeStmtLines[] = $tmpVar . ' = ' . $this->parseExpr($arg->value) . '.toReference();';
$name = $tmpVar;
}
$list_args[] = '&' . $name;
@ -2378,8 +2345,8 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->fatalError($arg, 'The unpack expression for variadic arguments must be the last');
}
$tmpVar = $this->genTmpVarName();
$this->beforeStmtLines[] = self::TYPE_ARRAY . ' ' . $tmpVar . '{' . implode(', ', $list_args) . '};';
$this->beforeStmtLines[] = $tmpVar . '.merge(' . $this->parseArrayArg($arg) . ');';
$this->context->beforeStmtLines[] = self::TYPE_ARRAY . ' ' . $tmpVar . '{' . implode(', ', $list_args) . '};';
$this->context->beforeStmtLines[] = $tmpVar . '.merge(' . $this->parseArrayArg($arg) . ');';
return $tmpVar;
}
$list_args[] = $this->parseArg($arg);
@ -2427,8 +2394,8 @@ class CompilerBase extends \PhpAot\Core\Translator
$prop = $this->identifierToStr($expr->var->name);
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_VAR);
$this->beforeStmtLines[] = $tmpVar . ' = php::getStaticProperty(' . $class . ', ' . $prop . ');';
$this->afterStmtLines[] = 'php::setStaticProperty(' . $class . ', ' . $prop . ', ' . $tmpVar . ' ' . $op . ' 1);';
$this->context->beforeStmtLines[] = $tmpVar . ' = php::getStaticProperty(' . $class . ', ' . $prop . ');';
$this->context->afterStmtLines[] = 'php::setStaticProperty(' . $class . ', ' . $prop . ', ' . $tmpVar . ' ' . $op . ' 1);';
return $tmpVar;
}
@ -2582,29 +2549,29 @@ class CompilerBase extends \PhpAot\Core\Translator
/**
* 逻辑比较的运算,必须返回 bool 类型.
*/
protected function parseBinaryOpLogicalAnd(Node $expr): string
protected function parseBinaryOpLogicalAnd(Node\Expr\BinaryOp\LogicalAnd|Node\Expr\BinaryOp\BooleanAnd $expr): string
{
return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '&&'));
}
protected function parseBinaryOpLogicalOr(Node $expr): string
protected function parseBinaryOpLogicalOr(Node\Expr\BinaryOp\LogicalOr|Node\Expr\BinaryOp\BooleanOr $expr): string
{
return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '||'));
}
protected function parseBinaryOpLogicalXor(Node $expr): string
protected function parseBinaryOpLogicalXor(Node\Expr\BinaryOp\LogicalXor $expr): string
{
return $this->convertBoolExpr($this->parseBinaryOp($expr->left, $expr->right, '^'));
}
protected function parseBooleanNot(Node $expr): string
protected function parseBooleanNot(Node\Expr\BooleanNot $expr): string
{
$expr = $this->parseExpr($expr->expr);
return '!' . $expr;
}
protected function parseWhile(Node $v): string
protected function parseWhile(Node\Stmt\While_ $v): string
{
$cond = $this->parseExpr($v->cond);
$stmts = $v->stmts;
@ -2764,10 +2731,10 @@ class CompilerBase extends \PhpAot\Core\Translator
$classDef = $expr->class;
$className = $this->genAnonClassName();
$classDef->name = new Node\Identifier($className);
$this->beforeStmtLines[] = 'static THREAD_LOCAL bool ' . $className . '_defined = false;';
$this->context->beforeStmtLines[] = 'static THREAD_LOCAL bool ' . $className . '_defined = false;';
$classCode = $this->genEmbeddedCode($classDef);
$this->addConstData($className . '_code', $classCode);
$this->beforeStmtLines[] = 'if (!' . $className . '_defined) {'
$this->context->beforeStmtLines[] = 'if (!' . $className . '_defined) {'
. $className . '_defined = true; php::eval((const char *)' . $className . '_code);}';
$className = '\\' . $className;
$cePtr = $this->getClassEntryPtr($className);
@ -2861,14 +2828,14 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'php::constant("' . $this->escapeString($name) . '")';
}
protected function parseUnaryMinus(Node $expr): string
protected function parseUnaryMinus(Node\Expr\UnaryMinus $expr): string
{
$code = $this->parseExpr($expr->expr);
return '-' . $code;
}
protected function parseUnaryPlus(mixed $expr)
protected function parseUnaryPlus(Node\Expr\UnaryPlus $expr)
{
return $this->parseExpr($expr->expr);
}
@ -2897,12 +2864,12 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'php::concat({' . implode(', ', $list) . '})';
}
protected function parseInterpolatedStringPart(Node $expr): string
protected function parseInterpolatedStringPart(Node\InterpolatedStringPart $expr): string
{
return '"' . $this->escapeString($expr->value) . '"';
}
protected function parseGlobal(Node $v): string
protected function parseGlobal(Node\Stmt\Global_ $v): string
{
foreach ($v->vars as $v) {
$name = $this->escapeVarName($v->name);
@ -3001,7 +2968,7 @@ class CompilerBase extends \PhpAot\Core\Translator
if ($objectName === 'this_') {
$nativeProperty = $this->findNativeProperty($object, $propertyName, $this->class, $this->namespace);
} elseif ($this->isTypedObject($objectName)) {
$nativeProperty = $this->findNativeProperty($object, $propertyName, $this->objects[$objectName]);
$nativeProperty = $this->findNativeProperty($object, $propertyName, $this->context->objects[$objectName]);
}
if ($nativeProperty) {
return $nativeProperty;
@ -3052,13 +3019,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseForeachArray(Foreach_ $node, string $iteratorVar): string
{
if ($node->keyVar) {
$keyVar = $this->parseIdentifier($node->keyVar);
}
$code = 'for (auto iter = ' . $iteratorVar . '.begin(); iter != ' . $iteratorVar . '.end(); ++iter) {' . PHP_EOL;
$this->indentLevel++;
if ($node->keyVar) {
$keyVar = $this->parseIdentifier($node->keyVar);
$this->checkVar($node, $keyVar);
$code .= $this->getIndent() . ' ' . $keyVar . ' = iter.key();' . PHP_EOL;
}
@ -3175,7 +3139,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$var_def = $type . ' ' . $tmp_var . ' = ' . $this->parseExpr($cond) . ';' . PHP_EOL;
// 保存作用域,switch 可能会解析失败,在这个过程中会增加变量,需重置
$localVars = $this->localVars;
$localVars = $this->context->localVars;
$code = $this->parseBeforeStmtLines() . PHP_EOL;
if ($type === self::TYPE_INT or $type === self::TYPE_BOOL) {
@ -3187,7 +3151,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} else {
$condType = $case->cond->getType();
if ($condType !== 'Scalar_Int' and $condType !== 'Scalar_Float') {
$this->localVars = $localVars;
$this->context->localVars = $localVars;
goto _fail;
}
$code .= $this->getIndent() . 'case ' . $this->parseScalar($case->cond) . ': {' . PHP_EOL;
@ -3314,7 +3278,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseBreak(mixed $v): string
{
if (!$this->inLoop) {
if (!$this->context->inLoop) {
$this->fatalError($v, 'Cannot break outside loop');
}
$num = $v->num;
@ -3330,7 +3294,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseContinue(Node\Stmt\Continue_ $v): string
{
if (!$this->inLoop) {
if (!$this->context->inLoop) {
$this->fatalError($v, 'Cannot continue outside loop');
}
if ($v->num and $v->num->value > 1) {
@ -3339,7 +3303,7 @@ class CompilerBase extends \PhpAot\Core\Translator
return 'continue;';
}
protected function parseScalarFloat(Node $expr): string
protected function parseScalarFloat(Node\Scalar\Float_ $expr): string
{
$value = $expr->value;
@ -3361,16 +3325,16 @@ class CompilerBase extends \PhpAot\Core\Translator
if (count($vars) > 1) {
$list = [];
foreach ($vars as $var) {
$list[] = $this->parseChainedExpr($var, 'isset');
$list[] = $this->parseChainedExpr($var, self::OP_ISSET);
}
return '(' . implode(' && ', $list) . ')';
}
return $this->parseChainedExpr($vars[0], 'isset');
return $this->parseChainedExpr($vars[0], self::OP_ISSET);
}
protected function parseEmpty(Node\Expr\Empty_ $expr): string
{
return $this->parseChainedExpr($expr->expr, 'empty');
return $this->parseChainedExpr($expr->expr, self::OP_EMPTY);
}
/**
@ -3410,7 +3374,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} else {
$var = $this->genTmpVarName();
$this->addLocalVar($var, self::TYPE_VAR);
$this->beforeStmtLines[] = $var . '=' . $this->parseExpr($expr) . ';';
$this->context->beforeStmtLines[] = $var . '=' . $this->parseExpr($expr) . ';';
break;
}
$expr = $expr->var;
@ -3432,7 +3396,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function hasStaticVar(string $name): bool
{
return array_key_exists($name, $this->staticVars);
return array_key_exists($name, $this->context->staticVars);
}
protected function parseCastDouble(mixed $expr): string
@ -3480,14 +3444,14 @@ class CompilerBase extends \PhpAot\Core\Translator
return $id;
}
if (isset($this->objectWrappers[$id])) {
return $this->objectWrappers[$id];
if (isset($this->context->objectWrappers[$id])) {
return $this->context->objectWrappers[$id];
}
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, self::TYPE_OBJECT);
$this->beforeStmtLines[] = $this->getIndent() . $tmpVar . ' = ' . $id . ';';
$this->objectWrappers[$id] = $tmpVar;
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . ' = ' . $id . ';';
$this->context->objectWrappers[$id] = $tmpVar;
return $tmpVar;
}
@ -3497,7 +3461,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->checkLeftValue($expr);
$var = $this->parseIdentifier($expr);
if ($this->isVarExpr($expr) and $this->isNativeTypeVar($var)) {
$this->localVars[$var] = self::TYPE_VAR;
$this->context->localVars[$var] = self::TYPE_VAR;
}
return $this->parseIdentifier($expr) . '.toReference()';
}
@ -3505,9 +3469,9 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseAssignRef(Node\Expr\AssignRef $expr): string
{
if ($this->isVarExpr($expr->var)) {
$this->inAssignExpr = true;
$this->context->inAssignExpr = true;
$left = $this->parseIdentifier($expr->var);
$this->inAssignExpr = false;
$this->context->inAssignExpr = false;
if (!$this->hasVar($left)) {
$this->addLocalVar($left, self::TYPE_REF);
} else {
@ -3553,7 +3517,7 @@ class CompilerBase extends \PhpAot\Core\Translator
// 可转为原生调用的 MethodCall
if ($this->isVarExpr($expr->var) and $this->isNamedMethod($expr->name)) {
$this->beforeStmtLines[] = '// Method Call: ' . $object . '->' . $this->parseIdentifier($expr->name) . '()';
$this->context->beforeStmtLines[] = '// Method Call: ' . $object . '->' . $this->parseIdentifier($expr->name) . '()';
$nativeFunc = $this->findNativeMethod($expr, $object, $this->parseIdentifier($expr->name));
if ($nativeFunc) {
$expr->setAttribute('nativeCall', $nativeFunc);
@ -3639,7 +3603,7 @@ class CompilerBase extends \PhpAot\Core\Translator
} elseif ($class === 'static') {
$methodPtr = $this->identifierToStr($expr->name, literal: true);
$fn = 'php_get_called_ce(this_), php::getMethod(php_get_called_ce(this_), ' . $methodPtr . ')';
$this->beforeStmtLines[] = '// Static Method Call: static::' . $this->parseIdentifier($expr->name) . '()';
$this->context->beforeStmtLines[] = '// Static Method Call: static::' . $this->parseIdentifier($expr->name) . '()';
$placeHolder = $this->genArray(['php_get_called_class(this_)', $methodPtr]);
} elseif ($this->isNameExpr($expr->class)) {
if ($class === 'self') {
@ -3652,7 +3616,7 @@ class CompilerBase extends \PhpAot\Core\Translator
_do_call:
$method = $this->parseIdentifier($expr->name);
$this->beforeStmtLines[] = '// Static Method Call: ' . $class . '::' . $method . '()';
$this->context->beforeStmtLines[] = '// Static Method Call: ' . $class . '::' . $method . '()';
if ($this->isNameExpr($expr->class) and $this->isIdExpr($expr->name)) {
$callScope = [$this->genCharPtr($class, true), $this->genCharPtr($method)];
@ -4049,10 +4013,10 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function parseErrorSuppress(Node\Expr\ErrorSuppress $expr): string
{
$tmpVar = $this->genTmpVarName();
$this->beforeStmtLines[] = 'auto ' . $tmpVar . ' = EG(error_reporting);';
$this->beforeStmtLines[] = 'php::call(' . $this->getFuncPtr('error_reporting') . ', {E_FATAL_ERRORS});';
$this->context->beforeStmtLines[] = 'auto ' . $tmpVar . ' = EG(error_reporting);';
$this->context->beforeStmtLines[] = 'php::call(' . $this->getFuncPtr('error_reporting') . ', {E_FATAL_ERRORS});';
$code = $this->parseExpr($expr->expr);
$this->afterStmtLines[] = 'php::call(' . $this->getFuncPtr('error_reporting') . ', {' . $tmpVar . '});';
$this->context->afterStmtLines[] = 'php::call(' . $this->getFuncPtr('error_reporting') . ', {' . $tmpVar . '});';
return $code;
}
@ -4158,7 +4122,7 @@ class CompilerBase extends \PhpAot\Core\Translator
protected function genLocalVarDecl(): string
{
$code = '';
foreach ($this->localVars as $name => $type) {
foreach ($this->context->localVars as $name => $type) {
if (isset($this->arguments[$name])) {
continue;
}
@ -4194,8 +4158,8 @@ class CompilerBase extends \PhpAot\Core\Translator
{
$cb = function () use ($expr) {
$code = $this->parseExpr($expr->expr);
if ($this->beforeStmtLines) {
$beforeCode = implode(PHP_EOL, $this->beforeStmtLines);
if ($this->context->beforeStmtLines) {
$beforeCode = implode(PHP_EOL, $this->context->beforeStmtLines);
} else {
$beforeCode = '';
}
@ -4228,10 +4192,10 @@ class CompilerBase extends \PhpAot\Core\Translator
$this->checkLeftValue($expr->var);
$isset = $this->parseChainedExpr($expr->var, self::OP_ISSET);
$inAssignExpr = $this->inAssignExpr;
$this->inAssignExpr = true;
$inAssignExpr = $this->context->inAssignExpr;
$this->context->inAssignExpr = true;
$var = $this->parseIdentifier($expr->var);
$this->inAssignExpr = $inAssignExpr;
$this->context->inAssignExpr = $inAssignExpr;
$right = $this->parseExpr($expr->expr);
if ($this->isVarExpr($expr->expr) and !$this->hasVar($right)) {
@ -4263,7 +4227,7 @@ class CompilerBase extends \PhpAot\Core\Translator
foreach ($items as $item) {
$value = $this->parseIdentifier($item->value);
if ($item->unpack) {
$this->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.merge(' . $value . ');';
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.merge(' . $value . ');';
} elseif ($item->key) {
$key = $this->parseIdentifier($item->key);
if (str_starts_with($key, self::LITERAL_STRINGS)) {
@ -4271,14 +4235,14 @@ class CompilerBase extends \PhpAot\Core\Translator
} elseif ($key === '0L') {
$key = 'php::zero';
}
$this->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.set(' . $key . ', ' . $value . ');';
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.set(' . $key . ', ' . $value . ');';
} else {
$this->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.append(' . $value . ');';
$this->context->beforeStmtLines[] = $this->getIndent() . $tmpVar . '.append(' . $value . ');';
}
}
// 释放临时变量,避免修改数组产生数组复制操作
$this->afterStmtLines[] = $this->getIndent() . $tmpVar . '.unset();';
$this->context->afterStmtLines[] = $this->getIndent() . $tmpVar . '.unset();';
return $tmpVar;
}
@ -4317,7 +4281,7 @@ class CompilerBase extends \PhpAot\Core\Translator
}
}
$object = $this->addTmpVar(self::TYPE_OBJECT);
$this->beforeStmtLines[] = $this->getIndent() . $object . ' = ' . $this->parseIdentifier($expr) . ';';
$this->context->beforeStmtLines[] = $this->getIndent() . $object . ' = ' . $this->parseIdentifier($expr) . ';';
break;
}
}
@ -4327,7 +4291,7 @@ class CompilerBase extends \PhpAot\Core\Translator
$tmpFn = $this->genTmpVarName();
$code = $comment . PHP_EOL . 'auto ' . $tmpFn . ' = [&]() -> ' . self::TYPE_VAR . '{' . PHP_EOL;
$update = $this->escapeBool($this->inAssignExpr);
$update = $this->escapeBool($this->context->inAssignExpr);
foreach ($list as $key => $item) {
$tmpVar = $this->addTmpVar($key !== $last ? self::TYPE_OBJECT : self::TYPE_VAR);
@ -4341,7 +4305,12 @@ class CompilerBase extends \PhpAot\Core\Translator
$object = $tmpVar;
}
$code .= $this->getIndent() . "return $object; };";
$this->beforeStmtLines[] = $code;
$this->context->beforeStmtLines[] = $code;
return "$tmpFn()";
}
protected function parseFullyQualifiedName(Node\Name\FullyQualified $expr): string
{
return $expr->name;
}
}

@ -54,4 +54,12 @@ class Constants
'template',
'errno', // Linux error code
];
public const UNSUPPORTED_FUNCTIONS = [
'compact',
'extract',
'func_num_args',
'func_get_arg',
'func_get_args',
];
}

@ -0,0 +1,45 @@
<?php
namespace PhpAot\Php\Context;
class FunctionContext
{
/**
* @var array<string, string>
*/
public array $objects = [];
public array $localVars = [];
public array $staticVars = [];
/**
* @var array<string, string>
*/
public array $objectWrappers = [];
/**
* @var array<string, string>
*/
public array $ceWrappers = [];
public int $tmpVarIndex = 0;
public array $arguments = [];
public bool $inLoop = false;
public bool $inClosure = false;
/**
* 赋值表达式的左值,写操作,右值为读操作.
*/
public bool $inAssignExpr = false;
public array $beforeStmtLines = [];
public array $afterStmtLines = [];
function __construct()
{
$this->localVars = [];
$this->staticVars = [];
$this->arguments = [];
$this->objects = [];
$this->objectWrappers = [];
$this->ceWrappers = [];
$this->tmpVarIndex = 0;
$this->inLoop = false;
$this->inClosure = false;
$this->inAssignExpr = false;
}
}

@ -8,6 +8,7 @@
namespace PhpAot\Php\Generator;
use PhpAot\Php\Context\FunctionContext;
use PhpParser\NodeAbstract;
trait ClosureGenerator
@ -35,27 +36,14 @@ trait ClosureGenerator
. self::TYPE_ARGS . ' &vars_) ' .
'-> ' . self::TYPE_VAR . ' {' . PHP_EOL;
if (!$useCurrentScope) {
$oriLocalVars = $this->localVars;
$this->localVars = [];
$oriContext = $this->context;
if ($useCurrentScope) {
$oriBeforeStmtLines = $oriContext->beforeStmtLines;
$oriAfterStmtLines = $oriContext->afterStmtLines;
} else {
$this->context = new FunctionContext();
}
$oriObjects = $this->objects;
$oriObjectWrappers = $this->objectWrappers;
$oriCeWrappers = $this->ceWrappers;
$oriArgs = $this->arguments;
$oriInClosure = $this->inClosure;
$oriBeforeStmtLines = $this->beforeStmtLines;
$oriAfterStmtLines = $this->afterStmtLines;
$this->objects = [];
$this->objectWrappers = [];
$this->ceWrappers = [];
$this->arguments = [];
$this->inClosure = true;
$this->beforeStmtLines = [];
$this->afterStmtLines = [];
$this->indentLevel++;
foreach ($params as $i => $param) {
@ -110,17 +98,12 @@ trait ClosureGenerator
}
}
if (!$useCurrentScope) {
$this->localVars = $oriLocalVars;
$this->context = $oriContext;
$this->context->beforeStmtLines[] = $code;
if ($useCurrentScope) {
$this->context->beforeStmtLines = $oriBeforeStmtLines;
$this->context->afterStmtLines = $oriAfterStmtLines;
}
$this->objects = $oriObjects;
$this->objectWrappers = $oriObjectWrappers;
$this->ceWrappers = $oriCeWrappers;
$this->arguments = $oriArgs;
$this->inClosure = $oriInClosure;
$this->beforeStmtLines = $oriBeforeStmtLines;
$this->afterStmtLines = $oriAfterStmtLines;
$this->beforeStmtLines[] = $code;
if ($this->methodDef) {
return 'php::newClosure(' . $tmpVar . ', { ' . implode(', ', $useVars) . ' }, this_)';

@ -34,13 +34,6 @@ class Translator extends Preprocessor
protected array $registerSymbols = [];
protected array $staticPropertyList = [];
protected bool $useRegisterSymbolsFn = false;
protected array $unsupportedFunctions = [
'compact',
'extract',
'func_num_args',
'func_get_arg',
'func_get_args',
];
public function __construct(string $rootPath)
{

Loading…
Cancel
Save