From 162f94957f5a65aa0177bdea2b5810d44b637b5a Mon Sep 17 00:00:00 2001 From: Mark Lam Date: Fri, 31 Mar 2023 10:49:49 -0700 Subject: [PATCH] Cherry-pick 259548.395@safari-7615.1.26.11-branch (1039f0c3235f). Cherry-pick 2c49ff7b0481. rdar://problem/107369977 CloneDeserializer::deserialize() should store cell pointers in a MarkedVector. https://bugs.webkit.org/show_bug.cgi?id=254797 rdar://107369977 Reviewed by Justin Michaud. Previously, CloneDeserializer::deserialize() was storing pointers to newly created objects in a few Vectors. This is problematic because the GC is not aware of Vectors, and cannot scan them. In this patch, we refactor the MarkedArgumentBuffer class into a MarkedVector template class that offer 2 enhancements: 1. It can be configured to store specific types of cell pointer types. This avoids us having to constantly cast JSValues into these pointers. 2. It allows us to specify the type of OverflowHandler we want to use. In this case, we want to use CrashOnOverflow. The previous MarkedArgumentBuffer always assumes RecordOnOverflow. This allows us to avoid having to manually check for overflows, or have to use appendWithCrashOnOverflow. For our current needs, MarkedVector can be used as a drop in replacement for Vector. And we fix the CloneDeserializer::deserialize() issue by replacing the use of Vectors with MarkedVector instead. * Source/JavaScriptCore/heap/Heap.cpp: (JSC::Heap::addCoreConstraints): * Source/JavaScriptCore/heap/Heap.h: * Source/JavaScriptCore/heap/HeapInlines.h: * Source/JavaScriptCore/runtime/ArgList.cpp: (JSC::MarkedVectorBase::addMarkSet): (JSC::MarkedVectorBase::markLists): (JSC::MarkedVectorBase::slowEnsureCapacity): (JSC::MarkedVectorBase::expandCapacity): (JSC::MarkedVectorBase::slowAppend): (JSC::MarkedArgumentBufferBase::addMarkSet): Deleted. (JSC::MarkedArgumentBufferBase::markLists): Deleted. (JSC::MarkedArgumentBufferBase::slowEnsureCapacity): Deleted. (JSC::MarkedArgumentBufferBase::expandCapacity): Deleted. (JSC::MarkedArgumentBufferBase::slowAppend): Deleted. * Source/JavaScriptCore/runtime/ArgList.h: (JSC::MarkedVectorWithSize::MarkedVectorWithSize): (JSC::MarkedVectorWithSize::at const): (JSC::MarkedVectorWithSize::clear): (JSC::MarkedVectorWithSize::append): (JSC::MarkedVectorWithSize::appendWithCrashOnOverflow): (JSC::MarkedVectorWithSize::last const): (JSC::MarkedVectorWithSize::takeLast): (JSC::MarkedVectorWithSize::ensureCapacity): (JSC::MarkedVectorWithSize::hasOverflowed): (JSC::MarkedVectorWithSize::fill): (JSC::MarkedArgumentBufferWithSize::MarkedArgumentBufferWithSize): Deleted. * Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp: (WebCore::AudioWorkletProcessor::buildJSArguments): * Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h: * Source/WebCore/bindings/js/SerializedScriptValue.cpp: (WebCore::CloneDeserializer::deserialize): Canonical link: https://commits.webkit.org/259548.530@safari-7615-branch Identifier: 259548.395@safari-7615.1.26.11-branch --- Source/JavaScriptCore/heap/Heap.cpp | 6 +- Source/JavaScriptCore/heap/Heap.h | 8 +- Source/JavaScriptCore/heap/HeapInlines.h | 6 +- Source/JavaScriptCore/runtime/ArgList.cpp | 46 ++-- Source/JavaScriptCore/runtime/ArgList.h | 206 ++++++++++-------- .../webaudio/AudioWorkletProcessor.cpp | 4 +- .../Modules/webaudio/AudioWorkletProcessor.h | 7 +- .../bindings/js/SerializedScriptValue.cpp | 11 +- 8 files changed, 160 insertions(+), 134 deletions(-) diff --git a/Source/JavaScriptCore/heap/Heap.cpp b/Source/JavaScriptCore/heap/Heap.cpp index 8e53ddead1fd..7e3f8487f3db 100644 --- a/Source/JavaScriptCore/heap/Heap.cpp +++ b/Source/JavaScriptCore/heap/Heap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2022 Apple Inc. All rights reserved. + * Copyright (C) 2003-2023 Apple Inc. All rights reserved. * Copyright (C) 2007 Eric Seidel * * This library is free software; you can redistribute it and/or @@ -2836,9 +2836,9 @@ void Heap::addCoreConstraints() visitor.appendUnbarriered(pair.key); } - if (m_markListSet && m_markListSet->size()) { + if (!m_markListSet.isEmpty()) { SetRootMarkReasonScope rootScope(visitor, RootMarkReason::ConservativeScan); - MarkedArgumentBufferBase::markLists(visitor, *m_markListSet); + MarkedVectorBase::markLists(visitor, m_markListSet); } { diff --git a/Source/JavaScriptCore/heap/Heap.h b/Source/JavaScriptCore/heap/Heap.h index af0e4c46a6ce..fd8cf668baae 100644 --- a/Source/JavaScriptCore/heap/Heap.h +++ b/Source/JavaScriptCore/heap/Heap.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003-2022 Apple Inc. All rights reserved. + * Copyright (C) 2003-2023 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -85,7 +85,7 @@ class MarkStackArray; class MarkStackMergingConstraint; class MarkedJSValueRefArray; class BlockDirectory; -class MarkedArgumentBufferBase; +class MarkedVectorBase; class MarkingConstraint; class MarkingConstraintSet; class MutatorScheduler; @@ -410,7 +410,7 @@ public: JS_EXPORT_PRIVATE std::unique_ptr protectedObjectTypeCounts(); JS_EXPORT_PRIVATE std::unique_ptr objectTypeCounts(); - HashSet& markListSet(); + HashSet& markListSet(); void addMarkedJSValueRefArray(MarkedJSValueRefArray*); template void forEachProtectedCell(const Functor&); @@ -779,7 +779,7 @@ private: size_t m_deprecatedExtraMemorySize { 0 }; ProtectCountSet m_protectedValues; - std::unique_ptr> m_markListSet; + HashSet m_markListSet; SentinelLinkedList> m_markedJSValueRefArrays; std::unique_ptr m_machineThreads; diff --git a/Source/JavaScriptCore/heap/HeapInlines.h b/Source/JavaScriptCore/heap/HeapInlines.h index 39c06b659d9c..4d767a564d5f 100644 --- a/Source/JavaScriptCore/heap/HeapInlines.h +++ b/Source/JavaScriptCore/heap/HeapInlines.h @@ -206,11 +206,9 @@ inline void Heap::decrementDeferralDepthAndGCIfNeeded() } } -inline HashSet& Heap::markListSet() +inline HashSet& Heap::markListSet() { - if (!m_markListSet) - m_markListSet = makeUnique>(); - return *m_markListSet; + return m_markListSet; } inline void Heap::reportExtraMemoryAllocated(size_t size) diff --git a/Source/JavaScriptCore/runtime/ArgList.cpp b/Source/JavaScriptCore/runtime/ArgList.cpp index f2815b80c8c7..a72dea74a56f 100644 --- a/Source/JavaScriptCore/runtime/ArgList.cpp +++ b/Source/JavaScriptCore/runtime/ArgList.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2021 Apple Inc. All rights reserved. + * Copyright (C) 2003-2023 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -27,7 +27,7 @@ using std::min; namespace JSC { -void MarkedArgumentBufferBase::addMarkSet(JSValue v) +void MarkedVectorBase::addMarkSet(JSValue v) { if (m_markSet) return; @@ -52,47 +52,47 @@ void ArgList::getSlice(int startIndex, ArgList& result) const } template -void MarkedArgumentBufferBase::markLists(Visitor& visitor, ListSet& markSet) +void MarkedVectorBase::markLists(Visitor& visitor, ListSet& markSet) { ListSet::iterator end = markSet.end(); for (ListSet::iterator it = markSet.begin(); it != end; ++it) { - MarkedArgumentBufferBase* list = *it; + MarkedVectorBase* list = *it; for (int i = 0; i < list->m_size; ++i) visitor.appendUnbarriered(JSValue::decode(list->slotFor(i))); } } -template void MarkedArgumentBufferBase::markLists(AbstractSlotVisitor&, ListSet&); -template void MarkedArgumentBufferBase::markLists(SlotVisitor&, ListSet&); +template void MarkedVectorBase::markLists(AbstractSlotVisitor&, ListSet&); +template void MarkedVectorBase::markLists(SlotVisitor&, ListSet&); -void MarkedArgumentBufferBase::slowEnsureCapacity(size_t requestedCapacity) +auto MarkedVectorBase::slowEnsureCapacity(size_t requestedCapacity) -> Status { setNeedsOverflowCheck(); auto checkedNewCapacity = CheckedInt32(requestedCapacity); if (UNLIKELY(checkedNewCapacity.hasOverflowed())) - return this->overflowed(); - expandCapacity(checkedNewCapacity); + return Status::Overflowed; + return expandCapacity(checkedNewCapacity); } -void MarkedArgumentBufferBase::expandCapacity() +auto MarkedVectorBase::expandCapacity() -> Status { setNeedsOverflowCheck(); auto checkedNewCapacity = CheckedInt32(m_capacity) * 2; if (UNLIKELY(checkedNewCapacity.hasOverflowed())) - return this->overflowed(); - expandCapacity(checkedNewCapacity); + return Status::Overflowed; + return expandCapacity(checkedNewCapacity); } -void MarkedArgumentBufferBase::expandCapacity(int newCapacity) +auto MarkedVectorBase::expandCapacity(int newCapacity) -> Status { setNeedsOverflowCheck(); ASSERT(m_capacity < newCapacity); auto checkedSize = CheckedSize(newCapacity) * sizeof(EncodedJSValue); if (UNLIKELY(checkedSize.hasOverflowed())) - return this->overflowed(); + return Status::Overflowed; EncodedJSValue* newBuffer = static_cast(Gigacage::tryMalloc(Gigacage::JSValue, checkedSize)); if (!newBuffer) - return this->overflowed(); + return Status::Overflowed; for (int i = 0; i < m_size; ++i) { newBuffer[i] = m_buffer[i]; addMarkSet(JSValue::decode(m_buffer[i])); @@ -103,21 +103,23 @@ void MarkedArgumentBufferBase::expandCapacity(int newCapacity) m_buffer = newBuffer; m_capacity = newCapacity; + return Status::Success; } -void MarkedArgumentBufferBase::slowAppend(JSValue v) +auto MarkedVectorBase::slowAppend(JSValue v) -> Status { ASSERT(m_size <= m_capacity); - if (m_size == m_capacity) - expandCapacity(); - if (UNLIKELY(Base::hasOverflowed())) { - ASSERT(m_needsOverflowCheck); - return; + if (m_size == m_capacity) { + auto status = expandCapacity(); + if (status == Status::Overflowed) { + ASSERT(m_needsOverflowCheck); + return status; + } } - slotFor(m_size) = JSValue::encode(v); ++m_size; addMarkSet(v); + return Status::Success; } } // namespace JSC diff --git a/Source/JavaScriptCore/runtime/ArgList.h b/Source/JavaScriptCore/runtime/ArgList.h index 8ea9b0e308b8..07632263266b 100644 --- a/Source/JavaScriptCore/runtime/ArgList.h +++ b/Source/JavaScriptCore/runtime/ArgList.h @@ -28,20 +28,20 @@ namespace JSC { -class alignas(alignof(EncodedJSValue)) MarkedArgumentBufferBase : public RecordOverflow { - WTF_MAKE_NONCOPYABLE(MarkedArgumentBufferBase); - WTF_MAKE_NONMOVABLE(MarkedArgumentBufferBase); +class alignas(alignof(EncodedJSValue)) MarkedVectorBase { + WTF_MAKE_NONCOPYABLE(MarkedVectorBase); + WTF_MAKE_NONMOVABLE(MarkedVectorBase); WTF_FORBID_HEAP_ALLOCATION; friend class VM; friend class ArgList; +protected: + enum class Status { Success, Overflowed }; public: - using Base = RecordOverflow; - typedef HashSet ListSet; + typedef HashSet ListSet; - ~MarkedArgumentBufferBase() + ~MarkedVectorBase() { - ASSERT(!m_needsOverflowCheck); if (m_markSet) m_markSet->remove(this); @@ -52,92 +52,20 @@ public: size_t size() const { return m_size; } bool isEmpty() const { return !m_size; } - JSValue at(int i) const - { - if (i >= m_size) - return jsUndefined(); - - return JSValue::decode(slotFor(i)); - } - - void clear() - { - ASSERT(!m_needsOverflowCheck); - clearOverflow(); - m_size = 0; - } - - enum OverflowCheckAction { - CrashOnOverflow, - WillCheckLater - }; - template - void appendWithAction(JSValue v) - { - ASSERT(m_size <= m_capacity); - if (m_size == m_capacity || mallocBase()) { - slowAppend(v); - if (action == CrashOnOverflow) - RELEASE_ASSERT(!hasOverflowed()); - return; - } - - slotFor(m_size) = JSValue::encode(v); - ++m_size; - } - void append(JSValue v) { appendWithAction(v); } - void appendWithCrashOnOverflow(JSValue v) { appendWithAction(v); } - void removeLast() { ASSERT(m_size); m_size--; } - JSValue last() - { - ASSERT(m_size); - return JSValue::decode(slotFor(m_size - 1)); - } - - JSValue takeLast() - { - JSValue result = last(); - removeLast(); - return result; - } - template static void markLists(Visitor&, ListSet&); - void ensureCapacity(size_t requestedCapacity) - { - if (requestedCapacity > static_cast(m_capacity)) - slowEnsureCapacity(requestedCapacity); - } - - bool hasOverflowed() - { - clearNeedsOverflowCheck(); - return Base::hasOverflowed(); - } - void overflowCheckNotNeeded() { clearNeedsOverflowCheck(); } - template - void fill(size_t count, const Functor& func) - { - ASSERT(!m_size); - ensureCapacity(count); - if (Base::hasOverflowed()) - return; - m_size = count; - func(reinterpret_cast(&slotFor(0))); - } - protected: // Constructor for a read-write list, to which you may append values. // FIXME: Remove all clients of this API, then remove this API. - MarkedArgumentBufferBase(size_t capacity) + MarkedVectorBase(size_t capacity) : m_size(0) , m_capacity(capacity) , m_buffer(inlineBuffer()) @@ -147,17 +75,16 @@ protected: EncodedJSValue* inlineBuffer() { - return bitwise_cast(bitwise_cast(this) + sizeof(MarkedArgumentBufferBase)); + return bitwise_cast(bitwise_cast(this) + sizeof(MarkedVectorBase)); } -private: - void expandCapacity(); - void expandCapacity(int newCapacity); - void slowEnsureCapacity(size_t requestedCapacity); + Status expandCapacity(); + Status expandCapacity(int newCapacity); + Status slowEnsureCapacity(size_t requestedCapacity); void addMarkSet(JSValue); - JS_EXPORT_PRIVATE void slowAppend(JSValue); + JS_EXPORT_PRIVATE Status slowAppend(JSValue); EncodedJSValue& slotFor(int item) const { @@ -172,11 +99,14 @@ private: } #if ASSERT_ENABLED - void setNeedsOverflowCheck() { m_needsOverflowCheck = true; } + void disableNeedsOverflowCheck() { m_overflowCheckEnabled = false; } + void setNeedsOverflowCheck() { m_needsOverflowCheck = m_overflowCheckEnabled; } void clearNeedsOverflowCheck() { m_needsOverflowCheck = false; } bool m_needsOverflowCheck { false }; + bool m_overflowCheckEnabled { true }; #else + void disableNeedsOverflowCheck() { } void setNeedsOverflowCheck() { } void clearNeedsOverflowCheck() { } #endif // ASSERT_ENABLED @@ -186,22 +116,114 @@ private: ListSet* m_markSet; }; -template -class MarkedArgumentBufferWithSize : public MarkedArgumentBufferBase { +template +class MarkedVector : public OverflowHandler, public MarkedVectorBase { public: static constexpr size_t inlineCapacity = passedInlineCapacity; - MarkedArgumentBufferWithSize() - : MarkedArgumentBufferBase(inlineCapacity) + MarkedVector() + : MarkedVectorBase(inlineCapacity) { ASSERT(inlineBuffer() == m_inlineBuffer); + if constexpr (std::is_same_v) { + // CrashOnOverflow handles overflows immediately. So, we do not + // need to check for it after. + disableNeedsOverflowCheck(); + } + } + + auto at(int i) const -> decltype(auto) + { + if constexpr (std::is_same_v) { + if (i >= m_size) + return jsUndefined(); + return JSValue::decode(slotFor(i)); + } else { + if (i >= m_size) + return static_cast(nullptr); + return jsCast(JSValue::decode(slotFor(i)).asCell()); + } + } + + void clear() + { + ASSERT(!m_needsOverflowCheck); + OverflowHandler::clearOverflow(); + m_size = 0; + } + + void append(T v) + { + ASSERT(m_size <= m_capacity); + if (m_size == m_capacity || mallocBase()) { + if (slowAppend(v) == Status::Overflowed) + this->overflowed(); + return; + } + + slotFor(m_size) = JSValue::encode(v); + ++m_size; + } + + void appendWithCrashOnOverflow(T v) + { + append(v); + if constexpr (!std::is_same::value) + RELEASE_ASSERT(!this->hasOverflowed()); + } + + auto last() const -> decltype(auto) + { + if constexpr (std::is_same_v) { + ASSERT(m_size); + return JSValue::decode(slotFor(m_size - 1)); + } else { + ASSERT(m_size); + return jsCast(JSValue::decode(slotFor(m_size - 1)).asCell()); + } + } + + JSValue takeLast() + { + JSValue result = last(); + removeLast(); + return result; + } + + void ensureCapacity(size_t requestedCapacity) + { + if (requestedCapacity > static_cast(m_capacity)) { + if (slowEnsureCapacity(requestedCapacity) == Status::Overflowed) + this->overflowed(); + } + } + + bool hasOverflowed() + { + clearNeedsOverflowCheck(); + return OverflowHandler::hasOverflowed(); + } + + template + void fill(size_t count, const Functor& func) + { + ASSERT(!m_size); + ensureCapacity(count); + if (OverflowHandler::hasOverflowed()) + return; + m_size = count; + func(reinterpret_cast(&slotFor(0))); } private: EncodedJSValue m_inlineBuffer[inlineCapacity] { }; }; -using MarkedArgumentBuffer = MarkedArgumentBufferWithSize<>; +template +class MarkedArgumentBufferWithSize : public MarkedVector { +}; + +using MarkedArgumentBuffer = MarkedVector; class ArgList { WTF_MAKE_FAST_ALLOCATED; diff --git a/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp b/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp index c8c486a6e9a6..4f0a26574132 100644 --- a/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp +++ b/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Apple Inc. All rights reserved. + * Copyright (C) 2020-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 @@ -218,7 +218,7 @@ AudioWorkletProcessor::AudioWorkletProcessor(AudioWorkletGlobalScope& globalScop ASSERT(!isMainThread()); } -void AudioWorkletProcessor::buildJSArguments(VM& vm, JSGlobalObject& globalObject, MarkedArgumentBufferBase& args, const Vector>& inputs, Vector>& outputs, const HashMap>& paramValuesMap) +void AudioWorkletProcessor::buildJSArguments(VM& vm, JSGlobalObject& globalObject, MarkedArgumentBuffer& args, const Vector>& inputs, Vector>& outputs, const HashMap>& paramValuesMap) { // For performance reasons, we cache the arrays passed to JS and reconstruct them only when the topology changes. if (!copyDataFromBusesToJSArray(vm, globalObject, inputs, toJSArray(m_jsInputs))) diff --git a/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h b/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h index 7d256ea557bb..9ad78225ee51 100644 --- a/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h +++ b/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 Apple Inc. All rights reserved. + * Copyright (C) 2020-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 @@ -40,7 +40,8 @@ namespace JSC { class JSArray; -class MarkedArgumentBufferBase; +template class MarkedVector; +using MarkedArgumentBuffer = MarkedVector; } namespace WebCore { @@ -69,7 +70,7 @@ public: private: explicit AudioWorkletProcessor(AudioWorkletGlobalScope&, const AudioWorkletProcessorConstructionData&); - void buildJSArguments(JSC::VM&, JSC::JSGlobalObject&, JSC::MarkedArgumentBufferBase&, const Vector>& inputs, Vector>& outputs, const HashMap>& paramValuesMap); + void buildJSArguments(JSC::VM&, JSC::JSGlobalObject&, JSC::MarkedArgumentBuffer&, const Vector>& inputs, Vector>& outputs, const HashMap>& paramValuesMap); AudioWorkletGlobalScope& m_globalScope; String m_name; diff --git a/Source/WebCore/bindings/js/SerializedScriptValue.cpp b/Source/WebCore/bindings/js/SerializedScriptValue.cpp index 2e6038948a8a..a9841fe057b8 100644 --- a/Source/WebCore/bindings/js/SerializedScriptValue.cpp +++ b/Source/WebCore/bindings/js/SerializedScriptValue.cpp @@ -539,6 +539,7 @@ static const unsigned StringDataIs8BitFlag = 0x80000000; using DeserializationResult = std::pair; class CloneBase { + WTF_FORBID_HEAP_ALLOCATION; protected: CloneBase(JSGlobalObject* lexicalGlobalObject) : m_lexicalGlobalObject(lexicalGlobalObject) @@ -616,6 +617,7 @@ template <> bool writeLittleEndian(Vector& buffer, const uint8 } class CloneSerializer : CloneBase { + WTF_FORBID_HEAP_ALLOCATION; public: static SerializationReturnCode serialize(JSGlobalObject* lexicalGlobalObject, JSValue value, Vector>& messagePorts, Vector>& arrayBuffers, const Vector>& imageBitmaps, #if ENABLE(OFFSCREEN_CANVAS_IN_WORKERS) @@ -2148,6 +2150,7 @@ SerializationReturnCode CloneSerializer::serialize(JSValue in) } class CloneDeserializer : CloneBase { + WTF_FORBID_HEAP_ALLOCATION; public: static String deserializeString(const Vector& buffer) { @@ -3920,10 +3923,10 @@ DeserializationResult CloneDeserializer::deserialize() Vector indexStack; Vector propertyNameStack; - Vector outputObjectStack; - Vector mapKeyStack; - Vector mapStack; - Vector setStack; + MarkedVector outputObjectStack; + MarkedVector mapKeyStack; + MarkedVector mapStack; + MarkedVector setStack; Vector stateStack; WalkerState lexicalGlobalObject = StateUnknown; JSValue outValue; -- 2.40.0