text: // Copyright 2015 the V8 project authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  // Flags: --harmony-do-expressions --harmony-sloppy-let --allow-natives-syntax
  // Flags: --harmony-default-parameters --harmony-destructuring-bind
  // Flags: --harmony-completion
  
  function returnValue(v) { return v; }
  function MyError() {}
  var global = this;
  
  function TestBasic() {
    // Looping and lexical declarations
    assertEquals(512, returnValue(do {
      let n = 2;
      for (let i = 0; i < 4; i++) n <<= 2;
    }));
  
    // Strings do the right thing
    assertEquals("spooky halloween", returnValue(do {
      "happy halloween".replace('happy', 'spooky');
    }));
  
    // Do expressions with no completion produce an undefined value
    assertEquals(undefined, returnValue(do {}));
    assertEquals(undefined, returnValue(do { var x = 99; }));
    assertEquals(undefined, returnValue(do { function f() {}; }));
    assertEquals(undefined, returnValue(do { let z = 33; }));
  
    // Propagation of exception
    assertThrows(function() {
      (do {
        throw new MyError();
        "potatoes";
      });
    }, MyError);
  
    assertThrows(function() {
      return do {
        throw new MyError();
        "potatoes";
      };
    }, MyError);
  
    // Return value within do-block overrides `return |do-expression|`
    assertEquals("inner-return", (function() {
      return "outer-return" + do {
        return "inner-return";
        "";
      };
    })());
  
    var count = 0, n = 1;
    // Breaking out |do-expression|
    assertEquals(3, (function() {
      for (var i = 0; i < 10; ++i) (count += 2 * do { if (i === 3) break; ++n });
      return i;
    })());
    // (2 * 2) + (2 * 3) + (2 * 4)
    assertEquals(18, count);
  
    // Continue in |do-expression|
    count = 0, n = 1;
    assertEquals([1, 3, 5, 7, 9], (function() {
      var values = [];
      for (var i = 0; i < 10; ++i) {
        count += 2 * (do {
          if ((i & 1) === 0) continue;
          values.push(i);
          ++n;
        }) + 1;
      }
      // (2*2) + 1 + (2*3) + 1 + (2*4) + 1 + (2*5) + 1 + (2*6) + 1
      return values;
    })());
    assertEquals(count, 45);
  
    assertThrows("(do { break; });", SyntaxError);
    assertThrows("(do { continue; });", SyntaxError);
  
    // Real-world use case for desugaring
    var array = [1, 2, 3, 4, 5], iterable = [6, 7, 8,9];
    assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], do {
      for (var element of iterable) array.push(element);
      array;
    });
  
    // Nested do-expressions
    assertEquals(125, do { (do { (do { 5 * 5 * 5 }) }) });
  
    // Directives are not honoured
    (do {
      "use strict";
      foo = 80;
      assertEquals(foo, 80);
    });
  
    // Non-empty operand stack testing
    var O = {
      method1() {
        let x = 256;
        return x + do {
          for (var i = 0; i < 4; ++i) x += i;
        } + 17;
      },
      method2() {
        let x = 256;
        this.reset();
        return x + do {
          for (var i = 0; i < this.length(); ++i) x += this.index() * 2;
        };
      },
      _index: 0,
      index() {
        return ++this._index;
      },
      _length: 4,
      length() { return this._length; },
      reset() { this._index = 0; }
    };
    assertEquals(535, O["method" + do { 1 } + ""]());
    assertEquals(532, O["method" + do { ({ valueOf() { return "2"; } }); }]());
    assertEquals(532, O[
        do { let s = ""; for (let c of "method") s += c; } + "2"]());
  }
  TestBasic();
  
  
  function TestDeoptimization1() {
    function f(v) {
      return 88 + do {
        v.a * v.b + v.c;
      };
    }
  
    var o1 = {};
    o1.a = 10;
    o1.b = 5;
    o1.c = 50;
  
    var o2 = {};
    o2.c = 100;
    o2.a = 10;
    o2.b = 10;
  
    assertEquals(188, f(o1));
    assertEquals(188, f(o1));
    %OptimizeFunctionOnNextCall(f);
    assertEquals(188, f(o1));
    assertOptimized(f);
    assertEquals(288, f(o2));
    assertUnoptimized(f);
    assertEquals(288, f(o2));
  }
  TestDeoptimization1();
  
  
  function TestInParameterInitializers() {
    var first_name = "George";
    var last_name = "Jetson";
    function fn1(name = do { first_name + " " + last_name }) {
      return name;
    }
    assertEquals("George Jetson", fn1());
  
    var _items = [1, 2, 3, NaN, 4, 5];
    function fn2(items = do {
      let items = [];
      for (var el of _items) {
        if (el !== el) {
          items;
          break;
        }
        items.push(el), items;
      }
    }) {
      return items;
    }
    assertEquals([1, 2, 3], fn2());
  
    function thrower() { throw new MyError(); }
    function fn3(exception = do {  try { thrower(); } catch (e) { e } }) {
      return exception;
    }
    assertDoesNotThrow(fn3);
    assertInstanceof(fn3(), MyError);
  
    function fn4(exception = do { throw new MyError() }) {}
    function catcher(fn) {
      try {
        fn();
        assertUnreachable("fn() initializer should throw");
      } catch (e) {
        assertInstanceof(e, MyError);
      }
    }
    catcher(fn4);
  }
  TestInParameterInitializers();
  
  
  function TestWithEval() {
    (function sloppy1() {
      assertEquals(do { eval("var x = 5"), x }, 5);
      assertEquals(x, 5);
    })();
  
    assertThrows(function strict1() {
      "use strict";
      (do { eval("var x = 5"), x }, 5);
    }, ReferenceError);
  
    assertThrows(function strict2() {
      (do { eval("'use strict'; var x = 5"), x }, 5);
    }, ReferenceError);
  }
  TestWithEval();
  
  
  function TestHoisting() {
    (do { var a = 1; });
    assertEquals(a, 1);
    assertEquals(global.a, undefined);
  
    (do {
      for (let it of [1, 2, 3, 4, 5]) {
        var b = it;
      }
    });
    assertEquals(b, 5);
    assertEquals(global.b, undefined);
  
    {
      let x = 1
  
      // TODO(caitp): ensure VariableStatements in |do-expressions| in parameter
      // initializers, are evaluated in the same VariableEnvironment as they would
      // be for eval().
      // function f1(a = do { var x = 2 }, b = x) { return b }
      // assertEquals(1, f1())
  
      // function f2(a = x, b = do { var x = 2 }) { return a }
      // assertEquals(1, f2())
  
      function f3({a = do { var x = 2 }, b = x}) { return b }
      assertEquals(2, f3({}))
  
      function f4({a = x, b = do { var x = 2 }}) { return b }
      assertEquals(undefined, f4({}))
  
      function f5(a = do { var y = 0 }) {}
      assertThrows(() => y, ReferenceError)
    }
  
    // TODO(caitp): Always block-scope function declarations in |do| expressions
    //(do {
    //  assertEquals(true, inner_func());
    //  function inner_func() { return true; }
    //});
    //assertThrows(function() { return innerFunc(); }, ReferenceError);
  }
  TestHoisting();
  
  
  // v8:4661
  
  function tryFinallySimple() { (do { try {} finally {} }); }
  tryFinallySimple();
  tryFinallySimple();
  tryFinallySimple();
  tryFinallySimple();
  
  var finallyRanCount = 0;
  function tryFinallyDoExpr() {
    return (do {
      try {
        throw "BOO";
      } catch (e) {
        "Caught: " + e + " (" + finallyRanCount + ")"
      } finally {
        ++finallyRanCount;
      }
    });
  }
  assertEquals("Caught: BOO (0)", tryFinallyDoExpr());
  assertEquals(1, finallyRanCount);
  assertEquals("Caught: BOO (1)", tryFinallyDoExpr());
  assertEquals(2, finallyRanCount);
  assertEquals("Caught: BOO (2)", tryFinallyDoExpr());
  assertEquals(3, finallyRanCount);
  assertEquals("Caught: BOO (3)", tryFinallyDoExpr());
  assertEquals(4, finallyRanCount);
  
  
  function TestOSR() {
    var numbers = do {
      let nums = [];
      for (let i = 0; i < 1000; ++i) {
        let value = (Math.random() * 100) | 0;
        nums.push(value === 0 ? 1 : value), nums;
      }
    };
    assertEquals(numbers.length, 1000);
  }
  
  for (var i = 0; i < 64; ++i) TestOSR();
  