stack-parsers.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const utils = require('@sentry/utils');
  3. // global reference to slice
  4. const UNKNOWN_FUNCTION = '?';
  5. const OPERA10_PRIORITY = 10;
  6. const OPERA11_PRIORITY = 20;
  7. const CHROME_PRIORITY = 30;
  8. const WINJS_PRIORITY = 40;
  9. const GECKO_PRIORITY = 50;
  10. function createFrame(filename, func, lineno, colno) {
  11. const frame = {
  12. filename,
  13. function: func,
  14. in_app: true, // All browser frames are considered in_app
  15. };
  16. if (lineno !== undefined) {
  17. frame.lineno = lineno;
  18. }
  19. if (colno !== undefined) {
  20. frame.colno = colno;
  21. }
  22. return frame;
  23. }
  24. // Chromium based browsers: Chrome, Brave, new Opera, new Edge
  25. const chromeRegex =
  26. /^\s*at (?:(.+?\)(?: \[.+\])?|.*?) ?\((?:address at )?)?(?:async )?((?:<anonymous>|[-a-z]+:|.*bundle|\/)?.*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i;
  27. const chromeEvalRegex = /\((\S*)(?::(\d+))(?::(\d+))\)/;
  28. const chrome = line => {
  29. const parts = chromeRegex.exec(line);
  30. if (parts) {
  31. const isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line
  32. if (isEval) {
  33. const subMatch = chromeEvalRegex.exec(parts[2]);
  34. if (subMatch) {
  35. // throw out eval line/column and use top-most line/column number
  36. parts[2] = subMatch[1]; // url
  37. parts[3] = subMatch[2]; // line
  38. parts[4] = subMatch[3]; // column
  39. }
  40. }
  41. // Kamil: One more hack won't hurt us right? Understanding and adding more rules on top of these regexps right now
  42. // would be way too time consuming. (TODO: Rewrite whole RegExp to be more readable)
  43. const [func, filename] = extractSafariExtensionDetails(parts[1] || UNKNOWN_FUNCTION, parts[2]);
  44. return createFrame(filename, func, parts[3] ? +parts[3] : undefined, parts[4] ? +parts[4] : undefined);
  45. }
  46. return;
  47. };
  48. const chromeStackLineParser = [CHROME_PRIORITY, chrome];
  49. // gecko regex: `(?:bundle|\d+\.js)`: `bundle` is for react native, `\d+\.js` also but specifically for ram bundles because it
  50. // generates filenames without a prefix like `file://` the filenames in the stacktrace are just 42.js
  51. // We need this specific case for now because we want no other regex to match.
  52. const geckoREgex =
  53. /^\s*(.*?)(?:\((.*?)\))?(?:^|@)?((?:[-a-z]+)?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js)|\/[\w\-. /=]+)(?::(\d+))?(?::(\d+))?\s*$/i;
  54. const geckoEvalRegex = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i;
  55. const gecko = line => {
  56. const parts = geckoREgex.exec(line);
  57. if (parts) {
  58. const isEval = parts[3] && parts[3].indexOf(' > eval') > -1;
  59. if (isEval) {
  60. const subMatch = geckoEvalRegex.exec(parts[3]);
  61. if (subMatch) {
  62. // throw out eval line/column and use top-most line number
  63. parts[1] = parts[1] || 'eval';
  64. parts[3] = subMatch[1];
  65. parts[4] = subMatch[2];
  66. parts[5] = ''; // no column when eval
  67. }
  68. }
  69. let filename = parts[3];
  70. let func = parts[1] || UNKNOWN_FUNCTION;
  71. [func, filename] = extractSafariExtensionDetails(func, filename);
  72. return createFrame(filename, func, parts[4] ? +parts[4] : undefined, parts[5] ? +parts[5] : undefined);
  73. }
  74. return;
  75. };
  76. const geckoStackLineParser = [GECKO_PRIORITY, gecko];
  77. const winjsRegex = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:[-a-z]+):.*?):(\d+)(?::(\d+))?\)?\s*$/i;
  78. const winjs = line => {
  79. const parts = winjsRegex.exec(line);
  80. return parts
  81. ? createFrame(parts[2], parts[1] || UNKNOWN_FUNCTION, +parts[3], parts[4] ? +parts[4] : undefined)
  82. : undefined;
  83. };
  84. const winjsStackLineParser = [WINJS_PRIORITY, winjs];
  85. const opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i;
  86. const opera10 = line => {
  87. const parts = opera10Regex.exec(line);
  88. return parts ? createFrame(parts[2], parts[3] || UNKNOWN_FUNCTION, +parts[1]) : undefined;
  89. };
  90. const opera10StackLineParser = [OPERA10_PRIORITY, opera10];
  91. const opera11Regex =
  92. / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^)]+))\(.*\))? in (.*):\s*$/i;
  93. const opera11 = line => {
  94. const parts = opera11Regex.exec(line);
  95. return parts ? createFrame(parts[5], parts[3] || parts[4] || UNKNOWN_FUNCTION, +parts[1], +parts[2]) : undefined;
  96. };
  97. const opera11StackLineParser = [OPERA11_PRIORITY, opera11];
  98. const defaultStackLineParsers = [chromeStackLineParser, geckoStackLineParser, winjsStackLineParser];
  99. const defaultStackParser = utils.createStackParser(...defaultStackLineParsers);
  100. /**
  101. * Safari web extensions, starting version unknown, can produce "frames-only" stacktraces.
  102. * What it means, is that instead of format like:
  103. *
  104. * Error: wat
  105. * at function@url:row:col
  106. * at function@url:row:col
  107. * at function@url:row:col
  108. *
  109. * it produces something like:
  110. *
  111. * function@url:row:col
  112. * function@url:row:col
  113. * function@url:row:col
  114. *
  115. * Because of that, it won't be captured by `chrome` RegExp and will fall into `Gecko` branch.
  116. * This function is extracted so that we can use it in both places without duplicating the logic.
  117. * Unfortunately "just" changing RegExp is too complicated now and making it pass all tests
  118. * and fix this case seems like an impossible, or at least way too time-consuming task.
  119. */
  120. const extractSafariExtensionDetails = (func, filename) => {
  121. const isSafariExtension = func.indexOf('safari-extension') !== -1;
  122. const isSafariWebExtension = func.indexOf('safari-web-extension') !== -1;
  123. return isSafariExtension || isSafariWebExtension
  124. ? [
  125. func.indexOf('@') !== -1 ? func.split('@')[0] : UNKNOWN_FUNCTION,
  126. isSafariExtension ? `safari-extension:${filename}` : `safari-web-extension:${filename}`,
  127. ]
  128. : [func, filename];
  129. };
  130. exports.chromeStackLineParser = chromeStackLineParser;
  131. exports.defaultStackLineParsers = defaultStackLineParsers;
  132. exports.defaultStackParser = defaultStackParser;
  133. exports.geckoStackLineParser = geckoStackLineParser;
  134. exports.opera10StackLineParser = opera10StackLineParser;
  135. exports.opera11StackLineParser = opera11StackLineParser;
  136. exports.winjsStackLineParser = winjsStackLineParser;
  137. //# sourceMappingURL=stack-parsers.js.map