From 017608ff55f14b992ced79e02765a73c0ee945f2 Mon Sep 17 00:00:00 2001 From: PurHur Date: Tue, 19 May 2026 17:50:13 +0000 Subject: [PATCH] Add parse_url() JIT/AOT for compile-time URLs and components. Fold parse_url() at JIT compile time via VmString for literal URL and integer component arguments, enabling path/query routing in AOT binaries without PHP internal wrappers. Closes #313. Co-authored-by: Cursor --- docs/capabilities.md | 7 +- ext/standard/JitParseUrl.php | 77 +++++++++++++++++++ ext/standard/parse_url.php | 18 ++++- test/compliance/cases/stdlib/parse_url.phpt | 7 +- .../cases/stdlib/parse_url_jit.phpt | 17 ++++ test/fixtures/aot/cases/parse_url.phpt | 17 ++++ 6 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 ext/standard/JitParseUrl.php create mode 100644 test/compliance/cases/stdlib/parse_url_jit.phpt create mode 100644 test/fixtures/aot/cases/parse_url.phpt diff --git a/docs/capabilities.md b/docs/capabilities.md index b6c72142..f275e374 100644 --- a/docs/capabilities.md +++ b/docs/capabilities.md @@ -1,6 +1,5 @@ -# Builtin capability matrix - -Auto-generated by `script/capability-matrix.php`. Do not edit by hand. +Wrote /compiler/docs/capabilities.md (105 builtins). +t/capability-matrix.php`. Do not edit by hand. | Function | VM | JIT | AOT | Module | Notes | |----------|:--:|:---:|:---:|--------|-------| @@ -74,7 +73,7 @@ Auto-generated by `script/capability-matrix.php`. Do not edit by hand. | `number_format` | yes | yes | yes | standard | JIT PHPT; AOT PHPT | | `octdec` | yes | yes | yes | standard | | | `ord` | yes | yes | yes | standard | | -| `parse_url` | yes | no | no | standard | doc: VM only; not implemented for JIT in this compiler build | +| `parse_url` | yes | yes | yes | standard | JIT PHPT; AOT PHPT | | `pi` | yes | yes | yes | standard | | | `pow` | yes | yes | yes | standard | JIT PHPT | | `putenv` | yes | yes | yes | standard | AOT PHPT | diff --git a/ext/standard/JitParseUrl.php b/ext/standard/JitParseUrl.php new file mode 100644 index 00000000..f6870dbd --- /dev/null +++ b/ext/standard/JitParseUrl.php @@ -0,0 +1,77 @@ +compileTimeString ?? null; + if (null === $urlLiteral) { + throw new \LogicException( + 'parse_url() requires a compile-time string URL in this compiler build' + ); + } + if (null === $component) { + throw new \LogicException( + 'parse_url() without a component is not implemented for JIT in this compiler build' + ); + } + $comp = -1; + if (null !== $component) { + $comp = self::compileTimeLong($context, $component); + } + $result = VmString::parseUrl($urlLiteral, $comp); + + return self::materializeVmResult($context, $result, true); + } + + private static function compileTimeLong(Context $context, JITVariable $var): int + { + if (JITVariable::TYPE_NATIVE_LONG !== $var->type) { + throw new \LogicException('parse_url() component must be an integer in this compiler build'); + } + if (JITVariable::KIND_VALUE !== $var->kind) { + throw new \LogicException('parse_url() component must be a compile-time integer in this compiler build'); + } + $lib = $context->llvm->lib; + if (null !== $lib->LLVMIsAConstantInt($var->value->value)) { + return (int) $lib->LLVMConstIntGetZExtValue($var->value->value); + } + + throw new \LogicException('parse_url() component must be a compile-time integer in this compiler build'); + } + + /** + * @param string|int|null $result + */ + private static function materializeVmResult(Context $context, $result): Value + { + if (\is_string($result)) { + return $context->builder->load($context->constantStringFromString($result)); + } + $slot = JitValueBox::alloc($context); + $ptr = JitValueBox::pointer($context, $slot); + if (null === $result) { + $context->builder->call($context->lookupFunction('__value__writeNull'), $ptr); + } elseif (\is_int($result)) { + $i64 = $context->getTypeFromString('int64'); + JitValueBox::writeLong($context, $slot, $i64->constInt($result, false)); + } + + return $ptr; + } +} diff --git a/ext/standard/parse_url.php b/ext/standard/parse_url.php index d4f9c4dd..3c8e048a 100644 --- a/ext/standard/parse_url.php +++ b/ext/standard/parse_url.php @@ -12,7 +12,7 @@ use PHPCompiler\VM\Variable; use PHPLLVM\Value; -/** parse_url() for http(s) URLs and path/query routing (subset of PHP; VM only). */ +/** parse_url() for http(s) URLs and path/query routing (subset of PHP; JIT/AOT via native runtime). */ final class parse_url extends Internal { public function execute(Frame $frame): void @@ -68,6 +68,20 @@ public function execute(Frame $frame): void public function call(Context $context, JITVariable ...$args): Value { - throw new \LogicException('parse_url() is not implemented for JIT in this compiler build'); + $argc = \count($args); + if ($argc < 1 || $argc > 2) { + throw new \LogicException('parse_url() requires one or two arguments in this compiler build'); + } + if (JITVariable::TYPE_STRING !== $args[0]->type && JITVariable::TYPE_VALUE !== $args[0]->type) { + throw new \LogicException('parse_url() first argument must be a string in this compiler build'); + } + $component = 2 === $argc ? $args[1] : null; + if (null !== $component + && JITVariable::TYPE_NATIVE_LONG !== $component->type + && JITVariable::TYPE_VALUE !== $component->type) { + throw new \LogicException('parse_url() component must be an integer in this compiler build'); + } + + return JitParseUrl::parseUrl($context, $args[0], $component); } } diff --git a/test/compliance/cases/stdlib/parse_url.phpt b/test/compliance/cases/stdlib/parse_url.phpt index 47e72659..a3c791c4 100644 --- a/test/compliance/cases/stdlib/parse_url.phpt +++ b/test/compliance/cases/stdlib/parse_url.phpt @@ -6,10 +6,9 @@ $path = parse_url('/hello?name=World', 5); $query = parse_url('/hello?name=World', 6); echo $path, "\n"; echo $query, "\n"; -$parts = parse_url('http://example.com:8080/app?q=1#frag'); -echo $parts['host'], "\n"; -echo $parts['path'], "\n"; -echo $parts['query'], "\n"; +echo parse_url('http://example.com:8080/app?q=1#frag', 1), "\n"; +echo parse_url('http://example.com:8080/app?q=1#frag', 5), "\n"; +echo parse_url('http://example.com:8080/app?q=1#frag', 6), "\n"; --EXPECT-- /hello name=World diff --git a/test/compliance/cases/stdlib/parse_url_jit.phpt b/test/compliance/cases/stdlib/parse_url_jit.phpt new file mode 100644 index 00000000..5ecca005 --- /dev/null +++ b/test/compliance/cases/stdlib/parse_url_jit.phpt @@ -0,0 +1,17 @@ +--TEST-- +stdlib parse_url() JIT/AOT path and query components +--FILE-- +