Bug 1874684 - Part 21: Rename SecondsAndNanoseconds::toTotalNanoseconds. r=dminor
[gecko.git] / js / src / builtin / WeakRefObject.cpp
blobd198b6a3a0855f38b9f37984cd6be23585027ca3
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"
9 #include "jsapi.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"
20 namespace js {
22 /* static */
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")) {
30 return false;
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));
38 return false;
41 // 3. Let weakRef be ? OrdinaryCreateFromConstructor(NewTarget,
42 // "%WeakRefPrototype%", « [[Target]] »).
43 RootedObject proto(cx);
44 if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WeakRef, &proto)) {
45 return false;
48 Rooted<WeakRefObject*> weakRef(
49 cx, NewObjectWithClassProto<WeakRefObject>(cx, proto));
50 if (!weakRef) {
51 return false;
54 RootedObject target(cx);
55 target = CheckedUnwrapDynamic(&args[0].toObject(), cx);
56 if (!target) {
57 ReportAccessDenied(cx);
58 return false;
61 // If the target is a DOM wrapper, preserve it.
62 if (!preserveDOMWrapper(cx, target)) {
63 return false;
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)) {
73 return false;
76 if (JS_IsDeadWrapper(wrappedWeakRef)) {
77 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
78 return false;
81 // 4. Perform AddToKeptObjects(target).
82 if (!target->zone()->addToKeptObjects(target)) {
83 ReportOutOfMemory(cx);
84 return false;
87 // Add an entry to the per-zone maps from target JS object to a list of weak
88 // ref objects.
89 gc::GCRuntime* gc = &cx->runtime()->gc;
90 if (!gc->registerWeakRef(target, wrappedWeakRef)) {
91 ReportOutOfMemory(cx);
92 return false;
95 // 5. Set weakRef.[[Target]] to target.
96 weakRef->setReservedSlotGCThingAsPrivate(TargetSlot, target);
98 // 6. Return weakRef.
99 args.rval().setObject(*weakRef);
100 return true;
103 /* static */
104 bool WeakRefObject::preserveDOMWrapper(JSContext* cx, HandleObject obj) {
105 if (!MaybePreserveDOMWrapper(cx, obj)) {
106 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
107 JSMSG_BAD_WEAKREF_TARGET);
108 return false;
111 return true;
114 /* static */
115 void WeakRefObject::trace(JSTracer* trc, JSObject* obj) {
116 WeakRefObject* weakRef = &obj->as<WeakRefObject>();
118 if (trc->traceWeakEdges()) {
119 JSObject* target = weakRef->target();
120 if (target) {
121 TraceManuallyBarrieredEdge(trc, &target, "WeakRefObject::target");
122 weakRef->setTargetUnbarriered(target);
127 /* static */
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
141 nullptr, // resolve
142 nullptr, // mayResolve
143 finalize, // finalize
144 nullptr, // call
145 nullptr, // construct
146 trace, // trace
149 const ClassSpec WeakRefObject::classSpec_ = {
150 GenericCreateConstructor<WeakRefObject::construct, 1,
151 gc::AllocKind::FUNCTION>,
152 GenericCreatePrototype<WeakRefObject>,
153 nullptr,
154 nullptr,
155 WeakRefObject::methods,
156 WeakRefObject::properties,
159 const JSClass WeakRefObject::class_ = {
160 "WeakRef",
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),
175 JS_FS_END};
177 /* static */
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
185 // exception.
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");
191 return false;
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).
203 // b. Return target.
204 // 6. Return undefined.
205 if (!weakRef->target()) {
206 args.rval().setUndefined();
207 return true;
210 RootedObject target(cx, weakRef->target());
211 if (!target->zone()->addToKeptObjects(target)) {
212 return false;
215 // Target should be wrapped into the current realm before returning it.
216 RootedObject wrappedTarget(cx, target);
217 if (!JS_WrapObject(cx, &wrappedTarget)) {
218 return false;
221 args.rval().setObject(*wrappedTarget);
222 return true;
225 void WeakRefObject::setTargetUnbarriered(JSObject* target) {
226 setReservedSlotGCThingAsPrivateUnbarriered(TargetSlot, target);
229 void WeakRefObject::clearTarget() {
230 clearReservedSlotGCThingAsPrivate(TargetSlot);
233 /* static */
234 void WeakRefObject::readBarrier(JSContext* cx, Handle<WeakRefObject*> self) {
235 RootedObject obj(cx, self->target());
236 if (!obj) {
237 return;
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
243 // the target.
244 MOZ_ASSERT(cx->runtime()->hasReleasedWrapperCallback);
245 bool wasReleased = cx->runtime()->hasReleasedWrapperCallback(obj);
246 if (wasReleased) {
247 obj->zone()->finalizationObservers()->removeWeakRefTarget(obj, self);
248 return;
252 gc::ReadBarrier(obj.get());
255 namespace gc {
257 void GCRuntime::traceKeptObjects(JSTracer* trc) {
258 for (GCZonesIter zone(this); !zone.done(); zone.next()) {
259 zone->traceKeptObjects(trc);
263 } // namespace gc
265 } // namespace js