"use strict";
const isNode = typeof process !== 'undefined' && !!process.versions?.node;
function assert(condition, message) {
if (!condition) {
throw new Error(message || "Assertion failed");
}
}
{
console.log("==== Indirect eval of empty for statement ====");
assert((0,eval)("for(false;false;false);") === undefined, "Indirect eval of empty for statement did not return undefined");
}
if (!isNode) {
console.log("==== Indirect eval of various statements ====");
var count_x = 0;
(0,eval)('var static; count_x += 1;');
assert(count_x === 1, "First eval did not increment count_x to 1");
(0,eval)('with ({}) {} count_x += 1;');
assert(count_x === 2, "Second eval did not increment count_x to 2");
(0,eval)('unresolvable_x = null; count_x += 1;');
assert(count_x === 3, "Third eval did not increment count_x to 3");
}
{
console.log("==== Indirect eval with conflicting function declarations ====");
try {
(0,eval)("function shouldNotBeDefined1() {} function NaN() {} function shouldNotBeDefined2() {}");
} catch (e) {
if (!(e instanceof TypeError)) {
throw e;
}
}
assert(Object.getOwnPropertyDescriptor(this, "shouldNotBeDefined1") === undefined, "declaration preceding");
assert(Object.getOwnPropertyDescriptor(this, "shouldNotBeDefined2") === undefined, "declaration following");
}
{
console.log("==== Indirect eval with conflicting function declarations and var statements ====");
try {
(0,eval)("var varShouldNotBeDefined1; function NaN() {} var varShouldNotBeDefined2;");
} catch (e) {
if (!(e instanceof TypeError)) {
throw e;
}
}
assert(Object.getOwnPropertyDescriptor(this, "varShouldNotBeDefined1") === undefined, "declaration preceding");
assert(Object.getOwnPropertyDescriptor(this, "varShouldNotBeDefined2") === undefined, "declaration following");
}
{
console.log("==== Indirect eval of empty if statement ====");
assert((0,eval)("if (false) ;") === undefined, "Indirect eval of empty if statement did not return undefined");
}
{
console.log("==== Indirect eval of invalid continue statement ====");
try {
(0,eval)("continue;");
throw new Error("Expected SyntaxError was not thrown");
} catch (e) {
if (!(e instanceof SyntaxError)) {
throw e;
}
}
try {
for (var i = 0; i <= 1; i++) {
(0,eval)("continue;");
throw new Error("First iteration should not complete");
}
throw new Error("Iteration should not complete");
} catch (e) {
if (!(e instanceof SyntaxError)) {
throw e;
}
}
}
{
console.log("==== Indirect eval of invalid break statement ====");
try {
(0,eval)("break;");
throw new Error("Expected SyntaxError was not thrown");
} catch (e) {
if (!(e instanceof SyntaxError)) {
throw e;
}
}
try {
for (var i = 0; i <= 1; i++) {
(0,eval)("break;");
throw new Error("First iteration should not complete");
}
throw new Error("Iteration should not complete");
} catch (e) {
if (!(e instanceof SyntaxError)) {
throw e;
}
}
}
{
console.log("==== Indirect eval of strict mode function declaration ====");
var typeofInside;
(function() {
(0,eval)("'use strict'; function fun(){}");
typeofInside = typeof fun;
}());
assert(typeofInside === "undefined", "typeofInside should be undefined");
assert(typeof fun === "undefined", "fun should be undefined");
}
{
console.log("==== Indirect eval defining non-definable global function throws TypeError ====");
try {
(0,eval)("function NaN() {}");
throw new Error("Expected TypeError was not thrown");
} catch (e) {
if (!(e instanceof TypeError)) {
throw e;
}
}
}
{
console.log("==== Indirect eval of var statement ====");
assert((0,eval)("var x = 1") === undefined, "Indirect eval of var statement did not return undefined");
}
{
console.log("==== Indirect eval of invalid syntax with line terminator ====");
try {
(0,eval)("x = 1; x\u000A++");
throw new Error("Expected SyntaxError was not thrown");
} catch (e) {
if (!(e instanceof SyntaxError)) {
throw e;
}
}
}
{
console.log("==== Indirect eval of strict mode var declaration does not leak to global scope ====");
if (!('foo_88' in this)) {
(1,eval)('"use strict"; var foo_88 = 88;');
if ('foo_88' in this) {
throw new Error("Strict indirect eval leaked a top level declaration");
}
}
}
{
console.log("==== Direct eval of strict mode var declaration does not leak to calling context ====");
var leakedVar_99 = 0;
function directEvalStrict() {
eval('"use strict"; var leakedVar_99 = 99;');
assert(leakedVar_99 === 0, "Direct eval in strict mode leaked a variable to the calling context");
}
directEvalStrict();
}
{
console.log("==== Indirect eval of strict mode function declaration does not leak to calling context ====");
function testcase_strict() {
eval("function fun(x){ return x }");
assert(typeof (fun) === "undefined", "Indirect eval in strict mode leaked function declaration to calling context");
}
testcase_strict();
}
{
console.log("==== Direct eval of strict mode function declaration does not leak to calling context ====");
function testcase_direct_eval_strict_func() {
eval("'use strict'; function _10_4_2_1_4_fun(){}");
assert(typeof _10_4_2_1_4_fun === "undefined", "Strict indirect eval leaked function declaration to calling context");
}
testcase_direct_eval_strict_func();
}
{
console.log("==== Direct eval of invalid syntax with line terminator ====");
var x_plus_plus;
function tests() {
eval("x_plus_plus = 1; x_plus_plus\u000A++");
}
try {
tests();
} catch (e) {
if (!(e instanceof SyntaxError)) {
throw new Error("Expected no SyntaxError to be thrown");
}
}
}
{
console.log("==== const ForDeclaration: creates a fresh binding per iteration ====");
let s = 0;
let f = [undefined, undefined, undefined];
for (const x of [1, 2, 3]) {
s += x;
f[x-1] = function() { return x; }
}
assert(s === 6, "The value of `s` is `6`");
assert(f[0]() === 1, "`f[0]()` returns `1`");
assert(f[1]() === 2, "`f[1]()` returns `2`");
assert(f[2]() === 3, "`f[2]()` returns `3`");
}
{
console.log("==== leading `async` token in for-of LHS ====");
var async = { x: 0 };
for (async.x of [1]) ;
assert(async.x === 1, "The value of `async.x` is `1`");
}
{
console.log("==== Completion value when head has a declaration and no iteration occurs ====");
assert(eval('1; for (var a of []) { }') === undefined, "Completion value of first eval is not undefined");
assert((0,eval)('2; for (var b of []) { 3; }') === undefined, "Completion value of second eval is not undefined");
}
{
console.log("==== Completion value when head has a declaration and iteration is cancelled ====");
assert(eval('1; for (var a of [0]) { break; }') === undefined, "Completion value of first eval is not undefined");
assert(eval('2; for (var b of [0]) { 3; break; }') === 3, "Completion value of second eval is not 3");
assert(
eval('4; outer: do { for (var a of [0]) { continue outer; } } while (false)') === undefined,
"Completion value of third eval is not undefined"
);
assert(
eval('5; outer: do { for (var b of [0]) { 6; continue outer; } } while (false)') === 6,
"Completion value of fourth eval is not 6"
);
}
{
console.log("==== Extra sanity check tests ====");
assert(eval("1; L: { 2; break L; }") === 2, "Label block break");
assert(eval("switch(1){ case 1: 5; break; }") === 5, "Switch break");
assert(eval("L: try { 1; break L; } finally {}") === 1, "Try break with finally normal");
assert(eval("L: try { 1; break L; } finally { 2; }") === 1, "Try break with finally normal (value check)");
assert(eval("outer: try { 1; break outer; } finally { 2; break outer; }") === 2, "Finally break overrides");
}
{
console.log("==== ForIn/Of: Bound names of ForDeclaration are in TDZ (for-of) ====");
try {
let x = 1;
for (const x of [x]) {}
throw new Error("Expected ReferenceError was not thrown");
} catch (e) {
if (!(e instanceof ReferenceError)) {
throw e;
}
}
}
{
console.log("==== Completion value when head has a declaration and iteration occurs ====");
assert(eval('1; for (var a of [0]) { }') === undefined, "Completion value of first eval is not undefined");
assert(eval('2; for (var b of [0]) { 3; }') === 3, "Completion value of second eval is not 3");
}
{
console.log("==== Completion value when head has no declaration and iteration is cancelled ====");
assert(eval('var a; 1; for (a of [0]) { break; }') === undefined, "Completion value of first eval is not undefined");
assert(eval('var b; 2; for (b of [0]) { 3; break; }') === 3, "Completion value of second eval is not 3");
assert(
eval('var a; 4; outer: do { for (a of [0]) { continue outer; } } while (false)') === undefined,
"Completion value of third eval is not undefined"
);
assert(
eval('var b; 5; outer: do { for (b of [0]) { 6; continue outer; } } while (false)') === 6,
"Completion value of fourth eval is not 6"
);
}
{
console.log("==== Completion value when head has no declaration and iteration occurs ====");
assert(eval('var a; 1; for (a of [0]) { }') === undefined, "Completion value of first eval is not undefined");
assert(eval('var b; 2; for (b of [0]) { 3; }') === 3, "Completion value of second eval is not 3");
}
{
console.log("==== ForIn/Of: Bound names of ForDeclaration are in TDZ (for-of) ====");
try {
let x = 1;
for (let x of [x]) {}
throw new Error("Expected ReferenceError was not thrown");
} catch (e) {
if (!(e instanceof ReferenceError)) {
throw e;
}
}
}
{
console.log("==== Error in Array entry access during traversal using for..of ====");
var array = [];
Object.defineProperty(array, '0', {
get: function() { throw new Error('myerr'); }
});
assert(array.length === 1, "Array length is 1");
var iterationCount = 0;
try {
for (var value of array) {
iterationCount += 1;
throw new Error("Loop body should not be executed");
}
throw new Error("Loop should not complete");
} catch (e) {
assert(e instanceof Error, "Caught exception is an Error");
assert(e.message === 'myerr', "Caught error message is 'myerr'");
}
assert(iterationCount === 0, "The loop body is not evaluated");
}
{
console.log("==== Completion value when head has a declaration and iteration is skipped (for-in) ====");
assert(eval('1; for (var a in undefined) { }') === undefined, "Completion value of first eval is not undefined");
assert(eval('2; for (var b in undefined) { 3; }') === undefined, "Completion value of second eval is not undefined");
assert(eval('4; for (var c in null) { }') === undefined, "Completion value of third eval is not undefined");
assert(eval('5; for (var d in null) { 6; }') === undefined, "Completion value of fourth eval is not undefined");
}
{
console.log("==== ForIn: The head's declaration may contain duplicate entries ====");
var iterCount = 0;
for (var [x, x] in { ab: null }) {
assert(x === 'b', "The value of `x` is `'b'`");
iterCount += 1;
}
assert(iterCount === 1, "The value of `iterCount` is `1`");
}
{
console.log("==== Function constructor assigning to 'eval' inside a strict-mode script ====");
var f = Function('eval = 42;');
f();
assert(eval === 42, "eval should be assigned the value 42");
}
console.log("All tests passed.");