Skip to content

Swing, SwingX and JavaFX compatibility layers: desktop Java apps as Codename One apps - #5980

Open
shai-almog wants to merge 328 commits into
masterfrom
desktop-compat-layer
Open

shai-almog wants to merge 328 commits into
masterfrom
desktop-compat-layer

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

What this adds

An existing Swing or JavaFX project builds as a Codename One application for every target, the way an Android module does with the Android compatibility layer. The sources are imported with cn1:import-desktop-project into common/src/main/desktop and built unmodified.

  • maven/swing-compat: java.awt, Java2D, javax.swing, java.beans, javax.imageio, a SwingX subset and the FlatLaf entry points, written over Codename One components. Authored already relocated, because the JDK owns those package names; applications compile against the JDK's Swing.
  • maven/javafx-compat: the javafx.* subset (scene graph, controls, charts, layout panes, properties and bindings, animation, effects, Canvas), authored under the real names.
  • maven/fxml-compiler: FXML and JavaFX CSS compiled at build time; nothing on a device parses XML or reflects.
  • maven/compat-jdk: the JDK classes a device lacks (streams, java.util.function, java.nio.file, regex, CompletableFuture, java.time gaps and more), shared with the Android layer.
  • maven/build-engine: one composed relocation pass over every layer, generated entry points and registries, records and pattern switches desugared, and the compliance check extended so that unsupported API is a build error reported under its original name and source line.
  • Maven and Gradle plugin wiring, the archetype profile, samples (scripts/desktop-compat-samples), screenshot tests in hellocodenameone, an integration test, a real-port interaction harness (maven/integration-tests/desktop-compat-realport) and a benchmark harness (scripts/desktop-compat-benchmarks).
  • Developer guide chapter docs/developer-guide/Desktop-Interop.asciidoc, with measured size, memory and start-up tables.

Verified locally

  • Unit tests green: compat-jdk 276, swing-compat 731, javafx-compat 496, fxml-compiler 45, build-engine 2561 (3 skipped), Maven plugin 439, Gradle plugin 24.
  • SpotBugs: zero findings in the seven modules that produce a report.
  • Control characters, author and since tags, copyright headers, package-info, cast semantics, build-hint catalog, workflow validation and the markdown doc validator pass.

Not verified / known gaps

  • The new workflows (desktop-compat.yml additions, desktop-compat-benchmarks.yml) have never run on GitHub. Windows is unmeasured; macOS runtime numbers need run-macos-bench.sh.
  • Screenshot goldens for the new DesktopCompat* tests have to be seeded from the first CI run.
  • Native Linux port issues found while measuring, not fixed here yet: window size is fixed at 800x600; clips under a transform cull children of a scaled node (one JavaFX game draws only its grid); images are never freed (an image-per-frame canvas application grows without bound); typed text is not visible in text fields.
  • JavaSE port: Ctrl/Alt/Meta key combinations do not reach the application, so accelerators and mnemonics are inert.
  • The cloud builder needs a companion change before cloud builds pick the layers up.

🤖 Generated with Claude Code

shai-almog and others added 30 commits October 8, 2026 21:18
…g nested resources twice

The first end-to-end run of the Swing gallery sample. A bundled library names
API no device can serve (java.beans persistence, AWT's heavyweight widgets);
those types now exist marked @linkonly, which the compliance check accepts
from a bundled library and still reports from the application's own classes.
WeakHashMap, Externalizable and ClassLoader.loadClass get shims. The Gradle
build left the nested copy of a flattened desktop resource in the upload.
The sample uses the look and feel, ImageIO, clipboard, editable combo and
HTML editor pane the hardening slice added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…atibility chapter

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s it is

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SwingGallerySampleTest compiles the sample as its own build does, remaps it
with MigLayout bundled, requires zero compliance findings, and starts the
generated main class on the headless implementation in a class loader that
holds only the remapped classes: one frame, eight tabs, every tab and every
layout tab painted. It found that a Swing component in no window answered a
null font, where the desktop's is never null (getFont().deriveFont in a cell
renderer). A project holding an Android and a desktop application now fails
with a message that names both and the two ways out. The integration script
checks every staged jar for toolkit references and prints what the layer
adds to an upload.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntrols resolve

An FXML document was compiled in generate-sources, against the class path alone,
so an element naming a class of the application itself was an unknown class. The
documents are now compiled by the remap, as its first step: after javac, before
anything is relocated, with the application's classes beside the class path, and
by an in-process javac writing into the classes directory. The early step keeps
the style sheets and writes a marker source carrying a digest of the documents,
so an FXML-only edit still recompiles the module, and so a classes directory the
remap already relocated is recognised and left alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plain Maven JavaFX 17 project, written as a JavaFX developer writes one:
controls, layouts, bindings, an FXML screen with an included document and a
custom control, style sheets, shapes and a canvas, animation, dialogs, a task
and menus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…urces, shipped size

A first end to end build of a project holding an Android application and a
Swing one showed the conflict message added for it never fired: the Android
remap runs first, so the main class extends the RELOCATED lifecycle and the
unrelocated name alone never matched. Both names are recognized and the
integration script now builds that project: it must stop naming both
applications while the entry record exists, and stage with both runtimes
relocated once the record is gone.

The Gradle plugin filters the desktop resource directories out of the source
set rather than out of processResources, whose exclude answers a value SpotBugs
rightly asks about. Tests for the importer's library list, the pom updater and
the loadClass rewrite; the sample's French bundle is escaped, as a properties
file on a real JDK 8 has to be.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirrors android-compat.yml: the module tests, then desktop-compat-test.sh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ors, stage icons

What the JavaFX gallery sample needed and the layer lacked: TableView sorts by
its headers and by getSortOrder (comparatorProperty, for a SortedList to bind
to) and edits cells (TextFieldTableCell, the edit events of a column,
TablePosition); Node.setClip clips drawing and picking to the box of the clip
node; KeyCombination and KeyCodeCombination, MenuItem accelerators fired from a
MenuBar of the scene, Scene.getAccelerators; Stage.getIcons, recorded; and a
style sheet may say infinity for a length.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The idiomatic wrapper around FXMLLoader.load in a constructor; the device
library has no such class and the compliance check refused the application.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The remap writes a desktop resource under its flat name and removes the nested
copy from the classes directory, where Maven had put it. Gradle copies resources
to a directory of their own, so every upload carried each resource twice and
differed from Maven's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…660c

# Conflicts:
#	maven/build-engine/src/main/java/com/codename1/maven/Relocation.java
#	maven/codenameone-gradle-plugin/src/main/java/com/codename1/gradle/AppSupport.java
kind=library in cn1-desktop.properties never generates a main class; without
a record, a project whose own main class is a compiled class is a library too,
and the build says so at info level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s JavaFX keeps them

Form's constructor lays itself out when the theme installs a global toolbar,
which reached StageForm.layoutContainer before its fields existed and threw.
CellEditEvent reads its table from the position, which is transient as in
JavaFX, and a descending sort exchanges the operands instead of negating.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Built by the build's own steps (resource compiler, javac, FXML compiled
against the application's classes, relocation, compliance), then started on
the headless implementation: every tab selected and painted, the FXML
button fired through its controller, style sheet values read back. The
workflow also compiles the pristine sample against OpenJFX on JDK 17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e application classes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SwingInterop.asComponent and FxInterop.asComponent answer a Codename One component that lays out, paints and takes pointer input for a Swing tree or a scene graph. Without a lifecycle of the layer nothing ends the application: closing the last window returns to the form it was shown over. setNativeWindows(false) keeps secondary windows inside the application's own window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tal builds

The remap copies the relocated runtime into the application's own
target/classes, where it shadows the jar on the next non-clean compile. A
public class the remap does not move then names the RELOCATED Parent in its
signature while the application source still names javafx.scene.Parent, and
javac refuses the call. Declaring the parameter as Object and checking it
with instanceof compiles against either copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hellocodenameone compiles common/src/main/desktop in library mode (no entry
point generated, the Android layer stays active) and embeds thirteen static
scenes in forms of its own: seven Swing/SwingX and six JavaFX, including a
JOptionPane and an Alert that are captured while open and then answered from
code, with the blocking call's return value asserted.

DesktopCompatScenesTest drives the same sources headless: it compiles the
FXML and the Java, remaps them in library mode, checks bytecode compliance,
and shows, paints and answers every scene from the relocated classes.

The feature is registered in port_status.json. No goldens are committed:
each port's screenshot job reports the thirteen DesktopCompat* images as
missing_expected until its artifact is copied into that port's golden
directory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Describes library mode (no entry point generated), SwingInterop.asComponent
and FxInterop.asComponent, and opening frames, stages and dialogs from a
Codename One application. The snippets are compiled demo sources, so the
demo module gains the Swing runtime as a provided dependency.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… key note

The diff-scoped header gate covers .css, and four style sheets added with the
layers had none. JTable's class comment promised Home/End/Page/F2 handling
that rt/KeyMap documents no port can deliver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…L compile

The screenshot scenes test was written against the DesktopResourceCompiler
that took the compile classpath; the JavaFX merge moved FXML compilation into
the remap and dropped that parameter, so build-engine's test sources no longer
compiled and the PR SpotBugs line stopped at build-engine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The parity tests compare each layer class with the class of the JDK the
test runs on, and the layers model Java 8. On Java 17 and 21 the JDK itself
answers differently in a handful of places -- CLDR number symbols, a URL
host with a space, tight curve bounds, GroupLayout reaching sun.swing --
so those comparisons failed without the layer being wrong. They now call
ReferenceJdk.assume with the reason and are reported as skipped there.

