Skip to content
Closed
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
2 changes: 2 additions & 0 deletions lib/JIT.php
Original file line number Diff line number Diff line change
Expand Up @@ -11009,6 +11009,8 @@ private function compileBlockInternal(
case OpCode::TYPE_PRINT:
JIT\JitNativeString::ensureInsertBlock($this->context);
$this->context->intrinsic->builder = $this->context->builder;
// Call-site ensure — thin minimal init no longer always-on ObOutput (#34695).
JIT\Builtin\ObOutputRuntime::ensureLinked($this->context);
$this->context->callSiteLine = OpCode::TYPE_ECHO === $op->type
? (int) ($op->arg2 ?? 0)
: (int) ($op->arg3 ?? 0);
Expand Down
1 change: 1 addition & 0 deletions lib/JIT/Builtin/ReflectionExtensionInfoRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ public static function emit(Context $context, Value $nameCstr, Value $nameLen):

private static function echoBakedText(Context $context, string $text): void
{
ObOutputRuntime::ensureLinked($context);
$i8p = $context->getTypeFromString('int8*');
$strMap = $context->structFieldMap['__string__'];
$infoStr = $context->builder->load($context->constantStringFromString($text));
Expand Down
2 changes: 2 additions & 0 deletions lib/JIT/Builtin/ScriptExit.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static function emit(Context $context, Variable $arg): void

private static function emitStatusOnly(Context $context, Variable $arg): void
{
ObOutputRuntime::ensureLinked($context);
switch ($arg->type) {
case Variable::TYPE_NULL:
// PHP 8.4+ string|int: null → E_DEPRECATED then status 0 (#29575).
Expand Down Expand Up @@ -115,6 +116,7 @@ private static function emitStatusOnly(Context $context, Variable $arg): void

private static function emitMessage(Context $context, Variable $arg): void
{
ObOutputRuntime::ensureLinked($context);
switch ($arg->type) {
case Variable::TYPE_NULL:
return;
Expand Down
1 change: 1 addition & 0 deletions lib/JIT/Builtin/StringFormat.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ final class StringFormat

public static function ensureLinked(Context $context): void
{
ObOutputRuntime::ensureLinked($context);
self::implement($context);
}

Expand Down
2 changes: 2 additions & 0 deletions lib/JIT/Builtin/ValueEchoRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ final class ValueEchoRuntime

public static function ensureLinked(Context $context): void
{
// ObOutput before type bridges — Context full/minimal no longer always-on (#34695).
ObOutputRuntime::ensureLinked($context);
self::implement($context);
}

Expand Down
9 changes: 7 additions & 2 deletions lib/JIT/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -2318,7 +2318,10 @@ private function ensureMinimalUserStandaloneBodies(): void
// JitReturnPending always-on removed (#34621): TryCatchHelper / emitPendingReturnResume
// already ensureLinked before lookup (peer #34612). JitHelperAbiBridge restores insert
// mid-{main}. Leftover Context NestedJIT vs Runtime ABI drift mints *.1 (#31894 / #32122).
Builtin\ObOutputRuntime::ensureLinked($this);
// ObOutputRuntime always-on removed (#34695): ValueEchoHelper / ValueEchoRuntime /
// JIT ECHO/PRINT already ensureLinked before __phpc_ob_echo_* lookup (peer #34642).
// Leftover Context NestedJIT vs Runtime ABI drift mints *.1 (#31894 / #32122).
// Thin AOT hello-world must not NestedJIT ob_* ABI during init.
// StringRandomBytes / Utf8Latin1 / RewriteVars / Define / StrContains / StatPath /
// FileGetContents / MetaTags / HashCrypto / MbNumericEntity / Readfile / Bin2hex /
// Addslashes / Stripslashes / FilePutContents / IniRuntime always-on removed (#34578):
Expand Down Expand Up @@ -2368,7 +2371,9 @@ private function ensureFullStandaloneBodies(): void
Builtin\AssertFail::ensureStandaloneBodies($this);
Builtin\AssertOptionsRuntime::ensureStandaloneBodies($this);
Builtin\JitReturnPending::ensureStandaloneBodies($this);
Builtin\ObOutputRuntime::ensureLinked($this);
// ObOutput always-on before ValueEcho removed (#34695): ValueEchoRuntime::ensureLinked
// already ObOutputRuntime::ensureLinked (peer #34642). Leftover Context NestedJIT vs
// Runtime ABI drift mints *.1 (#31894 / #32122).
Builtin\ValueEchoRuntime::ensureLinked($this);
Builtin\CliArgvRuntime::ensureStandaloneBodies($this);
// Nested-JIT string helpers: lazy via ensureLinked during spine/thin init (#14472, #20571).
Expand Down
4 changes: 4 additions & 0 deletions lib/JIT/ValueEchoHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ final class ValueEchoHelper

public static function echoLiteral(Context $context, string $literal): void
{
// Call-site ensure — Context ensureMinimal no longer always-on ObOutput (#34695 / peer #34642).
Builtin\ObOutputRuntime::ensureLinked($context);
$charPtr = $context->getTypeFromString('char*');
$context->builder->call(
$context->lookupFunction('__phpc_ob_echo_cstr'),
Expand All @@ -42,6 +44,7 @@ public static function echoNativeLong(
?Operand $sourceOperand = null
): void
{
Builtin\ObOutputRuntime::ensureLinked($context);
Builtin\StringDir::ensureLinked($context);
$tag = 'enl'.(string) ++self::$seq;
$i64 = $context->getTypeFromString('int64');
Expand Down Expand Up @@ -189,6 +192,7 @@ public static function echoObjectVariable(Context $context, Variable $objectVar,

public static function echoStringVariable(Context $context, Variable $stringVar): void
{
Builtin\ObOutputRuntime::ensureLinked($context);
$argValue = $context->helper->loadValue($stringVar);
$offset = $context->structFieldIndex($argValue, 'length');
$__str__length = $context->builder->load(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,10 @@ public function testEnsureMinimalDropsEagerDomGcProgressAssert(): void
);
}

// Essentials for thin echo / error / argv / getenv surface stay.
// Essentials for thin argv / getenv stay.
// HtmlEntities / Decode / ErrorHandler / ExceptionHandler dropped in #34612 (peer this test).
// LastError dropped in #34631 (peer this test).
// LastError dropped in #34631; ObOutput / TriggerError in #34695 / #34641.
foreach ([
'ObOutputRuntime::ensureLinked($this)',
'StringTriggerError::ensureStandaloneBodies($this)',
'CliArgvRuntime::ensureStandaloneBodies($this)',
'EnvLocalRuntime::ensureLinked($this)',
'SuperglobalNameRuntime::ensureLinked($this)',
Expand All @@ -55,6 +53,16 @@ public function testEnsureMinimalDropsEagerDomGcProgressAssert(): void
$minimalBody,
'ensureMinimal must not eagerly LastErrorRuntime (#34631)'
);
$this->assertStringNotContainsString(
'ObOutputRuntime::ensureLinked($this)',
$minimalBody,
'ensureMinimal must not eagerly ObOutputRuntime (#34695)'
);
$this->assertStringNotContainsString(
'StringTriggerError::ensureStandaloneBodies($this)',
$minimalBody,
'ensureMinimal must not eagerly StringTriggerError (#34641)'
);
foreach ([
'HtmlEntitiesJit::ensureStandaloneBodies($this)',
'StringHtmlspecialcharsDecode::ensureStandaloneBodies($this)',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,9 @@ public function testEnsureMinimalDropsEagerHtmlErrorHandler(): void
);
}

// Essentials for thin echo / error / argv / getenv surface stay.
// LastError dropped in #34631 (peer this test).
// Essentials for thin argv / getenv stay.
// LastError dropped in #34631; ObOutput / TriggerError in #34695 / #34641.
foreach ([
'ObOutputRuntime::ensureLinked($this)',
'StringTriggerError::ensureStandaloneBodies($this)',
'CliArgvRuntime::ensureStandaloneBodies($this)',
'EnvLocalRuntime::ensureLinked($this)',
'SuperglobalNameRuntime::ensureLinked($this)',
Expand All @@ -53,6 +51,16 @@ public function testEnsureMinimalDropsEagerHtmlErrorHandler(): void
$minimalBody,
'ensureMinimal must not eagerly LastErrorRuntime (#34631)'
);
$this->assertStringNotContainsString(
'ObOutputRuntime::ensureLinked($this)',
$minimalBody,
'ensureMinimal must not eagerly ObOutputRuntime (#34695)'
);
$this->assertStringNotContainsString(
'StringTriggerError::ensureStandaloneBodies($this)',
$minimalBody,
'ensureMinimal must not eagerly StringTriggerError (#34641)'
);
}

public function testCallSitesEnsureBeforeLookup(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@ public function testEnsureMinimalDropsEagerHtmlspecialchars(): void
'ensureMinimalUserStandaloneBodies must not eagerly StringHtmlspecialchars (#34642)'
);

// Essentials for thin echo / error / argv / getenv surface stay.
// Essentials for thin argv / getenv / bridges stay (#34695 ObOutput lazy).
foreach ([
'ObOutputRuntime::ensureLinked($this)',
'StringTriggerError::ensureStandaloneBodies($this)',
'CliArgvRuntime::ensureStandaloneBodies($this)',
'EnvLocalRuntime::ensureLinked($this)',
'SuperglobalNameRuntime::ensureLinked($this)',
Expand All @@ -42,6 +40,12 @@ public function testEnsureMinimalDropsEagerHtmlspecialchars(): void
] as $keep) {
$this->assertStringContainsString($keep, $minimalBody, "keep {$keep} in minimal (#34642)");
}

$this->assertStringNotContainsString(
'ObOutputRuntime::ensureLinked($this)',
$minimalBody,
'ensureMinimal must not eagerly ObOutputRuntime (#34695)'
);
}

public function testCallSiteEnsuresBeforeLookup(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ public function testEnsureMinimalDropsEagerLastError(): void
'ensureMinimalUserStandaloneBodies must not eagerly LastErrorRuntime (#34631)'
);

// Essentials for thin echo / error / argv / getenv surface stay.
// Essentials for thin argv / getenv / bridges stay (#34695 ObOutput lazy).
foreach ([
'ObOutputRuntime::ensureLinked($this)',
'CliArgvRuntime::ensureStandaloneBodies($this)',
'EnvLocalRuntime::ensureLinked($this)',
'SuperglobalNameRuntime::ensureLinked($this)',
Expand All @@ -47,6 +46,11 @@ public function testEnsureMinimalDropsEagerLastError(): void
$minimalBody,
'ensureMinimal must not eagerly StringTriggerError (#34641)'
);
$this->assertStringNotContainsString(
'ObOutputRuntime::ensureLinked($this)',
$minimalBody,
'ensureMinimal must not eagerly ObOutputRuntime (#34695)'
);

// Full standalone still links LastError after TriggerError.
$fullPos = strpos($context, 'private function ensureFullStandaloneBodies');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace PHPCompiler\Test\Unit;

use PHPUnit\Framework\TestCase;

/**
* Drop Context::ensureMinimalUserStandaloneBodies always-on ObOutputRuntime (#34695 / peer #34642).
*
* Thin AOT hello-world must not eagerly NestedJIT ob_* ABI; ValueEchoHelper /
* ValueEchoRuntime / JIT ECHO·PRINT ensureLinked lazily (#32122 .1 mint class).
*
* php-src: ext/standard/output.c — php_output_* / echo flush path
*/
final class ContextMinimalStandaloneLazyObOutputRuntimeShrinkTest extends TestCase
{
public function testEnsureMinimalDropsEagerObOutput(): void
{
$context = (string) file_get_contents(__DIR__.'/../../lib/JIT/Context.php');
$this->assertStringContainsString('#34695', $context);
$minimalPos = strpos($context, 'private function ensureMinimalUserStandaloneBodies');
$this->assertNotFalse($minimalPos);
$minimalEnd = strpos($context, 'private function ensureBootstrapAotStandaloneBodies', $minimalPos);
$this->assertNotFalse($minimalEnd);
$minimalBody = substr($context, $minimalPos, $minimalEnd - $minimalPos);

$this->assertStringNotContainsString(
'ObOutputRuntime::ensureLinked($this)',
$minimalBody,
'ensureMinimalUserStandaloneBodies must not eagerly ObOutputRuntime (#34695)'
);

// Essentials for thin argv / getenv / bridges stay.
foreach ([
'CliArgvRuntime::ensureStandaloneBodies($this)',
'EnvLocalRuntime::ensureLinked($this)',
'SuperglobalNameRuntime::ensureLinked($this)',
'ExceptionBridge::ensureStandaloneBodies($this)',
'ErrorBridge::ensureStandaloneBodies($this)',
] as $keep) {
$this->assertStringContainsString($keep, $minimalBody, "keep {$keep} in minimal (#34695)");
}

// Full standalone: ObOutput before ValueEcho dropped; ValueEchoRuntime::ensureLinked
// pulls ObOutput (peer #34642).
$fullPos = strpos($context, 'private function ensureFullStandaloneBodies');
$this->assertNotFalse($fullPos);
$fullSlice = substr($context, $fullPos, 2500);
$this->assertStringNotContainsString(
"Builtin\\ObOutputRuntime::ensureLinked(\$this);\n Builtin\\ValueEchoRuntime::ensureLinked(\$this)",
$fullSlice,
'ensureFull must not always-on ObOutput immediately before ValueEcho (#34695)'
);
$this->assertStringContainsString('ValueEchoRuntime::ensureLinked($this)', $fullSlice);
}

public function testCallSitesEnsureBeforeLookup(): void
{
$checks = [
'lib/JIT/ValueEchoHelper.php' => 'ObOutputRuntime::ensureLinked',
'lib/JIT/Builtin/ValueEchoRuntime.php' => 'ObOutputRuntime::ensureLinked',
'lib/JIT.php' => 'ObOutputRuntime::ensureLinked($this->context)',
'lib/JIT/Builtin/StringFormat.php' => 'ObOutputRuntime::ensureLinked($context)',
'lib/JIT/Builtin/ScriptExit.php' => 'ObOutputRuntime::ensureLinked($context)',
];
foreach ($checks as $rel => $needle) {
$path = __DIR__.'/../../'.$rel;
$this->assertFileExists($path, $rel);
$source = (string) file_get_contents($path);
$this->assertStringContainsString($needle, $source, $rel.' must ensure lazily (#34695)');
}
}

public function testNoNewRuntimeCForMinimalObOutputLazy(): void
{
$runtimeDir = dirname(__DIR__, 2).'/lib/AOT/runtime';
$this->assertFileDoesNotExist(
$runtimeDir.'/ob_output.c',
'must not add ob_output.c for #34695 — PHP JIT bridges only'
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@ public function testEnsureMinimalDropsEagerJitReturnPending(): void
'ensureMinimalUserStandaloneBodies must not eagerly JitReturnPending (#34621)'
);

// Essentials for thin echo / error / argv / getenv surface stay.
// LastError dropped in #34631 (peer this test).
// Essentials for thin argv / getenv / bridges stay.
// LastError dropped in #34631; ObOutput / TriggerError in #34695 / #34641.
foreach ([
'ObOutputRuntime::ensureLinked($this)',
'StringTriggerError::ensureStandaloneBodies($this)',
'CliArgvRuntime::ensureStandaloneBodies($this)',
'EnvLocalRuntime::ensureLinked($this)',
'SuperglobalNameRuntime::ensureLinked($this)',
Expand All @@ -48,6 +46,16 @@ public function testEnsureMinimalDropsEagerJitReturnPending(): void
$minimalBody,
'ensureMinimal must not eagerly LastErrorRuntime (#34631)'
);
$this->assertStringNotContainsString(
'ObOutputRuntime::ensureLinked($this)',
$minimalBody,
'ensureMinimal must not eagerly ObOutputRuntime (#34695)'
);
$this->assertStringNotContainsString(
'StringTriggerError::ensureStandaloneBodies($this)',
$minimalBody,
'ensureMinimal must not eagerly StringTriggerError (#34641)'
);

// Full standalone still links return-pending after TriggerError.
$fullPos = strpos($context, 'private function ensureFullStandaloneBodies');
Expand Down
14 changes: 11 additions & 3 deletions test/unit/ContextMinimalStandaloneLazyStdlibRuntimeShrinkTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,24 @@ public function testEnsureMinimalDropsEagerStdlibBatch(): void
);
}

// Essentials for thin echo / error / argv / getenv surface stay.
// Essentials for thin argv / getenv stay (#34695 ObOutput / #34641 TriggerError lazy).
foreach ([
'ObOutputRuntime::ensureLinked($this)',
'StringTriggerError::ensureStandaloneBodies($this)',
'CliArgvRuntime::ensureStandaloneBodies($this)',
'EnvLocalRuntime::ensureLinked($this)',
'SuperglobalNameRuntime::ensureLinked($this)',
] as $keep) {
$this->assertStringContainsString($keep, $minimalBody, "keep {$keep} in minimal (#34578)");
}
$this->assertStringNotContainsString(
'ObOutputRuntime::ensureLinked($this)',
$minimalBody,
'ensureMinimal must not eagerly ObOutputRuntime (#34695)'
);
$this->assertStringNotContainsString(
'StringTriggerError::ensureStandaloneBodies($this)',
$minimalBody,
'ensureMinimal must not eagerly StringTriggerError (#34641)'
);
}

public function testCallSitesEnsureBeforeLookup(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,8 @@ public function testEnsureMinimalDropsEagerStringTriggerError(): void
'ensureMinimalUserStandaloneBodies must not eagerly StringTriggerError (#34641)'
);

// Essentials for thin echo / argv / getenv / bridges stay.
// Essentials for thin argv / getenv / bridges stay (#34695 ObOutput lazy).
foreach ([
'StringHtmlspecialchars::ensureStandaloneBodies($this)',
'ObOutputRuntime::ensureLinked($this)',
'CliArgvRuntime::ensureStandaloneBodies($this)',
'EnvLocalRuntime::ensureLinked($this)',
'SuperglobalNameRuntime::ensureLinked($this)',
Expand All @@ -43,6 +41,12 @@ public function testEnsureMinimalDropsEagerStringTriggerError(): void
$this->assertStringContainsString($keep, $minimalBody, "keep {$keep} in minimal (#34641)");
}

$this->assertStringNotContainsString(
'ObOutputRuntime::ensureLinked($this)',
$minimalBody,
'ensureMinimal must not eagerly ObOutputRuntime (#34695)'
);

// Full standalone still links StringTriggerError before AssertFail / LastError.
$fullPos = strpos($context, 'private function ensureFullStandaloneBodies');
$this->assertNotFalse($fullPos);
Expand Down
Loading