[前端] V8引擎编译原理

news2024/11/16 3:37:42

文章目录

      • 1.什么是V8
        • 1.1 扫描器Scanner
        • 1.2 解析器parser
        • 1.3 预解析PreParser
        • 1.4 解释器Ignition
        • 1.5 编译器TurboFan

1.什么是V8

V8是谷歌的开源高性能JavaScript和WebAssembly引擎,用C++编写。它被用于Chrome和Node.js等。它实现ECMAScript和WebAssembly,并在Windows 7或更高版本、macOS 10.12+以及使用x64、IA-32、ARM或MIPS处理器的Linux系统上运行。V8可以独立运行,也可以嵌入到任何C++应用程序中。

在这里插入图片描述

  • 扫描器Scanner

  • 解析器Parser

  • 预解析PreParser

  • 解释器Ignition

  • 编译器TurboFan

在这里插入图片描述

1.1 扫描器Scanner

Blink(谷歌浏览器的渲染引擎,基于webkit分支开发)主要负责HTML DOM CSS 渲染,嵌入V8引擎,执行js,计算样式和布局,嵌入合成器,绘制图形。

Blink 拿到html代码分析,找到script代码交给V8引擎解析,注意Blink是通过流的形式传给V8的。

通过以流的形式传输数据,Blink可以逐步接收和处理来自网络的字节流,并在需要时将相应的数据传递给V8引擎执行。这种流式处理方式使得浏览器可以在数据到达的同时并行处理不同的任务,提高了页面的加载速度和用户体验

Scanner(扫描器)首先会进行词法分析:

V8_INLINE Token::Value Scanner::ScanSingleToken() {
  Token::Value token;
  do {
    next().location.beg_pos = source_pos();

    if (V8_LIKELY(static_cast<unsigned>(c0_) <= kMaxAscii)) {
      token = one_char_tokens[c0_];
      switch (token) {
        case Token::LPAREN:
        case Token::RPAREN:
        case Token::LBRACE:
        case Token::RBRACE:
        case Token::LBRACK:
        case Token::RBRACK:
        case Token::COLON:
        case Token::SEMICOLON:
        case Token::COMMA:
        case Token::BIT_NOT:
        case Token::ILLEGAL:
          // One character tokens.
          return Select(token);

        case Token::CONDITIONAL:
          // ? ?. ?? ??=
          Advance();
          if (c0_ == '.') {
            Advance();
            if (!IsDecimalDigit(c0_)) return Token::QUESTION_PERIOD;
            PushBack('.');
          } else if (c0_ == '?') {
            return Select('=', Token::ASSIGN_NULLISH, Token::NULLISH);
          }
          return Token::CONDITIONAL;

        case Token::STRING:
          return ScanString();

        case Token::LT:
          // < <= << <<= <!--
          Advance();
          if (c0_ == '=') return Select(Token::LTE);
          if (c0_ == '<') return Select('=', Token::ASSIGN_SHL, Token::SHL);
          if (c0_ == '!') {
            token = ScanHtmlComment();
            continue;
          }
          return Token::LT;

        case Token::GT:
          // > >= >> >>= >>> >>>=
          Advance();
          if (c0_ == '=') return Select(Token::GTE);
          if (c0_ == '>') {
            // >> >>= >>> >>>=
            Advance();
            if (c0_ == '=') return Select(Token::ASSIGN_SAR);
            if (c0_ == '>') return Select('=', Token::ASSIGN_SHR, Token::SHR);
            return Token::SAR;
          }
          return Token::GT;

        case Token::ASSIGN:
          // = == === =>
          Advance();
          if (c0_ == '=') return Select('=', Token::EQ_STRICT, Token::EQ);
          if (c0_ == '>') return Select(Token::ARROW);
          return Token::ASSIGN;

        case Token::NOT:
          // ! != !==
          Advance();
          if (c0_ == '=') return Select('=', Token::NE_STRICT, Token::NE);
          return Token::NOT;

        case Token::ADD:
          // + ++ +=
          Advance();
          if (c0_ == '+') return Select(Token::INC);
          if (c0_ == '=') return Select(Token::ASSIGN_ADD);
          return Token::ADD;

        case Token::SUB:
          // - -- --> -=
          Advance();
          if (c0_ == '-') {
            Advance();
            if (c0_ == '>' && next().after_line_terminator) {
              // For compatibility with SpiderMonkey, we skip lines that
              // start with an HTML comment end '-->'.
              token = SkipSingleHTMLComment();
              continue;
            }
            return Token::DEC;
          }
          if (c0_ == '=') return Select(Token::ASSIGN_SUB);
          return Token::SUB;

        case Token::MUL:
          // * *=
          Advance();
          if (c0_ == '*') return Select('=', Token::ASSIGN_EXP, Token::EXP);
          if (c0_ == '=') return Select(Token::ASSIGN_MUL);
          return Token::MUL;

        case Token::MOD:
          // % %=
          return Select('=', Token::ASSIGN_MOD, Token::MOD);

        case Token::DIV:
          // /  // /* /=
          Advance();
          if (c0_ == '/') {
            uc32 c = Peek();
            if (c == '#' || c == '@') {
              Advance();
              Advance();
              token = SkipSourceURLComment();
              continue;
            }
            token = SkipSingleLineComment();
            continue;
          }
          if (c0_ == '*') {
            token = SkipMultiLineComment();
            continue;
          }
          if (c0_ == '=') return Select(Token::ASSIGN_DIV);
          return Token::DIV;

        case Token::BIT_AND:
          // & && &= &&=
          Advance();
          if (c0_ == '&') return Select('=', Token::ASSIGN_AND, Token::AND);
          if (c0_ == '=') return Select(Token::ASSIGN_BIT_AND);
          return Token::BIT_AND;

        case Token::BIT_OR:
          // | || |= ||=
          Advance();
          if (c0_ == '|') return Select('=', Token::ASSIGN_OR, Token::OR);
          if (c0_ == '=') return Select(Token::ASSIGN_BIT_OR);
          return Token::BIT_OR;

        case Token::BIT_XOR:
          // ^ ^=
          return Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);

        case Token::PERIOD:
          // . Number
          Advance();
          if (IsDecimalDigit(c0_)) return ScanNumber(true);
          if (c0_ == '.') {
            if (Peek() == '.') {
              Advance();
              Advance();
              return Token::ELLIPSIS;
            }
          }
          return Token::PERIOD;

        case Token::TEMPLATE_SPAN:
          Advance();
          return ScanTemplateSpan();

        case Token::PRIVATE_NAME:
          if (source_pos() == 0 && Peek() == '!') {
            token = SkipSingleLineComment();
            continue;
          }
          return ScanPrivateName();

        case Token::WHITESPACE:
          token = SkipWhiteSpace();
          continue;

        case Token::NUMBER:
          return ScanNumber(false);

        case Token::IDENTIFIER:
          return ScanIdentifierOrKeyword();

        default:
          UNREACHABLE();
      }
    }

    if (IsIdentifierStart(c0_) ||
        (CombineSurrogatePair() && IsIdentifierStart(c0_))) {
      return ScanIdentifierOrKeyword();
    }
    if (c0_ == kEndOfInput) {
      return source_->has_parser_error() ? Token::ILLEGAL : Token::EOS;
    }
    token = SkipWhiteSpace();

    // Continue scanning for tokens as long as we're just skipping whitespace.
  } while (token == Token::WHITESPACE);

  return token;
}
  1. 首先获取当前字符c0_的值,并设置token为初始值。
  2. 判断c0_是否是ASCII字符,如果是,则根据c0_的值来确定token的类型,并返回相应的Token。
  3. 对于一些特殊情况,如条件运算符、字符串、小于号、大于号、等号、逻辑非、加号、减号、乘号、取模、除号、按位与、按位或等,根据当前字符和后续字符的组合来确定token的类型,并返回相应的Token。
  4. 如果c0_不是ASCII字符,或者不满足以上条件,则判断c0_是否是标识符的起始字符,如果是,则调用ScanIdentifierOrKeyword()函数来获取标识符或关键字的Token。
  5. 如果c0_是HTML注释的结束符’-',则调用SkipSingleHTMLComment()函数来跳过整个HTML注释。
  6. 如果扫描到文件末尾,则返回Token::EOS。
  7. 否则,如果遇到空白字符,则调用SkipWhiteSpace()函数来跳过连续的空白字符,并继续扫描下一个Token。
  8. 返回扫描到的Token。

