Bug 1314055 - Part 1: Port async/await implementation from self-hosted JS to C++. r=till
authorTooru Fujisawa <arai_a@mac.com>
Wed, 09 Nov 2016 03:27:49 +0900
changeset 348404 9ced693a9b87d2870f980bfd3e0046439b58115c
parent 348403 d6a3f82cfc8107fc5cba91a89e067e88773ef027
child 348405 2b2960ee6b45cf7e4ae227427b26f6c4fa8cc43f
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1314055
milestone52.0a1
Bug 1314055 - Part 1: Port async/await implementation from self-hosted JS to C++. r=till
js/src/builtin/AsyncFunctions.js
js/src/builtin/SelfHostingDefines.h
js/src/frontend/BytecodeEmitter.cpp
js/src/jsfun.cpp
js/src/moz.build
js/src/vm/AsyncFunction.cpp
js/src/vm/AsyncFunction.h
js/src/vm/CommonPropertyNames.h
js/src/vm/Interpreter.cpp
js/src/vm/Opcodes.h
js/src/vm/SelfHosting.cpp
deleted file mode 100644
--- a/js/src/builtin/AsyncFunctions.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/* 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/. */
-
-// Called when creating async function.
-// See the comment for js::CreateAsyncFunction what unwrapped, wrapped and the
-// return value are.
-function AsyncFunction_wrap(unwrapped) {
-    var wrapper = function() {
-        // The try block is required to handle throws in default arguments properly.
-        try {
-            return AsyncFunction_start(callFunction(std_Function_apply, unwrapped, this, arguments));
-        } catch (e) {
-            var promiseCtor = GetBuiltinConstructor('Promise');
-            return callFunction(Promise_static_reject, promiseCtor, e);
-        }
-    };
-    return CreateAsyncFunction(wrapper, unwrapped);
-}
-
-function AsyncFunction_start(generator) {
-    return AsyncFunction_resume(generator, undefined, generator.next);
-}
-
-function AsyncFunction_resume(gen, v, method) {
-    var promiseCtor = GetBuiltinConstructor('Promise');
-    let result;
-    try {
-        // get back into async function, run to next await point
-        result = callFunction(method, gen, v);
-    } catch (exc) {
-        // The async function itself failed.
-        return callFunction(Promise_static_reject, promiseCtor, exc);
-    }
-
-    if (result.done)
-        return callFunction(Promise_static_resolve, promiseCtor, result.value);
-
-    // If we get here, `await` occurred. `gen` is paused at a yield point.
-    return callFunction(Promise_then,
-                        callFunction(Promise_static_resolve, promiseCtor, result.value),
-                        function(val) {
-                            return AsyncFunction_resume(gen, val, gen.next);
-                        }, function (err) {
-                            return AsyncFunction_resume(gen, err, gen.throw);
-                        });
-}
--- a/js/src/builtin/SelfHostingDefines.h
+++ b/js/src/builtin/SelfHostingDefines.h
@@ -88,19 +88,16 @@
 #define REGEXP_FLAGS_SLOT 2
 
 #define REGEXP_IGNORECASE_FLAG  0x01
 #define REGEXP_GLOBAL_FLAG      0x02
 #define REGEXP_MULTILINE_FLAG   0x04
 #define REGEXP_STICKY_FLAG      0x08
 #define REGEXP_UNICODE_FLAG     0x10
 
