summaryrefslogtreecommitdiffstats
path: root/js/src/tests/test262/staging/JSON
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/src/tests/test262/staging/JSON
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/test262/staging/JSON')
-rw-r--r--js/src/tests/test262/staging/JSON/browser.js0
-rw-r--r--js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js97
-rw-r--r--js/src/tests/test262/staging/JSON/json-parse-with-source.js292
-rw-r--r--js/src/tests/test262/staging/JSON/shell.js329
4 files changed, 718 insertions, 0 deletions
diff --git a/js/src/tests/test262/staging/JSON/browser.js b/js/src/tests/test262/staging/JSON/browser.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/js/src/tests/test262/staging/JSON/browser.js
diff --git a/js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js b/js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js
new file mode 100644
index 0000000000..45465333d4
--- /dev/null
+++ b/js/src/tests/test262/staging/JSON/json-parse-with-source-snapshot.js
@@ -0,0 +1,97 @@
+// |reftest| skip -- json-parse-with-source is not supported
+// Copyright (C) 2023 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: V8 mjsunit test for JSON.parse with source snapshotting
+includes: [deepEqual.js]
+features: [json-parse-with-source]
+---*/
+
+const replacements = [42,
+ ['foo'],
+ {foo:'bar'},
+ 'foo'];
+
+function TestArrayForwardModify(replacement) {
+ let alreadyReplaced = false;
+ let expectedKeys = ['0','1',''];
+ // lol who designed reviver semantics
+ if (typeof replacement === 'object') {
+ expectedKeys.splice(1, 0, ...Object.keys(replacement));
+ }
+ const o = JSON.parse('[1, 2]', function (k, v, { source }) {
+ assert.sameValue(expectedKeys.shift(), k);
+ if (k === '0') {
+ if (!alreadyReplaced) {
+ this[1] = replacement;
+ alreadyReplaced = true;
+ }
+ } else if (k !== '') {
+ assert.sameValue(undefined, source);
+ }
+ return this[k];
+ });
+ assert.sameValue(0, expectedKeys.length);
+ assert.deepEqual([1, replacement], o);
+}
+
+function TestObjectForwardModify(replacement) {
+ let alreadyReplaced = false;
+ let expectedKeys = ['p','q',''];
+ if (typeof replacement === 'object') {
+ expectedKeys.splice(1, 0, ...Object.keys(replacement));
+ }
+ const o = JSON.parse('{"p":1, "q":2}', function (k, v, { source }) {
+ assert.sameValue(expectedKeys.shift(), k);
+ if (k === 'p') {
+ if (!alreadyReplaced) {
+ this.q = replacement;
+ alreadyReplaced = true;
+ }
+ } else if (k !== '') {
+ assert.sameValue(undefined, source);
+ }
+ return this[k];
+ });
+ assert.sameValue(0, expectedKeys.length);
+ assert.deepEqual({p:1, q:replacement}, o);
+}
+
+for (const r of replacements) {
+ TestArrayForwardModify(r);
+ TestObjectForwardModify(r);
+}
+
+(function TestArrayAppend() {
+ let log = [];
+ const o = JSON.parse('[1,[]]', function (k, v, { source }) {
+ log.push([k, v, source]);
+ if (v === 1) {
+ this[1].push('barf');
+ }
+ return this[k];
+ });
+ assert.deepEqual([['0', 1, '1'],
+ ['0', 'barf', undefined],
+ ['1', ['barf'], undefined],
+ ['', [1, ['barf']], undefined]],
+ log);
+})();
+
+(function TestObjectAddProperty() {
+ let log = [];
+ const o = JSON.parse('{"p":1,"q":{}}', function (k, v, { source }) {
+ log.push([k, v, source]);
+ if (v === 1) {
+ this.q.added = 'barf';
+ }
+ return this[k];
+ });
+ assert.deepEqual([['p', 1, '1'],
+ ['added', 'barf', undefined],
+ ['q', {added:'barf'}, undefined],
+ ['', {p:1, q:{added:'barf'}}, undefined]],
+ log);
+})();
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/JSON/json-parse-with-source.js b/js/src/tests/test262/staging/JSON/json-parse-with-source.js
new file mode 100644
index 0000000000..f8674511ef
--- /dev/null
+++ b/js/src/tests/test262/staging/JSON/json-parse-with-source.js
@@ -0,0 +1,292 @@
+// |reftest| skip -- json-parse-with-source is not supported
+// Copyright (C) 2023 the V8 project authors. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: V8 mjsunit test for JSON.parse with source
+includes: [deepEqual.js]
+features: [json-parse-with-source]
+---*/
+
+(function TestBigInt() {
+ const tooBigForNumber = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
+ const intToBigInt = (key, val, { source }) =>
+ typeof val === 'number' && val % 1 === 0 ? BigInt(source) : val;
+ const roundTripped = JSON.parse(String(tooBigForNumber), intToBigInt);
+ assert.sameValue(tooBigForNumber, roundTripped);
+
+ const bigIntToRawJSON = (key, val) =>
+ typeof val === 'bigint' ? JSON.rawJSON(val) : val;
+ const embedded = JSON.stringify({ tooBigForNumber }, bigIntToRawJSON);
+ assert.sameValue('{"tooBigForNumber":9007199254740993}', embedded);
+})();
+
+function GenerateParseReviverFunction(texts) {
+ let i = 0;
+ return function (key, value, context) {
+ assert(typeof context === 'object');
+ assert.sameValue(Object.prototype, Object.getPrototypeOf(context));
+ // The json value is a primitive value, it's context only has a source property.
+ if (texts[i] !== undefined) {
+ const descriptor = Object.getOwnPropertyDescriptor(context, 'source');
+ assert(descriptor.configurable);
+ assert(descriptor.enumerable);
+ assert(descriptor.writable);
+ assert.sameValue(undefined, descriptor.get);
+ assert.sameValue(undefined, descriptor.set);
+ assert.sameValue(texts[i++], descriptor.value);
+
+ assert.deepEqual(['source'], Object.getOwnPropertyNames(context));
+ assert.deepEqual([], Object.getOwnPropertySymbols(context));
+ } else {
+ // The json value is JSArray or JSObject, it's context has no property.
+ assert(!Object.hasOwn(context, 'source'));
+ assert.deepEqual([], Object.getOwnPropertyNames(context));
+ assert.deepEqual([], Object.getOwnPropertySymbols(context));
+ i++;
+ }
+ return value;
+ };
+}
+
+(function TestNumber() {
+ assert.sameValue(1, JSON.parse('1', GenerateParseReviverFunction(['1'])));
+ assert.sameValue(1.1, JSON.parse('1.1', GenerateParseReviverFunction(['1.1'])));
+ assert.sameValue(-1, JSON.parse('-1', GenerateParseReviverFunction(['-1'])));
+ assert.sameValue(
+ -1.1,
+ JSON.parse('-1.1', GenerateParseReviverFunction(['-1.1']))
+ );
+ assert.sameValue(
+ 11,
+ JSON.parse('1.1e1', GenerateParseReviverFunction(['1.1e1']))
+ );
+ assert.sameValue(
+ 11,
+ JSON.parse('1.1e+1', GenerateParseReviverFunction(['1.1e+1']))
+ );
+ assert.sameValue(
+ 0.11,
+ JSON.parse('1.1e-1', GenerateParseReviverFunction(['1.1e-1']))
+ );
+ assert.sameValue(
+ 11,
+ JSON.parse('1.1E1', GenerateParseReviverFunction(['1.1E1']))
+ );
+ assert.sameValue(
+ 11,
+ JSON.parse('1.1E+1', GenerateParseReviverFunction(['1.1E+1']))
+ );
+ assert.sameValue(
+ 0.11,
+ JSON.parse('1.1E-1', GenerateParseReviverFunction(['1.1E-1']))
+ );
+
+ assert.sameValue('1', JSON.stringify(JSON.rawJSON(1)));
+ assert.sameValue('1.1', JSON.stringify(JSON.rawJSON(1.1)));
+ assert.sameValue('-1', JSON.stringify(JSON.rawJSON(-1)));
+ assert.sameValue('-1.1', JSON.stringify(JSON.rawJSON(-1.1)));
+ assert.sameValue('11', JSON.stringify(JSON.rawJSON(1.1e1)));
+ assert.sameValue('0.11', JSON.stringify(JSON.rawJSON(1.1e-1)));
+})();
+
+(function TestBasic() {
+ assert.sameValue(
+ null,
+ JSON.parse('null', GenerateParseReviverFunction(['null']))
+ );
+ assert.sameValue(
+ true,
+ JSON.parse('true', GenerateParseReviverFunction(['true']))
+ );
+ assert.sameValue(
+ false,
+ JSON.parse('false', GenerateParseReviverFunction(['false']))
+ );
+ assert.sameValue(
+ 'foo',
+ JSON.parse('"foo"', GenerateParseReviverFunction(['"foo"']))
+ );
+
+ assert.sameValue('null', JSON.stringify(JSON.rawJSON(null)));
+ assert.sameValue('true', JSON.stringify(JSON.rawJSON(true)));
+ assert.sameValue('false', JSON.stringify(JSON.rawJSON(false)));
+ assert.sameValue('"foo"', JSON.stringify(JSON.rawJSON('"foo"')));
+})();
+
+(function TestObject() {
+ assert.deepEqual(
+ {},
+ JSON.parse('{}', GenerateParseReviverFunction([]))
+ );
+ assert.deepEqual(
+ { 42: 37 },
+ JSON.parse('{"42":37}', GenerateParseReviverFunction(['37']))
+ );
+ assert.deepEqual(
+ { x: 1, y: 2 },
+ JSON.parse('{"x": 1, "y": 2}', GenerateParseReviverFunction(['1', '2']))
+ );
+ // undefined means the json value is JSObject or JSArray and the passed
+ // context to the reviver function has no source property.
+ assert.deepEqual(
+ { x: [1, 2], y: [2, 3] },
+ JSON.parse(
+ '{"x": [1,2], "y": [2,3]}',
+ GenerateParseReviverFunction(['1', '2', undefined, '2', '3', undefined])
+ )
+ );
+ assert.deepEqual(
+ { x: { x: 1, y: 2 } },
+ JSON.parse(
+ '{"x": {"x": 1, "y": 2}}',
+ GenerateParseReviverFunction(['1', '2', undefined, undefined])
+ )
+ );
+
+ assert.sameValue('{"42":37}', JSON.stringify({ 42: JSON.rawJSON(37) }));
+ assert.sameValue(
+ '{"x":1,"y":2}',
+ JSON.stringify({ x: JSON.rawJSON(1), y: JSON.rawJSON(2) })
+ );
+ assert.sameValue(
+ '{"x":{"x":1,"y":2}}',
+ JSON.stringify({ x: { x: JSON.rawJSON(1), y: JSON.rawJSON(2) } })
+ );
+})();
+
+(function TestArray() {
+ assert.deepEqual([1], JSON.parse('[1.0]', GenerateParseReviverFunction(['1.0'])));
+ assert.deepEqual(
+ [1.1],
+ JSON.parse('[1.1]', GenerateParseReviverFunction(['1.1']))
+ );
+ assert.deepEqual([], JSON.parse('[]', GenerateParseReviverFunction([])));
+ assert.deepEqual(
+ [1, '2', true, null, { x: 1, y: 1 }],
+ JSON.parse(
+ '[1, "2", true, null, {"x": 1, "y": 1}]',
+ GenerateParseReviverFunction(['1', '"2"', 'true', 'null', '1', '1'])
+ )
+ );
+
+ assert.sameValue('[1,1.1]', JSON.stringify([JSON.rawJSON(1), JSON.rawJSON(1.1)]));
+ assert.sameValue(
+ '["1",true,null,false]',
+ JSON.stringify([
+ JSON.rawJSON('"1"'),
+ JSON.rawJSON(true),
+ JSON.rawJSON(null),
+ JSON.rawJSON(false),
+ ])
+ );
+ assert.sameValue(
+ '[{"x":1,"y":1}]',
+ JSON.stringify([{ x: JSON.rawJSON(1), y: JSON.rawJSON(1) }])
+ );
+})();
+
+function assertIsRawJson(rawJson, expectedRawJsonValue) {
+ assert.sameValue(null, Object.getPrototypeOf(rawJson));
+ assert(Object.hasOwn(rawJson, 'rawJSON'));
+ assert.deepEqual(['rawJSON'], Object.getOwnPropertyNames(rawJson));
+ assert.deepEqual([], Object.getOwnPropertySymbols(rawJson));
+ assert.sameValue(expectedRawJsonValue, rawJson.rawJSON);
+}
+
+(function TestRawJson() {
+ assertIsRawJson(JSON.rawJSON(1), '1');
+ assertIsRawJson(JSON.rawJSON(null), 'null');
+ assertIsRawJson(JSON.rawJSON(true), 'true');
+ assertIsRawJson(JSON.rawJSON(false), 'false');
+ assertIsRawJson(JSON.rawJSON('"foo"'), '"foo"');
+
+ assert.throws(TypeError, () => {
+ JSON.rawJSON(Symbol('123'));
+ });
+
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON(undefined);
+ });
+
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON({});
+ });
+
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON([]);
+ });
+
+ const ILLEGAL_END_CHARS = ['\n', '\t', '\r', ' '];
+ for (const char of ILLEGAL_END_CHARS) {
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON(`${char}123`);
+ });
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON(`123${char}`);
+ });
+ }
+
+ assert.throws(SyntaxError, () => {
+ JSON.rawJSON('');
+ });
+
+ const values = [1, 1.1, null, false, true, '123'];
+ for (const value of values) {
+ assert(!JSON.isRawJSON(value));
+ assert(JSON.isRawJSON(JSON.rawJSON(value)));
+ }
+ assert(!JSON.isRawJSON(undefined));
+ assert(!JSON.isRawJSON(Symbol('123')));
+ assert(!JSON.isRawJSON([]));
+ assert(!JSON.isRawJSON({ rawJSON: '123' }));
+})();
+
+(function TestReviverModifyJsonValue() {
+ {
+ let reviverCallIndex = 0;
+ const expectedKeys = ['a', 'b', 'c', ''];
+ const reviver = function(key, value, {source}) {
+ assert.sameValue(expectedKeys[reviverCallIndex++], key);
+ if (key == 'a') {
+ this.b = 2;
+ assert.sameValue('0', source);
+ } else if (key == 'b') {
+ this.c = 3;
+ assert.sameValue(2, value);
+ assert.sameValue(undefined, source);
+ } else if (key == 'c') {
+ assert.sameValue(3, value);
+ assert.sameValue(undefined, source);
+ }
+ return value;
+ }
+ assert.deepEqual({a: 0, b: 2, c: 3}, JSON.parse('{"a": 0, "b": 1, "c": [1, 2]}', reviver));
+ }
+ {
+ let reviverCallIndex = 0;
+ const expectedKeys = ['0', '1', '2', '3', ''];
+ const reviver = function(key, value, {source}) {
+ assert.sameValue(expectedKeys[reviverCallIndex++], key);
+ if (key == '0') {
+ this[1] = 3;
+ assert.sameValue(1, value);
+ assert.sameValue('1', source);
+ } else if (key == '1') {
+ this[2] = 4;
+ assert.sameValue(3, value);
+ assert.sameValue(undefined, source);
+ } else if(key == '2') {
+ this[3] = 5;
+ assert.sameValue(4, value);
+ assert.sameValue(undefined, source);
+ } else if(key == '5'){
+ assert.sameValue(5, value);
+ assert.sameValue(undefined, source);
+ }
+ return value;
+ }
+ assert.deepEqual([1, 3, 4, 5], JSON.parse('[1, 2, 3, {"a": 1}]', reviver));
+ }
+})();
+
+reportCompare(0, 0);
diff --git a/js/src/tests/test262/staging/JSON/shell.js b/js/src/tests/test262/staging/JSON/shell.js
new file mode 100644
index 0000000000..276ee734ff
--- /dev/null
+++ b/js/src/tests/test262/staging/JSON/shell.js
@@ -0,0 +1,329 @@
+// GENERATED, DO NOT EDIT
+// file: deepEqual.js
+// Copyright 2019 Ron Buckton. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+description: >
+ Compare two values structurally
+defines: [assert.deepEqual]
+---*/
+
+assert.deepEqual = function(actual, expected, message) {
+ var format = assert.deepEqual.format;
+ assert(
+ assert.deepEqual._compare(actual, expected),
+ `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`
+ );
+};
+
+assert.deepEqual.format = function(value, seen) {
+ switch (typeof value) {
+ case 'string':
+ return typeof JSON !== "undefined" ? JSON.stringify(value) : `"${value}"`;
+ case 'number':
+ case 'boolean':
+ case 'symbol':
+ case 'bigint':
+ return value.toString();
+ case 'undefined':
+ return 'undefined';
+ case 'function':
+ return `[Function${value.name ? `: ${value.name}` : ''}]`;
+ case 'object':
+ if (value === null) return 'null';
+ if (value instanceof Date) return `Date "${value.toISOString()}"`;
+ if (value instanceof RegExp) return value.toString();
+ if (!seen) {
+ seen = {
+ counter: 0,
+ map: new Map()
+ };
+ }
+
+ let usage = seen.map.get(value);
+ if (usage) {
+ usage.used = true;
+ return `[Ref: #${usage.id}]`;
+ }
+
+ usage = { id: ++seen.counter, used: false };
+ seen.map.set(value, usage);
+
+ if (typeof Set !== "undefined" && value instanceof Set) {
+ return `Set {${Array.from(value).map(value => assert.deepEqual.format(value, seen)).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (typeof Map !== "undefined" && value instanceof Map) {
+ return `Map {${Array.from(value).map(pair => `${assert.deepEqual.format(pair[0], seen)} => ${assert.deepEqual.format(pair[1], seen)}}`).join(', ')}}${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ if (Array.isArray ? Array.isArray(value) : value instanceof Array) {
+ return `[${value.map(value => assert.deepEqual.format(value, seen)).join(', ')}]${usage.used ? ` as #${usage.id}` : ''}`;
+ }
+ let tag = Symbol.toStringTag in value ? value[Symbol.toStringTag] : 'Object';
+ if (tag === 'Object' && Object.getPrototypeOf(value) === null) {
+ tag = '[Object: null prototype]';
+ }
+ return `${tag ? `${tag} ` : ''}{ ${Object.keys(value).map(key => `${key.toString()}: ${assert.deepEqual.format(value[key], seen)}`).join(', ')} }${usage.used ? ` as #${usage.id}` : ''}`;
+ default:
+ return typeof value;
+ }
+};
+
+assert.deepEqual._compare = (function () {
+ var EQUAL = 1;
+ var NOT_EQUAL = -1;
+ var UNKNOWN = 0;
+
+ function deepEqual(a, b) {
+ return compareEquality(a, b) === EQUAL;
+ }
+
+ function compareEquality(a, b, cache) {
+ return compareIf(a, b, isOptional, compareOptionality)
+ || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality)
+ || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache)
+ || NOT_EQUAL;
+ }
+
+ function compareIf(a, b, test, compare, cache) {
+ return !test(a)
+ ? !test(b) ? UNKNOWN : NOT_EQUAL
+ : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache);
+ }
+
+ function tryCompareStrictEquality(a, b) {
+ return a === b ? EQUAL : UNKNOWN;
+ }
+
+ function tryCompareTypeOfEquality(a, b) {
+ return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function tryCompareToStringTagEquality(a, b) {
+ var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined;
+ var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined;
+ return aTag !== bTag ? NOT_EQUAL : UNKNOWN;
+ }
+
+ function isOptional(value) {
+ return value === undefined
+ || value === null;
+ }
+
+ function compareOptionality(a, b) {
+ return tryCompareStrictEquality(a, b)
+ || NOT_EQUAL;
+ }
+
+ function isPrimitiveEquatable(value) {
+ switch (typeof value) {
+ case 'string':
+ case 'number':
+ case 'bigint':
+ case 'boolean':
+ case 'symbol':
+ return true;
+ default:
+ return isBoxed(value);
+ }
+ }
+
+ function comparePrimitiveEquality(a, b) {
+ if (isBoxed(a)) a = a.valueOf();
+ if (isBoxed(b)) b = b.valueOf();
+ return tryCompareStrictEquality(a, b)
+ || tryCompareTypeOfEquality(a, b)
+ || compareIf(a, b, isNaNEquatable, compareNaNEquality)
+ || NOT_EQUAL;
+ }
+
+ function isNaNEquatable(value) {
+ return typeof value === 'number';
+ }
+
+ function compareNaNEquality(a, b) {
+ return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL;
+ }
+
+ function isObjectEquatable(value) {
+ return typeof value === 'object';
+ }
+
+ function compareObjectEquality(a, b, cache) {
+ if (!cache) cache = new Map();
+ return getCache(cache, a, b)
+ || setCache(cache, a, b, EQUAL) // consider equal for now
+ || cacheComparison(a, b, tryCompareStrictEquality, cache)
+ || cacheComparison(a, b, tryCompareToStringTagEquality, cache)
+ || compareIf(a, b, isValueOfEquatable, compareValueOfEquality)
+ || compareIf(a, b, isToStringEquatable, compareToStringEquality)
+ || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache)
+ || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache)
+ || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || cacheComparison(a, b, fail, cache);
+ }
+
+ function isBoxed(value) {
+ return value instanceof String
+ || value instanceof Number
+ || value instanceof Boolean
+ || typeof Symbol === 'function' && value instanceof Symbol
+ || typeof BigInt === 'function' && value instanceof BigInt;
+ }
+
+ function isValueOfEquatable(value) {
+ return value instanceof Date;
+ }
+
+ function compareValueOfEquality(a, b) {
+ return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isToStringEquatable(value) {
+ return value instanceof RegExp;
+ }
+
+ function compareToStringEquality(a, b) {
+ return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality)
+ || NOT_EQUAL;
+ }
+
+ function isArrayLikeEquatable(value) {
+ return (Array.isArray ? Array.isArray(value) : value instanceof Array)
+ || (typeof Uint8Array === 'function' && value instanceof Uint8Array)
+ || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray)
+ || (typeof Uint16Array === 'function' && value instanceof Uint16Array)
+ || (typeof Uint32Array === 'function' && value instanceof Uint32Array)
+ || (typeof Int8Array === 'function' && value instanceof Int8Array)
+ || (typeof Int16Array === 'function' && value instanceof Int16Array)
+ || (typeof Int32Array === 'function' && value instanceof Int32Array)
+ || (typeof Float32Array === 'function' && value instanceof Float32Array)
+ || (typeof Float64Array === 'function' && value instanceof Float64Array)
+ || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array)
+ || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array);
+ }
+
+ function compareArrayLikeEquality(a, b, cache) {
+ if (a.length !== b.length) return NOT_EQUAL;
+ for (var i = 0; i < a.length; i++) {
+ if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+ return EQUAL;
+ }
+
+ function isStructurallyEquatable(value) {
+ return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference
+ || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference
+ || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference
+ || typeof Map === 'function' && value instanceof Map // comparable via @@iterator
+ || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator
+ }
+
+ function compareStructuralEquality(a, b, cache) {
+ var aKeys = [];
+ for (var key in a) aKeys.push(key);
+
+ var bKeys = [];
+ for (var key in b) bKeys.push(key);
+
+ if (aKeys.length !== bKeys.length) {
+ return NOT_EQUAL;
+ }
+
+ aKeys.sort();
+ bKeys.sort();
+
+ for (var i = 0; i < aKeys.length; i++) {
+ var aKey = aKeys[i];
+ var bKey = bKeys[i];
+ if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) {
+ return NOT_EQUAL;
+ }
+ }
+
+ return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache)
+ || EQUAL;
+ }
+
+ function isIterableEquatable(value) {
+ return typeof Symbol === 'function'
+ && typeof value[Symbol.iterator] === 'function';
+ }
+
+ function compareIteratorEquality(a, b, cache) {
+ if (typeof Map === 'function' && a instanceof Map && b instanceof Map ||
+ typeof Set === 'function' && a instanceof Set && b instanceof Set) {
+ if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size
+ }
+
+ var ar, br;
+ while (true) {
+ ar = a.next();
+ br = b.next();
+ if (ar.done) {
+ if (br.done) return EQUAL;
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ if (br.done) {
+ if (a.return) a.return();
+ return NOT_EQUAL;
+ }
+ if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) {
+ if (a.return) a.return();
+ if (b.return) b.return();
+ return NOT_EQUAL;
+ }
+ }
+ }
+
+ function compareIterableEquality(a, b, cache) {
+ return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache);
+ }
+
+ function cacheComparison(a, b, compare, cache) {
+ var result = compare(a, b, cache);
+ if (cache && (result === EQUAL || result === NOT_EQUAL)) {
+ setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result));
+ }
+ return result;
+ }
+
+ function fail() {
+ return NOT_EQUAL;
+ }
+
+ function setCache(cache, left, right, result) {
+ var otherCache;
+
+ otherCache = cache.get(left);
+ if (!otherCache) cache.set(left, otherCache = new Map());
+ otherCache.set(right, result);
+
+ otherCache = cache.get(right);
+ if (!otherCache) cache.set(right, otherCache = new Map());
+ otherCache.set(left, result);
+ }
+
+ function getCache(cache, left, right) {
+ var otherCache;
+ var result;
+
+ otherCache = cache.get(left);
+ result = otherCache && otherCache.get(right);
+ if (result) return result;
+
+ otherCache = cache.get(right);
+ result = otherCache && otherCache.get(left);
+ if (result) return result;
+
+ return UNKNOWN;
+ }
+
+ return deepEqual;
+})();