2011-01-18 Darin Adler <darin@apple.com>
authordarin@apple.com <darin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Jan 2011 01:53:50 +0000 (01:53 +0000)
committerdarin@apple.com <darin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Jan 2011 01:53:50 +0000 (01:53 +0000)
        Reviewed by Geoffrey Garen.

        Stack overflow when converting an Error object to string
        https://bugs.webkit.org/show_bug.cgi?id=46410

        * fast/js/script-tests/toString-recursion.js: Added.
        * fast/js/toString-recursion-expected.txt: Added.
        * fast/js/toString-recursion.html: Added.
2011-01-18  Darin Adler  <darin@apple.com>

        Reviewed by Geoffrey Garen.

        Stack overflow when converting an Error object to string
        https://bugs.webkit.org/show_bug.cgi?id=46410

        * Android.mk: Added StringRecursionChecker.cpp and
        StringRecursionChecker.h.
        * CMakeLists.txt: Ditto.
        * GNUmakefile.am: Ditto.
        * JavaScriptCore.gypi: Ditto.
        * JavaScriptCore.pro: Ditto.
        * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: Ditto.
        * JavaScriptCore.xcodeproj/project.pbxproj: Ditto.

        * runtime/ArrayPrototype.cpp:
        (JSC::arrayProtoFuncToString): Use StringRecursionChecker instead
        of the older hand-written code to do the same thing.
        (JSC::arrayProtoFuncToLocaleString): Ditto.
        (JSC::arrayProtoFuncJoin): Ditto.

        * runtime/ErrorPrototype.cpp:
        (JSC::errorProtoFuncToString): Use StringRecursionChecker.

        * runtime/JSGlobalData.h: Renamed arrayVisitedElements to
        stringRecursionCheckVisitedObjects.

        * runtime/RegExpPrototype.cpp:
        (JSC::regExpProtoFuncToString): Use StringRecursionChecker.

        * runtime/StringRecursionChecker.cpp: Added.
        * runtime/StringRecursionChecker.h: Added.

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

18 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/js/script-tests/toString-recursion.js [new file with mode: 0644]
LayoutTests/fast/js/toString-recursion-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/toString-recursion.html [new file with mode: 0644]
Source/JavaScriptCore/Android.mk
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/GNUmakefile.am
Source/JavaScriptCore/JavaScriptCore.gypi
Source/JavaScriptCore/JavaScriptCore.pro
Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/runtime/ArrayPrototype.cpp
Source/JavaScriptCore/runtime/ErrorPrototype.cpp
Source/JavaScriptCore/runtime/JSGlobalData.h
Source/JavaScriptCore/runtime/RegExpPrototype.cpp
Source/JavaScriptCore/runtime/StringRecursionChecker.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/StringRecursionChecker.h [new file with mode: 0644]

index d07a6b4..b327f1c 100644 (file)
@@ -1,3 +1,14 @@
+2011-01-18  Darin Adler  <darin@apple.com>
+
+        Reviewed by Geoffrey Garen.
+
+        Stack overflow when converting an Error object to string
+        https://bugs.webkit.org/show_bug.cgi?id=46410
+
+        * fast/js/script-tests/toString-recursion.js: Added.
+        * fast/js/toString-recursion-expected.txt: Added.
+        * fast/js/toString-recursion.html: Added.
+
 2011-01-19  Oliver Hunt  <oliver@apple.com>
 
         Reviewed by Gavin Barraclough.
diff --git a/LayoutTests/fast/js/script-tests/toString-recursion.js b/LayoutTests/fast/js/script-tests/toString-recursion.js
new file mode 100644 (file)
index 0000000..42b3a7e
--- /dev/null
@@ -0,0 +1,14 @@
+description(
+"This test checks that toString() does not have a problem when an object has itself as a property."
+);
+
+// Array (elements)
+shouldBe("var array = []; array[0] = array; array + ''", "''");
+
+// Error (name, message)
+shouldBe("var error = new Error; error.name = error; error.message = error; error + ''", "': '");
+
+// RegExp (source)
+shouldBe("var regexp = /a/; regexp.source = regexp; regexp + ''", "'/a/'");
+
+var successfullyParsed = true;
diff --git a/LayoutTests/fast/js/toString-recursion-expected.txt b/LayoutTests/fast/js/toString-recursion-expected.txt
new file mode 100644 (file)
index 0000000..8a00e9a
--- /dev/null
@@ -0,0 +1,12 @@
+This test checks that toString() does not have a problem when an object has itself as a property.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS var array = []; array[0] = array; array + '' is ''
+PASS var error = new Error; error.name = error; error.message = error; error + '' is ': '
+PASS var regexp = /a/; regexp.source = regexp; regexp + '' is '/a/'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/js/toString-recursion.html b/LayoutTests/fast/js/toString-recursion.html
new file mode 100644 (file)
index 0000000..df349c9
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="resources/js-test-style.css">
+<script src="resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/toString-recursion.js"></script>
+<script src="resources/js-test-post.js"></script>
+</body>
+</html>
index 3c01aa0..80c8b61 100644 (file)
@@ -151,6 +151,7 @@ LOCAL_SRC_FILES := \
        runtime/StringConstructor.cpp \
        runtime/StringObject.cpp \
        runtime/StringPrototype.cpp \
