Follow-up to #133 (thanks for the quick turnaround on v0.5.158!). Shipping Bloom Jump to web uncovered another --target web bug not covered by that fix.
Perry version: 0.5.158 (built from source, b6d3cb9).
Symptom
A while loop inside a user function, where the loop variable is a function-scoped let initialized from a parameter, hangs on web — the loop never advances even though the increment statement is reached. The same code pattern works on native targets and works at the outer function scope in the same file.
Minimal repro
function parseSimple(s: string, start: number): number {
let i = start;
while (i < s.length) {
const c = s.charCodeAt(i);
if (c === 32 || c === 10 || c === 13) {
i = i + 1;
} else {
break;
}
}
return i;
}
const idx = parseSimple(" hello", 0);
console.log("stopped at " + idx.toString()); // native: 3. web: hangs.
With a 200-iteration guard, observed behavior is i stays at start forever — the i = i + 1 assignment doesn't persist across iterations.
Context / scope
- Same pattern in the caller function (a top-level
while (idx < dlen) { ...; idx = idx + 1.0; }) executes correctly.
- Substituting a module-level
const SCRATCH = [0] + SCRATCH[0] = SCRATCH[0] + 1.0 in place of i doesn't help — array-slot mutation doesn't persist either inside this nested loop.
- Switching the counter type (
let i = start | 0;) didn't help.
- Splitting the compound
|| condition into separate if statements didn't help.
- Collapsing the loop to a single condition (
if (c === 32) { i = i + 1; } else { break; }) still hangs.
Possibly related: the problem surfaced on parsing level files for Bloom Jump (parseNumberAt), which is invoked 5–10 times in a Pass-1 loop. If this is a single-entry-point optimizer issue, the function being called repeatedly could be relevant.
Workaround used
Rewrote the hot path to avoid the nested while-loop entirely by pre-slicing the substring and delegating to native parseInt. But parseInt on its own still needs a small while-loop to find the end-of-number, which also hangs — so currently blocked on having any level-load path working on web.
Happy to provide more instrumentation if useful; also willing to test a fix candidate directly against the Bloom Jump repro.
Follow-up to #133 (thanks for the quick turnaround on v0.5.158!). Shipping Bloom Jump to web uncovered another
--target webbug not covered by that fix.Perry version: 0.5.158 (built from source, b6d3cb9).
Symptom
A
whileloop inside a user function, where the loop variable is a function-scopedletinitialized from a parameter, hangs on web — the loop never advances even though the increment statement is reached. The same code pattern works on native targets and works at the outer function scope in the same file.Minimal repro
With a 200-iteration guard, observed behavior is
istays atstartforever — thei = i + 1assignment doesn't persist across iterations.Context / scope
while (idx < dlen) { ...; idx = idx + 1.0; }) executes correctly.const SCRATCH = [0]+SCRATCH[0] = SCRATCH[0] + 1.0in place ofidoesn't help — array-slot mutation doesn't persist either inside this nested loop.let i = start | 0;) didn't help.||condition into separateifstatements didn't help.if (c === 32) { i = i + 1; } else { break; }) still hangs.Possibly related: the problem surfaced on parsing level files for Bloom Jump (
parseNumberAt), which is invoked 5–10 times in a Pass-1 loop. If this is a single-entry-point optimizer issue, the function being called repeatedly could be relevant.Workaround used
Rewrote the hot path to avoid the nested while-loop entirely by pre-slicing the substring and delegating to native
parseInt. ButparseInton its own still needs a small while-loop to find the end-of-number, which also hangs — so currently blocked on having any level-load path working on web.Happy to provide more instrumentation if useful; also willing to test a fix candidate directly against the Bloom Jump repro.