mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
By default V8 only compiles the top-level function and skips code generation for inner functions - that would only be done when those inner functions are invoked. Since builtins are compiled as wrapped functions, most functions that look visually top-level are not actually included in the built-in code cache. For most of the builtins this is not too bad because usually only a subset of all builtin functions are needed by a particular application and including all their code in the binary would incur an unnecessary size overhead. But there is also a subset of more commonly used builtins and it would be better to include the inner functions in the built-in code cache because they are more universally used by most applications. This patch changes the compilation strategy to eager compilation (including inner functions) for the following scripts: 1. Primordials (internal/per_context/*), in all situations. 2. Bootstrap scripts (internal/bootstrap/*) and main scripts (internal/main/*), when being compiled for built-in code cache. 3. Any scripts loaded during built-in snapshot generation. We can't compile the code eagerly during snapshot generation and include them into the V8 snapshot itself just now because we need to start the inspector before context deserialization for coverage collection to work. So leave that as a TODO. With this patch the binary size increases by about 666KB (~0.6% increase) in return the worker startup can be 18-19% faster. PR-URL: https://github.com/nodejs/node/pull/51672 Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io>
215 lines
8.0 KiB
C++
215 lines
8.0 KiB
C++
#ifndef SRC_NODE_BUILTINS_H_
|
|
#define SRC_NODE_BUILTINS_H_
|
|
|
|
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#include <list>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
#include "node_external_reference.h"
|
|
#include "node_mutex.h"
|
|
#include "node_threadsafe_cow.h"
|
|
#include "node_union_bytes.h"
|
|
#include "v8.h"
|
|
|
|
// Forward declare test fixture for `friend` declaration.
|
|
class PerProcessTest;
|
|
|
|
namespace node {
|
|
class SnapshotBuilder;
|
|
class ExternalReferenceRegistry;
|
|
class Realm;
|
|
|
|
namespace builtins {
|
|
|
|
class BuiltinCodeCacheData {
|
|
public:
|
|
BuiltinCodeCacheData() : data(nullptr), length(0), owning_ptr(nullptr) {}
|
|
|
|
explicit BuiltinCodeCacheData(
|
|
std::shared_ptr<v8::ScriptCompiler::CachedData> cached_data)
|
|
: data(cached_data->data),
|
|
length(cached_data->length),
|
|
owning_ptr(cached_data) {}
|
|
|
|
explicit BuiltinCodeCacheData(
|
|
std::shared_ptr<std::vector<uint8_t>> cached_data)
|
|
: data(cached_data->data()),
|
|
length(cached_data->size()),
|
|
owning_ptr(cached_data) {}
|
|
|
|
BuiltinCodeCacheData(const uint8_t* data, size_t length)
|
|
: data(data), length(length), owning_ptr(nullptr) {}
|
|
|
|
const uint8_t* data;
|
|
size_t length;
|
|
|
|
// Returns a v8::ScriptCompiler::CachedData corresponding to this
|
|
// BuiltinCodeCacheData. The lifetime of the returned
|
|
// v8::ScriptCompiler::CachedData must not outlive that of the data.
|
|
std::unique_ptr<v8::ScriptCompiler::CachedData> AsCachedData() {
|
|
return std::make_unique<v8::ScriptCompiler::CachedData>(
|
|
data, length, v8::ScriptCompiler::CachedData::BufferNotOwned);
|
|
}
|
|
|
|
private:
|
|
// If not null, represents a pointer which owns data. Otherwise indicates
|
|
// that data has static lifetime.
|
|
std::shared_ptr<void> owning_ptr;
|
|
};
|
|
|
|
struct CodeCacheInfo {
|
|
std::string id;
|
|
BuiltinCodeCacheData data;
|
|
};
|
|
|
|
using BuiltinSourceMap = std::map<std::string, UnionBytes>;
|
|
using BuiltinCodeCacheMap =
|
|
std::unordered_map<std::string, BuiltinCodeCacheData>;
|
|
|
|
// Generated by tools/js2c.py as node_javascript.cc
|
|
void RegisterExternalReferencesForInternalizedBuiltinCode(
|
|
ExternalReferenceRegistry* registry);
|
|
|
|
// Handles compilation and caching of built-in JavaScript modules and
|
|
// bootstrap scripts, whose source are bundled into the binary as static data.
|
|
class NODE_EXTERN_PRIVATE BuiltinLoader {
|
|
public:
|
|
BuiltinLoader();
|
|
BuiltinLoader(const BuiltinLoader&) = delete;
|
|
BuiltinLoader& operator=(const BuiltinLoader&) = delete;
|
|
|
|
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
|
|
static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
|
v8::Local<v8::ObjectTemplate> target);
|
|
static void CreatePerContextProperties(v8::Local<v8::Object> target,
|
|
v8::Local<v8::Value> unused,
|
|
v8::Local<v8::Context> context,
|
|
void* priv);
|
|
|
|
// The parameters used to compile the scripts are detected based on
|
|
// the pattern of the id.
|
|
v8::MaybeLocal<v8::Function> LookupAndCompile(v8::Local<v8::Context> context,
|
|
const char* id,
|
|
Realm* optional_realm);
|
|
|
|
v8::MaybeLocal<v8::Value> CompileAndCall(v8::Local<v8::Context> context,
|
|
const char* id,
|
|
int argc,
|
|
v8::Local<v8::Value> argv[],
|
|
Realm* optional_realm);
|
|
|
|
v8::MaybeLocal<v8::Value> CompileAndCall(v8::Local<v8::Context> context,
|
|
const char* id,
|
|
Realm* realm);
|
|
|
|
// Returns config.gypi as a JSON string
|
|
v8::Local<v8::String> GetConfigString(v8::Isolate* isolate);
|
|
bool Exists(const char* id);
|
|
bool Add(const char* id, const UnionBytes& source);
|
|
|
|
bool CompileAllBuiltinsAndCopyCodeCache(
|
|
v8::Local<v8::Context> context,
|
|
const std::vector<std::string>& lazy_builtins,
|
|
std::vector<CodeCacheInfo>* out);
|
|
void RefreshCodeCache(const std::vector<CodeCacheInfo>& in);
|
|
|
|
void CopySourceAndCodeCacheReferenceFrom(const BuiltinLoader* other);
|
|
|
|
std::vector<std::string_view> GetBuiltinIds() const;
|
|
|
|
void SetEagerCompile() { should_eager_compile_ = true; }
|
|
|
|
private:
|
|
// Only allow access from friends.
|
|
friend class CodeCacheBuilder;
|
|
|
|
// Generated by tools/js2c.py as node_javascript.cc
|
|
void LoadJavaScriptSource(); // Loads data into source_
|
|
UnionBytes GetConfig(); // Return data for config.gypi
|
|
|
|
struct BuiltinCategories {
|
|
std::set<std::string> can_be_required;
|
|
std::set<std::string> cannot_be_required;
|
|
};
|
|
// This method builds `BuiltinCategories` from scratch every time,
|
|
// and is therefore somewhat expensive, but also currently only being
|
|
// used for testing, so that should not be an issue.
|
|
BuiltinCategories GetBuiltinCategories() const;
|
|
|
|
const v8::ScriptCompiler::CachedData* GetCodeCache(const char* id) const;
|
|
enum class Result { kWithCache, kWithoutCache };
|
|
v8::MaybeLocal<v8::String> LoadBuiltinSource(v8::Isolate* isolate,
|
|
const char* id) const;
|
|
// If an exception is encountered (e.g. source code contains
|
|
// syntax error), the returned value is empty.
|
|
v8::MaybeLocal<v8::Function> LookupAndCompileInternal(
|
|
v8::Local<v8::Context> context,
|
|
const char* id,
|
|
std::vector<v8::Local<v8::String>>* parameters,
|
|
Realm* optional_realm);
|
|
void SaveCodeCache(const char* id, v8::Local<v8::Function> fn);
|
|
|
|
static void RecordResult(const char* id,
|
|
BuiltinLoader::Result result,
|
|
Realm* realm);
|
|
static void GetBuiltinCategories(
|
|
v8::Local<v8::Name> property,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
|
static void GetCacheUsage(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
// Passing ids of built-in source code into JS land as
|
|
// internalBinding('builtins').builtinIds
|
|
static void BuiltinIdsGetter(v8::Local<v8::Name> property,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
|
// Passing config.gypi into JS land as internalBinding('builtins').config
|
|
static void ConfigStringGetter(
|
|
v8::Local<v8::Name> property,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
|
// Compile a specific built-in as a function
|
|
static void CompileFunction(const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
static void HasCachedBuiltins(
|
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
|
// For legacy process.binding('natives')
|
|
static void GetNatives(v8::Local<v8::Name> property,
|
|
const v8::PropertyCallbackInfo<v8::Value>& info);
|
|
|
|
void AddExternalizedBuiltin(const char* id, const char* filename);
|
|
|
|
ThreadsafeCopyOnWrite<BuiltinSourceMap> source_;
|
|
|
|
const UnionBytes config_;
|
|
|
|
// If any bulitins should be eagerly compiled i.e. with inner functions
|
|
// compiled too, either use should_eager_compile_ to compile all builtins
|
|
// eagerly, or use to_eager_compile_ to compile specific builtins eagerly.
|
|
// Currently we set should_eager_compile_ to true when compiling primordials,
|
|
// and use to_eager_compile_ to compile code cache that complements the
|
|
// snapshot, where builtins already loaded in the snapshot and a few extras
|
|
// are compiled eagerly (other less-essential built-ins are compiled lazily to
|
|
// avoid bloating the binary size). At runtime any additional compilation is
|
|
// done lazily.
|
|
bool should_eager_compile_ = false;
|
|
std::unordered_set<std::string> to_eager_compile_;
|
|
|
|
struct BuiltinCodeCache {
|
|
RwLock mutex;
|
|
BuiltinCodeCacheMap map;
|
|
bool has_code_cache = false;
|
|
};
|
|
std::shared_ptr<BuiltinCodeCache> code_cache_;
|
|
|
|
friend class ::PerProcessTest;
|
|
};
|
|
|
|
} // namespace builtins
|
|
|
|
} // namespace node
|
|
|
|
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
|
|
|
|
#endif // SRC_NODE_BUILTINS_H_
|