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