RegExpMatchesArray should not copy the ovector
authorbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 21 Mar 2012 20:55:41 +0000 (20:55 +0000)
committerbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 21 Mar 2012 20:55:41 +0000 (20:55 +0000)
https://bugs.webkit.org/show_bug.cgi?id=81742

Reviewed by Michael Saboff.

Currently, all RegExpMatchesArray object contain Vector<int, 32>, used to hold any sub-pattern results.
This makes allocation/construction/destruction of these objects more expensive. Instead, just store the
main match, and recreate the sub-pattern ranges only if necessary (these are often only used for grouping,
and the results never accessed).
If the main match (index 0) of the RegExpMatchesArray is accessed, reify that value alone.

* dfg/DFGOperations.cpp:
    - RegExpObject match renamed back to test (test returns a bool).
* runtime/RegExpConstructor.cpp:
(JSC):
    - Removed RegExpResult, RegExpMatchesArray constructor, destroy method.
(JSC::RegExpMatchesArray::finishCreation):
    - Removed RegExpConstructorPrivate parameter.
(JSC::RegExpMatchesArray::reifyAllProperties):
    - (Was fillArrayInstance) Reify all properties of the RegExpMatchesArray.
    If there are sub-pattern properties, the RegExp is re-run to generate their values.
(JSC::RegExpMatchesArray::reifyMatchProperty):
    - Reify just the match (index 0) property of the RegExpMatchesArray.
* runtime/RegExpConstructor.h:
(RegExpConstructor):
(JSC::RegExpConstructor::performMatch):
    - performMatch now returns a MatchResult, rather than using out-parameters.
* runtime/RegExpMatchesArray.h:
(JSC::RegExpMatchesArray::RegExpMatchesArray):
    - Moved from .cpp, stores the input/regExp/result to use when lazily reifying properties.
(RegExpMatchesArray):
(JSC::RegExpMatchesArray::create):
    - Now passed the input string matched against, the RegExp, and the MatchResult.
(JSC::RegExpMatchesArray::reifyAllPropertiesIfNecessary):
(JSC::RegExpMatchesArray::reifyMatchPropertyIfNecessary):
    - Helpers to conditionally reify properties.
(JSC::RegExpMatchesArray::getOwnPropertySlot):
(JSC::RegExpMatchesArray::getOwnPropertySlotByIndex):
(JSC::RegExpMatchesArray::getOwnPropertyDescriptor):
(JSC::RegExpMatchesArray::put):
(JSC::RegExpMatchesArray::putByIndex):
(JSC::RegExpMatchesArray::deleteProperty):
(JSC::RegExpMatchesArray::deletePropertyByIndex):
(JSC::RegExpMatchesArray::getOwnPropertyNames):
(JSC::RegExpMatchesArray::defineOwnProperty):
    - Changed to use reifyAllPropertiesIfNecessary/reifyMatchPropertyIfNecessary
    (getOwnPropertySlotByIndex calls reifyMatchPropertyIfNecessary if index is 0).
* runtime/RegExpObject.cpp:
(JSC::RegExpObject::exec):
(JSC::RegExpObject::match):
    - match now returns a MatchResult.
* runtime/RegExpObject.h:
(JSC::MatchResult::MatchResult):
    - Added the result of a match is a start & end tuple.
(JSC::MatchResult::failed):
    - A failure is indicated by (notFound, 0).
(JSC::MatchResult::operator bool):
    - Evaluates to false if the match failed.
(JSC::MatchResult::empty):
    - Evaluates to true if the match succeeded with length 0.
(JSC::RegExpObject::test):
    - Now returns a bool.
* runtime/RegExpPrototype.cpp:
(JSC::regExpProtoFuncTest):
    - RegExpObject match renamed back to test (test returns a bool).
* runtime/StringPrototype.cpp:
(JSC::removeUsingRegExpSearch):
(JSC::replaceUsingRegExpSearch):
(JSC::stringProtoFuncMatch):
(JSC::stringProtoFuncSearch):
    - performMatch now returns a MatchResult, rather than using out-parameters.

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/runtime/RegExpConstructor.cpp
Source/JavaScriptCore/runtime/RegExpConstructor.h
Source/JavaScriptCore/runtime/RegExpMatchesArray.h
Source/JavaScriptCore/runtime/RegExpObject.cpp
Source/JavaScriptCore/runtime/RegExpObject.h
Source/JavaScriptCore/runtime/RegExpPrototype.cpp
Source/JavaScriptCore/runtime/StringPrototype.cpp

