mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Vyacheslav Egorov](/assets/img/avatar_default.png)
This is the next step towards preventing compiler from directly peeking into runtime and instead interact with runtime through a well defined surface. The goal of the refactoring to locate all places where compiler accesses some runtime information and partion those accesses into two categories: - creating objects in the host runtime (e.g. allocating strings, numbers, etc) during compilation; - accessing properties of the target runtime (e.g. offsets of fields) to embed those into the generated code; This change introduces dart::compiler and dart::compiler::target namespaces. All code in the compiler will gradually be moved into dart::compiler namespace. One of the motivations for this change is to be able to prevent access to globally defined host constants like kWordSize by shadowing them in the dart::compiler namespace. The nested namespace dart::compiler::target hosts all information about target runtime that compiler could access, e.g. compiler::target::kWordSize defines word size of the target which will eventually be made different from the host kWordSize (defined by dart::kWordSize). The API for compiler to runtime interaction is placed into compiler_api.h. Note that we still permit runtime to access compiler internals directly - this is not going to be decoupled as part of this work. Issue https://github.com/dart-lang/sdk/issues/31709 Change-Id: If4396d295879391becfa6c38d4802bbff81f5b20 Reviewed-on: https://dart-review.googlesource.com/c/90242 Commit-Queue: Vyacheslav Egorov <vegorov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
289 lines
9.6 KiB
C++
289 lines
9.6 KiB
C++
// 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();
|
|
}
|
|
|
|
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:
|
|
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 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_
|