Repository navigation
Closed
Labels
package-auditFound by the 2026 package audit: compiling real npm packages from source instead of native bindingsFound by the 2026 package audit: compiling real npm packages from source instead of native bindings
Description
Found by the package audit (compiling real npm packages from source instead of Perry's native bindings) on
Perry e6dcb62 (v0.5.1587), Linux x64. With a runtime-built body, new Function(p, body) runs through the #6559 interpreter. The call-without-new spellings Function(p, body), Function.apply(null, [...]) and Function.call(null, ...) are compiled to a function that always throws "new Function() cannot run in an ahead-of-time compiled binary", even though the runtime has the interpreter and the end-of-build notice says these sites go to it.
Reproduction
main.ts:
const body = ["return a", "+", "b"].join(" "); // any runtime-built body
const t = (label: string, mk: () => any) => {
try { console.log(label, mk()(2, 3)); } catch (e) { console.log(label, "THREW", String(e)); }
};
t("new Function(a, b, body) ", () => new Function("a", "b", body));
t("Function(a, b, body) ", () => Function("a", "b", body));
t("Function.apply(null, [a,b,body])", () => Function.apply(null, ["a", "b", body]));
t("Function.call(null, a, b, body)", () => Function.call(null, "a", "b", body));perry compile main.ts -o out && ./out
PERRY_NO_AUTO_OPTIMIZE=1 perry compile main.ts -o out2 && ./out2 # identical resultExpected (Node 26.5.1)
new Function(a, b, body) 5
Function(a, b, body) 5
Function.apply(null, [a,b,body]) 5
Function.call(null, a, b, body) 5
Actual (Perry)
Compile-time notice (both modes):
notice: 4 ahead-of-time-unsupported sites handled at runtime:
- Function(...) main.ts:6 → runtime interpreter (#6559)
- Function(...) main.ts:7 → runtime interpreter (#6559)
- Function(...) main.ts:8 → runtime interpreter (#6559)
- new Function(...) main.ts:5 → runtime interpreter (#6559)
Run (both modes; <dir> stands for the absolute project path):
new Function(a, b, body) 5
Function(a, b, body) THREW Error: new Function() cannot run in an ahead-of-time compiled binary (<dir>/main.ts:6)
Function.apply(null, [a,b,body]) THREW Error: new Function() cannot run in an ahead-of-time compiled binary (<dir>/main.ts:7)
Function.call(null, a, b, body) THREW Error: new Function() cannot run in an ahead-of-time compiled binary (<dir>/main.ts:8)
The new Function line in the same binary shows that the interpreter is linked.
Impact
- mysql2 3.23.2: every query with a result set fails.
mysql2/lib/parsers/text_parser.js:6,128,211-213andbinary_parser.js:7,96,267build row parsers withgenerate-function2.3.1, whosetoFunctionisreturn Function.apply(null, keys.concat(src)).apply(null, vals)(generate-function/index.js:172). The audit's mysql2 program dies withFATAL Error: new Function() cannot run in an ahead-of-time compiled binary (.../node_modules/generate-function/index.js:381). The same failure without a database, using generate-function 2.3.1 (dependencyis-property1.0.2) andcompilePackages: ["generate-function"]:import genfun from "generate-function"; const gen = genfun(); gen("function add(a, b) {")("return a + b")("}"); console.log(gen.toFunction()(2, 3)); // Node: 5; Perry: Error: new Function() cannot run in an ahead-of-time compiled binary (.../generate-function/index.js:381)
- lodash 4.18.1 modular build:
lodash/template.js:271isFunction(importsKeys, sourceURL + 'return ' + source).apply(undefined, importsValues), a bare globalFunction(...)call. It also passes an array of names (new Functiondrops non-string arguments instead of calling ToString:new Function(['a','b'], body)loses its parameters, and spread argument lists produce an empty body #10424). This is inferred from the call shape;lodash/template.jswas not compiled on its own. The monolithiclodash.jsspells the same call through a local alias instead (Calling theFunctionconstructor through a value (F(...), a localvar Function,Function.bind,module.exports = Function) returnsundefined;fn.constructor(...)returns an object #10423). - Any library that uses
Function(...)withoutnewfor its generated code is affected, which is common in older CJS packages.
Notes
- The same stub is produced for a site inside a CJS helper module (
exports.make = (p, s) => Function.apply(null, p.concat(s))) and inside a package. - Code (verified by reading):
crates/perry-hir/src/lower/expr_call/intrinsics/eval_strict.rs:111-117. ForEvalDecision::DeferToRuntimeError,check_eval_function_callreturnssynth_deferred_eval_value(...)(crates/perry-hir/src/lower/const_fold_fn.rs:124-157), which builds(function () { throw new Error(...) }). Thenew Functionpath handles the same decision by falling through toExpr::New { "Function" }, which reachesjs_function_ctor_from_stringsand the interpreter (crates/perry-hir/src/lower/expr_new.rs:796-815, see the "runtime: dynamic code evaluation (new Function) for schema-codegen libs (ajv / fast-json-stringify / find-my-way) — blocks kimi-code #6559: a runtime-constructed body now EVALUATES" comment). That branch was never ported to the call form. ForFunction.apply(thisArg, argsArray), CreateDynamicFunction ignoresthisArg, so the fix would pass the array's elements as the argument list (inferred). - The notice text comes from
crates/perry/src/commands/compile/helpers.rs:227-231, which picks the "runtime interpreter" wording from the site kind alone, so it misreports these stubbed sites. - In CJS modules the
file:linein the thrown message is off by about 200 lines (generate-function/index.js:381for a 181-line file whose call is at line 172). - Sibling issues: Auto-optimize drops the
new Functioninterpreter when the only Function-constructor sites are aliases or known codegen libraries (find-my-way / ajv / fast-json-stringify) #10421 (auto-optimize omits the interpreter for unrecorded sites), Calling theFunctionconstructor through a value (F(...), a localvar Function,Function.bind,module.exports = Function) returnsundefined;fn.constructor(...)returns an object #10423 (callingFunctionthrough a value returnsundefined),new Functiondrops non-string arguments instead of calling ToString:new Function(['a','b'], body)loses its parameters, and spread argument lists produce an empty body #10424 (array parameter lists). - Related closed issues: runtime: dynamic code evaluation (new Function) for schema-codegen libs (ajv / fast-json-stringify / find-my-way) — blocks kimi-code #6559 (interpreter; its description names only
new Function), AOT: compile eval / new Function to a deferred runtime error (with compile-time notice) instead of refusing the build; add strict mode to block #5206 (defer-to-runtime-error design).
Metadata
Metadata
Assignees
Labels
package-auditFound by the 2026 package audit: compiling real npm packages from source instead of native bindingsFound by the 2026 package audit: compiling real npm packages from source instead of native bindings
Activity
fix(runtime): build functions from every Function constructor call shape