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 "JSCVirtualMachine.h"
22
23#include "JSCContextPrivate.h"
24#include "JSCVirtualMachinePrivate.h"
25#include <wtf/HashMap.h>
26#include <wtf/NeverDestroyed.h>
27#include <wtf/glib/WTFGType.h>
28
29/**
30 * SECTION: JSCVirtualMachine
31 * @short_description: JavaScript Virtual Machine
32 * @title: JSCVirtualMachine
33 * @see_also: JSCContext
34 *
35 * JSCVirtualMachine represents a group of JSCContext<!-- -->s. It allows
36 * concurrent JavaScript exeution by creating a different instance of
37 * JSCVirtualMachine in each thread.
38 *
39 * To create a group of JSCContext<!-- -->s pass the same JSCVirtualMachine
40 * instance to every JSCContext constructor.
41 */
42
43struct _JSCVirtualMachinePrivate {
44 JSContextGroupRef jsContextGroup;
45 HashMap<JSGlobalContextRef, JSCContext*> contextCache;
46};
47
48WEBKIT_DEFINE_TYPE(JSCVirtualMachine, jsc_virtual_machine, G_TYPE_OBJECT)
49
50static Lock wrapperCacheMutex;
51
52static HashMap<JSContextGroupRef, JSCVirtualMachine*>& wrapperMap()
53{
54 static NeverDestroyed<HashMap<JSContextGroupRef, JSCVirtualMachine*>> map;
55 return map;
56}
57
58static void addWrapper(JSContextGroupRef group, JSCVirtualMachine* vm)
59{
60 std::lock_guard<Lock> lock(wrapperCacheMutex);
61 ASSERT(!wrapperMap().contains(group));
62 wrapperMap().set(group, vm);
63}
64
65static void removeWrapper(JSContextGroupRef group)
66{
67 std::lock_guard<Lock> lock(wrapperCacheMutex);
68 ASSERT(wrapperMap().contains(group));
69 wrapperMap().remove(group);
70}
71
72static void jscVirtualMachineSetContextGroup(JSCVirtualMachine *vm, JSContextGroupRef group)
73{
74 if (group) {
75 ASSERT(!vm->priv->jsContextGroup);
76 vm->priv->jsContextGroup = group;
77 JSContextGroupRetain(vm->priv->jsContextGroup);
78 addWrapper(vm->priv->jsContextGroup, vm);
79 } else if (vm->priv->jsContextGroup) {
80 removeWrapper(vm->priv->jsContextGroup);
81 JSContextGroupRelease(vm->priv->jsContextGroup);
82 vm->priv->jsContextGroup = nullptr;
83 }
84}
85
86static void jscVirtualMachineEnsureContextGroup(JSCVirtualMachine *vm)
87{
88 if (vm->priv->jsContextGroup)
89 return;
90
91 auto* jsContextGroup = JSContextGroupCreate();
92 jscVirtualMachineSetContextGroup(vm, jsContextGroup);
93 JSContextGroupRelease(jsContextGroup);
94}
95
96static void jscVirtualMachineDispose(GObject* object)
97{
98 JSCVirtualMachine* vm = JSC_VIRTUAL_MACHINE(object);
99 jscVirtualMachineSetContextGroup(vm, nullptr);
100
101 G_OBJECT_CLASS(jsc_virtual_machine_parent_class)->dispose(object);
102}
103
104static void jsc_virtual_machine_class_init(JSCVirtualMachineClass* klass)
105{
106 GObjectClass* objClass = G_OBJECT_CLASS(klass);
107 objClass->dispose = jscVirtualMachineDispose;
108}
109
110GRefPtr<JSCVirtualMachine> jscVirtualMachineGetOrCreate(JSContextGroupRef jsContextGroup)
111{
112 GRefPtr<JSCVirtualMachine> vm = wrapperMap().get(jsContextGroup);
113 if (!vm) {
114 vm = adoptGRef(jsc_virtual_machine_new());
115 jscVirtualMachineSetContextGroup(vm.get(), jsContextGroup);
116 }
117 return vm;
118}
119
120JSContextGroupRef jscVirtualMachineGetContextGroup(JSCVirtualMachine* vm)
121{
122 jscVirtualMachineEnsureContextGroup(vm);
123 return vm->priv->jsContextGroup;
124}
125
126void jscVirtualMachineAddContext(JSCVirtualMachine* vm, JSCContext* context)
127{
128 ASSERT(vm->priv->jsContextGroup);
129 auto jsContext = jscContextGetJSContext(context);
130 ASSERT(JSContextGetGroup(jsContext) == vm->priv->jsContextGroup);
131 ASSERT(!vm->priv->contextCache.contains(jsContext));
132 vm->priv->contextCache.set(jsContext, context);
133}
134
135void jscVirtualMachineRemoveContext(JSCVirtualMachine* vm, JSCContext* context)
136{
137 ASSERT(vm->priv->jsContextGroup);
138 auto jsContext = jscContextGetJSContext(context);
139 ASSERT(JSContextGetGroup(jsContext) == vm->priv->jsContextGroup);
140 ASSERT(vm->priv->contextCache.contains(jsContext));
141 vm->priv->contextCache.remove(jsContext);
142}
143
144JSCContext* jscVirtualMachineGetContext(JSCVirtualMachine* vm, JSGlobalContextRef jsContext)
145{
146 return vm->priv->contextCache.get(jsContext);
147}
148
149/**
150 * jsc_virtual_machine_new:
151 *
152 * Create a new #JSCVirtualMachine.
153 *
154 * Returns: (transfer full): the newly created #JSCVirtualMachine.
155 */
156JSCVirtualMachine* jsc_virtual_machine_new()
157{
158 return JSC_VIRTUAL_MACHINE(g_object_new(JSC_TYPE_VIRTUAL_MACHINE, nullptr));
159}
160