修复 C++ 代码的缩进问题

master
韩天峰 5 days ago
parent 4b8d0eb685
commit 70d13a3601
  1. 20
      phpunit/code/generated-code-indentation.php
  2. 40
      phpunit/src/GeneratedCodeIndentationTest.php
  3. 30
      phpunit/src/TypeCheckGeneratorTest.php
  4. 46
      src/CompilerBase.php
  5. 20
      src/Generator/TypeCheckGenerator.php
  6. 14
      src/Parser/AssignOpTrait.php
  7. 16
      src/Parser/BinaryOpTrait.php
  8. 16
      src/Parser/ConditionalControlTrait.php
  9. 20
      src/Parser/ExceptionControlFlowTrait.php
  10. 16
      src/Parser/ForeachTrait.php
  11. 21
      src/Parser/NullsafeAccessTrait.php
  12. 31
      src/Parser/PropertyAccessTrait.php
  13. 87
      src/Parser/SelectionExpressionTrait.php
  14. 39
      src/Translator.php
  15. 17
      src/TypeSystem/NativeTypeCompatibilityTrait.php

@ -0,0 +1,20 @@
<?php
function generatedCodeIndentation(array $items): int
{
foreach ($items as $item) {
try {
if ($item) {
return 1;
} elseif ($item === 0) {
return 0;
} else {
continue;
}
} catch (RuntimeException) {
return 2;
}
}
return -1;
}

@ -0,0 +1,40 @@
<?php
use TypePhp\CompilerTest;
class GeneratedCodeIndentationTest extends \PHPUnit\Framework\TestCase
{
public function testNestedStatementsAndZendWrappersAreConsistentlyIndented(): void
{
global $translator;
$compiler = CompilerTest::create(ROOT_PATH);
$translator = $compiler;
$source = ROOT_PATH . '/phpunit/code/generated-code-indentation.php';
$compiler->addFiles([$source]);
$compiler->prepareFile($source);
$cppFile = $compiler->convertFile($source);
$this->assertNotNull($cppFile);
$code = file_get_contents($cppFile);
$this->assertIsString($code);
$this->assertStringContainsString(
"\t\twhile (tmp_var_0.next()) {\n\t\t\titem = tmp_var_0.value();",
$code,
);
$this->assertStringContainsString(
"\t\t\ttry {\n\t\t\t\tif (",
$code,
);
$this->assertStringContainsString(
"\t\t\t\t} else {\n\t\t\t\t\tif (",
$code,
);
$this->assertStringContainsString(
"\ttry {\n\t\tphp::checkCallArgCount(1, 1, false);",
$code,
);
$this->assertStringNotContainsString("\ntry {", $code);
$this->assertStringNotContainsString("\ncatch (zend_object", $code);
$this->assertDoesNotMatchRegularExpression('/}[ \\t]+}/', $code);
}
}

@ -103,4 +103,34 @@ class TypeCheckGeneratorTest extends \PHPUnit\Framework\TestCase
$this->assertStringContainsString('php::throwReturnTypeError(', $returnCode);
$this->assertStringNotContainsString('must be of type', $returnCode);
}
public function testDynamicStrictScalarArgumentsUseInlinePhpxConversions(): void
{
$compiler = CompilerTest::create(ROOT_PATH);
$this->setProtectedProperty($compiler, 'noLiteralStrings', true);
foreach ([
Type::INT => 'php::toIntArgExact',
Type::FLOAT => 'php::toFloatArgExact',
Type::BOOL => 'php::toBoolArgExact',
Type::STR => 'php::toStringArgExact',
] as $type => $helper) {
$argInfo = new ArgInfo();
$argInfo->name = 'value';
$argInfo->phpName = 'value';
$argInfo->type = $type;
$expr = $this->invokeMethod(
$compiler,
'genStrictScalarArgConversion',
[$argInfo, 'dynamic_value', 'Foo\\Bar::run', '2'],
);
$this->assertSame(
$helper . '(dynamic_value, php::Str{ZEND_STRL("Foo\\\\Bar::run")}, 2, php::Str{ZEND_STRL("value")})',
$expr,
);
$this->assertStringNotContainsString('[&]', $expr);
}
}
}

