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"');