• 深入V8引擎-AST(6)


    花了5篇才把一个字符串词法给解析完,不知道要多久才能刷完整个流程,GC、复杂数据类型的V8实现那些估计又是几十篇,天呐,真是给自己挖了个大坑。

    前面几篇实际上只是执行了scanner.Initialize方法,并未开始全面解析,继续跑流程。

    FunctionLiteral* Parser::ParseProgram(Isolate* isolate, ParseInfo* info) {
      // ...
      /**
       * 前面完成了这一步
       */
      scanner_.Initialize();
      if (FLAG_harmony_hashbang) {
        scanner_.SkipHashBang();
      }
      // 接下来进这个方法
      FunctionLiteral* result = DoParseProgram(isolate, info);
      // ...
      return result;
    }

    后面的方法域都在Parser类下,毕竟这是整个AST的执行类,parseInfo只是一个编译信息的存储类,直接进DoParseProgram方法。

    FunctionLiteral* Parser::DoParseProgram(Isolate* isolate, ParseInfo* info) {
      // ...
      FunctionLiteral* result = nullptr;
      {
        // ...
        ScopedPtrList<Statement> body(pointer_buffer());
        int beg_pos = scanner()->location().beg_pos;
        if (parsing_module_) {
          // ...
        } else if (info->is_wrapped_as_function()) {
          ParseWrapped(isolate, info, &body, scope, zone());
        } else {
          // 进入这里
          this->scope()->SetLanguageMode(info->language_mode());
          ParseStatementList(&body, Token::EOS);
        }
        // ...
      }
      // ...
      return result;
    }

    这里对编译描述类进行查询,判断待编译字符串是否是模块、包装函数,会进入不同的分支,由于测试代码只是两个字符串相加,所以进入最后的分支。

    void ParserBase<Impl>::ParseStatementList(StatementListT* body, Token::Value end_token) {
      // ...
    
      /**
       * 当之前初始化的第一个Token是字符串时
       * 判断可能是设置模式
       */
      while (peek() == Token::STRING) {
        bool use_strict = false;
        bool use_asm = false;
    
        Scanner::Location token_loc = scanner()->peek_location();
        /**
         * 做字符串严格判定
         * 声明类似于"use  strict"、"use 
    strict"、"use x73trict"都是不合法的
         */
        if (scanner()->NextLiteralExactlyEquals("use strict")) {
          use_strict = true;
        } else if (scanner()->NextLiteralExactlyEquals("use asm")) {
          use_asm = true;
        }
        
        // 这里提前进行解析
        StatementT stat = ParseStatementListItem();
        // ...
    
        if (use_strict) {
          // 进入严格模式
        } else if (use_asm) {
          // 进入asm模式
        } else {
          // 非配置字符串 进入普通模式
          RaiseLanguageMode(LanguageMode::kSloppy);
        }
      }
    
      TargetScopeT target_scope(this);
      /**
       * 其他情况全面解析AST
       */
      while (peek() != end_token) {
        StatementT stat = ParseStatementListItem();
        if (impl()->IsNull(stat)) return;
        if (stat->IsEmptyStatement()) continue;
        body->Add(stat);
      }
    }

    这一步v8对之前解析的第一个词法进行了判断,来选择是否开启严格模式或者asm模式,可以看一眼严格模式定义。

    To invoke strict mode for an entire script, put the exact statement "use strict"; (or 'use strict';) before any other statements.

    由于这个语句必须出现在最前面,所以会在进行全面转换前判断待编译字符串的第一个Token是不是严格等于这个字符串,来设置编译模式。

    这里还有另外一个新鲜的配置,即"use asm",大部分人不会接触到这个东西,asm是JavaScript的一个子集,虽然语法总的来说也是JS的,但是非常难阅读,对于机器来说更加友好。如果用了这个配置,v8会跳过一些阶段,直接将代码编译成机器指令(贴一个介绍链接)。

    如果不配置模式,v8会以普通模式来编译代码,随后底部的while循环指向了全面的AST转换。

    先简单介绍一下AST的容器body,初始化代码如下。

    /**
     * 构造函数参数是一个指针 指向一个内容为任意指针的vector
     * std::vector<void*>* pointer_buffer() { return &pointer_buffer_; }
     * std::vector<void*> pointer_buffer_;
     * ScopedPtrList<Statement>的简写类型为StatementListT 定义如下
     * using StatementListT = typename Types::StatementList;
     * using Types = ParserTypes<Impl>;
     * struct ParserTypes<Parser> { using StatementList = ScopedPtrList<v8::internal::Statement>; }
     */
    ScopedPtrList<Statement> body(pointer_buffer());

    这里的类型包含两部分,一个是容器类ScopePtrList,一个是内容Statement,先看容器。

    template <typename T>
    class ScopedPtrList final {
      public:
        void Add(T* value) {
          buffer_.push_back(value);
          ++end_;
        }
      private:
        std::vector<void*>& buffer_;
        size_t start_;
        size_t end_;
    }

    象征性的给一些属性和方法,传统的vector一把梭,知道一个Add方法就差不多了,反正都那样。

    至于Statement类则是标准的抽象语法树节点类,简单看一下定义就行了,其本身没有什么东西,大量的枚举类型都定义在父类上,暂时不展开。

    class Statement : public AstNode {
     protected:
      Statement(int position, NodeType type) : AstNode(position, type) {}
    
      static const uint8_t kNextBitFieldIndex = AstNode::kNextBitFieldIndex;
    };

    转换的AST会以Statement类存储,并通过Add方法加到容器中。

    这一篇感觉啥都没写,先这样吧,下一篇开始正式全面解析AST。

  • 相关阅读:
    静态库与动态库的创建与使用
    MinGW 仿 linux 开发环境
    SICP 1.7-1.8 solution (Scheme)
    PHP 学生管理系统实现
    【2014最新】常用hosts集锦,分享给大家
    【Android快速入门3】布局简介及例子
    【Android快速入门2】拨号器的实现
    【Android快速入门1】目录结构及adb命令(以API19为例)
    基于深度及广度优先搜索的迷宫问题的演示
    基于HTML5的js构造爱心,动态时间校准
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/11137931.html
Copyright © 2020-2023  润新知