@ -1653,7 +1653,12 @@ class CompilerBase implements PropertyAccessContext
if (!$stmts) {
return '';
}
return $this->getIndent() . implode(PHP_EOL . $this->getIndent(), $stmts) . PHP_EOL;
$code = '';
foreach ($stmts as $stmt) {
$code .= $this->formatStatementFragment($stmt) . PHP_EOL;
}
return $code;
}
protected function genConditionWithCapturedStmts(NodeAbstract $cond, string $openPrefix): string
@ -1673,7 +1678,7 @@ class CompilerBase implements PropertyAccessContext
$condExpr = '(' . $condExpr . ')';
}
$condExpr = $this->convertConditionExpr($cond, $condExpr);
$code .= $openPrefix . '(' . $condExpr . ') {' . PHP_EOL;
$code .= $this->getIndent() . $openPrefix . '(' . $condExpr . ') {' . PHP_EOL;
return $code;
}
@ -1815,13 +1820,48 @@ class CompilerBase implements PropertyAccessContext
$code = '';
foreach ($lines as $line) {
$code .= $this->getIndent() . $line . PHP_EOL;
$code .= $this->formatStatementFragment($line) . PHP_EOL;
}
$this->context->leaveScope();
return $code;
}
/**
* Statement lowerers may return a multi-line fragment. Some nested lines
* already carry their absolute indentation, while simple statements do
* not. Apply the current scope indentation to every unindented physical
* line instead of only the first line of the fragment.
*/
protected function formatStatementFragment(string $fragment): string
{
$indent = $this->getIndent();
$fragment = rtrim($fragment, "\r\n");
$lines = preg_split('/\R/', $fragment);
if ($lines === false) {
return $indent . $fragment;
}
$firstContentLine = true;
foreach ($lines as &$line) {
if ($line === '') {
continue;
}
if ($firstContentLine) {
// A fragment represents one statement at the current scope.
// Its first physical line must not retain indentation captured
// from an intermediate expression-lowering context.
$line = $indent . ltrim($line);
$firstContentLine = false;
} elseif ($line[0] !== ' ' && $line[0] !== "\t") {
$line = $indent . $line;
}
}
unset($line);
return implode(PHP_EOL, $lines);
}
protected function assertMustUseResultIsConsumed(NodeAbstract $expr): void
{
$functionDef = $this->resolveCalledFunctionDef($expr);

@ -72,6 +72,26 @@ trait TypeCheckGenerator
return $code;
}
protected function genStrictScalarArgConversion(
ArgInfo $argInfo,
string $valueExpr,
string $callableName,
string $argNoExpr
): string {
$helper = match ($argInfo->type) {
Type::INT => 'php::toIntArgExact',
Type::FLOAT => 'php::toFloatArgExact',
Type::BOOL => 'php::toBoolArgExact',
Type::STR => 'php::toStringArgExact',
default => throw new \LogicException('Not a strict scalar type: ' . $argInfo->type),
};
$paramName = $argInfo->phpName ?: $this->unescapeVarName($argInfo->name);
return $helper . '(' . $valueExpr . ', '
. $this->getLiteralString($callableName) . ', ' . $argNoExpr . ', '
. $this->getLiteralString($paramName) . ')';
}
protected function genStrictScalarReturnCheck(string $valueExpr, string $returnType): string
{
if (!$this->isStrictScalarType($returnType)) {

@ -245,11 +245,11 @@ trait AssignOpTrait
protected function parseAssignToList(Expr $left, Expr $right): string
{
$items = $left->items;
$code = '{';
$code = '{' . PHP_EOL;
$this->indentLevel++;
$tmpVar = $this->genTmpVarName();
$this->addLocalVar($tmpVar, Type::VAR);
$code .= $this->getIndent() . $tmpVar . ' = ' . $this->parseExpr($right) . '; ';
$code .= $this->getIndent() . $tmpVar . ' = ' . $this->parseExpr($right) . ';' . PHP_EOL;
foreach ($items as $k => $item) {
if (!$item) {
continue;
@ -259,14 +259,16 @@ trait AssignOpTrait
if ($item->value instanceof Expr\List_) {
$nestedTmp = $this->genTmpVarName();
$this->addLocalVar($nestedTmp, Type::ARRAY);
$code .= "{$nestedTmp} = {$tmpVar}.item({$key}); ";
$code .= $this->parseAssignToList($item->value, new Variable($nestedTmp));
$code .= $this->getIndent() . "{$nestedTmp} = {$tmpVar}.item({$key});" . PHP_EOL;
$code .= $this->getIndent()
. $this->parseAssignToList($item->value, new Variable($nestedTmp))
. PHP_EOL;
} else {
$var = $this->parseWritableIdentifier($item->value);
if ($this->isVarExpr($item->value) and !$this->hasVar($var)) {
$this->addLocalVar($var, Type::VAR);
}
$code .= "{$var} = {$tmpVar}.item({$key}); ";
$code .= $this->getIndent() . "{$var} = {$tmpVar}.item({$key});" . PHP_EOL;
}
} else {
abort($item);
@ -274,7 +276,7 @@ trait AssignOpTrait
}
$this->indentLevel--;
return $code . '}';
return $code . $this->getIndent() . '}';
}
protected function parseAssignFinally(

@ -909,20 +909,24 @@ trait BinaryOpTrait
$shortCircuitValue = $op === '&&' ? 'false' : 'true';
$rightCondition = $op === '&&' ? $leftBool : '!(' . $leftBool . ')';
$code = '[&]() -> bool {';
$code .= $this->getIndent() . 'if (' . $rightCondition . ') {';
$code = '[&]() -> bool {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . 'if (' . $rightCondition . ') {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->formatCapturedStmtLines($rightBeforeStmts);
if ($rightAfterStmts) {
$rightTmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . $rightTmpVar . ' = ' . $rightExpr . ';';
$code .= $this->getIndent() . $rightTmpVar . ' = ' . $rightExpr . ';' . PHP_EOL;
$code .= $this->formatCapturedStmtLines($rightAfterStmts);
$rightExpr = $rightTmpVar;
$rightBool = $this->convertPythonObjectToBool($right, $rightExpr)
?? $this->convertBoolExpr($rightExpr, $this->detectTypeOfExpr($right));
}
$code .= $this->getIndent() . 'return ' . $rightBool . ';';
$code .= $this->getIndent() . '}';
$code .= $this->getIndent() . 'return ' . $shortCircuitValue . ';';
$code .= $this->getIndent() . 'return ' . $rightBool . ';' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
$code .= $this->getIndent() . 'return ' . $shortCircuitValue . ';' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}()';
return $code;

@ -18,7 +18,8 @@ trait ConditionalControlTrait
$arms[] = [$elseif->cond, $elseif->stmts];
}
return $this->parseBeforeStmtLines() . PHP_EOL . $this->getIndent() . $this->parseIfChain($arms, $v->else, 0) . PHP_EOL;
return $this->parseBeforeStmtLines()
. $this->parseIfChain($arms, $v->else, 0) . PHP_EOL;
}
protected function parseIfChain(array $arms, ?Node\Stmt\Else_ $else, int $index): string
@ -27,16 +28,22 @@ trait ConditionalControlTrait
if (!$else || $this->isEmptyStmtList($else->stmts)) {
return '';
}
return $this->parseBlockStmts($else->stmts);
return $this->parseStmts($else->stmts);
}
[$cond, $stmts] = $arms[$index];
$code = $this->genConditionWithCapturedStmts($cond, 'if ');
$code .= $this->parseBlockStmts($stmts);
$tail = $this->parseIfChain($arms, $else, $index + 1);
if ($tail !== '') {
$hasTail = isset($arms[$index + 1]) || ($else && !$this->isEmptyStmtList($else->stmts));
if ($hasTail) {
$code .= $this->getIndent() . '} else {' . PHP_EOL;
$this->indentLevel++;
$tail = $this->parseIfChain($arms, $else, $index + 1);
$code .= $tail;
$this->indentLevel--;
if (!str_ends_with($tail, PHP_EOL)) {
$code .= PHP_EOL;
}
}
$code .= $this->getIndent() . '}';
return $code;
@ -56,4 +63,3 @@ trait ConditionalControlTrait
* 逻辑比较的运算,必须返回 bool 类型.
*/
}

@ -45,7 +45,7 @@ trait ExceptionControlFlowTrait
protected function parseTryCatch(mixed $v): string
{
$code = $this->parseBeforeStmtLines() . PHP_EOL;
$code = $this->parseBeforeStmtLines();
$code .= 'try {';
$finally = $v->finally;
$stmts = $finally ? $this->injectFinallyBeforeReturn($v->stmts, $finally->stmts) : $v->stmts;
@ -68,23 +68,23 @@ trait ExceptionControlFlowTrait
$code .= PHP_EOL;
$code .= $this->parseBlockStmts($stmts);
$code .= $this->getIndent() . '}' . PHP_EOL;
$code .= $this->getIndent() . '}';
$exVar = $this->genTmpVarName();
$this->addLocalVar($exVar, Type::VAR);
$code .= ' catch (zend_object *_ex) {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . $exVar . ' = php::catchException();' . PHP_EOL;
if ($catches) {
$catchMatched = $this->genTmpVarName();
$code .= $this->getIndent() . 'bool ' . $catchMatched . ' = false;' . PHP_EOL;
$this->indentLevel++;
foreach ($catches as $catch) {
$code .= $this->parseCatch($catch, $exVar, $catchMatched, $finally?->stmts ?? []);
$code .= $this->parseCatch($catch, $exVar, $catchMatched, $finally?->stmts ?? []) . PHP_EOL;
}
$this->indentLevel--;
}
$code .= '}' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
if ($finally) {
$code .= $this->parseStmts($finally->stmts);
@ -93,7 +93,11 @@ trait ExceptionControlFlowTrait
$rethrow = $this->inGeneratorBody
? 'typephp_fiber_rethrow(' . $exVar . ');'
: 'php::throwException(php::Object(' . $exVar . '));';
$code .= 'if (' . $exVar . ') {' . PHP_EOL . $this->getIndent() . $rethrow . PHP_EOL . $this->getIndent() . '}';
$code .= 'if (' . $exVar . ') {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . $rethrow . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}';
return $code;
}
@ -199,7 +203,7 @@ trait ExceptionControlFlowTrait
$this->addLocalVar($var, Type::OBJECT);
}
$code = $this->parseBeforeStmtLines() . PHP_EOL;
$code = $this->parseBeforeStmtLines();
$code .= $this->getIndent() . 'if (!' . $catchMatched . ' && ' . $exVar . ' && ';
$conditions = [];
foreach ($types as $type) {

@ -27,7 +27,7 @@ trait ForeachTrait
if ($item->value instanceof Expr\List_) {
$nestedTmpVar = $this->genTmpVarName();
$this->addLocalVar($nestedTmpVar, Type::VAR);
$code .= $this->getIndent() . ' ' . $nestedTmpVar . ' = ' . $listTmpVar . '.item(' . $key . ');' . PHP_EOL;
$code .= $this->getIndent() . $nestedTmpVar . ' = ' . $listTmpVar . '.item(' . $key . ');' . PHP_EOL;
$code .= $this->parseForeachItemAsList($nestedTmpVar, $item->value->items);
continue;
}
@ -35,7 +35,7 @@ trait ForeachTrait
if ($this->isVarExpr($item->value) and !$this->hasVar($var)) {
$this->addLocalVar($var, Type::VAR);
}
$code .= $this->getIndent() . ' ' . $var . ' = ' . $listTmpVar . '.item(' . $key . ');' . PHP_EOL;
$code .= $this->getIndent() . $var . ' = ' . $listTmpVar . '.item(' . $key . ');' . PHP_EOL;
} else {
$this->fatalError($item, 'Unsupported foreach item type');
}
@ -56,7 +56,7 @@ trait ForeachTrait
$keyVar = $this->parseIdentifier($node->keyVar);
$this->checkVar($node, $keyVar, $defaultType);
return $this->getIndent() . ' ' . $keyVar . ' = ' . $keyExpr . ';' . PHP_EOL;
return $this->getIndent() . $keyVar . ' = ' . $keyExpr . ';' . PHP_EOL;
}
protected function parseForeachValueAssignment(Foreach_ $node, string $valueExpr, ?string $valueRefExpr = null): string
@ -75,7 +75,7 @@ trait ForeachTrait
}
$listTmpVar = $this->genTmpVarName();
$this->addLocalVar($listTmpVar, Type::VAR);
return $this->getIndent() . ' ' . $listTmpVar . ' = ' . $valueExpr . ';' . PHP_EOL
return $this->getIndent() . $listTmpVar . ' = ' . $valueExpr . ';' . PHP_EOL
. $this->parseForeachItemAsList($listTmpVar, $node->valueVar->items);
}
@ -106,7 +106,7 @@ trait ForeachTrait
$this->fatalError($node, 'Cannot bind foreach reference to native variable of type ' . $this->getVarType($valueVar));
}
}
return $this->getIndent() . ' ' . $valueRefExpr . '(' . $valueVar . ');' . PHP_EOL;
return $this->getIndent() . $valueRefExpr . '(' . $valueVar . ');' . PHP_EOL;
}
if ($this->isVarExpr($node->valueVar)) {
@ -114,7 +114,7 @@ trait ForeachTrait
$this->checkVar($node, $valueVar);
}
}
return $this->getIndent() . ' ' . $valueVar . ' = ' . $valueExpr . ';' . PHP_EOL;
return $this->getIndent() . $valueVar . ' = ' . $valueExpr . ';' . PHP_EOL;
}
protected function parseForeachIterable(Foreach_ $node, string $iterableVar): string
@ -138,8 +138,8 @@ trait ForeachTrait
$body = $this->parseForeachBody($node);
$this->indentLevel--;
$code .= $this->parseBeforeStmtLines() . PHP_EOL;
$code .= $body . PHP_EOL;
$code .= $this->parseBeforeStmtLines();
$code .= $body;
$code .= $this->getIndent() . '}';
$this->indentLevel--;
$code .= PHP_EOL . $this->getIndent() . '}';