ApiShapeTest gets a one-way allowance for GroupLayout.SequentialGroup,
which the JDK made final after Java 17: the layer stays as open as the
most permissive JDK an application can be compiled against.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shai-almog and others added 19 commits October 15, 2026 18:43
Moves the compat-jdk java.io layer into vm/JavaAPI and Ports/CLDC11:
BufferedInputStream/OutputStream, BufferedReader (without lines()),
BufferedWriter, PrintWriter (printf/format over String.format),
Closeable implemented by InputStream/OutputStream/Reader/Writer,
UncheckedIOException, Externalizable/ObjectInput/ObjectOutput with
their JDK members, and the JdkIo members readAllBytes, readNBytes and
transferTo on InputStream and Reader. CLDC11 Writer becomes Appendable
as JavaAPI's already is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
java.io.File, FileInputStream/FileOutputStream, FileReader/FileWriter,
FileFilter/FilenameFilter, the PrintWriter file constructors, java.nio.file
and java.nio.file.attribute move into vm/JavaAPI and Ports/CLDC11, all going
through JdkHost. JdkHost gains the file test/set/length/lastModified/create/
replace/canonicalPath/freeSpace operations; CoreJdkHost maps them onto
FileSystemStorage, and PosixJdkHost implements them over PosixFileAccess.

Behaviour change: a program translated without Codename One must install
PosixJdkHost before touching files. The vm/tests fixtures and the IoBench
benchmark now do (vm/benchmarks PosixHost).

The stream-returning methods (Files.lines x2, list, walk x2, find, and
BufferedReader.lines) are left out.

ProcessBuilder takes directory(File), and RuntimeExec gains the File
overloads.

BufferedInputStream.read(byte[],int,int) now keeps reading while the
underlying stream has bytes available, as the JDK does; the new parity test
found the difference.

Parity tests: NioPathParityTest, NioFilesParityTest and IoParityTest in
maven/jdk-parity-tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generated app module now declares the nio flavour of the desugared
library (same 2.1.5 release), so java.nio.file works below API 26.
The dependency line is built by a package-private static helper,
coreLibraryDesugaringDependency, with a unit test.

A desugared library the project already declares, in any flavour, still
wins. The legacy android.useGradle8=false scaffolding has no desugaring
at all, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… CLDC11 and JavaAPI

