/
githubmr
/
facebook-react
Обзор
Документация
Войти
/
githubmr
/
facebook-react
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
compiler/packages/babel-plugin-react-compiler/src/HIR/ScopeDependencyUtils.ts
297 строк
7 KB
lauren
[compiler] Migrate CompilerError.invariant to new CompilerDiagnostic infra (#34403)
06 сен 2025, 19:58
Не верифицирован
06 сен 2025, 19:58
474f258
Код
Авторство
О чём код?
import { Place, ReactiveScopeDependency, Identifier, makeInstructionId, InstructionKind, GeneratedSource, BlockId, makeTemporaryIdentifier, Effect, GotoVariant, HIR, } from './HIR'; import {CompilerError} from '../CompilerError'; import {Environment} from './Environment'; import HIRBuilder from './HIRBuilder'; import {lowerValueToTemporary} from './BuildHIR'; type DependencyInstructions = { place: Place; value: HIR; exitBlockId: BlockId; }; export function buildDependencyInstructions( dep: ReactiveScopeDependency, env: Environment, ): DependencyInstructions { const builder = new HIRBuilder(env, { entryBlockKind: 'value', }); let dependencyValue: Identifier; if (dep.path.every(path => !path.optional)) { dependencyValue = writeNonOptionalDependency(dep, env, builder); } else { dependencyValue = writeOptionalDependency(dep, builder, null); } const exitBlockId = builder.terminate( { kind: 'unsupported', loc: GeneratedSource, id: makeInstructionId(0), }, null, ); return { place: { kind: 'Identifier', identifier: dependencyValue, effect: Effect.Freeze, reactive: dep.reactive, loc: GeneratedSource, }, value: builder.build(), exitBlockId, }; } /** * Write instructions for a simple dependency (without optional chains) */ function writeNonOptionalDependency( dep: ReactiveScopeDependency, env: Environment, builder: HIRBuilder, ): Identifier { const loc = dep.identifier.loc; let curr: Identifier = makeTemporaryIdentifier(env.nextIdentifierId, loc); builder.push({ lvalue: { identifier: curr, kind: 'Identifier', effect: Effect.Mutate, reactive: dep.reactive, loc, }, value: { kind: 'LoadLocal', place: { identifier: dep.identifier, kind: 'Identifier', effect: Effect.Freeze, reactive: dep.reactive, loc, }, loc, }, id: makeInstructionId(1), loc: loc, effects: null, }); /** * Iteratively build up dependency instructions by reading from the last written * instruction. */ for (const path of dep.path) { const next = makeTemporaryIdentifier(env.nextIdentifierId, loc); builder.push({ lvalue: { identifier: next, kind: 'Identifier', effect: Effect.Mutate, reactive: dep.reactive, loc, }, value: { kind: 'PropertyLoad', object: { identifier: curr, kind: 'Identifier', effect: Effect.Freeze, reactive: dep.reactive, loc, }, property: path.property, loc, }, id: makeInstructionId(1), loc: loc, effects: null, }); curr = next; } return curr; } /** * Write a dependency into optional blocks. * * e.g. `a.b?.c.d` is written to an optional block that tests `a.b` and * conditionally evaluates `c.d`. */ function writeOptionalDependency( dep: ReactiveScopeDependency, builder: HIRBuilder, parentAlternate: BlockId | null, ): Identifier { const env = builder.environment; /** * Reserve an identifier which will be used to store the result of this * dependency. */ const dependencyValue: Place = { kind: 'Identifier', identifier: makeTemporaryIdentifier(env.nextIdentifierId, GeneratedSource), effect: Effect.Mutate, reactive: dep.reactive, loc: GeneratedSource, }; /** * Reserve a block which is the fallthrough (and transitive successor) of this * optional chain. */ const continuationBlock = builder.reserve(builder.currentBlockKind()); let alternate; if (parentAlternate != null) { alternate = parentAlternate; } else { /** * If an outermost alternate block has not been reserved, write one * * $N = Primitive undefined * $M = StoreLocal $OptionalResult = $N * goto fallthrough */ alternate = builder.enter('value', () => { const temp = lowerValueToTemporary(builder, { kind: 'Primitive', value: undefined, loc: GeneratedSource, }); lowerValueToTemporary(builder, { kind: 'StoreLocal', lvalue: {kind: InstructionKind.Const, place: {...dependencyValue}}, value: {...temp}, type: null, loc: GeneratedSource, }); return { kind: 'goto', variant: GotoVariant.Break, block: continuationBlock.id, id: makeInstructionId(0), loc: GeneratedSource, }; }); } // Reserve the consequent block, which is the successor of the test block const consequent = builder.reserve('value'); let testIdentifier: Identifier | null = null; const testBlock = builder.enter('value', () => { const testDependency = { ...dep, path: dep.path.slice(0, dep.path.length - 1), }; const firstOptional = dep.path.findIndex(path => path.optional); CompilerError.invariant(firstOptional !== -1, { reason: '[ScopeDependencyUtils] Internal invariant broken: expected optional path', description: null, details: [ { kind: 'error', loc: dep.identifier.loc, message: null, }, ], suggestions: null, }); if (firstOptional === dep.path.length - 1) { // Base case: the test block is simple testIdentifier = writeNonOptionalDependency(testDependency, env, builder); } else { // Otherwise, the test block is a nested optional chain testIdentifier = writeOptionalDependency( testDependency, builder, alternate, ); } return { kind: 'branch', test: { identifier: testIdentifier, effect: Effect.Freeze, kind: 'Identifier', loc: GeneratedSource, reactive: dep.reactive, }, consequent: consequent.id, alternate, id: makeInstructionId(0), loc: GeneratedSource, fallthrough: continuationBlock.id, }; }); builder.enterReserved(consequent, () => { CompilerError.invariant(testIdentifier !== null, { reason: 'Satisfy type checker', description: null, details: [ { kind: 'error', loc: null, message: null, }, ], suggestions: null, }); lowerValueToTemporary(builder, { kind: 'StoreLocal', lvalue: {kind: InstructionKind.Const, place: {...dependencyValue}}, value: lowerValueToTemporary(builder, { kind: 'PropertyLoad', object: { identifier: testIdentifier, kind: 'Identifier', effect: Effect.Freeze, reactive: dep.reactive, loc: GeneratedSource, }, property: dep.path.at(-1)!.property, loc: GeneratedSource, }), type: null, loc: GeneratedSource, }); return { kind: 'goto', variant: GotoVariant.Break, block: continuationBlock.id, id: makeInstructionId(0), loc: GeneratedSource, }; }); builder.terminateWithContinuation( { kind: 'optional', optional: dep.path.at(-1)!.optional, test: testBlock, fallthrough: continuationBlock.id, id: makeInstructionId(0), loc: GeneratedSource, }, continuationBlock, ); return dependencyValue.identifier; }