diff --git a/js/src/jit-test/tests/latin1/regexp.js b/js/src/jit-test/tests/latin1/regexp.js new file mode 100644 index 000000000000..d8e9bda99a87 --- /dev/null +++ b/js/src/jit-test/tests/latin1/regexp.js @@ -0,0 +1,15 @@ +// Latin1 +var re = new RegExp(toLatin1("foo[bB]a\\r"), toLatin1("im")); +assertEq(isLatin1(re.source), true); +assertEq(re.source, "foo[bB]a\\r"); +assertEq(re.multiline, true); +assertEq(re.ignoreCase, true); +assertEq(re.sticky, false); + +// TwoByte +re = new RegExp("foo[bB]a\\r\u1200", "im"); +assertEq(isLatin1(re.source), false); +assertEq(re.source, "foo[bB]a\\r\u1200"); +assertEq(re.multiline, true); +assertEq(re.ignoreCase, true); +assertEq(re.sticky, false); diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 47ccd77bdded..d8930ef45a3a 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -1062,40 +1062,76 @@ js::CloneRegExpObject(JSContext *cx, JSObject *obj_) return res; } +static bool +HandleRegExpFlag(RegExpFlag flag, RegExpFlag *flags) +{ + if (*flags & flag) + return false; + *flags = RegExpFlag(*flags | flag); + return true; +} + +template +static size_t +ParseRegExpFlags(const CharT *chars, size_t length, RegExpFlag *flagsOut, jschar *lastParsedOut) +{ + *flagsOut = RegExpFlag(0); + + for (size_t i = 0; i < length; i++) { + *lastParsedOut = chars[i]; + switch (chars[i]) { + case 'i': + if (!HandleRegExpFlag(IgnoreCaseFlag, flagsOut)) + return false; + break; + case 'g': + if (!HandleRegExpFlag(GlobalFlag, flagsOut)) + return false; + break; + case 'm': + if (!HandleRegExpFlag(MultilineFlag, flagsOut)) + return false; + break; + case 'y': + if (!HandleRegExpFlag(StickyFlag, flagsOut)) + return false; + break; + default: + return false; + } + } + + return true; +} + bool js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut) { - size_t n = flagStr->length(); - const jschar *s = flagStr->getChars(cx); - if (!s) + JSLinearString *linear = flagStr->ensureLinear(cx); + if (!linear) return false; - *flagsOut = RegExpFlag(0); - for (size_t i = 0; i < n; i++) { -#define HANDLE_FLAG(name_) \ - JS_BEGIN_MACRO \ - if (*flagsOut & (name_)) \ - goto bad_flag; \ - *flagsOut = RegExpFlag(*flagsOut | (name_)); \ - JS_END_MACRO - switch (s[i]) { - case 'i': HANDLE_FLAG(IgnoreCaseFlag); break; - case 'g': HANDLE_FLAG(GlobalFlag); break; - case 'm': HANDLE_FLAG(MultilineFlag); break; - case 'y': HANDLE_FLAG(StickyFlag); break; - default: - bad_flag: - { - char charBuf[2]; - charBuf[0] = char(s[i]); - charBuf[1] = '\0'; - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr, - JSMSG_BAD_REGEXP_FLAG, charBuf); - return false; - } - } -#undef HANDLE_FLAG + size_t len = linear->length(); + + bool ok; + jschar lastParsed; + if (linear->hasLatin1Chars()) { + JS::AutoCheckCannotGC nogc; + ok = ::ParseRegExpFlags(linear->latin1Chars(nogc), len, flagsOut, &lastParsed); + } else { + JS::AutoCheckCannotGC nogc; + ok = ::ParseRegExpFlags(linear->twoByteChars(nogc), len, flagsOut, &lastParsed); } + + if (!ok) { + char charBuf[2]; + charBuf[0] = char(lastParsed); + charBuf[1] = '\0'; + JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, nullptr, + JSMSG_BAD_REGEXP_FLAG, charBuf); + return false; + } + return true; }