LCOV - code coverage report
Current view: top level - js/src/vm - AsyncFunction.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 77 105 73.3 %
Date: 2017-07-14 16:53:18 Functions: 10 11 90.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "vm/AsyncFunction.h"
       8             : 
       9             : #include "mozilla/Maybe.h"
      10             : 
      11             : #include "jscompartment.h"
      12             : 
      13             : #include "builtin/Promise.h"
      14             : #include "vm/GeneratorObject.h"
      15             : #include "vm/GlobalObject.h"
      16             : #include "vm/Interpreter.h"
      17             : #include "vm/SelfHosting.h"
      18             : 
      19             : using namespace js;
      20             : using namespace js::gc;
      21             : 
      22             : using mozilla::Maybe;
      23             : 
      24             : /* static */ bool
      25          66 : GlobalObject::initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global)
      26             : {
      27          66 :     if (global->getReservedSlot(ASYNC_FUNCTION_PROTO).isObject())
      28           0 :         return true;
      29             : 
      30         132 :     RootedObject asyncFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
      31          66 :     if (!asyncFunctionProto)
      32           0 :         return false;
      33             : 
      34          66 :     if (!DefineToStringTag(cx, asyncFunctionProto, cx->names().AsyncFunction))
      35           0 :         return false;
      36             : 
      37         132 :     RootedValue function(cx, global->getConstructor(JSProto_Function));
      38          66 :     if (!function.toObjectOrNull())
      39           0 :         return false;
      40         132 :     RootedObject proto(cx, &function.toObject());
      41         132 :     RootedAtom name(cx, cx->names().AsyncFunction);
      42         132 :     RootedObject asyncFunction(cx, NewFunctionWithProto(cx, AsyncFunctionConstructor, 1,
      43             :                                                         JSFunction::NATIVE_CTOR, nullptr, name,
      44         132 :                                                         proto));
      45          66 :     if (!asyncFunction)
      46           0 :         return false;
      47         132 :     if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto,
      48         132 :                                      JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
      49             :     {
      50           0 :         return false;
      51             :     }
      52             : 
      53          66 :     global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction));
      54          66 :     global->setReservedSlot(ASYNC_FUNCTION_PROTO, ObjectValue(*asyncFunctionProto));
      55          66 :     return true;
      56             : }
      57             : 
      58             : static MOZ_MUST_USE bool AsyncFunctionStart(JSContext* cx, Handle<PromiseObject*> resultPromise,
      59             :                                             HandleValue generatorVal);
      60             : 
      61             : #define UNWRAPPED_ASYNC_WRAPPED_SLOT 1
      62             : #define WRAPPED_ASYNC_UNWRAPPED_SLOT 0
      63             : 
      64             : // Async Functions proposal 1.1.8 and 1.2.14.
      65             : static bool
      66          52 : WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp)
      67             : {
      68          52 :     CallArgs args = CallArgsFromVp(argc, vp);
      69             : 
      70         104 :     RootedFunction wrapped(cx, &args.callee().as<JSFunction>());
      71         104 :     RootedValue unwrappedVal(cx, wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT));
      72         104 :     RootedFunction unwrapped(cx, &unwrappedVal.toObject().as<JSFunction>());
      73         104 :     RootedValue thisValue(cx, args.thisv());
      74             : 
      75             :     // Step 2.
      76             :     // Also does a part of 2.2 steps 1-2.
      77         104 :     RootedValue generatorVal(cx);
      78         104 :     InvokeArgs args2(cx);
      79          52 :     if (!args2.init(cx, argc))
      80           0 :         return false;
      81          68 :     for (size_t i = 0, len = argc; i < len; i++)
      82          16 :         args2[i].set(args[i]);
      83          52 :     if (Call(cx, unwrappedVal, thisValue, args2, &generatorVal)) {
      84             :         // Step 1.
      85         104 :         Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx, generatorVal));
      86          52 :         if (!resultPromise)
      87           0 :             return false;
      88             : 
      89             :         // Step 3.
      90          52 :         if (!AsyncFunctionStart(cx, resultPromise, generatorVal))
      91           0 :             return false;
      92             : 
      93             :         // Step 5.
      94          52 :         args.rval().setObject(*resultPromise);
      95          52 :         return true;
      96             :     }
      97             : 
      98           0 :     if (!cx->isExceptionPending())
      99           0 :         return false;
     100             : 
     101             :     // Steps 1, 4.
     102           0 :     RootedValue exc(cx);
     103           0 :     if (!GetAndClearException(cx, &exc))
     104           0 :         return false;
     105           0 :     RootedObject rejectPromise(cx, PromiseObject::unforgeableReject(cx, exc));
     106           0 :     if (!rejectPromise)
     107           0 :         return false;
     108             : 
     109             :     // Step 5.
     110           0 :     args.rval().setObject(*rejectPromise);
     111           0 :     return true;
     112             : }
     113             : 
     114             : // Async Functions proposal 2.1 steps 1, 3 (partially).
     115             : // In the spec it creates a function, but we create 2 functions `unwrapped` and
     116             : // `wrapped`.  `unwrapped` is a generator that corresponds to
     117             : //  the async function's body, replacing `await` with `yield`.  `wrapped` is a
     118             : // function that is visible to the outside, and handles yielded values.
     119             : JSObject*
     120         260 : js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto)
     121             : {
     122         260 :     MOZ_ASSERT(unwrapped->isAsync());
     123         260 :     MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default"
     124             :                       "%FunctionPrototype% fallback in NewFunctionWithProto().");
     125             : 
     126             :     // Create a new function with AsyncFunctionPrototype, reusing the name and
     127             :     // the length of `unwrapped`.
     128             : 
     129         520 :     RootedAtom funName(cx, unwrapped->explicitName());
     130             :     uint16_t length;
     131         260 :     if (!JSFunction::getLength(cx, unwrapped, &length))
     132           0 :         return nullptr;
     133             : 
     134             :     // Steps 3 (partially).
     135         520 :     RootedFunction wrapped(cx, NewFunctionWithProto(cx, WrappedAsyncFunction, length,
     136             :                                                     JSFunction::NATIVE_FUN, nullptr,
     137             :                                                     funName, proto,
     138             :                                                     AllocKind::FUNCTION_EXTENDED,
     139         520 :                                                     TenuredObject));
     140         260 :     if (!wrapped)
     141           0 :         return nullptr;
     142             : 
     143         260 :     if (unwrapped->hasCompileTimeName())
     144          10 :         wrapped->setCompileTimeName(unwrapped->compileTimeName());
     145             : 
     146             :     // Link them to each other to make GetWrappedAsyncFunction and
     147             :     // GetUnwrappedAsyncFunction work.
     148         260 :     unwrapped->setExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped));
     149         260 :     wrapped->setExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT, ObjectValue(*unwrapped));
     150             : 
     151         260 :     return wrapped;
     152             : }
     153             : 
     154             : JSObject*
     155         260 : js::WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped)
     156             : {
     157         520 :     RootedObject proto(cx, GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global()));
     158         260 :     if (!proto)
     159           0 :         return nullptr;
     160             : 
     161         260 :     return WrapAsyncFunctionWithProto(cx, unwrapped, proto);
     162             : }
     163             : 
     164             : enum class ResumeKind {
     165             :     Normal,
     166             :     Throw
     167             : };
     168             : 
     169             : // Async Functions proposal 2.2 steps 3.f, 3.g.
     170             : // Async Functions proposal 2.2 steps 3.d-e, 3.g.
     171             : // Implemented in js/src/builtin/Promise.cpp
     172             : 
     173             : // Async Functions proposal 2.2 steps 3-8, 2.4 steps 2-7, 2.5 steps 2-7.
     174             : static bool
     175         104 : AsyncFunctionResume(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue generatorVal,
     176             :                     ResumeKind kind, HandleValue valueOrReason)
     177             : {
     178         208 :     RootedObject stack(cx, resultPromise->allocationSite());
     179         208 :     Maybe<JS::AutoSetAsyncStackForNewCalls> asyncStack;
     180         104 :     if (stack) {
     181         196 :         asyncStack.emplace(cx, stack, "async",
     182          98 :                            JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
     183             :     }
     184             : 
     185             :     // Execution context switching is handled in generator.
     186             :     HandlePropertyName funName = kind == ResumeKind::Normal
     187         103 :                                  ? cx->names().StarGeneratorNext
     188         207 :                                  : cx->names().StarGeneratorThrow;
     189         208 :     FixedInvokeArgs<1> args(cx);
     190         104 :     args[0].set(valueOrReason);
     191         208 :     RootedValue value(cx);
     192         104 :     if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &value))
     193           0 :         return AsyncFunctionThrown(cx, resultPromise);
     194             : 
     195         104 :     if (generatorVal.toObject().as<GeneratorObject>().isAfterAwait())
     196          71 :         return AsyncFunctionAwait(cx, resultPromise, value);
     197             : 
     198          33 :     return AsyncFunctionReturned(cx, resultPromise, value);
     199             : }
     200             : 
     201             : // Async Functions proposal 2.2 steps 3-8.
     202             : static MOZ_MUST_USE bool
     203          52 : AsyncFunctionStart(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue generatorVal)
     204             : {
     205          52 :     return AsyncFunctionResume(cx, resultPromise, generatorVal, ResumeKind::Normal, UndefinedHandleValue);
     206             : }
     207             : 
     208             : // Async Functions proposal 2.3 steps 1-8.
     209             : // Implemented in js/src/builtin/Promise.cpp
     210             : 
     211             : // Async Functions proposal 2.4.
     212             : MOZ_MUST_USE bool
     213          51 : js::AsyncFunctionAwaitedFulfilled(JSContext* cx, Handle<PromiseObject*> resultPromise,
     214             :                                   HandleValue generatorVal, HandleValue value)
     215             : {
     216             :     // Step 1 (implicit).
     217             : 
     218             :     // Steps 2-7.
     219          51 :     return AsyncFunctionResume(cx, resultPromise, generatorVal, ResumeKind::Normal, value);
     220             : }
     221             : 
     222             : // Async Functions proposal 2.5.
     223             : MOZ_MUST_USE bool
     224           1 : js::AsyncFunctionAwaitedRejected(JSContext* cx, Handle<PromiseObject*> resultPromise,
     225             :                                  HandleValue generatorVal, HandleValue reason)
     226             : {
     227             :     // Step 1 (implicit).
     228             : 
     229             :     // Step 2-7.
     230           1 :     return AsyncFunctionResume(cx, resultPromise, generatorVal, ResumeKind::Throw, reason);
     231             : }
     232             : 
     233             : JSFunction*
     234           2 : js::GetWrappedAsyncFunction(JSFunction* unwrapped)
     235             : {
     236           2 :     MOZ_ASSERT(unwrapped->isAsync());
     237           2 :     return &unwrapped->getExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT).toObject().as<JSFunction>();
     238             : }
     239             : 
     240             : JSFunction*
     241           0 : js::GetUnwrappedAsyncFunction(JSFunction* wrapped)
     242             : {
     243           0 :     MOZ_ASSERT(IsWrappedAsyncFunction(wrapped));
     244           0 :     JSFunction* unwrapped = &wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT).toObject().as<JSFunction>();
     245           0 :     MOZ_ASSERT(unwrapped->isAsync());
     246           0 :     return unwrapped;
     247             : }
     248             : 
     249             : bool
     250         930 : js::IsWrappedAsyncFunction(JSFunction* fun)
     251             : {
     252         930 :     return fun->maybeNative() == WrappedAsyncFunction;
     253             : }

Generated by: LCOV version 1.13