index ec576ab..f05083a 100644 (file)
@@ -1,3 +1,77 @@
+2012-03-20  Gavin Barraclough  <barraclough@apple.com>
+
+        RegExpMatchesArray should not copy the ovector
+        https://bugs.webkit.org/show_bug.cgi?id=81742
+
+        Reviewed by Michael Saboff.
+
+        Currently, all RegExpMatchesArray object contain Vector<int, 32>, used to hold any sub-pattern results.
+        This makes allocation/construction/destruction of these objects more expensive. Instead, just store the
+        main match, and recreate the sub-pattern ranges only if necessary (these are often only used for grouping,
+        and the results never accessed).
+        If the main match (index 0) of the RegExpMatchesArray is accessed, reify that value alone.
+
+        * dfg/DFGOperations.cpp:
+            - RegExpObject match renamed back to test (test returns a bool).
+        * runtime/RegExpConstructor.cpp:
+        (JSC):
+            - Removed RegExpResult, RegExpMatchesArray constructor, destroy method.
+        (JSC::RegExpMatchesArray::finishCreation):
+            - Removed RegExpConstructorPrivate parameter.
+        (JSC::RegExpMatchesArray::reifyAllProperties):
+            - (Was fillArrayInstance) Reify all properties of the RegExpMatchesArray.
+            If there are sub-pattern properties, the RegExp is re-run to generate their values.
+        (JSC::RegExpMatchesArray::reifyMatchProperty):
+            - Reify just the match (index 0) property of the RegExpMatchesArray.
+        * runtime/RegExpConstructor.h:
+        (RegExpConstructor):
+        (JSC::RegExpConstructor::performMatch):
+            - performMatch now returns a MatchResult, rather than using out-parameters.
+        * runtime/RegExpMatchesArray.h:
+        (JSC::RegExpMatchesArray::RegExpMatchesArray):
+            - Moved from .cpp, stores the input/regExp/result to use when lazily reifying properties.
+        (RegExpMatchesArray):
+        (JSC::RegExpMatchesArray::create):
+            - Now passed the input string matched against, the RegExp, and the MatchResult.
+        (JSC::RegExpMatchesArray::reifyAllPropertiesIfNecessary):
+        (JSC::RegExpMatchesArray::reifyMatchPropertyIfNecessary):
+            - Helpers to conditionally reify properties.
+        (JSC::RegExpMatchesArray::getOwnPropertySlot):
+        (JSC::RegExpMatchesArray::getOwnPropertySlotByIndex):
+        (JSC::RegExpMatchesArray::getOwnPropertyDescriptor):
+        (JSC::RegExpMatchesArray::put):
+        (JSC::RegExpMatchesArray::putByIndex):
+        (JSC::RegExpMatchesArray::deleteProperty):
+        (JSC::RegExpMatchesArray::deletePropertyByIndex):
+        (JSC::RegExpMatchesArray::getOwnPropertyNames):
+        (JSC::RegExpMatchesArray::defineOwnProperty):
+            - Changed to use reifyAllPropertiesIfNecessary/reifyMatchPropertyIfNecessary
+            (getOwnPropertySlotByIndex calls reifyMatchPropertyIfNecessary if index is 0).
+        * runtime/RegExpObject.cpp:
+        (JSC::RegExpObject::exec):
+        (JSC::RegExpObject::match):
+            - match now returns a MatchResult.
+        * runtime/RegExpObject.h:
+        (JSC::MatchResult::MatchResult):
+            - Added the result of a match is a start & end tuple.
+        (JSC::MatchResult::failed):
+            - A failure is indicated by (notFound, 0).
+        (JSC::MatchResult::operator bool):
+            - Evaluates to false if the match failed.
+        (JSC::MatchResult::empty):
+            - Evaluates to true if the match succeeded with length 0.
+        (JSC::RegExpObject::test):
+            - Now returns a bool.
+        * runtime/RegExpPrototype.cpp:
+        (JSC::regExpProtoFuncTest):
+            - RegExpObject match renamed back to test (test returns a bool).
+        * runtime/StringPrototype.cpp:
+        (JSC::removeUsingRegExpSearch):
+        (JSC::replaceUsingRegExpSearch):
+        (JSC::stringProtoFuncMatch):
+        (JSC::stringProtoFuncSearch):
+            - performMatch now returns a MatchResult, rather than using out-parameters.
+
 2012-03-21  Hojong Han  <hojong.han@samsung.com>
 
         Fix out of memory by allowing overcommit
index 00b4be8..1237bc9 100644 (file)
@@ -522,7 +522,7 @@ size_t DFG_OPERATION operationRegExpTest(ExecState* exec, JSCell* base, JSCell*
 
     ASSERT(argument->isString() || argument->isObject());
     JSString* input = argument->isString() ? asString(argument) : asObject(argument)->toString(exec);
-    return asRegExpObject(base)->match(exec, input);
+    return asRegExpObject(base)->test(exec, input);
 }
         
 EncodedJSValue DFG_OPERATION operationArrayPop(ExecState* exec, JSArray* array)
