UNPKG

chrome-devtools-frontend

Version:
143 lines (128 loc) 4.59 kB
// Copyright 2026 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Common from '../../core/common/common.js'; import type * as Platform from '../../core/platform/platform.js'; import type {RawFrame} from './Trie.js'; /** * Takes a V8 Error#stack string and extracts structured information. */ export function parseRawFramesFromErrorStack(stack: string): RawFrame[] { const lines = stack.split('\n'); const rawFrames: RawFrame[] = []; for (const line of lines) { const match = /^\s*at\s+(.*)/.exec(line); if (!match) { continue; } let lineContent = match[1]; let isAsync = false; if (lineContent.startsWith('async ')) { isAsync = true; lineContent = lineContent.substring(6); } let isConstructor = false; if (lineContent.startsWith('new ')) { isConstructor = true; lineContent = lineContent.substring(4); } let functionName = ''; let url = ''; let lineNumber = -1; let columnNumber = -1; let typeName: string|undefined; let methodName: string|undefined; let isEval = false; let isWasm = false; let wasmModuleName: string|undefined; let wasmFunctionIndex: number|undefined; let promiseIndex: number|undefined; let evalOrigin: RawFrame|undefined; const openParenIndex = lineContent.indexOf(' ('); if (lineContent.endsWith(')') && openParenIndex !== -1) { functionName = lineContent.substring(0, openParenIndex).trim(); let location = lineContent.substring(openParenIndex + 2, lineContent.length - 1); if (location.startsWith('eval at ')) { isEval = true; const commaIndex = location.lastIndexOf(', '); let evalOriginStr = location; if (commaIndex !== -1) { evalOriginStr = location.substring(0, commaIndex); location = location.substring(commaIndex + 2); } else { location = ''; } if (evalOriginStr.startsWith('eval at ')) { evalOriginStr = evalOriginStr.substring(8); } const innerOpenParen = evalOriginStr.indexOf(' ('); let evalFunctionName = evalOriginStr; let evalLocation = ''; if (innerOpenParen !== -1) { evalFunctionName = evalOriginStr.substring(0, innerOpenParen).trim(); evalLocation = evalOriginStr.substring(innerOpenParen + 2, evalOriginStr.length - 1); evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName} (${evalLocation})`)[0]; } else { evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName}`)[0]; } } if (location.startsWith('index ')) { promiseIndex = parseInt(location.substring(6), 10); url = ''; } else if (location.includes(':wasm-function[')) { isWasm = true; const wasmMatch = /^(.*):wasm-function\[(\d+)\]:(0x[0-9a-fA-F]+)$/.exec(location); if (wasmMatch) { url = wasmMatch[1]; wasmFunctionIndex = parseInt(wasmMatch[2], 10); columnNumber = parseInt(wasmMatch[3], 16); } } else { const splitResult = Common.ParsedURL.ParsedURL.splitLineAndColumn(location); url = splitResult.url; lineNumber = splitResult.lineNumber ?? -1; columnNumber = splitResult.columnNumber ?? -1; } } else { const splitResult = Common.ParsedURL.ParsedURL.splitLineAndColumn(lineContent); url = splitResult.url; lineNumber = splitResult.lineNumber ?? -1; columnNumber = splitResult.columnNumber ?? -1; } // Handle "typeName.methodName [as alias]" if (functionName) { const aliasMatch = /(.*)\s+\[as\s+(.*)\]/.exec(functionName); if (aliasMatch) { methodName = aliasMatch[2]; functionName = aliasMatch[1]; } const dotIndex = functionName.indexOf('.'); if (dotIndex !== -1) { typeName = functionName.substring(0, dotIndex); methodName = methodName ?? functionName.substring(dotIndex + 1); } if (isWasm && typeName) { wasmModuleName = typeName; } } rawFrames.push({ url: url as Platform.DevToolsPath.UrlString, functionName, lineNumber, columnNumber, parsedFrameInfo: { isAsync, isConstructor, isEval, evalOrigin, isWasm, wasmModuleName, wasmFunctionIndex, typeName, methodName, promiseIndex, }, }); } return rawFrames; }