11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst { 41cb0ef41Sopenharmony_ci ArrayPrototypeIndexOf, 51cb0ef41Sopenharmony_ci ArrayPrototypeJoin, 61cb0ef41Sopenharmony_ci ArrayPrototypeMap, 71cb0ef41Sopenharmony_ci ErrorPrototypeToString, 81cb0ef41Sopenharmony_ci RegExpPrototypeSymbolSplit, 91cb0ef41Sopenharmony_ci StringPrototypeRepeat, 101cb0ef41Sopenharmony_ci StringPrototypeSlice, 111cb0ef41Sopenharmony_ci StringPrototypeStartsWith, 121cb0ef41Sopenharmony_ci SafeStringIterator, 131cb0ef41Sopenharmony_ci} = primordials; 141cb0ef41Sopenharmony_ci 151cb0ef41Sopenharmony_cilet debug = require('internal/util/debuglog').debuglog('source_map', (fn) => { 161cb0ef41Sopenharmony_ci debug = fn; 171cb0ef41Sopenharmony_ci}); 181cb0ef41Sopenharmony_ciconst { getStringWidth } = require('internal/util/inspect'); 191cb0ef41Sopenharmony_ciconst { readFileSync } = require('fs'); 201cb0ef41Sopenharmony_ciconst { findSourceMap } = require('internal/source_map/source_map_cache'); 211cb0ef41Sopenharmony_ciconst { 221cb0ef41Sopenharmony_ci kNoOverride, 231cb0ef41Sopenharmony_ci overrideStackTrace, 241cb0ef41Sopenharmony_ci maybeOverridePrepareStackTrace, 251cb0ef41Sopenharmony_ci kIsNodeError, 261cb0ef41Sopenharmony_ci} = require('internal/errors'); 271cb0ef41Sopenharmony_ciconst { fileURLToPath } = require('internal/url'); 281cb0ef41Sopenharmony_ciconst { setGetSourceMapErrorSource } = internalBinding('errors'); 291cb0ef41Sopenharmony_ci 301cb0ef41Sopenharmony_ci// Create a prettified stacktrace, inserting context from source maps 311cb0ef41Sopenharmony_ci// if possible. 321cb0ef41Sopenharmony_ciconst prepareStackTrace = (globalThis, error, trace) => { 331cb0ef41Sopenharmony_ci // API for node internals to override error stack formatting 341cb0ef41Sopenharmony_ci // without interfering with userland code. 351cb0ef41Sopenharmony_ci // TODO(bcoe): add support for source-maps to repl. 361cb0ef41Sopenharmony_ci if (overrideStackTrace.has(error)) { 371cb0ef41Sopenharmony_ci const f = overrideStackTrace.get(error); 381cb0ef41Sopenharmony_ci overrideStackTrace.delete(error); 391cb0ef41Sopenharmony_ci return f(error, trace); 401cb0ef41Sopenharmony_ci } 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_ci const globalOverride = 431cb0ef41Sopenharmony_ci maybeOverridePrepareStackTrace(globalThis, error, trace); 441cb0ef41Sopenharmony_ci if (globalOverride !== kNoOverride) return globalOverride; 451cb0ef41Sopenharmony_ci 461cb0ef41Sopenharmony_ci let errorString; 471cb0ef41Sopenharmony_ci if (kIsNodeError in error) { 481cb0ef41Sopenharmony_ci errorString = `${error.name} [${error.code}]: ${error.message}`; 491cb0ef41Sopenharmony_ci } else { 501cb0ef41Sopenharmony_ci errorString = ErrorPrototypeToString(error); 511cb0ef41Sopenharmony_ci } 521cb0ef41Sopenharmony_ci 531cb0ef41Sopenharmony_ci if (trace.length === 0) { 541cb0ef41Sopenharmony_ci return errorString; 551cb0ef41Sopenharmony_ci } 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci let lastSourceMap; 581cb0ef41Sopenharmony_ci let lastFileName; 591cb0ef41Sopenharmony_ci const preparedTrace = ArrayPrototypeJoin(ArrayPrototypeMap(trace, (t, i) => { 601cb0ef41Sopenharmony_ci const str = i !== 0 ? '\n at ' : ''; 611cb0ef41Sopenharmony_ci try { 621cb0ef41Sopenharmony_ci // A stack trace will often have several call sites in a row within the 631cb0ef41Sopenharmony_ci // same file, cache the source map and file content accordingly: 641cb0ef41Sopenharmony_ci let fileName = t.getFileName(); 651cb0ef41Sopenharmony_ci if (fileName === undefined) { 661cb0ef41Sopenharmony_ci fileName = t.getEvalOrigin(); 671cb0ef41Sopenharmony_ci } 681cb0ef41Sopenharmony_ci const sm = fileName === lastFileName ? 691cb0ef41Sopenharmony_ci lastSourceMap : 701cb0ef41Sopenharmony_ci findSourceMap(fileName); 711cb0ef41Sopenharmony_ci lastSourceMap = sm; 721cb0ef41Sopenharmony_ci lastFileName = fileName; 731cb0ef41Sopenharmony_ci if (sm) { 741cb0ef41Sopenharmony_ci // Source Map V3 lines/columns start at 0/0 whereas stack traces 751cb0ef41Sopenharmony_ci // start at 1/1: 761cb0ef41Sopenharmony_ci const { 771cb0ef41Sopenharmony_ci originalLine, 781cb0ef41Sopenharmony_ci originalColumn, 791cb0ef41Sopenharmony_ci originalSource, 801cb0ef41Sopenharmony_ci } = sm.findEntry(t.getLineNumber() - 1, t.getColumnNumber() - 1); 811cb0ef41Sopenharmony_ci if (originalSource && originalLine !== undefined && 821cb0ef41Sopenharmony_ci originalColumn !== undefined) { 831cb0ef41Sopenharmony_ci const name = getOriginalSymbolName(sm, trace, i); 841cb0ef41Sopenharmony_ci // Construct call site name based on: v8.dev/docs/stack-trace-api: 851cb0ef41Sopenharmony_ci const fnName = t.getFunctionName() ?? t.getMethodName(); 861cb0ef41Sopenharmony_ci const typeName = t.getTypeName(); 871cb0ef41Sopenharmony_ci const namePrefix = typeName !== null && typeName !== 'global' ? `${typeName}.` : ''; 881cb0ef41Sopenharmony_ci const originalName = `${namePrefix}${fnName || '<anonymous>'}`; 891cb0ef41Sopenharmony_ci // The original call site may have a different symbol name 901cb0ef41Sopenharmony_ci // associated with it, use it: 911cb0ef41Sopenharmony_ci const prefix = (name && name !== originalName) ? 921cb0ef41Sopenharmony_ci `${name}` : 931cb0ef41Sopenharmony_ci `${originalName}`; 941cb0ef41Sopenharmony_ci const hasName = !!(name || originalName); 951cb0ef41Sopenharmony_ci const originalSourceNoScheme = 961cb0ef41Sopenharmony_ci StringPrototypeStartsWith(originalSource, 'file://') ? 971cb0ef41Sopenharmony_ci fileURLToPath(originalSource) : originalSource; 981cb0ef41Sopenharmony_ci // Replace the transpiled call site with the original: 991cb0ef41Sopenharmony_ci return `${str}${prefix}${hasName ? ' (' : ''}` + 1001cb0ef41Sopenharmony_ci `${originalSourceNoScheme}:${originalLine + 1}:` + 1011cb0ef41Sopenharmony_ci `${originalColumn + 1}${hasName ? ')' : ''}`; 1021cb0ef41Sopenharmony_ci } 1031cb0ef41Sopenharmony_ci } 1041cb0ef41Sopenharmony_ci } catch (err) { 1051cb0ef41Sopenharmony_ci debug(err); 1061cb0ef41Sopenharmony_ci } 1071cb0ef41Sopenharmony_ci return `${str}${t}`; 1081cb0ef41Sopenharmony_ci }), ''); 1091cb0ef41Sopenharmony_ci return `${errorString}\n at ${preparedTrace}`; 1101cb0ef41Sopenharmony_ci}; 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_ci// Transpilers may have removed the original symbol name used in the stack 1131cb0ef41Sopenharmony_ci// trace, if possible restore it from the names field of the source map: 1141cb0ef41Sopenharmony_cifunction getOriginalSymbolName(sourceMap, trace, curIndex) { 1151cb0ef41Sopenharmony_ci // First check for a symbol name associated with the enclosing function: 1161cb0ef41Sopenharmony_ci const enclosingEntry = sourceMap.findEntry( 1171cb0ef41Sopenharmony_ci trace[curIndex].getEnclosingLineNumber() - 1, 1181cb0ef41Sopenharmony_ci trace[curIndex].getEnclosingColumnNumber() - 1, 1191cb0ef41Sopenharmony_ci ); 1201cb0ef41Sopenharmony_ci if (enclosingEntry.name) return enclosingEntry.name; 1211cb0ef41Sopenharmony_ci // Fallback to using the symbol name attached to the next stack frame: 1221cb0ef41Sopenharmony_ci const currentFileName = trace[curIndex].getFileName(); 1231cb0ef41Sopenharmony_ci const nextCallSite = trace[curIndex + 1]; 1241cb0ef41Sopenharmony_ci if (nextCallSite && currentFileName === nextCallSite.getFileName()) { 1251cb0ef41Sopenharmony_ci const { name } = sourceMap.findEntry( 1261cb0ef41Sopenharmony_ci nextCallSite.getLineNumber() - 1, 1271cb0ef41Sopenharmony_ci nextCallSite.getColumnNumber() - 1, 1281cb0ef41Sopenharmony_ci ); 1291cb0ef41Sopenharmony_ci return name; 1301cb0ef41Sopenharmony_ci } 1311cb0ef41Sopenharmony_ci} 1321cb0ef41Sopenharmony_ci 1331cb0ef41Sopenharmony_ci// Places a snippet of code from where the exception was originally thrown 1341cb0ef41Sopenharmony_ci// above the stack trace. This logic is modeled after GetErrorSource in 1351cb0ef41Sopenharmony_ci// node_errors.cc. 1361cb0ef41Sopenharmony_cifunction getErrorSource( 1371cb0ef41Sopenharmony_ci sourceMap, 1381cb0ef41Sopenharmony_ci originalSourcePath, 1391cb0ef41Sopenharmony_ci originalLine, 1401cb0ef41Sopenharmony_ci originalColumn, 1411cb0ef41Sopenharmony_ci) { 1421cb0ef41Sopenharmony_ci const originalSourcePathNoScheme = 1431cb0ef41Sopenharmony_ci StringPrototypeStartsWith(originalSourcePath, 'file://') ? 1441cb0ef41Sopenharmony_ci fileURLToPath(originalSourcePath) : originalSourcePath; 1451cb0ef41Sopenharmony_ci const source = getOriginalSource( 1461cb0ef41Sopenharmony_ci sourceMap.payload, 1471cb0ef41Sopenharmony_ci originalSourcePath, 1481cb0ef41Sopenharmony_ci ); 1491cb0ef41Sopenharmony_ci if (typeof source !== 'string') { 1501cb0ef41Sopenharmony_ci return; 1511cb0ef41Sopenharmony_ci } 1521cb0ef41Sopenharmony_ci const lines = RegExpPrototypeSymbolSplit(/\r?\n/, source, originalLine + 1); 1531cb0ef41Sopenharmony_ci const line = lines[originalLine]; 1541cb0ef41Sopenharmony_ci if (!line) { 1551cb0ef41Sopenharmony_ci return; 1561cb0ef41Sopenharmony_ci } 1571cb0ef41Sopenharmony_ci 1581cb0ef41Sopenharmony_ci // Display ^ in appropriate position, regardless of whether tabs or 1591cb0ef41Sopenharmony_ci // spaces are used: 1601cb0ef41Sopenharmony_ci let prefix = ''; 1611cb0ef41Sopenharmony_ci for (const character of new SafeStringIterator( 1621cb0ef41Sopenharmony_ci StringPrototypeSlice(line, 0, originalColumn + 1))) { 1631cb0ef41Sopenharmony_ci prefix += character === '\t' ? '\t' : 1641cb0ef41Sopenharmony_ci StringPrototypeRepeat(' ', getStringWidth(character)); 1651cb0ef41Sopenharmony_ci } 1661cb0ef41Sopenharmony_ci prefix = StringPrototypeSlice(prefix, 0, -1); // The last character is '^'. 1671cb0ef41Sopenharmony_ci 1681cb0ef41Sopenharmony_ci const exceptionLine = 1691cb0ef41Sopenharmony_ci `${originalSourcePathNoScheme}:${originalLine + 1}\n${line}\n${prefix}^\n\n`; 1701cb0ef41Sopenharmony_ci return exceptionLine; 1711cb0ef41Sopenharmony_ci} 1721cb0ef41Sopenharmony_ci 1731cb0ef41Sopenharmony_cifunction getOriginalSource(payload, originalSourcePath) { 1741cb0ef41Sopenharmony_ci let source; 1751cb0ef41Sopenharmony_ci // payload.sources has been normalized to be an array of absolute urls. 1761cb0ef41Sopenharmony_ci const sourceContentIndex = 1771cb0ef41Sopenharmony_ci ArrayPrototypeIndexOf(payload.sources, originalSourcePath); 1781cb0ef41Sopenharmony_ci if (payload.sourcesContent?.[sourceContentIndex]) { 1791cb0ef41Sopenharmony_ci // First we check if the original source content was provided in the 1801cb0ef41Sopenharmony_ci // source map itself: 1811cb0ef41Sopenharmony_ci source = payload.sourcesContent[sourceContentIndex]; 1821cb0ef41Sopenharmony_ci } else if (StringPrototypeStartsWith(originalSourcePath, 'file://')) { 1831cb0ef41Sopenharmony_ci // If no sourcesContent was found, attempt to load the original source 1841cb0ef41Sopenharmony_ci // from disk: 1851cb0ef41Sopenharmony_ci debug(`read source of ${originalSourcePath} from filesystem`); 1861cb0ef41Sopenharmony_ci const originalSourcePathNoScheme = fileURLToPath(originalSourcePath); 1871cb0ef41Sopenharmony_ci try { 1881cb0ef41Sopenharmony_ci source = readFileSync(originalSourcePathNoScheme, 'utf8'); 1891cb0ef41Sopenharmony_ci } catch (err) { 1901cb0ef41Sopenharmony_ci debug(err); 1911cb0ef41Sopenharmony_ci } 1921cb0ef41Sopenharmony_ci } 1931cb0ef41Sopenharmony_ci return source; 1941cb0ef41Sopenharmony_ci} 1951cb0ef41Sopenharmony_ci 1961cb0ef41Sopenharmony_cifunction getSourceMapErrorSource(fileName, lineNumber, columnNumber) { 1971cb0ef41Sopenharmony_ci const sm = findSourceMap(fileName); 1981cb0ef41Sopenharmony_ci if (sm === undefined) { 1991cb0ef41Sopenharmony_ci return; 2001cb0ef41Sopenharmony_ci } 2011cb0ef41Sopenharmony_ci const { 2021cb0ef41Sopenharmony_ci originalLine, 2031cb0ef41Sopenharmony_ci originalColumn, 2041cb0ef41Sopenharmony_ci originalSource, 2051cb0ef41Sopenharmony_ci } = sm.findEntry(lineNumber - 1, columnNumber); 2061cb0ef41Sopenharmony_ci const errorSource = getErrorSource(sm, originalSource, originalLine, originalColumn); 2071cb0ef41Sopenharmony_ci return errorSource; 2081cb0ef41Sopenharmony_ci} 2091cb0ef41Sopenharmony_ci 2101cb0ef41Sopenharmony_cisetGetSourceMapErrorSource(getSourceMapErrorSource); 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_cimodule.exports = { 2131cb0ef41Sopenharmony_ci prepareStackTrace, 2141cb0ef41Sopenharmony_ci}; 215