V8源码分析之d8源码注解(第六篇)

0x00 前言

d8自己封装了一个js代码执行器,上一篇我们的代码执行到options.isolate_sources[0].Execute(isolate,本文将做进一步分析。

0x01 调用栈

#0  v8::SourceGroup::Execute (this=0x5555556542a8, isolate=0x1e9d00000000) at ../../src/d8/d8.cc:2567
#1  0x000055555560008b in v8::Shell::RunMain (isolate=0x1e9d00000000, argc=2, argv=0x7fffffffdf38, last_run=true) at ../../src/d8/d8.cc:3100
#2  0x00005555556013a6 in v8::Shell::Main (argc=2, argv=0x7fffffffdf38) at ../../src/d8/d8.cc:3741
#3  0x00005555556016e2 in main (argc=2, argv=0x7fffffffdf38) at ../../src/d8/d8.cc:3777

0x02 SourceGroup::Execute函数

bool SourceGroup::Execute(Isolate* isolate) {
  bool success = true;
  for (int i = begin_offset_; i < end_offset_; ++i) {
    const char* arg = argv_[i];
    //解析-e参数,d8需要执行一段js代码字符串的话,走这个条件分支
    if (strcmp(arg, "-e") == 0 && i + 1 < end_offset_) {
      // Execute argument given to -e option directly.
      //创建作用域
      HandleScope handle_scope(isolate);
      //创建匿名文件名
      Local<String> file_name =
          String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal)
              .ToLocalChecked();
      //读取js代码
      Local<String> source =
          String::NewFromUtf8(isolate, argv_[i + 1], NewStringType::kNormal)
              .ToLocalChecked();
      Shell::set_script_executed();
      //执行js代码字符串
      if (!Shell::ExecuteString(isolate, source, file_name,
                                Shell::kNoPrintResult, Shell::kReportExceptions,
                                Shell::kNoProcessMessageQueue)) {
        success = false;
        break;
      }
      ++i;
      continue;
      //判断是否为js的module文件,规则后缀名必须为.mjs与--module参数连用
    } else if (ends_with(arg, ".mjs")) {
      Shell::set_script_executed();
      if (!Shell::ExecuteModule(isolate, arg)) {
        success = false;
        break;
      }
      continue;
      // 判断是否为module执行模式
    } else if (strcmp(arg, "--module") == 0 && i + 1 < end_offset_) {
      // Treat the next file as a module.
      arg = argv_[++i];
      Shell::set_script_executed();
      if (!Shell::ExecuteModule(isolate, arg)) {
        success = false;
        break;
      }
      continue;
    } else if (arg[0] == '-') {
      // Ignore other options. They have been parsed already.
      continue;
    }
	//LZ这里的执行命令为d8 test.js,所以前面的逻辑都会跳过,真正的入口位置在这里
    // Use all other arguments as names of files to load and run.
    //定义作用域
    HandleScope handle_scope(isolate);
    //创建文件名字符串
    Local<String> file_name =
        String::NewFromUtf8(isolate, arg, NewStringType::kNormal)
            .ToLocalChecked();
    //从文件中读取文件内容
    Local<String> source = ReadFile(isolate, arg);
    if (source.IsEmpty()) {
      printf("Error reading '%s'\n", arg);
      base::OS::ExitProcess(1);
    }
    //设置执行状态为true,该静态函数在d8.h中定义
    Shell::set_script_executed();
    //执行js代码
    if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
                              Shell::kReportExceptions,
                              Shell::kProcessMessageQueue)) {
      success = false;
      break;
    }
  }
  return success;
}

0x03 Shell::ExecuteString函数

