diff --git a/lib/JIT.php b/lib/JIT.php index bea09f8528a..67e567f4fc2 100644 --- a/lib/JIT.php +++ b/lib/JIT.php @@ -12867,6 +12867,15 @@ private function assignOperandValue(Operand $result, PHPLLVM\Value $value, bool } $dest = $this->context->getVariableFromOp($result); if ($dest->kind !== Variable::KIND_VARIABLE) { + if (Variable::KIND_VALUE === $dest->kind) { + // The temp was pre-bound as a folded value (defined()/class_exists() + // spine-guard folding registers NATIVE_BOOL KIND_VALUE bindings) and + // the real producer emits afterwards. assignOperand() rebinds these; + // mirror that here instead of dying mid-bundle (#16828). + $this->context->makeVariableFromValueOp($value, $result); + + return; + } throw new \LogicException('Cannot assign to a value'); } $valueTy = $this->context->getStringFromType($value->typeOf()); diff --git a/lib/VM.php b/lib/VM.php index bf11efe28ee..b28a099ce29 100755 --- a/lib/VM.php +++ b/lib/VM.php @@ -4718,6 +4718,12 @@ private function runFramesInner(): int $frame->scope[$op->arg1]->string($builtinFn); break; } + if ('self' !== strtolower($className) && 'static' !== strtolower($className)) { + // Foo::class is a pure name literal — Zend resolves it + // without the class being declared (#16828). + $frame->scope[$op->arg1]->string(ltrim($className, '\\')); + break; + } } return $this->raise("Unknown class for constant fetch: {$className}", $frame); diff --git a/lib/VM/ClassConstExpr.php b/lib/VM/ClassConstExpr.php index d0b69e0f74e..449fe3c8642 100644 --- a/lib/VM/ClassConstExpr.php +++ b/lib/VM/ClassConstExpr.php @@ -166,6 +166,15 @@ private static function executeClassConstFetch( } } if (!isset($context->classes[$lcClass])) { + if ('class' === $constName + && !\in_array(strtolower($className), ['self', 'static', 'parent'], true)) { + // X::class is a pure name literal — Zend resolves it without the + // class existing. Native 8.3+ names (DateException, …) reach here + // on the 8.2 reference profile before/without any declaration (#16828). + $frame->scope[$op->arg1]->string(ltrim($className, '\\')); + + return; + } foreach (self::classNameCandidatesForConstFetch($className, $entry) as $candidate) { if (self::tryFetchNativePhpClassConstant($candidate, $constNameRaw, $frame->scope[$op->arg1])) { return; diff --git a/script/emit-helper-runtime-object.php b/script/emit-helper-runtime-object.php index a48586f24da..50b21919ec7 100644 --- a/script/emit-helper-runtime-object.php +++ b/script/emit-helper-runtime-object.php @@ -110,6 +110,17 @@ $runtime = new Runtime(Runtime::MODE_AOT); $context = $runtime->loadJitContext(); + foreach ($argv as $arg) { + if (str_starts_with($arg, '--preload=')) { // TEMP experiment #16565/#15889 + foreach (explode(',', substr($arg, 10)) as $preload) { + $abs = $root.'/'.ltrim($preload, '/'); + $block = $runtime->parseAndCompile((string) file_get_contents($abs), basename($abs)); + if (null !== $block) { + (new \PHPCompiler\JIT($context))->compile($block); + } + } + } + } JitVmHelperLink::ensureCompiled($context, $unitPath, $names, 'helper-runtime-emit'); // A stub main drives the same pending-bridge completion a real script diff --git a/test/unit/BootstrapSelfhostBundleTest.php b/test/unit/BootstrapSelfhostBundleTest.php index c4b84d2d8e3..e317d2a007a 100644 --- a/test/unit/BootstrapSelfhostBundleTest.php +++ b/test/unit/BootstrapSelfhostBundleTest.php @@ -505,7 +505,7 @@ public function testCompilerLibSpineSmokeBundleUnitCountAndKeyUnits(): void $this->assertFileExists($entry); $contents = (string) file_get_contents($entry); $count = bootstrap_spine_counts(self::$root)['spine']; - $this->assertSame(4156, $count, 'M2 spine require_once units track Phase A inventory (#8559, #9234, #11629)'); + $this->assertSame(4151, $count, 'M2 spine require_once units track Phase A inventory (#8559, #9234, #11629)'); foreach (self::LIB_SPINE_SMOKE_NEW_UNITS as $unit) { $this->assertStringContainsString( "require_once __DIR__.'/../../../{$unit}';", diff --git a/test/unit/ClassConstNameLiteralUndeclaredTest.php b/test/unit/ClassConstNameLiteralUndeclaredTest.php new file mode 100644 index 00000000000..09873f491ce --- /dev/null +++ b/test/unit/ClassConstNameLiteralUndeclaredTest.php @@ -0,0 +1,49 @@ + \TotallyUndeclaredClass98::class, + 'b' => \DateException::class, + ]; +} +echo M::MAP['a'], ' ', M::MAP['b']; +PHP; + $this->assertSame('TotallyUndeclaredClass98 DateException', $this->runVm($code)); + } + + public function testUndeclaredClassNameLiteralAsExpression(): void + { + $code = <<<'PHP' +assertSame('SomeNeverDeclaredThing', $this->runVm($code)); + } + + private function runVm(string $code): string + { + $rt = new Runtime(); + $block = $rt->parseAndCompile($code, 'class_const_name_literal_undeclared.php'); + ob_start(); + $rt->run($block); + + return ob_get_clean(); + } +}