/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ (function(global) { const undefined = void 0; /* * completesNormally(CODE) returns true if evaluating CODE (as eval * code) completes normally (rather than throwing an exception). */ global.completesNormally = function completesNormally(code) { try { eval(code); return true; } catch (exception) { return false; } } /* * raisesException(EXCEPTION)(CODE) returns true if evaluating CODE (as * eval code) throws an exception object that is an instance of EXCEPTION, * and returns false if it throws any other error or evaluates * successfully. For example: raises(TypeError)("0()") == true. */ global.raisesException = function raisesException(exception) { return function (code) { try { eval(code); return false; } catch (actual) { return actual instanceof exception; } }; }; /* * Return true if A is equal to B, where equality on arrays and objects * means that they have the same set of enumerable properties, the values * of each property are deep_equal, and their 'length' properties are * equal. Equality on other types is ==. */ global.deepEqual = function deepEqual(a, b) { if (typeof a != typeof b) return false; if (typeof a == 'object') { var props = {}; // For every property of a, does b have that property with an equal value? for (var prop in a) { if (!deepEqual(a[prop], b[prop])) return false; props[prop] = true; } // Are all of b's properties present on a? for (var prop in b) if (!props[prop]) return false; // length isn't enumerable, but we want to check it, too. return a.length == b.length; } if (a === b) { // Distinguish 0 from -0, even though they are ===. return a !== 0 || 1/a === 1/b; } // Treat NaNs as equal, even though NaN !== NaN. // NaNs are the only non-reflexive values, i.e., if a !== a, then a is a NaN. // isNaN is broken: it converts its argument to number, so isNaN("foo") => true return a !== a && b !== b; } /** Make an iterator with a return method. */ global.makeIterator = function makeIterator(overrides) { var throwMethod; if (overrides && overrides.throw) throwMethod = overrides.throw; var iterator = { throw: throwMethod, next: function(x) { if (overrides && overrides.next) return overrides.next(x); return { done: false }; }, return: function(x) { if (overrides && overrides.ret) return overrides.ret(x); return { done: true }; } }; return function() { return iterator; }; }; /** Yield every permutation of the elements in some array. */ global.Permutations = function* Permutations(items) { if (items.length == 0) { yield []; } else { items = items.slice(0); for (let i = 0; i < items.length; i++) { let swap = items[0]; items[0] = items[i]; items[i] = swap; for (let e of Permutations(items.slice(1, items.length))) yield [items[0]].concat(e); } } }; if (typeof global.assertThrowsValue === 'undefined') { global.assertThrowsValue = function assertThrowsValue(f, val, msg) { var fullmsg; try { f(); } catch (exc) { if ((exc === val) === (val === val) && (val !== 0 || 1 / exc === 1 / val)) return; fullmsg = "Assertion failed: expected exception " + val + ", got " + exc; } if (fullmsg === undefined) fullmsg = "Assertion failed: expected exception " + val + ", no exception thrown"; if (msg !== undefined) fullmsg += " - " + msg; throw new Error(fullmsg); }; } if (typeof global.assertThrowsInstanceOf === 'undefined') { global.assertThrowsInstanceOf = function assertThrowsInstanceOf(f, ctor, msg) { var fullmsg; try { f(); } catch (exc) { if (exc instanceof ctor) return; fullmsg = `Assertion failed: expected exception ${ctor.name}, got ${exc}`; } if (fullmsg === undefined) fullmsg = `Assertion failed: expected exception ${ctor.name}, no exception thrown`; if (msg !== undefined) fullmsg += " - " + msg; throw new Error(fullmsg); }; } global.assertDeepEq = (function(){ var call = Function.prototype.call, Array_isArray = Array.isArray, Array_includes = call.bind(Array.prototype.includes), Map_ = Map, Error_ = Error, Symbol_ = Symbol, Symbol_keyFor = Symbol.keyFor, Symbol_description = call.bind(Object.getOwnPropertyDescriptor(Symbol.prototype, "description").get), Map_has = call.bind(Map.prototype.has), Map_get = call.bind(Map.prototype.get), Map_set = call.bind(Map.prototype.set), Object_toString = call.bind(Object.prototype.toString), Function_toString = call.bind(Function.prototype.toString), Object_getPrototypeOf = Object.getPrototypeOf, Object_hasOwnProperty = call.bind(Object.prototype.hasOwnProperty), Object_getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor, Object_isExtensible = Object.isExtensible, Object_getOwnPropertyNames = Object.getOwnPropertyNames; // Return true iff ES6 Type(v) isn't Object. // Note that `typeof document.all === "undefined"`. function isPrimitive(v) { return (v === null || v === undefined || typeof v === "boolean" || typeof v === "number" || typeof v === "string" || typeof v === "symbol"); } function assertSameValue(a, b, msg) { try { assertEq(a, b); } catch (exc) { throw Error_(exc.message + (msg ? " " + msg : "")); } } function assertSameClass(a, b, msg) { var ac = Object_toString(a), bc = Object_toString(b); assertSameValue(ac, bc, msg); switch (ac) { case "[object Function]": if (typeof isProxy !== "undefined" && !isProxy(a) && !isProxy(b)) assertSameValue(Function_toString(a), Function_toString(b), msg); } } function at(prevmsg, segment) { return prevmsg ? prevmsg + segment : "at _" + segment; } // Assert that the arguments a and b are thoroughly structurally equivalent. // // For the sake of speed, we cut a corner: // var x = {}, y = {}, ax = [x]; // assertDeepEq([ax, x], [ax, y]); // passes (?!) // // Technically this should fail, since the two object graphs are different. // (The graph of [ax, y] contains one more object than the graph of [ax, x].) // // To get technically correct behavior, pass {strictEquivalence: true}. // This is slower because we have to walk the entire graph, and Object.prototype // is big. // return function assertDeepEq(a, b, options) { var strictEquivalence = options ? options.strictEquivalence : false; function assertSameProto(a, b, msg) { check(Object_getPrototypeOf(a), Object_getPrototypeOf(b), at(msg, ".__proto__")); } function failPropList(na, nb, msg) { throw Error_("got own properties " + JSON.stringify(na) + ", expected " + JSON.stringify(nb) + (msg ? " " + msg : "")); } function assertSameProps(a, b, msg) { var na = Object_getOwnPropertyNames(a), nb = Object_getOwnPropertyNames(b); if (na.length !== nb.length) failPropList(na, nb, msg); // Ignore differences in whether Array elements are stored densely. if (Array_isArray(a)) { na.sort(); nb.sort(); } for (var i = 0; i < na.length; i++) { var name = na[i]; if (name !== nb[i]) failPropList(na, nb, msg); var da = Object_getOwnPropertyDescriptor(a, name), db = Object_getOwnPropertyDescriptor(b, name); var pmsg = at(msg, /^[_$A-Za-z0-9]+$/.test(name) ? /0|[1-9][0-9]*/.test(name) ? "[" + name + "]" : "." + name : "[" + JSON.stringify(name) + "]"); assertSameValue(da.configurable, db.configurable, at(pmsg, ".[[Configurable]]")); assertSameValue(da.enumerable, db.enumerable, at(pmsg, ".[[Enumerable]]")); if (Object_hasOwnProperty(da, "value")) { if (!Object_hasOwnProperty(db, "value")) throw Error_("got data property, expected accessor property" + pmsg); check(da.value, db.value, pmsg); } else { if (Object_hasOwnProperty(db, "value")) throw Error_("got accessor property, expected data property" + pmsg); check(da.get, db.get, at(pmsg, ".[[Get]]")); check(da.set, db.set, at(pmsg, ".[[Set]]")); } } }; const wellKnownSymbols = Reflect.ownKeys(Symbol) .map(key => Symbol[key]) .filter(value => typeof value === "symbol"); // The standard doesn't offer a convenient way to distinguish well-known // symbols from user-created symbols. function isSimilarSymbol(a, b) { // Fast path for same symbols. if (a === b) { return true; } // 1. Symbol descriptions must match. // 2. Either both symbols are in the registry or none is. // 3. Neither symbol must be a well-known symbol, because those are // already handled through the fast path. return Symbol_description(a) === Symbol_description(b) && Symbol_keyFor(a) === Symbol_keyFor(b) && !Array_includes(wellKnownSymbols, a) && !Array_includes(wellKnownSymbols, b); } var ab = new Map_(); var bpath = new Map_(); function check(a, b, path) { if (typeof a === "symbol") { // Symbols are primitives, but they have identity. // Symbol("x") !== Symbol("x") but // assertDeepEq(Symbol("x"), Symbol("x")) should pass. if (typeof b !== "symbol") { throw Error_("got " + String(a) + ", expected " + String(b) + " " + path); } else if (!isSimilarSymbol(a, b)) { throw Error_("got " + String(a) + ", expected " + String(b) + " " + path); } else if (Map_has(ab, a)) { assertSameValue(Map_get(ab, a), b, path); } else if (Map_has(bpath, b)) { var bPrevPath = Map_get(bpath, b) || "_"; throw Error_("got distinct symbols " + at(path, "") + " and " + at(bPrevPath, "") + ", expected the same symbol both places"); } else { Map_set(ab, a, b); Map_set(bpath, b, path); } } else if (isPrimitive(a)) { assertSameValue(a, b, path); } else if (isPrimitive(b)) { throw Error_("got " + Object_toString(a) + ", expected " + String(b) + " " + path); } else if (Map_has(ab, a)) { assertSameValue(Map_get(ab, a), b, path); } else if (Map_has(bpath, b)) { var bPrevPath = Map_get(bpath, b) || "_"; throw Error_("got distinct objects " + at(path, "") + " and " + at(bPrevPath, "") + ", expected the same object both places"); } else { Map_set(ab, a, b); Map_set(bpath, b, path); if (a !== b || strictEquivalence) { assertSameClass(a, b, path); assertSameProto(a, b, path); assertSameProps(a, b, path); assertSameValue(Object_isExtensible(a), Object_isExtensible(b), at(path, ".[[Extensible]]")); } } } check(a, b, ""); }; })(); })(this);