fourthTier: DFG shouldn't exit just because a String GetByVal went out-of-bounds
https://bugs.webkit.org/show_bug.cgi?id=117906

Source/JavaScriptCore:

Reviewed by Mark Hahnenberg.

This does the obvious thing, but also makes sure that out-of-bounds accesses
don't fall off into a C call, but try to do the fast thing if the prototype
chain is sane. We ought to probably do this for other array accesses in the
future, as well, since it's so darn easy.

* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::executeEffects):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileGetByValOnString):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::objectPrototypeIsSane):
(JSC):
(JSC::JSGlobalObject::arrayPrototypeChainIsSane):
(JSC::JSGlobalObject::stringPrototypeChainIsSane):
* runtime/JSGlobalObject.h:
(JSGlobalObject):

LayoutTests:

Reviewed by Mark Hahnenberg.

The out-of-bounds benchmark that isn't insane speeds up by 22x in this
patch.

* fast/js/regress/script-tests/string-get-by-val-out-of-bounds-insane.js: Added.
(foo):
* fast/js/regress/script-tests/string-get-by-val-out-of-bounds.js: Added.
(foo):
* fast/js/regress/string-get-by-val-out-of-bounds-expected.txt: Added.
* fast/js/regress/string-get-by-val-out-of-bounds-insane-expected.txt: Added.
* fast/js/regress/string-get-by-val-out-of-bounds-insane.html: Added.
* fast/js/regress/string-get-by-val-out-of-bounds.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@153244 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index e8b0b5b..a6dbf538 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -391,13 +391,25 @@
             blessArrayOperation(node->child1(), node->child2(), node->child3());
             
             ArrayMode arrayMode = node->arrayMode();
-            if (arrayMode.type() == Array::Double
-                && arrayMode.arrayClass() == Array::OriginalArray
-                && arrayMode.speculation() == Array::InBounds
-                && arrayMode.conversion() == Array::AsIs
-                && m_graph.globalObjectFor(node->codeOrigin)->arrayPrototypeChainIsSane()
-                && !(node->flags() & NodeUsedAsOther))
-                node->setArrayMode(arrayMode.withSpeculation(Array::SaneChain));
+            switch (arrayMode.type()) {
+            case Array::Double:
+                if (arrayMode.arrayClass() == Array::OriginalArray
+                    && arrayMode.speculation() == Array::InBounds
+                    && arrayMode.conversion() == Array::AsIs
+                    && m_graph.globalObjectFor(node->codeOrigin)->arrayPrototypeChainIsSane()
+                    && !(node->flags() & NodeUsedAsOther))
+                    node->setArrayMode(arrayMode.withSpeculation(Array::SaneChain));
+                break;
+                
+            case Array::String:
+                if ((node->prediction() & ~SpecString)
+                    || m_graph.hasExitSite(node->codeOrigin, OutOfBounds))
+                    node->setArrayMode(arrayMode.withSpeculation(Array::OutOfBounds));
+                break;
+                
+            default:
+                break;
+            }
             
             switch (node->arrayMode().type()) {
             case Array::SelectUsingPredictions: