Commit b5a19c10 authored by ager@chromium.org's avatar ager@chromium.org

Rework the way we handle the fact that the ARM simulator uses a

separate JS stack.

In exception handling, we need to be able to compare addresses into
the JavaScript portion of the stack with the address of a C++ handler
on the stack.  Since the stacks are separate on the simulator, we need
a JavaScript stack address corresponding to a C++ try catch handler in
order to perform valid address comparisons.

On the simulator, we now link the C++ try catch handlers indirectly
through the JS stack and use the JS stack indirection address for
comparisons.

      JS                    C++
                           
                           handler
 [C++ address]   <------    next_
                \
                 \
                  \---->   handler
 [C++ address]   <------    next_


On actual hardware the C++ try catch handlers continue to be directly
linked.

BUG=http://code.google.com/p/v8/issues/detail?id=271
Review URL: http://codereview.chromium.org/360004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3228 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent ec7034e6
......@@ -129,8 +129,9 @@ class Data;
namespace internal {
class Object;
class Arguments;
class Object;
class Top;
}
......@@ -2532,15 +2533,16 @@ class V8EXPORT TryCatch {
*/
void SetCaptureMessage(bool value);
public:
TryCatch* next_;
private:
void* next_;
void* exception_;
void* message_;
bool is_verbose_ : 1;
bool can_continue_ : 1;
bool capture_message_ : 1;
bool rethrow_ : 1;
void* js_handler_;
friend class v8::internal::Top;
};
......
......@@ -1191,14 +1191,13 @@ void Script::SetData(v8::Handle<Value> data) {
v8::TryCatch::TryCatch()
: next_(i::Top::try_catch_handler()),
: next_(i::Top::try_catch_handler_address()),
exception_(i::Heap::the_hole_value()),
message_(i::Smi::FromInt(0)),
is_verbose_(false),
can_continue_(true),
capture_message_(true),
rethrow_(false),
js_handler_(NULL) {
rethrow_(false) {
i::Top::RegisterTryCatchHandler(this);
}
......
......@@ -1924,6 +1924,25 @@ int32_t Simulator::Call(byte* entry, int argument_count, ...) {
return result;
}
uintptr_t Simulator::PushAddress(uintptr_t address) {
int new_sp = get_register(sp) - sizeof(uintptr_t);
uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(new_sp);
*stack_slot = address;
set_register(sp, new_sp);
return new_sp;
}
uintptr_t Simulator::PopAddress() {
int current_sp = get_register(sp);
uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(current_sp);
uintptr_t address = *stack_slot;
set_register(sp, current_sp + sizeof(uintptr_t));
return address;
}
} } // namespace assembler::arm
#endif // !defined(__arm__)
......@@ -52,6 +52,12 @@ class SimulatorStack : public v8::internal::AllStatic {
static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
return c_limit;
}
static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
return try_catch_address;
}
static inline void UnregisterCTryCatch() { }
};
......@@ -60,6 +66,10 @@ class SimulatorStack : public v8::internal::AllStatic {
#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6) \
entry(p0, p1, p2, p3, p4, p5, p6)
#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
reinterpret_cast<TryCatch*>(try_catch_address)
#else // defined(__arm__)
// When running with the simulator transition into simulated execution at this
......@@ -73,6 +83,11 @@ class SimulatorStack : public v8::internal::AllStatic {
assembler::arm::Simulator::current()->Call( \
FUNCTION_ADDR(entry), 7, p0, p1, p2, p3, p4, p5, p6)
#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
try_catch_address == NULL ? \
NULL : *(reinterpret_cast<TryCatch**>(try_catch_address))
#include "constants-arm.h"
......@@ -124,6 +139,12 @@ class Simulator {
// which sets up the simulator state and grabs the result on return.
int32_t Call(byte* entry, int argument_count, ...);
// Push an address onto the JS stack.
uintptr_t PushAddress(uintptr_t address);
// Pop an address from the JS stack.
uintptr_t PopAddress();
private:
enum special_values {
// Known bad pc value to ensure that the simulator does not execute
......@@ -198,20 +219,20 @@ class Simulator {
void SetFpResult(const double& result);
void TrashCallerSaveRegisters();
// architecture state
// Architecture state.
int32_t registers_[16];
bool n_flag_;
bool z_flag_;
bool c_flag_;
bool v_flag_;
// simulator support
// Simulator support.
char* stack_;
bool pc_modified_;
int icount_;
static bool initialized_;
// registered breakpoints
// Registered breakpoints.
Instr* break_pc_;
instr_t break_instr_;
};
......@@ -229,6 +250,15 @@ class SimulatorStack : public v8::internal::AllStatic {
static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
return assembler::arm::Simulator::current()->StackLimit();
}
static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
assembler::arm::Simulator* sim = assembler::arm::Simulator::current();
return sim->PushAddress(try_catch_address);
}
static inline void UnregisterCTryCatch() {
assembler::arm::Simulator::current()->PopAddress();
}
};
......
......@@ -31,18 +31,8 @@
#include "api.h"
#include "codegen-inl.h"
#if V8_TARGET_ARCH_IA32
#include "ia32/simulator-ia32.h"
#elif V8_TARGET_ARCH_X64
#include "x64/simulator-x64.h"
#elif V8_TARGET_ARCH_ARM
#include "arm/simulator-arm.h"
#else
#error Unsupported target architecture.
#endif
#include "debug.h"
#include "simulator.h"
#include "v8threads.h"
namespace v8 {
......
......@@ -43,6 +43,12 @@ class SimulatorStack : public v8::internal::AllStatic {
static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
return c_limit;
}
static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
return try_catch_address;
}
static inline void UnregisterCTryCatch() { }
};
// Call the generated regexp code directly. The entry function pointer should
......@@ -50,4 +56,7 @@ class SimulatorStack : public v8::internal::AllStatic {
#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6) \
entry(p0, p1, p2, p3, p4, p5, p6)
#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
reinterpret_cast<TryCatch*>(try_catch_address)
#endif // V8_IA32_SIMULATOR_IA32_H_
......@@ -30,13 +30,7 @@
#include "assembler.h"
#include "regexp-stack.h"
#include "regexp-macro-assembler.h"
#if V8_TARGET_ARCH_ARM
#include "arm/simulator-arm.h"
#elif V8_TARGET_ARCH_IA32
#include "ia32/simulator-ia32.h"
#elif V8_TARGET_ARCH_X64
#include "x64/simulator-x64.h"
#endif
#include "simulator.h"
namespace v8 {
namespace internal {
......
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_SIMULATOR_H_
#define V8_SIMULATOR_H_
#if V8_TARGET_ARCH_IA32
#include "ia32/simulator-ia32.h"
#elif V8_TARGET_ARCH_X64
#include "x64/simulator-x64.h"
#elif V8_TARGET_ARCH_ARM
#include "arm/simulator-arm.h"
#else
#error Unsupported target architecture.
#endif
#endif // V8_SIMULATOR_H_
......@@ -31,8 +31,9 @@
#include "bootstrapper.h"
#include "debug.h"
#include "execution.h"
#include "string-stream.h"
#include "platform.h"
#include "simulator.h"
#include "string-stream.h"
namespace v8 {
namespace internal {
......@@ -50,6 +51,30 @@ Address top_addresses[] = {
NULL
};
v8::TryCatch* ThreadLocalTop::TryCatchHandler() {
return TRY_CATCH_FROM_ADDRESS(try_catch_handler_address());
}
void ThreadLocalTop::Initialize() {
c_entry_fp_ = 0;
handler_ = 0;
#ifdef ENABLE_LOGGING_AND_PROFILING
js_entry_sp_ = 0;
#endif
stack_is_cooked_ = false;
try_catch_handler_address_ = NULL;
context_ = NULL;
int id = ThreadManager::CurrentId();
thread_id_ = (id == 0) ? ThreadManager::kInvalidId : id;
external_caught_exception_ = false;
failed_access_check_callback_ = NULL;
save_context_ = NULL;
catcher_ = NULL;
}
Address Top::get_address_from_id(Top::AddressId id) {
return top_addresses[id];
}
......@@ -70,9 +95,9 @@ void Top::Iterate(ObjectVisitor* v, ThreadLocalTop* thread) {
v->VisitPointer(bit_cast<Object**, Context**>(&(thread->context_)));
v->VisitPointer(&(thread->scheduled_exception_));
for (v8::TryCatch* block = thread->try_catch_handler_;
for (v8::TryCatch* block = thread->TryCatchHandler();
block != NULL;
block = block->next_) {
block = TRY_CATCH_FROM_ADDRESS(block->next_)) {
v->VisitPointer(bit_cast<Object**, void**>(&(block->exception_)));
v->VisitPointer(bit_cast<Object**, void**>(&(block->message_)));
}
......@@ -91,23 +116,10 @@ void Top::Iterate(ObjectVisitor* v) {
void Top::InitializeThreadLocal() {
thread_local_.c_entry_fp_ = 0;
thread_local_.handler_ = 0;
#ifdef ENABLE_LOGGING_AND_PROFILING
thread_local_.js_entry_sp_ = 0;
#endif
thread_local_.stack_is_cooked_ = false;
thread_local_.try_catch_handler_ = NULL;
thread_local_.context_ = NULL;
int id = ThreadManager::CurrentId();
thread_local_.thread_id_ = (id == 0) ? ThreadManager::kInvalidId : id;
thread_local_.external_caught_exception_ = false;
thread_local_.failed_access_check_callback_ = NULL;
thread_local_.Initialize();
clear_pending_exception();
clear_pending_message();
clear_scheduled_exception();
thread_local_.save_context_ = NULL;
thread_local_.catcher_ = NULL;
}
......@@ -254,46 +266,24 @@ void Top::TearDown() {
}
// There are cases where the C stack is separated from JS stack (ARM simulator).
// To figure out the order of top-most JS try-catch handler and the top-most C
// try-catch handler, the C try-catch handler keeps a reference to the top-most
// JS try_catch handler when it was created.
//
// Here is a picture to explain the idea:
// Top::thread_local_.handler_ Top::thread_local_.try_catch_handler_
//
// | |
// v v
//
// | JS handler | | C try_catch handler |
// | next |--+ +-------- | js_handler_ |
// | | | next_ |--+
// | | |
// | JS handler |--+ <---------+ |
// | next |
//
// If the top-most JS try-catch handler is not equal to
// Top::thread_local_.try_catch_handler_.js_handler_, it means the JS handler
// is on the top. Otherwise, it means the C try-catch handler is on the top.
//
void Top::RegisterTryCatchHandler(v8::TryCatch* that) {
StackHandler* handler =
reinterpret_cast<StackHandler*>(thread_local_.handler_);
// Find the top-most try-catch handler.
while (handler != NULL && !handler->is_try_catch()) {
handler = handler->next();
}
that->js_handler_ = handler; // casted to void*
thread_local_.try_catch_handler_ = that;
// The ARM simulator has a separate JS stack. We therefore register
// the C++ try catch handler with the simulator and get back an
// address that can be used for comparisons with addresses into the
// JS stack. When running without the simulator, the address
// returned will be the address of the C++ try catch handler itself.
Address address = reinterpret_cast<Address>(
SimulatorStack::RegisterCTryCatch(reinterpret_cast<uintptr_t>(that)));
thread_local_.set_try_catch_handler_address(address);
}
void Top::UnregisterTryCatchHandler(v8::TryCatch* that) {
ASSERT(thread_local_.try_catch_handler_ == that);
thread_local_.try_catch_handler_ = that->next_;
ASSERT(thread_local_.TryCatchHandler() == that);
thread_local_.set_try_catch_handler_address(
reinterpret_cast<Address>(that->next_));
thread_local_.catcher_ = NULL;
SimulatorStack::UnregisterCTryCatch();
}
......@@ -725,20 +715,18 @@ bool Top::ShouldReturnException(bool* is_caught_externally,
// Get the address of the external handler so we can compare the address to
// determine which one is closer to the top of the stack.
v8::TryCatch* try_catch = thread_local_.try_catch_handler_;
Address external_handler_address = thread_local_.try_catch_handler_address();
// The exception has been externally caught if and only if there is
// an external handler which is on top of the top-most try-catch
// handler.
//
// See comments in RegisterTryCatchHandler for details.
*is_caught_externally = try_catch != NULL &&
(handler == NULL || handler == try_catch->js_handler_ ||
*is_caught_externally = external_handler_address != NULL &&
(handler == NULL || handler->address() > external_handler_address ||
!catchable_by_javascript);
if (*is_caught_externally) {
// Only report the exception if the external handler is verbose.
return thread_local_.try_catch_handler_->is_verbose_;
return thread_local_.TryCatchHandler()->is_verbose_;
} else {
// Report the exception if it isn't caught by JavaScript code.
return handler == NULL;
......@@ -775,7 +763,7 @@ void Top::DoThrow(Object* exception,
MessageLocation potential_computed_location;
bool try_catch_needs_message =
is_caught_externally &&
thread_local_.try_catch_handler_->capture_message_;
thread_local_.TryCatchHandler()->capture_message_;
if (report_exception || try_catch_needs_message) {
if (location == NULL) {
// If no location was specified we use a computed one instead
......@@ -806,7 +794,7 @@ void Top::DoThrow(Object* exception,
}
if (is_caught_externally) {
thread_local_.catcher_ = thread_local_.try_catch_handler_;
thread_local_.catcher_ = thread_local_.TryCatchHandler();
}
// NOTE: Notifying the debugger or generating the message
......@@ -830,15 +818,15 @@ void Top::ReportPendingMessages() {
} else if (thread_local_.pending_exception_ ==
Heap::termination_exception()) {
if (external_caught) {
thread_local_.try_catch_handler_->can_continue_ = false;
thread_local_.try_catch_handler_->exception_ = Heap::null_value();
thread_local_.TryCatchHandler()->can_continue_ = false;
thread_local_.TryCatchHandler()->exception_ = Heap::null_value();
}
} else {
Handle<Object> exception(pending_exception());
thread_local_.external_caught_exception_ = false;
if (external_caught) {
thread_local_.try_catch_handler_->can_continue_ = true;
thread_local_.try_catch_handler_->exception_ =
thread_local_.TryCatchHandler()->can_continue_ = true;
thread_local_.TryCatchHandler()->exception_ =
thread_local_.pending_exception_;
if (!thread_local_.pending_message_obj_->IsTheHole()) {
try_catch_handler()->message_ = thread_local_.pending_message_obj_;
......@@ -892,9 +880,9 @@ bool Top::OptionalRescheduleException(bool is_bottom_call) {
// If the exception is externally caught, clear it if there are no
// JavaScript frames on the way to the C++ frame that has the
// external handler.
ASSERT(thread_local_.try_catch_handler_ != NULL);
ASSERT(thread_local_.try_catch_handler_address() != NULL);
Address external_handler_address =
reinterpret_cast<Address>(thread_local_.try_catch_handler_);
thread_local_.try_catch_handler_address();
JavaScriptFrameIterator it;
if (it.done() || (it.frame()->sp() > external_handler_address)) {
clear_exception = true;
......
......@@ -43,6 +43,41 @@ class SaveContext; // Forward declaration.
class ThreadLocalTop BASE_EMBEDDED {
public:
// Initialize the thread data.
void Initialize();
// Get the top C++ try catch handler or NULL if none are registered.
//
// This method is not guarenteed to return an address that can be
// used for comparison with addresses into the JS stack. If such an
// address is needed, use try_catch_handler_address.
v8::TryCatch* TryCatchHandler();
// Get the address of the top C++ try catch handler or NULL if
// none are registered.
//
// This method always returns an address that can be compared to
// pointers into the JavaScript stack. When running on actual
// hardware, try_catch_handler_address and TryCatchHandler return
// the same pointer. When running on a simulator with a separate JS
// stack, try_catch_handler_address returns a JS stack address that
// corresponds to the place on the JS stack where the C++ handler
// would have been if the stack were not separate.
inline Address try_catch_handler_address() {
return try_catch_handler_address_;
}
// Set the address of the top C++ try catch handler.
inline void set_try_catch_handler_address(Address address) {
try_catch_handler_address_ = address;
}
void Free() {
ASSERT(!has_pending_message_);
ASSERT(!external_caught_exception_);
ASSERT(try_catch_handler_address_ == NULL);
}
// The context where the current execution method is created and for variable
// lookups.
Context* context_;
......@@ -59,7 +94,6 @@ class ThreadLocalTop BASE_EMBEDDED {
// unify them later.
Object* scheduled_exception_;
bool external_caught_exception_;
v8::TryCatch* try_catch_handler_;
SaveContext* save_context_;
v8::TryCatch* catcher_;
......@@ -79,14 +113,11 @@ class ThreadLocalTop BASE_EMBEDDED {
// Call back function to report unsafe JS accesses.
v8::FailedAccessCheckCallback failed_access_check_callback_;
void Free() {
ASSERT(!has_pending_message_);
ASSERT(!external_caught_exception_);
ASSERT(try_catch_handler_ == NULL);
}
private:
Address try_catch_handler_address_;
};
#define TOP_ADDRESS_LIST(C) \
#define TOP_ADDRESS_LIST(C) \
C(handler_address) \
C(c_entry_fp_address) \
C(context_address) \
......@@ -157,7 +188,10 @@ class Top {
thread_local_.pending_message_script_ = NULL;
}
static v8::TryCatch* try_catch_handler() {
return thread_local_.try_catch_handler_;
return thread_local_.TryCatchHandler();
}
static Address try_catch_handler_address() {
return thread_local_.try_catch_handler_address();
}
// This method is called by the api after operations that may throw
// exceptions. If an exception was thrown and not handled by an external
......@@ -189,7 +223,7 @@ class Top {
thread_local_.external_caught_exception_ =
has_pending_exception() &&
(thread_local_.catcher_ != NULL) &&
(thread_local_.try_catch_handler_ == thread_local_.catcher_);
(try_catch_handler() == thread_local_.catcher_);
}
// Tells whether the current context has experienced an out of memory
......
......@@ -30,13 +30,10 @@
#include "bootstrapper.h"
#include "debug.h"
#include "serialize.h"
#include "simulator.h"
#include "stub-cache.h"
#include "oprofile-agent.h"
#if V8_TARGET_ARCH_ARM
#include "arm/simulator-arm.h"
#endif
namespace v8 {
namespace internal {
......
......@@ -44,6 +44,12 @@ class SimulatorStack : public v8::internal::AllStatic {
static inline uintptr_t JsLimitFromCLimit(uintptr_t c_limit) {
return c_limit;
}
static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
return try_catch_address;
}
static inline void UnregisterCTryCatch() { }
};
// Call the generated regexp code directly. The entry function pointer should
......@@ -51,4 +57,7 @@ class SimulatorStack : public v8::internal::AllStatic {
#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6) \
entry(p0, p1, p2, p3, p4, p5, p6)
#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
reinterpret_cast<TryCatch*>(try_catch_address)
#endif // V8_X64_SIMULATOR_X64_H_
......@@ -65,11 +65,3 @@ test-api/OutOfMemoryNested: SKIP
# BUG(355): Test crashes on ARM.
test-log/ProfLazyMode: SKIP
[ $simulator == arm ]
# BUG(271): During exception propagation, we compare pointers into the
# stack. These tests fail on the ARM simulator because the C++ and
# the JavaScript stacks are separate.
test-api/ExceptionOrder: FAIL
test-api/TryCatchInTryFinally: FAIL
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment