Title: [239153] trunk
Revision
239153
Author
yusukesuz...@slowstart.org
Date
2018-12-12 23:13:38 -0800 (Wed, 12 Dec 2018)

Log Message

[JSC] Optimize Object.keys by caching own keys results in StructureRareData
https://bugs.webkit.org/show_bug.cgi?id=190047

Reviewed by Keith Miller.

JSTests:

* stress/object-keys-cached-zero.js: Added.
(shouldBe):
(test):
* stress/object-keys-changed-attribute.js: Added.
(shouldBe):
(test):
* stress/object-keys-changed-index.js: Added.
(shouldBe):
(test):
* stress/object-keys-changed.js: Added.
(shouldBe):
(test):
* stress/object-keys-indexed-non-cache.js: Added.
(shouldBe):
(test):
* stress/object-keys-overrides-get-property-names.js: Added.
(shouldBe):
(test):
(noInline):

Source/_javascript_Core:

Object.keys is one of the most frequently used function in web-tooling-benchmarks (WTB).
Object.keys is dominant in lebab of WTB, and frequently called in babel and others.
Since our Structure knows the shape of JSObject, we can cache the result of Object.keys
in Structure (StructureRareData) as we cache JSPropertyNameEnumerator in StructureRareData.

This patch caches the result of Object.keys in StructureRareData. The cached array is created
as JSImmutableButterfly. And Object.keys creates CoW from this data. Currently, the lifetime
strategy of this JSImmutableButterfly is the same to cached JSPropertyNameEnumerator. It is
referenced from Structure, and collected when Structure is collected.

This improves several benchmarks in SixSpeed.

                                baseline                  patched

    object-assign.es5      350.1710+-3.6303     ^    226.0368+-4.7558        ^ definitely 1.5492x faster
    for-of-object.es6      269.1941+-3.3430     ^    127.9317+-2.3875        ^ definitely 2.1042x faster

And it improves WTB lebab by 11.8%.

    Before: lebab:  6.10 runs/s
    After:  lebab:  6.82 runs/s

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.cpp:
(JSC::DFG::Node::convertToNewArrayBuffer):
* dfg/DFGNode.h:
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileObjectKeys):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLAbstractHeapRepository.h:
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileObjectKeys):
* runtime/Intrinsic.cpp:
(JSC::intrinsicName):
* runtime/Intrinsic.h:
* runtime/JSImmutableButterfly.h:
(JSC::JSImmutableButterfly::createSentinel):
* runtime/ObjectConstructor.cpp:
(JSC::ownPropertyKeys):
* runtime/Structure.cpp:
(JSC::Structure::canCachePropertyNameEnumerator const):
* runtime/Structure.h:
* runtime/StructureInlines.h:
(JSC::Structure::setCachedOwnKeys):
(JSC::Structure::cachedOwnKeys const):
(JSC::Structure::canCacheOwnKeys const):
* runtime/StructureRareData.cpp:
(JSC::StructureRareData::visitChildren):
(JSC::StructureRareData::cachedPropertyNameEnumerator const): Deleted.
(JSC::StructureRareData::setCachedPropertyNameEnumerator): Deleted.
* runtime/StructureRareData.h:
* runtime/StructureRareDataInlines.h:
(JSC::StructureRareData::cachedPropertyNameEnumerator const):
(JSC::StructureRareData::setCachedPropertyNameEnumerator):
(JSC::StructureRareData::cachedOwnKeys const):
(JSC::StructureRareData::cachedOwnKeysConcurrently const):
(JSC::StructureRareData::setCachedOwnKeys):
(JSC::StructureRareData::previousID const): Deleted.
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:

Modified Paths

Added Paths

Diff

Modified: trunk/JSTests/ChangeLog (239152 => 239153)


--- trunk/JSTests/ChangeLog	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/JSTests/ChangeLog	2018-12-13 07:13:38 UTC (rev 239153)
@@ -1,3 +1,30 @@
+2018-12-10  Yusuke Suzuki  <yusukesuz...@slowstart.org>
+
+        [JSC] Optimize Object.keys by caching own keys results in StructureRareData
+        https://bugs.webkit.org/show_bug.cgi?id=190047
+
+        Reviewed by Keith Miller.
+
+        * stress/object-keys-cached-zero.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-keys-changed-attribute.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-keys-changed-index.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-keys-changed.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-keys-indexed-non-cache.js: Added.
+        (shouldBe):
+        (test):
+        * stress/object-keys-overrides-get-property-names.js: Added.
+        (shouldBe):
+        (test):
+        (noInline):
+
 2018-12-12  Yusuke Suzuki  <yusukesuz...@slowstart.org>
 
         [DFG][FTL] Add NewSymbol

Added: trunk/JSTests/stress/object-keys-cached-zero.js (0 => 239153)


--- trunk/JSTests/stress/object-keys-cached-zero.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-cached-zero.js	2018-12-13 07:13:38 UTC (rev 239153)
@@ -0,0 +1,21 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+var object = {};
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 0);
+    shouldBe(result[0], undefined);
+    result[0] = i;
+    shouldBe(result.length, 1);
+    shouldBe(result[0], i);
+}

Added: trunk/JSTests/stress/object-keys-changed-attribute.js (0 => 239153)


--- trunk/JSTests/stress/object-keys-changed-attribute.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-changed-attribute.js	2018-12-13 07:13:38 UTC (rev 239153)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+var object = { Cocoa: 42 };
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 1);
+    shouldBe(result[0], 'Cocoa');
+}
+
+Reflect.defineProperty(object, 'Cocoa', {
+    enumerable: false
+});
+
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 0);
+    shouldBe(result[0], undefined);
+}

Added: trunk/JSTests/stress/object-keys-changed-index.js (0 => 239153)


--- trunk/JSTests/stress/object-keys-changed-index.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-changed-index.js	2018-12-13 07:13:38 UTC (rev 239153)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+var object = {};
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 0);
+    shouldBe(result[0], undefined);
+    result[0] = i;
+    shouldBe(result.length, 1);
+    shouldBe(result[0], i);
+}
+
+object[0] = 42;
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 1);
+    shouldBe(result[0], '0');
+}

Added: trunk/JSTests/stress/object-keys-changed.js (0 => 239153)


--- trunk/JSTests/stress/object-keys-changed.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-changed.js	2018-12-13 07:13:38 UTC (rev 239153)
@@ -0,0 +1,28 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+var object = {};
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 0);
+    shouldBe(result[0], undefined);
+    result[0] = i;
+    shouldBe(result.length, 1);
+    shouldBe(result[0], i);
+}
+
+object.Cocoa = 42;
+for (var i = 0; i < 1e6; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 1);
+    shouldBe(result[0], 'Cocoa');
+}

Added: trunk/JSTests/stress/object-keys-indexed-non-cache.js (0 => 239153)


--- trunk/JSTests/stress/object-keys-indexed-non-cache.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-indexed-non-cache.js	2018-12-13 07:13:38 UTC (rev 239153)
@@ -0,0 +1,25 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+var object = {0: 42};
+for (var i = 0; i < 1e3; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 1);
+    shouldBe(result[0], '0');
+}
+object[1] = 44;
+for (var i = 0; i < 1e3; ++i) {
+    var result = test(object);
+    shouldBe(result.length, 2);
+    shouldBe(result[0], '0');
+    shouldBe(result[1], '1');
+}

Added: trunk/JSTests/stress/object-keys-overrides-get-property-names.js (0 => 239153)


--- trunk/JSTests/stress/object-keys-overrides-get-property-names.js	                        (rev 0)
+++ trunk/JSTests/stress/object-keys-overrides-get-property-names.js	2018-12-13 07:13:38 UTC (rev 239153)
@@ -0,0 +1,57 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error('bad value: ' + actual);
+}
+
+function test(object)
+{
+    return Object.keys(object);
+}
+noInline(test);
+
+{
+    let object = new String("Cocoa");
+    for (let i = 0; i < 1e3; ++i) {
+        let result = test(object);
+        shouldBe(result.length, 5);
+        shouldBe(result[0], '0');
+        shouldBe(result[1], '1');
+        shouldBe(result[2], '2');
+        shouldBe(result[3], '3');
+        shouldBe(result[4], '4');
+    }
+
+    object.Cocoa = 42;
+    let result = test(object);
+    shouldBe(result.length, 6);
+    shouldBe(result[0], '0');
+    shouldBe(result[1], '1');
+    shouldBe(result[2], '2');
+    shouldBe(result[3], '3');
+    shouldBe(result[4], '4');
+    shouldBe(result[5], 'Cocoa');
+}
+
+{
+    let object = new String("Cocoa");
+    for (let i = 0; i < 1e3; ++i) {
+        let result = test(object);
+        shouldBe(result.length, 5);
+        shouldBe(result[0], '0');
+        shouldBe(result[1], '1');
+        shouldBe(result[2], '2');
+        shouldBe(result[3], '3');
+        shouldBe(result[4], '4');
+    }
+
+    object[8] = 42;
+    let result = test(object);
+    shouldBe(result.length, 6);
+    shouldBe(result[0], '0');
+    shouldBe(result[1], '1');
+    shouldBe(result[2], '2');
+    shouldBe(result[3], '3');
+    shouldBe(result[4], '4');
+    shouldBe(result[5], '8');
+}

Modified: trunk/Source/_javascript_Core/ChangeLog (239152 => 239153)


--- trunk/Source/_javascript_Core/ChangeLog	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/ChangeLog	2018-12-13 07:13:38 UTC (rev 239153)
@@ -1,3 +1,96 @@
+2018-12-10  Yusuke Suzuki  <yusukesuz...@slowstart.org>
+
+        [JSC] Optimize Object.keys by caching own keys results in StructureRareData
+        https://bugs.webkit.org/show_bug.cgi?id=190047
+
+        Reviewed by Keith Miller.
+
+        Object.keys is one of the most frequently used function in web-tooling-benchmarks (WTB).
+        Object.keys is dominant in lebab of WTB, and frequently called in babel and others.
+        Since our Structure knows the shape of JSObject, we can cache the result of Object.keys
+        in Structure (StructureRareData) as we cache JSPropertyNameEnumerator in StructureRareData.
+
+        This patch caches the result of Object.keys in StructureRareData. The cached array is created
+        as JSImmutableButterfly. And Object.keys creates CoW from this data. Currently, the lifetime
+        strategy of this JSImmutableButterfly is the same to cached JSPropertyNameEnumerator. It is
+        referenced from Structure, and collected when Structure is collected.
+
+        This improves several benchmarks in SixSpeed.
+
+                                        baseline                  patched
+
+            object-assign.es5      350.1710+-3.6303     ^    226.0368+-4.7558        ^ definitely 1.5492x faster
+            for-of-object.es6      269.1941+-3.3430     ^    127.9317+-2.3875        ^ definitely 2.1042x faster
+
+        And it improves WTB lebab by 11.8%.
+
+            Before: lebab:  6.10 runs/s
+            After:  lebab:  6.82 runs/s
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.cpp:
+        (JSC::DFG::Node::convertToNewArrayBuffer):
+        * dfg/DFGNode.h:
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileObjectKeys):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLAbstractHeapRepository.h:
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileObjectKeys):
+        * runtime/Intrinsic.cpp:
+        (JSC::intrinsicName):
+        * runtime/Intrinsic.h:
+        * runtime/JSImmutableButterfly.h:
+        (JSC::JSImmutableButterfly::createSentinel):
+        * runtime/ObjectConstructor.cpp:
+        (JSC::ownPropertyKeys):
+        * runtime/Structure.cpp:
+        (JSC::Structure::canCachePropertyNameEnumerator const):
+        * runtime/Structure.h:
+        * runtime/StructureInlines.h:
+        (JSC::Structure::setCachedOwnKeys):
+        (JSC::Structure::cachedOwnKeys const):
+        (JSC::Structure::canCacheOwnKeys const):
+        * runtime/StructureRareData.cpp:
+        (JSC::StructureRareData::visitChildren):
+        (JSC::StructureRareData::cachedPropertyNameEnumerator const): Deleted.
+        (JSC::StructureRareData::setCachedPropertyNameEnumerator): Deleted.
+        * runtime/StructureRareData.h:
+        * runtime/StructureRareDataInlines.h:
+        (JSC::StructureRareData::cachedPropertyNameEnumerator const):
+        (JSC::StructureRareData::setCachedPropertyNameEnumerator):
+        (JSC::StructureRareData::cachedOwnKeys const):
+        (JSC::StructureRareData::cachedOwnKeysConcurrently const):
+        (JSC::StructureRareData::setCachedOwnKeys):
+        (JSC::StructureRareData::previousID const): Deleted.
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+
 2018-12-12  Yusuke Suzuki  <yusukesuz...@slowstart.org>
 
         [DFG][FTL] Add NewSymbol

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -43,6 +43,7 @@
 #include "Operations.h"
 #include "PutByIdStatus.h"
 #include "StringObject.h"
+#include "StructureRareDataInlines.h"
 #include <wtf/BooleanLattice.h>
 #include <wtf/CheckedArithmetic.h>
 
@@ -2575,6 +2576,30 @@
         break;
     }
 
+    case ObjectKeys: {
+        if (node->child1().useKind() == ObjectUse) {
+            auto& structureSet = forNode(node->child1()).m_structure;
+            if (structureSet.isFinite() && structureSet.size() == 1) {
+                RegisteredStructure structure = structureSet.onlyStructure();
+                if (auto* rareData = structure->rareDataConcurrently()) {
+                    auto* immutableButterfly = rareData->cachedOwnKeysConcurrently();
+                    if (immutableButterfly && immutableButterfly != m_vm.sentinelImmutableButterfly.get()) {
+                        if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+                            m_state.setFoundConstants(true);
+                            didFoldClobberWorld();
+                            setTypeForNode(node, SpecArray);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        clobberWorld();
+        setTypeForNode(node, SpecArray);
+        break;
+    }
+
     case ToObject:
     case CallObjectConstructor: {
         AbstractValue& source = forNode(node->child1());

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -2688,6 +2688,15 @@
         return true;
     }
 
+    case ObjectKeysIntrinsic: {
+        if (argumentCountIncludingThis < 2)
+            return false;
+
+        insertChecks();
+        set(result, addToGraph(ObjectKeys, get(virtualRegisterForArgument(1, registerOffset))));
+        return true;
+    }
+
     case ReflectGetPrototypeOfIntrinsic: {
         if (argumentCountIncludingThis != 2)
             return false;

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -667,6 +667,7 @@
     case CreateThis:
     case InstanceOf:
     case StringValueOf:
+    case ObjectKeys:
         read(World);
         write(Heap);
         return;
@@ -1528,7 +1529,6 @@
         }
     }
 
-
     case NewObject:
     case NewRegexp:
     case NewSymbol:

Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -766,6 +766,26 @@
                 break;
             }
 
+            case ObjectKeys: {
+                if (node->child1().useKind() == ObjectUse) {
+                    auto& structureSet = m_state.forNode(node->child1()).m_structure;
+                    if (structureSet.isFinite() && structureSet.size() == 1) {
+                        RegisteredStructure structure = structureSet.onlyStructure();
+                        if (auto* rareData = structure->rareDataConcurrently()) {
+                            auto* immutableButterfly = rareData->cachedOwnKeysConcurrently();
+                            if (immutableButterfly && immutableButterfly != m_graph.m_vm.sentinelImmutableButterfly.get()) {
+                                if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+                                    node->convertToNewArrayBuffer(m_graph.freeze(immutableButterfly));
+                                    changed = true;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+
             case ToNumber: {
                 if (m_state.forNode(node->child1()).m_type & ~SpecBytecodeNumber)
                     break;

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -338,6 +338,7 @@
     case ToThis:
     case CreateThis:
     case ObjectCreate:
+    case ObjectKeys:
     case AllocatePropertyStorage:
     case ReallocatePropertyStorage:
     case Arrayify:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -1575,6 +1575,14 @@
             break;
         }
 
+        case ObjectKeys: {
+            if (node->child1()->shouldSpeculateObject()) {
+                watchHavingABadTime(node);
+                fixEdge<ObjectUse>(node->child1());
+            }
+            break;
+        }
+
         case CheckStringIdent: {
             fixEdge<StringIdentUse>(node->child1());
             break;

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGNode.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -31,6 +31,7 @@
 #include "DFGGraph.h"
 #include "DFGPromotedHeapLocation.h"
 #include "JSCInlines.h"
+#include "JSImmutableButterfly.h"
 
 namespace JSC { namespace DFG {
 
@@ -223,6 +224,17 @@
     children.reset();
 }
 
+void Node::convertToNewArrayBuffer(FrozenValue* immutableButterfly)
+{
+    setOpAndDefaultFlags(NewArrayBuffer);
+    NewArrayBufferData data { };
+    data.indexingMode = immutableButterfly->cast<JSImmutableButterfly*>()->indexingMode();
+    data.vectorLengthHint = immutableButterfly->cast<JSImmutableButterfly*>()->toButterfly()->vectorLength();
+    children.reset();
+    m_opInfo = immutableButterfly;
+    m_opInfo2 = data.asQuadWord;
+}
+
 void Node::convertToDirectCall(FrozenValue* executable)
 {
     NodeType newOp = LastNodeType;

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGNode.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -761,6 +761,8 @@
         m_opInfo = structure;
         m_opInfo2 = OpInfoWrapper();
     }
+
+    void convertToNewArrayBuffer(FrozenValue* immutableButterfly);
     
     void convertToDirectCall(FrozenValue*);
 

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -264,6 +264,7 @@
     macro(ParseInt, NodeMustGenerate | NodeResultJS) \
     macro(GetPrototypeOf, NodeMustGenerate | NodeResultJS) \
     macro(ObjectCreate, NodeMustGenerate | NodeResultJS) \
+    macro(ObjectKeys, NodeMustGenerate | NodeResultJS) \
     \
     /* Atomics object functions. */\
     macro(AtomicsAdd, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -248,6 +248,25 @@
     return JSValue::encode(JSValue::decode(encodedOp).toThis(exec, StrictMode));
 }
 
+JSArray* JIT_OPERATION operationObjectKeys(ExecState* exec, EncodedJSValue encodedObject)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    JSObject* object = JSValue::decode(encodedObject).toObject(exec);
+    RETURN_IF_EXCEPTION(scope, nullptr);
+    scope.release();
+    return ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude);
+}
+
+JSArray* JIT_OPERATION operationObjectKeysObject(ExecState* exec, JSObject* object)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    return ownPropertyKeys(exec, object, PropertyNameMode::Strings, DontEnumPropertiesMode::Exclude);
+}
+
 JSCell* JIT_OPERATION operationObjectCreate(ExecState* exec, EncodedJSValue encodedPrototype)
 {
     VM& vm = exec->vm();

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -43,6 +43,8 @@
 // These routines provide callbacks out to C++ implementations of operations too complex to JIT.
 JSCell* JIT_OPERATION operationCallObjectConstructor(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationToObject(ExecState*, JSGlobalObject*, EncodedJSValue encodedTarget, UniquedStringImpl*) WTF_INTERNAL;
+JSArray* JIT_OPERATION operationObjectKeys(ExecState*, EncodedJSValue) WTF_INTERNAL;
+JSArray* JIT_OPERATION operationObjectKeysObject(ExecState*, JSObject*) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationObjectCreate(ExecState*, EncodedJSValue) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationObjectCreateObject(ExecState*, JSObject*) WTF_INTERNAL;
 JSCell* JIT_OPERATION operationCreateThis(ExecState*, JSObject* constructor, uint32_t inlineCapacity) WTF_INTERNAL;

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -975,7 +975,8 @@
         case NewArray:
         case NewArrayWithSize:
         case CreateRest:
-        case NewArrayBuffer: {
+        case NewArrayBuffer:
+        case ObjectKeys: {
             setPrediction(SpecArray);
             break;
         }

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -178,6 +178,7 @@
     case ToThis:
     case CreateThis:
     case ObjectCreate:
+    case ObjectKeys:
     case GetCallee:
     case SetCallee:
     case GetArgumentCountIncludingThis:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -12345,6 +12345,94 @@
     jsValueResult(tempRegs, node);
 }
 
+void SpeculativeJIT::compileObjectKeys(Node* node)
+{
+    switch (node->child1().useKind()) {
+    case ObjectUse: {
+        if (m_graph.isWatchingHavingABadTimeWatchpoint(node)) {
+            SpeculateCellOperand object(this, node->child1());
+            GPRTemporary structure(this);
+            GPRTemporary scratch(this);
+            GPRTemporary scratch2(this);
+            GPRTemporary scratch3(this);
+            GPRTemporary result(this);
+
+            GPRReg objectGPR = object.gpr();
+            GPRReg structureGPR = structure.gpr();
+            GPRReg scratchGPR = scratch.gpr();
+            GPRReg scratch2GPR = scratch2.gpr();
+            GPRReg scratch3GPR = scratch3.gpr();
+            GPRReg resultGPR = result.gpr();
+
+            speculateObject(node->child1(), objectGPR);
+
+            CCallHelpers::JumpList slowCases;
+            m_jit.emitLoadStructure(*m_jit.vm(), objectGPR, structureGPR, scratchGPR);
+            m_jit.loadPtr(CCallHelpers::Address(structureGPR, Structure::previousOrRareDataOffset()), scratchGPR);
+
+            slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratchGPR));
+            slowCases.append(m_jit.branch32(CCallHelpers::Equal, CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()), TrustedImm32(bitwise_cast<int32_t>(m_jit.vm()->structureStructure->structureID()))));
+
+            m_jit.loadPtr(CCallHelpers::Address(scratchGPR, StructureRareData::offsetOfCachedOwnKeys()), scratchGPR);
+
+            slowCases.append(m_jit.branchTestPtr(CCallHelpers::Zero, scratchGPR));
+            slowCases.append(m_jit.branchPtr(CCallHelpers::Equal, scratchGPR, TrustedImmPtr::weakPointer(m_jit.graph(), m_jit.vm()->sentinelImmutableButterfly.get())));
+
+            MacroAssembler::JumpList slowButArrayBufferCases;
+
+            JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node->origin.semantic);
+            RegisteredStructure arrayStructure = m_jit.graph().registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(CopyOnWriteArrayWithContiguous));
+
+            m_jit.move(scratchGPR, scratch3GPR);
+            m_jit.addPtr(TrustedImmPtr(JSImmutableButterfly::offsetOfData()), scratchGPR);
+
+            emitAllocateJSObject<JSArray>(resultGPR, TrustedImmPtr(arrayStructure), scratchGPR, structureGPR, scratch2GPR, slowButArrayBufferCases);
+
+            addSlowPathGenerator(slowPathCall(slowButArrayBufferCases, this, operationNewArrayBuffer, resultGPR, arrayStructure, scratch3GPR));
+
+            addSlowPathGenerator(slowPathCall(slowCases, this, operationObjectKeysObject, resultGPR, objectGPR));
+
+            cellResult(resultGPR, node);
+            break;
+        }
+
+        SpeculateCellOperand object(this, node->child1());
+
+        GPRReg objectGPR = object.gpr();
+
+        speculateObject(node->child1(), objectGPR);
+
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+        callOperation(operationObjectKeysObject, resultGPR, objectGPR);
+        m_jit.exceptionCheck();
+
+        cellResult(resultGPR, node);
+        break;
+    }
+
+    case UntypedUse: {
+        JSValueOperand object(this, node->child1());
+
+        JSValueRegs objectRegs = object.jsValueRegs();
+
+        flushRegisters();
+        GPRFlushedCallResult result(this);
+        GPRReg resultGPR = result.gpr();
+        callOperation(operationObjectKeys, resultGPR, objectRegs);
+        m_jit.exceptionCheck();
+
+        cellResult(resultGPR, node);
+        break;
+    }
+
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+        break;
+    }
+}
+
 void SpeculativeJIT::compileObjectCreate(Node* node)
 {
     switch (node->child1().useKind()) {

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -1478,6 +1478,7 @@
     void compileNewArrayWithSize(Node*);
     void compileNewTypedArray(Node*);
     void compileToThis(Node*);
+    void compileObjectKeys(Node*);
     void compileObjectCreate(Node*);
     void compileCreateThis(Node*);
     void compileNewObject(Node*);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -3162,6 +3162,11 @@
         break;
     }
 
+    case ObjectKeys: {
+        compileObjectKeys(node);
+        break;
+    }
+
     case CreateThis: {
         compileCreateThis(node);
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -3404,6 +3404,11 @@
         break;
     }
 
+    case ObjectKeys: {
+        compileObjectKeys(node);
+        break;
+    }
+
     case CreateThis: {
         compileCreateThis(node);
         break;

Modified: trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h (239152 => 239153)


--- trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/ftl/FTLAbstractHeapRepository.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -116,8 +116,10 @@
     macro(Structure_globalObject, Structure::globalObjectOffset()) \
     macro(Structure_indexingModeIncludingHistory, Structure::indexingModeIncludingHistoryOffset()) \
     macro(Structure_inlineCapacity, Structure::inlineCapacityOffset()) \
+    macro(Structure_previousOrRareData, Structure::previousOrRareDataOffset()) \
     macro(Structure_prototype, Structure::prototypeOffset()) \
     macro(Structure_structureID, Structure::structureIDOffset()) \
+    macro(StructureRareData_cachedOwnKeys, StructureRareData::offsetOfCachedOwnKeys()) \
     macro(HashMapImpl_capacity, HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfCapacity()) \
     macro(HashMapImpl_buffer,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfBuffer()) \
     macro(HashMapImpl_head,  HashMapImpl<HashMapBucket<HashMapBucketDataKey>>::offsetOfHead()) \

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -200,6 +200,7 @@
     case CallObjectConstructor:
     case CallStringConstructor:
     case ObjectCreate:
+    case ObjectKeys:
     case MakeRope:
     case NewArrayWithSize:
     case TryGetById:

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -863,6 +863,9 @@
         case ObjectCreate:
             compileObjectCreate();
             break;
+        case ObjectKeys:
+            compileObjectKeys();
+            break;
         case NewObject:
             compileNewObject();
             break;
@@ -5485,6 +5488,75 @@
         setInt32(m_out.phi(Int32, zeroLengthResult, nonZeroLengthResult));
     }
 
+    void compileObjectKeys()
+    {
+        switch (m_node->child1().useKind()) {
+        case ObjectUse: {
+            if (m_graph.isWatchingHavingABadTimeWatchpoint(m_node)) {
+                LBasicBlock notNullCase = m_out.newBlock();
+                LBasicBlock rareDataCase = m_out.newBlock();
+                LBasicBlock notNullCacheCase = m_out.newBlock();
+                LBasicBlock useCacheCase = m_out.newBlock();
+                LBasicBlock slowButArrayBufferCase = m_out.newBlock();
+                LBasicBlock slowCase = m_out.newBlock();
+                LBasicBlock continuation = m_out.newBlock();
+
+                LValue object = lowObject(m_node->child1());
+                LValue structure = loadStructure(object);
+                LValue previousOrRareData = m_out.loadPtr(structure, m_heaps.Structure_previousOrRareData);
+                m_out.branch(m_out.notNull(previousOrRareData), unsure(notNullCase), unsure(slowCase));
+
+                LBasicBlock lastNext = m_out.appendTo(notNullCase, rareDataCase);
+                m_out.branch(
+                    m_out.notEqual(m_out.load32(previousOrRareData, m_heaps.JSCell_structureID), m_out.constInt32(m_graph.m_vm.structureStructure->structureID())),
+                    unsure(rareDataCase), unsure(slowCase));
+
+                m_out.appendTo(rareDataCase, notNullCacheCase);
+                LValue cachedOwnKeys = m_out.loadPtr(previousOrRareData, m_heaps.StructureRareData_cachedOwnKeys);
+                m_out.branch(m_out.notNull(cachedOwnKeys), unsure(notNullCacheCase), unsure(slowCase));
+
+                m_out.appendTo(notNullCacheCase, useCacheCase);
+                m_out.branch(m_out.notEqual(cachedOwnKeys, weakPointer(m_graph.m_vm.sentinelImmutableButterfly.get())), unsure(useCacheCase), unsure(slowCase));
+
+                m_out.appendTo(useCacheCase, slowButArrayBufferCase);
+                JSGlobalObject* globalObject = m_graph.globalObjectFor(m_node->origin.semantic);
+                RegisteredStructure arrayStructure = m_graph.registerStructure(globalObject->arrayStructureForIndexingTypeDuringAllocation(CopyOnWriteArrayWithContiguous));
+                LValue fastArray = allocateObject<JSArray>(arrayStructure, m_out.addPtr(cachedOwnKeys, JSImmutableButterfly::offsetOfData()), slowButArrayBufferCase);
+                ValueFromBlock fastResult = m_out.anchor(fastArray);
+                m_out.jump(continuation);
+
+                m_out.appendTo(slowButArrayBufferCase, slowCase);
+                LValue slowArray = vmCall(Int64, m_out.operation(operationNewArrayBuffer), m_callFrame, weakStructure(arrayStructure), cachedOwnKeys);
+                ValueFromBlock slowButArrayBufferResult = m_out.anchor(slowArray);
+                m_out.jump(continuation);
+
+                m_out.appendTo(slowCase, continuation);
+                VM& vm = this->vm();
+                LValue slowResultValue = lazySlowPath(
+                    [=, &vm] (const Vector<Location>& locations) -> RefPtr<LazySlowPath::Generator> {
+                        return createLazyCallGenerator(vm,
+                            operationObjectKeysObject, locations[0].directGPR(), locations[1].directGPR());
+                    },
+                    object);
+                ValueFromBlock slowResult = m_out.anchor(slowResultValue);
+                m_out.jump(continuation);
+
+                m_out.appendTo(continuation, lastNext);
+                setJSValue(m_out.phi(pointerType(), fastResult, slowButArrayBufferResult, slowResult));
+                break;
+            }
+            setJSValue(vmCall(Int64, m_out.operation(operationObjectKeysObject), m_callFrame, lowObject(m_node->child1())));
+            break;
+        }
+        case UntypedUse:
+            setJSValue(vmCall(Int64, m_out.operation(operationObjectKeys), m_callFrame, lowJSValue(m_node->child1())));
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+            break;
+        }
+    }
+
     void compileObjectCreate()
     {
         switch (m_node->child1().useKind()) {

Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/Intrinsic.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -119,6 +119,8 @@
         return "ObjectGetPrototypeOfIntrinsic";
     case ObjectIsIntrinsic:
         return "ObjectIsIntrinsic";
+    case ObjectKeysIntrinsic:
+        return "ObjectKeysIntrinsic";
     case ReflectGetPrototypeOfIntrinsic:
         return "ReflectGetPrototypeOfIntrinsic";
     case StringPrototypeValueOfIntrinsic:

Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.h (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/Intrinsic.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -72,6 +72,7 @@
     ObjectCreateIntrinsic,
     ObjectGetPrototypeOfIntrinsic,
     ObjectIsIntrinsic,
+    ObjectKeysIntrinsic,
     ReflectGetPrototypeOfIntrinsic,
     StringPrototypeValueOfIntrinsic,
     StringPrototypeReplaceIntrinsic,

Modified: trunk/Source/_javascript_Core/runtime/JSImmutableButterfly.h (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/JSImmutableButterfly.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/JSImmutableButterfly.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -67,6 +67,11 @@
         return array;
     }
 
+    static JSImmutableButterfly* createSentinel(VM& vm)
+    {
+        return create(vm, CopyOnWriteArrayWithContiguous, 0);
+    }
+
     unsigned publicLength() const { return m_header.publicLength(); }
     unsigned vectorLength() const { return m_header.vectorLength(); }
     unsigned length() const { return m_header.publicLength(); }

Modified: trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/ObjectConstructor.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -30,6 +30,7 @@
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
 #include "JSGlobalObjectFunctions.h"
+#include "JSImmutableButterfly.h"
 #include "Lookup.h"
 #include "ObjectPrototype.h"
 #include "PropertyDescriptor.h"
@@ -73,7 +74,7 @@
   getOwnPropertyDescriptors objectConstructorGetOwnPropertyDescriptors  DontEnum|Function 1
   getOwnPropertyNames       objectConstructorGetOwnPropertyNames        DontEnum|Function 1
   getOwnPropertySymbols     objectConstructorGetOwnPropertySymbols      DontEnum|Function 1
-  keys                      objectConstructorKeys                       DontEnum|Function 1
+  keys                      objectConstructorKeys                       DontEnum|Function 1 ObjectKeysIntrinsic
   defineProperty            objectConstructorDefineProperty             DontEnum|Function 3
   defineProperties          objectConstructorDefineProperties           DontEnum|Function 2
   create                    objectConstructorCreate                     DontEnum|Function 2 ObjectCreateIntrinsic
@@ -271,7 +272,6 @@
     RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(exec, object, PropertyNameMode::Symbols, DontEnumPropertiesMode::Include)));
 }
 
-// FIXME: Use the enumeration cache.
 EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec)
 {
     VM& vm = exec->vm();
@@ -892,11 +892,25 @@
     return JSValue::encode(jsBoolean(sameValue(exec, exec->argument(0), exec->argument(1))));
 }
 
-// FIXME: Use the enumeration cache.
 JSArray* ownPropertyKeys(ExecState* exec, JSObject* object, PropertyNameMode propertyNameMode, DontEnumPropertiesMode dontEnumPropertiesMode)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* globalObject = exec->lexicalGlobalObject();
+    bool isObjectKeys = propertyNameMode == PropertyNameMode::Strings && dontEnumPropertiesMode == DontEnumPropertiesMode::Exclude;
+    // We attempt to look up own property keys cache in Object.keys case.
+    if (isObjectKeys) {
+        if (LIKELY(!globalObject->isHavingABadTime())) {
+            if (auto* immutableButterfly = object->structure(vm)->cachedOwnKeys()) {
+                if (immutableButterfly != vm.sentinelImmutableButterfly.get()) {
+                    Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(immutableButterfly->indexingMode());
+                    return JSArray::createWithButterfly(vm, nullptr, arrayStructure, immutableButterfly->toButterfly());
+                }
+            }
+        }
+    }
+
     PropertyNameArray properties(&vm, propertyNameMode, PrivateSymbolMode::Exclude);
     object->methodTable(vm)->getOwnPropertyNames(object, exec, properties, EnumerationMode(dontEnumPropertiesMode));
     RETURN_IF_EXCEPTION(scope, nullptr);
@@ -918,8 +932,31 @@
     if (propertyNameMode != PropertyNameMode::StringsAndSymbols) {
         ASSERT(propertyNameMode == PropertyNameMode::Strings || propertyNameMode == PropertyNameMode::Symbols);
         if (!mustFilterProperty && properties.size() < MIN_SPARSE_ARRAY_INDEX) {
-            auto* globalObject = exec->lexicalGlobalObject();
             if (LIKELY(!globalObject->isHavingABadTime())) {
+                if (isObjectKeys) {
+                    Structure* structure = object->structure(vm);
+                    if (structure->canCacheOwnKeys()) {
+                        auto* cachedButterfly = structure->cachedOwnKeys();
+                        if (cachedButterfly == vm.sentinelImmutableButterfly.get()) {
+                            // Cache the immutable butterfly!
+                            size_t numProperties = properties.size();
+                            auto* newButterfly = JSImmutableButterfly::create(vm, CopyOnWriteArrayWithContiguous, numProperties);
+                            for (size_t i = 0; i < numProperties; i++) {
+                                const auto& identifier = properties[i];
+                                ASSERT(!identifier.isSymbol());
+                                newButterfly->setIndex(vm, i, jsOwnedString(&vm, identifier.string()));
+                            }
+
+                            structure->setCachedOwnKeys(vm, newButterfly);
+                            Structure* arrayStructure = globalObject->originalArrayStructureForIndexingType(newButterfly->indexingMode());
+                            return JSArray::createWithButterfly(vm, nullptr, arrayStructure, newButterfly->toButterfly());
+                        }
+
+                        if (cachedButterfly == nullptr)
+                            structure->setCachedOwnKeys(vm, jsCast<JSImmutableButterfly*>(vm.sentinelImmutableButterfly.get()));
+                    }
+                }
+
                 size_t numProperties = properties.size();
                 JSArray* keys = JSArray::create(vm, globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous), numProperties);
                 WriteBarrier<Unknown>* buffer = keys->butterfly()->contiguous().data();

Modified: trunk/Source/_javascript_Core/runtime/Structure.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/Structure.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/Structure.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -1252,17 +1252,7 @@
 
 bool Structure::canCachePropertyNameEnumerator() const
 {
-    auto canCache = [] (const Structure* structure) {
-        if (structure->isDictionary())
-            return false;
-        if (hasIndexedProperties(structure->indexingType()))
-            return false;
-        if (structure->typeInfo().overridesGetPropertyNames())
-            return false;
-        return true;
-    };
-
-    if (!canCache(this))
+    if (!this->canCacheOwnKeys())
         return false;
 
     StructureChain* structureChain = m_cachedPrototypeChain.get();
@@ -1271,7 +1261,7 @@
     while (true) {
         if (!structure->get())
             return true;
-        if (!canCache(structure->get()))
+        if (!structure->get()->canCacheOwnKeys())
             return false;
         structure++;
     }

Modified: trunk/Source/_javascript_Core/runtime/Structure.h (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/Structure.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/Structure.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -38,7 +38,6 @@
 #include "PutPropertySlot.h"
 #include "StructureIDBlob.h"
 #include "StructureRareData.h"
-#include "StructureRareDataInlines.h"
 #include "StructureTransitionTable.h"
 #include "JSTypeInfo.h"
 #include "Watchpoint.h"
@@ -326,6 +325,15 @@
         return static_cast<const StructureRareData*>(m_previousOrRareData.get());
     }
 
+    const StructureRareData* rareDataConcurrently() const
+    {
+        JSCell* cell = m_previousOrRareData.get();
+        WTF::loadLoadFence();
+        if (isRareData(cell))
+            return static_cast<StructureRareData*>(cell);
+        return nullptr;
+    }
+
     StructureRareData* ensureRareData(VM& vm)
     {
         if (!hasRareData())
@@ -472,6 +480,10 @@
     bool canCachePropertyNameEnumerator() const;
     bool canAccessPropertiesQuicklyForEnumeration() const;
 
+    void setCachedOwnKeys(VM&, JSImmutableButterfly*);
+    JSImmutableButterfly* cachedOwnKeys() const;
+    bool canCacheOwnKeys() const;
+
     void getPropertyNamesFromStructure(VM&, PropertyNameArray&, EnumerationMode);
 
     JSString* objectToStringValue()
@@ -520,6 +532,11 @@
         return OBJECT_OFFSETOF(Structure, m_inlineCapacity);
     }
 
+    static ptrdiff_t previousOrRareDataOffset()
+    {
+        return OBJECT_OFFSETOF(Structure, m_previousOrRareData);
+    }
+
     static Structure* createStructure(VM&);
         
     bool transitionWatchpointSetHasBeenInvalidated() const

Modified: trunk/Source/_javascript_Core/runtime/StructureInlines.h (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/StructureInlines.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/StructureInlines.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -219,6 +219,29 @@
     return false;
 }
 
+inline void Structure::setCachedOwnKeys(VM& vm, JSImmutableButterfly* ownKeys)
+{
+    ensureRareData(vm)->setCachedOwnKeys(vm, ownKeys);
+}
+
+inline JSImmutableButterfly* Structure::cachedOwnKeys() const
+{
+    if (!hasRareData())
+        return nullptr;
+    return rareData()->cachedOwnKeys();
+}
+
+inline bool Structure::canCacheOwnKeys() const
+{
+    if (isDictionary())
+        return false;
+    if (hasIndexedProperties(indexingType()))
+        return false;
+    if (typeInfo().overridesGetPropertyNames())
+        return false;
+    return true;
+}
+
 ALWAYS_INLINE JSValue prototypeForLookupPrimitiveImpl(JSGlobalObject* globalObject, const Structure* structure)
 {
     ASSERT(!structure->isObject());

Modified: trunk/Source/_javascript_Core/runtime/StructureRareData.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/StructureRareData.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/StructureRareData.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -27,6 +27,7 @@
 #include "StructureRareData.h"
 
 #include "AdaptiveInferredPropertyValueWatchpointBase.h"
+#include "JSImmutableButterfly.h"
 #include "JSPropertyNameEnumerator.h"
 #include "JSString.h"
 #include "JSCInlines.h"
@@ -70,18 +71,9 @@
     visitor.append(thisObject->m_previous);
     visitor.append(thisObject->m_objectToStringValue);
     visitor.append(thisObject->m_cachedPropertyNameEnumerator);
+    visitor.append(thisObject->m_cachedOwnKeys);
 }
 
-JSPropertyNameEnumerator* StructureRareData::cachedPropertyNameEnumerator() const
-{
-    return m_cachedPropertyNameEnumerator.get();
-}
-
-void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator)
-{
-    m_cachedPropertyNameEnumerator.set(vm, this, enumerator);
-}
-
 // ----------- Object.prototype.toString() helper watchpoint classes -----------
 
 class ObjectToStringAdaptiveInferredPropertyValueWatchpoint : public AdaptiveInferredPropertyValueWatchpointBase {

Modified: trunk/Source/_javascript_Core/runtime/StructureRareData.h (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/StructureRareData.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/StructureRareData.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -58,7 +58,10 @@
 
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue prototype);
 
-    Structure* previousID() const;
+    Structure* previousID() const
+    {
+        return m_previous.get();
+    }
     void setPreviousID(VM&, Structure*);
     void clearPreviousID();
 
@@ -68,11 +71,20 @@
     JSPropertyNameEnumerator* cachedPropertyNameEnumerator() const;
     void setCachedPropertyNameEnumerator(VM&, JSPropertyNameEnumerator*);
 
+    JSImmutableButterfly* cachedOwnKeys() const;
+    JSImmutableButterfly* cachedOwnKeysConcurrently() const;
+    void setCachedOwnKeys(VM&, JSImmutableButterfly*);
+
     Box<InlineWatchpointSet> copySharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; }
     const Box<InlineWatchpointSet>& sharedPolyProtoWatchpoint() const { return m_polyProtoWatchpoint; }
     void setSharedPolyProtoWatchpoint(Box<InlineWatchpointSet>&& sharedPolyProtoWatchpoint) { m_polyProtoWatchpoint = WTFMove(sharedPolyProtoWatchpoint); }
     bool hasSharedPolyProtoWatchpoint() const { return static_cast<bool>(m_polyProtoWatchpoint); }
 
+    static ptrdiff_t offsetOfCachedOwnKeys()
+    {
+        return OBJECT_OFFSETOF(StructureRareData, m_cachedOwnKeys);
+    }
+
     DECLARE_EXPORT_INFO;
 
 private:
@@ -86,7 +98,10 @@
 
     WriteBarrier<Structure> m_previous;
     WriteBarrier<JSString> m_objectToStringValue;
+    // FIXME: We should have some story for clearing these property names caches in GC.
+    // https://bugs.webkit.org/show_bug.cgi?id=192659
     WriteBarrier<JSPropertyNameEnumerator> m_cachedPropertyNameEnumerator;
+    WriteBarrier<JSImmutableButterfly> m_cachedOwnKeys;
 
     typedef HashMap<PropertyOffset, RefPtr<WatchpointSet>, WTF::IntHash<PropertyOffset>, WTF::UnsignedWithZeroKeyHashTraits<PropertyOffset>> PropertyWatchpointMap;
     std::unique_ptr<PropertyWatchpointMap> m_replacementWatchpointSets;

Modified: trunk/Source/_javascript_Core/runtime/StructureRareDataInlines.h (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/StructureRareDataInlines.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/StructureRareDataInlines.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -25,16 +25,13 @@
 
 #pragma once
 
+#include "JSImmutableButterfly.h"
+#include "JSPropertyNameEnumerator.h"
 #include "JSString.h"
 #include "StructureRareData.h"
 
 namespace JSC {
 
-inline Structure* StructureRareData::previousID() const
-{
-    return m_previous.get();
-}
-
 inline void StructureRareData::setPreviousID(VM& vm, Structure* structure)
 {
     m_previous.set(vm, this, structure);
@@ -50,4 +47,33 @@
     return m_objectToStringValue.get();
 }
 
+inline JSPropertyNameEnumerator* StructureRareData::cachedPropertyNameEnumerator() const
+{
+    return m_cachedPropertyNameEnumerator.get();
+}
+
+inline void StructureRareData::setCachedPropertyNameEnumerator(VM& vm, JSPropertyNameEnumerator* enumerator)
+{
+    m_cachedPropertyNameEnumerator.set(vm, this, enumerator);
+}
+
+inline JSImmutableButterfly* StructureRareData::cachedOwnKeys() const
+{
+    ASSERT(!compilationOrGCThread())
+    return m_cachedOwnKeys.get();
+}
+
+inline JSImmutableButterfly* StructureRareData::cachedOwnKeysConcurrently() const
+{
+    auto* result = m_cachedOwnKeys.get();
+    WTF::loadLoadFence();
+    return result;
+}
+
+inline void StructureRareData::setCachedOwnKeys(VM& vm, JSImmutableButterfly* butterfly)
+{
+    WTF::storeStoreFence();
+    m_cachedOwnKeys.set(vm, this, butterfly);
+}
+
 } // namespace JSC

Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/VM.cpp	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp	2018-12-13 07:13:38 UTC (rev 239153)
@@ -434,6 +434,7 @@
     exceptionStructure.set(*this, Exception::createStructure(*this, 0, jsNull()));
     promiseDeferredStructure.set(*this, JSPromiseDeferred::createStructure(*this, 0, jsNull()));
     internalPromiseDeferredStructure.set(*this, JSInternalPromiseDeferred::createStructure(*this, 0, jsNull()));
+    nativeStdFunctionCellStructure.set(*this, NativeStdFunctionCell::createStructure(*this, 0, jsNull()));
     programCodeBlockStructure.set(*this, ProgramCodeBlock::createStructure(*this, 0, jsNull()));
     moduleProgramCodeBlockStructure.set(*this, ModuleProgramCodeBlock::createStructure(*this, 0, jsNull()));
     evalCodeBlockStructure.set(*this, EvalCodeBlock::createStructure(*this, 0, jsNull()));
@@ -447,8 +448,8 @@
 
     sentinelSetBucket.set(*this, JSSet::BucketType::createSentinel(*this));
     sentinelMapBucket.set(*this, JSMap::BucketType::createSentinel(*this));
+    sentinelImmutableButterfly.set(*this, JSImmutableButterfly::createSentinel(*this));
 
-    nativeStdFunctionCellStructure.set(*this, NativeStdFunctionCell::createStructure(*this, 0, jsNull()));
     smallStrings.initializeCommonStrings(*this);
 
     Thread::current().setCurrentAtomicStringTable(existingEntryAtomicStringTable);

Modified: trunk/Source/_javascript_Core/runtime/VM.h (239152 => 239153)


--- trunk/Source/_javascript_Core/runtime/VM.h	2018-12-13 06:51:06 UTC (rev 239152)
+++ trunk/Source/_javascript_Core/runtime/VM.h	2018-12-13 07:13:38 UTC (rev 239153)
@@ -571,6 +571,7 @@
     Strong<JSCell> emptyPropertyNameEnumerator;
     Strong<JSCell> sentinelSetBucket;
     Strong<JSCell> sentinelMapBucket;
+    Strong<JSCell> sentinelImmutableButterfly;
 
     std::unique_ptr<PromiseDeferredTimer> promiseDeferredTimer;
     
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to