Regexp matching should incur less call overhead
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 8 Mar 2016 21:15:07 +0000 (21:15 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 8 Mar 2016 21:15:07 +0000 (21:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=155181

Reviewed by Geoffrey Garen.

Previously we had DFG/FTL code call into the DFGOperation, which then called in to
RegExpObject, which then called into createRegExpMatchesArray, which then called into
RegExp, which then called the code generated by Yarr.

Now we have DFG/FTL code call into the DFGOperation, which does all of the things and calls
into code generated by Yarr.

This is another tiny Octane/regexp speed-up.

* JavaScriptCore.xcodeproj/project.pbxproj:
* dfg/DFGOperations.cpp:
* runtime/RegExp.cpp:
(JSC::regExpFlags):
(JSC::RegExp::compile):
(JSC::RegExp::match):
(JSC::RegExp::compileMatchOnly):
(JSC::RegExp::deleteCode):
(JSC::RegExpFunctionalTestCollector::clearRegExp): Deleted.
(JSC::RegExp::compileIfNecessary): Deleted.
(JSC::RegExp::compileIfNecessaryMatchOnly): Deleted.
* runtime/RegExp.h:
* runtime/RegExpInlines.h: Added.
(JSC::RegExpFunctionalTestCollector::clearRegExp):
(JSC::RegExp::compileIfNecessary):
(JSC::RegExp::matchInline):
(JSC::RegExp::compileIfNecessaryMatchOnly):
* runtime/RegExpMatchesArray.cpp:
(JSC::createEmptyRegExpMatchesArray):
(JSC::createStructureImpl):
(JSC::tryCreateUninitializedRegExpMatchesArray): Deleted.
(JSC::createRegExpMatchesArray): Deleted.
* runtime/RegExpMatchesArray.h:
(JSC::tryCreateUninitializedRegExpMatchesArray):
(JSC::createRegExpMatchesArray):
* runtime/RegExpObject.cpp:
(JSC::RegExpObject::put):
(JSC::RegExpObject::exec):
(JSC::RegExpObject::match):
(JSC::getLastIndexAsUnsigned): Deleted.
* runtime/RegExpObject.h:
(JSC::RegExpObject::getLastIndex):
(JSC::RegExpObject::test):
(JSC::RegExpObject::testInline):
* runtime/RegExpObjectInlines.h: Added.
(JSC::getRegExpObjectLastIndexAsUnsigned):
(JSC::RegExpObject::execInline):
(JSC::RegExpObject::matchInline):

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

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/runtime/RegExp.cpp
Source/JavaScriptCore/runtime/RegExp.h
Source/JavaScriptCore/runtime/RegExpInlines.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/RegExpMatchesArray.cpp
Source/JavaScriptCore/runtime/RegExpMatchesArray.h
Source/JavaScriptCore/runtime/RegExpObject.cpp
Source/JavaScriptCore/runtime/RegExpObject.h
Source/JavaScriptCore/runtime/RegExpObjectInlines.h [new file with mode: 0644]

index c90f722..65c8006 100644 (file)
@@ -1,3 +1,58 @@
+2016-03-08  Filip Pizlo  <fpizlo@apple.com>
+
+        Regexp matching should incur less call overhead
+        https://bugs.webkit.org/show_bug.cgi?id=155181
+
+        Reviewed by Geoffrey Garen.
+
+        Previously we had DFG/FTL code call into the DFGOperation, which then called in to
+        RegExpObject, which then called into createRegExpMatchesArray, which then called into
+        RegExp, which then called the code generated by Yarr.
+
+        Now we have DFG/FTL code call into the DFGOperation, which does all of the things and calls
+        into code generated by Yarr.
+
+        This is another tiny Octane/regexp speed-up.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * dfg/DFGOperations.cpp:
+        * runtime/RegExp.cpp:
+        (JSC::regExpFlags):
+        (JSC::RegExp::compile):
+        (JSC::RegExp::match):
+        (JSC::RegExp::compileMatchOnly):
+        (JSC::RegExp::deleteCode):
+        (JSC::RegExpFunctionalTestCollector::clearRegExp): Deleted.
+        (JSC::RegExp::compileIfNecessary): Deleted.
+        (JSC::RegExp::compileIfNecessaryMatchOnly): Deleted.
+        * runtime/RegExp.h:
+        * runtime/RegExpInlines.h: Added.
+        (JSC::RegExpFunctionalTestCollector::clearRegExp):
+        (JSC::RegExp::compileIfNecessary):
+        (JSC::RegExp::matchInline):
+        (JSC::RegExp::compileIfNecessaryMatchOnly):
+        * runtime/RegExpMatchesArray.cpp:
+        (JSC::createEmptyRegExpMatchesArray):
+        (JSC::createStructureImpl):
+        (JSC::tryCreateUninitializedRegExpMatchesArray): Deleted.
+        (JSC::createRegExpMatchesArray): Deleted.
+        * runtime/RegExpMatchesArray.h:
+        (JSC::tryCreateUninitializedRegExpMatchesArray):
+        (JSC::createRegExpMatchesArray):
+        * runtime/RegExpObject.cpp:
+        (JSC::RegExpObject::put):
+        (JSC::RegExpObject::exec):
+        (JSC::RegExpObject::match):
+        (JSC::getLastIndexAsUnsigned): Deleted.
+        * runtime/RegExpObject.h:
+        (JSC::RegExpObject::getLastIndex):
+        (JSC::RegExpObject::test):
+        (JSC::RegExpObject::testInline):
+        * runtime/RegExpObjectInlines.h: Added.
+        (JSC::getRegExpObjectLastIndexAsUnsigned):
+        (JSC::RegExpObject::execInline):
+        (JSC::RegExpObject::matchInline):
+
 2016-03-08  Mark Lam  <mark.lam@apple.com>
 
         synthesizePrototype() and friends need to be followed by exception checks (or equivalent).
index ad379ef..bd83a1f 100644 (file)
                0F7B294B14C3CD2F007C3DB1 /* DFGCapabilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD82E1F14172C2F00179C94 /* DFGCapabilities.h */; };
                0F7B294D14C3CD4C007C3DB1 /* DFGCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC0977E1469EBC400CF2442 /* DFGCommon.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F7C11AD1BC3862C00C74CDB /* CopyBarrier.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F7C11AC1BC3862C00C74CDB /* CopyBarrier.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F7C39FB1C8F629300480151 /* RegExpInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F7C39FA1C8F629300480151 /* RegExpInlines.h */; };
