PageSourceSearch

https://launchtimevps.com/js/stacktrace.js

js launchtimevps.com collected 2026-09-29 11:40:03 UTC 13,512 bytes, 352 lines download raw bytes

1 //Domain Public by Eric Wendelin http://eriwen.com/ (2008)
2//                  Luke Smith http://lucassmith.name/ (2008)
3//                  Loic Dachary <[email protected]> (2008)
4//                  Johan Euphrosine <[email protected]> (2008)
5//                  Øyvind Sean Kinsey http://kinsey.no/blog (2010)
6//                  Victor Homyakov (2010)
7//
8// Information and discussions
9// http://jspoker.pokersource.info/skin/test-printstacktrace.html
10// http://eriwen.com/javascript/js-stack-trace/
11// http://eriwen.com/javascript/stacktrace-update/
12// http://pastie.org/253058
13// 
14// guessFunctionNameFromLines comes from firebug
15//
16// Software License Agreement (BSD License)
17//
18// Copyright (c) 2007, Parakey Inc.
19// All rights reserved.
20//
21// Redistribution and use of this software in source and binary forms, with or without modification,
22// are permitted provided that the following conditions are met:
23//
24// * Redistributions of source code must retain the above
25//   copyright notice, this list of conditions and the
26//   following disclaimer.
27//
28// * Redistributions in binary form must reproduce the above
29//   copyright notice, this list of conditions and the
30//   following disclaimer in the documentation and/or other
31//   materials provided with the distribution.
32//
33// * Neither the name of Parakey Inc. nor the names of its
34//   contributors may be used to endorse or promote products
35//   derived from this software without specific prior
36//   written permission of Parakey Inc.
37//
38// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
40// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
43// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
44// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46
47/**
48 * Main function giving a function stack trace with a forced or passed in Error 
49 *
50 * @cfg {Error} e The error to create a stacktrace from (optional)
51 * @cfg {Boolean} guess If we should try to resolve the names of anonymous functions
52 * @return {Array} of Strings with functions, lines, files, and arguments where possible 
53 */
54function printStackTrace(options) {
55    var ex = (options && options.e) ? options.e : null;
56    var guess = options ? !!options.guess : true;
57    
58    var p = new printStackTrace.implementation();
59    var result = p.run(ex);
60    return (guess) ? p.guessFunctions(result) : result;
61}
62
63printStackTrace.implementation = function() {};
64
65printStackTrace.implementation.prototype = {
66    run: function(ex) {
67        ex = ex ||
68            (function() {
69                try {
70                    var _err = __undef__ << 1;
71                } catch (e) {
72                    return e;
73                }
74            })();
75        // Use either the stored mode, or resolve it
76        var mode = this._mode || this.mode(ex);
77        if (mode === 'other') {
78            return this.other(arguments.callee);
79        } else {
80            return this[mode](ex);
81        }
82    },
83    
84    /**
85     * @return {String} mode of operation for the environment in question.
86     */
87    mode: function(e) {
88        if (e['arguments']) {
89            return (this._mode = 'chrome');
90        } else if (window.opera && e.stacktrace) {
91            return (this._mode = 'opera10');
92        } else if (e.stack) {
93            return (this._mode = 'firefox');
94        } else if (window.opera && !('stacktrace' in e)) { //Opera 9-
95            return (this._mode = 'opera');
96        }
97        return (this._mode = 'other');
98    },
99
100    /**
101     * Given a context, function name, and callback function, overwrite it so that it calls
102     * printStackTrace() first with a callback and then runs the rest of the body.
103     * 
104     * @param {Object} context of execution (e.g. window)
105     * @param {String} functionName to instrument
106     * @param {Function} function to call with a stack trace on invocation
107     */
108    instrumentFunction: function(context, functionName, callback) {
109        context = context || window;
110        context['_old' + functionName] = context[functionName];
111        context[functionName] = function() { 
112            callback.call(this, printStackTrace());
113            return context['_old' + functionName].apply(this, arguments);
114        };
115        context[functionName]._instrumented = true;
116    },
117    
118    /**
119     * Given a context and function name of a function that has been
120     * instrumented, revert the function to it's original (non-instrumented)
121     * state.
122     *
123     * @param {Object} context of execution (e.g. window)
124     * @param {String} functionName to de-instrument
125     */
126    deinstrumentFunction: function(context, functionName) {
127        if (context[functionName].constructor === Function &&
128                context[functionName]._instrumented &&
129                context['_old' + functionName].constructor === Function) {
130            context[functionName] = context['_old' + functionName];
131        }
132    },
133    
134    /**
135     * Given an Error object, return a formatted Array based on Chrome's stack string.
136     * 
137     * @param e - Error object to inspect
138     * @return Array<String> of function calls, files and line numbers
139     */
140    chrome: function(e) {
141        return e.stack.replace(/^[^\(]+?[\n$]/gm, '').replace(/^\s+at\s+/gm, '').replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@').split('\n');
142    },
143
144    /**
145     * Given an Error object, return a formatted Array based on Firefox's stack string.
146     * 
147     * @param e - Error object to inspect
148     * @return Array<String> of function calls, files and line numbers
149     */
150    firefox: function(e) {
151        return e.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^\(/gm, '{anonymous}(').split('\n');
152    },
153
154    /**
155     * Given an Error object, return a formatted Array based on Opera 10's stacktrace string.
156     * 
157     * @param e - Error object to inspect
158     * @return Array<String> of function calls, files and line numbers
159     */
160    opera10: function(e) {
161        var stack = e.stacktrace;
162        var lines = stack.split('\n'), ANON = '{anonymous}',
163            lineRE = /.*line (\d+), column (\d+) in ((<anonymous function\:?\s*(\S+))|([^\(]+)\([^\)]*\))(?: in )?(.*)\s*$/i, i, j, len;
164        for (i = 2, j = 0, len = lines.length; i < len - 2; i++) {
165            if (lineRE.test(lines[i])) {
166                var location = RegExp.$6 + ':' + RegExp.$1 + ':' + RegExp.$2;
167                var fnName = RegExp.$3;
168                fnName = fnName.replace(/<anonymous function\:?\s?(\S+)?>/g, ANON);
169                lines[j++] = fnName + '@' + location;
170            }
171        }
172        
173        lines.splice(j, lines.length - j);
174        return lines;
175    },
176    
177    // Opera 7.x-9.x only!
178    opera: function(e) {
179        var lines = e.message.split('\n'), ANON = '{anonymous}', 
180            lineRE = /Line\s+(\d+).*script\s+(http\S+)(?:.*in\s+function\s+(\S+))?/i, 
181            i, j, len;
182        
183        for (i = 4, j = 0, len = lines.length; i < len; i += 2) {
184            //TODO: RegExp.exec() would probably be cleaner here
185            if (lineRE.test(lines[i])) {
186                lines[j++] = (RegExp.$3 ? RegExp.$3 + '()@' + RegExp.$2 + RegExp.$1 : ANON + '()@' + RegExp.$2 + ':' + RegExp.$1) + ' -- ' + lines[i + 1].replace(/^\s+/, '');
187            }
188        }
189        
190        lines.splice(j, lines.length - j);
191        return lines;
192    },
193    
194    // Safari, IE, and others
195    other: function(curr) {
196        var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i,
197            stack = [], fn, args, maxStackSize = 10;
198        
199        while (curr && stack.length < maxStackSize) {
200            fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
201            args = Array.prototype.slice.call(curr['arguments']);
202            stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')';
203            curr = curr.caller;
204        }
205        return stack;
206    },
207    
208    /**
209     * Given arguments array as a String, subsituting type names for non-string types.
210     *
211     * @param {Arguments} object
212     * @return {Array} of Strings with stringified arguments
213     */
214    stringifyArguments: function(args) {
215        for (var i = 0; i < args.length; ++i) {
216            var arg = args[i];
217            if (arg === undefined) {
218                args[i] = 'undefined';
219            } else if (arg === null) {
220                args[i] = 'null';
221            } else if (arg.constructor) {
222                if (arg.constructor === Array) {
223                    if (arg.length < 3) {
224                        args[i] = '[' + this.stringifyArguments(arg) + ']';
225                    } else {
226                        args[i] = '[' + this.stringifyArguments(Array.prototype.slice.call(arg, 0, 1)) + '...' + this.stringifyArguments(Array.prototype.slice.call(arg, -1)) + ']';
227                    }
228                } else if (arg.constructor === Object) {
229                    args[i] = '#object';
230                } else if (arg.constructor === Function) {
231                    args[i] = '#function';
232                } else if (arg.constructor === String) {
233                    args[i] = '"' + arg + '"';
234                }
235            }
236        }
237        return args.join(',');
238    },
239    
240    sourceCache: {},
241    
242    /**
243     * @return the text from a given URL.
244     */
245    ajax: function(url) {
246        var req = this.createXMLHTTPObject();
247        if (!req) {
248            return;
249        }
250        req.open('GET', url, false);
251        req.setRequestHeader('User-Agent', 'XMLHTTP/1.0');
252        req.send('');
253        return req.responseText;
254    },
255    
256    /**
257     * Try XHR methods in order and store XHR factory.
258     *
259     * @return <Function> XHR function or equivalent
260     */
261    createXMLHTTPObject: function() {
262        var xmlhttp, XMLHttpFactories = [
263            function() {
264                return new XMLHttpRequest();
265            }, function() {
266                return new ActiveXObject('Msxml2.XMLHTTP');
267            }, function() {
268                return new ActiveXObject('Msxml3.XMLHTTP');
269            }, function() {
270                return new ActiveXObject('Microsoft.XMLHTTP');
271            }
272        ];
273        for (var i = 0; i < XMLHttpFactories.length; i++) {
274            try {
275                xmlhttp = XMLHttpFactories[i]();
276                // Use memoization to cache the factory
277                this.createXMLHTTPObject = XMLHttpFactories[i];
278                return xmlhttp;
279            } catch (e) {}
280        }
281    },
282
283    /**
284     * Given a URL, check if it is in the same domain (so we can get the source
285     * via Ajax).
286     *
287     * @param url <String> source url
288     * @return False if we need a cross-domain request
289     */
290    isSameDomain: function(url) {
291        return url.indexOf(location.hostname) !== -1;
292    },
293    
294    /**
295     * Get source code from given URL if in the same domain.
296     *
297     * @param url <String> JS source URL
298     * @return <Array> Array of source code lines
299     */
300    getSource: function(url) {
301        if (!(url in this.sourceCache)) {
302            this.sourceCache[url] = this.ajax(url).split('\n');
303        }
304        return this.sourceCache[url];
305    },
306    
307    guessFunctions: function(stack) {
308        for (var i = 0; i < stack.length; ++i) {
309            var reStack = /\{anonymous\}\(.*\)@(\w+:\/\/([\-\w\.]+)+(:\d+)?[^:]+):(\d+):?(\d+)?/;
310            var frame = stack[i], m = reStack.exec(frame);
311            if (m) {
312                var file = m[1], lineno = m[4]; //m[7] is character position in Chrome
313                if (file && this.isSameDomain(file) && lineno) {
314                    var functionName = this.guessFunctionName(file, lineno);
315                    stack[i] = frame.replace('{anonymous}', functionName);
316                }
317            }
318        }
319        return stack;
320    },
321    
322    guessFunctionName: function(url, lineNo) {
323        try {
324            return this.guessFunctionNameFromLines(lineNo, this.getSource(url));
325        } catch (e) {
326            return 'getSource failed with url: ' + url + ', exception: ' + e.toString();
327        }
328    },
329    
330    guessFunctionNameFromLines: function(lineNo, source) {
331        var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/;
332        var reGuessFunction = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/;
333        // Walk backwards from the first line in the function until we find the line which
334        // matches the pattern above, which is the function definition
335        var line = "", maxLines = 10;
336        for (var i = 0; i < maxLines; ++i) {
337            line = source[lineNo - i] + line;
338            if (line !== undefined) {
339                var m = reGuessFunction.exec(line);
340                if (m && m[1]) {
341                    return m[1];
342                } else {
343                    m = reFunctionArgNames.exec(line);
344                    if (m && m[1]) {
345                        return m[1];
346                    }
347                }
348            }
349        }
350        return '(?)';
351    }
352};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.