-#define ASYNC_WRAPPED_SLOT 1
-#define ASYNC_UNWRAPPED_SLOT 1
-
 #define MODULE_OBJECT_ENVIRONMENT_SLOT 2
 
 #define MODULE_STATE_FAILED       0
 #define MODULE_STATE_PARSED       1
 #define MODULE_STATE_INSTANTIATED 2
 #define MODULE_STATE_EVALUATED    3
 
 #endif
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -7130,59 +7130,49 @@ BytecodeEmitter::emitAsyncWrapperLambda(
         if (!emitIndex32(JSOP_LAMBDA, index))
             return false;
     }
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow) {
+BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow)
+{
     // needsHomeObject can be true for propertyList for extended class.
     // In that case push both unwrapped and wrapped function, in order to
     // initialize home object of unwrapped function, and set wrapped function
     // as a property.
     //
     //   lambda       // unwrapped
-    //   getintrinsic // unwrapped AsyncFunction_wrap
-    //   undefined    // unwrapped AsyncFunction_wrap undefined
-    //   dupat 2      // unwrapped AsyncFunction_wrap undefined unwrapped
-    //   call 1       // unwrapped wrapped
+    //   dup          // unwrapped unwrapped
+    //   toasync      // unwrapped wrapped
     //
     // Emitted code is surrounded by the following code.
     //
     //                    // classObj classCtor classProto
     //   (emitted code)   // classObj classCtor classProto unwrapped wrapped
     //   swap             // classObj classCtor classProto wrapped unwrapped
     //   inithomeobject 1 // classObj classCtor classProto wrapped unwrapped
     //                    //   initialize the home object of unwrapped
     //                    //   with classProto here
     //   pop              // classObj classCtor classProto wrapped
     //   inithiddenprop   // classObj classCtor classProto wrapped
     //                    //   initialize the property of the classProto
     //                    //   with wrapped function here
     //   pop              // classObj classCtor classProto
     //
     // needsHomeObject is false for other cases, push wrapped function only.
-    if (needsHomeObject) {
-        if (!emitAsyncWrapperLambda(index, isArrow))
-            return false;
-    }
-    if (!emitAtomOp(cx->names().AsyncFunction_wrap, JSOP_GETINTRINSIC))
-        return false;
-    if (!emit1(JSOP_UNDEFINED))
+    if (!emitAsyncWrapperLambda(index, isArrow))
         return false;
     if (needsHomeObject) {
-        if (!emitDupAt(2))
-            return false;
-    } else {
-        if (!emitAsyncWrapperLambda(index, isArrow))
-            return false;
-    }
-    if (!emitCall(JSOP_CALL, 1))
+        if (!emit1(JSOP_DUP))
+            return false;
+    }
+    if (!emit1(JSOP_TOASYNC))
         return false;
     return true;
 }
 
 bool
 BytecodeEmitter::emitDo(ParseNode* pn)
 {
     /* Emit an annotated nop so IonBuilder can recognize the 'do' loop. */
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -986,17 +986,17 @@ js::FunctionToString(JSContext* cx, Hand
     if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
         return nullptr;
 
     if (IsAsmJSModule(fun))
         return AsmJSModuleToString(cx, fun, !lambdaParen);
     if (IsAsmJSFunction(fun))
         return AsmJSFunctionToString(cx, fun);
 
-    if (IsWrappedAsyncFunction(cx, fun)) {
+    if (IsWrappedAsyncFunction(fun)) {
         RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun));
         return FunctionToString(cx, unwrapped, lambdaParen);
     }
 
     StringBuffer out(cx);
     RootedScript script(cx);
 
     if (fun->hasScript()) {
@@ -1925,20 +1925,23 @@ js::Generator(JSContext* cx, unsigned ar
 
 bool
 js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!FunctionConstructor(cx, argc, vp, StarGenerator, AsyncFunction))
         return false;
 
-    FixedInvokeArgs<1> args2(cx);
-    args2[0].set(args.rval());
-    return CallSelfHostedFunction(cx, cx->names().AsyncFunction_wrap,
-                                  NullHandleValue, args2, args.rval());
+    RootedFunction unwrapped(cx, &args.rval().toObject().as<JSFunction>());
+    RootedObject wrapped(cx, WrapAsyncFunction(cx, unwrapped));
+    if (!wrapped)
+        return false;
+
+    args.rval().setObject(*wrapped);
+    return true;
 }
 
 bool
 JSFunction::isBuiltinFunctionConstructor()
 {
     return maybeNative() == Function || maybeNative() == Generator;
 }
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -745,17 +745,16 @@ GENERATED_FILES += [('selfhosted.out.h',
 selfhosted = GENERATED_FILES[('selfhosted.out.h', 'selfhosted.js')]
 selfhosted.script = 'builtin/embedjs.py:generate_selfhosted'
 selfhosted.inputs = [
     'js.msg',
     'builtin/TypedObjectConstants.h',
     'builtin/SelfHostingDefines.h',
     'builtin/Utilities.js',
     'builtin/Array.js',
-    'builtin/AsyncFunctions.js',
     'builtin/Classes.js',
     'builtin/Date.js',
     'builtin/Error.js',
     'builtin/Function.js',
     'builtin/Generator.js',
     'builtin/Intl.js',
     'builtin/IntlData.js',
     'builtin/IntlTzData.js',
--- a/js/src/vm/AsyncFunction.cpp
+++ b/js/src/vm/AsyncFunction.cpp
@@ -3,18 +3,19 @@
  * 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/. */
 
 #include "vm/AsyncFunction.h"
 
 #include "jscompartment.h"
 
-#include "builtin/SelfHostingDefines.h"
+#include "builtin/Promise.h"
 #include "vm/GlobalObject.h"
+#include "vm/Interpreter.h"
 #include "vm/SelfHosting.h"
 
 using namespace js;
 using namespace js::gc;
 
 /* static */ bool
 GlobalObject::initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global)
 {
@@ -41,78 +42,272 @@ GlobalObject::initAsyncFunction(JSContex
     if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto))
         return false;
 
     global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction));
     global->setReservedSlot(ASYNC_FUNCTION_PROTO, ObjectValue(*asyncFunctionProto));
     return true;
 }
 
+static bool AsyncFunctionStart(JSContext* cx, HandleValue generatorVal, MutableHandleValue rval);
+
+#define UNWRAPPED_ASYNC_WRAPPED_SLOT 1
+#define WRAPPED_ASYNC_UNWRAPPED_SLOT 0
+
+// Async Functions proposal 1.1.8 and 1.2.14.
+static bool
+WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedFunction wrapped(cx, &args.callee().as<JSFunction>());
+    RootedValue unwrappedVal(cx, wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT));
+    RootedFunction unwrapped(cx, &unwrappedVal.toObject().as<JSFunction>());
+    RootedValue thisValue(cx, args.thisv());
+
+    // Step 2.
+    // Also does a part of 2.2 steps 1-2.
+    RootedValue generatorVal(cx);
+    InvokeArgs args2(cx);
+    if (!args2.init(cx, argc))
+        return false;
+    for (size_t i = 0, len = argc; i < len; i++)
+        args2[i].set(args[i]);
+    if (Call(cx, unwrappedVal, thisValue, args2, &generatorVal)) {
+        // Steps 3, 5.
+        return AsyncFunctionStart(cx, generatorVal, args.rval());
+    }
+
+    // Step 4.
+    RootedValue exc(cx);
+    if (!GetAndClearException(cx, &exc))
+        return false;
+    RootedObject rejectPromise(cx, PromiseObject::unforgeableReject(cx, exc));
+    if (!rejectPromise)
+        return false;
+
+    // Step 5.
+    args.rval().setObject(*rejectPromise);
+    return true;
+}
+
+// Async Functions proposal 2.1 steps 1, 3 (partially).
+// In the spec it creates a function, but we create 2 functions `unwrapped` and
+// `wrapped`.  `unwrapped` is a generator that corresponds to
+//  the async function's body, replacing `await` with `yield`.  `wrapped` is a
+// function that is visible to the outside, and handles yielded values.
+JSObject*
+js::WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped)
+{
+    MOZ_ASSERT(unwrapped->isStarGenerator());
+
+    // Create a new function with AsyncFunctionPrototype, reusing the name and
+    // the length of `unwrapped`.
+
+    // Step 1.
+    RootedObject proto(cx, GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global()));
+    if (!proto)
+        return nullptr;
+
+    RootedAtom funName(cx, unwrapped->name());
+    uint16_t length;
+    if (!unwrapped->getLength(cx, &length))
+        return nullptr;
+
+    // Steps 3 (partially).
+    RootedFunction wrapped(cx, NewFunctionWithProto(cx, WrappedAsyncFunction, length,
+                                                    JSFunction::NATIVE_FUN, nullptr,
+                                                    funName, proto,
+                                                    AllocKind::FUNCTION_EXTENDED,
+                                                    TenuredObject));
+    if (!wrapped)
+        return nullptr;
+
+    // Link them to each other to make GetWrappedAsyncFunction and
+    // GetUnwrappedAsyncFunction work.
+    unwrapped->setExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped));
+    wrapped->setExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT, ObjectValue(*unwrapped));
+
+    return wrapped;
+}
+
+// Async Functions proposal 2.2 steps 3.f, 3.g.
+static bool
+AsyncFunctionThrown(JSContext* cx, MutableHandleValue rval)
+{
+    // Step 3.f.
+    RootedValue exc(cx);
+    if (!GetAndClearException(cx, &exc))
+        return false;
+
+    RootedObject rejectPromise(cx, PromiseObject::unforgeableReject(cx, exc));
+    if (!rejectPromise)
+        return false;
+
+    // Step 3.g.
+    rval.setObject(*rejectPromise);
+    return true;
+}
+
+// Async Functions proposal 2.2 steps 3.d-e, 3.g.
+static bool
+AsyncFunctionReturned(JSContext* cx, HandleValue value, MutableHandleValue rval)
+{
+    // Steps 3.d-e.
+    RootedObject resolveObj(cx, PromiseObject::unforgeableResolve(cx, value));
+    if (!resolveObj)
+        return false;
+
+    // Step 3.g.
+    rval.setObject(*resolveObj);
+    return true;
+}
+
+static bool AsyncFunctionAwait(JSContext* cx, HandleValue generatorVal, HandleValue value,
+                               MutableHandleValue rval);
+
+enum class ResumeKind {
+    Normal,
+    Throw
+};
+
+// Async Functions proposal 2.2 steps 3-8, 2.4 steps 2-7, 2.5 steps 2-7.
+static bool
+AsyncFunctionResume(JSContext* cx, HandleValue generatorVal, ResumeKind kind,
+                    HandleValue valueOrReason, MutableHandleValue rval)
+{
+    // Execution context switching is handled in generator.
+    HandlePropertyName funName = kind == ResumeKind::Normal
+                                 ? cx->names().StarGeneratorNext
+                                 : cx->names().StarGeneratorThrow;
+    FixedInvokeArgs<1> args(cx);
+    args[0].set(valueOrReason);
+    RootedValue result(cx);
+    if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result))
+        return AsyncFunctionThrown(cx, rval);
+
+    RootedObject resultObj(cx, &result.toObject());
+    RootedValue doneVal(cx);
+    RootedValue value(cx);
+    if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
+        return false;
+    if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
+        return false;
+
+    if (doneVal.toBoolean())
+        return AsyncFunctionReturned(cx, value, rval);
+
+    return AsyncFunctionAwait(cx, generatorVal, value, rval);
+}
+
+// Async Functions proposal 2.2 steps 3-8.
+static bool
+AsyncFunctionStart(JSContext* cx, HandleValue generatorVal, MutableHandleValue rval)
+{
+    return AsyncFunctionResume(cx, generatorVal, ResumeKind::Normal, UndefinedHandleValue, rval);
+}
+
+#define AWAITED_FUNC_GENERATOR_SLOT 0
+
+static bool AsyncFunctionAwaitedFulfilled(JSContext* cx, unsigned argc, Value* vp);
+static bool AsyncFunctionAwaitedRejected(JSContext* cx, unsigned argc, Value* vp);
+
+// Async Functions proposal 2.3 steps 1-8.
+static bool
+AsyncFunctionAwait(JSContext* cx, HandleValue generatorVal, HandleValue value,
+                   MutableHandleValue rval)
+{
+    // Step 1 (implicit).
+
+    // Steps 2-3.
+    RootedObject resolveObj(cx, PromiseObject::unforgeableResolve(cx, value));
+    if (!resolveObj)
+        return false;
+
+    Rooted<PromiseObject*> resolvePromise(cx, resolveObj.as<PromiseObject>());
+
+    // Step 4.
+    RootedAtom funName(cx, cx->names().empty);
+    RootedFunction onFulfilled(cx, NewNativeFunction(cx, AsyncFunctionAwaitedFulfilled, 1,
+                                                      funName, gc::AllocKind::FUNCTION_EXTENDED,
+                                                      GenericObject));
+    if (!onFulfilled)
+        return false;
+
+    // Step 5.
+    RootedFunction onRejected(cx, NewNativeFunction(cx, AsyncFunctionAwaitedRejected, 1,
+                                                    funName, gc::AllocKind::FUNCTION_EXTENDED,
+                                                    GenericObject));
+    if (!onRejected)
+        return false;
+
+    // Step 6.
+    onFulfilled->setExtendedSlot(AWAITED_FUNC_GENERATOR_SLOT, generatorVal);
+    onRejected->setExtendedSlot(AWAITED_FUNC_GENERATOR_SLOT, generatorVal);
+
+    // Step 8.
+    RootedValue onFulfilledVal(cx, ObjectValue(*onFulfilled));
+    RootedValue onRejectedVal(cx, ObjectValue(*onRejected));
+    RootedObject resultPromise(cx, OriginalPromiseThen(cx, resolvePromise, onFulfilledVal,
+                                                       onRejectedVal));
+    if (!resultPromise)
+        return false;
+
+    rval.setObject(*resultPromise);
+    return true;
+}
+
+// Async Functions proposal 2.4.
+static bool
+AsyncFunctionAwaitedFulfilled(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 1);
+
+    RootedFunction F(cx, &args.callee().as<JSFunction>());
+    RootedValue value(cx, args[0]);
+
+    // Step 1.
+    RootedValue generatorVal(cx, F->getExtendedSlot(AWAITED_FUNC_GENERATOR_SLOT));
+
+    // Steps 2-7.
+    return AsyncFunctionResume(cx, generatorVal, ResumeKind::Normal, value, args.rval());
+}
+
+// Async Functions proposal 2.5.
+static bool
+AsyncFunctionAwaitedRejected(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 1);
+
+    RootedFunction F(cx, &args.callee().as<JSFunction>());
+    RootedValue reason(cx, args[0]);
+
+    // Step 1.
+    RootedValue generatorVal(cx, F->getExtendedSlot(AWAITED_FUNC_GENERATOR_SLOT));
+
+    // Step 2-7.
+    return AsyncFunctionResume(cx, generatorVal, ResumeKind::Throw, reason, args.rval());
+}
+
 JSFunction*
 js::GetWrappedAsyncFunction(JSFunction* unwrapped)
 {
     MOZ_ASSERT(unwrapped->isAsync());
-    return &unwrapped->getExtendedSlot(ASYNC_WRAPPED_SLOT).toObject().as<JSFunction>();
+    return &unwrapped->getExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT).toObject().as<JSFunction>();
 }
 
 JSFunction*
