150 lines
5.4 KiB
C++
150 lines
5.4 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "vm/ProxyObject.h"
|
|
|
|
#include "jscompartment.h"
|
|
|
|
#include "proxy/DeadObjectProxy.h"
|
|
#include "proxy/ScriptedProxyHandler.h"
|
|
|
|
#include "jsobjinlines.h"
|
|
|
|
using namespace js;
|
|
|
|
/* static */ ProxyObject*
|
|
ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv, TaggedProto proto_,
|
|
const ProxyOptions& options)
|
|
{
|
|
Rooted<TaggedProto> proto(cx, proto_);
|
|
|
|
const Class* clasp = options.clasp();
|
|
|
|
MOZ_ASSERT(isValidProxyClass(clasp));
|
|
MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
|
|
MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
|
|
MOZ_ASSERT(clasp->hasFinalize());
|
|
|
|
/*
|
|
* Eagerly mark properties unknown for proxies, so we don't try to track
|
|
* their properties and so that we don't need to walk the compartment if
|
|
* their prototype changes later. But don't do this for DOM proxies,
|
|
* because we want to be able to keep track of them in typesets in useful
|
|
* ways.
|
|
*/
|
|
if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) {
|
|
RootedObject protoObj(cx, proto.toObject());
|
|
if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj))
|
|
return nullptr;
|
|
}
|
|
|
|
// Ensure that the wrapper has the same lifetime assumptions as the
|
|
// wrappee. Prefer to allocate in the nursery, when possible.
|
|
NewObjectKind newKind = NurseryAllocatedProxy;
|
|
if (options.singleton()) {
|
|
MOZ_ASSERT(priv.isNull() || (priv.isGCThing() && priv.toGCThing()->isTenured()));
|
|
newKind = SingletonObject;
|
|
} else if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
|
|
!handler->canNurseryAllocate() ||
|
|
!handler->finalizeInBackground(priv))
|
|
{
|
|
newKind = TenuredObject;
|
|
}
|
|
|
|
gc::AllocKind allocKind = gc::GetGCObjectKind(clasp);
|
|
if (handler->finalizeInBackground(priv))
|
|
allocKind = GetBackgroundAllocKind(allocKind);
|
|
|
|
AutoSetNewObjectMetadata metadata(cx);
|
|
// Note: this will initialize the object's |data| to strange values, but we
|
|
// will immediately overwrite those below.
|
|
RootedObject obj(cx, NewObjectWithGivenTaggedProto(cx, clasp, proto, allocKind,
|
|
newKind));
|
|
if (!obj)
|
|
return nullptr;
|
|
|
|
Rooted<ProxyObject*> proxy(cx, &obj->as<ProxyObject>());
|
|
new (proxy->data.values) detail::ProxyValueArray;
|
|
proxy->data.handler = handler;
|
|
proxy->setCrossCompartmentPrivate(priv);
|
|
|
|
/* Don't track types of properties of non-DOM and non-singleton proxies. */
|
|
if (newKind != SingletonObject && !clasp->isDOMClass())
|
|
MarkObjectGroupUnknownProperties(cx, proxy->group());
|
|
|
|
return proxy;
|
|
}
|
|
|
|
gc::AllocKind
|
|
ProxyObject::allocKindForTenure() const
|
|
{
|
|
gc::AllocKind allocKind = gc::GetGCObjectKind(group()->clasp());
|
|
if (data.handler->finalizeInBackground(const_cast<ProxyObject*>(this)->private_()))
|
|
allocKind = GetBackgroundAllocKind(allocKind);
|
|
return allocKind;
|
|
}
|
|
|
|
/* static */ size_t
|
|
ProxyObject::objectMovedDuringMinorGC(TenuringTracer* trc, JSObject* dst, JSObject* src)
|
|
{
|
|
ProxyObject& psrc = src->as<ProxyObject>();
|
|
ProxyObject& pdst = dst->as<ProxyObject>();
|
|
|
|
// We're about to sweep the nursery heap, so migrate the inline
|
|
// ProxyValueArray to the malloc heap if they were nursery allocated.
|
|
if (trc->runtime()->gc.nursery.isInside(psrc.data.values))
|
|
pdst.data.values = js_new<detail::ProxyValueArray>(*psrc.data.values);
|
|
else
|
|
trc->runtime()->gc.nursery.removeMallocedBuffer(psrc.data.values);
|
|
return sizeof(detail::ProxyValueArray);
|
|
}
|
|
|
|
void
|
|
ProxyObject::setCrossCompartmentPrivate(const Value& priv)
|
|
{
|
|
*slotOfPrivate() = priv;
|
|
}
|
|
|
|
void
|
|
ProxyObject::setSameCompartmentPrivate(const Value& priv)
|
|
{
|
|
MOZ_ASSERT(IsObjectValueInCompartment(priv, compartment()));
|
|
*slotOfPrivate() = priv;
|
|
}
|
|
|
|
void
|
|
ProxyObject::nuke()
|
|
{
|
|
// When nuking scripted proxies, isCallable and isConstructor values for
|
|
// the proxy needs to be preserved. Do this before clearing the target.
|
|
uint32_t callable = handler()->isCallable(this)
|
|
? ScriptedProxyHandler::IS_CALLABLE : 0;
|
|
uint32_t constructor = handler()->isConstructor(this)
|
|
? ScriptedProxyHandler::IS_CONSTRUCTOR : 0;
|
|
setExtra(ScriptedProxyHandler::IS_CALLCONSTRUCT_EXTRA,
|
|
PrivateUint32Value(callable | constructor));
|
|
|
|
// Clear the target reference.
|
|
setSameCompartmentPrivate(NullValue());
|
|
|
|
// Update the handler to make this a DeadObjectProxy.
|
|
setHandler(&DeadObjectProxy::singleton);
|
|
|
|
// The proxy's extra slots are not cleared and will continue to be
|
|
// traced. This avoids the possibility of triggering write barriers while
|
|
// nuking proxies in dead compartments which could otherwise cause those
|
|
// compartments to be kept alive. Note that these are slots cannot hold
|
|
// cross compartment pointers, so this cannot cause the target compartment
|
|
// to leak.
|
|
}
|
|
|
|
JS_FRIEND_API(void)
|
|
js::SetValueInProxy(Value* slot, const Value& value)
|
|
{
|
|
// Slots in proxies are not GCPtrValues, so do a cast whenever assigning
|
|
// values to them which might trigger a barrier.
|
|
*reinterpret_cast<GCPtrValue*>(slot) = value;
|
|
}
|