Title: [282943] branches/safari-612-branch
Revision
282943
Author
repst...@apple.com
Date
2021-09-22 22:14:29 -0700 (Wed, 22 Sep 2021)

Log Message

Cherry-pick r282707. rdar://problem/83429953

    [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):

    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@282707 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Modified Paths

Added Paths

Diff

Modified: branches/safari-612-branch/JSTests/ChangeLog (282942 => 282943)


--- branches/safari-612-branch/JSTests/ChangeLog	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/JSTests/ChangeLog	2021-09-23 05:14:29 UTC (rev 282943)
@@ -1,3 +1,133 @@
+2021-09-22  Alan Coon  <alanc...@apple.com>
+
+        Cherry-pick r282707. rdar://problem/83429953
+
+    [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):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@282707 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-09-17  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [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-16  Russell Epstein  <repst...@apple.com>
 
         Cherry-pick r282239. rdar://problem/83183776

Added: branches/safari-612-branch/JSTests/stress/json-stringify-object-modify.js (0 => 282943)


--- branches/safari-612-branch/JSTests/stress/json-stringify-object-modify.js	                        (rev 0)
+++ branches/safari-612-branch/JSTests/stress/json-stringify-object-modify.js	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/_javascript_Core/ChangeLog (282942 => 282943)


--- branches/safari-612-branch/Source/_javascript_Core/ChangeLog	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/_javascript_Core/ChangeLog	2021-09-23 05:14:29 UTC (rev 282943)
@@ -1,5 +1,213 @@
 2021-09-22  Alan Coon  <alanc...@apple.com>
 
+        Cherry-pick r282707. rdar://problem/83429953
+
+    [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):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@282707 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-09-17  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [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-22  Alan Coon  <alanc...@apple.com>
+
         Cherry-pick r282663. rdar://problem/83429716
 
     PutByVal and PutPrivateName ICs should emit a write barrier if a butterfly might be allocated

Modified: branches/safari-612-branch/Source/_javascript_Core/heap/Heap.cpp (282942 => 282943)


--- branches/safari-612-branch/Source/_javascript_Core/heap/Heap.cpp	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/_javascript_Core/heap/Heap.cpp	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/_javascript_Core/heap/Heap.h (282942 => 282943)


--- branches/safari-612-branch/Source/_javascript_Core/heap/Heap.h	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/_javascript_Core/heap/Heap.h	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/_javascript_Core/heap/HeapInlines.h (282942 => 282943)


--- branches/safari-612-branch/Source/_javascript_Core/heap/HeapInlines.h	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/_javascript_Core/heap/HeapInlines.h	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/_javascript_Core/runtime/ArgList.cpp (282942 => 282943)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/ArgList.cpp	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/ArgList.cpp	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/_javascript_Core/runtime/ArgList.h (282942 => 282943)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/ArgList.h	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/ArgList.h	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/_javascript_Core/runtime/JSONObject.cpp (282942 => 282943)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/JSONObject.cpp	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/JSONObject.cpp	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/_javascript_Core/runtime/ObjectConstructorInlines.h (282942 => 282943)


--- branches/safari-612-branch/Source/_javascript_Core/runtime/ObjectConstructorInlines.h	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/_javascript_Core/runtime/ObjectConstructorInlines.h	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/WebCore/ChangeLog (282942 => 282943)


--- branches/safari-612-branch/Source/WebCore/ChangeLog	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/WebCore/ChangeLog	2021-09-23 05:14:29 UTC (rev 282943)
@@ -1,5 +1,133 @@
 2021-09-22  Alan Coon  <alanc...@apple.com>
 
+        Cherry-pick r282707. rdar://problem/83429953
+
+    [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):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@282707 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-09-17  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [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-22  Alan Coon  <alanc...@apple.com>
+
         Cherry-pick r282615. rdar://problem/83429940
 
     Throttle a couple of editing-related timers in cases where the user has not interacted with subframes

Modified: branches/safari-612-branch/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp (282942 => 282943)


--- branches/safari-612-branch/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.cpp	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h (282942 => 282943)


--- branches/safari-612-branch/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/WebCore/Modules/webaudio/AudioWorkletProcessor.h	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/WebKitLegacy/mac/ChangeLog (282942 => 282943)


--- branches/safari-612-branch/Source/WebKitLegacy/mac/ChangeLog	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/WebKitLegacy/mac/ChangeLog	2021-09-23 05:14:29 UTC (rev 282943)
@@ -1,5 +1,133 @@
 2021-09-22  Alan Coon  <alanc...@apple.com>
 
+        Cherry-pick r282707. rdar://problem/83429953
+
+    [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):
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@282707 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-09-17  Yusuke Suzuki  <ysuz...@apple.com>
+
+            [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-22  Alan Coon  <alanc...@apple.com>
+
         Cherry-pick r282393. rdar://problem/83429703
 
     [Hardening] Validate IDBValue's blob paths in WebIDBServer::putOrAdd()

Modified: branches/safari-612-branch/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.h (282942 => 282943)


--- branches/safari-612-branch/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.h	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.h	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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: branches/safari-612-branch/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm (282942 => 282943)


--- branches/safari-612-branch/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm	2021-09-23 05:14:23 UTC (rev 282942)
+++ branches/safari-612-branch/Source/WebKitLegacy/mac/Plugins/Hosted/NetscapePluginInstanceProxy.mm	2021-09-23 05:14:29 UTC (rev 282943)
@@ -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
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to