diff options
Diffstat (limited to 'js/src/tests/js1_8_5/extensions/clone-object.js')
-rw-r--r-- | js/src/tests/js1_8_5/extensions/clone-object.js | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/js/src/tests/js1_8_5/extensions/clone-object.js b/js/src/tests/js1_8_5/extensions/clone-object.js new file mode 100644 index 0000000..1f85ae7 --- /dev/null +++ b/js/src/tests/js1_8_5/extensions/clone-object.js @@ -0,0 +1,233 @@ +// -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +// Assert that cloning b does the right thing as far as we can tell. +// Caveat: getters in b must produce the same value each time they're +// called. We may call them several times. +// +// If desc is provided, then the very first thing we do to b is clone it. +// (The self-modifying object test counts on this.) +// +function check(b, desc) { + function classOf(obj) { + return Object.prototype.toString.call(obj); + } + + function ownProperties(obj) { + return Object.getOwnPropertyNames(obj). + map(function (p) { return [p, Object.getOwnPropertyDescriptor(obj, p)]; }); + } + + function isCloneable(pair) { + return typeof pair[0] === 'string' && pair[1].enumerable; + } + + function notIndex(p) { + var u = p >>> 0; + return !("" + u == p && u != 0xffffffff); + } + + function assertIsCloneOf(a, b, path) { + assertEq(a === b, false); + + var ca = classOf(a); + assertEq(ca, classOf(b), path); + + assertEq(Object.getPrototypeOf(a), + ca == "[object Object]" ? Object.prototype : Array.prototype, + path); + + // 'b', the original object, may have non-enumerable or XMLName + // properties; ignore them. 'a', the clone, should not have any + // non-enumerable properties (except .length, if it's an Array) or + // XMLName properties. + var pb = ownProperties(b).filter(isCloneable); + var pa = ownProperties(a); + for (var i = 0; i < pa.length; i++) { + assertEq(typeof pa[i][0], "string", "clone should not have E4X properties " + path); + if (!pa[i][1].enumerable) { + if (Array.isArray(a) && pa[i][0] == "length") { + // remove it so that the comparisons below will work + pa.splice(i, 1); + i--; + } else { + throw new Error("non-enumerable clone property " + uneval(pa[i][0]) + " " + path); + } + } + } + + // Check that, apart from properties whose names are array indexes, + // the enumerable properties appear in the same order. + var aNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex); + var bNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex); + assertEq(aNames.join(","), bNames.join(","), path); + + // Check that the lists are the same when including array indexes. + function byName(a, b) { a = a[0]; b = b[0]; return a < b ? -1 : a === b ? 0 : 1; } + pa.sort(byName); + pb.sort(byName); + assertEq(pa.length, pb.length, "should see the same number of properties " + path); + for (var i = 0; i < pa.length; i++) { + var aName = pa[i][0]; + var bName = pb[i][0]; + assertEq(aName, bName, path); + + var path2 = path + "." + aName; + var da = pa[i][1]; + var db = pb[i][1]; + assertEq(da.configurable, true, path2); + assertEq(da.writable, true, path2); + assertEq("value" in da, true, path2); + var va = da.value; + var vb = b[pb[i][0]]; + if (typeof va === "object" && va !== null) + queue.push([va, vb, path2]); + else + assertEq(va, vb, path2); + } + } + + var banner = "while testing clone of " + (desc || uneval(b)); + var a = deserialize(serialize(b)); + var queue = [[a, b, banner]]; + while (queue.length) { + var triple = queue.shift(); + assertIsCloneOf(triple[0], triple[1], triple[2]); + } + + return a; // for further testing +} + +function test() { + check({}); + check([]); + check({x: 0}); + check({x: 0.7, p: "forty-two", y: null, z: undefined}); + check(Array.prototype); + check(Object.prototype); + + // before and after + var b, a; + + // Slow array. + b = [, 1, 2, 3]; + b.expando = true; + b[5] = 5; + b[0] = 0; + b[4] = 4; + delete b[2]; + check(b); + + // Check cloning properties other than basic data properties. (check() + // asserts that the properties of the clone are configurable, writable, + // enumerable data properties.) + b = {}; + Object.defineProperties(b, { + x: {enumerable: true, get: function () { return 12479; }}, + y: {enumerable: true, configurable: true, writable: false, value: 0}, + z: {enumerable: true, configurable: false, writable: true, value: 0}, + hidden: {enumerable:false, value: 1334}}); + check(b); + + // Check corner cases involving property names. + b = {"-1": -1, + 0xffffffff: null, + 0x100000000: null, + "": 0, + "\xff\x7f\u7fff\uffff\ufeff\ufffe": 1, // random unicode id + "\ud800 \udbff \udc00 \udfff": 2}; // busted surrogate pairs + check(b); + + b = []; + b[-1] = -1; + b[0xffffffff] = null; + b[0x100000000] = null; + b[""] = 0; + b["\xff\x7f\u7fff\uffff\ufeff\ufffe"] = 1; + b["\ud800 \udbff \udc00 \udfff"] = 2; + check(b); + + // An array's .length property is not enumerable, so it is not cloned. + b = Array(5); + assertEq(b.length, 5); + a = check(b); + assertEq(a.length, 0); + + b[1] = "ok"; + a = check(b); + assertEq(a.length, 2); + + // Check that prototypes are not cloned, per spec. + b = Object.create({x:1}); + b.y = 2; + b.z = 3; + check(b); + + // Check that cloning separates merge points in the tree, per spec. + var same = {}; + b = {one: same, two: same}; + a = check(b); + assertEq(a.one === a.two, false); + + b = [same, same]; + a = check(b); + assertEq(a[0] === a[1], false); + + // Try cloning a deep object. Don't fail with "too much recursion". + b = {}; + var current = b; + for (var i = 0; i < 10000; i++) { + var next = {}; + current['x' + i] = next; + current = next; + } + check(b, "deepObject"); // takes 2 seconds :-\ + + /* + XXX TODO spin this out into its own test + // This fails quickly with an OOM error. An exception would be nicer. + function Infinitree() { + return { get left() { return new Infinitree; }, + get right() { return new Infinitree; }}; + } + var threw = false; + try { + serialize(new Infinitree); + } catch (exc) { + threw = true; + } + assertEq(threw, true); + */ + + // Clone an array with holes. + check([0, 1, 2, , 4, 5, 6]); + + // Array holes should not take up space. + b = []; + b[255] = 1; + check(b); + assertEq(serialize(b).length < 255, true); + + // Self-modifying object. + // This should never read through to b's prototype. + b = Object.create({y: 2}, + {x: {enumerable: true, + configurable: true, + get: function() { if (this.hasOwnProperty("y")) delete this.y; return 1; }}, + y: {enumerable: true, + configurable: true, + writable: true, + value: 3}}); + check(b, "selfModifyingObject"); + + // Ignore properties with object-ids. + var uri = "http://example.net"; + b = {x: 1, y: 2}; + Object.defineProperty(b, QName(uri, "x"), {enumerable: true, value: 3}); + Object.defineProperty(b, QName(uri, "y"), {enumerable: true, value: 5}); + check(b); +} + +test(); +reportCompare(0, 0, 'ok'); |