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