Skip to content

Commit dcf9519

Browse files
ryzokukenBethGriggs
authored andcommitted
module: revert module._compile to original state if module is patched
Backport-PR-URL: #27124 PR-URL: #21573 Fixes: #17396 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 1a15fdc commit dcf9519

File tree

121 files changed

+15289
-48
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+15289
-48
lines changed

lib/assert.js

-11
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ let isDeepEqual;
3838
let isDeepStrictEqual;
3939
let parseExpressionAt;
4040
let findNodeAround;
41-
let columnOffset = 0;
4241
let decoder;
4342

4443
function lazyLoadComparison() {
@@ -255,16 +254,6 @@ function getErrMessage(message, fn) {
255254
const line = call.getLineNumber() - 1;
256255
let column = call.getColumnNumber() - 1;
257256

258-
// Line number one reports the wrong column due to being wrapped in a
259-
// function. Remove that offset to get the actual call.
260-
if (line === 0) {
261-
if (columnOffset === 0) {
262-
const { wrapper } = require('internal/modules/cjs/loader');
263-
columnOffset = wrapper[0].length;
264-
}
265-
column -= columnOffset;
266-
}
267-
268257
const identifier = `${filename}${line}${column}`;
269258

270259
if (errorCache.has(identifier)) {

lib/internal/modules/cjs/helpers.js

+11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
'use strict';
22

33
const { validateString } = require('internal/validators');
4+
const path = require('path');
5+
const { pathToFileURL } = require('internal/url');
6+
const { URL } = require('url');
47

58
const {
69
CHAR_LINE_FEED,
@@ -152,10 +155,18 @@ function addBuiltinLibsToObject(object) {
152155
});
153156
}
154157

158+
function normalizeReferrerURL(referrer) {
159+
if (typeof referrer === 'string' && path.isAbsolute(referrer)) {
160+
return pathToFileURL(referrer).href;
161+
}
162+
return new URL(referrer).href;
163+
}
164+
155165
module.exports = exports = {
156166
addBuiltinLibsToObject,
157167
builtinLibs,
158168
makeRequireFunction,
169+
normalizeReferrerURL,
159170
requireDepth: 0,
160171
stripBOM,
161172
stripShebang

lib/internal/modules/cjs/loader.js

+83-17
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ const assert = require('assert').ok;
2929
const fs = require('fs');
3030
const internalFS = require('internal/fs/utils');
3131
const path = require('path');
32-
const { URL } = require('url');
3332
const {
3433
internalModuleReadJSON,
3534
internalModuleStat
3635
} = process.binding('fs');
3736
const { safeGetenv } = process.binding('util');
3837
const {
3938
makeRequireFunction,
39+
normalizeReferrerURL,
4040
requireDepth,
4141
stripBOM,
4242
stripShebang
@@ -45,6 +45,7 @@ const { getOptionValue } = require('internal/options');
4545
const preserveSymlinks = getOptionValue('--preserve-symlinks');
4646
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
4747
const experimentalModules = getOptionValue('--experimental-modules');
48+
const { compileFunction } = internalBinding('contextify');
4849

4950
const {
5051
ERR_INVALID_ARG_VALUE,
@@ -122,15 +123,52 @@ Module._extensions = Object.create(null);
122123
var modulePaths = [];
123124
Module.globalPaths = [];
124125

125-
Module.wrap = function(script) {
126+
let patched = false;
127+
128+
// eslint-disable-next-line func-style
129+
let wrap = function(script) {
126130
return Module.wrapper[0] + script + Module.wrapper[1];
127131
};
128132

129-
Module.wrapper = [
133+
const wrapper = [
130134
'(function (exports, require, module, __filename, __dirname) { ',
131135
'\n});'
132136
];
133137

138+
let wrapperProxy = new Proxy(wrapper, {
139+
set(target, property, value, receiver) {
140+
patched = true;
141+
return Reflect.set(target, property, value, receiver);
142+
},
143+
144+
defineProperty(target, property, descriptor) {
145+
patched = true;
146+
return Object.defineProperty(target, property, descriptor);
147+
}
148+
});
149+
150+
Object.defineProperty(Module, 'wrap', {
151+
get() {
152+
return wrap;
153+
},
154+
155+
set(value) {
156+
patched = true;
157+
wrap = value;
158+
}
159+
});
160+
161+
Object.defineProperty(Module, 'wrapper', {
162+
get() {
163+
return wrapperProxy;
164+
},
165+
166+
set(value) {
167+
patched = true;
168+
wrapperProxy = value;
169+
}
170+
});
171+
134172
const debug = util.debuglog('module');
135173

136174
Module._debug = util.deprecate(debug, 'Module._debug is deprecated.',
@@ -657,13 +695,6 @@ Module.prototype.require = function(id) {
657695
// (needed for setting breakpoint when called with --inspect-brk)
658696
var resolvedArgv;
659697

660-
function normalizeReferrerURL(referrer) {
661-
if (typeof referrer === 'string' && path.isAbsolute(referrer)) {
662-
return pathToFileURL(referrer).href;
663-
}
664-
return new URL(referrer).href;
665-
}
666-
667698

668699
// Run the file contents in the correct scope or sandbox. Expose
669700
// the correct helper variables (require, module, exports) to
@@ -673,13 +704,48 @@ Module.prototype._compile = function(content, filename) {
673704

674705
content = stripShebang(content);
675706

676-
const compiledWrapper = vm.compileFunction(content, [
677-
'exports',
678-
'require',
679-
'module',
680-
'__filename',
681-
'__dirname',
682-
], { filename });
707+
let compiledWrapper;
708+
if (patched) {
709+
const wrapper = Module.wrap(content);
710+
compiledWrapper = vm.runInThisContext(wrapper, {
711+
filename,
712+
lineOffset: 0,
713+
displayErrors: true,
714+
importModuleDynamically: experimentalModules ? async (specifier) => {
715+
if (asyncESM === undefined) lazyLoadESM();
716+
const loader = await asyncESM.loaderPromise;
717+
return loader.import(specifier, normalizeReferrerURL(filename));
718+
} : undefined,
719+
});
720+
} else {
721+
compiledWrapper = compileFunction(
722+
content,
723+
filename,
724+
0,
725+
0,
726+
undefined,
727+
false,
728+
undefined,
729+
[],
730+
[
731+
'exports',
732+
'require',
733+
'module',
734+
'__filename',
735+
'__dirname',
736+
]
737+
);
738+
if (experimentalModules) {
739+
const { callbackMap } = internalBinding('module_wrap');
740+
callbackMap.set(compiledWrapper, {
741+
importModuleDynamically: async (specifier) => {
742+
if (asyncESM === undefined) lazyLoadESM();
743+
const loader = await asyncESM.loaderPromise;
744+
return loader.import(specifier, normalizeReferrerURL(filename));
745+
}
746+
});
747+
}
748+
}
683749

684750
var inspectorWrapper = null;
685751
if (process._breakFirstLine && process._eval == null) {

src/env-inl.h

+3
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,9 @@ inline uint32_t Environment::get_next_module_id() {
447447
inline uint32_t Environment::get_next_script_id() {
448448
return script_id_counter_++;
449449
}
450+
inline uint32_t Environment::get_next_function_id() {
451+
return function_id_counter_++;
452+
}
450453

451454
Environment::ShouldNotAbortOnUncaughtScope::ShouldNotAbortOnUncaughtScope(
452455
Environment* env)

src/env.cc

+12-1
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ Environment::Environment(IsolateData* isolate_data,
174174
}
175175
}
176176

177+
CompileFnEntry::CompileFnEntry(Environment* env, uint32_t id)
178+
: env(env), id(id) {
179+
env->compile_fn_entries.insert(this);
180+
}
181+
177182
Environment::~Environment() {
178183
isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback(
179184
BuildEmbedderGraph, this);
@@ -182,7 +187,13 @@ Environment::~Environment() {
182187
// CleanupHandles() should have removed all of them.
183188
CHECK(file_handle_read_wrap_freelist_.empty());
184189

185-
v8::HandleScope handle_scope(isolate());
190+
// dispose the Persistent references to the compileFunction
191+
// wrappers used in the dynamic import callback
192+
for (auto& entry : compile_fn_entries) {
193+
delete entry;
194+
}
195+
196+
HandleScope handle_scope(isolate());
186197

187198
#if HAVE_INSPECTOR
188199
// Destroy inspector agent before erasing the context. The inspector

src/env.h

+10
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,12 @@ struct ContextInfo {
425425
bool is_default = false;
426426
};
427427

428+
struct CompileFnEntry {
429+
Environment* env;
430+
uint32_t id;
431+
CompileFnEntry(Environment* env, uint32_t id);
432+
};
433+
428434
// Listing the AsyncWrap provider types first enables us to cast directly
429435
// from a provider type to a debug category.
430436
#define DEBUG_CATEGORY_NAMES(V) \
@@ -681,9 +687,12 @@ class Environment {
681687
std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map;
682688
std::unordered_map<uint32_t, contextify::ContextifyScript*>
683689
id_to_script_map;
690+
std::unordered_set<CompileFnEntry*> compile_fn_entries;
691+
std::unordered_map<uint32_t, Persistent<v8::Function>> id_to_function_map;
684692

685693
inline uint32_t get_next_module_id();
686694
inline uint32_t get_next_script_id();
695+
inline uint32_t get_next_function_id();
687696

688697
std::unordered_map<std::string, const loader::PackageConfig>
689698
package_json_cache;
@@ -913,6 +922,7 @@ class Environment {
913922

914923
uint32_t module_id_counter_ = 0;
915924
uint32_t script_id_counter_ = 0;
925+
uint32_t function_id_counter_ = 0;
916926

917927
AliasedBuffer<uint32_t, v8::Uint32Array> should_abort_on_uncaught_toggle_;
918928
int should_not_abort_scope_counter_ = 0;

src/module_wrap.cc

+2
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static MaybeLocal<Promise> ImportModuleDynamically(
750750
} else if (type == ScriptType::kModule) {
751751
ModuleWrap* wrap = ModuleWrap::GetFromID(env, id);
752752
object = wrap->object();
753+
} else if (type == ScriptType::kFunction) {
754+
object = env->id_to_function_map.find(id)->second.Get(iso);
753755
} else {
754756
UNREACHABLE();
755757
}

src/module_wrap.h

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ enum PackageMainCheck : bool {
2020
enum ScriptType : int {
2121
kScript,
2222
kModule,
23+
kFunction,
2324
};
2425

2526
enum HostDefinedOptions : int {

src/node_contextify.cc

+47-8
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,15 @@ void ContextifyContext::WeakCallback(
281281
delete context;
282282
}
283283

284+
void ContextifyContext::WeakCallbackCompileFn(
285+
const WeakCallbackInfo<CompileFnEntry>& data) {
286+
CompileFnEntry* entry = data.GetParameter();
287+
if (entry->env->compile_fn_entries.erase(entry) != 0) {
288+
entry->env->id_to_function_map.erase(entry->id);
289+
delete entry;
290+
}
291+
}
292+
284293
// static
285294
ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox(
286295
Environment* env,
@@ -1039,7 +1048,30 @@ void ContextifyContext::CompileFunction(
10391048
data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
10401049
}
10411050

1042-
ScriptOrigin origin(filename, line_offset, column_offset);
1051+
// Get the function id
1052+
uint32_t id = env->get_next_function_id();
1053+
1054+
// Set host_defined_options
1055+
Local<PrimitiveArray> host_defined_options =
1056+
PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
1057+
host_defined_options->Set(
1058+
isolate,
1059+
loader::HostDefinedOptions::kType,
1060+
Number::New(isolate, loader::ScriptType::kFunction));
1061+
host_defined_options->Set(
1062+
isolate, loader::HostDefinedOptions::kID, Number::New(isolate, id));
1063+
1064+
ScriptOrigin origin(filename,
1065+
line_offset, // line offset
1066+
column_offset, // column offset
1067+
True(isolate), // is cross origin
1068+
Local<Integer>(), // script id
1069+
Local<Value>(), // source map URL
1070+
False(isolate), // is opaque (?)
1071+
False(isolate), // is WASM
1072+
False(isolate), // is ES Module
1073+
host_defined_options);
1074+
10431075
ScriptCompiler::Source source(code, origin, cached_data);
10441076
ScriptCompiler::CompileOptions options;
10451077
if (source.GetCachedData() == nullptr) {
@@ -1073,38 +1105,45 @@ void ContextifyContext::CompileFunction(
10731105
}
10741106
}
10751107

1076-
MaybeLocal<Function> maybe_fun = ScriptCompiler::CompileFunctionInContext(
1108+
MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunctionInContext(
10771109
parsing_context, &source, params.size(), params.data(),
10781110
context_extensions.size(), context_extensions.data(), options);
10791111

1080-
Local<Function> fun;
1081-
if (maybe_fun.IsEmpty() || !maybe_fun.ToLocal(&fun)) {
1112+
if (maybe_fn.IsEmpty()) {
10821113
ContextifyScript::DecorateErrorStack(env, try_catch);
10831114
try_catch.ReThrow();
10841115
return;
10851116
}
1117+
Local<Function> fn = maybe_fn.ToLocalChecked();
1118+
env->id_to_function_map.emplace(std::piecewise_construct,
1119+
std::make_tuple(id),
1120+
std::make_tuple(isolate, fn));
1121+
CompileFnEntry* gc_entry = new CompileFnEntry(env, id);
1122+
env->id_to_function_map[id].SetWeak(gc_entry,
1123+
WeakCallbackCompileFn,
1124+
v8::WeakCallbackType::kParameter);
10861125

10871126
if (produce_cached_data) {
10881127
const std::unique_ptr<ScriptCompiler::CachedData> cached_data(
1089-
ScriptCompiler::CreateCodeCacheForFunction(fun));
1128+
ScriptCompiler::CreateCodeCacheForFunction(fn));
10901129
bool cached_data_produced = cached_data != nullptr;
10911130
if (cached_data_produced) {
10921131
MaybeLocal<Object> buf = Buffer::Copy(
10931132
env,
10941133
reinterpret_cast<const char*>(cached_data->data),
10951134
cached_data->length);
1096-
if (fun->Set(
1135+
if (fn->Set(
10971136
parsing_context,
10981137
env->cached_data_string(),
10991138
buf.ToLocalChecked()).IsNothing()) return;
11001139
}
1101-
if (fun->Set(
1140+
if (fn->Set(
11021141
parsing_context,
11031142
env->cached_data_produced_string(),
11041143
Boolean::New(isolate, cached_data_produced)).IsNothing()) return;
11051144
}
11061145

1107-
args.GetReturnValue().Set(fun);
1146+
args.GetReturnValue().Set(fn);
11081147
}
11091148

11101149

0 commit comments

Comments
 (0)