-js::GetUnwrappedAsyncFunction(JSFunction* wrapper)
+js::GetUnwrappedAsyncFunction(JSFunction* wrapped)
 {
-    JSFunction* unwrapped = &wrapper->getExtendedSlot(ASYNC_UNWRAPPED_SLOT).toObject().as<JSFunction>();
+    MOZ_ASSERT(IsWrappedAsyncFunction(wrapped));
+    JSFunction* unwrapped = &wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT).toObject().as<JSFunction>();
     MOZ_ASSERT(unwrapped->isAsync());
     return unwrapped;
 }
 
 bool
-js::IsWrappedAsyncFunction(JSContext* cx, JSFunction* wrapper)
+js::IsWrappedAsyncFunction(JSFunction* fun)
 {
-    return IsSelfHostedFunctionWithName(wrapper, cx->names().AsyncWrapped);
+    return fun->maybeNative() == WrappedAsyncFunction;
 }
 
-bool
-js::CreateAsyncFunction(JSContext* cx, HandleFunction wrapper, HandleFunction unwrapped,
-                        MutableHandleFunction result)
-{
-    // Create a new function with AsyncFunctionPrototype, reusing the script
-    // and the environment of `wrapper` function, and the name and the length
-    // of `unwrapped` function.
-    RootedObject proto(cx, GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global()));
-    RootedObject scope(cx, wrapper->environment());
-    RootedAtom atom(cx, unwrapped->name());
-    RootedFunction wrapped(cx, NewFunctionWithProto(cx, nullptr, 0,
-                                                    JSFunction::INTERPRETED_LAMBDA,
-                                                    scope, atom, proto,
-                                                    AllocKind::FUNCTION_EXTENDED, TenuredObject));
-    if (!wrapped)
-        return false;
-
-    wrapped->initScript(wrapper->nonLazyScript());
-
-    // Link them each other to make GetWrappedAsyncFunction and
-    // GetUnwrappedAsyncFunction work.
-    unwrapped->setExtendedSlot(ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped));
-    wrapped->setExtendedSlot(ASYNC_UNWRAPPED_SLOT, ObjectValue(*unwrapped));
-
-    // The script of `wrapper` is self-hosted, so `wrapped` should also be
-    // set as self-hosted function.
-    wrapped->setIsSelfHostedBuiltin();
-
-    // Set LAZY_FUNCTION_NAME_SLOT to "AsyncWrapped" to make it detectable in
-    // IsWrappedAsyncFunction.
-    wrapped->setExtendedSlot(LAZY_FUNCTION_NAME_SLOT, StringValue(cx->names().AsyncWrapped));
-
-    // The length of the script of `wrapper` is different than the length of
-    // `unwrapped`.  We should set actual length as resolved length, to avoid
-    // using the length of the script.
-    uint16_t length;
-    if (!unwrapped->getLength(cx, &length))
-        return false;
-
-    RootedValue lengthValue(cx, NumberValue(length));
-    if (!DefineProperty(cx, wrapped, cx->names().length, lengthValue,
-                        nullptr, nullptr, JSPROP_READONLY))
-    {
-        return false;
-    }
-
-    result.set(wrapped);
-    return true;
-}
--- a/js/src/vm/AsyncFunction.h
+++ b/js/src/vm/AsyncFunction.h
@@ -11,20 +11,19 @@
 #include "jsobj.h"
 
 namespace js {
 
 JSFunction*
 GetWrappedAsyncFunction(JSFunction* unwrapped);
 
 JSFunction*
-GetUnwrappedAsyncFunction(JSFunction* wrapper);
+GetUnwrappedAsyncFunction(JSFunction* wrapped);
 
 bool
-IsWrappedAsyncFunction(JSContext* cx, JSFunction* wrapper);
+IsWrappedAsyncFunction(JSFunction* fun);
 
-bool
-CreateAsyncFunction(JSContext* cx, HandleFunction wrapper, HandleFunction unwrapped,
-                    MutableHandleFunction result);
+JSObject*
+WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped);
 
 } // namespace js
 
 #endif /* vm_AsyncFunction_h */
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -25,17 +25,16 @@
     macro(ArraySpeciesCreate, ArraySpeciesCreate, "ArraySpeciesCreate") \
     macro(ArrayToLocaleString, ArrayToLocaleString, "ArrayToLocaleString") \
     macro(ArrayType, ArrayType, "ArrayType") \
     macro(ArrayValues, ArrayValues, "ArrayValues") \
     macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
     macro(as, as, "as") \
     macro(Async, Async, "Async") \
     macro(AsyncFunction, AsyncFunction, "AsyncFunction") \
-    macro(AsyncFunction_wrap, AsyncFunction_wrap, "AsyncFunction_wrap") \
     macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \
     macro(async, async, "async") \
     macro(await, await, "await") \
     macro(Bool8x16, Bool8x16, "Bool8x16") \
     macro(Bool16x8, Bool16x8, "Bool16x8") \
     macro(Bool32x4, Bool32x4, "Bool32x4") \
     macro(Bool64x2, Bool64x2, "Bool64x2") \
     macro(boundWithSpace, boundWithSpace, "bound ") \
@@ -275,16 +274,18 @@
     macro(setPrototypeOf, setPrototypeOf, "setPrototypeOf") \
     macro(shape, shape, "shape") \
     macro(size, size, "size") \
     macro(source, source, "source") \
     macro(SpeciesConstructor, SpeciesConstructor, "SpeciesConstructor") \
     macro(stack, stack, "stack") \
     macro(star, star, "*") \
     macro(starDefaultStar, starDefaultStar, "*default*") \
+    macro(StarGeneratorNext, StarGeneratorNext, "StarGeneratorNext") \
+    macro(StarGeneratorThrow, StarGeneratorThrow, "StarGeneratorThrow") \
     macro(startTimestamp, startTimestamp, "startTimestamp") \
     macro(state, state, "state") \
     macro(static, static_, "static") \
     macro(std_Function_apply, std_Function_apply, "std_Function_apply") \
     macro(sticky, sticky, "sticky") \
     macro(StringIterator, StringIterator, "String Iterator") \
     macro(strings, strings, "strings") \
     macro(StructType, StructType, "StructType") \
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -33,16 +33,17 @@
 #include "jsscript.h"
 #include "jsstr.h"
 
 #include "builtin/Eval.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonAnalysis.h"
