blob: 3621365de3472334053bffa177c9fc7a0b7a9774 [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.
//-------------------------------------------------------------------------------------------------------
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
var tests = [
{
name: "Check if Symbol.unscopables is defined",
body: function ()
{
assert.isTrue(Array.prototype.hasOwnProperty(Symbol.unscopables), "Array should have Array.prototype[@@unscopables] property");
}
},
{
name: "Global scope test on Arrays",
body: function ()
{
var globalScope = -1;
var find = globalScope;
var findIndex = globalScope;
var fill = globalScope;
var copyWithin = globalScope;
var entries = globalScope;
var includes = globalScope;
var keys = globalScope;
var values = globalScope;
with([])
{
assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
assert.areEqual(globalScope, findIndex, "findIndex property is not brought into scope by the with statement");
assert.areEqual(globalScope, fill, "fill property is not brought into scope by the with statement");
assert.areEqual(globalScope, copyWithin, "copyWithin property is not brought into scope by the with statement");
assert.areEqual(globalScope, entries, "entries property is not brought into scope by the with statement");
assert.areEqual(globalScope, includes, "includes property is not brought into scope by the with statement");
assert.areEqual(globalScope, keys, "keys property is not brought into scope by the with statement");
assert.areEqual(globalScope, values, "values property is not brought into scope by the with statement");
}
}
},
{
name: "Add to Array.prototype[@@unscopables]",
body: function ()
{
var globalScope = -1;
var find = globalScope;
var findIndex = globalScope;
var fill = globalScope;
var copyWithin = globalScope;
var entries = globalScope;
var includes = globalScope;
var keys = globalScope;
var values = globalScope;
var slice = globalScope;
var a = [];
a[Symbol.unscopables]["slice"] = true;
with(a)
{
assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
assert.areEqual(globalScope, findIndex, "findIndex property is not brought into scope by the with statement");
assert.areEqual(globalScope, fill, "fill property is not brought into scope by the with statement");
assert.areEqual(globalScope, copyWithin, "copyWithin property is not brought into scope by the with statement");
assert.areEqual(globalScope, entries, "entries property is not brought into scope by the with statement");
assert.areEqual(globalScope, includes, "includes property is not brought into scope by the with statement");
assert.areEqual(globalScope, keys, "keys property is not brought into scope by the with statement");
assert.areEqual(globalScope, values, "values property is not brought into scope by the with statement");
assert.areEqual(globalScope, slice, "slice property is not brought into scope by the with statement");
}
}
},
{
name: "Overwrite @@unscopables",
body: function ()
{
var globalScope = -1;
var c =
{
find : function () {},
slice: function () {},
[Symbol.unscopables]: {find : true }
};
var find = globalScope;
var slice = globalScope;
with(c)
{
assert.isTrue(globalScope != slice, "slice should be on Array scope");
assert.areEqual(globalScope, find, "find should not be on Array scope");
}
var props = {"slice" : true};
c[Symbol.unscopables] = props;
with(c)
{
assert.isTrue(globalScope != find, "find should be on Array scope");
assert.areEqual(globalScope, slice, "slice should not be on Array scope");
}
}
},
{
name: "Adding to @@unscopables in a with statement",
body: function ()
{
var globalScope = -1;
var find = globalScope;
var slice = globalScope;
var c =
{
find : function () {},
slice: function () {},
[Symbol.unscopables]: {"find" : true}
};
with(c)
{
assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
c[Symbol.unscopables]["slice"] = true;
assert.areEqual(globalScope, slice, "slice property is not brought into scope by the with statement");
}
}
},
{
name: "Make sure we did not break with Scope",
body: function ()
{
var b =1;
var c =
{
get : function ()
{
return 4;
},
valueOf: function ()
{
WScript.Echo("valueOf");
return {}; // not a primitive
},
toString: function ()
{
WScript.Echo("toString");
return {}; // not a primitive
}
}
with ({a: 1 , e: { l : 1, w:2}})
{
function f()
{
a = 2; //Set test
b = a; //Get test
}
f();
assert.areEqual(3, Object.keys(c).length, "There are three properties on c");
assert.areEqual(2, Object.keys(e).length, "There are two properties on e");
delete e;
assert.throws(function() {e.l}, ReferenceError, "e should no longer exist");
assert.areEqual(2, a, "a should be 2");
assert.areEqual(2, b, "b should be 2");
}
with(c)
{
assert.areEqual(4, get(), "get is "+get()+" c.get() should be 4");
}
}
},
{
name: "Make sure we do not expose the with Object",
body: function ()
{
var o = {f: function(){ return this; }, x : 2, [Symbol.unscopables]: {"x" : true}};
var x = -1;
var testValue = o.f();
with (o)
{
eval("var b = f();");
assert.areEqual(testValue, b, "This should be handled by the ScopedLdInst unwrapping, which should mean testValue and b are equivalent");
var a = f();
// if this is broken We will get an Assert in the WithScopeObject on chk builds before the areEqual call but I'll leave this for fre builds
assert.areEqual(testValue, a, "This test checks testValue and a are equivalent");
assert.areEqual(-1, x, "x is not brought into scope by the with statement");
assert.areEqual(o.x, b.x, "x is not brought into scope by the with statement");
assert.areEqual(o.x, a.x, "x is not brought into scope by the with statement");
}
}
},
{
name: "Lambda expressions",
body: function ()
{
var adder = function (x)
{
return function (y)
{
return x + y;
};
};
var find = -1;
var findArray = [].find;
with([])
{
find = adder(5);
assert.areEqual(6, find(1), "This should equal 6");
}
assert.isTrue(-1 != find, "This should now be equal to a lambda");
find = findArray;
}
},
{
name: "Operator precedence test",
body: function ()
{
var obj = { a : 1 };
var a = false;
with(obj)
{
obj[Symbol.unscopables] = {};
a = obj[Symbol.unscopables]["a"] = true;
}
assert.areEqual(1, obj.a, " should still be 1");
assert.areEqual(true, a, "a.root should be set to true RHS evaluated before assignment");
}
},
{
name: "Nested functions in with",
body: function ()
{
var globalScope = -1;
var find = globalScope;
var findIndex = globalScope;
var fill = globalScope;
var copyWithin = globalScope;
var entries = globalScope;
var includes = globalScope;
var keys = globalScope;
var values = globalScope;
with([])
{
function foo()
{
function bar()
{
function f00()
{
function bat()
{
assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
assert.areEqual(globalScope, findIndex, "findIndex property is not brought into scope by the with statement");
assert.areEqual(globalScope, fill, "fill property is not brought into scope by the with statement");
assert.areEqual(globalScope, copyWithin, "copyWithin property is not brought into scope by the with statement");
assert.areEqual(globalScope, entries, "entries property is not brought into scope by the with statement");
assert.areEqual(globalScope, includes, "includes property is not brought into scope by the with statement");
assert.areEqual(globalScope, keys, "keys property is not brought into scope by the with statement");
assert.areEqual(globalScope, values, "values property is not brought into scope by the with statement");
}
}
}
}
}
}
},
{
name: "Nested with statements",
body: function ()
{
var str =
{
search: function () {},
split: function () {},
concat: function () {},
reduce: function () {},
[Symbol.unscopables]: {"search" : true, "split": true, "concat" : true, "reduce" : true}
};
var arr =
{
find: function () {},
keys: function () {},
concat: function () {},
reduce: function () {},
[Symbol.unscopables]: {"find" : true, "keys" : true}
};
var globalScope = -1;
var find = globalScope;
var keys = globalScope;
var search = globalScope;
var split = globalScope;
var reduce = globalScope;
var concat = globalScope;
var arrConcat = arr.concat;
var arrReduce = arr.reduce;
with(arr)
{
with(str)
{
assert.areEqual(globalScope, search, "search property is not brought into scope by the with statement");
assert.areEqual(globalScope, split, "split property is not brought into scope by the with statement");
assert.areEqual(arrConcat, concat, "concat should be on the array scope");
assert.areEqual(arrReduce, reduce, "toString should be on the array scope");
assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
assert.areEqual(globalScope, keys, "keys property is not brought into scope by the with statement");
}
}
arr[Symbol.unscopables]["concat"] = true;
arr[Symbol.unscopables]["reduce"] = true;
with(arr)
{
with(str)
{
assert.areEqual(globalScope, search, "search property is not brought into scope by the with statement");
assert.areEqual(globalScope, split, "split property is not brought into scope by the with statement");
assert.areEqual(globalScope, concat, "concat property is not brought into scope by the with statement");
assert.areEqual(globalScope, reduce, "toString property is not brought into scope by the with statement");
assert.areEqual(globalScope, find, "find property is not brought into scope by the with statement");
assert.areEqual(globalScope, keys, "keys property is not brought into scope by the with statement");
}
}
}
},
{
name: "Inheritance test",
body: function ()
{
function foo ()
{
var p = {a: 1};
var obj = {__proto__: p, [Symbol.unscopables]: {'a' : true}};
var a = 2;
with (obj)
{
assert.areEqual(2, a, ""); //Spec change we no longer inherit
}
}
foo();
let p = {a: 1};
let obj = {__proto__: p, [Symbol.unscopables]: {'a' : true}};
let a = 2;
with (obj)
{
assert.areEqual(2, a, "");
}
}
},
{
name: "Per object unscopables check",
body: function ()
{
var globalScope = -1;
var proto = { a: 1, b: 2, c: 3, [Symbol.unscopables]: {'a' : true} };
var child = {__proto__: proto, [Symbol.unscopables]: {'b' : true} };
var child2 = {__proto__: proto, b: 21, c: 31, [Symbol.unscopables]: {'b' : true} };
var a = globalScope;
var b = globalScope;
with(child)
{
assert.areEqual(1, a, "Get @@unscopables finds {'b' : true} on child fist so a is not unscoped");
assert.areEqual(globalScope, b, "b is unscopable in child and we don't property walk to find b on proto");
assert.areEqual(3, c, "c is only on the proto");
a = 3;
b = 4;
assert.areEqual(2, proto.b, "proto.b is never set because child b is unscopable");
}
assert.areEqual(4, b, "root.b is set to 4 because child b is unscopable");
b = globalScope;
assert.areEqual(3, child.a, "child.a should be set to 3");
assert.areEqual(1, proto.a, "proto.a should be set to 1");
var a = globalScope;
proto[Symbol.unscopables]["c"] = true;
with(child2)
{
assert.areEqual(1, a, "Get @@unscopables finds {'b' : true} on child fist so a is not unscoped");
assert.areEqual(globalScope, b, "b is unscopable in child2 and we don't property walk to find b on proto");
assert.areEqual(31, c, "c is unscopable in proto but not child2");
delete c;
assert.areEqual(3, proto.c, "No delete should have happened");
assert.areEqual(3, child2.c, "delete should have happened to 31 should now be 3");
delete c;
assert.areEqual(3, proto.c, "No delete should have happened");
assert.areEqual(3, child2.c, "child2 is still 3");
}
}
},
{
name: "@@unscopables overwritten as something other than an object",
body: function ()
{
var globalScope = -1;
var find = globalScope;
var values = globalScope;
var c =
{
find : function () {},
values: function () {},
[Symbol.unscopables]: {"find" : true, "values" : true }
};
c[Symbol.unscopables] = 5;
with(c)
{
assert.isTrue(globalScope != find, "find should be on Array scope");
assert.isTrue(globalScope != values, "values should be on Array scope");
}
}
},
{
name: "Eval tests",
body: function ()
{
var globalScope = -1;
var find = globalScope;
var c =
{
find : function () {},
[Symbol.unscopables]: {"find" : true }
};
with (c)
{
assert.areEqual(globalScope, eval("find"), "This property is not brought into scope by the with statement");
eval("find = 2");
assert.areEqual(2, eval("find"), "This property is not brought into scope by the with statement");
assert.areEqual(false, eval("delete find"), "You can only delete properties");
}
}
},
{
name: "Mutation test (like the redefinition test just in the with statement)",
body: function ()
{
var o = {a: 1};
var a = 2;
with (o)
{
o[Symbol.unscopables] = {'a' : true }
assert.areEqual(2, a, "root.a should have been set");
}
}
},
{
name: "Compound assignment",
body: function ()
{
var o = {a: 1};
var a = 2;
with (o)
{
a += (o[Symbol.unscopables] = {'a' : true }, 2);
// This is a modification of Brian's Operator precedence test above
// This is a tricky one a is originally not unscopable so the a we use is o.a
// then the assignment happens after it is made unscopable
assert.areEqual(3, a, "should be 1+2");
}
assert.areEqual(1, o.a, "root.a should not have changed");
assert.areEqual(3, a, "should be 1+2");
}
},
{
name: "Global Object affect",
body: function ()
{
var a = 1
this[Symbol.unscopables] = {"a" : true }
assert.areEqual(1, a, "No with statement so @@unscopables should never hit");
var b;
this[Symbol.unscopables]["b"] = true;
b = 1;
assert.areEqual(1, b, "No with statement so @@unscopables should never hit");
this[Symbol.unscopables]["c"] = true;
var c = 1;
assert.areEqual(1, b, "No with statement so @@unscopables should never hit");
}
},
{
name: "Set test",
body: function ()
{
with ([])
{
find = 2;
assert.areEqual(2, find, "find property is not brought into scope by the with statement");
}
assert.areEqual(2, find, "find property is not brought into scope by the with statement");
// strict mode
with ([])
{
function test()
{
"use strict";
assert.throws( function () {findIndex = 2;}, ReferenceError, "In strict mode the variable is undefined");
}
test();
}
// assignment test with let
let o = {[Symbol.unscopables]: {'b' : true }};
let b = -1;
with (o)
{
b = 1;
}
// assignment test with evals
assert.areEqual(undefined, o.b, "o.b should never have been set");
assert.areEqual(1, b, "root.a should have been set");
with(o)
{
eval("b =2;");
}
assert.areEqual(undefined, o.b, "o.b should never have been set");
assert.areEqual(2, b, "root.a should have been set");
}
},
{
name: "Define unscopables for RegExp and then check Global Scope",
body: function ()
{
var globalScope = -1;
var input = globalScope;
var lastMatch = globalScope;
var lastParen = globalScope;
var leftContext = globalScope;
var props = {"input" : true, "lastMatch" : true , "lastParen" : true , "leftContext" : true};
RegExp[Symbol.unscopables] = props;
for( i in RegExp[Symbol.unscopables])
{
assert.areEqual(props[i], RegExp[Symbol.unscopables][i]);
}
assert.isTrue(RegExp.hasOwnProperty(Symbol.unscopables), "RegExp should have RegExp.prototype[@@unscopables] property after definition");
with(RegExp)
{
assert.areEqual(globalScope, input, "input property is not brought into scope by the with statement");
assert.areEqual(globalScope, lastMatch, "lastMatch property is not brought into scope by the with statement");
assert.areEqual(globalScope, lastParen, "lastParen property is not brought into scope by the with statement");
assert.areEqual(globalScope, leftContext, "leftContext property is not brought into scope by the with statement");
}
}
},
{
name: "Confirm a call to @@unscopables happens if the environment record property is called",
body: function ()
{
var env = {x : 1};
var callCount = 0;
Object.defineProperty(env, Symbol.unscopables, {
get: function() {
callCount += 1;
}
});
with (env) {
void x;
}
assert.areEqual(1, callCount, "The environment record has the requested property confirm a call happens");
}
},
{
name: "Spec Bug Fix for OS 4892049",
body: function ()
{
var x = 0;
var env = {};
var callCount = 0;
Object.defineProperty(env, Symbol.unscopables, {
get: function() {
callCount += 1;
}
});
with (env) {
void x;
}
assert.areEqual(0, callCount, "If the environment record does not have requested property don't look up unscopables");
var x = 0;
var env = { x: 1 };
env[Symbol.unscopables] = {};
env[Symbol.unscopables].x = false;
with (env) {
assert.areEqual(1, x, "8.1.1.2.1 step 9a return ToBoolean on the getProperty, if false property is not unscopable");
}
}
},
{
name: "Let unscopables be Get(bindings, @@unscopables) should do prototype chain lookups on unscopables",
body: function ()
{
var unscopables = { x : true };
Object.setPrototypeOf(unscopables, { y: true });
var env = { x : 1, y : 2, [Symbol.unscopables] : unscopables};
var x = -1;
var y = -2;
with(env)
{
assert.areEqual(-1, x, "x is on the @@unscopables object");
assert.areEqual(-2, y, "y is on the @@unscopables prototype");
}
}
},
{
name: "Array.prototype[@@unscopables] [[prototype]] slot is null",
body: function ()
{
assert.areEqual(null, Object.getPrototypeOf(Array.prototype[Symbol.unscopables]), "Array.prototype[@@unscopables].__proto__ === null");
}
},
{
name: "Array.prototype[@@unscopables] property descriptor",
body: function ()
{
var p = Object.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables);
assert.isFalse(p.writable, "Object.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables).writable === false");
assert.isFalse(p.enumerable, "Object.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables).enumerable === false");
assert.isTrue(p.configurable, "Object.getOwnPropertyDescriptor(Array.prototype, Symbol.unscopables).configurable === true");
}
},
];
testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });