ES6RestrictedProperties.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  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 restricted property tests
  6. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  7. function verifyAttributes(obj, prop, attribs, name) {
  8. var p = Object.getOwnPropertyDescriptor(obj, prop);
  9. assert.areNotEqual(undefined, p, name + " does not have property named " + prop);
  10. assert.areEqual(attribs.writable, p.writable, name + " has property named " + prop + " with writable = " + attribs.writable);
  11. assert.areEqual(attribs.enumerable, p.enumerable, name + " has property named " + prop + " with enumerable = " + attribs.enumerable);
  12. assert.areEqual(attribs.configurable, p.configurable, name + " has property named " + prop + " with configurable = " + attribs.configurable);
  13. }
  14. function verifyHasRestrictedOwnProperties(obj, name) {
  15. assert.isTrue(obj.hasOwnProperty('caller'), name + " reports that it has own property 'caller'")
  16. assert.isTrue(obj.hasOwnProperty('arguments'), name + " reports that it has own property 'arguments'")
  17. var names = Object.getOwnPropertyNames(obj);
  18. assert.areNotEqual(-1, names.findIndex((e) => { return e === 'arguments'; }), name + " has 'arguments' own property");
  19. assert.areNotEqual(-1, names.findIndex((e) => { return e === 'caller'; }), name + " has 'caller' own property");
  20. verifyAttributes(obj, 'caller', { writable: false, enumerable: false, configurable: false }, name);
  21. assert.isFalse(obj.propertyIsEnumerable('caller'), name + " says 'caller' property is not enumerable");
  22. verifyAttributes(obj, 'arguments', { writable: false, enumerable: false, configurable: false }, name);
  23. assert.isFalse(obj.propertyIsEnumerable('arguments'), name + " says 'arguments' property is not enumerable");
  24. assert.areEqual(null, obj.caller, name + " says 'caller' property is null")
  25. assert.areEqual(null, obj.arguments, name + " says 'arguments' property is null")
  26. assert.doesNotThrow(function() { obj.caller = 'something'; }, name + " has 'caller' property which can't be assigned to");
  27. assert.doesNotThrow(function() { obj.arguments = 'something'; }, name + " has 'arguments' property which can't be assigned to");
  28. assert.throws(function() { 'use strict'; obj.caller = 'something'; }, TypeError, name + " has 'caller' own property but it is not configurable so we will throw in strict mode", "Assignment to read-only properties is not allowed in strict mode");
  29. assert.throws(function() { 'use strict'; obj.arguments = 'something'; }, TypeError, name + " has 'arguments' own property but it is not configurable so we will throw in strict mode", "Assignment to read-only properties is not allowed in strict mode");
  30. assert.areEqual(null, obj.caller, name + " says 'caller' property is null")
  31. assert.areEqual(null, obj.arguments, name + " says 'arguments' property is null")
  32. assert.throws(function() { Object.defineProperty(obj, 'arguments', { value: 123 }); }, TypeError, name + " has 'arguments' property as non-writable, non-configurable", "Cannot modify non-writable property 'arguments'");
  33. assert.throws(function() { Object.defineProperty(obj, 'caller', { value: 123 }); }, TypeError, name + " has 'caller' property as non-writable, non-configurable", "Cannot modify non-writable property 'caller'");
  34. assert.isFalse(delete obj.arguments, name + " has 'arguments' property as non-configurable so delete returns false");
  35. assert.isFalse(delete obj.caller, name + " has 'caller' property as non-configurable so delete returns false");
  36. assert.throws(function() { 'use strict'; delete obj.caller; }, TypeError, name + " has 'caller' own property but it is not configurable so we will throw in strict mode", "Calling delete on 'caller' is not allowed in strict mode");
  37. assert.throws(function() { 'use strict'; delete obj.arguments; }, TypeError, name + " has 'arguments' own property but it is not configurable so we will throw in strict mode", "Calling delete on 'arguments' is not allowed in strict mode");
  38. }
  39. function verifyDoesNotHaveRestrictedOwnProperties(obj, name) {
  40. assert.isFalse(obj.hasOwnProperty('caller'), name + " does not report that it has own property 'caller'")
  41. assert.isFalse(obj.hasOwnProperty('arguments'), name + " does not report that it has own property 'arguments'")
  42. var names = Object.getOwnPropertyNames(obj);
  43. assert.areEqual(-1, names.findIndex((e) => { return e === 'arguments'; }), name + " does not have 'arguments' own property");
  44. assert.areEqual(-1, names.findIndex((e) => { return e === 'caller'; }), name + " does not have 'caller' own property");
  45. assert.areEqual(undefined, Object.getOwnPropertyDescriptor(obj, 'caller'), name + " does not have 'caller' own property")
  46. assert.isFalse(obj.propertyIsEnumerable('caller'), name + " says 'caller' property is not enumerable");
  47. assert.areEqual(undefined, Object.getOwnPropertyDescriptor(obj, 'arguments'), name + " does not have 'arguments' own property");
  48. assert.isFalse(obj.propertyIsEnumerable('arguments'), name + " says 'arguments' property is not enumerable");
  49. assert.throws(function() { obj.caller; }, TypeError, name + " throws on access to 'caller' property", "Accessing the 'caller' property is restricted in this context");
  50. assert.throws(function() { obj.arguments; }, TypeError, name + " throws on access to 'arguments' property", "Accessing the 'arguments' property is restricted in this context");
  51. assert.throws(function() { 'use strict'; obj.caller; }, TypeError, name + " throws on access to 'caller' property in strict mode", "Accessing the 'caller' property is restricted in this context");
  52. assert.throws(function() { 'use strict'; obj.arguments; }, TypeError, name + " throws on access to 'arguments' property in strict mode", "Accessing the 'arguments' property is restricted in this context");
  53. assert.throws(function() { obj.caller = 'something'; }, TypeError, name + " throws trying to assign to 'caller' property", "Accessing the 'caller' property is restricted in this context");
  54. assert.throws(function() { obj.arguments = 'something'; }, TypeError, name + " throws trying to assign to 'arguments' property", "Accessing the 'arguments' property is restricted in this context");
  55. assert.throws(function() { 'use strict'; obj.caller = 'something'; }, TypeError, name + " throws trying to assign to 'caller' property in strict mode", "Accessing the 'caller' property is restricted in this context");
  56. assert.throws(function() { 'use strict'; obj.arguments = 'something'; }, TypeError, name + " throws trying to assign to 'arguments' property in strict mode", "Accessing the 'arguments' property is restricted in this context");
  57. assert.isTrue(delete obj.arguments, name + " allows deleting own property named 'arguments' if that property doesn't exist");
  58. assert.doesNotThrow(function() { Object.defineProperty(obj, 'arguments', { value: 123, writable: true, enumerable: true, configurable: true }); }, name + " doesn't have own 'arguments' property");
  59. assert.isTrue(obj.hasOwnProperty('arguments'), name + " has own property 'arguments' after defineProperty")
  60. assert.isTrue(obj.propertyIsEnumerable('arguments'), name + " says 'arguments' property is enumerable if it is an enumerable own property");
  61. assert.areEqual(123, obj.arguments, name + " can have an own property defined for 'arguments'")
  62. verifyAttributes(obj, 'arguments', { writable: true, enumerable: true, configurable: true }, name);
  63. assert.isTrue(delete obj.arguments, name + " allows deleting own property named 'arguments' if that property does exist");
  64. assert.isFalse(obj.hasOwnProperty('arguments'), name + " doesn't have own property 'arguments' after delete")
  65. assert.isTrue(delete obj.caller, name + " allows deleting own property named 'caller' if that property doesn't exist");
  66. assert.doesNotThrow(function() { Object.defineProperty(obj, 'caller', { value: 123, writable: true, enumerable: true, configurable: true }); }, name + " doesn't have own 'caller' property");
  67. assert.isTrue(obj.hasOwnProperty('caller'), name + " has own property 'caller' after defineProperty")
  68. assert.isTrue(obj.propertyIsEnumerable('caller'), name + " says 'caller' property is enumerable if it is an enumerable own property");
  69. assert.areEqual(123, obj.caller, name + " can have an own property defined for 'caller'")
  70. verifyAttributes(obj, 'caller', { writable: true, enumerable: true, configurable: true }, name);
  71. assert.isTrue(delete obj.caller, name + " allows deleting own property named 'caller' if that property does exist");
  72. assert.isFalse(obj.hasOwnProperty('caller'), name + " doesn't have own property 'caller' after delete")
  73. // Remove Function.prototype from the prototype chain.
  74. Object.setPrototypeOf(obj, Object.prototype);
  75. assert.areEqual(undefined, obj.arguments, name + " does not initially have 'arguments' property when disconnected from Function.prototype");
  76. assert.doesNotThrow(function() { obj.arguments = 'abc'; }, name + " can set the 'arguments' property when disconnected from Function.prototype");
  77. assert.areEqual('abc', obj.arguments, name + " can set the 'arguments' property when disconnected from Function.prototype");
  78. assert.isTrue(obj.hasOwnProperty('arguments'), name + " has 'arguments' own property")
  79. assert.isTrue(obj.propertyIsEnumerable('arguments'), name + " says 'arguments' property is enumerable if it is an enumerable own property");
  80. verifyAttributes(obj, 'arguments', { writable: true, enumerable: true, configurable: true }, name);
  81. assert.isTrue(delete obj.arguments, name + " allows deleting own property named 'arguments' if that property does exist");
  82. assert.isFalse(obj.hasOwnProperty('arguments'), name + " doesn't have own property 'arguments' after delete")
  83. assert.areEqual(undefined, obj.caller, name + " does not initially have 'caller' property when disconnected from Function.prototype");
  84. assert.doesNotThrow(function() { obj.caller = 'abc'; }, name + " can set the 'caller' property when disconnected from Function.prototype");
  85. assert.areEqual('abc', obj.caller, name + " can set the 'caller' property when disconnected from Function.prototype");
  86. assert.isTrue(obj.hasOwnProperty('caller'), name + " has 'caller' own property")
  87. assert.isTrue(obj.propertyIsEnumerable('caller'), name + " says 'caller' property is enumerable if it is an enumerable own property");
  88. verifyAttributes(obj, 'caller', { writable: true, enumerable: true, configurable: true }, name);
  89. assert.isTrue(delete obj.caller, name + " allows deleting own property named 'caller' if that property does exist");
  90. assert.isFalse(obj.hasOwnProperty('caller'), name + " doesn't have own property 'caller' after delete")
  91. }
  92. var tests = [
  93. {
  94. name: "Restricted properties of Function.prototype",
  95. body: function () {
  96. var obj = Function.prototype;
  97. assert.isTrue(obj.hasOwnProperty('caller'), "Function.prototype has own property 'caller'")
  98. assert.isTrue(obj.hasOwnProperty('arguments'), "Function.prototype has own property 'arguments'")
  99. var p = Object.getOwnPropertyDescriptor(obj, 'caller');
  100. assert.isFalse(p.enumerable, "Function.prototype function has 'caller' own property which is not enumerable");
  101. assert.isFalse(p.configurable, "Function.prototype function has 'caller' own property which is not configurable");
  102. assert.isFalse(obj.propertyIsEnumerable('caller'), "Function.prototype says 'caller' property is not enumerable");
  103. assert.areEqual('function', typeof p.get, "Function.prototype['caller'] has get accessor function");
  104. assert.areEqual('function', typeof p.set, "Function.prototype['caller'] has set accessor function");
  105. assert.throws(function() { p.get(); }, TypeError, "Function.prototype['caller'] has get accessor which throws");
  106. assert.throws(function() { p.set(); }, TypeError, "Function.prototype['caller'] has set accessor which throws");
  107. assert.isTrue(p.get === p.set, "Function.prototype returns the same ThrowTypeError function for get/set accessor of 'caller' property");
  108. var p2 = Object.getOwnPropertyDescriptor(obj, 'arguments');
  109. assert.isFalse(p2.enumerable, "Function.prototype function has 'arguments' own property which is not enumerable");
  110. assert.isFalse(p2.configurable, "Function.prototype function has 'arguments' own property which is not configurable");
  111. assert.isFalse(obj.propertyIsEnumerable('arguments'), "Function.prototype says 'arguments' property is not enumerable");
  112. assert.areEqual('function', typeof p2.get, "Function.prototype['arguments'] has get accessor function");
  113. assert.areEqual('function', typeof p2.set, "Function.prototype['arguments'] has set accessor function");
  114. assert.throws(function() { p2.get(); }, TypeError, "Function.prototype['arguments'] has get accessor which throws");
  115. assert.throws(function() { p2.set(); }, TypeError, "Function.prototype['arguments'] has set accessor which throws");
  116. assert.isTrue(p2.get === p2.set, "Function.prototype returns the same ThrowTypeError function for get/set accessor of 'arguments' property");
  117. assert.isTrue(p.get === p2.get, "Function.prototype returns the same ThrowTypeError function for accessor of both 'arguments' and 'caller' properties");
  118. var names = Object.getOwnPropertyNames(obj);
  119. assert.areNotEqual(-1, names.findIndex((e) => { return e === 'arguments'; }), "Function.prototype has 'arguments' own property");
  120. assert.areNotEqual(-1, names.findIndex((e) => { return e === 'caller'; }), "Function.prototype has 'caller' own property");
  121. assert.throws(function() { obj.caller; }, TypeError, "Function.prototype throws on access to 'caller' property", "Accessing the 'caller' property is restricted in this context");
  122. assert.throws(function() { obj.arguments; }, TypeError, "Function.prototype throws on access to 'arguments' property", "Accessing the 'arguments' property is restricted in this context");
  123. assert.throws(function() { obj.caller = 'something'; }, TypeError, "Function.prototype throws trying to assign to 'caller' property", "Accessing the 'caller' property is restricted in this context");
  124. assert.throws(function() { obj.arguments = 'something'; }, TypeError, "Function.prototype throws trying to assign to 'arguments' property", "Accessing the 'arguments' property is restricted in this context");
  125. // TODO: These descriptors should have configurable set to true so remaining asserts in this test should actually succeed
  126. assert.throws(function() { Object.defineProperty(obj, 'arguments', { value: 123 }); }, TypeError, "Function.prototype has 'arguments' property as non-configurable", "Cannot redefine non-configurable property 'arguments'");
  127. assert.throws(function() { Object.defineProperty(obj, 'caller', { value: 123 }); }, TypeError, "Function.prototype has 'caller' property as non-configurable", "Cannot redefine non-configurable property 'caller'");
  128. assert.isFalse(delete obj.arguments, "Function.prototype has 'arguments' property as non-configurable so delete returns false");
  129. assert.isFalse(delete obj.caller, "Function.prototype has 'caller' property as non-configurable so delete returns false");
  130. assert.throws(function() { 'use strict'; delete obj.caller; }, TypeError, "Function.prototype has 'caller' own property but it is not configurable so we will throw in strict mode", "Calling delete on 'caller' is not allowed in strict mode");
  131. assert.throws(function() { 'use strict'; delete obj.arguments; }, TypeError, "Function.prototype has 'arguments' own property but it is not configurable so we will throw in strict mode", "Calling delete on 'arguments' is not allowed in strict mode");
  132. }
  133. },
  134. {
  135. name: "Restricted properties of non-strict function",
  136. body: function () {
  137. function obj() {};
  138. verifyHasRestrictedOwnProperties(obj, "Non-strict function");
  139. }
  140. },
  141. {
  142. name: "Restricted properties of strict function",
  143. body: function () {
  144. function foo() { 'use strict'; };
  145. verifyDoesNotHaveRestrictedOwnProperties(foo, "Strict function");
  146. }
  147. },
  148. {
  149. name: "Restricted properties of class",
  150. body: function () {
  151. class A { };
  152. verifyDoesNotHaveRestrictedOwnProperties(A, "Class");
  153. }
  154. },
  155. {
  156. name: "Restricted properties of class static method",
  157. body: function () {
  158. class A {
  159. static static_method() { }
  160. };
  161. verifyDoesNotHaveRestrictedOwnProperties(A.static_method, "Class static method");
  162. }
  163. },
  164. {
  165. name: "Restricted properties of strict-mode class static method",
  166. body: function () {
  167. class A {
  168. static static_method() { 'use strict'; }
  169. };
  170. verifyDoesNotHaveRestrictedOwnProperties(A.static_method, "Class strict-mode static method");
  171. }
  172. },
  173. {
  174. name: "Restricted properties of class method",
  175. body: function () {
  176. class A {
  177. method() { }
  178. };
  179. verifyDoesNotHaveRestrictedOwnProperties(A.prototype.method, "Class method");
  180. }
  181. },
  182. {
  183. name: "Restricted properties of strict-mode class method",
  184. body: function () {
  185. class A {
  186. method() { 'use strict'; }
  187. };
  188. verifyDoesNotHaveRestrictedOwnProperties(A.prototype.method, "Class strict-mode method");
  189. }
  190. },
  191. {
  192. name: "Restricted properties of class with 'caller' static method",
  193. body: function () {
  194. var obj = class A {
  195. static caller() { return 42; }
  196. };
  197. assert.isTrue(obj.hasOwnProperty('caller'), "Class does has own property 'caller'")
  198. assert.isFalse(obj.hasOwnProperty('arguments'), "Class does not report that it has own property 'arguments'")
  199. assert.areEqual('{"writable":true,"enumerable":false,"configurable":true}', JSON.stringify(Object.getOwnPropertyDescriptor(obj, 'caller')), "Class does not have 'caller' own property")
  200. assert.areEqual(undefined, JSON.stringify(Object.getOwnPropertyDescriptor(obj, 'arguments')), "Class does not have 'arguments' own property");
  201. assert.areEqual('["caller","length","name","prototype"]', JSON.stringify(Object.getOwnPropertyNames(obj).sort()), "Class does not have 'caller' and 'arguments' own properties");
  202. assert.areEqual(42, obj.caller(), "Accessing the 'caller' property is not restricted");
  203. assert.throws(function() { obj.arguments; }, TypeError, "Class throws on access to 'arguments' property", "Accessing the 'arguments' property is restricted in this context");
  204. }
  205. },
  206. {
  207. name: "Restricted properties of class with 'arguments' static get method",
  208. body: function () {
  209. var obj = class A {
  210. static get arguments() { return 42; }
  211. };
  212. assert.isFalse(obj.hasOwnProperty('caller'), "Class does not report that it has own property 'caller'")
  213. assert.isTrue(obj.hasOwnProperty('arguments'), "Class has own property 'arguments'")
  214. assert.areEqual(undefined, JSON.stringify(Object.getOwnPropertyDescriptor(obj, 'caller')), "Class does not have 'caller' own property")
  215. assert.areEqual('{"enumerable":false,"configurable":true}', JSON.stringify(Object.getOwnPropertyDescriptor(obj, 'arguments')), "Class has 'arguments' own property");
  216. assert.areEqual('["arguments","length","name","prototype"]', JSON.stringify(Object.getOwnPropertyNames(obj).sort()), "Class has 'arguments' own property, no 'caller' own property");
  217. assert.throws(function() { obj.caller; }, TypeError, "Class method throws on access to 'caller' property", "Accessing the 'caller' property is restricted in this context");
  218. assert.areEqual(42, obj.arguments, "Accessing the 'arguments' property is not restricted");
  219. }
  220. },
  221. {
  222. name: "Restricted properties of class with 'arguments' set method",
  223. body: function () {
  224. var my_v;
  225. class A {
  226. set arguments(v) { my_v = v; }
  227. };
  228. var obj = A;
  229. assert.isFalse(obj.hasOwnProperty('caller'), "Class does not report that it has own property 'caller'")
  230. assert.isFalse(obj.hasOwnProperty('arguments'), "Class does not report that it has own property 'arguments'")
  231. assert.areEqual(undefined, JSON.stringify(Object.getOwnPropertyDescriptor(obj, 'caller')), "Class does not have 'caller' own property")
  232. assert.areEqual(undefined, JSON.stringify(Object.getOwnPropertyDescriptor(obj, 'arguments')), "Class has 'arguments' own property");
  233. assert.areEqual('["length","name","prototype"]', JSON.stringify(Object.getOwnPropertyNames(obj).sort()), "Class has 'arguments' own property, no 'caller' own property");
  234. assert.throws(function() { obj.caller; }, TypeError, "Class method throws on access to 'caller' property", "Accessing the 'caller' property is restricted in this context");
  235. assert.throws(function() { obj.arguments; }, TypeError, "Class method throws on access to 'arguments' property", "Accessing the 'arguments' property is restricted in this context");
  236. var a = new A();
  237. assert.isFalse(a.hasOwnProperty('caller'), "Class instance does not report that it has own property 'caller'")
  238. assert.isFalse(a.hasOwnProperty('arguments'), "Class instance does not report that it has own property 'arguments'")
  239. assert.isFalse(a.__proto__.hasOwnProperty('caller'), "Class prototype does not report that it has own property 'caller'")
  240. assert.isTrue(a.__proto__.hasOwnProperty('arguments'), "Class prototype has own property 'arguments'")
  241. a.arguments = 50;
  242. assert.areEqual(50, my_v, "Accessing the 'arguments' property was not restricted");
  243. assert.areEqual(undefined, a.caller, "Access to class instance 'caller' property doesn't throw - instance is not a function");
  244. a.caller = 123;
  245. assert.areEqual(123, a.caller, "Assignment to class instance 'caller' property works normally");
  246. }
  247. },
  248. {
  249. name: "Restricted properties of lambda",
  250. body: function () {
  251. var obj = () => { }
  252. verifyDoesNotHaveRestrictedOwnProperties(obj, "Lambda");
  253. }
  254. },
  255. {
  256. name: "Restricted properties of strict-mode lambda",
  257. body: function () {
  258. var obj = () => { 'use strict'; }
  259. verifyDoesNotHaveRestrictedOwnProperties(obj, "Strict-mode lambda");
  260. }
  261. },
  262. {
  263. name: "Restricted properties of bound function",
  264. body: function () {
  265. function target() {}
  266. var obj = target.bind(null);
  267. verifyDoesNotHaveRestrictedOwnProperties(obj, "Bound function");
  268. }
  269. },
  270. {
  271. name: "Restricted properties of bound strict-mode function",
  272. body: function () {
  273. function target() { 'use strict'; }
  274. var obj = target.bind(null);
  275. verifyDoesNotHaveRestrictedOwnProperties(obj, "Bound strict-mode function");
  276. }
  277. },
  278. {
  279. name: "Restricted properties of generator function",
  280. body: function () {
  281. function* gf() { }
  282. verifyDoesNotHaveRestrictedOwnProperties(gf, "Generator function");
  283. }
  284. },
  285. {
  286. name: "Restricted properties of strict-mode generator function",
  287. body: function () {
  288. function* gf() { 'use strict'; }
  289. verifyDoesNotHaveRestrictedOwnProperties(gf, "Generator strict-mode function");
  290. }
  291. },
  292. {
  293. name: "Restricted properties of object-literal function",
  294. body: function () {
  295. var obj = { func() { } }
  296. verifyHasRestrictedOwnProperties(obj.func, "Object-literal function");
  297. }
  298. },
  299. {
  300. name: "Restricted properties of strict-mode object-literal function",
  301. body: function () {
  302. var obj = { func() { 'use strict'; } }
  303. verifyDoesNotHaveRestrictedOwnProperties(obj.func, "Object-literal strict-mode function");
  304. }
  305. },
  306. ];
  307. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });