Home | History | Annotate | Download | only in harmony
      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 --harmony-generators
     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 integers_until(max) {
     44   function next() {
     45     var ret = { value: this.n, done: this.n == max };
     46     this.n++;
     47     return ret;
     48   }
     49   return { next: next, n: 0 }
     50 }
     51 
     52 function results(results) {
     53   var i = 0;
     54   function next() {
     55     return results[i++];
     56   }
     57   return { next: next }
     58 }
     59 
     60 function* integers_from(n) {
     61   while (1) yield n++;
     62 }
     63 
     64 // A destructive append.
     65 function append(x, tail) {
     66   tail[tail.length] = x;
     67   return tail;
     68 }
     69 
     70 function sum(x, tail) {
     71   return x + tail;
     72 }
     73 
     74 function fold(cons, seed, iter) {
     75   for (var x of iter) {
     76     seed = cons(x, seed);
     77   }
     78   return seed;
     79 }
     80 
     81 function* take(iter, n) {
     82   if (n == 0) return;
     83   for (let x of iter) {
     84     yield x;
     85     if (--n == 0) break;
     86   }
     87 }
     88 
     89 function nth(iter, n) {
     90   for (let x of iter) {
     91     if (n-- == 0) return x;
     92   }
     93   throw "unreachable";
     94 }
     95 
     96 function* skip_every(iter, n) {
     97   var i = 0;
     98   for (let x of iter) {
     99     if (++i % n == 0) continue;
    100     yield x;
    101   }
    102 }
    103 
    104 function* iter_map(iter, f) {
    105   for (var x of iter) {
    106     yield f(x);
    107   }
    108 }
    109 function nested_fold(cons, seed, iter) {
    110   var visited = []
    111   for (let x of iter) {
    112     for (let y of x) {
    113       seed = cons(y, seed);
    114     }
    115   }
    116   return seed;
    117 }
    118 
    119 function* unreachable(iter) {
    120   for (let x of iter) {
    121     throw "not reached";
    122   }
    123 }
    124 
    125 function one_time_getter(o, prop, val) {
    126   function set_never() { throw "unreachable"; }
    127   var gotten = false;
    128   function get_once() {
    129     if (gotten) throw "got twice";
    130     gotten = true;
    131     return val;
    132   }
    133   Object.defineProperty(o, prop, {get: get_once, set: set_never})
    134   return o;
    135 }
    136 
    137 function never_getter(o, prop) {
    138   function never() { throw "unreachable"; }
    139   Object.defineProperty(o, prop, {get: never, set: never})
    140   return o;
    141 }
    142 
    143 function remove_next_after(iter, n) {
    144   function next() {
    145     if (n-- == 0) delete this.next;
    146     return iter.next();
    147   }
    148   return { next: next }
    149 }
    150 
    151 function poison_next_after(iter, n) {
    152   function next() {
    153     return iter.next();
    154   }
    155   function next_getter() {
    156     if (n-- < 0)
    157       throw "poisoned";
    158     return next;
    159   }
    160   var o = {};
    161   Object.defineProperty(o, 'next', { get: next_getter });
    162   return o;
    163 }
    164 
    165 // Now, the tests.
    166 
    167 // Non-generator iterators.
    168 assertEquals(45, fold(sum, 0, integers_until(10)));
    169 // Generator iterators.
    170 assertEquals([1, 2, 3], fold(append, [], values(1, 2, 3)));
    171 // Break.
    172 assertEquals(45, fold(sum, 0, take(integers_from(0), 10)));
    173 // Continue.
    174 assertEquals(90, fold(sum, 0, take(skip_every(integers_from(0), 2), 10)));
    175 // Return.
    176 assertEquals(10, nth(integers_from(0), 10));
    177 // Nested for-of.
    178 assertEquals([0, 0, 1, 0, 1, 2, 0, 1, 2, 3],
    179              nested_fold(append,
    180                          [],
    181                          iter_map(integers_until(5), integers_until)));
    182 // Result objects with sparse fields.
    183 assertEquals([undefined, 1, 2, 3],
    184              fold(append, [],
    185                   results([{ done: false },
    186                            { value: 1, done: false },
    187                            // A missing "done" is the same as undefined, which
    188                            // is false.
    189                            { value: 2 },
    190                            // Not done.
    191                            { value: 3, done: 0 },
    192                            // Done.
    193                            { value: 4, done: 42 }])));
    194 // Results that are not objects.
    195 assertEquals([undefined, undefined, undefined],
    196              fold(append, [],
    197                   results([10, "foo", /qux/, { value: 37, done: true }])));
    198 // Getters (shudder).
    199 assertEquals([1, 2],
    200              fold(append, [],
    201                   results([one_time_getter({ value: 1 }, 'done', false),
    202                            one_time_getter({ done: false }, 'value', 2),
    203                            { value: 37, done: true },
    204                            never_getter(never_getter({}, 'done'), 'value')])));
    205 
    206 // Null and undefined do not cause an error.
    207 assertEquals(0, fold(sum, 0, unreachable(null)));
    208 assertEquals(0, fold(sum, 0, unreachable(undefined)));
    209 
    210 // Other non-iterators do cause an error.
    211 assertThrows('fold(sum, 0, unreachable({}))', TypeError);
    212 assertThrows('fold(sum, 0, unreachable("foo"))', TypeError);
    213 assertThrows('fold(sum, 0, unreachable(37))', TypeError);
    214 
    215 // "next" is looked up each time.
    216 assertThrows('fold(sum, 0, remove_next_after(integers_until(10), 5))',
    217              TypeError);
    218 // It is not called at any other time.
    219 assertEquals(45,
    220              fold(sum, 0, remove_next_after(integers_until(10), 10)));
    221 // It is not looked up too many times.
    222 assertEquals(45,
    223              fold(sum, 0, poison_next_after(integers_until(10), 10)));
    224 
    225 function labelled_continue(iter) {
    226   var n = 0;
    227 outer:
    228   while (true) {
    229     n++;
    230     for (var x of iter) continue outer;
    231     break;
    232   }
    233   return n;
    234 }
    235 assertEquals(11, labelled_continue(integers_until(10)));
    236 
    237 function labelled_break(iter) {
    238   var n = 0;
    239 outer:
    240   while (true) {
    241     n++;
    242     for (var x of iter) break outer;
    243   }
    244   return n;
    245 }
    246 assertEquals(1, labelled_break(integers_until(10)));
    247 
    248 // Test continue/break in catch.
    249 function catch_control(iter, k) {
    250   var n = 0;
    251   for (var x of iter) {
    252     try {
    253       return k(x);
    254     } catch (e) {
    255       if (e == "continue") continue;
    256       else if (e == "break") break;
    257       else throw e;
    258     }
    259   } while (false);
    260   return false;
    261 }
    262 assertEquals(false,
    263              catch_control(integers_until(10),
    264                            function() { throw "break" }));
    265 assertEquals(false,
    266              catch_control(integers_until(10),
    267                            function() { throw "continue" }));
    268 assertEquals(5,
    269              catch_control(integers_until(10),
    270                            function(x) {
    271                              if (x == 5) return x;
    272                              throw "continue";
    273                            }));
    274 
    275 // Test continue/break in try.
    276 function try_control(iter, k) {
    277   var n = 0;
    278   for (var x of iter) {
    279     try {
    280       var e = k(x);
    281       if (e == "continue") continue;
    282       else if (e == "break") break;
    283       return e;
    284     } catch (e) {
    285       throw e;
    286     }
    287   } while (false);
    288   return false;
    289 }
    290 assertEquals(false,
    291              try_control(integers_until(10),
    292                          function() { return "break" }));
    293 assertEquals(false,
    294              try_control(integers_until(10),
    295                          function() { return "continue" }));
    296 assertEquals(5,
    297              try_control(integers_until(10),
    298                          function(x) { return (x == 5) ? x : "continue" }));
    299 
    300 // Proxy results, with getters.
    301 function transparent_proxy(x) {
    302   return Proxy.create({
    303     get: function(receiver, name) { return x[name]; }
    304   });
    305 }
    306 assertEquals([1, 2],
    307              fold(append, [],
    308                   results([one_time_getter({ value: 1 }, 'done', false),
    309                            one_time_getter({ done: false }, 'value', 2),
    310                            { value: 37, done: true },
    311                            never_getter(never_getter({}, 'done'), 'value')]
    312                           .map(transparent_proxy))));
    313 
    314 // Proxy iterators.
    315 function poison_proxy_after(x, n) {
    316   return Proxy.create({
    317     get: function(receiver, name) {
    318       if (name == 'next' && n-- < 0) throw "unreachable";
    319       return x[name];
    320     },
    321     // Needed for integers_until(10)'s this.n++.
    322     set: function(receiver, name, val) {
    323       return x[name] = val;
    324     }
    325   });
    326 }
    327 assertEquals(45, fold(sum, 0, poison_proxy_after(integers_until(10), 10)));
    328