上篇简单介绍了入口方法的流程以及scanner类相关的部分内容,这一篇主要讲scanner的初始化,即

scanner_.Initialize();

注意,这不是调用静态方法。实际上Parser实例生成的时候也把scanner属性初始化了,所以这里可以直接用。

Parser::Parser(ParseInfo* info) : ParserBase<Parser>(/* 初始化父类的属性 */)
scanner_(info->character_stream(), info->is_module()),/* 初始化其他属性 */

实际上,就是初始化了scanner上的source_属性与模块的flag,以便调用Initialize方法。

这个方法有点类似于libuv的异步操作,不过当然完全不是一个东西,源码如下。

/**
* 注意 这里不做AST的全面转换
*/
void Scanner::Initialize() {
Init();
next().after_line_terminator = true;
Scan();
}

第二步我也不晓得是干啥的,暂时不理解那个变量的意义,所以只讲第一和第三步,首先是Init。

void Init() {
Advance();
/**
* TokenDesc token_storage_[3];
* 这里做一个映射 相当于alias
*/
current_ = &token_storage_[];
next_ = &token_storage_[];
next_next_ = &token_storage_[]; found_html_comment_ = false;
scanner_error_ = MessageTemplate::kNone;
} /**
* source_在Parser的构造函数中初始化
* 类型为Utf16CharacterStream 需要去那边看实现
*/
void Advance() {
c0_ = source_->Advance();
}

从scanner层级来看,其Advance方法的作用仅仅是对私有属性c0_(当前字符的Unicode编码)进行赋值,做实际操作是source_属性上的Advance方法,而这个属性类型为前面转换后的Stream类(全称是xxxCharacterStream,因为太长了,后面全部简称Stream类),所以具体实现需要跳到那边去,源码如下。

/**
* 从这里开始方法域跳到了Utf16CharacterStream、BufferedCharacterStreams
* 即Utf16CharacterStream::Advance、Utf16CharacterStream::Peek、Utf16CharacterStream::ReadBlockChecked
*/
inline uc32 Advance() {
uc32 result = Peek();
buffer_cursor_++;
return result;
} /**
* 返回游标所在位置的值
* 1、已初始化
* 2、未初始化
* 3、已到结尾
*/
inline uc32 Peek() {
if (V8_LIKELY(buffer_cursor_ < buffer_end_)) {
return static_cast<uc32>(*buffer_cursor_);
} else if (ReadBlockChecked()) {
return static_cast<uc32>(*buffer_cursor_);
} else {
return kEndOfInput;
}
}

这里有一些东西需要解释,首先是关于Stream类的3个游标属性(这个名字是我自己取的,看AST的解析总让我想到高中的游标卡尺),分别是buffer_start_、buffer_cursor_、buffer_end_,分别代表字符解析中的开始、当前、结束位置,在Stream类初始化时这三个属性没有处理,默认置0。注意,这里的属性指向字符,跟词法是不同的概念,在scanner层级的三个属性是词法。比如说if从词法角度讲是一个,但是从字符角度来说是两个。

下面的3个判断注释中给出了意义,比较有意思的是V8_LIKELY宏,对于开发者来说算是一个无意义的宏,但是这个宏是给编译器看的,表明这个分支比较有可能发生,推荐进行优化。由于初始化只会走一遍,在解析未结束前大部分情况都是走第一个分支直接返回当前游标指向的值。不过目前是第一次调用这个方法,我们走第二个分支。

/**
* 这里是做一个合法性检测
* 实际上只有ReadBlock做事
*/
bool ReadBlockChecked() {
size_t position = pos();
USE(position);
bool success = !has_parser_error() && ReadBlock(); // Post-conditions: 1, We should always be at the right position.
// 2, Cursor should be inside the buffer.
// 3, We should have more characters available iff success.
DCHECK_EQ(pos(), position);
DCHECK_LE(buffer_cursor_, buffer_end_);
DCHECK_LE(buffer_start_, buffer_cursor_);
DCHECK_EQ(success, buffer_cursor_ < buffer_end_);
return success;
} /**
* buffer_pos_代表当前进度位置 类型为整形
* cursor、start作为指针指向buffer_数组的当前、初始地址
* 而数组在内存中地址连续 且unsigned short类型占1
* 所以可以直接通过计算得到当前位置
*/
inline size_t pos() const {
return buffer_pos_ + (buffer_cursor_ - buffer_start_);
} /**
* 1、buffer_是一个unsigned short数组 存储编码处理后的单个字符
* 2、指针start、end分别初始化为数组的头尾
* 3、cursor是游标 初始指向start
* 例如"(function)"在buffer_表示为[40, 102, ...]
*/
bool ReadBlock() final {
size_t position = pos();
buffer_pos_ = position;
buffer_start_ = &buffer_[];
buffer_cursor_ = buffer_start_; DisallowHeapAllocation no_gc;
Range<uint8_t> range = byte_stream_.GetDataAt(position, runtime_call_stats(), &no_gc);
if (range.length() == ) {
buffer_end_ = buffer_start_;
return false;
} size_t length = Min(kBufferSize, range.length());
i::CopyCharsUnsigned(buffer_, range.start, length);
buffer_end_ = &buffer_[length];
return true;
}

这一块的内容较多,实际上说多也不多。第一个方法只是纯粹的检查,保证游标属性的合法,pos方法则是直接通过地址计算来得到当前解析位置,原理写在注释里了。

ReadBlock方法负责对Stream属性的初始化,这个类前面没有给出声明,buffer_是其一个私有属性,长度为512的short数组。DisallowHeapAllocation不要去管,v8里面有很多奇奇怪怪的东西,目前理解不了,当然与AST本身也毫无关系。GetDataAt比较麻烦,不想讲,从结果上来讲,最后返回的是字符串每个字符的Unicode编码,通过CopyCharsUnsigned方法复制到了buffer_上面,并将buffer_end_指向了最后结尾的部分。

比如说待编译字符串为"'Hello' + ' World'",经过GetDataAt处理后,会变成39, 72, ...。

这里给一个调试结果,buffer_初始化后,会有一堆脏数据,内容如下(长度512,只截取了前面一部分)。

经过该方法的一系列处理,变成了

加上空格,整个字符串共有18位,所以0-17的值全部被重置,后面还是老的脏数据。这些数字手动转换一下,可以得到

刚好是待编译的字符串(先假设字符串长度小于512,复杂情况后面再搞)。

至此,整个Init方法才完事,没想到这么长,Scan下一篇讲,要干活了。

