@react-native/debugger-frontend
Version:
Debugger frontend for React Native based on Chrome DevTools
2 lines (1 loc) • 18.8 kB
JavaScript
import*as e from"../../core/common/common.js";import{assertNotNullOrUndefined as t}from"../../core/platform/platform.js";import*as o from"../../core/root/root.js";import*as i from"../../core/sdk/sdk.js";import*as r from"../bindings/bindings.js";import*as s from"../formatter/formatter.js";import*as n from"../source_map_scopes/source_map_scopes.js";import*as a from"../workspace/workspace.js";let u;class d extends e.ObjectWrapper.ObjectWrapper{storage=new h;#e;targetManager;debuggerWorkspaceBinding;#t=new Map;#o=new Map;#i=new Map;#r=[];constructor(e,t,o,r){super(),this.#e=t,this.targetManager=e,this.debuggerWorkspaceBinding=o,this.storage.mute(),this.#s(r??100),this.storage.unmute(),this.#e.addEventListener(a.Workspace.Events.UISourceCodeAdded,this.uiSourceCodeAdded,this),this.#e.addEventListener(a.Workspace.Events.UISourceCodeRemoved,this.uiSourceCodeRemoved,this),this.#e.addEventListener(a.Workspace.Events.ProjectRemoved,this.projectRemoved,this),this.targetManager.observeModels(i.DebuggerModel.DebuggerModel,this)}#s(e){let t=this.storage.breakpoints.size-e;for(const e of this.storage.breakpoints.values()){if(t>0){t--;continue}const o=h.computeId(e),i=new l(this,null,e,"RESTORED");this.#i.set(o,i)}}static instance(e={forceNew:null,targetManager:null,workspace:null,debuggerWorkspaceBinding:null}){const{forceNew:t,targetManager:o,workspace:i,debuggerWorkspaceBinding:r,restoreInitialBreakpointCount:s}=e;if(!u||t){if(!o||!i||!r)throw new Error(`Unable to create settings: targetManager, workspace, and debuggerWorkspaceBinding must be provided: ${(new Error).stack}`);u=new d(o,i,r,s)}return u}modelAdded(e){o.Runtime.experiments.isEnabled("instrumentation-breakpoints")&&e.setSynchronizeBreakpointsCallback(this.restoreBreakpointsForScript.bind(this))}modelRemoved(e){e.setSynchronizeBreakpointsCallback(null)}addUpdateBindingsCallback(e){this.#r.push(e)}async copyBreakpoints(e,t){const o=t.project().uiSourceCodeForURL(t.url())!==t||this.#e.project(t.project().id())!==t.project(),i=this.storage.breakpointItems(e.url(),e.contentType().name());for(const e of i)o?this.storage.updateBreakpoint({...e,url:t.url(),resourceTypeName:t.contentType().name()}):await this.setBreakpoint(t,e.lineNumber,e.columnNumber,e.condition,e.enabled,e.isLogpoint,"RESTORED")}async restoreBreakpointsForScript(e){if(!o.Runtime.experiments.isEnabled("instrumentation-breakpoints"))return;if(!e.sourceURL)return;const i=await this.getUISourceCodeWithUpdatedBreakpointInfo(e);this.#n(e.sourceURL)&&await this.#a(i);const r=e.debuggerModel,s=await r.sourceMapManager().sourceMapForClientPromise(e);if(s)for(const t of s.sourceURLs())if(this.#n(t)){const o=await this.debuggerWorkspaceBinding.uiSourceCodeForSourceMapSourceURLPromise(r,t,e.isContentScript());await this.#a(o)}const{pluginManager:n}=this.debuggerWorkspaceBinding,a=await n.getSourcesForScript(e);if(Array.isArray(a))for(const e of a)if(this.#n(e)){const o=await this.debuggerWorkspaceBinding.uiSourceCodeForDebuggerLanguagePluginSourceURLPromise(r,e);t(o),await this.#a(o)}}async getUISourceCodeWithUpdatedBreakpointInfo(e){const o=this.debuggerWorkspaceBinding.uiSourceCodeForScript(e);return t(o),await this.#u(o),o}async#u(e){if(this.#r.length>0){const t=[];for(const o of this.#r)t.push(o(e));await Promise.all(t)}}async#a(e){this.restoreBreakpoints(e);const t=this.#i.values(),o=Array.from(t).filter((t=>t.uiSourceCodes.has(e)));await Promise.all(o.map((e=>e.updateBreakpoint())))}#n(e){return this.storage.breakpointItems(e).length>0}static getScriptForInlineUiSourceCode(e){const t=r.DefaultScriptMapping.DefaultScriptMapping.scriptForUISourceCode(e);return t&&t.isInlineScript()&&!t.hasSourceURL?t:null}static breakpointLocationFromUiLocation(e){const t=e.uiSourceCode,o=d.getScriptForInlineUiSourceCode(t),{lineNumber:i,columnNumber:r}=o?o.relativeLocationToRawLocation(e):e;return{lineNumber:i,columnNumber:r}}static uiLocationFromBreakpointLocation(e,t,o){const i=d.getScriptForInlineUiSourceCode(e);return i&&({lineNumber:t,columnNumber:o}=i.rawLocationToRelativeLocation({lineNumber:t,columnNumber:o})),e.uiLocation(t,o)}static isValidPositionInScript(e,t,o){return!o||!(e<o.lineOffset||e>o.endLine)&&(!(e===o.lineOffset&&t&&t<o.columnOffset)&&!(e===o.endLine&&(!t||t>=o.endColumn)))}restoreBreakpoints(e){const t=d.getScriptForInlineUiSourceCode(e),o=t?.sourceURL??e.url();if(!o)return;const i=e.contentType();this.storage.mute();const r=this.storage.breakpointItems(o,i.name());for(const o of r){const{lineNumber:i,columnNumber:r}=o;d.isValidPositionInScript(i,r,t)&&this.innerSetBreakpoint(e,i,r,o.condition,o.enabled,o.isLogpoint,"RESTORED")}this.storage.unmute()}uiSourceCodeAdded(e){const t=e.data;this.restoreBreakpoints(t)}uiSourceCodeRemoved(e){const t=e.data;this.removeUISourceCode(t)}projectRemoved(e){const t=e.data;for(const e of t.uiSourceCodes())this.removeUISourceCode(e)}removeUISourceCode(e){this.#d(e).forEach((t=>t.removeUISourceCode(e)))}async setBreakpoint(t,o,i,r,s,n,u){const c=this.#e.findCompatibleUISourceCodes(t);let l;for(const p of c){const c=new a.UISourceCode.UILocation(p,o,i),h=await this.debuggerWorkspaceBinding.normalizeUILocation(c),g=d.breakpointLocationFromUiLocation(h),b=this.innerSetBreakpoint(h.uiSourceCode,g.lineNumber,g.columnNumber,r,s,n,u);t===p&&(h.id()!==c.id()&&e.Revealer.reveal(h),l=b)}return console.assert(void 0!==l,"The passed uiSourceCode is expected to be a valid uiSourceCode"),l}innerSetBreakpoint(e,t,o,i,r,s,n){const a={url:d.getScriptForInlineUiSourceCode(e)?.sourceURL??e.url(),resourceTypeName:e.contentType().name(),lineNumber:t,columnNumber:o,condition:i,enabled:r,isLogpoint:s},u=h.computeId(a);let c=this.#i.get(u);return c?(c.updateState(a),c.addUISourceCode(e),c.updateBreakpoint(),c):(c=new l(this,e,a,n),this.#i.set(u,c),c)}findBreakpoint(e){const t=this.#o.get(e.uiSourceCode);return t&&t.get(e.id())||null}addHomeUISourceCode(e,t){let o=this.#t.get(e);o||(o=new Set,this.#t.set(e,o)),o.add(t)}removeHomeUISourceCode(e,t){const o=this.#t.get(e);o&&(o.delete(t),0===o.size&&this.#t.delete(e))}async possibleBreakpoints(e,t){const o=await this.debuggerWorkspaceBinding.uiLocationRangeToRawLocationRanges(e,t),i=(await Promise.all(o.map((({start:e,end:t})=>e.debuggerModel.getPossibleBreakpoints(e,t,!1))))).flat(),r=new Map;return await Promise.all(i.map((async o=>{const i=await this.debuggerWorkspaceBinding.rawLocationToUILocation(o);null!==i&&i.uiSourceCode===e&&t.containsLocation(i.lineNumber,i.columnNumber??0)&&r.set(i.id(),i)}))),[...r.values()]}breakpointLocationsForUISourceCode(e){const t=this.#o.get(e);return t?Array.from(t.values()):[]}#d(e){return this.breakpointLocationsForUISourceCode(e).map((e=>e.breakpoint)).concat(Array.from(this.#t.get(e)??[]))}allBreakpointLocations(){const e=[];for(const t of this.#o.values())e.push(...t.values());return e}removeBreakpoint(e,t){const o=e.breakpointStorageId();t&&this.storage.removeBreakpoint(o),this.#i.delete(o)}uiLocationAdded(e,t){let o=this.#o.get(t.uiSourceCode);o||(o=new Map,this.#o.set(t.uiSourceCode,o));const i=new g(e,t);o.set(t.id(),i),this.dispatchEventToListeners(c.BreakpointAdded,i)}uiLocationRemoved(e,t){const o=this.#o.get(t.uiSourceCode);if(!o)return;const i=o.get(t.id())||null;i&&(o.delete(t.id()),0===o.size&&this.#o.delete(t.uiSourceCode),this.dispatchEventToListeners(c.BreakpointRemoved,i))}supportsConditionalBreakpoints(e){return this.debuggerWorkspaceBinding.supportsConditionalBreakpoints(e)}}var c;!function(e){e.BreakpointAdded="breakpoint-added",e.BreakpointRemoved="breakpoint-removed"}(c||(c={}));class l{breakpointManager;#c=new Set;uiSourceCodes=new Set;#l;#p;isRemoved=!1;#h=null;#g=new Map;constructor(e,t,o,r){this.breakpointManager=e,this.#p=r,this.updateState(o),t?(console.assert(t.contentType().name()===o.resourceTypeName),this.addUISourceCode(t)):this.#b(o),this.breakpointManager.targetManager.observeModels(i.DebuggerModel.DebuggerModel,this)}#b(t){t.resolvedState?this.#h=t.resolvedState.map((e=>({...e,scriptHash:""}))):t.resourceTypeName===e.ResourceType.resourceTypes.Script.name()&&(this.#h=[{url:t.url,lineNumber:t.lineNumber,columnNumber:t.columnNumber,scriptHash:"",condition:this.backendCondition()}])}getLastResolvedState(){return this.#h}updateLastResolvedState(e){let t;this.#h=e,e&&(t=e.map((e=>({url:e.url,lineNumber:e.lineNumber,columnNumber:e.columnNumber,condition:e.condition})))),function(e,t){if(e===t)return!0;if(!e||!t||e.length!==t.length)return!1;for(let o=0;o<e.length;o++){const i=e[o],r=t[o];if(i.url!==r.url||i.lineNumber!==r.lineNumber||i.columnNumber!==r.columnNumber||i.condition!==r.condition)return!1}return!0}(this.#l.resolvedState,t)||(this.#l={...this.#l,resolvedState:t},this.breakpointManager.storage.updateBreakpoint(this.#l))}get origin(){return this.#p}async refreshInDebugger(){if(!this.isRemoved){const e=Array.from(this.#g.values());await Promise.all(e.map((async e=>(await e.resetBreakpoint(),this.#m(e)))))}}modelAdded(e){const t=this.breakpointManager.debuggerWorkspaceBinding,o=new p(e,this,t);this.#g.set(e,o),this.#m(o),e.addEventListener(i.DebuggerModel.Events.DebuggerWasEnabled,this.#k,this),e.addEventListener(i.DebuggerModel.Events.DebuggerWasDisabled,this.#S,this),e.addEventListener(i.DebuggerModel.Events.ScriptSourceWasEdited,this.#v,this)}modelRemoved(e){const t=this.#g.get(e);t?.cleanUpAfterDebuggerIsGone(),this.#g.delete(e),this.#f(e)}#f(e){e.removeEventListener(i.DebuggerModel.Events.DebuggerWasEnabled,this.#k,this),e.removeEventListener(i.DebuggerModel.Events.DebuggerWasDisabled,this.#S,this),e.removeEventListener(i.DebuggerModel.Events.ScriptSourceWasEdited,this.#v,this)}#k(e){const t=e.data,o=this.#g.get(t);o&&this.#m(o)}#S(e){const t=e.data,o=this.#g.get(t);o?.cleanUpAfterDebuggerIsGone()}async#v(e){const{source:t,data:{script:o,status:r}}=e;if("Ok"!==r)return;console.assert(t instanceof i.DebuggerModel.DebuggerModel);const s=this.#g.get(t);s?.wasSetIn(o.scriptId)&&(await s.resetBreakpoint(),this.#m(s))}modelBreakpoint(e){return this.#g.get(e)}addUISourceCode(e){this.uiSourceCodes.has(e)||(this.uiSourceCodes.add(e),this.breakpointManager.addHomeUISourceCode(e,this),this.bound()||this.breakpointManager.uiLocationAdded(this,this.defaultUILocation(e)))}clearUISourceCodes(){this.bound()||this.removeAllUnboundLocations();for(const e of this.uiSourceCodes)this.removeUISourceCode(e)}removeUISourceCode(e){if(this.uiSourceCodes.has(e)&&(this.uiSourceCodes.delete(e),this.breakpointManager.removeHomeUISourceCode(e,this),this.bound()||this.breakpointManager.uiLocationRemoved(this,this.defaultUILocation(e))),this.bound()){for(const t of this.#c)t.uiSourceCode===e&&(this.#c.delete(t),this.breakpointManager.uiLocationRemoved(this,t));this.bound()||this.isRemoved||this.addAllUnboundLocations()}}url(){return this.#l.url}lineNumber(){return this.#l.lineNumber}columnNumber(){return this.#l.columnNumber}uiLocationAdded(e){this.isRemoved||(this.bound()||this.removeAllUnboundLocations(),this.#c.add(e),this.breakpointManager.uiLocationAdded(this,e))}uiLocationRemoved(e){this.#c.has(e)&&(this.#c.delete(e),this.breakpointManager.uiLocationRemoved(this,e),this.bound()||this.isRemoved||this.addAllUnboundLocations())}enabled(){return this.#l.enabled}bound(){return 0!==this.#c.size}hasBoundScript(){for(const e of this.uiSourceCodes)if(e.project().type()===a.Workspace.projectTypes.Network)return!0;return!1}setEnabled(e){this.updateState({...this.#l,enabled:e})}condition(){return this.#l.condition}backendCondition(e){const t=this.condition();if(""===t)return"";const o=e=>{let t=i.DebuggerModel.COND_BREAKPOINT_SOURCE_URL;return this.isLogpoint()&&(e=`${b}${e}${m}`,t=i.DebuggerModel.LOGPOINT_SOURCE_URL),`${e}\n\n//# sourceURL=${t}`};return e?n.NamesResolver.allVariablesAtPosition(e).then((e=>e.size>0?s.FormatterWorkerPool.formatterWorkerPool().javaScriptSubstitute(t,e):t)).then((e=>o(e)),(()=>o(t))):o(t)}setCondition(e,t){this.updateState({...this.#l,condition:e,isLogpoint:t})}isLogpoint(){return this.#l.isLogpoint}get storageState(){return this.#l}updateState(e){if(this.#l&&(this.#l.url!==e.url||this.#l.lineNumber!==e.lineNumber||this.#l.columnNumber!==e.columnNumber))throw new Error("Invalid breakpoint state update");this.#l?.enabled===e.enabled&&this.#l?.condition===e.condition&&this.#l?.isLogpoint===e.isLogpoint||(this.#l=e,this.breakpointManager.storage.updateBreakpoint(this.#l),this.updateBreakpoint())}async updateBreakpoint(){return this.bound()||(this.removeAllUnboundLocations(),this.isRemoved||this.addAllUnboundLocations()),this.#L()}async remove(e){if(this.getIsRemoved())return;this.isRemoved=!0;const t=!e;for(const e of this.#g.keys())this.#f(e);await this.#L(),this.breakpointManager.removeBreakpoint(this,t),this.breakpointManager.targetManager.unobserveModels(i.DebuggerModel.DebuggerModel,this),this.clearUISourceCodes()}breakpointStorageId(){return h.computeId(this.#l)}defaultUILocation(e){return d.uiLocationFromBreakpointLocation(e,this.#l.lineNumber,this.#l.columnNumber)}removeAllUnboundLocations(){for(const e of this.uiSourceCodes)this.breakpointManager.uiLocationRemoved(this,this.defaultUILocation(e))}addAllUnboundLocations(){for(const e of this.uiSourceCodes)this.breakpointManager.uiLocationAdded(this,this.defaultUILocation(e))}getUiSourceCodes(){return this.uiSourceCodes}getIsRemoved(){return this.isRemoved}async#L(){await Promise.all(Array.from(this.#g.values()).map((e=>this.#m(e))))}async#m(e){const t=await e.scheduleUpdateInDebugger();"ERROR_BACKEND"===t?await this.remove(!0):"ERROR_BREAKPOINT_CLASH"===t&&await this.remove(!1)}}class p{#I;#B;#C;#R=new r.LiveLocation.LiveLocationPool;#c=new Map;#U=new e.Mutex.Mutex;#M=!1;#N=null;#w=[];#E=new Set;constructor(e,t,o){this.#I=e,this.#B=t,this.#C=o}get currentState(){return this.#N}resetLocations(){for(const e of this.#c.values())this.#B.uiLocationRemoved(e);this.#c.clear(),this.#R.disposeAll(),this.#E.clear()}async scheduleUpdateInDebugger(){if(!this.#I.debuggerEnabled())return"OK";const e=await this.#U.acquire();let t="PENDING";for(;"PENDING"===t;)t=await this.#D();return e(),t}scriptDiverged(){for(const e of this.#B.getUiSourceCodes()){const t=this.#C.scriptFile(e,this.#I);if(t&&t.hasDivergedFromVM())return!0}return!1}async#D(){if(this.#I.target().isDisposed())return this.cleanUpAfterDebuggerIsGone(),"OK";const e=this.#B.lineNumber(),t=this.#B.columnNumber(),i=this.#B.backendCondition();let s=null;if(!this.#B.getIsRemoved()&&this.#B.enabled()&&!this.scriptDiverged()){let n=[];for(const o of this.#B.getUiSourceCodes()){const{lineNumber:i,columnNumber:s}=d.uiLocationFromBreakpointLocation(o,e,t);if(n=(await r.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().uiLocationToRawLocations(o,i,s)).filter((e=>e.debuggerModel===this.#I)),n.length)break}if(n.length&&n.every((e=>e.script()))){const e=await Promise.all(n.map((async e=>{const t=e.script(),o=await this.#B.backendCondition(e);return{url:t.sourceURL,scriptHash:t.hash,lineNumber:e.lineNumber,columnNumber:e.columnNumber,condition:o}})));s=e.slice(0)}else if(!o.Runtime.experiments.isEnabled("instrumentation-breakpoints")){const o=this.#B.getLastResolvedState();if(o)s=o.map((e=>({...e,condition:i})));else{s=[{url:this.#B.url(),scriptHash:"",lineNumber:e,columnNumber:t,condition:i}]}}}const n=this.#w.length;if(n&&l.State.subset(s,this.#N))return"OK";if(this.#B.updateLastResolvedState(s),n)return await this.resetBreakpoint(),"PENDING";if(!s)return"OK";const{breakpointIds:a,locations:u,serverError:c}=await this.#y(s),p=c&&this.#I.debuggerEnabled()&&!this.#I.isReadyToPause();if(!a.length&&p)return"PENDING";if(this.#N=s,this.#M)return this.#M=!1,"OK";if(!a.length)return"ERROR_BACKEND";this.#w=a,this.#w.forEach((e=>this.#I.addBreakpointListener(e,this.breakpointResolved,this)));return(await Promise.all(u.map((e=>this.addResolvedLocation(e))))).includes("ERROR")?"ERROR_BREAKPOINT_CLASH":"OK"}async#y(e){const t=await Promise.all(e.map((e=>e.url?this.#I.setBreakpointByURL(e.url,e.lineNumber,e.columnNumber,e.condition):this.#I.setBreakpointInAnonymousScript(e.scriptHash,e.lineNumber,e.columnNumber,e.condition)))),o=[];let i=[],r=!1;for(const e of t)e.breakpointId?(o.push(e.breakpointId),i=i.concat(e.locations)):r=!0;return{breakpointIds:o,locations:i,serverError:r}}async resetBreakpoint(){this.#w.length&&(this.resetLocations(),await Promise.all(this.#w.map((e=>this.#I.removeBreakpoint(e)))),this.didRemoveFromDebugger(),this.#N=null)}didRemoveFromDebugger(){this.#M?this.#M=!1:(this.resetLocations(),this.#w.forEach((e=>this.#I.removeBreakpointListener(e,this.breakpointResolved,this))),this.#w=[])}async breakpointResolved({data:e}){"ERROR"===await this.addResolvedLocation(e)&&await this.#B.remove(!1)}async locationUpdated(e){const t=this.#c.get(e),o=await e.uiLocation();t&&this.#B.uiLocationRemoved(t),o?(this.#c.set(e,o),this.#B.uiLocationAdded(o)):this.#c.delete(e)}async addResolvedLocation(e){this.#E.add(e.scriptId);const t=await this.#C.rawLocationToUILocation(e);if(!t)return"OK";const o=this.#B.breakpointManager.findBreakpoint(t);return o&&o.breakpoint!==this.#B?"ERROR":(await this.#C.createLiveLocation(e,this.locationUpdated.bind(this),this.#R),"OK")}cleanUpAfterDebuggerIsGone(){this.#M=!0,this.resetLocations(),this.#N=null,this.#w.length&&this.didRemoveFromDebugger()}wasSetIn(e){return this.#E.has(e)}}!function(e){let t;!function(e){e.subset=function(e,t){if(e===t)return!0;if(!e||!t)return!1;if(0===e.length)return!1;for(const o of e)if(void 0===t.find((e=>o.url===e.url&&o.scriptHash===e.scriptHash&&o.lineNumber===e.lineNumber&&o.columnNumber===e.columnNumber&&o.condition===e.condition)))return!1;return!0}}(t=e.State||(e.State={}))}(l||(l={}));class h{setting;breakpoints;#F;constructor(){this.setting=e.Settings.Settings.instance().createLocalSetting("breakpoints",[]),this.breakpoints=new Map,this.#F=!1;for(const e of this.setting.get())this.breakpoints.set(h.computeId(e),e)}mute(){this.#F=!0}unmute(){this.#F=!1}breakpointItems(e,t){const o=[];for(const i of this.breakpoints.values())i.url===e&&(i.resourceTypeName!==t&&void 0!==t||o.push(i));return o}updateBreakpoint(e){if(this.#F)return;const t=h.computeId(e);t&&(this.breakpoints.delete(t),this.breakpoints.set(t,e),this.save())}removeBreakpoint(e){this.#F||(this.breakpoints.delete(e),this.save())}save(){this.setting.set(Array.from(this.breakpoints.values()))}static computeId({url:e,resourceTypeName:t,lineNumber:o,columnNumber:i}){if(!e)return"";let r=`${e}:${t}:${o}`;return void 0!==i&&(r+=`:${i}`),r}}class g{breakpoint;uiLocation;constructor(e,t){this.breakpoint=e,this.uiLocation=t}}const b="/** DEVTOOLS_LOGPOINT */ console.log(",m=")";var k=Object.freeze({__proto__:null,BreakpointManager:d,get Events(){return c},get Breakpoint(){return l},ModelBreakpoint:p,EMPTY_BREAKPOINT_CONDITION:"",NEVER_PAUSE_HERE_CONDITION:"false",BreakpointLocation:g});export{k as BreakpointManager};