Home | History | Annotate | Download | only in es6
      1 // Copyright 2013 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 // Flags: --harmony-scoping --harmony-proxies
     29 
     30 // Test for-of semantics.
     31 
     32 "use strict";
     33 
     34 
     35 // First, some helpers.
     36 
     37 function* values() {
     38   for (var i = 0; i < arguments.length; i++) {
     39     yield arguments[i];
     40   }
     41 }
     42 
     43 function wrap_iterator(iterator) {
     44     var iterable = {};
     45     iterable[Symbol.iterator] = function() { return iterator; };
     46     return iterable;
     47 }
     48 
     49 function integers_until(max) {
     50   function next() {
     51     var ret = { value: this.n, done: this.n == max };
     52     this.n++;
     53     return ret;
     54   }
     55   return wrap_iterator({ next: next, n: 0 });
     56 }
     57 
     58 function results(results) {
     59   var i = 0;
     60   function next() {
     61     return results[i++];
     62   }
     63   return wrap_iterator({ next: next });
     64 }
     65 
     66 function* integers_from(n) {
     67   while (1) yield n++;
     68 }
     69 
     70 // A destructive append.
     71 function append(x, tail) {
     72   tail[tail.length] = x;
     73   return tail;
     74 }
     75 
     76 function sum(x, tail) {
     77   return x + tail;
     78 }
     79 
     80 function fold(cons, seed, iterable) {
     81   for (var x of iterable) {
     82     seed = cons(x, seed);
     83   }
     84   return seed;
     85 }
     86 
     87 function* take(iterable, n) {
     88   if (n == 0) return;
     89   for (let x of iterable) {
     90     yield x;
     91     if (--n == 0) break;
     92   }
     93 }
     94 
     95 function nth(iterable, n) {
     96   for (let x of iterable) {
     97     if (n-- == 0) return x;
     98   }
     99   throw "unreachable";
    100 }
    101 
    102 function* skip_every(iterable, n) {
    103   var i = 0;
    104   for (let x of iterable) {
    105     if (++i % n == 0) continue;
    106     yield x;
    107   }
    108 }
    109 
    110 function* iter_map(iterable, f) {
    111   for (var x of iterable) {
    112     yield f(x);
    113   }
    114 }
    115 function nested_fold(cons, seed, iterable) {
    116   var visited = []
    117   for (let x of iterable) {
    118     for (let y of x) {
    119       seed = cons(y, seed);
    120     }
    121   }
    122   return seed;
    123 }
    124 
    125 function* unreachable(iterable) {
    126   for (let x of iterable) {
    127     throw "not reached";
    128   }
    129 }
    130 
    131 function one_time_getter(o, prop, val) {
    132   function set_never() { throw "unreachable"; }
    133   var gotten = false;
    134   function get_once() {
    135     if (gotten) throw "got twice";
    136     gotten = true;
    137     return val;
    138   }
    139   Object.defineProperty(o, prop, {get: get_once, set: set_never})
    140   return o;
    141 }
    142 
    143 function never_getter(o, prop) {
    144   function never() { throw "unreachable"; }
    145   Object.defineProperty(o, prop, {get: never, set: never})
    146   return o;
    147 }
    148 
    149 function remove_next_after(iterable, n) {
    150   var iterator = iterable[Symbol.iterator]();
    151   function next() {
    152     if (n-- == 0) delete this.next;
    153     return iterator.next();
    154   }
    155   return wrap_iterator({ next: next });
    156 }
    157 
    158 function poison_next_after(iterable, n) {
    159   var iterator = iterable[Symbol.iterator]();
    160   function next() {
    161     return iterator.next();
    162   }
    163   function next_getter() {
    164     if (n-- < 0)
    165       throw "poisoned";
    166     return next;
    167   }
    168   var o = {};
    169   Object.defineProperty(o, 'next', { get: next_getter });
    170   return wrap_iterator(o);
    171 }
    172 
    173 // Now, the tests.
    174 
    175 // Non-generator iterators.
    176 assertEquals(45, fold(sum, 0, integers_until(10)));
    177 // Generator iterators.
    178 assertEquals([1, 2, 3], fold(append, [], values(1, 2, 3)));
    179 // Break.
    180 assertEquals(45, fold(sum, 0, take(integers_from(0), 10)));
    181 // Continue.
    182 assertEquals(90, fold(sum, 0, take(skip_every(integers_from(0), 2), 10)));
    183 // Return.
    184 assertEquals(10, nth(integers_from(0), 10));
    185 // Nested for-of.
    186 assertEquals([0, 0, 1, 0, 1, 2, 0, 1, 2, 3],
    187              nested_fold(append,
    188                          [],
    189                          iter_map(integers_until(5), integers_until)));
    190 // Result objects with sparse fields.
    191 assertEquals([undefined, 1, 2, 3],
    192              fold(append, [],
    193                   results([{ done: false },
    194                            { value: 1, done: false },
    195                            // A missing "done" is the same as undefined, which
    196                            // is false.
    197                            { value: 2 },
    198                            // Not done.
    199                            { value: 3, done: 0 },
    200                            // Done.
    201                            { value: 4, done: 42 }])));
    202 // Results that are not objects.
    203 assertEquals([undefined, undefined, undefined],
    204              fold(append, [],
    205                   results([10, "foo", /qux/, { value: 37, done: true }])));
    206 // Getters (shudder).
    207 assertEquals([1, 2],
    208              fold(append, [],
    209                   results([one_time_getter({ value: 1 }, 'done', false),
    210                            one_time_getter({ done: false }, 'value', 2),
    211                            { value: 37, done: true },
    212                            never_getter(never_getter({}, 'done'), 'value')])));
    213 
    214 // Unlike the case with for-in, null and undefined cause an error.
    215 assertThrows('fold(sum, 0, unreachable(null))', TypeError);
    216 assertThrows('fold(sum, 0, unreachable(undefined))', TypeError);
    217 
    218 // Other non-iterators do cause an error.
    219 assertThrows('fold(sum, 0, unreachable({}))', TypeError);
    220 assertThrows('fold(sum, 0, unreachable(false))', TypeError);
    221 assertThrows('fold(sum, 0, unreachable(37))', TypeError);
    222 
    223 // "next" is looked up each time.
    224 assertThrows('fold(sum, 0, remove_next_after(integers_until(10), 5))',
    225              TypeError);
    226 // It is not called at any other time.
    227 assertEquals(45,
    228              fold(sum, 0, remove_next_after(integers_until(10), 10)));
    229 // It is not looked up too many times.
    230 assertEquals(45,
    231              fold(sum, 0, poison_next_after(integers_until(10), 10)));
    232 
    233 function labelled_continue(iterable) {
    234   var n = 0;
    235 outer:
    236   while (true) {
    237     n++;
    238     for (var x of iterable) continue outer;
    239     break;
    240   }
    241   return n;
    242 }
    243 assertEquals(11, labelled_continue(integers_until(10)));
    244 
    245 function labelled_break(iterable) {
    246   var n = 0;
    247 outer:
    248   while (true) {
    249     n++;
    250     for (var x of iterable) break outer;
    251   }
    252   return n;
    253 }
    254 assertEquals(1, labelled_break(integers_until(10)));
    255 
    256 // Test continue/break in catch.
    257 function catch_control(iterable, k) {
    258   var n = 0;
    259   for (var x of iterable) {
    260     try {
    261       return k(x);
    262     } catch (e) {
    263       if (e == "continue") continue;
    264       else if (e == "break") break;
    265       else throw e;
    266     }
    267   } while (false);
    268   return false;
    269 }
    270 assertEquals(false,
    271              catch_control(integers_until(10),
    272                            function() { throw "break" }));
    273 assertEquals(false,
    274              catch_control(integers_until(10),
    275                            function() { throw "continue" }));
    276 assertEquals(5,
    277              catch_control(integers_until(10),
    278                            function(x) {
    279                              if (x == 5) return x;
    280                              throw "continue";
    281                            }));
    282 
    283 // Test continue/break in try.
    284 function try_control(iterable, k) {
    285   var n = 0;
    286   for (var x of iterable) {
    287     try {
    288       var e = k(x);
    289       if (e == "continue") continue;
    290       else if (e == "break") break;
    291       return e;
    292     } catch (e) {
    293       throw e;
    294     }
    295   } while (false);
    296   return false;
    297 }
    298 assertEquals(false,
    299              try_control(integers_until(10),
    300                          function() { return "break" }));
    301 assertEquals(false,
    302              try_control(integers_until(10),
    303                          function() { return "continue" }));
    304 assertEquals(5,
    305              try_control(integers_until(10),
    306                          function(x) { return (x == 5) ? x : "continue" }));
    307 
    308 // Proxy results, with getters.
    309 function transparent_proxy(x) {
    310   return Proxy.create({
    311     get: function(receiver, name) { return x[name]; }
    312   });
    313 }
    314 assertEquals([1, 2],
    315              fold(append, [],
    316                   results([one_time_getter({ value: 1 }, 'done', false),
    317                            one_time_getter({ done: false }, 'value', 2),
    318                            { value: 37, done: true },
    319                            never_getter(never_getter({}, 'done'), 'value')]
    320                           .map(transparent_proxy))));
    321 
    322 // Proxy iterators.
    323 function poison_proxy_after(iterable, n) {
    324   var iterator = iterable[Symbol.iterator]();
    325   return wrap_iterator(Proxy.create({
    326     get: function(receiver, name) {
    327       if (name == 'next' && n-- < 0) throw "unreachable";
    328       return iterator[name];
    329     },
    330     // Needed for integers_until(10)'s this.n++.
    331     set: function(receiver, name, val) {
    332       return iterator[name] = val;
    333     }
    334   }));
    335 }
    336 assertEquals(45, fold(sum, 0, poison_proxy_after(integers_until(10), 10)));
    337