深入V8引擎-AST(3)的更多相关文章

  1. 深入V8引擎-AST(1)

    没办法了,开坑吧,接下来的几篇会讲述JavaScript字符串源码在v8中转换成AST(抽象语法树)的过程. JS代码在V8的解析只有简单的几步,其中第一步就是将源字符串转换为抽象语法树,非常类似于v ...

  2. 深入V8引擎-AST(6)

    花了5篇才把一个字符串词法给解析完,不知道要多久才能刷完整个流程,GC.复杂数据类型的V8实现那些估计又是几十篇,天呐,真是给自己挖了个大坑. 前面几篇实际上只是执行了scanner.Initiali ...

  3. 深入V8引擎-AST(2)

    先声明一下,这种长系列的大块头博客只能保证尽可能的深入到每一行源码,有些代码我不乐意深究就写个注释说明一下作用.另外,由于本地整理的比较好,博客就随心写了. 整个Compile过程目前只看到asmjs ...

  4. 深入V8引擎-AST(4)

    (再声明一下,为了简单暴力的讲解AST的转换过程,这里的编译内容以"'Hello' + ' World'"作为案例) 上一篇基本上花了一整篇讲完了scanner的Init方法,接下 ...

  5. 深入V8引擎-AST(5)

    懒得发首页了,有时候因为贴的代码太多会被下,而且这东西本来也只是对自己学习的记录,阅读体验极差,所以就本地自娱自乐的写着吧! 由于是解析字符串,所以在开始之前介绍一下词法结构体中关于管理字符串类的属性 ...

  6. [翻译] V8引擎的解析

    原文:Parsing in V8 explained 本文档介绍了 V8 引擎是如何解析 JavaScript 源代码的,以及我们将改进它的计划. 动机 我们有个解析器和一个更快的预解析器(~2x), ...

  7. 精读《V8 引擎 Lazy Parsing》

    1. 引言 本周精读的文章是 V8 引擎 Lazy Parsing,看看 V8 引擎为了优化性能,做了怎样的尝试吧! 这篇文章介绍的优化技术叫 preparser,是通过跳过不必要函数编译的方式优化性 ...

  8. Javascript的V8引擎研究

    1.针对上下文的Snapshot技术 什么是上下文(Contexts)?实际是JS应用程序的运行环境,避免应用程序的修改相互影响,例如一个页面js修改内置对象方法toString,不应该影响到另外页面 ...

  9. V8引擎——详解

    前言 JavaScript绝对是最火的编程语言之一,一直具有很大的用户群,随着在服务端的使用(NodeJs),更是爆发了极强的生命力.编程语言分为编译型语言和解释型语言两类,编译型语言在执行之前要先进 ...

随机推荐

  1. Win7 64有点找不到MSVCP71.DLL和MSVCR71.dll

     现象: win7启动好多程序都报找不到MSVCP71.DLL,网页上不去,可是非常奇怪的是好像在线给系统打补丁没有受到不论什么影响,能正常打补丁. 解决: 从本机搜索了一下msvcp71.dll ...

  2. DDD实战9 经销商领域上下文

    1.创建Dealer.Domain 类库项目 2.创建实体和值对象 3.安装ef的包 4.创建上下文接口(IDealerContext)之所以要创建上下文接口,是为了可替换,在其他项目总使用接口,当需 ...

  3. OpenMP编程的任务调度控制

    在OpenMP的for任务分担中,各个线程的任务划分是可以由程序员控制调整的.考虑这样一种情况,当在一个循环中每次迭代的计算量不相等时,如果根据系统默认简单的给每个线程分配相同次数的迭代量的话,会导致 ...

  4. [Elasticsearch] 分布式搜索

    分布式搜索 本文翻译自Elasticsearch官方指南的Distributed Search Execution一章. 在继续之前,我们将绕一段路来谈谈在分布式环境中,搜索是怎样运行的.和在分布式文 ...

  5. 使用python3的base64编解码实现字符串的简易加密解密

    import base64 copyright = 'Copyright (c) 2012 Doucube Inc. All rights reserved.' def main(): #转成byte ...

  6. crossplaform---Nodejs in Visual Studio Code 04.Swig模版

    1.开始 设置Node_Global:npm config set prefix "C:\Program Files\nodejs" Express组件:npm install e ...

  7. [ACM] POJ 3096 Surprising Strings (map使用)

    Surprising Strings Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 5783   Accepted: 379 ...

  8. CORSFilter

    import java.io.IOException; import javax.servlet.Filter;import javax.servlet.FilterChain;import java ...

  9. 操作系统hosts文件

    为了便于北京和大连两个更好的测试系统.该公司专门申请一个域名:大连r \\ u0026 D侧只需要部署(我方系统全权负责在大连研发.所以在大连并列比较的部署方面easy--不要忘记,该项目比我们实际做 ...

  10. C 语言main 函数终极探秘(&& 的含义是:如果 && 前面的程序正常退出,则继续执行 && 后面的程序,否则不执行)

           所有的C程序必须定义一个称之为main的外部函数,这个函数是程序的入口,也就是当程序启动时所执行的第一个函数,当这个函数返回时,程序也将终止,并且这个函数的返回值被看成是程序成功或失败的 ...