[前端] V8引擎编译原理

文章目录

      • 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.mfbz.cn/a/181729.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Java】定时器的简单应用

在写代码的过程中&#xff0c;如果我们遇到了隔一段时间就要进行一项任务时&#xff0c;采用定时器会提高我们的效率。下面对定时器的使用进行简单说明 1、应用说明 首先我们要创建一个Timer类 Timer timer new Timer(); 然后在timer中调用schedule()方法添加任务 timer.…

SpringBoot自定义注解+数据脱敏(一看就懂)

一、注解的介绍 在Java中&#xff0c;注解&#xff08;Annotation&#xff09;是JDK5.0引入的一个重要特性。注解提供了一种元数据机制&#xff0c;可以用于描述和定义程序中的元素&#xff08;类、方法、成员变量等&#xff09;。注解是一种能被添加到java源代码中的元…

羊大师教你如何科学控制体重,轻松瘦下来

羊大师教你如何科学控制体重&#xff0c;轻松瘦下来 我们都知道&#xff0c;控制体重对于保持健康和美丽至关重要。然而&#xff0c;许多人在减肥的道路上走得波折重重&#xff0c;常常陷入挫败和不知所措的境地。那么&#xff0c;如何科学控制体重&#xff0c;轻松瘦下来呢&a…

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;接下来…

C++设计模式之工厂模式(上)——简单工厂模式

工厂模式 概述简单工厂模式介绍示例示例使用运行结果缺点 概述 工厂模式属于一种创建型设计模式。其可以分为简单工厂模式&#xff0c;工厂模式和抽象工厂模式。工厂模式分为上、中、下三篇&#xff0c;本篇主要介绍简单工厂模式。 简单工厂模式 介绍 简单工厂模式可以理解…

SpringBoot启动后自动打开浏览器访问项目

更简单的一个方法 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " url); Springboot项目启动后自动打开浏览器访问(超实用)_浏览器访问springboot项目-CSDN博客 Springboot项目启动后自动打开浏览器访问 1、在Springboot项目中每次启动完项…

vue3之echarts渐变柱状图

vue3之echarts渐变柱状图 效果&#xff1a; 核心代码&#xff1a; <template><div class"abnormal"><div class"chart" ref"chartsRef"></div></div> </template><script setup> import * as echa…

美国大学陪读签证申请条件是什么?

美国大学陪读签证是留学生的家庭成员在美国陪同留学生就读期间的签证类型。申请该签证需要满足一定的条件&#xff0c;这些条件通常包括&#xff1a; 1.申请人身份关系证明&#xff1a;申请人必须是留学生的家庭成员&#xff0c;如配偶或子女&#xff0c;需要提供证明文件&…

几个基础python防坑小常识!

本期整理了几个基础python防坑小常识&#xff0c;希望对大家有所帮助。 1. type object&#xff1f; 执行以下代码的结果是什么&#xff1a; >>> isinstance(type, object) True >>> isinstance(object, type) True >>> isinstance(object, obj…

上市公司常见的印章问题契约锁如何帮您解决?

您知道公司印章的管理和使用是否存在问题&#xff1f;公司内部该如何通过印章问题自查&#xff0c;及时进行风险防治&#xff1f; 印章是上市公司权利的象征&#xff0c;开展“印章管理审查”确保管理和使用合规&#xff0c;也是上市公司内控和监管的一项重要内容。如果存在不合…

DALSA.SaperaLT.SapClassBasic无法加载,试图加载格式不正确的程序,c#

情景&#xff1a;用c#wpf写DALSA线扫相机的项目&#xff0c;生成时不报错&#xff0c;运行到DALSA相关的代码就报错找不到dll&#xff08;DALSA的技术支持没给到任何支持 &#xff09; 一.根据框架选择dll 如果是.net framework框架&#xff08;比如说.net480&#xff09;&am…

react中虚拟dom,diff,fiber - 初级了解

借鉴&#xff1a; 「React深入」一文吃透虚拟DOM和diff算法 - 掘金 (juejin.cn) 虚拟dom、fiber、渲染dom、dom-diff - 掘金 (juejin.cn) 未阅读源码&#xff0c;了解层面&#xff0c;后续可以深入了解 1.虚拟DOM ①.结构上&#xff1a;虚拟DOM比真实DOM轻很多 ②.操作上&…

HTTP ERROR 403 No valid crumb was included in the request

1、报错截图&#xff1a; 2、产生原因&#xff1a; 开启了csrf&#xff0c;即跨站请求伪造 3、新版本不支持页面修改&#xff0c;故需要修改jenkins配置文件 3.1 进入编辑配置文件 vim /etc/sysconfig/jenkins 3.2 修改JENKINS_JAVA_OPTIONS&#xff0c;并保存修改 JENKI…

ssm+vue的物资物流系统的设计与实现(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的物资物流系统的设计与实现&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体…

蓝桥杯算法双周赛心得——迷宫逃脱(dp)

大家好&#xff0c;我是晴天学长&#xff0c;dp版的来啦&#xff0c;可以是受益匪浅啊&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .迷宫逃脱 迷官逃脱[算法赛] 问题描述 在数学王国中&#xff0c;存…

Docker 快速搭建 Gitlab 服务

linux环境&#xff1a; 使用 vim 编辑 hosts 文件&#xff1a; vim /etc/hosts按 I 进入编辑模式&#xff0c;在文件末行追加上虚拟机的 IP 和要设置的域名&#xff1a; 192.168.1.17 gitlab.kunwu.toplwindows环境&#xff1a; Windows 系统的 hosts 文件位于 C:\Windows\S…

SEM代运营的服务费用为什么不按广告费比例抽佣?

如果SEM代运营公司是按广告费比例来抽取佣金&#xff0c;就意味着客户的广告费花得越多&#xff0c;代运营公司的收入也就越高。本来应当屏蔽掉的无效广告点击和恶意点击&#xff0c;出于代运营公司自身利益考虑&#xff0c;处理这些事情时也就不那么干脆利索。只要能保障客户有…

论文阅读:MedSegDiff: Medical Image Segmentation with Diffusion Probabilistic Model

论文标题&#xff1a; MedSegDiff: Medical Image Segmentation with Diffusion Probabilistic Model 翻译&#xff1a; MedSegDiff&#xff1a;基于扩散概率模型的医学图像分割 名词解释&#xff1a; 高频分量&#xff08;高频信号&#xff09;对应着图像变化剧烈的部分&…

第7章-使用统计方法进行变量有效性测试-7.4.2-多元线性回归

目录 多元线性回归模型 总体回归函数 样本回归函数 线性回归模型的假定 普通最小二乘法&#xff08;Ordinary Least Squares&#xff0c;OLS&#xff09; 拟合优度指标 F检验 回归系数的t检验 Python中构建多元线性回归模型 数据理解 数据读取 数据清洗 相关分析 …
最新文章