Skip to content

Commit 655479b

Browse files
committed
Merge remote-tracking branch 'origin/develop' into work/compose-host
# Conflicts: # ksp-fixtures/compose/src/desktopMain/kotlin/fixture/compose/PythonComposition.kt
2 parents 9e61d11 + 329935a commit 655479b

150 files changed

Lines changed: 7429 additions & 11322 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.

‎AGENTS.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,8 @@ interoperability in both directions:
160160
| `binary/` | Source archives of per-platform CPython distributions |
161161
| `python_for_kotlin_binding.mermaid` | User-authored sketch of the object model (see rule 14) |
162162
| `docs/INTENT.md`, `docs/SPEC.md` | Intent and behavioural contract (rule 5) |
163-
| `docs/design/`, `docs/platforms/`, `docs/investigations/`, `docs/roadmap/` | Design records, platform notes, investigations, progress log |
163+
| `docs/design/`, `docs/platforms/`, `docs/investigations/`, `docs/roadmap/` | Current design records (one per topic), platform notes, investigation conclusions, the work left to do |
164+
| `docs/archive/` | Superseded designs and investigation narratives, kept for history (see its `README.md`) |
164165

165166
### Source-set hierarchy
166167

‎PROJECT.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Kotlin Multiplatform 에 CPython 을 임베딩해 **Kotlin 과 Python 이 서로
44
쓰게 한다. 모든 지원 플랫폼에서 같은 방식으로 동작하고, 경계를 넘는 비용을 측정하며 줄인다.
55

66
- 의도(경계): [`docs/INTENT.md`](docs/INTENT.md) · 스펙(동작 계약과 상태): [`docs/SPEC.md`](docs/SPEC.md)
7-
- 에이전트 규정: [`AGENTS.md`](AGENTS.md) · 진척 기록: [`docs/roadmap/ROADMAP.md`](docs/roadmap/ROADMAP.md)
7+
- 에이전트 규정: [`AGENTS.md`](AGENTS.md) · 남은 작업: [`docs/roadmap/ROADMAP.md`](docs/roadmap/ROADMAP.md)
88

99
---
1010

@@ -13,7 +13,7 @@ Kotlin Multiplatform 에 CPython 을 임베딩해 **Kotlin 과 Python 이 서로
1313
| 방향 | 이름 | 메커니즘 | 상세 |
1414
|---|---|---|---|
1515
| Kotlin → Python | **다운콜** | CPython Stable ABI 함수 호출 (`expect` 약 330개) | [`docs/design/downcall-design.md`](docs/design/downcall-design.md) |
16-
| Python → Kotlin | **업콜** | 빌드 타임 생성 함수 테이블 + 플랫폼별 진입점 1개 | [`docs/design/upcall-design.md`](docs/design/upcall-design.md) |
16+
| Python → Kotlin | **업콜** | 빌드 타임 생성 함수 테이블 + 플랫폼별 진입점 1개 | [`docs/design/upcall.md`](docs/design/upcall.md) |
1717

1818
```
1919
┌──────────────────────────────────────────┐

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ no per-platform Python → Kotlin binding code. Details: [design notes](docs/des
185185

186186
## 📊 Status
187187

188-
Honest summary — see the [roadmap](docs/roadmap/ROADMAP.md) for the full record.
188+
Honest summary — see the [roadmap](docs/roadmap/ROADMAP.md) for what is left to do.
189189

190190
| Area | State |
191191
|---|---|

‎benchmarks/cost_table.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
Why this exists
66
---------------
7-
`docs/upcall-design.md` and `docs/downcall-design.md` both carry cost tables, and both have been
7+
`docs/design/upcall.md` and `docs/design/downcall-design.md` both carry cost tables, and both have been
88
invalidated at least once by numbers that were transcribed by hand from a run whose conditions were
99
not written down next to them. The two failures were:
1010

‎benchmarks/rn-benchmark/COMPARISON.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# React Native's boundary against PythonMultiplatform's, measured the same way
22

33
Reference project: `/Volumes/macMini/thisisthepy/PythonMultiplatform`, whose figures below are
4-
quoted from `docs/upcall-design.md`, `docs/downcall-design.md` and `ROADMAP.md` at the commit
4+
quoted from `docs/design/upcall.md`, `docs/downcall-design.md` and `ROADMAP.md` at the commit
55
**`73c40da4`** (`develop`, 2026-08-14). **Nothing in the reference project was modified or re-run**;
66
its numbers are read, not reproduced.
77

@@ -13,7 +13,7 @@ its numbers are read, not reproduced.
1313
**One fourth source, added for the iOS comparison and cited separately everywhere it appears:**
1414
`docs/cost-table.md`, whose `iosSimulatorArm64` row was captured at **`958c0082b294`** at load
1515
[1.97, 2.29, 5.92], marked uncontaminated, from three runs. §4b and §1c use it rather than
16-
`upcall-design.md` because it is the only reference-side source that publishes an iOS upcall, its
16+
`docs/design/upcall.md` because it is the only reference-side source that publishes an iOS upcall, its
1717
same-run pure-Python callee control and its `withPython` scope figure together — the three cells the
1818
comparison needs from one execution. Its upcall figure (2253.20–2312.97 ns) and §4a's quoted
1919
2266–2301 ns are close but **are not the same measurement**; neither is derived from the other.
@@ -62,7 +62,7 @@ by reading generated code rather than documentation:
6262

6363
| | React Native | PythonMultiplatform |
6464
|---|---|---|
65-
| **iOS** | JSI (C++) → Objective-C message send. One crossing. The generated `RNBenchSpec.h` builds `NativeBenchSpecJSI` over `ObjCTurboModule::InitParams` and calls the ObjC method directly. | No runtime boundary at all: a `PyMethodDef` whose `ml_meth` is a `staticCFunction` in the same binary (`docs/upcall-design.md`, "cheapest of the three"). |
65+
| **iOS** | JSI (C++) → Objective-C message send. One crossing. The generated `RNBenchSpec.h` builds `NativeBenchSpecJSI` over `ObjCTurboModule::InitParams` and calls the ObjC method directly. | No runtime boundary at all: a `PyMethodDef` whose `ml_meth` is a `staticCFunction` in the same binary (`docs/design/upcall.md` §7.4). |
6666
| **Android** | JSI (C++) → **JNI** → Kotlin. Two crossings. Confirmed with `nm` on the built `libappmodules.so`: `NativeBenchSpecJSI::NativeBenchSpecJSI(JavaTurboModule::InitParams const&)`. | C entry point in `artMain/cinterop/jni_onload.def` → **JNI** → Kotlin. Two crossings, "one shim per shape, plus a JNI upcall per call". |
6767

6868
So on both projects Android is structurally the expensive side of the same asymmetry, for the same
@@ -107,7 +107,7 @@ all. Getting this wrong is the failure mode that matters most: two tables side b
107107
comparison whether or not the cells correspond.
108108

109109
Reference-side sources: `UpcallBoundaryCostTest` (`commonTest`, quoted through
110-
`docs/upcall-design.md` §"One upcall, across all five platforms") and `overhead/BenchmarkTest`
110+
`docs/design/upcall.md` §"One upcall, across all five platforms") and `overhead/BenchmarkTest`
111111
(quoted through `docs/downcall-design.md`).
112112

113113
| row here | what it charges | reference-project counterpart | same direction? | comparable? |
@@ -265,7 +265,7 @@ JS (Hermes, no JIT) --JSI--> C++ --JNI--> Kotlin, running on ART
265265

266266
The callee of every boundary row is `BenchModule.kt` executing on ART. **ART is precisely the
267267
runtime whose tier-up the reference project measured, and it produced the largest warmup requirement
268-
in its entire table** (`docs/upcall-design.md`, "Is 100 000 enough on a device?"):
268+
in its entire table** (`docs/design/upcall.md` §7.2):
269269

270270
| reference target | first repetition ÷ plateau | flat from | margin at a 100 000 warmup |
271271
|---|---|---|---|
@@ -350,7 +350,7 @@ row on iOS**, which is the opposite of what §3a predicts for Android.
350350

351351
### 4a. Ours, quoted (not re-run)
352352

353-
`UpcallBoundaryCostTest`, `docs/upcall-design.md`. **Re-quoted 2026-08-14 at `73c40da4`.** The
353+
`UpcallBoundaryCostTest`, `docs/design/upcall.md`. **Re-quoted 2026-08-14 at `73c40da4`.** The
354354
previous version of this table was taken at `bd0029d1` and is superseded: it carried four rows as
355355
*italic* 3 000-warmup readings and two ART rows with three columns reading *not recorded*. Upstream
356356
has since re-measured every device row at the 100 000 warmup and filled all five columns from one

‎benchmarks/rn-benchmark/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# rn-benchmark
22

33
A React Native app whose only purpose is to price the **JS ↔ native boundary** with the same
4-
measurement method the [PythonMultiplatform](../thisisthepy/PythonMultiplatform) repository uses to
4+
measurement method the [PythonMultiplatform](../..) repository uses to
55
price its **Python ↔ Kotlin** boundary, so that the two can be read next to each other.
66

77
The comparison itself, including what corresponds to what and what is still unmeasured, is in

‎benchmarks/rn-benchmark/RNBench/bench/appRun.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const REPS = 5;
3939
* JS (Hermes, no JIT) --JSI--> C++ --JNI--> **Kotlin, running on ART**
4040
*
4141
* The callee of every boundary row is `BenchModule.kt` executing on ART, and ART is precisely the
42-
* runtime whose tier-up the reference project measured: its `docs/upcall-design.md` sweep found ART
42+
* runtime whose tier-up the reference project measured: its `docs/design/upcall.md` sweep found ART
4343
* on `pmp_api36` still 9% above its plateau at 70 000 calls and only arriving at ~90 000-100 000 --
4444
* the largest warmup requirement of any configuration it measured, and the one that set its
4545
* constant. So the RN Android boundary rows sit on the same curve, for the same reason, on the same

‎benchmarks/rn-benchmark/RNBench/bench/harness.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@
134134

135135
/**
136136
* Where a sweep settles, in the same terms the reference project's device sweep table uses
137-
* (`docs/upcall-design.md`, "Is 100 000 enough on a device?"): first repetition over the plateau,
137+
* (`docs/design/upcall.md` §7.2): first repetition over the plateau,
138138
* the call count from which the series stays flat, and the margin a given warmup has over it.
139139
*
140140
* The plateau is the mean of the second half, so it cannot be dragged by the early readings the

‎docs/INTENT.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Behind the per-platform entry point, every platform uses the same generated func
7878
no per-platform Python → Kotlin binding code to maintain.
7979

8080
> Inferred — confirm with the maintainer: this "one mechanism everywhere" property is a goal, not
81-
> only an implementation detail. It is stated as a decision in `PROJECT.md` and `docs/design/upcall-design.md`.
81+
> only an implementation detail. It is stated as a decision in `PROJECT.md` and `docs/design/upcall.md`.
8282
8383
## 3. What it deliberately is NOT
8484

@@ -87,7 +87,7 @@ no per-platform Python → Kotlin binding code to maintain.
8787
| A binder that renames namespaces (`androidx` → `pythonx`, or any other) | §2.2 |
8888
| A replacement for the `pythonx` packages | §2.3 — they are real packages in their own repository |
8989
| Runtime-reflection or name-lookup based binding | §2.1 — impossible under Kotlin/Native and native image |
90-
| A hand-written wrapper per Kotlin function or class | Generic adaptation plus generated tables; per-declaration wrappers were tried in 2024 and rotted (`docs/design/pythonx-adapter-design.md`) |
90+
| A hand-written wrapper per Kotlin function or class | Generic adaptation plus generated tables; per-declaration wrappers were tried in 2024 and rotted (`docs/archive/pythonx-adapter-design.md`) |
9191
| A Python re-implementation, transpiler, or JVM-hosted Python | It embeds CPython itself, so C extensions keep working |
9292
| A build tool, dependency resolver or packager | That is pypackpack (the work) and toolchain (the Gradle vocabulary) |
9393
| A sub-interpreter parallelism model | Rejected: extensions that do not declare multi-interpreter support fail to import; parallelism comes from free-threaded CPython |

‎docs/SPEC.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,13 @@ All in `PM/commonTest`, so they run wherever the interpreter loads.
178178
on disk is what `import pythonx` loads. The answer is the same whichever installer
179179
(`PythonProxySource`, `PythonxAdapter`) ran first for a table. `Status: implemented` on desktop —
180180
`PM/desktopTest/.../pythonx/KotlinNamedSurfaceTest.kt`, `ksp-fixtures/compose/.../KotlinSignatureMetadataTest.kt`.
181+
- **U-9** A Pythonic package can serve extra member names on a Kotlin proxy through one hook,
182+
`python_multiplatform.binding.add_member_resolver(fn)`, `fn(kotlin_type_name, requested_name,
183+
kotlin_member_names) -> kotlin_name | None`, asked only when no Kotlin member of that name exists.
184+
The binder renames nothing itself (no resolver: `AttributeError`), and aliases are cached in the
185+
registry, not written on the proxy class (`dir()` stays Kotlin-only). Contract in `KotlinSurface.kt`'s
186+
KDoc. `Status: implemented` on desktop — `PM/desktopTest/.../pythonx/MemberResolverTest.kt`,
187+
`ksp-fixtures/compose/.../MemberResolverComposeTest.kt`.
181188

182189
## 6. Binding prebuilt libraries (Gradle plugin)
183190

0 commit comments

Comments
 (0)