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 "builtin/WeakRefObject.h"
11 #include "gc/FinalizationObservers.h"
12 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
13 #include "vm/GlobalObject.h"
14 #include "vm/JSContext.h"
16 #include "gc/PrivateIterators-inl.h"
17 #include "vm/JSObject-inl.h"
18 #include "vm/NativeObject-inl.h"
23 bool WeakRefObject::construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
24 CallArgs args
= CallArgsFromVp(argc
, vp
);
26 // https://tc39.es/proposal-weakrefs/#sec-weak-ref-constructor
27 // The WeakRef constructor is not intended to be called as a function and will
28 // throw an exception when called in that manner.
29 if (!ThrowIfNotConstructing(cx
, args
, "WeakRef")) {
33 // https://tc39.es/proposal-weakrefs/#sec-weak-ref-target
34 // 1. If NewTarget is undefined, throw a TypeError exception.
35 // 2. If Type(target) is not Object, throw a TypeError exception.
36 if (!args
.get(0).isObject()) {
37 ReportNotObject(cx
, args
.get(0));
41 // 3. Let weakRef be ? OrdinaryCreateFromConstructor(NewTarget,
42 // "%WeakRefPrototype%", « [[Target]] »).
43 RootedObject
proto(cx
);
44 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_WeakRef
, &proto
)) {
48 Rooted
<WeakRefObject
*> weakRef(
49 cx
, NewObjectWithClassProto
<WeakRefObject
>(cx
, proto
));
54 RootedObject
target(cx
);
55 target
= CheckedUnwrapDynamic(&args
[0].toObject(), cx
);
57 ReportAccessDenied(cx
);
61 // If the target is a DOM wrapper, preserve it.
62 if (!preserveDOMWrapper(cx
, target
)) {
66 // Wrap the weakRef into the target's Zone. This is a cross-compartment
67 // wrapper if the Zone is different, or same-compartment (the original
68 // object) if the Zone is the same *even if* the compartments are different.
69 RootedObject
wrappedWeakRef(cx
, weakRef
);
70 bool sameZone
= target
->zone() == weakRef
->zone();
71 AutoRealm
ar(cx
, sameZone
? weakRef
: target
);
72 if (!JS_WrapObject(cx
, &wrappedWeakRef
)) {
76 if (JS_IsDeadWrapper(wrappedWeakRef
)) {
77 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
81 // 4. Perform AddToKeptObjects(target).
82 if (!target
->zone()->addToKeptObjects(target
)) {
83 ReportOutOfMemory(cx
);
87 // Add an entry to the per-zone maps from target JS object to a list of weak
89 gc::GCRuntime
* gc
= &cx
->runtime()->gc
;
90 if (!gc
->registerWeakRef(target
, wrappedWeakRef
)) {
91 ReportOutOfMemory(cx
);
95 // 5. Set weakRef.[[Target]] to target.
96 weakRef
->setReservedSlotGCThingAsPrivate(TargetSlot
, target
);
99 args
.rval().setObject(*weakRef
);
104 bool WeakRefObject::preserveDOMWrapper(JSContext
* cx
, HandleObject obj
) {
105 if (!MaybePreserveDOMWrapper(cx
, obj
)) {
106 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
107 JSMSG_BAD_WEAKREF_TARGET
);
115 void WeakRefObject::trace(JSTracer
* trc
, JSObject
* obj
) {
116 WeakRefObject
* weakRef
= &obj
->as
<WeakRefObject
>();
118 if (trc
->traceWeakEdges()) {
119 JSObject
* target
= weakRef
->target();
121 TraceManuallyBarrieredEdge(trc
, &target
, "WeakRefObject::target");
122 weakRef
->setTargetUnbarriered(target
);
128 void WeakRefObject::finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
129 // The target is cleared when the target's zone is swept and that always
130 // happens before this object is finalized because of the CCW from the target
131 // zone to this object. If the CCW is nuked, the target is cleared in
132 // NotifyGCNukeWrapper().
133 MOZ_ASSERT(!obj
->as
<WeakRefObject
>().target());
136 const JSClassOps
WeakRefObject::classOps_
= {
137 nullptr, // addProperty
138 nullptr, // delProperty
139 nullptr, // enumerate
140 nullptr, // newEnumerate
142 nullptr, // mayResolve
143 finalize
, // finalize
145 nullptr, // construct
149 const ClassSpec
WeakRefObject::classSpec_
= {
150 GenericCreateConstructor
<WeakRefObject::construct
, 1,
151 gc::AllocKind::FUNCTION
>,
152 GenericCreatePrototype
<WeakRefObject
>,
155 WeakRefObject::methods
,
156 WeakRefObject::properties
,
159 const JSClass
WeakRefObject::class_
= {
161 JSCLASS_HAS_RESERVED_SLOTS(SlotCount
) |
162 JSCLASS_HAS_CACHED_PROTO(JSProto_WeakRef
) | JSCLASS_FOREGROUND_FINALIZE
,
163 &classOps_
, &classSpec_
};
165 const JSClass
WeakRefObject::protoClass_
= {
166 // https://tc39.es/proposal-weakrefs/#sec-weak-ref.prototype
167 // https://tc39.es/proposal-weakrefs/#sec-properties-of-the-weak-ref-prototype-object
168 "WeakRef.prototype", JSCLASS_HAS_CACHED_PROTO(JSProto_WeakRef
),
169 JS_NULL_CLASS_OPS
, &classSpec_
};
171 const JSPropertySpec
WeakRefObject::properties
[] = {
172 JS_STRING_SYM_PS(toStringTag
, "WeakRef", JSPROP_READONLY
), JS_PS_END
};
174 const JSFunctionSpec
WeakRefObject::methods
[] = {JS_FN("deref", deref
, 0, 0),
178 bool WeakRefObject::deref(JSContext
* cx
, unsigned argc
, Value
* vp
) {
179 CallArgs args
= CallArgsFromVp(argc
, vp
);
181 // https://tc39.es/proposal-weakrefs/#sec-weak-ref.prototype.deref
182 // 1. Let weakRef be the this value.
183 // 2. If Type(weakRef) is not Object, throw a TypeError exception.
184 // 3. If weakRef does not have a [[Target]] internal slot, throw a TypeError
186 if (!args
.thisv().isObject() ||
187 !args
.thisv().toObject().is
<WeakRefObject
>()) {
188 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
189 JSMSG_NOT_A_WEAK_REF
,
190 "Receiver of WeakRef.deref call");
194 Rooted
<WeakRefObject
*> weakRef(cx
,
195 &args
.thisv().toObject().as
<WeakRefObject
>());
197 // We need to perform a read barrier, which may clear the target.
198 readBarrier(cx
, weakRef
);
200 // 4. Let target be the value of weakRef.[[Target]].
201 // 5. If target is not empty,
202 // a. Perform AddToKeptObjects(target).
204 // 6. Return undefined.
205 if (!weakRef
->target()) {
206 args
.rval().setUndefined();
210 RootedObject
target(cx
, weakRef
->target());
211 if (!target
->zone()->addToKeptObjects(target
)) {
215 // Target should be wrapped into the current realm before returning it.
216 RootedObject
wrappedTarget(cx
, target
);
217 if (!JS_WrapObject(cx
, &wrappedTarget
)) {
221 args
.rval().setObject(*wrappedTarget
);
225 void WeakRefObject::setTargetUnbarriered(JSObject
* target
) {
226 setReservedSlotGCThingAsPrivateUnbarriered(TargetSlot
, target
);
229 void WeakRefObject::clearTarget() {
230 clearReservedSlotGCThingAsPrivate(TargetSlot
);
234 void WeakRefObject::readBarrier(JSContext
* cx
, Handle
<WeakRefObject
*> self
) {
235 RootedObject
obj(cx
, self
->target());
240 if (obj
->getClass()->isDOMClass()) {
241 // We preserved the target when the WeakRef was created. If it has since
242 // been released then the DOM object it wraps has been collected, so clear
244 MOZ_ASSERT(cx
->runtime()->hasReleasedWrapperCallback
);
245 bool wasReleased
= cx
->runtime()->hasReleasedWrapperCallback(obj
);
247 obj
->zone()->finalizationObservers()->removeWeakRefTarget(obj
, self
);
252 gc::ReadBarrier(obj
.get());
257 void GCRuntime::traceKeptObjects(JSTracer
* trc
) {
258 for (GCZonesIter
zone(this); !zone
.done(); zone
.next()) {
259 zone
->traceKeptObjects(trc
);