diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/vm/ProxyObject.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | js/src/vm/ProxyObject.cpp | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/js/src/vm/ProxyObject.cpp b/js/src/vm/ProxyObject.cpp new file mode 100644 index 0000000000..24e9d2308c --- /dev/null +++ b/js/src/vm/ProxyObject.cpp @@ -0,0 +1,214 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * 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 "gc/Allocator.h" +#include "gc/GCProbes.h" +#include "proxy/DeadObjectProxy.h" +#include "vm/Realm.h" + +#include "gc/ObjectKind-inl.h" +#include "gc/WeakMap-inl.h" +#include "vm/JSObject-inl.h" + +using namespace js; + +static gc::AllocKind GetProxyGCObjectKind(const JSClass* clasp, + const BaseProxyHandler* handler, + const Value& priv) { + MOZ_ASSERT(clasp->isProxy()); + + uint32_t nreserved = JSCLASS_RESERVED_SLOTS(clasp); + + // For now assert each Proxy Class has at least 1 reserved slot. This is + // not a hard requirement, but helps catch Classes that need an explicit + // JSCLASS_HAS_RESERVED_SLOTS since bug 1360523. + MOZ_ASSERT(nreserved > 0); + + MOZ_ASSERT( + js::detail::ProxyValueArray::sizeOf(nreserved) % sizeof(Value) == 0, + "ProxyValueArray must be a multiple of Value"); + + uint32_t nslots = + js::detail::ProxyValueArray::sizeOf(nreserved) / sizeof(Value); + MOZ_ASSERT(nslots <= NativeObject::MAX_FIXED_SLOTS); + + gc::AllocKind kind = gc::GetGCObjectKind(nslots); + if (handler->finalizeInBackground(priv)) { + kind = ForegroundToBackgroundAllocKind(kind); + } + + return kind; +} + +void ProxyObject::init(const BaseProxyHandler* handler, HandleValue priv, + JSContext* cx) { + setInlineValueArray(); + + detail::ProxyValueArray* values = detail::GetProxyDataLayout(this)->values(); + values->init(numReservedSlots()); + + data.handler = handler; + + if (IsCrossCompartmentWrapper(this)) { + MOZ_ASSERT(cx->global() == &cx->compartment()->globalForNewCCW()); + setCrossCompartmentPrivate(priv); + } else { + setSameCompartmentPrivate(priv); + } + + // The expando slot is nullptr until required by the installation of + // a private field. + setExpando(nullptr); +} + +/* static */ +ProxyObject* ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, + HandleValue priv, TaggedProto proto_, + const JSClass* clasp) { + Rooted<TaggedProto> proto(cx, proto_); + + MOZ_ASSERT(clasp->isProxy()); + MOZ_ASSERT(isValidProxyClass(clasp)); + MOZ_ASSERT(clasp->shouldDelayMetadataBuilder()); + MOZ_ASSERT_IF(proto.isObject(), + cx->compartment() == proto.toObject()->compartment()); + MOZ_ASSERT(clasp->hasFinalize()); + +#ifdef DEBUG + if (priv.isGCThing()) { + JS::AssertCellIsNotGray(priv.toGCThing()); + } +#endif + + gc::AllocKind allocKind = GetProxyGCObjectKind(clasp, handler, priv); + + Realm* realm = cx->realm(); + + AutoSetNewObjectMetadata metadata(cx); + // Try to look up the group and shape in the NewProxyCache. + RootedObjectGroup group(cx); + RootedShape shape(cx); + if (!realm->newProxyCache.lookup(clasp, proto, group.address(), + shape.address())) { + group = ObjectGroup::defaultNewGroup(cx, clasp, proto); + if (!group) { + return nullptr; + } + + shape = EmptyShape::getInitialShape(cx, clasp, proto, /* nfixed = */ 0); + if (!shape) { + return nullptr; + } + + realm->newProxyCache.add(group, shape); + } + + MOZ_ASSERT(group->realm() == realm); + MOZ_ASSERT(shape->zone() == cx->zone()); + MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(group.address())); + MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(shape.address())); + + // Ensure that the wrapper has the same lifetime assumptions as the + // wrappee. Prefer to allocate in the nursery, when possible. + gc::InitialHeap heap; + if ((priv.isGCThing() && priv.toGCThing()->isTenured()) || + !handler->canNurseryAllocate()) { + heap = gc::TenuredHeap; + } else { + heap = gc::DefaultHeap; + } + + debugCheckNewObject(group, shape, allocKind, heap); + + JSObject* obj = + AllocateObject(cx, allocKind, /* nDynamicSlots = */ 0, heap, clasp); + if (!obj) { + return nullptr; + } + + ProxyObject* proxy = static_cast<ProxyObject*>(obj); + proxy->initGroup(group); + proxy->initShape(shape); + + MOZ_ASSERT(clasp->shouldDelayMetadataBuilder()); + realm->setObjectPendingMetadata(cx, proxy); + + gc::gcprobes::CreateObject(proxy); + + proxy->init(handler, priv, cx); + + return proxy; +} + +gc::AllocKind ProxyObject::allocKindForTenure() const { + MOZ_ASSERT(usingInlineValueArray()); + Value priv = private_(); + return GetProxyGCObjectKind(getClass(), data.handler, priv); +} + +void ProxyObject::setCrossCompartmentPrivate(const Value& priv) { + setPrivate(priv); +} + +void ProxyObject::setSameCompartmentPrivate(const Value& priv) { + MOZ_ASSERT(IsObjectValueInCompartment(priv, compartment())); + setPrivate(priv); +} + +inline void ProxyObject::setPrivate(const Value& priv) { + MOZ_ASSERT_IF(IsMarkedBlack(this) && priv.isGCThing(), + !JS::GCThingIsMarkedGray(JS::GCCellPtr(priv))); + *slotOfPrivate() = priv; +} + +void ProxyObject::setExpando(JSObject* expando) { + // Ensure that we don't accidentally end up pointing to a + // grey object, which would violate GC invariants. + MOZ_ASSERT_IF(IsMarkedBlack(this) && expando, + !JS::GCThingIsMarkedGray(JS::GCCellPtr(expando))); + + // Ensure we're in the same compartment as the proxy object: Don't want the + // expando to end up as a CCW. + MOZ_ASSERT_IF(expando, expando->compartment() == compartment()); + *slotOfExpando() = ObjectOrNullValue(expando); +} + +void ProxyObject::nuke() { + // Notify the zone that a delegate is no longer a delegate. Be careful not to + // expose this pointer, because it has already been removed from the wrapper + // map yet we have assertions during tracing that will verify that it is + // still present. + JSObject* delegate = UncheckedUnwrapWithoutExpose(this); + if (delegate != this) { + delegate->zone()->beforeClearDelegate(this, delegate); + } + + // Clear the target reference and replaced it with a value that encodes + // various information about the original target. + setSameCompartmentPrivate(DeadProxyTargetValue(this)); + + // Clear out the expando + setExpando(nullptr); + + // Update the handler to make this a DeadObjectProxy. + setHandler(&DeadObjectProxy::singleton); + + // The proxy's reserved 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::detail::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; +} |