compiler/scripts/debug-print-reactive.mjs 1,016 lines View on github.com → Search inside
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}

Code quality findings 61

Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (loc == null || typeof loc === "symbol") {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (loc == null || typeof loc === "symbol") {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (type == null) return "Type";
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (type.kind) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
return type.shapeId != null ? `Object<${type.shapeId}>` : "Object";
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
type.shapeId != null ? `Function<${type.shapeId}>` : "Function";
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return ret !== "Type" ? `${base}():${ret}` : base;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (name == null) return "null";
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (typeof name === "object" && name.value != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (typeof name === "object" && name.value != null) {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (typeof name === "object" && name.value != null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (range == null) return "[0:0]";
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (scope == null) return "null";
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (id == null) return "null";
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (ps.kind === "Identifier") return `$${ps.identifier.id}`;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (ps.kind === "Spread") return `...$${ps.place.identifier.id}`;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return `[${pattern.items.map((item) => (item.kind === "Hole" ? "<hole>" : item.kind === "Spread" ? `...$${item.place.identifier.id}` : `$${item.identifier.id}`)).join(", ")}]`;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
return `{${pattern.properties.map((p) => p.kind === "Spread" ? `...$${p.place.identifier.id}` : `${printObjectPropertyKey(p.key)}: $${p.place.identifier.id}`).join(", ")}}`;
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (kind) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
`${ind(d + 1)}value: ${value.value === undefined ? "undefined" : JSON.stringify(value.value)}`
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (value.properties != null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (prop.kind === "ObjectProperty") {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
`${ind(d + 1)}elements: [${value.elements.map((e) => (e.kind === "Hole" ? "<hole>" : e.kind === "Spread" ? `...$${e.place.identifier.id}` : `$${e.identifier.id}`)).join(", ")}]`
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (b.module != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (b.imported != null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (value.tag.kind === "Identifier") {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (attr.kind === "JsxAttribute") {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (value.children != null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
kind === "FunctionExpression" ? "FunctionExpression" : "ObjectMethod";
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (kind === "FunctionExpression") {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
`${ind(d + 1)}name: ${value.name != null ? JSON.stringify(value.name) : "null"}`
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
`${ind(d + 1)}aliasingEffects: ${ae != null ? `[${ae.length} effects]` : "null"}`
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
`${ind(d + 1)}deps: ${value.deps != null ? `[${value.deps.length} deps]` : "null"}`
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
lines.push(`${ind(d + 1)}pruned: ${value.pruned === true}`);
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
`${ind(d + 1)}type: ${value.node != null ? value.node.type : "unknown"}`
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (value.kind) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (instr.lvalue != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (instr.effects != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (scope.earlyReturnValue != null) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (kind) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (terminal.alternate != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (c.test != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (c.block != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (terminal.update != null) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (terminal.handlerBinding != null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (block == null || block.length === 0) {
Ensure all cases are handled or a default case is present
info correctness switch-without-default
switch (stmt.kind) {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (stmt.label != null) {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (value == null) return;
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
value.kind === "FunctionExpression" ||
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
value.kind === "ObjectMethod"
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (value.kind === "SequenceExpression") {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (value.kind === "LogicalExpression") {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (value.kind === "ConditionalExpression") {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
} else if (value.kind === "OptionalExpression") {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
`${ind(d1)}id: ${fn.id != null ? JSON.stringify(fn.id) : "null"}`
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
`${ind(d1)}nameHint: ${fn.nameHint != null ? JSON.stringify(fn.nameHint) : "null"}`
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (param.kind === "Identifier") {
Use strict equality (===) to prevent type coercion bugs
info correctness loose-equality
if (fn.env != null && typeof fn.env.getOutlinedFunctions === "function") {
Use strict inequality (!==) to prevent type coercion bugs
info correctness loose-inequality
if (fn.env != null && typeof fn.env.getOutlinedFunctions === "function") {
Be cautious with typeof; it has limitations (e.g., typeof null === 'object')
info correctness typeof-pitfall
if (fn.env != null && typeof fn.env.getOutlinedFunctions === "function") {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.