For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/proposal-error-frames-above.md.
  • 简体中文
  • Error option framesAbove S1

    中文标题:Error 选项 framesAbove

    提案概览
    提案速览

    该提案为 ECMAScript Error 构造函数引入了新的 framesAbove 选项,允许开发者指定一个函数,其最顶层调用(及其上方的所有帧)将从错误的堆栈跟踪中排除。它旨在移除样板包装帧,将当前存在于宿主特定 API(如 Node.js 的 Error.captureStackTrace)中的行为标准化。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    ECMAScript 提案:Error 选项 framesAbove

    状态

    Champion: Ruben Bridgewater

    Author: Ruben Bridgewater ruben@bridgewater.de

    阶段: 1

    概述

    本提案引入了新的 Error 选项属性 framesAbove,允许作者在捕获错误堆栈时排除特定的前导堆栈帧。

    • framesAbove 接收一个方法(即可调用函数)。如果提供的值既不是方法也不是 undefined,构造函数将抛出 TypeError
    • 当收集错误的堆栈跟踪时,所有位于所提供的函数最顶层调用之上的帧(包括该调用)都会从生成的堆栈跟踪中省略。
    • 由于 framesAbove 省略的帧不计入任何已定义的堆栈跟踪限制(例如,V8 中的 Error.stackTraceLimit)。

    此能力与长期存在的宿主特定设施(例如,Node.js/V8 的 Error.captureStackTrace 中的 constructorOpt 参数)相对应,同时提供标准化的、跨平台的选项,可用于所有 ECMAScript 内置 Error 子类的构造函数。 除了更易于使用的 API 和更好的堆栈限制处理外,它还使用更少的 CPU 周期,因为只评估一次堆栈帧而不是两次。

    提议的 API

    new Error(message, { framesAbove: someMethod })
    new TypeError(message, { framesAbove: someMethod })
    new RangeError(message, { framesAbove: someMethod })
    new AggregateError(iterable, message, { framesAbove: someMethod })
    // ... 适用于所有接受 options 袋的内置错误构造函数

    验证

    • 如果提供了 options 并且其 framesAbove 属性的值不是 undefined 且不可调用,则抛出 TypeError
    • 如果 framesAboveundefined 或不存在,则默认行为不变。

    动机

    应用程序经常围绕用户代码创建小的包装工具。这些包装器出现在堆栈跟踪的顶部,为调试失败的开发人员提供的价值很小。作者当前依赖宿主特定 API 或后处理来移除这些帧。

    framesAbove 在语言中标准化了这种行为:

    • 提高错误堆栈中的信噪比。
    • 在引擎之间提供确定性的行为。
    • 避免依赖非标准 API 或脆弱的正则后处理。

    语义

    • 设 F 为 options.framesAbove 的值。
      • 如果 F 是 undefined,则不执行任何特殊操作。
      • 否则,如果 F 不可调用,则抛出 TypeError
      • 否则,记录 F 与新创建的错误实例关联,以便在堆栈收集期间使用。
    • 在收集错误的堆栈时:
      • 从堆栈中最近的帧开始向下扫描,找到其关联函数为 F 的帧的最顶层出现。
      • 省略该出现之上严格高于的所有帧,以及该出现本身。
      • 仅对剩余的帧应用任何堆栈跟踪限制。由于 framesAbove 省略的帧不计入限制。
    • 如果 F 未出现在捕获的堆栈中,则堆栈不变(除了任何无关的平台行为和堆栈限制)。
    • 如果 F 多次出现(例如,递归),则使用最顶层的出现。

    注意:帧到关联函数的确切映射目前是宿主定义的。本提案使用引擎在堆栈跟踪帧中已经使用的相同关联。

    示例

    基本包装器移除

    function wrapper(fn, ...args) {
      return fn(...args);
    }
    
    function doWork() {
      throw new Error('boom', { framesAbove: wrapper });
    }
    
    // 没有 framesAbove,堆栈可能以以下开始:
    // Error: boom
    //   at doWork (...)
    //   at wrapper (...)
    //   at main (...)
    //
    // 使用 framesAbove: wrapper,堆栈从 wrapper 下面的第一个帧开始:
    // Error: boom
    //   at doWork (...)
    //   at main (...)

    多次出现(最顶层获胜)

    function trampoline(fn) { return fn(); }
    function inner() { throw new Error('x', { framesAbove: trampoline }); }
    function mid() { return trampoline(inner); }
    function outer() { return trampoline(mid); }
    outer();
    // 所有最顶层 trampoline 调用之上的帧(包括该调用)都被省略。

    与堆栈跟踪限制的交互

    Error.stackTraceLimit = 2;
    function helper() { throw new Error('x', { framesAbove: helper }); }
    helper();
    // 省略的 helper 帧不消耗限制。
    // 剩余的 2 帧取自 helper 下方。

    详细语义

    • framesAbove 是所有接受 options 袋的标准 Error 子类构造函数识别的选项。
    • framesAbove 关联的值:
      • 必须是可调用函数,或 undefined
      • 仅针对其提供该选项的错误进行考虑;它没有全局效果。
      • 不改变消息、名称或 cause 语义。
    • 省略的帧:
      • 不会出现在生成的堆栈字符串中。
      • 不计入引擎应用的任何堆栈长度限制。
      • 在应用任何限制或格式化之前,概念上被移除。

    stack frame 是实现定义的。

    与现有宿主行为的关系

    许多宿主已经提供了移除帧的能力(例如,V8/Node 的 Error.captureStackTrace(target, constructorOpt))。framesAbove 提供了标准化的语言级行为,具有类似的意图:

    • 可在不暴露宿主特定 API 的环境中工作。
    • 为“最顶层调用”和“包含性省略”提供一致的语义。

    宿主可以继续提供额外的设施;这些与 framesAbove 保持正交。

    规范文本(大纲)

    本节概述了规范性更改;精确措辞和集成点将在随附的规范文本中提供。

    1. 在每个接受 options 参数的 Error(及子类)构造函数中:
      • options 是第二个(对于 AggregateError 是第三个)参数。
      • 如果 options 不是 undefined
        • framesAbove 为 ? Get(options, "framesAbove").
        • 如果 framesAbove 不是 undefined 且 IsCallable(framesAbove) 为 false,则抛出 TypeError
    2. 在错误实例初始化期间,将 framesAbove(可能是 undefined)记录在错误的内部错误数据槽中,以便稍后在堆栈捕获期间使用。
    3. 在捕获错误实例 E 的堆栈时:
      • S 为在无该选项的情况下本应为 E 捕获的帧序列。
      • 如果 E.[[ErrorData]].[[FramesAboveFunction]] 是函数 F:
        • kS 中最小的索引(从最近帧的索引 0 开始计数),使得 S[k] 的关联函数是 F。
        • 如果存在这样的 k,则将 S 替换为从索引 k + 1 到末尾的 S 切片。
      • S 应用任何堆栈长度限制。
      • 照常从 S 生成堆栈字符串。

    参见完整规范草案:

    非目标与边缘情况

    • 本提案不标准化堆栈字符串格式、源映射应用或异步堆栈拼接。
    • 如果提供的函数从未出现在捕获的堆栈中,则不删除任何内容。
    • 如果提供的值可调用但具有外部性(例如,Proxy 包装的函数),引擎使用其现有的帧到函数关联的概念。
    • 该选项是针对每个错误的,不影响其他错误。

    考虑的替代方案

    • 继续依赖宿主特定 API(Error.captureStackTrace)或后处理。由于缺乏可移植性到其他 JS 引擎、API 使用困难以及相关的 CPU 开销而被拒绝。
    • 接受帧索引或哨兵而不是函数。由于脆弱性和跨调用路径缺乏组合性而被拒绝。

    结论

    framesAbove 提供了一种简单、明确的方法来从错误堆栈中移除无用的包装器帧,改善开发人员体验,同时与现有的宿主实践保持一致。通过标准化这种行为,生态系统获得了一个可移植、确定性的工具,用于生成更高信号的堆栈跟踪。