+       runtime/StringRecursionChecker.cpp \
        runtime/Structure.cpp \
        runtime/StructureChain.cpp \
        runtime/TimeoutChecker.cpp \
index 29de8a7..674d272 100644 (file)
@@ -163,6 +163,7 @@ SET(JavaScriptCore_SOURCES
     runtime/StringConstructor.cpp
     runtime/StringObject.cpp
     runtime/StringPrototype.cpp
+    runtime/StringRecursionChecker.cpp
     runtime/Structure.cpp
     runtime/StructureChain.cpp
     runtime/TimeoutChecker.cpp
index b2184e5..a1f5f73 100644 (file)
@@ -1,3 +1,37 @@
+2011-01-18  Darin Adler  <darin@apple.com>
+
+        Reviewed by Geoffrey Garen.
+
+        Stack overflow when converting an Error object to string
+        https://bugs.webkit.org/show_bug.cgi?id=46410
+
+        * Android.mk: Added StringRecursionChecker.cpp and
+        StringRecursionChecker.h.
+        * CMakeLists.txt: Ditto.
+        * GNUmakefile.am: Ditto.
+        * JavaScriptCore.gypi: Ditto.
+        * JavaScriptCore.pro: Ditto.
+        * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: Ditto.
+        * JavaScriptCore.xcodeproj/project.pbxproj: Ditto.
+
+        * runtime/ArrayPrototype.cpp:
+        (JSC::arrayProtoFuncToString): Use StringRecursionChecker instead
+        of the older hand-written code to do the same thing.
+        (JSC::arrayProtoFuncToLocaleString): Ditto.
+        (JSC::arrayProtoFuncJoin): Ditto.
+
+        * runtime/ErrorPrototype.cpp:
+        (JSC::errorProtoFuncToString): Use StringRecursionChecker.
+
+        * runtime/JSGlobalData.h: Renamed arrayVisitedElements to
+        stringRecursionCheckVisitedObjects.
+
+        * runtime/RegExpPrototype.cpp:
+        (JSC::regExpProtoFuncToString): Use StringRecursionChecker.
+
+        * runtime/StringRecursionChecker.cpp: Added.
+        * runtime/StringRecursionChecker.h: Added.
+
 2011-01-19  Oliver Hunt  <oliver@apple.com>
 
         Reviewed by Gavin Barraclough.
index 4c8d204..7272471 100644 (file)
@@ -404,6 +404,8 @@ javascriptcore_sources += \
        Source/JavaScriptCore/runtime/StringObjectThatMasqueradesAsUndefined.h \
        Source/JavaScriptCore/runtime/StringPrototype.cpp \
        Source/JavaScriptCore/runtime/StringPrototype.h \
+       Source/JavaScriptCore/runtime/StringRecursionChecker.cpp \
+       Source/JavaScriptCore/runtime/StringRecursionChecker.h \
        Source/JavaScriptCore/runtime/StructureChain.cpp \
        Source/JavaScriptCore/runtime/StructureChain.h \
        Source/JavaScriptCore/runtime/Structure.cpp \
index a08b439..6ca18a0 100644 (file)
             'runtime/StringObjectThatMasqueradesAsUndefined.h',
             'runtime/StringPrototype.cpp',
             'runtime/StringPrototype.h',
+            'runtime/StringRecursionChecker.cpp',
+            'runtime/StringRecursionChecker.h',
             'runtime/Structure.cpp',
             'runtime/Structure.h',
             'runtime/StructureChain.cpp',
index ca420d2..9d254b7 100644 (file)
@@ -205,6 +205,7 @@ SOURCES += \
     runtime/StringConstructor.cpp \
     runtime/StringObject.cpp \
     runtime/StringPrototype.cpp \
+    runtime/StringRecursionChecker.cpp \
     runtime/StructureChain.cpp \
     runtime/Structure.cpp \
     runtime/TimeoutChecker.cpp \
index 1ddc251..12310d8 100644 (file)
                                >
                        </File>
                        <File
+                               RelativePath="..\..\runtime\StringRecursionChecker.cpp"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\..\runtime\StringRecursionChecker.h"
+                               >
+                       </File>
+                       <File
                                RelativePath="..\..\runtime\Structure.cpp"
                                >
                        </File>
index 0f9b4ab..358e38c 100644 (file)
                932F5BEA0822A1C700736975 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 932F5BD90822A1C700736975 /* JavaScriptCore.framework */; };
                933040040E6A749400786E6A /* SmallStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = 93303FEA0E6A72C000786E6A /* SmallStrings.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9330402C0E6A764000786E6A /* SmallStrings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93303FE80E6A72B500786E6A /* SmallStrings.cpp */; };
+               9335F24D12E6765B002B5553 /* StringRecursionChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93345A8712D838C400302BE3 /* StringRecursionChecker.cpp */; };
                933F5CDC1269229B0049191E /* NullPtr.h in Headers */ = {isa = PBXBuildFile; fileRef = 933F5CDB126922690049191E /* NullPtr.h */; settings = {ATTRIBUTES = (Private, ); }; };
                937013480CA97E0E00FA14D3 /* pcre_ucp_searchfuncs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 937013470CA97E0E00FA14D3 /* pcre_ucp_searchfuncs.cpp */; settings = {COMPILER_FLAGS = "-Wno-sign-compare"; }; };
                93854A9A12C93D3B00DAAF77 /* NullPtr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 93854A9912C93D3B00DAAF77 /* NullPtr.cpp */; };
                932F5BE10822A1C700736975 /* jsc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jsc; sourceTree = BUILT_PRODUCTS_DIR; };
                93303FE80E6A72B500786E6A /* SmallStrings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SmallStrings.cpp; sourceTree = "<group>"; };
                93303FEA0E6A72C000786E6A /* SmallStrings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmallStrings.h; sourceTree = "<group>"; };
+               93345A8712D838C400302BE3 /* StringRecursionChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringRecursionChecker.cpp; sourceTree = "<group>"; };
+               93345A8812D838C400302BE3 /* StringRecursionChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringRecursionChecker.h; sourceTree = "<group>"; };
                933A349A038AE7C6008635CE /* Identifier.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = Identifier.h; sourceTree = "<group>"; tabWidth = 8; };
                933A349D038AE80F008635CE /* Identifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Identifier.cpp; sourceTree = "<group>"; tabWidth = 8; };
                933F5CDB126922690049191E /* NullPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NullPtr.h; sourceTree = "<group>"; };
                                BC18C3C40E16EE3300B34460 /* StringObjectThatMasqueradesAsUndefined.h */,
                                BC18C3C50E16EE3300B34460 /* StringPrototype.cpp */,
                                BC18C3C60E16EE3300B34460 /* StringPrototype.h */,
+                               93345A8712D838C400302BE3 /* StringRecursionChecker.cpp */,
+                               93345A8812D838C400302BE3 /* StringRecursionChecker.h */,
                                BCDE3AB00E6C82CF001453A7 /* Structure.cpp */,
                                BCDE3AB10E6C82CF001453A7 /* Structure.h */,
                                7E4EE70E0EBB7A5B005934AA /* StructureChain.cpp */,
                                86704B8612DBA33700A9FE7B /* YarrJIT.cpp in Sources */,
                                86704B8912DBA33700A9FE7B /* YarrPattern.cpp in Sources */,
                                86704B4212DB8A8100A9FE7B /* YarrSyntaxChecker.cpp in Sources */,
