Skip to content

Commit

Permalink
Add promise.any
Browse files Browse the repository at this point in the history
  • Loading branch information
wangwl03 committed Oct 8, 2019
1 parent 6b98ef8 commit e87d1ee
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/Runtime/Base/JnDirectFields.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ ENTRY2(xor_, _u("xor"))
ENTRY(add)
ENTRY(all)
ENTRY(allSettled)
ENTRY(any)
ENTRY(anchor)
ENTRY(apply)
ENTRY(Array)
Expand Down
2 changes: 2 additions & 0 deletions lib/Runtime/Library/JavascriptBuiltInFunctionList.h
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ BUILTIN(JavascriptListIterator, Next, EntryNext, FunctionInfo::ErrorOnNew)
BUILTIN(JavascriptPromise, NewInstance, NewInstance, FunctionInfo::SkipDefaultNewObject)
BUILTIN(JavascriptPromise, All, EntryAll, FunctionInfo::ErrorOnNew)
BUILTIN(JavascriptPromise, AllSettled, EntryAllSettled, FunctionInfo::ErrorOnNew)
BUILTIN(JavascriptPromise, Any, EntryAny, FunctionInfo::ErrorOnNew)
BUILTIN(JavascriptPromise, Catch, EntryCatch, FunctionInfo::ErrorOnNew)
BUILTIN(JavascriptPromise, Race, EntryRace, FunctionInfo::ErrorOnNew)
BUILTIN(JavascriptPromise, Reject, EntryReject, FunctionInfo::ErrorOnNew)
Expand All @@ -476,6 +477,7 @@ BUILTIN(JavascriptPromise, ResolveOrRejectFunction, EntryResolveOrRejectFunction
BUILTIN(JavascriptPromise, CapabilitiesExecutorFunction, EntryCapabilitiesExecutorFunction, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
BUILTIN(JavascriptPromise, AllResolveElementFunction, EntryAllResolveElementFunction, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
BUILTIN(JavascriptPromise, AllSettledResolveOrRejectElementFunction, EntryAllSettledResolveOrRejectElementFunction, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
BUILTIN(JavascriptPromise, AnyRejectElementFunction, EntryAnyRejectElementFunction, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
BUILTIN(JavascriptPromise, GetterSymbolSpecies, EntryGetterSymbolSpecies, FunctionInfo::ErrorOnNew)
BUILTIN(JavascriptReflect, DefineProperty, EntryDefineProperty, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
BUILTIN(JavascriptReflect, DeleteProperty, EntryDeleteProperty, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)
Expand Down
14 changes: 14 additions & 0 deletions lib/Runtime/Library/JavascriptLibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2569,6 +2569,7 @@ namespace Js

library->AddFunctionToLibraryObject(promiseConstructor, PropertyIds::all, &JavascriptPromise::EntryInfo::All, 1);
library->AddFunctionToLibraryObject(promiseConstructor, PropertyIds::allSettled, &JavascriptPromise::EntryInfo::AllSettled, 1);
library->AddFunctionToLibraryObject(promiseConstructor, PropertyIds::any, &JavascriptPromise::EntryInfo::Any, 1);
library->AddFunctionToLibraryObject(promiseConstructor, PropertyIds::race, &JavascriptPromise::EntryInfo::Race, 1);
library->AddFunctionToLibraryObject(promiseConstructor, PropertyIds::reject, &JavascriptPromise::EntryInfo::Reject, 1);
library->AddMember(promiseConstructor, PropertyIds::resolve, library->EnsurePromiseResolveFunction(), PropertyBuiltInMethodDefaults);
Expand Down Expand Up @@ -6548,6 +6549,19 @@ namespace Js
return function;
}

JavascriptPromiseAnyRejectElementFunction* JavascriptLibrary::CreatePromiseAnyRejectElementFunction(JavascriptMethod entryPoint, uint32 index, JavascriptArray* values, JavascriptPromiseCapability* capabilities, JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper* remainingElements, JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper* alreadyCalledWrapper)
{
Assert(scriptContext->GetConfig()->IsES6PromiseEnabled());

FunctionInfo* functionInfo = &Js::JavascriptPromise::EntryInfo::AnyRejectElementFunction;
DynamicType* type = DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, entryPoint, GetDeferredAnonymousFunctionTypeHandler());
JavascriptPromiseAnyRejectElementFunction* function = RecyclerNewEnumClass(this->GetRecycler(), EnumFunctionClass, JavascriptPromiseAnyRejectElementFunction, type, functionInfo, index, values, capabilities, remainingElements, alreadyCalledWrapper);

function->SetPropertyWithAttributes(PropertyIds::length, TaggedInt::ToVarUnchecked(1), PropertyConfigurable, nullptr);

return function;
}

JavascriptPromiseThenFinallyFunction* JavascriptLibrary::CreatePromiseThenFinallyFunction(JavascriptMethod entryPoint, RecyclableObject* OnFinally, RecyclableObject* Constructor, bool shouldThrow)
{
Assert(scriptContext->GetConfig()->IsES6PromiseEnabled());
Expand Down
1 change: 1 addition & 0 deletions lib/Runtime/Library/JavascriptLibrary.h
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ namespace Js
JavascriptPromiseThenFinallyFunction* CreatePromiseThenFinallyFunction(JavascriptMethod entryPoint, RecyclableObject* OnFinally, RecyclableObject* Constructor, bool shouldThrow);
JavascriptPromiseThunkFinallyFunction* CreatePromiseThunkFinallyFunction(JavascriptMethod entryPoint, Var value, bool shouldThrow);
JavascriptExternalFunction* CreateWrappedExternalFunction(JavascriptExternalFunction* wrappedFunction);
JavascriptPromiseAnyRejectElementFunction* CreatePromiseAnyRejectElementFunction(JavascriptMethod entryPoint, uint32 index, JavascriptArray* values, JavascriptPromiseCapability* capabilities, JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper* remainingElements, JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper* alreadyCalledWrapper);

#if ENABLE_NATIVE_CODEGEN
#if !FLOATVAR
Expand Down
205 changes: 205 additions & 0 deletions lib/Runtime/Library/JavascriptPromise.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,123 @@ namespace Js
return promiseCapability->GetPromise();
}

Var JavascriptPromise::EntryAny(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
Assert(!(callInfo.Flags & CallFlags_New));

ScriptContext* scriptContext = function->GetScriptContext();

AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Promise.any"));

// 1. Let C be the this value.
Var constructor = args[0];

// 2. If Type(C) is not Object, throw a TypeError exception.
if (!JavascriptOperators::IsObject(constructor))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedObject, _u("Promise.allSettled"));
}

JavascriptLibrary* library = scriptContext->GetLibrary();
Var iterable;

if (args.Info.Count > 1)
{
iterable = args[1];
}
else
{
iterable = library->GetUndefined();
}

// 3. Let promiseCapability be NewPromiseCapability(C).
JavascriptPromiseCapability* promiseCapability = NewPromiseCapability(constructor, scriptContext);

RecyclableObject* constructorObject = VarTo<RecyclableObject>(constructor);

uint32 index = 0;
JavascriptArray* values = nullptr;

JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper* remainingElementsWrapper = RecyclerNewStructZ(scriptContext->GetRecycler(), JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper);
remainingElementsWrapper->remainingElements = 1;

JavascriptExceptionObject* exception = nullptr;
try
{
// 4. Let iterator be GetIterator(iterable).
RecyclableObject* iterator = JavascriptOperators::GetIterator(iterable, scriptContext);

Var resolveVar = JavascriptOperators::GetProperty(constructorObject, Js::PropertyIds::resolve, scriptContext);
if (!JavascriptConversion::IsCallable(resolveVar))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction);
}

RecyclableObject* resolveFunc = VarTo<RecyclableObject>(resolveVar);
values = library->CreateArray(0);

Var nextValue;
while (JavascriptOperators::IteratorStepAndValue(iterator, scriptContext, &nextValue)) {
ThreadContext* threadContext = scriptContext->GetThreadContext();
Var nextPromise = nullptr;
BEGIN_SAFE_REENTRANT_CALL(threadContext)
{
nextPromise = CALL_FUNCTION(threadContext,
resolveFunc, Js::CallInfo(CallFlags_Value, 2),
constructorObject,
nextValue);
}
END_SAFE_REENTRANT_CALL

JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper* alreadyCalledWrapper = RecyclerNewStructZ(scriptContext->GetRecycler(), JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper);
alreadyCalledWrapper->alreadyResolved = false;
Var rejectElement = library->CreatePromiseAnyRejectElementFunction(EntryAnyRejectElementFunction, index, values, promiseCapability, remainingElementsWrapper, alreadyCalledWrapper);

remainingElementsWrapper->remainingElements++;

RecyclableObject* nextPromiseObject;
if (!JavascriptConversion::ToObject(nextPromise, scriptContext, &nextPromiseObject))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject);
}

Var thenVar = JavascriptOperators::GetProperty(nextPromiseObject, Js::PropertyIds::then, scriptContext);

if (!JavascriptConversion::IsCallable(thenVar))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction);
}

