|  | /* | 
|  | *  Copyright (C) 2011, 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 | 
|  | * | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #include "CallFrame.h" | 
|  | #include "VMInlines.h" | 
|  | #include <wtf/StackStats.h> | 
|  |  | 
|  | namespace JSC { | 
|  |  | 
|  | class StringRecursionChecker { | 
|  | WTF_MAKE_NONCOPYABLE(StringRecursionChecker); | 
|  |  | 
|  | public: | 
|  | StringRecursionChecker(ExecState*, JSObject* thisObject); | 
|  | ~StringRecursionChecker(); | 
|  |  | 
|  | JSValue earlyReturnValue() const; // 0 if everything is OK, value to return for failure cases | 
|  |  | 
|  | private: | 
|  | JSValue throwStackOverflowError(); | 
|  | JSValue emptyString(); | 
|  | JSValue performCheck(); | 
|  |  | 
|  | ExecState* m_exec; | 
|  | JSObject* m_thisObject; | 
|  | JSValue m_earlyReturnValue; | 
|  |  | 
|  | StackStats::CheckPoint stackCheckpoint; | 
|  | }; | 
|  |  | 
|  | inline JSValue StringRecursionChecker::performCheck() | 
|  | { | 
|  | VM& vm = m_exec->vm(); | 
|  | if (UNLIKELY(!vm.isSafeToRecurseSoft())) | 
|  | return throwStackOverflowError(); | 
|  |  | 
|  | bool alreadyVisited = false; | 
|  | if (!vm.stringRecursionCheckFirstObject) | 
|  | vm.stringRecursionCheckFirstObject = m_thisObject; | 
|  | else if (vm.stringRecursionCheckFirstObject == m_thisObject) | 
|  | alreadyVisited = true; | 
|  | else | 
|  | alreadyVisited = !vm.stringRecursionCheckVisitedObjects.add(m_thisObject).isNewEntry; | 
|  |  | 
|  | if (alreadyVisited) | 
|  | return emptyString(); // Return empty string to avoid infinite recursion. | 
|  | return JSValue(); // Indicate success. | 
|  | } | 
|  |  | 
|  | inline StringRecursionChecker::StringRecursionChecker(ExecState* exec, JSObject* thisObject) | 
|  | : m_exec(exec) | 
|  | , m_thisObject(thisObject) | 
|  | , m_earlyReturnValue(performCheck()) | 
|  | { | 
|  | } | 
|  |  | 
|  | inline JSValue StringRecursionChecker::earlyReturnValue() const | 
|  | { | 
|  | return m_earlyReturnValue; | 
|  | } | 
|  |  | 
|  | inline StringRecursionChecker::~StringRecursionChecker() | 
|  | { | 
|  | if (m_earlyReturnValue) | 
|  | return; | 
|  |  | 
|  | VM& vm = m_exec->vm(); | 
|  | if (vm.stringRecursionCheckFirstObject == m_thisObject) | 
|  | vm.stringRecursionCheckFirstObject = nullptr; | 
|  | else { | 
|  | ASSERT(vm.stringRecursionCheckVisitedObjects.contains(m_thisObject)); | 
|  | vm.stringRecursionCheckVisitedObjects.remove(m_thisObject); | 
|  | } | 
|  | } | 
|  |  | 
|  | } // namespace JSC |