blob: b0822e1593b7ccdffe689f68067309aa00310451 [file] [log] [blame]
// 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();
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();