chrome-devtools-frontend 1.0.1706026 → 1.0.1709383
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/code-review/SKILL.md +121 -0
- package/AUTHORS +2 -0
- package/front_end/core/common/Color.ts +6 -0
- package/front_end/core/common/VersionController.ts +0 -53
- package/front_end/core/root/Runtime.ts +0 -41
- package/front_end/core/sdk/CSSModel.ts +57 -0
- package/front_end/core/sdk/CSSPropertyParser.ts +10 -17
- package/front_end/core/sdk/DOMModel.ts +13 -6
- package/front_end/core/sdk/DebuggerModel.ts +90 -56
- package/front_end/core/sdk/NetworkRequest.ts +70 -12
- package/front_end/core/sdk/OverlayModel.ts +47 -0
- package/front_end/core/sdk/PageResourceLoader.ts +26 -6
- package/front_end/core/sdk/SourceMap.ts +37 -9
- package/front_end/core/sdk/SourceMapScopesInfo.ts +223 -86
- package/front_end/core/text_utils/ContentData.ts +18 -1
- package/front_end/core/text_utils/TextUtils.ts +1 -1
- package/front_end/entrypoints/devtools_app/devtools_app.ts +1 -0
- package/front_end/entrypoints/heap_snapshot_worker/ContextAnalyzer.ts +102 -4
- package/front_end/entrypoints/main/MainImpl.ts +2 -21
- package/front_end/generated/InspectorBackendCommands.ts +9 -2
- package/front_end/generated/SupportedCSSProperties.ts +12 -3
- package/front_end/generated/protocol-mapping.d.ts +31 -0
- package/front_end/generated/protocol-proxy-api.d.ts +32 -0
- package/front_end/generated/protocol.ts +19 -3
- package/front_end/models/ai_assistance/AiAgent2.ts +127 -2
- package/front_end/models/ai_assistance/AiConversation.ts +2 -2
- package/front_end/models/ai_assistance/AiUtils.ts +4 -0
- package/front_end/models/ai_assistance/agents/AiAgent.ts +64 -13
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +2 -2
- package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +4 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +257 -2
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -1
- package/front_end/models/ai_assistance/ai_assistance.ts +2 -2
- package/front_end/models/ai_assistance/contexts/{AccessibilityContext.snapshot.txt → LighthouseContext.snapshot.txt} +23 -2
- package/front_end/models/ai_assistance/contexts/{AccessibilityContext.ts → LighthouseContext.ts} +24 -5
- package/front_end/models/ai_assistance/data_formatters/LighthouseFormatter.ts +26 -3
- package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +15 -16
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +8 -18
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +2 -2
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +81 -103
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +202 -15
- package/front_end/models/ai_assistance/skills/Skill.ts +1 -2
- package/front_end/models/ai_assistance/skills/SkillRegistry.ts +2 -0
- package/front_end/models/ai_assistance/skills/lighthouse.md +33 -0
- package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +6 -0
- package/front_end/models/ai_assistance/tools/GetCookieValues.ts +3 -0
- package/front_end/models/ai_assistance/tools/GetDetailedCallTree.ts +2 -0
- package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +2 -0
- package/front_end/models/ai_assistance/tools/GetInsightDetails.ts +2 -0
- package/front_end/models/ai_assistance/tools/GetLighthouseAudits.ts +8 -7
- package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +2 -0
- package/front_end/models/ai_assistance/tools/GetSourceContent.ts +2 -0
- package/front_end/models/ai_assistance/tools/GetStorageBreakdown.ts +2 -0
- package/front_end/models/ai_assistance/tools/GetStorageValues.ts +3 -0
- package/front_end/models/ai_assistance/tools/GetStyles.ts +2 -0
- package/front_end/models/ai_assistance/tools/GetTraceEventByKey.ts +3 -1
- package/front_end/models/ai_assistance/tools/GetTraceFunctionCode.ts +2 -0
- package/front_end/models/ai_assistance/tools/GetTraceMainThreadSummary.ts +2 -0
- package/front_end/models/ai_assistance/tools/GetTraceNetworkSummary.ts +2 -0
- package/front_end/models/ai_assistance/tools/GetTraceResourceContent.ts +2 -0
- package/front_end/models/ai_assistance/tools/ListCookies.ts +2 -0
- package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +2 -0
- package/front_end/models/ai_assistance/tools/ListPageOrigins.ts +2 -0
- package/front_end/models/ai_assistance/tools/ListSources.ts +2 -0
- package/front_end/models/ai_assistance/tools/ListStorageKeys.ts +2 -0
- package/front_end/models/ai_assistance/tools/RecordPerformanceTrace.ts +2 -0
- package/front_end/models/ai_assistance/tools/ResolveDevtoolsNodePath.ts +2 -0
- package/front_end/models/ai_assistance/tools/RunLighthouse.ts +20 -12
- package/front_end/models/ai_assistance/tools/SelectTraceEventByKey.ts +2 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +53 -0
- package/front_end/models/bindings/CompilerScriptMapping.ts +102 -45
- package/front_end/models/bindings/DebuggerLanguagePlugins.ts +25 -33
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +70 -49
- package/front_end/models/bindings/SourceMapStepping.ts +50 -0
- package/front_end/models/bindings/StylesSourceMapping.ts +3 -0
- package/front_end/models/bindings/bindings.ts +2 -0
- package/front_end/models/emulation/DeviceModeModel.ts +85 -62
- package/front_end/models/extensions/ExtensionEndpoint.ts +27 -6
- package/front_end/models/extensions/LanguageExtensionEndpoint.ts +106 -21
- package/front_end/models/extensions/RecorderExtensionEndpoint.ts +4 -2
- package/front_end/models/har/Importer.ts +2 -3
- package/front_end/models/javascript_metadata/NativeFunctions.ts +6 -2
- package/front_end/models/lighthouse/LighthouseReporterTypes.ts +5 -1
- package/front_end/models/persistence/NetworkPersistenceManager.ts +4 -3
- package/front_end/models/source_map_scopes/NamesResolver.ts +30 -13
- package/front_end/models/stack_trace/StackTraceImpl.ts +138 -47
- package/front_end/models/stack_trace/StackTraceModel.ts +117 -84
- package/front_end/models/stack_trace/Trie.ts +27 -0
- package/front_end/models/trace/LanternComputationData.ts +8 -0
- package/front_end/models/trace/Processor.ts +2 -1
- package/front_end/models/trace/lantern/core/NetworkAnalyzer.ts +7 -2
- package/front_end/models/trace/lantern/graph/NetworkNode.ts +14 -0
- package/front_end/models/trace/lantern/graph/PageDependencyGraph.ts +1 -1
- package/front_end/models/trace/lantern/simulation/ConnectionPool.ts +4 -4
- package/front_end/models/trace/lantern/simulation/Simulator.ts +6 -2
- package/front_end/models/trace/lantern/simulation/TCPConnection.ts +12 -12
- package/front_end/models/trace/lantern/types/Lantern.ts +8 -0
- package/front_end/panels/accessibility/ARIAAttributesView.ts +6 -6
- package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +2 -2
- package/front_end/panels/accessibility/AccessibilityNodeView.ts +6 -9
- package/front_end/panels/accessibility/AccessibilitySidebarView.ts +83 -69
- package/front_end/panels/accessibility/AccessibilitySubPane.ts +24 -9
- package/front_end/panels/accessibility/SourceOrderView.ts +7 -3
- package/front_end/panels/accessibility/accessibilitySidebarView.css +5 -3
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +14 -12
- package/front_end/panels/ai_assistance/components/ChatInput.ts +1 -1
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +121 -13
- package/front_end/panels/ai_assistance/components/chatMessage.css +47 -0
- package/front_end/panels/application/BackgroundServiceView.ts +31 -33
- package/front_end/panels/application/IndexedDBModel.ts +8 -20
- package/front_end/panels/application/IndexedDBViews.ts +2 -6
- package/front_end/panels/application/ServiceWorkerUpdateCycleView.ts +8 -8
- package/front_end/panels/application/ServiceWorkersView.ts +44 -52
- package/front_end/panels/application/WebMCPView.ts +50 -50
- package/front_end/panels/application/serviceWorkersView.css +1 -1
- package/front_end/panels/comments/CommentsPane.ts +266 -0
- package/front_end/panels/comments/comments-meta.ts +47 -0
- package/front_end/panels/comments/comments.ts +9 -0
- package/front_end/panels/comments/commentsPane.css +206 -0
- package/front_end/panels/common/CommentThreadWidget.ts +30 -1
- package/front_end/panels/common/CommentsOverlayWidget.ts +42 -38
- package/front_end/panels/common/CommentsStatusBarPill.ts +3 -1
- package/front_end/panels/common/common-meta.ts +21 -9
- package/front_end/panels/console/ConsoleInsightTeaser.ts +25 -19
- package/front_end/panels/console/ConsoleViewMessage.ts +52 -23
- package/front_end/panels/console/ConsoleViewport.ts +1 -1
- package/front_end/panels/console/SymbolizedErrorWidget.ts +18 -9
- package/front_end/panels/console/console-meta.ts +1 -5
- package/front_end/panels/elements/CSSValueTraceView.ts +4 -4
- package/front_end/panels/elements/ComputedStyleWidget.ts +46 -42
- package/front_end/panels/elements/DOMPath.ts +11 -1
- package/front_end/panels/elements/DOMTreeContextMenu.ts +15 -0
- package/front_end/panels/elements/DOMTreeWidget.ts +237 -606
- package/front_end/panels/elements/ElementsPanel.ts +15 -16
- package/front_end/panels/elements/ElementsTreeElement.ts +24 -18
- package/front_end/panels/elements/EventListenersWidget.ts +5 -3
- package/front_end/panels/elements/InspectElementModeController.ts +6 -3
- package/front_end/panels/elements/LayoutPane.ts +3 -3
- package/front_end/panels/elements/PropertyRenderer.ts +132 -87
- package/front_end/panels/elements/StandaloneStylesContainer.ts +7 -3
- package/front_end/panels/elements/StylePropertiesSection.ts +49 -4
- package/front_end/panels/elements/StylePropertyTreeElement.ts +139 -36
- package/front_end/panels/elements/StylesContainer.ts +2 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +75 -16
- package/front_end/panels/elements/components/CSSPropertyDocsView.ts +2 -1
- package/front_end/panels/elements/elements-meta.ts +24 -75
- package/front_end/panels/emulation/DeviceModeToolbar.ts +5 -2
- package/front_end/panels/emulation/DeviceModeView.ts +5 -2
- package/front_end/panels/emulation/emulation-meta.ts +3 -8
- package/front_end/panels/layer_viewer/Layers3DView.ts +8 -3
- package/front_end/panels/layer_viewer/layer_viewer-meta.ts +2 -5
- package/front_end/panels/lighthouse/LighthouseController.ts +9 -4
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +13 -3
- package/front_end/panels/network/NetworkConfigView.ts +261 -190
- package/front_end/panels/network/NetworkLogView.ts +7 -5
- package/front_end/panels/network/NetworkPanel.ts +4 -3
- package/front_end/panels/network/NetworkWaterfallColumn.ts +7 -4
- package/front_end/panels/network/RequestPayloadView.ts +22 -14
- package/front_end/panels/network/network-meta.ts +23 -34
- package/front_end/panels/profiler/HeapDetachedElementsDataGrid.ts +0 -1
- package/front_end/panels/profiler/HeapProfileView.ts +20 -24
- package/front_end/panels/profiler/ProfilesPanel.ts +8 -3
- package/front_end/panels/recorder/RecordingView.ts +42 -47
- package/front_end/panels/recorder/StepEditor.ts +1 -0
- package/front_end/panels/recorder/recordingView.css +4 -4
- package/front_end/panels/screencast/ScreencastView.ts +6 -4
- package/front_end/panels/sources/CSSPlugin.ts +11 -0
- package/front_end/panels/sources/CoveragePlugin.ts +34 -23
- package/front_end/panels/sources/NavigatorView.ts +97 -52
- package/front_end/panels/sources/Plugin.ts +15 -1
- package/front_end/panels/sources/SourcesView.ts +47 -80
- package/front_end/panels/sources/TabbedEditorContainer.ts +63 -34
- package/front_end/panels/sources/UISourceCodeFrame.ts +11 -2
- package/front_end/panels/sources/navigatorTree.css +2 -2
- package/front_end/panels/timeline/TimelineHistoryManager.ts +2 -4
- package/front_end/panels/timeline/components/LiveMetricsView.ts +7 -20
- package/front_end/panels/timeline/components/MetricCard.ts +262 -315
- package/front_end/panels/timeline/components/liveMetricsView.css +4 -0
- package/front_end/panels/timeline/components/metricCard.css +2 -24
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +33 -17
- package/front_end/panels/timeline/overlays/components/TimeRangeOverlay.ts +182 -130
- package/front_end/panels/timeline/overlays/components/timeRangeOverlay.css +76 -74
- package/front_end/panels/whats_new/WhatsNewImpl.ts +2 -1
- package/front_end/panels/whats_new/whats_new-meta.ts +2 -5
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/web-vitals/README.chromium +2 -2
- package/front_end/third_party/web-vitals/package/README.md +5 -3
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onINP.js +1 -1
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onLCP.js +8 -5
- package/front_end/third_party/web-vitals/package/package.json +1 -1
- package/front_end/third_party/web-vitals/package/src/attribution/onINP.ts +1 -1
- package/front_end/third_party/web-vitals/package/src/attribution/onLCP.ts +9 -5
- package/front_end/third_party/web-vitals/rebuild.sh +2 -2
- package/front_end/ui/comments/CommentAnchorResolver.ts +17 -0
- package/front_end/ui/components/suggestion_input/SuggestionInput.ts +10 -1
- package/front_end/ui/components/text_editor/AiCodeCompletionTeaser.ts +1 -1
- package/front_end/ui/components/text_editor/AiCodeGenerationTeaser.ts +2 -2
- package/front_end/ui/components/text_editor/aiCodeCompletionTeaser.css +0 -11
- package/front_end/ui/components/text_editor/aiCodeGenerationTeaser.css +0 -11
- package/front_end/ui/components/text_editor/config.ts +2 -1
- package/front_end/ui/legacy/SoftDropDown.ts +17 -4
- package/front_end/ui/legacy/StackPane.ts +141 -0
- package/front_end/ui/legacy/TabbedPane.ts +67 -12
- package/front_end/ui/legacy/TextPrompt.ts +37 -7
- package/front_end/ui/legacy/Toolbar.ts +36 -14
- package/front_end/ui/legacy/Widget.ts +3 -3
- package/front_end/ui/legacy/components/inline_editor/PositionAreaEditor.ts +85 -0
- package/front_end/ui/legacy/components/inline_editor/positionAreaEditor.css +61 -0
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +93 -31
- package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +15 -7
- package/front_end/ui/legacy/components/utils/Linkifier.ts +25 -45
- package/front_end/ui/legacy/inspectorCommon.css +11 -0
- package/front_end/ui/legacy/legacy.ts +2 -0
- package/front_end/ui/legacy/softContextMenu.css +2 -0
- package/front_end/ui/legacy/tabbedPane.css +12 -8
- package/front_end/ui/settings/ConsoleSettings.ts +7 -0
- package/front_end/ui/settings/ElementsSettings.ts +93 -0
- package/front_end/ui/settings/EmulationSettings.ts +17 -0
- package/front_end/ui/settings/LayerViewerSettings.ts +12 -0
- package/front_end/ui/settings/NetworkSettings.ts +26 -0
- package/front_end/ui/settings/WhatsNewSettings.ts +11 -0
- package/front_end/ui/settings/settings.ts +10 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +17 -0
- package/inspector_overlay/highlight_anchor.ts +1 -1
- package/package.json +1 -1
|
@@ -16,13 +16,16 @@ export class SourceMapScopesInfo {
|
|
|
16
16
|
readonly #sourceMap: SourceMap;
|
|
17
17
|
readonly #originalScopes: Array<ScopesCodec.OriginalScope[]|null>;
|
|
18
18
|
readonly #generatedRanges: ScopesCodec.GeneratedRange[];
|
|
19
|
+
/** Whether the scope information was derived from the AST and mappings (see {@link createFromAst}). */
|
|
20
|
+
readonly #isFromAst: boolean;
|
|
19
21
|
|
|
20
22
|
#cachedVariablesAndBindingsPresent: boolean|null = null;
|
|
21
23
|
|
|
22
|
-
constructor(sourceMap: SourceMap, scopeInfo: ScopesCodec.ScopeInfo) {
|
|
24
|
+
constructor(sourceMap: SourceMap, scopeInfo: ScopesCodec.ScopeInfo, {isFromAst = false}: {isFromAst?: boolean} = {}) {
|
|
23
25
|
this.#sourceMap = sourceMap;
|
|
24
26
|
this.#originalScopes = scopeInfo.scopes;
|
|
25
27
|
this.#generatedRanges = scopeInfo.ranges;
|
|
28
|
+
this.#isFromAst = isFromAst;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
/**
|
|
@@ -114,7 +117,8 @@ export class SourceMapScopesInfo {
|
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
return new SourceMapScopesInfo(sourceMap, {scopes: scopesBySourceUrl, ranges: rootRange ? [rootRange] : []}
|
|
120
|
+
return new SourceMapScopesInfo(sourceMap, {scopes: scopesBySourceUrl, ranges: rootRange ? [rootRange] : []},
|
|
121
|
+
{isFromAst: true});
|
|
118
122
|
|
|
119
123
|
/**
|
|
120
124
|
* Finds the correct place in the tree to insert the new scope.
|
|
@@ -211,6 +215,10 @@ export class SourceMapScopesInfo {
|
|
|
211
215
|
return noScopes && !this.#generatedRanges.length;
|
|
212
216
|
}
|
|
213
217
|
|
|
218
|
+
hasGeneratedRanges(): boolean {
|
|
219
|
+
return this.#generatedRanges.length > 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
214
222
|
addOriginalScopesAtIndex(sourceIdx: number, scopes: ScopesCodec.OriginalScope[]): void {
|
|
215
223
|
if (!this.#originalScopes[sourceIdx]?.length) {
|
|
216
224
|
this.#originalScopes[sourceIdx] = scopes;
|
|
@@ -219,39 +227,17 @@ export class SourceMapScopesInfo {
|
|
|
219
227
|
}
|
|
220
228
|
}
|
|
221
229
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return this.#isOutlinedFrame(rangeChain);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
#isOutlinedFrame(rangeChain: ScopesCodec.GeneratedRange[]): boolean {
|
|
231
|
-
for (let i = rangeChain.length - 1; i >= 0; --i) {
|
|
232
|
-
if (rangeChain[i].isStackFrame) {
|
|
233
|
-
return rangeChain[i].isHidden;
|
|
234
|
-
}
|
|
230
|
+
#generatedFrameKind(rangeChain: ScopesCodec.GeneratedRange[]): GeneratedFrameKind {
|
|
231
|
+
const functionRange = rangeChain.findLast(range => range.isStackFrame);
|
|
232
|
+
if (!functionRange) {
|
|
233
|
+
// Top-level code.
|
|
234
|
+
return GeneratedFrameKind.VISIBLE;
|
|
235
235
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* @returns true, iff the range surrounding the provided position contains multiple
|
|
241
|
-
* inlined original functions.
|
|
242
|
-
*/
|
|
243
|
-
hasInlinedFrames(generatedLine: number, generatedColumn: number): boolean {
|
|
244
|
-
const rangeChain = this.#findGeneratedRangeChain(generatedLine, generatedColumn);
|
|
245
|
-
for (let i = rangeChain.length - 1; i >= 0; --i) {
|
|
246
|
-
if (rangeChain[i].isStackFrame) {
|
|
247
|
-
// We stop looking for inlined original functions once we reach the current frame.
|
|
248
|
-
return false;
|
|
249
|
-
}
|
|
250
|
-
if (rangeChain[i].callSite) {
|
|
251
|
-
return true;
|
|
252
|
-
}
|
|
236
|
+
if (!functionRange.originalScope) {
|
|
237
|
+
// For scope information derived from the AST, we merely failed to map the function.
|
|
238
|
+
return this.#isFromAst ? GeneratedFrameKind.VISIBLE : GeneratedFrameKind.HIDDEN;
|
|
253
239
|
}
|
|
254
|
-
return
|
|
240
|
+
return functionRange.isHidden ? GeneratedFrameKind.OUTLINED : GeneratedFrameKind.VISIBLE;
|
|
255
241
|
}
|
|
256
242
|
|
|
257
243
|
/**
|
|
@@ -419,54 +405,53 @@ export class SourceMapScopesInfo {
|
|
|
419
405
|
/**
|
|
420
406
|
* Returns the authored function scope of the function containing the provided generated position.
|
|
421
407
|
*/
|
|
422
|
-
findOriginalFunctionScope(
|
|
408
|
+
findOriginalFunctionScope(position: ScopesCodec.Position):
|
|
423
409
|
{scope: ScopesCodec.OriginalScope, url?: Platform.DevToolsPath.UrlString}|null {
|
|
410
|
+
const rangeChain = this.#findGeneratedRangeChain(position.line, position.column);
|
|
411
|
+
const functionScope =
|
|
412
|
+
this.#findFunctionScopeInOriginalScopeChain(this.#innerMostOriginalScope(rangeChain, position));
|
|
413
|
+
return functionScope ? {scope: functionScope, url: this.#sourceURLOfScope(functionScope)} : null;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Returns the inner-most original scope containing the generated `position`. `rangeChain` must be the generated
|
|
418
|
+
* range chain of `position`.
|
|
419
|
+
*/
|
|
420
|
+
#innerMostOriginalScope(rangeChain: ScopesCodec.GeneratedRange[],
|
|
421
|
+
position: ScopesCodec.Position): ScopesCodec.OriginalScope|undefined {
|
|
424
422
|
// There are 2 approaches:
|
|
425
423
|
// 1) Find the inner-most generated range containing the provided generated position
|
|
426
|
-
// and use
|
|
424
|
+
// and use its OriginalScope.
|
|
427
425
|
// 2) Use the mappings to turn the generated position into an original position.
|
|
428
426
|
// Then find the inner-most original scope containing that original position.
|
|
429
|
-
// Then walk it outwards until we hit a function.
|
|
430
427
|
//
|
|
431
428
|
// Both approaches should yield the same result (assuming the mappings are spec compliant
|
|
432
429
|
// w.r.t. generated ranges). But in the case of "pasta" scopes and extension provided
|
|
433
430
|
// scope info, we only have the OriginalScope parts and mappings without GeneratedRanges.
|
|
434
|
-
|
|
435
|
-
let originalInnerMostScope: ScopesCodec.OriginalScope|undefined;
|
|
436
|
-
|
|
437
431
|
if (this.#generatedRanges.length > 0) {
|
|
438
|
-
|
|
439
|
-
originalInnerMostScope = rangeChain.at(-1)?.originalScope;
|
|
440
|
-
} else {
|
|
441
|
-
// No GeneratedRanges. Try to use mappings.
|
|
442
|
-
const entry = this.#sourceMap.findEntry(line, column);
|
|
443
|
-
if (entry?.sourceIndex === undefined) {
|
|
444
|
-
return null;
|
|
445
|
-
}
|
|
446
|
-
originalInnerMostScope =
|
|
447
|
-
this.#findOriginalScopeChain(
|
|
448
|
-
{sourceIndex: entry.sourceIndex, line: entry.sourceLineNumber, column: entry.sourceColumnNumber})
|
|
449
|
-
.at(-1);
|
|
432
|
+
return rangeChain.at(-1)?.originalScope;
|
|
450
433
|
}
|
|
451
434
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const functionScope = this.#findFunctionScopeInOriginalScopeChain(originalInnerMostScope);
|
|
457
|
-
if (!functionScope) {
|
|
458
|
-
return null;
|
|
435
|
+
// No GeneratedRanges. Try to use mappings.
|
|
436
|
+
const entry = this.#sourceMap.findEntry(position.line, position.column);
|
|
437
|
+
if (entry?.sourceIndex === undefined) {
|
|
438
|
+
return undefined;
|
|
459
439
|
}
|
|
440
|
+
return this
|
|
441
|
+
.#findOriginalScopeChain(
|
|
442
|
+
{sourceIndex: entry.sourceIndex, line: entry.sourceLineNumber, column: entry.sourceColumnNumber})
|
|
443
|
+
.at(-1);
|
|
444
|
+
}
|
|
460
445
|
|
|
446
|
+
/** @returns the URL of the original source that `scope` belongs to. */
|
|
447
|
+
#sourceURLOfScope(scope: ScopesCodec.OriginalScope): Platform.DevToolsPath.UrlString|undefined {
|
|
461
448
|
// Find the root scope for some given original source, to get the source url.
|
|
462
|
-
let rootScope: ScopesCodec.OriginalScope =
|
|
449
|
+
let rootScope: ScopesCodec.OriginalScope = scope;
|
|
463
450
|
while (rootScope.parent) {
|
|
464
451
|
rootScope = rootScope.parent;
|
|
465
452
|
}
|
|
466
453
|
const sourceIndex = this.#originalScopes.findIndex(scopes => scopes?.includes(rootScope));
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return functionScope ? {scope: functionScope, url} : null;
|
|
454
|
+
return sourceIndex !== -1 ? this.#sourceMap.sourceURLForSourceIndex(sourceIndex) : undefined;
|
|
470
455
|
}
|
|
471
456
|
|
|
472
457
|
/**
|
|
@@ -513,33 +498,54 @@ export class SourceMapScopesInfo {
|
|
|
513
498
|
}
|
|
514
499
|
|
|
515
500
|
/**
|
|
516
|
-
*
|
|
517
|
-
*
|
|
518
|
-
* @returns An empty array if no mapping at the call-site was found, or the resulting frames
|
|
519
|
-
* in top-to-bottom order in case of inlining.
|
|
520
|
-
* @throws If this range is marked "hidden". Outlining needs to be handled externally as
|
|
521
|
-
* outlined function segments in stack traces can span across bundles.
|
|
501
|
+
* Translates a single "raw frame" or call-site, including outlined functions. It's the caller's responsibility to
|
|
502
|
+
* merge outlined frames with their caller(s) (see {@link GeneratedFrameKind}).
|
|
522
503
|
*/
|
|
523
|
-
|
|
504
|
+
translateRawFrame(generatedLine: number, generatedColumn: number): RawFrameTranslation {
|
|
524
505
|
const rangeChain = this.#findGeneratedRangeChain(generatedLine, generatedColumn);
|
|
525
|
-
|
|
526
|
-
|
|
506
|
+
const kind = this.#generatedFrameKind(rangeChain);
|
|
507
|
+
if (kind === GeneratedFrameKind.HIDDEN) {
|
|
508
|
+
return {kind, frames: []};
|
|
527
509
|
}
|
|
510
|
+
const frame = this.#translateTopFrame(generatedLine, generatedColumn, rangeChain);
|
|
511
|
+
return {kind, frames: frame ? [frame, ...this.#translateInlinedCallers(rangeChain)] : []};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* The top-most frame is translated the same, regardless of whether we have inlined functions: The name is the
|
|
516
|
+
* original function surrounding the generated position, and the location is the mapped generated position.
|
|
517
|
+
*
|
|
518
|
+
* If the generated position has no mapping, the generated ranges may still tell which authored function (or
|
|
519
|
+
* which file, for top-level code) the position belongs to. The frame then has no position.
|
|
520
|
+
*/
|
|
521
|
+
#translateTopFrame(generatedLine: number, generatedColumn: number,
|
|
522
|
+
rangeChain: ScopesCodec.GeneratedRange[]): TranslatedFrame|null {
|
|
523
|
+
const position = {line: generatedLine, column: generatedColumn};
|
|
524
|
+
const innerMostScope = this.#innerMostOriginalScope(rangeChain, position);
|
|
525
|
+
const functionScope = this.#findFunctionScopeInOriginalScopeChain(innerMostScope);
|
|
526
|
+
const name = functionScope ? (functionScope.name ?? '') : undefined;
|
|
528
527
|
|
|
529
528
|
const mapping = this.#sourceMap.findEntry(generatedLine, generatedColumn);
|
|
530
|
-
if (mapping?.sourceIndex
|
|
531
|
-
return
|
|
529
|
+
if (mapping?.sourceIndex !== undefined) {
|
|
530
|
+
return {
|
|
531
|
+
line: mapping.sourceLineNumber,
|
|
532
|
+
column: mapping.sourceColumnNumber,
|
|
533
|
+
name,
|
|
534
|
+
url: mapping.sourceURL,
|
|
535
|
+
functionStart: functionScope?.start,
|
|
536
|
+
};
|
|
532
537
|
}
|
|
538
|
+
if (!innerMostScope) {
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
return {name, url: this.#sourceURLOfScope(functionScope ?? innerMostScope), functionStart: functionScope?.start};
|
|
542
|
+
}
|
|
533
543
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
url: mapping.sourceURL,
|
|
540
|
-
}];
|
|
541
|
-
|
|
542
|
-
// Walk the range chain inside out until we find a generated function and for each inlined function add a frame.
|
|
544
|
+
/**
|
|
545
|
+
* Walk the range chain inside out until we find a generated function and for each inlined function add a frame.
|
|
546
|
+
*/
|
|
547
|
+
#translateInlinedCallers(rangeChain: ScopesCodec.GeneratedRange[]): TranslatedFrame[] {
|
|
548
|
+
const result: TranslatedFrame[] = [];
|
|
543
549
|
for (let i = rangeChain.length - 1; i >= 0 && !rangeChain[i].isStackFrame; --i) {
|
|
544
550
|
const range = rangeChain[i];
|
|
545
551
|
if (!range.callSite) {
|
|
@@ -547,29 +553,160 @@ export class SourceMapScopesInfo {
|
|
|
547
553
|
}
|
|
548
554
|
|
|
549
555
|
const originalScopeChain = this.#findOriginalScopeChain(range.callSite);
|
|
556
|
+
const functionScope = this.#findFunctionScopeInOriginalScopeChain(originalScopeChain.at(-1));
|
|
550
557
|
result.push({
|
|
551
558
|
line: range.callSite.line,
|
|
552
559
|
column: range.callSite.column,
|
|
553
|
-
name:
|
|
560
|
+
name: functionScope ? (functionScope.name ?? '') : undefined,
|
|
554
561
|
url: this.#sourceMap.sourceURLForSourceIndex(range.callSite.sourceIndex),
|
|
562
|
+
functionStart: functionScope?.start,
|
|
555
563
|
});
|
|
556
564
|
}
|
|
557
565
|
|
|
558
566
|
return result;
|
|
559
567
|
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* @returns the body of the innermost inlined function at the position (the innermost range with a `callSite`
|
|
571
|
+
* within the generated function), or null if the position is not inside an inlined function.
|
|
572
|
+
*
|
|
573
|
+
* The innermost inlined function is the logical frame the position belongs to, i.e. the analogue of the top frame
|
|
574
|
+
* of a real call stack. Stepping over its body therefore goes up exactly one logical frame: into the caller, which
|
|
575
|
+
* may itself be inlined. An outer range would leave several logical frames at once.
|
|
576
|
+
*/
|
|
577
|
+
inlinedFunctionRange(generatedLine: number, generatedColumn: number): PositionRange|null {
|
|
578
|
+
const rangeChain = this.#findGeneratedRangeChain(generatedLine, generatedColumn);
|
|
579
|
+
for (let i = rangeChain.length - 1; i >= 0 && !rangeChain[i].isStackFrame; --i) {
|
|
580
|
+
if (rangeChain[i].callSite) {
|
|
581
|
+
return {start: rangeChain[i].start, end: rangeChain[i].end};
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* @returns the bodies of all functions that were inlined directly into the logical function at the position
|
|
589
|
+
* (the innermost inlined function, or else the generated function). Doesn't descend into inlined
|
|
590
|
+
* functions or nested generated functions.
|
|
591
|
+
*/
|
|
592
|
+
inlinedCalleeRanges(generatedLine: number, generatedColumn: number): PositionRange[] {
|
|
593
|
+
const rangeChain = this.#findGeneratedRangeChain(generatedLine, generatedColumn);
|
|
594
|
+
let body: ScopesCodec.GeneratedRange|undefined;
|
|
595
|
+
for (let i = rangeChain.length - 1; i >= 0 && !body; --i) {
|
|
596
|
+
if (rangeChain[i].callSite || rangeChain[i].isStackFrame) {
|
|
597
|
+
body = rangeChain[i];
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
const result: PositionRange[] = [];
|
|
601
|
+
(function walk(range: ScopesCodec.GeneratedRange) {
|
|
602
|
+
for (const child of range.children) {
|
|
603
|
+
if (child.isStackFrame) {
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (child.callSite) {
|
|
607
|
+
result.push({start: child.start, end: child.end});
|
|
608
|
+
} else {
|
|
609
|
+
walk(child);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
})(body ?? {children: this.#generatedRanges} as ScopesCodec.GeneratedRange);
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* @returns true, iff any generated function is outlined, i.e. marked as "hidden" but with a definition (see
|
|
618
|
+
* {@link GeneratedFrameKind.OUTLINED}). Hidden functions without a definition are compiler helpers.
|
|
619
|
+
*/
|
|
620
|
+
hasOutlinedFunctions(): boolean {
|
|
621
|
+
const hasOutlined = (ranges: ScopesCodec.GeneratedRange[]): boolean =>
|
|
622
|
+
ranges.some(range => (range.isStackFrame && range.isHidden && range.originalScope !== undefined) ||
|
|
623
|
+
hasOutlined(range.children));
|
|
624
|
+
return hasOutlined(this.#generatedRanges);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* @returns the "artificial" generated functions (in the DWARF sense): functions that contain no authored code at all
|
|
629
|
+
* (no original scope anywhere in their subtree), e.g. compiler helpers. Sorted by start position,
|
|
630
|
+
* non-overlapping.
|
|
631
|
+
*/
|
|
632
|
+
artificialFunctionRanges(): PositionRange[] {
|
|
633
|
+
const hasOriginalScope = (range: ScopesCodec.GeneratedRange): boolean =>
|
|
634
|
+
range.originalScope !== undefined || range.children.some(hasOriginalScope);
|
|
635
|
+
const result: PositionRange[] = [];
|
|
636
|
+
(function walk(ranges: ScopesCodec.GeneratedRange[]) {
|
|
637
|
+
for (const range of ranges) {
|
|
638
|
+
if (range.isStackFrame && !hasOriginalScope(range)) {
|
|
639
|
+
result.push({start: range.start, end: range.end});
|
|
640
|
+
} else {
|
|
641
|
+
walk(range.children);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
})(this.#generatedRanges);
|
|
645
|
+
return result;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/** A range of generated positions, relative to the start of the script. `end` is exclusive. */
|
|
650
|
+
export interface PositionRange {
|
|
651
|
+
readonly start: ScopesCodec.Position;
|
|
652
|
+
readonly end: ScopesCodec.Position;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Describes how the generated function surrounding a generated position shows up in stack traces.
|
|
657
|
+
*
|
|
658
|
+
* Compilers tend to introduce functions (and calls to them) that don't exist in the authored code. The scopes
|
|
659
|
+
* proposal distinguishes two cases:
|
|
660
|
+
*
|
|
661
|
+
* 1) The generated function contains authored code, e.g. a block scope that was turned into a function. The
|
|
662
|
+
* generated range is marked "hidden", but links to the original scope via its definition. We can pause
|
|
663
|
+
* in such a function, but the frame is merged with its caller(s) in stack traces: The outlined code
|
|
664
|
+
* logically belongs to the (authored) function that transitively calls it.
|
|
665
|
+
*
|
|
666
|
+
* 2) The generated function doesn't represent any authored code, e.g. a compiler helper. The generated range
|
|
667
|
+
* has no definition. The scopes spec is being updated to say that such a range can be ignored in stack
|
|
668
|
+
* traces, so we drop these frames.
|
|
669
|
+
*/
|
|
670
|
+
export const enum GeneratedFrameKind {
|
|
671
|
+
/** A regular (possibly with inlined functions) generated function, or top-level code. */
|
|
672
|
+
VISIBLE = 'VISIBLE',
|
|
673
|
+
/** A generated function marked as "hidden" that has a definition (case 1). */
|
|
674
|
+
OUTLINED = 'OUTLINED',
|
|
675
|
+
/** A generated function without a definition (case 2). */
|
|
676
|
+
HIDDEN = 'HIDDEN',
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/** See {@link SourceMapScopesInfo.translateRawFrame}. */
|
|
680
|
+
export interface RawFrameTranslation {
|
|
681
|
+
kind: GeneratedFrameKind;
|
|
682
|
+
/**
|
|
683
|
+
* [top, ...inlinedCallers] in top-to-bottom order. Empty for {@link GeneratedFrameKind.HIDDEN} frames, or if
|
|
684
|
+
* neither the mappings nor the generated ranges know anything about the generated position.
|
|
685
|
+
*
|
|
686
|
+
* For {@link GeneratedFrameKind.OUTLINED} frames, the top frame is named after the authored function the outlined
|
|
687
|
+
* code belongs to.
|
|
688
|
+
*/
|
|
689
|
+
frames: TranslatedFrame[];
|
|
560
690
|
}
|
|
561
691
|
|
|
562
692
|
/**
|
|
563
693
|
* Represents a stack frame in original terms. It closely aligns with StackTrace.StackTrace.Frame,
|
|
564
694
|
* but since we can't import that type here we mirror it here somewhat.
|
|
565
695
|
*
|
|
566
|
-
* Equivalent to Pick<StackTrace.StackTrace.Frame, 'line'|'column'|'name'|'url'
|
|
696
|
+
* Equivalent to Pick<StackTrace.StackTrace.Frame, 'line'|'column'|'name'|'url'>, except that the position is
|
|
697
|
+
* optional.
|
|
567
698
|
*/
|
|
568
699
|
export interface TranslatedFrame {
|
|
569
|
-
|
|
570
|
-
|
|
700
|
+
/**
|
|
701
|
+
* `line` and `column` are undefined iff the generated position has no mapping. The frame then only identifies
|
|
702
|
+
* the authored function: `name`, `functionStart` and the `url` of the function's source.
|
|
703
|
+
*/
|
|
704
|
+
line?: number;
|
|
705
|
+
column?: number;
|
|
571
706
|
name?: string;
|
|
572
707
|
url?: Platform.DevToolsPath.UrlString;
|
|
708
|
+
/** Start of the original function scope containing this frame's position. Undefined for top-level code. */
|
|
709
|
+
functionStart?: ScopesCodec.Position;
|
|
573
710
|
}
|
|
574
711
|
|
|
575
712
|
export function findExpression(range: ScopesCodec.GeneratedRange|undefined, index: number, line = 0,
|
|
@@ -19,6 +19,23 @@ const objectUrlRegistry = new FinalizationRegistry<string>(url => {
|
|
|
19
19
|
*/
|
|
20
20
|
const MAX_BLOB_SIZE_BYTES = 10 * 1024 * 1024;
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Returns `charset` if `TextDecoder` supports it, and 'utf-8' otherwise.
|
|
24
|
+
* The charset usually comes straight from a `Content-Type` header, and
|
|
25
|
+
* `TextDecoder` has no non-throwing way to check a label.
|
|
26
|
+
*/
|
|
27
|
+
function supportedCharsetOrUtf8(charset: string|undefined): string {
|
|
28
|
+
if (!charset) {
|
|
29
|
+
return 'utf-8';
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
new TextDecoder(charset);
|
|
33
|
+
return charset;
|
|
34
|
+
} catch {
|
|
35
|
+
return 'utf-8';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
22
39
|
/**
|
|
23
40
|
* This class is a small wrapper around either raw binary or text data.
|
|
24
41
|
* As the binary data can actually contain textual data, we also store the
|
|
@@ -44,7 +61,7 @@ export class ContentData {
|
|
|
44
61
|
#imagePreviewUrl?: string;
|
|
45
62
|
|
|
46
63
|
constructor(data: string, isBase64: boolean, mimeType: string, charset?: string) {
|
|
47
|
-
this.charset = charset
|
|
64
|
+
this.charset = supportedCharsetOrUtf8(charset);
|
|
48
65
|
if (isBase64) {
|
|
49
66
|
this.#contentAsBase64 = data;
|
|
50
67
|
} else {
|
|
@@ -9,7 +9,7 @@ import {SearchMatch} from './ContentProvider.js';
|
|
|
9
9
|
import type {Text} from './Text.js';
|
|
10
10
|
|
|
11
11
|
const KEY_VALUE_FILTER_REGEXP = /(?:^|\s)(\-)?([\w\-]+):([^\s]+)/;
|
|
12
|
-
const REGEXP_FILTER_REGEXP = /(?:^|\s)(\-)?\/([^\/\\]+
|
|
12
|
+
const REGEXP_FILTER_REGEXP = /(?:^|\s)(\-)?\/((?:[^\/\\]|\\.)+)\//;
|
|
13
13
|
const TEXT_FILTER_REGEXP = /(?:^|\s)(\-)?([^\s]+)/;
|
|
14
14
|
const SPACE_CHAR_REGEXP = /\s/;
|
|
15
15
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
import '../shell/shell.js';
|
|
5
|
+
import '../../panels/comments/comments-meta.js';
|
|
5
6
|
import '../../panels/css_overview/css_overview-meta.js';
|
|
6
7
|
import '../../panels/elements/elements-meta.js';
|
|
7
8
|
import '../../panels/browser_debugger/browser_debugger-meta.js';
|
|
@@ -28,6 +28,11 @@ type ScopeInfoNodeIndex = Platform.Brand.Brand<number, 'ScopeInfoNodeIndex'>;
|
|
|
28
28
|
*/
|
|
29
29
|
type ContextNodeIndex = Platform.Brand.Brand<number, 'ContextNodeIndex'>;
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Index of a `system / Map` node within the heap snapshot's node array.
|
|
33
|
+
*/
|
|
34
|
+
type MapNodeIndex = Platform.Brand.Brand<number, 'MapNodeIndex'>;
|
|
35
|
+
|
|
31
36
|
/**
|
|
32
37
|
* Values addressed by scope. Since a `ScopeId` is only unique within its
|
|
33
38
|
* script, scopes are keyed by script first.
|
|
@@ -74,6 +79,8 @@ interface ScopeAccumulator {
|
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
interface LiveClosure {
|
|
82
|
+
// The JSFunction or generator object this entry was created for.
|
|
83
|
+
ownerNodeIndex: number;
|
|
77
84
|
contextNodeIndex: ContextNodeIndex;
|
|
78
85
|
scriptNodeIndex: ScriptNodeIndex;
|
|
79
86
|
scopeId: ScopeId;
|
|
@@ -99,6 +106,8 @@ interface HeapScan {
|
|
|
99
106
|
scripts: Map<ScriptNodeIndex, ScriptInfo>;
|
|
100
107
|
contextNodes: ContextNodeInfo[];
|
|
101
108
|
liveClosures: LiveClosure[];
|
|
109
|
+
// Top-level functions of modules that finished evaluating.
|
|
110
|
+
finishedModuleFunctionNodeIndexes: Set<number>;
|
|
102
111
|
// Maps a ScopeInfo to the script it belongs to. A stored `undefined` is a memoized
|
|
103
112
|
// failed lookup. It is therefore not the same as an absent entry, which merely means
|
|
104
113
|
// "not resolved yet".
|
|
@@ -114,7 +123,7 @@ export function analyzeContexts(snapshot: HeapSnapshot): HeapSnapshotModel.HeapS
|
|
|
114
123
|
const scan = scanHeap(snapshot);
|
|
115
124
|
|
|
116
125
|
// (3) Group live closures by function scope and record the context chains they can reach.
|
|
117
|
-
const liveFunctionsByScript = buildLiveFunctions(snapshot, scan.liveClosures);
|
|
126
|
+
const liveFunctionsByScript = buildLiveFunctions(snapshot, scan.liveClosures, scan.finishedModuleFunctionNodeIndexes);
|
|
118
127
|
|
|
119
128
|
// (4) Correlate contexts and their field values with embedded scopes.
|
|
120
129
|
const {scopes, scriptsWithoutScopes} = correlateContextsWithScopes(snapshot, scan, scopesByScript);
|
|
@@ -240,6 +249,9 @@ function scanHeap(snapshot: HeapSnapshot): HeapScan {
|
|
|
240
249
|
const scripts = new Map<ScriptNodeIndex, ScriptInfo>();
|
|
241
250
|
const contextNodes: ContextNodeInfo[] = [];
|
|
242
251
|
const liveClosures: LiveClosure[] = [];
|
|
252
|
+
const finishedModuleFunctionNodeIndexes = new Set<number>();
|
|
253
|
+
// Caches for each map whether it belongs to a generator object. See `isGeneratorObject`.
|
|
254
|
+
const generatorMapNodeIndexes = new Map<MapNodeIndex, boolean>();
|
|
243
255
|
const scopeInfoScriptNodeIndexes = new Map<ScopeInfoNodeIndex, ScriptNodeIndex|undefined>();
|
|
244
256
|
const node = snapshot.createNode();
|
|
245
257
|
const nodes = snapshot.nodes;
|
|
@@ -257,10 +269,12 @@ function scanHeap(snapshot: HeapSnapshot): HeapScan {
|
|
|
257
269
|
processContext(contextNodes, node);
|
|
258
270
|
} else if (node.rawType() === nodeClosureType) {
|
|
259
271
|
processClosure(liveClosures, node);
|
|
272
|
+
} else if (isGeneratorObject(generatorMapNodeIndexes, node)) {
|
|
273
|
+
processGeneratorObject(liveClosures, finishedModuleFunctionNodeIndexes, node);
|
|
260
274
|
}
|
|
261
275
|
}
|
|
262
276
|
|
|
263
|
-
return {scripts, contextNodes, liveClosures, scopeInfoScriptNodeIndexes};
|
|
277
|
+
return {scripts, contextNodes, liveClosures, finishedModuleFunctionNodeIndexes, scopeInfoScriptNodeIndexes};
|
|
264
278
|
}
|
|
265
279
|
|
|
266
280
|
function processScript(scripts: Map<ScriptNodeIndex, ScriptInfo>, node: HeapSnapshotNode): void {
|
|
@@ -426,6 +440,11 @@ function processClosure(liveClosures: LiveClosure[], node: HeapSnapshotNode): vo
|
|
|
426
440
|
if (!sharedFunctionInfo || !closureContext) {
|
|
427
441
|
return;
|
|
428
442
|
}
|
|
443
|
+
addLiveClosure(liveClosures, node, sharedFunctionInfo, closureContext);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function addLiveClosure(liveClosures: LiveClosure[], ownerNode: HeapSnapshotNode, sharedFunctionInfo: HeapSnapshotNode,
|
|
447
|
+
contextNode: HeapSnapshotNode): void {
|
|
429
448
|
const script = sharedFunctionInfo.findInternalEdgeTarget('script');
|
|
430
449
|
if (!script) {
|
|
431
450
|
return;
|
|
@@ -433,18 +452,97 @@ function processClosure(liveClosures: LiveClosure[], node: HeapSnapshotNode): vo
|
|
|
433
452
|
const scopeId = sharedFunctionInfo.findInternalEdgeTarget('scope_id')?.nodeValueAsInt() as ScopeId | undefined;
|
|
434
453
|
if (scopeId !== undefined) {
|
|
435
454
|
liveClosures.push({
|
|
436
|
-
|
|
455
|
+
ownerNodeIndex: ownerNode.nodeIndex,
|
|
456
|
+
contextNodeIndex: contextNode.nodeIndex as ContextNodeIndex,
|
|
437
457
|
scriptNodeIndex: script.nodeIndex as ScriptNodeIndex,
|
|
438
458
|
scopeId,
|
|
439
459
|
});
|
|
440
460
|
}
|
|
441
461
|
}
|
|
442
462
|
|
|
443
|
-
|
|
463
|
+
// V8's `JSGeneratorObject::kGeneratorClosed`: the generator has finished and can't be resumed.
|
|
464
|
+
const GENERATOR_CLOSED = -1;
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Handles generator objects, which V8 uses for generators, async functions, async generators and
|
|
468
|
+
* the top-level code of modules.
|
|
469
|
+
*/
|
|
470
|
+
function processGeneratorObject(liveClosures: LiveClosure[], finishedModuleFunctionNodeIndexes: Set<number>,
|
|
471
|
+
node: HeapSnapshotNode): void {
|
|
472
|
+
const continuation = node.findInternalEdgeTarget('continuation')?.nodeValueAsInt();
|
|
473
|
+
const generatorFunction = node.findInternalEdgeTarget('function');
|
|
474
|
+
if (continuation === undefined || generatorFunction?.rawType() !== node.snapshot.nodeClosureType) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
const sharedFunctionInfo = generatorFunction.findInternalEdgeTarget('shared');
|
|
478
|
+
if (!sharedFunctionInfo) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
if (continuation === GENERATOR_CLOSED) {
|
|
482
|
+
const scopeId = sharedFunctionInfo.findInternalEdgeTarget('scope_id')?.nodeValueAsInt();
|
|
483
|
+
|
|
484
|
+
// Only the top-level code of a module is both a generator and a root scope.
|
|
485
|
+
if (scopeId !== undefined && isScriptRootScopeId(scopeId)) {
|
|
486
|
+
// A module stores a reference to its generator object. The generator object then
|
|
487
|
+
// references the top-level JSFunction. Once the generator object is finished its
|
|
488
|
+
// JSFunction can't run anymore and user code can't call it. Here we record such closures
|
|
489
|
+
// in order to skip them later in buildLiveFunctions().
|
|
490
|
+
finishedModuleFunctionNodeIndexes.add(generatorFunction.nodeIndex);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// A finished generator can't run again, so it doesn't get a LiveClosure. That way the fields
|
|
494
|
+
// of its context can be reported as dead.
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const generatorContext = node.findInternalEdgeTarget('context');
|
|
498
|
+
if (generatorContext) {
|
|
499
|
+
// The context of the generator's function is the one the function was created in. The body
|
|
500
|
+
// of the generator, however, runs in a context of its own, which only the generator object
|
|
501
|
+
// refers to. A generator therefore counts as a closure of its function with the context
|
|
502
|
+
// it resumes in.
|
|
503
|
+
addLiveClosure(liveClosures, node, sharedFunctionInfo, generatorContext);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// V8's instance types of `JSGeneratorObject` and its subclasses.
|
|
508
|
+
const GENERATOR_INSTANCE_TYPE_NAMES = new Set([
|
|
509
|
+
'JS_GENERATOR_OBJECT_TYPE',
|
|
510
|
+
'JS_ASYNC_FUNCTION_OBJECT_TYPE',
|
|
511
|
+
'JS_ASYNC_GENERATOR_OBJECT_TYPE',
|
|
512
|
+
]);
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Checks the instance type of the object's map, so that other objects with fields of the same name
|
|
516
|
+
* aren't mistaken for generator objects. The result is cached for each map.
|
|
517
|
+
*/
|
|
518
|
+
function isGeneratorObject(generatorMapNodeIndexes: Map<MapNodeIndex, boolean>, node: HeapSnapshotNode): boolean {
|
|
519
|
+
if (node.rawType() !== node.snapshot.nodeObjectType) {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
const map = node.findInternalEdgeTarget('map');
|
|
523
|
+
if (!map) {
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
const mapNodeIndex = map.nodeIndex as MapNodeIndex;
|
|
527
|
+
let isGenerator = generatorMapNodeIndexes.get(mapNodeIndex);
|
|
528
|
+
if (isGenerator === undefined) {
|
|
529
|
+
const instanceTypeName = map.findInternalEdgeTarget('instance_type_name')?.rawName();
|
|
530
|
+
isGenerator = instanceTypeName !== undefined && GENERATOR_INSTANCE_TYPE_NAMES.has(instanceTypeName);
|
|
531
|
+
generatorMapNodeIndexes.set(mapNodeIndex, isGenerator);
|
|
532
|
+
}
|
|
533
|
+
return isGenerator;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function buildLiveFunctions(snapshot: HeapSnapshot, liveClosures: LiveClosure[],
|
|
537
|
+
finishedModuleFunctionNodeIndexes: Set<number>): ByScope<LiveFunction> {
|
|
444
538
|
const liveFunctionsByScript: ByScope<LiveFunction> = new Map();
|
|
445
539
|
const node = snapshot.createNode();
|
|
446
540
|
|
|
447
541
|
for (const closure of liveClosures) {
|
|
542
|
+
if (finishedModuleFunctionNodeIndexes.has(closure.ownerNodeIndex)) {
|
|
543
|
+
// The top-level code of this module has finished and can't run anymore.
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
448
546
|
let scriptFunctions = liveFunctionsByScript.get(closure.scriptNodeIndex);
|
|
449
547
|
if (!scriptFunctions) {
|
|
450
548
|
scriptFunctions = new Map<ScopeId, LiveFunction>();
|