objlit.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  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. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  6. var tests = [
  7. {
  8. name: "Identifier = value shorthand",
  9. body: function() {
  10. var str = "prop";
  11. assert.areEqual({ str : str }, { str } );
  12. assert.areEqual({"b" : 123, str : str, "foo" : "bar"}, {"b" : 123, str, "foo" : "bar"});
  13. }
  14. },
  15. {
  16. name: "Shorthand names `get` and `set` parse without error (they are not keywords in these cases)",
  17. body: function () {
  18. var a = 0;
  19. var get = 1;
  20. var set = 2;
  21. var z = 3;
  22. var o = { get };
  23. var p = { set };
  24. var q = { get, set };
  25. var r = { set, get };
  26. var s = { get, z };
  27. var t = { a, set };
  28. var u = { a, get, z };
  29. assert.areEqual(1, o.get, "o.get = 1");
  30. assert.areEqual(2, p.set, "p.set = 2");
  31. assert.areEqual(1, q.get, "q.get = 1");
  32. assert.areEqual(2, q.set, "q.set = 2");
  33. assert.areEqual(2, r.set, "r.set = 2");
  34. assert.areEqual(1, r.get, "r.get = 1");
  35. assert.areEqual(1, s.get, "s.get = 1");
  36. assert.areEqual(3, s.z, "s.z = 3");
  37. assert.areEqual(0, t.a, "t.a = 0");
  38. assert.areEqual(2, t.set, "t.set = 2");
  39. assert.areEqual(0, u.a, "u.a = 0");
  40. assert.areEqual(1, u.get, "u.get = 1");
  41. assert.areEqual(3, u.z, "u.z = 3");
  42. }
  43. },
  44. {
  45. name: "Concise method shorthand",
  46. body: function() {
  47. var obj = {
  48. foo() { return "foo"; }
  49. };
  50. assert.areEqual("foo", obj.foo());
  51. assert.areEqual("foo", ({ foo: function() { }, foo() { return "foo"; } }).foo());
  52. assert.areEqual("foo", ({ foo(x) { }, foo() { return "foo"; } }).foo());
  53. }
  54. },
  55. {
  56. name: "Concise method shorthand with `get` and `set` names",
  57. body: function () {
  58. var o = {
  59. get() { return "g"; },
  60. set() { return "s"; }
  61. };
  62. assert.areEqual('g', o.get(), "o.get returns 'g'");
  63. assert.areEqual('s', o.set(), "o.set returns 's'");
  64. }
  65. },
  66. {
  67. name: "Computed property names",
  68. body: function() {
  69. var x;
  70. var obj = {
  71. ["foo" + "bar"] : 1,
  72. [1 * 10 * 10] : 2,
  73. [x = "notfoobar"] : 3,
  74. // computed function name
  75. ["bar" + "foo"] () { return 4 },
  76. [2 * 10 * 10] () { return 5 },
  77. [x = "notbarfoo"] () { return 6 },
  78. // computed get/set method name
  79. set ["boo" + "far" ] (a) { this.x = a * 2 },
  80. get ["boo" + "far" ] () { return this.x },
  81. set [3 * 10 * 10] (a) { this.y = a * a },
  82. get [3 * 10 * 10] () { return this.y },
  83. set [x = "notboofar"] (a) { this.z = a / 3 },
  84. get [x = "notboofar"] () { return this.z }
  85. };
  86. assert.areEqual(1, obj.foobar, "String concat expr as property name");
  87. assert.areEqual(2, obj[100], "Math expr as property name");
  88. assert.areEqual(3, obj.notfoobar, "Element list as property name");
  89. assert.areEqual(4, obj.barfoo(), "String concat expr as method name");
  90. assert.areEqual(5, obj[200] (), "Math expr as method name");
  91. assert.areEqual(6, obj.notbarfoo(), "Element list as method name");
  92. obj.boofar=7;
  93. assert.areEqual(14, obj.boofar, "String concat expr as setter/getter method names");
  94. obj[300]=8;
  95. assert.areEqual(64, obj[300], "Math expr as setter/getter method names");
  96. obj.notboofar=9;
  97. assert.areEqual(3, obj.notboofar, "Element list as setter/getter method names");
  98. var protoObj = {
  99. ["__proto__"] : { abc : 123 }
  100. };
  101. assert.areEqual(protoObj.abc, undefined, "__proto__ does not get assigned as the intrinsic proto when used as a computed property name");
  102. var nestedProtoObj = {
  103. ["__proto__"] : {
  104. ["__" + "proto" + "__"] : {
  105. abc : 123
  106. }
  107. }
  108. };
  109. assert.areEqual(nestedProtoObj.abc, undefined, "Nested dynamic __proto__ literals");
  110. protoObj = {
  111. "__proto__" : { abc : 123 }
  112. };
  113. assert.areEqual(protoObj.abc, 123, "__proto__ get assigned when used as a normal production");
  114. assert.throws(function () { eval("var b = { ['str'] }"); }, SyntaxError, "Invalid computed identifier shorthand", "Expected ':'");
  115. assert.throws(function () { eval("var b = { [1, 2]: 3 }"); }, SyntaxError, "Disallow 'Expression' inside 'ComputedPropertyName'", "Expected ']'");
  116. }
  117. },
  118. {
  119. name: "Duplicate property handling",
  120. body: function () {
  121. // Valid overwrite cases: old style definitions and computed property names
  122. var obj = {
  123. foobar : 1,
  124. "foobar" : 2,
  125. ["foo" + "bar"] : 3,
  126. ["foo" + "bar"] : 4
  127. }
  128. assert.areEqual(obj.foobar, 4, "Opt-in duplicate property handling");
  129. var obj2 = {
  130. ["foo" + "bar"] : 1,
  131. ["foo" + "bar"] : 2
  132. }
  133. assert.areEqual(obj2.foobar, 2, "Duplicate computed property names are allowed");
  134. // Valid cases
  135. var a = "str";
  136. assert.areEqual("str", ({ a, a }).a, "Duplicate identifier references");
  137. assert.areEqual("str", ({ 'foo' : '1', foo() { return "str"; } }).foo(), "Duplicate data property and method definition");
  138. assert.areEqual("str", ({ set foo(x) { }, foo : "str" }).foo, "Duplicate accessors and data property");
  139. assert.areEqual("str", ({ get foo() {}, set foo(x) { }, foo(x) { return "str"; } }).foo(), "Duplicate accessors and method definition");
  140. assert.areEqual("a", ({ get foo() { return "str"; }, set foo(x) { }, ["foo"] : "a" }).foo, "Duplicate accessors and computed property");
  141. }
  142. },
  143. {
  144. name: "BLUE 552728: Object Literal: Use of keywords is not throwing syntax error",
  145. body: function () {
  146. // The following definitions ignore 'yield'
  147. var keywords = ["break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do",
  148. "else", "export", "extends", "finally", "for", "function", "if", "import", "in", "instanceof",
  149. "new", "return", "super", "switch", "this", "throw", "try", "typeof", "var", "void", "while",
  150. "with"];
  151. var futureStrict = ["implements", "let", "private", "public", "interface", "package", "protected", "static"];
  152. // Strict mode rules
  153. for (var keyword in futureStrict) {
  154. assert.throws(function () { eval("use strict; var " + keyword + " = 1; var o = { " + keyword + " };"); }, SyntaxError, keyword + " is a forbidden identifier reference");
  155. }
  156. // TODO (tcare): When generators are implemented, add a test case where the yield operator is used.
  157. assert.throws(function () { eval("use strict; var yield = 1; var o = { yield }; "); }, SyntaxError);
  158. // Non-strict mode rules
  159. for (var keyword in keywords) {
  160. assert.throws(function () { eval("var " + keyword + " = 1; var o = { " + keyword + " };"); }, SyntaxError, keyword + " is a forbidden identifier reference");
  161. }
  162. var yield = 1;
  163. var yieldObj = { yield }; // No error
  164. }
  165. },
  166. {
  167. name: "BLUE 551475: Duplicate property definition not throwing syntax error",
  168. body: function () {
  169. assert.areEqual(3, ({ set b(v) { }, b : 3 }).b, "Duplicate set accessor and data property");
  170. assert.areEqual(3, ({ get b() { }, b : 3 }).b, "Duplicate get accessor and data property");
  171. assert.areEqual(4, ({ b : 3, get b() { return 4; } }).b, "Duplicate data property and set accessor");
  172. }
  173. },
  174. {
  175. name: "BLUE 594468: Computed properties in nested object and function return statements cause assertion",
  176. body: function () {
  177. () => {
  178. return {
  179. ["a"]: null
  180. }
  181. }
  182. }
  183. },
  184. {
  185. name: "BLUE 563637: Computed property ordering causes crash",
  186. body: function () {
  187. var a = {
  188. ["a"] : 10,
  189. b() {
  190. return this.a;
  191. }
  192. };
  193. // The implementation of computed properties causes object literals to
  194. // be constructed (InitFld) up to the first computed property, then StElem
  195. // for each computed property and StFld for any other non-computed properties.
  196. // The following test cases test these transitions.
  197. var original = {
  198. a : 1,
  199. b : 2,
  200. c : 3,
  201. d : 4,
  202. e : 5
  203. };
  204. var orderOne = {
  205. a : 1,
  206. b : 2,
  207. ["c"] : 3,
  208. d : 4,
  209. e : 5
  210. };
  211. var orderTwo = {
  212. a : 1,
  213. b : 2,
  214. c : 3,
  215. d : 4,
  216. ["e"] : 5
  217. };
  218. var orderThree = {
  219. ["a"] : 1,
  220. b : 2,
  221. c : 3,
  222. d : 4,
  223. e : 5
  224. };
  225. var orderFour = {
  226. ["a"] : 1,
  227. b : 2,
  228. ["c"] : 3,
  229. d : 4,
  230. ["e"] : 5
  231. };
  232. assert.areEqual(original, orderOne);
  233. assert.areEqual(original, orderTwo);
  234. assert.areEqual(original, orderThree);
  235. assert.areEqual(original, orderFour);
  236. }
  237. },
  238. {
  239. name: "BLUE 603997: Method formals redeclaration error",
  240. body: function() {
  241. assert.doesNotThrow(function() { eval("var obj = { method(a) { var a; } };"); }, "Object literal method with a var redeclaration does not throw");
  242. assert.throws(function() { eval("var obj = { method(a) { let a; } };"); }, SyntaxError, "Object literal method with a let redeclaration throws", "Let/Const redeclaration");
  243. assert.throws(function() { eval("var obj = { method(a) { const a; } };"); }, SyntaxError, "Object literal method with a const redeclaration throws", "Let/Const redeclaration");
  244. assert.doesNotThrow(function() { eval("var obj = { method(a,b,c) { var b; } };"); }, "Object literal method with a var redeclaration does not throw");
  245. assert.throws(function() { eval("var obj = { method(a,b,c) { let b; } };"); }, SyntaxError, "Object literal method with a let redeclaration throws", "Let/Const redeclaration");
  246. assert.throws(function() { eval("var obj = { method(a,b,c) { const b; } };"); }, SyntaxError, "Object literal method with a const redeclaration throws", "Let/Const redeclaration");
  247. assert.doesNotThrow(function() { eval("var obj = { set method(a) { var a; } };"); }, "Object literal set method with a var redeclaration does not throw");
  248. assert.throws(function() { eval("var obj = { set method(a) { let a; } };"); }, SyntaxError, "Object literal set method with a let redeclaration throws", "Let/Const redeclaration");
  249. assert.throws(function() { eval("var obj = { set method(a) { const a; } };"); }, SyntaxError, "Object literal set method with a const redeclaration throws", "Let/Const redeclaration");
  250. }
  251. },
  252. {
  253. name: "BLUE 618132: __proto__ after a computed property",
  254. body: function () {
  255. var p = { p: 123 };
  256. var o = {
  257. ['someprop'] : 'someprop',
  258. __proto__: p
  259. };
  260. assert.areEqual(p, Object.getPrototypeOf(o));
  261. assert.isTrue(!o.hasOwnProperty("__proto__"));
  262. assert.areEqual(123, o.p);
  263. assert.areEqual('someprop', o.someprop);
  264. assert.areEqual(p, Object.getPrototypeOf(o));
  265. }
  266. },
  267. {
  268. name: "BLUE 617446: Arguments identifier syntax",
  269. body: function () {
  270. function foo() {
  271. var args = { arguments };
  272. return [args.arguments[0], args.arguments[1], args.arguments.length];
  273. }
  274. assert.areEqual([undefined, undefined, 0], foo(), "Arguments object correctly works with identifier syntax");
  275. assert.areEqual([-1, 1, 2], foo(-1, 1), "Arguments object correctly works with identifier syntax");
  276. assert.areEqual([-1, 1, 3], foo(-1, 1, 0), "Arguments object correctly works with identifier syntax");
  277. }
  278. },
  279. {
  280. name: "__proto__ productions",
  281. body: function() {
  282. assert.throws(function() { eval("{ __proto__ : Function.prototype, __proto__ : Array.prototype }"); }, SyntaxError, "More than one regular productions can't define __proto__");
  283. var __proto__ = {};
  284. assert.throws(function() { eval("var o = { __proto__ : Function.prototype, __proto__, __proto__ : Array.prototype };"); }, SyntaxError, "More than one regular productions can't define __proto__ even if there are other productions present");
  285. assert.isTrue({ __proto__, __proto__ : [], __proto__() {}, __proto__ } instanceof Array, "Regular production model should win over all other");
  286. assert.isTrue({ ['__proto__'] : Object.prototype, __proto__ : [], ['__proto__'] : {} } instanceof Array, "Computed property definition of __proto__ shouldn't override the regular production");
  287. assert.isTrue({ __proto__ : [] } instanceof Array, "Regular production for __proto__ should set the internal prototype");
  288. assert.areEqual(Object.getPrototypeOf({ __proto__ : null }), null, "Null should be set as the prototype when specified using normal production");
  289. assert.areEqual(Object.getPrototypeOf({ __proto__ : undefined }), Object.prototype, "Undefined should not be set as the internal prototype for object literal");
  290. assert.areEqual(Object.getPrototypeOf({ __proto__ : "a" }), Object.prototype, "Non-object type string shouldn't be set as the internal prototype for object literal");
  291. assert.areEqual(Object.getPrototypeOf({ __proto__ : 10 }), Object.prototype, "Non-object type number shouldn't be set as the internal prototype for object literal");
  292. assert.areEqual(Object.getPrototypeOf({ __proto__ : true }), Object.prototype, "Non-object type boolean shouldn't be set as the internal prototype for object literal");
  293. var str = "__proto__";
  294. assert.isFalse({ [str] : [] } instanceof Array, "Computed property shouldn't set the internal prototype");
  295. __proto__ = [];
  296. assert.isFalse({ __proto__ } instanceof Array, "Identifier reference shouldn't set the internal prototype");
  297. assert.isFalse({__proto__() {}} instanceof Function, "Method definition shouldn't set the internal prototype");
  298. function f() {}
  299. var obj = { "__proto__" : [], ["__proto__"] : f.prototype };
  300. Array.prototype.x = 1;
  301. f.prototype.x = 10;
  302. assert.areEqual(obj.x, 1, "Regular production should assign the internal prototype");
  303. assert.areEqual(obj.__proto__.x, 10, "Computed property definition of __proto__ is added as a data member");
  304. }
  305. },
  306. {
  307. name: "computed property getters can call super methods",
  308. body: function () {
  309. function ID(x) { return x; }
  310. var proto = {
  311. m() { return ' proto m'; }
  312. };
  313. var object = {
  314. get ['a']() { return 'a' + super.m(); },
  315. get [ID('b')]() { return 'b' + super.m(); },
  316. get [0]() { return '0' + super.m(); },
  317. get [ID(1)]() { return '1' + super.m(); },
  318. };
  319. Object.setPrototypeOf(object, proto);
  320. assert.areEqual('a proto m', object.a, "The value of `object.a` is `'a proto m'`. Defined as `get ['a']() { return 'a' + super.m(); }`");
  321. assert.areEqual('b proto m', object.b, "The value of `object.a` is `'b proto m'`. Defined as `get [ID('b')]() { return 'b' + super.m(); }`");
  322. assert.areEqual('0 proto m', object[0], "The value of `object[0]` is `'0 proto m'`. Defined as `get [0]() { return '0' + super.m(); }`");
  323. assert.areEqual('1 proto m', object[1], "The value of `object[1]` is `'1 proto m'`. Defined as `get [ID(1)]() { return '1' + super.m(); }`");
  324. }
  325. },
  326. {
  327. name: "computed property setters can call super methods",
  328. body: function () {
  329. function ID(x) {
  330. return x;
  331. }
  332. var value;
  333. var proto = {
  334. m(name, v) { value = name + ' ' + v; }
  335. };
  336. var object = {
  337. set ['a'](v) { super.m('a', v); },
  338. set [ID('b')](v) { super.m('b', v); },
  339. set [0](v) { super.m('0', v); },
  340. set [ID(1)](v) { super.m('1', v); },
  341. };
  342. Object.setPrototypeOf(object, proto);
  343. object.a = 2;
  344. assert.areEqual('a 2', value, "The value of `value` is `'a 2'`, after executing `object.a = 2;`");
  345. object.b = 3;
  346. assert.areEqual('b 3', value, "The value of `value` is `'b 3'`, after executing `object.b = 3;`");
  347. object[0] = 4;
  348. assert.areEqual('0 4', value, "The value of `value` is `'0 4'`, after executing `object[0] = 4;`");
  349. object[1] = 5;
  350. assert.areEqual('1 5', value, "The value of `value` is `'1 5'`, after executing `object[1] = 5;`");
  351. }
  352. },
  353. {
  354. name: "computed property methods can call super methods",
  355. body: function () {
  356. function ID(x) { return x; }
  357. var proto = {
  358. m() { return ' proto m'; }
  359. };
  360. var object = {
  361. ['a']() { return 'a' + super.m(); },
  362. [ID('b')]() { return 'b' + super.m(); },
  363. [0]() { return '0' + super.m(); },
  364. [ID(1)]() { return '1' + super.m(); },
  365. };
  366. Object.setPrototypeOf(object, proto);
  367. assert.areEqual('a proto m', object.a(), "`object.a()` returns `'a proto m'`, after executing `Object.setPrototypeOf(object, proto);`");
  368. assert.areEqual('b proto m', object.b(), "`object.b()` returns `'b proto m'`, after executing `Object.setPrototypeOf(object, proto);`");
  369. assert.areEqual('0 proto m', object[0](), "`object[0]()` returns `'0 proto m'`, after executing `Object.setPrototypeOf(object, proto);`");
  370. assert.areEqual('1 proto m', object[1](), "`object[1]()` returns `'1 proto m'`, after executing `Object.setPrototypeOf(object, proto);`");
  371. }
  372. },
  373. {
  374. name: "super method calls in object literal method",
  375. body: function () {
  376. var proto = {
  377. method(x) { return 'proto' + x; }
  378. };
  379. var object = {
  380. method(x) { return super.method(x); }
  381. };
  382. Object.setPrototypeOf(object, proto);
  383. assert.areEqual('proto42', object.method(42), "`object.method(42)` returns `'proto42'`, after executing `Object.setPrototypeOf(object, proto);`");
  384. assert.areEqual('proto42', proto.method(42), "`proto.method(42)` returns `'proto42'`, after executing `Object.setPrototypeOf(object, proto);`");
  385. assert.areEqual('proto42', Object.getPrototypeOf(object).method(42), "`Object.getPrototypeOf(object).method(42)` returns `'proto42'`");
  386. }
  387. },
  388. {
  389. name: "super method calls in object literal getter",
  390. body: function () {
  391. var proto = {
  392. _x: 42,
  393. get x() { return 'proto' + this._x; }
  394. };
  395. var object = {
  396. get x() { return super.x; }
  397. };
  398. Object.setPrototypeOf(object, proto);
  399. assert.areEqual('proto42', object.x, "The value of `object.x` is `'proto42'`, after executing `Object.setPrototypeOf(object, proto);`");
  400. assert.areEqual(42, object._x, "The value of `object._x` is `42`, after executing `Object.setPrototypeOf(object, proto);`");
  401. assert.areEqual(42, Object.getPrototypeOf(object)._x, "The value of `Object.getPrototypeOf(object)._x` is `42`");
  402. }
  403. },
  404. {
  405. name: "super method calls in object literal setter",
  406. body: function () {
  407. var proto = {
  408. _x: 0,
  409. set x(v) { return this._x = v; }
  410. };
  411. var object = {
  412. set x(v) { super.x = v; }
  413. };
  414. Object.setPrototypeOf(object, proto);
  415. assert.areEqual(1, object.x = 1, "`object.x = 1` is `1`, after executing `Object.setPrototypeOf(object, proto);`");
  416. assert.areEqual(1, object._x, "The value of `object._x` is `1`, after executing `Object.setPrototypeOf(object, proto);`");
  417. assert.areEqual(0, Object.getPrototypeOf(object)._x, "The value of `Object.getPrototypeOf(object)._x` is `0`");
  418. }
  419. },
  420. {
  421. name: "The HomeObject of Functions declared as methods is the Object prototype.",
  422. body: function () {
  423. var obj = {
  424. method() { return super.toString; }
  425. };
  426. obj.toString = null;
  427. assert.areEqual(Object.prototype.toString, obj.method());
  428. }
  429. },
  430. {
  431. name: "The HomeObject accessed through default argument of Functions declared as methods is the Object prototype.",
  432. body: function () {
  433. var obj = {
  434. method(x = super.toString) { return x; }
  435. };
  436. obj.toString = null;
  437. assert.areEqual(Object.prototype.toString, obj.method());
  438. }
  439. },
  440. ];
  441. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });