// 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 RUNTIME_VM_DART_API_STATE_H_ #define RUNTIME_VM_DART_API_STATE_H_ #include "include/dart_api.h" #include "platform/utils.h" #include "vm/bitfield.h" #include "vm/dart_api_impl.h" #include "vm/flags.h" #include "vm/growable_array.h" #include "vm/handles.h" #include "vm/heap/weak_table.h" #include "vm/object.h" #include "vm/os.h" #include "vm/os_thread.h" #include "vm/raw_object.h" #include "vm/thread_pool.h" #include "vm/visitor.h" #include "vm/handles_impl.h" namespace dart { // 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_() { Thread* thread = Thread::Current(); Zone* zone = thread != NULL ? thread->zone() : NULL; zone_.Link(zone); if (thread != NULL) { thread->set_zone(&zone_); } if (FLAG_trace_zones) { OS::PrintErr("*** Starting a new Api zone 0x%" Px "(0x%" Px ")\n", reinterpret_cast(this), reinterpret_cast(&zone_)); } } // Delete all memory associated with the zone. ~ApiZone() { Thread* thread = Thread::Current(); #if defined(DEBUG) if (thread == NULL) { ASSERT(zone_.handles()->CountScopedHandles() == 0); ASSERT(zone_.handles()->CountZoneHandles() == 0); } #endif if ((thread != NULL) && (thread->zone() == &zone_)) { thread->set_zone(zone_.previous_); } if (FLAG_trace_zones) { OS::PrintErr("*** Deleting Api zone 0x%" Px "(0x%" Px ")\n", reinterpret_cast(this), reinterpret_cast(&zone_)); } } // Allocates an array sized to hold 'len' elements of type // 'ElementType'. Checks for integer overflow when performing the // size computation. template ElementType* Alloc(intptr_t len) { return zone_.Alloc(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 ElementType* Realloc(ElementType* old_array, intptr_t old_len, intptr_t new_len) { return zone_.Realloc(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() 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_; } void Reinit(Thread* thread) { if (thread == NULL) { zone_.Link(NULL); } else { zone_.Link(thread->zone()); thread->set_zone(&zone_); } } void Reset(Thread* thread) { if ((thread != NULL) && (thread->zone() == &zone_)) { thread->set_zone(zone_.previous_); } zone_.DeleteAll(); } private: Zone zone_; template 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. ObjectPtr ptr() const { return ptr_; } void set_ptr(ObjectPtr ptr) { ptr_ = ptr; } static intptr_t ptr_offset() { return OFFSET_OF(LocalHandle, ptr_); } Dart_Handle apiHandle() { return reinterpret_cast(this); } private: LocalHandle() {} ~LocalHandle() {} ObjectPtr ptr_; 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. ObjectPtr ptr() const { return ptr_; } void set_ptr(ObjectPtr ref) { ptr_ = ref; } void set_ptr(const LocalHandle& ref) { ptr_ = ref.ptr(); } void set_ptr(const Object& object) { ptr_ = object.ptr(); } ObjectPtr* raw_addr() { return &ptr_; } Dart_PersistentHandle apiHandle() { return reinterpret_cast(this); } static intptr_t ptr_offset() { return OFFSET_OF(PersistentHandle, ptr_); } static PersistentHandle* Cast(Dart_PersistentHandle handle); private: friend class PersistentHandles; PersistentHandle() {} ~PersistentHandle() {} // Overload the ptr_ field as a next pointer when adding freed // handles to the free list. PersistentHandle* Next() { return reinterpret_cast(static_cast(ptr_)); } void SetNext(PersistentHandle* free_list) { ptr_ = static_cast(reinterpret_cast(free_list)); ASSERT(!ptr_->IsHeapObject()); } void FreeHandle(PersistentHandle* free_list) { SetNext(free_list); } ObjectPtr ptr_; 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: static FinalizablePersistentHandle* New(IsolateGroup* isolate_group, const Object& object, void* peer, Dart_HandleFinalizer callback, intptr_t external_size, bool auto_delete); // Accessors. ObjectPtr ptr() const { return ptr_; } ObjectPtr* ptr_addr() { return &ptr_; } static intptr_t ptr_offset() { return OFFSET_OF(FinalizablePersistentHandle, ptr_); } void* peer() const { return peer_; } Dart_HandleFinalizer callback() const { return callback_; } Dart_WeakPersistentHandle ApiWeakPersistentHandle() { return reinterpret_cast(this); } Dart_FinalizableHandle ApiFinalizableHandle() { return reinterpret_cast(this); } bool auto_delete() const { return auto_delete_; } bool IsFinalizedNotFreed() const { return ptr_ == static_cast(reinterpret_cast(this)); } intptr_t external_size() const { return ExternalSizeInWordsBits::decode(external_data_) * kWordSize; } void SetExternalSize(intptr_t size, IsolateGroup* isolate_group) { ASSERT(size >= 0); set_external_size(size); if (SpaceForExternal() == Heap::kNew) { SetExternalNewSpaceBit(); } isolate_group->heap()->AllocatedExternal(external_size(), SpaceForExternal()); } void UpdateExternalSize(intptr_t size, IsolateGroup* isolate_group) { ASSERT(size >= 0); intptr_t old_size = external_size(); set_external_size(size); if (size > old_size) { isolate_group->heap()->AllocatedExternal(size - old_size, SpaceForExternal()); } else { isolate_group->heap()->FreedExternal(old_size - size, SpaceForExternal()); } } // Called when the referent becomes unreachable. void UpdateUnreachable(IsolateGroup* isolate_group) { EnsureFreedExternal(isolate_group); Finalize(isolate_group, this); } // Called when the referent has moved, potentially between generations. void UpdateRelocated(IsolateGroup* isolate_group) { if (IsSetNewSpaceBit() && (SpaceForExternal() == Heap::kOld)) { isolate_group->heap()->PromotedExternal(external_size()); ClearExternalNewSpaceBit(); } } // Idempotent. Called when the handle is explicitly deleted or the // referent becomes unreachable. void EnsureFreedExternal(IsolateGroup* isolate_group) { isolate_group->heap()->FreedExternal(external_size(), SpaceForExternal()); set_external_size(0); } static FinalizablePersistentHandle* Cast(Dart_WeakPersistentHandle handle); static FinalizablePersistentHandle* Cast(Dart_FinalizableHandle handle); private: enum { kExternalNewSpaceBit = 0, kExternalSizeBits = 1, kExternalSizeBitsSize = (kBitsPerWord - 1), }; // This part of external_data_ is the number of externally allocated bytes. class ExternalSizeInWordsBits : public BitField {}; // This bit of external_data_ is true if the referent was created in new // space and UpdateRelocated has not yet detected any promotion. class ExternalNewSpaceBit : public BitField {}; friend class FinalizablePersistentHandles; FinalizablePersistentHandle() : ptr_(nullptr), peer_(NULL), external_data_(0), callback_(NULL) {} ~FinalizablePersistentHandle() {} static void Finalize(IsolateGroup* isolate_group, FinalizablePersistentHandle* handle); // Overload the ptr_ field as a next pointer when adding freed // handles to the free list. FinalizablePersistentHandle* Next() { return reinterpret_cast( static_cast(ptr_)); } void SetNext(FinalizablePersistentHandle* free_list) { ptr_ = static_cast(reinterpret_cast(free_list)); ASSERT(!ptr_->IsHeapObject()); } void SetFinalizedNotFreed() { // `handle->raw_ != Object::null()` or the GC will finalize again. SetNext(this); } void FreeHandle(FinalizablePersistentHandle* free_list) { Clear(); SetNext(free_list); } void Clear() { ptr_ = Object::null(); peer_ = nullptr; external_data_ = 0; callback_ = nullptr; auto_delete_ = false; } void set_ptr(ObjectPtr raw) { ptr_ = raw; } void set_ptr(const LocalHandle& ref) { ptr_ = ref.ptr(); } void set_ptr(const Object& object) { ptr_ = object.ptr(); } void set_peer(void* peer) { peer_ = peer; } void set_callback(Dart_HandleFinalizer callback) { callback_ = callback; } void set_auto_delete(bool auto_delete) { auto_delete_ = auto_delete; } void set_external_size(intptr_t size) { intptr_t size_in_words = Utils::RoundUp(size, kObjectAlignment) / kWordSize; ASSERT(ExternalSizeInWordsBits::is_valid(size_in_words)); external_data_ = ExternalSizeInWordsBits::update(size_in_words, external_data_); } bool IsSetNewSpaceBit() const { return ExternalNewSpaceBit::decode(external_data_); } void SetExternalNewSpaceBit() { external_data_ = ExternalNewSpaceBit::update(true, external_data_); } void ClearExternalNewSpaceBit() { external_data_ = ExternalNewSpaceBit::update(false, external_data_); } // Returns the space to charge for the external size. Heap::Space SpaceForExternal() const { // Non-heap and VM-heap objects count as old space here. return ptr_->IsSmiOrOldObject() ? Heap::kOld : Heap::kNew; } ObjectPtr ptr_; void* peer_; uword external_data_; Dart_HandleFinalizer callback_; bool auto_delete_; 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 { public: LocalHandles() : Handles() { if (FLAG_trace_handles) { OS::PrintErr("*** Starting a new Local handle block 0x%" Px "\n", reinterpret_cast(this)); } } ~LocalHandles() { if (FLAG_trace_handles) { OS::PrintErr("*** Handle Counts for 0x(%" Px "):Scoped = %d\n", reinterpret_cast(this), CountHandles()); OS::PrintErr("*** Deleting Local handle block 0x%" Px "\n", reinterpret_cast(this)); } } // Visit all object pointers stored in the various handles. void VisitObjectPointers(ObjectPointerVisitor* visitor) { visitor->set_gc_root_type("local handle"); Handles::VisitObjectPointers(visitor); visitor->clear_gc_root_type(); } // Reset the local handles block for reuse. void Reset() { Handles::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. LocalHandle* AllocateHandle() { return reinterpret_cast(AllocateScopedHandle()); } // Validate if passed in handle is a Local Handle. bool IsValidHandle(Dart_Handle object) const { return IsValidScopedHandle(reinterpret_cast(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 { public: PersistentHandles() : Handles(), free_list_(NULL) { if (FLAG_trace_handles) { OS::PrintErr("*** Starting a new Persistent handle block 0x%" Px "\n", reinterpret_cast(this)); } } ~PersistentHandles() { free_list_ = NULL; if (FLAG_trace_handles) { OS::PrintErr("*** Handle Counts for 0x(%" Px "):Scoped = %d\n", reinterpret_cast(this), CountHandles()); OS::PrintErr("*** Deleting Persistent handle block 0x%" Px "\n", reinterpret_cast(this)); } } // 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) { visitor->set_gc_root_type("persistent handle"); Handles::VisitObjectPointers(visitor); visitor->clear_gc_root_type(); } // Visit all the handles. void Visit(HandleVisitor* visitor) { Handles::Visit(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(AllocateScopedHandle()); } handle->set_ptr(Object::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_PersistentHandle object) const { return IsValidScopedHandle(reinterpret_cast(object)); } bool IsFreeHandle(Dart_PersistentHandle object) const { PersistentHandle* handle = free_list_; while (handle != NULL) { if (handle == reinterpret_cast(object)) { return true; } handle = handle->Next(); } return false; } // 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 { public: FinalizablePersistentHandles() : Handles(), 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::Visit(visitor); } // Visit all object pointers stored in the various handles. void VisitObjectPointers(ObjectPointerVisitor* visitor) { visitor->set_gc_root_type("weak persistent handle"); Handles:: VisitObjectPointers(visitor); visitor->clear_gc_root_type(); } // 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(); handle->set_ptr(Object::null()); return handle; } handle = reinterpret_cast(AllocateScopedHandle()); handle->Clear(); return handle; } void ClearHandle(FinalizablePersistentHandle* handle) { handle->Clear(); handle->SetFinalizedNotFreed(); } void FreeHandle(FinalizablePersistentHandle* handle) { handle->FreeHandle(free_list()); set_free_list(handle); } // Validate if passed in handle is a Persistent Handle. bool IsValidHandle(Dart_WeakPersistentHandle object) const { return IsValidScopedHandle(reinterpret_cast(object)); } bool IsValidHandle(Dart_FinalizableHandle object) const { return IsValidScopedHandle(reinterpret_cast(object)); } bool IsFreeHandle(Dart_WeakPersistentHandle object) const { FinalizablePersistentHandle* handle = free_list_; while (handle != NULL) { if (handle == reinterpret_cast(object)) { return true; } handle = handle->Next(); } return false; } // Returns a count of active handles (used for testing purposes). int CountHandles() const { return CountScopedHandles(); } private: FinalizablePersistentHandle* free_list_; DISALLOW_COPY_AND_ASSIGN(FinalizablePersistentHandles); }; // 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; } // Reinit the ApiLocalScope to new values. void Reinit(Thread* thread, ApiLocalScope* previous, uword stack_marker) { previous_ = previous; stack_marker_ = stack_marker; zone_.Reinit(thread); } // Reset the ApiLocalScope so that it can be reused again. void Reset(Thread* thread) { local_handles_.Reset(); zone_.Reset(thread); previous_ = NULL; stack_marker_ = 0; } // 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); }; class ApiNativeScope { public: ApiNativeScope() { // Currently no support for nesting native scopes. ASSERT(Current() == NULL); OSThread::SetThreadLocal(Api::api_native_key_, reinterpret_cast(this)); // We manually increment the memory usage counter since there is memory // initially allocated within the zone on creation. IncrementNativeScopeMemoryCapacity(zone_.GetZone()->CapacityInBytes()); } ~ApiNativeScope() { ASSERT(Current() == this); OSThread::SetThreadLocal(Api::api_native_key_, 0); // We must also manually decrement the memory usage counter since the native // is still holding it's initial memory and ~Zone() won't be able to // determine which memory usage counter to decrement. DecrementNativeScopeMemoryCapacity(zone_.GetZone()->CapacityInBytes()); } static inline ApiNativeScope* Current() { return reinterpret_cast( OSThread::GetThreadLocal(Api::api_native_key_)); } static uintptr_t current_memory_usage() { return current_memory_usage_; } static void IncrementNativeScopeMemoryCapacity(intptr_t size) { current_memory_usage_.fetch_add(size); } static void DecrementNativeScopeMemoryCapacity(intptr_t size) { current_memory_usage_.fetch_sub(size); } Zone* zone() { Zone* result = zone_.GetZone(); ASSERT(result->handles()->CountScopedHandles() == 0); ASSERT(result->handles()->CountZoneHandles() == 0); return result; } private: // The current total memory usage within ApiNativeScopes. static RelaxedAtomic current_memory_usage_; 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 class ApiGrowableArray : public BaseGrowableArray { public: explicit ApiGrowableArray(int initial_capacity) : BaseGrowableArray( initial_capacity, ApiNativeScope::Current()->zone()) {} ApiGrowableArray() : BaseGrowableArray( ApiNativeScope::Current()->zone()) {} ApiGrowableArray(intptr_t initial_capacity, Zone* zone) : BaseGrowableArray(initial_capacity, zone) {} }; // Implementation of the API State used in dart api for maintaining // local scopes, persistent handles etc. These are setup on a per isolate // group basis and destroyed when the isolate group is shutdown. class ApiState { public: ApiState() : persistent_handles_(), weak_persistent_handles_(), null_(NULL), true_(NULL), false_(NULL), acquired_error_(NULL) {} ~ApiState() { 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; } } void MergeOtherApiState(ApiState* api_state); void VisitObjectPointersUnlocked(ObjectPointerVisitor* visitor) { persistent_handles_.VisitObjectPointers(visitor); if (visitor->visit_weak_persistent_handles()) { weak_persistent_handles_.VisitObjectPointers(visitor); } } void VisitWeakHandlesUnlocked(HandleVisitor* visitor) { weak_persistent_handles_.VisitHandles(visitor); } PersistentHandle* AllocatePersistentHandle() { MutexLocker ml(&mutex_); return persistent_handles_.AllocateHandle(); } void FreePersistentHandle(PersistentHandle* ref) { MutexLocker ml(&mutex_); persistent_handles_.FreeHandle(ref); } FinalizablePersistentHandle* AllocateWeakPersistentHandle() { MutexLocker ml(&mutex_); return weak_persistent_handles_.AllocateHandle(); } void ClearWeakPersistentHandle(FinalizablePersistentHandle* weak_ref) { MutexLocker ml(&mutex_); weak_persistent_handles_.ClearHandle(weak_ref); } void FreeWeakPersistentHandle(FinalizablePersistentHandle* weak_ref) { MutexLocker ml(&mutex_); weak_persistent_handles_.FreeHandle(weak_ref); } bool IsValidPersistentHandle(Dart_PersistentHandle object) { MutexLocker ml(&mutex_); return persistent_handles_.IsValidHandle(object); } bool IsActivePersistentHandle(Dart_PersistentHandle object) { MutexLocker ml(&mutex_); return persistent_handles_.IsValidHandle(object) && !persistent_handles_.IsFreeHandle(object); } bool IsValidWeakPersistentHandle(Dart_WeakPersistentHandle object) { MutexLocker ml(&mutex_); return weak_persistent_handles_.IsValidHandle(object); } bool IsValidFinalizableHandle(Dart_FinalizableHandle object) { MutexLocker ml(&mutex_); return weak_persistent_handles_.IsValidHandle(object); } bool IsActiveWeakPersistentHandle(Dart_WeakPersistentHandle object) { MutexLocker ml(&mutex_); return weak_persistent_handles_.IsValidHandle(object) && !weak_persistent_handles_.IsFreeHandle(object); } bool IsProtectedHandle(PersistentHandle* object) { MutexLocker ml(&mutex_); if (object == NULL) return false; return object == null_ || object == true_ || object == false_; } int CountPersistentHandles() { MutexLocker ml(&mutex_); return persistent_handles_.CountHandles(); } PersistentHandle* AcquiredError() { // The ApiError pre-allocated in the "vm-isolate" since we will not be able // to allocate it when the error actually occurs. // When the error occurs there will be outstanding acquires to internal // data pointers making it unsafe to allocate objects on the dart heap. MutexLocker ml(&mutex_); if (acquired_error_ == nullptr) { acquired_error_ = persistent_handles_.AllocateHandle(); acquired_error_->set_ptr(ApiError::typed_data_acquire_error()); } return acquired_error_; } void RunWithLockedPersistentHandles( std::function fun) { MutexLocker ml(&mutex_); fun(persistent_handles_); } void RunWithLockedWeakPersistentHandles( std::function fun) { MutexLocker ml(&mutex_); fun(weak_persistent_handles_); } WeakTable* acquired_table() { return &acquired_table_; } private: Mutex mutex_; PersistentHandles persistent_handles_; FinalizablePersistentHandles weak_persistent_handles_; WeakTable acquired_table_; // Persistent handles to important objects. PersistentHandle* null_; PersistentHandle* true_; PersistentHandle* false_; PersistentHandle* acquired_error_; DISALLOW_COPY_AND_ASSIGN(ApiState); }; inline FinalizablePersistentHandle* FinalizablePersistentHandle::New( IsolateGroup* isolate_group, const Object& object, void* peer, Dart_HandleFinalizer callback, intptr_t external_size, bool auto_delete) { ApiState* state = isolate_group->api_state(); ASSERT(state != NULL); FinalizablePersistentHandle* ref = state->AllocateWeakPersistentHandle(); ref->set_ptr(object); ref->set_peer(peer); ref->set_callback(callback); ref->set_auto_delete(auto_delete); // This may trigger GC, so it must be called last. ref->SetExternalSize(external_size, isolate_group); return ref; } } // namespace dart #endif // RUNTIME_VM_DART_API_STATE_H_