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
30 changes: 13 additions & 17 deletions ext/standard/VmMemory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace PHPCompiler\ext\standard;

use PHPCompiler\Frame;
use PHPCompiler\VM\EnumCaseSupport;
use PHPCompiler\VM\MemoryAccounting;
use PHPCompiler\VM\Variable;
Expand Down Expand Up @@ -36,14 +37,13 @@ public static function beginRequest(): void
self::$peakReal = 0;
}

public static function resolveUsageArg(Variable $var, string $fn): bool
/**
* Z_PARAM_BOOL $real_usage — MemoryUsage enum first, then frame-aware bool
* (strict_types → TypeError on null; soft → DEP+false) (#30346 / #21615).
*/
public static function resolveUsageArg(Frame $frame, int $argIndex, string $fn): bool
{
$var = $var->resolveIndirect();
if (Variable::TYPE_NULL === $var->type) {
VmNullNumberParamDeprecation::emit(null, $fn, 1, 'real_usage', 'bool');

return false;
}
$var = $frame->calledArgs[$argIndex]->resolveIndirect();
$fromEnum = self::tryMemoryUsageBool($var);
if (null !== $fromEnum) {
return $fromEnum;
Expand All @@ -55,18 +55,14 @@ public static function resolveUsageArg(Variable $var, string $fn): bool
EnumCaseSupport::typeNameForVariable($var)
));
}
if (Variable::TYPE_BOOLEAN === $var->type) {
return $var->toBool();
}
if (Variable::TYPE_INTEGER === $var->type) {
return 0 !== $var->toInt();
}

throw new \TypeError(sprintf(
'%s(): Argument #1 ($real_usage) must be of type bool, %s given',
return VmMath::parseBoolBuiltinArgForFrame(
$frame,
$argIndex,
$fn,
EnumCaseSupport::typeNameForVariable($var)
));
1,
'real_usage'
);
}

public static function tryMemoryUsageBool(Variable $var): ?bool
Expand Down
20 changes: 19 additions & 1 deletion ext/standard/memory_get_peak_usage.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\ExceptionBridge;
use PHPCompiler\JIT\JitNativeString;
use PHPCompiler\JIT\JitValueBox;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\MemoryAccounting;
use PHPLLVM\Value;
Expand Down Expand Up @@ -45,6 +48,20 @@ public function call(Context $context, JITVariable ...$args): Value
'memory_get_peak_usage() expects at most 1 argument, '.\count($args).' given'
);
}
// Compile-time null under strict: catchable TypeError then stop IR (#30346 peer).
if (isset($args[0]) && $context->callerStrictTypes && (
JITVariable::TYPE_NULL === $args[0]->type || ($args[0]->isNullConstant ?? false)
)) {
JitNativeString::ensureInsertBlock($context);
ExceptionBridge::emitTypeErrorAndAbort(
$context,
'memory_get_peak_usage(): Argument #1 ($real_usage) must be of type bool, null given'
);
JitNativeString::ensureInsertBlock($context);
$slot = JitValueBox::alloc($context);

return JitValueBox::pointer($context, $slot);
}

return JitMemory::getPeakUsage($context, $args[0] ?? null);
}
Expand All @@ -60,6 +77,7 @@ private static function resolveRealUsage(Frame $frame): bool
if (0 === $argc) {
return false;
}
return VmMemory::resolveUsageArg($frame->calledArgs[0], 'memory_get_peak_usage');
// Z_PARAM_BOOL — caller strict_types → TypeError on null (#30346 peer).
return VmMemory::resolveUsageArg($frame, 0, 'memory_get_peak_usage');
}
}
20 changes: 19 additions & 1 deletion ext/standard/memory_get_usage.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\ExceptionBridge;
use PHPCompiler\JIT\JitNativeString;
use PHPCompiler\JIT\JitValueBox;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\MemoryAccounting;
use PHPLLVM\Value;
Expand Down Expand Up @@ -38,6 +41,20 @@ public function call(Context $context, JITVariable ...$args): Value
'memory_get_usage() expects at most 1 argument, '.\count($args).' given'
);
}
// Compile-time null under strict: catchable TypeError then stop IR (#30346 / peer #30169).
if (isset($args[0]) && $context->callerStrictTypes && (
JITVariable::TYPE_NULL === $args[0]->type || ($args[0]->isNullConstant ?? false)
)) {
JitNativeString::ensureInsertBlock($context);
ExceptionBridge::emitTypeErrorAndAbort(
$context,
'memory_get_usage(): Argument #1 ($real_usage) must be of type bool, null given'
);
JitNativeString::ensureInsertBlock($context);
$slot = JitValueBox::alloc($context);

return JitValueBox::pointer($context, $slot);
}

return JitMemory::getUsage($context, $args[0] ?? null);
}
Expand All @@ -53,6 +70,7 @@ private static function resolveRealUsage(Frame $frame): bool
if (0 === $argc) {
return false;
}
return VmMemory::resolveUsageArg($frame->calledArgs[0], 'memory_get_usage');
// Z_PARAM_BOOL — caller strict_types → TypeError on null (#30346).
return VmMemory::resolveUsageArg($frame, 0, 'memory_get_usage');
}
}
5 changes: 5 additions & 0 deletions lib/JIT/JitMemoryUsageArg.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ public static function lower(Context $context, ?Variable $arg, string $fn): Valu
return $context->constantFromBool($compileTime);
}

// Z_PARAM_BOOL — honor caller strict_types; soft-null DEP+coerce (#30346).
if (Variable::TYPE_NULL === $arg->type || ($arg->isNullConstant ?? false)) {
return JitBoolArg::lowerCoerceZParamBool($context, $arg, $fn, 'real_usage', 1);
}

if (Variable::TYPE_NATIVE_BOOL === $arg->type) {
return $context->helper->loadValue($arg);
}
Expand Down
2 changes: 2 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,8 @@
<file>./test/compliance/CompactBoolWarningActual30119JITTest.php</file>
<file>./test/compliance/ObjectBoolTypeErrorActual30100VMTest.php</file>
<file>./test/compliance/ObjectBoolTypeErrorActual30100JITTest.php</file>
<file>./test/compliance/MemoryGetUsageNullStrictVMTest.php</file>
<file>./test/compliance/MemoryGetUsageNullStrictJITTest.php</file>
</testsuite>
<testsuite name="PHPCompiler Unit Test Suites">
<directory suffix=".php">./test/unit</directory>
Expand Down
26 changes: 26 additions & 0 deletions test/compliance/MemoryGetUsageNullStrictJITTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace PHPCompiler;

require_once __DIR__.'/../BaseTest.php';

/** JIT: memory_get_usage/peak_usage(null) TypeError under strict_types (#30346). */
final class MemoryGetUsageNullStrictJITTest extends BaseTest
{
protected static string $DIR = __DIR__;

public static function providePHPTests(): \Generator
{
yield 'memory_get_usage_null_strict_jit.phpt' => self::parsePHPT(
__DIR__.'/cases/stdlib/memory_get_usage_null_strict_jit.phpt',
'memory_get_usage_null_strict_jit.phpt'
);
}

public function setUp(): void
{
$this->BIN = realpath(__DIR__.'/../../bin/jit.php');
}
}
26 changes: 26 additions & 0 deletions test/compliance/MemoryGetUsageNullStrictVMTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace PHPCompiler;

require_once __DIR__.'/../BaseTest.php';

