From d820258d3b5f9fef23b2a8f00018b405f29af000 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Mon, 9 May 2022 18:22:48 +0800 Subject: [PATCH 1/5] src: move context snapshot index to SnapshotData Also added comments for the members of SnapshotData and renamed blob to v8_snapshot_blob_data for clarity. --- src/env.h | 10 +++++++++- src/node_main_instance.cc | 2 +- src/node_snapshot_builder.h | 3 --- src/node_snapshotable.cc | 19 +++++++++++-------- src/node_worker.cc | 4 ++-- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/env.h b/src/env.h index 475ca4d8b7cd71..1d352083458cb7 100644 --- a/src/env.h +++ b/src/env.h @@ -973,8 +973,16 @@ struct EnvSerializeInfo { }; struct SnapshotData { - v8::StartupData blob; + // The result of v8::SnapshotCreator::CreateBlob() during the snapshot + // building process. + v8::StartupData v8_snapshot_blob_data; + + static const size_t kNodeBaseContextIndex = 0; + static const size_t kNodeMainContextIndex = kNodeBaseContextIndex + 1; + std::vector isolate_data_indices; + // TODO(joyeecheung): there should be a vector of env_info once we snapshot + // the worker environments. EnvSerializeInfo env_info; }; diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index f5e7ef61fb57f6..951a55f3568a82 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -183,7 +183,7 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) { EnvironmentFlags::kDefaultFlags, {})); context = Context::FromSnapshot(isolate_, - SnapshotBuilder::kNodeMainContextIndex, + SnapshotData::kNodeMainContextIndex, {DeserializeNodeInternalFields, env.get()}) .ToLocalChecked(); diff --git a/src/node_snapshot_builder.h b/src/node_snapshot_builder.h index 2714293fbc9976..c5d2ee2a4bcd83 100644 --- a/src/node_snapshot_builder.h +++ b/src/node_snapshot_builder.h @@ -29,9 +29,6 @@ class NODE_EXTERN_PRIVATE SnapshotBuilder { static void InitializeIsolateParams(const SnapshotData* data, v8::Isolate::CreateParams* params); - static const size_t kNodeBaseContextIndex = 0; - static const size_t kNodeMainContextIndex = kNodeBaseContextIndex + 1; - private: // Used to synchronize access to the snapshot data static Mutex snapshot_data_mutex_; diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index 9cb5985ea20841..2c91c0a11ce78c 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -59,11 +59,13 @@ namespace node { static const char blob_data[] = { )"; - WriteVector(&ss, data->blob.data, data->blob.raw_size); + WriteVector(&ss, + data->v8_snapshot_blob_data.data, + data->v8_snapshot_blob_data.raw_size); ss << R"(}; static const int blob_size = )" - << data->blob.raw_size << R"(; + << data->v8_snapshot_blob_data.raw_size << R"(; SnapshotData snapshot_data { // -- blob begins -- @@ -103,7 +105,8 @@ const std::vector& SnapshotBuilder::CollectExternalReferences() { void SnapshotBuilder::InitializeIsolateParams(const SnapshotData* data, Isolate::CreateParams* params) { params->external_references = CollectExternalReferences().data(); - params->snapshot_blob = const_cast(&(data->blob)); + params->snapshot_blob = + const_cast(&(data->v8_snapshot_blob_data)); } void SnapshotBuilder::Generate(SnapshotData* out, @@ -153,7 +156,7 @@ void SnapshotBuilder::Generate(SnapshotData* out, // without breaking compatibility. { size_t index = creator.AddContext(CreateBaseContext()); - CHECK_EQ(index, SnapshotBuilder::kNodeBaseContextIndex); + CHECK_EQ(index, SnapshotData::kNodeBaseContextIndex); } // The main instance context. @@ -222,17 +225,17 @@ void SnapshotBuilder::Generate(SnapshotData* out, // Serialize the context size_t index = creator.AddContext( main_context, {SerializeNodeContextInternalFields, env}); - CHECK_EQ(index, SnapshotBuilder::kNodeMainContextIndex); + CHECK_EQ(index, SnapshotData::kNodeMainContextIndex); } } // Must be out of HandleScope - out->blob = + out->v8_snapshot_blob_data = creator.CreateBlob(SnapshotCreator::FunctionCodeHandling::kClear); // We must be able to rehash the blob when we restore it or otherwise // the hash seed would be fixed by V8, introducing a vulnerability. - CHECK(out->blob.CanBeRehashed()); + CHECK(out->v8_snapshot_blob_data.CanBeRehashed()); // We cannot resurrect the handles from the snapshot, so make sure that // no handles are left open in the environment after the blob is created @@ -260,7 +263,7 @@ std::string SnapshotBuilder::Generate( SnapshotData data; Generate(&data, args, exec_args); std::string result = FormatBlob(&data); - delete[] data.blob.data; + delete[] data.v8_snapshot_blob_data.data; return result; } diff --git a/src/node_worker.cc b/src/node_worker.cc index acc1d56d6ad329..1009c0788cc624 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -301,8 +301,8 @@ void Worker::Run() { // though. TryCatch try_catch(isolate_); if (snapshot_data_ != nullptr) { - context = Context::FromSnapshot( - isolate_, SnapshotBuilder::kNodeBaseContextIndex) + context = Context::FromSnapshot(isolate_, + SnapshotData::kNodeBaseContextIndex) .ToLocalChecked(); if (!context.IsEmpty() && !InitializeContextRuntime(context).IsJust()) { From c3561f73b2ad66df7ddece61763c26408d6521c6 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Thu, 18 Nov 2021 11:56:39 +0800 Subject: [PATCH 2/5] bootstrap: include code cache in the embedded snapshot Since V8 code cache encodes indices to the read-only space it is safer to make sure that the code cache is generated in the same heap used to generate the embdded snapshot. This patch merges the code cache builder into the snapshot builder and makes the code cache part of node::SnapshotData that is deserialized into the native module loader during bootstrap. --- .github/CODEOWNERS | 2 - configure.py | 2 +- lib/internal/bootstrap/node.js | 5 +- node.gyp | 106 +++------------------ src/env.h | 6 +- src/node.cc | 6 +- src/node_code_cache_stub.cc | 22 ----- src/node_main_instance.cc | 2 + src/node_native_module.cc | 33 ++++++- src/node_native_module.h | 9 ++ src/node_native_module_env.cc | 64 ++++++++++++- src/node_native_module_env.h | 18 ++-- src/node_snapshotable.cc | 84 +++++++++++++++-- src/node_wasm_web_api.cc | 1 + test/parallel/test-code-cache.js | 4 +- tools/code_cache/README.md | 38 -------- tools/code_cache/cache_builder.cc | 148 ------------------------------ tools/code_cache/cache_builder.h | 16 ---- tools/code_cache/mkcodecache.cc | 74 --------------- 19 files changed, 214 insertions(+), 426 deletions(-) delete mode 100644 src/node_code_cache_stub.cc delete mode 100644 tools/code_cache/README.md delete mode 100644 tools/code_cache/cache_builder.cc delete mode 100644 tools/code_cache/cache_builder.h delete mode 100644 tools/code_cache/mkcodecache.cc diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4170038a0ac0fc..6d69bbf54be1d6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -107,10 +107,8 @@ /benchmark/misc/startup.js @nodejs/startup /src/node.cc @nodejs/startup -/src/node_code_cache_stub.cc @nodejs/startup /src/node_native_module* @nodejs/startup /lib/internal/bootstrap/* @nodejs/startup -/tools/code_cache/* @nodejs/startup /tools/snapshot/* @nodejs/startup # V8 diff --git a/configure.py b/configure.py index 17ff53ef645493..187c381660b369 100755 --- a/configure.py +++ b/configure.py @@ -1249,7 +1249,7 @@ def configure_node(o): o['variables']['node_use_node_snapshot'] = b( not cross_compiling and not options.shared) - if options.without_node_code_cache or options.node_builtin_modules_path: + if options.without_node_code_cache or options.without_node_snapshot or options.node_builtin_modules_path: o['variables']['node_use_node_code_cache'] = 'false' else: # TODO(refack): fix this when implementing embedded code-cache when cross-compiling. diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 1fc59fdeffd510..7506bf0204a9bc 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -270,9 +270,8 @@ const features = { tls_sni: hasOpenSSL, tls_ocsp: hasOpenSSL, tls: hasOpenSSL, - // This needs to be dynamic because snapshot is built without code cache. - // TODO(joyeecheung): https://github.com/nodejs/node/issues/31074 - // Make it possible to build snapshot with code cache + // This needs to be dynamic because --no-node-snapshot disables the + // code cache even if the bianry is built with embedded code cache. get cached_builtins() { return nativeModule.hasCachedBuiltins(); } diff --git a/node.gyp b/node.gyp index b4ce6188da8525..353129bce49a3c 100644 --- a/node.gyp +++ b/node.gyp @@ -55,7 +55,6 @@ 'deps/undici/undici.js', ], 'node_mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)node_mksnapshot<(EXECUTABLE_SUFFIX)', - 'mkcodecache_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)mkcodecache<(EXECUTABLE_SUFFIX)', 'conditions': [ ['GENERATOR == "ninja"', { 'node_text_start_object_path': 'src/large_pages/node_text_start.node_text_start.o' @@ -304,32 +303,7 @@ }, }, }], - ['node_use_node_code_cache=="true"', { - 'dependencies': [ - 'mkcodecache', - ], - 'actions': [ - { - 'action_name': 'run_mkcodecache', - 'process_outputs_as_sources': 1, - 'inputs': [ - '<(mkcodecache_exec)', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/node_code_cache.cc', - ], - 'action': [ - '<@(_inputs)', - '<@(_outputs)', - ], - }, - ], - }, { - 'sources': [ - 'src/node_code_cache_stub.cc' - ], - }], - ['node_use_node_snapshot=="true"', { + ['node_use_node_snapshot=="true"', { 'dependencies': [ 'node_mksnapshot', ], @@ -739,7 +713,6 @@ [ 'node_shared=="true"', { 'sources': [ 'src/node_snapshot_stub.cc', - 'src/node_code_cache_stub.cc', ] }], [ 'node_shared=="true" and node_module_version!="" and OS!="win"', { @@ -749,6 +722,11 @@ '@rpath/lib<(node_core_target_name).<(shlib_suffix)' }, }], + [ 'node_use_node_code_cache=="true"', { + 'defines': [ + 'NODE_USE_NODE_CODE_CACHE=1', + ], + }], ['node_shared=="true" and OS=="aix"', { 'product_name': 'node_base', }], @@ -1149,7 +1127,6 @@ ], 'sources': [ 'src/node_snapshot_stub.cc', - 'src/node_code_cache_stub.cc', 'test/fuzzers/fuzz_url.cc', ], 'conditions': [ @@ -1192,7 +1169,6 @@ ], 'sources': [ 'src/node_snapshot_stub.cc', - 'src/node_code_cache_stub.cc', 'test/fuzzers/fuzz_env.cc', ], 'conditions': [ @@ -1242,7 +1218,6 @@ 'sources': [ 'src/node_snapshot_stub.cc', - 'src/node_code_cache_stub.cc', 'test/cctest/node_test_fixture.cc', 'test/cctest/node_test_fixture.h', 'test/cctest/test_aliased_buffer.cc', @@ -1335,7 +1310,6 @@ 'sources': [ 'src/node_snapshot_stub.cc', - 'src/node_code_cache_stub.cc', 'test/embedding/embedtest.cc', ], @@ -1379,68 +1353,6 @@ }], ] }, # overlapped-checker - - # TODO(joyeecheung): do not depend on node_lib, - # instead create a smaller static library node_lib_base that does - # just enough for node_native_module.cc and the cache builder to - # compile without compiling the generated code cache C++ file. - # So generate_code_cache -> mkcodecache -> node_lib_base, - # node_lib -> node_lib_base & generate_code_cache - { - 'target_name': 'mkcodecache', - 'type': 'executable', - - 'dependencies': [ - '<(node_lib_target_name)', - 'deps/histogram/histogram.gyp:histogram', - 'deps/uvwasi/uvwasi.gyp:uvwasi', - ], - - 'includes': [ - 'node.gypi' - ], - - 'include_dirs': [ - 'src', - 'tools/msvs/genfiles', - 'deps/v8/include', - 'deps/cares/include', - 'deps/uv/include', - 'deps/uvwasi/include', - ], - - 'defines': [ - 'NODE_WANT_INTERNALS=1' - ], - 'sources': [ - 'src/node_snapshot_stub.cc', - 'src/node_code_cache_stub.cc', - 'tools/code_cache/mkcodecache.cc', - 'tools/code_cache/cache_builder.cc', - 'tools/code_cache/cache_builder.h', - ], - - 'conditions': [ - [ 'node_use_openssl=="true"', { - 'defines': [ - 'HAVE_OPENSSL=1', - ], - }], - ['v8_enable_inspector==1', { - 'defines': [ - 'HAVE_INSPECTOR=1', - ], - }], - ['OS=="win"', { - 'libraries': [ - 'dbghelp.lib', - 'PsApi.lib', - 'winmm.lib', - 'Ws2_32.lib', - ], - }], - ], - }, # mkcodecache { 'target_name': 'node_mksnapshot', 'type': 'executable', @@ -1468,7 +1380,6 @@ 'sources': [ 'src/node_snapshot_stub.cc', - 'src/node_code_cache_stub.cc', 'tools/snapshot/node_mksnapshot.cc', ], @@ -1478,6 +1389,11 @@ 'HAVE_OPENSSL=1', ], }], + [ 'node_use_node_code_cache=="true"', { + 'defines': [ + 'NODE_USE_NODE_CODE_CACHE=1', + ], + }], ['v8_enable_inspector==1', { 'defines': [ 'HAVE_INSPECTOR=1', diff --git a/src/env.h b/src/env.h index 1d352083458cb7..82240de8ced616 100644 --- a/src/env.h +++ b/src/env.h @@ -34,7 +34,6 @@ #include "handle_wrap.h" #include "node.h" #include "node_binding.h" -#include "node_external_reference.h" #include "node_main_instance.h" #include "node_native_module.h" #include "node_options.h" @@ -984,6 +983,11 @@ struct SnapshotData { // TODO(joyeecheung): there should be a vector of env_info once we snapshot // the worker environments. EnvSerializeInfo env_info; + // A vector of built-in ids and v8::ScriptCompiler::CachedData, this can be + // shared across Node.js instances because they are supposed to share the + // read only space. We use the vector because v8::ScriptCompiler::CachedData + // is not copyable. + std::vector code_cache; }; class Environment : public MemoryRetainer { diff --git a/src/node.cc b/src/node.cc index 5910a400c25c25..76a26adc97053e 100644 --- a/src/node.cc +++ b/src/node.cc @@ -980,8 +980,6 @@ int InitializeNodeWithArgs(std::vector* argv, #endif // defined(NODE_HAVE_I18N_SUPPORT) - NativeModuleEnv::InitializeCodeCache(); - // We should set node_is_initialized here instead of in node::Start, // otherwise embedders using node::Init to initialize everything will not be // able to set it and native modules will not load for them. @@ -1175,6 +1173,10 @@ int Start(int argc, char** argv) { : nullptr; uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME); + if (snapshot_data != nullptr) { + native_module::NativeModuleEnv::RefreshCodeCache( + snapshot_data->code_cache); + } NodeMainInstance main_instance(snapshot_data, uv_default_loop(), per_process::v8_platform.Platform(), diff --git a/src/node_code_cache_stub.cc b/src/node_code_cache_stub.cc deleted file mode 100644 index 9d9901738f63b0..00000000000000 --- a/src/node_code_cache_stub.cc +++ /dev/null @@ -1,22 +0,0 @@ -// This file is part of the embedder test, which is intentionally built without -// NODE_WANT_INTERNALS, so we define it here manually. -#define NODE_WANT_INTERNALS 1 - -#include "node_native_module_env.h" - -// The stub here is used when configure is run without `--code-cache-path`. -// When --code-cache-path is set this stub will not be used and instead -// an implementation will be generated. See tools/code_cache/README.md for -// more information. - -namespace node { -namespace native_module { - -const bool has_code_cache = false; - -// The generated source code would insert pairs -// into NativeModuleLoader::instance.code_cache_. -void NativeModuleEnv::InitializeCodeCache() {} - -} // namespace native_module -} // namespace node diff --git a/src/node_main_instance.cc b/src/node_main_instance.cc index 951a55f3568a82..ad4f6a7d95fb32 100644 --- a/src/node_main_instance.cc +++ b/src/node_main_instance.cc @@ -6,6 +6,7 @@ #include "debug_utils-inl.h" #include "node_external_reference.h" #include "node_internals.h" +#include "node_native_module_env.h" #include "node_options-inl.h" #include "node_snapshot_builder.h" #include "node_snapshotable.h" @@ -189,6 +190,7 @@ NodeMainInstance::CreateMainEnvironment(int* exit_code) { CHECK(!context.IsEmpty()); Context::Scope context_scope(context); + CHECK(InitializeContextRuntime(context).IsJust()); SetIsolateErrorHandlers(isolate_, {}); env->InitializeMainContext(context, &(snapshot_data_->env_info)); diff --git a/src/node_native_module.cc b/src/node_native_module.cc index 5d20e1d6a86416..cea80e36b7287d 100644 --- a/src/node_native_module.cc +++ b/src/node_native_module.cc @@ -1,6 +1,7 @@ #include "node_native_module.h" -#include "util-inl.h" #include "debug_utils-inl.h" +#include "node_internals.h" +#include "util-inl.h" namespace node { namespace native_module { @@ -277,6 +278,11 @@ MaybeLocal NativeModuleLoader::LookupAndCompile( : ScriptCompiler::kEagerCompile; ScriptCompiler::Source script_source(source, origin, cached_data); + per_process::Debug(DebugCategory::CODE_CACHE, + "Compiling %s %s code cache\n", + id, + has_cache ? "with" : "without"); + MaybeLocal maybe_fun = ScriptCompiler::CompileFunctionInContext(context, &script_source, @@ -304,6 +310,19 @@ MaybeLocal NativeModuleLoader::LookupAndCompile( *result = (has_cache && !script_source.GetCachedData()->rejected) ? Result::kWithCache : Result::kWithoutCache; + + if (has_cache) { + per_process::Debug(DebugCategory::CODE_CACHE, + "Code cache of %s (%s) %s\n", + id, + script_source.GetCachedData()->buffer_policy == + ScriptCompiler::CachedData::BufferNotOwned + ? "BufferNotOwned" + : "BufferOwned", + script_source.GetCachedData()->rejected ? "is rejected" + : "is accepted"); + } + // Generate new cache for next compilation std::unique_ptr new_cached_data( ScriptCompiler::CreateCodeCacheForFunction(fun)); @@ -311,10 +330,14 @@ MaybeLocal NativeModuleLoader::LookupAndCompile( { Mutex::ScopedLock lock(code_cache_mutex_); - // The old entry should've been erased by now so we can just emplace. - // If another thread did the same thing in the meantime, that should not - // be an issue. - code_cache_.emplace(id, std::move(new_cached_data)); + const auto it = code_cache_.find(id); + // TODO(joyeecheung): it's safer for each thread to have its own + // copy of the code cache map. + if (it == code_cache_.end()) { + code_cache_.emplace(id, std::move(new_cached_data)); + } else { + it->second.reset(new_cached_data.release()); + } } return scope.Escape(fun); diff --git a/src/node_native_module.h b/src/node_native_module.h index 7acd154d419de8..7b8360f422b00f 100644 --- a/src/node_native_module.h +++ b/src/node_native_module.h @@ -15,6 +15,7 @@ class PerProcessTest; namespace node { +class SnapshotBuilder; namespace native_module { using NativeModuleRecordMap = std::map; @@ -22,6 +23,11 @@ using NativeModuleCacheMap = std::unordered_map>; +struct CodeCacheInfo { + std::string id; + std::vector data; +}; + // The native (C++) side of the NativeModule in JS land, which // handles compilation and caching of builtin modules (NativeModule) // and bootstrappers, whose source are bundled into the binary @@ -38,6 +44,7 @@ class NODE_EXTERN_PRIVATE NativeModuleLoader { // Only allow access from friends. friend class NativeModuleEnv; friend class CodeCacheBuilder; + friend class ::node::SnapshotBuilder; NativeModuleLoader(); static NativeModuleLoader* GetInstance(); @@ -66,6 +73,8 @@ class NODE_EXTERN_PRIVATE NativeModuleLoader { bool CannotBeRequired(const char* id); NativeModuleCacheMap* code_cache(); + const Mutex& code_cache_mutex() const { return code_cache_mutex_; } + v8::ScriptCompiler::CachedData* GetCodeCache(const char* id) const; enum class Result { kWithCache, kWithoutCache }; v8::MaybeLocal LoadBuiltinModuleSource(v8::Isolate* isolate, diff --git a/src/node_native_module_env.cc b/src/node_native_module_env.cc index b388b8cd07e583..a6ba5e870c9f16 100644 --- a/src/node_native_module_env.cc +++ b/src/node_native_module_env.cc @@ -1,6 +1,9 @@ -#include "node_native_module_env.h" +#include + +#include "debug_utils-inl.h" #include "env-inl.h" #include "node_external_reference.h" +#include "node_native_module_env.h" namespace node { namespace native_module { @@ -22,6 +25,8 @@ using v8::SideEffectType; using v8::String; using v8::Value; +bool NativeModuleEnv::has_code_cache = false; + bool NativeModuleEnv::Add(const char* id, const UnionBytes& source) { return NativeModuleLoader::GetInstance()->Add(id, source); } @@ -38,6 +43,61 @@ Local NativeModuleEnv::GetConfigString(Isolate* isolate) { return NativeModuleLoader::GetInstance()->GetConfigString(isolate); } +bool NativeModuleEnv::CompileAllModules(Local context) { + NativeModuleLoader* loader = NativeModuleLoader::GetInstance(); + std::vector ids = loader->GetModuleIds(); + bool all_succeeded = true; + for (const auto& id : ids) { + // TODO(joyeecheung): compile non-module scripts here too. + if (!loader->CanBeRequired(id.c_str())) { + continue; + } + v8::TryCatch bootstrapCatch(context->GetIsolate()); + native_module::NativeModuleLoader::Result result; + USE(loader->CompileAsModule(context, id.c_str(), &result)); + if (bootstrapCatch.HasCaught()) { + per_process::Debug(DebugCategory::CODE_CACHE, + "Failed to compile code cache for %s\n", + id.c_str()); + all_succeeded = false; + PrintCaughtException(context->GetIsolate(), context, bootstrapCatch); + } + } + return all_succeeded; +} + +void NativeModuleEnv::CopyCodeCache(std::vector* out) { + NativeModuleLoader* loader = NativeModuleLoader::GetInstance(); + Mutex::ScopedLock lock(loader->code_cache_mutex()); + auto in = loader->code_cache(); + for (auto const& item : *in) { + out->push_back( + {item.first, + {item.second->data, item.second->data + item.second->length}}); + } +} + +void NativeModuleEnv::RefreshCodeCache(const std::vector& in) { + NativeModuleLoader* loader = NativeModuleLoader::GetInstance(); + Mutex::ScopedLock lock(loader->code_cache_mutex()); + auto out = loader->code_cache(); + for (auto const& item : in) { + size_t length = item.data.size(); + uint8_t* buffer = new uint8_t[length]; + memcpy(buffer, item.data.data(), length); + auto new_cache = std::make_unique( + buffer, length, v8::ScriptCompiler::CachedData::BufferOwned); + auto cache_it = out->find(item.id); + if (cache_it != out->end()) { + // Release the old cache and replace it with the new copy. + cache_it->second.reset(new_cache.release()); + } else { + out->emplace(item.id, new_cache.release()); + } + } + NativeModuleEnv::has_code_cache = true; +} + void NativeModuleEnv::GetModuleCategories( Local property, const PropertyCallbackInfo& info) { Environment* env = Environment::GetCurrent(info); @@ -187,7 +247,7 @@ MaybeLocal NativeModuleEnv::LookupAndCompile( void HasCachedBuiltins(const FunctionCallbackInfo& args) { args.GetReturnValue().Set( - v8::Boolean::New(args.GetIsolate(), has_code_cache)); + v8::Boolean::New(args.GetIsolate(), NativeModuleEnv::has_code_cache)); } // TODO(joyeecheung): It is somewhat confusing that Class::Initialize diff --git a/src/node_native_module_env.h b/src/node_native_module_env.h index 0a53771ff5d1ca..0e8526181cf0f8 100644 --- a/src/node_native_module_env.h +++ b/src/node_native_module_env.h @@ -3,16 +3,17 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS +#include #include "node_native_module.h" namespace node { class Environment; class ExternalReferenceRegistry; - namespace native_module { -extern const bool has_code_cache; - +// TODO(joyeecheung): since it's safer to make the code cache part of the +// embedded snapshot, there is no need to separate NativeModuleEnv and +// NativeModuleLoader. Merge the two. class NativeModuleEnv { public: static void RegisterExternalReferences(ExternalReferenceRegistry* registry); @@ -33,11 +34,10 @@ class NativeModuleEnv { static bool Exists(const char* id); static bool Add(const char* id, const UnionBytes& source); - // Loads data into NativeModuleLoader::.instance.code_cache_ - // Generated by mkcodecache as node_code_cache.cc when - // the build is configured with --code-cache-path=.... They are noops - // in node_code_cache_stub.cc - static void InitializeCodeCache(); + static bool CompileAllModules(v8::Local context); + static void RefreshCodeCache(const std::vector& in); + static void CopyCodeCache(std::vector* out); + static bool has_code_cache; private: static void RecordResult(const char* id, @@ -57,6 +57,8 @@ class NativeModuleEnv { const v8::PropertyCallbackInfo& info); // Compile a specific native module as a function static void CompileFunction(const v8::FunctionCallbackInfo& args); + static void CopyCodeCache(const NativeModuleCacheMap& in, + NativeModuleCacheMap* out); }; } // namespace native_module diff --git a/src/node_snapshotable.cc b/src/node_snapshotable.cc index 2c91c0a11ce78c..1fc374842ff5c6 100644 --- a/src/node_snapshotable.cc +++ b/src/node_snapshotable.cc @@ -11,6 +11,7 @@ #include "node_file.h" #include "node_internals.h" #include "node_main_instance.h" +#include "node_native_module_env.h" #include "node_process.h" #include "node_snapshot_builder.h" #include "node_v8.h" @@ -45,6 +46,47 @@ void WriteVector(std::ostringstream* ss, const T* vec, size_t size) { } } +static std::string GetCodeCacheDefName(const std::string& id) { + char buf[64] = {0}; + size_t size = id.size(); + CHECK_LT(size, sizeof(buf)); + for (size_t i = 0; i < size; ++i) { + char ch = id[i]; + buf[i] = (ch == '-' || ch == '/') ? '_' : ch; + } + return std::string(buf) + std::string("_cache_data"); +} + +static std::string FormatSize(int size) { + char buf[64] = {0}; + if (size < 1024) { + snprintf(buf, sizeof(buf), "%.2fB", static_cast(size)); + } else if (size < 1024 * 1024) { + snprintf(buf, sizeof(buf), "%.2fKB", static_cast(size / 1024)); + } else { + snprintf( + buf, sizeof(buf), "%.2fMB", static_cast(size / 1024 / 1024)); + } + return buf; +} + +static void WriteStaticCodeCacheData(std::ostringstream* ss, + const native_module::CodeCacheInfo& info) { + *ss << "static const uint8_t " << GetCodeCacheDefName(info.id) << "[] = {\n"; + WriteVector(ss, info.data.data(), info.data.size()); + *ss << "};"; +} + +static void WriteCodeCacheInitializer(std::ostringstream* ss, + const std::string& id) { + std::string def_name = GetCodeCacheDefName(id); + *ss << " { \"" << id << "\",\n"; + *ss << " {" << def_name << ",\n"; + *ss << " " << def_name << " + arraysize(" << def_name << "),\n"; + *ss << " }\n"; + *ss << " },\n"; +} + std::string FormatBlob(SnapshotData* data) { std::ostringstream ss; @@ -57,20 +99,26 @@ std::string FormatBlob(SnapshotData* data) { namespace node { -static const char blob_data[] = { +static const char v8_snapshot_blob_data[] = { )"; WriteVector(&ss, data->v8_snapshot_blob_data.data, data->v8_snapshot_blob_data.raw_size); ss << R"(}; -static const int blob_size = )" - << data->v8_snapshot_blob_data.raw_size << R"(; +static const int v8_snapshot_blob_size = )" + << data->v8_snapshot_blob_data.raw_size << ";"; + + // Windows can't deal with too many large vector initializers. + // Store the data into static arrays first. + for (const auto& item : data->code_cache) { + WriteStaticCodeCacheData(&ss, item); + } -SnapshotData snapshot_data { - // -- blob begins -- - { blob_data, blob_size }, - // -- blob ends -- + ss << R"(SnapshotData snapshot_data { + // -- v8_snapshot_blob_data begins -- + { v8_snapshot_blob_data, v8_snapshot_blob_size }, + // -- v8_snapshot_blob_data ends -- // -- isolate_data_indices begins -- { )"; @@ -83,6 +131,15 @@ SnapshotData snapshot_data { )" << data->env_info << R"( // -- env_info ends -- + , + // -- code_cache begins -- + {)"; + for (const auto& item : data->code_cache) { + WriteCodeCacheInitializer(&ss, item.id); + } + ss << R"( + } + // -- code_cache ends -- }; const SnapshotData* SnapshotBuilder::GetEmbeddedSnapshotData() { @@ -226,6 +283,19 @@ void SnapshotBuilder::Generate(SnapshotData* out, size_t index = creator.AddContext( main_context, {SerializeNodeContextInternalFields, env}); CHECK_EQ(index, SnapshotData::kNodeMainContextIndex); + +#ifdef NODE_USE_NODE_CODE_CACHE + // Regenerate all the code cache. + CHECK(native_module::NativeModuleEnv::CompileAllModules(main_context)); + native_module::NativeModuleEnv::CopyCodeCache(&(out->code_cache)); + for (const auto& item : out->code_cache) { + std::string size_str = FormatSize(item.data.size()); + per_process::Debug(DebugCategory::MKSNAPSHOT, + "Generated code cache for %d: %s\n", + item.id.c_str(), + size_str.c_str()); + } +#endif } } diff --git a/src/node_wasm_web_api.cc b/src/node_wasm_web_api.cc index 1d3febdca6e52b..67437034bbee34 100644 --- a/src/node_wasm_web_api.cc +++ b/src/node_wasm_web_api.cc @@ -2,6 +2,7 @@ #include "memory_tracker-inl.h" #include "node_errors.h" +#include "node_external_reference.h" namespace node { namespace wasm_web_api { diff --git a/test/parallel/test-code-cache.js b/test/parallel/test-code-cache.js index f61ed9f5c54077..00deafd6d49465 100644 --- a/test/parallel/test-code-cache.js +++ b/test/parallel/test-code-cache.js @@ -36,8 +36,8 @@ const loadedModules = extractModules(process.moduleLoadList); // Cross-compiled binaries do not have code cache, verifies that the builtins // are all compiled without cache and we are doing the bookkeeping right. if (!process.features.cached_builtins) { - console.log('The binary is not configured with code cache'); - assert(!process.config.variables.node_use_node_code_cache); + assert(!process.config.variables.node_use_node_code_cache || + process.execArgv.includes('--no-node-snapshot')); if (isMainThread) { assert.deepStrictEqual(compiledWithCache, new Set()); diff --git a/tools/code_cache/README.md b/tools/code_cache/README.md deleted file mode 100644 index 8feb280caae585..00000000000000 --- a/tools/code_cache/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Node.js code cache builder - -This is the V8 code cache builder of Node.js. It pre-compiles all the -JavaScript native modules of Node.js and serializes the code cache (including -the bytecodes) that will be embedded into the Node.js executable. When a Node.js -JavaScript native module is `require`d at runtime, Node.js can deserialize from -the code cache instead of parsing the source code and generating the bytecode -for it before execution, which should reduce the load time of these JavaScript -native modules. - -## How it's built and used - -The code cache builder is built with the `mkcodecache` target in `node.gyp` -when `node_use_node_code_cache` is set to true, which is currently done by -default. - -In the default build of the Node.js executable, to embed the V8 code cache of -the native modules into the Node.js executable, `libnode` is first built with -these unresolved symbols: - -- `node::native_module::has_code_cache` -- `node::native_module::NativeModuleEnv::InitializeCodeCache` - -Then the `mkcodecache` executable is built with C++ files in this directory, -as well as `src/node_code_cache_stub.cc` which defines the unresolved symbols. - -`mkcodecache` is run to generate a C++ file -`<(SHARED_INTERMEDIATE_DIR)/node_code_cache.cc` that is similar to -`src/node_code_cache_stub.cc` in structure, but contains the code cache data -written as static char array literals. Then `libnode` is built with -`node_code_cache.cc` to produce the final Node.js executable with the code -cache data embedded. - -For debugging, Node.js can be built without code cache if -`--without-node-code-cache` is passed to `configure`. Note that even if the -code cache is not pre-compiled and embedded into the Node.js executable, the -internal infrastructure is still used to share code cache between the main -thread and worker threads (if there is any). diff --git a/tools/code_cache/cache_builder.cc b/tools/code_cache/cache_builder.cc deleted file mode 100644 index 837357a0fbda76..00000000000000 --- a/tools/code_cache/cache_builder.cc +++ /dev/null @@ -1,148 +0,0 @@ -#include "cache_builder.h" -#include "debug_utils-inl.h" -#include "node_native_module.h" -#include "util.h" - -#include -#include -#include -#include -#include - -namespace node { -namespace native_module { - -using v8::Context; -using v8::Local; -using v8::ScriptCompiler; - -static std::string GetDefName(const std::string& id) { - char buf[64] = {0}; - size_t size = id.size(); - CHECK_LT(size, sizeof(buf)); - for (size_t i = 0; i < size; ++i) { - char ch = id[i]; - buf[i] = (ch == '-' || ch == '/') ? '_' : ch; - } - return buf; -} - -static std::string FormatSize(size_t size) { - char buf[64] = {0}; - if (size < 1024) { - snprintf(buf, sizeof(buf), "%.2fB", static_cast(size)); - } else if (size < 1024 * 1024) { - snprintf(buf, sizeof(buf), "%.2fKB", static_cast(size / 1024)); - } else { - snprintf( - buf, sizeof(buf), "%.2fMB", static_cast(size / 1024 / 1024)); - } - return buf; -} - -static std::string GetDefinition(const std::string& id, - size_t size, - const uint8_t* data) { - std::stringstream ss; - ss << "static const uint8_t " << GetDefName(id) << "[] = {\n"; - for (size_t i = 0; i < size; ++i) { - uint8_t ch = data[i]; - ss << std::to_string(ch) << (i == size - 1 ? '\n' : ','); - } - ss << "};"; - return ss.str(); -} - -static void GetInitializer(const std::string& id, std::stringstream& ss) { - std::string def_name = GetDefName(id); - ss << " code_cache.emplace(\n"; - ss << " \"" << id << "\",\n"; - ss << " std::make_unique(\n"; - ss << " " << def_name << ",\n"; - ss << " static_cast(arraysize(" << def_name << ")), policy\n"; - ss << " )\n"; - ss << " );"; -} - -static std::string GenerateCodeCache( - const std::map& data) { - std::stringstream ss; - ss << R"(#include -#include "node_native_module_env.h" - -// This file is generated by mkcodecache (tools/code_cache/mkcodecache.cc) - -namespace node { -namespace native_module { - -const bool has_code_cache = true; - -)"; - - size_t total = 0; - for (const auto& x : data) { - const std::string& id = x.first; - ScriptCompiler::CachedData* cached_data = x.second; - total += cached_data->length; - std::string def = GetDefinition(id, cached_data->length, cached_data->data); - ss << def << "\n\n"; - std::string size_str = FormatSize(cached_data->length); - std::string total_str = FormatSize(total); - per_process::Debug(DebugCategory::CODE_CACHE, - "Generated cache for %s, size = %s, total = %s\n", - id.c_str(), - size_str.c_str(), - total_str.c_str()); - } - - ss << R"(void NativeModuleEnv::InitializeCodeCache() { - NativeModuleCacheMap& code_cache = - *NativeModuleLoader::GetInstance()->code_cache(); - CHECK(code_cache.empty()); - auto policy = v8::ScriptCompiler::CachedData::BufferPolicy::BufferNotOwned; -)"; - - for (const auto& x : data) { - GetInitializer(x.first, ss); - ss << "\n\n"; - } - - ss << R"( -} - -} // namespace native_module -} // namespace node -)"; - return ss.str(); -} - -std::string CodeCacheBuilder::Generate(Local context) { - NativeModuleLoader* loader = NativeModuleLoader::GetInstance(); - std::vector ids = loader->GetModuleIds(); - - std::map data; - - for (const auto& id : ids) { - // TODO(joyeecheung): we can only compile the modules that can be - // required here because the parameters for other types of builtins - // are still very flexible. We should look into auto-generating - // the parameters from the source somehow. - if (loader->CanBeRequired(id.c_str())) { - NativeModuleLoader::Result result; - USE(loader->CompileAsModule(context, id.c_str(), &result)); - ScriptCompiler::CachedData* cached_data = - loader->GetCodeCache(id.c_str()); - if (cached_data == nullptr) { - // TODO(joyeecheung): display syntax errors - std::cerr << "Failed to compile " << id << "\n"; - } else { - data.emplace(id, cached_data); - } - } - } - - return GenerateCodeCache(data); -} - -} // namespace native_module -} // namespace node diff --git a/tools/code_cache/cache_builder.h b/tools/code_cache/cache_builder.h deleted file mode 100644 index d5a6cd4241dfb0..00000000000000 --- a/tools/code_cache/cache_builder.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef TOOLS_CODE_CACHE_CACHE_BUILDER_H_ -#define TOOLS_CODE_CACHE_CACHE_BUILDER_H_ - -#include -#include "v8.h" - -namespace node { -namespace native_module { -class CodeCacheBuilder { - public: - static std::string Generate(v8::Local context); -}; -} // namespace native_module -} // namespace node - -#endif // TOOLS_CODE_CACHE_CACHE_BUILDER_H_ diff --git a/tools/code_cache/mkcodecache.cc b/tools/code_cache/mkcodecache.cc deleted file mode 100644 index 68690252a147cd..00000000000000 --- a/tools/code_cache/mkcodecache.cc +++ /dev/null @@ -1,74 +0,0 @@ -#include -#include -#include -#include -#include - -#include "cache_builder.h" -#include "debug_utils-inl.h" -#include "libplatform/libplatform.h" -#include "v8.h" - -using node::native_module::CodeCacheBuilder; -using v8::ArrayBuffer; -using v8::Context; -using v8::HandleScope; -using v8::Isolate; -using v8::Local; - -#ifdef _WIN32 -#include -#include -#include - -int wmain(int argc, wchar_t* argv[]) { -#else // UNIX -int main(int argc, char* argv[]) { - argv = uv_setup_args(argc, argv); -#endif // _WIN32 - - v8::V8::SetFlagsFromString("--random_seed=42"); - v8::V8::SetFlagsFromString("--harmony-import-assertions"); - - if (argc < 2) { - std::cerr << "Usage: " << argv[0] << " \n"; - return 1; - } - - std::ofstream out; - out.open(argv[1], std::ios::out | std::ios::binary); - if (!out.is_open()) { - std::cerr << "Cannot open " << argv[1] << "\n"; - return 1; - } - - node::per_process::enabled_debug_list.Parse(nullptr); - - std::unique_ptr platform = v8::platform::NewDefaultPlatform(); - v8::V8::InitializePlatform(platform.get()); - v8::V8::Initialize(); - - // Create a new Isolate and make it the current one. - Isolate::CreateParams create_params; - create_params.array_buffer_allocator_shared.reset( - ArrayBuffer::Allocator::NewDefaultAllocator()); - Isolate* isolate = Isolate::New(create_params); - { - Isolate::Scope isolate_scope(isolate); - v8::HandleScope handle_scope(isolate); - v8::Local context = v8::Context::New(isolate); - v8::Context::Scope context_scope(context); - - // The command line flags are part of the code cache's checksum so reset - // --random_seed= to its default value before creating the code cache. - v8::V8::SetFlagsFromString("--random_seed=0"); - std::string cache = CodeCacheBuilder::Generate(context); - out << cache; - out.close(); - } - isolate->Dispose(); - - v8::V8::Dispose(); - v8::V8::DisposePlatform(); - return 0; -} From 581c046101645d68a3813bdfef960fa5a4a6d8ca Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 10 May 2022 20:14:55 +0800 Subject: [PATCH 3/5] fixup! bootstrap: include code cache in the embedded snapshot --- lib/internal/bootstrap/node.js | 2 +- src/env.h | 4 ++-- src/node_native_module.h | 1 - src/node_native_module_env.cc | 4 ++-- src/node_native_module_env.h | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 7506bf0204a9bc..0be64e136d19a6 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -271,7 +271,7 @@ const features = { tls_ocsp: hasOpenSSL, tls: hasOpenSSL, // This needs to be dynamic because --no-node-snapshot disables the - // code cache even if the bianry is built with embedded code cache. + // code cache even if the binary is built with embedded code cache. get cached_builtins() { return nativeModule.hasCachedBuiltins(); } diff --git a/src/env.h b/src/env.h index 82240de8ced616..e55bb70daa72d7 100644 --- a/src/env.h +++ b/src/env.h @@ -985,8 +985,8 @@ struct SnapshotData { EnvSerializeInfo env_info; // A vector of built-in ids and v8::ScriptCompiler::CachedData, this can be // shared across Node.js instances because they are supposed to share the - // read only space. We use the vector because v8::ScriptCompiler::CachedData - // is not copyable. + // read only space. We use native_module::CodeCacheInfo because + // v8::ScriptCompiler::CachedData is not copyable. std::vector code_cache; }; diff --git a/src/node_native_module.h b/src/node_native_module.h index 7b8360f422b00f..1bbb400586e070 100644 --- a/src/node_native_module.h +++ b/src/node_native_module.h @@ -44,7 +44,6 @@ class NODE_EXTERN_PRIVATE NativeModuleLoader { // Only allow access from friends. friend class NativeModuleEnv; friend class CodeCacheBuilder; - friend class ::node::SnapshotBuilder; NativeModuleLoader(); static NativeModuleLoader* GetInstance(); diff --git a/src/node_native_module_env.cc b/src/node_native_module_env.cc index a6ba5e870c9f16..24e12023c9bfee 100644 --- a/src/node_native_module_env.cc +++ b/src/node_native_module_env.cc @@ -95,7 +95,7 @@ void NativeModuleEnv::RefreshCodeCache(const std::vector& in) { out->emplace(item.id, new_cache.release()); } } - NativeModuleEnv::has_code_cache = true; + NativeModuleEnv::has_code_cache_ = true; } void NativeModuleEnv::GetModuleCategories( @@ -247,7 +247,7 @@ MaybeLocal NativeModuleEnv::LookupAndCompile( void HasCachedBuiltins(const FunctionCallbackInfo& args) { args.GetReturnValue().Set( - v8::Boolean::New(args.GetIsolate(), NativeModuleEnv::has_code_cache)); + v8::Boolean::New(args.GetIsolate(), NativeModuleEnv::has_code_cache_)); } // TODO(joyeecheung): It is somewhat confusing that Class::Initialize diff --git a/src/node_native_module_env.h b/src/node_native_module_env.h index 0e8526181cf0f8..6c4199fb946a1e 100644 --- a/src/node_native_module_env.h +++ b/src/node_native_module_env.h @@ -37,7 +37,6 @@ class NativeModuleEnv { static bool CompileAllModules(v8::Local context); static void RefreshCodeCache(const std::vector& in); static void CopyCodeCache(std::vector* out); - static bool has_code_cache; private: static void RecordResult(const char* id, @@ -59,6 +58,7 @@ class NativeModuleEnv { static void CompileFunction(const v8::FunctionCallbackInfo& args); static void CopyCodeCache(const NativeModuleCacheMap& in, NativeModuleCacheMap* out); + static bool has_code_cache_; }; } // namespace native_module From d8d40fe8cb235bbcb3817b1287fbb91c48ea7499 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 10 May 2022 23:53:24 +0800 Subject: [PATCH 4/5] fixup! fixup! bootstrap: include code cache in the embedded snapshot --- src/node_native_module_env.cc | 4 ++-- src/node_native_module_env.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node_native_module_env.cc b/src/node_native_module_env.cc index 24e12023c9bfee..4914de3c253860 100644 --- a/src/node_native_module_env.cc +++ b/src/node_native_module_env.cc @@ -25,7 +25,7 @@ using v8::SideEffectType; using v8::String; using v8::Value; -bool NativeModuleEnv::has_code_cache = false; +bool NativeModuleEnv::has_code_cache_ = false; bool NativeModuleEnv::Add(const char* id, const UnionBytes& source) { return NativeModuleLoader::GetInstance()->Add(id, source); @@ -245,7 +245,7 @@ MaybeLocal NativeModuleEnv::LookupAndCompile( return maybe; } -void HasCachedBuiltins(const FunctionCallbackInfo& args) { +void NativeModuleEnv::HasCachedBuiltins(const FunctionCallbackInfo& args) { args.GetReturnValue().Set( v8::Boolean::New(args.GetIsolate(), NativeModuleEnv::has_code_cache_)); } diff --git a/src/node_native_module_env.h b/src/node_native_module_env.h index 6c4199fb946a1e..c7edf4447fd965 100644 --- a/src/node_native_module_env.h +++ b/src/node_native_module_env.h @@ -56,8 +56,8 @@ class NativeModuleEnv { const v8::PropertyCallbackInfo& info); // Compile a specific native module as a function static void CompileFunction(const v8::FunctionCallbackInfo& args); - static void CopyCodeCache(const NativeModuleCacheMap& in, - NativeModuleCacheMap* out); + static void HasCachedBuiltins(const v8::FunctionCallbackInfo& args); + static bool has_code_cache_; }; From a181607d5e260d65fccbbbab5da79212c4a5a7c2 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 11 May 2022 18:21:35 +0800 Subject: [PATCH 5/5] fixup! fixup! fixup! bootstrap: include code cache in the embedded snapshot --- src/node_native_module_env.cc | 3 ++- src/node_native_module_env.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/node_native_module_env.cc b/src/node_native_module_env.cc index 4914de3c253860..05513b89f8f188 100644 --- a/src/node_native_module_env.cc +++ b/src/node_native_module_env.cc @@ -245,7 +245,8 @@ MaybeLocal NativeModuleEnv::LookupAndCompile( return maybe; } -void NativeModuleEnv::HasCachedBuiltins(const FunctionCallbackInfo& args) { +void NativeModuleEnv::HasCachedBuiltins( + const FunctionCallbackInfo& args) { args.GetReturnValue().Set( v8::Boolean::New(args.GetIsolate(), NativeModuleEnv::has_code_cache_)); } diff --git a/src/node_native_module_env.h b/src/node_native_module_env.h index c7edf4447fd965..adcfc7b6fb4954 100644 --- a/src/node_native_module_env.h +++ b/src/node_native_module_env.h @@ -56,7 +56,8 @@ class NativeModuleEnv { const v8::PropertyCallbackInfo& info); // Compile a specific native module as a function static void CompileFunction(const v8::FunctionCallbackInfo& args); - static void HasCachedBuiltins(const v8::FunctionCallbackInfo& args); + static void HasCachedBuiltins( + const v8::FunctionCallbackInfo& args); static bool has_code_cache_; };