+#include "vm/AsyncFunction.h"
 #include "vm/Debugger.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Opcodes.h"
 #include "vm/Scope.h"
 #include "vm/Shape.h"
 #include "vm/Stopwatch.h"
 #include "vm/TraceLogging.h"
 
@@ -1864,17 +1865,16 @@ CASE(EnableInterruptsPseudoOpcode)
     /* Commence executing the actual opcode. */
     SANITY_CHECKS();
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
 CASE(JSOP_NOP_DESTRUCTURING)
-CASE(JSOP_UNUSED149)
 CASE(JSOP_UNUSED182)
 CASE(JSOP_UNUSED183)
 CASE(JSOP_UNUSED187)
 CASE(JSOP_UNUSED192)
 CASE(JSOP_UNUSED209)
 CASE(JSOP_UNUSED210)
 CASE(JSOP_UNUSED211)
 CASE(JSOP_UNUSED219)
@@ -3475,16 +3475,28 @@ CASE(JSOP_LAMBDA_ARROW)
     if (!obj)
         goto error;
 
     MOZ_ASSERT(obj->staticPrototype());
     REGS.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_LAMBDA_ARROW)
 
+CASE(JSOP_TOASYNC)
+{
+    ReservedRooted<JSFunction*> unwrapped(&rootFunction0,
+                                          &REGS.sp[-1].toObject().as<JSFunction>());
+    JSObject* wrapped = WrapAsyncFunction(cx, unwrapped);
+    if (!wrapped)
+        goto error;
+
+    REGS.sp[-1].setObject(*wrapped);
+}
+END_CASE(JSOP_TOASYNC)
+
 CASE(JSOP_CALLEE)
     MOZ_ASSERT(REGS.fp()->isFunctionFrame());
     PUSH_COPY(REGS.fp()->calleev());
 END_CASE(JSOP_CALLEE)
 
 CASE(JSOP_INITPROP_GETTER)
 CASE(JSOP_INITHIDDENPROP_GETTER)
 CASE(JSOP_INITPROP_SETTER)
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1524,17 +1524,25 @@ 1234567890123456789012345678901234567890
      * Push "new.target"
      *
      *   Category: Variables and Scopes
      *   Type: Arguments
      *   Operands:
      *   Stack: => new.target
      */ \
     macro(JSOP_NEWTARGET,  148, "newtarget", NULL,      1,  0,  1,  JOF_BYTE) \
