Ben Murdoch | c561043 | 2016-08-08 18:44:38 +0100 | [diff] [blame] | 1 | // Copyright 2016 the V8 project authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | // Flags: --harmony-async-await --allow-natives-syntax |
| 6 | |
| 7 | // Do not install `AsyncFunction` constructor on global object |
| 8 | |
| 9 | function assertThrowsAsync(run, errorType, message) { |
| 10 | var actual; |
| 11 | var hadValue = false; |
| 12 | var hadError = false; |
| 13 | var promise = run(); |
| 14 | |
| 15 | if (typeof promise !== "object" || typeof promise.then !== "function") { |
| 16 | throw new MjsUnitAssertionError( |
| 17 | "Expected " + run.toString() + |
| 18 | " to return a Promise, but it returned " + PrettyPrint(promise)); |
| 19 | } |
| 20 | |
| 21 | promise.then(function(value) { hadValue = true; actual = value; }, |
| 22 | function(error) { hadError = true; actual = error; }); |
| 23 | |
| 24 | assertFalse(hadValue || hadError); |
| 25 | |
| 26 | %RunMicrotasks(); |
| 27 | |
| 28 | if (!hadError) { |
| 29 | throw new MjsUnitAssertionError( |
| 30 | "Expected " + run + "() to throw " + errorType.name + |
| 31 | ", but did not throw."); |
| 32 | } |
| 33 | if (!(actual instanceof errorType)) |
| 34 | throw new MjsUnitAssertionError( |
| 35 | "Expected " + run + "() to throw " + errorType.name + |
| 36 | ", but threw '" + actual + "'"); |
| 37 | if (message !== void 0 && actual.message !== message) |
| 38 | throw new MjsUnitAssertionError( |
| 39 | "Expected " + run + "() to throw '" + message + "', but threw '" + |
| 40 | actual.message + "'"); |
| 41 | }; |
| 42 | |
| 43 | function assertEqualsAsync(expected, run, msg) { |
| 44 | var actual; |
| 45 | var hadValue = false; |
| 46 | var hadError = false; |
| 47 | var promise = run(); |
| 48 | |
| 49 | if (typeof promise !== "object" || typeof promise.then !== "function") { |
| 50 | throw new MjsUnitAssertionError( |
| 51 | "Expected " + run.toString() + |
| 52 | " to return a Promise, but it returned " + PrettyPrint(promise)); |
| 53 | } |
| 54 | |
| 55 | promise.then(function(value) { hadValue = true; actual = value; }, |
| 56 | function(error) { hadError = true; actual = error; }); |
| 57 | |
| 58 | assertFalse(hadValue || hadError); |
| 59 | |
| 60 | %RunMicrotasks(); |
| 61 | |
| 62 | if (hadError) throw actual; |
| 63 | |
| 64 | assertTrue( |
| 65 | hadValue, "Expected '" + run.toString() + "' to produce a value"); |
| 66 | |
| 67 | assertEquals(expected, actual, msg); |
| 68 | }; |
| 69 | |
| 70 | assertEquals(undefined, this.AsyncFunction); |
| 71 | let AsyncFunction = (async function() {}).constructor; |
| 72 | |
| 73 | // Let functionPrototype be the intrinsic object %AsyncFunctionPrototype%. |
| 74 | async function asyncFunctionForProto() {} |
| 75 | assertEquals(AsyncFunction.prototype, |
| 76 | Object.getPrototypeOf(asyncFunctionForProto)); |
| 77 | assertEquals(AsyncFunction.prototype, |
| 78 | Object.getPrototypeOf(async function() {})); |
| 79 | assertEquals(AsyncFunction.prototype, Object.getPrototypeOf(async () => {})); |
| 80 | assertEquals(AsyncFunction.prototype, |
| 81 | Object.getPrototypeOf({ async method() {} }.method)); |
| 82 | assertEquals(AsyncFunction.prototype, Object.getPrototypeOf(AsyncFunction())); |
| 83 | assertEquals(AsyncFunction.prototype, |
| 84 | Object.getPrototypeOf(new AsyncFunction())); |
| 85 | |
| 86 | // AsyncFunctionCreate does not produce an object with a Prototype |
| 87 | assertEquals(undefined, asyncFunctionForProto.prototype); |
| 88 | assertEquals(false, asyncFunctionForProto.hasOwnProperty("prototype")); |
| 89 | assertEquals(undefined, (async function() {}).prototype); |
| 90 | assertEquals(false, (async function() {}).hasOwnProperty("prototype")); |
| 91 | assertEquals(undefined, (async() => {}).prototype); |
| 92 | assertEquals(false, (async() => {}).hasOwnProperty("prototype")); |
| 93 | assertEquals(undefined, ({ async method() {} }).method.prototype); |
| 94 | assertEquals(false, ({ async method() {} }).method.hasOwnProperty("prototype")); |
| 95 | assertEquals(undefined, AsyncFunction().prototype); |
| 96 | assertEquals(false, AsyncFunction().hasOwnProperty("prototype")); |
| 97 | assertEquals(undefined, (new AsyncFunction()).prototype); |
| 98 | assertEquals(false, (new AsyncFunction()).hasOwnProperty("prototype")); |
| 99 | |
| 100 | assertEquals(1, async function(a) { await 1; }.length); |
| 101 | assertEquals(2, async function(a, b) { await 1; }.length); |
| 102 | assertEquals(1, async function(a, b = 2) { await 1; }.length); |
| 103 | assertEquals(2, async function(a, b, ...c) { await 1; }.length); |
| 104 | |
| 105 | assertEquals(1, (async(a) => await 1).length); |
| 106 | assertEquals(2, (async(a, b) => await 1).length); |
| 107 | assertEquals(1, (async(a, b = 2) => await 1).length); |
| 108 | assertEquals(2, (async(a, b, ...c) => await 1).length); |
| 109 | |
| 110 | assertEquals(1, ({ async f(a) { await 1; } }).f.length); |
| 111 | assertEquals(2, ({ async f(a, b) { await 1; } }).f.length); |
| 112 | assertEquals(1, ({ async f(a, b = 2) { await 1; } }).f.length); |
| 113 | assertEquals(2, ({ async f(a, b, ...c) { await 1; } }).f.length); |
| 114 | |
| 115 | assertEquals(1, AsyncFunction("a", "await 1").length); |
| 116 | assertEquals(2, AsyncFunction("a", "b", "await 1").length); |
| 117 | assertEquals(1, AsyncFunction("a", "b = 2", "await 1").length); |
| 118 | assertEquals(2, AsyncFunction("a", "b", "...c", "await 1").length); |
| 119 | |
| 120 | assertEquals(1, (new AsyncFunction("a", "await 1")).length); |
| 121 | assertEquals(2, (new AsyncFunction("a", "b", "await 1")).length); |
| 122 | assertEquals(1, (new AsyncFunction("a", "b = 2", "await 1")).length); |
| 123 | assertEquals(2, (new AsyncFunction("a", "b", "...c", "await 1")).length); |
| 124 | |
| 125 | // AsyncFunction.prototype[ @@toStringTag ] |
| 126 | var descriptor = |
| 127 | Object.getOwnPropertyDescriptor(AsyncFunction.prototype, |
| 128 | Symbol.toStringTag); |
| 129 | assertEquals("AsyncFunction", descriptor.value); |
| 130 | assertEquals(false, descriptor.enumerable); |
| 131 | assertEquals(false, descriptor.writable); |
| 132 | assertEquals(true, descriptor.configurable); |
| 133 | |
| 134 | assertEquals(1, AsyncFunction.length); |
| 135 | |
| 136 | // Let F be ! FunctionAllocate(functionPrototype, Strict, "non-constructor") |
| 137 | async function asyncNonConstructorDecl() {} |
| 138 | assertThrows( |
| 139 | () => new asyncNonConstructorDecl(), TypeError); |
| 140 | assertThrows( |
| 141 | () => new (async function() {}), TypeError); |
| 142 | assertThrows( |
| 143 | () => new ({ async nonConstructor() {} }).nonConstructor(), TypeError); |
| 144 | assertThrows( |
| 145 | () => new (() => "not a constructor!"), TypeError); |
| 146 | assertThrows( |
| 147 | () => new (AsyncFunction()), TypeError); |
| 148 | assertThrows( |
| 149 | () => new (new AsyncFunction()), TypeError); |
| 150 | |
| 151 | // Normal completion |
| 152 | async function asyncDecl() { return "test"; } |
| 153 | assertEqualsAsync("test", asyncDecl); |
| 154 | assertEqualsAsync("test2", async function() { return "test2"; }); |
| 155 | assertEqualsAsync("test3", async () => "test3"); |
| 156 | assertEqualsAsync("test4", () => ({ async f() { return "test4"; } }).f()); |
| 157 | assertEqualsAsync("test5", () => AsyncFunction("no", "return 'test' + no;")(5)); |
| 158 | assertEqualsAsync("test6", |
| 159 | () => (new AsyncFunction("no", "return 'test' + no;"))(6)); |
| 160 | |
| 161 | class MyError extends Error {}; |
| 162 | |
| 163 | // Throw completion |
| 164 | async function asyncDeclThrower(e) { throw new MyError(e); } |
| 165 | assertThrowsAsync(() => asyncDeclThrower("boom!"), MyError, "boom!"); |
| 166 | assertThrowsAsync( |
| 167 | () => (async function(e) { throw new MyError(e); })("boom!!!"), |
| 168 | MyError, "boom!!!"); |
| 169 | assertThrowsAsync( |
| 170 | () => (async e => { throw new MyError(e) })("boom!!"), MyError, "boom!!"); |
| 171 | assertThrowsAsync( |
| 172 | () => ({ async thrower(e) { throw new MyError(e); } }).thrower("boom!1!"), |
| 173 | MyError, "boom!1!"); |
| 174 | assertThrowsAsync( |
| 175 | () => AsyncFunction("msg", "throw new MyError(msg)")("boom!2!!"), |
| 176 | MyError, "boom!2!!"); |
| 177 | assertThrowsAsync( |
| 178 | () => (new AsyncFunction("msg", "throw new MyError(msg)"))("boom!2!!!"), |
| 179 | MyError, "boom!2!!!"); |
| 180 | |
| 181 | function resolveLater(value) { return Promise.resolve(value); } |
| 182 | function rejectLater(error) { return Promise.reject(error); } |
| 183 | |
| 184 | // Resume after Normal completion |
| 185 | var log = []; |
| 186 | async function resumeAfterNormal(value) { |
| 187 | log.push("start:" + value); |
| 188 | value = await resolveLater(value + 1); |
| 189 | log.push("resume:" + value); |
| 190 | value = await resolveLater(value + 1); |
| 191 | log.push("resume:" + value); |
| 192 | return value + 1; |
| 193 | } |
| 194 | |
| 195 | assertEqualsAsync(4, () => resumeAfterNormal(1)); |
| 196 | assertEquals("start:1 resume:2 resume:3", log.join(" ")); |
| 197 | |
| 198 | var O = { |
| 199 | async resumeAfterNormal(value) { |
| 200 | log.push("start:" + value); |
| 201 | value = await resolveLater(value + 1); |
| 202 | log.push("resume:" + value); |
| 203 | value = await resolveLater(value + 1); |
| 204 | log.push("resume:" + value); |
| 205 | return value + 1; |
| 206 | } |
| 207 | }; |
| 208 | log = []; |
| 209 | assertEqualsAsync(5, () => O.resumeAfterNormal(2)); |
| 210 | assertEquals("start:2 resume:3 resume:4", log.join(" ")); |
| 211 | |
| 212 | var resumeAfterNormalArrow = async (value) => { |
| 213 | log.push("start:" + value); |
| 214 | value = await resolveLater(value + 1); |
| 215 | log.push("resume:" + value); |
| 216 | value = await resolveLater(value + 1); |
| 217 | log.push("resume:" + value); |
| 218 | return value + 1; |
| 219 | }; |
| 220 | log = []; |
| 221 | assertEqualsAsync(6, () => resumeAfterNormalArrow(3)); |
| 222 | assertEquals("start:3 resume:4 resume:5", log.join(" ")); |
| 223 | |
| 224 | var resumeAfterNormalEval = AsyncFunction("value", ` |
| 225 | log.push("start:" + value); |
| 226 | value = await resolveLater(value + 1); |
| 227 | log.push("resume:" + value); |
| 228 | value = await resolveLater(value + 1); |
| 229 | log.push("resume:" + value); |
| 230 | return value + 1;`); |
| 231 | log = []; |
| 232 | assertEqualsAsync(7, () => resumeAfterNormalEval(4)); |
| 233 | assertEquals("start:4 resume:5 resume:6", log.join(" ")); |
| 234 | |
| 235 | var resumeAfterNormalNewEval = new AsyncFunction("value", ` |
| 236 | log.push("start:" + value); |
| 237 | value = await resolveLater(value + 1); |
| 238 | log.push("resume:" + value); |
| 239 | value = await resolveLater(value + 1); |
| 240 | log.push("resume:" + value); |
| 241 | return value + 1;`); |
| 242 | log = []; |
| 243 | assertEqualsAsync(8, () => resumeAfterNormalNewEval(5)); |
| 244 | assertEquals("start:5 resume:6 resume:7", log.join(" ")); |
| 245 | |
| 246 | // Resume after Throw completion |
| 247 | async function resumeAfterThrow(value) { |
| 248 | log.push("start:" + value); |
| 249 | try { |
| 250 | value = await rejectLater("throw1"); |
| 251 | } catch (e) { |
| 252 | log.push("resume:" + e); |
| 253 | } |
| 254 | try { |
| 255 | value = await rejectLater("throw2"); |
| 256 | } catch (e) { |
| 257 | log.push("resume:" + e); |
| 258 | } |
| 259 | return value + 1; |
| 260 | } |
| 261 | |
| 262 | log = []; |
| 263 | assertEqualsAsync(2, () => resumeAfterThrow(1)); |
| 264 | assertEquals("start:1 resume:throw1 resume:throw2", log.join(" ")); |
| 265 | |
| 266 | var O = { |
| 267 | async resumeAfterThrow(value) { |
| 268 | log.push("start:" + value); |
| 269 | try { |
| 270 | value = await rejectLater("throw1"); |
| 271 | } catch (e) { |
| 272 | log.push("resume:" + e); |
| 273 | } |
| 274 | try { |
| 275 | value = await rejectLater("throw2"); |
| 276 | } catch (e) { |
| 277 | log.push("resume:" + e); |
| 278 | } |
| 279 | return value + 1; |
| 280 | } |
| 281 | } |
| 282 | log = []; |
| 283 | assertEqualsAsync(3, () => O.resumeAfterThrow(2)); |
| 284 | assertEquals("start:2 resume:throw1 resume:throw2", log.join(" ")); |
| 285 | |
| 286 | var resumeAfterThrowArrow = async (value) => { |
| 287 | log.push("start:" + value); |
| 288 | try { |
| 289 | value = await rejectLater("throw1"); |
| 290 | } catch (e) { |
| 291 | log.push("resume:" + e); |
| 292 | } |
| 293 | try { |
| 294 | value = await rejectLater("throw2"); |
| 295 | } catch (e) { |
| 296 | log.push("resume:" + e); |
| 297 | } |
| 298 | return value + 1; |
| 299 | }; |
| 300 | |
| 301 | log = []; |
| 302 | |
| 303 | assertEqualsAsync(4, () => resumeAfterThrowArrow(3)); |
| 304 | assertEquals("start:3 resume:throw1 resume:throw2", log.join(" ")); |
| 305 | |
| 306 | var resumeAfterThrowEval = AsyncFunction("value", ` |
| 307 | log.push("start:" + value); |
| 308 | try { |
| 309 | value = await rejectLater("throw1"); |
| 310 | } catch (e) { |
| 311 | log.push("resume:" + e); |
| 312 | } |
| 313 | try { |
| 314 | value = await rejectLater("throw2"); |
| 315 | } catch (e) { |
| 316 | log.push("resume:" + e); |
| 317 | } |
| 318 | return value + 1;`); |
| 319 | log = []; |
| 320 | assertEqualsAsync(5, () => resumeAfterThrowEval(4)); |
| 321 | assertEquals("start:4 resume:throw1 resume:throw2", log.join(" ")); |
| 322 | |
| 323 | var resumeAfterThrowNewEval = new AsyncFunction("value", ` |
| 324 | log.push("start:" + value); |
| 325 | try { |
| 326 | value = await rejectLater("throw1"); |
| 327 | } catch (e) { |
| 328 | log.push("resume:" + e); |
| 329 | } |
| 330 | try { |
| 331 | value = await rejectLater("throw2"); |
| 332 | } catch (e) { |
| 333 | log.push("resume:" + e); |
| 334 | } |
| 335 | return value + 1;`); |
| 336 | log = []; |
| 337 | assertEqualsAsync(6, () => resumeAfterThrowNewEval(5)); |
| 338 | assertEquals("start:5 resume:throw1 resume:throw2", log.join(" ")); |
| 339 | |
| 340 | async function foo() {} |
| 341 | assertEquals("async function foo() {}", foo.toString()); |
| 342 | assertEquals("async function () {}", async function() {}.toString()); |
| 343 | assertEquals("async x => x", (async x => x).toString()); |
| 344 | assertEquals("async x => { return x }", (async x => { return x }).toString()); |
| 345 | class AsyncMethod { async foo() { } } |
Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 346 | assertEquals("async foo() { }", |
| 347 | Function.prototype.toString.call(AsyncMethod.prototype.foo)); |
| 348 | assertEquals("async foo() { }", |
| 349 | Function.prototype.toString.call({async foo() { }}.foo)); |
| 350 | |
| 351 | // Async functions are not constructible |
| 352 | assertThrows(() => class extends (async function() {}) {}, TypeError); |
| 353 | |
| 354 | // Regress v8:5148 |
| 355 | assertEqualsAsync("1", () => (async({ a = NaN }) => a)({ a: "1" })); |
| 356 | assertEqualsAsync( |
| 357 | "10", () => (async(foo, { a = NaN }) => foo + a)("1", { a: "0" })); |
| 358 | assertEqualsAsync("2", () => (async({ a = "2" }) => a)({ a: undefined })); |
| 359 | assertEqualsAsync( |
| 360 | "20", () => (async(foo, { a = "0" }) => foo + a)("2", { a: undefined })); |
| 361 | assertThrows(() => eval("async({ foo = 1 })"), SyntaxError); |
| 362 | assertThrows(() => eval("async(a, { foo = 1 })"), SyntaxError); |
| 363 | |
| 364 | // https://bugs.chromium.org/p/chromium/issues/detail?id=638019 |
| 365 | async function gaga() { |
| 366 | let i = 1; |
| 367 | while (i-- > 0) { await 42 } |
| 368 | } |
| 369 | assertDoesNotThrow(gaga); |