Title: [200117] trunk
Revision
200117
Author
[email protected]
Date
2016-04-26 18:28:03 -0700 (Tue, 26 Apr 2016)

Log Message

[ES] Implement RegExp.prototype.@@replace and use it for String.prototype.replace
https://bugs.webkit.org/show_bug.cgi?id=156562

Reviewed by Filip Pizlo.

Source/_javascript_Core:

Added builtins for String.prototype.replace as well as RegExp.prototype[Symbol.replace].

The String.prototype.replace also has an intrinsic, StringPrototypeReplaceIntrinsic.
This original intrinsic was copied to make StringPrototypeReplaceRegExpIntrinsic.
The difference between the two intrinsics is that StringPrototypeReplaceIntrinsic has
the same checks found in the new builtin hasObservableSideEffectsForStringReplace.
We implement these primordial checks for StringPrototypeReplaceIntrinsic in two places.
First, we do a trial check during ByteCode parsing time to see if the current
RegExp.prototype properties have changed from the original.  If they have, we don't
inline the intrinsic.  Later, in the fixup phase, we add nodes to the IR to emit the
checks at runtime.

The new intrinsic StringPrototypeReplaceRegExpIntrinsic is only available via the
private @replaceUsingRegExp, which is called in the String.prototype.replace builtin.
It is only called after hasObservableSideEffectsForStringReplace has been called

Both of these intrinsics are needed, because the JS code containing String.replace() calls
runs initially in the LLint and then the baseline JIT.  Even after the function tiers up
to the DFG JIT, the inlining budget may not allow StringPrototypeReplaceIntrinsic to be inlined.
Having StringPrototypeReplaceRegExpIntrinsic allows for the String.prototype.replace builtin to
get reasonable performance before the other intrinsic is inlined or when it can't.

* builtins/RegExpPrototype.js:
(match):
(getSubstitution):
(replace):
(search):
(split):
* builtins/StringPrototype.js:
(repeat):
(hasObservableSideEffectsForStringReplace):
(intrinsic.StringPrototypeReplaceIntrinsic.replace):
(localeCompare):
New builtins for String.prototype.replace and RegExp.prototype[Symbol.replace].

* bytecode/BytecodeIntrinsicRegistry.cpp:
* bytecode/BytecodeIntrinsicRegistry.h:
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsicCall):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::fixupGetAndSetLocalsInBlock):
(JSC::DFG::FixupPhase::tryAddStringReplacePrimordialChecks):
(JSC::DFG::FixupPhase::checkArray):
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::getRegExpPrototypeProperty):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::getRegExpPrototypeProperty):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleNode):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
* runtime/CommonIdentifiers.h:
* runtime/Intrinsic.h:
* runtime/RegExpPrototype.cpp:
(JSC::RegExpPrototype::finishCreation):
* runtime/StringPrototype.cpp:
(JSC::StringPrototype::finishCreation):
(JSC::replace):
(JSC::stringProtoFuncReplaceUsingRegExp):
(JSC::stringProtoFuncReplaceUsingStringSearch):
(JSC::operationStringProtoFuncReplaceGeneric):
(JSC::stringProtoFuncReplace): Deleted.
Added StringReplaceRegExp intrinsic.  Added checks for RegExp profiled arguments to StringReplace
that mirror what is in hasObservableSideEffectsForStringReplace().  If we aren't able to add the
checks, we OSR exit.  Add Graph::getPrimordialRegExpPrototypeProperty() as a helper to get the
primordial values from RegExp.prototype.

* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init): Added @regExpPrototypeSymbolReplace and
@hasObservableSideEffectsForStringReplace here instead og String.prototype so that we reduce the
number of objects we have to traverse.

* tests/es6.yaml: Changed expectations for the various replace related tests to passing.

* tests/stress/regexp-replace-proxy.js:
(assert):
(let.getProxyNullExec.new.Proxy):
(let.getSetProxyNullExec.new.Proxy):
(get resetTracking):
(let.getSetProxyMatches_comma.new.Proxy):
(set get getSetProxyNullExec):
(let.getSetProxyReplace_phoneNumber.new.Proxy):
(set get getSetProxyMatches_comma):
(let.getSetProxyReplaceUnicode_digit_nonGreedy.new.Proxy):
(set get resetTracking):
* tests/stress/string-replace-proxy.js:
(assert):
(let.getSetProxyReplace.new.Proxy.replace):
New tests.

LayoutTests:

Updated tests.  Needed to update js/regress-141098.js test, because builtins are
only compilied when called.  This test checks behavior at or near running out of
stack space.  It turns out that String.replace is used by the -pre.js test harness
and I was running out of stack space when compiling the String.prototype.replace
builting.  Therefore, I added a call to testPassed() to precompile String.replace.

* js/Object-getOwnPropertyNames-expected.txt:
* js/regress-141098-expected.txt:
* js/script-tests/Object-getOwnPropertyNames.js:
* js/script-tests/regress-141098.js:
(probeAndRecurse):
* fast/profiler/nested-start-and-stop-profiler-expected.txt:
* js/Object-getOwnPropertyNames-expected.txt:
* js/dom/string-prototype-properties-expected.txt:
* js/regress-141098-expected.txt:
* js/script-tests/Object-getOwnPropertyNames.js:
* js/script-tests/regress-141098.js:
(probeAndRecurse):
* sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.11_String.prototype.replace/S15.5.4.11_A1_T3-expected.txt:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (200116 => 200117)


--- trunk/LayoutTests/ChangeLog	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/LayoutTests/ChangeLog	2016-04-27 01:28:03 UTC (rev 200117)
@@ -1,3 +1,30 @@
+2016-04-26  Michael Saboff  <[email protected]>
+
+        [ES] Implement RegExp.prototype.@@replace and use it for String.prototype.replace
+        https://bugs.webkit.org/show_bug.cgi?id=156562
+
+        Reviewed by Filip Pizlo.
+
+        Updated tests.  Needed to update js/regress-141098.js test, because builtins are
+        only compilied when called.  This test checks behavior at or near running out of
+        stack space.  It turns out that String.replace is used by the -pre.js test harness
+        and I was running out of stack space when compiling the String.prototype.replace
+        builting.  Therefore, I added a call to testPassed() to precompile String.replace.
+
+        * js/Object-getOwnPropertyNames-expected.txt:
+        * js/regress-141098-expected.txt:
+        * js/script-tests/Object-getOwnPropertyNames.js:
+        * js/script-tests/regress-141098.js:
+        (probeAndRecurse):
+        * fast/profiler/nested-start-and-stop-profiler-expected.txt:
+        * js/Object-getOwnPropertyNames-expected.txt:
+        * js/dom/string-prototype-properties-expected.txt:
+        * js/regress-141098-expected.txt:
+        * js/script-tests/Object-getOwnPropertyNames.js:
+        * js/script-tests/regress-141098.js:
+        (probeAndRecurse):
+        * sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.11_String.prototype.replace/S15.5.4.11_A1_T3-expected.txt:
+
 2016-04-26  Myles C. Maxfield  <[email protected]>
 
         [WK2] [OS X] Create API for switching RTL scrollbar policy

Modified: trunk/LayoutTests/fast/profiler/nested-start-and-stop-profiler-expected.txt (200116 => 200117)


--- trunk/LayoutTests/fast/profiler/nested-start-and-stop-profiler-expected.txt	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/LayoutTests/fast/profiler/nested-start-and-stop-profiler-expected.txt	2016-04-27 01:28:03 UTC (rev 200117)
@@ -24,16 +24,25 @@
             appendChild (no file) (line 0:0)
             printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                replace (no file) (line 0:0)
+                  hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                     (anonymous function) (no file) (line 0:0)
+                  anonymous (no file) (line 0:0)
                createTextNode (no file) (line 0:0)
                appendChild (no file) (line 0:0)
                children (no file) (line 0:0)
                printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                   replace (no file) (line 0:0)
+                     hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                        (anonymous function) (no file) (line 0:0)
+                     anonymous (no file) (line 0:0)
                   createTextNode (no file) (line 0:0)
                   appendChild (no file) (line 0:0)
                   children (no file) (line 0:0)
                   printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                      replace (no file) (line 0:0)
+                        hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                           (anonymous function) (no file) (line 0:0)
+                        anonymous (no file) (line 0:0)
                      createTextNode (no file) (line 0:0)
                      appendChild (no file) (line 0:0)
                      children (no file) (line 0:0)
@@ -57,16 +66,25 @@
             appendChild (no file) (line 0:0)
             printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                replace (no file) (line 0:0)
+                  hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                     (anonymous function) (no file) (line 0:0)
+                  anonymous (no file) (line 0:0)
                createTextNode (no file) (line 0:0)
                appendChild (no file) (line 0:0)
                children (no file) (line 0:0)
                printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                   replace (no file) (line 0:0)
+                     hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                        (anonymous function) (no file) (line 0:0)
+                     anonymous (no file) (line 0:0)
                   createTextNode (no file) (line 0:0)
                   appendChild (no file) (line 0:0)
                   children (no file) (line 0:0)
                   printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                      replace (no file) (line 0:0)
+                        hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                           (anonymous function) (no file) (line 0:0)
+                        anonymous (no file) (line 0:0)
                      createTextNode (no file) (line 0:0)
                      appendChild (no file) (line 0:0)
                      children (no file) (line 0:0)
@@ -84,44 +102,84 @@
             appendChild (no file) (line 0:0)
             printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                replace (no file) (line 0:0)
+                  hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                     (anonymous function) (no file) (line 0:0)
+                  anonymous (no file) (line 0:0)
                createTextNode (no file) (line 0:0)
                appendChild (no file) (line 0:0)
                children (no file) (line 0:0)
                printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                   replace (no file) (line 0:0)
+                     hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                        (anonymous function) (no file) (line 0:0)
+                     anonymous (no file) (line 0:0)
                   createTextNode (no file) (line 0:0)
                   appendChild (no file) (line 0:0)
                   children (no file) (line 0:0)
                   printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                      replace (no file) (line 0:0)
+                        hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                           (anonymous function) (no file) (line 0:0)
+                        anonymous (no file) (line 0:0)
                      createTextNode (no file) (line 0:0)
                      appendChild (no file) (line 0:0)
                      children (no file) (line 0:0)
                      printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                         replace (no file) (line 0:0)
+                           hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                              (anonymous function) (no file) (line 0:0)
+                           anonymous (no file) (line 0:0)
                         createTextNode (no file) (line 0:0)
                         appendChild (no file) (line 0:0)
                         children (no file) (line 0:0)
                         printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                            replace (no file) (line 0:0)
+                              hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                                 (anonymous function) (no file) (line 0:0)
+                              anonymous (no file) (line 0:0)
                            createTextNode (no file) (line 0:0)
                            appendChild (no file) (line 0:0)
                            children (no file) (line 0:0)
                            printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                               replace (no file) (line 0:0)
+                                 hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                                    (anonymous function) (no file) (line 0:0)
+                                 anonymous (no file) (line 0:0)
                               createTextNode (no file) (line 0:0)
                               appendChild (no file) (line 0:0)
                               children (no file) (line 0:0)
                               printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                                  replace (no file) (line 0:0)
+                                    hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                                       (anonymous function) (no file) (line 0:0)
+                                    anonymous (no file) (line 0:0)
                                  createTextNode (no file) (line 0:0)
                                  appendChild (no file) (line 0:0)
                                  children (no file) (line 0:0)
                                  printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
                                     replace (no file) (line 0:0)
+                                       hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                                          (anonymous function) (no file) (line 0:0)
+                                       anonymous (no file) (line 0:0)
                                     createTextNode (no file) (line 0:0)
                                     appendChild (no file) (line 0:0)
                                     children (no file) (line 0:0)
+                                    printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
+                                       replace (no file) (line 0:0)
+                                          hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                                             (anonymous function) (no file) (line 0:0)
+                                          anonymous (no file) (line 0:0)
+                                       createTextNode (no file) (line 0:0)
+                                       appendChild (no file) (line 0:0)
+                                       children (no file) (line 0:0)
+                                       printProfileNodeWithoutTime profiler-test-JS-resources.js (line 77:37)
+                                          replace (no file) (line 0:0)
+                                             hasObservableSideEffectsForStringReplace (no file) (line 0:0)
+                                                (anonymous function) (no file) (line 0:0)
+                                             anonymous (no file) (line 0:0)
+                                          createTextNode (no file) (line 0:0)
+                                          appendChild (no file) (line 0:0)
+                                          children (no file) (line 0:0)
             getElementById (no file) (line 0:0)
          notifyDone (no file) (line 0:0)
 

Modified: trunk/LayoutTests/js/Object-getOwnPropertyNames-expected.txt (200116 => 200117)


--- trunk/LayoutTests/js/Object-getOwnPropertyNames-expected.txt	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/LayoutTests/js/Object-getOwnPropertyNames-expected.txt	2016-04-27 01:28:03 UTC (rev 200117)
@@ -61,7 +61,7 @@
 PASS getSortedOwnPropertyNames(Error.prototype) is ['constructor', 'message', 'name', 'toString']
 PASS getSortedOwnPropertyNames(Math) is ['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']
 PASS getSortedOwnPropertyNames(JSON) is ['parse', 'stringify']
-PASS getSortedOwnPropertyNames(Symbol) is ['for', 'hasInstance', 'isConcatSpreadable', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'search', 'species', 'split', 'toPrimitive', 'toStringTag', 'unscopables']
+PASS getSortedOwnPropertyNames(Symbol) is ['for', 'hasInstance', 'isConcatSpreadable', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'replace', 'search', 'species', 'split', 'toPrimitive', 'toStringTag', 'unscopables']
 PASS getSortedOwnPropertyNames(Symbol.prototype) is ['constructor', 'toString', 'valueOf']
 PASS getSortedOwnPropertyNames(Map) is ['length', 'name', 'prototype']
 PASS getSortedOwnPropertyNames(Map.prototype) is ['clear', 'constructor', 'delete', 'entries', 'forEach', 'get', 'has', 'keys', 'set', 'size', 'values']

Modified: trunk/LayoutTests/js/dom/string-prototype-properties-expected.txt (200116 => 200117)


--- trunk/LayoutTests/js/dom/string-prototype-properties-expected.txt	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/LayoutTests/js/dom/string-prototype-properties-expected.txt	2016-04-27 01:28:03 UTC (rev 200117)
@@ -11,7 +11,7 @@
 PASS String.prototype.indexOf.call(undefined, '2') threw exception TypeError: Type error.
 PASS String.prototype.lastIndexOf.call(undefined, '2') threw exception TypeError: Type error.
 PASS String.prototype.match.call(undefined, /2+/) threw exception TypeError: String.prototype.match requires that |this| not be undefined.
-PASS String.prototype.replace.call(undefined, /2+/, '-') threw exception TypeError: Type error.
+PASS String.prototype.replace.call(undefined, /2+/, '-') threw exception TypeError: String.prototype.replace requires that |this| not be undefined.
 PASS String.prototype.search.call(undefined, '4') threw exception TypeError: String.prototype.search requires that |this| not be undefined.
 PASS String.prototype.slice.call(undefined, 1, 3) threw exception TypeError: Type error.
 PASS String.prototype.split.call(undefined, '2') threw exception TypeError: String.prototype.split requires that |this| not be undefined.

Modified: trunk/LayoutTests/js/regress-141098-expected.txt (200116 => 200117)


--- trunk/LayoutTests/js/regress-141098-expected.txt	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/LayoutTests/js/regress-141098-expected.txt	2016-04-27 01:28:03 UTC (rev 200117)
@@ -3,6 +3,7 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
+PASS Initial setup
 PASS Exception: RangeError: Maximum call stack size exceeded.
 PASS Exception: RangeError: Maximum call stack size exceeded.
 PASS successfullyParsed is true

Modified: trunk/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js (200116 => 200117)


--- trunk/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/LayoutTests/js/script-tests/Object-getOwnPropertyNames.js	2016-04-27 01:28:03 UTC (rev 200117)
@@ -70,7 +70,7 @@
     "Error.prototype": "['constructor', 'message', 'name', 'toString']",
     "Math": "['E','LN10','LN2','LOG10E','LOG2E','PI','SQRT1_2','SQRT2','abs','acos','acosh','asin','asinh','atan','atan2','atanh','cbrt','ceil','clz32','cos','cosh','exp','expm1','floor','fround','hypot','imul','log','log10','log1p','log2','max','min','pow','random','round','sign','sin','sinh','sqrt','tan','tanh','trunc']",
     "JSON": "['parse', 'stringify']",
