Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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/. */
6 :
7 : #include "vm/ProxyObject.h"
8 :
9 : #include "jscompartment.h"
10 :
11 : #include "proxy/DeadObjectProxy.h"
12 :
13 : #include "jsobjinlines.h"
14 :
15 : using namespace js;
16 :
17 : static gc::AllocKind
18 11438 : GetProxyGCObjectKind(const Class* clasp, const BaseProxyHandler* handler, const Value& priv)
19 : {
20 11438 : MOZ_ASSERT(clasp->isProxy());
21 :
22 11438 : uint32_t nreserved = JSCLASS_RESERVED_SLOTS(clasp);
23 :
24 : // For now assert each Proxy Class has at least 1 reserved slot. This is
25 : // not a hard requirement, but helps catch Classes that need an explicit
26 : // JSCLASS_HAS_RESERVED_SLOTS since bug 1360523.
27 11438 : MOZ_ASSERT(nreserved > 0);
28 :
29 11438 : MOZ_ASSERT(js::detail::ProxyValueArray::sizeOf(nreserved) % sizeof(Value) == 0,
30 : "ProxyValueArray must be a multiple of Value");
31 :
32 11438 : uint32_t nslots = js::detail::ProxyValueArray::sizeOf(nreserved) / sizeof(Value);
33 11438 : MOZ_ASSERT(nslots <= NativeObject::MAX_FIXED_SLOTS);
34 :
35 11438 : gc::AllocKind kind = gc::GetGCObjectKind(nslots);
36 11438 : if (handler->finalizeInBackground(priv))
37 8924 : kind = GetBackgroundAllocKind(kind);
38 :
39 11438 : return kind;
40 : }
41 :
42 : /* static */ ProxyObject*
43 9333 : ProxyObject::New(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv, TaggedProto proto_,
44 : const ProxyOptions& options)
45 : {
46 18666 : Rooted<TaggedProto> proto(cx, proto_);
47 :
48 9333 : const Class* clasp = options.clasp();
49 :
50 9333 : MOZ_ASSERT(isValidProxyClass(clasp));
51 9333 : MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
52 9333 : MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
53 9333 : MOZ_ASSERT(clasp->hasFinalize());
54 :
55 : /*
56 : * Eagerly mark properties unknown for proxies, so we don't try to track
57 : * their properties and so that we don't need to walk the compartment if
58 : * their prototype changes later. But don't do this for DOM proxies,
59 : * because we want to be able to keep track of them in typesets in useful
60 : * ways.
61 : */
62 9333 : if (proto.isObject() && !options.singleton() && !clasp->isDOMClass()) {
63 0 : RootedObject protoObj(cx, proto.toObject());
64 0 : if (!JSObject::setNewGroupUnknown(cx, clasp, protoObj))
65 0 : return nullptr;
66 : }
67 :
68 : // Ensure that the wrapper has the same lifetime assumptions as the
69 : // wrappee. Prefer to allocate in the nursery, when possible.
70 9333 : NewObjectKind newKind = NurseryAllocatedProxy;
71 9333 : if (options.singleton()) {
72 14 : MOZ_ASSERT(priv.isNull() || (priv.isGCThing() && priv.toGCThing()->isTenured()));
73 14 : newKind = SingletonObject;
74 12765 : } else if ((priv.isGCThing() && priv.toGCThing()->isTenured()) ||
75 3446 : !handler->canNurseryAllocate())
76 : {
77 5918 : newKind = TenuredObject;
78 : }
79 :
80 9333 : gc::AllocKind allocKind = GetProxyGCObjectKind(clasp, handler, priv);
81 :
82 18666 : AutoSetNewObjectMetadata metadata(cx);
83 : // Note: this will initialize the object's |data| to strange values, but we
84 : // will immediately overwrite those below.
85 : ProxyObject* proxy;
86 9333 : JS_TRY_VAR_OR_RETURN_NULL(cx, proxy, create(cx, clasp, proto, allocKind, newKind));
87 :
88 9333 : proxy->setInlineValueArray();
89 :
90 9333 : detail::ProxyValueArray* values = detail::GetProxyDataLayout(proxy)->values();
91 9333 : values->init(proxy->numReservedSlots());
92 :
93 9333 : proxy->data.handler = handler;
94 9333 : if (IsCrossCompartmentWrapper(proxy))
95 9256 : proxy->setCrossCompartmentPrivate(priv);
96 : else
97 77 : proxy->setSameCompartmentPrivate(priv);
98 :
99 : /* Don't track types of properties of non-DOM and non-singleton proxies. */
100 9333 : if (newKind != SingletonObject && !clasp->isDOMClass())
101 9266 : MarkObjectGroupUnknownProperties(cx, proxy->group());
102 :
103 9333 : return proxy;
104 : }
105 :
106 : gc::AllocKind
107 2105 : ProxyObject::allocKindForTenure() const
108 : {
109 2105 : MOZ_ASSERT(usingInlineValueArray());
110 2105 : Value priv = const_cast<ProxyObject*>(this)->private_();
111 2105 : return GetProxyGCObjectKind(getClass(), data.handler, priv);
112 : }
113 :
114 : void
115 9266 : ProxyObject::setCrossCompartmentPrivate(const Value& priv)
116 : {
117 9266 : *slotOfPrivate() = priv;
118 9266 : }
119 :
120 : void
121 121 : ProxyObject::setSameCompartmentPrivate(const Value& priv)
122 : {
123 121 : MOZ_ASSERT(IsObjectValueInCompartment(priv, compartment()));
124 121 : *slotOfPrivate() = priv;
125 121 : }
126 :
127 : void
128 44 : ProxyObject::nuke()
129 : {
130 : // Select a dead proxy handler based on the properties of this wrapper.
131 : // Do this before clearing the target.
132 44 : const BaseProxyHandler* handler = SelectDeadProxyHandler(this);
133 :
134 : // Clear the target reference.
135 44 : setSameCompartmentPrivate(NullValue());
136 :
137 : // Update the handler to make this a DeadObjectProxy.
138 44 : setHandler(handler);
139 :
140 : // The proxy's reserved slots are not cleared and will continue to be
141 : // traced. This avoids the possibility of triggering write barriers while
142 : // nuking proxies in dead compartments which could otherwise cause those
143 : // compartments to be kept alive. Note that these are slots cannot hold
144 : // cross compartment pointers, so this cannot cause the target compartment
145 : // to leak.
146 44 : }
147 :
148 : /* static */ JS::Result<ProxyObject*, JS::OOM&>
149 9333 : ProxyObject::create(JSContext* cx, const Class* clasp, Handle<TaggedProto> proto,
150 : gc::AllocKind allocKind, NewObjectKind newKind)
151 : {
152 9333 : MOZ_ASSERT(clasp->isProxy());
153 :
154 9333 : JSCompartment* comp = cx->compartment();
155 18666 : RootedObjectGroup group(cx);
156 18666 : RootedShape shape(cx);
157 :
158 : // Try to look up the group and shape in the NewProxyCache.
159 9333 : if (!comp->newProxyCache.lookup(clasp, proto, group.address(), shape.address())) {
160 330 : group = ObjectGroup::defaultNewGroup(cx, clasp, proto, nullptr);
161 330 : if (!group)
162 0 : return cx->alreadyReportedOOM();
163 :
164 330 : shape = EmptyShape::getInitialShape(cx, clasp, proto, /* nfixed = */ 0);
165 330 : if (!shape)
166 0 : return cx->alreadyReportedOOM();
167 :
168 330 : comp->newProxyCache.add(group, shape);
169 : }
170 :
171 9333 : gc::InitialHeap heap = GetInitialHeap(newKind, clasp);
172 9333 : debugCheckNewObject(group, shape, allocKind, heap);
173 :
174 9333 : JSObject* obj = js::Allocate<JSObject>(cx, allocKind, /* numDynamicSlots = */ 0, heap, clasp);
175 9333 : if (!obj)
176 0 : return cx->alreadyReportedOOM();
177 :
178 9333 : ProxyObject* pobj = static_cast<ProxyObject*>(obj);
179 9333 : pobj->group_.init(group);
180 9333 : pobj->initShape(shape);
181 :
182 9333 : MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
183 9333 : cx->compartment()->setObjectPendingMetadata(cx, pobj);
184 :
185 9333 : js::gc::TraceCreateObject(pobj);
186 :
187 9333 : if (newKind == SingletonObject) {
188 28 : Rooted<ProxyObject*> pobjRoot(cx, pobj);
189 14 : if (!JSObject::setSingleton(cx, pobjRoot))
190 0 : return cx->alreadyReportedOOM();
191 14 : pobj = pobjRoot;
192 : }
193 :
194 9333 : return pobj;
195 : }
196 :
197 : JS_FRIEND_API(void)
198 106 : js::SetValueInProxy(Value* slot, const Value& value)
199 : {
200 : // Slots in proxies are not GCPtrValues, so do a cast whenever assigning
201 : // values to them which might trigger a barrier.
202 106 : *reinterpret_cast<GCPtrValue*>(slot) = value;
203 106 : }
|