From f37388f5317b050b7a43101765e90e56da5955a6 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sun, 1 Sep 2024 15:02:01 +0200 Subject: [PATCH] Fix checks for property hooks emulation with asymmetric visibility --- ChangeLog.md | 2 ++ .../php/lang/ast/emit/PropertyHooks.class.php | 35 ++++++++++++++----- .../emit/AsymmetricVisibilityTest.class.php | 18 ++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index fde4fcff..57b04c0f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -5,6 +5,8 @@ XP Compiler ChangeLog ## 9.3.0 / 2024-08-31 +* Fixed checks for property hooks emulation with asymmetric visibility + (@thekid) * Added PHP 8.4 emitter which natively emits property hooks and asymmetric visibility syntax. This is integration-tested with PHP 8.4.0 Beta 4. See https://github.com/php/php-src/blob/php-8.4.0beta4/NEWS diff --git a/src/main/php/lang/ast/emit/PropertyHooks.class.php b/src/main/php/lang/ast/emit/PropertyHooks.class.php index 310226f8..7f76d509 100755 --- a/src/main/php/lang/ast/emit/PropertyHooks.class.php +++ b/src/main/php/lang/ast/emit/PropertyHooks.class.php @@ -72,13 +72,16 @@ protected function withScopeCheck($modifiers, $nodes) { protected function emitProperty($result, $property) { static $lookup= [ - 'public' => MODIFIER_PUBLIC, - 'protected' => MODIFIER_PROTECTED, - 'private' => MODIFIER_PRIVATE, - 'static' => MODIFIER_STATIC, - 'final' => MODIFIER_FINAL, - 'abstract' => MODIFIER_ABSTRACT, - 'readonly' => MODIFIER_READONLY, + 'public' => MODIFIER_PUBLIC, + 'protected' => MODIFIER_PROTECTED, + 'private' => MODIFIER_PRIVATE, + 'static' => MODIFIER_STATIC, + 'final' => MODIFIER_FINAL, + 'abstract' => MODIFIER_ABSTRACT, + 'readonly' => MODIFIER_READONLY, + 'public(set)' => 0x1000000, + 'protected(set)' => 0x0000800, + 'private(set)' => 0x0001000, ]; // Emit XP meta information for the reflection API @@ -88,6 +91,22 @@ protected function emitProperty($result, $property) { $modifiers|= $lookup[$name]; } + // Derive modifiers for private(set) and protected(set), folding declarations + // like `[visibility] [visibility](set)` to just the visibility itself. + if ($modifiers & 0x1000000) { + $check= null; + $modifiers&= ~0x1000000; + $write= MODIFIER_PUBLIC; + } else if ($modifiers & 0x0000800) { + $modifiers & MODIFIER_PROTECTED && $modifiers&= ~0x0000800; + $write= MODIFIER_PROTECTED; + } else if ($modifiers & 0x0001000) { + $modifiers & MODIFIER_PRIVATE && $modifiers&= ~0x0001000; + $write= MODIFIER_PRIVATE; + } else { + $write= $modifiers; + } + $scope->meta[self::PROPERTY][$property->name]= [ DETAIL_RETURNS => $property->type ? $property->type->name() : 'var', DETAIL_ANNOTATIONS => $property->annotations, @@ -137,7 +156,7 @@ protected function emitProperty($result, $property) { )], null // $hook->annotations )); - $set= $this->withScopeCheck($modifiers, [new InvokeExpression( + $set= $this->withScopeCheck($write, [new InvokeExpression( new InstanceExpression(new Variable('this'), new Literal($method)), [new Variable('value')] )]); diff --git a/src/test/php/lang/ast/unittest/emit/AsymmetricVisibilityTest.class.php b/src/test/php/lang/ast/unittest/emit/AsymmetricVisibilityTest.class.php index 1c327efe..1007425e 100755 --- a/src/test/php/lang/ast/unittest/emit/AsymmetricVisibilityTest.class.php +++ b/src/test/php/lang/ast/unittest/emit/AsymmetricVisibilityTest.class.php @@ -123,4 +123,22 @@ public function same_modifier_for_get_and_set($modifier) { $t->property('fixture')->toString() ); } + + #[Test] + public function interaction_with_hooks() { + $t= $this->declare('class %T { + public private(set) string $fixture { + get => $this->fixture; + set => strtolower($value); + } + + public function rename($name) { + $this->fixture= $name; + return $this; + } + }'); + + Assert::throws(Error::class, fn() => $t->newInstance()->fixture= 'Changed'); + Assert::equals('changed', $t->newInstance()->rename('Changed')->fixture); + } } \ No newline at end of file