diff options
Diffstat (limited to 'js/src/tests/non262/async-functions')
35 files changed, 1556 insertions, 0 deletions
diff --git a/js/src/tests/non262/async-functions/BoundNames.js b/js/src/tests/non262/async-functions/BoundNames.js new file mode 100644 index 0000000000..a366334da1 --- /dev/null +++ b/js/src/tests/non262/async-functions/BoundNames.js @@ -0,0 +1,21 @@ +var BUGNUMBER = 1185106; +var summary = "Bound names of async functions"; + +print(BUGNUMBER + ": " + summary); + +async function test() {} +assertEq(test.name, "test"); + +var test2 = (async function test2() {}); +assertEq(test2.name, "test2"); + +var anon = async function() {}; +assertEq(anon.name, "anon"); + +if (typeof Reflect !== "undefined" && Reflect.parse) { + var tree = Reflect.parse("export default async function() {}", { target: "module" }); + assertEq(tree.body[0].declaration.id.name, "default"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/EarlyErrors.js b/js/src/tests/non262/async-functions/EarlyErrors.js new file mode 100644 index 0000000000..eeac4b2541 --- /dev/null +++ b/js/src/tests/non262/async-functions/EarlyErrors.js @@ -0,0 +1,51 @@ +var BUGNUMBER = 1185106; +var summary = "EarlyErrors for async function"; + +print(BUGNUMBER + ": " + summary); + +function assertThrowsSE(code) { + assertThrowsInstanceOf(() => Reflect.parse(code), SyntaxError); +} + +if (typeof Reflect !== "undefined" && Reflect.parse) { + // If FormalParameters Contains AwaitExpression is true. + assertThrowsSE("async function a(k = await 3) {}"); + assertThrowsSE("(async function(k = await 3) {})"); + assertThrowsSE("(async function a(k = await 3) {})"); + + // If BindingIdentifier is `eval` or `arguments`. + assertThrowsSE("'use strict'; async function eval() {}"); + assertThrowsSE("'use strict'; (async function eval() {})"); + + assertThrowsSE("'use strict'; async function arguments() {}"); + assertThrowsSE("'use strict'; (async function arguments() {})"); + + // If any element of the BoundNames of FormalParameters also occurs in the + // LexicallyDeclaredNames of AsyncFunctionBody. + assertThrowsSE("async function a(x) { let x; }"); + assertThrowsSE("(async function(x) { let x; })"); + assertThrowsSE("(async function a(x) { let x; })"); + + // If FormalParameters contains SuperProperty is true. + assertThrowsSE("async function a(k = super.prop) { }"); + assertThrowsSE("(async function(k = super.prop) {})"); + assertThrowsSE("(async function a(k = super.prop) {})"); + + // If AsyncFunctionBody contains SuperProperty is true. + assertThrowsSE("async function a() { super.prop(); }"); + assertThrowsSE("(async function() { super.prop(); })"); + assertThrowsSE("(async function a() { super.prop(); })"); + + // If FormalParameters contains SuperCall is true. + assertThrowsSE("async function a(k = super()) {}"); + assertThrowsSE("(async function(k = super()) {})"); + assertThrowsSE("(async function a(k = super()) {})"); + + // If AsyncFunctionBody contains SuperCall is true. + assertThrowsSE("async function a() { super(); }"); + assertThrowsSE("(async function() { super(); })"); + assertThrowsSE("(async function a() { super(); })"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/ErrorStack.js b/js/src/tests/non262/async-functions/ErrorStack.js new file mode 100644 index 0000000000..9f3adc4f72 --- /dev/null +++ b/js/src/tests/non262/async-functions/ErrorStack.js @@ -0,0 +1,103 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue + +var BUGNUMBER = 1343158; +var summary = "Error.stack should provide meaningful stack trace in async function"; + +print(BUGNUMBER + ": " + summary); + +let COOKIE = "C0F5DBB89807"; + +async function thrower() { + let stack = new Error().stack; // line 11 + assertEq(/^thrower@.+ErrorStack.js:11/m.test(stack), true, toMessage(stack)); + assertEq(/^inner@.+ErrorStack.js:38/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*middle@.+ErrorStack.js:58/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*outer@.+ErrorStack.js:78/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*@.+ErrorStack.js:82/m.test(stack), true, toMessage(stack)); + + throw new Error(COOKIE); // line 18 +} + +async function inner() { + let stack = new Error().stack; // line 22 + assertEq(/thrower@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/^inner@.+ErrorStack.js:22/m.test(stack), true, toMessage(stack)); + assertEq(/^middle@.+ErrorStack.js:58/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*outer@.+ErrorStack.js:78/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*@.+ErrorStack.js:82/m.test(stack), true, toMessage(stack)); + + await Promise.resolve(100); + + stack = new Error().stack; // line 31 + assertEq(/thrower@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/^inner@.+ErrorStack.js:31/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*middle@.+ErrorStack.js:58/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*outer@.+ErrorStack.js:78/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*@.+ErrorStack.js:82/m.test(stack), true, toMessage(stack)); + + await thrower(); // line 38 +} + +async function middle() { + let stack = new Error().stack; // line 42 + assertEq(/thrower@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/inner@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/^middle@.+ErrorStack.js:42/m.test(stack), true, toMessage(stack)); + assertEq(/^outer@.+ErrorStack.js:78/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*@.+ErrorStack.js:82/m.test(stack), true, toMessage(stack)); + + await Promise.resolve(1000); + + stack = new Error().stack; // line 51 + assertEq(/thrower@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/inner@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/^middle@.+ErrorStack.js:51/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*outer@.+ErrorStack.js:78/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*@.+ErrorStack.js:82/m.test(stack), true, toMessage(stack)); + + await inner(); // line 58 +} + +async function outer() { + let stack = new Error().stack; // line 62 + assertEq(/thrower@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/inner@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/middle@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/^outer@.+ErrorStack.js:62/m.test(stack), true, toMessage(stack)); + assertEq(/^@.+ErrorStack.js:82/m.test(stack), true, toMessage(stack)); + + await Promise.resolve(10000); + + stack = new Error().stack; // line 71 + assertEq(/thrower@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/inner@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/middle@.+ErrorStack.js/m.test(stack), false, toMessage(stack)); + assertEq(/^outer@.+ErrorStack.js:71/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*@.+ErrorStack.js:82/m.test(stack), true, toMessage(stack)); + + await middle(); // line 78 +} + +try { + getPromiseResult(outer()); // line 82 + assertEq(true, false); +} catch (e) { + // Re-throw the exception to log the assertion failure properly. + if (!e.message.includes(COOKIE)) + throw e; + + let stack = e.stack; + assertEq(/^thrower@.+ErrorStack.js:18/m.test(stack), true, toMessage(stack)); + assertEq(/^inner@.+ErrorStack.js:38/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*middle@.+ErrorStack.js:58/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*outer@.+ErrorStack.js:78/m.test(stack), true, toMessage(stack)); + assertEq(/^async\*@.+ErrorStack.js:82/m.test(stack), true, toMessage(stack)); +} + +function toMessage(stack) { + // Provide the stack string in the error message for debugging. + return `[stack: ${stack.replace(/\n/g, "\\n")}]`; +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/arguments_callee.js b/js/src/tests/non262/async-functions/arguments_callee.js new file mode 100644 index 0000000000..2a577e0b9d --- /dev/null +++ b/js/src/tests/non262/async-functions/arguments_callee.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue + +var BUGNUMBER = 1185106; +var summary = "arguments.callee in sloppy mode should return wrapped function"; + +print(BUGNUMBER + ": " + summary); + +async function decl1() { + return arguments.callee; +} +assertEventuallyEq(decl1(), decl1); + +var expr1 = async function foo() { + return arguments.callee; +}; +assertEventuallyEq(expr1(), expr1); + +var expr2 = async function() { + return arguments.callee; +}; +assertEventuallyEq(expr2(), expr2); + +var obj = { + async method1() { + return arguments.callee; + } +}; +assertEventuallyEq(obj.method1(), obj.method1); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/async-contains-unicode-escape.js b/js/src/tests/non262/async-functions/async-contains-unicode-escape.js new file mode 100644 index 0000000000..d53dff696c --- /dev/null +++ b/js/src/tests/non262/async-functions/async-contains-unicode-escape.js @@ -0,0 +1,54 @@ +var BUGNUMBER = 1315815; +var summary = "async/await containing escapes"; + +print(BUGNUMBER + ": " + summary); + +// Using "eval" as the argument name is fugly, but it means evals below are +// *direct* evals, and so their effects in the unescaped case won't extend +// past each separate |test| call (as would happen if we used a different name +// that made each eval into an indirect eval, affecting code in the global +// scope). +function test(code, eval) +{ + var unescaped = code.replace("###", "async"); + var escaped = code.replace("###", "\\u0061"); + + assertThrowsInstanceOf(() => eval(escaped), SyntaxError); + eval(unescaped); +} + +test("### function f() {}", eval); +test("var x = ### function f() {}", eval); +test("### x => {};", eval); +test("var x = ### x => {}", eval); +test("### () => {};", eval); +test("var x = ### () => {}", eval); +test("### (y) => {};", eval); +test("var x = ### (y) => {}", eval); +test("({ ### x() {} })", eval); +test("var x = ### function f() {}", eval); + +if (typeof parseModule === "function") + test("export default ### function f() {}", parseModule); + +assertThrowsInstanceOf(() => eval("async await => 1;"), + SyntaxError); +assertThrowsInstanceOf(() => eval("async aw\\u0061it => 1;"), + SyntaxError); + +var async = 0; +assertEq(\u0061sync, 0); + +var obj = { \u0061sync() { return 1; } }; +assertEq(obj.async(), 1); + +async = function() { return 42; }; + +var z = async(obj); +assertEq(z, 42); + +var w = async(obj)=>{}; +assertEq(typeof w, "function"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/async-function-declaration-in-modules.js b/js/src/tests/non262/async-functions/async-function-declaration-in-modules.js new file mode 100644 index 0000000000..41cc37bf33 --- /dev/null +++ b/js/src/tests/non262/async-functions/async-function-declaration-in-modules.js @@ -0,0 +1,13 @@ +// |reftest| module + +async function f() { + return "success"; +} + +var AsyncFunction = (async function(){}).constructor; + +assertEq(f instanceof AsyncFunction, true); + +f().then(v => { + reportCompare("success", v); +}); diff --git a/js/src/tests/non262/async-functions/async-property-name-error.js b/js/src/tests/non262/async-functions/async-property-name-error.js new file mode 100644 index 0000000000..926794e5d0 --- /dev/null +++ b/js/src/tests/non262/async-functions/async-property-name-error.js @@ -0,0 +1,21 @@ +function assertSyntaxError(code) { + assertThrowsInstanceOf(() => { Function(code); }, SyntaxError, "Function:" + code); + assertThrowsInstanceOf(() => { eval(code); }, SyntaxError, "eval:" + code); + var ieval = eval; + assertThrowsInstanceOf(() => { ieval(code); }, SyntaxError, "indirect eval:" + code); +} + +assertSyntaxError(`({async async: 0})`); +assertSyntaxError(`({async async})`); +assertSyntaxError(`({async async, })`); +assertSyntaxError(`({async async = 0} = {})`); + +for (let decl of ["var", "let", "const"]) { + assertSyntaxError(`${decl} {async async: a} = {}`); + assertSyntaxError(`${decl} {async async} = {}`); + assertSyntaxError(`${decl} {async async, } = {}`); + assertSyntaxError(`${decl} {async async = 0} = {}`); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/await-error.js b/js/src/tests/non262/async-functions/await-error.js new file mode 100644 index 0000000000..6fa6143efc --- /dev/null +++ b/js/src/tests/non262/async-functions/await-error.js @@ -0,0 +1,16 @@ +var BUGNUMBER = 1317153; +var summary = "await outside of async function should provide better error"; + +print(BUGNUMBER + ": " + summary); + +let caught = false; +try { + eval("await 10"); +} catch(e) { + assertEq(e.message.includes("await is only valid in"), true); + caught = true; +} +assertEq(caught, true); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/await-in-arrow-parameters.js b/js/src/tests/non262/async-functions/await-in-arrow-parameters.js new file mode 100644 index 0000000000..ebb4ea9dac --- /dev/null +++ b/js/src/tests/non262/async-functions/await-in-arrow-parameters.js @@ -0,0 +1,94 @@ +var ieval = eval; +var AsyncFunction = async function(){}.constructor; + +var functionContext = { + Function: { + constructor: Function, + toSourceBody: code => `function f() { ${code} }`, + toSourceParameter: code => `function f(x = ${code}) { }`, + }, + AsyncFunction: { + constructor: AsyncFunction, + toSourceBody: code => `async function f() { ${code} }`, + toSourceParameter: code => `async function f(x = ${code}) { }`, + }, +}; + +function assertSyntaxError(kind, code) { + var {constructor, toSourceBody, toSourceParameter} = functionContext[kind]; + var body = toSourceBody(code); + var parameter = toSourceParameter(code); + + assertThrowsInstanceOf(() => { constructor(code); }, SyntaxError, constructor.name + ":" + code); + assertThrowsInstanceOf(() => { constructor(`x = ${code}`, ""); }, SyntaxError, constructor.name + ":" + code); + + assertThrowsInstanceOf(() => { eval(body); }, SyntaxError, "eval:" + body); + assertThrowsInstanceOf(() => { ieval(body); }, SyntaxError, "indirect eval:" + body); + + assertThrowsInstanceOf(() => { eval(parameter); }, SyntaxError, "eval:" + parameter); + assertThrowsInstanceOf(() => { ieval(parameter); }, SyntaxError, "indirect eval:" + parameter); +} + +function assertNoSyntaxError(kind, code) { + var {constructor, toSourceBody, toSourceParameter} = functionContext[kind]; + var body = toSourceBody(code); + var parameter = toSourceParameter(code); + + constructor(code); + constructor(`x = ${code}`, ""); + + eval(body); + ieval(body); + + eval(parameter); + ieval(parameter); +} + +function assertSyntaxErrorAsync(code) { + assertNoSyntaxError("Function", code); + assertSyntaxError("AsyncFunction", code); +} + +function assertSyntaxErrorBoth(code) { + assertSyntaxError("Function", code); + assertSyntaxError("AsyncFunction", code); +} + + +// Bug 1353691 +// |await| expression is invalid in arrow functions in async-context. +// |await/r/g| first parses as |AwaitExpression RegularExpressionLiteral|, when reparsing the +// arrow function, it is parsed as |IdentRef DIV IdentRef DIV IdentRef|. We need to ensure in this +// case, that we still treat |await| as a keyword and hence throw a SyntaxError. +assertSyntaxErrorAsync("(a = await/r/g) => {}"); +assertSyntaxErrorBoth("async(a = await/r/g) => {}"); + +// Also applies when nesting arrow functions. +assertSyntaxErrorAsync("(a = (b = await/r/g) => {}) => {}"); +assertSyntaxErrorBoth("async(a = (b = await/r/g) => {}) => {}"); +assertSyntaxErrorBoth("(a = async(b = await/r/g) => {}) => {}"); +assertSyntaxErrorBoth("async(a = async(b = await/r/g) => {}) => {}"); + + +// Bug 1355860 +// |await| cannot be used as rest-binding parameter in arrow functions in async-context. +assertSyntaxErrorAsync("(...await) => {}"); +assertSyntaxErrorBoth("async(...await) => {}"); + +assertSyntaxErrorAsync("(a, ...await) => {}"); +assertSyntaxErrorBoth("async(a, ...await) => {}"); + +// Also test nested arrow functions. +assertSyntaxErrorAsync("(a = (...await) => {}) => {}"); +assertSyntaxErrorBoth("(a = async(...await) => {}) => {}"); +assertSyntaxErrorBoth("async(a = (...await) => {}) => {}"); +assertSyntaxErrorBoth("async(a = async(...await) => {}) => {}"); + +assertSyntaxErrorAsync("(a = (b, ...await) => {}) => {}"); +assertSyntaxErrorBoth("(a = async(b, ...await) => {}) => {}"); +assertSyntaxErrorBoth("async(a = (b, ...await) => {}) => {}"); +assertSyntaxErrorBoth("async(a = async(b, ...await) => {}) => {}"); + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/await-in-parameters-of-async-func.js b/js/src/tests/non262/async-functions/await-in-parameters-of-async-func.js new file mode 100644 index 0000000000..ef4b84d99b --- /dev/null +++ b/js/src/tests/non262/async-functions/await-in-parameters-of-async-func.js @@ -0,0 +1,67 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1478910; +var summary = 'JSMSG_AWAIT_IN_PARAMETER error for incomplete await expr in async function/generator parameter'; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + printBugNumber(BUGNUMBER); + printStatus(summary); + + let testAwaitInDefaultExprOfAsyncFunc = (code) => { + assertThrowsInstanceOf(() => eval(code), SyntaxError, "await expression can't be used in parameter"); + }; + + let testNoException = (code) => { + assertEq(completesNormally(code), true); + }; + + // https://www.ecma-international.org/ecma-262/9.0/ + + // Async Generator Function Definitions : AsyncGeneratorDeclaration & AsyncGeneratorExpression + // async function* f() {} + // f = async function*() {} + testAwaitInDefaultExprOfAsyncFunc("async function* f(a = await) {}"); + testAwaitInDefaultExprOfAsyncFunc("let f = async function*(a = await) {}"); + + testAwaitInDefaultExprOfAsyncFunc("function f(a = async function*(a = await) {}) {}"); + testAwaitInDefaultExprOfAsyncFunc("function f() { a = async function*(a = await) {}; }"); + + testAwaitInDefaultExprOfAsyncFunc("async function* f() { a = async function*(a = await) {}; }"); + testNoException("async function* f() { let a = function(a = await) {}; }"); + + testNoException("async function* f(a = async function*() { await 1; }) {}"); + + // Async Function Definitions : AsyncFunctionDeclaration & AsyncFunctionExpression + // async function f() {} + // f = async function() {} + testAwaitInDefaultExprOfAsyncFunc("async function f(a = await) {}"); + testAwaitInDefaultExprOfAsyncFunc("let f = async function(a = await) {}"); + + testAwaitInDefaultExprOfAsyncFunc("function f(a = async function(a = await) {}) {}"); + testAwaitInDefaultExprOfAsyncFunc("function f() { a = async function(a = await) {}; }"); + + testAwaitInDefaultExprOfAsyncFunc("async function f() { a = async function(a = await) {}; }"); + testNoException("async function f() { let a = function(a = await) {}; }"); + + testNoException("async function f(a = async function() { await 1; }) {}"); + + // Async Arrow Function Definitions : AsyncArrowFunction + // async () => {} + testAwaitInDefaultExprOfAsyncFunc("async (a = await) => {}"); + + testNoException("async (a = async () => { await 1; }) => {}"); + + reportCompare(true, true, summary); +} diff --git a/js/src/tests/non262/async-functions/await-newline.js b/js/src/tests/non262/async-functions/await-newline.js new file mode 100644 index 0000000000..dc42fa481a --- /dev/null +++ b/js/src/tests/non262/async-functions/await-newline.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue + +var BUGNUMBER = 1331009; +var summary = "Newline is allowed between await and operand"; + +print(BUGNUMBER + ": " + summary); + +var expr = async function foo() { + return await + 10; +}; +assertEventuallyEq(expr(), 10); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/browser.js b/js/src/tests/non262/async-functions/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/async-functions/browser.js diff --git a/js/src/tests/non262/async-functions/construct-newtarget.js b/js/src/tests/non262/async-functions/construct-newtarget.js new file mode 100644 index 0000000000..7d75d0c939 --- /dev/null +++ b/js/src/tests/non262/async-functions/construct-newtarget.js @@ -0,0 +1,79 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const AsyncFunction = async function(){}.constructor; + + +// Test subclassing %AsyncFunction% works correctly. +class MyAsync extends AsyncFunction {} + +var fn = new MyAsync(); +assertEq(fn instanceof MyAsync, true); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), MyAsync.prototype); + +fn = Reflect.construct(MyAsync, []); +assertEq(fn instanceof MyAsync, true); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), MyAsync.prototype); + +fn = Reflect.construct(MyAsync, [], MyAsync); +assertEq(fn instanceof MyAsync, true); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), MyAsync.prototype); + +fn = Reflect.construct(MyAsync, [], AsyncFunction); +assertEq(fn instanceof MyAsync, false); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype); + + +// Set a different constructor as NewTarget. +fn = Reflect.construct(MyAsync, [], Array); +assertEq(fn instanceof MyAsync, false); +assertEq(fn instanceof AsyncFunction, false); +assertEq(Object.getPrototypeOf(fn), Array.prototype); + +fn = Reflect.construct(AsyncFunction, [], Array); +assertEq(fn instanceof AsyncFunction, false); +assertEq(Object.getPrototypeOf(fn), Array.prototype); + + +// The prototype defaults to %AsyncFunctionPrototype% if null. +function NewTargetNullPrototype() {} +NewTargetNullPrototype.prototype = null; + +fn = Reflect.construct(AsyncFunction, [], NewTargetNullPrototype); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype); + +fn = Reflect.construct(MyAsync, [], NewTargetNullPrototype); +assertEq(fn instanceof MyAsync, false); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype); + + +// "prototype" property is retrieved exactly once. +var trapLog = [], getLog = []; +var ProxiedConstructor = new Proxy(AsyncFunction, new Proxy({ + get(target, propertyKey, receiver) { + getLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +}, { + get(target, propertyKey, receiver) { + trapLog.push(propertyKey); + return Reflect.get(target, propertyKey, receiver); + } +})); + +fn = Reflect.construct(AsyncFunction, [], ProxiedConstructor); +assertEqArray(trapLog, ["get"]); +assertEqArray(getLog, ["prototype"]); +assertEq(fn instanceof AsyncFunction, true); +assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype); + + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/async-functions/constructor.js b/js/src/tests/non262/async-functions/constructor.js new file mode 100644 index 0000000000..ccacb4363a --- /dev/null +++ b/js/src/tests/non262/async-functions/constructor.js @@ -0,0 +1,33 @@ +var BUGNUMBER = 1185106; +var summary = "async function constructor and prototype"; + +print(BUGNUMBER + ": " + summary); + +var f1 = async function() {}; + +var AsyncFunction = f1.constructor; +var AsyncFunctionPrototype = AsyncFunction.prototype; + +assertEq(AsyncFunction.name, "AsyncFunction"); +assertEq(AsyncFunction.length, 1); +assertEq(Object.getPrototypeOf(async function() {}), AsyncFunctionPrototype); + +assertEq(AsyncFunctionPrototype.constructor, AsyncFunction); + +var f2 = AsyncFunction("await 1"); +assertEq(f2.constructor, AsyncFunction); +assertEq(f2.length, 0); +assertEq(Object.getPrototypeOf(f2), AsyncFunctionPrototype); + +var f3 = new AsyncFunction("await 1"); +assertEq(f3.constructor, AsyncFunction); +assertEq(f3.length, 0); +assertEq(Object.getPrototypeOf(f3), AsyncFunctionPrototype); + +var f4 = AsyncFunction("a", "b", "c", "await 1"); +assertEq(f4.constructor, AsyncFunction); +assertEq(f4.length, 3); +assertEq(Object.getPrototypeOf(f4), AsyncFunctionPrototype); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/cover-init-name-syntax.js b/js/src/tests/non262/async-functions/cover-init-name-syntax.js new file mode 100644 index 0000000000..cb7858a67a --- /dev/null +++ b/js/src/tests/non262/async-functions/cover-init-name-syntax.js @@ -0,0 +1,65 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// CoverInitName in async arrow parameters. +async ({a = 1}) => {}; +async ({a = 1}, {b = 2}) => {}; +async ({a = 1}, {b = 2}, {c = 3}) => {}; +async ({a = 1} = {}, {b = 2}, {c = 3}) => {}; +async ({a = 1} = {}, {b = 2} = {}, {c = 3}) => {}; +async ({a = 1} = {}, {b = 2} = {}, {c = 3} = {}) => {}; + +// CoverInitName nested in array destructuring. +async ([{a = 0}]) => {}; + +// CoverInitName nested in rest pattern. +async ([...[{a = 0}]]) => {}; + +// CoverInitName nested in object destructuring. +async ({p: {a = 0}}) => {}; + +// CoverInitName in CoverCallExpressionAndAsyncArrowHead, but not AsyncArrowHead. +assertThrowsInstanceOf(() => eval(` + NotAsync({a = 1}); +`), SyntaxError); +assertThrowsInstanceOf(() => eval(` + NotAsync({a = 1}, {b = 2}, {c = 3}); +`), SyntaxError); +assertThrowsInstanceOf(() => eval(` + NotAsync({a = 1}, {b = 2}, {c = 3} = {}); +`), SyntaxError); +assertThrowsInstanceOf(() => eval(` + NotAsync({a = 1}, {b = 2} = {}, {c = 3} = {}); +`), SyntaxError); + +// Starts with "async", but not called from AssignmentExpression. +assertThrowsInstanceOf(() => eval(` + typeof async({a = 1}); +`), SyntaxError); +assertThrowsInstanceOf(() => eval(` + typeof async({a = 1}, {b = 2}, {c = 3}); +`), SyntaxError); +assertThrowsInstanceOf(() => eval(` + typeof async({a = 1}, {b = 2}, {c = 3} = {}); +`), SyntaxError); +assertThrowsInstanceOf(() => eval(` + typeof async({a = 1}, {b = 2} = {}, {c = 3} = {}); +`), SyntaxError); + +// CoverInitName in CoverCallExpressionAndAsyncArrowHead, but not AsyncArrowHead. +assertThrowsInstanceOf(() => eval(` + obj.async({a = 1}); +`), SyntaxError); +assertThrowsInstanceOf(() => eval(` + obj.async({a = 1}, {b = 2}, {c = 3}); +`), SyntaxError); +assertThrowsInstanceOf(() => eval(` + obj.async({a = 1}, {b = 2}, {c = 3} = {}); +`), SyntaxError); +assertThrowsInstanceOf(() => eval(` + obj.async({a = 1}, {b = 2} = {}, {c = 3} = {}); +`), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/async-functions/create-function-parse-before-getprototype.js b/js/src/tests/non262/async-functions/create-function-parse-before-getprototype.js new file mode 100644 index 0000000000..413236ec16 --- /dev/null +++ b/js/src/tests/non262/async-functions/create-function-parse-before-getprototype.js @@ -0,0 +1,19 @@ +var getProtoCalled = false; + +var newTarget = Object.defineProperty(function(){}.bind(), "prototype", { + get() { + getProtoCalled = true; + return null; + } +}); + +var AsyncFunction = async function(){}.constructor; + +assertThrowsInstanceOf(() => { + Reflect.construct(AsyncFunction, ["@error"], newTarget); +}, SyntaxError); + +assertEq(getProtoCalled, false); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/duplicate-__proto__.js b/js/src/tests/non262/async-functions/duplicate-__proto__.js new file mode 100644 index 0000000000..d60b052484 --- /dev/null +++ b/js/src/tests/non262/async-functions/duplicate-__proto__.js @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Async arrow function parameters. +async ({__proto__: a, __proto__: b}) => {}; + +// Async arrow function parameters with defaults (initially parsed as destructuring assignment). +async ({__proto__: a, __proto__: b} = {}) => {}; + +// Duplicate __proto__ in CoverCallExpressionAndAsyncArrowHead, but not AsyncArrowHead. +assertThrowsInstanceOf(() => eval(` + NotAsync({__proto__: a, __proto__: b}); +`), SyntaxError); + +// Starts with "async", but not called from AssignmentExpression. +assertThrowsInstanceOf(() => eval(` + typeof async({__proto__: a, __proto__: b}); +`), SyntaxError); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/async-functions/forbidden-as-consequent.js b/js/src/tests/non262/async-functions/forbidden-as-consequent.js new file mode 100644 index 0000000000..656ed46deb --- /dev/null +++ b/js/src/tests/non262/async-functions/forbidden-as-consequent.js @@ -0,0 +1,14 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +assertThrowsInstanceOf(() => eval("if (1) async function foo() {}"), + SyntaxError); +assertThrowsInstanceOf(() => eval("'use strict'; if (1) async function foo() {}"), + SyntaxError); + +var async = 42; +assertEq(eval("if (1) async \n function foo() {}"), 42); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/identity.js b/js/src/tests/non262/async-functions/identity.js new file mode 100644 index 0000000000..f3876bf233 --- /dev/null +++ b/js/src/tests/non262/async-functions/identity.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue + +var BUGNUMBER = 1185106; +var summary = "Named async function expression should get wrapped function for the name inside it"; + +print(BUGNUMBER + ": " + summary); + +var expr = async function foo() { + return foo; +}; +assertEventuallyEq(expr(), expr); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/inner-caller.js b/js/src/tests/non262/async-functions/inner-caller.js new file mode 100644 index 0000000000..f76f978f01 --- /dev/null +++ b/js/src/tests/non262/async-functions/inner-caller.js @@ -0,0 +1,17 @@ +function g() { + return g.caller; +} + +(async function f() { + var inner = g(); + assertEq(inner, null); +})(); + +(async function f() { + "use strict"; + var inner = g(); + assertEq(inner, null); +})(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/length.js b/js/src/tests/non262/async-functions/length.js new file mode 100644 index 0000000000..da95e23b89 --- /dev/null +++ b/js/src/tests/non262/async-functions/length.js @@ -0,0 +1,12 @@ +var BUGNUMBER = 1185106; +var summary = "async function length"; + +print(BUGNUMBER + ": " + summary); + +assertEq(async function() {}.length, 0); +assertEq(async function(a) {}.length, 1); +assertEq(async function(a, b, c) {}.length, 3); +assertEq(async function(a, b, c, ...d) {}.length, 3); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/methods.js b/js/src/tests/non262/async-functions/methods.js new file mode 100644 index 0000000000..061a0e8267 --- /dev/null +++ b/js/src/tests/non262/async-functions/methods.js @@ -0,0 +1,61 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue +var BUGNUMBER = 1185106; +var summary = "async methods semantics"; + +print(BUGNUMBER + ": " + summary); + +class X { + constructor() { + this.value = 42; + } + async getValue() { + return this.value; + } + setValue(value) { + this.value = value; + } + async increment() { + var value = await this.getValue(); + this.setValue(value + 1); + return this.getValue(); + } + async getBaseClassName() { + return 'X'; + } + static async getStaticValue() { + return 44; + } + async 10() { + return 46; + } + async ["foo"]() { + return 47; + } +} + +class Y extends X { + async getBaseClassName() { + return super.getBaseClassName(); + } +} + +var objLiteral = { + async get() { + return 45; + }, + someStuff: 5 +}; + +var x = new X(); +var y = new Y(); + +assertEventuallyEq(x.getValue(), 42); +assertEventuallyEq(x.increment(), 43); +assertEventuallyEq(x[10](), 46); +assertEventuallyEq(x.foo(), 47); +assertEventuallyEq(X.getStaticValue(), 44); +assertEventuallyEq(objLiteral.get(), 45); +assertEventuallyEq(y.getBaseClassName(), 'X'); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/no-expression-closure.js b/js/src/tests/non262/async-functions/no-expression-closure.js new file mode 100644 index 0000000000..44d139fe41 --- /dev/null +++ b/js/src/tests/non262/async-functions/no-expression-closure.js @@ -0,0 +1,17 @@ +function assertSyntaxError(code) { + assertThrowsInstanceOf(function () { Function(code); }, SyntaxError, "Function:" + code); + assertThrowsInstanceOf(function () { eval(code); }, SyntaxError, "eval:" + code); + var ieval = eval; + assertThrowsInstanceOf(function () { ieval(code); }, SyntaxError, "indirect eval:" + code); +} + +// AsyncFunction statement +assertSyntaxError(`async function f() 0`); + +// AsyncFunction expression +assertSyntaxError(`void async function() 0`); +assertSyntaxError(`void async function f() 0`); + + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/parameters-error-reject-promise.js b/js/src/tests/non262/async-functions/parameters-error-reject-promise.js new file mode 100644 index 0000000000..a6cca7cdde --- /dev/null +++ b/js/src/tests/non262/async-functions/parameters-error-reject-promise.js @@ -0,0 +1,56 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue + +class ExpectedError extends Error { + name = "ExpectedError"; +} + +class UnexpectedError extends Error { + name = "UnexpectedError"; +} + +function throwExpectedError() { + throw new ExpectedError(); +} + +async function throwsInParameterExpression(a = throwExpectedError()) { + throw new UnexpectedError(); +} +assertEventuallyThrows(throwsInParameterExpression(), ExpectedError); + +async function throwsInObjectDestructuringParameterEmpty({}) { + throw new UnexpectedError(); +} +assertEventuallyThrows(throwsInObjectDestructuringParameterEmpty(), TypeError); + +let objectThrowingExpectedError = { + get a() { + throw new ExpectedError(); + } +} + +async function throwsInObjectDestructuringParameter({a}) { + throw new UnexpectedError(); +} +assertEventuallyThrows(throwsInObjectDestructuringParameter(), TypeError); +assertEventuallyThrows(throwsInObjectDestructuringParameter(objectThrowingExpectedError), ExpectedError); + +let iteratorThrowingExpectedError = { + [Symbol.iterator]() { + throw new ExpectedError(); + } +}; + +async function throwsInArrayDestructuringParameterEmpty([]) { + throw new UnexpectedError(); +} +assertEventuallyThrows(throwsInArrayDestructuringParameterEmpty(), TypeError); +assertEventuallyThrows(throwsInArrayDestructuringParameterEmpty(iteratorThrowingExpectedError), ExpectedError); + +async function throwsInArrayDestructuringParameter([a]) { + throw new UnexpectedError(); +} +assertEventuallyThrows(throwsInArrayDestructuringParameter(), TypeError); +assertEventuallyThrows(throwsInArrayDestructuringParameter(iteratorThrowingExpectedError), ExpectedError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/properties.js b/js/src/tests/non262/async-functions/properties.js new file mode 100644 index 0000000000..ca383901bb --- /dev/null +++ b/js/src/tests/non262/async-functions/properties.js @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function assertOwnDescriptor(object, propertyKey, expected) { + var desc = Object.getOwnPropertyDescriptor(object, propertyKey); + if (desc === undefined) { + assertEq(expected, undefined, "Property shouldn't be present"); + return; + } + + assertEq(desc.enumerable, expected.enumerable, `${String(propertyKey)}.[[Enumerable]]`); + assertEq(desc.configurable, expected.configurable, `${String(propertyKey)}.[[Configurable]]`); + + if (Object.prototype.hasOwnProperty.call(desc, "value")) { + assertEq(desc.value, expected.value, `${String(propertyKey)}.[[Value]]`); + assertEq(desc.writable, expected.writable, `${String(propertyKey)}.[[Writable]]`); + } else { + assertEq(desc.get, expected.get, `${String(propertyKey)}.[[Get]]`); + assertEq(desc.set, expected.set, `${String(propertyKey)}.[[Set]]`); + } +} + +async function asyncFunc(){} +var AsyncFunctionPrototype = Object.getPrototypeOf(asyncFunc); +var AsyncFunction = AsyncFunctionPrototype.constructor; + + +// ES2017, 25.5.2 Properties of the AsyncFunction Constructor + +assertEqArray(Object.getOwnPropertyNames(AsyncFunction).sort(), ["length", "name", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(AsyncFunction), []); + +assertOwnDescriptor(AsyncFunction, "length", { + value: 1, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(AsyncFunction, "name", { + value: "AsyncFunction", writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(AsyncFunction, "prototype", { + value: AsyncFunctionPrototype, writable: false, enumerable: false, configurable: false +}); + + +// ES2017, 25.5.3 Properties of the AsyncFunction Prototype Object + +assertEqArray(Object.getOwnPropertyNames(AsyncFunctionPrototype).sort(), ["constructor"]); +assertEqArray(Object.getOwnPropertySymbols(AsyncFunctionPrototype), [Symbol.toStringTag]); + +assertOwnDescriptor(AsyncFunctionPrototype, "constructor", { + value: AsyncFunction, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(AsyncFunctionPrototype, Symbol.toStringTag, { + value: "AsyncFunction", writable: false, enumerable: false, configurable: true +}); + + +// ES2017, 25.5.4 AsyncFunction Instances + +assertEqArray(Object.getOwnPropertyNames(asyncFunc).sort(), ["length", "name"]); +assertEqArray(Object.getOwnPropertySymbols(asyncFunc), []); + +assertOwnDescriptor(asyncFunc, "length", { + value: 0, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(asyncFunc, "name", { + value: "asyncFunc", writable: false, enumerable: false, configurable: true +}); + + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/property.js b/js/src/tests/non262/async-functions/property.js new file mode 100644 index 0000000000..53f779c330 --- /dev/null +++ b/js/src/tests/non262/async-functions/property.js @@ -0,0 +1,49 @@ +var BUGNUMBER = 1185106; +var summary = "async name token in property and object destructuring pattern"; + +print(BUGNUMBER + ": " + summary); + +{ + let a = { async: 10 }; + assertEq(a.async, 10); +} + +{ + let a = { async() {} }; + assertEq(a.async instanceof Function, true); + assertEq(a.async.name, "async"); +} + +{ + let async = 11; + let a = { async }; + assertEq(a.async, 11); +} + +{ + let { async } = { async: 12 }; + assertEq(async, 12); +} + +{ + let { async = 13 } = {}; + assertEq(async, 13); +} + +{ + let { async: a = 14 } = {}; + assertEq(a, 14); +} + +{ + let { async, other } = { async: 15, other: 16 }; + assertEq(async, 15); + assertEq(other, 16); + + let a = { async, other }; + assertEq(a.async, 15); + assertEq(a.other, 16); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/semantics.js b/js/src/tests/non262/async-functions/semantics.js new file mode 100644 index 0000000000..5814890a63 --- /dev/null +++ b/js/src/tests/non262/async-functions/semantics.js @@ -0,0 +1,169 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue +var BUGNUMBER = 1185106; +var summary = "async functions semantics"; + +print(BUGNUMBER + ": " + summary); + +async function empty() { +} +assertEventuallyEq(empty(), undefined); + +async function simpleReturn() { + return 1; +} +assertEventuallyEq(simpleReturn(), 1); + +async function simpleAwait() { + var result = await 2; + return result; +} +assertEventuallyEq(simpleAwait(), 2); + +async function simpleAwaitAsync() { + var result = await simpleReturn(); + return 2 + result; +} +assertEventuallyEq(simpleAwaitAsync(), 3); + +async function returnOtherAsync() { + return 1 + await simpleAwaitAsync(); +} +assertEventuallyEq(returnOtherAsync(), 4); + +async function simpleThrower() { + throw new Error(); +} +assertEventuallyThrows(simpleThrower(), Error); + +async function delegatedThrower() { + var val = await simpleThrower(); + return val; +} + +async function tryCatch() { + try { + await delegatedThrower(); + return 'FAILED'; + } catch (_) { + return 5; + } +} +assertEventuallyEq(tryCatch(), 5); + +async function tryCatchThrow() { + try { + await delegatedThrower(); + return 'FAILED'; + } catch (_) { + return delegatedThrower(); + } +} +assertEventuallyThrows(tryCatchThrow(), Error); + +async function wellFinally() { + try { + await delegatedThrower(); + } catch (_) { + return 'FAILED'; + } finally { + return 6; + } +} +assertEventuallyEq(wellFinally(), 6); + +async function finallyMayFail() { + try { + await delegatedThrower(); + } catch (_) { + return 5; + } finally { + return delegatedThrower(); + } +} +assertEventuallyThrows(finallyMayFail(), Error); + +async function embedded() { + async function inner() { + return 7; + } + return await inner(); +} +assertEventuallyEq(embedded(), 7); + +// recursion, it works! +async function fib(n) { + return (n == 0 || n == 1) ? n : await fib(n - 1) + await fib(n - 2); +} +assertEventuallyEq(fib(6), 8); + +// mutual recursion +async function isOdd(n) { + async function isEven(n) { + return n === 0 || await isOdd(n - 1); + } + return n !== 0 && await isEven(n - 1); +} +assertEventuallyEq(isOdd(12).then(v => v ? "oops" : 12), 12); + +// recursion, take three! +var hardcoreFib = async function fib2(n) { + return (n == 0 || n == 1) ? n : await fib2(n - 1) + await fib2(n - 2); +} +assertEventuallyEq(hardcoreFib(7), 13); + +var asyncExpr = async function() { + return 10; +} +assertEventuallyEq(asyncExpr(), 10); + +var namedAsyncExpr = async function simple() { + return 11; +} +assertEventuallyEq(namedAsyncExpr(), 11); + +async function executionOrder() { + var value = 0; + async function first() { + return (value = value === 0 ? 1 : value); + } + async function second() { + return (value = value === 0 ? 2 : value); + } + async function third() { + return (value = value === 0 ? 3 : value); + } + return await first() + await second() + await third() + 6; +} +assertEventuallyEq(executionOrder(), 9); + +async function miscellaneous() { + if (arguments.length === 3 && + arguments.callee.name === "miscellaneous") + return 14; +} +assertEventuallyEq(miscellaneous(1, 2, 3), 14); + +function thrower() { + throw 15; +} + +async function defaultArgs(arg = thrower()) { +} +assertEventuallyEq(defaultArgs().catch(e => e), 15); + +let arrowAwaitExpr = async () => await 2; +assertEventuallyEq(arrowAwaitExpr(), 2); + +let arrowAwaitBlock = async () => { return await 2; }; +assertEventuallyEq(arrowAwaitBlock(), 2); + +// Async functions are not constructible +assertThrowsInstanceOf(() => { + async function Person() { + + } + new Person(); +}, TypeError); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/shell.js b/js/src/tests/non262/async-functions/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/non262/async-functions/shell.js diff --git a/js/src/tests/non262/async-functions/subclass.js b/js/src/tests/non262/async-functions/subclass.js new file mode 100644 index 0000000000..da20ab19b8 --- /dev/null +++ b/js/src/tests/non262/async-functions/subclass.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const AsyncFunction = async function(){}.constructor; + +class MyAsync extends AsyncFunction {} + +// MyGen inherits from %AsyncFunction%. +assertEq(Object.getPrototypeOf(MyAsync), AsyncFunction); + +// MyGen.prototype inherits from %AsyncFunctionPrototype%. +assertEq(Object.getPrototypeOf(MyAsync.prototype), AsyncFunction.prototype); + +var fn = new MyAsync("return await 'ok';"); + +// fn inherits from MyAsync.prototype. +assertEq(Object.getPrototypeOf(fn), MyAsync.prototype); + +// Ensure the new async function can be executed. +var promise = fn(); + +// promise inherits from %Promise.prototype%. +assertEq(Object.getPrototypeOf(promise), Promise.prototype); + +// Computes the expected result. +assertEventuallyEq(promise, "ok"); + +if (typeof reportCompare === "function") + reportCompare(0, 0); diff --git a/js/src/tests/non262/async-functions/syntax-arrow.js b/js/src/tests/non262/async-functions/syntax-arrow.js new file mode 100644 index 0000000000..a2c96e7dd9 --- /dev/null +++ b/js/src/tests/non262/async-functions/syntax-arrow.js @@ -0,0 +1,104 @@ +var BUGNUMBER = 1185106; +var summary = "async arrow function syntax"; + +print(BUGNUMBER + ": " + summary); + +if (typeof Reflect !== "undefined" && Reflect.parse) { + // Parameters. + Reflect.parse("async () => 1"); + Reflect.parse("async a => 1"); + Reflect.parse("async (a) => 1"); + Reflect.parse("async async => 1"); + Reflect.parse("async (async) => 1"); + Reflect.parse("async ([a]) => 1"); + Reflect.parse("async ([a, b]) => 1"); + Reflect.parse("async ({a}) => 1"); + Reflect.parse("async ({a, b}) => 1"); + + assertThrowsInstanceOf(() => Reflect.parse("async await => 1"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async (await) => 1"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async ([await]) => 1"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async ({await}) => 1"), SyntaxError); + + assertThrowsInstanceOf(() => Reflect.parse("async (a=await) => 1"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async ([a=await]) => 1"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async ({a=await}) => 1"), SyntaxError); + + assertThrowsInstanceOf(() => Reflect.parse("async (a=await 1) => 1"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async ([a=await 1]) => 1"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async ({a=await 1}) => 1"), SyntaxError); + + assertThrowsInstanceOf(() => Reflect.parse("async [a] => 1"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async [a, b] => 1"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async {a} => 1"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async {a: b} => 1"), SyntaxError); + + // Expression body. + Reflect.parse("async a => a == b"); + + // Expression body with nested async function. + Reflect.parse("async a => async"); + Reflect.parse("async a => async b => c"); + Reflect.parse("async a => async function() {}"); + Reflect.parse("async a => async function b() {}"); + + assertThrowsInstanceOf(() => Reflect.parse("async a => async b"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async a => async function"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async a => async function()"), SyntaxError); + + // Expression body with `await`. + Reflect.parse("async a => await 1"); + Reflect.parse("async a => await await 1"); + Reflect.parse("async a => await await await 1"); + + assertThrowsInstanceOf(() => Reflect.parse("async a => await"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async a => await await"), SyntaxError); + + // `await` is Unary Expression and it cannot have `async` function as an + // operand. + assertThrowsInstanceOf(() => Reflect.parse("async a => await async X => Y"), SyntaxError); + Reflect.parse("async a => await (async X => Y)"); + // But it can have `async` identifier as an operand. + Reflect.parse("async async => await async"); + + // Block body. + Reflect.parse("async X => {yield}"); + + // `yield` handling. + Reflect.parse("async X => yield"); + Reflect.parse("async yield => X"); + Reflect.parse("async yield => yield"); + Reflect.parse("async X => {yield}"); + + Reflect.parse("async X => {yield}"); + Reflect.parse("async yield => {X}"); + Reflect.parse("async yield => {yield}"); + Reflect.parse("function* g() { async X => yield }"); + + assertThrowsInstanceOf(() => Reflect.parse("'use strict'; async yield => X"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("'use strict'; async (yield) => X"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("'use strict'; async X => yield"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("'use strict'; async X => {yield}"), SyntaxError); + + assertThrows(() => Reflect.parse("function* g() { async yield => X }")); + assertThrows(() => Reflect.parse("function* g() { async (yield) => X }")); + assertThrows(() => Reflect.parse("function* g() { async ([yield]) => X }")); + assertThrows(() => Reflect.parse("function* g() { async ({yield}) => X }")); + + // Not async functions. + Reflect.parse("async ()"); + Reflect.parse("async (a)"); + Reflect.parse("async (async)"); + Reflect.parse("async ([a])"); + Reflect.parse("async ([a, b])"); + Reflect.parse("async ({a})"); + Reflect.parse("async ({a, b})"); + + // Async arrow function is assignment expression. + Reflect.parse("a ? async () => {1} : b"); + Reflect.parse("a ? b : async () => {1}"); + assertThrowsInstanceOf(() => Reflect.parse("async () => {1} ? a : b"), SyntaxError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/syntax-modules.js b/js/src/tests/non262/async-functions/syntax-modules.js new file mode 100644 index 0000000000..265ece9264 --- /dev/null +++ b/js/src/tests/non262/async-functions/syntax-modules.js @@ -0,0 +1,28 @@ +var BUGNUMBER = 1185106; +var summary = "async/await syntax in module"; + +print(BUGNUMBER + ": " + summary); + +if (typeof parseModule === "function") { + parseModule("async function f() { await 3; }"); + parseModule("async function f() { await 3; }"); + assertThrowsInstanceOf(() => parseModule("function f() { await 5; }"), SyntaxError); + assertThrowsInstanceOf(() => parseModule("() => { await 5; }"), SyntaxError); + assertThrowsInstanceOf(() => parseModule("export var await;"), SyntaxError); + assertThrowsInstanceOf(() => parseModule("await => 1;"), SyntaxError); + assertThrowsInstanceOf(() => parseModule("async function f() { function g() { await 3; } }"), SyntaxError); + + if (typeof Reflect !== "undefined" && Reflect.parse) { + Reflect.parse("export async function f() {}", { target: "module" }); + assertThrowsInstanceOf(() => Reflect.parse("export async function() {}", { target: "module" }), SyntaxError); + + Reflect.parse("export default async function() {}", { target: "module" }); + Reflect.parse("export default async function f() {}", { target: "module" }); + + assertThrowsInstanceOf(() => Reflect.parse("export default async function() { yield; }", { target: "module" }), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("export default async function() { yield = 1; }", { target: "module" }), SyntaxError); + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/syntax.js b/js/src/tests/non262/async-functions/syntax.js new file mode 100644 index 0000000000..a5ec254e2e --- /dev/null +++ b/js/src/tests/non262/async-functions/syntax.js @@ -0,0 +1,83 @@ +var BUGNUMBER = 1185106; +var summary = "async/await syntax"; + +print(BUGNUMBER + ": " + summary); + +if (typeof Reflect !== "undefined" && Reflect.parse) { + assertEq(Reflect.parse("function a() {}").body[0].async, false); + assertEq(Reflect.parse("function* a() {}").body[0].async, false); + assertEq(Reflect.parse("async function a() {}").body[0].async, true); + assertEq(Reflect.parse("() => {}").body[0].async, undefined); + + // No line terminator after async + assertEq(Reflect.parse("async\nfunction a(){}").body[0].expression.name, "async"); + + // Async function expressions + assertEq(Reflect.parse("(async function() {})()").body[0].expression.callee.async, true); + assertEq(Reflect.parse("var k = async function() {}").body[0].declarations[0].init.async, true); + assertEq(Reflect.parse("var nmd = async function named() {}").body[0].declarations[0].init.id.name, "named"); + + // `await` handling for function declaration name inherits. + assertEq(Reflect.parse("async function await() {}").body[0].id.name, "await"); + assertThrowsInstanceOf(() => Reflect.parse("async function f() { async function await() {} }"), SyntaxError); + + // `await` is not allowed in function expression name. + assertThrowsInstanceOf(() => Reflect.parse("(async function await() {})"), SyntaxError); + + // Awaiting not directly inside an async function is not allowed + assertThrowsInstanceOf(() => Reflect.parse("await 4;"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("function a() { await 4; }"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("function* a() { await 4; }"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async function k() { function a() { await 4; } }"), SyntaxError); + + // Await is not allowed as a default expr. + assertThrowsInstanceOf(() => Reflect.parse("async function a(k = await 3) {}"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async function a() { async function b(k = await 3) {} }"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async function a() { async function b(k = [await 3]) {} }"), SyntaxError); + + assertThrowsInstanceOf(() => Reflect.parse("async function a() { async function b([k = await 3]) {} }"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async function a() { async function b([k = [await 3]]) {} }"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async function a() { async function b({k = await 3}) {} }"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async function a() { async function b({k = [await 3]}) {} }"), SyntaxError); + + // Await is not legal as an identifier in an async function. + assertThrowsInstanceOf(() => Reflect.parse("async function a() { var await = 4; }"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("async function a() { return await; }"), SyntaxError); + + // Await is still available as an identifier name in strict mode code. + Reflect.parse("function a() { 'use strict'; var await = 3; }"); + Reflect.parse("'use strict'; var await = 3;"); + + // Await is treated differently depending on context. Various cases. + Reflect.parse("var await = 3; async function a() { await 4; }"); + Reflect.parse("async function a() { await 4; } var await = 5"); + Reflect.parse("async function a() { function b() { return await; } }"); + + Reflect.parse("async function a() { var k = { async: 4 } }"); + + Reflect.parse("function a() { await: 4 }"); + + assertEq(Reflect.parse("async function a() { await 4; }") + .body[0].body.body[0].expression.operator, "await"); + + assertEq(Reflect.parse("async function a() { async function b() { await 4; } }") + .body[0].body.body[0].body.body[0].expression.operator, "await"); + + // operator priority test + assertEq(Reflect.parse("async function a() { await 2 + 3; }") + .body[0].body.body[0].expression.left.argument.value, 2); + assertEq(Reflect.parse("async function a() { await 2 + 3; }") + .body[0].body.body[0].expression.left.operator, "await"); + assertEq(Reflect.parse("async function a() { await 2 + 3; }") + .body[0].body.body[0].expression.right.value, 3); + + // blocks and other constructions + assertEq(Reflect.parse("{ async function a() { return 2; } }") + .body[0].body[0].async, true); + + // Async function expression is primary expression. + Reflect.parse("(async function a() {}.constructor)"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/toSource.js b/js/src/tests/non262/async-functions/toSource.js new file mode 100644 index 0000000000..3cd2943aec --- /dev/null +++ b/js/src/tests/non262/async-functions/toSource.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!Function.prototype.toSource) + +var BUGNUMBER = 1335025; +var summary = "(non-standard) async function toSource"; + +print(BUGNUMBER + ": " + summary); + +async function f1(a, b, c) { await a; } + +assertEq(f1.toSource(), + "async function f1(a, b, c) { await a; }"); + +assertEq(async function (a, b, c) { await a; }.toSource(), + "(async function (a, b, c) { await a; })"); + +assertEq((async (a, b, c) => await a).toSource(), + "async (a, b, c) => await a"); + +assertEq((async (a, b, c) => { await a; }).toSource(), + "async (a, b, c) => { await a; }"); + +assertEq({ async foo(a, b, c) { await a; } }.foo.toSource(), + "async foo(a, b, c) { await a; }"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/toString.js b/js/src/tests/non262/async-functions/toString.js new file mode 100644 index 0000000000..a4ecd7b653 --- /dev/null +++ b/js/src/tests/non262/async-functions/toString.js @@ -0,0 +1,24 @@ +var BUGNUMBER = 1185106; +var summary = "async function toString"; + +print(BUGNUMBER + ": " + summary); + +async function f1(a, b, c) { await a; } + +assertEq(f1.toString(), + "async function f1(a, b, c) { await a; }"); + +assertEq(async function (a, b, c) { await a; }.toString(), + "async function (a, b, c) { await a; }"); + +assertEq((async (a, b, c) => await a).toString(), + "async (a, b, c) => await a"); + +assertEq((async (a, b, c) => { await a; }).toString(), + "async (a, b, c) => { await a; }"); + +assertEq({ async foo(a, b, c) { await a; } }.foo.toString(), + "async foo(a, b, c) { await a; }"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/non262/async-functions/yield.js b/js/src/tests/non262/async-functions/yield.js new file mode 100644 index 0000000000..1d5b836a33 --- /dev/null +++ b/js/src/tests/non262/async-functions/yield.js @@ -0,0 +1,71 @@ +var BUGNUMBER = 1185106; +var summary = "yield handling in async function"; + +print(BUGNUMBER + ": " + summary); + +function testPassArgsBody(argsbody) { + Reflect.parse(`async function a${argsbody}`); + Reflect.parse(`(async function a${argsbody})`); + Reflect.parse(`(async function ${argsbody})`); + Reflect.parse(`({ async m${argsbody} })`); +} + +function testErrorArgsBody(argsbody, prefix="") { + assertThrowsInstanceOf(() => Reflect.parse(`${prefix} async function a${argsbody}`), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse(`${prefix} (async function a${argsbody})`), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse(`${prefix} (async function ${argsbody})`), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse(`${prefix} ({ async m${argsbody} })`), SyntaxError); +} + +function testErrorArgsBodyStrict(argsbody) { + testErrorArgsBody(argsbody); + testErrorArgsBody(argsbody, "'use strict'; "); + assertThrowsInstanceOf(() => Reflect.parse(`class X { async m${argsbody} }`), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse(`class X { static async m${argsbody} }`), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse(`export default async function ${argsbody}`, { target: "module" }), SyntaxError); +} + +if (typeof Reflect !== "undefined" && Reflect.parse) { + // `yield` handling is inherited in async function declaration name. + Reflect.parse("async function yield() {}"); + Reflect.parse("function f() { async function yield() {} }"); + assertThrowsInstanceOf(() => Reflect.parse("function* g() { async function yield() {} }"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("'use strict'; async function yield() {}"), SyntaxError); + + // `yield` is treated as an identifier in an async function expression name. + // `yield` is not allowed as an identifier in strict code. + Reflect.parse("(async function yield() {});"); + Reflect.parse("function f() { (async function yield() {}); }"); + Reflect.parse("function* g() { (async function yield() {}); }"); + assertThrowsInstanceOf(() => Reflect.parse("'use strict'; (async function yield() {});"), SyntaxError); + + // `yield` handling is inherited in async method name. + Reflect.parse("({ async yield() {} });"); + Reflect.parse("function f() { ({ async yield() {} }); }"); + Reflect.parse("function* g() { ({ async yield() {} }); }"); + Reflect.parse("'use strict'; ({ async yield() {} });"); + Reflect.parse("class X { async yield() {} }"); + + Reflect.parse("({ async [yield]() {} });"); + Reflect.parse("function f() { ({ async [yield]() {} }); }"); + Reflect.parse("function* g() { ({ async [yield]() {} }); }"); + assertThrowsInstanceOf(() => Reflect.parse("'use strict'; ({ async [yield]() {} });"), SyntaxError); + assertThrowsInstanceOf(() => Reflect.parse("class X { async [yield]() {} }"), SyntaxError); + + // `yield` is treated as an identifier in an async function parameter + // `yield` is not allowed as an identifier in strict code. + testPassArgsBody("(yield) {}"); + testPassArgsBody("(yield = 1) {}"); + testPassArgsBody("(a = yield) {}"); + testErrorArgsBodyStrict("(yield 3) {}"); + testErrorArgsBodyStrict("(a = yield 3) {}"); + + // `yield` is treated as an identifier in an async function body + // `yield` is not allowed as an identifier in strict code. + testPassArgsBody("() { yield; }"); + testPassArgsBody("() { yield = 1; }"); + testErrorArgsBodyStrict("() { yield 3; }"); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); |