ES6NewTarget.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. // ES6 new.target tests
  6. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  7. var tests = [
  8. {
  9. name: "Test new.target parsing path doesn't confuse 'new target'",
  10. body: function() {
  11. function target() { return { name: 'something' }; }
  12. var t = new target; // implicitly 'new target()'
  13. assert.areEqual('something', t.name, "new target() returned our new object instead of new.target");
  14. }
  15. },
  16. {
  17. name: "Test new.target in various block scopes'",
  18. body: function() {
  19. assert.doesNotThrow(function(){{new.target;}}, "new.target one level block do not throw in a function");
  20. assert.doesNotThrow(function(){{{new.target;}}}, "new.target two level block do not throw in a function");
  21. assert.doesNotThrow(function(){with({}) {new.target;}}, "new.target with scope body call does not throw");
  22. assert.doesNotThrow(function() { function parent(x) { new x();}; function child(){ with(new.target) {toString();}}; parent(child); }, "new.target with scope parameter does not throw");
  23. assert.doesNotThrow(function(){{if(true){new.target;}}}, "new.target condition block in nested block do not throw in a function");
  24. assert.doesNotThrow(function(){try { throw Error;} catch(e){new.target;}}, "new.target catch block do not throw in a function");
  25. 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");
  26. assert.doesNotThrow(function(){ var x = function() {new.target;}; x();}, "new.target in function expression do not throw in a function");
  27. assert.doesNotThrow(function(){ var o = { "foo" : function () { new.target}}; o.foo();}, "new.target in named function expression do not throw in a function");
  28. }
  29. },
  30. {
  31. name: "Test new.target parsing path with badly-formed meta-property references",
  32. body: function() {
  33. assert.throws(function() { return new['target']; }, TypeError, "Meta-property new.target is not a real property lookup", "Object doesn't support this action");
  34. assert.throws(function() { return eval('new.'); }, SyntaxError, "Something like 'new.' should fall out of the meta-property parser path", "'new.' is only valid if followed by 'target'");
  35. assert.throws(function() { return eval('new.target2'); }, SyntaxError, "No other keywords should produce meta-properties", "'new.' is only valid if followed by 'target'");
  36. assert.throws(function() { return eval('new.something'); }, SyntaxError, "No other keywords should produce meta-properties", "'new.' is only valid if followed by 'target'");
  37. assert.throws(function() { return eval('new.eval'); }, SyntaxError, "No other keywords should produce meta-properties", "'new.' is only valid if followed by 'target'");
  38. }
  39. },
  40. {
  41. name: "There is now a well-known PID for 'target' - ensure it doesn't break",
  42. body: function() {
  43. var obj = { target: 'something' };
  44. assert.areEqual('something', obj.target, "The name 'target' can be used as an identifier");
  45. }
  46. },
  47. {
  48. name: "new.target is not valid for assignment",
  49. body: function() {
  50. 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");
  51. 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");
  52. }
  53. },
  54. {
  55. name: "Simple base class gets new.target correctly",
  56. body: function() {
  57. var called = false;
  58. class SimpleBaseClass {
  59. constructor() {
  60. assert.isTrue(new.target === SimpleBaseClass, "new.target === SimpleBaseClass");
  61. called = true;
  62. }
  63. }
  64. var myObj = new SimpleBaseClass();
  65. assert.isTrue(called, "The constructor was called.");
  66. }
  67. },
  68. {
  69. name: "Simple derived and base class passes new.target correctly",
  70. body: function() {
  71. var called = false;
  72. class BaseClassForB {
  73. constructor() {
  74. assert.isTrue(new.target === DerivedClassForB, "new.target === DerivedClassForB");
  75. called = true;
  76. }
  77. }
  78. class DerivedClassForB extends BaseClassForB {
  79. constructor() {
  80. assert.isTrue(new.target === DerivedClassForB, "new.target === DerivedClassForB");
  81. super();
  82. }
  83. }
  84. var myB = new DerivedClassForB();
  85. assert.isTrue(called, "The super-chain was called.");
  86. }
  87. },
  88. {
  89. name: "Simple base class with arrow function using new.target correctly",
  90. body: function() {
  91. var called = false;
  92. class SimpleBaseClass {
  93. constructor() {
  94. var arrow = () => {
  95. assert.isTrue(new.target === SimpleBaseClass, "new.target === SimpleBaseClass");
  96. called = true;
  97. }
  98. arrow();
  99. }
  100. }
  101. var myObj = new SimpleBaseClass();
  102. assert.isTrue(called, "The constructor was called.");
  103. }
  104. },
  105. {
  106. name: "new.target behavior in arrow function inside derived class",
  107. body: function() {
  108. let constructed = false;
  109. class C {
  110. constructor() {
  111. let arrow = () => {
  112. assert.isTrue(D === new.target, "Class constructor implicitly invoked via super call has new.target set to derived constructor (also in arrow)");
  113. constructed = true;
  114. };
  115. arrow();
  116. }
  117. }
  118. class D extends C {
  119. constructor() {
  120. let arrow = () => {
  121. assert.isTrue(D === new.target, "Class constructor explicitly invoked via new keyword has new.target set to that constructor (also in arrow)");
  122. };
  123. arrow();
  124. super();
  125. }
  126. }
  127. let myD = new D();
  128. assert.isTrue(constructed, "We actually ran the constructor code");
  129. }
  130. },
  131. {
  132. name: "new.target behavior in a normal function",
  133. body: function() {
  134. function foo() {
  135. assert.isTrue(undefined === new.target, "Normal function call has new.target set to undefined in the function body");
  136. return new.target;
  137. }
  138. assert.isTrue(undefined === foo(), "Normal function returning new.target returns undefined");
  139. }
  140. },
  141. {
  142. name: "new.target behavior in a normal function in a new expression",
  143. body: function() {
  144. function foo() {
  145. assert.isTrue(foo === new.target, "Function called as new expression has new.target set to the function in the function body");
  146. return new.target;
  147. }
  148. assert.isTrue(foo === new foo(), "Function called-as-constructor has new.target set to that function");
  149. }
  150. },
  151. {
  152. name: "new.target behavior in an arrow in a normal function",
  153. body: function() {
  154. function foo() {
  155. let arrow = () => {
  156. assert.isTrue(undefined === new.target, "Normal function call has new.target set to undefined in the function body");
  157. return new.target;
  158. };
  159. return arrow();
  160. }
  161. assert.isTrue(undefined === foo(), "Normal function returning new.target returns undefined");
  162. }
  163. },
  164. {
  165. name: "new.target behaviour in an arrow in a normal function in a new expression",
  166. body: function() {
  167. function foo() {
  168. let arrow = () => {
  169. assert.isTrue(foo === new.target, "Function called as new expression has new.target set to the function in the function body");
  170. return new.target;
  171. };
  172. return arrow();
  173. }
  174. assert.isTrue(foo === new foo(), "Function called-as-constructor has new.target set to that function");
  175. }
  176. },
  177. {
  178. name: "new.target captured from class constructor via arrow",
  179. body: function() {
  180. class base {
  181. constructor() {
  182. let arrow = () => {
  183. assert.isTrue(derived === new.target, "Function called as new expression has new.target set to the function in the function body");
  184. return new.target;
  185. };
  186. return arrow;
  187. }
  188. }
  189. class derived extends base {
  190. constructor() {
  191. return super();
  192. }
  193. }
  194. let arrow = new derived();
  195. assert.isTrue(derived === arrow(), "Arrow capturing new.target returns correct value");
  196. }
  197. },
  198. {
  199. name: "new.target inline constructor case",
  200. body: function() {
  201. function foo()
  202. {
  203. return new.target;
  204. }
  205. function bar()
  206. {
  207. return new foo(); //foo will be inlined here
  208. }
  209. 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");
  210. }
  211. },
  212. {
  213. name: "new.target inline case",
  214. body: function() {
  215. function foo()
  216. {
  217. return new.target;
  218. }
  219. function bar()
  220. {
  221. return foo(); //foo will be inlined here
  222. }
  223. assert.isTrue(bar() == undefined, "Normal inlined function has new.target set to undefined in the function body");
  224. }
  225. },
  226. {
  227. name: "new.target generator case",
  228. body: function() {
  229. function *foo()
  230. {
  231. yield new.target;
  232. }
  233. assert.isTrue((foo()).next().value == undefined, "Generator function has new.target set to undefined in the function body");
  234. }
  235. },
  236. {
  237. name: "new.target inside eval() in function",
  238. body: function() {
  239. function func() {
  240. var g = ()=>eval('new.target;');
  241. return g();
  242. }
  243. assert.areEqual(undefined, func(), "plain function call");
  244. assert.areEqual(undefined, eval("func()"), "function call inside eval");
  245. assert.areEqual(undefined, eval("eval('func()')"), "function call inside nested evals");
  246. assert.areEqual(undefined, (()=>func())(), "function call inside arrow function");
  247. assert.areEqual(undefined, (()=>(()=>func())())(), "function call inside nested arrow functions");
  248. assert.areEqual(undefined, eval("(()=>func())()"), "function call inside arrow function inside eval");
  249. assert.areEqual(undefined, (()=>eval("func()"))(), "function call inside eval inside arrow function");
  250. assert.areEqual(undefined, eval("(()=>eval('func()'))()"), "function call inside eval inside arrow function inside eval");
  251. assert.areEqual(func, new func(), "plain constructor call");
  252. assert.areEqual(func, eval("new func()"), "constructor call inside eval");
  253. assert.areEqual(func, eval("eval('new func()')"), "constructor call inside nested evals");
  254. assert.areEqual(func, (()=>new func())(), "constructor call inside arrow function");
  255. assert.areEqual(func, (()=>(()=>new func())())(), "constructor call inside nested arrow functions");
  256. assert.areEqual(func, eval("(()=>new func())()"), "constructor call inside arrow function inside eval");
  257. assert.areEqual(func, (()=>eval("new func()"))(), "constructor call inside eval inside arrow function");
  258. assert.areEqual(func, eval("(()=>eval('new func()'))()"), "constructor call inside eval inside arrow function inside eval");
  259. }
  260. },
  261. {
  262. name: "new.target inside netsted eval, arrow function, and function defintion through eval",
  263. body: function() {
  264. eval("function func() {var f = ()=>{function g() {}; return eval('new.target')}; return f(); }" );
  265. assert.areEqual(undefined, func(), "plain function call");
  266. assert.areEqual(undefined, eval("func()"), "function call inside eval");
  267. assert.areEqual(undefined, eval("eval('func()')"), "function call inside nested evals");
  268. assert.areEqual(undefined, (()=>func())(), "function call inside arrow function");
  269. assert.areEqual(undefined, (()=>(()=>func())())(), "function call inside nested arrow functions");
  270. assert.areEqual(undefined, eval("(()=>func())()"), "function call inside arrow function inside eval");
  271. assert.areEqual(undefined, (()=>eval("func()"))(), "function call inside eval inside arrow function");
  272. assert.areEqual(undefined, eval("(()=>eval('func()'))()"), "function call inside eval inside arrow function inside eval");
  273. assert.areEqual(func, new func(), "plain constructor call");
  274. assert.areEqual(func, eval("new func()"), "constructor call inside eval");
  275. assert.areEqual(func, eval("eval('new func()')"), "constructor call inside nested evals");
  276. assert.areEqual(func, (()=>new func())(), "constructor call inside arrow function");
  277. assert.areEqual(func, (()=>(()=>new func())())(), "constructor call inside nested arrow functions");
  278. assert.areEqual(func, eval("(()=>new func())()"), "constructor call inside arrow function inside eval");
  279. assert.areEqual(func, (()=>eval("new func()"))(), "constructor call inside eval inside arrow function");
  280. assert.areEqual(func, eval("(()=>eval('new func()'))()"), "constructor call inside eval inside arrow function inside eval");
  281. }
  282. },
  283. {
  284. name: "direct and indirect eval with new.target",
  285. body: function() {
  286. function scriptThrows(func, errType, info, errMsg) {
  287. try {
  288. func();
  289. throw Error("No exception thrown");
  290. } catch (err) {
  291. assert.areEqual(errType.name + ':' + errMsg, err.name + ':' + err.message, info);
  292. }
  293. }
  294. scriptThrows(()=>{ WScript.LoadScript("eval('new.target')", "samethread"); }, SyntaxError, "direct eval in global function", "Invalid use of the 'new.target' keyword");
  295. scriptThrows(()=>{ WScript.LoadScript("(()=>eval('new.target'))();", "samethread"); }, SyntaxError, "direct eval in lambda in global function", "Invalid use of the 'new.target' keyword");
  296. 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");
  297. assert.doesNotThrow(()=>{ WScript.LoadScript("(function() { eval('new.target;') })()", "samethread"); }, "direct eval in function");
  298. 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");
  299. scriptThrows(()=>{ WScript.LoadScript("(0, eval)('new.target;')", "samethread"); }, SyntaxError, "indirect eval in global function", "Invalid use of the 'new.target' keyword");
  300. scriptThrows(()=>{ WScript.LoadScript("(()=>(0, eval)('new.target'))();", "samethread"); }, SyntaxError, "indirect eval in lambda in global function", "Invalid use of the 'new.target' keyword");
  301. 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");
  302. scriptThrows(()=>{ WScript.LoadScript("(function() { (0, eval)('new.target;') })()", "samethread")}, SyntaxError, "indirect eval in function", "Invalid use of the 'new.target' keyword");
  303. 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");
  304. }
  305. },
  306. ];
  307. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });