From d6fd68a0f532d4ee62173f7e7b33a37c61465306 Mon Sep 17 00:00:00 2001 From: Rob Hogan Date: Thu, 17 Sep 2026 12:46:56 +0100 Subject: [PATCH] Fix symbolication of stack traces with Windows absolute paths Summary: `metro-symbolicate` parses stack frames with a regex that doesn't allow `:` in file names, so a frame with a Windows absolute path is misparsed at the drive letter: ``` someFunc@D:\app\foo.js:4:0 ``` is read as function `D`, file `\app\foo.js`. The match starts at the drive letter, so `someFunc@` is left in the output, and the file name loses its drive - which matters for directory contexts (`symbolicate `), where the file name is used to find the source map. This allows an optional `X:\` drive prefix on the function-name-or-file slot and the file slot. Posix paths, module IDs (`123.js`), Android (`bar:4:18063`, `bar:123.js:4:18063`) and `[native code]` frames parse exactly as before - the prefix requires a backslash, so e.g. a single-letter function before a posix path (`a:/js/foo.js:4:1`) is unaffected. Changelog: [Fix] Fix `metro-symbolicate` stack trace parsing of Windows absolute paths Test Plan: Adds a platform-independent test that symbolicates `testfile.stack` with `C:\app\` prefixed file names, and expects the same output as the original. Without this change it fails on all platforms: ``` - thrower.js:18:null + throws6@thrower.js:18:null - thrower.js:30:arguments + o@thrower.js:30:arguments ``` Removes `symbolicate-test.js` from the Windows skip list. Its directory context tests use real absolute paths, and previously failed on Windows CI with: ``` - /js/react-native-github/Libraries/BatchedBridge/BatchedBridge.js:23:Object + someFunc@/js/react-native-github/Libraries/BatchedBridge/BatchedBridge.js:23:Object - /__fixtures__/directory/fileThatDoesntExist.js:10:null + fn@\a\metro\metro\packages\metro-symbolicate\src\__tests__\__fixtures__\directory\fileThatDoesntExist.js:10:null ``` ``` yarn jest packages/metro-symbolicate Tests: 76 passed, 76 total ``` --- packages/metro-symbolicate/src/Symbolication.js | 4 +++- .../src/__tests__/symbolicate-test.js | 12 ++++++++++++ scripts/jestFilter.js | 4 ---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/metro-symbolicate/src/Symbolication.js b/packages/metro-symbolicate/src/Symbolication.js index 585c0055a0..407c2e0870 100644 --- a/packages/metro-symbolicate/src/Symbolication.js +++ b/packages/metro-symbolicate/src/Symbolication.js @@ -161,11 +161,13 @@ class SymbolicationContext { // IOS: foo@123.js:4:18131, Android: bar:123.js:4:18063 // sample stack trace without function name: // 123.js:4:18131 + // sample stack trace with a Windows absolute path: + // foo@C:\app\123.js:4:18131 // sample result: // IOS: foo.js:57:foo, Android: bar.js:75:bar symbolicate(stackTrace: string): string { return stackTrace.replace( - /(?:([^@: \n(]+)(@|:))?(?:(?:([^@: \n(]+):)?(\d+):(\d+)|\[native code\])/g, + /(?:((?:[A-Za-z]:\\)?[^@: \n(]+)(@|:))?(?:(?:((?:[A-Za-z]:\\)?[^@: \n(]+):)?(\d+):(\d+)|\[native code\])/g, (match, func, delimiter, fileName, line, column) => { if (delimiter === ':' && func && !fileName) { fileName = func; diff --git a/packages/metro-symbolicate/src/__tests__/symbolicate-test.js b/packages/metro-symbolicate/src/__tests__/symbolicate-test.js index 932f0c79af..ba7d33782a 100644 --- a/packages/metro-symbolicate/src/__tests__/symbolicate-test.js +++ b/packages/metro-symbolicate/src/__tests__/symbolicate-test.js @@ -210,6 +210,18 @@ test('symbolicating a stack trace', async () => execute([TESTFILE_MAP], read('testfile.stack')), ).resolves.toMatchSnapshot()); +test('symbolicating a stack trace with Windows absolute paths', async () => { + const stack = read('testfile.stack'); + const windowsStack = stack.replaceAll( + /(^|@)(thrower\.min\.js)/gm, + '$1C:\\app\\$2', + ); + expect(windowsStack).not.toEqual(stack); + expect(await execute([TESTFILE_MAP], windowsStack)).toEqual( + await execute([TESTFILE_MAP], stack), + ); +}); + test('symbolicating a stack trace in Node format', async () => await expect( execute([TESTFILE_MAP], read('testfile.node.stack')), diff --git a/scripts/jestFilter.js b/scripts/jestFilter.js index f8b8c42c9b..26398044f9 100644 --- a/scripts/jestFilter.js +++ b/scripts/jestFilter.js @@ -15,10 +15,6 @@ const SKIPPED_ON_WINDOWS = [ // flow-api-translator emits os.EOL line endings in generated comments. // Snapshots are generated and verified on posix only. 'scripts/__tests__/api-snapshots-sync-test.js', - - // TODO: Windows product bugs - // Stack trace parsing does not support drive letters - 'packages/metro-symbolicate/src/__tests__/symbolicate-test.js', ]; const SKIPPED_PATHS = process.platform === 'win32' ? SKIPPED_ON_WINDOWS : [];