Index: test/mjsunit/harmony/do-expressions.js |
diff --git a/test/mjsunit/harmony/do-expressions.js b/test/mjsunit/harmony/do-expressions.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a2c014cbc6f523e7bbba07a2bc5323fa548cd8a7 |
--- /dev/null |
+++ b/test/mjsunit/harmony/do-expressions.js |
@@ -0,0 +1,273 @@ |
+// 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 |
+ |
+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) 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(); |
+ |
+ |
+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(); |