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