1/*
2 * Copyright (C) 2004 Apple Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "runtime_root.h"
28
29#include "BridgeJSC.h"
30#include "runtime_object.h"
31#include <heap/StrongInlines.h>
32#include <heap/Weak.h>
33#include <heap/WeakInlines.h>
34#include <runtime/JSGlobalObject.h>
35#include <wtf/HashCountedSet.h>
36#include <wtf/HashSet.h>
37#include <wtf/NeverDestroyed.h>
38#include <wtf/Ref.h>
39#include <wtf/StdLibExtras.h>
40
41namespace JSC { namespace Bindings {
42
43// This code attempts to solve two problems: (1) plug-ins leaking references to
44// JS and the DOM; (2) plug-ins holding stale references to JS and the DOM. Previous
45// comments in this file claimed that problem #1 was an issue in Java, in particular,
46// because Java, allegedly, didn't always call finalize when collecting an object.
47
48typedef HashSet<RootObject*> RootObjectSet;
49
50static RootObjectSet& rootObjectSet()
51{
52    static NeverDestroyed<RootObjectSet> staticRootObjectSet;
53    return staticRootObjectSet;
54}
55
56// FIXME:  These two functions are a potential performance problem.  We could
57// fix them by adding a JSObject to RootObject dictionary.
58
59RootObject* findProtectingRootObject(JSObject* jsObject)
60{
61    RootObjectSet::const_iterator end = rootObjectSet().end();
62    for (RootObjectSet::const_iterator it = rootObjectSet().begin(); it != end; ++it) {
63        if ((*it)->gcIsProtected(jsObject))
64            return *it;
65    }
66    return 0;
67}
68
69RootObject* findRootObject(JSGlobalObject* globalObject)
70{
71    RootObjectSet::const_iterator end = rootObjectSet().end();
72    for (RootObjectSet::const_iterator it = rootObjectSet().begin(); it != end; ++it) {
73        if ((*it)->globalObject() == globalObject)
74            return *it;
75    }
76    return 0;
77}
78
79RootObject::InvalidationCallback::~InvalidationCallback()
80{
81}
82
83PassRefPtr<RootObject> RootObject::create(const void* nativeHandle, JSGlobalObject* globalObject)
84{
85    return adoptRef(new RootObject(nativeHandle, globalObject));
86}
87
88RootObject::RootObject(const void* nativeHandle, JSGlobalObject* globalObject)
89    : m_isValid(true)
90    , m_nativeHandle(nativeHandle)
91    , m_globalObject(globalObject->vm(), globalObject)
92{
93    ASSERT(globalObject);
94    rootObjectSet().add(this);
95}
96
97RootObject::~RootObject()
98{
99    if (m_isValid)
100        invalidate();
101}
102
103void RootObject::invalidate()
104{
105    if (!m_isValid)
106        return;
107
108    {
109        HashMap<RuntimeObject*, JSC::Weak<RuntimeObject>>::iterator end = m_runtimeObjects.end();
110        for (HashMap<RuntimeObject*, JSC::Weak<RuntimeObject>>::iterator it = m_runtimeObjects.begin(); it != end; ++it) {
111            RuntimeObject* runtimeObject = it->value.get();
112            if (!runtimeObject) // Skip zombies.
113                continue;
114            runtimeObject->invalidate();
115        }
116
117        m_runtimeObjects.clear();
118    }
119
120    m_isValid = false;
121
122    m_nativeHandle = 0;
123    m_globalObject.clear();
124
125    {
126        HashSet<InvalidationCallback*>::iterator end = m_invalidationCallbacks.end();
127        for (HashSet<InvalidationCallback*>::iterator iter = m_invalidationCallbacks.begin(); iter != end; ++iter)
128            (**iter)(this);
129
130        m_invalidationCallbacks.clear();
131    }
132
133    ProtectCountSet::iterator end = m_protectCountSet.end();
134    for (ProtectCountSet::iterator it = m_protectCountSet.begin(); it != end; ++it)
135        JSC::gcUnprotect(it->key);
136    m_protectCountSet.clear();
137
138    rootObjectSet().remove(this);
139}
140
141void RootObject::gcProtect(JSObject* jsObject)
142{
143    ASSERT(m_isValid);
144
145    if (!m_protectCountSet.contains(jsObject)) {
146        JSC::JSLockHolder holder(&globalObject()->vm());
147        JSC::gcProtect(jsObject);
148    }
149    m_protectCountSet.add(jsObject);
150}
151
152void RootObject::gcUnprotect(JSObject* jsObject)
153{
154    ASSERT(m_isValid);
155
156    if (!jsObject)
157        return;
158
159    if (m_protectCountSet.count(jsObject) == 1) {
160        JSC::JSLockHolder holder(&globalObject()->vm());
161        JSC::gcUnprotect(jsObject);
162    }
163    m_protectCountSet.remove(jsObject);
164}
165
166bool RootObject::gcIsProtected(JSObject* jsObject)
167{
168    ASSERT(m_isValid);
169    return m_protectCountSet.contains(jsObject);
170}
171
172const void* RootObject::nativeHandle() const
173{
174    ASSERT(m_isValid);
175    return m_nativeHandle;
176}
177
178JSGlobalObject* RootObject::globalObject() const
179{
180    ASSERT(m_isValid);
181    return m_globalObject.get();
182}
183
184void RootObject::updateGlobalObject(JSGlobalObject* globalObject)
185{
186    m_globalObject.set(globalObject->vm(), globalObject);
187}
188
189void RootObject::addRuntimeObject(VM&, RuntimeObject* object)
190{
191    ASSERT(m_isValid);
192    weakAdd(m_runtimeObjects, object, JSC::Weak<RuntimeObject>(object, this));
193}
194
195void RootObject::removeRuntimeObject(RuntimeObject* object)
196{
197    if (!m_isValid)
198        return;
199    weakRemove(m_runtimeObjects, object, object);
200}
201
202void RootObject::finalize(JSC::Handle<JSC::Unknown> handle, void*)
203{
204    RuntimeObject* object = static_cast<RuntimeObject*>(handle.slot()->asCell());
205
206    Ref<RootObject> protect(*this);
207    object->invalidate();
208    weakRemove(m_runtimeObjects, object, object);
209}
210
211} } // namespace JSC::Bindings
212