- Replace ArgList with VarList for fixed argument arrays to avoid dynamic Args allocation
- Add call argument limit assertion to prevent unbounded call generation
- Implement direct static method calls using callStaticMethod when possible
- Add alternating dynamic static call benchmarks for class and method variations
- Update documentation on request-local call slot destruction requirements
- Fix dynamic static call target evaluation order to precede argument evaluation
- Add tests for call argument limits and dynamic class evaluation ordering
TypePHP assigns one request-local slot to each unresolved function or object-method call site. String callables cache their resolved `zend_function`; method calls additionally guard the receiver class. Non-string callables, transient magic trampolines, and relative `self::` / `parent::` / `static::` strings continue through Zend's full resolver.
#### `typephp_call_method_scoped_cached()`
#### `typephp_call_method_scoped_cached()`
Used for dynamic object method calls. It uses `CallableScope::resolve()` to obtain a `zend_fcall_info_cache`, retains cacheable results in a request-local call-site slot, then executes `zend_call_function()`.
Used for dynamic object method calls. It uses `CallableScope::resolve()` to obtain a `zend_fcall_info_cache`, retains cacheable results in a request-local call-site slot, then executes `zend_call_function()`.
@ -339,13 +343,17 @@ save EG(fake_scope)
7. When adding a PHP built-in function that synchronously invokes callbacks, update the callback argument description table, noting the position, argument name, and whether it is a callback map.
7. When adding a PHP built-in function that synchronously invokes callbacks, update the callback argument description table, noting the position, argument name, and whether it is a callback map.
8. Functions that save a callback but do not invoke it immediately must not mark the scope fallback merely because they receive a callable, for example `spl_autoload_register()`.
8. Functions that save a callback but do not invoke it immediately must not mark the scope fallback merely because they receive a callable, for example `spl_autoload_register()`.
9. When adding a `FakeScopeGuard` usage that crosses a Zend bailout, code review must check whether `zend_catch` explicitly restores it.
9. When adding a `FakeScopeGuard` usage that crosses a Zend bailout, code review must check whether `zend_catch` explicitly restores it.
10. Request-local call slots must be destroyed before project request symbols are cleared. Do not retain Closure objects, receiver objects, or trampolines in those slots.
## 8. Performance Model
## 8. Performance Model
| Path | Main Cost | Optimization Strategy |
| Path | Main Cost | Optimization Strategy |
| --- | --- | --- |
| --- | --- | --- |
| `CallableScope` | Initializing one synthetic frame | At most once per AOT method, reused across loops |
| `CallableScope` | Initializing one synthetic frame | At most once per AOT method, reused across loops |
| `typephp_call_cached()` | Resolving a dynamic string callable | One request-local slot per call site; non-string and relative callables remain dynamic |
| `typephp_call_method_cached()` | Resolving a method name against a runtime object | Cache only a guarded monomorphic target; disable the slot after its class or name changes |
| `typephp_call_method_scoped_cached()` | Guarded cache lookup; `zend_is_callable_at_frame()` on a miss | One request-local slot per dynamic call site; resolvable Native Calls do not enter this path |
| `typephp_call_method_scoped_cached()` | Guarded cache lookup; `zend_is_callable_at_frame()` on a miss | One request-local slot per dynamic call site; resolvable Native Calls do not enter this path |
| Stable static property | Resolving class/property metadata and its Zend slot | Lazily cache only the final `zval*` in the generated C++ function; rebuild a lightweight `Variant` view on every access |
| `prepareScopedCallback()` | One callable resolution | Public absolute callbacks do not create a Closure |
| `prepareScopedCallback()` | One callable resolution | Public absolute callbacks do not create a Closure |
| `makeScopedCallable()` | Callable resolution and Closure allocation | Used only for first-class callables |
| `makeScopedCallable()` | Callable resolution and Closure allocation | Used only for first-class callables |
| `UserCodeScopeGuard` | One pointer lookup and write at method entry, plus restoration at exit | Generated only for `call_user_func*`, callback maps, or unresolved unpack callbacks |
| `UserCodeScopeGuard` | One pointer lookup and write at method entry, plus restoration at exit | Generated only for `call_user_func*`, callback maps, or unresolved unpack callbacks |