Repository navigation
Flutter: port ordinary apps unmodified (language, dart:io/convert, pub packages, plugin stubs) - #6055
Conversation
…on classes, redirecting constructors, function-typed params, mixin on-clauses Real Flutter apps lean on these constructs (bloc's async* event handlers, `mixin X on State<W>`, `: this(...)` constructors), and each one used to fail the build with E01xx/E03xx. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s, super assignment, >>> operator Also fixes two pre-existing emitter bugs real code hits: a factory in a generic class referenced the class's type variable from a static method, and a class declaring factory constructors could not be implemented (the shape of every freezed model). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed list literals dart:convert (json, utf8, ascii, latin1, base64, LineSplitter) in dart-runtime with a stub, and a Dyn runtime that dispatches member reads, writes, calls and indexing on dynamic receivers -- maps, lists, strings, numbers, functions, and transpiled classes through generated DynamicObject members. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
File, Directory and FileSystemEntity over FileSystemStorage (an in-memory file system before Codename One starts), Platform from Display.getPlatformName(), HttpClient/HttpClientRequest/HttpClientResponse over ConnectionRequest, HttpHeaders, ContentType, HttpStatus and WebSocket over com.codename1.io.WebSocket. Stream gains its factories, terminal operations, derived streams and transform(); the dart:convert codecs are StreamTransformers. Future.then types an untyped callback parameter from the future's value type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ceptance app
Stub packs (flutter-runtime com.codename1.flutter.plugins.*, META-INF/dart/plugin_*.dart):
bloc/flutter_bloc, equatable, json_annotation, shared_preferences, path_provider, dio,
go_router (with MaterialApp.router) and cached_network_image, each with a headless
behavior test. The importer lists them as covered and transpiles other pure-Dart pub
packages from their sources.
Transpiler: stub extension methods, witness inference from a typed selector parameter
(context.select), explicit Java type arguments on generic stub calls, an empty {} read
as a Set where a Set is expected, Icons style-variant fallback, and javac-accurate
reachability after try/catch and if/else. Runtime: the full Material icon set,
FilledButton, Provider create blocks, DateTime.parse/toIso8601String.
scripts/flutter-apps/pet_adoption (bloc, go_router, dio, shared_preferences, equatable,
json_annotation, cached_network_image) imports, transpiles and compiles unmodified via
scripts/flutter-apps/Acceptance.java.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… CI job Plugin stub packs backed by Codename One APIs: cross_file, share_plus, package_info_plus, device_info_plus, connectivity_plus, geolocator, image_picker, app_links, flutter_local_notifications, firebase_core/auth/ analytics/messaging and google_maps_flutter. The importer marks them covered, and the generated entry point forwards push, local-notification and deep-link callbacks only when the app depends on the matching plugin. Widget fidelity: PopScope and WillPopScope veto Navigator.maybePop and the toolbar back command. StreamBuilder is new. FutureBuilder and StreamBuilder builders are typed, and Dismissible callbacks are typed too. Transpiler: - A user enum or class that shares a stub's name wins. - Explicit type arguments on non-generic Java methods are cast. - Callback parameters take the receiver's type arguments, so a closure passed to stream.listen gets typed parameters. A second acceptance app, field_notes (firebase, notifications, share, connectivity, PopScope, StreamBuilder), joins pet_adoption. scripts/flutter-apps/run-acceptance.sh runs both, and flutter-acceptance.yml runs it in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f0322cca0c
ℹ️ 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".
| if (path.startsWith("file:")) { | ||
| return path; | ||
| } | ||
| return path.startsWith("/") ? "file://" + path : path; |
There was a problem hiding this comment.
Resolve relative paths against Directory.current
For every on-device File or Directory created with a relative path such as File('notes.json'), this passes the raw name directly to FileSystemStorage. The platform implementations interpret that relative to the process working directory (and Android commonly ends up at an unwritable location), while Dart resolves it against Directory.current, which this runtime initializes to the app home. Consequently ordinary relative file reads and writes can fail or access the wrong location; qualify these paths with the current/app-home directory before calling FileSystemStorage.
Useful? React with 👍 / 👎.
| } | ||
|
|
||
| private void push(Object event) { | ||
| queue.addLast(event); |
There was a problem hiding this comment.
Preserve null events in StreamIterator
When an await for loop or any derived stream backed by StreamIterator receives a legitimate null event, ArrayDeque.addLast(null) throws. Stream.deliverData catches and logs that exception, so the event is silently dropped; if the source remains open, the pending moveNext() can wait forever. Nullable streams such as a controller emitting null or Stream.periodic with a null computation therefore produce incorrect results or hang, so the queue needs a non-null sentinel for null data.
Useful? React with 👍 / 👎.
| public Object decode(Object input, Boolean allowMalformed) { | ||
| if (kind == BASE64 || kind == BASE64_URL) { | ||
| return Uint8List.fromBytes(unbase64((String) input)); | ||
| } | ||
| return text(bytesOf(input)); |
There was a problem hiding this comment.
Reject malformed UTF-8 unless explicitly allowed
For utf8.decode with the default allowMalformed: false, this delegates to Java's new String(..., "UTF-8"), which silently replaces malformed sequences with U+FFFD rather than throwing Dart's FormatException; the allowMalformed argument is ignored entirely. Inputs such as [0xff] therefore appear successfully decoded and can let corrupt protocol or file data pass validation.
Useful? React with 👍 / 👎.
| protected void firstBuild() { | ||
| // After Element.mount linked the parent, so create(context) can read the | ||
| // providers above it -- `create: (c) => Cubit(c.read<Repo>())`. | ||
| supplyCreated(widget()); |
There was a problem hiding this comment.
Honor Provider's default lazy creation
Whenever a Provider(create: ...) is mounted, firstBuild() invokes the factory immediately even though Provider.lazy defaults to true and its setter is otherwise unused. Providers that are never read will still start requests, allocate blocs/resources, or throw during construction, unlike the package contract where creation is deferred until the first lookup; only lazy: false should create during the initial build.
Useful? React with 👍 / 👎.
| // Dart rounds a seventh digit and beyond into the microseconds. | ||
| if (frac.length() > 6 && frac.charAt(6) >= '5') { | ||
| all++; |
There was a problem hiding this comment.
Truncate excess DateTime fractional digits
For timestamps with more than six fractional digits, Dart's parser truncates after microseconds, but this code rounds based on the seventh digit. Thus DateTime.parse('...00.1234567Z') becomes .123457Z instead of .123456Z, and .9999999 even rolls into the next second, causing parsed instants and equality/order comparisons to differ from Dart.
Useful? React with 👍 / 👎.
| if (dots >= 3) { | ||
| int last = full.lastIndexOf('.'); | ||
| version = full.substring(0, last); | ||
| build = full.substring(last + 1); | ||
| } |
There was a problem hiding this comment.
Report the imported pubspec build number
For the normal Flutter version form 1.2.3+42, the importer sets AppVersion to the marketing part 1.2.3 and writes 42 to the native build-number hints. Since this code only invents a build number when AppVersion has at least three dots, PackageInfo.fromPlatform() returns an empty build number for virtually every imported app despite the installed Android/iOS build carrying 42, breaking version checks and diagnostics that use package_info_plus.
Useful? React with 👍 / 👎.
| this.active = req; | ||
| this.onCancel = cancelled; |
There was a problem hiding this comment.
Cancel every request attached to a CancelToken
When the same CancelToken is passed to multiple concurrent Dio requests, each attach overwrites the preceding request and callback. Calling cancel() therefore kills and completes only the most recently attached request, while the earlier requests continue running despite sharing the canceled token; retain all active request/callback pairs until they complete and cancel each of them.
Useful? React with 👍 / 👎.
| public Future<DartUri> getLatestLink() { | ||
| return getInitialLink(); | ||
| } |
There was a problem hiding this comment.
Return the most recently received app link
After the application receives a second deep link while running, appStarted() emits that link but leaves initial unchanged, and this implementation still returns the first launch link. Callers using getLatestLink() after a resume therefore navigate using stale data; track the most recently consumed AppArg separately from the one-shot initial link.
Useful? React with 👍 / 👎.
| synchronized (listeners) { | ||
| listeners.add(c); | ||
| } |
There was a problem hiding this comment.
Remove canceled Firebase auth stream controllers
Every call to authStateChanges(), idTokenChanges(), or userChanges() permanently adds a controller here, but no onCancel handler closes or removes it. In applications that recreate and cancel these subscriptions as widgets are mounted, the singleton retains one controller per old subscription for the lifetime of the process, and each auth update repeatedly scans them; remove the controller when its last listener cancels.
Useful? React with 👍 / 👎.
| if (c.superclass != null && !inherits) { | ||
| return false; |
There was a problem hiding this comment.
Emit dynamic members for models extending runtime classes
Any application class whose direct superclass is supplied by a runtime stub is excluded from DynamicObject, even when it is ordinary model data. For example, a Pet extends Equatable accessed through dynamic pet will throw NoSuchMethodError for pet.name, because Dyn.get has neither generated member dispatch nor reflection; restrict this optimization to known framework lifecycle classes rather than every non-program superclass.
Useful? React with 👍 / 👎.
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
Flutter vs Codename OneNo benchmark results were produced. |
|
Compared 178 screenshots: 178 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
|
Compared 214 screenshots: 214 matched. |
✅ ByteCodeTranslator Quality ReportTest & Coverage
Benchmark Results
Static Analysis
Generated automatically by the PR CI workflow. |
…pile - AsyncAndDependencyTest types its FutureBuilder builder (AsyncSnapshot<Object>). - dart:io resolves relative paths against Directory.current. - StreamIterator carries null events instead of dropping them. - utf8.decode throws FormatException on malformed input unless allowMalformed. - DateTime.parse truncates digits past microseconds, as the Dart VM does. - Stream.fromFutures emits in completion order. - Provider honours lazy: create runs on first lookup unless lazy is false. - Equatable hashes through DartRuntime.hashOf, so 1 and 1.0 agree. - The importer ignores import-like text inside triple-quoted strings. - Geolocator shares one platform listener and removes it with the last subscriber. - Local-notification payloads persist across process restarts. - package_info_plus reports the pubspec build number from a resource the importer writes. - CancelToken cancels every request it is attached to. - AppLinks.getLatestLink returns the most recent link. - FirebaseAuth drops change-stream controllers once they are cancelled. - App classes extending runtime model classes such as Equatable get dynamic members; only widget, State, Element and RenderObject subclasses are skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 281d800c2f
ℹ️ 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".
| if (to instanceof Future) { | ||
| // FutureOr<String?>: only an already-completed answer can steer a navigation | ||
| // that is happening now. | ||
| to = ((Future<?>) to).completedValue(); |
There was a problem hiding this comment.
Await asynchronous redirects before opening the route
When a redirect performs asynchronous work, such as reading an auth session, its Future is normally incomplete here, so completedValue() returns null and navigation immediately opens the unredirected route. Nothing subscribes to the future afterward, meaning even a later redirect result is permanently ignored and authentication guards can expose protected screens; defer route construction until the redirect future completes.
Useful? React with 👍 / 👎.
| collectDartSources(libDir, "", own); | ||
| for (String f : own) { | ||
| work.add(new String[] {null, f}); | ||
| seen.add("\u0000" + f); |
There was a problem hiding this comment.
Traverse dependencies from the actual entry point
When lib/ contains a dormant Dart file that the application's entry point never imports, this seeds the dependency walk with that file anyway. If the file imports an unsupported platform package—for example, an unused experimental camera implementation—the importer records that package as reached and rejects an application that Flutter itself can build because the file is outside the real import graph; seed the walk from the selected entry point and follow only reachable directives.
Useful? React with 👍 / 👎.
| } catch (Throwable t) { | ||
| stopped[0] = true; | ||
| c.completeError(dart.runtime.DartRuntime.caught(t)); | ||
| } |
There was a problem hiding this comment.
Cancel terminal stream subscriptions when callbacks throw
When a callback used by forEach, any, every, fold, or another collect-based operation throws after the asynchronous subscription has been installed, this catch completes the result with an error but never cancels the subscription. Since stopped then suppresses every later event, an infinite or platform-backed source remains subscribed indefinitely without doing useful work; cancel holder[0] here as the stream-error path already does.
Useful? React with 👍 / 👎.
| if (cut > 0) { | ||
| sink.yield$(text(head)); | ||
| } | ||
| } | ||
| if (carry.length > 0) { | ||
| sink.yield$(text(carry)); |
There was a problem hiding this comment.
Validate malformed UTF-8 in the streaming decoder
When stream.transform(utf8.decoder) receives malformed bytes, completed chunks and the final carry are passed directly to Java's string decoder, which substitutes U+FFFD rather than emitting Dart's FormatException. The new strict check in the direct decode path does not cover this bind path, so inputs such as a final [0xff] still pass silently; validate each completed prefix and the carry before decoding.
Useful? React with 👍 / 👎.
| } else if (c == '=') { | ||
| break; | ||
| } else { | ||
| throw new FormatException("Invalid character (at character " + (i + 1) + ")"); | ||
| } | ||
| acc = (acc << 6) | v; | ||
| bits += 6; | ||
| if (bits >= 8) { | ||
| bits -= 8; | ||
| out[n++] = (byte) (acc >> bits); | ||
| } | ||
| } |
There was a problem hiding this comment.
Reject invalid Base64 padding and trailing data
When an input has impossible length, misplaced padding, or characters after padding, encountering the first = simply stops decoding and any leftover bits are discarded. Consequently malformed values such as base64.decode('A') return empty bytes and base64.decode('QQ=garbage') returns truncated data instead of throwing FormatException, allowing corrupted encoded payloads to be accepted; validate the complete normalized quartet and padding structure.
Useful? React with 👍 / 👎.
| boolean pop = Boolean.TRUE.equals(ok) && !stack.isEmpty() && stack.get(stack.size() - 1) == top; | ||
| if (pop) { | ||
| pop(context, result); | ||
| PopGuards.notifyPopScopes(scopes, true, result); | ||
| } |
There was a problem hiding this comment.
Notify PopScope when another guard vetoes the pop
When a route contains a PopScope(canPop: true) alongside a WillPopScope that returns false or errors, mayPop completes false and this branch never invokes the PopScope callback. Flutter calls onPopInvokedWithResult for every attempted pop with didPop: false, so code that records the attempt or restores UI state misses it; notify the scopes on this veto path without double-notifying the existing canPop: false path.
Useful? React with 👍 / 👎.
| DartMap<String, Object> body = DartMap.<String, Object>of("idToken", user.idToken, | ||
| "returnSecureToken", Boolean.FALSE); |
There was a problem hiding this comment.
Refresh restored sessions before authenticated mutations
After a process restart, restore() reconstructs the current user with only a refresh token and leaves idToken null, but profile updates send that null token directly. The same direct use occurs for deletion and email verification, so these authenticated operations fail until application code happens to call getIdToken() first; refresh an absent or expired token before issuing each authenticated request.
Useful? React with 👍 / 👎.
| public final Stream<T> take(final long count) { | ||
| return derive(new Funcs.VoidFunc2<StreamIterator<T>, AsyncStar.Sink<T>>() { | ||
| @Override | ||
| public void call(StreamIterator<T> it, AsyncStar.Sink<T> s) { | ||
| for (long i = 0; i < count && it.next(); i++) { |
There was a problem hiding this comment.
Reject negative stream counts before subscribing
When take is called with a negative count, Dart throws a RangeError, whereas this implementation silently returns an empty stream. skip similarly passes every event, and elementAt(-1) consumes the entire source before failing—hanging forever for an infinite stream—so validate these arguments before creating or listening to the derived operation.
Useful? React with 👍 / 👎.
| } else if (!p.equalsIgnoreCase(t)) { | ||
| return false; |
There was a problem hiding this comment.
Match route literals case-sensitively
With the default go_router behavior, literal path segments are case-sensitive, but this comparison accepts any casing. A route declared as /Admin therefore also handles /admin, and if both variants are declared the first matching route makes the other unreachable; compare literal segments exactly unless an explicit case-insensitive route option is added.
Useful? React with 👍 / 👎.
| long at = scheduledDate instanceof DateTime | ||
| ? ((DateTime) scheduledDate).millisecondsSinceEpoch() : System.currentTimeMillis() + 10; | ||
| schedule(id, title, body, details, payload, at, LocalNotification.REPEAT_NONE); |
There was a problem hiding this comment.
Preserve zonedSchedule recurrence requests
When callers pass matchDateTimeComponents to request a daily or weekly recurring notification, this unconditionally schedules REPEAT_NONE. The first notification may fire at the requested instant, but all subsequent occurrences are lost; map the supported date-component modes to the platform repeat interval instead of treating every zoned schedule as one-shot.
Useful? React with 👍 / 👎.
- Transpiler: fix gallery regressions (stub super calls, model-only dynamic members, implicit downcast of dynamic results). - GoRouter: case-sensitive literal segments; async redirects awaited. - dart:async/convert/io: cancel on collect failure, Dart equality in distinct/contains, range checks; strict UTF-8 and Base64 validation; HttpClientRequest.abort kills the in-flight request. - Plugins: FCM opened flag, bloc released when explicit bloc dropped, dio ResponseType.stream returns a ResponseBody, PopScope notified once including WillPopScope vetoes, FirebaseAuth refreshes the ID token before account calls, zonedSchedule recurrence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6091c28c71
ℹ️ 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".
| } else if (len % 4 != 0) { | ||
| throw new FormatException("Invalid length, must be multiple of four (at character " + (len + 1) + ")"); |
There was a problem hiding this comment.
Allow valid unpadded Base64 input
When decoding common unpadded Base64 or Base64URL values such as QQ or JWT segments, this branch rejects every length that is not a multiple of four. Dart normalizes omitted padding for lengths modulo four of two or three—only a remainder of one is impossible—so valid tokens now fail with FormatException; validate the impossible remainder while decoding the valid leftover bits.
Useful? React with 👍 / 👎.
| Funcs.VoidFunc1<NotificationResponse> cb = onTap; | ||
| if (cb != null) { | ||
| Long id = null; |
There was a problem hiding this comment.
Preserve notification taps until initialization
On a cold launch caused by a local-notification tap, the Android launch flow invokes the generated LocalNotificationCallback before Lifecycle.run starts the Flutter app, so initialize() has not assigned onTap yet and this branch silently discards the notification ID. Because getNotificationAppLaunchDetails() also always returns null, an application launched from a killed state can never recover or route that tap; retain the pending response and deliver it after initialization.
Useful? React with 👍 / 👎.
| if (contentType[0] == null) { | ||
| contentType[0] = ro.contentType; | ||
| } | ||
| final byte[] payload = encodeBody(ro, contentType); |
There was a problem hiding this comment.
Route request-encoding failures through Dio's future
When request data cannot be encoded—for example, a map contains an object that jsonEncode cannot serialize—encodeBody throws here while fetch() is still executing synchronously. The exception therefore escapes dio.post() instead of completing its returned future with a DioException, and error interceptors never run; catch body-encoding failures and send them through call.error.
Useful? React with 👍 / 👎.
| public File absolute() { | ||
| return this; |
There was a problem hiding this comment.
Resolve File.absolute against Directory.current
For a relative entity such as File('notes.json'), absolute() returns the same relative object even though subsequent I/O resolves it against Directory.current. Callers that persist, share, compare, or inspect file.absolute.path therefore still receive notes.json rather than an absolute path; construct the resolved path here and apply the same correction to Directory.absolute().
Useful? React with 👍 / 👎.
| if (test == null || dart.runtime.Dyn.invoke(test, e) == Boolean.TRUE) { | ||
| deliverError(onError, e); |
There was a problem hiding this comment.
Forward exceptions thrown by handleError
When the source emits an error and the supplied handleError callback itself throws, deliverError catches and merely logs that exception as if it came from a terminal listener. handleError is a stream transformer, so Dart emits the callback's thrown exception on the returned stream; here downstream retry or error handling sees neither the original nor replacement error. Invoke the handler under a transformer-specific try/catch and pass the thrown value to out.addError.
Useful? React with 👍 / 👎.
| String raw = p.named ? "(n == null ? null : n.idx(\"" + p.name + "\"))" | ||
| : "(a.length > " + pi + " ? a[" + pi + "] : null)"; |
There was a problem hiding this comment.
Apply optional defaults in dynamic method dispatch
When a method is invoked through a dynamic receiver with an omitted optional parameter, this dispatcher always supplies null instead of the parameter's declared default. Reference parameters therefore observe the wrong value, while primitive parameters such as int count = 1 fail during unboxing; emit p.defaultValue for absent positional and named arguments, while preserving an explicitly supplied null.
Useful? React with 👍 / 👎.
| if (fs.exists(path)) { | ||
| if (exclusive != null && exclusive.booleanValue()) { | ||
| throw new FileSystemException("Cannot create file, it already exists", path); | ||
| } | ||
| return; |
There was a problem hiding this comment.
Reject directory paths in File.create
When path already names a directory and exclusive is false, File.createSync() returns successfully because this check treats every existing entity as an existing file. Dart instead completes with a FileSystemException for this file/directory collision, so callers can believe a file was created and fail only during later I/O; verify that the existing entity is actually a file before returning.
Useful? React with 👍 / 👎.
| req.setFailSilently(true); | ||
| req.setReadResponseForErrors(true); | ||
| req.setFollowRedirects(followRedirects); | ||
| req.setDuplicateSupported(true); |
There was a problem hiding this comment.
Enforce HttpClientRequest.maxRedirects
When callers lower maxRedirects—especially to zero to inspect the first redirect—close() only forwards followRedirects and never uses the stored limit. The platform therefore follows its own redirect policy instead of failing after the requested number, which can send authenticated requests through an unexpected redirect chain; enforce the request's maxRedirects while handling redirects.
Useful? React with 👍 / 👎.
| while (it.next()) { | ||
| T v = it.current(); | ||
| if (first || !equals.call(last, v).booleanValue()) { | ||
| s.yield$(v); |
There was a problem hiding this comment.
Keep distinct alive after equality callback errors
When a custom distinct(equals) callback throws for one event, the exception escapes this generator body, so AsyncStar.run emits the error and then closes the derived stream. Dart replaces only that data event with an error and continues processing later events without updating the previous value; catch the equality failure inside the loop, emit it as an error, and continue listening.
Useful? React with 👍 / 👎.
| sink.controller.onCancel(new Funcs.VoidFunc0() { | ||
| @Override | ||
| public void call() { | ||
| sink.cancelled = true; | ||
| } |
There was a problem hiding this comment.
Cancel upstream subscriptions when derived streams are canceled
When a listener cancels a derive-based stream such as source.distinct() while the source is idle, this hook only marks the sink canceled; the generator remains blocked in StreamIterator.next() and its upstream subscription is not canceled until another event arrives. A quiet platform source can therefore retain GPS, socket, or controller listeners indefinitely after cancellation; propagate cancellation to the active iterator/subscription immediately.
Useful? React with 👍 / 👎.
- Transpiler: dynamic dispatch applies declared defaults for omitted optional arguments (behavior case dart_dynamic_call_defaults). - Streams: handleError emits what its handler throws; a throwing distinct(equals) errors one event and continues; cancelling a derived stream cancels its upstream and wakes a pending moveNext (behavior case dart_stream_transform_errors). - dart:io: File/Directory.absolute resolve against Directory.current; File.create fails on a directory; HttpClientRequest enforces maxRedirects. Base64 strictness kept (matches the Dart VM), documented. - Plugins: dio routes body-encoding failures through its future and detaches settled requests from a CancelToken; bloc reports failed async handlers to onError; a cold-launch notification tap is reported by getNotificationAppLaunchDetails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
When a project is re-imported after adding or removing app_links, flutter_local_notifications, or firebase_messaging, the newly generated entry-point source differs from the previous generated file, but the presence of its original backup makes this branch classify that difference as a developer edit and return without writing it. Newly added plugins therefore never receive their lifecycle callbacks, while removed push dependencies can leave push enabled; compare against the previously generated template or otherwise distinguish generated changes from user edits.
When a later import changes version: 1.2.3+42 to a version without a +build suffix, this branch is skipped and leaves the old Android/iOS build hints and cn1-flutter-build-number.txt in place. Native builds and PackageInfo.buildNumber therefore continue reporting 42; remove or reset all three generated values when the current pubspec has no build-number component.
ℹ️ 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".
| return new DateTime(year, month, day, hour, minute, second, milli, micro); | ||
| } | ||
| DateTime u = utc(year, month, day, hour, minute, second, milli, micro); |
There was a problem hiding this comment.
Return null from tryParse for out-of-range dates
When a syntactically valid input lies outside Dart's DateTime range, such as 999999-01-01, these constructor calls throw ArgumentError from the range checks. DateTime.tryParse must return null for any value that DateTime.parse cannot produce, while parse should surface a FormatException; callers using tryParse on untrusted timestamps therefore unexpectedly throw instead of taking their invalid-input path.
Useful? React with 👍 / 👎.
| if (cut > 0) { | ||
| // Strict as decode() is: Java's decoder would substitute U+FFFD. | ||
| checkUtf8(head, head.length); | ||
| sink.yield$(text(head)); |
There was a problem hiding this comment.
Strip the UTF-8 BOM only at stream start
When a UTF-8 stream contains U+FEFF at the beginning of any later chunk, calling text(head) independently for every chunk strips that chunk's first EF BB BF sequence as though it were the stream BOM. For example, joining decoded chunks [0x41] and [0xef,0xbb,0xbf,0x42] yields AB instead of A\uFEFFB; track whether the decoder has processed its first code point rather than applying BOM removal to every chunk.
Useful? React with 👍 / 👎.
| if (fs.exists(path)) { | ||
| return; |
There was a problem hiding this comment.
Reject file collisions in Directory.create
When the requested path already exists as a regular file, this branch reports successful directory creation. Code can then continue with the returned Directory and fail only on a later listing or child write, whereas Dart's Directory.create/createSync throws FileSystemException for this collision; return early only when fs.isDirectory(path) is true.
Useful? React with 👍 / 👎.
| if (m.isStatic || m.isSetter || !m.typeParams.isEmpty() || m.name.startsWith("$")) { | ||
| continue; |
There was a problem hiding this comment.
Emit dynamic dispatch for declared setters
When a model exposes a property through an explicit setter rather than a mutable backing field, this exclusion leaves no corresponding $dset case. An assignment such as dynamic model = value; model.name = 'x'; therefore throws NoSuchMethodError even though the Dart class declares set name(...); generate setter dispatch instead of skipping these methods.
Useful? React with 👍 / 👎.
| String invoke = "this." + javaMethodName(m.name) + "(" + args + ")"; | ||
| call.append(" case \"").append(m.name).append("\": "); | ||
| if (rt.is("void") || overridesVoid(c, m.name)) { | ||
| call.append(invoke).append("; return null;\n"); | ||
| } else { | ||
| call.append("return ").append(invoke).append(";\n"); |
There was a problem hiding this comment.
Validate arguments before dynamic method dispatch
When a dynamic call supplies too many positional arguments or an unknown named argument, the generated case invokes the method using only its declared parameters and silently ignores the extras. Missing required nullable arguments are similarly converted to null and invoked; Dart instead raises NoSuchMethodError for each mismatched invocation, so validate the positional count and named-key set before executing the method.
Useful? React with 👍 / 👎.
| if (q.length() == 0) { | ||
| return url; | ||
| } | ||
| return url + (url.indexOf('?') >= 0 ? "&" : "?") + q; |
There was a problem hiding this comment.
Insert query parameters before URL fragments
When a request path contains a fragment and also supplies queryParameters, this concatenates the query after #; for example, /items#section becomes /items#section?q=x. The query is then part of the fragment and is not sent to the server, so construct the query before the fragment while preserving the fragment at the end.
Useful? React with 👍 / 👎.
| if (toJson != null && !toJson.isStatic && toJson.params.isEmpty() && !c.isMixin | ||
| && (toJson.returnType == null || !(toJson.returnType.is("int") || toJson.returnType.is("double") | ||
| || toJson.returnType.is("bool") || toJson.returnType.is("void")))) { |
There was a problem hiding this comment.
Dispatch primitive-returning toJson methods
When a class declares a valid zero-argument toJson returning int, double, or bool, this condition excludes it from JsonEncodable. jsonEncode(instance) consequently throws JsonUnsupportedObjectError instead of encoding the primitive returned by toJson; generate a boxed bridge or otherwise dispatch every non-void toJson regardless of its declared return type.
Useful? React with 👍 / 👎.
| } | ||
| } | ||
| } | ||
| fs.delete(path); |
There was a problem hiding this comment.
Reject non-recursive deletion of non-empty directories
When Directory.deleteSync() is called without recursive: true on a directory containing children, this falls through to the storage deletion without checking that the directory is empty. The in-memory backend removes only the directory entry and leaves orphaned children, while platform deletion may silently fail, yet the method reports success in both cases; Dart instead throws FileSystemException, so reject a non-recursive delete before calling fs.delete when fs.list(path) is non-empty.
Useful? React with 👍 / 👎.
| public static FirebaseAuth instanceFor(FirebaseApp app, Object persistence) { | ||
| return instance; |
There was a problem hiding this comment.
Bind FirebaseAuth instances to their requested app
When an application calls FirebaseAuth.instanceFor for a secondary FirebaseApp, this returns the default singleton and discards the supplied app. The transport later reads only [DEFAULT]'s API key, and the user/session state is also shared, so authentication intended for a second Firebase project is sent to the wrong project and can expose the default project's signed-in user; maintain an auth instance and credentials namespace per app.
Useful? React with 👍 / 👎.
| if (name != null) { | ||
| body.put("displayName", name); | ||
| } | ||
| if (photo != null) { | ||
| body.put("photoUrl", photo); | ||
| } |
There was a problem hiding this comment.
Send profile-field deletions to Firebase
When updateDisplayName(null) or updatePhotoURL(null) is used to clear a profile field, these conditions omit the field from the REST request, which means the remote account retains its previous value. The success callback nevertheless clears the local User, leaving client and server state inconsistent; send Firebase's explicit delete attribute for null fields rather than treating null as no update.
Useful? React with 👍 / 👎.
- Dyn: hand-written sign; ParparVM's JavaAPI has no Math/Long.signum, so the Linux and Windows gallery builds failed to link. - FirebaseMessaging no longer names com.codename1.push: any reference turns push on in an Android build, which then requires google-services.json even for apps without Firebase. The generated entry point supplies the push key only when firebase_messaging is used. - StreamIterator: checking the queue and publishing the waiter is now atomic; an event pushed between the two from a timer thread was lost and an async* body hung (about 1 in 60 runs under load). - Accept the gallery's JavaScript size growth for this PR (scripts/flutter-bench/baseline/pr/6055.json). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f78e349063
ℹ️ 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".
| public Future<RemoteMessage> getInitialMessage() { | ||
| return Future.value(null); |
There was a problem hiding this comment.
Preserve cold-start messages for getInitialMessage
When a notification tap launches a terminated app, pushReceived publishes the message to the broadcast OPENED stream before Dart code necessarily installs a listener, so that event can be dropped. Since getInitialMessage() then always returns null, the standard Firebase Messaging cold-start flow can never recover the message or route the user; retain the launch message and return it here.
Useful? React with 👍 / 👎.
| public Future<Object> reload() { | ||
| return Future.value(null); |
There was a problem hiding this comment.
Refresh account data in User.reload
When a user follows an email-verification link and the app calls await user.reload(), this completes successfully without fetching the updated account record. Because emailVerified is never assigned anywhere else in this implementation, it remains false permanently and common verification gates continue rejecting the user; reload the account through the authenticated Firebase lookup endpoint and update the local fields.
Useful? React with 👍 / 👎.
| ((Future<Object>) to).then(answered, new Funcs.VoidFunc1<Object>() { | ||
| @Override | ||
| public void call(Object e) { | ||
| then.call(loc); |
There was a problem hiding this comment.
Do not navigate after failed async redirects
When an asynchronous redirect used as an auth guard completes with an error, this handler treats the failure as if no redirect were requested and continues to the original location. push() then constructs and displays that route, so a transient authentication or network failure can open guarded content instead of surfacing the routing error; propagate the error or route it through errorBuilder rather than continuing with loc.
Useful? React with 👍 / 👎.
| return AsyncStar.asyncStar(new Funcs.VoidFunc1<AsyncStar.Sink<R>>() { | ||
| @Override | ||
| public void call(AsyncStar.Sink<R> s) { | ||
| final StreamIterator<T> it = new StreamIterator<T>(self); |
There was a problem hiding this comment.
Keep derived streams subscribed after source errors
When a source emits an error and later continues with data, every derive-based operator—including take, skip, expand, and asyncMap—reads it through StreamIterator, whose error path enqueues DONE and cancels its subscription. The generated stream therefore emits the first error and closes, dropping all later events, whereas Dart stream transformations forward source errors and remain subscribed; use a non-canceling error-forwarding subscription for these operators.
Useful? React with 👍 / 👎.
| snapshot = new AsyncSnapshot(ConnectionState.none, snapshot.data(), snapshot.error(), | ||
| snapshot.stackTrace()); |
There was a problem hiding this comment.
Clear stale errors when StreamBuilder changes streams
When a StreamBuilder replaces a stream after the old stream emitted an error, this carries that error and stack trace into both the disconnected and waiting snapshots for the new stream. Builders that prioritize snapshot.hasError therefore keep rendering the old failure instead of the new stream's loading state until it emits; preserve the last data but clear the old error when disconnecting and subscribing.
Useful? React with 👍 / 👎.
| case "toInt": | ||
| return Long.valueOf(dbl ? (long) d : n.longValue()); |
There was a problem hiding this comment.
Reject non-finite dynamic toInt calls
When a dynamic receiver contains double.nan or an infinity, this Java cast returns 0 or a saturated long instead of throwing Dart's UnsupportedError. Code that validates or converts calculated dynamic values can consequently accept a fabricated integer; route the double case through the existing checked DartRuntime.toInt implementation.
Useful? React with 👍 / 👎.
| while (i < s.length() && Character.isDigit(s.charAt(i))) { | ||
| i++; |
There was a problem hiding this comment.
Restrict JSON number digits to ASCII
When an otherwise numeric token contains Unicode decimal digits, such as 1٢, Character.isDigit consumes them and Long.parseLong accepts them, so this decoder returns 12. JSON permits only ASCII 0–9, and Dart's decoder rejects this document with FormatException; use an explicit ASCII digit predicate throughout number parsing.
Useful? React with 👍 / 👎.
| BlocBase<?> b = explicitBloc(); | ||
| if (b == null) { | ||
| Class<?> type = blocType(); | ||
| Object found = context().providerValueOfType(type == null ? BlocBase.class : type, false); |
There was a problem hiding this comment.
Resubscribe when the provided bloc changes
When an ancestor BlocProvider replaces its bloc while the BlocBuilder, BlocListener, BlocConsumer, or BlocSelector widget itself remains in place, this non-listening lookup registers no provider dependency and didUpdateWidget sees no explicit-bloc change. The state therefore remains subscribed to the old, potentially disposed bloc and never observes the replacement; track provider dependency changes and release/rebind the subscription when the resolved bloc changes.
Useful? React with 👍 / 👎.
| Long t = times.get(key(path)); | ||
| return t == null ? 0 : t.longValue(); |
There was a problem hiding this comment.
Fail lastModified for missing files
When File.lastModifiedSync() or lastModified() targets a missing file in the headless/in-memory backend, this returns zero and the public API reports the Unix epoch as a valid modification time. Dart instead throws or completes with a FileSystemException, so callers cannot distinguish a missing file from a real epoch-dated file; check that the file exists before returning its timestamp.
Useful? React with 👍 / 👎.
|
Compared 236 screenshots: 236 matched. |
|
Compared 176 screenshots: 176 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
… a test-harness timeout - Icons are static methods rather than 2,176 eagerly built fields, and dynamic dispatch switch cases are pruned to member names the program reaches through dynamic receivers, so ParparVM's dead-code removal can drop the rest. - Derived streams (take/skip/expand/asyncMap...) forward source errors and stay subscribed; failed async GoRouter redirects show the error page; User.reload refreshes the account; getInitialMessage returns the cold-start push; bloc widgets rebind when the provided bloc changes; dynamic toInt/floor/ceil reject non-finite values; JSON numbers are ASCII digits; lastModified fails on a missing file. - The behavior harness kills a generated program after a timeout instead of hanging the suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f5d6c0991
ℹ️ 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".
| return; | ||
| } | ||
| @SuppressWarnings("unchecked") | ||
| StreamIterator<T> it = new StreamIterator<T>((Stream<T>) source); |
There was a problem hiding this comment.
Forward yield-star errors without terminating the generator
When an async* generator uses yield* on a stream that emits an error followed by more data, this plain StreamIterator subscribes with cancelOnError: true and makes next() throw on the error. AsyncStar.run then emits that error and closes the entire generator, losing both subsequent source events and statements after yield*. Delegation should forward error events and continue until the source finishes; use an error-forwarding iterator, as Stream.derive already does, rather than the terminating iterator used for await for.
Useful? React with 👍 / 👎.
| try { | ||
| while (it.next()) { |
There was a problem hiding this comment.
Cancel the delegated subscription when its listener cancels
When a listener cancels an async* stream while yield* is waiting for an idle source, the sink only sets cancelled; this iterator is not registered with onCancel, so its pending next() never wakes and the finally block cannot cancel the upstream subscription. This leaves upstream resources subscribed and prevents generator cleanup from running indefinitely if the source emits nothing further (and leaves a non-daemon timer thread blocked in headless execution). Register the delegated iterator with the sink's cancellation hooks so cancellation releases the pending wait and unwinds the generator.
Useful? React with 👍 / 👎.
| req.setUrl(ro.url()); | ||
| req.setHttpMethod(ro.method); | ||
| req.setPost(payload != null); | ||
| req.setFailSilently(true); |
There was a problem hiding this comment.
Complete request futures on transport failures
With Codename One initialized, a DNS failure, TLS error, or socket timeout leaves the Dio request future pending indefinitely. ConnectionRequest.performOperationComplete() schedules postResponse() only after normal completion, while NetworkManager merely logs transport exceptions when failSilently is true, bypassing this request's handleException() override altogether. Consequently neither the error interceptors nor the request completer are reached. Settle the request through an exception path that actually runs instead of deferring failure handling to postResponse(); the same pattern also needs correction in FirebaseAuth.request() and HttpClientRequest.close().
Useful? React with 👍 / 👎.
| if (files instanceof List) { | ||
| for (Object f : (List<?>) files) { | ||
| if (f instanceof XFile) { | ||
| path = ((XFile) f).path(); |
There was a problem hiding this comment.
Materialize in-memory XFiles before sharing them
When an app shares an export created with XFile.fromData(bytes, name: ...), XFile holds the bytes only in memory and defaults its path to an empty string. Extracting only path() here therefore passes an empty attachment path to Display.share, without ever writing or reading the export bytes, so the recipient cannot receive the generated file. The instance API has the same problem through ShareParams.firstFilePath(). Materialize data-backed XFiles into temporary files before passing their paths to the platform share sheet.
Useful? React with 👍 / 👎.
| } else if (v instanceof Enum) { | ||
| // Not encodable in Dart either without toJson; name it in the error. | ||
| throw new JsonUnsupportedObjectError(v, "Converting object to an encodable object failed"); |
There was a problem hiding this comment.
Apply custom JSON encoders to enum values
When jsonEncode or JsonEncoder receives an enum and a toEncodable callback, this branch throws before the callback can run. For example, jsonEncode(Status.ready, toEncodable: (_) => 'ready') should produce a JSON string but instead raises JsonUnsupportedObjectError; enums nested in maps or lists fail the same way. Since the transpiler emits Dart enums as Java enums, route these values through the existing custom-conversion fallback and reject them only when no usable conversion is available.
Useful? React with 👍 / 👎.
yield* forwards source errors and releases the source on cancel; Dio, FirebaseAuth and HttpClient settle transport failures instead of leaving the request pending; in-memory XFiles are written out before sharing; jsonEncode lets toEncodable/toJson encode enums. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The whole flutter-runtime jar, all 19 plugin packs, was merged into every app. The Android builder's class scan then saw geolocator/maps/firebase classes and added play-services-location plus location, audio and boot permissions to apps that use none of them (+762 KB dex on the gallery). Executor.unzip now drops every pack nothing outside the plugin tree references, keeping transitive pack dependencies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 91ff218dff
ℹ️ 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".
| if (samePath(location, initialLocation)) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
Reevaluate redirects when navigating to the initial location
When the initial location redirects elsewhere, this early return prevents subsequent navigation back to that location from resolving its current destination. For example, if / initially redirects an unauthenticated user to /login, calling go('/') after signing in simply exposes the original login page again without invoking the redirect or rebuilding the route. It also leaves state describing the previous navigation. Resolve the requested location before deciding whether the existing base page can be reused.
Useful? React with 👍 / 👎.
| if (autoUncompress) { | ||
| r.headers().set(HttpHeaders.acceptEncodingHeader, "gzip"); | ||
| } |
There was a problem hiding this comment.
Decompress gzip responses when autoUncompress is enabled
On the JavaSE simulator, a server honoring the default Accept-Encoding: gzip header returns compressed bytes, but HttpClientRequest.readResponse() copies those bytes directly into the response without decompression. The inspected JavaSEPort.openInputStream() only buffers the underlying HTTP stream; gzip handling is provided separately by GZConnectionRequest. Consequently ordinary UTF-8 or JSON response decoding fails for gzip-enabled servers despite autoUncompress being true. Decode compressed responses when enabled, accounting for platforms that already decompress them.
Useful? React with 👍 / 👎.
| private <R> Stream<R> derive(final Funcs.VoidFunc2<StreamIterator<T>, AsyncStar.Sink<R>> body) { | ||
| final Stream<T> self = this; | ||
| return AsyncStar.asyncStar(new Funcs.VoidFunc1<AsyncStar.Sink<R>>() { | ||
| @Override | ||
| public void call(final AsyncStar.Sink<R> s) { |
There was a problem hiding this comment.
Subscribe derived streams before returning from listen
For a live broadcast source, operators using derive, such as take, skip, and asyncMap, lose events emitted immediately after the downstream listener attaches. AsyncStar.asyncStar() schedules its body on a timer, and the upstream subscription is established only when that body eventually calls StreamIterator.next(). Thus controller.stream().take(1).listen(...); controller.add(value); on the EDT drops value because the controller still has no listener, potentially leaving the result waiting indefinitely. Establish and buffer the upstream subscription synchronously during downstream registration rather than deferring registration with generator execution.
Useful? React with 👍 / 👎.
| case "contains": | ||
| return Boolean.valueOf(s.contains(String.valueOf(arg(a, 0)))); | ||
| case "startsWith": | ||
| return Boolean.valueOf(s.startsWith(String.valueOf(arg(a, 0)))); |
There was a problem hiding this comment.
Honor string search offsets on dynamic receivers
When a string is accessed through dynamic, these branches accept but discard the optional search offset. For example, dynamic s = 'abc'; s.startsWith('b', 1) returns false instead of true, while s.contains('a', 1) returns true instead of false; the indexOf branch below likewise always searches from zero. This silently changes parsing and matching results compared with statically typed calls. Forward the supplied offset to the existing range-checked DString overloads rather than ignoring it.
Useful? React with 👍 / 👎.
| // one onPopInvoked per request: didPop false for any refusal, a | ||
| // WillPopScope's included, as Flutter reports it | ||
| PopGuards.notifyPopScopes(scopes, pop, result); |
There was a problem hiding this comment.
Notify PopScope callbacks for explicit pops
When an application closes a page with Navigator.pop(context, result), its PopScope.onPopInvokedWithResult or onPopInvoked callback never runs: the only notification call is here inside maybePop, while the explicit pop path removes the page without notifying its scopes. Although explicit pops correctly bypass canPop, they still need to report the successful pop and its result, as the new PopScope contract promises. Put successful-pop notification in the actual pop path and avoid notifying it twice through maybePop, so callback-based cleanup and result handling also run for programmatic navigation.
Useful? React with 👍 / 👎.
| public dart.core.DartUri uri() { | ||
| return dart.core.DartUri.parse(path.startsWith("/") ? "file://" + path : path); |
There was a problem hiding this comment.
Escape filesystem paths before constructing their URIs
For filenames containing URI delimiters or literal percent escapes, parsing the raw path changes which file the URI represents. For example, File('/tmp/a#b').uri treats b as a fragment, so passing it to File.fromUri throws instead of recovering the original path; /tmp/a%20b instead round-trips to /tmp/a b, a different filename. The same implementation serves directories. Construct the URI from escaped filesystem path components so literal #, ?, and % characters remain part of the filename.
Useful? React with 👍 / 👎.
- Dyn: contains/startsWith/indexOf forward the optional offset. - FileSystemEntity.uri escapes the path, so '#', '?' and '%' stay in names. - Navigator: an explicit pop notifies PopScope (didPop true); maybePop reports only refusals, so a pop is never reported twice. - GoRouter.go runs redirects even for the initial location. - HttpClient gunzips gzip responses under autoUncompress; contentLength is the declared Content-Length (-1 if absent or decompressed). - Derived streams (take/skip/asyncMap...) subscribe to the source inside listen(), so events right after listen are not lost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
AndroidGradleBuilder overrides unzip, so the prune in Executor.unzip never ran on Android: every plugin pack reached the permission scan, and FirebaseMessaging's com/codename1/push reference switched FCM on, failing the gallery build on a missing google-services.json. Drop pr/6055.json: the latest run's JavaScript and Linux sizes are within tolerance of the current rows (JavaScript once #5976's overlay is re-based, on branch fix-5976-flutter-baseline), so nothing needs rebaselining. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#5976 merged after the nightly fold had already moved javascript gallery/code_bytes to 13,304,997, so its overlay's 'from' (13,238,731) no longer matched and every baseline read failed: master's Performance baselines check and every PR's Flutter benchmark gate. Its measured value (13,338,532) is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 2d83e24)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e6b4a09)
…n app - FirebaseMessaging no longer names com.codename1.push: topic calls go through a topicSink hook the import-generated entry point installs, as tokenSource already did. Pruning keeps the whole firebase pack for an auth-only app, and that reference turned on FCM (and required google-services.json) on Android. NoPushReferenceTest guards it. - User keeps the FirebaseAuth that created or restored it, so a secondary app's token refresh, profile update, reload and delete use that app's key and session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Compared 179 screenshots: 179 matched. Benchmark Results
Detailed Performance Metrics
ParparVM vs HotSpot (JDK 25): macos-arm64The performance gate could not run: the gate's own unit tests (test_perf_gate.py) failed. See |
|
Compared 176 screenshots: 176 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
Rows master had already moved made it unreadable; same repair as 3702c6c on parley-messaging, so the two branches merge cleanly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Build time:
- Importer: a package importing an unsupported one is unsupported too
('depends on X'), so its files never reach the build.
- Generated $dcall/$dset convert arguments through DartRuntime.as
(instanceof, Dart TypeError) instead of bare casts, which ParparVM does
not check. Explicit setters and generic methods are now dispatched.
- Plugin pruning also reads library jars in libs/: a pack only a cn1lib
calls is kept.
Runtime:
- AsyncStar.over is the shared transformer plumbing: LineSplitter and
utf8/base64 bind subscribe inside listen() and forward source errors
without ending the stream, as Stream.derive already did.
- Sink.onCancel and the cancel handler share one lock (a cancel between
check and add left the hook unrun).
- Dyn join/padLeft/compareTo and base64.decode check their String casts.
- GoogleMap's camera poll stops after ~500 ms still and on unmount.
- ImagePicker's single-pick fallback propagates errors.
- HttpClientResponse.contentLength is -1 for any gzip body under
autoUncompress, including one iOS already inflated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Transpiler: a[i]++ through the index write (old value for postfix), dynamic compound assignment evaluates receiver/key once, left operand pinned when the right lifts statements, unary -/~ operator overloads, generic local functions erased. Plugins: SharedPreferences mock values stay off Storage, held cold-start pushes replayed to a late onMessageOpenedApp listener. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bloc: close() completes live emitters (cancels forEach/onEach), a handler's synchronous error goes to onError and is reported instead of thrown at add's caller, on<E> handler and Emitter.forEach/onEach typed so block handlers and emit.forEach bind statically. Transpiler: a block lambda with no value return targeting a dynamic-returning function answers null. connectivity: events delivered on the EDT, listener registered once Display is up. path_provider: separate temp and cache subdirectories. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A block lambda emitted with a null tail is value-shaped, so then()/catchError pick the value SAM (block-bodied then callbacks failed to compile). Local sync*/async* functions lowered like generator closures, with their declared element type. await for over a stream of unknown element type reads it as Stream<Object>. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Compared 193 screenshots: 193 matched. Benchmark ResultsDetailed Performance Metrics
ParparVM vs HotSpot (JDK 25): Windows x64Runner CPU: AMD64 Family 25 Model 1 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 193 screenshots: 193 matched. Benchmark ResultsDetailed Performance Metrics
|
|
Compared 193 screenshots: 193 matched. ParparVM vs HotSpot (JDK 25): Linux x64Runner CPU: AMD EPYC 9V45 96-Core Processor (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 193 screenshots: 193 matched. ParparVM vs HotSpot (JDK 25): Linux arm64Runner CPU: Neoverse-N2 (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 193 screenshots: 193 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 |
The runtime jar no longer carries plugin pack classes. Packs ship as sources under META-INF/dart/java and the transpiler copies in only those the generated code or the app's own Java names (transitively), sweeping any it no longer selects; the mojo also drops their stale classes. The Executor and AndroidGradleBuilder class-scan prune is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ordinary Flutter apps should now import and build with no edits. The approach is hybrid: pure-Dart pub packages are transpiled from source, and native plugins get Java stub packs.
Language (dart-transpiler)
async*/await for, mixin application classes, and redirecting constructors and factories.onclauses, generator and generic closures, super assignment, and>>>.dart:convert, dynamic dispatch, arrow factories, and mixed list literals.Runtime (dart-runtime)
dart:iosubset: File, Directory, Platform, HttpClient, and WebSocket.Import (build-engine)
package_config.jsonare transpiled from the pub cache.Plugin stubs (flutter-runtime)
Widgets
Acceptance
scripts/flutter-apps:pet_adoption: dio, shared_preferences, provider.field_notes: firebase, notifications, share, connectivity.run-acceptance.shimports each app, transpiles it, and compiles the result.flutter-acceptance.ymlruns it in CI.Testing
🤖 Generated with Claude Code