Skip to content

Follow up #220/#230: drop unused timeout id and emit U+FFFD for malformed UTF-16 - #235

Open
bkaradzic-microsoft wants to merge 3 commits into
BabylonJS:mainfrom
bkaradzic-microsoft:fix/post-merge-followups
Open

Follow up #220/#230: drop unused timeout id and emit U+FFFD for malformed UTF-16#235
bkaradzic-microsoft wants to merge 3 commits into
BabylonJS:mainfrom
bkaradzic-microsoft:fix/post-merge-followups

Conversation

@bkaradzic-microsoft

Copy link
Copy Markdown
Member

Summary

Follow-up for two post-merge comments:

TimeoutDispatcher (#220)

DispatchImpl still took an id, but after #220 the only caller is Dispatch, which always passed 0. The id == 0 branch was therefore dead, and the remaining reuse path is unsafe: m_idMap.insert keeps the existing Timeout on collision, then m_timeMap.insert adds a second entry pointing at it, and Clear() erases only one.

Dispatch now always allocates a fresh id via NextTimeoutId() and emplaces the new Timeout. DispatchImpl is gone.

TextDecoder UTF-16 (#230)

The UTF-16 path dropped a trailing odd byte and created JS strings from unpaired surrogates. Replacement mode in the Encoding Standard requires U+FFFD for both, in either endianness.

DecodeUtf16 now:

  • emits U+FFFD for a leftover odd byte
  • emits U+FFFD for a lone lead or trail surrogate
  • treats a lead followed by a non-trail as one replacement, then reprocesses the second unit
  • treats an unpaired lead plus leftover odd byte as a single end-of-queue replacement

Tests

  • Trailing odd byte, both endiannesses
  • Lone lead / lone trail, both endiannesses
  • Lead + BMP (U+D800 then 'A') → "\uFFFDA"
  • Lead + leftover odd byte → a single "\uFFFD"

Existing valid-pair and BOM tests are unchanged.

DispatchImpl still accepted an id after BabylonJS#220, but Dispatch always passed 0.
If a future caller reused an existing id, unordered_map::insert would keep
the old Timeout and m_timeMap would gain a second entry pointing at it;
Clear() erases only one. Always allocate a fresh id.

The UTF-16 decoder dropped a trailing odd byte and passed unpaired
surrogates through to Napi::String. The Encoding Standard replacement
mode requires U+FFFD for both, in either endianness.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da74bc94-a7dc-4817-bd81-59b5c6b123fc
Copilot AI lite review requested due to automatic review settings September 3, 2026 22:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The changes are focused, match the stated follow-up goals, and are supported by targeted new tests for the UTF-16 edge cases.

Pull request overview

This PR is a follow-up to prior review comments in #220 and #230, tightening TimeoutDispatcher’s timeout-id handling to avoid unsafe reuse paths and aligning the UTF-16 TextDecoder implementation with the Encoding Standard’s replacement behavior for malformed input.

Changes:

  • Removes the unused DispatchImpl path and ensures TimeoutDispatcher::Dispatch always allocates a fresh timeout id.
  • Updates UTF-16 decoding to emit U+FFFD for a trailing odd byte and for unpaired surrogates, matching replacement-mode behavior.
  • Adds unit tests covering malformed UTF-16 edge cases for both utf-16le and utf-16be, and updates the polyfill README accordingly.
File summaries
File Description
Tests/UnitTests/Scripts/tests.ts Adds JS tests for malformed UTF-16 replacement behavior in TextDecoder.
Polyfills/TextDecoder/Source/TextDecoder.cpp Implements replacement-mode malformed UTF-16 handling (odd byte + unpaired surrogates).
Polyfills/TextDecoder/README.md Documents the updated malformed UTF-16 replacement behavior.
Polyfills/Scheduling/Source/TimeoutDispatcher.h Removes the now-unused DispatchImpl declaration.
Polyfills/Scheduling/Source/TimeoutDispatcher.cpp Removes DispatchImpl and makes Dispatch always allocate a fresh id to avoid unsafe reuse/collision behavior.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Polyfills/TextDecoder/README.md Outdated
The previous wording said errors were "not detected" while also describing
U+FFFD replacement. fatal still does not throw; replacement is the
supported behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da74bc94-a7dc-4817-bd81-59b5c6b123fc
@bkaradzic-microsoft
bkaradzic-microsoft requested a lite review from Copilot September 3, 2026 22:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment thread Polyfills/Scheduling/Source/TimeoutDispatcher.cpp Outdated
Use try_emplace and only insert into m_timeMap from the owned map entry so
a colliding id cannot destroy the Timeout and leave a dangling pointer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: da74bc94-a7dc-4817-bd81-59b5c6b123fc
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