-    macro(JSOP_UNUSED149,  149, "unused149", NULL,      1,  0,  0,  JOF_BYTE) \
+    /*
+     * Pops the top of stack value as 'unwrapped', converts it to async
+     * function 'wrapped', and pushes 'wrapped' back on the stack.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: unwrapped => wrapped
+     */ \
+    macro(JSOP_TOASYNC,       149, "toasync", NULL,       1,  1,  1, JOF_BYTE) \
     /*
      * Pops the top two values 'lval' and 'rval' from the stack, then pushes
      * the result of 'Math.pow(lval, rval)'.
      *   Category: Operators
      *   Type: Arithmetic Operators
      *   Operands:
      *   Stack: lval, rval => (lval ** rval)
      */ \
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -33,17 +33,16 @@
 #include "builtin/TypedObject.h"
 #include "builtin/WeakSetObject.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "jit/AtomicOperations.h"
 #include "jit/InlinableNatives.h"
 #include "js/CharacterEncoding.h"
 #include "js/Date.h"
-#include "vm/AsyncFunction.h"
 #include "vm/Compression.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Interpreter.h"
 #include "vm/RegExpObject.h"
 #include "vm/String.h"
 #include "vm/StringBuffer.h"
 #include "vm/TypedArrayCommon.h"
 #include "vm/WrapperObject.h"
