From 8a8d8ecb356ba98088e1ab01a6c53d0dcdc16f4a Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 27 Nov 2022 12:33:02 +0100 Subject: [PATCH] LibJS: Add ASTNodeWithTailArray template to pack AST node + array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This template allows us to allocate an AST node and an array of some arbitrary type T with one allocation instead of two. This can save a lot of memory in some cases. Thanks to Jonathan Müller for suggesting this technique! :^) --- Userland/Libraries/LibJS/AST.h | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 79962eae53f..fa1b8dca007 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -49,6 +49,10 @@ create_ast_node(SourceRange range, Args&&... args) class ASTNode : public RefCounted { public: virtual ~ASTNode() = default; + + // NOTE: This is here to stop ASAN complaining about mismatch between new/delete sizes in ASTNodeWithTailArray. + void operator delete(void* ptr) { ::operator delete(ptr); } + virtual Completion execute(Interpreter&) const = 0; virtual Bytecode::CodeGenerationErrorOr generate_bytecode(Bytecode::Generator&) const; virtual void dump(int indent) const; @@ -97,6 +101,46 @@ private: u32 m_end_offset { 0 }; }; +// This is a helper class that packs an array of T after the AST node, all in the same allocation. +template +class ASTNodeWithTailArray : public Base { +public: + virtual ~ASTNodeWithTailArray() override + { + for (auto& value : tail_span()) + value.~T(); + } + + Span tail_span() const { return { tail_data(), tail_size() }; } + + T const* tail_data() const { return reinterpret_cast(reinterpret_cast(this) + sizeof(Derived)); } + size_t tail_size() const { return m_tail_size; } + +protected: + template + static NonnullRefPtr create(size_t tail_size, SourceRange source_range, Args&&... args) + { + static_assert(sizeof(ActualDerived) == sizeof(Derived), "This leaf class cannot add more members"); + static_assert(alignof(ActualDerived) % alignof(T) == 0, "Need padding for tail array"); + auto memory = ::operator new(sizeof(ActualDerived) + tail_size * sizeof(T)); + return adopt_ref(*::new (memory) ActualDerived(move(source_range), forward(args)...)); + } + + ASTNodeWithTailArray(SourceRange source_range, Span values) + : Base(move(source_range)) + , m_tail_size(values.size()) + { + VERIFY(values.size() <= NumericLimits::max()); + for (size_t i = 0; i < values.size(); ++i) + new (&tail_data()[i]) T(values[i]); + } + +private: + T* tail_data() { return reinterpret_cast(reinterpret_cast(this) + sizeof(Derived)); } + + u32 m_tail_size { 0 }; +}; + class Statement : public ASTNode { public: explicit Statement(SourceRange source_range)