forinnonenumerableshadowing.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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. // Tests for...in behavior when child object shadows a prototype property with a non-enumerable shadow
  6. // See OS bug #850013
  7. if (this.WScript && this.WScript.LoadScriptFile) { // Check for running in ch
  8. this.WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  9. }
  10. function forInKeysToArray(obj) {
  11. var s = [];
  12. for (key in obj) {
  13. s.push(key);
  14. }
  15. return s;
  16. }
  17. var tests = [
  18. {
  19. name: "Simple test of prototype property shadowed by non-enumerable property",
  20. body: function () {
  21. var proto = { x: 1 };
  22. var child = Object.create(proto, { x: { value: 2, enumerable: false} });
  23. var result = forInKeysToArray(child);
  24. assert.areEqual([], result, "for...in does not enumerate a key which is enumerable in a prototype but shadowed by a non-enumerable property");
  25. }
  26. },
  27. {
  28. name: "Multiple properties on an object with some prototype properties shadowed by non-enumerable versions",
  29. body: function () {
  30. var proto = { a: 1, b: 2, c: 3, d: 4, e: 5 };
  31. var child = Object.create(proto, { b: { value: 20, enumerable: false} });
  32. Object.defineProperty(child, 'c', { enumerable: false, value: 30 });
  33. child['d'] = 4;
  34. var result = forInKeysToArray(child);
  35. assert.areEqual(['d','a','e'], result, "for...in does not enumerate a key which is enumerable in a prototype but shadowed by a non-enumerable property");
  36. }
  37. },
  38. {
  39. name: "Array indices which are non-enumerable (force ES5Array object)",
  40. body: function () {
  41. var o = [0,1,2];
  42. o[4] = 4;
  43. Object.defineProperty(o, 3, { enumerable: false, value: '3' })
  44. var result = forInKeysToArray(o);
  45. assert.areEqual(['0','1','2','4'], result, "for...in does not enumerate non-enumerable properties, even for array indices");
  46. }
  47. },
  48. {
  49. name: "Explicitly test for...in fast path",
  50. body: function () {
  51. function test(obj, expected) {
  52. var result = forInKeysToArray(obj);
  53. result = result.concat(forInKeysToArray(obj));
  54. result = result.concat(forInKeysToArray(obj));
  55. assert.areEqual(expected, result, "for...in does not enumerate non-enumerable properties, even from the fast-path");
  56. }
  57. var o = Object.create(null);
  58. Object.defineProperty(o, 'a', { value: 1, enumerable: false });
  59. Object.defineProperty(o, 'b', { value: 2, enumerable: false });
  60. Object.defineProperty(o, 'c', { value: 3, enumerable: false });
  61. test(o, []);
  62. var o = Object.create(null);
  63. Object.defineProperty(o, 'a', { value: 1, enumerable: true });
  64. Object.defineProperty(o, 'b', { value: 2, enumerable: false });
  65. Object.defineProperty(o, 'c', { value: 3, enumerable: false });
  66. test(o, ['a','a','a']);
  67. var o = Object.create(null);
  68. Object.defineProperty(o, 'a', { value: 1, enumerable: false });
  69. Object.defineProperty(o, 'b', { value: 2, enumerable: false });
  70. Object.defineProperty(o, 'c', { value: 3, enumerable: true });
  71. test(o, ['c','c','c']);
  72. var o = Object.create(null);
  73. Object.defineProperty(o, 'a', { value: 1, enumerable: false });
  74. Object.defineProperty(o, 'b', { value: 2, enumerable: true });
  75. Object.defineProperty(o, 'c', { value: 3, enumerable: false });
  76. test(o, ['b','b','b']);
  77. var o = Object.create(null);
  78. Object.defineProperty(o, 'a', { value: 1, enumerable: true });
  79. Object.defineProperty(o, 'b', { value: 2, enumerable: false });
  80. Object.defineProperty(o, 'c', { value: 3, enumerable: true });
  81. test(o, ['a','c','a','c','a','c']);
  82. // JSON is a delay-load object
  83. test(JSON, []);
  84. }
  85. },
  86. {
  87. name: "Shadowing non-enumerable prototype property with an enumerable version",
  88. body: function () {
  89. var proto = Object.create(null, { x: { value: 1, enumerable: false} });
  90. var child = Object.create(proto, { x: { value: 2, enumerable: true} });
  91. var result = forInKeysToArray(child);
  92. assert.areEqual(['x'], result, "Child property shadows proto property");
  93. }
  94. },
  95. {
  96. name: "Shadowing non-enumerable prototype property with another non-enumerable version",
  97. body: function () {
  98. var proto = Object.create(null, { x: { value: 1, enumerable: false} });
  99. var child = Object.create(proto, { x: { value: 2, enumerable: false} });
  100. var result = forInKeysToArray(child);
  101. assert.areEqual([], result, "Child property shadows proto property with another non-enumerable property");
  102. }
  103. },
  104. {
  105. name: "Enumerating RegExp constructor is a bit of a special case",
  106. body: function() {
  107. var result = forInKeysToArray(RegExp);
  108. assert.areEqual(['$1','$2','$3','$4','$5','$6','$7','$8','$9','input','rightContext','leftContext','lastParen','lastMatch'], result, "for..in of RegExp constructor returns some special properties");
  109. var result = Object.keys(RegExp);
  110. assert.areEqual(['$1','$2','$3','$4','$5','$6','$7','$8','$9','input','rightContext','leftContext','lastParen','lastMatch'], result, "Object.keys returns the same set of properties for RegExp as for..in");
  111. var result = Object.getOwnPropertyNames(RegExp);
  112. assert.areEqual(['$1','$2','$3','$4','$5','$6','$7','$8','$9','input','rightContext','leftContext','lastParen','lastMatch','length','prototype','name','$_','$&','$+','$`',"$'",'index'], result, "Object.getOwnPropertyNames returns special non-enumerable properties too");
  113. }
  114. },
  115. {
  116. name: "Multiple objects in prototype chain with enum and non-enum property shadowing",
  117. body: function() {
  118. var proto = Object.create(null, {
  119. a: { value: 1, enumerable: false},
  120. b: { value: 1, enumerable: true},
  121. c: { value: 1, enumerable: false},
  122. d: { value: 1, enumerable: false},
  123. w: { value: 1, enumerable: true},
  124. x: { value: 1, enumerable: false},
  125. y: { value: 1, enumerable: true},
  126. z: { value: 1, enumerable: true},
  127. });
  128. var child = Object.create(proto, {
  129. a: { value: 2, enumerable: false},
  130. b: { value: 2, enumerable: false},
  131. c: { value: 2, enumerable: true},
  132. d: { value: 2, enumerable: false},
  133. w: { value: 2, enumerable: true},
  134. x: { value: 2, enumerable: true},
  135. y: { value: 2, enumerable: false},
  136. z: { value: 2, enumerable: true},
  137. });
  138. var childchild = Object.create(child, {
  139. a: { value: 3, enumerable: false},
  140. b: { value: 3, enumerable: false},
  141. c: { value: 3, enumerable: false},
  142. d: { value: 3, enumerable: true},
  143. w: { value: 3, enumerable: false},
  144. x: { value: 3, enumerable: true},
  145. y: { value: 3, enumerable: true},
  146. z: { value: 3, enumerable: true},
  147. });
  148. var result = forInKeysToArray(childchild);
  149. assert.areEqual(['d','x','y','z'], result, "childchild should shadow all properties and disable enumerable properties from the prototype chain leaking out");
  150. var result = forInKeysToArray(child);
  151. assert.areEqual(['c','w','x','z'], result, "child should shadow all properties and disable enumerable properties from the prototype chain leaking out");
  152. var result = forInKeysToArray(proto);
  153. assert.areEqual(['b','w','y','z'], result, "proto doesn't shadow any properties but non-enumerable properties should not show up in for..in loop");
  154. }
  155. },
  156. {
  157. name: "OS: 1905906 - Enumerating a type and alternating non-enumerable properties causes assert",
  158. body: function() {
  159. function foo() { JSON.stringify(arguments); }
  160. foo();
  161. var arr = [];
  162. function foo2() {
  163. for(var i in arguments) {
  164. arr.push(i);
  165. }
  166. }
  167. foo2('a','b');
  168. assert.areEqual(['0','1'], arr, "Correct values are enumerated via for...in loop");
  169. }
  170. },
  171. ];
  172. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });