基础篇(一)字节码生成及语法树的构建详情分析

看到HorkeyChen写的文章《[WebKit] JavaScriptCore解析–基础篇(三)从脚本代码到JIT编译的代码实现》,写的很好,深受启发。想补充一些Horkey没有写到的细节比如字节码是如何生成的等等,为此成文。

JSC对JavaScript的处理,其实与Webkit对CSS的处理许多地方是类似的,它这么几个部分:

(1)词法分析->出来词语(Token);

(2)语法分析->出来抽象语法树(AST:Abstract Syntax Tree);

(3)遍历抽象语法树->生成字节码(Bytecode);

(4)用解释器(LLInt:Low Level Interpreter)执行字节码;

(5)如果性能不够好就用Baseline JIT编译字节码生成机器码、然后执行此机器码;

(6)如果性能还不够好,就用DFG JIT重新编译字节码生成更好的机器码、然后执行此机器码;

(7)最后,如果还不好,就祭出重器–虚拟器(LLVM:Low Level Virtual Machine)来编译DFG的中间表示代码、生成更高优化的机器码并执行。接下来,我将会用一下系列文章描述此过程。

其中,步骤1、2是类似的,3、4、5步的思想,CSS JIT也是采用类似方法,请参考[1]。想写写JSC的文章,用菜鸟和愚公移山的方式,敲开JSC的冰山一角。

本篇主要描述词法和语法解析的细节。

一、 JavaScriptCore的词法分析器工作流程分析

是这么解释词法和语法工作流程的:

词法器Tokenizer的工作过程如下,就是不断从字符串中寻找一个个的词(Token),比如找到连续的“true”字符串,就创建一个TokenTrue。词法器工作过程如下:

JavaScriptCore/interpreter/interpreter.cpp:template <typename CharType>template <ParserMode mode> TokenType LiteralParser<CharType>::Lexer::lex(LiteralParserToken<CharType>& token){while (m_ptr < m_end && isJSONWhiteSpace(*m_ptr))++m_ptr;if (m_ptr >= m_end) {token.type = TokEnd;token.start = token.end = m_ptr;return TokEnd;}token.type = TokError;token.start = m_ptr;switch (*m_ptr) {case '[':token.type = TokLBracket;token.end = ++m_ptr;return TokLBracket;case ']':token.type = TokRBracket;token.end = ++m_ptr;return TokRBracket;case '(':token.type = TokLParen;token.end = ++m_ptr;return TokLParen;case ')':token.type = TokRParen;token.end = ++m_ptr;return TokRParen;case ',':token.type = TokComma;token.end = ++m_ptr;return TokComma;case ':':token.type = TokColon;token.end = ++m_ptr;return TokColon;case '"':return lexString<mode, '"'>(token);case 't':if (m_end – m_ptr >= 4 && m_ptr[1] == 'r' && m_ptr[2] == 'u' && m_ptr[3] == 'e') {m_ptr += 4;token.type = TokTrue;token.end = m_ptr;return TokTrue;}break;case '-':case '0':case '9':return lexNumber(token);}if (m_ptr < m_end) {if (*m_ptr == '.') {token.type = TokDot;token.end = ++m_ptr;return TokDot;}if (*m_ptr == '=') {token.type = TokAssign;token.end = ++m_ptr;return TokAssign;}if (*m_ptr == ';') {token.type = TokSemi;token.end = ++m_ptr;return TokAssign;}if (isASCIIAlpha(*m_ptr) || *m_ptr == '_' || *m_ptr == '$')return lexIdentifier(token);if (*m_ptr == '\&;') {return lexString<mode, '\&;'>(token);}}m_lexErrorMessage = String::format("Unrecognized token '%c'", *m_ptr).impl();return TokError;} 经过此过程,一个完整的JSC世界的Token就生成了。然后,再进行语法分析,,生成抽象语法树.下图就是JavaScriptCore世界语法节点的静态类关系:

下面我们看看,语法解析具体过程:

JavaScriptCore/parser/parser.cpp:

PassRefPtr<ParsedNode> Parser<LexerType>::parse(JSGlobalObject* lexicalGlobalObject, Debugger* debugger, ExecState* debuggerExecState, JSObject** exception)</span>{ASSERT(lexicalGlobalObject);ASSERT(exception && !*exception);int errLine;UString errMsg;if (ParsedNode::scopeIsFunction)m_lexer->setIsReparsing();m_sourceElements = 0;errLine = -1;errMsg = UString();UString parseError = parseInner();。。。}

创建抽象语法树Builder,并用来解析、生成语法节点:

UString Parser<LexerType>::parseInner(){UString parseError = UString(); unsigned oldFunctionCacheSize = m_functionCache ? m_functionCache->byteSize() : 0;//抽象语法树Builder:ASTBuilder context(const_cast<JSGlobalData*>(m_globalData), const_cast<SourceCode*>(m_source));if (m_lexer->isReparsing())m_statementDepth–;ScopeRef scope = currentScope();//开始解析生成语法树的一个节点:SourceElements* sourceElements = parseSourceElements<CheckForStrictMode>(context);if (!sourceElements || !consume(EOFTOK))} 举例说来,根据Token的类型,JSC认为输入的Token是一个常量声明,就会使用如下的模板函数生成语法节点(Node),然后放入ASTBuilder里面:

JavaScriptCore/bytecompiler/NodeCodeGen.cpp:template <typename LexerType>template <class TreeBuilder> TreeConstDeclList Parser<LexerType>::parseConstDeclarationList(TreeBuilder& context){failIfTrue(strictMode());TreeConstDeclList constDecls = 0;TreeConstDeclList tail = 0;do {next();matchOrFail(IDENT);const Identifier* name = m_token.m_data.ident;next();bool hasInitializer = match(EQUAL);declareVariable(name);context.addVar(name, DeclarationStacks::IsConstant | (hasInitializer ? DeclarationStacks::HasInitializer : 0));TreeExpression initializer = 0;if (hasInitializer) {next(TreeBuilder::DontBuildStrings); // consume '='initializer = parseAssignmentExpression(context);}tail = context.appendConstDecl(m_lexer->lastLineNumber(), tail, name, initializer);if (!constDecls)constDecls = tail;} while (match(COMMA));return constDecls;}

接下来,就会调用BytecodeGenerator::generate生成字节码,具体分下节分析。我们先看看下面来自JavaScript的一个个语法树节点生成字节码的过程:

留下许多叫知识和情感的东西握在手里。

基础篇(一)字节码生成及语法树的构建详情分析

相关文章:

你感兴趣的文章:

标签云: