Fix exception scope verification failures in runtime/RegExp* files.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Nov 2016 00:06:50 +0000 (00:06 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Nov 2016 00:06:50 +0000 (00:06 +0000)
https://bugs.webkit.org/show_bug.cgi?id=165054

Reviewed by Saam Barati.

Also replaced returning JSValue() with returning { }.

* runtime/RegExpConstructor.cpp:
(JSC::toFlags):
(JSC::regExpCreate):
(JSC::constructRegExp):
* runtime/RegExpObject.cpp:
(JSC::RegExpObject::defineOwnProperty):
(JSC::collectMatches):
(JSC::RegExpObject::matchGlobal):
* runtime/RegExpObjectInlines.h:
(JSC::getRegExpObjectLastIndexAsUnsigned):
(JSC::RegExpObject::execInline):
(JSC::RegExpObject::matchInline):
* runtime/RegExpPrototype.cpp:
(JSC::regExpProtoFuncCompile):
(JSC::flagsString):
(JSC::regExpProtoFuncToString):
(JSC::regExpProtoFuncSplitFast):

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/RegExpConstructor.cpp
Source/JavaScriptCore/runtime/RegExpObject.cpp
Source/JavaScriptCore/runtime/RegExpObjectInlines.h
Source/JavaScriptCore/runtime/RegExpPrototype.cpp

index 026e690..aed64eb 100644 (file)
@@ -1,3 +1,30 @@
+2016-11-29  Mark Lam  <mark.lam@apple.com>
+
+        Fix exception scope verification failures in runtime/RegExp* files.
+        https://bugs.webkit.org/show_bug.cgi?id=165054
+
+        Reviewed by Saam Barati.
+
+        Also replaced returning JSValue() with returning { }.
+
+        * runtime/RegExpConstructor.cpp:
+        (JSC::toFlags):
+        (JSC::regExpCreate):
+        (JSC::constructRegExp):
+        * runtime/RegExpObject.cpp:
+        (JSC::RegExpObject::defineOwnProperty):
+        (JSC::collectMatches):
+        (JSC::RegExpObject::matchGlobal):
+        * runtime/RegExpObjectInlines.h:
+        (JSC::getRegExpObjectLastIndexAsUnsigned):
+        (JSC::RegExpObject::execInline):
+        (JSC::RegExpObject::matchInline):
+        * runtime/RegExpPrototype.cpp:
+        (JSC::regExpProtoFuncCompile):
+        (JSC::flagsString):
+        (JSC::regExpProtoFuncToString):
+        (JSC::regExpProtoFuncSplitFast):
+
 2016-11-29  Andy Estes  <aestes@apple.com>
 
         [Cocoa] Enable two clang warnings recommended by Xcode
index 7c6cccd..f3fb525 100644 (file)
@@ -214,11 +214,10 @@ inline RegExpFlags toFlags(ExecState* exec, JSValue flags)
 
     if (flags.isUndefined())
         return NoFlags;
-    JSString* flagsString = flags.toString(exec);
-    ASSERT(scope.exception() || flagsString);
-    if (!flagsString) {
+    JSString* flagsString = flags.toStringOrNull(exec);
+    ASSERT(!!scope.exception() == !flagsString);
+    if (UNLIKELY(!flagsString))
         return InvalidFlags;
-    }
 
     RegExpFlags result = regExpFlags(flagsString->value(exec));
     RETURN_IF_EXCEPTION(scope, InvalidFlags);
@@ -236,7 +235,8 @@ static JSObject* regExpCreate(ExecState* exec, JSGlobalObject* globalObject, JSV
     RETURN_IF_EXCEPTION(scope, nullptr);
 
     RegExpFlags flags = toFlags(exec, flagsArg);
-    if (flags == InvalidFlags)
+    ASSERT(!!scope.exception() == (flags == InvalidFlags));
+    if (UNLIKELY(flags == InvalidFlags))
         return nullptr;
 
     RegExp* regExp = RegExp::create(vm, pattern, flags);
@@ -257,6 +257,7 @@ JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const A
 
     bool isPatternRegExp = patternArg.inherits(RegExpObject::info());
     bool constructAsRegexp = isRegExp(vm, exec, patternArg);
+    RETURN_IF_EXCEPTION(scope, nullptr);
 
     if (newTarget.isUndefined() && constructAsRegexp && flagsArg.isUndefined()) {
         JSValue constructor = patternArg.get(exec, vm.propertyNames->constructor);
@@ -274,21 +275,26 @@ JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const A
 
         if (!flagsArg.isUndefined()) {
             RegExpFlags flags = toFlags(exec, flagsArg);
+            ASSERT(!!scope.exception() == (flags == InvalidFlags));
             if (flags == InvalidFlags)
                 return nullptr;
             regExp = RegExp::create(vm, regExp->pattern(), flags);
         }
 
-        return RegExpObject::create(exec->vm(), structure, regExp);
+        return RegExpObject::create(vm, structure, regExp);
     }
 
     if (constructAsRegexp) {
         JSValue pattern = patternArg.get(exec, vm.propertyNames->source);
-        if (flagsArg.isUndefined())
+        RETURN_IF_EXCEPTION(scope, nullptr);
+        if (flagsArg.isUndefined()) {
             flagsArg = patternArg.get(exec, vm.propertyNames->flags);
+            RETURN_IF_EXCEPTION(scope, nullptr);
+        }
         patternArg = pattern;
     }
 
+    scope.release();
     return regExpCreate(exec, globalObject, newTarget, patternArg, flagsArg);
 }
 
index 55fe5f3..0ceb5b8 100644 (file)
@@ -119,13 +119,16 @@ bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, Property
                 return typeError(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyChangeError));
             return true;
         }
-        if (descriptor.value())
+        if (descriptor.value()) {
             regExp->setLastIndex(exec, descriptor.value(), false);
+            RETURN_IF_EXCEPTION(scope, false);
+        }
         if (descriptor.writablePresent() && !descriptor.writable())
             regExp->m_lastIndexIsWritable = false;
         return true;
     }
 
+    scope.release();
     return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
 }
 
@@ -179,12 +182,17 @@ JSValue collectMatches(VM& vm, ExecState* exec, JSString* string, const String&
     static unsigned maxSizeForDirectPath = 100000;
     
     JSArray* array = constructEmptyArray(exec, nullptr);
-    RETURN_IF_EXCEPTION(scope, JSValue());
+    RETURN_IF_EXCEPTION(scope, { });
 
+    bool hasException = false;
     auto iterate = [&] () {
         size_t end = result.end;
         size_t length = end - result.start;
         array->push(exec, JSRopeString::createSubstringOfResolved(vm, string, result.start, length));
+        if (UNLIKELY(scope.exception())) {
+            hasException = true;
+            return;
+        }
         if (!length)
             end = fixEnd(end);
         result = constructor->performMatch(vm, regExp, string, s, end);
@@ -216,9 +224,12 @@ JSValue collectMatches(VM& vm, ExecState* exec, JSString* string, const String&
             
             // OK, we have a sensible number of matches. Now we can create them for reals.
             result = savedResult;
-            do
+            do {
                 iterate();
-            while (result);
+                ASSERT(!!scope.exception() == hasException);
+                if (UNLIKELY(hasException))
+                    return { };
+            } while (result);
             
             return array;
         }
@@ -238,20 +249,22 @@ JSValue RegExpObject::matchGlobal(ExecState* exec, JSGlobalObject* globalObject,
     ASSERT(regExp->global());
 
     setLastIndex(exec, 0);
-    RETURN_IF_EXCEPTION(scope, JSValue());
+    RETURN_IF_EXCEPTION(scope, { });
 
     String s = string->value(exec);
     RegExpConstructor* regExpConstructor = globalObject->regExpConstructor();
     
     if (regExp->unicode()) {
         unsigned stringLength = s.length();
+        scope.release();
         return collectMatches(
             vm, exec, string, s, regExpConstructor, regExp,
             [&] (size_t end) -> size_t {
                 return advanceStringUnicode(s, stringLength, end);
             });
     }
-    
+
+    scope.release();
     return collectMatches(
         vm, exec, string, s, regExpConstructor, regExp,
         [&] (size_t end) -> size_t {
index 4ac09b6..296bd15 100644 (file)
@@ -36,17 +36,22 @@ namespace JSC {
 ALWAYS_INLINE unsigned getRegExpObjectLastIndexAsUnsigned(
     ExecState* exec, RegExpObject* regExpObject, const String& input)
 {
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
     JSValue jsLastIndex = regExpObject->getLastIndex();
     unsigned lastIndex;
     if (LIKELY(jsLastIndex.isUInt32())) {
         lastIndex = jsLastIndex.asUInt32();
         if (lastIndex > input.length()) {
+            scope.release();
             regExpObject->setLastIndex(exec, 0);
             return UINT_MAX;
         }
     } else {
         double doubleLastIndex = jsLastIndex.toInteger(exec);
+        RETURN_IF_EXCEPTION(scope, UINT_MAX);
         if (doubleLastIndex < 0 || doubleLastIndex > input.length()) {
+            scope.release();
             regExpObject->setLastIndex(exec, 0);
             return UINT_MAX;
         }
@@ -63,13 +68,14 @@ JSValue RegExpObject::execInline(ExecState* exec, JSGlobalObject* globalObject,
     RegExp* regExp = this->regExp();
     RegExpConstructor* regExpConstructor = globalObject->regExpConstructor();
     String input = string->value(exec);
-    RETURN_IF_EXCEPTION(scope, JSValue());
+    RETURN_IF_EXCEPTION(scope, { });
 
     bool globalOrSticky = regExp->globalOrSticky();
 
     unsigned lastIndex;
     if (globalOrSticky) {
         lastIndex = getRegExpObjectLastIndexAsUnsigned(exec, this, input);
+        ASSERT(!scope.exception() || lastIndex == UINT_MAX);
         if (lastIndex == UINT_MAX)
             return jsNull();
     } else
@@ -79,6 +85,7 @@ JSValue RegExpObject::execInline(ExecState* exec, JSGlobalObject* globalObject,
     JSArray* array =
         createRegExpMatchesArray(vm, globalObject, string, input, regExp, lastIndex, result);
     if (!array) {
+        scope.release();
         if (globalOrSticky)
             setLastIndex(exec, 0);
         return jsNull();
@@ -86,6 +93,7 @@ JSValue RegExpObject::execInline(ExecState* exec, JSGlobalObject* globalObject,
 
     if (globalOrSticky)
         setLastIndex(exec, result.end);
+    RETURN_IF_EXCEPTION(scope, { });
     regExpConstructor->recordMatch(vm, regExp, string, result);
     return array;
 }
@@ -100,16 +108,18 @@ MatchResult RegExpObject::matchInline(
     RegExp* regExp = this->regExp();
     RegExpConstructor* regExpConstructor = globalObject->regExpConstructor();
     String input = string->value(exec);
-    RETURN_IF_EXCEPTION(scope, MatchResult());
+    RETURN_IF_EXCEPTION(scope, { });
 
     if (!regExp->global() && !regExp->sticky())
         return regExpConstructor->performMatch(vm, regExp, string, input, 0);
 
     unsigned lastIndex = getRegExpObjectLastIndexAsUnsigned(exec, this, input);
+    ASSERT(!scope.exception() || (lastIndex == UINT_MAX));
     if (lastIndex == UINT_MAX)
         return MatchResult::failed();
     
     MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex);
+    scope.release();
     setLastIndex(exec, result.end);
     return result;
 }
index f388017..8824082 100644 (file)
@@ -183,6 +183,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncCompile(ExecState* exec)
         return throwVMError(exec, scope, createSyntaxError(exec, regExp->errorMessage()));
 
     asRegExpObject(thisValue)->setRegExp(vm, regExp);
+    scope.release();
     asRegExpObject(thisValue)->setLastIndex(exec, 0);
     return JSValue::encode(thisValue);
 }
@@ -197,15 +198,15 @@ static inline FlagsString flagsString(ExecState* exec, JSObject* regexp)
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
 
-    JSValue globalValue = regexp->get(exec, exec->propertyNames().global);
+    JSValue globalValue = regexp->get(exec, vm.propertyNames->global);
     RETURN_IF_EXCEPTION(scope, string);
-    JSValue ignoreCaseValue = regexp->get(exec, exec->propertyNames().ignoreCase);
+    JSValue ignoreCaseValue = regexp->get(exec, vm.propertyNames->ignoreCase);
     RETURN_IF_EXCEPTION(scope, string);
-    JSValue multilineValue = regexp->get(exec, exec->propertyNames().multiline);
+    JSValue multilineValue = regexp->get(exec, vm.propertyNames->multiline);
     RETURN_IF_EXCEPTION(scope, string);
-    JSValue unicodeValue = regexp->get(exec, exec->propertyNames().unicode);
+    JSValue unicodeValue = regexp->get(exec, vm.propertyNames->unicode);
     RETURN_IF_EXCEPTION(scope, string);
-    JSValue stickyValue = regexp->get(exec, exec->propertyNames().sticky);
+    JSValue stickyValue = regexp->get(exec, vm.propertyNames->sticky);
     RETURN_IF_EXCEPTION(scope, string);
 
     unsigned index = 0;
@@ -236,6 +237,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec)
     JSObject* thisObject = asObject(thisValue);
 
     StringRecursionChecker checker(exec, thisObject);
+    ASSERT(!scope.exception() || checker.earlyReturnValue());
     if (JSValue earlyReturnValue = checker.earlyReturnValue())
         return JSValue::encode(earlyReturnValue);
 
@@ -621,8 +623,10 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncSplitFast(ExecState* exec)
         // b. If z is not null, return A.
         // c. Perform ! CreateDataProperty(A, "0", S).
         // d. Return A.
-        if (!regexp->match(vm, input, 0))
+        if (!regexp->match(vm, input, 0)) {
             result->putDirectIndex(exec, 0, inputString);
+            RETURN_IF_EXCEPTION(scope, encodedJSValue());
+        }
         return JSValue::encode(result);
     }
 
@@ -643,16 +647,19 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncSplitFast(ExecState* exec)
         },
         [&] (bool isDefined, unsigned start, unsigned length) -> SplitControl {
             result->putDirectIndex(exec, resultLength++, isDefined ? JSRopeString::createSubstringOfResolved(vm, inputString, start, length) : jsUndefined());
+            RETURN_IF_EXCEPTION(scope, AbortSplit);
             if (resultLength >= limit)
                 return AbortSplit;
             return ContinueSplit;
         });
-    
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
     if (resultLength >= limit)
         return JSValue::encode(result);
     if (resultLength < maxSizeForDirectPath) {
         // 20. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive) through size (exclusive).
         // 21. Perform ! CreateDataProperty(A, ! ToString(lengthA), T).
+        scope.release();
         result->putDirectIndex(exec, resultLength, JSRopeString::createSubstringOfResolved(vm, inputString, position, inputSize - position));
         
         // 22. Return A.
@@ -679,7 +686,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncSplitFast(ExecState* exec)
     
     if (resultLength + dryRunCount >= MAX_STORAGE_VECTOR_LENGTH) {
         throwOutOfMemoryError(exec, scope);
-        return JSValue::encode(jsUndefined());
+        return encodedJSValue();
     }
     
     // OK, we know that if we finish the split, we won't have to OOM.
@@ -693,16 +700,19 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncSplitFast(ExecState* exec)
         },
         [&] (bool isDefined, unsigned start, unsigned length) -> SplitControl {
             result->putDirectIndex(exec, resultLength++, isDefined ? JSRopeString::createSubstringOfResolved(vm, inputString, start, length) : jsUndefined());
+            RETURN_IF_EXCEPTION(scope, AbortSplit);
             if (resultLength >= limit)
                 return AbortSplit;
             return ContinueSplit;
         });
-    
+    RETURN_IF_EXCEPTION(scope, encodedJSValue());
+
     if (resultLength >= limit)
         return JSValue::encode(result);
     
     // 20. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive) through size (exclusive).
     // 21. Perform ! CreateDataProperty(A, ! ToString(lengthA), T).
+    scope.release();
     result->putDirectIndex(exec, resultLength, JSRopeString::createSubstringOfResolved(vm, inputString, position, inputSize - position));
     // 22. Return A.
     return JSValue::encode(result);