Skip to content

Commit

Permalink
Bug 1736737 - Implement Heap<ArrayBufferOrView> (for all subtypes) r=…
Browse files Browse the repository at this point in the history
…jonco

Differential Revision: https://phabricator.services.mozilla.com/D129328
  • Loading branch information
hotsphink committed Oct 25, 2021
1 parent c13a461 commit baf7e9e
Show file tree
Hide file tree
Showing 5 changed files with 312 additions and 140 deletions.
32 changes: 17 additions & 15 deletions js/public/RootingAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@

namespace js {

template <typename T>
// The defaulted Enable parameter for the following two types is for restricting
// specializations with std::enable_if.
template <typename T, typename Enable = void>
struct BarrierMethods {};

// The defaulted Enable parameter is for restricting specializations
// with std::enable_if.
template <typename Element, typename Wrapper, typename Enable = void>
class WrappedPtrOperations {};

Expand All @@ -141,18 +141,20 @@ class HeapOperations : public MutableWrappedPtrOperations<T, Wrapper> {};

// Cannot use FOR_EACH_HEAP_ABLE_GC_POINTER_TYPE, as this would import too many
// macros into scope
template <typename T>
struct IsHeapConstructibleType {
static constexpr bool value = false;
};
#define DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE(T) \
template <> \
struct IsHeapConstructibleType<T> { \
static constexpr bool value = true; \
};
JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
#undef DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE

// Add a 2nd template parameter to allow conditionally enabling partial
// specializations via std::enable_if.
template <typename T, typename Enable = void>
struct IsHeapConstructibleType : public std::false_type {};

#define JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE(T) \
template <> \
struct IsHeapConstructibleType<T> : public std::true_type {};
JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
JS_FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
// Note that JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE is left defined, to allow
// declaring other types (eg from js/public/experimental/TypedData.h) to
// be used with Heap<>.

namespace gc {
struct Cell;
Expand Down
63 changes: 57 additions & 6 deletions js/public/experimental/TypedData.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include "jstypes.h" // JS_PUBLIC_API

#include "js/Object.h" // JS::GetClass, JS::GetReservedSlot, JS::GetMaybePtrFromReservedSlot
#include "js/RootingAPI.h" // JS::Handle
#include "js/RootingAPI.h" // JS::Handle, JS_DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE
#include "js/ScalarType.h" // JS::Scalar::Type
#include "js/Wrapper.h" // js::CheckedUnwrapStatic

Expand Down Expand Up @@ -298,7 +298,7 @@ using ExternalTypeOf_t = typename ExternalTypeOf<ArrayType>::Type;
// A class holding a JSObject referring to a buffer of data. Either an
// ArrayBufferObject or some sort of ArrayBufferViewObject (see below).
// Note that this will always hold an unwrapped object.
class MOZ_STACK_CLASS JS_PUBLIC_API ArrayBufferOrView {
class JS_PUBLIC_API ArrayBufferOrView {
public:
// Typed Arrays will set this to their specific element type.
// Everything else just claims to expose things as uint8_t*.
Expand Down Expand Up @@ -327,14 +327,34 @@ class MOZ_STACK_CLASS JS_PUBLIC_API ArrayBufferOrView {

// Allow use as Rooted<JS::ArrayBufferOrView>.
void trace(JSTracer* trc) {
UnsafeTraceRoot(trc, &obj, "ArrayBufferOrView object");
if (obj) {
js::gc::TraceExternalEdge(trc, &obj, "ArrayBufferOrView object");
}
}

void reset() { obj = nullptr; }

bool isDetached() const;

JSObject* asObject() const { return obj; }
void exposeToActiveJS() const {
if (obj) {
js::BarrierMethods<JSObject*>::exposeToJS(obj);
}
}

JSObject* asObject() const {
exposeToActiveJS();
return obj;
}

JSObject* asObjectUnbarriered() const { return obj; }

JSObject** addressOfObject() { return &obj; }

bool operator==(const ArrayBufferOrView& other) const {
return obj == other.asObjectUnbarriered();
}
bool operator!=(const ArrayBufferOrView& other) const {
return obj != other.asObjectUnbarriered();
}
};

class JS_PUBLIC_API ArrayBuffer : public ArrayBufferOrView {
Expand Down Expand Up @@ -650,8 +670,39 @@ class WrappedPtrOperations<T, Wrapper, EnableIfABOVType<T>> {
}
};

// Allow usage within Heap<T>.
template <typename T>
struct IsHeapConstructibleType<T, EnableIfABOVType<T>> : public std::true_type {
};

template <typename T>
struct BarrierMethods<T, EnableIfABOVType<T>> {
static gc::Cell* asGCThingOrNull(T view) {
return reinterpret_cast<gc::Cell*>(view.asObjectUnbarriered());
}
static void postWriteBarrier(T* viewp, T prev, T next) {
BarrierMethods<JSObject*>::postWriteBarrier(viewp->addressOfObject(),
prev.asObjectUnbarriered(),
next.asObjectUnbarriered());
}
static void exposeToJS(T view) { view.exposeToActiveJS(); }
static void readBarrier(T view) {
JSObject* obj = view.asObjectUnbarriered();
if (obj) {
js::gc::IncrementalReadBarrier(JS::GCCellPtr(obj));
}
}
};

} // namespace js

namespace JS {
template <typename T>
struct SafelyInitialized<T, js::EnableIfABOVType<T>> {
static T create() { return T::fromObject(nullptr); }
};
} // namespace JS

/*
* JS_Is(type)Array(JSObject* maybeWrapped)
*
Expand Down
26 changes: 25 additions & 1 deletion js/src/gc/Barrier.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "gc/Cell.h"
#include "gc/StoreBuffer.h"
#include "js/ComparisonOperators.h" // JS::detail::DefineComparisonOps
#include "js/experimental/TypedData.h" // js::EnableIfABOVType
#include "js/HeapAPI.h"
#include "js/Id.h"
#include "js/RootingAPI.h"
Expand Down Expand Up @@ -328,7 +329,7 @@ struct MOZ_RAII AutoTouchingGrayThings {
#endif
};

template <typename T>
template <typename T, typename Enable = void>
struct InternalBarrierMethods {};

template <typename T>
Expand Down Expand Up @@ -412,6 +413,29 @@ struct InternalBarrierMethods<jsid> {
#endif
};

// Specialization for JS::ArrayBufferOrView subclasses.
template <typename T>
struct InternalBarrierMethods<T, EnableIfABOVType<T>> {
using BM = BarrierMethods<T>;

static bool isMarkable(const T& thing) { return bool(thing); }
static void preBarrier(const T& thing) {
gc::PreWriteBarrier(thing.asObjectUnbarriered());
}
static void postBarrier(T* tp, const T& prev, const T& next) {
BM::postWriteBarrier(tp, prev, next);
}
static void readBarrier(const T& thing) { BM::readBarrier(thing); }
#ifdef DEBUG
static void assertThingIsNotGray(const T& thing) {
JSObject* obj = thing.asObjectUnbarriered();
if (obj) {
JS::AssertValueIsNotGray(JS::ObjectValue(*obj));
}
}
#endif
};

template <typename T>
static inline void AssertTargetIsNotGray(const T& v) {
#ifdef DEBUG
Expand Down
11 changes: 11 additions & 0 deletions js/src/jsapi-tests/testArrayBufferView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,17 @@ bool TestViewType(JSContext* cx) {
CHECK(data1 == data2);
CHECK(shared1 == shared2);
CHECK(len == ExpectedLength);

JS::Heap<ViewType> hv(view);

bool shared3;
size_t len3;
uint8_t* data3 =
reinterpret_cast<uint8_t*>(hv.getLengthAndData(&len3, &shared3, nogc));
CHECK(obj == hv.asObject());
CHECK(data1 == data3);
CHECK(shared1 == shared3);
CHECK(len3 == ExpectedLength);
}

JS::RealmOptions options;
Expand Down
Loading

0 comments on commit baf7e9e

Please sign in to comment.