์žฅ์šฉ์„ ๋ธ”๋กœ๊ทธ
๋Œ์•„๊ฐ€๊ธฐ
21 min read
๐Ÿ‡บ๐Ÿ‡ธ EN
React Compiler, ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ๊นŒ [3] - HIR๋กœ ๋ณ€ํ™˜ (Lowering)

์ด์ „์ด์•ผ๊ธฐ

์ง€๋‚œ ๋‘ํŽธ์„ ํ†ตํ•ด ์ปดํŒŒ์ผ๋Ÿฌ์˜ ์ง„์ž…์ ๊ณผ ์บ์‹ฑ๋ฐฉ๋ฒ•์ธ useMemoCache์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜๋‹ค. ์ด๋ฒˆ ๋ถ€ํ„ฐ๋Š” ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์–ด๋–ค ๊ณผ์ •๋“ค์„ ํ†ตํ•ด์„œ ๊ตฌ๋ฌธ ๋ถ„์„์„ ํ•˜๊ณ  ์ปดํŒŒ์ผ์„ ํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

1ํŽธ์—์„œ ์‚ดํŽด๋ดค๋˜ ์‹ค์งˆ์ ์œผ๋กœ ์ปดํŒŒ์ผ์„ ์ง„ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์ธ compileFn์„ ๋‹ค์‹œ ์‚ดํŽด๋ณด์ž.

export function compileFn(
  func: NodePath<
    t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
  >,
  config: EnvironmentConfig,
  fnType: ReactFunctionType,
  useMemoCacheIdentifier: string,
  logger: Logger | null,
  filename: string | null,
  code: string | null
): CodegenFunction {
  let generator = run(
    func,
    config,
    fnType,
    useMemoCacheIdentifier,
    logger,
    filename,
    code
  );
  while (true) {
    const next = generator.next();
    if (next.done) {
      return next.value;
    }
  }
}

compileFn ํ•จ์ˆ˜๋Š” run ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , generator.next()๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ณผ์ •์„ ๋ฐ˜๋ณตํ•œ๋‹ค.

// packages/babel-plugin-react-compiler/src/Entrypoint/Pipline.ts
export type CompilerPipelineValue =
  | { kind: "ast"; name: string; value: CodegenFunction }
  | { kind: "hir"; name: string; value: HIRFunction }
  | { kind: "reactive"; name: string; value: ReactiveFunction }
  | { kind: "debug"; name: string; value: string };

export function* run(
  func: NodePath<
    t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
  >,
  config: EnvironmentConfig,
  fnType: ReactFunctionType,
  useMemoCacheIdentifier: string,
  logger: Logger | null,
  filename: string | null,
  code: string | null
): Generator<CompilerPipelineValue, CodegenFunction> {
  const contextIdentifiers = findContextIdentifiers(func);
  const env = new Environment(
    fnType,
    config,
    contextIdentifiers,
    logger,
    filename,
    code,
    useMemoCacheIdentifier
  );
  yield {
    kind: "debug",
    name: "EnvironmentConfig",
    value: prettyFormat(env.config),
  };
  const ast = yield* runWithEnvironment(func, env);
  return ast;
}

run ํ•จ์ˆ˜๋Š” Environment๋ฅผ ์ƒ์„ฑํ•˜๊ณ , runWithEnvironment ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
Environment๋Š” ์ปดํŒŒ์ผ ๊ณผ์ •์—์„œ ์ „์—ญ์ ์ธ ์ƒํƒœ์™€ ์„ค์ •์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฐ์ฒด์ด๋‹ค. ์ด์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋‹ค์Œ์— ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ณ , ์ผ๋‹จ ๋„˜์–ด๊ฐ€์ž.

์ด์ œ runWithEnvironment ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด์ž.
์‹ค์งˆ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.

runWithEnvironment

// packages/babel-plugin-react-compiler/src/Entrypoint/Pipline.ts
function* runWithEnvironment(
  func: NodePath<
    t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
  >,
  env: Environment
): Generator<CompilerPipelineValue, CodegenFunction> {
  const hir = lower(func, env).unwrap();
  yield log({ kind: "hir", name: "HIR", value: hir });

  pruneMaybeThrows(hir);
  yield log({ kind: "hir", name: "PruneMaybeThrows", value: hir });

  validateContextVariableLValues(hir);
  validateUseMemo(hir);

  dropManualMemoization(hir);
  yield log({ kind: "hir", name: "DropManualMemoization", value: hir });

  // ~~~~~~~~~~~~~~~~~~~~~
  // --- ์ค‘๊ฐ„ ์—ฌ๋Ÿฌ ๊ณผ์ •๋“ค ---
  // ~~~~~~~~~~~~~~~~~~~~~

  const ast = codegenFunction(reactiveFunction, uniqueIdentifiers).unwrap();
  yield log({ kind: "ast", name: "Codegen", value: ast });

  /**
   * This flag should be only set for unit / fixture tests to check
   * that Forget correctly handles unexpected errors (e.g. exceptions
   * thrown by babel functions or other unexpected exceptions).
   */
  if (env.config.throwUnknownException__testonly) {
    throw new Error("unexpected error");
  }

  // ์ตœ์ข…์ ์œผ๋กœ ์ƒ์„ฑ๋œ ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜
  return ast;
}

์ „์ฒด ๊ณผ์ •์€ ์•ฝ 40๊ฐœ ์ •๋„์˜ ๊ณผ์ •์„ ๊ฑฐ์น˜๋ฉฐ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
๊ฐ ๊ณผ์ •์„์„ ์šฐ์„  ํฌ๊ฒŒ ๋ถ„๋ฅ˜ํ•ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. โ€˜Loweringโ€™: AST๋ฅผ HIR(High-level Intermediate Representation)๋กœ ๋ณ€ํ™˜
  2. โ€˜Normalization, Optimizationโ€™ : HIR์„ ์ •๊ทœํ™”ํ•˜๊ณ  ์ตœ์ ํ™”ํ•˜๋Š” ๊ณผ์ •
  3. โ€˜Static Analysis, Type Inferenceโ€™ : SSA ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  ํƒ€์ž… ์ถ”๋ก 
  4. โ€˜Reactive Optimizationโ€™ : ๋ฐ˜์‘์„ฑ ์ตœ์ ํ™”
  5. โ€˜Code Generationโ€™ : ์ฝ”๋“œ ์ƒ์„ฑ

