blob: 77da9c395cc14663c20dbe585eef7c48509ff20a [file] [log] [blame]
/*
* Copyright (C) 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
WebInspector.StackTrace = class StackTrace extends WebInspector.Object
{
constructor(callFrames)
{
super();
console.assert(callFrames && callFrames.every((callFrame) => callFrame instanceof WebInspector.CallFrame));
this._callFrames = callFrames;
}
// Static
static fromPayload(payload)
{
var callFrames = payload.map(WebInspector.CallFrame.fromPayload);
return new WebInspector.StackTrace(callFrames);
}
static fromString(stack)
{
var payload = WebInspector.StackTrace._parseStackTrace(stack);
return WebInspector.StackTrace.fromPayload(payload);
}
// May produce false negatives; must not produce any false positives.
// It may return false on a valid stack trace, but it will never return true on an invalid stack trace.
static isLikelyStackTrace(stack)
{
// This function runs for every logged string. It penalizes the performance.
// As most logged strings are not stack traces, exit as early as possible.
const smallestPossibleStackTraceLength = "http://a.bc/:9:1".length;
if (stack.length < smallestPossibleStackTraceLength.length * 2)
return false;
const approximateStackLengthOf50Items = 5000;
if (stack.length > approximateStackLengthOf50Items)
return false;
if (/^[^a-z$_]/i.test(stack[0]))
return false;
const reasonablyLongLineLength = 500;
const reasonablyLongNativeMethodLength = 120;
const stackTraceLine = `(.{1,${reasonablyLongLineLength}}:\\d+:\\d+|eval code|.{1,${reasonablyLongNativeMethodLength}}@\\[native code\\])`;
const stackTrace = new RegExp(`^${stackTraceLine}(\\n${stackTraceLine})*$`, "g");
return stackTrace.test(stack);
}
static _parseStackTrace(stack)
{
var lines = stack.split(/\n/g);
var result = [];
for (var line of lines) {
var functionName = "";
var url = "";
var lineNumber = 0;
var columnNumber = 0;
var atIndex = line.indexOf("@");
if (atIndex !== -1) {
functionName = line.slice(0, atIndex);
({url, lineNumber, columnNumber} = WebInspector.StackTrace._parseLocation(line.slice(atIndex + 1)));
} else if (line.includes("/"))
({url, lineNumber, columnNumber} = WebInspector.StackTrace._parseLocation(line));
else
functionName = line;
result.push({functionName, url, lineNumber, columnNumber});
}
return result;
}
static _parseLocation(locationString)
{
var result = {url: "", lineNumber: 0, columnNumber: 0};
var locationRegEx = /(.+?)(?::(\d+)(?::(\d+))?)?$/;
var matched = locationString.match(locationRegEx);
if (!matched)
return result;
result.url = matched[1];
if (matched[2])
result.lineNumber = parseInt(matched[2]);
if (matched[3])
result.columnNumber = parseInt(matched[3]);
return result;
}
// Public
get callFrames()
{
return this._callFrames;
}
get firstNonNativeCallFrame()
{
for (let frame of this._callFrames) {
if (!frame.nativeCode)
return frame;
}
return null;
}
get firstNonNativeNonAnonymousCallFrame()
{
for (let frame of this._callFrames) {
if (frame.nativeCode)
continue;
if (frame.sourceCodeLocation) {
let sourceCode = frame.sourceCodeLocation.sourceCode;
if (sourceCode instanceof WebInspector.Script && sourceCode.anonymous)
continue;
}
return frame;
}
return null;
}
};