es6HasInstance.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  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 Function unit tests
  6. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  7. var tests = [
  8. {
  9. name: "change Symbol.hasInstance property of a normal function constructor",
  10. body: function() {
  11. var F = function (a, b) {
  12. this.x = a;
  13. this.y = b;
  14. }
  15. var checked = 0;
  16. Object.defineProperty(F, Symbol.hasInstance, {
  17. value: function () {
  18. checked++;
  19. return true;
  20. }
  21. });
  22. assert.areEqual(true, undefined instanceof F, "undefined instanceof F");
  23. assert.areEqual(1, checked, "Symbol.hasInstance in a normal function contructor - checked==1");
  24. assert.areEqual(true, null instanceof F, "null instanceof F");
  25. assert.areEqual(2, checked, "Symbol.hasInstance in a normal function contructor - checked==2");
  26. assert.areEqual(true, true instanceof F, "true instanceof F");
  27. assert.areEqual(3, checked, "Symbol.hasInstance in a normal function contructor - checked==3");
  28. assert.areEqual(true, false instanceof F, "false instanceof F");
  29. assert.areEqual(4, checked, "Symbol.hasInstance in a normal function contructor - checked==4");
  30. assert.areEqual(true, 0 instanceof F, "0 instanceof F");
  31. assert.areEqual(5, checked, "Symbol.hasInstance in a normal function contructor - checked==5");
  32. assert.areEqual(true, 1.5e16 instanceof F, "1.5e16 instanceof F");
  33. assert.areEqual(6, checked, "Symbol.hasInstance in a normal function contructor - checked==6");
  34. assert.areEqual(true, NaN instanceof F, "NaN instanceof F");
  35. assert.areEqual(7, checked, "Symbol.hasInstance in a normal function contructor - checked==7");
  36. assert.areEqual(true, '' instanceof F, "'' instanceof F");
  37. assert.areEqual(8, checked, "Symbol.hasInstance in a normal function contructor - checked==8");
  38. assert.areEqual(true, 'abc' instanceof F, "'abc' instanceof F");
  39. assert.areEqual(9, checked, "Symbol.hasInstance in a normal function contructor - checked==9");
  40. assert.areEqual(true, {} instanceof F, "{} instanceof F");
  41. assert.areEqual(10, checked, "Symbol.hasInstance in a normal function contructor - checked==10");
  42. assert.areEqual(true, function(){} instanceof F, "function(){} instanceof F");
  43. assert.areEqual(11, checked, "Symbol.hasInstance in a normal function contructor - checked==11");
  44. }
  45. },
  46. {
  47. name: "change Symbol.hasInstance property of a non-function objects",
  48. body: function() {
  49. [
  50. {},
  51. {0:1,"length":1},
  52. [],
  53. [0,1,2],
  54. ['abc'],
  55. ].forEach(function(item) {
  56. testInstanceof(item);
  57. });
  58. function testInstanceof(O) {
  59. var checked = 0;
  60. var oldf = O[Symbol.hasInstance];
  61. O[Symbol.hasInstance] = function() {
  62. checked++;
  63. return true;
  64. };
  65. assertInstanceOf(O);
  66. O[Symbol.hasInstance] = oldf;
  67. checked = 0;
  68. var desc = Object.getOwnPropertyDescriptor(O, Symbol.hasInstance);
  69. Object.defineProperty(O, Symbol.hasInstance, {
  70. value: function () {
  71. checked++;
  72. return true;
  73. }
  74. });
  75. assertInstanceOf(O);
  76. Object.defineProperty(O, Symbol.hasInstance, desc);
  77. function assertInstanceOf(O) {
  78. assert.areEqual(true, undefined instanceof O, "undefined instanceof O");
  79. assert.areEqual(1, checked, "Symbol.hasInstance in a non-function object - checked==1");
  80. assert.areEqual(true, null instanceof O, "null instanceof O");
  81. assert.areEqual(2, checked, "Symbol.hasInstance in a non-function object - checked==2");
  82. assert.areEqual(true, true instanceof O, "true instanceof O");
  83. assert.areEqual(3, checked, "Symbol.hasInstance in a non-function object - checked==3");
  84. assert.areEqual(true, false instanceof O, "false instanceof O");
  85. assert.areEqual(4, checked, "Symbol.hasInstance in a non-function object - checked==4");
  86. assert.areEqual(true, 0 instanceof O, "0 instanceof O");
  87. assert.areEqual(5, checked, "Symbol.hasInstance in a non-function object - checked==5");
  88. assert.areEqual(true, 1.5e16 instanceof O, "1.5e16 instanceof O");
  89. assert.areEqual(6, checked, "Symbol.hasInstance in a non-function object - checked==6");
  90. assert.areEqual(true, NaN instanceof O, "NaN instanceof O");
  91. assert.areEqual(7, checked, "Symbol.hasInstance in a non-function object - checked==7");
  92. assert.areEqual(true, '' instanceof O, "'' instanceof O");
  93. assert.areEqual(8, checked, "Symbol.hasInstance in a non-function object - checked==8");
  94. assert.areEqual(true, 'abc' instanceof O, "'abc' instanceof O");
  95. assert.areEqual(9, checked, "Symbol.hasInstance in a non-function object - checked==9");
  96. assert.areEqual(true, {} instanceof O, "{} instanceof O");
  97. assert.areEqual(10, checked, "Symbol.hasInstance in a non-function object - checked==10");
  98. assert.areEqual(true, function(){} instanceof O, "function(){} instanceof O");
  99. assert.areEqual(11, checked, "Symbol.hasInstance in a non-function object - checked==11");
  100. }
  101. }
  102. }
  103. },
  104. {
  105. name: "change Symbol.hasInstance property of a bound function constructor",
  106. body: function() {
  107. var F = function (a, b) {
  108. this.x = a;
  109. this.y = b;
  110. }
  111. var BoundF = F.bind(1,2);
  112. var checked = 0;
  113. Object.defineProperty(F, Symbol.hasInstance, {
  114. value: function () {
  115. checked++;
  116. return true;
  117. }
  118. });
  119. assert.areEqual(true, BoundF instanceof F, "BoundF instanceof F");
  120. assert.areEqual(1, checked, "Symbol.hasInstance in a function contructor bound - checked==1");
  121. assert.areEqual(true, Object.create(BoundF) instanceof F, "Object.create(BoundF) instanceof f");
  122. assert.areEqual(2, checked, "Symbol.hasInstance in a function contructor bound - checked==2");
  123. assert.areEqual(true, new BoundF() instanceof F, "new BoundF() instanceof F");
  124. assert.areEqual(3, checked, "Symbol.hasInstance in a function contructor bound - checked==3");
  125. assert.areEqual(true, Object.create(F.prototype) instanceof BoundF, "Object.create(F.prototype) instanceof F");
  126. assert.areEqual(true, new F() instanceof BoundF, "instanceof f");
  127. }
  128. },
  129. {
  130. name: "change Symbol.hasInstance property of a proxy of function constructor",
  131. body: function() {
  132. function Foo() { };
  133. var checked = 0;
  134. var checkedString = [];
  135. Object.defineProperty(Foo, Symbol.hasInstance, {
  136. value: function () {
  137. checked++;
  138. return false;
  139. }
  140. });
  141. var ProxyFoo = new Proxy(Foo, {
  142. get : function (target, property){
  143. checkedString.push(property.toString());
  144. return Reflect.get(target, property);
  145. }
  146. });
  147. assert.areEqual(false, new ProxyFoo() instanceof ProxyFoo, "new ProxyFoo() instanceof ProxyFoo");
  148. assert.areEqual(1, checked, "Symbol.hasInstance in a function contructor through proxy - checked==1");
  149. assert.areEqual(['Symbol(Symbol.hasInstance)'], checkedString, "checkedString==['Symbol(Symbol.hasInstance)']");
  150. assert.areEqual(false, new ProxyFoo() instanceof Foo, "new ProxyFoo() instanceof Foo");
  151. assert.areEqual(2, checked, "Symbol.hasInstance in a function contructor through proxy - checked==2");
  152. }
  153. },
  154. {
  155. name: "change Symbol.hasInstance property of a bound proxy of function constructor",
  156. body: function() {
  157. function Foo() { };
  158. var checked = 0;
  159. var checkedString = [];
  160. Object.defineProperty(Foo, Symbol.hasInstance, {
  161. value: function () {
  162. checked++;
  163. return false;
  164. }
  165. });
  166. var ProxyFoo = new Proxy(Foo, {
  167. get : function (target, property){
  168. checkedString.push(property.toString());
  169. return Reflect.get(target, property);
  170. }
  171. });
  172. var BP = ProxyFoo.bind();
  173. assert.areEqual(false, BP instanceof ProxyFoo, "BP instanceof ProxyFoo");
  174. assert.areEqual(1, checked, "Symbol.hasInstance in a function contructor through bound proxy - checked==1");
  175. assert.areEqual(['bind','length','name','Symbol(Symbol.hasInstance)'], checkedString, "checkedString value");
  176. }
  177. },
  178. {
  179. name: "instanceof operator and default instOfHandler gets 'prototype' property of right-hand side",
  180. body: function() {
  181. [
  182. undefined,
  183. null,
  184. true,
  185. false,
  186. 'string',
  187. Symbol(),
  188. 0,
  189. ].forEach(function(item) {
  190. testInstanceof(item, function(){});
  191. });
  192. function testInstanceof(prototypeObj, O) {
  193. O.prototype = prototypeObj;
  194. assert.throws(()=>{({}) instanceof O}, TypeError, "({}) instanceof O", "Function does not have a valid prototype object");
  195. assert.throws(()=>{({0:1,"length":1}) instanceof O}, TypeError, "({0:1,\"length\":1}) instanceof O", "Function does not have a valid prototype object");
  196. assert.throws(()=>{[] instanceof O}, TypeError, "[] instanceof O", "Function does not have a valid prototype object");
  197. assert.throws(()=>{[0,1,2] instanceof O}, TypeError, "[0,1,2] instanceof O", "Function does not have a valid prototype object");
  198. assert.throws(()=>{['abc'] instanceof O}, TypeError, "['abc'] instanceof O", "Function does not have a valid prototype object");
  199. assert.throws(()=>{(function(){}) instanceof O}, TypeError, "(function(){}) instanceof O", "Function does not have a valid prototype object");
  200. assert.throws(()=>{O[Symbol.hasInstance]({})}, TypeError, "O[Symbol.hasInstance]({})", "Function does not have a valid prototype object");
  201. assert.throws(()=>{O[Symbol.hasInstance]({0:1,"length":1})}, TypeError, "O[Symbol.hasInstance]({0:1,\"length\":1})", "Function does not have a valid prototype object");
  202. assert.throws(()=>{O[Symbol.hasInstance]([])}, TypeError, "O[Symbol.hasInstance]([])", "Function does not have a valid prototype object");
  203. assert.throws(()=>{O[Symbol.hasInstance]([0,1,2])}, TypeError, "O[Symbol.hasInstance]([0,1,2])", "Function does not have a valid prototype object");
  204. assert.throws(()=>{O[Symbol.hasInstance](['abc'])}, TypeError, "O[Symbol.hasInstance](['abc'])", "Function does not have a valid prototype object");
  205. assert.throws(()=>{O[Symbol.hasInstance](function(){})}, TypeError, "O[Symbol.hasInstance](function(){})", "Function does not have a valid prototype object");
  206. }
  207. }
  208. },
  209. {
  210. name: "instanceof operator calling user-defined instOfHandler converts the return value to boolean using ToBoolean abstract operation",
  211. body: function() {
  212. [
  213. [function() { return undefined; }, false],
  214. [function() { return null; }, false],
  215. [function() { return NaN; }, false],
  216. [function() { return 1; }, true],
  217. [function() { return 0; }, false],
  218. [function() { return ''; }, false],
  219. [function() { return 'abc'; }, true],
  220. [function() { return Symbol(); }, true],
  221. [function() { return {}; }, true],
  222. ].forEach(function(item) {
  223. testInstanceof(item[0], item[1], {});
  224. testInstanceof(item[0], item[1], []);
  225. });
  226. function testInstanceof(instOfHandler, expected, O) {
  227. O[Symbol.hasInstance] = instOfHandler;
  228. assert.areEqual(expected, undefined instanceof O, "undefined instanceof O");
  229. assert.areEqual(expected, null instanceof O, "null instanceof O");
  230. assert.areEqual(expected, true instanceof O, "true instanceof O");
  231. assert.areEqual(expected, false instanceof O, "false instanceof O");
  232. assert.areEqual(expected, 0 instanceof O, "0 instanceof O");
  233. assert.areEqual(expected, 1.5e16 instanceof O, "1.5e16 instanceof O");
  234. assert.areEqual(expected, NaN instanceof O, "NaN instanceof O");
  235. assert.areEqual(expected, '' instanceof O, "'' instanceof O");
  236. assert.areEqual(expected, 'abc' instanceof O, "'abc' instanceof O");
  237. assert.areEqual(expected, {} instanceof O, "{} instanceof O");
  238. assert.areEqual(expected, function(){} instanceof O, "function(){} instanceof O");
  239. }
  240. }
  241. },
  242. {
  243. name: "instanceof operator calling OrdinaryHasInstance abstract operation returns false on non-object left-hand side values",
  244. body: function() {
  245. var F = function() {};
  246. [
  247. undefined,
  248. null,
  249. true,
  250. false,
  251. '',
  252. 'abc',
  253. Symbol(),
  254. Symbol('abc'),
  255. 0,
  256. 1.5e16,
  257. NaN,
  258. ].forEach(function(item) {
  259. assert.isFalse(item instanceof F, typeof(item) == ('symbol' ? 'Symbol' : item) + " instanceof F");
  260. });
  261. }
  262. },
  263. {
  264. name: "properties of Function.prototype[Symbol.hasInstance]",
  265. body: function() {
  266. var desc = Object.getOwnPropertyDescriptor(Function.prototype, Symbol.hasInstance);
  267. assert.areEqual(false, desc.enumerable, "protype:enumerable==false");
  268. assert.areEqual(false, desc.writable, "protype:writable==false");
  269. assert.areEqual(false, desc.configurable, "protype:configurable==false");
  270. var f = Function.prototype[Symbol.hasInstance];
  271. assert.areEqual(1, f.length, "Function.prototype[Symbol.hasInstance].length==1");
  272. desc = Object.getOwnPropertyDescriptor(f, 'length');
  273. assert.areEqual(false, desc.enumerable, "length:enumerable==false");
  274. assert.areEqual(false, desc.writable, "length:enumerable==false");
  275. assert.areEqual(true, desc.configurable, "length:enumerable==true");
  276. assert.areEqual('[Symbol.hasInstance]', f.name, "Function.prototype[Symbol.hasInstance].name");
  277. desc = Object.getOwnPropertyDescriptor(f, 'name');
  278. assert.areEqual(false, desc.enumerable, "name:enumerable==false");
  279. assert.areEqual(false, desc.writable, "name:writable==false");
  280. assert.areEqual(true, desc.configurable, "name:configurable==true");
  281. assert.areEqual(false, f.call(), "Function.prototype[Symbol.hasInstance].call()");
  282. assert.areEqual(false, f.call({}), "Function.prototype[Symbol.hasInstance].call({})");
  283. }
  284. },
  285. {
  286. name: "instanceof operator on callable object invokes get on 'prototype' property",
  287. body: function() {
  288. // method 'F' has no 'prototype' property
  289. var F = Object.getOwnPropertyDescriptor({ get f() {} }, 'f').get;
  290. Object.defineProperty(F, 'prototype', {
  291. get: function() {
  292. throw new Error('Hit prototype');
  293. }
  294. });
  295. assert.throws(()=>{undefined instanceof F}, Error, "undefined instanceof F", 'Hit prototype');
  296. }
  297. },
  298. {
  299. name: "instanceof operator invokes [[getPrototypeOf]] internal method on left-hand side value",
  300. body: function() {
  301. var p = new Proxy({}, {
  302. getPrototypeOf: function() {
  303. throw new Error('Hit getPrototypeOf');
  304. }
  305. });
  306. var obj = Object.create(p);
  307. obj.prototype = {};
  308. var F = function() {};
  309. assert.throws(()=>{p instanceof F}, Error, "p instanceof F", 'Hit getPrototypeOf');
  310. assert.throws(()=>{obj instanceof F}, Error, "obj instanceof F", 'Hit getPrototypeOf');
  311. }
  312. },
  313. {
  314. name: "changing Symbol.hasIstance property on a function contructor invalidates inline cache",
  315. body: function() {
  316. var F = function() {}
  317. var changeHasInstance = function() {
  318. Object.defineProperty(F, Symbol.hasInstance, {
  319. value: function(inst) { return true; }
  320. });
  321. }
  322. function func() {
  323. return 0 instanceof F;
  324. }
  325. var changed = false;
  326. for (var i=0; i<100; i++) {
  327. var x = func();
  328. assert.areEqual(changed, x, "i=="+i);
  329. if (i==20) {
  330. changeHasInstance();
  331. changed = true;
  332. }
  333. }
  334. }
  335. },
  336. {
  337. name: "changing 'prototype' property on a function contructor invalidates inline cache",
  338. body: function() {
  339. var F = function() {}
  340. var changeHasInstance = function() {
  341. Object.defineProperty(F, 'prototype', {
  342. value: Function.prototype
  343. });
  344. }
  345. function func() {
  346. return function(){} instanceof F;
  347. }
  348. var changed = false;
  349. for (var i=0; i<100; i++) {
  350. var x = func();
  351. assert.areEqual(changed, x, "i=="+i);
  352. if (i==20) {
  353. changeHasInstance();
  354. changed = true;
  355. }
  356. }
  357. }
  358. },
  359. ];
  360. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });