diff --git a/src/bun.js/bindings/WebCoreOpaqueRoot.h b/src/bun.js/bindings/WebCoreOpaqueRoot.h index dff351fd291359..96b98a0a775b40 100644 --- a/src/bun.js/bindings/WebCoreOpaqueRoot.h +++ b/src/bun.js/bindings/WebCoreOpaqueRoot.h @@ -26,6 +26,8 @@ #pragma once +#include "Node.h" + namespace WebCore { class WebCoreOpaqueRoot { @@ -38,48 +40,28 @@ class WebCoreOpaqueRoot { WebCoreOpaqueRoot(std::nullptr_t) {} - bool isNode() const { return false; } void* pointer() const { return m_pointer; } private: void* m_pointer { nullptr }; - bool m_isNode { false }; }; template -ALWAYS_INLINE void addWebCoreOpaqueRoot(Visitor& visitor, WebCoreOpaqueRoot root) -{ - visitor.addOpaqueRoot(root.pointer()); -} +inline void addWebCoreOpaqueRoot(Visitor&, WebCoreOpaqueRoot); template -ALWAYS_INLINE void addWebCoreOpaqueRoot(Visitor& visitor, ImplType* impl) -{ - addWebCoreOpaqueRoot(visitor, root(impl)); -} +inline void addWebCoreOpaqueRoot(Visitor&, ImplType*); template -ALWAYS_INLINE void addWebCoreOpaqueRoot(Visitor& visitor, ImplType& impl) -{ - addWebCoreOpaqueRoot(visitor, root(&impl)); -} +inline void addWebCoreOpaqueRoot(Visitor&, ImplType&); template -ALWAYS_INLINE bool containsWebCoreOpaqueRoot(Visitor& visitor, WebCoreOpaqueRoot root) -{ - return visitor.containsOpaqueRoot(root.pointer()); -} +inline bool containsWebCoreOpaqueRoot(Visitor&, WebCoreOpaqueRoot); template -ALWAYS_INLINE bool containsWebCoreOpaqueRoot(Visitor& visitor, ImplType& impl) -{ - return containsWebCoreOpaqueRoot(visitor, root(&impl)); -} +inline bool containsWebCoreOpaqueRoot(Visitor&, ImplType&); template -ALWAYS_INLINE bool containsWebCoreOpaqueRoot(Visitor& visitor, ImplType* impl) -{ - return containsWebCoreOpaqueRoot(visitor, root(impl)); -} +inline bool containsWebCoreOpaqueRoot(Visitor&, ImplType*); } // namespace WebCore diff --git a/src/bun.js/bindings/WebCoreOpaqueRootInlines.h b/src/bun.js/bindings/WebCoreOpaqueRootInlines.h new file mode 100644 index 00000000000000..e25b855e903c8d --- /dev/null +++ b/src/bun.js/bindings/WebCoreOpaqueRootInlines.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "WebCoreOpaqueRoot.h" + +namespace WebCore { + +template +ALWAYS_INLINE void addWebCoreOpaqueRoot(Visitor& visitor, WebCoreOpaqueRoot root) +{ + visitor.addOpaqueRoot(root.pointer()); +} + +template +ALWAYS_INLINE void addWebCoreOpaqueRoot(Visitor& visitor, ImplType* impl) +{ + addWebCoreOpaqueRoot(visitor, root(impl)); +} + +template +ALWAYS_INLINE void addWebCoreOpaqueRoot(Visitor& visitor, ImplType& impl) +{ + addWebCoreOpaqueRoot(visitor, root(&impl)); +} + +template +ALWAYS_INLINE bool containsWebCoreOpaqueRoot(Visitor& visitor, WebCoreOpaqueRoot root) +{ + return visitor.containsOpaqueRoot(root.pointer()); +} + +template +ALWAYS_INLINE bool containsWebCoreOpaqueRoot(Visitor& visitor, ImplType& impl) +{ + return containsWebCoreOpaqueRoot(visitor, root(&impl)); +} + +template +ALWAYS_INLINE bool containsWebCoreOpaqueRoot(Visitor& visitor, ImplType* impl) +{ + return containsWebCoreOpaqueRoot(visitor, root(impl)); +} + +} // namespace WebCore diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 8bd08af25cb6bb..c1bcb693198f73 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -1835,7 +1835,6 @@ JSC_DECLARE_HOST_FUNCTION(makeGetterTypeErrorForBuiltins); JSC_DECLARE_HOST_FUNCTION(makeDOMExceptionForBuiltins); JSC_DECLARE_HOST_FUNCTION(createWritableStreamFromInternal); JSC_DECLARE_HOST_FUNCTION(getInternalWritableStream); -JSC_DECLARE_HOST_FUNCTION(whenSignalAborted); JSC_DECLARE_HOST_FUNCTION(isAbortSignal); JSC_DEFINE_HOST_FUNCTION(makeThisTypeErrorForBuiltins, (JSGlobalObject * globalObject, CallFrame* callFrame)) @@ -1918,7 +1917,7 @@ JSC_DEFINE_HOST_FUNCTION(createWritableStreamFromInternal, (JSGlobalObject * glo return JSValue::encode(toJSNewlyCreated(globalObject, jsDOMGlobalObject, WritableStream::create(WTFMove(internalWritableStream)))); } -JSC_DEFINE_HOST_FUNCTION(whenSignalAborted, (JSGlobalObject * globalObject, CallFrame* callFrame)) +JSC_DEFINE_HOST_FUNCTION(addAbortAlgorithmToSignal, (JSGlobalObject * globalObject, CallFrame* callFrame)) { ASSERT(callFrame); ASSERT(callFrame->argumentCount() == 2); @@ -1930,8 +1929,21 @@ JSC_DEFINE_HOST_FUNCTION(whenSignalAborted, (JSGlobalObject * globalObject, Call Ref abortAlgorithm = JSAbortAlgorithm::create(vm, callFrame->uncheckedArgument(1).getObject()); - bool result = WebCore::AbortSignal::whenSignalAborted(abortSignal->wrapped(), WTFMove(abortAlgorithm)); - return JSValue::encode(result ? JSValue(JSC::JSValue::JSTrue) : JSValue(JSC::JSValue::JSFalse)); + auto algorithmIdentifier = AbortSignal::addAbortAlgorithmToSignal(abortSignal->wrapped(), WTFMove(abortAlgorithm)); + return JSValue::encode(JSC::jsNumber(algorithmIdentifier)); +} + +JSC_DEFINE_HOST_FUNCTION(removeAbortAlgorithmFromSignal, (JSGlobalObject*, CallFrame* callFrame)) +{ + ASSERT(callFrame); + ASSERT(callFrame->argumentCount() == 2); + + auto* abortSignal = jsDynamicCast(callFrame->uncheckedArgument(0)); + if (UNLIKELY(!abortSignal)) + return JSValue::encode(JSValue(JSC::JSValue::JSFalse)); + + AbortSignal::removeAbortAlgorithmFromSignal(abortSignal->wrapped(), callFrame->uncheckedArgument(1).asUInt32()); + return JSValue::encode(JSC::jsUndefined()); } JSC_DEFINE_HOST_FUNCTION(isAbortSignal, (JSGlobalObject*, CallFrame* callFrame)) @@ -3351,7 +3363,8 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm) GlobalPropertyInfo(builtinNames.makeThisTypeErrorPrivateName(), JSFunction::create(vm, this, 2, String(), makeThisTypeErrorForBuiltins, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.makeGetterTypeErrorPrivateName(), JSFunction::create(vm, this, 2, String(), makeGetterTypeErrorForBuiltins, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.makeDOMExceptionPrivateName(), JSFunction::create(vm, this, 2, String(), makeDOMExceptionForBuiltins, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), - GlobalPropertyInfo(builtinNames.whenSignalAbortedPrivateName(), JSFunction::create(vm, this, 2, String(), whenSignalAborted, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), + GlobalPropertyInfo(builtinNames.addAbortAlgorithmToSignalPrivateName(), JSFunction::create(vm, this, 2, String(), addAbortAlgorithmToSignal, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), + GlobalPropertyInfo(builtinNames.removeAbortAlgorithmFromSignalPrivateName(), JSFunction::create(vm, this, 2, String(), removeAbortAlgorithmFromSignal, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.cloneArrayBufferPrivateName(), JSFunction::create(vm, this, 3, String(), cloneArrayBuffer, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.structuredCloneForStreamPrivateName(), JSFunction::create(vm, this, 1, String(), structuredCloneForStream, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.isAbortSignalPrivateName(), JSFunction::create(vm, this, 1, String(), isAbortSignal, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 210f3df7a265f9..f7e77d87222f09 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -5261,7 +5261,7 @@ extern "C" bool WebCore__AbortSignal__aborted(WebCore__AbortSignal* arg0) extern "C" JSC__JSValue WebCore__AbortSignal__abortReason(WebCore__AbortSignal* arg0) { WebCore::AbortSignal* abortSignal = reinterpret_cast(arg0); - return JSC::JSValue::encode(abortSignal->reason()); + return JSC::JSValue::encode(abortSignal->reason().getValue(jsNull())); } extern "C" WebCore__AbortSignal* WebCore__AbortSignal__ref(WebCore__AbortSignal* arg0) @@ -5288,7 +5288,7 @@ extern "C" WebCore__AbortSignal* WebCore__AbortSignal__addListener(WebCore__Abor WebCore::AbortSignal* abortSignal = reinterpret_cast(arg0); if (abortSignal->aborted()) { - callback(ctx, JSC::JSValue::encode(abortSignal->reason())); + callback(ctx, JSC::JSValue::encode(abortSignal->reason().getValue(jsNull()))); return arg0; } diff --git a/src/bun.js/bindings/webcore/AbortController.cpp b/src/bun.js/bindings/webcore/AbortController.cpp index 8e419971ac0c58..46dffc7c196bc3 100644 --- a/src/bun.js/bindings/webcore/AbortController.cpp +++ b/src/bun.js/bindings/webcore/AbortController.cpp @@ -29,7 +29,9 @@ #include "AbortSignal.h" #include "DOMException.h" #include "JSDOMException.h" -// #include +#include +#include "WebCoreOpaqueRoot.h" +#include "WebCoreOpaqueRootInlines.h" namespace WebCore { @@ -56,9 +58,19 @@ void AbortController::abort(JSDOMGlobalObject& globalObject, JSC::JSValue reason { ASSERT(reason); if (reason.isUndefined()) - reason = toJS(&globalObject, &globalObject, DOMException::create(AbortError)); + reason = toJS(&globalObject, &globalObject, DOMException::create(ExceptionCode::AbortError)); - m_signal->signalAbort(reason); + protectedSignal()->signalAbort(reason); +} + +WebCoreOpaqueRoot AbortController::opaqueRoot() +{ + return root(&signal()); +} + +Ref AbortController::protectedSignal() const +{ + return m_signal; } } diff --git a/src/bun.js/bindings/webcore/AbortController.h b/src/bun.js/bindings/webcore/AbortController.h index f7bf96f25b69be..563cffed6c9f65 100644 --- a/src/bun.js/bindings/webcore/AbortController.h +++ b/src/bun.js/bindings/webcore/AbortController.h @@ -43,6 +43,8 @@ class GlobalObject; namespace WebCore { +class WebCoreOpaqueRoot; + class AbortSignal; class ScriptExecutionContext; @@ -55,8 +57,11 @@ class AbortController final : public ScriptWrappable, public RefCounted protectedSignal() const; void abort(Zig::GlobalObject&, JSC::JSValue reason); + WebCoreOpaqueRoot opaqueRoot(); + private: explicit AbortController(ScriptExecutionContext&); diff --git a/src/bun.js/bindings/webcore/AbortSignal.cpp b/src/bun.js/bindings/webcore/AbortSignal.cpp index d38891b678443c..0c7a3a6de6d2c0 100644 --- a/src/bun.js/bindings/webcore/AbortSignal.cpp +++ b/src/bun.js/bindings/webcore/AbortSignal.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2022 Apple Inc. All rights reserved. + * Copyright (C) 2017-2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,9 +33,10 @@ #include "EventNames.h" #include "JSDOMException.h" #include "ScriptExecutionContext.h" +#include "WebCoreOpaqueRoot.h" #include #include -// #include +#include namespace WebCore { @@ -51,7 +52,7 @@ Ref AbortSignal::abort(JSDOMGlobalObject& globalObject, ScriptExecu { ASSERT(reason); if (reason.isUndefined()) - reason = toJS(&globalObject, &globalObject, DOMException::create(AbortError)); + reason = toJS(&globalObject, &globalObject, DOMException::create(ExceptionCode::AbortError)); return adoptRef(*new AbortSignal(&context, Aborted::Yes, reason)); } @@ -82,16 +83,51 @@ Ref AbortSignal::timeout(ScriptExecutionContext& context, uint64_t return signal; } +Ref AbortSignal::any(ScriptExecutionContext& context, const Vector>& signals) +{ + Ref resultSignal = AbortSignal::create(&context); + + auto abortedSignalIndex = signals.findIf([](auto& signal) { return signal->aborted(); }); + if (abortedSignalIndex != notFound) { + resultSignal->signalAbort(signals[abortedSignalIndex]->reason().getValue()); + return resultSignal; + } + + resultSignal->markAsDependent(); + for (auto& signal : signals) + resultSignal->addSourceSignal(*signal); + + return resultSignal; +} + AbortSignal::AbortSignal(ScriptExecutionContext* context, Aborted aborted, JSC::JSValue reason) : ContextDestructionObserver(context) + , m_reason(reason) , m_aborted(aborted == Aborted::Yes) - , m_reason(context->vm(), reason) { ASSERT(reason); } AbortSignal::~AbortSignal() = default; +void AbortSignal::addSourceSignal(AbortSignal& signal) +{ + if (signal.isDependent()) { + for (Ref sourceSignal : signal.sourceSignals()) + addSourceSignal(sourceSignal); + return; + } + ASSERT(!signal.aborted()); + ASSERT(signal.sourceSignals().isEmptyIgnoringNullReferences()); + m_sourceSignals.add(signal); + signal.addDependentSignal(*this); +} + +void AbortSignal::addDependentSignal(AbortSignal& signal) +{ + m_dependentSignals.add(signal); +} + // https://dom.spec.whatwg.org/#abortsignal-signal-abort void AbortSignal::signalAbort(JSC::JSValue reason) { @@ -101,15 +137,12 @@ void AbortSignal::signalAbort(JSC::JSValue reason) // 2. Set signal’s aborted flag. m_aborted = true; + m_sourceSignals.clear(); + // FIXME: This code is wrong: we should emit a write-barrier. Otherwise, GC can collect it. + // https://bugs.webkit.org/show_bug.cgi?id=236353 ASSERT(reason); - auto& vm = scriptExecutionContext()->vm(); - m_reason.set(vm, reason); - - Ref protectedThis { *this }; - auto algorithms = std::exchange(m_algorithms, {}); - for (auto& algorithm : algorithms) - algorithm(reason); + m_reason.setWeakly(reason); auto callbacks = std::exchange(m_native_callbacks, {}); for (auto callback : callbacks) { @@ -117,6 +150,10 @@ void AbortSignal::signalAbort(JSC::JSValue reason) func(ctx, JSC::JSValue::encode(reason)); } + auto algorithms = std::exchange(m_algorithms, {}); + for (auto& algorithm : algorithms) + algorithm.second(reason); + // 5. Fire an event named abort at signal. dispatchEvent(Event::create(eventNames().abortEvent, Event::CanBubble::No, Event::IsCancelable::No)); } @@ -140,21 +177,15 @@ void AbortSignal::signalFollow(AbortSignal& signal) return; if (signal.aborted()) { - signalAbort(signal.reason()); + signalAbort(signal.reason().getValue()); return; } ASSERT(!m_followingSignal); m_followingSignal = signal; - signal.addAlgorithm([weakThis = WeakPtr { this }](JSC::JSValue reason) { - if (weakThis) { - if (reason.isEmpty() || reason.isUndefined()) { - weakThis->signalAbort(weakThis->m_followingSignal ? weakThis->m_followingSignal->reason() - : JSC::jsUndefined()); - } else { - weakThis->signalAbort(reason); - } - } + signal.addAlgorithm([weakThis = WeakPtr { *this }](JSC::JSValue reason) { + if (RefPtr signal = weakThis.get()) + signal->signalAbort(reason); }); } @@ -163,16 +194,33 @@ void AbortSignal::eventListenersDidChange() m_hasAbortEventListener = hasEventListeners(eventNames().abortEvent); } -bool AbortSignal::whenSignalAborted(AbortSignal& signal, Ref&& algorithm) +uint32_t AbortSignal::addAbortAlgorithmToSignal(AbortSignal& signal, Ref&& algorithm) { if (signal.aborted()) { - algorithm->handleEvent(signal.m_reason.get()); - return true; + algorithm->handleEvent(signal.m_reason.getValue()); + return 0; } - signal.addAlgorithm([algorithm = WTFMove(algorithm)](JSC::JSValue value) mutable { + return signal.addAlgorithm([algorithm = WTFMove(algorithm)](JSC::JSValue value) mutable { algorithm->handleEvent(value); }); - return false; +} + +void AbortSignal::removeAbortAlgorithmFromSignal(AbortSignal& signal, uint32_t algorithmIdentifier) +{ + signal.removeAlgorithm(algorithmIdentifier); +} + +uint32_t AbortSignal::addAlgorithm(Algorithm&& algorithm) +{ + m_algorithms.append(std::make_pair(++m_algorithmIdentifier, WTFMove(algorithm))); + return m_algorithmIdentifier; +} + +void AbortSignal::removeAlgorithm(uint32_t algorithmIdentifier) +{ + m_algorithms.removeFirstMatching([algorithmIdentifier](auto& pair) { + return pair.first == algorithmIdentifier; + }); } void AbortSignal::throwIfAborted(JSC::JSGlobalObject& lexicalGlobalObject) @@ -180,9 +228,14 @@ void AbortSignal::throwIfAborted(JSC::JSGlobalObject& lexicalGlobalObject) if (!aborted()) return; - auto& vm = lexicalGlobalObject.vm(); + Ref vm = lexicalGlobalObject.vm(); auto scope = DECLARE_THROW_SCOPE(vm); - throwException(&lexicalGlobalObject, scope, m_reason.get()); + throwException(&lexicalGlobalObject, scope, m_reason.getValue()); +} + +WebCoreOpaqueRoot root(AbortSignal* signal) +{ + return WebCoreOpaqueRoot { signal }; } } // namespace WebCore diff --git a/src/bun.js/bindings/webcore/AbortSignal.h b/src/bun.js/bindings/webcore/AbortSignal.h index b60d2a6bfc31a1..e5fe1369cba4a8 100644 --- a/src/bun.js/bindings/webcore/AbortSignal.h +++ b/src/bun.js/bindings/webcore/AbortSignal.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017-2022 Apple Inc. All rights reserved. + * Copyright (C) 2017-2023 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,19 +25,22 @@ #pragma once +#include "config.h" + #include "ContextDestructionObserver.h" #include "EventTarget.h" -// #include "JSDOMPromiseDeferred.h" #include "JSValueInWrappedObject.h" #include #include #include +#include #include namespace WebCore { class AbortAlgorithm; class ScriptExecutionContext; +class WebCoreOpaqueRoot; class AbortSignal final : public RefCounted, public EventTargetWithInlineData, private ContextDestructionObserver { WTF_MAKE_ISO_ALLOCATED_EXPORT(AbortSignal, WEBCORE_EXPORT); @@ -49,14 +52,19 @@ class AbortSignal final : public RefCounted, public EventTargetWith static Ref abort(JSDOMGlobalObject&, ScriptExecutionContext&, JSC::JSValue reason); static Ref timeout(ScriptExecutionContext&, uint64_t milliseconds); + static Ref any(ScriptExecutionContext&, const Vector>&); - static bool whenSignalAborted(AbortSignal&, Ref&&); + static uint32_t addAbortAlgorithmToSignal(AbortSignal&, Ref&&); + static void removeAbortAlgorithmFromSignal(AbortSignal&, uint32_t algorithmIdentifier); void signalAbort(JSC::JSValue reason); void signalFollow(AbortSignal&); bool aborted() const { return m_aborted; } - JSValue reason() const { return m_reason.get(); } + const JSValueInWrappedObject& reason() const { return m_reason; } + + void cleanNativeBindings(void* ref); + void addNativeCallback(NativeCallbackTuple callback) { m_native_callbacks.append(callback); } bool hasActiveTimeoutTimer() const { return m_hasActiveTimeoutTimer; } bool hasAbortEventListener() const { return m_hasAbortEventListener; } @@ -64,15 +72,18 @@ class AbortSignal final : public RefCounted, public EventTargetWith using RefCounted::deref; using RefCounted::ref; - using Algorithm = Function; - void addAlgorithm(Algorithm&& algorithm) { m_algorithms.append(WTFMove(algorithm)); } - void cleanNativeBindings(void* ref); - void addNativeCallback(NativeCallbackTuple callback) { m_native_callbacks.append(callback); } + using Algorithm = Function; + uint32_t addAlgorithm(Algorithm&&); + void removeAlgorithm(uint32_t); bool isFollowingSignal() const { return !!m_followingSignal; } void throwIfAborted(JSC::JSGlobalObject&); + using AbortSignalSet = WeakListHashSet; + const AbortSignalSet& sourceSignals() const { return m_sourceSignals; } + AbortSignalSet& sourceSignals() { return m_sourceSignals; } + private: enum class Aborted : bool { No, Yes }; @@ -80,6 +91,11 @@ class AbortSignal final : public RefCounted, public EventTargetWith void setHasActiveTimeoutTimer(bool hasActiveTimeoutTimer) { m_hasActiveTimeoutTimer = hasActiveTimeoutTimer; } + bool isDependent() const { return m_isDependent; } + void markAsDependent() { m_isDependent = true; } + void addSourceSignal(AbortSignal&); + void addDependentSignal(AbortSignal&); + // EventTarget. EventTargetInterface eventTargetInterface() const final { return AbortSignalEventTargetInterfaceType; } ScriptExecutionContext* scriptExecutionContext() const final { return ContextDestructionObserver::scriptExecutionContext(); } @@ -87,13 +103,19 @@ class AbortSignal final : public RefCounted, public EventTargetWith void derefEventTarget() final { deref(); } void eventListenersDidChange() final; - bool m_aborted { false }; - Vector m_algorithms; + Vector> m_algorithms; + WeakPtr m_followingSignal; + AbortSignalSet m_sourceSignals; + AbortSignalSet m_dependentSignals; + JSValueInWrappedObject m_reason; Vector m_native_callbacks; - WeakPtr m_followingSignal; - JSC::Strong m_reason; + uint32_t m_algorithmIdentifier { 0 }; + bool m_aborted { false }; bool m_hasActiveTimeoutTimer { false }; bool m_hasAbortEventListener { false }; + bool m_isDependent { false }; }; -} +WebCoreOpaqueRoot root(AbortSignal*); + +} // namespace WebCore diff --git a/src/bun.js/bindings/webcore/BroadcastChannel.cpp b/src/bun.js/bindings/webcore/BroadcastChannel.cpp index c78bb74cea214a..c393107b6467a0 100644 --- a/src/bun.js/bindings/webcore/BroadcastChannel.cpp +++ b/src/bun.js/bindings/webcore/BroadcastChannel.cpp @@ -72,7 +72,7 @@ static HashMap& ch // return { WTFMove(topOrigin), WTFMove(securityOrigin) }; // } -class BroadcastChannel::MainThreadBridge : public ThreadSafeRefCounted { +class BroadcastChannel::MainThreadBridge : public ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr { public: static Ref create(BroadcastChannel& channel, const String& name, ScriptExecutionContext& context) { @@ -87,13 +87,15 @@ class BroadcastChannel::MainThreadBridge : public ThreadSafeRefCounted&&); - // WeakPtr m_broadcastChannel; - WeakPtr m_broadcastChannel; + WeakPtr m_broadcastChannel; const BroadcastChannelIdentifier m_identifier; const String m_name; // Main thread only. ScriptExecutionContextIdentifier m_contextId; diff --git a/src/bun.js/bindings/webcore/EventTarget.h b/src/bun.js/bindings/webcore/EventTarget.h index f5c4354ee29354..07779a99623bac 100644 --- a/src/bun.js/bindings/webcore/EventTarget.h +++ b/src/bun.js/bindings/webcore/EventTarget.h @@ -65,7 +65,22 @@ struct EventTargetData { bool isFiringEventListeners { false }; }; -class EventTarget : public ScriptWrappable, public CanMakeWeakPtr { +// Do not make WeakPtrImplWithEventTargetData a derived class of DefaultWeakPtrImpl to catch the bug which uses incorrect impl class. +class WeakPtrImplWithEventTargetData final : public WTF::WeakPtrImplBaseSingleThread { +public: + EventTargetData& eventTargetData() { return m_eventTargetData; } + const EventTargetData& eventTargetData() const { return m_eventTargetData; } + + template WeakPtrImplWithEventTargetData(T* ptr) + : WTF::WeakPtrImplBaseSingleThread(ptr) + { + } + +private: + EventTargetData m_eventTargetData; +}; + +class EventTarget : public ScriptWrappable, public CanMakeWeakPtrWithBitField { WTF_MAKE_ISO_ALLOCATED(EventTarget); public: diff --git a/src/bun.js/bindings/webcore/JSAbortController.cpp b/src/bun.js/bindings/webcore/JSAbortController.cpp index 926c8989717f83..e1b7bbfdc65e04 100644 --- a/src/bun.js/bindings/webcore/JSAbortController.cpp +++ b/src/bun.js/bindings/webcore/JSAbortController.cpp @@ -39,9 +39,10 @@ #include "JSDOMWrapperCache.h" #include "ScriptExecutionContext.h" #include "WebCoreJSClientData.h" +#include "WebCoreOpaqueRootInlines.h" #include #include - +#include #include #include #include @@ -49,8 +50,6 @@ #include #include -#include "weak_handle.h" - namespace WebCore { using namespace JSC; @@ -97,9 +96,9 @@ STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSAbortControllerPrototype, JSAbortControlle using JSAbortControllerDOMConstructor = JSDOMConstructor; -template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSAbortControllerDOMConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) +template<> EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSAbortControllerDOMConstructor::construct(JSGlobalObject* lexicalGlobalObject, CallFrame* callFrame) { - VM& vm = lexicalGlobalObject->vm(); + auto& vm = lexicalGlobalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); auto* castedThis = jsCast(callFrame->jsCallee()); ASSERT(castedThis); @@ -139,8 +138,8 @@ template<> void JSAbortControllerDOMConstructor::initializeProperties(VM& vm, JS /* Hash table for prototype */ static const HashTableValue JSAbortControllerPrototypeTableValues[] = { - { "constructor"_s, static_cast(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortControllerConstructor, 0 } }, - { "signal"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortController_signal, 0 } }, + { "constructor"_s, static_cast(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortControllerConstructor, 0 } }, + { "signal"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute, NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortController_signal, 0 } }, { "abort"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsAbortControllerPrototypeFunction_abort, 0 } }, }; @@ -160,13 +159,7 @@ JSAbortController::JSAbortController(Structure* structure, JSDOMGlobalObject& gl { } -void JSAbortController::finishCreation(VM& vm) -{ - Base::finishCreation(vm); - ASSERT(inherits(info())); - - // static_assert(!std::is_base_of::value, "Interface is not marked as [ActiveDOMObject] even though implementation class subclasses ActiveDOMObject."); -} +// static_assert(!std::is_base_of::value, "Interface is not marked as [ActiveDOMObject] even though implementation class subclasses ActiveDOMObject."); JSObject* JSAbortController::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) { @@ -191,14 +184,14 @@ void JSAbortController::destroy(JSC::JSCell* cell) thisObject->JSAbortController::~JSAbortController(); } -JSC_DEFINE_CUSTOM_GETTER(jsAbortControllerConstructor, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, PropertyName)) +JSC_DEFINE_CUSTOM_GETTER(jsAbortControllerConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) { - VM& vm = JSC::getVM(lexicalGlobalObject); + auto& vm = JSC::getVM(lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); auto* prototype = jsDynamicCast(JSValue::decode(thisValue)); if (UNLIKELY(!prototype)) return throwVMTypeError(lexicalGlobalObject, throwScope); - return JSValue::encode(JSAbortController::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject())); + return JSValue::encode(JSAbortController::getConstructor(vm, prototype->globalObject())); } static inline JSValue jsAbortController_signalGetter(JSGlobalObject& lexicalGlobalObject, JSAbortController& thisObject) @@ -209,7 +202,7 @@ static inline JSValue jsAbortController_signalGetter(JSGlobalObject& lexicalGlob RELEASE_AND_RETURN(throwScope, (toJS>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.signal()))); } -JSC_DEFINE_CUSTOM_GETTER(jsAbortController_signal, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, PropertyName attributeName)) +JSC_DEFINE_CUSTOM_GETTER(jsAbortController_signal, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) { return IDLAttribute::get(*lexicalGlobalObject, thisValue, attributeName); } @@ -248,7 +241,7 @@ void JSAbortController::visitChildrenImpl(JSCell* cell, Visitor& visitor) auto* thisObject = jsCast(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); Base::visitChildren(thisObject, visitor); - visitor.addOpaqueRoot(WTF::getPtr(thisObject->wrapped().signal())); + addWebCoreOpaqueRoot(visitor, thisObject->wrapped().opaqueRoot()); } DEFINE_VISIT_CHILDREN(JSAbortController); @@ -258,7 +251,7 @@ void JSAbortController::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); Base::analyzeHeap(cell, analyzer); } @@ -277,8 +270,44 @@ void JSAbortControllerOwner::finalize(JSC::Handle handle, void* co uncacheWrapper(world, &jsAbortController->wrapped(), jsAbortController); } +void JSAbortController::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(info())); +} + +#if ENABLE(BINDING_INTEGRITY) +#if PLATFORM(WIN) +#pragma warning(disable : 4483) +extern "C" { +extern void (*const __identifier("??_7AbortController@WebCore@@6B@")[])(); +} +#else +extern "C" { +extern void* _ZTVN7WebCore15AbortControllerE[]; +} +#endif +#endif + JSC::JSValue toJSNewlyCreated(JSC::JSGlobalObject*, JSDOMGlobalObject* globalObject, Ref&& impl) { + + if constexpr (std::is_polymorphic_v) { +#if ENABLE(BINDING_INTEGRITY) + const void* actualVTablePointer = getVTablePointer(impl.ptr()); +#if PLATFORM(WIN) + void* expectedVTablePointer = __identifier("??_7AbortController@WebCore@@6B@"); +#else + void* expectedVTablePointer = &_ZTVN7WebCore15AbortControllerE[2]; +#endif + + // If you hit this assertion you either have a use after free bug, or + // AbortController has subclasses. If AbortController has subclasses that get passed + // to toJS() we currently require AbortController you to opt out of binding hardening + // by adding the SkipVTableValidation attribute to the interface IDL definition + RELEASE_ASSERT(actualVTablePointer == expectedVTablePointer); +#endif + } return createWrapper(globalObject, WTFMove(impl)); } @@ -287,7 +316,7 @@ JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* g return wrap(lexicalGlobalObject, globalObject, impl); } -AbortController* JSAbortController::toWrapped(JSC::VM& vm, JSC::JSValue value) +AbortController* JSAbortController::toWrapped(JSC::VM&, JSC::JSValue value) { if (auto* wrapper = jsDynamicCast(value)) return &wrapper->wrapped(); diff --git a/src/bun.js/bindings/webcore/JSAbortController.h b/src/bun.js/bindings/webcore/JSAbortController.h index 7801a6207bd104..f2c5deebc67d76 100644 --- a/src/bun.js/bindings/webcore/JSAbortController.h +++ b/src/bun.js/bindings/webcore/JSAbortController.h @@ -20,8 +20,6 @@ #pragma once -#include "root.h" - #include "AbortController.h" #include "JSDOMWrapper.h" #include @@ -33,8 +31,9 @@ class JSAbortController : public JSDOMWrapper { using Base = JSDOMWrapper; static JSAbortController* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref&& impl) { - JSAbortController* ptr = new (NotNull, JSC::allocateCell(globalObject->vm())) JSAbortController(structure, *globalObject, WTFMove(impl)); - ptr->finishCreation(globalObject->vm()); + auto& vm = globalObject->vm(); + JSAbortController* ptr = new (NotNull, JSC::allocateCell(vm)) JSAbortController(structure, *globalObject, WTFMove(impl)); + ptr->finishCreation(vm); return ptr; } diff --git a/src/bun.js/bindings/webcore/JSAbortSignal.cpp b/src/bun.js/bindings/webcore/JSAbortSignal.cpp index aa33d8871e3bb7..62ad867a3894d9 100644 --- a/src/bun.js/bindings/webcore/JSAbortSignal.cpp +++ b/src/bun.js/bindings/webcore/JSAbortSignal.cpp @@ -26,7 +26,6 @@ #include "ExtendedDOMClientIsoSubspaces.h" #include "ExtendedDOMIsoSubspaces.h" #include "IDLTypes.h" -#include "JSAbortAlgorithm.h" #include "JSAbortSignal.h" #include "JSDOMAttribute.h" #include "JSDOMBinding.h" @@ -34,9 +33,9 @@ #include "JSDOMConvertAny.h" #include "JSDOMConvertBase.h" #include "JSDOMConvertBoolean.h" -#include "JSDOMConvertCallbacks.h" #include "JSDOMConvertInterface.h" #include "JSDOMConvertNumbers.h" +#include "JSDOMConvertSequences.h" #include "JSDOMExceptionHandling.h" #include "JSDOMGlobalObject.h" #include "JSDOMGlobalObjectInlines.h" @@ -46,6 +45,7 @@ #include "ScriptExecutionContext.h" #include "WebCoreJSClientData.h" #include +#include #include #include #include @@ -59,9 +59,9 @@ using namespace JSC; // Functions -static JSC_DECLARE_HOST_FUNCTION(jsAbortSignalConstructorFunction_whenSignalAborted); static JSC_DECLARE_HOST_FUNCTION(jsAbortSignalConstructorFunction_abort); static JSC_DECLARE_HOST_FUNCTION(jsAbortSignalConstructorFunction_timeout); +static JSC_DECLARE_HOST_FUNCTION(jsAbortSignalConstructorFunction_any); static JSC_DECLARE_HOST_FUNCTION(jsAbortSignalPrototypeFunction_throwIfAborted); // Attributes @@ -109,9 +109,9 @@ using JSAbortSignalDOMConstructor = JSDOMConstructorNotConstructable(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsAbortSignalConstructorFunction_whenSignalAborted, 2 } }, { "abort"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsAbortSignalConstructorFunction_abort, 0 } }, { "timeout"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsAbortSignalConstructorFunction_timeout, 1 } }, + { "any"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsAbortSignalConstructorFunction_any, 1 } }, }; template<> const ClassInfo JSAbortSignalDOMConstructor::s_info = { "AbortSignal"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSAbortSignalDOMConstructor) }; @@ -129,18 +129,27 @@ template<> void JSAbortSignalDOMConstructor::initializeProperties(VM& vm, JSDOMG putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); putDirect(vm, vm.propertyNames->prototype, JSAbortSignal::prototype(vm, globalObject), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); reifyStaticProperties(vm, JSAbortSignal::info(), JSAbortSignalConstructorTableValues, *this); + // if (!((&globalObject)->inherits() || (&globalObject)->inherits())) { + // auto propertyName = Identifier::fromString(vm, "timeout"_s); + // VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable); + // DeletePropertySlot slot; + // JSObject::deleteProperty(this, &globalObject, propertyName, slot); + // } + // if (!jsCast(&globalObject)->scriptExecutionContext()->settingsValues().abortSignalAnyOperationEnabled) { + // auto propertyName = Identifier::fromString(vm, "any"_s); + // VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable); + // DeletePropertySlot slot; + // JSObject::deleteProperty(this, &globalObject, propertyName, slot); + // } } /* Hash table for prototype */ static const HashTableValue JSAbortSignalPrototypeTableValues[] = { - { "constructor"_s, static_cast(JSC::PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortSignalConstructor, 0 } }, - { "aborted"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortSignal_aborted, 0 } }, - { "reason"_s, static_cast(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortSignal_reason, 0 } }, - { "onabort"_s, - static_cast(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute), - NoIntrinsic, - { HashTableValue::GetterSetterType, jsAbortSignal_onabort, setJSAbortSignal_onabort } }, + { "constructor"_s, static_cast(PropertyAttribute::DontEnum), NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortSignalConstructor, 0 } }, + { "aborted"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute, NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortSignal_aborted, 0 } }, + { "reason"_s, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute, NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortSignal_reason, 0 } }, + { "onabort"_s, JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute, NoIntrinsic, { HashTableValue::GetterSetterType, jsAbortSignal_onabort, setJSAbortSignal_onabort } }, { "throwIfAborted"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsAbortSignalPrototypeFunction_throwIfAborted, 0 } }, }; @@ -150,7 +159,6 @@ void JSAbortSignalPrototype::finishCreation(VM& vm) { Base::finishCreation(vm); reifyStaticProperties(vm, JSAbortSignal::info(), JSAbortSignalPrototypeTableValues, *this); - putDirect(vm, static_cast(vm.clientData)->builtinNames().whenSignalAbortedPrivateName(), JSFunction::create(vm, globalObject(), 0, String(), jsAbortSignalConstructorFunction_whenSignalAborted, ImplementationVisibility::Public), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); } @@ -167,9 +175,16 @@ void JSAbortSignal::finishCreation(VM& vm) ASSERT(inherits(info())); } +Ref JSAbortSignal::protectedWrapped() const +{ + return wrapped(); +} + JSObject* JSAbortSignal::createPrototype(VM& vm, JSDOMGlobalObject& globalObject) { - return JSAbortSignalPrototype::create(vm, &globalObject, JSAbortSignalPrototype::createStructure(vm, &globalObject, JSEventTarget::prototype(vm, globalObject))); + auto* structure = JSAbortSignalPrototype::createStructure(vm, &globalObject, JSEventTarget::prototype(vm, globalObject)); + structure->setMayBePrototype(true); + return JSAbortSignalPrototype::create(vm, &globalObject, structure); } JSObject* JSAbortSignal::prototype(VM& vm, JSDOMGlobalObject& globalObject) @@ -182,14 +197,14 @@ JSValue JSAbortSignal::getConstructor(VM& vm, const JSGlobalObject* globalObject return getDOMConstructor(vm, *jsCast(globalObject)); } -JSC_DEFINE_CUSTOM_GETTER(jsAbortSignalConstructor, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, PropertyName)) +JSC_DEFINE_CUSTOM_GETTER(jsAbortSignalConstructor, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName)) { - VM& vm = JSC::getVM(lexicalGlobalObject); + auto& vm = JSC::getVM(lexicalGlobalObject); auto throwScope = DECLARE_THROW_SCOPE(vm); auto* prototype = jsDynamicCast(JSValue::decode(thisValue)); if (UNLIKELY(!prototype)) return throwVMTypeError(lexicalGlobalObject, throwScope); - return JSValue::encode(JSAbortSignal::getConstructor(JSC::getVM(lexicalGlobalObject), prototype->globalObject())); + return JSValue::encode(JSAbortSignal::getConstructor(vm, prototype->globalObject())); } static inline JSValue jsAbortSignal_abortedGetter(JSGlobalObject& lexicalGlobalObject, JSAbortSignal& thisObject) @@ -200,7 +215,7 @@ static inline JSValue jsAbortSignal_abortedGetter(JSGlobalObject& lexicalGlobalO RELEASE_AND_RETURN(throwScope, (toJS(lexicalGlobalObject, throwScope, impl.aborted()))); } -JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_aborted, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, PropertyName attributeName)) +JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_aborted, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) { return IDLAttribute::get(*lexicalGlobalObject, thisValue, attributeName); } @@ -213,7 +228,7 @@ static inline JSValue jsAbortSignal_reasonGetter(JSGlobalObject& lexicalGlobalOb RELEASE_AND_RETURN(throwScope, (toJS(lexicalGlobalObject, throwScope, impl.reason()))); } -JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_reason, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, PropertyName attributeName)) +JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_reason, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) { return IDLAttribute::get(*lexicalGlobalObject, thisValue, attributeName); } @@ -221,10 +236,10 @@ JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_reason, (JSGlobalObject * lexicalGlobalOb static inline JSValue jsAbortSignal_onabortGetter(JSGlobalObject& lexicalGlobalObject, JSAbortSignal& thisObject) { UNUSED_PARAM(lexicalGlobalObject); - return eventHandlerAttribute(thisObject.wrapped(), eventNames().abortEvent, worldForDOMObject(thisObject)); + return eventHandlerAttribute(thisObject.protectedWrapped(), eventNames().abortEvent, worldForDOMObject(thisObject)); } -JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_onabort, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, PropertyName attributeName)) +JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_onabort, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName)) { return IDLAttribute::get(*lexicalGlobalObject, thisValue, attributeName); } @@ -232,40 +247,19 @@ JSC_DEFINE_CUSTOM_GETTER(jsAbortSignal_onabort, (JSGlobalObject * lexicalGlobalO static inline bool setJSAbortSignal_onabortSetter(JSGlobalObject& lexicalGlobalObject, JSAbortSignal& thisObject, JSValue value) { auto& vm = JSC::getVM(&lexicalGlobalObject); - setEventHandlerAttribute(thisObject.wrapped(), eventNames().abortEvent, value, thisObject); + UNUSED_PARAM(vm); + setEventHandlerAttribute(thisObject.protectedWrapped(), eventNames().abortEvent, value, thisObject); vm.writeBarrier(&thisObject, value); ensureStillAliveHere(value); return true; } -JSC_DEFINE_CUSTOM_SETTER(setJSAbortSignal_onabort, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue encodedValue, PropertyName attributeName)) +JSC_DEFINE_CUSTOM_SETTER(setJSAbortSignal_onabort, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, EncodedJSValue encodedValue, PropertyName attributeName)) { return IDLAttribute::set(*lexicalGlobalObject, thisValue, encodedValue, attributeName); } -static inline JSC::EncodedJSValue jsAbortSignalConstructorFunction_whenSignalAbortedBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) -{ - auto& vm = JSC::getVM(lexicalGlobalObject); - auto throwScope = DECLARE_THROW_SCOPE(vm); - UNUSED_PARAM(throwScope); - UNUSED_PARAM(callFrame); - if (UNLIKELY(callFrame->argumentCount() < 2)) - return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); - EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); - auto object = convert>(*lexicalGlobalObject, argument0.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentTypeError(lexicalGlobalObject, scope, 0, "object", "AbortSignal", "whenSignalAborted", "AbortSignal"); }); - RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); - EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1); - auto algorithm = convert>(*lexicalGlobalObject, argument1.value(), [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeFunctionError(lexicalGlobalObject, scope, 1, "algorithm", "AbortSignal", "whenSignalAborted"); }); - RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); - RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS(*lexicalGlobalObject, throwScope, AbortSignal::whenSignalAborted(*object, algorithm.releaseNonNull())))); -} - -JSC_DEFINE_HOST_FUNCTION(jsAbortSignalConstructorFunction_whenSignalAborted, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) -{ - return IDLOperation::callStatic(*lexicalGlobalObject, *callFrame, "whenSignalAborted"); -} - static inline JSC::EncodedJSValue jsAbortSignalConstructorFunction_abortBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) { auto& vm = JSC::getVM(lexicalGlobalObject); @@ -308,6 +302,28 @@ JSC_DEFINE_HOST_FUNCTION(jsAbortSignalConstructorFunction_timeout, (JSGlobalObje return IDLOperation::callStatic(*lexicalGlobalObject, *callFrame, "timeout"); } +static inline JSC::EncodedJSValue jsAbortSignalConstructorFunction_anyBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame) +{ + auto& vm = JSC::getVM(lexicalGlobalObject); + auto throwScope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(throwScope); + UNUSED_PARAM(callFrame); + if (UNLIKELY(callFrame->argumentCount() < 1)) + return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject)); + auto* context = jsCast(lexicalGlobalObject)->scriptExecutionContext(); + if (UNLIKELY(!context)) + return JSValue::encode(jsUndefined()); + EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0); + auto signals = convert>>(*lexicalGlobalObject, argument0.value()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + RELEASE_AND_RETURN(throwScope, JSValue::encode(toJSNewlyCreated>(*lexicalGlobalObject, *jsCast(lexicalGlobalObject), throwScope, AbortSignal::any(*context, WTFMove(signals))))); +} + +JSC_DEFINE_HOST_FUNCTION(jsAbortSignalConstructorFunction_any, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame)) +{ + return IDLOperation::callStatic(*lexicalGlobalObject, *callFrame, "any"); +} + static inline JSC::EncodedJSValue jsAbortSignalPrototypeFunction_throwIfAbortedBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation::ClassParameter castedThis) { auto& vm = JSC::getVM(lexicalGlobalObject); @@ -360,7 +376,7 @@ void JSAbortSignal::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer) auto* thisObject = jsCast(cell); analyzer.setWrappedObjectForCell(cell, &thisObject->wrapped()); if (thisObject->scriptExecutionContext()) - analyzer.setLabelForCell(cell, "url " + thisObject->scriptExecutionContext()->url().string()); + analyzer.setLabelForCell(cell, "url "_s + thisObject->scriptExecutionContext()->url().string()); Base::analyzeHeap(cell, analyzer); } @@ -368,7 +384,7 @@ void JSAbortSignalOwner::finalize(JSC::Handle handle, void* contex { auto* jsAbortSignal = static_cast(handle.slot()->asCell()); auto& world = *static_cast(context); - uncacheWrapper(world, &jsAbortSignal->wrapped(), jsAbortSignal); + uncacheWrapper(world, jsAbortSignal->protectedWrapped().ptr(), jsAbortSignal); } #if ENABLE(BINDING_INTEGRITY) @@ -411,10 +427,11 @@ JSC::JSValue toJS(JSC::JSGlobalObject* lexicalGlobalObject, JSDOMGlobalObject* g return wrap(lexicalGlobalObject, globalObject, impl); } -AbortSignal* JSAbortSignal::toWrapped(JSC::VM& vm, JSC::JSValue value) +AbortSignal* JSAbortSignal::toWrapped(JSC::VM&, JSC::JSValue value) { if (auto* wrapper = jsDynamicCast(value)) return &wrapper->wrapped(); return nullptr; } + } diff --git a/src/bun.js/bindings/webcore/JSAbortSignal.h b/src/bun.js/bindings/webcore/JSAbortSignal.h index afca5f42c245e1..c2fe6fa9cc450a 100644 --- a/src/bun.js/bindings/webcore/JSAbortSignal.h +++ b/src/bun.js/bindings/webcore/JSAbortSignal.h @@ -33,8 +33,9 @@ class JSAbortSignal : public JSEventTarget { using DOMWrapped = AbortSignal; static JSAbortSignal* create(JSC::Structure* structure, JSDOMGlobalObject* globalObject, Ref&& impl) { - JSAbortSignal* ptr = new (NotNull, JSC::allocateCell(globalObject->vm())) JSAbortSignal(structure, *globalObject, WTFMove(impl)); - ptr->finishCreation(globalObject->vm()); + auto& vm = globalObject->vm(); + JSAbortSignal* ptr = new (NotNull, JSC::allocateCell(vm)) JSAbortSignal(structure, *globalObject, WTFMove(impl)); + ptr->finishCreation(vm); return ptr; } @@ -66,6 +67,9 @@ class JSAbortSignal : public JSEventTarget { { return static_cast(Base::wrapped()); } + + Ref protectedWrapped() const; + protected: JSAbortSignal(JSC::Structure*, JSDOMGlobalObject&, Ref&&); diff --git a/src/bun.js/bindings/webcore/JSDOMConvertSequences.h b/src/bun.js/bindings/webcore/JSDOMConvertSequences.h index eec3ca36c00152..9ac0e4281e68f9 100644 --- a/src/bun.js/bindings/webcore/JSDOMConvertSequences.h +++ b/src/bun.js/bindings/webcore/JSDOMConvertSequences.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2017 Apple Inc. All rights reserved. + * Copyright (C) 2016-2022 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -95,23 +95,24 @@ struct NumericSequenceConverter { auto indexValue = array->butterfly()->contiguousInt32().at(array, i).get(); ASSERT(!indexValue || indexValue.isInt32()); if (!indexValue) - result.unsafeAppendWithoutCapacityCheck(0); + result.append(0); else - result.unsafeAppendWithoutCapacityCheck(indexValue.asInt32()); + result.append(indexValue.asInt32()); } return WTFMove(result); } ASSERT(indexingType == JSC::DoubleShape); + ASSERT(JSC::Options::allowDoubleShape()); for (unsigned i = 0; i < length; i++) { double doubleValue = array->butterfly()->contiguousDouble().at(array, i); if (std::isnan(doubleValue)) - result.unsafeAppendWithoutCapacityCheck(0); + result.append(0); else { auto convertedValue = Converter::convert(lexicalGlobalObject, scope, doubleValue); RETURN_IF_EXCEPTION(scope, {}); - result.unsafeAppendWithoutCapacityCheck(convertedValue); + result.append(convertedValue); } } return WTFMove(result); @@ -219,7 +220,7 @@ struct SequenceConverter { auto convertedValue = Converter::convert(lexicalGlobalObject, indexValue); RETURN_IF_EXCEPTION(scope, {}); - result.unsafeAppendWithoutCapacityCheck(convertedValue); + result.append(convertedValue); } return result; } @@ -234,7 +235,7 @@ struct SequenceConverter { auto convertedValue = Converter::convert(lexicalGlobalObject, indexValue); RETURN_IF_EXCEPTION(scope, {}); - result.unsafeAppendWithoutCapacityCheck(convertedValue); + result.append(convertedValue); } return result; } diff --git a/src/bun.js/bindings/webcore/JSValueInWrappedObject.h b/src/bun.js/bindings/webcore/JSValueInWrappedObject.h index 041bba307d5c58..82bd23723193d8 100644 --- a/src/bun.js/bindings/webcore/JSValueInWrappedObject.h +++ b/src/bun.js/bindings/webcore/JSValueInWrappedObject.h @@ -26,9 +26,9 @@ #include "DOMWrapperWorld.h" #include "JSDOMWrapper.h" -#include +#include #include -#include +#include #include namespace WebCore { @@ -51,10 +51,6 @@ class JSValueInWrappedObject { void setWeakly(JSC::JSValue); JSC::JSValue getValue(JSC::JSValue nullValue = JSC::jsUndefined()) const; - // FIXME: Remove this once IDBRequest semantic bug is fixed. - // https://bugs.webkit.org/show_bug.cgi?id=236278 - void setWithoutBarrier(JSValueInWrappedObject&); - private: // Keep in mind that all of these fields are accessed concurrently without lock from concurrent GC thread. JSC::JSValue m_nonCell {}; @@ -62,7 +58,6 @@ class JSValueInWrappedObject { }; JSC::JSValue cachedPropertyValue(JSC::ThrowScope&, JSC::JSGlobalObject&, const JSDOMObject& owner, JSValueInWrappedObject& cacheSlot, const Function&); -JSC::JSValue cachedPropertyValue(JSC::JSGlobalObject&, const JSDOMObject& owner, JSValueInWrappedObject& cacheSlot, const Function&); inline JSValueInWrappedObject::JSValueInWrappedObject(JSC::JSValue value) { @@ -115,14 +110,6 @@ inline void JSValueInWrappedObject::clear() m_cell.clear(); } -inline void JSValueInWrappedObject::setWithoutBarrier(JSValueInWrappedObject& other) -{ - JSC::Weak weak { other.m_cell.get() }; - WTF::storeStoreFence(); // Ensure Weak is fully initialized for concurrent access. - m_nonCell = other.m_nonCell; - m_cell = WTFMove(weak); -} - inline JSC::JSValue cachedPropertyValue(JSC::JSGlobalObject& lexicalGlobalObject, const JSDOMObject& owner, JSValueInWrappedObject& cachedValue, const Function& function) { if (cachedValue && isWorldCompatible(lexicalGlobalObject, cachedValue.getValue())) diff --git a/src/js/builtins.d.ts b/src/js/builtins.d.ts index 386e32f583e8fd..da6f1d6db48315 100644 --- a/src/js/builtins.d.ts +++ b/src/js/builtins.d.ts @@ -228,6 +228,7 @@ declare const $asyncContext: InternalFieldObject<[ReadonlyArray | undefined declare var $_events: TODO; declare function $abortAlgorithm(): TODO; declare function $abortSteps(): TODO; +declare function $addAbortAlgorithmToSignal(signal: AbortSignal, algorithm: () => void): TODO; declare function $addEventListener(): TODO; declare function $appendFromJS(): TODO; declare function $argv(): TODO; @@ -367,6 +368,7 @@ declare function $readableStreamToArray(): TODO; declare function $reader(): TODO; declare function $readyPromise(): TODO; declare function $readyPromiseCapability(): TODO; +declare function $removeAbortAlgorithmFromSignal(signal: AbortSignal, algorithmIdentifier: number): TODO; declare function $redirect(): TODO; declare function $relative(): TODO; declare function $releaseLock(): TODO; diff --git a/src/js/builtins/BunBuiltinNames.h b/src/js/builtins/BunBuiltinNames.h index 3a56f99811a051..884d17263fab4f 100644 --- a/src/js/builtins/BunBuiltinNames.h +++ b/src/js/builtins/BunBuiltinNames.h @@ -25,6 +25,7 @@ using namespace JSC; macro(abortAlgorithm) \ macro(AbortSignal) \ macro(abortSteps) \ + macro(addAbortAlgorithmToSignal) \ macro(addEventListener) \ macro(appendFromJS) \ macro(argv) \ @@ -180,6 +181,7 @@ using namespace JSC; macro(readRequests) \ macro(readyPromise) \ macro(readyPromiseCapability) \ + macro(removeAbortAlgorithmFromSignal) \ macro(redirect) \ macro(relative) \ macro(releaseLock) \ @@ -234,7 +236,6 @@ using namespace JSC; macro(version) \ macro(versions) \ macro(view) \ - macro(whenSignalAborted) \ macro(writable) \ macro(WritableStream) \ macro(WritableStreamDefaultController) \ diff --git a/src/js/builtins/ReadableStreamInternals.ts b/src/js/builtins/ReadableStreamInternals.ts index f693fe8f35e032..1cdcb1bc7b3b6b 100644 --- a/src/js/builtins/ReadableStreamInternals.ts +++ b/src/js/builtins/ReadableStreamInternals.ts @@ -245,7 +245,6 @@ export function readableStreamPipeToWritableStream( source.$disturbed = true; - pipeState.finalized = false; pipeState.shuttingDown = false; pipeState.promiseCapability = $newPromiseCapability(Promise); pipeState.pendingReadPromiseCapability = $newPromiseCapability(Promise); @@ -254,8 +253,6 @@ export function readableStreamPipeToWritableStream( if (signal !== undefined) { const algorithm = reason => { - if (pipeState.finalized) return; - $pipeToShutdownWithAction( pipeState, () => { @@ -290,7 +287,13 @@ export function readableStreamPipeToWritableStream( reason, ); }; - if ($whenSignalAborted(signal, algorithm)) return pipeState.promiseCapability.promise; + const abortAlgorithmIdentifier = (pipeState.abortAlgorithmIdentifier = $addAbortAlgorithmToSignal( + signal, + algorithm, + )); + + if (!abortAlgorithmIdentifier) return pipeState.promiseCapability.promise; + pipeState.signal = signal; } $pipeToErrorsMustBePropagatedForward(pipeState); @@ -480,8 +483,8 @@ export function pipeToFinalize(pipeState) { $writableStreamDefaultWriterRelease(pipeState.writer); $readableStreamReaderGenericRelease(pipeState.reader); - // Instead of removing the abort algorithm as per spec, we make it a no-op which is equivalent. - pipeState.finalized = true; + const signal = pipeState.signal; + if (signal) $removeAbortAlgorithmFromSignal(signal, pipeState.abortAlgorithmIdentifier); if (arguments.length > 1) pipeState.promiseCapability.reject.$call(undefined, arguments[1]); else pipeState.promiseCapability.resolve.$call();