summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/NameCollections.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/NameCollections.h')
-rw-r--r--js/src/frontend/NameCollections.h338
1 files changed, 338 insertions, 0 deletions
diff --git a/js/src/frontend/NameCollections.h b/js/src/frontend/NameCollections.h
new file mode 100644
index 000000000..58c5d0ac0
--- /dev/null
+++ b/js/src/frontend/NameCollections.h
@@ -0,0 +1,338 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef frontend_NameCollections_h
+#define frontend_NameCollections_h
+
+#include "ds/InlineTable.h"
+#include "frontend/NameAnalysisTypes.h"
+#include "js/Vector.h"
+#include "vm/Stack.h"
+
+namespace js {
+namespace frontend {
+
+// A pool of recyclable containers for use in the frontend. The Parser and
+// BytecodeEmitter create many maps for name analysis that are short-lived
+// (i.e., for the duration of parsing or emitting a lexical scope). Making
+// them recyclable cuts down significantly on allocator churn.
+template <typename RepresentativeCollection, typename ConcreteCollectionPool>
+class CollectionPool
+{
+ using RecyclableCollections = Vector<void*, 32, SystemAllocPolicy>;
+
+ RecyclableCollections all_;
+ RecyclableCollections recyclable_;
+
+ static RepresentativeCollection* asRepresentative(void* p) {
+ return reinterpret_cast<RepresentativeCollection*>(p);
+ }
+
+ RepresentativeCollection* allocate() {
+ size_t newAllLength = all_.length() + 1;
+ if (!all_.reserve(newAllLength) || !recyclable_.reserve(newAllLength))
+ return nullptr;
+
+ RepresentativeCollection* collection = js_new<RepresentativeCollection>();
+ if (collection)
+ all_.infallibleAppend(collection);
+ return collection;
+ }
+
+ public:
+ ~CollectionPool() {
+ purgeAll();
+ }
+
+ bool empty() const {
+ return all_.empty();
+ }
+
+ void purgeAll() {
+ void** end = all_.end();
+ for (void** it = all_.begin(); it != end; ++it)
+ js_delete(asRepresentative(*it));
+
+ all_.clearAndFree();
+ recyclable_.clearAndFree();
+ }
+
+ // Fallibly aquire one of the supported collection types from the pool.
+ template <typename Collection>
+ Collection* acquire(ExclusiveContext* cx) {
+ ConcreteCollectionPool::template assertInvariants<Collection>();
+
+ RepresentativeCollection* collection;
+ if (recyclable_.empty()) {
+ collection = allocate();
+ if (!collection)
+ ReportOutOfMemory(cx);
+ } else {
+ collection = asRepresentative(recyclable_.popCopy());
+ collection->clear();
+ }
+ return reinterpret_cast<Collection*>(collection);
+ }
+
+ // Release a collection back to the pool.
+ template <typename Collection>
+ void release(Collection** collection) {
+ ConcreteCollectionPool::template assertInvariants<Collection>();
+ MOZ_ASSERT(*collection);
+
+#ifdef DEBUG
+ bool ok = false;
+ // Make sure the collection is in |all_| but not already in |recyclable_|.
+ for (void** it = all_.begin(); it != all_.end(); ++it) {
+ if (*it == *collection) {
+ ok = true;
+ break;
+ }
+ }
+ MOZ_ASSERT(ok);
+ for (void** it = recyclable_.begin(); it != recyclable_.end(); ++it)
+ MOZ_ASSERT(*it != *collection);
+#endif
+
+ MOZ_ASSERT(recyclable_.length() < all_.length());
+ // Reserved in allocateFresh.
+ recyclable_.infallibleAppend(*collection);
+ *collection = nullptr;
+ }
+};
+
+template <typename Wrapped>
+struct RecyclableAtomMapValueWrapper
+{
+ union {
+ Wrapped wrapped;
+ uint64_t dummy;
+ };
+
+ static void assertInvariant() {
+ static_assert(sizeof(Wrapped) <= sizeof(uint64_t),
+ "Can only recycle atom maps with values smaller than uint64");
+ }
+
+ RecyclableAtomMapValueWrapper() {
+ assertInvariant();
+ }
+
+ MOZ_IMPLICIT RecyclableAtomMapValueWrapper(Wrapped w)
+ : wrapped(w)
+ {
+ assertInvariant();
+ }
+
+ MOZ_IMPLICIT operator Wrapped&() {
+ return wrapped;
+ }
+
+ MOZ_IMPLICIT operator Wrapped&() const {
+ return wrapped;
+ }
+
+ Wrapped* operator->() {
+ return &wrapped;
+ }
+
+ const Wrapped* operator->() const {
+ return &wrapped;
+ }
+};
+
+template <typename MapValue>
+using RecyclableNameMap = InlineMap<JSAtom*,
+ RecyclableAtomMapValueWrapper<MapValue>,
+ 24,
+ DefaultHasher<JSAtom*>,
+ SystemAllocPolicy>;
+
+using DeclaredNameMap = RecyclableNameMap<DeclaredNameInfo>;
+using CheckTDZMap = RecyclableNameMap<MaybeCheckTDZ>;
+using NameLocationMap = RecyclableNameMap<NameLocation>;
+using AtomIndexMap = RecyclableNameMap<uint32_t>;
+
+#undef RECYCLABLE_NAME_MAP_TYPE
+
+template <typename RepresentativeTable>
+class InlineTablePool
+ : public CollectionPool<RepresentativeTable, InlineTablePool<RepresentativeTable>>
+{
+ public:
+ template <typename Table>
+ static void assertInvariants() {
+ static_assert(Table::SizeOfInlineEntries == RepresentativeTable::SizeOfInlineEntries,
+ "Only tables with the same size for inline entries are usable in the pool.");
+ static_assert(mozilla::IsPod<typename Table::Table::Entry>::value,
+ "Only tables with POD values are usable in the pool.");
+ }
+};
+
+using FunctionBoxVector = Vector<FunctionBox*, 24, SystemAllocPolicy>;
+
+template <typename RepresentativeVector>
+class VectorPool : public CollectionPool<RepresentativeVector, VectorPool<RepresentativeVector>>
+{
+ public:
+ template <typename Vector>
+ static void assertInvariants() {
+ static_assert(Vector::sMaxInlineStorage == RepresentativeVector::sMaxInlineStorage,
+ "Only vectors with the same size for inline entries are usable in the pool.");
+ static_assert(mozilla::IsPod<typename Vector::ElementType>::value,
+ "Only vectors of POD values are usable in the pool.");
+ static_assert(sizeof(typename Vector::ElementType) ==
+ sizeof(typename RepresentativeVector::ElementType),
+ "Only vectors with same-sized elements are usable in the pool.");
+ }
+};
+
+class NameCollectionPool
+{
+ InlineTablePool<AtomIndexMap> mapPool_;
+ VectorPool<AtomVector> vectorPool_;
+ uint32_t activeCompilations_;
+
+ public:
+ NameCollectionPool()
+ : activeCompilations_(0)
+ { }
+
+ bool hasActiveCompilation() const {
+ return activeCompilations_ != 0;
+ }
+
+ void addActiveCompilation() {
+ activeCompilations_++;
+ }
+
+ void removeActiveCompilation() {
+ MOZ_ASSERT(hasActiveCompilation());
+ activeCompilations_--;
+ }
+
+ template <typename Map>
+ Map* acquireMap(ExclusiveContext* cx) {
+ MOZ_ASSERT(hasActiveCompilation());
+ return mapPool_.acquire<Map>(cx);
+ }
+
+ template <typename Map>
+ void releaseMap(Map** map) {
+ MOZ_ASSERT(hasActiveCompilation());
+ MOZ_ASSERT(map);
+ if (*map)
+ mapPool_.release(map);
+ }
+
+ template <typename Vector>
+ Vector* acquireVector(ExclusiveContext* cx) {
+ MOZ_ASSERT(hasActiveCompilation());
+ return vectorPool_.acquire<Vector>(cx);
+ }
+
+ template <typename Vector>
+ void releaseVector(Vector** vec) {
+ MOZ_ASSERT(hasActiveCompilation());
+ MOZ_ASSERT(vec);
+ if (*vec)
+ vectorPool_.release(vec);
+ }
+
+ void purge() {
+ if (!hasActiveCompilation()) {
+ mapPool_.purgeAll();
+ vectorPool_.purgeAll();
+ }
+ }
+};
+
+#define POOLED_COLLECTION_PTR_METHODS(N, T) \
+ NameCollectionPool& pool_; \
+ T* collection_; \
+ \
+ T& collection() { \
+ MOZ_ASSERT(collection_); \
+ return *collection_; \
+ } \
+ \
+ const T& collection() const { \
+ MOZ_ASSERT(collection_); \
+ return *collection_; \
+ } \
+ \
+ public: \
+ explicit N(NameCollectionPool& pool) \
+ : pool_(pool), \
+ collection_(nullptr) \
+ { } \
+ \
+ ~N() { \
+ pool_.release##T(&collection_); \
+ } \
+ \
+ bool acquire(ExclusiveContext* cx) { \
+ MOZ_ASSERT(!collection_); \
+ collection_ = pool_.acquire##T<T>(cx); \
+ return !!collection_; \
+ } \
+ \
+ explicit operator bool() const { \
+ return !!collection_; \
+ } \
+ \
+ T* operator->() { \
+ return &collection(); \
+ } \
+ \
+ const T* operator->() const { \
+ return &collection(); \
+ } \
+ \
+ T& operator*() { \
+ return collection(); \
+ } \
+ \
+ const T& operator*() const { \
+ return collection(); \
+ }
+
+template <typename Map>
+class PooledMapPtr
+{
+ POOLED_COLLECTION_PTR_METHODS(PooledMapPtr, Map)
+};
+
+template <typename Vector>
+class PooledVectorPtr
+{
+ POOLED_COLLECTION_PTR_METHODS(PooledVectorPtr, Vector)
+
+ typename Vector::ElementType& operator[](size_t index) {
+ return collection()[index];
+ }
+
+ const typename Vector::ElementType& operator[](size_t index) const {
+ return collection()[index];
+ }
+};
+
+#undef POOLED_COLLECTION_PTR_METHODS
+
+} // namespace frontend
+} // namespace js
+
+namespace mozilla {
+
+template <>
+struct IsPod<js::MaybeCheckTDZ> : TrueType {};
+
+template <typename T>
+struct IsPod<js::frontend::RecyclableAtomMapValueWrapper<T>> : IsPod<T> {};
+
+} // namespace mozilla
+
+#endif // frontend_NameCollections_h