Skip to content

Commit bc009d0

Browse files
RaisinTenRafaelGSS
authored andcommitted
sea: add support for V8 bytecode-only caching
Refs: nodejs/single-executable#73 Signed-off-by: Darshan Sen <[email protected]> PR-URL: #48191 Fixes: nodejs/single-executable#73 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Stephen Belanger <[email protected]> Reviewed-By: Joyee Cheung <[email protected]>
1 parent 660da78 commit bc009d0

15 files changed

+347
-41
lines changed

doc/api/single-executable-applications.md

+17-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ changes:
1010
- version: REPLACEME
1111
pr-url: https://github.com/nodejs/node/pull/46824
1212
description: Added support for "useSnapshot".
13+
- version: REPLACEME
14+
pr-url: https://github.com/nodejs/node/pull/48191
15+
description: Added support for "useCodeCache".
1316
-->
1417

1518
> Stability: 1 - Experimental: This feature is being designed and will change.
@@ -174,7 +177,8 @@ The configuration currently reads the following top-level fields:
174177
"main": "/path/to/bundled/script.js",
175178
"output": "/path/to/write/the/generated/blob.blob",
176179
"disableExperimentalSEAWarning": true, // Default: false
177-
"useSnapshot": false // Default: false
180+
"useSnapshot": false, // Default: false
181+
"useCodeCache": true // Default: false
178182
}
179183
```
180184
@@ -213,6 +217,18 @@ and the main script can use the [`v8.startupSnapshot` API][] to adapt to
213217
these constraints. See
214218
[documentation about startup snapshot support in Node.js][].
215219
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+
216232
## Notes
217233
218234
### `require(id)` in the injected module is not file based

lib/internal/modules/cjs/loader.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -1123,7 +1123,7 @@ Module.prototype.require = function(id) {
11231123
let resolvedArgv;
11241124
let hasPausedEntry = false;
11251125
let Script;
1126-
function wrapSafe(filename, content, cjsModuleInstance) {
1126+
function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
11271127
if (patched) {
11281128
const wrapper = Module.wrap(content);
11291129
if (Script === undefined) {
@@ -1158,13 +1158,21 @@ function wrapSafe(filename, content, cjsModuleInstance) {
11581158
'__dirname',
11591159
], {
11601160
filename,
1161+
cachedData: codeCache,
11611162
importModuleDynamically(specifier, _, importAssertions) {
11621163
const cascadedLoader = getCascadedLoader();
11631164
return cascadedLoader.import(specifier, normalizeReferrerURL(filename),
11641165
importAssertions);
11651166
},
11661167
});
11671168

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+
11681176
// Cache the source map for the module if present.
11691177
if (result.sourceMapURL) {
11701178
maybeCacheSourceMap(filename, content, this, false, undefined, result.sourceMapURL);

lib/internal/util/embedding.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
'use strict';
2-
const { codes: { ERR_UNKNOWN_BUILTIN_MODULE } } = require('internal/errors');
32
const { BuiltinModule: { normalizeRequirableId } } = require('internal/bootstrap/realm');
43
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');
56

67
// This is roughly the same as:
78
//
@@ -15,7 +16,11 @@ const { Module, wrapSafe } = require('internal/modules/cjs/loader');
1516

1617
function embedderRunCjs(contents) {
1718
const filename = process.execPath;
18-
const compiledWrapper = wrapSafe(filename, contents);
19+
const compiledWrapper = wrapSafe(
20+
isSea() ? getCodePath() : filename,
21+
contents,
22+
undefined,
23+
getCodeCache());
1924

2025
const customModule = new Module(filename, null);
2126
customModule.filename = filename;

src/json_parser.cc

+1-20
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,15 @@
44
#include "util-inl.h"
55

66
namespace node {
7-
using v8::ArrayBuffer;
87
using v8::Context;
98
using v8::Isolate;
109
using v8::Local;
1110
using v8::Object;
1211
using v8::String;
1312
using v8::Value;
1413

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-
3114
JSONParser::JSONParser()
32-
: allocator_(ArrayBuffer::Allocator::NewDefaultAllocator()),
33-
isolate_(NewIsolate(allocator_.get())),
34-
handle_scope_(isolate_.get()),
15+
: handle_scope_(isolate_.get()),
3516
context_(isolate_.get(), Context::New(isolate_.get())),
3617
context_scope_(context_.Get(isolate_.get())) {}
3718

src/json_parser.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ class JSONParser {
2424
private:
2525
// We might want a lighter-weight JSON parser for this use case. But for now
2626
// 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_;
3028
v8::HandleScope handle_scope_;
3129
v8::Global<v8::Context> context_;
3230
v8::Context::Scope context_scope_;

src/node_contextify.cc

+16
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,22 @@ Maybe<bool> StoreCodeCacheResult(
935935
return Just(true);
936936
}
937937

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+
938954
bool ContextifyScript::InstanceOf(Environment* env,
939955
const Local<Value>& value) {
940956
return !value.IsEmpty() &&

src/node_contextify.h

+6
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ v8::Maybe<bool> StoreCodeCacheResult(
210210
bool produce_cached_data,
211211
std::unique_ptr<v8::ScriptCompiler::CachedData> new_cached_data);
212212

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+
213219
} // namespace contextify
214220
} // namespace node
215221

0 commit comments

Comments
 (0)