diff --git a/src/Php/CompilerBase.php b/src/Php/CompilerBase.php index 5dc70b9d..6f2cb288 100644 --- a/src/Php/CompilerBase.php +++ b/src/Php/CompilerBase.php @@ -34,6 +34,7 @@ use PhpParser\Node\Scalar\MagicConst; use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\UnionType; use PhpParser\NodeAbstract; +use PhpParser\NodeFinder; use PhpParser\Parser; use PhpParser\ParserFactory; use PhpParser\PrettyPrinter; @@ -4300,6 +4301,27 @@ class CompilerBase extends \PhpAot\Core\Translator protected function parseArrowFunction(Node\Expr\ArrowFunction $expr): string { + $nodeFinder = new NodeFinder(); + $vars = $nodeFinder->findInstanceOf($expr->expr, Node\Expr\Variable::class); + $uses = []; + $params = []; + + foreach ($expr->params as $i => $param) { + if ($param->byRef) { + $this->fatalError($expr, 'Closure cannot use reference parameter'); + } + if ($param->var instanceof Node\Expr\Variable) { + $params[$param->var->name] = $i; + } + } + + foreach ($vars as $var) { + if ($var->name === 'this' or !$this->hasLocalVar($var->name) or isset($params[$var->name])) { + continue; + } + $uses[] = new Node\ClosureUse($var); + } + $cb = function () use ($expr) { $code = $this->parseExpr($expr->expr); if ($this->context->beforeStmtLines) { @@ -4315,7 +4337,8 @@ class CompilerBase extends \PhpAot\Core\Translator } return $beforeCode . PHP_EOL . 'return ' . $code . ';'; }; - return $this->genClosure($expr, $expr->params, $cb, [], true); + + return $this->genClosure($expr, $expr->params, $cb, $uses); } protected function parseClosure(Node\Expr\Closure $expr): string diff --git a/src/Php/Generator/ClosureGenerator.php b/src/Php/Generator/ClosureGenerator.php index c8f31b90..2aa52d54 100644 --- a/src/Php/Generator/ClosureGenerator.php +++ b/src/Php/Generator/ClosureGenerator.php @@ -22,29 +22,25 @@ trait ClosureGenerator } /** - * @param $useCurrentScope bool 直接使用当前作用域,C++ 函数将使用 & 捕获所有闭包变量 + * @param NodeAbstract $expr + * @param array $params + * @param callable $bodyGenCb + * @param array $uses + * @return string */ - protected function genClosure(NodeAbstract $expr, array $params, callable $bodyGenCb, array $uses = [], bool $useCurrentScope = false): string + protected function genClosure(NodeAbstract $expr, array $params, callable $bodyGenCb, array $uses = []): string { $tmpVar = $this->genTmpVarName(); - // 必须使用 = 捕获,不能使用 & ,否则可能会出现悬空指针 - // 在 PHP 中 = 赋值是浅拷贝,仅增加一次引用计数,和 zval (16 字节) 封装的赋值 - $capture = $useCurrentScope ? '&' : ''; $code = $this->getIndent() . - 'php::ClosureFn ' . $tmpVar . ' = [' . $capture . '](' + 'php::ClosureFn ' . $tmpVar . ' = [](' . 'INTERNAL_FUNCTION_PARAMETERS, ' . self::TYPE_OBJECT . ' &this_, ' . self::TYPE_ARGS . ' &vars_) ' . '-> ' . self::TYPE_VAR . ' {' . PHP_EOL; $oriContext = $this->context; - if ($useCurrentScope) { - $oriBeforeStmtLines = $oriContext->beforeStmtLines; - $oriAfterStmtLines = $oriContext->afterStmtLines; - } else { - $this->context = new FunctionContext(); - } + $this->context = new FunctionContext(); $this->context->inClosure = true; $this->indentLevel++; @@ -83,14 +79,8 @@ trait ClosureGenerator } if ($useItem->byRef) { // 闭包的 use 语法,若为引用类型,可以就地创建变量 - if ($useCurrentScope) { - if (!$this->hasVar($var)) { - $this->addLocalVar($var, self::TYPE_REF); - } - } else { - if (!isset($oriContext->localVars[$var])) { - $oriContext->localVars[$var] = self::TYPE_REF; - } + if (!isset($oriContext->localVars[$var])) { + $oriContext->localVars[$var] = self::TYPE_REF; } $useVars[] = $this->convertToRef($useItem->var); } else { @@ -103,10 +93,6 @@ trait ClosureGenerator } $this->context = $oriContext; - if ($useCurrentScope) { - $this->context->beforeStmtLines = $oriBeforeStmtLines; - $this->context->afterStmtLines = $oriAfterStmtLines; - } $this->context->beforeStmtLines[] = $code; if ($this->methodDef) { diff --git a/tests/aot/arrow-func.phpt b/tests/aot/arrow_fn/001.phpt similarity index 100% rename from tests/aot/arrow-func.phpt rename to tests/aot/arrow_fn/001.phpt diff --git a/tests/aot/arrow-func-2.phpt b/tests/aot/arrow_fn/002.phpt similarity index 100% rename from tests/aot/arrow-func-2.phpt rename to tests/aot/arrow_fn/002.phpt diff --git a/tests/aot/arrow-functions.phpt b/tests/aot/arrow_fn/003.phpt similarity index 100% rename from tests/aot/arrow-functions.phpt rename to tests/aot/arrow_fn/003.phpt