RecyclableObject* thenFunc = VarTo<RecyclableObject>(thenVar);

BEGIN_SAFE_REENTRANT_CALL(threadContext)
{
CALL_FUNCTION(scriptContext->GetThreadContext(),
thenFunc, Js::CallInfo(CallFlags_Value, 3),
nextPromiseObject,
promiseCapability->GetResolve(),
rejectElement);
}
END_SAFE_REENTRANT_CALL

index++;
}
}
catch (const JavascriptException& err)
{
exception = err.GetAndClear();
}

if (exception != nullptr)
{
TryRejectWithExceptionObject(exception, promiseCapability->GetReject(), scriptContext);
}

return promiseCapability->GetPromise();
}

Var JavascriptPromise::EntryAllSettled(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
Expand Down Expand Up @@ -1523,6 +1640,61 @@ namespace Js
return undefinedVar;
}

Var JavascriptPromise::EntryAnyRejectElementFunction(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
ARGUMENTS(args, callInfo);
Assert(!(callInfo.Flags & CallFlags_New));

ScriptContext* scriptContext = function->GetScriptContext();
Var undefinedVar = scriptContext->GetLibrary()->GetUndefined();
Var x;

if (args.Info.Count > 1)
{
x = args[1];
}
else
{
x = undefinedVar;
}

JavascriptPromiseAnyRejectElementFunction* anyRejectElementFunction = VarTo<JavascriptPromiseAnyRejectElementFunction>(function);

if (anyRejectElementFunction->IsAlreadyCalled())
{
return undefinedVar;
}

anyRejectElementFunction->SetAlreadyCalled(true);

uint32 index = anyRejectElementFunction->GetIndex();
JavascriptArray* values = anyRejectElementFunction->GetValues();
JavascriptPromiseCapability* promiseCapability = anyRejectElementFunction->GetCapabilities();
JavascriptExceptionObject* exception = nullptr;

try
{
values->SetItem(index, x, PropertyOperation_None);
}
catch (const JavascriptException& err)
{
exception = err.GetAndClear();
}

if (exception != nullptr)
{
return TryRejectWithExceptionObject(exception, promiseCapability->GetReject(), scriptContext);
}

if (anyRejectElementFunction->DecrementRemainingElements() == 0)
{
return TryCallResolveOrRejectHandler(promiseCapability->GetResolve(), values, scriptContext);
}

return undefinedVar;
}

Var JavascriptPromise::EntryJavascriptPromiseAsyncSpawnExecutorFunction(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
Expand Down Expand Up @@ -2542,6 +2714,39 @@ namespace Js
}
#endif

JavascriptPromiseAnyRejectElementFunction::JavascriptPromiseAnyRejectElementFunction(DynamicType* type)
: JavascriptPromiseAllResolveElementFunction(type), alreadyCalledWrapper(nullptr)
{ }

JavascriptPromiseAnyRejectElementFunction::JavascriptPromiseAnyRejectElementFunction(DynamicType* type, FunctionInfo* functionInfo, uint32 index, JavascriptArray* values, JavascriptPromiseCapability* capabilities, JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper* remainingElementsWrapper, JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper* alreadyCalledWrapper)
: JavascriptPromiseAllResolveElementFunction(type, functionInfo, index, values, capabilities, remainingElementsWrapper), alreadyCalledWrapper(alreadyCalledWrapper)
{ }

bool JavascriptPromiseAnyRejectElementFunction::IsAlreadyCalled() const
{
Assert(this->alreadyCalledWrapper);

return this->alreadyCalledWrapper->alreadyResolved;
}

void JavascriptPromiseAnyRejectElementFunction::SetAlreadyCalled(const bool is)
{
Assert(this->alreadyCalledWrapper);

this->alreadyCalledWrapper->alreadyResolved = is;
}

template <> bool VarIsImpl<JavascriptPromiseAnyRejectElementFunction>(RecyclableObject* obj)
{
if (VarIs<JavascriptFunction>(obj))
{
return VirtualTableInfo<JavascriptPromiseAnyRejectElementFunction>::HasVirtualTable(obj)
|| VirtualTableInfo<CrossSiteObject<JavascriptPromiseAnyRejectElementFunction>>::HasVirtualTable(obj);
}

return false;
}

JavascriptPromiseAllResolveElementFunction::JavascriptPromiseAllResolveElementFunction(DynamicType* type)
: RuntimeFunction(type, &Js::JavascriptPromise::EntryInfo::AllResolveElementFunction), index(0), values(nullptr), capabilities(nullptr), remainingElementsWrapper(nullptr), alreadyCalled(false)
{ }
Expand Down
23 changes: 23 additions & 0 deletions lib/Runtime/Library/JavascriptPromise.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,25 @@ namespace Js

template <> bool VarIsImpl<JavascriptPromiseAllSettledResolveOrRejectElementFunction>(RecyclableObject* obj);

class JavascriptPromiseAnyRejectElementFunction : public JavascriptPromiseAllResolveElementFunction
{
protected:
DEFINE_VTABLE_CTOR(JavascriptPromiseAnyRejectElementFunction, JavascriptPromiseAllResolveElementFunction);
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptPromiseAnyRejectElementFunction);

public:
JavascriptPromiseAnyRejectElementFunction(DynamicType* type);
JavascriptPromiseAnyRejectElementFunction(DynamicType* type, FunctionInfo* functionInfo, uint32 index, JavascriptArray* values, JavascriptPromiseCapability* capabilities, JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper* remainingElementsWrapper, JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper* alreadyCalledWrapper);

bool IsAlreadyCalled() const;
void SetAlreadyCalled(const bool is);

private:
Field(JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper*) alreadyCalledWrapper;
};

template <> bool VarIsImpl<JavascriptPromiseAnyRejectElementFunction>(RecyclableObject* obj);

class JavascriptPromiseCapability : FinalizableObject
{
private:
Expand Down Expand Up @@ -422,6 +441,7 @@ namespace Js
static FunctionInfo Then;
static FunctionInfo Finally;
static FunctionInfo AllSettled;
static FunctionInfo Any;

static FunctionInfo Identity;
static FunctionInfo Thrower;
Expand All @@ -432,6 +452,7 @@ namespace Js
static FunctionInfo CapabilitiesExecutorFunction;
static FunctionInfo AllResolveElementFunction;
static FunctionInfo AllSettledResolveOrRejectElementFunction;
static FunctionInfo AnyRejectElementFunction;

static FunctionInfo GetterSymbolSpecies;
};
Expand All @@ -448,6 +469,7 @@ namespace Js
static Var EntryThen(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFinally(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryAllSettled(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryAny(RecyclableObject* function, CallInfo callInfo, ...);

static Var EntryThunkFinallyFunction(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryThenFinallyFunction(RecyclableObject* function, CallInfo callInfo, ...);
Expand All @@ -459,6 +481,7 @@ namespace Js
static Var EntryThrowerFunction(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryAllResolveElementFunction(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryAllSettledResolveOrRejectElementFunction(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryAnyRejectElementFunction(RecyclableObject* function, CallInfo callInfo, ...);

static Var EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...);

Expand Down
1 change: 1 addition & 0 deletions lib/Runtime/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ namespace Js
class JavascriptPromiseResolveThenableTaskFunction;
class JavascriptPromiseAllResolveElementFunction;
class JavascriptPromiseAllSettledResolveOrRejectElementFunction;
class JavascriptPromiseAnyRejectElementFunction;
struct JavascriptPromiseAllResolveElementFunctionRemainingElementsWrapper;
struct JavascriptPromiseResolveOrRejectFunctionAlreadyResolvedWrapper;
class JavascriptGenerator;
Expand Down

0 comments on commit e87d1ee

Please sign in to comment.