Title: [282707] trunk
Revision
282707
Author
[email protected]
Date
2021-09-17 16:17:23 -0700 (Fri, 17 Sep 2021)

Log Message

[JSC] Add fast property enumeration mode for JSON.stringify
https://bugs.webkit.org/show_bug.cgi?id=230393

Reviewed by Mark Lam.

JSTests:

* stress/json-stringify-object-modify.js: Added.
(shouldBe):
(throw.new.Error.let.object.hello.get inner):
(throw.new.Error):
(shouldBe.let.object.hello.get inner):

Source/_javascript_Core:

We collected profiles and found several subtests are using JSON.stringify enough. And generated strings are many serialized leaf objects.

This patch adds fast object property enumeration. When we know that source object meets some conditions, we can say that,
as long as structure is not changed, we can continue using property names and offset collected from the structure.
This way removes non observable [[Get]] operations to accelerate JSON.stringify performance for major object iteration cases.

We also extend MarkedArgumentBuffer: introducing MarkedArgumentBufferWithSize which can take default inline capacity as a template
parameter. This is used in JSON.stringify to increase the buffer because now we also need to record structures in MarkedArgumentBuffer.

This offers 0.4% improvement in Speedometer2 (EmberJS-TodoMVC, Vanilla-XXX, EmberJS-Debug-TodoMVC, they have enough amount of JSON.stringify
time).

----------------------------------------------------------------------------------------------------------------------------------
|               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
----------------------------------------------------------------------------------------------------------------------------------
| Elm-TodoMVC                          |117.710000   |117.751667   |1.000354  | 0.883246                                         |
| VueJS-TodoMVC                        |24.500000    |24.311667    |0.992313  | 0.365130                                         |
| EmberJS-TodoMVC                      |126.646667   |125.738333   |0.992828  | 0.002587 (significant)                           |
| BackboneJS-TodoMVC                   |47.873333    |47.911667    |1.000801  | 0.762509                                         |
| Preact-TodoMVC                       |17.020000    |17.070000    |1.002938  | 0.786799                                         |
| AngularJS-TodoMVC                    |129.856667   |129.353333   |0.996124  | 0.177632                                         |
| Vanilla-ES2015-TodoMVC               |61.698333    |61.120000    |0.990626  | 0.000003 (significant)                           |
| Inferno-TodoMVC                      |62.840000    |62.496667    |0.994536  | 0.312340                                         |
| Flight-TodoMVC                       |77.095000    |76.936667    |0.997946  | 0.702724                                         |
| Angular2-TypeScript-TodoMVC          |39.740000    |39.191667    |0.986202  | 0.053485                                         |
| VanillaJS-TodoMVC                    |49.008333    |48.346667    |0.986499  | 0.000638 (significant)                           |
| jQuery-TodoMVC                       |216.785000   |217.188333   |1.001861  | 0.270747                                         |
| EmberJS-Debug-TodoMVC                |344.230000   |342.993333   |0.996407  | 0.012262 (significant)                           |
| React-TodoMVC                        |85.461667    |85.411667    |0.999415  | 0.758049                                         |
| React-Redux-TodoMVC                  |140.681667   |140.640000   |0.999704  | 0.871277                                         |
| Vanilla-ES2015-Babel-Webpack-TodoMVC |59.928333    |59.351667    |0.990377  | 0.000000 (significant)                           |
----------------------------------------------------------------------------------------------------------------------------------
a mean = 264.40650
b mean = 265.51533
pValue = 0.0005567357
(Bigger means are better.)
1.004 times better
Results ARE significant