@ -86,16 +86,21 @@ trait NullsafeAccessTrait
$tmpFn = $this->genTmpVarName();
$code = $comment . 'auto ' . $tmpFn . ' = [&]() -> ' . Type::VAR . ' {' . PHP_EOL;
$this->indentLevel++;
foreach ($list as $key => $item) {
$tmpVar = $this->addTmpVar($key !== $last ? Type::OBJECT : Type::VAR);
$ownedTmpVars[] = $tmpVar;
if ($item[3]) {
$code .= "if ({$object}.isNull()) { return " . self::VALUE_NULL . '; }';
$code .= $this->getIndent() . "if ({$object}.isNull()) {" . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . 'return ' . self::VALUE_NULL . ';' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
}
if ($item[0] == 'property') {
$update = $this->escapeAttrMode($this->isPropertyFetchUpdate($item[2]));
$code .= $this->getIndent() . "{$tmpVar} = {$object}.attr({$item[1]}, {$update});";
$code .= $this->getIndent() . "{$tmpVar} = {$object}.attr({$item[1]}, {$update});" . PHP_EOL;
} else {
$methodName = $this->isNamedMethod($item[4]->name)
? $this->parseIdentifier($item[4]->name)
@ -110,22 +115,24 @@ trait NullsafeAccessTrait
$this->context->beforeStmtLines = array_slice($this->context->beforeStmtLines, 0, $beforeStmtCount);
$this->context->afterStmtLines = array_slice($this->context->afterStmtLines, 0, $afterStmtCount);
if ($argBeforeStmts) {
$code .= $this->getIndent() . implode(PHP_EOL . $this->getIndent(), $argBeforeStmts) . PHP_EOL;
$code .= $this->formatCapturedStmtLines($argBeforeStmts);
}
if ($requiresDynamicScope && $this->methodDef) {
$code .= $this->getIndent()
. "{$tmpVar} = php::callScoped({$object}, {$item[1]}, "
. $this->getCallableScopeExpr() . ", {$args});";
. $this->getCallableScopeExpr() . ", {$args});" . PHP_EOL;
} else {
$code .= $this->getIndent() . "{$tmpVar} = {$object}.call({$item[1]}, {$args});";
$code .= $this->getIndent() . "{$tmpVar} = {$object}.call({$item[1]}, {$args});" . PHP_EOL;
}
if ($argAfterStmts) {
$code .= $this->getIndent() . implode(PHP_EOL . $this->getIndent(), $argAfterStmts) . PHP_EOL;
$code .= $this->formatCapturedStmtLines($argAfterStmts);
}
}
$object = $tmpVar;
}
$code .= $this->getIndent() . "return {$object}; };";
$code .= $this->getIndent() . "return {$object};" . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '};';
$this->context->beforeStmtLines[] = $code;
// C++ temporaries are function-scoped; release their zvals at the PHP statement boundary.

@ -694,18 +694,25 @@ trait PropertyAccessTrait
. ', ' . $tmpVar . '.typeStr())';
}
$coercion = $this->compositeTypeNeedsIntToFloatCoercion($typeCheck)
? 'if (' . $tmpVar . '.isInt()) { ' . $tmpVar . ' = php::toFloat(' . $tmpVar . '); } '
: '';
return '([&]() -> ' . Type::VAR . ' { '
. $tmpVar . ' = ' . $rightExpr . '; '
. $coercion
. 'if (UNEXPECTED(!(' . implode(' || ', $conditions) . '))) { '
. $throwExpr . '; '
. '} '
. 'return ' . $tmpVar . '; '
. '}())';
$code = '([&]() -> ' . Type::VAR . ' {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . $tmpVar . ' = ' . $rightExpr . ';' . PHP_EOL;
if ($this->compositeTypeNeedsIntToFloatCoercion($typeCheck)) {
$code .= $this->getIndent() . 'if (' . $tmpVar . '.isInt()) {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . $tmpVar . ' = php::toFloat(' . $tmpVar . ');' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
}
$code .= $this->getIndent() . 'if (UNEXPECTED(!(' . implode(' || ', $conditions) . '))) {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . $throwExpr . ';' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
$code .= $this->getIndent() . 'return ' . $tmpVar . ';' . PHP_EOL;
$this->indentLevel--;
return $code . $this->getIndent() . '}())';
}
private function getObjectPropertyAssignTypeCheck(PropertyDef $def): array

@ -69,20 +69,26 @@ trait SelectionExpressionTrait
$ternaryType = $nativeSelection
? $this->getNativeObjectPointerType($nativeClass)
: $this->getNormalAssignType($typeChanged ? Type::VAR : $ifType);
$code = '[&]() -> ' . $ternaryType . '{';
$code = '[&]() -> ' . $ternaryType . ' {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->formatCapturedStmtLines($condBeforeStmts);
if ($condAfterStmts) {
$condTmpVar = $this->addTmpVar(Type::VAR);
$code .= $this->getIndent() . "{$condTmpVar} = {$cond};";
$code .= $this->getIndent() . "{$condTmpVar} = {$cond};" . PHP_EOL;
$code .= $this->formatCapturedStmtLines($condAfterStmts);
$cond = $condTmpVar;
}
$cond = $this->convertConditionExpr($expr->cond, $cond);
$code .= $this->getIndent() . 'if (' . $cond . ') {';
$code .= $this->getIndent() . 'if (' . $cond . ') {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->formatTernaryReturn($expr->if, $if, $ifBeforeStmts, $ifAfterStmts, $ternaryType, $ifType, $nativeClass);
$code .= $this->getIndent() . '} else {';
$this->indentLevel--;
$code .= $this->getIndent() . '} else {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->formatTernaryReturn($expr->else, $else, $elseBeforeStmts, $elseAfterStmts, $ternaryType, $elseType, $nativeClass);
$code .= $this->getIndent() . '}';
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}()';
return $code;
}
@ -114,13 +120,13 @@ trait SelectionExpressionTrait
} else {
$tmpVar = $this->addTmpVar($returnType);
}
$code .= $this->getIndent() . "{$tmpVar} = {$value};";
$code .= $this->getIndent() . "{$tmpVar} = {$value};" . PHP_EOL;
$code .= $this->formatCapturedStmtLines($afterStmts);
$code .= $this->getIndent() . 'return ' . $tmpVar . ';';
$code .= $this->getIndent() . 'return ' . $tmpVar . ';' . PHP_EOL;
} else {
$code .= $returnType === Type::VAR
? $this->getIndent() . 'return php::Var(' . $value . ');'
: $this->getIndent() . 'return ' . $value . ';';
? $this->getIndent() . 'return php::Var(' . $value . ');' . PHP_EOL
: $this->getIndent() . 'return ' . $value . ';' . PHP_EOL;
}
return $code;
}
@ -152,7 +158,8 @@ trait SelectionExpressionTrait
$var = $tmpVar;
}
$code = '[&]() -> ' . $returnType . '{';
$code = '[&]() -> ' . $returnType . ' {' . PHP_EOL;
$this->indentLevel++;
$default = null;
foreach ($expr->arms as $arm) {
if ($arm->conds === null) {
@ -160,14 +167,15 @@ trait SelectionExpressionTrait
continue;
}
$matched = $this->genTmpVarName();
$code .= $this->getIndent() . 'bool ' . $matched . ' = false;';
$code .= $this->getIndent() . 'bool ' . $matched . ' = false;' . PHP_EOL;
foreach ($arm->conds as $cond) {
if ($this->isMatchExpr($cond)) {
$this->fatalError($arm, 'Match expression cannot be used as a condition');
}
$this->assertExprCanBeUsedAsValue($cond, 'match arm condition');
[$condValue, $beforeStmts, $afterStmts] = $this->parseExprWithCapturedStmts($cond);
$code .= $this->getIndent() . 'if (!' . $matched . ') {';
$code .= $this->getIndent() . 'if (!' . $matched . ') {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->formatCapturedStmtLines($beforeStmts);
if ($afterStmts) {
$condClass = $this->detectClassOfExpr($cond);
@ -178,7 +186,7 @@ trait SelectionExpressionTrait
} else {
$condTmpVar = $this->addTmpVar(Type::VAR);
}
$code .= $this->getIndent() . "{$condTmpVar} = {$condValue};";
$code .= $this->getIndent() . "{$condTmpVar} = {$condValue};" . PHP_EOL;
$code .= $this->formatCapturedStmtLines($afterStmts);
$condValue = $condTmpVar;
}
@ -197,27 +205,38 @@ trait SelectionExpressionTrait
// its expression must still be evaluated for effects.
$comparison = '(static_cast<void>(' . $condValue . '), false)';
}
$code .= $this->getIndent() . $matched . ' = (' . $comparison . ');';
$code .= $this->getIndent() . $matched . ' = (' . $comparison . ');' . PHP_EOL;
} else {
$code .= $this->getIndent() . $matched . ' = php::same(' . $var . ', ' . $condValue . ');';
$code .= $this->getIndent() . $matched . ' = php::same(' . $var . ', ' . $condValue . ');' . PHP_EOL;
}
$code .= $this->getIndent() . '}';
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
}
$code .= $this->getIndent() . 'if (' . $matched . ') {';
$code .= $this->getIndent() . 'if (' . $matched . ') {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->formatMatchReturn($arm->body, $nativeClass);
$code .= $this->getIndent() . '}';
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
}
if ($default) {
$code .= $this->getIndent() . '{';
$code .= $this->getIndent() . '{' . PHP_EOL;
$this->indentLevel++;
$code .= $this->formatMatchReturn($default, $nativeClass);
$code .= $this->getIndent() . '}';
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
} else {
$code .= $this->getIndent() . '{' . PHP_EOL;
$this->indentLevel++;
$code .= $nativeSelection
? $this->getIndent() . '{ php::throwException("UnhandledMatchError", "Unhandled match case"); return nullptr; }'
: $this->getIndent() . '{ return php::throwException("UnhandledMatchError", "Unhandled match case"); }';
? $this->getIndent() . 'php::throwException("UnhandledMatchError", "Unhandled match case");' . PHP_EOL
. $this->getIndent() . 'return nullptr;' . PHP_EOL
: $this->getIndent() . 'return php::throwException("UnhandledMatchError", "Unhandled match case");' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
}
$code .= '}()';
$this->indentLevel--;
$code .= $this->getIndent() . '}()';
return $code;
}
@ -238,11 +257,11 @@ trait SelectionExpressionTrait
} else {
$tmpVar = $this->addTmpVar(Type::VAR);
}
$code .= $this->getIndent() . "{$tmpVar} = {$value};";
$code .= $this->getIndent() . "{$tmpVar} = {$value};" . PHP_EOL;
$code .= $this->formatCapturedStmtLines($afterStmts);
$code .= $this->getIndent() . 'return ' . $tmpVar . ';';
$code .= $this->getIndent() . 'return ' . $tmpVar . ';' . PHP_EOL;
} else {
$code .= $this->getIndent() . 'return ' . $value . ';';
$code .= $this->getIndent() . 'return ' . $value . ';' . PHP_EOL;
}
return $code;
}
@ -332,21 +351,27 @@ trait SelectionExpressionTrait
$this->addLocalVar($leftTmp, $pointerType);
$this->addNativeObject($leftTmp, $nativeClass);
$code = '[&]() -> ' . $pointerType . '{';
$code = '[&]() -> ' . $pointerType . ' {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->formatCapturedStmtLines($leftBefore);
$code .= $this->getIndent() . $leftTmp . ' = ' . $leftValue . ';';
$code .= $this->getIndent() . $leftTmp . ' = ' . $leftValue . ';' . PHP_EOL;
$code .= $this->formatCapturedStmtLines($leftAfter);
$code .= $this->getIndent() . 'if (' . $leftTmp . ' != nullptr) { return ' . $leftTmp . '; }';
$code .= $this->getIndent() . 'if (' . $leftTmp . ' != nullptr) {' . PHP_EOL;
$this->indentLevel++;
$code .= $this->getIndent() . 'return ' . $leftTmp . ';' . PHP_EOL;
$this->indentLevel--;
$code .= $this->getIndent() . '}' . PHP_EOL;
$code .= $this->formatCapturedStmtLines($rightBefore);
if ($rightAfter) {
$rightTmp = $this->genTmpVarName();
$this->addLocalVar($rightTmp, $pointerType);
$this->addNativeObject($rightTmp, $nativeClass);
$code .= $this->getIndent() . $rightTmp . ' = ' . $rightValue . ';';
$code .= $this->getIndent() . $rightTmp . ' = ' . $rightValue . ';' . PHP_EOL;
$code .= $this->formatCapturedStmtLines($rightAfter);
$rightValue = $rightTmp;
}
$code .= $this->getIndent() . 'return ' . $rightValue . ';';
$code .= $this->getIndent() . 'return ' . $rightValue . ';' . PHP_EOL;
$this->indentLevel--;
return $code . $this->getIndent() . '}()';
}

