Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions lib/JIT/Builtin/ReflectionPropertyIsFinalRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use PHPLLVM\Value;

/**
* JIT/AOT link for ReflectionProperty::isFinal() (#23845).
* JIT/AOT link for ReflectionProperty::isFinal() (#23845, #27315).
*
* Final flags live on Object_::finalPropertyNames (markPropertyFinal during DECLARE_PROPERTY),
* not on thin-standalone VM ClassEntry — emit a strcmp table instead of NestedJIT+VM probe.
Expand Down Expand Up @@ -45,6 +45,8 @@ public static function ensureLinked(Context $context): void
} catch (\Throwable) {
}

StringCaseCompare::ensureStrcasecmpLinked($context);

$strPtr = $context->getTypeFromString('__string__*');
$i1 = $context->getTypeFromString('int1');
$i8p = $context->getTypeFromString('int8*');
Expand All @@ -58,12 +60,16 @@ public static function ensureLinked(Context $context): void
$context->builder->positionAtEnd($entry);
$classArg = $fn->getParam(0);
$propArg = $fn->getParam(1);
$classRaw = $context->builder->pointerCast($classArg, $i8p);
$propRaw = $context->builder->pointerCast($propArg, $i8p);
$classData = $context->builder->gep($classRaw, $context->constantFromInteger(16, 'size_t'));
$propData = $context->builder->gep($propRaw, $context->constantFromInteger(16, 'size_t'));
$classCstr = $context->builder->pointerCast($classData, $i8p);
$propCstr = $context->builder->pointerCast($propData, $i8p);
$strMap = $context->structFieldMap['__string__'];
// Use structGep — hardcoded gep+16 on i8* drifts with __ref__ layout (#27315).
$classCstr = $context->builder->pointerCast(
$context->builder->structGep($classArg, $strMap['value']),
$i8p
);
$propCstr = $context->builder->pointerCast(
$context->builder->structGep($propArg, $strMap['value']),
$i8p
);

$pairs = self::collectFinalPairs($context);
$trueBlock = $fn->appendBasicBlock('refl_prop_final_yes');
Expand All @@ -72,8 +78,17 @@ public static function ensureLinked(Context $context): void
$n = \count($pairs);
foreach ($pairs as $idx => [$className, $propName]) {
$context->builder->positionAtEnd($checkBlock);
$wantClass = $context->builder->pointerCast($context->constantFromString($className), $i8p);
$wantProp = $context->builder->pointerCast($context->constantFromString($propName), $i8p);
// constantStringFromString → __string__* (same shape as runtime args), not raw [N x i8]*.
$wantClassStr = $context->builder->load($context->constantStringFromString($className));
$wantPropStr = $context->builder->load($context->constantStringFromString($propName));
$wantClass = $context->builder->pointerCast(
$context->builder->structGep($wantClassStr, $strMap['value']),
$i8p
);
$wantProp = $context->builder->pointerCast(
$context->builder->structGep($wantPropStr, $strMap['value']),
$i8p
);
$classCmp = $context->builder->call(
$context->lookupFunction('strcasecmp'),
$classCstr,
Expand Down
7 changes: 5 additions & 2 deletions lib/JIT/Builtin/Type/Object_.php
Original file line number Diff line number Diff line change
Expand Up @@ -3348,8 +3348,11 @@ private function registerExternalClass(string $lcname, string $displayName): voi
}
if ('reflectionproperty' === $lcname) {
// Zend public surface: $name then $class (#22504).
$this->defineProperty($id, \PHPCompiler\VM\ReflectionSupport::PROP_PROPERTY_NAME, Variable::TYPE_STRING);
$this->defineProperty($id, \PHPCompiler\VM\ReflectionSupport::PROP_DECLARING_CLASS_NAME, Variable::TYPE_STRING);
// TYPE_VALUE: emitSetStringPropertyFromCstr stores heap __value__* boxes (#21551 / #27315).
$this->defineProperty($id, \PHPCompiler\VM\ReflectionSupport::PROP_PROPERTY_NAME, Variable::TYPE_VALUE);
$this->defineProperty($id, \PHPCompiler\VM\ReflectionSupport::PROP_DECLARING_CLASS_NAME, Variable::TYPE_VALUE);
// Thin user-script AOT must call __construct (not allocate-only) (#27315 / #27303 / #26772).
$this->markHasConstructor($id);
}
if ('reflectionclassconstant' === $lcname) {
// Zend public surface: $name then $class (#22503).
Expand Down
6 changes: 3 additions & 3 deletions lib/JIT/Call/ReflectionPropertyIsFinal.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
use PHPLLVM\Builder;
use PHPLLVM\Value;

/** ReflectionProperty::isFinal() — JIT/AOT (#23845, ext/reflection/php_reflection.c). */
/** ReflectionProperty::isFinal() — JIT/AOT (#23845, #27315, ext/reflection/php_reflection.c). */
final class ReflectionPropertyIsFinal implements Call
{
public function call(Context $context, Variable ...$args): Value
{
$obj = ReflectionSetup::loadObjectFromArg($context, $args[0]);
// Construct stores heap __value__* via emitSetStringPropertyFromCstr; declared JIT type
// is TYPE_STRING so propertyFetch/loadValue would mis-cast (#21551 / #23845).
// Construct stores heap __value__* via emitSetStringPropertyFromCstr; slots are TYPE_VALUE
// (#27315 / #27303) so propertySlotFor + __value__readString matches the box layout.
$classStr = self::readStoredStringBox($context, $obj, ReflectionSupport::PROP_DECLARING_CLASS_NAME);
$propStr = self::readStoredStringBox($context, $obj, ReflectionSupport::PROP_PROPERTY_NAME);
$isFinal = ReflectionPropertyIsFinalRuntime::invoke($context, $classStr, $propStr);
Expand Down
16 changes: 16 additions & 0 deletions test/fixtures/aot/cases/final_plain_property_isfinal_84.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
AOT: ReflectionProperty::isFinal() for final plain property under PROFILE=8.4 (#27315)
--ENV--
PHP_COMPILER_PROFILE=8.4
--FILE--
<?php
class A {
final public string $x = 'a';
}
$a = new A;
$a->x = 'b';
echo "wrote:", $a->x, "\n";
echo 'isFinal=', (int) (new ReflectionProperty(A::class, 'x'))->isFinal(), "\n";
--EXPECT--
wrote:b
isFinal=1
26 changes: 26 additions & 0 deletions test/repro/maintainer_gap_final_plain_property_84.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
/**
* #27315 — final plain properties under PROFILE=8.4 (php-src-strict).
*
* Zend 8.4+: `final` is inheritance-only (post-construct writes succeed);
* ReflectionProperty::isFinal() is true. Thin AOT previously returned isFinal=0
* because ReflectionProperty skipped __construct / TYPE_STRING slots dropped boxes.
*
* php-src: Zend/zend_compile.c, Zend/zend_inheritance.c, ext/reflection/php_reflection.c
*
* Run:
* PHP_COMPILER_PROFILE=8.4 php bin/vm.php test/repro/maintainer_gap_final_plain_property_84.php
* PHP_COMPILER_PROFILE=8.4 php bin/jit.php test/repro/maintainer_gap_final_plain_property_84.php
* PHP_COMPILER_PROFILE=8.4 ./phpc build -o /tmp/f84 test/repro/maintainer_gap_final_plain_property_84.php && /tmp/f84
*/
class A
{
final public string $x = 'a';
}

$a = new A();
$a->x = 'b';
echo "wrote\n";

$rp = new ReflectionProperty(A::class, 'x');
echo 'isFinal=', (int) $rp->isFinal(), "\n";
Loading