+               0F7C39FD1C8F659500480151 /* RegExpObjectInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F7C39FC1C8F659500480151 /* RegExpObjectInlines.h */; };
                0F8023EA1613832B00A0BA45 /* ByValInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8023E91613832300A0BA45 /* ByValInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F8335B71639C1E6001443B5 /* ArrayAllocationProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */; };
                0F8335B81639C1EA001443B5 /* ArrayAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F79085319A290B200F6310C /* DFGStructureRegistrationPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGStructureRegistrationPhase.cpp; path = dfg/DFGStructureRegistrationPhase.cpp; sourceTree = "<group>"; };
                0F79085419A290B200F6310C /* DFGStructureRegistrationPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGStructureRegistrationPhase.h; path = dfg/DFGStructureRegistrationPhase.h; sourceTree = "<group>"; };
                0F7C11AC1BC3862C00C74CDB /* CopyBarrier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CopyBarrier.h; sourceTree = "<group>"; };
+               0F7C39FA1C8F629300480151 /* RegExpInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegExpInlines.h; sourceTree = "<group>"; };
+               0F7C39FC1C8F659500480151 /* RegExpObjectInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegExpObjectInlines.h; sourceTree = "<group>"; };
                0F8023E91613832300A0BA45 /* ByValInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ByValInfo.h; sourceTree = "<group>"; };
                0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayAllocationProfile.cpp; sourceTree = "<group>"; };
                0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayAllocationProfile.h; sourceTree = "<group>"; };
                                86F75EFC151C062F007C9BA3 /* RegExpCachedResult.h */,
                                BCD202BD0E1706A7002C7E82 /* RegExpConstructor.cpp */,
                                BCD202BE0E1706A7002C7E82 /* RegExpConstructor.h */,
+                               0F7C39FA1C8F629300480151 /* RegExpInlines.h */,
                                A1712B4011C7B235007A5315 /* RegExpKey.h */,
                                86F75EFD151C062F007C9BA3 /* RegExpMatchesArray.cpp */,
                                93CEDDFB0EA91EE600258EBE /* RegExpMatchesArray.h */,
                                F692A87B0255597D01FF60F7 /* RegExpObject.cpp */,
                                F692A87C0255597D01FF60F7 /* RegExpObject.h */,
+                               0F7C39FC1C8F659500480151 /* RegExpObjectInlines.h */,
                                BCD202BF0E1706A7002C7E82 /* RegExpPrototype.cpp */,
                                BCD202C00E1706A7002C7E82 /* RegExpPrototype.h */,
                                0FB7F39115ED8E3800F167B2 /* Reject.h */,
                                0FBC0AE81496C7C700D4FBDD /* DFGExitProfile.h in Headers */,
                                A78A9775179738B8009DF744 /* DFGFailedFinalizer.h in Headers */,
                                A7BFF3C0179868940002F462 /* DFGFiltrationResult.h in Headers */,
+                               0F7C39FB1C8F629300480151 /* RegExpInlines.h in Headers */,
                                A78A9777179738B8009DF744 /* DFGFinalizer.h in Headers */,
                                0F2BDC16151C5D4F00CD8910 /* DFGFixupPhase.h in Headers */,
                                0F9D339717FFC4E60073C2BC /* DFGFlushedAt.h in Headers */,
                                A709F2F017A0AC0400512E98 /* SlowPathCall.h in Headers */,
                                933040040E6A749400786E6A /* SmallStrings.h in Headers */,
                                BC18C4640E16F5CD00B34460 /* SourceCode.h in Headers */,
+                               0F7C39FD1C8F659500480151 /* RegExpObjectInlines.h in Headers */,
                                BC18C4630E16F5CD00B34460 /* SourceProvider.h in Headers */,
                                E49DC16C12EF294E00184A1F /* SourceProviderCache.h in Headers */,
                                E49DC16D12EF295300184A1F /* SourceProviderCacheItem.h in Headers */,
index 61be090..7c01066 100644 (file)
@@ -624,7 +624,7 @@ EncodedJSValue JIT_OPERATION operationRegExpExecString(ExecState* exec, JSGlobal
     VM& vm = globalObject->vm();
     NativeCallFrameTracer tracer(&vm, exec);
     
-    return JSValue::encode(regExpObject->exec(exec, globalObject, argument));
+    return JSValue::encode(regExpObject->execInline(exec, globalObject, argument));
 }
         
 EncodedJSValue JIT_OPERATION operationRegExpExec(ExecState* exec, JSGlobalObject* globalObject, RegExpObject* regExpObject, EncodedJSValue encodedArgument)
@@ -637,7 +637,7 @@ EncodedJSValue JIT_OPERATION operationRegExpExec(ExecState* exec, JSGlobalObject
     JSString* input = argument.toStringOrNull(exec);
     if (!input)
         return JSValue::encode(jsUndefined());
-    return JSValue::encode(regExpObject->exec(exec, globalObject, input));
+    return JSValue::encode(regExpObject->execInline(exec, globalObject, input));
 }
         
 EncodedJSValue JIT_OPERATION operationRegExpExecGeneric(ExecState* exec, JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedArgument)
@@ -662,7 +662,7 @@ size_t JIT_OPERATION operationRegExpTestString(ExecState* exec, JSGlobalObject*
     VM& vm = globalObject->vm();
     NativeCallFrameTracer tracer(&vm, exec);
 
-    return regExpObject->test(exec, globalObject, input);
+    return regExpObject->testInline(exec, globalObject, input);
 }
 
 size_t JIT_OPERATION operationRegExpTest(ExecState* exec, JSGlobalObject* globalObject, RegExpObject* regExpObject, EncodedJSValue encodedArgument)
@@ -675,7 +675,7 @@ size_t JIT_OPERATION operationRegExpTest(ExecState* exec, JSGlobalObject* global
     JSString* input = argument.toStringOrNull(exec);
     if (!input)
         return false;
-    return regExpObject->test(exec, globalObject, input);
+    return regExpObject->testInline(exec, globalObject, input);
 }
 
 size_t JIT_OPERATION operationRegExpTestGeneric(ExecState* exec, JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedArgument)
index 84b37a2..8f9f534 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2001, 2004 Harri Porten (porten@kde.org)
- *  Copyright (c) 2007, 2008 Apple Inc. All rights reserved.
+ *  Copyright (c) 2007, 2008, 2016 Apple Inc. All rights reserved.
  *  Copyright (C) 2009 Torch Mobile, Inc.
  *  Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged
  *
 #include "Lexer.h"
 #include "JSCInlines.h"
 #include "RegExpCache.h"
+#include "RegExpInlines.h"
 #include "Yarr.h"
 #include "YarrJIT.h"
 #include <wtf/Assertions.h>
 
-#define REGEXP_FUNC_TEST_DATA_GEN 0
-
-#if REGEXP_FUNC_TEST_DATA_GEN
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#endif
-
 namespace JSC {
 
 const ClassInfo RegExp::s_info = { "RegExp", 0, 0, CREATE_METHOD_TABLE(RegExp) };
@@ -81,33 +74,6 @@ RegExpFlags regExpFlags(const String& string)
 }
 
 #if REGEXP_FUNC_TEST_DATA_GEN
-class RegExpFunctionalTestCollector {
-    // This class is not thread safe.
-protected:
-    static const char* const s_fileName;
-
-public:
-    static RegExpFunctionalTestCollector* get();
-
-    ~RegExpFunctionalTestCollector();
-
-    void outputOneTest(RegExp*, String, int, int*, int);
-    void clearRegExp(RegExp* regExp)
-    {
-        if (regExp == m_lastRegExp)
-            m_lastRegExp = 0;
-    }
-
-private:
-    RegExpFunctionalTestCollector();
-
-    void outputEscapedString(const String&, bool escapeSlash = false);
-
-    static RegExpFunctionalTestCollector* s_instance;
-    FILE* m_file;
-    RegExp* m_lastRegExp;
-};
-
 const char* const RegExpFunctionalTestCollector::s_fileName = "/tmp/RegExpTestsData";
 RegExpFunctionalTestCollector* RegExpFunctionalTestCollector::s_instance = 0;
 
@@ -320,89 +286,9 @@ void RegExp::compile(VM* vm, Yarr::YarrCharSize charSize)
     m_regExpBytecode = Yarr::byteCompile(pattern, &vm->m_regExpAllocator);
 }
 
-void RegExp::compileIfNecessary(VM& vm, Yarr::YarrCharSize charSize)
-{
-    if (hasCode()) {
-#if ENABLE(YARR_JIT)
-        if (m_state != JITCode)
-            return;
-        if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCode()))
-            return;
-        if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCode()))
-            return;
-#else
-        return;
-#endif
-    }
-
-    compile(&vm, charSize);
-}
-
 int RegExp::match(VM& vm, const String& s, unsigned startOffset, Vector<int, 32>& ovector)
 {
-#if ENABLE(REGEXP_TRACING)
-    m_rtMatchCallCount++;
-    m_rtMatchTotalSubjectStringLen += (double)(s.length() - startOffset);
-#endif
-
-    ASSERT(m_state != ParseError);
-    compileIfNecessary(vm, s.is8Bit() ? Yarr::Char8 : Yarr::Char16);
-
-    int offsetVectorSize = (m_numSubpatterns + 1) * 2;
-    ovector.resize(offsetVectorSize);
-    int* offsetVector = ovector.data();
-
-    int result;
-#if ENABLE(YARR_JIT)
-    if (m_state == JITCode) {
-        if (s.is8Bit())
-            result = m_regExpJITCode.execute(s.characters8(), startOffset, s.length(), offsetVector).start;
-        else
-            result = m_regExpJITCode.execute(s.characters16(), startOffset, s.length(), offsetVector).start;
-#if ENABLE(YARR_JIT_DEBUG)
-        matchCompareWithInterpreter(s, startOffset, offsetVector, result);
-#endif
-    } else
-#endif
-        result = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
-
-    // FIXME: The YARR engine should handle unsigned or size_t length matches.
-    // The YARR Interpreter is "unsigned" clean, while the YARR JIT hasn't been addressed.
-    // The offset vector handling needs to change as well.
-    // Right now we convert a match where the offsets overflowed into match failure.
-    // There are two places in WebCore that call the interpreter directly that need to
-    // have their offsets changed to int as well. They are yarr/RegularExpression.cpp
-    // and inspector/ContentSearchUtilities.cpp
-    if (s.length() > INT_MAX) {
-        bool overflowed = false;
-
-        if (result < -1)
-            overflowed = true;
-
-        for (unsigned i = 0; i <= m_numSubpatterns; i++) {
-            if ((offsetVector[i*2] < -1) || ((offsetVector[i*2] >= 0) && (offsetVector[i*2+1] < -1))) {
-                overflowed = true;
-                offsetVector[i*2] = -1;
-                offsetVector[i*2+1] = -1;
-            }
-        }
-
-        if (overflowed)
-            result = -1;
-    }
-
-    ASSERT(result >= -1);
-
-#if REGEXP_FUNC_TEST_DATA_GEN
-    RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result);
-#endif
-
-#if ENABLE(REGEXP_TRACING)
-    if (result != -1)
-        m_rtMatchFoundCount++;
-#endif
-
-    return result;
+    return matchInline(vm, s, startOffset, ovector);
 }
 
 void RegExp::compileMatchOnly(VM* vm, Yarr::YarrCharSize charSize)
@@ -439,65 +325,9 @@ void RegExp::compileMatchOnly(VM* vm, Yarr::YarrCharSize charSize)
     m_regExpBytecode = Yarr::byteCompile(pattern, &vm->m_regExpAllocator);
 }
 
-void RegExp::compileIfNecessaryMatchOnly(VM& vm, Yarr::YarrCharSize charSize)
-{
-    if (hasCode()) {
-#if ENABLE(YARR_JIT)
-        if (m_state != JITCode)
-            return;
-        if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCodeMatchOnly()))
-            return;
-        if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCodeMatchOnly()))
-            return;
-#else
-        return;
-#endif
-    }
-
-    compileMatchOnly(&vm, charSize);
-}
-
 MatchResult RegExp::match(VM& vm, const String& s, unsigned startOffset)
 {
-#if ENABLE(REGEXP_TRACING)
-    m_rtMatchOnlyCallCount++;
-    m_rtMatchOnlyTotalSubjectStringLen += (double)(s.length() - startOffset);
-#endif
-
-    ASSERT(m_state != ParseError);
-    compileIfNecessaryMatchOnly(vm, s.is8Bit() ? Yarr::Char8 : Yarr::Char16);
-
-#if ENABLE(YARR_JIT)
-    if (m_state == JITCode) {
-        MatchResult result = s.is8Bit() ?
-            m_regExpJITCode.execute(s.characters8(), startOffset, s.length()) :
-            m_regExpJITCode.execute(s.characters16(), startOffset, s.length());
-#if ENABLE(REGEXP_TRACING)
-        if (!result)
-            m_rtMatchOnlyFoundCount++;
-#endif
-        return result;
-    }
-#endif
-
-    int offsetVectorSize = (m_numSubpatterns + 1) * 2;
-    int* offsetVector;
-    Vector<int, 32> nonReturnedOvector;
-    nonReturnedOvector.resize(offsetVectorSize);
-    offsetVector = nonReturnedOvector.data();
-    int r = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
-#if REGEXP_FUNC_TEST_DATA_GEN
-    RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result);
-#endif
-
-    if (r >= 0) {
-#if ENABLE(REGEXP_TRACING)
-        m_rtMatchOnlyFoundCount++;
-#endif
-        return MatchResult(r, reinterpret_cast<unsigned*>(offsetVector)[1]);
-    }
-
-    return MatchResult::failed();
+    return matchInline(vm, s, startOffset);
 }
 
 void RegExp::deleteCode()
index 611c5ab..9c1983f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
+ *  Copyright (C) 2007, 2008, 2009, 2016 Apple Inc. All rights reserved.
  *  Copyright (C) 2009 Torch Mobile, Inc.
  *
  *  This library is free software; you can redistribute it and/or
@@ -64,6 +64,11 @@ public:
 
     JS_EXPORT_PRIVATE int match(VM&, const String&, unsigned startOffset, Vector<int, 32>& ovector);
     JS_EXPORT_PRIVATE MatchResult match(VM&, const String&, unsigned startOffset);
+
+    // Call these versions of the match functions if you're desperate for performance.
+    int matchInline(VM&, const String&, unsigned startOffset, Vector<int, 32>& ovector);
+    MatchResult matchInline(VM&, const String&, unsigned startOffset);
+    
     unsigned numSubpatterns() const { return m_numSubpatterns; }
 
     bool hasCode()
diff --git a/Source/JavaScriptCore/runtime/RegExpInlines.h b/Source/JavaScriptCore/runtime/RegExpInlines.h
new file mode 100644 (file)
index 0000000..fd36b4b
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ *  Copyright (C) 1999-2001, 2004 Harri Porten (porten@kde.org)
+ *  Copyright (c) 2007, 2008, 2016 Apple Inc. All rights reserved.
+ *  Copyright (C) 2009 Torch Mobile, Inc.
+ *  Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef RegExpInlines_h
+#define RegExpInlines_h
+
+#include "RegExp.h"
+#include "JSCInlines.h"
+#include "Yarr.h"
+#include "YarrJIT.h"
+
+#define REGEXP_FUNC_TEST_DATA_GEN 0
+
+#if REGEXP_FUNC_TEST_DATA_GEN
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+namespace JSC {
+
+#if REGEXP_FUNC_TEST_DATA_GEN
+class RegExpFunctionalTestCollector {
+    // This class is not thread safe.
+protected:
+    static const char* const s_fileName;
+
+public:
+    static RegExpFunctionalTestCollector* get();
+
+    ~RegExpFunctionalTestCollector();
+
+    void outputOneTest(RegExp*, String, int, int*, int);
+    void clearRegExp(RegExp* regExp)
+    {
+        if (regExp == m_lastRegExp)
+            m_lastRegExp = 0;
+    }
+
+private:
+    RegExpFunctionalTestCollector();
+
+    void outputEscapedString(const String&, bool escapeSlash = false);
+
+    static RegExpFunctionalTestCollector* s_instance;
+    FILE* m_file;
+    RegExp* m_lastRegExp;
+};
+#endif // REGEXP_FUNC_TEST_DATA_GEN
+
+ALWAYS_INLINE void RegExp::compileIfNecessary(VM& vm, Yarr::YarrCharSize charSize)
+{
+    if (hasCode()) {
+#if ENABLE(YARR_JIT)
+        if (m_state != JITCode)
+            return;
+        if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCode()))
+            return;
+        if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCode()))
+            return;
+#else
+        return;
+#endif
+    }
+
+    compile(&vm, charSize);
+}
+
+ALWAYS_INLINE int RegExp::matchInline(VM& vm, const String& s, unsigned startOffset, Vector<int, 32>& ovector)
+{
+#if ENABLE(REGEXP_TRACING)
+    m_rtMatchCallCount++;
+    m_rtMatchTotalSubjectStringLen += (double)(s.length() - startOffset);
+#endif
+
+    ASSERT(m_state != ParseError);
+    compileIfNecessary(vm, s.is8Bit() ? Yarr::Char8 : Yarr::Char16);
+
+    int offsetVectorSize = (m_numSubpatterns + 1) * 2;
+    ovector.resize(offsetVectorSize);
+    int* offsetVector = ovector.data();
+
+    int result;
+#if ENABLE(YARR_JIT)
+    if (m_state == JITCode) {
+        if (s.is8Bit())
+            result = m_regExpJITCode.execute(s.characters8(), startOffset, s.length(), offsetVector).start;
+        else
+            result = m_regExpJITCode.execute(s.characters16(), startOffset, s.length(), offsetVector).start;
+#if ENABLE(YARR_JIT_DEBUG)
+        matchCompareWithInterpreter(s, startOffset, offsetVector, result);
+#endif
+    } else
+#endif
+        result = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
+
+    // FIXME: The YARR engine should handle unsigned or size_t length matches.
+    // The YARR Interpreter is "unsigned" clean, while the YARR JIT hasn't been addressed.
+    // The offset vector handling needs to change as well.
+    // Right now we convert a match where the offsets overflowed into match failure.
+    // There are two places in WebCore that call the interpreter directly that need to
+    // have their offsets changed to int as well. They are yarr/RegularExpression.cpp
+    // and inspector/ContentSearchUtilities.cpp
+    if (s.length() > INT_MAX) {
+        bool overflowed = false;
+
+        if (result < -1)
+            overflowed = true;
+
+        for (unsigned i = 0; i <= m_numSubpatterns; i++) {
+            if ((offsetVector[i*2] < -1) || ((offsetVector[i*2] >= 0) && (offsetVector[i*2+1] < -1))) {
+                overflowed = true;
+                offsetVector[i*2] = -1;
+                offsetVector[i*2+1] = -1;
+            }
+        }
+
+        if (overflowed)
+            result = -1;
+    }
+
+    ASSERT(result >= -1);
+
+#if REGEXP_FUNC_TEST_DATA_GEN
+    RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result);
+#endif
+
+#if ENABLE(REGEXP_TRACING)
+    if (result != -1)
+        m_rtMatchFoundCount++;
+#endif
+
+    return result;
+}
+
+ALWAYS_INLINE void RegExp::compileIfNecessaryMatchOnly(VM& vm, Yarr::YarrCharSize charSize)
+{
+    if (hasCode()) {
+#if ENABLE(YARR_JIT)
+        if (m_state != JITCode)
+            return;
+        if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCodeMatchOnly()))
+            return;
+        if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCodeMatchOnly()))
+            return;
+#else
+        return;
+#endif
+    }
+
+    compileMatchOnly(&vm, charSize);
+}
+
+ALWAYS_INLINE MatchResult RegExp::matchInline(VM& vm, const String& s, unsigned startOffset)
+{
+#if ENABLE(REGEXP_TRACING)
+    m_rtMatchOnlyCallCount++;
+    m_rtMatchOnlyTotalSubjectStringLen += (double)(s.length() - startOffset);
+#endif
+
+    ASSERT(m_state != ParseError);
+    compileIfNecessaryMatchOnly(vm, s.is8Bit() ? Yarr::Char8 : Yarr::Char16);
+
+#if ENABLE(YARR_JIT)
+    if (m_state == JITCode) {
+        MatchResult result = s.is8Bit() ?
+            m_regExpJITCode.execute(s.characters8(), startOffset, s.length()) :
+            m_regExpJITCode.execute(s.characters16(), startOffset, s.length());
+#if ENABLE(REGEXP_TRACING)
+        if (!result)
+            m_rtMatchOnlyFoundCount++;
+#endif
+        return result;
+    }
+#endif
+
+    int offsetVectorSize = (m_numSubpatterns + 1) * 2;
+    int* offsetVector;
+    Vector<int, 32> nonReturnedOvector;
+    nonReturnedOvector.resize(offsetVectorSize);
+    offsetVector = nonReturnedOvector.data();
+    int r = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
+#if REGEXP_FUNC_TEST_DATA_GEN
+    RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result);
+#endif
+
+    if (r >= 0) {
+#if ENABLE(REGEXP_TRACING)
+        m_rtMatchOnlyFoundCount++;
+#endif
+        return MatchResult(r, reinterpret_cast<unsigned*>(offsetVector)[1]);
+    }
+
+    return MatchResult::failed();
+}
+
+} // namespace JSC
+
+#endif // RegExpInlines_h
+
index 247f93d..a4cfdd9 100644 (file)
 #include "config.h"
 #include "RegExpMatchesArray.h"
 
