Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / vm / ProxyObject.cpp
blob3d655291313435b07c0b5c16986fdb5c6038e001
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"
11 #include "gc/Zone.h"
12 #include "proxy/DeadObjectProxy.h"
13 #include "vm/Compartment.h"
14 #include "vm/Realm.h"
16 #include "gc/ObjectKind-inl.h"
17 #include "vm/JSContext-inl.h"
19 using namespace js;
21 static gc::AllocKind GetProxyGCObjectKind(const JSClass* clasp,
22 const BaseProxyHandler* handler,
23 const Value& priv,
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);
34 uint32_t nslots = 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);
45 return kind;
48 void ProxyObject::init(const BaseProxyHandler* handler, HandleValue priv,
49 JSContext* cx) {
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);
60 } else {
61 setSameCompartmentPrivate(priv);
64 // The expando slot is nullptr until required by the installation of
65 // a private field.
66 setExpando(nullptr);
69 /* static */
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());
83 #ifdef DEBUG
84 if (priv.isGCThing()) {
85 JS::AssertCellIsNotGray(priv.toGCThing());
87 #endif
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());
99 if (!shape) {
100 return nullptr;
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.
111 gc::Heap heap;
112 if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
113 !handler->canNurseryAllocate()) {
114 heap = gc::Heap::Tenured;
115 } else {
116 heap = gc::Heap::Default;
119 debugCheckNewObject(shape, allocKind, heap);
121 ProxyObject* proxy = cx->newCell<ProxyObject>(allocKind, heap, clasp);
122 if (!proxy) {
123 return nullptr;
126 proxy->initShape(shape);
128 MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
129 realm->setObjectPendingMetadata(proxy);
131 gc::gcprobes::CreateObject(proxy);
133 proxy->init(handler, priv, cx);
135 return proxy;
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) {
145 setPrivate(priv);
148 void ProxyObject::setSameCompartmentPrivate(const Value& priv) {
149 MOZ_ASSERT(IsObjectValueInCompartment(priv, compartment()));
150 setPrivate(priv);
153 inline void ProxyObject::setPrivate(const Value& priv) {
154 #ifdef DEBUG
155 JS::AssertValueIsNotGray(priv);
156 #endif
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
177 // still present.
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
188 setExpando(nullptr);
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
198 // to leak.
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;