diff --git a/cmake/tools/SetupWebKit.cmake b/cmake/tools/SetupWebKit.cmake index aa07c060ec3588..c4ccdf0e3ea405 100644 --- a/cmake/tools/SetupWebKit.cmake +++ b/cmake/tools/SetupWebKit.cmake @@ -2,7 +2,7 @@ option(WEBKIT_VERSION "The version of WebKit to use") option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading") if(NOT WEBKIT_VERSION) - set(WEBKIT_VERSION 6d0f3aac0b817cc01a846b3754b21271adedac12) + set(WEBKIT_VERSION preview-pr-113-a8c96c80) endif() string(SUBSTRING ${WEBKIT_VERSION} 0 16 WEBKIT_VERSION_PREFIX) diff --git a/src/bun.js/bindings/BunPlugin.cpp b/src/bun.js/bindings/BunPlugin.cpp index c7f463c0da7018..93114e1f93a477 100644 --- a/src/bun.js/bindings/BunPlugin.cpp +++ b/src/bun.js/bindings/BunPlugin.cpp @@ -593,15 +593,15 @@ extern "C" JSC_DEFINE_HOST_FUNCTION(JSMock__jsModuleMock, (JSC::JSGlobalObject * if (result && result.isObject()) { while (JSC::JSPromise* promise = jsDynamicCast(result)) { - switch (promise->status(vm)) { + switch (promise->status()) { case JSC::JSPromise::Status::Rejected: { - result = promise->result(vm); + result = promise->result(); scope.throwException(globalObject, result); return {}; break; } case JSC::JSPromise::Status::Fulfilled: { - result = promise->result(vm); + result = promise->result(); break; } // TODO: blocking wait for promise @@ -740,13 +740,13 @@ EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, BunStri RETURN_IF_EXCEPTION(scope, {}); if (auto* promise = JSC::jsDynamicCast(result)) { - switch (promise->status(vm)) { + switch (promise->status()) { case JSPromise::Status::Rejected: case JSPromise::Status::Pending: { return JSValue::encode(promise); } case JSPromise::Status::Fulfilled: { - result = promise->result(vm); + result = promise->result(); break; } } @@ -826,18 +826,18 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, BunS } if (auto* promise = JSC::jsDynamicCast(result)) { - switch (promise->status(vm)) { + switch (promise->status()) { case JSPromise::Status::Pending: { JSC::throwTypeError(globalObject, scope, "onResolve() doesn't support pending promises yet"_s); return {}; } case JSPromise::Status::Rejected: { promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(static_cast(JSC::JSPromise::Status::Fulfilled))); - result = promise->result(vm); + result = promise->result(); return JSValue::encode(result); } case JSPromise::Status::Fulfilled: { - result = promise->result(vm); + result = promise->result(); break; } } @@ -913,13 +913,13 @@ JSC::JSValue runVirtualModule(Zig::GlobalObject* globalObject, BunString* specif RETURN_IF_EXCEPTION(throwScope, JSC::jsUndefined()); if (auto* promise = JSC::jsDynamicCast(result)) { - switch (promise->status(vm)) { + switch (promise->status()) { case JSPromise::Status::Rejected: case JSPromise::Status::Pending: { return promise; } case JSPromise::Status::Fulfilled: { - result = promise->result(vm); + result = promise->result(); break; } } diff --git a/src/bun.js/bindings/JSMockFunction.cpp b/src/bun.js/bindings/JSMockFunction.cpp index 9b9e0731c13d97..acd6c38ad5a382 100644 --- a/src/bun.js/bindings/JSMockFunction.cpp +++ b/src/bun.js/bindings/JSMockFunction.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -965,6 +966,7 @@ JSC_DEFINE_HOST_FUNCTION(jsMockFunctionCall, (JSGlobalObject * lexicalGlobalObje } case JSMockImplementation::Kind::RejectedValue: { JSValue rejectedPromise = JSC::JSPromise::rejectedPromise(globalObject, impl->underlyingValue.get()); + RETURN_IF_EXCEPTION(scope, {}); setReturnValue(createMockResult(vm, globalObject, "return"_s, rejectedPromise)); return JSValue::encode(rejectedPromise); } diff --git a/src/bun.js/bindings/JSType.zig b/src/bun.js/bindings/JSType.zig index 305ce12034e395..2130af39017bdb 100644 --- a/src/bun.js/bindings/JSType.zig +++ b/src/bun.js/bindings/JSType.zig @@ -488,13 +488,17 @@ pub const JSType = enum(u8) { /// Internal object used to track the state of Promise.all() resolution. PromiseAllContext = 77, + /// Promise reaction object for tracking promise callbacks. + /// Internal object used in the promise resolution mechanism. + PromiseReaction = 78, + /// JavaScript Map object for key-value storage. /// ```js /// new Map() /// map.set(key, value) /// map.get(key) /// ``` - Map = 78, + Map = 79, /// JavaScript Set object for unique value storage. /// ```js @@ -502,34 +506,34 @@ pub const JSType = enum(u8) { /// set.add(value) /// set.has(value) /// ``` - Set = 79, + Set = 80, /// WeakMap for weak key-value references. /// ```js /// new WeakMap() /// weakMap.set(object, value) /// ``` - WeakMap = 80, + WeakMap = 81, /// WeakSet for weak value references. /// ```js /// new WeakSet() /// weakSet.add(object) /// ``` - WeakSet = 81, + WeakSet = 82, - WebAssemblyModule = 82, - WebAssemblyInstance = 83, - WebAssemblyGCObject = 84, + WebAssemblyModule = 83, + WebAssemblyInstance = 84, + WebAssemblyGCObject = 85, /// Boxed String object. /// ```js /// new String("hello") /// ``` - StringObject = 85, + StringObject = 86, - DerivedStringObject = 86, - InternalFieldTuple = 87, + DerivedStringObject = 87, + InternalFieldTuple = 88, MaxJS = 0b11111111, Event = 0b11101111, diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index 7727c7411bc525..fe551c4404fb71 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -676,11 +676,11 @@ JSValue fetchCommonJSModule( RELEASE_AND_RETURN(scope, target); } JSPromise* promise = jsCast(promiseOrCommonJSModule); - switch (promise->status(vm)) { + switch (promise->status()) { case JSPromise::Status::Rejected: { uint32_t promiseFlags = promise->internalField(JSPromise::Field::Flags).get().asUInt32AsAnyInt(); promise->internalField(JSPromise::Field::Flags).set(vm, promise, jsNumber(promiseFlags | JSPromise::isHandledFlag)); - JSC::throwException(globalObject, scope, promise->result(vm)); + JSC::throwException(globalObject, scope, promise->result()); RELEASE_AND_RETURN(scope, JSValue {}); } case JSPromise::Status::Pending: { @@ -693,7 +693,7 @@ JSValue fetchCommonJSModule( RELEASE_AND_RETURN(scope, {}); } if (!wasModuleMock) { - auto* jsSourceCode = jsCast(promise->result(vm)); + auto* jsSourceCode = jsCast(promise->result()); globalObject->moduleLoader()->provideFetch(globalObject, specifierValue, jsSourceCode->sourceCode()); RETURN_IF_EXCEPTION(scope, {}); } @@ -727,11 +727,11 @@ JSValue fetchCommonJSModule( RELEASE_AND_RETURN(scope, target); } JSPromise* promise = jsCast(promiseOrCommonJSModule); - switch (promise->status(vm)) { + switch (promise->status()) { case JSPromise::Status::Rejected: { uint32_t promiseFlags = promise->internalField(JSPromise::Field::Flags).get().asUInt32AsAnyInt(); promise->internalField(JSPromise::Field::Flags).set(vm, promise, jsNumber(promiseFlags | JSPromise::isHandledFlag)); - JSC::throwException(globalObject, scope, promise->result(vm)); + JSC::throwException(globalObject, scope, promise->result()); RELEASE_AND_RETURN(scope, JSValue {}); } case JSPromise::Status::Pending: { @@ -744,7 +744,7 @@ JSValue fetchCommonJSModule( RELEASE_AND_RETURN(scope, {}); } if (!wasModuleMock) { - auto* jsSourceCode = jsCast(promise->result(vm)); + auto* jsSourceCode = jsCast(promise->result()); globalObject->moduleLoader()->provideFetch(globalObject, specifierValue, jsSourceCode->sourceCode()); RETURN_IF_EXCEPTION(scope, {}); } diff --git a/src/bun.js/bindings/NodeVM.cpp b/src/bun.js/bindings/NodeVM.cpp index a58e27405f7119..2b595c9cbc64dc 100644 --- a/src/bun.js/bindings/NodeVM.cpp +++ b/src/bun.js/bindings/NodeVM.cpp @@ -319,7 +319,7 @@ static JSInternalPromise* importModuleInner(JSGlobalObject* globalObject, JSStri promise->fulfill(globalObject, result); RETURN_IF_EXCEPTION(scope, nullptr); - promise = promise->then(globalObject, transformer, nullptr); + promise = promise->then(globalObject, transformer, globalObject->promiseEmptyOnRejectedFunction()); RETURN_IF_EXCEPTION(scope, nullptr); RELEASE_AND_RETURN(scope, promise); diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index a2f8b35b4c96a0..6f8956b2a64f75 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -840,6 +840,13 @@ void GlobalObject::promiseRejectionTracker(JSGlobalObject* obj, JSC::JSPromise* // Do this in C++ for now auto* globalObj = static_cast(obj); + + // JSInternalPromise should not be tracked through the normal promise rejection mechanism + // as they are internal to the engine and should not be exposed to user space. + // See: JSInternalPromise.h - "CAUTION: Must not leak the JSInternalPromise to the user space" + if (jsDynamicCast(promise)) + return; + switch (operation) { case JSPromiseRejectionOperation::Reject: globalObj->m_aboutToBeNotifiedRejectedPromises.append(obj->vm(), globalObj, promise); @@ -1047,8 +1054,9 @@ JSC_DEFINE_HOST_FUNCTION(functionQueueMicrotask, } // This is a JSC builtin function - lexicalGlobalObject->queueMicrotask(function, callback, asyncContext, - JSC::JSValue {}, JSC::JSValue {}); + // BunPerformMicrotaskJob expects: performMicrotask, job, asyncContext, arg0, arg1 + JSC::QueuedTask task { nullptr, JSC::InternalMicrotask::BunPerformMicrotaskJob, globalObject, function, callback, asyncContext }; + globalObject->vm().queueMicrotask(WTFMove(task)); return JSC::JSValue::encode(JSC::jsUndefined()); } @@ -2968,7 +2976,7 @@ void GlobalObject::handleRejectedPromises() JSC::VM& virtual_machine = vm(); auto scope = DECLARE_CATCH_SCOPE(virtual_machine); while (auto* promise = m_aboutToBeNotifiedRejectedPromises.takeFirst(this)) { - if (promise->isHandled(virtual_machine)) + if (promise->isHandled()) continue; Bun__handleRejectedPromise(this, promise); @@ -3075,7 +3083,9 @@ extern "C" void JSC__JSGlobalObject__queueMicrotaskCallback(Zig::GlobalObject* g #endif // Do not use JSCell* here because the GC will try to visit it. - globalObject->queueMicrotask(function, JSValue(std::bit_cast(reinterpret_cast(ptr))), JSValue(std::bit_cast(reinterpret_cast(callback))), jsUndefined(), jsUndefined()); + // Use BunInvokeJobWithArguments to pass the two arguments (ptr and callback) to the trampoline function + JSC::QueuedTask task { nullptr, JSC::InternalMicrotask::BunInvokeJobWithArguments, globalObject, function, JSValue(std::bit_cast(reinterpret_cast(ptr))), JSValue(std::bit_cast(reinterpret_cast(callback))) }; + globalObject->vm().queueMicrotask(WTFMove(task)); } JSC::Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject* jsGlobalObject, diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index a3153d67e1ead1..444438e0f961f5 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -46,6 +46,7 @@ #include "JavaScriptCore/JSArray.h" #include "JavaScriptCore/JSArrayBuffer.h" #include "JavaScriptCore/JSArrayInlines.h" +#include "JavaScriptCore/JSFunction.h" #include "JavaScriptCore/ErrorInstanceInlines.h" #include "JavaScriptCore/BigIntObject.h" #include "JavaScriptCore/OrderedHashTableHelper.h" @@ -2902,12 +2903,12 @@ JSC::EncodedJSValue JSC__JSModuleLoader__evaluate(JSC::JSGlobalObject* globalObj promise->rejectWithCaughtException(globalObject, scope); } - auto status = promise->status(vm); + auto status = promise->status(); if (status == JSC::JSPromise::Status::Fulfilled) { - return JSC::JSValue::encode(promise->result(vm)); + return JSC::JSValue::encode(promise->result()); } else if (status == JSC::JSPromise::Status::Rejected) { - *arg6 = JSC::JSValue::encode(promise->result(vm)); + *arg6 = JSC::JSValue::encode(promise->result()); return JSC::JSValue::encode(JSC::jsUndefined()); } else { return JSC::JSValue::encode(promise); @@ -3313,7 +3314,7 @@ JSC__JSModuleLoader__loadAndEvaluateModule(JSC::JSGlobalObject* globalObject, JSC::JSNativeStdFunction* resolverFunction = JSC::JSNativeStdFunction::create( vm, globalObject, 1, String(), resolverFunctionCallback); - auto* newPromise = promise->then(globalObject, resolverFunction, nullptr); + auto* newPromise = promise->then(globalObject, resolverFunction, globalObject->promiseEmptyOnRejectedFunction()); EXCEPTION_ASSERT(!!scope.exception() == !newPromise); return newPromise; } @@ -3413,7 +3414,7 @@ JSC::EncodedJSValue JSC__JSPromise__wrap(JSC::JSGlobalObject* globalObject, void ASSERT_WITH_MESSAGE(!value.isEmpty(), "Promise.reject cannot be called with a empty JSValue"); auto& vm = JSC::getVM(globalObject); ASSERT_WITH_MESSAGE(arg0->inherits(), "Argument is not a promise"); - ASSERT_WITH_MESSAGE(arg0->status(vm) == JSC::JSPromise::Status::Pending, "Promise is already resolved or rejected"); + ASSERT_WITH_MESSAGE(arg0->status() == JSC::JSPromise::Status::Pending, "Promise is already resolved or rejected"); JSC::Exception* exception = nullptr; if (!value.inherits()) { @@ -3428,7 +3429,7 @@ JSC::EncodedJSValue JSC__JSPromise__wrap(JSC::JSGlobalObject* globalObject, void [[ZIG_EXPORT(check_slow)]] void JSC__JSPromise__rejectAsHandled(JSC::JSPromise* arg0, JSC::JSGlobalObject* arg1, JSC::EncodedJSValue JSValue2) { ASSERT_WITH_MESSAGE(arg0->inherits(), "Argument is not a promise"); - ASSERT_WITH_MESSAGE(arg0->status(arg0->vm()) == JSC::JSPromise::Status::Pending, "Promise is already resolved or rejected"); + ASSERT_WITH_MESSAGE(arg0->status() == JSC::JSPromise::Status::Pending, "Promise is already resolved or rejected"); arg0->rejectAsHandled(arg1, JSC::JSValue::decode(JSValue2)); } @@ -3443,7 +3444,7 @@ JSC::JSPromise* JSC__JSPromise__rejectedPromise(JSC::JSGlobalObject* arg0, JSC:: JSValue target = JSValue::decode(JSValue2); ASSERT_WITH_MESSAGE(arg0->inherits(), "Argument is not a promise"); - ASSERT_WITH_MESSAGE(arg0->status(arg0->vm()) == JSC::JSPromise::Status::Pending, "Promise is already resolved or rejected"); + ASSERT_WITH_MESSAGE(arg0->status() == JSC::JSPromise::Status::Pending, "Promise is already resolved or rejected"); ASSERT(!target.isEmpty()); ASSERT_WITH_MESSAGE(arg0 != target, "Promise cannot be resolved to itself"); @@ -3506,12 +3507,8 @@ void JSC__JSPromise__rejectOnNextTickWithHandled(JSC::JSPromise* promise, JSC::J value = jsUndefined(); } - globalObject->queueMicrotask( - microtaskFunction, - rejectPromiseFunction, - globalObject->m_asyncContextData.get()->getInternalField(0), - promise, - value); + JSC::QueuedTask task { nullptr, JSC::InternalMicrotask::BunPerformMicrotaskJob, globalObject, microtaskFunction, rejectPromiseFunction, globalObject->m_asyncContextData.get()->getInternalField(0), promise, value }; + globalObject->vm().queueMicrotask(WTFMove(task)); RETURN_IF_EXCEPTION(scope, ); } } @@ -3531,7 +3528,7 @@ JSC::JSPromise* JSC__JSPromise__resolvedPromise(JSC::JSGlobalObject* globalObjec // if the promise is rejected we automatically mark it as handled so it // doesn't end up in the promise rejection tracker - switch (promise->status(vm)) { + switch (promise->status()) { case JSC::JSPromise::Status::Rejected: { uint32_t flags = promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32(); if (!(flags & JSC::JSPromise::isFirstResolvingFunctionCalledFlag)) { @@ -3540,7 +3537,7 @@ JSC::JSPromise* JSC__JSPromise__resolvedPromise(JSC::JSGlobalObject* globalObjec } // fallthrough intended case JSC::JSPromise::Status::Fulfilled: { - return JSValue::encode(promise->result(vm)); + return JSValue::encode(promise->result()); } default: return JSValue::encode(JSValue {}); @@ -3549,7 +3546,7 @@ JSC::JSPromise* JSC__JSPromise__resolvedPromise(JSC::JSGlobalObject* globalObjec [[ZIG_EXPORT(nothrow)]] uint32_t JSC__JSPromise__status(const JSC::JSPromise* arg0, JSC::VM* arg1) { - switch (arg0->status(*arg1)) { + switch (arg0->status()) { case JSC::JSPromise::Status::Pending: return 0; case JSC::JSPromise::Status::Fulfilled: @@ -3562,13 +3559,11 @@ JSC::JSPromise* JSC__JSPromise__resolvedPromise(JSC::JSGlobalObject* globalObjec } [[ZIG_EXPORT(nothrow)]] bool JSC__JSPromise__isHandled(const JSC::JSPromise* arg0, JSC::VM* arg1) { - return arg0->isHandled(*arg1); + return arg0->isHandled(); } [[ZIG_EXPORT(nothrow)]] void JSC__JSPromise__setHandled(JSC::JSPromise* promise, JSC::VM* arg1) { - auto& vm = *arg1; - auto flags = promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32(); - promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(flags | JSC::JSPromise::isHandledFlag)); + promise->markAsHandled(); } #pragma mark - JSC::JSInternalPromise @@ -3627,11 +3622,11 @@ JSC::JSInternalPromise* JSC__JSInternalPromise__resolvedPromise(JSC::JSGlobalObj JSC::EncodedJSValue JSC__JSInternalPromise__result(const JSC::JSInternalPromise* arg0, JSC::VM* arg1) { - return JSC::JSValue::encode(arg0->result(*arg1)); + return JSC::JSValue::encode(arg0->result()); } uint32_t JSC__JSInternalPromise__status(const JSC::JSInternalPromise* arg0, JSC::VM* arg1) { - switch (arg0->status(*arg1)) { + switch (arg0->status()) { case JSC::JSInternalPromise::Status::Pending: return 0; case JSC::JSInternalPromise::Status::Fulfilled: @@ -3644,7 +3639,7 @@ uint32_t JSC__JSInternalPromise__status(const JSC::JSInternalPromise* arg0, JSC: } bool JSC__JSInternalPromise__isHandled(const JSC::JSInternalPromise* arg0, JSC::VM* arg1) { - return arg0->isHandled(*arg1); + return arg0->isHandled(); } void JSC__JSInternalPromise__setHandled(JSC::JSInternalPromise* promise, JSC::VM* arg1) { @@ -5376,7 +5371,7 @@ extern "C" void JSC__JSGlobalObject__queueMicrotaskJob(JSC::JSGlobalObject* arg0 if (microtaskArgs[3].isEmpty()) { microtaskArgs[3] = jsUndefined(); } - auto microTaskFunction = globalObject->performMicrotaskFunction(); + JSC::JSFunction* microTaskFunction = globalObject->performMicrotaskFunction(); #if ASSERT_ENABLED ASSERT_WITH_MESSAGE(microTaskFunction, "Invalid microtask function"); auto& vm = globalObject->vm(); @@ -5398,12 +5393,8 @@ extern "C" void JSC__JSGlobalObject__queueMicrotaskJob(JSC::JSGlobalObject* arg0 #endif - globalObject->queueMicrotask( - microTaskFunction, - WTFMove(microtaskArgs[0]), - WTFMove(microtaskArgs[1]), - WTFMove(microtaskArgs[2]), - WTFMove(microtaskArgs[3])); + JSC::QueuedTask task { nullptr, JSC::InternalMicrotask::BunPerformMicrotaskJob, globalObject, microTaskFunction, WTFMove(microtaskArgs[0]), WTFMove(microtaskArgs[1]), WTFMove(microtaskArgs[2]), WTFMove(microtaskArgs[3]) }; + globalObject->vm().queueMicrotask(WTFMove(task)); } extern "C" WebCore::AbortSignal* WebCore__AbortSignal__new(JSC::JSGlobalObject* globalObject) diff --git a/src/bun.js/bindings/webcore/JSDOMPromise.cpp b/src/bun.js/bindings/webcore/JSDOMPromise.cpp index 1403399c488ce0..a8ec36dea61abf 100644 --- a/src/bun.js/bindings/webcore/JSDOMPromise.cpp +++ b/src/bun.js/bindings/webcore/JSDOMPromise.cpp @@ -76,12 +76,12 @@ auto DOMPromise::whenPromiseIsSettled(JSDOMGlobalObject* globalObject, JSC::JSOb JSC::JSValue DOMPromise::result() const { - return promise()->result(m_globalObject->vm()); + return promise()->result(); } DOMPromise::Status DOMPromise::status() const { - switch (promise()->status(m_globalObject->vm())) { + switch (promise()->status()) { case JSC::JSPromise::Status::Pending: return Status::Pending; case JSC::JSPromise::Status::Fulfilled: diff --git a/src/bun.js/modules/NodeModuleModule.cpp b/src/bun.js/modules/NodeModuleModule.cpp index 6655ea9fbc3c86..05f35367ea39d7 100644 --- a/src/bun.js/modules/NodeModuleModule.cpp +++ b/src/bun.js/modules/NodeModuleModule.cpp @@ -725,7 +725,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionRunMain, (JSGlobalObject * globalObject, JSC: RETURN_IF_EXCEPTION(scope, {}); JSC::JSNativeStdFunction* resolverFunction = JSC::JSNativeStdFunction::create(vm, globalObject, 1, String(), resolverFunctionCallback); - auto result = promise->then(globalObject, resolverFunction, nullptr); + auto result = promise->then(globalObject, resolverFunction, globalObject->promiseEmptyOnRejectedFunction()); RETURN_IF_EXCEPTION(scope, {}); Bun__VirtualMachine__setOverrideModuleRunMainPromise(defaultGlobalObject(globalObject)->bunVM(), result);