#include "vm/AsyncFunction.h"
#include "mozilla/Maybe.h"
#include "builtin/Promise.h"
#include "vm/GeneratorObject.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/Realm.h"
#include "vm/SelfHosting.h"
#include "vm/JSObject-inl.h"
using namespace js;
using mozilla::Maybe;
bool GlobalObject::initAsyncFunction(JSContext* cx,
Handle<GlobalObject*> global) {
if (global->getReservedSlot(ASYNC_FUNCTION_PROTO).isObject()) {
return true;
}
RootedObject asyncFunctionProto(
cx, NewSingletonObjectWithFunctionPrototype(cx, global));
if (!asyncFunctionProto) {
return false;
}
if (!DefineToStringTag(cx, asyncFunctionProto, cx->names().AsyncFunction)) {
return false;
}
RootedValue function(cx, global->getConstructor(JSProto_Function));
if (!function.toObjectOrNull()) {
return false;
}
RootedObject proto(cx, &function.toObject());
RootedAtom name(cx, cx->names().AsyncFunction);
RootedObject asyncFunction(
cx, NewFunctionWithProto(cx, AsyncFunctionConstructor, 1,
JSFunction::NATIVE_CTOR, nullptr, name, proto));
if (!asyncFunction) {
return false;
}
if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto,
JSPROP_PERMANENT | JSPROP_READONLY,
JSPROP_READONLY)) {
return false;
}
global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction));
global->setReservedSlot(ASYNC_FUNCTION_PROTO,
ObjectValue(*asyncFunctionProto));
return true;
}
enum class ResumeKind { Normal, Throw };
static bool AsyncFunctionResume(JSContext* cx,
Handle<AsyncFunctionGeneratorObject*> generator,
ResumeKind kind, HandleValue valueOrReason) {
if (generator->isClosed()) {
return true;
}
Rooted<PromiseObject*> resultPromise(cx, generator->promise());
RootedObject stack(cx);
Maybe<JS::AutoSetAsyncStackForNewCalls> asyncStack;
if (JSObject* allocationSite = resultPromise->allocationSite()) {
stack = allocationSite->as<SavedFrame>().getParent();
if (stack) {
asyncStack.emplace(
cx, stack, "async",
JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
}
}
MOZ_ASSERT(generator->isSuspended(),
"non-suspended generator when resuming async function");
HandlePropertyName funName = kind == ResumeKind::Normal
? cx->names().AsyncFunctionNext
: cx->names().AsyncFunctionThrow;
FixedInvokeArgs<1> args(cx);
args[0].set(valueOrReason);
RootedValue generatorOrValue(cx, ObjectValue(*generator));
if (!CallSelfHostedFunction(cx, funName, generatorOrValue, args,
&generatorOrValue)) {
if (!generator->isClosed()) {
generator->setClosed();
if (cx->isExceptionPending()) {
RootedValue exn(cx);
if (!GetAndClearException(cx, &exn)) {
return false;
}
return AsyncFunctionThrown(cx, resultPromise, exn);
}
}
return false;
}
MOZ_ASSERT_IF(generator->isClosed(), generatorOrValue.isObject());
MOZ_ASSERT_IF(generator->isClosed(),
&generatorOrValue.toObject() == resultPromise);
MOZ_ASSERT_IF(!generator->isClosed(), generator->isAfterAwait());
return true;
}
MOZ_MUST_USE bool js::AsyncFunctionAwaitedFulfilled(
JSContext* cx, Handle<AsyncFunctionGeneratorObject*> generator,
HandleValue value) {
return AsyncFunctionResume(cx, generator, ResumeKind::Normal, value);
}
MOZ_MUST_USE bool js::AsyncFunctionAwaitedRejected(
JSContext* cx, Handle<AsyncFunctionGeneratorObject*> generator,
HandleValue reason) {
return AsyncFunctionResume(cx, generator, ResumeKind::Throw, reason);
}
JSObject* js::AsyncFunctionResolve(
JSContext* cx, Handle<AsyncFunctionGeneratorObject*> generator,
HandleValue valueOrReason, AsyncFunctionResolveKind resolveKind) {
Rooted<PromiseObject*> promise(cx, generator->promise());
if (resolveKind == AsyncFunctionResolveKind::Fulfill) {
if (!AsyncFunctionReturned(cx, promise, valueOrReason)) {
return nullptr;
}
} else {
if (!AsyncFunctionThrown(cx, promise, valueOrReason)) {
return nullptr;
}
}
return promise;
}
const Class AsyncFunctionGeneratorObject::class_ = {
"AsyncFunctionGenerator",
JSCLASS_HAS_RESERVED_SLOTS(AsyncFunctionGeneratorObject::RESERVED_SLOTS)};
AsyncFunctionGeneratorObject* AsyncFunctionGeneratorObject::create(
JSContext* cx, HandleFunction fun) {
MOZ_ASSERT(fun->isAsync() && !fun->isGenerator());
Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx));
if (!resultPromise) {
return nullptr;
}
auto* obj = NewBuiltinClassInstance<AsyncFunctionGeneratorObject>(cx);
if (!obj) {
return nullptr;
}
obj->initFixedSlot(PROMISE_SLOT, ObjectValue(*resultPromise));
obj->setResumeIndex(AbstractGeneratorObject::RESUME_INDEX_RUNNING);
return obj;
}