Skip to content

Commit 38d1199

Browse files
authored
Merge pull request #309 from illegalstudio/feature/universal-runtime
feature/universal runtime
2 parents 97bb2a8 + 2bc0404 commit 38d1199

145 files changed

Lines changed: 22488 additions & 1753 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ROADMAP.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ gaps that must be fixed first.
507507
- [x] Phase 5 — iterator decorators (`ArrayIterator`, `ArrayObject`, `IteratorIterator`, `LimitIterator`, `NoRewindIterator`, `InfiniteIterator`, `EmptyIterator`, `AppendIterator`, `MultipleIterator`, `CallbackFilterIterator`, `FilterIterator`, `CachingIterator`, `RecursiveArrayIterator`, `RecursiveCallbackFilterIterator`, `RecursiveFilterIterator`, `RecursiveIteratorIterator`, `ParentIterator`); functions `iterator_to_array`, `iterator_count`, `iterator_apply`, `class_implements`, `class_parents`, `class_uses`
508508
- [x] Runtime callable dispatch expansion — generated descriptor cases for dynamic string builtin callbacks and public `Class::method` strings, plus `call_user_func()` / `call_user_func_array()` support for invokable objects and callable arrays stored directly or in local variables
509509
- [x] Runtime callable descriptor ABI/storage foundation — closure, first-class callable, SPL callback-adapter, object-property, array, local, and Fiber storage now carries descriptor pointers; indirect call sites and callback runtimes load the entry ABI slot before invocation
510-
- [ ] Universal runtime callable descriptors — complete runtime descriptor metadata for signature/default/by-ref/variadic handling, receiver/capture environments, and invocation support for string, array, closure, first-class callable, object `__invoke`, static/instance method, builtin, and extern callable shapes
510+
- [x] Universal runtime callable descriptors — complete runtime descriptor metadata for signature/default/by-ref/variadic handling, receiver/capture environments, and invocation support for string, array, closure, first-class callable, object `__invoke`, static/instance method, builtin, and extern callable shapes
511511
- [x] Phase 5 follow-up — iterator-dependent Phase 4 parity: `SplFixedArray::getIterator()` plus `IteratorAggregate`/`InternalIterator` runtime wiring once iterator classes are available
512512
- [ ] Phase 6 — `SplHeap`, `SplMaxHeap`, `SplMinHeap`, `SplPriorityQueue`, `SplObjectStorage`, and per-instance handle finalization
513513
- [ ] Phase 7 — `RegexIterator`, `RecursiveRegexIterator`
@@ -595,6 +595,7 @@ and runtime foundation.
595595
- [ ] Symbol visibility control
596596
- [ ] Auto-generated C header file
597597
- [ ] Null-terminated string convention for C interop
598+
- [x] Stateful FFI callback trampolines — generate C-ABI-compatible trampoline symbols for descriptor-backed callables passed to extern `callable` parameters, retaining descriptor/capture/receiver environments for supported scalar/ptr signatures and documenting constraints for C APIs without userdata/context slots
598599
- [ ] `pkg-config` generation
599600
- [ ] FFI documentation for C, Rust, Python, Go
600601

docs/beyond-php/extern.md

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,23 @@ Argument registers follow the selected target's C ABI: AArch64 uses
4141
x86_64 uses the System V registers (`rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
4242
for integers/pointers and `xmm0`-`xmm7` for doubles).
4343

44-
`callable` in an `extern function` signature is a special FFI-only path: the
45-
call site must provide a string-literal elephc function name, and codegen passes
46-
that function's native symbol address to C. General PHP callable values use
47-
runtime descriptor pointers internally and are not passed directly to C as
48-
closures or first-class callable descriptors.
44+
`callable` in an `extern function` signature is a special FFI-only path. The
45+
call site can provide either a string-literal elephc function name (resolved
46+
case-insensitively like PHP function names) or a callable descriptor. Descriptor
47+
callbacks are bound into generated C-ABI trampoline symbols, including values
48+
with closure captures, object receivers, late-static-binding context, or
49+
branch-selected descriptor state. The trampoline receives the C callback arguments, reloads the retained
50+
descriptor from global storage, invokes the descriptor's runtime invoker, and
51+
casts the boxed result back to the C-compatible callback return type.
52+
53+
FFI callback descriptors are limited to fixed scalar/pointer signatures:
54+
`int`, `float`, `bool`, `ptr`, and `void` return values, with parameters drawn
55+
from `int`, `float`, `bool`, and `ptr`. `string`, arrays, objects, variadics,
56+
defaults, and by-reference callback parameters are rejected because ownership
57+
and temporary lifetime across a C callback boundary are not modeled safely yet.
58+
For C APIs without a userdata/context parameter, each generated trampoline owns
59+
one mutable descriptor slot for that callsite; registering a new descriptor at
60+
the same callsite replaces the previous slot owner.
4961

5062
## String conversion
5163
- **Calling C**: elephc creates temporary null-terminated copy, frees after call
@@ -65,6 +77,11 @@ Argument expressions are evaluated in PHP source order, then elephc loads the
6577
resulting values into the target C ABI registers. This matters when positional,
6678
named, or spread arguments have side effects.
6779

80+
Declared extern functions can also be selected by dynamic PHP string callbacks
81+
such as `call_user_func($cb, ...)` and `call_user_func_array($cb, $args)`.
82+
The runtime descriptor uses an extern invocation shape and a generated PHP-ABI
83+
wrapper; the wrapper performs the C ABI call after PHP argument normalization.
84+
6885
## Callbacks
6986
```php
7087
<?php
@@ -76,8 +93,36 @@ function on_signal($sig) {
7693

7794
signal(15, "on_signal");
7895
```
96+
97+
Descriptor-backed callbacks can carry state:
98+
99+
```php
100+
<?php
101+
extern function signal(int $sig, callable $handler): ptr;
102+
103+
$delta = 3;
104+
$handler = function (int $sig) use ($delta): void {
105+
echo $sig + $delta;
106+
};
107+
108+
signal(15, $handler);
109+
```
79110
Callbacks must use C-compatible types only. No strings, arrays, variadic, defaults, or pass-by-reference.
80111

112+
Descriptor-backed callbacks are accepted when no environment is needed:
113+
114+
```php
115+
<?php
116+
extern function signal(int $sig, callable $handler): ptr;
117+
118+
function on_signal(int $sig): void {
119+
echo $sig;
120+
}
121+
122+
$handler = on_signal(...);
123+
signal(15, $handler);
124+
```
125+
81126
## Extern globals
82127
```php
83128
<?php

0 commit comments

Comments
 (0)