blob: 9a0454182c3602e074253d7d4cb5bdfce0d2ef14 [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
// ES6 new.target tests
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
var tests = [
{
name: "Test new.target parsing path doesn't confuse 'new target'",
body: function() {
function target() { return { name: 'something' }; }
var t = new target; // implicitly 'new target()'
assert.areEqual('something', t.name, "new target() returned our new object instead of new.target");
}
},
{
name: "Test new.target in various block scopes'",
body: function() {
assert.doesNotThrow(function(){{new.target;}}, "new.target one level block do not throw in a function");
assert.doesNotThrow(function(){{{new.target;}}}, "new.target two level block do not throw in a function");
assert.doesNotThrow(function(){with({}) {new.target;}}, "new.target with scope body call does not throw");
assert.doesNotThrow(function() { function parent(x) { new x();}; function child(){ with(new.target) {toString();}}; parent(child); }, "new.target with scope parameter does not throw");
assert.doesNotThrow(function(){{if(true){new.target;}}}, "new.target condition block in nested block do not throw in a function");
assert.doesNotThrow(function(){try { throw Error;} catch(e){new.target;}}, "new.target catch block do not throw in a function");
assert.doesNotThrow(function(){ var a = b = c = 1; try {} catch([a,b,c]) { new.target;}}, "new.target in CatchParamPattern block do not throw in a function");
assert.doesNotThrow(function(){ var x = function() {new.target;}; x();}, "new.target in function expression do not throw in a function");
assert.doesNotThrow(function(){ var o = { "foo" : function () { new.target}}; o.foo();}, "new.target in named function expression do not throw in a function");
}
},
{
name: "Test new.target parsing path with badly-formed meta-property references",
body: function() {
assert.throws(function() { return new['target']; }, TypeError, "Meta-property new.target is not a real property lookup", "Object doesn't support this action");
assert.throws(function() { return eval('new.'); }, SyntaxError, "Something like 'new.' should fall out of the meta-property parser path", "Syntax error");
assert.throws(function() { return eval('new.target2'); }, SyntaxError, "No other keywords should produce meta-properties", "Syntax error");
assert.throws(function() { return eval('new.something'); }, SyntaxError, "No other keywords should produce meta-properties", "Syntax error");
assert.throws(function() { return eval('new.eval'); }, SyntaxError, "No other keywords should produce meta-properties", "Syntax error");
}
},
{
name: "There is now a well-known PID for 'target' - ensure it doesn't break",
body: function() {
var obj = { target: 'something' };
assert.areEqual('something', obj.target, "The name 'target' can be used as an identifier");
}
},
{
name: "new.target is not valid for assignment",
body: function() {
assert.throws(function() { eval("new.target = 'something';"); }, ReferenceError, "new.target cannot be a lhs in an assignment expression - this is an early reference error", "Invalid left-hand side in assignment");
assert.throws(function() { eval("((new.target)) = 'something';"); }, ReferenceError, "new.target cannot be a lhs in an assignment expression - this is an early reference error", "Invalid left-hand side in assignment");
}
},
{
name: "Simple base class gets new.target correctly",
body: function() {
var called = false;
class SimpleBaseClass {
constructor() {
assert.isTrue(new.target === SimpleBaseClass, "new.target === SimpleBaseClass");
called = true;
}
}
var myObj = new SimpleBaseClass();
assert.isTrue(called, "The constructor was called.");
}
},
{
name: "Simple derived and base class passes new.target correctly",
body: function() {
var called = false;
class BaseClassForB {
constructor() {
assert.isTrue(new.target === DerivedClassForB, "new.target === DerivedClassForB");
called = true;
}
}
class DerivedClassForB extends BaseClassForB {
constructor() {
assert.isTrue(new.target === DerivedClassForB, "new.target === DerivedClassForB");
super();
}
}
var myB = new DerivedClassForB();
assert.isTrue(called, "The super-chain was called.");
}
},
{
name: "Simple base class with arrow function using new.target correctly",
body: function() {
var called = false;
class SimpleBaseClass {
constructor() {
var arrow = () => {
assert.isTrue(new.target === SimpleBaseClass, "new.target === SimpleBaseClass");
called = true;
}
arrow();
}
}
var myObj = new SimpleBaseClass();
assert.isTrue(called, "The constructor was called.");
}
},
{
name: "new.target behavior in arrow function inside derived class",
body: function() {
let constructed = false;
class C {
constructor() {
let arrow = () => {
assert.isTrue(D === new.target, "Class constructor implicitly invoked via super call has new.target set to derived constructor (also in arrow)");
constructed = true;
};
arrow();
}
}
class D extends C {
constructor() {
let arrow = () => {
assert.isTrue(D === new.target, "Class constructor explicitly invoked via new keyword has new.target set to that constructor (also in arrow)");
};
arrow();
super();
}
}
let myD = new D();
assert.isTrue(constructed, "We actually ran the constructor code");
}
},
{
name: "new.target behavior in a normal function",
body: function() {
function foo() {
assert.isTrue(undefined === new.target, "Normal function call has new.target set to undefined in the function body");
return new.target;
}
assert.isTrue(undefined === foo(), "Normal function returning new.target returns undefined");
}
},
{
name: "new.target behavior in a normal function in a new expression",
body: function() {
function foo() {
assert.isTrue(foo === new.target, "Function called as new expression has new.target set to the function in the function body");
return new.target;
}
assert.isTrue(foo === new foo(), "Function called-as-constructor has new.target set to that function");
}
},
{
name: "new.target behavior in an arrow in a normal function",
body: function() {
function foo() {
let arrow = () => {
assert.isTrue(undefined === new.target, "Normal function call has new.target set to undefined in the function body");
return new.target;
};
return arrow();
}
assert.isTrue(undefined === foo(), "Normal function returning new.target returns undefined");
}
},
{
name: "new.target behaviour in an arrow in a normal function in a new expression",
body: function() {
function foo() {
let arrow = () => {
assert.isTrue(foo === new.target, "Function called as new expression has new.target set to the function in the function body");
return new.target;
};
return arrow();
}
assert.isTrue(foo === new foo(), "Function called-as-constructor has new.target set to that function");
}
},
{
name: "new.target captured from class constructor via arrow",
body: function() {
class base {
constructor() {
let arrow = () => {
assert.isTrue(derived === new.target, "Function called as new expression has new.target set to the function in the function body");
return new.target;
};
return arrow;
}
}
class derived extends base {
constructor() {
return super();
}
}
let arrow = new derived();
assert.isTrue(derived === arrow(), "Arrow capturing new.target returns correct value");
}
},
{
name: "new.target inline constructor case",
body: function() {
function foo()
{
return new.target;
}
function bar()
{
return new foo(); //foo will be inlined here
}
assert.isTrue(bar() == foo, "Function called as new expression has new.target set to the function in the function body when the constructor is inlined");
}
},
{
name: "new.target inline case",
body: function() {
function foo()
{
return new.target;
}
function bar()
{
return foo(); //foo will be inlined here
}
assert.isTrue(bar() == undefined, "Normal inlined function has new.target set to undefined in the function body");
}
},
{
name: "new.target generator case",
body: function() {
function *foo()
{
yield new.target;
}
assert.isTrue((foo()).next().value == undefined, "Generator function has new.target set to undefined in the function body");
}
},
{
name: "new.target inside eval() in function",
body: function() {
function func() {
var g = ()=>eval('new.target;');
return g();
}
assert.areEqual(undefined, func(), "plain function call");
assert.areEqual(undefined, eval("func()"), "function call inside eval");
assert.areEqual(undefined, eval("eval('func()')"), "function call inside nested evals");
assert.areEqual(undefined, (()=>func())(), "function call inside arrow function");
assert.areEqual(undefined, (()=>(()=>func())())(), "function call inside nested arrow functions");
assert.areEqual(undefined, eval("(()=>func())()"), "function call inside arrow function inside eval");
assert.areEqual(undefined, (()=>eval("func()"))(), "function call inside eval inside arrow function");
assert.areEqual(undefined, eval("(()=>eval('func()'))()"), "function call inside eval inside arrow function inside eval");
assert.areEqual(func, new func(), "plain constructor call");
assert.areEqual(func, eval("new func()"), "constructor call inside eval");
assert.areEqual(func, eval("eval('new func()')"), "constructor call inside nested evals");
assert.areEqual(func, (()=>new func())(), "constructor call inside arrow function");
assert.areEqual(func, (()=>(()=>new func())())(), "constructor call inside nested arrow functions");
assert.areEqual(func, eval("(()=>new func())()"), "constructor call inside arrow function inside eval");
assert.areEqual(func, (()=>eval("new func()"))(), "constructor call inside eval inside arrow function");
assert.areEqual(func, eval("(()=>eval('new func()'))()"), "constructor call inside eval inside arrow function inside eval");
}
},
{
name: "new.target inside netsted eval, arrow function, and function defintion through eval",
body: function() {
eval("function func() {var f = ()=>{function g() {}; return eval('new.target')}; return f(); }" );
assert.areEqual(undefined, func(), "plain function call");
assert.areEqual(undefined, eval("func()"), "function call inside eval");
assert.areEqual(undefined, eval("eval('func()')"), "function call inside nested evals");
assert.areEqual(undefined, (()=>func())(), "function call inside arrow function");
assert.areEqual(undefined, (()=>(()=>func())())(), "function call inside nested arrow functions");
assert.areEqual(undefined, eval("(()=>func())()"), "function call inside arrow function inside eval");
assert.areEqual(undefined, (()=>eval("func()"))(), "function call inside eval inside arrow function");
assert.areEqual(undefined, eval("(()=>eval('func()'))()"), "function call inside eval inside arrow function inside eval");
assert.areEqual(func, new func(), "plain constructor call");
assert.areEqual(func, eval("new func()"), "constructor call inside eval");
assert.areEqual(func, eval("eval('new func()')"), "constructor call inside nested evals");
assert.areEqual(func, (()=>new func())(), "constructor call inside arrow function");
assert.areEqual(func, (()=>(()=>new func())())(), "constructor call inside nested arrow functions");
assert.areEqual(func, eval("(()=>new func())()"), "constructor call inside arrow function inside eval");
assert.areEqual(func, (()=>eval("new func()"))(), "constructor call inside eval inside arrow function");
assert.areEqual(func, eval("(()=>eval('new func()'))()"), "constructor call inside eval inside arrow function inside eval");
}
},
{
name: "direct and indirect eval with new.target",
body: function() {
function scriptThrows(func, errType, info, errMsg) {
try {
func();
throw Error("No exception thrown");
} catch (err) {
assert.areEqual(errType.name + ':' + errMsg, err.name + ':' + err.message, info);
}
}
scriptThrows(()=>{ WScript.LoadScript("eval('new.target')", "samethread"); }, SyntaxError, "direct eval in global function", "Invalid use of the 'new.target' keyword");
scriptThrows(()=>{ WScript.LoadScript("(()=>eval('new.target'))();", "samethread"); }, SyntaxError, "direct eval in lambda in global function", "Invalid use of the 'new.target' keyword");
scriptThrows(()=>{ WScript.LoadScript("var f=()=>eval('new.target'); (function() { return f(); })();", "samethread"); }, SyntaxError, "direct eval in lambda in global function called by a function", "Invalid use of the 'new.target' keyword");
assert.doesNotThrow(()=>{ WScript.LoadScript("(function() { eval('new.target;') })()", "samethread"); }, "direct eval in function");
assert.doesNotThrow(()=>{ WScript.LoadScript("var f =(function() { return ()=>eval('new.target;') })(); f();", "samethread"); }, "direct eval in lambda defined in function and called by global function");
scriptThrows(()=>{ WScript.LoadScript("(0, eval)('new.target;')", "samethread"); }, SyntaxError, "indirect eval in global function", "Invalid use of the 'new.target' keyword");
scriptThrows(()=>{ WScript.LoadScript("(()=>(0, eval)('new.target'))();", "samethread"); }, SyntaxError, "indirect eval in lambda in global function", "Invalid use of the 'new.target' keyword");
scriptThrows(()=>{ WScript.LoadScript("var f=()=>(0, eval)('new.target'); (function() { return f(); })();", "samethread"); }, SyntaxError, "indirect eval in lambda in global function called by a function", "Invalid use of the 'new.target' keyword");
scriptThrows(()=>{ WScript.LoadScript("(function() { (0, eval)('new.target;') })()", "samethread")}, SyntaxError, "indirect eval in function", "Invalid use of the 'new.target' keyword");
scriptThrows(()=>{ WScript.LoadScript("var f =(function() { return ()=>(0, eval)('new.target;') })(); f();", "samethread"); }, SyntaxError, "indirect eval in lambda defined in function and called by global function", "Invalid use of the 'new.target' keyword");
}
},
];
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });