definegettersetter.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
  4. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  5. //-------------------------------------------------------------------------------------------------------
  6. // Basic __defineGetter__, __defineSetter__, __lookupGetter__, and __lookupSetter tests -- verifies the API properties and functionality
  7. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  8. var globalObject = this;
  9. var tests = {
  10. test01: {
  11. name: "__defineGetter__ defines an accessor property with getter as specified and enumerable and configurable set to true",
  12. body: function () {
  13. var o = { };
  14. var result = o.__defineGetter__("a", function () { return 1234; });
  15. assert.isTrue(result === undefined, "__defineGetter__ should return undefined");
  16. assert.isTrue(o.a === 1234, "Getter should call the given function and return its value");
  17. var d = Object.getOwnPropertyDescriptor(o, "a");
  18. assert.isTrue(d.enumerable, "Getter accessor property should be enumerable");
  19. assert.isTrue(d.configurable, "Getter accessor property should be configurable");
  20. }
  21. },
  22. test02: {
  23. name: "__defineSetter__ defines an accessor property with getter as specified and enumerable and configurable set to true",
  24. body: function () {
  25. var o = { v: 0 };
  26. var result = o.__defineSetter__("a", function (v) { throw new Error(); });
  27. assert.isTrue(result === undefined, "__defineSetter__ should return undefined");
  28. assert.throws(function () { o.a = 1234; }, Error, "Setter should call the given function");
  29. var d = Object.getOwnPropertyDescriptor(o, "a");
  30. assert.isTrue(d.enumerable, "Setter accessor property should be enumerable");
  31. assert.isTrue(d.configurable, "Setter accessor property should be configurable");
  32. }
  33. },
  34. test03: {
  35. name: "__defineGetter__ should not assign a setter and __defineSetter__ should not define a getter",
  36. body: function () {
  37. var o = { };
  38. o.__defineGetter__("a", function () { return 1234; });
  39. o.__defineSetter__("b", function (v) { });
  40. var da = Object.getOwnPropertyDescriptor(o, "a");
  41. var db = Object.getOwnPropertyDescriptor(o, "b");
  42. assert.isTrue(da.set === undefined, "__defineGetter__ does not add a setter");
  43. assert.isTrue(db.get === undefined, "__defineSetter__ does not add a getter");
  44. o.a = 10;
  45. assert.isTrue(o.a === 1234, "Getter only property should be unaffected by uses in setter context");
  46. assert.isTrue(o.b === undefined, "Setter only property should return undefined if used in getter context");
  47. }
  48. },
  49. test04: {
  50. name: "get and set functions should have access to the object's properties via this",
  51. body: function () {
  52. var o = { x: 1, y: 2, z: 3 };
  53. o.__defineGetter__("a", function () { return this.x + this.y + this.z; });
  54. o.__defineSetter__("b", function (v) { this.x = v; this.y = v * 2; this.z = v * 3; });
  55. assert.isTrue(o.a === 6, "Getter should return 1 + 2 + 3");
  56. o.b = 2;
  57. assert.isTrue(o.a === 12, "Getter should now return 2 + 4 + 6");
  58. }
  59. },
  60. test05: {
  61. name: "__defineGetter__ and __defineSetter__ called on the same property are additive; they do not clobber previous accessor",
  62. body: function () {
  63. var o = { };
  64. o.__defineGetter__("a", function () { return 1; });
  65. o.__defineSetter__("a", function (v) { throw new Error(2); });
  66. o.__defineSetter__("b", function (v) { throw new Error(3); });
  67. o.__defineGetter__("b", function () { return 4; });
  68. assert.isTrue(o.a === 1, "getter in 'a' should return 1");
  69. assert.isTrue((function () { try { o.a = 0; } catch (e) { return e.message; } return null; })() === "2", "setter in 'a' should throw a new Error with number equal to 2");
  70. assert.isTrue((function () { try { o.b = 0; } catch (e) { return e.message; } return null; })() === "3", "setter in 'b' should throw a new Error with number equal to 3");
  71. assert.isTrue(o.b === 4, "getter in 'b' should return 4");
  72. }
  73. },
  74. test06: {
  75. name: "__defineGetter__ and __defineSetter__ only allow functions as the accessor argument",
  76. body: function () {
  77. function testBadArg(arg) {
  78. var o = { };
  79. assert.throws(function () { o.__defineGetter__("a", arg); }, TypeError, "__defineGetter__ should throw with getter function arg: " + arg);
  80. assert.throws(function () { o.__defineSetter__("a", arg); }, TypeError, "__defineSetter__ should throw with setter function arg: " + arg);
  81. }
  82. testBadArg(undefined);
  83. testBadArg(null);
  84. testBadArg(0);
  85. testBadArg(1234);
  86. testBadArg("hello");
  87. testBadArg({ a: 1, b: 2 });
  88. testBadArg([ 1, 2 ]);
  89. }
  90. },
  91. test07: {
  92. name: "__defineGetter__ and __defineSetter__ overwrite existing property descriptors when configurable, otherwise throws",
  93. body: function () {
  94. function testWithExistingDescriptor(descriptor) {
  95. var shouldThrow = descriptor.configurable ? false : true;
  96. var o = { };
  97. Object.defineProperty(o, "a", descriptor);
  98. var fnDefGet = function () { o.__defineGetter__("a", function () { return undefined; }); };
  99. var fnDefSet = function () { o.__defineSetter__("a", function (v) { }); };
  100. if (shouldThrow) {
  101. assert.throws(fnDefGet, TypeError, "__defineGetter__ should throw when called on existing non-configurable property");
  102. assert.throws(fnDefSet, TypeError, "__defineSetter__ should throw when called on existing non-configurable property");
  103. } else {
  104. fnDefGet();
  105. fnDefSet();
  106. var owndesc = Object.getOwnPropertyDescriptor(o, "a");
  107. assert.isFalse(owndesc.hasOwnProperty("writable"), "property should no longer be a data accessor if it happened to be");
  108. assert.isFalse(owndesc.hasOwnProperty("value"), "property should no longer be a data accessor if it happened to be");
  109. assert.isTrue(owndesc.get !== undefined, "property should now have a getter");
  110. assert.isTrue(owndesc.set !== undefined, "property should now have a setter");
  111. assert.isTrue(owndesc.configurable, "property should still be configurable");
  112. assert.isTrue(owndesc.enumerable, "property should now be enumerable if it wasn't already");
  113. }
  114. }
  115. // generic descriptor
  116. testWithExistingDescriptor({ configurable: true });
  117. testWithExistingDescriptor({ enumerable: true });
  118. testWithExistingDescriptor({ configurable: true, enumerable: true });
  119. testWithExistingDescriptor({ configurable: false });
  120. testWithExistingDescriptor({ enumerable: false });
  121. testWithExistingDescriptor({ configurable: false, enumerable: false });
  122. // data descriptor
  123. testWithExistingDescriptor({ value: 10 });
  124. testWithExistingDescriptor({ writable: true });
  125. testWithExistingDescriptor({ value: 10, writable: true });
  126. testWithExistingDescriptor({ value: 10, enumerable: true });
  127. testWithExistingDescriptor({ writable: true, enumerable: true });
  128. testWithExistingDescriptor({ value: 10, writable: true, enumerable: true });
  129. testWithExistingDescriptor({ value: 10, configurable: true });
  130. testWithExistingDescriptor({ writable: true, configurable: true });
  131. testWithExistingDescriptor({ value: 10, writable: true, configurable: true });
  132. testWithExistingDescriptor({ value: 10, configurable: true, enumerable: true });
  133. testWithExistingDescriptor({ writable: true, configurable: true, enumerable: true });
  134. testWithExistingDescriptor({ value: 10, writable: true, configurable: true, enumerable: true });
  135. // accessor descriptor
  136. //
  137. // already handled accessor descriptors implicitly via successive calls to
  138. // __defineGetter__ and __defineSetter__ with the same property name
  139. // Just make sure non-configurable accessor descriptor cannot be changed:
  140. testWithExistingDescriptor({ get: function () { }, configurable: false });
  141. testWithExistingDescriptor({ set: function (v) { }, configurable: false });
  142. }
  143. },
  144. test08: {
  145. name: "__defineGetter__ and __defineSetter__ should work regardless whether Object.defineProperty is changed by the user or not",
  146. body: function () {
  147. var builtinDefineProperty = Object.defineProperty;
  148. Object.defineProperty = function (o, p, d) { throw new Error("Should not execute this"); };
  149. var o = { };
  150. o.__defineGetter__("a", function () { return 1234; });
  151. o.__defineSetter__("a", function (v) { throw new Error(); });
  152. assert.isTrue(o.a === 1234, "Getter should be assigned and execute like normal");
  153. assert.throws(function () { o.a = 0; }, Error, "Setter should be assigned and execute like normal");
  154. var d = Object.getOwnPropertyDescriptor(o, "a");
  155. assert.isTrue(d.get !== undefined, "Accessor descriptor has get value");
  156. assert.isTrue(d.set !== undefined, "Accessor descriptor has set value");
  157. assert.isTrue(d.configurable, "Property is configurable");
  158. assert.isTrue(d.enumerable, "Property is enumerable");
  159. Object.defineProperty = builtinDefineProperty;
  160. }
  161. },
  162. test09: {
  163. name: "__defineGetter__ and __defineSetter__ both have length 2 and __lookupGetter__ and __lookupSetter__ both have length 1",
  164. body: function () {
  165. assert.isTrue(Object.prototype.__defineGetter__.length === 2, "__defineGetter__.length should be 2");
  166. assert.isTrue(Object.prototype.__defineSetter__.length === 2, "__defineSetter__.length should be 2");
  167. assert.isTrue(Object.prototype.__lookupGetter__.length === 1, "__lookupGetter__.length should be 1");
  168. assert.isTrue(Object.prototype.__lookupSetter__.length === 1, "__lookupSetter__.length should be 1");
  169. }
  170. },
  171. test10: {
  172. name: "__defineGetter__ and __defineSetter__ should throw TypeError with null/undefined this argument",
  173. body: function () {
  174. assert.throws(() => { Object.prototype.__defineGetter__.call(undefined, "test10_undefined_getter", function () { return undefined; }); }, TypeError, "__defineGetter__ should throw TypeError when this object is Undefined.");
  175. assert.throws(() => { Object.prototype.__defineGetter__.call(null, "test10_null_getter", function () { return undefined; }); }, TypeError, "__defineGetter__ should throw TypeError when this object is Null.");
  176. assert.throws(() => { Object.prototype.__defineSetter__.call(undefined, "test10_undefined_setter", function (v) { }); }, TypeError, "__defineSetter__ should throw TypeError when this object is Undefined.");
  177. assert.throws(() => { Object.prototype.__defineSetter__.call(null, "test10_null_setter", function (v) { }); }, TypeError, "__defineSetter__ should throw TypeError when this object is Null.");
  178. assert.isFalse(globalObject.hasOwnProperty("test10_undefined_getter"), "global object should now have a getter named test10_undefined_getter");
  179. assert.isFalse(globalObject.hasOwnProperty("test10_null_getter"), "global object should now have a getter named test10_null_getter");
  180. assert.isFalse(globalObject.hasOwnProperty("test10_undefined_setter"), "global object should now have a setter named test10_undefined_setter");
  181. assert.isFalse(globalObject.hasOwnProperty("test10_null_setter"), "global object should now have a setter named test10_null_setter");
  182. }
  183. },
  184. test11: {
  185. name: "__lookupGetter__ and __lookupSetter__ find getters and setters of the given name on the calling object respectively",
  186. body: function () {
  187. var o = {
  188. get a() { return undefined; },
  189. set b(v) { },
  190. };
  191. var a = Object.getOwnPropertyDescriptor(o, "a").get;
  192. var b = Object.getOwnPropertyDescriptor(o, "b").set;
  193. var f = o.__lookupGetter__("a");
  194. assert.isTrue(f !== undefined, "__lookupGetter__ should have returned a value");
  195. assert.isTrue(typeof f === "function", "That value should be a function");
  196. assert.isTrue(f === a, "And it should be the same function returned by Object.getOwnPropertyDescriptor");
  197. f = o.__lookupSetter__("b");
  198. assert.isTrue(f !== undefined, "__lookupSetter__ should have returned a value");
  199. assert.isTrue(typeof f === "function", "That value should be a function");
  200. assert.isTrue(f === b, "And it should be the same function returned by Object.getOwnPropertyDescriptor");
  201. }
  202. },
  203. test12: {
  204. name: "__lookupGetter__ and __lookupSetter__ should look for accessors up the prototype chain",
  205. body: function () {
  206. var a = function () { return undefined; };
  207. var b = function (v) { };
  208. function Foo () { }
  209. Object.defineProperty(Foo.prototype, "a", { get: a });
  210. Object.defineProperty(Foo.prototype, "b", { set: b });
  211. var o = new Foo();
  212. var f = o.__lookupGetter__("a");
  213. assert.isTrue(f !== undefined, "__lookupGetter__ should have returned a value");
  214. assert.isTrue(typeof f === "function", "That value should be a function");
  215. assert.isTrue(f === a, "And it should be the same function as the defined getter");
  216. f = o.__lookupSetter__("b");
  217. assert.isTrue(f !== undefined, "__lookupSetter__ should have returned a value");
  218. assert.isTrue(typeof f === "function", "That value should be a function");
  219. assert.isTrue(f === b, "And it should be the same function as the defined setter");
  220. }
  221. },
  222. test13: {
  223. name: "__lookupGetter__ and __lookupSetter__ should look for accessors up the prototype chain",
  224. body: function () {
  225. var getfn = function () { return undefined; };
  226. var setfn = function (v) { };
  227. function Foo () { }
  228. Object.defineProperty(Foo.prototype, "geta", { get: getfn });
  229. Object.defineProperty(Foo.prototype, "getb", { get: getfn });
  230. Object.defineProperty(Foo.prototype, "seta", { set: setfn });
  231. Object.defineProperty(Foo.prototype, "setb", { set: setfn });
  232. var o = new Foo();
  233. Object.defineProperty(o, "geta", { set: setfn, configurable: true, enumerable: true });
  234. Object.defineProperty(o, "getb", { value: 123, configurable: true, enumerable: true, writable: true });
  235. Object.defineProperty(o, "seta", { get: getfn, configurable: true, enumerable: true });
  236. Object.defineProperty(o, "setb", { value: 123, configurable: true, enumerable: true, writable: true });
  237. WScript.Echo(o.__lookupGetter__("geta"));
  238. assert.isTrue(o.__lookupGetter__("geta") === undefined, "accessor property on o shadows accessor property on prototype but it is set-only so looking up a getter should return undefined");
  239. assert.isTrue(o.__lookupGetter__("getb") === getfn, "data property on o shadows accessor property on prototype but __lookupGetter__ looks for the first accessor property, skipping all others, so should return getfn");
  240. assert.isTrue(o.__lookupSetter__("seta") === undefined, "accessor property on o shadows accessor property on prototype but it is get-only so looking up a setter should return undefined");
  241. assert.isTrue(o.__lookupSetter__("setb") === setfn, "data property on o shadows accessor property on prototype but __lookupGetter__ looks for the first accessor property, skipping all others, so should return setfn");
  242. }
  243. },
  244. test14: {
  245. name: "__defineGetter__ and __defineSetter__ should throw TypeError when the object specified as getter/setter is not callable",
  246. body: function () {
  247. assert.throws(() => { __defineGetter__.call(this, 'x', 23); }, TypeError, "__defineGetter__ should throw TypeError when the function object is not callable.");
  248. assert.throws(() => { this.__defineSetter__('y', {}); }, TypeError, "__defineGetter__ should throw TypeError when the function object is not callable.");
  249. }
  250. },
  251. };
  252. testRunner.runTests(tests);