index 90082f0..5c72ad5 100644 (file)
@@ -97,16 +97,6 @@ const ClassInfo RegExpMatchesArray::s_info = {"Array", &JSArray::s_info, 0, 0, C
 @end
 */
 
-RegExpResult& RegExpResult::operator=(const RegExpConstructorPrivate& rhs)
-{
-    this->input = rhs.input;
-    this->ovector = rhs.lastOvector();
-    this->lastNumSubPatterns = rhs.lastNumSubPatterns;
-    
-    return *this;            
-}
-
-
 RegExpConstructor::RegExpConstructor(JSGlobalObject* globalObject, Structure* structure)
     : InternalFunction(globalObject, structure)
 {
@@ -129,45 +119,47 @@ void RegExpConstructor::destroy(JSCell* cell)
     jsCast<RegExpConstructor*>(cell)->RegExpConstructor::~RegExpConstructor();
 }
 
-RegExpMatchesArray::RegExpMatchesArray(ExecState* exec)
-    : JSArray(exec->globalData(), exec->lexicalGlobalObject()->regExpMatchesArrayStructure())
-    , m_didFillArrayInstance(false)
-{
-}
-
-void RegExpMatchesArray::finishCreation(JSGlobalData& globalData, const RegExpConstructorPrivate& data)
+void RegExpMatchesArray::finishCreation(JSGlobalData& globalData)
 {
-    Base::finishCreation(globalData, data.lastNumSubPatterns + 1);
-    m_regExpResult = data;
+    Base::finishCreation(globalData, m_regExp->numSubpatterns() + 1);
 }
 
-void RegExpMatchesArray::destroy(JSCell* cell)
+void RegExpMatchesArray::reifyAllProperties(ExecState* exec)
 {
-    jsCast<RegExpMatchesArray*>(cell)->RegExpMatchesArray::~RegExpMatchesArray();
-}
+    ASSERT(m_state != ReifiedAll);
+    ASSERT(m_result);
+    reifyMatchPropertyIfNecessary(exec);
 
-void RegExpMatchesArray::fillArrayInstance(ExecState* exec)
-{
-    unsigned lastNumSubpatterns = m_regExpResult.lastNumSubPatterns;
+    if (unsigned numSubpatterns = m_regExp->numSubpatterns()) {
+        Vector<int, 32> subPatternResults;
+        int position = m_regExp->match(exec->globalData(), m_input->value(exec), m_result.start, &subPatternResults);
+        ASSERT_UNUSED(position, position >= 0 && static_cast<size_t>(position) == m_result.start);
+        ASSERT(m_result.start == static_cast<size_t>(subPatternResults[0]));
+        ASSERT(m_result.end == static_cast<size_t>(subPatternResults[1]));
 
-    for (unsigned i = 0; i <= lastNumSubpatterns; ++i) {
-        int start = m_regExpResult.ovector[2 * i];
-        if (start >= 0)
-            putDirectIndex(exec, i, jsSubstring(exec, m_regExpResult.input, start, m_regExpResult.ovector[2 * i + 1] - start), false);
-        else
-            putDirectIndex(exec, i, jsUndefined(), false);
+        for (unsigned i = 1; i <= numSubpatterns; ++i) {
+            int start = subPatternResults[2 * i];
+            if (start >= 0)
+                putDirectIndex(exec, i, jsSubstring(exec, m_input.get(), start, subPatternResults[2 * i + 1] - start), false);
+            else
+                putDirectIndex(exec, i, jsUndefined(), false);
+        }
     }
 
     PutPropertySlot slot;
-    JSArray::put(this, exec, exec->propertyNames().index, jsNumber(m_regExpResult.ovector[0]), slot);
-    JSArray::put(this, exec, exec->propertyNames().input, jsString(exec, m_regExpResult.input), slot);
+    JSArray::put(this, exec, exec->propertyNames().index, jsNumber(m_result.start), slot);
+    JSArray::put(this, exec, exec->propertyNames().input, m_input.get(), slot);
 
-    m_didFillArrayInstance = true;
+    m_state = ReifiedAll;
 }
 
-JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const
+void RegExpMatchesArray::reifyMatchProperty(ExecState* exec)
 {
-    return RegExpMatchesArray::create(exec, d);
+    ASSERT(m_state == ReifiedNone);
+    ASSERT(m_result);
+    putDirectIndex(exec, 0, jsSubstring(exec, m_input.get(), m_result.start, m_result.end - m_result.start), false);
+    m_state = ReifiedMatch;
 }
 
 JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i) const
index 08a96b5..6a0ceae 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "InternalFunction.h"
 #include "RegExp.h"
+#include "RegExpObject.h"
 #include <wtf/OwnPtr.h>
 
 namespace JSC {
@@ -93,8 +94,7 @@ namespace JSC {
 
         static const ClassInfo s_info;
 
-        void performMatch(JSGlobalData&, RegExp*, const UString&, int startOffset, int& position, int& length, int** ovector = 0);
-        JSObject* arrayOfMatches(ExecState*) const;
+        MatchResult performMatch(JSGlobalData&, RegExp*, const UString&, int startOffset, int** ovector = 0);
 
         void setInput(const UString&);
         const UString& input() const;
@@ -135,23 +135,27 @@ namespace JSC {
       expression matching through the performMatch function. We use cached results to calculate, 
       e.g., RegExp.lastMatch and RegExp.leftParen.
     */
-    ALWAYS_INLINE void RegExpConstructor::performMatch(JSGlobalData& globalData, RegExp* r, const UString& s, int startOffset, int& position, int& length, int** ovector)
+    ALWAYS_INLINE MatchResult RegExpConstructor::performMatch(JSGlobalData& globalData, RegExp* r, const UString& s, int startOffset, int** ovector)
     {
-        position = r->match(globalData, s, startOffset, &d.tempOvector());
+        int position = r->match(globalData, s, startOffset, &d.tempOvector());
 
         if (ovector)
             *ovector = d.tempOvector().data();
 
-        if (position != -1) {
-            ASSERT(!d.tempOvector().isEmpty());
+        if (position == -1)
+            return MatchResult::failed();
 
-            length = d.tempOvector()[1] - d.tempOvector()[0];
+        ASSERT(!d.tempOvector().isEmpty());
+        ASSERT(d.tempOvector()[0] == position);
+        ASSERT(d.tempOvector()[1] >= position);
+        size_t end = d.tempOvector()[1];
 
-            d.input = s;
-            d.lastInput = s;
-            d.changeLastOvector();
-            d.lastNumSubPatterns = r->numSubpatterns();
-        }
+        d.input = s;
+        d.lastInput = s;
+        d.changeLastOvector();
+        d.lastNumSubPatterns = r->numSubpatterns();
+
+        return MatchResult(position, end);
     }
 
 } // namespace JSC
index a3c4497..3b3cca0 100644 (file)
@@ -26,18 +26,28 @@ namespace JSC {
 
     class RegExpMatchesArray : public JSArray {
     private:
-        RegExpMatchesArray(ExecState*);
+        RegExpMatchesArray(JSGlobalData& globalData, JSGlobalObject* globalObject, JSString* input, RegExp* regExp, MatchResult result)
+            : JSArray(globalData, globalObject->regExpMatchesArrayStructure())
+            , m_result(result)
+            , m_state(ReifiedNone)
+        {
+            m_input.set(globalData, this, input);
+            m_regExp.set(globalData, this, regExp);
+        }
+
+        enum ReifiedState { ReifiedNone, ReifiedMatch, ReifiedAll };
 
     public:
         typedef JSArray Base;
 
-        static RegExpMatchesArray* create(ExecState* exec, const RegExpConstructorPrivate& ctorPrivate)
+        static RegExpMatchesArray* create(ExecState* exec, JSString* input, RegExp* regExp, MatchResult result)
         {
-            RegExpMatchesArray* regExp = new (NotNull, allocateCell<RegExpMatchesArray>(*exec->heap())) RegExpMatchesArray(exec);
-            regExp->finishCreation(exec->globalData(), ctorPrivate);
-            return regExp;
+            ASSERT(result);
+            JSGlobalData& globalData = exec->globalData();
+            RegExpMatchesArray* array = new (NotNull, allocateCell<RegExpMatchesArray>(globalData.heap)) RegExpMatchesArray(globalData, exec->lexicalGlobalObject(), input, regExp, result);
+            array->finishCreation(globalData);
+            return array;
         }
-        static void destroy(JSCell*);
 
         static const ClassInfo s_info;
 
@@ -47,77 +57,94 @@ namespace JSC {
         }
 
     protected:
-        void finishCreation(JSGlobalData&, const RegExpConstructorPrivate& data);
+        void finishCreation(JSGlobalData&);
 
     private:
+        ALWAYS_INLINE void reifyAllPropertiesIfNecessary(ExecState* exec)
+        {
+            if (m_state != ReifiedAll)
+                reifyAllProperties(exec);
+        }
+
+        ALWAYS_INLINE void reifyMatchPropertyIfNecessary(ExecState* exec)
+        {
+            if (m_state == ReifiedNone)
+                reifyMatchProperty(exec);
+        }
+
         static bool getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
         {
             RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell);
-            if (!thisObject->m_didFillArrayInstance)
-                thisObject->fillArrayInstance(exec);
+            thisObject->reifyAllPropertiesIfNecessary(exec);
             return JSArray::getOwnPropertySlot(thisObject, exec, propertyName, slot);
         }
 
         static bool getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)
         {
             RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell);
-            if (!thisObject->m_didFillArrayInstance)
-                thisObject->fillArrayInstance(exec);
+            if (propertyName)
+                thisObject->reifyAllPropertiesIfNecessary(exec);
+            else
+                thisObject->reifyMatchPropertyIfNecessary(exec);
             return JSArray::getOwnPropertySlotByIndex(thisObject, exec, propertyName, slot);
         }
 
         static bool getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
         {
             RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(object);
-            if (!thisObject->m_didFillArrayInstance)
-                thisObject->fillArrayInstance(exec);
+            thisObject->reifyAllPropertiesIfNecessary(exec);
             return JSArray::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
         }
 
         static void put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue v, PutPropertySlot& slot)
         {
             RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell);
-            if (!thisObject->m_didFillArrayInstance)
-                thisObject->fillArrayInstance(exec);
+            thisObject->reifyAllPropertiesIfNecessary(exec);
             JSArray::put(thisObject, exec, propertyName, v, slot);
         }
         
         static void putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue v, bool shouldThrow)
         {
             RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell);
-            if (!thisObject->m_didFillArrayInstance)
-                thisObject->fillArrayInstance(exec);
+            thisObject->reifyAllPropertiesIfNecessary(exec);
             JSArray::putByIndex(thisObject, exec, propertyName, v, shouldThrow);
         }
 
         static bool deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName)
         {
             RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell);
-            if (!thisObject->m_didFillArrayInstance)
-                thisObject->fillArrayInstance(exec);
+            thisObject->reifyAllPropertiesIfNecessary(exec);
             return JSArray::deleteProperty(thisObject, exec, propertyName);
         }
 
         static bool deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
         {
             RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(cell);
-            if (!thisObject->m_didFillArrayInstance)
-                thisObject->fillArrayInstance(exec);
+            thisObject->reifyAllPropertiesIfNecessary(exec);
             return JSArray::deletePropertyByIndex(thisObject, exec, propertyName);
         }
 
         static void getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& arr, EnumerationMode mode = ExcludeDontEnumProperties)
         {
             RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(object);
-            if (!thisObject->m_didFillArrayInstance)
-                thisObject->fillArrayInstance(exec);
+            thisObject->reifyAllPropertiesIfNecessary(exec);
             JSArray::getOwnPropertyNames(thisObject, exec, arr, mode);
         }
 
