typesea 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/CHANGELOG.md +85 -6
  2. package/README.md +143 -28
  3. package/dist/adapters/index.d.ts +50 -8
  4. package/dist/adapters/index.d.ts.map +1 -1
  5. package/dist/adapters/index.js +169 -48
  6. package/dist/aot/index.d.ts +19 -3
  7. package/dist/aot/index.d.ts.map +1 -1
  8. package/dist/aot/index.js +115 -17
  9. package/dist/async/index.d.ts +28 -56
  10. package/dist/async/index.d.ts.map +1 -1
  11. package/dist/async/index.js +94 -37
  12. package/dist/builders/composite.d.ts +43 -9
  13. package/dist/builders/composite.d.ts.map +1 -1
  14. package/dist/builders/composite.js +100 -17
  15. package/dist/builders/index.d.ts +8 -5
  16. package/dist/builders/index.d.ts.map +1 -1
  17. package/dist/builders/index.js +7 -4
  18. package/dist/builders/modifier.d.ts +36 -5
  19. package/dist/builders/modifier.d.ts.map +1 -1
  20. package/dist/builders/modifier.js +52 -5
  21. package/dist/builders/object/guard.d.ts +72 -24
  22. package/dist/builders/object/guard.d.ts.map +1 -1
  23. package/dist/builders/object/guard.js +139 -29
  24. package/dist/builders/object/index.d.ts +4 -2
  25. package/dist/builders/object/index.d.ts.map +1 -1
  26. package/dist/builders/object/index.js +3 -1
  27. package/dist/builders/object/schema.d.ts +88 -11
  28. package/dist/builders/object/schema.d.ts.map +1 -1
  29. package/dist/builders/object/schema.js +290 -23
  30. package/dist/builders/object/types.d.ts +20 -31
  31. package/dist/builders/object/types.d.ts.map +1 -1
  32. package/dist/builders/object/types.js +2 -0
  33. package/dist/builders/runtime.d.ts +40 -0
  34. package/dist/builders/runtime.d.ts.map +1 -0
  35. package/dist/builders/runtime.js +150 -0
  36. package/dist/builders/scalar.d.ts +49 -9
  37. package/dist/builders/scalar.d.ts.map +1 -1
  38. package/dist/builders/scalar.js +87 -9
  39. package/dist/builders/table.d.ts +35 -5
  40. package/dist/builders/table.d.ts.map +1 -1
  41. package/dist/builders/table.js +35 -5
  42. package/dist/builders/types.d.ts +20 -4
  43. package/dist/builders/types.d.ts.map +1 -1
  44. package/dist/builders/types.js +2 -0
  45. package/dist/compile/check-composite.d.ts +25 -2
  46. package/dist/compile/check-composite.d.ts.map +1 -1
  47. package/dist/compile/check-composite.js +699 -27
  48. package/dist/compile/check-scalar.d.ts +88 -0
  49. package/dist/compile/check-scalar.d.ts.map +1 -1
  50. package/dist/compile/check-scalar.js +570 -3
  51. package/dist/compile/check.d.ts +12 -0
  52. package/dist/compile/check.d.ts.map +1 -1
  53. package/dist/compile/check.js +62 -3
  54. package/dist/compile/context.d.ts +47 -9
  55. package/dist/compile/context.d.ts.map +1 -1
  56. package/dist/compile/context.js +53 -8
  57. package/dist/compile/first.d.ts +26 -0
  58. package/dist/compile/first.d.ts.map +1 -0
  59. package/dist/compile/first.js +850 -0
  60. package/dist/compile/graph-predicate.d.ts +4 -2
  61. package/dist/compile/graph-predicate.d.ts.map +1 -1
  62. package/dist/compile/graph-predicate.js +2272 -165
  63. package/dist/compile/guard.d.ts +16 -24
  64. package/dist/compile/guard.d.ts.map +1 -1
  65. package/dist/compile/guard.js +202 -72
  66. package/dist/compile/index.d.ts +3 -1
  67. package/dist/compile/index.d.ts.map +1 -1
  68. package/dist/compile/index.js +2 -0
  69. package/dist/compile/issue.d.ts +110 -0
  70. package/dist/compile/issue.d.ts.map +1 -1
  71. package/dist/compile/issue.js +184 -1
  72. package/dist/compile/names.d.ts +12 -2
  73. package/dist/compile/names.d.ts.map +1 -1
  74. package/dist/compile/names.js +19 -3
  75. package/dist/compile/predicate.d.ts +24 -0
  76. package/dist/compile/predicate.d.ts.map +1 -1
  77. package/dist/compile/predicate.js +287 -10
  78. package/dist/compile/runtime.d.ts +100 -13
  79. package/dist/compile/runtime.d.ts.map +1 -1
  80. package/dist/compile/runtime.js +56 -6
  81. package/dist/compile/source.d.ts +10 -2
  82. package/dist/compile/source.d.ts.map +1 -1
  83. package/dist/compile/source.js +385 -26
  84. package/dist/compile/types.d.ts +22 -0
  85. package/dist/compile/types.d.ts.map +1 -1
  86. package/dist/compile/types.js +2 -0
  87. package/dist/decoder/index.d.ts +92 -46
  88. package/dist/decoder/index.d.ts.map +1 -1
  89. package/dist/decoder/index.js +266 -39
  90. package/dist/evaluate/check-composite.d.ts +111 -2
  91. package/dist/evaluate/check-composite.d.ts.map +1 -1
  92. package/dist/evaluate/check-composite.js +343 -8
  93. package/dist/evaluate/check-scalar.d.ts +25 -0
  94. package/dist/evaluate/check-scalar.d.ts.map +1 -1
  95. package/dist/evaluate/check-scalar.js +124 -3
  96. package/dist/evaluate/check.d.ts +7 -0
  97. package/dist/evaluate/check.d.ts.map +1 -1
  98. package/dist/evaluate/check.js +62 -4
  99. package/dist/evaluate/index.d.ts +2 -0
  100. package/dist/evaluate/index.d.ts.map +1 -1
  101. package/dist/evaluate/index.js +2 -0
  102. package/dist/evaluate/issue.d.ts +11 -1
  103. package/dist/evaluate/issue.d.ts.map +1 -1
  104. package/dist/evaluate/issue.js +15 -1
  105. package/dist/evaluate/predicate.d.ts +16 -5
  106. package/dist/evaluate/predicate.d.ts.map +1 -1
  107. package/dist/evaluate/predicate.js +20 -5
  108. package/dist/evaluate/shared.d.ts +78 -13
  109. package/dist/evaluate/shared.d.ts.map +1 -1
  110. package/dist/evaluate/shared.js +101 -8
  111. package/dist/evaluate/state.d.ts +35 -13
  112. package/dist/evaluate/state.d.ts.map +1 -1
  113. package/dist/evaluate/state.js +35 -2
  114. package/dist/guard/array.d.ts +48 -0
  115. package/dist/guard/array.d.ts.map +1 -0
  116. package/dist/guard/array.js +84 -0
  117. package/dist/guard/base.d.ts +111 -31
  118. package/dist/guard/base.d.ts.map +1 -1
  119. package/dist/guard/base.js +165 -32
  120. package/dist/guard/date.d.ts +34 -0
  121. package/dist/guard/date.d.ts.map +1 -0
  122. package/dist/guard/date.js +60 -0
  123. package/dist/guard/error.d.ts +10 -5
  124. package/dist/guard/error.d.ts.map +1 -1
  125. package/dist/guard/error.js +10 -5
  126. package/dist/guard/index.d.ts +4 -0
  127. package/dist/guard/index.d.ts.map +1 -1
  128. package/dist/guard/index.js +4 -0
  129. package/dist/guard/number.d.ts +86 -11
  130. package/dist/guard/number.d.ts.map +1 -1
  131. package/dist/guard/number.js +159 -11
  132. package/dist/guard/props.d.ts +27 -3
  133. package/dist/guard/props.d.ts.map +1 -1
  134. package/dist/guard/props.js +27 -3
  135. package/dist/guard/read.d.ts +115 -10
  136. package/dist/guard/read.d.ts.map +1 -1
  137. package/dist/guard/read.js +185 -10
  138. package/dist/guard/registry.d.ts +12 -2
  139. package/dist/guard/registry.d.ts.map +1 -1
  140. package/dist/guard/registry.js +15 -3
  141. package/dist/guard/string.d.ts +115 -13
  142. package/dist/guard/string.d.ts.map +1 -1
  143. package/dist/guard/string.js +250 -13
  144. package/dist/guard/types.d.ts +110 -40
  145. package/dist/guard/types.d.ts.map +1 -1
  146. package/dist/guard/types.js +2 -0
  147. package/dist/index.d.ts +5 -5
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +4 -4
  150. package/dist/internal/index.d.ts +42 -6
  151. package/dist/internal/index.d.ts.map +1 -1
  152. package/dist/internal/index.js +51 -8
  153. package/dist/ir/builder.d.ts +17 -127
  154. package/dist/ir/builder.d.ts.map +1 -1
  155. package/dist/ir/builder.js +80 -137
  156. package/dist/ir/freeze.d.ts +4 -0
  157. package/dist/ir/freeze.d.ts.map +1 -1
  158. package/dist/ir/freeze.js +66 -0
  159. package/dist/ir/index.d.ts +3 -1
  160. package/dist/ir/index.d.ts.map +1 -1
  161. package/dist/ir/index.js +2 -0
  162. package/dist/ir/regexp.d.ts +2 -0
  163. package/dist/ir/regexp.d.ts.map +1 -1
  164. package/dist/ir/regexp.js +2 -0
  165. package/dist/ir/types.d.ts +94 -56
  166. package/dist/ir/types.d.ts.map +1 -1
  167. package/dist/ir/types.js +2 -0
  168. package/dist/ir/validate.d.ts +8 -1
  169. package/dist/ir/validate.d.ts.map +1 -1
  170. package/dist/ir/validate.js +511 -61
  171. package/dist/issue/index.d.ts +42 -10
  172. package/dist/issue/index.d.ts.map +1 -1
  173. package/dist/issue/index.js +65 -11
  174. package/dist/json-schema/emit-combinator.d.ts +44 -4
  175. package/dist/json-schema/emit-combinator.d.ts.map +1 -1
  176. package/dist/json-schema/emit-combinator.js +44 -4
  177. package/dist/json-schema/emit-composite.d.ts +16 -2
  178. package/dist/json-schema/emit-composite.d.ts.map +1 -1
  179. package/dist/json-schema/emit-composite.js +81 -13
  180. package/dist/json-schema/emit-scalar.d.ts +26 -3
  181. package/dist/json-schema/emit-scalar.d.ts.map +1 -1
  182. package/dist/json-schema/emit-scalar.js +124 -10
  183. package/dist/json-schema/emit-types.d.ts +11 -1
  184. package/dist/json-schema/emit-types.d.ts.map +1 -1
  185. package/dist/json-schema/emit-types.js +2 -0
  186. package/dist/json-schema/emit.d.ts +12 -1
  187. package/dist/json-schema/emit.d.ts.map +1 -1
  188. package/dist/json-schema/emit.js +23 -3
  189. package/dist/json-schema/freeze.d.ts +13 -2
  190. package/dist/json-schema/freeze.d.ts.map +1 -1
  191. package/dist/json-schema/freeze.js +41 -8
  192. package/dist/json-schema/index.d.ts +16 -2
  193. package/dist/json-schema/index.d.ts.map +1 -1
  194. package/dist/json-schema/index.js +23 -3
  195. package/dist/json-schema/issue.d.ts +4 -1
  196. package/dist/json-schema/issue.d.ts.map +1 -1
  197. package/dist/json-schema/issue.js +4 -1
  198. package/dist/json-schema/read.d.ts +24 -3
  199. package/dist/json-schema/read.d.ts.map +1 -1
  200. package/dist/json-schema/read.js +59 -12
  201. package/dist/json-schema/types.d.ts +45 -16
  202. package/dist/json-schema/types.d.ts.map +1 -1
  203. package/dist/json-schema/types.js +2 -0
  204. package/dist/kind/index.d.ts +40 -28
  205. package/dist/kind/index.d.ts.map +1 -1
  206. package/dist/kind/index.js +41 -13
  207. package/dist/lower/index.d.ts +6 -1
  208. package/dist/lower/index.d.ts.map +1 -1
  209. package/dist/lower/index.js +462 -46
  210. package/dist/message/index.d.ts +64 -10
  211. package/dist/message/index.d.ts.map +1 -1
  212. package/dist/message/index.js +155 -17
  213. package/dist/optimize/algebraic.d.ts +54 -0
  214. package/dist/optimize/algebraic.d.ts.map +1 -0
  215. package/dist/optimize/algebraic.js +314 -0
  216. package/dist/optimize/compact.d.ts +8 -1
  217. package/dist/optimize/compact.d.ts.map +1 -1
  218. package/dist/optimize/compact.js +13 -2
  219. package/dist/optimize/domain.d.ts +16 -0
  220. package/dist/optimize/domain.d.ts.map +1 -0
  221. package/dist/optimize/domain.js +619 -0
  222. package/dist/optimize/fold-boolean.d.ts +17 -2
  223. package/dist/optimize/fold-boolean.d.ts.map +1 -1
  224. package/dist/optimize/fold-boolean.js +59 -14
  225. package/dist/optimize/fold-common.d.ts +43 -8
  226. package/dist/optimize/fold-common.d.ts.map +1 -1
  227. package/dist/optimize/fold-common.js +37 -6
  228. package/dist/optimize/fold-constraints.d.ts +33 -0
  229. package/dist/optimize/fold-constraints.d.ts.map +1 -0
  230. package/dist/optimize/fold-constraints.js +484 -0
  231. package/dist/optimize/fold-scalar.d.ts +98 -13
  232. package/dist/optimize/fold-scalar.d.ts.map +1 -1
  233. package/dist/optimize/fold-scalar.js +98 -13
  234. package/dist/optimize/fold.d.ts +8 -1
  235. package/dist/optimize/fold.d.ts.map +1 -1
  236. package/dist/optimize/fold.js +22 -2
  237. package/dist/optimize/index.d.ts +9 -1
  238. package/dist/optimize/index.d.ts.map +1 -1
  239. package/dist/optimize/index.js +18 -3
  240. package/dist/optimize/map-node.d.ts +3 -1
  241. package/dist/optimize/map-node.d.ts.map +1 -1
  242. package/dist/optimize/map-node.js +48 -3
  243. package/dist/optimize/peephole.d.ts +16 -0
  244. package/dist/optimize/peephole.d.ts.map +1 -0
  245. package/dist/optimize/peephole.js +254 -0
  246. package/dist/optimize/remap.d.ts +2 -0
  247. package/dist/optimize/remap.d.ts.map +1 -1
  248. package/dist/optimize/remap.js +2 -0
  249. package/dist/optimize/rewrite.d.ts +13 -8
  250. package/dist/optimize/rewrite.d.ts.map +1 -1
  251. package/dist/optimize/rewrite.js +13 -8
  252. package/dist/plan/cache.d.ts +9 -3
  253. package/dist/plan/cache.d.ts.map +1 -1
  254. package/dist/plan/cache.js +34 -6
  255. package/dist/plan/index.d.ts +2 -0
  256. package/dist/plan/index.d.ts.map +1 -1
  257. package/dist/plan/index.js +2 -0
  258. package/dist/plan/predicate.d.ts +2 -0
  259. package/dist/plan/predicate.d.ts.map +1 -1
  260. package/dist/plan/predicate.js +298 -29
  261. package/dist/plan/schema-predicate.d.ts +6 -0
  262. package/dist/plan/schema-predicate.d.ts.map +1 -1
  263. package/dist/plan/schema-predicate.js +382 -19
  264. package/dist/plan/types.d.ts +2 -0
  265. package/dist/plan/types.d.ts.map +1 -1
  266. package/dist/plan/types.js +2 -0
  267. package/dist/result/index.d.ts +19 -5
  268. package/dist/result/index.d.ts.map +1 -1
  269. package/dist/result/index.js +10 -2
  270. package/dist/schema/common.d.ts +69 -6
  271. package/dist/schema/common.d.ts.map +1 -1
  272. package/dist/schema/common.js +104 -10
  273. package/dist/schema/freeze.d.ts +4 -0
  274. package/dist/schema/freeze.d.ts.map +1 -1
  275. package/dist/schema/freeze.js +40 -0
  276. package/dist/schema/index.d.ts +5 -2
  277. package/dist/schema/index.d.ts.map +1 -1
  278. package/dist/schema/index.js +4 -1
  279. package/dist/schema/lazy.d.ts +4 -0
  280. package/dist/schema/lazy.d.ts.map +1 -1
  281. package/dist/schema/lazy.js +4 -0
  282. package/dist/schema/literal.d.ts +7 -1
  283. package/dist/schema/literal.d.ts.map +1 -1
  284. package/dist/schema/literal.js +7 -1
  285. package/dist/schema/types.d.ts +109 -100
  286. package/dist/schema/types.d.ts.map +1 -1
  287. package/dist/schema/types.js +13 -2
  288. package/dist/schema/undefined.d.ts +17 -0
  289. package/dist/schema/undefined.d.ts.map +1 -0
  290. package/dist/schema/undefined.js +77 -0
  291. package/dist/schema/validate.d.ts +8 -1
  292. package/dist/schema/validate.d.ts.map +1 -1
  293. package/dist/schema/validate.js +255 -57
  294. package/docs/api.md +128 -8
  295. package/docs/assets/benchmark-headline.svg +163 -0
  296. package/docs/engine-notes.md +62 -15
  297. package/docs/index.html +1340 -702
  298. package/docs/ko/api.md +375 -0
  299. package/docs/ko/engine-notes.md +156 -0
  300. package/docs/ko/readme.md +378 -0
  301. package/package.json +66 -65
