Skip to content

Commit e161e62

Browse files
joyeecheungmarco-ippolito
authored andcommittedMay 3, 2024
src: use dedicated routine to compile function for builtin CJS loader
So that we can use it to handle code caching in a central place. Drive-by: use per-isolate persistent strings for the parameters and mark GetHostDefinedOptions() since it's only used in one compilation unit PR-URL: #52016 Refs: #47472 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
1 parent 1f12534 commit e161e62

File tree

8 files changed

+139
-96
lines changed

8 files changed

+139
-96
lines changed
 

‎lib/internal/modules/cjs/loader.js

+6-17
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,13 @@ const {
9191
getLazy,
9292
} = require('internal/util');
9393
const {
94-
internalCompileFunction,
9594
makeContextifyScript,
9695
runScriptInThisContext,
9796
} = require('internal/vm');
98-
const { containsModuleSyntax } = internalBinding('contextify');
97+
const {
98+
containsModuleSyntax,
99+
compileFunctionForCJSLoader,
100+
} = internalBinding('contextify');
99101

100102
const assert = require('internal/assert');
101103
const fs = require('fs');
@@ -1275,23 +1277,10 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
12751277
return runScriptInThisContext(script, true, false);
12761278
}
12771279

1278-
const params = [ 'exports', 'require', 'module', '__filename', '__dirname' ];
12791280
try {
1280-
const result = internalCompileFunction(
1281-
content, // code,
1282-
filename, // filename
1283-
0, // lineOffset
1284-
0, // columnOffset,
1285-
codeCache, // cachedData
1286-
false, // produceCachedData
1287-
undefined, // parsingContext
1288-
undefined, // contextExtensions
1289-
params, // params
1290-
hostDefinedOptionId, // hostDefinedOptionId
1291-
importModuleDynamically, // importModuleDynamically
1292-
);
1281+
const result = compileFunctionForCJSLoader(content, filename);
12931282

1294-
// The code cache is used for SEAs only.
1283+
// cachedDataRejected is only set for cache coming from SEA.
12951284
if (codeCache &&
12961285
result.cachedDataRejected !== false &&
12971286
internalBinding('sea').isSea()) {

‎lib/internal/modules/esm/translators.js

+7-26
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ function lazyTypes() {
2929
return _TYPES = require('internal/util/types');
3030
}
3131

32-
const { containsModuleSyntax } = internalBinding('contextify');
32+
const {
33+
containsModuleSyntax,
34+
compileFunctionForCJSLoader,
35+
} = internalBinding('contextify');
36+
3337
const { BuiltinModule } = require('internal/bootstrap/realm');
3438
const assert = require('internal/assert');
3539
const { readFileSync } = require('fs');
@@ -56,10 +60,7 @@ const { maybeCacheSourceMap } = require('internal/source_map/source_map_cache');
5660
const moduleWrap = internalBinding('module_wrap');
5761
const { ModuleWrap } = moduleWrap;
5862
const { emitWarningSync } = require('internal/process/warning');
59-
const { internalCompileFunction } = require('internal/vm');
60-
const {
61-
vm_dynamic_import_default_internal,
62-
} = internalBinding('symbols');
63+
6364
// Lazy-loading to avoid circular dependencies.
6465
let getSourceSync;
6566
/**
@@ -210,28 +211,8 @@ function enrichCJSError(err, content, filename) {
210211
*/
211212
function loadCJSModule(module, source, url, filename) {
212213
let compileResult;
213-
const hostDefinedOptionId = vm_dynamic_import_default_internal;
214-
const importModuleDynamically = vm_dynamic_import_default_internal;
215214
try {
216-
compileResult = internalCompileFunction(
217-
source, // code,
218-
filename, // filename
219-
0, // lineOffset
220-
0, // columnOffset,
221-
undefined, // cachedData
222-
false, // produceCachedData
223-
undefined, // parsingContext
224-
undefined, // contextExtensions
225-
[ // params
226-
'exports',
227-
'require',
228-
'module',
229-
'__filename',
230-
'__dirname',
231-
],
232-
hostDefinedOptionId, // hostDefinedOptionsId
233-
importModuleDynamically, // importModuleDynamically
234-
);
215+
compileResult = compileFunctionForCJSLoader(source, filename);
235216
} catch (err) {
236217
enrichCJSError(err, source, filename);
237218
throw err;

‎lib/internal/util/embedding.js

+2-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
const { BuiltinModule: { normalizeRequirableId } } = require('internal/bootstrap/realm');
33
const { Module, wrapSafe } = require('internal/modules/cjs/loader');
44
const { codes: { ERR_UNKNOWN_BUILTIN_MODULE } } = require('internal/errors');
5-
const { getCodeCache, getCodePath, isSea } = internalBinding('sea');
5+
const { getCodePath, isSea } = internalBinding('sea');
66

77
// This is roughly the same as:
88
//
@@ -18,9 +18,7 @@ function embedderRunCjs(contents) {
1818
const filename = process.execPath;
1919
const compiledWrapper = wrapSafe(
2020
isSea() ? getCodePath() : filename,
21-
contents,
22-
undefined,
23-
getCodeCache());
21+
contents);
2422

2523
const customModule = new Module(filename, null);
2624
customModule.filename = filename;

‎src/env_properties.h

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
// Strings are per-isolate primitives but Environment proxies them
5555
// for the sake of convenience. Strings should be ASCII-only.
5656
#define PER_ISOLATE_STRING_PROPERTIES(V) \
57+
V(__filename_string, "__filename") \
58+
V(__dirname_string, "__dirname") \
5759
V(ack_string, "ack") \
5860
V(address_string, "address") \
5961
V(aliases_string, "aliases") \

‎src/node_contextify.cc

+116-15
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "node_errors.h"
2929
#include "node_external_reference.h"
3030
#include "node_internals.h"
31+
#include "node_sea.h"
3132
#include "node_snapshot_builder.h"
3233
#include "node_watchdog.h"
3334
#include "util-inl.h"
@@ -1151,6 +1152,15 @@ ContextifyScript::ContextifyScript(Environment* env, Local<Object> object)
11511152

11521153
ContextifyScript::~ContextifyScript() {}
11531154

1155+
static Local<PrimitiveArray> GetHostDefinedOptions(Isolate* isolate,
1156+
Local<Symbol> id_symbol) {
1157+
Local<PrimitiveArray> host_defined_options =
1158+
PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
1159+
host_defined_options->Set(
1160+
isolate, loader::HostDefinedOptions::kID, id_symbol);
1161+
return host_defined_options;
1162+
}
1163+
11541164
void ContextifyContext::CompileFunction(
11551165
const FunctionCallbackInfo<Value>& args) {
11561166
Environment* env = Environment::GetCurrent(args);
@@ -1281,15 +1291,6 @@ void ContextifyContext::CompileFunction(
12811291
args.GetReturnValue().Set(result);
12821292
}
12831293

1284-
Local<PrimitiveArray> ContextifyContext::GetHostDefinedOptions(
1285-
Isolate* isolate, Local<Symbol> id_symbol) {
1286-
Local<PrimitiveArray> host_defined_options =
1287-
PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
1288-
host_defined_options->Set(
1289-
isolate, loader::HostDefinedOptions::kID, id_symbol);
1290-
return host_defined_options;
1291-
}
1292-
12931294
ScriptCompiler::Source ContextifyContext::GetCommonJSSourceInstance(
12941295
Isolate* isolate,
12951296
Local<String> code,
@@ -1323,6 +1324,16 @@ ScriptCompiler::CompileOptions ContextifyContext::GetCompileOptions(
13231324
return options;
13241325
}
13251326

1327+
static std::vector<Local<String>> GetCJSParameters(IsolateData* data) {
1328+
return {
1329+
data->exports_string(),
1330+
data->require_string(),
1331+
data->module_string(),
1332+
data->__filename_string(),
1333+
data->__dirname_string(),
1334+
};
1335+
}
1336+
13261337
Local<Object> ContextifyContext::CompileFunctionAndCacheResult(
13271338
Environment* env,
13281339
Local<Context> parsing_context,
@@ -1451,12 +1462,7 @@ void ContextifyContext::ContainsModuleSyntax(
14511462
isolate, code, filename, 0, 0, host_defined_options, nullptr);
14521463
ScriptCompiler::CompileOptions options = GetCompileOptions(source);
14531464

1454-
std::vector<Local<String>> params = {
1455-
String::NewFromUtf8(isolate, "exports").ToLocalChecked(),
1456-
String::NewFromUtf8(isolate, "require").ToLocalChecked(),
1457-
String::NewFromUtf8(isolate, "module").ToLocalChecked(),
1458-
String::NewFromUtf8(isolate, "__filename").ToLocalChecked(),
1459-
String::NewFromUtf8(isolate, "__dirname").ToLocalChecked()};
1465+
std::vector<Local<String>> params = GetCJSParameters(env->isolate_data());
14601466

14611467
TryCatchScope try_catch(env);
14621468
ShouldNotAbortOnUncaughtScope no_abort_scope(env);
@@ -1486,6 +1492,96 @@ void ContextifyContext::ContainsModuleSyntax(
14861492
args.GetReturnValue().Set(found_error_message_caused_by_module_syntax);
14871493
}
14881494

1495+
static void CompileFunctionForCJSLoader(
1496+
const FunctionCallbackInfo<Value>& args) {
1497+
CHECK(args[0]->IsString());
1498+
CHECK(args[1]->IsString());
1499+
Local<String> code = args[0].As<String>();
1500+
Local<String> filename = args[1].As<String>();
1501+
Isolate* isolate = args.GetIsolate();
1502+
Local<Context> context = isolate->GetCurrentContext();
1503+
Environment* env = Environment::GetCurrent(context);
1504+
1505+
Local<Symbol> symbol = env->vm_dynamic_import_default_internal();
1506+
Local<PrimitiveArray> hdo = GetHostDefinedOptions(isolate, symbol);
1507+
ScriptOrigin origin(isolate,
1508+
filename,
1509+
0, // line offset
1510+
0, // column offset
1511+
true, // is cross origin
1512+
-1, // script id
1513+
Local<Value>(), // source map URL
1514+
false, // is opaque
1515+
false, // is WASM
1516+
false, // is ES Module
1517+
hdo);
1518+
ScriptCompiler::CachedData* cached_data = nullptr;
1519+
1520+
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
1521+
bool used_cache_from_sea = false;
1522+
if (sea::IsSingleExecutable()) {
1523+
sea::SeaResource sea = sea::FindSingleExecutableResource();
1524+
if (sea.use_code_cache()) {
1525+
std::string_view data = sea.code_cache.value();
1526+
cached_data = new ScriptCompiler::CachedData(
1527+
reinterpret_cast<const uint8_t*>(data.data()),
1528+
static_cast<int>(data.size()),
1529+
v8::ScriptCompiler::CachedData::BufferNotOwned);
1530+
used_cache_from_sea = true;
1531+
}
1532+
}
1533+
#endif
1534+
ScriptCompiler::Source source(code, origin, cached_data);
1535+
1536+
TryCatchScope try_catch(env);
1537+
1538+
std::vector<Local<String>> params = GetCJSParameters(env->isolate_data());
1539+
1540+
MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunction(
1541+
context,
1542+
&source,
1543+
params.size(),
1544+
params.data(),
1545+
0, /* context extensions size */
1546+
nullptr, /* context extensions data */
1547+
// TODO(joyeecheung): allow optional eager compilation.
1548+
cached_data == nullptr ? ScriptCompiler::kNoCompileOptions
1549+
: ScriptCompiler::kConsumeCodeCache,
1550+
v8::ScriptCompiler::NoCacheReason::kNoCacheNoReason);
1551+
1552+
Local<Function> fn;
1553+
if (!maybe_fn.ToLocal(&fn)) {
1554+
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
1555+
errors::DecorateErrorStack(env, try_catch);
1556+
if (!try_catch.HasTerminated()) {
1557+
try_catch.ReThrow();
1558+
}
1559+
return;
1560+
}
1561+
}
1562+
1563+
bool cache_rejected = false;
1564+
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
1565+
if (used_cache_from_sea) {
1566+
cache_rejected = source.GetCachedData()->rejected;
1567+
}
1568+
#endif
1569+
1570+
std::vector<Local<Name>> names = {
1571+
env->cached_data_rejected_string(),
1572+
env->source_map_url_string(),
1573+
env->function_string(),
1574+
};
1575+
std::vector<Local<Value>> values = {
1576+
Boolean::New(isolate, cache_rejected),
1577+
fn->GetScriptOrigin().SourceMapUrl(),
1578+
fn,
1579+
};
1580+
Local<Object> result = Object::New(
1581+
isolate, v8::Null(isolate), names.data(), values.data(), names.size());
1582+
args.GetReturnValue().Set(result);
1583+
}
1584+
14891585
static void StartSigintWatchdog(const FunctionCallbackInfo<Value>& args) {
14901586
int ret = SigintWatchdogHelper::GetInstance()->Start();
14911587
args.GetReturnValue().Set(ret == 0);
@@ -1538,6 +1634,10 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
15381634
isolate, target, "watchdogHasPendingSigint", WatchdogHasPendingSigint);
15391635

15401636
SetMethod(isolate, target, "measureMemory", MeasureMemory);
1637+
SetMethod(isolate,
1638+
target,
1639+
"compileFunctionForCJSLoader",
1640+
CompileFunctionForCJSLoader);
15411641
}
15421642

15431643
static void CreatePerContextProperties(Local<Object> target,
@@ -1577,6 +1677,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
15771677
ContextifyContext::RegisterExternalReferences(registry);
15781678
ContextifyScript::RegisterExternalReferences(registry);
15791679

1680+
registry->Register(CompileFunctionForCJSLoader);
15801681
registry->Register(StartSigintWatchdog);
15811682
registry->Register(StopSigintWatchdog);
15821683
registry->Register(WatchdogHasPendingSigint);

‎src/node_contextify.h

-2
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ class ContextifyContext : public BaseObject {
9494
bool produce_cached_data,
9595
v8::Local<v8::Symbol> id_symbol,
9696
const errors::TryCatchScope& try_catch);
97-
static v8::Local<v8::PrimitiveArray> GetHostDefinedOptions(
98-
v8::Isolate* isolate, v8::Local<v8::Symbol> id_symbol);
9997
static v8::ScriptCompiler::Source GetCommonJSSourceInstance(
10098
v8::Isolate* isolate,
10199
v8::Local<v8::String> code,

‎src/node_sea.cc

+4-32
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ using node::ExitCode;
3131
using v8::ArrayBuffer;
3232
using v8::BackingStore;
3333
using v8::Context;
34-
using v8::DataView;
3534
using v8::Function;
3635
using v8::FunctionCallbackInfo;
3736
using v8::HandleScope;
@@ -219,6 +218,10 @@ bool SeaResource::use_snapshot() const {
219218
return static_cast<bool>(flags & SeaFlags::kUseSnapshot);
220219
}
221220

221+
bool SeaResource::use_code_cache() const {
222+
return static_cast<bool>(flags & SeaFlags::kUseCodeCache);
223+
}
224+
222225
SeaResource FindSingleExecutableResource() {
223226
static const SeaResource sea_resource = []() -> SeaResource {
224227
std::string_view blob = FindSingleExecutableBlob();
@@ -258,35 +261,6 @@ void IsExperimentalSeaWarningNeeded(const FunctionCallbackInfo<Value>& args) {
258261
sea_resource.flags & SeaFlags::kDisableExperimentalSeaWarning));
259262
}
260263

261-
void GetCodeCache(const FunctionCallbackInfo<Value>& args) {
262-
if (!IsSingleExecutable()) {
263-
return;
264-
}
265-
266-
Isolate* isolate = args.GetIsolate();
267-
268-
SeaResource sea_resource = FindSingleExecutableResource();
269-
270-
if (!static_cast<bool>(sea_resource.flags & SeaFlags::kUseCodeCache)) {
271-
return;
272-
}
273-
274-
std::shared_ptr<BackingStore> backing_store = ArrayBuffer::NewBackingStore(
275-
const_cast<void*>(
276-
static_cast<const void*>(sea_resource.code_cache->data())),
277-
sea_resource.code_cache->length(),
278-
[](void* /* data */, size_t /* length */, void* /* deleter_data */) {
279-
// The code cache data blob is not freed here because it is a static
280-
// blob which is not allocated by the BackingStore allocator.
281-
},
282-
nullptr);
283-
Local<ArrayBuffer> array_buffer = ArrayBuffer::New(isolate, backing_store);
284-
Local<DataView> data_view =
285-
DataView::New(array_buffer, 0, array_buffer->ByteLength());
286-
287-
args.GetReturnValue().Set(data_view);
288-
}
289-
290264
void GetCodePath(const FunctionCallbackInfo<Value>& args) {
291265
DCHECK(IsSingleExecutable());
292266

@@ -653,15 +627,13 @@ void Initialize(Local<Object> target,
653627
"isExperimentalSeaWarningNeeded",
654628
IsExperimentalSeaWarningNeeded);
655629
SetMethod(context, target, "getCodePath", GetCodePath);
656-
SetMethod(context, target, "getCodeCache", GetCodeCache);
657630
SetMethod(context, target, "getAsset", GetAsset);
658631
}
659632

660633
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
661634
registry->Register(IsSea);
662635
registry->Register(IsExperimentalSeaWarningNeeded);
663636
registry->Register(GetCodePath);
664-
registry->Register(GetCodeCache);
665637
registry->Register(GetAsset);
666638
}
667639

‎src/node_sea.h

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ struct SeaResource {
3737
std::unordered_map<std::string_view, std::string_view> assets;
3838

3939
bool use_snapshot() const;
40+
bool use_code_cache() const;
41+
4042
static constexpr size_t kHeaderSize = sizeof(kMagic) + sizeof(SeaFlags);
4143
};
4244

0 commit comments

Comments
 (0)
Please sign in to comment.