Skip to content

Run Unity 2D projects as Codename One applications - #5976

Merged
shai-almog merged 77 commits into
masterfrom
unity-compat
Oct 21, 2026
Merged

shai-almog merged 77 commits into
masterfrom
unity-compat

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Summary

A Unity 2D project (C# scripts, scenes, prefabs, sprites, project settings) dropped into common/src/main/unity, or imported with cn1:import-unity-project, builds unmodified as a Codename One application for every target. No Unity player and no .NET runtime ship with the app.

  • maven/cil-translator: reads the assembly the C# compiler produced and writes one class file per C# type; compiles scenes, prefabs, animator controllers, clips, tile maps and particle systems to reflection-free Java (UnityAppImpl).
  • maven/unity-compat: the runtime. A .NET base library in Java, the UnityEngine API over com.codename1.gaming (2D rendering, Box2D physics with exact casts and overlap queries, Animator, tile maps, particles, uGUI, touch, audio, PlayerPrefs, Resources), plus our own implementations of the documented TextMesh Pro and Cinemachine API.
  • Build wiring: UnityProjectBuilder in build-engine, the compile-unity and import-unity-project Maven goals, the Gradle compileUnity task, the archetype profile, and release publishing behind -Dunity-compat (the modules need the .NET SDK; the framework build does not).
  • Java interop: UnityApplication (createForm, onProjectInstalled, onStarted, callInFrame), so a game sits inside a Codename One form and uses the rest of the framework.
  • Docs: a new developer guide chapter, Unity-Interop.asciidoc.

Clean room: no Unity source, assemblies or package source were read or shipped; the stub UnityEngine.dll is compiled from our own sources.

Changes outside the new modules

Bugs this work uncovered, fixed at the root, each with a regression test:

  • ParparVM JavaScript: wide stack shuffles (POP2/DUP2/DUP_X2), MULTIANEWARRAY dimension pops, a call result compared via FCMP/DCMP evaluated more than once, SWAP losing the lower value.
  • ParparVM native: arraycopy/clone of an array of primitive arrays took the primitive path.
  • GameView: a key repeat released a held key after 800 ms; a focused hosted child kept the multi-touch arrays from the view.
  • Box2D: chain against chain threw in World.popContact; the broad phase allocated a merge buffer every step above 31 pairs (now an in-place sort, checked against Arrays.sort over randomized pair sets).

Behavior changes that affect every application:

  • Native builds compile generated C with -ffp-contract=off.
  • The JavaScript target rounds every float result with Math.fround.

Both make float arithmetic match the JVM bit for bit. Their performance cost on ordinary applications has not been measured.

Verification

  • Seven in-tree samples (scripts/unity-compat-samples) produce byte-identical traces on the JVM, ParparVM native and JavaScript (maven/integration-tests/unity-compat-test.sh --parparvm).
  • Five open-source Unity games build unmodified with identical traces on all three targets: Fruitopia, Asteroids, Pong, Snake, Sokoban. They are not in the tree.
  • cil-translator and unity-compat: SpotBugs 0, Checkstyle 0, PMD forbidden list clean, cast-semantics clean.
  • Core unit tests: 7,702 run, 1 failure (MapViewMarkerLabelTest timeout on a loaded machine; passes alone).
  • Developer guide gates pass locally (asciidoctor, Vale, LanguageTool, snippets, xrefs).

Not verified

  • .github/workflows/unity-compat.yml and the release.yml changes have never run on CI.
  • Nothing has run on a real device; audio, sign-in and share from the bridge samples are untested there.
  • The Java samples in the guide are copies of code that ran, but docs CI does not compile them.
  • The tree was rebased onto current master after the last local test run.
  • Core GameInput still reads edge flags that a press landing mid-frame can miss on ports that render off the EDT; left for a separate decision.

🤖 Generated with Claude Code

A Unity project's Assets and ProjectSettings in common/src/main/unity build
unmodified: the .NET SDK compiles the C# scripts, maven/cil-translator turns
the assembly into Java class files and compiles every scene, prefab,
animator controller, clip, tile map and particle system to reflection-free
Java, and maven/unity-compat is the runtime they run against (a .NET base
library, the UnityEngine API over com.codename1.gaming, and our own
TextMesh Pro and Cinemachine implementations). Maven (compile-unity,
import-unity-project) and Gradle (compileUnity) share UnityProjectBuilder.
Both modules sit behind the unity-compat profile and are published by the
release.

Float results have to match C# on every target, so native builds get
-ffp-contract=off and the JavaScript target rounds float results with
Math.fround.

Bugs this uncovered and fixes outside the new modules, each with a test:
- ParparVM JavaScript: wide stack shuffles, MULTIANEWARRAY dimension pops,
  a call result compared with FCMP/DCMP evaluated more than once, SWAP
  losing the lower value.
- ParparVM native: arraycopy/clone of an array of primitive arrays took
  the primitive path.
- GameView: a key repeat released a held key; a focused hosted child kept
  the multi-touch arrays from the view.
- Box2D: chain against chain threw in World.popContact; the broad phase
  allocated a merge buffer on every step above 31 pairs.

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

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 56ms / native 5ms = 11.2x speedup
SIMD float-mul (64K x300) java 62ms / native 5ms = 12.4x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 74.000 ms
Base64 CN1 decode 94.000 ms
Base64 SIMD encode 108.000 ms
Base64 encode ratio (SIMD/CN1) 1.459x (45.9% slower)
Base64 SIMD decode 104.000 ms
Base64 decode ratio (SIMD/CN1) 1.106x (10.6% slower)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 4.000 ms
Image createMask ratio (SIMD on/off) 0.500x (50.0% faster)
Image applyMask (SIMD off) 25.000 ms
Image applyMask (SIMD on) 29.000 ms
Image applyMask ratio (SIMD on/off) 1.160x (16.0% slower)
Image modifyAlpha (SIMD off) 29.000 ms
Image modifyAlpha (SIMD on) 22.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.759x (24.1% faster)
Image modifyAlpha removeColor (SIMD off) 25.000 ms
Image modifyAlpha removeColor (SIMD on) 15.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.600x (40.0% faster)

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 178 screenshots: 178 matched.

Native Android coverage

  • 📊 Line coverage: 17.40% (30679/176345 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 15.70% (144888/923101), branch 10.66% (10013/93896), complexity 8.22% (7449/90604), method 12.01% (5157/42930), class 20.74% (1059/5105)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • com.codename1.androidcompat.rt.com.codename1.androidcompat.rt.TextXmlPullParser – 0.00% (0/623 lines covered)
      • com.codename1.androidcompat.androidx.constraintlayout.core.widgets.com.codename1.androidcompat.androidx.constraintlayout.core.widgets.Flow – 0.00% (0/576 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 17.40% (30679/176345 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 15.70% (144888/923101), branch 10.66% (10013/93896), complexity 8.22% (7449/90604), method 12.01% (5157/42930), class 20.74% (1059/5105)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6367 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.ClassReader – 0.00% (0/1524 lines covered)
      • org.jacoco.agent.rt.internal_0e20598.asm.org.jacoco.agent.rt.internal_0e20598.asm.MethodWriter – 0.00% (0/922 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/736 lines covered)
      • com.google.common.cache.com.google.common.cache.LocalCache$Segment – 0.00% (0/726 lines covered)
      • okio.okio.Buffer – 0.00% (0/687 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/625 lines covered)
      • com.codename1.androidcompat.rt.com.codename1.androidcompat.rt.TextXmlPullParser – 0.00% (0/623 lines covered)
      • com.codename1.androidcompat.androidx.constraintlayout.core.widgets.com.codename1.androidcompat.androidx.constraintlayout.core.widgets.Flow – 0.00% (0/576 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 319ms / native 131ms = 2.4x speedup
SIMD float-mul (64K x300) java 132ms / native 121ms = 1.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 85.000 ms
Base64 CN1 decode 84.000 ms
Base64 native encode 421.000 ms
Base64 encode ratio (CN1/native) 0.202x (79.8% faster)
Base64 native decode 266.000 ms
Base64 decode ratio (CN1/native) 0.316x (68.4% faster)
Image encode benchmark status skipped (SIMD unsupported)

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@github-actions

github-actions Bot commented Oct 9, 2026 •

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)
    • ✅ android-compat: 0 findings (no issues)
    • ✅ android-res-compiler: 0 findings (no issues)
    • ✅ backend: 0 findings (no issues)
    • ✅ backend-test: 0 findings (no issues)
    • ✅ build-engine: 0 findings (no issues)
    • ✅ build-hint-catalog: 0 findings (no issues)
    • ✅ build-hint-tools: 0 findings (no issues)
    • ✅ codenameone-gradle-plugin: 0 findings (no issues)
    • ✅ codenameone-maven-plugin: 0 findings (no issues)
    • ✅ core-unittests: 0 findings (no issues)
    • ✅ ios: 0 findings (no issues)
    • ✅ javac: 0 findings (no issues)
    • ✅ project-model: 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 Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 56ms / native 4ms = 14.0x speedup
SIMD float-mul (64K x300) java 56ms / native 4ms = 14.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 78.000 ms
Base64 CN1 decode 94.000 ms
Base64 SIMD encode 120.000 ms
Base64 encode ratio (SIMD/CN1) 1.538x (53.8% slower)
Base64 SIMD decode 105.000 ms
Base64 decode ratio (SIMD/CN1) 1.117x (11.7% slower)
Image encode benchmark iterations 100
Image createMask (SIMD off) 14.000 ms
Image createMask (SIMD on) 9.000 ms
Image createMask ratio (SIMD on/off) 0.643x (35.7% faster)
Image applyMask (SIMD off) 48.000 ms
Image applyMask (SIMD on) 48.000 ms
Image applyMask ratio (SIMD on/off) 1.000x (0.0% slower)
Image modifyAlpha (SIMD off) 40.000 ms
Image modifyAlpha (SIMD on) 21.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.525x (47.5% faster)
Image modifyAlpha removeColor (SIMD off) 30.000 ms
Image modifyAlpha removeColor (SIMD on) 17.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.567x (43.3% faster)

ParparVM vs HotSpot (JDK 25): windows-x64

The performance gate could not run: the gate's own unit tests (test_perf_gate.py) failed. See perf-build.log in this job's artifacts.

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

ParparVM vs HotSpot (JDK 25): linux-x64

The performance gate could not run: the gate's own unit tests (test_perf_gate.py) failed. See perf-build.log in this job's artifacts.

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

ParparVM vs HotSpot (JDK 25): linux-arm64

The performance gate could not run: the gate's own unit tests (test_perf_gate.py) failed. See perf-build.log in this job's artifacts.

@shai-almog

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 193 screenshots: 193 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 49ms / native 3ms = 16.3x speedup
SIMD float-mul (64K x300) java 48ms / native 2ms = 24.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 60.000 ms
Base64 CN1 decode 69.000 ms
Base64 SIMD encode 78.000 ms
Base64 encode ratio (SIMD/CN1) 1.300x (30.0% slower)
Base64 SIMD decode 69.000 ms
Base64 decode ratio (SIMD/CN1) 1.000x (0.0% slower)
Image encode benchmark iterations 100
Image createMask (SIMD off) 5.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.400x (60.0% faster)
Image applyMask (SIMD off) 20.000 ms
Image applyMask (SIMD on) 20.000 ms
Image applyMask ratio (SIMD on/off) 1.000x (0.0% slower)
Image modifyAlpha (SIMD off) 18.000 ms
Image modifyAlpha (SIMD on) 13.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.722x (27.8% faster)
Image modifyAlpha removeColor (SIMD off) 22.000 ms
Image modifyAlpha removeColor (SIMD on) 13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.591x (40.9% faster)

ParparVM vs HotSpot (JDK 25): windows-arm64

The performance gate could not run: the gate's own unit tests (test_perf_gate.py) failed. See perf-build.log in this job's artifacts.

@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-20T18:01:10.064107Z 1ac2c49 Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • ✅ Tests: 879 total, 0 failed, 57 skipped

Benchmark Results

  • Execution Time: 5791 ms

  • Hotspots (Top 20 sampled methods):

    • 8.24% com.codename1.tools.translator.IteratorEscape.ctorOnlyStoresParamsIntoThis (77 samples)
    • 7.91% java.util.ArrayList.indexOf (74 samples)
    • 5.88% java.lang.System.identityHashCode (55 samples)
    • 4.60% java.lang.Object.hashCode (43 samples)
    • 4.28% com.codename1.tools.translator.BytecodeMethod.equals (40 samples)
    • 3.32% java.lang.StringBuilder.append (31 samples)
    • 2.89% com.codename1.tools.translator.BytecodeMethod.updateInlinableFieldDependencies (27 samples)
    • 2.25% com.codename1.tools.translator.IteratorEscape.mangle (21 samples)
    • 2.03% com.codename1.tools.translator.JavascriptReachability.enqueueResolved (19 samples)
    • 1.82% com.codename1.tools.translator.Parser.cullMethods (17 samples)
    • 1.60% java.lang.String.equals (15 samples)
    • 1.50% com.codename1.tools.translator.IteratorEscape.walk (14 samples)
    • 1.50% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (14 samples)
    • 1.39% com.codename1.tools.translator.classfile.analysis.Frame.execute (13 samples)
    • 1.39% java.util.HashMap.hash (13 samples)
    • 1.39% java.util.AbstractCollection.containsAll (13 samples)
    • 1.39% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (13 samples)
    • 1.07% com.codename1.tools.translator.ByteCodeClass.generateCCode (10 samples)
    • 1.07% com.codename1.tools.translator.ReachabilityCull.run (10 samples)
    • 1.07% com.codename1.tools.translator.DeadFieldElimination.run (10 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@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: 5639acb341

ℹ️ 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 on lines +102 to +104
UnityRuntime.begin();

view = new UnityGameView();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Install view services before waking the scene

Calling UnityRuntime.begin() before the UnityGameView is created and initialized runs every script's Awake/OnEnable while PlayerPrefs still uses its transient in-memory fallback and AudioSource has no output. Consequently, saved preferences read in Awake appear missing, writes made there disappear once the store is installed, and every scene AudioSource with Play On Awake marks itself playing without ever sending audio to the device. Initialize these view-backed services before waking the first scene.

Useful? React with 👍 / 👎.

Comment on lines +92 to +93
if (negative) {
value = -value;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve Int32.MinValue as a valid parse result

For the valid input "-2147483648", negating the scanned magnitude produces Long.MIN_VALUE, which is also used as the invalid-input sentinel. Parse therefore throws FormatException, and TryParse returns false, instead of producing Integer.MIN_VALUE. Use a distinct parse-status indicator so the minimum Int32 remains representable.

Useful? React with 👍 / 👎.

Comment on lines +1539 to +1540
c.resumeAt = 0f;
c.phase = 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Defer null yields until the following frame

When StartCoroutine is called from Start or Update, it immediately advances the iterator and adds it with phase == 0 and resumeAt == 0; the same frame's later runCoroutines(0) then advances it again. Thus a first yield return null resumes in the current frame rather than the next frame as promised, so initialization or frame-based loops can execute twice in one frame. Record the frame at which a null/default yield may next resume.

Useful? React with 👍 / 👎.

Comment on lines +86 to +87
public static float get_timeSinceLevelLoad() {
return time;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reset timeSinceLevelLoad when changing scenes

get_timeSinceLevelLoad() returns the application-wide Time.time, but changeScene() never resets that clock. After loading a second scene, scripts therefore see time since application startup rather than time since the current scene was loaded, which breaks scene-local timers and spawn schedules. Track a separate scene-load timestamp or reset a dedicated level clock on every load.

Useful? React with 👍 / 👎.

Comment on lines +51 to +52
public static boolean get_isMobilePlatform() {
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Report mobile platforms on device builds

Application.isMobilePlatform is hard-coded to false, including when the generated application runs on Android or iOS. Unity scripts commonly use this property to enable touch controls, mobile layouts, or device-specific behavior, so those paths remain disabled on the very device builds this compatibility layer targets. Have the host install or derive the actual platform value instead of returning a constant.

Useful? React with 👍 / 👎.

Comment on lines 147 to +148
xvfb-run -a mvn deploy -Psign-artifacts -Dgpg.passphrase=$MAVEN_GPG_PASSPHRASE \
-Plocal-dev-javase -Darchetype.test.skip=true -DskipTests \
-Plocal-dev-javase -Dunity-compat -Darchetype.test.skip=true -DskipTests \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Version the profile-only modules before release deployment

The release step first runs update-version.sh, whose mvn versions:set invocation does not activate unity-compat, and only activates this profile in the later deploy command. These two modules are therefore absent from the version-update reactor and retain their explicit 8.0-SNAPSHOT parent and artifact versions; once the root POM has the tag version, activating them for deploy produces a parent-version mismatch or publishes under snapshot coordinates instead of the tag. Run the version update with this profile active or explicitly update these two POMs before deployment.

Useful? React with 👍 / 👎.

Comment on lines +1026 to +1027
mv.visitJumpInsn(IFNE, ok);
invoke(INVOKESTATIC, Names.INTEROP, "divideByZero", "()V", false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject signed division overflow before emitting JVM division

The generated check covers only a zero divisor. For int.MinValue / -1 and long.MinValue / -1, CIL signed division must throw OverflowException, while JVM IDIV/LDIV silently returns the minimum value. Translated C# therefore continues with a corrupt arithmetic result and cannot catch the expected exception; add the minimum-value/negative-one check before emitting the division opcode.

Useful? React with 👍 / 👎.

Comment on lines +60 to +61
public static int Abs(int v) {
return v < 0 ? -v : v;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Throw on Math.Abs of the minimum signed value

System.Math.Abs(int.MinValue) must throw OverflowException, because the positive magnitude is not representable, but negating it here wraps and returns the same negative value; the long overload has the same defect. Scripts using the documented System.Math surface can therefore propagate a negative value from an operation guaranteed to be nonnegative. Detect the minimum values and throw the runtime's OverflowException before negating.

Useful? React with 👍 / 👎.

shai-almog and others added 2 commits October 9, 2026 19:55
… perf baseline

On the Ubuntu runner dotnet is linked into /usr/bin, so dropping every PATH
directory that holds it also dropped env, bash and mvn. The directory is now
shadowed by links to everything in it except dotnet.

objectAllocation RAM on Neoverse-N2 is bimodal; the row is recalibrated from
this run and three master runs with a wider RAM tolerance. No improvement is
claimed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review findings (rationale for the ones left alone is in comments at the
code concerned):
- Install the view's services (PlayerPrefs store, audio, platform) before
  the first scene wakes.
- A coroutine started from Start or Update resumes its first null yield on
  the next frame.
- Time.timeSinceLevelLoad restarts on a scene load.
- Application.platform and isMobilePlatform come from the host platform.
- Signed division and remainder of MinValue by -1 throw OverflowException,
  as does System.Math.Abs of MinValue.

JavaScript screenshots: XYChart rotated the canvas and rotated it back,
which with float-rounded angles leaves a transform 1e-14 off identity, and
the JavaScript port draws DOM text only for an exact identity. XYChart and
ChartComponent now restore the transform they were handed, and the port's
DOM text decision tolerates that residue.

A differential fixture against the JVM found four older JavaScript target
bugs: int to float/double read an un-normalized int, float/double to int
wrapped instead of saturating, a static read before its first write was
undefined, and Float.toString printed the double.

The offscreen check now runs on every scene sample in the integration
test. It found: a flick release delivered at the press point because
CodenameOneImplementation aliased the shared pointer arrays, GameView
missing single finger presses after the first drag, and a sub-image cache
keyed by corner only.

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

The JavaScript target now truncates a float product the way the JVM does, so
the six theme screens that size from one move by a pixel; the goldens are this
run's CI captures. JsNumericDifferentialApp compares narrowing compound
assignments against the JVM on purpose and joins the other compiler fixtures
CodeQL does not read.

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

shai-almog commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

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

…lLoad

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

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 171 screenshots: 171 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

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

Benchmark Results

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

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 58ms / native 5ms = 11.6x speedup
SIMD float-mul (64K x300) java 53ms / native 3ms = 17.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 59.000 ms
Base64 CN1 decode 57.000 ms
Base64 native encode 660.000 ms
Base64 encode ratio (CN1/native) 0.089x (91.1% faster)
Base64 native decode 279.000 ms
Base64 decode ratio (CN1/native) 0.204x (79.6% faster)
Base64 SIMD encode 58.000 ms
Base64 encode ratio (SIMD/CN1) 0.983x (1.7% faster)
Base64 SIMD decode 45.000 ms
Base64 decode ratio (SIMD/CN1) 0.789x (21.1% faster)
Base64 encode ratio (SIMD/native) 0.088x (91.2% faster)
Base64 decode ratio (SIMD/native) 0.161x (83.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 13.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.231x (76.9% faster)
Image applyMask (SIMD off) 64.000 ms
Image applyMask (SIMD on) 60.000 ms
Image applyMask ratio (SIMD on/off) 0.938x (6.3% faster)
Image modifyAlpha (SIMD off) 68.000 ms
Image modifyAlpha (SIMD on) 61.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.897x (10.3% faster)
Image modifyAlpha removeColor (SIMD off) 66.000 ms
Image modifyAlpha removeColor (SIMD on) 54.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.818x (18.2% faster)

@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

Compared 236 screenshots: 236 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@shai-almog

Copy link
Copy Markdown
Collaborator Author

@codex review

@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

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

Benchmark Results

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

Build and Run Timing

Metric Duration
Simulator Boot 94000 ms
Simulator Boot (Run) 1000 ms
App Install 20000 ms
App Launch 2000 ms
Test Execution 552000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 124ms / native 3ms = 41.3x speedup
SIMD float-mul (64K x300) java 143ms / native 35ms = 4.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 79.000 ms
Base64 CN1 decode 119.000 ms
Base64 native encode 568.000 ms
Base64 encode ratio (CN1/native) 0.139x (86.1% faster)
Base64 native decode 399.000 ms
Base64 decode ratio (CN1/native) 0.298x (70.2% faster)
Base64 SIMD encode 71.000 ms
Base64 encode ratio (SIMD/CN1) 0.899x (10.1% faster)
Base64 SIMD decode 80.000 ms
Base64 decode ratio (SIMD/CN1) 0.672x (32.8% faster)
Base64 encode ratio (SIMD/native) 0.125x (87.5% faster)
Base64 decode ratio (SIMD/native) 0.201x (79.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 6.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.333x (66.7% faster)
Image applyMask (SIMD off) 64.000 ms
Image applyMask (SIMD on) 56.000 ms
Image applyMask ratio (SIMD on/off) 0.875x (12.5% faster)
Image modifyAlpha (SIMD off) 178.000 ms
Image modifyAlpha (SIMD on) 212.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.191x (19.1% slower)
Image modifyAlpha removeColor (SIMD off) 503.000 ms
Image modifyAlpha removeColor (SIMD on) 97.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.193x (80.7% faster)

@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: 8a51fcb4b1

ℹ️ 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 on lines +87 to +88
public static boolean Any(IEnumerable_1 source, Class t) {
return walk(source).MoveNext();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Dispose enumerators used by LINQ terminal operators

When the source is a custom enumerator or a compiler-generated iterator with a finally block, terminal operations that stop early—such as this Any, First, Contains, ElementAt, and Take—abandon the enumerator without calling Dispose(), even though IEnumerator_1 implements IDisposable. The iterator's cleanup therefore never runs after an early result or predicate exception; wrap enumeration in try/finally and dispose the enumerator on every exit path.

Useful? React with 👍 / 👎.

Comment on lines +94 to +98
public static Object GetValues(Type enumType) {
Type t = enumType(enumType);
int[] out = new int[t.values.length];
for (int i = 0; i < out.length; i++) {
out[i] = (int) t.values[i];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Return arrays matching the enum's underlying type

For any enum whose underlying type is not int—for example, enum E : long { Huge = 1L << 40 }—Enum.GetValues always returns an int[] and truncates every member. Translated callers cast the result to the enum's normalized JVM array type (for this example, long[]), so the usual (E[])Enum.GetValues(typeof(E)) either fails with a cast error or receives corrupted values. Construct the primitive array corresponding to the enum's recorded underlying type instead.

Useful? React with 👍 / 👎.

Comment on lines +100 to +104
for (int i = 1; i < out.length; i++) {
int v = out[i];
int j = i - 1;
while (j >= 0 && out[j] > v) {
out[j + 1] = out[j];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Sort enum values by unsigned magnitude

When an enum contains negative members, such as the common flags value All = -1, this signed comparison places that member before zero. Enum.GetValues orders members by the unsigned magnitude of their binary representation, so { All = -1, None = 0, One = 1 } must enumerate as None, One, All, whereas this code returns All, None, One. Use an unsigned comparison appropriate to the enum's underlying width.

Useful? React with 👍 / 👎.

Comment on lines +139 to +143
} else if (c == '}') {
if (i + 1 < n && format.charAt(i + 1) == '}') {
i++;
}
sb.append('}');

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject unmatched closing braces in String.Format

For a malformed composite format such as "value }", .NET throws FormatException, but this branch appends the unmatched brace and returns normally. This changes control flow for scripts that validate or recover from user-provided format strings; only a doubled }} should emit a literal closing brace, while a standalone } must be rejected.

Useful? React with 👍 / 👎.

Comment on lines +55 to +59
public static Type $of(Class type, String fullName) {
Type t = (Type) byClass.get(type);
if (t == null) {
t = new Type(fullName, type, null, null, false);
byClass.put(type, t);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep primitive System.Type instances distinct

The cache key is only the JVM Class, but multiple distinct .NET primitives share one box class: int and uint both use Integer.class, long and ulong use Long.class, and byte and sbyte use Byte.class. Consequently, whichever type is requested first is returned for both, making typeof(int) == typeof(uint) true and potentially reporting the wrong FullName. Include the .NET type identity in the cache key rather than keying solely by the erased Java class.

Useful? React with 👍 / 👎.

Comment on lines +43 to +45
static long number(Object value) {
if (value instanceof EnumBox) {
return ((EnumBox) value).value;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve enum type identity in reflective operations

This conversion discards the EnumBox.type, so APIs such as Enum.IsDefined, Enum.GetName, HasFlag, and CompareTo accept a boxed member of an unrelated enum and operate only on its numeric value. For example, firstEnum.HasFlag(secondEnum) can return true when their bits overlap, whereas .NET throws ArgumentException for differing enum types. Validate the box's enum type against the requested or receiver type before extracting its number.

Useful? React with 👍 / 👎.

Comment on lines +230 to +232
public void set_time(float value) {
startedAt = Time.unscaledTime - value;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Seek the audio output when setting AudioSource.time

When a playing source executes audio.time = value, this setter only changes the runtime's bookkeeping and never seeks the device's Media, so the audible clip continues from its old position while scripts observe a different position. The bookkeeping is also immediately wrong when pitch != 1, because get_time() multiplies elapsed time by pitch but this setter does not divide the requested clip position by pitch. Add a seek operation to AudioOutput and derive startedAt consistently with the playback rate.

Useful? React with 👍 / 👎.

Comment on lines +360 to +362
public static String Trim(String s) {
return s.trim();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Trim the full .NET whitespace set

String.trim() only removes characters at or below U+0020, whereas System.String.Trim() removes Unicode whitespace. Inputs surrounded by characters such as non-breaking space (U+00A0) or em space (U+2003) therefore remain untrimmed, even though this runtime already recognizes them in Char_.IsWhiteSpace. Scan the ends using the .NET whitespace predicate instead of delegating to Java's trim().

Useful? React with 👍 / 👎.

Comment on lines +121 to +128
String wanted = value.trim();
for (int i = 0; i < t.names.length; i++) {
if (ignoreCase ? t.names[i].equalsIgnoreCase(wanted) : t.names[i].equals(wanted)) {
return new EnumBox(t, t.values[i]);
}
}
try {
return new EnumBox(t, Long.parseLong(wanted));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Parse comma-separated enum member names

Enum.Parse accepts a comma-separated list of member names, which is the standard way to parse flags such as "Read, Write"; this implementation only checks the entire string against one member and then attempts numeric parsing. Valid combined values therefore throw ArgumentException. Split on commas, trim each name, resolve each with the requested case sensitivity, and combine their underlying values.

Useful? React with 👍 / 👎.

Comment on lines +57 to +60
if (t instanceof NegativeArraySizeException) {
return new OverflowException();
}
return t;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Wrap Java argument errors for translated catch blocks

Several public runtime APIs throw java.lang.IllegalArgumentException for invalid C# calls, including PlayerPrefs, SceneManager, Input, physics queries, and Instantiate, but this dispatcher leaves that throwable unchanged. Because generated catch clauses test against the custom System.Exception hierarchy, even catch (Exception) cannot catch these failures and the raw JVM exception escapes the script. Throw the corresponding compatibility exception at each API or map Java argument exceptions here.

Useful? React with 👍 / 👎.

@shai-almog

shai-almog commented Oct 10, 2026 •

Copy link
Copy Markdown
Collaborator Author

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

Benchmark Results

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

Build and Run Timing

Metric Duration
Simulator Boot 89000 ms
Simulator Boot (Run) 1000 ms
App Install 15000 ms
App Launch 2000 ms
Test Execution 451000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 90ms / native 4ms = 22.5x speedup
SIMD float-mul (64K x300) java 267ms / native 3ms = 89.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 77.000 ms
Base64 CN1 decode 125.000 ms
Base64 native encode 743.000 ms
Base64 encode ratio (CN1/native) 0.104x (89.6% faster)
Base64 native decode 461.000 ms
Base64 decode ratio (CN1/native) 0.271x (72.9% faster)
Base64 SIMD encode 89.000 ms
Base64 encode ratio (SIMD/CN1) 1.156x (15.6% slower)
Base64 SIMD decode 57.000 ms
Base64 decode ratio (SIMD/CN1) 0.456x (54.4% faster)
Base64 encode ratio (SIMD/native) 0.120x (88.0% faster)
Base64 decode ratio (SIMD/native) 0.124x (87.6% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 9.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.333x (66.7% faster)
Image applyMask (SIMD off) 52.000 ms
Image applyMask (SIMD on) 65.000 ms
Image applyMask ratio (SIMD on/off) 1.250x (25.0% slower)
Image modifyAlpha (SIMD off) 79.000 ms
Image modifyAlpha (SIMD on) 24.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.304x (69.6% faster)
Image modifyAlpha removeColor (SIMD off) 36.000 ms
Image modifyAlpha removeColor (SIMD on) 27.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.750x (25.0% faster)

…seline

Base library: LINQ operators dispose their enumerators on every exit, Enum
values keep their underlying type and unsigned order, Enum.Parse takes comma
lists, HasFlag/CompareTo/IsDefined refuse another enum's member, String.Format
rejects a lone closing brace, Trim follows Char.IsWhiteSpace, typeof(int) and
typeof(uint) are distinct, a float key's two zeros are one key, and a Java
exception thrown by the runtime is caught by a script's catch (Exception).
AudioSource.time seeks the device's media and accounts for pitch.

The macOS draw-gradient golden is this branch's CI capture: with
-ffp-contract=off the mutable-image ellipse edge now equals the on-screen one.

Linux x64 perf baseline rows recalibrated per runner CPU from PR and master
runs; the shift is present on master at the base commit.

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: f0abf10218

ℹ️ 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 on lines +931 to +934
Image image = (Image) paintedTexts.get(key);
if (image == null) {
image = paint(d, width, height);
paintedTexts.put(key, image);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Bound the cache for dynamically changing text

When a Text component displays a changing value such as a timer, score, or frame counter, every distinct string creates an Image and stores it permanently in paintedTexts; the map is never cleared or evicted. A frequently updated label therefore retains thousands of raster images and can exhaust device memory during an otherwise normal game session. Keep only current/recent entries or use a bounded cache.

Useful? React with 👍 / 👎.

Comment on lines +1578 to +1582
if (yielded instanceof WaitForSeconds) {
c.resumeAt = Time.time + ((WaitForSeconds) yielded).seconds;
} else if (yielded instanceof Coroutine) {
c.waitingFor = (Coroutine) yielded;
} else if (yielded instanceof WaitForFixedUpdate) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Execute yielded IEnumerator instances before resuming parents

When a coroutine uses the common yield return ChildRoutine() form, the yielded value is an IEnumerator, but this chain recognizes only a started Coroutine; it consequently treats the child iterator like null, never executes it, and resumes the parent on the next frame. Add nested-enumerator handling so the parent waits while the yielded iterator is driven to completion.

Useful? React with 👍 / 👎.

Comment on lines +69 to +70
public static float get_unscaledDeltaTime() {
return unscaledDeltaTime;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Return the fixed unscaled step during FixedUpdate

When Time.unscaledDeltaTime is read from FixedUpdate, Unity reports the unscaled duration of that fixed step, but this getter always returns the enclosing render frame's dt. At nonmatching render/physics rates—or when several fixed steps run in one frame—fixed-update timers and movement therefore advance by the render delta once per physics step, producing speed and scheduling errors.

Useful? React with 👍 / 👎.

Comment on lines +572 to +575
if (f.isDirectory()) {
collectStamped(f, prefix + f.getName() + "/", out);
} else {
out.add(prefix + f.getName() + ":" + f.length() + ":" + f.lastModified());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include file contents in the Unity build digest

In the new Unity incremental build cache, a source or asset rewritten with the same byte length and preserved modification time—such as after restoring or synchronizing files with timestamps intact—produces the same stamp even if its contents changed. run() then accepts state.txt and skips compilation, leaving stale translated classes or resources in the application; hash the file bytes rather than only name, size, and timestamp.

Useful? React with 👍 / 👎.

Comment on lines +39 to +44
public List_1() {
items = new Object[4];
}

public List_1(int capacity) {
items = new Object[capacity < 4 ? 4 : capacity];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Implement the IEnumerable List constructor

When valid C# uses new List<T>(source)—including the new List<int>(numbers) array case explicitly anticipated by ArrayView—the translator emits a call to the erased List_1(IEnumerable_1) constructor, but this runtime class exposes only the parameterless and capacity constructors. The Unity project therefore fails translation with a missing runtime method instead of constructing the list; add the enumerable constructor and dispose its enumerator after copying.

Useful? React with 👍 / 👎.

Comment on lines +850 to +855
String simple = meta.getName().substring(0, meta.getName().length() - ".cs.meta".length());
for (CilAssembly assembly : scripts) {
for (TypeDef t : assembly.types()) {
String full = t.fullName();
if (full.equals(simple) || full.endsWith("." + simple)) {
scriptsByGuid.put(guid, t);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve same-named scripts by their source file

When a valid Unity project contains two scripts with the same filename in different folders and namespaces, each .cs.meta GUID is mapped by simple class name and this loop always selects the first matching TypeDef. Both GUIDs can therefore resolve to the same script class, causing scene objects to receive the wrong behaviour and serialized fields from the other script to be dropped. Use source/PDB information or another unambiguous file-to-type mapping.

Useful? React with 👍 / 👎.

Comment on lines +733 to +738
GameObject go = (GameObject) objects.get(i);
if (go.destroyed || !go.activeInHierarchy()) {
continue;
}
if ((name != null && !go.name.equals(name)) || (tag != null && !go.tag.equals(tag))) {
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Traverse hierarchy paths in GameObject.Find

When a script calls GameObject.Find("Canvas/Panel/Button"), Unity interprets the slash-separated argument as a hierarchy path, but this implementation compares the whole argument only against each object's leaf name. Such lookups always return null unless an object literally contains slashes in its name, breaking a standard way scripts locate nested scene objects; resolve the root and traverse children as the existing Transform.Find implementation does.

Useful? React with 👍 / 👎.

Comment on lines +204 to +208
public java.lang.Object GetComponentInChildren(Class type) {
if (!active) {
return null;
}
java.lang.Object c = GetComponent(type);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Search the inactive root in child component queries

When GetComponentInChildren is invoked on an inactive GameObject, Unity still searches that root object even though the one-argument overload excludes its inactive descendants, but this early return skips the root as well. Managers querying an inactive prefab or temporarily disabled object therefore receive null despite the requested component being attached directly to it; GetComponentsInChildren has the same issue through collect, so both traversals should always inspect their starting object.

Useful? React with 👍 / 👎.

Comment on lines +62 to +64
public static boolean IsDigit(char c) {
return c >= '0' && c <= '9';
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Recognize Unicode decimal digits in Char.IsDigit

For decimal digits outside ASCII, such as Arabic-Indic ٣ or full-width 5, System.Char.IsDigit must return true because they have the Unicode decimal-digit category, but this implementation accepts only 0 through 9. Translated validation and parsing code therefore rejects digit strings that work in .NET; use a Unicode digit-category check rather than an ASCII range.

Useful? React with 👍 / 👎.

@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: 925e929975

ℹ️ 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".

wsy = (a - b) / len;
wcos = m00 / len;
wsin = m10 / len;
wrot = (float) (com.codename1.unitycompat.system.Math.Atan2(m10, m00) * RAD2DEG);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve rotation independently of the affine basis

When a parent has non-uniform scale and a child has local rotation, deriving wrot from the composed matrix column makes scale alter the reported world rotation. For example, parent scale (2,1) and child rotation 45° produce atan2(0.707, 1.414) = 26.565°, although the child's Unity world rotation remains 45°. Consequently rotation, eulerAngles, right, up, and TransformDirection all return incorrect values in this common hierarchy; retain the quaternion/angle composition separately from the affine basis used by TransformPoint.

Useful? React with 👍 / 👎.

Comment on lines +147 to +148
float min = minNormalAngle < maxNormalAngle ? minNormalAngle : maxNormalAngle;
float max = minNormalAngle < maxNormalAngle ? maxNormalAngle : minNormalAngle;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Honor infinite normal-angle bounds

When a filter uses infinity for a normal-angle bound, this comparison leaves the infinity literal in the range instead of applying Unity's documented sentinel behavior: either sign of infinity means the lowest possible value for minNormalAngle and the highest possible value for maxNormalAngle (minimum, maximum). For example, SetNormalAngle(+Infinity, +Infinity) should admit the full angular range, but here every finite angle is below min and is filtered out; normalize each bound according to whether it is the minimum or maximum before testing.

Useful? React with 👍 / 👎.

Comment on lines +88 to +90
sb.append(sb.length() == 0 ? "" : ", ").append(real(key.get("time"), "0")).append(", ")
.append(real(value, "0")).append(", ").append(real(in, "0")).append(", ")
.append(real(out, "0"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve weighted animation tangents

When an animation key uses weightedMode In, Out, or Both with a non-default inWeight or outWeight, this emits only its time, value, and slopes, so AnimationClip.sample evaluates it as an ordinary unweighted Hermite segment. Unity uses those weights when calculating the affected curve segments (WeightedMode documentation), so importing such a clip silently changes its animated positions, scales, colors, or other properties; serialize and evaluate the mode and weights rather than discarding them.

Useful? React with 👍 / 👎.

Comment on lines +2611 to +2612
} else if ("2".equals(mode)) {
passed = nodes.get(fileId(arguments.get("m_ObjectArgument")));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve asset arguments for persistent event calls

When an Inspector-configured persistent listener passes an asset such as an AudioClip, sprite, or prefab through its object argument, m_ObjectArgument contains an external GUID/file ID rather than a node in the current scene graph. Looking it up only in nodes therefore produces null, and the generated listener passes null even though these asset types are supported elsewhere by the compiler; resolve this reference through the normal asset lookup path before emitting the call.

Useful? React with 👍 / 👎.

Comment on lines +2624 to +2628
if (one) {
CilType param = types.storage(m.sig.params[0]);
if (wanted != null ? param.kind != wanted : types.isStruct(param)
|| universe.definitionOf(param) == null) {
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Match persistent-call overloads by argument type

When a persistent listener targets overloaded public methods that each accept a different reference type, this accepts whichever one appears first in metadata without consulting m_ObjectArgumentAssemblyTypeName or checking the serialized argument's assignability. For example, if Handle(AudioClip) precedes the Inspector-selected Handle(GameObject), the compiler binds the former and later substitutes null for the incompatible GameObject, invoking the wrong overload; use the recorded argument type to select the method before generating the call.

Useful? React with 👍 / 👎.

…rmal-angle bounds, persistent calls pick the recorded overload and resolve asset arguments; weighted-tangent build warning

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: 2c6481936b

ℹ️ 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 on lines +2743 to +2744
if (key.startsWith("m_") || "serializedVersion".equals(key)) {
continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve user fields before skipping m_ keys

When a script declares a serialized field with the common m_ prefix, such as [SerializeField] private int m_health, Unity writes that exact key into the scene or prefab YAML, but this branch discards it before attempting findField. The generated scene therefore leaves the field at its initializer/default value without warning; skip only the known MonoBehaviour metadata keys, or try resolving a serialized script field first.

Useful? React with 👍 / 👎.

Comment on lines +3552 to +3554
line(sb, v + ".$setup(" + f(offset.get("x"), "0") + ", " + f(offset.get("y"), "0") + ", "
+ f(offset.get("z"), "-10") + ", " + f(p.get("m_XDamping"), "1") + ", "
+ f(p.get("m_YDamping"), "1") + ");");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the transposer binding mode

When a scene uses CinemachineTransposer with a target-relative m_BindingMode such as LockToTarget, this setup serializes only the offset and damping, so the runtime always adds the raw offset in world axes. Rotating the Follow target therefore leaves the camera on the same world side instead of rotating it around the target, contrary to the documented binding-mode behavior; pass the serialized mode through and interpret the offset in its selected coordinate space.

Useful? React with 👍 / 👎.

int n = UnityRuntime.$sceneCount();
for (int i = 0; i < n; i++) {
String path = UnityRuntime.$scenePath(i);
if (path.equals(sceneName) || name(path).equals(sceneName)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Accept Unity's scene-path spellings

When the build contains Assets/Scenes/Level.unity, calls such as LoadScene("Assets/Scenes/Level") or LoadScene("level") fail these exact comparisons and throw, even though Unity accepts a full build-settings path without the .unity extension and compares scene names case-insensitively (Unity documentation). Strip the extension from the stored path and compare both supported forms without regard to case.

Useful? React with 👍 / 👎.

…ing mode turns the offset with the target, LoadScene accepts extensionless paths and any case

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: 2a174f00f5

ℹ️ 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".


UnityPomUpdater(String pom) {
String p = pom;
if (p.indexOf("<artifactId>codenameone-unity-compat</artifactId>") < 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Distinguish managed declarations from runtime dependencies

When a legacy common POM already mentions codenameone-unity-compat only in <dependencyManagement> (or even in a comment), this substring check treats the runtime as installed and skips adding the active profile. The import then reports success, but CompileUnityMojo cannot find the runtime in project.getArtifacts(), so the imported Unity project fails to build. Parse the dependency sections and require an effective dependency rather than any textual occurrence.

Useful? React with 👍 / 👎.

Comment on lines +721 to +722
if (output != null) {
output.play(v, c, level(volumeScale), pitch, false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clamp negative one-shot volume scales

When a script calls PlayOneShot(clip, -1f), this passes a negative gain to AudioOutput and saves it for later volume updates, even though that interface requires a 0–1 volume. Unity automatically clamps negative volumeScale values to zero, so the call should produce silence rather than backend-dependent behavior such as an invalid or inverted gain; clamp the scale before playing and retaining it. Unity AudioSource.PlayOneShot documentation

Useful? React with 👍 / 👎.

Comment on lines +1892 to +1894
for (Object removed : list(modification.get("m_RemovedComponents"))) {
source.removeAll(targets(source, removed));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Apply removed GameObject prefab overrides

When a prefab instance has a nonempty m_RemovedGameObjects list, this expansion applies removed components but never removes the listed GameObjects, so the compiled scene retains the deleted child together with its renderers, colliders, and scripts. Unity defines this list as the source-prefab GameObjects removed from this particular instance; resolve each entry and remove that object's whole subtree from source. Unity YAML prefab serialization documentation

Useful? React with 👍 / 👎.

Comment on lines +383 to +395
if (c == '\\' && i + 1 < s.length()) {
char n = s.charAt(++i);
int digits = n == 'u' ? 4 : n == 'x' ? 2 : 0;
if (digits > 0 && i + digits < s.length()) {
try {
sb.append((char) Integer.parseInt(s.substring(i + 1, i + 1 + digits), 16));
i += digits;
continue;
} catch (NumberFormatException e) {
// Not an escape after all; keep the letter.
}
}
sb.append(n == 'n' ? '\n' : n == 't' ? '\t' : n == 'r' ? '\r' : n);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Decode all UnityYAML escape sequences

When a serialized string contains a YAML escape other than n, t, r, x, or u, this fallback silently drops the backslash and keeps the escape letter; for example, "a\bb" becomes "abb" instead of containing a backspace, and \U0001F600 becomes literal text. UnityYAML supports double-quoted scalars, and YAML 1.1 requires escapes such as \0, \a, \b, \v, \f, \e, and eight-digit \U to decode to their Unicode values, so serialized script strings are otherwise corrupted during compilation. UnityYAML format, YAML 1.1 escape sequences

Useful? React with 👍 / 👎.

…very YAML escape decodes, negative one-shot volume is silence; the importer ignores a runtime named only in comments or dependency management

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: 7379c3b8f0

ℹ️ 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 on lines +822 to +823
return (f.flags & FIELD_ACCESS_MASK) == FIELD_PUBLIC
|| type.assembly.attributes(f.token()).contains("UnityEngine.SerializeField");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exclude unsupported types from serialized field copies

When a public or [SerializeField] field has an unsupported type such as int[][], this predicate still returns true. Fresh evidence after the earlier serialization-rule fix is that only field modifiers are checked, even though Unity does not serialize jagged arrays, dictionaries, or nested containers (Unity serialization rules); emitSerializedCopies consequently clones any SZARRAY and shallow-copies nested arrays during Instantiate, causing clones to copy and share state that Unity would leave uninitialized. Include recursive field-type eligibility in this predicate.

Useful? React with 👍 / 👎.

…ponent walks pointer listeners by index (ComponentTest ConcurrentModificationException)

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: bbacea63c6

ℹ️ 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 on lines +122 to +123
File stale = new File(target, path);
if (stale.isFile() && stale.delete()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject symlinked parents during stale cleanup

When a previously imported directory is removed from the source and its destination is replaced with a symlink, a recorded child such as Assets/old/file.txt reaches this cleanup without passing through copy(). isFile() follows the linked parent and delete() then removes the external target file. The new exact-destination link check in copy() therefore does not close this path; validate every stale path's ancestors or resolve and verify containment before deleting.

Useful? React with 👍 / 👎.

Comment on lines +749 to +750
for (int i = 0; i < list.size(); i++) {
if (!(list.get(i) instanceof NativeTextSelectionListener)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Snapshot listeners before inspecting them

When another thread removes a listener after list.size() succeeds but before list.get(i), this indexed traversal can still throw IndexOutOfBoundsException; removing an earlier element can also make it skip an application listener. Since EventDispatcher exposes its mutable ArrayList and synchronizes mutations on the dispatcher, take a snapshot or traverse while holding that same lock rather than replacing one unsafe iterator with another unsynchronized traversal.

Useful? React with 👍 / 👎.

…der; Component's listener walk documents why it takes no lock

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: 7ea5d73e28

ℹ️ 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 (rest < duration) {
break;
}
rest -= duration;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard the cycle-remainder loop against float stagnation

When a looping particle system has a tiny positive duration such as 1e-20f, a normal frame leaves rest around 0.016f, and rest -= duration rounds back to exactly the same float. The loop condition therefore remains true forever and blocks the frame thread. Use a direct remainder calculation or apply the same no-progress guard already used in the emission-segment loop above.

Useful? React with 👍 / 👎.

Comment on lines +228 to +229
if (v > 709.78) {
return 1.0 / 0.0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve finite results near the top of Math.Exp

When translated code calls Math.Exp with an input between 709.78 and the actual overflow boundary, such as 709.781, this branch returns positive infinity even though the correct result is still finite (approximately 1.7946e308). This also affects non-integral Math.Pow calls that delegate to Exp; use the true boundary near 709.782712893384 or let the scaling calculation determine whether the result overflows.

Useful? React with 👍 / 👎.

// A record is only ever written by install(), but it is a file
// in the build directory: a line that climbs out of the classes
// directory is not followed.
if (rel.length() > 0 && !rel.contains("..")) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Permit harmless double dots in cleanup records

When a used Unity image has a legal name such as icon..png and the Maven module later removes src/main/unity, its installed path contains .. but no parent-directory component. This condition skips it, so removeOutput() deletes the record and work tree while leaving the image in target/classes, causing subsequent packages to continue shipping a stale asset with no record left to remove it. Reject exact .. path components rather than every occurrence of the substring.

Useful? React with 👍 / 👎.


public void StopCoroutine(Coroutine routine) {
if (routine != null) {
routine.done = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict StopCoroutine to the receiving behaviour

When one script obtains a Coroutine started by another behaviour and calls StopCoroutine on its own instance, this assignment stops the other behaviour's coroutine because it never checks routine.owner. Unity's StopCoroutine contract only stops a coroutine running on the receiving behaviour, so cross-component coroutine handles can unexpectedly cancel unrelated work; ignore or reject handles whose owner is not this.

Useful? React with 👍 / 👎.

…th.Exp keeps finite results to the true boundary, cleanup records reject only a .. component, StopCoroutine stops only its own coroutines

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: 98f5be03aa

ℹ️ 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 on lines +967 to +968
if (!installed.contains(rel)) {
deleteQuietly(new File(classesOut, rel));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate stale install records before deleting files

When target/unity/installed.txt is edited or restored from a poisoned build cache with an entry such as ../../pom.xml, this stale-output loop resolves the entry relative to classesOut and deletes the file outside the classes directory. removeOutput() validates the same record with climbs(rel), but this install path does not; reject parent components or verify canonical containment before deleting recorded paths.

Useful? React with 👍 / 👎.

shai-almog and others added 2 commits October 20, 2026 20:22
…ectory are ignored when reinstalling

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

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 1ac2c493ab

ℹ️ 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".

shai-almog and others added 2 commits October 21, 2026 07:00
…t resolve instead of failing the Hugo build; the gate's check tests run on a throwaway tree, not the repository's overlays

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shai-almog
shai-almog merged commit c550e7b into master Oct 21, 2026
110 of 132 checks passed
@shai-almog
shai-almog deleted the unity-compat branch October 21, 2026 11:45
shai-almog added a commit that referenced this pull request Oct 21, 2026
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>
shai-almog added a commit that referenced this pull request Oct 21, 2026
#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)
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