@samitouri / QOS-React / commits / beb6e1431c

[new-arch] Improve reference-kind analysis

This implements an alternative approach to reference kind inference in the new architecture based on feedback. Here, we track an environment that maps top-level identifiers (IdentifierId) to the "kind" of value stored: immutable, mutable, frozen, or maybe-frozen. We then do a forward data flow analysis updating this environment based on the semantics of each instruction combined with the types of values present. For example a reference of a value in a "mutable" position is inferred as readonly if the value is known to be frozen or immutable. Similarly, a usage of a reference in a "freeze" position is inferred as a freeze if the value is not yet definitively frozen, and inferred as readonly if the value is already frozen. When multiple control paths converge we merge the previous and new incoming environments, and only reprocess the block if the environment changed relative to the previous value. This has some noticeable benefits over the previous version: * We now infer precisely where `makeReadOnly()` calls need to be inserted, aka points where a value needs to be frozen may not yet be frozen. * We track immutable values and can infer their usage as readonly rather than mutable. * The system handles aliasing by representing values as distinct from variables, so that we can handle situations such as: ```javascript const a = []; // env: {a: value0; value0: mutable} const b = a; // env: {a: value0, b: value0; value0: mutable} freeze(a); // env: {a: value0, b: value0; value0: frozen} mayMutate(b); // ordinarily inferred as a mutable reference, but we know its readonly ```