-    "Symbol": "['for', 'hasInstance', 'isConcatSpreadable', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'search', 'species', 'split', 'toPrimitive', 'toStringTag', 'unscopables']",
+    "Symbol": "['for', 'hasInstance', 'isConcatSpreadable', 'iterator', 'keyFor', 'length', 'match', 'name', 'prototype', 'replace', 'search', 'species', 'split', 'toPrimitive', 'toStringTag', 'unscopables']",
     "Symbol.prototype": "['constructor', 'toString', 'valueOf']",
     "Map": "['length', 'name', 'prototype']",
     "Map.prototype": "['clear', 'constructor', 'delete', 'entries', 'forEach', 'get', 'has', 'keys', 'set', 'size', 'values']",

Modified: trunk/LayoutTests/js/script-tests/regress-141098.js (200116 => 200117)


--- trunk/LayoutTests/js/script-tests/regress-141098.js	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/LayoutTests/js/script-tests/regress-141098.js	2016-04-27 01:28:03 UTC (rev 200117)
@@ -66,6 +66,11 @@
     return 1;
 }
 
+// Because this test intentionlly exhausts the stack, we call testPassed() to ensure
+// everything we need in that path has been compiled and is available.  Otherwise we
+// might properly handle an out of stack, but run out of stack calling testPassed().
+testPassed("Initial setup");
+
 var depth = probeAndRecurse(0, false);
 
 // Tier up the eval'ed code.

Modified: trunk/LayoutTests/sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.11_String.prototype.replace/S15.5.4.11_A1_T3-expected.txt (200116 => 200117)


--- trunk/LayoutTests/sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.11_String.prototype.replace/S15.5.4.11_A1_T3-expected.txt	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/LayoutTests/sputnik/Conformance/15_Native_Objects/15.5_String/15.5.4/15.5.4.11_String.prototype.replace/S15.5.4.11_A1_T3-expected.txt	2016-04-27 01:28:03 UTC (rev 200117)
@@ -1,6 +1,6 @@
 S15.5.4.11_A1_T3
 
-FAIL TypeError: Type error
+FAIL TypeError: String.prototype.replace requires that |this| not be undefined
 
 TEST COMPLETE
 

Modified: trunk/Source/_javascript_Core/ChangeLog (200116 => 200117)


--- trunk/Source/_javascript_Core/ChangeLog	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-04-27 01:28:03 UTC (rev 200117)
@@ -1,3 +1,119 @@
+2016-04-26  Michael Saboff  <[email protected]>
+
+        [ES] Implement RegExp.prototype.@@replace and use it for String.prototype.replace
+        https://bugs.webkit.org/show_bug.cgi?id=156562
+
+        Reviewed by Filip Pizlo.
+
+        Added builtins for String.prototype.replace as well as RegExp.prototype[Symbol.replace].
+
+        The String.prototype.replace also has an intrinsic, StringPrototypeReplaceIntrinsic.
+        This original intrinsic was copied to make StringPrototypeReplaceRegExpIntrinsic.
+        The difference between the two intrinsics is that StringPrototypeReplaceIntrinsic has
+        the same checks found in the new builtin hasObservableSideEffectsForStringReplace.
+        We implement these primordial checks for StringPrototypeReplaceIntrinsic in two places.
+        First, we do a trial check during ByteCode parsing time to see if the current
+        RegExp.prototype properties have changed from the original.  If they have, we don't
+        inline the intrinsic.  Later, in the fixup phase, we add nodes to the IR to emit the
+        checks at runtime.
+
+        The new intrinsic StringPrototypeReplaceRegExpIntrinsic is only available via the
+        private @replaceUsingRegExp, which is called in the String.prototype.replace builtin.
+        It is only called after hasObservableSideEffectsForStringReplace has been called
+
+        Both of these intrinsics are needed, because the JS code containing String.replace() calls
+        runs initially in the LLint and then the baseline JIT.  Even after the function tiers up
+        to the DFG JIT, the inlining budget may not allow StringPrototypeReplaceIntrinsic to be inlined.
+        Having StringPrototypeReplaceRegExpIntrinsic allows for the String.prototype.replace builtin to
+        get reasonable performance before the other intrinsic is inlined or when it can't.
+
+        * builtins/RegExpPrototype.js:
+        (match):
+        (getSubstitution):
+        (replace):
+        (search):
+        (split):
+        * builtins/StringPrototype.js:
+        (repeat):
+        (hasObservableSideEffectsForStringReplace):
+        (intrinsic.StringPrototypeReplaceIntrinsic.replace):
+        (localeCompare):
+        New builtins for String.prototype.replace and RegExp.prototype[Symbol.replace].
+
+        * bytecode/BytecodeIntrinsicRegistry.cpp:
+        * bytecode/BytecodeIntrinsicRegistry.h:
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsicCall):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::fixupGetAndSetLocalsInBlock):
+        (JSC::DFG::FixupPhase::tryAddStringReplacePrimordialChecks):
+        (JSC::DFG::FixupPhase::checkArray):
+        * dfg/DFGGraph.cpp:
+        (JSC::DFG::Graph::getRegExpPrototypeProperty):
+        * dfg/DFGGraph.h:
+        (JSC::DFG::Graph::getRegExpPrototypeProperty):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStrengthReductionPhase.cpp:
+        (JSC::DFG::StrengthReductionPhase::handleNode):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        * runtime/CommonIdentifiers.h:
+        * runtime/Intrinsic.h:
+        * runtime/RegExpPrototype.cpp:
+        (JSC::RegExpPrototype::finishCreation):
+        * runtime/StringPrototype.cpp:
+        (JSC::StringPrototype::finishCreation):
+        (JSC::replace):
+        (JSC::stringProtoFuncReplaceUsingRegExp):
+        (JSC::stringProtoFuncReplaceUsingStringSearch):
+        (JSC::operationStringProtoFuncReplaceGeneric):
+        (JSC::stringProtoFuncReplace): Deleted.
+        Added StringReplaceRegExp intrinsic.  Added checks for RegExp profiled arguments to StringReplace
+        that mirror what is in hasObservableSideEffectsForStringReplace().  If we aren't able to add the
+        checks, we OSR exit.  Add Graph::getPrimordialRegExpPrototypeProperty() as a helper to get the
+        primordial values from RegExp.prototype.
+
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init): Added @regExpPrototypeSymbolReplace and
+        @hasObservableSideEffectsForStringReplace here instead og String.prototype so that we reduce the
+        number of objects we have to traverse.
+
+        * tests/es6.yaml: Changed expectations for the various replace related tests to passing.
+
+        * tests/stress/regexp-replace-proxy.js:
+        (assert):
+        (let.getProxyNullExec.new.Proxy):
+        (let.getSetProxyNullExec.new.Proxy):
+        (get resetTracking):
+        (let.getSetProxyMatches_comma.new.Proxy):
+        (set get getSetProxyNullExec):
+        (let.getSetProxyReplace_phoneNumber.new.Proxy):
+        (set get getSetProxyMatches_comma):
+        (let.getSetProxyReplaceUnicode_digit_nonGreedy.new.Proxy):
+        (set get resetTracking):
+        * tests/stress/string-replace-proxy.js:
+        (assert):
+        (let.getSetProxyReplace.new.Proxy.replace):
+        New tests.
+
 2016-04-26  Mark Lam  <[email protected]>
 
         Gardening: speculative build fix.

Modified: trunk/Source/_javascript_Core/builtins/RegExpPrototype.js (200116 => 200117)


--- trunk/Source/_javascript_Core/builtins/RegExpPrototype.js	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/builtins/RegExpPrototype.js	2016-04-27 01:28:03 UTC (rev 200117)
@@ -120,6 +120,171 @@
     }
 }
 
