initial commit
This commit is contained in:
Executable
+747
@@ -0,0 +1,747 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright The Closure Library Authors.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Logging and debugging utilities.
|
||||
*
|
||||
* @see ../demos/debug.html
|
||||
*/
|
||||
|
||||
goog.provide('goog.debug');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.debug.errorcontext');
|
||||
|
||||
|
||||
/** @define {boolean} Whether logging should be enabled. */
|
||||
goog.debug.LOGGING_ENABLED =
|
||||
goog.define('goog.debug.LOGGING_ENABLED', goog.DEBUG);
|
||||
|
||||
|
||||
/** @define {boolean} Whether to force "sloppy" stack building. */
|
||||
goog.debug.FORCE_SLOPPY_STACKS =
|
||||
goog.define('goog.debug.FORCE_SLOPPY_STACKS', false);
|
||||
|
||||
|
||||
/**
|
||||
* @define {boolean} TODO(user): Remove this hack once bug is resolved.
|
||||
*/
|
||||
goog.debug.CHECK_FOR_THROWN_EVENT =
|
||||
goog.define('goog.debug.CHECK_FOR_THROWN_EVENT', false);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Catches onerror events fired by windows and similar objects.
|
||||
* @param {function(Object)} logFunc The function to call with the error
|
||||
* information.
|
||||
* @param {boolean=} opt_cancel Whether to stop the error from reaching the
|
||||
* browser.
|
||||
* @param {Object=} opt_target Object that fires onerror events.
|
||||
* @suppress {strictMissingProperties} onerror is not defined as a property
|
||||
* on Object.
|
||||
*/
|
||||
goog.debug.catchErrors = function(logFunc, opt_cancel, opt_target) {
|
||||
'use strict';
|
||||
var target = opt_target || goog.global;
|
||||
var oldErrorHandler = target.onerror;
|
||||
var retVal = !!opt_cancel;
|
||||
|
||||
/**
|
||||
* New onerror handler for this target. This onerror handler follows the spec
|
||||
* according to
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/#runtime-script-errors
|
||||
* The spec was changed in August 2013 to support receiving column information
|
||||
* and an error object for all scripts on the same origin or cross origin
|
||||
* scripts with the proper headers. See
|
||||
* https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
|
||||
*
|
||||
* @param {string} message The error message. For cross-origin errors, this
|
||||
* will be scrubbed to just "Script error.". For new browsers that have
|
||||
* updated to follow the latest spec, errors that come from origins that
|
||||
* have proper cross origin headers will not be scrubbed.
|
||||
* @param {string} url The URL of the script that caused the error. The URL
|
||||
* will be scrubbed to "" for cross origin scripts unless the script has
|
||||
* proper cross origin headers and the browser has updated to the latest
|
||||
* spec.
|
||||
* @param {number} line The line number in the script that the error
|
||||
* occurred on.
|
||||
* @param {number=} opt_col The optional column number that the error
|
||||
* occurred on. Only browsers that have updated to the latest spec will
|
||||
* include this.
|
||||
* @param {Error=} opt_error The optional actual error object for this
|
||||
* error that should include the stack. Only browsers that have updated
|
||||
* to the latest spec will inlude this parameter.
|
||||
* @return {boolean} Whether to prevent the error from reaching the browser.
|
||||
*/
|
||||
target.onerror = function(message, url, line, opt_col, opt_error) {
|
||||
'use strict';
|
||||
if (oldErrorHandler) {
|
||||
oldErrorHandler(message, url, line, opt_col, opt_error);
|
||||
}
|
||||
logFunc({
|
||||
message: message,
|
||||
fileName: url,
|
||||
line: line,
|
||||
lineNumber: line,
|
||||
col: opt_col,
|
||||
error: opt_error
|
||||
});
|
||||
return retVal;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a string representing an object and all its properties.
|
||||
* @param {Object|null|undefined} obj Object to expose.
|
||||
* @param {boolean=} opt_showFn Show the functions as well as the properties,
|
||||
* default is false.
|
||||
* @return {string} The string representation of `obj`.
|
||||
*/
|
||||
goog.debug.expose = function(obj, opt_showFn) {
|
||||
'use strict';
|
||||
if (typeof obj == 'undefined') {
|
||||
return 'undefined';
|
||||
}
|
||||
if (obj == null) {
|
||||
return 'NULL';
|
||||
}
|
||||
var str = [];
|
||||
|
||||
for (var x in obj) {
|
||||
if (!opt_showFn && typeof obj[x] === 'function') {
|
||||
continue;
|
||||
}
|
||||
var s = x + ' = ';
|
||||
|
||||
try {
|
||||
s += obj[x];
|
||||
} catch (e) {
|
||||
s += '*** ' + e + ' ***';
|
||||
}
|
||||
str.push(s);
|
||||
}
|
||||
return str.join('\n');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a string representing a given primitive or object, and for an
|
||||
* object, all its properties and nested objects. NOTE: The output will include
|
||||
* Uids on all objects that were exposed. Any added Uids will be removed before
|
||||
* returning.
|
||||
* @param {*} obj Object to expose.
|
||||
* @param {boolean=} opt_showFn Also show properties that are functions (by
|
||||
* default, functions are omitted).
|
||||
* @return {string} A string representation of `obj`.
|
||||
*/
|
||||
goog.debug.deepExpose = function(obj, opt_showFn) {
|
||||
'use strict';
|
||||
var str = [];
|
||||
|
||||
// Track any objects where deepExpose added a Uid, so they can be cleaned up
|
||||
// before return. We do this globally, rather than only on ancestors so that
|
||||
// if the same object appears in the output, you can see it.
|
||||
var uidsToCleanup = [];
|
||||
var ancestorUids = {};
|
||||
|
||||
var helper = function(obj, space) {
|
||||
'use strict';
|
||||
var nestspace = space + ' ';
|
||||
|
||||
var indentMultiline = function(str) {
|
||||
'use strict';
|
||||
return str.replace(/\n/g, '\n' + space);
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
if (obj === undefined) {
|
||||
str.push('undefined');
|
||||
} else if (obj === null) {
|
||||
str.push('NULL');
|
||||
} else if (typeof obj === 'string') {
|
||||
str.push('"' + indentMultiline(obj) + '"');
|
||||
} else if (typeof obj === 'function') {
|
||||
str.push(indentMultiline(String(obj)));
|
||||
} else if (goog.isObject(obj)) {
|
||||
// Add a Uid if needed. The struct calls implicitly adds them.
|
||||
if (!goog.hasUid(obj)) {
|
||||
uidsToCleanup.push(obj);
|
||||
}
|
||||
var uid = goog.getUid(obj);
|
||||
if (ancestorUids[uid]) {
|
||||
str.push('*** reference loop detected (id=' + uid + ') ***');
|
||||
} else {
|
||||
ancestorUids[uid] = true;
|
||||
str.push('{');
|
||||
for (var x in obj) {
|
||||
if (!opt_showFn && typeof obj[x] === 'function') {
|
||||
continue;
|
||||
}
|
||||
str.push('\n');
|
||||
str.push(nestspace);
|
||||
str.push(x + ' = ');
|
||||
helper(obj[x], nestspace);
|
||||
}
|
||||
str.push('\n' + space + '}');
|
||||
delete ancestorUids[uid];
|
||||
}
|
||||
} else {
|
||||
str.push(obj);
|
||||
}
|
||||
} catch (e) {
|
||||
str.push('*** ' + e + ' ***');
|
||||
}
|
||||
};
|
||||
|
||||
helper(obj, '');
|
||||
|
||||
// Cleanup any Uids that were added by the deepExpose.
|
||||
for (var i = 0; i < uidsToCleanup.length; i++) {
|
||||
goog.removeUid(uidsToCleanup[i]);
|
||||
}
|
||||
|
||||
return str.join('');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Recursively outputs a nested array as a string.
|
||||
* @param {Array<?>} arr The array.
|
||||
* @return {string} String representing nested array.
|
||||
*/
|
||||
goog.debug.exposeArray = function(arr) {
|
||||
'use strict';
|
||||
var str = [];
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (Array.isArray(arr[i])) {
|
||||
str.push(goog.debug.exposeArray(arr[i]));
|
||||
} else {
|
||||
str.push(arr[i]);
|
||||
}
|
||||
}
|
||||
return '[ ' + str.join(', ') + ' ]';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Normalizes the error/exception object between browsers.
|
||||
* @param {*} err Raw error object.
|
||||
* @return {{
|
||||
* message: (?|undefined),
|
||||
* name: (?|undefined),
|
||||
* lineNumber: (?|undefined),
|
||||
* fileName: (?|undefined),
|
||||
* stack: (?|undefined)
|
||||
* }} Representation of err as an Object. It will never return err.
|
||||
* @suppress {strictMissingProperties} properties not defined on err
|
||||
*/
|
||||
goog.debug.normalizeErrorObject = function(err) {
|
||||
'use strict';
|
||||
var href = goog.getObjectByName('window.location.href');
|
||||
if (err == null) {
|
||||
err = 'Unknown Error of type "null/undefined"';
|
||||
}
|
||||
if (typeof err === 'string') {
|
||||
return {
|
||||
'message': err,
|
||||
'name': 'Unknown error',
|
||||
'lineNumber': 'Not available',
|
||||
'fileName': href,
|
||||
'stack': 'Not available'
|
||||
};
|
||||
}
|
||||
|
||||
var lineNumber, fileName;
|
||||
var threwError = false;
|
||||
|
||||
try {
|
||||
lineNumber = err.lineNumber || err.line || 'Not available';
|
||||
} catch (e) {
|
||||
// Firefox 2 sometimes throws an error when accessing 'lineNumber':
|
||||
// Message: Permission denied to get property UnnamedClass.lineNumber
|
||||
lineNumber = 'Not available';
|
||||
threwError = true;
|
||||
}
|
||||
|
||||
try {
|
||||
fileName = err.fileName || err.filename || err.sourceURL ||
|
||||
// $googDebugFname may be set before a call to eval to set the filename
|
||||
// that the eval is supposed to present.
|
||||
goog.global['$googDebugFname'] || href;
|
||||
} catch (e) {
|
||||
// Firefox 2 may also throw an error when accessing 'filename'.
|
||||
fileName = 'Not available';
|
||||
threwError = true;
|
||||
}
|
||||
|
||||
var stack = goog.debug.serializeErrorStack_(err);
|
||||
|
||||
// The IE Error object contains only the name and the message.
|
||||
// The Safari Error object uses the line and sourceURL fields.
|
||||
if (threwError || !err.lineNumber || !err.fileName || !err.stack ||
|
||||
!err.message || !err.name) {
|
||||
var message = err.message;
|
||||
if (message == null) {
|
||||
if (err.constructor && err.constructor instanceof Function) {
|
||||
var ctorName = err.constructor.name ?
|
||||
err.constructor.name :
|
||||
goog.debug.getFunctionName(err.constructor);
|
||||
message = 'Unknown Error of type "' + ctorName + '"';
|
||||
// TODO(user): Remove this hack once bug is resolved.
|
||||
if (goog.debug.CHECK_FOR_THROWN_EVENT && ctorName == 'Event') {
|
||||
try {
|
||||
message = message + ' with Event.type "' + (err.type || '') + '"';
|
||||
} catch (e) {
|
||||
// Just give up on getting more information out of the error object.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
message = 'Unknown Error of unknown type';
|
||||
}
|
||||
|
||||
// Avoid TypeError since toString could be missing from the instance
|
||||
// (e.g. if created Object.create(null)).
|
||||
if (typeof err.toString === 'function' &&
|
||||
Object.prototype.toString !== err.toString) {
|
||||
message += ': ' + err.toString();
|
||||
}
|
||||
}
|
||||
return {
|
||||
'message': message,
|
||||
'name': err.name || 'UnknownError',
|
||||
'lineNumber': lineNumber,
|
||||
'fileName': fileName,
|
||||
'stack': stack || 'Not available'
|
||||
};
|
||||
}
|
||||
// Standards error object
|
||||
// Typed !Object. Should be a subtype of the return type, but it's not.
|
||||
err.stack = stack;
|
||||
|
||||
// Return non-standard error to allow for consistent result (eg. enumerable).
|
||||
return {
|
||||
'message': err.message,
|
||||
'name': err.name,
|
||||
'lineNumber': err.lineNumber,
|
||||
'fileName': err.fileName,
|
||||
'stack': err.stack
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Serialize stack by including the cause chain of the exception if it exists.
|
||||
*
|
||||
*
|
||||
* @param {*} e an exception that may have a cause
|
||||
* @param {!Object=} seen set of cause that have already been serialized
|
||||
* @return {string}
|
||||
* @private
|
||||
* @suppress {missingProperties} properties not defined on cause and e
|
||||
*/
|
||||
goog.debug.serializeErrorStack_ = function(e, seen) {
|
||||
'use strict';
|
||||
if (!seen) {
|
||||
seen = {};
|
||||
}
|
||||
seen[goog.debug.serializeErrorAsKey_(e)] = true;
|
||||
|
||||
var stack = e['stack'] || '';
|
||||
|
||||
// Add cause if exists.
|
||||
var cause = e.cause;
|
||||
if (cause && !seen[goog.debug.serializeErrorAsKey_(cause)]) {
|
||||
stack += '\nCaused by: ';
|
||||
// Some browsers like Chrome add the error message as the first frame of the
|
||||
// stack, In this case we don't need to add it. Note: we don't use
|
||||
// String.startsWith method because it might have to be polyfilled.
|
||||
if (!cause.stack || cause.stack.indexOf(cause.toString()) != 0) {
|
||||
stack += (typeof cause === 'string') ? cause : cause.message + '\n';
|
||||
}
|
||||
stack += goog.debug.serializeErrorStack_(cause, seen);
|
||||
}
|
||||
|
||||
return stack;
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize an error to a string key.
|
||||
* @param {*} e an exception
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
goog.debug.serializeErrorAsKey_ = function(e) {
|
||||
'use strict';
|
||||
var keyPrefix = '';
|
||||
|
||||
if (typeof e.toString === 'function') {
|
||||
keyPrefix = '' + e;
|
||||
}
|
||||
|
||||
return keyPrefix + e['stack'];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts an object to an Error using the object's toString if it's not
|
||||
* already an Error, adds a stacktrace if there isn't one, and optionally adds
|
||||
* an extra message.
|
||||
* @param {*} err The original thrown error, object, or string.
|
||||
* @param {string=} opt_message optional additional message to add to the
|
||||
* error.
|
||||
* @return {!Error} If err is an Error, it is enhanced and returned. Otherwise,
|
||||
* it is converted to an Error which is enhanced and returned.
|
||||
*/
|
||||
goog.debug.enhanceError = function(err, opt_message) {
|
||||
'use strict';
|
||||
var error;
|
||||
if (!(err instanceof Error)) {
|
||||
error = Error(err);
|
||||
if (Error.captureStackTrace) {
|
||||
// Trim this function off the call stack, if we can.
|
||||
Error.captureStackTrace(error, goog.debug.enhanceError);
|
||||
}
|
||||
} else {
|
||||
error = err;
|
||||
}
|
||||
|
||||
if (!error.stack) {
|
||||
error.stack = goog.debug.getStacktrace(goog.debug.enhanceError);
|
||||
}
|
||||
if (opt_message) {
|
||||
// find the first unoccupied 'messageX' property
|
||||
var x = 0;
|
||||
while (error['message' + x]) {
|
||||
++x;
|
||||
}
|
||||
error['message' + x] = String(opt_message);
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts an object to an Error using the object's toString if it's not
|
||||
* already an Error, adds a stacktrace if there isn't one, and optionally adds
|
||||
* context to the Error, which is reported by the closure error reporter.
|
||||
* @param {*} err The original thrown error, object, or string.
|
||||
* @param {!Object<string, string>=} opt_context Key-value context to add to the
|
||||
* Error.
|
||||
* @return {!Error} If err is an Error, it is enhanced and returned. Otherwise,
|
||||
* it is converted to an Error which is enhanced and returned.
|
||||
*/
|
||||
goog.debug.enhanceErrorWithContext = function(err, opt_context) {
|
||||
'use strict';
|
||||
var error = goog.debug.enhanceError(err);
|
||||
if (opt_context) {
|
||||
for (var key in opt_context) {
|
||||
goog.debug.errorcontext.addErrorContext(error, key, opt_context[key]);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the current stack trace. Simple and iterative - doesn't worry about
|
||||
* catching circular references or getting the args.
|
||||
* @param {number=} opt_depth Optional maximum depth to trace back to.
|
||||
* @return {string} A string with the function names of all functions in the
|
||||
* stack, separated by \n.
|
||||
* @suppress {es5Strict}
|
||||
*/
|
||||
goog.debug.getStacktraceSimple = function(opt_depth) {
|
||||
'use strict';
|
||||
if (!goog.debug.FORCE_SLOPPY_STACKS) {
|
||||
var stack = goog.debug.getNativeStackTrace_(goog.debug.getStacktraceSimple);
|
||||
if (stack) {
|
||||
return stack;
|
||||
}
|
||||
// NOTE: browsers that have strict mode support also have native "stack"
|
||||
// properties. Fall-through for legacy browser support.
|
||||
}
|
||||
|
||||
var sb = [];
|
||||
var fn = arguments.callee.caller;
|
||||
var depth = 0;
|
||||
|
||||
while (fn && (!opt_depth || depth < opt_depth)) {
|
||||
sb.push(goog.debug.getFunctionName(fn));
|
||||
sb.push('()\n');
|
||||
|
||||
try {
|
||||
fn = fn.caller;
|
||||
} catch (e) {
|
||||
sb.push('[exception trying to get caller]\n');
|
||||
break;
|
||||
}
|
||||
depth++;
|
||||
if (depth >= goog.debug.MAX_STACK_DEPTH) {
|
||||
sb.push('[...long stack...]');
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (opt_depth && depth >= opt_depth) {
|
||||
sb.push('[...reached max depth limit...]');
|
||||
} else {
|
||||
sb.push('[end]');
|
||||
}
|
||||
|
||||
return sb.join('');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Max length of stack to try and output
|
||||
* @type {number}
|
||||
*/
|
||||
goog.debug.MAX_STACK_DEPTH = 50;
|
||||
|
||||
|
||||
/**
|
||||
* @param {Function} fn The function to start getting the trace from.
|
||||
* @return {?string}
|
||||
* @private
|
||||
*/
|
||||
goog.debug.getNativeStackTrace_ = function(fn) {
|
||||
'use strict';
|
||||
var tempErr = new Error();
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(tempErr, fn);
|
||||
return String(tempErr.stack);
|
||||
} else {
|
||||
// IE10, only adds stack traces when an exception is thrown.
|
||||
try {
|
||||
throw tempErr;
|
||||
} catch (e) {
|
||||
tempErr = e;
|
||||
}
|
||||
var stack = tempErr.stack;
|
||||
if (stack) {
|
||||
return String(stack);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the current stack trace, either starting from the caller or starting
|
||||
* from a specified function that's currently on the call stack.
|
||||
* @param {?Function=} fn If provided, when collecting the stack trace all
|
||||
* frames above the topmost call to this function, including that call,
|
||||
* will be left out of the stack trace.
|
||||
* @return {string} Stack trace.
|
||||
* @suppress {es5Strict}
|
||||
*/
|
||||
goog.debug.getStacktrace = function(fn) {
|
||||
'use strict';
|
||||
var stack;
|
||||
if (!goog.debug.FORCE_SLOPPY_STACKS) {
|
||||
// Try to get the stack trace from the environment if it is available.
|
||||
var contextFn = fn || goog.debug.getStacktrace;
|
||||
stack = goog.debug.getNativeStackTrace_(contextFn);
|
||||
}
|
||||
if (!stack) {
|
||||
// NOTE: browsers that have strict mode support also have native "stack"
|
||||
// properties. This function will throw in strict mode.
|
||||
stack = goog.debug.getStacktraceHelper_(fn || arguments.callee.caller, []);
|
||||
}
|
||||
return stack;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Private helper for getStacktrace().
|
||||
* @param {?Function} fn If provided, when collecting the stack trace all
|
||||
* frames above the topmost call to this function, including that call,
|
||||
* will be left out of the stack trace.
|
||||
* @param {Array<!Function>} visited List of functions visited so far.
|
||||
* @return {string} Stack trace starting from function fn.
|
||||
* @suppress {es5Strict}
|
||||
* @private
|
||||
*/
|
||||
goog.debug.getStacktraceHelper_ = function(fn, visited) {
|
||||
'use strict';
|
||||
var sb = [];
|
||||
|
||||
// Circular reference, certain functions like bind seem to cause a recursive
|
||||
// loop so we need to catch circular references
|
||||
if (goog.array.contains(visited, fn)) {
|
||||
sb.push('[...circular reference...]');
|
||||
|
||||
// Traverse the call stack until function not found or max depth is reached
|
||||
} else if (fn && visited.length < goog.debug.MAX_STACK_DEPTH) {
|
||||
sb.push(goog.debug.getFunctionName(fn) + '(');
|
||||
var args = fn.arguments;
|
||||
// Args may be null for some special functions such as host objects or eval.
|
||||
for (var i = 0; args && i < args.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.push(', ');
|
||||
}
|
||||
var argDesc;
|
||||
var arg = args[i];
|
||||
switch (typeof arg) {
|
||||
case 'object':
|
||||
argDesc = arg ? 'object' : 'null';
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
argDesc = arg;
|
||||
break;
|
||||
|
||||
case 'number':
|
||||
argDesc = String(arg);
|
||||
break;
|
||||
|
||||
case 'boolean':
|
||||
argDesc = arg ? 'true' : 'false';
|
||||
break;
|
||||
|
||||
case 'function':
|
||||
argDesc = goog.debug.getFunctionName(arg);
|
||||
argDesc = argDesc ? argDesc : '[fn]';
|
||||
break;
|
||||
|
||||
case 'undefined':
|
||||
default:
|
||||
argDesc = typeof arg;
|
||||
break;
|
||||
}
|
||||
|
||||
if (argDesc.length > 40) {
|
||||
argDesc = argDesc.substr(0, 40) + '...';
|
||||
}
|
||||
sb.push(argDesc);
|
||||
}
|
||||
visited.push(fn);
|
||||
sb.push(')\n');
|
||||
|
||||
try {
|
||||
sb.push(goog.debug.getStacktraceHelper_(fn.caller, visited));
|
||||
} catch (e) {
|
||||
sb.push('[exception trying to get caller]\n');
|
||||
}
|
||||
|
||||
} else if (fn) {
|
||||
sb.push('[...long stack...]');
|
||||
} else {
|
||||
sb.push('[end]');
|
||||
}
|
||||
return sb.join('');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets a function name
|
||||
* @param {Function} fn Function to get name of.
|
||||
* @return {string} Function's name.
|
||||
*/
|
||||
goog.debug.getFunctionName = function(fn) {
|
||||
'use strict';
|
||||
if (goog.debug.fnNameCache_[fn]) {
|
||||
return goog.debug.fnNameCache_[fn];
|
||||
}
|
||||
|
||||
// Heuristically determine function name based on code.
|
||||
var functionSource = String(fn);
|
||||
if (!goog.debug.fnNameCache_[functionSource]) {
|
||||
var matches = /function\s+([^\(]+)/m.exec(functionSource);
|
||||
if (matches) {
|
||||
var method = matches[1];
|
||||
goog.debug.fnNameCache_[functionSource] = method;
|
||||
} else {
|
||||
goog.debug.fnNameCache_[functionSource] = '[Anonymous]';
|
||||
}
|
||||
}
|
||||
|
||||
return goog.debug.fnNameCache_[functionSource];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Makes whitespace visible by replacing it with printable characters.
|
||||
* This is useful in finding diffrences between the expected and the actual
|
||||
* output strings of a testcase.
|
||||
* @param {string} string whose whitespace needs to be made visible.
|
||||
* @return {string} string whose whitespace is made visible.
|
||||
*/
|
||||
goog.debug.makeWhitespaceVisible = function(string) {
|
||||
'use strict';
|
||||
return string.replace(/ /g, '[_]')
|
||||
.replace(/\f/g, '[f]')
|
||||
.replace(/\n/g, '[n]\n')
|
||||
.replace(/\r/g, '[r]')
|
||||
.replace(/\t/g, '[t]');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of a value. If a constructor is passed, and a suitable
|
||||
* string cannot be found, 'unknown type name' will be returned.
|
||||
*
|
||||
* <p>Forked rather than moved from {@link goog.asserts.getType_}
|
||||
* to avoid adding a dependency to goog.asserts.
|
||||
* @param {*} value A constructor, object, or primitive.
|
||||
* @return {string} The best display name for the value, or 'unknown type name'.
|
||||
*/
|
||||
goog.debug.runtimeType = function(value) {
|
||||
'use strict';
|
||||
if (value instanceof Function) {
|
||||
return value.displayName || value.name || 'unknown type name';
|
||||
} else if (value instanceof Object) {
|
||||
return /** @type {string} */ (value.constructor.displayName) ||
|
||||
value.constructor.name || Object.prototype.toString.call(value);
|
||||
} else {
|
||||
return value === null ? 'null' : typeof value;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hash map for storing function names that have already been looked up.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
goog.debug.fnNameCache_ = {};
|
||||
|
||||
|
||||
/**
|
||||
* Private internal function to support goog.debug.freeze.
|
||||
* @param {T} arg
|
||||
* @return {T}
|
||||
* @template T
|
||||
* @private
|
||||
*/
|
||||
goog.debug.freezeInternal_ = goog.DEBUG && Object.freeze || function(arg) {
|
||||
'use strict';
|
||||
return arg;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Freezes the given object, but only in debug mode (and in browsers that
|
||||
* support it). Note that this is a shallow freeze, so for deeply nested
|
||||
* objects it must be called at every level to ensure deep immutability.
|
||||
* @param {T} arg
|
||||
* @return {T}
|
||||
* @template T
|
||||
*/
|
||||
goog.debug.freeze = function(arg) {
|
||||
'use strict';
|
||||
// NOTE: this compiles to nothing, but hides the possible side effect of
|
||||
// freezeInternal_ from the compiler so that the entire call can be
|
||||
// removed if the result is not used.
|
||||
return {
|
||||
valueOf: function() {
|
||||
'use strict';
|
||||
return goog.debug.freezeInternal_(arg);
|
||||
}
|
||||
}.valueOf();
|
||||
};
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright The Closure Library Authors.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview A global registry for entry points into a program,
|
||||
* so that they can be instrumented. Each module should register their
|
||||
* entry points with this registry. Designed to be compiled out
|
||||
* if no instrumentation is requested.
|
||||
*
|
||||
* Entry points may be registered before or after a call to
|
||||
* goog.debug.entryPointRegistry.monitorAll. If an entry point is registered
|
||||
* later, the existing monitor will instrument the new entry point.
|
||||
*/
|
||||
|
||||
goog.provide('goog.debug.EntryPointMonitor');
|
||||
goog.provide('goog.debug.entryPointRegistry');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*/
|
||||
goog.debug.entryPointRegistry.EntryPointMonitor = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Instruments a function.
|
||||
*
|
||||
* @param {!Function} fn A function to instrument.
|
||||
* @return {!Function} The instrumented function.
|
||||
*/
|
||||
goog.debug.entryPointRegistry.EntryPointMonitor.prototype.wrap;
|
||||
|
||||
|
||||
/**
|
||||
* Try to remove an instrumentation wrapper created by this monitor.
|
||||
* If the function passed to unwrap is not a wrapper created by this
|
||||
* monitor, then we will do nothing.
|
||||
*
|
||||
* Notice that some wrappers may not be unwrappable. For example, if other
|
||||
* monitors have applied their own wrappers, then it will be impossible to
|
||||
* unwrap them because their wrappers will have captured our wrapper.
|
||||
*
|
||||
* So it is important that entry points are unwrapped in the reverse
|
||||
* order that they were wrapped.
|
||||
*
|
||||
* @param {!Function} fn A function to unwrap.
|
||||
* @return {!Function} The unwrapped function, or `fn` if it was not
|
||||
* a wrapped function created by this monitor.
|
||||
*/
|
||||
goog.debug.entryPointRegistry.EntryPointMonitor.prototype.unwrap;
|
||||
|
||||
/**
|
||||
* Alias for goog.debug.entryPointRegistry.EntryPointMonitor, for compatibility
|
||||
* purposes.
|
||||
* @const
|
||||
*/
|
||||
goog.debug.EntryPointMonitor = goog.debug.entryPointRegistry.EntryPointMonitor;
|
||||
|
||||
|
||||
/**
|
||||
* An array of entry point callbacks.
|
||||
* @type {!Array<function(!Function)>}
|
||||
* @private
|
||||
*/
|
||||
goog.debug.entryPointRegistry.refList_ = [];
|
||||
|
||||
|
||||
/**
|
||||
* Monitors that should wrap all the entry points.
|
||||
* @type {!Array<!goog.debug.EntryPointMonitor>}
|
||||
* @private
|
||||
*/
|
||||
goog.debug.entryPointRegistry.monitors_ = [];
|
||||
|
||||
|
||||
/**
|
||||
* Whether goog.debug.entryPointRegistry.monitorAll has ever been called.
|
||||
* Checking this allows the compiler to optimize out the registrations.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.debug.entryPointRegistry.monitorsMayExist_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Register an entry point with this module.
|
||||
*
|
||||
* The entry point will be instrumented when a monitor is passed to
|
||||
* goog.debug.entryPointRegistry.monitorAll. If this has already occurred, the
|
||||
* entry point is instrumented immediately.
|
||||
*
|
||||
* @param {function(!Function)} callback A callback function which is called
|
||||
* with a transforming function to instrument the entry point. The callback
|
||||
* is responsible for wrapping the relevant entry point with the
|
||||
* transforming function.
|
||||
*/
|
||||
goog.debug.entryPointRegistry.register = function(callback) {
|
||||
'use strict';
|
||||
// Don't use push(), so that this can be compiled out.
|
||||
goog.debug.entryPointRegistry
|
||||
.refList_[goog.debug.entryPointRegistry.refList_.length] = callback;
|
||||
// If no one calls monitorAll, this can be compiled out.
|
||||
if (goog.debug.entryPointRegistry.monitorsMayExist_) {
|
||||
var monitors = goog.debug.entryPointRegistry.monitors_;
|
||||
for (var i = 0; i < monitors.length; i++) {
|
||||
callback(goog.bind(monitors[i].wrap, monitors[i]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Configures a monitor to wrap all entry points.
|
||||
*
|
||||
* Entry points that have already been registered are immediately wrapped by
|
||||
* the monitor. When an entry point is registered in the future, it will also
|
||||
* be wrapped by the monitor when it is registered.
|
||||
*
|
||||
* @param {!goog.debug.EntryPointMonitor} monitor An entry point monitor.
|
||||
*/
|
||||
goog.debug.entryPointRegistry.monitorAll = function(monitor) {
|
||||
'use strict';
|
||||
goog.debug.entryPointRegistry.monitorsMayExist_ = true;
|
||||
var transformer = goog.bind(monitor.wrap, monitor);
|
||||
for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
|
||||
goog.debug.entryPointRegistry.refList_[i](transformer);
|
||||
}
|
||||
goog.debug.entryPointRegistry.monitors_.push(monitor);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Try to unmonitor all the entry points that have already been registered. If
|
||||
* an entry point is registered in the future, it will not be wrapped by the
|
||||
* monitor when it is registered. Note that this may fail if the entry points
|
||||
* have additional wrapping.
|
||||
*
|
||||
* @param {!goog.debug.EntryPointMonitor} monitor The last monitor to wrap
|
||||
* the entry points.
|
||||
* @throws {Error} If the monitor is not the most recently configured monitor.
|
||||
*/
|
||||
goog.debug.entryPointRegistry.unmonitorAllIfPossible = function(monitor) {
|
||||
'use strict';
|
||||
var monitors = goog.debug.entryPointRegistry.monitors_;
|
||||
goog.asserts.assert(
|
||||
monitor == monitors[monitors.length - 1],
|
||||
'Only the most recent monitor can be unwrapped.');
|
||||
var transformer = goog.bind(monitor.unwrap, monitor);
|
||||
for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
|
||||
goog.debug.entryPointRegistry.refList_[i](transformer);
|
||||
}
|
||||
monitors.length--;
|
||||
};
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright The Closure Library Authors.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Provides a base class for custom Error objects such that the
|
||||
* stack is correctly maintained.
|
||||
*
|
||||
* You should never need to throw DebugError(msg) directly, Error(msg) is
|
||||
* sufficient.
|
||||
*/
|
||||
|
||||
goog.module('goog.debug.Error');
|
||||
goog.module.declareLegacyNamespace();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Base class for custom error objects.
|
||||
* @param {*=} msg The message associated with the error.
|
||||
* @param {{
|
||||
* message: (?|undefined),
|
||||
* name: (?|undefined),
|
||||
* lineNumber: (?|undefined),
|
||||
* fileName: (?|undefined),
|
||||
* stack: (?|undefined),
|
||||
* cause: (?|undefined),
|
||||
* }=} cause The original error object to chain with.
|
||||
* @constructor
|
||||
* @extends {Error}
|
||||
*/
|
||||
function DebugError(msg = undefined, cause = undefined) {
|
||||
// Attempt to ensure there is a stack trace.
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, DebugError);
|
||||
} else {
|
||||
const stack = new Error().stack;
|
||||
if (stack) {
|
||||
/** @override */
|
||||
this.stack = stack;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg) {
|
||||
/** @override */
|
||||
this.message = String(msg);
|
||||
}
|
||||
|
||||
if (cause !== undefined) {
|
||||
/** @type {?} */
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to report this error to the server. Setting this to false will
|
||||
* cause the error reporter to not report the error back to the server,
|
||||
* which can be useful if the client knows that the error has already been
|
||||
* logged on the server.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.reportErrorToServer = true;
|
||||
}
|
||||
goog.inherits(DebugError, Error);
|
||||
|
||||
|
||||
/** @override @type {string} */
|
||||
DebugError.prototype.name = 'CustomError';
|
||||
|
||||
|
||||
exports = DebugError;
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright The Closure Library Authors.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Provides methods dealing with context on error objects.
|
||||
*/
|
||||
|
||||
goog.provide('goog.debug.errorcontext');
|
||||
|
||||
|
||||
/**
|
||||
* Adds key-value context to the error.
|
||||
* @param {!Error} err The error to add context to.
|
||||
* @param {string} contextKey Key for the context to be added.
|
||||
* @param {string} contextValue Value for the context to be added.
|
||||
*/
|
||||
goog.debug.errorcontext.addErrorContext = function(
|
||||
err, contextKey, contextValue) {
|
||||
'use strict';
|
||||
if (!err[goog.debug.errorcontext.CONTEXT_KEY_]) {
|
||||
err[goog.debug.errorcontext.CONTEXT_KEY_] = {};
|
||||
}
|
||||
err[goog.debug.errorcontext.CONTEXT_KEY_][contextKey] = contextValue;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!Error} err The error to get context from.
|
||||
* @return {!Object<string, string>} The context of the provided error.
|
||||
*/
|
||||
goog.debug.errorcontext.getErrorContext = function(err) {
|
||||
'use strict';
|
||||
return err[goog.debug.errorcontext.CONTEXT_KEY_] || {};
|
||||
};
|
||||
|
||||
|
||||
// TODO(user): convert this to a Symbol once goog.debug.ErrorReporter is
|
||||
// able to use ES6.
|
||||
/** @private @const {string} */
|
||||
goog.debug.errorcontext.CONTEXT_KEY_ = '__closure__error__context__984382';
|
||||
Executable
+364
@@ -0,0 +1,364 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright The Closure Library Authors.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Error handling utilities.
|
||||
*/
|
||||
|
||||
goog.provide('goog.debug.ErrorHandler');
|
||||
goog.provide('goog.debug.ErrorHandler.ProtectedFunctionError');
|
||||
|
||||
goog.require('goog.Disposable');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.debug.EntryPointMonitor');
|
||||
goog.require('goog.debug.Error');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The ErrorHandler can be used to to wrap functions with a try/catch
|
||||
* statement. If an exception is thrown, the given error handler function will
|
||||
* be called.
|
||||
*
|
||||
* When this object is disposed, it will stop handling exceptions and tracing.
|
||||
* It will also try to restore window.setTimeout and window.setInterval
|
||||
* if it wrapped them. Notice that in the general case, it is not technically
|
||||
* possible to remove the wrapper, because functions have no knowledge of
|
||||
* what they have been assigned to. So the app is responsible for other
|
||||
* forms of unwrapping.
|
||||
*
|
||||
* @param {Function} handler Handler for exceptions.
|
||||
* @constructor
|
||||
* @extends {goog.Disposable}
|
||||
* @implements {goog.debug.EntryPointMonitor}
|
||||
*/
|
||||
goog.debug.ErrorHandler = function(handler) {
|
||||
'use strict';
|
||||
goog.debug.ErrorHandler.base(this, 'constructor');
|
||||
|
||||
/**
|
||||
* Handler for exceptions, which can do logging, reporting, etc.
|
||||
* @type {Function}
|
||||
* @private
|
||||
*/
|
||||
this.errorHandlerFn_ = handler;
|
||||
|
||||
/**
|
||||
* Whether errors should be wrapped in
|
||||
* goog.debug.ErrorHandler.ProtectedFunctionError before rethrowing.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.wrapErrors_ = true; // TODO(malteubl) Change default.
|
||||
|
||||
/**
|
||||
* Whether to add a prefix to all error messages. The prefix is
|
||||
* goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX. This option
|
||||
* only has an effect if this.wrapErrors_ is set to false.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.prefixErrorMessages_ = false;
|
||||
};
|
||||
goog.inherits(goog.debug.ErrorHandler, goog.Disposable);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.debug.ErrorHandler.prototype.wrap = function(fn) {
|
||||
'use strict';
|
||||
return this.protectEntryPoint(goog.asserts.assertFunction(fn));
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.debug.ErrorHandler.prototype.unwrap = function(fn) {
|
||||
'use strict';
|
||||
goog.asserts.assertFunction(fn);
|
||||
return fn[this.getFunctionIndex_(false)] || fn;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the index for a function. Used for internal indexing.
|
||||
* @param {boolean} wrapper True for the wrapper; false for the wrapped.
|
||||
* @return {string} The index where we should store the function in its
|
||||
* wrapper/wrapped function.
|
||||
* @private
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.getFunctionIndex_ = function(wrapper) {
|
||||
'use strict';
|
||||
return (wrapper ? '__wrapper_' : '__protected_') + goog.getUid(this) + '__';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Installs exception protection for an entry point function. When an exception
|
||||
* is thrown from a protected function, a handler will be invoked to handle it.
|
||||
*
|
||||
* @param {Function} fn An entry point function to be protected.
|
||||
* @return {!Function} A protected wrapper function that calls the entry point
|
||||
* function.
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.protectEntryPoint = function(fn) {
|
||||
'use strict';
|
||||
var protectedFnName = this.getFunctionIndex_(true);
|
||||
if (!fn[protectedFnName]) {
|
||||
var wrapper = fn[protectedFnName] = this.getProtectedFunction(fn);
|
||||
wrapper[this.getFunctionIndex_(false)] = fn;
|
||||
}
|
||||
return fn[protectedFnName];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helps {@link #protectEntryPoint} by actually creating the protected
|
||||
* wrapper function, after {@link #protectEntryPoint} determines that one does
|
||||
* not already exist for the given function. Can be overridden by subclasses
|
||||
* that may want to implement different error handling, or add additional
|
||||
* entry point hooks.
|
||||
* @param {!Function} fn An entry point function to be protected.
|
||||
* @return {!Function} protected wrapper function.
|
||||
* @protected
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.getProtectedFunction = function(fn) {
|
||||
'use strict';
|
||||
var that = this;
|
||||
var googDebugErrorHandlerProtectedFunction = function() {
|
||||
'use strict';
|
||||
var self = /** @type {?} */ (this);
|
||||
if (that.isDisposed()) {
|
||||
return fn.apply(self, arguments);
|
||||
}
|
||||
|
||||
try {
|
||||
return fn.apply(self, arguments);
|
||||
} catch (e) {
|
||||
that.handleError_(e);
|
||||
}
|
||||
};
|
||||
googDebugErrorHandlerProtectedFunction[this.getFunctionIndex_(false)] = fn;
|
||||
return googDebugErrorHandlerProtectedFunction;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Internal error handler.
|
||||
* @param {?} e The error string or an Error-like object.
|
||||
* @private
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.handleError_ = function(e) {
|
||||
'use strict';
|
||||
// Don't re-report errors that have already been handled by this code.
|
||||
var MESSAGE_PREFIX =
|
||||
goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX;
|
||||
if ((e && typeof e === 'object' && typeof e.message === 'string' &&
|
||||
e.message.indexOf(MESSAGE_PREFIX) == 0) ||
|
||||
(typeof e === 'string' && e.indexOf(MESSAGE_PREFIX) == 0)) {
|
||||
return;
|
||||
}
|
||||
this.errorHandlerFn_(e);
|
||||
if (!this.wrapErrors_) {
|
||||
// Add the prefix to the existing message.
|
||||
if (this.prefixErrorMessages_) {
|
||||
if (typeof e === 'object' && e && typeof e.message === 'string') {
|
||||
/** @type {{message}} */ (e).message = MESSAGE_PREFIX + e.message;
|
||||
} else {
|
||||
e = MESSAGE_PREFIX + e;
|
||||
}
|
||||
}
|
||||
if (goog.DEBUG) {
|
||||
// Work around for https://code.google.com/p/v8/issues/detail?id=2625
|
||||
// and https://code.google.com/p/chromium/issues/detail?id=237059
|
||||
// Custom errors and errors with custom stack traces show the wrong
|
||||
// stack trace
|
||||
// If it has a stack and Error.captureStackTrace is supported (only
|
||||
// supported in V8 as of May 2013) log the stack to the console.
|
||||
if (e && typeof e.stack === 'string' && Error.captureStackTrace &&
|
||||
goog.global['console']) {
|
||||
goog.global['console']['error'](e.message, e.stack);
|
||||
}
|
||||
}
|
||||
// Re-throw original error. This is great for debugging as it makes
|
||||
// browser JS dev consoles show the correct error and stack trace.
|
||||
throw e;
|
||||
}
|
||||
// Re-throw it since this may be expected by the caller.
|
||||
throw new goog.debug.ErrorHandler.ProtectedFunctionError(e);
|
||||
};
|
||||
|
||||
|
||||
// TODO(mknichel): Allow these functions to take in the window to protect.
|
||||
/**
|
||||
* Installs exception protection for window.setTimeout to handle exceptions.
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.protectWindowSetTimeout = function() {
|
||||
'use strict';
|
||||
this.protectWindowFunctionsHelper_('setTimeout');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Install exception protection for window.setInterval to handle exceptions.
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.protectWindowSetInterval = function() {
|
||||
'use strict';
|
||||
this.protectWindowFunctionsHelper_('setInterval');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Install an unhandledrejection event listener that reports rejected promises.
|
||||
* Note: this will only work with Chrome 49+ and friends, but so far is the only
|
||||
* way to report uncaught errors in aysnc/await functions.
|
||||
* @param {!Window=} win the window to instrument, defaults to current window
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.catchUnhandledRejections = function(win) {
|
||||
'use strict';
|
||||
win = win || goog.global['window'];
|
||||
if ('onunhandledrejection' in win) {
|
||||
win.onunhandledrejection = (event) => {
|
||||
// event.reason contains the rejection reason. When an Error is
|
||||
// thrown, this is the Error object. If it is undefined, create a new
|
||||
// error object.
|
||||
const e =
|
||||
event && event.reason ? event.reason : new Error('uncaught error');
|
||||
this.handleError_(e);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Install exception protection for window.requestAnimationFrame to handle
|
||||
* exceptions.
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.protectWindowRequestAnimationFrame =
|
||||
function() {
|
||||
'use strict';
|
||||
var win = goog.global['window'];
|
||||
var fnNames = [
|
||||
'requestAnimationFrame', 'mozRequestAnimationFrame', 'webkitAnimationFrame',
|
||||
'msRequestAnimationFrame'
|
||||
];
|
||||
for (var i = 0; i < fnNames.length; i++) {
|
||||
var fnName = fnNames[i];
|
||||
if (fnNames[i] in win) {
|
||||
this.protectWindowFunctionsHelper_(fnName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper function for protecting a function that causes a function to be
|
||||
* asynchronously called, for example setTimeout or requestAnimationFrame.
|
||||
* @param {string} fnName The name of the function to protect.
|
||||
* @private
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.protectWindowFunctionsHelper_ = function(
|
||||
fnName) {
|
||||
'use strict';
|
||||
var win = goog.global['window'];
|
||||
var originalFn = win[fnName];
|
||||
var that = this;
|
||||
win[fnName] = function(fn, time) {
|
||||
'use strict';
|
||||
// Don't try to protect strings. In theory, we could try to globalEval
|
||||
// the string, but this seems to lead to permission errors on IE6.
|
||||
if (typeof fn === 'string') {
|
||||
fn = goog.partial(goog.globalEval, fn);
|
||||
}
|
||||
arguments[0] = fn = that.protectEntryPoint(fn);
|
||||
|
||||
// IE doesn't support .call for setInterval/setTimeout, but it
|
||||
// also doesn't care what "this" is, so we can just call the
|
||||
// original function directly
|
||||
if (originalFn.apply) {
|
||||
return originalFn.apply(/** @type {?} */ (this), arguments);
|
||||
} else {
|
||||
var callback = fn;
|
||||
if (arguments.length > 2) {
|
||||
var args = Array.prototype.slice.call(arguments, 2);
|
||||
callback = function() {
|
||||
'use strict';
|
||||
fn.apply(/** @type {?} */ (this), args);
|
||||
};
|
||||
}
|
||||
return originalFn(callback, time);
|
||||
}
|
||||
};
|
||||
win[fnName][this.getFunctionIndex_(false)] = originalFn;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to wrap errors that occur in protected functions in a
|
||||
* goog.debug.ErrorHandler.ProtectedFunctionError.
|
||||
* @param {boolean} wrapErrors Whether to wrap errors.
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.setWrapErrors = function(wrapErrors) {
|
||||
'use strict';
|
||||
this.wrapErrors_ = wrapErrors;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to add a prefix to all error messages that occur in protected
|
||||
* functions.
|
||||
* @param {boolean} prefixErrorMessages Whether to add a prefix to error
|
||||
* messages.
|
||||
*/
|
||||
goog.debug.ErrorHandler.prototype.setPrefixErrorMessages = function(
|
||||
prefixErrorMessages) {
|
||||
'use strict';
|
||||
this.prefixErrorMessages_ = prefixErrorMessages;
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.debug.ErrorHandler.prototype.disposeInternal = function() {
|
||||
'use strict';
|
||||
// Try to unwrap window.setTimeout and window.setInterval.
|
||||
var win = goog.global['window'];
|
||||
win.setTimeout = this.unwrap(win.setTimeout);
|
||||
win.setInterval = this.unwrap(win.setInterval);
|
||||
|
||||
goog.debug.ErrorHandler.base(this, 'disposeInternal');
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Error thrown to the caller of a protected entry point if the entry point
|
||||
* throws an error.
|
||||
* @param {*} cause The error thrown by the entry point.
|
||||
* @constructor
|
||||
* @extends {goog.debug.Error}
|
||||
* @final
|
||||
*/
|
||||
goog.debug.ErrorHandler.ProtectedFunctionError = function(cause) {
|
||||
'use strict';
|
||||
/** @suppress {missingProperties} message may not be defined. */
|
||||
var message = goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX +
|
||||
(cause && cause.message ? String(cause.message) : String(cause));
|
||||
goog.debug.ErrorHandler.ProtectedFunctionError.base(
|
||||
this, 'constructor', message, /** @type {?} */ (cause));
|
||||
|
||||
/** @suppress {missingProperties} stack may not be defined. */
|
||||
var stack = cause && cause.stack;
|
||||
if (stack && typeof stack === 'string') {
|
||||
this.stack = /** @type {string} */ (stack);
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.debug.ErrorHandler.ProtectedFunctionError, goog.debug.Error);
|
||||
|
||||
|
||||
/**
|
||||
* Text to prefix the message with.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.debug.ErrorHandler.ProtectedFunctionError.MESSAGE_PREFIX =
|
||||
'Error in protected function: ';
|
||||
Reference in New Issue
Block a user