https://blog.csdn.net/counsellor/category_9549440.html
(4 条消息)V8 源码分析之 d8 源码注解 (第五篇)_counsellor 的专栏 - CSDN 博客_v8 源码分析之 d8
0x00 前言
没了你,我颓废了自己。心里那些苦,都只哽在喉咙里,一想起来就泪如雨下。 ---- 王国维
0x01 调用栈
Thread 1 "d8" received signal SIGINT, Interrupt.
0x00007ffff4a8ea44 in v8::base::LocalKeyToPthreadKey (local_key=32767) at ../../src/base/platform/platform-posix.cc:856
856 static pthread_key_t LocalKeyToPthreadKey(Thread::LocalStorageKey local_key) {
(gdb) bt
#0 0x00007ffff4a8ea44 in v8::base::LocalKeyToPthreadKey (local_key=32767) at ../../src/base/platform/platform-posix.cc:856
#1 0x00007ffff4a8ea6a in v8::base::Thread::GetThreadLocal (key=3) at ../../src/base/platform/platform-posix.cc:952 #2 0x00007ffff6929720 in v8::internal::PerThreadAssertData::GetCurrent () at ../../src/common/assert-scope.cc:45 #3 0x00007ffff692ad2b in v8::internal::PerThreadAssertScope<(v8::internal::PerThreadAssertType)2, true>::IsAllowed () at ../../src/common/assert-scope.cc:91 #4 0x00007ffff6ac3a00 in v8::internal::HandleBase::IsDereferenceAllowed (this=0x7fffffffc360) at ../../src/handles/handles.cc:43 #5 0x000055555561a9c5 in v8::internal::HandleBase::location (this=0x7fffffffc360) at ../../src/handles/handles.h:59 #6 0x00007ffff689e560 in v8::internal::Handle::operator* (this=0x7fffffffc360) at ../../src/handles/handles.h:137 #7 0x00007ffff689e2b5 in v8::internal::Handle::operator-> (this=0x7fffffffc360) at ../../src/handles/handles.h:131 #8 0x00007ffff6aee3a3 in v8::internal::Factory::NewRawOneByteString (this=0x14e800000000, length=267, allocation=v8::internal::AllocationType::kYoung) at ../../src/heap/factory.cc:1085 #9 0x00007ffff6aee5cb in v8::internal::Factory::NewStringFromUtf8 (this=0x14e800000000, string=..., allocation=v8::internal::AllocationType::kYoung) at ../../src/heap/factory.cc:787 #10 0x00007ffff66d0f43 in v8::(anonymous namespace)::NewString (factory=0x14e800000000, type=v8::NewStringType::kNormal, string=...) at ../../src/api/api.cc:6248 #11 0x00007ffff66d0d97 in v8::String::NewFromUtf8 (isolate=0x14e800000000, data=0x1426612c1000 "127.0.0.1\tlocalhost\n127.0.1.1\tubuntu\n127.0.0.1 chrome-infra-packages.appspot.com\n\n# The following lines are desirable for IPv6 capable hosts\n::1 ip6-localhost ip6-loopback\nfe00::0 ip6-localnet\nf"..., type=v8::NewStringType::kNormal, length=267) at ../../src/api/api.cc:6297 #12 0x00005555555f178e in v8::Shell::ReadFile (isolate=0x14e800000000, name=0x55555570aa30 "/etc/hosts") at ../../src/d8/d8.cc:2358 #13 0x00005555555f83e3 in v8::Shell::Read (args=...) at ../../src/d8/d8.cc:1412 #14 0x00007ffff6081f0f in Builtins_CallApiCallback () from /home/test/git/google/v8/out/x64.debug/libv8.so #15 0x00007fffffffc768 in ?? () #16 0x00007fffffffc798 in ?? () #17 0x0000000000000001 in ?? () #18 0x0000000000000040 in ?? () #19 0x00007fffffffc7a0 in ?? () #20 0x00007fffffffc720 in ?? () #21 0x0000000000000006 in ?? () #22 0x00007fffffffc7f0 in ?? () #23 0x000014e800082c3e in ?? () #24 0x000014e8083c0119 in ?? () #25 0x000014e800000000 in ?? () #26 0x000014e808040305 in ?? () #27 0x000014e808040305 in ?? () #28 0x000014e808040305 in ?? () #29 0x000014e808040305 in ?? () #30 0x000014e80824fdbd in ?? () #31 0x000014e8083c0119 in ?? () #32 0x000014e808240ced in ?? () #33 0x000014e80824fec1 in ?? () #34 0x000014e80824fdbd in ?? () #35 0x000014e80824e125 in ?? () #36 0x000014e80836a2d9 in ?? () #37 0x0000000000000098 in ?? () #38 0x000014e80824fe59 in ?? () #39 0x000014e80824fec1 in ?? () #40 0x000014e808240ced in ?? () #41 0x00007fffffffc818 in ?? () #42 0x00007ffff60753da in Builtins_JSEntryTrampoline () from /home/test/git/google/v8/out/x64.debug/libv8.so #43 0x000014e8083c0119 in ?? () #44 0x000014e80824fec1 in ?? () #45 0x0000000000000022 in ?? () #46 0x00007fffffffc880 in ?? () #47 0x00007ffff60751b8 in Builtins_JSEntry () from /home/test/git/google/v8/out/x64.debug/libv8.so
0x02 RunMain 函数分析
路径
v8\src\d8\d8.cc
源码
int Shell::RunMain(Isolate* isolate, int argc, char* argv[], bool last_run) {
//默认d8启动一个线程跑,options.num_isolates默认值为1
for (int i = 1; i < options.num_isolates; ++i) { options.isolate_sources[i].StartExecuteInThread(); } bool success = true; { SetWaitUntilDone(isolate, false); // 默认为null,不会走这个分支 if (options.lcov_file) { debug::Coverage::SelectMode(isolate, debug::CoverageMode::kBlockCount); } //创建HandleScope类的实例对象scope //HandleScope类基于栈分配方式管理所有local句柄,相当于local句柄的作用域。 //所有的本地句柄都由该作用域分配。 //定义位于../../src/api/api.cc:1124 HandleScope scope(isolate); //可以看见0x02小结的注解 Local context = CreateEvaluationContext(isolate); //last_run 此时为true,use_interactive_shell()用于判断是否为交互shell bool use_existing_context = last_run && use_interactive_shell(); //LZ运行的js文件,所以这里为false,跳过 if (use_existing_context) { // Keep using the same context in the interactive shell. evaluation_context_.Reset(isolate, context); } { //创建Scope类型的作用域,用于管理context Context::Scope cscope(context); //创建InspectorClient,默认options.enable_inspector为false InspectorClient inspector_client(context, options.enable_inspector); //自定义了一个scope PerIsolateData::RealmScope realm_scope(PerIsolateData::Get(isolate)); //开始运行第一个isolate孤岛,暂定这样翻译吧 //LZ放了一个死循环,gdb的是否就会停在这里,等待中断 if (!options.isolate_sources[0].Execute(isolate)) success = false; if (!CompleteMessageLoop(isolate)) success = false; } if (!use_existing_context) { DisposeModuleEmbedderData(context); } WriteLcovData(isolate, options.lcov_file); } //回收isolate CollectGarbage(isolate); //多线程的情况,需要启动或等待每个线程。不太懂last_run是啥意思 // 可以看到索引从1开始,0代表第一个线程,已经执行过了,从1以后需要单独执行 for (int i = 1; i < options.num_isolates; ++i) { if (last_run) { options.isolate_sources[i].JoinThread(); } else { options.isolate_sources[i].WaitForThread(); } } //等待所有worker结束 WaitForRunningWorkers(); // In order to finish successfully, success must be != expected_to_throw. return success == Shell::options.expected_to_throw ? 1 : 0; }
0x03 CreateEvaluationContext 函数分析
这个函数是 d8.cc 中用于创建一段内置 js 代码的自定义函数。这里创建 shell 中内置的 arguments 参数变量。
Local Shell::CreateEvaluationContext(Isolate* isolate) {
// This needs to be a critical section since this is not thread-safe
base::MutexGuard lock_guard(context_mutex_.Pointer());
// Initialize the global objects
//创建全局模板 Local global_template = CreateGlobalTemplate(isolate); //创建一个作用域handle EscapableHandleScope handle_scope(isolate); //创建一个context上下文 Local context = Context::New(isolate, nullptr, global_template); DCHECK(!context.IsEmpty()); if (i::FLAG_perf_prof_annotate_wasm || i::FLAG_vtune_prof_annotate_wasm) { isolate->SetWasmLoadSourceMapCallback(ReadFile); } //初始化嵌入器数据 //在context中指定的索引index位置,预设EmbedderData,用于保存一些自定义数据 InitializeModuleEmbedderData(context); //d8是否包含参数,就是d8 '--'后面跟随的参数列表 if (options.include_arguments) { //创建Scope作用域,用于管理context对象 Context::Scope scope(context); //保存arguments的值 const std::vector<const char*>& args = options.arguments; //arguments的个数 int size = static_cast<int>(args.size()); //在isolate中创建一个lLocal数组变量 Local array = Array::New(isolate, size); //遍历arguments的值 for (int i = 0; i < size; i++) { //创建一个string类型的Local变量,urf8编码,arguments的值用于初始化 Local arg = v8::String::NewFromUtf8(isolate, args[i], v8::NewStringType::kNormal) .ToLocalChecked(); // 创建Number类型的Local变量,保存arguments的索引 Local index = v8::Number::New(isolate, i); //给js语言中数据赋上js语言的index和字符串的值。 array->Set(context, index, arg).FromJust(); } //这个js数组变量还没有名字,此时创建一个Local name,初始值为"arguments",即为我们 //在d8中使用的arguments数组变量 Local name = String::NewFromUtf8(isolate, "arguments", NewStringType::kInternalized) .ToLocalChecked(); //在context上下文中加入名为name的array数据 context->Global()->Set(context, name, array).FromJust(); } //返回带有此上下文的作用域handle_scope句柄 //【q1】擦,为什么escape后就变成一个context了,为什么不直接返回一个context? return handle_scope.Escape(context); }
0x04 参考文献
https://v8docs.nodesource.com/node-0.8/df/d69/classv8_1_1_context.html
https://zhuanlan.zhihu.com/p/35371048
https://www.yuque.com/killa/node/v8-api
全文完
本文由 简悦 SimpRead 优化,用以提升阅读体验
使用了 全新的简悦词法分析引擎 beta,点击查看详细说明
0x00 前言0x01 调用栈0x02 RunMain 函数分析路径源码0x03 CreateEvaluationContext 函数分析0x04 参考文献
V8 源码分析之 d8 源码注解 (第六篇)_counsellor 的专栏 - CSDN 博客_kconsumecodecache
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 file_name = String::NewFromUtf8(isolate, "unnamed", NewStringType::kNormal) .ToLocalChecked(); //读取js代码 Local 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 file_name = String::NewFromUtf8(isolate, arg, NewStringType::kNormal) .ToLocalChecked(); //从文件中读取文件内容 Local 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 source,
Local 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(isolate);
i::VMState state(i_isolate);
i::Handle 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 maybe_result; bool success = true; { //获取自定义数据,get后为null。 PerIsolateData* data = PerIsolateData::Get(isolate); //创建realm变量 Local realm = Local::New(isolate, data->realms_[data->realm_current_]); Context::Scope context_scope(realm); MaybeLocal