๋” ์„ธ๋ถ€์ ์œผ๋กœ ํŽผ์ณ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
์•„์ง ๊นŠ๊ฒŒ ๋“ค์—ฌ๋‹ค๋ณด์ง„ ๋ง๊ณ  ์–ด๋–ค ๊ณผ์ •๋“ค์ด ์žˆ๋Š”์ง€๋งŒ ํ›‘์–ด๋ณด์ž.

  1. โ€˜Loweringโ€™
    • lower: AST๋ฅผ HIR๋กœ ๋ณ€ํ™˜
  2. โ€™์ตœ์ ํ™” ๋ฐ ์ •๊ทœํ™”โ€™
    • pruneMaybeThrows: HIR์—์„œ MaybeThrows๋ฅผ ์ œ๊ฑฐ
    • validateContextVariableLValues: HIR์—์„œ Context ๋ณ€์ˆ˜์˜ LValues๋ฅผ ๊ฒ€์ฆ
    • validateUseMemo: HIR์—์„œ useMemo๋ฅผ ๊ฒ€์ฆ
    • dropManualMemoization: ์ˆ˜๋™์œผ๋กœ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ์ œ๊ฑฐ
    • inlineImmediatelyInvokedFunctionExpressions: ์ฆ‰์‹œ ํ˜ธ์ถœ ํ•จ์ˆ˜ ํ‘œํ˜„์‹(IIFE)์„ ์ธ๋ผ์ธํ™”
    • mergeConsecutiveBlocks: ์—ฐ์†๋œ ๋ธ”๋ก์„ ๋ณ‘ํ•ฉ
  3. โ€™์ •์  ๋ถ„์„ ๋ฐ ํƒ€์ž… ์ถ”๋ก โ€™
    • enterSSA: SSA(Static Single Assignment) ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜
    • eliminateRedundantPhi: ์ค‘๋ณต๋œ Phi ๋…ธ๋“œ ์ œ๊ฑฐ
    • constantPropagation: ์ƒ์ˆ˜ ์ „ํŒŒ
    • inferTypes: ํƒ€์ž… ์ถ”๋ก 
    • validateHooksUsage: ํ›… ์‚ฌ์šฉ์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
    • validateNoCapitalizedCalls: ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ•จ์ˆ˜ ํ˜ธ์ถœ ๊ฒ€์‚ฌ
    • analyseFunctions: ํ•จ์ˆ˜ ๋ถ„์„
    • inferReferenceEffects: ์ฐธ์กฐ ํšจ๊ณผ ์ถ”๋ก 
    • deadCodeElimination: ๋ฐ๋“œ ์ฝ”๋“œ ์ œ๊ฑฐ
    • inferMutableRanges: ๊ฐ€๋ณ€ ๋ฒ”์œ„ ์ถ”๋ก 
    • inferReactivePlaces: ๋ฐ˜์‘ํ˜• ์žฅ์†Œ ์ถ”๋ก 
    • leaveSSA: SSAํ˜•ํƒœ์—์„œ ์ผ๋ฐ˜์ ์ธ ํ˜•ํƒœ๋กœ ๋ณต๊ท€
  4. โ€™๋ฐ˜์‘ํ˜• ์ตœ์ ํ™”(HIR)โ€™
    • inferReactiveScopeVariables: ๋ฐ˜์‘ํ˜• ์Šค์ฝ”ํ”„ ๋ณ€์ˆ˜ ์ถ”๋ก 
    • alignMethodCallScopes: ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์Šค์ฝ”ํ”„ ์ •๋ ฌ
    • alignObjectMethodScopes: ๊ฐ์ฒด ๋ฉ”์„œ๋“œ ์Šค์ฝ”ํ”„ ์ •๋ ฌ
    • memoizeFbtOperandsInSameScope: ๋™์ผํ•œ ์Šค์ฝ”ํ”„ ๋‚ด Fbt ํ”ผ์—ฐ์‚ฐ์ž ๋ฉ”๋ชจ์ด์ œ์ด์…˜
    • pruneUnusedLabelsHIR: ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ ˆ์ด๋ธ” ์ œ๊ฑฐ
    • alignReactiveScopesToBlockScopesHIR: ๋ฐ˜์‘ํ˜• ์Šค์ฝ”ํ”„๋ฅผ ๋ธ”๋ก ์Šค์ฝ”ํ”„์— ์ •๋ ฌ
    • mergeOverlappingReactiveScopesHIR: ๊ฒน์น˜๋Š” ๋ฐ˜์‘ํ˜• ์Šค์ฝ”ํ”„ ๋ณ‘ํ•ฉ
    • buildReactiveScopeTerminalsHIR: ๋ฐ˜์‘ํ˜• ์Šค์ฝ”ํ”„ ๋‹จ๋ง ์ƒ์„ฑ
    • buildReactiveFunction: ๋ฐ˜์‘ํ˜• ํ•จ์ˆ˜ ์ƒ์„ฑ
  5. โ€™๋ฐ˜์‘ํ˜• ์ตœ์ ํ™” (Reactive function)โ€™
    • pruneUnusedLabels: ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ๋ ˆ์ด๋ธ” ์ œ๊ฑฐ
    • flattenReactiveLoops: ๋ฐ˜์‘ํ˜• ๋ฃจํ”„ ํ‰ํƒ„ํ™”
    • propagateScopeDependencies: ์Šค์ฝ”ํ”„ ์˜์กด์„ฑ ์ „ํŒŒ
    • pruneNonReactiveDependencies: ๋น„๋ฐ˜์‘ํ˜• ์˜์กด์„ฑ ์ œ๊ฑฐ
    • pruneUnusedScopes: ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ์Šค์ฝ”ํ”„ ์ œ๊ฑฐ
    • mergeReactiveScopesThatInvalidateTogether: ํ•จ๊ป˜ ๋ฌดํšจํ™”๋˜๋Š” ๋ฐ˜์‘ํ˜• ์Šค์ฝ”ํ”„ ๋ณ‘ํ•ฉ
    • pruneAlwaysInvalidatingScopes: ํ•ญ์ƒ ๋ฌดํšจํ™”๋˜๋Š” ์Šค์ฝ”ํ”„ ์ œ๊ฑฐ
    • propagateEarlyReturns: ์กฐ๊ธฐ ๋ฐ˜ํ™˜ ์ „ํŒŒ
    • promoteUsedTemporaries: ์‚ฌ์šฉ๋œ ์ž„์‹œ ๋ณ€์ˆ˜ ํ”„๋กœ๋ชจ์…˜
    • pruneUnusedLValues: ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” LValue ์ œ๊ฑฐ
    • extractScopeDeclarationsFromDestructuring: ํ•ด์ฒด์—์„œ ์Šค์ฝ”ํ”„ ์„ ์–ธ ์ถ”์ถœ
    • stabilizeBlockIds: ๋ธ”๋ก ID ์•ˆ์ •ํ™”
    • renameVariables: ๋ณ€์ˆ˜ ์ด๋ฆ„ ๋ณ€๊ฒฝ
    • pruneHoistedContexts: ํ˜ธ์ด์ŠคํŒ…๋œ ์ปจํ…์ŠคํŠธ ์ œ๊ฑฐ
  6. โ€™Code Generationโ€™
    • codegenFunction: ์ตœ์ข… ์ฝ”๋“œ ์ƒ์„ฑ

๊ณผ์ •๋“ค์„ ๋‹ค ์‚ดํŽด๋ณผ์ง€๋Š” ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ, ์šฐ์„  ์‹œ์ž‘ํ•ด๋ณด์ž.

Lowering

const hir = lower(func, env).unwrap();

๋ฆฌ์•กํŠธ ์ปดํŒŒ์ผ๋Ÿฌ์˜ ์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„์ธ Lowering์€ AST(Abstract Syntax Tree)๋ฅผ HIR(High-level Intermediate Representation)๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ด๋‹ค.

๋จผ์ € ์ปดํŒŒ์ผ์— ๋Œ€ํ•ด ๋‹ค์‹œ ์ง‘๊ณ  ๋„˜์–ด๊ฐ€๋ณด์ž.

to collect information from different places and arrange it in a book, report, or list
[Cambridge Dictionary]

์‚ฌ์ „์  ์˜๋ฏธ๋กœ๋Š” ๋‹ค์–‘ํ•œ ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜์—ฌ ์ฑ…, ๋ณด๊ณ ์„œ ๋˜๋Š” ๋ชฉ๋ก์œผ๋กœ ์ •๋ฆฌํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.
์ปดํ“จํ„ฐ ๊ณผํ•™์—์„œ ์ปดํŒŒ์ผ์€ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๊ธฐ๊ณ„์–ด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์„ ์˜๋ฏธํ•œ๋‹ค.

๊ธฐ๊ณ„์–ด๋กœ ๋ฐ”๊พผ๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•ด, ์ปดํ“จํ„ฐ๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์–ธ์–ด๋กœ ๋ฐ”๊พผ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์ปดํŒŒ์ผ๋Ÿฌ

์†Œ์Šค์ฝ”๋“œ์—์„œ ๋ฐ”๋ฒจ์— ์˜ํ•ด AST๋กœ ๋ณ€ํ™˜๋˜์—ˆ๊ณ  ์ด AST๋ฅผ ํ•œ๋‹จ๊ณ„ ๋” ๋‚ฎ์ถฐ ์ค‘๊ฐ„ํ‘œํ˜„(IR)์œผ๋กœ ๋ณ€ํ™˜ ํ•˜์˜€๊ธฐ์— Lowering์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

๊ทธ๋Ÿผ HIR๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” lower ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด์ž.

// packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts

 /* 
  * ํ•จ์ˆ˜๋ฅผ ์ œ์–ด ํ๋ฆ„ ๊ทธ๋ž˜ํ”„(Control-Flow Graph, CFG)๋กœ ๋‚˜ํƒ€๋‚ด๋Š” ๊ณ ์ˆ˜์ค€ ์ค‘๊ฐ„ ํ˜•ํƒœ(HIR)๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. 
  * ๋ชจ๋“  ์ •์ƒ ์ œ์–ด ํ๋ฆ„์€ ์ •ํ™•ํ•˜๊ฒŒ ๋ชจ๋ธ๋ง๋˜์–ด ์ •ํ™•ํ•œ ํ‘œํ˜„์ˆ˜์ค€(expression-level)์˜ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. 
  * ์ฃผ์š” ์˜ˆ์™ธ๋Š” try/catch ๋ฌธ๊ณผ ์˜ˆ์™ธ์ž…๋‹ˆ๋‹ค. 
  * ํ˜„์žฌ try/catch๋ฅผ ์œ„ํ•ด ์ปดํŒŒ์ผ์„ ๊ฑด๋„ˆ๋›ฐ๊ณ  ์˜ˆ์™ธ์˜ ์ œ์–ด ํ๋ฆ„์„ ๋ชจ๋ธ๋งํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. 
  * ์ด๋Š” JavaScript์˜ ์–ด๋””์—์„œ๋‚˜ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์™ธ์ž…๋‹ˆ๋‹ค. 
  * ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์˜ˆ์™ธ๊ฐ€ ๋Ÿฐํƒ€์ž„์— ์˜ํ•ด ์ฒ˜๋ฆฌ๋  ๊ฒƒ์œผ๋กœ ๊ฐ€์ •ํ•˜๋ฉฐ, 
  * ์ฆ‰ ๋ฉ”๋ชจ์ด์ œ์ด์…˜์„ ๋ฌดํšจํ™”ํ•จ์œผ๋กœ์จ ์ฒ˜๋ฆฌ๋  ๊ฒƒ์œผ๋กœ ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.
  */
export function lower(
  func: NodePath<t.Function>,
  env: Environment,
  bindings: Bindings | null = null,
  capturedRefs: Array<t.Identifier> = [],
  // ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ๊ฒฝ์šฐ(๋žŒ๋‹ค ํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ) lower()๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฐ€์žฅ ๋ฐ”๊นฅ์ชฝ ํ•จ์ˆ˜
  parent: NodePath<t.Function> | null = null
): Result<HIRFunction, CompilerError> {
// ...
}

ํ•จ์ˆ˜ ์œ„์˜ ์ฃผ์„์„ ์ฐธ๊ณ ํ•ด๋ณด๋ฉด, AST(Abstract Syntax Tree)๋ฅผ CFG(Control-Flow Graph ์ดํ•˜ CFG)๋กœ ๋‚˜ํƒ€๋‚ด๋Š” ๊ณ ์ˆ˜์ค€ ์ค‘๊ฐ„ ํ‘œํ˜„(HIR)๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.

AST๋Š” ๊ตฌ๋ฌธ ํŠธ๋ฆฌ๋กœ, ํ”„๋กœ๊ทธ๋žจ์˜ ๊ตฌ๋ฌธ์„ ๋‚˜ํƒ€๋‚ด๋Š” ํŠธ๋ฆฌ ํ˜•ํƒœ์˜ ์ž๋ฃŒ๊ตฌ์กฐ์ธ ๋ฐ˜๋ฉด, CFG๋Š” ์ œ์–ด ํ๋ฆ„ ๊ทธ๋ž˜ํ”„๋กœ, ํ”„๋กœ๊ทธ๋žจ์˜ ์ œ์–ด ํ๋ฆ„์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ทธ๋ž˜ํ”„ ํ˜•ํƒœ์˜ ์ž๋ฃŒ๊ตฌ์กฐ์ด๋‹ค.
๊ทธ๋ž˜ํ”„์˜ ๊ฐ ๋…ธ๋“œ๋Š” ๊ธฐ๋ณธ ๋ธ”๋ก(Basic Block)์ด๋ผ๊ณ  ํ•˜๋Š” ์ฝ”๋“œ์˜ ์—ฐ์†์ ์ธ ๋ถ€๋ถ„์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์—ฃ์ง€(edge)๋Š” ํ•œ ๊ธฐ๋ณธ ๋ธ”๋ก์—์„œ ๋‹ค๋ฅธ ๊ธฐ๋ณธ ๋ธ”๋ก์œผ๋กœ์˜ ์ œ์–ด ํ๋ฆ„์„ ๋‚˜ํƒ€ ๋‚ธ๋‹ค.
์ œ์–ด ํ๋ฆ„ ๊ตฌ์กฐ๋ž€, if/else, switch, loop์™€ ๊ฐ™์€ ๋ถ„๊ธฐ์™€ ๋ฐ˜๋ณต์„ ์˜๋ฏธํ•œ๋‹ค. CFG๋Š” ํ”„๋กœ๊ทธ๋žจ์˜ ๋ชจ๋“  ๊ฐ€๋Šฅํ•œ ์‹คํ–‰ ๊ฒฝ๋กœ๋ฅผ ํฌ์ฐฉํ•˜์—ฌ ์ฝ”๋“œ ์ตœ์ ํ™”์™€ ๋ถ„์„์— ์‚ฌ์šฉ๋œ๋‹ค.

AST & HIR

๊ทธ๋ฆผ์€ ์ด๋ ‡๊ฒŒ ๊ทธ๋ ธ์ง€๋งŒ ๊ฐ ๋…ธ๋“œ๋Š” ์„œ๋กœ ๋งค์นญ๋˜์ง€ ์•Š๋Š”๋‹ค.
์ดํ•ด๋ฅผ ๋•๊ธฐ ์œ„ํ•ด ๊ฒฐ๊ณผ๋ฌผ์„ ๋จผ์ € ์‚ดํŽด๋ณด์ž.
๋‹ค์Œ์€ ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋ฅผ AST์™€ HIR๋กœ ๋ณ€ํ™˜ํ•œ ๊ฒฐ๊ณผ๋ฌผ์ด๋‹ค.

function Component({ color }: { color: string }) {
  if (color === "red") {
    return (<div styles={{ color }}>hello red</div>)
  } else {
    return (<div styles={{ color }}>hello etc</div>)
  }
}

์ดํ•ด๋ฅผ ๋•๊ธฐ์œ„ํ•œ ์˜ˆ์ œ ์ฝ”๋“œ์ด๋‹ค. Component ํ•จ์ˆ˜๋Š” color๊ฐ€ red์ผ ๋•Œ์™€ ์•„๋‹ ๋•Œ๋ฅผ ๋‚˜๋ˆ„์–ด ๋‹ค๋ฅธ JSX๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ด ์ฝ”๋“œ๋ฅผ AST๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ตฌ๋ฌธ์— ๋Œ€ํ•œ ํŠธ๋ฆฌ๋กœ ํ‘œํ˜„ ๋  ๊ฒƒ์ด๋‹ค.

// Component ํ•จ์ˆ˜
FunctionDeclaration
  Identifier
  Parameter
    ObjectBindingPattern
      BindingElement
        Identifier
    TypeLiteral
  Block
    IfStatement
      BinaryExpression
        Identifier
        EqualsEqualsEqualsToken
        StringLiteral
      Block
        ReturnStatement
          ParenthesizedExpression
            JsxElement
              JsxOpeningElement
                Identifier
                //...
//...

๊ทธ๋ž˜ํ”„๋กœ ํ‘œํ˜„ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

AST ๊ทธ๋ž˜ํ”„ (๊ฐ„๋žต)

์ด์ œ ์ด AST๋ฅผ HIR๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋‚˜์˜จ๋‹ค.

function Component
bb0 (block):
  [1] <unknown> $2 = Destructure Let { color: <unknown> color$1 } = <unknown> $0
  [2] <unknown> $11 = LoadLocal <unknown> color$1
  [3] <unknown> $12 = "red"
  [4] <unknown> $13 = Binary <unknown> $11 === <unknown> $12
  [5] If (<unknown> $13) then:bb2 else:bb4 fallthrough=bb1
bb2 (block):
  predecessor blocks: bb0
  [6] <unknown> $3 = LoadLocal <unknown> color$1
  [7] <unknown> $4 = Object { color: <unknown> $3 }
  [8] <unknown> $5 = JSXText "hello red"
  [9] <unknown> $6 = JSX <div styles={<unknown> $4} >{<unknown> $5}</div>
  [10] Return <unknown> $6
bb4 (block):
  predecessor blocks: bb0
  [11] <unknown> $7 = LoadLocal <unknown> color$1
  [12] <unknown> $8 = Object { color: <unknown> $7 }
  [13] <unknown> $9 = JSXText "hello etc"
  [14] <unknown> $10 = JSX <div styles={<unknown> $8} >{<unknown> $9}</div>
  [15] Return <unknown> $10
bb1 (block):
  [16] Unreachable

์—ฌ๊ธฐ์„œ ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜์ง€ ์•Š์•„๋„ ๊ดœ์ฐฎ๋‹ค. ํ›‘์–ด๋งŒ ๋ณด์ž.
์ด ์˜ˆ์ œ ์ฝ”๋“œ์—์„œ์˜ ์ œ์–ดํ๋ฆ„์€ Component ํ•จ์ˆ˜ ๋‚ด์˜ if๋ฌธ์ด๋‹ค. ๊ทธ์— ๋”ฐ๋ผ Component ํ•จ์ˆ˜๋Š” bb0, bb2, bb4 ๊ทธ๋ฆฌ๊ณ  bb1 ๋„ค ๊ฐœ์˜ ๊ธฐ๋ณธ ๋ธ”๋Ÿญ์œผ๋กœ ๋‚˜๋‰˜์—ˆ๋‹ค.

  [5] If (<unknown> $13) then:bb2 else:bb4 fallthrough=bb1

์ด ๋ถ€๋ถ„์„ ๋ณด๋ฉด If๋ฌธ์„ ํ†ตํ•ด bb2๋กœ ๊ฐ€๋Š” ๊ฒฝ์šฐ์™€ bb4๋กœ ๊ฐ€๋Š” ๊ฒฝ์šฐ, ๊ทธ๋ฆฌ๊ณ  fallthrough๋กœ bb1๋กœ ๊ฐ€๋Š” ํ๋ฆ„์ด ํ‘œํ˜„๋˜์–ด ์žˆ๋‹ค. fallthrough๋Š” if๋ฌธ์„ ํ†ต๊ณผํ•œ ํ›„์˜ ํ๋ฆ„์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. ์ด ๊ฒฝ์šฐ์—๋Š” ๋„๋‹ฌ ํ•  ์ˆ˜ ์—†๋Š” ์ฝ”๋“œ๋กœ ํ‘œ์‹œ๋˜์–ด ์žˆ๋‹ค.
์ดํ•ด๋ฅผ ๋•๊ธฐ ์œ„ํ•ด ๊ทธ๋ฆผ์œผ๋กœ ๊ทธ๋ ค๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๋ชจ์Šต์ด๋‹ค.

์ œ์–ด ํ๋ฆ„ ๊ทธ๋ž˜ํ”„

์•„~ ์ด์ œ ์–ด๋–ค ๋ชจ์Šต์œผ๋กœ ๋ณ€ํ™˜๋˜๋Š”์ง€ ๊ฐ์ด ์˜ค๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค.
๊ตฌ๋ฌธ๋‹จ์œ„์˜ ํŠธ๋ฆฌ๋ฅผ ์ด๋Ÿฐ ํ˜•ํƒœ์˜ ์ œ์–ด ํ๋ฆ„์— ๋”ฐ๋ฅธ ๊ทธ๋ž˜ํ”„๋กœ ํ‘œํ˜„ํ•˜๊ธฐ์œ„ํ•œ ๋ณ€ํ™˜ ๊ณผ์ •์ด Lowering์ด๋ผ๋Š” ๊ฒƒ์ด๋‹ค.
๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋ณ€ํ™˜๋œ ํ‘œํ˜„ํ˜•ํƒœ๋ฅผ HIR๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. (React Compiler ์—์„œ)

์ค‘๊ฐ„ ํ•˜์ฐจ ์ง€์ 

์—ฌ๊ธฐ๊นŒ์ง€๋งŒ ์‚ดํŽด๋ณด์•„๋„, HIR์ด ์–ด๋–ค ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ์˜๋ฏธํ•˜๋Š”์ง€ ๋Œ€๋žต์ ์œผ๋กœ ์•Œ ์ˆ˜ ์žˆ์–ด, ์ปดํŒŒ์ผ ๊ณผ์ •์„ ์ดํ•ดํ•˜๋Š”๋ฐ๋Š” ๋ฌธ์ œ ์—†์„ ๊ฒƒ์ด๋‹ค.
๊ทธ๋ ‡๊ธฐ์— ๋‹ค์Œ ํŽธ์œผ๋กœ ๋ฐ”๋กœ ์ด๋™ํ•ด๋„ ์ข‹๋‹ค.

Detach!

๋‹ค์‹œ lower ํ•จ์ˆ˜๋กœ

๋’ค์— ์ด์–ด์„œ๋Š” ๋‹จ์ˆœ ์ง€์  ํ˜ธ๊ธฐ์‹ฌ์œผ๋กœ lowering ๊ณผ์ •์„ ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค. ๋ชจ๋“  ๊ตฌ๋ฌธ์— ๋Œ€ํ•œ lowering ๊ณผ์ •์ด ํฌํ•จ๋˜์–ด์žˆ์–ด. ์ฝ”๋“œ ์ž์ฒด๋Š” 4,000์ค„ ๊ฐ€๋Ÿ‰ ๋œ๋‹ค.
๊ทธ๋Ÿผ ์ฐจ๊ทผ์ฐจ๊ทผ ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ž.

๋‹ค์‹œ ์ฒ˜์Œ์œผ๋กœ ๋Œ์•„์™€ lower ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด์ž.

export function lower(
  func: NodePath<t.Function>,
  env: Environment,
  bindings: Bindings | null = null,
  capturedRefs: Array<t.Identifier> = [],
  // the outermost function being compiled, in case lower() is called recursively (for lambdas)
  parent: NodePath<t.Function> | null = null
): Result<HIRFunction, CompilerError> {}

lower ํ•จ์ˆ˜๋Š” ๋ฐ”๋ฒจ์—์„œ ๋น„๋กฏ๋œ NodePath<t.Function> ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„ HIRFunction์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ CompilerError๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.)
๊ธฐํƒ€ ์ธ์ž๋“ค๋กœ๋Š”, Environment ๊ฐ์ฒด, Bindings, capturedRefs, parent ๋“ฑ์ด ์žˆ๋‹ค.

์ด๋•Œ ์ž…๋ ฅ์œผ๋กœ ๋“ค์–ด์˜ค๋Š” func์˜ ๋‹จ์œ„๋Š” ์–ด๋–ค ๊ฒƒ์ธ๊ฐ€?
์ด์ „์— 1ํŽธ์—์„œ ์‚ดํŽด๋ดค๋˜ ๊ธฐ์–ต์„ ๋˜์‚ด๋ ค๋ณด์ž.

program ๋…ธ๋“œ์˜ traverse ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ ์ˆœํšŒํ•˜๋ฉด์„œ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ๋…ธ๋“œ๋ฅผ ์ฐพ์•„๋‚ด๊ณ , ๊ทธ ๋…ธ๋“œ๋ฅผ compileFn ํ•จ์ˆ˜์— ๋„˜๊ฒจ์ฃผ์—ˆ๋‹ค.
๊ทธ๋ ‡๊ธฐ์— func๋Š” ๋ฐ”๋ฒจ AST๋กœ ๋ถ€ํ„ฐ ๋„˜์–ด์˜ค๋Š” ํ•จ์ˆ˜์— ๋Œ€ํ•œ ๋…ธ๋“œ๊ฐ€ ๋  ๊ฒƒ์ด๋‹ค.
FunctionDeclaration, FunctionExpression, ArrowFunctionExpression ์ด๋ ‡๊ฒŒ๊ฐ€ ๋  ๊ฒƒ์ด๋‹ค.

์ด์–ด์„œโ€ฆ

const builder = new HIRBuilder(env, parent ?? func, bindings, capturedRefs);
const context: Array<Place> = [];

HIRBuilder ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ํ• ๋‹นํ•ด๋‘๊ณ , context ๋ฐฐ์—ด์„ ์ดˆ๊ธฐํ™” ํ•œ๋‹ค. ํ•จ์ˆ˜ ์ปจํ…์ŠคํŠธ ์ •๋ณด๋ฅผ ๋‹ด๋Š” ๋ฐฐ์—ด์ด๋‹ค.

for (const ref of capturedRefs ?? []) {
  context.push({
    kind: "Identifier",
    identifier: builder.resolveBinding(ref),
    effect: Effect.Unknown,
    reactive: false,
    loc: ref.loc ?? GeneratedSource,
  });
}

capturedRefs ๋ฐฐ์—ด์„ ์ˆœํšŒํ•˜๋ฉฐ, context ๋ฐฐ์—ด์— Place ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.
์ด๋ถ€๋ถ„์€ ์žฌ๊ท€๋กœ ํ˜ธ์ถœ๋˜๋Š” ๊ฒฝ์šฐ์ด๋‹ˆ ๋‚˜์ค‘์— ๋‹ค์‹œ ๋ณด๋Ÿฌ์˜ค๊ธฐ๋กœ ํ•˜๊ณ  ์ผ๋‹จ ๋„˜์–ด๊ฐ€์ž.

ํ•จ์ˆ˜ ์ด๋ฆ„ ์ถ”์ถœ(์‹๋ณ„์ž, Identifier)

ํ•จ์ˆ˜๊ฐ€ FunctionDeclaration ๋˜๋Š” FunctionExpression์ธ ๊ฒฝ์šฐ, ํ•จ์ˆ˜์˜ id(Identifier) ์‹๋ณ„์ž๋ฅผ ๊ฐ€์ ธ์™€ id ๋ณ€์ˆ˜์— ํ• ๋‹นํ•œ๋‹ค.

let id: string | null = null;
if (func.isFunctionDeclaration() || func.isFunctionExpression()) {
  const idNode = (
    func as NodePath<t.FunctionDeclaration | t.FunctionExpression>
  ).get("id");
  if (hasNode(idNode)) {
    id = idNode.node.name;
  }
}

FunctionDeclaration(ํ•จ์ˆ˜ ์„ ์–ธ์‹) ๋…ธ๋“œ์™€ FunctionExpression(ํ•จ์ˆ˜ ํ‘œํ˜„์‹) ๋…ธ๋“œ๋Š” ์‹๋ณ„์ž๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ, ArrowFunctionExpression(ํ™”์‚ดํ‘œ ํ•จ์ˆ˜) ๋…ธ๋“œ๋Š” ์‹๋ณ„์ž๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์—†๋‹ค.

// FunctionDeclaration
function foo() {}
// FunctionExpression
const foo = function bar() {}
// ArrowFunctionExpression
const foo = () => {}

์ธ์ž ์ถ”์ถœ(Parameters)

ํ•จ์ˆ˜์˜ ์ธ์ž๋ฅผ ์ถ”์ถœํ•˜๊ณ , params ๋ฐฐ์—ด์— Place ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

params: Array<Identifier | Pattern | RestElement> (required)

params์—๋Š” Identifier, Pattern, RestElement๊ฐ€ ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
Identifier๋Š” ์‹๋ณ„์ž, Pattern์€ ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด ํŒจํ„ด, RestElement๋Š” ๋‚˜๋จธ์ง€ ์š”์†Œ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.

const params: Array<Place | SpreadPattern> = [];
func.get("params").forEach((param) => {
  if (param.isIdentifier()) {}
  else if (param.isObjectPattern() || param.isArrayPattern() || param.isAssignmentPattern()) {}
  else if (param.isRestElement()) {}
  else {}
  // ...
});

Identifier(๋‹จ์ผ ๋ณ€์ˆ˜ ์ด๋ฆ„)

// ์˜ˆ์‹œ
const greet = function(name) { // name์ด Identifier
  console.log(`Hello, ${name}!`);
};

์ธ์ž๊ฐ€ Identifier์ธ ๊ฒฝ์šฐ, Place ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ params ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•œ๋‹ค.

if (param.isIdentifier()) {
  const binding = builder.resolveIdentifier(param);
  const place: Place = {
    kind: "Identifier",
    identifier: binding.identifier,
    effect: Effect.Unknown,
    reactive: false,
    loc: param.node.loc ?? GeneratedSource,
  };
  params.push(place);
}

์•ž์œผ๋กœ ์ž์ฃผ ๋“ฑ์žฅํ•  ๊ฒƒ์ธ๋ฐ Place ๊ฐ์ฒด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜๋˜์–ด ์žˆ๋‹ค.

/*
 * ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์“ธ ์ˆ˜ ์žˆ๋Š” ์žฅ์†Œ:
 * - ๋ณ€์ˆ˜(์‹๋ณ„์ž)
 * - ์‹๋ณ„์ž๋กœ์˜ ๊ฒฝ๋กœ
 */
export type Place = {
  kind: "Identifier"; // ์ข…๋ฅ˜
  identifier: Identifier; // ์‹๋ณ„์ž
  effect: Effect; // The effect with which a value is modified. (์ˆ˜์ •๋œ ๊ฐ’์˜ ํšจ๊ณผ)
  reactive: boolean;
  loc: SourceLocation;
};

ObjectPattern(๊ฐ์ฒด ํŒจํ„ด), ArrayPattern(๋ฐฐ์—ด ํŒจํ„ด), AssignmentPattern(ํ• ๋‹น ํŒจํ„ด)

// ์˜ˆ์‹œ
// ObjectPattern
const greet = function({ name }) { 
  console.log(`Hello, ${name}!`);
};

// ArrayPattern
const greet = function([name]) { 
  console.log(`Hello, ${name}!`);
};

// AssignmentPattern
const greet = function(name = 'world') { 
  console.log(`Hello, ${name}!`);
};

์ธ์ž๊ฐ€ ObjectPattern, ArrayPattern, AssignmentPattern์ธ ๊ฒฝ์šฐ, ์ž„์‹œ ๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Place ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ params ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•œ๋‹ค. lowerAssignment ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ํ• ๋‹น์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

else if (
  param.isObjectPattern() ||
  param.isArrayPattern() ||
  param.isAssignmentPattern()
) {
  const place: Place = {
    kind: "Identifier",
    identifier: builder.makeTemporary(),
    effect: Effect.Unknown,
    reactive: false,
    loc: param.node.loc ?? GeneratedSource,
  };
  params.push(place);
  lowerAssignment(
    builder,
    param.node.loc ?? GeneratedSource,
    InstructionKind.Let,
    param,
    place,
    "Assignment"
  );
}

RestElement(๋‚˜๋จธ์ง€ ์š”์†Œ)

// ์˜ˆ์‹œ
const greet = function(...names) { 
  console.log(`Hello, ${names.join(', ')}!`);
};

์ธ์ž๊ฐ€ RestElement์ธ ๊ฒฝ์šฐ, ์ž„์‹œ ๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Spread ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ params ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•œ๋‹ค.

else if (param.isRestElement()) {
  const place: Place = {
    kind: "Identifier",
    identifier: builder.makeTemporary(),
    effect: Effect.Unknown,
    reactive: false,
    loc: param.node.loc ?? GeneratedSource,
  };
  params.push({
    kind: "Spread",
    place,
  });
  lowerAssignment(
    builder,
    param.node.loc ?? GeneratedSource,
    InstructionKind.Let,
    param.get("argument"),
    place,
    "Assignment"
  );
}

์ด๋ ‡๊ฒŒ ํ•จ์ˆ˜์˜ ์ธ์ž๋ฅผ ์ถ”์ถœํ•˜๊ณ  params ๋ฐฐ์—ด์— Place ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค.

์ด์ œ ํ•จ์ˆ˜์˜ ๋ณธ๋ฌธ์„ ์ถ”์ถœํ•˜๊ณ , body ๋ฐฐ์—ด์— Instruction ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ณผ์ •์„ ์‚ดํŽด๋ณด์ž.

ํ•จ์ˆ˜ ๋ณธ๋ฌธ ์ถ”์ถœ(Body)

let directives: Array<string> = [];
const body = func.get("body");

ํ•จ์ˆ˜์˜ ๋ณธ๋ฌธ์„ ์ถ”์ถœํ•˜๊ณ , directives ๋ฐฐ์—ด์„ ์ดˆ๊ธฐํ™”ํ•œ๋‹ค.

Expression(ํ‘œํ˜„์‹)

if (body.isExpression()) {
  const fallthrough = builder.reserve("block");
  const terminal: ReturnTerminal = {
    kind: "return",
    loc: GeneratedSource,
    value: lowerExpressionToTemporary(builder, body),
    id: makeInstructionId(0),
  };
  builder.terminateWithContinuation(terminal, fallthrough);
}

BlockStatement(๋ธ”๋ก๋ฌธ)

๋ธ”๋Ÿญ๋ฌธ์ผ ๊ฒฝ์šฐ, lowerStatement ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด body๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค.

else if (body.isBlockStatement()) {
  lowerStatement(builder, body);
  directives = body.get("directives").map((d) => d.node.value.value);
}

์ด ๋ถ€๋ถ„์ด ์‚ฌ์‹ค ์ฝ”์–ดํ•œ ๋ถ€๋ถ„์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
lowerStatement ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด์ž.

