blob: f5986e0b8791eb33a012fc304d2536306ed99180 [file] [log] [blame]
/*
* Copyright (C) 2018 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "JSCVirtualMachine.h"
#include "JSCContextPrivate.h"
#include "JSCVirtualMachinePrivate.h"
#include "JSContextRef.h"
#include <wtf/HashMap.h>
#include <wtf/Lock.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/glib/WTFGType.h>
/**
* SECTION: JSCVirtualMachine
* @short_description: JavaScript Virtual Machine
* @title: JSCVirtualMachine
* @see_also: JSCContext
*
* JSCVirtualMachine represents a group of JSCContext<!-- -->s. It allows
* concurrent JavaScript execution by creating a different instance of
* JSCVirtualMachine in each thread.
*
* To create a group of JSCContext<!-- -->s pass the same JSCVirtualMachine
* instance to every JSCContext constructor.
*/
struct _JSCVirtualMachinePrivate {
JSContextGroupRef jsContextGroup;
HashMap<JSGlobalContextRef, JSCContext*> contextCache;
};
WEBKIT_DEFINE_TYPE(JSCVirtualMachine, jsc_virtual_machine, G_TYPE_OBJECT)
static Lock wrapperCacheMutex;
static HashMap<JSContextGroupRef, JSCVirtualMachine*>& wrapperMap() WTF_REQUIRES_LOCK(wrapperCacheMutex)
{
static LazyNeverDestroyed<HashMap<JSContextGroupRef, JSCVirtualMachine*>> shared;
static std::once_flag onceKey;
std::call_once(onceKey, [&] {
shared.construct();
});
return shared;
}
static void addWrapper(JSContextGroupRef group, JSCVirtualMachine* vm)
{
Locker locker { wrapperCacheMutex };
ASSERT(!wrapperMap().contains(group));
wrapperMap().set(group, vm);
}
static void removeWrapper(JSContextGroupRef group)
{
Locker locker { wrapperCacheMutex };
ASSERT(wrapperMap().contains(group));
wrapperMap().remove(group);
}
static void jscVirtualMachineSetContextGroup(JSCVirtualMachine *vm, JSContextGroupRef group)
{
if (group) {
ASSERT(!vm->priv->jsContextGroup);
vm->priv->jsContextGroup = group;
JSContextGroupRetain(vm->priv->jsContextGroup);
addWrapper(vm->priv->jsContextGroup, vm);
} else if (vm->priv->jsContextGroup) {
removeWrapper(vm->priv->jsContextGroup);
JSContextGroupRelease(vm->priv->jsContextGroup);
vm->priv->jsContextGroup = nullptr;
}
}
static void jscVirtualMachineEnsureContextGroup(JSCVirtualMachine *vm)
{
if (vm->priv->jsContextGroup)
return;
auto* jsContextGroup = JSContextGroupCreate();
jscVirtualMachineSetContextGroup(vm, jsContextGroup);
JSContextGroupRelease(jsContextGroup);
}
static void jscVirtualMachineDispose(GObject* object)
{
JSCVirtualMachine* vm = JSC_VIRTUAL_MACHINE(object);
jscVirtualMachineSetContextGroup(vm, nullptr);
G_OBJECT_CLASS(jsc_virtual_machine_parent_class)->dispose(object);
}
static void jsc_virtual_machine_class_init(JSCVirtualMachineClass* klass)
{
GObjectClass* objClass = G_OBJECT_CLASS(klass);
objClass->dispose = jscVirtualMachineDispose;
}
GRefPtr<JSCVirtualMachine> jscVirtualMachineGetOrCreate(JSContextGroupRef jsContextGroup)
{
GRefPtr<JSCVirtualMachine> vm = wrapperMap().get(jsContextGroup);
if (!vm) {
vm = adoptGRef(jsc_virtual_machine_new());
jscVirtualMachineSetContextGroup(vm.get(), jsContextGroup);
}
return vm;
}
JSContextGroupRef jscVirtualMachineGetContextGroup(JSCVirtualMachine* vm)
{
jscVirtualMachineEnsureContextGroup(vm);
return vm->priv->jsContextGroup;
}
void jscVirtualMachineAddContext(JSCVirtualMachine* vm, JSCContext* context)
{
ASSERT(vm->priv->jsContextGroup);
auto jsContext = jscContextGetJSContext(context);
ASSERT(JSContextGetGroup(jsContext) == vm->priv->jsContextGroup);
ASSERT(!vm->priv->contextCache.contains(jsContext));
vm->priv->contextCache.set(jsContext, context);
}
void jscVirtualMachineRemoveContext(JSCVirtualMachine* vm, JSCContext* context)
{
ASSERT(vm->priv->jsContextGroup);
auto jsContext = jscContextGetJSContext(context);
ASSERT(JSContextGetGroup(jsContext) == vm->priv->jsContextGroup);
ASSERT(vm->priv->contextCache.contains(jsContext));
vm->priv->contextCache.remove(jsContext);
}
JSCContext* jscVirtualMachineGetContext(JSCVirtualMachine* vm, JSGlobalContextRef jsContext)
{
return vm->priv->contextCache.get(jsContext);
}
/**
* jsc_virtual_machine_new:
*
* Create a new #JSCVirtualMachine.
*
* Returns: (transfer full): the newly created #JSCVirtualMachine.
*/
JSCVirtualMachine* jsc_virtual_machine_new()
{
return JSC_VIRTUAL_MACHINE(g_object_new(JSC_TYPE_VIRTUAL_MACHINE, nullptr));
}