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