Home | History | Annotate | Download | only in harmony
      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 
      7 function returnValue(v) { return v; }
      8 function MyError() {}
      9 var global = this;
     10 
     11 function 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 }
    125 TestBasic();
    126 
    127 
    128 function 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 }
    154 TestDeoptimization1();
    155 
    156 
    157 function 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 }
    198 TestInParameterInitializers();
    199 
    200 
    201 function 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 }
    216 TestWithEval();
    217 
    218 
    219 function 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 }
    261 TestHoisting();
    262 
    263 
    264 // v8:4661
    265 
    266 function tryFinallySimple() { (do { try {} finally {} }); }
    267 tryFinallySimple();
    268 tryFinallySimple();
    269 tryFinallySimple();
    270 tryFinallySimple();
    271 
    272 var finallyRanCount = 0;
    273 function tryFinallyDoExpr() {
    274   return (do {
    275     try {
    276       throw "BOO";
    277     } catch (e) {
    278       "Caught: " + e + " (" + finallyRanCount + ")"
    279     } finally {
    280       ++finallyRanCount;
    281     }
    282   });
    283 }
    284 assertEquals("Caught: BOO (0)", tryFinallyDoExpr());
    285 assertEquals(1, finallyRanCount);
    286 assertEquals("Caught: BOO (1)", tryFinallyDoExpr());
    287 assertEquals(2, finallyRanCount);
    288 assertEquals("Caught: BOO (2)", tryFinallyDoExpr());
    289 assertEquals(3, finallyRanCount);
    290 assertEquals("Caught: BOO (3)", tryFinallyDoExpr());
    291 assertEquals(4, finallyRanCount);
    292 
    293 
    294 function 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 
    305 for (var i = 0; i < 64; ++i) TestOSR();
    306