/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/PrintReactiveFunction.ts
464 строки
12 KB
lauren
[compiler] Migrate CompilerError.invariant to new CompilerDiagnostic infra (#34403)
06 сен 2025, 19:58
Не верифицирован
06 сен 2025, 19:58
474f258
Код
Авторство
О чём код?
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import {CompilerError} from '../CompilerError'; import { PrunedReactiveScopeBlock, ReactiveFunction, ReactiveScope, ReactiveScopeBlock, ReactiveScopeDependency, ReactiveStatement, ReactiveTerminal, ReactiveValue, } from '../HIR/HIR'; import { printFunction, printIdentifier, printInstructionValue, printPlace, printType, } from '../HIR/PrintHIR'; import {assertExhaustive} from '../Utils/utils'; export function printReactiveFunctionWithOutlined( fn: ReactiveFunction, ): string { const writer = new Writer(); writeReactiveFunction(fn, writer); for (const outlined of fn.env.getOutlinedFunctions()) { writer.writeLine('\nfunction ' + printFunction(outlined.fn)); } return writer.complete(); } export function printReactiveFunction(fn: ReactiveFunction): string { const writer = new Writer(); writeReactiveFunction(fn, writer); return writer.complete(); } function writeReactiveFunction(fn: ReactiveFunction, writer: Writer): void { writer.writeLine(`function ${fn.id !== null ? fn.id : '<unknown>'}(`); writer.indented(() => { for (const param of fn.params) { if (param.kind === 'Identifier') { writer.writeLine(`${printPlace(param)},`); } else { writer.writeLine(`...${printPlace(param.place)},`); } } }); writer.writeLine(') {'); writeReactiveInstructions(writer, fn.body); writer.writeLine('}'); } export function printReactiveScopeSummary(scope: ReactiveScope): string { const items = []; // If the scope has a return value it needs a label items.push('scope'); items.push(`@${scope.id}`); items.push(`[${scope.range.start}:${scope.range.end}]`); items.push( `dependencies=[${Array.from(scope.dependencies) .map(dep => printDependency(dep)) .join(', ')}]`, ); items.push( `declarations=[${Array.from(scope.declarations) .map(([, decl]) => printIdentifier({...decl.identifier, scope: decl.scope}), ) .join(', ')}]`, ); items.push( `reassignments=[${Array.from(scope.reassignments).map(reassign => printIdentifier(reassign), )}]`, ); if (scope.earlyReturnValue !== null) { items.push( `earlyReturn={id: ${printIdentifier( scope.earlyReturnValue.value, )}, label: ${scope.earlyReturnValue.label}}}`, ); } return items.join(' '); } export function writeReactiveBlock( writer: Writer, block: ReactiveScopeBlock, ): void { writer.writeLine(`${printReactiveScopeSummary(block.scope)} {`); writeReactiveInstructions(writer, block.instructions); writer.writeLine('}'); } export function writePrunedScope( writer: Writer, block: PrunedReactiveScopeBlock, ): void { writer.writeLine(`<pruned> ${printReactiveScopeSummary(block.scope)} {`); writeReactiveInstructions(writer, block.instructions); writer.writeLine('}'); } export function printDependency(dependency: ReactiveScopeDependency): string { const identifier = printIdentifier(dependency.identifier) + printType(dependency.identifier.type); return `${identifier}${dependency.path.map(token => `${token.optional ? '?.' : '.'}${token.property}`).join('')}`; } export function printReactiveInstructions( instructions: Array<ReactiveStatement>, ): string { const writer = new Writer(); writeReactiveInstructions(writer, instructions); return writer.complete(); } export function writeReactiveInstructions( writer: Writer, instructions: Array<ReactiveStatement>, ): void { writer.indented(() => { for (const instr of instructions) { writeReactiveInstruction(writer, instr); } }); } function writeReactiveInstruction( writer: Writer, instr: ReactiveStatement, ): void { switch (instr.kind) { case 'instruction': { const {instruction} = instr; const id = `[${instruction.id}]`; if (instruction.lvalue !== null) { writer.write(`${id} ${printPlace(instruction.lvalue)} = `); writeReactiveValue(writer, instruction.value); writer.newline(); } else { writer.write(`${id} `); writeReactiveValue(writer, instruction.value); writer.newline(); } break; } case 'scope': { writeReactiveBlock(writer, instr); break; } case 'pruned-scope': { writePrunedScope(writer, instr); break; } case 'terminal': { if (instr.label !== null) { writer.write(`bb${instr.label.id}: `); } writeTerminal(writer, instr.terminal); break; } default: { assertExhaustive( instr, `Unexpected terminal kind \`${(instr as any).kind}\``, ); } } } export function printReactiveValue(value: ReactiveValue): string { const writer = new Writer(); writeReactiveValue(writer, value); return writer.complete(); } function writeReactiveValue(writer: Writer, value: ReactiveValue): void { switch (value.kind) { case 'ConditionalExpression': { writer.writeLine(`Ternary `); writer.indented(() => { writeReactiveValue(writer, value.test); writer.writeLine(`? `); writer.indented(() => { writeReactiveValue(writer, value.consequent); }); writer.writeLine(`: `); writer.indented(() => { writeReactiveValue(writer, value.alternate); }); }); writer.newline(); break; } case 'LogicalExpression': { writer.writeLine(`Logical`); writer.indented(() => { writeReactiveValue(writer, value.left); writer.write(`${value.operator} `); writeReactiveValue(writer, value.right); }); writer.newline(); break; } case 'SequenceExpression': { writer.writeLine(`Sequence`); writer.indented(() => { writer.indented(() => { value.instructions.forEach(instr => writeReactiveInstruction(writer, { kind: 'instruction', instruction: instr, }), ); writer.write(`[${value.id}] `); writeReactiveValue(writer, value.value); }); }); writer.newline(); break; } case 'OptionalExpression': { writer.append(`OptionalExpression optional=${value.optional}`); writer.newline(); writer.indented(() => { writeReactiveValue(writer, value.value); }); writer.newline(); break; } default: { const printed = printInstructionValue(value); const lines = printed.split('\n'); if (lines.length === 1) { writer.writeLine(printed); } else { writer.indented(() => { for (const line of lines) { writer.writeLine(line); } }); } } } } export function printReactiveTerminal(terminal: ReactiveTerminal): string { const writer = new Writer(); writeTerminal(writer, terminal); return writer.complete(); } function writeTerminal(writer: Writer, terminal: ReactiveTerminal): void { switch (terminal.kind) { case 'break': { const id = terminal.id !== null ? `[${terminal.id}]` : []; writer.writeLine( `${id} break bb${terminal.target} (${terminal.targetKind})`, ); break; } case 'continue': { const id = `[${terminal.id}]`; writer.writeLine( `${id} continue bb${terminal.target} (${terminal.targetKind})`, ); break; } case 'do-while': { writer.writeLine(`[${terminal.id}] do-while {`); writeReactiveInstructions(writer, terminal.loop); writer.writeLine('} ('); writer.indented(() => { writeReactiveValue(writer, terminal.test); }); writer.writeLine(')'); break; } case 'while': { writer.writeLine(`[${terminal.id}] while (`); writer.indented(() => { writeReactiveValue(writer, terminal.test); }); writer.writeLine(') {'); writeReactiveInstructions(writer, terminal.loop); writer.writeLine('}'); break; } case 'if': { const {test, consequent, alternate} = terminal; writer.writeLine(`[${terminal.id}] if (${printPlace(test)}) {`); writeReactiveInstructions(writer, consequent); if (alternate !== null) { writer.writeLine('} else {'); writeReactiveInstructions(writer, alternate); } writer.writeLine('}'); break; } case 'switch': { writer.writeLine( `[${terminal.id}] switch (${printPlace(terminal.test)}) {`, ); writer.indented(() => { for (const case_ of terminal.cases) { let prefix = case_.test !== null ? `case ${printPlace(case_.test)}` : 'default'; writer.writeLine(`${prefix}: {`); writer.indented(() => { const block = case_.block; CompilerError.invariant(block != null, { reason: 'Expected case to have a block', description: null, details: [ { kind: 'error', loc: case_.test?.loc ?? null, message: null, }, ], suggestions: null, }); writeReactiveInstructions(writer, block); }); writer.writeLine('}'); } }); writer.writeLine('}'); break; } case 'for': { writer.writeLine(`[${terminal.id}] for (`); writer.indented(() => { writeReactiveValue(writer, terminal.init); writer.writeLine(';'); writeReactiveValue(writer, terminal.test); writer.writeLine(';'); if (terminal.update !== null) { writeReactiveValue(writer, terminal.update); } }); writer.writeLine(') {'); writeReactiveInstructions(writer, terminal.loop); writer.writeLine('}'); break; } case 'for-of': { writer.writeLine(`[${terminal.id}] for-of (`); writer.indented(() => { writeReactiveValue(writer, terminal.init); writer.writeLine(';'); writeReactiveValue(writer, terminal.test); }); writer.writeLine(') {'); writeReactiveInstructions(writer, terminal.loop); writer.writeLine('}'); break; } case 'for-in': { writer.writeLine(`[${terminal.id}] for-in (`); writer.indented(() => { writeReactiveValue(writer, terminal.init); }); writer.writeLine(') {'); writeReactiveInstructions(writer, terminal.loop); writer.writeLine('}'); break; } case 'throw': { writer.writeLine(`[${terminal.id}] throw ${printPlace(terminal.value)}`); break; } case 'return': { writer.writeLine(`[${terminal.id}] return ${printPlace(terminal.value)}`); break; } case 'label': { writer.writeLine('{'); writeReactiveInstructions(writer, terminal.block); writer.writeLine('}'); break; } case 'try': { writer.writeLine(`[${terminal.id}] try {`); writeReactiveInstructions(writer, terminal.block); writer.write(`} catch `); if (terminal.handlerBinding !== null) { writer.writeLine(`(${printPlace(terminal.handlerBinding)}) {`); } else { writer.writeLine(`{`); } writeReactiveInstructions(writer, terminal.handler); writer.writeLine('}'); break; } default: assertExhaustive( terminal, `Unhandled terminal kind \`${(terminal as any).kind}\``, ); } } export class Writer { #out: Array<string> = []; #line: string; #depth: number; constructor({depth}: {depth: number} = {depth: 0}) { this.#depth = Math.max(depth, 0); this.#line = ''; } complete(): string { const line = this.#line.trimEnd(); if (line.length > 0) { this.#out.push(line); } return this.#out.join('\n'); } append(s: string): void { this.write(s); } newline(): void { const line = this.#line.trimEnd(); if (line.length > 0) { this.#out.push(line); } this.#line = ''; } write(s: string): void { if (this.#line.length === 0 && this.#depth > 0) { // indent before writing this.#line = ' '.repeat(this.#depth); } this.#line += s; } writeLine(s: string): void { this.write(s); this.newline(); } indented(f: () => void): void { this.#depth++; f(); this.#depth--; } }