* heap/Heap.cpp:
(JSC::Heap::addCoreConstraints):
* heap/Heap.h:
* heap/HeapInlines.h:
* runtime/ArgList.cpp:
(JSC::MarkedArgumentBufferBase::addMarkSet):
(JSC::MarkedArgumentBufferBase::markLists):
(JSC::MarkedArgumentBufferBase::slowEnsureCapacity):
(JSC::MarkedArgumentBufferBase::expandCapacity):
(JSC::MarkedArgumentBufferBase::slowAppend):
(JSC::MarkedArgumentBuffer::addMarkSet): Deleted.
(JSC::MarkedArgumentBuffer::markLists): Deleted.
(JSC::MarkedArgumentBuffer::slowEnsureCapacity): Deleted.
(JSC::MarkedArgumentBuffer::expandCapacity): Deleted.
(JSC::MarkedArgumentBuffer::slowAppend): Deleted.
* runtime/ArgList.h:
(JSC::MarkedArgumentBufferWithSize::MarkedArgumentBufferWithSize):
(JSC::MarkedArgumentBuffer::MarkedArgumentBuffer): Deleted.
(JSC::MarkedArgumentBuffer::~MarkedArgumentBuffer): Deleted.
(JSC::MarkedArgumentBuffer::size const): Deleted.
(JSC::MarkedArgumentBuffer::isEmpty const): Deleted.
(JSC::MarkedArgumentBuffer::at const): Deleted.
(JSC::MarkedArgumentBuffer::clear): Deleted.
(JSC::MarkedArgumentBuffer::appendWithAction): Deleted.
(JSC::MarkedArgumentBuffer::append): Deleted.
(JSC::MarkedArgumentBuffer::appendWithCrashOnOverflow): Deleted.
(JSC::MarkedArgumentBuffer::removeLast): Deleted.
(JSC::MarkedArgumentBuffer::last): Deleted.
(JSC::MarkedArgumentBuffer::takeLast): Deleted.
(JSC::MarkedArgumentBuffer::ensureCapacity): Deleted.
(JSC::MarkedArgumentBuffer::hasOverflowed): Deleted.
(JSC::MarkedArgumentBuffer::overflowCheckNotNeeded): Deleted.
(JSC::MarkedArgumentBuffer::fill): Deleted.
(JSC::MarkedArgumentBuffer::slotFor const): Deleted.
(JSC::MarkedArgumentBuffer::mallocBase): Deleted.
(JSC::MarkedArgumentBuffer::setNeedsOverflowCheck): Deleted.
(JSC::MarkedArgumentBuffer::clearNeedsOverflowCheck): Deleted.
* runtime/JSONObject.cpp:
(JSC::Stringifier::Holder::hasFastObjectProperties const):
(JSC::Stringifier::appendStringifiedValue):
(JSC::Stringifier::Holder::Holder):
(JSC::Stringifier::Holder::appendNextProperty):
* runtime/ObjectConstructorInlines.h:
(JSC::canPerformFastPropertyEnumerationForJSONStringify):

Source/WebCore:

* Modules/webaudio/AudioWorkletProcessor.cpp:
(WebCore::AudioWorkletProcessor::buildJSArguments):
* Modules/webaudio/AudioWorkletProcessor.h:

Source/WebKitLegacy/mac:

* Plugins/Hosted/NetscapePluginInstanceProxy.h:
* Plugins/Hosted/NetscapePluginInstanceProxy.mm:
(WebKit::NetscapePluginInstanceProxy::demarshalValues):

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (282706 => 282707)


--- trunk/JSTests/ChangeLog	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/JSTests/ChangeLog	2021-09-17 23:17:23 UTC (rev 282707)
@@ -1,3 +1,16 @@
+2021-09-17  Yusuke Suzuki  <[email protected]>
+
+        [JSC] Add fast property enumeration mode for JSON.stringify
+        https://bugs.webkit.org/show_bug.cgi?id=230393
+
+        Reviewed by Mark Lam.
+
+        * stress/json-stringify-object-modify.js: Added.
+        (shouldBe):
+        (throw.new.Error.let.object.hello.get inner):
+        (throw.new.Error):
+        (shouldBe.let.object.hello.get inner):
+
 2021-09-15  Yusuke Suzuki  <[email protected]>
 
         [JSC] Optimize leaf object creation in JSON.parse

Added: trunk/JSTests/stress/json-stringify-object-modify.js (0 => 282707)


--- trunk/JSTests/stress/json-stringify-object-modify.js	                        (rev 0)
+++ trunk/JSTests/stress/json-stringify-object-modify.js	2021-09-17 23:17:23 UTC (rev 282707)
@@ -0,0 +1,35 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+{
+    let object = {
+        hello: {
+            get inner() {
+                $vm.toUncacheableDictionary(object);
+                object.world = 0;
+                return 33;
+            }
+        },
+        world: 42,
+        test: null,
+    };
+
+    shouldBe(JSON.stringify(object), `{"hello":{"inner":33},"world":0,"test":null}`);
+}
+{
+    let object = {
+        hello: {
+            get inner() {
+                delete object.world;
+                object.testing = 33;
+                return 33;
+            }
+        },
+        world: 42,
+        test: null,
+    };
+
+    shouldBe(JSON.stringify(object), `{"hello":{"inner":33},"test":null}`);
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (282706 => 282707)


--- trunk/Source/_javascript_Core/ChangeLog	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-09-17 23:17:23 UTC (rev 282707)
@@ -1,3 +1,94 @@
+2021-09-17  Yusuke Suzuki  <[email protected]>
+
+        [JSC] Add fast property enumeration mode for JSON.stringify
+        https://bugs.webkit.org/show_bug.cgi?id=230393
+
+        Reviewed by Mark Lam.
+
+        We collected profiles and found several subtests are using JSON.stringify enough. And generated strings are many serialized leaf objects.
+
+        This patch adds fast object property enumeration. When we know that source object meets some conditions, we can say that,
+        as long as structure is not changed, we can continue using property names and offset collected from the structure.
+        This way removes non observable [[Get]] operations to accelerate JSON.stringify performance for major object iteration cases.
+
+        We also extend MarkedArgumentBuffer: introducing MarkedArgumentBufferWithSize which can take default inline capacity as a template
+        parameter. This is used in JSON.stringify to increase the buffer because now we also need to record structures in MarkedArgumentBuffer.
+
+        This offers 0.4% improvement in Speedometer2 (EmberJS-TodoMVC, Vanilla-XXX, EmberJS-Debug-TodoMVC, they have enough amount of JSON.stringify
+        time).
+
+        ----------------------------------------------------------------------------------------------------------------------------------
+        |               subtest                |     ms      |     ms      |  b / a   | pValue (significance using False Discovery Rate) |
+        ----------------------------------------------------------------------------------------------------------------------------------
+        | Elm-TodoMVC                          |117.710000   |117.751667   |1.000354  | 0.883246                                         |
+        | VueJS-TodoMVC                        |24.500000    |24.311667    |0.992313  | 0.365130                                         |
+        | EmberJS-TodoMVC                      |126.646667   |125.738333   |0.992828  | 0.002587 (significant)                           |
+        | BackboneJS-TodoMVC                   |47.873333    |47.911667    |1.000801  | 0.762509                                         |
+        | Preact-TodoMVC                       |17.020000    |17.070000    |1.002938  | 0.786799                                         |
+        | AngularJS-TodoMVC                    |129.856667   |129.353333   |0.996124  | 0.177632                                         |
+        | Vanilla-ES2015-TodoMVC               |61.698333    |61.120000    |0.990626  | 0.000003 (significant)                           |
+        | Inferno-TodoMVC                      |62.840000    |62.496667    |0.994536  | 0.312340                                         |
+        | Flight-TodoMVC                       |77.095000    |76.936667    |0.997946  | 0.702724                                         |
+        | Angular2-TypeScript-TodoMVC          |39.740000    |39.191667    |0.986202  | 0.053485                                         |
+        | VanillaJS-TodoMVC                    |49.008333    |48.346667    |0.986499  | 0.000638 (significant)                           |
+        | jQuery-TodoMVC                       |216.785000   |217.188333   |1.001861  | 0.270747                                         |
+        | EmberJS-Debug-TodoMVC                |344.230000   |342.993333   |0.996407  | 0.012262 (significant)                           |
+        | React-TodoMVC                        |85.461667    |85.411667    |0.999415  | 0.758049                                         |
+        | React-Redux-TodoMVC                  |140.681667   |140.640000   |0.999704  | 0.871277                                         |
+        | Vanilla-ES2015-Babel-Webpack-TodoMVC |59.928333    |59.351667    |0.990377  | 0.000000 (significant)                           |
+        ----------------------------------------------------------------------------------------------------------------------------------
+        a mean = 264.40650
+        b mean = 265.51533
+        pValue = 0.0005567357
+        (Bigger means are better.)
+        1.004 times better
+        Results ARE significant
+
+        * heap/Heap.cpp:
+        (JSC::Heap::addCoreConstraints):
+        * heap/Heap.h:
+        * heap/HeapInlines.h:
+        * runtime/ArgList.cpp:
+        (JSC::MarkedArgumentBufferBase::addMarkSet):
+        (JSC::MarkedArgumentBufferBase::markLists):
+        (JSC::MarkedArgumentBufferBase::slowEnsureCapacity):
+        (JSC::MarkedArgumentBufferBase::expandCapacity):
+        (JSC::MarkedArgumentBufferBase::slowAppend):
+        (JSC::MarkedArgumentBuffer::addMarkSet): Deleted.
+        (JSC::MarkedArgumentBuffer::markLists): Deleted.
+        (JSC::MarkedArgumentBuffer::slowEnsureCapacity): Deleted.
+        (JSC::MarkedArgumentBuffer::expandCapacity): Deleted.
+        (JSC::MarkedArgumentBuffer::slowAppend): Deleted.
+        * runtime/ArgList.h:
+        (JSC::MarkedArgumentBufferWithSize::MarkedArgumentBufferWithSize):
+        (JSC::MarkedArgumentBuffer::MarkedArgumentBuffer): Deleted.
+        (JSC::MarkedArgumentBuffer::~MarkedArgumentBuffer): Deleted.
+        (JSC::MarkedArgumentBuffer::size const): Deleted.
+        (JSC::MarkedArgumentBuffer::isEmpty const): Deleted.
+        (JSC::MarkedArgumentBuffer::at const): Deleted.
+        (JSC::MarkedArgumentBuffer::clear): Deleted.
+        (JSC::MarkedArgumentBuffer::appendWithAction): Deleted.
+        (JSC::MarkedArgumentBuffer::append): Deleted.
+        (JSC::MarkedArgumentBuffer::appendWithCrashOnOverflow): Deleted.
+        (JSC::MarkedArgumentBuffer::removeLast): Deleted.
+        (JSC::MarkedArgumentBuffer::last): Deleted.
+        (JSC::MarkedArgumentBuffer::takeLast): Deleted.
+        (JSC::MarkedArgumentBuffer::ensureCapacity): Deleted.
+        (JSC::MarkedArgumentBuffer::hasOverflowed): Deleted.
+        (JSC::MarkedArgumentBuffer::overflowCheckNotNeeded): Deleted.
+        (JSC::MarkedArgumentBuffer::fill): Deleted.
+        (JSC::MarkedArgumentBuffer::slotFor const): Deleted.
+        (JSC::MarkedArgumentBuffer::mallocBase): Deleted.
+        (JSC::MarkedArgumentBuffer::setNeedsOverflowCheck): Deleted.
+        (JSC::MarkedArgumentBuffer::clearNeedsOverflowCheck): Deleted.
+        * runtime/JSONObject.cpp:
+        (JSC::Stringifier::Holder::hasFastObjectProperties const):
+        (JSC::Stringifier::appendStringifiedValue):
+        (JSC::Stringifier::Holder::Holder):
+        (JSC::Stringifier::Holder::appendNextProperty):
+        * runtime/ObjectConstructorInlines.h:
+        (JSC::canPerformFastPropertyEnumerationForJSONStringify):
+
 2021-09-17  Ross Kirsling  <[email protected]>
 
         Unreviewed fix for JSCOnly build with ENABLE_DFG_JIT off.

Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (282706 => 282707)


--- trunk/Source/_javascript_Core/heap/Heap.cpp	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp	2021-09-17 23:17:23 UTC (rev 282707)
@@ -2767,7 +2767,7 @@
             
             if (m_markListSet && m_markListSet->size()) {
                 SetRootMarkReasonScope rootScope(visitor, RootMarkReason::ConservativeScan);
-                MarkedArgumentBuffer::markLists(visitor, *m_markListSet);
+                MarkedArgumentBufferBase::markLists(visitor, *m_markListSet);
             }
 
             {

Modified: trunk/Source/_javascript_Core/heap/Heap.h (282706 => 282707)


--- trunk/Source/_javascript_Core/heap/Heap.h	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/_javascript_Core/heap/Heap.h	2021-09-17 23:17:23 UTC (rev 282707)
@@ -77,7 +77,7 @@
 class MarkStackMergingConstraint;
 class MarkedJSValueRefArray;
 class BlockDirectory;
-class MarkedArgumentBuffer;
+class MarkedArgumentBufferBase;
 class MarkingConstraint;
 class MarkingConstraintSet;
 class MutatorScheduler;
@@ -245,7 +245,7 @@
     JS_EXPORT_PRIVATE std::unique_ptr<TypeCountSet> protectedObjectTypeCounts();
     JS_EXPORT_PRIVATE std::unique_ptr<TypeCountSet> objectTypeCounts();
 
-    HashSet<MarkedArgumentBuffer*>& markListSet();
+    HashSet<MarkedArgumentBufferBase*>& markListSet();
     void addMarkedJSValueRefArray(MarkedJSValueRefArray*);
     
     template<typename Functor> void forEachProtectedCell(const Functor&);
@@ -624,7 +624,7 @@
     HashSet<const JSCell*> m_copyingRememberedSet;
 
     ProtectCountSet m_protectedValues;
-    std::unique_ptr<HashSet<MarkedArgumentBuffer*>> m_markListSet;
+    std::unique_ptr<HashSet<MarkedArgumentBufferBase*>> m_markListSet;
     SentinelLinkedList<MarkedJSValueRefArray, BasicRawSentinelNode<MarkedJSValueRefArray>> m_markedJSValueRefArrays;
 
     std::unique_ptr<MachineThreads> m_machineThreads;

Modified: trunk/Source/_javascript_Core/heap/HeapInlines.h (282706 => 282707)


--- trunk/Source/_javascript_Core/heap/HeapInlines.h	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/_javascript_Core/heap/HeapInlines.h	2021-09-17 23:17:23 UTC (rev 282707)
@@ -215,10 +215,10 @@
     }
 }
 
-inline HashSet<MarkedArgumentBuffer*>& Heap::markListSet()
+inline HashSet<MarkedArgumentBufferBase*>& Heap::markListSet()
 {
     if (!m_markListSet)
-        m_markListSet = makeUnique<HashSet<MarkedArgumentBuffer*>>();
+        m_markListSet = makeUnique<HashSet<MarkedArgumentBufferBase*>>();
     return *m_markListSet;
 }
 

Modified: trunk/Source/_javascript_Core/runtime/ArgList.cpp (282706 => 282707)


--- trunk/Source/_javascript_Core/runtime/ArgList.cpp	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/_javascript_Core/runtime/ArgList.cpp	2021-09-17 23:17:23 UTC (rev 282707)
@@ -27,7 +27,7 @@
 
 namespace JSC {
 
-void MarkedArgumentBuffer::addMarkSet(JSValue v)
+void MarkedArgumentBufferBase::addMarkSet(JSValue v)
 {
     if (m_markSet)
         return;
@@ -52,20 +52,20 @@
 }
 
 template<typename Visitor>
-void MarkedArgumentBuffer::markLists(Visitor& visitor, ListSet& markSet)
+void MarkedArgumentBufferBase::markLists(Visitor& visitor, ListSet& markSet)
 {
     ListSet::iterator end = markSet.end();
     for (ListSet::iterator it = markSet.begin(); it != end; ++it) {
-        MarkedArgumentBuffer* list = *it;
+        MarkedArgumentBufferBase* list = *it;
         for (int i = 0; i < list->m_size; ++i)
             visitor.appendUnbarriered(JSValue::decode(list->slotFor(i)));
     }
 }
 
-template void MarkedArgumentBuffer::markLists(AbstractSlotVisitor&, ListSet&);
-template void MarkedArgumentBuffer::markLists(SlotVisitor&, ListSet&);
+template void MarkedArgumentBufferBase::markLists(AbstractSlotVisitor&, ListSet&);
+template void MarkedArgumentBufferBase::markLists(SlotVisitor&, ListSet&);
 
-void MarkedArgumentBuffer::slowEnsureCapacity(size_t requestedCapacity)
+void MarkedArgumentBufferBase::slowEnsureCapacity(size_t requestedCapacity)
 {
     setNeedsOverflowCheck();
     auto checkedNewCapacity = CheckedInt32(requestedCapacity);
@@ -74,7 +74,7 @@
     expandCapacity(checkedNewCapacity);
 }
 
-void MarkedArgumentBuffer::expandCapacity()
+void MarkedArgumentBufferBase::expandCapacity()
 {
     setNeedsOverflowCheck();
     auto checkedNewCapacity = CheckedInt32(m_capacity) * 2;
@@ -83,7 +83,7 @@
     expandCapacity(checkedNewCapacity);
 }
 
-void MarkedArgumentBuffer::expandCapacity(int newCapacity)
+void MarkedArgumentBufferBase::expandCapacity(int newCapacity)
 {
     setNeedsOverflowCheck();
     ASSERT(m_capacity < newCapacity);
@@ -105,7 +105,7 @@
     m_capacity = newCapacity;
 }
 
-void MarkedArgumentBuffer::slowAppend(JSValue v)
+void MarkedArgumentBufferBase::slowAppend(JSValue v)
 {
     ASSERT(m_size <= m_capacity);
     if (m_size == m_capacity)

Modified: trunk/Source/_javascript_Core/runtime/ArgList.h (282706 => 282707)


--- trunk/Source/_javascript_Core/runtime/ArgList.h	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/_javascript_Core/runtime/ArgList.h	2021-09-17 23:17:23 UTC (rev 282707)
@@ -28,9 +28,9 @@
 
 namespace JSC {
 
-class MarkedArgumentBuffer : public RecordOverflow {
-    WTF_MAKE_NONCOPYABLE(MarkedArgumentBuffer);
-    WTF_MAKE_NONMOVABLE(MarkedArgumentBuffer);
+class alignas(alignof(EncodedJSValue)) MarkedArgumentBufferBase : public RecordOverflow {
+    WTF_MAKE_NONCOPYABLE(MarkedArgumentBufferBase);
+    WTF_MAKE_NONMOVABLE(MarkedArgumentBufferBase);
     WTF_FORBID_HEAP_ALLOCATION;
     friend class VM;
     friend class ArgList;
@@ -37,21 +37,10 @@
 
 public:
     using Base = RecordOverflow;
-    static constexpr size_t inlineCapacity = 8;
-    typedef HashSet<MarkedArgumentBuffer*> ListSet;
+    typedef HashSet<MarkedArgumentBufferBase*> ListSet;
 
-    // Constructor for a read-write list, to which you may append values.
-    // FIXME: Remove all clients of this API, then remove this API.
-    MarkedArgumentBuffer()
-        : m_size(0)
-        , m_capacity(inlineCapacity)
-        , m_buffer(m_inlineBuffer)
-        , m_markSet(nullptr)
+    ~MarkedArgumentBufferBase()
     {
-    }
-
-    ~MarkedArgumentBuffer()
-    {
         ASSERT(!m_needsOverflowCheck);
         if (m_markSet)
             m_markSet->remove(this);
@@ -145,6 +134,22 @@
         func(reinterpret_cast<JSValue*>(&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)
+        : m_size(0)
+        , m_capacity(capacity)
+        , m_buffer(inlineBuffer())
+        , m_markSet(nullptr)
+    {
+    }
+
+    EncodedJSValue* inlineBuffer()
+    {
+        return bitwise_cast<EncodedJSValue*>(bitwise_cast<uint8_t*>(this) + sizeof(MarkedArgumentBufferBase));
+    }
+
 private:
     void expandCapacity();
     void expandCapacity(int newCapacity);
@@ -161,7 +166,7 @@
         
     EncodedJSValue* mallocBase()
     {
-        if (m_buffer == m_inlineBuffer)
+        if (m_buffer == inlineBuffer())
             return nullptr;
         return &slotFor(0);
     }
@@ -177,11 +182,27 @@
 #endif // ASSERT_ENABLED
     int m_size;
     int m_capacity;
-    EncodedJSValue m_inlineBuffer[inlineCapacity];
     EncodedJSValue* m_buffer;
     ListSet* m_markSet;
 };
 
+template<size_t passedInlineCapacity = 8>
+class MarkedArgumentBufferWithSize : public MarkedArgumentBufferBase {
+public:
+    static constexpr size_t inlineCapacity = passedInlineCapacity;
+
+    MarkedArgumentBufferWithSize()
+        : MarkedArgumentBufferBase(inlineCapacity)
+    {
+        ASSERT(inlineBuffer() == m_inlineBuffer);
+    }
+
+private:
+    EncodedJSValue m_inlineBuffer[inlineCapacity] { };
+};
+
+using MarkedArgumentBuffer = MarkedArgumentBufferWithSize<>;
+
 class ArgList {
     WTF_MAKE_FAST_ALLOCATED;
     friend class Interpreter;

Modified: trunk/Source/_javascript_Core/runtime/JSONObject.cpp (282706 => 282707)


--- trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/_javascript_Core/runtime/JSONObject.cpp	2021-09-17 23:17:23 UTC (rev 282707)
@@ -33,7 +33,7 @@
 #include "JSArrayInlines.h"
 #include "JSCInlines.h"
 #include "LiteralParser.h"
-#include "ObjectConstructor.h"
+#include "ObjectConstructorInlines.h"
 #include "PropertyNameArray.h"
 #include "VMInlines.h"
 #include <wtf/text/StringBuilder.h>
@@ -88,21 +88,25 @@
     class Holder {
     public:
         enum RootHolderTag { RootHolder };
-        Holder(JSGlobalObject*, JSObject*);
+        Holder(JSGlobalObject*, JSObject*, Structure*);
         Holder(RootHolderTag, JSObject*);
 
         JSObject* object() const { return m_object; }
         bool isArray() const { return m_isArray; }
+        bool hasFastObjectProperties() const { return m_hasFastObjectProperties; }
 
         bool appendNextProperty(Stringifier&, StringBuilder&);
 
     private:
-        JSObject* m_object;
-        const bool m_isJSArray;
-        const bool m_isArray;
+        JSObject* m_object { nullptr };
+        Structure* m_structure { nullptr };
+        const bool m_isJSArray { false };
+        const bool m_isArray { false };
+        bool m_hasFastObjectProperties { false };
         unsigned m_index { 0 };
         unsigned m_size { 0 };
         RefPtr<PropertyNameArrayData> m_propertyNames;
+        Vector<std::tuple<Identifier, unsigned>, 8> m_propertiesAndOffsets;
     };
 
     friend class Holder;
@@ -125,7 +129,7 @@
     CallData m_replacerCallData;
     String m_gap;
 
-    MarkedArgumentBuffer m_objectStack;
+    MarkedArgumentBufferWithSize<16> m_objectStack;
     Vector<Holder, 16, UnsafeVectorOverflow> m_holderStack;
     String m_repeatedGap;
     String m_indent;
@@ -408,8 +412,10 @@
     }
 
     bool holderStackWasEmpty = m_holderStack.isEmpty();
-    m_holderStack.append(Holder(m_globalObject, object));
+    Structure* structure = object->structure(vm);
+    m_holderStack.append(Holder(m_globalObject, object, structure));
     m_objectStack.appendWithCrashOnOverflow(object);
+    m_objectStack.appendWithCrashOnOverflow(structure);
     RETURN_IF_EXCEPTION(scope, StringifyFailed);
     if (!holderStackWasEmpty)
         return StringifySucceeded;
@@ -422,6 +428,7 @@
             return StringifyFailed;
         m_holderStack.removeLast();
         m_objectStack.removeLast();
+        m_objectStack.removeLast();
     } while (!m_holderStack.isEmpty());
     return StringifySucceeded;
 }
@@ -455,8 +462,9 @@
     builder.append(m_indent);
 }
 
-inline Stringifier::Holder::Holder(JSGlobalObject* globalObject, JSObject* object)
+inline Stringifier::Holder::Holder(JSGlobalObject* globalObject, JSObject* object, Structure* structure)
     : m_object(object)
+    , m_structure(structure)
     , m_isJSArray(isJSArray(object))
     , m_isArray(JSC::isArray(globalObject, object))
 {
@@ -464,8 +472,6 @@
 
 inline Stringifier::Holder::Holder(RootHolderTag, JSObject* object)
     : m_object(object)
-    , m_isJSArray(false)
-    , m_isArray(false)
 {
 }
 
@@ -490,15 +496,29 @@
             RETURN_IF_EXCEPTION(scope, false);
             builder.append('[');
         } else {
-            if (stringifier.m_usingArrayReplacer)
+            if (stringifier.m_usingArrayReplacer) {
                 m_propertyNames = stringifier.m_arrayReplacerPropertyNames.data();
-            else {
+                m_size = m_propertyNames->propertyNameVector().size();
+            } else if (m_structure && m_object->structureID() == m_structure->id() && canPerformFastPropertyEnumerationForJSONStringify(m_structure)) {
+                m_structure->forEachProperty(vm, [&](const PropertyMapEntry& entry) -> bool {
+                    if (entry.attributes & PropertyAttribute::DontEnum)
+                        return true;
+
+                    PropertyName propertyName(entry.key);
+                    if (propertyName.isSymbol())
+                        return true;
+                    m_propertiesAndOffsets.constructAndAppend(Identifier::fromUid(vm, entry.key), entry.offset);
+                    return true;
+                });
+                m_hasFastObjectProperties = true;
+                m_size = m_propertiesAndOffsets.size();
+            } else {
                 PropertyNameArray objectPropertyNames(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
                 m_object->methodTable(vm)->getOwnPropertyNames(m_object, globalObject, objectPropertyNames, DontEnumPropertiesMode::Exclude);
                 RETURN_IF_EXCEPTION(scope, false);
                 m_propertyNames = objectPropertyNames.releaseData();
+                m_size = m_propertyNames->propertyNameVector().size();
             }
-            m_size = m_propertyNames->propertyNameVector().size();
             builder.append('{');
         }
         stringifier.indent();
@@ -538,10 +558,22 @@
         stringifyResult = stringifier.appendStringifiedValue(builder, value, *this, index);
         ASSERT(stringifyResult != StringifyFailedDueToUndefinedOrSymbolValue);
     } else {
-        // Get the value.
-        Identifier& propertyName = m_propertyNames->propertyNameVector()[index];
-        JSValue value = m_object->get(globalObject, propertyName);
-        RETURN_IF_EXCEPTION(scope, false);
+        Identifier propertyName;
+        JSValue value;
+        if (m_hasFastObjectProperties) {
+            propertyName = std::get<0>(m_propertiesAndOffsets[index]);
+            if (m_object->structureID() == m_structure->id()) {
+                unsigned offset = std::get<1>(m_propertiesAndOffsets[index]);
+                value = m_object->getDirect(offset);
+            } else {
+                value = m_object->get(globalObject, propertyName);
+                RETURN_IF_EXCEPTION(scope, false);
+            }
+        } else {
+            propertyName = m_propertyNames->propertyNameVector()[index];
+            value = m_object->get(globalObject, propertyName);
+            RETURN_IF_EXCEPTION(scope, false);
+        }
 
         rollBackPoint = builder.length();
 

Modified: trunk/Source/_javascript_Core/runtime/ObjectConstructorInlines.h (282706 => 282707)


--- trunk/Source/_javascript_Core/runtime/ObjectConstructorInlines.h	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/_javascript_Core/runtime/ObjectConstructorInlines.h	2021-09-17 23:17:23 UTC (rev 282707)
@@ -53,6 +53,11 @@
     return true;
 }
 
+ALWAYS_INLINE bool canPerformFastPropertyEnumerationForJSONStringify(Structure* structure)
+{
+    return canPerformFastPropertyEnumerationForObjectAssign(structure);
+}
+
 ALWAYS_INLINE void objectAssignFast(VM& vm, JSObject* target, JSObject* source, Vector<RefPtr<UniquedStringImpl>, 8>& properties, MarkedArgumentBuffer& values)
 {
     // |source| Structure does not have any getters. And target can perform fast put.

Modified: trunk/Source/WebCore/ChangeLog (282706 => 282707)


--- trunk/Source/WebCore/ChangeLog	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/WebCore/ChangeLog	2021-09-17 23:17:23 UTC (rev 282707)
@@ -1,3 +1,14 @@
+2021-09-17  Yusuke Suzuki  <[email protected]>
+
+        [JSC] Add fast property enumeration mode for JSON.stringify
+        https://bugs.webkit.org/show_bug.cgi?id=230393
+
+        Reviewed by Mark Lam.
+
+        * Modules/webaudio/AudioWorkletProcessor.cpp:
+        (WebCore::AudioWorkletProcessor::buildJSArguments):
+        * Modules/webaudio/AudioWorkletProcessor.h:
+
 2021-09-17  Johnson Zhou  <[email protected]>
 
         Add CustomToJSObject wrapper for CSSStyleValueCustom

Modified: trunk/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp (282706 => 282707)


--- trunk/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp	2021-09-17 23:17:23 UTC (rev 282707)
@@ -213,7 +213,7 @@
     ASSERT(!isMainThread());
 }
 
-void AudioWorkletProcessor::buildJSArguments(VM& vm, JSGlobalObject& globalObject, MarkedArgumentBuffer& args, const Vector<RefPtr<AudioBus>>& inputs, Vector<Ref<AudioBus>>& outputs, const HashMap<String, std::unique_ptr<AudioFloatArray>>& paramValuesMap)
+void AudioWorkletProcessor::buildJSArguments(VM& vm, JSGlobalObject& globalObject, MarkedArgumentBufferBase& args, const Vector<RefPtr<AudioBus>>& inputs, Vector<Ref<AudioBus>>& outputs, const HashMap<String, std::unique_ptr<AudioFloatArray>>& 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)))

Modified: trunk/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h (282706 => 282707)


--- trunk/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h	2021-09-17 23:17:23 UTC (rev 282707)
@@ -38,7 +38,7 @@
 
 namespace JSC {
 class JSArray;
-class MarkedArgumentBuffer;
+class MarkedArgumentBufferBase;
 }
 
 namespace WebCore {
@@ -67,7 +67,7 @@
 
 private:
     explicit AudioWorkletProcessor(const AudioWorkletProcessorConstructionData&);
-    void buildJSArguments(JSC::VM&, JSC::JSGlobalObject&, JSC::MarkedArgumentBuffer&, const Vector<RefPtr<AudioBus>>& inputs, Vector<Ref<AudioBus>>& outputs, const HashMap<String, std::unique_ptr<AudioFloatArray>>& paramValuesMap);
+    void buildJSArguments(JSC::VM&, JSC::JSGlobalObject&, JSC::MarkedArgumentBufferBase&, const Vector<RefPtr<AudioBus>>& inputs, Vector<Ref<AudioBus>>& outputs, const HashMap<String, std::unique_ptr<AudioFloatArray>>& paramValuesMap);
 
     String m_name;
     Ref<MessagePort> m_port;

Modified: trunk/Source/WebKitLegacy/mac/ChangeLog (282706 => 282707)


--- trunk/Source/WebKitLegacy/mac/ChangeLog	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/WebKitLegacy/mac/ChangeLog	2021-09-17 23:17:23 UTC (rev 282707)
@@ -1,3 +1,14 @@
+2021-09-17  Yusuke Suzuki  <[email protected]>
+
+        [JSC] Add fast property enumeration mode for JSON.stringify
+        https://bugs.webkit.org/show_bug.cgi?id=230393
+
+        Reviewed by Mark Lam.
+
+        * Plugins/Hosted/NetscapePluginInstanceProxy.h:
+        * Plugins/Hosted/NetscapePluginInstanceProxy.mm:
+        (WebKit::NetscapePluginInstanceProxy::demarshalValues):
+
 2021-09-14  Chris Dumez  <[email protected]>
 
         [Hardening] Validate IDBValue's blob paths in WebIDBServer::putOrAdd()

Modified: trunk/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.h (282706 => 282707)


--- trunk/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.h	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.h	2021-09-17 23:17:23 UTC (rev 282707)
@@ -310,7 +310,7 @@
     void addValueToArray(NSMutableArray *, JSC::JSGlobalObject* exec, JSC::JSValue value);
     
     bool demarshalValueFromArray(JSC::JSGlobalObject*, NSArray *array, NSUInteger& index, JSC::JSValue& result);
-    void demarshalValues(JSC::JSGlobalObject*, data_t valuesData, mach_msg_type_number_t valuesLength, JSC::MarkedArgumentBuffer& result);
+    void demarshalValues(JSC::JSGlobalObject*, data_t valuesData, mach_msg_type_number_t valuesLength, JSC::MarkedArgumentBufferBase& result);
 
     class LocalObjectMap {
         WTF_MAKE_NONCOPYABLE(LocalObjectMap);

Modified: trunk/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm (282706 => 282707)


--- trunk/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm	2021-09-17 23:15:45 UTC (rev 282706)
+++ trunk/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm	2021-09-17 23:17:23 UTC (rev 282707)
@@ -1433,7 +1433,7 @@
     return value;
 }
 
-void NetscapePluginInstanceProxy::demarshalValues(JSGlobalObject* lexicalGlobalObject, data_t valuesData, mach_msg_type_number_t valuesLength, MarkedArgumentBuffer& result)
+void NetscapePluginInstanceProxy::demarshalValues(JSGlobalObject* lexicalGlobalObject, data_t valuesData, mach_msg_type_number_t valuesLength, MarkedArgumentBufferBase& result)
 {
     RetainPtr<NSData> data = "" alloc] initWithBytesNoCopy:valuesData length:valuesLength freeWhenDone:NO]);
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to