1/*
2 * Copyright (C) 2018 Igalia S.L.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library 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 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "JSCWrapperMap.h"
22
23#include "APICast.h"
24#include "JSAPIWrapperGlobalObject.h"
25#include "JSAPIWrapperObject.h"
26#include "JSCClassPrivate.h"
27#include "JSCContextPrivate.h"
28#include "JSCGLibWrapperObject.h"
29#include "JSCInlines.h"
30#include "JSCValuePrivate.h"
31#include "JSCallbackObject.h"
32
33namespace JSC {
34
35WrapperMap::WrapperMap(JSGlobalContextRef jsContext)
36 : m_cachedJSWrappers(std::make_unique<JSC::WeakGCMap<gpointer, JSC::JSObject>>(toJS(jsContext)->vm()))
37{
38}
39
40WrapperMap::~WrapperMap()
41{
42 for (const auto& jscClass : m_classMap.values())
43 jscClassInvalidate(jscClass.get());
44}
45
46GRefPtr<JSCValue> WrapperMap::gobjectWrapper(JSCContext* jscContext, JSValueRef jsValue)
47{
48 auto* jsContext = jscContextGetJSContext(jscContext);
49 JSC::JSLockHolder locker(toJS(jsContext));
50 ASSERT(toJSGlobalObject(jsContext)->wrapperMap() == this);
51 GRefPtr<JSCValue> value = m_cachedGObjectWrappers.get(jsValue);
52 if (!value) {
53 value = adoptGRef(jscValueCreate(jscContext, jsValue));
54 m_cachedGObjectWrappers.set(jsValue, value.get());
55 }
56 return value;
57}
58
59void WrapperMap::unwrap(JSValueRef jsValue)
60{
61 ASSERT(m_cachedGObjectWrappers.contains(jsValue));
62 m_cachedGObjectWrappers.remove(jsValue);
63}
64
65void WrapperMap::registerClass(JSCClass* jscClass)
66{
67 auto* jsClass = jscClassGetJSClass(jscClass);
68 ASSERT(!m_classMap.contains(jsClass));
69 m_classMap.set(jsClass, jscClass);
70}
71
72JSCClass* WrapperMap::registeredClass(JSClassRef jsClass) const
73{
74 return m_classMap.get(jsClass);
75}
76
77JSObject* WrapperMap::createJSWrappper(JSGlobalContextRef jsContext, JSClassRef jsClass, JSValueRef prototype, gpointer wrappedObject, GDestroyNotify destroyFunction)
78{
79 ASSERT(toJSGlobalObject(jsContext)->wrapperMap() == this);
80 ExecState* exec = toJS(jsContext);
81 VM& vm = exec->vm();
82 JSLockHolder locker(vm);
83 auto* object = JSC::JSCallbackObject<JSC::JSAPIWrapperObject>::create(exec, exec->lexicalGlobalObject(), exec->lexicalGlobalObject()->glibWrapperObjectStructure(), jsClass, nullptr);
84 if (wrappedObject) {
85 object->setWrappedObject(new JSC::JSCGLibWrapperObject(wrappedObject, destroyFunction));
86 m_cachedJSWrappers->set(wrappedObject, object);
87 }
88 if (prototype)
89 JSObjectSetPrototype(jsContext, toRef(object), prototype);
90 else if (auto* jsPrototype = jsClass->prototype(exec))
91 object->setPrototypeDirect(vm, jsPrototype);
92 return object;
93}
94
95JSGlobalContextRef WrapperMap::createContextWithJSWrappper(JSContextGroupRef jsGroup, JSClassRef jsClass, JSValueRef prototype, gpointer wrappedObject, GDestroyNotify destroyFunction)
96{
97 Ref<VM> vm(*toJS(jsGroup));
98 JSLockHolder locker(vm.ptr());
99 auto* globalObject = JSCallbackObject<JSAPIWrapperGlobalObject>::create(vm.get(), jsClass, JSCallbackObject<JSAPIWrapperGlobalObject>::createStructure(vm.get(), nullptr, jsNull()));
100 if (wrappedObject) {
101 globalObject->setWrappedObject(new JSC::JSCGLibWrapperObject(wrappedObject, destroyFunction));
102 m_cachedJSWrappers->set(wrappedObject, globalObject);
103 }
104 ExecState* exec = globalObject->globalExec();
105 if (prototype)
106 globalObject->resetPrototype(vm.get(), toJS(exec, prototype));
107 else if (auto jsPrototype = jsClass->prototype(exec))
108 globalObject->resetPrototype(vm.get(), jsPrototype);
109 else
110 globalObject->resetPrototype(vm.get(), jsNull());
111
112 return JSGlobalContextRetain(toGlobalRef(exec));
113}
114
115JSObject* WrapperMap::jsWrapper(gpointer wrappedObject) const
116{
117 if (!wrappedObject)
118 return nullptr;
119 return m_cachedJSWrappers->get(wrappedObject);
120}
121
122gpointer WrapperMap::wrappedObject(JSGlobalContextRef jsContext, JSObjectRef jsObject) const
123{
124 ASSERT(toJSGlobalObject(jsContext)->wrapperMap() == this);
125 JSLockHolder locker(toJS(jsContext));
126 VM& vm = toJS(jsContext)->vm();
127 auto* object = toJS(jsObject);
128 if (object->inherits(vm, JSC::JSCallbackObject<JSC::JSAPIWrapperObject>::info())) {
129 if (auto* wrapper = JSC::jsCast<JSC::JSAPIWrapperObject*>(object)->wrappedObject())
130 return static_cast<JSC::JSCGLibWrapperObject*>(wrapper)->object();
131 }
132 if (object->inherits(vm, JSC::JSCallbackObject<JSC::JSAPIWrapperGlobalObject>::info())) {
133 if (auto* wrapper = JSC::jsCast<JSC::JSAPIWrapperGlobalObject*>(object)->wrappedObject())
134 return wrapper->object();
135 }
136 return nullptr;
137}
138
139} // namespace JSC
140