Skip to content

Commit 270c824

Browse files
committed
Implement RegExp v flag with set notation + properties of strings
https://bugs.webkit.org/show_bug.cgi?id=241593 rdar://100337109 Reviewed by Yusuke Suzuki. This change implements the TC39 stage 3 proposal RegExp v flag with set notation + properties of strings, https://github.com/tc39/proposal-regexp-v-flag. It adds a new "unicodeSets" compile mode for the Yarr engine. Like the prior Unicode Yarr features, this change is driven by Unicode Database Files (UCD). This change includes two such new files, JavaScriptCore/ucd/{emoji-sequences.txt & emoji-zwj-sequences.txt}. The newly added properties include lists of strings. These strings are processed via the character class syntax through. When it comes to matching however, there is some desuguraing that turns such a property of strings into a list of alternations. For example, say a property has strings str1...strN plus a traditional character class, single-character-class, we create the pattern equivalent of: (?:str1|str2|...|strN|[single-character-class]) Per the spec, longer strings appear earlier in the alternation, and before the traditional character class. This allows for searching for longer properties in a property list where substrings of other strings are included in that list. There are new set of combining operators allowed in the class set character classes. Two character class elements that appear adjacent to each other implicitly have the Union combining operations. There is also an Intersection operation with the && operator and a Subtraction operation with the || operator. There is new ClassSet parsing that follows new "cleaner" rules that traditional character classes. The prior ccharacter class constructor and delegates are mostly unchanged, except for the compile mode now being switched on an enum instead of a bool. Added check that both 'u' and 'v' flags don't appear in the same RegExp. Added unicodeSets getter watchpoint to the m_regExpPrimordialPropertiesWatchpointSet. * JSTests/es6/Proxy_internal_get_calls_RegExp.prototype.flags.js: * JSTests/stress/regexp-vflag-property-of-strings.js: Added. (arrayToString): (objectToString): (dumpValue): (compareArray): (compareGroups): (testRegExp): (testRegExpSyntaxError): * JSTests/stress/static-getter-in-names.js: * JSTests/test262/config.yaml: * LayoutTests/js/Object-getOwnPropertyNames-expected.txt: * LayoutTests/js/script-tests/Object-getOwnPropertyNames.js: * Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj: * Source/JavaScriptCore/builtins/BuiltinNames.h: * Source/JavaScriptCore/builtins/RegExpPrototype.js: (linkTimeConstant.hasObservableSideEffectsForRegExpMatch): (linkTimeConstant.hasObservableSideEffectsForRegExpSplit): (overriddenName.string_appeared_here.split): * Source/JavaScriptCore/builtins/StringPrototype.js: (linkTimeConstant.hasObservableSideEffectsForStringReplace): * Source/JavaScriptCore/bytecode/LinkTimeConstant.h: * Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * Source/JavaScriptCore/dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::addStringReplacePrimordialChecks): * Source/JavaScriptCore/dfg/DFGOperations.cpp: (JSC::DFG::JSC_DEFINE_JIT_OPERATION): * Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp: (JSC::DFG::StrengthReductionPhase::handleNode): * Source/JavaScriptCore/runtime/CachedTypes.cpp: * Source/JavaScriptCore/runtime/CommonIdentifiers.h: * Source/JavaScriptCore/runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::init): * Source/JavaScriptCore/runtime/JSGlobalObject.h: * Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h: (JSC::JSGlobalObject::regExpProtoUnicodeSetsGetter const): * Source/JavaScriptCore/runtime/RegExp.h: * Source/JavaScriptCore/runtime/RegExpCache.h: * Source/JavaScriptCore/runtime/RegExpObject.cpp: (JSC::RegExpObject::matchGlobal): * Source/JavaScriptCore/runtime/RegExpPrototype.cpp: (JSC::RegExpPrototype::finishCreation): (JSC::JSC_DEFINE_HOST_FUNCTION): * Source/JavaScriptCore/ucd/emoji-sequences.txt: Added. * Source/JavaScriptCore/ucd/emoji-zwj-sequences.txt: Added. * Source/JavaScriptCore/yarr/Yarr.h: * Source/JavaScriptCore/yarr/YarrErrorCode.cpp: (JSC::Yarr::errorMessage): (JSC::Yarr::errorToThrow): * Source/JavaScriptCore/yarr/YarrErrorCode.h: * Source/JavaScriptCore/yarr/YarrFlags.h: * Source/JavaScriptCore/yarr/YarrInterpreter.cpp: (JSC::Yarr::ByteTermDumper::ByteTermDumper): (JSC::Yarr::ByteTermDumper::unicode const): (JSC::Yarr::ByteTermDumper::unicodeSets const): (JSC::Yarr::ByteTermDumper::eitherUnicode const): (JSC::Yarr::Interpreter::tryConsumeBackReference): (JSC::Yarr::Interpreter::matchCharacterClass): (JSC::Yarr::Interpreter::backtrackCharacterClass): (JSC::Yarr::Interpreter::matchDisjunction): (JSC::Yarr::Interpreter::Interpreter): (JSC::Yarr::Interpreter::isLegacyCompilation const): (JSC::Yarr::Interpreter::isUnicodeCompilation const): (JSC::Yarr::Interpreter::isUnicodeSetsCompilation const): (JSC::Yarr::Interpreter::isEitherUnicodeCompilation const): (JSC::Yarr::ByteTermDumper::dumpTerm): (JSC::Yarr::ByteTermDumper::unicode): Deleted. * Source/JavaScriptCore/yarr/YarrInterpreter.h: (JSC::Yarr::BytecodePattern::BytecodePattern): (JSC::Yarr::BytecodePattern::compileMode const): (JSC::Yarr::BytecodePattern::unicodeSets const): (JSC::Yarr::BytecodePattern::eitherUnicode const): * Source/JavaScriptCore/yarr/YarrJIT.cpp: * Source/JavaScriptCore/yarr/YarrParser.h: (JSC::Yarr::Parser::CharacterClassParserDelegate::CharacterClassParserDelegate): (JSC::Yarr::Parser::ClassSetParserDelegate::ClassSetParserDelegate): (JSC::Yarr::Parser::ClassSetParserDelegate::begin): (JSC::Yarr::Parser::ClassSetParserDelegate::nestedClassBegin): (JSC::Yarr::Parser::ClassSetParserDelegate::doneAfterCharacterClassEnd): (JSC::Yarr::Parser::ClassSetParserDelegate::setUnionOp): (JSC::Yarr::Parser::ClassSetParserDelegate::setSubtractOp): (JSC::Yarr::Parser::ClassSetParserDelegate::setIntersectionOp): (JSC::Yarr::Parser::ClassSetParserDelegate::flushCachedCharacterIfNeeded): (JSC::Yarr::Parser::ClassSetParserDelegate::atomPatternCharacter): (JSC::Yarr::Parser::ClassSetParserDelegate::atomBuiltInCharacterClass): (JSC::Yarr::Parser::ClassSetParserDelegate::end): (JSC::Yarr::Parser::ClassSetParserDelegate::error): (JSC::Yarr::Parser::ClassSetParserDelegate::assertionWordBoundary): (JSC::Yarr::Parser::ClassSetParserDelegate::atomBackReference): (JSC::Yarr::Parser::ClassSetParserDelegate::atomNamedBackReference): (JSC::Yarr::Parser::ClassSetParserDelegate::atomNamedForwardReference): (JSC::Yarr::Parser::ClassStringDisjunctionParserDelegate::ClassStringDisjunctionParserDelegate): (JSC::Yarr::Parser::ClassStringDisjunctionParserDelegate::atomPatternCharacter): (JSC::Yarr::Parser::ClassStringDisjunctionParserDelegate::newAlternative): (JSC::Yarr::Parser::ClassStringDisjunctionParserDelegate::end): (JSC::Yarr::Parser::ClassStringDisjunctionParserDelegate::assertionWordBoundary): (JSC::Yarr::Parser::ClassStringDisjunctionParserDelegate::atomBackReference): (JSC::Yarr::Parser::ClassStringDisjunctionParserDelegate::atomNamedBackReference): (JSC::Yarr::Parser::ClassStringDisjunctionParserDelegate::atomNamedForwardReference): (JSC::Yarr::Parser::ClassStringDisjunctionParserDelegate::atomBuiltInCharacterClass): (JSC::Yarr::Parser::Parser): (JSC::Yarr::Parser::isIdentityEscapeAnError): (JSC::Yarr::Parser::parseEscape): (JSC::Yarr::Parser::consumePossibleSurrogatePair): (JSC::Yarr::Parser::parseAtomEscape): (JSC::Yarr::Parser::parseCharacterClassEscape): (JSC::Yarr::Parser::parseClassSetEscape): (JSC::Yarr::Parser::parseClassStringDisjunctionEscape): (JSC::Yarr::Parser::parseCharacterClass): (JSC::Yarr::Parser::parseClassSet): (JSC::Yarr::Parser::parseClassStringDisjunction): (JSC::Yarr::Parser::parseParenthesesEnd): (JSC::Yarr::Parser::parseTokens): (JSC::Yarr::Parser::handleIllegalReferences): (JSC::Yarr::Parser::tryConsumeUnicodeEscape): (JSC::Yarr::Parser::tryConsumeUnicodePropertyExpression): (JSC::Yarr::Parser::isLegacyCompilation const): (JSC::Yarr::Parser::isUnicodeCompilation const): (JSC::Yarr::Parser::isUnicodeSetsCompilation const): (JSC::Yarr::Parser::isEitherUnicodeCompilation const): (JSC::Yarr::compileMode): (JSC::Yarr::parse): * Source/JavaScriptCore/yarr/YarrPattern.cpp: (JSC::Yarr::CharacterClassConstructor::CharacterClassConstructor): (JSC::Yarr::CharacterClassConstructor::reset): (JSC::Yarr::CharacterClassConstructor::combiningSetOp): (JSC::Yarr::CharacterClassConstructor::append): (JSC::Yarr::CharacterClassConstructor::appendInverted): (JSC::Yarr::CharacterClassConstructor::putRange): (JSC::Yarr::CharacterClassConstructor::atomClassStringDisjunction): (JSC::Yarr::CharacterClassConstructor::performSetOpWith): (JSC::Yarr::CharacterClassConstructor::performSetOpWithStrings): (JSC::Yarr::CharacterClassConstructor::performSetOpWithMatches): (JSC::Yarr::CharacterClassConstructor::hasInverteStrings): (JSC::Yarr::CharacterClassConstructor::compareUTF32Strings): (JSC::Yarr::CharacterClassConstructor::sort): (JSC::Yarr::CharacterClassConstructor::charClass): (JSC::Yarr::CharacterClassConstructor::mergeRangesFrom): (JSC::Yarr::CharacterClassConstructor::unionStrings): (JSC::Yarr::CharacterClassConstructor::intersectionStrings): (JSC::Yarr::CharacterClassConstructor::subtractionStrings): (JSC::Yarr::CharacterClassConstructor::asciiOpSorted): (JSC::Yarr::CharacterClassConstructor::unicodeOpSorted): (JSC::Yarr::YarrPatternConstructor::YarrPatternConstructor): (JSC::Yarr::YarrPatternConstructor::resetForReparsing): (JSC::Yarr::YarrPatternConstructor::atomPatternCharacter): (JSC::Yarr::YarrPatternConstructor::atomBuiltInCharacterClass): (JSC::Yarr::YarrPatternConstructor::atomCharacterClassAtom): (JSC::Yarr::YarrPatternConstructor::atomCharacterClassRange): (JSC::Yarr::YarrPatternConstructor::atomCharacterClassBuiltIn): (JSC::Yarr::YarrPatternConstructor::atomClassStringDisjunction): (JSC::Yarr::YarrPatternConstructor::atomCharacterClassSetOp): (JSC::Yarr::YarrPatternConstructor::atomCharacterClassPushNested): (JSC::Yarr::YarrPatternConstructor::atomCharacterClassPopNested): (JSC::Yarr::YarrPatternConstructor::atomCharacterClassEnd): (JSC::Yarr::YarrPattern::compile): (JSC::Yarr::PatternTerm::dump): (JSC::Yarr::YarrPattern::dumpPatternString): (JSC::Yarr::YarrPattern::dumpPattern): * Source/JavaScriptCore/yarr/YarrPattern.h: (JSC::Yarr::CharacterClass::CharacterClass): (JSC::Yarr::CharacterClass::hasNonBMPCharacters const): (JSC::Yarr::CharacterClass::hasOneCharacterSize const): (JSC::Yarr::CharacterClass::hasOnlyNonBMPCharacters const): (JSC::Yarr::CharacterClass::hasStrings const): (JSC::Yarr::CharacterClass::hasSingleCharacters const): (JSC::Yarr::ClassSet::ClassSet): (JSC::Yarr::YarrPattern::unicodeSets const): (JSC::Yarr::YarrPattern::eitherUnicode const): (JSC::Yarr::YarrPattern::compileMode const): (JSC::Yarr::CharacterClass::hasNonBMPCharacters): Deleted. (JSC::Yarr::CharacterClass::hasOneCharacterSize): Deleted. (JSC::Yarr::CharacterClass::hasOnlyNonBMPCharacters): Deleted. * Source/JavaScriptCore/yarr/YarrSyntaxChecker.cpp: (JSC::Yarr::SyntaxChecker::atomClassStringDisjunction): (JSC::Yarr::SyntaxChecker::atomCharacterClassSetOp): (JSC::Yarr::SyntaxChecker::atomCharacterClassPushNested): (JSC::Yarr::SyntaxChecker::atomCharacterClassPopNested): (JSC::Yarr::checkSyntax): * Source/JavaScriptCore/yarr/YarrUnicodeProperties.cpp: (JSC::Yarr::unicodeMatchProperty): (JSC::Yarr::createUnicodeCharacterClassFor): (JSC::Yarr::characterClassMayContainStrings): * Source/JavaScriptCore/yarr/YarrUnicodeProperties.h: * Source/JavaScriptCore/yarr/generateYarrUnicodePropertyTables.py: (PropertyData.__init__): (PropertyData.addMatchString): (PropertyData.stringsCompare): (PropertyData): (PropertyData.sortStrings): (PropertyData.dumpMatchData): (PropertyData.convertStringToCppFormat): (PropertyData.dump): (PropertyData.dumpAll): (PropertyData.dumpMayContainStringFunc): (BinaryProperty.dump): (SequenceProperty): (SequenceProperty.__init__): (SequenceProperty.parsePropertyFile): (SequenceProperty.dump): * Source/WebCore/contentextensions/URLFilterParser.cpp: (WebCore::ContentExtensions::PatternParser::atomClassStringDisjunction): (WebCore::ContentExtensions::PatternParser::atomCharacterClassSetOp): (WebCore::ContentExtensions::PatternParser::atomCharacterClassPushNested): (WebCore::ContentExtensions::PatternParser::atomCharacterClassPopNested): (WebCore::ContentExtensions::URLFilterParser::addPattern): Canonical link: https://commits.webkit.org/261188@main
1 parent b085855 commit 270c824