-        void fillArrayInstance(ExecState*);
+        static bool defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool shouldThrow)
+        {
+            RegExpMatchesArray* thisObject = jsCast<RegExpMatchesArray*>(object);
+            thisObject->reifyAllPropertiesIfNecessary(exec);
+            return JSArray::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
+        }
+
+        void reifyAllProperties(ExecState*);
+        void reifyMatchProperty(ExecState*);
 
-        RegExpResult m_regExpResult;
-        bool m_didFillArrayInstance;
+        WriteBarrier<JSString> m_input;
+        WriteBarrier<RegExp> m_regExp;
+        MatchResult m_result;
+        ReifiedState m_state;
 };
 
 }
index d6f2d6e..52db8a9 100644 (file)
@@ -29,6 +29,7 @@
 #include "Lexer.h"
 #include "Lookup.h"
 #include "RegExpConstructor.h"
+#include "RegExpMatchesArray.h"
 #include "RegExpPrototype.h"
 #include "UStringBuilder.h"
 #include "UStringConcatenate.h"
@@ -278,23 +279,20 @@ void RegExpObject::put(JSCell* cell, ExecState* exec, const Identifier& property
 
 JSValue RegExpObject::exec(ExecState* exec, JSString* string)
 {
-    if (match(exec, string))
-        return exec->lexicalGlobalObject()->regExpConstructor()->arrayOfMatches(exec);
+    if (MatchResult result = match(exec, string))
+        return RegExpMatchesArray::create(exec, string, regExp(), result);
     return jsNull();
 }
 
 // Shared implementation used by test and exec.
-bool RegExpObject::match(ExecState* exec, JSString* string)
+MatchResult RegExpObject::match(ExecState* exec, JSString* string)
 {
+    RegExp* regExp = this->regExp();
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
     UString input = string->value(exec);
-    JSGlobalData* globalData = &exec->globalData();
-    if (!regExp()->global()) {
-        int position;
-        int length;
-        regExpConstructor->performMatch(*globalData, m_regExp.get(), input, 0, position, length);
-        return position >= 0;
-    }
+    JSGlobalData& globalData = exec->globalData();
+    if (!regExp->global())
+        return regExpConstructor->performMatch(globalData, regExp, input, 0);
 
     JSValue jsLastIndex = getLastIndex();
     unsigned lastIndex;
@@ -302,27 +300,20 @@ bool RegExpObject::match(ExecState* exec, JSString* string)
         lastIndex = jsLastIndex.asUInt32();
         if (lastIndex > input.length()) {
             setLastIndex(exec, 0);
-            return false;
+            return MatchResult::failed();
         }
     } else {
         double doubleLastIndex = jsLastIndex.toInteger(exec);
         if (doubleLastIndex < 0 || doubleLastIndex > input.length()) {
             setLastIndex(exec, 0);
-            return false;
+            return MatchResult::failed();
         }
         lastIndex = static_cast<unsigned>(doubleLastIndex);
     }
 
-    int position;
-    int length = 0;
-    regExpConstructor->performMatch(*globalData, m_regExp.get(), input, lastIndex, position, length);
-    if (position < 0) {
-        setLastIndex(exec, 0);
-        return false;
-    }
-
-    setLastIndex(exec, position + length);
-    return true;
+    MatchResult result = regExpConstructor->performMatch(globalData, regExp, input, lastIndex);
+    setLastIndex(exec, result.end);
+    return result;
 }
 
 } // namespace JSC
index 7130d2f..c3ece6e 100644 (file)
 
 namespace JSC {
     
+    struct MatchResult {
+        ALWAYS_INLINE MatchResult(size_t start, size_t end)
+            : start(start)
+            , end(end)
+        {
+        }
+        ALWAYS_INLINE static MatchResult failed()
+        {
+            return MatchResult(WTF::notFound, 0);
+        }
+        ALWAYS_INLINE operator bool()
+        {
+            return start != WTF::notFound;
+        }
+        ALWAYS_INLINE bool empty()
+        {
+            return start == end;
+        }
+        size_t start;
+        size_t end;
+    };
+    
     class RegExpObject : public JSNonFinalObject {
     public:
         typedef JSNonFinalObject Base;
@@ -67,8 +89,8 @@ namespace JSC {
             return m_lastIndex.get();
         }
 
-        bool match(ExecState*, JSString* string);
-        JSValue exec(ExecState*, JSString* string);
+        bool test(ExecState* exec, JSString* string) { return match(exec, string); }
+        JSValue exec(ExecState*, JSString*);
 
         static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&);
         static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&);
@@ -95,6 +117,8 @@ namespace JSC {
         JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow);
 
     private:
+        MatchResult match(ExecState*, JSString*);
+
         WriteBarrier<RegExp> m_regExp;
         WriteBarrier<Unknown> m_lastIndex;
         bool m_lastIndexIsWritable;
index ebf5402..fba4de2 100644 (file)
@@ -84,7 +84,7 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncTest(ExecState* exec)
     JSValue thisValue = exec->hostThisValue();
     if (!thisValue.inherits(&RegExpObject::s_info))
         return throwVMTypeError(exec);
-    return JSValue::encode(jsBoolean(asRegExpObject(thisValue)->match(exec, exec->argument(0).toString(exec))));
+    return JSValue::encode(jsBoolean(asRegExpObject(thisValue)->test(exec, exec->argument(0).toString(exec))));
 }
 
 EncodedJSValue JSC_HOST_CALL regExpProtoFuncExec(ExecState* exec)
index 708c1fb..bcd3727 100644 (file)
@@ -35,6 +35,7 @@
 #include "PropertyNameArray.h"
 #include "RegExpCache.h"
 #include "RegExpConstructor.h"
+#include "RegExpMatchesArray.h"
 #include "RegExpObject.h"
 #include <wtf/ASCIICType.h>
 #include <wtf/MathExtras.h>
@@ -404,7 +405,7 @@ static ALWAYS_INLINE JSValue jsSpliceSubstringsWithSeparators(ExecState* exec, J
 
 static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSString* string, const UString& source, RegExp* regExp)
 {
-    int lastIndex = 0;
+    size_t lastIndex = 0;
     unsigned startPosition = 0;
 
     Vector<StringRange, 16> sourceRanges;
@@ -413,21 +414,18 @@ static NEVER_INLINE EncodedJSValue removeUsingRegExpSearch(ExecState* exec, JSSt
     unsigned sourceLen = source.length();
 
     while (true) {
-        int matchIndex;
-        int matchLen = 0;
-        int* ovector;
-        regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector);
-        if (matchIndex < 0)
+        MatchResult result = regExpConstructor->performMatch(*globalData, regExp, source, startPosition);
+        if (!result)
             break;
 
-        if (lastIndex < matchIndex)
-            sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
+        if (lastIndex < result.start)
+            sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
 
-        lastIndex = matchIndex + matchLen;
+        lastIndex = result.end;
         startPosition = lastIndex;
 
         // special case of empty match
-        if (!matchLen) {
+        if (result.empty()) {
             startPosition++;
             if (startPosition > sourceLen)
                 break;
@@ -471,7 +469,7 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
 
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
 
-    int lastIndex = 0;
+    size_t lastIndex = 0;
     unsigned startPosition = 0;
 
     Vector<StringRange, 16> sourceRanges;
@@ -488,16 +486,13 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
         JSGlobalData* globalData = &exec->globalData();
         if (source.is8Bit()) {
             while (true) {
-                int matchIndex;
-                int matchLen = 0;
                 int* ovector;
-                regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector);
-                if (matchIndex < 0)
+                MatchResult result = regExpConstructor->performMatch(*globalData, regExp, source, startPosition, &ovector);
+                if (!result)
                     break;
 
-                sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
+                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
 
-                int completeMatchStart = ovector[0];
                 unsigned i = 0;
                 for (; i < regExp->numSubpatterns() + 1; ++i) {
                     int matchStart = ovector[i * 2];
@@ -509,20 +504,20 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
                         cachedCall.setArgument(i, jsSubstring8(globalData, source, matchStart, matchLen));
                 }
 
-                cachedCall.setArgument(i++, jsNumber(completeMatchStart));
+                cachedCall.setArgument(i++, jsNumber(result.start));
                 cachedCall.setArgument(i++, string);
 
                 cachedCall.setThis(jsUndefined());
-                JSValue result = cachedCall.call();
-                replacements.append(result.toString(cachedCall.newCallFrame(exec))->value(exec));
+                JSValue jsResult = cachedCall.call();
+                replacements.append(jsResult.toString(cachedCall.newCallFrame(exec))->value(exec));
                 if (exec->hadException())
                     break;
 
-                lastIndex = matchIndex + matchLen;
+                lastIndex = result.end;
                 startPosition = lastIndex;
 
                 // special case of empty match
-                if (!matchLen) {
+                if (result.empty()) {
                     startPosition++;
                     if (startPosition > sourceLen)
                         break;
@@ -530,16 +525,13 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
             }
         } else {
             while (true) {
-                int matchIndex;
-                int matchLen = 0;
                 int* ovector;
-                regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector);
-                if (matchIndex < 0)
+                MatchResult result = regExpConstructor->performMatch(*globalData, regExp, source, startPosition, &ovector);
+                if (!result)
                     break;
 
-                sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
+                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
 
-                int completeMatchStart = ovector[0];
                 unsigned i = 0;
                 for (; i < regExp->numSubpatterns() + 1; ++i) {
                     int matchStart = ovector[i * 2];
@@ -551,20 +543,20 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
                         cachedCall.setArgument(i, jsSubstring(globalData, source, matchStart, matchLen));
                 }
 
-                cachedCall.setArgument(i++, jsNumber(completeMatchStart));
+                cachedCall.setArgument(i++, jsNumber(result.start));
                 cachedCall.setArgument(i++, string);
 
                 cachedCall.setThis(jsUndefined());
-                JSValue result = cachedCall.call();
-                replacements.append(result.toString(cachedCall.newCallFrame(exec))->value(exec));
+                JSValue jsResult = cachedCall.call();
+                replacements.append(jsResult.toString(cachedCall.newCallFrame(exec))->value(exec));
                 if (exec->hadException())
                     break;
 
-                lastIndex = matchIndex + matchLen;
+                lastIndex = result.end;
                 startPosition = lastIndex;
 
                 // special case of empty match
-                if (!matchLen) {
+                if (result.empty()) {
                     startPosition++;
                     if (startPosition > sourceLen)
                         break;
@@ -574,17 +566,14 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
     } else {
         JSGlobalData* globalData = &exec->globalData();
         do {
-            int matchIndex;
-            int matchLen = 0;
             int* ovector;
-            regExpConstructor->performMatch(*globalData, regExp, source, startPosition, matchIndex, matchLen, &ovector);
-            if (matchIndex < 0)
+            MatchResult result = regExpConstructor->performMatch(*globalData, regExp, source, startPosition, &ovector);
+            if (!result)
                 break;
 
             if (callType != CallTypeNone) {
-                sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
+                sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
 
-                int completeMatchStart = ovector[0];
                 MarkedArgumentBuffer args;
 
                 for (unsigned i = 0; i < regExp->numSubpatterns() + 1; ++i) {
@@ -597,7 +586,7 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
                         args.append(jsSubstring(exec, source, matchStart, matchLen));
                 }
 
-                args.append(jsNumber(completeMatchStart));
+                args.append(jsNumber(result.start));
                 args.append(string);
 
                 replacements.append(call(exec, replaceValue, callType, callData, jsUndefined(), args).toString(exec)->value(exec));
@@ -605,8 +594,8 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
                     break;
             } else {
                 int replLen = replacementString.length();
-                if (lastIndex < matchIndex || replLen) {
-                    sourceRanges.append(StringRange(lastIndex, matchIndex - lastIndex));
+                if (lastIndex < result.start || replLen) {
+                    sourceRanges.append(StringRange(lastIndex, result.start - lastIndex));
 
                     if (replLen)
                         replacements.append(substituteBackreferences(replacementString, source, ovector, regExp));
@@ -615,11 +604,11 @@ static NEVER_INLINE EncodedJSValue replaceUsingRegExpSearch(ExecState* exec, JSS
                 }
             }
 
-            lastIndex = matchIndex + matchLen;
+            lastIndex = result.end;
             startPosition = lastIndex;
 
             // special case of empty match
-            if (!matchLen) {
+            if (result.empty()) {
                 startPosition++;
                 if (startPosition > sourceLen)
                     break;
@@ -810,17 +799,18 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec)
     JSValue thisValue = exec->hostThisValue();
     if (thisValue.isUndefinedOrNull()) // CheckObjectCoercible
         return throwVMTypeError(exec);
-    UString s = thisValue.toString(exec)->value(exec);
+    JSString* string = thisValue.toString(exec);
+    UString s = string->value(exec);
     JSGlobalData* globalData = &exec->globalData();
 
     JSValue a0 = exec->argument(0);
 
-    RegExp* reg;
+    RegExp* regExp;
     bool global = false;
     if (a0.inherits(&RegExpObject::s_info)) {
         RegExpObject* regExpObject = asRegExpObject(a0);
-        reg = regExpObject->regExp();
-        if ((global = reg->global())) {
+        regExp = regExpObject->regExp();
+        if ((global = regExp->global())) {
             // ES5.1 15.5.4.10 step 8.a.
             regExpObject->setLastIndex(exec, 0);
             if (exec->hadException())
@@ -833,27 +823,25 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncMatch(ExecState* exec)
          *  replaced with the result of the expression new RegExp(regexp).
          *  Per ECMA 15.10.4.1, if a0 is undefined substitute the empty string.
          */
-        reg = RegExp::create(exec->globalData(), a0.isUndefined() ? UString("") : a0.toString(exec)->value(exec), NoFlags);
-        if (!reg->isValid())
-            return throwVMError(exec, createSyntaxError(exec, reg->errorMessage()));
+        regExp = RegExp::create(exec->globalData(), a0.isUndefined() ? UString("") : a0.toString(exec)->value(exec), NoFlags);
+        if (!regExp->isValid())
+            return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage()));
     }
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
-    int pos;
-    int matchLength = 0;
-    regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength);
-    if (!global) {
-        // case without 'g' flag is handled like RegExp.prototype.exec
-        if (pos < 0)
-            return JSValue::encode(jsNull());
-        return JSValue::encode(regExpConstructor->arrayOfMatches(exec));
-    }
+    MatchResult result = regExpConstructor->performMatch(*globalData, regExp, s, 0);
+    // case without 'g' flag is handled like RegExp.prototype.exec
+    if (!global)
+        return JSValue::encode(result ? RegExpMatchesArray::create(exec, string, regExp, result) : jsNull());
 
     // return array of matches
     MarkedArgumentBuffer list;
-    while (pos >= 0) {
-        list.append(jsSubstring(exec, s, pos, matchLength));
-        pos += matchLength == 0 ? 1 : matchLength;
-        regExpConstructor->performMatch(*globalData, reg, s, pos, pos, matchLength);
+    while (result) {
+        size_t end = result.end;
+        size_t length = end - result.start;
+        list.append(jsSubstring(exec, s, result.start, length));
+        if (!length)
+            ++end;
+        result = regExpConstructor->performMatch(*globalData, regExp, s, end);
     }
     if (list.isEmpty()) {
         // if there are no matches at all, it's important to return
@@ -890,10 +878,8 @@ EncodedJSValue JSC_HOST_CALL stringProtoFuncSearch(ExecState* exec)
             return throwVMError(exec, createSyntaxError(exec, reg->errorMessage()));
     }
     RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
-    int pos;
-    int matchLength = 0;
-    regExpConstructor->performMatch(*globalData, reg, s, 0, pos, matchLength);
-    return JSValue::encode(jsNumber(pos));
+    MatchResult result = regExpConstructor->performMatch(*globalData, reg, s, 0);
+    return JSValue::encode(result ? jsNumber(result.start) : jsNumber(-1));
 }
 
 EncodedJSValue JSC_HOST_CALL stringProtoFuncSlice(ExecState* exec)