+function replace(strArg, replace)
+{
+    "use strict";
+
+    function getSubstitution(matched, str, position, captures, replacement)
+    {
+        "use strict";
+
+        let matchLength = matched.length;
+        let stringLength = str.length;
+        let tailPos = position + matchLength;
+        let m = captures.length;
+        let replacementLength = replacement.length;
+        let result = "";
+        let lastStart = 0;
+
+        for (let start = 0; start = replacement.indexOf("$", lastStart), start !== -1; lastStart = start) {
+            if (start - lastStart > 0)
+                result = result + replacement.substring(lastStart, start);
+            start++;
+            let ch = replacement.charAt(start);
+            if (ch === "")
+                result = result + "$";
+            else {
+                switch (ch)
+                {
+                case "$":
+                    result = result + "$";
+                    start++;
+                    break;
+                case "&":
+                    result = result + matched;
+                    start++;
+                    break;
+                case "`":
+                    if (position > 0)
+                        result = result + str.substring(0, position);
+                    start++;
+                    break;
+                case "'":
+                    if (tailPos < stringLength)
+                        result = result + str.substring(tailPos);
+                    start++;
+                    break;
+                default:
+                    let chCode = ch.charCodeAt(0);
+                    if (chCode >= 0x30 && chCode <= 0x39) {
+                        start++;
+                        let n = chCode - 0x30;
+                        if (n > m)
+                            break;
+                        if (start < replacementLength) {
+                            let nextChCode = replacement.charCodeAt(start);
+                            if (nextChCode >= 0x30 && nextChCode <= 0x39) {
+                                let nn = 10 * n + nextChCode - 0x30;
+                                if (nn <= m) {
+                                    n = nn;
+                                    start++;
+                                }
+                            }
+                        }
+
+                        if (n == 0)
+                            break;
+
+                        if (captures[n] != @undefined)
+                            result = result + captures[n];
+                    } else
+                        result = result + "$";
+                    break;
+                }
+            }
+        }
+
+        return result + replacement.substring(lastStart);
+    }
+
+    if (!(this instanceof @Object))
+        throw new @TypeError("RegExp.prototype.@@replace requires that |this| be an Object");
+
+    let regexp = this;
+
+    let str = @toString(strArg);
+    let stringLength = str.length;
+    let functionalReplace = typeof replace === 'function';
+
+    if (!functionalReplace)
+        replace = @toString(replace);
+
+    let global = regexp.global;
+    let unicode = false;
+
+    if (global) {
+        unicode = regexp.unicode;
+        regexp.lastIndex = 0;
+    }
+
+    let resultList = [];
+    let result;
+    let done = false;
+    while (!done) {
+        result = @regExpExec(regexp, str);
+
+        if (result === null)
+            done = true;
+        else {
+            resultList.@push(result);
+            if (!global)
+                done = true;
+            else {
+                let matchStr = @toString(result[0]);
+
+                if (!matchStr.length)
+                    regexp.lastIndex = @advanceStringIndex(str, regexp.lastIndex, unicode);
+            }
+        }
+    }
+
+    let accumulatedResult = "";
+    let nextSourcePosition = 0;
+    let lastPosition = 0;
+
+    for (result of resultList) {
+        let nCaptures = result.length - 1;
+        if (nCaptures < 0)
+            nCaptures = 0;
+        let matched = @toString(result[0]);
+        let matchLength = matched.length;
+        let position = result.index;
+        position = (position > stringLength) ? stringLength : position;
+        position = (position < 0) ? 0 : position;
+
+        let captures = [];
+        for (let n = 1; n <= nCaptures; n++) {
+            let capN = result[n];
+            if (capN !== @undefined)
+                capN = @toString(capN);
+            captures[n] = capN;
+        }
+
+        let replacement;
+
+        if (functionalReplace) {
+            let replacerArgs = [ matched ].concat(captures.slice(1));
+            replacerArgs.@push(position);
+            replacerArgs.@push(str);
+
+            let replValue = replace.@apply(@undefined, replacerArgs);
+            replacement = @toString(replValue);
+        } else
+            replacement = getSubstitution(matched, str, position, captures, replace);
+
+        if (position >= nextSourcePosition && position >= lastPosition) {
+            accumulatedResult = accumulatedResult + str.substring(nextSourcePosition, position) + replacement;
+            nextSourcePosition = position + matchLength;
+            lastPosition = position;
+        }
+    }
+
+    if (nextSourcePosition >= stringLength)
+        return  accumulatedResult;
+
+    return accumulatedResult + str.substring(nextSourcePosition);
+}
+
 // 21.2.5.9 RegExp.prototype[@@search] (string)
 function search(strArg)
 {
@@ -322,4 +487,3 @@
     // 22. Return A.
     return result;
 }
-

Modified: trunk/Source/_javascript_Core/builtins/StringPrototype.js (200116 => 200117)


--- trunk/Source/_javascript_Core/builtins/StringPrototype.js	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/builtins/StringPrototype.js	2016-04-27 01:28:03 UTC (rev 200117)
@@ -105,6 +105,49 @@
     return @repeatSlowPath(string, count);
 }
 
+function hasObservableSideEffectsForStringReplace(regexp, replacer) {
+    if (replacer !== @regExpPrototypeSymbolReplace)
+        return true;
+    
+    let regexpExec = @tryGetById(regexp, "exec");
+    if (regexpExec !== @regExpBuiltinExec)
+        return true;
+
+    let regexpGlobal = @tryGetById(regexp, "global");
+    if (regexpGlobal !== @regExpProtoGlobalGetter)
+        return true;
+
+    let regexpUnicode = @tryGetById(regexp, "unicode");
+    if (regexpUnicode !== @regExpProtoUnicodeGetter)
+        return true;
+
+    return !@isRegExpObject(regexp);
+}
+
+[intrinsic=StringPrototypeReplaceIntrinsic] function replace(search, replace)
+{
+    "use strict";
+
+    if (this == null) {
+        if (this === null)
+            throw new @TypeError("String.prototype.replace requires that |this| not be null");
+        throw new @TypeError("String.prototype.replace requires that |this| not be undefined");
+    }
+
+    if (search != null) {
+        let replacer = search[@symbolReplace];
+        if (replacer !== @undefined) {
+            if (!@hasObservableSideEffectsForStringReplace(search, replacer))
+                return @toString(this).@replaceUsingRegExp(search, replace);
+            return replacer.@call(search, this, replace);
+        }
+    }
+
+    let thisString = @toString(this);
+    let searchString = @toString(search);
+    return thisString.@replaceUsingStringSearch(searchString, replace);
+}
+    
 function localeCompare(that/*, locales, options */)
 {
     "use strict";

Modified: trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -58,6 +58,7 @@
     m_symbolIsConcatSpreadable.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->isConcatSpreadableSymbol.impl())));
     m_symbolIterator.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->iteratorSymbol.impl())));
     m_symbolMatch.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->matchSymbol.impl())));
+    m_symbolReplace.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->replaceSymbol.impl())));
     m_symbolSearch.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->searchSymbol.impl())));
     m_symbolSpecies.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->speciesSymbol.impl())));
     m_symbolSplit.set(m_vm, Symbol::create(m_vm, static_cast<SymbolImpl&>(*m_vm.propertyNames->splitSymbol.impl())));

Modified: trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.h (200116 => 200117)


--- trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.h	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeIntrinsicRegistry.h	2016-04-27 01:28:03 UTC (rev 200117)
@@ -60,6 +60,7 @@
     macro(symbolIsConcatSpreadable) \
     macro(symbolIterator) \
     macro(symbolMatch) \
+    macro(symbolReplace) \
     macro(symbolSearch) \
     macro(symbolSpecies) \
     macro(symbolSplit) \

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2016-04-27 01:28:03 UTC (rev 200117)
@@ -1739,6 +1739,7 @@
         break;
             
     case StringReplace:
+    case StringReplaceRegExp:
         if (node->child1().useKind() == StringUse
             && node->child2().useKind() == RegExpObjectUse
             && node->child3().useKind() == StringUse) {

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -49,6 +49,7 @@
 #include "PreciseJumpTargets.h"
 #include "PutByIdFlags.h"
 #include "PutByIdStatus.h"
+#include <RegExpPrototype.h>
 #include "StackAlignment.h"
 #include "StringConstructor.h"
 #include "StructureStubInfo.h"
@@ -2257,12 +2258,64 @@
         if (argumentCountIncludingThis != 3)
             return false;
 
+        // Don't inline intrinsic if we exited due to "search" not being a RegExp or String object.
+        if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadType))
+            return false;
+
+        // Don't inline intrinsic if we exited due to one of the primordial RegExp checks failing.
+        if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCell))
+            return false;
+
+        JSGlobalObject* globalObject = m_inlineStackTop->m_codeBlock->globalObject();
+        Structure* regExpStructure = globalObject->regExpStructure();
+        m_graph.registerStructure(regExpStructure);
+        ASSERT(regExpStructure->storedPrototype().isObject());
+        ASSERT(regExpStructure->storedPrototype().asCell()->classInfo() == RegExpPrototype::info());
+
+        FrozenValue* regExpPrototypeObjectValue = m_graph.freeze(regExpStructure->storedPrototype());
+        Structure* regExpPrototypeStructure = regExpPrototypeObjectValue->structure();
+
+        auto isRegExpPropertySame = [&] (JSValue primordialProperty, UniquedStringImpl* propertyUID) {
+            JSValue currentProperty;
+            if (!m_graph.getRegExpPrototypeProperty(regExpStructure->storedPrototypeObject(), regExpPrototypeStructure, propertyUID, currentProperty))
+                return false;
+
+            return currentProperty == primordialProperty;
+        };
+
+        // Check that searchRegExp.exec is still the primordial RegExp.prototype.exec
+        if (!isRegExpPropertySame(globalObject->regExpProtoExecFunction(), m_vm->propertyNames->exec.impl()))
+            return false;
+
+        // Check that searchRegExp.global is still the primordial RegExp.prototype.global
+        if (!isRegExpPropertySame(globalObject->regExpProtoGlobalGetter(), m_vm->propertyNames->global.impl()))
+            return false;
+
+        // Check that searchRegExp.unicode is still the primordial RegExp.prototype.unicode
+        if (!isRegExpPropertySame(globalObject->regExpProtoUnicodeGetter(), m_vm->propertyNames->unicode.impl()))
+            return false;
+
+        // Check that searchRegExp[Symbol.match] is still the primordial RegExp.prototype[Symbol.replace]
+        if (!isRegExpPropertySame(globalObject->regExpProtoSymbolReplaceFunction(), m_vm->propertyNames->replaceSymbol.impl()))
+            return false;
+
         insertChecks();
+
         Node* result = addToGraph(StringReplace, OpInfo(0), OpInfo(prediction), get(virtualRegisterForArgument(0, registerOffset)), get(virtualRegisterForArgument(1, registerOffset)), get(virtualRegisterForArgument(2, registerOffset)));
         set(VirtualRegister(resultOperand), result);
         return true;
     }
         
+    case StringPrototypeReplaceRegExpIntrinsic: {
+        if (argumentCountIncludingThis != 3)
+            return false;
+        
+        insertChecks();
+        Node* result = addToGraph(StringReplaceRegExp, OpInfo(0), OpInfo(prediction), get(virtualRegisterForArgument(0, registerOffset)), get(virtualRegisterForArgument(1, registerOffset)), get(virtualRegisterForArgument(2, registerOffset)));
+        set(VirtualRegister(resultOperand), result);
+        return true;
+    }
+        
     case RoundIntrinsic:
     case FloorIntrinsic:
     case CeilIntrinsic:

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2016-04-27 01:28:03 UTC (rev 200117)
@@ -1120,6 +1120,7 @@
         return;
 
     case StringReplace:
+    case StringReplaceRegExp:
         if (node->child1().useKind() == StringUse
             && node->child2().useKind() == RegExpObjectUse
             && node->child3().useKind() == StringUse) {

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -280,6 +280,7 @@
     case SetFunctionName:
     case StrCat:
     case StringReplace:
+    case StringReplaceRegExp:
         return true;
         
     case MultiPutByOffset:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -910,10 +910,25 @@
             break;
         }
 
