Skip to content

refactor(escape): Use the escape/unescape function from dtkcore to ensure consistency#352

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
ComixHe:master
May 9, 2026
Merged

refactor(escape): Use the escape/unescape function from dtkcore to ensure consistency#352
deepin-bot[bot] merged 1 commit into
linuxdeepin:masterfrom
ComixHe:master

Conversation

@ComixHe

@ComixHe ComixHe commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Bug: PMS-357251

Summary by Sourcery

Add UTF-8 byte-based escaping for object paths and application IDs to support non-ASCII names while remaining compatible with D-Bus and systemd requirements.

Enhancements:

  • Refactor escaping utilities to operate on raw UTF-8 bytes and centralize byte unescaping logic for reuse between object path and application ID handling.

Tests:

  • Add comprehensive unit tests covering UTF-8 byte escaping/unescaping, round-trip behavior, and D-Bus/systemd compliance for object paths and application IDs.

@ComixHe ComixHe requested a review from BLumia April 27, 2026 03:20
@sourcery-ai

sourcery-ai Bot commented Apr 27, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors the escaping/unescaping logic for D-Bus object paths and systemd-style application IDs to operate on raw UTF-8 bytes instead of truncated QChar cells, and adds comprehensive unit tests to verify correct UTF-8 handling, round-tripping, and spec compliance.

File-Level Changes

Change Details Files
Rework escape/unescape helpers to operate on raw UTF-8 byte sequences instead of QChar cells, enabling lossless handling of non-ASCII characters.
  • Replace isBaseAlnum(QChar) with isAlnum(char) to classify ASCII-safe bytes directly.
  • Introduce Detail::unescapeToBytes that parses hex-escaped sequences into a QByteArray instead of returning QString.
  • Change escapeToObjectPath to take QByteArrayView, escape non-alphanumeric and non-'/' bytes as _xx, and add an overload that converts QStringView to UTF-8 before escaping.
  • Change unescapeFromObjectPath to decode from UTF-8 bytes produced by Detail::unescapeToBytes.
  • Change escapeApplicationId to take QByteArrayView, escape unsafe bytes as \xhh (operating on UTF-8), and add an overload from QStringView.
  • Change unescapeApplicationId to use Detail::unescapeToBytes and then decode from UTF-8.
  • Emit qCWarning diagnostics when non-ASCII input is encountered for object paths and application IDs, warning about spec compliance and future compatibility.
src/global.h
Adjust call sites to use the new UTF-8–based escape helpers for object paths built from known-ASCII and app IDs.
  • Update getCurrentSessionId to call escapeToObjectPath with a narrow literal "graphical-session.target".
  • Update getObjectPathFromAppId to convert appId to local 8-bit (UTF-8-compatible) bytes and pass them to escapeToObjectPath.
src/global.h
Add unit tests to validate UTF-8 byte-based escaping/unescaping, round-tripping, and spec-compliant behavior for D-Bus object paths and systemd-style application IDs.
  • Include global.h in the test file to access the new helpers.
  • Add EscapeToObjectPath_UTF8ByteSequence tests covering ASCII, control chars, and multibyte UTF-8 (CJK, accented characters).
  • Add UnescapeFromObjectPath_UTF8ByteSequence tests including mixed ASCII/UTF-8 and control characters.
  • Add ObjectPath_RoundTrip tests exercising various ASCII and non-ASCII strings.
  • Add ObjectPath_DBusCompliance tests that validate each path segment against D-Bus allowed characters.
  • Add EscapeApplicationId_SystemdStandard tests that cover systemd escaping rules including slashes, leading dots, spaces, hyphens, control chars, and UTF-8 sequences.
  • Add EscapeApplicationId_SystemdEscapeCompatible tests to assert basic sanity for systemd unit name compatibility.
  • Add UnescapeApplicationId_UTF8ByteSequence tests including malformed escape sequences that should be preserved verbatim.
  • Add ApplicationId_RoundTrip tests for ASCII and non-ASCII IDs.
