Chromium Code Reviews| 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..1f393d93bbe3aa43fd00945de0d32253b0f32758 |
| --- /dev/null |
| +++ b/test/mjsunit/harmony/do-expressions.js |
| @@ -0,0 +1,244 @@ |
| +// 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 |
| + |
| +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"; |
| + ""; |
| + }; |
| + })()); |
| + |
| + // Breaking out |do-expression| |
| + assertEquals(3, (function() { |
| + for (var i = 0; i < 10; ++i) (do { if (i === 3) break; }); |
|
rossberg
2015/10/16 12:09:40
I'm mildly surprised that this one actually works
caitp (gmail)
2015/10/16 13:12:18
From local testing, it seems to be working fine --
|
| + return i; |
| + })()); |
| + |
| + // Continue in |do-expression| |
| + assertEquals([1, 3, 5, 7, 9], (function() { |
| + var values = []; |
| + for (var i = 0; i < 10; ++i) { |
| + (do { |
| + if ((i & 1) === 0) continue; |
|
rossberg
2015/10/16 12:09:40
Same here.
|
| + values.push(i); |
| + }); |
| + } |
| + return values; |
| + })()); |
| + |
| + 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() { |
|
rossberg
2015/10/16 12:09:40
Can you add a test with do-var inside a parameter
caitp (gmail)
2015/10/16 12:21:00
I don't expect it to work the way I'd think it wou
rossberg
2015/10/16 12:37:02
Sure, TODO is fine, just was curious in how far it
caitp (gmail)
2015/10/16 13:12:18
Yeah, you're right. Then this doesn't need a TODO
caitp (gmail)
2015/10/16 13:23:03
Is this actually right? Each parameter with an ini
rossberg
2015/10/16 13:28:28
Note the curly braces: this is a single parameter
|
| + (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); |
| + |
| + // 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(); |