You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AOT: builtin class methods are resolved from a hand-written proxy allowlist, so every unlisted method lowers to a silent null — root cause of the one-issue-per-method treadmill (lib/JIT/Context.php findInternalBuiltinInModule, lib/JIT/Call/ExternalMethod.php) #36202
Foundation: · builtin dispatch architecture · child of #36188
Problem
Since 2026-08-30 the tracker closed 81 issues titled AOT: <Class>::<method> … — leftover of <sibling> (#NNNN) (XMLReader ×25, SQLite3 ×12, SimpleXML, XMLWriter, DOM …); 1,066 of the ~2,000 commits of the last 14 days carry the AOT: prefix, most of them one-method fixes. They share one mechanical cause:
Functions are found by Context::findInternalBuiltinInModule() (lib/JIT/Context.php:1277-1289), which scans only $module->getFunctions(). Class methods are never looked up there. When resolution fails, :1114 installs new Call\ExternalMethod($proxyName) whose call() emits __value__writeNull (lib/JIT/Call/ExternalMethod.php:41-47) — the call site becomes null with no diagnostic (Self-host P0: JIT external class method calls in bundled lib (PhpParser, etc.) #579).
The compensation is a set of hand-maintained allowlists: lib/JIT/XmlReaderInstanceMethodJit.php:12-68 (const METHODS, 28 entries, each annotated // leftover of …), its mirror match in ext/xmlreader/JitXmlReaderMethod.php:16-70, SimpleXml/Xslt/XmlWriter/RandomizerInstanceMethodJit, and ~369 functionProxies[...] = lines in Context.php (SQLite3 :2452-2472, DateTime :2484-2600).
Coverage: 771 VM method registrations across 56 files vs 327 distinct class::method JIT proxies → ~73 % of builtin class methods are silent null under AOT today; 40 of 84 ext/ directories contain no Jit* file at all.
The fix is already 90 % built: lib/VM/Builtin/VmClassMethod.php:17 — abstract class VmClassMethod extends Internal — so every VM method object already implements Call, and its inherited call() (:19-24) throws LogicException('… is not implemented for JIT in this compiler build'). The VM registry is reachable from the JIT: Context::$runtime (:586) → Runtime::$vmContext (lib/Runtime.php:91) → VM\Context::$classes (lib/VM/Context.php:20) → ClassEntry::$methods (lib/VM/ClassEntry.php:52).
php-src reference
Zend/zend_API.c — zend_register_internal_class_ex / zend_register_functions: one registration feeds every executor; there is no second table for the JIT.
PHP implementation target
Context::findInternalClassMethodInModule(string $lc): split on ::, look up $this->runtime->vmContext->classes[$class]->methods[$method], return it (it is a Call). Call it from resolveRegisteredInternalBuiltin() (:1204-1212) before falling back to Call\ExternalMethod at :1114. Result: a method without a lowering fails at compile time with its name, never at runtime with null.
Move lowering onto the VM method class (public function call() overridden in e.g. ext/xmlreader/XmlReaderRead.php), then delete the allowlists (XmlReaderInstanceMethodJit::METHODS, the JitXmlReaderMethod match, the functionProxies[...] block). Adding a method must touch one production file.
ExternalMethod stays only for genuinely external (user-code cross-TU) classes and must be loud by default: drop the array_slice($names, 0, 40) cap at Context.php:1503, make emitStubReachedWarning unconditional, and default PHP_COMPILER_FAIL_ON_EXTERNAL_STUBS=1 for user-script AOT.
Pick any method not in the allowlists; today it prints NULL under AOT and the right value under Zend.
Done when
A fabricated call (new SQLite3(":memory:"))->noSuchMethod() fails the AOT build with the class::method name (not a runtime null); the same for any registered-but-unlowered method
grep -c "functionProxies\[" lib/JIT/Context.php < 40; XmlReaderInstanceMethodJit::METHODS and JitXmlReaderMethod match deleted
script/differential-sweep.sh --aot --repeat 3 unchanged; a new test/differential/cases/ case exercises ≥ 5 previously-null methods per class for XMLReader, SQLite3, SimpleXMLElement, XMLWriter, DOMDocument
CONTRIBUTING gains a rule: no new per-method allowlist entries; new methods override call() on the VM class
Category
Foundation:· builtin dispatch architecture · child of #36188Problem
Since 2026-08-30 the tracker closed 81 issues titled
AOT: <Class>::<method> … — leftover of <sibling> (#NNNN)(XMLReader ×25, SQLite3 ×12, SimpleXML, XMLWriter, DOM …); 1,066 of the ~2,000 commits of the last 14 days carry theAOT:prefix, most of them one-method fixes. They share one mechanical cause:Context::findInternalBuiltinInModule()(lib/JIT/Context.php:1277-1289), which scans only$module->getFunctions(). Class methods are never looked up there. When resolution fails,:1114installsnew Call\ExternalMethod($proxyName)whosecall()emits__value__writeNull(lib/JIT/Call/ExternalMethod.php:41-47) — the call site becomesnullwith no diagnostic (Self-host P0: JIT external class method calls in bundled lib (PhpParser, etc.) #579).lib/JIT/XmlReaderInstanceMethodJit.php:12-68(const METHODS, 28 entries, each annotated// leftover of …), its mirrormatchinext/xmlreader/JitXmlReaderMethod.php:16-70,SimpleXml/Xslt/XmlWriter/RandomizerInstanceMethodJit, and ~369functionProxies[...] =lines inContext.php(SQLite3 :2452-2472, DateTime :2484-2600).class::methodJIT proxies → ~73 % of builtin class methods are silent null under AOT today; 40 of 84ext/directories contain noJit*file at all.The fix is already 90 % built:
lib/VM/Builtin/VmClassMethod.php:17—abstract class VmClassMethod extends Internal— so every VM method object already implementsCall, and its inheritedcall()(:19-24) throwsLogicException('… is not implemented for JIT in this compiler build'). The VM registry is reachable from the JIT:Context::$runtime(:586) →Runtime::$vmContext(lib/Runtime.php:91) →VM\Context::$classes(lib/VM/Context.php:20) →ClassEntry::$methods(lib/VM/ClassEntry.php:52).php-src reference
zend_register_internal_class_ex/zend_register_functions: one registration feeds every executor; there is no second table for the JIT.PHP implementation target
Context::findInternalClassMethodInModule(string $lc): split on::, look up$this->runtime->vmContext->classes[$class]->methods[$method], return it (it is aCall). Call it fromresolveRegisteredInternalBuiltin()(:1204-1212) before falling back toCall\ExternalMethodat :1114. Result: a method without a lowering fails at compile time with its name, never at runtime with null.public function call()overridden in e.g.ext/xmlreader/XmlReaderRead.php), then delete the allowlists (XmlReaderInstanceMethodJit::METHODS, theJitXmlReaderMethodmatch, thefunctionProxies[...]block). Adding a method must touch one production file.ExternalMethodstays only for genuinely external (user-code cross-TU) classes and must be loud by default: drop thearray_slice($names, 0, 40)cap atContext.php:1503, makeemitStubReachedWarningunconditional, and defaultPHP_COMPILER_FAIL_ON_EXTERNAL_STUBS=1for user-script AOT.isPreRegisterModuleNestedJitKernel/isSpineChunkRuntimeInternalKernel,Context.php:1217-1244) that exist because$this->modulesis empty in NestedJIT/SPINE_CHUNK contexts — the direct cause of AOT: substr()/strlen() lower to silent-null stubs inside JitVmHelperLink helpers, while correct in user code #24217 (substr/strlensilent null inside helpers): helper compiles must resolve against$runtime->modules.Repro
Pick any method not in the allowlists; today it prints
NULLunder AOT and the right value under Zend.Done when
(new SQLite3(":memory:"))->noSuchMethod()fails the AOT build with the class::method name (not a runtime null); the same for any registered-but-unlowered methodgrep -c "functionProxies\[" lib/JIT/Context.php< 40;XmlReaderInstanceMethodJit::METHODSandJitXmlReaderMethodmatch deletedsubstrinside aJitVmHelperLinkhelper) passes with no whitelist entryscript/differential-sweep.sh --aot --repeat 3unchanged; a newtest/differential/cases/case exercises ≥ 5 previously-null methods per class for XMLReader, SQLite3, SimpleXMLElement, XMLWriter, DOMDocumentcall()on the VM class