Index: src/runtime.cc |
diff --git a/src/runtime.cc b/src/runtime.cc |
index efb7aeeccdf6288740a073f39b82c1ee4d3aaaac..29fd54ffa6f460861c2ee29072bec12fd602d22f 100644 |
--- a/src/runtime.cc |
+++ b/src/runtime.cc |
@@ -11108,9 +11108,10 @@ static Handle<JSObject> MaterializeBlockScope( |
} |
-// Iterate over the actual scopes visible from a stack frame. All scopes are |
+// Iterate over the actual scopes visible from a stack frame. The iteration |
+// proceeds from the innermost visible nested scope outwards. All scopes are |
// backed by an actual context except the local scope, which is inserted |
-// "artifically" in the context chain. |
+// "artificially" in the context chain. |
class ScopeIterator { |
public: |
enum ScopeType { |
@@ -11130,28 +11131,52 @@ class ScopeIterator { |
inlined_frame_index_(inlined_frame_index), |
function_(JSFunction::cast(frame->function())), |
context_(Context::cast(frame->context())), |
- local_done_(false), |
- at_local_(false) { |
+ nested_scope_chain_(4) { |
- // Check whether the first scope is actually a local scope. |
- // If there is a stack slot for .result then this local scope has been |
- // created for evaluating top level code and it is not a real local scope. |
+ // Catch the case when the debugger stops in an internal function. |
+ Handle<SharedFunctionInfo> shared_info(function_->shared()); |
+ if (shared_info->script() == isolate->heap()->undefined_value()) { |
+ while (context_->closure() == *function_) { |
+ context_ = Handle<Context>(context_->previous(), isolate_); |
+ } |
+ return; |
+ } |
+ |
+ // Check whether we are in global code or function code. If there is a stack |
+ // slot for .result then this function has been created for evaluating |
+ // global code and it is not a real function. |
// Checking for the existence of .result seems fragile, but the scope info |
// saved with the code object does not otherwise have that information. |
- int index = function_->shared()->scope_info()-> |
+ int index = shared_info->scope_info()-> |
StackSlotIndex(isolate_->heap()->result_symbol()); |
+ |
+ // Reparse the code and analyze the scopes. |
+ ZoneScope zone_scope(isolate, DELETE_ON_EXIT); |
+ Handle<Script> script(Script::cast(shared_info->script())); |
+ Scope* scope; |
if (index >= 0) { |
- local_done_ = true; |
- } else if (context_->IsGlobalContext() || |
- context_->IsFunctionContext()) { |
- at_local_ = true; |
- } else if (context_->closure() != *function_) { |
- // The context_ is a block or with or catch block from the outer function. |
- ASSERT(context_->IsWithContext() || |
- context_->IsCatchContext() || |
- context_->IsBlockContext()); |
- at_local_ = true; |
+ // Global code |
+ CompilationInfo info(script); |
+ info.MarkAsGlobal(); |
+ bool result = ParserApi::Parse(&info); |
+ ASSERT(result); |
+ result = Scope::Analyze(&info); |
+ ASSERT(result); |
+ scope = info.function()->scope(); |
+ } else { |
+ // Function code |
+ CompilationInfo info(shared_info); |
+ bool result = ParserApi::Parse(&info); |
+ ASSERT(result); |
+ result = Scope::Analyze(&info); |
+ ASSERT(result); |
+ scope = info.function()->scope(); |
} |
+ |
+ // Retrieve the scope chain for the current position. |
+ int statement_position = |
+ shared_info->code()->SourceStatementPosition(frame_->pc()); |
+ scope->GetNestedScopeChain(&nested_scope_chain_, statement_position); |
} |
// More scopes? |
@@ -11159,40 +11184,48 @@ class ScopeIterator { |
// Move to the next scope. |
void Next() { |
- // If at a local scope mark the local scope as passed. |
- if (at_local_) { |
- at_local_ = false; |
- local_done_ = true; |
- |
- // If the current context is not associated with the local scope the |
- // current context is the next real scope, so don't move to the next |
- // context in this case. |
- if (context_->closure() != *function_) { |
- return; |
- } |
- } |
- |
- // The global scope is always the last in the chain. |
- if (context_->IsGlobalContext()) { |
+ ScopeType scope_type = Type(); |
+ if (scope_type == ScopeTypeGlobal) { |
+ // The global scope is always the last in the chain. |
+ ASSERT(context_->IsGlobalContext()); |
context_ = Handle<Context>(); |
return; |
} |
- |
- // Move to the next context. |
- context_ = Handle<Context>(context_->previous(), isolate_); |
- |
- // If passing the local scope indicate that the current scope is now the |
- // local scope. |
- if (!local_done_ && |
- (context_->IsGlobalContext() || context_->IsFunctionContext())) { |
- at_local_ = true; |
+ if (nested_scope_chain_.is_empty()) { |
+ context_ = Handle<Context>(context_->previous(), isolate_); |
+ } else { |
+ if (nested_scope_chain_.last()->HasContext()) { |
+ context_ = Handle<Context>(context_->previous(), isolate_); |
+ } |
+ nested_scope_chain_.RemoveLast(); |
} |
} |
// Return the type of the current scope. |
ScopeType Type() { |
- if (at_local_) { |
- return ScopeTypeLocal; |
+ if (!nested_scope_chain_.is_empty()) { |
+ Handle<SerializedScopeInfo> scope_info = nested_scope_chain_.last(); |
+ switch (scope_info->Type()) { |
+ case FUNCTION_SCOPE: |
+ ASSERT(context_->IsFunctionContext() || |
+ !scope_info->HasContext()); |
+ return ScopeTypeLocal; |
+ case GLOBAL_SCOPE: |
+ ASSERT(context_->IsGlobalContext()); |
+ return ScopeTypeGlobal; |
+ case WITH_SCOPE: |
+ ASSERT(context_->IsWithContext()); |
+ return ScopeTypeWith; |
+ case CATCH_SCOPE: |
+ ASSERT(context_->IsCatchContext()); |
+ return ScopeTypeCatch; |
+ case BLOCK_SCOPE: |
+ ASSERT(!scope_info->HasContext() || |
+ context_->IsBlockContext()); |
+ return ScopeTypeBlock; |
+ case EVAL_SCOPE: |
+ UNREACHABLE(); |
+ } |
} |
if (context_->IsGlobalContext()) { |
ASSERT(context_->global()->IsGlobalObject()); |
@@ -11218,6 +11251,7 @@ class ScopeIterator { |
return Handle<JSObject>(CurrentContext()->global()); |
case ScopeIterator::ScopeTypeLocal: |
// Materialize the content of the local scope into a JSObject. |
+ ASSERT(nested_scope_chain_.length() == 1); |
return MaterializeLocalScope(isolate_, frame_, inlined_frame_index_); |
case ScopeIterator::ScopeTypeWith: |
// Return the with object. |
@@ -11234,13 +11268,30 @@ class ScopeIterator { |
return Handle<JSObject>(); |
} |
+ Handle<SerializedScopeInfo> CurrentScopeInfo() { |
+ if (!nested_scope_chain_.is_empty()) { |
+ return nested_scope_chain_.last(); |
+ } else if (context_->IsBlockContext()) { |
+ return Handle<SerializedScopeInfo>( |
+ SerializedScopeInfo::cast(context_->extension())); |
+ } else if (context_->IsFunctionContext()) { |
+ return Handle<SerializedScopeInfo>( |
+ context_->closure()->shared()->scope_info()); |
+ } |
+ return Handle<SerializedScopeInfo>::null(); |
+ } |
+ |
// Return the context for this scope. For the local context there might not |
// be an actual context. |
Handle<Context> CurrentContext() { |
- if (at_local_ && context_->closure() != *function_) { |
+ if (Type() == ScopeTypeGlobal || |
+ nested_scope_chain_.is_empty()) { |
+ return context_; |
+ } else if (nested_scope_chain_.last()->HasContext()) { |
+ return context_; |
+ } else { |
return Handle<Context>(); |
} |
- return context_; |
} |
#ifdef DEBUG |
@@ -11303,8 +11354,7 @@ class ScopeIterator { |
int inlined_frame_index_; |
Handle<JSFunction> function_; |
Handle<Context> context_; |
- bool local_done_; |
- bool at_local_; |
+ List<Handle<SerializedScopeInfo> > nested_scope_chain_; |
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator); |
}; |
@@ -11767,46 +11817,65 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ClearStepping) { |
// Creates a copy of the with context chain. The copy of the context chain is |
// is linked to the function context supplied. |
-static Handle<Context> CopyWithContextChain(Isolate* isolate, |
- Handle<JSFunction> function, |
- Handle<Context> current, |
- Handle<Context> base) { |
- // At the end of the chain. Return the base context to link to. |
- if (current->IsFunctionContext() || current->IsGlobalContext()) { |
- return base; |
+static Handle<Context> CopyNestedScopeContextChain(Isolate* isolate, |
+ Handle<JSFunction> function, |
+ Handle<Context> base, |
+ JavaScriptFrame* frame, |
+ int inlined_frame_index) { |
+ HandleScope scope(isolate); |
+ List<Handle<SerializedScopeInfo> > scope_chain; |
+ List<Handle<Context> > context_chain; |
+ |
+ ScopeIterator it(isolate, frame, inlined_frame_index); |
+ for (; it.Type() != ScopeIterator::ScopeTypeGlobal && |
+ it.Type() != ScopeIterator::ScopeTypeLocal ; it.Next()) { |
+ ASSERT(!it.Done()); |
+ scope_chain.Add(it.CurrentScopeInfo()); |
+ context_chain.Add(it.CurrentContext()); |
} |
- // Recursively copy the with and catch contexts. |
- HandleScope scope(isolate); |
- Handle<Context> previous(current->previous()); |
- Handle<Context> new_previous = |
- CopyWithContextChain(isolate, function, previous, base); |
- Handle<Context> new_current; |
- if (current->IsCatchContext()) { |
- Handle<String> name(String::cast(current->extension())); |
- Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX)); |
- new_current = |
- isolate->factory()->NewCatchContext(function, |
- new_previous, |
- name, |
- thrown_object); |
- } else if (current->IsBlockContext()) { |
- Handle<SerializedScopeInfo> scope_info( |
- SerializedScopeInfo::cast(current->extension())); |
- new_current = |
- isolate->factory()->NewBlockContext(function, new_previous, scope_info); |
- // Copy context slots. |
- int num_context_slots = scope_info->NumberOfContextSlots(); |
- for (int i = Context::MIN_CONTEXT_SLOTS; i < num_context_slots; ++i) { |
- new_current->set(i, current->get(i)); |
+ // At the end of the chain. Return the base context to link to. |
+ Handle<Context> context = base; |
+ |
+ // Iteratively copy and or materialize the nested contexts. |
+ while (!scope_chain.is_empty()) { |
+ Handle<SerializedScopeInfo> scope_info = scope_chain.RemoveLast(); |
+ Handle<Context> current = context_chain.RemoveLast(); |
+ ASSERT(!(scope_info->HasContext() & current.is_null())); |
+ |
+ if (scope_info->Type() == CATCH_SCOPE) { |
+ Handle<String> name(String::cast(current->extension())); |
+ Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX)); |
+ context = |
+ isolate->factory()->NewCatchContext(function, |
+ context, |
+ name, |
+ thrown_object); |
+ } else if (scope_info->Type() == BLOCK_SCOPE) { |
+ // Materialize the contents of the block scope into a JSObject. |
+ Handle<JSObject> block_scope_object = |
+ MaterializeBlockScope(isolate, current); |
+ if (block_scope_object.is_null()) { |
+ return Handle<Context>::null(); |
+ } |
+ // Allocate a new function context for the debug evaluation and set the |
+ // extension object. |
+ Handle<Context> new_context = |
+ isolate->factory()->NewFunctionContext(Context::MIN_CONTEXT_SLOTS, |
+ function); |
+ new_context->set_extension(*block_scope_object); |
+ new_context->set_previous(*context); |
+ context = new_context; |
+ } else { |
+ ASSERT(scope_info->Type() == WITH_SCOPE); |
+ ASSERT(current->IsWithContext()); |
+ Handle<JSObject> extension(JSObject::cast(current->extension())); |
+ context = |
+ isolate->factory()->NewWithContext(function, context, extension); |
} |
- } else { |
- ASSERT(current->IsWithContext()); |
- Handle<JSObject> extension(JSObject::cast(current->extension())); |
- new_current = |
- isolate->factory()->NewWithContext(function, new_previous, extension); |
} |
- return scope.CloseAndEscape(new_current); |
+ |
+ return scope.CloseAndEscape(context); |
} |
@@ -11944,7 +12013,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) { |
if (scope_info->HasHeapAllocatedLocals()) { |
function_context = Handle<Context>(frame_context->declaration_context()); |
} |
- context = CopyWithContextChain(isolate, go_between, frame_context, context); |
+ context = CopyNestedScopeContextChain(isolate, |
+ go_between, |
+ context, |
+ frame, |
+ inlined_frame_index); |
if (additional_context->IsJSObject()) { |
Handle<JSObject> extension = Handle<JSObject>::cast(additional_context); |