Skip to content

Commit 5273982

Browse files
committed
src: implement IsInsideNodeModules() in C++
This previously compiles a script and run it in a new context to avoid global pollution, which is more complex than necessary and can be too slow for it to be reused in other cases. The new implementation just checks the frames in C++ which is safe from global pollution, faster and simpler. The previous implementation also had a bug when the call site is in a ESM, because ESM have URLs as their script names, which don't start with '/' or '\' and will be skipped. The new implementation removes the skipping to fix it for ESM.
1 parent 20d8b85 commit 5273982

File tree

10 files changed

+107
-31
lines changed

10 files changed

+107
-31
lines changed

lib/buffer.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ const {
7979
ONLY_ENUMERABLE,
8080
},
8181
getOwnNonIndexProperties,
82+
isInsideNodeModules,
8283
} = internalBinding('util');
8384
const {
8485
customInspectSymbol,
85-
isInsideNodeModules,
8686
lazyDOMException,
8787
normalizeEncoding,
8888
kIsEncodingSymbol,
@@ -178,13 +178,15 @@ function showFlaggedDeprecation() {
178178
if (bufferWarningAlreadyEmitted ||
179179
++nodeModulesCheckCounter > 10000 ||
180180
(!require('internal/options').getOptionValue('--pending-deprecation') &&
181-
isInsideNodeModules())) {
181+
isInsideNodeModules(100, true))) {
182182
// We don't emit a warning, because we either:
183183
// - Already did so, or
184184
// - Already checked too many times whether a call is coming
185185
// from node_modules and want to stop slowing down things, or
186186
// - We aren't running with `--pending-deprecation` enabled,
187187
// and the code is inside `node_modules`.
188+
// - We found node_modules in up to the topmost 100 frames, or
189+
// there are more than 100 frames and we don't want to search anymore.
188190
return;
189191
}
190192

lib/internal/util.js

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -516,31 +516,6 @@ function getStructuredStack() {
516516
return getStructuredStackImpl();
517517
}
518518

519-
function isInsideNodeModules() {
520-
const stack = getStructuredStack();
521-
522-
// Iterate over all stack frames and look for the first one not coming
523-
// from inside Node.js itself:
524-
if (ArrayIsArray(stack)) {
525-
for (const frame of stack) {
526-
const filename = frame.getFileName();
527-
528-
if (
529-
filename == null ||
530-
StringPrototypeStartsWith(filename, 'node:') === true ||
531-
(
532-
filename[0] !== '/' &&
533-
StringPrototypeIncludes(filename, '\\') === false
534-
)
535-
) {
536-
continue;
537-
}
538-
return isUnderNodeModules(filename);
539-
}
540-
}
541-
return false;
542-
}
543-
544519
function once(callback, { preserveReturnValue = false } = kEmptyObject) {
545520
let called = false;
546521
let returnValue;
@@ -914,7 +889,6 @@ module.exports = {
914889
getSystemErrorName,
915890
guessHandleType,
916891
isError,
917-
isInsideNodeModules,
918892
isUnderNodeModules,
919893
isMacOS,
920894
isWindows,

src/node_util.cc

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,47 @@ static void GetCallSite(const FunctionCallbackInfo<Value>& args) {
292292

293293
callsite_objects.push_back(obj);
294294
}
295+
}
296+
297+
static void IsInsideNodeModules(const FunctionCallbackInfo<Value>& args) {
298+
Isolate* isolate = args.GetIsolate();
299+
CHECK_EQ(args.Length(), 2);
300+
CHECK(args[0]->IsInt32()); // frame_limit
301+
// The second argument is the default value.
302+
303+
int frames_limit = args[0].As<v8::Int32>()->Value();
304+
Local<StackTrace> stack = StackTrace::CurrentStackTrace(isolate, frames_limit);
305+
int frame_count = stack->GetFrameCount();
306+
307+
// If the search requires looking into more than |frames_limit| frames, give up
308+
// and return the specified default value.
309+
if (frame_count == frames_limit) {
310+
return args.GetReturnValue().Set(args[1]);
311+
}
295312

296-
Local<Array> callsites =
297-
Array::New(isolate, callsite_objects.data(), callsite_objects.size());
298-
args.GetReturnValue().Set(callsites);
313+
bool result = false;
314+
for (int i = 0; i < frame_count; ++i) {
315+
Local<StackFrame> stack_frame = stack->GetFrame(isolate, i);
316+
Local<String> script_name = stack_frame->GetScriptName();
317+
318+
if (script_name.IsEmpty() || script_name->Length() == 0) {
319+
continue;
320+
}
321+
322+
std::string script_name_str = Utf8Value(isolate, script_name).ToString();
323+
if (script_name_str.starts_with("node:")) {
324+
continue;
325+
}
326+
if (script_name_str.find("/node_modules/") != std::string::npos ||
327+
script_name_str.find("\\node_modules\\") != std::string::npos ||
328+
script_name_str.find("/node_modules\\") != std::string::npos ||
329+
script_name_str.find("\\node_modules/") != std::string::npos) {
330+
result = true;
331+
break;
332+
}
333+
}
334+
335+
args.GetReturnValue().Set(result);
299336
}
300337

301338
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
@@ -313,6 +350,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
313350
registry->Register(FastGuessHandleType);
314351
registry->Register(fast_guess_handle_type_.GetTypeInfo());
315352
registry->Register(ParseEnv);
353+
registry->Register(IsInsideNodeModules);
316354
}
317355

318356
void Initialize(Local<Object> target,
@@ -396,6 +434,8 @@ void Initialize(Local<Object> target,
396434
target->Set(context, env->constants_string(), constants).Check();
397435
}
398436

437+
SetMethod(
438+
context, target, "isInsideNodeModules", IsInsideNodeModules);
399439
SetMethodNoSideEffect(
400440
context, target, "getPromiseDetails", GetPromiseDetails);
401441
SetMethodNoSideEffect(context, target, "getProxyDetails", GetProxyDetails);

test/fixtures/warning_node_modules/new-buffer-cjs.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/warning_node_modules/new-buffer-esm.mjs

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/warning_node_modules/node_modules/new-buffer-cjs/index.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/warning_node_modules/node_modules/new-buffer-cjs/package.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/warning_node_modules/node_modules/new-buffer-esm/index.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/fixtures/warning_node_modules/node_modules/new-buffer-esm/package.json

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const fixtures = require('../common/fixtures');
5+
const { spawnSyncAndAssert } = require('../common/child_process');
6+
7+
if (process.env.NODE_PENDING_DEPRECATION)
8+
common.skip('test does not work when NODE_PENDING_DEPRECATION is set');
9+
10+
spawnSyncAndAssert(
11+
process.execPath,
12+
[ fixtures.path('warning_node_modules', 'new-buffer-cjs.js') ],
13+
{
14+
trim: true,
15+
stderr: '',
16+
}
17+
);
18+
19+
spawnSyncAndAssert(
20+
process.execPath,
21+
[ fixtures.path('warning_node_modules', 'new-buffer-esm.mjs') ],
22+
{
23+
trim: true,
24+
stderr: '',
25+
}
26+
);
27+
28+
spawnSyncAndAssert(
29+
process.execPath,
30+
[
31+
'--pending-deprecation',
32+
fixtures.path('warning_node_modules', 'new-buffer-cjs.js')
33+
],
34+
{
35+
stderr: /DEP0005/
36+
}
37+
);
38+
39+
spawnSyncAndAssert(
40+
process.execPath,
41+
[
42+
'--pending-deprecation',
43+
fixtures.path('warning_node_modules', 'new-buffer-esm.mjs')
44+
],
45+
{
46+
stderr: /DEP0005/
47+
}
48+
);

0 commit comments

Comments
 (0)