//------------------------------------------------------------------------------------------------------- | |
// Copyright (C) Microsoft. All rights reserved. | |
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. | |
//------------------------------------------------------------------------------------------------------- | |
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js"); | |
var tests = [ | |
{ | |
name: "Basic destructuring syntax as catch param", | |
body: function () { | |
assert.doesNotThrow(function () { eval("try {} catch({}) {}"); }, "Object destructuring pattern (empty) as a catch param is valid syntax"); | |
assert.doesNotThrow(function () { eval("try {} catch([]) {}"); }, "Array destructuring pattern (empty) as a catch param is valid syntax"); | |
assert.doesNotThrow(function () { eval("try {} catch({x:x}) {}"); }, "Object destructuring pattern as a catch param is valid syntax"); | |
assert.doesNotThrow(function () { eval("try {} catch([e]) {}"); }, "Object destructuring pattern as a catch param is valid syntax"); | |
assert.doesNotThrow(function () { eval("try {} catch({x}) {}"); }, "Object destructuring pattern (as short-hand) as a catch param is valid syntax"); | |
assert.doesNotThrow(function () { eval("function foo() {try {} catch({x, y:[y]}) {} }"); }, "Object destructuring pattern as a catch param inside a function is valid syntax"); | |
assert.doesNotThrow(function () { eval("function foo() {try {} catch([x, {y:[y]}]) {} }"); }, "Object destructuring pattern as a catch param inside a function is valid syntax"); | |
} | |
}, | |
{ | |
name: "Destructuring syntax as catch param - invalid syntax", | |
body: function () { | |
assert.throws(function () { eval("function foo() {try {} catch({,}) {} }"); }, SyntaxError, "Object destructuring pattern as a catch param with empty names is not valid syntax", "Expected identifier, string or number"); | |
assert.throws(function () { eval("function foo() {try {} catch(([])) {} }"); }, SyntaxError, "Object destructuring pattern as a catch param with empty names is not valid syntax", "Expected identifier"); | |
assert.throws(function () { eval("function foo() {try {} catch({x:abc+1}) {} }"); }, SyntaxError, "Object destructuring pattern as a catch param with operator is not valid syntax", "Unexpected operator in destructuring expression"); | |
assert.throws(function () { eval("function foo() {try {} catch([abc.d]) {} }"); }, SyntaxError, "Array destructuring pattern as a catch param with property reference is not valid syntax", "Syntax error"); | |
assert.throws(function () { eval("function foo() {try {} catch([x], [y]) {} }"); }, SyntaxError, "More than one patterns/identifiers as catch params is not valid syntax", "Expected ')'"); | |
assert.throws(function () { eval("function foo() {'use strict'; try {} catch([arguments]) {} }"); }, SyntaxError, "StrictMode - identifier under pattern named as 'arguments' is not valid syntax", "Invalid usage of 'arguments' in strict mode"); | |
assert.throws(function () { eval("function foo() {'use strict'; try {} catch([eval]) {} }"); }, SyntaxError, "StrictMode - identifier under pattern named as 'eval' is not valid syntax", "Invalid usage of 'eval' in strict mode"); | |
} | |
}, | |
{ | |
name: "Destructuring syntax as params - Initializer", | |
body: function () { | |
assert.doesNotThrow(function () { eval("function foo() {try {} catch({x:x = 20}) {} }"); }, "Catch param as object destructuring pattern with initializer is valid syntax"); | |
assert.doesNotThrow(function () { eval("function foo() {try {} catch([x = 20]) {} }"); }, "Catch param as array destructuring pattern with initializer is valid syntax"); | |
assert.doesNotThrow(function () { eval("function foo() {try {} catch({x1:x1 = 1, x2:x2 = 2, x3:x3 = 3}) {} }"); }, "Catch param as object destructuring pattern has three names with initializer is valid syntax"); | |
assert.doesNotThrow(function () { eval("function foo() {try {} catch([x1 = 1, x2 = 2, x3 = 3]) {} }"); }, "Catch param as array destructuring pattern has three names with initializer is valid syntax"); | |
assert.throws(function () { eval("function foo() {try {} catch({x:x} = {x:1}) {} }"); }, SyntaxError, "Catch param as pattern with default is not valid syntax", "Destructuring declarations cannot have an initializer"); | |
} | |
}, | |
{ | |
name: "Destructuring syntax as params - redeclarations", | |
body: function () { | |
assert.throws(function () { eval("function foo() {try {} catch({x:x, x:x}) {} }"); }, SyntaxError, "Catch param as object pattern has duplicate binding identifiers is not valid syntax", "Let/Const redeclaration"); | |
assert.throws(function () { eval("function foo() {try {} catch([x, x]) {} }"); }, SyntaxError, "Catch param as array pattern has duplicate binding identifiers is not valid syntax", "Let/Const redeclaration"); | |
assert.throws(function () { eval("function foo() {try {} catch({z1, x:{z:[z1]}}) {} }"); }, SyntaxError, "Catch param has nesting pattern has has matching is not valid syntax", "Let/Const redeclaration"); | |
assert.throws(function () { eval("function foo() {try {} catch([x]) { let x = 10;} }"); }, SyntaxError, "Catch param as a pattern and matching name with let/const variable in body is not valid syntax", "Let/Const redeclaration"); | |
assert.throws(function () { eval("function foo() {try {} catch([x]) { function x() {} } }"); }, SyntaxError, "Catch param as a pattern and matching name with function name in body is not valid syntax", "Let/Const redeclaration"); | |
assert.doesNotThrow(function () { eval("function foo() {try {} catch([x]) { var x = 10;} }"); }, "Catch param as a pattern and matching name with var declared name in body is valid syntax"); | |
(function () { | |
try { | |
} catch ({x}) { | |
var x = 1; | |
} | |
assert.areEqual(x, undefined, "Assignment inside the catch block should assign the value to the catch param not the body var"); | |
})(); | |
(function () { | |
let y = 1; | |
try { | |
throw { y : 10 }; | |
} catch ({y}) { | |
assert.areEqual(y, 10, "Catch block refers to the destructured param"); | |
} | |
assert.areEqual(y, 1, "Function body refers to the let variable"); | |
})(); | |
(function () { | |
let x = 1; | |
try { | |
throw [{ x : 10 }]; | |
} catch ([{ | |
x | |
}]) { | |
assert.areEqual(x, 10, "Catch block with nested destructured param"); | |
} | |
assert.areEqual(x, 1, "Let declaration in the function body is not affected by the destructured params"); | |
})(); | |
} | |
}, | |
{ | |
name: "Destructuring on catch param - basic functionality", | |
body: function () { | |
try { | |
throw [1]; | |
} | |
catch ([e1]) { | |
assert.areEqual(e1, 1, "Array pattern as a catch param matches with actual exception and initializes the identifier correctly"); | |
} | |
try { | |
throw {e2:2}; | |
} | |
catch({e2}) { | |
assert.areEqual(e2, 2, "Object pattern as a catch param matches with actual exception and initializes the identifier correctly"); | |
} | |
try { | |
throw [3, {e4:[4]}]; | |
} | |
catch([e3, {e4:[e4]}]) { | |
assert.areEqual(e3, 3, "First identifier in catch param as pattern is matched and initialized correctly"); | |
assert.areEqual(e4, 4, "Second identifier in catch param as pattern is matched and initialized correctly"); | |
} | |
} | |
}, | |
{ | |
name: "Destructuring on catch param - initializer", | |
body: function () { | |
try { | |
throw []; | |
} | |
catch ([e1 = 11]) { | |
assert.areEqual(e1, 11, "Array pattern as a catch param has initializer and initializes with initializer value"); | |
} | |
try { | |
throw {}; | |
} | |
catch({e2:e2 = 22}) { | |
assert.areEqual(e2, 22, "Object pattern as a catch param has initializer and initializes with initializer value"); | |
} | |
try { | |
throw [, {e4:[]}]; | |
} | |
catch([e3 = 11, {e4:[e4 = 22]} = {e4:[]}]) { | |
assert.areEqual(e3, 11, "First identifier in catch params as a pattern is initialized with initializer value"); | |
assert.areEqual(e4, 22, "Second identifier in catch params as a pattern is initialized with initializer value"); | |
} | |
} | |
}, | |
{ | |
name: "Destructuring on catch param - captures", | |
body : function () { | |
(function () { | |
try { | |
throw {x1:'x1', x2:'x2', x3:'x3'}; | |
} | |
catch ({x1, x2, x3}) { | |
(function () { | |
x1;x2;x3; | |
})(); | |
let m = x1+x2+x3; | |
assert.areEqual(m, 'x1x2x3', "Inner Function - capturing all identifiers from object pattern in inner function is working correctly"); | |
} | |
})(); | |
(function () { | |
try { | |
throw ['y1', 'y2', 'y3']; | |
} | |
catch ([x1, x2, x3]) { | |
(function () { | |
x1;x2;x3; | |
})(); | |
let m = x1+x2+x3; | |
assert.areEqual(m, 'y1y2y3', "Inner Function - capturing all identifiers from array pattern in inner function is working correctly"); | |
} | |
})(); | |
(function () { | |
try { | |
throw ['y1', 'y2', 'y3']; | |
} | |
catch ([x1, x2, x3]) { | |
(function () { | |
x2; | |
})(); | |
let m = x1+x2+x3; | |
assert.areEqual(m, 'y1y2y3', "Inner Function - capturing only one identifier from pattern in inner function is working correctly"); | |
} | |
})(); | |
(function () { | |
try { | |
throw ['y1', 'y2', 'y3']; | |
} | |
catch ([x1, x2, x3]) { | |
eval(''); | |
let m = x1+x2+x3; | |
assert.areEqual(m, 'y1y2y3', "Has eval - identifiers from catch params are initialized correctly"); | |
} | |
})(); | |
(function () { | |
try { | |
throw ['y1', 'y2', 'y3']; | |
} | |
catch ([x1, x2, x3]) { | |
(function () { | |
x1;x2;x3; | |
})(); | |
eval(''); | |
let m = x1+x2+x3; | |
assert.areEqual(m, 'y1y2y3', "Has eval and inner function - identifiers from catch params are initialized correctly"); | |
} | |
})(); | |
(function () { | |
try { | |
throw ['y1', 'y2', 'y3']; | |
} | |
catch ([x1, x2, x3]) { | |
(function () { | |
eval(''); | |
x1;x2;x3; | |
})(); | |
let m = x1+x2+x3; | |
assert.areEqual(m, 'y1y2y3', "Inner function has eval - identifiers from catch params are initialized correctly"); | |
} | |
})(); | |
} | |
}, | |
{ | |
name: "Function definitions in catch's parameter", | |
body: function () { | |
(function() { | |
try { | |
var c = 10; | |
throw ['inside']; | |
} catch ([x, y = function() { return c; }]) { | |
assert.areEqual(y(), 10, "Function should be able to capture symbols from try's body properly"); | |
assert.areEqual(x, 'inside', "Function should be able to capture symbols from try's body properly"); | |
} | |
})(); | |
(function() { | |
try { | |
throw []; | |
} catch ([x = 10, y = function() { return x; }]) { | |
assert.areEqual(y(), 10, "Function should be able to capture symbols from catch's param"); | |
} | |
})(); | |
(function() { | |
try { | |
throw []; | |
} catch ([x = 10, y = function() { return x; }]) { | |
eval(""); | |
assert.areEqual(y(), 10, "Function should be able to capture symbols from catch's param"); | |
} | |
})(); | |
(function() { | |
try { | |
throw {}; | |
} catch ({x = 10, y = function() { return x; }}) { | |
assert.areEqual(y(), 10, "Function should be able to capture symbols from catch's param"); | |
} | |
})(); | |
(function() { | |
try { | |
throw ['inside', {}]; | |
} catch ([x = 10, { y = function() { return x; } }]) { | |
eval(""); | |
assert.areEqual(y(), 'inside', "Function should be able to capture symbols from catch's param"); | |
} | |
})(); | |
(function() { | |
try { | |
throw ['inside', {}]; | |
} catch ([x, { y = () => arguments[0] }]) { | |
assert.areEqual(y(), 10, "Function should be able to capture the arguments symbol from the parent function"); | |
assert.areEqual(x, 'inside', "Function should be able to capture symbols from try's body properly"); | |
} | |
})(10); | |
(function(a = 1, b = () => a) { | |
try { | |
throw []; | |
} catch ([x = 10, y = function() { return b; }]) { | |
assert.areEqual(y()(), 1, "Function should be able to capture formals from a split scoped function"); | |
} | |
})(); | |
(function () { | |
var z = 100; | |
(function() { | |
try { | |
throw []; | |
} catch ([x = 10, y = () => x + z]) { | |
assert.areEqual(y(), 110, "Function should be able to capture symbols from outer functions"); | |
} | |
})(); | |
})(); | |
(function () { | |
var z = 100; | |
(function() { | |
try { | |
throw []; | |
} catch ([x = z = 10, y = () => x]) { | |
assert.areEqual(y(), 10, "Function should be able to capture symbols from outer functions"); | |
assert.areEqual(z, 10, "Variable from the outer function is updated during the param initialization"); | |
} | |
})(); | |
})(); | |
(function () { | |
var a = 100; | |
(function() { | |
var b = 200; | |
try { | |
throw []; | |
} catch ([x = () => y, y = 10, z = () => a]) { | |
c = () => x() + z() + b; | |
assert.areEqual(c(), 310, "Variable from all three levels are accessible"); | |
} | |
})(); | |
})(); | |
(function () { | |
var a = 100; | |
(function() { | |
var b = 200; | |
try { | |
throw []; | |
} catch ([x = () => y, y = 10, z = () => a]) { | |
c = () => x() + z() + b; | |
assert.areEqual(c(), 310, "Variable from all three levels are accessible with eval in catch's body"); | |
eval(""); | |
} | |
})(); | |
})(); | |
(function () { | |
try { | |
var c = 10; | |
throw [ ]; | |
} catch ([x = 1, y = function() { eval(""); return c + x; }]) { | |
assert.areEqual(y(), 11, "Function should be able to capture symbols from outer functions even with eval in the body"); | |
} | |
})(); | |
(function () { | |
try { | |
eval(""); | |
var c = 10; | |
throw [ ]; | |
} catch ([x = 1, y = function() { return c + x; }]) { | |
assert.areEqual(y(), 11, "Function should be able to capture symbols from outer functions even with eval in the try block"); | |
} | |
})(); | |
(function () { | |
try { | |
var c = 10; | |
throw {x : 'inside', y: []}; | |
} catch ({x, y: [y = function(a = 10, b = () => a) { return b; }]}) { | |
assert.areEqual(y()(), 10, "Function should be able to capture symbols from outer functions even if it has split scope"); | |
} | |
})(); | |
(function () { | |
var f = function foo(a) { | |
try { | |
if (!a) { | |
return foo(1); | |
} | |
var c = 10; | |
throw [ ]; | |
} catch ([y = function() { return c + a; }]) { | |
assert.areEqual(y(), 11, "Function should be able to capture symbols from outer functions when inside a named function expression"); | |
} | |
}; | |
f(); | |
})(); | |
} | |
} | |
]; | |
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" }); |