Skip to content
Go back

deepkit协程学习

Updated:

协程(coroutine)在deepkit compiler中主要用于处理复杂的类型操作,特别是需要延迟执行或者分步骤处理的类型计算。

  1. 基本结构:
class CompilerProgram {
    protected activeCoRoutines: { ops: ReflectionOp[] }[] = [];
    protected coRoutines: { ops: ReflectionOp[] }[] = [];
}

主要用途: 协程主要用在以下几种场景:

a) 条件类型(Conditional Types):

protected extractPackStructOfType(node: Node, program: CompilerProgram): void {
    case SyntaxKind.ConditionalType: {
        const narrowed = node as ConditionalTypeNode;

        program.pushCoRoutine();
        this.extractPackStructOfType(narrowed.trueType, program);
        const trueProgram = program.popCoRoutine();

        program.pushCoRoutine();
        this.extractPackStructOfType(narrowed.falseType, program);
        const falseProgram = program.popCoRoutine();

        program.pushOp(ReflectionOp.jumpCondition, trueProgram, falseProgram);
    }
}

b) 映射类型(Mapped Types):

case SyntaxKind.MappedType: {
    const narrowed = node as MappedTypeNode;

    program.pushCoRoutine();
    if (narrowed.type) {
        this.extractPackStructOfType(narrowed.type, program);
    }
    const coRoutineIndex = program.popCoRoutine();

    program.pushOp(ReflectionOp.mappedType, coRoutineIndex, modifier);
}

工作原理:

pushCoRoutine(): void {
    this.pushFrame(true); // 协程有隐式栈帧
    this.activeCoRoutines.push({ ops: [] });
}

popCoRoutine(): number {
    const coRoutine = this.activeCoRoutines.pop();
    if (!coRoutine) throw new Error('No active co routine found');
    this.popFrameImplicit();

    if (this.mainOffset === 0) {
        this.mainOffset = 2; // 添加 JUMP + index 当构建程序时
    }
    const startIndex = this.mainOffset;
    coRoutine.ops.push(ReflectionOp.return);
    this.coRoutines.push(coRoutine);
    this.mainOffset += coRoutine.ops.length;
    return startIndex;
}

使用流程:

  1. 当遇到需要延迟执行的类型操作时,调用 pushCoRoutine()
  2. 在协程中收集操作码
  3. 调用 popCoRoutine() 结束协程并获取其起始索引
  4. 最终在 buildPackStruct() 中组装所有协程的操作码:
buildPackStruct() {
    const ops: ReflectionOp[] = [...this.ops];

    if (this.coRoutines.length) {
        // 从后向前插入所有协程的操作码
        for (let i = this.coRoutines.length - 1; i >= 0; i--) {
            ops.unshift(...this.coRoutines[i].ops);
        }
    }

    if (this.mainOffset) {
        ops.unshift(ReflectionOp.jump, this.mainOffset);
    }

    return { ops, stack: this.stack };
}

协程的主要优势是:

  1. 延迟执行: 可以先收集类型操作,后续再执行
  2. 独立上下文: 每个协程有自己的操作码序列和栈帧
  3. 流程控制: 支持条件跳转等复杂控制流程
  4. 模块化: 将复杂的类型操作分解成小的、独立的单元

需要延迟执行的原因:

  1. 条件分支处理:
    • 条件类型有两个分支(trueType 和 falseType)
    • 只有在运行时根据实际类型才能确定走哪个分支
    • 因此需要把两个分支的代码都准备好,但延迟到运行时才执行
  2. 分配式条件类型:
type DistributiveCheck<T> = T extends any[] ? T[number] : T;
type Test = DistributiveCheck<string | number[] | boolean[]>;
// 结果: string | number | boolean

以条件类型为例:

type CheckNumber<T> = T extends number ? "Yes, it's number" : "No, it's not number";

在编译器中处理这种条件类型的代码:

case SyntaxKind.ConditionalType: {
    const narrowed = node as ConditionalTypeNode;

    // 分配式条件类型的特殊处理
    const distributiveOverIdentifier = isTypeReferenceNode(narrowed.checkType)
        && isIdentifier(narrowed.checkType.typeName)
        ? narrowed.checkType.typeName : undefined;

    if (distributiveOverIdentifier) {
        program.pushFrame();
        // 添加要分配的原始类型到栈
        this.extractPackStructOfType(narrowed.checkType, program);

        // 添加变量T作为分配的目标
        program.pushVariable(getIdentifierName(distributiveOverIdentifier));
        program.pushCoRoutine();
    }

    // 条件判断的框架
    program.pushConditionalFrame();

    // 1. 检查类型 (T)
    this.extractPackStructOfType(narrowed.checkType, program);
    // 2. extends 的目标类型 (number)
    this.extractPackStructOfType(narrowed.extendsType, program);
    // 3. 添加 extends 操作
    program.pushOp(ReflectionOp.extends);

    // 真值分支的协程
    program.pushCoRoutine();
    this.extractPackStructOfType(narrowed.trueType, program);
    const trueProgram = program.popCoRoutine();

    // 假值分支的协程
    program.pushCoRoutine();
    this.extractPackStructOfType(narrowed.falseType, program);
    const falseProgram = program.popCoRoutine();

    // 添加条件跳转
    program.pushOp(ReflectionOp.jumpCondition, trueProgram, falseProgram);
}

生成的字节码大致是这样的结构:

[
    // 主程序
    ReflectionOp.extends,  // 检查类型关系
    ReflectionOp.jumpCondition, trueIndex, falseIndex, // 条件跳转

    // 协程1: true分支的代码
    [...trueTypeOps],
    ReflectionOp.return,

    // 协程2: false分支的代码
    [...falseTypeOps],
    ReflectionOp.return
]

运行时执行流程:

  1. 检查类型是否满足extends条件
  2. 根据检查结果跳转到对应的协程
  3. 执行相应分支的类型计算
  4. 返回计算结果

Suggest Changes

Previous Post
deepkit学习-bytecode生成
Next Post
deepkit中的type compiler调试