blob: 5ac5850f4f40c4cdf6a8d5a7648bed8ec30b2a35 [file] [log] [blame] [edit]
/**
* @license
* Copyright The Closure Library Authors.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Utilities to check the preconditions, postconditions and
* invariants runtime.
*/
goog.provide('jspb.asserts');
/**
* Throws an exception with the given message and "Assertion failed" prefixed
* onto it.
* @param {string} defaultMessage The message to use if givenMessage is empty.
* @param {?Array<*>} defaultArgs The substitution arguments for defaultMessage.
* @param {string|undefined} givenMessage Message supplied by the caller.
* @param {!Array<*>} givenArgs The substitution arguments for givenMessage.
* @throws {Error} When the value is not a number.
*/
jspb.asserts.doAssertFailure = function(defaultMessage, defaultArgs, givenMessage, givenArgs) {
let message = 'Assertion failed';
let args;
if (givenMessage) {
message += ': ' + givenMessage;
args = givenArgs;
} else if (defaultMessage) {
message += ': ' + defaultMessage;
args = defaultArgs;
}
// The '' + works around an Opera 10 bug in the unit tests. Without it,
// a stack trace is added to var message above. With this, a stack trace is
// not added until this line (it causes the extra garbage to be added after
// the assertion message instead of in the middle of it).
throw new Error('' + message, args || []);
}
/**
* Checks if the condition evaluates to true.
* @template T
* @param {T} condition The condition to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} args The items to substitute into the failure message.
* @return {T} The value of the condition.
* @throws {Error} When the condition evaluates to false.
*/
jspb.asserts.assert = function(condition, opt_message, ...args) {
if (!condition) {
jspb.asserts.doAssertFailure('', null, opt_message, args);
}
return condition;
};
/**
* Checks if the value is a string.
* @param {*} value The value to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} args The items to substitute into the failure message.
* @return {string} The value, guaranteed to be a string when asserts enabled.
* @throws {Error} When the value is not a string.
*/
jspb.asserts.assertString = function(value, opt_message, ...args) {
if (typeof value !== 'string') {
jspb.asserts.doAssertFailure(
'Expected string but got %s: %s.', [goog.typeOf(value), value],
opt_message, args);
}
return /** @type {string} */ (value);
};
/**
* Checks if the value is an Array.
* @param {*} value The value to check.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} args The items to substitute into the failure message.
* @return {!Array<?>} The value, guaranteed to be a non-null array.
* @throws {Error} When the value is not an array.
*/
jspb.asserts.assertArray = function(value, opt_message, ...args) {
if (!Array.isArray(value)) {
jspb.asserts.doAssertFailure(
'Expected array but got %s: %s.', [goog.typeOf(value), value],
opt_message, args);
}
return /** @type {!Array<?>} */ (value);
};
/**
* Triggers a failure. This function is useful in case when we want to add a
* check in the unreachable area like switch-case statement:
*
* <pre>
* switch(type) {
* case FOO: doSomething(); break;
* case BAR: doSomethingElse(); break;
* default: jspb.asserts.JspbFail('Unrecognized type: ' + type);
* // We have only 2 types - "default:" section is unreachable code.
* }
* </pre>
*
* @param {string=} opt_message Error message in case of failure.
* @param {...*} args The items to substitute into the failure message.
* @return {void}
* @throws {Error} Failure.
*/
jspb.asserts.fail = function(opt_message, ...args) {
throw new Error(
'Failure' + (opt_message ? ': ' + opt_message : ''),
args);
};
/**
* Checks if the value is an instance of the user-defined type.
*
* Do not use this to ensure a value is an HTMLElement or a subclass! Cross-
* document DOM inherits from separate - though identical - browser classes, and
* such a check will unexpectedly fail.
*
* @param {?} value The value to check.
* @param {function(new: T, ...)} type A user-defined constructor.
* @param {string=} opt_message Error message in case of failure.
* @param {...*} args The items to substitute into the failure message.
* @throws {Error} When the value is not an instance of
* type.
* @return {T}
* @template T
*/
jspb.asserts.assertInstanceof = function(value, type, opt_message, ...args) {
if (!(value instanceof type)) {
jspb.asserts.doAssertFailure(
'Expected instanceof %s but got %s.',
[jspb.asserts.getType(type), jspb.asserts.getType(value)],
opt_message, args);
}
return value;
};
/**
* Returns the type of a value. If a constructor is passed, and a suitable
* string cannot be found, 'unknown type name' will be returned.
* @param {*} value A constructor, object, or primitive.
* @return {string} The best display name for the value, or 'unknown type name'.
* @private
*/
jspb.asserts.getType = function(value) {
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;
}
}