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

use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\JitLongArg;
use PHPCompiler\JIT\JitStringArg;
use PHPCompiler\JIT\JitStringBuiltinArg;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPLLVM\Value;

Expand All @@ -15,7 +15,7 @@ final class JitJsonValidate
{
public static function invoke(Context $context, JITVariable $json, JITVariable $depth): Value
{
$jsonPtr = JitStringArg::lower($context, $json, 'json_validate() argument #1');
$jsonPtr = JitStringBuiltinArg::lower($context, $json, 'json_validate', 0, 'json');
$depthVal = JitLongArg::lower($context, $depth, 'json_validate() argument #2');

return self::invokeWithDepth($context, $jsonPtr, $depthVal);
Expand Down
15 changes: 9 additions & 6 deletions ext/standard/json_validate.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\JitStringArg;
use PHPCompiler\JIT\JitStringBuiltinArg;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\Variable;
use PHPLLVM\Value;
Expand All @@ -34,10 +35,12 @@ public function execute(Frame $frame): void
if (null === $frame->returnVar) {
return;
}
$jsonVar = $frame->calledArgs[0]->resolveIndirect();
if (Variable::TYPE_STRING !== $jsonVar->type) {
throw new \LogicException('json_validate() argument #1 must be a string in this compiler build');
}
$json = VmString::coerceStringBuiltinArg(
$frame->calledArgs[0],
'json_validate',
0,
'json'
);
$depth = 512;
if ($argc > 1) {
$depthVar = $frame->calledArgs[1]->resolveIndirect();
Expand All @@ -58,7 +61,7 @@ public function execute(Frame $frame): void
if ($argc > 3) {
throw new \LogicException('json_validate() accepts at most three arguments');
}
$frame->returnVar->bool(VmJsonValidate::validate($jsonVar->toString(), $depth));
$frame->returnVar->bool(VmJsonValidate::validate($json, $depth));
}

public function call(Context $context, JITVariable ...$args): Value
Expand Down Expand Up @@ -95,7 +98,7 @@ public function call(Context $context, JITVariable ...$args): Value
return $context->getTypeFromString('int1')->constInt($ok ? 1 : 0, false);
}

$jsonPtr = JitStringArg::lower($context, $args[0], 'json_validate() argument #1');
$jsonPtr = JitStringBuiltinArg::lower($context, $args[0], 'json_validate', 0, 'json');
$depthConst = $context->getTypeFromString('int64')->constInt($depth, false);

return JitJsonValidate::invokeWithDepth($context, $jsonPtr, $depthConst);
Expand Down
30 changes: 16 additions & 14 deletions ext/standard/preg_quote.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\JitStringArg;
use PHPCompiler\JIT\JitStringBuiltinArg;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\Variable;
use PHPLLVM\Value;

/** preg_quote() — escape regex metacharacters (subset of PHP; native LLVM in JIT/AOT). */
Expand All @@ -21,22 +20,25 @@ public function execute(Frame $frame): void
if ($argc < 1 || $argc > 2) {
throw new \LogicException('preg_quote() requires one or two arguments in this compiler build');
}
$subject = $frame->calledArgs[0]->resolveIndirect();
if (null === $frame->returnVar) {
return;
}
if (Variable::TYPE_STRING !== $subject->type) {
throw new \LogicException('preg_quote() subject must be a string in this compiler build');
}
$subject = VmString::coerceStringBuiltinArg(
$frame->calledArgs[0],
'preg_quote',
0,
'str'
);
$delimiter = null;
if (2 === $argc) {
$delimVar = $frame->calledArgs[1]->resolveIndirect();
if (Variable::TYPE_STRING !== $delimVar->type) {
throw new \LogicException('preg_quote() delimiter must be a string in this compiler build');
}
$delimiter = $delimVar->toString();
$delimiter = VmString::coerceStringBuiltinArg(
$frame->calledArgs[1],
'preg_quote',
1,
'delimiter'
);
}
$frame->returnVar->string(VmString::pregQuote($subject->toString(), $delimiter));
$frame->returnVar->string(VmString::pregQuote($subject, $delimiter));
}

