diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 8d88ec41..23ca9943 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.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 - */ - protected array $objects = []; - protected array $localVars = []; - protected array $staticVars = []; - - /** - * @var array - */ - protected array $objectWrappers = []; - - /** - * @var array - */ - 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; + } } diff --git a/src/Php/Constants.php b/src/Php/Constants.php index 8794425d..5587a994 100644 --- a/src/Php/Constants.php +++ b/src/Php/Constants.php @@ -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', + ]; } diff --git a/src/Php/Context/FunctionContext.php b/src/Php/Context/FunctionContext.php new file mode 100644 index 00000000..9bfbadef --- /dev/null +++ b/src/Php/Context/FunctionContext.php @@ -0,0 +1,45 @@ + + */ + public array $objects = []; + public array $localVars = []; + public array $staticVars = []; + /** + * @var array + */ + public array $objectWrappers = []; + /** + * @var array + */ + 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; + } +} \ No newline at end of file diff --git a/src/Php/Generator/ClosureGenerator.php b/src/Php/Generator/ClosureGenerator.php index ec14f020..1526c11d 100644 --- a/src/Php/Generator/ClosureGenerator.php +++ b/src/Php/Generator/ClosureGenerator.php @@ -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_)'; diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 17857140..7d464b6e 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -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) {