funcAndboundFuncLength.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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. function lengthDefaultState(func, paramCount, identifier)
  7. {
  8. "use strict";
  9. const descriptor = Object.getOwnPropertyDescriptor(func, "length");
  10. assert.isTrue(descriptor.configurable, identifier + " length is configurable");
  11. assert.isFalse(descriptor.writable, identifier + " length is not writeable");
  12. assert.isFalse(descriptor.enumerable, identifier + " length is not enumerable");
  13. assert.isTrue(func.hasOwnProperty("length"), identifier + " should have length property");
  14. assert.areEqual(func.length, paramCount, identifier + " length property should default to number of parameters");
  15. assert.throws(()=>{func.length = 5;}, TypeError, "Writing to " + identifier + " length should throw type error");
  16. }
  17. function deleteLength(func, identifier)
  18. {
  19. "use strict";
  20. assert.doesNotThrow(()=>{delete func.length}, "Deleting " + identifier + ".length should not throw");
  21. assert.isTrue(!func.hasOwnProperty("length"), "Deleting " + identifier + ".length should succeed");
  22. assert.areEqual(0, func.length, identifier + ".length once deleted should return 0 i.e. prototype value");
  23. assert.throws(()=>{func.length = 5}, TypeError, "Attempting to write to " + identifier + " deleted length should throw in strict mode");
  24. assert.isTrue(!func.hasOwnProperty("length"), "recreating deleted " + identifier + ".length fails");
  25. assert.areEqual(0, func.length, identifier + ".length once deleted should return 0 i.e. prototype value");
  26. reDefineLengthStrict(func, identifier);
  27. }
  28. function reDefineLengthStrict(func, identifier)
  29. {
  30. "use strict";
  31. const initialValue = func.length;
  32. assert.throws(()=>{func.length = initialValue - 1}, TypeError, "Writing to " + identifier + ".length throws in strict mode");
  33. assert.areEqual(initialValue, func.length, "Failed attempt to write to " + identifier + ".length does not change it's value");
  34. Object.defineProperty(func, "length", {enumerable : true, writable : true, value : initialValue + 1});
  35. const descriptor = Object.getOwnPropertyDescriptor(func, "length");
  36. assert.isTrue(descriptor.writable, identifier + " after redefinition length can be writeable");
  37. assert.isTrue(descriptor.enumerable, identifier + " after redefinition length can be enumerable");
  38. assert.areEqual(initialValue + 1, func.length, identifier + ".length after redefinition takes new value");
  39. func.length = initialValue - 1;
  40. assert.areEqual(initialValue - 1, func.length, identifier + ".length after redefinition can be writeable");
  41. }
  42. function reDefineLength(func, identifier)
  43. {
  44. const initialValue = func.length;
  45. assert.doesNotThrow(()=>{func.length = initialValue - 1}, "Writing to " + identifier + ".length does not throw when not in strict mode");
  46. assert.areEqual(initialValue, func.length, "Failed attempt to write to " + identifier + ".length does not change it's value");
  47. Object.defineProperty(func, "length", {enumerable : true, writable : true, value : initialValue + 1});
  48. const descriptor = Object.getOwnPropertyDescriptor(func, "length");
  49. assert.isTrue(descriptor.writable, identifier + " after redefinition length can be writeable");
  50. assert.isTrue(descriptor.enumerable, identifier + " after redefinition length can be enumerable");
  51. assert.areEqual(initialValue + 1, func.length, identifier + ".length after redefinition takes new value");
  52. func.length = initialValue - 1;
  53. assert.areEqual(initialValue - 1, func.length, identifier + ".length after redefinition can be writeable");
  54. }
  55. const tests = [
  56. {
  57. name : "function.length default state",
  58. body : function()
  59. {
  60. function normalFunction (a, b) { }
  61. const anonymousFunction = function (a, b, c) { }
  62. const lambda = (a, b, c, d) => { }
  63. const anonGen = function* (a, b) {}
  64. function* genFunc () {}
  65. async function asyncFunc (a) {}
  66. const anonAsync = async function () { }
  67. lengthDefaultState(normalFunction, 2, "function");
  68. lengthDefaultState(anonymousFunction, 3, "Anonymous function");
  69. lengthDefaultState(lambda, 4, "Lambda function");
  70. lengthDefaultState(anonGen, 2, "Anonymous generator");
  71. lengthDefaultState(genFunc, 0, "Generator function");
  72. lengthDefaultState(anonAsync, 0, "Anonymous async function");
  73. lengthDefaultState(asyncFunc, 1, "Async function");
  74. deleteLength(normalFunction, "function");
  75. deleteLength(anonymousFunction, "Anonymous function");
  76. deleteLength(lambda, "Lambda function");
  77. deleteLength(anonGen, "Anonymous generator");
  78. deleteLength(genFunc, "Generator function");
  79. deleteLength(anonAsync, "Anonymous async function");
  80. deleteLength(asyncFunc, "Async function");
  81. }
  82. },
  83. {
  84. name : "Redefining function.length with defineProperty",
  85. body : function()
  86. {
  87. function normalFunction (a, b) { }
  88. const anonymousFunction = function (a, b, c) { }
  89. const lambda = (a, b, c, d) => { }
  90. const anonGen = function* (a, b) {}
  91. function* genFunc () {}
  92. async function asyncFunc (a) {}
  93. const anonAsync = async function () { }
  94. reDefineLength(normalFunction, "function");
  95. reDefineLength(anonymousFunction, "Anonymous function");
  96. reDefineLength(lambda, "Lambda function");
  97. reDefineLength(anonGen, "Lambda function");
  98. reDefineLength(genFunc, "Lambda function");
  99. reDefineLength(asyncFunc, "Lambda function");
  100. reDefineLength(anonAsync, "Lambda function");
  101. }
  102. },
  103. {
  104. name : "bound function.length default state",
  105. body : function()
  106. {
  107. function normalFunction (a, b) { }
  108. const anonymousFunction = function (a, b, c) { }
  109. const lambda = (a, b, c, d) => { }
  110. function* genFunc (a, b, c, d, e) {}
  111. async function asyncFunc (a, b) {}
  112. const boundNormal = normalFunction.bind({}, 1);
  113. const boundAnon = anonymousFunction.bind({}, 1, 1, 1, 1);
  114. const boundLambda = lambda.bind({}, 1, 1);
  115. const boundGen = genFunc.bind({}, 1, 1, 1, 1);
  116. const boundAsync = asyncFunc.bind({}, 1);
  117. lengthDefaultState(boundNormal, 1, "Bound Function");
  118. lengthDefaultState(boundAnon, 0, "Anonymous Bound Function");
  119. lengthDefaultState(boundLambda, 2, "Bound Lambda Function");
  120. lengthDefaultState(boundGen, 1, "Bound Generator Function");
  121. lengthDefaultState(boundAsync, 1, "Bound Async Function");
  122. deleteLength(boundNormal, "Bound Function");
  123. deleteLength(boundAnon, "Anonymous Bound Function");
  124. deleteLength(boundLambda, "Bound Lambda Function");
  125. deleteLength(boundGen, 1, "Bound Generator Function");
  126. deleteLength(boundAsync, 1, "Bound Async Function");
  127. }
  128. },
  129. {
  130. name : "Redefining boundFunction.length with defineProperty",
  131. body : function()
  132. {
  133. function normalFunction (a, b) { }
  134. const anonymousFunction = function (a, b, c) { }
  135. const lambda = (a, b, c, d) => { }
  136. const boundNormal = normalFunction.bind({}, 1);
  137. const boundAnon = anonymousFunction.bind({}, 1, 1, 1, 1);
  138. const boundLambda = lambda.bind({}, 1, 1);
  139. reDefineLength(boundNormal, "Bound Function");
  140. reDefineLength(boundAnon, "Anonymous Bound Function");
  141. reDefineLength(boundLambda, "Bound Lambda Function");
  142. }
  143. },
  144. {
  145. name : "Lengths of different bound functions",
  146. body : function()
  147. {
  148. const targ = function(a, b) {};
  149. let testBound = targ.bind({});
  150. assert.areEqual(testBound.length, 2, "Bound function uses target function's length when created");
  151. Object.defineProperty(targ, "length", {value : 5, writable : true});
  152. testBound = targ.bind({});
  153. assert.areEqual(testBound.length, 5, "Bound function uses target function's length when created if different to arg count");
  154. testBound = targ.bind({}, 1, 2);
  155. assert.areEqual(testBound.length, 3, "Bound function deducts bound parameters from length when created");
  156. targ.length = 1;
  157. assert.areEqual(testBound.length, 3, "Bound function length does not change if target function length is changed");
  158. testBound = targ.bind({}, 1, 2);
  159. assert.areEqual(testBound.length, 0, "Bound function length will default to 0 if it would be negative");
  160. delete targ.length;
  161. testBound = targ.bind({});
  162. assert.areEqual(testBound.length, 0, "Bound function length will default to 0 if target function has no length property");
  163. }
  164. }
  165. ];
  166. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });