Fix windows

pull/41/head
韩天峰 4 weeks ago
parent 606819a8ad
commit 89e58350a8
  1. 18
      examples/tetris-win32/main.php
  2. 21
      phpunit/src/ConstantExpressionValidatorTest.php
  3. 7
      src/Preprocessor.php
  4. 20
      src/Transform/ConstantExpressionValidationVisitor.php
  5. 6
      src/Transform/ConstantExpressionValidator.php
  6. 26
      src/Transform/Visitor.php
  7. 5
      src/Translator.php
  8. 1
      tests/compiler/array/array-push-empty-unpack.phpt

@ -42,8 +42,8 @@ function rgb(int $r, int $g, int $b): int
} }
// Piece colors // Piece colors
const COLOR_CYAN = 0x00FFFF; // I const COLOR_CYAN = 0xFFFF00; // I
const COLOR_YELLOW = 0x00FFFF; // O - will override below const COLOR_YELLOW = 0x00FFFF; // O
const COLOR_PURPLE = 0x800080; // T const COLOR_PURPLE = 0x800080; // T
const COLOR_GREEN = 0x00FF00; // S const COLOR_GREEN = 0x00FF00; // S
const COLOR_RED = 0x0000FF; // Z const COLOR_RED = 0x0000FF; // Z
@ -51,13 +51,13 @@ const COLOR_BLUE = 0xFF0000; // J
const COLOR_ORANGE = 0x00A5FF; // L const COLOR_ORANGE = 0x00A5FF; // L
const PIECE_COLORS = [ const PIECE_COLORS = [
rgb(0, 255, 255), // I - Cyan COLOR_CYAN,
rgb(255, 255, 0), // O - Yellow COLOR_YELLOW,
rgb(128, 0, 128), // T - Purple COLOR_PURPLE,
rgb(0, 255, 0), // S - Green COLOR_GREEN,
rgb(255, 0, 0), // Z - Red COLOR_RED,
rgb(0, 0, 255), // J - Blue COLOR_BLUE,
rgb(255, 165, 0), // L - Orange COLOR_ORANGE,
]; ];
// 7 tetromino shapes (4 rotations each, 4x4 grid) - defined in PHP! // 7 tetromino shapes (4 rotations each, 4x4 grid) - defined in PHP!

@ -258,6 +258,27 @@ final class ConstantExpressionValidatorTest extends PHPUnit\Framework\TestCase
)); ));
} }
public function testCompilerBoundaryRoutesUnsupportedSyntaxThroughFatalDiagnostic(): void
{
$parser = (new ParserFactory())->createForVersion(PhpVersion::fromString('8.5'));
$statements = $parser->parse("<?php\nconst VALUE = loadValue();");
self::assertNotNull($statements);
$traverser = new NodeTraverser();
$traverser->addVisitor(new ConstantExpressionValidationVisitor(
'8.5',
static function (Node $node, string $message): never {
throw new \RuntimeException("fatal: {$message} at line {$node->getStartLine()}");
},
));
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage(
'fatal: Constant expression contains invalid operations at line 2',
);
$traverser->traverse($statements);
}
private function parseAttributeExpression(string $expression, string $phpVersion): Node\Expr private function parseAttributeExpression(string $expression, string $phpVersion): Node\Expr
{ {
return $this->parseAttributeArguments($expression, $phpVersion)[0]->value; return $this->parseAttributeArguments($expression, $phpVersion)[0]->value;

@ -40,7 +40,7 @@ use PhpParser\NodeVisitor\NameResolver;
class Preprocessor extends CompilerBase class Preprocessor extends CompilerBase
{ {
protected function getSortedFiles(array $list): array public function getSortedFiles(array $list): array
{ {
$sorter = new StringSort(); $sorter = new StringSort();
$fileDeps = []; $fileDeps = [];
@ -149,7 +149,10 @@ class Preprocessor extends CompilerBase
fn (Node $node, string $message) => $this->warning($node, $message), fn (Node $node, string $message) => $this->warning($node, $message),
$this->file, $this->file,
)); ));
$traverser->addVisitor(new ConstantExpressionValidationVisitor($this->phpVersion)); $traverser->addVisitor(new ConstantExpressionValidationVisitor(
$this->phpVersion,
fn (Node $node, string $message) => $this->fatalError($node, $message),
));
$traverser->addVisitor(new RuntimeAttributeFactoryLowering($this->file)); $traverser->addVisitor(new RuntimeAttributeFactoryLowering($this->file));
$stmts = $traverser->traverse($ast); $stmts = $traverser->traverse($ast);

@ -8,8 +8,10 @@
namespace TypePhp\Transform; namespace TypePhp\Transform;
use Closure;
use PhpParser\Node; use PhpParser\Node;
use PhpParser\NodeVisitorAbstract; use PhpParser\NodeVisitorAbstract;
use TypePhp\Exception\SyntaxError;
/** /**
* Applies the allow_dynamic values used by php-src at each declaration site. * Applies the allow_dynamic values used by php-src at each declaration site.
@ -26,13 +28,29 @@ final class ConstantExpressionValidationVisitor extends NodeVisitorAbstract
private readonly bool $supportsDynamicStaticInitializers; private readonly bool $supportsDynamicStaticInitializers;
public function __construct(string $phpVersion) /** @param null|Closure(Node, string): never $fatalError */
public function __construct(
string $phpVersion,
private readonly ?Closure $fatalError = null,
)
{ {
$this->validator = new ConstantExpressionValidator($phpVersion); $this->validator = new ConstantExpressionValidator($phpVersion);
$this->supportsDynamicStaticInitializers = version_compare($phpVersion, '8.3', '>='); $this->supportsDynamicStaticInitializers = version_compare($phpVersion, '8.3', '>=');
} }
public function enterNode(Node $node): null public function enterNode(Node $node): null
{
try {
return $this->validateNode($node);
} catch (SyntaxError $error) {
if ($this->fatalError !== null) {
($this->fatalError)($node, $error->getMessage());
}
throw $error;
}
}
private function validateNode(Node $node): null
{ {
if ($node instanceof Node\Attribute) { if ($node instanceof Node\Attribute) {
$this->validator->validateArguments( $this->validator->validateArguments(

@ -321,7 +321,11 @@ final class ConstantExpressionValidator
private function tryEvaluate(Expr $expression): array private function tryEvaluate(Expr $expression): array
{ {
try { try {
$value = (new ConstExprEvaluator())->evaluateDirectly($expression); $value = (new ConstExprEvaluator(
static function (): never {
throw new \LogicException('Expression is not statically known');
},
))->evaluateDirectly($expression);
return [true, $value]; return [true, $value];
} catch (\Throwable) { } catch (\Throwable) {
return [false, null]; return [false, null];

@ -54,16 +54,22 @@ class Visitor extends NodeVisitorAbstract
$classReadonly = $node instanceof Stmt\Class_ && $node->isReadonly(); $classReadonly = $node instanceof Stmt\Class_ && $node->isReadonly();
foreach ($node->stmts as $stmt) { foreach ($node->stmts as $stmt) {
if ($stmt instanceof Stmt\Property) { if ($stmt instanceof Stmt\Property) {
array_push($methods, ...PropertyHookLowering::lowerProperty($stmt)); foreach (PropertyHookLowering::lowerProperty($stmt) as $method) {
array_push($methods, ...$this->guard( $methods[] = $method;
}
foreach ($this->guard(
$stmt, $stmt,
static fn () => GetterLowering::lowerProperty($stmt), static fn () => GetterLowering::lowerProperty($stmt),
'Getter', 'Getter',
)); ) as $method) {
array_push($methods, ...$this->guard( $methods[] = $method;
}
foreach ($this->guard(
$stmt, $stmt,
static fn () => PropertyMethodLowering::lowerProperty($stmt, $classReadonly), static fn () => PropertyMethodLowering::lowerProperty($stmt, $classReadonly),
)); ) as $method) {
$methods[] = $method;
}
} elseif ($stmt instanceof Stmt\ClassMethod && $stmt->name->toLowerString() === '__construct') { } elseif ($stmt instanceof Stmt\ClassMethod && $stmt->name->toLowerString() === '__construct') {
foreach ($stmt->params as $param) { foreach ($stmt->params as $param) {
$marker = PropertyHookLowering::lowerPromotedProperty($param); $marker = PropertyHookLowering::lowerPromotedProperty($param);
@ -78,15 +84,19 @@ class Visitor extends NodeVisitorAbstract
if ($getter !== null) { if ($getter !== null) {
$methods[] = $getter; $methods[] = $getter;
} }
array_push($methods, ...$this->guard( foreach ($this->guard(
$param, $param,
static fn () => PropertyMethodLowering::lowerPromotedProperty($param, $classReadonly), static fn () => PropertyMethodLowering::lowerPromotedProperty($param, $classReadonly),
)); ) as $method) {
$methods[] = $method;
}
} }
} }
} }
if ($methods !== []) { if ($methods !== []) {
array_push($node->stmts, ...$methods); foreach ($methods as $method) {
$node->stmts[] = $method;
}
} }
$this->guard($node, static fn () => ConstructorLowering::lowerClassLike($node), 'Constructor'); $this->guard($node, static fn () => ConstructorLowering::lowerClassLike($node), 'Constructor');
if ($node instanceof Stmt\Class_) { if ($node instanceof Stmt\Class_) {

@ -2313,7 +2313,10 @@ CODE;
$traverser = new NodeTraverser(); $traverser = new NodeTraverser();
$traverser->addVisitor(new NameResolver(null, ['replaceNodes' => false])); $traverser->addVisitor(new NameResolver(null, ['replaceNodes' => false]));
$traverser->addVisitor(new Visitor(sourceFile: $this->file)); $traverser->addVisitor(new Visitor(sourceFile: $this->file));
$traverser->addVisitor(new ConstantExpressionValidationVisitor($this->phpVersion)); $traverser->addVisitor(new ConstantExpressionValidationVisitor(
$this->phpVersion,
fn (Node $node, string $message) => $this->fatalError($node, $message),
));
$traverser->addVisitor(new RuntimeAttributeFactoryLowering($this->file)); $traverser->addVisitor(new RuntimeAttributeFactoryLowering($this->file));
$stmts = $traverser->traverse($ast); $stmts = $traverser->traverse($ast);

@ -5,6 +5,7 @@ array_push retains its required array argument when an unpacked list is empty
function main(): void function main(): void
{ {
// Keep the required by-reference argument before an empty unpack.
$values = []; $values = [];
array_push($values, ...[]); array_push($values, ...[]);
var_dump($values); var_dump($values);

Loading…
Cancel
Save