From 0efd60e57e2fbeb4da3df27df69772d4aac5391d Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sat, 5 Apr 2025 15:17:30 +0200 Subject: [PATCH 1/3] Add support for final properties --- .../lang/ast/emit/FinalProperties.class.php | 25 +++++++++++++++++++ .../lang/ast/emit/RewriteProperties.class.php | 8 +++++- .../emit/ArgumentPromotionTest.class.php | 10 ++++++++ .../ast/unittest/emit/MembersTest.class.php | 9 +++++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100755 src/main/php/lang/ast/emit/FinalProperties.class.php diff --git a/src/main/php/lang/ast/emit/FinalProperties.class.php b/src/main/php/lang/ast/emit/FinalProperties.class.php new file mode 100755 index 00000000..fc2349af --- /dev/null +++ b/src/main/php/lang/ast/emit/FinalProperties.class.php @@ -0,0 +1,25 @@ + MODIFIER_PUBLIC, + 'protected' => MODIFIER_PROTECTED, + 'private' => MODIFIER_PRIVATE, + 'static' => MODIFIER_STATIC, + 'final' => MODIFIER_FINAL, + 'abstract' => MODIFIER_ABSTRACT, + 'readonly' => 0x0080, // XP 10.13: MODIFIER_READONLY + ]; + + $modifiers= 0; + foreach ($property->modifiers as $name) { + $modifiers|= $lookup[$name]; + } + + $property->modifiers= array_diff($property->modifiers, ['final']); + parent::emitProperty($result, $property); + $result->codegen->scope[0]->meta[self::PROPERTY][$property->name][DETAIL_ARGUMENTS]= [$modifiers]; + } +} \ No newline at end of file diff --git a/src/main/php/lang/ast/emit/RewriteProperties.class.php b/src/main/php/lang/ast/emit/RewriteProperties.class.php index 3073aad9..72102e9b 100755 --- a/src/main/php/lang/ast/emit/RewriteProperties.class.php +++ b/src/main/php/lang/ast/emit/RewriteProperties.class.php @@ -3,8 +3,9 @@ use ReflectionProperty; trait RewriteProperties { - use PropertyHooks, ReadonlyProperties, AsymmetricVisibility { + use PropertyHooks, FinalProperties, ReadonlyProperties, AsymmetricVisibility { PropertyHooks::emitProperty as emitPropertyHooks; + FinalProperties::emitProperty as emitFinalProperties; ReadonlyProperties::emitProperty as emitReadonlyProperties; AsymmetricVisibility::emitProperty as emitAsymmetricVisibility; } @@ -17,6 +18,11 @@ protected function emitProperty($result, $property) { array_intersect($property->modifiers, ['private(set)', 'protected(set)', 'public(set)']) ) { return $this->emitAsymmetricVisibility($result, $property); + } else if ( + $this->targetVersion < 80400 && + in_array('final', $property->modifiers) + ) { + return $this->emitFinalProperties($result, $property); } else if ( $this->targetVersion < 80100 && in_array('readonly', $property->modifiers) diff --git a/src/test/php/lang/ast/unittest/emit/ArgumentPromotionTest.class.php b/src/test/php/lang/ast/unittest/emit/ArgumentPromotionTest.class.php index 26481133..6ea0214f 100755 --- a/src/test/php/lang/ast/unittest/emit/ArgumentPromotionTest.class.php +++ b/src/test/php/lang/ast/unittest/emit/ArgumentPromotionTest.class.php @@ -10,6 +10,7 @@ * @see https://github.com/xp-framework/rfc/issues/240 * @see https://docs.hhvm.com/hack/other-features/constructor-parameter-promotion * @see https://wiki.php.net/rfc/constructor_promotion (PHP 8.0) + * @see https://wiki.php.net/rfc/final_prompotion * @see https://wiki.php.net/rfc/automatic_property_initialization (Declined) */ class ArgumentPromotionTest extends EmittingTest { @@ -142,4 +143,13 @@ public function __construct(private array $list) { } }'); Assert::equals('Test', $t->newInstance(['Test'])->first); } + + #[Test] + public function promoted_final() { + $t= $this->declare('class %T { + public function __construct(public final string $name) { } + }'); + + Assert::equals(MODIFIER_PUBLIC | MODIFIER_FINAL, $t->property('name')->modifiers()->bits()); + } } \ No newline at end of file diff --git a/src/test/php/lang/ast/unittest/emit/MembersTest.class.php b/src/test/php/lang/ast/unittest/emit/MembersTest.class.php index 348aa566..2465cb97 100755 --- a/src/test/php/lang/ast/unittest/emit/MembersTest.class.php +++ b/src/test/php/lang/ast/unittest/emit/MembersTest.class.php @@ -425,4 +425,13 @@ public function run() { Assert::equals('Test', $r); } + + #[Test] + public function final_property() { + $t= $this->declare('class %T { + public final string $fixture= "Test"; + }'); + + Assert::equals(MODIFIER_PUBLIC | MODIFIER_FINAL, $t->property('fixture')->modifiers()->bits()); + } } \ No newline at end of file From 82299e29de670f1a4ee69ce005d95765ccd9ca41 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sat, 5 Apr 2025 15:20:48 +0200 Subject: [PATCH 2/3] Optimize xp::$meta --- src/main/php/lang/ast/emit/FinalProperties.class.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/php/lang/ast/emit/FinalProperties.class.php b/src/main/php/lang/ast/emit/FinalProperties.class.php index fc2349af..ccbb3532 100755 --- a/src/main/php/lang/ast/emit/FinalProperties.class.php +++ b/src/main/php/lang/ast/emit/FinalProperties.class.php @@ -18,8 +18,9 @@ protected function emitProperty($result, $property) { $modifiers|= $lookup[$name]; } + // Emit without final modifier, store `final` in xp::$meta $property->modifiers= array_diff($property->modifiers, ['final']); parent::emitProperty($result, $property); - $result->codegen->scope[0]->meta[self::PROPERTY][$property->name][DETAIL_ARGUMENTS]= [$modifiers]; + $result->codegen->scope[0]->meta[self::PROPERTY][$property->name][DETAIL_ARGUMENTS]= [MODIFIER_FINAL]; } } \ No newline at end of file From aa8b4e7f6014b8f387a680a0397dd86223961c21 Mon Sep 17 00:00:00 2001 From: Timm Friebe Date: Sat, 5 Apr 2025 15:32:37 +0200 Subject: [PATCH 3/3] Ensure syntactic support for final properties --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index b06dd5cf..f98e301e 100755 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "require" : { "xp-framework/core": "^12.0 | ^11.6 | ^10.16", "xp-framework/reflection": "^3.2 | ^2.15", - "xp-framework/ast": "^11.3", + "xp-framework/ast": "^11.5", "php" : ">=7.4.0" }, "require-dev" : {