Skip to content

Commit 362df16

Browse files
committed
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
1 parent 73025c4 commit 362df16

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');
@@ -1274,23 +1276,10 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
12741276
return runScriptInThisContext(script, true, false);
12751277
}
12761278

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

1293-
// The code cache is used for SEAs only.
1282+
// cachedDataRejected is only set for cache coming from SEA.
12941283
if (codeCache &&
12951284
result.cachedDataRejected !== false &&
12961285
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');
@@ -57,10 +61,7 @@ const moduleWrap = internalBinding('module_wrap');
5761
const { ModuleWrap } = moduleWrap;
5862
const asyncESM = require('internal/process/esm_loader');
5963
const { emitWarningSync } = require('internal/process/warning');
60-
const { internalCompileFunction } = require('internal/vm');
61-
const {
62-
vm_dynamic_import_default_internal,
63-
} = internalBinding('symbols');
64+
6465
// Lazy-loading to avoid circular dependencies.
6566
let getSourceSync;
6667
/**
@@ -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
@@ -56,6 +56,8 @@
5656
// Strings are per-isolate primitives but Environment proxies them
5757
// for the sake of convenience. Strings should be ASCII-only.
5858
#define PER_ISOLATE_STRING_PROPERTIES(V) \
59+
V(__filename_string, "__filename") \
60+
V(__dirname_string, "__dirname") \
5961
V(ack_string, "ack") \
6062
V(address_string, "address") \
6163
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"
@@ -1150,6 +1151,15 @@ ContextifyScript::ContextifyScript(Environment* env, Local<Object> object)
11501151

11511152
ContextifyScript::~ContextifyScript() {}
11521153

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

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

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

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

14601466
TryCatchScope try_catch(env);
14611467
ShouldNotAbortOnUncaughtScope no_abort_scope(env);
@@ -1485,6 +1491,96 @@ void ContextifyContext::ContainsModuleSyntax(
14851491
args.GetReturnValue().Set(found_error_message_caused_by_module_syntax);
14861492
}
14871493

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

15391635
SetMethod(isolate, target, "measureMemory", MeasureMemory);
1636+
SetMethod(isolate,
1637+
target,
1638+
"compileFunctionForCJSLoader",
1639+
CompileFunctionForCJSLoader);
15401640
}
15411641

15421642
static void CreatePerContextProperties(Local<Object> target,
@@ -1576,6 +1676,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
15761676
ContextifyContext::RegisterExternalReferences(registry);
15771677
ContextifyScript::RegisterExternalReferences(registry);
15781678

1679+
registry->Register(CompileFunctionForCJSLoader);
15791680
registry->Register(StartSigintWatchdog);
15801681
registry->Register(StopSigintWatchdog);
15811682
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

0 commit comments

Comments
 (0)