-        case StringReplace: {
+        case StringReplace:
+        case StringReplaceRegExp: {
+            if (node->child2()->shouldSpeculateString()) {
+                m_insertionSet.insertNode(
+                    m_indexInBlock, SpecNone, Check, node->origin,
+                    Edge(node->child2().node(), StringUse));
+                fixEdge<StringUse>(node->child2());
+            } else if (op == StringReplace) {
+                if (node->child2()->shouldSpeculateRegExpObject())
+                    addStringReplacePrimordialChecks(node->child2().node());
+                else 
+                    m_insertionSet.insertNode(
+                        m_indexInBlock, SpecNone, ForceOSRExit, node->origin);
+            }
+
             if (node->child1()->shouldSpeculateString()
                 && node->child2()->shouldSpeculateRegExpObject()
                 && node->child3()->shouldSpeculateString()) {
+
                 fixEdge<StringUse>(node->child1());
                 fixEdge<RegExpObjectUse>(node->child2());
                 fixEdge<StringUse>(node->child3());
@@ -1900,6 +1915,39 @@
         m_insertionSet.execute(block);
     }
     
+    void addStringReplacePrimordialChecks(Node* searchRegExp)
+    {
+        Node* node = m_currentNode;
+
+        // Check that structure of searchRegExp is RegExp object
+        m_insertionSet.insertNode(
+            m_indexInBlock, SpecNone, Check, node->origin,
+            Edge(searchRegExp, RegExpObjectUse));
+
+        auto emitPrimordialCheckFor = [&] (JSValue primordialProperty, UniquedStringImpl* propertyUID) {
+            unsigned index = m_graph.identifiers().ensure(propertyUID);
+
+            Node* actualProperty = m_insertionSet.insertNode(
+                m_indexInBlock, SpecNone, TryGetById, node->origin,
+                OpInfo(index), OpInfo(SpecFunction), Edge(searchRegExp, CellUse));
+
+            m_insertionSet.insertNode(
+                m_indexInBlock, SpecNone, CheckCell, node->origin,
+                OpInfo(m_graph.freeze(primordialProperty)), Edge(actualProperty, CellUse));
+        };
+
+        JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+
+        // Check that searchRegExp.exec is the primordial RegExp.prototype.exec
+        emitPrimordialCheckFor(globalObject->regExpProtoExecFunction(), vm().propertyNames->exec.impl());
+        // Check that searchRegExp.global is the primordial RegExp.prototype.global
+        emitPrimordialCheckFor(globalObject->regExpProtoGlobalGetter(), vm().propertyNames->global.impl());
+        // Check that searchRegExp.unicode is the primordial RegExp.prototype.unicode
+        emitPrimordialCheckFor(globalObject->regExpProtoUnicodeGetter(), vm().propertyNames->unicode.impl());
+        // Check that searchRegExp[Symbol.match] is the primordial RegExp.prototype[Symbol.replace]
+        emitPrimordialCheckFor(globalObject->regExpProtoSymbolReplaceFunction(), vm().propertyNames->replaceSymbol.impl());
+    }
+
     Node* checkArray(ArrayMode arrayMode, const NodeOrigin& origin, Node* array, Node* index, bool (*storageCheck)(const ArrayMode&) = canCSEStorage)
     {
         ASSERT(arrayMode.isSpecific());

Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -44,6 +44,7 @@
 #include "DFGVariableAccessDataDump.h"
 #include "FullBytecodeLiveness.h"
 #include "FunctionExecutableDump.h"
+#include "GetterSetter.h"
 #include "JIT.h"
 #include "JSLexicalEnvironment.h"
 #include "MaxFrameExtentForSlowPathCall.h"
@@ -1511,6 +1512,34 @@
     return true;
 }
 
+bool Graph::getRegExpPrototypeProperty(JSObject* regExpPrototype, Structure* regExpPrototypeStructure, UniquedStringImpl* uid, JSValue& returnJSValue)
+{
+    unsigned attributesUnused;
+    PropertyOffset offset = regExpPrototypeStructure->getConcurrently(uid, attributesUnused);
+    if (!isValidOffset(offset))
+        return false;
+
+    JSValue value = tryGetConstantProperty(regExpPrototype, regExpPrototypeStructure, offset);
+    if (!value)
+        return false;
+
+    // We only care about functions and getters at this point. If you want to access other properties
+    // you'll have to add code for those types.
+    JSFunction* function = jsDynamicCast<JSFunction*>(value);
+    if (!function) {
+        GetterSetter* getterSetter = jsDynamicCast<GetterSetter*>(value);
+
+        if (!getterSetter)
+            return false;
+
+        returnJSValue = JSValue(getterSetter);
+        return true;
+    }
+
+    returnJSValue = value;
+    return true;
+}
+
 bool Graph::canOptimizeStringObjectAccess(const CodeOrigin& codeOrigin)
 {
     if (hasExitSite(codeOrigin, NotStringObject))

Modified: trunk/Source/_javascript_Core/dfg/DFGGraph.h (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGGraph.h	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGGraph.h	2016-04-27 01:28:03 UTC (rev 200117)
@@ -314,6 +314,8 @@
 
     bool canOptimizeStringObjectAccess(const CodeOrigin&);
 
+    bool getRegExpPrototypeProperty(JSObject* regExpPrototype, Structure* regExpPrototypeStructure, UniquedStringImpl* uid, JSValue& returnJSValue);
+
     bool roundShouldSpeculateInt32(Node* arithRound, PredictionPass pass)
     {
         ASSERT(arithRound->op() == ArithRound || arithRound->op() == ArithFloor || arithRound->op() == ArithCeil || arithRound->op() == ArithTrunc);

Modified: trunk/Source/_javascript_Core/dfg/DFGNode.h (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGNode.h	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGNode.h	2016-04-27 01:28:03 UTC (rev 200117)
@@ -1384,6 +1384,7 @@
         case GetGlobalVar:
         case GetGlobalLexicalVariable:
         case StringReplace:
+        case StringReplaceRegExp:
             return true;
         default:
             return false;

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2016-04-27 01:28:03 UTC (rev 200117)
@@ -246,6 +246,7 @@
     macro(RegExpExec, NodeResultJS | NodeMustGenerate) \
     macro(RegExpTest, NodeResultJS | NodeMustGenerate) \
     macro(StringReplace, NodeResultJS | NodeMustGenerate) \
+    macro(StringReplaceRegExp, NodeResultJS | NodeMustGenerate) \
     \
     /* Optimizations for string access */ \
     macro(StringCharCodeAt, NodeResultInt32) \

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -674,6 +674,7 @@
         case RegExpExec:
         case RegExpTest:
         case StringReplace:
+        case StringReplaceRegExp:
         case GetById:
         case GetByIdFlush:
         case GetByOffset:

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2016-04-27 01:28:03 UTC (rev 200117)
@@ -344,6 +344,7 @@
     case ForwardVarargs:
     case CopyRest:
     case StringReplace:
+    case StringReplaceRegExp:
     case GetRegExpObjectLastIndex:
     case SetRegExpObjectLastIndex:
     case RecordRegExpCachedResult:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -2981,7 +2981,8 @@
         break;
     }
 
-    case StringReplace: {
+    case StringReplace:
+    case StringReplaceRegExp: {
         if (node->child1().useKind() == StringUse
             && node->child2().useKind() == RegExpObjectUse
             && node->child3().useKind() == StringUse) {
@@ -3026,14 +3027,19 @@
             cellResult(resultPayload.gpr(), node);
             break;
         }
+
+        // If we fixed up the edge of child2, we inserted a Check(@child2, String).
+        OperandSpeculationMode child2SpeculationMode = AutomaticOperandSpeculation;
+        if (node->child2().useKind() == StringUse)
+            child2SpeculationMode = ManualOperandSpeculation;
         
         JSValueOperand string(this, node->child1());
-        JSValueOperand regExp(this, node->child2());
+        JSValueOperand search(this, node->child2(), child2SpeculationMode);
         JSValueOperand replace(this, node->child3());
         GPRReg stringTagGPR = string.tagGPR();
         GPRReg stringPayloadGPR = string.payloadGPR();
-        GPRReg regExpTagGPR = regExp.tagGPR();
-        GPRReg regExpPayloadGPR = regExp.payloadGPR();
+        GPRReg searchTagGPR = search.tagGPR();
+        GPRReg searchPayloadGPR = search.payloadGPR();
         GPRReg replaceTagGPR = replace.tagGPR();
         GPRReg replacePayloadGPR = replace.payloadGPR();
         
@@ -3042,7 +3048,7 @@
         GPRFlushedCallResult resultPayload(this);
         callOperation(
             operationStringProtoFuncReplaceGeneric, resultTag.gpr(), resultPayload.gpr(),
-            stringTagGPR, stringPayloadGPR, regExpTagGPR, regExpPayloadGPR, replaceTagGPR,
+            stringTagGPR, stringPayloadGPR, searchTagGPR, searchPayloadGPR, replaceTagGPR,
             replacePayloadGPR);
         m_jit.exceptionCheck();
         cellResult(resultPayload.gpr(), node);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -3112,7 +3112,8 @@
         break;
     }
 
-    case StringReplace: {
+    case StringReplace:
+    case StringReplaceRegExp: {
         bool sample = false;
 
         if (sample)
@@ -3164,18 +3165,23 @@
                 m_jit.decrementSuperSamplerCount();
             break;
         }
-        
+
+        // If we fixed up the edge of child2, we inserted a Check(@child2, String).
+        OperandSpeculationMode child2SpeculationMode = AutomaticOperandSpeculation;
+        if (node->child2().useKind() == StringUse)
+            child2SpeculationMode = ManualOperandSpeculation;
+
         JSValueOperand string(this, node->child1());
-        JSValueOperand regExp(this, node->child2());
+        JSValueOperand search(this, node->child2(), child2SpeculationMode);
         JSValueOperand replace(this, node->child3());
         GPRReg stringGPR = string.gpr();
-        GPRReg regExpGPR = regExp.gpr();
+        GPRReg searchGPR = search.gpr();
         GPRReg replaceGPR = replace.gpr();
         
         flushRegisters();
         GPRFlushedCallResult result(this);
         callOperation(
-            operationStringProtoFuncReplaceGeneric, result.gpr(), stringGPR, regExpGPR,
+            operationStringProtoFuncReplaceGeneric, result.gpr(), stringGPR, searchGPR,
             replaceGPR);
         m_jit.exceptionCheck();
         cellResult(result.gpr(), node);

Modified: trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/dfg/DFGStrengthReductionPhase.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -618,7 +618,8 @@
             break;
         }
 
-        case StringReplace: {
+        case StringReplace:
+        case StringReplaceRegExp: {
             Node* stringNode = m_node->child1().node();
             String string = stringNode->tryGetString(m_graph);
             if (!string)

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -233,6 +233,7 @@
     case RegExpTest:
     case NewRegexp:
     case StringReplace:
+    case StringReplaceRegExp: 
     case GetRegExpObjectLastIndex:
     case SetRegExpObjectLastIndex:
     case RecordRegExpCachedResult:

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -957,6 +957,7 @@
             compileSetFunctionName();
             break;
         case StringReplace:
+        case StringReplaceRegExp:
             compileStringReplace();
             break;
         case GetRegExpObjectLastIndex:

Modified: trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h (200116 => 200117)


--- trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h	2016-04-27 01:28:03 UTC (rev 200117)
@@ -315,6 +315,7 @@
     macro(isConcatSpreadable) \
     macro(iterator) \
     macro(match) \
+    macro(replace) \
     macro(search) \
     macro(species) \
     macro(split) \
@@ -425,6 +426,8 @@
     macro(regExpCreate) \
     macro(SetIterator) \
     macro(setIteratorNext) \
+    macro(replaceUsingRegExp) \
+    macro(replaceUsingStringSearch) \
     macro(MapIterator) \
     macro(mapIteratorNext) \
     macro(regExpBuiltinExec) \
@@ -436,8 +439,10 @@
     macro(regExpProtoSourceGetter) \
     macro(regExpProtoStickyGetter) \
     macro(regExpProtoUnicodeGetter) \
+    macro(regExpReplaceFast) \
     macro(regExpSearchFast) \
     macro(regExpSplitFast) \
+    macro(regExpPrototypeSymbolReplace) \
     macro(stringIncludesInternal) \
     macro(stringSplitFast) \
     macro(stringSubstrInternal) \

Modified: trunk/Source/_javascript_Core/runtime/Intrinsic.h (200116 => 200117)


--- trunk/Source/_javascript_Core/runtime/Intrinsic.h	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/runtime/Intrinsic.h	2016-04-27 01:28:03 UTC (rev 200117)
@@ -53,6 +53,7 @@
     RegExpTestIntrinsic,
     StringPrototypeValueOfIntrinsic,
     StringPrototypeReplaceIntrinsic,
+    StringPrototypeReplaceRegExpIntrinsic,
     IMulIntrinsic,
     RandomIntrinsic,
     FRoundIntrinsic,

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -559,12 +559,18 @@
 
     JSObject* regExpProtoFlagsGetterObject = getGetterById(exec, m_regExpPrototype.get(), vm.propertyNames->flags);
     JSObject* regExpProtoGlobalGetterObject = getGetterById(exec, m_regExpPrototype.get(), vm.propertyNames->global);
+    m_regExpProtoGlobalGetter.set(vm, this, regExpProtoGlobalGetterObject);
     JSObject* regExpProtoIgnoreCaseGetterObject = getGetterById(exec, m_regExpPrototype.get(), vm.propertyNames->ignoreCase);
     JSObject* regExpProtoMultilineGetterObject = getGetterById(exec, m_regExpPrototype.get(), vm.propertyNames->multiline);
     JSObject* regExpProtoSourceGetterObject = getGetterById(exec, m_regExpPrototype.get(), vm.propertyNames->source);
     JSObject* regExpProtoStickyGetterObject = getGetterById(exec, m_regExpPrototype.get(), vm.propertyNames->sticky);
     JSObject* regExpProtoUnicodeGetterObject = getGetterById(exec, m_regExpPrototype.get(), vm.propertyNames->unicode);
-    
+    m_regExpProtoUnicodeGetter.set(vm, this, regExpProtoUnicodeGetterObject);
+    JSObject* builtinRegExpExec = asObject(m_regExpPrototype->getDirect(vm, vm.propertyNames->exec).asCell());
+    m_regExpProtoExec.set(vm, this, builtinRegExpExec);
+    JSObject* regExpSymbolReplace = asObject(m_regExpPrototype->getDirect(vm, vm.propertyNames->replaceSymbol).asCell());
+    m_regExpProtoSymbolReplace.set(vm, this, regExpSymbolReplace);
+
     GlobalPropertyInfo staticGlobals[] = {
         GlobalPropertyInfo(vm.propertyNames->NaN, jsNaN(), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->Infinity, jsNumber(std::numeric_limits<double>::infinity()), DontEnum | DontDelete | ReadOnly),
@@ -646,7 +652,7 @@
         GlobalPropertyInfo(vm.propertyNames->regExpProtoUnicodeGetterPrivateName, regExpProtoUnicodeGetterObject, DontEnum | DontDelete | ReadOnly),
 
         // RegExp.prototype helpers.
-        GlobalPropertyInfo(vm.propertyNames->regExpBuiltinExecPrivateName, m_regExpPrototype->getDirect(vm, vm.propertyNames->exec), DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->regExpBuiltinExecPrivateName, builtinRegExpExec, DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->regExpCreatePrivateName, JSFunction::create(vm, this, 2, String(), esSpecRegExpCreate, NoIntrinsic), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().hasObservableSideEffectsForRegExpMatchPrivateName(), JSFunction::createBuiltinFunction(vm, regExpPrototypeHasObservableSideEffectsForRegExpMatchCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->builtinNames().hasObservableSideEffectsForRegExpSplitPrivateName(), JSFunction::createBuiltinFunction(vm, regExpPrototypeHasObservableSideEffectsForRegExpSplitCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly),
@@ -655,8 +661,10 @@
         GlobalPropertyInfo(vm.propertyNames->regExpMatchFastPrivateName, JSFunction::create(vm, this, 2, String(), regExpProtoFuncMatchFast), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->regExpSearchFastPrivateName, JSFunction::create(vm, this, 2, String(), regExpProtoFuncSearchFast), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->regExpSplitFastPrivateName, JSFunction::create(vm, this, 2, String(), regExpProtoFuncSplitFast), DontEnum | DontDelete | ReadOnly),
+        GlobalPropertyInfo(vm.propertyNames->regExpPrototypeSymbolReplacePrivateName, m_regExpPrototype->getDirect(vm, vm.propertyNames->replaceSymbol), DontEnum | DontDelete | ReadOnly),
 
         // String.prototype helpers.
+        GlobalPropertyInfo(vm.propertyNames->builtinNames().hasObservableSideEffectsForStringReplacePrivateName(), JSFunction::createBuiltinFunction(vm, stringPrototypeHasObservableSideEffectsForStringReplaceCodeGenerator(vm), this), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->stringIncludesInternalPrivateName, JSFunction::create(vm, this, 1, String(), builtinStringIncludesInternal), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->stringSplitFastPrivateName, JSFunction::create(vm, this, 2, String(), stringProtoFuncSplitFast), DontEnum | DontDelete | ReadOnly),
         GlobalPropertyInfo(vm.propertyNames->stringSubstrInternalPrivateName, JSFunction::create(vm, this, 2, String(), builtinStringSubstrInternal), DontEnum | DontDelete | ReadOnly),

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (200116 => 200117)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2016-04-27 01:28:03 UTC (rev 200117)
@@ -229,6 +229,10 @@
     WriteBarrier<JSFunction> m_initializePromiseFunction;
     WriteBarrier<JSFunction> m_newPromiseCapabilityFunction;
     WriteBarrier<JSFunction> m_functionProtoHasInstanceSymbolFunction;
+    WriteBarrier<JSObject> m_regExpProtoExec;
+    WriteBarrier<JSObject> m_regExpProtoSymbolReplace;
+    WriteBarrier<JSObject> m_regExpProtoGlobalGetter;
+    WriteBarrier<JSObject> m_regExpProtoUnicodeGetter;
     WriteBarrier<GetterSetter> m_throwTypeErrorGetterSetter;
 
     WriteBarrier<ModuleLoaderObject> m_moduleLoader;
@@ -452,6 +456,10 @@
     JSFunction* initializePromiseFunction() const { return m_initializePromiseFunction.get(); }
     JSFunction* newPromiseCapabilityFunction() const { return m_newPromiseCapabilityFunction.get(); }
     JSFunction* functionProtoHasInstanceSymbolFunction() const { return m_functionProtoHasInstanceSymbolFunction.get(); }
+    JSObject* regExpProtoExecFunction() const { return m_regExpProtoExec.get(); }
+    JSObject* regExpProtoSymbolReplaceFunction() const { return m_regExpProtoSymbolReplace.get(); }
+    JSObject* regExpProtoGlobalGetter() const { return m_regExpProtoGlobalGetter.get(); }
+    JSObject* regExpProtoUnicodeGetter() const { return m_regExpProtoUnicodeGetter.get(); }
     GetterSetter* throwTypeErrorGetterSetter(VM& vm)
     {
         if (!m_throwTypeErrorGetterSetter)

Modified: trunk/Source/_javascript_Core/runtime/RegExpPrototype.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/runtime/RegExpPrototype.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/runtime/RegExpPrototype.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -79,6 +79,7 @@
     JSC_NATIVE_GETTER(vm.propertyNames->source, regExpProtoGetterSource, DontEnum | Accessor);
     JSC_NATIVE_GETTER(vm.propertyNames->flags, regExpProtoGetterFlags, DontEnum | Accessor);
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->matchSymbol, regExpPrototypeMatchCodeGenerator, DontEnum);
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->replaceSymbol, regExpPrototypeReplaceCodeGenerator, DontEnum);
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->searchSymbol, regExpPrototypeSearchCodeGenerator, DontEnum);
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->splitSymbol, regExpPrototypeSplitCodeGenerator, DontEnum);
 

Modified: trunk/Source/_javascript_Core/runtime/StringPrototype.cpp (200116 => 200117)


--- trunk/Source/_javascript_Core/runtime/StringPrototype.cpp	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/runtime/StringPrototype.cpp	2016-04-27 01:28:03 UTC (rev 200117)
@@ -68,7 +68,8 @@
 EncodedJSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncPadEnd(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncPadStart(ExecState*);
-EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState*);
+EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(ExecState*);
+EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstr(ExecState*);
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState*);
@@ -111,6 +112,7 @@
 @begin stringPrototypeTable
     match     JSBuiltin    DontEnum|Function 1
     repeat    JSBuiltin    DontEnum|Function 1
+    replace   JSBuiltin    DontEnum|Function 2
     search    JSBuiltin    DontEnum|Function 1
     split     JSBuiltin    DontEnum|Function 1
 @end
@@ -137,7 +139,8 @@
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("lastIndexOf", stringProtoFuncLastIndexOf, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("padEnd", stringProtoFuncPadEnd, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("padStart", stringProtoFuncPadStart, DontEnum, 1);
-    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION("replace", stringProtoFuncReplace, DontEnum, 2, StringPrototypeReplaceIntrinsic);
+    JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->replaceUsingRegExpPrivateName, stringProtoFuncReplaceUsingRegExp, DontEnum, 2, StringPrototypeReplaceRegExpIntrinsic);
+    JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->replaceUsingStringSearchPrivateName, stringProtoFuncReplaceUsingStringSearch, DontEnum, 2);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("slice", stringProtoFuncSlice, DontEnum, 2);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substr", stringProtoFuncSubstr, DontEnum, 2);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("substring", stringProtoFuncSubstring, DontEnum, 2);
@@ -909,11 +912,28 @@
     return replace(vm, exec, string, searchValue, replaceValue);
 }
 
-EncodedJSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec)
+EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingRegExp(ExecState* exec)
 {
-    return replace(exec->vm(), exec, exec->thisValue(), exec->argument(0), exec->argument(1));
+    JSString* string = exec->thisValue().toString(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    JSValue searchValue = exec->argument(0);
+    if (!searchValue.inherits(RegExpObject::info()))
+        return JSValue::encode(jsUndefined());
+
+    return replaceUsingRegExpSearch(exec->vm(), exec, string, searchValue, exec->argument(1));
 }
 
+EncodedJSValue JSC_HOST_CALL stringProtoFuncReplaceUsingStringSearch(ExecState* exec)
+{
+    JSString* string = exec->thisValue().toString(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    return replaceUsingStringSearch(exec->vm(), exec, string, exec->argument(0), exec->argument(1));
+}
+
 EncodedJSValue JIT_OPERATION operationStringProtoFuncReplaceGeneric(
     ExecState* exec, EncodedJSValue thisValue, EncodedJSValue searchValue,
     EncodedJSValue replaceValue)

Modified: trunk/Source/_javascript_Core/tests/es6.yaml (200116 => 200117)


--- trunk/Source/_javascript_Core/tests/es6.yaml	2016-04-27 01:25:26 UTC (rev 200116)
+++ trunk/Source/_javascript_Core/tests/es6.yaml	2016-04-27 01:28:03 UTC (rev 200117)
@@ -1003,7 +1003,7 @@
 - path: es6/Proxy_internal_get_calls_RegExp.prototype[Symbol.match].js
   cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_RegExp.prototype[Symbol.replace].js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_RegExp.prototype[Symbol.search].js
   cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_RegExp.prototype[Symbol.split].js
@@ -1013,7 +1013,7 @@
 - path: es6/Proxy_internal_get_calls_String.prototype.match.js
   cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_String.prototype.replace.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_String.prototype.search.js
   cmd: runES6 :normal
 - path: es6/Proxy_internal_get_calls_String.prototype.split.js
@@ -1089,7 +1089,7 @@
 - path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.match].js
   cmd: runES6 :normal
 - path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.replace].js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.search].js
   cmd: runES6 :normal
 - path: es6/RegExp.prototype_properties_RegExp.prototype[Symbol.split].js
@@ -1199,7 +1199,7 @@
 - path: es6/well-known_symbols_Symbol.match.js
   cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.replace.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.search.js
   cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.species_Array.prototype.concat.js

Added: trunk/Source/_javascript_Core/tests/stress/regexp-replace-proxy.js (0 => 200117)


--- trunk/Source/_javascript_Core/tests/stress/regexp-replace-proxy.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/regexp-replace-proxy.js	2016-04-27 01:28:03 UTC (rev 200117)
@@ -0,0 +1,154 @@
+function assert(assertion) {
+    if (typeof assertion != "string")
+        throw new Error("Invalid assertion.");
+
+    let result = eval(assertion);
+
+    if (!result)
+        throw new Error("Bad assertion: " + assertion);
+}
+
+let get = [];
+let getSet = [];
+
+function resetTracking()
+{
+    get = [];
+    getSet = [];
+}
+
+let getProxyNullExec = new Proxy({
+        exec: function()
+        {
+            return null;
+        }
+    }, {
+        get: function(o, k)
+        {
+            get.push(k); return o[k];
+        }
+    });
+
+resetTracking();
+RegExp.prototype[Symbol.replace].call(getProxyNullExec);
+assert('get == "global,exec"');
+
+let getSetProxyNullExec = new Proxy(
+    {
+        exec: function()
+        {
+            return null;
+        }
+    }, {
+        get: function(o, k)
+        {
+            get.push(k);
+            getSet.push(k);
+            return o[k];
+        },
+        set: function(o, k, v)
+        {
+            getSet.push(k);
+            o[k] = v;
+        }
+    });
+
+getSetProxyNullExec.global = true;
+
+resetTracking();
+RegExp.prototype[Symbol.replace].call(getSetProxyNullExec);
+assert('get == "global,unicode,exec"');
+assert('getSet == "global,unicode,lastIndex,exec"');
+
+let regExpGlobal_comma = new RegExp(",", "g");
+let getSetProxyMatches_comma = new Proxy(
+    {
+        exec: function(string)
+        {
+            return regExpGlobal_comma.exec(string);
+        }
+    }, {
+        get: function(o, k)
+        {
+            get.push(k);
+            getSet.push(k);
+            return o[k];
+        },
+        set: function(o, k, v)
+        {
+            getSet.push(k);
+            o[k] = v;
+        }
+    });
+
+getSetProxyMatches_comma.global = true;
+resetTracking();
+let replaceResult = RegExp.prototype[Symbol.replace].call(getSetProxyMatches_comma, "John,,Doe,121 Main St.,Anytown", ":");
+assert('replaceResult == "John::Doe:121 Main St.:Anytown"');
+assert('get == "global,unicode,exec,exec,exec,exec,exec"');
+assert('getSet == "global,unicode,lastIndex,exec,exec,exec,exec,exec"');
+
+let regExp_phoneNumber = new RegExp("(\\d{3})(\\d{3})(\\d{4})", "");
+let getSetProxyReplace_phoneNumber = new Proxy(
+    {
+        exec: function(string)
+        {
+            return regExp_phoneNumber.exec(string);
+        }
+    }, {
+        get: function(o, k)
+        {
+            get.push(k);
+            getSet.push(k);
+            if (k.toString() == "lastIndex")
+                return regExpGlobal_phoneNumber.lastIndex;
+            return o[k];
+        },
+        set: function(o, k, v)
+        {
+            getSet.push(k);
+            if (k.toString() == "lastIndex")
+                regExp_phoneNumber.lastIndex = v;
+            o[k] = v;
+        }
+    });
+
+resetTracking();
+replaceResult = RegExp.prototype[Symbol.replace].call(getSetProxyReplace_phoneNumber, "8005551212", "$1-$2-$3");
+assert('replaceResult == "800-555-1212"');
+assert('get == "global,exec"');
+assert('getSet == "global,exec"');
+
+let regExpGlobalUnicode_digit_nonGreedy = new RegExp("\\d{0,1}", "gu");
+let getSetProxyReplaceUnicode_digit_nonGreedy = new Proxy(
+    {
+        exec: function(string)
+        {
+            return regExpGlobalUnicode_digit_nonGreedy.exec(string);
+        }
+    }, {
+        get: function(o, k)
+        {
+            get.push(k);
+            getSet.push(k);
+            if (k.toString() == "lastIndex")
+                return regExpGlobalUnicode_digit_nonGreedy.lastIndex;
+            return o[k];
+        },
+        set: function(o, k, v)
+        {
+            getSet.push(k);
+            if (k.toString() == "lastIndex")
+                regExpGlobalUnicode_digit_nonGreedy.lastIndex = v;
+            o[k] = v;
+        }
+    });
+
+getSetProxyReplaceUnicode_digit_nonGreedy.global = true;
+getSetProxyReplaceUnicode_digit_nonGreedy.unicode = true;
+
+resetTracking();
+replaceResult = RegExp.prototype[Symbol.replace].call(getSetProxyReplaceUnicode_digit_nonGreedy, "12X3\u{10400}4", "[$&]");
+assert('replaceResult == "[1][2][]X[3][]\u{10400}[4][]"');
+assert('get == "global,unicode,exec,exec,exec,lastIndex,exec,exec,lastIndex,exec,exec,lastIndex,exec"');
+assert('getSet == "global,unicode,lastIndex,exec,exec,exec,lastIndex,lastIndex,exec,exec,lastIndex,lastIndex,exec,exec,lastIndex,lastIndex,exec"');

Added: trunk/Source/_javascript_Core/tests/stress/string-replace-proxy.js (0 => 200117)


--- trunk/Source/_javascript_Core/tests/stress/string-replace-proxy.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/string-replace-proxy.js	2016-04-27 01:28:03 UTC (rev 200117)
@@ -0,0 +1,50 @@
+function assert(assertion) {
+    if (typeof assertion != "string")
+        throw new Error("Invalid assertion.");
+
+    let result = eval(assertion);
+
+    if (!result)
+        throw new Error("Bad assertion: " + assertion);
+}
+
+let calls = 0;
+let getSet = [];
+
+function resetTracking()
+{
+    calls = 0;
+    getSet = [];
+}
+
+let getSetProxyReplace = new Proxy(
+    {
+        replace: function(string, search, replaceWith)
+        {
+            calls++;
+            return string.replace(search, replaceWith);
+        }
+    }, {
+        get: function(o, k)
+        {
+            getSet.push(k);
+            return o[k];
+        },
+        set: function(o, k, v)
+        {
+            getSet.push(k);
+            o[k] = v;
+        }
+    });
+
+resetTracking();
+let replaceResult = getSetProxyReplace.replace("This is a test", / /g, "_");
+assert('replaceResult == "This_is_a_test"');
+assert('calls === 1')
+assert('getSet == "replace"');
+
+resetTracking();
+replaceResult = getSetProxyReplace.replace("This is a test", " ", "_");
+assert('replaceResult == "This_is a test"');
+assert('calls === 1')
+assert('getSet == "replace"');
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to