js代码就会变成tokens 接下来进行语法分析。

1.2 解析器parser

parser 的作用就是将 tokens 转化为 AST 抽象语法树。

Program 
└── VariableDeclaration 
├── Identifier (name: "2") 
└── StringLiteral (value: "'1'")
1.3 预解析PreParser

作用是在 JavaScript 代码执行之前对代码进行可选的预处理。预解析器的存在是为了提高代码的执行效率。

V8 引擎采用了延迟解析(Lazy Parsing)的策略,它的原理是只解析当前需要的内容,而把其他内容推迟到函数被调用时再进行解析。这样可以减少不必要的解析工作,提高网页的运行效率。

例如,在一个函数 outer 内部定义了另一个函数 inner,那么 inner 函数就会进行预解析。这意味着在函数 outer 被调用之前,只会对 outer 函数的内容进行解析,而对于 inner 函数的解析会在 outer 函数调用到 inner 函数时才进行。

通过延迟解析的方式,V8 引擎可以避免解析和编译未被执行的函数,节省了不必要的时间和资源开销,提高了 JavaScript 代码的执行效率。这种优化策略在大型复杂的 JavaScript 应用程序中尤为重要,可以帮助提升整体性能和用户体验。

1.4 解释器Ignition

作用主要就是将AST 抽象语法树 转化成 字节码(bytecode)

  1. 跨平台执行:不同的硬件架构和操作系统有不同的机器码格式。通过将代码转换为字节码,可以使得同一份字节码在不同的平台上都能执行,实现跨平台的能力。
  2. 快速启动和解析:将代码转换为字节码可以比直接生成机器码更快速地进行启动和解析。字节码通常具有更简单的格式和结构,可以更快地被引擎加载和解释执行。
  3. 动态优化:现代的JavaScript引擎通常具有即时编译(JIT)功能,可以将热点代码编译成高效的机器码。通过首先将代码转换为字节码,引擎可以更好地进行动态优化和编译,根据实际执行情况生成最优的机器码。这种方式可以在运行时根据代码的实际执行情况进行优化,而不需要提前生成固定的机器码。
  4. 代码安全性:字节码作为中间表示形式,可以提供一定的代码安全性。字节码相对于源代码或机器码来说更难以理解和修改,可以提供一定程度的代码保护。
Program 
└── VariableDeclaration 
├── Identifier (name: "2") 
└── StringLiteral (value: "'1'")
0001: PushString "'1'" 
0002: StoreVar "2"

将字符串字面量 “‘1’” 推入堆栈(栈帧)。在这个例子中,它将字符串 “‘1’” 推入堆栈。

将栈顶的值存储到变量 “2” 中。在这个例子中,它将栈顶的字符串值存储到变量 “2”。

1.5 编译器TurboFan

编译器就是将字节码也可以叫中间代码 最后 转换成 机器码 能让我们的CPU识别。

CPU有不同的架构 ARM X86。

X86机器码

MOV EAX, '1' ; 将字符串 '1' 存储到寄存器 
EAX MOV [2], EAX ; 将寄存器 EAX 的值存储到变量 2 对应的内存地址中

ARM机器码。

LDR R0, ='1' ; 将字符串 '1' 的地址加载到寄存器
R0 STR R0, [2] ; 将寄存器 R0 中的值存储到变量 2 对应的内存地址中

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1243136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅谈Jmeter接口测试

