diff options
Diffstat (limited to 'deps/v8/src/debug/debug-scopes.cc')
-rw-r--r-- | deps/v8/src/debug/debug-scopes.cc | 267 |
1 files changed, 195 insertions, 72 deletions
diff --git a/deps/v8/src/debug/debug-scopes.cc b/deps/v8/src/debug/debug-scopes.cc index 4569780d00..512b85fb09 100644 --- a/deps/v8/src/debug/debug-scopes.cc +++ b/deps/v8/src/debug/debug-scopes.cc @@ -84,6 +84,117 @@ void ScopeIterator::Restart() { UnwrapEvaluationContext(); } +namespace { + +// Takes the scope of a parsed script, a function and a break location +// inside the function. The result is the innermost lexical scope around +// the break point, which serves as the starting point of the ScopeIterator. +// And the scope of the function that was passed in (called closure scope). +// +// The start scope is guaranteed to be either the closure scope itself, +// or a child of the closure scope. +class ScopeChainRetriever { + public: + ScopeChainRetriever(DeclarationScope* scope, Handle<JSFunction> function, + int position) + : scope_(scope), + break_scope_start_(function->shared().StartPosition()), + break_scope_end_(function->shared().EndPosition()), + is_default_constructor_( + IsDefaultConstructor(function->shared().kind())), + position_(position) { + DCHECK_NOT_NULL(scope); + RetrieveScopes(); + } + + DeclarationScope* ClosureScope() { return closure_scope_; } + Scope* StartScope() { return start_scope_; } + + private: + DeclarationScope* scope_; + const int break_scope_start_; + const int break_scope_end_; + const bool is_default_constructor_; + const int position_; + + DeclarationScope* closure_scope_ = nullptr; + Scope* start_scope_ = nullptr; + + void RetrieveScopes() { + if (is_default_constructor_) { + // Even though the DefaultBaseConstructor is a child of a Class scope, the + // source positions are *not* nested. This means the actual scope for the + // DefaultBaseConstructor needs to be found by doing a DFS. + RetrieveScopeChainDefaultConstructor(scope_); + } else { + RetrieveScopeChain(); + } + DCHECK_NOT_NULL(closure_scope_); + DCHECK_NOT_NULL(start_scope_); + } + + bool RetrieveScopeChainDefaultConstructor(Scope* scope) { + const int beg_pos = scope->start_position(); + const int end_pos = scope->end_position(); + if (beg_pos == position_ && end_pos == position_) { + DCHECK(scope->is_function_scope()); + DCHECK( + IsDefaultConstructor(scope->AsDeclarationScope()->function_kind())); + start_scope_ = scope; + closure_scope_ = scope->AsDeclarationScope(); + return true; + } + + for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr; + inner_scope = inner_scope->sibling()) { + if (RetrieveScopeChainDefaultConstructor(inner_scope)) return true; + } + return false; + } + + void RetrieveScopeChain() { + Scope* parent = nullptr; + Scope* current = scope_; + SetClosureScopeIfFound(current); + + while (parent != current) { + parent = current; + for (Scope* inner_scope = current->inner_scope(); inner_scope != nullptr; + inner_scope = inner_scope->sibling()) { + if (SetClosureScopeIfFound(inner_scope) || + ContainsPosition(inner_scope)) { + current = inner_scope; + break; + } + } + } + start_scope_ = current; + } + + bool SetClosureScopeIfFound(Scope* scope) { + const int start = scope->start_position(); + const int end = scope->end_position(); + if (start == break_scope_start_ && end == break_scope_end_) { + closure_scope_ = scope->AsDeclarationScope(); + return true; + } + return false; + } + + bool ContainsPosition(Scope* scope) { + const int start = scope->start_position(); + const int end = scope->end_position(); + // In case the closure_scope_ hasn't been found yet, we are less strict + // about recursing downwards. This might be the case for nested arrow + // functions that have the same end position. + const bool position_fits_end = + closure_scope_ ? position_ < end : position_ <= end; + return start < position_ && position_fits_end; + } +}; + +} // namespace + void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) { // Catch the case when the debugger stops in an internal function. Handle<SharedFunctionInfo> shared_info(function_->shared(), isolate_); @@ -105,7 +216,6 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) { return; } - DCHECK_NE(IGNORE_NESTED_SCOPES, option); bool ignore_nested_scopes = false; if (shared_info->HasBreakInfo() && frame_inspector_ != nullptr) { // The source position at return is always the end of the function, @@ -123,44 +233,39 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) { } // Reparse the code and analyze the scopes. - // Check whether we are in global, eval or function code. - if (scope_info->scope_type() == FUNCTION_SCOPE) { - // Inner function. - info_ = new ParseInfo(isolate_, shared_info); - } else { - // Global or eval code. - Handle<Script> script(Script::cast(shared_info->script()), isolate_); - info_ = new ParseInfo(isolate_, script); - if (scope_info->scope_type() == EVAL_SCOPE) { - info_->set_eval(); - if (!context_->IsNativeContext()) { - info_->set_outer_scope_info(handle(context_->scope_info(), isolate_)); - } - // Language mode may be inherited from the eval caller. - // Retrieve it from shared function info. - info_->set_language_mode(shared_info->language_mode()); - } else if (scope_info->scope_type() == MODULE_SCOPE) { - DCHECK(info_->is_module()); - } else { - DCHECK_EQ(SCRIPT_SCOPE, scope_info->scope_type()); + Handle<Script> script(Script::cast(shared_info->script()), isolate_); + info_ = new ParseInfo(isolate_, script); + info_->set_eager(); + if (scope_info->scope_type() == EVAL_SCOPE || script->is_wrapped()) { + info_->set_eval(); + if (!context_->IsNativeContext()) { + info_->set_outer_scope_info(handle(context_->scope_info(), isolate_)); } + // Language mode may be inherited from the eval caller. + // Retrieve it from shared function info. + info_->set_language_mode(shared_info->language_mode()); + } else if (scope_info->scope_type() == MODULE_SCOPE) { + DCHECK(info_->is_module()); + } else { + DCHECK(scope_info->scope_type() == SCRIPT_SCOPE || + scope_info->scope_type() == FUNCTION_SCOPE); } if (parsing::ParseAny(info_, shared_info, isolate_) && Rewriter::Rewrite(info_)) { info_->ast_value_factory()->Internalize(isolate_); - closure_scope_ = info_->literal()->scope(); + DeclarationScope* literal_scope = info_->literal()->scope(); - if (option == COLLECT_NON_LOCALS) { - DCHECK(non_locals_.is_null()); - non_locals_ = info_->literal()->scope()->CollectNonLocals( - isolate_, info_, StringSet::New(isolate_)); - if (!closure_scope_->has_this_declaration() && - closure_scope_->HasThisReference()) { - non_locals_ = StringSet::Add(isolate_, non_locals_, - isolate_->factory()->this_string()); - } - } + ScopeChainRetriever scope_chain_retriever(literal_scope, function_, + GetSourcePosition()); + start_scope_ = scope_chain_retriever.StartScope(); + current_scope_ = start_scope_; + + // In case of a FUNCTION_SCOPE, the ScopeIterator expects + // {closure_scope_} to be set to the scope of the function. + closure_scope_ = scope_info->scope_type() == FUNCTION_SCOPE + ? scope_chain_retriever.ClosureScope() + : literal_scope; CHECK(DeclarationScope::Analyze(info_)); if (ignore_nested_scopes) { @@ -169,9 +274,8 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) { if (closure_scope_->NeedsContext()) { context_ = handle(context_->closure_context(), isolate_); } - } else { - RetrieveScopeChain(closure_scope_); } + UnwrapEvaluationContext(); } else { // A failed reparse indicates that the preparser has diverged from the @@ -260,6 +364,38 @@ bool ScopeIterator::HasContext() const { return !InInnerScope() || current_scope_->NeedsContext(); } +void ScopeIterator::AdvanceOneScope() { + if (current_scope_->NeedsContext()) { + DCHECK(!context_->previous().is_null()); + context_ = handle(context_->previous(), isolate_); + } + DCHECK(current_scope_->outer_scope() != nullptr); + current_scope_ = current_scope_->outer_scope(); +} + +void ScopeIterator::AdvanceToNonHiddenScope() { + do { + AdvanceOneScope(); + } while (current_scope_->is_hidden()); +} + +void ScopeIterator::AdvanceContext() { + DCHECK(!context_->IsNativeContext()); + context_ = handle(context_->previous(), isolate_); + + // While advancing one context, we need to advance at least one + // scope, but until we hit the next scope that actually requires + // a context. All the locals collected along the way build the + // blacklist for debug-evaluate for this context. + locals_ = StringSet::New(isolate_); + do { + if (!current_scope_ || !current_scope_->outer_scope()) break; + + current_scope_ = current_scope_->outer_scope(); + CollectLocalsFromCurrentScope(); + } while (!current_scope_->NeedsContext()); +} + void ScopeIterator::Next() { DCHECK(!Done()); @@ -283,19 +419,17 @@ void ScopeIterator::Next() { context_ = handle(context_->previous(), isolate_); } } else if (!inner) { - DCHECK(!context_->IsNativeContext()); - context_ = handle(context_->previous(), isolate_); + AdvanceContext(); } else { DCHECK_NOT_NULL(current_scope_); - do { - if (current_scope_->NeedsContext()) { - DCHECK(!context_->previous().is_null()); - context_ = handle(context_->previous(), isolate_); - } - DCHECK_IMPLIES(InInnerScope(), current_scope_->outer_scope() != nullptr); - current_scope_ = current_scope_->outer_scope(); - // Repeat to skip hidden scopes. - } while (current_scope_->is_hidden()); + AdvanceToNonHiddenScope(); + + if (!InInnerScope() && current_scope_ != closure_scope_) { + // Edge case when we just go past {closure_scope_}. This case + // already needs to start collecting locals for the blacklist. + locals_ = StringSet::New(isolate_); + CollectLocalsFromCurrentScope(); + } } UnwrapEvaluationContext(); @@ -453,7 +587,20 @@ bool ScopeIterator::SetVariableValue(Handle<String> name, return false; } -Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; } +bool ScopeIterator::ClosureScopeHasThisReference() const { + return !closure_scope_->has_this_declaration() && + closure_scope_->HasThisReference(); +} + +void ScopeIterator::CollectLocalsFromCurrentScope() { + DCHECK(locals_->IsStringSet()); + for (Variable* var : *current_scope_->locals()) { + if (var->location() == VariableLocation::PARAMETER || + var->location() == VariableLocation::LOCAL) { + locals_ = StringSet::Add(isolate_, locals_, var->name()); + } + } +} #ifdef DEBUG // Debug print of the content of the current scope. @@ -524,31 +671,6 @@ int ScopeIterator::GetSourcePosition() { } } -void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) { - DCHECK_NOT_NULL(scope); - - const int position = GetSourcePosition(); - - Scope* parent = nullptr; - Scope* current = scope; - while (parent != current) { - parent = current; - for (Scope* inner_scope = current->inner_scope(); inner_scope != nullptr; - inner_scope = inner_scope->sibling()) { - int beg_pos = inner_scope->start_position(); - int end_pos = inner_scope->end_position(); - DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden()); - if (beg_pos < position && position < end_pos) { - current = inner_scope; - break; - } - } - } - - start_scope_ = current; - current_scope_ = current; -} - void ScopeIterator::VisitScriptScope(const Visitor& visitor) const { Handle<JSGlobalObject> global(context_->global_object(), isolate_); Handle<ScriptContextTable> script_contexts( @@ -884,9 +1006,10 @@ bool ScopeIterator::SetContextVariableValue(Handle<String> variable_name, VariableMode mode; InitializationFlag flag; MaybeAssignedFlag maybe_assigned_flag; + IsStaticFlag is_static_flag; int slot_index = ScopeInfo::ContextSlotIndex(context_->scope_info(), *variable_name, &mode, - &flag, &maybe_assigned_flag); + &flag, &maybe_assigned_flag, &is_static_flag); if (slot_index < 0) return false; context_->set(slot_index, *new_value); |