| #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 <ranges> |
| #include <string> |
| #include <unordered_set> |
| #include <vector> |
| #include "builtin_info.h" |
| #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; |
| }; |
| |
| struct BuiltinSource { |
| std::string id; |
| UnionBytes source; |
| BuiltinSourceType type; |
| }; |
| |
| using BuiltinSourceMap = std::map<std::string, BuiltinSource>; |
| using BuiltinCodeCacheMap = |
| std::unordered_map<std::string, BuiltinCodeCacheData>; |
| |
| // Generated by tools/js2c.cc 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> LookupAndCompileFunction( |
| v8::Local<v8::Context> context, const char* id, Realm* optional_realm); |
| |
| v8::MaybeLocal<v8::Value> CompileAndCallWith(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); |
| const BuiltinSource* AddFromDisk(const char* id, |
| const std::string& filename, |
| 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); |
| |
| [[nodiscard]] std::ranges::keys_view< |
| std::ranges::ref_view<const BuiltinSourceMap>> |
| GetBuiltinIds() const; |
| |
| void SetEagerCompile() { should_eager_compile_ = true; } |
| |
| private: |
| // Only allow access from friends. |
| friend class CodeCacheBuilder; |
| |
| // Generated by tools/js2c.cc 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 }; |
| const BuiltinSource* LoadBuiltinSource(v8::Isolate* isolate, const char* id); |
| // If an exception is encountered (e.g. source code contains |
| // syntax error), the returned value is empty. |
| v8::MaybeLocal<v8::Data> LookupAndCompile(v8::Local<v8::Context> context, |
| const char* id, |
| Realm* optional_realm); |
| v8::MaybeLocal<v8::Data> LookupAndCompile(v8::Local<v8::Context> context, |
| const BuiltinSource* builtin_source, |
| Realm* optional_realm); |
| void SaveCodeCache(const std::string& id, v8::Local<v8::Data> data); |
| |
| static void RecordResult(const std::string& 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); |
| v8::MaybeLocal<v8::Module> LoadBuiltinSourceTextModule(Realm* realm, |
| const char* id); |
| v8::MaybeLocal<v8::Value> ImportBuiltinSourceTextModule(Realm* realm, |
| const char* id); |
| static void ImportBuiltinSourceTextModule( |
| const v8::FunctionCallbackInfo<v8::Value>& args); |
| |
| static v8::MaybeLocal<v8::Module> ResolveModuleCallback( |
| v8::Local<v8::Context> context, |
| v8::Local<v8::String> specifier, |
| v8::Local<v8::FixedArray> import_attributes, |
| v8::Local<v8::Module> referrer); |
| 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); |
| |
| const BuiltinSource* 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_; |
| |
| std::unordered_map<std::string, v8::Global<v8::Module>> module_cache_; |
| friend class ::PerProcessTest; |
| }; |
| |
| } // namespace builtins |
| |
| } // namespace node |
| |
| #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS |
| |
| #endif // SRC_NODE_BUILTINS_H_ |