tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

clone-complex-object.js (8675B)


      1 // |reftest| slow skip-if(!xulRuntime.shell)
      2 // -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
      3 // Any copyright is dedicated to the Public Domain.
      4 // http://creativecommons.org/licenses/publicdomain/
      5 
      6 // Set of properties on a cloned object that are legitimately non-enumerable,
      7 // grouped by object type.
      8 var non_enumerable = { 'Array': [ 'length' ],
      9                       'String': [ 'length' ] };
     10 
     11 // Set of properties on a cloned object that are legitimately non-configurable,
     12 // grouped by object type. The property name '0' stands in for any indexed
     13 // property.
     14 var non_configurable = { 'String': [ 0 ] };
     15 
     16 // Set of properties on a cloned object that are legitimately non-writable,
     17 // grouped by object type. The property name '0' stands in for any indexed
     18 // property.
     19 var non_writable = { 'String': [ 0 ] };
     20 
     21 function classOf(obj) {
     22    var classString = Object.prototype.toString.call(obj);
     23    var [ all, classname ] = classString.match(/\[object (\w+)/);
     24    return classname;
     25 }
     26 
     27 function isIndex(p) {
     28    var u = p >>> 0;
     29    return ("" + u == p && u != 0xffffffff);
     30 }
     31 
     32 function notIndex(p) {
     33    return !isIndex(p);
     34 }
     35 
     36 function tableContains(table, cls, prop) {
     37    if (isIndex(prop))
     38        prop = 0;
     39    if (cls.match(/\wArray$/))
     40        cls = "(typed array)";
     41    var exceptionalProps = table[cls] || [];
     42    return exceptionalProps.indexOf(prop) != -1;
     43 }
     44 
     45 function shouldBeConfigurable(cls, prop) {
     46    return !tableContains(non_configurable, cls, prop);
     47 }
     48 
     49 function shouldBeWritable(cls, prop) {
     50    return !tableContains(non_writable, cls, prop);
     51 }
     52 
     53 function ownProperties(obj) {
     54    return Object.getOwnPropertyNames(obj).
     55        map(function (p) { return [p, Object.getOwnPropertyDescriptor(obj, p)]; });
     56 }
     57 
     58 function isCloneable(pair) {
     59    return typeof pair[0] === 'string' && pair[1].enumerable;
     60 }
     61 
     62 function compareProperties(a, b, stack, path) {
     63    var ca = classOf(a);
     64 
     65    // 'b', the original object, may have non-enumerable or XMLName properties;
     66    // ignore them. 'a', the clone, should not have any non-enumerable
     67    // properties (except .length, if it's an Array or String) or XMLName
     68    // properties.
     69    var pb = ownProperties(b).filter(isCloneable);
     70    var pa = ownProperties(a);
     71    for (var i = 0; i < pa.length; i++) {
     72        var propname = pa[i][0];
     73        assertEq(typeof propname, "string", "clone should not have E4X properties " + path);
     74        if (!pa[i][1].enumerable) {
     75            if (tableContains(non_enumerable, ca, propname)) {
     76                // remove it so that the comparisons below will work
     77                pa.splice(i, 1);
     78                i--;
     79            } else {
     80                throw new Error("non-enumerable clone property " + propname + " " + path);
     81            }
     82        }
     83    }
     84 
     85    // Check that, apart from properties whose names are array indexes,
     86    // the enumerable properties appear in the same order.
     87    var aNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex);
     88    var bNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex);
     89    assertEq(aNames.join(","), bNames.join(","), path);
     90 
     91    // Check that the lists are the same when including array indexes.
     92    function byName(a, b) { a = a[0]; b = b[0]; return a < b ? -1 : a === b ? 0 : 1; }
     93    pa.sort(byName);
     94    pb.sort(byName);
     95    assertEq(pa.length, pb.length, "should see the same number of properties " + path);
     96    for (var i = 0; i < pa.length; i++) {
     97        var aName = pa[i][0];
     98        var bName = pb[i][0];
     99        assertEq(aName, bName, path);
    100 
    101        var path2 = isIndex(aName) ? path + "[" + aName + "]" : path + "." + aName;
    102        var da = pa[i][1];
    103        var db = pb[i][1];
    104        assertEq(da.configurable, shouldBeConfigurable(ca, aName), path2);
    105        assertEq(da.writable, shouldBeWritable(ca, aName), path2);
    106        assertEq("value" in da, true, path2);
    107        var va = da.value;
    108        var vb = b[pb[i][0]];
    109        stack.push([va, vb, path2]);
    110    }
    111 }
    112 
    113 function isClone(a, b) {
    114    var stack = [[a, b, 'obj']];
    115    var memory = new WeakMap();
    116    var rmemory = new WeakMap();
    117 
    118    while (stack.length > 0) {
    119        var pair = stack.pop();
    120        var x = pair[0], y = pair[1], path = pair[2];
    121        if (typeof x !== "object" || x === null) {
    122            // x is primitive.
    123            assertEq(x, y, "equal primitives");
    124        } else if (x instanceof Date) {
    125            assertEq(x.getTime(), y.getTime(), "equal times for cloned Dates");
    126        } else if (memory.has(x)) {
    127            // x is an object we have seen before in a.
    128            assertEq(y, memory.get(x), "repeated object the same");
    129            assertEq(rmemory.get(y), x, "repeated object's clone already seen");
    130        } else {
    131            // x is an object we have not seen before.
    132     // Check that we have not seen y before either.
    133            assertEq(rmemory.has(y), false);
    134 
    135            var xcls = classOf(x);
    136            var ycls = classOf(y);
    137            assertEq(xcls, ycls, "same [[Class]]");
    138 
    139            // clone objects should have the default prototype of the class
    140            assertEq(Object.getPrototypeOf(x), this[xcls].prototype);
    141 
    142            compareProperties(x, y, stack, path);
    143 
    144            // Record that we have seen this pair of objects.
    145            memory.set(x, y);
    146            rmemory.set(y, x);
    147        }
    148    }
    149    return true;
    150 }
    151 
    152 function check(val) {
    153    var clone = deserialize(serialize(val));
    154    assertEq(isClone(val, clone), true);
    155    return clone;
    156 }
    157 
    158 // Various recursive objects
    159 
    160 // Recursive array.
    161 var a = [];
    162 a[0] = a;
    163 check(a);
    164 
    165 // Recursive Object.
    166 var b = {};
    167 b.next = b;
    168 check(b);
    169 
    170 // Mutually recursive objects.
    171 var a = [];
    172 var b = {};
    173 var c = {};
    174 a[0] = b;
    175 a[1] = b;
    176 a[2] = b;
    177 b.next = a;
    178 check(a);
    179 check(b);
    180 
    181 // A date
    182 check(new Date);
    183 
    184 // A recursive object that is very large.
    185 a = [];
    186 b = a;
    187 for (var i = 0; i < 10000; i++) {
    188    b[0] = {};
    189    b[1] = [];
    190    b = b[1];
    191 }
    192 b[0] = {owner: a};
    193 b[1] = [];
    194 check(a);
    195 
    196 // Date objects should not be identical even if representing the same date
    197 var ar = [ new Date(1000), new Date(1000) ];
    198 var clone = check(ar);
    199 assertEq(clone[0] === clone[1], false);
    200 
    201 // Identity preservation for various types of objects
    202 
    203 function checkSimpleIdentity(v)
    204 {
    205    a = check([ v, v ]);
    206    assertEq(a[0] === a[1], true);
    207    return a;
    208 }
    209 
    210 var v = new Boolean(true);
    211 checkSimpleIdentity(v);
    212 
    213 v = new Number(17);
    214 checkSimpleIdentity(v);
    215 
    216 v = new String("yo");
    217 checkSimpleIdentity(v);
    218 
    219 v = "fish";
    220 checkSimpleIdentity(v);
    221 
    222 v = new Int8Array([ 10, 20 ]);
    223 checkSimpleIdentity(v);
    224 
    225 v = new ArrayBuffer(7);
    226 checkSimpleIdentity(v);
    227 
    228 v = new Date(1000);
    229 b = [ v, v, { 'date': v } ];
    230 clone = check(b);
    231 assertEq(clone[0] === clone[1], true);
    232 assertEq(clone[0], clone[2]['date']);
    233 assertEq(clone[0] === v, false);
    234 
    235 // Reduced and modified from postMessage_structured_clone test
    236 let foo = { };
    237 let baz = { };
    238 let obj = { 'foo': foo,
    239            'bar': { 'foo': foo },
    240            'expando': { 'expando': baz },
    241            'baz': baz };
    242 check(obj);
    243 
    244 for (obj of getTestContent())
    245    check(obj);
    246 
    247 // Stolen wholesale from postMessage_structured_clone_helper.js
    248 function* getTestContent()
    249 {
    250  yield "hello";
    251  yield 2+3;
    252  yield 12;
    253  yield null;
    254  yield "complex" + "string";
    255  yield new Object();
    256  yield new Date(1306113544);
    257  yield [1, 2, 3, 4, 5];
    258  let obj = new Object();
    259  obj.foo = 3;
    260  obj.bar = "hi";
    261  obj.baz = new Date(1306113544);
    262  obj.boo = obj;
    263  yield obj;
    264 
    265  let recursiveobj = new Object();
    266  recursiveobj.a = recursiveobj;
    267  recursiveobj.foo = new Object();
    268  recursiveobj.foo.bar = "bar";
    269  recursiveobj.foo.backref = recursiveobj;
    270  recursiveobj.foo.baz = 84;
    271  recursiveobj.foo.backref2 = recursiveobj;
    272  recursiveobj.bar = new Object();
    273  recursiveobj.bar.foo = "foo";
    274  recursiveobj.bar.backref = recursiveobj;
    275  recursiveobj.bar.baz = new Date(1306113544);
    276  recursiveobj.bar.backref2 = recursiveobj;
    277  recursiveobj.expando = recursiveobj;
    278  yield recursiveobj;
    279 
    280  obj = new Object();
    281  obj.expando1 = 1;
    282  obj.foo = new Object();
    283  obj.foo.bar = 2;
    284  obj.bar = new Object();
    285  obj.bar.foo = obj.foo;
    286  obj.expando = new Object();
    287  obj.expando.expando = new Object();
    288  obj.expando.expando.obj = obj;
    289  obj.expando2 = 4;
    290  obj.baz = obj.expando.expando;
    291  obj.blah = obj.bar;
    292  obj.foo.baz = obj.blah;
    293  obj.foo.blah = obj.blah;
    294  yield obj;
    295 
    296  let diamond = new Object();
    297  obj = new Object();
    298  obj.foo = "foo";
    299  obj.bar = 92;
    300  obj.backref = diamond;
    301  diamond.ref1 = obj;
    302  diamond.ref2 = obj;
    303  yield diamond;
    304 
    305  let doubleref = new Object();
    306  obj = new Object();
    307  doubleref.ref1 = obj;
    308  doubleref.ref2 = obj;
    309  yield doubleref;
    310 }
    311 
    312 reportCompare(0, 0, 'ok');