/ runtime / StringRecursionChecker.h
StringRecursionChecker.h
 1  /*
 2   *  Copyright (C) 2011, 2016 Apple Inc. All rights reserved.
 3   *
 4   *  This library is free software; you can redistribute it and/or
 5   *  modify it under the terms of the GNU Lesser General Public
 6   *  License as published by the Free Software Foundation; either
 7   *  version 2 of the License, or (at your option) any later version.
 8   *
 9   *  This library is distributed in the hope that it will be useful,
10   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   *  Lesser General Public License for more details.
13   *
14   *  You should have received a copy of the GNU Lesser General Public
15   *  License along with this library; if not, write to the Free Software
16   *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17   *
18   */
19  
20  #pragma once
21  
22  #include "CallFrame.h"
23  #include "GetVM.h"
24  #include "VMInlines.h"
25  #include <wtf/StackStats.h>
26  
27  namespace JSC {
28  
29  class StringRecursionChecker {
30      WTF_MAKE_NONCOPYABLE(StringRecursionChecker);
31  
32  public:
33      StringRecursionChecker(JSGlobalObject*, JSObject* thisObject);
34      ~StringRecursionChecker();
35  
36      JSValue earlyReturnValue() const; // 0 if everything is OK, value to return for failure cases
37  
38  private:
39      JSValue throwStackOverflowError();
40      JSValue emptyString();
41      JSValue performCheck();
42  
43      JSGlobalObject* m_globalObject;
44      JSObject* m_thisObject;
45      JSValue m_earlyReturnValue;
46  
47      StackStats::CheckPoint stackCheckpoint;
48  };
49  
50  inline JSValue StringRecursionChecker::performCheck()
51  {
52      VM& vm = getVM(m_globalObject);
53      if (UNLIKELY(!vm.isSafeToRecurseSoft()))
54          return throwStackOverflowError();
55  
56      bool alreadyVisited = false;
57      if (!vm.stringRecursionCheckFirstObject)
58          vm.stringRecursionCheckFirstObject = m_thisObject;
59      else if (vm.stringRecursionCheckFirstObject == m_thisObject)
60          alreadyVisited = true;
61      else
62          alreadyVisited = !vm.stringRecursionCheckVisitedObjects.add(m_thisObject).isNewEntry;
63  
64      if (alreadyVisited)
65          return emptyString(); // Return empty string to avoid infinite recursion.
66      return JSValue(); // Indicate success.
67  }
68  
69  inline StringRecursionChecker::StringRecursionChecker(JSGlobalObject* globalObject, JSObject* thisObject)
70      : m_globalObject(globalObject)
71      , m_thisObject(thisObject)
72      , m_earlyReturnValue(performCheck())
73  {
74  }
75  
76  inline JSValue StringRecursionChecker::earlyReturnValue() const
77  {
78      return m_earlyReturnValue;
79  }
80  
81  inline StringRecursionChecker::~StringRecursionChecker()
82  {
83      if (m_earlyReturnValue)
84          return;
85  
86      VM& vm = getVM(m_globalObject);
87      if (vm.stringRecursionCheckFirstObject == m_thisObject)
88          vm.stringRecursionCheckFirstObject = nullptr;
89      else {
90          ASSERT(vm.stringRecursionCheckVisitedObjects.contains(m_thisObject));
91          vm.stringRecursionCheckVisitedObjects.remove(m_thisObject);
92      }
93  }
94  
95  } // namespace JSC