42 files changed

Lines changed: 4679 additions & 135 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎JSTests/es6/Proxy_internal_get_calls_RegExp.prototype.flags.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ function test() {
44
var get = [];
55
var p = new Proxy({}, { get: function(o, k) { get.push(k); return o[k]; }});
66
Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get.call(p);
7-
return get + '' === "hasIndices,global,ignoreCase,multiline,dotAll,unicode,sticky";
7+
return get + '' === "hasIndices,global,ignoreCase,multiline,dotAll,unicode,unicodeSets,sticky";
88

99
}
1010

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
// With verbose set to false, this test is successful if there is no output. Set verbose to true to see expected matches.
2+
let verbose = false;
3+
4+
function arrayToString(arr)
5+
{
6+
let str = '';
7+
arr.forEach(function(v, index) {
8+
if (typeof v == "string")
9+
str += "\"" + v + "\"";
10+
else
11+
str += v;
12+
13+
if (index != (arr.length - 1))
14+
str += ',';
15+
});
16+
return str;
17+
}
18+
19+
function objectToString(obj)
20+
{
21+
let str = "";
22+
23+
firstEntry = true;
24+
25+
for (const [key, value] of Object.entries(obj)) {
26+
if (!firstEntry)
27+
str += ", ";
28+
29+
str += key + ": " + dumpValue(value);
30+
31+
firstEntry = false;
32+
}
33+
34+
return "{ " + str + " }";
35+
}
36+
37+
function dumpValue(v)
38+
{
39+
if (v === null)
40+
return "<null>";
41+
42+
if (v === undefined)
43+
return "<undefined>";
44+
45+
if (typeof v == "string")
46+
return "\"" + v + "\"";
47+
48+
let str = "";
49+
50+
if (v.length)
51+
str += arrayToString(v);
52+
53+
if (v.groups) {
54+
groupStr = objectToString(v.groups);
55+
56+
if (str.length) {
57+
if ( groupStr.length)
58+
str += ", " + groupStr;
59+
} else
60+
str = groupStr;
61+
}
62+
63+
return "[ " + str + " ]";
64+
}
65+
66+
function compareArray(expected, actual)
67+
{
68+
if (expected === null && actual === null)
69+
return true;
70+
71+
if (expected === null) {
72+
print("### expected is null, actual is not null");
73+
return false;
74+
}
75+
76+
if (actual === null) {
77+
print("### expected is not null, actual is null");
78+
return false;
79+
}
80+
81+
if (expected.length !== actual.length) {
82+
print("### expected.length: " + expected.length + ", actual.length: " + actual.length);
83+
return false;
84+
}
85+
86+
for (var i = 0; i < expected.length; i++) {
87+
if (expected[i] !== actual[i]) {
88+
print("### expected[" + i + "]: \"" + expected[i] + "\" !== actual[" + i + "]: \"" + actual[i] + "\"");
89+
return false;
90+
}
91+
}
92+
93+
return true;
94+
}
95+
96+
function compareGroups(expected, actual)
97+
{
98+
if (expected === null && actual === null)
99+
return true;
100+
101+
if (expected === null) {
102+
print("### expected group is null, actual group is not null");
103+
return false;
104+
}
105+
106+
if (actual === null) {
107+
print("### expected group is not null, actual group is null");
108+
return false;
109+
}
110+
111+
for (const key in expected) {
112+
if (expected[key] !== actual[key]) {
113+
print("### expected." + key + ": " + dumpValue(expected[key]) + " !== actual." + key + ": " + dumpValue(actual[key]));
114+
return false;
115+
}
116+
}
117+
118+
return true;
119+
}
120+
121+
let testNumber = 0;
122+
123+
function testRegExp(re, str, exp, groups)
124+
{
125+
testNumber++;
126+
127+
if (groups)
128+
exp.groups = groups;
129+
130+
let actual = re.exec(str);
131+
132+
let result = compareArray(exp, actual);;
133+
134+
if (exp && exp.groups) {
135+
if (!compareGroups(exp.groups, actual.groups))
136+
result = false;
137+
}
138+
139+
if (result) {
140+
if (verbose)
141+
print(re.toString() + ".exec(" + dumpValue(str) + "), passed ", dumpValue(exp));
142+
} else
143+
print(re.toString() + ".exec(" + dumpValue(str) + "), FAILED test #" + testNumber + ", Expected ", dumpValue(exp), " got ", dumpValue(actual));
144+
}
145+
146+
function testRegExpSyntaxError(reString, flags, expError)
147+
{
148+
testNumber++;
149+
150+
try {
151+
let re = new RegExp(reString, flags);
152+
} catch (e) {
153+
if (e != expError)
154+
print("FAILED test #" + testNumber + ", Expected /" + reString + "/" + flags + " to throw \"" + expError + "\" got \"" + e + "\"");
155+
else if (verbose)
156+
print("/" + reString + "/" + flags + " passed, it threw \"" + expError + "\" as expected");
157+
}
158+
}
159+
160+
// Test 1
161+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}a]/, "a", ["a"]);
162+
testRegExpSyntaxError("[\\p{RGI_Emoji_Tag_Sequence}a]", "u", "SyntaxError: Invalid regular expression: invalid property expression");
163+
testRegExpSyntaxError("\\P{Emoji_Keycap_Sequence}", "v", "SyntaxError: Invalid regular expression: Negated class set may contain strings");
164+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}a]/v, "a", ["a"]);
165+
testRegExp(/(?:\u{1f3f4}\u{e0067}\u{e0062}\u{e0065}\u{e006e}\u{e0067}\u{e007F}|\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}|\u{1f3f4}\u{e0067}\u{e0062}\u{e0077}\u{e006C}\u{e0073}\u{e007F}|[a])/v, "\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}", ["\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}"]);
166+
167+
// Test 6
168+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}a]/v, "\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}", ["\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}"]);
169+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}a]/v, "a", ["a"]);
170+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}\q{\u{1f1fa}\u{1f1f8}}]/v, "\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}", ["\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}"]);
171+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}\q{\u{1f1fa}\u{1f1f8}}]/v, "\u{1f1fa}\u{1f1f8}", ["\u{1f1fa}\u{1f1f8}"]);
172+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}\q{\u{1f1fa}\u{1f1f8}}]/v, "a", null);
173+
174+
// Test 11
175+
testRegExp(/[\q{\u{1f1fa}\u{1f1f8}}a]/v, "a", ["a"]);
176+
testRegExp(/[\q{\u{1f1fa}\u{1f1f8}}a]/v, "\u{1f1fa}\u{1f1f8}", ["\u{1f1fa}\u{1f1f8}"]);
177+
testRegExp(/[\q{\u{1f1fa}}\q{\u{1f1fa}\u{1f1f8}}]/v, "\u{1f1fa}\u{1f1f8}", ["\u{1f1fa}\u{1f1f8}"]);
178+
testRegExp(/[\q{\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}}\p{RGI_Emoji_Tag_Sequence}]/v, "\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}", ["\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}"]);
179+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}[\q{\u{1f1fa}\u{1f1f8}}a]]/v, "\u{1f1fa}\u{1f1f8}", ["\u{1f1fa}\u{1f1f8}"]);
180+
181+
// Test 16
182+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}[\q{\u{1f1fa}\u{1f1f8}}a]]/v, "a", ["a"]);
183+
testRegExp(/[b-z[a]]/v, "a", ["a"]);
184+
testRegExp(/[a-z--k]/v, "a", ["a"]);
185+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}--\q{\u{1F3f4}\u{e0067}\u{e0062}\u{e0077}\u{e006c}\u{e0073}\u{e007f}}]/v, "\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}", ["\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}"]);
186+
testRegExp(/[a-z\q{X}]/v, "X", ["X"]);
187+
188+
// Test 21
189+
testRegExp(/[a-z--\q{k}]/v, "a", ["a"]);
190+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}\q{\u{1f1fa}\u{1f1f8}|abc|a|\u{1f1fa}}]/v, "a", ["a"]);
191+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}--\q{\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}}]/v, "\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}", null);
192+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}--\q{\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}}]/v, "\u{1f3f4}\u{e0067}\u{e0062}\u{e0077}\u{e006c}\u{e0073}\u{e007f}", ["\u{1f3f4}\u{e0067}\u{e0062}\u{e0077}\u{e006c}\u{e0073}\u{e007f}"]);
193+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}&&\q{\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}}]/v, "\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}", ["\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}"]);
194+
195+
// Test 26
196+
testRegExp(/[\p{RGI_Emoji_Tag_Sequence}&&\q{\u{1f3f4}\u{e0067}\u{e0062}\u{e0073}\u{e0063}\u{e0074}\u{e007f}}]/v, "\u{1f3f4}\u{e0067}\u{e0062}\u{e0077}\u{e006c}\u{e0073}\u{e007f}", null);
197+
testRegExp(/[a-z&&\q{a|e|i|o|u|X|Y|Z}]/v, "a", ["a"]);
198+
testRegExp(/[\p{White_Space}&&\p{ASCII}]/v, " ", [" "]);
199+
testRegExp(/[\p{White_Space}&&\p{ASCII}]/v, "\u2028", null);
200+
testRegExp(/[\p{White_Space}--\p{ASCII}]/v, " ", null);
201+
202+
// Test 31
203+
testRegExp(/[\p{White_Space}--\p{ASCII}]/v, "\u2028", ["\u2028"]);
204+
testRegExp(/^[[0-9]&&\d]+$/v, "0", ["0"]);
205+
testRegExp(/^[_--[0-9]]+$/v, "_", ["_"]);
206+
testRegExp(/[[a-z]--[a]]/v, "a", null);
207+
testRegExp(/^\p{RGI_Emoji_Flag_Sequence}+$/v, "\u{1F1E6}\u{1F1E8}", ["\u{1F1E6}\u{1F1E8}"]);
208+
209+
// Test 36
210+
testRegExp(/^\p{RGI_Emoji_Flag_Sequence}+$/v, "\u{1F1E6}\u{1F1E8}\u{1f1e7}\u{1f1f1}", ["\u{1f1e6}\u{1f1e8}\u{1f1e7}\u{1f1f1}"]);
211+
testRegExp(/^\p{RGI_Emoji_Flag_Sequence}+$/v, "\u{1f1f9}\u{1f1ef}\u{1F1E6}\u{1F1E8}\u{1f1f9}\u{1f1f7}", ["\u{1f1f9}\u{1f1ef}\u{1F1E6}\u{1F1E8}\u{1f1f9}\u{1f1f7}"]);
212+
testRegExp(/^\p{Emoji_Keycap_Sequence}+$/v, "#\u{fe0f}\u{20e3}", ["#\u{fe0f}\u{20e3}"]);
213+
testRegExp(/^\p{RGI_Emoji}+$/v, "#\u{fe0f}\u{20e3}", ["#\u{fe0f}\u{20e3}"]);

‎JSTests/stress/static-getter-in-names.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ function shouldBe(actual, expected) {
33
throw new Error('bad value: ' + actual);
44
}
55

6-
shouldBe(JSON.stringify(Object.getOwnPropertyNames(RegExp.prototype).sort()), '["compile","constructor","dotAll","exec","flags","global","hasIndices","ignoreCase","multiline","source","sticky","test","toString","unicode"]');
6+
shouldBe(JSON.stringify(Object.getOwnPropertyNames(RegExp.prototype).sort()), '["compile","constructor","dotAll","exec","flags","global","hasIndices","ignoreCase","multiline","source","sticky","test","toString","unicode","unicodeSets"]');
77
shouldBe(JSON.stringify(Object.getOwnPropertyNames(/Cocoa/).sort()), '["lastIndex"]');

‎JSTests/test262/config.yaml‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ flags:
1111
resizable-arraybuffer: useArrayBufferTransfer
1212
skip:
1313
features:
14-
- regexp-v-flag
1514
- callable-boundary-realms
1615
- FinalizationRegistry.prototype.cleanupSome
1716
- decorators

‎LayoutTests/js/Object-getOwnPropertyNames-expected.txt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ PASS getSortedOwnPropertyNames(Number.prototype) is ['constructor', 'toExponenti
5757
PASS getSortedOwnPropertyNames(Date) is ['UTC', 'length', 'name', 'now', 'parse', 'prototype']
5858
PASS getSortedOwnPropertyNames(Date.prototype) is ['constructor', 'getDate', 'getDay', 'getFullYear', 'getHours', 'getMilliseconds', 'getMinutes', 'getMonth', 'getSeconds', 'getTime', 'getTimezoneOffset', 'getUTCDate', 'getUTCDay', 'getUTCFullYear', 'getUTCHours', 'getUTCMilliseconds', 'getUTCMinutes', 'getUTCMonth', 'getUTCSeconds', 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', 'setYear', 'toDateString', 'toGMTString', 'toISOString', 'toJSON', 'toLocaleDateString', 'toLocaleString', 'toLocaleTimeString', 'toString', 'toTimeString', 'toUTCString', 'valueOf']
5959
PASS getSortedOwnPropertyNames(RegExp) is ['$&', "$'", '$*', '$+', '$1', '$2', '$3', '$4', '$5', '$6', '$7', '$8', '$9', '$_', '$`', 'input', 'lastMatch', 'lastParen', 'leftContext', 'length', 'multiline', 'name', 'prototype', 'rightContext']
60-
PASS getSortedOwnPropertyNames(RegExp.prototype) is ['compile', 'constructor', 'dotAll', 'exec', 'flags', 'global', 'hasIndices', 'ignoreCase', 'multiline', 'source', 'sticky', 'test', 'toString', 'unicode']
60+
PASS getSortedOwnPropertyNames(RegExp.prototype) is ['compile', 'constructor', 'dotAll', 'exec', 'flags', 'global', 'hasIndices', 'ignoreCase', 'multiline', 'source', 'sticky', 'test', 'toString', 'unicode', 'unicodeSets']
6161
PASS getSortedOwnPropertyNames(Error) is ['length', 'name', 'prototype', 'stackTraceLimit']
6262
PASS getSortedOwnPropertyNames(Error.prototype) is ['constructor', 'message', 'name', 'toString']
6363
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']

‎LayoutTests/js/script-tests/Object-getOwnPropertyNames.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ var expectedPropertyNamesSet = {
6666
"Date": "['UTC', 'length', 'name', 'now', 'parse', 'prototype']",
6767
"Date.prototype": "['constructor', 'getDate', 'getDay', 'getFullYear', 'getHours', 'getMilliseconds', 'getMinutes', 'getMonth', 'getSeconds', 'getTime', 'getTimezoneOffset', 'getUTCDate', 'getUTCDay', 'getUTCFullYear', 'getUTCHours', 'getUTCMilliseconds', 'getUTCMinutes', 'getUTCMonth', 'getUTCSeconds', 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', 'setYear', 'toDateString', 'toGMTString', 'toISOString', 'toJSON', 'toLocaleDateString', 'toLocaleString', 'toLocaleTimeString', 'toString', 'toTimeString', 'toUTCString', 'valueOf']",
6868
"RegExp": "['$&', \"$'\", '$*', '$+', '$1', '$2', '$3', '$4', '$5', '$6', '$7', '$8', '$9', '$_', '$`', 'input', 'lastMatch', 'lastParen', 'leftContext', 'length', 'multiline', 'name', 'prototype', 'rightContext']",
69-
"RegExp.prototype": "['compile', 'constructor', 'dotAll', 'exec', 'flags', 'global', 'hasIndices', 'ignoreCase', 'multiline', 'source', 'sticky', 'test', 'toString', 'unicode']",
69+
"RegExp.prototype": "['compile', 'constructor', 'dotAll', 'exec', 'flags', 'global', 'hasIndices', 'ignoreCase', 'multiline', 'source', 'sticky', 'test', 'toString', 'unicode', 'unicodeSets']",
7070
"Error": "['length', 'name', 'prototype', 'stackTraceLimit']",
7171
"Error.prototype": "['constructor', 'message', 'name', 'toString']",
7272
"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']",

‎Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,7 @@
11751175
657CF45919BF6662004ACBF2 /* JSCallee.h in Headers */ = {isa = PBXBuildFile; fileRef = 657CF45719BF6662004ACBF2 /* JSCallee.h */; settings = {ATTRIBUTES = (Private, ); }; };
11761176
658824AF1E5CFDB000FB7359 /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 658824AE1E5CFDB000FB7359 /* ConfigFile.h */; settings = {ATTRIBUTES = (Private, ); }; };
11771177
658D3A5619638268003C45D6 /* VMEntryRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = 658D3A5519638268003C45D6 /* VMEntryRecord.h */; settings = {ATTRIBUTES = (Private, ); }; };
1178+
6592B74929AC7C670040308E /* UnicodePatternTables.h in Headers */ = {isa = PBXBuildFile; fileRef = 6592B74829AC7C670040308E /* UnicodePatternTables.h */; };
11781179
659CDA5B1F6753F200D3E53F /* YarrUnicodeProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = 659CDA5A1F67509800D3E53F /* YarrUnicodeProperties.h */; settings = {ATTRIBUTES = (Private, ); }; };
11791180
65B8392E1BACAD360044E824 /* CachedRecovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 65B8392C1BACA92A0044E824 /* CachedRecovery.h */; };
11801181
6A38CFAA1E32B5AB0060206F /* AsyncStackTrace.h in Headers */ = {isa = PBXBuildFile; fileRef = 6A38CFA81E32B58B0060206F /* AsyncStackTrace.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -4267,6 +4268,7 @@
42674268
658824AE1E5CFDB000FB7359 /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigFile.h; sourceTree = "<group>"; };
42684269
658824B01E5CFDF400FB7359 /* ConfigFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConfigFile.cpp; sourceTree = "<group>"; };
42694270
658D3A5519638268003C45D6 /* VMEntryRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = VMEntryRecord.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
4271+
6592B74829AC7C670040308E /* UnicodePatternTables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = UnicodePatternTables.h; path = ../../../../../../WebKitBuild/Debug/DerivedSources/JavaScriptCore/UnicodePatternTables.h; sourceTree = "<group>"; };
42704272
65987F2C167FE84B003C2F8D /* DFGOSRExitCompilationInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGOSRExitCompilationInfo.h; path = dfg/DFGOSRExitCompilationInfo.h; sourceTree = "<group>"; };
42714273
659CDA591F67509800D3E53F /* YarrUnicodeProperties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YarrUnicodeProperties.cpp; path = yarr/YarrUnicodeProperties.cpp; sourceTree = "<group>"; };
42724274
659CDA5A1F67509800D3E53F /* YarrUnicodeProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YarrUnicodeProperties.h; path = yarr/YarrUnicodeProperties.h; sourceTree = "<group>"; };
@@ -7434,6 +7436,7 @@
74347436
F6D67D4526F9130A006E0349 /* TemporalInstantConstructor.lut.h */,
74357437
F6D67D4626F9130B006E0349 /* TemporalInstantPrototype.lut.h */,
74367438
F6F150202693D450004B98EF /* TemporalObject.lut.h */,
7439+
6592B74829AC7C670040308E /* UnicodePatternTables.h */,
74377440
533B15DE1DC7F463004D500A /* WasmOps.h */,
74387441
AD2FCC0A1DB59C5900B3E736 /* WebAssemblyCompileErrorConstructor.lut.h */,
74397442
AD2FCC0B1DB59C5900B3E736 /* WebAssemblyCompileErrorPrototype.lut.h */,
@@ -11366,6 +11369,7 @@
1136611369
A7A8AF4217ADB5F3005AB174 /* Uint32Array.h in Headers */,
1136711370
A7A8AF3F17ADB5F3005AB174 /* Uint8Array.h in Headers */,
1136811371
A7A8AF4017ADB5F3005AB174 /* Uint8ClampedArray.h in Headers */,
11372+
6592B74929AC7C670040308E /* UnicodePatternTables.h in Headers */,
1136911373
A7B601821639FD2A00372BA3 /* UnlinkedCodeBlock.h in Headers */,
1137011374
E32C3C6923E94C1E00BC97C0 /* UnlinkedCodeBlockGenerator.h in Headers */,
1137111375
14AD91241DCA9FA40014F9FE /* UnlinkedEvalCodeBlock.h in Headers */,

‎Source/JavaScriptCore/builtins/BuiltinNames.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ namespace JSC {
166166
macro(regExpProtoSourceGetter) \
167167
macro(regExpProtoStickyGetter) \
168168
macro(regExpProtoUnicodeGetter) \
169+
macro(regExpProtoUnicodeSetsGetter) \
169170
macro(regExpPrototypeSymbolMatch) \
170171
macro(regExpPrototypeSymbolReplace) \
171172
macro(regExpSearchFast) \

‎Source/JavaScriptCore/builtins/RegExpPrototype.js‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ function hasObservableSideEffectsForRegExpMatch(regexp)
9595
if (regexpUnicode !== @regExpProtoUnicodeGetter)
9696
return true;
9797

98+
var regexpUnicodeSets = @tryGetById(regexp, "unicodeSets");
99+
if (regexpUnicodeSets !== @regExpProtoUnicodeSetsGetter)
100+
return true;
101+
98102
return typeof regexp.lastIndex !== "number";
99103
}
100104

@@ -460,7 +464,10 @@ function hasObservableSideEffectsForRegExpSplit(regexp)
460464
var regexpUnicode = @tryGetById(regexp, "unicode");
461465
if (regexpUnicode !== @regExpProtoUnicodeGetter)
462466
return true;
463-
467+
var regexpUnicodeSets = @tryGetById(regexp, "unicodeSets");
468+
if (regexpUnicodeSets !== @regExpProtoUnicodeSetsGetter)
469+
return true;
470+
464471
// These are accessed by the RegExp species constructor.
465472
var regexpSource = @tryGetById(regexp, "source");
466473
if (regexpSource !== @regExpProtoSourceGetter)
@@ -496,9 +503,9 @@ function split(string, limit)
496503
// 5. Let flags be ? ToString(? Get(rx, "flags")).
497504
var flags = @toString(regexp.flags);
498505

499-
// 6. If flags contains "u", var unicodeMatching be true.
506+
// 6. If flags contains "u" or flags contains "v", var unicodeMatching be true.
500507
// 7. Else, let unicodeMatching be false.
501-
var unicodeMatching = @stringIncludesInternal.@call(flags, "u");
508+
var unicodeMatching = @stringIncludesInternal.@call(flags, "u") || @stringIncludesInternal.@call(flags, "v");
502509
// 8. If flags contains "y", var newFlags be flags.
503510
// 9. Else, let newFlags be the string that is the concatenation of flags and "y".
504511
var newFlags = @stringIncludesInternal.@call(flags, "y") ? flags : flags + "y";

‎Source/JavaScriptCore/builtins/StringPrototype.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ function hasObservableSideEffectsForStringReplace(regexp, replacer)
234234
if (regexpUnicode !== @regExpProtoUnicodeGetter)
235235
return true;
236236

237+
var regexpUnicodeSets = @tryGetById(regexp, "unicodeSets");
238+
if (regexpUnicodeSets !== @regExpProtoUnicodeSetsGetter)
239+
return true;
240+
237241
return typeof regexp.lastIndex !== "number";
238242
}
239243

0 commit comments

Comments
 (0)