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 3 // found in the LICENSE file. 4 5 #include "src/debug/debug-scopes.h" 6 7 #include <memory> 8 9 #include "src/ast/ast.h" 10 #include "src/ast/scopes.h" 11 #include "src/debug/debug.h" 12 #include "src/frames-inl.h" 13 #include "src/globals.h" 14 #include "src/isolate-inl.h" 15 #include "src/parsing/parse-info.h" 16 #include "src/parsing/parsing.h" 17 #include "src/parsing/rewriter.h" 18 19 namespace v8 { 20 namespace internal { 21 22 ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector, 23 ScopeIterator::Option option) 24 : isolate_(isolate), 25 frame_inspector_(frame_inspector), 26 nested_scope_chain_(4), 27 seen_script_scope_(false) { 28 if (!frame_inspector->GetContext()->IsContext()) { 29 // Optimized frame, context or function cannot be materialized. Give up. 30 return; 31 } 32 33 context_ = Handle<Context>::cast(frame_inspector->GetContext()); 34 35 // We should not instantiate a ScopeIterator for wasm frames. 36 DCHECK(frame_inspector->GetScript()->type() != Script::TYPE_WASM); 37 38 // Catch the case when the debugger stops in an internal function. 39 Handle<JSFunction> function = GetFunction(); 40 Handle<SharedFunctionInfo> shared_info(function->shared()); 41 Handle<ScopeInfo> scope_info(shared_info->scope_info()); 42 if (shared_info->script()->IsUndefined(isolate)) { 43 while (context_->closure() == *function) { 44 context_ = Handle<Context>(context_->previous(), isolate_); 45 } 46 return; 47 } 48 49 // Currently it takes too much time to find nested scopes due to script 50 // parsing. Sometimes we want to run the ScopeIterator as fast as possible 51 // (for example, while collecting async call stacks on every 52 // addEventListener call), even if we drop some nested scopes. 53 // Later we may optimize getting the nested scopes (cache the result?) 54 // and include nested scopes into the "fast" iteration case as well. 55 bool ignore_nested_scopes = (option == IGNORE_NESTED_SCOPES); 56 bool collect_non_locals = (option == COLLECT_NON_LOCALS); 57 if (!ignore_nested_scopes && shared_info->HasDebugInfo()) { 58 // The source position at return is always the end of the function, 59 // which is not consistent with the current scope chain. Therefore all 60 // nested with, catch and block contexts are skipped, and we can only 61 // inspect the function scope. 62 // This can only happen if we set a break point inside right before the 63 // return, which requires a debug info to be available. 64 Handle<DebugInfo> debug_info(shared_info->GetDebugInfo()); 65 66 // Find the break point where execution has stopped. 67 BreakLocation location = BreakLocation::FromFrame(debug_info, GetFrame()); 68 69 ignore_nested_scopes = location.IsReturn(); 70 } 71 72 if (ignore_nested_scopes) { 73 if (scope_info->HasContext()) { 74 context_ = Handle<Context>(context_->declaration_context(), isolate_); 75 } else { 76 while (context_->closure() == *function) { 77 context_ = Handle<Context>(context_->previous(), isolate_); 78 } 79 } 80 if (scope_info->scope_type() == FUNCTION_SCOPE) { 81 nested_scope_chain_.Add(ExtendedScopeInfo(scope_info, 82 shared_info->start_position(), 83 shared_info->end_position())); 84 } 85 if (!collect_non_locals) return; 86 } 87 88 // Reparse the code and analyze the scopes. 89 // Check whether we are in global, eval or function code. 90 std::unique_ptr<ParseInfo> info; 91 if (scope_info->scope_type() != FUNCTION_SCOPE) { 92 // Global or eval code. 93 Handle<Script> script(Script::cast(shared_info->script())); 94 info.reset(new ParseInfo(script)); 95 if (scope_info->scope_type() == EVAL_SCOPE) { 96 info->set_eval(); 97 if (!function->context()->IsNativeContext()) { 98 info->set_outer_scope_info(handle(function->context()->scope_info())); 99 } 100 // Language mode may be inherited from the eval caller. 101 // Retrieve it from shared function info. 102 info->set_language_mode(shared_info->language_mode()); 103 } else if (scope_info->scope_type() == MODULE_SCOPE) { 104 info->set_module(); 105 } else { 106 DCHECK(scope_info->scope_type() == SCRIPT_SCOPE); 107 } 108 } else { 109 // Inner function. 110 info.reset(new ParseInfo(shared_info)); 111 } 112 if (parsing::ParseAny(info.get()) && Rewriter::Rewrite(info.get())) { 113 DeclarationScope* scope = info->literal()->scope(); 114 if (!ignore_nested_scopes || collect_non_locals) { 115 CollectNonLocals(info.get(), scope); 116 } 117 if (!ignore_nested_scopes) { 118 DeclarationScope::Analyze(info.get(), AnalyzeMode::kDebugger); 119 RetrieveScopeChain(scope); 120 } 121 } else { 122 // A failed reparse indicates that the preparser has diverged from the 123 // parser or that the preparse data given to the initial parse has been 124 // faulty. We fail in debug mode but in release mode we only provide the 125 // information we get from the context chain but nothing about 126 // completely stack allocated scopes or stack allocated locals. 127 // Or it could be due to stack overflow. 128 // Silently fail by presenting an empty context chain. 129 CHECK(isolate_->has_pending_exception()); 130 isolate_->clear_pending_exception(); 131 context_ = Handle<Context>(); 132 } 133 UnwrapEvaluationContext(); 134 } 135 136 ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function) 137 : isolate_(isolate), 138 frame_inspector_(NULL), 139 context_(function->context()), 140 seen_script_scope_(false) { 141 if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>(); 142 UnwrapEvaluationContext(); 143 } 144 145 ScopeIterator::ScopeIterator(Isolate* isolate, 146 Handle<JSGeneratorObject> generator) 147 : isolate_(isolate), 148 frame_inspector_(NULL), 149 context_(generator->context()), 150 seen_script_scope_(false) { 151 if (!generator->function()->shared()->IsSubjectToDebugging()) { 152 context_ = Handle<Context>(); 153 } 154 UnwrapEvaluationContext(); 155 } 156 157 void ScopeIterator::UnwrapEvaluationContext() { 158 while (true) { 159 if (context_.is_null()) return; 160 if (!context_->IsDebugEvaluateContext()) return; 161 Handle<Object> wrapped(context_->get(Context::WRAPPED_CONTEXT_INDEX), 162 isolate_); 163 if (wrapped->IsContext()) { 164 context_ = Handle<Context>::cast(wrapped); 165 } else { 166 context_ = Handle<Context>(context_->previous(), isolate_); 167 } 168 } 169 } 170 171 172 MUST_USE_RESULT MaybeHandle<JSObject> ScopeIterator::MaterializeScopeDetails() { 173 // Calculate the size of the result. 174 Handle<FixedArray> details = 175 isolate_->factory()->NewFixedArray(kScopeDetailsSize); 176 // Fill in scope details. 177 details->set(kScopeDetailsTypeIndex, Smi::FromInt(Type())); 178 Handle<JSObject> scope_object; 179 ASSIGN_RETURN_ON_EXCEPTION(isolate_, scope_object, ScopeObject(), JSObject); 180 details->set(kScopeDetailsObjectIndex, *scope_object); 181 Handle<JSFunction> js_function = HasContext() 182 ? handle(CurrentContext()->closure()) 183 : Handle<JSFunction>::null(); 184 if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) { 185 return isolate_->factory()->NewJSArrayWithElements(details); 186 } 187 188 int start_position = 0; 189 int end_position = 0; 190 if (!nested_scope_chain_.is_empty()) { 191 js_function = GetFunction(); 192 start_position = nested_scope_chain_.last().start_position; 193 end_position = nested_scope_chain_.last().end_position; 194 } else if (!js_function.is_null()) { 195 start_position = js_function->shared()->start_position(); 196 end_position = js_function->shared()->end_position(); 197 } 198 199 if (!js_function.is_null()) { 200 Handle<String> closure_name = JSFunction::GetDebugName(js_function); 201 if (!closure_name.is_null() && closure_name->length() != 0) { 202 details->set(kScopeDetailsNameIndex, *closure_name); 203 } 204 details->set(kScopeDetailsStartPositionIndex, Smi::FromInt(start_position)); 205 details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position)); 206 details->set(kScopeDetailsFunctionIndex, *js_function); 207 } 208 return isolate_->factory()->NewJSArrayWithElements(details); 209 } 210 211 212 void ScopeIterator::Next() { 213 DCHECK(!Done()); 214 ScopeType scope_type = Type(); 215 if (scope_type == ScopeTypeGlobal) { 216 // The global scope is always the last in the chain. 217 DCHECK(context_->IsNativeContext()); 218 context_ = Handle<Context>(); 219 } else if (scope_type == ScopeTypeScript) { 220 seen_script_scope_ = true; 221 if (context_->IsScriptContext()) { 222 context_ = Handle<Context>(context_->previous(), isolate_); 223 } 224 if (!nested_scope_chain_.is_empty()) { 225 DCHECK_EQ(nested_scope_chain_.last().scope_info->scope_type(), 226 SCRIPT_SCOPE); 227 nested_scope_chain_.RemoveLast(); 228 DCHECK(nested_scope_chain_.is_empty()); 229 } 230 CHECK(context_->IsNativeContext()); 231 } else if (nested_scope_chain_.is_empty()) { 232 context_ = Handle<Context>(context_->previous(), isolate_); 233 } else { 234 do { 235 if (nested_scope_chain_.last().scope_info->HasContext()) { 236 DCHECK(context_->previous() != NULL); 237 context_ = Handle<Context>(context_->previous(), isolate_); 238 } 239 nested_scope_chain_.RemoveLast(); 240 if (nested_scope_chain_.is_empty()) break; 241 // Repeat to skip hidden scopes. 242 } while (nested_scope_chain_.last().is_hidden()); 243 } 244 UnwrapEvaluationContext(); 245 } 246 247 248 // Return the type of the current scope. 249 ScopeIterator::ScopeType ScopeIterator::Type() { 250 DCHECK(!Done()); 251 if (!nested_scope_chain_.is_empty()) { 252 Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info; 253 switch (scope_info->scope_type()) { 254 case FUNCTION_SCOPE: 255 DCHECK(context_->IsFunctionContext() || !scope_info->HasContext()); 256 return ScopeTypeLocal; 257 case MODULE_SCOPE: 258 DCHECK(context_->IsModuleContext()); 259 return ScopeTypeModule; 260 case SCRIPT_SCOPE: 261 DCHECK(context_->IsScriptContext() || context_->IsNativeContext()); 262 return ScopeTypeScript; 263 case WITH_SCOPE: 264 DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext()); 265 return ScopeTypeWith; 266 case CATCH_SCOPE: 267 DCHECK(context_->IsCatchContext()); 268 return ScopeTypeCatch; 269 case BLOCK_SCOPE: 270 DCHECK(!scope_info->HasContext() || context_->IsBlockContext()); 271 return ScopeTypeBlock; 272 case EVAL_SCOPE: 273 DCHECK(!scope_info->HasContext() || context_->IsEvalContext()); 274 return ScopeTypeEval; 275 } 276 UNREACHABLE(); 277 } 278 if (context_->IsNativeContext()) { 279 DCHECK(context_->global_object()->IsJSGlobalObject()); 280 // If we are at the native context and have not yet seen script scope, 281 // fake it. 282 return seen_script_scope_ ? ScopeTypeGlobal : ScopeTypeScript; 283 } 284 if (context_->IsFunctionContext() || context_->IsEvalContext()) { 285 return ScopeTypeClosure; 286 } 287 if (context_->IsCatchContext()) { 288 return ScopeTypeCatch; 289 } 290 if (context_->IsBlockContext()) { 291 return ScopeTypeBlock; 292 } 293 if (context_->IsModuleContext()) { 294 return ScopeTypeModule; 295 } 296 if (context_->IsScriptContext()) { 297 return ScopeTypeScript; 298 } 299 DCHECK(context_->IsWithContext() || context_->IsDebugEvaluateContext()); 300 return ScopeTypeWith; 301 } 302 303 304 MaybeHandle<JSObject> ScopeIterator::ScopeObject() { 305 DCHECK(!Done()); 306 switch (Type()) { 307 case ScopeIterator::ScopeTypeGlobal: 308 return Handle<JSObject>(CurrentContext()->global_proxy()); 309 case ScopeIterator::ScopeTypeScript: 310 return MaterializeScriptScope(); 311 case ScopeIterator::ScopeTypeLocal: 312 // Materialize the content of the local scope into a JSObject. 313 DCHECK(nested_scope_chain_.length() == 1); 314 return MaterializeLocalScope(); 315 case ScopeIterator::ScopeTypeWith: 316 return WithContextExtension(); 317 case ScopeIterator::ScopeTypeCatch: 318 return MaterializeCatchScope(); 319 case ScopeIterator::ScopeTypeClosure: 320 // Materialize the content of the closure scope into a JSObject. 321 return MaterializeClosure(); 322 case ScopeIterator::ScopeTypeBlock: 323 case ScopeIterator::ScopeTypeEval: 324 return MaterializeInnerScope(); 325 case ScopeIterator::ScopeTypeModule: 326 return MaterializeModuleScope(); 327 } 328 UNREACHABLE(); 329 return Handle<JSObject>(); 330 } 331 332 333 bool ScopeIterator::HasContext() { 334 ScopeType type = Type(); 335 if (type == ScopeTypeBlock || type == ScopeTypeLocal || 336 type == ScopeTypeEval) { 337 if (!nested_scope_chain_.is_empty()) { 338 return nested_scope_chain_.last().scope_info->HasContext(); 339 } 340 } 341 return true; 342 } 343 344 345 bool ScopeIterator::SetVariableValue(Handle<String> variable_name, 346 Handle<Object> new_value) { 347 DCHECK(!Done()); 348 switch (Type()) { 349 case ScopeIterator::ScopeTypeGlobal: 350 break; 351 case ScopeIterator::ScopeTypeLocal: 352 return SetLocalVariableValue(variable_name, new_value); 353 case ScopeIterator::ScopeTypeWith: 354 break; 355 case ScopeIterator::ScopeTypeCatch: 356 return SetCatchVariableValue(variable_name, new_value); 357 case ScopeIterator::ScopeTypeClosure: 358 return SetClosureVariableValue(variable_name, new_value); 359 case ScopeIterator::ScopeTypeScript: 360 return SetScriptVariableValue(variable_name, new_value); 361 case ScopeIterator::ScopeTypeBlock: 362 case ScopeIterator::ScopeTypeEval: 363 return SetInnerScopeVariableValue(variable_name, new_value); 364 case ScopeIterator::ScopeTypeModule: 365 // TODO(neis): Implement. 366 break; 367 } 368 return false; 369 } 370 371 372 Handle<ScopeInfo> ScopeIterator::CurrentScopeInfo() { 373 DCHECK(!Done()); 374 if (!nested_scope_chain_.is_empty()) { 375 return nested_scope_chain_.last().scope_info; 376 } else if (context_->IsBlockContext() || context_->IsFunctionContext() || 377 context_->IsEvalContext()) { 378 return Handle<ScopeInfo>(context_->scope_info()); 379 } 380 return Handle<ScopeInfo>::null(); 381 } 382 383 384 Handle<Context> ScopeIterator::CurrentContext() { 385 DCHECK(!Done()); 386 if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript || 387 nested_scope_chain_.is_empty()) { 388 return context_; 389 } else if (nested_scope_chain_.last().scope_info->HasContext()) { 390 return context_; 391 } else { 392 return Handle<Context>(); 393 } 394 } 395 396 Handle<StringSet> ScopeIterator::GetNonLocals() { return non_locals_; } 397 398 #ifdef DEBUG 399 // Debug print of the content of the current scope. 400 void ScopeIterator::DebugPrint() { 401 OFStream os(stdout); 402 DCHECK(!Done()); 403 switch (Type()) { 404 case ScopeIterator::ScopeTypeGlobal: 405 os << "Global:\n"; 406 CurrentContext()->Print(os); 407 break; 408 409 case ScopeIterator::ScopeTypeLocal: { 410 os << "Local:\n"; 411 GetFunction()->shared()->scope_info()->Print(); 412 if (!CurrentContext().is_null()) { 413 CurrentContext()->Print(os); 414 if (CurrentContext()->has_extension()) { 415 Handle<HeapObject> extension(CurrentContext()->extension(), isolate_); 416 if (extension->IsJSContextExtensionObject()) { 417 extension->Print(os); 418 } 419 } 420 } 421 break; 422 } 423 424 case ScopeIterator::ScopeTypeWith: 425 os << "With:\n"; 426 CurrentContext()->extension()->Print(os); 427 break; 428 429 case ScopeIterator::ScopeTypeCatch: 430 os << "Catch:\n"; 431 CurrentContext()->extension()->Print(os); 432 CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print(os); 433 break; 434 435 case ScopeIterator::ScopeTypeClosure: 436 os << "Closure:\n"; 437 CurrentContext()->Print(os); 438 if (CurrentContext()->has_extension()) { 439 Handle<HeapObject> extension(CurrentContext()->extension(), isolate_); 440 if (extension->IsJSContextExtensionObject()) { 441 extension->Print(os); 442 } 443 } 444 break; 445 446 case ScopeIterator::ScopeTypeScript: 447 os << "Script:\n"; 448 CurrentContext() 449 ->global_object() 450 ->native_context() 451 ->script_context_table() 452 ->Print(os); 453 break; 454 455 default: 456 UNREACHABLE(); 457 } 458 PrintF("\n"); 459 } 460 #endif 461 462 void ScopeIterator::RetrieveScopeChain(DeclarationScope* scope) { 463 DCHECK_NOT_NULL(scope); 464 int source_position = frame_inspector_->GetSourcePosition(); 465 GetNestedScopeChain(isolate_, scope, source_position); 466 } 467 468 void ScopeIterator::CollectNonLocals(ParseInfo* info, DeclarationScope* scope) { 469 DCHECK_NOT_NULL(scope); 470 DCHECK(non_locals_.is_null()); 471 non_locals_ = scope->CollectNonLocals(info, StringSet::New(isolate_)); 472 } 473 474 475 MaybeHandle<JSObject> ScopeIterator::MaterializeScriptScope() { 476 Handle<JSGlobalObject> global(CurrentContext()->global_object()); 477 Handle<ScriptContextTable> script_contexts( 478 global->native_context()->script_context_table()); 479 480 Handle<JSObject> script_scope = 481 isolate_->factory()->NewJSObjectWithNullProto(); 482 483 for (int context_index = 0; context_index < script_contexts->used(); 484 context_index++) { 485 Handle<Context> context = 486 ScriptContextTable::GetContext(script_contexts, context_index); 487 Handle<ScopeInfo> scope_info(context->scope_info()); 488 CopyContextLocalsToScopeObject(scope_info, context, script_scope); 489 } 490 return script_scope; 491 } 492 493 494 MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() { 495 Handle<JSFunction> function = GetFunction(); 496 497 Handle<JSObject> local_scope = 498 isolate_->factory()->NewJSObjectWithNullProto(); 499 frame_inspector_->MaterializeStackLocals(local_scope, function); 500 501 Handle<Context> frame_context = 502 Handle<Context>::cast(frame_inspector_->GetContext()); 503 504 HandleScope scope(isolate_); 505 Handle<SharedFunctionInfo> shared(function->shared()); 506 Handle<ScopeInfo> scope_info(shared->scope_info()); 507 508 if (!scope_info->HasContext()) return local_scope; 509 510 // Fill all context locals. 511 Handle<Context> function_context(frame_context->closure_context()); 512 CopyContextLocalsToScopeObject(scope_info, function_context, local_scope); 513 514 // Finally copy any properties from the function context extension. 515 // These will be variables introduced by eval. 516 if (function_context->closure() == *function && 517 !function_context->IsNativeContext()) { 518 CopyContextExtensionToScopeObject(function_context, local_scope, 519 KeyCollectionMode::kIncludePrototypes); 520 } 521 522 return local_scope; 523 } 524 525 526 // Create a plain JSObject which materializes the closure content for the 527 // context. 528 Handle<JSObject> ScopeIterator::MaterializeClosure() { 529 Handle<Context> context = CurrentContext(); 530 DCHECK(context->IsFunctionContext() || context->IsEvalContext()); 531 532 Handle<SharedFunctionInfo> shared(context->closure()->shared()); 533 Handle<ScopeInfo> scope_info(shared->scope_info()); 534 535 // Allocate and initialize a JSObject with all the content of this function 536 // closure. 537 Handle<JSObject> closure_scope = 538 isolate_->factory()->NewJSObjectWithNullProto(); 539 540 // Fill all context locals to the context extension. 541 CopyContextLocalsToScopeObject(scope_info, context, closure_scope); 542 543 // Finally copy any properties from the function context extension. This will 544 // be variables introduced by eval. 545 CopyContextExtensionToScopeObject(context, closure_scope, 546 KeyCollectionMode::kOwnOnly); 547 548 return closure_scope; 549 } 550 551 552 // Create a plain JSObject which materializes the scope for the specified 553 // catch context. 554 Handle<JSObject> ScopeIterator::MaterializeCatchScope() { 555 Handle<Context> context = CurrentContext(); 556 DCHECK(context->IsCatchContext()); 557 Handle<String> name(context->catch_name()); 558 Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX), 559 isolate_); 560 Handle<JSObject> catch_scope = 561 isolate_->factory()->NewJSObjectWithNullProto(); 562 JSObject::SetOwnPropertyIgnoreAttributes(catch_scope, name, thrown_object, 563 NONE) 564 .Check(); 565 return catch_scope; 566 } 567 568 // Retrieve the with-context extension object. If the extension object is 569 // a proxy, return an empty object. 570 Handle<JSObject> ScopeIterator::WithContextExtension() { 571 Handle<Context> context = CurrentContext(); 572 DCHECK(context->IsWithContext()); 573 if (context->extension_receiver()->IsJSProxy()) { 574 return isolate_->factory()->NewJSObjectWithNullProto(); 575 } 576 return handle(JSObject::cast(context->extension_receiver())); 577 } 578 579 // Create a plain JSObject which materializes the block scope for the specified 580 // block context. 581 Handle<JSObject> ScopeIterator::MaterializeInnerScope() { 582 Handle<JSObject> inner_scope = 583 isolate_->factory()->NewJSObjectWithNullProto(); 584 585 Handle<Context> context = Handle<Context>::null(); 586 if (!nested_scope_chain_.is_empty()) { 587 Handle<ScopeInfo> scope_info = nested_scope_chain_.last().scope_info; 588 frame_inspector_->MaterializeStackLocals(inner_scope, scope_info); 589 if (scope_info->HasContext()) context = CurrentContext(); 590 } else { 591 context = CurrentContext(); 592 } 593 594 if (!context.is_null()) { 595 // Fill all context locals. 596 CopyContextLocalsToScopeObject(CurrentScopeInfo(), context, inner_scope); 597 CopyContextExtensionToScopeObject(context, inner_scope, 598 KeyCollectionMode::kOwnOnly); 599 } 600 return inner_scope; 601 } 602 603 604 // Create a plain JSObject which materializes the module scope for the specified 605 // module context. 606 MaybeHandle<JSObject> ScopeIterator::MaterializeModuleScope() { 607 Handle<Context> context = CurrentContext(); 608 DCHECK(context->IsModuleContext()); 609 Handle<ScopeInfo> scope_info(context->scope_info()); 610 Handle<JSObject> module_scope = 611 isolate_->factory()->NewJSObjectWithNullProto(); 612 CopyContextLocalsToScopeObject(scope_info, context, module_scope); 613 CopyModuleVarsToScopeObject(scope_info, context, module_scope); 614 return module_scope; 615 } 616 617 bool ScopeIterator::SetParameterValue(Handle<ScopeInfo> scope_info, 618 JavaScriptFrame* frame, 619 Handle<String> parameter_name, 620 Handle<Object> new_value) { 621 // Setting stack locals of optimized frames is not supported. 622 if (frame->is_optimized()) return false; 623 HandleScope scope(isolate_); 624 for (int i = 0; i < scope_info->ParameterCount(); ++i) { 625 if (String::Equals(handle(scope_info->ParameterName(i)), parameter_name)) { 626 frame->SetParameterValue(i, *new_value); 627 return true; 628 } 629 } 630 return false; 631 } 632 633 bool ScopeIterator::SetStackVariableValue(Handle<ScopeInfo> scope_info, 634 Handle<String> variable_name, 635 Handle<Object> new_value) { 636 if (frame_inspector_ == nullptr) return false; 637 JavaScriptFrame* frame = GetFrame(); 638 // Setting stack locals of optimized frames is not supported. 639 if (frame->is_optimized()) return false; 640 HandleScope scope(isolate_); 641 for (int i = 0; i < scope_info->StackLocalCount(); ++i) { 642 if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) { 643 frame->SetExpression(scope_info->StackLocalIndex(i), *new_value); 644 return true; 645 } 646 } 647 return false; 648 } 649 650 bool ScopeIterator::SetContextVariableValue(Handle<ScopeInfo> scope_info, 651 Handle<Context> context, 652 Handle<String> variable_name, 653 Handle<Object> new_value) { 654 HandleScope scope(isolate_); 655 for (int i = 0; i < scope_info->ContextLocalCount(); i++) { 656 Handle<String> next_name(scope_info->ContextLocalName(i)); 657 if (String::Equals(variable_name, next_name)) { 658 VariableMode mode; 659 InitializationFlag init_flag; 660 MaybeAssignedFlag maybe_assigned_flag; 661 int context_index = ScopeInfo::ContextSlotIndex( 662 scope_info, next_name, &mode, &init_flag, &maybe_assigned_flag); 663 context->set(context_index, *new_value); 664 return true; 665 } 666 } 667 668 if (context->has_extension()) { 669 Handle<JSObject> ext(context->extension_object()); 670 Maybe<bool> maybe = JSReceiver::HasOwnProperty(ext, variable_name); 671 DCHECK(maybe.IsJust()); 672 if (maybe.FromJust()) { 673 // We don't expect this to do anything except replacing property value. 674 JSObject::SetOwnPropertyIgnoreAttributes(ext, variable_name, new_value, 675 NONE) 676 .Check(); 677 return true; 678 } 679 } 680 681 return false; 682 } 683 684 bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name, 685 Handle<Object> new_value) { 686 JavaScriptFrame* frame = GetFrame(); 687 Handle<ScopeInfo> scope_info(frame->function()->shared()->scope_info()); 688 689 // Parameter might be shadowed in context. Don't stop here. 690 bool result = SetParameterValue(scope_info, frame, variable_name, new_value); 691 692 // Stack locals. 693 if (SetStackVariableValue(scope_info, variable_name, new_value)) { 694 return true; 695 } 696 697 if (scope_info->HasContext() && 698 SetContextVariableValue(scope_info, CurrentContext(), variable_name, 699 new_value)) { 700 return true; 701 } 702 703 return result; 704 } 705 706 bool ScopeIterator::SetInnerScopeVariableValue(Handle<String> variable_name, 707 Handle<Object> new_value) { 708 Handle<ScopeInfo> scope_info = CurrentScopeInfo(); 709 DCHECK(scope_info->scope_type() == BLOCK_SCOPE || 710 scope_info->scope_type() == EVAL_SCOPE); 711 712 // Setting stack locals of optimized frames is not supported. 713 if (SetStackVariableValue(scope_info, variable_name, new_value)) { 714 return true; 715 } 716 717 if (HasContext() && SetContextVariableValue(scope_info, CurrentContext(), 718 variable_name, new_value)) { 719 return true; 720 } 721 722 return false; 723 } 724 725 // This method copies structure of MaterializeClosure method above. 726 bool ScopeIterator::SetClosureVariableValue(Handle<String> variable_name, 727 Handle<Object> new_value) { 728 DCHECK(CurrentContext()->IsFunctionContext() || 729 CurrentContext()->IsEvalContext()); 730 return SetContextVariableValue(CurrentScopeInfo(), CurrentContext(), 731 variable_name, new_value); 732 } 733 734 bool ScopeIterator::SetScriptVariableValue(Handle<String> variable_name, 735 Handle<Object> new_value) { 736 Handle<String> internalized_variable_name = 737 isolate_->factory()->InternalizeString(variable_name); 738 Handle<Context> context = CurrentContext(); 739 Handle<ScriptContextTable> script_contexts( 740 context->global_object()->native_context()->script_context_table()); 741 ScriptContextTable::LookupResult lookup_result; 742 if (ScriptContextTable::Lookup(script_contexts, internalized_variable_name, 743 &lookup_result)) { 744 Handle<Context> script_context = ScriptContextTable::GetContext( 745 script_contexts, lookup_result.context_index); 746 script_context->set(lookup_result.slot_index, *new_value); 747 return true; 748 } 749 750 return false; 751 } 752 753 bool ScopeIterator::SetCatchVariableValue(Handle<String> variable_name, 754 Handle<Object> new_value) { 755 Handle<Context> context = CurrentContext(); 756 DCHECK(context->IsCatchContext()); 757 Handle<String> name(context->catch_name()); 758 if (!String::Equals(name, variable_name)) { 759 return false; 760 } 761 context->set(Context::THROWN_OBJECT_INDEX, *new_value); 762 return true; 763 } 764 765 766 void ScopeIterator::CopyContextLocalsToScopeObject( 767 Handle<ScopeInfo> scope_info, Handle<Context> context, 768 Handle<JSObject> scope_object) { 769 Isolate* isolate = scope_info->GetIsolate(); 770 int local_count = scope_info->ContextLocalCount(); 771 if (local_count == 0) return; 772 // Fill all context locals to the context extension. 773 for (int i = 0; i < local_count; ++i) { 774 Handle<String> name(scope_info->ContextLocalName(i)); 775 if (ScopeInfo::VariableIsSynthetic(*name)) continue; 776 int context_index = Context::MIN_CONTEXT_SLOTS + i; 777 Handle<Object> value = Handle<Object>(context->get(context_index), isolate); 778 // Reflect variables under TDZ as undefined in scope object. 779 if (value->IsTheHole(isolate)) continue; 780 // This should always succeed. 781 // TODO(verwaest): Use AddDataProperty instead. 782 JSObject::SetOwnPropertyIgnoreAttributes(scope_object, name, value, NONE) 783 .Check(); 784 } 785 } 786 787 void ScopeIterator::CopyModuleVarsToScopeObject(Handle<ScopeInfo> scope_info, 788 Handle<Context> context, 789 Handle<JSObject> scope_object) { 790 Isolate* isolate = scope_info->GetIsolate(); 791 792 int module_variable_count = 793 Smi::cast(scope_info->get(scope_info->ModuleVariableCountIndex())) 794 ->value(); 795 for (int i = 0; i < module_variable_count; ++i) { 796 Handle<String> local_name; 797 Handle<Object> value; 798 { 799 String* name; 800 int index; 801 scope_info->ModuleVariable(i, &name, &index); 802 CHECK(!ScopeInfo::VariableIsSynthetic(name)); 803 local_name = handle(name, isolate); 804 value = Module::LoadVariable(handle(context->module(), isolate), index); 805 } 806 807 // Reflect variables under TDZ as undefined in scope object. 808 if (value->IsTheHole(isolate)) continue; 809 // This should always succeed. 810 // TODO(verwaest): Use AddDataProperty instead. 811 JSObject::SetOwnPropertyIgnoreAttributes(scope_object, local_name, value, 812 NONE) 813 .Check(); 814 } 815 } 816 817 void ScopeIterator::CopyContextExtensionToScopeObject( 818 Handle<Context> context, Handle<JSObject> scope_object, 819 KeyCollectionMode mode) { 820 if (context->extension_object() == nullptr) return; 821 Handle<JSObject> extension(context->extension_object()); 822 Handle<FixedArray> keys = 823 KeyAccumulator::GetKeys(extension, mode, ENUMERABLE_STRINGS) 824 .ToHandleChecked(); 825 826 for (int i = 0; i < keys->length(); i++) { 827 // Names of variables introduced by eval are strings. 828 DCHECK(keys->get(i)->IsString()); 829 Handle<String> key(String::cast(keys->get(i))); 830 Handle<Object> value = 831 Object::GetPropertyOrElement(extension, key).ToHandleChecked(); 832 JSObject::SetOwnPropertyIgnoreAttributes(scope_object, key, value, NONE) 833 .Check(); 834 } 835 } 836 837 void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope, 838 int position) { 839 if (scope->is_function_scope()) { 840 // Do not collect scopes of nested inner functions inside the current one. 841 // Nested arrow functions could have the same end positions. 842 Handle<JSFunction> function = frame_inspector_->GetFunction(); 843 if (scope->start_position() > function->shared()->start_position() && 844 scope->end_position() <= function->shared()->end_position()) { 845 return; 846 } 847 } 848 if (scope->is_hidden()) { 849 // We need to add this chain element in case the scope has a context 850 // associated. We need to keep the scope chain and context chain in sync. 851 nested_scope_chain_.Add(ExtendedScopeInfo(scope->scope_info())); 852 } else { 853 nested_scope_chain_.Add(ExtendedScopeInfo( 854 scope->scope_info(), scope->start_position(), scope->end_position())); 855 } 856 for (Scope* inner_scope = scope->inner_scope(); inner_scope != nullptr; 857 inner_scope = inner_scope->sibling()) { 858 int beg_pos = inner_scope->start_position(); 859 int end_pos = inner_scope->end_position(); 860 DCHECK((beg_pos >= 0 && end_pos >= 0) || inner_scope->is_hidden()); 861 if (beg_pos <= position && position < end_pos) { 862 GetNestedScopeChain(isolate, inner_scope, position); 863 return; 864 } 865 } 866 } 867 868 } // namespace internal 869 } // namespace v8 870