前言 接口测试是目前最主流的自动化测试手段&#xff0c;将不同的参数组合起来向服务器发送请求&#xff0c;接受和分析响应结果&#xff0c;通过测试数据交换逻辑验证服务端程序的运行正确性。 我们在测试中需要考虑不同的输入组合以涵盖不同的测试范围 1、Jmeter简介 Jmet…

实现了一个简易的计算器

计算器的界面如下&#xff1a; 实现过程&#xff1a; 通过html和css编写这样一个界面JavaScript实现功能 在通过JavaScript实现计算器功能的过程中&#xff0c;其实使用的都是一些基本指数。主要包括以下几点&#xff1a; If/else 分支.For 循环JavaScript 函数箭头函数&…

DRAM(Distilling and Refining Annotations of Metabolism,提取和精练代谢注释)工具安装和使用

先看文章介绍吧&#xff1a;DRAM for distilling microbial metabolism to automate the curation of microbiome function | Nucleic Acids Research | Oxford Academic (oup.com) 1、安装 默认使用conda安装吧&#xff0c;也建议使用conda&#xff0c;pip安装其实都差不多&a…

低代码表单设计器:可视化+灵活+易操作,降本增效轻松实现!

在现代化办公环境中&#xff0c;拥有先进的低代码表单设计器&#xff0c;可以让企业降本又增效&#xff0c;节约企业成本的同时&#xff0c;也能高效利用企业内部资源&#xff0c;为实现数字化转型升级提供夯实根基。那么&#xff0c;低代码表单设计器拥有什么样的特点&#xf…

志愿服务暖人心,村委致谢送锦旗

“衷心感谢济宁市运河义工服务协会对我们村里的大学生凯凯一直以来的关注和帮扶。真是太谢谢您们了&#xff0c;如不是您们的帮助孩子也不能那么放心地去上大学&#xff0c;毕竟家里有从小就相依为命的年迈的奶奶&#xff0c;是他一直的担心和牵挂&#xff0c; 咱们协会不仅对孩…

Django中间件与csrf

一. django中间件 1. 什么是django中间件 # django中间件是django的门户1. 请求来的时候需要先经过中间件才能到达真正的django后端2. 响应走的时候最后也需要经过中间件才能发送出去 2. django中间件的个数 django自带七个中间件, 分别是SecurityMiddleware, SessionMiddle…

Linux宝塔面板搭建Discuz论坛, 并内网穿透实现公网访问

Linux宝塔面板搭建Discuz论坛&#xff0c; 并内网穿透实现公网访问 文章目录 Linux宝塔面板搭建Discuz论坛&#xff0c; 并内网穿透实现公网访问前言1.安装基础环境2.一键部署Discuz3.安装cpolar工具4.配置域名访问Discuz5.固定域名公网地址6.配置Discuz论坛 &#x1f4f7; 江池…

深兰科技“汉境”入选2023年湖北省人工智能十大优秀应用案例

11月18日&#xff0c;央视“专精特新制造强国”城市大会在湖北武汉召开。会上&#xff0c;正式发布了“湖北省工业互联网标识十大优秀应用案例”&#xff0c;由深兰科技(武汉)股份有限公司基于AIGC多模态融合大模型技术开发打造的江汉路步行街元宇宙场景应用——汉境&#xff0…

Python爬虫基础教程之urllib和requests的区别详解

文章目录 前言1、获取网页数据第一步&#xff0c;引入模块。第二步&#xff0c;简单网页发起的请求。第三步&#xff0c;数据封装。 2、解析网页数据3.保存数据关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项…

关于ElectronVue3中集成讯飞星火AI

前言&#xff1a;我的最终目的是为了在QQ上集成一个AI机器人&#xff0c;因此在这里先实现一个简单的集成 先上效果图 总体还是很简单的&#xff0c;我在调用websock获取回复内容的基础上另外集成了一个事件总线&#xff0c;让我们在调用获取消息的时候能够更加方便快捷 工具代…

【SwiftUI】7.预览及其内部机制

