| 884 | } |
| 885 | |
| 886 | async bindBreakpoints() { |
| 887 | const uniqueTargets = new SafeMap(); |
| 888 | |
| 889 | for (let probeIndex = 0; probeIndex < this.probes.length; probeIndex++) { |
| 890 | const { target } = this.probes[probeIndex]; |
| 891 | const key = `${target.suffix}\n${target.line}\n${target.column ?? ''}`; |
| 892 | let entry = uniqueTargets.get(key); |
| 893 | if (entry === undefined) { |
| 894 | entry = { target, probeIndices: [] }; |
| 895 | uniqueTargets.set(key, entry); |
| 896 | } |
| 897 | ArrayPrototypePush(entry.probeIndices, probeIndex); |
| 898 | } |
| 899 | |
| 900 | for (const { target, probeIndices } of uniqueTargets.values()) { |
| 901 | // On Windows, normalize backslashes to forward slashes so the regex matches |
| 902 | // V8 script URLs which always use forward slashes. |
| 903 | const normalizedFile = process.platform === 'win32' ? |
| 904 | SideEffectFreeRegExpPrototypeSymbolReplace(/\\/g, target.suffix, '/') : |
| 905 | target.suffix; |
| 906 | const escapedPath = SideEffectFreeRegExpPrototypeSymbolReplace( |
| 907 | /([/\\.?*()^${}|[\]])/g, |
| 908 | normalizedFile, |
| 909 | '\\$1', |
| 910 | ); |
| 911 | const params = { |
| 912 | urlRegex: `^(.*[\\/\\\\])?${escapedPath}$`, |
| 913 | // CDP locations are 0-based, the probe target from CLI is 1-based. |
| 914 | lineNumber: target.line - 1, |
| 915 | }; |
| 916 | if (target.column !== undefined) { |
| 917 | // Only pass columnNumber to CDP when the user specifies one, otherwise let |
| 918 | // the inspector bind to the first executable column. |
| 919 | params.columnNumber = target.column - 1; |
| 920 | } |
| 921 | |
| 922 | const result = await this.callCdp('Debugger.setBreakpointByUrl', params); |
| 923 | debug('breakpoint set: id=%s urlRegex=%s locations=%j', |
| 924 | result.breakpointId, params.urlRegex, result.locations); |
| 925 | this.breakpointDefinitions.set(result.breakpointId, { probeIndices }); |
| 926 | } |
| 927 | } |
| 928 | |
| 929 | getPendingProbeIndices() { |
| 930 | const pending = []; |