+                               9335F24D12E6765B002B5553 /* StringRecursionChecker.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index ab0c3d4..7615ffc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
- *  Copyright (C) 2003, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ *  Copyright (C) 2003, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
  *  Copyright (C) 2003 Peter Kelly (pmk@post.com)
  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
  *
@@ -32,6 +32,7 @@
 #include "Lookup.h"
 #include "ObjectPrototype.h"
 #include "Operations.h"
+#include "StringRecursionChecker.h"
 #include <algorithm>
 #include <wtf/Assertions.h>
 #include <wtf/HashSet.h>
@@ -168,15 +169,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
         return throwVMTypeError(exec);
     JSArray* thisObj = asArray(thisValue);
     
-    HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
-    if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
-        if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
-            return throwVMError(exec, createStackOverflowError(exec));
-    }
-
-    bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
-    if (alreadyVisited)
-        return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoiding infinite recursion.
+    StringRecursionChecker checker(exec, thisObj);
+    if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
+        return earlyReturnValue;
 
     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
     unsigned totalSize = length ? length - 1 : 0;
@@ -209,7 +204,6 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToString(ExecState* exec)
         if (exec->hadException())
             break;
     }
-    arrayVisitedElements.remove(thisObj);
     if (!totalSize)
         return JSValue::encode(jsEmptyString(exec));
     Vector<UChar> buffer;
@@ -234,15 +228,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
         return throwVMTypeError(exec);
     JSObject* thisObj = asArray(thisValue);
 
-    HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
-    if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
-        if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
-            return throwVMError(exec, createStackOverflowError(exec));
-    }
-
-    bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
-    if (alreadyVisited)
-        return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion.
+    StringRecursionChecker checker(exec, thisObj);
+    if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
+        return earlyReturnValue;
 
     JSStringBuilder strBuffer;
     unsigned length = thisObj->get(exec, exec->propertyNames().length).toUInt32(exec);
@@ -264,7 +252,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncToLocaleString(ExecState* exec)
             strBuffer.append(str);
         }
     }
-    arrayVisitedElements.remove(thisObj);
+
     return JSValue::encode(strBuffer.build(exec));
 }
 
@@ -272,15 +260,9 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
 {
     JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
 
-    HashSet<JSObject*>& arrayVisitedElements = exec->globalData().arrayVisitedElements;
-    if (arrayVisitedElements.size() >= MaxSmallThreadReentryDepth) {
-        if (arrayVisitedElements.size() >= exec->globalData().maxReentryDepth)
-            return throwVMError(exec, createStackOverflowError(exec));
-    }
-
-    bool alreadyVisited = !arrayVisitedElements.add(thisObj).second;
-    if (alreadyVisited)
-        return JSValue::encode(jsEmptyString(exec)); // return an empty string, avoding infinite recursion.
+    StringRecursionChecker checker(exec, thisObj);
+    if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
+        return earlyReturnValue;
 
     JSStringBuilder strBuffer;
 
@@ -335,7 +317,7 @@ EncodedJSValue JSC_HOST_CALL arrayProtoFuncJoin(ExecState* exec)
         if (!element.isUndefinedOrNull())
             strBuffer.append(element.toString(exec));
     }
-    arrayVisitedElements.remove(thisObj);
+
     return JSValue::encode(strBuffer.build(exec));
 }
 
index d18e7d8..d8fc829 100644 (file)
@@ -26,6 +26,7 @@
 #include "JSStringBuilder.h"
 #include "ObjectPrototype.h"
 #include "PrototypeFunction.h"
+#include "StringRecursionChecker.h"
 #include "UString.h"
 
 namespace JSC {
@@ -47,6 +48,11 @@ ErrorPrototype::ErrorPrototype(ExecState* exec, JSGlobalObject* globalObject, No
 EncodedJSValue JSC_HOST_CALL errorProtoFuncToString(ExecState* exec)
 {
     JSObject* thisObj = exec->hostThisValue().toThisObject(exec);
+
+    StringRecursionChecker checker(exec, thisObj);
+    if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
+        return earlyReturnValue;
+
     JSValue name = thisObj->get(exec, exec->propertyNames().name);
     JSValue message = thisObj->get(exec, exec->propertyNames().message);
 
index a24732a..1c3af2a 100644 (file)
@@ -214,7 +214,7 @@ namespace JSC {
         JSGlobalObject* head;
         JSGlobalObject* dynamicGlobalObject;
 
-        HashSet<JSObject*> arrayVisitedElements;
+        HashSet<JSObject*> stringRecursionCheckVisitedObjects;
 
         Stringifier* firstStringifierToMark;
 
index 04bcc3b..a7c447d 100644 (file)
@@ -34,6 +34,7 @@
 #include "RegExpObject.h"
 #include "RegExp.h"
 #include "RegExpCache.h"
+#include "StringRecursionChecker.h"
 #include "UStringConcatenate.h"
 
 namespace JSC {
@@ -111,15 +112,21 @@ EncodedJSValue JSC_HOST_CALL regExpProtoFuncToString(ExecState* exec)
         return throwVMTypeError(exec);
     }
 
+    RegExpObject* thisObject = asRegExpObject(thisValue);
+
+    StringRecursionChecker checker(exec, thisObject);
+    if (EncodedJSValue earlyReturnValue = checker.earlyReturnValue())
+        return earlyReturnValue;
+
     char postfix[5] = { '/', 0, 0, 0, 0 };
     int index = 1;
-    if (asRegExpObject(thisValue)->get(exec, exec->propertyNames().global).toBoolean(exec))
+    if (thisObject->get(exec, exec->propertyNames().global).toBoolean(exec))
         postfix[index++] = 'g';
-    if (asRegExpObject(thisValue)->get(exec, exec->propertyNames().ignoreCase).toBoolean(exec))
+    if (thisObject->get(exec, exec->propertyNames().ignoreCase).toBoolean(exec))
         postfix[index++] = 'i';
-    if (asRegExpObject(thisValue)->get(exec, exec->propertyNames().multiline).toBoolean(exec))
+    if (thisObject->get(exec, exec->propertyNames().multiline).toBoolean(exec))
         postfix[index] = 'm';
-    UString source = asRegExpObject(thisValue)->get(exec, exec->propertyNames().source).toString(exec);
+    UString source = thisObject->get(exec, exec->propertyNames().source).toString(exec);
     // If source is empty, use "/(?:)/" to avoid colliding with comment syntax
     return JSValue::encode(jsMakeNontrivialString(exec, "/", source.length() ? source : UString("(?:)"), postfix));
 }
diff --git a/Source/JavaScriptCore/runtime/StringRecursionChecker.cpp b/Source/JavaScriptCore/runtime/StringRecursionChecker.cpp
new file mode 100644 (file)
index 0000000..4e74735
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ *  Copyright (C) 2011 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
+ *
+ */
+
+#include "config.h"
+#include "StringRecursionChecker.h"
+
+#include "Error.h"
+#include "ExceptionHelpers.h"
+
+namespace JSC {
+
+EncodedJSValue StringRecursionChecker::throwStackOverflowError()
+{
+    return throwVMError(m_exec, createStackOverflowError(m_exec));
+}
+
+EncodedJSValue StringRecursionChecker::emptyString()
+{
+    return JSValue::encode(jsEmptyString(m_exec));
+}
+
+}
diff --git a/Source/JavaScriptCore/runtime/StringRecursionChecker.h b/Source/JavaScriptCore/runtime/StringRecursionChecker.h
new file mode 100644 (file)
index 0000000..314f14e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *  Copyright (C) 2011 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 StringRecursionChecker_h
+#define StringRecursionChecker_h
+
+#include "Interpreter.h"
+
+namespace JSC {
+
+class StringRecursionChecker {
+    WTF_MAKE_NONCOPYABLE(StringRecursionChecker);
+
+public:
+    StringRecursionChecker(ExecState*, JSObject* thisObject);
+    ~StringRecursionChecker();
+
+    EncodedJSValue earlyReturnValue() const; // 0 if everything is OK, value to return for failure cases
+
+private:
+    EncodedJSValue throwStackOverflowError();
+    EncodedJSValue emptyString();
+    EncodedJSValue performCheck();
+
+    ExecState* m_exec;
+    JSObject* m_thisObject;
+    EncodedJSValue m_earlyReturnValue;
+};
+
+inline EncodedJSValue StringRecursionChecker::performCheck()
+{
+    int size = m_exec->globalData().stringRecursionCheckVisitedObjects.size();
+    if (size >= MaxSmallThreadReentryDepth && size >= m_exec->globalData().maxReentryDepth)
+        return throwStackOverflowError();
+    bool alreadyVisited = !m_exec->globalData().stringRecursionCheckVisitedObjects.add(m_thisObject).second;
+    if (alreadyVisited)
+        return emptyString(); // Return empty string to avoid infinite recursion.
+    return 0; // Indicate success.
+}
+
+inline StringRecursionChecker::StringRecursionChecker(ExecState* exec, JSObject* thisObject)
+    : m_exec(exec)
+    , m_thisObject(thisObject)
+    , m_earlyReturnValue(performCheck())
+{
+}
+
+inline EncodedJSValue StringRecursionChecker::earlyReturnValue() const
+{
+    return m_earlyReturnValue;
+}
+
+inline StringRecursionChecker::~StringRecursionChecker()
+{
+    if (m_earlyReturnValue)
+        return;
+    ASSERT(m_exec->globalData().stringRecursionCheckVisitedObjects.contains(m_thisObject));
+    m_exec->globalData().stringRecursionCheckVisitedObjects.remove(m_thisObject);
+}
+
+}
+
+#endif