Add support for RegExp named capture groups
https://bugs.webkit.org/show_bug.cgi?id=176435
Reviewed by Filip Pizlo.
Source/JavaScriptCore:
Added parsing for both naming a captured parenthesis as well and using a named group in
a back reference. Also added support for using named groups with String.prototype.replace().
This patch does not throw Syntax Errors as described in the current spec text for the two
cases of malformed back references in String.prototype.replace() as I believe that it
is inconsistent with the current semantics for handling of other malformed replacement
tokens. I filed an issue for the requested change to the proposed spec and also filed
a FIXME bug https://bugs.webkit.org/show_bug.cgi?id=176434.
This patch does not implement strength reduction in the optimizing JITs for named capture
groups. Filed https://bugs.webkit.org/show_bug.cgi?id=176464.
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGStrengthReductionPhase.cpp:
(JSC::DFG::StrengthReductionPhase::handleNode):
* runtime/CommonIdentifiers.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::haveABadTime):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::regExpMatchesArrayWithGroupsStructure const):
* runtime/RegExp.cpp:
(JSC::RegExp::finishCreation):
* runtime/RegExp.h:
* runtime/RegExpMatchesArray.cpp:
(JSC::createStructureImpl):
(JSC::createRegExpMatchesArrayWithGroupsStructure):
(JSC::createRegExpMatchesArrayWithGroupsSlowPutStructure):
* runtime/RegExpMatchesArray.h:
(JSC::createRegExpMatchesArray):
* runtime/StringPrototype.cpp:
(JSC::substituteBackreferencesSlow):
(JSC::replaceUsingRegExpSearch):
* yarr/YarrParser.h:
(JSC::Yarr::Parser::CharacterClassParserDelegate::atomNamedBackReference):
(JSC::Yarr::Parser::parseEscape):
(JSC::Yarr::Parser::parseParenthesesBegin):
(JSC::Yarr::Parser::tryConsumeUnicodeEscape):
(JSC::Yarr::Parser::tryConsumeIdentifierCharacter):
(JSC::Yarr::Parser::isIdentifierStart):
(JSC::Yarr::Parser::isIdentifierPart):
(JSC::Yarr::Parser::tryConsumeGroupName):
* yarr/YarrPattern.cpp:
(JSC::Yarr::YarrPatternConstructor::atomParenthesesSubpatternBegin):
(JSC::Yarr::YarrPatternConstructor::atomNamedBackReference):
(JSC::Yarr::YarrPattern::errorMessage):
* yarr/YarrPattern.h:
(JSC::Yarr::YarrPattern::reset):
* yarr/YarrSyntaxChecker.cpp:
(JSC::Yarr::SyntaxChecker::atomParenthesesSubpatternBegin):
(JSC::Yarr::SyntaxChecker::atomNamedBackReference):
Source/WebCore:
Implemented stub routines to support named capture groups. These are no-ops
just like for number capture group.
No new tests as this is covered by existing tests.
* contentextensions/URLFilterParser.cpp:
(WebCore::ContentExtensions::PatternParser::atomNamedBackReference):
(WebCore::ContentExtensions::PatternParser::atomParenthesesSubpatternBegin):
LayoutTests:
New regression tests.
* js/regexp-named-capture-groups-expected.txt: Added.
* js/regexp-named-capture-groups.html: Added.
* js/script-tests/regexp-named-capture-groups.js: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@221769 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h
index 7e1938e..af6c55b 100644
--- a/Source/JavaScriptCore/runtime/RegExpMatchesArray.h
+++ b/Source/JavaScriptCore/runtime/RegExpMatchesArray.h
@@ -31,6 +31,7 @@
static const PropertyOffset RegExpMatchesArrayIndexPropertyOffset = 100;
static const PropertyOffset RegExpMatchesArrayInputPropertyOffset = 101;
+static const PropertyOffset RegExpMatchesArrayGroupsPropertyOffset = 102;
ALWAYS_INLINE JSArray* tryCreateUninitializedRegExpMatchesArray(ObjectInitializationScope& scope, GCDeferralContext* deferralContext, Structure* structure, unsigned initialLength)
{
@@ -76,18 +77,26 @@
// FIXME: This should handle array allocation errors gracefully.
// https://bugs.webkit.org/show_bug.cgi?id=155144
+ unsigned numSubpatterns = regExp->numSubpatterns();
+ bool hasNamedCaptures = regExp->hasNamedCaptures();
+ JSObject* groups = nullptr;
+
auto setProperties = [&] () {
array->putDirect(vm, RegExpMatchesArrayIndexPropertyOffset, jsNumber(result.start));
array->putDirect(vm, RegExpMatchesArrayInputPropertyOffset, input);
+ if (hasNamedCaptures) {
+ groups = JSFinalObject::create(vm, JSFinalObject::createStructure(vm, globalObject, globalObject->objectPrototype(), 0));
+ array->putDirect(vm, RegExpMatchesArrayGroupsPropertyOffset, groups);
+ }
};
-
- unsigned numSubpatterns = regExp->numSubpatterns();
-
+
GCDeferralContext deferralContext(vm.heap);
-
+
+ Structure* matchStructure = hasNamedCaptures ? globalObject->regExpMatchesArrayWithGroupsStructure() : globalObject->regExpMatchesArrayStructure();
+
if (UNLIKELY(globalObject->isHavingABadTime())) {
ObjectInitializationScope scope(vm);
- array = JSArray::tryCreateUninitializedRestricted(scope, &deferralContext, globalObject->regExpMatchesArrayStructure(), numSubpatterns + 1);
+ array = JSArray::tryCreateUninitializedRestricted(scope, &deferralContext, matchStructure, numSubpatterns + 1);
// FIXME: we should probably throw an out of memory error here, but
// when making this change we should check that all clients of this
// function will correctly handle an exception being thrown from here.
@@ -106,10 +115,15 @@
else
value = jsUndefined();
array->initializeIndexWithoutBarrier(scope, i, value);
+ if (groups) {
+ String groupName = regExp->getCaptureGroupName(i);
+ if (!groupName.isEmpty())
+ groups->putDirect(vm, Identifier::fromString(&vm, groupName), value);
+ }
}
} else {
ObjectInitializationScope scope(vm);
- array = tryCreateUninitializedRegExpMatchesArray(scope, &deferralContext, globalObject->regExpMatchesArrayStructure(), numSubpatterns + 1);
+ array = tryCreateUninitializedRegExpMatchesArray(scope, &deferralContext, matchStructure, numSubpatterns + 1);
RELEASE_ASSERT(array);
setProperties();
@@ -126,6 +140,11 @@
else
value = jsUndefined();
array->initializeIndexWithoutBarrier(scope, i, value, ArrayWithContiguous);
+ if (groups) {
+ String groupName = regExp->getCaptureGroupName(i);
+ if (!groupName.isEmpty())
+ groups->putDirect(vm, Identifier::fromString(&vm, groupName), value);
+ }
}
}
return array;
@@ -139,5 +158,7 @@
JSArray* createEmptyRegExpMatchesArray(JSGlobalObject*, JSString*, RegExp*);
Structure* createRegExpMatchesArrayStructure(VM&, JSGlobalObject*);
Structure* createRegExpMatchesArraySlowPutStructure(VM&, JSGlobalObject*);
+Structure* createRegExpMatchesArrayWithGroupsStructure(VM&, JSGlobalObject*);
+Structure* createRegExpMatchesArrayWithGroupsSlowPutStructure(VM&, JSGlobalObject*);
} // namespace JSC