Skip to content

Fix BytecodeComplianceMojo skipping checks on recompiled classes#5216

Open
shai-almog wants to merge 1 commit into
masterfrom
fix/compliance-mojo-stale-rewrite
Open

Fix BytecodeComplianceMojo skipping checks on recompiled classes#5216
shai-almog wants to merge 1 commit into
masterfrom
fix/compliance-mojo-stale-rewrite

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Problem

BytecodeComplianceMojo.hasChangedSinceLastCheck() gated the compliance check purely on source mtimes vs the compliance_check.txt marker. But the check's invocation rewrites (String.replaceAll/replaceFirst/splitJdkApiRewriteHelper, added in #4893) mutate target/classes in place. When a later build recompiles those classes with unchanged sources (e.g. a second mvn package re-running javac), the rewrites are silently lost — and the source-only gate keeps skipping, so they are never re-applied.

The unrewritten bytecode then reaches ParparVM, which emits calls to virtual_java_lang_String_replaceAll___... that its JavaAPI never declares, and the iOS build fails deep inside xcodebuild:

error: call to undeclared function 'virtual_java_lang_String_replaceAll___java_lang_String_java_lang_String_R_java_lang_String'

(Hit in practice while iterating on local iOS builds of scripts/hellocodenameone; the only recovery was wiping common/target.)

A second hole in the same gate: after a failed check writes its violation report into the marker file, a rerun with unchanged sources skipped the check entirely — the build that had just failed would silently pass.

Fix

The skip is now only taken when, in addition to unchanged sources:

  • No compiled class is newer than the marker — checks .class files under the output directory and the target/kotlin-ic/compile/classes incremental Kotlin tree. The marker is written after the rewrites, so an untouched output tree still skips (no always-rerun perf regression), and the rewrites are idempotent when a re-run does happen.
  • The marker records a success — a failure report is not a completed check. The failure header is extracted to a shared constant so the report writer and the gate cannot drift apart.

Testing

  • 3 new unit tests in BytecodeComplianceMojoTest covering: recompiled classes invalidate the skip (and older classes do not), newer classes in the kotlin-ic tree invalidate the skip, and a previous failure report forces a re-run.
  • Full plugin suite: 177 tests, 0 failures.
  • End-to-end on scripts/hellocodenameone with the installed plugin: an unchanged second run still logs "Sources haven't changed since the last compliance check. Skipping check"; touching a compiled class triggers a full re-run instead of the silent skip.

🤖 Generated with Claude Code

The compliance skip-gate compared only source mtimes against the
compliance_check.txt marker. The check's invocation rewrites
(String.replaceAll/replaceFirst/split -> JdkApiRewriteHelper) mutate
target/classes in place, so a later compile pass that regenerates the
classes with unchanged sources silently sheds the rewrites and the gate
keeps skipping. The unrewritten bytecode then reaches ParparVM, which
emits calls to virtual_java_lang_String_replaceAll etc. that its
JavaAPI never declares, failing iOS builds deep inside xcodebuild.

The skip is now only taken when, in addition to unchanged sources:

- no .class under the output directory (or the kotlin-ic incremental
  output tree) is newer than the marker. The marker is written after
  the rewrites, so an untouched output tree still skips and there is
  no always-rerun regression; the rewrites themselves are idempotent.
- the marker records a success. Previously a rerun after a FAILED
  check skipped straight past the violations that had just failed the
  build. The failure header is now a shared constant so the report
  writer and the gate cannot drift apart.

Verified end-to-end on scripts/hellocodenameone: unchanged second run
still skips; touching a compiled class triggers a full re-run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shai-almog

shai-almog commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 128 screenshots: 128 matched.

