上篇简单介绍了入口方法的流程以及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. expdp&amp;impdp

    1 创建逻辑文件夹,该命令不会在操作系统创建真正的文件夹,最好以system等管理员创建.  create directory dpdata1 as '/opt/oracle/dpdata1';  c ...

  2. 在.net core自带DI中服务生命周期 Transient,Scoped,Singleton

    只要是透过WebHost产生实例的类型,都可以在构造方法注入.所以Controller.View.Filter.Middleware或自定义的Service等都可以被注入. Transient是瞬时的 ...

  3. Matlab Tricks(二十五) —— 二维图像的 shuffle

    比如对于 mnist (手写字符图像),每幅图像的像素点为 28*28,所以有: perm = randperm(28*28); % 重排列 mnist.train_images = reshape( ...

  4. OpenGL+VS2012编译环境配置

    OpenGL库主体分为三部分,分别是 gl(OpenGL核心库) glu(Utility Library,OpenGL实用库) glut(Utility Toolkit,OpenGL实用工具库) gl ...

  5. Bootstrap Button 使用方法

    Getting Started <!-- basic button --> <com.beardedhen.androidbootstrap.BootstrapButton andr ...

  6. Vertica变化Local时间到GMT时间

    在Vertica的数据库的使用过程中碰到这么一种场景.程序从不同一时候区的集群中收集数据写入同一张表,然后我们须要把这些数据依照GMT时间来显示. 此时我们能够通过Vertica提供TIME ZONE ...

  7. [AC自己主动机+可能性dp] hdu 3689 Infinite monkey theorem

    意甲冠军: 给n快报,和m频率. 然后进入n字母出现的概率 然后给目标字符串str 然后问m概率倍的目标字符串是敲数量. 思维: AC自己主动机+可能性dp简单的问题. 首先建立trie图,然后就是状 ...

  8. [转]Nodejs开发框架Express4.x开发手记

    Express: ?web application framework for?Node.js? Express 是一个简洁.灵活的 node.js Web 应用开发框架, 它提供一系列强大的特性,帮 ...

  9. jquery li练习

    <!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...

  10. 【C#】WixToolset快速入门教程

    原文:[C#]WixToolset快速入门教程 介绍 给windows系统做软件,常见的打包工具大家可能都听说过,如:大名鼎鼎的Installshield.Inno setup等.在遇见Wix之前In ...