Optimizing HandleScope. Also fixed HandleScope destruction when API getter throws an exception.
Review URL: http://codereview.chromium.org/3792003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5689 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
1589238329
commit
0ea6c1e09f
11
include/v8.h
11
include/v8.h
@ -467,16 +467,21 @@ class V8EXPORT HandleScope {
|
||||
// typedef in the ImplementationUtilities class.
|
||||
class V8EXPORT Data {
|
||||
public:
|
||||
int extensions;
|
||||
internal::Object** next;
|
||||
internal::Object** limit;
|
||||
int level;
|
||||
|
||||
inline void Initialize() {
|
||||
extensions = -1;
|
||||
next = limit = NULL;
|
||||
level = 0;
|
||||
}
|
||||
};
|
||||
|
||||
void Leave();
|
||||
|
||||
Data previous_;
|
||||
|
||||
internal::Object** prev_next_;
|
||||
internal::Object** prev_limit_;
|
||||
|
||||
// Allow for the active closing of HandleScopes which allows to pass a handle
|
||||
// from the HandleScope being closed to the next top most HandleScope.
|
||||
|
26
src/api.cc
26
src/api.cc
@ -457,19 +457,37 @@ void V8::DisposeGlobal(i::Object** obj) {
|
||||
// --- H a n d l e s ---
|
||||
|
||||
|
||||
HandleScope::HandleScope() : is_closed_(false) {
|
||||
HandleScope::HandleScope()
|
||||
: prev_next_(i::HandleScope::current_.next),
|
||||
prev_limit_(i::HandleScope::current_.limit),
|
||||
is_closed_(false) {
|
||||
API_ENTRY_CHECK("HandleScope::HandleScope");
|
||||
i::HandleScope::Enter(&previous_);
|
||||
i::HandleScope::current_.level++;
|
||||
}
|
||||
|
||||
|
||||
HandleScope::~HandleScope() {
|
||||
if (!is_closed_) {
|
||||
i::HandleScope::Leave(&previous_);
|
||||
Leave();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HandleScope::Leave() {
|
||||
i::HandleScope::current_.level--;
|
||||
ASSERT(i::HandleScope::current_.level >= 0);
|
||||
i::HandleScope::current_.next = prev_next_;
|
||||
if (i::HandleScope::current_.limit != prev_limit_) {
|
||||
i::HandleScope::current_.limit = prev_limit_;
|
||||
i::HandleScope::DeleteExtensions();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
i::HandleScope::ZapRange(prev_next_, prev_limit_);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int HandleScope::NumberOfHandles() {
|
||||
return i::HandleScope::NumberOfHandles();
|
||||
}
|
||||
@ -553,7 +571,7 @@ i::Object** v8::HandleScope::RawClose(i::Object** value) {
|
||||
result = *value;
|
||||
}
|
||||
is_closed_ = true;
|
||||
i::HandleScope::Leave(&previous_);
|
||||
Leave();
|
||||
|
||||
if (value == NULL) {
|
||||
return NULL;
|
||||
|
35
src/api.h
35
src/api.h
@ -353,7 +353,7 @@ class HandleScopeImplementer {
|
||||
|
||||
|
||||
inline internal::Object** GetSpareOrNewBlock();
|
||||
inline void DeleteExtensions(int extensions);
|
||||
inline void DeleteExtensions(internal::Object** prev_limit);
|
||||
|
||||
inline void IncrementCallDepth() {call_depth_++;}
|
||||
inline void DecrementCallDepth() {call_depth_--;}
|
||||
@ -465,25 +465,28 @@ internal::Object** HandleScopeImplementer::GetSpareOrNewBlock() {
|
||||
}
|
||||
|
||||
|
||||
void HandleScopeImplementer::DeleteExtensions(int extensions) {
|
||||
if (spare_ != NULL) {
|
||||
DeleteArray(spare_);
|
||||
spare_ = NULL;
|
||||
}
|
||||
for (int i = extensions; i > 1; --i) {
|
||||
internal::Object** block = blocks_.RemoveLast();
|
||||
void HandleScopeImplementer::DeleteExtensions(internal::Object** prev_limit) {
|
||||
while (!blocks_.is_empty()) {
|
||||
internal::Object** block_start = blocks_.last();
|
||||
internal::Object** block_limit = block_start + kHandleBlockSize;
|
||||
#ifdef DEBUG
|
||||
v8::ImplementationUtilities::ZapHandleRange(block,
|
||||
&block[kHandleBlockSize]);
|
||||
// NoHandleAllocation may make the prev_limit to point inside the block.
|
||||
if (block_start <= prev_limit && prev_limit <= block_limit) break;
|
||||
#else
|
||||
if (prev_limit == block_limit) break;
|
||||
#endif
|
||||
DeleteArray(block);
|
||||
}
|
||||
spare_ = blocks_.RemoveLast();
|
||||
|
||||
blocks_.RemoveLast();
|
||||
#ifdef DEBUG
|
||||
v8::ImplementationUtilities::ZapHandleRange(
|
||||
spare_,
|
||||
&spare_[kHandleBlockSize]);
|
||||
v8::ImplementationUtilities::ZapHandleRange(block_start, block_limit);
|
||||
#endif
|
||||
if (spare_ != NULL) {
|
||||
DeleteArray(spare_);
|
||||
}
|
||||
spare_ = block_start;
|
||||
}
|
||||
ASSERT((blocks_.is_empty() && prev_limit == NULL) ||
|
||||
(!blocks_.is_empty() && prev_limit != NULL));
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
@ -583,6 +583,12 @@ ExternalReference ExternalReference::fill_heap_number_with_random_function() {
|
||||
}
|
||||
|
||||
|
||||
ExternalReference ExternalReference::delete_handle_scope_extensions() {
|
||||
return ExternalReference(Redirect(FUNCTION_ADDR(
|
||||
HandleScope::DeleteExtensions)));
|
||||
}
|
||||
|
||||
|
||||
ExternalReference ExternalReference::random_uint32_function() {
|
||||
return ExternalReference(Redirect(FUNCTION_ADDR(V8::Random)));
|
||||
}
|
||||
@ -653,8 +659,8 @@ ExternalReference ExternalReference::new_space_allocation_limit_address() {
|
||||
}
|
||||
|
||||
|
||||
ExternalReference ExternalReference::handle_scope_extensions_address() {
|
||||
return ExternalReference(HandleScope::current_extensions_address());
|
||||
ExternalReference ExternalReference::handle_scope_level_address() {
|
||||
return ExternalReference(HandleScope::current_level_address());
|
||||
}
|
||||
|
||||
|
||||
|
@ -482,6 +482,7 @@ class ExternalReference BASE_EMBEDDED {
|
||||
static ExternalReference fill_heap_number_with_random_function();
|
||||
static ExternalReference random_uint32_function();
|
||||
static ExternalReference transcendental_cache_array_address();
|
||||
static ExternalReference delete_handle_scope_extensions();
|
||||
|
||||
// Static data in the keyed lookup cache.
|
||||
static ExternalReference keyed_lookup_cache_keys();
|
||||
@ -519,9 +520,9 @@ class ExternalReference BASE_EMBEDDED {
|
||||
static ExternalReference double_fp_operation(Token::Value operation);
|
||||
static ExternalReference compare_doubles();
|
||||
|
||||
static ExternalReference handle_scope_extensions_address();
|
||||
static ExternalReference handle_scope_next_address();
|
||||
static ExternalReference handle_scope_limit_address();
|
||||
static ExternalReference handle_scope_level_address();
|
||||
|
||||
static ExternalReference scheduled_exception_address();
|
||||
|
||||
|
@ -55,18 +55,22 @@ inline T* Handle<T>::operator*() const {
|
||||
inline NoHandleAllocation::NoHandleAllocation() {
|
||||
v8::ImplementationUtilities::HandleScopeData* current =
|
||||
v8::ImplementationUtilities::CurrentHandleScope();
|
||||
extensions_ = current->extensions;
|
||||
// Shrink the current handle scope to make it impossible to do
|
||||
// handle allocations without an explicit handle scope.
|
||||
current->limit = current->next;
|
||||
current->extensions = -1;
|
||||
|
||||
level_ = current->level;
|
||||
current->level = 0;
|
||||
}
|
||||
|
||||
|
||||
inline NoHandleAllocation::~NoHandleAllocation() {
|
||||
// Restore state in current handle scope to re-enable handle
|
||||
// allocations.
|
||||
v8::ImplementationUtilities::CurrentHandleScope()->extensions = extensions_;
|
||||
v8::ImplementationUtilities::HandleScopeData* current =
|
||||
v8::ImplementationUtilities::CurrentHandleScope();
|
||||
ASSERT_EQ(0, current->level);
|
||||
current->level = level_;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace internal {
|
||||
|
||||
|
||||
v8::ImplementationUtilities::HandleScopeData HandleScope::current_ =
|
||||
{ -1, NULL, NULL };
|
||||
{ NULL, NULL, 0 };
|
||||
|
||||
|
||||
int HandleScope::NumberOfHandles() {
|
||||
@ -61,7 +61,7 @@ Object** HandleScope::Extend() {
|
||||
ASSERT(result == current_.limit);
|
||||
// Make sure there's at least one scope on the stack and that the
|
||||
// top of the scope stack isn't a barrier.
|
||||
if (current_.extensions < 0) {
|
||||
if (current_.level == 0) {
|
||||
Utils::ReportApiFailure("v8::HandleScope::CreateHandle()",
|
||||
"Cannot create a handle without a HandleScope");
|
||||
return NULL;
|
||||
@ -73,6 +73,7 @@ Object** HandleScope::Extend() {
|
||||
Object** limit = &impl->blocks()->last()[kHandleBlockSize];
|
||||
if (current_.limit != limit) {
|
||||
current_.limit = limit;
|
||||
ASSERT(limit - current_.next < kHandleBlockSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,7 +85,6 @@ Object** HandleScope::Extend() {
|
||||
// Add the extension to the global list of blocks, but count the
|
||||
// extension as part of the current scope.
|
||||
impl->blocks()->Add(result);
|
||||
current_.extensions++;
|
||||
current_.limit = &result[kHandleBlockSize];
|
||||
}
|
||||
|
||||
@ -93,21 +93,20 @@ Object** HandleScope::Extend() {
|
||||
|
||||
|
||||
void HandleScope::DeleteExtensions() {
|
||||
ASSERT(current_.extensions != 0);
|
||||
HandleScopeImplementer::instance()->DeleteExtensions(current_.extensions);
|
||||
HandleScopeImplementer::instance()->DeleteExtensions(current_.limit);
|
||||
}
|
||||
|
||||
|
||||
void HandleScope::ZapRange(Object** start, Object** end) {
|
||||
if (start == NULL) return;
|
||||
for (Object** p = start; p < end; p++) {
|
||||
ASSERT(end - start <= kHandleBlockSize);
|
||||
for (Object** p = start; p != end; p++) {
|
||||
*reinterpret_cast<Address*>(p) = v8::internal::kHandleZapValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Address HandleScope::current_extensions_address() {
|
||||
return reinterpret_cast<Address>(¤t_.extensions);
|
||||
Address HandleScope::current_level_address() {
|
||||
return reinterpret_cast<Address>(¤t_.level);
|
||||
}
|
||||
|
||||
|
||||
|
@ -107,12 +107,20 @@ class Handle {
|
||||
// for which the handle scope has been deleted is undefined.
|
||||
class HandleScope {
|
||||
public:
|
||||
HandleScope() : previous_(current_) {
|
||||
current_.extensions = 0;
|
||||
HandleScope() : prev_next_(current_.next), prev_limit_(current_.limit) {
|
||||
current_.level++;
|
||||
}
|
||||
|
||||
~HandleScope() {
|
||||
Leave(&previous_);
|
||||
current_.next = prev_next_;
|
||||
current_.level--;
|
||||
if (current_.limit != prev_limit_) {
|
||||
current_.limit = prev_limit_;
|
||||
DeleteExtensions();
|
||||
}
|
||||
#ifdef DEBUG
|
||||
ZapRange(prev_next_, prev_limit_);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Counts the number of allocated handles.
|
||||
@ -136,9 +144,9 @@ class HandleScope {
|
||||
// Deallocates any extensions used by the current scope.
|
||||
static void DeleteExtensions();
|
||||
|
||||
static Address current_extensions_address();
|
||||
static Address current_next_address();
|
||||
static Address current_limit_address();
|
||||
static Address current_level_address();
|
||||
|
||||
private:
|
||||
// Prevent heap allocation or illegal handle scopes.
|
||||
@ -148,27 +156,8 @@ class HandleScope {
|
||||
void operator delete(void* size_t);
|
||||
|
||||
static v8::ImplementationUtilities::HandleScopeData current_;
|
||||
const v8::ImplementationUtilities::HandleScopeData previous_;
|
||||
|
||||
// Pushes a fresh handle scope to be used when allocating new handles.
|
||||
static void Enter(
|
||||
v8::ImplementationUtilities::HandleScopeData* previous) {
|
||||
*previous = current_;
|
||||
current_.extensions = 0;
|
||||
}
|
||||
|
||||
// Re-establishes the previous scope state. Should be called only
|
||||
// once, and only for the current scope.
|
||||
static void Leave(
|
||||
const v8::ImplementationUtilities::HandleScopeData* previous) {
|
||||
if (current_.extensions > 0) {
|
||||
DeleteExtensions();
|
||||
}
|
||||
current_ = *previous;
|
||||
#ifdef DEBUG
|
||||
ZapRange(current_.next, current_.limit);
|
||||
#endif
|
||||
}
|
||||
Object** const prev_next_;
|
||||
Object** const prev_limit_;
|
||||
|
||||
// Extend the handle scope making room for more handles.
|
||||
static internal::Object** Extend();
|
||||
@ -362,7 +351,7 @@ class NoHandleAllocation BASE_EMBEDDED {
|
||||
inline NoHandleAllocation();
|
||||
inline ~NoHandleAllocation();
|
||||
private:
|
||||
int extensions_;
|
||||
int level_;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -695,7 +695,6 @@ class Assembler : public Malloced {
|
||||
void call(Label* L);
|
||||
void call(byte* entry, RelocInfo::Mode rmode);
|
||||
void call(const Operand& adr);
|
||||
void call(const ExternalReference& target);
|
||||
void call(Handle<Code> code, RelocInfo::Mode rmode);
|
||||
|
||||
// Jumps
|
||||
|
@ -3058,74 +3058,12 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
// If true, a Handle<T> passed by value is passed and returned by
|
||||
// using the location_ field directly. If false, it is passed and
|
||||
// returned as a pointer to a handle.
|
||||
#ifdef USING_BSD_ABI
|
||||
static const bool kPassHandlesDirectly = true;
|
||||
#else
|
||||
static const bool kPassHandlesDirectly = false;
|
||||
#endif
|
||||
|
||||
|
||||
void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
|
||||
Label empty_handle;
|
||||
Label prologue;
|
||||
Label promote_scheduled_exception;
|
||||
__ EnterApiExitFrame(kStackSpace, kArgc);
|
||||
STATIC_ASSERT(kArgc == 4);
|
||||
if (kPassHandlesDirectly) {
|
||||
// When handles as passed directly we don't have to allocate extra
|
||||
// space for and pass an out parameter.
|
||||
__ mov(Operand(esp, 0 * kPointerSize), ebx); // name.
|
||||
__ mov(Operand(esp, 1 * kPointerSize), eax); // arguments pointer.
|
||||
} else {
|
||||
// The function expects three arguments to be passed but we allocate
|
||||
// four to get space for the output cell. The argument slots are filled
|
||||
// as follows:
|
||||
//
|
||||
// 3: output cell
|
||||
// 2: arguments pointer
|
||||
// 1: name
|
||||
// 0: pointer to the output cell
|
||||
//
|
||||
// Note that this is one more "argument" than the function expects
|
||||
// so the out cell will have to be popped explicitly after returning
|
||||
// from the function.
|
||||
__ mov(Operand(esp, 1 * kPointerSize), ebx); // name.
|
||||
__ mov(Operand(esp, 2 * kPointerSize), eax); // arguments pointer.
|
||||
__ mov(ebx, esp);
|
||||
__ add(Operand(ebx), Immediate(3 * kPointerSize));
|
||||
__ mov(Operand(esp, 0 * kPointerSize), ebx); // output
|
||||
__ mov(Operand(esp, 3 * kPointerSize), Immediate(0)); // out cell.
|
||||
}
|
||||
// Call the api function!
|
||||
__ call(fun()->address(), RelocInfo::RUNTIME_ENTRY);
|
||||
// Check if the function scheduled an exception.
|
||||
ExternalReference scheduled_exception_address =
|
||||
ExternalReference::scheduled_exception_address();
|
||||
__ cmp(Operand::StaticVariable(scheduled_exception_address),
|
||||
Immediate(Factory::the_hole_value()));
|
||||
__ j(not_equal, &promote_scheduled_exception, not_taken);
|
||||
if (!kPassHandlesDirectly) {
|
||||
// The returned value is a pointer to the handle holding the result.
|
||||
// Dereference this to get to the location.
|
||||
__ mov(eax, Operand(eax, 0));
|
||||
}
|
||||
// Check if the result handle holds 0.
|
||||
__ test(eax, Operand(eax));
|
||||
__ j(zero, &empty_handle, not_taken);
|
||||
// It was non-zero. Dereference to get the result value.
|
||||
__ mov(eax, Operand(eax, 0));
|
||||
__ bind(&prologue);
|
||||
__ LeaveExitFrame();
|
||||
__ ret(0);
|
||||
__ bind(&promote_scheduled_exception);
|
||||
__ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
|
||||
__ bind(&empty_handle);
|
||||
// It was zero; the result is undefined.
|
||||
__ mov(eax, Factory::undefined_value());
|
||||
__ jmp(&prologue);
|
||||
__ PrepareCallApiFunction(kStackSpace, kArgc);
|
||||
STATIC_ASSERT(kArgc == 2);
|
||||
__ mov(ApiParameterOperand(0), ebx); // name.
|
||||
__ mov(ApiParameterOperand(1), eax); // arguments pointer.
|
||||
__ CallApiFunctionAndReturn(fun(), kArgc);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1115,66 +1115,116 @@ void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PushHandleScope(Register scratch) {
|
||||
// Push the number of extensions, smi-tagged so the gc will ignore it.
|
||||
ExternalReference extensions_address =
|
||||
ExternalReference::handle_scope_extensions_address();
|
||||
mov(scratch, Operand::StaticVariable(extensions_address));
|
||||
SmiTag(scratch);
|
||||
push(scratch);
|
||||
mov(Operand::StaticVariable(extensions_address), Immediate(0));
|
||||
// Push next and limit pointers which will be wordsize aligned and
|
||||
// hence automatically smi tagged.
|
||||
// If true, a Handle<T> passed by value is passed and returned by
|
||||
// using the location_ field directly. If false, it is passed and
|
||||
// returned as a pointer to a handle.
|
||||
#ifdef USING_BSD_ABI
|
||||
static const bool kPassHandlesDirectly = true;
|
||||
#else
|
||||
static const bool kPassHandlesDirectly = false;
|
||||
#endif
|
||||
|
||||
|
||||
Operand ApiParameterOperand(int index) {
|
||||
return Operand(esp, (index + (kPassHandlesDirectly ? 0 : 1)) * kPointerSize);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PrepareCallApiFunction(int stack_space, int argc) {
|
||||
if (kPassHandlesDirectly) {
|
||||
EnterApiExitFrame(stack_space, argc);
|
||||
// When handles as passed directly we don't have to allocate extra
|
||||
// space for and pass an out parameter.
|
||||
} else {
|
||||
// We allocate two additional slots: return value and pointer to it.
|
||||
EnterApiExitFrame(stack_space, argc + 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function, int argc) {
|
||||
if (!kPassHandlesDirectly) {
|
||||
// The argument slots are filled as follows:
|
||||
//
|
||||
// n + 1: output cell
|
||||
// n: arg n
|
||||
// ...
|
||||
// 1: arg1
|
||||
// 0: pointer to the output cell
|
||||
//
|
||||
// Note that this is one more "argument" than the function expects
|
||||
// so the out cell will have to be popped explicitly after returning
|
||||
// from the function. The out cell contains Handle.
|
||||
lea(eax, Operand(esp, (argc + 1) * kPointerSize)); // pointer to out cell.
|
||||
mov(Operand(esp, 0 * kPointerSize), eax); // output.
|
||||
mov(Operand(esp, (argc + 1) * kPointerSize), Immediate(0)); // out cell.
|
||||
}
|
||||
|
||||
ExternalReference next_address =
|
||||
ExternalReference::handle_scope_next_address();
|
||||
push(Operand::StaticVariable(next_address));
|
||||
ExternalReference limit_address =
|
||||
ExternalReference::handle_scope_limit_address();
|
||||
push(Operand::StaticVariable(limit_address));
|
||||
}
|
||||
ExternalReference level_address =
|
||||
ExternalReference::handle_scope_level_address();
|
||||
|
||||
// Allocate HandleScope in callee-save registers.
|
||||
mov(ebx, Operand::StaticVariable(next_address));
|
||||
mov(edi, Operand::StaticVariable(limit_address));
|
||||
add(Operand::StaticVariable(level_address), Immediate(1));
|
||||
|
||||
Object* MacroAssembler::PopHandleScopeHelper(Register saved,
|
||||
Register scratch,
|
||||
bool gc_allowed) {
|
||||
Object* result = NULL;
|
||||
ExternalReference extensions_address =
|
||||
ExternalReference::handle_scope_extensions_address();
|
||||
Label write_back;
|
||||
mov(scratch, Operand::StaticVariable(extensions_address));
|
||||
cmp(Operand(scratch), Immediate(0));
|
||||
j(equal, &write_back);
|
||||
push(saved);
|
||||
if (gc_allowed) {
|
||||
CallRuntime(Runtime::kDeleteHandleScopeExtensions, 0);
|
||||
} else {
|
||||
result = TryCallRuntime(Runtime::kDeleteHandleScopeExtensions, 0);
|
||||
if (result->IsFailure()) return result;
|
||||
// Call the api function!
|
||||
call(function->address(), RelocInfo::RUNTIME_ENTRY);
|
||||
|
||||
if (!kPassHandlesDirectly) {
|
||||
// The returned value is a pointer to the handle holding the result.
|
||||
// Dereference this to get to the location.
|
||||
mov(eax, Operand(eax, 0));
|
||||
}
|
||||
pop(saved);
|
||||
|
||||
bind(&write_back);
|
||||
ExternalReference limit_address =
|
||||
ExternalReference::handle_scope_limit_address();
|
||||
pop(Operand::StaticVariable(limit_address));
|
||||
ExternalReference next_address =
|
||||
ExternalReference::handle_scope_next_address();
|
||||
pop(Operand::StaticVariable(next_address));
|
||||
pop(scratch);
|
||||
SmiUntag(scratch);
|
||||
mov(Operand::StaticVariable(extensions_address), scratch);
|
||||
Label empty_handle;
|
||||
Label prologue;
|
||||
Label promote_scheduled_exception;
|
||||
Label delete_allocated_handles;
|
||||
Label leave_exit_frame;
|
||||
|
||||
return result;
|
||||
}
|
||||
// Check if the result handle holds 0.
|
||||
test(eax, Operand(eax));
|
||||
j(zero, &empty_handle, not_taken);
|
||||
// It was non-zero. Dereference to get the result value.
|
||||
mov(eax, Operand(eax, 0));
|
||||
bind(&prologue);
|
||||
// No more valid handles (the result handle was the last one). Restore
|
||||
// previous handle scope.
|
||||
mov(Operand::StaticVariable(next_address), ebx);
|
||||
sub(Operand::StaticVariable(level_address), Immediate(1));
|
||||
Assert(above_equal, "Invalid HandleScope level");
|
||||
cmp(edi, Operand::StaticVariable(limit_address));
|
||||
j(not_equal, &delete_allocated_handles, not_taken);
|
||||
bind(&leave_exit_frame);
|
||||
|
||||
// Check if the function scheduled an exception.
|
||||
ExternalReference scheduled_exception_address =
|
||||
ExternalReference::scheduled_exception_address();
|
||||
cmp(Operand::StaticVariable(scheduled_exception_address),
|
||||
Immediate(Factory::the_hole_value()));
|
||||
j(not_equal, &promote_scheduled_exception, not_taken);
|
||||
LeaveExitFrame();
|
||||
ret(0);
|
||||
bind(&promote_scheduled_exception);
|
||||
TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
|
||||
bind(&empty_handle);
|
||||
// It was zero; the result is undefined.
|
||||
mov(eax, Factory::undefined_value());
|
||||
jmp(&prologue);
|
||||
|
||||
void MacroAssembler::PopHandleScope(Register saved, Register scratch) {
|
||||
PopHandleScopeHelper(saved, scratch, true);
|
||||
}
|
||||
|
||||
|
||||
Object* MacroAssembler::TryPopHandleScope(Register saved, Register scratch) {
|
||||
return PopHandleScopeHelper(saved, scratch, false);
|
||||
// HandleScope limit has changed. Delete allocated extensions.
|
||||
bind(&delete_allocated_handles);
|
||||
mov(Operand::StaticVariable(limit_address), edi);
|
||||
mov(edi, eax);
|
||||
mov(eax, Immediate(ExternalReference::delete_handle_scope_extensions()));
|
||||
call(Operand(eax));
|
||||
mov(eax, edi);
|
||||
jmp(&leave_exit_frame);
|
||||
}
|
||||
|
||||
|
||||
|
@ -480,15 +480,16 @@ class MacroAssembler: public Assembler {
|
||||
void CallCFunction(ExternalReference function, int num_arguments);
|
||||
void CallCFunction(Register function, int num_arguments);
|
||||
|
||||
void PushHandleScope(Register scratch);
|
||||
// Prepares stack to put arguments (aligns and so on). Reserves
|
||||
// space for return value if needed (assumes the return value is a handle).
|
||||
// Uses callee-saved esi to restore stack state after call. Arguments must be
|
||||
// stored in ApiParameterOperand(0), ApiParameterOperand(1) etc.
|
||||
void PrepareCallApiFunction(int stack_space, int argc);
|
||||
|
||||
// Pops a handle scope using the specified scratch register and
|
||||
// ensuring that saved register is left unchanged.
|
||||
void PopHandleScope(Register saved, Register scratch);
|
||||
|
||||
// As PopHandleScope, but does not perform a GC. Instead, returns a
|
||||
// retry after GC failure object if GC is necessary.
|
||||
Object* TryPopHandleScope(Register saved, Register scratch);
|
||||
// Tail call an API function (jump). Allocates HandleScope, extracts
|
||||
// returned value from handle and propagates exceptions.
|
||||
// Clobbers ebx, esi, edi and caller-save registers.
|
||||
void CallApiFunctionAndReturn(ApiFunction* function, int argc);
|
||||
|
||||
// Jump to a runtime routine.
|
||||
void JumpToExternalReference(const ExternalReference& ext);
|
||||
@ -639,6 +640,9 @@ static inline Operand FieldOperand(Register object,
|
||||
return Operand(object, index, scale, offset - kHeapObjectTag);
|
||||
}
|
||||
|
||||
// Generates an Operand for saving parameters after PrepareCallApiFunction.
|
||||
Operand ApiParameterOperand(int index);
|
||||
|
||||
|
||||
#ifdef GENERATED_CODE_COVERAGE
|
||||
extern void LogGeneratedCodeCoverage(const char* file_line);
|
||||
|
@ -1035,7 +1035,6 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
|
||||
Handle<AccessorInfo> callback_handle(callback);
|
||||
|
||||
__ EnterInternalFrame();
|
||||
__ PushHandleScope(scratch2);
|
||||
// Push the stack address where the list of arguments ends.
|
||||
__ mov(scratch2, esp);
|
||||
__ sub(Operand(scratch2), Immediate(2 * kPointerSize));
|
||||
@ -1070,17 +1069,6 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
|
||||
*failure = Failure::cast(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to avoid using eax since that now holds the result.
|
||||
Register tmp = scratch2.is(eax) ? reg : scratch2;
|
||||
// Emitting PopHandleScope may try to allocate. Do not allow the
|
||||
// assembler to perform a garbage collection but instead return a
|
||||
// failure object.
|
||||
result = masm()->TryPopHandleScope(eax, tmp);
|
||||
if (result->IsFailure()) {
|
||||
*failure = Failure::cast(result);
|
||||
return false;
|
||||
}
|
||||
__ LeaveInternalFrame();
|
||||
|
||||
__ ret(0);
|
||||
|
@ -9951,13 +9951,6 @@ static Object* Runtime_Abort(Arguments args) {
|
||||
}
|
||||
|
||||
|
||||
static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
|
||||
ASSERT(args.length() == 0);
|
||||
HandleScope::DeleteExtensions();
|
||||
return Heap::undefined_value();
|
||||
}
|
||||
|
||||
|
||||
static Object* CacheMiss(FixedArray* cache_obj, int index, Object* key_obj) {
|
||||
ASSERT(index % 2 == 0); // index of the key
|
||||
ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
|
||||
|
@ -297,8 +297,6 @@ namespace internal {
|
||||
F(Log, 2, 1) \
|
||||
/* ES5 */ \
|
||||
F(LocalKeys, 1, 1) \
|
||||
/* Handle scopes */ \
|
||||
F(DeleteHandleScopeExtensions, 0, 1) \
|
||||
/* Cache suport */ \
|
||||
F(GetFromCache, 2, 1) \
|
||||
\
|
||||
|
@ -337,6 +337,11 @@ void ExternalReferenceTable::PopulateTable() {
|
||||
3,
|
||||
"V8::Random");
|
||||
|
||||
Add(ExternalReference::delete_handle_scope_extensions().address(),
|
||||
RUNTIME_ENTRY,
|
||||
3,
|
||||
"HandleScope::DeleteExtensions");
|
||||
|
||||
// Miscellaneous
|
||||
Add(ExternalReference::the_hole_value_location().address(),
|
||||
UNCLASSIFIED,
|
||||
@ -457,6 +462,18 @@ void ExternalReferenceTable::PopulateTable() {
|
||||
UNCLASSIFIED,
|
||||
29,
|
||||
"TranscendentalCache::caches()");
|
||||
Add(ExternalReference::handle_scope_next_address().address(),
|
||||
UNCLASSIFIED,
|
||||
30,
|
||||
"HandleScope::next");
|
||||
Add(ExternalReference::handle_scope_limit_address().address(),
|
||||
UNCLASSIFIED,
|
||||
31,
|
||||
"HandleScope::limit");
|
||||
Add(ExternalReference::handle_scope_level_address().address(),
|
||||
UNCLASSIFIED,
|
||||
32,
|
||||
"HandleScope::level");
|
||||
}
|
||||
|
||||
|
||||
|
@ -2484,11 +2484,7 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
|
||||
|
||||
|
||||
void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
|
||||
Label empty_result;
|
||||
Label prologue;
|
||||
Label promote_scheduled_exception;
|
||||
__ EnterApiExitFrame(kStackSpace, 0);
|
||||
ASSERT_EQ(kArgc, 4);
|
||||
__ PrepareCallApiFunction(kStackSpace);
|
||||
#ifdef _WIN64
|
||||
// All the parameters should be set up by a caller.
|
||||
#else
|
||||
@ -2497,35 +2493,7 @@ void ApiGetterEntryStub::Generate(MacroAssembler* masm) {
|
||||
// Second parameter register rdi should be set with pointer to AccessorInfo
|
||||
// by a caller.
|
||||
#endif
|
||||
// Call the api function!
|
||||
__ movq(rax,
|
||||
reinterpret_cast<int64_t>(fun()->address()),
|
||||
RelocInfo::RUNTIME_ENTRY);
|
||||
__ call(rax);
|
||||
// Check if the function scheduled an exception.
|
||||
ExternalReference scheduled_exception_address =
|
||||
ExternalReference::scheduled_exception_address();
|
||||
__ movq(rsi, scheduled_exception_address);
|
||||
__ Cmp(Operand(rsi, 0), Factory::the_hole_value());
|
||||
__ j(not_equal, &promote_scheduled_exception);
|
||||
#ifdef _WIN64
|
||||
// rax keeps a pointer to v8::Handle, unpack it.
|
||||
__ movq(rax, Operand(rax, 0));
|
||||
#endif
|
||||
// Check if the result handle holds 0.
|
||||
__ testq(rax, rax);
|
||||
__ j(zero, &empty_result);
|
||||
// It was non-zero. Dereference to get the result value.
|
||||
__ movq(rax, Operand(rax, 0));
|
||||
__ bind(&prologue);
|
||||
__ LeaveExitFrame();
|
||||
__ ret(0);
|
||||
__ bind(&promote_scheduled_exception);
|
||||
__ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
|
||||
__ bind(&empty_result);
|
||||
// It was zero; the result is undefined.
|
||||
__ Move(rax, Factory::undefined_value());
|
||||
__ jmp(&prologue);
|
||||
__ CallApiFunctionAndReturn(fun());
|
||||
}
|
||||
|
||||
|
||||
|
@ -469,76 +469,89 @@ static int Offset(ExternalReference ref0, ExternalReference ref1) {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PushHandleScope(Register scratch) {
|
||||
ExternalReference extensions_address =
|
||||
ExternalReference::handle_scope_extensions_address();
|
||||
const int kExtensionsOffset = 0;
|
||||
const int kNextOffset = Offset(
|
||||
ExternalReference::handle_scope_next_address(),
|
||||
extensions_address);
|
||||
const int kLimitOffset = Offset(
|
||||
ExternalReference::handle_scope_limit_address(),
|
||||
extensions_address);
|
||||
|
||||
// Push the number of extensions, smi-tagged so the gc will ignore it.
|
||||
movq(kScratchRegister, extensions_address);
|
||||
movq(scratch, Operand(kScratchRegister, kExtensionsOffset));
|
||||
movq(Operand(kScratchRegister, kExtensionsOffset), Immediate(0));
|
||||
Integer32ToSmi(scratch, scratch);
|
||||
push(scratch);
|
||||
// Push next and limit pointers which will be wordsize aligned and
|
||||
// hence automatically smi tagged.
|
||||
push(Operand(kScratchRegister, kNextOffset));
|
||||
push(Operand(kScratchRegister, kLimitOffset));
|
||||
void MacroAssembler::PrepareCallApiFunction(int stack_space) {
|
||||
EnterApiExitFrame(stack_space, 0);
|
||||
}
|
||||
|
||||
|
||||
Object* MacroAssembler::PopHandleScopeHelper(Register saved,
|
||||
Register scratch,
|
||||
bool gc_allowed) {
|
||||
ExternalReference extensions_address =
|
||||
ExternalReference::handle_scope_extensions_address();
|
||||
const int kExtensionsOffset = 0;
|
||||
const int kNextOffset = Offset(
|
||||
ExternalReference::handle_scope_next_address(),
|
||||
extensions_address);
|
||||
const int kLimitOffset = Offset(
|
||||
ExternalReference::handle_scope_limit_address(),
|
||||
extensions_address);
|
||||
|
||||
Object* result = NULL;
|
||||
void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function) {
|
||||
Label empty_result;
|
||||
Label prologue;
|
||||
Label promote_scheduled_exception;
|
||||
Label delete_allocated_handles;
|
||||
Label leave_exit_frame;
|
||||
Label write_back;
|
||||
movq(kScratchRegister, extensions_address);
|
||||
cmpq(Operand(kScratchRegister, kExtensionsOffset), Immediate(0));
|
||||
j(equal, &write_back);
|
||||
push(saved);
|
||||
if (gc_allowed) {
|
||||
CallRuntime(Runtime::kDeleteHandleScopeExtensions, 0);
|
||||
} else {
|
||||
result = TryCallRuntime(Runtime::kDeleteHandleScopeExtensions, 0);
|
||||
if (result->IsFailure()) return result;
|
||||
}
|
||||
pop(saved);
|
||||
movq(kScratchRegister, extensions_address);
|
||||
|
||||
bind(&write_back);
|
||||
pop(Operand(kScratchRegister, kLimitOffset));
|
||||
pop(Operand(kScratchRegister, kNextOffset));
|
||||
pop(scratch);
|
||||
SmiToInteger32(scratch, scratch);
|
||||
movq(Operand(kScratchRegister, kExtensionsOffset), scratch);
|
||||
ExternalReference next_address =
|
||||
ExternalReference::handle_scope_next_address();
|
||||
const int kNextOffset = 0;
|
||||
const int kLimitOffset = Offset(
|
||||
ExternalReference::handle_scope_limit_address(),
|
||||
next_address);
|
||||
const int kLevelOffset = Offset(
|
||||
ExternalReference::handle_scope_level_address(),
|
||||
next_address);
|
||||
ExternalReference scheduled_exception_address =
|
||||
ExternalReference::scheduled_exception_address();
|
||||
|
||||
return result;
|
||||
}
|
||||
// Allocate HandleScope in callee-save registers.
|
||||
Register prev_next_address_reg = r14;
|
||||
Register prev_limit_reg = rbx;
|
||||
Register base_reg = kSmiConstantRegister;
|
||||
movq(base_reg, next_address);
|
||||
movq(prev_next_address_reg, Operand(base_reg, kNextOffset));
|
||||
movq(prev_limit_reg, Operand(base_reg, kLimitOffset));
|
||||
addl(Operand(base_reg, kLevelOffset), Immediate(1));
|
||||
// Call the api function!
|
||||
movq(rax,
|
||||
reinterpret_cast<int64_t>(function->address()),
|
||||
RelocInfo::RUNTIME_ENTRY);
|
||||
call(rax);
|
||||
|
||||
#ifdef _WIN64
|
||||
// rax keeps a pointer to v8::Handle, unpack it.
|
||||
movq(rax, Operand(rax, 0));
|
||||
#endif
|
||||
// Check if the result handle holds 0.
|
||||
testq(rax, rax);
|
||||
j(zero, &empty_result);
|
||||
// It was non-zero. Dereference to get the result value.
|
||||
movq(rax, Operand(rax, 0));
|
||||
bind(&prologue);
|
||||
|
||||
void MacroAssembler::PopHandleScope(Register saved, Register scratch) {
|
||||
PopHandleScopeHelper(saved, scratch, true);
|
||||
}
|
||||
// No more valid handles (the result handle was the last one). Restore
|
||||
// previous handle scope.
|
||||
subl(Operand(base_reg, kLevelOffset), Immediate(1));
|
||||
movq(Operand(base_reg, kNextOffset), prev_next_address_reg);
|
||||
cmpq(prev_limit_reg, Operand(base_reg, kLimitOffset));
|
||||
j(not_equal, &delete_allocated_handles);
|
||||
bind(&leave_exit_frame);
|
||||
InitializeSmiConstantRegister();
|
||||
|
||||
// Check if the function scheduled an exception.
|
||||
movq(rsi, scheduled_exception_address);
|
||||
Cmp(Operand(rsi, 0), Factory::the_hole_value());
|
||||
j(not_equal, &promote_scheduled_exception);
|
||||
|
||||
Object* MacroAssembler::TryPopHandleScope(Register saved, Register scratch) {
|
||||
return PopHandleScopeHelper(saved, scratch, false);
|
||||
LeaveExitFrame();
|
||||
ret(0);
|
||||
|
||||
bind(&promote_scheduled_exception);
|
||||
TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
|
||||
|
||||
bind(&empty_result);
|
||||
// It was zero; the result is undefined.
|
||||
Move(rax, Factory::undefined_value());
|
||||
jmp(&prologue);
|
||||
|
||||
// HandleScope limit has changed. Delete allocated extensions.
|
||||
bind(&delete_allocated_handles);
|
||||
movq(Operand(base_reg, kLimitOffset), prev_limit_reg);
|
||||
movq(prev_limit_reg, rax);
|
||||
movq(rax, ExternalReference::delete_handle_scope_extensions());
|
||||
call(rax);
|
||||
movq(rax, prev_limit_reg);
|
||||
jmp(&leave_exit_frame);
|
||||
}
|
||||
|
||||
|
||||
|
@ -816,19 +816,18 @@ class MacroAssembler: public Assembler {
|
||||
int num_arguments,
|
||||
int result_size);
|
||||
|
||||
void PushHandleScope(Register scratch);
|
||||
|
||||
// Pops a handle scope using the specified scratch register and
|
||||
// ensuring that saved register is left unchanged.
|
||||
void PopHandleScope(Register saved, Register scratch);
|
||||
|
||||
// As PopHandleScope, but does not perform a GC. Instead, returns a
|
||||
// retry after GC failure object if GC is necessary.
|
||||
Object* TryPopHandleScope(Register saved, Register scratch);
|
||||
|
||||
// Jump to a runtime routine.
|
||||
void JumpToExternalReference(const ExternalReference& ext, int result_size);
|
||||
|
||||
// Prepares stack to put arguments (aligns and so on).
|
||||
// Uses calle-saved esi to restore stack state after call.
|
||||
void PrepareCallApiFunction(int stack_space);
|
||||
|
||||
// Tail call an API function (jump). Allocates HandleScope, extracts
|
||||
// returned value from handle and propogates exceptions.
|
||||
// Clobbers ebx, edi and caller-save registers.
|
||||
void CallApiFunctionAndReturn(ApiFunction* function);
|
||||
|
||||
// Before calling a C-function from generated code, align arguments on stack.
|
||||
// After aligning the frame, arguments must be stored in esp[0], esp[4],
|
||||
// etc., not pushed. The argument count assumes all arguments are word sized.
|
||||
|
@ -2547,7 +2547,6 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
|
||||
Handle<AccessorInfo> callback_handle(callback);
|
||||
|
||||
__ EnterInternalFrame();
|
||||
__ PushHandleScope(scratch2);
|
||||
// Push the stack address where the list of arguments ends.
|
||||
__ movq(scratch2, rsp);
|
||||
__ subq(scratch2, Immediate(2 * kPointerSize));
|
||||
@ -2601,17 +2600,6 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
|
||||
// Discard allocated slot.
|
||||
__ addq(rsp, Immediate(kPointerSize));
|
||||
#endif
|
||||
|
||||
// We need to avoid using rax since that now holds the result.
|
||||
Register tmp = scratch2.is(rax) ? reg : scratch2;
|
||||
// Emitting PopHandleScope may try to allocate. Do not allow the
|
||||
// assembler to perform a garbage collection but instead return a
|
||||
// failure object.
|
||||
result = masm()->TryPopHandleScope(rax, tmp);
|
||||
if (result->IsFailure()) {
|
||||
*failure = Failure::cast(result);
|
||||
return false;
|
||||
}
|
||||
__ LeaveInternalFrame();
|
||||
|
||||
__ ret(0);
|
||||
|
Loading…
Reference in New Issue
Block a user