The 35 java.util.function interfaces the class library lacked (the Int/Long/Double
forms, the To*/Obj* functions, BiPredicate, BooleanSupplier) are added to both
Ports/CLDC11 and vm/JavaAPI, with the defaults the JDK declares. The eight that were
there gain their defaults and statics: Function compose/andThen/identity,
BiFunction.andThen, BinaryOperator now extends BiFunction and has minBy/maxBy,
UnaryOperator.identity, Consumer/BiConsumer andThen, Predicate and/or/negate/not/
isEqual. Comparator gains comparingInt/Long/Double, thenComparing(key, comparator),
thenComparingInt/Long/Double and nullsFirst/nullsLast (whose reversed() keeps null
on its side and reverses the rest, as the JDK's does); CLDC11's Comparator gets the
whole default set JavaAPI already had. This is what compat-jdk's JdkFunctions and
its 35 interfaces provided under relocated names.

No lambdas in the library: the composed objects are anonymous classes, so nothing
reaches invokedynamic and no public nested type is added to an interface.

Tests: vm/tests FunctionalApiIntegrationTest runs FunctionEdgeApp on the JVM and on
ParparVM and compares (the Function/Comparator defaults cannot be renamed apart from
the JDK collections that call them); maven/jdk-parity-tests relocates the 35 new
interfaces and holds their defaults against the JDK's (PrimitiveFunctionParityTest).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…LDC11

JavaAPI's StreamImpl is replaced by compat-jdk's lazy pull pipelines
(ObjPipeline, Int/Long/DoublePipeline, StreamState, PullIterator), ported
without lambdas, together with IntStream/LongStream/DoubleStream, the
Optional* and *SummaryStatistics value types, PrimitiveIterator, the full
Collectors set and the Stream/BaseStream members. Ports/CLDC11 gets the
same files, so core compiles against exactly what the device runs.

Collection.stream()/parallelStream()/spliterator()/toArray(IntFunction),
Iterator.remove()/forEachRemaining, Iterable.spliterator, List/Set/Map.of
and copyOf, Map.entry/ofEntries, Map.Entry.comparingBy*, Arrays.stream and
Optional.stream join the JDK-owned interfaces; a minimal Spliterator,
Spliterators and StreamSupport carry Collection.stream() into the package.

Two behaviours StreamImpl had and the compat pipeline lacked are kept: a
terminal operation that throws closes every mapped stream flatMap still
holds (latest first, close failures suppressed), and close() rethrows a
sneaky checked exception unwrapped. isParallel() now reports what was
asked for. Immutable sets and maps are hashed, not linked, so an app that
never calls them does not retain LinkedHashMap/LinkedHashSet.

Core regex regains Pattern.splitAsStream and Matcher.results(). The
compat stream, primitive stream, collectors and immutable-collection
differential tests run in jdk-parity-tests against the relocated JavaAPI
classes; StreamPipelineApp holds the JDK-owned members on ParparVM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ctions

The executors, CompletableFuture, ConcurrentHashMap, the copy-on-write
collections, CountDownLatch and the scheduled executor move from compat-jdk
into the class library, identical in vm/JavaAPI and Ports/CLDC11.

- Thread-safe for real, since vm/backend runs them multi-threaded: the map
  and list synchronize on their own lock, futures complete under a monitor.
- Executors.Pool replaces the fixed pool: workers start lazily up to the
  limit, honour the ThreadFactory, and the cached pool is unbounded with a
  keep-alive. FutureTask gains done(), set(), setException() and
  runAndReset(), so ExecutorCompletionService now learns of completions.
- A wait on the UI thread (Future.get, CompletableFuture.get/join,
  CountDownLatch.await, awaitTermination) goes through JdkHost.blockUi.
  Waits are interruptible.
- invokeAll cancels in reverse order on failure, here and in compat-jdk's
  Executors: cancelling the first task freed a worker that started the
  next one before the loop reached it.

Tests: ConcurrencyContractTest (deterministic invokeAll order, proven to
fail with the old order; blockUi routing; concurrent mutation), and the
compat ExecutorsTest and CopyOnWriteArraySetDifferentialTest moved to
jdk-parity-tests; ConcurrentApiApp compares ParparVM against the JVM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… CLDC11

Brings vm/JavaAPI's java.time up to the member set compat-jdk's JdkTime and
JdkDates provided, so desktop and Android code can use the device classes
directly:

- DayOfWeek, Month, TemporalUnit/ChronoUnit, TemporalQuery, TemporalAmount,
  a real Temporal and the chrono interfaces.
- Duration/Period/Instant/LocalDate/LocalTime/LocalDateTime/ZonedDateTime/
  OffsetDateTime/Clock members (from, parse, until, truncatedTo, overlap
  offsets, Instant.MIN/MAX, OffsetDateTime.MIN/MAX, Clock.withZone/offset).
- DateTimeFormatter: SMART resolution of 24:00 and clock hour 0, ISO_INSTANT
  over the full Instant range, ISO_INSTANT parse with an override zone.
- java.util.Date: from/toInstant and the JDK accessors.
- TimeZoneSupport folded into java.time.

Ports/CLDC11/src/java/time is now byte-identical to JavaAPI's.

TimeParityTest in maven/jdk-parity-tests replaces compat-jdk's
TimeDifferentialTest against the relocated JavaAPI classes, with the
JDK 8 defects JavaAPI does not copy special-cased. TimeEdgeApp exercises the
new API through the translator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…JavaAPI and CLDC11

Format gets the JDK shape (format(Object, StringBuffer, FieldPosition),
parseObject(String, ParsePosition), a final format(Object)). NumberFormat,
DecimalFormat, DecimalFormatSymbols, ChoiceFormat, MessageFormat,
FieldPosition and ParsePosition move in from compat-jdk, identical in both
trees: NumberFormat and MessageFormat now extend Format, DecimalFormat's exact
comparison uses a private base-1e9 magnitude instead of a core BigInteger, and
DecimalFormatSymbols reads the device's separators and currency from
JdkHost.localeNumberSymbols(). MessageFormat gains parse, getFormats and the
setFormat family.

DateFormat gains the Format adapters and parse(String, ParsePosition) while
keeping its native style formatting. Parsing at an offset exposed two
SimpleDateFormat bugs that are fixed here: number and month reads tested
index 0 instead of the read offset, and a field the pattern does not parse
took the current time instead of the epoch, as the JDK does.

jdk-parity-tests relocates java/text/ and runs DecimalFormatParityTest,
MessageFormatParityTest (now covering parse and the format setters) and
DecimalFormatSymbolsHostTest against the JDK on 8 and 17.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… core resource support

JavaAPI and CLDC11 gain the members the switch-over from compat-jdk relies on:
List's sequenced methods, CharSequence chars/codePoints, Throwable
printStackTrace(PrintWriter), lazy Files.lines/list/walk/find,
BufferedReader.lines, ServiceLoader.findFirst and load(ModuleLayer, Class),
URLEncoder/URLDecoder. JavaAPI's java.util.regex is gone: desktop regex is
relocated to com.codename1.util.regex.

Throwable now prints the JDK's shape on a device: toString() as the first
line (a never-thrown throwable has no native trace and printed "null"; a
thrown one printed only its class name) and "Caused by: " before each cause.

Core Resources answers META-INF/services files from the generated registry
(cn1ServicesFile); the JavaSE and Android ports install CompatResourceSupport
(cn1res URLs and a flat resource loader whose services lookups see device
semantics) and UncaughtExceptions keeps the EDT hook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Relocation keeps java.* names except regex (com.codename1.util.regex),
ProcessBuilder/Process (com.codename1.io), Runtime.exec, String's regex members
and resource lookups. ApplicationRegistry generates
com.codename1.generated.compat.CompatRegistry, which the lifecycles, the
generated main and library mode install. Tests load JdkHost from the class
library and install the registry the way an application does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
swing-compat, javafx-compat, android-compat and fxml-compiler use java.*
and core com.codename1.impl.compat directly; the layers carry the placeholder
CompatRegistry. compat-testing compiles the JavaSE port's JdkHost and installs
JDK resource support in the headless implementation. android-compat's
com.codename1.compat.jdk tests go with the module they tested; the bulk
ParparVM conformance port covers that behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its API lives in Ports/CLDC11 and vm/JavaAPI under the real java.* names.
Reactor, CI workflows, SpotBugs gate list, cast-semantics scope, benchmark
scripts and CLAUDE.md updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, JavaSE vs ParparVM

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d on ART

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every one of these answered differently from the JDK on ParparVM and was
found by running the class-library checks on both:

- Thread.interrupt now wakes Object.wait and Thread.sleep. The waiter
  publishes the monitor it waits on (ThreadLocalData.waitingOn) and the
  interrupter broadcasts that monitor's condition, taking its mutex with
  trylock and retrying so it can never deadlock against a thread holding
  lockCriticalSection; a pre-set interrupt throws before waiting. sleep is
  cut into 50ms slices that stop on the flag. The JS runtime's wait throws
  for a pre-set interrupt the same way.
- String.getBytes, new String(bytes, name), OutputStreamWriter and
  InputStreamReader refuse an unknown charset name with
  UnsupportedEncodingException instead of silently using UTF-8, and honour
  aliases in any case (latin1, iso-8859-1, ISO8859_1). PrintWriter asks
  Charset for the same reason.
- File permission setters chmod (they answered false and did nothing),
  Files.move no longer half-copies a source it cannot remove, URI rejects
  illegal characters in authority/path/query/fragment, Random.nextBytes
  uses the JDK's documented algorithm, and DecimalFormat produces the JDK's
  digits through LegacyDoubleDigits, a rewrite of OpenJDK's FloatingDecimal
  digit generation that keeps its Oracle notice (NOTICE, header exclusions).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-parity-tests

BulkConformanceIntegrationTest compiles one program of named checks, runs it
on HotSpot, translates and builds it ONCE with ParparVM, runs it, and diffs
the two outputs check by check (282 checks in 36 groups). It replaces:

- the vm/tests per-API integration tests (CatchRethrow, CollectionSemantics,
  FunctionalApi, LangMembers, StreamApi, StringBuilderGrowth, StringFormat,
  SystemMembers, TimeApi), each of which paid for its own translation and
  native build; their fixture programs move under bulk/ unchanged in what
  they check;
- maven/jdk-parity-tests, which ran vm/JavaAPI relocated on the JVM rather
  than translated. Its 259 tests are ported check for check, except the
  Optional relocation self-check (meaningless without relocation) and
  CoreJdkHostInstallTest, which tests core code and moves to core-unittests.
  Delays.pending() assertions become the public effect they stood for
  (the dropped work becomes collectable), since Delays is package-private.

Checks that a JDK may legitimately answer differently (host-backed
behaviour, an internal ordering the JDK races on) run as cn1Only with
expectations. Weak-reference checks drop their referents on a thread that
has ended: ParparVM scans native stacks conservatively, so a stale word on a
live stack may keep a referent, which the JDK contract allows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…piler

FileClass, PosixJdkHost, Lock, ReadWriteLock and StampedLock integration
tests each compiled JavaAPI, translated and ran a native build over the same
compiler matrix. HostLocksIntegrationTest builds one program holding all
five per configuration and keeps every assertion: it compiles, the CMake
project is emitted and builds, the run exits 0 (FileTestApp exits 1 on any
failure) and prints POSIX-HOST-OK. PosixHostApp runs first because it checks
the JdkHost defaults before anything is installed. The lock programs are
still built and not run, as before (their run was switched off for a crash
on Linux runners), behind a branch no command line takes so the dead-code
pass keeps them.

ExecutorIntegrationTest was @disabled and ran nothing; its two programs are
now the bulk check Executors.aSubclassOfAbstractExecutorServiceRunsWhatItIsSubmitted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SwitchoverMembersApp becomes one check of the bulk conformance program
instead of a translation of its own; the bulk compile prefers JDK 21 for
its sequenced List calls. Comments no longer name the deleted compat-jdk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A translated backend program that wrote a file before touching Backend
got 'No file system': the PosixJdkHost install sat only in Backend's
static initializer. The clean target's generated main() now calls
FileIo.installJdkHost first when the backend runtime is translated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shai-almog and others added 2 commits October 15, 2026 23:12
- vm/tests: javac takes its arguments through an @argfile; the JavaAPI
  compile outgrew Windows' 32K command line (CreateProcess error=206).
- Resources.loadClass answers from the generated registry instead of
  Class.forName (Error Prone BanClassForName).
- ZonedDateTime: the zone in the doc example was read as a link.
- Optional: /// comment; StandardCharsets and the regex package-info get
  the full header; the Harmony files are listed as excluded.
- check-guide-api-names reads docs/demos, where the guide defines Level1.
- SecurityChecks seeds SecureRandom from generateSeed (CodeQL).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- TimeArithmetic.multiplyExact: test MIN_VALUE * -1 before dividing; C's
  division traps (SIGFPE) on x86 for MIN_VALUE / -1.
- floatToIntBits/doubleToLongBits natives answer the canonical NaN, as the
  JDK does, instead of whatever the hardware produced.
- android-compat Context: directories are built from native paths
  (FileSystemStorage.toNativePath); the platform's java.io.File no longer
  receives a file: URL now that the JDK shim module is gone.
- check-native-signatures: the backend compile sees JavaAPI's JdkHost.
- PMD: @OverRide on the digest methods, foreach, Resources.exists close.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

seconds += amount * 86400L;

P2 Badge Reject overflowing duration text instead of wrapping

When a parsed day, hour, or minute field overflows while being converted to seconds, these unchecked operations wrap to an unrelated duration. For example, Duration.parse("P106751991167301D") returns a large negative duration here, while the JDK reports a DateTimeParseException; use checked multiplication and addition throughout this parsing block and translate arithmetic overflow into a parse failure. The mirrored vm/JavaAPI implementation has the same issue.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/CLDC11/src/java/nio/file/PathImpl.java Outdated
Comment thread Ports/CLDC11/src/java/util/concurrent/CompletableFuture.java
Comment thread vm/JavaAPI/src/java/lang/String.java Outdated
Comment thread Ports/CLDC11/src/java/util/stream/IntPipeline.java Outdated
Comment thread Ports/CLDC11/src/java/nio/file/Files.java Outdated
…aths

- Regex: under CASE_INSENSITIVE the case classes (\p{Lower}, \p{Lu},
  javaLowerCase, ...) match either case, as on the JDK since 16
  (JDK-8214245); the differential test skips them on an older JDK.
- Time zones: far-future instants are folded into 2400-2799 before asking
  libc; glibc answered standard time for distant years where the JDK and
  macOS apply the recurring rule.
- Windows port: stripFileUrl accepts the standard file:///C:/ form, which
  java.io.File hands over; it produced /C:/..., refused with error 123.
- Bulk executor check proves the repetition continues rather than timing
  300 runs, which a loaded CI runner could not fit in 30 seconds.
- Review: Path.toUri ends a directory in '/', orTimeout/completeOnTimeout
  require the unit, String.indent(MIN_VALUE), primitive stream terminals
  release flatMap inner streams on failure, Files.createFile and CREATE_NEW
  claim the path with the exclusive create, Duration.parse refuses
  overflowing fields (with bulk cases).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cfb5423474

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/CLDC11/src/java/nio/file/Files.java Outdated
Comment thread Ports/CLDC11/src/java/time/LocalTime.java Outdated
Comment thread Ports/CLDC11/src/java/time/ZoneId.java
Comment thread Ports/CLDC11/src/java/util/WeakHashMap.java
Comment thread vm/JavaAPI/src/java/lang/Character.java Outdated
Comment thread CodenameOne/src/com/codename1/util/regex/Pattern.java
shai-almog and others added 2 commits October 16, 2026 06:14
…on units

- Regex: \b and \B use ASCII word characters, as JDK 19+ does; the
  differential test compares that only against a JDK that agrees.
- Pattern.compile rejects flag bits no constant names.
- Character.getNumericValue answers superscripts, subscripts, Roman
  numerals and circled numbers, and -2 for fractions; a bulk check sweeps
  those ranges against HotSpot.
- LocalTime/Instant.truncatedTo accept any unit that divides a day.
- Files.createDirectory reports an existing path as FileAlreadyExists, and
  temporary names retry on it instead of failing a racing caller.
- WeakHashMap reads purge collected keys, amortized over the reads.
- ZoneId.of: why an unknown region is not rejected.
- Windows x64 hello RAM baseline for a runner CPU model with no row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved:
- Escape analysis: master landed the same fix (EXACT_CALL, lastClusterStore,
  resolveReceiverCall); took master's and kept the private-method nestmate
  resolution in resolveReceiverCall.
- URLEncoder/URLDecoder (JavaAPI): took master's codec.
- Linux/Windows ports: kept the core JDK host installer, added master's window
  size hints; Linux a11y keeps the pointer pass-through on master's batching.
- Lock/FileClass integration tests: kept the deletion (covered by bulk runs).
- Gate lists, spotbugs excludes and guide accept lists: kept both sides.

CI fixes in the same commit:
- Stream pipelines unbox through helpers, so the cast-semantics gate no longer
  sees a checkcast inside the try/catch(Throwable) blocks.
- iOS packaging and Metal screenshot timeouts raised: the hello app now ships
  the Swing/JavaFX runtimes, about 22% more translated C.
- Bulk conformance case 38 uses an empty charset name, which both runtimes
  refuse under master's lazier encoder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Flutter vs Codename One

Same 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.

android

9 interleaved rounds; host load 0.54, 1.61, 1.4

Metric Codename One Flutter Ratio Winner
Installed size 78.2 MB 95.8 MB 1.23x Codename One
Executable code 4.7 MB 20.6 MB 4.42x Codename One
Download size (zipped) 78.2 MB 95.8 MB 1.23x Codename One
Cold start to first frame on screen 474 ms 1527 ms 3.15x Codename One
Memory at rest 46.4 MB 79.3 MB 1.72x Codename One

Start-up and memory at rest are judged round by round: the ratio is the median of the per-round ratios, each from two launches made back to back, and the times and sizes shown are each side's median, so the ratio need not equal their quotient. Codename One was ahead in 9 of 9 rounds on cold start to first frame on screen; 9 of 9 rounds on memory at rest.

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Workload Codename One Flutter Ratio
intArithmetic 89 ms 186 ms 2.09x
longArithmetic 64 ms 58 ms 0.91x
mathTranscendental 226 ms 348 ms 1.54x
arraySequential 27 ms 36 ms 1.33x
arrayRandom 274 ms 300 ms 1.09x
objectAllocation 66 ms 51 ms 0.77x
valueEscape 7 ms 7 ms 1.00x
hashMapChurn 143 ms 299 ms 2.09x
stringBuilding 97 ms 126 ms 1.30x
recursion 85 ms 272 ms 3.20x
quicksortBench 127 ms 152 ms 1.20x
Geometric mean 1.38x

Gate: within tolerance against scripts/flutter-bench/baseline.

Android sizes are for the arm64-v8a slice a phone is delivered, on both sides; Flutter's release apk carries three ABIs and would otherwise count its engine about three times.
Android start-up and memory come from an x86_64 emulator with a software GPU. They compare the two runtimes on one machine; they are not phone timings, and a renderer that leans on the GPU is penalised more there.
This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

ios

9 interleaved rounds; host load 34.03, 38.6, 32.89

Metric Codename One Flutter Ratio Winner
Installed size 107.9 MB 110.5 MB 1.02x Codename One
Executable code 16.9 MB 19.1 MB 1.13x Codename One
Download size (zipped) 79.9 MB 81.9 MB 1.02x Codename One
Cold start to first frame on screen -- -- -- not measured
Memory at rest -- -- -- not measured

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Not measured: iOS compute is not measured: a device build needs signed hardware, and on the simulator Flutter runs its JIT debug engine rather than AOT code. The macOS row runs the same ParparVM and Dart AOT compilers on the same Apple silicon.

Gate: within tolerance against scripts/flutter-bench/baseline. Smaller than its baseline, which does not fail the gate: Executable code -8.86%; Installed size -1.50%; Download size (zipped) -0.74% -- rebaseline to tighten. To accept the move, download this platform's baseline-<platform>.json artifact and run python3 scripts/flutter-bench/flutter_baseline.py calibrate --pr 5980 --reason "<why these moved>" baseline-ios.json, then commit the overlay it writes.

iOS start-up and memory are not measured here: Flutter cannot AOT-compile for the simulator, so a simulator run would time a JIT debug build against a release build. Sizes come from release device bundles; timings need signed hardware.
This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

javascript

9 interleaved rounds; host load 1.93, 1.22, 0.55

Metric Codename One Flutter Ratio Winner
Installed size 103.8 MB 122.5 MB 1.18x Codename One
Executable code 12.7 MB 25.2 MB 1.98x Codename One
Download size (zipped) 76.3 MB 84.1 MB 1.10x Codename One
Cold start to first frame on screen -- -- -- not measured
Memory at rest -- -- -- not measured

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Workload Codename One Flutter Ratio
intArithmetic 643 ms wrong result Flutter: wrong result
longArithmetic 5082 ms did not run Flutter: did not run
mathTranscendental 570 ms did not run Flutter: did not run
arraySequential 226 ms 82 ms 0.36x
arrayRandom 938 ms wrong result Flutter: wrong result
objectAllocation 38 ms 27 ms 0.71x
valueEscape 149 ms 7 ms 0.05x
hashMapChurn 1097 ms 246 ms 0.22x
stringBuilding 521 ms 106 ms 0.20x
recursion 507 ms 489 ms 0.96x
quicksortBench 401 ms wrong result Flutter: wrong result
Geometric mean 0.28x

Gate: OUTSIDE THE BASELINE against scripts/flutter-bench/baseline; BEHIND FLUTTER on Compute (geomean of the VM workloads) (reported, not gated: a loss to Flutter on the web is currently accepted). To accept the move, download this platform's baseline-<platform>.json artifact and run python3 scripts/flutter-bench/flutter_baseline.py calibrate --pr 5980 --reason "<why these moved>" baseline-javascript.json, then commit the overlay it writes.

the JavaScript adapter needs a Chrome DevTools driver; sizes are reported and timings are not
This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

linux

9 interleaved rounds; host load 0.17, 2.74, 2.32

Metric Codename One Flutter Ratio Winner
Installed size 111.1 MB 143.8 MB 1.29x Codename One
Executable code 13.9 MB 16.1 MB 1.16x Codename One
Download size (zipped) 80.8 MB 92.9 MB 1.15x Codename One
Cold start to first frame on screen 150 ms 215 ms 1.45x Codename One
Memory at rest 87.6 MB 236.5 MB 2.70x Codename One

Start-up and memory at rest are judged round by round: the ratio is the median of the per-round ratios, each from two launches made back to back, and the times and sizes shown are each side's median, so the ratio need not equal their quotient. Codename One was ahead in 9 of 9 rounds on cold start to first frame on screen; 9 of 9 rounds on memory at rest.

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Workload Codename One Flutter Ratio
intArithmetic 77 ms 235 ms 3.05x
longArithmetic 61 ms 66 ms 1.08x
mathTranscendental 223 ms 256 ms 1.15x
arraySequential 20 ms 40 ms 2.00x
arrayRandom 236 ms 259 ms 1.10x
objectAllocation 32 ms 39 ms 1.22x
valueEscape 4 ms 13 ms 3.25x
hashMapChurn 47 ms 298 ms 6.34x
stringBuilding 31 ms 122 ms 3.94x
recursion 100 ms 208 ms 2.08x
quicksortBench 154 ms 151 ms 0.98x
Geometric mean 1.96x

Gate: OUTSIDE THE BASELINE against scripts/flutter-bench/baseline. To accept the move, download this platform's baseline-<platform>.json artifact and run python3 scripts/flutter-bench/flutter_baseline.py calibrate --pr 5980 --reason "<why these moved>" baseline-linux.json, then commit the overlay it writes.

This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

macos

9 interleaved rounds; host load 5.15, 11.21, 14.76

Metric Codename One Flutter Ratio Winner
Installed size 123.7 MB 139.4 MB 1.13x Codename One
Executable code 32.6 MB 47.9 MB 1.47x Codename One
Download size (zipped) 85.4 MB 93.3 MB 1.09x Codename One
Cold start to first frame on screen 573 ms 644 ms 1.21x Codename One
Memory at rest 36.5 MB 54.6 MB 1.51x Codename One

Start-up and memory at rest are judged round by round: the ratio is the median of the per-round ratios, each from two launches made back to back, and the times and sizes shown are each side's median, so the ratio need not equal their quotient. Codename One was ahead in 7 of 9 rounds on cold start to first frame on screen; 9 of 9 rounds on memory at rest.

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Workload Codename One Flutter Ratio
intArithmetic 138 ms 263 ms 1.91x
longArithmetic 88 ms 76 ms 0.86x
mathTranscendental 325 ms 350 ms 1.08x
arraySequential 12 ms 46 ms 3.83x
arrayRandom 314 ms 320 ms 1.02x
objectAllocation 55 ms 30 ms 0.55x
valueEscape 5 ms 13 ms 2.60x
hashMapChurn 43 ms 193 ms 4.49x
stringBuilding 30 ms 135 ms 4.50x
recursion 163 ms 271 ms 1.66x
quicksortBench 155 ms 140 ms 0.90x
Geometric mean 1.68x

Gate: within tolerance against scripts/flutter-bench/baseline. Smaller than its baseline, which does not fail the gate: Executable code -7.73%; Installed size -2.16%; Download size (zipped) -1.31% -- rebaseline to tighten. To accept the move, download this platform's baseline-<platform>.json artifact and run python3 scripts/flutter-bench/flutter_baseline.py calibrate --pr 5980 --reason "<why these moved>" baseline-macos.json, then commit the overlay it writes.

This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

windows

9 interleaved rounds

Metric Codename One Flutter Ratio Winner
Installed size 107.5 MB 121.1 MB 1.13x Codename One
Executable code 11.2 MB 14.1 MB 1.26x Codename One
Download size (zipped) 77.8 MB 86.4 MB 1.11x Codename One
Cold start to first frame on screen 126 ms 210 ms 1.67x Codename One
Memory at rest 58.1 MB 92.7 MB 1.59x Codename One

Start-up and memory at rest are judged round by round: the ratio is the median of the per-round ratios, each from two launches made back to back, and the times and sizes shown are each side's median, so the ratio need not equal their quotient. Codename One was ahead in 9 of 9 rounds on cold start to first frame on screen; 9 of 9 rounds on memory at rest.

Compute -- the VM workloads (vm/benchmarks), run inside each app; time is the best of the app's own repetitions, higher ratio means Codename One is faster.

Workload Codename One Flutter Ratio
intArithmetic 71 ms 169 ms 2.38x
longArithmetic 53 ms 56 ms 1.06x
mathTranscendental 162 ms 193 ms 1.19x
arraySequential 8 ms 30 ms 3.75x
arrayRandom 223 ms 235 ms 1.05x
objectAllocation 16 ms 27 ms 1.69x
valueEscape 0 ms 6 ms too fast to time
hashMapChurn 34 ms 262 ms 7.71x
stringBuilding 28 ms 93 ms 3.32x
recursion 148 ms 233 ms 1.57x
quicksortBench 121 ms 127 ms 1.05x
Geometric mean 1.96x

Gate: within tolerance against scripts/flutter-bench/baseline. Smaller than its baseline, which does not fail the gate: Executable code -11.65%; Installed size -1.35%; Download size (zipped) -0.89% -- rebaseline to tighten. To accept the move, download this platform's baseline-<platform>.json artifact and run python3 scripts/flutter-bench/flutter_baseline.py calibrate --pr 5980 --reason "<why these moved>" baseline-windows.json, then commit the overlay it writes.

This platform's adapter has not been exercised end to end before; treat the first published run as the thing under review.

Codename One wins 26 of 26 measured metrics across 6 platform(s).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants