Invalid flags in a RegExp literal should be an early SyntaxError
authorross.kirsling@sony.com <ross.kirsling@sony.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Mar 2019 06:20:53 +0000 (06:20 +0000)
committerross.kirsling@sony.com <ross.kirsling@sony.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 11 Mar 2019 06:20:53 +0000 (06:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=195514

Reviewed by Darin Adler.

JSTests:

* test262/expectations.yaml:
Mark 4 test cases as passing.

* stress/regexp-syntax-error-invalid-flags.js:
* stress/regress-161995.js: Removed.
Update existing test, merging in an older test for the same behavior.

Source/JavaScriptCore:

Currently we're throwing a *runtime* SyntaxError; this should occur at parse time.

  12.2.8.1 Static Semantics: Early Errors
    PrimaryExpression : RegularExpressionLiteral
      - It is a Syntax Error if BodyText of RegularExpressionLiteral cannot be recognized
        using the goal symbol Pattern of the ECMAScript RegExp grammar specified in 21.2.1.
      - It is a Syntax Error if FlagText of RegularExpressionLiteral contains any code points
        other than "g", "i", "m",  "s", "u", or "y", or if it contains the same code point more than once.

In fixing this, let's also move flag handling from runtime/ to yarr/.

* yarr/YarrSyntaxChecker.cpp:
(JSC::Yarr::checkSyntax):
Check flags before checking pattern.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* bytecompiler/NodesCodegen.cpp:
(JSC::RegExpNode::emitBytecode):
* inspector/ContentSearchUtilities.cpp:
(Inspector::ContentSearchUtilities::findMagicComment):
* runtime/CachedTypes.cpp:
* runtime/RegExp.cpp:
(JSC::RegExp::RegExp):
(JSC::RegExp::createWithoutCaching):
(JSC::RegExp::create):
(JSC::regExpFlags): Deleted.
* runtime/RegExp.h:
* runtime/RegExpCache.cpp:
(JSC::RegExpCache::lookupOrCreate):
(JSC::RegExpCache::ensureEmptyRegExpSlow):
* runtime/RegExpCache.h:
* runtime/RegExpConstructor.cpp:
(JSC::toFlags):
(JSC::regExpCreate):
(JSC::constructRegExp):
* runtime/RegExpKey.h:
(JSC::RegExpKey::RegExpKey):
(WTF::HashTraits<JSC::RegExpKey>::constructDeletedValue):
(WTF::HashTraits<JSC::RegExpKey>::isDeletedValue):
(): Deleted.
* runtime/RegExpPrototype.cpp:
(JSC::regExpProtoFuncCompile):
* testRegExp.cpp:
(parseRegExpLine):
* yarr/RegularExpression.cpp:
(JSC::Yarr::RegularExpression::Private::compile):
* yarr/YarrFlags.cpp: Added.
(JSC::Yarr::parseFlags):
* yarr/YarrFlags.h: Added.
* yarr/YarrInterpreter.h:
(JSC::Yarr::BytecodePattern::ignoreCase const):
(JSC::Yarr::BytecodePattern::multiline const):
(JSC::Yarr::BytecodePattern::sticky const):
(JSC::Yarr::BytecodePattern::unicode const):
(JSC::Yarr::BytecodePattern::dotAll const):
* yarr/YarrPattern.cpp:
(JSC::Yarr::YarrPattern::compile):
(JSC::Yarr::YarrPattern::YarrPattern):
(JSC::Yarr::YarrPattern::dumpPattern):
* yarr/YarrPattern.h:
(JSC::Yarr::YarrPattern::global const):
(JSC::Yarr::YarrPattern::ignoreCase const):
(JSC::Yarr::YarrPattern::multiline const):
(JSC::Yarr::YarrPattern::sticky const):
(JSC::Yarr::YarrPattern::unicode const):
(JSC::Yarr::YarrPattern::dotAll const):
Move flag handling to Yarr and modernize API.

Source/WebCore:

* bindings/js/SerializedScriptValue.cpp:
(WebCore::CloneDeserializer::readTerminal):
Consume YarrFlags.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@242699 268f45cc-cd09-0410-ab3c-d52691b4dbfc

28 files changed:
JSTests/ChangeLog
JSTests/stress/regexp-syntax-error-invalid-flags.js
JSTests/stress/regress-161995.js [deleted file]
JSTests/test262/expectations.yaml
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/inspector/ContentSearchUtilities.cpp
Source/JavaScriptCore/runtime/CachedTypes.cpp
Source/JavaScriptCore/runtime/RegExp.cpp
Source/JavaScriptCore/runtime/RegExp.h
Source/JavaScriptCore/runtime/RegExpCache.cpp
Source/JavaScriptCore/runtime/RegExpCache.h
Source/JavaScriptCore/runtime/RegExpConstructor.cpp
Source/JavaScriptCore/runtime/RegExpKey.h
Source/JavaScriptCore/runtime/RegExpPrototype.cpp
Source/JavaScriptCore/testRegExp.cpp
Source/JavaScriptCore/yarr/RegularExpression.cpp
Source/JavaScriptCore/yarr/YarrFlags.cpp [new file with mode: 0644]
Source/JavaScriptCore/yarr/YarrFlags.h [new file with mode: 0644]
Source/JavaScriptCore/yarr/YarrInterpreter.h
Source/JavaScriptCore/yarr/YarrPattern.cpp
Source/JavaScriptCore/yarr/YarrPattern.h
Source/JavaScriptCore/yarr/YarrSyntaxChecker.cpp
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/SerializedScriptValue.cpp

index 52b6a7b..f3dea19 100644 (file)
@@ -1,3 +1,17 @@
+2019-03-10  Ross Kirsling  <ross.kirsling@sony.com>
+
+        Invalid flags in a RegExp literal should be an early SyntaxError
+        https://bugs.webkit.org/show_bug.cgi?id=195514
+
+        Reviewed by Darin Adler.
+
+        * test262/expectations.yaml:
+        Mark 4 test cases as passing.
+
+        * stress/regexp-syntax-error-invalid-flags.js:
+        * stress/regress-161995.js: Removed.
+        Update existing test, merging in an older test for the same behavior.
+
 2019-03-08  Mark Lam  <mark.lam@apple.com>
 
         Stack overflow crash in JSC::JSObject::hasInstance.
index a4961ba..7787107 100644 (file)
@@ -1,23 +1,17 @@
-function shouldThrow(func, errorMessage) {
-    var errorThrown = false;
-    var error = null;
+function shouldThrowSyntaxError(script) {
+    let error;
     try {
-        func();
+        eval(script);
     } catch (e) {
-        errorThrown = true;
         error = e;
     }
-    if (!errorThrown)
+
+    if (!error)
         throw new Error('not thrown');
-    if (String(error) !== errorMessage)
+    if (String(error) !== 'SyntaxError: Invalid regular expression: invalid flags')
         throw new Error(`bad error: ${String(error)}`);
 }
 
-function test()
-{
-    return /Hello/cocoa;
-}
-noInline(test);
-
-for (var i = 0; i < 1e4; ++i)
-    shouldThrow(test, `SyntaxError: Invalid regular expression: invalid flags`);
+shouldThrowSyntaxError('/Hello/cocoa');
+shouldThrowSyntaxError('/a/Z');
+shouldThrowSyntaxError('/./ii');
diff --git a/JSTests/stress/regress-161995.js b/JSTests/stress/regress-161995.js
deleted file mode 100644 (file)
index 604e939..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-// Regression test for 161995.
-
-function testStatic()
-{
-    return /a/Z;
-}
-
-try {
-    testStatic();
-    throw "Expected a SyntaxEerror for bad RegExp flags, but didn't get one.";
-} catch(e) {
-    if (e != "SyntaxError: Invalid regular expression: invalid flags")
-        throw "Incorrect exception for bad RegExp flags.  Got: " + e;
-}
index d3267b7..10a0aa8 100644 (file)
@@ -2396,12 +2396,6 @@ test/language/literals/numeric/numeric-separator-literal-sign-minus-dds-nsl-dd.j
 test/language/literals/numeric/numeric-separator-literal-sign-plus-dds-nsl-dd.js:
   default: 'SyntaxError: No identifiers allowed directly after numeric literal'
   strict mode: 'SyntaxError: No identifiers allowed directly after numeric literal'
-test/language/literals/regexp/early-err-bad-flag.js:
-  default: 'Test262: This statement should not be evaluated.'
-  strict mode: 'Test262: This statement should not be evaluated.'
-test/language/literals/regexp/early-err-dup-flag.js:
-  default: 'Test262: This statement should not be evaluated.'
-  strict mode: 'Test262: This statement should not be evaluated.'
 test/language/literals/regexp/named-groups/invalid-dangling-groupname-2-u.js:
   default: 'Test262: This statement should not be evaluated.'
   strict mode: 'Test262: This statement should not be evaluated.'
index 878eaf2..f03d0ad 100644 (file)
@@ -1000,6 +1000,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     yarr/RegularExpression.h
     yarr/Yarr.h
     yarr/YarrErrorCode.h
+    yarr/YarrFlags.h
     yarr/YarrInterpreter.h
     yarr/YarrJIT.h
     yarr/YarrParser.h
index be78ec5..142fa4b 100644 (file)
@@ -1,3 +1,80 @@
+2019-03-10  Ross Kirsling  <ross.kirsling@sony.com>
+
+        Invalid flags in a RegExp literal should be an early SyntaxError
+        https://bugs.webkit.org/show_bug.cgi?id=195514
+
+        Reviewed by Darin Adler.
+
+        Currently we're throwing a *runtime* SyntaxError; this should occur at parse time. 
+
+          12.2.8.1 Static Semantics: Early Errors
+            PrimaryExpression : RegularExpressionLiteral
+              - It is a Syntax Error if BodyText of RegularExpressionLiteral cannot be recognized
+                using the goal symbol Pattern of the ECMAScript RegExp grammar specified in 21.2.1.
+              - It is a Syntax Error if FlagText of RegularExpressionLiteral contains any code points
+                other than "g", "i", "m",  "s", "u", or "y", or if it contains the same code point more than once.
+
+        In fixing this, let's also move flag handling from runtime/ to yarr/.
+
+        * yarr/YarrSyntaxChecker.cpp:
+        (JSC::Yarr::checkSyntax):
+        Check flags before checking pattern.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::RegExpNode::emitBytecode):
+        * inspector/ContentSearchUtilities.cpp:
+        (Inspector::ContentSearchUtilities::findMagicComment):
+        * runtime/CachedTypes.cpp:
+        * runtime/RegExp.cpp:
+        (JSC::RegExp::RegExp):
+        (JSC::RegExp::createWithoutCaching):
+        (JSC::RegExp::create):
+        (JSC::regExpFlags): Deleted.
+        * runtime/RegExp.h:
+        * runtime/RegExpCache.cpp:
+        (JSC::RegExpCache::lookupOrCreate):
+        (JSC::RegExpCache::ensureEmptyRegExpSlow):
+        * runtime/RegExpCache.h:
+        * runtime/RegExpConstructor.cpp:
+        (JSC::toFlags):
+        (JSC::regExpCreate):
+        (JSC::constructRegExp):
+        * runtime/RegExpKey.h:
+        (JSC::RegExpKey::RegExpKey):
+        (WTF::HashTraits<JSC::RegExpKey>::constructDeletedValue):
+        (WTF::HashTraits<JSC::RegExpKey>::isDeletedValue):
+        (): Deleted.
+        * runtime/RegExpPrototype.cpp:
+        (JSC::regExpProtoFuncCompile):
+        * testRegExp.cpp:
+        (parseRegExpLine):
+        * yarr/RegularExpression.cpp:
+        (JSC::Yarr::RegularExpression::Private::compile):
+        * yarr/YarrFlags.cpp: Added.
+        (JSC::Yarr::parseFlags):
+        * yarr/YarrFlags.h: Added.
+        * yarr/YarrInterpreter.h:
+        (JSC::Yarr::BytecodePattern::ignoreCase const):
+        (JSC::Yarr::BytecodePattern::multiline const):
+        (JSC::Yarr::BytecodePattern::sticky const):
+        (JSC::Yarr::BytecodePattern::unicode const):
+        (JSC::Yarr::BytecodePattern::dotAll const):
+        * yarr/YarrPattern.cpp:
+        (JSC::Yarr::YarrPattern::compile):
+        (JSC::Yarr::YarrPattern::YarrPattern):
+        (JSC::Yarr::YarrPattern::dumpPattern):
+        * yarr/YarrPattern.h:
+        (JSC::Yarr::YarrPattern::global const):
+        (JSC::Yarr::YarrPattern::ignoreCase const):
+        (JSC::Yarr::YarrPattern::multiline const):
+        (JSC::Yarr::YarrPattern::sticky const):
+        (JSC::Yarr::YarrPattern::unicode const):
+        (JSC::Yarr::YarrPattern::dotAll const):
+        Move flag handling to Yarr and modernize API.
+
 2019-03-09  Robin Morisset  <rmorisset@apple.com>
 
         Compilation can be shrunk by 8 bytes
index e48a64b..bb19f60 100644 (file)
                A1D792FD1B43864B004516F5 /* IntlNumberFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = A1D792F71B43864B004516F5 /* IntlNumberFormat.h */; };
                A1D792FF1B43864B004516F5 /* IntlNumberFormatConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = A1D792F91B43864B004516F5 /* IntlNumberFormatConstructor.h */; };
                A1D793011B43864B004516F5 /* IntlNumberFormatPrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = A1D792FB1B43864B004516F5 /* IntlNumberFormatPrototype.h */; };
+               A3FF9BC72234749100B1A9AB /* YarrFlags.h in Headers */ = {isa = PBXBuildFile; fileRef = A3FF9BC52234746600B1A9AB /* YarrFlags.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A503FA1A188E0FB000110F14 /* JavaScriptCallFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = A503FA14188E0FAF00110F14 /* JavaScriptCallFrame.h */; };
                A503FA1E188E0FB000110F14 /* JSJavaScriptCallFramePrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = A503FA18188E0FB000110F14 /* JSJavaScriptCallFramePrototype.h */; };
                A503FA21188EFF6800110F14 /* ScriptBreakpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = A503FA1F188EFF6800110F14 /* ScriptBreakpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A1E0451B1C25B4B100BB663C /* StringPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = StringPrototype.js; sourceTree = "<group>"; };
                A1FE1EB01C2C537E00A289FF /* DatePrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = DatePrototype.js; sourceTree = "<group>"; };
                A27958D7FA1142B0AC9E364D /* WasmContextInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmContextInlines.h; sourceTree = "<group>"; };
+               A3FF9BC52234746600B1A9AB /* YarrFlags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = YarrFlags.h; path = yarr/YarrFlags.h; sourceTree = "<group>"; };
+               A3FF9BC62234746600B1A9AB /* YarrFlags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = YarrFlags.cpp; path = yarr/YarrFlags.cpp; sourceTree = "<group>"; };
                A503FA13188E0FAF00110F14 /* JavaScriptCallFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JavaScriptCallFrame.cpp; sourceTree = "<group>"; };
                A503FA14188E0FAF00110F14 /* JavaScriptCallFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JavaScriptCallFrame.h; sourceTree = "<group>"; };
                A503FA15188E0FB000110F14 /* JSJavaScriptCallFrame.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSJavaScriptCallFrame.cpp; sourceTree = "<group>"; };
                                65C6BEE021128C3B006849C3 /* YarrDisassembler.h */,
                                E3282BB91FE930A300EDAF71 /* YarrErrorCode.cpp */,
                                E3282BBA1FE930A400EDAF71 /* YarrErrorCode.h */,
+                               A3FF9BC62234746600B1A9AB /* YarrFlags.cpp */,
+                               A3FF9BC52234746600B1A9AB /* YarrFlags.h */,
                                86704B7D12DBA33700A9FE7B /* YarrInterpreter.cpp */,
                                86704B7E12DBA33700A9FE7B /* YarrInterpreter.h */,
                                86704B7F12DBA33700A9FE7B /* YarrJIT.cpp */,
                                9959E92E1BD17FA4001AA413 /* xxd.pl in Headers */,
                                451539B912DC994500EF7AC4 /* Yarr.h in Headers */,
                                E3282BBB1FE930AF00EDAF71 /* YarrErrorCode.h in Headers */,
+                               A3FF9BC72234749100B1A9AB /* YarrFlags.h in Headers */,
                                86704B8512DBA33700A9FE7B /* YarrInterpreter.h in Headers */,
                                86704B8712DBA33700A9FE7B /* YarrJIT.h in Headers */,
                                86704B8812DBA33700A9FE7B /* YarrParser.h in Headers */,
index 60fcff0..b7c8e24 100644 (file)
@@ -1045,6 +1045,7 @@ yarr/RegularExpression.cpp
 yarr/YarrCanonicalizeUCS2.cpp
 yarr/YarrDisassembler.cpp
 yarr/YarrErrorCode.cpp
+yarr/YarrFlags.cpp
 yarr/YarrInterpreter.cpp
 yarr/YarrJIT.cpp
 yarr/YarrPattern.cpp
index df9dfd8..f07592b 100644 (file)
@@ -41,6 +41,7 @@
 #include "Lexer.h"
 #include "Parser.h"
 #include "StackAlignment.h"
+#include "YarrFlags.h"
 #include <wtf/Assertions.h>
 #include <wtf/Threading.h>
 #include <wtf/text/StringBuilder.h>
@@ -141,9 +142,13 @@ RegisterID* RegExpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* d
 {
     if (dst == generator.ignoredResult())
         return nullptr;
-    RegExp* regExp = RegExp::create(*generator.vm(), m_pattern.string(), regExpFlags(m_flags.string()));
+
+    auto flags = Yarr::parseFlags(m_flags.string());
+    ASSERT(flags.hasValue());
+    RegExp* regExp = RegExp::create(*generator.vm(), m_pattern.string(), flags.value());
     if (regExp->isValid())
         return generator.emitNewRegExp(generator.finalDestination(dst), regExp);
+
     const char* messageCharacters = regExp->errorMessage();
     const Identifier& message = generator.parserArena().identifierArena().makeIdentifier(generator.vm(), bitwise_cast<const LChar*>(messageCharacters), strlen(messageCharacters));
     generator.emitThrowStaticError(ErrorType::SyntaxError, message);
index dd878ff..c8de078 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "RegularExpression.h"
 #include "Yarr.h"
+#include "YarrFlags.h"
 #include "YarrInterpreter.h"
 #include <wtf/BumpPointerAllocator.h>
 #include <wtf/StdLibExtras.h>
@@ -167,7 +168,7 @@ static String findMagicComment(const String& content, const String& patternStrin
         return String();
 
     JSC::Yarr::ErrorCode error { JSC::Yarr::ErrorCode::NoError };
-    YarrPattern pattern(patternString, JSC::RegExpFlags::FlagMultiline, error);
+    YarrPattern pattern(patternString, JSC::Yarr::Flags::Multiline, error);
     ASSERT(!hasError(error));
     BumpPointerAllocator regexAllocator;
     auto bytecodePattern = byteCompile(pattern, &regexAllocator);
index 0344869..e6df5d3 100644 (file)
@@ -28,7 +28,6 @@
 
 #include "BytecodeCacheVersion.h"
 #include "BytecodeLivenessAnalysis.h"
-#include "JSCast.h"
 #include "JSImmutableButterfly.h"
 #include "JSTemplateObjectDescriptor.h"
 #include "ScopedArgumentsTable.h"
 #include "UnlinkedModuleProgramCodeBlock.h"
 #include "UnlinkedProgramCodeBlock.h"
 #include <wtf/FastMalloc.h>
-#include <wtf/Forward.h>
 #include <wtf/Optional.h>
 #include <wtf/UUID.h>
 #include <wtf/text/AtomicStringImpl.h>
 
 namespace JSC {
 
+namespace Yarr {
+enum class Flags : uint8_t;
+}
+
 template <typename T, typename = void>
 struct SourceTypeImpl {
     using type = T;
@@ -1111,7 +1113,7 @@ public:
 
 private:
     CachedString m_patternString;
-    RegExpFlags m_flags;
+    OptionSet<Yarr::Flags> m_flags;
 };
 
 class CachedTemplateObjectDescriptor : public CachedObject<TemplateObjectDescriptor> {
index 05de498..93ad2e9 100644 (file)
@@ -28,7 +28,6 @@
 #include "JSCInlines.h"
 #include "RegExpCache.h"
 #include "RegExpInlines.h"
-#include "Yarr.h"
 #include "YarrJIT.h"
 #include <wtf/Assertions.h>
 
@@ -36,56 +35,6 @@ namespace JSC {
 
 const ClassInfo RegExp::s_info = { "RegExp", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(RegExp) };
 
-RegExpFlags regExpFlags(const String& string)
-{
-    RegExpFlags flags = NoFlags;
-
-    for (unsigned i = 0; i < string.length(); ++i) {
-        switch (string[i]) {
-        case 'g':
-            if (flags & FlagGlobal)
-                return InvalidFlags;
-            flags = static_cast<RegExpFlags>(flags | FlagGlobal);
-            break;
-
-        case 'i':
-            if (flags & FlagIgnoreCase)
-                return InvalidFlags;
-            flags = static_cast<RegExpFlags>(flags | FlagIgnoreCase);
-            break;
-
-        case 'm':
-            if (flags & FlagMultiline)
-                return InvalidFlags;
-            flags = static_cast<RegExpFlags>(flags | FlagMultiline);
-            break;
-
-        case 's':
-            if (flags & FlagDotAll)
-                return InvalidFlags;
-            flags = static_cast<RegExpFlags>(flags | FlagDotAll);
-            break;
-            
-        case 'u':
-            if (flags & FlagUnicode)
-                return InvalidFlags;
-            flags = static_cast<RegExpFlags>(flags | FlagUnicode);
-            break;
-                
-        case 'y':
-            if (flags & FlagSticky)
-                return InvalidFlags;
-            flags = static_cast<RegExpFlags>(flags | FlagSticky);
-            break;
-
-        default:
-            return InvalidFlags;
-        }
-    }
-
-    return flags;
-}
-
 #if REGEXP_FUNC_TEST_DATA_GEN
 const char* const RegExpFunctionalTestCollector::s_fileName = "/tmp/RegExpTestsData";
 RegExpFunctionalTestCollector* RegExpFunctionalTestCollector::s_instance = 0;
@@ -210,11 +159,12 @@ void RegExpFunctionalTestCollector::outputEscapedString(const String& s, bool es
 }
 #endif
 
-RegExp::RegExp(VM& vm, const String& patternString, RegExpFlags flags)
+RegExp::RegExp(VM& vm, const String& patternString, OptionSet<Yarr::Flags> flags)
     : JSCell(vm, vm.regExpStructure.get())
     , m_patternString(patternString)
     , m_flags(flags)
 {
+    ASSERT(m_flags != Yarr::Flags::DeletedValue);
 }
 
 void RegExp::finishCreation(VM& vm)
@@ -249,14 +199,14 @@ size_t RegExp::estimatedSize(JSCell* cell, VM& vm)
     return Base::estimatedSize(cell, vm) + regexDataSize;
 }
 
-RegExp* RegExp::createWithoutCaching(VM& vm, const String& patternString, RegExpFlags flags)
+RegExp* RegExp::createWithoutCaching(VM& vm, const String& patternString, OptionSet<Yarr::Flags> flags)
 {
     RegExp* regExp = new (NotNull, allocateCell<RegExp>(vm.heap)) RegExp(vm, patternString, flags);
     regExp->finishCreation(vm);
     return regExp;
 }
 
-RegExp* RegExp::create(VM& vm, const String& patternString, RegExpFlags flags)
+RegExp* RegExp::create(VM& vm, const String& patternString, OptionSet<Yarr::Flags> flags)
 {
     return vm.regExpCache()->lookupOrCreate(patternString, flags);
 }
index 03add45..35b7a4b 100644 (file)
@@ -38,8 +38,6 @@ namespace JSC {
 struct RegExpRepresentation;
 class VM;
 
-JS_EXPORT_PRIVATE RegExpFlags regExpFlags(const String&);
-
 class RegExp final : public JSCell {
     friend class CachedRegExp;
 
@@ -47,23 +45,23 @@ public:
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
 
-    JS_EXPORT_PRIVATE static RegExp* create(VM&, const String& pattern, RegExpFlags);
+    JS_EXPORT_PRIVATE static RegExp* create(VM&, const String& pattern, OptionSet<Yarr::Flags>);
     static const bool needsDestruction = true;
     static void destroy(JSCell*);
     static size_t estimatedSize(JSCell*, VM&);
     JS_EXPORT_PRIVATE static void dumpToStream(const JSCell*, PrintStream&);
 
-    bool global() const { return m_flags & FlagGlobal; }
-    bool ignoreCase() const { return m_flags & FlagIgnoreCase; }
-    bool multiline() const { return m_flags & FlagMultiline; }
-    bool sticky() const { return m_flags & FlagSticky; }
+    bool global() const { return m_flags.contains(Yarr::Flags::Global); }
+    bool ignoreCase() const { return m_flags.contains(Yarr::Flags::IgnoreCase); }
+    bool multiline() const { return m_flags.contains(Yarr::Flags::Multiline); }
+    bool sticky() const { return m_flags.contains(Yarr::Flags::Sticky); }
     bool globalOrSticky() const { return global() || sticky(); }
-    bool unicode() const { return m_flags & FlagUnicode; }
-    bool dotAll() const { return m_flags & FlagDotAll; }
+    bool unicode() const { return m_flags.contains(Yarr::Flags::Unicode); }
+    bool dotAll() const { return m_flags.contains(Yarr::Flags::DotAll); }
 
     const String& pattern() const { return m_patternString; }
 
-    bool isValid() const { return !Yarr::hasError(m_constructionErrorCode) && m_flags != InvalidFlags; }
+    bool isValid() const { return !Yarr::hasError(m_constructionErrorCode); }
     const char* errorMessage() const { return Yarr::errorMessage(m_constructionErrorCode); }
     JSObject* errorToThrow(ExecState* exec) { return Yarr::errorToThrow(exec, m_constructionErrorCode); }
     void reset()
@@ -136,9 +134,9 @@ protected:
 
 private:
     friend class RegExpCache;
-    RegExp(VM&, const String&, RegExpFlags);
+    RegExp(VM&, const String&, OptionSet<Yarr::Flags>);
 
-    static RegExp* createWithoutCaching(VM&, const String&, RegExpFlags);
+    static RegExp* createWithoutCaching(VM&, const String&, OptionSet<Yarr::Flags>);
 
     enum RegExpState : uint8_t {
         ParseError,
@@ -161,7 +159,7 @@ private:
 
     String m_patternString;
     RegExpState m_state { NotCompiled };
-    RegExpFlags m_flags;
+    OptionSet<Yarr::Flags> m_flags;
     ConcurrentJSLock m_lock;
     Yarr::ErrorCode m_constructionErrorCode { Yarr::ErrorCode::NoError };
     unsigned m_numSubpatterns { 0 };
index d76bb39..6308695 100644 (file)
@@ -35,7 +35,7 @@
 
 namespace JSC {
 
-RegExp* RegExpCache::lookupOrCreate(const String& patternString, RegExpFlags flags)
+RegExp* RegExpCache::lookupOrCreate(const String& patternString, OptionSet<Yarr::Flags> flags)
 {
     RegExpKey key(flags, patternString);
     if (RegExp* regExp = m_weakCache.get(key))
@@ -58,7 +58,7 @@ RegExpCache::RegExpCache(VM* vm)
 
 RegExp* RegExpCache::ensureEmptyRegExpSlow(VM& vm)
 {
-    RegExp* regExp = RegExp::create(vm, "", NoFlags);
+    RegExp* regExp = RegExp::create(vm, "", { });
     m_emptyRegExp.set(vm, regExp);
     return regExp;
 }
index bbda63f..083a111 100644 (file)
 
 namespace JSC {
 
+namespace Yarr {
+enum class Flags : uint8_t;
+}
+
 class RegExpCache : private WeakHandleOwner {
     WTF_MAKE_FAST_ALLOCATED;
 
@@ -63,7 +67,7 @@ private:
 
     RegExp* ensureEmptyRegExpSlow(VM&);
 
-    RegExp* lookupOrCreate(const WTF::String& patternString, RegExpFlags);
+    RegExp* lookupOrCreate(const WTF::String& patternString, OptionSet<Yarr::Flags>);
     void addToStrongCache(RegExp*);
     RegExpCacheMap m_weakCache; // Holds all regular expressions currently live.
     int m_nextEntryInStrongCache;
index 88a6331..6da496f 100644 (file)
 
 #include "Error.h"
 #include "GetterSetter.h"
-#include "JSCInlines.h"
 #include "RegExpGlobalDataInlines.h"
 #include "RegExpPrototype.h"
 #include "StructureInlines.h"
+#include "YarrFlags.h"
 
 namespace JSC {
 
@@ -177,23 +177,22 @@ inline Structure* getRegExpStructure(ExecState* exec, JSGlobalObject* globalObje
     return structure;
 }
 
-inline RegExpFlags toFlags(ExecState* exec, JSValue flags)
+inline OptionSet<Yarr::Flags> toFlags(ExecState* exec, JSValue flags)
 {
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
     if (flags.isUndefined())
-        return NoFlags;
-    JSString* flagsString = flags.toStringOrNull(exec);
-    EXCEPTION_ASSERT(!!scope.exception() == !flagsString);
-    if (UNLIKELY(!flagsString))
-        return InvalidFlags;
-
-    RegExpFlags result = regExpFlags(flagsString->value(exec));
-    RETURN_IF_EXCEPTION(scope, InvalidFlags);
-    if (result == InvalidFlags)
+        return { };
+    
+    auto result = Yarr::parseFlags(flags.toWTFString(exec));
+    RETURN_IF_EXCEPTION(scope, { });
+    if (!result) {
         throwSyntaxError(exec, scope, "Invalid flags supplied to RegExp constructor."_s);
-    return result;
+        return { };
+    }
+
+    return result.value();
 }
 
 static JSObject* regExpCreate(ExecState* exec, JSGlobalObject* globalObject, JSValue newTarget, JSValue patternArg, JSValue flagsArg)
@@ -204,10 +203,8 @@ static JSObject* regExpCreate(ExecState* exec, JSGlobalObject* globalObject, JSV
     String pattern = patternArg.isUndefined() ? emptyString() : patternArg.toWTFString(exec);
     RETURN_IF_EXCEPTION(scope, nullptr);
 
-    RegExpFlags flags = toFlags(exec, flagsArg);
-    EXCEPTION_ASSERT(!!scope.exception() == (flags == InvalidFlags));
-    if (UNLIKELY(flags == InvalidFlags))
-        return nullptr;
+    auto flags = toFlags(exec, flagsArg);
+    RETURN_IF_EXCEPTION(scope, nullptr);
 
     RegExp* regExp = RegExp::create(vm, pattern, flags);
     if (UNLIKELY(!regExp->isValid())) {
@@ -246,12 +243,10 @@ JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const A
         RETURN_IF_EXCEPTION(scope, nullptr);
 
         if (!flagsArg.isUndefined()) {
-            RegExpFlags flags = toFlags(exec, flagsArg);
-            EXCEPTION_ASSERT(!!scope.exception() == (flags == InvalidFlags));
-            if (flags == InvalidFlags)
-                return nullptr;
-            regExp = RegExp::create(vm, regExp->pattern(), flags);
+            auto flags = toFlags(exec, flagsArg);
+            RETURN_IF_EXCEPTION(scope, nullptr);
 
+            regExp = RegExp::create(vm, regExp->pattern(), flags);
             if (UNLIKELY(!regExp->isValid())) {
                 throwException(exec, scope, regExp->errorToThrow(exec));
                 return nullptr;
index d01e40a..141d70d 100644 (file)
 
 #pragma once
 
+#include "YarrFlags.h"
+#include <wtf/OptionSet.h>
 #include <wtf/text/StringHash.h>
-#include <wtf/text/WTFString.h>
 
 namespace JSC {
 
-enum RegExpFlags : int8_t {
-    NoFlags = 0,
-    FlagGlobal = 1,
-    FlagIgnoreCase = 2,
-    FlagMultiline = 4,
-    FlagSticky = 8,
-    FlagUnicode = 16,
-    FlagDotAll = 32,
-    InvalidFlags = 64,
-    DeletedValueFlags = -1
-};
-
 struct RegExpKey {
-    RegExpFlags flagsValue;
+    OptionSet<Yarr::Flags> flagsValue;
     RefPtr<StringImpl> pattern;
 
     RegExpKey()
-        : flagsValue(NoFlags)
     {
     }
 
-    RegExpKey(RegExpFlags flags)
+    RegExpKey(OptionSet<Yarr::Flags> flags)
         : flagsValue(flags)
     {
     }
 
-    RegExpKey(RegExpFlags flags, const String& pattern)
+    RegExpKey(OptionSet<Yarr::Flags> flags, const String& pattern)
         : flagsValue(flags)
         , pattern(pattern.impl())
     {
     }
 
-    RegExpKey(RegExpFlags flags, RefPtr<StringImpl>&& pattern)
+    RegExpKey(OptionSet<Yarr::Flags> flags, RefPtr<StringImpl>&& pattern)
         : flagsValue(flags)
         , pattern(WTFMove(pattern))
     {
     }
 
-    RegExpKey(RegExpFlags flags, const RefPtr<StringImpl>& pattern)
+    RegExpKey(OptionSet<Yarr::Flags> flags, const RefPtr<StringImpl>& pattern)
         : flagsValue(flags)
         , pattern(pattern)
     {
@@ -107,7 +95,7 @@ template<> struct DefaultHash<JSC::RegExpKey> {
 
 template<> struct HashTraits<JSC::RegExpKey> : GenericHashTraits<JSC::RegExpKey> {
     static const bool emptyValueIsZero = true;
-    static void constructDeletedValue(JSC::RegExpKey& slot) { slot.flagsValue = JSC::DeletedValueFlags; }
-    static bool isDeletedValue(const JSC::RegExpKey& value) { return value.flagsValue == JSC::DeletedValueFlags; }
+    static void constructDeletedValue(JSC::RegExpKey& slot) { slot.flagsValue = JSC::Yarr::Flags::DeletedValue; }
+    static bool isDeletedValue(const JSC::RegExpKey& value) { return value.flagsValue == JSC::Yarr::Flags::DeletedValue; }
 };
 } // namespace WTF
index 2ce0b2d..5cc819d 100644 (file)
 #include "JSCInlines.h"
 #include "JSCJSValue.h"
 #include "JSFunction.h"
-#include "JSObject.h"
 #include "JSStringInlines.h"
 #include "Lexer.h"
 #include "ObjectPrototype.h"
-#include "RegExp.h"
 #include "RegExpCache.h"
 #include "RegExpObject.h"
 #include "RegExpObjectInlines.h"
 #include "StringObject.h"
 #include "StringRecursionChecker.h"
+#include "YarrFlags.h"
 #include <wtf/text/StringBuilder.h>
 
 namespace JSC {
@@ -149,14 +148,12 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec)
         String pattern = arg0.isUndefined() ? emptyString() : arg0.toWTFString(exec);
         RETURN_IF_EXCEPTION(scope, encodedJSValue());
 
-        RegExpFlags flags = NoFlags;
-        if (!arg1.isUndefined()) {
-            flags = regExpFlags(arg1.toWTFString(exec));
-            RETURN_IF_EXCEPTION(scope, encodedJSValue());
-            if (flags == InvalidFlags)
-                return throwVMError(exec, scope, createSyntaxError(exec, "Invalid flags supplied to RegExp constructor."_s));
-        }
-        regExp = RegExp::create(vm, pattern, flags);
+        auto flags = arg1.isUndefined() ? makeOptional(OptionSet<Yarr::Flags> { }) : Yarr::parseFlags(arg1.toWTFString(exec));
+        RETURN_IF_EXCEPTION(scope, encodedJSValue());
+        if (!flags)
+            return throwVMError(exec, scope, createSyntaxError(exec, "Invalid flags supplied to RegExp constructor."_s));
+
+        regExp = RegExp::create(vm, pattern, flags.value());
     }
 
     if (!regExp->isValid())
index 5091b71..cd0df24 100644 (file)
@@ -24,6 +24,7 @@
 #include "InitializeThreading.h"
 #include "JSCInlines.h"
 #include "JSGlobalObject.h"
+#include "YarrFlags.h"
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -328,11 +329,18 @@ static RegExp* parseRegExpLine(VM& vm, char* line, int lineLength, const char**
 
     ++i;
 
-    RegExp* r = RegExp::create(vm, pattern.toString(), regExpFlags(line + i));
+    auto flags = Yarr::parseFlags(line + i);
+    if (!flags) {
+        *regexpError = Yarr::errorMessage(Yarr::ErrorCode::InvalidRegularExpressionFlags);
+        return nullptr;
+    }
+
+    RegExp* r = RegExp::create(vm, pattern.toString(), flags.value());
     if (!r->isValid()) {
         *regexpError = r->errorMessage();
         return nullptr;
     }
+
     return r;
 }
 
index 1dcede6..f121579 100644 (file)
@@ -29,6 +29,7 @@
 #include "RegularExpression.h"
 
 #include "Yarr.h"
+#include "YarrFlags.h"
 #include "YarrInterpreter.h"
 #include <wtf/Assertions.h>
 #include <wtf/BumpPointerAllocator.h>
@@ -55,16 +56,16 @@ private:
 
     std::unique_ptr<JSC::Yarr::BytecodePattern> compile(const String& patternString, TextCaseSensitivity caseSensitivity, MultilineMode multilineMode, UnicodeMode unicodeMode)
     {
-        RegExpFlags flags = NoFlags;
+        OptionSet<JSC::Yarr::Flags> flags;
 
         if (caseSensitivity == TextCaseInsensitive)
-            flags = static_cast<RegExpFlags>(flags | FlagIgnoreCase);
+            flags.add(Flags::IgnoreCase);
 
         if (multilineMode == MultilineEnabled)
-            flags = static_cast<RegExpFlags>(flags | FlagMultiline);
+            flags.add(Flags::Multiline);
 
         if (unicodeMode == UnicodeAwareMode)
-            flags = static_cast<RegExpFlags>(flags | FlagUnicode);
+            flags.add(Flags::Unicode);
 
         JSC::Yarr::YarrPattern pattern(patternString, flags, m_constructionErrorCode);
         if (JSC::Yarr::hasError(m_constructionErrorCode)) {
diff --git a/Source/JavaScriptCore/yarr/YarrFlags.cpp b/Source/JavaScriptCore/yarr/YarrFlags.cpp
new file mode 100644 (file)
index 0000000..51379f6
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "YarrFlags.h"
+
+#include <wtf/OptionSet.h>
+#include <wtf/text/StringView.h>
+
+namespace JSC { namespace Yarr {
+
+Optional<OptionSet<Flags>> parseFlags(StringView string)
+{
+    OptionSet<Flags> flags;
+    for (auto character : string.codeUnits()) {
+        switch (character) {
+        case 'g':
+            if (flags.contains(Flags::Global))
+                return WTF::nullopt;
+            flags.add(Flags::Global);
+            break;
+
+        case 'i':
+            if (flags.contains(Flags::IgnoreCase))
+                return WTF::nullopt;
+            flags.add(Flags::IgnoreCase);
+            break;
+
+        case 'm':
+            if (flags.contains(Flags::Multiline))
+                return WTF::nullopt;
+            flags.add(Flags::Multiline);
+            break;
+
+        case 's':
+            if (flags.contains(Flags::DotAll))
+                return WTF::nullopt;
+            flags.add(Flags::DotAll);
+            break;
+            
+        case 'u':
+            if (flags.contains(Flags::Unicode))
+                return WTF::nullopt;
+            flags.add(Flags::Unicode);
+            break;
+                
+        case 'y':
+            if (flags.contains(Flags::Sticky))
+                return WTF::nullopt;
+            flags.add(Flags::Sticky);
+            break;
+
+        default:
+            return WTF::nullopt;
+        }
+    }
+
+    return makeOptional(flags);
+}
+
+} } // namespace JSC::Yarr
diff --git a/Source/JavaScriptCore/yarr/YarrFlags.h b/Source/JavaScriptCore/yarr/YarrFlags.h
new file mode 100644 (file)
index 0000000..7ae4dcb
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Forward.h>
+#include <wtf/Optional.h>
+
+namespace JSC { namespace Yarr {
+
+enum class Flags : uint8_t {
+    Global = 1 << 0,
+    IgnoreCase = 1 << 1,
+    Multiline = 1 << 2,
+    Sticky = 1 << 3,
+    Unicode = 1 << 4,
+    DotAll = 1 << 5,
+    DeletedValue = 1 << 6
+};
+
+JS_EXPORT_PRIVATE Optional<OptionSet<Flags>> parseFlags(StringView);
+
+} } // namespace JSC::Yarr
index a319cb3..4b85bed 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "ConcurrentJSLock.h"
+#include "YarrFlags.h"
 #include "YarrPattern.h"
 
 namespace WTF {
@@ -367,14 +368,14 @@ public:
 
     size_t estimatedSizeInBytes() const { return m_body->estimatedSizeInBytes(); }
     
-    bool ignoreCase() const { return m_flags & FlagIgnoreCase; }
-    bool multiline() const { return m_flags & FlagMultiline; }
-    bool sticky() const { return m_flags & FlagSticky; }
-    bool unicode() const { return m_flags & FlagUnicode; }
-    bool dotAll() const { return m_flags & FlagDotAll; }
+    bool ignoreCase() const { return m_flags.contains(Flags::IgnoreCase); }
+    bool multiline() const { return m_flags.contains(Flags::Multiline); }
+    bool sticky() const { return m_flags.contains(Flags::Sticky); }
+    bool unicode() const { return m_flags.contains(Flags::Unicode); }
+    bool dotAll() const { return m_flags.contains(Flags::DotAll); }
 
     std::unique_ptr<ByteDisjunction> m_body;
-    RegExpFlags m_flags;
+    OptionSet<Flags> m_flags;
     // Each BytecodePattern is associated with a RegExp, each RegExp is associated
     // with a VM.  Cache a pointer to out VM's m_regExpAllocator.
     BumpPointerAllocator* m_allocator;
index 36fc4d2..171f1c7 100644 (file)
@@ -36,7 +36,6 @@
 #include <wtf/StackPointer.h>
 #include <wtf/Threading.h>
 #include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
 
 namespace JSC { namespace Yarr {
 
@@ -1110,9 +1109,6 @@ ErrorCode YarrPattern::compile(const String& patternString, void* stackLimit)
 {
     YarrPatternConstructor constructor(*this, stackLimit);
 
-    if (m_flags == InvalidFlags)
-        return ErrorCode::InvalidRegularExpressionFlags;
-
     {
         ErrorCode error = parse(constructor, patternString, unicode());
         if (hasError(error))
@@ -1152,7 +1148,7 @@ ErrorCode YarrPattern::compile(const String& patternString, void* stackLimit)
     return ErrorCode::NoError;
 }
 
-YarrPattern::YarrPattern(const String& pattern, RegExpFlags flags, ErrorCode& error, void* stackLimit)
+YarrPattern::YarrPattern(const String& pattern, OptionSet<Flags> flags, ErrorCode& error, void* stackLimit)
     : m_containsBackreferences(false)
     , m_containsBOL(false)
     , m_containsUnsignedLengthPattern(false)
@@ -1160,6 +1156,7 @@ YarrPattern::YarrPattern(const String& pattern, RegExpFlags flags, ErrorCode& er
     , m_saveInitialStartValue(false)
     , m_flags(flags)
 {
+    ASSERT(m_flags != Flags::DeletedValue);
     error = compile(pattern, stackLimit);
 }
 
@@ -1420,7 +1417,7 @@ void YarrPattern::dumpPattern(PrintStream& out, const String& patternString)
     out.print("RegExp pattern for ");
     dumpPatternString(out, patternString);
 
-    if (m_flags != NoFlags) {
+    if (m_flags) {
         bool printSeperator = false;
         out.print(" (");
         if (global()) {
index afb4e5c..c6e7fb2 100644 (file)
 
 #pragma once
 
-#include "RegExpKey.h"
 #include "YarrErrorCode.h"
+#include "YarrFlags.h"
 #include "YarrUnicodeProperties.h"
 #include <wtf/CheckedArithmetic.h>
 #include <wtf/HashMap.h>
+#include <wtf/OptionSet.h>
 #include <wtf/PrintStream.h>
 #include <wtf/Vector.h>
-#include <wtf/text/WTFString.h>
+#include <wtf/text/StringHash.h>
 
 namespace JSC { namespace Yarr {
 
@@ -352,7 +353,7 @@ struct TermChain {
 
 
 struct YarrPattern {
-    JS_EXPORT_PRIVATE YarrPattern(const String& pattern, RegExpFlags, ErrorCode&, void* stackLimit = nullptr);
+    JS_EXPORT_PRIVATE YarrPattern(const String& pattern, OptionSet<Flags>, ErrorCode&, void* stackLimit = nullptr);
 
     void resetForReparsing()
     {
@@ -507,19 +508,19 @@ struct YarrPattern {
     void dumpPattern(const String& pattern);
     void dumpPattern(PrintStream& out, const String& pattern);
 
-    bool global() const { return m_flags & FlagGlobal; }
-    bool ignoreCase() const { return m_flags & FlagIgnoreCase; }
-    bool multiline() const { return m_flags & FlagMultiline; }
-    bool sticky() const { return m_flags & FlagSticky; }
-    bool unicode() const { return m_flags & FlagUnicode; }
-    bool dotAll() const { return m_flags & FlagDotAll; }
+    bool global() const { return m_flags.contains(Flags::Global); }
+    bool ignoreCase() const { return m_flags.contains(Flags::IgnoreCase); }
+    bool multiline() const { return m_flags.contains(Flags::Multiline); }
+    bool sticky() const { return m_flags.contains(Flags::Sticky); }
+    bool unicode() const { return m_flags.contains(Flags::Unicode); }
+    bool dotAll() const { return m_flags.contains(Flags::DotAll); }
 
     bool m_containsBackreferences : 1;
     bool m_containsBOL : 1;
     bool m_containsUnsignedLengthPattern : 1;
     bool m_hasCopiedParenSubexpressions : 1;
     bool m_saveInitialStartValue : 1;
-    RegExpFlags m_flags;
+    OptionSet<Flags> m_flags;
     unsigned m_numSubpatterns { 0 };
     unsigned m_maxBackReference { 0 };
     unsigned m_initialStartValueFrameLocation { 0 };
index 0e0f635..86b0e67 100644 (file)
@@ -26,9 +26,9 @@
 #include "config.h"
 #include "YarrSyntaxChecker.h"
 
+#include "YarrFlags.h"
 #include "YarrParser.h"
 #include <wtf/Optional.h>
-#include <wtf/text/WTFString.h>
 
 namespace JSC { namespace Yarr {
 
@@ -58,7 +58,12 @@ public:
 ErrorCode checkSyntax(const String& pattern, const String& flags)
 {
     SyntaxChecker syntaxChecker;
-    return parse(syntaxChecker, pattern, flags.contains('u'));
+
+    auto parsedFlags = parseFlags(flags);
+    if (!parsedFlags)
+        return ErrorCode::InvalidRegularExpressionFlags;
+
+    return parse(syntaxChecker, pattern, parsedFlags->contains(Flags::Unicode));
 }
 
 }} // JSC::Yarr
index f61f393..4cc0b62 100644 (file)
@@ -1,3 +1,14 @@
+2019-03-10  Ross Kirsling  <ross.kirsling@sony.com>
+
+        Invalid flags in a RegExp literal should be an early SyntaxError
+        https://bugs.webkit.org/show_bug.cgi?id=195514
+
+        Reviewed by Darin Adler.
+
+        * bindings/js/SerializedScriptValue.cpp:
+        (WebCore::CloneDeserializer::readTerminal):
+        Consume YarrFlags.
+
 2019-03-10  Tim Horton  <timothy_horton@apple.com>
 
         Add SPI to retrieve the set of text inputs in a given rect, and later focus one
index d5e191f..776ec43 100644 (file)
@@ -56,7 +56,6 @@
 #include "SharedBuffer.h"
 #include "WebCoreJSClientData.h"
 #include <JavaScriptCore/APICast.h>
-#include <JavaScriptCore/ArrayBuffer.h>
 #include <JavaScriptCore/BooleanObject.h>
 #include <JavaScriptCore/CatchScope.h>
 #include <JavaScriptCore/DateInstance.h>
@@ -81,6 +80,7 @@
 #include <JavaScriptCore/TypedArrayInlines.h>
 #include <JavaScriptCore/TypedArrays.h>
 #include <JavaScriptCore/WasmModule.h>
+#include <JavaScriptCore/YarrFlags.h>
 #include <limits>
 #include <wtf/MainThread.h>
 #include <wtf/RunLoop.h>
@@ -2891,10 +2891,10 @@ private:
             CachedStringRef flags;
             if (!readStringData(flags))
                 return JSValue();
-            RegExpFlags reFlags = regExpFlags(flags->string());
-            ASSERT(reFlags != InvalidFlags);
+            auto reFlags = Yarr::parseFlags(flags->string());
+            ASSERT(reFlags.hasValue());
             VM& vm = m_exec->vm();
-            RegExp* regExp = RegExp::create(vm, pattern->string(), reFlags);
+            RegExp* regExp = RegExp::create(vm, pattern->string(), reFlags.value());
             return RegExpObject::create(vm, m_globalObject->regExpStructure(), regExp);
         }
         case ObjectReferenceTag: {