blob: 19f39f9a652d1ffd4a97890a351cd5c89456a763 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00005
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})();