1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "vm/ProxyObject.h"
9 #include "gc/GCProbes.h"
10 #include "gc/Marking.h"
12 #include "proxy/DeadObjectProxy.h"
13 #include "vm/Compartment.h"
16 #include "gc/ObjectKind-inl.h"
17 #include "vm/JSContext-inl.h"
21 static gc::AllocKind
GetProxyGCObjectKind(const JSClass
* clasp
,
22 const BaseProxyHandler
* handler
,
24 bool withInlineValues
) {
25 MOZ_ASSERT(clasp
->isProxyObject());
27 uint32_t nreserved
= JSCLASS_RESERVED_SLOTS(clasp
);
29 // For now assert each Proxy Class has at least 1 reserved slot. This is
30 // not a hard requirement, but helps catch Classes that need an explicit
31 // JSCLASS_HAS_RESERVED_SLOTS since bug 1360523.
32 MOZ_ASSERT(nreserved
> 0);
35 if (withInlineValues
) {
36 nslots
= detail::ProxyValueArray::allocCount(nreserved
);
39 MOZ_ASSERT(nslots
<= NativeObject::MAX_FIXED_SLOTS
);
40 gc::AllocKind kind
= gc::GetGCObjectKind(nslots
);
41 if (handler
->finalizeInBackground(priv
)) {
42 kind
= ForegroundToBackgroundAllocKind(kind
);
48 void ProxyObject::init(const BaseProxyHandler
* handler
, HandleValue priv
,
50 setInlineValueArray();
52 detail::ProxyValueArray
* values
= detail::GetProxyDataLayout(this)->values();
53 values
->init(numReservedSlots());
55 data
.handler
= handler
;
57 if (IsCrossCompartmentWrapper(this)) {
58 MOZ_ASSERT(cx
->global() == &cx
->compartment()->globalForNewCCW());
59 setCrossCompartmentPrivate(priv
);
61 setSameCompartmentPrivate(priv
);
64 // The expando slot is nullptr until required by the installation of
70 ProxyObject
* ProxyObject::New(JSContext
* cx
, const BaseProxyHandler
* handler
,
71 HandleValue priv
, TaggedProto proto_
,
72 const JSClass
* clasp
) {
73 Rooted
<TaggedProto
> proto(cx
, proto_
);
75 MOZ_ASSERT(!clasp
->isNativeObject());
76 MOZ_ASSERT(clasp
->isProxyObject());
77 MOZ_ASSERT(isValidProxyClass(clasp
));
78 MOZ_ASSERT(clasp
->shouldDelayMetadataBuilder());
79 MOZ_ASSERT_IF(proto
.isObject(),
80 cx
->compartment() == proto
.toObject()->compartment());
81 MOZ_ASSERT(clasp
->hasFinalize());
84 if (priv
.isGCThing()) {
85 JS::AssertCellIsNotGray(priv
.toGCThing());
89 gc::AllocKind allocKind
= GetProxyGCObjectKind(clasp
, handler
, priv
,
90 /* withInlineValues = */ true);
92 Realm
* realm
= cx
->realm();
94 AutoSetNewObjectMetadata
metadata(cx
);
95 // Try to look up the shape in the NewProxyCache.
96 Rooted
<Shape
*> shape(cx
);
97 if (!realm
->newProxyCache
.lookup(clasp
, proto
, shape
.address())) {
98 shape
= ProxyShape::getShape(cx
, clasp
, realm
, proto
, ObjectFlags());
103 realm
->newProxyCache
.add(shape
);
106 MOZ_ASSERT(shape
->realm() == realm
);
107 MOZ_ASSERT(!IsAboutToBeFinalizedUnbarriered(shape
.get()));
109 // Ensure that the wrapper has the same lifetime assumptions as the
110 // wrappee. Prefer to allocate in the nursery, when possible.
112 if ((priv
.isGCThing() && priv
.toGCThing()->isTenured()) ||
113 !handler
->canNurseryAllocate()) {
114 heap
= gc::Heap::Tenured
;
116 heap
= gc::Heap::Default
;
119 debugCheckNewObject(shape
, allocKind
, heap
);
121 ProxyObject
* proxy
= cx
->newCell
<ProxyObject
>(allocKind
, heap
, clasp
);
126 proxy
->initShape(shape
);
128 MOZ_ASSERT(clasp
->shouldDelayMetadataBuilder());
129 realm
->setObjectPendingMetadata(proxy
);
131 gc::gcprobes::CreateObject(proxy
);
133 proxy
->init(handler
, priv
, cx
);
138 gc::AllocKind
ProxyObject::allocKindForTenure() const {
139 Value priv
= private_();
140 return GetProxyGCObjectKind(getClass(), data
.handler
, priv
,
141 usingInlineValueArray());
144 void ProxyObject::setCrossCompartmentPrivate(const Value
& priv
) {
148 void ProxyObject::setSameCompartmentPrivate(const Value
& priv
) {
149 MOZ_ASSERT(IsObjectValueInCompartment(priv
, compartment()));
153 inline void ProxyObject::setPrivate(const Value
& priv
) {
155 JS::AssertValueIsNotGray(priv
);
157 *slotOfPrivate() = priv
;
160 void ProxyObject::setExpando(JSObject
* expando
) {
161 // Ensure we're in the same compartment as the proxy object: Don't want the
162 // expando to end up as a CCW.
163 MOZ_ASSERT_IF(expando
, expando
->compartment() == compartment());
165 // Ensure that we don't accidentally end up pointing to a
166 // grey object, which would violate GC invariants.
167 MOZ_ASSERT_IF(!zone()->isGCPreparing() && isMarkedBlack() && expando
,
168 !JS::GCThingIsMarkedGray(JS::GCCellPtr(expando
)));
170 *slotOfExpando() = ObjectOrNullValue(expando
);
173 void ProxyObject::nuke() {
174 // Notify the zone that a delegate is no longer a delegate. Be careful not to
175 // expose this pointer, because it has already been removed from the wrapper
176 // map yet we have assertions during tracing that will verify that it is
178 JSObject
* delegate
= UncheckedUnwrapWithoutExpose(this);
179 if (delegate
!= this) {
180 delegate
->zone()->beforeClearDelegate(this, delegate
);
183 // Clear the target reference and replaced it with a value that encodes
184 // various information about the original target.
185 setSameCompartmentPrivate(DeadProxyTargetValue(this));
187 // Clear out the expando
190 // Update the handler to make this a DeadObjectProxy.
191 setHandler(&DeadObjectProxy::singleton
);
193 // The proxy's reserved slots are not cleared and will continue to be
194 // traced. This avoids the possibility of triggering write barriers while
195 // nuking proxies in dead compartments which could otherwise cause those
196 // compartments to be kept alive. Note that these are slots cannot hold
197 // cross compartment pointers, so this cannot cause the target compartment
201 JS_PUBLIC_API
void js::detail::SetValueInProxy(Value
* slot
,
202 const Value
& value
) {
203 // Slots in proxies are not GCPtr<Value>s, so do a cast whenever assigning
204 // values to them which might trigger a barrier.
205 *reinterpret_cast<GCPtr
<Value
>*>(slot
) = value
;