nodejs / nodejs/node

--preserve-symlinks-main not respected when using --require and --inspect-brk together

未关闭
#44,856 2 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

inspector module
主要语言
JavaScript
星标
122k
派生
37.4k
平均合并
4 天 3 小时
30 天内合并 PR
272

描述

Version

16.11.0

Platform

Darwin NAMTJ0390J2KQ 21.6.0 Darwin Kernel Version 21.6.0: Mon Aug 22 20:19:52 PDT 2022; root:xnu-8020.140.49~2/RELEASE_ARM64_T6000 arm64

Subsystem

internal/modules/cjs

What steps will reproduce the bug?

No response

How often does it reproduce? Is there a required condition?

No response

What is the expected behavior?

That symlinks are preserved for main entrypoint file path resolution when --require, --preserve-symlinks-main, and --inspect-brk flags are all used.

What do you see instead?

When using a symlinked entry point, and calling node with all of --require, --preserve-symlinks-main, and --inspect-brk, the symlink for the entry point is not preserved, and the realpath of the entrypoint is used for module loading (and future module loads relative to that entry point).

Additional information

When using node --require preload.js --inspect-brk thing.js, Module.prototype._compile is entered for preload.js before the main entrypoint is loaded. Note the following code block:

https://github.com/nodejs/node/blob/main/lib/internal/modules/cjs/loader.js#L1126

 if (getOptionValue('--inspect-brk') && process._eval == null) {
    if (!resolvedArgv) {
      // We enter the repl if we're not given a filename argument.
      if (process.argv[1]) {
        try {
          resolvedArgv = Module._resolveFilename(process.argv[1], null, false);
        } catch {
          // We only expect this codepath to be reached in the case of a
          // preloaded module (it will fail earlier with the main entry)
          assert(ArrayIsArray(getOptionValue('--require')));
        }
      } else {
        resolvedArgv = 'repl';
      }
    }

Note especially that resolvedArgv = Module._resolveFilename(process.argv[1], null, false); will resolve the main entrypoint filename with the isMain arg set to false. This will cause --preserve-symlinks-main not to be applied for the filename resolution, and the realpath rather than the superficial (not following the links) path to be used.

Later, when the main entrypoint is loaded "for real", Module._resolveFilename is called with isMain = true (correctly). However, when this enters Module._findPath, the filename from the previous resolution, with isMain = false, is pulled from the cache on Module._pathCache, because the cache key does not incorporate the isMain arg value - only path info -, and this value is returned from Module._resolveFilename:

https://github.com/nodejs/node/blob/2a4452a53af65a13db4efae474162a7dcfd38dd5/lib/internal/modules/cjs/loader.js#L549

 const cacheKey = request + '\x00' + ArrayPrototypeJoin(paths, '\x00');
  const entry = Module._pathCache[cacheKey];
  if (entry)
    return entry;

So, even though a different file path would potentially emerge as a result of the logic below, the value from the cache is used:

https://github.com/nodejs/node/blob/2a4452a53af65a13db4efae474162a7dcfd38dd5/lib/internal/modules/cjs/loader.js#L580

 if (!isMain) {
          if (preserveSymlinks) {
            filename = path.resolve(basePath);
          } else {
            filename = toRealPath(basePath);
          }
        } else if (preserveSymlinksMain) {
          // For the main module, we use the preserveSymlinksMain flag instead
          // mainly for backward compatibility, as the preserveSymlinks flag
          // historically has not applied to the main module.  Most likely this
          // was intended to keep .bin/ binaries working, as following those
          // symlinks is usually required for the imports in the corresponding
          // files to resolve; that said, in some use cases following symlinks
          // causes bigger problems which is why the preserveSymlinksMain option
          // is needed.
          filename = path.resolve(basePath);
        } else {
          filename = toRealPath(basePath);
        }

Potential actions:

  1. Add isMain to the cache key used for Module._pathCache in Module._findPath. I think this is important, as other code in node that uses Module._findPath may create race conditions, because the cache key is insufficiently inclusive, making the function, in a sense, impure.
  2. Pass isMain = true to the Module._resolveFilename call in the --inspect-brk logic in Module.prototype._compile. Isn't it guaranteed that we are dealing with the main module here, anyway?

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

从 lib/internal/modules/cjs/loader.js 开始,重点关注 Module.prototype._compile、Module._resolveFilename 中的 --inspect-brk 路径,以及 Module._findPath 中的 Module._pathCache 键。使用符号链接的入口点以及 --require、--preserve-symlinks-main 和 --inspect-brk 进行复现,然后验证主入口点保留其符号链接路径,而不是解析为实际路径。

由索引模型根据 Issue 内容生成。

评估

技术栈
javascript, nodejs
领域
backend
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
冷清
描述清晰度
基本清楚
新手友好度
48/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。