Commit 75d939ac authored by wingo@igalia.com's avatar wingo@igalia.com

Generators save and restore stack handlers

This CL adds machinery to unwind stack handlers from the stack and store
them into a generator's operand array.  It also includes routines to
reinstate them.  Together this allows generators to yield within
try/catch and try/finally blocks.

BUG=v8:2355
R=mstarzinger@chromium.org
TEST=mjsunit/harmony/generators-iteration

Review URL: https://codereview.chromium.org/14031028

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14586 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent d4fd9db3
......@@ -98,6 +98,12 @@ inline StackHandler::Kind StackHandler::kind() const {
}
inline unsigned StackHandler::index() const {
const int offset = StackHandlerConstants::kStateOffset;
return IndexField::decode(Memory::unsigned_at(address() + offset));
}
inline Object** StackHandler::context_address() const {
const int offset = StackHandlerConstants::kContextOffset;
return reinterpret_cast<Object**>(address() + offset);
......@@ -216,8 +222,9 @@ Object* JavaScriptFrame::GetParameter(int index) const {
inline Address JavaScriptFrame::GetOperandSlot(int index) const {
Address base = fp() + JavaScriptFrameConstants::kLocal0Offset;
ASSERT(IsAddressAligned(base, kPointerSize));
ASSERT(type() == JAVA_SCRIPT);
ASSERT(index < ComputeOperandsCount());
ASSERT_EQ(type(), JAVA_SCRIPT);
ASSERT_LT(index, ComputeOperandsCount());
ASSERT_LE(0, index);
// Operand stack grows down.
return base - index * kPointerSize;
}
......
......@@ -840,6 +840,72 @@ void JavaScriptFrame::PrintTop(Isolate* isolate,
}
void JavaScriptFrame::SaveOperandStack(FixedArray* store,
int* stack_handler_index) const {
int operands_count = store->length();
ASSERT_LE(operands_count, ComputeOperandsCount());
// Visit the stack in LIFO order, saving operands and stack handlers into the
// array. The saved stack handlers store a link to the next stack handler,
// which will allow RestoreOperandStack to rewind the handlers.
StackHandlerIterator it(this, top_handler());
int i = operands_count - 1;
*stack_handler_index = -1;
for (; !it.done(); it.Advance()) {
StackHandler* handler = it.handler();
// Save operands pushed after the handler was pushed.
for (; GetOperandSlot(i) < handler->address(); i--) {
store->set(i, GetOperand(i));
}
ASSERT_GE(i + 1, StackHandlerConstants::kSlotCount);
ASSERT_EQ(handler->address(), GetOperandSlot(i));
int next_stack_handler_index = i + 1 - StackHandlerConstants::kSlotCount;
handler->Unwind(isolate(), store, next_stack_handler_index,
*stack_handler_index);
*stack_handler_index = next_stack_handler_index;
i -= StackHandlerConstants::kSlotCount;
}
// Save any remaining operands.
for (; i >= 0; i--) {
store->set(i, GetOperand(i));
}
}
void JavaScriptFrame::RestoreOperandStack(FixedArray* store,
int stack_handler_index) {
int operands_count = store->length();
ASSERT_LE(operands_count, ComputeOperandsCount());
int i = 0;
while (i <= stack_handler_index) {
if (i < stack_handler_index) {
// An operand.
ASSERT_EQ(GetOperand(i), isolate()->heap()->the_hole_value());
Memory::Object_at(GetOperandSlot(i)) = store->get(i);
i++;
} else {
// A stack handler.
ASSERT_EQ(i, stack_handler_index);
// The FixedArray store grows up. The stack grows down. So the operand
// slot for i actually points to the bottom of the top word in the
// handler. The base of the StackHandler* is the address of the bottom
// word, which will be the last slot that is in the handler.
int handler_slot_index = i + StackHandlerConstants::kSlotCount - 1;
StackHandler *handler =
StackHandler::FromAddress(GetOperandSlot(handler_slot_index));
stack_handler_index = handler->Rewind(isolate(), store, i, fp());
i += StackHandlerConstants::kSlotCount;
}
}
for (; i < operands_count; i++) {
ASSERT_EQ(GetOperand(i), isolate()->heap()->the_hole_value());
Memory::Object_at(GetOperandSlot(i)) = store->get(i);
}
}
void FrameSummary::Print() {
PrintF("receiver: ");
receiver_->ShortPrint();
......@@ -1436,6 +1502,60 @@ InnerPointerToCodeCache::InnerPointerToCodeCacheEntry*
}
// -------------------------------------------------------------------------
void StackHandler::Unwind(Isolate* isolate,
FixedArray* array,
int offset,
int previous_handler_offset) const {
STATIC_ASSERT(StackHandlerConstants::kSlotCount == 5);
ASSERT_LE(0, offset);
ASSERT_GE(array->length(), offset + 5);
// Unwinding a stack handler into an array chains it in the opposite
// direction, re-using the "next" slot as a "previous" link, so that stack
// handlers can be later re-wound in the correct order. Decode the "state"
// slot into "index" and "kind" and store them separately, using the fp slot.
array->set(offset, Smi::FromInt(previous_handler_offset)); // next
array->set(offset + 1, *code_address()); // code
array->set(offset + 2, Smi::FromInt(static_cast<int>(index()))); // state
array->set(offset + 3, *context_address()); // context
array->set(offset + 4, Smi::FromInt(static_cast<int>(kind()))); // fp
*isolate->handler_address() = next()->address();
}
int StackHandler::Rewind(Isolate* isolate,
FixedArray* array,
int offset,
Address fp) {
STATIC_ASSERT(StackHandlerConstants::kSlotCount == 5);
ASSERT_LE(0, offset);
ASSERT_GE(array->length(), offset + 5);
Smi* prev_handler_offset = Smi::cast(array->get(offset));
Code* code = Code::cast(array->get(offset + 1));
Smi* smi_index = Smi::cast(array->get(offset + 2));
Object* context = array->get(offset + 3);
Smi* smi_kind = Smi::cast(array->get(offset + 4));
unsigned state = KindField::encode(static_cast<Kind>(smi_kind->value())) |
IndexField::encode(static_cast<unsigned>(smi_index->value()));
Memory::Address_at(address() + StackHandlerConstants::kNextOffset) =
*isolate->handler_address();
Memory::Object_at(address() + StackHandlerConstants::kCodeOffset) = code;
Memory::uintptr_at(address() + StackHandlerConstants::kStateOffset) = state;
Memory::Object_at(address() + StackHandlerConstants::kContextOffset) =
context;
Memory::Address_at(address() + StackHandlerConstants::kFPOffset) = fp;
*isolate->handler_address() = address();
return prev_handler_offset->value();
}
// -------------------------------------------------------------------------
int NumRegs(RegList reglist) {
......
......@@ -93,6 +93,7 @@ class StackHandlerConstants : public AllStatic {
static const int kFPOffset = 4 * kPointerSize;
static const int kSize = kFPOffset + kPointerSize;
static const int kSlotCount = kSize >> kPointerSizeLog2;
};
......@@ -131,9 +132,15 @@ class StackHandler BASE_EMBEDDED {
inline bool is_catch() const;
inline bool is_finally() const;
// Generator support to preserve stack handlers.
void Unwind(Isolate* isolate, FixedArray* array, int offset,
int previous_handler_offset) const;
int Rewind(Isolate* isolate, FixedArray* array, int offset, Address fp);
private:
// Accessors.
inline Kind kind() const;
inline unsigned index() const;
inline Object** context_address() const;
inline Object** code_address() const;
......@@ -541,6 +548,10 @@ class JavaScriptFrame: public StandardFrame {
inline Object* GetOperand(int index) const;
inline int ComputeOperandsCount() const;
// Generator support to preserve operand stack and stack handlers.
void SaveOperandStack(FixedArray* store, int* stack_handler_index) const;
void RestoreOperandStack(FixedArray* store, int stack_handler_index);
// Debugger access.
void SetParameterValue(int index, Object* value) const;
......
......@@ -415,6 +415,7 @@ void JSGeneratorObject::JSGeneratorObjectVerify() {
VerifyObjectField(kReceiverOffset);
VerifyObjectField(kOperandStackOffset);
VerifyObjectField(kContinuationOffset);
VerifyObjectField(kStackHandlerIndexOffset);
}
......
......@@ -5108,6 +5108,7 @@ ACCESSORS(JSGeneratorObject, context, Context, kContextOffset)
ACCESSORS(JSGeneratorObject, receiver, Object, kReceiverOffset)
SMI_ACCESSORS(JSGeneratorObject, continuation, kContinuationOffset)
ACCESSORS(JSGeneratorObject, operand_stack, FixedArray, kOperandStackOffset)
SMI_ACCESSORS(JSGeneratorObject, stack_handler_index, kStackHandlerIndexOffset)
JSGeneratorObject* JSGeneratorObject::cast(Object* obj) {
......
......@@ -6410,9 +6410,14 @@ class JSGeneratorObject: public JSObject {
inline int continuation();
inline void set_continuation(int continuation);
// [operands]: Saved operand stack.
// [operand_stack]: Saved operand stack.
DECL_ACCESSORS(operand_stack, FixedArray)
// [stack_handler_index]: Index of first stack handler in operand_stack, or -1
// if the captured activation had no stack handler.
inline int stack_handler_index();
inline void set_stack_handler_index(int stack_handler_index);
// Casting.
static inline JSGeneratorObject* cast(Object* obj);
......@@ -6430,7 +6435,9 @@ class JSGeneratorObject: public JSObject {
static const int kReceiverOffset = kContextOffset + kPointerSize;
static const int kContinuationOffset = kReceiverOffset + kPointerSize;
static const int kOperandStackOffset = kContinuationOffset + kPointerSize;
static const int kSize = kOperandStackOffset + kPointerSize;
static const int kStackHandlerIndexOffset =
kOperandStackOffset + kPointerSize;
static const int kSize = kStackHandlerIndexOffset + kPointerSize;
// Resume mode, for use by runtime functions.
enum ResumeMode { SEND, THROW };
......
......@@ -2577,6 +2577,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSGeneratorObject) {
generator->set_receiver(frame->receiver());
generator->set_continuation(0);
generator->set_operand_stack(isolate->heap()->empty_fixed_array());
generator->set_stack_handler_index(-1);
return generator;
}
......@@ -2603,23 +2604,18 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SuspendJSGeneratorObject) {
if (operands_count == 0) {
ASSERT_EQ(generator_object->operand_stack(),
isolate->heap()->empty_fixed_array());
ASSERT_EQ(generator_object->stack_handler_index(), -1);
// If there are no operands on the stack, there shouldn't be a handler
// active either.
ASSERT(!frame->HasHandler());
} else {
if (frame->HasHandler()) {
// TODO(wingo): Unwind the stack handlers.
UNIMPLEMENTED();
}
FixedArray* operand_stack;
int stack_handler_index = -1;
MaybeObject* alloc = isolate->heap()->AllocateFixedArray(operands_count);
FixedArray* operand_stack;
if (!alloc->To(&operand_stack)) return alloc;
for (int i = 0; i < operands_count; i++) {
operand_stack->set(i, frame->GetOperand(i));
}
frame->SaveOperandStack(operand_stack, &stack_handler_index);
generator_object->set_operand_stack(operand_stack);
generator_object->set_stack_handler_index(stack_handler_index);
}
// Set continuation down here to avoid side effects if the operand stack
......@@ -2669,14 +2665,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ResumeJSGeneratorObject) {
FixedArray* operand_stack = generator_object->operand_stack();
int operands_count = operand_stack->length();
if (operands_count != 0) {
// TODO(wingo): Rewind stack handlers. However until
// SuspendJSGeneratorObject unwinds them, we won't see frames with stack
// handlers here.
for (int i = 0; i < operands_count; i++) {
ASSERT_EQ(frame->GetOperand(i), isolate->heap()->the_hole_value());
Memory::Object_at(frame->GetOperandSlot(i)) = operand_stack->get(i);
}
frame->RestoreOperandStack(operand_stack,
generator_object->stack_handler_index());
generator_object->set_operand_stack(isolate->heap()->empty_fixed_array());
generator_object->set_stack_handler_index(-1);
}
JSGeneratorObject::ResumeMode resume_mode =
......
......@@ -64,6 +64,14 @@ class Memory {
return *reinterpret_cast<unsigned*>(addr);
}
static intptr_t& intptr_at(Address addr) {
return *reinterpret_cast<intptr_t*>(addr);
}
static uintptr_t& uintptr_at(Address addr) {
return *reinterpret_cast<uintptr_t*>(addr);
}
static double& double_at(Address addr) {
return *reinterpret_cast<double*>(addr);
}
......
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