| // Copyright 2009 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_REGEXP_REGEXP_STACK_H_ |
| #define V8_REGEXP_REGEXP_STACK_H_ |
| |
| #include "src/base/logging.h" |
| #include "src/base/macros.h" |
| #include "src/common/globals.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| class RegExpStack; |
| |
| // Maintains a per-v8thread stack area that can be used by irregexp |
| // implementation for its backtracking stack. |
| // Since there is only one stack area, the Irregexp implementation is not |
| // re-entrant. I.e., no regular expressions may be executed in the same thread |
| // during a preempted Irregexp execution. |
| class RegExpStackScope { |
| public: |
| // Create and delete an instance to control the life-time of a growing stack. |
| |
| // Initializes the stack memory area if necessary. |
| explicit RegExpStackScope(Isolate* isolate); |
| ~RegExpStackScope(); // Releases the stack if it has grown. |
| RegExpStackScope(const RegExpStackScope&) = delete; |
| RegExpStackScope& operator=(const RegExpStackScope&) = delete; |
| |
| RegExpStack* stack() const { return regexp_stack_; } |
| |
| private: |
| RegExpStack* regexp_stack_; |
| }; |
| |
| |
| class RegExpStack { |
| public: |
| RegExpStack(); |
| ~RegExpStack(); |
| RegExpStack(const RegExpStack&) = delete; |
| RegExpStack& operator=(const RegExpStack&) = delete; |
| |
| // Number of allocated locations on the stack below the limit. |
| // No sequence of pushes must be longer that this without doing a stack-limit |
| // check. |
| static constexpr int kStackLimitSlack = 32; |
| |
| // Gives the top of the memory used as stack. |
| Address stack_base() { |
| DCHECK_NE(0, thread_local_.memory_size_); |
| DCHECK_EQ(thread_local_.memory_top_, |
| thread_local_.memory_ + thread_local_.memory_size_); |
| return reinterpret_cast<Address>(thread_local_.memory_top_); |
| } |
| |
| // The total size of the memory allocated for the stack. |
| size_t stack_capacity() { return thread_local_.memory_size_; } |
| |
| // If the stack pointer gets below the limit, we should react and |
| // either grow the stack or report an out-of-stack exception. |
| // There is only a limited number of locations below the stack limit, |
| // so users of the stack should check the stack limit during any |
| // sequence of pushes longer that this. |
| Address* limit_address_address() { return &(thread_local_.limit_); } |
| |
| // Ensures that there is a memory area with at least the specified size. |
| // If passing zero, the default/minimum size buffer is allocated. |
| Address EnsureCapacity(size_t size); |
| |
| bool is_in_use() const { return thread_local_.is_in_use_; } |
| void set_is_in_use(bool v) { thread_local_.is_in_use_ = v; } |
| |
| // Thread local archiving. |
| static constexpr int ArchiveSpacePerThread() { |
| return static_cast<int>(kThreadLocalSize); |
| } |
| char* ArchiveStack(char* to); |
| char* RestoreStack(char* from); |
| void FreeThreadResources() { thread_local_.ResetToStaticStack(this); } |
| |
| // Maximal size of allocated stack area. |
| static constexpr size_t kMaximumStackSize = 64 * MB; |
| |
| private: |
| // Artificial limit used when the thread-local state has been destroyed. |
| static const Address kMemoryTop = |
| static_cast<Address>(static_cast<uintptr_t>(-1)); |
| |
| // Minimal size of dynamically-allocated stack area. |
| static constexpr size_t kMinimumDynamicStackSize = 1 * KB; |
| |
| // In addition to dynamically-allocated, variable-sized stacks, we also have |
| // a statically allocated and sized area that is used whenever no dynamic |
| // stack is allocated. This guarantees that a stack is always available and |
| // we can skip availability-checks later on. |
| // It's double the slack size to ensure that we have a bit of breathing room |
| // before NativeRegExpMacroAssembler::GrowStack must be called. |
| static constexpr size_t kStaticStackSize = |
| 2 * kStackLimitSlack * kSystemPointerSize; |
| byte static_stack_[kStaticStackSize] = {0}; |
| |
| STATIC_ASSERT(kStaticStackSize <= kMaximumStackSize); |
| |
| // Structure holding the allocated memory, size and limit. |
| struct ThreadLocal { |
| explicit ThreadLocal(RegExpStack* regexp_stack) { |
| ResetToStaticStack(regexp_stack); |
| } |
| |
| // If memory_size_ > 0 then memory_ and memory_top_ must be non-nullptr |
| // and memory_top_ = memory_ + memory_size_ |
| byte* memory_ = nullptr; |
| byte* memory_top_ = nullptr; |
| size_t memory_size_ = 0; |
| Address limit_ = kNullAddress; |
| bool owns_memory_ = false; // Whether memory_ is owned and must be freed. |
| bool is_in_use_ = false; // To guard against reentrancy. |
| |
| void ResetToStaticStack(RegExpStack* regexp_stack); |
| void FreeAndInvalidate(); |
| }; |
| static constexpr size_t kThreadLocalSize = sizeof(ThreadLocal); |
| |
| // Address of top of memory used as stack. |
| Address memory_top_address_address() { |
| return reinterpret_cast<Address>(&thread_local_.memory_top_); |
| } |
| |
| // Resets the buffer if it has grown beyond the default/minimum size. |
| // After this, the buffer is either the default size, or it is empty, so |
| // you have to call EnsureCapacity before using it again. |
| void Reset(); |
| |
| ThreadLocal thread_local_; |
| Isolate* isolate_; |
| |
| friend class ExternalReference; |
| friend class Isolate; |
| friend class RegExpStackScope; |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_REGEXP_REGEXP_STACK_H_ |