Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 28 | // Flags: --harmony-proxies |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 203 | assertThrows(function() { |
| 204 | assertEquals([undefined, undefined, undefined], |
| 205 | fold(append, [], |
| 206 | results([10, "foo", /qux/, { value: 37, done: true }]))); |
| 207 | }, TypeError); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 208 | // Getters (shudder). |
| 209 | assertEquals([1, 2], |
| 210 | fold(append, [], |
| 211 | results([one_time_getter({ value: 1 }, 'done', false), |
| 212 | one_time_getter({ done: false }, 'value', 2), |
| 213 | { value: 37, done: true }, |
| 214 | never_getter(never_getter({}, 'done'), 'value')]))); |
| 215 | |
| 216 | // Unlike the case with for-in, null and undefined cause an error. |
| 217 | assertThrows('fold(sum, 0, unreachable(null))', TypeError); |
| 218 | assertThrows('fold(sum, 0, unreachable(undefined))', TypeError); |
| 219 | |
| 220 | // Other non-iterators do cause an error. |
| 221 | assertThrows('fold(sum, 0, unreachable({}))', TypeError); |
| 222 | assertThrows('fold(sum, 0, unreachable(false))', TypeError); |
| 223 | assertThrows('fold(sum, 0, unreachable(37))', TypeError); |
| 224 | |
| 225 | // "next" is looked up each time. |
| 226 | assertThrows('fold(sum, 0, remove_next_after(integers_until(10), 5))', |
| 227 | TypeError); |
| 228 | // It is not called at any other time. |
| 229 | assertEquals(45, |
| 230 | fold(sum, 0, remove_next_after(integers_until(10), 10))); |
| 231 | // It is not looked up too many times. |
| 232 | assertEquals(45, |
| 233 | fold(sum, 0, poison_next_after(integers_until(10), 10))); |
| 234 | |
| 235 | function labelled_continue(iterable) { |
| 236 | var n = 0; |
| 237 | outer: |
| 238 | while (true) { |
| 239 | n++; |
| 240 | for (var x of iterable) continue outer; |
| 241 | break; |
| 242 | } |
| 243 | return n; |
| 244 | } |
| 245 | assertEquals(11, labelled_continue(integers_until(10))); |
| 246 | |
| 247 | function labelled_break(iterable) { |
| 248 | var n = 0; |
| 249 | outer: |
| 250 | while (true) { |
| 251 | n++; |
| 252 | for (var x of iterable) break outer; |
| 253 | } |
| 254 | return n; |
| 255 | } |
| 256 | assertEquals(1, labelled_break(integers_until(10))); |
| 257 | |
| 258 | // Test continue/break in catch. |
| 259 | function catch_control(iterable, k) { |
| 260 | var n = 0; |
| 261 | for (var x of iterable) { |
| 262 | try { |
| 263 | return k(x); |
| 264 | } catch (e) { |
| 265 | if (e == "continue") continue; |
| 266 | else if (e == "break") break; |
| 267 | else throw e; |
| 268 | } |
| 269 | } while (false); |
| 270 | return false; |
| 271 | } |
| 272 | assertEquals(false, |
| 273 | catch_control(integers_until(10), |
| 274 | function() { throw "break" })); |
| 275 | assertEquals(false, |
| 276 | catch_control(integers_until(10), |
| 277 | function() { throw "continue" })); |
| 278 | assertEquals(5, |
| 279 | catch_control(integers_until(10), |
| 280 | function(x) { |
| 281 | if (x == 5) return x; |
| 282 | throw "continue"; |
| 283 | })); |
| 284 | |
| 285 | // Test continue/break in try. |
| 286 | function try_control(iterable, k) { |
| 287 | var n = 0; |
| 288 | for (var x of iterable) { |
| 289 | try { |
| 290 | var e = k(x); |
| 291 | if (e == "continue") continue; |
| 292 | else if (e == "break") break; |
| 293 | return e; |
| 294 | } catch (e) { |
| 295 | throw e; |
| 296 | } |
| 297 | } while (false); |
| 298 | return false; |
| 299 | } |
| 300 | assertEquals(false, |
| 301 | try_control(integers_until(10), |
| 302 | function() { return "break" })); |
| 303 | assertEquals(false, |
| 304 | try_control(integers_until(10), |
| 305 | function() { return "continue" })); |
| 306 | assertEquals(5, |
| 307 | try_control(integers_until(10), |
| 308 | function(x) { return (x == 5) ? x : "continue" })); |
| 309 | |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 310 | // TODO(neis,cbruni): Enable once the corresponding traps work again. |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 311 | // Proxy results, with getters. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 312 | // function transparent_proxy(x) { |
| 313 | // return new Proxy({}, { |
| 314 | // get: function(receiver, name) { return x[name]; } |
| 315 | // }); |
| 316 | // } |
| 317 | // assertEquals([1, 2], |
| 318 | // fold(append, [], |
| 319 | // results([one_time_getter({ value: 1 }, 'done', false), |
| 320 | // one_time_getter({ done: false }, 'value', 2), |
| 321 | // { value: 37, done: true }, |
| 322 | // never_getter(never_getter({}, 'done'), 'value')] |
| 323 | // .map(transparent_proxy)))); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 324 | |
| 325 | // Proxy iterators. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 326 | // function poison_proxy_after(iterable, n) { |
| 327 | // var iterator = iterable[Symbol.iterator](); |
| 328 | // return wrap_iterator(new Proxy({}, { |
| 329 | // get: function(receiver, name) { |
| 330 | // if (name == 'next' && n-- < 0) throw "unreachable"; |
| 331 | // return iterator[name]; |
| 332 | // }, |
| 333 | // // Needed for integers_until(10)'s this.n++. |
| 334 | // set: function(receiver, name, val) { |
| 335 | // return iterator[name] = val; |
| 336 | // } |
| 337 | // })); |
| 338 | // } |
| 339 | // assertEquals(45, fold(sum, 0, poison_proxy_after(integers_until(10), 10))); |
| 340 | |
| 341 | |
| 342 | function test_iterator_result_object_non_object(value, descr) { |
| 343 | var arr = []; |
| 344 | var ex; |
| 345 | var message = 'Iterator result ' + (descr || value) + ' is not an object'; |
| 346 | try { |
| 347 | fold(append, arr, |
| 348 | results([{value: 1}, {}, value, {value: 2}, {done: true}])); |
| 349 | } catch (e) { |
| 350 | ex = e; |
| 351 | } |
| 352 | assertInstanceof(ex, TypeError); |
| 353 | assertEquals(message, ex.message); |
| 354 | assertArrayEquals([1, undefined], arr); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 355 | } |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame^] | 356 | test_iterator_result_object_non_object(null); |
| 357 | test_iterator_result_object_non_object(undefined); |
| 358 | test_iterator_result_object_non_object(42); |
| 359 | test_iterator_result_object_non_object('abc'); |
| 360 | test_iterator_result_object_non_object(false); |
| 361 | test_iterator_result_object_non_object(Symbol('x'), 'Symbol(x)'); |