michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: michael@0: /* michael@0: * Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/licenses/publicdomain/ michael@0: */ michael@0: michael@0: michael@0: /* michael@0: * Return true if both of these return true: michael@0: * - LENIENT_PRED applied to CODE michael@0: * - STRICT_PRED applied to CODE with a use strict directive added to the front michael@0: * michael@0: * Run STRICT_PRED first, for testing code that affects the global environment michael@0: * in loose mode, but fails in strict mode. michael@0: */ michael@0: function testLenientAndStrict(code, lenient_pred, strict_pred) { michael@0: return (strict_pred("'use strict'; " + code) && michael@0: lenient_pred(code)); michael@0: } michael@0: michael@0: /* michael@0: * completesNormally(CODE) returns true if evaluating CODE (as eval michael@0: * code) completes normally (rather than throwing an exception). michael@0: */ michael@0: function completesNormally(code) { michael@0: try { michael@0: eval(code); michael@0: return true; michael@0: } catch (exception) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * returns(VALUE)(CODE) returns true if evaluating CODE (as eval code) michael@0: * completes normally (rather than throwing an exception), yielding a value michael@0: * strictly equal to VALUE. michael@0: */ michael@0: function returns(value) { michael@0: return function(code) { michael@0: try { michael@0: return eval(code) === value; michael@0: } catch (exception) { michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * returnsCopyOf(VALUE)(CODE) returns true if evaluating CODE (as eval code) michael@0: * completes normally (rather than throwing an exception), yielding a value michael@0: * that is deepEqual to VALUE. michael@0: */ michael@0: function returnsCopyOf(value) { michael@0: return function(code) { michael@0: try { michael@0: return deepEqual(eval(code), value); michael@0: } catch (exception) { michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * raisesException(EXCEPTION)(CODE) returns true if evaluating CODE (as michael@0: * eval code) throws an exception object that is an instance of EXCEPTION, michael@0: * and returns false if it throws any other error or evaluates michael@0: * successfully. For example: raises(TypeError)("0()") == true. michael@0: */ michael@0: function raisesException(exception) { michael@0: return function (code) { michael@0: try { michael@0: eval(code); michael@0: return false; michael@0: } catch (actual) { michael@0: return actual instanceof exception; michael@0: } michael@0: }; michael@0: }; michael@0: michael@0: /* michael@0: * parsesSuccessfully(CODE) returns true if CODE parses as function michael@0: * code without an error. michael@0: */ michael@0: function parsesSuccessfully(code) { michael@0: try { michael@0: Function(code); michael@0: return true; michael@0: } catch (exception) { michael@0: return false; michael@0: } michael@0: }; michael@0: michael@0: /* michael@0: * parseRaisesException(EXCEPTION)(CODE) returns true if parsing CODE michael@0: * as function code raises EXCEPTION. michael@0: */ michael@0: function parseRaisesException(exception) { michael@0: return function (code) { michael@0: try { michael@0: Function(code); michael@0: return false; michael@0: } catch (actual) { michael@0: return actual instanceof exception; michael@0: } michael@0: }; michael@0: }; michael@0: michael@0: /* michael@0: * Return the result of applying uneval to VAL, and replacing all runs michael@0: * of whitespace with a single horizontal space (poor man's michael@0: * tokenization). michael@0: */ michael@0: function clean_uneval(val) { michael@0: return uneval(val).replace(/\s+/g, ' '); michael@0: } michael@0: michael@0: /* michael@0: * Return true if A is equal to B, where equality on arrays and objects michael@0: * means that they have the same set of enumerable properties, the values michael@0: * of each property are deep_equal, and their 'length' properties are michael@0: * equal. Equality on other types is ==. michael@0: */ michael@0: function deepEqual(a, b) { michael@0: if (typeof a != typeof b) michael@0: return false; michael@0: michael@0: if (typeof a == 'object') { michael@0: var props = {}; michael@0: // For every property of a, does b have that property with an equal value? michael@0: for (var prop in a) { michael@0: if (!deepEqual(a[prop], b[prop])) michael@0: return false; michael@0: props[prop] = true; michael@0: } michael@0: // Are all of b's properties present on a? michael@0: for (var prop in b) michael@0: if (!props[prop]) michael@0: return false; michael@0: // length isn't enumerable, but we want to check it, too. michael@0: return a.length == b.length; michael@0: } michael@0: michael@0: if (a === b) { michael@0: // Distinguish 0 from -0, even though they are ===. michael@0: return a !== 0 || 1/a === 1/b; michael@0: } michael@0: michael@0: // Treat NaNs as equal, even though NaN !== NaN. michael@0: // NaNs are the only non-reflexive values, i.e., if a !== a, then a is a NaN. michael@0: // isNaN is broken: it converts its argument to number, so isNaN("foo") => true michael@0: return a !== a && b !== b; michael@0: }