Mypal/js/src/frontend/NameCollections.h
2021-02-04 16:48:36 +02:00

338 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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