| 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);
|
|
|