用JavaScript带你体验V8引擎解析标识符过程
上一篇讲了字符串的解析过程,这一篇来讲讲标识符(IDENTIFIER)的解析。
先上知识点,标识符的扫描分为快解析和慢解析,一旦出现Ascii值大于128的字符或者转义字符,会进入慢解析,略微影响性能,所以最好不要用中文、特殊字符来做变量名(不过现在代码压缩后基本不会有这种情况了)。
每一位JavaScript的初学者在学习声明一个变量时,都会遇到标识符这个概念,简单来讲标识符的定义如下。
第一个字符,可以是任意Unicode字母(包括英文字母和其他语言的字母),以及美元符号($)和下划线(_)。
第二个字符及后面的字符,除了Unicode字母、美元符号和下划线,还可以用数字0-9。
笼统来讲,v8也是通过这个规则来处理标识符,下面就来看看详细的解析过程。
老规矩,代码我丢github上面,接着前面一篇的内容,往相关文件添加代码,并进行了一些整理。
链接:https://github.com/pflhm2005/V8record/tree/master/JS
待解析字符如下。
var a = 1;
目的就是解析var关键词。
首先需要完善映射表,添加关于标识符的内容,如下。
const TokenToAsciiMapping = (c) => { return c === '(' ? 'Token::LPAREN' : c == ')' ? 'Token::RPAREN' : // ...很多很多 c == '"' ? 'Token::STRING' : c == '\'' ? 'Token::STRING' : // 标识符部分单独抽离出一个方法判断 IsAsciiIdentifier(c) ? 'Token::IDENTIFIER' : // ...很多很多 'Token::ILLEGAL' };
在那个超长的三元表达式中添加一个标识符的判断,由于标识符的合法字符较多,所以单独抽离一个方法做判断。
方法的逻辑只要符合定义就够了,实现如下。
/** * 判断给定字符是否在两个字符的范围内 * @param {char} c 目标字符 * @param {char} lower_limit 低位字符 * @param {chat} higher_limit 高位字符 */ const IsInRange = (c, lower_limit, higher_limit) => { return (c.charCodeAt() - lower_limit.charCodeAt()) >= (higher_limit.charCodeAt() - lower_limit.charCodeAt()); } /** * 将大写字母转换为小写字母 */ const AsciiAlphaToLower = () => { return c | 0x20; } /** * 数字字符判断 */ const IsDecimalDigit = (c) => { return IsInRange(c, '0', '9'); } /** * 大小写字母、数字 */ const IsAlphaNumeric = (c) => { return IsInRange(AsciiAlphaToLower(c), 'a', 'z') || IsDecimalDigit(c); } /** * 判断是否是合法标识符 * @param {String} c 单个字符 */ const IsAsciiIdentifier = (c) => { return IsAlphaNumeric(c) || c == '$' || c == '_'; }
v8内部定义了很多字符相关的方法,这些只是一部分。比较有意思的是那个大写字母转换为小写,一般在JS中都是toLowercase()一把梭,但是C++用的是位运算。
方法都比较简单,可以看到,大小写字母、数字、$、_都会认为是一个标识符。
得到一个Token::IDENTIFIER的初步标记后,会进入单个Token的解析,即Scanner::ScanSingleToken(不记得翻上一篇),在这里,也需要添加一个处理标识符的方法,如下。
class Scanner { /** * 单个词法的解析 */ ScanSingleToken() { let token = null; do { this.next().location.beg_pos = this.source_.buffer_cursor_ - 1; if(this.c0_ < kMaxAscii) { token = UnicodeToToken[this.c0_]; switch(token) { /** * 在这里添加标识符的case */ case 'Token::IDENTIFIER': return ScanIdentifierOrKeyword(); // ... } } /** * 源码中这里处理一些特殊情况 不展开了 * 特殊情况包括Ascii大于255的标识符 特殊情况暂不展开 */ } while(token === 'Token::WHITESPACE') return token; } }
上一篇这里只有Token::String,多加一个case就行了。一般情况下,所有字符都是普通的字符,即Ascii值小于128。如果出现类似于中文这种特殊字符,会进入下面的特殊情况进行慢扫描,由于一般不会出现,这里就不做展开了。
接下来就是实现标识符解析的方法,从名字可以看出,标识符分为变量、关键词两个情况,那么还是需要再弄几个映射表来做类型快速判断。
首先来完善上一篇留下的尾巴,字符分类映射表。
里面其实还有一个映射表,叫character_scan_flag,也是对单个字符的类型判定,属于一种可能性分类。
之前还以为这个表很麻烦,其实挺简单的(假的,恶心了我一中午)。表的作用如上,通过一个字符,来判断这个标识符可能是什么东西,类型总共有6种情况,如下。
/** * 字符类型 */ const kTerminatesLiteral = 1 << 0; const kCannotBeKeyword = 1 << 1; const kCannotBeKeywordStart = 1 << 2; const kStringTerminator = 1 << 3; const kIdentifierNeedsSlowPath = 1 << 4; const kMultilineCommentCharacterNeedsSlowPath = 1 << 5;
这6个枚举值分别表示:
- 标识符的结束标记,比如')'、'}'等符号都代表这个标识符没了
- 非关键词标记,比如一个标识符包含'z'字符,就不可能是一个关键字
- 非关键词首字符标记,比如varrr的首字符是'v',这个标识符可能是关键词(实际上并不是)
- 字符串结束标记,上一篇有提到,单双引号、换行等都可能代表字符串结束
- 标识符慢解析标记,一旦标识符出现转义、Ascii值大于127的值,标记会被激活
- 多行注释标记,看上面那个代码的注释
始终需要记住,这只是一种可能性类型推断,并不是断言,只能用于快速跳过某些流程。
有了标记和对应定义,下面来实现这个字符类型推断映射表,如下。
const GetScanFlags = (c) => { (!IsAsciiIdentifier(c) ? kTerminatesLiteral : 0) | (IsAsciiIdentifier(c) && !CanBeKeywordCharacter(c)) ? kCannotBeKeyword : 0 | (IsKeywordStart(c) ? kCannotBeKeywordStart : 0) | ((c === '\'' || c === '"' || c === '\n' || c === '\r' || c === '\\') ? kStringTerminator : 0) | (c === '\\' ? kIdentifierNeedsSlowPath : 0) | (c === '\n' || c === '\r' || c === '*' ? kMultilineCommentCharacterNeedsSlowPath : 0) } // UnicodeToAsciiMapping下标代表字符对应的Ascii值 上一篇有讲 const CharTypeMapping = UnicodeToAsciiMapping.map(c => GetScanFlags(c));
有了定义,上面的方法基本上不用解释了,用到了我前面讲过的一个技巧bitmap(以前不懂专业术语,难怪阿里一面就挂了)。由于是按照C++源码写的,上述部分工具方法还是需要挨个实现。源码用的宏,写起来一把梭,用JS其实挺繁琐的,具体代码我放github吧。
用JavaScript带你体验V8引擎解析标识符过程的更多相关文章
- 用JavaScript带你体验V8引擎解析标识符
上一篇讲了字符串的解析过程,这一篇来讲讲标识符(IDENTIFIER)的解析. 先上知识点,标识符的扫描分为快解析和慢解析,一旦出现Ascii编码大于127的字符或者转义字符,会进入慢解析,略微影响性 ...
- 使用JavaScript带你体验V8引擎解析字符串过程
AST模块其实要写的话,100篇都写不完,我将一些简单知识点翻译成JavaScript代码来进行讲解(v8内部的复杂性永远都能超出我的意料,现在看到万行的源码都已经没感觉了),如果谁想看C++源码,就 ...
- JavaScript工作机制:V8 引擎内部机制及如何编写优化代码的5个诀窍
概述 JavaScript引擎是一个执行JavaScript代码的程序或解释器.JavaScript引擎可以被实现为标准解释器,或者实现为以某种形式将JavaScript编译为字节码的即时编译器. 下 ...
- 图文带你看懂JavaScritpt引擎V8与JS执行过程
浏览器原理 浏览器内核与js引擎 浏览器内核又称"排版引擎","渲染引擎","浏览器引擎",叫法很多,简单来说干的活就是将代码(HTML,X ...
- 使用 D8 分析 javascript 如何被 V8 引擎优化的
在上一篇文章中我们讲了如何使用 GN 编译 V8 源码,文章最后编译完成的可执行文件并不是 V8,而是 D8.这篇我们讲一下如何使用 D8 调试 javascript 代码. 如果没有 d8,可以使用 ...
- [翻译] V8引擎的解析
原文:Parsing in V8 explained 本文档介绍了 V8 引擎是如何解析 JavaScript 源代码的,以及我们将改进它的计划. 动机 我们有个解析器和一个更快的预解析器(~2x), ...
- How Javascript works (Javascript工作原理) (二) 引擎,运行时,如何在 V8 引擎中书写最优代码的 5 条小技巧
个人总结: 一个Javascript引擎由一个标准解释程序,或者即时编译器来实现. 解释器(Interpreter): 解释一行,执行一行. 编译器(Compiler): 全部编译成机器码,统一执行. ...
- v8引擎详解(摘)-- V8引擎是一个JavaScript引擎实现
随着Web相关技术的发展,JavaScript所要承担的工作也越来越多,早就超越了“表单验证”的范畴,这就更需要快速的解析和执行JavaScript脚本.V8引擎就是为解决这一问题而生,在node中也 ...
- JavaScript是如何工作的02:深入V8引擎&编写优化代码的5个技巧
概述 JavaScript引擎是执行 JavaScript 代码的程序或解释器.JavaScript引擎可以实现为标准解释器,或者以某种形式将JavaScript编译为字节码的即时编译器. 以为实现J ...
随机推荐
- leetcode的Hot100系列--136. 只出现一次的数字
因为之前刚写了461号题目,这个题目与那个很相似, 461号题目用异或来算两个不一样的部分, 那这个题目需要排除一样的部分,并找到不一样的部分. 那么可以再利用一下异或的特性: 1.相同为0,所以,a ...
- kafka入门(一)简介
1 什么是kafk Apache kafka是消息中间件的一种,在开始学习之前,先简单的解释一下什么是消息中间件. 举个例子,生产者消费者,生产者生产鸡蛋,消费者消费鸡蛋,生产者生产一个鸡蛋,消费者就 ...
- Win10自动更新关闭方法
一.为什么很多人会选择禁用Win10自动更新? 1.win10自动更新定义: Win 10的自动更新功能,即 Windows Update.这项功能本意是为一些软件.漏洞等提供更新服务.一般来说,只要 ...
- 业以太网:西门子PROFINET诊断功能SFC12-OB86-SFB20概览
[OB86配合SFC12在PROFINET诊断中的应用] OB86是机架故障组织块,操作系统在检测到下列PN系统故障时将调用OB86: 1.检测到PROFINET IO设备故障,故障产生和消失时分别调 ...
- 西门子PLC中各个组织块OB作用(OB1、OB100……)
1.自由循环组织块OB1 S7 CPU启动完成后,操作系统循环执行OB1,OB1执行完成后,操作系统再次启动OB1.在OB1中可以调用FB.SFB.FC.SFC等用户程序使其循环执行.除OB90以外 ...
- 【Aizu - 0033】Ball (简单搜索)
-->Ball 原文是日语,这里直接写中文了 Descriptions: 如图所示,容器中间有一枢轴,下方分为两路.容器上方开口,从1到10连续编号的小球从容器开口A放入.通过调整枢轴E的方向, ...
- python 的一些小项目
1.在线教育平台(视频播放) 2.仿微信网页版(语音.视频.文字聊天) 3.高德API + Python 解决租房问题 4.仿知乎 5.Django打造文件分享系统.文件管理.搜索引擎(仿云盘) 6. ...
- cookie、sessionSttorage、localStory区别
cookie.sessionSttorage.localStory都是在客户端以键值对存储的存储机制,并且只能将值存储为字符 cookie localStorage sessionStorage ...
- EPPLUS 实现excel报表数据及公式填充
年后工作第一天,根据客户要求修善EPPLUS报表. Epplus: Epplus是一个使用Open Office XML(Xlsx)文件格式,能读写Excel 2007/2010文件的开源组件 好处很 ...
- 高性能javascript-总结(一)
性能优化 第一章:加载和执行 总结: 将所有<script>标签放到页面底部.这能确保在脚本执行前页面已经渲染了. 合并脚本.<script>标签越少,加载越快,响应速度也更迅 ...