Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / tests / js1_8_5 / extensions / clone-object.js
1 // -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 // Any copyright is dedicated to the Public Domain.
3 // http://creativecommons.org/licenses/publicdomain/
4
5 // Assert that cloning b does the right thing as far as we can tell.
6 // Caveat: getters in b must produce the same value each time they're
7 // called. We may call them several times.
8 //
9 // If desc is provided, then the very first thing we do to b is clone it.
10 // (The self-modifying object test counts on this.)
11 //
12 function check(b, desc) {
13     function classOf(obj) {
14         return Object.prototype.toString.call(obj);
15     }
16
17     function ownProperties(obj) {
18         return Object.getOwnPropertyNames(obj).
19             map(function (p) { return [p, Object.getOwnPropertyDescriptor(obj, p)]; });
20     }
21
22     function isCloneable(pair) {
23         return typeof pair[0] === 'string' && pair[1].enumerable;
24     }
25
26     function notIndex(p) {
27         var u = p >>> 0;
28         return !("" + u == p && u != 0xffffffff);
29     }
30
31     function assertIsCloneOf(a, b, path) {
32         assertEq(a === b, false);
33
34         var ca = classOf(a);
35         assertEq(ca, classOf(b), path);
36
37         assertEq(Object.getPrototypeOf(a),
38                  ca == "[object Object]" ? Object.prototype : Array.prototype,
39                  path);
40
41         // 'b', the original object, may have non-enumerable or XMLName
42         // properties; ignore them.  'a', the clone, should not have any
43         // non-enumerable properties (except .length, if it's an Array) or
44         // XMLName properties.
45         var pb = ownProperties(b).filter(isCloneable);
46         var pa = ownProperties(a);
47         for (var i = 0; i < pa.length; i++) {
48             assertEq(typeof pa[i][0], "string", "clone should not have E4X properties " + path);
49             if (!pa[i][1].enumerable) {
50                 if (Array.isArray(a) && pa[i][0] == "length") {
51                     // remove it so that the comparisons below will work
52                     pa.splice(i, 1);
53                     i--;
54                 } else {
55                     throw new Error("non-enumerable clone property " + uneval(pa[i][0]) + " " + path);
56                 }
57             }
58         }
59
60         // Check that, apart from properties whose names are array indexes, 
61         // the enumerable properties appear in the same order.
62         var aNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex);
63         var bNames = pa.map(function (pair) { return pair[1]; }).filter(notIndex);
64         assertEq(aNames.join(","), bNames.join(","), path);
65
66         // Check that the lists are the same when including array indexes.
67         function byName(a, b) { a = a[0]; b = b[0]; return a < b ? -1 : a === b ? 0 : 1; }
68         pa.sort(byName);
69         pb.sort(byName);
70         assertEq(pa.length, pb.length, "should see the same number of properties " + path);
71         for (var i = 0; i < pa.length; i++) {
72             var aName = pa[i][0];
73             var bName = pb[i][0];
74             assertEq(aName, bName, path);
75
76             var path2 = path + "." + aName;
77             var da = pa[i][1];
78             var db = pb[i][1];
79             assertEq(da.configurable, true, path2);
80             assertEq(da.writable, true, path2);
81             assertEq("value" in da, true, path2);
82             var va = da.value;
83             var vb = b[pb[i][0]];
84             if (typeof va === "object" && va !== null)
85                 queue.push([va, vb, path2]);
86             else
87                 assertEq(va, vb, path2);
88         }
89     }
90
91     var banner = "while testing clone of " + (desc || uneval(b));
92     var a = deserialize(serialize(b));
93     var queue = [[a, b, banner]];
94     while (queue.length) {
95         var triple = queue.shift();
96         assertIsCloneOf(triple[0], triple[1], triple[2]);
97     }
98
99     return a; // for further testing
100 }
101
102 function test() {
103     check({});
104     check([]);
105     check({x: 0});
106     check({x: 0.7, p: "forty-two", y: null, z: undefined});
107     check(Array.prototype);
108     check(Object.prototype);
109
110     // before and after
111     var b, a;
112
113     // Slow array.
114     b = [, 1, 2, 3];
115     b.expando = true;
116     b[5] = 5;
117     b[0] = 0;
118     b[4] = 4;
119     delete b[2];
120     check(b);
121
122     // Check cloning properties other than basic data properties. (check()
123     // asserts that the properties of the clone are configurable, writable,
124     // enumerable data properties.)
125     b = {};
126     Object.defineProperties(b, {
127         x: {enumerable: true, get: function () { return 12479; }},
128         y: {enumerable: true, configurable: true, writable: false, value: 0},
129         z: {enumerable: true, configurable: false, writable: true, value: 0},
130         hidden: {enumerable:false, value: 1334}});
131     check(b);
132
133     // Check corner cases involving property names.
134     b = {"-1": -1,
135          0xffffffff: null,
136          0x100000000: null,
137          "": 0,
138          "\xff\x7f\u7fff\uffff\ufeff\ufffe": 1, // random unicode id
139          "\ud800 \udbff \udc00 \udfff": 2}; // busted surrogate pairs
140     check(b);
141
142     b = [];
143     b[-1] = -1;
144     b[0xffffffff] = null;
145     b[0x100000000] = null;
146     b[""] = 0;
147     b["\xff\x7f\u7fff\uffff\ufeff\ufffe"] = 1;
148     b["\ud800 \udbff \udc00 \udfff"] = 2;
149     check(b);
150
151     // An array's .length property is not enumerable, so it is not cloned.
152     b = Array(5);
153     assertEq(b.length, 5);
154     a = check(b);
155     assertEq(a.length, 0);
156
157     b[1] = "ok";
158     a = check(b);
159     assertEq(a.length, 2);
160
161     // Check that prototypes are not cloned, per spec.
162     b = Object.create({x:1});
163     b.y = 2;
164     b.z = 3;
165     check(b);
166
167     // Check that cloning separates merge points in the tree, per spec.
168     var same = {};
169     b = {one: same, two: same};
170     a = check(b);
171     assertEq(a.one === a.two, false);
172
173     b = [same, same];
174     a = check(b);
175     assertEq(a[0] === a[1], false);
176
177     // Try cloning a deep object. Don't fail with "too much recursion".
178     b = {};
179     var current = b;
180     for (var i = 0; i < 10000; i++) {
181         var next = {};
182         current['x' + i] = next;
183         current = next;
184     }
185     check(b, "deepObject");  // takes 2 seconds :-\
186
187     /*
188       XXX TODO spin this out into its own test
189     // This fails quickly with an OOM error. An exception would be nicer.
190     function Infinitree() {
191         return { get left() { return new Infinitree; },
192                  get right() { return new Infinitree; }};
193     }
194     var threw = false;
195     try {
196         serialize(new Infinitree);
197     } catch (exc) {
198         threw = true;
199     }
200     assertEq(threw, true);
201     */
202
203     // Clone an array with holes.
204     check([0, 1, 2, , 4, 5, 6]);
205
206     // Array holes should not take up space.
207     b = [];
208     b[255] = 1;
209     check(b);
210     assertEq(serialize(b).length < 255, true);
211
212     // Self-modifying object.
213     // This should never read through to b's prototype.
214     b = Object.create({y: 2}, 
215                       {x: {enumerable: true,
216                            configurable: true,
217                            get: function() { if (this.hasOwnProperty("y")) delete this.y; return 1; }},
218                        y: {enumerable: true,
219                            configurable: true,
220                            writable: true,
221                            value: 3}});
222     check(b, "selfModifyingObject");
223
224     // Ignore properties with object-ids.
225     var uri = "http://example.net";
226     b = {x: 1, y: 2};
227     Object.defineProperty(b, QName(uri, "x"), {enumerable: true, value: 3});
228     Object.defineProperty(b, QName(uri, "y"), {enumerable: true, value: 5});
229     check(b);
230 }
231
232 test();
233 reportCompare(0, 0, 'ok');