-#include "ButterflyInlines.h"
-#include "JSCInlines.h"
-
 namespace JSC {
 
-static const PropertyOffset indexPropertyOffset = 100;
-static const PropertyOffset inputPropertyOffset = 101;
-
-static JSArray* tryCreateUninitializedRegExpMatchesArray(VM& vm, Structure* structure, unsigned initialLength)
-{
-    unsigned vectorLength = std::max(BASE_VECTOR_LEN, initialLength);
-    if (vectorLength > MAX_STORAGE_VECTOR_LENGTH)
-        return 0;
-
-    void* temp;
-    if (!vm.heap.tryAllocateStorage(0, Butterfly::totalSize(0, structure->outOfLineCapacity(), true, vectorLength * sizeof(EncodedJSValue)), &temp))
-        return 0;
-    Butterfly* butterfly = Butterfly::fromBase(temp, 0, structure->outOfLineCapacity());
-    butterfly->setVectorLength(vectorLength);
-    butterfly->setPublicLength(initialLength);
-
-    return JSArray::createWithButterfly(vm, structure, butterfly);
-}
-
-JSArray* createRegExpMatchesArray(
-    ExecState* exec, JSGlobalObject* globalObject, JSString* input, RegExp* regExp,
-    unsigned startOffset, MatchResult& result)
-{
-    SamplingRegion samplingRegion("createRegExpMatchesArray");
-    
-    VM& vm = globalObject->vm();
-    
-    Vector<int, 32> subpatternResults;
-    int position = regExp->match(vm, input->value(exec), startOffset, subpatternResults);
-    if (position == -1) {
-        result = MatchResult::failed();
-        return nullptr;
-    }
-
-    result.start = position;
-    result.end = subpatternResults[1];
-    
-    JSArray* array;
-
-    // FIXME: This should handle array allocation errors gracefully.
-    // https://bugs.webkit.org/show_bug.cgi?id=155144
-    
-    if (UNLIKELY(globalObject->isHavingABadTime())) {
-        array = JSArray::tryCreateUninitialized(vm, globalObject->regExpMatchesArrayStructure(), regExp->numSubpatterns() + 1);
-        
-        array->initializeIndex(vm, 0, jsSubstringOfResolved(vm, input, result.start, result.end - result.start));
-        
-        if (unsigned numSubpatterns = regExp->numSubpatterns()) {
-            for (unsigned i = 1; i <= numSubpatterns; ++i) {
-                int start = subpatternResults[2 * i];
-                if (start >= 0)
-                    array->initializeIndex(vm, i, JSRopeString::createSubstringOfResolved(vm, input, start, subpatternResults[2 * i + 1] - start));
-                else
-                    array->initializeIndex(vm, i, jsUndefined());
-            }
-        }
-    } else {
-        array = tryCreateUninitializedRegExpMatchesArray(vm, globalObject->regExpMatchesArrayStructure(), regExp->numSubpatterns() + 1);
-        RELEASE_ASSERT(array);
-        
-        array->initializeIndex(vm, 0, jsSubstringOfResolved(vm, input, result.start, result.end - result.start), ArrayWithContiguous);
-        
-        if (unsigned numSubpatterns = regExp->numSubpatterns()) {
-            for (unsigned i = 1; i <= numSubpatterns; ++i) {
-                int start = subpatternResults[2 * i];
-                if (start >= 0)
-                    array->initializeIndex(vm, i, JSRopeString::createSubstringOfResolved(vm, input, start, subpatternResults[2 * i + 1] - start), ArrayWithContiguous);
-                else
-                    array->initializeIndex(vm, i, jsUndefined(), ArrayWithContiguous);
-            }
-        }
-    }
-
-    array->putDirect(vm, indexPropertyOffset, jsNumber(result.start));
-    array->putDirect(vm, inputPropertyOffset, input);
-
-    return array;
-}
-
 JSArray* createEmptyRegExpMatchesArray(JSGlobalObject* globalObject, JSString* input, RegExp* regExp)
 {
     VM& vm = globalObject->vm();
@@ -139,8 +57,8 @@ JSArray* createEmptyRegExpMatchesArray(JSGlobalObject* globalObject, JSString* i
         }
     }
 
-    array->putDirect(vm, indexPropertyOffset, jsNumber(-1));
-    array->putDirect(vm, inputPropertyOffset, input);
+    array->putDirect(vm, RegExpMatchesArrayIndexPropertyOffset, jsNumber(-1));
+    array->putDirect(vm, RegExpMatchesArrayInputPropertyOffset, input);
     return array;
 }
 
@@ -149,9 +67,9 @@ static Structure* createStructureImpl(VM& vm, JSGlobalObject* globalObject, Inde
     Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType);
     PropertyOffset offset;
     structure = Structure::addPropertyTransition(vm, structure, vm.propertyNames->index, 0, offset);
-    ASSERT(offset == indexPropertyOffset);
+    ASSERT(offset == RegExpMatchesArrayIndexPropertyOffset);
     structure = Structure::addPropertyTransition(vm, structure, vm.propertyNames->input, 0, offset);
-    ASSERT(offset == inputPropertyOffset);
+    ASSERT(offset == RegExpMatchesArrayInputPropertyOffset);
     return structure;
 }
 
index d470e7c..b034e97 100644 (file)
 #ifndef RegExpMatchesArray_h
 #define RegExpMatchesArray_h
 
+#include "ButterflyInlines.h"
 #include "JSArray.h"
+#include "JSCInlines.h"
 #include "JSGlobalObject.h"
+#include "RegExpInlines.h"
 #include "RegExpObject.h"
 
 namespace JSC {
 
-JSArray* createRegExpMatchesArray(ExecState*, JSGlobalObject*, JSString*, RegExp*, unsigned startOffset, MatchResult&);
+static const PropertyOffset RegExpMatchesArrayIndexPropertyOffset = 100;
+static const PropertyOffset RegExpMatchesArrayInputPropertyOffset = 101;
+
+ALWAYS_INLINE JSArray* tryCreateUninitializedRegExpMatchesArray(VM& vm, Structure* structure, unsigned initialLength)
+{
+    unsigned vectorLength = std::max(BASE_VECTOR_LEN, initialLength);
+    if (vectorLength > MAX_STORAGE_VECTOR_LENGTH)
+        return 0;
+
+    void* temp;
+    if (!vm.heap.tryAllocateStorage(0, Butterfly::totalSize(0, structure->outOfLineCapacity(), true, vectorLength * sizeof(EncodedJSValue)), &temp))
+        return 0;
+    Butterfly* butterfly = Butterfly::fromBase(temp, 0, structure->outOfLineCapacity());
+    butterfly->setVectorLength(vectorLength);
+    butterfly->setPublicLength(initialLength);
+
+    return JSArray::createWithButterfly(vm, structure, butterfly);
+}
+
+ALWAYS_INLINE JSArray* createRegExpMatchesArray(
+    VM& vm, JSGlobalObject* globalObject, JSString* input, const String& inputValue,
+    RegExp* regExp, unsigned startOffset, MatchResult& result)
+{
+    SamplingRegion samplingRegion("createRegExpMatchesArray");
+    
+    Vector<int, 32> subpatternResults;
+    int position = regExp->matchInline(vm, inputValue, startOffset, subpatternResults);
+    if (position == -1) {
+        result = MatchResult::failed();
+        return nullptr;
+    }
+
+    result.start = position;
+    result.end = subpatternResults[1];
+    
+    JSArray* array;
+
+    // FIXME: This should handle array allocation errors gracefully.
+    // https://bugs.webkit.org/show_bug.cgi?id=155144
+    
+    if (UNLIKELY(globalObject->isHavingABadTime())) {
+        array = JSArray::tryCreateUninitialized(vm, globalObject->regExpMatchesArrayStructure(), regExp->numSubpatterns() + 1);
+        
+        array->initializeIndex(vm, 0, jsSubstringOfResolved(vm, input, result.start, result.end - result.start));
+        
+        if (unsigned numSubpatterns = regExp->numSubpatterns()) {
+            for (unsigned i = 1; i <= numSubpatterns; ++i) {
+                int start = subpatternResults[2 * i];
+                if (start >= 0)
+                    array->initializeIndex(vm, i, JSRopeString::createSubstringOfResolved(vm, input, start, subpatternResults[2 * i + 1] - start));
+                else
+                    array->initializeIndex(vm, i, jsUndefined());
+            }
+        }
+    } else {
+        array = tryCreateUninitializedRegExpMatchesArray(vm, globalObject->regExpMatchesArrayStructure(), regExp->numSubpatterns() + 1);
+        RELEASE_ASSERT(array);
+        
+        array->initializeIndex(vm, 0, jsSubstringOfResolved(vm, input, result.start, result.end - result.start), ArrayWithContiguous);
+        
+        if (unsigned numSubpatterns = regExp->numSubpatterns()) {
+            for (unsigned i = 1; i <= numSubpatterns; ++i) {
+                int start = subpatternResults[2 * i];
+                if (start >= 0)
+                    array->initializeIndex(vm, i, JSRopeString::createSubstringOfResolved(vm, input, start, subpatternResults[2 * i + 1] - start), ArrayWithContiguous);
+                else
+                    array->initializeIndex(vm, i, jsUndefined(), ArrayWithContiguous);
+            }
+        }
+    }
+
+    array->putDirect(vm, RegExpMatchesArrayIndexPropertyOffset, jsNumber(result.start));
+    array->putDirect(vm, RegExpMatchesArrayInputPropertyOffset, input);
+
+    return array;
+}
+
 inline JSArray* createRegExpMatchesArray(ExecState* exec, JSGlobalObject* globalObject, JSString* string, RegExp* regExp, unsigned startOffset)
 {
     MatchResult ignoredResult;
-    return createRegExpMatchesArray(exec, globalObject, string, regExp, startOffset, ignoredResult);
+    return createRegExpMatchesArray(globalObject->vm(), globalObject, string, string->value(exec), regExp, startOffset, ignoredResult);
 }
 JSArray* createEmptyRegExpMatchesArray(JSGlobalObject*, JSString*, RegExp*);
 Structure* createRegExpMatchesArrayStructure(VM&, JSGlobalObject*);
index 2c7d352..54caa6c 100644 (file)
@@ -32,6 +32,7 @@
 #include "JSCInlines.h"
 #include "RegExpConstructor.h"
 #include "RegExpMatchesArray.h"
+#include "RegExpObjectInlines.h"
 #include "RegExpPrototype.h"
 #include <wtf/text/StringBuilder.h>
 
@@ -159,77 +160,15 @@ void RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName,
     Base::put(cell, exec, propertyName, value, slot);
 }
 
-ALWAYS_INLINE unsigned getLastIndexAsUnsigned(
-    ExecState* exec, RegExpObject* regExpObject, const String& input)
-{
-    JSValue jsLastIndex = regExpObject->getLastIndex();
-    unsigned lastIndex;
-    if (LIKELY(jsLastIndex.isUInt32())) {
-        lastIndex = jsLastIndex.asUInt32();
-        if (lastIndex > input.length()) {
-            regExpObject->setLastIndex(exec, 0);
-            return UINT_MAX;
-        }
-    } else {
-        double doubleLastIndex = jsLastIndex.toInteger(exec);
-        if (doubleLastIndex < 0 || doubleLastIndex > input.length()) {
-            regExpObject->setLastIndex(exec, 0);
-            return UINT_MAX;
-        }
-        lastIndex = static_cast<unsigned>(doubleLastIndex);
-    }
-    return lastIndex;
-}
-
 JSValue RegExpObject::exec(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
 {
-    RegExp* regExp = this->regExp();
-    RegExpConstructor* regExpConstructor = globalObject->regExpConstructor();
-    String input = string->value(exec); // FIXME: Handle errors. https://bugs.webkit.org/show_bug.cgi?id=155145
-    VM& vm = globalObject->vm();
-
-    if (!regExp->global()) {
-        MatchResult result;
-        JSArray* array = createRegExpMatchesArray(exec, globalObject, string, regExp, 0, result);
-        if (!array)
-            return jsNull();
-        regExpConstructor->recordMatch(vm, regExp, string, result);
-        return array;
-    }
-
-    unsigned lastIndex = getLastIndexAsUnsigned(exec, this, input);
-    if (lastIndex == UINT_MAX)
-        return jsNull();
-    
-    MatchResult result;
-    JSArray* array =
-        createRegExpMatchesArray(exec, globalObject, string, regExp, lastIndex, result);
-    if (!array) {
-        setLastIndex(exec, 0);
-        return jsNull();
-    }
-    setLastIndex(exec, result.end);
-    regExpConstructor->recordMatch(vm, regExp, string, result);
-    return array;
+    return execInline(exec, globalObject, string);
 }
 
 // Shared implementation used by test and exec.
 MatchResult RegExpObject::match(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
 {
-    RegExp* regExp = this->regExp();
-    RegExpConstructor* regExpConstructor = globalObject->regExpConstructor();
-    String input = string->value(exec); // FIXME: Handle errors. https://bugs.webkit.org/show_bug.cgi?id=155145
-    VM& vm = globalObject->vm();
-    if (!regExp->global())
-        return regExpConstructor->performMatch(vm, regExp, string, input, 0);
-
-    unsigned lastIndex = getLastIndexAsUnsigned(exec, this, input);
-    if (lastIndex == UINT_MAX)
-        return MatchResult::failed();
-    
-    MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex);
-    setLastIndex(exec, result.end);
-    return result;
+    return matchInline(exec, globalObject, string);
 }
 
 } // namespace JSC
index eae0183..6e715d5 100644 (file)
@@ -67,7 +67,9 @@ public:
     }
 
     bool test(ExecState* exec, JSGlobalObject* globalObject, JSString* string) { return !!match(exec, globalObject, string); }
+    bool testInline(ExecState* exec, JSGlobalObject* globalObject, JSString* string) { return !!matchInline(exec, globalObject, string); }
     JSValue exec(ExecState*, JSGlobalObject*, JSString*);
+    JSValue execInline(ExecState*, JSGlobalObject*, JSString*);
 
     static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
     static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
@@ -103,6 +105,7 @@ protected:
 
 private:
     MatchResult match(ExecState*, JSGlobalObject*, JSString*);
+    MatchResult matchInline(ExecState*, JSGlobalObject*, JSString*);
 
     WriteBarrier<RegExp> m_regExp;
     WriteBarrier<Unknown> m_lastIndex;
diff --git a/Source/JavaScriptCore/runtime/RegExpObjectInlines.h b/Source/JavaScriptCore/runtime/RegExpObjectInlines.h
new file mode 100644 (file)
index 0000000..ecc3350
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
+ *  Copyright (C) 2003, 2007, 2008, 2012, 2016 Apple Inc. All Rights Reserved.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef RegExpObjectInlines_h
+#define RegExpObjectInlines_h
+
+#include "ButterflyInlines.h"
+#include "Error.h"
+#include "ExceptionHelpers.h"
+#include "JSArray.h"
+#include "JSGlobalObject.h"
+#include "JSString.h"
+#include "JSCInlines.h"
+#include "RegExpConstructor.h"
+#include "RegExpMatchesArray.h"
+#include "RegExpObject.h"
+
+namespace JSC {
+
+ALWAYS_INLINE unsigned getRegExpObjectLastIndexAsUnsigned(
+    ExecState* exec, RegExpObject* regExpObject, const String& input)
+{
+    JSValue jsLastIndex = regExpObject->getLastIndex();
+    unsigned lastIndex;
+    if (LIKELY(jsLastIndex.isUInt32())) {
+        lastIndex = jsLastIndex.asUInt32();
+        if (lastIndex > input.length()) {
+            regExpObject->setLastIndex(exec, 0);
+            return UINT_MAX;
+        }
+    } else {
+        double doubleLastIndex = jsLastIndex.toInteger(exec);
+        if (doubleLastIndex < 0 || doubleLastIndex > input.length()) {
+            regExpObject->setLastIndex(exec, 0);
+            return UINT_MAX;
+        }
+        lastIndex = static_cast<unsigned>(doubleLastIndex);
+    }
+    return lastIndex;
+}
+
+JSValue RegExpObject::execInline(ExecState* exec, JSGlobalObject* globalObject, JSString* string)
+{
+    RegExp* regExp = this->regExp();
+    RegExpConstructor* regExpConstructor = globalObject->regExpConstructor();
+    String input = string->value(exec); // FIXME: Handle errors. https://bugs.webkit.org/show_bug.cgi?id=155145
+    VM& vm = globalObject->vm();
+
+    bool global = regExp->global();
+
+    unsigned lastIndex;
+    if (global) {
+        lastIndex = getRegExpObjectLastIndexAsUnsigned(exec, this, input);
+        if (lastIndex == UINT_MAX)
+            return jsNull();
+    } else
+        lastIndex = 0;
+    
+    MatchResult result;
+    JSArray* array =
+        createRegExpMatchesArray(vm, globalObject, string, input, regExp, lastIndex, result);
+    if (!array) {
+        if (global)
+            setLastIndex(exec, 0);
+        return jsNull();
+    }
+    if (global)
+        setLastIndex(exec, result.end);
+    regExpConstructor->recordMatch(vm, regExp, string, result);
+    return array;
+}
+
+// Shared implementation used by test and exec.
+MatchResult RegExpObject::matchInline(
+    ExecState* exec, JSGlobalObject* globalObject, JSString* string)
+{
+    RegExp* regExp = this->regExp();
+    RegExpConstructor* regExpConstructor = globalObject->regExpConstructor();
+    String input = string->value(exec); // FIXME: Handle errors. https://bugs.webkit.org/show_bug.cgi?id=155145
+    VM& vm = globalObject->vm();
+    if (!regExp->global())
+        return regExpConstructor->performMatch(vm, regExp, string, input, 0);
+
+    unsigned lastIndex = getRegExpObjectLastIndexAsUnsigned(exec, this, input);
+    if (lastIndex == UINT_MAX)
+        return MatchResult::failed();
+    
+    MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex);
+    setLastIndex(exec, result.end);
+    return result;
+}
+
+} // namespace JSC
+
+#endif // RegExpObjectInlines_h
+