// Executes a string within the current v8 context.
bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
                          Local<Value> name, PrintResult print_result,
                          ReportExceptions report_exceptions,
                          ProcessMessageQueue process_message_queue) {
  //i::FLAG_parse_only 为false
  if (i::FLAG_parse_only) {
    i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
    i::VMState<PARSER> state(i_isolate);
    i::Handle<i::String> str = Utils::OpenHandle(*(source));

    // Set up ParseInfo.
    i::ParseInfo parse_info(i_isolate);
    parse_info.set_toplevel();
    parse_info.set_allow_lazy_parsing();
    parse_info.set_language_mode(
        i::construct_language_mode(i::FLAG_use_strict));
    parse_info.set_script(
        parse_info.CreateScript(i_isolate, str, options.compile_options));

    if (!i::parsing::ParseProgram(&parse_info, i_isolate)) {
      fprintf(stderr, "Failed parsing\n");
      return false;
    }
    return true;
  }

  HandleScope handle_scope(isolate);
  TryCatch try_catch(isolate);
  try_catch.SetVerbose(true);

  MaybeLocal<Value> maybe_result;
  bool success = true;
  {
    //获取自定义数据,get后为null。
    PerIsolateData* data = PerIsolateData::Get(isolate);
    //创建realm变量
    Local<Context> realm =
        Local<Context>::New(isolate, data->realms_[data->realm_current_]);
    Context::Scope context_scope(realm);
    MaybeLocal<Script> maybe_script;
    //创建当前上下文context
    Local<Context> context(isolate->GetCurrentContext());
    //创建ScriptOrigin对象origin
    ScriptOrigin origin(name);
     //v8有code caching功能,输入的js代码第一次被编译后,会生成一份cache,再次运行这份js代码时,会优先加载cache,减少重复编译带来的开销。
	//编译选项如果为ScriptCompiler::kConsumeCodeCache,则寻找cache并加载
    if (options.compile_options == ScriptCompiler::kConsumeCodeCache) {
     //根据js代码字符串搜索cache
      ScriptCompiler::CachedData* cached_code =
          LookupCodeCache(isolate, source);
      //如果cache不为空
      if (cached_code != nullptr) {
        ScriptCompiler::Source script_source(source, origin, cached_code);
        maybe_script = ScriptCompiler::Compile(context, &script_source,
                                               options.compile_options);
        CHECK(!cached_code->rejected);
      } else {
        ScriptCompiler::Source script_source(source, origin);
        maybe_script = ScriptCompiler::Compile(
            context, &script_source, ScriptCompiler::kNoCompileOptions);
      }
      // options.stress_background_compile为true,则后台编译
    } else if (options.stress_background_compile) {
    // 启动一个后台线程用于编译js脚本,后台编译就是script streaming 的优化方式
    // 同code cache的地位一样重要。浏览器加载多个js脚本时,边下载边编译的过程称为script streaming
      // Start a background thread compiling the script.
      BackgroundCompileThread background_compile_thread(isolate, source);
      //检查线程是否已经就绪
      CHECK(background_compile_thread.Start());

      // In parallel, compile on the main thread to flush out any data races.
      {
        TryCatch ignore_try_catch(isolate);
        ScriptCompiler::Source script_source(source, origin);
        USE(ScriptCompiler::Compile(context, &script_source,
                                    ScriptCompiler::kNoCompileOptions));
      }

      // Join with background thread and finalize compilation.
      background_compile_thread.Join();
      maybe_script = v8::ScriptCompiler::Compile(
          context, background_compile_thread.streamed_source(), source, origin);
    } else {
      //没有任何优化的编译方式
      ScriptCompiler::Source script_source(source, origin);
      maybe_script = ScriptCompiler::Compile(context, &script_source,
                                             options.compile_options);
    }
	//定义script变量
    Local<Script> script;
    if (!maybe_script.ToLocal(&script)) {
      //打印编译过程中的所有报错
      // Print errors that happened during compilation.
      if (report_exceptions) ReportException(isolate, &try_catch);
      return false;
    }
	//如果kProduceCache选项开启,则将cache落地。
    if (options.code_cache_options ==
        ShellOptions::CodeCacheOptions::kProduceCache) {
      // Serialize and store it in memory for the next execution.
      ScriptCompiler::CachedData* cached_data =
          ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
      StoreInCodeCache(isolate, source, cached_data);
      delete cached_data;
    }
    //【重点】运行js脚本
    maybe_result = script->Run(realm);
    //处理选项,如果是kProduceCacheAfterExecute,则运行后在落地cache
    if (options.code_cache_options ==
        ShellOptions::CodeCacheOptions::kProduceCacheAfterExecute) {
      // Serialize and store it in memory for the next execution.
      ScriptCompiler::CachedData* cached_data =
          ScriptCompiler::CreateCodeCache(script->GetUnboundScript());
      StoreInCodeCache(isolate, source, cached_data);
      delete cached_data;
    }
    if (process_message_queue && !EmptyMessageQueues(isolate)) success = false;
    data->realm_current_ = data->realm_switch_;
  }
  Local<Value> result;
  //读取结果数据
  if (!maybe_result.ToLocal(&result)) {
    DCHECK(try_catch.HasCaught());
    // Print errors that happened during execution.
    if (report_exceptions) ReportException(isolate, &try_catch);
    return false;
  }
  DCHECK(!try_catch.HasCaught());
  if (print_result) {
    //如果配置了test_shell选项,则把结果重定向到标准输出
    if (options.test_shell) {
      //如果结果格式未定义,需要Stringify和格式化
      if (!result->IsUndefined()) {
        // If all went well and the result wasn't undefined then print
        // the returned value.
        v8::String::Utf8Value str(isolate, result);
        fwrite(*str, sizeof(**str), str.length(), stdout);
        printf("\n");
      }
    } else {
      v8::String::Utf8Value str(isolate, Stringify(isolate, result));
      fwrite(*str, sizeof(**str), str.length(), stdout);
      printf("\n");
    }
  }
  return success;
}

0x04 小结

Shell::ExecuteString函数说明了v8运行的大致流程,如果写demo可以参考这个函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值