Skip to content

codegen: function-declaration that filters a module-level array of objects SIGSEGVs when called from a closure; const function-expression works #5202

Description

@proggeramlug

Perry version: 0.5.1161 (native host target; also reproduced on iOS device)

Summary

A specific function — a function declaration that iterates a module-level array of objects, reads a string property, and calls a string method — segfaults (SIGSEGV) when it is invoked from inside a closure. Rewriting the identical body as a const function-expression fixes it.

Minimal repro (native host)

interface SP { id: string }
let products: SP[] = [{ id: "GSC_AGENCY_MONTHLY" }, { id: "GSC_PRO_MONTHLY" }, { id: "GSC_PRO_ANNUAL" }]

function productsMatching(needle: string): SP[] {
  const out: SP[] = []
  for (let i = 0; i < products.length; i++) {
    const pid: string = products[i].id
    if (pid.indexOf(needle) >= 0) out.push(products[i])
  }
  return out
}

console.log("START")
const a = productsMatching("PRO")           // called at MODULE scope
console.log("module pp.len=" + a.length)    // prints 2  ✅
const onPress = function (): void {
  const b = productsMatching("PRO")          // SAME call, from inside a closure
  console.log("closure pp.len=" + b.length)  // never prints — SIGSEGV here
}
onPress()
console.log("END")                           // never prints

perry run repro.ts → prints START, module pp.len=2, then crashes (exit 1, faulting frame is the closure). EXC_BAD_ACCESS at a small/near-null address (0xfffffffffffffff9), consistent with a mis-resolved/mis-boxed function reference.

The fix that works

Change only the declaration form — function productsMatching(...)const productsMatching = function (...). Then the closure call prints closure pp.len=2 and reaches END. Nothing else changes.

Characterization

It is not "any function declaration called from a closure" — simpler ones work from closures:

function noMod(x: number) { return x + 1 }
function withModArray(x: number) { let s=0; for (let i=0;i<arr.length;i++) s+=arr[i]; return s+x } // module array of NUMBERS
const cb = function () { noMod(5); withModArray(5) }  // both work fine from the closure

The crash needs the combination in productsMatching (module array of objects + property read + indexOf + push) AND the hoisted function-declaration form AND being called from a closure. The const function-expression form is immune.

(Possibly related to the inline obj.prop.method() miscompile in #5195 — same products[i].id.indexOf(...) shape appears here — but here it's a hard crash gated on the declaration form + closure-call site, which is distinct.)

Impact

Silent SIGSEGV in a common shape (a helper that filters a module-level list of objects, called from a button handler/closure). Worked around app-side by using const fn = function(){} instead of function fn(){}.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions