| OLD | NEW |
| 1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/debug/debug-scopes.h" | 5 #include "src/debug/debug-scopes.h" |
| 6 | 6 |
| 7 #include "src/ast/scopes.h" | 7 #include "src/ast/scopes.h" |
| 8 #include "src/debug/debug.h" | 8 #include "src/debug/debug.h" |
| 9 #include "src/frames-inl.h" | 9 #include "src/frames-inl.h" |
| 10 #include "src/globals.h" | 10 #include "src/globals.h" |
| 11 #include "src/isolate-inl.h" | 11 #include "src/isolate-inl.h" |
| 12 #include "src/parsing/parser.h" | 12 #include "src/parsing/parser.h" |
| 13 | 13 |
| 14 namespace v8 { | 14 namespace v8 { |
| 15 namespace internal { | 15 namespace internal { |
| 16 | 16 |
| 17 ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector, | 17 ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector, |
| 18 bool ignore_nested_scopes) | 18 ScopeIterator::Option option) |
| 19 : isolate_(isolate), | 19 : isolate_(isolate), |
| 20 frame_inspector_(frame_inspector), | 20 frame_inspector_(frame_inspector), |
| 21 nested_scope_chain_(4), | 21 nested_scope_chain_(4), |
| 22 non_locals_(nullptr), |
| 22 seen_script_scope_(false), | 23 seen_script_scope_(false), |
| 23 failed_(false) { | 24 failed_(false) { |
| 24 if (!frame_inspector->GetContext()->IsContext() || | 25 if (!frame_inspector->GetContext()->IsContext() || |
| 25 !frame_inspector->GetFunction()->IsJSFunction()) { | 26 !frame_inspector->GetFunction()->IsJSFunction()) { |
| 26 // Optimized frame, context or function cannot be materialized. Give up. | 27 // Optimized frame, context or function cannot be materialized. Give up. |
| 27 return; | 28 return; |
| 28 } | 29 } |
| 29 | 30 |
| 30 context_ = Handle<Context>(Context::cast(frame_inspector->GetContext())); | 31 context_ = Handle<Context>(Context::cast(frame_inspector->GetContext())); |
| 31 | 32 |
| 32 // Catch the case when the debugger stops in an internal function. | 33 // Catch the case when the debugger stops in an internal function. |
| 33 Handle<JSFunction> function = GetFunction(); | 34 Handle<JSFunction> function = GetFunction(); |
| 34 Handle<SharedFunctionInfo> shared_info(function->shared()); | 35 Handle<SharedFunctionInfo> shared_info(function->shared()); |
| 35 Handle<ScopeInfo> scope_info(shared_info->scope_info()); | 36 Handle<ScopeInfo> scope_info(shared_info->scope_info()); |
| 36 if (shared_info->script() == isolate->heap()->undefined_value()) { | 37 if (shared_info->script() == isolate->heap()->undefined_value()) { |
| 37 while (context_->closure() == *function) { | 38 while (context_->closure() == *function) { |
| 38 context_ = Handle<Context>(context_->previous(), isolate_); | 39 context_ = Handle<Context>(context_->previous(), isolate_); |
| 39 } | 40 } |
| 40 return; | 41 return; |
| 41 } | 42 } |
| 42 | 43 |
| 43 // Currently it takes too much time to find nested scopes due to script | 44 // Currently it takes too much time to find nested scopes due to script |
| 44 // parsing. Sometimes we want to run the ScopeIterator as fast as possible | 45 // parsing. Sometimes we want to run the ScopeIterator as fast as possible |
| 45 // (for example, while collecting async call stacks on every | 46 // (for example, while collecting async call stacks on every |
| 46 // addEventListener call), even if we drop some nested scopes. | 47 // addEventListener call), even if we drop some nested scopes. |
| 47 // Later we may optimize getting the nested scopes (cache the result?) | 48 // Later we may optimize getting the nested scopes (cache the result?) |
| 48 // and include nested scopes into the "fast" iteration case as well. | 49 // and include nested scopes into the "fast" iteration case as well. |
| 49 | 50 bool ignore_nested_scopes = (option == IGNORE_NESTED_SCOPES); |
| 51 bool collect_non_locals = (option == COLLECT_NON_LOCALS); |
| 50 if (!ignore_nested_scopes && shared_info->HasDebugInfo()) { | 52 if (!ignore_nested_scopes && shared_info->HasDebugInfo()) { |
| 51 // The source position at return is always the end of the function, | 53 // The source position at return is always the end of the function, |
| 52 // which is not consistent with the current scope chain. Therefore all | 54 // which is not consistent with the current scope chain. Therefore all |
| 53 // nested with, catch and block contexts are skipped, and we can only | 55 // nested with, catch and block contexts are skipped, and we can only |
| 54 // inspect the function scope. | 56 // inspect the function scope. |
| 55 // This can only happen if we set a break point inside right before the | 57 // This can only happen if we set a break point inside right before the |
| 56 // return, which requires a debug info to be available. | 58 // return, which requires a debug info to be available. |
| 57 Handle<DebugInfo> debug_info(shared_info->GetDebugInfo()); | 59 Handle<DebugInfo> debug_info(shared_info->GetDebugInfo()); |
| 58 | 60 |
| 59 // PC points to the instruction after the current one, possibly a break | 61 // PC points to the instruction after the current one, possibly a break |
| (...skipping 11 matching lines...) Expand all Loading... |
| 71 if (scope_info->HasContext()) { | 73 if (scope_info->HasContext()) { |
| 72 context_ = Handle<Context>(context_->declaration_context(), isolate_); | 74 context_ = Handle<Context>(context_->declaration_context(), isolate_); |
| 73 } else { | 75 } else { |
| 74 while (context_->closure() == *function) { | 76 while (context_->closure() == *function) { |
| 75 context_ = Handle<Context>(context_->previous(), isolate_); | 77 context_ = Handle<Context>(context_->previous(), isolate_); |
| 76 } | 78 } |
| 77 } | 79 } |
| 78 if (scope_info->scope_type() == FUNCTION_SCOPE) { | 80 if (scope_info->scope_type() == FUNCTION_SCOPE) { |
| 79 nested_scope_chain_.Add(scope_info); | 81 nested_scope_chain_.Add(scope_info); |
| 80 } | 82 } |
| 83 if (!collect_non_locals) return; |
| 84 } |
| 85 |
| 86 // Reparse the code and analyze the scopes. |
| 87 Scope* scope = NULL; |
| 88 // Check whether we are in global, eval or function code. |
| 89 Zone zone; |
| 90 if (scope_info->scope_type() != FUNCTION_SCOPE) { |
| 91 // Global or eval code. |
| 92 Handle<Script> script(Script::cast(shared_info->script())); |
| 93 ParseInfo info(&zone, script); |
| 94 if (scope_info->scope_type() == SCRIPT_SCOPE) { |
| 95 info.set_global(); |
| 96 } else { |
| 97 DCHECK(scope_info->scope_type() == EVAL_SCOPE); |
| 98 info.set_eval(); |
| 99 info.set_context(Handle<Context>(function->context())); |
| 100 } |
| 101 if (Parser::ParseStatic(&info) && Scope::Analyze(&info)) { |
| 102 scope = info.literal()->scope(); |
| 103 } |
| 104 if (!ignore_nested_scopes) RetrieveScopeChain(scope); |
| 105 if (collect_non_locals) CollectNonLocals(scope); |
| 81 } else { | 106 } else { |
| 82 // Reparse the code and analyze the scopes. | 107 // Function code |
| 83 Handle<Script> script(Script::cast(shared_info->script())); | 108 ParseInfo info(&zone, function); |
| 84 Scope* scope = NULL; | 109 if (Parser::ParseStatic(&info) && Scope::Analyze(&info)) { |
| 85 | 110 scope = info.literal()->scope(); |
| 86 // Check whether we are in global, eval or function code. | |
| 87 Zone zone; | |
| 88 if (scope_info->scope_type() != FUNCTION_SCOPE) { | |
| 89 // Global or eval code. | |
| 90 ParseInfo info(&zone, script); | |
| 91 if (scope_info->scope_type() == SCRIPT_SCOPE) { | |
| 92 info.set_global(); | |
| 93 } else { | |
| 94 DCHECK(scope_info->scope_type() == EVAL_SCOPE); | |
| 95 info.set_eval(); | |
| 96 info.set_context(Handle<Context>(function->context())); | |
| 97 } | |
| 98 if (Parser::ParseStatic(&info) && Scope::Analyze(&info)) { | |
| 99 scope = info.literal()->scope(); | |
| 100 } | |
| 101 RetrieveScopeChain(scope, shared_info); | |
| 102 } else { | |
| 103 // Function code | |
| 104 ParseInfo info(&zone, function); | |
| 105 if (Parser::ParseStatic(&info) && Scope::Analyze(&info)) { | |
| 106 scope = info.literal()->scope(); | |
| 107 } | |
| 108 RetrieveScopeChain(scope, shared_info); | |
| 109 } | 111 } |
| 112 if (!ignore_nested_scopes) RetrieveScopeChain(scope); |
| 113 if (collect_non_locals) CollectNonLocals(scope); |
| 110 } | 114 } |
| 111 } | 115 } |
| 112 | 116 |
| 113 | 117 |
| 114 ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function) | 118 ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function) |
| 115 : isolate_(isolate), | 119 : isolate_(isolate), |
| 116 frame_inspector_(NULL), | 120 frame_inspector_(NULL), |
| 117 context_(function->context()), | 121 context_(function->context()), |
| 122 non_locals_(nullptr), |
| 118 seen_script_scope_(false), | 123 seen_script_scope_(false), |
| 119 failed_(false) { | 124 failed_(false) { |
| 120 if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>(); | 125 if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>(); |
| 121 } | 126 } |
| 122 | 127 |
| 123 | 128 |
| 124 MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() { | 129 MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() { |
| 125 // Calculate the size of the result. | 130 // Calculate the size of the result. |
| 126 Handle<FixedArray> details = | 131 Handle<FixedArray> details = |
| 127 isolate_->factory()->NewFixedArray(kScopeDetailsSize); | 132 isolate_->factory()->NewFixedArray(kScopeDetailsSize); |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 313 if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript || | 318 if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript || |
| 314 nested_scope_chain_.is_empty()) { | 319 nested_scope_chain_.is_empty()) { |
| 315 return context_; | 320 return context_; |
| 316 } else if (nested_scope_chain_.last()->HasContext()) { | 321 } else if (nested_scope_chain_.last()->HasContext()) { |
| 317 return context_; | 322 return context_; |
| 318 } else { | 323 } else { |
| 319 return Handle<Context>(); | 324 return Handle<Context>(); |
| 320 } | 325 } |
| 321 } | 326 } |
| 322 | 327 |
| 328 |
| 329 void ScopeIterator::GetNonLocals(List<Handle<String> >* list_out) { |
| 330 Handle<String> this_string = isolate_->factory()->this_string(); |
| 331 for (HashMap::Entry* entry = non_locals_->Start(); entry != nullptr; |
| 332 entry = non_locals_->Next(entry)) { |
| 333 Handle<String> name(reinterpret_cast<String**>(entry->key)); |
| 334 // We need to treat "this" differently. |
| 335 if (name.is_identical_to(this_string)) continue; |
| 336 list_out->Add(Handle<String>(reinterpret_cast<String**>(entry->key))); |
| 337 } |
| 338 } |
| 339 |
| 340 |
| 341 bool ScopeIterator::ThisIsNonLocal() { |
| 342 Handle<String> this_string = isolate_->factory()->this_string(); |
| 343 void* key = reinterpret_cast<void*>(this_string.location()); |
| 344 HashMap::Entry* entry = non_locals_->Lookup(key, this_string->Hash()); |
| 345 return entry != nullptr; |
| 346 } |
| 347 |
| 348 |
| 323 #ifdef DEBUG | 349 #ifdef DEBUG |
| 324 // Debug print of the content of the current scope. | 350 // Debug print of the content of the current scope. |
| 325 void ScopeIterator::DebugPrint() { | 351 void ScopeIterator::DebugPrint() { |
| 326 OFStream os(stdout); | 352 OFStream os(stdout); |
| 327 DCHECK(!failed_); | 353 DCHECK(!failed_); |
| 328 switch (Type()) { | 354 switch (Type()) { |
| 329 case ScopeIterator::ScopeTypeGlobal: | 355 case ScopeIterator::ScopeTypeGlobal: |
| 330 os << "Global:\n"; | 356 os << "Global:\n"; |
| 331 CurrentContext()->Print(os); | 357 CurrentContext()->Print(os); |
| 332 break; | 358 break; |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 378 break; | 404 break; |
| 379 | 405 |
| 380 default: | 406 default: |
| 381 UNREACHABLE(); | 407 UNREACHABLE(); |
| 382 } | 408 } |
| 383 PrintF("\n"); | 409 PrintF("\n"); |
| 384 } | 410 } |
| 385 #endif | 411 #endif |
| 386 | 412 |
| 387 | 413 |
| 388 void ScopeIterator::RetrieveScopeChain(Scope* scope, | 414 void ScopeIterator::RetrieveScopeChain(Scope* scope) { |
| 389 Handle<SharedFunctionInfo> shared_info) { | |
| 390 if (scope != NULL) { | 415 if (scope != NULL) { |
| 391 int source_position = frame_inspector_->GetSourcePosition(); | 416 int source_position = frame_inspector_->GetSourcePosition(); |
| 392 scope->GetNestedScopeChain(isolate_, &nested_scope_chain_, source_position); | 417 scope->GetNestedScopeChain(isolate_, &nested_scope_chain_, source_position); |
| 393 } else { | 418 } else { |
| 394 // A failed reparse indicates that the preparser has diverged from the | 419 // A failed reparse indicates that the preparser has diverged from the |
| 395 // parser or that the preparse data given to the initial parse has been | 420 // parser or that the preparse data given to the initial parse has been |
| 396 // faulty. We fail in debug mode but in release mode we only provide the | 421 // faulty. We fail in debug mode but in release mode we only provide the |
| 397 // information we get from the context chain but nothing about | 422 // information we get from the context chain but nothing about |
| 398 // completely stack allocated scopes or stack allocated locals. | 423 // completely stack allocated scopes or stack allocated locals. |
| 399 // Or it could be due to stack overflow. | 424 // Or it could be due to stack overflow. |
| 400 DCHECK(isolate_->has_pending_exception()); | 425 DCHECK(isolate_->has_pending_exception()); |
| 401 failed_ = true; | 426 failed_ = true; |
| 402 } | 427 } |
| 403 } | 428 } |
| 404 | 429 |
| 405 | 430 |
| 431 void ScopeIterator::CollectNonLocals(Scope* scope) { |
| 432 if (scope != NULL) { |
| 433 DCHECK_NULL(non_locals_); |
| 434 non_locals_ = new HashMap(InternalizedStringMatch); |
| 435 scope->CollectNonLocals(non_locals_); |
| 436 } |
| 437 } |
| 438 |
| 439 |
| 406 MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() { | 440 MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() { |
| 407 Handle<JSGlobalObject> global(CurrentContext()->global_object()); | 441 Handle<JSGlobalObject> global(CurrentContext()->global_object()); |
| 408 Handle<ScriptContextTable> script_contexts( | 442 Handle<ScriptContextTable> script_contexts( |
| 409 global->native_context()->script_context_table()); | 443 global->native_context()->script_context_table()); |
| 410 | 444 |
| 411 Handle<JSObject> script_scope = | 445 Handle<JSObject> script_scope = |
| 412 isolate_->factory()->NewJSObject(isolate_->object_function()); | 446 isolate_->factory()->NewJSObject(isolate_->object_function()); |
| 413 | 447 |
| 414 for (int context_index = 0; context_index < script_contexts->used(); | 448 for (int context_index = 0; context_index < script_contexts->used(); |
| 415 context_index++) { | 449 context_index++) { |
| (...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 783 isolate_, value, Object::GetPropertyOrElement(extension, key), false); | 817 isolate_, value, Object::GetPropertyOrElement(extension, key), false); |
| 784 RETURN_ON_EXCEPTION_VALUE( | 818 RETURN_ON_EXCEPTION_VALUE( |
| 785 isolate_, JSObject::SetOwnPropertyIgnoreAttributes( | 819 isolate_, JSObject::SetOwnPropertyIgnoreAttributes( |
| 786 scope_object, key, value, NONE), false); | 820 scope_object, key, value, NONE), false); |
| 787 } | 821 } |
| 788 return true; | 822 return true; |
| 789 } | 823 } |
| 790 | 824 |
| 791 } // namespace internal | 825 } // namespace internal |
| 792 } // namespace v8 | 826 } // namespace v8 |
| OLD | NEW |