D8 源码分析

学习 v8在我看来有两种比较好的方式,一种是通过分析 js 对象或者函数,比如分析 Promise 的实现,另一种方式就是通过分析 d8的源码来进行学习. d8实际上就是一个 v8的开发者工具,已通过 d8来对 js 进行调试和分析.这篇文章主要是通过分析 d8的实现来学习 v8.

在 c 和 c++ 中,大部分程序的入口都是main 函数,因此我们从 main 函数开始分析.

int main(int argc, char* argv[]) { return v8::Shell::Main(argc, argv); }

从 main 函数中我们可以看到, main 函数很简单只调用了 Shell 类的 Main 函数,所有的逻辑都是在 Shell 类里面实现的.Shell 类实际上就是 d8的主要部分.

...
// static
ShellOptions Shell::options;
...
int Shell::Main(int argc, char* argv[]) {
  ...
  if (!SetOptions(argc, argv)) return 1;
  ...
  v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);
  ...
}

首先,Shell::Main 通过调用 SetOptions 函数解析命令行里面的参数并保存到Shell 类的静态成员变量 options 里面.然后通过相应的参数进行一系列的初始化工作.

InitializeICUDefaultLocation 函数主要是用来初始化 ICU 库的,默认会第一个参数是可执行文件路径,第二个参数是 icu 文件,如果为空,默认是 icudtl.dat 文件.

std::unique_ptr<platform::tracing::TracingController> tracing;
std::ofstream trace_file;
if (options.trace_enabled && !i::FLAG_verify_predictable) {
  tracing_file.open(options.trace_path ? options.trace_path : "v8_trace.json");
  ...
  platform::tracing::TraceBuffer* trace_buffer =
        platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
            platform::tracing::TraceBuffer::kRingBufferChunks,
            platform::tracing::TraceWriter::CreateJSONTraceWriter(trace_file));
  tracing->Initialize(trace_buffer);
}

接下来会分析是否需要保存追踪日志,如果与指定日志路径就会将追踪日志保存到 指定的路径,如果没有就会保存在当前的目录下的 v8_tracing.json 文件中.创建好 tracing之后接下来就是创建 v8 platform 实例了.

g_platform = v8::platform::NewDefaultPlatform(
    options.thread_pool_size, v8::platform::IdleTaskSupport::kEnabled,
    in_process_stack_dumping, std::move(tracing));
g_default_platform = g_platform.get();
if (i::FLAG_verify_predictable) {
  g_platform = MakePredictablePlatform(std::move(g_platform));
}
if (options.stress_delay_tasks) {
  int64_t random_seed = i::FLAG_fuzzer_random_seed;
  if (!random_seed) random_seed = i::FLAG_random_seed;
  // If random_seed is still 0 here, the {DelayedTasksPlatform} will choose a
  // random seed.
  g_platform = MakeDelayedTasksPlatform(std::move(g_platform), random_seed);
}

从上面在这段代码可以看到参数不一样会创建不相同的 platform 实例,g_default_platform 是通过 NewDefaultPlatform() 创建的 DefaultPlatform, 通过 MakePredictablePlatform() 创建的是 PredictablePlatform, 通过 MakeDelayedTasksPlatform() 创建的是一个 DelayedTasksPlatform.这里只分析 NewDefaultPlatform() 函数.

std::unique_ptr<v8::Platform> NewDefaultPlatform(
    int thread_pool_size, IdleTaskSupport idle_task_support,
    InProcessStackDumping in_process_stack_dumping,
    std::unique_ptr<v8::TracingController> tracing_controller) {
  if (in_process_stack_dumping == InProcessStackDumping::kEnabled) {
    v8::base::debug::EnableInProcessStackDumping();
  }
  std::unique_ptr<DefaultPlatform> platform(
      new DefaultPlatform(idle_task_support, std::move(tracing_controller)));
  platform->SetThreadPoolSize(thread_pool_size);
  platform->EnsureBackgroundTaskRunnerInitialized();
  return std::move(platform);
}

从 NewDefaultPlatform() 函数以及 Platform 类的注释,我们可以了解什么是 platform是干什么的, 以及platform 有什么用?

从注释来看是一个是对整个嵌入v8的程序的一个抽象概念,看看他的私有变量我们可以大概知道他是干嘛的,Platform用来管理isolate,确定他是在后台线程还是前台线程运行,管理线程池等。

创建完 platform 之后就正式开始进行 v8的初始化工作了.

v8::V8::InitializePlatform(g_platform.get());
v8::V8::Initialize();
if (options.snapshot_blob) { 
  v8::V8::InitializeExternalStartupDataFromFile(options.snapshot_blob);
} else {
  v8::V8::InitializeExternalStartupData(argv[0]);
}

如果options 里面指定了 snapshot_blob 则从指定的 snapshot_blob 文件加载外部的数据,否则从当前执行的文件里面加载外部数据,这里涉及到了 v8的 snapshot 技术,snapshot 就是把之前运行且编译过的代码进行快照,然后保存起来,之后直接从保存的文件中加载,这样可以加快 v8的启动速度.

初始化完成之后,接下来就进入 isolate 创建阶段了.

Isolate::CreateParams create_params;
...
create_params.array_buffer_allocator = Shell::array_buffer_allocator;
create_params.constraints.ConfigureDefaults(
      base::SysInfo::AmountOfPhysicalMemory(),
      base::SysInfo::AmountOfVirtualMemory());
...
Isolate* isolate = Isolate::New(create_params);
isolate->SetHostCleanupFinalizationGroupCallback(
    Shell::HostCleanupFinalizationGroup);
isolate->SetHostImportModuleDynamicallyCallback(
    Shell::HostImportModuleDynamically);
isolate->SetHostInitializeImportMetaObjectCallback(
    Shell::HostInitializeImportMetaObject);

创建 isolate 之前首先需要设置一些信息,比如缓冲区大小,物理内存和虚拟内存的大小等等,这些信息都通过 CreateParams 来保存,v8和 chromium 的代码有很多相似的地方,大部分的内容的初始化都会有一个 CreateParams 类来保存创建信息,比如 Browser::CreateParams, content::CreateParams等等.

在 v8里面创建对象都是通过类的静态成员函数 New 来创建的,而不是直接通过关键字 new 来创建的.

isolate 创建完成之后设置了三个回调函数.

SetHostCleanupFinalizationGroupCallback 指定了在准备清除finalization group并要求在将来的任务中调用FinalizationGroup :: Cleanup() 的回调函数。

SetHostImportModuleDynamicallyCallback 指定通过使用动态import 来加载模块的回调函数。

SetHostInitializeImportMetaObjectCallback 指定通过的 importa.meta 功能检索模块的host-defined元数据时需要调用的回调函数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容