1:227 * ``` * * Into a column layout: * * ``` * semicolon (:8:10) * jkl (:5:10) * asdf (:2:10) * (:11:1) * ``` */ function getStacktraceElements(props, preview) { const stack = []; if (!preview.stack) { return stack; } parseStackString(preview.stack).forEach((frame, index) => { let onLocationClick; const { filename, lineNumber, columnNumber, functionName, location } = frame; if ( props.onViewSourceInDebugger && !IGNORED_SOURCE_URLS.includes(filename) ) { onLocationClick = e => { // Don't trigger ObjectInspector expand/collapse. e.stopPropagation(); props.onViewSourceInDebugger({ url: filename, line: lineNumber, column: columnNumber, }); }; } stack.push( "\t", span( { key: `fn${index}`, className: "objectBox-stackTrace-fn", }, cleanFunctionName(functionName) ), " ", span( { key: `location${index}`, className: "objectBox-stackTrace-location", onClick: onLocationClick, title: onLocationClick ? `View source in debugger → ${location}` : undefined, }, location ), "\n" ); }); return span( { key: "stack", className: "objectBox-stackTrace-grid", }, stack ); } /** * Returns a React element representing the cause of the Error i.e. the `cause` * property in the second parameter of the Error constructor (`new Error("message", { cause })`) * * Example: * Caused by: Error: original error */ function getCauseElement(props, preview) { return div( { key: "cause-container", className: "error-rep-cause", }, "Caused by: ", props.Rep({ ...props, key: "cause", object: preview.cause, mode: props.mode || MODE.TINY, }) ); } /** * Parse a string that should represent a stack trace and returns an array of * the frames. The shape of the frames are extremely important as they can then * be processed here or in the toolbox by other components. * * @param {string} stack * @returns {Array} Array of frames, which are object with the following shape: * - {String} filename * - {String} functionName * - {String} location * - {Number} columnNumber * - {Number} lineNumber */ function parseStackString(stack) { if (!stack) { return []; } const isStacktraceALongString = isLongString(stack); const stackString = isStacktraceALongString ? stack.initial : stack; if (typeof stackString !== "string") { return []; } const res = []; stackString.split("\n").forEach((frame, index, frames) => { if (!frame) { // Skip any blank lines return; } // If the stacktrace is a longString, don't include the last frame in the // array, since it is certainly incomplete. // Can be removed when https://bugzilla.mozilla.org/show_bug.cgi?id=1448833 // is fixed. if (isStacktraceALongString && index === frames.length - 1) { return; } let functionName; let location; // Retrieve the index of the first @ to split the frame string. const atCharIndex = frame.indexOf("@"); if (atCharIndex > -1) { functionName = frame.slice(0, atCharIndex); location = frame.slice(atCharIndex + 1); } if (location && location.includes(" -> ")) { // If the resource was loaded by base-loader.sys.mjs, the location looks like: // resource://devtools/shared/base-loader.sys.mjs -> resource://path/to/file.js . // What's needed is only the last part after " -> ". location = location.split(" -> ").pop(); } if (!functionName) { functionName = ""; } // Given the input: "scriptLocation:2:100" // Result: // ["scriptLocation:2:100", "scriptLocation", "2", "100"] const locationParts = location ? location.match(/^(.*):(\d+):(\d+)$/) : null; if (location && locationParts) { const [, filename, line, column] = locationParts; res.push({ filename, functionName, location, columnNumber: Number(column), lineNumber: Number(line), }); } }); return res; } // Registration function supportsObject(object) { return ( object?.isError || object?.class === "DOMException" || object?.class === "Exception" ); } const rep = wrapRender(ErrorRep); // Exports from this module export { rep, supportsObject }; PK