Native Android coverage

  • 📊 Line coverage: 14.12% (8552/60567 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 11.42% (42161/369082), branch 4.96% (1720/34671), complexity 5.99% (1994/33263), method 10.40% (1619/15566), class 17.02% (373/2191)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 14.12% (8552/60567 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 11.42% (42161/369082), branch 4.96% (1720/34671), complexity 5.99% (1994/33263), method 10.40% (1619/15566), class 17.02% (373/2191)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 910.000 ms
Base64 CN1 encode 315.000 ms
Base64 encode ratio (CN1/native) 0.346x (65.4% faster)
Base64 native decode 1070.000 ms
Base64 CN1 decode 546.000 ms
Base64 decode ratio (CN1/native) 0.510x (49.0% faster)
Image encode benchmark status skipped (SIMD unsupported)

@github-actions

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@shai-almog

shai-almog commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 105 screenshots: 105 matched.
✅ Native Mac screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 118 seconds

@shai-almog

shai-almog commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 128 screenshots: 128 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 285 seconds

Build and Run Timing

Metric Duration
Simulator Boot 83000 ms
Simulator Boot (Run) 0 ms
App Install 14000 ms
App Launch 5000 ms
Test Execution 276000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 1234.000 ms
Base64 CN1 encode 1879.000 ms
Base64 encode ratio (CN1/native) 1.523x (52.3% slower)
Base64 native decode 463.000 ms
Base64 CN1 decode 1522.000 ms
Base64 decode ratio (CN1/native) 3.287x (228.7% slower)
Base64 SIMD encode 553.000 ms
Base64 encode ratio (SIMD/native) 0.448x (55.2% faster)
Base64 encode ratio (SIMD/CN1) 0.294x (70.6% faster)
Base64 SIMD decode 672.000 ms
Base64 decode ratio (SIMD/native) 1.451x (45.1% slower)
Base64 decode ratio (SIMD/CN1) 0.442x (55.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 87.000 ms
Image createMask (SIMD on) 10.000 ms
Image createMask ratio (SIMD on/off) 0.115x (88.5% faster)
Image applyMask (SIMD off) 227.000 ms
Image applyMask (SIMD on) 227.000 ms
Image applyMask ratio (SIMD on/off) 1.000x (0.0% slower)
Image modifyAlpha (SIMD off) 392.000 ms
Image modifyAlpha (SIMD on) 131.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.334x (66.6% faster)
Image modifyAlpha removeColor (SIMD off) 238.000 ms
Image modifyAlpha removeColor (SIMD on) 130.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.546x (45.4% faster)
Image PNG encode (SIMD off) 1696.000 ms
Image PNG encode (SIMD on) 1160.000 ms
Image PNG encode ratio (SIMD on/off) 0.684x (31.6% faster)
Image JPEG encode 689.000 ms

@shai-almog

shai-almog commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 124 screenshots: 124 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 238 seconds

Build and Run Timing

Metric Duration
Simulator Boot 86000 ms
Simulator Boot (Run) 1000 ms
App Install 14000 ms
App Launch 7000 ms
Test Execution 346000 ms

Detailed Performance Metrics

Metric Duration
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 native encode 806.000 ms
Base64 CN1 encode 1347.000 ms
Base64 encode ratio (CN1/native) 1.671x (67.1% slower)
Base64 native decode 346.000 ms
Base64 CN1 decode 1254.000 ms
Base64 decode ratio (CN1/native) 3.624x (262.4% slower)
Base64 SIMD encode 507.000 ms
Base64 encode ratio (SIMD/native) 0.629x (37.1% faster)
Base64 encode ratio (SIMD/CN1) 0.376x (62.4% faster)
Base64 SIMD decode 832.000 ms
Base64 decode ratio (SIMD/native) 2.405x (140.5% slower)
Base64 decode ratio (SIMD/CN1) 0.663x (33.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 78.000 ms
Image createMask (SIMD on) 12.000 ms
Image createMask ratio (SIMD on/off) 0.154x (84.6% faster)
Image applyMask (SIMD off) 189.000 ms
Image applyMask (SIMD on) 87.000 ms
Image applyMask ratio (SIMD on/off) 0.460x (54.0% faster)
Image modifyAlpha (SIMD off) 163.000 ms
Image modifyAlpha (SIMD on) 67.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.411x (58.9% faster)
Image modifyAlpha removeColor (SIMD off) 297.000 ms
Image modifyAlpha removeColor (SIMD on) 83.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.279x (72.1% faster)
Image PNG encode (SIMD off) 1150.000 ms
Image PNG encode (SIMD on) 911.000 ms
Image PNG encode ratio (SIMD on/off) 0.792x (20.8% faster)
Image JPEG encode 712.000 ms

@shai-almog

shai-almog commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 121 screenshots: 121 matched.
✅ JavaScript-port screenshot tests passed.

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.

1 participant