blob: e1ac24e9a4a77af8b6507bb34f373e77d42ac483 [file] [log] [blame]
// Copyright (c) 2012, 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_DART_API_STATE_H_
#define VM_DART_API_STATE_H_
#include "include/dart_api.h"
#include "platform/thread.h"
#include "vm/dart_api_impl.h"
#include "vm/flags.h"
#include "vm/growable_array.h"
#include "vm/handles.h"
#include "vm/object.h"
#include "vm/os.h"
#include "vm/raw_object.h"
#include "vm/visitor.h"
#include "vm/handles_impl.h"
namespace dart {
DECLARE_DEBUG_FLAG(bool, trace_zones);
DECLARE_DEBUG_FLAG(bool, trace_handles);
// Implementation of Zone support for very fast allocation of small chunks
// of memory. The chunks cannot be deallocated individually, but instead
// zones support deallocating all chunks in one fast operation when the
// scope is exited.
class ApiZone {
public:
// Create an empty zone.
ApiZone() : zone_() {
Isolate* isolate = Isolate::Current();
Zone* current_zone = isolate != NULL ? isolate->current_zone() : NULL;
zone_.Link(current_zone);
if (isolate != NULL) {
isolate->set_current_zone(&zone_);
}
#ifdef DEBUG
if (FLAG_trace_zones) {
OS::PrintErr("*** Starting a new Api zone 0x%"Px"(0x%"Px")\n",
reinterpret_cast<intptr_t>(this),
reinterpret_cast<intptr_t>(&zone_));
}
#endif
}
// Delete all memory associated with the zone.
~ApiZone() {
Isolate* isolate = Isolate::Current();
if (isolate != NULL && isolate->current_zone() == &zone_) {
isolate->set_current_zone(zone_.previous_);
}
#ifdef DEBUG
if (FLAG_trace_zones) {
OS::PrintErr("*** Deleting Api zone 0x%"Px"(0x%"Px")\n",
reinterpret_cast<intptr_t>(this),
reinterpret_cast<intptr_t>(&zone_));
}
#endif
}
// Allocates an array sized to hold 'len' elements of type
// 'ElementType'. Checks for integer overflow when performing the
// size computation.
template <class ElementType>
ElementType* Alloc(intptr_t len) { return zone_.Alloc<ElementType>(len); }
// Allocates an array sized to hold 'len' elements of type
// 'ElementType'. The new array is initialized from the memory of
// 'old_array' up to 'old_len'.
template <class ElementType>
ElementType* Realloc(ElementType* old_array,
intptr_t old_len,
intptr_t new_len) {
return zone_.Realloc<ElementType>(old_array, old_len, new_len);
}
// Allocates 'size' bytes of memory in the zone; expands the zone by
// allocating new segments of memory on demand using 'new'.
//
// It is preferred to use Alloc<T>() instead, as that function can
// check for integer overflow. If you use AllocUnsafe, you are
// responsible for avoiding integer overflow yourself.
uword AllocUnsafe(intptr_t size) { return zone_.AllocUnsafe(size); }
// Compute the total size of this zone. This includes wasted space that is
// due to internal fragmentation in the segments.
intptr_t SizeInBytes() const { return zone_.SizeInBytes(); }
Zone* GetZone() { return &zone_; }
private:
Zone zone_;
template<typename T> friend class ApiGrowableArray;
DISALLOW_COPY_AND_ASSIGN(ApiZone);
};
// Implementation of local handles which are handed out from every
// dart API call, these handles are valid only in the present scope
// and are destroyed when a Dart_ExitScope() is called.
class LocalHandle {
public:
// Accessors.
RawObject* raw() const { return raw_; }
void set_raw(RawObject* raw) { raw_ = raw; }
static intptr_t raw_offset() { return OFFSET_OF(LocalHandle, raw_); }
private:
LocalHandle() { }
~LocalHandle() { }
RawObject* raw_;
DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods.
DISALLOW_COPY_AND_ASSIGN(LocalHandle);
};
// A distinguished callback which indicates that a persistent handle
// should not be deleted from the dart api.
void ProtectedHandleCallback(void* peer);
// Implementation of persistent handles which are handed out through the
// dart API.
class PersistentHandle {
public:
// Accessors.
RawObject* raw() const { return raw_; }
void set_raw(RawObject* ref) { raw_ = ref; }
void set_raw(const LocalHandle& ref) { raw_ = ref.raw(); }
void set_raw(const Object& object) { raw_ = object.raw(); }
RawObject** raw_addr() { return &raw_; }
static intptr_t raw_offset() { return OFFSET_OF(PersistentHandle, raw_); }
private:
friend class PersistentHandles;
PersistentHandle() { }
~PersistentHandle() { }
// Overload the raw_ field as a next pointer when adding freed
// handles to the free list.
PersistentHandle* Next() {
return reinterpret_cast<PersistentHandle*>(raw_);
}
void SetNext(PersistentHandle* free_list) {
raw_ = reinterpret_cast<RawObject*>(free_list);
ASSERT(!raw_->IsHeapObject());
}
void FreeHandle(PersistentHandle* free_list) {
SetNext(free_list);
}
RawObject* raw_;
DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods.
DISALLOW_COPY_AND_ASSIGN(PersistentHandle);
};
// Implementation of persistent handles which are handed out through the
// dart API.
class FinalizablePersistentHandle {
public:
// Accessors.
RawObject* raw() const { return raw_; }
void set_raw(RawObject* raw) { raw_ = raw; }
void set_raw(const LocalHandle& ref) { raw_ = ref.raw(); }
void set_raw(const Object& object) { raw_ = object.raw(); }
RawObject** raw_addr() { return &raw_; }
static intptr_t raw_offset() {
return OFFSET_OF(FinalizablePersistentHandle, raw_);
}
void* peer() const { return peer_; }
void set_peer(void* peer) { peer_ = peer; }
Dart_WeakPersistentHandleFinalizer callback() const { return callback_; }
void set_callback(Dart_WeakPersistentHandleFinalizer callback) {
callback_ = callback;
}
static void Finalize(FinalizablePersistentHandle* handle) {
Dart_WeakPersistentHandleFinalizer callback = handle->callback();
if (callback != NULL) {
void* peer = handle->peer();
handle->Clear();
(*callback)(reinterpret_cast<Dart_Handle>(handle), peer);
} else {
handle->Clear();
}
}
private:
friend class FinalizablePersistentHandles;
FinalizablePersistentHandle() : raw_(NULL), peer_(NULL), callback_(NULL) { }
~FinalizablePersistentHandle() { }
// Overload the raw_ field as a next pointer when adding freed
// handles to the free list.
FinalizablePersistentHandle* Next() {
return reinterpret_cast<FinalizablePersistentHandle*>(raw_);
}
void SetNext(FinalizablePersistentHandle* free_list) {
raw_ = reinterpret_cast<RawObject*>(free_list);
ASSERT(!raw_->IsHeapObject());
}
void FreeHandle(FinalizablePersistentHandle* free_list) {
Clear();
SetNext(free_list);
}
void Clear() {
raw_ = Object::null();
peer_ = NULL;
callback_ = NULL;
}
RawObject* raw_;
void* peer_;
Dart_WeakPersistentHandleFinalizer callback_;
DISALLOW_ALLOCATION(); // Allocated through AllocateHandle methods.
DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandle);
};
// Local handles repository structure.
static const int kLocalHandleSizeInWords = sizeof(LocalHandle) / kWordSize;
static const int kLocalHandlesPerChunk = 64;
static const int kOffsetOfRawPtrInLocalHandle = 0;
class LocalHandles : Handles<kLocalHandleSizeInWords,
kLocalHandlesPerChunk,
kOffsetOfRawPtrInLocalHandle> {
public:
LocalHandles() : Handles<kLocalHandleSizeInWords,
kLocalHandlesPerChunk,
kOffsetOfRawPtrInLocalHandle>() {
#ifdef DEBUG
if (FLAG_trace_handles) {
OS::PrintErr("*** Starting a new Local handle block 0x%"Px"\n",
reinterpret_cast<intptr_t>(this));
}
#endif
}
~LocalHandles() {
#ifdef DEBUG
if (FLAG_trace_handles) {
OS::PrintErr("*** Handle Counts for 0x(%"Px"):Scoped = %d\n",
reinterpret_cast<intptr_t>(this),
CountHandles());
OS::PrintErr("*** Deleting Local handle block 0x%"Px"\n",
reinterpret_cast<intptr_t>(this));
}
#endif
}
// Visit all object pointers stored in the various handles.
void VisitObjectPointers(ObjectPointerVisitor* visitor) {
Handles<kLocalHandleSizeInWords,
kLocalHandlesPerChunk,
kOffsetOfRawPtrInLocalHandle>::VisitObjectPointers(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.
LocalHandle* AllocateHandle() {
return reinterpret_cast<LocalHandle*>(AllocateScopedHandle());
}
// Validate if passed in handle is a Local Handle.
bool IsValidHandle(Dart_Handle object) const {
return IsValidScopedHandle(reinterpret_cast<uword>(object));
}
// Returns a count of active handles (used for testing purposes).
int CountHandles() const {
return CountScopedHandles();
}
private:
DISALLOW_COPY_AND_ASSIGN(LocalHandles);
};
// Persistent handles repository structure.
static const int kPersistentHandleSizeInWords =
sizeof(PersistentHandle) / kWordSize;
static const int kPersistentHandlesPerChunk = 64;
static const int kOffsetOfRawPtrInPersistentHandle = 0;
class PersistentHandles : Handles<kPersistentHandleSizeInWords,
kPersistentHandlesPerChunk,
kOffsetOfRawPtrInPersistentHandle> {
public:
PersistentHandles() : Handles<kPersistentHandleSizeInWords,
kPersistentHandlesPerChunk,
kOffsetOfRawPtrInPersistentHandle>(),
free_list_(NULL) {
#ifdef DEBUG
if (FLAG_trace_handles) {
OS::PrintErr("*** Starting a new Persistent handle block 0x%"Px"\n",
reinterpret_cast<intptr_t>(this));
}
#endif
}
~PersistentHandles() {
free_list_ = NULL;
#ifdef DEBUG
if (FLAG_trace_handles) {
OS::PrintErr("*** Handle Counts for 0x(%"Px"):Scoped = %d\n",
reinterpret_cast<intptr_t>(this),
CountHandles());
OS::PrintErr("*** Deleting Persistent handle block 0x%"Px"\n",
reinterpret_cast<intptr_t>(this));
}
#endif
}
// Accessors.
PersistentHandle* free_list() const { return free_list_; }
void set_free_list(PersistentHandle* value) { free_list_ = value; }
// Visit all object pointers stored in the various handles.
void VisitObjectPointers(ObjectPointerVisitor* visitor) {
Handles<kPersistentHandleSizeInWords,
kPersistentHandlesPerChunk,
kOffsetOfRawPtrInPersistentHandle>::VisitObjectPointers(visitor);
}
// Allocates a persistent handle, these have to be destroyed explicitly
// by calling FreeHandle.
PersistentHandle* AllocateHandle() {
PersistentHandle* handle;
if (free_list_ != NULL) {
handle = free_list_;
free_list_ = handle->Next();
} else {
handle = reinterpret_cast<PersistentHandle*>(AllocateScopedHandle());
}
handle->set_raw(NULL);
return handle;
}
void FreeHandle(PersistentHandle* handle) {
handle->FreeHandle(free_list());
set_free_list(handle);
}
// Validate if passed in handle is a Persistent Handle.
bool IsValidHandle(Dart_Handle object) const {
return IsValidScopedHandle(reinterpret_cast<uword>(object));
}
// Returns a count of active handles (used for testing purposes).
int CountHandles() const {
return CountScopedHandles();
}
private:
PersistentHandle* free_list_;
DISALLOW_COPY_AND_ASSIGN(PersistentHandles);
};
// Finalizable persistent handles repository structure.
static const int kFinalizablePersistentHandleSizeInWords =
sizeof(FinalizablePersistentHandle) / kWordSize;
static const int kFinalizablePersistentHandlesPerChunk = 64;
static const int kOffsetOfRawPtrInFinalizablePersistentHandle = 0;
class FinalizablePersistentHandles
: Handles<kFinalizablePersistentHandleSizeInWords,
kFinalizablePersistentHandlesPerChunk,
kOffsetOfRawPtrInFinalizablePersistentHandle> {
public:
FinalizablePersistentHandles()
: Handles<kFinalizablePersistentHandleSizeInWords,
kFinalizablePersistentHandlesPerChunk,
kOffsetOfRawPtrInFinalizablePersistentHandle>(),
free_list_(NULL) { }
~FinalizablePersistentHandles() {
free_list_ = NULL;
}
// Accessors.
FinalizablePersistentHandle* free_list() const { return free_list_; }
void set_free_list(FinalizablePersistentHandle* value) { free_list_ = value; }
// Visit all handles stored in the various handle blocks.
void VisitHandles(HandleVisitor* visitor) {
Handles<kFinalizablePersistentHandleSizeInWords,
kFinalizablePersistentHandlesPerChunk,
kOffsetOfRawPtrInFinalizablePersistentHandle>::Visit(visitor);
}
// Visit all object pointers stored in the various handles.
void VisitObjectPointers(ObjectPointerVisitor* visitor) {
Handles<kFinalizablePersistentHandleSizeInWords,
kFinalizablePersistentHandlesPerChunk,
kOffsetOfRawPtrInFinalizablePersistentHandle>::VisitObjectPointers(
visitor);
}
// Allocates a persistent handle, these have to be destroyed explicitly
// by calling FreeHandle.
FinalizablePersistentHandle* AllocateHandle() {
FinalizablePersistentHandle* handle;
if (free_list_ != NULL) {
handle = free_list_;
free_list_ = handle->Next();
} else {
handle = reinterpret_cast<FinalizablePersistentHandle*>(
AllocateScopedHandle());
}
handle->set_callback(NULL);
return handle;
}
void FreeHandle(FinalizablePersistentHandle* handle) {
handle->FreeHandle(free_list());
set_free_list(handle);
}
// Validate if passed in handle is a Persistent Handle.
bool IsValidHandle(Dart_Handle object) const {
return IsValidScopedHandle(reinterpret_cast<uword>(object));
}
// Returns a count of active handles (used for testing purposes).
int CountHandles() const {
return CountScopedHandles();
}
private:
FinalizablePersistentHandle* free_list_;
DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandles);
};
class WeakReferenceSet {
public:
WeakReferenceSet(Dart_Handle* keys, intptr_t keys_length,
Dart_Handle* values, intptr_t values_length)
: next_(NULL),
keys_(keys), num_keys_(keys_length),
values_(values), num_values_(values_length) {
}
~WeakReferenceSet() {}
WeakReferenceSet* next() const { return next_; }
intptr_t num_keys() const { return num_keys_; }
RawObject** get_key(intptr_t i) {
ASSERT(i >= 0);
ASSERT(i < num_keys_);
return (reinterpret_cast<PersistentHandle*>(keys_[i]))->raw_addr();
}
intptr_t num_values() const { return num_values_; }
RawObject** get_value(intptr_t i) {
ASSERT(i >= 0);
ASSERT(i < num_values_);
return (reinterpret_cast<PersistentHandle*>(values_[i]))->raw_addr();
}
static WeakReferenceSet* Pop(WeakReferenceSet** queue) {
ASSERT(queue != NULL);
WeakReferenceSet* head = *queue;
if (head != NULL) {
*queue = head->next();
head->next_ = NULL;
}
return head;
}
static void Push(WeakReferenceSet* reference_set, WeakReferenceSet** queue) {
ASSERT(reference_set != NULL);
ASSERT(queue != NULL);
reference_set->next_ = *queue;
*queue = reference_set;
}
private:
WeakReferenceSet* next_;
Dart_Handle* keys_;
intptr_t num_keys_;
Dart_Handle* values_;
intptr_t num_values_;
DISALLOW_COPY_AND_ASSIGN(WeakReferenceSet);
};
// Structure used for the implementation of local scopes used in dart_api.
// These local scopes manage handles and memory allocated in the scope.
class ApiLocalScope {
public:
ApiLocalScope(ApiLocalScope* previous, uword stack_marker) :
previous_(previous), stack_marker_(stack_marker) { }
~ApiLocalScope() {
previous_ = NULL;
}
// Accessors.
ApiLocalScope* previous() const { return previous_; }
uword stack_marker() const { return stack_marker_; }
void set_previous(ApiLocalScope* value) { previous_ = value; }
LocalHandles* local_handles() { return &local_handles_; }
Zone* zone() { return zone_.GetZone(); }
private:
ApiLocalScope* previous_;
uword stack_marker_;
LocalHandles local_handles_;
ApiZone zone_;
DISALLOW_COPY_AND_ASSIGN(ApiLocalScope);
};
// Implementation of the API State used in dart api for maintaining
// local scopes, persistent handles etc. These are setup on a per isolate
// basis and destroyed when the isolate is shutdown.
class ApiState {
public:
ApiState() : top_scope_(NULL), delayed_weak_reference_sets_(NULL),
null_(NULL), true_(NULL), false_(NULL),
acquired_error_(NULL) {}
~ApiState() {
while (top_scope_ != NULL) {
ApiLocalScope* scope = top_scope_;
top_scope_ = top_scope_->previous();
delete scope;
}
if (null_ != NULL) {
persistent_handles().FreeHandle(null_);
null_ = NULL;
}
if (true_ != NULL) {
persistent_handles().FreeHandle(true_);
true_ = NULL;
}
if (false_ != NULL) {
persistent_handles().FreeHandle(false_);
false_ = NULL;
}
if (acquired_error_ != NULL) {
persistent_handles().FreeHandle(acquired_error_);
acquired_error_ = NULL;
}
}
// Accessors.
ApiLocalScope* top_scope() const { return top_scope_; }
void set_top_scope(ApiLocalScope* value) { top_scope_ = value; }
PersistentHandles& persistent_handles() { return persistent_handles_; }
FinalizablePersistentHandles& weak_persistent_handles() {
return weak_persistent_handles_;
}
FinalizablePersistentHandles& prologue_weak_persistent_handles() {
return prologue_weak_persistent_handles_;
}
WeakReferenceSet* delayed_weak_reference_sets() {
return delayed_weak_reference_sets_;
}
void set_delayed_weak_reference_sets(WeakReferenceSet* reference_set) {
delayed_weak_reference_sets_ = reference_set;
}
void UnwindScopes(uword stack_marker) {
// Unwind all scopes using the same stack_marker, i.e. all scopes allocated
// under the same top_exit_frame_info.
while (top_scope_ != NULL &&
top_scope_->stack_marker() != 0 &&
top_scope_->stack_marker() == stack_marker) {
ApiLocalScope* scope = top_scope_;
top_scope_ = top_scope_->previous();
delete scope;
}
}
void VisitObjectPointers(ObjectPointerVisitor* visitor,
bool visit_prologue_weak_handles) {
ApiLocalScope* scope = top_scope_;
while (scope != NULL) {
scope->local_handles()->VisitObjectPointers(visitor);
scope = scope->previous();
}
persistent_handles().VisitObjectPointers(visitor);
if (visit_prologue_weak_handles) {
prologue_weak_persistent_handles().VisitObjectPointers(visitor);
}
}
void VisitWeakHandles(HandleVisitor* visitor,
bool visit_prologue_weak_handles) {
weak_persistent_handles().VisitHandles(visitor);
if (visit_prologue_weak_handles) {
prologue_weak_persistent_handles().VisitHandles(visitor);
}
}
bool IsValidLocalHandle(Dart_Handle object) const {
ApiLocalScope* scope = top_scope_;
while (scope != NULL) {
if (scope->local_handles()->IsValidHandle(object)) {
return true;
}
scope = scope->previous();
}
return false;
}
bool IsValidPersistentHandle(Dart_Handle object) const {
return persistent_handles_.IsValidHandle(object);
}
bool IsValidWeakPersistentHandle(Dart_Handle object) const {
return weak_persistent_handles_.IsValidHandle(object);
}
bool IsValidPrologueWeakPersistentHandle(Dart_Handle object) const {
return prologue_weak_persistent_handles_.IsValidHandle(object);
}
bool IsProtectedHandle(PersistentHandle* object) const {
if (object == NULL) return false;
return object == null_ || object == true_ || object == false_;
}
int CountLocalHandles() const {
int total = 0;
ApiLocalScope* scope = top_scope_;
while (scope != NULL) {
total += scope->local_handles()->CountHandles();
scope = scope->previous();
}
return total;
}
int CountPersistentHandles() const {
return persistent_handles_.CountHandles();
}
int ZoneSizeInBytes() const {
int total = 0;
ApiLocalScope* scope = top_scope_;
while (scope != NULL) {
total += scope->zone()->SizeInBytes();
scope = scope->previous();
}
return total;
}
PersistentHandle* Null() {
if (null_ == NULL) {
null_ = persistent_handles().AllocateHandle();
null_->set_raw(Object::null());
}
return null_;
}
PersistentHandle* True() {
if (true_ == NULL) {
true_ = persistent_handles().AllocateHandle();
true_->set_raw(Bool::True());
}
return true_;
}
PersistentHandle* False() {
if (false_ == NULL) {
false_ = persistent_handles().AllocateHandle();
false_->set_raw(Bool::False());
}
return false_;
}
void SetupAcquiredError() {
ASSERT(acquired_error_ == NULL);
acquired_error_ = persistent_handles().AllocateHandle();
acquired_error_->set_raw(
String::New("Internal Dart data pointers have been acquired, "
"please release them using Dart_TypedDataReleaseData."));
}
PersistentHandle* AcquiredError() const {
ASSERT(acquired_error_ != NULL);
return acquired_error_;
}
void DelayWeakReferenceSet(WeakReferenceSet* reference_set) {
WeakReferenceSet::Push(reference_set, &delayed_weak_reference_sets_);
}
private:
PersistentHandles persistent_handles_;
FinalizablePersistentHandles weak_persistent_handles_;
FinalizablePersistentHandles prologue_weak_persistent_handles_;
ApiLocalScope* top_scope_;
WeakReferenceSet* delayed_weak_reference_sets_;
// Persistent handles to important objects.
PersistentHandle* null_;
PersistentHandle* true_;
PersistentHandle* false_;
PersistentHandle* acquired_error_;
DISALLOW_COPY_AND_ASSIGN(ApiState);
};
class ApiNativeScope {
public:
ApiNativeScope() {
// Currently no support for nesting native scopes.
ASSERT(Current() == NULL);
Thread::SetThreadLocal(Api::api_native_key_, reinterpret_cast<uword>(this));
}
~ApiNativeScope() {
ASSERT(Current() == this);
Thread::SetThreadLocal(Api::api_native_key_, 0);
}
static inline ApiNativeScope* Current() {
return reinterpret_cast<ApiNativeScope*>(
Thread::GetThreadLocal(Api::api_native_key_));
}
Zone* zone() { return zone_.GetZone(); }
private:
ApiZone zone_;
};
// Api growable arrays use a zone for allocation. The constructor
// picks the zone from the current isolate if in an isolate
// environment. When outside an isolate environment it picks the zone
// from the current native scope.
template<typename T>
class ApiGrowableArray : public BaseGrowableArray<T, ValueObject> {
public:
explicit ApiGrowableArray(int initial_capacity)
: BaseGrowableArray<T, ValueObject>(
initial_capacity,
ApiNativeScope::Current()->zone()) {}
ApiGrowableArray()
: BaseGrowableArray<T, ValueObject>(
ApiNativeScope::Current()->zone()) {}
};
} // namespace dart
#endif // VM_DART_API_STATE_H_