Goal
Make Perry handle eval / new Function the way an AOT compiler should: never execute a code-string at runtime. Every legitimate case is resolved at or before build time, so the runtime JS engine (perry-jsruntime / embedded V8) becomes deletable from shipped binaries.
What the investigation found (verified against vendored sources)
Conclusion: a runtime interpreter ("PerryVM") has no use case here. The only place an evaluator is needed is build time, and we already own one (V8) — so the move is to demote V8 to a build-time tool, not embed it at runtime.
Phases (strictly gated)
Each phase must be merged to main and parity-green before the next is started. No parallel work across phases.
End state
A Perry binary that links no runtime JS engine, where:
- constant code-strings became native functions (P1),
- schema/template codegen libraries ran at build time and emitted native code (P2/P3),
- the few imperative-runtime JITs have native equivalents (P4),
- anything left over is a clear compile error naming the package and call site (P0).
Goal
Make Perry handle
eval/new Functionthe way an AOT compiler should: never execute a code-string at runtime. Every legitimate case is resolved at or before build time, so the runtime JS engine (perry-jsruntime/ embedded V8) becomes deletable from shipped binaries.What the investigation found (verified against vendored sources)
evalis already dead in Perry-emitted code (perry-hir/src/lockdown.rs: "Perry doesn't emitevaltoday";new Function(body)already refused inperry-hir/.../expr_call/intrinsics.rs). Zero test-files use either.eval/new Function. Zod grep is clean; Effect'sSchemais a combinator interpreter over an AST. The Effect blocker is purely compiler-completeness (perry-codegen: Effect — (number).slice is not a function during Schema.ts__init (#680 follow-up, ~310th init) #684 / perry-codegen: object literal with computed-key props + computed-key methods + cross-module spread drops keys & mis-resolves methods — EffectHashRing.tsblocker (post-#740) #809), not dynamism.new Functioncorpus is small and Fastify-shaped. Runtime libraries that actually JIT:fast-json-stringify(new Function('validator','serializer', contextFunctionCode)from a build-time schema),ajv(schema → validator),find-my-way(registered routes → matcher). Everything else that matched is build tooling (vite/rollup/next/eslint/vitest) that never ships in a Perry binary, or trivial feature-probes.Conclusion: a runtime interpreter ("PerryVM") has no use case here. The only place an evaluator is needed is build time, and we already own one (V8) — so the move is to demote V8 to a build-time tool, not embed it at runtime.
Phases (strictly gated)
Each phase must be merged to
mainand parity-green before the next is started. No parallel work across phases.new Functionbodies → native: feat(codegen): const-fold literal new Function bodies to native functions #1679End state
A Perry binary that links no runtime JS engine, where: