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-do-expressions --harmony-sloppy-let --allow-natives-syntax 6// Flags: --harmony-default-parameters --harmony-destructuring-bind 7// Flags: --harmony-completion 8 9function returnValue(v) { return v; } 10function MyError() {} 11var global = this; 12 13function TestBasic() { 14 // Looping and lexical declarations 15 assertEquals(512, returnValue(do { 16 let n = 2; 17 for (let i = 0; i < 4; i++) n <<= 2; 18 })); 19 20 // Strings do the right thing 21 assertEquals("spooky halloween", returnValue(do { 22 "happy halloween".replace('happy', 'spooky'); 23 })); 24 25 // Do expressions with no completion produce an undefined value 26 assertEquals(undefined, returnValue(do {})); 27 assertEquals(undefined, returnValue(do { var x = 99; })); 28 assertEquals(undefined, returnValue(do { function f() {}; })); 29 assertEquals(undefined, returnValue(do { let z = 33; })); 30 31 // Propagation of exception 32 assertThrows(function() { 33 (do { 34 throw new MyError(); 35 "potatoes"; 36 }); 37 }, MyError); 38 39 assertThrows(function() { 40 return do { 41 throw new MyError(); 42 "potatoes"; 43 }; 44 }, MyError); 45 46 // Return value within do-block overrides `return |do-expression|` 47 assertEquals("inner-return", (function() { 48 return "outer-return" + do { 49 return "inner-return"; 50 ""; 51 }; 52 })()); 53 54 var count = 0, n = 1; 55 // Breaking out |do-expression| 56 assertEquals(3, (function() { 57 for (var i = 0; i < 10; ++i) (count += 2 * do { if (i === 3) break; ++n }); 58 return i; 59 })()); 60 // (2 * 2) + (2 * 3) + (2 * 4) 61 assertEquals(18, count); 62 63 // Continue in |do-expression| 64 count = 0, n = 1; 65 assertEquals([1, 3, 5, 7, 9], (function() { 66 var values = []; 67 for (var i = 0; i < 10; ++i) { 68 count += 2 * (do { 69 if ((i & 1) === 0) continue; 70 values.push(i); 71 ++n; 72 }) + 1; 73 } 74 // (2*2) + 1 + (2*3) + 1 + (2*4) + 1 + (2*5) + 1 + (2*6) + 1 75 return values; 76 })()); 77 assertEquals(count, 45); 78 79 assertThrows("(do { break; });", SyntaxError); 80 assertThrows("(do { continue; });", SyntaxError); 81 82 // Real-world use case for desugaring 83 var array = [1, 2, 3, 4, 5], iterable = [6, 7, 8,9]; 84 assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], do { 85 for (var element of iterable) array.push(element); 86 array; 87 }); 88 89 // Nested do-expressions 90 assertEquals(125, do { (do { (do { 5 * 5 * 5 }) }) }); 91 92 // Directives are not honoured 93 (do { 94 "use strict"; 95 foo = 80; 96 assertEquals(foo, 80); 97 }); 98 99 // Non-empty operand stack testing 100 var O = { 101 method1() { 102 let x = 256; 103 return x + do { 104 for (var i = 0; i < 4; ++i) x += i; 105 } + 17; 106 }, 107 method2() { 108 let x = 256; 109 this.reset(); 110 return x + do { 111 for (var i = 0; i < this.length(); ++i) x += this.index() * 2; 112 }; 113 }, 114 _index: 0, 115 index() { 116 return ++this._index; 117 }, 118 _length: 4, 119 length() { return this._length; }, 120 reset() { this._index = 0; } 121 }; 122 assertEquals(535, O["method" + do { 1 } + ""]()); 123 assertEquals(532, O["method" + do { ({ valueOf() { return "2"; } }); }]()); 124 assertEquals(532, O[ 125 do { let s = ""; for (let c of "method") s += c; } + "2"]()); 126} 127TestBasic(); 128 129 130function TestDeoptimization1() { 131 function f(v) { 132 return 88 + do { 133 v.a * v.b + v.c; 134 }; 135 } 136 137 var o1 = {}; 138 o1.a = 10; 139 o1.b = 5; 140 o1.c = 50; 141 142 var o2 = {}; 143 o2.c = 100; 144 o2.a = 10; 145 o2.b = 10; 146 147 assertEquals(188, f(o1)); 148 assertEquals(188, f(o1)); 149 %OptimizeFunctionOnNextCall(f); 150 assertEquals(188, f(o1)); 151 assertOptimized(f); 152 assertEquals(288, f(o2)); 153 assertUnoptimized(f); 154 assertEquals(288, f(o2)); 155} 156TestDeoptimization1(); 157 158 159function TestInParameterInitializers() { 160 var first_name = "George"; 161 var last_name = "Jetson"; 162 function fn1(name = do { first_name + " " + last_name }) { 163 return name; 164 } 165 assertEquals("George Jetson", fn1()); 166 167 var _items = [1, 2, 3, NaN, 4, 5]; 168 function fn2(items = do { 169 let items = []; 170 for (var el of _items) { 171 if (el !== el) { 172 items; 173 break; 174 } 175 items.push(el), items; 176 } 177 }) { 178 return items; 179 } 180 assertEquals([1, 2, 3], fn2()); 181 182 function thrower() { throw new MyError(); } 183 function fn3(exception = do { try { thrower(); } catch (e) { e } }) { 184 return exception; 185 } 186 assertDoesNotThrow(fn3); 187 assertInstanceof(fn3(), MyError); 188 189 function fn4(exception = do { throw new MyError() }) {} 190 function catcher(fn) { 191 try { 192 fn(); 193 assertUnreachable("fn() initializer should throw"); 194 } catch (e) { 195 assertInstanceof(e, MyError); 196 } 197 } 198 catcher(fn4); 199} 200TestInParameterInitializers(); 201 202 203function TestWithEval() { 204 (function sloppy1() { 205 assertEquals(do { eval("var x = 5"), x }, 5); 206 assertEquals(x, 5); 207 })(); 208 209 assertThrows(function strict1() { 210 "use strict"; 211 (do { eval("var x = 5"), x }, 5); 212 }, ReferenceError); 213 214 assertThrows(function strict2() { 215 (do { eval("'use strict'; var x = 5"), x }, 5); 216 }, ReferenceError); 217} 218TestWithEval(); 219 220 221function TestHoisting() { 222 (do { var a = 1; }); 223 assertEquals(a, 1); 224 assertEquals(global.a, undefined); 225 226 (do { 227 for (let it of [1, 2, 3, 4, 5]) { 228 var b = it; 229 } 230 }); 231 assertEquals(b, 5); 232 assertEquals(global.b, undefined); 233 234 { 235 let x = 1 236 237 // TODO(caitp): ensure VariableStatements in |do-expressions| in parameter 238 // initializers, are evaluated in the same VariableEnvironment as they would 239 // be for eval(). 240 // function f1(a = do { var x = 2 }, b = x) { return b } 241 // assertEquals(1, f1()) 242 243 // function f2(a = x, b = do { var x = 2 }) { return a } 244 // assertEquals(1, f2()) 245 246 function f3({a = do { var x = 2 }, b = x}) { return b } 247 assertEquals(2, f3({})) 248 249 function f4({a = x, b = do { var x = 2 }}) { return b } 250 assertEquals(undefined, f4({})) 251 252 function f5(a = do { var y = 0 }) {} 253 assertThrows(() => y, ReferenceError) 254 } 255 256 // TODO(caitp): Always block-scope function declarations in |do| expressions 257 //(do { 258 // assertEquals(true, inner_func()); 259 // function inner_func() { return true; } 260 //}); 261 //assertThrows(function() { return innerFunc(); }, ReferenceError); 262} 263TestHoisting(); 264 265 266// v8:4661 267 268function tryFinallySimple() { (do { try {} finally {} }); } 269tryFinallySimple(); 270tryFinallySimple(); 271tryFinallySimple(); 272tryFinallySimple(); 273 274var finallyRanCount = 0; 275function tryFinallyDoExpr() { 276 return (do { 277 try { 278 throw "BOO"; 279 } catch (e) { 280 "Caught: " + e + " (" + finallyRanCount + ")" 281 } finally { 282 ++finallyRanCount; 283 } 284 }); 285} 286assertEquals("Caught: BOO (0)", tryFinallyDoExpr()); 287assertEquals(1, finallyRanCount); 288assertEquals("Caught: BOO (1)", tryFinallyDoExpr()); 289assertEquals(2, finallyRanCount); 290assertEquals("Caught: BOO (2)", tryFinallyDoExpr()); 291assertEquals(3, finallyRanCount); 292assertEquals("Caught: BOO (3)", tryFinallyDoExpr()); 293assertEquals(4, finallyRanCount); 294 295 296function TestOSR() { 297 var numbers = do { 298 let nums = []; 299 for (let i = 0; i < 1000; ++i) { 300 let value = (Math.random() * 100) | 0; 301 nums.push(value === 0 ? 1 : value), nums; 302 } 303 }; 304 assertEquals(numbers.length, 1000); 305} 306 307for (var i = 0; i < 64; ++i) TestOSR(); 308