Repository navigation
Kotlin Multiplatform: Compose code as a Codename One app - #6106
Conversation
A KMP module's commonMain (and its JVM source set) builds as a Codename One application: kotlinc compiles it for the JVM with the Compose compiler plugin, the unmodified Compose runtime and kotlinx.coroutines ship as they are, and maven/compose-compat provides the UI layer over Codename One components. - compose-compat: Modifier chains, Box/Row/Column/Lazy layout policies, text, material3, animation; top and navigation bars pad by the part of the safe area their host lies under, read at measure time. - cn1:import-kmp-project, the archetype compose profile, Gradle support. - ParparVM: field updaters lowered at translate time, ServiceLoader registry, the JavaAPI classes the Compose runtime and coroutines need. - Theme loading no longer leaves 24k strings behind (ThemeStrings), and Font.derive finds a cached font without building a key. - hellocodenameone moves to Kotlin 2.2.10 and carries KmpCompat screenshot tests; scripts/kmp-bench compares the same source against Compose Multiplatform; new developer guide chapter with diagrams. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Compared 12 screenshots: 12 matched. |
Native fidelity (Android, Material 3)54 pairs compared -- median 95.6%, worst 91.3% ( Distribution --
Geometry vs native (bbox offset / size ratio / center offset / corner radius) -- gated separately from the visual score
Side-by-side comparisons (worst first)
|
… reactor; guide snippets by tag Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Flutter vs Codename OneSame application, built both ways: Flutter's AOT compile of the gallery, and the identical Dart source transpiled to Java on Codename One. Lower is better for every metric, so a ratio above 1.00x means Codename One is ahead by that factor. windows9 interleaved rounds
Compute -- the VM workloads (
Gate: within tolerance against
Codename One wins 5 of 5 measured metrics across 1 platform(s). |
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
Native fidelity (ios-27-metal)68 pairs compared -- median 94.6%, worst 74.5% ( Distribution --
Geometry vs native (bbox offset / size ratio / center offset / corner radius) -- gated separately from the visual score
Side-by-side comparisons (worst first)
|
…ndroid duplicate classes, guide snippets, mac goldens Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s coroutines version Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native fidelity (iOS Modern, Metal)68 pairs compared -- median 95.0%, worst 83.5% ( Distribution --
Geometry vs native (bbox offset / size ratio / center offset / corner radius) -- gated separately from the visual score
Side-by-side comparisons (worst first)
|
# Conflicts: # .github/workflows/developer-guide-docs.yml # scripts/copyright-header-exclusions.txt
|
Developer Guide build artifacts are available for download from this workflow run:
Developer Guide quality checks: |
Cloudflare Preview
|
…names real animate functions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Compared 210 screenshots: 210 matched. Benchmark ResultsDetailed Performance Metrics
ParparVM vs HotSpot (JDK 25): Windows x64Runner CPU: AMD64 Family 26 Model 2 Stepping 1, AuthenticAMD (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 20% (time) / 25% (RAM) above its baseline in
Result: no regression |
|
Compared 210 screenshots: 210 matched. Benchmark ResultsDetailed Performance Metrics
ParparVM vs HotSpot (JDK 25): Windows arm64Runner CPU: ARMv8 (64-bit) Family 8 Model D49 Revision 0, MICROSOFT CORPORATION (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 20% (time) / 25% (RAM) above its baseline in
Result: no regression |
✅ ByteCodeTranslator Quality ReportTest & Coverage
Benchmark Results
Static Analysis
Generated automatically by the PR CI workflow. |
|
Compared 196 screenshots: 196 matched. Benchmark Results
Detailed Performance Metrics
ParparVM vs HotSpot (JDK 25): macOS arm64Runner CPU: Apple M1 (Virtual) (baseline Ratios are ParparVM / JDK 25: below 1.00x ParparVM is faster (time) or smaller (RAM). Median of 5 interleaved, paired rounds; every run's output was verified. A ratio more than 20% (time) / 25% (RAM) above its baseline in
Result: no regression |
…rd covers the Compose screens Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ows KmpCompat goldens Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ith their implementors JVMS 5.5 initializes a superinterface declaring a default method together with any class implementing it. The JS runtime only initialized the superclass chain, and a default method reads its own interface's statics without an init guard, so Compose's MonotonicFrameClock.getKey() (= Key) answered null; CombinedContext.minusKey(null) then failed Kotlin's parameter check, whose message builder indexed an empty stack trace (AIOOBE 0 in every KmpCompat screenshot test). The translator now flags such interfaces (D: 1) and ensureClassInitialized and its suspending twin initialize them after the base class. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Compared 210 screenshots: 210 matched. Benchmark ResultsDetailed Performance Metrics
|
…routines' stdlib; JS KmpCompat goldens; guide terms Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Compared 231 screenshots: 231 matched. |
…lib; arm64 Linux suite gets swap; Android KmpCompat goldens Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # docs/developer-guide/languagetool-accept.txt
|
Compared 195 screenshots: 195 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Modifier.clip clipped to the bounding rectangle, so a card's accent stripe kept square corners. It now clips to the rounded outline, cut to the clip already in force. That cut threw: BezierCurve.segment read the signed crossing count -1 of a curve running right to left as an unsupported curve order. Any rounded path crossing a clip edge failed to intersect. Also normalize a bound color override in one pass into a reused buffer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… EDT, clip rounded shapes as polygons Recomposition median 42.6ms -> 3.8ms and scroll work 40ms -> 3.3ms on the Keynote benchmark: the pipeline lost two EDT cycles of latency, and the rounded clip no longer allocates curve intersections every paint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…no frames for a hidden host Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…them Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… after a parent paint The padding a safe-area container sets for one measurement announced a style change, which queued a revalidate of the whole Form from inside its layout: a Form with a Toolbar on a notched device never stopped laying out (14-26% idle CPU on an iPhone simulator). It was left announcing because the JavaSE video peer went blank without it; the cause there was Component.repaint(x, y, w, h) dropping region repaints while repaintPending stayed set from a full repaint that a parent, not the component itself, had painted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… dropping to Java and the platform API Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nting buffers excluded from SpotBugs Suppressing the snap's PADDING event also dropped the setShouldCalcPreferredSize(true) it carried, so nested containers kept layouts made for the unsnapped padding. Do that part explicitly, still without the parent revalidateLater that caused the treadmill. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers; guide credits KMP and explains the imperative gap Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ain unit test for the Java one Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… repaints it The form's endless safe-area relayout used to do this by accident; with that gone, the video peer showed no frames. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…radle-provided libraries on the 16 KB list The side-by-side Compose and Java cards and the test now live in compiled sources the guide includes by tag, and the Java card is self-contained so the Playground runs it. kotlinx-coroutines-core, androidx.collection and androidx.annotation are declared by the Android builder for Compose apps, so the 16 KB alignment check resolves them too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…th NSContactsUsageDescription alone Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…k; a generic description when the app gives none The builder now asks ContactsPermissionScan, the scan the Android builder already uses, whether the application reaches the address book. When it does, AddressBook is linked and, if no NSContactsUsageDescription was declared, a generic one is supplied, which also turns on the define the natives compile under. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…above it and 8MB failed by 8KB Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d 30 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The constant interface map's lookup range-tested the class id and trapped on a gap on every interface call, which cost the backend benchmark about 1% of peak throughput. The table is now dense (a gap takes the lowest implementer's row) and an out-of-range id is clamped with a conditional move, so an interface call is the same two loads as before with no branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Master's #6106 suppresses the snap's padding events and flags the container for re-measure instead. Folded into the split snap: during measurement only nested containers are flagged, so the size just measured is kept and the stale-inset check can run. Measurement now uses the inset the last layout applied (or the first reading in a new safe-area context) rather than the position read mid-layout, which can be stale in either direction. The context (safe margins and surface size) is recorded so rotation, a new safe area, hiding or re-adding the bar re-reads its position. New tests cover a bar pushed off the edge, partly over it, and a display gaining an inset after layout. Goldens: Android SheetSlideUpAnimation and the iOS metal-27 animation filmstrips, where bars measured before placement are now sized with their inset (the toolbar matches its height in the static screenshots). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…snippet self-contained The import-report JSON, the stub-pack Dart and the stub-pack Java were inline blocks; they now live in docs/demos (the Java as a compiled class) and are included by tag. The UIManager javadoc snippet named an undefined resourceFile and was baselined under an old hash since #6106 changed it; it now compiles in the Playground and its baseline entry is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#6132) * Dart sources debuggable as Dart; Flutter support in the Gradle plugin The Dart transpiler marks every statement with its Dart line and stores a line table in each generated class; DartLineMapper (build-engine) rewrites the compiled classes after javac so their line numbers, source file and a JSR-45 source map point at the .dart file. A Java debugger steps through the Dart source and stack traces name main.dart:12 on every target. The transcode-flutter goal's logic moves into FlutterProjectBuilder in build-engine, which the Maven goal and a new Gradle transcodeFlutter task both run. convert-to-gradle leaves the runtime to the plugin. gradle-flutter-test.sh builds one Flutter app both ways and compares them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Flutter interop: dart:developer, RandomAccessFile, import report, 17 plugin packs, Firebase federated sign-in Library-scoped stubs (dartlib_*.dart) so dart:developer's log does not shadow dart:math's; RandomAccessFile; the importer skips web-only branches, transpiles desktop-only plugins and writes a JSON report with the transitive cause of every unsupported package. Plugin packs for timezone, app_settings, install referrer, geocoding, file_picker, audioplayers, mixpanel, web auth, image compress, image_picker platform, webview, video player/thumbnail/compress, camera, emoji picker and svg. firebase_auth exchanges Google/Apple/OAuth credentials through signInWithIdp; google_sign_in over GoogleConnect. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Flutter interop: FirebaseUI, firebase_ai, App Check, l10n/RTL, package-scoped stubs - FirebaseUI auth screens, Google/Apple providers, google_sign_in, federated firebase_auth credentials; App Check; firebase_ai over the AI Logic REST API. - Stub files can declare `// cn1:package`, so two packages exporting one name resolve per importing library, and an app's own class is never shadowed. - @Unapplied stub parameters warn (W0002); stub packs declare import coverage. - en/es/he Material and Cupertino localizations; RTL mirroring of rows, wraps, directional alignment, stacks, text, list tiles, app bars and arrow icons. - cached_network_image placeholder/progress/error widgets, polyline and circle taps, ui.Image prefix, ByteData.buffer, depth for dio, url_launcher, image_picker, messaging, notifications and maps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Flutter: per-library stub views rebuilt each time, bounded image cache, header reloads Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Flutter asset tests follow the asset code into build-engine The Gradle support moved copyAssets/removeStaleAssets from TranscodeFlutterMojo into FlutterProjectBuilder; the plugin's test still called the mojo and failed test compilation in every job that builds the plugin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Guide snippets: Flutter examples included from docs/demos, UIManager snippet self-contained The import-report JSON, the stub-pack Dart and the stub-pack Java were inline blocks; they now live in docs/demos (the Java as a compiled class) and are included by tag. The UIManager javadoc snippet named an undefined resourceFile and was baselined under an old hash since #6106 changed it; it now compiles in the Playground and its baseline entry is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Flutter guide: contractions for the Vale gate Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Flutter benchmark: rebaseline gallery code size for the new runtime surface dart:io RandomAccessFile, UiImage/ByteBuffer, the Material/Cupertino l10n tables and RTL mirroring are linked into every Flutter app, so the gallery's executable grows 3-11% across linux, windows and javascript. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Since 8f5006b the watch resolves Sheet/SheetTitleBar from the theme as tvOS does: rounded top corners and a divider, no fallback box. Master has failed this test since then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n on divide by zero (#6163) * JS port: port the C backend's emitter optimizations, throw on integer divide by zero Getter/setter inlining, StringBuilder fusion for builders proven not to escape, dotted member access in place of hoisted keys, packed Java array allocation and type-flow devirtualization in the JavaScript backend. stringBuilding goes from 99 to 68 ms (dart2js ~99), quicksort -6%, arraySequential -5%, every benchmark checksum still matching the JVM. Integer division and remainder by zero now throw java.lang.ArithmeticException on the JavaScript port; they returned 0 (int) or threw an uncatchable JS Error (long) before. Measured and not ported: scalar replacement (V8 already does it), bounds-check elimination (slower), repeated-checkcast removal (casts measured free). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * JS port: a folded getter on a null receiver throws NullPointerException The straight-line fold replaced the invoke with a bare property read, which on null threw a JS TypeError that no Java catch sees (unity-compat console sample, null string Length). The receiver goes through _nn unless it is this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * JS port: a folded setter on a null receiver throws NullPointerException too Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * vm/tests: copyright headers on the new JS flow fixtures Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * vm/tests: explicit narrowing in the short-overflow torture case (CodeQL) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: Vale contractions in the JavaScript detach section Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * iOS: define cn1ReadSafeAreaInsets for every UIKit target It lived in the iPhone-only branch while viewSafeAreaInsetsDidChange calls it on all of them, so the Metal tvOS and Mac Catalyst builds failed on master (-Wobjc-method-access as error). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: avoid 'callees' (LanguageTool) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * vm/tests: NativeImage size getters must not query the host The facade test asserted the old delegation to resolveWidth/resolveHeight, which asks the host and made layout code generators; it now asserts the opposite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * watchOS: rebaseline Sheet after the HashMap theme loading (#6106) Since 8f5006b the watch resolves Sheet/SheetTitleBar from the theme as tvOS does: rounded top corners and a divider, no fallback box. Master has failed this test since then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>




















































































































































































































































































































































































A Kotlin Multiplatform module's
commonMainbuilds as a Codename One application for every target. kotlinc compiles it for the JVM with the Compose compiler plugin. The Compose runtime andkotlinx.coroutinesship unmodified, andmaven/compose-compatreplaces only the UI layer, writing it over Codename One components.Benchmarks
The same
commonMainwas built both ways and run on the iPhone 17 Pro simulator (scripts/kmp-bench).For reference, an empty UIKit app measures 34 MB and starts in 640-750 ms on the same simulator.
What's included
compose-compat): layout policies, modifiers, text, material3 and animation.LazyColumnandLazyRowcompose only the items in view. The top app bar and navigation bar now pad by the part of the safe area their host actually lies under, so the bar no longer draws under the status bar.cn1:import-kmp-project, acomposeprofile in the archetype, and Gradle support. Gradle stops with the line to add if the Compose plugin isn't applied, andconvert-to-gradlewrites that line.ServiceLoaderresolves through a registry built at translate time, and JavaAPI gains the classes the Compose runtime and coroutines need.ThemeStrings), andFont.derivefinds a cached font without building a key. Live heap drops from 3.7 MB to 3.2 MB, and footprint by about 2 MB.KmpCompat*screenshot tests covering layout, typography, lists and state/animation. Their goldens get seeded on CI.Verified locally
LanguageTool and the screenshot suites were not run locally; they run on CI.
🤖 Generated with Claude Code