Use strict equality (===) to prevent type coercion bugs
if (loc == null || typeof loc === "symbol") {
1/**2 * Copyright (c) Meta Platforms, Inc. and affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 */78/**9 * Debug ReactiveFunction printer for the Rust port testing infrastructure.10 *11 * Custom printer that walks the ReactiveFunction tree structure and prints12 * every field of every scope, instruction, terminal, and reactive value node.13 *14 * This does NOT delegate to printReactiveFunctionWithOutlined() — it is a15 * standalone walker that produces a detailed, deterministic representation16 * suitable for cross-compiler comparison between the TS and Rust implementations.17 *18 * @param {Function} _printReactiveFunctionWithOutlined - Unused (kept for API compat)19 * @param {object} reactiveFunction - The ReactiveFunction to print20 * @returns {string} The debug representation21 */22export function debugPrintReactive(23 _printReactiveFunctionWithOutlined,24 reactiveFunction25) {26 const outlined = [];27 const result = printReactiveFunction(reactiveFunction, 0, outlined);28 const parts = [result];29 for (let i = 0; i < outlined.length; i++) {30 parts.push(printReactiveFunction(outlined[i], i + 1, outlined));31 }32 return parts.join("\n\n");33}3435// ---------------------------------------------------------------------------36// Helpers37// ---------------------------------------------------------------------------3839function ind(depth) {40 return " ".repeat(depth);41}4243function formatLoc(loc) {44 if (loc == null || typeof loc === "symbol") {45 return "generated";46 }47 return `${loc.start.line}:${loc.start.column}-${loc.end.line}:${loc.end.column}`;48}4950function formatEffect(effect) {51 return String(effect);52}5354function formatType(type) {55 if (type == null) return "Type";56 switch (type.kind) {57 case "Type":58 return "Type";59 case "Primitive":60 return "Primitive";61 case "Object":62 return type.shapeId != null ? `Object<${type.shapeId}>` : "Object";63 case "Function": {64 const ret = formatType(type.return);65 const base =66 type.shapeId != null ? `Function<${type.shapeId}>` : "Function";67 return ret !== "Type" ? `${base}():${ret}` : base;68 }69 case "Poly":70 return "Poly";71 case "Phi": {72 const ops = type.operands.map(formatType).join(", ");73 return `Phi(${ops})`;74 }75 case "Property": {76 const objType = formatType(type.objectType);77 return `Property(${objType}.${type.objectName})`;78 }79 case "ObjectMethod":80 return "ObjectMethod";81 default:82 return "Type";83 }84}8586function formatIdentifierName(name) {87 if (name == null) return "null";88 if (typeof name === "object" && name.value != null) {89 return JSON.stringify(name.value);90 }91 return JSON.stringify(String(name));92}9394function formatMutableRange(range) {95 if (range == null) return "[0:0]";96 return `[${range.start}:${range.end}]`;97}9899function formatScopeId(scope) {100 if (scope == null) return "null";101 return `@${scope.id}`;102}103104function formatDeclarationId(id) {105 if (id == null) return "null";106 return String(id);107}108109// ---------------------------------------------------------------------------110// Place printing111// ---------------------------------------------------------------------------112113function printPlaceInline(place, depth) {114 const id = place.identifier;115 return [116 `${ind(depth)}Place {`,117 `${ind(depth + 1)}identifier: $${id.id}`,118 `${ind(depth + 1)}effect: ${formatEffect(place.effect)}`,119 `${ind(depth + 1)}reactive: ${place.reactive}`,120 `${ind(depth + 1)}loc: ${formatLoc(place.loc)}`,121 `${ind(depth)}}`,122 ].join("\n");123}124125// ---------------------------------------------------------------------------126// Object property key127// ---------------------------------------------------------------------------128129function printObjectPropertyKey(key) {130 switch (key.kind) {131 case "identifier":132 return key.name;133 case "string":134 return `"${key.name}"`;135 case "computed":136 return `[$${key.name.identifier.id}]`;137 case "number":138 return String(key.name);139 default:140 return String(key.name ?? key.kind);141 }142}143144function printPlaceOrSpread(ps) {145 if (ps.kind === "Identifier") return `$${ps.identifier.id}`;146 if (ps.kind === "Spread") return `...$${ps.place.identifier.id}`;147 return "<hole>";148}149150function printPattern(pattern) {151 switch (pattern.kind) {152 case "ArrayPattern":153 return `[${pattern.items.map((item) => (item.kind === "Hole" ? "<hole>" : item.kind === "Spread" ? `...$${item.place.identifier.id}` : `$${item.identifier.id}`)).join(", ")}]`;154 case "ObjectPattern":155 return `{${pattern.properties.map((p) => p.kind === "Spread" ? `...$${p.place.identifier.id}` : `${printObjectPropertyKey(p.key)}: $${p.place.identifier.id}`).join(", ")}}`;156 default:157 return String(pattern);158 }159}160161// ---------------------------------------------------------------------------162// InstructionValue printing (shared with HIR printer)163// ---------------------------------------------------------------------------164165function printInstructionValueFields(value, depth) {166 const d = depth;167 const lines = [];168 const kind = value.kind;169170 switch (kind) {171 case "LoadLocal":172 lines.push(`${ind(d)}LoadLocal {`);173 lines.push(`${ind(d + 1)}place: $${value.place.identifier.id}`);174 lines.push(`${ind(d)}}`);175 break;176 case "LoadContext":177 lines.push(`${ind(d)}LoadContext {`);178 lines.push(`${ind(d + 1)}place: $${value.place.identifier.id}`);179 lines.push(`${ind(d)}}`);180 break;181 case "DeclareLocal":182 lines.push(`${ind(d)}DeclareLocal {`);183 lines.push(`${ind(d + 1)}lvalue.kind: ${value.lvalue.kind}`);184 lines.push(185 `${ind(d + 1)}lvalue.place: $${value.lvalue.place.identifier.id}`186 );187 lines.push(`${ind(d)}}`);188 break;189 case "DeclareContext":190 lines.push(`${ind(d)}DeclareContext {`);191 lines.push(`${ind(d + 1)}lvalue.kind: ${value.lvalue.kind}`);192 lines.push(193 `${ind(d + 1)}lvalue.place: $${value.lvalue.place.identifier.id}`194 );195 lines.push(`${ind(d)}}`);196 break;197 case "StoreLocal":198 lines.push(`${ind(d)}StoreLocal {`);199 lines.push(`${ind(d + 1)}lvalue.kind: ${value.lvalue.kind}`);200 lines.push(201 `${ind(d + 1)}lvalue.place: $${value.lvalue.place.identifier.id}`202 );203 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);204 lines.push(`${ind(d)}}`);205 break;206 case "StoreContext":207 lines.push(`${ind(d)}StoreContext {`);208 lines.push(`${ind(d + 1)}lvalue.kind: ${value.lvalue.kind}`);209 lines.push(210 `${ind(d + 1)}lvalue.place: $${value.lvalue.place.identifier.id}`211 );212 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);213 lines.push(`${ind(d)}}`);214 break;215 case "Destructure":216 lines.push(`${ind(d)}Destructure {`);217 lines.push(`${ind(d + 1)}lvalue.kind: ${value.lvalue.kind}`);218 lines.push(219 `${ind(d + 1)}lvalue.pattern: ${printPattern(value.lvalue.pattern)}`220 );221 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);222 lines.push(`${ind(d)}}`);223 break;224 case "Primitive":225 lines.push(`${ind(d)}Primitive {`);226 lines.push(227 `${ind(d + 1)}value: ${value.value === undefined ? "undefined" : JSON.stringify(value.value)}`228 );229 lines.push(`${ind(d)}}`);230 break;231 case "JSXText":232 lines.push(`${ind(d)}JSXText {`);233 lines.push(`${ind(d + 1)}value: ${JSON.stringify(value.value)}`);234 lines.push(`${ind(d)}}`);235 break;236 case "BinaryExpression":237 lines.push(`${ind(d)}BinaryExpression {`);238 lines.push(`${ind(d + 1)}operator: ${value.operator}`);239 lines.push(`${ind(d + 1)}left: $${value.left.identifier.id}`);240 lines.push(`${ind(d + 1)}right: $${value.right.identifier.id}`);241 lines.push(`${ind(d)}}`);242 break;243 case "UnaryExpression":244 lines.push(`${ind(d)}UnaryExpression {`);245 lines.push(`${ind(d + 1)}operator: ${value.operator}`);246 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);247 lines.push(`${ind(d)}}`);248 break;249 case "CallExpression":250 lines.push(`${ind(d)}CallExpression {`);251 lines.push(`${ind(d + 1)}callee: $${value.callee.identifier.id}`);252 lines.push(253 `${ind(d + 1)}args: [${value.args.map(printPlaceOrSpread).join(", ")}]`254 );255 lines.push(`${ind(d)}}`);256 break;257 case "MethodCall":258 lines.push(`${ind(d)}MethodCall {`);259 lines.push(`${ind(d + 1)}receiver: $${value.receiver.identifier.id}`);260 lines.push(`${ind(d + 1)}property: $${value.property.identifier.id}`);261 lines.push(262 `${ind(d + 1)}args: [${value.args.map(printPlaceOrSpread).join(", ")}]`263 );264 lines.push(`${ind(d)}}`);265 break;266 case "NewExpression":267 lines.push(`${ind(d)}NewExpression {`);268 lines.push(`${ind(d + 1)}callee: $${value.callee.identifier.id}`);269 lines.push(270 `${ind(d + 1)}args: [${value.args.map(printPlaceOrSpread).join(", ")}]`271 );272 lines.push(`${ind(d)}}`);273 break;274 case "ObjectExpression":275 lines.push(`${ind(d)}ObjectExpression {`);276 if (value.properties != null) {277 lines.push(`${ind(d + 1)}properties:`);278 for (const prop of value.properties) {279 if (prop.kind === "ObjectProperty") {280 lines.push(281 `${ind(d + 2)}${printObjectPropertyKey(prop.key)}: $${prop.place.identifier.id}`282 );283 } else {284 lines.push(`${ind(d + 2)}...$${prop.place.identifier.id}`);285 }286 }287 } else {288 lines.push(`${ind(d + 1)}properties: null`);289 }290 lines.push(`${ind(d)}}`);291 break;292 case "ArrayExpression":293 lines.push(`${ind(d)}ArrayExpression {`);294 lines.push(295 `${ind(d + 1)}elements: [${value.elements.map((e) => (e.kind === "Hole" ? "<hole>" : e.kind === "Spread" ? `...$${e.place.identifier.id}` : `$${e.identifier.id}`)).join(", ")}]`296 );297 lines.push(`${ind(d)}}`);298 break;299 case "PropertyLoad":300 lines.push(`${ind(d)}PropertyLoad {`);301 lines.push(`${ind(d + 1)}object: $${value.object.identifier.id}`);302 lines.push(`${ind(d + 1)}property: ${value.property}`);303 lines.push(`${ind(d)}}`);304 break;305 case "PropertyStore":306 lines.push(`${ind(d)}PropertyStore {`);307 lines.push(`${ind(d + 1)}object: $${value.object.identifier.id}`);308 lines.push(`${ind(d + 1)}property: ${value.property}`);309 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);310 lines.push(`${ind(d)}}`);311 break;312 case "PropertyDelete":313 lines.push(`${ind(d)}PropertyDelete {`);314 lines.push(`${ind(d + 1)}object: $${value.object.identifier.id}`);315 lines.push(`${ind(d + 1)}property: ${value.property}`);316 lines.push(`${ind(d)}}`);317 break;318 case "ComputedLoad":319 lines.push(`${ind(d)}ComputedLoad {`);320 lines.push(`${ind(d + 1)}object: $${value.object.identifier.id}`);321 lines.push(`${ind(d + 1)}property: $${value.property.identifier.id}`);322 lines.push(`${ind(d)}}`);323 break;324 case "ComputedStore":325 lines.push(`${ind(d)}ComputedStore {`);326 lines.push(`${ind(d + 1)}object: $${value.object.identifier.id}`);327 lines.push(`${ind(d + 1)}property: $${value.property.identifier.id}`);328 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);329 lines.push(`${ind(d)}}`);330 break;331 case "ComputedDelete":332 lines.push(`${ind(d)}ComputedDelete {`);333 lines.push(`${ind(d + 1)}object: $${value.object.identifier.id}`);334 lines.push(`${ind(d + 1)}property: $${value.property.identifier.id}`);335 lines.push(`${ind(d)}}`);336 break;337 case "LoadGlobal": {338 lines.push(`${ind(d)}LoadGlobal {`);339 const b = value.binding;340 lines.push(`${ind(d + 1)}binding.kind: ${b.kind}`);341 lines.push(`${ind(d + 1)}binding.name: ${b.name}`);342 if (b.module != null) {343 lines.push(`${ind(d + 1)}binding.module: ${b.module}`);344 }345 if (b.imported != null) {346 lines.push(`${ind(d + 1)}binding.imported: ${b.imported}`);347 }348 lines.push(`${ind(d)}}`);349 break;350 }351 case "StoreGlobal":352 lines.push(`${ind(d)}StoreGlobal {`);353 lines.push(`${ind(d + 1)}name: ${value.name}`);354 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);355 lines.push(`${ind(d)}}`);356 break;357 case "TypeCastExpression":358 lines.push(`${ind(d)}TypeCastExpression {`);359 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);360 lines.push(`${ind(d + 1)}type: ${formatType(value.type)}`);361 lines.push(`${ind(d)}}`);362 break;363 case "JsxExpression": {364 lines.push(`${ind(d)}JsxExpression {`);365 if (value.tag.kind === "Identifier") {366 lines.push(`${ind(d + 1)}tag: $${value.tag.identifier.id}`);367 } else {368 lines.push(`${ind(d + 1)}tag: "${value.tag.name}"`);369 }370 lines.push(`${ind(d + 1)}props:`);371 for (const attr of value.props) {372 if (attr.kind === "JsxAttribute") {373 lines.push(374 `${ind(d + 2)}${attr.name}: $${attr.place.identifier.id}`375 );376 } else {377 lines.push(`${ind(d + 2)}...$${attr.argument.identifier.id}`);378 }379 }380 if (value.children != null) {381 lines.push(382 `${ind(d + 1)}children: [${value.children.map((c) => `$${c.identifier.id}`).join(", ")}]`383 );384 } else {385 lines.push(`${ind(d + 1)}children: null`);386 }387 lines.push(`${ind(d)}}`);388 break;389 }390 case "JsxFragment":391 lines.push(`${ind(d)}JsxFragment {`);392 lines.push(393 `${ind(d + 1)}children: [${value.children.map((c) => `$${c.identifier.id}`).join(", ")}]`394 );395 lines.push(`${ind(d)}}`);396 break;397 case "FunctionExpression":398 case "ObjectMethod": {399 const label =400 kind === "FunctionExpression" ? "FunctionExpression" : "ObjectMethod";401 lines.push(`${ind(d)}${label} {`);402 if (kind === "FunctionExpression") {403 lines.push(404 `${ind(d + 1)}name: ${value.name != null ? JSON.stringify(value.name) : "null"}`405 );406 }407 lines.push(408 `${ind(d + 1)}loweredFunc.id: ${value.loweredFunc.func.id ?? "null"}`409 );410 const ctx = value.loweredFunc.func.context;411 lines.push(412 `${ind(d + 1)}context: [${ctx.map((c) => `$${c.identifier.id}`).join(", ")}]`413 );414 const ae = value.loweredFunc.func.aliasingEffects;415 lines.push(416 `${ind(d + 1)}aliasingEffects: ${ae != null ? `[${ae.length} effects]` : "null"}`417 );418 lines.push(`${ind(d)}}`);419 break;420 }421 case "TaggedTemplateExpression":422 lines.push(`${ind(d)}TaggedTemplateExpression {`);423 lines.push(`${ind(d + 1)}tag: $${value.tag.identifier.id}`);424 lines.push(425 `${ind(d + 1)}value.raw: ${JSON.stringify(value.value.raw)}`426 );427 lines.push(`${ind(d)}}`);428 break;429 case "TemplateLiteral":430 lines.push(`${ind(d)}TemplateLiteral {`);431 lines.push(432 `${ind(d + 1)}quasis: [${value.quasis.map((q) => JSON.stringify(q.raw)).join(", ")}]`433 );434 lines.push(435 `${ind(d + 1)}subexprs: [${value.subexprs.map((s) => `$${s.identifier.id}`).join(", ")}]`436 );437 lines.push(`${ind(d)}}`);438 break;439 case "RegExpLiteral":440 lines.push(`${ind(d)}RegExpLiteral {`);441 lines.push(`${ind(d + 1)}pattern: ${value.pattern}`);442 lines.push(`${ind(d + 1)}flags: ${value.flags}`);443 lines.push(`${ind(d)}}`);444 break;445 case "MetaProperty":446 lines.push(`${ind(d)}MetaProperty {`);447 lines.push(`${ind(d + 1)}meta: ${value.meta}`);448 lines.push(`${ind(d + 1)}property: ${value.property}`);449 lines.push(`${ind(d)}}`);450 break;451 case "Await":452 lines.push(`${ind(d)}Await {`);453 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);454 lines.push(`${ind(d)}}`);455 break;456 case "GetIterator":457 lines.push(`${ind(d)}GetIterator {`);458 lines.push(459 `${ind(d + 1)}collection: $${value.collection.identifier.id}`460 );461 lines.push(`${ind(d)}}`);462 break;463 case "IteratorNext":464 lines.push(`${ind(d)}IteratorNext {`);465 lines.push(`${ind(d + 1)}iterator: $${value.iterator.identifier.id}`);466 lines.push(467 `${ind(d + 1)}collection: $${value.collection.identifier.id}`468 );469 lines.push(`${ind(d)}}`);470 break;471 case "NextPropertyOf":472 lines.push(`${ind(d)}NextPropertyOf {`);473 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);474 lines.push(`${ind(d)}}`);475 break;476 case "PostfixUpdate":477 lines.push(`${ind(d)}PostfixUpdate {`);478 lines.push(`${ind(d + 1)}lvalue: $${value.lvalue.identifier.id}`);479 lines.push(`${ind(d + 1)}operation: ${value.operation}`);480 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);481 lines.push(`${ind(d)}}`);482 break;483 case "PrefixUpdate":484 lines.push(`${ind(d)}PrefixUpdate {`);485 lines.push(`${ind(d + 1)}lvalue: $${value.lvalue.identifier.id}`);486 lines.push(`${ind(d + 1)}operation: ${value.operation}`);487 lines.push(`${ind(d + 1)}value: $${value.value.identifier.id}`);488 lines.push(`${ind(d)}}`);489 break;490 case "Debugger":491 lines.push(`${ind(d)}Debugger {}`);492 break;493 case "StartMemoize":494 lines.push(`${ind(d)}StartMemoize {`);495 lines.push(`${ind(d + 1)}manualMemoId: ${value.manualMemoId}`);496 lines.push(497 `${ind(d + 1)}deps: ${value.deps != null ? `[${value.deps.length} deps]` : "null"}`498 );499 lines.push(`${ind(d)}}`);500 break;501 case "FinishMemoize":502 lines.push(`${ind(d)}FinishMemoize {`);503 lines.push(`${ind(d + 1)}manualMemoId: ${value.manualMemoId}`);504 lines.push(`${ind(d + 1)}decl: $${value.decl.identifier.id}`);505 lines.push(`${ind(d + 1)}pruned: ${value.pruned === true}`);506 lines.push(`${ind(d)}}`);507 break;508 case "UnsupportedNode":509 lines.push(`${ind(d)}UnsupportedNode {`);510 lines.push(511 `${ind(d + 1)}type: ${value.node != null ? value.node.type : "unknown"}`512 );513 lines.push(`${ind(d)}}`);514 break;515 default:516 lines.push(`${ind(d)}${kind} {}`);517 break;518 }519 return lines.join("\n");520}521522// ---------------------------------------------------------------------------523// Reactive value printing (tree-structured values)524// ---------------------------------------------------------------------------525526function printReactiveValue(value, depth, outlinedCollector) {527 const d = depth;528 const lines = [];529530 switch (value.kind) {531 case "LogicalExpression":532 lines.push(`${ind(d)}LogicalExpression {`);533 lines.push(`${ind(d + 1)}operator: ${value.operator}`);534 lines.push(`${ind(d + 1)}left:`);535 lines.push(printReactiveValue(value.left, d + 2, outlinedCollector));536 lines.push(`${ind(d + 1)}right:`);537 lines.push(printReactiveValue(value.right, d + 2, outlinedCollector));538 lines.push(`${ind(d + 1)}loc: ${formatLoc(value.loc)}`);539 lines.push(`${ind(d)}}`);540 break;541 case "ConditionalExpression":542 lines.push(`${ind(d)}ConditionalExpression {`);543 lines.push(`${ind(d + 1)}test:`);544 lines.push(printReactiveValue(value.test, d + 2, outlinedCollector));545 lines.push(`${ind(d + 1)}consequent:`);546 lines.push(547 printReactiveValue(value.consequent, d + 2, outlinedCollector)548 );549 lines.push(`${ind(d + 1)}alternate:`);550 lines.push(551 printReactiveValue(value.alternate, d + 2, outlinedCollector)552 );553 lines.push(`${ind(d + 1)}loc: ${formatLoc(value.loc)}`);554 lines.push(`${ind(d)}}`);555 break;556 case "SequenceExpression":557 lines.push(`${ind(d)}SequenceExpression {`);558 lines.push(`${ind(d + 1)}id: ${value.id}`);559 lines.push(`${ind(d + 1)}instructions:`);560 for (const instr of value.instructions) {561 lines.push(printReactiveInstruction(instr, d + 2, outlinedCollector));562 }563 lines.push(`${ind(d + 1)}value:`);564 lines.push(printReactiveValue(value.value, d + 2, outlinedCollector));565 lines.push(`${ind(d + 1)}loc: ${formatLoc(value.loc)}`);566 lines.push(`${ind(d)}}`);567 break;568 case "OptionalExpression":569 lines.push(`${ind(d)}OptionalExpression {`);570 lines.push(`${ind(d + 1)}id: ${value.id}`);571 lines.push(`${ind(d + 1)}optional: ${value.optional}`);572 lines.push(`${ind(d + 1)}value:`);573 lines.push(printReactiveValue(value.value, d + 2, outlinedCollector));574 lines.push(`${ind(d + 1)}loc: ${formatLoc(value.loc)}`);575 lines.push(`${ind(d)}}`);576 break;577 default:578 // Plain InstructionValue579 lines.push(printInstructionValueFields(value, d));580 break;581 }582 return lines.join("\n");583}584585// ---------------------------------------------------------------------------586// Reactive instruction printing587// ---------------------------------------------------------------------------588589function printReactiveInstruction(instr, depth, outlinedCollector) {590 const lines = [];591 lines.push(`${ind(depth)}[${instr.id}] ReactiveInstruction {`);592 const d = depth + 1;593 lines.push(`${ind(d)}id: ${instr.id}`);594 // lvalue595 if (instr.lvalue != null) {596 lines.push(`${ind(d)}lvalue:`);597 lines.push(printPlaceInline(instr.lvalue, d + 1));598 } else {599 lines.push(`${ind(d)}lvalue: null`);600 }601 // value602 lines.push(`${ind(d)}value:`);603 lines.push(printReactiveValue(instr.value, d + 1, outlinedCollector));604 // Collect outlined functions605 collectOutlinedFromValue(instr.value, outlinedCollector);606 // effects607 if (instr.effects != null) {608 lines.push(`${ind(d)}effects: [${instr.effects.length} effects]`);609 } else {610 lines.push(`${ind(d)}effects: null`);611 }612 lines.push(`${ind(d)}loc: ${formatLoc(instr.loc)}`);613 lines.push(`${ind(depth)}}`);614 return lines.join("\n");615}616617// ---------------------------------------------------------------------------618// Reactive scope printing619// ---------------------------------------------------------------------------620621function printReactiveScopeDetails(scope, depth) {622 const lines = [];623 const d = depth;624 lines.push(`${ind(d)}scope @${scope.id} {`);625 lines.push(`${ind(d + 1)}id: ${scope.id}`);626 lines.push(627 `${ind(d + 1)}range: ${formatMutableRange(scope.range)}`628 );629 // dependencies630 const deps = [...scope.dependencies];631 lines.push(`${ind(d + 1)}dependencies: [${deps.length}]`);632 for (const dep of deps) {633 const path = dep.path634 .map((p) => `${p.optional ? "?." : "."}${p.property}`)635 .join("");636 lines.push(637 `${ind(d + 2)}$${dep.identifier.id}${path} (reactive=${dep.reactive})`638 );639 }640 // declarations641 const decls = [...scope.declarations].sort((a, b) => a[0] - b[0]);642 lines.push(`${ind(d + 1)}declarations: [${decls.length}]`);643 for (const [id, decl] of decls) {644 lines.push(`${ind(d + 2)}$${id}: $${decl.identifier.id}`);645 }646 // reassignments647 const reassigns = [...scope.reassignments];648 lines.push(`${ind(d + 1)}reassignments: [${reassigns.length}]`);649 for (const ident of reassigns) {650 lines.push(`${ind(d + 2)}$${ident.id}`);651 }652 // earlyReturnValue653 if (scope.earlyReturnValue != null) {654 lines.push(`${ind(d + 1)}earlyReturnValue:`);655 lines.push(656 `${ind(d + 2)}value: $${scope.earlyReturnValue.value.id}`657 );658 lines.push(659 `${ind(d + 2)}label: bb${scope.earlyReturnValue.label}`660 );661 } else {662 lines.push(`${ind(d + 1)}earlyReturnValue: null`);663 }664 // merged665 const merged = [...scope.merged];666 if (merged.length > 0) {667 lines.push(668 `${ind(d + 1)}merged: [${merged.map((m) => `@${m}`).join(", ")}]`669 );670 } else {671 lines.push(`${ind(d + 1)}merged: []`);672 }673 lines.push(`${ind(d + 1)}loc: ${formatLoc(scope.loc)}`);674 lines.push(`${ind(d)}}`);675 return lines.join("\n");676}677678// ---------------------------------------------------------------------------679// Reactive terminal printing680// ---------------------------------------------------------------------------681682function printReactiveTerminal(terminal, depth, outlinedCollector) {683 const lines = [];684 const d = depth;685 const kind = terminal.kind;686687 lines.push(`${ind(d)}${reactiveTerminalName(kind)} {`);688 lines.push(`${ind(d + 1)}id: ${terminal.id}`);689690 switch (kind) {691 case "break":692 lines.push(`${ind(d + 1)}target: bb${terminal.target}`);693 lines.push(`${ind(d + 1)}targetKind: ${terminal.targetKind}`);694 break;695 case "continue":696 lines.push(`${ind(d + 1)}target: bb${terminal.target}`);697 lines.push(`${ind(d + 1)}targetKind: ${terminal.targetKind}`);698 break;699 case "return":700 lines.push(`${ind(d + 1)}value:`);701 lines.push(printPlaceInline(terminal.value, d + 2));702 break;703 case "throw":704 lines.push(`${ind(d + 1)}value:`);705 lines.push(printPlaceInline(terminal.value, d + 2));706 break;707 case "if":708 lines.push(`${ind(d + 1)}test:`);709 lines.push(printPlaceInline(terminal.test, d + 2));710 lines.push(`${ind(d + 1)}consequent:`);711 lines.push(712 printReactiveBlock(terminal.consequent, d + 2, outlinedCollector)713 );714 if (terminal.alternate != null) {715 lines.push(`${ind(d + 1)}alternate:`);716 lines.push(717 printReactiveBlock(terminal.alternate, d + 2, outlinedCollector)718 );719 } else {720 lines.push(`${ind(d + 1)}alternate: null`);721 }722 break;723 case "switch":724 lines.push(`${ind(d + 1)}test:`);725 lines.push(printPlaceInline(terminal.test, d + 2));726 lines.push(`${ind(d + 1)}cases:`);727 for (const c of terminal.cases) {728 if (c.test != null) {729 lines.push(`${ind(d + 2)}case $${c.test.identifier.id}:`);730 } else {731 lines.push(`${ind(d + 2)}default:`);732 }733 if (c.block != null) {734 lines.push(printReactiveBlock(c.block, d + 3, outlinedCollector));735 } else {736 lines.push(`${ind(d + 3)}(empty)`);737 }738 }739 break;740 case "do-while":741 lines.push(`${ind(d + 1)}loop:`);742 lines.push(743 printReactiveBlock(terminal.loop, d + 2, outlinedCollector)744 );745 lines.push(`${ind(d + 1)}test:`);746 lines.push(747 printReactiveValue(terminal.test, d + 2, outlinedCollector)748 );749 break;750 case "while":751 lines.push(`${ind(d + 1)}test:`);752 lines.push(753 printReactiveValue(terminal.test, d + 2, outlinedCollector)754 );755 lines.push(`${ind(d + 1)}loop:`);756 lines.push(757 printReactiveBlock(terminal.loop, d + 2, outlinedCollector)758 );759 break;760 case "for":761 lines.push(`${ind(d + 1)}init:`);762 lines.push(763 printReactiveValue(terminal.init, d + 2, outlinedCollector)764 );765 lines.push(`${ind(d + 1)}test:`);766 lines.push(767 printReactiveValue(terminal.test, d + 2, outlinedCollector)768 );769 if (terminal.update != null) {770 lines.push(`${ind(d + 1)}update:`);771 lines.push(772 printReactiveValue(terminal.update, d + 2, outlinedCollector)773 );774 } else {775 lines.push(`${ind(d + 1)}update: null`);776 }777 lines.push(`${ind(d + 1)}loop:`);778 lines.push(779 printReactiveBlock(terminal.loop, d + 2, outlinedCollector)780 );781 break;782 case "for-of":783 lines.push(`${ind(d + 1)}init:`);784 lines.push(785 printReactiveValue(terminal.init, d + 2, outlinedCollector)786 );787 lines.push(`${ind(d + 1)}test:`);788 lines.push(789 printReactiveValue(terminal.test, d + 2, outlinedCollector)790 );791 lines.push(`${ind(d + 1)}loop:`);792 lines.push(793 printReactiveBlock(terminal.loop, d + 2, outlinedCollector)794 );795 break;796 case "for-in":797 lines.push(`${ind(d + 1)}init:`);798 lines.push(799 printReactiveValue(terminal.init, d + 2, outlinedCollector)800 );801 lines.push(`${ind(d + 1)}loop:`);802 lines.push(803 printReactiveBlock(terminal.loop, d + 2, outlinedCollector)804 );805 break;806 case "label":807 lines.push(`${ind(d + 1)}block:`);808 lines.push(809 printReactiveBlock(terminal.block, d + 2, outlinedCollector)810 );811 break;812 case "try":813 lines.push(`${ind(d + 1)}block:`);814 lines.push(815 printReactiveBlock(terminal.block, d + 2, outlinedCollector)816 );817 if (terminal.handlerBinding != null) {818 lines.push(`${ind(d + 1)}handlerBinding:`);819 lines.push(printPlaceInline(terminal.handlerBinding, d + 2));820 } else {821 lines.push(`${ind(d + 1)}handlerBinding: null`);822 }823 lines.push(`${ind(d + 1)}handler:`);824 lines.push(825 printReactiveBlock(terminal.handler, d + 2, outlinedCollector)826 );827 break;828 default:829 break;830 }831832 lines.push(`${ind(d + 1)}loc: ${formatLoc(terminal.loc)}`);833 lines.push(`${ind(d)}}`);834 return lines.join("\n");835}836837function reactiveTerminalName(kind) {838 const names = {839 break: "Break",840 continue: "Continue",841 return: "Return",842 throw: "Throw",843 if: "If",844 switch: "Switch",845 "do-while": "DoWhile",846 while: "While",847 for: "For",848 "for-of": "ForOf",849 "for-in": "ForIn",850 label: "Label",851 try: "Try",852 };853 return names[kind] ?? kind;854}855856// ---------------------------------------------------------------------------857// Reactive block printing (array of ReactiveStatements)858// ---------------------------------------------------------------------------859860function printReactiveBlock(block, depth, outlinedCollector) {861 if (block == null || block.length === 0) {862 return `${ind(depth)}(empty block)`;863 }864 const lines = [];865 for (const stmt of block) {866 lines.push(printReactiveStatement(stmt, depth, outlinedCollector));867 }868 return lines.join("\n");869}870871function printReactiveStatement(stmt, depth, outlinedCollector) {872 const lines = [];873 const d = depth;874875 switch (stmt.kind) {876 case "instruction":877 lines.push(878 printReactiveInstruction(stmt.instruction, d, outlinedCollector)879 );880 break;881 case "scope":882 lines.push(`${ind(d)}ReactiveScopeBlock {`);883 lines.push(printReactiveScopeDetails(stmt.scope, d + 1));884 lines.push(`${ind(d + 1)}instructions:`);885 lines.push(886 printReactiveBlock(stmt.instructions, d + 2, outlinedCollector)887 );888 lines.push(`${ind(d)}}`);889 break;890 case "pruned-scope":891 lines.push(`${ind(d)}PrunedReactiveScopeBlock {`);892 lines.push(printReactiveScopeDetails(stmt.scope, d + 1));893 lines.push(`${ind(d + 1)}instructions:`);894 lines.push(895 printReactiveBlock(stmt.instructions, d + 2, outlinedCollector)896 );897 lines.push(`${ind(d)}}`);898 break;899 case "terminal":900 if (stmt.label != null) {901 lines.push(902 `${ind(d)}label bb${stmt.label.id} (implicit=${stmt.label.implicit}):`903 );904 }905 lines.push(906 printReactiveTerminal(stmt.terminal, d, outlinedCollector)907 );908 break;909 default:910 lines.push(`${ind(d)}Unknown statement kind: ${stmt.kind}`);911 break;912 }913914 return lines.join("\n");915}916917// ---------------------------------------------------------------------------918// Collect outlined functions from reactive values919// ---------------------------------------------------------------------------920921function collectOutlinedFromValue(value, collector) {922 if (value == null) return;923 if (924 value.kind === "FunctionExpression" ||925 value.kind === "ObjectMethod"926 ) {927 // The loweredFunc in reactive context points to an HIRFunction928 // which in turn has a reactive body after BuildReactiveFunction.929 // But outlined functions are collected from env, so we just track930 // them for the main function printer.931 }932 // For reactive compound values, recurse933 if (value.kind === "SequenceExpression") {934 for (const instr of value.instructions) {935 collectOutlinedFromValue(instr.value, collector);936 }937 collectOutlinedFromValue(value.value, collector);938 } else if (value.kind === "LogicalExpression") {939 collectOutlinedFromValue(value.left, collector);940 collectOutlinedFromValue(value.right, collector);941 } else if (value.kind === "ConditionalExpression") {942 collectOutlinedFromValue(value.test, collector);943 collectOutlinedFromValue(value.consequent, collector);944 collectOutlinedFromValue(value.alternate, collector);945 } else if (value.kind === "OptionalExpression") {946 collectOutlinedFromValue(value.value, collector);947 }948}949950// ---------------------------------------------------------------------------951// Main function printer952// ---------------------------------------------------------------------------953954function printReactiveFunction(fn, functionIndex, outlinedCollector) {955 const lines = [];956 const d0 = 0;957 const d1 = 1;958 const d2 = 2;959960 lines.push(`${ind(d0)}ReactiveFunction #${functionIndex}:`);961962 // id963 lines.push(964 `${ind(d1)}id: ${fn.id != null ? JSON.stringify(fn.id) : "null"}`965 );966967 // nameHint968 lines.push(969 `${ind(d1)}nameHint: ${fn.nameHint != null ? JSON.stringify(fn.nameHint) : "null"}`970 );971972 // params973 lines.push(`${ind(d1)}params:`);974 for (let i = 0; i < fn.params.length; i++) {975 const param = fn.params[i];976 if (param.kind === "Identifier") {977 lines.push(`${ind(d2)}[${i}]`);978 lines.push(printPlaceInline(param, d2 + 1));979 } else {980 lines.push(`${ind(d2)}[${i}] ...`);981 lines.push(printPlaceInline(param.place, d2 + 1));982 }983 }984985 // generator / async986 lines.push(`${ind(d1)}generator: ${fn.generator}`);987 lines.push(`${ind(d1)}async: ${fn.async}`);988989 // directives990 if (fn.directives.length > 0) {991 lines.push(992 `${ind(d1)}directives: [${fn.directives.map((d) => JSON.stringify(d)).join(", ")}]`993 );994 } else {995 lines.push(`${ind(d1)}directives: []`);996 }997998 // loc999 lines.push(`${ind(d1)}loc: ${formatLoc(fn.loc)}`);10001001 // body1002 lines.push("");1003 lines.push(`${ind(d1)}body:`);1004 lines.push(printReactiveBlock(fn.body, d2, outlinedCollector));10051006 // Outlined functions from env1007 if (fn.env != null && typeof fn.env.getOutlinedFunctions === "function") {1008 const outlinedFns = fn.env.getOutlinedFunctions();1009 for (const outlined of outlinedFns) {1010 outlinedCollector.push(outlined.fn);1011 }1012 }10131014 return lines.join("\n");1015}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.