public function call(Context $context, JITVariable ...$args): Value
Expand All @@ -45,15 +47,15 @@ public function call(Context $context, JITVariable ...$args): Value
if ($argc < 1 || $argc > 2) {
throw new \LogicException('preg_quote() requires one or two arguments in this compiler build');
}
$subject = JitStringArg::lower($context, $args[0], 'preg_quote() subject');
$subject = JitStringBuiltinArg::lower($context, $args[0], 'preg_quote', 0, 'str');
if (1 === $argc) {
return JitPregQuote::quote($context, $subject, null);
}

return JitPregQuote::quote(
$context,
$subject,
JitStringArg::lower($context, $args[1], 'preg_quote() delimiter')
JitStringBuiltinArg::lower($context, $args[1], 'preg_quote', 1, 'delimiter')
);
}
}
15 changes: 5 additions & 10 deletions ext/standard/preg_split.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\JitStringArg;
use PHPCompiler\JIT\JitStringBuiltinArg;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPCompiler\VM\Variable;
use PHPLLVM\Value;
Expand All @@ -29,12 +29,7 @@ public function execute(Frame $frame): void
);
}
$pattern = VmReflection::stringArg($frame->calledArgs[0], 'preg_split() pattern', 0);
$subjectVar = $frame->calledArgs[1]->resolveIndirect();
if (Variable::TYPE_STRING !== $subjectVar->type) {
throw new \LogicException(
'preg_split() subject must be a string in this compiler build'
);
}
$subject = VmReflection::stringArg($frame->calledArgs[1], 'preg_split() subject', 1);
$limit = -1;
$flags = 0;
if ($argc >= 3) {
Expand All @@ -58,7 +53,7 @@ public function execute(Frame $frame): void
if (null === $frame->returnVar) {
return;
}
$parts = VmPreg::pregSplit($pattern, $subjectVar->toString(), $limit, $flags);
$parts = VmPreg::pregSplit($pattern, $subject, $limit, $flags);
if (false === $parts) {
$frame->returnVar->bool(false);

Expand All @@ -83,8 +78,8 @@ public function call(Context $context, JITVariable ...$args): Value

return JitPregSplit::invoke(
$context,
JitStringArg::lower($context, $args[0], 'preg_split() pattern'),
JitStringArg::lower($context, $args[1], 'preg_split() subject')
JitStringBuiltinArg::lower($context, $args[0], 'preg_split', 0, 'pattern'),
JitStringBuiltinArg::lower($context, $args[1], 'preg_split', 1, 'subject')
);
}
}
14 changes: 14 additions & 0 deletions test/compliance/cases/stdlib/json_validate_enum_typeerror.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
stdlib json_validate() — enum case json operand TypeError (#5999, ext/json/php_json.c)
--FILE--
<?php
declare(strict_types=1);
enum E: string { case A = 'x'; }
try {
json_validate(E::A);
echo "uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
--EXPECT--
json_validate(): Argument #1 ($json) must be of type string, E given
14 changes: 14 additions & 0 deletions test/compliance/cases/stdlib/preg_grep_enum_typeerror.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
stdlib preg_grep() — enum case pattern TypeError (#5999, ext/pcre/php_pcre.c)
--FILE--
<?php
declare(strict_types=1);
enum E: string { case A = 'x'; }
try {
preg_grep(E::A, []);
echo "uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
--EXPECT--
preg_grep(): Argument #1 ($pattern) must be of type string, E given
14 changes: 14 additions & 0 deletions test/compliance/cases/stdlib/preg_quote_enum_typeerror.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
stdlib preg_quote() — enum case operand TypeError (#5999, ext/pcre/php_pcre.c)
--FILE--
<?php
declare(strict_types=1);
enum E: string { case A = 'x'; }
try {
preg_quote(E::A);
echo "uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
--EXPECT--
preg_quote(): Argument #1 ($str) must be of type string, E given
14 changes: 14 additions & 0 deletions test/compliance/cases/stdlib/preg_quote_enum_typeerror_jit.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
stdlib preg_quote() JIT — enum case operand TypeError (#5999)
--FILE--
<?php
declare(strict_types=1);
enum E: string { case A = 'x'; }
try {
preg_quote(E::A);
echo "uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
--EXPECT--
preg_quote(): Argument #1 ($str) must be of type string, E given
14 changes: 14 additions & 0 deletions test/compliance/cases/stdlib/preg_split_enum_typeerror.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
stdlib preg_split() — enum case subject TypeError (#5999, ext/pcre/php_pcre.c)
--FILE--
<?php
declare(strict_types=1);
enum E: string { case A = 'x'; }
try {
preg_split('/a/', E::A);
echo "uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
--EXPECT--
preg_split(): Argument #2 ($subject) must be of type string, E given
14 changes: 14 additions & 0 deletions test/compliance/cases/stdlib/preg_split_enum_typeerror_jit.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
stdlib preg_split() JIT — enum case subject TypeError (#5999)
--FILE--
<?php
declare(strict_types=1);
enum E: string { case A = 'x'; }
try {
preg_split('/a/', E::A);
echo "uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
--EXPECT--
preg_split(): Argument #2 ($subject) must be of type string, E given
10 changes: 10 additions & 0 deletions test/fixtures/aot/compile-only/preg_quote_enum_typeerror.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
// Compile-only (#5999): preg_quote() must lower enum-case TypeError guards for AOT.
enum E: string { case A = 'x'; }
try {
preg_quote(E::A);
echo "uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
10 changes: 10 additions & 0 deletions test/fixtures/aot/compile-only/preg_split_enum_typeerror.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
// Compile-only (#5999): preg_split() must lower enum-case TypeError guards for AOT.
enum E: string { case A = 'x'; }
try {
preg_split('/a/', E::A);
echo "uncaught\n";
} catch (TypeError $e) {
echo $e->getMessage(), "\n";
}
10 changes: 10 additions & 0 deletions test/repro/maintainer_gap_json_validate_enum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
enum E: string { case A = 'x'; }
try {
json_validate(E::A);
echo "uncaught\n";
} catch (TypeError $e) {
echo 'TypeError: ', $e->getMessage(), "\n";
} catch (Throwable $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
10 changes: 10 additions & 0 deletions test/repro/maintainer_gap_preg_grep_enum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
enum E: string { case A = 'x'; }
try {
preg_grep(E::A, []);
echo "uncaught\n";
} catch (TypeError $e) {
echo 'TypeError: ', $e->getMessage(), "\n";
} catch (Throwable $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
10 changes: 10 additions & 0 deletions test/repro/maintainer_gap_preg_quote_enum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
enum E: string { case A = 'x'; }
try {
preg_quote(E::A);
echo "uncaught\n";
} catch (TypeError $e) {
echo 'TypeError: ', $e->getMessage(), "\n";
} catch (Throwable $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
10 changes: 10 additions & 0 deletions test/repro/maintainer_gap_preg_split_enum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
enum E: string { case A = 'x'; }
try {
preg_split(E::A, '');
echo "uncaught\n";
} catch (TypeError $e) {
echo 'TypeError: ', $e->getMessage(), "\n";
} catch (Throwable $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
55 changes: 55 additions & 0 deletions test/unit/PregEnumTypeerrorJitCompileTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace PHPCompiler;

use PHPUnit\Framework\TestCase;

/**
* AOT lint verify for preg_quote()/preg_split() enum-case TypeError guards (#5999).
*
* php-src: ext/pcre/php_pcre.c — php_pcre_quote, php_pcre_split
*/
final class PregEnumTypeerrorJitCompileTest extends TestCase
{
/**
* @dataProvider compileOnlyFixtureProvider
*/
public function testPregEnumCaseTypeErrorAotLint(string $relativePath): void
{
$root = dirname(__DIR__, 2);
$bin = realpath($root.'/bin/compile.php');
$this->assertNotFalse($bin);
$target = $root.'/'.$relativePath;
$cmd = [PHP_BINARY, $bin, '-l', $target];
$descriptorSpec = [
0 => ['pipe', 'r'],
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];
$proc = proc_open($cmd, $descriptorSpec, $pipes, $root);
$this->assertIsResource($proc);
fclose($pipes[0]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$exit = proc_close($proc);
$this->assertSame(
0,
$exit,
trim($stderr !== false ? $stderr : '')."\n".'compile.php -l failed for '.$relativePath
);
}

/**
* @return list<list<string>>
*/
public static function compileOnlyFixtureProvider(): array
{
return [
['test/fixtures/aot/compile-only/preg_quote_enum_typeerror.php'],
['test/fixtures/aot/compile-only/preg_split_enum_typeerror.php'],
];
}
}