diff --git a/src/builtins/BuiltinPromise.cpp b/src/builtins/BuiltinPromise.cpp index c9b68a36a8..c79fa08821 100644 --- a/src/builtins/BuiltinPromise.cpp +++ b/src/builtins/BuiltinPromise.cpp @@ -203,8 +203,16 @@ static Value builtinPromiseAll(ExecutionState& state, Value thisValue, size_t ar *remainingElementsCount = *remainingElementsCount + 1; // Perform ? Invoke(nextPromise, "then", « resolveElement, resultCapability.[[Reject]] »). Object* nextPromiseObject = nextPromise.toObject(state); - Value argv[] = { Value(resolveElement), Value(promiseCapability.m_rejectFunction) }; - Object::call(state, nextPromiseObject->get(state, strings->then).value(state, nextPromiseObject), nextPromiseObject, 2, argv); + Value then = nextPromiseObject->get(state, strings->then).value(state, nextPromiseObject); + + if (LIKELY(nextPromiseObject->isPromiseObject() && then.isObject() && then.asObject() == state.context()->globalObject()->promiseThen())) { + // fast path for then call + nextPromiseObject->asPromiseObject()->then(state, resolveElement, promiseCapability.m_rejectFunction); + } else { + Value argv[] = { Value(resolveElement), Value(promiseCapability.m_rejectFunction) }; + Object::call(state, then, nextPromiseObject, 2, argv); + } + // Increase index by 1. index++; } @@ -312,8 +320,15 @@ static Value builtinPromiseRace(ExecutionState& state, Value thisValue, size_t a Value nextPromise = Object::call(state, promiseResolve, C, 1, &nextValue); // Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »). Object* nextPromiseObject = nextPromise.toObject(state); - Value argv[] = { Value(promiseCapability.m_resolveFunction), Value(promiseCapability.m_rejectFunction) }; - Object::call(state, nextPromiseObject->get(state, strings->then).value(state, nextPromiseObject), nextPromiseObject, 2, argv); + Value then = nextPromiseObject->get(state, strings->then).value(state, nextPromiseObject); + + if (LIKELY(nextPromiseObject->isPromiseObject() && then.isObject() && then.asObject() == state.context()->globalObject()->promiseThen())) { + // fast path for then call + nextPromiseObject->asPromiseObject()->then(state, promiseCapability.m_resolveFunction, promiseCapability.m_rejectFunction); + } else { + Value argv[] = { Value(promiseCapability.m_resolveFunction), Value(promiseCapability.m_rejectFunction) }; + Object::call(state, then, nextPromiseObject, 2, argv); + } } } catch (const Value& e) { Value exceptionValue = e; @@ -368,6 +383,12 @@ static Value builtinPromiseCatch(ExecutionState& state, Value thisValue, size_t Object* thisObject = thisValue.toObject(state); Value onRejected = argv[0]; Value then = thisObject->get(state, strings->then).value(state, thisObject); + + if (LIKELY(thisObject->isPromiseObject() && then.isObject() && then.asObject() == state.context()->globalObject()->promiseThen())) { + // fast path for then call + return thisObject->asPromiseObject()->then(state, Value(), onRejected); + } + Value arguments[] = { Value(), onRejected }; return Object::call(state, then, thisObject, 2, arguments); } @@ -402,17 +423,23 @@ static Value builtinPromiseFinally(ExecutionState& state, Value thisValue, size_ } Value then = thisObject->get(state, strings->then).value(state, thisObject); + + if (LIKELY(thisObject->isPromiseObject() && then.isObject() && then.asObject() == state.context()->globalObject()->promiseThen())) { + // fast path for then call + return thisObject->asPromiseObject()->then(state, arguments[0], arguments[1]); + } + return Object::call(state, then, thisObject, 2, arguments); } static Value builtinPromiseThen(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional newTarget) { auto strings = &state.context()->staticStrings(); - if (!thisValue.isObject() || !thisValue.asObject()->isPromiseObject()) + if (!thisValue.isObject() || !thisValue.asObject()->isPromiseObject()) { ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, strings->Promise.string(), false, strings->then.string(), "%s: not a Promise object"); - Value C = thisValue.asObject()->speciesConstructor(state, state.context()->globalObject()->promise()); - PromiseReaction::Capability promiseCapability = PromiseObject::newPromiseCapability(state, C.asObject(), thisValue.asObject()->asPromiseObject()); - return thisValue.asObject()->asPromiseObject()->then(state, argv[0], argv[1], promiseCapability).value(); + } + + return thisValue.asObject()->asPromiseObject()->then(state, argv[0], argv[1]); } // https://tc39.es/ecma262/#sec-performpromiseallsettled @@ -507,9 +534,17 @@ static Value performPromiseAllSettled(ExecutionState& state, IteratorRecord* ite // Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1. *remainingElementsCount = *remainingElementsCount + 1; + // Perform ? Invoke(nextPromise, "then", « resolveElement, rejectElement »). - Value argv[2] = { resolveElement, rejectElement }; - Object::call(state, Object::getMethod(state, nextPromise, state.context()->staticStrings().then), nextPromise, 2, argv); + Value then = Object::getMethod(state, nextPromise, state.context()->staticStrings().then); + if (nextPromise.isObject() && nextPromise.asObject()->isPromiseObject() && then.isObject() && then.asObject() == state.context()->globalObject()->promiseThen()) { + // fast path for then call + nextPromise.asObject()->asPromiseObject()->then(state, resolveElement, rejectElement); + } else { + Value argv[2] = { resolveElement, rejectElement }; + Object::call(state, then, nextPromise, 2, argv); + } + // Set index to index + 1. index++; } @@ -668,8 +703,16 @@ static Value performPromiseAny(ExecutionState& state, IteratorRecord* iteratorRe // Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], rejectElement »). Object* nextPromiseObject = nextPromise.toObject(state); - Value argv2[] = { Value(resultCapability.m_resolveFunction), Value(rejectElement) }; - Object::call(state, nextPromiseObject->get(state, strings->then).value(state, nextPromiseObject), nextPromiseObject, 2, argv2); + Value then = nextPromiseObject->get(state, strings->then).value(state, nextPromiseObject); + + if (LIKELY(nextPromiseObject->isPromiseObject() && then.isObject() && then.asObject() == state.context()->globalObject()->promiseThen())) { + // fast path for then call + nextPromiseObject->asPromiseObject()->then(state, resultCapability.m_resolveFunction, rejectElement); + } else { + Value argv2[] = { Value(resultCapability.m_resolveFunction), Value(rejectElement) }; + Object::call(state, then, nextPromiseObject, 2, argv2); + } + // Set index to index + 1. index++; } @@ -771,8 +814,6 @@ void GlobalObject::installPromise(ExecutionState& state) m_promisePrototype->setGlobalIntrinsicObject(state, true); m_promisePrototype->defineOwnProperty(state, ObjectPropertyName(strings->constructor), ObjectPropertyDescriptor(m_promise, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); - m_promisePrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().toStringTag), - ObjectPropertyDescriptor(Value(state.context()->staticStrings().Promise.string()), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent))); // $25.4.4.1 Promise.all(iterable); m_promise->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->all), @@ -794,10 +835,13 @@ void GlobalObject::installPromise(ExecutionState& state) m_promisePrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->stringCatch), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->stringCatch, builtinPromiseCatch, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + // $25.4.5.3 Promise.prototype.then(onFulfilled, onRejected) + m_promiseThen = new NativeFunctionObject(state, NativeFunctionInfo(strings->then, builtinPromiseThen, 2, NativeFunctionInfo::Strict)); m_promisePrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->then), - ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->then, builtinPromiseThen, 2, NativeFunctionInfo::Strict)), + ObjectPropertyDescriptor(m_promiseThen, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent))); + // $25.6.5.3 Promise.prototype.finally ( onFinally ) m_promisePrototype->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->finally), ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->finally, builtinPromiseFinally, 1, NativeFunctionInfo::Strict)), diff --git a/src/interpreter/ByteCodeInterpreter.cpp b/src/interpreter/ByteCodeInterpreter.cpp index d14fd2f381..f5d1c4b5e7 100644 --- a/src/interpreter/ByteCodeInterpreter.cpp +++ b/src/interpreter/ByteCodeInterpreter.cpp @@ -3517,7 +3517,7 @@ NEVER_INLINE void ByteCodeInterpreter::callFunctionComplexCase(ExecutionState& s ExtendedNativeFunctionObject* onRejected = new ExtendedNativeFunctionObjectImpl<1>(state, NativeFunctionInfo(AtomicString(), callDynamicImportRejected, 1)); onRejected->setInternalSlotAsPointer(0, promiseCapability.m_promise); // Perform ! PerformPromiseThen(capability.[[Promise]], onFulfilled, onRejected). - innerPromiseCapability.m_promise->asPromiseObject()->then(state, onFulfilled, onRejected); + innerPromiseCapability.m_promise->asPromiseObject()->then(state, onFulfilled, onRejected, PromiseReaction::Capability()); Global::platform()->hostImportModuleDynamically(byteCodeBlock->m_codeBlock->context(), referencingScriptOrModule, specifierString, innerPromiseCapability.m_promise->asPromiseObject()); diff --git a/src/parser/Script.cpp b/src/parser/Script.cpp index a78741a712..73a8f0438a 100644 --- a/src/parser/Script.cpp +++ b/src/parser/Script.cpp @@ -1252,7 +1252,7 @@ void Script::moduleExecuteAsyncModule(ExecutionState& state) ExtendedNativeFunctionObject* onRejected = new ExtendedNativeFunctionObjectImpl<1>(state, NativeFunctionInfo(AtomicString(), asyncModuleRejectedFunction, 1)); onRejected->setInternalSlotAsPointer(0, this); // Perform ! PerformPromiseThen(capability.[[Promise]], onFulfilled, onRejected). - capability.m_promise->asPromiseObject()->then(state, onFulfilled, onRejected); + capability.m_promise->asPromiseObject()->then(state, onFulfilled, onRejected, PromiseReaction::Capability()); // Perform ! module.ExecuteModule(capability). moduleExecute(state, capability); diff --git a/src/runtime/AsyncGeneratorObject.cpp b/src/runtime/AsyncGeneratorObject.cpp index 047652429e..abb2232dd2 100644 --- a/src/runtime/AsyncGeneratorObject.cpp +++ b/src/runtime/AsyncGeneratorObject.cpp @@ -170,7 +170,7 @@ Value AsyncGeneratorObject::asyncGeneratorResumeNext(ExecutionState& state, Asyn ExtendedNativeFunctionObject* onRejected = new ExtendedNativeFunctionObjectImpl<1>(state, NativeFunctionInfo(AtomicString(), asyncGeneratorResumeNextReturnProcessorRejectedFunction, 1)); onRejected->setInternalSlot(AsyncGeneratorObject::BuiltinFunctionSlot::Generator, generator); // Perform ! PerformPromiseThen(promise, onFulfilled, onRejected). - promise->then(state, onFulfilled, onRejected); + promise->then(state, onFulfilled, onRejected, PromiseReaction::Capability()); // Return undefined. return Value(); } else { diff --git a/src/runtime/GlobalObject.h b/src/runtime/GlobalObject.h index b57042b2ef..7940706102 100644 --- a/src/runtime/GlobalObject.h +++ b/src/runtime/GlobalObject.h @@ -179,7 +179,8 @@ class FunctionObject; #define GLOBALOBJECT_BUILTIN_PROMISE(F, objName) \ F(promise, FunctionObject, objName) \ - F(promisePrototype, Object, objName) + F(promisePrototype, Object, objName) \ + F(promiseThen, FunctionObject, objName) #define GLOBALOBJECT_BUILTIN_PROXY(F, objName) \ F(proxy, FunctionObject, objName) #define GLOBALOBJECT_BUILTIN_REFLECT(F, objName) \ diff --git a/src/runtime/Job.cpp b/src/runtime/Job.cpp index 23d97c7def..66c0fefbdf 100644 --- a/src/runtime/Job.cpp +++ b/src/runtime/Job.cpp @@ -112,6 +112,11 @@ SandBox::SandBoxResult PromiseResolveThenableJob::run() SandBox sb(state.context()); auto res = sb.run([&]() -> Value { + if (LIKELY(m_thenable->isPromiseObject() && m_then == state.context()->globalObject()->promiseThen())) { + // fast path for then call + return m_thenable->asPromiseObject()->then(state, capability.m_resolveFunction, capability.m_rejectFunction); + } + Value arguments[] = { capability.m_resolveFunction, capability.m_rejectFunction }; return Object::call(state, m_then, m_thenable, 2, arguments); }); diff --git a/src/runtime/Object.cpp b/src/runtime/Object.cpp index 3c447f96f0..69182c3564 100644 --- a/src/runtime/Object.cpp +++ b/src/runtime/Object.cpp @@ -1993,14 +1993,14 @@ ValueVectorWithInlineStorage Object::enumerableOwnProperties(ExecutionState& sta Value Object::speciesConstructor(ExecutionState& state, const Value& defaultConstructor) { - ASSERT(isObject()); + ASSERT(isObject() & defaultConstructor.isObject()); Value C = asObject()->get(state, state.context()->staticStrings().constructor).value(state, this); - if (C.isUndefined()) { + if (UNLIKELY(C.isUndefined())) { return defaultConstructor; } - if (!C.isObject()) { + if (UNLIKELY(!C.isObject())) { ErrorObject::throwBuiltinError(state, ErrorObject::TypeError, "constructor is not an object"); } diff --git a/src/runtime/PromiseObject.cpp b/src/runtime/PromiseObject.cpp index 5b2f6f5ca3..97d352cc96 100644 --- a/src/runtime/PromiseObject.cpp +++ b/src/runtime/PromiseObject.cpp @@ -174,18 +174,21 @@ Object* PromiseObject::then(ExecutionState& state, Value handler) return then(state, handler, Value(), newPromiseResultCapability(state)).value(); } +Object* PromiseObject::then(ExecutionState& state, Value onFulfilled, Value onRejected) +{ + return then(state, onFulfilled, onRejected, newPromiseResultCapability(state)).value(); +} + Object* PromiseObject::catchOperation(ExecutionState& state, Value handler) { return then(state, Value(), handler, newPromiseResultCapability(state)).value(); } -Optional PromiseObject::then(ExecutionState& state, Value onFulfilledValue, Value onRejectedValue, Optional resultCapability) +Optional PromiseObject::then(ExecutionState& state, Value onFulfilledValue, Value onRejectedValue, PromiseReaction::Capability capability) { Object* onFulfilled = onFulfilledValue.isCallable() ? onFulfilledValue.asObject() : (Object*)(1); Object* onRejected = onRejectedValue.isCallable() ? onRejectedValue.asObject() : (Object*)(2); - PromiseReaction::Capability capability = resultCapability.hasValue() ? resultCapability.value() : PromiseReaction::Capability(nullptr, nullptr, nullptr); - #ifdef ESCARGOT_DEBUGGER if (state.context()->debugger() != nullptr) { capability.m_savedStackTrace = Debugger::saveStackTrace(state); @@ -211,11 +214,7 @@ Optional PromiseObject::then(ExecutionState& state, Value onFulfilledVa break; } - if (resultCapability) { - return capability.m_promise; - } else { - return nullptr; - } + return capability.m_promise; } void PromiseObject::triggerPromiseReactions(ExecutionState& state, PromiseObject::Reactions& reactions) diff --git a/src/runtime/PromiseObject.h b/src/runtime/PromiseObject.h index ffc15d7787..bd249125e4 100644 --- a/src/runtime/PromiseObject.h +++ b/src/runtime/PromiseObject.h @@ -130,10 +130,11 @@ class PromiseObject : public Object { } Object* then(ExecutionState& state, Value handler); + Object* then(ExecutionState& state, Value onFulfilled, Value onRejected); Object* catchOperation(ExecutionState& state, Value handler); // http://www.ecma-international.org/ecma-262/10.0/#sec-performpromisethen // You can get return value when you give resultCapability - Optional then(ExecutionState& state, Value onFulfilled, Value onRejected, Optional resultCapability = Optional()); + Optional then(ExecutionState& state, Value onFulfilled, Value onRejected, PromiseReaction::Capability capability); void* operator new(size_t size); void* operator new[](size_t size) = delete; diff --git a/src/runtime/ScriptAsyncFunctionObject.cpp b/src/runtime/ScriptAsyncFunctionObject.cpp index fe00bfd81b..d6adafdc0e 100644 --- a/src/runtime/ScriptAsyncFunctionObject.cpp +++ b/src/runtime/ScriptAsyncFunctionObject.cpp @@ -156,7 +156,7 @@ PromiseObject* ScriptAsyncFunctionObject::awaitOperationBeforePause(ExecutionSta FunctionObject* onRejected = new ScriptAsyncFunctionHelperFunctionObject(state, NativeFunctionInfo(AtomicString(), awaitRejectedFunction, 1), executionPauser, source); // Perform ! PerformPromiseThen(promise, onFulfilled, onRejected). - promise->then(state, onFulfilled, onRejected, Optional()); + promise->then(state, onFulfilled, onRejected, PromiseReaction::Capability()); return promise; }