async-generator-apis.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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. // ES2018 Async Generator API tests -- verifies built-in API objects and properties
  7. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  8. const globObj = this;
  9. function checkAttributes(name, o, p, a) {
  10. const desc = Object.getOwnPropertyDescriptor(o, p);
  11. const msgPrefix = "Property " + p.toString() + " on " + name + " is ";
  12. assert.isTrue(!!desc, msgPrefix + "not found; there is no descriptor");
  13. assert.areEqual(a.writable, desc.writable, msgPrefix + (a.writable ? "" : "not") + " writable");
  14. assert.areEqual(a.enumerable, desc.enumerable, msgPrefix + (a.enumerable ? "" : "not") + " enumerable");
  15. assert.areEqual(a.configurable, desc.configurable, msgPrefix + (a.configurable ? "" : "not") + " configurable");
  16. }
  17. function testMethod(object, name, objectName, methodName = name) {
  18. assert.isTrue(name in object, `${objectName} should have property ${name}`);
  19. const method = object[name];
  20. assert.areEqual(methodName, method.name, `${objectName}.${name}.name should equal ${methodName}`);
  21. assert.areEqual('function', typeof method, `${objectName}.${name} should be a function`);
  22. assert.isTrue(method.toString().includes("[native code]"), `${objectName}.${name} should be a tagged as [native code]`);
  23. }
  24. const tests = [
  25. {
  26. name: "Async Generator function with overwritten prototype",
  27. body: function () {
  28. async function* agf() {};
  29. var gfp = agf.prototype;
  30. assert.strictEqual(agf().__proto__, gfp, "Async Generator function uses prototype.");
  31. var obj = {};
  32. agf.prototype = obj;
  33. assert.strictEqual(agf().__proto__, obj, "Async Generator function uses overwritten prototype.");
  34. agf.prototype = 1;
  35. assert.areEqual(agf().__proto__.toString(), gfp.toString(), "Generator function falls back to original prototype.");
  36. if (agf().__proto__ === gfp) { assert.error("Original prototype should not be same object as gfp")}
  37. var originalGfp = agf().__proto__;
  38. assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype.");
  39. agf.prototype = 0;
  40. assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype.");
  41. agf.prototype = "hi";
  42. assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype.");
  43. delete gfp.prototype;
  44. assert.strictEqual(agf().__proto__, originalGfp, "Async Generator function falls back to original prototype.");
  45. }
  46. },
  47. {
  48. name: "AsyncGeneratorFunction is not exposed on the global object",
  49. body: function () {
  50. assert.isFalse(globObj.hasOwnProperty("AsyncGeneratorFunction"), "Global object does not have property named AsyncGeneratorFunction");
  51. }
  52. },
  53. {
  54. name: "Async generator function instances have length and name properties",
  55. body: function () {
  56. async function* agf () {}
  57. assert.isTrue(agf.hasOwnProperty("length"), "Async generator function objects have a 'length' property");
  58. assert.isTrue(agf.hasOwnProperty("name"), "Async generator function objects have a 'name' property");
  59. checkAttributes("agf", agf, "length", { writable: false, enumerable: false, configurable: true });
  60. checkAttributes("agf", agf, "name", { writable: false, enumerable: false, configurable: true });
  61. assert.areEqual(0, agf.length, "Async generator function object's 'length' property matches the number of parameters (0)");
  62. assert.areEqual("agf", agf.name, "Async generator function object's 'name' property matches the function's name");
  63. async function* agf2(a, b, c) { }
  64. assert.areEqual(3, agf2.length, "Async generator function object's 'length' property matches the number of parameters (3)");
  65. }
  66. },
  67. {
  68. name: "Async generator functions are not constructors",
  69. body: function () {
  70. async function* agf () {}
  71. assert.throws(function () { new agf(); }, TypeError, "Async functions cannot be used as constructors", "Function is not a constructor");
  72. }
  73. },
  74. {
  75. name: "Async generator functions do not have arguments nor caller properties regardless of strictness",
  76. body: function () {
  77. async function* agf () {}
  78. assert.isFalse(agf.hasOwnProperty("arguments"), "Async function objects do not have an 'arguments' property");
  79. assert.isFalse(agf.hasOwnProperty("caller"), "Async function objects do not have a 'caller' property");
  80. // Test JavascriptFunction APIs that special case PropertyIds::caller and ::arguments
  81. Object.setPrototypeOf(agf, Object.prototype); // Remove Function.prototype so we don't find its 'caller' and 'arguments' in these operations
  82. assert.isFalse("arguments" in agf, "Has operation on 'arguments' property returns false initially");
  83. assert.areEqual(undefined, agf.arguments, "Get operation on 'arguments' property returns undefined initially");
  84. assert.areEqual(undefined, Object.getOwnPropertyDescriptor(agf, "arguments"), "No property descriptor for 'arguments' initially");
  85. assert.isTrue(delete agf.arguments, "Delete operation on 'arguments' property returns true");
  86. assert.areEqual(0, agf.arguments = 0, "Set operation on 'arguments' creates new property with assigned value");
  87. assert.isTrue("arguments" in agf, "Has operation on 'arguments' property returns true now");
  88. assert.areEqual(0, agf.arguments, "Get operation on 'arguments' property returns property value now");
  89. checkAttributes("agf", agf, "arguments", { writable: true, enumerable: true, configurable: true });
  90. assert.isTrue(delete agf.arguments, "Delete operation on 'arguments' property still returns true");
  91. assert.isFalse(agf.hasOwnProperty("arguments"), "'arguments' property is gone");
  92. assert.isFalse("caller" in agf, "Has operation on 'caller' property returns false initially");
  93. assert.areEqual(undefined, agf.caller, "Get operation on 'caller' property returns undefined initially");
  94. assert.areEqual(undefined, Object.getOwnPropertyDescriptor(agf, "caller"), "No property descriptor for 'caller' initially");
  95. assert.isTrue(delete agf.caller, "Delete operation on 'caller' property returns true");
  96. assert.areEqual(0, agf.caller = 0, "Set operation on 'caller' creates new property with assigned value");
  97. assert.isTrue("caller" in agf, "Has operation on 'caller' property returns true now");
  98. assert.areEqual(0, agf.caller, "Get operation on 'caller' property returns property value now");
  99. checkAttributes("agf", agf, "caller", { writable: true, enumerable: true, configurable: true });
  100. assert.isTrue(delete agf.caller, "Delete operation on 'caller' property still returns true");
  101. assert.isFalse(agf.hasOwnProperty("caller"), "'caller' property is gone");
  102. async function* agfstrict() { "use strict"; }
  103. assert.isFalse(agfstrict.hasOwnProperty("arguments"), "Strict mode async function objects do not have an 'arguments' property");
  104. assert.isFalse(agfstrict.hasOwnProperty("caller"), "Strict mode async function objects do not have a 'caller' property");
  105. Object.setPrototypeOf(agfstrict, Object.prototype); // Remove Function.prototype so we don't find its 'caller' and 'arguments' in these operations
  106. assert.isFalse("arguments" in agfstrict, "Has operation on 'arguments' property returns false initially for a strict mode async function");
  107. assert.areEqual(undefined, agfstrict.arguments, "Get operation on 'arguments' property returns undefined initially for a strict mode async function");
  108. assert.areEqual(undefined, Object.getOwnPropertyDescriptor(agfstrict, "arguments"), "No property descriptor for 'arguments' initially for a strict mode async function");
  109. assert.isTrue(delete agfstrict.arguments, "Delete operation on 'arguments' property returns true initially for a strict mode async function");
  110. assert.areEqual(0, agfstrict.arguments = 0, "Set operation on 'arguments' creates new property with assigned value for a strict mode async function");
  111. assert.isTrue("arguments" in agfstrict, "Has operation on 'arguments' property returns true now for a strict mode async function");
  112. assert.areEqual(0, agfstrict.arguments, "Get operation on 'arguments' property returns property value now for a strict mode async function");
  113. checkAttributes("agfstrict", agfstrict, "arguments", { writable: true, enumerable: true, configurable: true });
  114. assert.isTrue(delete agfstrict.arguments, "Delete operation on 'arguments' property still returns true for a strict mode async function");
  115. assert.isFalse(agfstrict.hasOwnProperty("arguments"), "'arguments' property is gone for a strict mode async function");
  116. assert.isFalse("caller" in agfstrict, "Has operation on 'caller' property returns false initially for a strict mode async function");
  117. assert.areEqual(undefined, agfstrict.caller, "Get operation on 'caller' property returns undefined initially for a strict mode async function");
  118. assert.areEqual(undefined, Object.getOwnPropertyDescriptor(agfstrict, "caller"), "No property descriptor for 'caller' initially for a strict mode async function");
  119. assert.isTrue(delete agfstrict.caller, "Delete operation on 'caller' property returns true initially for a strict mode async function");
  120. assert.areEqual(0, agfstrict.caller = 0, "Set operation on 'caller' creates new property with assigned value for a strict mode async function");
  121. assert.isTrue("caller" in agfstrict, "Has operation on 'caller' property returns true now for a strict mode async function");
  122. assert.areEqual(0, agfstrict.caller, "Get operation on 'caller' property returns property value now for a strict mode async function");
  123. checkAttributes("agfstrict", agfstrict, "caller", { writable: true, enumerable: true, configurable: true });
  124. assert.isTrue(delete agfstrict.caller, "Delete operation on 'caller' property still returns true for a strict mode async function");
  125. assert.isFalse(agfstrict.hasOwnProperty("caller"), "'caller' property is gone for a strict mode async function");
  126. }
  127. },
  128. {
  129. name: "Async Generator function instances have %AsyncGeneratorFunctionPrototype% as their prototype and it has the correct properties and prototype",
  130. body: function () {
  131. async function* agf () {}
  132. const asyncGeneratorFunctionPrototype = Object.getPrototypeOf(agf);
  133. assert.areEqual(Function.prototype, Object.getPrototypeOf(asyncGeneratorFunctionPrototype), "%AsyncGeneratorFunctionPrototype%'s prototype is Function.prototype");
  134. assert.isTrue(asyncGeneratorFunctionPrototype.hasOwnProperty("constructor"), "%AsyncGeneratorFunctionPrototype% has 'constructor' property");
  135. assert.isTrue(asyncGeneratorFunctionPrototype.hasOwnProperty(Symbol.toStringTag), "%AsyncGeneratorFunctionPrototype% has [Symbol.toStringTag] property");
  136. checkAttributes("%AsyncGeneratorFunctionPrototype%", asyncGeneratorFunctionPrototype, "constructor", { writable: false, enumerable: false, configurable: true });
  137. checkAttributes("%AsyncGeneratorFunctionPrototype%", asyncGeneratorFunctionPrototype, Symbol.toStringTag, { writable: false, enumerable: false, configurable: true });
  138. assert.areEqual("AsyncGeneratorFunction", asyncGeneratorFunctionPrototype[Symbol.toStringTag], "%AsyncGeneratorFunctionPrototype%'s [Symbol.toStringTag] property is 'asyncGeneratorFunction'");
  139. assert.isTrue(asyncGeneratorFunctionPrototype.hasOwnProperty("prototype"), "%AsyncGeneratorFunctionPrototype% has a 'prototype' property");
  140. }
  141. },
  142. {
  143. name : "Async Generator function instances have the correct prototype object",
  144. body () {
  145. async function* agf () {}
  146. const prototype = agf.prototype;
  147. testMethod(prototype, "next", "AsyncGenerator instance prototype");
  148. testMethod(prototype, "throw", "AsyncGenerator instance prototype");
  149. testMethod(prototype, "return", "AsyncGenerator instance prototype")
  150. assert.isTrue('constructor' in prototype, "AsyncGenerator instance prototype has prototype propert");
  151. assert.areEqual('', prototype.constructor.name, "AsyncGenerator instance prototype constructor property is anonymous");
  152. }
  153. },
  154. {
  155. name: "%AsyncGeneratorFunction% constructor is the value of the constructor property of %AsyncGeneratorFunctionPrototype% and has the specified properties and prototype",
  156. body: function () {
  157. async function* agf () {}
  158. const asyncGeneratorFunctionPrototype = Object.getPrototypeOf(agf);
  159. const asyncGeneratorFunctionConstructor = asyncGeneratorFunctionPrototype.constructor;
  160. const asyncGeneratorPrototype = asyncGeneratorFunctionConstructor.prototype.prototype;
  161. assert.areEqual(Function, Object.getPrototypeOf(asyncGeneratorFunctionConstructor), "%AsyncGeneratorFunction%'s prototype is Function");
  162. assert.isTrue(asyncGeneratorFunctionConstructor.hasOwnProperty("name"), "%AsyncGeneratorFunction% has 'name' property");
  163. assert.isTrue(asyncGeneratorFunctionConstructor.hasOwnProperty("length"), "%AsyncGeneratorFunction% has 'length' property");
  164. assert.isTrue(asyncGeneratorFunctionConstructor.hasOwnProperty("prototype"), "%AsyncGeneratorFunction% has 'prototype' property");
  165. checkAttributes("%AsyncGeneratorFunction%", asyncGeneratorFunctionConstructor, "name", { writable: false, enumerable: false, configurable: true });
  166. checkAttributes("%AsyncGeneratorFunction%", asyncGeneratorFunctionConstructor, "length", { writable: false, enumerable: false, configurable: true });
  167. checkAttributes("%AsyncGeneratorFunction%", asyncGeneratorFunctionConstructor, "prototype", { writable: false, enumerable: false, configurable: false });
  168. testMethod(asyncGeneratorPrototype, "next", "%AsyncGeneratorPrototype%");
  169. testMethod(asyncGeneratorPrototype, "throw", "%AsyncGeneratorPrototype%");
  170. testMethod(asyncGeneratorPrototype, "return", "%AsyncGeneratorPrototype%");
  171. //print(asyncGeneratorPrototype.constructor);
  172. assert.areEqual("AsyncGeneratorFunction", asyncGeneratorFunctionConstructor.name, "%AsyncGeneratorFunction%'s 'name' property is 'AsyncGeneratorFunction'");
  173. assert.areEqual(asyncGeneratorFunctionPrototype, asyncGeneratorFunctionConstructor.prototype, "%AsyncGeneratorFunction%'s 'prototype' property is %AsyncGeneratorFunction%.prototype");
  174. assert.areEqual(1, asyncGeneratorFunctionConstructor.length, "%AsyncGeneratorFunction%'s 'length' property is 1");
  175. }
  176. },
  177. {
  178. name : "%AsyncGenerator% object shape",
  179. body () {
  180. async function* agf () {}
  181. const asyncGenerator = agf();
  182. testMethod(asyncGenerator, "next", "%AsyncGeneratorPrototype%");
  183. testMethod(asyncGenerator, "throw", "%AsyncGeneratorPrototype%");
  184. testMethod(asyncGenerator, "return", "%AsyncGeneratorPrototype%");
  185. }
  186. },
  187. {
  188. name: "Anonymous Async Generator function",
  189. body: function () {
  190. const asyncGeneratorFunctionPrototype = Object.getPrototypeOf(async function* () { });
  191. const asyncGeneratorFunction = asyncGeneratorFunctionPrototype.constructor;
  192. let agf = new asyncGeneratorFunction('return await 1;');
  193. assert.areEqual(asyncGeneratorFunctionPrototype, Object.getPrototypeOf(agf), "Async function created by %AsyncGeneratorFunction% should have the same prototype as syntax declared async functions");
  194. assert.areEqual("anonymous", agf.name, "asyncGeneratorFunction constructed async function's name is 'anonymous'");
  195. assert.areEqual("async function* anonymous(\n) {return await 1;\n}", agf.toString(), "toString of asyncGeneratorFunction constructed function is named 'anonymous'");
  196. agf = new asyncGeneratorFunction('a', 'b', 'c', 'await a; await b; await c;');
  197. assert.areEqual("async function* anonymous(a,b,c\n) {await a; await b; await c;\n}", agf.toString(), "toString of asyncGeneratorFunction constructed function is named 'anonymous' with specified parameters");
  198. }
  199. },
  200. {
  201. name: "Other forms of Async Generator",
  202. body: function () {
  203. const obj = {
  204. async *oagf() {}
  205. }
  206. class cla {
  207. async *cagf() {}
  208. static async *scagf() {}
  209. }
  210. const instance = new cla();
  211. const asyncGeneratorFunctionPrototype = Object.getPrototypeOf(async function* () { });
  212. assert.areEqual(asyncGeneratorFunctionPrototype, Object.getPrototypeOf(instance.cagf), "Async generator class method should have the same prototype as async generator function");
  213. assert.areEqual(asyncGeneratorFunctionPrototype, Object.getPrototypeOf(cla.scagf), "Async generator class static method should have the same prototype as async generator function");
  214. assert.areEqual(asyncGeneratorFunctionPrototype, Object.getPrototypeOf(obj.oagf), "Async generator object method should have the same prototype as async generator function");
  215. }
  216. },
  217. {
  218. name: "Attempt to use Generator methods with AsyncGenerator",
  219. body() {
  220. function* gf () {}
  221. async function* agf() {}
  222. const ag = agf();
  223. const g = gf();
  224. assert.throws(()=>{g.next.call(ag)}, TypeError, "Generator.prototype.next should throw TypeError when called on an Async Generator");
  225. assert.throws(()=>{g.throw.call(ag)}, TypeError, "Generator.prototype.throw should throw TypeError when called on an Async Generator");
  226. assert.throws(()=>{g.return.call(ag)}, TypeError, "Generator.prototype.return should throw TypeError when called on an Async Generator");
  227. }
  228. }
  229. ];
  230. // Cannot easily verify behavior of async generator functions in conjunction with UnitTestFramework.js
  231. // due to callback nature of their execution. Instead, verification of behavior is
  232. // found in async-generator-functionality.js test.
  233. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });