WebKit 中断言宏指南

背景

WebKit 提供了许多宏来断言代码中的条件得到满足。它们定义在 Source/JavaScriptCore/wtf/Assertions.h 中。本文档概述了各种宏,包括其使用指南和最佳实践。

ASSERT 宏的类型

ASSERT() 宏及其变体在发布版本中会被编译掉。它们旨在开发过程中用于捕获编程错误。对于那些接受表达式作为参数的宏,该表达式在发布版本中也会被编译掉,因此在最终代码中不会产生开销。

  • ASSERT(expression) – 在开发过程中使用,以确保不会发生意外情况。如果表达式评估为假,则中止执行并进入调试器。
  • ASSERT_NOT_REACHED() – 用于指示某段代码分支永远不应被执行。
if (condition) {
  ...
} else {
    // This should never happen.
    ASSERT_NOT_REACHED();
}
  • ASSERT_UNUSED(variable, expression) – 用于断言检查否则未使用的变量的值。如果您考虑这样一种情况,即您希望断言一个使用在包含函数中不会被使用的变量的表达式,那么这种宏的必要性就显而易见了。在这种情况下不能使用 ASSERT(),因为在发布版本中编译器会就未使用的变量发出警告。ASSERT_UNUSED() 避免了此警告。

    示例来自 Source/JavaScriptCore/jit/ExecutableAllocatorPosix.cpp

void ExecutablePool::systemRelease(const ExecutablePool::Allocation& alloc)
{ 
    int result = munmap(alloc.pages, alloc.size);
    // result not used outside the ASSERT().
    ASSERT_UNUSED(result, !result);
}

CRASH() 宏

CRASH() 引发致命错误,导致程序终止并触发调试器或崩溃报告器。它在调试模式和发布模式下都有效。CRASH() 直接影响用户,因为它会中断或结束他们的浏览会话。如果浏览器供应商追踪崩溃,这对于诊断可能只在用户机器上发生的难以发现的问题非常有帮助。

使用 ASSERT() 和 CRASH() 宏的注意事项。

使用 ASSERT() 系列宏的风险

ASSERTASSERT_UNUSED 宏内部的表达式,连同宏本身,在发布版本中会被编译掉。如果使用的表达式具有副作用,那么在发布版本中省略它可能会导致在调试版本中不出现的编程错误。

使用 CRASH 的好处

  • 如果浏览器供应商追踪其软件中的崩溃,CRASH() 可以从最终用户那里提供重要信息,以便解决问题。
  • CRASH() 之后的代码保证不可达,这有助于防止某些 Bug 成为安全隐患。

使用 CRASH 的代价

  • 使用 CRASH 宏会将编程错误变成崩溃,在本来可能无害的情况下导致网页或整个网页浏览器崩溃。
  • 在发布版本中检查错误条件可能会降低程序速度。

使用 ASSERT() 和 CRASH() 宏的指南。

  • 对永远不应该发生但如果发生会导致结果不正确而非崩溃或内存损坏的情况使用 ASSERT()
  • 断言是程序员已知为真的声明,它们仅在程序员因某种编程错误而犯错时才触发。不应有“一厢情愿”的想法。例如,不应使用 ASSERT() 来验证文件系统调用是否成功,因为程序员无法保证这一点。
  • 对于不应发生但如果发生将无法恢复的情况使用 CRASH()。例如,内存不足错误。

使用 CRASH() 与 ASSERT() 的示例

// Not having any children indicates a programming error.
ASSERT(object->numChildren() > 0);

Allocation bitmapStorage = systemAlloc(allocSize);
if (!bitmapStorage.pages)
    CRASH(); // Not possible to recover from an out of memory error.

如果您对以上内容有任何意见或关于提高清晰度、范围或展示的其他想法,请发送邮件至 WebKit 邮件列表