@@ -1,21 +1,98 @@
1
1
  /**
2
2
  * @file graph-predicate.ts
3
3
  * @brief Predicate source emitter backed by optimized Sea-of-Nodes graphs.
4
+ * @details Generated-source helpers keep the side-table ABI and JavaScript source shape
5
+ * stable across runtime and AOT emission.
4
6
  */
5
- import { NodeTag } from "../kind/index.js";
7
+ import { ArrayCheckTag, NodeTag, ObjectModeTag, PresenceTag, SchemaTag } from "../kind/index.js";
6
8
  import { makeValidationPlan } from "../plan/index.js";
9
+ import { schemaCanAcceptUndefined } from "../schema/index.js";
7
10
  import { pushKeyset, pushLiteral, pushRegex, pushSchema, stringRef } from "./context.js";
11
+ /**
12
+ * @brief Allocate root emitter state for a predicate body.
13
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
14
+ * @returns Empty state with no known branch facts.
15
+ */
16
+ function makeGraphEmitState() {
17
+ return {
18
+ chunks: [],
19
+ dataSlots: new Map(),
20
+ dataGuards: new Set(),
21
+ dataLiterals: new Map(),
22
+ knownObjects: new Set(),
23
+ knownPredicates: new Set(),
24
+ knownTypeofs: new Set(),
25
+ failureLabel: undefined,
26
+ temp: 0
27
+ };
28
+ }
29
+ /**
30
+ * @brief Fork emitter state for a speculative branch.
31
+ * @param parent State at the branch entry.
32
+ * @returns New state with copied facts and independent output chunks.
33
+ * @details Branches inherit proven facts but must not append directly to the
34
+ * parent until the caller chooses the branch source.
35
+ */
36
+ function makeBranchEmitState(parent) {
37
+ return {
38
+ chunks: [],
39
+ dataSlots: cloneDataSlots(parent.dataSlots),
40
+ dataGuards: new Set(parent.dataGuards),
41
+ dataLiterals: new Map(parent.dataLiterals),
42
+ knownObjects: new Set(parent.knownObjects),
43
+ knownPredicates: new Set(parent.knownPredicates),
44
+ knownTypeofs: new Set(parent.knownTypeofs),
45
+ failureLabel: parent.failureLabel,
46
+ temp: parent.temp
47
+ };
48
+ }
49
+ /**
50
+ * @brief Fork emitter state that exits through a local failure label.
51
+ * @param parent State at the branch entry.
52
+ * @param failureLabel Label used when the branch fails.
53
+ * @returns Branch state configured to break instead of returning false.
54
+ * @details Union and dispatch emitters use labels to probe a branch without
55
+ * aborting the whole predicate.
56
+ */
57
+ function makeFailureBranchEmitState(parent, failureLabel) {
58
+ const state = makeBranchEmitState(parent);
59
+ return {
60
+ chunks: state.chunks,
61
+ dataSlots: state.dataSlots,
62
+ dataGuards: state.dataGuards,
63
+ dataLiterals: state.dataLiterals,
64
+ knownObjects: state.knownObjects,
65
+ knownPredicates: state.knownPredicates,
66
+ knownTypeofs: state.knownTypeofs,
67
+ failureLabel,
68
+ temp: state.temp
69
+ };
70
+ }
71
+ /**
72
+ * @brief clone data slots.
73
+ * @details Data slots carry aliases and descriptor-backed values across branch scopes so generated code can reuse proven reads without changing validation semantics.
74
+ */
75
+ function cloneDataSlots(slots) {
76
+ const cloned = new Map();
77
+ for (const [key, slot] of slots) {
78
+ cloned.set(key, {
79
+ descriptor: slot.descriptor,
80
+ value: slot.value
81
+ });
82
+ }
83
+ return cloned;
84
+ }
8
85
  /**
9
86
  * @brief emit graph function.
10
87
  * @details Emits one predicate function from the optimized graph owned by a schema plan.
11
88
  * @returns Generated function name.
12
89
  */
13
- export function emitGraphFunction(schema, context) {
90
+ export function emitGraphFunction(schema, context, preferredName) {
14
91
  const cached = context.functionNames.get(schema);
15
92
  if (cached !== undefined) {
16
93
  return cached;
17
94
  }
18
- const name = `p${String(context.functions.length)}`;
95
+ const name = preferredName ?? nextGraphFunctionName(context);
19
96
  const source = {
20
97
  name,
21
98
  body: ""
@@ -41,26 +118,85 @@ export function emitGraphFunctions(context) {
41
118
  }
42
119
  return chunks.join("");
43
120
  }
121
+ /**
122
+ * @brief emit graph child function.
123
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
124
+ */
125
+ function emitGraphChildFunction(graph, context) {
126
+ const name = nextGraphFunctionName(context);
127
+ const source = {
128
+ name,
129
+ body: ""
130
+ };
131
+ context.functions.push(source);
132
+ source.body = emitGraphBody(graph, graph.result, "v", context);
133
+ return name;
134
+ }
135
+ /**
136
+ * @brief Execute next graph function name.
137
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
138
+ */
139
+ function nextGraphFunctionName(context) {
140
+ let index = context.functions.length;
141
+ let name = `p${String(index)}`;
142
+ while (hasFunctionName(context, name)) {
143
+ index += 1;
144
+ name = `p${String(index)}`;
145
+ }
146
+ return name;
147
+ }
148
+ /**
149
+ * @brief Check function name.
150
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
151
+ */
152
+ function hasFunctionName(context, name) {
153
+ for (let index = 0; index < context.functions.length; index += 1) {
154
+ if (context.functions[index]?.name === name) {
155
+ return true;
156
+ }
157
+ }
158
+ for (let index = 0; index < context.checkFunctions.length; index += 1) {
159
+ if (context.checkFunctions[index]?.name === name) {
160
+ return true;
161
+ }
162
+ }
163
+ return false;
164
+ }
165
+ /**
166
+ * @brief Check unsafe mode.
167
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
168
+ */
169
+ function isUnsafeMode(context) {
170
+ return context.mode !== "safe";
171
+ }
172
+ /**
173
+ * @brief Check unchecked mode.
174
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
175
+ */
176
+ function isUncheckedMode(context) {
177
+ return context.mode === "unchecked";
178
+ }
44
179
  /**
45
180
  * @brief emit graph body.
181
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
46
182
  */
47
183
  function emitGraphBody(graph, id, value, context) {
48
- const state = {
49
- chunks: [],
50
- dataSlots: new Map(),
51
- dataGuards: new Set(),
52
- temp: 0
53
- };
184
+ const state = makeGraphEmitState();
54
185
  emitGraphReturn(graph, id, value, context, state);
55
186
  return state.chunks.join("");
56
187
  }
57
188
  /**
58
189
  * @brief emit graph return.
190
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
59
191
  */
60
192
  function emitGraphReturn(graph, id, value, context, state) {
61
193
  const node = graph.nodes[id];
62
194
  if (node === undefined) {
63
- state.chunks.push("return false;");
195
+ state.chunks.push(failStatement(state));
196
+ return;
197
+ }
198
+ if (node.tag === NodeTag.Const) {
199
+ state.chunks.push(node.value === true ? "return true;" : failStatement(state));
64
200
  return;
65
201
  }
66
202
  switch (node.tag) {
@@ -74,7 +210,17 @@ function emitGraphReturn(graph, id, value, context, state) {
74
210
  emitOrReturn(graph, node.values, value, context, state);
75
211
  return;
76
212
  case NodeTag.DiscriminantDispatch:
77
- emitDiscriminantDispatchReturn(graph, node.value, value, node.key, node.literals, node.schemas, context, state);
213
+ emitDiscriminantDispatchReturn(graph, node, value, context, state);
214
+ return;
215
+ case NodeTag.UnionDispatch:
216
+ emitUnionDispatchReturn(graph, node, value, context, state);
217
+ return;
218
+ case NodeTag.PrimitiveUnion:
219
+ emitPrimitiveUnionReturn(graph, node, value, context, state);
220
+ return;
221
+ case NodeTag.ObjectShape:
222
+ emitObjectShapeCheck(graph, node, value, context, state);
223
+ state.chunks.push("return true;");
78
224
  return;
79
225
  default:
80
226
  state.chunks.push(`return ${emitGraphExpression(graph, id, value, context, state)};`);
@@ -82,6 +228,7 @@ function emitGraphReturn(graph, id, value, context, state) {
82
228
  }
83
229
  /**
84
230
  * @brief emit and return.
231
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
85
232
  */
86
233
  function emitAndReturn(graph, ids, value, context, state) {
87
234
  for (let index = 0; index < ids.length; index += 1) {
@@ -94,10 +241,27 @@ function emitAndReturn(graph, ids, value, context, state) {
94
241
  }
95
242
  /**
96
243
  * @brief emit false check.
244
+ * @details Guard snippets keep the generated fast path straight-line while making early failure branches share the same source shape.
97
245
  */
98
246
  function emitFalseCheck(graph, id, value, context, state) {
99
247
  const node = graph.nodes[id];
248
+ if (node?.tag === NodeTag.Const) {
249
+ if (node.value !== true) {
250
+ state.chunks.push(failStatement(state));
251
+ }
252
+ return;
253
+ }
254
+ if (node?.tag === NodeTag.Return) {
255
+ emitFalseCheck(graph, node.value, value, context, state);
256
+ return;
257
+ }
100
258
  if (node?.tag === NodeTag.And) {
259
+ if (emitStringAndCheck(graph, node.values, value, context, state)) {
260
+ return;
261
+ }
262
+ if (emitNumberAndCheck(graph, node.values, value, context, state)) {
263
+ return;
264
+ }
101
265
  for (let index = 0; index < node.values.length; index += 1) {
102
266
  const child = node.values[index];
103
267
  if (child !== undefined) {
@@ -107,11 +271,19 @@ function emitFalseCheck(graph, id, value, context, state) {
107
271
  return;
108
272
  }
109
273
  if (node?.tag === NodeTag.ArrayEvery) {
110
- emitArrayEveryCheck(graph, node.value, node.item, value, context, state);
274
+ emitArrayEveryCheck(graph, node.value, node.item, node.checks, node.itemGraph, value, context, state);
275
+ return;
276
+ }
277
+ if (node?.tag === NodeTag.TupleItems) {
278
+ emitTupleItemsCheck(graph, node.value, node.items, node.itemGraphs, value, context, state);
111
279
  return;
112
280
  }
113
281
  if (node?.tag === NodeTag.RecordEvery) {
114
- emitRecordEveryCheck(graph, node.value, node.item, value, context, state);
282
+ emitRecordEveryCheck(graph, node.value, node.item, node.itemGraph, value, context, state);
283
+ return;
284
+ }
285
+ if (node?.tag === NodeTag.PrimitiveUnion) {
286
+ emitPrimitiveUnionCheck(graph, node, value, context, state);
115
287
  return;
116
288
  }
117
289
  if (node?.tag === NodeTag.HasOwnData) {
@@ -122,57 +294,337 @@ function emitFalseCheck(graph, id, value, context, state) {
122
294
  emitStrictKeysCheck(graph, node.object, node.keys, value, context, state);
123
295
  return;
124
296
  }
125
- state.chunks.push(`if(!${emitGraphExpression(graph, id, value, context, state)})return false;`);
297
+ if (isKnownPredicateNode(graph, node, value, context, state)) {
298
+ return;
299
+ }
300
+ if (node?.tag === NodeTag.IsNumber) {
301
+ emitNumberGuard(emitGraphExpression(graph, node.value, value, context, state), state);
302
+ return;
303
+ }
304
+ if (node !== undefined && isKnownPredicateTag(node.tag) &&
305
+ node.tag !== NodeTag.IsObject && "value" in node &&
306
+ typeof node.value === "number") {
307
+ emitPredicateGuard(node.tag, emitGraphExpression(graph, node.value, value, context, state), state);
308
+ return;
309
+ }
310
+ if (node?.tag === NodeTag.IsObject) {
311
+ emitObjectGuard(emitGraphExpression(graph, node.value, value, context, state), state);
312
+ return;
313
+ }
314
+ if (node?.tag === NodeTag.ObjectShape) {
315
+ emitObjectShapeCheck(graph, node, value, context, state);
316
+ return;
317
+ }
318
+ state.chunks.push(`if(!${emitGraphExpression(graph, id, value, context, state)})${failStatement(state)}`);
126
319
  }
127
320
  /**
128
321
  * @brief emit array every check.
322
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
323
+ * @param graph Source graph that owns the array value node.
324
+ * @param valueId Node id that produces the candidate array value.
325
+ * @param item Source schema retained for sparse-hole policy.
326
+ * @param itemGraph Optimized graph used for each validated slot.
327
+ * @param value Root generated value expression.
328
+ * @param context Shared code-generation context.
329
+ * @param state Mutable graph emitter state.
330
+ * @post Appends branch code to `state.chunks`.
129
331
  */
130
- function emitArrayEveryCheck(graph, valueId, item, value, context, state) {
332
+ function emitArrayEveryCheck(graph, valueId, item, checks, itemGraph, value, context, state) {
333
+ if (isUnsafeMode(context)) {
334
+ emitUnsafeArrayEveryCheck(graph, valueId, checks, itemGraph, value, context, state);
335
+ return;
336
+ }
131
337
  const arrayExpression = emitGraphExpression(graph, valueId, value, context, state);
132
338
  const index = `i${String(state.temp)}`;
133
339
  state.temp += 1;
134
340
  const descriptor = `d${String(state.temp)}`;
135
341
  state.temp += 1;
136
- const itemValue = `v${String(state.temp)}`;
137
- state.temp += 1;
138
- state.chunks.push(`for(let ${index}=0;${index}<${arrayExpression}.length;${index}+=1){`, `const ${descriptor}=gp(${arrayExpression},${index});`, `if(${descriptor}!==undefined&&!h.call(${descriptor},"value"))return false;`, `const ${itemValue}=${descriptor}===undefined?undefined:${descriptor}.value;`, `if(!${emitInlineSchemaExpression(item, itemValue, context)})return false;`, "}");
342
+ const allowsUndefined = schemaCanAcceptUndefined(item);
343
+ const itemConstant = readGraphResultBoolean(itemGraph);
344
+ emitArrayGuard(arrayExpression, state);
345
+ emitArrayLengthChecks(arrayExpression, checks, state);
346
+ if (itemConstant === false) {
347
+ /*
348
+ * If the item graph is impossible, only an empty array can pass. This
349
+ * avoids emitting a loop that would fail on the first slot.
350
+ */
351
+ state.chunks.push(`if(${arrayExpression}.length!==0)${failStatement(state)}`);
352
+ return;
353
+ }
354
+ if (allowsUndefined) {
355
+ /*
356
+ * Holes are already accepted by the source schema, so the hot path scales
357
+ * with present descriptors rather than logical length.
358
+ */
359
+ emitPresentArrayEveryCheck(itemGraph, itemConstant, arrayExpression, context, state);
360
+ return;
361
+ }
362
+ state.chunks.push(`for(let ${index}=0;${index}<${arrayExpression}.length;${index}+=1){`, `const ${descriptor}=gp(${arrayExpression},${index});`, `if(${descriptor}===undefined)${failStatement(state)}`);
363
+ if (itemConstant !== true) {
364
+ const itemValue = `v${String(state.temp)}`;
365
+ state.temp += 1;
366
+ state.chunks.push(`const ${itemValue}=${descriptor}.value;`);
367
+ emitFalseCheck(itemGraph, itemGraph.result, itemValue, context, state);
368
+ }
369
+ state.chunks.push("}");
139
370
  }
140
371
  /**
141
- * @brief emit record every check.
372
+ * @brief emit present array every check.
373
+ * @details When holes are valid `undefined` values, only present own index slots
374
+ * can fail validation or hide accessor code, so sparse arrays avoid hole scans.
375
+ * @param itemGraph Optimized graph used for each present index.
376
+ * @param itemConstant Precomputed constant result for the item graph.
377
+ * @param arrayExpression Generated expression for the array value.
378
+ * @param context Shared code-generation context.
379
+ * @param state Mutable graph emitter state.
380
+ * @post Appends a present-index loop to `state.chunks`.
142
381
  */
143
- function emitRecordEveryCheck(graph, valueId, item, value, context, state) {
144
- const recordExpression = emitGraphExpression(graph, valueId, value, context, state);
382
+ function emitPresentArrayEveryCheck(itemGraph, itemConstant, arrayExpression, context, state) {
383
+ const keyIndex = `x${String(state.temp)}`;
384
+ state.temp += 1;
385
+ const key = `k${String(state.temp)}`;
386
+ state.temp += 1;
387
+ const descriptor = `d${String(state.temp)}`;
388
+ state.temp += 1;
145
389
  const keys = `ks${String(state.temp)}`;
146
390
  state.temp += 1;
391
+ /*
392
+ * Object.getOwnPropertyNames allocates one key list, but it avoids a
393
+ * length-proportional scan for hostile sparse arrays with huge length.
394
+ */
395
+ state.chunks.push(`const ${keys}=Object.getOwnPropertyNames(${arrayExpression});`, `for(let ${keyIndex}=0;${keyIndex}<${keys}.length;${keyIndex}+=1){`, `const ${key}=${keys}[${keyIndex}];`, `if(!ai(${key},${arrayExpression}.length))continue;`, `const ${descriptor}=gp(${arrayExpression},${key});`, `if(${descriptor}!==undefined&&!h.call(${descriptor},"value"))${failStatement(state)}`);
396
+ if (itemConstant !== true) {
397
+ /*
398
+ * The descriptor may disappear between key enumeration and lookup on
399
+ * exotic arrays. Treat absence as a valid hole because this path exists
400
+ * only after undefined was accepted.
401
+ */
402
+ const itemValue = `v${String(state.temp)}`;
403
+ state.temp += 1;
404
+ state.chunks.push(`if(${descriptor}!==undefined){`, `const ${itemValue}=${descriptor}.value;`);
405
+ emitFalseCheck(itemGraph, itemGraph.result, itemValue, context, state);
406
+ state.chunks.push("}");
407
+ }
408
+ state.chunks.push("}");
409
+ }
410
+ /**
411
+ * @brief Execute emit unsafe array every check.
412
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
413
+ */
414
+ function emitUnsafeArrayEveryCheck(graph, valueId, checks, itemGraph, value, context, state) {
415
+ const arrayExpression = emitGraphExpression(graph, valueId, value, context, state);
147
416
  const index = `i${String(state.temp)}`;
148
417
  state.temp += 1;
418
+ const itemConstant = readGraphResultBoolean(itemGraph);
419
+ emitArrayGuard(arrayExpression, state);
420
+ emitArrayLengthChecks(arrayExpression, checks, state);
421
+ if (itemConstant === false) {
422
+ state.chunks.push(`if(${arrayExpression}.length!==0)${failStatement(state)}`);
423
+ return;
424
+ }
425
+ if (itemConstant === true) {
426
+ return;
427
+ }
428
+ const itemValue = `v${String(state.temp)}`;
429
+ state.temp += 1;
430
+ state.chunks.push(`for(let ${index}=0;${index}<${arrayExpression}.length;${index}+=1){`, `const ${itemValue}=${arrayExpression}[${index}];`);
431
+ emitFalseCheck(itemGraph, itemGraph.result, itemValue, context, state);
432
+ state.chunks.push("}");
433
+ }
434
+ /**
435
+ * @brief Emit array length failure branches.
436
+ * @param arrayExpression Generated expression already proven to be an array.
437
+ * @param checks Normalized array length checks.
438
+ * @param state Mutable graph emitter state.
439
+ * @post Appends no code when the schema has no length checks.
440
+ */
441
+ function emitArrayLengthChecks(arrayExpression, checks, state) {
442
+ for (let index = 0; index < checks.length; index += 1) {
443
+ const check = checks[index];
444
+ if (check === undefined) {
445
+ state.chunks.push(failStatement(state));
446
+ continue;
447
+ }
448
+ switch (check.tag) {
449
+ case ArrayCheckTag.Min:
450
+ state.chunks.push(`if(${arrayExpression}.length<${String(check.value)})${failStatement(state)}`);
451
+ break;
452
+ case ArrayCheckTag.Max:
453
+ state.chunks.push(`if(${arrayExpression}.length>${String(check.value)})${failStatement(state)}`);
454
+ break;
455
+ }
456
+ }
457
+ }
458
+ /**
459
+ * @brief emit tuple items check.
460
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
461
+ */
462
+ function emitTupleItemsCheck(graph, valueId, items, itemGraphs, value, context, state) {
463
+ if (isUnsafeMode(context)) {
464
+ emitUnsafeTupleItemsCheck(graph, valueId, itemGraphs, value, context, state);
465
+ return;
466
+ }
467
+ const tupleExpression = emitGraphExpression(graph, valueId, value, context, state);
468
+ emitArrayGuard(tupleExpression, state);
469
+ state.chunks.push(`if(${tupleExpression}.length!==${String(itemGraphs.length)})${failStatement(state)}`);
470
+ for (let index = 0; index < itemGraphs.length; index += 1) {
471
+ const itemGraph = itemGraphs[index];
472
+ if (itemGraph === undefined) {
473
+ state.chunks.push(failStatement(state));
474
+ continue;
475
+ }
476
+ const item = items[index];
477
+ if (item === undefined) {
478
+ state.chunks.push(failStatement(state));
479
+ continue;
480
+ }
481
+ const itemConstant = readGraphResultBoolean(itemGraph);
482
+ if (itemConstant === false) {
483
+ state.chunks.push(failStatement(state));
484
+ return;
485
+ }
486
+ const allowsUndefined = schemaCanAcceptUndefined(item);
487
+ const descriptor = `d${String(state.temp)}`;
488
+ state.temp += 1;
489
+ state.chunks.push(`const ${descriptor}=gp(${tupleExpression},${String(index)});`);
490
+ if (allowsUndefined) {
491
+ state.chunks.push(`if(${descriptor}!==undefined&&!h.call(${descriptor},"value"))${failStatement(state)}`);
492
+ }
493
+ else {
494
+ state.chunks.push(`if(${descriptor}===undefined)${failStatement(state)}`);
495
+ }
496
+ if (itemConstant !== true) {
497
+ const itemValue = `v${String(state.temp)}`;
498
+ state.temp += 1;
499
+ state.chunks.push(allowsUndefined
500
+ ? `const ${itemValue}=${descriptor}===undefined?undefined:${descriptor}.value;`
501
+ : `const ${itemValue}=${descriptor}.value;`);
502
+ emitFalseCheck(itemGraph, itemGraph.result, itemValue, context, state);
503
+ }
504
+ }
505
+ }
506
+ /**
507
+ * @brief Execute emit unsafe tuple items check.
508
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
509
+ */
510
+ function emitUnsafeTupleItemsCheck(graph, valueId, itemGraphs, value, context, state) {
511
+ const tupleExpression = emitGraphExpression(graph, valueId, value, context, state);
512
+ emitArrayGuard(tupleExpression, state);
513
+ state.chunks.push(`if(${tupleExpression}.length!==${String(itemGraphs.length)})${failStatement(state)}`);
514
+ for (let index = 0; index < itemGraphs.length; index += 1) {
515
+ const itemGraph = itemGraphs[index];
516
+ if (itemGraph === undefined) {
517
+ state.chunks.push(failStatement(state));
518
+ continue;
519
+ }
520
+ const itemConstant = readGraphResultBoolean(itemGraph);
521
+ if (itemConstant === false) {
522
+ state.chunks.push(failStatement(state));
523
+ return;
524
+ }
525
+ if (itemConstant !== true) {
526
+ const itemValue = `v${String(state.temp)}`;
527
+ state.temp += 1;
528
+ state.chunks.push(`const ${itemValue}=${tupleExpression}[${String(index)}];`);
529
+ emitFalseCheck(itemGraph, itemGraph.result, itemValue, context, state);
530
+ }
531
+ }
532
+ }
533
+ /**
534
+ * @brief emit record every check.
535
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
536
+ */
537
+ function emitRecordEveryCheck(graph, valueId, item, itemGraph, value, context, state) {
538
+ if (isUnsafeMode(context)) {
539
+ emitUnsafeRecordEveryCheck(graph, valueId, itemGraph, value, context, state);
540
+ return;
541
+ }
542
+ const recordExpression = emitGraphExpression(graph, valueId, value, context, state);
149
543
  const key = `key${String(state.temp)}`;
150
544
  state.temp += 1;
151
545
  const descriptor = `d${String(state.temp)}`;
152
546
  state.temp += 1;
547
+ const itemConstant = readGraphResultBoolean(itemGraph);
548
+ const rejectsUndefined = !schemaCanAcceptUndefined(item);
549
+ emitObjectGuard(recordExpression, state);
550
+ if (itemConstant === false) {
551
+ state.chunks.push(`for(const ${key} in ${recordExpression}){`, `if(h.call(${recordExpression},${key}))${failStatement(state)}`, "}");
552
+ return;
553
+ }
554
+ state.chunks.push(`for(const ${key} in ${recordExpression}){`, `if(!h.call(${recordExpression},${key}))continue;`, `const ${descriptor}=gp(${recordExpression},${key});`, rejectsUndefined
555
+ ? `if(${descriptor}===undefined)${failStatement(state)}`
556
+ : `if(${descriptor}===undefined||!h.call(${descriptor},"value"))${failStatement(state)}`);
557
+ if (itemConstant !== true) {
558
+ const itemValue = `v${String(state.temp)}`;
559
+ state.temp += 1;
560
+ state.chunks.push(`const ${itemValue}=${descriptor}.value;`);
561
+ emitFalseCheck(itemGraph, itemGraph.result, itemValue, context, state);
562
+ }
563
+ state.chunks.push("}");
564
+ }
565
+ /**
566
+ * @brief Execute emit unsafe record every check.
567
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
568
+ */
569
+ function emitUnsafeRecordEveryCheck(graph, valueId, itemGraph, value, context, state) {
570
+ const recordExpression = emitGraphExpression(graph, valueId, value, context, state);
571
+ const key = `key${String(state.temp)}`;
572
+ state.temp += 1;
573
+ const itemConstant = readGraphResultBoolean(itemGraph);
574
+ emitObjectGuard(recordExpression, state);
575
+ if (itemConstant === false) {
576
+ if (isUncheckedMode(context)) {
577
+ state.chunks.push(`for(const ${key} in ${recordExpression}){`, failStatement(state), "}");
578
+ }
579
+ else {
580
+ state.chunks.push(`for(const ${key} in ${recordExpression}){`, `if(h.call(${recordExpression},${key}))${failStatement(state)}`, "}");
581
+ }
582
+ return;
583
+ }
584
+ if (itemConstant === true) {
585
+ return;
586
+ }
153
587
  const itemValue = `v${String(state.temp)}`;
154
588
  state.temp += 1;
155
- state.chunks.push(`const ${keys}=Object.keys(${recordExpression});`, `for(let ${index}=0;${index}<${keys}.length;${index}+=1){`, `const ${key}=${keys}[${index}];`, `if(${key}===undefined)return false;`, `const ${descriptor}=gp(${recordExpression},${key});`, `if(${descriptor}===undefined||!h.call(${descriptor},"value"))return false;`, `const ${itemValue}=${descriptor}.value;`, `if(!${emitInlineSchemaExpression(item, itemValue, context)})return false;`, "}");
589
+ if (isUncheckedMode(context)) {
590
+ state.chunks.push(`for(const ${key} in ${recordExpression}){`, `const ${itemValue}=${recordExpression}[${key}];`);
591
+ }
592
+ else {
593
+ state.chunks.push(`for(const ${key} in ${recordExpression}){`, `if(!h.call(${recordExpression},${key}))continue;`, `const ${itemValue}=${recordExpression}[${key}];`);
594
+ }
595
+ emitFalseCheck(itemGraph, itemGraph.result, itemValue, context, state);
596
+ state.chunks.push("}");
156
597
  }
157
598
  /**
158
599
  * @brief emit strict keys check.
600
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
159
601
  */
160
602
  function emitStrictKeysCheck(graph, object, keys, value, context, state) {
161
603
  const objectExpression = emitGraphExpression(graph, object, value, context, state);
162
- if (hasAllRequiredKeys(object, keys, state)) {
163
- state.chunks.push(`if(Object.getOwnPropertyNames(${objectExpression}).length!==${String(keys.length)}||Object.getOwnPropertySymbols(${objectExpression}).length!==0)return false;`);
604
+ if (isUncheckedMode(context)) {
605
+ return;
606
+ }
607
+ if (isUnsafeMode(context)) {
608
+ emitUnsafeStrictKeyLoop(objectExpression, keys, state);
609
+ return;
610
+ }
611
+ if (hasAllRequiredKeys(object, keys, objectExpression, state)) {
612
+ state.chunks.push(`if(Object.getOwnPropertyNames(${objectExpression}).length!==${String(keys.length)}||Object.getOwnPropertySymbols(${objectExpression}).length!==0)${failStatement(state)}`);
164
613
  return;
165
614
  }
166
615
  const present = `xs${String(state.temp)}`;
167
616
  state.temp += 1;
617
+ const length = `n${String(state.temp)}`;
618
+ state.temp += 1;
168
619
  const index = `i${String(state.temp)}`;
169
620
  state.temp += 1;
170
621
  const key = `key${String(state.temp)}`;
171
622
  state.temp += 1;
172
- state.chunks.push(`const ${present}=Reflect.ownKeys(${objectExpression});`, `for(let ${index}=0;${index}<${present}.length;${index}+=1){`, `const ${key}=${present}[${index}];`, `if(typeof ${key}!=="string"||!${keyMembershipExpression(key, keys, context)})return false;`, "}");
623
+ state.chunks.push(`const ${present}=Reflect.ownKeys(${objectExpression});`, `const ${length}=${present}.length;`, `for(let ${index}=0;${index}<${length};${index}+=1){`, `const ${key}=${present}[${index}];`, `if(typeof ${key}!=="string"||!${keyMembershipExpression(key, keys, context)})${failStatement(state)}`, "}");
173
624
  }
174
625
  /**
175
626
  * @brief emit or return.
627
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
176
628
  */
177
629
  function emitOrReturn(graph, ids, value, context, state) {
178
630
  for (let index = 0; index < ids.length; index += 1) {
@@ -181,133 +633,1244 @@ function emitOrReturn(graph, ids, value, context, state) {
181
633
  state.chunks.push(`if(${emitGraphExpression(graph, id, value, context, state)})return true;`);
182
634
  }
183
635
  }
184
- state.chunks.push("return false;");
636
+ state.chunks.push(failStatement(state));
185
637
  }
186
638
  /**
187
639
  * @brief emit discriminant dispatch return.
640
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
188
641
  */
189
- function emitDiscriminantDispatchReturn(graph, object, value, key, literals, schemas, context, state) {
190
- const objectExpression = emitGraphExpression(graph, object, value, context, state);
642
+ function emitDiscriminantDispatchReturn(graph, node, value, context, state) {
643
+ const objectExpression = emitGraphExpression(graph, node.value, value, context, state);
644
+ if (isUnsafeMode(context)) {
645
+ emitUnsafeDiscriminantDispatchReturn(node, objectExpression, context, state);
646
+ return;
647
+ }
191
648
  const descriptor = `d${String(state.temp)}`;
192
649
  state.temp += 1;
193
- state.chunks.push(`if(!o(${objectExpression}))return false;`, `const ${descriptor}=g(${objectExpression},${stringRef(context, key)});`, `if(${descriptor}===undefined||typeof ${descriptor}.value!=="string")return false;`, `switch(${descriptor}.value){`);
194
- for (let index = 0; index < schemas.length; index += 1) {
195
- const schema = schemas[index];
196
- const literal = literals[index];
197
- if (schema !== undefined && literal !== undefined) {
198
- state.chunks.push(`case ${stringRef(context, literal)}:return ${emitGraphFunction(schema, context)}(${objectExpression});`);
650
+ state.chunks.push(objectGuardStatement(objectExpression, state), `const ${descriptor}=gp(${objectExpression},${stringRef(context, node.key)});`, `if(${descriptor}===undefined||typeof ${descriptor}.value!=="string")${failStatement(state)}`, `switch(${descriptor}.value){`);
651
+ markKnownObject(objectExpression, state);
652
+ for (let index = 0; index < node.graphs.length; index += 1) {
653
+ const childGraph = node.graphs[index];
654
+ const literal = node.literals[index];
655
+ if (childGraph !== undefined && literal !== undefined &&
656
+ readGraphResultBoolean(childGraph) !== false) {
657
+ const branch = makeBranchEmitState(state);
658
+ const param = findParamNode(childGraph);
659
+ markKnownObject(objectExpression, branch);
660
+ if (param !== undefined) {
661
+ seedDataSlot(branch, param, node.key, objectExpression, descriptor, `${descriptor}.value`, literal);
662
+ }
663
+ emitFalseCheck(childGraph, childGraph.result, objectExpression, context, branch);
664
+ state.chunks.push(`case ${stringRef(context, literal)}:{`, ...branch.chunks, "return true;}");
665
+ if (branch.temp > state.temp) {
666
+ state.temp = branch.temp;
667
+ }
199
668
  }
200
669
  }
201
- state.chunks.push("default:return false;}");
670
+ state.chunks.push(`default:${failStatement(state)}}`);
202
671
  }
203
672
  /**
204
- * @brief emit graph expression.
673
+ * @brief Execute emit unsafe discriminant dispatch return.
674
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
205
675
  */
206
- function emitGraphExpression(graph, id, value, context, state) {
207
- const node = graph.nodes[id];
208
- if (node === undefined) {
209
- return "undefined";
210
- }
211
- switch (node.tag) {
212
- case NodeTag.Start:
213
- return "true";
214
- case NodeTag.Param:
215
- return value;
216
- case NodeTag.Const:
217
- return literalExpression(node.value, context);
218
- case NodeTag.GetProp:
219
- return state === undefined
220
- ? `gv(${emitGraphExpression(graph, node.object, value, context, state)},${stringRef(context, node.key)})`
221
- : emitDataValueSlot(graph, node.object, node.key, value, context, state);
222
- case NodeTag.IsString:
223
- return `(typeof ${emitGraphExpression(graph, node.value, value, context, state)}==="string")`;
224
- case NodeTag.IsNumber:
225
- return finiteNumberExpression(emitGraphExpression(graph, node.value, value, context, state));
226
- case NodeTag.IsBoolean:
227
- return `(typeof ${emitGraphExpression(graph, node.value, value, context, state)}==="boolean")`;
228
- case NodeTag.IsBigInt:
229
- return `(typeof ${emitGraphExpression(graph, node.value, value, context, state)}==="bigint")`;
230
- case NodeTag.IsSymbol:
231
- return `(typeof ${emitGraphExpression(graph, node.value, value, context, state)}==="symbol")`;
232
- case NodeTag.IsObject:
233
- return `o(${emitGraphExpression(graph, node.value, value, context, state)})`;
234
- case NodeTag.IsArray:
235
- return `Array.isArray(${emitGraphExpression(graph, node.value, value, context, state)})`;
236
- case NodeTag.IsUndefined:
237
- return `(${emitGraphExpression(graph, node.value, value, context, state)}===undefined)`;
238
- case NodeTag.IsNull:
239
- return `(${emitGraphExpression(graph, node.value, value, context, state)}===null)`;
240
- case NodeTag.IsInteger:
241
- return `Number.isInteger(${emitGraphExpression(graph, node.value, value, context, state)})`;
242
- case NodeTag.Not:
243
- return `(${emitGraphExpression(graph, node.value, value, context, state)}!==true)`;
244
- case NodeTag.Equals:
245
- return `Object.is(${emitGraphExpression(graph, node.left, value, context, state)},${emitGraphExpression(graph, node.right, value, context, state)})`;
246
- case NodeTag.Gte:
247
- return `(${emitGraphExpression(graph, node.left, value, context, state)}>=${emitGraphExpression(graph, node.right, value, context, state)})`;
248
- case NodeTag.Lte:
249
- return `(${emitGraphExpression(graph, node.left, value, context, state)}<=${emitGraphExpression(graph, node.right, value, context, state)})`;
250
- case NodeTag.StringMin:
251
- return `(${emitGraphExpression(graph, node.value, value, context, state)}.length>=${String(node.bound)})`;
252
- case NodeTag.StringMax:
253
- return `(${emitGraphExpression(graph, node.value, value, context, state)}.length<=${String(node.bound)})`;
254
- case NodeTag.Regex:
255
- return regexExpression(emitGraphExpression(graph, node.value, value, context, state), node.regex, context);
256
- case NodeTag.HasOwn:
257
- return `ho(${emitGraphExpression(graph, node.object, value, context, state)},${stringRef(context, node.key)})`;
258
- case NodeTag.HasOwnData:
259
- return state === undefined
260
- ? `hd(${emitGraphExpression(graph, node.object, value, context, state)},${stringRef(context, node.key)})`
261
- : hasDataExpression(graph, node.object, node.key, value, context, state);
262
- case NodeTag.StrictKeys:
263
- return `sk(${emitGraphExpression(graph, node.object, value, context, state)},k[${String(pushKeyset(context, node.keys))}])`;
264
- case NodeTag.ArrayEvery:
265
- return `ea(${emitGraphExpression(graph, node.value, value, context, state)},${emitGraphFunction(node.item, context)})`;
266
- case NodeTag.TupleItems:
267
- return emitTupleItemsExpression(emitGraphExpression(graph, node.value, value, context, state), node.items, context);
268
- case NodeTag.RecordEvery:
269
- return `er(${emitGraphExpression(graph, node.value, value, context, state)},${emitGraphFunction(node.item, context)})`;
270
- case NodeTag.DiscriminantDispatch:
271
- return emitDiscriminantDispatchExpression(emitGraphExpression(graph, node.value, value, context, state), node.key, node.literals, node.schemas, context);
272
- case NodeTag.SchemaCheck:
273
- return `d(${String(pushSchema(context, node.schema))},${emitGraphExpression(graph, node.value, value, context, state)})`;
274
- case NodeTag.And:
275
- return emitBooleanFoldExpression(graph, node.values, value, context, state, true);
276
- case NodeTag.Or:
277
- return emitBooleanFoldExpression(graph, node.values, value, context, state, false);
278
- case NodeTag.Return:
279
- return emitGraphExpression(graph, node.value, value, context, state);
676
+ function emitUnsafeDiscriminantDispatchReturn(node, objectExpression, context, state) {
677
+ const discriminant = `v${String(state.temp)}`;
678
+ state.temp += 1;
679
+ state.chunks.push(objectGuardStatement(objectExpression, state), `const ${discriminant}=${unsafePropertyReadExpression(objectExpression, node.key)};`, `if(typeof ${discriminant}!=="string")${failStatement(state)}`, `switch(${discriminant}){`);
680
+ markKnownObject(objectExpression, state);
681
+ for (let index = 0; index < node.graphs.length; index += 1) {
682
+ const childGraph = node.graphs[index];
683
+ const literal = node.literals[index];
684
+ if (childGraph !== undefined && literal !== undefined &&
685
+ readGraphResultBoolean(childGraph) !== false) {
686
+ const branch = makeBranchEmitState(state);
687
+ markKnownObject(objectExpression, branch);
688
+ emitFalseCheck(childGraph, childGraph.result, objectExpression, context, branch);
689
+ state.chunks.push(`case ${unsafeStringLiteralExpression(literal)}:{`, ...branch.chunks, "return true;}");
690
+ if (branch.temp > state.temp) {
691
+ state.temp = branch.temp;
692
+ }
693
+ }
280
694
  }
695
+ state.chunks.push(`default:${failStatement(state)}}`);
281
696
  }
282
697
  /**
283
- * @brief emit boolean fold expression.
698
+ * @brief emit primitive union return.
699
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
284
700
  */
285
- function emitBooleanFoldExpression(graph, ids, value, context, state, and) {
286
- const fallback = and ? "true" : "false";
287
- const operator = and ? "&&" : "||";
288
- const parts = [];
289
- for (let index = 0; index < ids.length; index += 1) {
290
- const id = ids[index];
291
- if (id !== undefined) {
292
- parts.push(emitGraphExpression(graph, id, value, context, undefined));
293
- }
294
- }
295
- if (parts.length === 0) {
296
- return fallback;
297
- }
298
- return `(${parts.join(operator)})`;
701
+ function emitPrimitiveUnionReturn(graph, node, value, context, state) {
702
+ const subjectExpression = emitGraphExpression(graph, node.value, value, context, state);
703
+ const subject = emitSubjectAlias(subjectExpression, state);
704
+ emitPrimitiveUnionTail(node, subject, context, state, "return true;");
299
705
  }
300
706
  /**
301
- * @brief emit data slot.
707
+ * @brief emit primitive union check.
708
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
302
709
  */
303
- function emitDataSlot(graph, object, key, value, context, state) {
304
- const cacheKey = dataSlotKey(object, key);
305
- const cached = state.dataSlots.get(cacheKey);
306
- if (cached !== undefined) {
307
- return cached;
308
- }
309
- const objectExpression = emitGraphExpression(graph, object, value, context, state);
310
- const descriptor = `d${String(state.temp)}`;
710
+ function emitPrimitiveUnionCheck(graph, node, value, context, state) {
711
+ const success = `us${String(state.temp)}`;
712
+ state.temp += 1;
713
+ const subjectExpression = emitGraphExpression(graph, node.value, value, context, state);
714
+ const subject = emitSubjectAlias(subjectExpression, state);
715
+ state.chunks.push(`${success}:{`);
716
+ emitPrimitiveUnionTail(node, subject, context, state, `break ${success};`);
717
+ state.chunks.push("}");
718
+ }
719
+ /**
720
+ * @brief emit primitive union function.
721
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
722
+ */
723
+ function emitPrimitiveUnionFunction(node, context) {
724
+ const name = `p${String(context.functions.length)}`;
725
+ const source = {
726
+ name,
727
+ body: ""
728
+ };
729
+ context.functions.push(source);
730
+ source.body = emitPrimitiveUnionBody(node, "v", context);
731
+ return name;
732
+ }
733
+ /**
734
+ * @brief emit primitive union body.
735
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
736
+ */
737
+ function emitPrimitiveUnionBody(node, value, context) {
738
+ const state = makeGraphEmitState();
739
+ emitPrimitiveUnionTail(node, value, context, state, "return true;");
740
+ return state.chunks.join("");
741
+ }
742
+ /**
743
+ * @brief emit primitive union tail.
744
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
745
+ */
746
+ function emitPrimitiveUnionTail(node, value, context, state, successStatement) {
747
+ const type = `ut${String(state.temp)}`;
748
+ state.temp += 1;
749
+ state.chunks.push(`const ${type}=typeof ${value};`);
750
+ for (let index = 0; index < node.graphs.length;) {
751
+ const mask = node.masks[index];
752
+ if (mask === undefined || mask === 0 || node.graphs[index] === undefined) {
753
+ index += 1;
754
+ continue;
755
+ }
756
+ const guard = primitiveUnionMaskExpression(mask, value, type);
757
+ if (guard === "false") {
758
+ index += 1;
759
+ continue;
760
+ }
761
+ const end = nextPrimitiveUnionMaskRun(node, index, mask);
762
+ if (end === index + 1) {
763
+ const graph = node.graphs[index];
764
+ if (graph !== undefined && readGraphResultBoolean(graph) !== false) {
765
+ const branch = makeBranchEmitState(state);
766
+ markUnionMaskRefinement(mask, value, branch);
767
+ emitFalseCheck(graph, graph.result, value, context, branch);
768
+ state.chunks.push(`if(${guard}){`, ...branch.chunks, successStatement, "}");
769
+ if (branch.temp > state.temp) {
770
+ state.temp = branch.temp;
771
+ }
772
+ }
773
+ index = end;
774
+ continue;
775
+ }
776
+ const runChunks = [];
777
+ for (let runIndex = index; runIndex < end; runIndex += 1) {
778
+ const graph = node.graphs[runIndex];
779
+ if (graph === undefined || readGraphResultBoolean(graph) === false) {
780
+ continue;
781
+ }
782
+ const label = `ub${String(state.temp)}`;
783
+ state.temp += 1;
784
+ const branch = makeFailureBranchEmitState(state, label);
785
+ markUnionMaskRefinement(mask, value, branch);
786
+ emitFalseCheck(graph, graph.result, value, context, branch);
787
+ if (branch.chunks.length === 0) {
788
+ runChunks.push(successStatement);
789
+ }
790
+ else {
791
+ runChunks.push(`${label}:{`, ...branch.chunks, successStatement, "}");
792
+ }
793
+ if (branch.temp > state.temp) {
794
+ state.temp = branch.temp;
795
+ }
796
+ }
797
+ if (runChunks.length !== 0) {
798
+ state.chunks.push(`if(${guard}){`, ...runChunks, "}");
799
+ }
800
+ index = end;
801
+ }
802
+ state.chunks.push(failStatement(state));
803
+ }
804
+ /**
805
+ * @brief emit union dispatch return.
806
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
807
+ */
808
+ function emitUnionDispatchReturn(graph, node, value, context, state) {
809
+ const subjectExpression = emitGraphExpression(graph, node.value, value, context, state);
810
+ const subject = emitSubjectAlias(subjectExpression, state);
811
+ emitUnionDispatchTail(node, subject, context, state);
812
+ }
813
+ /**
814
+ * @brief emit union dispatch function.
815
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
816
+ */
817
+ function emitUnionDispatchFunction(node, context) {
818
+ const name = `p${String(context.functions.length)}`;
819
+ const source = {
820
+ name,
821
+ body: ""
822
+ };
823
+ context.functions.push(source);
824
+ source.body = emitUnionDispatchBody(node, "v", context);
825
+ return name;
826
+ }
827
+ /**
828
+ * @brief emit union dispatch body.
829
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
830
+ */
831
+ function emitUnionDispatchBody(node, value, context) {
832
+ const state = makeGraphEmitState();
833
+ emitUnionDispatchTail(node, value, context, state);
834
+ return state.chunks.join("");
835
+ }
836
+ /**
837
+ * @brief emit union dispatch tail.
838
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
839
+ */
840
+ function emitUnionDispatchTail(node, value, context, state) {
841
+ const type = `ut${String(state.temp)}`;
842
+ state.temp += 1;
843
+ state.chunks.push(`const ${type}=typeof ${value};`);
844
+ for (let index = 0; index < node.graphs.length;) {
845
+ const mask = node.masks[index];
846
+ if (mask === undefined || mask === 0 || node.graphs[index] === undefined) {
847
+ index += 1;
848
+ continue;
849
+ }
850
+ const guard = unionMaskExpression(mask, value, type);
851
+ const end = nextUnionMaskRun(node, index, mask);
852
+ const runChunks = [];
853
+ for (let runIndex = index; runIndex < end; runIndex += 1) {
854
+ const graph = node.graphs[runIndex];
855
+ if (graph === undefined || readGraphResultBoolean(graph) === false) {
856
+ continue;
857
+ }
858
+ const label = `ub${String(state.temp)}`;
859
+ state.temp += 1;
860
+ const branch = makeFailureBranchEmitState(state, label);
861
+ markUnionMaskRefinement(mask, value, branch);
862
+ emitFalseCheck(graph, graph.result, value, context, branch);
863
+ if (branch.chunks.length === 0) {
864
+ runChunks.push("return true;");
865
+ }
866
+ else {
867
+ runChunks.push(`${label}:{`, ...branch.chunks, "return true;}");
868
+ }
869
+ if (branch.temp > state.temp) {
870
+ state.temp = branch.temp;
871
+ }
872
+ }
873
+ if (runChunks.length !== 0) {
874
+ if (guard === "true") {
875
+ state.chunks.push(...runChunks);
876
+ }
877
+ else {
878
+ state.chunks.push(`if(${guard}){`, ...runChunks, "}");
879
+ }
880
+ }
881
+ index = end;
882
+ }
883
+ state.chunks.push(failStatement(state));
884
+ }
885
+ /**
886
+ * @brief emit string and check.
887
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
888
+ */
889
+ function emitStringAndCheck(graph, ids, value, context, state) {
890
+ const plan = readStringAndPlan(graph, ids);
891
+ if (plan === undefined) {
892
+ return false;
893
+ }
894
+ const subject = emitGraphExpression(graph, plan.value, value, context, state);
895
+ if (isKnownPredicate(NodeTag.IsString, subject, state)) {
896
+ emitKnownStringAndPlan(plan, subject, context, state);
897
+ }
898
+ else {
899
+ const failures = [`typeof ${subject}!=="string"`];
900
+ pushStringPlanFailures(plan, subject, context, failures);
901
+ state.chunks.push(`if(${failures.join("||")})${failStatement(state)}`);
902
+ markKnownPredicate(NodeTag.IsString, subject, state);
903
+ }
904
+ return true;
905
+ }
906
+ /**
907
+ * @brief emit known string and plan.
908
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
909
+ */
910
+ function emitKnownStringAndPlan(plan, subject, context, state) {
911
+ const failures = [];
912
+ pushStringPlanFailures(plan, subject, context, failures);
913
+ if (failures.length !== 0) {
914
+ state.chunks.push(`if(${failures.join("||")})${failStatement(state)}`);
915
+ }
916
+ }
917
+ /**
918
+ * @brief Execute push string plan failures.
919
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
920
+ */
921
+ function pushStringPlanFailures(plan, subject, context, failures) {
922
+ if (plan.min !== undefined) {
923
+ failures.push(`${subject}.length<${String(plan.min)}`);
924
+ }
925
+ if (plan.max !== undefined) {
926
+ failures.push(`${subject}.length>${String(plan.max)}`);
927
+ }
928
+ const regexes = plan.regexes;
929
+ for (let index = 0; index < regexes.length; index += 1) {
930
+ const regex = regexes[index];
931
+ if (regex !== undefined) {
932
+ failures.push(`!${regexExpression(subject, regex, context)}`);
933
+ }
934
+ }
935
+ }
936
+ /**
937
+ * @brief Collapse string-related graph nodes into one emission plan.
938
+ * @param graph Graph being emitted.
939
+ * @param ids Candidate node ids from a boolean fold.
940
+ * @returns A plan when every node constrains the same subject, otherwise undefined.
941
+ * @details This recognizes the common string hot path and lets codegen emit one
942
+ * straight-line guard rather than bouncing through each predicate node.
943
+ */
944
+ function readStringAndPlan(graph, ids) {
945
+ let value;
946
+ let sawString = false;
947
+ let min;
948
+ let max;
949
+ const regexes = [];
950
+ for (let index = 0; index < ids.length; index += 1) {
951
+ const id = ids[index];
952
+ if (id === undefined) {
953
+ return undefined;
954
+ }
955
+ const node = graph.nodes[id];
956
+ if (node === undefined) {
957
+ return undefined;
958
+ }
959
+ switch (node.tag) {
960
+ case NodeTag.IsString:
961
+ value = readSameStringValue(value, node.value);
962
+ sawString = true;
963
+ break;
964
+ case NodeTag.StringMin:
965
+ value = readSameStringValue(value, node.value);
966
+ min = node.bound;
967
+ break;
968
+ case NodeTag.StringMax:
969
+ value = readSameStringValue(value, node.value);
970
+ max = node.bound;
971
+ break;
972
+ case NodeTag.Regex:
973
+ value = readSameStringValue(value, node.value);
974
+ regexes.push(node.regex);
975
+ break;
976
+ default:
977
+ return undefined;
978
+ }
979
+ if (value === undefined) {
980
+ return undefined;
981
+ }
982
+ }
983
+ if (!sawString || value === undefined) {
984
+ return undefined;
985
+ }
986
+ return {
987
+ value,
988
+ min,
989
+ max,
990
+ regexes
991
+ };
992
+ }
993
+ /**
994
+ * @brief Maintain the single-subject invariant for string plan folding.
995
+ * @details Generated-source helpers keep the side-table ABI and JavaScript source shape
996
+ * stable across runtime and AOT emission.
997
+ * @param current Previously observed subject id.
998
+ * @param next Subject id from the next string predicate.
999
+ * @returns The shared subject id, or undefined when predicates target different values.
1000
+ */
1001
+ function readSameStringValue(current, next) {
1002
+ return current === undefined || current === next ? next : undefined;
1003
+ }
1004
+ /**
1005
+ * @brief emit number and check.
1006
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
1007
+ */
1008
+ function emitNumberAndCheck(graph, ids, value, context, state) {
1009
+ const plan = readNumberAndPlan(graph, ids);
1010
+ if (plan === undefined) {
1011
+ return false;
1012
+ }
1013
+ const subject = emitGraphExpression(graph, plan.value, value, context, state);
1014
+ const failures = [];
1015
+ if (plan.integer) {
1016
+ failures.push(`!Number.isInteger(${subject})`);
1017
+ markKnownPredicate(NodeTag.IsNumber, subject, state);
1018
+ }
1019
+ else if (!isKnownPredicate(NodeTag.IsNumber, subject, state)) {
1020
+ failures.push(`!${finiteNumberExpression(subject, state)}`);
1021
+ markKnownPredicate(NodeTag.IsNumber, subject, state);
1022
+ }
1023
+ if (plan.gte !== undefined) {
1024
+ failures.push(`${subject}<${finiteNumberLiteralExpression(plan.gte)}`);
1025
+ }
1026
+ if (plan.lte !== undefined) {
1027
+ failures.push(`${subject}>${finiteNumberLiteralExpression(plan.lte)}`);
1028
+ }
1029
+ if (failures.length !== 0) {
1030
+ state.chunks.push(`if(${failures.join("||")})${failStatement(state)}`);
1031
+ }
1032
+ return true;
1033
+ }
1034
+ /**
1035
+ * @brief Collapse numeric graph predicates into one emission plan.
1036
+ * @details This helper preserves a code-generation invariant that would be easy to obscure inside the main emitter loop.
1037
+ * @param graph Graph being emitted.
1038
+ * @param ids Candidate node ids from a boolean fold.
1039
+ * @returns A numeric plan when all constraints share one subject.
1040
+ */
1041
+ function readNumberAndPlan(graph, ids) {
1042
+ let value;
1043
+ let sawGuard = false;
1044
+ let integer = false;
1045
+ let gte;
1046
+ let lte;
1047
+ for (let index = 0; index < ids.length; index += 1) {
1048
+ const id = ids[index];
1049
+ if (id === undefined) {
1050
+ return undefined;
1051
+ }
1052
+ const node = graph.nodes[id];
1053
+ if (node === undefined) {
1054
+ return undefined;
1055
+ }
1056
+ switch (node.tag) {
1057
+ case NodeTag.IsNumber:
1058
+ value = readSameNumberValue(value, node.value);
1059
+ sawGuard = true;
1060
+ break;
1061
+ case NodeTag.IsInteger:
1062
+ value = readSameNumberValue(value, node.value);
1063
+ sawGuard = true;
1064
+ integer = true;
1065
+ break;
1066
+ case NodeTag.Gte: {
1067
+ const bound = readFiniteNumberConst(graph, node.right);
1068
+ if (bound === undefined) {
1069
+ return undefined;
1070
+ }
1071
+ value = readSameNumberValue(value, node.left);
1072
+ gte = gte === undefined || bound > gte ? bound : gte;
1073
+ break;
1074
+ }
1075
+ case NodeTag.Lte: {
1076
+ const bound = readFiniteNumberConst(graph, node.right);
1077
+ if (bound === undefined) {
1078
+ return undefined;
1079
+ }
1080
+ value = readSameNumberValue(value, node.left);
1081
+ lte = lte === undefined || bound < lte ? bound : lte;
1082
+ break;
1083
+ }
1084
+ default:
1085
+ return undefined;
1086
+ }
1087
+ if (value === undefined) {
1088
+ return undefined;
1089
+ }
1090
+ }
1091
+ if (!sawGuard || value === undefined) {
1092
+ return undefined;
1093
+ }
1094
+ return {
1095
+ value,
1096
+ integer,
1097
+ gte,
1098
+ lte
1099
+ };
1100
+ }
1101
+ /**
1102
+ * @brief Maintain the single-subject invariant for numeric plan folding.
1103
+ * @details Generated-source helpers keep the side-table ABI and JavaScript source shape
1104
+ * stable across runtime and AOT emission.
1105
+ * @param current Previously observed numeric subject id.
1106
+ * @param next Subject id from the next numeric predicate.
1107
+ * @returns The shared subject id, or undefined on subject drift.
1108
+ */
1109
+ function readSameNumberValue(current, next) {
1110
+ return current === undefined || current === next ? next : undefined;
1111
+ }
1112
+ /**
1113
+ * @brief Read a finite numeric bound from a Const node.
1114
+ * @details This helper preserves a code-generation invariant that would be easy to obscure inside the main emitter loop.
1115
+ * @param graph Graph containing the bound node.
1116
+ * @param id Node id expected to reference a numeric constant.
1117
+ * @returns Finite numeric value, or undefined for unsupported bounds.
1118
+ */
1119
+ function readFiniteNumberConst(graph, id) {
1120
+ const node = graph.nodes[id];
1121
+ return node?.tag === NodeTag.Const &&
1122
+ typeof node.value === "number" &&
1123
+ Number.isFinite(node.value)
1124
+ ? node.value
1125
+ : undefined;
1126
+ }
1127
+ /**
1128
+ * @brief finite number literal expression.
1129
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
1130
+ */
1131
+ function finiteNumberLiteralExpression(value) {
1132
+ if (Object.is(value, -0)) {
1133
+ return "-0";
1134
+ }
1135
+ return String(value);
1136
+ }
1137
+ /**
1138
+ * @brief next union mask run.
1139
+ * @details Run and mask helpers group adjacent dispatch domains so emitted union code stays compact and predictable for V8.
1140
+ */
1141
+ function nextUnionMaskRun(node, start, mask) {
1142
+ let index = start + 1;
1143
+ while (index < node.masks.length && node.masks[index] === mask) {
1144
+ index += 1;
1145
+ }
1146
+ return index;
1147
+ }
1148
+ /**
1149
+ * @brief next primitive union mask run.
1150
+ * @details Run and mask helpers group adjacent dispatch domains so emitted union code stays compact and predictable for V8.
1151
+ */
1152
+ function nextPrimitiveUnionMaskRun(node, start, mask) {
1153
+ let index = start + 1;
1154
+ while (index < node.masks.length && node.masks[index] === mask) {
1155
+ index += 1;
1156
+ }
1157
+ return index;
1158
+ }
1159
+ /**
1160
+ * @brief primitive union mask expression.
1161
+ * @details Run and mask helpers group adjacent dispatch domains so emitted union code stays compact and predictable for V8.
1162
+ */
1163
+ function primitiveUnionMaskExpression(mask, value, type) {
1164
+ const parts = [];
1165
+ if ((mask & 1) !== 0) {
1166
+ parts.push(`${type}==="string"`);
1167
+ }
1168
+ if ((mask & 2) !== 0) {
1169
+ parts.push(`${type}==="number"`);
1170
+ }
1171
+ if ((mask & 4) !== 0) {
1172
+ parts.push(`${type}==="boolean"`);
1173
+ }
1174
+ if ((mask & 8) !== 0) {
1175
+ parts.push(`${type}==="bigint"`);
1176
+ }
1177
+ if ((mask & 16) !== 0) {
1178
+ parts.push(`${type}==="symbol"`);
1179
+ }
1180
+ if ((mask & 32) !== 0) {
1181
+ parts.push(`${type}==="undefined"`);
1182
+ }
1183
+ if ((mask & 64) !== 0) {
1184
+ parts.push(`${value}===null`);
1185
+ }
1186
+ if (parts.length === 0) {
1187
+ return "false";
1188
+ }
1189
+ if (parts.length === 7) {
1190
+ return "true";
1191
+ }
1192
+ if (parts.length === 1) {
1193
+ return parts[0] ?? "false";
1194
+ }
1195
+ return `(${parts.join("||")})`;
1196
+ }
1197
+ /**
1198
+ * @brief union mask expression.
1199
+ * @details Run and mask helpers group adjacent dispatch domains so emitted union code stays compact and predictable for V8.
1200
+ */
1201
+ function unionMaskExpression(mask, value, type) {
1202
+ const parts = [];
1203
+ if ((mask & 1) !== 0) {
1204
+ parts.push(`${type}==="string"`);
1205
+ }
1206
+ if ((mask & 2) !== 0) {
1207
+ parts.push(`${type}==="number"`);
1208
+ }
1209
+ if ((mask & 4) !== 0) {
1210
+ parts.push(`${type}==="boolean"`);
1211
+ }
1212
+ if ((mask & 8) !== 0) {
1213
+ parts.push(`${type}==="bigint"`);
1214
+ }
1215
+ if ((mask & 16) !== 0) {
1216
+ parts.push(`${type}==="symbol"`);
1217
+ }
1218
+ if ((mask & 32) !== 0) {
1219
+ parts.push(`${type}==="undefined"`);
1220
+ }
1221
+ if ((mask & 64) !== 0) {
1222
+ parts.push(`${value}===null`);
1223
+ }
1224
+ if ((mask & 128) !== 0) {
1225
+ parts.push(`Array.isArray(${value})`);
1226
+ }
1227
+ if ((mask & 256) !== 0) {
1228
+ parts.push(`(${type}==="object"&&${value}!==null&&!Array.isArray(${value}))`);
1229
+ }
1230
+ if ((mask & 512) !== 0) {
1231
+ parts.push(`${type}==="function"`);
1232
+ }
1233
+ if (parts.length === 0) {
1234
+ return "false";
1235
+ }
1236
+ if (parts.length === 10) {
1237
+ return "true";
1238
+ }
1239
+ if (parts.length === 1) {
1240
+ return parts[0] ?? "false";
1241
+ }
1242
+ return `(${parts.join("||")})`;
1243
+ }
1244
+ /**
1245
+ * @brief emit graph expression.
1246
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
1247
+ */
1248
+ function emitGraphExpression(graph, id, value, context, state) {
1249
+ const node = graph.nodes[id];
1250
+ if (node === undefined) {
1251
+ return "undefined";
1252
+ }
1253
+ switch (node.tag) {
1254
+ case NodeTag.Start:
1255
+ return "true";
1256
+ case NodeTag.Param:
1257
+ return value;
1258
+ case NodeTag.Const:
1259
+ return literalExpression(node.value, context);
1260
+ case NodeTag.GetProp:
1261
+ return state === undefined
1262
+ ? `gv(${emitGraphExpression(graph, node.object, value, context, state)},${stringRef(context, node.key)})`
1263
+ : emitDataValueSlot(graph, node.object, node.key, value, context, state);
1264
+ case NodeTag.IsString:
1265
+ return unaryPredicateExpression(graph, node.value, NodeTag.IsString, value, context, state, (subject) => `(typeof ${subject}==="string")`);
1266
+ case NodeTag.IsNumber:
1267
+ return numberPredicateExpression(graph, node.value, value, context, state);
1268
+ case NodeTag.IsBoolean:
1269
+ return unaryPredicateExpression(graph, node.value, NodeTag.IsBoolean, value, context, state, (subject) => `(typeof ${subject}==="boolean")`);
1270
+ case NodeTag.IsBigInt:
1271
+ return unaryPredicateExpression(graph, node.value, NodeTag.IsBigInt, value, context, state, (subject) => `(typeof ${subject}==="bigint")`);
1272
+ case NodeTag.IsSymbol:
1273
+ return unaryPredicateExpression(graph, node.value, NodeTag.IsSymbol, value, context, state, (subject) => `(typeof ${subject}==="symbol")`);
1274
+ case NodeTag.IsObject:
1275
+ return unaryPredicateExpression(graph, node.value, NodeTag.IsObject, value, context, state, (subject) => `o(${subject})`);
1276
+ case NodeTag.IsArray:
1277
+ return unaryPredicateExpression(graph, node.value, NodeTag.IsArray, value, context, state, (subject) => `Array.isArray(${subject})`);
1278
+ case NodeTag.IsUndefined:
1279
+ return unaryPredicateExpression(graph, node.value, NodeTag.IsUndefined, value, context, state, (subject) => `(${subject}===undefined)`);
1280
+ case NodeTag.IsNull:
1281
+ return unaryPredicateExpression(graph, node.value, NodeTag.IsNull, value, context, state, (subject) => `(${subject}===null)`);
1282
+ case NodeTag.IsInteger:
1283
+ return `Number.isInteger(${emitGraphExpression(graph, node.value, value, context, state)})`;
1284
+ case NodeTag.Not:
1285
+ return `(${emitGraphExpression(graph, node.value, value, context, state)}!==true)`;
1286
+ case NodeTag.Equals:
1287
+ return emitEqualsExpression(graph, node.left, node.right, value, context, state);
1288
+ case NodeTag.Gte:
1289
+ return `(${emitGraphExpression(graph, node.left, value, context, state)}>=${emitGraphExpression(graph, node.right, value, context, state)})`;
1290
+ case NodeTag.Lte:
1291
+ return `(${emitGraphExpression(graph, node.left, value, context, state)}<=${emitGraphExpression(graph, node.right, value, context, state)})`;
1292
+ case NodeTag.StringMin:
1293
+ return `(${emitGraphExpression(graph, node.value, value, context, state)}.length>=${String(node.bound)})`;
1294
+ case NodeTag.StringMax:
1295
+ return `(${emitGraphExpression(graph, node.value, value, context, state)}.length<=${String(node.bound)})`;
1296
+ case NodeTag.Regex:
1297
+ return regexExpression(emitGraphExpression(graph, node.value, value, context, state), node.regex, context);
1298
+ case NodeTag.HasOwn:
1299
+ return `ho(${emitGraphExpression(graph, node.object, value, context, state)},${stringRef(context, node.key)})`;
1300
+ case NodeTag.HasOwnData:
1301
+ return state === undefined
1302
+ ? `hd(${emitGraphExpression(graph, node.object, value, context, state)},${stringRef(context, node.key)})`
1303
+ : hasDataExpression(graph, node.object, node.key, value, context, state);
1304
+ case NodeTag.StrictKeys:
1305
+ return `sk(${emitGraphExpression(graph, node.object, value, context, state)},k[${String(pushKeyset(context, node.keys))}])`;
1306
+ case NodeTag.ArrayEvery:
1307
+ /*
1308
+ * Expression-mode code uses compact helpers. `eu` is the present-key
1309
+ * variant; `ea` is the dense logical-slot variant.
1310
+ */
1311
+ return emitArrayEveryExpression(emitGraphExpression(graph, node.value, value, context, state), node, context);
1312
+ case NodeTag.TupleItems:
1313
+ return emitTupleItemsExpression(emitGraphExpression(graph, node.value, value, context, state), node.itemGraphs, context);
1314
+ case NodeTag.RecordEvery:
1315
+ return `er(${emitGraphExpression(graph, node.value, value, context, state)},${emitGraphChildFunction(node.itemGraph, context)})`;
1316
+ case NodeTag.DiscriminantDispatch:
1317
+ return emitDiscriminantDispatchExpression(emitGraphExpression(graph, node.value, value, context, state), node, context);
1318
+ case NodeTag.ObjectShape:
1319
+ return `${emitObjectShapeFunction(node, context)}(${emitGraphExpression(graph, node.value, value, context, state)})`;
1320
+ case NodeTag.UnionDispatch:
1321
+ return `${emitUnionDispatchFunction(node, context)}(${emitGraphExpression(graph, node.value, value, context, state)})`;
1322
+ case NodeTag.PrimitiveUnion:
1323
+ return `${emitPrimitiveUnionFunction(node, context)}(${emitGraphExpression(graph, node.value, value, context, state)})`;
1324
+ case NodeTag.SchemaCheck:
1325
+ return `d(${String(pushSchema(context, node.schema))},${emitGraphExpression(graph, node.value, value, context, state)})`;
1326
+ case NodeTag.And:
1327
+ return emitBooleanFoldExpression(graph, node.values, value, context, state, true);
1328
+ case NodeTag.Or:
1329
+ return emitBooleanFoldExpression(graph, node.values, value, context, state, false);
1330
+ case NodeTag.Return:
1331
+ return emitGraphExpression(graph, node.value, value, context, state);
1332
+ }
1333
+ }
1334
+ /**
1335
+ * @brief emit object shape function.
1336
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
1337
+ */
1338
+ function emitObjectShapeFunction(node, context) {
1339
+ const name = `p${String(context.functions.length)}`;
1340
+ const source = {
1341
+ name,
1342
+ body: ""
1343
+ };
1344
+ context.functions.push(source);
1345
+ source.body = emitObjectShapeBody(node, "v", context);
1346
+ return name;
1347
+ }
1348
+ /**
1349
+ * @brief emit object shape body.
1350
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
1351
+ */
1352
+ function emitObjectShapeBody(node, value, context) {
1353
+ const state = makeGraphEmitState();
1354
+ emitObjectGuard(value, state);
1355
+ const emittedStrictKeyCount = emitEarlyStrictKeyCount(node, value, context, state);
1356
+ emitObjectShapeEntries(node, value, context, state);
1357
+ emitObjectShapeCatchall(node, value, context, state);
1358
+ if (!emittedStrictKeyCount) {
1359
+ emitObjectShapeStrictKeys(node, value, context, state);
1360
+ }
1361
+ state.chunks.push("return true;");
1362
+ return state.chunks.join("");
1363
+ }
1364
+ /**
1365
+ * @brief emit object shape check.
1366
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
1367
+ */
1368
+ function emitObjectShapeCheck(graph, node, value, context, state) {
1369
+ const objectExpression = emitGraphExpression(graph, node.value, value, context, state);
1370
+ emitObjectGuard(objectExpression, state);
1371
+ const emittedStrictKeyCount = emitEarlyStrictKeyCount(node, objectExpression, context, state);
1372
+ emitObjectShapeEntries(node, objectExpression, context, state);
1373
+ emitObjectShapeCatchall(node, objectExpression, context, state);
1374
+ if (!emittedStrictKeyCount) {
1375
+ emitObjectShapeStrictKeys(node, objectExpression, context, state);
1376
+ }
1377
+ }
1378
+ /**
1379
+ * @brief Execute emit early strict key count.
1380
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
1381
+ */
1382
+ function emitEarlyStrictKeyCount(node, objectExpression, context, state) {
1383
+ if (node.mode !== ObjectModeTag.Strict ||
1384
+ node.catchall !== undefined ||
1385
+ !node.allRequired ||
1386
+ isUnsafeMode(context)) {
1387
+ return false;
1388
+ }
1389
+ state.chunks.push(`if(Object.getOwnPropertyNames(${objectExpression}).length!==${String(node.entries.length)}||Object.getOwnPropertySymbols(${objectExpression}).length!==0)${failStatement(state)}`);
1390
+ return true;
1391
+ }
1392
+ /**
1393
+ * @brief Emit catchall validation for undeclared own object keys.
1394
+ * @param node Object shape node with optional catchall graph.
1395
+ * @param objectExpression JavaScript object expression already proven object.
1396
+ * @param context Shared emission context.
1397
+ * @param state Mutable graph emitter state.
1398
+ */
1399
+ function emitObjectShapeCatchall(node, objectExpression, context, state) {
1400
+ if (node.catchallGraph === undefined) {
1401
+ return;
1402
+ }
1403
+ if (isUnsafeMode(context)) {
1404
+ emitUnsafeObjectShapeCatchall(node, objectExpression, context, state);
1405
+ return;
1406
+ }
1407
+ const keys = `xs${String(state.temp)}`;
1408
+ state.temp += 1;
1409
+ const length = `n${String(state.temp)}`;
1410
+ state.temp += 1;
1411
+ const index = `i${String(state.temp)}`;
1412
+ state.temp += 1;
1413
+ const key = `key${String(state.temp)}`;
1414
+ state.temp += 1;
1415
+ const descriptor = `d${String(state.temp)}`;
1416
+ state.temp += 1;
1417
+ const itemValue = `v${String(state.temp)}`;
1418
+ state.temp += 1;
1419
+ state.chunks.push(`const ${keys}=Reflect.ownKeys(${objectExpression});`, `const ${length}=${keys}.length;`, `for(let ${index}=0;${index}<${length};${index}+=1){`, `const ${key}=${keys}[${index}];`, `if(typeof ${key}==="string"&&${keyMembershipExpression(key, node.keys, context)})continue;`, `const ${descriptor}=gp(${objectExpression},${key});`, `if(${descriptor}===undefined||!h.call(${descriptor},"value"))${failStatement(state)}`, `const ${itemValue}=${descriptor}.value;`);
1420
+ emitFalseCheck(node.catchallGraph, node.catchallGraph.result, itemValue, context, state);
1421
+ state.chunks.push("}");
1422
+ }
1423
+ /**
1424
+ * @brief Emit unsafe catchall validation for undeclared own object keys.
1425
+ * @param node Object shape node with catchall graph.
1426
+ * @param objectExpression JavaScript object expression already proven object.
1427
+ * @param context Shared emission context.
1428
+ * @param state Mutable graph emitter state.
1429
+ */
1430
+ function emitUnsafeObjectShapeCatchall(node, objectExpression, context, state) {
1431
+ if (node.catchallGraph === undefined) {
1432
+ return;
1433
+ }
1434
+ const keys = `xs${String(state.temp)}`;
1435
+ state.temp += 1;
1436
+ const length = `n${String(state.temp)}`;
1437
+ state.temp += 1;
1438
+ const index = `i${String(state.temp)}`;
1439
+ state.temp += 1;
1440
+ const key = `key${String(state.temp)}`;
1441
+ state.temp += 1;
1442
+ const itemValue = `v${String(state.temp)}`;
1443
+ state.temp += 1;
1444
+ state.chunks.push(`const ${keys}=Reflect.ownKeys(${objectExpression});`, `const ${length}=${keys}.length;`, `for(let ${index}=0;${index}<${length};${index}+=1){`, `const ${key}=${keys}[${index}];`, `if(typeof ${key}==="string"&&${unsafeKeyMembershipExpression(key, node.keys)})continue;`, `const ${itemValue}=${objectExpression}[${key}];`);
1445
+ emitFalseCheck(node.catchallGraph, node.catchallGraph.result, itemValue, context, state);
1446
+ state.chunks.push("}");
1447
+ }
1448
+ /**
1449
+ * @brief emit object shape entries.
1450
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
1451
+ */
1452
+ function emitObjectShapeEntries(node, objectExpression, context, state) {
1453
+ if (isUnsafeMode(context)) {
1454
+ emitUnsafeObjectShapeEntries(node, objectExpression, context, state);
1455
+ return;
1456
+ }
1457
+ const entries = scheduleObjectShapeEntries(node.entries);
1458
+ for (let index = 0; index < entries.length; index += 1) {
1459
+ const entry = entries[index];
1460
+ if (entry === undefined) {
1461
+ state.chunks.push(failStatement(state));
1462
+ continue;
1463
+ }
1464
+ const cacheKey = dataSlotKey(node.value, entry.key, objectExpression);
1465
+ const slot = emitDataSlotForExpression(node.value, entry.key, objectExpression, context, state);
1466
+ if (entry.presence === PresenceTag.Optional) {
1467
+ const rejectsUndefined = !schemaCanAcceptUndefined(entry.schema);
1468
+ state.chunks.push(`if(${slot.descriptor}!==undefined){`);
1469
+ if (!rejectsUndefined) {
1470
+ state.chunks.push(`if(!h.call(${slot.descriptor},"value"))${failStatement(state)}`);
1471
+ }
1472
+ state.dataGuards.add(cacheKey);
1473
+ const itemValue = emitDataSlotValue(cacheKey, slot, state);
1474
+ emitFalseCheck(entry.graph, entry.graph.result, itemValue, context, state);
1475
+ state.chunks.push(`}else if(h.call(${objectExpression},${stringRef(context, entry.key)}))${failStatement(state)}`);
1476
+ }
1477
+ else {
1478
+ if (!state.dataGuards.has(cacheKey)) {
1479
+ const rejectsUndefined = !schemaCanAcceptUndefined(entry.schema);
1480
+ state.chunks.push(rejectsUndefined
1481
+ ? `if(${slot.descriptor}===undefined)${failStatement(state)}`
1482
+ : `if(${slot.descriptor}===undefined||!h.call(${slot.descriptor},"value"))${failStatement(state)}`);
1483
+ state.dataGuards.add(cacheKey);
1484
+ }
1485
+ if (isKnownLiteralDataSlot(entry.schema, cacheKey, state)) {
1486
+ continue;
1487
+ }
1488
+ const itemValue = emitDataSlotValue(cacheKey, slot, state);
1489
+ emitFalseCheck(entry.graph, entry.graph.result, itemValue, context, state);
1490
+ }
1491
+ }
1492
+ }
1493
+ /**
1494
+ * @brief Schedule pure object fields for predicate-only fast failure.
1495
+ * @details SchemaCheck nodes can run user code through refine or lazy fallback,
1496
+ * so they act as barriers. Pure fields between barriers may be reordered because
1497
+ * boolean validation has no diagnostic ordering contract.
1498
+ * @param entries Object shape entries in schema order.
1499
+ * @returns Entry view ordered for V8-friendly predicate emission.
1500
+ */
1501
+ function scheduleObjectShapeEntries(entries) {
1502
+ if (entries.length < 2) {
1503
+ return entries;
1504
+ }
1505
+ const scheduled = [];
1506
+ let index = 0;
1507
+ while (index < entries.length) {
1508
+ const entry = entries[index];
1509
+ if (entry === undefined) {
1510
+ index += 1;
1511
+ continue;
1512
+ }
1513
+ if (!isSchedulableObjectShapeEntry(entry)) {
1514
+ scheduled.push(entry);
1515
+ index += 1;
1516
+ continue;
1517
+ }
1518
+ const run = [];
1519
+ while (index < entries.length) {
1520
+ const candidate = entries[index];
1521
+ if (candidate === undefined || !isSchedulableObjectShapeEntry(candidate)) {
1522
+ break;
1523
+ }
1524
+ run.push({
1525
+ entry: candidate,
1526
+ order: index
1527
+ });
1528
+ index += 1;
1529
+ }
1530
+ run.sort(compareScheduledObjectShapeEntries);
1531
+ for (let runIndex = 0; runIndex < run.length; runIndex += 1) {
1532
+ const scheduledEntry = run[runIndex];
1533
+ if (scheduledEntry !== undefined) {
1534
+ scheduled.push(scheduledEntry.entry);
1535
+ }
1536
+ }
1537
+ }
1538
+ return scheduled;
1539
+ }
1540
+ /**
1541
+ * @brief Compare two object entries for predicate emission.
1542
+ * @details Required fields can fail on absence and are therefore tested before
1543
+ * optional fields. Within the same presence class, cheaper child graphs run
1544
+ * first so invalid objects fail before arrays, records, or regex-heavy checks.
1545
+ * @param left Left scheduled entry.
1546
+ * @param right Right scheduled entry.
1547
+ * @returns Negative when left should be emitted before right.
1548
+ */
1549
+ function compareScheduledObjectShapeEntries(left, right) {
1550
+ const leftPresence = objectShapePresenceCost(left.entry);
1551
+ const rightPresence = objectShapePresenceCost(right.entry);
1552
+ if (leftPresence !== rightPresence) {
1553
+ return leftPresence - rightPresence;
1554
+ }
1555
+ const leftCost = estimateGraphCost(left.entry.graph);
1556
+ const rightCost = estimateGraphCost(right.entry.graph);
1557
+ if (leftCost !== rightCost) {
1558
+ return leftCost - rightCost;
1559
+ }
1560
+ return left.order - right.order;
1561
+ }
1562
+ /**
1563
+ * @brief Return whether an object entry may move within its pure run.
1564
+ * @details SchemaCheck is the IR boundary for user predicates and lazy
1565
+ * resolution. Keeping those entries fixed preserves observable user-code order.
1566
+ * @param entry Object shape entry under consideration.
1567
+ * @returns True when the entry graph has no opaque runtime callback.
1568
+ */
1569
+ function isSchedulableObjectShapeEntry(entry) {
1570
+ return !graphContainsSchemaCheck(entry.graph);
1571
+ }
1572
+ /**
1573
+ * @brief Estimate object field presence cost.
1574
+ * @details Required fields receive the lower rank because missing required keys
1575
+ * are common fast-fail cases and optional absence usually succeeds.
1576
+ * @param entry Object shape entry.
1577
+ * @returns Numeric rank used before graph cost.
1578
+ */
1579
+ function objectShapePresenceCost(entry) {
1580
+ return entry.presence === PresenceTag.Optional ? 1_000 : 0;
1581
+ }
1582
+ /**
1583
+ * @brief Estimate predicate cost for one child graph.
1584
+ * @details The values are intentionally coarse. The scheduler only needs stable
1585
+ * buckets that keep scalar checks before regexes and bounded loops.
1586
+ * @param graph Child graph emitted for one object field.
1587
+ * @returns Relative cost used by Tide scheduling.
1588
+ */
1589
+ function estimateGraphCost(graph) {
1590
+ let cost = 0;
1591
+ for (let index = 0; index < graph.nodes.length; index += 1) {
1592
+ const node = graph.nodes[index];
1593
+ if (node !== undefined) {
1594
+ cost += estimateNodeCost(node);
1595
+ }
1596
+ }
1597
+ return cost;
1598
+ }
1599
+ /**
1600
+ * @brief Estimate one node's predicate cost.
1601
+ * @details Compile-time scheduling only needs relative ordering, so the weights
1602
+ * favor V8-friendly scalar branches and push bounded or dynamic loops later.
1603
+ * @param node Graph node inspected by the scheduler.
1604
+ * @returns Relative node cost.
1605
+ */
1606
+ function estimateNodeCost(node) {
1607
+ switch (node.tag) {
1608
+ case NodeTag.Start:
1609
+ case NodeTag.Param:
1610
+ case NodeTag.Const:
1611
+ case NodeTag.Return:
1612
+ return 0;
1613
+ case NodeTag.IsString:
1614
+ case NodeTag.IsNumber:
1615
+ case NodeTag.IsBoolean:
1616
+ case NodeTag.IsBigInt:
1617
+ case NodeTag.IsSymbol:
1618
+ case NodeTag.IsObject:
1619
+ case NodeTag.IsArray:
1620
+ case NodeTag.IsUndefined:
1621
+ case NodeTag.IsNull:
1622
+ case NodeTag.IsInteger:
1623
+ case NodeTag.StringMin:
1624
+ case NodeTag.StringMax:
1625
+ case NodeTag.Gte:
1626
+ case NodeTag.Lte:
1627
+ case NodeTag.Equals:
1628
+ return 1;
1629
+ case NodeTag.Regex:
1630
+ return 8;
1631
+ case NodeTag.PrimitiveUnion:
1632
+ case NodeTag.UnionDispatch:
1633
+ case NodeTag.DiscriminantDispatch:
1634
+ case NodeTag.ObjectShape:
1635
+ case NodeTag.TupleItems:
1636
+ return 16;
1637
+ case NodeTag.ArrayEvery:
1638
+ case NodeTag.RecordEvery:
1639
+ return 64;
1640
+ case NodeTag.SchemaCheck:
1641
+ return 128;
1642
+ default:
1643
+ return 2;
1644
+ }
1645
+ }
1646
+ /**
1647
+ * @brief Check whether a graph can enter user-controlled runtime logic.
1648
+ * @details Refine and lazy schemas lower to SchemaCheck nodes. Treating them as
1649
+ * scheduling barriers prevents pure field reordering from hiding side effects.
1650
+ * @param graph Child graph scanned for opaque checks.
1651
+ * @returns True when the graph contains a SchemaCheck node at some depth.
1652
+ */
1653
+ function graphContainsSchemaCheck(graph) {
1654
+ for (let index = 0; index < graph.nodes.length; index += 1) {
1655
+ const node = graph.nodes[index];
1656
+ if (node === undefined) {
1657
+ continue;
1658
+ }
1659
+ if (node.tag === NodeTag.SchemaCheck) {
1660
+ return true;
1661
+ }
1662
+ if (nodeContainsSchemaCheck(node)) {
1663
+ return true;
1664
+ }
1665
+ }
1666
+ return false;
1667
+ }
1668
+ /**
1669
+ * @brief Scan nested graphs owned by one node.
1670
+ * @details Composite IR nodes store child graphs by value, so barrier discovery
1671
+ * must recurse through those embedded validation graphs.
1672
+ * @param node Node whose embedded graphs are inspected.
1673
+ * @returns True when a nested graph contains SchemaCheck.
1674
+ */
1675
+ function nodeContainsSchemaCheck(node) {
1676
+ switch (node.tag) {
1677
+ case NodeTag.ArrayEvery:
1678
+ case NodeTag.RecordEvery:
1679
+ return graphContainsSchemaCheck(node.itemGraph);
1680
+ case NodeTag.TupleItems:
1681
+ return graphArrayContainsSchemaCheck(node.itemGraphs);
1682
+ case NodeTag.ObjectShape:
1683
+ return objectEntriesContainSchemaCheck(node.entries);
1684
+ case NodeTag.DiscriminantDispatch:
1685
+ case NodeTag.UnionDispatch:
1686
+ case NodeTag.PrimitiveUnion:
1687
+ return graphArrayContainsSchemaCheck(node.graphs);
1688
+ default:
1689
+ return false;
1690
+ }
1691
+ }
1692
+ /**
1693
+ * @brief Scan a graph vector for SchemaCheck.
1694
+ * @details Undefined holes are ignored because they carry no executable graph.
1695
+ * @param graphs Child graph vector.
1696
+ * @returns True when a present graph contains SchemaCheck.
1697
+ */
1698
+ function graphArrayContainsSchemaCheck(graphs) {
1699
+ for (let index = 0; index < graphs.length; index += 1) {
1700
+ const graph = graphs[index];
1701
+ if (graph !== undefined && graphContainsSchemaCheck(graph)) {
1702
+ return true;
1703
+ }
1704
+ }
1705
+ return false;
1706
+ }
1707
+ /**
1708
+ * @brief Scan object entries for SchemaCheck.
1709
+ * @details Nested object shapes may appear inside scheduled parent fields.
1710
+ * @param entries Nested object shape entries.
1711
+ * @returns True when an entry graph contains an opaque check.
1712
+ */
1713
+ function objectEntriesContainSchemaCheck(entries) {
1714
+ for (let index = 0; index < entries.length; index += 1) {
1715
+ const entry = entries[index];
1716
+ if (entry !== undefined && graphContainsSchemaCheck(entry.graph)) {
1717
+ return true;
1718
+ }
1719
+ }
1720
+ return false;
1721
+ }
1722
+ /**
1723
+ * @brief Execute emit unsafe object shape entries.
1724
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
1725
+ */
1726
+ function emitUnsafeObjectShapeEntries(node, objectExpression, context, state) {
1727
+ for (let index = 0; index < node.entries.length; index += 1) {
1728
+ const entry = node.entries[index];
1729
+ if (entry === undefined) {
1730
+ state.chunks.push(failStatement(state));
1731
+ continue;
1732
+ }
1733
+ const keyRef = unsafeStringLiteralExpression(entry.key);
1734
+ const itemValue = `v${String(state.temp)}`;
1735
+ state.temp += 1;
1736
+ const maybeUndefined = schemaCanAcceptUndefined(entry.schema);
1737
+ state.chunks.push(`const ${itemValue}=${unsafePropertyReadExpression(objectExpression, entry.key)};`);
1738
+ if (entry.presence === PresenceTag.Optional) {
1739
+ const presentBranch = makeBranchEmitState(state);
1740
+ emitFalseCheck(entry.graph, entry.graph.result, itemValue, context, presentBranch);
1741
+ if (presentBranch.temp > state.temp) {
1742
+ state.temp = presentBranch.temp;
1743
+ }
1744
+ const ownUndefinedBranch = makeBranchEmitState(state);
1745
+ emitFalseCheck(entry.graph, entry.graph.result, itemValue, context, ownUndefinedBranch);
1746
+ if (ownUndefinedBranch.temp > state.temp) {
1747
+ state.temp = ownUndefinedBranch.temp;
1748
+ }
1749
+ state.chunks.push(`if(${itemValue}!==undefined){`, ...presentBranch.chunks, `}else if(h.call(${objectExpression},${keyRef})){`, ...ownUndefinedBranch.chunks, "}");
1750
+ }
1751
+ else {
1752
+ if (maybeUndefined) {
1753
+ state.chunks.push(`if(${itemValue}===undefined&&!h.call(${objectExpression},${keyRef}))${failStatement(state)}`);
1754
+ }
1755
+ emitFalseCheck(entry.graph, entry.graph.result, itemValue, context, state);
1756
+ }
1757
+ }
1758
+ }
1759
+ /**
1760
+ * @brief emit object shape strict keys.
1761
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
1762
+ */
1763
+ function emitObjectShapeStrictKeys(node, objectExpression, context, state) {
1764
+ if (node.mode !== ObjectModeTag.Strict) {
1765
+ return;
1766
+ }
1767
+ if (node.catchall !== undefined) {
1768
+ return;
1769
+ }
1770
+ if (isUncheckedMode(context)) {
1771
+ return;
1772
+ }
1773
+ if (isUnsafeMode(context)) {
1774
+ emitUnsafeStrictKeyLoop(objectExpression, node.keys, state);
1775
+ return;
1776
+ }
1777
+ if (node.allRequired) {
1778
+ state.chunks.push(`if(Object.getOwnPropertyNames(${objectExpression}).length!==${String(node.entries.length)}||Object.getOwnPropertySymbols(${objectExpression}).length!==0)${failStatement(state)}`);
1779
+ return;
1780
+ }
1781
+ const present = `xs${String(state.temp)}`;
1782
+ state.temp += 1;
1783
+ const length = `n${String(state.temp)}`;
1784
+ state.temp += 1;
1785
+ const index = `i${String(state.temp)}`;
1786
+ state.temp += 1;
1787
+ const key = `key${String(state.temp)}`;
1788
+ state.temp += 1;
1789
+ state.chunks.push(`const ${present}=Reflect.ownKeys(${objectExpression});`, `const ${length}=${present}.length;`, `for(let ${index}=0;${index}<${length};${index}+=1){`, `const ${key}=${present}[${index}];`, `if(typeof ${key}!=="string"||!${keyMembershipExpression(key, node.keys, context)})${failStatement(state)}`, "}");
1790
+ }
1791
+ /**
1792
+ * @brief Execute emit unsafe strict key loop.
1793
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
1794
+ */
1795
+ function emitUnsafeStrictKeyLoop(objectExpression, keys, state) {
1796
+ const key = `key${String(state.temp)}`;
1797
+ state.temp += 1;
1798
+ state.chunks.push(`for(const ${key} in ${objectExpression}){`, `if(h.call(${objectExpression},${key})&&!${unsafeKeyMembershipExpression(key, keys)})${failStatement(state)}`, "}");
1799
+ }
1800
+ /**
1801
+ * @brief emit boolean fold expression.
1802
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
1803
+ */
1804
+ function emitBooleanFoldExpression(graph, ids, value, context, state, and) {
1805
+ const fallback = and ? "true" : "false";
1806
+ const operator = and ? "&&" : "||";
1807
+ const parts = [];
1808
+ for (let index = 0; index < ids.length; index += 1) {
1809
+ const id = ids[index];
1810
+ if (id !== undefined) {
1811
+ parts.push(emitGraphExpression(graph, id, value, context, undefined));
1812
+ }
1813
+ }
1814
+ if (parts.length === 0) {
1815
+ return fallback;
1816
+ }
1817
+ return `(${parts.join(operator)})`;
1818
+ }
1819
+ /**
1820
+ * @brief emit equals expression.
1821
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
1822
+ */
1823
+ function emitEqualsExpression(graph, left, right, value, context, state) {
1824
+ const leftNode = graph.nodes[left];
1825
+ const rightNode = graph.nodes[right];
1826
+ if (rightNode?.tag === NodeTag.Const) {
1827
+ return literalEqualsExpression(emitGraphExpression(graph, left, value, context, state), rightNode.value, context);
1828
+ }
1829
+ if (leftNode?.tag === NodeTag.Const) {
1830
+ return literalEqualsExpression(emitGraphExpression(graph, right, value, context, state), leftNode.value, context);
1831
+ }
1832
+ return `Object.is(${emitGraphExpression(graph, left, value, context, state)},${emitGraphExpression(graph, right, value, context, state)})`;
1833
+ }
1834
+ /**
1835
+ * @brief literal equals expression.
1836
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
1837
+ */
1838
+ function literalEqualsExpression(subject, literal, context) {
1839
+ if (literal === null) {
1840
+ return `(${subject}===null)`;
1841
+ }
1842
+ if (literal === undefined) {
1843
+ return `(${subject}===undefined)`;
1844
+ }
1845
+ switch (typeof literal) {
1846
+ case "string":
1847
+ case "boolean":
1848
+ case "bigint":
1849
+ case "symbol":
1850
+ return `(${subject}===${literalExpression(literal, context)})`;
1851
+ case "number":
1852
+ if (Number.isNaN(literal)) {
1853
+ return `Number.isNaN(${subject})`;
1854
+ }
1855
+ if (Object.is(literal, 0) || Object.is(literal, -0)) {
1856
+ return `Object.is(${subject},${literalExpression(literal, context)})`;
1857
+ }
1858
+ return `(${subject}===${literalExpression(literal, context)})`;
1859
+ default:
1860
+ return "false";
1861
+ }
1862
+ }
1863
+ /**
1864
+ * @brief emit data slot for expression.
1865
+ * @details Data slots carry aliases and descriptor-backed values across branch scopes so generated code can reuse proven reads without changing validation semantics.
1866
+ */
1867
+ function emitDataSlotForExpression(object, key, objectExpression, context, state) {
1868
+ const cacheKey = dataSlotKey(object, key, objectExpression);
1869
+ const cached = state.dataSlots.get(cacheKey);
1870
+ if (cached !== undefined) {
1871
+ return cached;
1872
+ }
1873
+ const descriptor = `d${String(state.temp)}`;
311
1874
  state.temp += 1;
312
1875
  const keyRef = stringRef(context, key);
313
1876
  state.chunks.push(`const ${descriptor}=gp(${objectExpression},${keyRef});`);
@@ -320,10 +1883,19 @@ function emitDataSlot(graph, object, key, value, context, state) {
320
1883
  }
321
1884
  /**
322
1885
  * @brief emit data value slot.
1886
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
323
1887
  */
324
1888
  function emitDataValueSlot(graph, object, key, value, context, state) {
325
- const cacheKey = dataSlotKey(object, key);
326
- const slot = emitDataSlot(graph, object, key, value, context, state);
1889
+ const objectExpression = emitGraphExpression(graph, object, value, context, state);
1890
+ const cacheKey = dataSlotKey(object, key, objectExpression);
1891
+ const slot = emitDataSlotForExpression(object, key, objectExpression, context, state);
1892
+ return emitDataSlotValue(cacheKey, slot, state);
1893
+ }
1894
+ /**
1895
+ * @brief emit data slot value.
1896
+ * @details Data slots carry aliases and descriptor-backed values across branch scopes so generated code can reuse proven reads without changing validation semantics.
1897
+ */
1898
+ function emitDataSlotValue(cacheKey, slot, state) {
327
1899
  if (slot.value !== undefined) {
328
1900
  return slot.value;
329
1901
  }
@@ -336,35 +1908,470 @@ function emitDataValueSlot(graph, object, key, value, context, state) {
336
1908
  slot.value = localValue;
337
1909
  return localValue;
338
1910
  }
1911
+ /**
1912
+ * @brief seed data slot.
1913
+ * @details Data slots carry aliases and descriptor-backed values across branch scopes so generated code can reuse proven reads without changing validation semantics.
1914
+ */
1915
+ function seedDataSlot(state, object, key, objectExpression, descriptor, value, literal) {
1916
+ const cacheKey = dataSlotKey(object, key, objectExpression);
1917
+ state.dataSlots.set(cacheKey, {
1918
+ descriptor,
1919
+ value
1920
+ });
1921
+ state.dataGuards.add(cacheKey);
1922
+ if (literal !== undefined) {
1923
+ state.dataLiterals.set(cacheKey, literal);
1924
+ }
1925
+ }
339
1926
  /**
340
1927
  * @brief emit has data check.
1928
+ * @details Emission is isolated here so optimized graph nodes lower to stable JavaScript shapes across safe, unsafe, and unchecked modes.
341
1929
  */
342
1930
  function emitHasDataCheck(graph, object, key, value, context, state) {
343
- const cacheKey = dataSlotKey(object, key);
344
- const slot = emitDataSlot(graph, object, key, value, context, state);
345
- state.chunks.push(`if(${slot.descriptor}===undefined||!h.call(${slot.descriptor},"value"))return false;`);
1931
+ const objectExpression = emitGraphExpression(graph, object, value, context, state);
1932
+ const cacheKey = dataSlotKey(object, key, objectExpression);
1933
+ const slot = emitDataSlotForExpression(object, key, objectExpression, context, state);
1934
+ state.chunks.push(`if(${slot.descriptor}===undefined||!h.call(${slot.descriptor},"value"))${failStatement(state)}`);
346
1935
  state.dataGuards.add(cacheKey);
347
1936
  }
348
1937
  /**
349
- * @brief has data expression.
1938
+ * @brief Emit an expression that tests for an own data descriptor.
1939
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
1940
+ * @param graph Graph being emitted.
1941
+ * @param object Node id for the object expression.
1942
+ * @param key Property key being checked.
1943
+ * @param value Root input expression.
1944
+ * @param context Shared compile context.
1945
+ * @param state Emitter state receiving descriptor cache entries.
1946
+ * @returns JavaScript expression that is true only for own data slots.
350
1947
  */
351
1948
  function hasDataExpression(graph, object, key, value, context, state) {
352
- const slot = emitDataSlot(graph, object, key, value, context, state);
1949
+ const objectExpression = emitGraphExpression(graph, object, value, context, state);
1950
+ const slot = emitDataSlotForExpression(object, key, objectExpression, context, state);
353
1951
  return `(${slot.descriptor}!==undefined&&h.call(${slot.descriptor},"value"))`;
354
1952
  }
1953
+ /**
1954
+ * @brief unary predicate expression.
1955
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
1956
+ */
1957
+ function unaryPredicateExpression(graph, subjectId, tag, value, context, state, emit) {
1958
+ const subject = emitGraphExpression(graph, subjectId, value, context, state);
1959
+ if (state !== undefined && isKnownPredicate(tag, subject, state)) {
1960
+ return "true";
1961
+ }
1962
+ return emit(subject);
1963
+ }
1964
+ /**
1965
+ * @brief number predicate expression.
1966
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
1967
+ */
1968
+ function numberPredicateExpression(graph, subjectId, value, context, state) {
1969
+ const subject = emitGraphExpression(graph, subjectId, value, context, state);
1970
+ if (state !== undefined && isKnownPredicate(NodeTag.IsNumber, subject, state)) {
1971
+ return "true";
1972
+ }
1973
+ return finiteNumberExpression(subject, state);
1974
+ }
1975
+ /**
1976
+ * @brief emit number guard.
1977
+ * @details Guard snippets keep the generated fast path straight-line while making early failure branches share the same source shape.
1978
+ */
1979
+ function emitNumberGuard(value, state) {
1980
+ if (isKnownPredicate(NodeTag.IsNumber, value, state)) {
1981
+ return;
1982
+ }
1983
+ state.chunks.push(`if(!${finiteNumberExpression(value, state)})${failStatement(state)}`);
1984
+ markKnownPredicate(NodeTag.IsNumber, value, state);
1985
+ markKnownTypeof("number", value, state);
1986
+ }
1987
+ /**
1988
+ * @brief emit predicate guard.
1989
+ * @details Guard snippets keep the generated fast path straight-line while making early failure branches share the same source shape.
1990
+ */
1991
+ function emitPredicateGuard(tag, value, state) {
1992
+ if (isKnownPredicate(tag, value, state)) {
1993
+ return;
1994
+ }
1995
+ const expression = unaryPredicateCheckExpression(tag, value);
1996
+ state.chunks.push(`if(!${expression})${failStatement(state)}`);
1997
+ markKnownPredicate(tag, value, state);
1998
+ }
1999
+ /**
2000
+ * @brief unary predicate check expression.
2001
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
2002
+ */
2003
+ function unaryPredicateCheckExpression(tag, value) {
2004
+ switch (tag) {
2005
+ case NodeTag.IsString:
2006
+ return `(typeof ${value}==="string")`;
2007
+ case NodeTag.IsBoolean:
2008
+ return `(typeof ${value}==="boolean")`;
2009
+ case NodeTag.IsBigInt:
2010
+ return `(typeof ${value}==="bigint")`;
2011
+ case NodeTag.IsSymbol:
2012
+ return `(typeof ${value}==="symbol")`;
2013
+ case NodeTag.IsArray:
2014
+ return `Array.isArray(${value})`;
2015
+ case NodeTag.IsUndefined:
2016
+ return `(${value}===undefined)`;
2017
+ case NodeTag.IsNull:
2018
+ return `(${value}===null)`;
2019
+ default:
2020
+ return "false";
2021
+ }
2022
+ }
2023
+ /**
2024
+ * @brief Check whether a predicate node is already proven in this branch.
2025
+ * @details Generated-source helpers keep the side-table ABI and JavaScript source shape
2026
+ * stable across runtime and AOT emission.
2027
+ * @param graph Graph being emitted.
2028
+ * @param node Candidate predicate node.
2029
+ * @param value Root input expression.
2030
+ * @param context Shared compile context.
2031
+ * @param state Current branch state.
2032
+ * @returns True when the node can be skipped.
2033
+ */
2034
+ function isKnownPredicateNode(graph, node, value, context, state) {
2035
+ if (node === undefined || !isKnownPredicateTag(node.tag)) {
2036
+ return false;
2037
+ }
2038
+ const subject = "value" in node &&
2039
+ typeof node.value === "number"
2040
+ ? emitGraphExpression(graph, node.value, value, context, state)
2041
+ : undefined;
2042
+ return subject !== undefined && isKnownPredicate(node.tag, subject, state);
2043
+ }
2044
+ /**
2045
+ * @brief mark union mask refinement.
2046
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
2047
+ */
2048
+ function markUnionMaskRefinement(mask, value, state) {
2049
+ switch (mask) {
2050
+ case 1:
2051
+ markKnownPredicate(NodeTag.IsString, value, state);
2052
+ return;
2053
+ case 2:
2054
+ markKnownTypeof("number", value, state);
2055
+ return;
2056
+ case 4:
2057
+ markKnownPredicate(NodeTag.IsBoolean, value, state);
2058
+ return;
2059
+ case 8:
2060
+ markKnownPredicate(NodeTag.IsBigInt, value, state);
2061
+ return;
2062
+ case 16:
2063
+ markKnownPredicate(NodeTag.IsSymbol, value, state);
2064
+ return;
2065
+ case 32:
2066
+ markKnownPredicate(NodeTag.IsUndefined, value, state);
2067
+ return;
2068
+ case 64:
2069
+ markKnownPredicate(NodeTag.IsNull, value, state);
2070
+ return;
2071
+ case 128:
2072
+ markKnownPredicate(NodeTag.IsArray, value, state);
2073
+ return;
2074
+ case 256:
2075
+ markKnownPredicate(NodeTag.IsObject, value, state);
2076
+ return;
2077
+ default:
2078
+ return;
2079
+ }
2080
+ }
2081
+ /**
2082
+ * @brief mark known predicate.
2083
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
2084
+ */
2085
+ function markKnownPredicate(tag, value, state) {
2086
+ state.knownPredicates.add(predicateKey(tag, value));
2087
+ markKnownPredicateTypeof(tag, value, state);
2088
+ if (tag === NodeTag.IsObject) {
2089
+ markKnownObject(value, state);
2090
+ }
2091
+ }
2092
+ /**
2093
+ * @brief Query the branch fact table for a proven predicate.
2094
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
2095
+ * @param tag Predicate node tag.
2096
+ * @param value JavaScript subject expression.
2097
+ * @param state Current branch state.
2098
+ * @returns True when equivalent emitted code already proved the predicate.
2099
+ */
2100
+ function isKnownPredicate(tag, value, state) {
2101
+ return state.knownPredicates.has(predicateKey(tag, value)) ||
2102
+ (tag === NodeTag.IsObject && isKnownObject(value, state));
2103
+ }
2104
+ /**
2105
+ * @brief Test whether a node tag can participate in predicate fact tracking.
2106
+ * @details Generated-source helpers keep the side-table ABI and JavaScript source shape
2107
+ * stable across runtime and AOT emission.
2108
+ * @param tag Candidate NodeTag value.
2109
+ * @returns True for primitive/object/array/null/undefined predicate tags.
2110
+ */
2111
+ function isKnownPredicateTag(tag) {
2112
+ return tag === NodeTag.IsString ||
2113
+ tag === NodeTag.IsNumber ||
2114
+ tag === NodeTag.IsBoolean ||
2115
+ tag === NodeTag.IsBigInt ||
2116
+ tag === NodeTag.IsSymbol ||
2117
+ tag === NodeTag.IsObject ||
2118
+ tag === NodeTag.IsArray ||
2119
+ tag === NodeTag.IsUndefined ||
2120
+ tag === NodeTag.IsNull;
2121
+ }
2122
+ /**
2123
+ * @brief predicate key.
2124
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
2125
+ */
2126
+ function predicateKey(tag, value) {
2127
+ return `${String(tag)}:${value}`;
2128
+ }
2129
+ /**
2130
+ * @brief mark known predicate typeof.
2131
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
2132
+ */
2133
+ function markKnownPredicateTypeof(tag, value, state) {
2134
+ switch (tag) {
2135
+ case NodeTag.IsString:
2136
+ markKnownTypeof("string", value, state);
2137
+ return;
2138
+ case NodeTag.IsNumber:
2139
+ markKnownTypeof("number", value, state);
2140
+ return;
2141
+ case NodeTag.IsBoolean:
2142
+ markKnownTypeof("boolean", value, state);
2143
+ return;
2144
+ case NodeTag.IsBigInt:
2145
+ markKnownTypeof("bigint", value, state);
2146
+ return;
2147
+ case NodeTag.IsSymbol:
2148
+ markKnownTypeof("symbol", value, state);
2149
+ return;
2150
+ case NodeTag.IsUndefined:
2151
+ markKnownTypeof("undefined", value, state);
2152
+ return;
2153
+ default:
2154
+ return;
2155
+ }
2156
+ }
2157
+ /**
2158
+ * @brief mark known typeof.
2159
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
2160
+ */
2161
+ function markKnownTypeof(type, value, state) {
2162
+ state.knownTypeofs.add(typeofKey(type, value));
2163
+ }
2164
+ /**
2165
+ * @brief Query branch facts for a proven typeof result.
2166
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
2167
+ * @param type Expected typeof string.
2168
+ * @param value JavaScript subject expression.
2169
+ * @param state Optional branch state.
2170
+ * @returns True when the typeof test is already known in this branch.
2171
+ */
2172
+ function isKnownTypeof(type, value, state) {
2173
+ return state?.knownTypeofs.has(typeofKey(type, value)) === true;
2174
+ }
2175
+ /**
2176
+ * @brief typeof key.
2177
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
2178
+ */
2179
+ function typeofKey(type, value) {
2180
+ return `${type}:${value}`;
2181
+ }
355
2182
  /**
356
2183
  * @brief data slot key.
2184
+ * @details Data slots carry aliases and descriptor-backed values across branch scopes so generated code can reuse proven reads without changing validation semantics.
2185
+ */
2186
+ function dataSlotKey(object, key, objectExpression) {
2187
+ return `${String(object)}:${key}:${objectExpression}`;
2188
+ }
2189
+ /**
2190
+ * @brief Check whether a cached data slot already proved a literal value.
2191
+ * @details Generated-source helpers keep the side-table ABI and JavaScript source shape
2192
+ * stable across runtime and AOT emission.
2193
+ * @param schema Literal schema being emitted.
2194
+ * @param cacheKey Descriptor cache key for the property slot.
2195
+ * @param state Current branch state.
2196
+ * @returns True when a previous descriptor read established the same literal.
2197
+ */
2198
+ function isKnownLiteralDataSlot(schema, cacheKey, state) {
2199
+ if (!state.dataLiterals.has(cacheKey) || schema.tag !== SchemaTag.Literal) {
2200
+ return false;
2201
+ }
2202
+ return Object.is(state.dataLiterals.get(cacheKey), schema.value);
2203
+ }
2204
+ /**
2205
+ * @brief emit array guard.
2206
+ * @details Guard snippets keep the generated fast path straight-line while making early failure branches share the same source shape.
2207
+ */
2208
+ function emitArrayGuard(value, state) {
2209
+ if (isKnownPredicate(NodeTag.IsArray, value, state)) {
2210
+ return;
2211
+ }
2212
+ state.chunks.push(`if(!Array.isArray(${value}))${failStatement(state)}`);
2213
+ markKnownPredicate(NodeTag.IsArray, value, state);
2214
+ }
2215
+ /**
2216
+ * @brief emit object guard.
2217
+ * @details Guard snippets keep the generated fast path straight-line while making early failure branches share the same source shape.
2218
+ */
2219
+ function emitObjectGuard(value, state) {
2220
+ const statement = objectGuardStatement(value, state);
2221
+ if (statement.length !== 0) {
2222
+ state.chunks.push(statement);
2223
+ }
2224
+ }
2225
+ /**
2226
+ * @brief Build the object guard statement for a subject expression.
2227
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
2228
+ * @param value JavaScript subject expression.
2229
+ * @param state Current branch state.
2230
+ * @returns Empty string when object-ness is already known, otherwise a guard.
2231
+ */
2232
+ function objectGuardStatement(value, state) {
2233
+ if (isKnownObject(value, state)) {
2234
+ return "";
2235
+ }
2236
+ markKnownObject(value, state);
2237
+ return `if(${objectRejectExpression(value)})${failStatement(state)}`;
2238
+ }
2239
+ /**
2240
+ * @brief Execute object reject expression.
2241
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
2242
+ */
2243
+ function objectRejectExpression(value) {
2244
+ return `typeof ${value}!=="object"||${value}===null||Array.isArray(${value})`;
2245
+ }
2246
+ /**
2247
+ * @brief emit subject alias.
2248
+ * @details Sea-of-Nodes lowering often hands codegen an existing local. Reusing
2249
+ * that identifier keeps the generated predicate closer to V8's preferred SSA
2250
+ * shape; only compound expressions need a one-shot alias.
2251
+ */
2252
+ function emitSubjectAlias(expression, state) {
2253
+ if (isGeneratedIdentifier(expression)) {
2254
+ return expression;
2255
+ }
2256
+ const subject = `u${String(state.temp)}`;
2257
+ state.temp += 1;
2258
+ state.chunks.push(`const ${subject}=${expression};`);
2259
+ return subject;
2260
+ }
2261
+ /**
2262
+ * @brief Decide whether an expression can be reused without a temporary alias.
2263
+ * @details Generated-source helpers keep the side-table ABI and JavaScript source shape
2264
+ * stable across runtime and AOT emission.
2265
+ * @param value JavaScript expression text.
2266
+ * @returns True when the expression is a simple generated identifier.
2267
+ */
2268
+ function isGeneratedIdentifier(value) {
2269
+ if (value.length === 0) {
2270
+ return false;
2271
+ }
2272
+ const first = value.charCodeAt(0);
2273
+ if (!isIdentifierStart(first)) {
2274
+ return false;
2275
+ }
2276
+ for (let index = 1; index < value.length; index += 1) {
2277
+ if (!isIdentifierPart(value.charCodeAt(index))) {
2278
+ return false;
2279
+ }
2280
+ }
2281
+ return true;
2282
+ }
2283
+ /**
2284
+ * @brief Validate the first character of an emitter-owned identifier.
2285
+ * @details Generated-source helpers keep the side-table ABI and JavaScript source shape
2286
+ * stable across runtime and AOT emission.
2287
+ * @param code UTF-16 code unit.
2288
+ * @returns True for `$`, `_`, or ASCII letters.
2289
+ */
2290
+ function isIdentifierStart(code) {
2291
+ return code === 36 ||
2292
+ code === 95 ||
2293
+ (code >= 65 && code <= 90) ||
2294
+ (code >= 97 && code <= 122);
2295
+ }
2296
+ /**
2297
+ * @brief Validate a non-leading character of an emitter-owned identifier.
2298
+ * @details Generated-source helpers keep the side-table ABI and JavaScript source shape
2299
+ * stable across runtime and AOT emission.
2300
+ * @param code UTF-16 code unit.
2301
+ * @returns True for identifier-start characters or ASCII digits.
2302
+ */
2303
+ function isIdentifierPart(code) {
2304
+ return isIdentifierStart(code) || (code >= 48 && code <= 57);
2305
+ }
2306
+ /**
2307
+ * @brief fail statement.
2308
+ * @details Guard snippets keep the generated fast path straight-line while making early failure branches share the same source shape.
2309
+ */
2310
+ function failStatement(state) {
2311
+ return state.failureLabel === undefined
2312
+ ? "return false;"
2313
+ : `break ${state.failureLabel};`;
2314
+ }
2315
+ /**
2316
+ * @brief Read graph result boolean.
2317
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
2318
+ */
2319
+ function readGraphResultBoolean(graph) {
2320
+ const result = graph.nodes[graph.result];
2321
+ if (result?.tag !== NodeTag.Return) {
2322
+ return undefined;
2323
+ }
2324
+ const value = graph.nodes[result.value];
2325
+ if (value?.tag === NodeTag.Const && typeof value.value === "boolean") {
2326
+ return value.value;
2327
+ }
2328
+ return undefined;
2329
+ }
2330
+ /**
2331
+ * @brief mark known object.
2332
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
2333
+ */
2334
+ function markKnownObject(value, state) {
2335
+ state.knownObjects.add(value);
2336
+ }
2337
+ /**
2338
+ * @brief Query branch facts for a proven plain object guard.
2339
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
2340
+ * @param value JavaScript subject expression.
2341
+ * @param state Current branch state.
2342
+ * @returns True when object guard emission can be skipped.
2343
+ */
2344
+ function isKnownObject(value, state) {
2345
+ return state.knownObjects.has(value);
2346
+ }
2347
+ /**
2348
+ * @brief find param node.
2349
+ * @details Parameter discovery is kept local to graph emission so child graphs can be inlined without assuming a global node id.
357
2350
  */
358
- function dataSlotKey(object, key) {
359
- return `${String(object)}:${key}`;
2351
+ function findParamNode(graph) {
2352
+ for (let index = 0; index < graph.nodes.length; index += 1) {
2353
+ const node = graph.nodes[index];
2354
+ if (node?.tag === NodeTag.Param) {
2355
+ return node.id;
2356
+ }
2357
+ }
2358
+ return undefined;
360
2359
  }
361
2360
  /**
362
- * @brief has all required keys.
2361
+ * @brief Check whether every required object key already has a data guard.
2362
+ * @details Generated-source helpers keep the side-table ABI and JavaScript source shape
2363
+ * stable across runtime and AOT emission.
2364
+ * @param object Object node id.
2365
+ * @param keys Required property keys.
2366
+ * @param objectExpression JavaScript object expression.
2367
+ * @param state Current branch state.
2368
+ * @returns True when strict object emission can reuse existing descriptor guards.
363
2369
  */
364
- function hasAllRequiredKeys(object, keys, state) {
2370
+ function hasAllRequiredKeys(object, keys, objectExpression, state) {
365
2371
  for (let index = 0; index < keys.length; index += 1) {
366
2372
  const key = keys[index];
367
- if (key === undefined || !state.dataGuards.has(dataSlotKey(object, key))) {
2373
+ if (key === undefined ||
2374
+ !state.dataGuards.has(dataSlotKey(object, key, objectExpression))) {
368
2375
  return false;
369
2376
  }
370
2377
  }
@@ -372,6 +2379,7 @@ function hasAllRequiredKeys(object, keys, state) {
372
2379
  }
373
2380
  /**
374
2381
  * @brief key membership expression.
2382
+ * @details Branch fact tables let later emitters reuse proven predicates and avoid redundant guards while keeping failure paths explicit.
375
2383
  */
376
2384
  function keyMembershipExpression(key, keys, context) {
377
2385
  if (keys.length === 0) {
@@ -387,61 +2395,160 @@ function keyMembershipExpression(key, keys, context) {
387
2395
  return `(${parts.join("||")})`;
388
2396
  }
389
2397
  /**
390
- * @brief emit inline schema expression.
2398
+ * @brief Execute unsafe key membership expression.
2399
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
391
2400
  */
392
- function emitInlineSchemaExpression(schema, value, context) {
393
- const plan = makeValidationPlan(schema);
394
- return emitGraphExpression(plan.graph, plan.graph.result, value, context, undefined);
2401
+ function unsafeKeyMembershipExpression(key, keys) {
2402
+ if (keys.length === 0) {
2403
+ return "false";
2404
+ }
2405
+ const parts = new Array(keys.length);
2406
+ for (let index = 0; index < keys.length; index += 1) {
2407
+ const value = keys[index];
2408
+ parts[index] = value === undefined
2409
+ ? "false"
2410
+ : `${key}===${unsafeStringLiteralExpression(value)}`;
2411
+ }
2412
+ return `(${parts.join("||")})`;
2413
+ }
2414
+ /**
2415
+ * @brief Execute unsafe property read expression.
2416
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
2417
+ */
2418
+ function unsafePropertyReadExpression(objectExpression, key) {
2419
+ if (isAsciiIdentifierName(key)) {
2420
+ return `${objectExpression}.${key}`;
2421
+ }
2422
+ return `${objectExpression}[${unsafeStringLiteralExpression(key)}]`;
2423
+ }
2424
+ /**
2425
+ * @brief Check ascii identifier name.
2426
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
2427
+ */
2428
+ function isAsciiIdentifierName(value) {
2429
+ return /^[A-Za-z_$][0-9A-Za-z_$]*$/u.test(value);
2430
+ }
2431
+ /**
2432
+ * @brief Execute unsafe string literal expression.
2433
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
2434
+ */
2435
+ function unsafeStringLiteralExpression(value) {
2436
+ return JSON.stringify(value)
2437
+ .replace(/\u2028/gu, "\\u2028")
2438
+ .replace(/\u2029/gu, "\\u2029");
395
2439
  }
396
2440
  /**
397
2441
  * @brief finite number expression.
2442
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
398
2443
  */
399
- function finiteNumberExpression(value) {
2444
+ function finiteNumberExpression(value, state) {
2445
+ if (isKnownTypeof("number", value, state)) {
2446
+ return `Number.isFinite(${value})`;
2447
+ }
400
2448
  return `(typeof ${value}==="number"&&Number.isFinite(${value}))`;
401
2449
  }
402
2450
  /**
403
2451
  * @brief regex expression.
2452
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
404
2453
  */
405
2454
  function regexExpression(value, regex, context) {
406
2455
  const index = pushRegex(context, regex);
407
2456
  const ref = `r[${String(index)}]`;
2457
+ if (!regexNeedsLastIndexReset(regex)) {
2458
+ return `${ref}.test(${value})`;
2459
+ }
408
2460
  return `((${ref}.lastIndex=0),${ref}.test(${value}))`;
409
2461
  }
2462
+ /**
2463
+ * @brief Execute regex needs last index reset.
2464
+ * @details Code generation helpers keep emitted JavaScript shape stable across runtime and AOT paths.
2465
+ */
2466
+ function regexNeedsLastIndexReset(regex) {
2467
+ return regex.global || regex.sticky;
2468
+ }
2469
+ /**
2470
+ * @brief Emit compact expression-mode array iteration.
2471
+ * @param value Generated expression for the candidate array.
2472
+ * @param node ArrayEvery node with sparse-slot and length metadata.
2473
+ * @param context Shared code-generation context.
2474
+ * @returns Boolean JavaScript expression for array iteration.
2475
+ */
2476
+ function emitArrayEveryExpression(value, node, context) {
2477
+ const loop = schemaCanAcceptUndefined(node.item)
2478
+ ? `eu(${value},${emitGraphChildFunction(node.itemGraph, context)})`
2479
+ : `ea(${value},${emitGraphChildFunction(node.itemGraph, context)})`;
2480
+ const lengthParts = arrayLengthPredicateParts(value, node.checks);
2481
+ if (lengthParts.length === 0) {
2482
+ return loop;
2483
+ }
2484
+ return `(${lengthParts.join("&&")}&&${loop})`;
2485
+ }
2486
+ /**
2487
+ * @brief Build expression-mode array length predicates.
2488
+ * @param value Generated expression for the candidate array.
2489
+ * @param checks Normalized length check vector.
2490
+ * @returns Predicate fragments guarding safe `.length` access.
2491
+ */
2492
+ function arrayLengthPredicateParts(value, checks) {
2493
+ if (checks.length === 0) {
2494
+ return [];
2495
+ }
2496
+ const parts = [`Array.isArray(${value})`];
2497
+ for (let index = 0; index < checks.length; index += 1) {
2498
+ const check = checks[index];
2499
+ if (check === undefined) {
2500
+ parts.push("false");
2501
+ continue;
2502
+ }
2503
+ switch (check.tag) {
2504
+ case ArrayCheckTag.Min:
2505
+ parts.push(`${value}.length>=${String(check.value)}`);
2506
+ break;
2507
+ case ArrayCheckTag.Max:
2508
+ parts.push(`${value}.length<=${String(check.value)}`);
2509
+ break;
2510
+ }
2511
+ }
2512
+ return parts;
2513
+ }
410
2514
  /**
411
2515
  * @brief emit tuple items expression.
2516
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
412
2517
  */
413
- function emitTupleItemsExpression(value, items, context) {
2518
+ function emitTupleItemsExpression(value, itemGraphs, context) {
414
2519
  const parts = [
415
2520
  `Array.isArray(${value})`,
416
- `${value}.length===${String(items.length)}`
2521
+ `${value}.length===${String(itemGraphs.length)}`
417
2522
  ];
418
- for (let index = 0; index < items.length; index += 1) {
419
- const item = items[index];
420
- if (item !== undefined) {
421
- parts.push(`ev(${value},${String(index)},${emitGraphFunction(item, context)})`);
2523
+ for (let index = 0; index < itemGraphs.length; index += 1) {
2524
+ const itemGraph = itemGraphs[index];
2525
+ if (itemGraph !== undefined) {
2526
+ parts.push(`ev(${value},${String(index)},${emitGraphChildFunction(itemGraph, context)})`);
422
2527
  }
423
2528
  }
424
2529
  return `(${parts.join("&&")})`;
425
2530
  }
426
2531
  /**
427
2532
  * @brief emit discriminant dispatch expression.
2533
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
428
2534
  */
429
- function emitDiscriminantDispatchExpression(value, key, literals, schemas, context) {
2535
+ function emitDiscriminantDispatchExpression(value, node, context) {
430
2536
  const functions = [];
431
- for (let index = 0; index < schemas.length; index += 1) {
432
- const schema = schemas[index];
433
- if (schema !== undefined) {
434
- functions.push(emitGraphFunction(schema, context));
2537
+ for (let index = 0; index < node.graphs.length; index += 1) {
2538
+ const graph = node.graphs[index];
2539
+ if (graph !== undefined) {
2540
+ functions.push(emitGraphChildFunction(graph, context));
435
2541
  }
436
2542
  }
437
- const keyset = `k[${String(pushKeyset(context, literals))}]`;
2543
+ const keyset = `k[${String(pushKeyset(context, node.literals))}]`;
438
2544
  if (functions.length === 0) {
439
- return `dj(${value},${stringRef(context, key)},${keyset})`;
2545
+ return `dj(${value},${stringRef(context, node.key)},${keyset})`;
440
2546
  }
441
- return `dj(${value},${stringRef(context, key)},${keyset},${functions.join(",")})`;
2547
+ return `dj(${value},${stringRef(context, node.key)},${keyset},${functions.join(",")})`;
442
2548
  }
443
2549
  /**
444
2550
  * @brief literal expression.
2551
+ * @details Expression helpers centralize escaping, side-table references, and JavaScript corner cases so runtime and AOT output stay byte-for-byte consistent.
445
2552
  */
446
2553
  function literalExpression(value, context) {
447
2554
  if (value === true) {