Index: src/runtime.cc |
diff --git a/src/runtime.cc b/src/runtime.cc |
index ecb923f02a89de76499b0d8c27ca837d20a76500..8e0c737a9c12f58c256661f78a2007b14f59740e 100644 |
--- a/src/runtime.cc |
+++ b/src/runtime.cc |
@@ -11182,27 +11182,99 @@ class ScopeIterator { |
inlined_frame_index_(inlined_frame_index), |
function_(JSFunction::cast(frame->function())), |
context_(Context::cast(frame->context())), |
- local_done_(false), |
- at_local_(false) { |
- |
- // 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. |
- // 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()-> |
- StackSlotIndex(isolate_->heap()->result_symbol()); |
- 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; |
+ nested_scope_chain_(4) { |
+ |
+ // Catch the case when the debugger stops in an internal function. |
+ Handle<SharedFunctionInfo> shared_info(function_->shared()); |
+ Handle<ScopeInfo> scope_info(shared_info->scope_info()); |
+ if (shared_info->script() == isolate->heap()->undefined_value()) { |
+ while (context_->closure() == *function_) { |
+ context_ = Handle<Context>(context_->previous(), isolate_); |
+ } |
+ return; |
+ } |
+ |
+ // Get the start of the frame exit code. |
+ Handle<Code> code(shared_info->code()); |
+ RelocIterator it(*code, RelocInfo::ModeMask(RelocInfo::JS_RETURN)); |
+ RelocInfo* info = it.rinfo(); |
+ Address frame_exit_code = info->pc(); |
+ it.next(); |
+ ASSERT(it.done()); |
+ |
+ Address frame_exit_return = |
+ frame_exit_code + Assembler::kCallInstructionLength; |
+ if (frame_->pc() == frame_exit_return) { |
+ // We are within the return sequence. At the momemt it is not possible to |
+ // get a source position which is consistent with the current scope chain. |
+ // Thus all nested with, catch and block contexts are skipped and we only |
+ // provide the function scope. |
+ if (scope_info->HasContext()) { |
+ context_ = Handle<Context>(context_->declaration_context(), isolate_); |
+ } else { |
+ while (context_->closure() == *function_) { |
+ context_ = Handle<Context>(context_->previous(), isolate_); |
+ } |
+ } |
+ if (scope_info->Type() != EVAL_SCOPE) nested_scope_chain_.Add(scope_info); |
+ } else { |
+ // Reparse the code and analyze the scopes. |
+ ZoneScope zone_scope(isolate, DELETE_ON_EXIT); |
+ Handle<Script> script(Script::cast(shared_info->script())); |
+ Scope* scope = NULL; |
+ |
+ // Check whether we are in global, eval or function code. |
+ Handle<ScopeInfo> scope_info(shared_info->scope_info()); |
+ if (scope_info->Type() != FUNCTION_SCOPE) { |
+ // Global or eval code. |
+ CompilationInfo info(script); |
+ ScriptDataImpl* pre_data = NULL; |
+ if (scope_info->Type() == GLOBAL_SCOPE) { |
+ int flags = kNoParsingFlags; |
+ if (info.is_native() || FLAG_allow_natives_syntax) { |
+ flags |= kAllowNativesSyntax; |
+ } |
+ if (!info.is_native() && FLAG_harmony_scoping) { |
+ flags |= kHarmonyScoping; |
+ } |
+ Handle<String> source(String::cast(script->source())); |
+ if (source->length() >= FLAG_min_preparse_length) { |
+ pre_data = ParserApi::PartialPreParse(source, NULL, flags); |
+ info.SetPreParseData(pre_data); |
+ } |
+ info.MarkAsGlobal(); |
+ } else { |
+ ASSERT(scope_info->Type() == EVAL_SCOPE); |
+ info.MarkAsEval(); |
+ info.SetCallingContext(Handle<Context>(function_->context())); |
+ } |
+ if (ParserApi::Parse(&info) && Scope::Analyze(&info)) { |
+ scope = info.function()->scope(); |
+ } |
+ // Delete preparse data again. |
+ if (pre_data != NULL) { |
+ delete pre_data; |
+ } |
+ } else { |
+ // Function code |
+ CompilationInfo info(shared_info); |
+ if (ParserApi::Parse(&info) && Scope::Analyze(&info)) { |
+ scope = info.function()->scope(); |
+ } |
+ } |
+ |
+ // Retrieve the scope chain for the current position. |
+ if (scope != NULL) { |
+ int source_position = shared_info->code()->SourcePosition(frame_->pc()); |
+ scope->GetNestedScopeChain(&nested_scope_chain_, source_position); |
+ } else { |
+ // A failed reparse indicates that the preparser has diverged from the |
+ // parser or that the preparse data given to the initial parse has been |
+ // faulty. We fail in debug mode but in release mode we only provide the |
+ // information we get from the context chain but nothing about |
+ // completely stack allocated scopes or stack allocated locals. |
+ UNREACHABLE(); |
+ } |
} |
} |
@@ -11211,40 +11283,49 @@ 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()) { |
+ ASSERT(context_->previous() != NULL); |
+ 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<ScopeInfo> 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()); |
@@ -11270,6 +11351,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. |
@@ -11286,13 +11368,28 @@ class ScopeIterator { |
return Handle<JSObject>(); |
} |
+ Handle<ScopeInfo> CurrentScopeInfo() { |
+ if (!nested_scope_chain_.is_empty()) { |
+ return nested_scope_chain_.last(); |
+ } else if (context_->IsBlockContext()) { |
+ return Handle<ScopeInfo>(ScopeInfo::cast(context_->extension())); |
+ } else if (context_->IsFunctionContext()) { |
+ return Handle<ScopeInfo>(context_->closure()->shared()->scope_info()); |
+ } |
+ return Handle<ScopeInfo>::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 |
@@ -11354,8 +11451,7 @@ class ScopeIterator { |
int inlined_frame_index_; |
Handle<JSFunction> function_; |
Handle<Context> context_; |
- bool local_done_; |
- bool at_local_; |
+ List<Handle<ScopeInfo> > nested_scope_chain_; |
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator); |
}; |
@@ -11818,45 +11914,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<ScopeInfo> > 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<ScopeInfo> scope_info(ScopeInfo::cast(current->extension())); |
- new_current = |
- isolate->factory()->NewBlockContext(function, new_previous, scope_info); |
- // Copy context slots. |
- int num_context_slots = scope_info->ContextLength(); |
- 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<ScopeInfo> 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); |
} |
@@ -11991,7 +12107,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) { |
if (scope_info->HasContext()) { |
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); |