@ -3469,13 +3469,18 @@ CODE;
// Zend's C frames: those frames perform their cleanup after the handler
// returns with EG(exception) set. Convert back to normal Zend exception
// propagation at the outermost wrapper.
$cppCode = 'try {' . PHP_EOL;
$this->indentLevel++;
$cppCode = $this->getIndent() . 'try {' . PHP_EOL;
$this->indentLevel++;
$cppCode .= $this->genParameterCountCheck(
$argCountCheck = $this->genParameterCountCheck(
$functionDef->argCountRequired,
count($functionDef->argInfoList),
$functionDef->hasVariadicArg(),
);
if ($argCountCheck !== '') {
$cppCode .= $this->getIndent() . rtrim($argCountCheck) . PHP_EOL;
}
$callParams = '';
foreach ($functionDef->argInfoList as $k => $argInfo) {
@ -3494,7 +3499,7 @@ CODE;
$cppCode .= $this->getIndent() . $var . '.append(php::getCallArg(i));' . PHP_EOL;
}
$this->indentLevel--;
$cppCode .= '}' . PHP_EOL;
$cppCode .= $this->getIndent() . '}' . PHP_EOL;
$cppCode .= $this->genExtraNamedVariadicArgs($var);
} else {
if ($argInfo->default !== '') {
@ -3562,11 +3567,14 @@ CODE;
} else {
$cppCode .= $this->getIndent() . $fn . '(' . $callParams . ');' . PHP_EOL;
}
$cppCode .= '}' . PHP_EOL;
$cppCode .= 'catch (zend_object *) {' . PHP_EOL;
$this->indentLevel--;
$cppCode .= $this->getIndent() . '} catch (zend_object *) {' . PHP_EOL;
$this->indentLevel++;
$cppCode .= $this->getIndent() . '/* EG(exception) is already set; return control to ZendVM for frame cleanup. */' . PHP_EOL;
$cppCode .= '}' . PHP_EOL;
$cppCode .= '}' . PHP_EOL . PHP_EOL;
$this->indentLevel--;
$cppCode .= $this->getIndent() . '}' . PHP_EOL;
$this->indentLevel--;
$cppCode .= $this->getIndent() . '}' . PHP_EOL . PHP_EOL;
return $cppCode;
}
@ -3577,13 +3585,14 @@ CODE;
* 对于常驻内存型应用,执行完当前逻辑后,会立即进入长时间的事件循环等待。
* 因此,这些变量仅作为临时用途,用完后应即刻销毁,无需长期持有。
*/
$cppCode = "const char *value = " . $this->genCharPtr($entryFile, true) . ';' . PHP_EOL;
$cppCode .= 'php::Var &_SERVER = ' . $this->escapeGlobalVar('_SERVER') . ';' . PHP_EOL;
$cppCode .= '_SERVER.item("PHP_SELF", true) = value;'. PHP_EOL;
$cppCode .= '_SERVER.item("SCRIPT_NAME", true) = value;'. PHP_EOL;
$cppCode .= '_SERVER.item("SCRIPT_FILENAME", true) = value;'. PHP_EOL;
$cppCode .= '_SERVER.item("PATH_TRANSLATED", true) = value;'. PHP_EOL;
$cppCode .= '_SERVER.item("DOCUMENT_ROOT", true) = "";' . PHP_EOL;
$indent = $this->getIndent();
$cppCode = $indent . "const char *value = " . $this->genCharPtr($entryFile, true) . ';' . PHP_EOL;
$cppCode .= $indent . 'php::Var &_SERVER = ' . $this->escapeGlobalVar('_SERVER') . ';' . PHP_EOL;
$cppCode .= $indent . '_SERVER.item("PHP_SELF", true) = value;'. PHP_EOL;
$cppCode .= $indent . '_SERVER.item("SCRIPT_NAME", true) = value;'. PHP_EOL;
$cppCode .= $indent . '_SERVER.item("SCRIPT_FILENAME", true) = value;'. PHP_EOL;
$cppCode .= $indent . '_SERVER.item("PATH_TRANSLATED", true) = value;'. PHP_EOL;
$cppCode .= $indent . '_SERVER.item("DOCUMENT_ROOT", true) = "";' . PHP_EOL;
return $cppCode . PHP_EOL;
}
@ -3609,7 +3618,9 @@ CODE;
{
$name = $classDef->getNamespacedName();
$cppCode = 'ZEND_METHOD(' . $name . ', ' . $methodDef->name . ') {' . PHP_EOL;
$this->indentLevel++;
$cppCode .= $this->getIndent() . Type::OBJECT . ' this_(&execute_data->This);' . PHP_EOL;
$this->indentLevel--;
$fn = self::PREFIX . $this->getNativeMethodName($classDef, $methodDef);
$cppCode .= $this->genWrapperFunctionArgs(
$fn,

@ -242,20 +242,15 @@ trait NativeTypeCompatibilityTrait
if (($type === Type::VAR || $type === Type::REF) && $this->isStrictScalarType($argInfo->type)) {
// A native scalar ABI value has already lost its zval type. Preserve
// the dynamic value until strict_types validation has completed.
// Keep the check inside the argument expression: beforeStmtLines
// may run before an enclosing assignment or comma expression has
// initialized a compiler-generated argument temporary.
$checkedArg = 'typephp_checked_arg';
$check = rtrim($this->genStrictScalarParamCheck(
// The PHPX helper evaluates the expression exactly once and returns
// the final native ABI type without an immediately-invoked closure.
$this->checkVarAssignExpr($arg, $argInfo->type, $type);
return $this->genStrictScalarArgConversion(
$argInfo,
$checkedArg,
$expr,
$callableName,
(string) ($argIndex + 1)
));
$expr = '([&](php::Var ' . $checkedArg . ') -> php::Var {' . PHP_EOL
. $check . PHP_EOL
. $this->getIndent() . 'return ' . $checkedArg . ';' . PHP_EOL
. $this->getIndent() . '})(' . $expr . ')';
);
}
$this->checkVarAssignExpr($arg, $argInfo->type, $type);

Loading…
Cancel
Save