@@ -1860,33 +1859,16 @@ js::ReportIncompatibleSelfHostedMethod(J
         }
         ++iter;
     }
 
     MOZ_ASSERT_UNREACHABLE("How did we not find a useful self-hosted frame?");
     return false;
 }
 
-bool
-intrinsic_CreateAsyncFunction(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() == 2);
-
-    RootedFunction wrapper(cx, &args[0].toObject().as<JSFunction>());
-    RootedFunction unwrapped(cx, &args[1].toObject().as<JSFunction>());
-
-    RootedFunction wrapped(cx);
-    if (!CreateAsyncFunction(cx, wrapper, unwrapped, &wrapped))
-        return false;
-
-    args.rval().setObject(*wrapped);
-    return true;
-}
-
 /**
  * Returns the default locale as a well-formed, but not necessarily canonicalized,
  * BCP-47 language tag.
  */
 static bool
 intrinsic_RuntimeDefaultLocale(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -2276,18 +2258,16 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("MakeDefaultConstructor",  intrinsic_MakeDefaultConstructor,  2,0),
     JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
     JS_FN("_NameForTypedArray",      intrinsic_NameForTypedArray, 1,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("_FinishBoundFunctionInit", intrinsic_FinishBoundFunctionInit, 3,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
     JS_FN("AddContentTelemetry",     intrinsic_AddContentTelemetry,     2,0),
 
-    JS_FN("CreateAsyncFunction",     intrinsic_CreateAsyncFunction,     1,0),
-
     JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing,        0,0,
                     IntrinsicIsConstructing),
     JS_INLINABLE_FN("SubstringKernel", intrinsic_SubstringKernel,       3,0,
                     IntrinsicSubstringKernel),
     JS_INLINABLE_FN("_DefineDataProperty",              intrinsic_DefineDataProperty,      4,0,
                     IntrinsicDefineDataProperty),
     JS_INLINABLE_FN("ObjectHasPrototype",               intrinsic_ObjectHasPrototype,      2,0,
                     IntrinsicObjectHasPrototype),