From 8866d63cc943f153de55c704b0b36fd9c1adcb4e Mon Sep 17 00:00:00 2001 From: "keuchel@chromium.org" Date: Mon, 28 Nov 2011 12:47:39 +0000 Subject: [PATCH] The ScopeIterator uses recorded scope position - as detailed in scopes.h - and source code positions it gets from the program counter to recreate the scope chain by reparsing the function or program. This CL includes the following changes * Adds source code positions for the assignment added by the rewriter. * Run the preparser over global code first. * Use the ScopeType from the ScopeInfo to determine if the code being debugged is eval, function or global code instead of looking up the '.result' symbol. TEST=mjsunit/debug-stepout-scope.js Review URL: http://codereview.chromium.org/8590027 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10076 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ast.h | 1 + src/parser.cc | 12 +- src/parser.h | 2 +- src/rewriter.cc | 16 +- src/runtime.cc | 277 +++++++++++++++-------- src/scopes.cc | 4 +- test/cctest/test-parsing.cc | 8 +- test/mjsunit/debug-stepout-scope.js | 423 ++++++++++++++++++++++++++++++++++++ 8 files changed, 645 insertions(+), 98 deletions(-) create mode 100644 test/mjsunit/debug-stepout-scope.js diff --git a/src/ast.h b/src/ast.h index 628391c..94a20ad 100644 --- a/src/ast.h +++ b/src/ast.h @@ -1176,6 +1176,7 @@ class VariableProxy: public Expression { bool is_this, int position = RelocInfo::kNoPosition); + friend class Rewriter; friend class Scope; }; diff --git a/src/parser.cc b/src/parser.cc index b795891..efffe5a 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -5541,7 +5541,7 @@ static ScriptDataImpl* DoPreParse(UC16CharacterStream* source, // Preparse, but only collect data that is immediately useful, // even if the preparser data is only used once. -ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source, +ScriptDataImpl* ParserApi::PartialPreParse(Handle source, v8::Extension* extension, int flags) { bool allow_lazy = FLAG_lazy && (extension == NULL); @@ -5552,7 +5552,15 @@ ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source, } flags |= kAllowLazy; PartialParserRecorder recorder; - return DoPreParse(source, flags, &recorder); + int source_length = source->length(); + if (source->IsExternalTwoByteString()) { + ExternalTwoByteStringUC16CharacterStream stream( + Handle::cast(source), 0, source_length); + return DoPreParse(&stream, flags, &recorder); + } else { + GenericStringUC16CharacterStream stream(source, 0, source_length); + return DoPreParse(&stream, flags, &recorder); + } } diff --git a/src/parser.h b/src/parser.h index dc5882b..75f8e10 100644 --- a/src/parser.h +++ b/src/parser.h @@ -178,7 +178,7 @@ class ParserApi { // Preparser that only does preprocessing that makes sense if only used // immediately after. - static ScriptDataImpl* PartialPreParse(UC16CharacterStream* source, + static ScriptDataImpl* PartialPreParse(Handle source, v8::Extension* extension, int flags); }; diff --git a/src/rewriter.cc b/src/rewriter.cc index 3d4c2dc..a70cd82 100644 --- a/src/rewriter.cc +++ b/src/rewriter.cc @@ -236,10 +236,22 @@ bool Rewriter::Rewrite(CompilationInfo* info) { if (processor.HasStackOverflow()) return false; if (processor.result_assigned()) { + ASSERT(function->end_position() != RelocInfo::kNoPosition); Isolate* isolate = info->isolate(); Zone* zone = isolate->zone(); - VariableProxy* result_proxy = new(zone) VariableProxy(isolate, result); - body->Add(new(zone) ReturnStatement(result_proxy)); + // Set the position of the assignment statement one character past the + // source code, such that it definitely is not in the source code range + // of an immediate inner scope. For example in + // eval('with ({x:1}) x = 1'); + // the end position of the function generated for executing the eval code + // coincides with the end of the with scope which is the position of '1'. + int position = function->end_position(); + VariableProxy* result_proxy = new(zone) VariableProxy( + isolate, result->name(), false, position); + result_proxy->BindTo(result); + Statement* result_statement = new(zone) ReturnStatement(result_proxy); + result_statement->set_statement_pos(position); + body->Add(result_statement); } } diff --git a/src/runtime.cc b/src/runtime.cc index 43e1a6d..fe8e137 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -11201,27 +11201,82 @@ 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 shared_info(function_->shared()); + Handle scope_info(shared_info->scope_info()); + if (shared_info->script() == isolate->heap()->undefined_value()) { + while (context_->closure() == *function_) { + context_ = Handle(context_->previous(), isolate_); + } + return; + } + + // Get the start of the frame exit code. + Handle 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_->declaration_context(), isolate_); + } else { + while (context_->closure() == *function_) { + context_ = Handle(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