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

@proggeramlug

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(){}.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions