mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Previously RawSyntaxCacheNode was not increasing the RawSyntax refCount and accessing a cached node would fail if the node had been deleted in the meantime. We weren't hitting this so far, because all nodes were allocated using a bump allocator and thus the underlying memory never freed.
163 lines
5.1 KiB
C++
163 lines
5.1 KiB
C++
//===--- SyntaxArena.cpp - SyntaxArena implementation -----------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Syntax/SyntaxArena.h"
|
|
#include "swift/Syntax/RawSyntax.h"
|
|
#include "llvm/ADT/FoldingSet.h"
|
|
|
|
using namespace swift;
|
|
using namespace swift::syntax;
|
|
|
|
namespace {
|
|
class RawSyntaxCacheNode;
|
|
}
|
|
|
|
/// Implementation detail of SyntaxArena.
|
|
struct SyntaxArena::Implementation {
|
|
/// Allocator.
|
|
llvm::BumpPtrAllocator Allocator;
|
|
|
|
/// List of pointers to the allocated RawSyntax
|
|
std::vector<RawSyntax *> AllocatedRawSyntaxList;
|
|
|
|
/// Cached Tokens.
|
|
llvm::FoldingSet<RawSyntaxCacheNode> CachedTokens;
|
|
|
|
Implementation() = default;
|
|
void *Allocate(size_t size, size_t alignment) {
|
|
return Allocator.Allocate(size, alignment);
|
|
}
|
|
|
|
void *AllocateRawSyntax(size_t size, size_t alignment) {
|
|
void *data = Allocator.Allocate(size, alignment);
|
|
/// Remember the allocated pointers so that we can destruct them.
|
|
AllocatedRawSyntaxList.push_back(static_cast<RawSyntax *>(data));
|
|
return data;
|
|
}
|
|
|
|
~Implementation() {
|
|
// Destruct all allocated RawSyntax. They might contain heap allocated
|
|
// propeties and/or children.
|
|
for (auto *N : AllocatedRawSyntaxList)
|
|
N->~RawSyntax();
|
|
}
|
|
};
|
|
|
|
SyntaxArena::SyntaxArena() : Impl(*new Implementation()){};
|
|
SyntaxArena::~SyntaxArena() { delete &Impl; }
|
|
|
|
llvm::BumpPtrAllocator &SyntaxArena::getAllocator() const {
|
|
return Impl.Allocator;
|
|
}
|
|
|
|
void *SyntaxArena::Allocate(size_t size, size_t alignment) {
|
|
return Impl.Allocate(size, alignment);
|
|
}
|
|
|
|
void *SyntaxArena::AllocateRawSyntax(size_t size, size_t alignment) {
|
|
return Impl.AllocateRawSyntax(size, alignment);
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// Cache node for RawSyntax.
|
|
class RawSyntaxCacheNode : public llvm::FoldingSetNode {
|
|
|
|
friend llvm::FoldingSetTrait<RawSyntaxCacheNode>;
|
|
|
|
/// Associated RawSyntax.
|
|
RC<RawSyntax> Obj;
|
|
/// FoldingSet node identifier of the associated RawSyntax.
|
|
llvm::FoldingSetNodeIDRef IDRef;
|
|
|
|
public:
|
|
RawSyntaxCacheNode(RC<RawSyntax> Obj, const llvm::FoldingSetNodeIDRef IDRef)
|
|
: Obj(Obj), IDRef(IDRef) {}
|
|
|
|
/// Retrieve assciated RawSyntax.
|
|
RC<RawSyntax> get() { return Obj; }
|
|
|
|
// Only allow allocation of Node using the allocator in SyntaxArena.
|
|
void *operator new(size_t Bytes, SyntaxArena &Arena,
|
|
unsigned Alignment = alignof(RawSyntaxCacheNode)) {
|
|
return Arena.Allocate(Bytes, Alignment);
|
|
}
|
|
|
|
void *operator new(size_t Bytes) throw() = delete;
|
|
void operator delete(void *Data) throw() = delete;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
namespace llvm {
|
|
|
|
/// FoldingSet traits for RawSyntax wrapper.
|
|
template <> struct FoldingSetTrait<RawSyntaxCacheNode> {
|
|
|
|
static inline void Profile(RawSyntaxCacheNode &X, FoldingSetNodeID &ID) {
|
|
ID.AddNodeID(X.IDRef);
|
|
}
|
|
|
|
static inline bool Equals(RawSyntaxCacheNode &X, const FoldingSetNodeID &ID,
|
|
unsigned, FoldingSetNodeID &) {
|
|
return ID == X.IDRef;
|
|
}
|
|
static inline unsigned ComputeHash(RawSyntaxCacheNode &X,
|
|
FoldingSetNodeID &) {
|
|
return X.IDRef.ComputeHash();
|
|
}
|
|
};
|
|
|
|
} // namespace llvm
|
|
|
|
/// Retrive "token" RawSyntax from the given Arena.
|
|
RC<RawSyntax> RawSyntax::getToken(SyntaxArena &Arena, tok TokKind,
|
|
OwnedString Text,
|
|
llvm::ArrayRef<TriviaPiece> LeadingTrivia,
|
|
llvm::ArrayRef<TriviaPiece> TrailingTrivia) {
|
|
|
|
// Determine whether this token is worth to cache.
|
|
if (
|
|
// Is string_literal with >16 length.
|
|
(TokKind == tok::string_literal && Text.size() > 16) ||
|
|
// Has leading comment trivia et al.
|
|
any_of(LeadingTrivia,
|
|
[](const syntax::TriviaPiece &T) { return T.getText().size(); }) ||
|
|
// Has trailing comment trivia et al.
|
|
any_of(TrailingTrivia,
|
|
[](const syntax::TriviaPiece &T) { return T.getText().size(); })) {
|
|
|
|
// Do not use cache.
|
|
return RawSyntax::make(TokKind, Text, LeadingTrivia, TrailingTrivia,
|
|
SourcePresence::Present, &Arena);
|
|
}
|
|
|
|
// This node is cacheable. Get or create.
|
|
auto &CachedTokens = Arena.Impl.CachedTokens;
|
|
|
|
llvm::FoldingSetNodeID ID;
|
|
RawSyntax::Profile(ID, TokKind, Text, LeadingTrivia, TrailingTrivia);
|
|
|
|
void *insertPos = nullptr;
|
|
if (auto existing = CachedTokens.FindNodeOrInsertPos(ID, insertPos))
|
|
// Found in the cache. Just return it.
|
|
return existing->get();
|
|
|
|
// Could not found in the cache. Create it.
|
|
auto Raw = RawSyntax::make(TokKind, Text, LeadingTrivia, TrailingTrivia,
|
|
SourcePresence::Present, &Arena);
|
|
auto IDRef = ID.Intern(Arena.getAllocator());
|
|
auto CacheNode = new (Arena) RawSyntaxCacheNode(Raw, IDRef);
|
|
CachedTokens.InsertNode(CacheNode, insertPos);
|
|
return Raw;
|
|
}
|