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 #ifndef builtin_temporal_Wrapped_h
8 #define builtin_temporal_Wrapped_h
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Attributes.h"
13 #include <type_traits>
15 #include "gc/Tracer.h"
16 #include "js/RootingAPI.h"
17 #include "vm/JSObject.h"
18 #include "vm/NativeObject.h"
20 namespace js::temporal
{
23 * Type to represent possibly wrapped objects from a different compartment.
25 * This can be used to represent specific JSObject sub-classes in return types
26 * without having to pass unwrapped objects around.
29 class MOZ_STACK_CLASS Wrapped final
{
30 static_assert(std::is_pointer_v
<T
>);
31 static_assert(std::is_convertible_v
<T
, NativeObject
*>);
33 using U
= std::remove_pointer_t
<T
>;
35 JSObject
* ptr_
= nullptr;
40 MOZ_IMPLICIT
Wrapped(decltype(nullptr)) : ptr_(nullptr) {}
42 MOZ_IMPLICIT
Wrapped(T ptr
) : ptr_(ptr
) {
43 // No assertion needed when the object already has the correct type.
46 MOZ_IMPLICIT
Wrapped(JSObject
* ptr
) : ptr_(ptr
) {
47 // Ensure the caller passed a valid pointer.
48 MOZ_ASSERT_IF(ptr_
, ptr_
->canUnwrapAs
<U
>());
53 const JS::Rooted
<S
>& root
,
54 std::enable_if_t
<std::is_convertible_v
<S
, T
>, int> dummy
= 0)
55 : Wrapped(root
.get()) {}
57 MOZ_IMPLICIT
Wrapped(const JS::Rooted
<JSObject
*>& root
)
58 : Wrapped(root
.get()) {}
62 const JS::Handle
<S
>& root
,
63 std::enable_if_t
<std::is_convertible_v
<S
, T
>, int> dummy
= 0)
64 : Wrapped(root
.get()) {}
66 MOZ_IMPLICIT
Wrapped(const JS::Handle
<JSObject
*>& root
)
67 : Wrapped(root
.get()) {}
71 const JS::MutableHandle
<S
>& root
,
72 std::enable_if_t
<std::is_convertible_v
<S
, T
>, int> dummy
= 0)
73 : Wrapped(root
.get()) {}
75 MOZ_IMPLICIT
Wrapped(const JS::MutableHandle
<JSObject
*>& root
)
76 : Wrapped(root
.get()) {}
78 Wrapped
& operator=(decltype(nullptr)) {
83 Wrapped
& operator=(T ptr
) {
88 explicit operator bool() const { return !!ptr_
; }
90 JSObject
* operator->() const { return ptr_
; }
92 JSObject
& operator*() const { return *ptr_
; }
94 JSObject
* get() const { return ptr_
; }
96 operator JSObject
*() const { return get(); }
98 auto address() const { return &ptr_
; }
103 // Direct unwrap because the constructor already verified the object can be
106 // We use JSObject::unwrapAs() instead of JSObject::maybeUnwrapIf(), because
107 // this is an unrooted Wrapped, so hazard analysis will ensure that no
108 // wrappers have been invalidated, because wrapper invalidation generally
109 // only happens in the same case as GC.
111 // Rooted Wrapped are accessed through their WrappedPtrOperations
112 // specialization, which uses JSObject::maybeUnwrapIf() to handle the
113 // wrapper invalidation case correctly.
114 return ptr_
->unwrapAs
<U
>();
117 U
* unwrapOrNull() const {
118 // Direct unwrap because the constructor already verified the object can be
121 // See Wrapped::unwrap() for why we don't call maybeUnwrapIf() here.
122 return ptr_
? &ptr_
->unwrapAs
<U
>() : nullptr;
125 void trace(JSTracer
* trc
) { TraceNullableRoot(trc
, &ptr_
, "Wrapped::ptr_"); }
128 void ReportDeadWrapperOrAccessDenied(JSContext
* cx
, JSObject
* obj
);
130 } /* namespace js::temporal */
133 template <typename T
, typename Container
>
134 class WrappedPtrOperations
<temporal::Wrapped
<T
>, Container
> {
135 using U
= std::remove_pointer_t
<T
>;
137 const auto& wrapped() const {
138 return static_cast<const Container
*>(this)->get();
142 explicit operator bool() const { return !!wrapped(); }
144 JSObject
* operator->() const { return wrapped().get(); }
146 JSObject
& operator*() const { return *wrapped().get(); }
148 JS::Handle
<JSObject
*> object() const {
149 return JS::Handle
<JSObject
*>::fromMarkedLocation(wrapped().address());
152 operator JS::Handle
<JSObject
*>() const { return object(); }
154 [[nodiscard
]] U
* unwrap(JSContext
* cx
) const {
155 JSObject
* obj
= wrapped().get();
157 // Call JSObject::maybeUnwrapIf() instead of JSObject::unwrapAs() in case
158 // |obj| is an invalidated wrapper.
159 if (auto* unwrapped
= obj
->maybeUnwrapIf
<U
>()) {
163 temporal::ReportDeadWrapperOrAccessDenied(cx
, obj
);
169 #endif /* builtin_temporal_Wrapped_h */