function lowerStatement(
  builder: HIRBuilder,
  stmtPath: NodePath<t.Statement>,
  label: string | null = null
): void {
  const stmtNode = stmtPath.node;
  switch (stmtNode.type) {
    case "ThrowStatement":
    case "ReturnStatement":
    case "IfStatement":
    case "BlockStatement":
    case "BreakStatement":
    case "ContinueStatement":
    case "ForStatement":
    case "WhileStatement":
    case "LabeledStatement":
    case "SwitchStatement":
    case "VariableDeclaration":
    case "ExpressionStatement":
    case "DoWhileStatement":
    case "FunctionDeclaration":
    case "ForOfStatement":
    case "ForInStatement":
    case "DebuggerStatement":
    case "EmptyStatement":
    case "TryStatement":
    // ------- skip -------
    case "TypeAlias":
    case "TSTypeAliasDeclaration":

    // --- unsupported ---
    case "ClassDeclaration":
    // ~
    case "WithStatement":
    default:

stmtPath(Statement Path)์˜ ๋…ธ๋“œ ํƒ€์ž…์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•œ๋‹ค.
lowerํ•จ์ˆ˜์— ์˜ํ•ด์„œ ํ˜ธ์ถœ๋  ๋•Œ, stmtPath๋Š” BlockStatement๊ฐ€ ๋“ค์–ด์˜ฌ ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿผ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด์„œ ์‚ดํŽด๋ณด์ž.

// ์˜ˆ์‹œ
function complexExample(x) {
  let result = 0;
  if (x > 0) {
    result = x * 2;
  } else {
    result = x * 3;
  }
  return result;
}

ํ•จ์ˆ˜ complexExample์˜ body๋Š” BlockStatement์ด๋‹ค. ์ด BlockStatement๋ฅผ lowerStatement ํ•จ์ˆ˜์— ๋„˜๊ฒจ๋ณด์ž.

case "BlockStatement": {
  const stmt = stmtPath as NodePath<t.BlockStatement>;
  const statements = stmt.get("body");

stmtPath๋ฅผ BlockStatement๋กœ ํƒ€์ž… ์บ์ŠคํŒ…ํ•˜๊ณ , body๋ฅผ ์ถ”์ถœํ•œ๋‹ค.
body๋Š” NodePath<t.Statement>[] ํƒ€์ž…์œผ๋กœ, ๋ธ”๋ก๋ฌธ ๋‚ด์˜ ๊ฐ ๋ฌธ์žฅ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.

for (const s of statements) {
  lowerStatement(builder, s);
}

statements๋“ค์„ ์ˆœํšŒ ํ•˜๋ฉด์„œ, lowerStatement ํ•จ์ˆ˜๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•œ๋‹ค.

blockStatment ์ฒ˜๋ฆฌ ์ค‘์— ์žฌ๊ท€์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณผ์ •๋ง๊ณ ๋„ ๋‹ค๋ฅธ ๊ณผ์ •์ด ํ•˜๋‚˜ ๋” ์žˆ๋‹ค.

โ€˜constโ€™ ๋ณ€์ˆ˜ ์„ ์–ธ์˜ Hoisting ์ฒ˜๋ฆฌ

๋จผ์ € ์–ด๋–ค ๋ฐ”์ธ๋”ฉ์ด ์„ ์–ธ๋˜๊ธฐ ์ „์— ์ฐธ์กฐ๋  ๊ฒฝ์šฐ ํ˜ธ์ด์ŠคํŒ…๋  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•œ ๋’ค,
ํ•ด๋‹น ์„ ์–ธ์„ ์ฐธ์กฐ๋˜๋Š” ๊ฐ€์žฅ ๋น ๋ฅธ ์ง€์ (์ฆ‰, ๋ฐ”๋กœ ์•ž์˜ ์ตœ์ƒ์œ„ ๋ฌธ)์œผ๋กœ ์ปจํ…์ŠคํŠธ ๋ณ€์ˆ˜๋กœ ํ˜ธ์ด์ŠคํŒ…ํ•œ๋‹ค.\

const hoistableIdentifiers: Set<t.Identifier> = new Set();

for (const [, binding] of Object.entries(stmt.scope.bindings)) {
  // refs to params are always valid / never need to be hoisted
  if (binding.kind !== "param") {
    hoistableIdentifiers.add(binding.identifier);
  }
}

hoistableIdentifiers๋ผ๋Š” Set์„ ์ƒ์„ฑํ•˜๊ณ , stmt.scope.bindings๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ identifier ๋“ค์„ hoistableIdentifiers์— ์ถ”๊ฐ€ํ•œ๋‹ค.
stmt.scope.bindings๋Š” ํ˜„์žฌ ๋ธ”๋ก์˜ ๋ฐ”์ธ๋”ฉ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋‹ค.
๋ถ€๋ชจ๋‚˜ ์ž์‹ ๋ธ”๋ก์˜ ๋ฐ”์ธ๋”ฉ ์ •๋ณด๋Š” ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค.
์ด๋•Œ, const, let, var์ด hoistableIdentifiers์— ์ถ”๊ฐ€๋œ๋‹ค.

๊ฐ ๋ฌธ์„ ์ˆœํšŒํ• ๋•Œ, ํ˜ธ์ด์ŠคํŒ…์ด ํ•„์š”ํ•œ ์š”์†Œ๋“ค์„ ๋„ฃ์„ willHoist๋ผ๋Š” Set์„ ์ƒ์„ฑํ•œ๋‹ค.

for (const s of statements) {
  const willHoist = new Set<NodePath<t.Identifier>>();
  // ...
}

ํ•จ์ˆ˜ ์ปจํ…์ŠคํŠธ์˜ ๊นŠ์ด๋ฅผ ์ถ”์ ํ•œ๋‹ค. ์‹๋ณ„์ž ์ฐธ์กฐ๊ฐ€ ๋‚ด๋ถ€ ํ•จ์ˆ˜์—์„œ ๋ฐœ์ƒํ•˜๋Š”์ง€ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค.
traverse๋ฅผ ํ†ตํ•ด ํƒ์ƒ‰ํ•˜๋ฉด์„œ FunctionExpression, FunctionDeclaration, ArrowFunctionExpression, ObjectMethod ๋…ธ๋“œ๋ฅผ ๋งŒ๋‚˜๋ฉด ํ•จ์ˆ˜ ๊นŠ์ด๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๊ณ , ๋…ธ๋“œ๋ฅผ ๋น ์ ธ๋‚˜์˜ค๋ฉด ํ•จ์ˆ˜ ๊นŠ์ด๋ฅผ ๊ฐ์†Œ์‹œํ‚จ๋‹ค.

let fnDepth = s.isFunctionDeclaration() ? 1 : 0;
const withFunctionContext = {
  enter: (): void => {
    fnDepth++; // ํ•จ์ˆ˜ ๊นŠ์ด ์ฆ๊ฐ€
  },
  exit: (): void => {
    fnDepth--; // ํ•จ์ˆ˜ ๊นŠ์ด ๊ฐ์†Œ
  },
};
s.traverse({
  FunctionExpression: withFunctionContext, // ํ•จ์ˆ˜ ํ‘œํ˜„์‹
  FunctionDeclaration: withFunctionContext, // ํ•จ์ˆ˜ ์„ ์–ธ์‹
  ArrowFunctionExpression: withFunctionContext,  // ํ™”์‚ดํ‘œ ํ•จ์ˆ˜
  ObjectMethod: withFunctionContext, // ๊ฐ์ฒด ๋ฉ”์„œ๋“œ
  // ...
});

๋ฌธ์„ ์ˆœํšŒํ•˜๋ฉด์„œ, ์‹๋ณ„์ž๋ฅผ ์ฐพ๋Š”๋‹ค.
์‹๋ณ„์ž๊ฐ€ ์ฐธ์กฐ๋˜์ง€ ์•Š๊ฑฐ๋‚˜, ๋ถ€๋ชจ๊ฐ€ ํ• ๋‹น ํ‘œํ˜„์‹์ด ์•„๋‹Œ ๊ฒฝ์šฐ, ๋„˜์–ด๊ฐ„๋‹ค.
์‹๋ณ„์ž๊ฐ€ ์ฐธ์กฐ๋˜๊ณ  hoistableIdentifiers์— ์žˆ์œผ๋ฉฐ, ํ•จ์ˆ˜ ๊นŠ์ด๊ฐ€ 0๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ ๋ฐ”์ธ๋”ฉ์ด hoisted์ธ ๊ฒฝ์šฐ, willHoist์— ์ถ”๊ฐ€ํ•œ๋‹ค.

s.traverse({
  // ...
  Identifier(id: NodePath<t.Identifier>) {
    const id2 = id;
    if (
      !id2.isReferencedIdentifier() &&
      // isReferencedIdentifier is broken and returns false for reassignments
      id.parent.type !== "AssignmentExpression"
    ) {
      return;
    }
    const binding = id.scope.getBinding(id.node.name);
    /*
    * ์‹๋ณ„์ž ์„ ์–ธ์„ ํ˜ธ์ด์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
    * 1. ์ฐธ์กฐ๊ฐ€ ๋‚ด๋ถ€ ํ•จ์ˆ˜ ๋‚ด์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ
    * ๋˜๋Š”
    * 2. ์„ ์–ธ ์ž์ฒด๊ฐ€ ํ˜ธ์ด์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ
    */
    if (
      binding != null &&
      hoistableIdentifiers.has(binding.identifier) &&
      (fnDepth > 0 || binding.kind === "hoisted")
    ) {
      willHoist.add(id);
    }
  },
});

์ดํ›„ ๋‹ค์‹œ ๋…ธ๋“œ๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ, hoistableIdentifiers์— ์žˆ๋Š” ์‹๋ณ„์ž๋ฅผ ์‚ญ์ œํ•œ๋‹ค.

s.traverse({
  Identifier(path: NodePath<t.Identifier>) {
    if (hoistableIdentifiers.has(path.node)) {
      hoistableIdentifiers.delete(path.node);
    }
  },
});

willHost๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ, identifier๋ฅผ resolveIdentifier๋ฅผ ํ†ตํ•ด ๊ณ ์œ ํ•œ ์‹๋ณ„์ž๋กœ ๋ณ€ํ™˜ํ•˜๊ณ , lowerValueToTemporary ํ•จ์ˆ˜ ํ†ตํ•ด ์ž„์‹œ๋ณ€์ˆ˜์— DeclareContext๋ฅผ ์ƒ์„ฑํ•˜๊ณ  builder์— pushํ•œ๋‹ค.
์ดํ›„ ์ „์—ญ ํ™˜๊ฒฝ์˜ #contextIdentifiers, #hoistedIdentifiers์— ์ถ”๊ฐ€ํ•œ๋‹ค.

// Hoist declarations that need it to the earliest point where they are needed
for (const id of willHoist) {
  const binding = stmt.scope.getBinding(id.node.name);
  if (builder.environment.isHoistedIdentifier(binding.identifier)) {
    // Already hoisted
    continue;
  }
  const identifier = builder.resolveIdentifier(id);
  const place: Place = {
    effect: Effect.Unknown,
    identifier: identifier.identifier,
    kind: "Identifier",
    reactive: false,
    loc: id.node.loc ?? GeneratedSource,
  };
  lowerValueToTemporary(builder, {
    kind: "DeclareContext",
    lvalue: {
      kind: InstructionKind.HoistedConst,
      place,
    },
    loc: id.node.loc ?? GeneratedSource,
  });
  builder.environment.addHoistedIdentifier(binding.identifier);

๋‚˜์ค‘์— ์ฝ”๋“œ ์ƒ์„ฑ ์ „์— DeclareContext๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ์—ฐ๊ด€๋œ StoreContext๋ฅผ ๋‹ค์‹œ ๋ณ€ํ™˜ํ•˜์—ฌ ์›๋ž˜ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋ณต์›ํ•œ๋‹ค.
๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ ์„ ์–ธ์— ๋Œ€ํ•œ ํ˜ธ์ด์ŠคํŒ…์€ ํ–ฅํ›„ ๊ตฌํ˜„๋  ์˜ˆ์ •์ด๋‹ค.

ํ˜ธ์ด์ŠคํŒ…(Hoisting)์„ ํ•˜๋Š” ์ด์œ ๊ฐ€ ๋ญ˜๊นŒ?

๊ทธ๋Ÿฐ๋ฐ, ์ด๋ฒˆ์— ์‚ดํŽด๋ณธ ๊ฒƒ์€ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ•˜๋Š” ํ˜ธ์ด์ŠคํŒ…์ด๋‹ค.

const ํ˜ธ์ด์ŠคํŒ…์„ ํ•ด์ฃผ๋Š” ์ด์œ ๋Š”, ๋ญ˜๊นŒ? ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์‚ดํŽด๋ณด์ž. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๋ณด๋ฉด ์–‘์งˆ์˜ ์˜ˆ์‹œ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค. /src/__tests__/fixtures/compiler ๋กœ ์ด๋™ํ•ด๋ณด์ž.
๊ฐ„๊ฒฐํ•ด๋ณด์ด๋Š” ์ฝ”๋“œ ํ•˜๋‚˜๋ฅผ ์ค์—ˆ๋‹ค. โ€œhoisting-simple-const-declaration.expected.mdโ€ ํŒŒ์ผ์„ ์—ด์–ด๋ณด์ž.

function hoisting() {
  const foo = () => {
    return bar + baz;
  };
  const bar = 3;
  const baz = 2;
  return foo(); // OK: called outside of TDZ for bar/baz
}

๊ฐ„๋‹จํ•œ ํ˜ธ์ด์ŠคํŒ… ์˜ˆ์ œ์ด๋‹ค. ์ด๊ฑธ HIR๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณ€ํ•œ๋‹ค.

function hoisting
bb0 (block):
  [1] <unknown> $1 = DeclareContext HoistedConst <unknown> bar$0
  [2] <unknown> $3 = DeclareContext HoistedConst <unknown> baz$2
  [3] <unknown> $4 = LoadContext <unknown> bar$0
  [4] <unknown> $5 = LoadContext <unknown> baz$2
  [5] <unknown> $10 = Function  @deps[<unknown> $4,<unknown> $5] @context[<unknown> bar$0,<unknown> baz$2] @effects[]:
      bb1 (block):
        [1] <unknown> $6 = LoadContext <unknown> bar$0
        [2] <unknown> $7 = LoadContext <unknown> baz$2
        [3] <unknown> $8 = Binary <unknown> $6 + <unknown> $7
        [4] Return <unknown> $8
  [6] <unknown> $12 = StoreLocal Const <unknown> foo$11 = <unknown> $10
  [7] <unknown> $13 = 3
  [8] <unknown> $14 = StoreContext Reassign <unknown> bar$0 = <unknown> $13
  [9] <unknown> $15 = 2
  [10] <unknown> $16 = StoreContext Reassign <unknown> baz$2 = <unknown> $15
  [11] <unknown> $17 = LoadLocal <unknown> foo$11
  [12] <unknown> $18 = Call <unknown> $17()
  [13] Return <unknown> $18

๋งจ ์œ„์˜DeclareContext HoistedConst๋ฅผ ํ†ตํ•ด bar, baz๊ฐ€ ํ˜ธ์ด์ŠคํŒ… ๋˜์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
ํ  ์—ฌ๊ธฐ๊นŒ์ง€ ๋ดค์„๋• ๋”ฑํžˆ ๋ญ”๊ฐ€ ์™€๋‹ฟ๋Š” ๊ฒƒ์ด ์—†๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์ด์ œ ํ˜ธ์ด์ŠคํŒ…์„ ๋‹ค ๋„๊ณ  ์ƒ์„ฑํ•ด๋ณด์ž. ์ „๋ถ€ ์ฃผ์„์ฒ˜๋ฆฌ ํ•˜๊ณ  ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ๋‹ค์‹œ ๋Œ๋ ค๋ณด์•˜๋‹ค.

// ๐Ÿ˜ด
case "BlockStatement": {
  const stmt = stmtPath as NodePath<t.BlockStatement>;
  const statements = stmt.get("body");
  // const hoistableIdentifiers: Set<t.Identifier> = new Set();
  // for (const [, binding] of Object.entries(stmt.scope.bindings)) {
  //   // refs to params are always valid / never need to be hoisted
  //   if (binding.kind !== "param") {
  //     hoistableIdentifiers.add(binding.identifier);
  //   }
  // }

  for (const s of statements) {
    // const willHoist = new Set<NodePath<t.Identifier>>();
    // /*
    //  * If we see a hoistable identifier before its declaration, it should be hoisted just
    //  * before the statement that references it.
    //  */
    // let fnDepth = s.isFunctionDeclaration() ? 1 : 0;
    // const withFunctionContext = {
    //   enter: (): void => {
    //     fnDepth++;
    //   },
    //   exit: (): void => {
    //     fnDepth--;
    //   },
    // ...
    lowerStatement(builder, s);
  }

์šฐ์„  ์ด๋Ÿฐ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

Todo: [hoisting] EnterSSA: Expected identifier to be defined before being used. \
Identifier bar$0 is undefined (5:5)

๋ญ”๊ฐ€ ๋‹ฌ๋ผ์กŒ๋‹ค. bar$0์ด ์ •์˜๋˜๊ธฐ ์ „์— ์‚ฌ์šฉ๋˜์—ˆ๋‹ค๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.
HIR ์ฝ”๋“œ๋„ ์‚ดํŽด๋ณด์ž. ์–ด๋–ค ์ฐจ์ด๊ฐ€ ์ƒ๊ฒผ์„๊นŒ?

function hoisting
bb0 (block):
  [1] <unknown> $1 = LoadLocal <unknown> bar$0
  [2] <unknown> $3 = LoadLocal <unknown> baz$2
  [3] <unknown> $8 = Function  @deps[<unknown> $1,<unknown> $3] @context[<unknown> bar$0,<unknown> baz$2] @effects[]:
      bb1 (block):
        [1] <unknown> $4 = LoadLocal <unknown> bar$0
        [2] <unknown> $5 = LoadLocal <unknown> baz$2
        [3] <unknown> $6 = Binary <unknown> $4 + <unknown> $5
        [4] Return <unknown> $6
  [4] <unknown> $10 = StoreLocal Const <unknown> foo$9 = <unknown> $8
  [5] <unknown> $11 = 3
  [6] <unknown> $12 = StoreLocal Const <unknown> bar$0 = <unknown> $11
  [7] <unknown> $13 = 2
  [8] <unknown> $14 = StoreLocal Const <unknown> baz$2 = <unknown> $13
  [9] <unknown> $15 = LoadLocal <unknown> foo$9
  [10] <unknown> $16 = Call <unknown> $15()
  [11] Return <unknown> $16

์˜คโ€ฆ DeclareContext HoistedConst๊ฐ€ ์‚ฌ๋ผ์กŒ๋‹ค. ์ด๊ฑด ๋‹น์—ฐํ•˜๊ฒ ์ง€. ํ˜ธ์ด์ŠคํŒ…์„ ํ•˜์ง€ ์•Š์•˜์œผ๋‹ˆ๊นŒ.
๊ทธ๋กœ์ธํ•ด [1] ์—์„œ ์ •์˜๋˜๊ธฐ ์ „์— LoadLocal์ด ๋ฐœ์ƒํ•˜์—ฌ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด๋‹ค. ๋’ค๋Šฆ๊ฒŒ [6], [8],์—์„œ StoreLocal Const๋กœ ์ •์˜๋˜์—ˆ์ง€๋งŒ, ์ด๋ฏธ ์‚ฌ์šฉ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

javascript๊ฐ€ ์‹คํ–‰๋˜๋Š” ํ™˜๊ฒฝ์—์„œ๋Š” ์—”์ง„์ด ํ˜ธ์ด์ŠคํŒ…์„ ํ•ด์ฃผ์ง€๋งŒ, HIR๋กœ ๋ณ€ํ™˜๋˜๊ณ ๋‚˜๋ฉด ๋‹ค๋ฅธ ์–ธ์–ด๋กœ ๋ณ€ํ™˜๋œ ๊ฒƒ์ด๋‚˜ ๋‹ค๋ฆ„์—†๋‹ค. ๊ทธ๋ ‡๊ธฐ์—, ์ตœ๋Œ€ํ•œ javascript๋ฅผ ๋ชจ๋ธ๋งํ•˜์—ฌ์•ผ ํ–ˆ๋˜ ๊ฒƒ์ด์ง€ ์•Š์„๊นŒ ์‹ถ๋‹ค. ๊ทธ์™ธ์—๋„ ์ด๋กœ์ธํ•ด DCE๋‚˜ Const Propagation ๋“ฑ ๋‹ค๋ฅธ ์ตœ์ ํ™” ๊ณผ์ •์—์„œ๋„ ๋‚˜์ค‘์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ ์‹ถ์€ ์ƒ๊ฐ์„ ๋‚จ๊ฒจ๋ณธ๋‹ค.

๊ทธ๋ž˜์„œ ๊ถ๊ธˆํ•ด์„œ Compiler ๊ฐœ๋ฐœ์ž lauren ์—๊ฒŒ ๋ฌผ์–ด๋ณด์•˜๋‹ค. ์งˆ๋ฌธ์„ ๋˜์ง€๊ณ ๋‚˜์„œ์•ผ ๋จธ์“ฑํ•˜๊ฒŒ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ์ฐพ์•„๋ณด๊ฒŒ ๋˜์—ˆ๊ณ  ๊ตฌํ˜„์ด ํ•„์—ฐ์ ์ด์—ˆ์Œ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.
(์งˆ๋ฌธ ์•ž์ชฝ์ด ํ•˜๋‚˜ ์ž˜๋ฆฌ๊ธดํ–ˆ๋Š”๋ฐ)

์š”๋Ÿฐ ์นœ์ ˆํ•œ ๋‹ต๋ณ€์„ ๋ฐ›์•˜๋‹ค. ๊ถ๊ธˆํ•  ๋•Œ ๋ฐ”๋กœ ์งˆ๋ฌธ ํ•ด๋ณผ ์ˆ˜ ์žˆ๋Š” ์šฉ๊ธฐ๊ฐ€(?) ์กฐ๊ธˆ ์ƒ๊ธด ๊ฒƒ ๊ฐ™๋‹ค.
const hoisting์ด ๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ์˜€๊ธฐ์—, ๋จผ์ € ๊ตฌํ˜„ ๋˜์—ˆ๊ณ , ์•„์ง ๋‹ค๋ฅธ ์ตœ์ ํ™” ๊ณผ์ •์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š์•˜๋‹ค๊ณ  ํ•œ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด, ์ด์ œ ๋‹ค์‹œ lowerStatement ํ•จ์ˆ˜๋กœ ๋Œ์•„๊ฐ€์„œ, ๋‹ค๋ฅธ ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ž. ์ด์ œ ์žฌ๊ท€์ ์œผ๋กœ ๋Œ๋ฉด์„œ ๊ฐ์ผ€์ด์Šค์— ๋งž๋Š” lowering์„ ์ˆ˜ํ–‰ํ•  ๊ฒƒ์ด๋‹ค. ์ด ๊ฒƒ์„ ๋‹ค ์ ๊ณ  ์žˆ๋…ธ๋ผ๋ฉด, ์ฝ๋Š” ์ด๋„ ์ง€๋ฃจํ•  ๊ฒƒ ๊ฐ™์•„์„œ. ๋‹ค์Œ์— ์งค๋ง‰ํ•˜๊ฒŒ ๋ช‡๊ฐœ๋งŒ ์‚ดํŽด๋ณด๊ฑฐ๋‚˜ ์ด๋ฒˆ ๊ธ€์— ๋‚˜์ค‘์— ์ฒจ๋ถ€ํ•ด๋‘๊ฒ ๋‹ค. ์จ‹๋“  ์žฌ๊ท€์ ์œผ๋กœ body ๋ถ€๋ถ„์„ lowering ํ•˜๊ณ  ๋‚˜๋ฉด ์•„๋ž˜๋กœ ๋น ์ ธ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค.

builder.terminate(
  {
    kind: "return",
    loc: GeneratedSource,
    value: lowerValueToTemporary(builder, {
      kind: "Primitive",
      value: undefined,
      loc: GeneratedSource,
    }),
    id: makeInstructionId(0),
  },
  null
);

return Ok({
  id,
  params,
  fnType: parent == null ? env.fnType : "Other",
  returnType: null, // TODO: extract the actual return type node if present
  body: builder.build(),
  context,
  generator: func.node.generator === true,
  async: func.node.async === true,
  loc: func.node.loc ?? GeneratedSource,
  env,
  effects: null,
  directives,
});

builder.terminate๋ฅผ ํ†ตํ•ด return์„ ์ƒ์„ฑํ•˜๊ณ , builder.build()๋ฅผ ํ†ตํ•ด ์ƒ์„ฑํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
๊ทธ๋ ‡๊ฒŒ HIRFunction์„ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ๋œ๋‹ค.

const hir = lower(func, env).unwrap();

HIR๋กค ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์—์„œ builder๊ฐ€ ๋งŽ์€ ์—ญํ• ์„ ํ•˜๋Š”๋ฐ, ์ด๋Š” ๊นŠ๊ฒŒ ์‚ดํŽด๋ณด์ง€ ๋ชปํ–ˆ๋‹ค. ์ด๋Š” ๋‹ค์Œ์— ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

๋งˆ๋ฌด๋ฆฌ

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” lower ํ•จ์ˆ˜๋ฅผ ์‚ดํŽด๋ณด์•˜๋‹ค.

  • lower ํ•จ์ˆ˜๋Š” ๋ฐ”๋ฒจ AST๋กœ ๋ถ€ํ„ฐ HIRFunction์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ด๋‹ค.
  • ํ•จ์ˆ˜์˜ ์ธ์ž, ์‹๋ณ„์ž, ๋ณธ๋ฌธ์„ ์ถ”์ถœํ•˜๊ณ  HIRBuilder๋ฅผ ํ†ตํ•ด Instruction ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.
  • const ๋ณ€์ˆ˜ ์„ ์–ธ์˜ ํ˜ธ์ด์ŠคํŒ…์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • lowerStatement ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๊ฐ์ข… ๋ฌธ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • lower ํ•จ์ˆ˜๋Š” ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋ฉฐ, ํ•จ์ˆ˜์˜ ๋ณธ๋ฌธ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  HIRFunction์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

๊ทธ๋Ÿผ ์•ˆ๋…•!

์ด์šฉํ•œ ํˆด

์ฐธ๊ณ ์ž๋ฃŒ

RSS ๊ตฌ๋