• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5
6function sloppyDefaultSet(o, p, v) { return o[p] = v }
7function sloppyReflectSet(o, p, v) { return Reflect.set(o, p, v) }
8function strictDefaultSet(o, p, v) { "use strict"; return o[p] = v }
9function strictReflectSet(o, p, v) { "use strict"; return Reflect.set(o, p, v) }
10
11sloppyDefaultSet.shouldThrow = false;
12sloppyReflectSet.shouldThrow = false;
13strictDefaultSet.shouldThrow = true;
14strictReflectSet.shouldThrow = false;
15
16sloppyDefaultSet.returnsBool = false;
17sloppyReflectSet.returnsBool = true;
18strictDefaultSet.returnsBool = false;
19strictReflectSet.returnsBool = true;
20
21
22function assertTrueIf(flag, x) { if (flag) assertTrue(x) }
23function assertFalseIf(flag, x) { if (flag) assertFalse(x) }
24function assertSetFails(mySet, o, p, v) {
25  if (mySet.shouldThrow) {
26    assertThrows(() => mySet(o, p, v), TypeError);
27  } else {
28    assertFalseIf(mySet.returnsBool, mySet(o, p, v));
29  }
30}
31
32
33function dataDescriptor(x) {
34  return {value: x, writable: true, enumerable: true, configurable: true};
35}
36
37
38function toKey(x) {
39  if (typeof x === "symbol") return x;
40  return String(x);
41}
42
43
44var properties =
45    ["bla", "0", 1, Symbol(), {[Symbol.toPrimitive]() {return "a"}}];
46
47
48function TestForwarding(handler, mySet) {
49  assertTrue(undefined == handler.set);
50  assertTrue(undefined == handler.getOwnPropertyDescriptor);
51  assertTrue(undefined == handler.defineProperty);
52
53  var target = {};
54  var proxy = new Proxy(target, handler);
55
56  // Property does not exist on target.
57  for (var p of properties) {
58    assertTrueIf(mySet.returnsBool, mySet(proxy, p, 42));
59    assertSame(42, target[p]);
60  }
61
62  // Property exists as writable data on target.
63  for (var p of properties) {
64    assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
65    assertSame(0, target[p]);
66  }
67
68  // Property exists as non-writable data on target.
69  for (var p of properties) {
70    Object.defineProperty(target, p,
71        {value: 42, configurable: true, writable: false});
72    assertSetFails(mySet, proxy, p, 42);
73    assertSetFails(mySet, proxy, p, 0);
74    assertEquals(42, target[p]);
75  }
76};
77
78(function () {
79  // No trap.
80  var handler = {};
81  TestForwarding(handler, sloppyDefaultSet);
82  TestForwarding(handler, sloppyReflectSet);
83  TestForwarding(handler, strictDefaultSet);
84  TestForwarding(handler, strictReflectSet);
85})();
86
87(function () {
88  // "Undefined" trap.
89  var handler = { set: null };
90  TestForwarding(handler, sloppyDefaultSet);
91  TestForwarding(handler, sloppyReflectSet);
92  TestForwarding(handler, strictDefaultSet);
93  TestForwarding(handler, strictReflectSet);
94})();
95
96
97function TestForwarding2(mySet) {
98  // Check that setting on a proxy without "set" trap correctly triggers its
99  // "getOwnProperty" trap and its "defineProperty" trap.
100
101  var target = {};
102  var handler = {};
103  var observations = [];
104  var proxy = new Proxy(target, handler);
105
106  handler.getOwnPropertyDescriptor = function() {
107      observations.push(arguments);
108      return Reflect.getOwnPropertyDescriptor(...arguments);
109  }
110
111  handler.defineProperty = function() {
112      observations.push(arguments);
113      return Reflect.defineProperty(...arguments);
114  }
115
116  for (var p of properties) {
117    mySet(proxy, p, 42);
118    assertEquals(2, observations.length)
119    assertArrayEquals([target, toKey(p)], observations[0]);
120    assertSame(target, observations[0][0]);
121    assertArrayEquals([target, toKey(p), dataDescriptor(42)], observations[1]);
122    assertSame(target, observations[1][0]);
123    observations = [];
124
125    mySet(proxy, p, 42);
126    assertEquals(2, observations.length)
127    assertArrayEquals([target, toKey(p)], observations[0]);
128    assertSame(target, observations[0][0]);
129    assertArrayEquals([target, toKey(p), {value: 42}], observations[1]);
130    assertSame(target, observations[1][0]);
131    observations = [];
132  }
133}
134
135TestForwarding2(sloppyDefaultSet);
136TestForwarding2(sloppyReflectSet);
137TestForwarding2(strictDefaultSet);
138TestForwarding2(strictReflectSet);
139
140
141function TestInvalidTrap(proxy, mySet) {
142  for (var p of properties) {
143    assertThrows(() => mySet(proxy, p, 42), TypeError);
144  }
145}
146
147(function () {
148  var target = {};
149  var handler = { set: true };
150  var proxy = new Proxy(target, handler);
151
152  TestInvalidTrap(proxy, sloppyDefaultSet);
153  TestInvalidTrap(proxy, sloppyReflectSet);
154  TestInvalidTrap(proxy, strictDefaultSet);
155  TestInvalidTrap(proxy, strictReflectSet);
156})();
157
158
159function TestTrappingFalsish(mySet) {
160  var target = {};
161  var handler = { set() {return ""} };
162  var proxy = new Proxy(target, handler);
163
164  for (var p of properties) {
165    assertSetFails(mySet, proxy, p, 42);
166  }
167}
168
169TestTrappingFalsish(sloppyDefaultSet);
170TestTrappingFalsish(sloppyReflectSet);
171TestTrappingFalsish(strictDefaultSet);
172TestTrappingFalsish(strictReflectSet);
173
174
175function TestTrappingTrueish(mySet) {
176  var target = {};
177  var handler = { set() {return 42} };
178  var proxy = new Proxy(target, handler);
179
180  // Trap returns trueish and property does not exist in target.
181  for (var p of properties) {
182    assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
183  }
184
185  // Trap returns trueish and target property is configurable or writable data.
186  for (var p of properties) {
187    Object.defineProperty(target, p, {configurable: true, writable: true});
188    assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
189    Object.defineProperty(target, p, {configurable: true, writable: false});
190    assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
191    Object.defineProperty(target, p, {configurable: false, writable: true});
192    assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
193  }
194}
195
196TestTrappingTrueish(sloppyDefaultSet);
197TestTrappingTrueish(sloppyReflectSet);
198TestTrappingTrueish(strictDefaultSet);
199TestTrappingTrueish(strictReflectSet);
200
201
202function TestTrappingTrueish2(mySet) {
203  var target = {};
204  var handler = { set() {return 42} };
205  var proxy = new Proxy(target, handler);
206
207  // Trap returns trueish but target property is frozen data.
208  for (var p of properties) {
209    Object.defineProperty(target, p, {
210        configurable: false, writable: false, value: 0
211    });
212    assertThrows(() => mySet(proxy, p, 666), TypeError);  // New value.
213    assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));  // Old value.
214  }
215};
216
217TestTrappingTrueish2(sloppyDefaultSet);
218TestTrappingTrueish2(sloppyReflectSet);
219TestTrappingTrueish2(strictDefaultSet);
220TestTrappingTrueish2(strictReflectSet);
221
222
223function TestTrappingTrueish3(mySet) {
224  var target = {};
225  var handler = { set() {return 42} };
226  var proxy = new Proxy(target, handler);
227
228  // Trap returns trueish and target property is configurable accessor.
229  for (var p of properties) {
230    Object.defineProperty(target, p, { configurable: true, set: undefined });
231    assertTrueIf(mySet.returnsBool, mySet(proxy, p, 0));
232  }
233
234  // Trap returns trueish and target property is non-configurable accessor.
235  for (var p of properties) {
236    Object.defineProperty(target, p, { configurable: false, set: undefined });
237    assertThrows(() => mySet(proxy, p, 0));
238  }
239};
240
241TestTrappingTrueish3(sloppyDefaultSet);
242TestTrappingTrueish3(sloppyReflectSet);
243TestTrappingTrueish3(strictDefaultSet);
244TestTrappingTrueish3(strictReflectSet);
245
246
247function TestTrapReceiverArgument(mySet) {
248  var target = {};
249  var handler = {};
250  var observations = [];
251  var proxy = new Proxy(target, handler);
252  var object = Object.create(proxy);
253
254  handler.set = function() {
255      observations.push(arguments);
256      return Reflect.set(...arguments);
257  }
258
259  for (var p of properties) {
260    mySet(object, p, 42);
261    assertEquals(1, observations.length)
262    assertArrayEquals([target, toKey(p), 42, object], observations[0]);
263    assertSame(target, observations[0][0]);
264    assertSame(object, observations[0][3]);
265    observations = [];
266  }
267};
268
269TestTrapReceiverArgument(sloppyDefaultSet);
270TestTrapReceiverArgument(sloppyReflectSet);
271TestTrapReceiverArgument(strictDefaultSet);
272TestTrapReceiverArgument(strictReflectSet);
273
274
275(function TestTrapReceiverArgument2() {
276  // Check that non-object receiver is passed through as well.
277
278  var target = {};
279  var handler = {};
280  var observations = [];
281  var proxy = new Proxy(target, handler);
282
283  handler.set = function() {
284      observations.push(arguments);
285      return Reflect.set(...arguments);
286  }
287
288  for (var p of properties) {
289    for (var receiver of [null, undefined, 1]) {
290      Reflect.set(proxy, p, 42, receiver);
291      assertEquals(1, observations.length)
292      assertArrayEquals([target, toKey(p), 42, receiver], observations[0]);
293      assertSame(target, observations[0][0]);
294      assertSame(receiver, observations[0][3]);
295      observations = [];
296    }
297  }
298
299  var object = Object.create(proxy);
300  for (var p of properties) {
301    for (var receiver of [null, undefined, 1]) {
302      Reflect.set(object, p, 42, receiver);
303      assertEquals(1, observations.length);
304      assertArrayEquals([target, toKey(p), 42, receiver], observations[0]);
305      assertSame(target, observations[0][0]);
306      assertSame(receiver, observations[0][3]);
307      observations = [];
308    }
309  }
310})();
311