上篇简单介绍了入口方法的流程以及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. 百度地图 JavaScript API 极速版 开发体会

    前段时间百度地图API推出了 JavaScript API 极速版 1.0 简单看了一下,从产品定位来说真是挺好. 把开发人员细分成普通web开发人员和移动web开发人员.正好用到了手机地图这块决定尝 ...

  2. 使用bcc32做在windowXP上qt3.2.1编译环境的配置

    1.安装borland C++编译器,编译器文件所在目录下的文件如下: 其中bcc32.cfg和ilink32.cfg文件是自己加进去的,bcc32.cfg内容是-I"C:\Borland\ ...

  3. matlab 工具函数、matlab toolbox(工具箱)

    minimize.m:最小化可微多元函数 minimize.m:最小化一个可微的多元函数: Minimize a differentiable multivariate function,函数接口说明 ...

  4. stream 文件操作

    简单的帮助类: private static byte[] StreamToBytes(Stream fs) { byte[] bArr = new byte[fs.Length]; fs.Read( ...

  5. Hadoop入门实验

    一.实验目的 了解Hadoop的MapeReduce工作原理 二.实验内容 实现基于单机的伪分布式运行模拟 三.实验需要准备的软件和源 1.Jdk1.6以上 下载地址:http://www.oracl ...

  6. DataGrid数据绑定

    后台数据绑定 用户场景是生成报表,展示公司各员工每个月的绩效 数据结构 包括报表和单个员工绩效两个实体 public class Report { /// <summary> /// 统计 ...

  7. C++中一个class类对象占用多少内字节(7个例子,很清楚)

    一个空的class在内存中多少字节?如果加入一个成员函数后是多大?这个成员函数存储在内存中什么部分? 一个Class对象需要占用多大的内存空间.最权威的结论是: *非静态成员变量总合. *加上编译器为 ...

  8. C#调用C/C++ DLL 参数传递和回调函数的总结

    原文:C#调用C/C++ DLL 参数传递和回调函数的总结 Int型传入: Dll端: extern "C" __declspec(dllexport) int Add(int a ...

  9. WPF显示html的几种方法

    原文:WPF显示html的几种方法 客户希望系统在一些特定的条件下,界面上能显示用户自定义的格式和内容,格式和内容通过html指定. 基本上在wpf中显示html有以下四种方法. 1.       W ...

  10. win10不用密码直接登陆设置

    点击开始菜单里的[运行],然后输入control userpasswords2 或者 netplwiz ,然后点击确定,如下图所示. 点击后,在用户帐户对话框里可以看到“要使用本计算机,用户必须输入用 ...