Joseph Savona committed Oct 11, 2022 at 11:32 UTC beb6e1431ca53b6f20f69e40f1dbb95bbdc5095a
31 files changed +1557 -1245
compiler/forget/src/HIR/BuildDefUseGraph.ts
+13 -13
@@ -22,8 +22,8 @@ import { mapTerminalSuccessors } from "./HIRBuilder";
22 import { printMixedHIR, printPlace } from "./PrintHIR";
23
24 const HOOKS: Map<string, Hook> = new Map([
25 - ["useState", { kind: "State", capability: Capability.Frozen }],
26 - ["useRef", { kind: "Ref", capability: Capability.Frozen }],
25 + ["useState", { kind: "State", capability: Capability.Freeze }],
26 + ["useRef", { kind: "Ref", capability: Capability.Freeze }],
27 ]);
28
29 type HookKind = { kind: "State" } | { kind: "Ref" } | { kind: "Custom" };
@@ -86,7 +86,7 @@ export default function buildDefUseGraph(fn: HIRFunction): Array<Vertex> {
86 memberPath: null,
87 value: param,
88 path: null as any, // TODO
89 - capability: Capability.Frozen,
89 + capability: Capability.Freeze,
90 };
91 preambleBuilder.init(place, null, true);
92 }
@@ -131,7 +131,7 @@ export default function buildDefUseGraph(fn: HIRFunction): Array<Vertex> {
131 // Vertices derived from a frozen value are also frozen
132 if (
133 vertex.place !== null &&
134 - vertex.place.capability === Capability.Frozen
134 + vertex.place.capability === Capability.Freeze
135 ) {
136 flowFrozennessForwards(vertex, 0);
137 }
@@ -157,7 +157,7 @@ function flowFrozennessForwards(vertex: Vertex, epoch: number) {
157 }
158 vertex.epoch = epoch;
159 if (vertex.place !== null) {
160 - vertex.place.capability = Capability.Frozen;
160 + vertex.place.capability = Capability.Freeze;
161 }
162 for (const outgoing of vertex.outgoing) {
163 flowFrozennessForwards(outgoing, epoch);
@@ -260,7 +260,7 @@ function buildInputsOutputsForBlock(
260 let valueCapability = Capability.Readonly;
261 switch (instrValue.kind) {
262 case "BinaryExpression": {
263 - valueCapability = Capability.Frozen;
263 + valueCapability = Capability.Freeze;
264 builder.reference(instrValue.left, instrValue, Capability.Readonly);
265 builder.reference(instrValue.right, instrValue, Capability.Readonly);
266 break;
@@ -301,7 +301,7 @@ function buildInputsOutputsForBlock(
301 break;
302 }
303 case "UnaryExpression": {
304 - valueCapability = Capability.Frozen; // TODO check that value must be a primitive, or make conditional based on the operator
304 + valueCapability = Capability.Freeze; // TODO check that value must be a primitive, or make conditional based on the operator
305 builder.reference(instrValue.value, instrValue, Capability.Readonly);
306 break;
307 }
@@ -310,13 +310,13 @@ function buildInputsOutputsForBlock(
310 break;
311 }
312 case "JsxExpression": {
313 - builder.reference(instrValue.tag, instrValue, Capability.Frozen);
313 + builder.reference(instrValue.tag, instrValue, Capability.Freeze);
314 for (const [_prop, value] of Object.entries(instrValue.props)) {
315 - builder.reference(value, instrValue, Capability.Frozen);
315 + builder.reference(value, instrValue, Capability.Freeze);
316 }
317 if (instrValue.children !== null) {
318 for (const child of instrValue.children) {
319 - builder.reference(child, instrValue, Capability.Frozen);
319 + builder.reference(child, instrValue, Capability.Freeze);
320 }
321 }
322 break;
@@ -354,7 +354,7 @@ function buildInputsOutputsForBlock(
354 builder.reference(
355 block.terminal.value,
356 block.terminal,
357 - Capability.Frozen
357 + Capability.Freeze
358 );
359 break;
360 }
@@ -363,7 +363,7 @@ function buildInputsOutputsForBlock(
363 builder.reference(
364 block.terminal.value,
365 block.terminal,
366 - Capability.Frozen
366 + Capability.Freeze
367 );
368 }
369 break;
@@ -416,7 +416,7 @@ function parseHookCall(place: Place): Hook | null {
416 if (hook != null) {
417 return hook;
418 }
419 - return { kind: "Custom", capability: Capability.Frozen };
419 + return { kind: "Custom", capability: Capability.Freeze };
420 }
421
422 type BlockResult = {
compiler/forget/src/HIR/HIR.ts
+4 -5
@@ -221,12 +221,11 @@ export type Identifier = {
221 export enum Capability {
222 // Default value: not allowed after lifetime inference
223 Unknown = "<unknown>",
224 - // The value is known to be deeply, permanently mutable at this point.
225 - Frozen = "frozen",
226 - // The value is not modified at this point or thereafter, but is not guaranteed to be
227 - // frozen. It would be safe to freeze the value at this point.
224 + // The value is made frozen at this point.
225 + Freeze = "freeze",
226 + // The value is not modified at this point.
227 Readonly = "readonly",
229 - // The value is mutated at this point or at some later point.
228 + // The value is modified at this point.
229 Mutable = "mutable",
230 }
231
compiler/forget/src/HIR/InferMutability.ts deleted
-676
@@ -1,676 +0,0 @@
1 -/**
2 - * Copyright (c) Facebook, Inc. and its affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - */
7 -
8 -import invariant from "invariant";
9 -import { assertExhaustive } from "../Common/utils";
10 -import {
11 - BasicBlock,
12 - BlockId,
13 - Capability,
14 - HIRFunction,
15 - IdentifierId,
16 - Instruction,
17 - InstructionValue,
18 - Place,
19 - Terminal,
20 -} from "./HIR";
21 -import { printMixedHIR, printPlace } from "./PrintHIR";
22 -
23 -const HOOKS: Map<string, Hook> = new Map([
24 - ["useState", { kind: "State", capability: Capability.Frozen }],
25 - ["useRef", { kind: "Ref", capability: Capability.Frozen }],
26 -]);
27 -
28 -type HookKind = { kind: "State" } | { kind: "Ref" } | { kind: "Custom" };
29 -type Hook = HookKind & { capability: Capability };
30 -
31 -/**
32 - * For every usage of a value, infers whether the value is accessed as frozen, mutable,
33 - * or readonly:
34 - * - Frozen: the value is known to be deeply immutable. This usage cannot alter the value,
35 - * and the value cannot change after this usage. The usage can therefore safely memoize
36 - * based on the value.
37 - * - Mutable: the value may be modified by this or a subsequent usage.
38 - * - Readonly: the value is not modified by this or any subsequent usage, but was previously
39 - * mutable. Thus the first usage of a value as readonly (after a previous mutable usage)
40 - * is a point at which the value could safely frozen.
41 - *
42 - * In general usages default to be inferred as readonly (for operations that read) or mutable
43 - * (for operations that may write). Usages can only be inferred as frozen when:
44 - * - The value is derived from a reactive input: props, a hook argument. Derived here means
45 - * that the value is itself a reactive input (`props`) or statically known to be extracted
46 - * purely from a reactive input (`props.a`, `props.a[1]` etc).
47 - * - The value is "moved" into a reactive ouput, ie is captured into a component/hook return
48 - * value.
49 - * - The value is known to be a primitive (boolean, number, string, null, or undefined). This
50 - * is only the case when the value is a constant primitive *or* is the result of an
51 - * operation that, under JS semantics, must produce a primitive. Examples are binary
52 - * expressions.
53 - *
54 - * ## Algorithm
55 - * Fixpoint iteration of the CFG to construct use-use chains (graphs) in which nodes
56 - * are usages of a particular value and the required capability (frozen/read/write),
57 - * and each "usage" node points to predecessor node(s). Iteration stops once no new
58 - * edges/nodes need to be added to the graph.
59 - *
60 - * After construction, walk the graph to propagate frozenness forward and mutability
61 - * backward:
62 - * - If a usage of a value is frozen, then any subsequent usage of that value must be
63 - * frozen as well. Flow the frozenness forward through the graph, following only
64 - * use chains (outoing edges, but not captures).
65 - * - If a usage of a vale is mutable, then any prior readonly references of that value
66 - * must be marked as mutable as well (since there is a subsequent mutation). Again,
67 - * usages only remain readonly if there is no subsequent mutation.
68 - * - Finally, any value that is *initialized* as readonly and never modified (ie, that is
69 - * still readonly in the graph) can be marked frozen (and all of its usages as frozen),
70 - * since it is never ever modified.
71 - */
72 -export default function inferLifetimes(fn: HIRFunction): Map<Place, Vertex> {
73 - const graph = UseGraph.empty();
74 - for (const param of fn.params) {
75 - const place: Place = {
76 - kind: "Identifier",
77 - memberPath: null,
78 - value: param,
79 - path: null as any, // TODO
80 - capability: Capability.Frozen,
81 - };
82 - graph.init(place, null);
83 - }
84 -
85 - const queue: Array<QueueEntry> = [{ block: fn.body.entry, graph }];
86 - const blockMemory: Map<BlockId, UseGraph> = new Map();
87 - while (queue.length !== 0) {
88 - const { block: blockId, graph: inputGraph } = queue.shift()!;
89 - const prevGraph = blockMemory.get(blockId);
90 - let nextGraph = null;
91 - if (prevGraph == null) {
92 - blockMemory.set(blockId, inputGraph);
93 - nextGraph = inputGraph.snapshot();
94 - } else {
95 - const merged = prevGraph.snapshot();
96 - const hasChanges = merged.merge(inputGraph);
97 - if (!hasChanges) {
98 - continue;
99 - }
100 - blockMemory.set(blockId, merged);
101 - nextGraph = prevGraph.snapshot();
102 - }
103 -
104 - const block = fn.body.blocks.get(blockId)!;
105 - inferBlock(nextGraph, block);
106 -
107 - const terminal = block.terminal;
108 - switch (terminal.kind) {
109 - case "throw": {
110 - nextGraph.reference(terminal.value, terminal, Capability.Frozen);
111 - break;
112 - }
113 - case "return": {
114 - if (terminal.value !== null) {
115 - nextGraph.reference(terminal.value, terminal, Capability.Frozen);
116 - }
117 - break;
118 - }
119 - case "goto": {
120 - queue.push({ block: terminal.block, graph: nextGraph });
121 - break;
122 - }
123 - case "if": {
124 - nextGraph.reference(terminal.test, terminal, Capability.Readonly);
125 - queue.push({ block: terminal.consequent, graph: nextGraph });
126 - queue.push({ block: terminal.alternate, graph: nextGraph });
127 - break;
128 - }
129 - case "switch": {
130 - nextGraph.reference(terminal.test, terminal, Capability.Readonly);
131 - for (const case_ of terminal.cases) {
132 - if (case_.test !== null) {
133 - nextGraph.reference(case_.test, terminal, Capability.Readonly);
134 - }
135 - queue.push({ block: case_.block, graph: nextGraph.snapshot() });
136 - }
137 - break;
138 - }
139 - default: {
140 - assertExhaustive(terminal, "Unexpected terminal kind");
141 - }
142 - }
143 - }
144 -
145 - // perform inference over the constructed graph and return it
146 - return analyzeGraph(graph);
147 -}
148 -
149 -type QueueEntry = {
150 - block: BlockId;
151 - graph: UseGraph;
152 -};
153 -
154 -function analyzeGraph(graph: UseGraph): Map<Place, Vertex> {
155 - const vertices = graph.build();
156 - let epoch = 0;
157 -
158 - // First flow frozen values forward through the graph
159 - for (const vertex of vertices.values()) {
160 - if (vertex.place.capability === Capability.Frozen) {
161 - flowFrozennessForwards(vertex, epoch++);
162 - }
163 - }
164 - // Then flow mutability backward through the graph
165 - for (const vertex of vertices.values()) {
166 - if (vertex.place.capability === Capability.Mutable) {
167 - flowMutabilityBackwards(vertex, epoch++);
168 - }
169 - }
170 - // Finally, any *entry* nodes that are readonly are now known to never be mutated,
171 - // so convert them to frozen (and flow that frozenness forward)
172 - for (const vertex of vertices.values()) {
173 - invariant(
174 - vertex.place.capability !== Capability.Unknown,
175 - "Expected capability to have been inferred as frozen, readonly, or mutable"
176 - );
177 - if (
178 - vertex.place.capability === Capability.Readonly &&
179 - vertex.incoming.size === 0
180 - ) {
181 - flowFrozennessForwards(vertex, epoch);
182 - }
183 - }
184 - return vertices;
185 -}
186 -
187 -/**
188 - * Once a value is known to be frozen, all usages forward of that point must be frozen too.
189 - */
190 -function flowFrozennessForwards(vertex: Vertex, epoch: number) {
191 - if (vertex.epoch === epoch) {
192 - return;
193 - }
194 - vertex.epoch = epoch;
195 - vertex.place.capability = Capability.Frozen;
196 - for (const outgoing of vertex.outgoing) {
197 - flowFrozennessForwards(outgoing, epoch);
198 - }
199 -}
200 -
201 -/**
202 - * If a value is known to be mutable, any previous readonly references must be mutable too.
203 - */
204 -function flowMutabilityBackwards(vertex: Vertex, epoch: number) {
205 - if (vertex.epoch === epoch) {
206 - return;
207 - }
208 - vertex.epoch = epoch;
209 - if (vertex.place.capability === Capability.Frozen) {
210 - return;
211 - }
212 - vertex.place.capability = Capability.Mutable;
213 - for (const incoming of vertex.incoming) {
214 - flowMutabilityBackwards(incoming, epoch);
215 - }
216 - for (const [id, capture] of vertex.captures) {
217 - flowMutabilityBackwards(capture, epoch);
218 - }
219 -}
220 -
221 -function inferBlock(graph: UseGraph, block: BasicBlock) {
222 - for (const instr of block.instructions) {
223 - if (instr.lvalue !== null) {
224 - if (instr.lvalue.place.memberPath == null) {
225 - graph.init(instr.lvalue.place, instr);
226 - } else {
227 - graph.reference(instr.lvalue.place, instr, Capability.Mutable);
228 - }
229 - }
230 - const instrValue = instr.value;
231 - let valueCapability = Capability.Readonly;
232 - switch (instrValue.kind) {
233 - case "BinaryExpression": {
234 - valueCapability = Capability.Frozen;
235 - graph.reference(instrValue.left, instrValue, Capability.Readonly);
236 - graph.reference(instrValue.right, instrValue, Capability.Readonly);
237 - break;
238 - }
239 - case "ArrayExpression": {
240 - for (const element of instrValue.elements) {
241 - graph.reference(element, instrValue, Capability.Readonly);
242 - if (instr.lvalue !== null) {
243 - graph.capture(instr.lvalue.place, instr, element);
244 - }
245 - }
246 - break;
247 - }
248 - case "NewExpression": {
249 - graph.reference(instrValue.callee, instrValue, Capability.Mutable);
250 - let prevArg: Place | null = null;
251 - for (const arg of instrValue.args) {
252 - graph.reference(arg, instrValue, Capability.Mutable);
253 - if (instr.lvalue !== null) {
254 - graph.capture(instr.lvalue.place, instr, arg);
255 - }
256 - if (prevArg !== null) {
257 - graph.capture(prevArg, instr, arg);
258 - }
259 - prevArg = arg;
260 - }
261 - break;
262 - }
263 - case "CallExpression": {
264 - let capability = Capability.Mutable;
265 - const hook = parseHookCall(instrValue.callee);
266 - if (hook !== null) {
267 - capability = hook.capability;
268 - valueCapability = hook.capability;
269 - }
270 - graph.reference(instrValue.callee, instrValue, capability);
271 - let prevArg: Place | null = null;
272 - for (const arg of instrValue.args) {
273 - graph.reference(arg, instrValue, capability);
274 - if (instr.lvalue !== null) {
275 - graph.capture(instr.lvalue.place, instr, arg);
276 - }
277 - if (prevArg !== null) {
278 - graph.capture(prevArg, instr, arg);
279 - }
280 - prevArg = arg;
281 - }
282 - break;
283 - }
284 - case "ObjectExpression": {
285 - // Object construction captures but does not modify the key/property values
286 - if (instrValue.properties !== null) {
287 - for (const [_key, value] of Object.entries(instrValue.properties)) {
288 - graph.reference(value, instrValue, Capability.Readonly);
289 - if (instr.lvalue !== null) {
290 - graph.capture(instr.lvalue.place, instr, value);
291 - }
292 - }
293 - }
294 - break;
295 - }
296 - case "UnaryExpression": {
297 - valueCapability = Capability.Frozen; // TODO check that value must be a primitive, or make conditional based on the operator
298 - graph.reference(instrValue.value, instrValue, Capability.Readonly);
299 - break;
300 - }
301 - case "OtherStatement": {
302 - // TODO: handle other statement kinds
303 - break;
304 - }
305 - case "JsxExpression": {
306 - graph.reference(instrValue.tag, instrValue, Capability.Readonly);
307 - for (const [_prop, value] of Object.entries(instrValue.props)) {
308 - graph.reference(value, instrValue, Capability.Readonly);
309 - if (instr.lvalue !== null) {
310 - graph.capture(instr.lvalue.place, instr, value);
311 - }
312 - }
313 - if (instrValue.children !== null) {
314 - for (const child of instrValue.children) {
315 - graph.reference(child, instrValue, Capability.Readonly);
316 - if (instr.lvalue !== null) {
317 - graph.capture(instr.lvalue.place, instr, child);
318 - }
319 - }
320 - }
321 - break;
322 - }
323 - case "JSXText":
324 - case "Primitive": {
325 - valueCapability = Capability.Frozen;
326 - break;
327 - }
328 - case "Identifier": {
329 - graph.reference(instrValue, instrValue, Capability.Readonly);
330 - if (instr.lvalue !== null) {
331 - graph.assign(instr.lvalue.place, instr, instrValue);
332 - }
333 - valueCapability = instrValue.capability;
334 - break;
335 - }
336 - default: {
337 - assertExhaustive(instrValue, "Unexpected instruction kind");
338 - }
339 - }
340 - if (instr.lvalue !== null) {
341 - instr.lvalue.place.capability = valueCapability;
342 - }
343 - }
344 -}
345 -
346 -function parseHookCall(place: Place): Hook | null {
347 - if (place.memberPath !== null) {
348 - // Hook calls must be statically resolved
349 - return null;
350 - }
351 - const name = place.value.name;
352 - if (name === null || !name.match(/^_?use/)) {
353 - return null;
354 - }
355 - const hook = HOOKS.get(name);
356 - if (hook != null) {
357 - return hook;
358 - }
359 - return { kind: "Custom", capability: Capability.Frozen };
360 -}
361 -
362 -/**
363 - * A graph of usages of references within a program.
364 - */
365 -class UseGraph {
366 - /**
367 - * Represents the last usage for each top-level identifier (by IdentifierId)
368 - * in the program. So after `let x = []`, there will be an entry mapping
369 - * `x` to a vertex. A subsequent `x.y` reference will update the mapping
370 - * for `x` to point to a new vertex, with the previous `let x = []` as
371 - * an incoming edge. Note again that vertices are always created based
372 - * on the top-level identifier id and ignore member paths.
373 - */
374 - #nodes: Map<IdentifierId, Vertex>;
375 -
376 - /**
377 - * A mapping of places to vertices, allowing repeated operations against
378 - * the same place (eg referencing, assigning, etc) to update the single
379 - * vertex for that place.
380 - *
381 - * NOTE: this algorithm and data structure relies on `Place` instances
382 - * being unique, hence BuildHIR is careful to clone Place instances rather
383 - * than use structural sharing.
384 - */
385 - #vertices: Map<Place, Vertex>;
386 -
387 - static empty(): UseGraph {
388 - return new UseGraph(new Map(), new Map());
389 - }
390 -
391 - constructor(nodes: Map<IdentifierId, Vertex>, vertices: Map<Place, Vertex>) {
392 - this.#nodes = nodes;
393 - this.#vertices = vertices;
394 - }
395 -
396 - /**
397 - * Lookup the current vertex for a given identifier.
398 - */
399 - lookup(id: IdentifierId): Vertex | null {
400 - return this.#nodes.get(id) ?? null;
401 - }
402 -
403 - /**
404 - * Represents assignment of a value to a Place. Unlike with `reference()`,
405 - * this does *not* establish an edge between this usage of the place and
406 - * previous usages, because the value is not the same.
407 - */
408 - init(place: Place, instr: Instruction | null) {
409 - let vertex = this.#vertices.get(place);
410 - if (vertex == null) {
411 - vertex = new Vertex(place, instr);
412 - this.#vertices.set(place, vertex);
413 - }
414 - this.#nodes.set(place.value.id, vertex);
415 - }
416 -
417 - /**
418 - * Represents assigning a (new) value to @param target via the given @param instr,
419 - * with @param value as the value being assigned.
420 - *
421 - * This breaks the use chain, such that this and subsequent usages of @param target
422 - * are not associated to previous usages. This replicates SSA semantics, conceptually
423 - * @param target is a new place now.
424 - */
425 - assign(target: Place, instr: Instruction, value: Place) {
426 - this.init(target, instr);
427 - if (value.capability === Capability.Frozen) {
428 - target.capability = Capability.Frozen;
429 - }
430 - this.capture(target, instr, value);
431 - }
432 -
433 - /**
434 - * Represents data flow in which two places capture references to each other,
435 - * such that a mutation of one place may affect data accessible via the other
436 - * place or vice versa. This includes assignment (`x = y`) but also things like
437 - * array or object construction, where `x = [y]` means that modifying either
438 - * `x` or `y` _could_ be visible through either refernece (eg `x[0].foo = ...`)
439 - * would modify `y`.
440 - */
441 - capture(target: Place, instr: Instruction, value: Place) {
442 - const targetVertex = this.#vertices.get(target)!;
443 - const valueVertex = this.#vertices.get(value)!;
444 -
445 - if (
446 - targetVertex.place.capability === Capability.Frozen ||
447 - valueVertex.place.capability === Capability.Frozen
448 - ) {
449 - return;
450 - }
451 -
452 - targetVertex.captures.set(value.value.id, valueVertex);
453 - valueVertex.captures.set(target.value.id, targetVertex);
454 - }
455 -
456 - /**
457 - * Represents a reference (usage of) a Place. This establishes an edge
458 - * with this usage and the previous usage
459 - */
460 - reference(
461 - place: Place,
462 - instr: Instruction | InstructionValue | Terminal,
463 - capability: Capability
464 - ) {
465 - place.capability = capability;
466 - let vertex = this.#vertices.get(place);
467 - if (vertex == null) {
468 - vertex = new Vertex(place, instr);
469 - this.#vertices.set(place, vertex);
470 - }
471 -
472 - let prev = this.#nodes.get(place.value.id);
473 - if (prev != null) {
474 - vertex.incoming.add(prev);
475 - prev.outgoing.add(vertex);
476 -
477 - for (const [id, _vertex] of prev.captures) {
478 - const capture = this.#nodes.get(id)!;
479 - if (capture.captures.has(place.value.id)) {
480 - vertex.captures.set(id, capture);
481 - }
482 - }
483 -
484 - if (prev.place.capability === Capability.Frozen) {
485 - vertex.place.capability = Capability.Frozen;
486 - }
487 - }
488 - this.#nodes.set(place.value.id, vertex);
489 - }
490 -
491 - /**
492 - * Returns a snapshot of the graph that can be used to represent different
493 - * control flows paths, and which later may merge together (see `merge()`).
494 - *
495 - * The snapshot contains a distinct mapping of the most recent vertex per
496 - * identifier, but *shares* the mapping of places to vertices (vertices are
497 - * stable across different control flow paths).
498 - *
499 - * The intent is that fixpoint iteration relies on updating the `#nodes`
500 - * mapping of identifiers to most recent vertex, but the underlying graph
501 - * of vertices is intended to be shared across control flow paths.
502 - */
503 - snapshot(): UseGraph {
504 - const nodes = new Map(this.#nodes);
505 - return new UseGraph(nodes, this.#vertices);
506 - }
507 -
508 - /**
509 - * Giver some @param other use graph that shares an ancestor with @param this,
510 - * merges the graphs together and returns whether there were any changes to
511 - * the underlying edges (if any new edges were added).
512 - *
513 - * Note that @param this and @param other must share an ancestor, ie one must
514 - * be derived from a `snapshot()` of the other, or they must both be derived
515 - * from a `snapshot()` of the same instance.
516 - */
517 - merge(other: UseGraph): boolean {
518 - let hasChange = false;
519 - for (const [id, newIncoming] of other.#nodes) {
520 - const prevIncoming = this.#nodes.get(id);
521 - if (prevIncoming == null || newIncoming === prevIncoming) {
522 - continue;
523 - }
524 - for (const prevOutgoing of prevIncoming.outgoing) {
525 - hasChange =
526 - hasChange ||
527 - !prevOutgoing.incoming.has(newIncoming) ||
528 - !newIncoming.outgoing.has(prevOutgoing);
529 - prevOutgoing.incoming.add(newIncoming);
530 - newIncoming.outgoing.add(prevOutgoing);
531 - }
532 - }
533 - return hasChange;
534 - }
535 -
536 - /**
537 - * Returns the mapping of places to vertices.
538 - */
539 - build(): Map<Place, Vertex> {
540 - return this.#vertices;
541 - }
542 -}
543 -
544 -/**
545 - * Represents a distinct usage of a `Place` within a program. Note that
546 - * vertices are only created for top-level identifiers, not for points
547 - * within an object. So both `x` and `x.y` create vertices for `x`,
548 - * though this is enforced by the _construction_ of vertices in UseGraph,
549 - * not the implementation of Vertex itself.
550 - */
551 -class Vertex {
552 - /**
553 - * A set of vertices whose values are captured into this vertex, keyed
554 - * by the identifier of the captured value. This represents any form
555 - * of capturing, including:
556 - * - direct assignment: `x = y`, `x.y = y`, `x[0] = y` all captures
557 - * x into y and vice-versa.
558 - * - composition of objects into arrays, object, jsx: `<foo>{x}{y}</div>`,
559 - * `{k1: x, k2: y}`, and `[x, y]` all capture x and y into the outer
560 - * value (jsx, object, array, respectively).
561 - * - Mutation that could cause values to take references into each other:
562 - * `foo(x, y)` could modify x and/or y in a way that they are assigned
563 - * into each other (eg it could internally do `x.y = y`, `y.x = x` etc).
564 - *
565 - * Note that the representation only stores *one* vertex for each identifier,
566 - * so eg `y.x = x` the `y` vertex can only store a capture of *one* vertex
567 - * for `x`. If there are multiple potentially prior usages of x when this
568 - * occurs, we have to take care that they are both represnted as captured:
569 - *
570 - * ```javascript
571 - * let x;
572 - * if (cond) {
573 - * x = [1];
574 - * } else {
575 - * x = [2]
576 - * }
577 - * y = x;
578 - * ```
579 - * The final line must reflect that y captures *either* of the two possible
580 - * values of `x`. This is achieved by *first* using `x`, *then* capturing.
581 - * The usage will create a single new `x` vertex in the final scope, to which
582 - * there are two incoming edges. Then the capture of `x` into `y` references
583 - * that single x vertex with two incoming edges. Thus the data model can capture
584 - * this case, but the order of construction is important.
585 - *
586 - * The result is a graph such as:
587 - *
588 - * [x @ let x] -> [ x @ x = [1] ] -> [ x @ y = x]
589 - * └--> [ x @ x = [2] ] ---┘ ↑
590 - * | capture
591 - * ↓
592 - * [ y @ y - x]
593 - *
594 - * Note that it's possible to walk from `y` to both of the possible x values.
595 - */
596 - captures: Map<IdentifierId, Vertex> = new Map();
597 -
598 - /**
599 - * the set of vertices where *this* place was previously referenced
600 - */
601 - incoming: Set<Vertex> = new Set();
602 -
603 - /**
604 - * the set of vertices where *this* place is subseqently referenced
605 - */
606 - outgoing: Set<Vertex> = new Set();
607 -
608 - /**
609 - * The place that is using the value.
610 - */
611 - place: Place;
612 -
613 - /**
614 - * The instruction where the reference occurs (for debugging)
615 - */
616 - instr: Instruction | InstructionValue | Terminal | null;
617 -
618 - /**
619 - * The last epoch in which this vertex was visited. This is used during
620 - * data-flow analysis post construction of the graph to ensure termination
621 - * by avoiding revisiting the same nodes in a given pass.
622 - */
623 - epoch: number | null = null;
624 -
625 - constructor(
626 - place: Place,
627 - instr: Instruction | InstructionValue | Terminal | null
628 - ) {
629 - this.place = place;
630 - this.instr = instr;
631 - }
632 -}
633 -
634 -/**
635 - * Prints the graph into GraphViz DOT format.
636 - * https://graphviz.org/doc/info/lang.html
637 - */
638 -export function printGraph(vertices: Map<Place, Vertex>): string {
639 - const output = [];
640 -
641 - // Maps vertices to short string names. Note that multiple vertex instances
642 - // can share the same Place, so we can't use the place (identifier/path etc).
643 - // Instead we create an auto-incrementing index.
644 - const indices: Map<Vertex, string> = new Map();
645 - function identify(vertex: Vertex): string {
646 - let id = indices.get(vertex);
647 - if (id == null) {
648 - id = `v${indices.size}`;
649 - indices.set(vertex, id);
650 - }
651 - return id;
652 - }
653 -
654 - for (const [place, vertex] of vertices) {
655 - const vertexId = identify(vertex);
656 - output.push(
657 - `${vertexId} [ label="${vertexId} (${printPlace(vertex.place)} @ ${
658 - vertex.instr
659 - ? printMixedHIR(vertex.instr).replaceAll('"', '\\"')
660 - : "<no-instr>"
661 - })", shape="box" ]`
662 - );
663 - for (const outgoing of vertex.outgoing) {
664 - const outgoingId = identify(outgoing);
665 - output.push(`${vertexId} -> ${outgoingId}`);
666 - }
667 - for (const [id, capture] of vertex.captures) {
668 - const captureId = identify(capture);
669 - output.push(`${captureId} -> ${vertexId} [ label="capture" ]`);
670 - }
671 - }
672 - const lines = output.map((line) => " " + line);
673 - lines.unshift("digraph InferMutability {");
674 - lines.push("}");
675 - return lines.join("\n");
676 -}
compiler/forget/src/HIR/InferReferenceCapability.ts new
+705
@@ -0,0 +1,705 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import { assertExhaustive } from "../Common/utils";
9 +import { invariant } from "../CompilerError";
10 +import {
11 + BasicBlock,
12 + BlockId,
13 + Capability,
14 + HIRFunction,
15 + IdentifierId,
16 + Instruction,
17 + InstructionValue,
18 + Place,
19 + Terminal,
20 +} from "./HIR";
21 +import { mapTerminalSuccessors } from "./HIRBuilder";
22 +import { printMixedHIR } from "./PrintHIR";
23 +
24 +/**
25 + * For every usage of a value in the given function, infers the capability or action
26 + * taken at that reference. Each reference is inferred as exactly one of:
27 + * - freeze: this usage freezes the value, ie converts it to frozen. This is only inferred
28 + * when the value *may* not already be frozen.
29 + * - frozen: the value is known to already be "owned" by React and is therefore already
30 + * frozen (permanently and transitively immutable).
31 + * - immutable: the value is not owned by React, but is known to be an immutable value
32 + * that therefore cannot ever change.
33 + * - readonly: the value is not frozen or immutable, but this usage of the value does
34 + * not modify it. the value may be mutated by a subsequent reference. Examples include
35 + * referencing the operands of a binary expression, or referencing the items/properties
36 + * of an array or object literal.
37 + * - mutable: the value is not frozen or immutable, and this usage *may* modify it.
38 + * Examples include passing a value to as a function argument or assigning into an object.
39 + *
40 + * Note that the inference follows variable assignment, so assigning a frozen value
41 + * to a different value will infer usages of the other variable as frozen as well.
42 + *
43 + * The inference assumes that the code follows the rules of React:
44 + * - React function arguments are frozen (component props, hook arguments).
45 + * - Hook arguments are frozen at the point the hook is invoked.
46 + * - React function return values are frozen at the point of being returned,
47 + * thus the return value of a hook call is frozen.
48 + * - JSX represents invocation of a React function (the component) and
49 + * therefore all values passed to JSX become frozen at the point the JSX
50 + * is created.
51 + *
52 + * Internally, the inference tracks the approximate type of value held by each variable,
53 + * and iterates over the control flow graph. The inferred capability of reach reference is
54 + * a combination of the operation performed (ie, assignment into an object mutably uses the
55 + * object; an if condition reads the condition) and the type of the value. The types of values
56 + * are:
57 + * - frozen: can be any type so long as the value is known to be owned by React, permanently
58 + * and transitively immutable
59 + * - maybe-frozen: the value may or may not be frozen, conditionally depending on control flow.
60 + * - immutable: a type with value semantics: primitives, records/tuples when standardized.
61 + * - mutable: a type with reference semantics eg array, object, class instance, etc.
62 + *
63 + * When control flow paths converge the types of values are merged together, with the value
64 + * types forming a lattice to ensure convergence.
65 + */
66 +export default function inferReferenceCapability(fn: HIRFunction) {
67 + // Initial environment contains function params
68 + // TODO: include module declarations here as well
69 + const initialEnvironment = Environment.empty();
70 + const id: Place = {
71 + kind: "Identifier",
72 + memberPath: null,
73 + value: fn.id as any,
74 + path: null as any, // TODO
75 + capability: Capability.Freeze,
76 + };
77 + const value: InstructionValue = {
78 + kind: "Primitive",
79 + path: null as any, // TODO
80 + value: undefined,
81 + };
82 + initialEnvironment.initialize(value, ValueKind.Frozen);
83 + initialEnvironment.define(id, value);
84 +
85 + for (const param of fn.params) {
86 + const place: Place = {
87 + kind: "Identifier",
88 + memberPath: null,
89 + value: param,
90 + path: null as any, // TODO
91 + capability: Capability.Freeze,
92 + };
93 + const value: InstructionValue = {
94 + kind: "Primitive",
95 + path: null as any, // TODO
96 + value: undefined,
97 + };
98 + initialEnvironment.initialize(value, ValueKind.Frozen);
99 + initialEnvironment.define(place, value);
100 + }
101 +
102 + // Queue of blocks to visit, with block and the incoming Environment value
103 + const queue: Array<QueueEntry> = [
104 + { blockId: fn.body.entry, environment: initialEnvironment },
105 + ];
106 + // Map of blocks to the last incoming environment that was processed
107 + const environmentsByBlock: Map<BlockId, Environment> = new Map();
108 +
109 + while (queue.length !== 0) {
110 + const { blockId, environment: incomingEnvironment } = queue.shift()!;
111 +
112 + let previousEnvironment = environmentsByBlock.get(blockId);
113 + let nextEnvironment: Environment | null = null;
114 + if (previousEnvironment === undefined) {
115 + // If no previous environment, save the incoming environment and
116 + // infer the block with this environment
117 + environmentsByBlock.set(blockId, incomingEnvironment);
118 + nextEnvironment = incomingEnvironment.clone();
119 + } else {
120 + // If there's a previous environment, merge the previous/new incoming
121 + // environments. If there are no changes, then the block can be skipped.
122 + // otherwise save the merged environment and revisit the block with it.
123 + const mergedEnvironment = previousEnvironment.merge(incomingEnvironment);
124 + if (mergedEnvironment !== null) {
125 + environmentsByBlock.set(blockId, mergedEnvironment);
126 + nextEnvironment = mergedEnvironment.clone();
127 + } else {
128 + continue;
129 + }
130 + }
131 +
132 + const environment = nextEnvironment; // rebind to preserve the non-null refinement
133 + const block = fn.body.blocks.get(blockId)!;
134 + inferBlock(environment, block);
135 +
136 + // TODO: add a `forEachTerminalSuccessor` helper, we don't actually want the result
137 + // here
138 + const _ = mapTerminalSuccessors(
139 + block.terminal,
140 + (nextBlockId, isFallthrough) => {
141 + if (!isFallthrough) {
142 + queue.push({ blockId: nextBlockId, environment });
143 + }
144 + return nextBlockId;
145 + }
146 + );
147 + }
148 +}
149 +
150 +type QueueEntry = {
151 + blockId: BlockId;
152 + environment: Environment;
153 +};
154 +
155 +/**
156 + * Maintains a mapping of top-level variables to the kind of value they hold
157 + */
158 +class Environment {
159 + // The kind of reach value, based on its allocation site
160 + #values: Map<InstructionValue, ValueKind>;
161 + // The set of values pointed to by each identifier. This is a set
162 + // to accomodate phi points (where a variable may have different
163 + // values from different control flow paths).
164 + #variables: Map<IdentifierId, Set<InstructionValue>>;
165 +
166 + constructor(
167 + values: Map<InstructionValue, ValueKind>,
168 + variables: Map<IdentifierId, Set<InstructionValue>>
169 + ) {
170 + this.#values = values;
171 + this.#variables = variables;
172 + }
173 +
174 + static empty(): Environment {
175 + return new Environment(new Map(), new Map());
176 + }
177 +
178 + /**
179 + * (Re)initializes a @param value with its default @param kind.
180 + */
181 + initialize(
182 + value: InstructionValue,
183 + kind: ValueKind,
184 + instr: Instruction | null = null
185 + ) {
186 + invariant(
187 + value.kind !== "Identifier" || value.memberPath !== null,
188 + "Expected all top-level identifiers to be defined as variables, not values"
189 + );
190 + this.#values.set(value, kind);
191 + }
192 +
193 + /**
194 + * Lookup the kind of the given @param value.
195 + */
196 + kind(place: Place): ValueKind {
197 + const values = this.#variables.get(place.value.id);
198 + invariant(
199 + values != null,
200 + `Expected value kind to be initialized at '${String(place.path)}'`
201 + );
202 + let mergedKind: ValueKind | null = null;
203 + for (const value of values) {
204 + const kind = this.#values.get(value)!;
205 + mergedKind = mergedKind !== null ? mergeValues(mergedKind, kind) : kind;
206 + }
207 + invariant(mergedKind !== null, "Expected at least value");
208 + return mergedKind;
209 + }
210 +
211 + /**
212 + * Updates the value at @param place to point to the same value as @param value.
213 + */
214 + alias(place: Place, value: Place) {
215 + const values = this.#variables.get(value.value.id);
216 + invariant(
217 + values != null,
218 + `Expected value to be populated at '${String(value.path)}' in '${String(
219 + value.path.parentPath
220 + )}'`
221 + );
222 + this.#variables.set(place.value.id, new Set(values));
223 + }
224 +
225 + /**
226 + * Defines (initializing or updating) a variable with a specific kind of value.
227 + */
228 + define(place: Place, value: InstructionValue) {
229 + invariant(
230 + place.memberPath === null,
231 + "Expected a top-level identifier, not a member path"
232 + );
233 + invariant(
234 + this.#values.has(value),
235 + `Expected value to be initialized at '${String(value.path)}' in '${String(
236 + value.path?.parentPath
237 + )}'`
238 + );
239 + this.#variables.set(place.value.id, new Set([value]));
240 + }
241 +
242 + /**
243 + * Records that a given Place was accessed with the given kind and:
244 + * - Updates the capability of @param place based on the kind of value
245 + * and the kind of reference (@param effectKind).
246 + * - Updates the value kind to reflect the effect of the reference.
247 + *
248 + * Notably, a mutable reference is downgraded to readonly if the
249 + * value unless the value is known to be mutable.
250 + *
251 + * Similarly, a freeze reference is converted to readonly if the
252 + * value is already frozen or is immutable.
253 + */
254 + reference(place: Place, effectKind: EffectKind) {
255 + const values = this.#variables.get(place.value.id);
256 + if (values === undefined) {
257 + place.capability =
258 + effectKind === EffectKind.Write
259 + ? Capability.Mutable
260 + : Capability.Readonly;
261 + return;
262 + }
263 + let capability: Capability | null = null;
264 + switch (effectKind) {
265 + case EffectKind.Freeze: {
266 + values.forEach((value) => {
267 + const valueKind = this.#values.get(value)!;
268 + if (
269 + valueKind === ValueKind.Mutable ||
270 + valueKind === ValueKind.MaybeFrozen
271 + ) {
272 + this.#values.set(value, ValueKind.Frozen);
273 + capability = Capability.Freeze;
274 + }
275 + });
276 + capability = capability ?? Capability.Readonly;
277 + break;
278 + }
279 + case EffectKind.Write: {
280 + let maybeFrozen = false;
281 + let maybeMutable = false;
282 + values.forEach((value) => {
283 + const valueKind = this.#values.get(value)!;
284 + if (
285 + valueKind === ValueKind.Frozen ||
286 + valueKind === ValueKind.MaybeFrozen
287 + ) {
288 + maybeFrozen = true;
289 + } else if (valueKind === ValueKind.Mutable) {
290 + maybeMutable = true;
291 + }
292 + });
293 + capability = maybeFrozen
294 + ? Capability.Readonly
295 + : maybeMutable
296 + ? Capability.Mutable
297 + : Capability.Readonly;
298 + break;
299 + }
300 + case EffectKind.Read: {
301 + capability = Capability.Readonly;
302 + break;
303 + }
304 + default: {
305 + assertExhaustive(
306 + effectKind,
307 + `Unexpected reference kind '${effectKind as any as string}'`
308 + );
309 + }
310 + }
311 + invariant(capability !== null, "Expected capability to be set");
312 + place.capability = capability;
313 + }
314 +
315 + /**
316 + * Combine the contents of @param this and @param other, returning a new
317 + * instance with the combined changes _if_ there are any changes, or
318 + * returning null if no changes would occur. Changes include:
319 + * - new entries in @param other that did not exist in @param this
320 + * - entries whose values differ in @param this and @param other,
321 + * and where joining the values produces a different value than
322 + * what was in @param this.
323 + *
324 + * Note that values are joined using a lattice operation to ensure
325 + * termination.
326 + */
327 + merge(other: Environment): Environment | null {
328 + let nextValues: Map<InstructionValue, ValueKind> | null = null;
329 + let nextVariables: Map<IdentifierId, Set<InstructionValue>> | null = null;
330 +
331 + for (const [id, thisValue] of this.#values) {
332 + const otherValue = other.#values.get(id);
333 + if (otherValue !== undefined) {
334 + const mergedValue = mergeValues(thisValue, otherValue);
335 + if (mergedValue !== thisValue) {
336 + nextValues = nextValues ?? new Map(this.#values);
337 + nextValues.set(id, mergedValue);
338 + }
339 + }
340 + }
341 + for (const [id, otherValue] of other.#values) {
342 + if (this.#values.has(id)) {
343 + // merged above
344 + continue;
345 + }
346 + nextValues = nextValues ?? new Map(this.#values);
347 + nextValues.set(id, otherValue);
348 + }
349 +
350 + for (const [id, thisValues] of this.#variables) {
351 + const otherValues = other.#variables.get(id);
352 + if (otherValues !== undefined) {
353 + let mergedValues: Set<InstructionValue> | null = null;
354 + for (const otherValue of otherValues) {
355 + if (!thisValues.has(otherValue)) {
356 + mergedValues = mergedValues ?? new Set(thisValues);
357 + mergedValues.add(otherValue);
358 + }
359 + }
360 + if (mergedValues !== null) {
361 + nextVariables = nextVariables ?? new Map(this.#variables);
362 + nextVariables.set(id, mergedValues);
363 + }
364 + }
365 + }
366 + for (const [id, otherValues] of other.#variables) {
367 + if (this.#variables.has(id)) {
368 + continue;
369 + }
370 + nextVariables = nextVariables ?? new Map(this.#variables);
371 + nextVariables.set(id, new Set(otherValues));
372 + }
373 +
374 + if (nextVariables === null && nextValues === null) {
375 + return null;
376 + } else {
377 + return new Environment(
378 + nextValues ?? new Map(this.#values),
379 + nextVariables ?? new Map(this.#variables)
380 + );
381 + }
382 + }
383 +
384 + /**
385 + * Returns a copy of this environment.
386 + * TODO: consider using persistent data structures to make
387 + * clone cheaper.
388 + */
389 + clone(): Environment {
390 + return new Environment(new Map(this.#values), new Map(this.#variables));
391 + }
392 +
393 + /**
394 + * For debugging purposes, dumps the environment to a plain
395 + * object so that it can printed as JSON.
396 + */
397 + debug(): any {
398 + const result: any = { values: {}, variables: {} };
399 + const objects: Map<InstructionValue, number> = new Map();
400 + function identify(value: InstructionValue): number {
401 + let id = objects.get(value);
402 + if (id == null) {
403 + id = objects.size;
404 + objects.set(value, id);
405 + }
406 + return id;
407 + }
408 + for (const [value, kind] of this.#values) {
409 + const id = identify(value);
410 + result.values[id] = { kind, value: printMixedHIR(value) };
411 + }
412 + for (const [variable, values] of this.#variables) {
413 + result.variables[variable] = [...values].map(identify);
414 + }
415 + return result;
416 + }
417 +}
418 +
419 +/**
420 + * Joins two values using the following rules:
421 + * == Effect Transitions ==
422 + *
423 + * Freezing an immutable value has not effect:
424 + * ┌───────────────┐
425 + * │ │
426 + * ▼ │ Freeze
427 + * ┌──────────────────────────┐ │
428 + * │ Immutable │──┘
429 + * └──────────────────────────┘
430 + *
431 + * Freezing a mutable or maybe-frozen value makes it frozen. Freezing a frozen
432 + * value has no effect:
433 + * ┌───────────────┐
434 + * ┌─────────────────────────┐ Freeze │ │
435 + * │ MaybeFrozen │────┐ ▼ │ Freeze
436 + * └─────────────────────────┘ │ ┌──────────────────────────┐ │
437 + * ├────▶│ Frozen │──┘
438 + * │ └──────────────────────────┘
439 + * ┌─────────────────────────┐ │
440 + * │ Mutable │────┘
441 + * └─────────────────────────┘
442 + *
443 + * == Join Lattice ==
444 + * - immutable | frozen => frozen
445 + * - frozen | mutable => maybe-frozen
446 + * - <any> | maybe-frozen => maybe-frozen
447 + *
448 + * ┌──────────────────────────┐
449 + * │ Immutable │───┐
450 + * └──────────────────────────┘ │
451 + * │ ┌─────────────────────────┐
452 + * ├───▶│ Frozen │──┐
453 + * ┌──────────────────────────┐ │ └─────────────────────────┘ │
454 + * │ Frozen │───┤ │ ┌─────────────────────────┐
455 + * └──────────────────────────┘ │ ├─▶│ MaybeFrozen │
456 + * │ ┌─────────────────────────┐ │ └─────────────────────────┘
457 + * ├───▶│ MaybeFrozen │──┘
458 + * ┌──────────────────────────┐ │ └─────────────────────────┘
459 + * │ Mutable │───┘
460 + * └──────────────────────────┘
461 + */
462 +function mergeValues(a: ValueKind, b: ValueKind): ValueKind {
463 + if (a === b) {
464 + return a;
465 + } else if (a === ValueKind.MaybeFrozen || b === ValueKind.MaybeFrozen) {
466 + return ValueKind.MaybeFrozen;
467 + // after this a and b differ and neither are MaybeFrozen
468 + } else if (a === ValueKind.Mutable || b === ValueKind.Mutable) {
469 + if (a === ValueKind.Frozen || b === ValueKind.Frozen) {
470 + // frozen | mutable
471 + return ValueKind.MaybeFrozen;
472 + } else {
473 + // mutable | immutable
474 + return ValueKind.Mutable;
475 + }
476 + } else {
477 + // frozen | immutable
478 + return ValueKind.Frozen;
479 + }
480 +}
481 +
482 +/**
483 + * Distinguish between different kinds of values relevant to inference purposes:
484 + * see the main docblock for the module for details.
485 + */
486 +enum ValueKind {
487 + MaybeFrozen = "MaybeFrozen",
488 + Frozen = "Frozen",
489 + Immutable = "Immutable",
490 + Mutable = "Mutable",
491 +}
492 +
493 +/**
494 + * Distinguish between different kinds of references.
495 + */
496 +enum EffectKind {
497 + Write = "Write",
498 + Read = "Read",
499 + Freeze = "Freeze",
500 +}
501 +
502 +/**
503 + * Iterates over the given @param block, defining variables and
504 + * recording references on the @param env according to JS semantics.
505 + */
506 +function inferBlock(env: Environment, block: BasicBlock) {
507 + for (const instr of block.instructions) {
508 + const instrValue = instr.value;
509 + let valueKind: ValueKind;
510 + switch (instrValue.kind) {
511 + case "BinaryExpression": {
512 + valueKind = ValueKind.Immutable;
513 + env.reference(instrValue.left, EffectKind.Read);
514 + env.reference(instrValue.right, EffectKind.Read);
515 + break;
516 + }
517 + case "ArrayExpression": {
518 + valueKind = ValueKind.Mutable;
519 + for (const element of instrValue.elements) {
520 + env.reference(element, EffectKind.Read);
521 + }
522 + break;
523 + }
524 + case "NewExpression": {
525 + valueKind = ValueKind.Mutable;
526 + env.reference(instrValue.callee, EffectKind.Write);
527 + for (const arg of instrValue.args) {
528 + env.reference(arg, EffectKind.Write);
529 + }
530 + break;
531 + }
532 + case "CallExpression": {
533 + let effectKind = EffectKind.Write;
534 + valueKind = ValueKind.Mutable;
535 + const hook = parseHookCall(instrValue.callee);
536 + if (hook !== null) {
537 + effectKind = hook.effectKind;
538 + valueKind = hook.valueKind;
539 + }
540 + env.reference(instrValue.callee, effectKind);
541 + for (const arg of instrValue.args) {
542 + env.reference(arg, effectKind);
543 + }
544 + break;
545 + }
546 + case "ObjectExpression": {
547 + valueKind = ValueKind.Mutable;
548 + // Object construction captures but does not modify the key/property values
549 + if (instrValue.properties !== null) {
550 + for (const [_key, value] of Object.entries(instrValue.properties)) {
551 + env.reference(value, EffectKind.Read);
552 + }
553 + }
554 + break;
555 + }
556 + case "UnaryExpression": {
557 + // TODO check that value must be a primitive, or make conditional based on the operator
558 + valueKind = ValueKind.Immutable;
559 + env.reference(instrValue.value, EffectKind.Read);
560 + break;
561 + }
562 + case "OtherStatement": {
563 + // TODO: handle other statement kinds
564 + valueKind = ValueKind.Mutable;
565 + break;
566 + }
567 + case "JsxExpression": {
568 + valueKind = ValueKind.Frozen;
569 + env.reference(instrValue.tag, EffectKind.Freeze);
570 + for (const [_prop, value] of Object.entries(instrValue.props)) {
571 + env.reference(value, EffectKind.Freeze);
572 + }
573 + if (instrValue.children !== null) {
574 + for (const child of instrValue.children) {
575 + env.reference(child, EffectKind.Freeze);
576 + }
577 + }
578 + break;
579 + }
580 + case "JSXText":
581 + case "Primitive": {
582 + valueKind = ValueKind.Immutable;
583 + break;
584 + }
585 + case "Identifier": {
586 + env.reference(instrValue, EffectKind.Read);
587 + const lvalue = instr.lvalue;
588 + if (lvalue !== null) {
589 + lvalue.place.capability = Capability.Mutable;
590 + if (
591 + lvalue.place.memberPath === null &&
592 + instrValue.memberPath === null
593 + ) {
594 + // direct aliasing: `a = b`;
595 + env.alias(lvalue.place, instrValue);
596 + } else if (lvalue.place.memberPath === null) {
597 + // redefine lvalue: `a = b.c.d`
598 + env.initialize(instrValue, env.kind(instrValue));
599 + env.define(lvalue.place, instrValue);
600 + } else if (instrValue.memberPath === null) {
601 + // no-op: `a.b.c = d`
602 + env.reference(lvalue.place, EffectKind.Write);
603 + } else {
604 + // no-op: `a.b.c = d.e.f`
605 + env.reference(lvalue.place, EffectKind.Write);
606 + }
607 + }
608 + continue;
609 + }
610 + default: {
611 + assertExhaustive(instrValue, "Unexpected instruction kind");
612 + }
613 + }
614 + env.initialize(instrValue, valueKind, instr);
615 + if (instr.lvalue !== null) {
616 + if (instr.lvalue.place.memberPath === null) {
617 + env.define(instr.lvalue.place, instrValue);
618 + } else {
619 + env.reference(instr.lvalue.place, EffectKind.Write);
620 + }
621 + instr.lvalue.place.capability = Capability.Mutable;
622 + }
623 + }
624 + switch (block.terminal.kind) {
625 + case "throw": {
626 + env.reference(block.terminal.value, EffectKind.Freeze);
627 + break;
628 + }
629 + case "return": {
630 + if (block.terminal.value !== null) {
631 + env.reference(block.terminal.value, EffectKind.Freeze);
632 + }
633 + break;
634 + }
635 + case "if": {
636 + env.reference(block.terminal.test, EffectKind.Read);
637 + break;
638 + }
639 + case "switch": {
640 + for (const case_ of block.terminal.cases) {
641 + if (case_.test !== null) {
642 + env.reference(case_.test, EffectKind.Read);
643 + }
644 + }
645 + break;
646 + }
647 + case "goto": {
648 + break;
649 + }
650 + default: {
651 + assertExhaustive(
652 + block.terminal,
653 + `Unexpected terminal kind '${(block.terminal as any as Terminal).kind}'`
654 + );
655 + }
656 + }
657 +}
658 +
659 +const GLOBALS: Map<string, ValueKind> = new Map([
660 + ["Map", ValueKind.Mutable],
661 + ["Set", ValueKind.Mutable],
662 + ["Math.max", ValueKind.Immutable],
663 +]);
664 +
665 +const HOOKS: Map<string, Hook> = new Map([
666 + [
667 + "useState",
668 + {
669 + kind: "State",
670 + effectKind: EffectKind.Freeze,
671 + valueKind: ValueKind.Frozen,
672 + },
673 + ],
674 + [
675 + "useRef",
676 + {
677 + kind: "Ref",
678 + effectKind: EffectKind.Read,
679 + valueKind: ValueKind.Mutable,
680 + },
681 + ],
682 +]);
683 +
684 +type HookKind = { kind: "State" } | { kind: "Ref" } | { kind: "Custom" };
685 +type Hook = HookKind & { effectKind: EffectKind; valueKind: ValueKind };
686 +
687 +function parseHookCall(place: Place): Hook | null {
688 + if (place.memberPath !== null) {
689 + // Hook calls must be statically resolved
690 + return null;
691 + }
692 + const name = place.value.name;
693 + if (name === null || !name.match(/^_?use/)) {
694 + return null;
695 + }
696 + const hook = HOOKS.get(name);
697 + if (hook != null) {
698 + return hook;
699 + }
700 + return {
701 + kind: "Custom",
702 + effectKind: EffectKind.Freeze,
703 + valueKind: ValueKind.Frozen,
704 + };
705 +}
compiler/forget/src/HIR/ScopeAnalysis.ts
+1 -1
@@ -66,7 +66,7 @@ export default function analyzeScopes(fn: HIRFunction): ReactFunction {
66 kind: "Identifier",
67 value: param,
68 memberPath: null,
69 - capability: Capability.Frozen,
69 + capability: Capability.Freeze,
70 path: null as any,
71 }))
72 ),
compiler/forget/src/__tests__/fixtures/hir-svg/component.svg
+242 -242
@@ -4,721 +4,721 @@
4 <!-- Generated by graphviz version 6.0.1 (20220924.0800)
5 -->
6 <!-- Title: BuildDefUseGraph Pages: 1 -->
7 -<svg width="13574pt" height="482pt"
8 - viewBox="0.00 0.00 13573.52 481.60" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
7 +<svg width="13456pt" height="482pt"
8 + viewBox="0.00 0.00 13455.59 481.60" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
9 <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 477.6)">
10 <title>BuildDefUseGraph</title>
11 -<polygon fill="white" stroke="none" points="-4,4 -4,-477.6 13569.52,-477.6 13569.52,4 -4,4"/>
11 +<polygon fill="white" stroke="none" points="-4,4 -4,-477.6 13451.59,-477.6 13451.59,4 -4,4"/>
12 <!-- v0 -->
13 <g id="node1" class="node">
14 <title>v0</title>
15 -<polygon fill="none" stroke="black" points="627.43,-396 272.01,-396 272.01,-360 627.43,-360 627.43,-396"/>
16 -<text text-anchor="middle" x="449.72" y="-373.8" font-family="Times,serif" font-size="14.00">v0 (frozen props$1.items @ frozen props$1.items) &lt;update&gt;</text>
15 +<polygon fill="none" stroke="black" points="622.43,-396 270.29,-396 270.29,-360 622.43,-360 622.43,-396"/>
16 +<text text-anchor="middle" x="446.36" y="-373.8" font-family="Times,serif" font-size="14.00">v0 (freeze props$1.items @ freeze props$1.items) &lt;update&gt;</text>
17 </g>
18 <!-- v1 -->
19 <g id="node2" class="node">
20 <title>v1</title>
21 -<polygon fill="none" stroke="black" points="453.66,-324 -0.22,-324 -0.22,-288 453.66,-288 453.66,-324"/>
22 -<text text-anchor="middle" x="226.72" y="-301.8" font-family="Times,serif" font-size="14.00">v1 (frozen items$2 @ Const frozen items$2 = frozen props$1.items) &lt;assign&gt;</text>
21 +<polygon fill="none" stroke="black" points="448.58,-324 0.14,-324 0.14,-288 448.58,-288 448.58,-324"/>
22 +<text text-anchor="middle" x="224.36" y="-301.8" font-family="Times,serif" font-size="14.00">v1 (freeze items$2 @ Const freeze items$2 = freeze props$1.items) &lt;assign&gt;</text>
23 </g>
24 <!-- v0&#45;&gt;v1 -->
25 <g id="edge1" class="edge">
26 <title>v0&#45;&gt;v1</title>
27 -<path fill="none" stroke="black" d="M395.45,-359.97C363.89,-350.06 323.93,-337.51 290.82,-327.12"/>
28 -<polygon fill="black" stroke="black" points="291.82,-323.77 281.23,-324.11 289.72,-330.45 291.82,-323.77"/>
27 +<path fill="none" stroke="black" d="M392.34,-359.97C360.91,-350.06 321.13,-337.51 288.17,-327.12"/>
28 +<polygon fill="black" stroke="black" points="289.22,-323.78 278.63,-324.11 287.11,-330.46 289.22,-323.78"/>
29 </g>
30 <!-- v2 -->
31 <g id="node3" class="node">
32 <title>v2</title>
33 -<polygon fill="none" stroke="black" points="887.68,-324 481.76,-324 481.76,-288 887.68,-288 887.68,-324"/>
34 -<text text-anchor="middle" x="684.72" y="-301.8" font-family="Times,serif" font-size="14.00">v2 (frozen props$1.maxItems @ frozen props$1.maxItems) &lt;update&gt;</text>
33 +<polygon fill="none" stroke="black" points="880.68,-324 478.04,-324 478.04,-288 880.68,-288 880.68,-324"/>
34 +<text text-anchor="middle" x="679.36" y="-301.8" font-family="Times,serif" font-size="14.00">v2 (freeze props$1.maxItems @ freeze props$1.maxItems) &lt;update&gt;</text>
35 </g>
36 <!-- v0&#45;&gt;v2 -->
37 <g id="edge2" class="edge">
38 <title>v0&#45;&gt;v2</title>
39 -<path fill="none" stroke="black" d="M506.91,-359.97C540.31,-350.01 582.64,-337.41 617.62,-326.99"/>
40 -<polygon fill="black" stroke="black" points="618.69,-330.32 627.28,-324.11 616.69,-323.61 618.69,-330.32"/>
39 +<path fill="none" stroke="black" d="M503.06,-359.97C536.04,-350.06 577.79,-337.51 612.39,-327.12"/>
40 +<polygon fill="black" stroke="black" points="613.84,-330.34 622.41,-324.11 611.82,-323.64 613.84,-330.34"/>
41 </g>
42 <!-- v12 -->
43 <g id="node4" class="node">
44 <title>v12</title>
45 -<polygon fill="none" stroke="black" points="425.01,-252 6.43,-252 6.43,-216 425.01,-216 425.01,-252"/>
46 -<text text-anchor="middle" x="215.72" y="-229.8" font-family="Times,serif" font-size="14.00">v12 (frozen items$2 @ If (frozen items$2) then:bb3 else:bb2) &lt;update&gt;</text>
45 +<polygon fill="none" stroke="black" points="421,-252 5.72,-252 5.72,-216 421,-216 421,-252"/>
46 +<text text-anchor="middle" x="213.36" y="-229.8" font-family="Times,serif" font-size="14.00">v12 (freeze items$2 @ If (freeze items$2) then:bb3 else:bb2) &lt;update&gt;</text>
47 </g>
48 <!-- v1&#45;&gt;v12 -->
49 <g id="edge3" class="edge">
50 <title>v1&#45;&gt;v12</title>
51 -<path fill="none" stroke="black" d="M224,-287.7C222.79,-279.98 221.33,-270.71 219.98,-262.11"/>
52 -<polygon fill="black" stroke="black" points="223.42,-261.44 218.41,-252.1 216.5,-262.53 223.42,-261.44"/>
51 +<path fill="none" stroke="black" d="M221.64,-287.7C220.43,-279.98 218.97,-270.71 217.62,-262.11"/>
52 +<polygon fill="black" stroke="black" points="221.06,-261.44 216.05,-252.1 214.14,-262.53 221.06,-261.44"/>
53 </g>
54 <!-- v3 -->
55 <g id="node5" class="node">
56 <title>v3</title>
57 -<polygon fill="none" stroke="black" points="970.79,-252 442.65,-252 442.65,-216 970.79,-216 970.79,-252"/>
58 -<text text-anchor="middle" x="706.72" y="-229.8" font-family="Times,serif" font-size="14.00">v3 (frozen maxItems$3 @ Const frozen maxItems$3 = frozen props$1.maxItems) &lt;assign&gt;</text>
57 +<polygon fill="none" stroke="black" points="962.21,-252 438.51,-252 438.51,-216 962.21,-216 962.21,-252"/>
58 +<text text-anchor="middle" x="700.36" y="-229.8" font-family="Times,serif" font-size="14.00">v3 (freeze maxItems$3 @ Const freeze maxItems$3 = freeze props$1.maxItems) &lt;assign&gt;</text>
59 </g>
60 <!-- v2&#45;&gt;v3 -->
61 <g id="edge4" class="edge">
62 <title>v2&#45;&gt;v3</title>
63 -<path fill="none" stroke="black" d="M690.16,-287.7C692.61,-279.9 695.56,-270.51 698.29,-261.83"/>
64 -<polygon fill="black" stroke="black" points="701.68,-262.69 701.34,-252.1 695.01,-260.59 701.68,-262.69"/>
63 +<path fill="none" stroke="black" d="M684.55,-287.7C686.89,-279.9 689.71,-270.51 692.31,-261.83"/>
64 +<polygon fill="black" stroke="black" points="695.71,-262.69 695.23,-252.1 689,-260.68 695.71,-262.69"/>
65 </g>
66 <!-- v10 -->
67 <g id="node6" class="node">
68 <title>v10</title>
69 -<polygon fill="none" stroke="black" points="990.97,-180 422.47,-180 422.47,-144 990.97,-144 990.97,-180"/>
70 -<text text-anchor="middle" x="706.72" y="-157.8" font-family="Times,serif" font-size="14.00">v10 (frozen maxItems$3 @ Call mutable Math$8.max(mutable $9, frozen maxItems$3)) &lt;update&gt;</text>
69 +<polygon fill="none" stroke="black" points="982.96,-180 417.76,-180 417.76,-144 982.96,-144 982.96,-180"/>
70 +<text text-anchor="middle" x="700.36" y="-157.8" font-family="Times,serif" font-size="14.00">v10 (freeze maxItems$3 @ Call mutable Math$8.max(mutable $9, freeze maxItems$3)) &lt;update&gt;</text>
71 </g>
72 <!-- v3&#45;&gt;v10 -->
73 <g id="edge5" class="edge">
74 <title>v3&#45;&gt;v10</title>
75 -<path fill="none" stroke="black" d="M706.72,-215.7C706.72,-207.98 706.72,-198.71 706.72,-190.11"/>
76 -<polygon fill="black" stroke="black" points="710.22,-190.1 706.72,-180.1 703.22,-190.1 710.22,-190.1"/>
75 +<path fill="none" stroke="black" d="M700.36,-215.7C700.36,-207.98 700.36,-198.71 700.36,-190.11"/>
76 +<polygon fill="black" stroke="black" points="703.86,-190.1 700.36,-180.1 696.86,-190.1 703.86,-190.1"/>
77 </g>
78 <!-- v4 -->
79 <g id="node7" class="node">
80 <title>v4</title>
81 -<polygon fill="none" stroke="black" points="1725.87,-470.8 1219.57,-470.8 1219.57,-434.8 1725.87,-434.8 1725.87,-470.8"/>
82 -<text text-anchor="middle" x="1472.72" y="-448.6" font-family="Times,serif" font-size="14.00">v4 (readonly renderedItems$4 @ Const readonly renderedItems$4 = Array []) &lt;assign&gt;</text>
81 +<polygon fill="none" stroke="black" points="1716.51,-470.8 1210.21,-470.8 1210.21,-434.8 1716.51,-434.8 1716.51,-470.8"/>
82 +<text text-anchor="middle" x="1463.36" y="-448.6" font-family="Times,serif" font-size="14.00">v4 (readonly renderedItems$4 @ Const readonly renderedItems$4 = Array []) &lt;assign&gt;</text>
83 </g>
84 <!-- v18 -->
85 <g id="node8" class="node">
86 <title>v18</title>
87 -<polygon fill="none" stroke="black" points="1927.13,-180 1430.31,-180 1430.31,-144 1927.13,-144 1927.13,-180"/>
88 -<text text-anchor="middle" x="1678.72" y="-157.8" font-family="Times,serif" font-size="14.00">v18 (readonly renderedItems$4.length @ readonly renderedItems$4.length) &lt;update&gt;</text>
87 +<polygon fill="none" stroke="black" points="1917.77,-180 1420.95,-180 1420.95,-144 1917.77,-144 1917.77,-180"/>
88 +<text text-anchor="middle" x="1669.36" y="-157.8" font-family="Times,serif" font-size="14.00">v18 (readonly renderedItems$4.length @ readonly renderedItems$4.length) &lt;update&gt;</text>
89 </g>
90 <!-- v4&#45;&gt;v18 -->
91 <g id="edge6" class="edge">
92 <title>v4&#45;&gt;v18</title>
93 -<path fill="none" stroke="black" d="M1658.17,-434.74C1713.55,-426.26 1765.05,-413.87 1785.72,-396 1846.43,-343.53 1848.15,-279.98 1799.72,-216 1789.25,-202.17 1774.4,-191.88 1758.67,-184.25"/>
94 -<polygon fill="black" stroke="black" points="1759.89,-180.97 1749.34,-180.06 1757.03,-187.35 1759.89,-180.97"/>
93 +<path fill="none" stroke="black" d="M1648.81,-434.74C1704.2,-426.26 1755.69,-413.87 1776.36,-396 1837.07,-343.53 1838.79,-279.98 1790.36,-216 1779.89,-202.17 1765.04,-191.88 1749.31,-184.25"/>
94 +<polygon fill="black" stroke="black" points="1750.54,-180.97 1739.98,-180.06 1747.67,-187.35 1750.54,-180.97"/>
95 </g>
96 <!-- v49 -->
97 <g id="node9" class="node">
98 <title>v49</title>
99 -<polygon fill="none" stroke="black" points="1743.52,-396 1165.91,-396 1165.91,-360 1743.52,-360 1743.52,-396"/>
100 -<text text-anchor="middle" x="1454.72" y="-373.8" font-family="Times,serif" font-size="14.00">v49 (mutable renderedItems$4.push @ Call mutable renderedItems$4.push(mutable $15)) &lt;update&gt;</text>
99 +<polygon fill="none" stroke="black" points="1734.17,-396 1156.56,-396 1156.56,-360 1734.17,-360 1734.17,-396"/>
100 +<text text-anchor="middle" x="1445.36" y="-373.8" font-family="Times,serif" font-size="14.00">v49 (mutable renderedItems$4.push @ Call mutable renderedItems$4.push(mutable $15)) &lt;update&gt;</text>
101 </g>
102 <!-- v4&#45;&gt;v49 -->
103 <g id="edge7" class="edge">
104 <title>v4&#45;&gt;v49</title>
105 -<path fill="none" stroke="black" d="M1468.45,-434.55C1466.35,-426.03 1463.76,-415.55 1461.39,-405.99"/>
106 -<polygon fill="black" stroke="black" points="1464.75,-405 1458.95,-396.13 1457.96,-406.68 1464.75,-405"/>
105 +<path fill="none" stroke="black" d="M1459.1,-434.55C1456.99,-426.03 1454.4,-415.55 1452.03,-405.99"/>
106 +<polygon fill="black" stroke="black" points="1455.39,-405 1449.6,-396.13 1448.6,-406.68 1455.39,-405"/>
107 </g>
108 <!-- v62 -->
109 <g id="node10" class="node">
110 <title>v62</title>
111 -<polygon fill="none" stroke="black" points="1790.46,-252 1566.98,-252 1566.98,-216 1790.46,-216 1790.46,-252"/>
112 -<text text-anchor="middle" x="1678.72" y="-229.8" font-family="Times,serif" font-size="14.00">v62 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
111 +<polygon fill="none" stroke="black" points="1781.1,-252 1557.62,-252 1557.62,-216 1781.1,-216 1781.1,-252"/>
112 +<text text-anchor="middle" x="1669.36" y="-229.8" font-family="Times,serif" font-size="14.00">v62 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
113 </g>
114 <!-- v4&#45;&gt;v62 -->
115 <g id="edge8" class="edge">
116 <title>v4&#45;&gt;v62</title>
117 -<path fill="none" stroke="black" d="M1616.67,-434.75C1673.44,-425.66 1730.41,-412.83 1752.72,-396 1793.22,-365.44 1816.79,-330.92 1789.72,-288 1781.09,-274.31 1767.84,-264.09 1753.47,-256.47"/>
118 -<polygon fill="black" stroke="black" points="1754.93,-253.29 1744.4,-252.05 1751.86,-259.58 1754.93,-253.29"/>
117 +<path fill="none" stroke="black" d="M1607.31,-434.75C1664.08,-425.66 1721.05,-412.83 1743.36,-396 1783.86,-365.44 1807.43,-330.92 1780.36,-288 1771.73,-274.31 1758.48,-264.09 1744.11,-256.47"/>
118 +<polygon fill="black" stroke="black" points="1745.57,-253.29 1735.05,-252.05 1742.5,-259.58 1745.57,-253.29"/>
119 </g>
120 <!-- v71 -->
121 <g id="node11" class="node">
122 <title>v71</title>
123 -<polygon fill="none" stroke="black" points="1212.46,-252 988.98,-252 988.98,-216 1212.46,-216 1212.46,-252"/>
124 -<text text-anchor="middle" x="1100.72" y="-229.8" font-family="Times,serif" font-size="14.00">v71 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
123 +<polygon fill="none" stroke="black" points="1203.1,-252 979.62,-252 979.62,-216 1203.1,-216 1203.1,-252"/>
124 +<text text-anchor="middle" x="1091.36" y="-229.8" font-family="Times,serif" font-size="14.00">v71 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
125 </g>
126 <!-- v4&#45;&gt;v71 -->
127 <g id="edge9" class="edge">
128 <title>v4&#45;&gt;v71</title>
129 -<path fill="none" stroke="black" d="M1317.95,-434.8C1252.32,-425.49 1184.6,-412.47 1156.72,-396 1121.34,-375.09 1112.39,-362.75 1098.72,-324 1091.75,-304.23 1092.72,-280.43 1095.18,-262.42"/>
130 -<polygon fill="black" stroke="black" points="1098.68,-262.72 1096.8,-252.3 1091.77,-261.62 1098.68,-262.72"/>
129 +<path fill="none" stroke="black" d="M1308.59,-434.8C1242.96,-425.49 1175.24,-412.47 1147.36,-396 1111.98,-375.09 1103.03,-362.75 1089.36,-324 1082.39,-304.23 1083.37,-280.43 1085.83,-262.42"/>
130 +<polygon fill="black" stroke="black" points="1089.32,-262.72 1087.44,-252.3 1082.41,-261.62 1089.32,-262.72"/>
131 </g>
132 <!-- v19 -->
133 <g id="node31" class="node">
134 <title>v19</title>
135 -<polygon fill="none" stroke="black" points="1590.51,-108 1020.93,-108 1020.93,-72 1590.51,-72 1590.51,-108"/>
136 -<text text-anchor="middle" x="1305.72" y="-85.8" font-family="Times,serif" font-size="14.00">v19 (readonly count$17 @ Const readonly count$17 = readonly renderedItems$4.length) &lt;assign&gt;</text>
135 +<polygon fill="none" stroke="black" points="1585.15,-108 1015.57,-108 1015.57,-72 1585.15,-72 1585.15,-108"/>
136 +<text text-anchor="middle" x="1300.36" y="-85.8" font-family="Times,serif" font-size="14.00">v19 (readonly count$17 @ Const readonly count$17 = readonly renderedItems$4.length) &lt;assign&gt;</text>
137 </g>
138 <!-- v18&#45;&gt;v19 -->
139 <g id="edge21" class="edge">
140 <title>v18&#45;&gt;v19</title>
141 -<path fill="none" stroke="black" d="M1587.95,-143.97C1533.13,-133.68 1463.16,-120.55 1406.61,-109.93"/>
142 -<polygon fill="black" stroke="black" points="1407.05,-106.45 1396.57,-108.05 1405.75,-113.33 1407.05,-106.45"/>
141 +<path fill="none" stroke="black" d="M1579.57,-143.97C1525.33,-133.68 1456.11,-120.55 1400.17,-109.93"/>
142 +<polygon fill="black" stroke="black" points="1400.72,-106.48 1390.24,-108.05 1399.41,-113.35 1400.72,-106.48"/>
143 </g>
144 <!-- v34 -->
145 <g id="node32" class="node">
146 <title>v34</title>
147 -<polygon fill="none" stroke="black" points="2492.71,-108 1608.73,-108 1608.73,-72 2492.71,-72 2492.71,-108"/>
148 -<text text-anchor="middle" x="2050.72" y="-85.8" font-family="Times,serif" font-size="14.00">v34 (frozen renderedItems$4 @ JSX &lt;frozen $18&gt;{frozen $19}{frozen $22}{frozen $23}{frozen renderedItems$4}{frozen $24}&lt;/frozen $18&gt;) &lt;update&gt;</text>
147 +<polygon fill="none" stroke="black" points="2474.28,-108 1602.45,-108 1602.45,-72 2474.28,-72 2474.28,-108"/>
148 +<text text-anchor="middle" x="2038.36" y="-85.8" font-family="Times,serif" font-size="14.00">v34 (freeze renderedItems$4 @ JSX &lt;freeze $18&gt;{freeze $19}{freeze $22}{freeze $23}{freeze renderedItems$4}{freeze $24}&lt;/freeze $18&gt;) &lt;update&gt;</text>
149 </g>
150 <!-- v18&#45;&gt;v34 -->
151 <g id="edge22" class="edge">
152 <title>v18&#45;&gt;v34</title>
153 -<path fill="none" stroke="black" d="M1769.24,-143.97C1823.92,-133.68 1893.7,-120.55 1950.1,-109.93"/>
154 -<polygon fill="black" stroke="black" points="1950.93,-113.34 1960.11,-108.05 1949.64,-106.46 1950.93,-113.34"/>
153 +<path fill="none" stroke="black" d="M1759.16,-143.97C1813.39,-133.68 1882.61,-120.55 1938.56,-109.93"/>
154 +<polygon fill="black" stroke="black" points="1939.31,-113.35 1948.48,-108.05 1938.01,-106.48 1939.31,-113.35"/>
155 </g>
156 <!-- v51 -->
157 <g id="node59" class="node">
158 <title>v51</title>
159 -<polygon fill="none" stroke="black" points="1780.61,-324 1128.83,-324 1128.83,-288 1780.61,-288 1780.61,-324"/>
160 -<text text-anchor="middle" x="1454.72" y="-301.8" font-family="Times,serif" font-size="14.00">v51 (readonly renderedItems$4.length @ Binary readonly renderedItems$4.length &gt;= readonly max$7) &lt;update&gt;</text>
159 +<polygon fill="none" stroke="black" points="1771.25,-324 1119.47,-324 1119.47,-288 1771.25,-288 1771.25,-324"/>
160 +<text text-anchor="middle" x="1445.36" y="-301.8" font-family="Times,serif" font-size="14.00">v51 (readonly renderedItems$4.length @ Binary readonly renderedItems$4.length &gt;= readonly max$7) &lt;update&gt;</text>
161 </g>
162 <!-- v49&#45;&gt;v51 -->
163 <g id="edge43" class="edge">
164 <title>v49&#45;&gt;v51</title>
165 -<path fill="none" stroke="black" d="M1454.72,-359.7C1454.72,-351.98 1454.72,-342.71 1454.72,-334.11"/>
166 -<polygon fill="black" stroke="black" points="1458.22,-334.1 1454.72,-324.1 1451.22,-334.1 1458.22,-334.1"/>
165 +<path fill="none" stroke="black" d="M1445.36,-359.7C1445.36,-351.98 1445.36,-342.71 1445.36,-334.11"/>
166 +<polygon fill="black" stroke="black" points="1448.86,-334.1 1445.36,-324.1 1441.86,-334.1 1448.86,-334.1"/>
167 </g>
168 <!-- v62&#45;&gt;v18 -->
169 <g id="edge54" class="edge">
170 <title>v62&#45;&gt;v18</title>
171 -<path fill="none" stroke="black" d="M1678.72,-215.7C1678.72,-207.98 1678.72,-198.71 1678.72,-190.11"/>
172 -<polygon fill="black" stroke="black" points="1682.22,-190.1 1678.72,-180.1 1675.22,-190.1 1682.22,-190.1"/>
171 +<path fill="none" stroke="black" d="M1669.36,-215.7C1669.36,-207.98 1669.36,-198.71 1669.36,-190.11"/>
172 +<polygon fill="black" stroke="black" points="1672.86,-190.1 1669.36,-180.1 1665.86,-190.1 1672.86,-190.1"/>
173 </g>
174 <!-- v71&#45;&gt;v49 -->
175 <g id="edge55" class="edge">
176 <title>v71&#45;&gt;v49</title>
177 -<path fill="none" stroke="black" d="M1099.11,-252.13C1098.24,-272.34 1100.23,-305.52 1119.72,-324 1136.11,-339.54 1172.75,-350.52 1216.03,-358.28"/>
178 -<polygon fill="black" stroke="black" points="1215.51,-361.75 1225.96,-359.99 1216.7,-354.85 1215.51,-361.75"/>
177 +<path fill="none" stroke="black" d="M1089.75,-252.13C1088.89,-272.34 1090.87,-305.52 1110.36,-324 1126.75,-339.54 1163.39,-350.52 1206.67,-358.28"/>
178 +<polygon fill="black" stroke="black" points="1206.15,-361.75 1216.6,-359.99 1207.34,-354.85 1206.15,-361.75"/>
179 </g>
180 <!-- v5 -->
181 <g id="node12" class="node">
182 <title>v5</title>
183 -<polygon fill="none" stroke="black" points="2081.28,-470.8 1762.16,-470.8 1762.16,-434.8 2081.28,-434.8 2081.28,-470.8"/>
184 -<text text-anchor="middle" x="1921.72" y="-448.6" font-family="Times,serif" font-size="14.00">v5 (mutable Set$6 @ New mutable Set$6()) &lt;update&gt;</text>
183 +<polygon fill="none" stroke="black" points="2071.92,-470.8 1752.8,-470.8 1752.8,-434.8 2071.92,-434.8 2071.92,-470.8"/>
184 +<text text-anchor="middle" x="1912.36" y="-448.6" font-family="Times,serif" font-size="14.00">v5 (mutable Set$6 @ New mutable Set$6()) &lt;update&gt;</text>
185 </g>
186 <!-- v6 -->
187 <g id="node13" class="node">
188 <title>v6</title>
189 -<polygon fill="none" stroke="black" points="2605.75,-470.8 2135.68,-470.8 2135.68,-434.8 2605.75,-434.8 2605.75,-470.8"/>
190 -<text text-anchor="middle" x="2370.72" y="-448.6" font-family="Times,serif" font-size="14.00">v6 (readonly seen$5 @ Const readonly seen$5 = New mutable Set$6()) &lt;assign&gt;</text>
189 +<polygon fill="none" stroke="black" points="2595.4,-470.8 2125.33,-470.8 2125.33,-434.8 2595.4,-434.8 2595.4,-470.8"/>
190 +<text text-anchor="middle" x="2360.36" y="-448.6" font-family="Times,serif" font-size="14.00">v6 (readonly seen$5 @ Const readonly seen$5 = New mutable Set$6()) &lt;assign&gt;</text>
191 </g>
192 <!-- v39 -->
193 <g id="node14" class="node">
194 <title>v39</title>
195 -<polygon fill="none" stroke="black" points="2604.1,-396 2137.33,-396 2137.33,-360 2604.1,-360 2604.1,-396"/>
196 -<text text-anchor="middle" x="2370.72" y="-373.8" font-family="Times,serif" font-size="14.00">v39 (mutable seen$5.has @ Call mutable seen$5.has(frozen item$10)) &lt;update&gt;</text>
195 +<polygon fill="none" stroke="black" points="2593.17,-396 2127.55,-396 2127.55,-360 2593.17,-360 2593.17,-396"/>
196 +<text text-anchor="middle" x="2360.36" y="-373.8" font-family="Times,serif" font-size="14.00">v39 (mutable seen$5.has @ Call mutable seen$5.has(freeze item$10)) &lt;update&gt;</text>
197 </g>
198 <!-- v6&#45;&gt;v39 -->
199 <g id="edge10" class="edge">
200 <title>v6&#45;&gt;v39</title>
201 -<path fill="none" stroke="black" d="M2370.72,-434.55C2370.72,-426.12 2370.72,-415.77 2370.72,-406.3"/>
202 -<polygon fill="black" stroke="black" points="2374.22,-406.13 2370.72,-396.13 2367.22,-406.13 2374.22,-406.13"/>
201 +<path fill="none" stroke="black" d="M2360.36,-434.55C2360.36,-426.12 2360.36,-415.77 2360.36,-406.3"/>
202 +<polygon fill="black" stroke="black" points="2363.86,-406.13 2360.36,-396.13 2356.86,-406.13 2363.86,-406.13"/>
203 </g>
204 <!-- v56 -->
205 <g id="node15" class="node">
206 <title>v56</title>
207 -<polygon fill="none" stroke="black" points="2221.46,-324 1997.98,-324 1997.98,-288 2221.46,-288 2221.46,-324"/>
208 -<text text-anchor="middle" x="2109.72" y="-301.8" font-family="Times,serif" font-size="14.00">v56 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
207 +<polygon fill="none" stroke="black" points="2211.1,-324 1987.62,-324 1987.62,-288 2211.1,-288 2211.1,-324"/>
208 +<text text-anchor="middle" x="2099.36" y="-301.8" font-family="Times,serif" font-size="14.00">v56 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
209 </g>
210 <!-- v6&#45;&gt;v56 -->
211 <g id="edge11" class="edge">
212 <title>v6&#45;&gt;v56</title>
213 -<path fill="none" stroke="black" d="M2241.91,-434.79C2193.23,-425.81 2145.5,-413.06 2128.72,-396 2112.92,-379.94 2108.83,-354.16 2108.3,-334.47"/>
214 -<polygon fill="black" stroke="black" points="2111.8,-334.26 2108.32,-324.26 2104.8,-334.25 2111.8,-334.26"/>
213 +<path fill="none" stroke="black" d="M2231.55,-434.79C2182.87,-425.81 2135.14,-413.06 2118.36,-396 2102.56,-379.94 2098.47,-354.16 2097.94,-334.47"/>
214 +<polygon fill="black" stroke="black" points="2101.44,-334.26 2097.96,-324.26 2094.44,-334.25 2101.44,-334.26"/>
215 </g>
216 <!-- v59 -->
217 <g id="node16" class="node">
218 <title>v59</title>
219 -<polygon fill="none" stroke="black" points="2493.46,-180 2269.98,-180 2269.98,-144 2493.46,-144 2493.46,-180"/>
220 -<text text-anchor="middle" x="2381.72" y="-157.8" font-family="Times,serif" font-size="14.00">v59 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
219 +<polygon fill="none" stroke="black" points="2482.1,-180 2258.62,-180 2258.62,-144 2482.1,-144 2482.1,-180"/>
220 +<text text-anchor="middle" x="2370.36" y="-157.8" font-family="Times,serif" font-size="14.00">v59 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
221 </g>
222 <!-- v6&#45;&gt;v59 -->
223 <g id="edge12" class="edge">
224 <title>v6&#45;&gt;v59</title>
225 -<path fill="none" stroke="black" d="M2510.26,-434.76C2556,-426.1 2598.73,-413.59 2612.72,-396 2680.39,-310.95 2518.87,-223.25 2432.21,-184.09"/>
226 -<polygon fill="black" stroke="black" points="2433.61,-180.89 2423.05,-180.01 2430.76,-187.28 2433.61,-180.89"/>
225 +<path fill="none" stroke="black" d="M2499.9,-434.76C2545.64,-426.1 2588.37,-413.59 2602.36,-396 2670.09,-310.88 2508.44,-223.38 2421.37,-184.21"/>
226 +<polygon fill="black" stroke="black" points="2422.73,-180.98 2412.17,-180.12 2419.88,-187.38 2422.73,-180.98"/>
227 </g>
228 <!-- v39&#45;&gt;v56 -->
229 <g id="edge33" class="edge">
230 <title>v39&#45;&gt;v56</title>
231 -<path fill="none" stroke="black" d="M2307.21,-359.97C2269.79,-349.93 2222.29,-337.19 2183.25,-326.72"/>
232 -<polygon fill="black" stroke="black" points="2184.08,-323.32 2173.52,-324.11 2182.27,-330.08 2184.08,-323.32"/>
231 +<path fill="none" stroke="black" d="M2296.85,-359.97C2259.43,-349.93 2211.93,-337.19 2172.89,-326.72"/>
232 +<polygon fill="black" stroke="black" points="2173.73,-323.32 2163.16,-324.11 2171.91,-330.08 2173.73,-323.32"/>
233 </g>
234 <!-- v39&#45;&gt;v59 -->
235 <g id="edge34" class="edge">
236 <title>v39&#45;&gt;v59</title>
237 -<path fill="none" stroke="black" d="M2367.72,-359.85C2366.09,-322.83 2370.32,-235.18 2375.62,-190.39"/>
238 -<polygon fill="black" stroke="black" points="2379.12,-190.6 2376.95,-180.23 2372.18,-189.69 2379.12,-190.6"/>
237 +<path fill="none" stroke="black" d="M2357.28,-359.85C2355.48,-322.83 2359.3,-235.18 2364.39,-190.39"/>
238 +<polygon fill="black" stroke="black" points="2367.89,-190.59 2365.67,-180.23 2360.95,-189.72 2367.89,-190.59"/>
239 </g>
240 <!-- v43 -->
241 <g id="node52" class="node">
242 <title>v43</title>
243 -<polygon fill="none" stroke="black" points="2344.71,-252 1874.73,-252 1874.73,-216 2344.71,-216 2344.71,-252"/>
244 -<text text-anchor="middle" x="2109.72" y="-229.8" font-family="Times,serif" font-size="14.00">v43 (mutable seen$5.add @ Call mutable seen$5.add(frozen item$10)) &lt;update&gt;</text>
243 +<polygon fill="none" stroke="black" points="2333.78,-252 1864.94,-252 1864.94,-216 2333.78,-216 2333.78,-252"/>
244 +<text text-anchor="middle" x="2099.36" y="-229.8" font-family="Times,serif" font-size="14.00">v43 (mutable seen$5.add @ Call mutable seen$5.add(freeze item$10)) &lt;update&gt;</text>
245 </g>
246 <!-- v56&#45;&gt;v43 -->
247 <g id="edge50" class="edge">
248 <title>v56&#45;&gt;v43</title>
249 -<path fill="none" stroke="black" d="M2103.8,-287.7C2103.01,-279.98 2102.78,-270.71 2103.12,-262.11"/>
250 -<polygon fill="black" stroke="black" points="2106.62,-262.32 2103.82,-252.1 2099.63,-261.84 2106.62,-262.32"/>
249 +<path fill="none" stroke="black" d="M2093.45,-287.7C2092.65,-279.98 2092.42,-270.71 2092.77,-262.11"/>
250 +<polygon fill="black" stroke="black" points="2096.26,-262.32 2093.47,-252.1 2089.28,-261.84 2096.26,-262.32"/>
251 </g>
252 <!-- v59&#45;&gt;v39 -->
253 <g id="edge53" class="edge">
254 <title>v59&#45;&gt;v39</title>
255 -<path fill="none" stroke="black" d="M2384.72,-180.23C2386.34,-217.32 2382.1,-304.98 2376.81,-349.71"/>
256 -<polygon fill="black" stroke="black" points="2373.31,-349.48 2375.48,-359.85 2380.25,-350.39 2373.31,-349.48"/>
255 +<path fill="none" stroke="black" d="M2373.44,-180.23C2375.24,-217.32 2371.41,-304.98 2366.32,-349.71"/>
256 +<polygon fill="black" stroke="black" points="2362.82,-349.49 2365.04,-359.85 2369.77,-350.37 2362.82,-349.49"/>
257 </g>
258 <!-- v7 -->
259 <g id="node17" class="node">
260 <title>v7</title>
261 -<polygon fill="none" stroke="black" points="3071.76,-470.8 2763.68,-470.8 2763.68,-434.8 3071.76,-434.8 3071.76,-470.8"/>
262 -<text text-anchor="middle" x="2917.72" y="-448.6" font-family="Times,serif" font-size="14.00">v7 (readonly $9 @ Const readonly $9 = 0) &lt;assign&gt;</text>
261 +<polygon fill="none" stroke="black" points="3060.4,-470.8 2752.32,-470.8 2752.32,-434.8 3060.4,-434.8 3060.4,-470.8"/>
262 +<text text-anchor="middle" x="2906.36" y="-448.6" font-family="Times,serif" font-size="14.00">v7 (readonly $9 @ Const readonly $9 = 0) &lt;assign&gt;</text>
263 </g>
264 <!-- v9 -->
265 <g id="node18" class="node">
266 <title>v9</title>
267 -<polygon fill="none" stroke="black" points="3175.6,-396 2659.83,-396 2659.83,-360 3175.6,-360 3175.6,-396"/>
268 -<text text-anchor="middle" x="2917.72" y="-373.8" font-family="Times,serif" font-size="14.00">v9 (mutable $9 @ Call mutable Math$8.max(mutable $9, frozen maxItems$3)) &lt;update&gt;</text>
267 +<polygon fill="none" stroke="black" points="3163.67,-396 2649.05,-396 2649.05,-360 3163.67,-360 3163.67,-396"/>
268 +<text text-anchor="middle" x="2906.36" y="-373.8" font-family="Times,serif" font-size="14.00">v9 (mutable $9 @ Call mutable Math$8.max(mutable $9, freeze maxItems$3)) &lt;update&gt;</text>
269 </g>
270 <!-- v7&#45;&gt;v9 -->
271 <g id="edge13" class="edge">
272 <title>v7&#45;&gt;v9</title>
273 -<path fill="none" stroke="black" d="M2917.72,-434.55C2917.72,-426.12 2917.72,-415.77 2917.72,-406.3"/>
274 -<polygon fill="black" stroke="black" points="2921.22,-406.13 2917.72,-396.13 2914.22,-406.13 2921.22,-406.13"/>
273 +<path fill="none" stroke="black" d="M2906.36,-434.55C2906.36,-426.12 2906.36,-415.77 2906.36,-406.3"/>
274 +<polygon fill="black" stroke="black" points="2909.86,-406.13 2906.36,-396.13 2902.86,-406.13 2909.86,-406.13"/>
275 </g>
276 <!-- v8 -->
277 <g id="node19" class="node">
278 <title>v8</title>
279 -<polygon fill="none" stroke="black" points="3663.26,-470.8 3090.18,-470.8 3090.18,-434.8 3663.26,-434.8 3663.26,-470.8"/>
280 -<text text-anchor="middle" x="3376.72" y="-448.6" font-family="Times,serif" font-size="14.00">v8 (mutable Math$8.max @ Call mutable Math$8.max(mutable $9, frozen maxItems$3)) &lt;update&gt;</text>
279 +<polygon fill="none" stroke="black" points="3650.33,-470.8 3078.39,-470.8 3078.39,-434.8 3650.33,-434.8 3650.33,-470.8"/>
280 +<text text-anchor="middle" x="3364.36" y="-448.6" font-family="Times,serif" font-size="14.00">v8 (mutable Math$8.max @ Call mutable Math$8.max(mutable $9, freeze maxItems$3)) &lt;update&gt;</text>
281 </g>
282 <!-- v11 -->
283 <g id="node20" class="node">
284 <title>v11</title>
285 -<polygon fill="none" stroke="black" points="4371.93,-470.8 3681.51,-470.8 3681.51,-434.8 4371.93,-434.8 4371.93,-470.8"/>
286 -<text text-anchor="middle" x="4026.72" y="-448.6" font-family="Times,serif" font-size="14.00">v11 (readonly max$7 @ Const readonly max$7 = Call mutable Math$8.max(mutable $9, frozen maxItems$3)) &lt;assign&gt;</text>
285 +<polygon fill="none" stroke="black" points="4356.5,-470.8 3668.23,-470.8 3668.23,-434.8 4356.5,-434.8 4356.5,-470.8"/>
286 +<text text-anchor="middle" x="4012.36" y="-448.6" font-family="Times,serif" font-size="14.00">v11 (readonly max$7 @ Const readonly max$7 = Call mutable Math$8.max(mutable $9, freeze maxItems$3)) &lt;assign&gt;</text>
287 </g>
288 <!-- v52 -->
289 <g id="node21" class="node">
290 <title>v52</title>
291 -<polygon fill="none" stroke="black" points="4147.15,-396 3590.29,-396 3590.29,-360 4147.15,-360 4147.15,-396"/>
292 -<text text-anchor="middle" x="3868.72" y="-373.8" font-family="Times,serif" font-size="14.00">v52 (readonly max$7 @ Binary readonly renderedItems$4.length &gt;= readonly max$7) &lt;update&gt;</text>
291 +<polygon fill="none" stroke="black" points="4131.79,-396 3574.93,-396 3574.93,-360 4131.79,-360 4131.79,-396"/>
292 +<text text-anchor="middle" x="3853.36" y="-373.8" font-family="Times,serif" font-size="14.00">v52 (readonly max$7 @ Binary readonly renderedItems$4.length &gt;= readonly max$7) &lt;update&gt;</text>
293 </g>
294 <!-- v11&#45;&gt;v52 -->
295 <g id="edge14" class="edge">
296 <title>v11&#45;&gt;v52</title>
297 -<path fill="none" stroke="black" d="M3989.68,-434.73C3967.37,-424.45 3938.78,-411.28 3915,-400.32"/>
298 -<polygon fill="black" stroke="black" points="3916.35,-397.09 3905.8,-396.09 3913.42,-403.45 3916.35,-397.09"/>
297 +<path fill="none" stroke="black" d="M3975.08,-434.73C3952.64,-424.45 3923.87,-411.28 3899.93,-400.32"/>
298 +<polygon fill="black" stroke="black" points="3901.23,-397.07 3890.68,-396.09 3898.32,-403.43 3901.23,-397.07"/>
299 </g>
300 <!-- v72 -->
301 <g id="node22" class="node">
302 <title>v72</title>
303 -<polygon fill="none" stroke="black" points="3980.46,-324 3756.98,-324 3756.98,-288 3980.46,-288 3980.46,-324"/>
304 -<text text-anchor="middle" x="3868.72" y="-301.8" font-family="Times,serif" font-size="14.00">v72 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
303 +<polygon fill="none" stroke="black" points="3965.1,-324 3741.62,-324 3741.62,-288 3965.1,-288 3965.1,-324"/>
304 +<text text-anchor="middle" x="3853.36" y="-301.8" font-family="Times,serif" font-size="14.00">v72 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
305 </g>
306 <!-- v11&#45;&gt;v72 -->
307 <g id="edge15" class="edge">
308 <title>v11&#45;&gt;v72</title>
309 -<path fill="none" stroke="black" d="M4104.56,-434.72C4124.57,-426.42 4143.79,-414.15 4155.72,-396 4164.5,-382.63 4165.95,-372.3 4155.72,-360 4134.33,-334.29 4058.68,-321.01 3990.43,-314.16"/>
310 -<polygon fill="black" stroke="black" points="3990.71,-310.68 3980.42,-313.2 3990.03,-317.64 3990.71,-310.68"/>
309 +<path fill="none" stroke="black" d="M4089.28,-434.72C4109.25,-426.43 4128.47,-414.15 4140.36,-396 4149.13,-382.62 4150.6,-372.3 4140.36,-360 4118.97,-334.29 4043.32,-321.01 3975.07,-314.16"/>
310 +<polygon fill="black" stroke="black" points="3975.35,-310.68 3965.06,-313.2 3974.68,-317.64 3975.35,-310.68"/>
311 </g>
312 <!-- v52&#45;&gt;v72 -->
313 <g id="edge47" class="edge">
314 <title>v52&#45;&gt;v72</title>
315 -<path fill="none" stroke="black" d="M3862.8,-359.7C3862.01,-351.98 3861.78,-342.71 3862.12,-334.11"/>
316 -<polygon fill="black" stroke="black" points="3865.62,-334.32 3862.82,-324.1 3858.63,-333.84 3865.62,-334.32"/>
315 +<path fill="none" stroke="black" d="M3847.45,-359.7C3846.65,-351.98 3846.42,-342.71 3846.77,-334.11"/>
316 +<polygon fill="black" stroke="black" points="3850.26,-334.32 3847.47,-324.1 3843.28,-333.84 3850.26,-334.32"/>
317 </g>
318 <!-- v72&#45;&gt;v52 -->
319 <g id="edge56" class="edge">
320 <title>v72&#45;&gt;v52</title>
321 -<path fill="none" stroke="black" d="M3874.61,-324.1C3875.42,-331.79 3875.66,-341.05 3875.32,-349.67"/>
322 -<polygon fill="black" stroke="black" points="3871.83,-349.48 3874.63,-359.7 3878.81,-349.96 3871.83,-349.48"/>
321 +<path fill="none" stroke="black" d="M3859.26,-324.1C3860.06,-331.79 3860.3,-341.05 3859.96,-349.67"/>
322 +<polygon fill="black" stroke="black" points="3856.47,-349.48 3859.28,-359.7 3863.45,-349.96 3856.47,-349.48"/>
323 </g>
324 <!-- v13 -->
325 <g id="node23" class="node">
326 <title>v13</title>
327 -<polygon fill="none" stroke="black" points="4733.54,-470.8 4389.9,-470.8 4389.9,-434.8 4733.54,-434.8 4733.54,-470.8"/>
328 -<text text-anchor="middle" x="4561.72" y="-448.6" font-family="Times,serif" font-size="14.00">v13 (readonly $11 @ Const readonly $11 = null) &lt;assign&gt;</text>
327 +<polygon fill="none" stroke="black" points="4718.18,-470.8 4374.54,-470.8 4374.54,-434.8 4718.18,-434.8 4718.18,-470.8"/>
328 +<text text-anchor="middle" x="4546.36" y="-448.6" font-family="Times,serif" font-size="14.00">v13 (readonly $11 @ Const readonly $11 = null) &lt;assign&gt;</text>
329 </g>
330 <!-- v15 -->
331 <g id="node24" class="node">
332 <title>v15</title>
333 -<polygon fill="none" stroke="black" points="4773.03,-396 4350.41,-396 4350.41,-360 4773.03,-360 4773.03,-396"/>
334 -<text text-anchor="middle" x="4561.72" y="-373.8" font-family="Times,serif" font-size="14.00">v15 (readonly $11 @ Binary frozen item$10 == readonly $11) &lt;update&gt;</text>
333 +<polygon fill="none" stroke="black" points="4757.1,-396 4335.62,-396 4335.62,-360 4757.1,-360 4757.1,-396"/>
334 +<text text-anchor="middle" x="4546.36" y="-373.8" font-family="Times,serif" font-size="14.00">v15 (readonly $11 @ Binary freeze item$10 == readonly $11) &lt;update&gt;</text>
335 </g>
336 <!-- v13&#45;&gt;v15 -->
337 <g id="edge16" class="edge">
338 <title>v13&#45;&gt;v15</title>
339 -<path fill="none" stroke="black" d="M4561.72,-434.55C4561.72,-426.12 4561.72,-415.77 4561.72,-406.3"/>
340 -<polygon fill="black" stroke="black" points="4565.22,-406.13 4561.72,-396.13 4558.22,-406.13 4565.22,-406.13"/>
339 +<path fill="none" stroke="black" d="M4546.36,-434.55C4546.36,-426.12 4546.36,-415.77 4546.36,-406.3"/>
340 +<polygon fill="black" stroke="black" points="4549.86,-406.13 4546.36,-396.13 4542.86,-406.13 4549.86,-406.13"/>
341 </g>
342 <!-- v14 -->
343 <g id="node25" class="node">
344 <title>v14</title>
345 -<polygon fill="none" stroke="black" points="5488.19,-470.8 5053.25,-470.8 5053.25,-434.8 5488.19,-434.8 5488.19,-470.8"/>
346 -<text text-anchor="middle" x="5270.72" y="-448.6" font-family="Times,serif" font-size="14.00">v14 (frozen item$10 @ Binary frozen item$10 == readonly $11) &lt;update&gt;</text>
345 +<polygon fill="none" stroke="black" points="5467.19,-470.8 5035.54,-470.8 5035.54,-434.8 5467.19,-434.8 5467.19,-470.8"/>
346 +<text text-anchor="middle" x="5251.36" y="-448.6" font-family="Times,serif" font-size="14.00">v14 (freeze item$10 @ Binary freeze item$10 == readonly $11) &lt;update&gt;</text>
347 </g>
348 <!-- v40 -->
349 <g id="node26" class="node">
350 <title>v40</title>
351 -<polygon fill="none" stroke="black" points="5234.1,-396 4791.33,-396 4791.33,-360 5234.1,-360 5234.1,-396"/>
352 -<text text-anchor="middle" x="5012.72" y="-373.8" font-family="Times,serif" font-size="14.00">v40 (frozen item$10 @ Call mutable seen$5.has(frozen item$10)) &lt;update&gt;</text>
351 +<polygon fill="none" stroke="black" points="5214.1,-396 4774.62,-396 4774.62,-360 5214.1,-360 5214.1,-396"/>
352 +<text text-anchor="middle" x="4994.36" y="-373.8" font-family="Times,serif" font-size="14.00">v40 (freeze item$10 @ Call mutable seen$5.has(freeze item$10)) &lt;update&gt;</text>
353 </g>
354 <!-- v14&#45;&gt;v40 -->
355 <g id="edge17" class="edge">
356 <title>v14&#45;&gt;v40</title>
357 -<path fill="none" stroke="black" d="M5210.23,-434.73C5172.12,-423.98 5122.76,-410.05 5082.92,-398.81"/>
358 -<polygon fill="black" stroke="black" points="5083.85,-395.43 5073.28,-396.09 5081.95,-402.17 5083.85,-395.43"/>
357 +<path fill="none" stroke="black" d="M5191.11,-434.73C5153.22,-424 5104.18,-410.11 5064.55,-398.88"/>
358 +<polygon fill="black" stroke="black" points="5065.26,-395.45 5054.68,-396.09 5063.35,-402.18 5065.26,-395.45"/>
359 </g>
360 <!-- v57 -->
361 <g id="node27" class="node">
362 <title>v57</title>
363 -<polygon fill="none" stroke="black" points="5382.46,-324 5158.98,-324 5158.98,-288 5382.46,-288 5382.46,-324"/>
364 -<text text-anchor="middle" x="5270.72" y="-301.8" font-family="Times,serif" font-size="14.00">v57 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
363 +<polygon fill="none" stroke="black" points="5363.1,-324 5139.62,-324 5139.62,-288 5363.1,-288 5363.1,-324"/>
364 +<text text-anchor="middle" x="5251.36" y="-301.8" font-family="Times,serif" font-size="14.00">v57 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
365 </g>
366 <!-- v14&#45;&gt;v57 -->
367 <g id="edge18" class="edge">
368 <title>v14&#45;&gt;v57</title>
369 -<path fill="none" stroke="black" d="M5266.16,-434.62C5263.56,-409.8 5263.27,-363.66 5265.31,-334.19"/>
370 -<polygon fill="black" stroke="black" points="5268.8,-334.42 5266.17,-324.16 5261.83,-333.82 5268.8,-334.42"/>
369 +<path fill="none" stroke="black" d="M5246.81,-434.62C5244.2,-409.8 5243.91,-363.66 5245.95,-334.19"/>
370 +<polygon fill="black" stroke="black" points="5249.45,-334.42 5246.81,-324.16 5242.47,-333.82 5249.45,-334.42"/>
371 </g>
372 <!-- v40&#45;&gt;v57 -->
373 <g id="edge35" class="edge">
374 <title>v40&#45;&gt;v57</title>
375 -<path fill="none" stroke="black" d="M5075.5,-359.97C5112.34,-349.97 5159.05,-337.3 5197.54,-326.85"/>
376 -<polygon fill="black" stroke="black" points="5198.92,-330.11 5207.65,-324.11 5197.09,-323.35 5198.92,-330.11"/>
375 +<path fill="none" stroke="black" d="M5056.9,-359.97C5093.59,-349.97 5140.12,-337.3 5178.47,-326.85"/>
376 +<polygon fill="black" stroke="black" points="5179.81,-330.12 5188.54,-324.11 5177.97,-323.36 5179.81,-330.12"/>
377 </g>
378 <!-- v57&#45;&gt;v14 -->
379 <g id="edge52" class="edge">
380 <title>v57&#45;&gt;v14</title>
381 -<path fill="none" stroke="black" d="M5275.27,-324.16C5277.88,-348.96 5278.17,-395.1 5276.13,-424.58"/>
382 -<polygon fill="black" stroke="black" points="5272.64,-424.36 5275.27,-434.62 5279.61,-424.95 5272.64,-424.36"/>
381 +<path fill="none" stroke="black" d="M5255.91,-324.16C5258.52,-348.96 5258.81,-395.1 5256.77,-424.58"/>
382 +<polygon fill="black" stroke="black" points="5253.28,-424.36 5255.92,-434.62 5260.25,-424.95 5253.28,-424.36"/>
383 </g>
384 <!-- v44 -->
385 <g id="node53" class="node">
386 <title>v44</title>
387 -<polygon fill="none" stroke="black" points="5367.66,-252 4923.78,-252 4923.78,-216 5367.66,-216 5367.66,-252"/>
388 -<text text-anchor="middle" x="5145.72" y="-229.8" font-family="Times,serif" font-size="14.00">v44 (frozen item$10 @ Call mutable seen$5.add(frozen item$10)) &lt;update&gt;</text>
387 +<polygon fill="none" stroke="black" points="5347.65,-252 4907.07,-252 4907.07,-216 5347.65,-216 5347.65,-252"/>
388 +<text text-anchor="middle" x="5127.36" y="-229.8" font-family="Times,serif" font-size="14.00">v44 (freeze item$10 @ Call mutable seen$5.add(freeze item$10)) &lt;update&gt;</text>
389 </g>
390 <!-- v57&#45;&gt;v44 -->
391 <g id="edge51" class="edge">
392 <title>v57&#45;&gt;v44</title>
393 -<path fill="none" stroke="black" d="M5240.14,-287.88C5223.65,-278.64 5203.1,-267.13 5185.38,-257.21"/>
394 -<polygon fill="black" stroke="black" points="5186.85,-254.02 5176.42,-252.19 5183.43,-260.13 5186.85,-254.02"/>
393 +<path fill="none" stroke="black" d="M5221.03,-287.88C5204.66,-278.64 5184.28,-267.13 5166.71,-257.21"/>
394 +<polygon fill="black" stroke="black" points="5168.24,-254.06 5157.81,-252.19 5164.8,-260.15 5168.24,-254.06"/>
395 </g>
396 <!-- v16 -->
397 <g id="node28" class="node">
398 <title>v16</title>
399 -<polygon fill="none" stroke="black" points="6024.81,-470.8 5506.63,-470.8 5506.63,-434.8 6024.81,-434.8 6024.81,-470.8"/>
400 -<text text-anchor="middle" x="5765.72" y="-448.6" font-family="Times,serif" font-size="14.00">v16 (frozen $12 @ Const frozen $12 = Binary frozen item$10 == readonly $11) &lt;assign&gt;</text>
399 +<polygon fill="none" stroke="black" points="5999.24,-470.8 5485.49,-470.8 5485.49,-434.8 5999.24,-434.8 5999.24,-470.8"/>
400 +<text text-anchor="middle" x="5742.36" y="-448.6" font-family="Times,serif" font-size="14.00">v16 (freeze $12 @ Const freeze $12 = Binary freeze item$10 == readonly $11) &lt;assign&gt;</text>
401 </g>
402 <!-- v17 -->
403 <g id="node29" class="node">
404 <title>v17</title>
405 -<polygon fill="none" stroke="black" points="5951.35,-396 5580.09,-396 5580.09,-360 5951.35,-360 5951.35,-396"/>
406 -<text text-anchor="middle" x="5765.72" y="-373.8" font-family="Times,serif" font-size="14.00">v17 (frozen $12 @ If (frozen $12) then:bb8 else:bb9) &lt;update&gt;</text>
405 +<polygon fill="none" stroke="black" points="5926.84,-396 5557.88,-396 5557.88,-360 5926.84,-360 5926.84,-396"/>
406 +<text text-anchor="middle" x="5742.36" y="-373.8" font-family="Times,serif" font-size="14.00">v17 (freeze $12 @ If (freeze $12) then:bb8 else:bb9) &lt;update&gt;</text>
407 </g>
408 <!-- v16&#45;&gt;v17 -->
409 <g id="edge19" class="edge">
410 <title>v16&#45;&gt;v17</title>
411 -<path fill="none" stroke="black" d="M5765.72,-434.55C5765.72,-426.12 5765.72,-415.77 5765.72,-406.3"/>
412 -<polygon fill="black" stroke="black" points="5769.22,-406.13 5765.72,-396.13 5762.22,-406.13 5769.22,-406.13"/>
411 +<path fill="none" stroke="black" d="M5742.36,-434.55C5742.36,-426.12 5742.36,-415.77 5742.36,-406.3"/>
412 +<polygon fill="black" stroke="black" points="5745.86,-406.13 5742.36,-396.13 5738.86,-406.13 5745.86,-406.13"/>
413 </g>
414 <!-- v38 -->
415 <g id="node30" class="node">
416 <title>v38</title>
417 -<polygon fill="none" stroke="black" points="5943.55,-324 5587.88,-324 5587.88,-288 5943.55,-288 5943.55,-324"/>
418 -<text text-anchor="middle" x="5765.72" y="-301.8" font-family="Times,serif" font-size="14.00">v38 (frozen $13 @ Const frozen $13 = frozen $12) &lt;assign&gt;</text>
417 +<polygon fill="none" stroke="black" points="5917.98,-324 5566.74,-324 5566.74,-288 5917.98,-288 5917.98,-324"/>
418 +<text text-anchor="middle" x="5742.36" y="-301.8" font-family="Times,serif" font-size="14.00">v38 (freeze $13 @ Const freeze $13 = freeze $12) &lt;assign&gt;</text>
419 </g>
420 <!-- v17&#45;&gt;v38 -->
421 <g id="edge20" class="edge">
422 <title>v17&#45;&gt;v38</title>
423 -<path fill="none" stroke="black" d="M5765.72,-359.7C5765.72,-351.98 5765.72,-342.71 5765.72,-334.11"/>
424 -<polygon fill="black" stroke="black" points="5769.22,-334.1 5765.72,-324.1 5762.22,-334.1 5769.22,-334.1"/>
423 +<path fill="none" stroke="black" d="M5742.36,-359.7C5742.36,-351.98 5742.36,-342.71 5742.36,-334.11"/>
424 +<polygon fill="black" stroke="black" points="5745.86,-334.1 5742.36,-324.1 5738.86,-334.1 5745.86,-334.1"/>
425 </g>
426 <!-- v42 -->
427 <g id="node50" class="node">
428 <title>v42</title>
429 -<polygon fill="none" stroke="black" points="6187.35,-252 5816.09,-252 5816.09,-216 6187.35,-216 6187.35,-252"/>
430 -<text text-anchor="middle" x="6001.72" y="-229.8" font-family="Times,serif" font-size="14.00">v42 (frozen $13 @ If (frozen $13) then:bb1 else:bb4) &lt;update&gt;</text>
429 +<polygon fill="none" stroke="black" points="6160.84,-252 5791.88,-252 5791.88,-216 6160.84,-216 6160.84,-252"/>
430 +<text text-anchor="middle" x="5976.36" y="-229.8" font-family="Times,serif" font-size="14.00">v42 (freeze $13 @ If (freeze $13) then:bb1 else:bb4) &lt;update&gt;</text>
431 </g>
432 <!-- v38&#45;&gt;v42 -->
433 <g id="edge32" class="edge">
434 <title>v38&#45;&gt;v42</title>
435 -<path fill="none" stroke="black" d="M5823.15,-287.97C5856.7,-278.01 5899.21,-265.41 5934.33,-254.99"/>
436 -<polygon fill="black" stroke="black" points="5935.44,-258.31 5944.03,-252.11 5933.45,-251.6 5935.44,-258.31"/>
435 +<path fill="none" stroke="black" d="M5799.3,-287.97C5832.57,-278.01 5874.72,-265.41 5909.55,-254.99"/>
436 +<polygon fill="black" stroke="black" points="5910.58,-258.33 5919.16,-252.11 5908.58,-251.62 5910.58,-258.33"/>
437 </g>
438 <!-- v25 -->
439 <g id="node33" class="node">
440 <title>v25</title>
441 -<polygon fill="none" stroke="black" points="1595.21,-36 1016.23,-36 1016.23,0 1595.21,0 1595.21,-36"/>
442 -<text text-anchor="middle" x="1305.72" y="-13.8" font-family="Times,serif" font-size="14.00">v25 (frozen count$17 @ JSX &lt;frozen $20&gt;{frozen count$17}{frozen $21}&lt;/frozen $20&gt;) &lt;update&gt;</text>
441 +<polygon fill="none" stroke="black" points="1585.99,-36 1014.73,-36 1014.73,0 1585.99,0 1585.99,-36"/>
442 +<text text-anchor="middle" x="1300.36" y="-13.8" font-family="Times,serif" font-size="14.00">v25 (freeze count$17 @ JSX &lt;freeze $20&gt;{freeze count$17}{freeze $21}&lt;/freeze $20&gt;) &lt;update&gt;</text>
443 </g>
444 <!-- v19&#45;&gt;v25 -->
445 <g id="edge23" class="edge">
446 <title>v19&#45;&gt;v25</title>
447 -<path fill="none" stroke="black" d="M1305.72,-71.7C1305.72,-63.98 1305.72,-54.71 1305.72,-46.11"/>
448 -<polygon fill="black" stroke="black" points="1309.22,-46.1 1305.72,-36.1 1302.22,-46.1 1309.22,-46.1"/>
447 +<path fill="none" stroke="black" d="M1300.36,-71.7C1300.36,-63.98 1300.36,-54.71 1300.36,-46.11"/>
448 +<polygon fill="black" stroke="black" points="1303.86,-46.1 1300.36,-36.1 1296.86,-46.1 1303.86,-46.1"/>
449 </g>
450 <!-- v20 -->
451 <g id="node34" class="node">
452 <title>v20</title>
453 -<polygon fill="none" stroke="black" points="6550.58,-470.8 6198.86,-470.8 6198.86,-434.8 6550.58,-434.8 6550.58,-470.8"/>
454 -<text text-anchor="middle" x="6374.72" y="-448.6" font-family="Times,serif" font-size="14.00">v20 (readonly $18 @ Const readonly $18 = &quot;div&quot;) &lt;assign&gt;</text>
453 +<polygon fill="none" stroke="black" points="6519.22,-470.8 6167.5,-470.8 6167.5,-434.8 6519.22,-434.8 6519.22,-470.8"/>
454 +<text text-anchor="middle" x="6343.36" y="-448.6" font-family="Times,serif" font-size="14.00">v20 (readonly $18 @ Const readonly $18 = &quot;div&quot;) &lt;assign&gt;</text>
455 </g>
456 <!-- v30 -->
457 <g id="node35" class="node">
458 <title>v30</title>
459 -<polygon fill="none" stroke="black" points="6780.14,-396 5969.3,-396 5969.3,-360 6780.14,-360 6780.14,-396"/>
460 -<text text-anchor="middle" x="6374.72" y="-373.8" font-family="Times,serif" font-size="14.00">v30 (frozen $18 @ JSX &lt;frozen $18&gt;{frozen $19}{frozen $22}{frozen $23}{frozen renderedItems$4}{frozen $24}&lt;/frozen $18&gt;) &lt;update&gt;</text>
459 +<polygon fill="none" stroke="black" points="6742.71,-396 5944.02,-396 5944.02,-360 6742.71,-360 6742.71,-396"/>
460 +<text text-anchor="middle" x="6343.36" y="-373.8" font-family="Times,serif" font-size="14.00">v30 (freeze $18 @ JSX &lt;freeze $18&gt;{freeze $19}{freeze $22}{freeze $23}{freeze renderedItems$4}{freeze $24}&lt;/freeze $18&gt;) &lt;update&gt;</text>
461 </g>
462 <!-- v20&#45;&gt;v30 -->
463 <g id="edge24" class="edge">
464 <title>v20&#45;&gt;v30</title>
465 -<path fill="none" stroke="black" d="M6374.72,-434.55C6374.72,-426.12 6374.72,-415.77 6374.72,-406.3"/>
466 -<polygon fill="black" stroke="black" points="6378.22,-406.13 6374.72,-396.13 6371.22,-406.13 6378.22,-406.13"/>
465 +<path fill="none" stroke="black" d="M6343.36,-434.55C6343.36,-426.12 6343.36,-415.77 6343.36,-406.3"/>
466 +<polygon fill="black" stroke="black" points="6346.86,-406.13 6343.36,-396.13 6339.86,-406.13 6346.86,-406.13"/>
467 </g>
468 <!-- v21 -->
469 <g id="node36" class="node">
470 <title>v21</title>
471 -<polygon fill="none" stroke="black" points="7338.02,-473.4 7069.42,-473.4 7069.42,-432.2 7338.02,-432.2 7338.02,-473.4"/>
472 -<text text-anchor="middle" x="7203.72" y="-457" font-family="Times,serif" font-size="14.00">v21 (readonly $19 @ Const readonly $19 = &quot;</text>
473 -<text text-anchor="middle" x="7203.72" y="-440.2" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;&#160;&#160;&quot;) &lt;assign&gt;</text>
471 +<polygon fill="none" stroke="black" points="7293.66,-473.4 7025.06,-473.4 7025.06,-432.2 7293.66,-432.2 7293.66,-473.4"/>
472 +<text text-anchor="middle" x="7159.36" y="-457" font-family="Times,serif" font-size="14.00">v21 (readonly $19 @ Const readonly $19 = &quot;</text>
473 +<text text-anchor="middle" x="7159.36" y="-440.2" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;&#160;&#160;&quot;) &lt;assign&gt;</text>
474 </g>
475 <!-- v31 -->
476 <g id="node37" class="node">
477 <title>v31</title>
478 -<polygon fill="none" stroke="black" points="7609.14,-396 6798.3,-396 6798.3,-360 7609.14,-360 7609.14,-396"/>
479 -<text text-anchor="middle" x="7203.72" y="-373.8" font-family="Times,serif" font-size="14.00">v31 (frozen $19 @ JSX &lt;frozen $18&gt;{frozen $19}{frozen $22}{frozen $23}{frozen renderedItems$4}{frozen $24}&lt;/frozen $18&gt;) &lt;update&gt;</text>
478 +<polygon fill="none" stroke="black" points="7558.71,-396 6760.02,-396 6760.02,-360 7558.71,-360 7558.71,-396"/>
479 +<text text-anchor="middle" x="7159.36" y="-373.8" font-family="Times,serif" font-size="14.00">v31 (freeze $19 @ JSX &lt;freeze $18&gt;{freeze $19}{freeze $22}{freeze $23}{freeze renderedItems$4}{freeze $24}&lt;/freeze $18&gt;) &lt;update&gt;</text>
480 </g>
481 <!-- v21&#45;&gt;v31 -->
482 <g id="edge25" class="edge">
483 <title>v21&#45;&gt;v31</title>
484 -<path fill="none" stroke="black" d="M7203.72,-431.91C7203.72,-424.09 7203.72,-414.99 7203.72,-406.56"/>
485 -<polygon fill="black" stroke="black" points="7207.22,-406.39 7203.72,-396.39 7200.22,-406.39 7207.22,-406.39"/>
484 +<path fill="none" stroke="black" d="M7159.36,-431.91C7159.36,-424.09 7159.36,-414.99 7159.36,-406.56"/>
485 +<polygon fill="black" stroke="black" points="7162.86,-406.39 7159.36,-396.39 7155.86,-406.39 7162.86,-406.39"/>
486 </g>
487 <!-- v22 -->
488 <g id="node38" class="node">
489 <title>v22</title>
490 -<polygon fill="none" stroke="black" points="8074.69,-470.8 7726.75,-470.8 7726.75,-434.8 8074.69,-434.8 8074.69,-470.8"/>
491 -<text text-anchor="middle" x="7900.72" y="-448.6" font-family="Times,serif" font-size="14.00">v22 (readonly $20 @ Const readonly $20 = &quot;h1&quot;) &lt;assign&gt;</text>
490 +<polygon fill="none" stroke="black" points="8020.33,-470.8 7672.39,-470.8 7672.39,-434.8 8020.33,-434.8 8020.33,-470.8"/>
491 +<text text-anchor="middle" x="7846.36" y="-448.6" font-family="Times,serif" font-size="14.00">v22 (readonly $20 @ Const readonly $20 = &quot;h1&quot;) &lt;assign&gt;</text>
492 </g>
493 <!-- v24 -->
494 <g id="node39" class="node">
495 <title>v24</title>
496 -<polygon fill="none" stroke="black" points="8174.6,-396 7626.84,-396 7626.84,-360 8174.6,-360 8174.6,-396"/>
497 -<text text-anchor="middle" x="7900.72" y="-373.8" font-family="Times,serif" font-size="14.00">v24 (frozen $20 @ JSX &lt;frozen $20&gt;{frozen count$17}{frozen $21}&lt;/frozen $20&gt;) &lt;update&gt;</text>
496 +<polygon fill="none" stroke="black" points="8116.38,-396 7576.34,-396 7576.34,-360 8116.38,-360 8116.38,-396"/>
497 +<text text-anchor="middle" x="7846.36" y="-373.8" font-family="Times,serif" font-size="14.00">v24 (freeze $20 @ JSX &lt;freeze $20&gt;{freeze count$17}{freeze $21}&lt;/freeze $20&gt;) &lt;update&gt;</text>
498 </g>
499 <!-- v22&#45;&gt;v24 -->
500 <g id="edge26" class="edge">
501 <title>v22&#45;&gt;v24</title>
502 -<path fill="none" stroke="black" d="M7900.72,-434.55C7900.72,-426.12 7900.72,-415.77 7900.72,-406.3"/>
503 -<polygon fill="black" stroke="black" points="7904.22,-406.13 7900.72,-396.13 7897.22,-406.13 7904.22,-406.13"/>
502 +<path fill="none" stroke="black" d="M7846.36,-434.55C7846.36,-426.12 7846.36,-415.77 7846.36,-406.3"/>
503 +<polygon fill="black" stroke="black" points="7849.86,-406.13 7846.36,-396.13 7842.86,-406.13 7849.86,-406.13"/>
504 </g>
505 <!-- v23 -->
506 <g id="node40" class="node">
507 <title>v23</title>
508 -<polygon fill="none" stroke="black" points="8650.79,-470.8 8282.65,-470.8 8282.65,-434.8 8650.79,-434.8 8650.79,-470.8"/>
509 -<text text-anchor="middle" x="8466.72" y="-448.6" font-family="Times,serif" font-size="14.00">v23 (readonly $21 @ Const readonly $21 = &quot; Items&quot;) &lt;assign&gt;</text>
508 +<polygon fill="none" stroke="black" points="8588.43,-470.8 8220.29,-470.8 8220.29,-434.8 8588.43,-434.8 8588.43,-470.8"/>
509 +<text text-anchor="middle" x="8404.36" y="-448.6" font-family="Times,serif" font-size="14.00">v23 (readonly $21 @ Const readonly $21 = &quot; Items&quot;) &lt;assign&gt;</text>
510 </g>
511 <!-- v26 -->
512 <g id="node41" class="node">
513 <title>v26</title>
514 -<polygon fill="none" stroke="black" points="8740.6,-396 8192.84,-396 8192.84,-360 8740.6,-360 8740.6,-396"/>
515 -<text text-anchor="middle" x="8466.72" y="-373.8" font-family="Times,serif" font-size="14.00">v26 (frozen $21 @ JSX &lt;frozen $20&gt;{frozen count$17}{frozen $21}&lt;/frozen $20&gt;) &lt;update&gt;</text>
514 +<polygon fill="none" stroke="black" points="8674.38,-396 8134.34,-396 8134.34,-360 8674.38,-360 8674.38,-396"/>
515 +<text text-anchor="middle" x="8404.36" y="-373.8" font-family="Times,serif" font-size="14.00">v26 (freeze $21 @ JSX &lt;freeze $20&gt;{freeze count$17}{freeze $21}&lt;/freeze $20&gt;) &lt;update&gt;</text>
516 </g>
517 <!-- v23&#45;&gt;v26 -->
518 <g id="edge27" class="edge">
519 <title>v23&#45;&gt;v26</title>
520 -<path fill="none" stroke="black" d="M8466.72,-434.55C8466.72,-426.12 8466.72,-415.77 8466.72,-406.3"/>
521 -<polygon fill="black" stroke="black" points="8470.22,-406.13 8466.72,-396.13 8463.22,-406.13 8470.22,-406.13"/>
520 +<path fill="none" stroke="black" d="M8404.36,-434.55C8404.36,-426.12 8404.36,-415.77 8404.36,-406.3"/>
521 +<polygon fill="black" stroke="black" points="8407.86,-406.13 8404.36,-396.13 8400.86,-406.13 8407.86,-406.13"/>
522 </g>
523 <!-- v27 -->
524 <g id="node42" class="node">
525 <title>v27</title>
526 -<polygon fill="none" stroke="black" points="9505.56,-470.8 8821.87,-470.8 8821.87,-434.8 9505.56,-434.8 9505.56,-470.8"/>
527 -<text text-anchor="middle" x="9163.72" y="-448.6" font-family="Times,serif" font-size="14.00">v27 (readonly $22 @ Const readonly $22 = JSX &lt;frozen $20&gt;{frozen count$17}{frozen $21}&lt;/frozen $20&gt;) &lt;assign&gt;</text>
526 +<polygon fill="none" stroke="black" points="9429.92,-470.8 8752.81,-470.8 8752.81,-434.8 9429.92,-434.8 9429.92,-470.8"/>
527 +<text text-anchor="middle" x="9091.36" y="-448.6" font-family="Times,serif" font-size="14.00">v27 (readonly $22 @ Const readonly $22 = JSX &lt;freeze $20&gt;{freeze count$17}{freeze $21}&lt;/freeze $20&gt;) &lt;assign&gt;</text>
528 </g>
529 <!-- v32 -->
530 <g id="node43" class="node">
531 <title>v32</title>
532 -<polygon fill="none" stroke="black" points="9569.14,-396 8758.3,-396 8758.3,-360 9569.14,-360 9569.14,-396"/>
533 -<text text-anchor="middle" x="9163.72" y="-373.8" font-family="Times,serif" font-size="14.00">v32 (frozen $22 @ JSX &lt;frozen $18&gt;{frozen $19}{frozen $22}{frozen $23}{frozen renderedItems$4}{frozen $24}&lt;/frozen $18&gt;) &lt;update&gt;</text>
532 +<polygon fill="none" stroke="black" points="9490.71,-396 8692.02,-396 8692.02,-360 9490.71,-360 9490.71,-396"/>
533 +<text text-anchor="middle" x="9091.36" y="-373.8" font-family="Times,serif" font-size="14.00">v32 (freeze $22 @ JSX &lt;freeze $18&gt;{freeze $19}{freeze $22}{freeze $23}{freeze renderedItems$4}{freeze $24}&lt;/freeze $18&gt;) &lt;update&gt;</text>
534 </g>
535 <!-- v27&#45;&gt;v32 -->
536 <g id="edge28" class="edge">
537 <title>v27&#45;&gt;v32</title>
538 -<path fill="none" stroke="black" d="M9163.72,-434.55C9163.72,-426.12 9163.72,-415.77 9163.72,-406.3"/>
539 -<polygon fill="black" stroke="black" points="9167.22,-406.13 9163.72,-396.13 9160.22,-406.13 9167.22,-406.13"/>
538 +<path fill="none" stroke="black" d="M9091.36,-434.55C9091.36,-426.12 9091.36,-415.77 9091.36,-406.3"/>
539 +<polygon fill="black" stroke="black" points="9094.86,-406.13 9091.36,-396.13 9087.86,-406.13 9094.86,-406.13"/>
540 </g>
541 <!-- v28 -->
542 <g id="node44" class="node">
543 <title>v28</title>
544 -<polygon fill="none" stroke="black" points="10127.02,-473.4 9858.42,-473.4 9858.42,-432.2 10127.02,-432.2 10127.02,-473.4"/>
545 -<text text-anchor="middle" x="9992.72" y="-457" font-family="Times,serif" font-size="14.00">v28 (readonly $23 @ Const readonly $23 = &quot;</text>
546 -<text text-anchor="middle" x="9992.72" y="-440.2" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;&#160;&#160;&quot;) &lt;assign&gt;</text>
544 +<polygon fill="none" stroke="black" points="10041.66,-473.4 9773.06,-473.4 9773.06,-432.2 10041.66,-432.2 10041.66,-473.4"/>
545 +<text text-anchor="middle" x="9907.36" y="-457" font-family="Times,serif" font-size="14.00">v28 (readonly $23 @ Const readonly $23 = &quot;</text>
546 +<text text-anchor="middle" x="9907.36" y="-440.2" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;&#160;&#160;&quot;) &lt;assign&gt;</text>
547 </g>
548 <!-- v33 -->
549 <g id="node45" class="node">
550 <title>v33</title>
551 -<polygon fill="none" stroke="black" points="10398.14,-396 9587.3,-396 9587.3,-360 10398.14,-360 10398.14,-396"/>
552 -<text text-anchor="middle" x="9992.72" y="-373.8" font-family="Times,serif" font-size="14.00">v33 (frozen $23 @ JSX &lt;frozen $18&gt;{frozen $19}{frozen $22}{frozen $23}{frozen renderedItems$4}{frozen $24}&lt;/frozen $18&gt;) &lt;update&gt;</text>
551 +<polygon fill="none" stroke="black" points="10306.71,-396 9508.02,-396 9508.02,-360 10306.71,-360 10306.71,-396"/>
552 +<text text-anchor="middle" x="9907.36" y="-373.8" font-family="Times,serif" font-size="14.00">v33 (freeze $23 @ JSX &lt;freeze $18&gt;{freeze $19}{freeze $22}{freeze $23}{freeze renderedItems$4}{freeze $24}&lt;/freeze $18&gt;) &lt;update&gt;</text>
553 </g>
554 <!-- v28&#45;&gt;v33 -->
555 <g id="edge29" class="edge">
556 <title>v28&#45;&gt;v33</title>
557 -<path fill="none" stroke="black" d="M9992.72,-431.91C9992.72,-424.09 9992.72,-414.99 9992.72,-406.56"/>
558 -<polygon fill="black" stroke="black" points="9996.22,-406.39 9992.72,-396.39 9989.22,-406.39 9996.22,-406.39"/>
557 +<path fill="none" stroke="black" d="M9907.36,-431.91C9907.36,-424.09 9907.36,-414.99 9907.36,-406.56"/>
558 +<polygon fill="black" stroke="black" points="9910.86,-406.39 9907.36,-396.39 9903.86,-406.39 9910.86,-406.39"/>
559 </g>
560 <!-- v29 -->
561 <g id="node46" class="node">
562 <title>v29</title>
563 -<polygon fill="none" stroke="black" points="10956.02,-473.4 10687.42,-473.4 10687.42,-432.2 10956.02,-432.2 10956.02,-473.4"/>
564 -<text text-anchor="middle" x="10821.72" y="-457" font-family="Times,serif" font-size="14.00">v29 (readonly $24 @ Const readonly $24 = &quot;</text>
565 -<text text-anchor="middle" x="10821.72" y="-440.2" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;&quot;) &lt;assign&gt;</text>
563 +<polygon fill="none" stroke="black" points="10857.66,-473.4 10589.06,-473.4 10589.06,-432.2 10857.66,-432.2 10857.66,-473.4"/>
564 +<text text-anchor="middle" x="10723.36" y="-457" font-family="Times,serif" font-size="14.00">v29 (readonly $24 @ Const readonly $24 = &quot;</text>
565 +<text text-anchor="middle" x="10723.36" y="-440.2" font-family="Times,serif" font-size="14.00"> &#160;&#160;&#160;&quot;) &lt;assign&gt;</text>
566 </g>
567 <!-- v35 -->
568 <g id="node47" class="node">
569 <title>v35</title>
570 -<polygon fill="none" stroke="black" points="11227.14,-396 10416.3,-396 10416.3,-360 11227.14,-360 11227.14,-396"/>
571 -<text text-anchor="middle" x="10821.72" y="-373.8" font-family="Times,serif" font-size="14.00">v35 (frozen $24 @ JSX &lt;frozen $18&gt;{frozen $19}{frozen $22}{frozen $23}{frozen renderedItems$4}{frozen $24}&lt;/frozen $18&gt;) &lt;update&gt;</text>
570 +<polygon fill="none" stroke="black" points="11122.71,-396 10324.02,-396 10324.02,-360 11122.71,-360 11122.71,-396"/>
571 +<text text-anchor="middle" x="10723.36" y="-373.8" font-family="Times,serif" font-size="14.00">v35 (freeze $24 @ JSX &lt;freeze $18&gt;{freeze $19}{freeze $22}{freeze $23}{freeze renderedItems$4}{freeze $24}&lt;/freeze $18&gt;) &lt;update&gt;</text>
572 </g>
573 <!-- v29&#45;&gt;v35 -->
574 <g id="edge30" class="edge">
575 <title>v29&#45;&gt;v35</title>
576 -<path fill="none" stroke="black" d="M10821.72,-431.91C10821.72,-424.09 10821.72,-414.99 10821.72,-406.56"/>
577 -<polygon fill="black" stroke="black" points="10825.22,-406.39 10821.72,-396.39 10818.22,-406.39 10825.22,-406.39"/>
576 +<path fill="none" stroke="black" d="M10723.36,-431.91C10723.36,-424.09 10723.36,-414.99 10723.36,-406.56"/>
577 +<polygon fill="black" stroke="black" points="10726.86,-406.39 10723.36,-396.39 10719.86,-406.39 10726.86,-406.39"/>
578 </g>
579 <!-- v36 -->
580 <g id="node48" class="node">
581 <title>v36</title>
582 -<polygon fill="none" stroke="black" points="11921.1,-470.8 10974.33,-470.8 10974.33,-434.8 11921.1,-434.8 11921.1,-470.8"/>
583 -<text text-anchor="middle" x="11447.72" y="-448.6" font-family="Times,serif" font-size="14.00">v36 (readonly $25 @ Const readonly $25 = JSX &lt;frozen $18&gt;{frozen $19}{frozen $22}{frozen $23}{frozen renderedItems$4}{frozen $24}&lt;/frozen $18&gt;) &lt;assign&gt;</text>
582 +<polygon fill="none" stroke="black" points="11811.24,-470.8 10875.48,-470.8 10875.48,-434.8 11811.24,-434.8 11811.24,-470.8"/>
583 +<text text-anchor="middle" x="11343.36" y="-448.6" font-family="Times,serif" font-size="14.00">v36 (readonly $25 @ Const readonly $25 = JSX &lt;freeze $18&gt;{freeze $19}{freeze $22}{freeze $23}{freeze renderedItems$4}{freeze $24}&lt;/freeze $18&gt;) &lt;assign&gt;</text>
584 </g>
585 <!-- v37 -->
586 <g id="node49" class="node">
587 <title>v37</title>
588 -<polygon fill="none" stroke="black" points="11592.15,-396 11303.29,-396 11303.29,-360 11592.15,-360 11592.15,-396"/>
589 -<text text-anchor="middle" x="11447.72" y="-373.8" font-family="Times,serif" font-size="14.00">v37 (frozen $25 @ Return frozen $25) &lt;update&gt;</text>
588 +<polygon fill="none" stroke="black" points="11486.15,-396 11200.57,-396 11200.57,-360 11486.15,-360 11486.15,-396"/>
589 +<text text-anchor="middle" x="11343.36" y="-373.8" font-family="Times,serif" font-size="14.00">v37 (freeze $25 @ Return freeze $25) &lt;update&gt;</text>
590 </g>
591 <!-- v36&#45;&gt;v37 -->
592 <g id="edge31" class="edge">
593 <title>v36&#45;&gt;v37</title>
594 -<path fill="none" stroke="black" d="M11447.72,-434.55C11447.72,-426.12 11447.72,-415.77 11447.72,-406.3"/>
595 -<polygon fill="black" stroke="black" points="11451.22,-406.13 11447.72,-396.13 11444.22,-406.13 11451.22,-406.13"/>
594 +<path fill="none" stroke="black" d="M11343.36,-434.55C11343.36,-426.12 11343.36,-415.77 11343.36,-406.3"/>
595 +<polygon fill="black" stroke="black" points="11346.86,-406.13 11343.36,-396.13 11339.86,-406.13 11346.86,-406.13"/>
596 </g>
597 <!-- v41 -->
598 <g id="node51" class="node">
599 <title>v41</title>
600 -<polygon fill="none" stroke="black" points="6514.18,-324 5961.26,-324 5961.26,-288 6514.18,-288 6514.18,-324"/>
601 -<text text-anchor="middle" x="6237.72" y="-301.8" font-family="Times,serif" font-size="14.00">v41 (readonly $13 @ Const readonly $13 = Call mutable seen$5.has(frozen item$10)) &lt;assign&gt;</text>
600 +<polygon fill="none" stroke="black" points="6487.25,-324 5935.47,-324 5935.47,-288 6487.25,-288 6487.25,-324"/>
601 +<text text-anchor="middle" x="6211.36" y="-301.8" font-family="Times,serif" font-size="14.00">v41 (readonly $13 @ Const readonly $13 = Call mutable seen$5.has(freeze item$10)) &lt;assign&gt;</text>
602 </g>
603 <!-- v41&#45;&gt;v42 -->
604 <g id="edge36" class="edge">
605 <title>v41&#45;&gt;v42</title>
606 -<path fill="none" stroke="black" d="M6180.29,-287.97C6146.74,-278.01 6104.23,-265.41 6069.11,-254.99"/>
607 -<polygon fill="black" stroke="black" points="6069.99,-251.6 6059.41,-252.11 6068,-258.31 6069.99,-251.6"/>
606 +<path fill="none" stroke="black" d="M6154.17,-287.97C6120.77,-278.01 6078.44,-265.41 6043.46,-254.99"/>
607 +<polygon fill="black" stroke="black" points="6044.39,-251.61 6033.8,-252.11 6042.39,-258.32 6044.39,-251.61"/>
608 </g>
609 <!-- v43&#45;&gt;v56 -->
610 <g id="edge38" class="edge">
611 <title>v43&#45;&gt;v56</title>
612 -<path fill="none" stroke="black" d="M2115.61,-252.1C2116.42,-259.79 2116.66,-269.05 2116.32,-277.67"/>
613 -<polygon fill="black" stroke="black" points="2112.83,-277.48 2115.63,-287.7 2119.81,-277.96 2112.83,-277.48"/>
612 +<path fill="none" stroke="black" d="M2105.26,-252.1C2106.06,-259.79 2106.3,-269.05 2105.96,-277.67"/>
613 +<polygon fill="black" stroke="black" points="2102.47,-277.48 2105.28,-287.7 2109.45,-277.96 2102.47,-277.48"/>
614 </g>
615 <!-- v43&#45;&gt;v59 -->
616 <g id="edge37" class="edge">
617 <title>v43&#45;&gt;v59</title>
618 -<path fill="none" stroke="black" d="M2175.91,-215.97C2214.99,-205.91 2264.62,-193.14 2305.35,-182.65"/>
619 -<polygon fill="black" stroke="black" points="2306.42,-185.99 2315.23,-180.11 2304.67,-179.21 2306.42,-185.99"/>
618 +<path fill="none" stroke="black" d="M2165.31,-215.97C2204.24,-205.91 2253.69,-193.14 2294.27,-182.65"/>
619 +<polygon fill="black" stroke="black" points="2295.31,-186 2304.12,-180.11 2293.56,-179.22 2295.31,-186"/>
620 </g>
621 <!-- v47 -->
622 <g id="node54" class="node">
623 <title>v47</title>
624 -<polygon fill="none" stroke="black" points="5517.07,-180 5024.36,-180 5024.36,-144 5517.07,-144 5517.07,-180"/>
625 -<text text-anchor="middle" x="5270.72" y="-157.8" font-family="Times,serif" font-size="14.00">v47 (frozen item$10 @ JSX &lt;frozen $14&gt;{frozen item$10}&lt;/frozen $14&gt;) &lt;update&gt;</text>
624 +<polygon fill="none" stroke="black" points="5494.43,-180 5008.3,-180 5008.3,-144 5494.43,-144 5494.43,-180"/>
625 +<text text-anchor="middle" x="5251.36" y="-157.8" font-family="Times,serif" font-size="14.00">v47 (freeze item$10 @ JSX &lt;freeze $14&gt;{freeze item$10}&lt;/freeze $14&gt;) &lt;update&gt;</text>
626 </g>
627 <!-- v44&#45;&gt;v47 -->
628 <g id="edge39" class="edge">
629 <title>v44&#45;&gt;v47</title>
630 -<path fill="none" stroke="black" d="M5176.3,-215.88C5192.79,-206.64 5213.34,-195.13 5231.06,-185.21"/>
631 -<polygon fill="black" stroke="black" points="5233.01,-188.13 5240.02,-180.19 5229.59,-182.02 5233.01,-188.13"/>
630 +<path fill="none" stroke="black" d="M5157.69,-215.88C5174.06,-206.64 5194.44,-195.13 5212.01,-185.21"/>
631 +<polygon fill="black" stroke="black" points="5213.92,-188.15 5220.91,-180.19 5210.48,-182.06 5213.92,-188.15"/>
632 </g>
633 <!-- v47&#45;&gt;v57 -->
634 <g id="edge41" class="edge">
635 <title>v47&#45;&gt;v57</title>
636 -<path fill="none" stroke="black" d="M5332.12,-180.05C5349.56,-188.03 5366.55,-199.55 5376.72,-216 5385.13,-229.61 5385.13,-238.39 5376.72,-252 5368.38,-265.5 5355.45,-275.67 5341.44,-283.3"/>
637 -<polygon fill="black" stroke="black" points="5339.51,-280.35 5332.12,-287.95 5342.63,-286.62 5339.51,-280.35"/>
636 +<path fill="none" stroke="black" d="M5311.93,-180.04C5329.29,-188.04 5346.25,-199.57 5356.36,-216 5364.75,-229.63 5364.75,-238.37 5356.36,-252 5348.06,-265.48 5335.16,-275.66 5321.21,-283.29"/>
637 +<polygon fill="black" stroke="black" points="5319.29,-280.34 5311.93,-287.96 5322.43,-286.6 5319.29,-280.34"/>
638 </g>
639 <!-- v45 -->
640 <g id="node55" class="node">
641 <title>v45</title>
642 -<polygon fill="none" stroke="black" points="12290.58,-470.8 11938.86,-470.8 11938.86,-434.8 12290.58,-434.8 12290.58,-470.8"/>
643 -<text text-anchor="middle" x="12114.72" y="-448.6" font-family="Times,serif" font-size="14.00">v45 (readonly $14 @ Const readonly $14 = &quot;div&quot;) &lt;assign&gt;</text>
642 +<polygon fill="none" stroke="black" points="12180.22,-470.8 11828.5,-470.8 11828.5,-434.8 12180.22,-434.8 12180.22,-470.8"/>
643 +<text text-anchor="middle" x="12004.36" y="-448.6" font-family="Times,serif" font-size="14.00">v45 (readonly $14 @ Const readonly $14 = &quot;div&quot;) &lt;assign&gt;</text>
644 </g>
645 <!-- v46 -->
646 <g id="node56" class="node">
647 <title>v46</title>
648 -<polygon fill="none" stroke="black" points="12348.69,-396 11880.75,-396 11880.75,-360 12348.69,-360 12348.69,-396"/>
649 -<text text-anchor="middle" x="12114.72" y="-373.8" font-family="Times,serif" font-size="14.00">v46 (frozen $14 @ JSX &lt;frozen $14&gt;{frozen item$10}&lt;/frozen $14&gt;) &lt;update&gt;</text>
648 +<polygon fill="none" stroke="black" points="12235.04,-396 11773.68,-396 11773.68,-360 12235.04,-360 12235.04,-396"/>
649 +<text text-anchor="middle" x="12004.36" y="-373.8" font-family="Times,serif" font-size="14.00">v46 (freeze $14 @ JSX &lt;freeze $14&gt;{freeze item$10}&lt;/freeze $14&gt;) &lt;update&gt;</text>
650 </g>
651 <!-- v45&#45;&gt;v46 -->
652 <g id="edge40" class="edge">
653 <title>v45&#45;&gt;v46</title>
654 -<path fill="none" stroke="black" d="M12114.72,-434.55C12114.72,-426.12 12114.72,-415.77 12114.72,-406.3"/>
655 -<polygon fill="black" stroke="black" points="12118.22,-406.13 12114.72,-396.13 12111.22,-406.13 12118.22,-406.13"/>
654 +<path fill="none" stroke="black" d="M12004.36,-434.55C12004.36,-426.12 12004.36,-415.77 12004.36,-406.3"/>
655 +<polygon fill="black" stroke="black" points="12007.86,-406.13 12004.36,-396.13 12000.86,-406.13 12007.86,-406.13"/>
656 </g>
657 <!-- v48 -->
658 <g id="node57" class="node">
659 <title>v48</title>
660 -<polygon fill="none" stroke="black" points="12911.65,-470.8 12307.79,-470.8 12307.79,-434.8 12911.65,-434.8 12911.65,-470.8"/>
661 -<text text-anchor="middle" x="12609.72" y="-448.6" font-family="Times,serif" font-size="14.00">v48 (readonly $15 @ Const readonly $15 = JSX &lt;frozen $14&gt;{frozen item$10}&lt;/frozen $14&gt;) &lt;assign&gt;</text>
660 +<polygon fill="none" stroke="black" points="12796.58,-470.8 12198.14,-470.8 12198.14,-434.8 12796.58,-434.8 12796.58,-470.8"/>
661 +<text text-anchor="middle" x="12497.36" y="-448.6" font-family="Times,serif" font-size="14.00">v48 (readonly $15 @ Const readonly $15 = JSX &lt;freeze $14&gt;{freeze item$10}&lt;/freeze $14&gt;) &lt;assign&gt;</text>
662 </g>
663 <!-- v50 -->
664 <g id="node58" class="node">
665 <title>v50</title>
666 -<polygon fill="none" stroke="black" points="12847.01,-396 12372.43,-396 12372.43,-360 12847.01,-360 12847.01,-396"/>
667 -<text text-anchor="middle" x="12609.72" y="-373.8" font-family="Times,serif" font-size="14.00">v50 (mutable $15 @ Call mutable renderedItems$4.push(mutable $15)) &lt;update&gt;</text>
666 +<polygon fill="none" stroke="black" points="12734.65,-396 12260.07,-396 12260.07,-360 12734.65,-360 12734.65,-396"/>
667 +<text text-anchor="middle" x="12497.36" y="-373.8" font-family="Times,serif" font-size="14.00">v50 (mutable $15 @ Call mutable renderedItems$4.push(mutable $15)) &lt;update&gt;</text>
668 </g>
669 <!-- v48&#45;&gt;v50 -->
670 <g id="edge42" class="edge">
671 <title>v48&#45;&gt;v50</title>
672 -<path fill="none" stroke="black" d="M12609.72,-434.55C12609.72,-426.12 12609.72,-415.77 12609.72,-406.3"/>
673 -<polygon fill="black" stroke="black" points="12613.22,-406.13 12609.72,-396.13 12606.22,-406.13 12613.22,-406.13"/>
672 +<path fill="none" stroke="black" d="M12497.36,-434.55C12497.36,-426.12 12497.36,-415.77 12497.36,-406.3"/>
673 +<polygon fill="black" stroke="black" points="12500.86,-406.13 12497.36,-396.13 12493.86,-406.13 12500.86,-406.13"/>
674 </g>
675 <!-- v51&#45;&gt;v18 -->
676 <g id="edge44" class="edge">
677 <title>v51&#45;&gt;v18</title>
678 -<path fill="none" stroke="black" d="M1471.31,-287.84C1490.68,-268.51 1524.34,-237.06 1557.72,-216 1577.57,-203.48 1600.88,-192.5 1621.72,-183.83"/>
679 -<polygon fill="black" stroke="black" points="1623.16,-187.03 1631.1,-180.01 1620.52,-180.54 1623.16,-187.03"/>
678 +<path fill="none" stroke="black" d="M1461.95,-287.84C1481.32,-268.51 1514.98,-237.06 1548.36,-216 1568.21,-203.48 1591.52,-192.5 1612.36,-183.83"/>
679 +<polygon fill="black" stroke="black" points="1613.8,-187.03 1621.74,-180.01 1611.16,-180.54 1613.8,-187.03"/>
680 </g>
681 <!-- v51&#45;&gt;v62 -->
682 <g id="edge45" class="edge">
683 <title>v51&#45;&gt;v62</title>
684 -<path fill="none" stroke="black" d="M1509.23,-287.97C1540.94,-278.06 1581.07,-265.51 1614.33,-255.12"/>
685 -<polygon fill="black" stroke="black" points="1615.46,-258.43 1623.96,-252.11 1613.38,-251.75 1615.46,-258.43"/>
684 +<path fill="none" stroke="black" d="M1499.87,-287.97C1531.58,-278.06 1571.72,-265.51 1604.97,-255.12"/>
685 +<polygon fill="black" stroke="black" points="1606.11,-258.43 1614.61,-252.11 1604.02,-251.75 1606.11,-258.43"/>
686 </g>
687 <!-- v51&#45;&gt;v71 -->
688 <g id="edge46" class="edge">
689 <title>v51&#45;&gt;v71</title>
690 -<path fill="none" stroke="black" d="M1368.57,-287.97C1316.65,-277.7 1250.42,-264.6 1196.8,-254"/>
691 -<polygon fill="black" stroke="black" points="1197.43,-250.56 1186.94,-252.05 1196.07,-257.42 1197.43,-250.56"/>
690 +<path fill="none" stroke="black" d="M1359.22,-287.97C1307.29,-277.7 1241.06,-264.6 1187.44,-254"/>
691 +<polygon fill="black" stroke="black" points="1188.07,-250.56 1177.59,-252.05 1186.72,-257.42 1188.07,-250.56"/>
692 </g>
693 <!-- v53 -->
694 <g id="node60" class="node">
695 <title>v53</title>
696 -<polygon fill="none" stroke="black" points="13565.32,-470.8 12930.12,-470.8 12930.12,-434.8 13565.32,-434.8 13565.32,-470.8"/>
697 -<text text-anchor="middle" x="13247.72" y="-448.6" font-family="Times,serif" font-size="14.00">v53 (frozen $16 @ Const frozen $16 = Binary readonly renderedItems$4.length &gt;= readonly max$7) &lt;assign&gt;</text>
696 +<polygon fill="none" stroke="black" points="13447.82,-470.8 12814.9,-470.8 12814.9,-434.8 13447.82,-434.8 13447.82,-470.8"/>
697 +<text text-anchor="middle" x="13131.36" y="-448.6" font-family="Times,serif" font-size="14.00">v53 (freeze $16 @ Const freeze $16 = Binary readonly renderedItems$4.length &gt;= readonly max$7) &lt;assign&gt;</text>
698 </g>
699 <!-- v54 -->
700 <g id="node61" class="node">
701 <title>v54</title>
702 -<polygon fill="none" stroke="black" points="13433.35,-396 13062.09,-396 13062.09,-360 13433.35,-360 13433.35,-396"/>
703 -<text text-anchor="middle" x="13247.72" y="-373.8" font-family="Times,serif" font-size="14.00">v54 (frozen $16 @ If (frozen $16) then:bb2 else:bb1) &lt;update&gt;</text>
702 +<polygon fill="none" stroke="black" points="13315.84,-396 12946.88,-396 12946.88,-360 13315.84,-360 13315.84,-396"/>
703 +<text text-anchor="middle" x="13131.36" y="-373.8" font-family="Times,serif" font-size="14.00">v54 (freeze $16 @ If (freeze $16) then:bb2 else:bb1) &lt;update&gt;</text>
704 </g>
705 <!-- v53&#45;&gt;v54 -->
706 <g id="edge48" class="edge">
707 <title>v53&#45;&gt;v54</title>
708 -<path fill="none" stroke="black" d="M13247.72,-434.55C13247.72,-426.12 13247.72,-415.77 13247.72,-406.3"/>
709 -<polygon fill="black" stroke="black" points="13251.22,-406.13 13247.72,-396.13 13244.22,-406.13 13251.22,-406.13"/>
708 +<path fill="none" stroke="black" d="M13131.36,-434.55C13131.36,-426.12 13131.36,-415.77 13131.36,-406.3"/>
709 +<polygon fill="black" stroke="black" points="13134.86,-406.13 13131.36,-396.13 13127.86,-406.13 13134.86,-406.13"/>
710 </g>
711 <!-- v55 -->
712 <g id="node62" class="node">
713 <title>v55</title>
714 -<polygon fill="none" stroke="black" points="583.43,-470.8 316.01,-470.8 316.01,-434.8 583.43,-434.8 583.43,-470.8"/>
715 -<text text-anchor="middle" x="449.72" y="-448.6" font-family="Times,serif" font-size="14.00">v55 (frozen props$1 @ &lt;no&#45;instr&gt;) &lt;assign&gt;</text>
714 +<polygon fill="none" stroke="black" points="579.5,-470.8 313.22,-470.8 313.22,-434.8 579.5,-434.8 579.5,-470.8"/>
715 +<text text-anchor="middle" x="446.36" y="-448.6" font-family="Times,serif" font-size="14.00">v55 (freeze props$1 @ &lt;no&#45;instr&gt;) &lt;assign&gt;</text>
716 </g>
717 <!-- v55&#45;&gt;v0 -->
718 <g id="edge49" class="edge">
719 <title>v55&#45;&gt;v0</title>
720 -<path fill="none" stroke="black" d="M449.72,-434.55C449.72,-426.12 449.72,-415.77 449.72,-406.3"/>
721 -<polygon fill="black" stroke="black" points="453.22,-406.13 449.72,-396.13 446.22,-406.13 453.22,-406.13"/>
720 +<path fill="none" stroke="black" d="M446.36,-434.55C446.36,-426.12 446.36,-415.77 446.36,-406.3"/>
721 +<polygon fill="black" stroke="black" points="449.86,-406.13 446.36,-396.13 442.86,-406.13 449.86,-406.13"/>
722 </g>
723 </g>
724 </svg>
compiler/forget/src/__tests__/fixtures/hir-svg/switch.svg
+97 -97
@@ -4,70 +4,70 @@
4 <!-- Generated by graphviz version 6.0.1 (20220924.0800)
5 -->
6 <!-- Title: BuildDefUseGraph Pages: 1 -->
7 -<svg width="5435pt" height="476pt"
8 - viewBox="0.00 0.00 5434.75 476.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
7 +<svg width="5406pt" height="476pt"
8 + viewBox="0.00 0.00 5405.61 476.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
9 <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 472)">
10 <title>BuildDefUseGraph</title>
11 -<polygon fill="white" stroke="none" points="-4,4 -4,-472 5430.75,-472 5430.75,4 -4,4"/>
11 +<polygon fill="white" stroke="none" points="-4,4 -4,-472 5401.61,-472 5401.61,4 -4,4"/>
12 <!-- v0 -->
13 <g id="node1" class="node">
14 <title>v0</title>
15 -<polygon fill="none" stroke="black" points="866.19,-468 519.21,-468 519.21,-432 866.19,-432 866.19,-468"/>
16 -<text text-anchor="middle" x="692.7" y="-445.8" font-family="Times,serif" font-size="14.00">v0 (readonly x$2 @ Let readonly x$2 = Array []) &lt;assign&gt;</text>
15 +<polygon fill="none" stroke="black" points="863.19,-468 516.21,-468 516.21,-432 863.19,-432 863.19,-468"/>
16 +<text text-anchor="middle" x="689.7" y="-445.8" font-family="Times,serif" font-size="14.00">v0 (readonly x$2 @ Let readonly x$2 = Array []) &lt;assign&gt;</text>
17 </g>
18 <!-- v7 -->
19 <g id="node2" class="node">
20 <title>v7</title>
21 -<polygon fill="none" stroke="black" points="665.13,-396 208.26,-396 208.26,-360 665.13,-360 665.13,-396"/>
22 -<text text-anchor="middle" x="436.7" y="-373.8" font-family="Times,serif" font-size="14.00">v7 (mutable x$2.push @ Call mutable x$2.push(frozen props$1.p2)) &lt;update&gt;</text>
21 +<polygon fill="none" stroke="black" points="662.06,-396 207.34,-396 207.34,-360 662.06,-360 662.06,-396"/>
22 +<text text-anchor="middle" x="434.7" y="-373.8" font-family="Times,serif" font-size="14.00">v7 (mutable x$2.push @ Call mutable x$2.push(freeze props$1.p2)) &lt;update&gt;</text>
23 </g>
24 <!-- v0&#45;&gt;v7 -->
25 <g id="edge1" class="edge">
26 <title>v0&#45;&gt;v7</title>
27 -<path fill="none" stroke="black" d="M630.4,-431.97C593.85,-421.97 547.5,-409.3 509.31,-398.85"/>
28 -<polygon fill="black" stroke="black" points="509.84,-395.37 499.27,-396.11 508,-402.12 509.84,-395.37"/>
27 +<path fill="none" stroke="black" d="M627.64,-431.97C591.24,-421.97 545.07,-409.3 507.02,-398.85"/>
28 +<polygon fill="black" stroke="black" points="507.6,-395.38 497.03,-396.11 505.75,-402.13 507.6,-395.38"/>
29 </g>
30 <!-- v12 -->
31 <g id="node3" class="node">
32 <title>v12</title>
33 -<polygon fill="none" stroke="black" points="637.48,-252 363.91,-252 363.91,-216 637.48,-216 637.48,-252"/>
34 -<text text-anchor="middle" x="500.7" y="-229.8" font-family="Times,serif" font-size="14.00">v12 (readonly x$2 @ readonly x$2) &lt;update&gt;</text>
33 +<polygon fill="none" stroke="black" points="635.48,-252 361.91,-252 361.91,-216 635.48,-216 635.48,-252"/>
34 +<text text-anchor="middle" x="498.7" y="-229.8" font-family="Times,serif" font-size="14.00">v12 (readonly x$2 @ readonly x$2) &lt;update&gt;</text>
35 </g>
36 <!-- v0&#45;&gt;v12 -->
37 <g id="edge2" class="edge">
38 <title>v0&#45;&gt;v12</title>
39 -<path fill="none" stroke="black" d="M696.22,-431.62C701.7,-399.31 708.32,-329.49 673.7,-288 662.06,-274.06 646.86,-263.72 630.3,-256.05"/>
40 -<polygon fill="black" stroke="black" points="631.55,-252.77 620.98,-252.06 628.8,-259.21 631.55,-252.77"/>
39 +<path fill="none" stroke="black" d="M693.21,-431.63C698.68,-399.32 705.29,-329.52 670.7,-288 659.05,-274.02 643.81,-263.66 627.2,-256"/>
40 +<polygon fill="black" stroke="black" points="628.43,-252.71 617.85,-252.01 625.68,-259.15 628.43,-252.71"/>
41 </g>
42 <!-- v15 -->
43 <g id="node4" class="node">
44 <title>v15</title>
45 -<polygon fill="none" stroke="black" points="1029.66,-180 431.73,-180 431.73,-144 1029.66,-144 1029.66,-180"/>
46 -<text text-anchor="middle" x="730.7" y="-157.8" font-family="Times,serif" font-size="14.00">v15 (frozen x$2 @ JSX &lt;frozen Component$0 data={frozen x$2} &gt;&lt;/frozen Component$0&gt;) &lt;update&gt;</text>
45 +<polygon fill="none" stroke="black" points="1023.37,-180 432.02,-180 432.02,-144 1023.37,-144 1023.37,-180"/>
46 +<text text-anchor="middle" x="727.7" y="-157.8" font-family="Times,serif" font-size="14.00">v15 (freeze x$2 @ JSX &lt;freeze Component$0 data={freeze x$2} &gt;&lt;/freeze Component$0&gt;) &lt;update&gt;</text>
47 </g>
48 <!-- v0&#45;&gt;v15 -->
49 <g id="edge3" class="edge">
50 <title>v0&#45;&gt;v15</title>
51 -<path fill="none" stroke="black" d="M701.51,-431.81C706.28,-421.66 711.78,-408.41 714.7,-396 731.81,-323.23 732.57,-234.42 731.66,-190.42"/>
52 -<polygon fill="black" stroke="black" points="735.15,-190.15 731.4,-180.24 728.16,-190.32 735.15,-190.15"/>
51 +<path fill="none" stroke="black" d="M698.51,-431.81C703.28,-421.66 708.78,-408.41 711.7,-396 728.81,-323.23 729.57,-234.42 728.66,-190.42"/>
52 +<polygon fill="black" stroke="black" points="732.15,-190.15 728.4,-180.24 725.16,-190.32 732.15,-190.15"/>
53 </g>
54 <!-- v9 -->
55 <g id="node15" class="node">
56 <title>v9</title>
57 -<polygon fill="none" stroke="black" points="665.13,-324 208.26,-324 208.26,-288 665.13,-288 665.13,-324"/>
58 -<text text-anchor="middle" x="436.7" y="-301.8" font-family="Times,serif" font-size="14.00">v9 (mutable x$2.push @ Call mutable x$2.push(frozen props$1.p3)) &lt;update&gt;</text>
57 +<polygon fill="none" stroke="black" points="662.06,-324 207.34,-324 207.34,-288 662.06,-288 662.06,-324"/>
58 +<text text-anchor="middle" x="434.7" y="-301.8" font-family="Times,serif" font-size="14.00">v9 (mutable x$2.push @ Call mutable x$2.push(freeze props$1.p3)) &lt;update&gt;</text>
59 </g>
60 <!-- v7&#45;&gt;v9 -->
61 <g id="edge10" class="edge">
62 <title>v7&#45;&gt;v9</title>
63 -<path fill="none" stroke="black" d="M436.7,-359.7C436.7,-351.98 436.7,-342.71 436.7,-334.11"/>
64 -<polygon fill="black" stroke="black" points="440.2,-334.1 436.7,-324.1 433.2,-334.1 440.2,-334.1"/>
63 +<path fill="none" stroke="black" d="M434.7,-359.7C434.7,-351.98 434.7,-342.71 434.7,-334.11"/>
64 +<polygon fill="black" stroke="black" points="438.2,-334.1 434.7,-324.1 431.2,-334.1 438.2,-334.1"/>
65 </g>
66 <!-- v12&#45;&gt;v15 -->
67 <g id="edge15" class="edge">
68 <title>v12&#45;&gt;v15</title>
69 -<path fill="none" stroke="black" d="M556.67,-215.97C589.22,-206.06 630.44,-193.51 664.58,-183.12"/>
70 -<polygon fill="black" stroke="black" points="665.93,-186.37 674.48,-180.11 663.89,-179.67 665.93,-186.37"/>
69 +<path fill="none" stroke="black" d="M554.42,-215.97C586.84,-206.06 627.87,-193.51 661.87,-183.12"/>
70 +<polygon fill="black" stroke="black" points="663.18,-186.38 671.72,-180.11 661.13,-179.69 663.18,-186.38"/>
71 </g>
72 <!-- v13 -->
73 <g id="node18" class="node">
@@ -78,224 +78,224 @@
78 <!-- v12&#45;&gt;v13 -->
79 <g id="edge14" class="edge">
80 <title>v12&#45;&gt;v13</title>
81 -<path fill="none" stroke="black" d="M429.15,-215.97C386.57,-205.83 332.39,-192.93 288.16,-182.4"/>
82 -<polygon fill="black" stroke="black" points="288.85,-178.96 278.31,-180.05 287.22,-185.77 288.85,-178.96"/>
81 +<path fill="none" stroke="black" d="M427.64,-215.97C385.34,-205.83 331.53,-192.93 287.61,-182.4"/>
82 +<polygon fill="black" stroke="black" points="288.36,-178.98 277.82,-180.05 286.73,-185.78 288.36,-178.98"/>
83 </g>
84 <!-- v1 -->
85 <g id="node5" class="node">
86 <title>v1</title>
87 -<polygon fill="none" stroke="black" points="1404.19,-180 1047.2,-180 1047.2,-144 1404.19,-144 1404.19,-180"/>
88 -<text text-anchor="middle" x="1225.7" y="-157.8" font-family="Times,serif" font-size="14.00">v1 (readonly y$3 @ Let readonly y$3 = undefined) &lt;assign&gt;</text>
87 +<polygon fill="none" stroke="black" points="1398.19,-180 1041.2,-180 1041.2,-144 1398.19,-144 1398.19,-180"/>
88 +<text text-anchor="middle" x="1219.7" y="-157.8" font-family="Times,serif" font-size="14.00">v1 (readonly y$3 @ Let readonly y$3 = undefined) &lt;assign&gt;</text>
89 </g>
90 <!-- v17 -->
91 <g id="node6" class="node">
92 <title>v17</title>
93 -<polygon fill="none" stroke="black" points="948.63,-108 484.76,-108 484.76,-72 948.63,-72 948.63,-108"/>
94 -<text text-anchor="middle" x="716.7" y="-85.8" font-family="Times,serif" font-size="14.00">v17 (mutable y$3.push @ Call mutable y$3.push(frozen props$1.p4)) &lt;update&gt;</text>
93 +<polygon fill="none" stroke="black" points="944.56,-108 482.84,-108 482.84,-72 944.56,-72 944.56,-108"/>
94 +<text text-anchor="middle" x="713.7" y="-85.8" font-family="Times,serif" font-size="14.00">v17 (mutable y$3.push @ Call mutable y$3.push(freeze props$1.p4)) &lt;update&gt;</text>
95 </g>
96 <!-- v1&#45;&gt;v17 -->
97 <g id="edge4" class="edge">
98 <title>v1&#45;&gt;v17</title>
99 -<path fill="none" stroke="black" d="M1101.83,-143.97C1025.93,-133.53 928.77,-120.17 851.03,-109.47"/>
100 -<polygon fill="black" stroke="black" points="851.06,-105.95 840.68,-108.05 850.1,-112.88 851.06,-105.95"/>
99 +<path fill="none" stroke="black" d="M1096.56,-143.97C1021.11,-133.53 924.52,-120.17 847.23,-109.47"/>
100 +<polygon fill="black" stroke="black" points="847.33,-105.95 836.94,-108.05 846.37,-112.89 847.33,-105.95"/>
101 </g>
102 <!-- v20 -->
103 <g id="node23" class="node">
104 <title>v20</title>
105 -<polygon fill="none" stroke="black" points="1062.85,-36 370.55,-36 370.55,0 1062.85,0 1062.85,-36"/>
106 -<text text-anchor="middle" x="716.7" y="-13.8" font-family="Times,serif" font-size="14.00">v20 (frozen y$3 @ JSX &lt;frozen Component$0 data={frozen y$3} &gt;{frozen child$6}&lt;/frozen Component$0&gt;) &lt;update&gt;</text>
105 +<polygon fill="none" stroke="black" points="1055.98,-36 371.41,-36 371.41,0 1055.98,0 1055.98,-36"/>
106 +<text text-anchor="middle" x="713.7" y="-13.8" font-family="Times,serif" font-size="14.00">v20 (freeze y$3 @ JSX &lt;freeze Component$0 data={freeze y$3} &gt;{freeze child$6}&lt;/freeze Component$0&gt;) &lt;update&gt;</text>
107 </g>
108 <!-- v17&#45;&gt;v20 -->
109 <g id="edge19" class="edge">
110 <title>v17&#45;&gt;v20</title>
111 -<path fill="none" stroke="black" d="M716.7,-71.7C716.7,-63.98 716.7,-54.71 716.7,-46.11"/>
112 -<polygon fill="black" stroke="black" points="720.2,-46.1 716.7,-36.1 713.2,-46.1 720.2,-46.1"/>
111 +<path fill="none" stroke="black" d="M713.7,-71.7C713.7,-63.98 713.7,-54.71 713.7,-46.11"/>
112 +<polygon fill="black" stroke="black" points="717.2,-46.1 713.7,-36.1 710.2,-46.1 717.2,-46.1"/>
113 </g>
114 <!-- v2 -->
115 <g id="node7" class="node">
116 <title>v2</title>
117 -<polygon fill="none" stroke="black" points="1274.66,-468 946.73,-468 946.73,-432 1274.66,-432 1274.66,-468"/>
118 -<text text-anchor="middle" x="1110.7" y="-445.8" font-family="Times,serif" font-size="14.00">v2 (readonly $4 @ Const readonly $4 = false) &lt;assign&gt;</text>
117 +<polygon fill="none" stroke="black" points="1270.66,-468 942.73,-468 942.73,-432 1270.66,-432 1270.66,-468"/>
118 +<text text-anchor="middle" x="1106.7" y="-445.8" font-family="Times,serif" font-size="14.00">v2 (readonly $4 @ Const readonly $4 = false) &lt;assign&gt;</text>
119 </g>
120 <!-- v6 -->
121 <g id="node8" class="node">
122 <title>v6</title>
123 -<polygon fill="none" stroke="black" points="1462.78,-396 758.61,-396 758.61,-360 1462.78,-360 1462.78,-396"/>
124 -<text text-anchor="middle" x="1110.7" y="-373.8" font-family="Times,serif" font-size="14.00">v6 (readonly $4 @ Switch (frozen props$1.p0); &#160;&#160;Case readonly $5: bb4; &#160;&#160;Case readonly $4: bb2; &#160;&#160;Default: bb1) &lt;update&gt;</text>
123 +<polygon fill="none" stroke="black" points="1457.71,-396 755.68,-396 755.68,-360 1457.71,-360 1457.71,-396"/>
124 +<text text-anchor="middle" x="1106.7" y="-373.8" font-family="Times,serif" font-size="14.00">v6 (readonly $4 @ Switch (freeze props$1.p0); &#160;&#160;Case readonly $5: bb4; &#160;&#160;Case readonly $4: bb2; &#160;&#160;Default: bb1) &lt;update&gt;</text>
125 </g>
126 <!-- v2&#45;&gt;v6 -->
127 <g id="edge5" class="edge">
128 <title>v2&#45;&gt;v6</title>
129 -<path fill="none" stroke="black" d="M1110.7,-431.7C1110.7,-423.98 1110.7,-414.71 1110.7,-406.11"/>
130 -<polygon fill="black" stroke="black" points="1114.2,-406.1 1110.7,-396.1 1107.2,-406.1 1114.2,-406.1"/>
129 +<path fill="none" stroke="black" d="M1106.7,-431.7C1106.7,-423.98 1106.7,-414.71 1106.7,-406.11"/>
130 +<polygon fill="black" stroke="black" points="1110.2,-406.1 1106.7,-396.1 1103.2,-406.1 1110.2,-406.1"/>
131 </g>
132 <!-- v3 -->
133 <g id="node9" class="node">
134 <title>v3</title>
135 -<polygon fill="none" stroke="black" points="1994,-468 1671.39,-468 1671.39,-432 1994,-432 1994,-468"/>
136 -<text text-anchor="middle" x="1832.7" y="-445.8" font-family="Times,serif" font-size="14.00">v3 (readonly $5 @ Const readonly $5 = true) &lt;assign&gt;</text>
135 +<polygon fill="none" stroke="black" points="1989,-468 1666.39,-468 1666.39,-432 1989,-432 1989,-468"/>
136 +<text text-anchor="middle" x="1827.7" y="-445.8" font-family="Times,serif" font-size="14.00">v3 (readonly $5 @ Const readonly $5 = true) &lt;assign&gt;</text>
137 </g>
138 <!-- v5 -->
139 <g id="node10" class="node">
140 <title>v5</title>
141 -<polygon fill="none" stroke="black" points="2184.78,-396 1480.61,-396 1480.61,-360 2184.78,-360 2184.78,-396"/>
142 -<text text-anchor="middle" x="1832.7" y="-373.8" font-family="Times,serif" font-size="14.00">v5 (readonly $5 @ Switch (frozen props$1.p0); &#160;&#160;Case readonly $5: bb4; &#160;&#160;Case readonly $4: bb2; &#160;&#160;Default: bb1) &lt;update&gt;</text>
141 +<polygon fill="none" stroke="black" points="2178.71,-396 1476.68,-396 1476.68,-360 2178.71,-360 2178.71,-396"/>
142 +<text text-anchor="middle" x="1827.7" y="-373.8" font-family="Times,serif" font-size="14.00">v5 (readonly $5 @ Switch (freeze props$1.p0); &#160;&#160;Case readonly $5: bb4; &#160;&#160;Case readonly $4: bb2; &#160;&#160;Default: bb1) &lt;update&gt;</text>
143 </g>
144 <!-- v3&#45;&gt;v5 -->
145 <g id="edge6" class="edge">
146 <title>v3&#45;&gt;v5</title>
147 -<path fill="none" stroke="black" d="M1832.7,-431.7C1832.7,-423.98 1832.7,-414.71 1832.7,-406.11"/>
148 -<polygon fill="black" stroke="black" points="1836.2,-406.1 1832.7,-396.1 1829.2,-406.1 1836.2,-406.1"/>
147 +<path fill="none" stroke="black" d="M1827.7,-431.7C1827.7,-423.98 1827.7,-414.71 1827.7,-406.11"/>
148 +<polygon fill="black" stroke="black" points="1831.2,-406.1 1827.7,-396.1 1824.2,-406.1 1831.2,-406.1"/>
149 </g>
150 <!-- v4 -->
151 <g id="node11" class="node">
152 <title>v4</title>
153 -<polygon fill="none" stroke="black" points="2942.66,-396 2202.73,-396 2202.73,-360 2942.66,-360 2942.66,-396"/>
154 -<text text-anchor="middle" x="2572.7" y="-373.8" font-family="Times,serif" font-size="14.00">v4 (frozen props$1.p0 @ Switch (frozen props$1.p0); &#160;&#160;Case readonly $5: bb4; &#160;&#160;Case readonly $4: bb2; &#160;&#160;Default: bb1) &lt;update&gt;</text>
153 +<polygon fill="none" stroke="black" points="2933.02,-396 2196.37,-396 2196.37,-360 2933.02,-360 2933.02,-396"/>
154 +<text text-anchor="middle" x="2564.7" y="-373.8" font-family="Times,serif" font-size="14.00">v4 (freeze props$1.p0 @ Switch (freeze props$1.p0); &#160;&#160;Case readonly $5: bb4; &#160;&#160;Case readonly $4: bb2; &#160;&#160;Default: bb1) &lt;update&gt;</text>
155 </g>
156 <!-- v8 -->
157 <g id="node12" class="node">
158 <title>v8</title>
159 -<polygon fill="none" stroke="black" points="2542.95,-324 2084.45,-324 2084.45,-288 2542.95,-288 2542.95,-324"/>
160 -<text text-anchor="middle" x="2313.7" y="-301.8" font-family="Times,serif" font-size="14.00">v8 (frozen props$1.p2 @ Call mutable x$2.push(frozen props$1.p2)) &lt;update&gt;</text>
159 +<polygon fill="none" stroke="black" points="2535.3,-324 2080.09,-324 2080.09,-288 2535.3,-288 2535.3,-324"/>
160 +<text text-anchor="middle" x="2307.7" y="-301.8" font-family="Times,serif" font-size="14.00">v8 (freeze props$1.p2 @ Call mutable x$2.push(freeze props$1.p2)) &lt;update&gt;</text>
161 </g>
162 <!-- v4&#45;&gt;v8 -->
163 <g id="edge7" class="edge">
164 <title>v4&#45;&gt;v8</title>
165 -<path fill="none" stroke="black" d="M2509.67,-359.97C2472.54,-349.93 2425.4,-337.19 2386.66,-326.72"/>
166 -<polygon fill="black" stroke="black" points="2387.57,-323.34 2377.01,-324.11 2385.75,-330.1 2387.57,-323.34"/>
165 +<path fill="none" stroke="black" d="M2502.16,-359.97C2465.47,-349.97 2418.94,-337.3 2380.59,-326.85"/>
166 +<polygon fill="black" stroke="black" points="2381.09,-323.36 2370.52,-324.11 2379.25,-330.12 2381.09,-323.36"/>
167 </g>
168 <!-- v18 -->
169 <g id="node13" class="node">
170 <title>v18</title>
171 -<polygon fill="none" stroke="black" points="2773.45,-108 2307.95,-108 2307.95,-72 2773.45,-72 2773.45,-108"/>
172 -<text text-anchor="middle" x="2540.7" y="-85.8" font-family="Times,serif" font-size="14.00">v18 (frozen props$1.p4 @ Call mutable y$3.push(frozen props$1.p4)) &lt;update&gt;</text>
171 +<polygon fill="none" stroke="black" points="2763.8,-108 2301.59,-108 2301.59,-72 2763.8,-72 2763.8,-108"/>
172 +<text text-anchor="middle" x="2532.7" y="-85.8" font-family="Times,serif" font-size="14.00">v18 (freeze props$1.p4 @ Call mutable y$3.push(freeze props$1.p4)) &lt;update&gt;</text>
173 </g>
174 <!-- v4&#45;&gt;v18 -->
175 <g id="edge8" class="edge">
176 <title>v4&#45;&gt;v18</title>
177 -<path fill="none" stroke="black" d="M2580.59,-359.75C2584.8,-349.58 2589.55,-336.33 2591.7,-324 2605.4,-245.18 2621.6,-218.2 2591.7,-144 2587.35,-133.21 2579.63,-123.39 2571.5,-115.21"/>
178 -<polygon fill="black" stroke="black" points="2573.69,-112.46 2564,-108.18 2568.91,-117.57 2573.69,-112.46"/>
177 +<path fill="none" stroke="black" d="M2572.59,-359.75C2576.8,-349.58 2581.55,-336.33 2583.7,-324 2597.4,-245.18 2613.6,-218.2 2583.7,-144 2579.35,-133.21 2571.63,-123.39 2563.5,-115.21"/>
178 +<polygon fill="black" stroke="black" points="2565.69,-112.46 2556,-108.18 2560.91,-117.57 2565.69,-112.46"/>
179 </g>
180 <!-- v25 -->
181 <g id="node14" class="node">
182 <title>v25</title>
183 -<polygon fill="none" stroke="black" points="2582.44,-180 2358.96,-180 2358.96,-144 2582.44,-144 2582.44,-180"/>
184 -<text text-anchor="middle" x="2470.7" y="-157.8" font-family="Times,serif" font-size="14.00">v25 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
183 +<polygon fill="none" stroke="black" points="2574.44,-180 2350.96,-180 2350.96,-144 2574.44,-144 2574.44,-180"/>
184 +<text text-anchor="middle" x="2462.7" y="-157.8" font-family="Times,serif" font-size="14.00">v25 (&lt;join&gt; @ &lt;no&#45;instr&gt;) &lt;update&gt;</text>
185 </g>
186 <!-- v4&#45;&gt;v25 -->
187 <g id="edge9" class="edge">
188 <title>v4&#45;&gt;v25</title>
189 -<path fill="none" stroke="black" d="M2575.17,-359.94C2578.77,-328.75 2582.25,-261.55 2553.7,-216 2545.8,-203.41 2533.7,-193.25 2521.15,-185.34"/>
190 -<polygon fill="black" stroke="black" points="2522.84,-182.27 2512.44,-180.21 2519.29,-188.3 2522.84,-182.27"/>
189 +<path fill="none" stroke="black" d="M2567.17,-359.94C2570.77,-328.75 2574.25,-261.55 2545.7,-216 2537.8,-203.41 2525.7,-193.25 2513.15,-185.34"/>
190 +<polygon fill="black" stroke="black" points="2514.84,-182.27 2504.44,-180.21 2511.29,-188.3 2514.84,-182.27"/>
191 </g>
192 <!-- v10 -->
193 <g id="node16" class="node">
194 <title>v10</title>
195 -<polygon fill="none" stroke="black" points="2544.45,-252 2078.95,-252 2078.95,-216 2544.45,-216 2544.45,-252"/>
196 -<text text-anchor="middle" x="2311.7" y="-229.8" font-family="Times,serif" font-size="14.00">v10 (frozen props$1.p3 @ Call mutable x$2.push(frozen props$1.p3)) &lt;update&gt;</text>
195 +<polygon fill="none" stroke="black" points="2536.8,-252 2074.59,-252 2074.59,-216 2536.8,-216 2536.8,-252"/>
196 +<text text-anchor="middle" x="2305.7" y="-229.8" font-family="Times,serif" font-size="14.00">v10 (freeze props$1.p3 @ Call mutable x$2.push(freeze props$1.p3)) &lt;update&gt;</text>
197 </g>
198 <!-- v8&#45;&gt;v10 -->
199 <g id="edge11" class="edge">
200 <title>v8&#45;&gt;v10</title>
201 -<path fill="none" stroke="black" d="M2313.2,-287.7C2312.98,-279.98 2312.72,-270.71 2312.47,-262.11"/>
202 -<polygon fill="black" stroke="black" points="2315.97,-262 2312.19,-252.1 2308.97,-262.2 2315.97,-262"/>
201 +<path fill="none" stroke="black" d="M2307.2,-287.7C2306.98,-279.98 2306.72,-270.71 2306.47,-262.11"/>
202 +<polygon fill="black" stroke="black" points="2309.97,-262 2306.19,-252.1 2302.97,-262.2 2309.97,-262"/>
203 </g>
204 <!-- v25&#45;&gt;v18 -->
205 <g id="edge22" class="edge">
206 <title>v25&#45;&gt;v18</title>
207 -<path fill="none" stroke="black" d="M2488,-143.7C2496.56,-135.14 2507.04,-124.66 2516.39,-115.3"/>
208 -<polygon fill="black" stroke="black" points="2519,-117.65 2523.59,-108.1 2514.05,-112.7 2519,-117.65"/>
207 +<path fill="none" stroke="black" d="M2480,-143.7C2488.56,-135.14 2499.04,-124.66 2508.39,-115.3"/>
208 +<polygon fill="black" stroke="black" points="2511,-117.65 2515.59,-108.1 2506.05,-112.7 2511,-117.65"/>
209 </g>
210 <!-- v9&#45;&gt;v12 -->
211 <g id="edge12" class="edge">
212 <title>v9&#45;&gt;v12</title>
213 -<path fill="none" stroke="black" d="M452.52,-287.7C460.27,-279.22 469.74,-268.86 478.22,-259.58"/>
214 -<polygon fill="black" stroke="black" points="480.89,-261.85 485.06,-252.1 475.73,-257.12 480.89,-261.85"/>
213 +<path fill="none" stroke="black" d="M450.52,-287.7C458.27,-279.22 467.74,-268.86 476.22,-259.58"/>
214 +<polygon fill="black" stroke="black" points="478.89,-261.85 483.06,-252.1 473.73,-257.12 478.89,-261.85"/>
215 </g>
216 <!-- v10&#45;&gt;v25 -->
217 <g id="edge13" class="edge">
218 <title>v10&#45;&gt;v25</title>
219 -<path fill="none" stroke="black" d="M2350.59,-215.88C2372.33,-206.31 2399.6,-194.3 2422.66,-184.15"/>
220 -<polygon fill="black" stroke="black" points="2424.19,-187.3 2431.93,-180.07 2421.36,-180.89 2424.19,-187.3"/>
219 +<path fill="none" stroke="black" d="M2344.1,-215.88C2365.57,-206.31 2392.5,-194.3 2415.27,-184.15"/>
220 +<polygon fill="black" stroke="black" points="2416.71,-187.34 2424.41,-180.07 2413.86,-180.94 2416.71,-187.34"/>
221 </g>
222 <!-- v11 -->
223 <g id="node17" class="node">
224 <title>v11</title>
225 -<polygon fill="none" stroke="black" points="3110.58,-468 2724.81,-468 2724.81,-432 3110.58,-432 3110.58,-468"/>
226 -<text text-anchor="middle" x="2917.7" y="-445.8" font-family="Times,serif" font-size="14.00">v11 (readonly y$3 @ Reassign readonly y$3 = Array []) &lt;assign&gt;</text>
225 +<polygon fill="none" stroke="black" points="3101.58,-468 2715.81,-468 2715.81,-432 3101.58,-432 3101.58,-468"/>
226 +<text text-anchor="middle" x="2908.7" y="-445.8" font-family="Times,serif" font-size="14.00">v11 (readonly y$3 @ Reassign readonly y$3 = Array []) &lt;assign&gt;</text>
227 </g>
228 <!-- v13&#45;&gt;v17 -->
229 <g id="edge16" class="edge">
230 <title>v13&#45;&gt;v17</title>
231 -<path fill="none" stroke="black" d="M330.8,-143.97C406.85,-133.53 504.21,-120.17 582.1,-109.47"/>
232 -<polygon fill="black" stroke="black" points="583.04,-112.88 592.47,-108.05 582.09,-105.94 583.04,-112.88"/>
231 +<path fill="none" stroke="black" d="M330.07,-143.97C405.67,-133.53 502.46,-120.17 579.9,-109.47"/>
232 +<polygon fill="black" stroke="black" points="580.78,-112.88 590.21,-108.05 579.82,-105.95 580.78,-112.88"/>
233 </g>
234 <!-- v14 -->
235 <g id="node19" class="node">
236 <title>v14</title>
237 -<polygon fill="none" stroke="black" points="3784.99,-468 3128.4,-468 3128.4,-432 3784.99,-432 3784.99,-468"/>
238 -<text text-anchor="middle" x="3456.7" y="-445.8" font-family="Times,serif" font-size="14.00">v14 (frozen Component$0 @ JSX &lt;frozen Component$0 data={frozen x$2} &gt;&lt;/frozen Component$0&gt;) &lt;update&gt;</text>
237 +<polygon fill="none" stroke="black" points="3769.7,-468 3119.69,-468 3119.69,-432 3769.7,-432 3769.7,-468"/>
238 +<text text-anchor="middle" x="3444.7" y="-445.8" font-family="Times,serif" font-size="14.00">v14 (freeze Component$0 @ JSX &lt;freeze Component$0 data={freeze x$2} &gt;&lt;/freeze Component$0&gt;) &lt;update&gt;</text>
239 </g>
240 <!-- v19 -->
241 <g id="node20" class="node">
242 <title>v19</title>
243 -<polygon fill="none" stroke="black" points="3832.18,-396 3081.22,-396 3081.22,-360 3832.18,-360 3832.18,-396"/>
244 -<text text-anchor="middle" x="3456.7" y="-373.8" font-family="Times,serif" font-size="14.00">v19 (frozen Component$0 @ JSX &lt;frozen Component$0 data={frozen y$3} &gt;{frozen child$6}&lt;/frozen Component$0&gt;) &lt;update&gt;</text>
243 +<polygon fill="none" stroke="black" points="3816.31,-396 3073.08,-396 3073.08,-360 3816.31,-360 3816.31,-396"/>
244 +<text text-anchor="middle" x="3444.7" y="-373.8" font-family="Times,serif" font-size="14.00">v19 (freeze Component$0 @ JSX &lt;freeze Component$0 data={freeze y$3} &gt;{freeze child$6}&lt;/freeze Component$0&gt;) &lt;update&gt;</text>
245 </g>
246 <!-- v14&#45;&gt;v19 -->
247 <g id="edge17" class="edge">
248 <title>v14&#45;&gt;v19</title>
249 -<path fill="none" stroke="black" d="M3456.7,-431.7C3456.7,-423.98 3456.7,-414.71 3456.7,-406.11"/>
250 -<polygon fill="black" stroke="black" points="3460.2,-406.1 3456.7,-396.1 3453.2,-406.1 3460.2,-406.1"/>
249 +<path fill="none" stroke="black" d="M3444.7,-431.7C3444.7,-423.98 3444.7,-414.71 3444.7,-406.11"/>
250 +<polygon fill="black" stroke="black" points="3448.2,-406.1 3444.7,-396.1 3441.2,-406.1 3448.2,-406.1"/>
251 </g>
252 <!-- v16 -->
253 <g id="node21" class="node">
254 <title>v16</title>
255 -<polygon fill="none" stroke="black" points="4594.61,-468 3818.79,-468 3818.79,-432 4594.61,-432 4594.61,-468"/>
256 -<text text-anchor="middle" x="4206.7" y="-445.8" font-family="Times,serif" font-size="14.00">v16 (readonly child$6 @ Const readonly child$6 = JSX &lt;frozen Component$0 data={frozen x$2} &gt;&lt;/frozen Component$0&gt;) &lt;assign&gt;</text>
255 +<polygon fill="none" stroke="black" points="4571.89,-468 3801.5,-468 3801.5,-432 4571.89,-432 4571.89,-468"/>
256 +<text text-anchor="middle" x="4186.7" y="-445.8" font-family="Times,serif" font-size="14.00">v16 (readonly child$6 @ Const readonly child$6 = JSX &lt;freeze Component$0 data={freeze x$2} &gt;&lt;/freeze Component$0&gt;) &lt;assign&gt;</text>
257 </g>
258 <!-- v21 -->
259 <g id="node22" class="node">
260 <title>v21</title>
261 -<polygon fill="none" stroke="black" points="4563.34,-396 3850.06,-396 3850.06,-360 4563.34,-360 4563.34,-396"/>
262 -<text text-anchor="middle" x="4206.7" y="-373.8" font-family="Times,serif" font-size="14.00">v21 (frozen child$6 @ JSX &lt;frozen Component$0 data={frozen y$3} &gt;{frozen child$6}&lt;/frozen Component$0&gt;) &lt;update&gt;</text>
261 +<polygon fill="none" stroke="black" points="4539.48,-396 3833.92,-396 3833.92,-360 4539.48,-360 4539.48,-396"/>
262 +<text text-anchor="middle" x="4186.7" y="-373.8" font-family="Times,serif" font-size="14.00">v21 (freeze child$6 @ JSX &lt;freeze Component$0 data={freeze y$3} &gt;{freeze child$6}&lt;/freeze Component$0&gt;) &lt;update&gt;</text>
263 </g>
264 <!-- v16&#45;&gt;v21 -->
265 <g id="edge18" class="edge">
266 <title>v16&#45;&gt;v21</title>
267 -<path fill="none" stroke="black" d="M4206.7,-431.7C4206.7,-423.98 4206.7,-414.71 4206.7,-406.11"/>
268 -<polygon fill="black" stroke="black" points="4210.2,-406.1 4206.7,-396.1 4203.2,-406.1 4210.2,-406.1"/>
267 +<path fill="none" stroke="black" d="M4186.7,-431.7C4186.7,-423.98 4186.7,-414.71 4186.7,-406.11"/>
268 +<polygon fill="black" stroke="black" points="4190.2,-406.1 4186.7,-396.1 4183.2,-406.1 4190.2,-406.1"/>
269 </g>
270 <!-- v22 -->
271 <g id="node24" class="node">
272 <title>v22</title>
273 -<polygon fill="none" stroke="black" points="5426.81,-468 4612.59,-468 4612.59,-432 5426.81,-432 5426.81,-468"/>
274 -<text text-anchor="middle" x="5019.7" y="-445.8" font-family="Times,serif" font-size="14.00">v22 (readonly $7 @ Const readonly $7 = JSX &lt;frozen Component$0 data={frozen y$3} &gt;{frozen child$6}&lt;/frozen Component$0&gt;) &lt;assign&gt;</text>
273 +<polygon fill="none" stroke="black" points="5397.52,-468 4589.88,-468 4589.88,-432 5397.52,-432 5397.52,-468"/>
274 +<text text-anchor="middle" x="4993.7" y="-445.8" font-family="Times,serif" font-size="14.00">v22 (readonly $7 @ Const readonly $7 = JSX &lt;freeze Component$0 data={freeze y$3} &gt;{freeze child$6}&lt;/freeze Component$0&gt;) &lt;assign&gt;</text>
275 </g>
276 <!-- v23 -->
277 <g id="node25" class="node">
278 <title>v23</title>
279 -<polygon fill="none" stroke="black" points="5157.13,-396 4882.26,-396 4882.26,-360 5157.13,-360 5157.13,-396"/>
280 -<text text-anchor="middle" x="5019.7" y="-373.8" font-family="Times,serif" font-size="14.00">v23 (frozen $7 @ Return frozen $7) &lt;update&gt;</text>
279 +<polygon fill="none" stroke="black" points="5129.49,-396 4857.91,-396 4857.91,-360 5129.49,-360 5129.49,-396"/>
280 +<text text-anchor="middle" x="4993.7" y="-373.8" font-family="Times,serif" font-size="14.00">v23 (freeze $7 @ Return freeze $7) &lt;update&gt;</text>
281 </g>
282 <!-- v22&#45;&gt;v23 -->
283 <g id="edge20" class="edge">
284 <title>v22&#45;&gt;v23</title>
285 -<path fill="none" stroke="black" d="M5019.7,-431.7C5019.7,-423.98 5019.7,-414.71 5019.7,-406.11"/>
286 -<polygon fill="black" stroke="black" points="5023.2,-406.1 5019.7,-396.1 5016.2,-406.1 5023.2,-406.1"/>
285 +<path fill="none" stroke="black" d="M4993.7,-431.7C4993.7,-423.98 4993.7,-414.71 4993.7,-406.11"/>
286 +<polygon fill="black" stroke="black" points="4997.2,-406.1 4993.7,-396.1 4990.2,-406.1 4997.2,-406.1"/>
287 </g>
288 <!-- v24 -->
289 <g id="node26" class="node">
290 <title>v24</title>
291 -<polygon fill="none" stroke="black" points="2706.41,-468 2438.98,-468 2438.98,-432 2706.41,-432 2706.41,-468"/>
292 -<text text-anchor="middle" x="2572.7" y="-445.8" font-family="Times,serif" font-size="14.00">v24 (frozen props$1 @ &lt;no&#45;instr&gt;) &lt;assign&gt;</text>
291 +<polygon fill="none" stroke="black" points="2697.84,-468 2431.56,-468 2431.56,-432 2697.84,-432 2697.84,-468"/>
292 +<text text-anchor="middle" x="2564.7" y="-445.8" font-family="Times,serif" font-size="14.00">v24 (freeze props$1 @ &lt;no&#45;instr&gt;) &lt;assign&gt;</text>
293 </g>
294 <!-- v24&#45;&gt;v4 -->
295 <g id="edge21" class="edge">
296 <title>v24&#45;&gt;v4</title>
297 -<path fill="none" stroke="black" d="M2572.7,-431.7C2572.7,-423.98 2572.7,-414.71 2572.7,-406.11"/>
298 -<polygon fill="black" stroke="black" points="2576.2,-406.1 2572.7,-396.1 2569.2,-406.1 2576.2,-406.1"/>
297 +<path fill="none" stroke="black" d="M2564.7,-431.7C2564.7,-423.98 2564.7,-414.71 2564.7,-406.11"/>
298 +<polygon fill="black" stroke="black" points="2568.2,-406.1 2564.7,-396.1 2561.2,-406.1 2568.2,-406.1"/>
299 </g>
300 </g>
301 </svg>
compiler/forget/src/__tests__/fixtures/hir/call.expect.md
+7 -7
@@ -34,15 +34,15 @@ function foo$0() {
34
35 ```
36 bb0:
37 - Const readonly a$2 = Array []
38 - Const readonly b$3 = Object { }
37 + Const mutable a$2 = Array []
38 + Const mutable b$3 = Object { }
39 Call mutable foo$4(mutable a$2, mutable b$3)
40 - Const readonly $6 = "div"
41 - Let readonly _$5 = JSX <frozen $6 a={frozen a$2} ></frozen $6>
40 + Const mutable $6 = "div"
41 + Let mutable _$5 = JSX <readonly $6 a={freeze a$2} ></readonly $6>
42 Call mutable foo$4(mutable b$3)
43 - Const readonly $7 = "div"
44 - Const readonly $8 = JSX <frozen $7 a={frozen a$2} b={frozen b$3} ></frozen $7>
45 - Return frozen $8
43 + Const mutable $7 = "div"
44 + Const mutable $8 = JSX <readonly $7 a={readonly a$2} b={freeze b$3} ></readonly $7>
45 + Return readonly $8
46 ```
47
48 ## Code
compiler/forget/src/__tests__/fixtures/hir/component.expect.md
+29 -29
@@ -35,45 +35,45 @@ function Component(props) {
35
36 ```
37 bb0:
38 - Const frozen items$2 = frozen props$1.items
39 - Const frozen maxItems$3 = frozen props$1.maxItems
40 - Const readonly renderedItems$4 = Array []
41 - Const readonly seen$5 = New mutable Set$6()
42 - Const readonly $9 = 0
43 - Const readonly max$7 = Call mutable Math$8.max(mutable $9, frozen maxItems$3)
38 + Const mutable items$2 = readonly props$1.items
39 + Const mutable maxItems$3 = readonly props$1.maxItems
40 + Const mutable renderedItems$4 = Array []
41 + Const mutable seen$5 = New mutable Set$6()
42 + Const mutable $9 = 0
43 + Const mutable max$7 = Call mutable Math$8.max(readonly $9, readonly maxItems$3)
44 Goto bb1
45 bb1:
46 - If (frozen items$2) then:bb3 else:bb2
46 + If (readonly items$2) then:bb3 else:bb2
47 bb3:
48 - Const readonly $11 = null
49 - Const frozen $12 = Binary frozen item$10 == readonly $11
50 - If (frozen $12) then:bb8 else:bb9
48 + Const mutable $11 = null
49 + Const mutable $12 = Binary readonly item$10 == readonly $11
50 + If (readonly $12) then:bb8 else:bb9
51 bb2:
52 - Const readonly count$17 = readonly renderedItems$4.length
53 - Const readonly $18 = "div"
54 - Const readonly $19 = "\n "
55 - Const readonly $20 = "h1"
56 - Const readonly $21 = " Items"
57 - Const readonly $22 = JSX <frozen $20>{frozen count$17}{frozen $21}</frozen $20>
58 - Const readonly $23 = "\n "
59 - Const readonly $24 = "\n "
60 - Const readonly $25 = JSX <frozen $18>{frozen $19}{frozen $22}{frozen $23}{frozen renderedItems$4}{frozen $24}</frozen $18>
61 - Return frozen $25
52 + Const mutable count$17 = readonly renderedItems$4.length
53 + Const mutable $18 = "div"
54 + Const mutable $19 = "\n "
55 + Const mutable $20 = "h1"
56 + Const mutable $21 = " Items"
57 + Const mutable $22 = JSX <readonly $20>{freeze count$17}{readonly $21}</readonly $20>
58 + Const mutable $23 = "\n "
59 + Const mutable $24 = "\n "
60 + Const mutable $25 = JSX <readonly $18>{readonly $19}{readonly $22}{readonly $23}{freeze renderedItems$4}{readonly $24}</readonly $18>
61 + Return readonly $25
62 bb8:
63 - Const frozen $13 = frozen $12
63 + Const mutable $13 = readonly $12
64 Goto bb7
65 bb9:
66 - Const readonly $13 = Call mutable seen$5.has(frozen item$10)
66 + Const mutable $13 = Call mutable seen$5.has(mutable item$10)
67 Goto bb7
68 bb7:
69 - If (frozen $13) then:bb1 else:bb4
69 + If (readonly $13) then:bb1 else:bb4
70 bb4:
71 - Call mutable seen$5.add(frozen item$10)
72 - Const readonly $14 = "div"
73 - Const readonly $15 = JSX <frozen $14>{frozen item$10}</frozen $14>
74 - Call mutable renderedItems$4.push(mutable $15)
75 - Const frozen $16 = Binary readonly renderedItems$4.length >= readonly max$7
76 - If (frozen $16) then:bb2 else:bb1
71 + Call mutable seen$5.add(mutable item$10)
72 + Const mutable $14 = "div"
73 + Const mutable $15 = JSX <readonly $14>{readonly item$10}</readonly $14>
74 + Call mutable renderedItems$4.push(readonly $15)
75 + Const mutable $16 = Binary readonly renderedItems$4.length >= readonly max$7
76 + If (readonly $16) then:bb2 else:bb1
77 ```
78
79 ## Code
compiler/forget/src/__tests__/fixtures/hir/conditional-break.expect.md
+34 -34
@@ -78,15 +78,15 @@ function Component(props) {
78
79 ```
80 bb0:
81 - Const readonly a_DEBUG$2 = Array []
82 - Call mutable a_DEBUG$2.push(frozen props$1.a)
83 - If (frozen props$1.b) then:bb2 else:bb1
81 + Const mutable a_DEBUG$2 = Array []
82 + Call mutable a_DEBUG$2.push(readonly props$1.a)
83 + If (readonly props$1.b) then:bb2 else:bb1
84 bb2:
85 - Const readonly $3 = null
86 - Return frozen $3
85 + Const mutable $3 = null
86 + Return readonly $3
87 bb1:
88 - Call mutable a_DEBUG$2.push(frozen props$1.d)
89 - Return frozen a_DEBUG$2
88 + Call mutable a_DEBUG$2.push(readonly props$1.d)
89 + Return freeze a_DEBUG$2
90 ```
91
92 ## Code
@@ -107,15 +107,15 @@ function Component$0(props$1) {
107
108 ```
109 bb0:
110 - Const readonly a$2 = Array []
111 - Call mutable a$2.push(frozen props$1.a)
112 - If (frozen props$1.b) then:bb2 else:bb1
110 + Const mutable a$2 = Array []
111 + Call mutable a$2.push(readonly props$1.a)
112 + If (readonly props$1.b) then:bb2 else:bb1
113 bb2:
114 - Call mutable a$2.push(frozen props$1.c)
114 + Call mutable a$2.push(readonly props$1.c)
115 Goto bb1
116 bb1:
117 - Call mutable a$2.push(frozen props$1.d)
118 - Return frozen a$2
117 + Call mutable a$2.push(readonly props$1.d)
118 + Return freeze a$2
119 ```
120
121 ## Code
@@ -137,16 +137,16 @@ function Component$0(props$1) {
137
138 ```
139 bb0:
140 - Const readonly a$2 = Array []
141 - Call mutable a$2.push(frozen props$1.a)
142 - If (frozen props$1.b) then:bb2 else:bb1
140 + Const mutable a$2 = Array []
141 + Call mutable a$2.push(readonly props$1.a)
142 + If (readonly props$1.b) then:bb2 else:bb1
143 bb2:
144 - Call mutable a$2.push(frozen props$1.c)
145 - Const readonly $3 = null
146 - Return frozen $3
144 + Call mutable a$2.push(readonly props$1.c)
145 + Const mutable $3 = null
146 + Return readonly $3
147 bb1:
148 - Call mutable a$2.push(frozen props$1.d)
149 - Return frozen a$2
148 + Call mutable a$2.push(readonly props$1.d)
149 + Return freeze a$2
150 ```
151
152 ## Code
@@ -168,15 +168,15 @@ function Component$0(props$1) {
168
169 ```
170 bb0:
171 - Const readonly a$2 = Array []
172 - Call mutable a$2.push(frozen props$1.a)
173 - If (frozen props$1.b) then:bb2 else:bb1
171 + Const mutable a$2 = Array []
172 + Call mutable a$2.push(readonly props$1.a)
173 + If (readonly props$1.b) then:bb2 else:bb1
174 bb2:
175 - Call mutable a$2.push(frozen props$1.c)
176 - Return frozen a$2
175 + Call mutable a$2.push(readonly props$1.c)
176 + Return freeze a$2
177 bb1:
178 - Call mutable a$2.push(frozen props$1.d)
179 - Return frozen a$2
178 + Call mutable a$2.push(readonly props$1.d)
179 + Return freeze a$2
180 ```
181
182 ## Code
@@ -198,14 +198,14 @@ function Component$0(props$1) {
198
199 ```
200 bb0:
201 - Const readonly a$2 = Array []
202 - Call mutable a$2.push(frozen props$1.a)
203 - If (frozen props$1.b) then:bb1 else:bb2
201 + Const mutable a$2 = Array []
202 + Call mutable a$2.push(readonly props$1.a)
203 + If (readonly props$1.b) then:bb1 else:bb2
204 bb1:
205 - Call mutable a$2.push(frozen props$1.d)
206 - Return frozen a$2
205 + Call mutable a$2.push(readonly props$1.d)
206 + Return freeze a$2
207 bb2:
208 - Call mutable a$2.push(frozen props$1.c)
208 + Call mutable a$2.push(readonly props$1.c)
209 Goto bb1
210 ```
211
compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md
+15 -15
@@ -67,20 +67,20 @@ function mayMutate() {}
67
68 ```
69 bb0:
70 - Const readonly a$2 = Array []
71 - Const readonly b$3 = Array []
70 + Const mutable a$2 = Array []
71 + Const mutable b$3 = Array []
72 If (readonly b$3) then:bb2 else:bb1
73 bb2:
74 - Call mutable a$2.push(frozen props$1.p0)
74 + Call mutable a$2.push(readonly props$1.p0)
75 Goto bb1
76 bb1:
77 - If (frozen props$1.p1) then:bb4 else:bb3
77 + If (readonly props$1.p1) then:bb4 else:bb3
78 bb4:
79 - Call mutable b$3.push(frozen props$1.p2)
79 + Call mutable b$3.push(readonly props$1.p2)
80 Goto bb3
81 bb3:
82 - Const readonly $5 = JSX <frozen Foo$4 a={frozen a$2} b={frozen b$3} ></frozen Foo$4>
83 - Return frozen $5
82 + Const mutable $5 = JSX <readonly Foo$4 a={freeze a$2} b={freeze b$3} ></readonly Foo$4>
83 + Return readonly $5
84 ```
85
86 ## Code
@@ -105,21 +105,21 @@ function Component$0(props$1) {
105
106 ```
107 bb0:
108 - Const readonly a$2 = Array []
109 - Const readonly b$3 = Array []
110 - Const readonly $5 = Call mutable mayMutate$4(mutable b$3)
108 + Const mutable a$2 = Array []
109 + Const mutable b$3 = Array []
110 + Const mutable $5 = Call mutable mayMutate$4(mutable b$3)
111 If (readonly $5) then:bb2 else:bb1
112 bb2:
113 - Call mutable a$2.push(frozen props$1.p0)
113 + Call mutable a$2.push(readonly props$1.p0)
114 Goto bb1
115 bb1:
116 - If (frozen props$1.p1) then:bb4 else:bb3
116 + If (readonly props$1.p1) then:bb4 else:bb3
117 bb4:
118 - Call mutable b$3.push(frozen props$1.p2)
118 + Call mutable b$3.push(readonly props$1.p2)
119 Goto bb3
120 bb3:
121 - Const readonly $7 = JSX <frozen Foo$6 a={frozen a$2} b={frozen b$3} ></frozen Foo$6>
122 - Return frozen $7
121 + Const mutable $7 = JSX <readonly Foo$6 a={freeze a$2} b={freeze b$3} ></readonly Foo$6>
122 + Return readonly $7
123 ```
124
125 ## Code
compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md
+7 -7
@@ -34,15 +34,15 @@ function Foo$0() {
34
35 ```
36 bb0:
37 - Const readonly a$2 = Array []
38 - Const readonly b$3 = Object { }
37 + Const mutable a$2 = Array []
38 + Const mutable b$3 = Object { }
39 New mutable Foo$4(mutable a$2, mutable b$3)
40 - Const readonly $6 = "div"
41 - Let readonly _$5 = JSX <frozen $6 a={frozen a$2} ></frozen $6>
40 + Const mutable $6 = "div"
41 + Let mutable _$5 = JSX <readonly $6 a={freeze a$2} ></readonly $6>
42 New mutable Foo$4(mutable b$3)
43 - Const readonly $7 = "div"
44 - Const readonly $8 = JSX <frozen $7 a={frozen a$2} b={frozen b$3} ></frozen $7>
45 - Return frozen $8
43 + Const mutable $7 = "div"
44 + Const mutable $8 = JSX <readonly $7 a={readonly a$2} b={freeze b$3} ></readonly $7>
45 + Return readonly $8
46 ```
47
48 ## Code
compiler/forget/src/__tests__/fixtures/hir/frozen-after-alias.expect.md new
+70
@@ -0,0 +1,70 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component() {
6 + const a = [];
7 + const b = a;
8 + useFreeze(a);
9 + foo(b); // should be readonly, value is guaranteed frozen via alias
10 +}
11 +
12 +function useFreeze() {}
13 +function foo(x) {}
14 +
15 +```
16 +
17 +## HIR
18 +
19 +```
20 +bb0:
21 + Const mutable a$1 = Array []
22 + Const mutable b$2 = readonly a$1
23 + Call readonly useFreeze$3(freeze a$1)
24 + Call mutable foo$4(readonly b$2)
25 + Return
26 +```
27 +
28 +## Code
29 +
30 +```javascript
31 +function Component$0() {
32 + const a$1 = [];
33 + const b$2 = a$1;
34 + useFreeze$3(a$1);
35 + foo$4(b$2);
36 + return;
37 +}
38 +
39 +```
40 +## HIR
41 +
42 +```
43 +bb0:
44 + Return
45 +```
46 +
47 +## Code
48 +
49 +```javascript
50 +function useFreeze$0() {
51 + return;
52 +}
53 +
54 +```
55 +## HIR
56 +
57 +```
58 +bb0:
59 + Return
60 +```
61 +
62 +## Code
63 +
64 +```javascript
65 +function foo$0(x$1) {
66 + return;
67 +}
68 +
69 +```
70 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/frozen-after-alias.js new
+9
@@ -0,0 +1,9 @@
1 +function Component() {
2 + const a = [];
3 + const b = a;
4 + useFreeze(a);
5 + foo(b); // should be readonly, value is guaranteed frozen via alias
6 +}
7 +
8 +function useFreeze() {}
9 +function foo(x) {}
compiler/forget/src/__tests__/fixtures/hir/hook-call.expect.md
+8 -8
@@ -53,14 +53,14 @@ function foo$0() {
53
54 ```
55 bb0:
56 - Const readonly x$2 = Array []
57 - Const frozen y$3 = Call frozen useFreeze$4(frozen x$2)
58 - Call mutable foo$5(frozen y$3, frozen x$2)
59 - Const readonly $6 = "\n "
60 - Const readonly $7 = "\n "
61 - Const readonly $8 = "\n "
62 - Const readonly $9 = JSX <frozen Component$0>{frozen $6}{frozen x$2}{frozen $7}{frozen y$3}{frozen $8}</frozen Component$0>
63 - Return frozen $9
56 + Const mutable x$2 = Array []
57 + Const mutable y$3 = Call readonly useFreeze$4(freeze x$2)
58 + Call mutable foo$5(readonly y$3, readonly x$2)
59 + Const mutable $6 = "\n "
60 + Const mutable $7 = "\n "
61 + Const mutable $8 = "\n "
62 + Const mutable $9 = JSX <readonly Component$0>{readonly $6}{readonly x$2}{readonly $7}{readonly y$3}{readonly $8}</readonly Component$0>
63 + Return readonly $9
64 ```
65
66 ## Code
compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-arguments.expect.md new
+71
@@ -0,0 +1,71 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component() {
6 + const a = [];
7 + useFreeze(a); // should freeze
8 + useFreeze(a); // should be readonly
9 + call(a); // should be readonly
10 + return a;
11 +}
12 +
13 +function useFreeze(x) {}
14 +function call(x) {}
15 +
16 +```
17 +
18 +## HIR
19 +
20 +```
21 +bb0:
22 + Const mutable a$1 = Array []
23 + Call readonly useFreeze$2(freeze a$1)
24 + Call readonly useFreeze$2(readonly a$1)
25 + Call mutable call$3(readonly a$1)
26 + Return readonly a$1
27 +```
28 +
29 +## Code
30 +
31 +```javascript
32 +function Component$0() {
33 + const a$1 = [];
34 + useFreeze$2(a$1);
35 + useFreeze$2(a$1);
36 + call$3(a$1);
37 + return a$1;
38 +}
39 +
40 +```
41 +## HIR
42 +
43 +```
44 +bb0:
45 + Return
46 +```
47 +
48 +## Code
49 +
50 +```javascript
51 +function useFreeze$0(x$1) {
52 + return;
53 +}
54 +
55 +```
56 +## HIR
57 +
58 +```
59 +bb0:
60 + Return
61 +```
62 +
63 +## Code
64 +
65 +```javascript
66 +function call$0(x$1) {
67 + return;
68 +}
69 +
70 +```
71 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-arguments.js new
+10
@@ -0,0 +1,10 @@
1 +function Component() {
2 + const a = [];
3 + useFreeze(a); // should freeze
4 + useFreeze(a); // should be readonly
5 + call(a); // should be readonly
6 + return a;
7 +}
8 +
9 +function useFreeze(x) {}
10 +function call(x) {}
compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-possibly-mutable-arguments.expect.md new
+97
@@ -0,0 +1,97 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + const cond = props.cond;
7 + const x = props.x;
8 + let a;
9 + if (cond) {
10 + a = x;
11 + } else {
12 + a = [];
13 + }
14 + useFreeze(a); // should freeze, value *may* be mutable
15 + useFreeze(a); // should be readonly
16 + call(a); // should be readonly
17 + return a;
18 +}
19 +
20 +function useFreeze(x) {}
21 +function call(x) {}
22 +
23 +```
24 +
25 +## HIR
26 +
27 +```
28 +bb0:
29 + Const mutable cond$2 = readonly props$1.cond
30 + Const mutable x$3 = readonly props$1.x
31 + Let mutable a$4 = undefined
32 + If (readonly cond$2) then:bb2 else:bb3
33 +bb2:
34 + Reassign mutable a$4 = readonly x$3
35 + Goto bb1
36 +bb3:
37 + Reassign mutable a$4 = Array []
38 + Goto bb1
39 +bb1:
40 + Call readonly useFreeze$5(freeze a$4)
41 + Call readonly useFreeze$5(readonly a$4)
42 + Call mutable call$6(readonly a$4)
43 + Return readonly a$4
44 +```
45 +
46 +## Code
47 +
48 +```javascript
49 +function Component$0(props$1) {
50 + const cond$2 = props$1.cond;
51 + const x$3 = props$1.x;
52 + let a$4 = undefined;
53 + if (cond$2) {
54 + a$4 = x$3;
55 + ("<<TODO: handle complex control flow in codegen>>");
56 + } else {
57 + a$4 = [];
58 + ("<<TODO: handle complex control flow in codegen>>");
59 + }
60 + useFreeze$5(a$4);
61 + useFreeze$5(a$4);
62 + call$6(a$4);
63 + return a$4;
64 +}
65 +
66 +```
67 +## HIR
68 +
69 +```
70 +bb0:
71 + Return
72 +```
73 +
74 +## Code
75 +
76 +```javascript
77 +function useFreeze$0(x$1) {
78 + return;
79 +}
80 +
81 +```
82 +## HIR
83 +
84 +```
85 +bb0:
86 + Return
87 +```
88 +
89 +## Code
90 +
91 +```javascript
92 +function call$0(x$1) {
93 + return;
94 +}
95 +
96 +```
97 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-possibly-mutable-arguments.js new
+17
@@ -0,0 +1,17 @@
1 +function Component(props) {
2 + const cond = props.cond;
3 + const x = props.x;
4 + let a;
5 + if (cond) {
6 + a = x;
7 + } else {
8 + a = [];
9 + }
10 + useFreeze(a); // should freeze, value *may* be mutable
11 + useFreeze(a); // should be readonly
12 + call(a); // should be readonly
13 + return a;
14 +}
15 +
16 +function useFreeze(x) {}
17 +function call(x) {}
compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md
+5 -5
@@ -97,16 +97,16 @@ function Foo$0() {
97
98 ```
99 bb0:
100 - Const readonly a$2 = Call mutable compute$3(frozen props$1.a)
101 - Const readonly b$4 = Call mutable compute$3(frozen props$1.b)
102 - If (frozen props$1.c) then:bb2 else:bb1
100 + Const mutable a$2 = Call mutable compute$3(readonly props$1.a)
101 + Const mutable b$4 = Call mutable compute$3(readonly props$1.b)
102 + If (readonly props$1.c) then:bb2 else:bb1
103 bb2:
104 Call mutable mutate$5(mutable a$2)
105 Call mutable mutate$5(mutable b$4)
106 Goto bb1
107 bb1:
108 - Const readonly $7 = JSX <frozen Foo$6 a={frozen a$2} b={frozen b$4} ></frozen Foo$6>
109 - Return frozen $7
108 + Const mutable $7 = JSX <readonly Foo$6 a={freeze a$2} b={freeze b$4} ></readonly Foo$6>
109 + Return readonly $7
110 ```
111
112 ## Code
compiler/forget/src/__tests__/fixtures/hir/independent.expect.md
+4 -4
@@ -28,10 +28,10 @@ function Foo() {}
28
29 ```
30 bb0:
31 - Const readonly a$2 = Call mutable compute$3(frozen props$1.a)
32 - Const readonly b$4 = Call mutable compute$3(frozen props$1.b)
33 - Const readonly $6 = JSX <frozen Foo$5 a={frozen a$2} b={frozen b$4} ></frozen Foo$5>
34 - Return frozen $6
31 + Const mutable a$2 = Call mutable compute$3(readonly props$1.a)
32 + Const mutable b$4 = Call mutable compute$3(readonly props$1.b)
33 + Const mutable $6 = JSX <readonly Foo$5 a={freeze a$2} b={freeze b$4} ></readonly Foo$5>
34 + Return readonly $6
35 ```
36
37 ## Code
compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md
+5 -5
@@ -76,15 +76,15 @@ function Foo$0() {
76
77 ```
78 bb0:
79 - Const readonly a$2 = Call mutable compute$3(frozen props$1.a)
80 - Const readonly b$4 = Call mutable compute$3(frozen props$1.b)
81 - If (frozen props$1.c) then:bb2 else:bb1
79 + Const mutable a$2 = Call mutable compute$3(readonly props$1.a)
80 + Const mutable b$4 = Call mutable compute$3(readonly props$1.b)
81 + If (readonly props$1.c) then:bb2 else:bb1
82 bb2:
83 Call mutable foo$5(mutable a$2, mutable b$4)
84 Goto bb1
85 bb1:
86 - Const readonly $7 = JSX <frozen Foo$6 a={frozen a$2} b={frozen b$4} ></frozen Foo$6>
87 - Return frozen $7
86 + Const mutable $7 = JSX <readonly Foo$6 a={freeze a$2} b={freeze b$4} ></readonly Foo$6>
87 + Return readonly $7
88 ```
89
90 ## Code
compiler/forget/src/__tests__/fixtures/hir/interdependent.expect.md
+4 -4
@@ -28,11 +28,11 @@ function Foo() {}
28
29 ```
30 bb0:
31 - Const readonly a$2 = Call mutable compute$3(frozen props$1.a)
32 - Const readonly b$4 = Call mutable compute$3(frozen props$1.b)
31 + Const mutable a$2 = Call mutable compute$3(readonly props$1.a)
32 + Const mutable b$4 = Call mutable compute$3(readonly props$1.b)
33 Call mutable foo$5(mutable a$2, mutable b$4)
34 - Const readonly $7 = JSX <frozen Foo$6 a={frozen a$2} b={frozen b$4} ></frozen Foo$6>
35 - Return frozen $7
34 + Const mutable $7 = JSX <readonly Foo$6 a={freeze a$2} b={freeze b$4} ></readonly Foo$6>
35 + Return readonly $7
36 ```
37
38 ## Code
compiler/forget/src/__tests__/fixtures/hir/property-assignment.expect.md
+7 -7
@@ -17,13 +17,13 @@ function Component(props) {
17
18 ```
19 bb0:
20 - Const readonly x$2 = Object { }
21 - Const readonly y$3 = Array []
22 - Reassign readonly x$2.y = readonly y$3
23 - Const readonly child$4 = JSX <frozen Component$0 data={frozen y$3} ></frozen Component$0>
24 - Call mutable x$2.y.push(frozen props$1.p0)
25 - Const readonly $5 = JSX <frozen Component$0 data={frozen x$2} >{frozen child$4}</frozen Component$0>
26 - Return frozen $5
20 + Const mutable x$2 = Object { }
21 + Const mutable y$3 = Array []
22 + Reassign mutable x$2.y = readonly y$3
23 + Const mutable child$4 = JSX <readonly Component$0 data={freeze y$3} ></readonly Component$0>
24 + Call mutable x$2.y.push(readonly props$1.p0)
25 + Const mutable $5 = JSX <readonly Component$0 data={freeze x$2} >{readonly child$4}</readonly Component$0>
26 + Return readonly $5
27 ```
28
29 ## Code
compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md
+9 -9
@@ -23,18 +23,18 @@ function Component(props) {
23
24 ```
25 bb0:
26 - Let readonly x$2 = Array []
27 - Call mutable x$2.push(frozen props$1.p0)
28 - Let readonly y$3 = readonly x$2
29 - If (frozen props$1.p1) then:bb2 else:bb1
26 + Let mutable x$2 = Array []
27 + Call mutable x$2.push(readonly props$1.p0)
28 + Let mutable y$3 = readonly x$2
29 + If (readonly props$1.p1) then:bb2 else:bb1
30 bb2:
31 - Reassign readonly x$2 = Array []
31 + Reassign mutable x$2 = Array []
32 Goto bb1
33 bb1:
34 - Let readonly _$4 = JSX <frozen Component$0 x={frozen x$2} ></frozen Component$0>
35 - Call mutable y$3.push(frozen props$1.p2)
36 - Const readonly $5 = JSX <frozen Component$0 x={frozen x$2} y={frozen y$3} ></frozen Component$0>
37 - Return frozen $5
34 + Let mutable _$4 = JSX <readonly Component$0 x={freeze x$2} ></readonly Component$0>
35 + Call readonly y$3.push(readonly props$1.p2)
36 + Const mutable $5 = JSX <readonly Component$0 x={readonly x$2} y={readonly y$3} ></readonly Component$0>
37 + Return readonly $5
38 ```
39
40 ## Code
compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md
+8 -8
@@ -21,14 +21,14 @@ function Component(props) {
21
22 ```
23 bb0:
24 - Let readonly x$2 = Array []
25 - Call mutable x$2.push(frozen props$1.p0)
26 - Let readonly y$3 = readonly x$2
27 - Reassign readonly x$2 = Array []
28 - Let readonly _$4 = JSX <frozen Component$0 x={frozen x$2} ></frozen Component$0>
29 - Call mutable y$3.push(frozen props$1.p1)
30 - Const readonly $5 = JSX <frozen Component$0 x={frozen x$2} y={frozen y$3} ></frozen Component$0>
31 - Return frozen $5
24 + Let mutable x$2 = Array []
25 + Call mutable x$2.push(readonly props$1.p0)
26 + Let mutable y$3 = readonly x$2
27 + Reassign mutable x$2 = Array []
28 + Let mutable _$4 = JSX <readonly Component$0 x={freeze x$2} ></readonly Component$0>
29 + Call mutable y$3.push(readonly props$1.p1)
30 + Const mutable $5 = JSX <readonly Component$0 x={readonly x$2} y={freeze y$3} ></readonly Component$0>
31 + Return readonly $5
32 ```
33
34 ## Code
compiler/forget/src/__tests__/fixtures/hir/simple.expect.md
+8 -8
@@ -15,16 +15,16 @@ function foo(x, y) {
15
16 ```
17 bb0:
18 - If (frozen x$1) then:bb2 else:bb1
18 + If (readonly x$1) then:bb2 else:bb1
19 bb2:
20 - Const readonly $3 = false
21 - Const readonly $4 = Call mutable foo$0(mutable $3, frozen y$2)
22 - Return frozen $4
20 + Const mutable $3 = false
21 + Const mutable $4 = Call readonly foo$0(readonly $3, readonly y$2)
22 + Return freeze $4
23 bb1:
24 - Const readonly $5 = 10
25 - Const frozen $6 = Binary frozen y$2 * readonly $5
26 - Const readonly $7 = Array [frozen $6]
27 - Return frozen $7
24 + Const mutable $5 = 10
25 + Const mutable $6 = Binary readonly y$2 * readonly $5
26 + Const mutable $7 = Array [readonly $6]
27 + Return freeze $7
28 ```
29
30 ## Code
compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md
+13 -13
@@ -32,27 +32,27 @@ function Component(props) {
32
33 ```
34 bb0:
35 - Let readonly x$2 = Array []
36 - Let readonly y$3 = undefined
37 - Const readonly $4 = false
38 - Const readonly $5 = true
39 - Const readonly $6 = 1
40 - Switch (frozen props$1.p0)
35 + Let mutable x$2 = Array []
36 + Let mutable y$3 = undefined
37 + Const mutable $4 = false
38 + Const mutable $5 = true
39 + Const mutable $6 = 1
40 + Switch (<unknown> props$1.p0)
41 Case readonly $6: bb1
42 Case readonly $5: bb6
43 Default: bb1
44 Case readonly $4: bb2
45 bb1:
46 - Const readonly child$7 = JSX <frozen Component$0 data={frozen x$2} ></frozen Component$0>
47 - Call mutable y$3.push(frozen props$1.p4)
48 - Const readonly $8 = JSX <frozen Component$0 data={frozen y$3} >{frozen child$7}</frozen Component$0>
49 - Return frozen $8
46 + Const mutable child$7 = JSX <readonly Component$0 data={freeze x$2} ></readonly Component$0>
47 + Call readonly y$3.push(readonly props$1.p4)
48 + Const mutable $8 = JSX <readonly Component$0 data={freeze y$3} >{readonly child$7}</readonly Component$0>
49 + Return readonly $8
50 bb6:
51 - Call mutable x$2.push(frozen props$1.p2)
52 - Reassign readonly y$3 = Array []
51 + Call mutable x$2.push(readonly props$1.p2)
52 + Reassign mutable y$3 = Array []
53 Goto bb1
54 bb2:
55 - Reassign readonly y$3 = readonly x$2
55 + Reassign mutable y$3 = readonly x$2
56 Goto bb1
57 ```
58
compiler/forget/src/__tests__/fixtures/hir/switch.expect.md
+13 -13
@@ -28,27 +28,27 @@ function Component(props) {
28
29 ```
30 bb0:
31 - Let readonly x$2 = Array []
32 - Let readonly y$3 = undefined
33 - Const readonly $4 = false
34 - Const readonly $5 = true
35 - Switch (frozen props$1.p0)
31 + Let mutable x$2 = Array []
32 + Let mutable y$3 = undefined
33 + Const mutable $4 = false
34 + Const mutable $5 = true
35 + Switch (<unknown> props$1.p0)
36 Case readonly $5: bb4
37 Case readonly $4: bb2
38 Default: bb1
39 bb4:
40 - Call mutable x$2.push(frozen props$1.p2)
41 - Call mutable x$2.push(frozen props$1.p3)
42 - Reassign readonly y$3 = Array []
40 + Call mutable x$2.push(readonly props$1.p2)
41 + Call mutable x$2.push(readonly props$1.p3)
42 + Reassign mutable y$3 = Array []
43 Goto bb2
44 bb2:
45 - Reassign readonly y$3 = readonly x$2
45 + Reassign mutable y$3 = readonly x$2
46 Goto bb1
47 bb1:
48 - Const readonly child$6 = JSX <frozen Component$0 data={frozen x$2} ></frozen Component$0>
49 - Call mutable y$3.push(frozen props$1.p4)
50 - Const readonly $7 = JSX <frozen Component$0 data={frozen y$3} >{frozen child$6}</frozen Component$0>
51 - Return frozen $7
48 + Const mutable child$6 = JSX <readonly Component$0 data={freeze x$2} ></readonly Component$0>
49 + Call readonly y$3.push(readonly props$1.p4)
50 + Const mutable $7 = JSX <readonly Component$0 data={readonly y$3} >{readonly child$6}</readonly Component$0>
51 + Return readonly $7
52 ```
53
54 ## Code
compiler/forget/src/__tests__/hir-test.ts
+18 -18
@@ -10,15 +10,14 @@
10 import generate from "@babel/generator";
11 import * as parser from "@babel/parser";
12 import traverse from "@babel/traverse";
13 -import { graphviz, wasmFolder } from "@hpcc-js/wasm";
14 -import { existsSync, unlinkSync, writeFileSync } from "fs";
13 +import { wasmFolder } from "@hpcc-js/wasm";
14 import invariant from "invariant";
15 import path from "path";
16 import prettier from "prettier";
18 -import buildDefUseGraph, { printGraph } from "../HIR/BuildDefUseGraph";
17 import { lower } from "../HIR/BuildHIR";
18 import codegen from "../HIR/Codegen";
19 import { HIRFunction } from "../HIR/HIR";
20 +import inferReferenceCapability from "../HIR/InferReferenceCapability";
21 import printHIR from "../HIR/PrintHIR";
22 import generateTestsFromFixtures from "./test-utils/generateTestsFromFixtures";
23
@@ -45,22 +44,23 @@ describe("React Forget (HIR version)", () => {
44 FunctionDeclaration: {
45 enter(nodePath) {
46 const ir: HIRFunction = lower(nodePath);
48 - const lifetimeGraph = buildDefUseGraph(ir);
47 + inferReferenceCapability(ir);
48 + // const lifetimeGraph = buildDefUseGraph(ir);
49 const textHIR = printHIR(ir.body);
50 - const textLifetimeGraph = printGraph(lifetimeGraph);
51 - const graphvizFile = path.join(
52 - __dirname,
53 - "fixtures",
54 - "hir-svg",
55 - file + ".svg"
56 - );
57 - if (input.indexOf("@Out DefUseGraph") !== -1) {
58 - graphviz.layout(textLifetimeGraph, "svg", "dot").then((svg) => {
59 - writeFileSync(graphvizFile, svg);
60 - });
61 - } else if (existsSync(graphvizFile)) {
62 - unlinkSync(graphvizFile);
63 - }
50 + // const textLifetimeGraph = printGraph(lifetimeGraph);
51 + // const graphvizFile = path.join(
52 + // __dirname,
53 + // "fixtures",
54 + // "hir-svg",
55 + // file + ".svg"
56 + // );
57 + // if (input.indexOf("@Out DefUseGraph") !== -1) {
58 + // graphviz.layout(textLifetimeGraph, "svg", "dot").then((svg) => {
59 + // writeFileSync(graphvizFile, svg);
60 + // });
61 + // } else if (existsSync(graphvizFile)) {
62 + // unlinkSync(graphvizFile);
63 + // }
64
65 const ast = codegen(ir);
66 const text = prettier.format(
compiler/forget/yarn.lock
+27 -17
@@ -63,7 +63,7 @@
63 json5 "^2.2.1"
64 semver "^6.3.0"
65
66 -"@babel/generator@7.2.0", "@babel/generator@^7.1.6", "@babel/generator@^7.19.3", "@babel/generator@^7.2.0", "@babel/generator@^7.7.2":
66 +"@babel/generator@7.2.0", "@babel/generator@^7.1.6", "@babel/generator@^7.19.4", "@babel/generator@^7.2.0", "@babel/generator@^7.7.2":
67 version "7.2.0"
68 resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.2.0.tgz#eaf3821fa0301d9d4aef88e63d4bcc19b73ba16c"
69 integrity sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg==
@@ -75,11 +75,11 @@
75 trim-right "^1.0.1"
76
77 "@babel/generator@^7.19.0":
78 - version "7.19.3"
79 - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.3.tgz#d7f4d1300485b4547cb6f94b27d10d237b42bf59"
80 - integrity sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==
78 + version "7.19.5"
79 + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.19.5.tgz#da3f4b301c8086717eee9cab14da91b1fa5dcca7"
80 + integrity sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==
81 dependencies:
82 - "@babel/types" "^7.19.3"
82 + "@babel/types" "^7.19.4"
83 "@jridgewell/gen-mapping" "^0.3.2"
84 jsesc "^2.5.1"
85
@@ -203,6 +203,11 @@
203 resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56"
204 integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==
205
206 +"@babel/helper-string-parser@^7.19.4":
207 + version "7.19.4"
208 + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
209 + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
210 +
211 "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1":
212 version "7.19.1"
213 resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
@@ -241,11 +246,16 @@
246 resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.1.tgz#6f6d6c2e621aad19a92544cc217ed13f1aac5b4c"
247 integrity sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==
248
244 -"@babel/parser@^7.18.6", "@babel/parser@^7.19.3":
249 +"@babel/parser@^7.18.6":
250 version "7.19.3"
251 resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a"
252 integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==
253
254 +"@babel/parser@^7.19.4":
255 + version "7.19.4"
256 + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.4.tgz#03c4339d2b8971eb3beca5252bafd9b9f79db3dc"
257 + integrity sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==
258 +
259 "@babel/plugin-syntax-async-generators@^7.8.4":
260 version "7.8.4"
261 resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
@@ -465,18 +475,18 @@
475 lodash "^4.17.10"
476
477 "@babel/traverse@^7.19.1":
468 - version "7.19.3"
469 - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.3.tgz#3a3c5348d4988ba60884e8494b0592b2f15a04b4"
470 - integrity sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==
478 + version "7.19.4"
479 + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.19.4.tgz#f117820e18b1e59448a6c1fa9d0ff08f7ac459a8"
480 + integrity sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==
481 dependencies:
482 "@babel/code-frame" "^7.18.6"
473 - "@babel/generator" "^7.19.3"
483 + "@babel/generator" "^7.19.4"
484 "@babel/helper-environment-visitor" "^7.18.9"
485 "@babel/helper-function-name" "^7.19.0"
486 "@babel/helper-hoist-variables" "^7.18.6"
487 "@babel/helper-split-export-declaration" "^7.18.6"
478 - "@babel/parser" "^7.19.3"
479 - "@babel/types" "^7.19.3"
488 + "@babel/parser" "^7.19.4"
489 + "@babel/types" "^7.19.4"
490 debug "^4.1.0"
491 globals "^11.1.0"
492
@@ -506,12 +516,12 @@
516 "@babel/helper-validator-identifier" "^7.18.6"
517 to-fast-properties "^2.0.0"
518
509 -"@babel/types@^7.19.3":
510 - version "7.19.3"
511 - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.3.tgz#fc420e6bbe54880bce6779ffaf315f5e43ec9624"
512 - integrity sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==
519 +"@babel/types@^7.19.4":
520 + version "7.19.4"
521 + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.4.tgz#0dd5c91c573a202d600490a35b33246fed8a41c7"
522 + integrity sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==
523 dependencies:
514 - "@babel/helper-string-parser" "^7.18.10"
524 + "@babel/helper-string-parser" "^7.19.4"
525 "@babel/helper-validator-identifier" "^7.19.1"
526 to-fast-properties "^2.0.0"
527