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