| 1 | /** |
| 2 | * Installs runtime-generated schema decoders without changing SchemaParser's |
| 3 | * public interface. Operations are compiled lazily. Import the separate |
| 4 | * `SchemaJITCompiler/enable` module to enable compilation globally. |
| 5 | * |
| 6 | * @stability unstable |
| 7 | * @since 4.0.0 |
| 8 | */ |
| 9 | import { type DecoderOperation, generate, shouldCompileParser } from "../internal/schema/codegen.ts" |
| 10 | import * as Registry from "../internal/schema/compilerRegistry.ts" |
| 11 | import type * as SchemaAST from "../SchemaAST.ts" |
| 12 | import type { CompiledDecoder } from "./SchemaCompiler.ts" |
| 13 | import { runtime } from "./SchemaCompiler/runtime.ts" |
| 14 | |
| 15 | let checked: FunctionConstructor | undefined |
| 16 | let supported = false |
| 17 | |
| 18 | /** |
| 19 | * @internal |
| 20 | */ |
| 21 | export const compiler: Registry.CompileSource = (ast, resolve) => { |
| 22 | if (!shouldCompileParser(ast)) return undefined |
| 23 | if (checked !== globalThis.Function) { |
| 24 | checked = globalThis.Function |
| 25 | try { |
| 26 | checked("return true") |
| 27 | supported = true |
| 28 | } catch { |
| 29 | supported = false |
| 30 | } |
| 31 | } |
| 32 | if (!supported) return undefined |
| 33 | let decodeFailed = false |
| 34 | const operation = (key: DecoderOperation) => { |
| 35 | const isConstruction = key === "make" || key === "makeEffect" |
| 36 | if (isConstruction || !decodeFailed) { |
| 37 | try { |
| 38 | const source = generate(ast, key) |
| 39 | if (source === undefined) return undefined |
| 40 | return globalThis.Function("ast", "R", "resolve", source)(ast, runtime, resolve) |
| 41 | } catch { |
| 42 | // Only code generation and initialization are inside this catch. |
| 43 | if (!isConstruction) decodeFailed = true |
| 44 | } |
| 45 | } |
| 46 | return key === "decodeEffect" |
| 47 | ? runtime.decode(ast, resolve) |
| 48 | : key === "makeEffect" |
| 49 | ? runtime.make(ast, resolve) |
| 50 | : undefined |
| 51 | } |
| 52 | return { |
| 53 | get is() { |
| 54 | return operation("is") |
| 55 | }, |
| 56 | get decode() { |
| 57 | return operation("decode") |
| 58 | }, |
| 59 | get make() { |
| 60 | return operation("make") |
| 61 | }, |
| 62 | get decodeEffect() { |
| 63 | return operation("decodeEffect") |
| 64 | }, |
| 65 | get makeEffect() { |
| 66 | return operation("makeEffect") |
| 67 | } |
| 68 | } satisfies CompiledDecoder |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * Enables lazy JIT compilation for an AST and its parsing dependencies. |
| 73 | * |
| 74 | * **Details** |
| 75 | * |
| 76 | * Uses the same registry as `SchemaCompiler.set`. Compilation failures, including |
| 77 | * environments that block `new Function`, retain interpreted parsing. Errors |
| 78 | * thrown while parsing are not retried. No checks, transformations or defaults |
| 79 | * execute during installation. |
| 80 | * |
| 81 | * **Gotchas** |
| 82 | * |
| 83 | * Enable before first use to optimize every consumer. Functions that already |
| 84 | * captured an entry keep it. Enable type-side and flipped ASTs separately when |
| 85 | * they differ from the supplied AST. |
| 86 | * |
| 87 | * @stability unstable |
| 88 | * @category compilation |
| 89 | * @since 4.0.0 |
| 90 | */ |
| 91 | export function enable(ast: SchemaAST.AST): void { |
| 92 | Registry.enable(ast, compiler) |
| 93 | } |