js/src/tests/js1_8_5/extensions/clone-object.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/tests/js1_8_5/extensions/clone-object.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,234 @@
     1.4 +// |reftest| skip-if(!xulRuntime.shell)
     1.5 +// -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.6 +// Any copyright is dedicated to the Public Domain.
     1.7 +// http://creativecommons.org/licenses/publicdomain/
     1.8 +
     1.9 +// Assert that cloning b does the right thing as far as we can tell.
    1.10 +// Caveat: getters in b must produce the same value each time they're
    1.11 +// called. We may call them several times.
    1.12 +//
    1.13 +// If desc is provided, then the very first thing we do to b is clone it.
    1.14 +// (The self-modifying object test counts on this.)
    1.15 +//
    1.16 +function check(b, desc) {
    1.17 +    function classOf(obj) {
    1.18 +        return Object.prototype.toString.call(obj);
    1.19 +    }
    1.20 +
    1.21 +    function ownProperties(obj) {
    1.22 +        return Object.getOwnPropertyNames(obj).
    1.23 +            map(function (p) { return [p, Object.getOwnPropertyDescriptor(obj, p)]; });
    1.24 +    }
    1.25 +
    1.26 +    function isCloneable(pair) {
    1.27 +        return typeof pair[0] === 'string' && pair[1].enumerable;
    1.28 +    }
    1.29 +
    1.30 +    function notIndex(p) {
    1.31 +        var u = p >>> 0;
    1.32 +        return !("" + u == p && u != 0xffffffff);
    1.33 +    }
    1.34 +
    1.35 +    function assertIsCloneOf(a, b, path) {
    1.36 +        assertEq(a === b, false);
    1.37 +
    1.38 +        var ca = classOf(a);
    1.39 +        assertEq(ca, classOf(b), path);
    1.40 +
    1.41 +        assertEq(Object.getPrototypeOf(a),
    1.42 +                 ca == "[object Object]" ? Object.prototype : Array.prototype,
    1.43 +                 path);
    1.44 +
    1.45 +        // 'b', the original object, may have non-enumerable or XMLName
    1.46 +        // properties; ignore them.  'a', the clone, should not have any
    1.47 +        // non-enumerable properties (except .length, if it's an Array) or
    1.48 +        // XMLName properties.
    1.49 +        var pb = ownProperties(b).filter(isCloneable);
    1.50 +        var pa = ownProperties(a);
    1.51 +        for (var i = 0; i < pa.length; i++) {
    1.52 +            assertEq(typeof pa[i][0], "string", "clone should not have E4X properties " + path);
    1.53 +            if (!pa[i][1].enumerable) {
    1.54 +                if (Array.isArray(a) && pa[i][0] == "length") {
    1.55 +                    // remove it so that the comparisons below will work
    1.56 +                    pa.splice(i, 1);
    1.57 +                    i--;
    1.58 +                } else {
    1.59 +                    throw new Error("non-enumerable clone property " + uneval(pa[i][0]) + " " + path);
    1.60 +                }
    1.61 +            }
    1.62 +        }
    1.63 +
    1.64 +        // Check that, apart from properties whose names are array indexes, 
    1.65 +        // the enumerable properties appear in the same order.
    1.66 +        var aNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex);
    1.67 +        var bNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex);
    1.68 +        assertEq(aNames.join(","), bNames.join(","), path);
    1.69 +
    1.70 +        // Check that the lists are the same when including array indexes.
    1.71 +        function byName(a, b) { a = a[0]; b = b[0]; return a < b ? -1 : a === b ? 0 : 1; }
    1.72 +        pa.sort(byName);
    1.73 +        pb.sort(byName);
    1.74 +        assertEq(pa.length, pb.length, "should see the same number of properties " + path);
    1.75 +        for (var i = 0; i < pa.length; i++) {
    1.76 +            var aName = pa[i][0];
    1.77 +            var bName = pb[i][0];
    1.78 +            assertEq(aName, bName, path);
    1.79 +
    1.80 +            var path2 = path + "." + aName;
    1.81 +            var da = pa[i][1];
    1.82 +            var db = pb[i][1];
    1.83 +            assertEq(da.configurable, true, path2);
    1.84 +            assertEq(da.writable, true, path2);
    1.85 +            assertEq("value" in da, true, path2);
    1.86 +            var va = da.value;
    1.87 +            var vb = b[pb[i][0]];
    1.88 +            if (typeof va === "object" && va !== null)
    1.89 +                queue.push([va, vb, path2]);
    1.90 +            else
    1.91 +                assertEq(va, vb, path2);
    1.92 +        }
    1.93 +    }
    1.94 +
    1.95 +    var banner = "while testing clone of " + (desc || uneval(b));
    1.96 +    var a = deserialize(serialize(b));
    1.97 +    var queue = [[a, b, banner]];
    1.98 +    while (queue.length) {
    1.99 +        var triple = queue.shift();
   1.100 +        assertIsCloneOf(triple[0], triple[1], triple[2]);
   1.101 +    }
   1.102 +
   1.103 +    return a; // for further testing
   1.104 +}
   1.105 +
   1.106 +function test() {
   1.107 +    check({});
   1.108 +    check([]);
   1.109 +    check({x: 0});
   1.110 +    check({x: 0.7, p: "forty-two", y: null, z: undefined});
   1.111 +    check(Array.prototype);
   1.112 +    check(Object.prototype);
   1.113 +
   1.114 +    // before and after
   1.115 +    var b, a;
   1.116 +
   1.117 +    // Slow array.
   1.118 +    b = [, 1, 2, 3];
   1.119 +    b.expando = true;
   1.120 +    b[5] = 5;
   1.121 +    b[0] = 0;
   1.122 +    b[4] = 4;
   1.123 +    delete b[2];
   1.124 +    check(b);
   1.125 +
   1.126 +    // Check cloning properties other than basic data properties. (check()
   1.127 +    // asserts that the properties of the clone are configurable, writable,
   1.128 +    // enumerable data properties.)
   1.129 +    b = {};
   1.130 +    Object.defineProperties(b, {
   1.131 +        x: {enumerable: true, get: function () { return 12479; }},
   1.132 +        y: {enumerable: true, configurable: true, writable: false, value: 0},
   1.133 +        z: {enumerable: true, configurable: false, writable: true, value: 0},
   1.134 +        hidden: {enumerable:false, value: 1334}});
   1.135 +    check(b);
   1.136 +
   1.137 +    // Check corner cases involving property names.
   1.138 +    b = {"-1": -1,
   1.139 +         0xffffffff: null,
   1.140 +         0x100000000: null,
   1.141 +         "": 0,
   1.142 +         "\xff\x7f\u7fff\uffff\ufeff\ufffe": 1, // random unicode id
   1.143 +         "\ud800 \udbff \udc00 \udfff": 2}; // busted surrogate pairs
   1.144 +    check(b);
   1.145 +
   1.146 +    b = [];
   1.147 +    b[-1] = -1;
   1.148 +    b[0xffffffff] = null;
   1.149 +    b[0x100000000] = null;
   1.150 +    b[""] = 0;
   1.151 +    b["\xff\x7f\u7fff\uffff\ufeff\ufffe"] = 1;
   1.152 +    b["\ud800 \udbff \udc00 \udfff"] = 2;
   1.153 +    check(b);
   1.154 +
   1.155 +    // An array's .length property is not enumerable, so it is not cloned.
   1.156 +    b = Array(5);
   1.157 +    assertEq(b.length, 5);
   1.158 +    a = check(b);
   1.159 +    assertEq(a.length, 0);
   1.160 +
   1.161 +    b[1] = "ok";
   1.162 +    a = check(b);
   1.163 +    assertEq(a.length, 2);
   1.164 +
   1.165 +    // Check that prototypes are not cloned, per spec.
   1.166 +    b = Object.create({x:1});
   1.167 +    b.y = 2;
   1.168 +    b.z = 3;
   1.169 +    check(b);
   1.170 +
   1.171 +    // Check that cloning does not separate merge points in the tree.
   1.172 +    var same = {};
   1.173 +    b = {one: same, two: same};
   1.174 +    a = check(b);
   1.175 +    assertEq(a.one === a.two, true);
   1.176 +
   1.177 +    b = [same, same];
   1.178 +    a = check(b);
   1.179 +    assertEq(a[0] === a[1], true);
   1.180 +
   1.181 +    // Try cloning a deep object. Don't fail with "too much recursion".
   1.182 +    b = {};
   1.183 +    var current = b;
   1.184 +    for (var i = 0; i < 10000; i++) {
   1.185 +        var next = {};
   1.186 +        current['x' + i] = next;
   1.187 +        current = next;
   1.188 +    }
   1.189 +    check(b, "deepObject");  // takes 2 seconds :-\
   1.190 +
   1.191 +    /*
   1.192 +      XXX TODO spin this out into its own test
   1.193 +    // This fails quickly with an OOM error. An exception would be nicer.
   1.194 +    function Infinitree() {
   1.195 +        return { get left() { return new Infinitree; },
   1.196 +                 get right() { return new Infinitree; }};
   1.197 +    }
   1.198 +    var threw = false;
   1.199 +    try {
   1.200 +        serialize(new Infinitree);
   1.201 +    } catch (exc) {
   1.202 +        threw = true;
   1.203 +    }
   1.204 +    assertEq(threw, true);
   1.205 +    */
   1.206 +
   1.207 +    // Clone an array with holes.
   1.208 +    check([0, 1, 2, , 4, 5, 6]);
   1.209 +
   1.210 +    // Array holes should not take up space.
   1.211 +    b = [];
   1.212 +    b[255] = 1;
   1.213 +    check(b);
   1.214 +    assertEq(serialize(b).clonebuffer.length < 255, true);
   1.215 +
   1.216 +    // Self-modifying object.
   1.217 +    // This should never read through to b's prototype.
   1.218 +    b = Object.create({y: 2}, 
   1.219 +                      {x: {enumerable: true,
   1.220 +                           configurable: true,
   1.221 +                           get: function() { if (this.hasOwnProperty("y")) delete this.y; return 1; }},
   1.222 +                       y: {enumerable: true,
   1.223 +                           configurable: true,
   1.224 +                           writable: true,
   1.225 +                           value: 3}});
   1.226 +    check(b, "selfModifyingObject");
   1.227 +
   1.228 +    // Ignore properties with object-ids.
   1.229 +    var uri = "http://example.net";
   1.230 +    b = {x: 1, y: 2};
   1.231 +    Object.defineProperty(b, Array(uri, "x"), {enumerable: true, value: 3});
   1.232 +    Object.defineProperty(b, Array(uri, "y"), {enumerable: true, value: 5});
   1.233 +    check(b);
   1.234 +}
   1.235 +
   1.236 +test();
   1.237 +reportCompare(0, 0, 'ok');

mercurial