JSCore运行时(三)- async函数与Generator

2020-06-14  本文已影响0人  FingerStyle

我们都知道ES7里面async函数内部是用generator实现的,而generator实现起来大概是这样,有一个核心的next函数,每次执行完一个yield语句则变更一下状态,相当于调用了一次next。

function myCo(gen) {
                return new Promise((resolve, reject) => {
                    const next = data => {
                        try {
                            const { value, done } = gen.next(data);
                        } catch (error) {
                            return reject(error);
                        }

                        if (!done) {
                            Promise.resolve(value).then(val => {
                                next(val);
                            }, reject);
                        } else {
                            resolve(value);
                        }
                    };

                    next(); //执行一次next
                });
            }

那JSCore是如何实现的呢?
首先,还得从语法分析说起...

async标识符的解析

function testPromise(){
    return new Promise(function (resolve, reject) {
        console.log('start new Promise...');
        var timeOut = Math.random() * 2;
        console.log('set timeout to: ' + timeOut + ' seconds.');
        setTimeout(function () {
            if (timeOut < 1) {
                console.log('call resolve()...');
                resolve('200 OK');
            }
            else {
                console.log('call reject()...');
                reject('timeout in ' + timeOut + ' seconds.');
            }
        }, timeOut * 1000);
    })
} 

async function testAsyncFunc() {
    try {
        var result = await testPromise();
         console.log('Done: ' + result);
    }catch (e) {
        console.log('Failed: ' + e);
    }
}

这样一段JS代码,在JSCore里面做语法解析的时候会根据扫描的关键词都判断是什么类型的语句,如果发现是async,就会走async函数的解析

template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementListItem(TreeBuilder& context, const Identifier*& directive, unsigned* directiveLiteralLength)
{
......
case IDENT:
        if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) {
            // Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
            // but could be mistakenly parsed as an AsyncFunctionExpression.
            SavePoint savePoint = createSavePoint(context);
            next();
            if (UNLIKELY(match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken())) {
                result = parseAsyncFunctionDeclaration(context);
                break;
            }
            restoreSavePoint(context, savePoint);
        }
        FALLTHROUGH;
case AWAIT:
case YIELD: {
        // This is a convenient place to notice labeled statements
        // (even though we also parse them as normal statements)
        // because we allow the following type of code in sloppy mode:
        // ``` function foo() { label: function bar() { } } ```
        bool allowFunctionDeclarationAsStatement = true;
        result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement);
        shouldSetPauseLocation = !context.shouldSkipPauseLocation(result);
        break;
    }
......
}

这里调用了parseAsyncFunctionDeclaration来解析async函数,这个方法里面主要就是做了几个事:

  1. 跳过async关键字,设置解析模式为async函数的模式
  2. 判断下上下文,设置解析要求,如果这个函数是export default导出的,则不需要函数名,否则需要解析出函数名称
    3.调用parseFunctionInfo解析函数的具体内容
    4.校验解析出来的函数名称等,如果没问题,就创建一个函数节点,插入到抽象语法树中
template <class TreeBuilder> TreeStatement Parser<LexerType>::parseAsyncFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext, Optional<int> functionConstructorParametersEndPosition){
 ASSERT(match(FUNCTION));
    JSTokenLocation location(tokenLocation());
    unsigned functionKeywordStart = tokenStart();
    next();
    ParserFunctionInfo<TreeBuilder> functionInfo;
    SourceParseMode parseMode = SourceParseMode::AsyncFunctionMode;
    if (consume(TIMES))
        parseMode = SourceParseMode::AsyncGeneratorWrapperFunctionMode;

    FunctionNameRequirements requirements = FunctionNameRequirements::Named;
    if (declarationDefaultContext == DeclarationDefaultContext::ExportDefault) {
        // Under the "export default" context, function declaration does not require the function name.
        //
        //     ExportDeclaration:
        //         ...
        //         export default HoistableDeclaration[~Yield, +Default]
        //         ...
        //
        //     HoistableDeclaration[Yield, Default]:
        //         FunctionDeclaration[?Yield, ?Default]
        //         GeneratorDeclaration[?Yield, ?Default]
        //
        //     FunctionDeclaration[Yield, Default]:
        //         ...
        //         [+Default] function ( FormalParameters[~Yield] ) { FunctionBody[~Yield] }
        //
        //     GeneratorDeclaration[Yield, Default]:
        //         ...
        //         [+Default] function * ( FormalParameters[+Yield] ) { GeneratorBody }
        //
        // In this case, we use "*default*" as this function declaration's name.
        requirements = FunctionNameRequirements::None;
        functionInfo.name = &m_vm.propertyNames->starDefaultPrivateName;
    }

    failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration, functionConstructorParametersEndPosition)), "Cannot parse this async function");
    failIfFalse(functionInfo.name, "Async function statements must have a name");

    std::pair<DeclarationResultMask, ScopeRef> functionDeclaration = declareFunction(functionInfo.name);
    DeclarationResultMask declarationResult = functionDeclaration.first;
    failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare an async function named '", functionInfo.name->impl(), "' in strict mode");
    if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration)
        internalFailWithMessage(false, "Cannot declare an async function that shadows a let/const/class/function variable '", functionInfo.name->impl(), "' in strict mode");
    if (exportType == ExportType::Exported) {
        semanticFailIfFalse(exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'");
        m_moduleScopeData->exportBinding(*functionInfo.name);
    }

    TreeStatement result = context.createFuncDeclStatement(location, functionInfo);
    if (TreeBuilder::CreatesAST)
        functionDeclaration.second->appendFunction(getMetadata(functionInfo));
    return result;

}

parseFunctionInfo就跟一般的函数解析差不多,这里就不多解释了

字节码生成

在BytecodeGenerator的构造函数中有这么一段针对async函数的处理

switch (parseMode) {
    ......
    case SourceParseMode::AsyncArrowFunctionMode:
    case SourceParseMode::AsyncMethodMode:
    case SourceParseMode::AsyncFunctionMode: {
        ASSERT(!isConstructor());
        ASSERT(constructorKind() == ConstructorKind::None);
        m_generatorRegister = addVar();
        m_promiseRegister = addVar();

        if (parseMode != SourceParseMode::AsyncArrowFunctionMode) {
            // FIXME: Emit to_this only when AsyncFunctionBody uses it.
            // https://bugs.webkit.org/show_bug.cgi?id=151586
            emitToThis();
        }

        emitNewGenerator(m_generatorRegister);
        emitNewPromise(promiseRegister(), m_isBuiltinFunction);
        break;
    }
......
}

可以看到,这里创建了一个默认的generator和 promise
在看下具体的字节码生成方法

void FunctionNode::emitBytecode(BytecodeGenerator& generator, RegisterID*)

 switch (generator.parseMode()) {
......
    case SourceParseMode::AsyncFunctionMode:
    case SourceParseMode::AsyncMethodMode:
    case SourceParseMode::AsyncArrowFunctionMode: {
        StatementNode* singleStatement = this->singleStatement();
        ASSERT(singleStatement->isExprStatement());
        ExprStatementNode* exprStatement = static_cast<ExprStatementNode*>(singleStatement);
        ExpressionNode* expr = exprStatement->expr();
        ASSERT(expr->isFuncExprNode());
        FuncExprNode* funcExpr = static_cast<FuncExprNode*>(expr);

        //创建了一个next函数节点
        RefPtr<RegisterID> next = generator.newTemporary();
        generator.emitNode(next.get(), funcExpr);

        if (generator.superBinding() == SuperBinding::Needed || (generator.parseMode() == SourceParseMode::AsyncArrowFunctionMode && generator.isSuperUsedInInnerArrowFunction())) {
            RefPtr<RegisterID> homeObject = emitHomeObjectForCallee(generator);
            emitPutHomeObject(generator, next.get(), homeObject.get());
        }
        
        if (generator.parseMode() == SourceParseMode::AsyncArrowFunctionMode && generator.isThisUsedInInnerArrowFunction())
            generator.emitLoadThisFromArrowFunctionLexicalEnvironment();
        //把next函数节点作为generator的next字段
        generator.emitPutGeneratorFields(next.get());

        ASSERT(startOffset() >= lineStartOffset());
        generator.emitDebugHook(WillLeaveCallFrame, lastLine(), startOffset(), lineStartOffset());

        // load and call @asyncFunctionResume
        RefPtr<RegisterID> asyncFunctionResume = generator.moveLinkTimeConstant(nullptr, LinkTimeConstant::asyncFunctionResume);

        CallArguments args(generator, nullptr, 4);
        unsigned argumentCount = 0;
        generator.emitLoad(args.thisRegister(), jsUndefined());
        generator.move(args.argumentRegister(argumentCount++), generator.generatorRegister());
        generator.move(args.argumentRegister(argumentCount++), generator.promiseRegister());
        generator.emitLoad(args.argumentRegister(argumentCount++), jsUndefined());
        generator.emitLoad(args.argumentRegister(argumentCount++), jsNumber(static_cast<int32_t>(JSGenerator::GeneratorResumeMode::NormalMode)));
        // JSTextPosition(int _line, int _offset, int _lineStartOffset)
        JSTextPosition divot(firstLine(), startOffset(), lineStartOffset());

        RefPtr<RegisterID> result = generator.newTemporary();
        generator.emitCallInTailPosition(result.get(), asyncFunctionResume.get(), NoExpectedFunction, args, divot, divot, divot, DebuggableCall::No);
        generator.emitReturn(result.get());
        break;
    }
......
}

这一段比较长,主要做了几件事:

  1. 创建了一个next函数节点,并将其作为generator的next字段。这里如果是箭头函数,还会做super和this的绑定。
  2. 调用asyncFunctionResume,这个是用内置的js实现的,有四个参数,分别是generator、promise、undefined和resumeMode(这里是normalMode),后面会展开讲。
  3. 创建一个临时的result节点并返回

在ByteCodeGenerater的构造函数中还有一段对generatorFrame的处理,generatorFrame按这里的注释的意思大概就是一个generator的词法域,这里把词法域保存起来,当async执行完成后再恢复,这样generator外面就访问不到内部的变量了

 // Set up the lexical environment scope as the generator frame. We store the saved and resumed generator registers into this scope with the symbol keys.
    // Since they are symbol keyed, these variables cannot be reached from the usual code.
    if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode)) {
        m_generatorFrameSymbolTable.set(m_vm, functionSymbolTable);
        m_generatorFrameSymbolTableIndex = symbolTableConstantIndex;
        if (m_lexicalEnvironmentRegister)
            move(generatorFrameRegister(), m_lexicalEnvironmentRegister);
        else {
            // It would be possible that generator does not need to suspend and resume any registers.
            // In this case, we would like to avoid creating a lexical environment as much as possible.
            // op_create_generator_frame_environment is a marker, which is similar to op_yield.
            // Generatorification inserts lexical environment creation if necessary. Otherwise, we convert it to op_mov frame, `undefined`.
            OpCreateGeneratorFrameEnvironment::emit(this, generatorFrameRegister(), scopeRegister(), VirtualRegister { symbolTableConstantIndex }, addConstantValue(jsUndefined()));
        }
        static_assert(static_cast<unsigned>(JSGenerator::Field::Frame) == static_cast<unsigned>(JSAsyncGenerator::Field::Frame));
        emitPutInternalField(generatorRegister(), static_cast<unsigned>(JSGenerator::Field::Frame), generatorFrameRegister());
    }

我们再来看functionBody的字节码生成,这同样也是前面提到的emitByteCode里面的一段代码(读起来真是绕啊)

    case SourceParseMode::AsyncGeneratorBodyMode:
    case SourceParseMode::AsyncArrowFunctionBodyMode:
    case SourceParseMode::AsyncFunctionBodyMode:
    case SourceParseMode::GeneratorBodyMode: {
        //生成generatorBodyLabel
        Ref<Label> generatorBodyLabel = generator.newLabel();
        {
           //生成conditionLabel及条件跳转指令
            RefPtr<RegisterID> condition = generator.newTemporary();
            generator.emitEqualityOp<OpStricteq>(condition.get(), generator.generatorResumeModeRegister(), generator.emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGenerator::GeneratorResumeMode::NormalMode))));
            generator.emitJumpIfTrue(condition.get(), generatorBodyLabel.get());
            //生成throwLabel
            Ref<Label> throwLabel = generator.newLabel();
            generator.emitEqualityOp<OpStricteq>(condition.get(), generator.generatorResumeModeRegister(), generator.emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGenerator::GeneratorResumeMode::ThrowMode))));
            //条件跳转指令
            generator.emitJumpIfTrue(condition.get(), throwLabel.get());
           //正常执行返回的指令
            generator.emitReturn(generator.generatorValueRegister());
            //回填throwLabel
            generator.emitLabel(throwLabel.get());
            //抛出异常的指令
            generator.emitThrow(generator.generatorValueRegister());
        }
        //回填generatorBodyLabel
        generator.emitLabel(generatorBodyLabel.get());

        emitStatementsBytecode(generator, generator.ignoredResult());
       //生成完成状态的label并填充指令
        Ref<Label> done = generator.newLabel();
        generator.emitLabel(done.get());
        generator.emitReturn(generator.emitLoad(nullptr, jsUndefined()));
        break;
    }

这里用到了回填技术,简单来说就是遇到有多分支的情况时, 先把不确定具体跳转地址的字节码用一个label标记起来,等跳转地址确定以后再回填指令。例如这里的generatorBodyLabel和throwLabel。为什么要这么做呢? 因为计算机执行指令的时候时是串行的,虽然这是一个异步方法,但cpu、寄存器一次只能干一件事,我们要告诉计算机下一步该执行哪个指令,但真正的计算机指令没有if .. else...,只有类似goto这种。那 goto到哪一行,只有等前面的主流程代码写完了才知道后面的分支的代码在哪一行。 大概就是这样:

--主流程
--判断条件,如果符合走分支1
--分支1
--判断条件,如果符合走分支2
--分支2
--结束

await语句

前面讲了async函数的创建,这里还有一个关键的点,就是await是如何解析的,在NodeCodeGen.cpp里面我们找到这个方法

// ------------------------------ AwaitExprNode --------------------------------

RegisterID* AwaitExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
    RefPtr<RegisterID> arg = generator.newTemporary();
    generator.emitNode(arg.get(), argument());
    RefPtr<RegisterID> value = generator.emitYield(arg.get(), JSAsyncGenerator::AsyncGeneratorSuspendReason::Await);
    if (dst == generator.ignoredResult())
        return nullptr;
    return generator.move(generator.finalDestination(dst), value.get());
}

这里是await字节码生成的入口,前面在语法解析的时候会生成一个await表达式的节点,到这里就用到了,如我们所想,他其实是调用了一个emitYield生成了一个yield指令(其实是多个指令的集合)

RegisterID* BytecodeGenerator::emitYield(RegisterID* argument, JSAsyncGenerator::AsyncGeneratorSuspendReason result)
{
    emitYieldPoint(argument, result);

    Ref<Label> normalLabel = newLabel();
    RefPtr<RegisterID> condition = newTemporary();
    emitEqualityOp<OpStricteq>(condition.get(), generatorResumeModeRegister(), emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGenerator::GeneratorResumeMode::NormalMode))));
    emitJumpIfTrue(condition.get(), normalLabel.get());

    Ref<Label> throwLabel = newLabel();
    emitEqualityOp<OpStricteq>(condition.get(), generatorResumeModeRegister(), emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGenerator::GeneratorResumeMode::ThrowMode))));
    emitJumpIfTrue(condition.get(), throwLabel.get());
    // Return.
    {
        RefPtr<RegisterID> returnRegister = generatorValueRegister();
        bool hasFinally = emitReturnViaFinallyIfNeeded(returnRegister.get());
        if (!hasFinally)
            emitReturn(returnRegister.get());
    }

    // Throw.
    emitLabel(throwLabel.get());
    emitThrow(generatorValueRegister());

    // Normal.
    emitLabel(normalLabel.get());
    return generatorValueRegister();
}

void BytecodeGenerator::emitYieldPoint(RegisterID* argument, JSAsyncGenerator::AsyncGeneratorSuspendReason result)
{
    Ref<Label> mergePoint = newLabel();
    unsigned yieldPointIndex = m_yieldPoints++;
    emitGeneratorStateChange(yieldPointIndex + 1);

    if (parseMode() == SourceParseMode::AsyncGeneratorBodyMode) {
        int suspendReason = static_cast<int32_t>(result);
        emitPutInternalField(generatorRegister(), static_cast<unsigned>(JSAsyncGenerator::Field::SuspendReason), emitLoad(nullptr, jsNumber(suspendReason)));
    }

    // Split the try range here.
    Ref<Label> savePoint = newEmittedLabel();
    for (unsigned i = m_tryContextStack.size(); i--;) {
        TryContext& context = m_tryContextStack[i];
        m_tryRanges.append(TryRange {
            context.start.copyRef(),
            savePoint.copyRef(),
            context.tryData
        });
        // Try range will be restared at the merge point.
        context.start = mergePoint.get();
    }
    Vector<TryContext> savedTryContextStack;
    m_tryContextStack.swap(savedTryContextStack);


#if CPU(NEEDS_ALIGNED_ACCESS)
    // conservatively align for the bytecode rewriter: it will delete this yield and
    // append a fragment, so we make sure that the start of the fragments is aligned
    while (m_writer.position() % OpcodeSize::Wide32)
        OpNop::emit<OpcodeSize::Narrow>(this);
#endif
    OpYield::emit(this, generatorFrameRegister(), yieldPointIndex, argument);

    // Restore the try contexts, which start offset is updated to the merge point.
    m_tryContextStack.swap(savedTryContextStack);
    emitLabel(mergePoint.get());
}

有前面的基础,看这一段代码应该不难理解了,就是生成了各种label和跳转指令,包括state的改变,finally的跳转以及每一步yield后词法域的处理等等 这里就不展开讲了。

asyncFunctionResume

这里是前面提到的asyncFunctionResume,我们看下逻辑:

@globalPrivate
function asyncFunctionResume(generator, promise, sentValue, resumeMode)
{
    "use strict";

    @assert(@isPromise(promise));

    var state = @getGeneratorInternalField(generator, @generatorFieldState);
    var value = @undefined;

    try {
        @putGeneratorInternalField(generator, @generatorFieldState, @GeneratorStateExecuting);
        //执行next函数
        value = @getGeneratorInternalField(generator, @generatorFieldNext).@call(@getGeneratorInternalField(generator, @generatorFieldThis), generator, state, sentValue, resumeMode, @getGeneratorInternalField(generator, @generatorFieldFrame));
        if (@getGeneratorInternalField(generator, @generatorFieldState) === @GeneratorStateExecuting) {
            @resolvePromiseWithFirstResolvingFunctionCallCheck(promise, value);
            return promise;
        }
    } catch (error) {
        @rejectPromiseWithFirstResolvingFunctionCallCheck(promise, error);
        return promise;
    }

    var capturedGenerator = generator;
    var capturedPromise = promise;
    @resolveWithoutPromise(value,
        function(value) { @asyncFunctionResume(capturedGenerator, capturedPromise, value, @GeneratorResumeModeNormal); },
        function(error) { @asyncFunctionResume(capturedGenerator, capturedPromise, error, @GeneratorResumeModeThrow); });

    return promise;
}

首先是获取并改变generator的状态为executing,然后执行next函数,
如果执行成功,则调用resolvePromiseWithFirstResolvingFunctionCallCheck,如果有异常则调用rejectPromiseWithFirstResolvingFunctionCallCheck
如果next已经执行过了的话,前面的executing判断会失败,就会走后面的resolveWithoutPromise

@globalPrivate
function resolvePromiseWithFirstResolvingFunctionCallCheck(promise, value)
{
    @assert(@isPromise(promise));
   //这里通过一个标识位判断是否已经调用过该方法,防止重复调用
    var flags = @getPromiseInternalField(promise, @promiseFieldFlags);
    if (flags & @promiseFlagsIsFirstResolvingFunctionCalled)
        return;
    @putPromiseInternalField(promise, @promiseFieldFlags, flags | @promiseFlagsIsFirstResolvingFunctionCalled);
    return @resolvePromise(promise, value);
}

这个方法核心的代码就一句@resolvePromise, 作用就是执行promise的resolve方法。Promise具体的实现可以参考https://www.jianshu.com/p/c76f35307092 (Promise这篇参考的源码比较老,有些函数跟现在这篇可能对不上,但是大概流程是差不多的)

@globalPrivate
function resolvePromise(promise, resolution)
{
    "use strict";

    @assert(@isPromise(promise));

    if (resolution === promise)
        return @rejectPromise(promise, @makeTypeError("Cannot resolve a promise with itself"));

    if (!@isObject(resolution))
        return @fulfillPromise(promise, resolution);

    var then;
    try {
        then = resolution.then;
    } catch (error) {
        return @rejectPromise(promise, error);
    }

    if (@isPromise(resolution) && then === @defaultPromiseThen) {
        @enqueueJob(@promiseResolveThenableJobFast, resolution, promise);
        return;
    }

    if (typeof then !== 'function')
        return @fulfillPromise(promise, resolution);
     //这句把promise的then方法放到了微任务队列里面,等待合适的时机进行调用
    @enqueueJob(@promiseResolveThenableJob, resolution, then, @createResolvingFunctions(promise));
}

rejectPromiseWithFirstResolvingFunctionCallCheck的实现跟resolvePromiseWithFirstResolvingFunctionCallCheck类似,只是调用了rejectPromise而已,这里就不贴源码了

未完待续....

上一篇 下一篇

猜你喜欢

热点阅读