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