Skip to content

Commit

Permalink
[JSC] Implement Promise.try
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=269775

Reviewed by Justin Michaud.

This patch implements https://github.com/tc39/proposal-promise-try behind a runtime option;
the proposal is currently at Stage 2 but the spec is very simple and not expected to change.

Effectively, Promise.try(f) is a more convenient way to write `new Promise((resolve) => { resolve(f()); })` --
we don't care whether f is sync or async, but if is synchronous, we want it to be executed immediately.

This implementation includes tc39/proposal-promise-try#16, which the champion expects to merge;
it simply extends the API to Promise.try(f, ...args) such that arguments can be forwarded to the callback.

* JSTests/stress/promise-try.js: Added.
* Source/JavaScriptCore/builtins/PromiseConstructor.js:
(try): Added.
* Source/JavaScriptCore/runtime/JSPromiseConstructor.cpp:
(JSC::JSPromiseConstructor::finishCreation):
* Source/JavaScriptCore/runtime/OptionsList.h:

Canonical link: https://commits.webkit.org/275081@main
  • Loading branch information
rkirsling committed Feb 21, 2024
1 parent 142e543 commit 6d51d57
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 0 deletions.
52 changes: 52 additions & 0 deletions JSTests/stress/promise-try.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//@ requireOptions("--usePromiseTryMethod=1")

function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error(`expected ${expected} but got ${actual}`);
}

function notReached() {
throw new Error('should not reach here');
}

async function delay() {
return new Promise((resolve, reject) => {
setTimeout(resolve);
});
}

shouldBe(Promise.try.length, 1);

async function test() {
{
let result = [];
Promise.try(() => { result.push(1); });
result.push(2);
await delay();
shouldBe(`${result}`, '1,2');
}

shouldBe(await Promise.try(() => Promise.resolve(3)), 3);

try {
await Promise.try(() => { throw 4; });
notReached();
} catch (e) {
shouldBe(e, 4);
}

try {
await Promise.try(() => Promise.reject(5));
notReached();
} catch (e) {
shouldBe(e, 5);
}

shouldBe(await Promise.try((x, y, z) => x + y + z, 1, 2, 3), 6);
}

test().catch((error) => {
print(`FAIL: ${error}\n${error.stack}`);
$vm.abort();
});
drainMicrotasks();
22 changes: 22 additions & 0 deletions Source/JavaScriptCore/builtins/PromiseConstructor.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,28 @@ function resolve(value)
return @promiseResolve(this, value);
}

function try(callback /*, ...args */)
{
"use strict";

if (!@isObject(this))
@throwTypeError("|this| is not an object");

var args = [];
for (var i = 1; i < arguments.length; i++)
@putByValDirect(args, i - 1, arguments[i]);

var promiseCapability = @newPromiseCapability(this);
try {
var value = callback.@apply(@undefined, args);
promiseCapability.resolve.@call(@undefined, value);
} catch (error) {
promiseCapability.reject.@call(@undefined, error);
}

return promiseCapability.promise;
}

function withResolvers()
{
"use strict";
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/runtime/JSPromiseConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ void JSPromiseConstructor::finishCreation(VM& vm, JSPromisePrototype* promisePro

if (Options::usePromiseWithResolversMethod())
JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().withResolversPublicName(), promiseConstructorWithResolversCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
if (Options::usePromiseTryMethod())
JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->tryKeyword, promiseConstructorTryCodeGenerator, static_cast<unsigned>(PropertyAttribute::DontEnum));
}

void JSPromiseConstructor::addOwnInternalSlots(VM& vm, JSGlobalObject* globalObject)
Expand Down
1 change: 1 addition & 0 deletions Source/JavaScriptCore/runtime/OptionsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ bool canUseHandlerIC();
v(Bool, useImportAttributes, true, Normal, "Enable import attributes.") \
v(Bool, useIntlDurationFormat, true, Normal, "Expose the Intl DurationFormat.") \
v(Bool, usePromiseWithResolversMethod, true, Normal, "Expose the Promise.withResolvers() method.") \
v(Bool, usePromiseTryMethod, false, Normal, "Expose the Promise.try() method.") \
v(Bool, useResizableArrayBuffer, true, Normal, "Expose ResizableArrayBuffer feature.") \
v(Bool, useSharedArrayBuffer, false, Normal, nullptr) \
v(Bool, useShadowRealm, false, Normal, "Expose the ShadowRealm object.") \
Expand Down

0 comments on commit 6d51d57

Please sign in to comment.