Home | History | Annotate | Download | only in harmony
      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 // Flags: --harmony-proxies --harmony-reflect
      6 
      7 
      8 function sloppyDefaultSet(o, p, v) { return o[p] = v }
      9 function sloppyReflectSet(o, p, v) { return Reflect.set(o, p, v) }
     10 function strictDefaultSet(o, p, v) { "use strict"; return o[p] = v }
     11 function strictReflectSet(o, p, v) { "use strict"; return Reflect.set(o, p, v) }
     12 
     13 sloppyDefaultSet.shouldThrow = false;
     14 sloppyReflectSet.shouldThrow = false;
     15 strictDefaultSet.shouldThrow = true;
     16 strictReflectSet.shouldThrow = false;
     17 
     18 sloppyDefaultSet.returnsBool = false;
     19 sloppyReflectSet.returnsBool = true;
     20 strictDefaultSet.returnsBool = false;
     21 strictReflectSet.returnsBool = true;
     22 
     23 
     24 function assertTrueIf(flag, x) { if (flag) assertTrue(x) }
     25 function assertFalseIf(flag, x) { if (flag) assertFalse(x) }
     26 function 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 
     35 function dataDescriptor(x) {
     36   return {value: x, writable: true, enumerable: true, configurable: true};
     37 }
     38 
     39 
     40 function toKey(x) {
     41   if (typeof x === "symbol") return x;
     42   return String(x);
     43 }
     44 
     45 
     46 var properties =
     47     ["bla", "0", 1, Symbol(), {[Symbol.toPrimitive]() {return "a"}}];
     48 
     49 
     50 function 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 
     99 function 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 
    137 TestForwarding2(sloppyDefaultSet);
    138 TestForwarding2(sloppyReflectSet);
    139 TestForwarding2(strictDefaultSet);
    140 TestForwarding2(strictReflectSet);
    141 
    142 
    143 function 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 
    161 function 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 
    171 TestTrappingFalsish(sloppyDefaultSet);
    172 TestTrappingFalsish(sloppyReflectSet);
    173 TestTrappingFalsish(strictDefaultSet);
    174 TestTrappingFalsish(strictReflectSet);
    175 
    176 
    177 function 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 
    198 TestTrappingTrueish(sloppyDefaultSet);
    199 TestTrappingTrueish(sloppyReflectSet);
    200 TestTrappingTrueish(strictDefaultSet);
    201 TestTrappingTrueish(strictReflectSet);
    202 
    203 
    204 function 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 
    219 TestTrappingTrueish2(sloppyDefaultSet);
    220 TestTrappingTrueish2(sloppyReflectSet);
    221 TestTrappingTrueish2(strictDefaultSet);
    222 TestTrappingTrueish2(strictReflectSet);
    223 
    224 
    225 function 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 
    243 TestTrappingTrueish3(sloppyDefaultSet);
    244 TestTrappingTrueish3(sloppyReflectSet);
    245 TestTrappingTrueish3(strictDefaultSet);
    246 TestTrappingTrueish3(strictReflectSet);
    247 
    248 
    249 function 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 
    271 TestTrapReceiverArgument(sloppyDefaultSet);
    272 TestTrapReceiverArgument(sloppyReflectSet);
    273 TestTrapReceiverArgument(strictDefaultSet);
    274 TestTrapReceiverArgument(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 })();
    313