GeezerVison is showing github.com — sea: add support for V8 bytecode-only caching · nodejs/node@bc009d0
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/single-executable-applications.md +17-1Lines changed: 17 additions & 1 deletion - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -10,6 +10,9 @@ changes: | 10 | 10 | - version: REPLACEME | 11 | 11 | pr-url: https://github.com/nodejs/node/pull/46824 | 12 | 12 | description: Added support for "useSnapshot". | | 13 | + - version: REPLACEME | | 14 | + pr-url: https://github.com/nodejs/node/pull/48191 | | 15 | + description: Added support for "useCodeCache". | 13 | 16 | --> | 14 | 17 |
| 15 | 18 | > Stability: 1 - Experimental: This feature is being designed and will change. | @@ -174,7 +177,8 @@ The configuration currently reads the following top-level fields: | 174 | 177 | "main": "/path/to/bundled/script.js", | 175 | 178 | "output": "/path/to/write/the/generated/blob.blob", | 176 | 179 | "disableExperimentalSEAWarning": true, // Default: false | 177 | | - "useSnapshot": false // Default: false | | 180 | + "useSnapshot": false, // Default: false | | 181 | + "useCodeCache": true // Default: false | 178 | 182 | } | 179 | 183 | ``` | 180 | 184 |
| @@ -213,6 +217,18 @@ and the main script can use the [`v8.startupSnapshot` API][] to adapt to | 213 | 217 | these constraints. See | 214 | 218 | [documentation about startup snapshot support in Node.js][]. | 215 | 219 |
| | 220 | +### V8 code cache support | | 221 | +
| | 222 | +When `useCodeCache` is set to `true` in the configuration, during the generation | | 223 | +of the single executable preparation blob, Node.js will compile the `main` | | 224 | +script to generate the V8 code cache. The generated code cache would be part of | | 225 | +the preparation blob and get injected into the final executable. When the single | | 226 | +executable application is launched, instead of compiling the `main` script from | | 227 | +scratch, Node.js would use the code cache to speed up the compilation, then | | 228 | +execute the script, which would improve the startup performance. | | 229 | +
| | 230 | +**Note:** `import()` does not work when `useCodeCache` is `true`. | | 231 | +
| 216 | 232 | ## Notes | 217 | 233 |
| 218 | 234 | ### `require(id)` in the injected module is not file based | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js +9-1Lines changed: 9 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -1123,7 +1123,7 @@ Module.prototype.require = function(id) { | 1123 | 1123 | let resolvedArgv; | 1124 | 1124 | let hasPausedEntry = false; | 1125 | 1125 | let Script; | 1126 | | -function wrapSafe(filename, content, cjsModuleInstance) { | | 1126 | +function wrapSafe(filename, content, cjsModuleInstance, codeCache) { | 1127 | 1127 | if (patched) { | 1128 | 1128 | const wrapper = Module.wrap(content); | 1129 | 1129 | if (Script === undefined) { | @@ -1158,13 +1158,21 @@ function wrapSafe(filename, content, cjsModuleInstance) { | 1158 | 1158 | '__dirname', | 1159 | 1159 | ], { | 1160 | 1160 | filename, | | 1161 | + cachedData: codeCache, | 1161 | 1162 | importModuleDynamically(specifier, _, importAssertions) { | 1162 | 1163 | const cascadedLoader = getCascadedLoader(); | 1163 | 1164 | return cascadedLoader.import(specifier, normalizeReferrerURL(filename), | 1164 | 1165 | importAssertions); | 1165 | 1166 | }, | 1166 | 1167 | }); | 1167 | 1168 |
| | 1169 | + // The code cache is used for SEAs only. | | 1170 | + if (codeCache && | | 1171 | + result.cachedDataRejected !== false && | | 1172 | + internalBinding('sea').isSea()) { | | 1173 | + process.emitWarning('Code cache data rejected.'); | | 1174 | + } | | 1175 | + | 1168 | 1176 | // Cache the source map for the module if present. | 1169 | 1177 | if (result.sourceMapURL) { | 1170 | 1178 | maybeCacheSourceMap(filename, content, this, false, undefined, result.sourceMapURL); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/util/embedding.js +7-2Lines changed: 7 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | 'use strict'; | 2 | | -const { codes: { ERR_UNKNOWN_BUILTIN_MODULE } } = require('internal/errors'); | 3 | 2 | const { BuiltinModule: { normalizeRequirableId } } = require('internal/bootstrap/realm'); | 4 | 3 | const { Module, wrapSafe } = require('internal/modules/cjs/loader'); | | 4 | +const { codes: { ERR_UNKNOWN_BUILTIN_MODULE } } = require('internal/errors'); | | 5 | +const { getCodeCache, getCodePath, isSea } = internalBinding('sea'); | 5 | 6 |
| 6 | 7 | // This is roughly the same as: | 7 | 8 | // | @@ -15,7 +16,11 @@ const { Module, wrapSafe } = require('internal/modules/cjs/loader'); | 15 | 16 |
| 16 | 17 | function embedderRunCjs(contents) { | 17 | 18 | const filename = process.execPath; | 18 | | - const compiledWrapper = wrapSafe(filename, contents); | | 19 | + const compiledWrapper = wrapSafe( | | 20 | + isSea() ? getCodePath() : filename, | | 21 | + contents, | | 22 | + undefined, | | 23 | + getCodeCache()); | 19 | 24 |
| 20 | 25 | const customModule = new Module(filename, null); | 21 | 26 | customModule.filename = filename; | |
Collapse file Copy file name to clipboardExpand all lines: src/json_parser.cc +1-20Lines changed: 1 addition & 20 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 4 | 4 | #include "util-inl.h" | 5 | 5 |
| 6 | 6 | namespace node { | 7 | | -using v8::ArrayBuffer; | 8 | 7 | using v8::Context; | 9 | 8 | using v8::Isolate; | 10 | 9 | using v8::Local; | 11 | 10 | using v8::Object; | 12 | 11 | using v8::String; | 13 | 12 | using v8::Value; | 14 | 13 |
| 15 | | -static Isolate* NewIsolate(v8::ArrayBuffer::Allocator* allocator) { | 16 | | - Isolate* isolate = Isolate::Allocate(); | 17 | | - CHECK_NOT_NULL(isolate); | 18 | | - per_process::v8_platform.Platform()->RegisterIsolate(isolate, | 19 | | - uv_default_loop()); | 20 | | - Isolate::CreateParams params; | 21 | | - params.array_buffer_allocator = allocator; | 22 | | - Isolate::Initialize(isolate, params); | 23 | | - return isolate; | 24 | | -} | 25 | | - | 26 | | -void JSONParser::FreeIsolate(Isolate* isolate) { | 27 | | - per_process::v8_platform.Platform()->UnregisterIsolate(isolate); | 28 | | - isolate->Dispose(); | 29 | | -} | 30 | | - | 31 | 14 | JSONParser::JSONParser() | 32 | | - : allocator_(ArrayBuffer::Allocator::NewDefaultAllocator()), | 33 | | - isolate_(NewIsolate(allocator_.get())), | 34 | | - handle_scope_(isolate_.get()), | | 15 | + : handle_scope_(isolate_.get()), | 35 | 16 | context_(isolate_.get(), Context::New(isolate_.get())), | 36 | 17 | context_scope_(context_.Get(isolate_.get())) {} | 37 | 18 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/json_parser.h +1-3Lines changed: 1 addition & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -24,9 +24,7 @@ class JSONParser { | 24 | 24 | private: | 25 | 25 | // We might want a lighter-weight JSON parser for this use case. But for now | 26 | 26 | // using V8 is good enough. | 27 | | - static void FreeIsolate(v8::Isolate* isolate); | 28 | | - std::unique_ptr<v8::ArrayBuffer::Allocator> allocator_; | 29 | | - DeleteFnPtr<v8::Isolate, FreeIsolate> isolate_; | | 27 | + RAIIIsolate isolate_; | 30 | 28 | v8::HandleScope handle_scope_; | 31 | 29 | v8::Global<v8::Context> context_; | 32 | 30 | v8::Context::Scope context_scope_; | |
Collapse file Copy file name to clipboardExpand all lines: src/node_contextify.cc +16Lines changed: 16 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -935,6 +935,22 @@ Maybe<bool> StoreCodeCacheResult( | 935 | 935 | return Just(true); | 936 | 936 | } | 937 | 937 |
| | 938 | +// TODO(RaisinTen): Reuse in ContextifyContext::CompileFunction(). | | 939 | +MaybeLocal<Function> CompileFunction(Local<Context> context, | | 940 | + Local<String> filename, | | 941 | + Local<String> content, | | 942 | + std::vector<Local<String>>* parameters) { | | 943 | + ScriptOrigin script_origin(context->GetIsolate(), filename, 0, 0, true); | | 944 | + ScriptCompiler::Source script_source(content, script_origin); | | 945 | + | | 946 | + return ScriptCompiler::CompileFunction(context, | | 947 | + &script_source, | | 948 | + parameters->size(), | | 949 | + parameters->data(), | | 950 | + 0, | | 951 | + nullptr); | | 952 | +} | | 953 | + | 938 | 954 | bool ContextifyScript::InstanceOf(Environment* env, | 939 | 955 | const Local<Value>& value) { | 940 | 956 | return !value.IsEmpty() && | |
Collapse file Copy file name to clipboardExpand all lines: src/node_contextify.h +6Lines changed: 6 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -210,6 +210,12 @@ v8::Maybe<bool> StoreCodeCacheResult( | 210 | 210 | bool produce_cached_data, | 211 | 211 | std::unique_ptr<v8::ScriptCompiler::CachedData> new_cached_data); | 212 | 212 |
| | 213 | +v8::MaybeLocal<v8::Function> CompileFunction( | | 214 | + v8::Local<v8::Context> context, | | 215 | + v8::Local<v8::String> filename, | | 216 | + v8::Local<v8::String> content, | | 217 | + std::vector<v8::Local<v8::String>>* parameters); | | 218 | + | 213 | 219 | } // namespace contextify | 214 | 220 | } // namespace node | 215 | 221 |
| |
|