| // Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | #ifndef RUNTIME_VM_HANDLES_H_ | 
 | #define RUNTIME_VM_HANDLES_H_ | 
 |  | 
 | #include "vm/allocation.h" | 
 | #include "vm/flags.h" | 
 | #include "vm/os.h" | 
 |  | 
 | namespace dart { | 
 |  | 
 | // Handles are used in the Dart Virtual Machine to ensure that access | 
 | // to dart objects in the virtual machine code is done in a | 
 | // Garbage Collection safe manner. | 
 | // | 
 | // The class Handles is the basic type that implements creation of handles and | 
 | // manages their life cycle (allocated either in the current zone or | 
 | // current handle scope). | 
 | // The two forms of handle allocation are: | 
 | // - allocation of handles in the current zone (Handle::AllocateZoneHandle). | 
 | //   Handles allocated in this manner are destroyed when the zone is destroyed. | 
 | // - allocation of handles in a scoped manner (Handle::AllocateHandle). | 
 | //   A new scope can be started using HANDLESCOPE(thread). | 
 | //   Handles allocated in this manner are destroyed when the HandleScope | 
 | //   object is destroyed. | 
 | // Code that uses scoped handles typically looks as follows: | 
 | //   { | 
 | //     HANDLESCOPE(thread); | 
 | //     const String& str = String::Handle(String::New("abc")); | 
 | //     ..... | 
 | //     ..... | 
 | //   } | 
 | // Code that uses zone handles typically looks as follows: | 
 | //   const String& str = String::ZoneHandle(String::New("abc")); | 
 | //   ..... | 
 | //   ..... | 
 | // | 
 | //   The Handle function for each object type internally uses the | 
 | //   Handles::AllocateHandle() function for creating handles. The Handle | 
 | //   function of the object type is the only way to create scoped handles | 
 | //   in the dart VM. | 
 | //   The ZoneHandle function for each object type internally uses the | 
 | //   Handles::AllocateZoneHandle() function for creating zone handles. | 
 | //   The ZoneHandle function of the object type is the only way to create | 
 | //   zone handles in the dart VM. | 
 |  | 
 | // Forward declarations. | 
 | class ObjectPointerVisitor; | 
 | class HandleVisitor; | 
 |  | 
 | DECLARE_FLAG(bool, verify_handles); | 
 |  | 
 | template <int kHandleSizeInWords, int kHandlesPerChunk, int kOffsetOfRawPtr> | 
 | class Handles { | 
 |  public: | 
 |   Handles() | 
 |       : zone_blocks_(NULL), | 
 |         first_scoped_block_(NULL), | 
 |         scoped_blocks_(&first_scoped_block_) {} | 
 |   ~Handles() { DeleteAll(); } | 
 |  | 
 |   // Visit all object pointers stored in the various handles. | 
 |   void VisitObjectPointers(ObjectPointerVisitor* visitor); | 
 |  | 
 |   // Visit all the scoped handles. | 
 |   void VisitScopedHandles(ObjectPointerVisitor* visitor); | 
 |  | 
 |   // Visit all blocks that have been added since the last time | 
 |   // this method was called. | 
 |   // Be careful with this, since multiple users of this method could | 
 |   // interfere with eachother. | 
 |   // Currently only used by GC trace facility. | 
 |   void VisitUnvisitedScopedHandles(ObjectPointerVisitor* visitor); | 
 |  | 
 |   // Visit all of the various handles. | 
 |   void Visit(HandleVisitor* visitor); | 
 |  | 
 |   // Reset the handles so that we can reuse. | 
 |   void Reset(); | 
 |  | 
 |   // Allocates a handle in the current handle scope. This handle is valid only | 
 |   // in the current handle scope and is destroyed when the current handle | 
 |   // scope ends. | 
 |   static uword AllocateHandle(Zone* zone); | 
 |  | 
 |   // Allocates a handle in the current zone. This handle will be destroyed | 
 |   // when the current zone is destroyed. | 
 |   static uword AllocateZoneHandle(Zone* zone); | 
 |  | 
 |   // Returns true if specified handle is a zone handle. | 
 |   static bool IsZoneHandle(uword handle); | 
 |  | 
 |   // Allocates space for a scoped handle. | 
 |   uword AllocateScopedHandle() { | 
 |     if (scoped_blocks_->IsFull()) { | 
 |       SetupNextScopeBlock(); | 
 |     } | 
 |     return scoped_blocks_->AllocateHandle(); | 
 |   } | 
 |  | 
 |   bool IsEmpty() const { | 
 |     if (zone_blocks_ != nullptr) return false; | 
 |     if (first_scoped_block_.HandleCount() != 0) return false; | 
 |     if (scoped_blocks_ != &first_scoped_block_) return false; | 
 |     return true; | 
 |   } | 
 |  | 
 |  protected: | 
 |   // Returns a count of active handles (used for testing purposes). | 
 |   int CountScopedHandles() const; | 
 |   int CountZoneHandles() const; | 
 |  | 
 |   // Returns true if passed in handle is a valid zone handle. | 
 |   bool IsValidScopedHandle(uword handle) const; | 
 |   bool IsValidZoneHandle(uword handle) const; | 
 |  | 
 |  private: | 
 |   // Base structure for managing blocks of handles. | 
 |   // Handles are allocated in Chunks (each chunk holds kHandlesPerChunk | 
 |   // handles). The chunk is uninitialized, subsequent requests for handles | 
 |   // is allocated from the chunk until we run out space in the chunk, | 
 |   // at this point another chunk is allocated. These chunks are chained | 
 |   // together. | 
 |   class HandlesBlock : public MallocAllocated { | 
 |    public: | 
 |     explicit HandlesBlock(HandlesBlock* next) | 
 |         : next_handle_slot_(0), next_block_(next) {} | 
 |     ~HandlesBlock(); | 
 |  | 
 |     // Reinitializes handle block for reuse. | 
 |     void ReInit(); | 
 |  | 
 |     // Returns true if the handle block is full. | 
 |     bool IsFull() const { | 
 |       return next_handle_slot_ >= (kHandleSizeInWords * kHandlesPerChunk); | 
 |     } | 
 |  | 
 |     // Returns true if passed in handle belongs to this block. | 
 |     bool IsValidHandle(uword handle) const { | 
 |       uword start = reinterpret_cast<uword>(data_); | 
 |       uword end = start + (kHandleSizeInWords * kWordSize * kHandlesPerChunk); | 
 |       return (start <= handle && handle < end); | 
 |     } | 
 |  | 
 |     // Allocates space for a handle in the data area. | 
 |     uword AllocateHandle() { | 
 |       ASSERT(!IsFull()); | 
 |       uword handle_address = reinterpret_cast<uword>(data_ + next_handle_slot_); | 
 |       next_handle_slot_ += kHandleSizeInWords; | 
 |       return handle_address; | 
 |     } | 
 |  | 
 |     // Visit all object pointers in the handle block. | 
 |     void VisitObjectPointers(ObjectPointerVisitor* visitor); | 
 |  | 
 |     // Visit all of the handles in the handle block. | 
 |     void Visit(HandleVisitor* visitor); | 
 |  | 
 | #if defined(DEBUG) | 
 |     // Zaps the free handle area to an uninitialized value. | 
 |     void ZapFreeHandles(); | 
 | #endif | 
 |  | 
 |     // Returns number of active handles in the handle block. | 
 |     int HandleCount() const; | 
 |  | 
 |     // Accessors. | 
 |     intptr_t next_handle_slot() const { return next_handle_slot_; } | 
 |     void set_next_handle_slot(intptr_t next_handle_slot) { | 
 |       next_handle_slot_ = next_handle_slot; | 
 |     } | 
 |     HandlesBlock* next_block() const { return next_block_; } | 
 |     void set_next_block(HandlesBlock* next) { next_block_ = next; } | 
 |  | 
 |    private: | 
 |     uword data_[kHandleSizeInWords * kHandlesPerChunk];  // Handles area. | 
 |     intptr_t next_handle_slot_;  // Next slot for allocation in current block. | 
 |     HandlesBlock* next_block_;   // Link to next block of handles. | 
 |  | 
 |     DISALLOW_COPY_AND_ASSIGN(HandlesBlock); | 
 |   }; | 
 |  | 
 |   // Deletes all the allocated handle blocks. | 
 |   void DeleteAll(); | 
 |   void DeleteHandleBlocks(HandlesBlock* blocks); | 
 |  | 
 |   // Sets up the next handle block (allocates a new one if needed). | 
 |   void SetupNextScopeBlock(); | 
 |  | 
 |   // Allocates space for a zone handle. | 
 |   uword AllocateHandleInZone() { | 
 |     if (zone_blocks_ == NULL || zone_blocks_->IsFull()) { | 
 |       SetupNextZoneBlock(); | 
 |     } | 
 |     return zone_blocks_->AllocateHandle(); | 
 |   } | 
 |  | 
 |   // Allocates a new handle block and links it up. | 
 |   void SetupNextZoneBlock(); | 
 |  | 
 | #if defined(DEBUG) | 
 |   // Verifies consistency of handle blocks after a scope is destroyed. | 
 |   void VerifyScopedHandleState(); | 
 |  | 
 |   // Zaps the free scoped handles to an uninitialized value. | 
 |   void ZapFreeScopedHandles(); | 
 | #endif | 
 |  | 
 |   HandlesBlock* zone_blocks_;        // List of zone handles. | 
 |   HandlesBlock first_scoped_block_;  // First block of scoped handles. | 
 |   HandlesBlock* scoped_blocks_;      // List of scoped handles. | 
 |  | 
 |   friend class HandleScope; | 
 |   friend class Dart; | 
 |   friend class IsolateObjectStore; | 
 |   friend class ObjectStore; | 
 |   friend class ThreadState; | 
 |   DISALLOW_ALLOCATION(); | 
 |   DISALLOW_COPY_AND_ASSIGN(Handles); | 
 | }; | 
 |  | 
 | static const int kVMHandleSizeInWords = 2; | 
 | static const int kVMHandlesPerChunk = 64; | 
 | static const int kOffsetOfRawPtr = kWordSize; | 
 | class VMHandles : public Handles<kVMHandleSizeInWords, | 
 |                                  kVMHandlesPerChunk, | 
 |                                  kOffsetOfRawPtr> { | 
 |  public: | 
 |   static const int kOffsetOfRawPtrInHandle = kOffsetOfRawPtr; | 
 |  | 
 |   VMHandles() | 
 |       : Handles<kVMHandleSizeInWords, kVMHandlesPerChunk, kOffsetOfRawPtr>() { | 
 |     if (FLAG_trace_handles) { | 
 |       OS::PrintErr("*** Starting a new VM handle block 0x%" Px "\n", | 
 |                    reinterpret_cast<intptr_t>(this)); | 
 |     } | 
 |   } | 
 |   ~VMHandles(); | 
 |  | 
 |   // Visit all object pointers stored in the various handles. | 
 |   void VisitObjectPointers(ObjectPointerVisitor* visitor); | 
 |  | 
 |   // Allocates a handle in the current handle scope of 'zone', which must be | 
 |   // the current zone. This handle is valid only in the current handle scope | 
 |   // and is destroyed when the current handle scope ends. | 
 |   static uword AllocateHandle(Zone* zone); | 
 |  | 
 |   // Allocates a handle in 'zone', which must be the current zone. This handle | 
 |   // will be destroyed when the current zone is destroyed. | 
 |   static uword AllocateZoneHandle(Zone* zone); | 
 |  | 
 |   // Returns true if specified handle is a zone handle. | 
 |   static bool IsZoneHandle(uword handle); | 
 |  | 
 |   // Returns number of handles, these functions are used for testing purposes. | 
 |   static int ScopedHandleCount(); | 
 |   static int ZoneHandleCount(); | 
 |  | 
 |   friend class ApiZone; | 
 |   friend class ApiNativeScope; | 
 | }; | 
 |  | 
 | // The class HandleScope is used to start a new handles scope in the code. | 
 | // It is used as follows: | 
 | // { | 
 | //   HANDLESCOPE(thread); | 
 | //   .... | 
 | //   ..... | 
 | //   code that creates some scoped handles. | 
 | //   .... | 
 | // } | 
 | class HandleScope : public StackResource { | 
 |  public: | 
 |   explicit HandleScope(ThreadState* thread); | 
 |   ~HandleScope(); | 
 |  | 
 |  private: | 
 |   void Initialize(); | 
 |  | 
 |   VMHandles::HandlesBlock* saved_handle_block_;  // Handle block at prev scope. | 
 |   uword saved_handle_slot_;  // Next available handle slot at previous scope. | 
 | #if defined(DEBUG) | 
 |   HandleScope* link_;  // Link to previous scope. | 
 | #endif | 
 |   DISALLOW_IMPLICIT_CONSTRUCTORS(HandleScope); | 
 | }; | 
 |  | 
 | // Macro to start a new Handle scope. | 
 | #define HANDLESCOPE(thread)                                                    \ | 
 |   dart::HandleScope vm_internal_handles_scope_(thread); | 
 |  | 
 | }  // namespace dart | 
 |  | 
 | #endif  // RUNTIME_VM_HANDLES_H_ |