1// Copyright 2011 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28// Helper.
29
30function TestWithProxies(test, x, y, z) {
31  test(function(h){ return new Proxy({}, h) }, x, y, z)
32}
33
34
35// Iterate over a proxy.
36
37function TestForIn(properties, handler) {
38  TestWithProxies(TestForIn2, properties, handler)
39}
40
41function TestForIn2(create, properties, handler) {
42  var p = create(handler)
43  var found = []
44  for (var x in p) found.push(x)
45  assertArrayEquals(properties, found)
46}
47
48TestForIn(["0", "a"], {
49  ownKeys() { return ["0", "a"] },
50  has(target, property) { return true },
51  getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }}
52})
53
54TestForIn(["null", "a"], {
55  ownKeys() { return this.enumerate() },
56  enumerate() { return ["null", "a"] },
57  has(target, property) { return true },
58  getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }}
59})
60
61
62// Iterate over an object with a proxy prototype.
63
64function TestForInDerived(properties, handler) {
65  TestWithProxies(TestForInDerived2, properties, handler)
66}
67
68function TestForInDerived2(create, properties, handler) {
69  var p = create(handler)
70  var o = Object.create(p)
71  o.z = 0
72  var found = []
73  for (var x in o) found.push(x)
74  assertArrayEquals(["z"].concat(properties), found)
75
76  var oo = Object.create(o)
77  oo.y = 0
78  var found = []
79  for (var x in oo) found.push(x)
80  assertArrayEquals(["y", "z"].concat(properties), found)
81}
82
83TestForInDerived(["0", "a"], {
84  ownKeys: function() { return ["0", "a"] },
85  has: function(t, k) { return k == "0" || k == "a" },
86  getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }}
87})
88
89TestForInDerived(["null", "a"], {
90  ownKeys: function() { return this.enumerate() },
91  enumerate: function() { return ["null", "a"] },
92  has: function(t, k) { return k == "null" || k == "a" },
93  getOwnPropertyDescriptor() { return { enumerable: true, configurable: true }}
94})
95
96
97
98// Throw exception in ownKeys trap.
99
100function TestForInThrow(handler) {
101  TestWithProxies(TestForInThrow2, handler)
102}
103
104function TestForInThrow2(create, handler) {
105  var p = create(handler)
106  var o = Object.create(p)
107  assertThrowsEquals(function(){ for (var x in p) {} }, "myexn")
108  assertThrowsEquals(function(){ for (var x in o) {} }, "myexn")
109}
110
111TestForInThrow({
112  ownKeys: function() { throw "myexn" }
113})
114
115TestForInThrow({
116  ownKeys: function() { return this.enumerate() },
117  enumerate: function() { throw "myexn" }
118})
119
120TestForInThrow(new Proxy({}, {
121  get: function(pr, pk) {
122    return function() { throw "myexn" }
123  }
124}));
125
126
127function keys(object) {
128  var keys = [];
129  for (var k in object) {
130    keys.push(k);
131  }
132  return keys;
133}
134
135(function testKeysProxyOnProtoEmpty() {
136  var p = new Proxy({}, {
137    ownKeys() { return []; },
138  });
139  var o = [0];
140  o.__proto__ = p;
141  assertEquals(["0"], keys(o));
142
143  delete o[0];
144  assertEquals([], keys(o));
145})();
146
147(function testKeysProxyOnProto() {
148  var handler = {ownKeys() { return ["0"]; }};
149  var proxy = new Proxy({}, handler);
150  var object = [0];
151  object.__proto__ = proxy;
152  assertEquals(["0"], keys(object));
153
154  // The Proxy doesn't set its ownKeys enumerable.
155  delete object[0];
156  assertEquals([], keys(object));
157
158  // The [[Has]] trap has no influence on which are enumerable properties are
159  // shown in for-in.
160  handler.has = function() { return true };
161  assertEquals([], keys(object));
162
163  handler.getOwnPropertyDescriptor = function() {
164    return {enumerable: true, configurable: true}
165  }
166  assertEquals(["0"], keys(object));
167})();
168
169(function testKeysProxyProto() {
170  var target = {t1:true, t2:true};
171  var handler = {};
172  var proxy = new Proxy(target, handler);
173
174  assertEquals(["t1", "t2"], keys(proxy));
175
176  target.__proto__ = {p1:true, p2:true};
177  assertEquals(["t1", "t2", "p1", "p2"], keys(proxy));
178
179  handler.getPrototypeOf = function(target) {
180    return {p3:true, p4:true};
181  };
182  // for-in walks the prototype chain for the [[Has]] / Enumerable check.
183  assertEquals(["t1", "t2", "p3", "p4"], keys(proxy));
184
185  // [[Has]] is not used in for-in.
186  handler.has = function() { return false };
187  assertEquals(["t1", "t2", "p3", "p4"], keys(proxy));
188
189  // Proxy intercepts enumerability check.
190  handler.getOwnPropertyDescriptor = function() {
191    return {enumerable: false, configurable: true}
192  }
193  assertEquals([], keys(proxy));
194
195  handler.getOwnPropertyDescriptor = function() {
196    return {enumerable: true, configurable: true}
197  }
198  assertEquals(["t1", "t2", "p3", "p4"], keys(proxy));
199
200  handler.getOwnPropertyDescriptor = function(target, key) {
201    return {
202      enumerable: key in target,
203      configurable: true
204    }
205  }
206  assertEquals(["t1", "t2"], keys(proxy));
207
208  handler.getPrototypeOf = function() { throw "error" };
209  assertThrowsEquals(() => {keys(proxy)}, "error");
210})();
211
212(function testNestedProxy() {
213  var handler = {
214    ownKeys() {
215      return ['c'];
216    },
217    getOwnPropertyDescriptor() { return {configurable: true, enumerable: true } }
218  }
219  var proxy = new Proxy({}, handler);
220  var proxy2 = new Proxy(proxy, {});
221  assertEquals(['c'], keys(proxy));
222  assertEquals(['c'], keys(proxy2));
223})();
224