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 <JavaScriptCore/JSGlobalObject.h>
32#include <JavaScriptCore/StrongInlines.h>
33#include <JavaScriptCore/Weak.h>
34#include <JavaScriptCore/WeakInlines.h>
35#include <wtf/HashSet.h>
36#include <wtf/NeverDestroyed.h>
37#include <wtf/Ref.h>
38#include <wtf/StdLibExtras.h>
39
40namespace JSC { namespace Bindings {
41
42// This code attempts to solve two problems: (1) plug-ins leaking references to
43// JS and the DOM; (2) plug-ins holding stale references to JS and the DOM. Previous
44// comments in this file claimed that problem #1 was an issue in Java, in particular,
45// because Java, allegedly, didn't always call finalize when collecting an object.
46
47typedef HashSet<RootObject*> RootObjectSet;
48
49static RootObjectSet& rootObjectSet()
50{
51 static NeverDestroyed<RootObjectSet> staticRootObjectSet;
52 return staticRootObjectSet;
53}
54
55// FIXME: These two functions are a potential performance problem. We could
56// fix them by adding a JSObject to RootObject dictionary.
57
58RootObject* findProtectingRootObject(JSObject* jsObject)
59{
60 RootObjectSet::const_iterator end = rootObjectSet().end();
61 for (RootObjectSet::const_iterator it = rootObjectSet().begin(); it != end; ++it) {
62 if ((*it)->gcIsProtected(jsObject))
63 return *it;
64 }
65 return 0;
66}
67
68RootObject* findRootObject(JSGlobalObject* globalObject)
69{
70 RootObjectSet::const_iterator end = rootObjectSet().end();
71 for (RootObjectSet::const_iterator it = rootObjectSet().begin(); it != end; ++it) {
72 if ((*it)->globalObject() == globalObject)
73 return *it;
74 }
75 return 0;
76}
77
78RootObject::InvalidationCallback::~InvalidationCallback() = default;
79
80Ref<RootObject> RootObject::create(const void* nativeHandle, JSGlobalObject* globalObject)
81{
82 return adoptRef(*new RootObject(nativeHandle, globalObject));
83}
84
85RootObject::RootObject(const void* nativeHandle, JSGlobalObject* globalObject)
86 : m_isValid(true)
87 , m_nativeHandle(nativeHandle)
88 , m_globalObject(globalObject->vm(), globalObject)
89{
90 ASSERT(globalObject);
91 rootObjectSet().add(this);
92}
93
94RootObject::~RootObject()
95{
96 if (m_isValid)
97 invalidate();
98}
99
100void RootObject::invalidate()
101{
102 if (!m_isValid)
103 return;
104
105 {
106 // Get the objects from the keys; the values might be nulled.
107 // Safe because finalized runtime objects are removed from m_runtimeObjects by RootObject::finalize.
108 for (RuntimeObject* runtimeObject : m_runtimeObjects.keys())
109 runtimeObject->invalidate();
110
111 m_runtimeObjects.clear();
112 }
113
114 m_isValid = false;
115
116 m_nativeHandle = 0;
117 m_globalObject.clear();
118
119 {
120 HashSet<InvalidationCallback*>::iterator end = m_invalidationCallbacks.end();
121 for (HashSet<InvalidationCallback*>::iterator iter = m_invalidationCallbacks.begin(); iter != end; ++iter)
122 (**iter)(this);
123
124 m_invalidationCallbacks.clear();
125 }
126
127 ProtectCountSet::iterator end = m_protectCountSet.end();
128 for (ProtectCountSet::iterator it = m_protectCountSet.begin(); it != end; ++it)
129 JSC::gcUnprotect(it->key);
130 m_protectCountSet.clear();
131
132 rootObjectSet().remove(this);
133}
134
135void RootObject::gcProtect(JSObject* jsObject)
136{
137 ASSERT(m_isValid);
138
139 if (!m_protectCountSet.contains(jsObject)) {
140 JSC::JSLockHolder holder(&globalObject()->vm());
141 JSC::gcProtect(jsObject);
142 }
143 m_protectCountSet.add(jsObject);
144}
145
146void RootObject::gcUnprotect(JSObject* jsObject)
147{
148 ASSERT(m_isValid);
149
150 if (!jsObject)
151 return;
152
153 if (m_protectCountSet.count(jsObject) == 1) {
154 JSC::JSLockHolder holder(&globalObject()->vm());
155 JSC::gcUnprotect(jsObject);
156 }
157 m_protectCountSet.remove(jsObject);
158}
159
160bool RootObject::gcIsProtected(JSObject* jsObject)
161{
162 ASSERT(m_isValid);
163 return m_protectCountSet.contains(jsObject);
164}
165
166const void* RootObject::nativeHandle() const
167{
168 ASSERT(m_isValid);
169 return m_nativeHandle;
170}
171
172JSGlobalObject* RootObject::globalObject() const
173{
174 ASSERT(m_isValid);
175 return m_globalObject.get();
176}
177
178void RootObject::updateGlobalObject(JSGlobalObject* globalObject)
179{
180 m_globalObject.set(globalObject->vm(), globalObject);
181}
182
183void RootObject::addRuntimeObject(VM&, RuntimeObject* object)
184{
185 ASSERT(m_isValid);
186 weakAdd(m_runtimeObjects, object, JSC::Weak<RuntimeObject>(object, this));
187}
188
189void RootObject::removeRuntimeObject(RuntimeObject* object)
190{
191 if (!m_isValid)
192 return;
193 weakRemove(m_runtimeObjects, object, object);
194}
195
196void RootObject::finalize(JSC::Handle<JSC::Unknown> handle, void*)
197{
198 RuntimeObject* object = static_cast<RuntimeObject*>(handle.slot()->asCell());
199
200 Ref<RootObject> protectedThis(*this);
201 object->invalidate();
202 weakRemove(m_runtimeObjects, object, object);
203}
204
205} } // namespace JSC::Bindings
206