大重构,新增 FunctionContext 类

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

@ -54,4 +54,12 @@ class Constants
'template', 'template',
'errno', // Linux error code '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; namespace PhpAot\Php\Generator;
use PhpAot\Php\Context\FunctionContext;
use PhpParser\NodeAbstract; use PhpParser\NodeAbstract;
trait ClosureGenerator trait ClosureGenerator
@ -35,27 +36,14 @@ trait ClosureGenerator
. self::TYPE_ARGS . ' &vars_) ' . . self::TYPE_ARGS . ' &vars_) ' .
'-> ' . self::TYPE_VAR . ' {' . PHP_EOL; '-> ' . self::TYPE_VAR . ' {' . PHP_EOL;
if (!$useCurrentScope) { $oriContext = $this->context;
$oriLocalVars = $this->localVars; if ($useCurrentScope) {
$this->localVars = []; $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++; $this->indentLevel++;
foreach ($params as $i => $param) { foreach ($params as $i => $param) {
@ -110,17 +98,12 @@ trait ClosureGenerator
} }
} }
if (!$useCurrentScope) { $this->context = $oriContext;
$this->localVars = $oriLocalVars; $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) { if ($this->methodDef) {
return 'php::newClosure(' . $tmpVar . ', { ' . implode(', ', $useVars) . ' }, this_)'; return 'php::newClosure(' . $tmpVar . ', { ' . implode(', ', $useVars) . ' }, this_)';

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

Loading…
Cancel
Save