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