blob: 92acd35a5d42d3e03bb9f695d98d2e4c2aadc5af [file] [log] [blame]
// 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 VM_HANDLES_H_
#define VM_HANDLES_H_
#include "vm/allocation.h"
#include "vm/flags.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(isolate).
// Handles allocated in this manner are destroyed when the HandleScope
// object is destroyed.
// Code that uses scoped handles typically looks as follows:
// {
// HANDLESCOPE(isolate);
// 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.
//
// There are some critical regions of the Dart VM were we may need to manipulate
// raw dart objects directly. We use NOHANDLESCOPE to assert that we do not
// add code that will allocate new handles during this critical area.
// {
// NOHANDLESCOPE(isolate);
// ....
// ....
// }
// Forward declarations.
class ObjectPointerVisitor;
DECLARE_FLAG(bool, verify_handles);
class HandleVisitor {
public:
virtual void VisitHandle(uword addr) = 0;
virtual ~HandleVisitor() {
}
};
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 of the various handles.
void Visit(HandleVisitor* visitor);
// 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(Isolate* isolate);
// Allocates a handle in the current zone. This handle will be destroyed
// when the current zone is destroyed.
static uword AllocateZoneHandle(Isolate* isolate);
// Returns true if specified handle is a zone handle.
static bool IsZoneHandle(uword handle);
protected:
// Allocates space for a scoped handle.
uword AllocateScopedHandle() {
if (scoped_blocks_->IsFull()) {
SetupNextScopeBlock();
}
return scoped_blocks_->AllocateHandle();
}
// 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:
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:
// Zap value used to indicate uninitialized handle area (debug purposes).
static const uword kZapUninitializedWord = 0xabababab;
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;
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>() { }
~VMHandles();
// Visit all object pointers stored in the various handles.
void VisitObjectPointers(ObjectPointerVisitor* visitor);
// 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(Isolate* isolate);
// Allocates a handle in the current zone. This handle will be destroyed
// when the current zone is destroyed.
static uword AllocateZoneHandle(Isolate* isolate);
// 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();
};
// The class HandleScope is used to start a new handles scope in the code.
// It is used as follows:
// {
// HANDLESCOPE(isolate);
// ....
// .....
// code that creates some scoped handles.
// ....
// }
class HandleScope : public StackResource {
public:
explicit HandleScope(BaseIsolate* isolate);
~HandleScope();
private:
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(isolate) \
dart::HandleScope vm_internal_handles_scope_(isolate);
// The class NoHandleScope is used in critical regions of the virtual machine
// code where raw dart object pointers are directly manipulated.
// This class asserts that we do not add code that will allocate new handles
// during this critical area.
// It is used as follows:
// {
// NOHANDLESCOPE(isolate);
// ....
// .....
// critical code that manipulates dart objects directly.
// ....
// }
#if defined(DEBUG)
class NoHandleScope : public StackResource {
public:
explicit NoHandleScope(BaseIsolate* isolate);
NoHandleScope();
~NoHandleScope();
private:
DISALLOW_COPY_AND_ASSIGN(NoHandleScope);
};
#else // defined(DEBUG)
class NoHandleScope : public ValueObject {
public:
explicit NoHandleScope(BaseIsolate* isolate) { }
NoHandleScope() { }
~NoHandleScope() { }
private:
DISALLOW_COPY_AND_ASSIGN(NoHandleScope);
};
#endif // defined(DEBUG)
// Macro to start a no handles scope in the code.
#define NOHANDLESCOPE(isolate) \
dart::NoHandleScope no_vm_internal_handles_scope_(isolate);
} // namespace dart
#endif // VM_HANDLES_H_