Skip to content

Commit c4e2ad1

Browse files
committed
src: use dedicated routine to compile function for builtin CJS loader
1 parent cece325 commit c4e2ad1

File tree

7 files changed

+142
-86
lines changed

7 files changed

+142
-86
lines changed

lib/internal/modules/cjs/loader.js

Lines changed: 8 additions & 17 deletions
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');
@@ -105,6 +107,7 @@ const { safeGetenv } = internalBinding('credentials');
105107
const {
106108
privateSymbols: {
107109
require_private_symbol,
110+
host_defined_option_symbol,
108111
},
109112
} = internalBinding('util');
110113
const {
@@ -1274,23 +1277,11 @@ function wrapSafe(filename, content, cjsModuleInstance, codeCache) {
12741277
return runScriptInThisContext(script, true, false);
12751278
}
12761279

1277-
const params = [ 'exports', 'require', 'module', '__filename', '__dirname' ];
12781280
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-
);
1281+
const result = compileFunctionForCJSLoader(content, filename);
1282+
result.function[host_defined_option_symbol] = hostDefinedOptionId;
12921283

1293-
// The code cache is used for SEAs only.
1284+
// cachedDataRejected is only set for cache coming from SEA.
12941285
if (codeCache &&
12951286
result.cachedDataRejected !== false &&
12961287
internalBinding('sea').isSea()) {

lib/internal/modules/esm/translators.js

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ const {
1919
globalThis: { WebAssembly },
2020
} = primordials;
2121

22+
const {
23+
privateSymbols: {
24+
host_defined_option_symbol,
25+
},
26+
} = internalBinding('util');
27+
2228
/** @type {import('internal/util/types')} */
2329
let _TYPES = null;
2430
/**
@@ -29,7 +35,11 @@ function lazyTypes() {
2935
return _TYPES = require('internal/util/types');
3036
}
3137

32-
const { containsModuleSyntax } = internalBinding('contextify');
38+
const {
39+
containsModuleSyntax,
40+
compileFunctionForCJSLoader,
41+
} = internalBinding('contextify');
42+
3343
const { BuiltinModule } = require('internal/bootstrap/realm');
3444
const assert = require('internal/assert');
3545
const { readFileSync } = require('fs');
@@ -56,7 +66,6 @@ const { maybeCacheSourceMap } = require('internal/source_map/source_map_cache');
5666
const moduleWrap = internalBinding('module_wrap');
5767
const { ModuleWrap } = moduleWrap;
5868
const { emitWarningSync } = require('internal/process/warning');
59-
const { internalCompileFunction } = require('internal/vm');
6069
const {
6170
vm_dynamic_import_default_internal,
6271
} = internalBinding('symbols');
@@ -210,28 +219,9 @@ function enrichCJSError(err, content, filename) {
210219
*/
211220
function loadCJSModule(module, source, url, filename) {
212221
let compileResult;
213-
const hostDefinedOptionId = vm_dynamic_import_default_internal;
214-
const importModuleDynamically = vm_dynamic_import_default_internal;
215222
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-
);
223+
compileResult = compileFunctionForCJSLoader(source, filename);
224+
compileResult.function[host_defined_option_symbol] = vm_dynamic_import_default_internal;
235225
} catch (err) {
236226
enrichCJSError(err, source, filename);
237227
throw err;

lib/internal/util/embedding.js

Lines changed: 2 additions & 4 deletions
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/node_contextify.cc

Lines changed: 111 additions & 9 deletions
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"
@@ -956,6 +957,15 @@ Maybe<bool> StoreCodeCacheResult(
956957
return Just(true);
957958
}
958959

960+
static Local<PrimitiveArray> GetHostDefinedOptions(Isolate* isolate,
961+
Local<Symbol> id_symbol) {
962+
Local<PrimitiveArray> host_defined_options =
963+
PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
964+
host_defined_options->Set(
965+
isolate, loader::HostDefinedOptions::kID, id_symbol);
966+
return host_defined_options;
967+
}
968+
959969
// TODO(RaisinTen): Reuse in ContextifyContext::CompileFunction().
960970
MaybeLocal<Function> CompileFunction(Local<Context> context,
961971
Local<String> filename,
@@ -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,
@@ -1485,6 +1486,102 @@ void ContextifyContext::ContainsModuleSyntax(
14851486
args.GetReturnValue().Set(found_error_message_caused_by_module_syntax);
14861487
}
14871488

1489+
static void CompileFunctionForCJSLoader(
1490+
const FunctionCallbackInfo<Value>& args) {
1491+
CHECK(args[0]->IsString());
1492+
CHECK(args[1]->IsString());
1493+
Local<String> code = args[0].As<String>();
1494+
Local<String> filename = args[1].As<String>();
1495+
Isolate* isolate = args.GetIsolate();
1496+
Local<Context> context = isolate->GetCurrentContext();
1497+
Environment* env = Environment::GetCurrent(context);
1498+
1499+
Local<Symbol> symbol = env->vm_dynamic_import_default_internal();
1500+
Local<PrimitiveArray> hdo = GetHostDefinedOptions(isolate, symbol);
1501+
ScriptOrigin origin(isolate,
1502+
filename,
1503+
0, // line offset
1504+
0, // column offset
1505+
true, // is cross origin
1506+
-1, // script id
1507+
Local<Value>(), // source map URL
1508+
false, // is opaque
1509+
false, // is WASM
1510+
false, // is ES Module
1511+
hdo);
1512+
ScriptCompiler::CachedData* cached_data = nullptr;
1513+
1514+
#ifndef DISABLE_SINGLE_EXECUTABLE_APPLICATION
1515+
bool used_cache_from_sea = false;
1516+
if (sea::IsSingleExecutable()) {
1517+
sea::SeaResource sea = sea::FindSingleExecutableResource();
1518+
if (sea.use_code_cache()) {
1519+
std::string_view data = sea.code_cache.value();
1520+
cached_data = new ScriptCompiler::CachedData(
1521+
reinterpret_cast<const uint8_t*>(data.data()),
1522+
static_cast<int>(data.size()),
1523+
v8::ScriptCompiler::CachedData::BufferNotOwned);
1524+
used_cache_from_sea = true;
1525+
}
1526+
}
1527+
#endif
1528+
ScriptCompiler::Source source(code, origin, cached_data);
1529+
1530+
TryCatchScope try_catch(env);
1531+
1532+
// TODO(joyeecheung): make it a per-realm persistent.
1533+
std::vector<Local<String>> params = {
1534+
FIXED_ONE_BYTE_STRING(isolate, "exports"),
1535+
FIXED_ONE_BYTE_STRING(isolate, "require"),
1536+
FIXED_ONE_BYTE_STRING(isolate, "module"),
1537+
FIXED_ONE_BYTE_STRING(isolate, "__filename"),
1538+
FIXED_ONE_BYTE_STRING(isolate, "__dirname"),
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+
14881585
static void StartSigintWatchdog(const FunctionCallbackInfo<Value>& args) {
14891586
int ret = SigintWatchdogHelper::GetInstance()->Start();
14901587
args.GetReturnValue().Set(ret == 0);
@@ -1537,6 +1634,10 @@ void CreatePerIsolateProperties(IsolateData* isolate_data,
15371634
isolate, target, "watchdogHasPendingSigint", WatchdogHasPendingSigint);
15381635

15391636
SetMethod(isolate, target, "measureMemory", MeasureMemory);
1637+
SetMethod(isolate,
1638+
target,
1639+
"compileFunctionForCJSLoader",
1640+
CompileFunctionForCJSLoader);
15401641
}
15411642

15421643
static void CreatePerContextProperties(Local<Object> target,
@@ -1576,6 +1677,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
15761677
ContextifyContext::RegisterExternalReferences(registry);
15771678
ContextifyScript::RegisterExternalReferences(registry);
15781679

1680+
registry->Register(CompileFunctionForCJSLoader);
15791681
registry->Register(StartSigintWatchdog);
15801682
registry->Register(StopSigintWatchdog);
15811683
registry->Register(WatchdogHasPendingSigint);

src/node_contextify.h

Lines changed: 0 additions & 2 deletions
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

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ bool SeaResource::use_snapshot() const {
219219
return static_cast<bool>(flags & SeaFlags::kUseSnapshot);
220220
}
221221

222+
bool SeaResource::use_code_cache() const {
223+
return static_cast<bool>(flags & SeaFlags::kUseCodeCache);
224+
}
225+
222226
SeaResource FindSingleExecutableResource() {
223227
static const SeaResource sea_resource = []() -> SeaResource {
224228
std::string_view blob = FindSingleExecutableBlob();
@@ -258,35 +262,6 @@ void IsExperimentalSeaWarningNeeded(const FunctionCallbackInfo<Value>& args) {
258262
sea_resource.flags & SeaFlags::kDisableExperimentalSeaWarning));
259263
}
260264

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-
290265
void GetCodePath(const FunctionCallbackInfo<Value>& args) {
291266
DCHECK(IsSingleExecutable());
292267

@@ -653,15 +628,13 @@ void Initialize(Local<Object> target,
653628
"isExperimentalSeaWarningNeeded",
654629
IsExperimentalSeaWarningNeeded);
655630
SetMethod(context, target, "getCodePath", GetCodePath);
656-
SetMethod(context, target, "getCodeCache", GetCodeCache);
657631
SetMethod(context, target, "getAsset", GetAsset);
658632
}
659633

660634
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
661635
registry->Register(IsSea);
662636
registry->Register(IsExperimentalSeaWarningNeeded);
663637
registry->Register(GetCodePath);
664-
registry->Register(GetCodeCache);
665638
registry->Register(GetAsset);
666639
}
667640

src/node_sea.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <vector>
1313

1414
#include "node_exit_code.h"
15+
#include "v8-script.h"
1516

1617
namespace node {
1718
namespace sea {
@@ -37,9 +38,12 @@ struct SeaResource {
3738
std::unordered_map<std::string_view, std::string_view> assets;
3839

3940
bool use_snapshot() const;
41+
bool use_code_cache() const;
42+
4043
static constexpr size_t kHeaderSize = sizeof(kMagic) + sizeof(SeaFlags);
4144
};
4245

46+
std::unique_ptr<v8::ScriptCompiler::CachedData> GetCodeCache();
4347
bool IsSingleExecutable();
4448
SeaResource FindSingleExecutableResource();
4549
std::tuple<int, char**> FixupArgsForSEA(int argc, char** argv);

0 commit comments

Comments
 (0)