tests/ut_escape.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In getObjectPathFromAppId, appId is converted using toLocal8Bit() before passing to escapeToObjectPath, whereas the rest of the escaping/unescaping logic consistently uses UTF-8; consider switching this to toUtf8() to avoid locale-dependent behavior and to match the new UTF-8–based semantics.
  • The logging messages added for non-ASCII warnings build a long string from multiple literals without consistently including trailing spaces, resulting in messages like for now,but and future.Please; it would be clearer to either use a single literal or ensure each fragment ends with the appropriate spacing.
  • The new overload set for escapeToObjectPath (QStringView vs QByteArrayView) may be ambiguous for string literals like "graphical-session.target"; it would be safer to consistently pass either QStringView (e.g. u"..."_s) or explicitly constructed QByteArrayView to make the intended overload selection unambiguous.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `getObjectPathFromAppId`, `appId` is converted using `toLocal8Bit()` before passing to `escapeToObjectPath`, whereas the rest of the escaping/unescaping logic consistently uses UTF-8; consider switching this to `toUtf8()` to avoid locale-dependent behavior and to match the new UTF-8–based semantics.
- The logging messages added for non-ASCII warnings build a long string from multiple literals without consistently including trailing spaces, resulting in messages like `for now,but` and `future.Please`; it would be clearer to either use a single literal or ensure each fragment ends with the appropriate spacing.
- The new overload set for `escapeToObjectPath` (QStringView vs QByteArrayView) may be ambiguous for string literals like `"graphical-session.target"`; it would be safer to consistently pass either `QStringView` (e.g. `u"..."_s`) or explicitly constructed `QByteArrayView` to make the intended overload selection unambiguous.

## Individual Comments

### Comment 1
<location path="src/global.h" line_range="493-499" />
<code_context>
     }

+    if (hasNonAscii) {
+        qCWarning(DDEAMUtils) << "String contains non-ASCII characters, which "
+                                 "does not conform to the DBus specification. A "
+                                 "compatible path is generated using UTF-8 byte escaping for now,"
+                                 "but this compatibility may be removed in the future."
+                                 "Please use ASCII-only names.";
+    }
</code_context>
<issue_to_address>
**nitpick (typo):** Warning message string concatenation misses spaces between some words.

Adjacent string literals are joined exactly as written, so these fragments produce `"for now,but"` and `"future.Please"` without spaces. Please add trailing/leading spaces to the relevant fragments so the final warning message reads correctly.

```suggestion
    if (hasNonAscii) {
        qCWarning(DDEAMUtils) << "String contains non-ASCII characters, which "
                                 "does not conform to the DBus specification. A "
                                 "compatible path is generated using UTF-8 byte escaping for now, "
                                 "but this compatibility may be removed in the future. "
                                 "Please use ASCII-only names.";
    }
```
</issue_to_address>

### Comment 2
<location path="src/global.h" line_range="906-907" />
<code_context>
     const auto &basePath = fromStaticRaw(DDEApplicationManager1ObjectPath);
     if (!appId.isEmpty()) {
-        return basePath % '/' % escapeToObjectPath(appId);
+        auto appIdBytes = appId.toLocal8Bit();
+        return basePath % '/' % escapeToObjectPath(appIdBytes);
     }

</code_context>
<issue_to_address>
**question (bug_risk):** Question the use of toLocal8Bit here instead of a UTF-8 conversion for object path escaping.

`escapeToObjectPath(QStringView)` uses UTF‑8 (`str.toUtf8()`), but `getObjectPathFromAppId` now passes `appId.toLocal8Bit()` to the `QByteArrayView` overload. This makes the object path encoding locale‑dependent and inconsistent with the UTF‑8‑based escaping logic, and could produce different results on non‑UTF‑8 locales. If the app ID is meant to be UTF‑8, consider using `appId.toUtf8()` or the `QStringView` overload instead.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/global.h Outdated
Comment on lines +493 to +499
if (hasNonAscii) {
qCWarning(DDEAMUtils) << "String contains non-ASCII characters, which "
"does not conform to the DBus specification. A "
"compatible path is generated using UTF-8 byte escaping for now,"
"but this compatibility may be removed in the future."
"Please use ASCII-only names.";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Warning message string concatenation misses spaces between some words.

Adjacent string literals are joined exactly as written, so these fragments produce "for now,but" and "future.Please" without spaces. Please add trailing/leading spaces to the relevant fragments so the final warning message reads correctly.

Suggested change
if (hasNonAscii) {
qCWarning(DDEAMUtils) << "String contains non-ASCII characters, which "
"does not conform to the DBus specification. A "
"compatible path is generated using UTF-8 byte escaping for now,"
"but this compatibility may be removed in the future."
"Please use ASCII-only names.";
}
if (hasNonAscii) {
qCWarning(DDEAMUtils) << "String contains non-ASCII characters, which "
"does not conform to the DBus specification. A "
"compatible path is generated using UTF-8 byte escaping for now, "
"but this compatibility may be removed in the future. "
"Please use ASCII-only names.";
}

Comment thread src/global.h Outdated
Comment on lines +906 to +907
auto appIdBytes = appId.toLocal8Bit();
return basePath % '/' % escapeToObjectPath(appIdBytes);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (bug_risk): Question the use of toLocal8Bit here instead of a UTF-8 conversion for object path escaping.

escapeToObjectPath(QStringView) uses UTF‑8 (str.toUtf8()), but getObjectPathFromAppId now passes appId.toLocal8Bit() to the QByteArrayView overload. This makes the object path encoding locale‑dependent and inconsistent with the UTF‑8‑based escaping logic, and could produce different results on non‑UTF‑8 locales. If the app ID is meant to be UTF‑8, consider using appId.toUtf8() or the QStringView overload instead.

BLumia
BLumia previously approved these changes Apr 27, 2026
@ComixHe ComixHe changed the title feat(escape): support full Unicode with UTF-8 byte escaping refactor(escape): Use the escape/unescape function from dtkcore to ensure consistency May 8, 2026
@ComixHe ComixHe requested a review from BLumia May 8, 2026 09:59
Comment thread src/global.h Outdated
namespace Detail {

inline bool isBaseAlnum(QChar ch) noexcept
inline bool isAlnum(char ch) noexcept

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这啥

@deepin-bot

deepin-bot Bot commented May 9, 2026

Copy link
Copy Markdown

TAG Bot

New tag: 1.2.52
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #355

BLumia
BLumia previously approved these changes May 9, 2026
…sure consistency

Bug: PMS-357251
Signed-off-by: ComixHe <heyuming@deepin.org>
@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: BLumia, ComixHe

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@deepin-ci-robot

Copy link
Copy Markdown

deepin pr auto review

Git Diff 代码审查报告

总体评估

这次修改主要将应用程序ID和对象路径的转义/反转义功能从本地实现迁移到Dtk6::Core库(DUtil),并添加了全面的单元测试。整体改动方向正确,符合代码复用原则,但存在一些需要改进的地方。

详细审查

1. 语法与逻辑

1.1 头文件包含

#include <DUtil>

问题:头文件包含方式不规范

  • 应该使用 #include <DUtil/DUtil> 或更具体的头文件路径
  • 建议检查DTK文档确认正确的头文件包含方式

1.2 escapeApplicationId函数实现

inline QString escapeApplicationId(QByteArrayView utf8)

问题:函数签名与调用方式不一致

  • escapeApplicationId(QStringView) 调用 escapeApplicationId(QByteArrayView)
  • 建议明确函数重载关系,或者统一使用一种类型

1.3 unescapeToBytes函数

inline QByteArray unescapeToBytes(const QStringView str, const QStringView prefix) noexcept

问题:边界条件处理

  • str 为空时,函数行为未明确
  • 建议添加空字符串检查

2. 代码质量

2.1 魔法数字

result.reserve(utf8.size() * 4);  // Worst case: every byte needs "\xxx" (4 chars)

建议

  • 虽然有注释,但建议定义常量 constexpr int MAX_ESCAPE_LENGTH = 4;
  • 提高代码可维护性

2.2 警告信息

qCWarning(DDEAMUtils) << "String contains non-ASCII characters...";

问题

  • 警告信息过长且格式不统一
  • 建议拆分为多行或使用更简洁的表述

2.3 函数命名

inline QString escapeApplicationId(QByteArrayView utf8)

建议

  • 参数名 utf8 与实际类型 QByteArrayView 不完全匹配
  • 建议改为 utf8Bytesdata

3. 代码性能

3.1 字符串转换

return escapeApplicationId(str.toUtf8());

问题

  • 每次调用都会创建临时 QByteArray
  • 建议考虑直接接受 QByteArrayView 参数,避免转换

3.2 内存分配

result.reserve(utf8.size() * 4);

建议

  • 对于大多数ASCII字符串,这个预留过大
  • 可以考虑动态调整或分批处理

3.3 字符串操作

result.append(uR"(\x)"_s);
result.append(QString::number(byte, 16).rightJustified(2, u'0').toLower());

问题

  • 多次字符串拼接和临时对象创建
  • 建议预分配足够空间并使用更高效的拼接方式

4. 代码安全

4.1 类型转换

auto byte = static_cast<unsigned char>(utf8.at(i));

问题

  • 需要确保 utf8.at(i) 不会越界
  • 建议添加边界检查

4.2 十六进制转换

auto byte = static_cast<unsigned char>(str.mid(i + prefixLen, 2).toUShort(&ok, 16));

问题

  • toUShort 可能失败,虽然有 ok 检查
  • 建议添加更严格的错误处理

4.3 非ASCII字符处理

if (byte > 0x7F) {
    hasNonAscii = true;
}

问题

  • 仅记录警告,可能导致不符合规范的ID被使用
  • 建议考虑更严格的处理策略

5. 单元测试

5.1 测试覆盖

优点

  • 测试用例全面,覆盖了各种边界情况
  • 包含了往返测试和兼容性测试

建议

  • 添加性能基准测试,比较新旧实现的性能差异
  • 添加压力测试,验证大量数据处理的稳定性

5.2 测试用例

{"test\x01", "test\\x01", "Control character should be escaped"},

问题

  • 控制字符测试用例较少
  • 建议添加更多控制字符测试(0x00-0x1F)

6. 依赖管理

6.1 版本要求

libdtk6core-dev (>= 6.7.41),

问题

  • 版本要求较新,需要确认目标系统是否支持
  • 建议添加版本检查和降级方案

改进建议

  1. 头文件包含

    #include <DUtil/DUtil>  // 确认正确路径
  2. 性能优化

    inline QString escapeApplicationId(QByteArrayView utf8)
    {
        if (utf8.isEmpty()) {
            return {};
        }
        
        constexpr int MAX_ESCAPE_LENGTH = 4;
        QString result;
        result.reserve(utf8.size() * MAX_ESCAPE_LENGTH);
        
        // ... 其余实现
    }
  3. 安全增强

    if (i > len - prefixLen - 2) {
        break;  // 防止越界
    }
  4. 错误处理

    if (!ok) {
        qCWarning(DDEAMUtils) << "Invalid escape sequence at position" << i;
        return {};  // 或采取其他恢复策略
    }

总结

这次修改整体方向正确,将通用功能迁移到DTK库中,提高了代码复用性。主要改进点应集中在:

  1. 规范头文件包含
  2. 优化字符串处理性能
  3. 增强错误处理和边界检查
  4. 改进非ASCII字符处理策略
  5. 完善单元测试覆盖

建议在合并前进行充分的集成测试,确保与DTK库的交互符合预期。

@ComixHe

ComixHe commented May 9, 2026

Copy link
Copy Markdown
Contributor Author

/forcemerge

@deepin-bot

deepin-bot Bot commented May 9, 2026

Copy link
Copy Markdown

This pr force merged! (status: blocked)

@deepin-bot deepin-bot Bot merged commit 7c121c0 into linuxdeepin:master May 9, 2026
16 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants