Skip to content

Preserve coroutine Job in generated async bridge R8 rules - #294

Merged
marcprux merged 4 commits into
mainfrom
codex/keep-async-bridge-job
Oct 11, 2026
Merged

marcprux merged 4 commits into
mainfrom
codex/keep-async-bridge-job

Conversation

@piercifani

@piercifani piercifani commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Since Skip 1.9.13, generated async bridge JNI lookups include Lkotlinx/coroutines/Job; in the method signature. In a minified app, R8 can rename that interface while the native signature remains unchanged. The lookup then fails, and the generated force unwrap crashes. Debug builds without minification do not expose the problem.

Change

Add -keep interface kotlinx.coroutines.Job to the shared defaultProguardContents generator used for app and framework ProGuard files. Extend the native-app initialization test to check the emitted rule.

The manual upgrade guidance is added directly to the existing ProGuard documentation in skiptools/skip.dev#26.

Related: skiptools/skip-bridge#85 (diagnostics for failed bridge lookups).

Validation

  • swift test --filter SkipCommandTests: 21 tests passed.
  • Standalone local JVM/JNI reproduction using R8 8.10.9-dev and kotlinx-coroutines 1.10.2, with rules extracted from the generator: without the new rule, Job mapped to kotlinx.coroutines.a and GetMethodID(..., "callback_fetch", "()Lkotlinx/coroutines/Job;") failed with NoSuchMethodError; with the rule, Job retained its name and the lookup succeeded. This exercised a real JNI lookup against R8 classfile output, not an Android-device end-to-end test.
  • git diff --check passed.

@cla-bot cla-bot Bot added the cla-signed label Oct 7, 2026

@marcprux marcprux left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ack! Proguard/R8 strikes again.

@marcprux

marcprux commented Oct 7, 2026

Copy link
Copy Markdown
Member

I wonder if the skip export command could someday auto-generate the right proguard-rules.pro (or an ancillary proguard-rules-skip.pro) file based on analysis of what functions/types need to be preserved for accessing through skip-bridge. That would have the potential to also drastically reduce the surface area of the skip.** packages that we already keep en masse (and thereby considerably reduce the overall app size).

piercifani and others added 2 commits October 8, 2026 11:14
Removed comment about Async bridge JNI signatures referencing kotlinx.coroutines.Job.
BridgedJob.cancel looks up kotlinx.coroutines.Job.cancel(CancellationException)
over JNI when the awaiting Swift task is cancelled. Keeping only the interface
name lets R8 rename or remove cancel, so the lookup fails and the force unwrap
crashes minified apps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@marcprux
marcprux merged commit eadfb7f into main Oct 11, 2026
6 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants