Skip to content

[Unity]/[UE] CommonJS 模块加载时 puer.evalScript 默认实现未使用 debugPath,导致 VSCode/V8 Inspector 调试路径丢失 #2334

@leo-zhang-git

Description

@leo-zhang-git

detail | 详细描述

问题描述

在 Unity PuerTS 的 CommonJS 模块加载链路中,ILoader.ReadFile(string filepath, out string debugpath) 返回的
debugPath 会被一路传递到 puer.evalScript(script, debugPath),但默认的 puer.evalScript 实现没有使用这个
debugPath

这会导致 CommonJS 模块最终以普通 eval(script) 的形式出现在 V8 Inspector / VSCode 调试器中,调试器无法稳定识别真实文
件路径,进而影响断点绑定和 sourcemap 定位。

涉及版本

我在以下两个版本/组合中都观察到了类似问题:

  1. com.tencent.puerts.core 2.0.4
    com.tencent.puerts.commonjs 1.1.0-pre.0

  2. com.tencent.puerts.core 3.0.2
    com.tencent.puerts.v8 3.0.2

相关代码

com.tencent.puerts.core 3.0.2 为例,ILoader 接口本身提供了 debugpath

public interface ILoader
{
    bool FileExists(string filepath);
    string ReadFile(string filepath, out string debugpath);
}

模块加载时puer.loadFile 会从 loader 读取 debugPath:

function loadFile(path) {
    let debugPath = [];
    var content = loader.ReadFile(path, debugPath);
    return { content: content, debugPath: debugPath[0] };
}
puer.loadFile = loadFile;

CommonJS 模块执行时,module.mjs 会把 debugPath 传给 puer.evalScript:

function executeModule(fullPath, script, debugPath) {
    if (debugPath === undefined) debugPath = fullPath;

    let wrapped = puer.evalScript(
        "(function (exports, require, module, __filename, __dirname) { " + script + "\n});",
        debugPath
    );

    wrapped(exports, createLazyRequire(fullPath), module, debugPath, dirname(debugPath));
}

但默认 puer.evalScript 实现没有使用 debugPath:

puer.evalScript = global.__tgjsEvalScript || function (script, debugPath) {
    return eval(script);
}

 com.tencent.puerts.core 2.0.4 + com.tencent.puerts.commonjs 1.1.0-pre.0 中也有类似链路:

let wrapped = puer.evalScript(
    "(function (exports, require, module, __filename, __dirname) { " + script + "\n});",
    debugPath
)

但默认实现仍是:

puer.evalScript = global.__tgjsEvalScript || function (script, debugPath) {
    return eval(script);
}

IL2CPP 初始化路径中甚至是:

puer.evalScript = eval

这也无法消费 debugPath。

### 当前现象

当自定义 loader 返回真实文件路径时,例如:

public string ReadFile(string filepath, out string debugpath)
{
    debugpath = fullPath;
    return File.ReadAllText(fullPath);
}

debugpath 能正确传递到 CommonJS 模块执行逻辑,但最终因为 puer.evalScript 只是 eval(script)调试器中看到的脚本路径不是
loader 返回的真实路径。

在 VSCode attach V8 Inspector 调试时,可能出现以下问题:

- CommonJS 模块显示为 eval 脚本,而不是实际文件路径
- 断点无法稳定绑定到本地 JS/TS 文件
- sourcemap 定位不稳定或失效
- 需要业务侧手动 patch puer.evalScript 才能恢复调试路径

### 当前 workaround

目前可以通过替换 puer.evalScript 并追加 sourceURL / 修正 sourceMappingURL 来规避:

puer.evalScript = function (script, debugPath) {
    if (typeof script !== 'string') {
        return eval(script);
    }

    if (debugPath) {
        var normalized = String(debugPath).replace(/\\/g, '/');

        if (/^[a-zA-Z]:\//.test(normalized)) {
            normalized = 'file:///' + normalized;
        } else if (normalized.charAt(0) === '/') {
            normalized = 'file://' + normalized;
        }

        script += '\n//# sourceURL=' + normalized;
    }

    return eval(script);
};

这样 VSCode / V8 Inspector 能够识别 CommonJS 模块对应的真实文件路径,断点和 sourcemap 也能正常工作。

### 期望行为

希望 PuerTS 默认的 puer.evalScript(script, debugPath) 能消费传入的 debugPath,例如:

1. 在默认实现中根据 debugPath 追加 //# sourceURL=...
2. 或者通过其他官方推荐方式把 debugPath 传递给底层 eval / V8 Inspector
3. 同时避免破坏用户自定义的 global.__tgjsEvalScript

也就是说,对于通过 ILoader.ReadFile(..., out debugpath) 返回的真实路径,CommonJS 模块执行时调试器应能看到对应的脚本路
径。

### 疑问

请问默认 puer.evalScript 忽略 debugPath 是有意设计吗?

如果不是,是否可以在默认实现中基于 debugPath 自动追加 sourceURL,或者提供一个官方推荐的方式来处理 CommonJS 模块调试路
径?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions