diff --git a/CHANGELOG.md b/CHANGELOG.md index dc1a5b277..d5c03b4ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ This changelog records changes to stable releases since 1.50.2. "TBA" changes he - fix: envFiles variables appending rather than replacing in attach ([vscode#1935510](https://github.com/microsoft/vscode/issues/1935510)) - fix: make source map renames scope-aware +- fix: breakpoints not setting in webpack `eval`-type sourcemaps ([vscode#194988](https://github.com/microsoft/vscode/issues/194988)) - fix: error when processing private properties with a map ([#1824](https://github.com/microsoft/vscode-js-debug/issues/1824)) ## v1.83 (September 2023) diff --git a/src/adapter/breakpoints.ts b/src/adapter/breakpoints.ts index 239593900..089617df5 100644 --- a/src/adapter/breakpoints.ts +++ b/src/adapter/breakpoints.ts @@ -697,17 +697,11 @@ export class BreakpointManager { // Fix: if we stopped in a script where an active entrypoint breakpoint // exists, regardless of the reason, treat this as a breakpoint. // ref: https://github.com/microsoft/vscode/issues/107859 - const entryInScript = [...this.moduleEntryBreakpoints.values()].filter( + const entryInScript = [...this.moduleEntryBreakpoints.values()].some( bp => bp.enabled && bp.cdpScriptIds.has(scriptId), ); - if (entryInScript.length) { - for (const breakpoint of entryInScript) { - if (!(breakpoint instanceof PatternEntryBreakpoint)) { - breakpoint.disable(); - } - } - + if (entryInScript) { return true; } @@ -717,6 +711,12 @@ export class BreakpointManager { }); } + /** Gets whether the CDP breakpoint ID refers to an entrypoint breakpoint. */ + public isEntrypointCdpBreak(cdpId: string) { + const bp = this._resolvedBreakpoints.get(cdpId); + return bp instanceof EntryBreakpoint; + } + /** * Handler that should be called *after* source map resolution on an entry * breakpoint. Returns whether the debugger should remain paused. diff --git a/src/adapter/breakpoints/breakpointBase.ts b/src/adapter/breakpoints/breakpointBase.ts index 42b972b6f..19471805d 100644 --- a/src/adapter/breakpoints/breakpointBase.ts +++ b/src/adapter/breakpoints/breakpointBase.ts @@ -518,6 +518,7 @@ export abstract class Breakpoint { bp.args.location.scriptId === script.scriptId && lcEqual(bp.args.location, lineColumn)) || (script.url && + !script.hasSourceURL && isSetByUrl(bp.args) && (bp.args.urlRegex ? new RegExp(bp.args.urlRegex).test(script.url) @@ -560,8 +561,9 @@ export abstract class Breakpoint { protected async _setForSpecific(thread: Thread, script: ISourceScript, lineColumn: LineColumn) { // prefer to set on script URL for non-anonymous scripts, since url breakpoints - // will survive and be hit on reload. - if (script.url) { + // will survive and be hit on reload. But don't set if the script has + // a source URL, since V8 doesn't resolve these + if (script.url && !script.hasSourceURL) { return this._setByUrl(thread, script.url, lineColumn); } else { return this._setByScriptId(thread, script, lineColumn); diff --git a/src/adapter/source.ts b/src/adapter/source.ts index 77c4f3f9d..74776eea3 100644 --- a/src/adapter/source.ts +++ b/src/adapter/source.ts @@ -384,6 +384,7 @@ export interface IWasmLocationProvider extends ISourceLocationProvider { export interface ISourceScript { executionContextId: Cdp.Runtime.ExecutionContextId; scriptId: Cdp.Runtime.ScriptId; + hasSourceURL: boolean; url: string; } diff --git a/src/adapter/threads.ts b/src/adapter/threads.ts index 08d0f2fdd..94e70b618 100644 --- a/src/adapter/threads.ts +++ b/src/adapter/threads.ts @@ -36,7 +36,14 @@ import * as objectPreview from './objectPreview'; import { PreviewContextType, getContextForType } from './objectPreview/contexts'; import { ExpectedPauseReason, IPausedDetails, StepDirection } from './pause'; import { SmartStepper } from './smartStepping'; -import { ISourceWithMap, IUiLocation, Source, base1To0, isSourceWithWasm } from './source'; +import { + ISourceScript, + ISourceWithMap, + IUiLocation, + Source, + base1To0, + isSourceWithWasm, +} from './source'; import { IPreferredUiLocation, SourceContainer } from './sourceContainer'; import { InlinedFrame, StackFrame, StackTrace, isStackFrameElement } from './stackTrace'; import { @@ -69,10 +76,7 @@ export class ExecutionContext { } } -export type Script = { - url: string; - scriptId: string; - executionContextId: number; +export type Script = ISourceScript & { source: Promise; resolvedSource?: Source; }; @@ -1265,8 +1269,11 @@ export class Thread implements IVariableStoreLocationProvider { event.reason === 'other' || event.reason === 'ambiguous'; - const hitAnyBreakpoint = !!(event.hitBreakpoints && event.hitBreakpoints.length); - if (hitAnyBreakpoint || !sameDebuggingSequence) this._sourceContainer.clearDisabledSourceMaps(); + const hitBreakpoints = + event.hitBreakpoints?.filter(bp => !this._breakpointManager.isEntrypointCdpBreak(bp)) || []; + + if (hitBreakpoints.length || !sameDebuggingSequence) + this._sourceContainer.clearDisabledSourceMaps(); if (event.hitBreakpoints && this._sourceMapDisabler) { for (const sourceToDisable of this._sourceMapDisabler(event.hitBreakpoints)) @@ -1379,10 +1386,10 @@ export class Thread implements IVariableStoreLocationProvider { description: l10n.t('Paused before Out Of Memory exception'), }; default: - if (event.hitBreakpoints && event.hitBreakpoints.length) { + if (hitBreakpoints.length) { let isStopOnEntry = false; // By default we assume breakpoints aren't stop on entry const userEntryBp = this.target.entryBreakpoint; - if (userEntryBp && event.hitBreakpoints.includes(userEntryBp.cdpId)) { + if (userEntryBp && hitBreakpoints.includes(userEntryBp.cdpId)) { isStopOnEntry = true; // But if it matches the entry breakpoint id, then it's probably stop on entry const entryBreakpointSource = this._sourceContainer.source({ path: fileUrlToAbsolutePath(userEntryBp.path), @@ -1404,7 +1411,7 @@ export class Thread implements IVariableStoreLocationProvider { } if (!isStopOnEntry) { - this._breakpointManager.registerBreakpointsHit(event.hitBreakpoints); + this._breakpointManager.registerBreakpointsHit(hitBreakpoints); } return { thread: this, @@ -1515,6 +1522,7 @@ export class Thread implements IVariableStoreLocationProvider { prevSource.addScript({ scriptId: event.scriptId, url: event.url, + hasSourceURL: !!event.hasSourceURL, executionContextId: event.executionContextId, }); return prevSource; @@ -1565,6 +1573,7 @@ export class Thread implements IVariableStoreLocationProvider { source.addScript({ scriptId: event.scriptId, url: event.url, + hasSourceURL: !!event.hasSourceURL, executionContextId: event.executionContextId, }); @@ -1573,6 +1582,7 @@ export class Thread implements IVariableStoreLocationProvider { const script: Script = { url: event.url, + hasSourceURL: !!event.hasSourceURL, scriptId: event.scriptId, executionContextId: event.executionContextId, source: createSource(), diff --git a/src/test/sources/sources-sourcemap-error-handling-logs-lazy-parse-errors.txt b/src/test/sources/sources-sourcemap-error-handling-logs-lazy-parse-errors.txt index b9c524945..671ba527c 100644 --- a/src/test/sources/sources-sourcemap-error-handling-logs-lazy-parse-errors.txt +++ b/src/test/sources/sources-sourcemap-error-handling-logs-lazy-parse-errors.txt @@ -1,3 +1,10 @@ Evaluating#1: //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZhbDEuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJldmFsMVNvdXJjZS5qcyJdLCJtYXBwaW5ncyI6IiMsIyMjIzsifQ== stderr> Could not read source map for http://localhost:8001/eval1.js: Error parsing mappings (code 4): invalid base 64 character while parsing a VLQ +{ + allThreadsStopped : false + description : Paused on breakpoint + reason : breakpoint + threadId : +} + @ localhost꞉8001/eval1.js:3:23 diff --git a/src/test/sources/sourcesTest.ts b/src/test/sources/sourcesTest.ts index 481c4b208..8a6384937 100644 --- a/src/test/sources/sourcesTest.ts +++ b/src/test/sources/sourcesTest.ts @@ -262,6 +262,7 @@ describe('sources', () => { `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${contents}\n`, ); await p.logger.logOutput(await output); + await waitForPause(p); await ev; p.assertLog(); });