blob: c6d2ff835626b0112b2557abc565ca0ad9f6be4c [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLibraryPch.h"
using namespace Js;
FunctionInfo JavascriptAsyncFunction::functionInfo(
FORCE_NO_WRITE_BARRIER_TAG(JavascriptAsyncFunction::EntryAsyncFunctionImplementation),
(FunctionInfo::Attributes)(FunctionInfo::DoNotProfile | FunctionInfo::ErrorOnNew));
JavascriptAsyncFunction::JavascriptAsyncFunction(
DynamicType* type,
GeneratorVirtualScriptFunction* scriptFunction) :
JavascriptGeneratorFunction(type, &functionInfo, scriptFunction)
{
DebugOnly(VerifyEntryPoint());
}
JavascriptAsyncFunction* JavascriptAsyncFunction::New(
ScriptContext* scriptContext,
GeneratorVirtualScriptFunction* scriptFunction)
{
return scriptContext->GetLibrary()->CreateAsyncFunction(
functionInfo.GetOriginalEntryPoint(),
scriptFunction);
}
template<>
bool Js::VarIsImpl<JavascriptAsyncFunction>(RecyclableObject* obj)
{
return VarIs<JavascriptFunction>(obj) && (
VirtualTableInfo<JavascriptAsyncFunction>::HasVirtualTable(obj) ||
VirtualTableInfo<CrossSiteObject<JavascriptAsyncFunction>>::HasVirtualTable(obj)
);
}
Var JavascriptAsyncFunction::EntryAsyncFunctionImplementation(
RecyclableObject* function,
CallInfo callInfo, ...)
{
auto* scriptContext = function->GetScriptContext();
PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
auto* library = scriptContext->GetLibrary();
auto* asyncFn = VarTo<JavascriptAsyncFunction>(function);
auto* scriptFn = asyncFn->GetGeneratorVirtualScriptFunction();
auto* generator = library->CreateGenerator(args, scriptFn, library->GetNull());
return BeginAsyncFunctionExecution(generator);
}
JavascriptPromise* JavascriptAsyncFunction::BeginAsyncFunctionExecution(JavascriptGenerator* generator)
{
auto* library = generator->GetLibrary();
auto* scriptContext = generator->GetScriptContext();
auto* promise = library->CreatePromise();
auto* stepFn = library->CreateAsyncSpawnStepFunction(
EntryAsyncSpawnStepNextFunction,
generator,
library->GetUndefined());
JavascriptExceptionObject* exception = nullptr;
JavascriptPromiseResolveOrRejectFunction* resolve;
JavascriptPromiseResolveOrRejectFunction* reject;
JavascriptPromise::InitializePromise(promise, &resolve, &reject, scriptContext);
try
{
AsyncSpawnStep(stepFn, generator, resolve, reject);
}
catch (const JavascriptException& err)
{
exception = err.GetAndClear();
}
if (exception != nullptr)
JavascriptPromise::TryRejectWithExceptionObject(exception, reject, scriptContext);
return promise;
}
Var JavascriptAsyncFunction::EntryAsyncSpawnStepNextFunction(
RecyclableObject* function,
CallInfo callInfo, ...)
{
auto* scriptContext = function->GetScriptContext();
PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
auto* stepFn = VarTo<JavascriptAsyncSpawnStepFunction>(function);
return stepFn->generator->CallGenerator(stepFn->argument, ResumeYieldKind::Normal);
}
Var JavascriptAsyncFunction::EntryAsyncSpawnStepThrowFunction(
RecyclableObject* function,
CallInfo callInfo, ...)
{
auto* scriptContext = function->GetScriptContext();
PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
auto* stepFn = VarTo<JavascriptAsyncSpawnStepFunction>(function);
return stepFn->generator->CallGenerator(stepFn->argument, ResumeYieldKind::Throw);
}
Var JavascriptAsyncFunction::EntryAsyncSpawnCallStepFunction(
RecyclableObject* function,
CallInfo callInfo, ...)
{
auto* scriptContext = function->GetScriptContext();
PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
auto* library = scriptContext->GetLibrary();
Var undefinedVar = library->GetUndefined();
Var resolvedValue = args.Info.Count > 1 ? args[1] : undefinedVar;
auto* stepFn = VarTo<JavascriptAsyncSpawnStepFunction>(function);
JavascriptMethod method = stepFn->isReject
? EntryAsyncSpawnStepThrowFunction
: EntryAsyncSpawnStepNextFunction;
auto* nextStepFn = library->CreateAsyncSpawnStepFunction(
method,
stepFn->generator,
resolvedValue);
AsyncSpawnStep(nextStepFn, stepFn->generator, stepFn->resolve, stepFn->reject);
return undefinedVar;
}
void JavascriptAsyncFunction::AsyncSpawnStep(
JavascriptAsyncSpawnStepFunction* stepFunction,
JavascriptGenerator* generator,
Var resolve,
Var reject)
{
ScriptContext* scriptContext = generator->GetScriptContext();
BEGIN_SAFE_REENTRANT_REGION(scriptContext->GetThreadContext())
JavascriptLibrary* library = scriptContext->GetLibrary();
Var undefinedVar = library->GetUndefined();
JavascriptExceptionObject* exception = nullptr;
RecyclableObject* result = nullptr;
try
{
Var resultVar = CALL_FUNCTION(
scriptContext->GetThreadContext(),
stepFunction,
CallInfo(CallFlags_Value, 1),
undefinedVar);
result = VarTo<RecyclableObject>(resultVar);
}
catch (const JavascriptException& err)
{
exception = err.GetAndClear();
}
if (exception != nullptr)
{
// If the generator threw an exception, reject the promise
JavascriptPromise::TryRejectWithExceptionObject(exception, reject, scriptContext);
return;
}
Assert(result != nullptr);
Var value = JavascriptOperators::GetProperty(result, PropertyIds::value, scriptContext);
// If the generator is done, resolve the promise
if (generator->IsCompleted())
{
if (!JavascriptConversion::IsCallable(resolve))
JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction);
CALL_FUNCTION(
scriptContext->GetThreadContext(),
VarTo<RecyclableObject>(resolve),
CallInfo(CallFlags_Value, 2),
undefinedVar,
value);
return;
}
else
{
Assert(JavascriptOperators::GetTypeId(result) == TypeIds_AwaitObject);
}
// Chain off the yielded promise and step again
auto* successFunction = library->CreateAsyncSpawnStepFunction(
EntryAsyncSpawnCallStepFunction,
generator,
undefinedVar,
resolve,
reject);
auto* failFunction = library->CreateAsyncSpawnStepFunction(
EntryAsyncSpawnCallStepFunction,
generator,
undefinedVar,
resolve,
reject,
true);
auto* promise = JavascriptPromise::InternalPromiseResolve(value, scriptContext);
auto* unused = JavascriptPromise::UnusedPromiseCapability(scriptContext);
JavascriptPromise::PerformPromiseThen(
promise,
unused,
successFunction,
failFunction,
scriptContext);
END_SAFE_REENTRANT_REGION
}
template<>
bool Js::VarIsImpl<JavascriptAsyncSpawnStepFunction>(RecyclableObject* obj)
{
return VarIs<JavascriptFunction>(obj) && (
VirtualTableInfo<JavascriptAsyncSpawnStepFunction>::HasVirtualTable(obj) ||
VirtualTableInfo<CrossSiteObject<JavascriptAsyncSpawnStepFunction>>::HasVirtualTable(obj)
);
}
#if ENABLE_TTD
TTD::NSSnapObjects::SnapObjectType JavascriptAsyncFunction::GetSnapTag_TTD() const
{
return TTD::NSSnapObjects::SnapObjectType::SnapAsyncFunction;
}
void JavascriptAsyncFunction::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
{
TTD::NSSnapObjects::SnapGeneratorFunctionInfo* fi = nullptr;
uint32 depCount = 0;
TTD_PTR_ID* depArray = nullptr;
this->CreateSnapObjectInfo(alloc, &fi, &depArray, &depCount);
if (depCount == 0)
{
TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapGeneratorFunctionInfo*, TTD::NSSnapObjects::SnapObjectType::SnapAsyncFunction>(objData, fi);
}
else
{
TTDAssert(depArray != nullptr, "depArray should be non-null if depCount is > 0");
TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapGeneratorFunctionInfo*, TTD::NSSnapObjects::SnapObjectType::SnapAsyncFunction>(objData, fi, alloc, depCount, depArray);
}
}
void JavascriptAsyncSpawnStepFunction::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
{
if (this->generator != nullptr)
{
extractor->MarkVisitVar(this->generator);
}
if (this->reject != nullptr)
{
extractor->MarkVisitVar(this->reject);
}
if (this->resolve != nullptr)
{
extractor->MarkVisitVar(this->resolve);
}
if (this->argument != nullptr)
{
extractor->MarkVisitVar(this->argument);
}
}
TTD::NSSnapObjects::SnapObjectType JavascriptAsyncSpawnStepFunction::GetSnapTag_TTD() const
{
return TTD::NSSnapObjects::SnapObjectType::JavascriptAsyncSpawnStepFunction;
}
void JavascriptAsyncSpawnStepFunction::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
{
TTD::NSSnapObjects::SnapJavascriptAsyncSpawnStepFunctionInfo* info = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapJavascriptAsyncSpawnStepFunctionInfo>();
info->generator = TTD_CONVERT_VAR_TO_PTR_ID(this->generator);
info->reject = this->reject;
info->resolve = this->resolve;
info->argument = this->argument;
info->isReject = this->isReject;
info->entryPoint = 0;
JavascriptMethod entryPoint = this->GetFunctionInfo()->GetOriginalEntryPoint();
if (entryPoint == JavascriptAsyncFunction::EntryAsyncSpawnStepNextFunction)
{
info->entryPoint = 1;
}
else if (entryPoint == JavascriptAsyncFunction::EntryAsyncSpawnStepThrowFunction)
{
info->entryPoint = 2;
}
else if (entryPoint == JavascriptAsyncFunction::EntryAsyncSpawnCallStepFunction)
{
info->entryPoint = 3;
}
else
{
TTDAssert(false, "Unexpected entrypoint found JavascriptAsyncSpawnStepArgumentExecutorFunction");
}
const uint32 maxDeps = 4;
uint32 depCount = 0;
TTD_PTR_ID* depArray = alloc.SlabReserveArraySpace<TTD_PTR_ID>(maxDeps);
if (this->reject != nullptr && TTD::JsSupport::IsVarComplexKind(this->reject))
{
depArray[depCount] = TTD_CONVERT_VAR_TO_PTR_ID(this->reject);
depCount++;
}
if (this->resolve != nullptr && TTD::JsSupport::IsVarComplexKind(this->resolve))
{
depArray[depCount] = TTD_CONVERT_VAR_TO_PTR_ID(this->resolve);
depCount++;
}
if (this->argument != nullptr && TTD::JsSupport::IsVarComplexKind(this->argument))
{
depArray[depCount] = TTD_CONVERT_VAR_TO_PTR_ID(this->argument);
depCount++;
}
if (this->generator != nullptr)
{
depArray[depCount] = TTD_CONVERT_VAR_TO_PTR_ID(this->generator);
depCount++;
}
if (depCount > 0)
{
alloc.SlabCommitArraySpace<TTD_PTR_ID>(depCount, maxDeps);
}
else
{
alloc.SlabAbortArraySpace<TTD_PTR_ID>(maxDeps);
}
if (depCount == 0)
{
TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapJavascriptAsyncSpawnStepFunctionInfo*, TTD::NSSnapObjects::SnapObjectType::JavascriptAsyncSpawnStepFunction>(objData, info);
}
else
{
TTDAssert(depArray != nullptr, "depArray should be non-null if depCount is > 0");
TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapJavascriptAsyncSpawnStepFunctionInfo*, TTD::NSSnapObjects::SnapObjectType::JavascriptAsyncSpawnStepFunction>(objData, info, alloc, depCount, depArray);
}
}
#endif