上一篇讲到了组件及组件化&#xff0c;从概念和优/缺点两个方向说明了组件化的意义&#xff0c;更为重要的是&#xff0c;组件和组件化是一个在编程领域&#xff0c;放之四海皆可以的概念&#xff0c;理解和运用它是非常必要的&#xff0c;希望大家能掌握。今天我们介绍另一个特…

Spring第一课,了解IDEA里面的文件,回顾Cookie和Session,获取Session,Cookie,Header的方式

目录 IDEA第一课&#xff08;熟悉里面内容&#xff09; 建立连接 -RequestMapping 路由映射 请求 1.传递单个参数​编辑 2.多个参数​编辑 3.传递数组 4.传递一个集合&#xff0c;但是这里我们传递的时候发生了500的错误 简单介绍JSON 回顾Cookie和S…

软文推广中如何提炼好产品卖点,媒介盒子分享

内容同质化的时代下&#xff0c;企业应该如何让用户留下印象&#xff0c;并且成功将产品卖出去&#xff0c;核心思维就在于提炼产品卖点&#xff0c;产品卖点是销量提升的关键&#xff0c;相信企业在推广产品时都会有点困惑&#xff0c;感觉自家产品和竞品比起来只是logo、外观…

【SpringCloud】为什么选择微服务?

一般的平台会遇到的问题&#xff1a; 服务配置复杂。基础服务多&#xff0c;服务的资源配置复杂&#xff0c;传统方式管理服务复杂 服务之间调用复杂。检索服务、用户中心服务等&#xff0c;服务之间的调用复杂&#xff0c;依赖多 服务监控难度大。服务比较多&#xff0c;…

【SpringCloud】什么是微服务?

一、单体架构 单体架构就是把所有业务模块编写在一个项目&#xff0c;最终打包成一个 war 包&#xff0c;进行部署 单体架构的优点&#xff1a; 部署简单&#xff1a;由于是完整的结构体&#xff0c;可以直接部署在一个服务器上即可 技术单一&#xff1a;项目不需要复杂的…

世界这么大,极米投影仪RS Pro 3任意门带你“云旅游”

在繁忙的工作之后&#xff0c;许多小伙伴选择出门旅游&#xff0c;但也有不少人想在家“躺平”。其实&#xff0c;对于当代年轻人来说&#xff0c;行走在钢筋混凝土丛林&#xff0c;在家用智能投影观影娱乐好好放松身心也是不错的选择。有一台投影仪来相伴&#xff0c;能够抚平…

Let’s xrOS 一款让你优先体验社区创作者的 visionOS App工具

Let’s xrOS Apple Vision Pro 发布预示着空间计算时代的到来&#xff0c;让科技爱好者和开发者开始思考如何在新的交互、系统和硬件上打造独特的三维应用。 自 WWDC 2023 的发布会后&#xff0c;社交媒体上涌现了许多精美的 visionOS App 的效果图和演示视频&#xff0c;然而…

Mac安装配置typescript及在VSCode上运行ts

一、Mac上安装typescript sudo npm install -g typescript 测试一下&#xff1a;出现Version则证明安装成功 tsc -v 二、在VSCode上运行 新建一个xxx.ts文件&#xff0c;测试能否运行 console.log("helloworld") 运行报错&#xff1a;ts-node: command not…

【无标题】学习HTML

由于工作需求&#xff0c;学习了一些html的相关知识&#xff0c;最终应用到打印功能上使用。 HTML是指超文本标记语言&#xff08;HyperText Markup Language&#xff09;。它是一种用于创建和呈现互联网上页面的标准标记语言。HTML是Web开发的基础&#xff0c;是构建网页和应…

关于SseEmitter(SSE)在本地可以响应,部署到服务器后无法响应的问题

关于SseEmitter(SSE)在本地可以响应&#xff0c;部署到服务器后无法响应的问题 GetMapping(value "/v3/detail", produces MediaType.TEXT_EVENT_STREAM_VALUE) ResponseBody public SseEmitter v3Detail(String id) {SseEmitter emitter new SseEmitter((long) …