/** VM: memory_get_usage/peak_usage(null) TypeError under strict_types (#30346). */
final class MemoryGetUsageNullStrictVMTest extends BaseTest
{
protected static string $DIR = __DIR__;

public static function providePHPTests(): \Generator
{
yield 'memory_get_usage_null_strict.phpt' => self::parsePHPT(
__DIR__.'/cases/stdlib/memory_get_usage_null_strict.phpt',
'memory_get_usage_null_strict.phpt'
);
}

public function setUp(): void
{
$this->BIN = realpath(__DIR__.'/../../bin/vm.php');
}
}
25 changes: 25 additions & 0 deletions test/compliance/cases/stdlib/memory_get_usage_null_strict.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
memory_get_usage/peak_usage(null) under strict_types TypeError (#30346, ext/standard/basic_functions.c Z_PARAM_BOOL)
--FILE--
<?php
declare(strict_types=1);

foreach (['memory_get_usage', 'memory_get_peak_usage'] as $fn) {
try {
var_export($fn(null));
echo " uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
}
$n = null;
try {
var_export(memory_get_usage($n));
echo " uncaught-var\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
--EXPECT--
memory_get_usage(): Argument #1 ($real_usage) must be of type bool, null given
memory_get_peak_usage(): Argument #1 ($real_usage) must be of type bool, null given
memory_get_usage(): Argument #1 ($real_usage) must be of type bool, null given
17 changes: 17 additions & 0 deletions test/compliance/cases/stdlib/memory_get_usage_null_strict_jit.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
JIT: memory_get_usage/peak_usage(null) under strict_types TypeError (#30346)
--FILE--
<?php
declare(strict_types=1);

foreach (['memory_get_usage', 'memory_get_peak_usage'] as $fn) {
try {
var_export($fn(null));
echo " uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
}
--EXPECT--
memory_get_usage(): Argument #1 ($real_usage) must be of type bool, null given
memory_get_peak_usage(): Argument #1 ($real_usage) must be of type bool, null given
14 changes: 14 additions & 0 deletions test/repro/issue_30346_memory_get_usage_null_strict.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
try {
memory_get_usage(null);
echo "uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
try {
memory_get_peak_usage(null);
echo "uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
62 changes: 62 additions & 0 deletions test/unit/MemoryGetUsageNullStrict30346Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace PHPCompiler;

use PHPUnit\Framework\TestCase;

/**
* memory_get_usage/peak_usage(null) under strict_types — TypeError like Zend (#30346).
*/
final class MemoryGetUsageNullStrict30346Test extends TestCase
{
public function testVmTypeErrorUnderStrictTypes(): void
{
$out = $this->runPhpScript(realpath(dirname(__DIR__, 2).'/bin/vm.php'));
$this->assertSame(
"memory_get_usage(): Argument #1 (\$real_usage) must be of type bool, null given\n"
."memory_get_peak_usage(): Argument #1 (\$real_usage) must be of type bool, null given\n",
$out
);
}

public function testJitTypeErrorUnderStrictTypes(): void
{
$out = $this->runPhpScript(realpath(dirname(__DIR__, 2).'/bin/jit.php'));
$this->assertStringContainsString(
'memory_get_usage(): Argument #1 ($real_usage) must be of type bool, null given',
$out
);
$this->assertStringContainsString(
'memory_get_peak_usage(): Argument #1 ($real_usage) must be of type bool, null given',
$out
);
}

private function runPhpScript(string $bin): string
{
$root = dirname(__DIR__, 2);
$repro = realpath($root.'/test/repro/issue_30346_memory_get_usage_null_strict.php');
$this->assertNotFalse($repro);
$cmd = [PHP_BINARY, '-d', 'error_reporting=E_ALL', '-d', 'display_errors=1', $bin, $repro];
$descriptorSpec = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$env = [];
foreach (array_merge($_ENV, $_SERVER) as $key => $value) {
if (is_string($value) || is_int($value) || is_float($value)) {
$env[(string) $key] = (string) $value;
}
}
$proc = proc_open($cmd, $descriptorSpec, $pipes, $root, $env);
$this->assertIsResource($proc);
fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$exit = proc_close($proc);
$this->assertSame(0, $exit, (string) $stdout.(string) $stderr);

return (string) $stdout;
}
}
Loading